@herbcaudill/ralph 0.6.5 → 0.7.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 (175) hide show
  1. package/README.md +13 -100
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +21 -7
  4. package/dist/cli.js.map +1 -1
  5. package/dist/components/App.d.ts +2 -1
  6. package/dist/components/App.d.ts.map +1 -1
  7. package/dist/components/App.js +5 -1
  8. package/dist/components/App.js.map +1 -1
  9. package/dist/components/InitRalph.js +5 -5
  10. package/dist/components/InitRalph.js.map +1 -1
  11. package/dist/components/IterationRunner.d.ts +1 -1
  12. package/dist/components/IterationRunner.d.ts.map +1 -1
  13. package/dist/components/IterationRunner.js +212 -56
  14. package/dist/components/IterationRunner.js.map +1 -1
  15. package/dist/components/IterationRunner.test.js +2 -2
  16. package/dist/components/IterationRunner.test.js.map +1 -1
  17. package/dist/components/JsonOutput.d.ts +14 -0
  18. package/dist/components/JsonOutput.d.ts.map +1 -0
  19. package/dist/components/JsonOutput.js +246 -0
  20. package/dist/components/JsonOutput.js.map +1 -0
  21. package/dist/components/ProgressBar.d.ts +7 -5
  22. package/dist/components/ProgressBar.d.ts.map +1 -1
  23. package/dist/components/ProgressBar.js +7 -5
  24. package/dist/components/ProgressBar.js.map +1 -1
  25. package/dist/components/ProgressBar.test.js +24 -11
  26. package/dist/components/ProgressBar.test.js.map +1 -1
  27. package/dist/lib/MessageQueue.d.ts +2 -0
  28. package/dist/lib/MessageQueue.d.ts.map +1 -1
  29. package/dist/lib/MessageQueue.js +35 -2
  30. package/dist/lib/MessageQueue.js.map +1 -1
  31. package/dist/lib/MessageQueue.test.js +146 -0
  32. package/dist/lib/MessageQueue.test.js.map +1 -1
  33. package/dist/lib/StdinCommandHandler.d.ts +42 -0
  34. package/dist/lib/StdinCommandHandler.d.ts.map +1 -0
  35. package/dist/lib/StdinCommandHandler.js +102 -0
  36. package/dist/lib/StdinCommandHandler.js.map +1 -0
  37. package/dist/lib/StdinCommandHandler.test.d.ts +2 -0
  38. package/dist/lib/StdinCommandHandler.test.d.ts.map +1 -0
  39. package/dist/lib/StdinCommandHandler.test.js +93 -0
  40. package/dist/lib/StdinCommandHandler.test.js.map +1 -0
  41. package/dist/lib/debug.d.ts +18 -0
  42. package/dist/lib/debug.d.ts.map +1 -0
  43. package/dist/lib/debug.js +44 -0
  44. package/dist/lib/debug.js.map +1 -0
  45. package/dist/lib/formatContentBlock.js +1 -1
  46. package/dist/lib/formatContentBlock.js.map +1 -1
  47. package/dist/lib/getNextLogFile.d.ts +12 -0
  48. package/dist/lib/getNextLogFile.d.ts.map +1 -0
  49. package/dist/lib/getNextLogFile.js +52 -0
  50. package/dist/lib/getNextLogFile.js.map +1 -0
  51. package/dist/lib/getNextLogFile.test.d.ts +2 -0
  52. package/dist/lib/getNextLogFile.test.d.ts.map +1 -0
  53. package/dist/lib/getNextLogFile.test.js +65 -0
  54. package/dist/lib/getNextLogFile.test.js.map +1 -0
  55. package/dist/lib/getOpenIssueCount.d.ts +11 -0
  56. package/dist/lib/getOpenIssueCount.d.ts.map +1 -0
  57. package/dist/lib/getOpenIssueCount.js +31 -0
  58. package/dist/lib/getOpenIssueCount.js.map +1 -0
  59. package/dist/lib/getOpenIssueCount.test.d.ts +2 -0
  60. package/dist/lib/getOpenIssueCount.test.d.ts.map +1 -0
  61. package/dist/lib/getOpenIssueCount.test.js +65 -0
  62. package/dist/lib/getOpenIssueCount.test.js.map +1 -0
  63. package/dist/lib/getProgress.d.ts +22 -6
  64. package/dist/lib/getProgress.d.ts.map +1 -1
  65. package/dist/lib/getProgress.js +69 -23
  66. package/dist/lib/getProgress.js.map +1 -1
  67. package/dist/lib/getProgress.test.js +68 -34
  68. package/dist/lib/getProgress.test.js.map +1 -1
  69. package/package.json +1 -1
  70. package/templates/prompt-beads.md +5 -5
  71. package/dist/lib/cleanupAllWorktrees.d.ts +0 -5
  72. package/dist/lib/cleanupAllWorktrees.d.ts.map +0 -1
  73. package/dist/lib/cleanupAllWorktrees.js +0 -23
  74. package/dist/lib/cleanupAllWorktrees.js.map +0 -1
  75. package/dist/lib/cleanupWorktree.d.ts +0 -6
  76. package/dist/lib/cleanupWorktree.d.ts.map +0 -1
  77. package/dist/lib/cleanupWorktree.js +0 -33
  78. package/dist/lib/cleanupWorktree.js.map +0 -1
  79. package/dist/lib/copyRalphFilesFromWorktree.d.ts +0 -5
  80. package/dist/lib/copyRalphFilesFromWorktree.d.ts.map +0 -1
  81. package/dist/lib/copyRalphFilesFromWorktree.js +0 -19
  82. package/dist/lib/copyRalphFilesFromWorktree.js.map +0 -1
  83. package/dist/lib/copyRalphFilesToWorktree.d.ts +0 -5
  84. package/dist/lib/copyRalphFilesToWorktree.d.ts.map +0 -1
  85. package/dist/lib/copyRalphFilesToWorktree.js +0 -21
  86. package/dist/lib/copyRalphFilesToWorktree.js.map +0 -1
  87. package/dist/lib/createWorktree.d.ts +0 -6
  88. package/dist/lib/createWorktree.d.ts.map +0 -1
  89. package/dist/lib/createWorktree.js +0 -33
  90. package/dist/lib/createWorktree.js.map +0 -1
  91. package/dist/lib/getGitRoot.d.ts +0 -5
  92. package/dist/lib/getGitRoot.d.ts.map +0 -1
  93. package/dist/lib/getGitRoot.js +0 -18
  94. package/dist/lib/getGitRoot.js.map +0 -1
  95. package/dist/lib/installDependencies.d.ts +0 -5
  96. package/dist/lib/installDependencies.d.ts.map +0 -1
  97. package/dist/lib/installDependencies.js +0 -23
  98. package/dist/lib/installDependencies.js.map +0 -1
  99. package/dist/lib/installDependencies.test.d.ts +0 -2
  100. package/dist/lib/installDependencies.test.d.ts.map +0 -1
  101. package/dist/lib/installDependencies.test.js +0 -37
  102. package/dist/lib/installDependencies.test.js.map +0 -1
  103. package/dist/lib/mergeWorktreeToMain.d.ts +0 -6
  104. package/dist/lib/mergeWorktreeToMain.d.ts.map +0 -1
  105. package/dist/lib/mergeWorktreeToMain.js +0 -54
  106. package/dist/lib/mergeWorktreeToMain.js.map +0 -1
  107. package/dist/lib/outputState.d.ts +0 -5
  108. package/dist/lib/outputState.d.ts.map +0 -1
  109. package/dist/lib/outputState.js +0 -6
  110. package/dist/lib/outputState.js.map +0 -1
  111. package/dist/lib/popStash.d.ts +0 -5
  112. package/dist/lib/popStash.d.ts.map +0 -1
  113. package/dist/lib/popStash.js +0 -17
  114. package/dist/lib/popStash.js.map +0 -1
  115. package/dist/lib/processEvent.d.ts +0 -2
  116. package/dist/lib/processEvent.d.ts.map +0 -1
  117. package/dist/lib/processEvent.js +0 -100
  118. package/dist/lib/processEvent.js.map +0 -1
  119. package/dist/lib/replayLog.d.ts +0 -2
  120. package/dist/lib/replayLog.d.ts.map +0 -1
  121. package/dist/lib/replayLog.js +0 -35
  122. package/dist/lib/replayLog.js.map +0 -1
  123. package/dist/lib/resolveConflicts.d.ts +0 -6
  124. package/dist/lib/resolveConflicts.d.ts.map +0 -1
  125. package/dist/lib/resolveConflicts.js +0 -76
  126. package/dist/lib/resolveConflicts.js.map +0 -1
  127. package/dist/lib/runIteration.d.ts +0 -2
  128. package/dist/lib/runIteration.d.ts.map +0 -1
  129. package/dist/lib/runIteration.js +0 -91
  130. package/dist/lib/runIteration.js.map +0 -1
  131. package/dist/lib/showToolUse.d.ts +0 -2
  132. package/dist/lib/showToolUse.d.ts.map +0 -1
  133. package/dist/lib/showToolUse.js +0 -17
  134. package/dist/lib/showToolUse.js.map +0 -1
  135. package/dist/lib/signalHandler.d.ts +0 -3
  136. package/dist/lib/signalHandler.d.ts.map +0 -1
  137. package/dist/lib/signalHandler.js +0 -23
  138. package/dist/lib/signalHandler.js.map +0 -1
  139. package/dist/lib/stashChanges.d.ts +0 -6
  140. package/dist/lib/stashChanges.d.ts.map +0 -1
  141. package/dist/lib/stashChanges.js +0 -27
  142. package/dist/lib/stashChanges.js.map +0 -1
  143. package/dist/lib/textFormatting.d.ts +0 -4
  144. package/dist/lib/textFormatting.d.ts.map +0 -1
  145. package/dist/lib/textFormatting.js +0 -94
  146. package/dist/lib/textFormatting.js.map +0 -1
  147. package/dist/lib/types.d.ts +0 -13
  148. package/dist/lib/types.d.ts.map +0 -1
  149. package/dist/lib/types.js +0 -15
  150. package/dist/lib/types.js.map +0 -1
  151. package/dist/lib/worktree.d.ts +0 -43
  152. package/dist/lib/worktree.d.ts.map +0 -1
  153. package/dist/lib/worktree.js +0 -210
  154. package/dist/lib/worktree.js.map +0 -1
  155. package/dist/ui/EventProcessor.d.ts +0 -11
  156. package/dist/ui/EventProcessor.d.ts.map +0 -1
  157. package/dist/ui/EventProcessor.js +0 -95
  158. package/dist/ui/EventProcessor.js.map +0 -1
  159. package/dist/ui/IterationApp.d.ts +0 -10
  160. package/dist/ui/IterationApp.d.ts.map +0 -1
  161. package/dist/ui/IterationApp.js +0 -21
  162. package/dist/ui/IterationApp.js.map +0 -1
  163. package/dist/ui/IterationUI.d.ts +0 -13
  164. package/dist/ui/IterationUI.d.ts.map +0 -1
  165. package/dist/ui/IterationUI.js +0 -19
  166. package/dist/ui/IterationUI.js.map +0 -1
  167. package/dist/ui/TextDisplay.d.ts +0 -7
  168. package/dist/ui/TextDisplay.d.ts.map +0 -1
  169. package/dist/ui/TextDisplay.js +0 -41
  170. package/dist/ui/TextDisplay.js.map +0 -1
  171. package/dist/ui/ToolUseDisplay.d.ts +0 -10
  172. package/dist/ui/ToolUseDisplay.d.ts.map +0 -1
  173. package/dist/ui/ToolUseDisplay.js +0 -10
  174. package/dist/ui/ToolUseDisplay.js.map +0 -1
  175. /package/templates/{prompt.md → prompt-todos.md} +0 -0
