feat: interactive mode — full CC sessions visible in tmux, headless mode via --headless flag
This commit is contained in:
41
loop.sh
41
loop.sh
@@ -122,6 +122,7 @@ while [[ $# -gt 0 ]]; do
|
|||||||
--tool=*) TOOL="${1#*=}"; shift ;;
|
--tool=*) TOOL="${1#*=}"; shift ;;
|
||||||
--no-hooks) AUTO_HOOKS=false; shift ;;
|
--no-hooks) AUTO_HOOKS=false; shift ;;
|
||||||
--dry-run) DRY_RUN=true; shift ;;
|
--dry-run) DRY_RUN=true; shift ;;
|
||||||
|
--headless) export LOOP_HEADLESS=true; shift ;;
|
||||||
--resume) RESUME=true; shift ;;
|
--resume) RESUME=true; shift ;;
|
||||||
--replan) log "ERROR: --replan is not yet implemented. Use /loop-plan interactively."; exit 1 ;;
|
--replan) log "ERROR: --replan is not yet implemented. Use /loop-plan interactively."; exit 1 ;;
|
||||||
[0-9]*) MAX_ITERATIONS="$1"; shift ;;
|
[0-9]*) MAX_ITERATIONS="$1"; shift ;;
|
||||||
@@ -186,8 +187,13 @@ if [ -n "$BRANCH" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Agent runner ---
|
# --- Agent runner ---
|
||||||
# Runs a prompt through the selected AI tool and captures output.
|
# Runs a prompt through the selected AI tool.
|
||||||
# Output is displayed live via tee to /dev/tty (if available) and captured to a temp file.
|
# 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.
|
||||||
|
# Output is captured via `script` for verdict parsing.
|
||||||
|
# Headless (no TTY or LOOP_HEADLESS=true): uses claude --print for fully autonomous operation.
|
||||||
|
#
|
||||||
# The function prints the captured output to stdout for the caller to capture.
|
# The function prints the captured output to stdout for the caller to capture.
|
||||||
run_agent() {
|
run_agent() {
|
||||||
local prompt="$1"
|
local prompt="$1"
|
||||||
@@ -195,23 +201,29 @@ run_agent() {
|
|||||||
output_file=$(mktemp)
|
output_file=$(mktemp)
|
||||||
LOOP_AGENT_TMPFILE="$output_file" # exposed for trap cleanup
|
LOOP_AGENT_TMPFILE="$output_file" # exposed for trap cleanup
|
||||||
|
|
||||||
# Determine whether we can display live output
|
local prompt_file
|
||||||
|
prompt_file=$(mktemp)
|
||||||
|
printf '%s\n' "$prompt" > "$prompt_file"
|
||||||
|
|
||||||
|
# Determine whether we can run interactively
|
||||||
local has_tty=false
|
local has_tty=false
|
||||||
if { true > /dev/tty; } 2>/dev/null; then
|
if [ "${LOOP_HEADLESS:-false}" != "true" ] && { true > /dev/tty; } 2>/dev/null; then
|
||||||
has_tty=true
|
has_tty=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run in subshell so a non-zero exit from the AI tool doesn't kill the loop.
|
# Run in subshell so a non-zero exit from the AI tool doesn't kill the loop.
|
||||||
# The subshell inherits set -e but its exit status is captured, not propagated.
|
|
||||||
local agent_exit=0
|
local agent_exit=0
|
||||||
(
|
(
|
||||||
case "$TOOL" in
|
case "$TOOL" in
|
||||||
claude)
|
claude)
|
||||||
if [ "$has_tty" = true ]; then
|
if [ "$has_tty" = true ]; then
|
||||||
printf '%s\n' "$prompt" | timeout "${LOOP_AGENT_TIMEOUT:-600}" \
|
# Interactive mode: full CC session visible in terminal.
|
||||||
claude --dangerously-skip-permissions --output-format text \
|
# Use script to capture output while showing it live.
|
||||||
--print 2>&1 | tee /dev/tty > "$output_file"
|
# The prompt is piped via stdin; claude reads it as the initial message.
|
||||||
|
script -q "$output_file" \
|
||||||
|
sh -c "printf '%s\n' \"\$(cat '$prompt_file')\" | claude --dangerously-skip-permissions"
|
||||||
else
|
else
|
||||||
|
# Headless mode: --print for autonomous operation
|
||||||
printf '%s\n' "$prompt" | timeout "${LOOP_AGENT_TIMEOUT:-600}" \
|
printf '%s\n' "$prompt" | timeout "${LOOP_AGENT_TIMEOUT:-600}" \
|
||||||
claude --dangerously-skip-permissions --output-format text \
|
claude --dangerously-skip-permissions --output-format text \
|
||||||
--print 2>&1 > "$output_file"
|
--print 2>&1 > "$output_file"
|
||||||
@@ -219,8 +231,8 @@ run_agent() {
|
|||||||
;;
|
;;
|
||||||
amp)
|
amp)
|
||||||
if [ "$has_tty" = true ]; then
|
if [ "$has_tty" = true ]; then
|
||||||
printf '%s\n' "$prompt" | timeout "${LOOP_AGENT_TIMEOUT:-600}" \
|
script -q "$output_file" \
|
||||||
amp --dangerously-allow-all 2>&1 | tee /dev/tty > "$output_file"
|
sh -c "printf '%s\n' \"\$(cat '$prompt_file')\" | amp --dangerously-allow-all"
|
||||||
else
|
else
|
||||||
printf '%s\n' "$prompt" | timeout "${LOOP_AGENT_TIMEOUT:-600}" \
|
printf '%s\n' "$prompt" | timeout "${LOOP_AGENT_TIMEOUT:-600}" \
|
||||||
amp --dangerously-allow-all 2>&1 > "$output_file"
|
amp --dangerously-allow-all 2>&1 > "$output_file"
|
||||||
@@ -233,11 +245,18 @@ run_agent() {
|
|||||||
esac
|
esac
|
||||||
) || agent_exit=$?
|
) || agent_exit=$?
|
||||||
|
|
||||||
|
rm -f "$prompt_file"
|
||||||
|
|
||||||
if [ "$agent_exit" -ne 0 ] && [ ! -s "$output_file" ]; then
|
if [ "$agent_exit" -ne 0 ] && [ ! -s "$output_file" ]; then
|
||||||
log "WARNING: Agent exited with code $agent_exit and produced no output."
|
log "WARNING: Agent exited with code $agent_exit and produced no output."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cat "$output_file"
|
# Strip ANSI escape codes for clean verdict parsing
|
||||||
|
if command -v sed &>/dev/null; then
|
||||||
|
sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' "$output_file"
|
||||||
|
else
|
||||||
|
cat "$output_file"
|
||||||
|
fi
|
||||||
rm -f "$output_file"
|
rm -f "$output_file"
|
||||||
LOOP_AGENT_TMPFILE=""
|
LOOP_AGENT_TMPFILE=""
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user