mcp / scripts /agent_worker.sh
mikeboone's picture
Claude Fable 5.1
chore(pipeline): the pickup run, worker and tracker CLI are executable
81be5c5
Raw History Blame Contribute Delete
3.46 kB
#!/usr/bin/env bash
# The agent's turn: one task from its queue, one branch, one pull request.
#
# Runs on boone's Mac, by hand or from launchd, under his own git and GitHub
# credentials: the agent has no login of its own. Its branches start with
# agent/, which is what keeps its PRs off the auto-approve path (the pickup
# run merges them only after Mike or Jack approves).
#
# scripts/agent_worker.sh # take the top open task for the agent
# scripts/agent_worker.sh --dry-run # say which task and branch; change nothing
#
# Needs on this Mac: the repo venv, `claude` (Claude Code CLI) and `gh`
# (GitHub CLI, logged in as boone10). Log: logs/agent_worker.log.
set -euo pipefail
REPO="${PIPELINE_REPO:-$(cd "$(dirname "$0")/.." && pwd)}"
PY="$REPO/.venv/bin/python"
CLI="$PY $REPO/scripts/tracker_cli.py"
BASE="${TRACKER_INTEGRATION_BRANCH:-develop}"
MAX_TURNS="${AGENT_MAX_TURNS:-80}"
LOG="$REPO/logs/agent_worker.log"
DRY=0
[[ "${1:-}" == "--dry-run" ]] && DRY=1
for tool in claude gh git; do
command -v "$tool" >/dev/null 2>&1 || { echo "ERROR: $tool is not installed or not on PATH" >&2; exit 2; }
done
[[ -x "$PY" ]] || { echo "ERROR: venv python not found at $PY" >&2; exit 2; }
mkdir -p "$REPO/logs"
cd "$REPO"
AGENT="${TRACKER_AGENT:-$("$PY" -c 'from demoprep_app.tracker import store as S; print(S.agent_username())')}"
TASK_JSON="$($CLI next --owner "$AGENT")"
if [[ -z "$TASK_JSON" || "$TASK_JSON" == "null" ]]; then
echo "no open task for $AGENT"
exit 0
fi
eval "$("$PY" - "$TASK_JSON" <<'PYEOF'
import json, re, shlex, sys
t = json.loads(sys.argv[1])
slug = re.sub(r"[^a-z0-9]+", "-", t["title"].lower()).strip("-")[:48].rstrip("-") or "change"
print(f"TID={t['id']}; TKEY={shlex.quote(t['task'])}; TSTATUS={t['status']}; "
f"TSLUG={slug}; TTITLE={shlex.quote(t['title'])}")
PYEOF
)"
BRANCH="agent/t${TID}-${TSLUG}"
WT="$REPO/pipeline/agent/t${TID}"
echo "task $TKEY ($TSTATUS): $TTITLE -> $BRANCH"
[[ $DRY == 1 ]] && exit 0
git fetch origin --prune
if [[ ! -d "$WT/.git" && ! -f "$WT/.git" ]]; then
git worktree add -B "$BRANCH" "$WT" "origin/$BASE"
fi
if [[ "$TSTATUS" == "open" ]]; then
$CLI update "$TKEY" --by "$AGENT" --status in_progress --note "worker started on $BRANCH" >/dev/null
fi
PROMPT="$(cat "$REPO/scripts/agent_worker_prompt.md")
REPO ROOT (venv and .env live here): $REPO
BRANCH: $BRANCH BASE: $BASE AGENT: $AGENT
TASK (JSON):
$TASK_JSON"
ALLOWED="Read,Edit,Write,Grep,Glob,Bash(git *),Bash($PY *)"
echo "=== $(date '+%F %T') $TKEY on $BRANCH ===" >>"$LOG"
( cd "$WT" && claude -p "$PROMPT" --max-turns "$MAX_TURNS" --allowedTools "$ALLOWED" --output-format text ) \
2>&1 | tee -a "$LOG" || true
AHEAD="$(git -C "$WT" rev-list --count "origin/$BASE..HEAD")"
if [[ "$AHEAD" == "0" ]]; then
$CLI note "$TKEY" --by "$AGENT" --note "worker made no commit on $BRANCH; see logs/agent_worker.log" >/dev/null
echo "no commit; task left as is"
exit 0
fi
git -C "$WT" push -u origin "$BRANCH"
ORIGIN_REPO="$(git remote get-url origin | sed -E 's#.*github\.com[:/]##; s#\.git$##')"
PR_URL="$(gh pr create --repo "$ORIGIN_REPO" --base "$BASE" --head "$BRANCH" \
--title "$TKEY: $TTITLE" \
--body "Opened by the agent ($AGENT) for $TKEY. Agent branches are never auto-approved: Mike or Jack reviews before the pickup run merges it." \
| tail -1)"
$CLI update "$TKEY" --by "$AGENT" --status review --pr-url "$PR_URL" --note "PR opened by the worker" >/dev/null
echo "PR: $PR_URL"