@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.
Files changed (53) hide show
  1. package/README.md +3 -14
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +33 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/components/App.d.ts.map +1 -1
  6. package/dist/components/App.js +2 -8
  7. package/dist/components/App.js.map +1 -1
  8. package/dist/components/EventDisplay.d.ts +8 -1
  9. package/dist/components/EventDisplay.d.ts.map +1 -1
  10. package/dist/components/EventDisplay.js +175 -57
  11. package/dist/components/EventDisplay.js.map +1 -1
  12. package/dist/components/EventDisplay.replay.test.js +53 -43
  13. package/dist/components/EventDisplay.replay.test.js.map +1 -1
  14. package/dist/components/EventDisplay.test.js +107 -11
  15. package/dist/components/EventDisplay.test.js.map +1 -1
  16. package/dist/components/FullScreenLayout.d.ts +14 -0
  17. package/dist/components/FullScreenLayout.d.ts.map +1 -0
  18. package/dist/components/FullScreenLayout.js +30 -0
  19. package/dist/components/FullScreenLayout.js.map +1 -0
  20. package/dist/components/Header.d.ts +2 -1
  21. package/dist/components/Header.d.ts.map +1 -1
  22. package/dist/components/Header.js +2 -2
  23. package/dist/components/Header.js.map +1 -1
  24. package/dist/components/InitRalph.js +1 -1
  25. package/dist/components/InitRalph.js.map +1 -1
  26. package/dist/components/IterationRunner.d.ts +3 -1
  27. package/dist/components/IterationRunner.d.ts.map +1 -1
  28. package/dist/components/IterationRunner.js +268 -186
  29. package/dist/components/IterationRunner.js.map +1 -1
  30. package/dist/components/IterationRunner.test.js +6 -6
  31. package/dist/components/IterationRunner.test.js.map +1 -1
  32. package/dist/components/ReplayLog.d.ts.map +1 -1
  33. package/dist/components/ReplayLog.js +7 -8
  34. package/dist/components/ReplayLog.js.map +1 -1
  35. package/dist/index.d.ts +0 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +0 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/lib/addTodo.d.ts +10 -0
  40. package/dist/lib/addTodo.d.ts.map +1 -0
  41. package/dist/lib/addTodo.js +55 -0
  42. package/dist/lib/addTodo.js.map +1 -0
  43. package/dist/lib/formatContentBlock.d.ts +10 -0
  44. package/dist/lib/formatContentBlock.d.ts.map +1 -0
  45. package/dist/lib/formatContentBlock.js +60 -0
  46. package/dist/lib/formatContentBlock.js.map +1 -0
  47. package/dist/lib/useTerminalSize.d.ts +5 -0
  48. package/dist/lib/useTerminalSize.d.ts.map +1 -0
  49. package/dist/lib/useTerminalSize.js +21 -0
  50. package/dist/lib/useTerminalSize.js.map +1 -0
  51. package/package.json +8 -5
  52. package/templates/prompt.md +1 -12
  53. 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. Document progress
