A working implementation of the Ralph Wiggum autonomous loop technique for Claude Code.
The official ralph-wiggum plugin in Anthropic's plugin marketplace is currently broken due to a security update in Claude Code v1.0.20 (August 2025).
The security patch (CVE-2025-54795) correctly blocks $() command substitution and multi-line bash commands to prevent command injection attacks. However, the official plugin's command file contains multi-line bash that triggers this protection:
Error: Bash command permission check failed for pattern "...":
Command contains newlines that could separate multiple commands
Multiple issues have been filed:
As of January 3, 2026, these remain unfixed.
This is a clean reimplementation that works with current Claude Code security restrictions.
Key architectural difference: The official plugin tries to execute bash directly from the command file. This plugin instead instructs Claude to create the state file using its Write tool, keeping the command file pure markdown with no bash execution. The stop hook runs as a separate shell script outside the permission system.
claude --plugin-dir /path/to/ralph# Create marketplace
mkdir -p ~/claude-plugins/.claude-plugin
echo '{"name": "local", "owner": {"name": "You"}}' > ~/claude-plugins/.claude-plugin/marketplace.json
# Move plugin into marketplace
mv /path/to/ralph ~/claude-plugins/
# In Claude Code:
/plugin marketplace add ~/claude-plugins
/plugin install ralph@local/ralph:ralph-loop "Your task" --max-iterations 30 --completion-promise "DONE"
| Argument | Description | Default |
|---|---|---|
| prompt | The task to complete | (required) |
| --max-iterations N | Safety limit | 30 |
| --completion-promise TEXT | Completion signal | TASK_COMPLETE |
/ralph:ralph-loop "Build a REST API with tests" --completion-promise "API_COMPLETE"
/ralph:ralph-loop "Follow the instructions in CLAUDE.md" --max-iterations 50 --completion-promise "BENCHMARK_COMPLETE"
/ralph:cancel-ralph
- Command instructs Claude to create
.claude/ralph-loop.local.mdwith task and config - Claude works on the task
- Claude finishes its turn (natural stop)
- Stop hook fires, checks transcript for
<promise>COMPLETION_PROMISE</promise> - Not found? → Hook returns
{"decision": "block", "reason": "..."}, Claude continues - Found? → Hook allows stop, cleans up state file
- Max iterations? → Hook allows stop regardless
- Claude Code 1.0.20+
jq(for JSON parsing in stop hook)
- Geoffrey Huntley — Original Ralph technique
- Boris Cherny — Official plugin
- Ralph — This working reimplementation
MIT