- Move cross-cutting docs (personas, design system, implementation phases, Ideen.md) to .specify/memory/ - Move cross-cutting research and plans to .specify/memory/research/ and .specify/memory/plans/ - Extract 5 setup tasks from spec/setup-tasks.md into individual specs/001-005/spec.md files with spec-kit template format - Extract 20 user stories from spec/userstories.md into individual specs/006-026/spec.md files with spec-kit template format - Relocate feature-specific research and plan docs into specs/[feature]/ - Add spec-kit constitution, templates, scripts, and slash commands - Slim down CLAUDE.md to Claude-Code-specific config, delegate principles to .specify/memory/constitution.md - Update ralph.sh with stream-json output and per-iteration logging - Delete old spec/ and docs/agents/ directories - Gitignore Ralph iteration JSONL logs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
195 lines
5.4 KiB
Bash
Executable File
195 lines
5.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: ralph.sh <run-directory> [options]
|
|
|
|
Arguments:
|
|
run-directory Path to the Ralph run directory (must contain instructions.md)
|
|
|
|
Options:
|
|
-n, --max-iterations N Maximum iterations (default: 20)
|
|
-m, --model MODEL Claude model to use (default: opus)
|
|
-t, --tools TOOLS Allowed tools, quoted (default: "Read Edit Write")
|
|
-h, --help Show this help message
|
|
|
|
Examples:
|
|
./ralph.sh .ralph/my-run
|
|
./ralph.sh .ralph/my-run -n 10 -m sonnet
|
|
./ralph.sh .ralph/my-run -n 30 -t "Read Edit Write Bash Glob Grep"
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
# Defaults
|
|
MAX_ITERATIONS=20
|
|
MODEL="opus"
|
|
TOOLS="Read Edit Write"
|
|
RUN_DIR=""
|
|
|
|
# Parse args
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-n|--max-iterations) MAX_ITERATIONS="$2"; shift 2 ;;
|
|
-m|--model) MODEL="$2"; shift 2 ;;
|
|
-t|--tools) TOOLS="$2"; shift 2 ;;
|
|
-h|--help) usage ;;
|
|
-*) echo "Error: Unknown option: $1" >&2; exit 1 ;;
|
|
*) RUN_DIR="$1"; shift ;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$RUN_DIR" ]]; then
|
|
echo "Error: No run directory specified." >&2
|
|
echo "Run './ralph.sh --help' for usage." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -d "$RUN_DIR" ]]; then
|
|
echo "Error: Run directory '$RUN_DIR' does not exist." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$RUN_DIR/instructions.md" ]]; then
|
|
echo "Error: '$RUN_DIR/instructions.md' not found." >&2
|
|
echo "Create an instructions.md in the run directory before starting." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Auto-create template files if they don't exist
|
|
RUN_NAME=$(basename "$RUN_DIR")
|
|
TODAY=$(date +%Y-%m-%d)
|
|
|
|
if [[ ! -f "$RUN_DIR/meta.md" ]]; then
|
|
cat > "$RUN_DIR/meta.md" <<EOF
|
|
# Run: $RUN_NAME
|
|
|
|
- **Created**: $TODAY
|
|
- **Description**: (add description here)
|
|
- **Model**: $MODEL
|
|
- **Max iterations**: $MAX_ITERATIONS
|
|
EOF
|
|
echo "Created $RUN_DIR/meta.md"
|
|
fi
|
|
|
|
if [[ ! -f "$RUN_DIR/chief-wiggum.md" ]]; then
|
|
cat > "$RUN_DIR/chief-wiggum.md" <<'EOF'
|
|
# Chief Wiggum's Notes
|
|
|
|
<!-- This file is written by the Chief Wiggum session. Ralph reads it but never modifies it. -->
|
|
|
|
## Action Required
|
|
|
|
(No action items.)
|
|
|
|
## Observations
|
|
|
|
(No observations.)
|
|
EOF
|
|
echo "Created $RUN_DIR/chief-wiggum.md"
|
|
fi
|
|
|
|
if [[ ! -f "$RUN_DIR/answers.md" ]]; then
|
|
cat > "$RUN_DIR/answers.md" <<'EOF'
|
|
# Answers
|
|
|
|
<!-- Human answers to open questions. Ralph processes these one per iteration. -->
|
|
EOF
|
|
echo "Created $RUN_DIR/answers.md"
|
|
fi
|
|
|
|
if [[ ! -f "$RUN_DIR/questions.md" ]]; then
|
|
cat > "$RUN_DIR/questions.md" <<'EOF'
|
|
# Questions
|
|
|
|
## Open
|
|
|
|
(No open questions.)
|
|
|
|
## Resolved
|
|
|
|
(No resolved questions.)
|
|
EOF
|
|
echo "Created $RUN_DIR/questions.md"
|
|
fi
|
|
|
|
if [[ ! -f "$RUN_DIR/progress.txt" ]]; then
|
|
cat > "$RUN_DIR/progress.txt" <<'EOF'
|
|
# Ralph Loop Progress Log
|
|
# Each iteration appends its findings and decisions here.
|
|
EOF
|
|
echo "Created $RUN_DIR/progress.txt"
|
|
fi
|
|
|
|
# Prepare prompt with {{RUN_DIR}} substitution
|
|
PROMPT=$(sed "s|{{RUN_DIR}}|$RUN_DIR|g" "$RUN_DIR/instructions.md")
|
|
|
|
COMPLETION_SIGNAL="<promise>COMPLETE</promise>"
|
|
TIMESTAMP=$(date +%Y-%m-%dT%H:%M:%S)
|
|
|
|
# Append to run.log
|
|
echo "" >> "$RUN_DIR/run.log"
|
|
echo "=== Run started: $TIMESTAMP | model=$MODEL | max=$MAX_ITERATIONS ===" >> "$RUN_DIR/run.log"
|
|
|
|
echo "=== Ralph Loop ==="
|
|
echo "Run dir: $RUN_DIR"
|
|
echo "Model: $MODEL"
|
|
echo "Max iter: $MAX_ITERATIONS"
|
|
echo "Tools: $TOOLS"
|
|
echo ""
|
|
|
|
for ((i = 1; i <= MAX_ITERATIONS; i++)); do
|
|
echo ""
|
|
echo "━━━ Iteration $i/$MAX_ITERATIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
ITER_START=$(date +%H:%M:%S)
|
|
echo "Started: $ITER_START"
|
|
echo ""
|
|
|
|
ITER_LOG="$RUN_DIR/iteration-${i}.jsonl"
|
|
|
|
echo "$PROMPT" | claude --print --model "$MODEL" --allowedTools "$TOOLS" --verbose --output-format stream-json > "$ITER_LOG" 2>&1
|
|
|
|
ITER_TIME=$(date +%H:%M:%S)
|
|
|
|
# Extract tool uses for a compact summary
|
|
TOOL_SUMMARY=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | " -> \(.name): \(.input.file_path // .input.pattern // .input.command // .input.content[0:80] // "" | tostring | .[0:100])"' "$ITER_LOG" 2>/dev/null) || true
|
|
|
|
if [[ -n "$TOOL_SUMMARY" ]]; then
|
|
echo "Tools used:"
|
|
echo "$TOOL_SUMMARY"
|
|
echo ""
|
|
fi
|
|
|
|
# Extract assistant text messages
|
|
ASSISTANT_TEXT=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text' "$ITER_LOG" 2>/dev/null) || true
|
|
|
|
if [[ -n "$ASSISTANT_TEXT" ]]; then
|
|
echo "Ralph says:"
|
|
echo "$ASSISTANT_TEXT" | sed 's/^/ /'
|
|
echo ""
|
|
fi
|
|
|
|
# Check for errors
|
|
ERROR_TEXT=$(jq -r 'select(.type == "result") | select(.subtype == "error") | .result' "$ITER_LOG" 2>/dev/null) || true
|
|
if [[ -n "$ERROR_TEXT" ]]; then
|
|
echo "[ERROR] $ERROR_TEXT"
|
|
fi
|
|
|
|
# Check for completion signal
|
|
if grep -q "$COMPLETION_SIGNAL" "$ITER_LOG" 2>/dev/null; then
|
|
echo "[$ITER_TIME] COMPLETE after $i iteration(s)" >> "$RUN_DIR/run.log"
|
|
echo "━━━ Loop complete after $i iteration(s). ━━━"
|
|
exit 0
|
|
fi
|
|
|
|
echo "[$ITER_TIME] Iteration $i done" >> "$RUN_DIR/run.log"
|
|
echo "--- Done at $ITER_TIME ---"
|
|
sleep 2
|
|
done
|
|
|
|
ITER_TIME=$(date +%H:%M:%S)
|
|
echo "[$ITER_TIME] WARNING: Max iterations ($MAX_ITERATIONS) reached without completion" >> "$RUN_DIR/run.log"
|
|
echo "WARNING: Max iterations ($MAX_ITERATIONS) reached without completion."
|
|
exit 1
|