@atlashub/smartstack-cli 1.2.0 → 1.3.0

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlashub/smartstack-cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "SmartStack Claude Code automation toolkit - GitFlow, APEX, EF Core migrations, prompts and more",
5
5
  "author": {
6
6
  "name": "SmartStack",
@@ -0,0 +1,18 @@
1
+ ---
2
+ description: "Cancel active Ralph Loop"
3
+ allowed-tools: ["Bash(test -f .claude/ralph-loop.local.md:*)", "Bash(rm .claude/ralph-loop.local.md)", "Read(.claude/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 `.claude/ralph-loop.local.md` exists using Bash: `test -f .claude/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 `.claude/ralph-loop.local.md` to get the current iteration number from the `iteration:` field
17
+ - Remove the file using Bash: `rm .claude/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 Loop plugin and available commands"
3
+ ---
4
+
5
+ # Ralph Loop Plugin Help
6
+
7
+ Please explain the following to the user:
8
+
9
+ ## What is Ralph Loop?
10
+
11
+ Ralph Loop implements the Ralph Wiggum technique - 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,18 @@
1
+ ---
2
+ description: "Start Ralph 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
+ Execute the setup script to initialize the Ralph loop:
11
+
12
+ ```!
13
+ "${CLAUDE_PLUGIN_ROOT}/scripts/setup-ralph-loop.sh" $ARGUMENTS
14
+ ```
15
+
16
+ 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.
17
+
18
+ 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.
@@ -10,6 +10,16 @@
10
10
  }
11
11
  ]
12
12
  }
13
+ ],
14
+ "Stop": [
15
+ {
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "${CLAUDE_HOOKS_DIR}/stop-hook.sh"
20
+ }
21
+ ]
22
+ }
13
23
  ]
14
24
  }
15
25
  }
@@ -0,0 +1,177 @@
1
+ #!/bin/bash
2
+
3
+ # Ralph Loop 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
+ set -euo pipefail
8
+
9
+ # Read hook input from stdin (advanced stop hook API)
10
+ HOOK_INPUT=$(cat)
11
+
12
+ # Check if ralph-loop is active
13
+ RALPH_STATE_FILE=".claude/ralph-loop.local.md"
14
+
15
+ if [[ ! -f "$RALPH_STATE_FILE" ]]; then
16
+ # No active loop - allow exit
17
+ exit 0
18
+ fi
19
+
20
+ # Parse markdown frontmatter (YAML between ---) and extract values
21
+ FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$RALPH_STATE_FILE")
22
+ ITERATION=$(echo "$FRONTMATTER" | grep '^iteration:' | sed 's/iteration: *//')
23
+ MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iterations: *//')
24
+ # Extract completion_promise and strip surrounding quotes if present
25
+ COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/')
26
+
27
+ # Validate numeric fields before arithmetic operations
28
+ if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then
29
+ echo "⚠️ Ralph loop: State file corrupted" >&2
30
+ echo " File: $RALPH_STATE_FILE" >&2
31
+ echo " Problem: 'iteration' field is not a valid number (got: '$ITERATION')" >&2
32
+ echo "" >&2
33
+ echo " This usually means the state file was manually edited or corrupted." >&2
34
+ echo " Ralph loop is stopping. Run /ralph-loop again to start fresh." >&2
35
+ rm "$RALPH_STATE_FILE"
36
+ exit 0
37
+ fi
38
+
39
+ if [[ ! "$MAX_ITERATIONS" =~ ^[0-9]+$ ]]; then
40
+ echo "⚠️ Ralph loop: State file corrupted" >&2
41
+ echo " File: $RALPH_STATE_FILE" >&2
42
+ echo " Problem: 'max_iterations' field is not a valid number (got: '$MAX_ITERATIONS')" >&2
43
+ echo "" >&2
44
+ echo " This usually means the state file was manually edited or corrupted." >&2
45
+ echo " Ralph loop is stopping. Run /ralph-loop again to start fresh." >&2
46
+ rm "$RALPH_STATE_FILE"
47
+ exit 0
48
+ fi
49
+
50
+ # Check if max iterations reached
51
+ if [[ $MAX_ITERATIONS -gt 0 ]] && [[ $ITERATION -ge $MAX_ITERATIONS ]]; then
52
+ echo "🛑 Ralph loop: Max iterations ($MAX_ITERATIONS) reached."
53
+ rm "$RALPH_STATE_FILE"
54
+ exit 0
55
+ fi
56
+
57
+ # Get transcript path from hook input
58
+ TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path')
59
+
60
+ if [[ ! -f "$TRANSCRIPT_PATH" ]]; then
61
+ echo "⚠️ Ralph loop: Transcript file not found" >&2
62
+ echo " Expected: $TRANSCRIPT_PATH" >&2
63
+ echo " This is unusual and may indicate a Claude Code internal issue." >&2
64
+ echo " Ralph loop is stopping." >&2
65
+ rm "$RALPH_STATE_FILE"
66
+ exit 0
67
+ fi
68
+
69
+ # Read last assistant message from transcript (JSONL format - one JSON per line)
70
+ # First check if there are any assistant messages
71
+ if ! grep -q '"role":"assistant"' "$TRANSCRIPT_PATH"; then
72
+ echo "⚠️ Ralph loop: No assistant messages found in transcript" >&2
73
+ echo " Transcript: $TRANSCRIPT_PATH" >&2
74
+ echo " This is unusual and may indicate a transcript format issue" >&2
75
+ echo " Ralph loop is stopping." >&2
76
+ rm "$RALPH_STATE_FILE"
77
+ exit 0
78
+ fi
79
+
80
+ # Extract last assistant message with explicit error handling
81
+ LAST_LINE=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1)
82
+ if [[ -z "$LAST_LINE" ]]; then
83
+ echo "⚠️ Ralph loop: Failed to extract last assistant message" >&2
84
+ echo " Ralph loop is stopping." >&2
85
+ rm "$RALPH_STATE_FILE"
86
+ exit 0
87
+ fi
88
+
89
+ # Parse JSON with error handling
90
+ LAST_OUTPUT=$(echo "$LAST_LINE" | jq -r '
91
+ .message.content |
92
+ map(select(.type == "text")) |
93
+ map(.text) |
94
+ join("\n")
95
+ ' 2>&1)
96
+
97
+ # Check if jq succeeded
98
+ if [[ $? -ne 0 ]]; then
99
+ echo "⚠️ Ralph loop: Failed to parse assistant message JSON" >&2
100
+ echo " Error: $LAST_OUTPUT" >&2
101
+ echo " This may indicate a transcript format issue" >&2
102
+ echo " Ralph loop is stopping." >&2
103
+ rm "$RALPH_STATE_FILE"
104
+ exit 0
105
+ fi
106
+
107
+ if [[ -z "$LAST_OUTPUT" ]]; then
108
+ echo "⚠️ Ralph loop: Assistant message contained no text content" >&2
109
+ echo " Ralph loop is stopping." >&2
110
+ rm "$RALPH_STATE_FILE"
111
+ exit 0
112
+ fi
113
+
114
+ # Check for completion promise (only if set)
115
+ if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then
116
+ # Extract text from <promise> tags using Perl for multiline support
117
+ # -0777 slurps entire input, s flag makes . match newlines
118
+ # .*? is non-greedy (takes FIRST tag), whitespace normalized
119
+ PROMISE_TEXT=$(echo "$LAST_OUTPUT" | perl -0777 -pe 's/.*?<promise>(.*?)<\/promise>.*/$1/s; s/^\s+|\s+$//g; s/\s+/ /g' 2>/dev/null || echo "")
120
+
121
+ # Use = for literal string comparison (not pattern matching)
122
+ # == in [[ ]] does glob pattern matching which breaks with *, ?, [ characters
123
+ if [[ -n "$PROMISE_TEXT" ]] && [[ "$PROMISE_TEXT" = "$COMPLETION_PROMISE" ]]; then
124
+ echo "✅ Ralph loop: Detected <promise>$COMPLETION_PROMISE</promise>"
125
+ rm "$RALPH_STATE_FILE"
126
+ exit 0
127
+ fi
128
+ fi
129
+
130
+ # Not complete - continue loop with SAME PROMPT
131
+ NEXT_ITERATION=$((ITERATION + 1))
132
+
133
+ # Extract prompt (everything after the closing ---)
134
+ # Skip first --- line, skip until second --- line, then print everything after
135
+ # Use i>=2 instead of i==2 to handle --- in prompt content
136
+ PROMPT_TEXT=$(awk '/^---$/{i++; next} i>=2' "$RALPH_STATE_FILE")
137
+
138
+ if [[ -z "$PROMPT_TEXT" ]]; then
139
+ echo "⚠️ Ralph loop: State file corrupted or incomplete" >&2
140
+ echo " File: $RALPH_STATE_FILE" >&2
141
+ echo " Problem: No prompt text found" >&2
142
+ echo "" >&2
143
+ echo " This usually means:" >&2
144
+ echo " • State file was manually edited" >&2
145
+ echo " • File was corrupted during writing" >&2
146
+ echo "" >&2
147
+ echo " Ralph loop is stopping. Run /ralph-loop again to start fresh." >&2
148
+ rm "$RALPH_STATE_FILE"
149
+ exit 0
150
+ fi
151
+
152
+ # Update iteration in frontmatter (portable across macOS and Linux)
153
+ # Create temp file, then atomically replace
154
+ TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$"
155
+ sed "s/^iteration: .*/iteration: $NEXT_ITERATION/" "$RALPH_STATE_FILE" > "$TEMP_FILE"
156
+ mv "$TEMP_FILE" "$RALPH_STATE_FILE"
157
+
158
+ # Build system message with iteration count and completion promise info
159
+ if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then
160
+ SYSTEM_MSG="🔄 Ralph iteration $NEXT_ITERATION | To stop: output <promise>$COMPLETION_PROMISE</promise> (ONLY when statement is TRUE - do not lie to exit!)"
161
+ else
162
+ SYSTEM_MSG="🔄 Ralph iteration $NEXT_ITERATION | No completion promise set - loop runs infinitely"
163
+ fi
164
+
165
+ # Output JSON to block the stop and feed prompt back
166
+ # The "reason" field contains the prompt that will be sent back to Claude
167
+ jq -n \
168
+ --arg prompt "$PROMPT_TEXT" \
169
+ --arg msg "$SYSTEM_MSG" \
170
+ '{
171
+ "decision": "block",
172
+ "reason": $prompt,
173
+ "systemMessage": $msg
174
+ }'
175
+
176
+ # Exit 0 for successful hook execution
177
+ exit 0