Ashira Pitchayapakayakul commited on
Commit
633a37a
Β·
1 Parent(s): 611f000

Fix: hardcoded /usr/bin/* + Mac /opt/homebrew/* -> PATH-based for Linux container

Browse files
bin/auto-orchestrate-loop.sh CHANGED
@@ -21,7 +21,7 @@ if [[ $LOAD -gt 8 ]] || [[ $FREE -lt 50000 ]]; then
21
  fi
22
 
23
  # Pick a real task: one TODO/FIXME from a randomly-chosen project
24
- TASK_INFO=$(/opt/homebrew/bin/python3 <<'PYEOF'
25
  import os, random, re, subprocess, json
26
  from pathlib import Path
27
 
@@ -38,7 +38,7 @@ if not PROJECTS:
38
 
39
  random.shuffle(PROJECTS)
40
  for proj in PROJECTS:
41
- cmd = ['/opt/homebrew/bin/rg', '--no-heading', '-n', '-m', '5',
42
  '--type', 'py', '--type', 'ts', '--type', 'go', '--type', 'sh',
43
  '-g', '!node_modules', '-g', '!.venv', '-g', '!__pycache__',
44
  '-g', '!.git', '-g', '!dist', '-g', '!build',
@@ -77,11 +77,11 @@ if [[ -z "$TASK_INFO" ]] || [[ "$TASK_INFO" == "{}" ]]; then
77
  exit 0
78
  fi
79
 
80
- PROJECT=$(echo "$TASK_INFO" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read())['project'])")
81
- PROJ_NAME=$(echo "$TASK_INFO" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read())['project_name'])")
82
- FILE=$(echo "$TASK_INFO" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read())['file'])")
83
- LINE=$(echo "$TASK_INFO" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read())['line'])")
84
- CONTENT=$(echo "$TASK_INFO" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read())['content'])")
85
 
86
  # Per-task throttle: don't redo same TODO within 4 hours
87
  TASK_HASH=$(echo "${PROJ_NAME}:${FILE}:${LINE}" | md5 | cut -c1-12)
 
21
  fi
22
 
23
  # Pick a real task: one TODO/FIXME from a randomly-chosen project
24
+ TASK_INFO=$(python3 <<'PYEOF'
25
  import os, random, re, subprocess, json
26
  from pathlib import Path
27
 
 
38
 
39
  random.shuffle(PROJECTS)
40
  for proj in PROJECTS:
41
+ cmd = ['rg', '--no-heading', '-n', '-m', '5',
42
  '--type', 'py', '--type', 'ts', '--type', 'go', '--type', 'sh',
43
  '-g', '!node_modules', '-g', '!.venv', '-g', '!__pycache__',
44
  '-g', '!.git', '-g', '!dist', '-g', '!build',
 
77
  exit 0
78
  fi
79
 
80
+ PROJECT=$(echo "$TASK_INFO" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['project'])")
81
+ PROJ_NAME=$(echo "$TASK_INFO" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['project_name'])")
82
+ FILE=$(echo "$TASK_INFO" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['file'])")
83
+ LINE=$(echo "$TASK_INFO" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['line'])")
84
+ CONTENT=$(echo "$TASK_INFO" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['content'])")
85
 
86
  # Per-task throttle: don't redo same TODO within 4 hours
87
  TASK_HASH=$(echo "${PROJ_NAME}:${FILE}:${LINE}" | md5 | cut -c1-12)
bin/dev-cloud-daemon.sh CHANGED
@@ -13,7 +13,7 @@ mkdir -p "$(dirname "$LOG")"
13
 
14
  # Redis connection: prefer Unix socket, fall back to TCP 127.0.0.1:6379.
15
  # REDIS_CLI_ARGS populates either "-s /path/to/socket" or "-h 127.0.0.1 -p 6379".
16
- REDIS_SOCK=$(/usr/bin/find /var/folders /tmp -name 'redis.socket' -type s 2>/dev/null | /usr/bin/head -1)
17
  if [[ -n "$REDIS_SOCK" ]] && [[ -S "$REDIS_SOCK" ]]; then
18
  REDIS_CLI_ARGS=(-s "$REDIS_SOCK")
19
  elif redis-cli -h 127.0.0.1 -p 6379 ping 2>/dev/null | grep -q PONG; then
@@ -29,7 +29,7 @@ while true; do
29
  # Budget-aware: check token budget before processing
30
  BUDGET_FILE="$HOME/.hermes/workspace/budget/tokens-$(/bin/date +%Y-%m-%d).json"
31
  if [[ -f "$BUDGET_FILE" ]]; then
32
- STATUS=$(/usr/bin/python3 -c "
33
  import json
34
  try:
35
  d = json.load(open('$BUDGET_FILE'))
@@ -45,10 +45,10 @@ except: print('OK')" 2>/dev/null)
45
  RESULT=$(redis-cli "${REDIS_CLI_ARGS[@]}" BLPOP "hermes:work:coding:$PROVIDER" 30 2>/dev/null)
46
  [[ -z "$RESULT" ]] && continue
47
 
48
- PAYLOAD=$(echo "$RESULT" | /usr/bin/tail -1)
49
  [[ -z "$PAYLOAD" ]] && continue
50
 
51
- PRIO_ID=$(echo "$PAYLOAD" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read())['id'])" 2>/dev/null)
52
  [[ -z "$PRIO_ID" ]] && continue
53
 
54
  # Worker lock (provider-specific so 6 daemons can work in parallel on same queue)
@@ -65,7 +65,7 @@ except: print('OK')" 2>/dev/null)
65
  # and works on exactly what the daemon locked (avoids "no free priority"
66
  # dead-ends when the file lock was touched earlier for this same PRIO_ID).
67
  HERMES_PRIO_ID="$PRIO_ID" \
68
- "$HOME/.claude/bin/dev-cloud-worker.sh" "$PROVIDER" 2>&1 | /usr/bin/tail -3 >> "$LOG"
69
  RC=${PIPESTATUS[0]}
70
  DUR=$(( $(date +%s) - START ))
71
  echo "[$(date '+%H:%M:%S')] $PROVIDER $PRIO_ID done in ${DUR}s (rc=$RC)" >> "$LOG"
 
13
 
14
  # Redis connection: prefer Unix socket, fall back to TCP 127.0.0.1:6379.
15
  # REDIS_CLI_ARGS populates either "-s /path/to/socket" or "-h 127.0.0.1 -p 6379".
16
+ REDIS_SOCK=$(find /var/folders /tmp -name 'redis.socket' -type s 2>/dev/null | head -1)
17
  if [[ -n "$REDIS_SOCK" ]] && [[ -S "$REDIS_SOCK" ]]; then
18
  REDIS_CLI_ARGS=(-s "$REDIS_SOCK")
19
  elif redis-cli -h 127.0.0.1 -p 6379 ping 2>/dev/null | grep -q PONG; then
 
29
  # Budget-aware: check token budget before processing
30
  BUDGET_FILE="$HOME/.hermes/workspace/budget/tokens-$(/bin/date +%Y-%m-%d).json"
31
  if [[ -f "$BUDGET_FILE" ]]; then
32
+ STATUS=$(python3 -c "
33
  import json
34
  try:
35
  d = json.load(open('$BUDGET_FILE'))
 
45
  RESULT=$(redis-cli "${REDIS_CLI_ARGS[@]}" BLPOP "hermes:work:coding:$PROVIDER" 30 2>/dev/null)
46
  [[ -z "$RESULT" ]] && continue
47
 
48
+ PAYLOAD=$(echo "$RESULT" | tail -1)
49
  [[ -z "$PAYLOAD" ]] && continue
50
 
51
+ PRIO_ID=$(echo "$PAYLOAD" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['id'])" 2>/dev/null)
52
  [[ -z "$PRIO_ID" ]] && continue
53
 
54
  # Worker lock (provider-specific so 6 daemons can work in parallel on same queue)
 
65
  # and works on exactly what the daemon locked (avoids "no free priority"
66
  # dead-ends when the file lock was touched earlier for this same PRIO_ID).
67
  HERMES_PRIO_ID="$PRIO_ID" \
68
+ "$HOME/.claude/bin/dev-cloud-worker.sh" "$PROVIDER" 2>&1 | tail -3 >> "$LOG"
69
  RC=${PIPESTATUS[0]}
70
  DUR=$(( $(date +%s) - START ))
71
  echo "[$(date '+%H:%M:%S')] $PROVIDER $PRIO_ID done in ${DUR}s (rc=$RC)" >> "$LOG"
bin/github-domain-scrape.sh CHANGED
@@ -15,7 +15,7 @@ mkdir -p "$(dirname "$LOG")" "$(dirname "$OUT")"
15
  TARGET="${1:-}"
16
  export LEDGER OUT GITHUB_TOKEN GITHUB_TOKEN_POOL TARGET
17
 
18
- /opt/homebrew/bin/python3 <<'PYEOF' 2>&1 | tee -a "$LOG"
19
  import os, json, urllib.request, urllib.parse, re, time, base64, sqlite3, random
20
  from datetime import datetime
21
  from pathlib import Path
 
15
  TARGET="${1:-}"
16
  export LEDGER OUT GITHUB_TOKEN GITHUB_TOKEN_POOL TARGET
17
 
18
+ python3 <<'PYEOF' 2>&1 | tee -a "$LOG"
19
  import os, json, urllib.request, urllib.parse, re, time, base64, sqlite3, random
20
  from datetime import datetime
21
  from pathlib import Path
bin/notify-discord.sh CHANGED
@@ -39,7 +39,7 @@ esac
39
 
40
  # Truncate body to Discord embed limit (4096) and escape JSON
41
  BODY_TRUNC="${BODY:0:3900}"
42
- BODY_JSON=$(/usr/bin/python3 -c "
43
  import json, sys
44
  print(json.dumps(sys.argv[1]))
45
  " "$BODY_TRUNC")
 
39
 
40
  # Truncate body to Discord embed limit (4096) and escape JSON
41
  BODY_TRUNC="${BODY:0:3900}"
42
+ BODY_JSON=$(python3 -c "
43
  import json, sys
44
  print(json.dumps(sys.argv[1]))
45
  " "$BODY_TRUNC")
bin/qwen-coder-daemon.sh CHANGED
@@ -8,7 +8,7 @@ LOG="$HOME/.claude/logs/qwen-coder-daemon.log"
8
  mkdir -p "$(dirname "$LOG")"
9
 
10
  # Resolve Redis: Unix socket β†’ TCP fallback. Build a redis-cli arg array reused below.
11
- REDIS_SOCK=$(/usr/bin/find /var/folders /tmp -name 'redis.socket' -type s 2>/dev/null | /usr/bin/head -1)
12
  if [[ -n "$REDIS_SOCK" ]] && [[ -S "$REDIS_SOCK" ]]; then
13
  RCLI=(redis-cli -s "$REDIS_SOCK")
14
  elif redis-cli -h 127.0.0.1 -p 6379 PING 2>/dev/null | grep -q PONG; then
@@ -26,10 +26,10 @@ while true; do
26
  RESULT=$("${RCLI[@]}" BLPOP 'hermes:work:coding:qwen-local' 30 2>/dev/null)
27
  [[ -z "$RESULT" ]] && continue
28
 
29
- PAYLOAD=$(echo "$RESULT" | /usr/bin/tail -1)
30
  [[ -z "$PAYLOAD" ]] && continue
31
 
32
- PRIO_ID=$(echo "$PAYLOAD" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read())['id'])" 2>/dev/null)
33
  [[ -z "$PRIO_ID" ]] && continue
34
 
35
  echo "[$(date '+%H:%M:%S')] pulled $PRIO_ID β€” processing" >> "$LOG"
@@ -45,7 +45,7 @@ while true; do
45
  # can't race with other workers / stale file locks.
46
  START=$(date +%s)
47
  HERMES_PRIO_ID="$PRIO_ID" \
48
- "$HOME/.claude/bin/qwen-coder-worker.sh" 2>&1 | /usr/bin/tail -3 >> "$LOG"
49
  DUR=$(( $(date +%s) - START ))
50
  echo "[$(date '+%H:%M:%S')] $PRIO_ID done in ${DUR}s" >> "$LOG"
51
 
 
8
  mkdir -p "$(dirname "$LOG")"
9
 
10
  # Resolve Redis: Unix socket β†’ TCP fallback. Build a redis-cli arg array reused below.
11
+ REDIS_SOCK=$(find /var/folders /tmp -name 'redis.socket' -type s 2>/dev/null | head -1)
12
  if [[ -n "$REDIS_SOCK" ]] && [[ -S "$REDIS_SOCK" ]]; then
13
  RCLI=(redis-cli -s "$REDIS_SOCK")
14
  elif redis-cli -h 127.0.0.1 -p 6379 PING 2>/dev/null | grep -q PONG; then
 
26
  RESULT=$("${RCLI[@]}" BLPOP 'hermes:work:coding:qwen-local' 30 2>/dev/null)
27
  [[ -z "$RESULT" ]] && continue
28
 
29
+ PAYLOAD=$(echo "$RESULT" | tail -1)
30
  [[ -z "$PAYLOAD" ]] && continue
31
 
32
+ PRIO_ID=$(echo "$PAYLOAD" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['id'])" 2>/dev/null)
33
  [[ -z "$PRIO_ID" ]] && continue
34
 
35
  echo "[$(date '+%H:%M:%S')] pulled $PRIO_ID β€” processing" >> "$LOG"
 
45
  # can't race with other workers / stale file locks.
46
  START=$(date +%s)
47
  HERMES_PRIO_ID="$PRIO_ID" \
48
+ "$HOME/.claude/bin/qwen-coder-worker.sh" 2>&1 | tail -3 >> "$LOG"
49
  DUR=$(( $(date +%s) - START ))
50
  echo "[$(date '+%H:%M:%S')] $PRIO_ID done in ${DUR}s" >> "$LOG"
51
 
bin/qwen-coder-worker.sh CHANGED
@@ -53,25 +53,25 @@ echo "[$(date '+%H:%M:%S')] $PRIO_ID start ($PRIO_PROJECT: $PRIO_TITLE)" >> "$LO
53
  # -------- Context: repo map + RAG-grounded code examples (anti-hallucination) --------
54
  REPO_MAP=""
55
  MAP_FILE="$SHARED/repo-maps/${PRIO_PROJECT}.md"
56
- [[ -f "$MAP_FILE" ]] && REPO_MAP=$(/usr/bin/head -c 3000 "$MAP_FILE")
57
 
58
  # RAG: fetch real code examples from THIS project's actual codebase via FTS
59
  # Grounds the model in real APIs/imports/patterns instead of hallucinating
60
  RAG_EXAMPLES=""
61
  if [[ -x "$HOME/.claude/bin/ask-sqlite.py" ]]; then
62
- RAG_EXAMPLES=$(/usr/bin/python3 "$HOME/.claude/bin/ask-sqlite.py" \
63
- "$PRIO_PROJECT $PRIO_TITLE" 2>/dev/null | /usr/bin/head -c 2500)
64
  fi
65
 
66
  # Few-shot: 1 recent ACCEPTED output (quality >=7) as anti-hallucination anchor
67
  FEWSHOT=""
68
- for review in $(ls -t "$HOME/.hermes/workspace/qwen-coder-reviews/"*.json 2>/dev/null | /usr/bin/head -20); do
69
- if /usr/bin/grep -l '"quality_score": *[789]' "$review" > /dev/null 2>&1 || \
70
- /usr/bin/grep -l '"quality_score": *10' "$review" > /dev/null 2>&1; then
71
  OUT_FILE=$(basename "$review" .review.json)
72
  OUT_PATH="$HOME/.hermes/workspace/qwen-coder/${OUT_FILE}.md"
73
  if [[ -f "$OUT_PATH" ]]; then
74
- FEWSHOT=$(/usr/bin/head -c 1500 "$OUT_PATH")
75
  break
76
  fi
77
  fi
@@ -79,8 +79,8 @@ done
79
 
80
  # Inject recent REJECTIONS as anti-patterns (what NOT to do) β€” last 3 rejected reasons
81
  ANTI_PATTERNS=""
82
- for review in $(ls -t "$HOME/.hermes/workspace/qwen-coder-reviews/"*.json 2>/dev/null | /usr/bin/head -10); do
83
- bugs=$(/usr/bin/python3 -c "
84
  import json, sys, re
85
  try:
86
  txt = open('$review').read()
@@ -94,7 +94,7 @@ except: pass
94
  " 2>/dev/null)
95
  [[ -n "$bugs" ]] && ANTI_PATTERNS="$ANTI_PATTERNS$bugs"$'\n'
96
  done
97
- ANTI_PATTERNS=$(echo "$ANTI_PATTERNS" | /usr/bin/head -8)
98
 
99
  PROMPT=$(cat <<EOF
100
  You are qwen-coder (local, always-on). Implement this priority.
@@ -169,7 +169,7 @@ body = {
169
  print(json.dumps(body))
170
  PYEOF
171
  )
172
- RESP=$(/usr/bin/curl -sS --max-time 180 \
173
  http://localhost:11434/v1/chat/completions \
174
  -H 'Content-Type: application/json' \
175
  -d "$BODY" 2>>"$LOG")
 
53
  # -------- Context: repo map + RAG-grounded code examples (anti-hallucination) --------
54
  REPO_MAP=""
55
  MAP_FILE="$SHARED/repo-maps/${PRIO_PROJECT}.md"
56
+ [[ -f "$MAP_FILE" ]] && REPO_MAP=$(head -c 3000 "$MAP_FILE")
57
 
58
  # RAG: fetch real code examples from THIS project's actual codebase via FTS
59
  # Grounds the model in real APIs/imports/patterns instead of hallucinating
60
  RAG_EXAMPLES=""
61
  if [[ -x "$HOME/.claude/bin/ask-sqlite.py" ]]; then
62
+ RAG_EXAMPLES=$(python3 "$HOME/.claude/bin/ask-sqlite.py" \
63
+ "$PRIO_PROJECT $PRIO_TITLE" 2>/dev/null | head -c 2500)
64
  fi
65
 
66
  # Few-shot: 1 recent ACCEPTED output (quality >=7) as anti-hallucination anchor
67
  FEWSHOT=""
68
+ for review in $(ls -t "$HOME/.hermes/workspace/qwen-coder-reviews/"*.json 2>/dev/null | head -20); do
69
+ if grep -l '"quality_score": *[789]' "$review" > /dev/null 2>&1 || \
70
+ grep -l '"quality_score": *10' "$review" > /dev/null 2>&1; then
71
  OUT_FILE=$(basename "$review" .review.json)
72
  OUT_PATH="$HOME/.hermes/workspace/qwen-coder/${OUT_FILE}.md"
73
  if [[ -f "$OUT_PATH" ]]; then
74
+ FEWSHOT=$(head -c 1500 "$OUT_PATH")
75
  break
76
  fi
77
  fi
 
79
 
80
  # Inject recent REJECTIONS as anti-patterns (what NOT to do) β€” last 3 rejected reasons
81
  ANTI_PATTERNS=""
82
+ for review in $(ls -t "$HOME/.hermes/workspace/qwen-coder-reviews/"*.json 2>/dev/null | head -10); do
83
+ bugs=$(python3 -c "
84
  import json, sys, re
85
  try:
86
  txt = open('$review').read()
 
94
  " 2>/dev/null)
95
  [[ -n "$bugs" ]] && ANTI_PATTERNS="$ANTI_PATTERNS$bugs"$'\n'
96
  done
97
+ ANTI_PATTERNS=$(echo "$ANTI_PATTERNS" | head -8)
98
 
99
  PROMPT=$(cat <<EOF
100
  You are qwen-coder (local, always-on). Implement this priority.
 
169
  print(json.dumps(body))
170
  PYEOF
171
  )
172
+ RESP=$(curl -sS --max-time 180 \
173
  http://localhost:11434/v1/chat/completions \
174
  -H 'Content-Type: application/json' \
175
  -d "$BODY" 2>>"$LOG")
bin/scrape-keyword-tuner.sh CHANGED
@@ -29,7 +29,7 @@ START=$(date +%s)
29
  echo "[$(date +%H:%M:%S)] tune cycle start" >> "$LOG"
30
 
31
  # Iterate slots, focus on ones with consecutive_zero_runs > 0 first
32
- /opt/homebrew/bin/python3 <<PYEOF >> "$LOG" 2>&1
33
  import os, re, json, sqlite3, time, urllib.request, urllib.error, urllib.parse
34
 
35
  TOKEN = "$TOKEN"
 
29
  echo "[$(date +%H:%M:%S)] tune cycle start" >> "$LOG"
30
 
31
  # Iterate slots, focus on ones with consecutive_zero_runs > 0 first
32
+ python3 <<PYEOF >> "$LOG" 2>&1
33
  import os, re, json, sqlite3, time, urllib.request, urllib.error, urllib.parse
34
 
35
  TOKEN = "$TOKEN"
bin/surrogate CHANGED
@@ -199,7 +199,7 @@ run_agent() {
199
  export AGENT_EFFORT="$EFFORT"
200
  export AGENT_CWD="$(pwd)"
201
 
202
- /usr/bin/python3 <<'PYEOF'
203
  import os, sys, json, re, sqlite3, subprocess, urllib.request, urllib.error, time
204
  from datetime import datetime
205
  from pathlib import Path
@@ -460,7 +460,7 @@ repl() {
460
  ;;
461
  /memory) ls -lh ~/.claude/state/surrogate-memory/ 2>&1 | head -10 ;;
462
  /cost)
463
- bash -c 'source ~/.hermes/.env; curl -s -H "Authorization: Bearer $OPENROUTER_API_KEY" https://openrouter.ai/api/v1/auth/key' 2>&1 | /usr/bin/python3 -c "import json,sys; d=json.load(sys.stdin).get('data',{}); print(f' OpenRouter: \${d.get(\"usage\",0):.4f}')"
464
  ;;
465
  /*) echo "${GY}unknown: $line β€” try /help${R}" ;;
466
  *) run_agent "$line" ;;
@@ -500,7 +500,7 @@ auto_dev_mode() {
500
  # Drive tasks from plan until all done
501
  while true; do
502
  # Pop next pending task from plan
503
- NEXT_TASK=$(/usr/bin/python3 <<'PYEOF'
504
  import sys, re
505
  from pathlib import Path
506
  plan_file = Path.home() / '.surrogate' / 'active-plan.md'
@@ -519,7 +519,7 @@ PYEOF
519
  echo "${BCY}${B}β–Έ Next task:${R} $NEXT_TASK"
520
  bash ~/.claude/bin/surrogate-orchestrate.sh "$NEXT_TASK"
521
  # Mark done in plan
522
- /usr/bin/python3 <<PYEOF
523
  from pathlib import Path
524
  plan_file = Path.home() / '.surrogate' / 'active-plan.md'
525
  if plan_file.exists():
 
199
  export AGENT_EFFORT="$EFFORT"
200
  export AGENT_CWD="$(pwd)"
201
 
202
+ python3 <<'PYEOF'
203
  import os, sys, json, re, sqlite3, subprocess, urllib.request, urllib.error, time
204
  from datetime import datetime
205
  from pathlib import Path
 
460
  ;;
461
  /memory) ls -lh ~/.claude/state/surrogate-memory/ 2>&1 | head -10 ;;
462
  /cost)
463
+ bash -c 'source ~/.hermes/.env; curl -s -H "Authorization: Bearer $OPENROUTER_API_KEY" https://openrouter.ai/api/v1/auth/key' 2>&1 | python3 -c "import json,sys; d=json.load(sys.stdin).get('data',{}); print(f' OpenRouter: \${d.get(\"usage\",0):.4f}')"
464
  ;;
465
  /*) echo "${GY}unknown: $line β€” try /help${R}" ;;
466
  *) run_agent "$line" ;;
 
500
  # Drive tasks from plan until all done
501
  while true; do
502
  # Pop next pending task from plan
503
+ NEXT_TASK=$(python3 <<'PYEOF'
504
  import sys, re
505
  from pathlib import Path
506
  plan_file = Path.home() / '.surrogate' / 'active-plan.md'
 
519
  echo "${BCY}${B}β–Έ Next task:${R} $NEXT_TASK"
520
  bash ~/.claude/bin/surrogate-orchestrate.sh "$NEXT_TASK"
521
  # Mark done in plan
522
+ python3 <<PYEOF
523
  from pathlib import Path
524
  plan_file = Path.home() / '.surrogate' / 'active-plan.md'
525
  if plan_file.exists():
bin/surrogate-agent.sh CHANGED
@@ -40,7 +40,7 @@ export AGENT_TASK="$TASK"
40
  export AGENT_MAX_STEPS="$MAX_STEPS"
41
  export AGENT_MODEL_OVERRIDE="$MODEL_OVERRIDE"
42
 
43
- /usr/bin/python3 <<'PYEOF'
44
  import sys, os, json, re, sqlite3, subprocess, urllib.request, urllib.error, time, uuid
45
  from datetime import datetime
46
  from pathlib import Path
@@ -138,7 +138,7 @@ def tool_glob(pattern, path='.'):
138
  def tool_grep(pattern, path='.', glob='*', context=0):
139
  base = os.path.expanduser(path)
140
  ctx = f'-C {context}' if context else ''
141
- cmd = f"/usr/bin/grep -rn {ctx} --include='{glob}' -E {subprocess.list2cmdline([pattern])} {base} 2>/dev/null | head -50"
142
  r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=15)
143
  return {'matches': r.stdout[:5000]}
144
 
 
40
  export AGENT_MAX_STEPS="$MAX_STEPS"
41
  export AGENT_MODEL_OVERRIDE="$MODEL_OVERRIDE"
42
 
43
+ python3 <<'PYEOF'
44
  import sys, os, json, re, sqlite3, subprocess, urllib.request, urllib.error, time, uuid
45
  from datetime import datetime
46
  from pathlib import Path
 
138
  def tool_grep(pattern, path='.', glob='*', context=0):
139
  base = os.path.expanduser(path)
140
  ctx = f'-C {context}' if context else ''
141
+ cmd = f"grep -rn {ctx} --include='{glob}' -E {subprocess.list2cmdline([pattern])} {base} 2>/dev/null | head -50"
142
  r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=15)
143
  return {'matches': r.stdout[:5000]}
144
 
bin/surrogate-daemon.sh CHANGED
@@ -33,7 +33,7 @@ case "$CMD" in
33
  shift
34
  TASK="$*"
35
  [[ -z "$TASK" ]] && { echo "need task"; exit 2; }
36
- /usr/bin/python3 -c "
37
  import json, uuid
38
  from datetime import datetime
39
  task = {'id': uuid.uuid4().hex[:12], 'ts': datetime.utcnow().isoformat(), 'task': '''$TASK''', 'status': 'pending', 'priority': 'P0-user'}
@@ -152,7 +152,7 @@ print(f\"enqueued: {task['id']} {task['task'][:60]}\")
152
  _worker)
153
  # ── Pop one task from queue (P0-user first, then plan, then self-gen) ──────
154
  _pop_queue() {
155
- /usr/bin/python3 <<PYEOF
156
  import json, os, sys, fcntl
157
  from pathlib import Path
158
  q = Path(os.path.expanduser('$QUEUE'))
@@ -181,7 +181,7 @@ PYEOF
181
 
182
  # ── Pop next task from active plan (no sleep needed β€” plan drives work) ──
183
  _pop_plan() {
184
- /usr/bin/python3 <<'PYEOF'
185
  import sys, json, os, re, uuid
186
  from pathlib import Path
187
  from datetime import datetime
@@ -216,7 +216,7 @@ PYEOF
216
 
217
  # ── Self-generate task from pool (fallback when no plan + queue empty) ──
218
  _self_gen() {
219
- AUTO_TASK=$(/usr/bin/python3 <<'PYEOF'
220
  import json, os, random
221
  from pathlib import Path
222
  ep = Path(os.path.expanduser('~/.claude/state/surrogate-memory/episodes.jsonl'))
@@ -271,7 +271,7 @@ for t in random.sample(pool, len(pool)):
271
  print(chosen or pool[0])
272
  PYEOF
273
  )
274
- echo "{\"id\":\"auto-$(/usr/bin/python3 -c 'import uuid; print(uuid.uuid4().hex[:8])')\",\"task\":\"$AUTO_TASK\",\"self_generated\":true,\"source\":\"self-gen\"}"
275
  }
276
 
277
  # ── Task resolution: queue β†’ plan β†’ self-gen (no 60s sleep) ─────────────
@@ -290,9 +290,9 @@ PYEOF
290
  fi
291
 
292
  # Extract task
293
- TASK=$(echo "$TASK_JSON" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read())['task'])")
294
- TID=$(echo "$TASK_JSON" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read())['id'])")
295
- SOURCE=$(echo "$TASK_JSON" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('source','queue'))")
296
 
297
  echo "[$(date +%H:%M:%S)] worker picked $TID [$SOURCE]: ${TASK:0:80}" >> "$LOG"
298
  START=$(date +%s)
@@ -304,7 +304,7 @@ PYEOF
304
 
305
  # If task came from plan, mark as done ([ ] β†’ [x])
306
  if [[ "$SOURCE" == "plan" ]]; then
307
- /usr/bin/python3 <<PYEOF >> "$LOG" 2>&1
308
  import re
309
  from pathlib import Path
310
  plan_file = Path.home() / '.surrogate' / 'active-plan.md'
@@ -321,9 +321,9 @@ PYEOF
321
  fi
322
 
323
  # Mark done in audit log
324
- /usr/bin/python3 <<PYEOF >> "$LOG" 2>&1
325
  import json
326
- done = {'id': '$TID', 'source': '$SOURCE', 'task': '''$TASK''', 'duration_sec': $DUR, 'output_tail': '''$(echo "$OUTPUT" | tail -20 | /usr/bin/python3 -c "import sys; print(sys.stdin.read().replace(chr(39),chr(34))[:2000])")'''}
327
  open('$DONE','a').write(json.dumps(done, ensure_ascii=False) + '\n')
328
  print(f"[{'$TID'}] done in {$DUR}s")
329
  PYEOF
 
33
  shift
34
  TASK="$*"
35
  [[ -z "$TASK" ]] && { echo "need task"; exit 2; }
36
+ python3 -c "
37
  import json, uuid
38
  from datetime import datetime
39
  task = {'id': uuid.uuid4().hex[:12], 'ts': datetime.utcnow().isoformat(), 'task': '''$TASK''', 'status': 'pending', 'priority': 'P0-user'}
 
152
  _worker)
153
  # ── Pop one task from queue (P0-user first, then plan, then self-gen) ──────
154
  _pop_queue() {
155
+ python3 <<PYEOF
156
  import json, os, sys, fcntl
157
  from pathlib import Path
158
  q = Path(os.path.expanduser('$QUEUE'))
 
181
 
182
  # ── Pop next task from active plan (no sleep needed β€” plan drives work) ──
183
  _pop_plan() {
184
+ python3 <<'PYEOF'
185
  import sys, json, os, re, uuid
186
  from pathlib import Path
187
  from datetime import datetime
 
216
 
217
  # ── Self-generate task from pool (fallback when no plan + queue empty) ──
218
  _self_gen() {
219
+ AUTO_TASK=$(python3 <<'PYEOF'
220
  import json, os, random
221
  from pathlib import Path
222
  ep = Path(os.path.expanduser('~/.claude/state/surrogate-memory/episodes.jsonl'))
 
271
  print(chosen or pool[0])
272
  PYEOF
273
  )
274
+ echo "{\"id\":\"auto-$(python3 -c 'import uuid; print(uuid.uuid4().hex[:8])')\",\"task\":\"$AUTO_TASK\",\"self_generated\":true,\"source\":\"self-gen\"}"
275
  }
276
 
277
  # ── Task resolution: queue β†’ plan β†’ self-gen (no 60s sleep) ─────────────
 
290
  fi
291
 
292
  # Extract task
293
+ TASK=$(echo "$TASK_JSON" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['task'])")
294
+ TID=$(echo "$TASK_JSON" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['id'])")
295
+ SOURCE=$(echo "$TASK_JSON" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('source','queue'))")
296
 
297
  echo "[$(date +%H:%M:%S)] worker picked $TID [$SOURCE]: ${TASK:0:80}" >> "$LOG"
298
  START=$(date +%s)
 
304
 
305
  # If task came from plan, mark as done ([ ] β†’ [x])
306
  if [[ "$SOURCE" == "plan" ]]; then
307
+ python3 <<PYEOF >> "$LOG" 2>&1
308
  import re
309
  from pathlib import Path
310
  plan_file = Path.home() / '.surrogate' / 'active-plan.md'
 
321
  fi
322
 
323
  # Mark done in audit log
324
+ python3 <<PYEOF >> "$LOG" 2>&1
325
  import json
326
+ done = {'id': '$TID', 'source': '$SOURCE', 'task': '''$TASK''', 'duration_sec': $DUR, 'output_tail': '''$(echo "$OUTPUT" | tail -20 | python3 -c "import sys; print(sys.stdin.read().replace(chr(39),chr(34))[:2000])")'''}
327
  open('$DONE','a').write(json.dumps(done, ensure_ascii=False) + '\n')
328
  print(f"[{'$TID'}] done in {$DUR}s")
329
  PYEOF
bin/surrogate-dev-loop.sh CHANGED
@@ -33,7 +33,7 @@ SEARCH_ROOTS=(
33
 
34
  # ── Task generators (pick one per cycle, weighted random) ────────────────────
35
  pick_task() {
36
- /usr/bin/python3 <<'PYEOF'
37
  import os, random, re, subprocess, json
38
  from pathlib import Path
39
 
@@ -47,7 +47,7 @@ ROOTS = [p for p in ROOTS if p.exists()]
47
 
48
  def find_todo():
49
  """Find a TODO/FIXME/XXX/HACK comment in user code (uses ripgrep β€” fast)."""
50
- cmd = ['/opt/homebrew/bin/rg', '--no-heading', '-n', '-m', '3',
51
  '--type', 'py', '--type', 'sh', '--type', 'ts', '--type', 'go',
52
  '-g', '!node_modules', '-g', '!.venv', '-g', '!__pycache__',
53
  '-g', '!.git', '-g', '!dist', '-g', '!build',
@@ -178,7 +178,7 @@ load_reflexion_lessons() {
178
  local kind="$1"
179
  local file="$HOME/.hermes/workspace/reflexion/lessons-${kind}.jsonl"
180
  [[ ! -f "$file" ]] && { echo ""; return; }
181
- /usr/bin/python3 <<PYEOF
182
  import json
183
  from pathlib import Path
184
  p = Path("$file")
@@ -209,7 +209,7 @@ save_reflexion_lesson() {
209
  local kind="$1" task="$2" response="$3" duration="$4"
210
  local file="$HOME/.hermes/workspace/reflexion/lessons-${kind}.jsonl"
211
  mkdir -p "$(dirname "$file")"
212
- /usr/bin/python3 <<PYEOF
213
  import json, re, sys
214
  from pathlib import Path
215
  from datetime import datetime
@@ -259,11 +259,11 @@ run_cycle() {
259
  fi
260
 
261
  local kind path line task_text context
262
- kind=$(echo "$task_json" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('kind',''))")
263
- path=$(echo "$task_json" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('path',''))")
264
- line=$(echo "$task_json" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('line',0))")
265
- task_text=$(echo "$task_json" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('task',''))")
266
- context=$(echo "$task_json" | /usr/bin/python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('context',''))")
267
 
268
  local id="$(date +%s)-${kind}"
269
  local out="$OUT_DIR/${id}.md"
@@ -285,7 +285,7 @@ $context
285
 
286
  # Call Surrogate-1 via Ollama (keep_alive=5m so model stays warm between cycles)
287
  local body
288
- body=$(PROMPT_VAR="$prompt" /usr/bin/python3 <<'PYEOF'
289
  import json, os
290
  print(json.dumps({
291
  "model": "surrogate-1",
@@ -298,13 +298,13 @@ print(json.dumps({
298
  PYEOF
299
  )
300
  local resp
301
- resp=$(/usr/bin/curl -sS --max-time 120 \
302
  http://localhost:11434/v1/chat/completions \
303
  -H 'Content-Type: application/json' \
304
  -d "$body" 2>/dev/null)
305
 
306
  local answer
307
- answer=$(echo "$resp" | /usr/bin/python3 -c "
308
  import json, sys
309
  try:
310
  d = json.load(sys.stdin)
@@ -342,7 +342,7 @@ EOF
342
  save_reflexion_lesson "$kind" "$task_text" "$answer" "$dur"
343
 
344
  # Append to training-data candidate (will be reviewed before promoting to JSONL)
345
- /usr/bin/python3 <<PYEOF
346
  import json
347
  from pathlib import Path
348
  candidate = Path.home() / 'axentx/surrogate/data/training-jsonl/local-dev-pending.jsonl'
 
33
 
34
  # ── Task generators (pick one per cycle, weighted random) ────────────────────
35
  pick_task() {
36
+ python3 <<'PYEOF'
37
  import os, random, re, subprocess, json
38
  from pathlib import Path
39
 
 
47
 
48
  def find_todo():
49
  """Find a TODO/FIXME/XXX/HACK comment in user code (uses ripgrep β€” fast)."""
50
+ cmd = ['rg', '--no-heading', '-n', '-m', '3',
51
  '--type', 'py', '--type', 'sh', '--type', 'ts', '--type', 'go',
52
  '-g', '!node_modules', '-g', '!.venv', '-g', '!__pycache__',
53
  '-g', '!.git', '-g', '!dist', '-g', '!build',
 
178
  local kind="$1"
179
  local file="$HOME/.hermes/workspace/reflexion/lessons-${kind}.jsonl"
180
  [[ ! -f "$file" ]] && { echo ""; return; }
181
+ python3 <<PYEOF
182
  import json
183
  from pathlib import Path
184
  p = Path("$file")
 
209
  local kind="$1" task="$2" response="$3" duration="$4"
210
  local file="$HOME/.hermes/workspace/reflexion/lessons-${kind}.jsonl"
211
  mkdir -p "$(dirname "$file")"
212
+ python3 <<PYEOF
213
  import json, re, sys
214
  from pathlib import Path
215
  from datetime import datetime
 
259
  fi
260
 
261
  local kind path line task_text context
262
+ kind=$(echo "$task_json" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('kind',''))")
263
+ path=$(echo "$task_json" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('path',''))")
264
+ line=$(echo "$task_json" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('line',0))")
265
+ task_text=$(echo "$task_json" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('task',''))")
266
+ context=$(echo "$task_json" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('context',''))")
267
 
268
  local id="$(date +%s)-${kind}"
269
  local out="$OUT_DIR/${id}.md"
 
285
 
286
  # Call Surrogate-1 via Ollama (keep_alive=5m so model stays warm between cycles)
287
  local body
288
+ body=$(PROMPT_VAR="$prompt" python3 <<'PYEOF'
289
  import json, os
290
  print(json.dumps({
291
  "model": "surrogate-1",
 
298
  PYEOF
299
  )
300
  local resp
301
+ resp=$(curl -sS --max-time 120 \
302
  http://localhost:11434/v1/chat/completions \
303
  -H 'Content-Type: application/json' \
304
  -d "$body" 2>/dev/null)
305
 
306
  local answer
307
+ answer=$(echo "$resp" | python3 -c "
308
  import json, sys
309
  try:
310
  d = json.load(sys.stdin)
 
342
  save_reflexion_lesson "$kind" "$task_text" "$answer" "$dur"
343
 
344
  # Append to training-data candidate (will be reviewed before promoting to JSONL)
345
+ python3 <<PYEOF
346
  import json
347
  from pathlib import Path
348
  candidate = Path.home() / 'axentx/surrogate/data/training-jsonl/local-dev-pending.jsonl'
bin/surrogate-orchestrate.sh CHANGED
@@ -47,7 +47,7 @@ $prompt
47
  Output your work to $output_file using the \`write\` tool when done.
48
  Previous artifacts available in: $WORKDIR/
49
  CWD: $(pwd)"
50
- ~/.claude/bin/surrogate -p "$agent_prompt" 2>&1 | /usr/bin/head -50 | sed 's/^/ /'
51
  # Check if file written
52
  if [[ -f "$output_file" ]]; then
53
  echo "${GR} ⎿ $role done β†’ $(basename "$output_file") ($(wc -c < "$output_file") bytes)${R}"
@@ -112,7 +112,7 @@ Task: $TASK
112
  " "$QA_OUT"
113
 
114
  # ═══ Stage 4: OPS (if task mentions infra) ═══
115
- if echo "$TASK" | /usr/bin/grep -iqE "deploy|docker|helm|k8s|terraform|cicd|ci/cd"; then
116
  OPS_OUT="$WORKDIR/4-ops-checklist.md"
117
  echo ""
118
  echo "${MA}${B}═══ Stage 4/5: OPS${R} ${D}β€” deploy + infra${R}"
@@ -167,7 +167,7 @@ ls -la "$WORKDIR/" 2>&1 | tail -n +2 | awk '{print " " $9}' | grep -v '^ $'
167
  # Show verdict + auto-commit if APPROVED
168
  VERDICT_TEXT=""
169
  if [[ -f "$REVIEW_OUT" ]]; then
170
- VERDICT_TEXT=$(grep -iE "verdict|APPROVE|REWORK|REJECT" "$REVIEW_OUT" | /usr/bin/head -3)
171
  echo ""
172
  echo "${B}β–Έ Final verdict:${R}"
173
  echo "$VERDICT_TEXT" | sed 's/^/ /'
@@ -198,7 +198,7 @@ if echo "$VERDICT_TEXT" | grep -qi "APPROVE"; then
198
  elif echo "$VERDICT_TEXT" | grep -qi "REWORK"; then
199
  echo ""
200
  echo "${YE}${B}β–Έ Reviewer requested REWORK β€” re-running dev stage${R}"
201
- REWORK_NOTES=$(grep -A5 -i "REWORK" "$REVIEW_OUT" | /usr/bin/head -8)
202
  DEV_OUT2="$WORKDIR/2b-dev-rework.md"
203
  call_agent "dev" "
204
  REWORK requested by reviewer. Fix the following issues:
 
47
  Output your work to $output_file using the \`write\` tool when done.
48
  Previous artifacts available in: $WORKDIR/
49
  CWD: $(pwd)"
50
+ ~/.claude/bin/surrogate -p "$agent_prompt" 2>&1 | head -50 | sed 's/^/ /'
51
  # Check if file written
52
  if [[ -f "$output_file" ]]; then
53
  echo "${GR} ⎿ $role done β†’ $(basename "$output_file") ($(wc -c < "$output_file") bytes)${R}"
 
112
  " "$QA_OUT"
113
 
114
  # ═══ Stage 4: OPS (if task mentions infra) ═══
115
+ if echo "$TASK" | grep -iqE "deploy|docker|helm|k8s|terraform|cicd|ci/cd"; then
116
  OPS_OUT="$WORKDIR/4-ops-checklist.md"
117
  echo ""
118
  echo "${MA}${B}═══ Stage 4/5: OPS${R} ${D}β€” deploy + infra${R}"
 
167
  # Show verdict + auto-commit if APPROVED
168
  VERDICT_TEXT=""
169
  if [[ -f "$REVIEW_OUT" ]]; then
170
+ VERDICT_TEXT=$(grep -iE "verdict|APPROVE|REWORK|REJECT" "$REVIEW_OUT" | head -3)
171
  echo ""
172
  echo "${B}β–Έ Final verdict:${R}"
173
  echo "$VERDICT_TEXT" | sed 's/^/ /'
 
198
  elif echo "$VERDICT_TEXT" | grep -qi "REWORK"; then
199
  echo ""
200
  echo "${YE}${B}β–Έ Reviewer requested REWORK β€” re-running dev stage${R}"
201
+ REWORK_NOTES=$(grep -A5 -i "REWORK" "$REVIEW_OUT" | head -8)
202
  DEV_OUT2="$WORKDIR/2b-dev-rework.md"
203
  call_agent "dev" "
204
  REWORK requested by reviewer. Fix the following issues:
bin/work-queue-producer.sh CHANGED
@@ -20,7 +20,7 @@ SHARED="$HOME/.hermes/workspace/swarm-shared"
20
  mkdir -p "$(dirname "$LOG")"
21
 
22
  # Try Unix socket first, then fall back to TCP 127.0.0.1:6379
23
- REDIS_SOCK=$(/usr/bin/find /var/folders /tmp -name 'redis.socket' -type s 2>/dev/null | /usr/bin/head -1)
24
  REDIS_MODE=""
25
  if [[ -n "$REDIS_SOCK" ]] && [[ -S "$REDIS_SOCK" ]]; then
26
  REDIS_MODE="sock"
@@ -34,7 +34,7 @@ fi
34
 
35
  [[ ! -f "$SHARED/priority.json" ]] && exit 0
36
 
37
- /usr/bin/python3 <<PYEOF 2>>"$LOG"
38
  import json
39
  import subprocess
40
  from pathlib import Path
 
20
  mkdir -p "$(dirname "$LOG")"
21
 
22
  # Try Unix socket first, then fall back to TCP 127.0.0.1:6379
23
+ REDIS_SOCK=$(find /var/folders /tmp -name 'redis.socket' -type s 2>/dev/null | head -1)
24
  REDIS_MODE=""
25
  if [[ -n "$REDIS_SOCK" ]] && [[ -S "$REDIS_SOCK" ]]; then
26
  REDIS_MODE="sock"
 
34
 
35
  [[ ! -f "$SHARED/priority.json" ]] && exit 0
36
 
37
+ python3 <<PYEOF 2>>"$LOG"
38
  import json
39
  import subprocess
40
  from pathlib import Path