diff --git a/scripts/kill-vitest.sh b/scripts/kill-vitest.sh new file mode 100755 index 00000000000..295f2c9f1dd --- /dev/null +++ b/scripts/kill-vitest.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# +# Kill all running vitest processes. +# +# Usage: +# scripts/kill-vitest.sh # kill all +# scripts/kill-vitest.sh --dry # preview what would be killed +# + +set -euo pipefail + +DRY_RUN=false +if [[ "${1:-}" == "--dry" || "${1:-}" == "--dry-run" || "${1:-}" == "-n" ]]; then + DRY_RUN=true +fi + +pids=() +lines=() + +while IFS= read -r line; do + [[ -z "$line" ]] && continue + pid=$(echo "$line" | awk '{print $2}') + pids+=("$pid") + lines+=("$line") +done < <(ps aux | grep -E '(^|/)(vitest|node .*/vitest)( |$)|/\.bin/vitest|vitest/dist|vitest\.mjs' | grep -v grep || true) + +if [[ ${#pids[@]} -eq 0 ]]; then + echo "No vitest processes found." + exit 0 +fi + +echo "Found ${#pids[@]} vitest process(es):" +echo "" + +for i in "${!pids[@]}"; do + line="${lines[$i]}" + pid=$(echo "$line" | awk '{print $2}') + start=$(echo "$line" | awk '{print $9}') + cmd=$(echo "$line" | awk '{for(i=11;i<=NF;i++) printf "%s ", $i; print ""}') + cmd=$(echo "$cmd" | sed "s|$HOME/||g") + printf " PID %-7s started %-10s %s\n" "$pid" "$start" "$cmd" +done + +echo "" + +if [[ "$DRY_RUN" == true ]]; then + echo "Dry run — re-run without --dry to kill these processes." + exit 0 +fi + +echo "Sending SIGTERM..." +for pid in "${pids[@]}"; do + kill -TERM "$pid" 2>/dev/null && echo " signaled $pid" || echo " $pid already gone" +done + +sleep 2 + +for pid in "${pids[@]}"; do + if kill -0 "$pid" 2>/dev/null; then + echo " $pid still alive, sending SIGKILL..." + kill -KILL "$pid" 2>/dev/null || true + fi +done + +echo "Done." diff --git a/scripts/paperclip-issue-update.sh b/scripts/paperclip-issue-update.sh index 2645d77ff85..36ee5617cd6 100755 --- a/scripts/paperclip-issue-update.sh +++ b/scripts/paperclip-issue-update.sh @@ -104,6 +104,8 @@ fi curl -sS -X PATCH \ "$PAPERCLIP_API_URL/api/issues/$issue_id" \ + --connect-timeout "${PAPERCLIP_API_CONNECT_TIMEOUT_SECONDS:-10}" \ + --max-time "${PAPERCLIP_API_MAX_TIME_SECONDS:-60}" \ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \ -H 'Content-Type: application/json' \ diff --git a/skills/paperclip/SKILL.md b/skills/paperclip/SKILL.md index 0e817fc3a84..b7b4895ead0 100644 --- a/skills/paperclip/SKILL.md +++ b/skills/paperclip/SKILL.md @@ -128,7 +128,9 @@ Headers: X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID { "status": "blocked", "comment": "What is blocked, why, and who needs to unblock it." } ``` -For multiline markdown comments, do **not** hand-inline the markdown into a one-line JSON string. That is how comments get "smooshed" together. Use the helper below or an equivalent `jq --arg` pattern so literal newlines survive JSON encoding: +For multiline markdown comments, do **not** hand-inline the markdown into a one-line JSON string. That is how comments get "smooshed" together. Use the helper below or an equivalent `jq --arg` pattern so literal newlines survive JSON encoding. + +Do **not** pipe a heredoc into `jq` while also using command substitution to read that same stream, for example `cat <<'MD' | jq -n --arg comment "$(cat)" ... | curl -d @-`. That pattern can leave `curl` blocked forever waiting for stdin and keep the whole heartbeat process group alive after the agent has already produced a final result. If you cannot use the helper, read the heredoc into a shell variable first, then pass that variable to `jq -n --arg`. ```bash scripts/paperclip-issue-update.sh --issue-id "$PAPERCLIP_TASK_ID" --status done <<'MD' @@ -356,6 +358,24 @@ MD If you cannot use the helper, use `jq -n --arg comment "$comment"` with `comment` read from a heredoc or file. Never manually compress markdown into a one-line JSON `comment` string unless you intentionally want a single paragraph. +Avoid this hanging anti-pattern: + +```bash +cat <<'MD' | jq -n --arg comment "$(cat)" '{"comment": $comment}' | curl -d @- ... +MD +``` + +That command has multiple readers and writers on the same pipeline and can leave `curl` waiting on stdin with no network request in flight. Safer direct fallback: + +```bash +comment="$(cat <<'MD' +Investigating comment formatting +MD +)" +jq -n --arg comment "$comment" '{"comment": $comment}' | + curl --fail --show-error --connect-timeout 10 --max-time 60 -d @- ... +``` + Example: ```md