agentic-forge 0.0.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/.gitattributes +24 -0
- package/.github/workflows/ci.yml +70 -0
- package/.markdownlint-cli2.jsonc +16 -0
- package/.prettierignore +3 -0
- package/.prettierrc +6 -0
- package/.vscode/agentic-forge.code-workspace +26 -0
- package/CHANGELOG.md +100 -0
- package/CLAUDE.md +158 -0
- package/CONTRIBUTING.md +152 -0
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/agentic-forge-banner.png +0 -0
- package/biome.json +21 -0
- package/package.json +5 -0
- package/scripts/copy-assets.js +21 -0
- package/src/agents/explorer.md +97 -0
- package/src/agents/reviewer.md +137 -0
- package/src/checkpoints/manager.ts +119 -0
- package/src/claude/.claude/skills/analyze/SKILL.md +241 -0
- package/src/claude/.claude/skills/analyze/references/bug.md +62 -0
- package/src/claude/.claude/skills/analyze/references/debt.md +76 -0
- package/src/claude/.claude/skills/analyze/references/doc.md +67 -0
- package/src/claude/.claude/skills/analyze/references/security.md +76 -0
- package/src/claude/.claude/skills/analyze/references/style.md +72 -0
- package/src/claude/.claude/skills/create-checkpoint/SKILL.md +88 -0
- package/src/claude/.claude/skills/create-log/SKILL.md +75 -0
- package/src/claude/.claude/skills/fix-analyze/SKILL.md +102 -0
- package/src/claude/.claude/skills/git-branch/SKILL.md +71 -0
- package/src/claude/.claude/skills/git-commit/SKILL.md +107 -0
- package/src/claude/.claude/skills/git-pr/SKILL.md +96 -0
- package/src/claude/.claude/skills/orchestrate/SKILL.md +120 -0
- package/src/claude/.claude/skills/sdlc-plan/SKILL.md +163 -0
- package/src/claude/.claude/skills/sdlc-plan/references/bug.md +115 -0
- package/src/claude/.claude/skills/sdlc-plan/references/chore.md +105 -0
- package/src/claude/.claude/skills/sdlc-plan/references/feature.md +130 -0
- package/src/claude/.claude/skills/sdlc-review/SKILL.md +215 -0
- package/src/claude/.claude/skills/workflow-builder/SKILL.md +185 -0
- package/src/claude/.claude/skills/workflow-builder/references/REFERENCE.md +487 -0
- package/src/claude/.claude/skills/workflow-builder/references/workflow-example.yaml +427 -0
- package/src/cli.ts +182 -0
- package/src/commands/config-cmd.ts +28 -0
- package/src/commands/index.ts +21 -0
- package/src/commands/init.ts +96 -0
- package/src/commands/release-notes.ts +85 -0
- package/src/commands/resume.ts +103 -0
- package/src/commands/run.ts +234 -0
- package/src/commands/shortcuts.ts +11 -0
- package/src/commands/skills-dir.ts +11 -0
- package/src/commands/status.ts +112 -0
- package/src/commands/update.ts +64 -0
- package/src/commands/version.ts +27 -0
- package/src/commands/workflows.ts +129 -0
- package/src/config.ts +129 -0
- package/src/console.ts +790 -0
- package/src/executor.ts +354 -0
- package/src/git/worktree.ts +236 -0
- package/src/logging/logger.ts +95 -0
- package/src/orchestrator.ts +815 -0
- package/src/parser.ts +225 -0
- package/src/progress.ts +306 -0
- package/src/prompts/agentic-system.md +31 -0
- package/src/ralph-loop.ts +260 -0
- package/src/renderer.ts +164 -0
- package/src/runner.ts +634 -0
- package/src/signal-manager.ts +55 -0
- package/src/steps/base.ts +71 -0
- package/src/steps/conditional-step.ts +144 -0
- package/src/steps/index.ts +15 -0
- package/src/steps/parallel-step.ts +213 -0
- package/src/steps/prompt-step.ts +121 -0
- package/src/steps/ralph-loop-step.ts +186 -0
- package/src/steps/serial-step.ts +84 -0
- package/src/templates/analysis/bug.md.j2 +35 -0
- package/src/templates/analysis/debt.md.j2 +38 -0
- package/src/templates/analysis/doc.md.j2 +45 -0
- package/src/templates/analysis/security.md.j2 +35 -0
- package/src/templates/analysis/style.md.j2 +44 -0
- package/src/templates/analysis-summary.md.j2 +58 -0
- package/src/templates/checkpoint.md.j2 +27 -0
- package/src/templates/implementation-report.md.j2 +81 -0
- package/src/templates/memory.md.j2 +16 -0
- package/src/templates/plan-bug.md.j2 +42 -0
- package/src/templates/plan-chore.md.j2 +27 -0
- package/src/templates/plan-feature.md.j2 +41 -0
- package/src/templates/progress.json.j2 +16 -0
- package/src/templates/ralph-report.md.j2 +45 -0
- package/src/types.ts +141 -0
- package/src/workflows/analyze-codebase-merge.yaml +328 -0
- package/src/workflows/analyze-codebase.yaml +196 -0
- package/src/workflows/analyze-single.yaml +56 -0
- package/src/workflows/demo.yaml +180 -0
- package/src/workflows/one-shot.yaml +54 -0
- package/src/workflows/plan-build-review.yaml +160 -0
- package/src/workflows/ralph-loop.yaml +73 -0
- package/tests/config.test.ts +219 -0
- package/tests/console.test.ts +506 -0
- package/tests/executor.test.ts +339 -0
- package/tests/init.test.ts +86 -0
- package/tests/logger.test.ts +110 -0
- package/tests/parser.test.ts +290 -0
- package/tests/progress.test.ts +345 -0
- package/tests/ralph-loop.test.ts +418 -0
- package/tests/renderer.test.ts +350 -0
- package/tests/runner.test.ts +497 -0
- package/tests/setup.test.ts +7 -0
- package/tests/signal-manager.test.ts +26 -0
- package/tests/steps.test.ts +412 -0
- package/tests/worktree.test.ts +411 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import {
|
|
6
|
+
type CompletionResult,
|
|
7
|
+
buildRalphSystemMessage,
|
|
8
|
+
createRalphState,
|
|
9
|
+
deactivateRalphState,
|
|
10
|
+
deleteRalphState,
|
|
11
|
+
detectCompletionPromise,
|
|
12
|
+
getRalphStatePath,
|
|
13
|
+
loadRalphState,
|
|
14
|
+
parseRalphState,
|
|
15
|
+
updateRalphIteration,
|
|
16
|
+
} from "../src/ralph-loop.js";
|
|
17
|
+
|
|
18
|
+
let tempDir: string;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
tempDir = path.join(tmpdir(), `ralph-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
22
|
+
mkdirSync(tempDir, { recursive: true });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
try {
|
|
27
|
+
const { rmSync } = require("node:fs");
|
|
28
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
29
|
+
} catch {
|
|
30
|
+
// Ignore cleanup errors on Windows
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("getRalphStatePath", () => {
|
|
35
|
+
it("follows expected format", () => {
|
|
36
|
+
const result = getRalphStatePath("workflow-123", "apply-fixes", tempDir);
|
|
37
|
+
const expected = path.join(
|
|
38
|
+
tempDir,
|
|
39
|
+
"agentic",
|
|
40
|
+
"outputs",
|
|
41
|
+
"workflow-123",
|
|
42
|
+
"ralph-apply-fixes.md",
|
|
43
|
+
);
|
|
44
|
+
expect(result).toBe(expected);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("sanitizes special characters in step name", () => {
|
|
48
|
+
const result = getRalphStatePath("workflow", "step/with:special@chars", tempDir);
|
|
49
|
+
const filename = path.basename(result);
|
|
50
|
+
expect(filename).not.toContain("/");
|
|
51
|
+
expect(filename).not.toContain(":");
|
|
52
|
+
expect(filename).not.toContain("@");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("createRalphState", () => {
|
|
57
|
+
it("creates basic ralph state", () => {
|
|
58
|
+
const state = createRalphState(
|
|
59
|
+
"test-workflow",
|
|
60
|
+
"apply-fixes",
|
|
61
|
+
"Fix the issues",
|
|
62
|
+
10,
|
|
63
|
+
"FIXES_COMPLETE",
|
|
64
|
+
tempDir,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(state.active).toBe(true);
|
|
68
|
+
expect(state.iteration).toBe(1);
|
|
69
|
+
expect(state.maxIterations).toBe(10);
|
|
70
|
+
expect(state.completionPromise).toBe("FIXES_COMPLETE");
|
|
71
|
+
expect(state.prompt).toBe("Fix the issues");
|
|
72
|
+
expect(state.startedAt).toBeTruthy();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("saves file to disk", () => {
|
|
76
|
+
createRalphState("save-test", "test-step", "Test prompt", 5, "DONE", tempDir);
|
|
77
|
+
|
|
78
|
+
const statePath = getRalphStatePath("save-test", "test-step", tempDir);
|
|
79
|
+
expect(existsSync(statePath)).toBe(true);
|
|
80
|
+
const content = readFileSync(statePath, "utf-8");
|
|
81
|
+
expect(content).toContain("active: true");
|
|
82
|
+
expect(content).toContain("iteration: 1");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("loadRalphState", () => {
|
|
87
|
+
it("loads existing ralph state", () => {
|
|
88
|
+
createRalphState("load-test", "test-step", "Original prompt", 10, "COMPLETE", tempDir);
|
|
89
|
+
|
|
90
|
+
const loaded = loadRalphState("load-test", "test-step", tempDir);
|
|
91
|
+
|
|
92
|
+
expect(loaded).not.toBeNull();
|
|
93
|
+
expect(loaded?.active).toBe(true);
|
|
94
|
+
expect(loaded?.iteration).toBe(1);
|
|
95
|
+
expect(loaded?.maxIterations).toBe(10);
|
|
96
|
+
expect(loaded?.completionPromise).toBe("COMPLETE");
|
|
97
|
+
expect(loaded?.prompt).toBe("Original prompt");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("returns null for nonexistent state", () => {
|
|
101
|
+
expect(loadRalphState("nonexistent", "step", tempDir)).toBeNull();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe("updateRalphIteration", () => {
|
|
106
|
+
it("increments iteration counter", () => {
|
|
107
|
+
createRalphState("update-test", "test-step", "Test", 10, "DONE", tempDir);
|
|
108
|
+
|
|
109
|
+
const state = updateRalphIteration("update-test", "test-step", tempDir);
|
|
110
|
+
|
|
111
|
+
expect(state).not.toBeNull();
|
|
112
|
+
expect(state?.iteration).toBe(2);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("persists iteration update to file", () => {
|
|
116
|
+
createRalphState("persist-test", "test-step", "Test", 10, "DONE", tempDir);
|
|
117
|
+
|
|
118
|
+
updateRalphIteration("persist-test", "test-step", tempDir);
|
|
119
|
+
const loaded = loadRalphState("persist-test", "test-step", tempDir);
|
|
120
|
+
|
|
121
|
+
expect(loaded).not.toBeNull();
|
|
122
|
+
expect(loaded?.iteration).toBe(2);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("returns null for nonexistent state", () => {
|
|
126
|
+
expect(updateRalphIteration("nonexistent", "step", tempDir)).toBeNull();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("deactivateRalphState", () => {
|
|
131
|
+
it("deactivates ralph state", () => {
|
|
132
|
+
createRalphState("deactivate-test", "test-step", "Test", 10, "DONE", tempDir);
|
|
133
|
+
|
|
134
|
+
deactivateRalphState("deactivate-test", "test-step", tempDir);
|
|
135
|
+
const loaded = loadRalphState("deactivate-test", "test-step", tempDir);
|
|
136
|
+
|
|
137
|
+
expect(loaded).not.toBeNull();
|
|
138
|
+
expect(loaded?.active).toBe(false);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("deleteRalphState", () => {
|
|
143
|
+
it("deletes ralph state file", () => {
|
|
144
|
+
createRalphState("delete-test", "test-step", "Test", 10, "DONE", tempDir);
|
|
145
|
+
|
|
146
|
+
const statePath = getRalphStatePath("delete-test", "test-step", tempDir);
|
|
147
|
+
expect(existsSync(statePath)).toBe(true);
|
|
148
|
+
|
|
149
|
+
deleteRalphState("delete-test", "test-step", tempDir);
|
|
150
|
+
expect(existsSync(statePath)).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("does not throw for nonexistent state", () => {
|
|
154
|
+
expect(() => deleteRalphState("nonexistent", "step", tempDir)).not.toThrow();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe("parseRalphState", () => {
|
|
159
|
+
it("parses valid markdown with frontmatter", () => {
|
|
160
|
+
const content = `---
|
|
161
|
+
active: true
|
|
162
|
+
iteration: 3
|
|
163
|
+
max_iterations: 10
|
|
164
|
+
completion_promise: COMPLETE
|
|
165
|
+
started_at: "2026-01-11T14:30:00Z"
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
# Ralph Loop Prompt
|
|
169
|
+
|
|
170
|
+
Fix all the bugs in the codebase.
|
|
171
|
+
`;
|
|
172
|
+
const state = parseRalphState(content);
|
|
173
|
+
|
|
174
|
+
expect(state).not.toBeNull();
|
|
175
|
+
expect(state?.active).toBe(true);
|
|
176
|
+
expect(state?.iteration).toBe(3);
|
|
177
|
+
expect(state?.maxIterations).toBe(10);
|
|
178
|
+
expect(state?.completionPromise).toBe("COMPLETE");
|
|
179
|
+
expect(state?.prompt).toBe("Fix all the bugs in the codebase.");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("returns null for content without frontmatter", () => {
|
|
183
|
+
expect(parseRalphState("Just plain text without frontmatter")).toBeNull();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("parses incomplete frontmatter with defaults", () => {
|
|
187
|
+
const content = `---
|
|
188
|
+
active: true
|
|
189
|
+
---`;
|
|
190
|
+
const state = parseRalphState(content);
|
|
191
|
+
expect(state).not.toBeNull();
|
|
192
|
+
expect(state?.active).toBe(true);
|
|
193
|
+
expect(state?.iteration).toBe(1);
|
|
194
|
+
expect(state?.maxIterations).toBe(5);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe("detectCompletionPromise", () => {
|
|
199
|
+
it("detects completion in JSON code block", () => {
|
|
200
|
+
const output = `
|
|
201
|
+
I've completed the task.
|
|
202
|
+
|
|
203
|
+
\`\`\`json
|
|
204
|
+
{
|
|
205
|
+
"ralph_complete": true,
|
|
206
|
+
"promise": "COMPLETED"
|
|
207
|
+
}
|
|
208
|
+
\`\`\`
|
|
209
|
+
`;
|
|
210
|
+
const result = detectCompletionPromise(output, "COMPLETED");
|
|
211
|
+
expect(result.isComplete).toBe(true);
|
|
212
|
+
expect(result.promiseMatched).toBe(true);
|
|
213
|
+
expect(result.promiseValue).toBe("COMPLETED");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("detects completion in plain code block", () => {
|
|
217
|
+
const output = `
|
|
218
|
+
Done!
|
|
219
|
+
|
|
220
|
+
\`\`\`
|
|
221
|
+
{"ralph_complete": true, "promise": "DONE"}
|
|
222
|
+
\`\`\`
|
|
223
|
+
`;
|
|
224
|
+
const result = detectCompletionPromise(output, "DONE");
|
|
225
|
+
expect(result.isComplete).toBe(true);
|
|
226
|
+
expect(result.promiseMatched).toBe(true);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("detects completion in raw JSON", () => {
|
|
230
|
+
const output = 'The task is done. {"ralph_complete": true, "promise": "FINISHED"}';
|
|
231
|
+
const result = detectCompletionPromise(output, "FINISHED");
|
|
232
|
+
expect(result.isComplete).toBe(true);
|
|
233
|
+
expect(result.promiseMatched).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("detects wrong promise value", () => {
|
|
237
|
+
const output = `
|
|
238
|
+
\`\`\`json
|
|
239
|
+
{"ralph_complete": true, "promise": "WRONG_PROMISE"}
|
|
240
|
+
\`\`\`
|
|
241
|
+
`;
|
|
242
|
+
const result = detectCompletionPromise(output, "EXPECTED_PROMISE");
|
|
243
|
+
expect(result.isComplete).toBe(true);
|
|
244
|
+
expect(result.promiseMatched).toBe(false);
|
|
245
|
+
expect(result.promiseValue).toBe("WRONG_PROMISE");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("detects ralph_complete: false as not complete", () => {
|
|
249
|
+
const output = `
|
|
250
|
+
\`\`\`json
|
|
251
|
+
{"ralph_complete": false, "promise": "DONE"}
|
|
252
|
+
\`\`\`
|
|
253
|
+
`;
|
|
254
|
+
const result = detectCompletionPromise(output, "DONE");
|
|
255
|
+
expect(result.isComplete).toBe(false);
|
|
256
|
+
expect(result.promiseMatched).toBe(false);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("returns not complete for output without JSON", () => {
|
|
260
|
+
const result = detectCompletionPromise("Just some regular output", "DONE");
|
|
261
|
+
expect(result.isComplete).toBe(false);
|
|
262
|
+
expect(result.promiseMatched).toBe(false);
|
|
263
|
+
expect(result.promiseValue).toBeNull();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("matches promise case-insensitively", () => {
|
|
267
|
+
const output = '{"ralph_complete": true, "promise": "completed"}';
|
|
268
|
+
const result = detectCompletionPromise(output, "COMPLETED");
|
|
269
|
+
expect(result.isComplete).toBe(true);
|
|
270
|
+
expect(result.promiseMatched).toBe(true);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("handles invalid JSON gracefully", () => {
|
|
274
|
+
const output = `
|
|
275
|
+
\`\`\`json
|
|
276
|
+
{invalid json here}
|
|
277
|
+
\`\`\`
|
|
278
|
+
`;
|
|
279
|
+
const result = detectCompletionPromise(output, "DONE");
|
|
280
|
+
expect(result.isComplete).toBe(false);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it("preserves original output in result", () => {
|
|
284
|
+
const output = "Full output text here";
|
|
285
|
+
const result = detectCompletionPromise(output, "DONE");
|
|
286
|
+
expect(result.output).toBe(output);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("detects failure signal in JSON code block", () => {
|
|
290
|
+
const output = `
|
|
291
|
+
I cannot complete the task due to permission errors.
|
|
292
|
+
|
|
293
|
+
\`\`\`json
|
|
294
|
+
{
|
|
295
|
+
"ralph_failed": true,
|
|
296
|
+
"reason": "Permission denied: cannot write to file"
|
|
297
|
+
}
|
|
298
|
+
\`\`\`
|
|
299
|
+
`;
|
|
300
|
+
const result = detectCompletionPromise(output, "COMPLETED");
|
|
301
|
+
expect(result.isComplete).toBe(true);
|
|
302
|
+
expect(result.isFailed).toBe(true);
|
|
303
|
+
expect(result.promiseMatched).toBe(false);
|
|
304
|
+
expect(result.failureReason).toBe("Permission denied: cannot write to file");
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("detects failure signal in raw JSON", () => {
|
|
308
|
+
const output = 'Cannot proceed. {"ralph_failed": true, "reason": "Access denied"}';
|
|
309
|
+
const result = detectCompletionPromise(output, "DONE");
|
|
310
|
+
expect(result.isComplete).toBe(true);
|
|
311
|
+
expect(result.isFailed).toBe(true);
|
|
312
|
+
expect(result.failureReason).toBe("Access denied");
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("uses default reason for failure without reason field", () => {
|
|
316
|
+
const output = '{"ralph_failed": true}';
|
|
317
|
+
const result = detectCompletionPromise(output, "DONE");
|
|
318
|
+
expect(result.isComplete).toBe(true);
|
|
319
|
+
expect(result.isFailed).toBe(true);
|
|
320
|
+
expect(result.failureReason).toBe("Unknown error");
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("prefers completion over failure when both present", () => {
|
|
324
|
+
const output = `
|
|
325
|
+
\`\`\`json
|
|
326
|
+
{"ralph_complete": true, "promise": "DONE"}
|
|
327
|
+
\`\`\`
|
|
328
|
+
Also has failure:
|
|
329
|
+
\`\`\`json
|
|
330
|
+
{"ralph_failed": true, "reason": "Some error"}
|
|
331
|
+
\`\`\`
|
|
332
|
+
`;
|
|
333
|
+
const result = detectCompletionPromise(output, "DONE");
|
|
334
|
+
expect(result.isComplete).toBe(true);
|
|
335
|
+
expect(result.promiseMatched).toBe(true);
|
|
336
|
+
expect(result.isFailed).toBe(false);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe("buildRalphSystemMessage", () => {
|
|
341
|
+
it("formats system message correctly", () => {
|
|
342
|
+
const message = buildRalphSystemMessage(3, 10, "COMPLETE");
|
|
343
|
+
expect(message).toContain("Iteration 3/10");
|
|
344
|
+
expect(message).toContain("COMPLETE");
|
|
345
|
+
expect(message).toContain("ralph_complete");
|
|
346
|
+
expect(message).toContain("7 iterations remaining");
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("handles first iteration", () => {
|
|
350
|
+
const message = buildRalphSystemMessage(1, 5, "DONE");
|
|
351
|
+
expect(message).toContain("Iteration 1/5");
|
|
352
|
+
expect(message).toContain("4 iterations remaining");
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("handles last iteration", () => {
|
|
356
|
+
const message = buildRalphSystemMessage(10, 10, "FINISHED");
|
|
357
|
+
expect(message).toContain("Iteration 10/10");
|
|
358
|
+
expect(message).toContain("0 iterations remaining");
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("includes failure signal instructions", () => {
|
|
362
|
+
const message = buildRalphSystemMessage(1, 5, "DONE");
|
|
363
|
+
expect(message).toContain("ralph_failed");
|
|
364
|
+
expect(message).toContain("Failure Signal");
|
|
365
|
+
expect(message.toLowerCase()).toContain("permission errors");
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
describe("CompletionResult", () => {
|
|
370
|
+
it("supports default values", () => {
|
|
371
|
+
const result: CompletionResult = {
|
|
372
|
+
isComplete: false,
|
|
373
|
+
promiseMatched: false,
|
|
374
|
+
promiseValue: null,
|
|
375
|
+
output: "test output",
|
|
376
|
+
isFailed: false,
|
|
377
|
+
failureReason: null,
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
expect(result.isComplete).toBe(false);
|
|
381
|
+
expect(result.promiseMatched).toBe(false);
|
|
382
|
+
expect(result.promiseValue).toBeNull();
|
|
383
|
+
expect(result.output).toBe("test output");
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it("supports success state", () => {
|
|
387
|
+
const result: CompletionResult = {
|
|
388
|
+
isComplete: true,
|
|
389
|
+
promiseMatched: true,
|
|
390
|
+
promiseValue: "DONE",
|
|
391
|
+
output: "completed",
|
|
392
|
+
isFailed: false,
|
|
393
|
+
failureReason: null,
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
expect(result.isComplete).toBe(true);
|
|
397
|
+
expect(result.promiseMatched).toBe(true);
|
|
398
|
+
expect(result.promiseValue).toBe("DONE");
|
|
399
|
+
expect(result.isFailed).toBe(false);
|
|
400
|
+
expect(result.failureReason).toBeNull();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it("supports failure state", () => {
|
|
404
|
+
const result: CompletionResult = {
|
|
405
|
+
isComplete: true,
|
|
406
|
+
promiseMatched: false,
|
|
407
|
+
promiseValue: null,
|
|
408
|
+
output: "failed output",
|
|
409
|
+
isFailed: true,
|
|
410
|
+
failureReason: "Permission denied",
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
expect(result.isComplete).toBe(true);
|
|
414
|
+
expect(result.isFailed).toBe(true);
|
|
415
|
+
expect(result.failureReason).toBe("Permission denied");
|
|
416
|
+
expect(result.promiseMatched).toBe(false);
|
|
417
|
+
});
|
|
418
|
+
});
|