diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 4b7f3f4c..20c6a28c 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -3,8 +3,8 @@ "name": "claude-plugin-boilerplate", "description": "Standard boilerplate for creating Claude Code plugins with testing, CI/CD, and validation", "owner": { - "name": "Your Name", - "email": "your.email@example.com" + "name": "jito", + "email": "git@baleen.me" }, "plugins": [ { @@ -12,13 +12,26 @@ "description": "Example plugin demonstrating all component types", "version": "1.0.0", "author": { - "name": "Your Name", - "email": "your.email@example.com" + "name": "jito", + "email": "git@baleen.me" }, "source": "./plugins/example-plugin", "category": "development", "tags": ["boilerplate", "example", "template"], "strict": false + }, + { + "name": "ralph-loop", + "description": "Continuous self-referential AI loops for interactive iterative development, implementing the Ralph Wiggum technique", + "version": "1.0.0", + "author": { + "name": "Anthropic", + "email": "support@anthropic.com" + }, + "source": "./plugins/ralph-loop", + "category": "development", + "tags": ["automation", "iteration", "loop", "ai"], + "strict": false } ] } diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 22a78c9d..238ec245 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,10 +12,13 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install BATS + - name: Install BATS and helpers run: | git clone --depth 1 --branch v1.10.0 https://github.com/bats-core/bats-core.git /tmp/bats sudo /tmp/bats/install.sh /usr/local + mkdir -p tests/helpers + git clone --depth 1 --branch v0.3.0 https://github.com/bats-core/bats-support.git tests/helpers/bats-support + git clone --depth 1 --branch v0.3.0 https://github.com/bats-core/bats-assert.git tests/helpers/bats-assert - name: Run tests run: bats tests/ diff --git a/README.md b/README.md index 1180a2d6..a02b3f37 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Standard boilerplate for creating Claude Code plugins with testing, CI/CD, and v - Standard Structure: Follows anthropics/claude-plugins-official patterns - Validation Scripts: JSON, YAML, naming, and path validation -- BATS Tests: Comprehensive test suite +- Structure Validation: Plugin structure and component verification - CI/CD: GitHub Actions workflows for PR validation and releases - Pre-commit Hooks: Automated quality checks - Example Plugin: Demonstrates all component types @@ -17,7 +17,7 @@ Standard boilerplate for creating Claude Code plugins with testing, CI/CD, and v ```bash # Clone the repository -git clone https://github.com/YOUR_USERNAME/claude-plugin-boilerplate.git +git clone https://github.com/jito/claude-plugin-boilerplate.git cd claude-plugin-boilerplate # Install as a marketplace @@ -43,9 +43,6 @@ cp -r plugins/example-plugin plugins/my-plugin # Validate all plugins bash scripts/validate-plugin.sh -# Run tests -bats tests/ - # Run pre-commit hooks manually pre-commit run --all-files ``` @@ -66,7 +63,6 @@ claude-plugin-boilerplate/ │ └── hooks/ ├── .github/workflows/ # CI/CD workflows ├── scripts/ # Validation scripts -├── tests/ # BATS tests └── schemas/ # JSON schemas ``` diff --git a/plugins/example-plugin/.claude-plugin/plugin.json b/plugins/example-plugin/.claude-plugin/plugin.json index 14b71eab..e3af183a 100644 --- a/plugins/example-plugin/.claude-plugin/plugin.json +++ b/plugins/example-plugin/.claude-plugin/plugin.json @@ -4,11 +4,11 @@ "description": "Example plugin demonstrating all Claude Code plugin component types", "version": "1.0.0", "author": { - "name": "Your Name", - "email": "your.email@example.com" + "name": "jito", + "email": "git@baleen.me" }, - "homepage": "https://github.com/YOUR_USERNAME/claude-plugin-boilerplate", - "repository": "https://github.com/YOUR_USERNAME/claude-plugin-boilerplate", + "homepage": "https://github.com/jito/claude-plugin-boilerplate", + "repository": "https://github.com/jito/claude-plugin-boilerplate", "license": "MIT", "keywords": ["claude-code", "plugin", "boilerplate", "example"] } diff --git a/plugins/ralph-loop/.claude-plugin/plugin.json b/plugins/ralph-loop/.claude-plugin/plugin.json new file mode 100644 index 00000000..bac0a0bb --- /dev/null +++ b/plugins/ralph-loop/.claude-plugin/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "ralph-loop", + "description": "Continuous self-referential AI loops for interactive iterative development, implementing the Ralph Wiggum technique. Run Claude in a while-true loop with the same prompt until task completion.", + "author": { + "name": "Anthropic", + "email": "support@anthropic.com" + } +} diff --git a/plugins/ralph-loop/README.md b/plugins/ralph-loop/README.md new file mode 100644 index 00000000..5c341d90 --- /dev/null +++ b/plugins/ralph-loop/README.md @@ -0,0 +1,177 @@ +# Ralph Loop Plugin + +Implementation of the Ralph Wiggum technique for iterative, self-referential AI development loops in Claude Code. + +## What is Ralph Loop? + +Ralph Loop is a development methodology based on continuous AI agent loops. As Geoffrey Huntley describes it: **"Ralph is a Bash loop"** - a simple `while true` that repeatedly feeds an AI agent a prompt file, allowing it to iteratively improve its work until completion. + +This technique is inspired by the Ralph Wiggum coding technique (named after the character from The Simpsons), embodying the philosophy of persistent iteration despite setbacks. + +### Core Concept + +This plugin implements Ralph using a **Stop hook** that intercepts Claude's exit attempts: + +```bash +# You run ONCE: +/ralph-loop "Your task description" --completion-promise "DONE" + +# Then Claude Code automatically: +# 1. Works on the task +# 2. Tries to exit +# 3. Stop hook blocks exit +# 4. Stop hook feeds the SAME prompt back +# 5. Repeat until completion +``` + +The loop happens **inside your current session** - you don't need external bash loops. The Stop hook in `hooks/stop-hook.sh` creates the self-referential feedback loop by blocking normal session exit. + +This creates a **self-referential feedback loop** where: +- The prompt never changes between iterations +- Claude's previous work persists in files +- Each iteration sees modified files and git history +- Claude autonomously improves by reading its own past work in files + +## Quick Start + +```bash +/ralph-loop "Build a REST API for todos. Requirements: CRUD operations, input validation, tests. Output COMPLETE when done." --completion-promise "COMPLETE" --max-iterations 50 +``` + +Claude will: +- Implement the API iteratively +- Run tests and see failures +- Fix bugs based on test output +- Iterate until all requirements met +- Output the completion promise when done + +## Commands + +### /ralph-loop + +Start a Ralph loop in your current session. + +**Usage:** +```bash +/ralph-loop "" [--max-iterations ] [--completion-promise ""] +``` + +**Options:** +- `--max-iterations ` - Stop after N iterations (default: unlimited) +- `--completion-promise ` - Phrase that signals completion + +### /cancel-ralph + +Cancel the active Ralph loop. + +**Usage:** +```bash +/cancel-ralph +``` + +## Prompt Writing Best Practices + +### 1. Clear Completion Criteria + +❌ Bad: "Build a todo API and make it good." + +✅ Good: +```markdown +Build a REST API for todos. +When complete: +- All CRUD endpoints working +- Input validation in place +- Tests passing (coverage > 80%) +- README with API docs +Output: COMPLETE +``` + +### 2. Incremental Goals + +❌ Bad: "Create a complete e-commerce platform." + +✅ Good: +```markdown +Phase 1: User authentication (JWT, tests) +Phase 2: Product catalog (list/search, tests) +Phase 3: Shopping cart (add/remove, tests) +Output COMPLETE when all phases done. +``` + +### 3. Self-Correction + +❌ Bad: "Write code for feature X." + +✅ Good: +```markdown +Implement feature X following TDD: +1. Write failing tests +2. Implement feature +3. Run tests +4. If any fail, debug and fix +5. Refactor if needed +6. Repeat until all green +7. Output: COMPLETE +``` + +### 4. Escape Hatches + +Always use `--max-iterations` as a safety net to prevent infinite loops on impossible tasks: + +```bash +# Recommended: Always set a reasonable iteration limit +/ralph-loop "Try to implement feature X" --max-iterations 20 + +# In your prompt, include what to do if stuck: +# "After 15 iterations, if not complete: +# - Document what's blocking progress +# - List what was attempted +# - Suggest alternative approaches" +``` + +**Note**: The `--completion-promise` uses exact string matching, so you cannot use it for multiple completion conditions (like "SUCCESS" vs "BLOCKED"). Always rely on `--max-iterations` as your primary safety mechanism. + +## Philosophy + +Ralph embodies several key principles: + +### 1. Iteration > Perfection +Don't aim for perfect on first try. Let the loop refine the work. + +### 2. Failures Are Data +"Deterministically bad" means failures are predictable and informative. Use them to tune prompts. + +### 3. Operator Skill Matters +Success depends on writing good prompts, not just having a good model. + +### 4. Persistence Wins +Keep trying until success. The loop handles retry logic automatically. + +## When to Use Ralph + +**Good for:** +- Well-defined tasks with clear success criteria +- Tasks requiring iteration and refinement (e.g., getting tests to pass) +- Greenfield projects where you can walk away +- Tasks with automatic verification (tests, linters) + +**Not good for:** +- Tasks requiring human judgment or design decisions +- One-shot operations +- Tasks with unclear success criteria +- Production debugging (use targeted debugging instead) + +## Real-World Results + +- Successfully generated 6 repositories overnight in Y Combinator hackathon testing +- One $50k contract completed for $297 in API costs +- Created entire programming language ("cursed") over 3 months using this approach + +## Learn More + +- Original technique: https://ghuntley.com/ralph/ +- Ralph Orchestrator: https://github.com/mikeyobrien/ralph-orchestrator + +## For Help + +Run `/help` in Claude Code for detailed command reference and examples. diff --git a/plugins/ralph-loop/commands/cancel-ralph.md b/plugins/ralph-loop/commands/cancel-ralph.md new file mode 100644 index 00000000..dd339209 --- /dev/null +++ b/plugins/ralph-loop/commands/cancel-ralph.md @@ -0,0 +1,13 @@ +--- +description: "Cancel active Ralph Loop" +allowed-tools: ["Bash(test -f .claude/ralph-loop.local.md:*)", "Bash(rm .claude/ralph-loop.local.md)", "Read(.claude/ralph-loop.local.md)"] +hide-from-slash-command-tool: "true" +--- +# Cancel Ralph +To cancel the Ralph loop: +1. Check if `.claude/ralph-loop.local.md` exists using Bash: `test -f .claude/ralph-loop.local.md && echo "EXISTS" || echo "NOT_FOUND"` +2. **If NOT_FOUND**: Say "No active Ralph loop found." +3. **If EXISTS**: +- Read `.claude/ralph-loop.local.md` to get the current iteration number from the `iteration:` field +- Remove the file using Bash: `rm .claude/ralph-loop.local.md` +- Report: "Cancelled Ralph loop (was at iteration N)" where N is the iteration value diff --git a/plugins/ralph-loop/commands/help.md b/plugins/ralph-loop/commands/help.md new file mode 100644 index 00000000..d798ec36 --- /dev/null +++ b/plugins/ralph-loop/commands/help.md @@ -0,0 +1,90 @@ +--- +description: "Explain Ralph Loop plugin and available commands" +--- +# Ralph Loop Plugin Help +Please explain the following to the user: +## What is Ralph Loop? +Ralph Loop implements the Ralph Wiggum technique - an iterative development methodology based on continuous AI loops, pioneered by Geoffrey Huntley. +**Core concept:** +```bash +while :; do +cat PROMPT.md | claude-code --continue +done +``` +The same prompt is fed to Claude repeatedly. The "self-referential" aspect comes from Claude seeing its own previous work in the files and git history, not from feeding output back as input. +**Each iteration:** +1. Claude receives the SAME prompt +2. Works on the task, modifying files +3. Tries to exit +4. Stop hook intercepts and feeds the same prompt again +5. Claude sees its previous work in the files +6. Iteratively improves until completion +The technique is described as "deterministically bad in an undeterministic world" - failures are predictable, enabling systematic improvement through prompt tuning. +## Available Commands +### /ralph-loop [OPTIONS] +Start a Ralph loop in your current session. +**Usage:** +``` +/ralph-loop "Refactor the cache layer" --max-iterations 20 +/ralph-loop "Add tests" --completion-promise "TESTS COMPLETE" +``` +**Options:** +- `--max-iterations ` - Max iterations before auto-stop +- `--completion-promise ` - Promise phrase to signal completion +**How it works:** +1. Creates `.claude/.ralph-loop.local.md` state file +2. You work on the task +3. When you try to exit, stop hook intercepts +4. Same prompt fed back +5. You see your previous work +6. Continues until promise detected or max iterations +--- +### /cancel-ralph +Cancel an active Ralph loop (removes the loop state file). +**Usage:** +``` +/cancel-ralph +``` +**How it works:** +- Checks for active loop state file +- Removes `.claude/.ralph-loop.local.md` +- Reports cancellation with iteration count +--- +## Key Concepts +### Completion Promises +To signal completion, Claude must output a `TAG` tag: +``` +TASK COMPLETE +``` +The stop hook looks for this specific tag. Without it (or `--max-iterations`), Ralph runs infinitely. +### Self-Reference Mechanism +The "loop" doesn't mean Claude talks to itself. It means: +- Same prompt repeated +- Claude's work persists in files +- Each iteration sees previous attempts +- Builds incrementally toward goal +## Example +### Interactive Bug Fix +``` +/ralph-loop "Fix the token refresh logic in auth.ts. Output FIXED when all tests pass." --completion-promise "FIXED" --max-iterations 10 +``` +You'll see Ralph: +- Attempt fixes +- Run tests +- See failures +- Iterate on solution +- In your current session +## When to Use Ralph +**Good for:** +- Well-defined tasks with clear success criteria +- Tasks requiring iteration and refinement +- Iterative development with self-correction +- Greenfield projects +**Not good for:** +- Tasks requiring human judgment or design decisions +- One-shot operations +- Tasks with unclear success criteria +- Debugging production issues (use targeted debugging instead) +## Learn More +- Original technique: https://ghuntley.com/ralph/ +- Ralph Orchestrator: https://github.com/mikeyobrien/ralph-orchestrator diff --git a/plugins/ralph-loop/commands/ralph-loop.md b/plugins/ralph-loop/commands/ralph-loop.md new file mode 100644 index 00000000..11f21f0e --- /dev/null +++ b/plugins/ralph-loop/commands/ralph-loop.md @@ -0,0 +1,13 @@ +--- +description: "Start Ralph Loop in current session" +argument-hint: "PROMPT [--max-iterations N] [--completion-promise TEXT]" +allowed-tools: ["Bash(${CLAUDE_PLUGIN_ROOT}/scripts/setup-ralph-loop.sh:*)"] +hide-from-slash-command-tool: "true" +--- +# Ralph Loop Command +Execute the setup script to initialize the Ralph loop: +```! +"${CLAUDE_PLUGIN_ROOT}/scripts/setup-ralph-loop.sh" $ARGUMENTS +``` +Please work on the task. When you try to exit, the Ralph loop will feed the SAME PROMPT back to you for the next iteration. You'll see your previous work in files and git history, allowing you to iterate and improve. +CRITICAL RULE: If a completion promise is set, you may ONLY output it when the statement is completely and unequivocally TRUE. Do not output false promises to escape the loop, even if you think you're stuck or should exit for other reasons. The loop is designed to continue until genuine completion. diff --git a/plugins/ralph-loop/hooks/hooks.json b/plugins/ralph-loop/hooks/hooks.json new file mode 100644 index 00000000..b4ad7be6 --- /dev/null +++ b/plugins/ralph-loop/hooks/hooks.json @@ -0,0 +1,15 @@ +{ + "description": "Ralph Loop plugin stop hook for self-referential loops", + "hooks": { + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh" + } + ] + } + ] + } +} diff --git a/plugins/ralph-loop/hooks/stop-hook.sh b/plugins/ralph-loop/hooks/stop-hook.sh new file mode 100755 index 00000000..768c1db2 --- /dev/null +++ b/plugins/ralph-loop/hooks/stop-hook.sh @@ -0,0 +1,176 @@ +#!/bin/bash +# Ralph Loop Stop Hook +# Prevents session exit when a ralph-loop is active +# Feeds Claude's output back as input to continue the loop +set -euo pipefail + +# Read hook input from stdin (advanced stop hook API) +HOOK_INPUT=$(cat) + +# Check if ralph-loop is active +RALPH_STATE_FILE=".claude/ralph-loop.local.md" +if [[ ! -f "$RALPH_STATE_FILE" ]]; then + # No active loop - allow exit + exit 0 +fi + +# Parse markdown frontmatter (YAML between ---) and extract values +FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$RALPH_STATE_FILE") +ITERATION=$(echo "$FRONTMATTER" | grep '^iteration:' | sed 's/iteration: *//') +MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iterations: *//') + +# Extract completion_promise and strip surrounding quotes if present +COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/') + +# Validate numeric fields before arithmetic operations +if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then + echo "⚠️ Ralph loop: State file corrupted" >&2 + echo " File: $RALPH_STATE_FILE" >&2 + echo " Problem: 'iteration' field is not a valid number (got: '$ITERATION')" >&2 + echo "" >&2 + echo " This usually means the state file was manually edited or corrupted." >&2 + echo " Ralph loop is stopping. Run /ralph-loop again to start fresh." >&2 + rm "$RALPH_STATE_FILE" + exit 0 +fi + +if [[ ! "$MAX_ITERATIONS" =~ ^[0-9]+$ ]]; then + echo "⚠️ Ralph loop: State file corrupted" >&2 + echo " File: $RALPH_STATE_FILE" >&2 + echo " Problem: 'max_iterations' field is not a valid number (got: '$MAX_ITERATIONS')" >&2 + echo "" >&2 + echo " This usually means the state file was manually edited or corrupted." >&2 + echo " Ralph loop is stopping. Run /ralph-loop again to start fresh." >&2 + rm "$RALPH_STATE_FILE" + exit 0 +fi + +# Check if max iterations reached +if [[ $MAX_ITERATIONS -gt 0 ]] && [[ $ITERATION -ge $MAX_ITERATIONS ]]; then + echo "🛑 Ralph loop: Max iterations ($MAX_ITERATIONS) reached." + rm "$RALPH_STATE_FILE" + exit 0 +fi + +# Get transcript path from hook input +TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path') + +if [[ ! -f "$TRANSCRIPT_PATH" ]]; then + echo "⚠️ Ralph loop: Transcript file not found" >&2 + echo " Expected: $TRANSCRIPT_PATH" >&2 + echo " This is unusual and may indicate a Claude Code internal issue." >&2 + echo " Ralph loop is stopping." >&2 + rm "$RALPH_STATE_FILE" + exit 0 +fi + +# Read last assistant message from transcript (JSONL format - one JSON per line) +# First check if there are any assistant messages +if ! grep -q '"role":"assistant"' "$TRANSCRIPT_PATH"; then + echo "⚠️ Ralph loop: No assistant messages found in transcript" >&2 + echo " Transcript: $TRANSCRIPT_PATH" >&2 + echo " This is unusual and may indicate a transcript format issue" >&2 + echo " Ralph loop is stopping." >&2 + rm "$RALPH_STATE_FILE" + exit 0 +fi + +# Extract last assistant message with explicit error handling +LAST_LINE=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1) + +if [[ -z "$LAST_LINE" ]]; then + echo "⚠️ Ralph loop: Failed to extract last assistant message" >&2 + echo " Ralph loop is stopping." >&2 + rm "$RALPH_STATE_FILE" + exit 0 +fi + +# Parse JSON with error handling +LAST_OUTPUT=$(echo "$LAST_LINE" | jq -r ' + .message.content | + map(select(.type == "text")) | + map(.text) | + join("\n") +' 2>&1) + +# Check if jq succeeded +if [[ $? -ne 0 ]]; then + echo "⚠️ Ralph loop: Failed to parse assistant message JSON" >&2 + echo " Error: $LAST_OUTPUT" >&2 + echo " This may indicate a transcript format issue" >&2 + echo " Ralph loop is stopping." >&2 + rm "$RALPH_STATE_FILE" + exit 0 +fi + +if [[ -z "$LAST_OUTPUT" ]]; then + echo "⚠️ Ralph loop: Assistant message contained no text content" >&2 + echo " Ralph loop is stopping." >&2 + rm "$RALPH_STATE_FILE" + exit 0 +fi + +# Check for completion promise (only if set) +if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then + # Extract text from tags using Perl for multiline support + # -0777 slurps entire input, s flag makes . match newlines + # .*? is non-greedy (takes FIRST tag), whitespace normalized + PROMISE_TEXT=$(echo "$LAST_OUTPUT" | perl -0777 -pe 's/.*?(.*?)<\/promise>.*/$1/s; s/^\s+|\s+$//g; s/\s+/ /g' 2>/dev/null || echo "") + + # Use = for literal string comparison (not pattern matching) + # == in [[ ]] does glob pattern matching which breaks with *, ?, [ characters + if [[ -n "$PROMISE_TEXT" ]] && [[ "$PROMISE_TEXT" = "$COMPLETION_PROMISE" ]]; then + echo "✅ Ralph loop: Detected $COMPLETION_PROMISE" + rm "$RALPH_STATE_FILE" + exit 0 + fi +fi + +# Not complete - continue loop with SAME PROMPT +NEXT_ITERATION=$((ITERATION + 1)) + +# Extract prompt (everything after the closing ---) +# Skip first --- line, skip until second --- line, then print everything after +# Use i>=2 instead of i==2 to handle --- in prompt content +PROMPT_TEXT=$(awk '/^---$/{i++; next} i>=2' "$RALPH_STATE_FILE") + +if [[ -z "$PROMPT_TEXT" ]]; then + echo "⚠️ Ralph loop: State file corrupted or incomplete" >&2 + echo " File: $RALPH_STATE_FILE" >&2 + echo " Problem: No prompt text found" >&2 + echo "" >&2 + echo " This usually means:" >&2 + echo " • State file was manually edited" >&2 + echo " • File was corrupted during writing" >&2 + echo "" >&2 + echo " Ralph loop is stopping. Run /ralph-loop again to start fresh." >&2 + rm "$RALPH_STATE_FILE" + exit 0 +fi + +# Update iteration in frontmatter (portable across macOS and Linux) +# Create temp file, then atomically replace +TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$" +sed "s/^iteration: .*/iteration: $NEXT_ITERATION/" "$RALPH_STATE_FILE" > "$TEMP_FILE" +mv "$TEMP_FILE" "$RALPH_STATE_FILE" + +# Build system message with iteration count and completion promise info +if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then + SYSTEM_MSG="🔄 Ralph iteration $NEXT_ITERATION | To stop: output $COMPLETION_PROMISE (ONLY when statement is TRUE - do not lie to exit!)" +else + SYSTEM_MSG="🔄 Ralph iteration $NEXT_ITERATION | No completion promise set - loop runs infinitely" +fi + +# Output JSON to block the stop and feed prompt back +# The "reason" field contains the prompt that will be sent back to Claude +jq -n \ + --arg prompt "$PROMPT_TEXT" \ + --arg msg "$SYSTEM_MSG" \ + '{ + "decision": "block", + "reason": $prompt, + "systemMessage": $msg +}' + +# Exit 0 for successful hook execution +exit 0 diff --git a/plugins/ralph-loop/scripts/setup-ralph-loop.sh b/plugins/ralph-loop/scripts/setup-ralph-loop.sh new file mode 100755 index 00000000..d1ab2a4b --- /dev/null +++ b/plugins/ralph-loop/scripts/setup-ralph-loop.sh @@ -0,0 +1,168 @@ +#!/bin/bash +# Ralph Loop Setup Script +# Creates state file for in-session Ralph loop +set -euo pipefail + +# Parse arguments +PROMPT_PARTS=() +MAX_ITERATIONS=0 +COMPLETION_PROMISE="null" + +# Parse options and positional arguments +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + cat << 'HELP_EOF' +Ralph Loop - Interactive self-referential development loop + +USAGE: + /ralph-loop [PROMPT...] [OPTIONS] + +ARGUMENTS: + PROMPT... Initial prompt to start the loop (can be multiple words without quotes) + +OPTIONS: + --max-iterations Maximum iterations before auto-stop (default: unlimited) + --completion-promise '' Promise phrase (USE QUOTES for multi-word) + -h, --help Show this help message + +DESCRIPTION: + Starts a Ralph Loop in your CURRENT session. The stop hook prevents + exit and feeds your output back as input until completion or iteration limit. + + To signal completion, you must output: YOUR_PHRASE + + Use this for: + - Interactive iteration where you want to see progress + - Tasks requiring self-correction and refinement + - Learning how Ralph works + +EXAMPLES: + /ralph-loop Build a todo API --completion-promise 'DONE' --max-iterations 20 + /ralph-loop --max-iterations 10 Fix the auth bug + /ralph-loop Refactor cache layer (runs forever) + /ralph-loop --completion-promise 'TASK COMPLETE' Create a REST API + +STOPPING: + Only by reaching --max-iterations or detecting --completion-promise + No manual stop - Ralph runs infinitely by default! + +MONITORING: + # View current iteration: + grep '^iteration:' .claude/ralph-loop.local.md + + # View full state: + head -10 .claude/ralph-loop.local.md +HELP_EOF + exit 0 + ;; + --max-iterations) + if [[ -z "${2:-}" ]]; then + echo "❌ Error: --max-iterations requires a number argument" >&2 + echo "" >&2 + echo " Valid examples:" >&2 + echo " --max-iterations 10" >&2 + echo " --max-iterations 50" >&2 + echo " --max-iterations 0 (unlimited)" >&2 + echo "" >&2 + echo " You provided: --max-iterations (with no number)" >&2 + exit 1 + fi + if ! [[ "$2" =~ ^[0-9]+$ ]]; then + echo "❌ Error: --max-iterations must be a positive integer or 0, got: $2" >&2 + echo "" >&2 + echo " Valid examples:" >&2 + echo " --max-iterations 10" >&2 + echo " --max-iterations 50" >&2 + echo " --max-iterations 0 (unlimited)" >&2 + echo "" >&2 + echo " Invalid: decimals (10.5), negative numbers (-5), text" >&2 + exit 1 + fi + MAX_ITERATIONS="$2" + shift 2 + ;; + --completion-promise) + if [[ -z "${2:-}" ]]; then + echo "❌ Error: --completion-promise requires a text argument" >&2 + echo "" >&2 + echo " Valid examples:" >&2 + echo " --completion-promise 'DONE'" >&2 + echo " --completion-promise 'TASK COMPLETE'" >&2 + echo " --completion-promise 'All tests passing'" >&2 + echo "" >&2 + echo " You provided: --completion-promise (with no text)" >&2 + echo "" >&2 + echo " Note: Multi-word promises must be quoted!" >&2 + exit 1 + fi + COMPLETION_PROMISE="$2" + shift 2 + ;; + *) + # Non-option argument - collect all as prompt parts + PROMPT_PARTS+=("$1") + shift + ;; + esac +done + +# Join all prompt parts with spaces +PROMPT="${PROMPT_PARTS[*]}" + +# Validate prompt is non-empty +if [[ -z "$PROMPT" ]]; then + echo "❌ Error: No prompt provided" >&2 + echo "" >&2 + echo " Ralph needs a task description to work on." >&2 + echo "" >&2 + echo " Examples:" >&2 + echo " /ralph-loop Build a REST API for todos" >&2 + echo " /ralph-loop Fix the auth bug --max-iterations 20" >&2 + echo " /ralph-loop --completion-promise 'DONE' Refactor code" >&2 + echo "" >&2 + echo " For all options: /ralph-loop --help" >&2 + exit 1 +fi + +# Create state file for stop hook (markdown with YAML frontmatter) +mkdir -p .claude + +# Quote completion promise for YAML if it contains special chars or is not null +if [[ -n "$COMPLETION_PROMISE" ]] && [[ "$COMPLETION_PROMISE" != "null" ]]; then + COMPLETION_PROMISE_YAML="\"$COMPLETION_PROMISE\"" +else + COMPLETION_PROMISE_YAML="null" +fi + +cat > .claude/ralph-loop.local.md < XML tags EXACTLY as shown above" +echo " ✓ The statement MUST be completely and unequivocally TRUE" +echo " ✓ Do NOT output false statements to exit the loop" +echo " ✓ Do NOT lie even if you think you should exit" +echo "" +echo "IMPORTANT - Do not circumvent the loop:" +echo " Even if you believe you're stuck, the task is impossible," +echo " or you've been running too long - you MUST NOT output a" +echo " false promise statement. The loop is designed to continue" +echo " until the promise is GENUINELY TRUE. Trust the process." +echo "" +echo " If the loop should stop, the promise statement will become" +echo " true naturally. Do not force it by lying." +echo "═══════════════════════════════════════════════════════════" +EOF diff --git a/tests/helpers/bats-assert b/tests/helpers/bats-assert deleted file mode 160000 index 9f88b420..00000000 --- a/tests/helpers/bats-assert +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9f88b4207da750093baabc4e3f41bf68f0dd3630 diff --git a/tests/helpers/bats-support b/tests/helpers/bats-support deleted file mode 160000 index 24a72e14..00000000 --- a/tests/helpers/bats-support +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 24a72e14349690bcbf7c151b9d2d1cdd32d36eb1