13
- 5. Commit changes
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
- - `progress.md` - Progress log (auto-updated)
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;AASnC,eAAO,MAAM,OAAO,SAkBhB,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), 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;AAMzB,eAAO,MAAM,GAAG,GAAI,yDAAyD,KAAK,sBAgBjF,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"}
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"}
@@ -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 (React.createElement(Box, { flexDirection: "column", marginX: 1 },
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(Box, { flexDirection: "column", marginX: 1 },
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,GAAG,EAAE,MAAM,KAAK,CAAA;AACzB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,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,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,OAAO,EAAE,CAAC;YACpC,oBAAC,MAAM,IAAC,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,GAAI;YACpE,oBAAC,SAAS,IAAC,QAAQ,EAAE,UAAU,GAAI,CAC/B,CACP,CAAA;IACH,CAAC;IAED,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,OAAO,EAAE,CAAC;QACpC,oBAAC,MAAM,IAAC,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,GAAI;QACpE,oBAAC,eAAe,IAAC,eAAe,EAAE,UAAU,GAAI,CAC5C,CACP,CAAA;AACH,CAAC,CAAA"}
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
- export declare const EventDisplay: ({ events }: Props) => React.JSX.Element;
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,KAA8B,MAAM,OAAO,CAAA;AAMlD,eAAO,MAAM,YAAY,GAAI,YAAY,KAAK,sBAuE7C,CAAA;AAED,KAAK,KAAK,GAAG;IACX,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CACvC,CAAA"}
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
- export const EventDisplay = ({ events }) => {
7
- const [contentBlocks, setContentBlocks] = useState([]);
8
- useEffect(() => {
9
- // Filter to only show complete assistant messages, not streaming events
10
- // streaming events are incomplete and cause duplicate/disappearing content
11
- const assistantEvents = events.filter(event => event.type === "assistant");
12
- // Group by message ID and merge content from all versions
13
- // The Claude CLI sends multiple snapshots of the same message as it builds up
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, []);
22
- }
23
- // Collect all content blocks from all versions
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
- // Create merged events with all content
28
- const mergedEvents = Array.from(messageMap.entries()).map(([messageId, allContent]) => {
29
- // Deduplicate content blocks by their ID (for tool_use) or text (for text blocks)
30
- const seenBlocks = new Set();
31
- const uniqueContent = [];
32
- for (const block of allContent) {
33
- const blockType = block.type;
34
- let blockKey;
35
- if (blockType === "tool_use") {
36
- blockKey = block.id;
37
- }
38
- else if (blockType === "text") {
39
- blockKey = `text:${block.text}`;
40
- }
41
- else {
42
- blockKey = JSON.stringify(block);
43
- }
44
- if (!seenBlocks.has(blockKey)) {
45
- seenBlocks.add(blockKey);
46
- uniqueContent.push(block);
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
- return {
50
- type: "assistant",
51
- message: {
52
- id: messageId,
53
- content: uniqueContent,
54
- },
55
- };
56
- });
57
- const blocks = mergedEvents.flatMap(event => eventToBlocks(event));
58
- setContentBlocks(blocks);
59
- }, [events]);
60
- return (React.createElement(Box, { flexDirection: "column", gap: 1 }, contentBlocks.map(block => block.type === "text" ?
61
- React.createElement(StreamingText, { key: block.id, content: block.content })
62
- : React.createElement(ToolUse, { key: block.id, name: block.name, arg: block.arg }))));
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":"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"}
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
- "I'll start by checking the types, unit tests, and end-to-end tests as instructed.
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
- [~] Run typecheck to verify types
44
- [ ] Run unit tests via pnpm test
45
- [ ] Run end-to-end tests via pnpm test:pw
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
- [x] Run typecheck to verify types
53
- [~] Run unit tests via pnpm test
54
- [ ] Run end-to-end tests via pnpm test:pw
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
- [x] Run typecheck to verify types
62
- [x] Run unit tests via pnpm test
63
- [~] Run end-to-end tests via pnpm test:pw
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
- "I'll start by checking that the project builds successfully and running tests.
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
- import { eventToBlocks } from "./src/components/eventToBlocks.js"
198
-
199
- const event = {
200
- type: "assistant",
201
- message: {
202
- id: "msg_123",
203
- content: [
204
- { type: "text", text: "Use the " },
205
- { type: "text", text: "\`console.log()\`" },
206
- { type: "text", text: " function to debug." },
207
- ],
208
- },
209
- }
210
-
211
- const result = eventToBlocks(event)
212
- console.log("Result:", JSON.stringify(result, null, 2))
213
- EOF
214
- tsx test-merge.ts
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
- unwanted gaps"
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
- Fix inline code blocks appearing in separate paragraphs
392
+ Fix inline code blocks appearing in separate paragraphs
384
393
 
385
- - Modified eventToBlocks.ts to merge consecutive text blocks using a textBuffer
386
- - Wrapped StreamingText content in Box component for proper inline rendering
387
- - Added test case to verify consecutive text blocks are merged
388
- - Updated existing tests to reflect new merging behavior
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
- Fixes issue where inline code like \`console.log()\` was rendered on separate
391
- lines with gaps instead of flowing inline with surrounding text.
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
- Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
394
- EOF
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,CAAC,oBAAC,YAAY,IAAC,MAAM,EAAE,MAAM,GAAI,CAAC,CAAA;IAE9D,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;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2BnD,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2UnD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
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"}