@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.
- package/README.md +13 -100
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +21 -7
- package/dist/cli.js.map +1 -1
- package/dist/components/App.d.ts +2 -1
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +5 -1
- package/dist/components/App.js.map +1 -1
- package/dist/components/InitRalph.js +5 -5
- package/dist/components/InitRalph.js.map +1 -1
- package/dist/components/IterationRunner.d.ts +1 -1
- package/dist/components/IterationRunner.d.ts.map +1 -1
- package/dist/components/IterationRunner.js +212 -56
- package/dist/components/IterationRunner.js.map +1 -1
- package/dist/components/IterationRunner.test.js +2 -2
- package/dist/components/IterationRunner.test.js.map +1 -1
- package/dist/components/JsonOutput.d.ts +14 -0
- package/dist/components/JsonOutput.d.ts.map +1 -0
- package/dist/components/JsonOutput.js +246 -0
- package/dist/components/JsonOutput.js.map +1 -0
- package/dist/components/ProgressBar.d.ts +7 -5
- package/dist/components/ProgressBar.d.ts.map +1 -1
- package/dist/components/ProgressBar.js +7 -5
- package/dist/components/ProgressBar.js.map +1 -1
- package/dist/components/ProgressBar.test.js +24 -11
- package/dist/components/ProgressBar.test.js.map +1 -1
- package/dist/lib/MessageQueue.d.ts +2 -0
- package/dist/lib/MessageQueue.d.ts.map +1 -1
- package/dist/lib/MessageQueue.js +35 -2
- package/dist/lib/MessageQueue.js.map +1 -1
- package/dist/lib/MessageQueue.test.js +146 -0
- package/dist/lib/MessageQueue.test.js.map +1 -1
- package/dist/lib/StdinCommandHandler.d.ts +42 -0
- package/dist/lib/StdinCommandHandler.d.ts.map +1 -0
- package/dist/lib/StdinCommandHandler.js +102 -0
- package/dist/lib/StdinCommandHandler.js.map +1 -0
- package/dist/lib/StdinCommandHandler.test.d.ts +2 -0
- package/dist/lib/StdinCommandHandler.test.d.ts.map +1 -0
- package/dist/lib/StdinCommandHandler.test.js +93 -0
- package/dist/lib/StdinCommandHandler.test.js.map +1 -0
- package/dist/lib/debug.d.ts +18 -0
- package/dist/lib/debug.d.ts.map +1 -0
- package/dist/lib/debug.js +44 -0
- package/dist/lib/debug.js.map +1 -0
- package/dist/lib/formatContentBlock.js +1 -1
- package/dist/lib/formatContentBlock.js.map +1 -1
- package/dist/lib/getNextLogFile.d.ts +12 -0
- package/dist/lib/getNextLogFile.d.ts.map +1 -0
- package/dist/lib/getNextLogFile.js +52 -0
- package/dist/lib/getNextLogFile.js.map +1 -0
- package/dist/lib/getNextLogFile.test.d.ts +2 -0
- package/dist/lib/getNextLogFile.test.d.ts.map +1 -0
- package/dist/lib/getNextLogFile.test.js +65 -0
- package/dist/lib/getNextLogFile.test.js.map +1 -0
- package/dist/lib/getOpenIssueCount.d.ts +11 -0
- package/dist/lib/getOpenIssueCount.d.ts.map +1 -0
- package/dist/lib/getOpenIssueCount.js +31 -0
- package/dist/lib/getOpenIssueCount.js.map +1 -0
- package/dist/lib/getOpenIssueCount.test.d.ts +2 -0
- package/dist/lib/getOpenIssueCount.test.d.ts.map +1 -0
- package/dist/lib/getOpenIssueCount.test.js +65 -0
- package/dist/lib/getOpenIssueCount.test.js.map +1 -0
- package/dist/lib/getProgress.d.ts +22 -6
- package/dist/lib/getProgress.d.ts.map +1 -1
- package/dist/lib/getProgress.js +69 -23
- package/dist/lib/getProgress.js.map +1 -1
- package/dist/lib/getProgress.test.js +68 -34
- package/dist/lib/getProgress.test.js.map +1 -1
- package/package.json +1 -1
- package/templates/prompt-beads.md +5 -5
- package/dist/lib/cleanupAllWorktrees.d.ts +0 -5
- package/dist/lib/cleanupAllWorktrees.d.ts.map +0 -1
- package/dist/lib/cleanupAllWorktrees.js +0 -23
- package/dist/lib/cleanupAllWorktrees.js.map +0 -1
- package/dist/lib/cleanupWorktree.d.ts +0 -6
- package/dist/lib/cleanupWorktree.d.ts.map +0 -1
- package/dist/lib/cleanupWorktree.js +0 -33
- package/dist/lib/cleanupWorktree.js.map +0 -1
- package/dist/lib/copyRalphFilesFromWorktree.d.ts +0 -5
- package/dist/lib/copyRalphFilesFromWorktree.d.ts.map +0 -1
- package/dist/lib/copyRalphFilesFromWorktree.js +0 -19
- package/dist/lib/copyRalphFilesFromWorktree.js.map +0 -1
- package/dist/lib/copyRalphFilesToWorktree.d.ts +0 -5
- package/dist/lib/copyRalphFilesToWorktree.d.ts.map +0 -1
- package/dist/lib/copyRalphFilesToWorktree.js +0 -21
- package/dist/lib/copyRalphFilesToWorktree.js.map +0 -1
- package/dist/lib/createWorktree.d.ts +0 -6
- package/dist/lib/createWorktree.d.ts.map +0 -1
- package/dist/lib/createWorktree.js +0 -33
- package/dist/lib/createWorktree.js.map +0 -1
- package/dist/lib/getGitRoot.d.ts +0 -5
- package/dist/lib/getGitRoot.d.ts.map +0 -1
- package/dist/lib/getGitRoot.js +0 -18
- package/dist/lib/getGitRoot.js.map +0 -1
- package/dist/lib/installDependencies.d.ts +0 -5
- package/dist/lib/installDependencies.d.ts.map +0 -1
- package/dist/lib/installDependencies.js +0 -23
- package/dist/lib/installDependencies.js.map +0 -1
- package/dist/lib/installDependencies.test.d.ts +0 -2
- package/dist/lib/installDependencies.test.d.ts.map +0 -1
- package/dist/lib/installDependencies.test.js +0 -37
- package/dist/lib/installDependencies.test.js.map +0 -1
- package/dist/lib/mergeWorktreeToMain.d.ts +0 -6
- package/dist/lib/mergeWorktreeToMain.d.ts.map +0 -1
- package/dist/lib/mergeWorktreeToMain.js +0 -54
- package/dist/lib/mergeWorktreeToMain.js.map +0 -1
- package/dist/lib/outputState.d.ts +0 -5
- package/dist/lib/outputState.d.ts.map +0 -1
- package/dist/lib/outputState.js +0 -6
- package/dist/lib/outputState.js.map +0 -1
- package/dist/lib/popStash.d.ts +0 -5
- package/dist/lib/popStash.d.ts.map +0 -1
- package/dist/lib/popStash.js +0 -17
- package/dist/lib/popStash.js.map +0 -1
- package/dist/lib/processEvent.d.ts +0 -2
- package/dist/lib/processEvent.d.ts.map +0 -1
- package/dist/lib/processEvent.js +0 -100
- package/dist/lib/processEvent.js.map +0 -1
- package/dist/lib/replayLog.d.ts +0 -2
- package/dist/lib/replayLog.d.ts.map +0 -1
- package/dist/lib/replayLog.js +0 -35
- package/dist/lib/replayLog.js.map +0 -1
- package/dist/lib/resolveConflicts.d.ts +0 -6
- package/dist/lib/resolveConflicts.d.ts.map +0 -1
- package/dist/lib/resolveConflicts.js +0 -76
- package/dist/lib/resolveConflicts.js.map +0 -1
- package/dist/lib/runIteration.d.ts +0 -2
- package/dist/lib/runIteration.d.ts.map +0 -1
- package/dist/lib/runIteration.js +0 -91
- package/dist/lib/runIteration.js.map +0 -1
- package/dist/lib/showToolUse.d.ts +0 -2
- package/dist/lib/showToolUse.d.ts.map +0 -1
- package/dist/lib/showToolUse.js +0 -17
- package/dist/lib/showToolUse.js.map +0 -1
- package/dist/lib/signalHandler.d.ts +0 -3
- package/dist/lib/signalHandler.d.ts.map +0 -1
- package/dist/lib/signalHandler.js +0 -23
- package/dist/lib/signalHandler.js.map +0 -1
- package/dist/lib/stashChanges.d.ts +0 -6
- package/dist/lib/stashChanges.d.ts.map +0 -1
- package/dist/lib/stashChanges.js +0 -27
- package/dist/lib/stashChanges.js.map +0 -1
- package/dist/lib/textFormatting.d.ts +0 -4
- package/dist/lib/textFormatting.d.ts.map +0 -1
- package/dist/lib/textFormatting.js +0 -94
- package/dist/lib/textFormatting.js.map +0 -1
- package/dist/lib/types.d.ts +0 -13
- package/dist/lib/types.d.ts.map +0 -1
- package/dist/lib/types.js +0 -15
- package/dist/lib/types.js.map +0 -1
- package/dist/lib/worktree.d.ts +0 -43
- package/dist/lib/worktree.d.ts.map +0 -1
- package/dist/lib/worktree.js +0 -210
- package/dist/lib/worktree.js.map +0 -1
- package/dist/ui/EventProcessor.d.ts +0 -11
- package/dist/ui/EventProcessor.d.ts.map +0 -1
- package/dist/ui/EventProcessor.js +0 -95
- package/dist/ui/EventProcessor.js.map +0 -1
- package/dist/ui/IterationApp.d.ts +0 -10
- package/dist/ui/IterationApp.d.ts.map +0 -1
- package/dist/ui/IterationApp.js +0 -21
- package/dist/ui/IterationApp.js.map +0 -1
- package/dist/ui/IterationUI.d.ts +0 -13
- package/dist/ui/IterationUI.d.ts.map +0 -1
- package/dist/ui/IterationUI.js +0 -19
- package/dist/ui/IterationUI.js.map +0 -1
- package/dist/ui/TextDisplay.d.ts +0 -7
- package/dist/ui/TextDisplay.d.ts.map +0 -1
- package/dist/ui/TextDisplay.js +0 -41
- package/dist/ui/TextDisplay.js.map +0 -1
- package/dist/ui/ToolUseDisplay.d.ts +0 -10
- package/dist/ui/ToolUseDisplay.d.ts.map +0 -1
- package/dist/ui/ToolUseDisplay.js +0 -10
- package/dist/ui/ToolUseDisplay.js.map +0 -1
- /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 @@
|
|
|
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
|
-
|
|
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:
|
|
10
|
-
*
|
|
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: (
|
|
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
|
-
*
|
|
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
|
|
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"}
|
package/dist/lib/getProgress.js
CHANGED
|
@@ -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:
|
|
11
|
-
*
|
|
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 = (
|
|
14
|
+
export const getProgress = (initialCount, startupTimestamp) => {
|
|
14
15
|
// Check for beads workspace first
|
|
15
16
|
if (existsSync(beadsDir)) {
|
|
16
|
-
return getBeadsProgress(
|
|
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",
|
|
23
|
+
return { type: "none", completed: 0, total: 0 };
|
|
23
24
|
};
|
|
24
|
-
const getBeadsProgress = (
|
|
25
|
+
const getBeadsProgress = (initialCount, startupTimestamp) => {
|
|
25
26
|
try {
|
|
26
|
-
//
|
|
27
|
-
const
|
|
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
|
-
|
|
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
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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",
|
|
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",
|
|
63
|
+
return { type: "todo", completed: checked, total };
|
|
57
64
|
}
|
|
58
65
|
catch {
|
|
59
|
-
return { type: "none",
|
|
66
|
+
return { type: "none", completed: 0, total: 0 };
|
|
60
67
|
}
|
|
61
68
|
};
|
|
62
69
|
/**
|
|
63
|
-
*
|
|
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
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
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":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;
|
|
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,
|
|
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
|
|
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
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
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.
|
|
29
|
-
expect(result.total).toBe(
|
|
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
|
|
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
|
-
|
|
38
|
-
|
|
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.
|
|
43
|
+
expect(result.completed).toBe(0);
|
|
41
44
|
expect(result.total).toBe(5);
|
|
42
45
|
});
|
|
43
|
-
it("
|
|
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
|
-
|
|
50
|
-
|
|
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.
|
|
53
|
-
expect(result.total).toBe(
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
151
|
+
expect(result.completed).toBe(0);
|
|
147
152
|
expect(result.total).toBe(0);
|
|
148
153
|
});
|
|
149
154
|
});
|
|
150
155
|
});
|
|
151
|
-
describe("
|
|
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
|
-
|
|
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 =
|
|
163
|
-
expect(result).
|
|
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
|
|
199
|
+
it("returns undefined when neither .beads nor todo.md exists", () => {
|
|
166
200
|
mockExistsSync.mockReturnValue(false);
|
|
167
|
-
const result =
|
|
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 =
|
|
213
|
+
const result = captureStartupSnapshot();
|
|
180
214
|
expect(result).toBeUndefined();
|
|
181
215
|
});
|
|
182
216
|
});
|