@@ -0,0 +1,65 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { getNextLogFile, getLatestLogFile } from "./getNextLogFile.js";
3
+ import { rmSync, existsSync } from "fs";
4
+ import { join } from "path";
5
+ describe("getNextLogFile", () => {
6
+ const testDir = join(process.cwd(), ".ralph-test");
7
+ beforeEach(() => {
8
+ // Clean up any existing test directory
9
+ if (existsSync(testDir)) {
10
+ rmSync(testDir, { recursive: true });
11
+ }
12
+ });
13
+ afterEach(() => {
14
+ // Clean up test directory
15
+ if (existsSync(testDir)) {
16
+ rmSync(testDir, { recursive: true });
17
+ }
18
+ });
19
+ describe("getNextLogFile", () => {
20
+ it("returns events-1.jsonl when no logs exist", () => {
21
+ // Mock the current working directory by creating a test context
22
+ // Note: This test uses the actual cwd, so we need to ensure .ralph doesn't exist
23
+ // or has no event logs. For unit testing, we'd typically mock the file system.
24
+ const result = getNextLogFile();
25
+ expect(result).toMatch(/events-\d+\.jsonl$/);
26
+ });
27
+ });
28
+ describe("getLatestLogFile", () => {
29
+ it("returns undefined when no logs exist in empty directory", () => {
30
+ // Create .ralph directory but with no log files
31
+ const ralphDir = join(process.cwd(), ".ralph");
32
+ // This test would need to mock the file system to be fully isolated
33
+ // For now, we test the function returns the expected pattern
34
+ const result = getLatestLogFile();
35
+ // Either undefined (no logs) or a valid path (existing logs)
36
+ if (result !== undefined) {
37
+ expect(result).toMatch(/events-\d+\.jsonl$/);
38
+ }
39
+ });
40
+ });
41
+ });
42
+ // Integration tests that verify the sequential numbering behavior
43
+ describe("sequential log file naming", () => {
44
+ it("getNextLogFile returns one higher than getLatestLogFile", () => {
45
+ const latest = getLatestLogFile();
46
+ const next = getNextLogFile();
47
+ if (latest === undefined) {
48
+ // No existing logs, next should be events-1.jsonl
49
+ expect(next).toContain("events-1.jsonl");
50
+ }
51
+ else {
52
+ // Extract numbers and verify next is latest + 1
53
+ const latestMatch = latest.match(/events-(\d+)\.jsonl$/);
54
+ const nextMatch = next.match(/events-(\d+)\.jsonl$/);
55
+ expect(latestMatch).not.toBeNull();
56
+ expect(nextMatch).not.toBeNull();
57
+ if (latestMatch && nextMatch) {
58
+ const latestNum = parseInt(latestMatch[1], 10);
59
+ const nextNum = parseInt(nextMatch[1], 10);
60
+ expect(nextNum).toBe(latestNum + 1);
61
+ }
62
+ }
63
+ });
64
+ });
65
+ //# sourceMappingURL=getNextLogFile.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getNextLogFile.test.js","sourceRoot":"","sources":["../../src/lib/getNextLogFile.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Get the number of open issues from beads.
3
+ * Returns 0 if beads is not available or there are no issues.
4
+ */
5
+ export declare const getOpenIssueCount: () => number;
6
+ /**
7
+ * Calculate the default number of iterations based on open issues.
8
+ * Returns 120% of open issues, with a minimum of 10 and maximum of 100.
9
+ */
10
+ export declare const getDefaultIterations: () => number;
11
+ //# sourceMappingURL=getOpenIssueCount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getOpenIssueCount.d.ts","sourceRoot":"","sources":["../../src/lib/getOpenIssueCount.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAAO,MAYpC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAOvC,CAAA"}
@@ -0,0 +1,31 @@
1
+ import { execSync } from "child_process";
2
+ /**
3
+ * Get the number of open issues from beads.
4
+ * Returns 0 if beads is not available or there are no issues.
5
+ */
6
+ export const getOpenIssueCount = () => {
7
+ try {
8
+ const output = execSync("bd list --status=open --json", {
9
+ encoding: "utf-8",
10
+ stdio: ["pipe", "pipe", "pipe"],
11
+ }).trim();
12
+ const issues = JSON.parse(output);
13
+ return Array.isArray(issues) ? issues.length : 0;
14
+ }
15
+ catch {
16
+ return 0;
17
+ }
18
+ };
19
+ /**
20
+ * Calculate the default number of iterations based on open issues.
21
+ * Returns 120% of open issues, with a minimum of 10 and maximum of 100.
22
+ */
23
+ export const getDefaultIterations = () => {
24
+ const openIssues = getOpenIssueCount();
25
+ if (openIssues === 0) {
26
+ return 10; // Fallback when no issues or bd not available
27
+ }
28
+ const calculated = Math.ceil(openIssues * 1.2);
29
+ return Math.max(10, Math.min(100, calculated));
30
+ };
31
+ //# sourceMappingURL=getOpenIssueCount.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getOpenIssueCount.js","sourceRoot":"","sources":["../../src/lib/getOpenIssueCount.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAW,EAAE;IAC5C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,8BAA8B,EAAE;YACtD,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC,IAAI,EAAE,CAAA;QAET,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QACjC,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAA;IACV,CAAC;AACH,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAW,EAAE;IAC/C,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAA;IACtC,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,CAAA,CAAC,8CAA8C;IAC1D,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC,CAAA;IAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAA;AAChD,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=getOpenIssueCount.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getOpenIssueCount.test.d.ts","sourceRoot":"","sources":["../../src/lib/getOpenIssueCount.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,65 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { getOpenIssueCount, getDefaultIterations } from "./getOpenIssueCount.js";
3
+ import * as childProcess from "child_process";
4
+ vi.mock("child_process");
5
+ describe("getOpenIssueCount", () => {
6
+ beforeEach(() => {
7
+ vi.resetAllMocks();
8
+ });
9
+ afterEach(() => {
10
+ vi.restoreAllMocks();
11
+ });
12
+ it("returns the count of open issues", () => {
13
+ vi.mocked(childProcess.execSync).mockReturnValue(JSON.stringify([{ id: "1" }, { id: "2" }, { id: "3" }]));
14
+ expect(getOpenIssueCount()).toBe(3);
15
+ });
16
+ it("returns 0 when bd command fails", () => {
17
+ vi.mocked(childProcess.execSync).mockImplementation(() => {
18
+ throw new Error("bd not found");
19
+ });
20
+ expect(getOpenIssueCount()).toBe(0);
21
+ });
22
+ it("returns 0 when output is invalid JSON", () => {
23
+ vi.mocked(childProcess.execSync).mockReturnValue("not json");
24
+ expect(getOpenIssueCount()).toBe(0);
25
+ });
26
+ it("returns 0 when output is not an array", () => {
27
+ vi.mocked(childProcess.execSync).mockReturnValue('{"error": "something"}');
28
+ expect(getOpenIssueCount()).toBe(0);
29
+ });
30
+ });
31
+ describe("getDefaultIterations", () => {
32
+ beforeEach(() => {
33
+ vi.resetAllMocks();
34
+ });
35
+ afterEach(() => {
36
+ vi.restoreAllMocks();
37
+ });
38
+ it("returns 120% of open issues (rounded up)", () => {
39
+ vi.mocked(childProcess.execSync).mockReturnValue(JSON.stringify(Array(10).fill({ id: "x" })));
40
+ expect(getDefaultIterations()).toBe(12); // 10 * 1.2 = 12
41
+ });
42
+ it("returns minimum of 10 when calculated value is lower", () => {
43
+ vi.mocked(childProcess.execSync).mockReturnValue(JSON.stringify(Array(5).fill({ id: "x" })));
44
+ expect(getDefaultIterations()).toBe(10); // 5 * 1.2 = 6, but min is 10
45
+ });
46
+ it("returns maximum of 100 when calculated value is higher", () => {
47
+ vi.mocked(childProcess.execSync).mockReturnValue(JSON.stringify(Array(100).fill({ id: "x" })));
48
+ expect(getDefaultIterations()).toBe(100); // 100 * 1.2 = 120, but max is 100
49
+ });
50
+ it("returns 10 when no open issues", () => {
51
+ vi.mocked(childProcess.execSync).mockReturnValue("[]");
52
+ expect(getDefaultIterations()).toBe(10);
53
+ });
54
+ it("returns 10 when bd command fails", () => {
55
+ vi.mocked(childProcess.execSync).mockImplementation(() => {
56
+ throw new Error("bd not found");
57
+ });
58
+ expect(getDefaultIterations()).toBe(10);
59
+ });
60
+ it("handles fractional calculations correctly", () => {
61
+ vi.mocked(childProcess.execSync).mockReturnValue(JSON.stringify(Array(15).fill({ id: "x" })));
62
+ expect(getDefaultIterations()).toBe(18); // 15 * 1.2 = 18
63
+ });
64
+ });
65
+ //# sourceMappingURL=getOpenIssueCount.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getOpenIssueCount.test.js","sourceRoot":"","sources":["../../src/lib/getOpenIssueCount.test.ts"],"names":[],"mappings":"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"}
@@ -1,18 +1,34 @@
1
1
  export type ProgressData = {
2
2
  type: "beads" | "todo" | "none";
3
- remaining: number;
3
+ /** Number of issues/tasks completed since startup */
4
+ completed: number;
5
+ /** Total issues/tasks seen since startup (initial + created since) */
4
6
  total: number;
5
7
  };
