minimal-agent 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "minimal-agent",
3
- "version": "0.1.7",
4
- "description": "最小化 Agent 系统 —— 单对话 + 10 工具 + MultiEdit + Pre-read Guard + 自动压缩 + OpenAI 兼容 + Ink TUI",
3
+ "version": "0.1.9",
4
+ "description": "最小化 Agent 系统 —— 单对话 + 9 工具 + 自动压缩 + OpenAI 兼容 + Ink TUI(学习/教学用)",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Bill Wang <leiwang0359@gmail.com>",
7
7
  "repository": {
@@ -37,6 +37,7 @@
37
37
  "dist",
38
38
  "vendor/ripgrep",
39
39
  "skills",
40
+ "plugins",
40
41
  "README.md",
41
42
  "LICENSE"
42
43
  ],
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "ralph-wiggum",
3
+ "version": "1.0.0",
4
+ "description": "Implementation of the Ralph Wiggum technique - continuous self-referential AI loops for interactive iterative development. Run Claude in a while-true loop with the same prompt until task completion.",
5
+ "author": {
6
+ "name": "Daisy Hollman",
7
+ "email": "daisy@anthropic.com"
8
+ }
9
+ }
@@ -0,0 +1,179 @@
1
+ # Ralph Wiggum Plugin
2
+
3
+ Implementation of the Ralph Wiggum technique for iterative, self-referential AI development loops in Minimal Agent.
4
+
5
+ ## What is Ralph?
6
+
7
+ Ralph 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.
8
+
9
+ The technique is named after Ralph Wiggum from The Simpsons, embodying the philosophy of persistent iteration despite setbacks.
10
+
11
+ ### Core Concept
12
+
13
+ This plugin implements Ralph using a **Stop hook** that intercepts Minimal Agent's exit attempts:
14
+
15
+ ```bash
16
+ # You run ONCE:
17
+ /ralph-loop "Your task description" --completion-promise "DONE"
18
+
19
+ # Then Claude Code automatically:
20
+ # 1. Works on the task
21
+ # 2. Tries to exit
22
+ # 3. Stop hook blocks exit
23
+ # 4. Stop hook feeds the SAME prompt back
24
+ # 5. Repeat until completion
25
+ ```
26
+
27
+ 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.
28
+
29
+ This creates a **self-referential feedback loop** where:
30
+ - The prompt never changes between iterations
31
+ - Claude's previous work persists in files
32
+ - Each iteration sees modified files and git history
33
+ - Claude autonomously improves by reading its own past work in files
34
+
35
+ ## Quick Start
36
+
37
+ ```bash
38
+ /ralph-loop "Build a REST API for todos. Requirements: CRUD operations, input validation, tests. Output <promise>COMPLETE</promise> when done." --completion-promise "COMPLETE" --max-iterations 50
39
+ ```
40
+
41
+ Claude will:
42
+ - Implement the API iteratively
43
+ - Run tests and see failures
44
+ - Fix bugs based on test output
45
+ - Iterate until all requirements met
46
+ - Output the completion promise when done
47
+
48
+ ## Commands
49
+
50
+ ### /ralph-loop
51
+
52
+ Start a Ralph loop in your current session.
53
+
54
+ **Usage:**
55
+ ```bash
56
+ /ralph-loop "<prompt>" --max-iterations <n> --completion-promise "<text>"
57
+ ```
58
+
59
+ **Options:**
60
+ - `--max-iterations <n>` - Stop after N iterations (default: unlimited)
61
+ - `--completion-promise <text>` - Phrase that signals completion
62
+
63
+ ### /cancel-ralph
64
+
65
+ Cancel the active Ralph loop.
66
+
67
+ **Usage:**
68
+ ```bash
69
+ /cancel-ralph
70
+ ```
71
+
72
+ ## Prompt Writing Best Practices
73
+
74
+ ### 1. Clear Completion Criteria
75
+
76
+ ❌ Bad: "Build a todo API and make it good."
77
+
78
+ ✅ Good:
79
+ ```markdown
80
+ Build a REST API for todos.
81
+
82
+ When complete:
83
+ - All CRUD endpoints working
84
+ - Input validation in place
85
+ - Tests passing (coverage > 80%)
86
+ - README with API docs
87
+ - Output: <promise>COMPLETE</promise>
88
+ ```
89
+
90
+ ### 2. Incremental Goals
91
+
92
+ ❌ Bad: "Create a complete e-commerce platform."
93
+
94
+ ✅ Good:
95
+ ```markdown
96
+ Phase 1: User authentication (JWT, tests)
97
+ Phase 2: Product catalog (list/search, tests)
98
+ Phase 3: Shopping cart (add/remove, tests)
99
+
100
+ Output <promise>COMPLETE</promise> when all phases done.
101
+ ```
102
+
103
+ ### 3. Self-Correction
104
+
105
+ ❌ Bad: "Write code for feature X."
106
+
107
+ ✅ Good:
108
+ ```markdown
109
+ Implement feature X following TDD:
110
+ 1. Write failing tests
111
+ 2. Implement feature
112
+ 3. Run tests
113
+ 4. If any fail, debug and fix
114
+ 5. Refactor if needed
115
+ 6. Repeat until all green
116
+ 7. Output: <promise>COMPLETE</promise>
117
+ ```
118
+
119
+ ### 4. Escape Hatches
120
+
121
+ Always use `--max-iterations` as a safety net to prevent infinite loops on impossible tasks:
122
+
123
+ ```bash
124
+ # Recommended: Always set a reasonable iteration limit
125
+ /ralph-loop "Try to implement feature X" --max-iterations 20
126
+
127
+ # In your prompt, include what to do if stuck:
128
+ # "After 15 iterations, if not complete:
129
+ # - Document what's blocking progress
130
+ # - List what was attempted
131
+ # - Suggest alternative approaches"
132
+ ```
133
+
134
+ **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.
135
+
136
+ ## Philosophy
137
+
138
+ Ralph embodies several key principles:
139
+
140
+ ### 1. Iteration > Perfection
141
+ Don't aim for perfect on first try. Let the loop refine the work.
142
+
143
+ ### 2. Failures Are Data
144
+ "Deterministically bad" means failures are predictable and informative. Use them to tune prompts.
145
+
146
+ ### 3. Operator Skill Matters
147
+ Success depends on writing good prompts, not just having a good model.
148
+
149
+ ### 4. Persistence Wins
150
+ Keep trying until success. The loop handles retry logic automatically.
151
+
152
+ ## When to Use Ralph
153
+
154
+ **Good for:**
155
+ - Well-defined tasks with clear success criteria
156
+ - Tasks requiring iteration and refinement (e.g., getting tests to pass)
157
+ - Greenfield projects where you can walk away
158
+ - Tasks with automatic verification (tests, linters)
159
+
160
+ **Not good for:**
161
+ - Tasks requiring human judgment or design decisions
162
+ - One-shot operations
163
+ - Tasks with unclear success criteria
164
+ - Production debugging (use targeted debugging instead)
165
+
166
+ ## Real-World Results
167
+
168
+ - Successfully generated 6 repositories overnight in Y Combinator hackathon testing
169
+ - One $50k contract completed for $297 in API costs
170
+ - Created entire programming language ("cursed") over 3 months using this approach
171
+
172
+ ## Learn More
173
+
174
+ - Original technique: https://ghuntley.com/ralph/
175
+ - Ralph Orchestrator: https://github.com/mikeyobrien/ralph-orchestrator
176
+
177
+ ## For Help
178
+
179
+ Run `/help` in Claude Code for detailed command reference and examples.
@@ -0,0 +1,18 @@
1
+ ---
2
+ description: "Cancel active Ralph Wiggum loop"
3
+ allowed-tools: ["Bash(test -f .minimal-agent/ralph-loop.local.md:*)", "Bash(rm .minimal-agent/ralph-loop.local.md)", "Read(.minimal-agent/ralph-loop.local.md)"]
4
+ hide-from-slash-command-tool: "true"
5
+ ---
6
+
7
+ # Cancel Ralph
8
+
9
+ To cancel the Ralph loop:
10
+
11
+ 1. Check if `.minimal-agent/ralph-loop.local.md` exists using Bash: `test -f .minimal-agent/ralph-loop.local.md && echo "EXISTS" || echo "NOT_FOUND"`
12
+
13
+ 2. **If NOT_FOUND**: Say "No active Ralph loop found."
14
+
15
+ 3. **If EXISTS**:
16
+ - Read `.minimal-agent/ralph-loop.local.md` to get the current iteration number from the `iteration:` field
17
+ - Remove the file using Bash: `rm .minimal-agent/ralph-loop.local.md`
18
+ - Report: "Cancelled Ralph loop (was at iteration N)" where N is the iteration value
@@ -0,0 +1,126 @@
1
+ ---
2
+ description: "Explain Ralph Wiggum technique and available commands"
3
+ ---
4
+
5
+ # Ralph Wiggum Plugin Help
6
+
7
+ Please explain the following to the user:
8
+
9
+ ## What is the Ralph Wiggum Technique?
10
+
11
+ The Ralph Wiggum technique is an iterative development methodology based on continuous AI loops, pioneered by Geoffrey Huntley.
12
+
13
+ **Core concept:**
14
+ ```bash
15
+ while :; do
16
+ cat PROMPT.md | claude-code --continue
17
+ done
18
+ ```
19
+
20
+ 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.
21
+
22
+ **Each iteration:**
23
+ 1. Claude receives the SAME prompt
24
+ 2. Works on the task, modifying files
25
+ 3. Tries to exit
26
+ 4. Stop hook intercepts and feeds the same prompt again
27
+ 5. Claude sees its previous work in the files
28
+ 6. Iteratively improves until completion
29
+
30
+ The technique is described as "deterministically bad in an undeterministic world" - failures are predictable, enabling systematic improvement through prompt tuning.
31
+
32
+ ## Available Commands
33
+
34
+ ### /ralph-loop <PROMPT> [OPTIONS]
35
+
36
+ Start a Ralph loop in your current session.
37
+
38
+ **Usage:**
39
+ ```
40
+ /ralph-loop "Refactor the cache layer" --max-iterations 20
41
+ /ralph-loop "Add tests" --completion-promise "TESTS COMPLETE"
42
+ ```
43
+
44
+ **Options:**
45
+ - `--max-iterations <n>` - Max iterations before auto-stop
46
+ - `--completion-promise <text>` - Promise phrase to signal completion
47
+
48
+ **How it works:**
49
+ 1. Creates `.claude/.ralph-loop.local.md` state file
50
+ 2. You work on the task
51
+ 3. When you try to exit, stop hook intercepts
52
+ 4. Same prompt fed back
53
+ 5. You see your previous work
54
+ 6. Continues until promise detected or max iterations
55
+
56
+ ---
57
+
58
+ ### /cancel-ralph
59
+
60
+ Cancel an active Ralph loop (removes the loop state file).
61
+
62
+ **Usage:**
63
+ ```
64
+ /cancel-ralph
65
+ ```
66
+
67
+ **How it works:**
68
+ - Checks for active loop state file
69
+ - Removes `.claude/.ralph-loop.local.md`
70
+ - Reports cancellation with iteration count
71
+
72
+ ---
73
+
74
+ ## Key Concepts
75
+
76
+ ### Completion Promises
77
+
78
+ To signal completion, Claude must output a `<promise>` tag:
79
+
80
+ ```
81
+ <promise>TASK COMPLETE</promise>
82
+ ```
83
+
84
+ The stop hook looks for this specific tag. Without it (or `--max-iterations`), Ralph runs infinitely.
85
+
86
+ ### Self-Reference Mechanism
87
+
88
+ The "loop" doesn't mean Claude talks to itself. It means:
89
+ - Same prompt repeated
90
+ - Claude's work persists in files
91
+ - Each iteration sees previous attempts
92
+ - Builds incrementally toward goal
93
+
94
+ ## Example
95
+
96
+ ### Interactive Bug Fix
97
+
98
+ ```
99
+ /ralph-loop "Fix the token refresh logic in auth.ts. Output <promise>FIXED</promise> when all tests pass." --completion-promise "FIXED" --max-iterations 10
100
+ ```
101
+
102
+ You'll see Ralph:
103
+ - Attempt fixes
104
+ - Run tests
105
+ - See failures
106
+ - Iterate on solution
107
+ - In your current session
108
+
109
+ ## When to Use Ralph
110
+
111
+ **Good for:**
112
+ - Well-defined tasks with clear success criteria
113
+ - Tasks requiring iteration and refinement
114
+ - Iterative development with self-correction
115
+ - Greenfield projects
116
+
117
+ **Not good for:**
118
+ - Tasks requiring human judgment or design decisions
119
+ - One-shot operations
120
+ - Tasks with unclear success criteria
121
+ - Debugging production issues (use targeted debugging instead)
122
+
123
+ ## Learn More
124
+
125
+ - Original technique: https://ghuntley.com/ralph/
126
+ - Ralph Orchestrator: https://github.com/mikeyobrien/ralph-orchestrator
@@ -0,0 +1,69 @@
1
+ ---
2
+ description: "Start Ralph Wiggum loop in current session"
3
+ argument-hint: "PROMPT [--max-iterations N] [--completion-promise TEXT]"
4
+ allowed-tools: ["Bash(${CLAUDE_PLUGIN_ROOT}/scripts/setup-ralph-loop.sh:*)"]
5
+ hide-from-slash-command-tool: "true"
6
+ ---
7
+
8
+ # Ralph Loop Command
9
+
10
+ <!--
11
+ minimal-agent 注意:以下 ```! ``` 块是 Claude Code 专有的 shell-on-command
12
+ 语法,minimal-agent **不会执行**它。GoalState 的初始化(goal.md / phase.md
13
+ / completion.md 等)由 src/plugins/pluginRunner.ts 在进循环前自动完成,
14
+ 不需要 setup 脚本。该块保留在这里只是为了与原版 Claude Code 兼容。
15
+ -->
16
+
17
+ Execute the setup script to initialize the Ralph loop:
18
+
19
+ ```!
20
+ "${CLAUDE_PLUGIN_ROOT}/scripts/setup-ralph-loop.sh" $ARGUMENTS
21
+ ```
22
+
23
+ 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.
24
+
25
+ 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.
26
+
27
+ ---
28
+
29
+ ## Goal State 初始化(首次迭代必做)
30
+
31
+ 在开始工作之前,请先执行以下步骤:
32
+
33
+ 1. **确认目标文件**:检查 `.minimal-agent/goal.md` 是否已包含用户的原始目标。如未包含,将用户目标写入。
34
+ 2. **生成完成判据**:在 `.minimal-agent/completion.md` 中写入 JSON 格式的客观验证条件(如果尚未存在或内容不完整):
35
+ ```json
36
+ [
37
+ { "type": "shell", "command": "你的验证命令" },
38
+ { "type": "file_exists", "file": "期望存在的文件路径" },
39
+ { "type": "file_contains", "file": "文件路径", "pattern": "正则表达式" }
40
+ ]
41
+ ```
42
+ 3. **设置阶段**:确认 `.minimal-agent/phase.md` 内容为 `plan`。
43
+
44
+ > 以上文件由 GoalState 系统自动管理,每轮迭代开始时会自动注入到你的上下文头部。你只需要在第一轮确保它们的内容正确。
45
+
46
+ ## 阶段工作流
47
+
48
+ 循环会自动管理阶段转换(PLAN → BUILD → VERIFY → HEAL → DONE),但你应主动配合当前阶段:
49
+
50
+ - **PLAN 阶段**:分析任务、拆解步骤、识别风险
51
+ - **BUILD 阶段**:按计划实现代码,每次聚焦一个子任务
52
+ - **VERIFY 阶段**:运行测试、检查输出、验证功能
53
+ - **HEAL 阶段**:定位问题根因、修复 bug、避免回归
54
+
55
+ ## 完成哨兵
56
+
57
+ 当且仅当任务真正完成时,输出:
58
+ ```
59
+ <promise>DONE</promise>
60
+ ```
61
+
62
+ 如果发现方案根本不可行需要重新规划:
63
+ ```
64
+ <PROMISE>NEED_REPLAN</PROMISE>
65
+ ```
66
+
67
+ ## 踩坑记录
68
+
69
+ 如果在工作中发现任何值得记录的教训(坑、陷阱、最佳实践),请明确说明,系统会自动记录到 `.minimal-agent/learnings.md` 供后续迭代参考。
@@ -0,0 +1,15 @@
1
+ {
2
+ "description": "Ralph Wiggum plugin stop hook for self-referential loops",
3
+ "hooks": {
4
+ "Stop": [
5
+ {
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh"
10
+ }
11
+ ]
12
+ }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,191 @@
1
+ #!/bin/bash
2
+
3
+ # Ralph Wiggum Stop Hook
4
+ # Prevents session exit when a ralph-loop is active
5
+ # Feeds Claude's output back as input to continue the loop
6
+ #
7
+ # NOTE (minimal-agent):
8
+ # This hook is the original Claude Code contract — kept here so the plugin
9
+ # stays portable to upstream. In minimal-agent the loop is driven natively
10
+ # by src/plugins/pluginRunner.ts; this hook is consulted as an *advisory*
11
+ # signal (block → inject reason into next iteration; pass / errors → no
12
+ # effect on loop termination). The loop's real exit conditions are:
13
+ # - <promise>DONE</promise> sentinel + verification pass
14
+ # - --max-iterations reached
15
+ # - user abort / NEED_REPLAN
16
+ # On Windows minimal-agent skips this hook entirely (bash unavailable).
17
+ # The .minimal-agent/ralph-loop.local.md state file referenced below is
18
+ # NOT created by minimal-agent — GoalState writes goal.md/phase.md/etc
19
+ # instead, so this hook simply exits 0 in minimal-agent runs.
20
+
21
+ set -euo pipefail
22
+
23
+ # Read hook input from stdin (advanced stop hook API)
24
+ HOOK_INPUT=$(cat)
25
+
26
+ # Check if ralph-loop is active
27
+ RALPH_STATE_FILE=".minimal-agent/ralph-loop.local.md"
28
+
29
+ if [[ ! -f "$RALPH_STATE_FILE" ]]; then
30
+ # No active loop - allow exit
31
+ exit 0
32
+ fi
33
+
34
+ # Parse markdown frontmatter (YAML between ---) and extract values
35
+ FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$RALPH_STATE_FILE")
36
+ ITERATION=$(echo "$FRONTMATTER" | grep '^iteration:' | sed 's/iteration: *//')
37
+ MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iterations: *//')
38
+ # Extract completion_promise and strip surrounding quotes if present
39
+ COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/')
40
+
41
+ # Validate numeric fields before arithmetic operations
42
+ if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then
43
+ echo "⚠️ Ralph loop: State file corrupted" >&2
44
+ echo " File: $RALPH_STATE_FILE" >&2
45
+ echo " Problem: 'iteration' field is not a valid number (got: '$ITERATION')" >&2
46
+ echo "" >&2
47
+ echo " This usually means the state file was manually edited or corrupted." >&2
48
+ echo " Ralph loop is stopping. Run /ralph-loop again to start fresh." >&2
49
+ rm "$RALPH_STATE_FILE"
50
+ exit 0
51
+ fi
52
+
53
+ if [[ ! "$MAX_ITERATIONS" =~ ^[0-9]+$ ]]; then
54
+ echo "⚠️ Ralph loop: State file corrupted" >&2
55
+ echo " File: $RALPH_STATE_FILE" >&2
56
+ echo " Problem: 'max_iterations' field is not a valid number (got: '$MAX_ITERATIONS')" >&2
57
+ echo "" >&2
58
+ echo " This usually means the state file was manually edited or corrupted." >&2
59
+ echo " Ralph loop is stopping. Run /ralph-loop again to start fresh." >&2
60
+ rm "$RALPH_STATE_FILE"
61
+ exit 0
62
+ fi
63
+
64
+ # Check if max iterations reached
65
+ if [[ $MAX_ITERATIONS -gt 0 ]] && [[ $ITERATION -ge $MAX_ITERATIONS ]]; then
66
+ echo "🛑 Ralph loop: Max iterations ($MAX_ITERATIONS) reached."
67
+ rm "$RALPH_STATE_FILE"
68
+ exit 0
69
+ fi
70
+
71
+ # Get transcript path from hook input
72
+ TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path')
73
+
74
+ if [[ ! -f "$TRANSCRIPT_PATH" ]]; then
75
+ echo "⚠️ Ralph loop: Transcript file not found" >&2
76
+ echo " Expected: $TRANSCRIPT_PATH" >&2
77
+ echo " This is unusual and may indicate a Claude Code internal issue." >&2
78
+ echo " Ralph loop is stopping." >&2
79
+ rm "$RALPH_STATE_FILE"
80
+ exit 0
81
+ fi
82
+
83
+ # Read last assistant message from transcript (JSONL format - one JSON per line)
84
+ # First check if there are any assistant messages
85
+ if ! grep -q '"role":"assistant"' "$TRANSCRIPT_PATH"; then
86
+ echo "⚠️ Ralph loop: No assistant messages found in transcript" >&2
87
+ echo " Transcript: $TRANSCRIPT_PATH" >&2
88
+ echo " This is unusual and may indicate a transcript format issue" >&2
89
+ echo " Ralph loop is stopping." >&2
90
+ rm "$RALPH_STATE_FILE"
91
+ exit 0
92
+ fi
93
+
94
+ # Extract last assistant message with explicit error handling
95
+ LAST_LINE=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1)
96
+ if [[ -z "$LAST_LINE" ]]; then
97
+ echo "⚠️ Ralph loop: Failed to extract last assistant message" >&2
98
+ echo " Ralph loop is stopping." >&2
99
+ rm "$RALPH_STATE_FILE"
100
+ exit 0
101
+ fi
102
+
103
+ # Parse JSON with error handling
104
+ LAST_OUTPUT=$(echo "$LAST_LINE" | jq -r '
105
+ .message.content |
106
+ map(select(.type == "text")) |
107
+ map(.text) |
108
+ join("\n")
109
+ ' 2>&1)
110
+
111
+ # Check if jq succeeded
112
+ if [[ $? -ne 0 ]]; then
113
+ echo "⚠️ Ralph loop: Failed to parse assistant message JSON" >&2
114
+ echo " Error: $LAST_OUTPUT" >&2
115
+ echo " This may indicate a transcript format issue" >&2
116
+ echo " Ralph loop is stopping." >&2
117
+ rm "$RALPH_STATE_FILE"
118
+ exit 0
119
+ fi
120
+
121
+ if [[ -z "$LAST_OUTPUT" ]]; then
122
+ echo "⚠️ Ralph loop: Assistant message contained no text content" >&2
123
+ echo " Ralph loop is stopping." >&2
124
+ rm "$RALPH_STATE_FILE"
125
+ exit 0
126
+ fi
127
+
128
+ # Check for completion promise (only if set)
129
+ if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then
130
+ # Extract text from <promise> tags using Perl for multiline support
131
+ # -0777 slurps entire input, s flag makes . match newlines
132
+ # .*? is non-greedy (takes FIRST tag), whitespace normalized
133
+ PROMISE_TEXT=$(echo "$LAST_OUTPUT" | perl -0777 -pe 's/.*?<promise>(.*?)<\/promise>.*/$1/s; s/^\s+|\s+$//g; s/\s+/ /g' 2>/dev/null || echo "")
134
+
135
+ # Use = for literal string comparison (not pattern matching)
136
+ # == in [[ ]] does glob pattern matching which breaks with *, ?, [ characters
137
+ if [[ -n "$PROMISE_TEXT" ]] && [[ "$PROMISE_TEXT" = "$COMPLETION_PROMISE" ]]; then
138
+ echo "✅ Ralph loop: Detected <promise>$COMPLETION_PROMISE</promise>"
139
+ rm "$RALPH_STATE_FILE"
140
+ exit 0
141
+ fi
142
+ fi
143
+
144
+ # Not complete - continue loop with SAME PROMPT
145
+ NEXT_ITERATION=$((ITERATION + 1))
146
+
147
+ # Extract prompt (everything after the closing ---)
148
+ # Skip first --- line, skip until second --- line, then print everything after
149
+ # Use i>=2 instead of i==2 to handle --- in prompt content
150
+ PROMPT_TEXT=$(awk '/^---$/{i++; next} i>=2' "$RALPH_STATE_FILE")
151
+
152
+ if [[ -z "$PROMPT_TEXT" ]]; then
153
+ echo "⚠️ Ralph loop: State file corrupted or incomplete" >&2
154
+ echo " File: $RALPH_STATE_FILE" >&2
155
+ echo " Problem: No prompt text found" >&2
156
+ echo "" >&2
157
+ echo " This usually means:" >&2
158
+ echo " • State file was manually edited" >&2
159
+ echo " • File was corrupted during writing" >&2
160
+ echo "" >&2
161
+ echo " Ralph loop is stopping. Run /ralph-loop again to start fresh." >&2
162
+ rm "$RALPH_STATE_FILE"
163
+ exit 0
164
+ fi
165
+
166
+ # Update iteration in frontmatter (portable across macOS and Linux)
167
+ # Create temp file, then atomically replace
168
+ TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$"
169
+ sed "s/^iteration: .*/iteration: $NEXT_ITERATION/" "$RALPH_STATE_FILE" > "$TEMP_FILE"
170
+ mv "$TEMP_FILE" "$RALPH_STATE_FILE"
171
+
172
+ # Build system message with iteration count and completion promise info
173
+ if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then
174
+ SYSTEM_MSG="🔄 Ralph iteration $NEXT_ITERATION | To stop: output <promise>$COMPLETION_PROMISE</promise> (ONLY when statement is TRUE - do not lie to exit!)"
175
+ else
176
+ SYSTEM_MSG="🔄 Ralph iteration $NEXT_ITERATION | No completion promise set - loop runs infinitely"
177
+ fi
178
+
179
+ # Output JSON to block the stop and feed prompt back
180
+ # The "reason" field contains the prompt that will be sent back to Claude
181
+ jq -n \
182
+ --arg prompt "$PROMPT_TEXT" \
183
+ --arg msg "$SYSTEM_MSG" \
184
+ '{
185
+ "decision": "block",
186
+ "reason": $prompt,
187
+ "systemMessage": $msg
188
+ }'
189
+
190
+ # Exit 0 for successful hook execution
191
+ exit 0