123 lines
3.7 KiB
Bash
Executable File
123 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# autoresearch tmux session orchestrator
|
|
# Usage: start.sh [start|stop|status]
|
|
set -euo pipefail
|
|
|
|
SESSION="autoresearch"
|
|
CONFIG="${HOME}/.claude/autoresearch.yaml"
|
|
LOGDIR="${HOME}/.claude/autoresearch"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
get_projects() {
|
|
python3 -c "
|
|
import yaml, os, sys
|
|
try:
|
|
cfg = yaml.safe_load(open('${CONFIG}'))
|
|
for p in cfg.get('projects', []):
|
|
print(os.path.expanduser(p['path']))
|
|
except Exception as e:
|
|
print(f'ERROR: {e}', file=sys.stderr)
|
|
sys.exit(1)
|
|
"
|
|
}
|
|
|
|
cmd_start() {
|
|
if tmux has-session -t "$SESSION" 2>/dev/null; then
|
|
echo "autoresearch: session '$SESSION' already running"
|
|
echo " attach: tmux attach -t $SESSION"
|
|
return 0
|
|
fi
|
|
|
|
mkdir -p "$LOGDIR"
|
|
|
|
# Window 0: overview — left: log viewer, right (30%): metrics
|
|
tmux new-session -d -s "$SESSION" -n "overview" -x 220 -y 50
|
|
tmux split-window -h -t "${SESSION}:overview" -p 30
|
|
tmux send-keys -t "${SESSION}:overview.left" \
|
|
"watch -n5 python3 '${SCRIPT_DIR}/log_view.py'" Enter
|
|
tmux send-keys -t "${SESSION}:overview.right" \
|
|
"watch -n10 python3 '${SCRIPT_DIR}/metrics.py'" Enter
|
|
|
|
# Pass 1: create all project windows and start claude
|
|
local win=1
|
|
local windows=()
|
|
while IFS= read -r project_path; do
|
|
local project_name
|
|
project_name=$(basename "$project_path")
|
|
local safe_path
|
|
safe_path=$(printf '%q' "$project_path")
|
|
tmux new-window -t "${SESSION}:${win}" -n "$project_name"
|
|
tmux send-keys -t "${SESSION}:${win}" \
|
|
"cd ${safe_path} && claude --name autoresearch-${project_name}" Enter
|
|
windows+=("${win}:${project_name}")
|
|
win=$((win + 1))
|
|
done < <(get_projects)
|
|
|
|
# Single sleep for all sessions to initialize
|
|
local project_count=$(( win - 1 ))
|
|
if [[ $project_count -gt 0 ]]; then
|
|
sleep 3
|
|
# Pass 2: invoke ralph-loop in all project windows
|
|
for entry in "${windows[@]}"; do
|
|
local win_id="${entry%%:*}"
|
|
tmux send-keys -t "${SESSION}:${win_id}" \
|
|
"/ralph-loop Run one autoresearch experiment iteration per the autoresearch-loop skill. --completion-promise STOP" Enter
|
|
done
|
|
fi
|
|
|
|
tmux select-window -t "${SESSION}:0"
|
|
echo "autoresearch: started — ${project_count} project window(s)"
|
|
echo " attach: tmux attach -t $SESSION"
|
|
}
|
|
|
|
cmd_stop() {
|
|
if ! tmux has-session -t "$SESSION" 2>/dev/null; then
|
|
echo "autoresearch: not running"
|
|
return 0
|
|
fi
|
|
tmux kill-session -t "$SESSION"
|
|
echo "autoresearch: stopped"
|
|
}
|
|
|
|
cmd_status() {
|
|
if ! tmux has-session -t "$SESSION" 2>/dev/null; then
|
|
echo "autoresearch: not running"
|
|
return 1
|
|
fi
|
|
echo "autoresearch: running"
|
|
tmux list-windows -t "$SESSION"
|
|
echo ""
|
|
echo "Last experiment per project:"
|
|
python3 - <<'PYEOF'
|
|
import json, os
|
|
from collections import OrderedDict
|
|
|
|
log = os.path.expanduser('~/.claude/autoresearch/log.jsonl')
|
|
if not os.path.exists(log):
|
|
print(" (no experiments yet)")
|
|
else:
|
|
latest = OrderedDict()
|
|
with open(log) as f:
|
|
for line in f:
|
|
try:
|
|
e = json.loads(line)
|
|
latest[e.get('project')] = e
|
|
except Exception:
|
|
pass
|
|
for proj, e in latest.items():
|
|
kept = '✓' if e.get('kept') else '✗'
|
|
desc = (e.get('description') or '')[:50]
|
|
print(f" {proj}: exp {e.get('exp', '?')} {kept} — {desc}")
|
|
PYEOF
|
|
}
|
|
|
|
case "${1:-start}" in
|
|
start) cmd_start ;;
|
|
stop) cmd_stop ;;
|
|
status) cmd_status ;;
|
|
*)
|
|
echo "Usage: $(basename "$0") [start|stop|status]"
|
|
exit 1
|
|
;;
|
|
esac
|