Spaces:
Runtime error
Runtime error
Ashira Pitchayapakayakul commited on
Commit ·
47c417c
1
Parent(s): 88079d2
Surrogate CLI: status line + cost meter + /undo + /history + /context (research wins #3,#4,#9)
Browse files- bin/surrogate +99 -2
bin/surrogate
CHANGED
|
@@ -271,7 +271,7 @@ def tool_glob(pattern, path=None):
|
|
| 271 |
|
| 272 |
def tool_grep(pattern, path=None, glob='*'):
|
| 273 |
base = os.path.expanduser(path) if path else CWD
|
| 274 |
-
cmd = f"grep -rn --include='{glob}' -E {subprocess.list2cmdline([pattern])} {base} 2>/dev/null | head -40"
|
| 275 |
r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=15)
|
| 276 |
return {'matches': r.stdout[:5000]}
|
| 277 |
|
|
@@ -474,6 +474,9 @@ ${B}Diagnostics${R}:
|
|
| 474 |
${CY}/cost-all${R} all provider usage breakdown
|
| 475 |
${CY}/health${R} check HF endpoint + local CLI status
|
| 476 |
${CY}/monitor${R} watch logs + system load (background)
|
|
|
|
|
|
|
|
|
|
| 477 |
|
| 478 |
${B}Hermes brain${R} (axentx/surrogate-1):
|
| 479 |
${CY}/remote${R} <task> force route via HF Space (override local)
|
|
@@ -486,6 +489,42 @@ ${B}Tips${R}:
|
|
| 486 |
EOF
|
| 487 |
}
|
| 488 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 489 |
# ═══ REPL ═══
|
| 490 |
repl() {
|
| 491 |
banner
|
|
@@ -493,6 +532,7 @@ repl() {
|
|
| 493 |
echo "${D}Type ${CY}/help${R}${D} for commands · ${CY}/exit${R}${D} to quit · plain text = task to Surrogate-1${R}"
|
| 494 |
echo ""
|
| 495 |
while true; do
|
|
|
|
| 496 |
# Show project + mode in prompt (Claude-Code style)
|
| 497 |
local prompt_proj="${SURROGATE_MD:+$(basename $(dirname "$SURROGATE_MD"))}"
|
| 498 |
local prompt_str="${B}${BMA}▶${R}"
|
|
@@ -502,6 +542,9 @@ repl() {
|
|
| 502 |
line="${line#"${line%%[![:space:]]*}"}" # ltrim
|
| 503 |
[[ -z "$line" ]] && continue
|
| 504 |
|
|
|
|
|
|
|
|
|
|
| 505 |
case "$line" in
|
| 506 |
/exit|/quit|exit|quit) break ;;
|
| 507 |
/help|/?) slash_help ;;
|
|
@@ -556,6 +599,60 @@ repl() {
|
|
| 556 |
esac
|
| 557 |
;;
|
| 558 |
/memory) ls -lh ~/.claude/state/surrogate-memory/ 2>&1 | head -10 ;;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 559 |
/cost)
|
| 560 |
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}')"
|
| 561 |
;;
|
|
@@ -842,7 +939,7 @@ monitor_mode() {
|
|
| 842 |
# If critical → spawn agent to investigate
|
| 843 |
if [[ $ERR_COUNT -gt 50 ]]; then
|
| 844 |
echo "${RE}⚠ elevated errors — dispatching investigator agent${R}"
|
| 845 |
-
(run_agent "เช็ค ~/.claude/logs/ หา pattern error ที่ recur บ่อย และเสนอ fix list (ห้ามแก้เอง รายงานอย่างเดียว)" 2>&1 | head -20) &
|
| 846 |
fi
|
| 847 |
sleep 30
|
| 848 |
done
|
|
|
|
| 271 |
|
| 272 |
def tool_grep(pattern, path=None, glob='*'):
|
| 273 |
base = os.path.expanduser(path) if path else CWD
|
| 274 |
+
cmd = f"/usr/bin/grep -rn --include='{glob}' -E {subprocess.list2cmdline([pattern])} {base} 2>/dev/null | head -40"
|
| 275 |
r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=15)
|
| 276 |
return {'matches': r.stdout[:5000]}
|
| 277 |
|
|
|
|
| 474 |
${CY}/cost-all${R} all provider usage breakdown
|
| 475 |
${CY}/health${R} check HF endpoint + local CLI status
|
| 476 |
${CY}/monitor${R} watch logs + system load (background)
|
| 477 |
+
${CY}/history${R} last 20 prompts you ran
|
| 478 |
+
${CY}/context${R} show full session context (cwd, mode, git, etc.)
|
| 479 |
+
${CY}/undo${R} restore last surrogate checkpoint (git stash/reset)
|
| 480 |
|
| 481 |
${B}Hermes brain${R} (axentx/surrogate-1):
|
| 482 |
${CY}/remote${R} <task> force route via HF Space (override local)
|
|
|
|
| 489 |
EOF
|
| 490 |
}
|
| 491 |
|
| 492 |
+
# ═══ Status line — show every prompt (cost, mode, model, project) ═══
|
| 493 |
+
print_statusline() {
|
| 494 |
+
local cost_str=""
|
| 495 |
+
if [[ -n "${OR_KEY:-${OPENROUTER_API_KEY:-}}" ]]; then
|
| 496 |
+
# Cached cost lookup — fetch once per minute
|
| 497 |
+
local cache=~/.surrogate/.cost-cache
|
| 498 |
+
if [[ ! -f "$cache" ]] || [[ $(($(date +%s) - $(stat -f %m "$cache" 2>/dev/null || stat -c %Y "$cache" 2>/dev/null || echo 0))) -gt 60 ]]; then
|
| 499 |
+
(curl -sS -m 5 -H "Authorization: Bearer ${OPENROUTER_API_KEY:-${OR_KEY:-}}" \
|
| 500 |
+
https://openrouter.ai/api/v1/auth/key 2>/dev/null \
|
| 501 |
+
| python3 -c "import json,sys; d=json.load(sys.stdin).get('data',{}); print(f'\$OR={d.get(\"usage\",0):.3f}')" \
|
| 502 |
+
> "$cache") 2>/dev/null &
|
| 503 |
+
fi
|
| 504 |
+
cost_str=$(cat "$cache" 2>/dev/null | head -1)
|
| 505 |
+
fi
|
| 506 |
+
local proj_short=""
|
| 507 |
+
[[ -n "$SURROGATE_MD" ]] && proj_short="📁 $(basename "$(dirname "$SURROGATE_MD")") "
|
| 508 |
+
local model_short="${MODEL:-auto}"
|
| 509 |
+
local hf_brain="${USE_HF_HERMES:-1}"
|
| 510 |
+
local hf_str="🌐 HF" ; [[ "$hf_brain" == "0" ]] && hf_str="💻 local"
|
| 511 |
+
echo "${D}─── ${proj_short}${YE}${PERM_MODE}${R}${D} · ${MA}${model_short}${R}${D} · ${hf_str}${R}${D} · ${cost_str:-\$?} ───${R}"
|
| 512 |
+
}
|
| 513 |
+
|
| 514 |
+
# ═══ History persistence ═══
|
| 515 |
+
HISTORY_FILE="$SURROGATE_HOME/history.jsonl"
|
| 516 |
+
mkdir -p "$(dirname "$HISTORY_FILE")"
|
| 517 |
+
save_history() {
|
| 518 |
+
local prompt="$1"
|
| 519 |
+
python3 -c "
|
| 520 |
+
import json, sys, time
|
| 521 |
+
from pathlib import Path
|
| 522 |
+
Path('$HISTORY_FILE').parent.mkdir(parents=True, exist_ok=True)
|
| 523 |
+
with open('$HISTORY_FILE', 'a') as f:
|
| 524 |
+
f.write(json.dumps({'ts': time.time(), 'cwd': '$(pwd)', 'prompt': '''$prompt'''[:500]}, ensure_ascii=False) + '\n')
|
| 525 |
+
" 2>/dev/null
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
# ═══ REPL ═══
|
| 529 |
repl() {
|
| 530 |
banner
|
|
|
|
| 532 |
echo "${D}Type ${CY}/help${R}${D} for commands · ${CY}/exit${R}${D} to quit · plain text = task to Surrogate-1${R}"
|
| 533 |
echo ""
|
| 534 |
while true; do
|
| 535 |
+
print_statusline
|
| 536 |
# Show project + mode in prompt (Claude-Code style)
|
| 537 |
local prompt_proj="${SURROGATE_MD:+$(basename $(dirname "$SURROGATE_MD"))}"
|
| 538 |
local prompt_str="${B}${BMA}▶${R}"
|
|
|
|
| 542 |
line="${line#"${line%%[![:space:]]*}"}" # ltrim
|
| 543 |
[[ -z "$line" ]] && continue
|
| 544 |
|
| 545 |
+
# Persist to history (skip for /exit and /clear)
|
| 546 |
+
case "$line" in /exit|/quit|exit|quit|/clear) ;; *) save_history "$line" ;; esac
|
| 547 |
+
|
| 548 |
case "$line" in
|
| 549 |
/exit|/quit|exit|quit) break ;;
|
| 550 |
/help|/?) slash_help ;;
|
|
|
|
| 599 |
esac
|
| 600 |
;;
|
| 601 |
/memory) ls -lh ~/.claude/state/surrogate-memory/ 2>&1 | head -10 ;;
|
| 602 |
+
/undo)
|
| 603 |
+
# Restore last checkpoint (git stash if uncommitted changes from last task)
|
| 604 |
+
if git -C "$(pwd)" rev-parse --git-dir &>/dev/null; then
|
| 605 |
+
LAST_STASH=$(git -C "$(pwd)" stash list 2>/dev/null | grep "surrogate-checkpoint" | head -1)
|
| 606 |
+
if [[ -n "$LAST_STASH" ]]; then
|
| 607 |
+
STASH_REF=$(echo "$LAST_STASH" | cut -d: -f1)
|
| 608 |
+
echo "${YE}▸ Restoring: $LAST_STASH${R}"
|
| 609 |
+
git -C "$(pwd)" stash pop "$STASH_REF" 2>&1 | head -10
|
| 610 |
+
else
|
| 611 |
+
# Try undoing last commit (if surrogate-auto)
|
| 612 |
+
LAST_COMMIT=$(git -C "$(pwd)" log -1 --format='%s' 2>/dev/null)
|
| 613 |
+
if echo "$LAST_COMMIT" | grep -qiE "surrogate|auto-dev|hermes"; then
|
| 614 |
+
echo "${YE}▸ Undoing last commit: $LAST_COMMIT${R}"
|
| 615 |
+
git -C "$(pwd)" reset --soft HEAD~1 2>&1 | tail -3
|
| 616 |
+
else
|
| 617 |
+
echo "${GY} no surrogate checkpoint to undo (last commit: $LAST_COMMIT)${R}"
|
| 618 |
+
fi
|
| 619 |
+
fi
|
| 620 |
+
else
|
| 621 |
+
echo "${RE} not a git repo — /undo requires git${R}"
|
| 622 |
+
fi
|
| 623 |
+
;;
|
| 624 |
+
/history)
|
| 625 |
+
if [[ -f "$HISTORY_FILE" ]]; then
|
| 626 |
+
python3 -c "
|
| 627 |
+
import json
|
| 628 |
+
from pathlib import Path
|
| 629 |
+
import time
|
| 630 |
+
lines = Path('$HISTORY_FILE').read_text().splitlines()[-20:]
|
| 631 |
+
for l in lines:
|
| 632 |
+
try:
|
| 633 |
+
d = json.loads(l)
|
| 634 |
+
age_min = int((time.time() - d.get('ts', 0)) / 60)
|
| 635 |
+
print(f\" [{age_min:>4}m ago] {d.get('prompt','')[:100]}\")
|
| 636 |
+
except: pass
|
| 637 |
+
"
|
| 638 |
+
else
|
| 639 |
+
echo "${GY} no history yet${R}"
|
| 640 |
+
fi
|
| 641 |
+
;;
|
| 642 |
+
/context)
|
| 643 |
+
echo "${B}Active context${R}"
|
| 644 |
+
echo " cwd: $(pwd)"
|
| 645 |
+
echo " project: ${SURROGATE_MD:-(none)}"
|
| 646 |
+
echo " mode: ${PERM_MODE}"
|
| 647 |
+
echo " model: ${MODEL:-auto}"
|
| 648 |
+
echo " effort: ${EFFORT}"
|
| 649 |
+
echo " HF brain: ${USE_HF_HERMES:-1} (1=route via HF Space, 0=local-only)"
|
| 650 |
+
echo " history: $(wc -l < "$HISTORY_FILE" 2>/dev/null || echo 0) commands"
|
| 651 |
+
if [[ -d ".git" ]]; then
|
| 652 |
+
echo " git branch: $(git branch --show-current 2>/dev/null)"
|
| 653 |
+
echo " git status: $(git status --short 2>/dev/null | wc -l | tr -d ' ') changed files"
|
| 654 |
+
fi
|
| 655 |
+
;;
|
| 656 |
/cost)
|
| 657 |
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}')"
|
| 658 |
;;
|
|
|
|
| 939 |
# If critical → spawn agent to investigate
|
| 940 |
if [[ $ERR_COUNT -gt 50 ]]; then
|
| 941 |
echo "${RE}⚠ elevated errors — dispatching investigator agent${R}"
|
| 942 |
+
(run_agent "เช็ค ~/.claude/logs/ หา pattern error ที่ recur บ่อย และเสนอ fix list (ห้ามแก้เอง รายงานอย่างเดียว)" 2>&1 | /usr/bin/head -20) &
|
| 943 |
fi
|
| 944 |
sleep 30
|
| 945 |
done
|