Loading...
Loading...
Use when implementing a GitHub issue end-to-end — MANDATORY for all dev tasks; spawns a repo worker devbox through speckit phases.
npx skill4agent add fellowship-dev/dogfooded-skills speckit-runner$0$1$PYLOT_API$PYLOT_GATEWAY_URL$PYLOT_DISPATCH_TOKEN$PYLOT_JOB_ID$1$PYLOT_REPODrive the worker in the foreground by polling the worker API — in short chunks you re-run yourself. After queueing each phase prompt, run the Poll-to-idle snippet (Step P) with the Bash tool, using the default Bash timeout (do NOT pass a long). Each call returns in under 2 minutes with atimeout; while it printsPOLL_RESULT, run Step P again immediately. Every ~30 min it printsPOLL_RESULT=runningwith a decision packet (heartbeat age, output-changed flag, log tail) — review it and, if the worker is healthy, run Step P again to grant another block. Repeat untilPOLL_RESULT=block_elapsed.POLL_RESULT=doneThe trap (read this): the harness auto-backgrounds any Bash command that runs past its tool(default ~120 s). A backgrounded poll is fatal. You may see a completion notification arrive for a background task — ignore that signal as a reason to wait. Those notifications only fire while your session is actively running tool calls; the instant you end your turn to "wait for it," the headlesstimeoutsession exits and the mission is finalized as failed — while the worker is still healthy. So: never set a long Bashclaude -pon Step P, never background it, never "wait for a notification," never end your turn while a worker turn is in flight. If Step P ever gets backgrounded, that is a bug — kill it and run it again. Each call is short synchronous shell you run, read, and re-run yourself.timeout
ISSUE_STATE=$(gh issue view $0 --repo $1 --json state --jq '.state' 2>/dev/null || echo "OPEN")
if [ "$ISSUE_STATE" = "CLOSED" ]; then
echo "[pylot] outcome=\"already complete — issue $0 is CLOSED\" status=success"
exit 0
fioutcome="already complete"REPO="${1:-$PYLOT_REPO}"
SPAWN_RESP=$(curl -s --max-time 90 -X POST \
-H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"repo\": \"$REPO\"}" \
"${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers")
WID=$(echo "$SPAWN_RESP" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("worker_id",""))' 2>/dev/null)
if [ -z "$WID" ]; then
echo "[pylot] outcome=\"worker spawn failed: $(echo $SPAWN_RESP | head -c 200)\" status=failed"
exit 1
fi
echo "[speckit-runner] worker spawned: $WID"poll-worker.sh~/.claude/skills/speckit-runner/poll-worker.shbash ~/.claude/skills/speckit-runner/poll-worker.sh "$WID" "$TURN_SEQ"POLL_RESULT=donePOLL_RESULT=runningWIDTURN_SEQPOLL_RESULT=block_elapsedheartbeat_ageoutput_changedoutput_changed=emptyPOST ${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}/stopoutput_changed=noPOLL_RESULT=ceiling_timeouttimeoutbash ~/.claude/skills/speckit-runner/poll-worker.sh "$WID" "$TURN_SEQ"POLL_RESULT=runningPROMPT=$(python3 -c "import json,sys; print(json.dumps('You are a worker running inside repo $REPO. Issue: #$0.\n\nPre-Flight (MANDATORY — do this FIRST):\n1. Fetch issue: gh issue view $0 --repo $REPO --json title,body,labels,comments\n2. Check if closed: if CLOSED, emit [pylot] outcome=\"already complete\" status=success and exit.\n3. Verify required labels exist (create '\''in-progress'\'' if missing).\n4. Gather real data: read issue comments, fetch referenced URLs, read existing code patterns.\n\nSpeckit Specify:\n5. Ensure you'\''re on the default branch: git checkout \$(gh repo view $REPO --json defaultBranchRef -q .defaultBranchRef.name) && git pull\n6. Bootstrap speckit scaffolding if absent: if [ ! -f \".specify/scripts/bash/create-new-feature.sh\" ]; then /setup-speckit; fi\n7. Run: /speckit-specify $0\n8. Read specs/ output. If there are open questions, answer them from pre-flight data, then run /speckit-clarify.\n9. Detect the feature branch created by specify: BRANCH=\$(git branch --show-current)\n\nWhen done: emit [pylot] phase=preflight status=done branch=\$BRANCH'))")
PROMPT_RESP=$(curl -s --max-time 30 -X POST \
-H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"prompt\": $PROMPT}" \
"${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}/prompt")
TURN_SEQ=$(echo "$PROMPT_RESP" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("turn_seq",""))' 2>/dev/null)
echo "[speckit-runner] preflight prompt queued (turn_seq=$TURN_SEQ)"bash ~/.claude/skills/speckit-runner/poll-worker.sh "$WID" "$TURN_SEQ"POLL_RESULT=runningPOLL_RESULT=donephase=preflight status=donestatus=failedbash ~/.claude/skills/speckit-runner/poll-worker.sh "$WID" "$TURN_SEQ"POLL_RESULT=runningPROMPT=$(python3 -c "import json; print(json.dumps('Continue on the feature branch from the previous phase.\nRun: /speckit-plan $0\nRead specs/{issue-slug}/plan.md and verify the approach.\nRun: /speckit-tasks $0\nRead specs/{issue-slug}/tasks.md and verify tasks are concrete.\nWhen done: emit [pylot] phase=plan status=done'))")
PROMPT_RESP=$(curl -s --max-time 30 -X POST \
-H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"prompt\": $PROMPT}" \
"${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}/prompt")
TURN_SEQ=$(echo "$PROMPT_RESP" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("turn_seq",""))' 2>/dev/null)
echo "[speckit-runner] plan prompt queued (turn_seq=$TURN_SEQ)"bash ~/.claude/skills/speckit-runner/poll-worker.sh "$WID" "$TURN_SEQ"POLL_RESULT=runningPOLL_RESULT=donephase=plan status=donebash ~/.claude/skills/speckit-runner/poll-worker.sh "$WID" "$TURN_SEQ"POLL_RESULT=runningPROMPT=$(python3 -c "import json; print(json.dumps('Continue on the feature branch. Run: /speckit-implement $0\nAfter implementation:\n- Run the project test suite (fix failures before proceeding).\n- If dev server available: verify affected pages/APIs.\n- Commit spec files: git add specs/ && git diff --cached --quiet || git commit -m '\''docs: add speckit specs for issue #$0'\''\n- Push: git push origin \$(git branch --show-current)\n- Create PR: gh pr create --repo $REPO --head \$(git branch --show-current) --base \$(gh repo view $REPO --json defaultBranchRef -q .defaultBranchRef.name) --title '\''fix/feat: <description> (#$0)'\'' --body '\''[PR body from /create-compelling-prs template]'\''\n- Run: /speckit-analyze $0 && /speckit-checklist $0\nWhen done: emit [pylot] phase=implement status=done pr=<PR_URL>'))")
PROMPT_RESP=$(curl -s --max-time 30 -X POST \
-H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"prompt\": $PROMPT}" \
"${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}/prompt")
TURN_SEQ=$(echo "$PROMPT_RESP" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("turn_seq",""))' 2>/dev/null)
echo "[speckit-runner] implement prompt queued (turn_seq=$TURN_SEQ)"bash ~/.claude/skills/speckit-runner/poll-worker.sh "$WID" "$TURN_SEQ"POLL_RESULT=runningPOLL_RESULT=block_elapsedPOLL_RESULT=donephase=implement status=done pr=<PR_URL># Re-fetch fresh — $ST from Step P does not survive into this separate Bash call.
ST=$(curl -s --max-time 20 -H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
"${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}")
WORKER_OUT=$(echo "$ST" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("last_output",""))' 2>/dev/null)
PR_NUM=$(echo "$WORKER_OUT" | python3 -c '
import sys, re, collections
wo = sys.stdin.read()
repo = "'$REPO'"
nums = re.findall(r"github\.com/%s/pull/(\d+)" % re.escape(repo), wo) if repo else []
if not nums: nums = re.findall(r"/pull/(\d+)", wo)
if not nums: raise SystemExit
cnt = collections.Counter(nums); last = {n: i for i, n in enumerate(nums)}
print(max(set(nums), key=lambda n: (cnt[n], last[n])))
' 2>/dev/null)
# Stop the worker
curl -s --max-time 30 -X POST \
-H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
"${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}/stop" >/dev/null 2>&1 || trueif [ -n "$PR_NUM" ]; then
echo "[pylot] outcome=\"speckit complete: PR #$PR_NUM opened for issue #$0\" status=success"
else
echo "[pylot] outcome=\"speckit complete but no PR URL found — check worker logs\" status=partial"
fibash ~/.claude/skills/speckit-runner/poll-worker.sh "$WID" "$TURN_SEQ"POLL_RESULT=runningtimeout"poll timeout"block_elapsed[pylot] outcome=... status=