@herbcaudill/ralph 0.7.0 → 0.8.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/dist/components/EventDisplay.replay.test.js +32 -32
- package/package.json +2 -2
- package/templates/prompt-beads.md +1 -1
- package/README.md +0 -183
|
@@ -85,24 +85,24 @@ describe("EventDisplay replay tests", () => {
|
|
|
85
85
|
Good! The build is successful and all tests pass. Now let me check the todo list to find the
|
|
86
86
|
highest-priority task.
|
|
87
87
|
|
|
88
|
-
Read
|
|
88
|
+
Read ../.ralph/todo.md
|
|
89
89
|
|
|
90
90
|
Looking at the todo list, I'll prioritize the "Inline code blocks are being put in separate
|
|
91
91
|
paragraphs" issue. This is a functional bug affecting the display quality, whereas the others are
|
|
92
92
|
enhancements. Let me investigate this issue first.
|
|
93
93
|
|
|
94
|
-
Read src/components/StreamingText.tsx
|
|
94
|
+
Read ../src/components/StreamingText.tsx
|
|
95
95
|
|
|
96
96
|
Let me check the tests to understand the expected behavior and see if there's a failing test case:
|
|
97
97
|
|
|
98
|
-
Read src/components/StreamingText.test.tsx
|
|
98
|
+
Read ../src/components/StreamingText.test.tsx
|
|
99
99
|
|
|
100
100
|
I need to understand where StreamingText is being used and what the actual problem is. Let me check
|
|
101
101
|
where it's called from and look at some example output:
|
|
102
102
|
|
|
103
103
|
Grep StreamingText
|
|
104
104
|
|
|
105
|
-
Read src/components/EventDisplay.tsx
|
|
105
|
+
Read ../src/components/EventDisplay.tsx
|
|
106
106
|
|
|
107
107
|
I see the issue now! In EventDisplay.tsx:70, there's a <Box flexDirection="column" gap={1}> which
|
|
108
108
|
wraps each StreamingText component. Each StreamingText (which represents text blocks) is being
|
|
@@ -114,7 +114,7 @@ describe("EventDisplay replay tests", () => {
|
|
|
114
114
|
|
|
115
115
|
Let me look at how the events are being split into blocks:
|
|
116
116
|
|
|
117
|
-
Read src/components/eventToBlocks.ts
|
|
117
|
+
Read ../src/components/eventToBlocks.ts
|
|
118
118
|
|
|
119
119
|
Now I understand the issue. Claude's message content can have multiple text blocks (e.g., "Here's
|
|
120
120
|
the code: ", then a tool_use block, then " and that's it"). Each text block becomes a separate
|
|
@@ -124,11 +124,11 @@ describe("EventDisplay replay tests", () => {
|
|
|
124
124
|
The fix should be to merge consecutive text blocks together. Let me write a test first to
|
|
125
125
|
demonstrate the problem:
|
|
126
126
|
|
|
127
|
-
Read src/components/EventDisplay.test.tsx
|
|
127
|
+
Read ../src/components/EventDisplay.test.tsx
|
|
128
128
|
|
|
129
129
|
Now let me add a test that demonstrates the inline code block problem and then fix it:
|
|
130
130
|
|
|
131
|
-
Edit src/components/EventDisplay.test.tsx
|
|
131
|
+
Edit ../src/components/EventDisplay.test.tsx
|
|
132
132
|
|
|
133
133
|
Now let me run the test to confirm it fails (demonstrating the problem):
|
|
134
134
|
|
|
@@ -137,11 +137,11 @@ describe("EventDisplay replay tests", () => {
|
|
|
137
137
|
Perfect! The test fails as expected, confirming the bug. Now I need to fix it by modifying
|
|
138
138
|
eventToBlocks to merge consecutive text blocks:
|
|
139
139
|
|
|
140
|
-
Edit src/components/eventToBlocks.ts
|
|
140
|
+
Edit ../src/components/eventToBlocks.ts
|
|
141
141
|
|
|
142
142
|
Now I need to also flush the text buffer at the end of the loop:
|
|
143
143
|
|
|
144
|
-
Edit src/components/eventToBlocks.ts
|
|
144
|
+
Edit ../src/components/eventToBlocks.ts
|
|
145
145
|
|
|
146
146
|
Now let me run the tests again to verify the fix works:
|
|
147
147
|
|
|
@@ -150,20 +150,20 @@ describe("EventDisplay replay tests", () => {
|
|
|
150
150
|
The test still fails. This suggests the problem might also be in EventDisplay, not just
|
|
151
151
|
eventToBlocks. Let me debug by adding a simpler test to see what's happening in eventToBlocks:
|
|
152
152
|
|
|
153
|
-
Read src/components/eventToBlocks.test.ts
|
|
153
|
+
Read ../src/components/eventToBlocks.test.ts
|
|
154
154
|
|
|
155
155
|
I see! The existing test "extracts multiple text blocks" expects them NOT to be merged. I need to
|
|
156
156
|
update this test and add a new one specifically for consecutive text blocks:
|
|
157
157
|
|
|
158
|
-
Edit src/components/eventToBlocks.test.ts
|
|
158
|
+
Edit ../src/components/eventToBlocks.test.ts
|
|
159
159
|
|
|
160
160
|
Now let me add a test for text blocks separated by tool calls:
|
|
161
161
|
|
|
162
|
-
Read src/components/eventToBlocks.test.ts
|
|
162
|
+
Read ../src/components/eventToBlocks.test.ts
|
|
163
163
|
|
|
164
164
|
Let me add a test that specifically checks text blocks separated by tool calls:
|
|
165
165
|
|
|
166
|
-
Edit src/components/eventToBlocks.test.ts
|
|
166
|
+
Edit ../src/components/eventToBlocks.test.ts
|
|
167
167
|
|
|
168
168
|
Now let me run all tests to verify my changes to eventToBlocks work:
|
|
169
169
|
|
|
@@ -171,12 +171,12 @@ describe("EventDisplay replay tests", () => {
|
|
|
171
171
|
|
|
172
172
|
I need to look at this failing test:
|
|
173
173
|
|
|
174
|
-
Read src/components/eventToBlocks.test.ts
|
|
174
|
+
Read ../src/components/eventToBlocks.test.ts
|
|
175
175
|
|
|
176
176
|
This test expects 3 separate IDs for 3 text blocks, but my code now merges them into 1. I need to
|
|
177
177
|
update this test to reflect the new behavior:
|
|
178
178
|
|
|
179
|
-
Edit src/components/eventToBlocks.test.ts
|
|
179
|
+
Edit ../src/components/eventToBlocks.test.ts
|
|
180
180
|
|
|
181
181
|
Now let me run the eventToBlocks tests again:
|
|
182
182
|
|
|
@@ -190,7 +190,7 @@ describe("EventDisplay replay tests", () => {
|
|
|
190
190
|
rendered. The issue must be in how EventDisplay merges the events. Let me look more carefully at the
|
|
191
191
|
EventDisplay logic:
|
|
192
192
|
|
|
193
|
-
Read src/components/EventDisplay.tsx
|
|
193
|
+
Read ../src/components/EventDisplay.tsx
|
|
194
194
|
|
|
195
195
|
I see! The EventDisplay is doing its own deduplication, but it's NOT merging consecutive text
|
|
196
196
|
blocks. The text blocks in the test data remain separate even after eventToBlocks merges them,
|
|
@@ -235,13 +235,13 @@ describe("EventDisplay replay tests", () => {
|
|
|
235
235
|
|
|
236
236
|
That should work! Let me check if there's an issue with my implementation. Let me re-read the code:
|
|
237
237
|
|
|
238
|
-
Read src/components/eventToBlocks.ts
|
|
238
|
+
Read ../src/components/eventToBlocks.ts
|
|
239
239
|
|
|
240
240
|
The code looks correct. Let me check if the EventDisplay is receiving the correct merged content. I
|
|
241
241
|
think the issue might be in how EventDisplay deduplicates and merges content before calling
|
|
242
242
|
eventToBlocks. Let me look at that logic again:
|
|
243
243
|
|
|
244
|
-
Read src/components/EventDisplay.tsx
|
|
244
|
+
Read ../src/components/EventDisplay.tsx
|
|
245
245
|
|
|
246
246
|
Ah! I see the problem now. In the test, we're providing a single event with one message containing 3
|
|
247
247
|
text blocks. EventDisplay keeps the text blocks separate in uniqueContent (lines 36-54), so they
|
|
@@ -269,7 +269,7 @@ describe("EventDisplay replay tests", () => {
|
|
|
269
269
|
|
|
270
270
|
Let me print what lastFrame() actually returns by modifying the test temporarily:
|
|
271
271
|
|
|
272
|
-
Edit src/components/EventDisplay.test.tsx
|
|
272
|
+
Edit ../src/components/EventDisplay.test.tsx
|
|
273
273
|
|
|
274
274
|
$ pnpm test EventDisplay.test.tsx 2>&1 | grep -A 20 "merges consecutive"
|
|
275
275
|
|
|
@@ -280,7 +280,7 @@ describe("EventDisplay replay tests", () => {
|
|
|
280
280
|
This means the EventDisplay deduplication logic is keeping them separate, OR there's a bug in my
|
|
281
281
|
eventToBlocks implementation. Let me add logging to eventToBlocks to see:
|
|
282
282
|
|
|
283
|
-
Edit src/components/eventToBlocks.ts
|
|
283
|
+
Edit ../src/components/eventToBlocks.ts
|
|
284
284
|
|
|
285
285
|
$ pnpm test EventDisplay.test.tsx 2>&1 | grep -A 30 "eventToBlocks content"
|
|
286
286
|
|
|
@@ -296,9 +296,9 @@ describe("EventDisplay replay tests", () => {
|
|
|
296
296
|
But the output still shows them on 3 separate lines. This means my merging logic isn't working. Let
|
|
297
297
|
me add more logging to see what's happening in the loop:
|
|
298
298
|
|
|
299
|
-
Edit src/components/eventToBlocks.ts
|
|
299
|
+
Edit ../src/components/eventToBlocks.ts
|
|
300
300
|
|
|
301
|
-
Edit src/components/eventToBlocks.ts
|
|
301
|
+
Edit ../src/components/eventToBlocks.ts
|
|
302
302
|
|
|
303
303
|
$ pnpm test EventDisplay.test.tsx 2>&1 | grep -A 50 "merges consecutive text blocks to prevent
|
|
304
304
|
unwanted gaps"
|
|
@@ -341,7 +341,7 @@ describe("EventDisplay replay tests", () => {
|
|
|
341
341
|
Let me check if the problem is in how StreamingText uses fragments. When you return a fragment with
|
|
342
342
|
multiple <Text> components in Ink, they might be rendered on separate lines. Let me check:
|
|
343
343
|
|
|
344
|
-
Read src/components/StreamingText.tsx
|
|
344
|
+
Read ../src/components/StreamingText.tsx
|
|
345
345
|
|
|
346
346
|
The issue is that StreamingText returns a fragment with multiple <Text> components. In Ink, these
|
|
347
347
|
might be rendered as separate blocks. The fix is to wrap them in a single <Text> component or use
|
|
@@ -350,17 +350,17 @@ describe("EventDisplay replay tests", () => {
|
|
|
350
350
|
Let me look at how Ink handles multiple Text components. Actually, the fix is simple - wrap all the
|
|
351
351
|
text parts in a single Box or Text component with inline rendering:
|
|
352
352
|
|
|
353
|
-
Edit src/components/StreamingText.tsx
|
|
353
|
+
Edit ../src/components/StreamingText.tsx
|
|
354
354
|
|
|
355
|
-
Edit src/components/StreamingText.tsx
|
|
355
|
+
Edit ../src/components/StreamingText.tsx
|
|
356
356
|
|
|
357
357
|
Now let me remove the console.log statements and test:
|
|
358
358
|
|
|
359
|
-
Edit src/components/eventToBlocks.ts
|
|
359
|
+
Edit ../src/components/eventToBlocks.ts
|
|
360
360
|
|
|
361
|
-
Edit src/components/eventToBlocks.ts
|
|
361
|
+
Edit ../src/components/eventToBlocks.ts
|
|
362
362
|
|
|
363
|
-
Edit src/components/EventDisplay.test.tsx
|
|
363
|
+
Edit ../src/components/EventDisplay.test.tsx
|
|
364
364
|
|
|
365
365
|
Now let me run all tests:
|
|
366
366
|
|
|
@@ -372,13 +372,13 @@ describe("EventDisplay replay tests", () => {
|
|
|
372
372
|
|
|
373
373
|
Excellent! Now let me update the todo list and progress file, then commit:
|
|
374
374
|
|
|
375
|
-
Read
|
|
375
|
+
Read ../.ralph/todo.md
|
|
376
376
|
|
|
377
|
-
Edit
|
|
377
|
+
Edit ../.ralph/todo.md
|
|
378
378
|
|
|
379
|
-
Read
|
|
379
|
+
Read ../.ralph/progress.md
|
|
380
380
|
|
|
381
|
-
Edit
|
|
381
|
+
Edit ../.ralph/progress.md
|
|
382
382
|
|
|
383
383
|
Now let me format the code with Prettier:
|
|
384
384
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herbcaudill/ralph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Autonomous AI iteration engine for Claude CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"typecheck": "tsc --noEmit",
|
|
60
60
|
"ralph": "tsx src/index.ts",
|
|
61
61
|
"test:all": "pnpm typecheck && pnpm test",
|
|
62
|
-
"test": "vitest
|
|
62
|
+
"test": "vitest run",
|
|
63
63
|
"test:e2e": "vitest --config vitest.e2e.config.ts",
|
|
64
64
|
"test:watch": "vitest --watch",
|
|
65
65
|
"test:ui": "vitest --ui",
|
|
@@ -19,7 +19,7 @@ If there are none:
|
|
|
19
19
|
|
|
20
20
|
### Step 3: Select a task
|
|
21
21
|
|
|
22
|
-
Select the highest-priority issue to work on.
|
|
22
|
+
Select the highest-priority issue to work on. Finish in-progress tasks first. Bugs take priority. Otherwise use your best judgement.
|
|
23
23
|
|
|
24
24
|
### Step 4: Work on a single task
|
|
25
25
|
|
package/README.md
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
```
|
|
2
|
-
█▀█ ▄▀█ █ █▀█ █ █
|
|
3
|
-
█▀▄ █▀█ █▄▄ █▀▀ █▀█
|
|
4
|
-
```
|
|
5
|
-
|
|
6
|
-
Autonomous AI iteration engine for Claude CLI. Ralph runs Claude in a loop to systematically work through your codebase's task list, enabling Claude to tackle work items iteratively.
|
|
7
|
-
|
|
8
|
-
https://github.com/user-attachments/assets/e8d89a33-8db5-4d72-9c97-927558f92516
|
|
9
|
-
|
|
10
|
-
## Overview
|
|
11
|
-
|
|
12
|
-
Ralph spawns individual Claude CLI sessions that:
|
|
13
|
-
|
|
14
|
-
1. Check project health (build, tests)
|
|
15
|
-
2. Select and work on the highest-priority task
|
|
16
|
-
3. Validate changes with tests
|
|
17
|
-
4. Commit changes
|
|
18
|
-
5. Repeat
|
|
19
|
-
|
|
20
|
-
## Installation
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
pnpm add -D @herbcaudill/ralph
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Or use directly with npx:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npx @herbcaudill/ralph
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Quick start
|
|
33
|
-
|
|
34
|
-
1. **Initialize ralph in your project:**
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
npx @herbcaudill/ralph init
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
This creates a `.ralph/` directory with template files:
|
|
41
|
-
- `prompt.md` - Instructions for Claude during each iteration
|
|
42
|
-
- `todo.md` - Your task list (optional if using bd)
|
|
43
|
-
- `events.jsonl` - Event log in JSONL format (auto-generated during runs)
|
|
44
|
-
|
|
45
|
-
2. **Customize the workflow:**
|
|
46
|
-
|
|
47
|
-
Edit `.ralph/prompt.md` to match your project's workflow (build commands, test commands, etc.).
|
|
48
|
-
|
|
49
|
-
3. **Add tasks:**
|
|
50
|
-
|
|
51
|
-
You can manage tasks using either a markdown file or [beads](https://github.com/steveyegge/beads), a lightweight issue tracker.
|
|
52
|
-
|
|
53
|
-
**Option A: Using `.ralph/todo.md`**
|
|
54
|
-
|
|
55
|
-
```markdown
|
|
56
|
-
### To do
|
|
57
|
-
|
|
58
|
-
- [ ] Add user authentication
|
|
59
|
-
- [ ] Fix login form validation
|
|
60
|
-
- [ ] Write tests for auth flow
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
### Done
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
**Option B: Using beads (recommended)**
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
# Install bd
|
|
71
|
-
brew install herbcaudill/tap/bd
|
|
72
|
-
|
|
73
|
-
# Initialize in your project
|
|
74
|
-
bd init
|
|
75
|
-
|
|
76
|
-
# Create issues
|
|
77
|
-
bd create "Add user authentication"
|
|
78
|
-
bd create "Fix login form validation"
|
|
79
|
-
bd create "Write tests for auth flow"
|
|
80
|
-
|
|
81
|
-
# View ready work
|
|
82
|
-
bd ready
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
4. **Run ralph:**
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
npx ralph # Run with dynamic default (120% of open issues)
|
|
89
|
-
npx ralph 5 # Run 5 iterations
|
|
90
|
-
npx ralph --watch # Run and watch for new issues
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Commands
|
|
94
|
-
|
|
95
|
-
| Command | Description |
|
|
96
|
-
| --------------- | -------------------------------------------------------------- |
|
|
97
|
-
| `ralph` | Run with dynamic default (120% of open issues, min 10 max 100) |
|
|
98
|
-
| `ralph <n>` | Run specified number of iterations |
|
|
99
|
-
| `ralph init` | Initialize .ralph directory with templates |
|
|
100
|
-
| `ralph --watch` | Watch for new issues after completion |
|
|
101
|
-
| `ralph --help` | Show help |
|
|
102
|
-
|
|
103
|
-
## Watch mode
|
|
104
|
-
|
|
105
|
-
Watch mode (`--watch`) keeps ralph running after completing all tasks, waiting for new issues to be added:
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
npx ralph --watch
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
When ralph finishes all available work and Claude outputs the completion signal, instead of exiting, it enters a waiting state. When you add a new issue via `bd create` or `bd q`, ralph automatically detects it and starts a new iteration to work on it.
|
|
112
|
-
|
|
113
|
-
This enables a "continuous integration" style workflow:
|
|
114
|
-
|
|
115
|
-
1. Start ralph in watch mode: `npx ralph --watch`
|
|
116
|
-
2. Add tasks as you think of them: `bd q "Fix header alignment"`
|
|
117
|
-
3. Ralph automatically picks up and works on new tasks
|
|
118
|
-
4. Keep adding tasks while ralph works
|
|
119
|
-
|
|
120
|
-
Watch mode requires **bd** (beads) for issue tracking—it watches for mutations to the beads database.
|
|
121
|
-
|
|
122
|
-
## Configuration
|
|
123
|
-
|
|
124
|
-
**`.ralph/prompt.md`**
|
|
125
|
-
|
|
126
|
-
Instructions for Claude's workflow. Customize this for your project:
|
|
127
|
-
|
|
128
|
-
- Build/typecheck commands
|
|
129
|
-
- Test commands
|
|
130
|
-
- Project-specific conventions
|
|
131
|
-
- Commit message style
|
|
132
|
-
|
|
133
|
-
### Customizing the prompt
|
|
134
|
-
|
|
135
|
-
The default prompt ([for beads](./templates/prompt-beads.md) or [for TODO.md](./templates/prompt-todos.md)) works for my projects, but you should customize it for your project. Replace with your actual build/test commands (e.g., `pnpm build`, `cargo test`, `pytest`, etc.).
|
|
136
|
-
|
|
137
|
-
## How it works
|
|
138
|
-
|
|
139
|
-
Ralph is a thin wrapper around the Claude CLI that:
|
|
140
|
-
|
|
141
|
-
1. **Spawns Claude CLI** with your project context (prompt, todo, progress files)
|
|
142
|
-
2. **Captures output** as streaming JSON events
|
|
143
|
-
3. **Processes events** to display tool usage (Read, Edit, Bash, etc.) in a readable format
|
|
144
|
-
4. **Logs everything** to `events.jsonl` for debugging
|
|
145
|
-
5. **Detects completion** when Claude outputs `<promise>COMPLETE</promise>`
|
|
146
|
-
6. **Recursively runs** next iteration until count reached or todo list complete
|
|
147
|
-
|
|
148
|
-
## Requirements
|
|
149
|
-
|
|
150
|
-
- **Claude CLI** must be installed and configured
|
|
151
|
-
- **Node.js** 18 or higher
|
|
152
|
-
- Git repository (for commits)
|
|
153
|
-
|
|
154
|
-
Install Claude CLI:
|
|
155
|
-
|
|
156
|
-
```bash
|
|
157
|
-
# macOS/Linux
|
|
158
|
-
curl https://claude.com/cli | sh
|
|
159
|
-
|
|
160
|
-
# Or with Homebrew
|
|
161
|
-
brew install anthropics/tap/claude
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Configure Claude CLI:
|
|
165
|
-
|
|
166
|
-
```bash
|
|
167
|
-
claude auth
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## Tips
|
|
171
|
-
|
|
172
|
-
- **Start with small iteration counts** (3-5) to verify the workflow before running longer sessions
|
|
173
|
-
- **Customize prompt.md** for your project's specific needs (build commands, test frameworks, etc.)
|
|
174
|
-
- **Break down complex tasks** into smaller subtasks in todo.md
|
|
175
|
-
- **Let Claude prioritize** by not ordering tasks strictly - Claude will choose what makes sense
|
|
176
|
-
|
|
177
|
-
## License
|
|
178
|
-
|
|
179
|
-
MIT
|
|
180
|
-
|
|
181
|
-
## Author
|
|
182
|
-
|
|
183
|
-
Herb Caudill
|