refactor: remove headless mode
Headless mode was half-built and untested. Agent-loop is a plugin that runs interactively via tmux — there's no CI use case yet. Removes --headless flag, timeout compatibility shim, output capture logic, and LOOP_AGENT_TMPFILE handling. Cuts 82 lines from loop.sh.
This commit is contained in:
146
loop.sh
146
loop.sh
@@ -13,7 +13,6 @@
|
||||
# --no-hooks Don't install stop hooks
|
||||
# --dry-run Print assembled prompts without running agents
|
||||
# --resume Skip already-passed stories (explicit mode)
|
||||
# --replan (reserved — not yet implemented)
|
||||
#
|
||||
# Each iteration:
|
||||
# 1. Generator: picks highest-priority incomplete story, does the work
|
||||
@@ -81,23 +80,6 @@ if ! command -v jq &>/dev/null && ! command -v python3 &>/dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- macOS timeout compatibility ---
|
||||
# macOS doesn't have GNU timeout. Use gtimeout (from coreutils) or a perl fallback.
|
||||
if ! command -v timeout &>/dev/null; then
|
||||
if command -v gtimeout &>/dev/null; then
|
||||
timeout() { gtimeout "$@"; }
|
||||
else
|
||||
# Perl-based fallback: runs command with alarm signal
|
||||
timeout() {
|
||||
local duration="$1"; shift
|
||||
perl -e '
|
||||
alarm shift @ARGV;
|
||||
exec @ARGV;
|
||||
' "$duration" "$@"
|
||||
}
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Load config defaults ---
|
||||
CONFIG_FILE="$LOOP_DIR/config.json"
|
||||
config_default() { get_config_value "$1" "$2"; }
|
||||
@@ -122,9 +104,7 @@ while [[ $# -gt 0 ]]; do
|
||||
--tool=*) TOOL="${1#*=}"; shift ;;
|
||||
--no-hooks) AUTO_HOOKS=false; shift ;;
|
||||
--dry-run) DRY_RUN=true; shift ;;
|
||||
--headless) export LOOP_HEADLESS=true; shift ;;
|
||||
--resume) RESUME=true; shift ;;
|
||||
--replan) log "ERROR: --replan is not yet implemented. Use /agent-loop:stories interactively."; exit 1 ;;
|
||||
[0-9]*) MAX_ITERATIONS="$1"; shift ;;
|
||||
*) log "Unknown option: $1"; exit 1 ;;
|
||||
esac
|
||||
@@ -147,7 +127,6 @@ fi
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
cleanup() {
|
||||
[ -n "${LOOP_AGENT_TMPFILE:-}" ] && rm -f "$LOOP_AGENT_TMPFILE"
|
||||
# Remove hooks in case we exit mid-agent (Ctrl+C during a claude session)
|
||||
[ "$AUTO_HOOKS" = true ] && remove_hooks 2>/dev/null
|
||||
release_lock
|
||||
@@ -178,8 +157,6 @@ finish() {
|
||||
read -r -t 30 2>/dev/null || true
|
||||
exit "$exit_code"
|
||||
}
|
||||
LOOP_AGENT_TMPFILE=""
|
||||
|
||||
# NOTE: Stop hook is installed/removed per-agent in run_agent(), not globally.
|
||||
# This prevents the hook from killing the orchestrating CC session.
|
||||
trap cleanup EXIT INT TERM
|
||||
@@ -215,14 +192,10 @@ fi
|
||||
# --- Agent runner ---
|
||||
# Runs a prompt through the selected AI tool.
|
||||
#
|
||||
# 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.
|
||||
# Pipes prompt to claude WITHOUT --print. This gives the full interactive
|
||||
# CC UI — tool calls, file edits, etc. A Stop hook sends SIGINT to the loop
|
||||
# when claude finishes, returning control to the while loop for the next
|
||||
# iteration. State is tracked via files (prd.json, .verdict), not stdout.
|
||||
run_agent() {
|
||||
local prompt="$1"
|
||||
local role="${2:-}"
|
||||
@@ -230,65 +203,31 @@ run_agent() {
|
||||
rm -f "$LOOP_DIR/.verdict"
|
||||
|
||||
local agent_exit=0
|
||||
if [ "${LOOP_HEADLESS:-false}" != "true" ]; then
|
||||
# --- Interactive mode (Ralph pattern) ---
|
||||
# Install Stop hook just before claude starts, remove after it exits.
|
||||
# This scopes the hook to only affect the loop's claude sessions.
|
||||
[ "$AUTO_HOOKS" = true ] && install_hooks
|
||||
# Install Stop hook just before claude starts, remove after it exits.
|
||||
# This scopes the hook to only affect the loop's claude sessions.
|
||||
[ "$AUTO_HOOKS" = true ] && install_hooks
|
||||
|
||||
(
|
||||
case "$TOOL" in
|
||||
claude)
|
||||
printf '%s\n' "$prompt" | claude --dangerously-skip-permissions
|
||||
;;
|
||||
amp)
|
||||
printf '%s\n' "$prompt" | amp --dangerously-allow-all
|
||||
;;
|
||||
*)
|
||||
log "ERROR: Unknown tool '$TOOL'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
) || agent_exit=$?
|
||||
(
|
||||
case "$TOOL" in
|
||||
claude)
|
||||
printf '%s\n' "$prompt" | claude --dangerously-skip-permissions
|
||||
;;
|
||||
amp)
|
||||
printf '%s\n' "$prompt" | amp --dangerously-allow-all
|
||||
;;
|
||||
*)
|
||||
log "ERROR: Unknown tool '$TOOL'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
) || agent_exit=$?
|
||||
|
||||
[ "$AUTO_HOOKS" = true ] && remove_hooks
|
||||
sleep 2 # Brief pause between sessions
|
||||
[ "$AUTO_HOOKS" = true ] && remove_hooks
|
||||
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
|
||||
else
|
||||
# --- Headless mode ---
|
||||
local output_file
|
||||
output_file=$(mktemp)
|
||||
LOOP_AGENT_TMPFILE="$output_file"
|
||||
|
||||
(
|
||||
case "$TOOL" in
|
||||
claude)
|
||||
printf '%s\n' "$prompt" | timeout "${LOOP_AGENT_TIMEOUT:-600}" \
|
||||
claude --dangerously-skip-permissions --output-format text \
|
||||
--print > "$output_file" 2>&1
|
||||
;;
|
||||
amp)
|
||||
printf '%s\n' "$prompt" | timeout "${LOOP_AGENT_TIMEOUT:-600}" \
|
||||
amp --dangerously-allow-all > "$output_file" 2>&1
|
||||
;;
|
||||
*)
|
||||
log "ERROR: Unknown tool '$TOOL'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
) || agent_exit=$?
|
||||
|
||||
if [ "$agent_exit" -ne 0 ] && [ ! -s "$output_file" ]; then
|
||||
log "WARNING: Agent exited with code $agent_exit and produced no output."
|
||||
fi
|
||||
|
||||
cat "$output_file"
|
||||
rm -f "$output_file"
|
||||
LOOP_AGENT_TMPFILE=""
|
||||
# Read verdict from file if evaluator wrote one
|
||||
if [ "$role" = "evaluator" ] && [ -f "$LOOP_DIR/.verdict" ]; then
|
||||
cat "$LOOP_DIR/.verdict"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -371,18 +310,7 @@ while [ "$ITERATION" -lt "$MAX_ITERATIONS" ]; do
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${LOOP_HEADLESS:-false}" != "true" ]; then
|
||||
# Interactive: run directly, no capture. User sees full CC UI.
|
||||
run_agent "$GENERATOR_PROMPT" "generator"
|
||||
GENERATOR_OUTPUT=""
|
||||
else
|
||||
# Headless: capture output for parsing.
|
||||
GENERATOR_OUTPUT=$(run_agent "$GENERATOR_PROMPT" "generator")
|
||||
if [ -z "$GENERATOR_OUTPUT" ]; then
|
||||
log "WARNING: Generator produced empty output (timeout or crash). Skipping to next iteration."
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
run_agent "$GENERATOR_PROMPT" "generator"
|
||||
|
||||
# --- Scope budget check ---
|
||||
# Verify the generator stayed within configured limits (files modified, lines written).
|
||||
@@ -419,22 +347,12 @@ while [ "$ITERATION" -lt "$MAX_ITERATIONS" ]; do
|
||||
|
||||
EVAL_PROMPT=$(build_prompt "evaluator" "$MODE")
|
||||
|
||||
if [ "${LOOP_HEADLESS:-false}" != "true" ]; then
|
||||
# Interactive: run directly, read verdict from file.
|
||||
run_agent "$EVAL_PROMPT" "evaluator"
|
||||
if [ -f "$LOOP_DIR/.verdict" ]; then
|
||||
EVAL_OUTPUT=$(cat "$LOOP_DIR/.verdict")
|
||||
else
|
||||
log "WARNING: No verdict file found. Treating as REJECT."
|
||||
EVAL_OUTPUT="<verdict>REJECT</verdict><rejection_reason>Evaluator produced no verdict file</rejection_reason>"
|
||||
fi
|
||||
run_agent "$EVAL_PROMPT" "evaluator"
|
||||
if [ -f "$LOOP_DIR/.verdict" ]; then
|
||||
EVAL_OUTPUT=$(cat "$LOOP_DIR/.verdict")
|
||||
else
|
||||
# Headless: capture output for parsing.
|
||||
EVAL_OUTPUT=$(run_agent "$EVAL_PROMPT" "evaluator")
|
||||
if [ -z "$EVAL_OUTPUT" ]; then
|
||||
log "WARNING: Evaluator produced empty output. Treating as REJECT."
|
||||
EVAL_OUTPUT="<verdict>REJECT</verdict><rejection_reason>Evaluator produced no output</rejection_reason>"
|
||||
fi
|
||||
log "WARNING: No verdict file found. Treating as REJECT."
|
||||
EVAL_OUTPUT="<verdict>REJECT</verdict><rejection_reason>Evaluator produced no verdict file</rejection_reason>"
|
||||
fi
|
||||
|
||||
VERDICT=$(parse_verdict "$EVAL_OUTPUT")
|
||||
|
||||
Reference in New Issue
Block a user