Spaces:
Runtime error
Runtime error
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 +7 -7
- bin/dev-cloud-daemon.sh +5 -5
- bin/github-domain-scrape.sh +1 -1
- bin/notify-discord.sh +1 -1
- bin/qwen-coder-daemon.sh +4 -4
- bin/qwen-coder-worker.sh +11 -11
- bin/scrape-keyword-tuner.sh +1 -1
- bin/surrogate +4 -4
- bin/surrogate-agent.sh +2 -2
- bin/surrogate-daemon.sh +11 -11
- bin/surrogate-dev-loop.sh +13 -13
- bin/surrogate-orchestrate.sh +4 -4
- bin/work-queue-producer.sh +2 -2
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=$(
|
| 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 = ['
|
| 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" |
|
| 81 |
-
PROJ_NAME=$(echo "$TASK_INFO" |
|
| 82 |
-
FILE=$(echo "$TASK_INFO" |
|
| 83 |
-
LINE=$(echo "$TASK_INFO" |
|
| 84 |
-
CONTENT=$(echo "$TASK_INFO" |
|
| 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=$(
|
| 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=$(
|
| 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" |
|
| 49 |
[[ -z "$PAYLOAD" ]] && continue
|
| 50 |
|
| 51 |
-
PRIO_ID=$(echo "$PAYLOAD" |
|
| 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 |
|
| 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 |
-
|
| 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=$(
|
| 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=$(
|
| 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" |
|
| 30 |
[[ -z "$PAYLOAD" ]] && continue
|
| 31 |
|
| 32 |
-
PRIO_ID=$(echo "$PAYLOAD" |
|
| 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 |
|
| 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=$(
|
| 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=$(
|
| 63 |
-
"$PRIO_PROJECT $PRIO_TITLE" 2>/dev/null |
|
| 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 |
|
| 69 |
-
if
|
| 70 |
-
|
| 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=$(
|
| 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 |
|
| 83 |
-
bugs=$(
|
| 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" |
|
| 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=$(
|
| 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 |
-
|
| 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 |
-
|
| 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 |
|
| 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=$(
|
| 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 |
-
|
| 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 |
-
|
| 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"
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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=$(
|
| 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-$(
|
| 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" |
|
| 294 |
-
TID=$(echo "$TASK_JSON" |
|
| 295 |
-
SOURCE=$(echo "$TASK_JSON" |
|
| 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 |
-
|
| 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 |
-
|
| 325 |
import json
|
| 326 |
-
done = {'id': '$TID', 'source': '$SOURCE', 'task': '''$TASK''', 'duration_sec': $DUR, 'output_tail': '''$(echo "$OUTPUT" | tail -20 |
|
| 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 |
-
|
| 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 = ['
|
| 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 |
-
|
| 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 |
-
|
| 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" |
|
| 263 |
-
path=$(echo "$task_json" |
|
| 264 |
-
line=$(echo "$task_json" |
|
| 265 |
-
task_text=$(echo "$task_json" |
|
| 266 |
-
context=$(echo "$task_json" |
|
| 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"
|
| 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=$(
|
| 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" |
|
| 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 |
-
|
| 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 |
|
| 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" |
|
| 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" |
|
| 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" |
|
| 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=$(
|
| 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 |
-
|
| 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
|