feat: adopt Ralph pattern — pipe to claude (no --print), working Stop hook

This commit is contained in:
2026-03-27 13:24:13 -04:00
parent 1e7f7ea6ed
commit 994908aed2
2 changed files with 59 additions and 33 deletions

41
loop.sh
View File

@@ -188,40 +188,33 @@ fi
# --- Agent runner ---
# Runs a prompt through the selected AI tool.
# Two modes:
# Interactive (default when TTY available): runs claude in full interactive mode.
# The user sees the complete CC session (tool calls, file edits, etc.) in the terminal.
# No output capture — state is tracked via prd.json and .verdict file.
# Headless (no TTY or LOOP_HEADLESS=true): uses claude --print for fully autonomous operation.
# Output is captured to a temp file for verdict parsing.
#
# The function prints captured output to stdout (headless) or nothing (interactive).
# Interactive (default): Pipes prompt to claude WITHOUT --print.
# This gives the full interactive CC UI — tool calls, file edits, etc.
# A Stop hook (installed at startup) sends SIGINT to the loop when claude
# finishes, which returns control to the while loop for the next iteration.
# State is tracked via files (prd.json, .verdict), not stdout.
#
# Headless (LOOP_HEADLESS=true): Uses claude --print for CI/background.
# Output captured to file for verdict parsing.
run_agent() {
local prompt="$1"
local role="${2:-}" # "generator" or "evaluator" — used for verdict file
local role="${2:-}"
# Clean up any previous verdict file
rm -f "$LOOP_DIR/.verdict"
# Determine whether we can run interactively
local has_tty=false
if [ "${LOOP_HEADLESS:-false}" != "true" ] && { true > /dev/tty; } 2>/dev/null; then
has_tty=true
fi
# Run in subshell so a non-zero exit from the AI tool doesn't kill the loop.
local agent_exit=0
if [ "$has_tty" = true ]; then
# --- Interactive mode ---
# Run claude directly in the terminal — full interactive UI visible.
# No output capture. State tracked via files (prd.json, .verdict).
if [ "${LOOP_HEADLESS:-false}" != "true" ]; then
# --- Interactive mode (Ralph pattern) ---
# Pipe prompt to claude without --print — full interactive UI.
# The Stop hook handles session exit.
(
case "$TOOL" in
claude)
claude --dangerously-skip-permissions "$prompt"
printf '%s\n' "$prompt" | claude --dangerously-skip-permissions
;;
amp)
amp --dangerously-allow-all "$prompt"
printf '%s\n' "$prompt" | amp --dangerously-allow-all
;;
*)
log "ERROR: Unknown tool '$TOOL'"
@@ -230,7 +223,9 @@ run_agent() {
esac
) || agent_exit=$?
# In interactive mode, read verdict from file if evaluator wrote one
sleep 2 # Brief pause between sessions
# Read verdict from file if evaluator wrote one
if [ "$role" = "evaluator" ] && [ -f "$LOOP_DIR/.verdict" ]; then
cat "$LOOP_DIR/.verdict"
fi