Files
cursor__plugins/ralph-loop/hooks/capture-response.sh
Eric Zakariasson e87eaecc7b Add Ralph Loop plugin (#15)
* Add Ralph Loop plugin

Port of the Ralph Wiggum technique from Claude Code to Cursor.
Runs Cursor in a self-referential loop, feeding the same prompt
back after every turn until a completion condition is met.

Components:
- Stop hook that intercepts session exit and re-injects the prompt
- Setup script for initializing loop state
- Skills for starting, cancelling, and explaining the loop

Co-authored-by: Cursor <cursoragent@cursor.com>

* Comply with Cursor hooks API

- Rewrite hooks.json to Cursor format (version 1, lowercase event
  names, flat structure, loop_limit: null)
- Replace Claude Code stop hook output (decision/block/reason) with
  Cursor's followup_message API
- Add afterAgentResponse hook to detect completion promise without
  depending on transcript format
- Use CURSOR_PROJECT_DIR env var for reliable state file paths
- Remove setup script; ralph-loop skill now instructs the agent to
  create the state file directly
- Update cancel-ralph skill to clean up done flag file

Co-authored-by: Cursor <cursoragent@cursor.com>

* Move state files to .cursor/ralph/ subdirectory

State file: .cursor/ralph/scratchpad.md
Done flag:  .cursor/ralph/done
Cancel cleans up with rm -rf .cursor/ralph

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 15:47:59 -08:00

47 lines
1.4 KiB
Bash
Executable File

#!/bin/bash
# afterAgentResponse hook for Ralph Loop.
# Checks if the agent's response contains a matching completion promise.
# If found, writes a done flag so the stop hook knows to end the loop.
#
# Input: { "text": "<assistant response text>" }
# Output: none (fire-and-forget)
set -euo pipefail
HOOK_INPUT=$(cat)
PROJECT_DIR="${CURSOR_PROJECT_DIR:-.}"
STATE_FILE="$PROJECT_DIR/.cursor/ralph/scratchpad.md"
DONE_FLAG="$PROJECT_DIR/.cursor/ralph/done"
# No active loop, nothing to do
if [[ ! -f "$STATE_FILE" ]]; then
exit 0
fi
# Extract completion promise from state file frontmatter
FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$STATE_FILE")
COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/')
# No promise configured, nothing to check
if [[ "$COMPLETION_PROMISE" = "null" ]] || [[ -z "$COMPLETION_PROMISE" ]]; then
exit 0
fi
# Extract response text from hook input
RESPONSE_TEXT=$(echo "$HOOK_INPUT" | jq -r '.text // empty')
if [[ -z "$RESPONSE_TEXT" ]]; then
exit 0
fi
# Check for <promise>TEXT</promise> in the response
PROMISE_TEXT=$(echo "$RESPONSE_TEXT" | perl -0777 -pe 's/.*?<promise>(.*?)<\/promise>.*/$1/s; s/^\s+|\s+$//g; s/\s+/ /g' 2>/dev/null || echo "")
if [[ -n "$PROMISE_TEXT" ]] && [[ "$PROMISE_TEXT" = "$COMPLETION_PROMISE" ]]; then
touch "$DONE_FLAG"
fi
exit 0