6
8
  /**
7
9
  * Get progress data from the workspace.
8
10
  *
9
- * For beads workspaces: remaining = open issues, total = provided initialOpen or calculated
10
- * For todo.md workspaces: remaining = unchecked items, total = all items
11
+ * For beads workspaces: Uses timestamp-based counting to accurately track
12
+ * issues closed and created since startup.
13
+ * For todo.md workspaces: completed = checked items, total = all items
11
14
  */
12
- export declare const getProgress: (initialOpen?: number) => ProgressData;
15
+ export declare const getProgress: (initialCount: number, startupTimestamp: string) => ProgressData;
16
+ export type StartupSnapshot = {
17
+ /** Initial count of open + in_progress issues */
18
+ initialCount: number;
19
+ /** RFC3339 timestamp of when the snapshot was taken */
20
+ timestamp: string;
21
+ /** Type of workspace (beads or todo) */
22
+ type: "beads" | "todo";
23
+ };
24
+ /**
25
+ * Capture a startup snapshot for beads workspaces.
26
+ * Call this once at startup to capture the baseline count and timestamp.
27
+ * Returns undefined if not a beads workspace.
28
+ */
29
+ export declare const captureStartupSnapshot: () => StartupSnapshot | undefined;
13
30
  /**
14
- * Get the initial open issue count for beads workspaces.
15
- * Call this once at startup to capture the baseline.
31
+ * @deprecated Use captureStartupSnapshot instead
16
32
  */
