@herbcaudill/ralph 0.4.3 → 0.6.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/README.md +3 -14
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +33 -1
- package/dist/cli.js.map +1 -1
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +2 -8
- package/dist/components/App.js.map +1 -1
- package/dist/components/EventDisplay.d.ts +8 -1
- package/dist/components/EventDisplay.d.ts.map +1 -1
- package/dist/components/EventDisplay.js +175 -57
- package/dist/components/EventDisplay.js.map +1 -1
- package/dist/components/EventDisplay.replay.test.js +53 -43
- package/dist/components/EventDisplay.replay.test.js.map +1 -1
- package/dist/components/EventDisplay.test.js +107 -11
- package/dist/components/EventDisplay.test.js.map +1 -1
- package/dist/components/FullScreenLayout.d.ts +14 -0
- package/dist/components/FullScreenLayout.d.ts.map +1 -0
- package/dist/components/FullScreenLayout.js +30 -0
- package/dist/components/FullScreenLayout.js.map +1 -0
- package/dist/components/Header.d.ts +2 -1
- package/dist/components/Header.d.ts.map +1 -1
- package/dist/components/Header.js +2 -2
- package/dist/components/Header.js.map +1 -1
- package/dist/components/InitRalph.js +1 -1
- package/dist/components/InitRalph.js.map +1 -1
- package/dist/components/IterationRunner.d.ts +3 -1
- package/dist/components/IterationRunner.d.ts.map +1 -1
- package/dist/components/IterationRunner.js +268 -186
- package/dist/components/IterationRunner.js.map +1 -1
- package/dist/components/IterationRunner.test.js +6 -6
- package/dist/components/IterationRunner.test.js.map +1 -1
- package/dist/components/ReplayLog.d.ts.map +1 -1
- package/dist/components/ReplayLog.js +7 -8
- package/dist/components/ReplayLog.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/addTodo.d.ts +10 -0
- package/dist/lib/addTodo.d.ts.map +1 -0
- package/dist/lib/addTodo.js +55 -0
- package/dist/lib/addTodo.js.map +1 -0
- package/dist/lib/formatContentBlock.d.ts +10 -0
- package/dist/lib/formatContentBlock.d.ts.map +1 -0
- package/dist/lib/formatContentBlock.js +60 -0
- package/dist/lib/formatContentBlock.js.map +1 -0
- package/dist/lib/useTerminalSize.d.ts +5 -0
- package/dist/lib/useTerminalSize.d.ts.map +1 -0
- package/dist/lib/useTerminalSize.js +21 -0
- package/dist/lib/useTerminalSize.js.map +1 -0
- package/package.json +8 -5
- package/templates/prompt.md +1 -12
- package/templates/progress.md +0 -11
package/README.md
CHANGED
|
@@ -9,9 +9,8 @@ Ralph spawns multiple Claude CLI sessions that:
|
|
|
9
9
|
1. Check project health (build, tests)
|
|
10
10
|
2. Select and work on the highest-priority task
|
|
11
11
|
3. Validate changes with tests
|
|
12
|
-
4.
|
|
13
|
-
5.
|
|
14
|
-
6. Repeat
|
|
12
|
+
4. Commit changes
|
|
13
|
+
5. Repeat
|
|
15
14
|
|
|
16
15
|
## Installation
|
|
17
16
|
|
|
@@ -37,8 +36,7 @@ This creates a `.ralph/` directory with template files:
|
|
|
37
36
|
|
|
38
37
|
- `prompt.md` - Instructions for Claude during each iteration
|
|
39
38
|
- `todo.md` - Your task list
|
|
40
|
-
- `
|
|
41
|
-
- `events.log` - Event log (auto-generated)
|
|
39
|
+
- `events.log` - Event log (auto-generated during runs)
|
|
42
40
|
|
|
43
41
|
2. **Customize the workflow:**
|
|
44
42
|
|
|
@@ -99,14 +97,6 @@ Your task list with priority and completion status. Tasks can be:
|
|
|
99
97
|
- Detailed descriptions with acceptance criteria
|
|
100
98
|
- Broken down into subtasks
|
|
101
99
|
|
|
102
|
-
**`.ralph/progress.md`**
|
|
103
|
-
|
|
104
|
-
Auto-updated log of completed work. Each entry includes:
|
|
105
|
-
|
|
106
|
-
- What was changed
|
|
107
|
-
- Why it was changed
|
|
108
|
-
- Commit information
|
|
109
|
-
|
|
110
100
|
**`.ralph/events.log`**
|
|
111
101
|
|
|
112
102
|
Machine-readable log of all Claude interactions (JSON). Use for debugging or replay.
|
|
@@ -161,7 +151,6 @@ claude auth
|
|
|
161
151
|
## Tips
|
|
162
152
|
|
|
163
153
|
- **Start with small iteration counts** (3-5) to verify the workflow before running longer sessions
|
|
164
|
-
- **Review progress.md** between runs to understand what changed
|
|
165
154
|
- **Customize prompt.md** for your project's specific needs (build commands, test frameworks, etc.)
|
|
166
155
|
- **Break down complex tasks** into smaller subtasks in todo.md
|
|
167
156
|
- **Let Claude prioritize** by not ordering tasks strictly - Claude will choose what makes sense
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAUnC,eAAO,MAAM,OAAO,SAkBhB,CAAA"}
|
package/dist/cli.js
CHANGED
|
@@ -5,12 +5,13 @@ import React from "react";
|
|
|
5
5
|
import { App } from "./components/App.js";
|
|
6
6
|
import { InitRalph } from "./components/InitRalph.js";
|
|
7
7
|
import { getClaudeVersion } from "./lib/getClaudeVersion.js";
|
|
8
|
+
import { addTodo } from "./lib/addTodo.js";
|
|
8
9
|
import packageJson from "../package.json" with { type: "json" };
|
|
9
10
|
export const program = new Command()
|
|
10
11
|
.name("ralph")
|
|
11
12
|
.description("Autonomous AI iteration engine for Claude CLI")
|
|
12
13
|
.version(packageJson.version)
|
|
13
|
-
.argument("[iterations]", "number of iterations to run", val => parseInt(val, 10),
|
|
14
|
+
.argument("[iterations]", "number of iterations to run", val => parseInt(val, 10), 30)
|
|
14
15
|
.option("--replay [file]", "replay events from log file")
|
|
15
16
|
.action((iterations, options) => {
|
|
16
17
|
const replayFile = options.replay !== undefined ?
|
|
@@ -28,4 +29,35 @@ program
|
|
|
28
29
|
.action(() => {
|
|
29
30
|
render(React.createElement(InitRalph));
|
|
30
31
|
});
|
|
32
|
+
program
|
|
33
|
+
.command("todo [description...]")
|
|
34
|
+
.description("add a todo item and commit it (safe to use while ralph is running)")
|
|
35
|
+
.action(async (descriptionParts) => {
|
|
36
|
+
let description = descriptionParts.join(" ").trim();
|
|
37
|
+
if (!description) {
|
|
38
|
+
// Prompt for description interactively
|
|
39
|
+
const readline = await import("readline");
|
|
40
|
+
const rl = readline.createInterface({
|
|
41
|
+
input: process.stdin,
|
|
42
|
+
output: process.stdout,
|
|
43
|
+
});
|
|
44
|
+
description = await new Promise(resolve => {
|
|
45
|
+
rl.question("Todo: ", answer => {
|
|
46
|
+
rl.close();
|
|
47
|
+
resolve(answer.trim());
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
if (!description) {
|
|
51
|
+
console.error("No todo description provided");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
addTodo(description);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error(`Failed to add todo: ${error instanceof Error ? error.message : error}`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
31
63
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC5D,OAAO,WAAW,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAE/D,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;KACjC,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,+CAA+C,CAAC;KAC5D,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC;KAC5B,QAAQ,CAAC,cAAc,EAAE,6BAA6B,EAAE,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;KACrF,MAAM,CAAC,iBAAiB,EAAE,6BAA6B,CAAC;KACxD,MAAM,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE;IAC9B,MAAM,UAAU,GACd,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAC5B,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;YAClC,OAAO,CAAC,MAAM;YAChB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,YAAY,CAAC;QAC/C,CAAC,CAAC,SAAS,CAAA;IAEb,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAA;IACxC,MAAM,YAAY,GAAG,WAAW,CAAC,OAAO,CAAA;IAExC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC,CAAC,CAAA;AAC3F,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,4CAA4C,CAAC;KACzD,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAA;AACxC,CAAC,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,eAAO,MAAM,GAAG,GAAI,yDAAyD,KAAK,sBAYjF,CAAA;AAED,KAAK,KAAK,GAAG;IACX,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA"}
|
package/dist/components/App.js
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Box } from "ink";
|
|
3
|
-
import { Header } from "./Header.js";
|
|
4
2
|
import { IterationRunner } from "./IterationRunner.js";
|
|
5
3
|
import { ReplayLog } from "./ReplayLog.js";
|
|
6
4
|
export const App = ({ iterations, replayFile, claudeVersion, ralphVersion }) => {
|
|
7
5
|
if (replayFile) {
|
|
8
|
-
return
|
|
9
|
-
React.createElement(Header, { claudeVersion: claudeVersion, ralphVersion: ralphVersion }),
|
|
10
|
-
React.createElement(ReplayLog, { filePath: replayFile })));
|
|
6
|
+
return React.createElement(ReplayLog, { filePath: replayFile });
|
|
11
7
|
}
|
|
12
|
-
return (React.createElement(
|
|
13
|
-
React.createElement(Header, { claudeVersion: claudeVersion, ralphVersion: ralphVersion }),
|
|
14
|
-
React.createElement(IterationRunner, { totalIterations: iterations })));
|
|
8
|
+
return (React.createElement(IterationRunner, { totalIterations: iterations, claudeVersion: claudeVersion, ralphVersion: ralphVersion }));
|
|
15
9
|
};
|
|
16
10
|
//# sourceMappingURL=App.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.js","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"App.js","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAS,EAAE,EAAE;IACpF,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,oBAAC,SAAS,IAAC,QAAQ,EAAE,UAAU,GAAI,CAAA;IAC5C,CAAC;IAED,OAAO,CACL,oBAAC,eAAe,IACd,eAAe,EAAE,UAAU,EAC3B,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,YAAY,GAC1B,CACH,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
|
|
2
|
+
type IterationEvents = {
|
|
3
|
+
iteration: number;
|
|
4
|
+
events: Array<Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
export declare const EventDisplay: ({ events, iteration, completedIterations, height }: Props) => React.JSX.Element;
|
|
3
7
|
type Props = {
|
|
4
8
|
events: Array<Record<string, unknown>>;
|
|
9
|
+
iteration: number;
|
|
10
|
+
completedIterations: IterationEvents[];
|
|
11
|
+
height?: number;
|
|
5
12
|
};
|
|
6
13
|
export {};
|
|
7
14
|
//# sourceMappingURL=EventDisplay.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EventDisplay.d.ts","sourceRoot":"","sources":["../../src/components/EventDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"EventDisplay.d.ts","sourceRoot":"","sources":["../../src/components/EventDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+C,MAAM,OAAO,CAAA;AAKnE,KAAK,eAAe,GAAG;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CACvC,CAAA;AAuGD,eAAO,MAAM,YAAY,GAAI,oDAAoD,KAAK,sBAgGrF,CAAA;AAED,KAAK,KAAK,GAAG;IACX,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,mBAAmB,EAAE,eAAe,EAAE,CAAA;IACtC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA"}
|
|
@@ -1,64 +1,182 @@
|
|
|
1
|
-
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Box } from "ink";
|
|
3
|
-
import { StreamingText } from "./StreamingText.js";
|
|
4
|
-
import { ToolUse } from "./ToolUse.js";
|
|
1
|
+
import React, { useMemo, useState, useEffect, useRef } from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
5
3
|
import { eventToBlocks } from "./eventToBlocks.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
messageMap.get(messageId).push(...content);
|
|
4
|
+
import { formatContentBlock, formatIterationHeader } from "../lib/formatContentBlock.js";
|
|
5
|
+
// Process raw events into content blocks
|
|
6
|
+
// With includePartialMessages: true, we receive multiple snapshots of the same message
|
|
7
|
+
// as it builds up. Each snapshot may contain different parts of the message content,
|
|
8
|
+
// so we need to merge them and deduplicate.
|
|
9
|
+
const processEvents = (events) => {
|
|
10
|
+
// Filter to only show complete assistant messages, not streaming events
|
|
11
|
+
// streaming events are incomplete and cause duplicate/disappearing content
|
|
12
|
+
const assistantEvents = events.filter(event => event.type === "assistant");
|
|
13
|
+
// Collect all content blocks from all snapshots of the same message
|
|
14
|
+
const messageMap = new Map();
|
|
15
|
+
for (const event of assistantEvents) {
|
|
16
|
+
const message = event.message;
|
|
17
|
+
const messageId = message?.id;
|
|
18
|
+
const content = message?.content;
|
|
19
|
+
if (messageId && content) {
|
|
20
|
+
if (!messageMap.has(messageId)) {
|
|
21
|
+
messageMap.set(messageId, []);
|
|
25
22
|
}
|
|
23
|
+
messageMap.get(messageId).push(...content);
|
|
26
24
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
25
|
+
}
|
|
26
|
+
// Create merged events with deduplicated content
|
|
27
|
+
const mergedEvents = Array.from(messageMap.entries()).map(([messageId, allContent]) => {
|
|
28
|
+
// Deduplicate content blocks by their ID (for tool_use) or text (for text blocks)
|
|
29
|
+
const seenBlocks = new Set();
|
|
30
|
+
const uniqueContent = [];
|
|
31
|
+
for (const block of allContent) {
|
|
32
|
+
const blockType = block.type;
|
|
33
|
+
let blockKey;
|
|
34
|
+
if (blockType === "tool_use") {
|
|
35
|
+
// Tool use blocks are unique by their ID
|
|
36
|
+
blockKey = `tool:${block.id}`;
|
|
37
|
+
}
|
|
38
|
+
else if (blockType === "text") {
|
|
39
|
+
// For text blocks, check if this is a prefix of or prefixed by existing text
|
|
40
|
+
// This handles incremental text updates where each snapshot has more content
|
|
41
|
+
const text = block.text;
|
|
42
|
+
let isDuplicate = false;
|
|
43
|
+
for (const seenKey of seenBlocks) {
|
|
44
|
+
if (seenKey.startsWith("text:")) {
|
|
45
|
+
const seenText = seenKey.substring(5);
|
|
46
|
+
// If existing text starts with this text, or this text starts with existing,
|
|
47
|
+
// keep only the longer one
|
|
48
|
+
if (seenText.startsWith(text)) {
|
|
49
|
+
// Existing is longer, this is a duplicate
|
|
50
|
+
isDuplicate = true;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
else if (text.startsWith(seenText)) {
|
|
54
|
+
// This is longer, remove the old one and add this
|
|
55
|
+
seenBlocks.delete(seenKey);
|
|
56
|
+
// Also remove from uniqueContent
|
|
57
|
+
const idx = uniqueContent.findIndex(b => b.type === "text" && b.text === seenText);
|
|
58
|
+
if (idx >= 0)
|
|
59
|
+
uniqueContent.splice(idx, 1);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
47
63
|
}
|
|
64
|
+
if (isDuplicate)
|
|
65
|
+
continue;
|
|
66
|
+
blockKey = `text:${text}`;
|
|
48
67
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
else {
|
|
69
|
+
blockKey = JSON.stringify(block);
|
|
70
|
+
}
|
|
71
|
+
if (!seenBlocks.has(blockKey)) {
|
|
72
|
+
seenBlocks.add(blockKey);
|
|
73
|
+
uniqueContent.push(block);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
type: "assistant",
|
|
78
|
+
message: {
|
|
79
|
+
id: messageId,
|
|
80
|
+
content: uniqueContent,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
return mergedEvents.flatMap(event => eventToBlocks(event));
|
|
85
|
+
};
|
|
86
|
+
// Convert content blocks to lines of formatted text
|
|
87
|
+
const blocksToLines = (blocks) => {
|
|
88
|
+
const lines = [];
|
|
89
|
+
for (const block of blocks) {
|
|
90
|
+
const blockLines = formatContentBlock(block);
|
|
91
|
+
lines.push(...blockLines);
|
|
92
|
+
// Add blank line after each block
|
|
93
|
+
lines.push("");
|
|
94
|
+
}
|
|
95
|
+
return lines;
|
|
96
|
+
};
|
|
97
|
+
export const EventDisplay = ({ events, iteration, completedIterations, height }) => {
|
|
98
|
+
// Scroll offset from bottom (0 = at bottom, positive = scrolled up)
|
|
99
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
100
|
+
// Track if user has manually scrolled
|
|
101
|
+
const userScrolledRef = useRef(false);
|
|
102
|
+
// Track previous line count for auto-scroll
|
|
103
|
+
const prevLineCountRef = useRef(0);
|
|
104
|
+
// Convert all content to lines for virtual scrolling
|
|
105
|
+
const allLines = useMemo(() => {
|
|
106
|
+
const lines = [];
|
|
107
|
+
// Add completed iterations
|
|
108
|
+
for (const completed of completedIterations) {
|
|
109
|
+
lines.push("");
|
|
110
|
+
lines.push("");
|
|
111
|
+
lines.push(formatIterationHeader(completed.iteration));
|
|
112
|
+
lines.push("");
|
|
113
|
+
const blocks = processEvents(completed.events);
|
|
114
|
+
lines.push(...blocksToLines(blocks));
|
|
115
|
+
}
|
|
116
|
+
// Add current iteration
|
|
117
|
+
lines.push("");
|
|
118
|
+
lines.push("");
|
|
119
|
+
lines.push(formatIterationHeader(iteration));
|
|
120
|
+
lines.push("");
|
|
121
|
+
const currentBlocks = processEvents(events);
|
|
122
|
+
lines.push(...blocksToLines(currentBlocks));
|
|
123
|
+
return lines;
|
|
124
|
+
}, [events, iteration, completedIterations]);
|
|
125
|
+
// Auto-scroll to bottom when new content arrives (unless user scrolled up)
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if (allLines.length > prevLineCountRef.current && !userScrolledRef.current) {
|
|
128
|
+
setScrollOffset(0);
|
|
129
|
+
}
|
|
130
|
+
prevLineCountRef.current = allLines.length;
|
|
131
|
+
}, [allLines.length]);
|
|
132
|
+
// Handle keyboard input for scrolling
|
|
133
|
+
useInput((input, key) => {
|
|
134
|
+
if (!height)
|
|
135
|
+
return;
|
|
136
|
+
const maxOffset = Math.max(0, allLines.length - height);
|
|
137
|
+
const pageSize = Math.max(1, height - 2);
|
|
138
|
+
if (key.upArrow || input === "k") {
|
|
139
|
+
userScrolledRef.current = true;
|
|
140
|
+
setScrollOffset(prev => Math.min(maxOffset, prev + 1));
|
|
141
|
+
}
|
|
142
|
+
else if (key.downArrow || input === "j") {
|
|
143
|
+
const newOffset = Math.max(0, scrollOffset - 1);
|
|
144
|
+
setScrollOffset(newOffset);
|
|
145
|
+
if (newOffset === 0) {
|
|
146
|
+
userScrolledRef.current = false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else if (key.pageUp) {
|
|
150
|
+
userScrolledRef.current = true;
|
|
151
|
+
setScrollOffset(prev => Math.min(maxOffset, prev + pageSize));
|
|
152
|
+
}
|
|
153
|
+
else if (key.pageDown) {
|
|
154
|
+
const newOffset = Math.max(0, scrollOffset - pageSize);
|
|
155
|
+
setScrollOffset(newOffset);
|
|
156
|
+
if (newOffset === 0) {
|
|
157
|
+
userScrolledRef.current = false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (input === "g" && key.shift) {
|
|
161
|
+
// Shift+G = go to bottom
|
|
162
|
+
setScrollOffset(0);
|
|
163
|
+
userScrolledRef.current = false;
|
|
164
|
+
}
|
|
165
|
+
else if (input === "g") {
|
|
166
|
+
// g = go to top
|
|
167
|
+
userScrolledRef.current = true;
|
|
168
|
+
setScrollOffset(maxOffset);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
// Calculate visible lines based on scroll position
|
|
172
|
+
const visibleLines = useMemo(() => {
|
|
173
|
+
if (!height || allLines.length <= height) {
|
|
174
|
+
return allLines;
|
|
175
|
+
}
|
|
176
|
+
const endIndex = allLines.length - scrollOffset;
|
|
177
|
+
const startIndex = Math.max(0, endIndex - height);
|
|
178
|
+
return allLines.slice(startIndex, endIndex);
|
|
179
|
+
}, [allLines, height, scrollOffset]);
|
|
180
|
+
return (React.createElement(Box, { flexDirection: "column" }, visibleLines.map((line, index) => (React.createElement(Text, { key: index, wrap: "wrap" }, line || " ")))));
|
|
63
181
|
};
|
|
64
182
|
//# sourceMappingURL=EventDisplay.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EventDisplay.js","sourceRoot":"","sources":["../../src/components/EventDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"EventDisplay.js","sourceRoot":"","sources":["../../src/components/EventDisplay.tsx"],"names":[],"mappings":"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"}
|
|
@@ -26,7 +26,7 @@ async function replay(logFile) {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
// Render with all events
|
|
29
|
-
const { lastFrame } = render(React.createElement(EventDisplay, { events: events }));
|
|
29
|
+
const { lastFrame } = render(React.createElement(EventDisplay, { events: events, iteration: 1, completedIterations: [] }));
|
|
30
30
|
// Wait for rendering
|
|
31
31
|
await vi.waitFor(() => {
|
|
32
32
|
const output = lastFrame() ?? "";
|
|
@@ -37,37 +37,46 @@ async function replay(logFile) {
|
|
|
37
37
|
describe("EventDisplay replay tests", () => {
|
|
38
38
|
it("replays 1.txt", async () => {
|
|
39
39
|
expect(await replay("1.txt")).toMatchInlineSnapshot(`
|
|
40
|
-
"
|
|
40
|
+
"
|
|
41
|
+
|
|
42
|
+
─── Iteration 1 ───
|
|
43
|
+
|
|
44
|
+
I'll start by checking the types, unit tests, and end-to-end tests as instructed.
|
|
41
45
|
|
|
42
46
|
TodoWrite
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
[~] Run typecheck to verify types
|
|
48
|
+
[ ] Run unit tests via pnpm test
|
|
49
|
+
[ ] Run end-to-end tests via pnpm test:pw
|
|
46
50
|
|
|
47
51
|
$ pnpm typecheck
|
|
48
52
|
|
|
49
53
|
Good! Types check successfully. Now let me run the unit tests.
|
|
50
54
|
|
|
51
55
|
TodoWrite
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
[x] Run typecheck to verify types
|
|
57
|
+
[~] Run unit tests via pnpm test
|
|
58
|
+
[ ] Run end-to-end tests via pnpm test:pw
|
|
55
59
|
|
|
56
60
|
$ pnpm test
|
|
57
61
|
|
|
58
62
|
Perfect! Unit tests pass. Now let me run the end-to-end tests.
|
|
59
63
|
|
|
60
64
|
TodoWrite
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
[x] Run typecheck to verify types
|
|
66
|
+
[x] Run unit tests via pnpm test
|
|
67
|
+
[~] Run end-to-end tests via pnpm test:pw
|
|
64
68
|
|
|
65
|
-
$ pnpm test:pw
|
|
69
|
+
$ pnpm test:pw
|
|
70
|
+
"
|
|
66
71
|
`);
|
|
67
72
|
});
|
|
68
73
|
it("replays 2.txt", async () => {
|
|
69
74
|
expect(await replay("2.txt")).toMatchInlineSnapshot(`
|
|
70
|
-
"
|
|
75
|
+
"
|
|
76
|
+
|
|
77
|
+
─── Iteration 1 ───
|
|
78
|
+
|
|
79
|
+
I'll start by checking that the project builds successfully and running tests.
|
|
71
80
|
|
|
72
81
|
$ pnpm build
|
|
73
82
|
|
|
@@ -194,24 +203,24 @@ describe("EventDisplay replay tests", () => {
|
|
|
194
203
|
Let me write a simpler test to see what's going on:
|
|
195
204
|
|
|
196
205
|
$ cat > test-merge.ts << 'EOF'
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
206
|
+
import { eventToBlocks } from "./src/components/eventToBlocks.js"
|
|
207
|
+
|
|
208
|
+
const event = {
|
|
209
|
+
type: "assistant",
|
|
210
|
+
message: {
|
|
211
|
+
id: "msg_123",
|
|
212
|
+
content: [
|
|
213
|
+
{ type: "text", text: "Use the " },
|
|
214
|
+
{ type: "text", text: "\`console.log()\`" },
|
|
215
|
+
{ type: "text", text: " function to debug." },
|
|
216
|
+
],
|
|
217
|
+
},
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const result = eventToBlocks(event)
|
|
221
|
+
console.log("Result:", JSON.stringify(result, null, 2))
|
|
222
|
+
EOF
|
|
223
|
+
tsx test-merge.ts
|
|
215
224
|
|
|
216
225
|
Let me check if my fix is actually working by looking at the eventToBlocks implementation more
|
|
217
226
|
carefully. Actually, let me just trace through what should happen:
|
|
@@ -292,7 +301,7 @@ describe("EventDisplay replay tests", () => {
|
|
|
292
301
|
Edit src/components/eventToBlocks.ts
|
|
293
302
|
|
|
294
303
|
$ pnpm test EventDisplay.test.tsx 2>&1 | grep -A 50 "merges consecutive text blocks to prevent
|
|
295
|
-
|
|
304
|
+
unwanted gaps"
|
|
296
305
|
|
|
297
306
|
EXCELLENT! The merging IS working correctly:
|
|
298
307
|
- eventToBlocks returns: [{ "type": "text", "content": "Use the console.log() function to debug.",
|
|
@@ -380,23 +389,24 @@ describe("EventDisplay replay tests", () => {
|
|
|
380
389
|
$ git add -A && git diff --cached --stat
|
|
381
390
|
|
|
382
391
|
$ git commit -m "$(cat <<'EOF'
|
|
383
|
-
|
|
392
|
+
Fix inline code blocks appearing in separate paragraphs
|
|
384
393
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
394
|
+
- Modified eventToBlocks.ts to merge consecutive text blocks using a textBuffer
|
|
395
|
+
- Wrapped StreamingText content in Box component for proper inline rendering
|
|
396
|
+
- Added test case to verify consecutive text blocks are merged
|
|
397
|
+
- Updated existing tests to reflect new merging behavior
|
|
389
398
|
|
|
390
|
-
|
|
391
|
-
|
|
399
|
+
Fixes issue where inline code like \`console.log()\` was rendered on separate
|
|
400
|
+
lines with gaps instead of flowing inline with surrounding text.
|
|
392
401
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
402
|
+
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
403
|
+
EOF
|
|
404
|
+
)"
|
|
396
405
|
|
|
397
406
|
Perfect! Let me verify git status:
|
|
398
407
|
|
|
399
|
-
$ git status
|
|
408
|
+
$ git status
|
|
409
|
+
"
|
|
400
410
|
`);
|
|
401
411
|
});
|
|
402
412
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EventDisplay.replay.test.js","sourceRoot":"","sources":["../../src/components/EventDisplay.replay.test.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD;;GAEG;AACH,KAAK,UAAU,MAAM,CAAC,OAAe;IACnC,qBAAqB;IACrB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,iBAAiB,EAAE,OAAO,CAAC,CAAA;IAC/D,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAEjD,uEAAuE;IACvE,MAAM,MAAM,GAAmC,EAAE,CAAA;IACjD,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAC5C,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;QAClC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;YAAE,SAAQ;QAC7B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YACjC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,oBAAoB;QACtB,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"EventDisplay.replay.test.js","sourceRoot":"","sources":["../../src/components/EventDisplay.replay.test.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD;;GAEG;AACH,KAAK,UAAU,MAAM,CAAC,OAAe;IACnC,qBAAqB;IACrB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,iBAAiB,EAAE,OAAO,CAAC,CAAA;IAC/D,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAEjD,uEAAuE;IACvE,MAAM,MAAM,GAAmC,EAAE,CAAA;IACjD,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAC5C,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;QAClC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;YAAE,SAAQ;QAC7B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YACjC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,oBAAoB;QACtB,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAC1B,oBAAC,YAAY,IAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,mBAAmB,EAAE,EAAE,GAAI,CACxE,CAAA;IAED,qBAAqB;IACrB,MAAM,EAAE,CAAC,OAAO,CACd,GAAG,EAAE;QACH,MAAM,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,CAAA;QAChC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;IAC1C,CAAC,EACD,EAAE,OAAO,EAAE,IAAI,EAAE,CAClB,CAAA;IAED,OAAO,SAAS,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC;AAED,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;QAC7B,MAAM,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAgCnD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;QAC7B,MAAM,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAgVnD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|