File size: 3,463 Bytes
62f3e9a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/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"