17
33
  export declare const getInitialBeadsCount: () => number | undefined;
18
34
  //# sourceMappingURL=getProgress.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getProgress.d.ts","sourceRoot":"","sources":["../../src/lib/getProgress.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAMD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,cAAc,MAAM,KAAG,YAYlD,CAAA;AAoDD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAAM,GAAG,SAwBhD,CAAA"}
1
+ {"version":3,"file":"getProgress.d.ts","sourceRoot":"","sources":["../../src/lib/getProgress.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;IAC/B,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAA;IACjB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAMD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,cAAc,MAAM,EAAE,kBAAkB,MAAM,KAAG,YAY5E,CAAA;AA8DD,MAAM,MAAM,eAAe,GAAG;IAC5B,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAA;IACpB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAA;IACjB,wCAAwC;IACxC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAA;CACvB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAO,eAAe,GAAG,SAY3D,CAAA;AAoDD;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAAM,GAAG,SAGhD,CAAA"}
@@ -7,40 +7,47 @@ const todoFile = join(ralphDir, "todo.md");
7
7
  /**
8
8
  * Get progress data from the workspace.
9
9
  *
10
- * For beads workspaces: remaining = open issues, total = provided initialOpen or calculated
11
- * For todo.md workspaces: remaining = unchecked items, total = all items
10
+ * For beads workspaces: Uses timestamp-based counting to accurately track
11
+ * issues closed and created since startup.
12
+ * For todo.md workspaces: completed = checked items, total = all items
12
13
  */
13
- export const getProgress = (initialOpen) => {
14
+ export const getProgress = (initialCount, startupTimestamp) => {
14
15
  // Check for beads workspace first
15
16
  if (existsSync(beadsDir)) {
16
- return getBeadsProgress(initialOpen);
17
+ return getBeadsProgress(initialCount, startupTimestamp);
17
18
  }
18
19
  // Check for todo.md
19
20
  if (existsSync(todoFile)) {
20
21
  return getTodoProgress();
21
22
  }
22
- return { type: "none", remaining: 0, total: 0 };
23
+ return { type: "none", completed: 0, total: 0 };
23
24
  };
24
- const getBeadsProgress = (initialOpen) => {
25
+ const getBeadsProgress = (initialCount, startupTimestamp) => {
25
26
  try {
26
- // Get open + in_progress issues count using bd count
27
- const openCount = parseInt(execSync("bd count --status=open", {
27
+ // Count issues created since startup
28
+ const createdSinceStartup = parseInt(execSync(`bd count --created-after="${startupTimestamp}"`, {
28
29
  encoding: "utf-8",
29
30
  stdio: ["pipe", "pipe", "pipe"],
30
31
  }).trim(), 10);
31
- const inProgressCount = parseInt(execSync("bd count --status=in_progress", {
32
+ // Count current open + in_progress issues
33
+ const currentOpen = parseInt(execSync("bd count --status=open", {
32
34
  encoding: "utf-8",
33
35
  stdio: ["pipe", "pipe", "pipe"],
34
36
  }).trim(), 10);
35
- const remaining = openCount + inProgressCount;
36
- // Total is the initial count (baseline) when provided, otherwise current remaining
37
- // If more issues are opened during the run (remaining > initialOpen), use the larger value
38
- const total = initialOpen !== undefined ? Math.max(initialOpen, remaining) : remaining;
39
- return { type: "beads", remaining, total };
37
+ const currentInProgress = parseInt(execSync("bd count --status=in_progress", {
38
+ encoding: "utf-8",
39
+ stdio: ["pipe", "pipe", "pipe"],
40
+ }).trim(), 10);
41
+ const currentRemaining = currentOpen + currentInProgress;
42
+ // Total = initial open+in_progress + any new issues created
43
+ const total = initialCount + createdSinceStartup;
44
+ // Completed = total - remaining (accounts for issues closed by any means)
45
+ const completed = total - currentRemaining;
46
+ return { type: "beads", completed, total };
40
47
  }
41
48
  catch {
42
49
  // If bd command fails, return no progress
43
- return { type: "none", remaining: 0, total: 0 };
50
+ return { type: "none", completed: 0, total: 0 };
44
51
  }
45
52
  };
46
53
  const getTodoProgress = () => {
@@ -53,21 +60,31 @@ const getTodoProgress = () => {
53
60
  const checkedMatches = content.match(/- \[[xX]\]/g);
54
61
  const checked = checkedMatches ? checkedMatches.length : 0;
55
62
  const total = unchecked + checked;
56
- return { type: "todo", remaining: unchecked, total };
63
+ return { type: "todo", completed: checked, total };
57
64
  }
58
65
  catch {
59
- return { type: "none", remaining: 0, total: 0 };
66
+ return { type: "none", completed: 0, total: 0 };
60
67
  }
61
68
  };
62
69
  /**
63
- * Get the initial open issue count for beads workspaces.
64
- * Call this once at startup to capture the baseline.
70
+ * Capture a startup snapshot for beads workspaces.
71
+ * Call this once at startup to capture the baseline count and timestamp.
72
+ * Returns undefined if not a beads workspace.
65
73
  */
66
- export const getInitialBeadsCount = () => {
67
- if (!existsSync(beadsDir)) {
68
- return undefined;
74
+ export const captureStartupSnapshot = () => {
75
+ // Check for beads workspace
76
+ if (existsSync(beadsDir)) {
77
+ return captureBeadsSnapshot();
78
+ }
79
+ // Check for todo.md workspace
80
+ if (existsSync(todoFile)) {
81
+ return captureTodoSnapshot();
69
82
  }
83
+ return undefined;
84
+ };
85
+ const captureBeadsSnapshot = () => {
70
86
  try {
87
+ const timestamp = new Date().toISOString();
71
88
  const openCount = parseInt(execSync("bd count --status=open", {
72
89
  encoding: "utf-8",
73
90
  stdio: ["pipe", "pipe", "pipe"],
@@ -76,10 +93,39 @@ export const getInitialBeadsCount = () => {
76
93
  encoding: "utf-8",
77
94
  stdio: ["pipe", "pipe", "pipe"],
78
95
  }).trim(), 10);
79
- return openCount + inProgressCount;
96
+ return {
97
+ initialCount: openCount + inProgressCount,
98
+ timestamp,
99
+ type: "beads",
100
+ };
101
+ }
102
+ catch {
103
+ return undefined;
104
+ }
105
+ };
106
+ const captureTodoSnapshot = () => {
107
+ try {
108
+ const content = readFileSync(todoFile, "utf-8");
109
+ // Count all items (checked + unchecked)
110
+ const uncheckedMatches = content.match(/- \[ \]/g);
111
+ const unchecked = uncheckedMatches ? uncheckedMatches.length : 0;
112
+ const checkedMatches = content.match(/- \[[xX]\]/g);
113
+ const checked = checkedMatches ? checkedMatches.length : 0;
114
+ return {
115
+ initialCount: unchecked + checked,
116
+ timestamp: new Date().toISOString(),
117
+ type: "todo",
118
+ };
80
119
  }
81
120
  catch {
82
121
  return undefined;
83
122
  }
84
123
  };
124
+ /**
125
+ * @deprecated Use captureStartupSnapshot instead
126
+ */
127
+ export const getInitialBeadsCount = () => {
128
+ const snapshot = captureStartupSnapshot();
129
+ return snapshot?.initialCount;
130
+ };
85
131
  //# sourceMappingURL=getProgress.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"getProgress.js","sourceRoot":"","sources":["../../src/lib/getProgress.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"getProgress.js","sourceRoot":"","sources":["../../src/lib/getProgress.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { getProgress, getInitialBeadsCount } from "./getProgress.js";
2
+ import { getProgress, captureStartupSnapshot } from "./getProgress.js";
3
3
  import * as fs from "fs";
4
4
  import * as child_process from "child_process";
5
5
  vi.mock("fs");
@@ -15,42 +15,47 @@ describe("getProgress", () => {
15
15
  vi.restoreAllMocks();
16
16
  });
17
17
  describe("with beads workspace", () => {
18
- it("returns beads progress when .beads directory exists", () => {
18
+ it("returns beads progress with timestamp-based counting", () => {
19
19
  mockExistsSync.mockImplementation(path => {
20
20
  if (typeof path === "string" && path.endsWith(".beads"))
21
21
  return true;
22
22
  return false;
23
23
  });
24
- // First call returns open count, second returns in_progress count
25
- mockExecSync.mockReturnValueOnce("2").mockReturnValueOnce("1");
26
- const result = getProgress(5);
24
+ // Calls: created-after, status=open, status=in_progress
25
+ // 1 created, 3 open, 1 in_progress → completed = (5+1) - (3+1) = 2
26
+ mockExecSync.mockReturnValueOnce("1").mockReturnValueOnce("3").mockReturnValueOnce("1");
27
+ const result = getProgress(5, "2024-01-01T00:00:00.000Z");
27
28
  expect(result.type).toBe("beads");
28
- expect(result.remaining).toBe(3);
29
- expect(result.total).toBe(5);
29
+ expect(result.completed).toBe(2); // total(6) - remaining(4) = 2
30
+ expect(result.total).toBe(6); // 5 initial + 1 created
30
31
  });
31
- it("handles empty beads list", () => {
32
+ it("handles no progress (0 closed, 0 created)", () => {
32
33
  mockExistsSync.mockImplementation(path => {
33
34
  if (typeof path === "string" && path.endsWith(".beads"))
34
35
  return true;
35
36
  return false;
36
37
  });
37
- mockExecSync.mockReturnValueOnce("0").mockReturnValueOnce("0");
38
- const result = getProgress(5);
38
+ // Calls: created-after, status=open, status=in_progress
39
+ // 0 created, 3 open, 2 in_progress → completed = 5 - 5 = 0
40
+ mockExecSync.mockReturnValueOnce("0").mockReturnValueOnce("3").mockReturnValueOnce("2");
41
+ const result = getProgress(5, "2024-01-01T00:00:00.000Z");
39
42
  expect(result.type).toBe("beads");
40
- expect(result.remaining).toBe(0);
43
+ expect(result.completed).toBe(0);
41
44
  expect(result.total).toBe(5);
42
45
  });
43
- it("uses remaining as total when no initial provided", () => {
46
+ it("handles all closed", () => {
44
47
  mockExistsSync.mockImplementation(path => {
45
48
  if (typeof path === "string" && path.endsWith(".beads"))
46
49
  return true;
47
50
  return false;
48
51
  });
49
- mockExecSync.mockReturnValueOnce("2").mockReturnValueOnce("0");
50
- const result = getProgress();
52
+ // Calls: created-after, status=open, status=in_progress
53
+ // 0 created, 0 open, 0 in_progress → completed = 5 - 0 = 5
54
+ mockExecSync.mockReturnValueOnce("0").mockReturnValueOnce("0").mockReturnValueOnce("0");
55
+ const result = getProgress(5, "2024-01-01T00:00:00.000Z");
51
56
  expect(result.type).toBe("beads");
52
- expect(result.remaining).toBe(2);
53
- expect(result.total).toBe(2);
57
+ expect(result.completed).toBe(5);
58
+ expect(result.total).toBe(5);
54
59
  });
55
60
  it("returns none when bd command fails", () => {
56
61
  mockExistsSync.mockImplementation(path => {
@@ -61,7 +66,7 @@ describe("getProgress", () => {
61
66
  mockExecSync.mockImplementation(() => {
62
67
  throw new Error("Command failed");
63
68
  });
64
- const result = getProgress();
69
+ const result = getProgress(5, "2024-01-01T00:00:00.000Z");
65
70
  expect(result.type).toBe("none");
66
71
  });
67
72
  });
@@ -83,9 +88,9 @@ describe("getProgress", () => {
83
88
 
84
89
  ### Done
85
90
  `);
86
- const result = getProgress();
91
+ const result = getProgress(4, "2024-01-01T00:00:00.000Z");
87
92
  expect(result.type).toBe("todo");
88
- expect(result.remaining).toBe(2);
93
+ expect(result.completed).toBe(2); // 2 checked
89
94
  expect(result.total).toBe(4);
90
95
  });
91
96
  it("handles all unchecked items", () => {
@@ -101,9 +106,9 @@ describe("getProgress", () => {
101
106
  - [ ] Task 2
102
107
  - [ ] Task 3
103
108
  `);
104
- const result = getProgress();
109
+ const result = getProgress(3, "2024-01-01T00:00:00.000Z");
105
110
  expect(result.type).toBe("todo");
106
- expect(result.remaining).toBe(3);
111
+ expect(result.completed).toBe(0);
107
112
  expect(result.total).toBe(3);
108
113
  });
109
114
  it("handles all checked items", () => {
@@ -118,9 +123,9 @@ describe("getProgress", () => {
118
123
  - [x] Task 1
119
124
  - [X] Task 2
120
125
  `);
121
- const result = getProgress();
126
+ const result = getProgress(2, "2024-01-01T00:00:00.000Z");
122
127
  expect(result.type).toBe("todo");
123
- expect(result.remaining).toBe(0);
128
+ expect(result.completed).toBe(2);
124
129
  expect(result.total).toBe(2);
125
130
  });
126
131
  it("handles empty todo file", () => {
@@ -132,39 +137,68 @@ describe("getProgress", () => {
132
137
  return false;
133
138
  });
134
139
  mockReadFileSync.mockReturnValue("");
135
- const result = getProgress();
140
+ const result = getProgress(0, "2024-01-01T00:00:00.000Z");
136
141
  expect(result.type).toBe("todo");
137
- expect(result.remaining).toBe(0);
142
+ expect(result.completed).toBe(0);
138
143
  expect(result.total).toBe(0);
139
144
  });
140
145
  });
141
146
  describe("with no workspace", () => {
142
147
  it("returns none when neither .beads nor todo.md exists", () => {
143
148
  mockExistsSync.mockReturnValue(false);
144
- const result = getProgress();
149
+ const result = getProgress(0, "2024-01-01T00:00:00.000Z");
145
150
  expect(result.type).toBe("none");
146
- expect(result.remaining).toBe(0);
151
+ expect(result.completed).toBe(0);
147
152
  expect(result.total).toBe(0);
148
153
  });
149
154
  });
150
155
  });
151
- describe("getInitialBeadsCount", () => {
156
+ describe("captureStartupSnapshot", () => {
152
157
  beforeEach(() => {
153
158
  vi.clearAllMocks();
159
+ vi.useFakeTimers();
160
+ vi.setSystemTime(new Date("2024-06-15T10:30:00.000Z"));
154
161
  });
155
- it("returns count when .beads exists", () => {
162
+ afterEach(() => {
163
+ vi.restoreAllMocks();
164
+ vi.useRealTimers();
165
+ });
166
+ it("returns snapshot when .beads exists", () => {
156
167
  mockExistsSync.mockImplementation(path => {
157
168
  if (typeof path === "string" && path.endsWith(".beads"))
158
169
  return true;
159
170
  return false;
160
171
  });
161
172
  mockExecSync.mockReturnValueOnce("2").mockReturnValueOnce("1");
162
- const result = getInitialBeadsCount();
163
- expect(result).toBe(3);
173
+ const result = captureStartupSnapshot();
174
+ expect(result).toEqual({
175
+ initialCount: 3,
176
+ timestamp: "2024-06-15T10:30:00.000Z",
177
+ type: "beads",
178
+ });
179
+ });
180
+ it("returns snapshot for todo.md workspace", () => {
181
+ mockExistsSync.mockImplementation(path => {
182
+ if (typeof path === "string" && path.endsWith(".beads"))
183
+ return false;
184
+ if (typeof path === "string" && path.endsWith("todo.md"))
185
+ return true;
186
+ return false;
187
+ });
188
+ mockReadFileSync.mockReturnValue(`
189
+ - [ ] Task 1
190
+ - [x] Task 2
191
+ `);
192
+ const result = captureStartupSnapshot();
193
+ expect(result).toEqual({
194
+ initialCount: 2,
195
+ timestamp: "2024-06-15T10:30:00.000Z",
196
+ type: "todo",
197
+ });
164
198
  });
165
- it("returns undefined when .beads does not exist", () => {
199
+ it("returns undefined when neither .beads nor todo.md exists", () => {
166
200
  mockExistsSync.mockReturnValue(false);
167
- const result = getInitialBeadsCount();
201
+ const result = captureStartupSnapshot();
168
202
  expect(result).toBeUndefined();
169
203
  });
170
204
  it("returns undefined when bd command fails", () => {
@@ -176,7 +210,7 @@ describe("getInitialBeadsCount", () => {
176
210
  mockExecSync.mockImplementation(() => {
177
211
  throw new Error("Command failed");
178
212
  });
179
- const result = getInitialBeadsCount();
213
+ const result = captureStartupSnapshot();
180
214
  expect(result).toBeUndefined();
181
215
  });
182
216
  });