agentic-forge 0.0.0 → 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 (209) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +31 -21
  3. package/dist/checkpoints/manager.d.ts +5 -0
  4. package/dist/checkpoints/manager.js +87 -0
  5. package/dist/checkpoints/manager.js.map +1 -0
  6. package/{src → dist}/claude/.claude/skills/analyze/SKILL.md +1 -1
  7. package/{src → dist}/claude/.claude/skills/create-checkpoint/SKILL.md +1 -1
  8. package/{src → dist}/claude/.claude/skills/create-log/SKILL.md +1 -1
  9. package/{src → dist}/claude/.claude/skills/fix-analyze/SKILL.md +1 -1
  10. package/{src → dist}/claude/.claude/skills/git-branch/SKILL.md +1 -1
  11. package/{src → dist}/claude/.claude/skills/git-commit/SKILL.md +1 -1
  12. package/{src → dist}/claude/.claude/skills/git-pr/SKILL.md +1 -1
  13. package/{src → dist}/claude/.claude/skills/sdlc-plan/SKILL.md +1 -1
  14. package/{src → dist}/claude/.claude/skills/sdlc-review/SKILL.md +1 -1
  15. package/{src → dist}/claude/.claude/skills/workflow-builder/SKILL.md +1 -1
  16. package/dist/cli.d.ts +3 -0
  17. package/dist/cli.js +155 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/commands/config-cmd.d.ts +2 -0
  20. package/dist/commands/config-cmd.js +30 -0
  21. package/dist/commands/config-cmd.js.map +1 -0
  22. package/{src/commands/index.ts → dist/commands/index.d.ts} +1 -10
  23. package/dist/commands/index.js +13 -0
  24. package/dist/commands/index.js.map +1 -0
  25. package/dist/commands/init.d.ts +6 -0
  26. package/dist/commands/init.js +83 -0
  27. package/dist/commands/init.js.map +1 -0
  28. package/dist/commands/release-notes.d.ts +5 -0
  29. package/dist/commands/release-notes.js +68 -0
  30. package/dist/commands/release-notes.js.map +1 -0
  31. package/dist/commands/resume.d.ts +5 -0
  32. package/dist/commands/resume.js +79 -0
  33. package/dist/commands/resume.js.map +1 -0
  34. package/dist/commands/run.d.ts +14 -0
  35. package/dist/commands/run.js +193 -0
  36. package/dist/commands/run.js.map +1 -0
  37. package/dist/commands/shortcuts.d.ts +2 -0
  38. package/dist/commands/shortcuts.js +11 -0
  39. package/dist/commands/shortcuts.js.map +1 -0
  40. package/dist/commands/skills-dir.d.ts +2 -0
  41. package/dist/commands/skills-dir.js +9 -0
  42. package/dist/commands/skills-dir.js.map +1 -0
  43. package/dist/commands/status.d.ts +4 -0
  44. package/dist/commands/status.js +99 -0
  45. package/dist/commands/status.js.map +1 -0
  46. package/dist/commands/update.d.ts +4 -0
  47. package/dist/commands/update.js +65 -0
  48. package/dist/commands/update.js.map +1 -0
  49. package/dist/commands/version.d.ts +3 -0
  50. package/dist/commands/version.js +26 -0
  51. package/dist/commands/version.js.map +1 -0
  52. package/dist/commands/workflows.d.ts +4 -0
  53. package/dist/commands/workflows.js +109 -0
  54. package/dist/commands/workflows.js.map +1 -0
  55. package/dist/config.d.ts +8 -0
  56. package/dist/config.js +110 -0
  57. package/dist/config.js.map +1 -0
  58. package/dist/console.d.ts +103 -0
  59. package/dist/console.js +670 -0
  60. package/dist/console.js.map +1 -0
  61. package/dist/executor.d.ts +27 -0
  62. package/dist/executor.js +236 -0
  63. package/dist/executor.js.map +1 -0
  64. package/dist/git/worktree.d.ts +23 -0
  65. package/dist/git/worktree.js +170 -0
  66. package/dist/git/worktree.js.map +1 -0
  67. package/dist/logging/logger.d.ts +27 -0
  68. package/dist/logging/logger.js +69 -0
  69. package/dist/logging/logger.js.map +1 -0
  70. package/dist/orchestrator.d.ts +44 -0
  71. package/dist/orchestrator.js +587 -0
  72. package/dist/orchestrator.js.map +1 -0
  73. package/dist/parser.d.ts +17 -0
  74. package/dist/parser.js +184 -0
  75. package/dist/parser.js.map +1 -0
  76. package/dist/progress.d.ts +29 -0
  77. package/dist/progress.js +275 -0
  78. package/dist/progress.js.map +1 -0
  79. package/dist/ralph-loop.d.ts +26 -0
  80. package/dist/ralph-loop.js +194 -0
  81. package/dist/ralph-loop.js.map +1 -0
  82. package/dist/renderer.d.ts +15 -0
  83. package/dist/renderer.js +123 -0
  84. package/dist/renderer.js.map +1 -0
  85. package/dist/runner.d.ts +84 -0
  86. package/dist/runner.js +529 -0
  87. package/dist/runner.js.map +1 -0
  88. package/dist/signal-manager.d.ts +16 -0
  89. package/dist/signal-manager.js +50 -0
  90. package/dist/signal-manager.js.map +1 -0
  91. package/dist/steps/base.d.ts +28 -0
  92. package/dist/steps/base.js +23 -0
  93. package/dist/steps/base.js.map +1 -0
  94. package/dist/steps/conditional-step.d.ts +12 -0
  95. package/dist/steps/conditional-step.js +106 -0
  96. package/dist/steps/conditional-step.js.map +1 -0
  97. package/{src/steps/index.ts → dist/steps/index.d.ts} +1 -9
  98. package/dist/steps/index.js +8 -0
  99. package/dist/steps/index.js.map +1 -0
  100. package/dist/steps/parallel-step.d.ts +11 -0
  101. package/dist/steps/parallel-step.js +166 -0
  102. package/dist/steps/parallel-step.js.map +1 -0
  103. package/dist/steps/prompt-step.d.ts +8 -0
  104. package/dist/steps/prompt-step.js +94 -0
  105. package/dist/steps/prompt-step.js.map +1 -0
  106. package/dist/steps/ralph-loop-step.d.ts +8 -0
  107. package/dist/steps/ralph-loop-step.js +132 -0
  108. package/dist/steps/ralph-loop-step.js.map +1 -0
  109. package/dist/steps/serial-step.d.ts +10 -0
  110. package/dist/steps/serial-step.js +57 -0
  111. package/dist/steps/serial-step.js.map +1 -0
  112. package/dist/types.d.ts +118 -0
  113. package/dist/types.js +10 -0
  114. package/dist/types.js.map +1 -0
  115. package/package.json +56 -2
  116. package/.gitattributes +0 -24
  117. package/.github/workflows/ci.yml +0 -70
  118. package/.markdownlint-cli2.jsonc +0 -16
  119. package/.prettierignore +0 -3
  120. package/.prettierrc +0 -6
  121. package/.vscode/agentic-forge.code-workspace +0 -26
  122. package/CHANGELOG.md +0 -100
  123. package/CLAUDE.md +0 -158
  124. package/CONTRIBUTING.md +0 -152
  125. package/biome.json +0 -21
  126. package/scripts/copy-assets.js +0 -21
  127. package/src/checkpoints/manager.ts +0 -119
  128. package/src/cli.ts +0 -182
  129. package/src/commands/config-cmd.ts +0 -28
  130. package/src/commands/init.ts +0 -96
  131. package/src/commands/release-notes.ts +0 -85
  132. package/src/commands/resume.ts +0 -103
  133. package/src/commands/run.ts +0 -234
  134. package/src/commands/shortcuts.ts +0 -11
  135. package/src/commands/skills-dir.ts +0 -11
  136. package/src/commands/status.ts +0 -112
  137. package/src/commands/update.ts +0 -64
  138. package/src/commands/version.ts +0 -27
  139. package/src/commands/workflows.ts +0 -129
  140. package/src/config.ts +0 -129
  141. package/src/console.ts +0 -790
  142. package/src/executor.ts +0 -354
  143. package/src/git/worktree.ts +0 -236
  144. package/src/logging/logger.ts +0 -95
  145. package/src/orchestrator.ts +0 -815
  146. package/src/parser.ts +0 -225
  147. package/src/progress.ts +0 -306
  148. package/src/ralph-loop.ts +0 -260
  149. package/src/renderer.ts +0 -164
  150. package/src/runner.ts +0 -634
  151. package/src/signal-manager.ts +0 -55
  152. package/src/steps/base.ts +0 -71
  153. package/src/steps/conditional-step.ts +0 -144
  154. package/src/steps/parallel-step.ts +0 -213
  155. package/src/steps/prompt-step.ts +0 -121
  156. package/src/steps/ralph-loop-step.ts +0 -186
  157. package/src/steps/serial-step.ts +0 -84
  158. package/src/types.ts +0 -141
  159. package/tests/config.test.ts +0 -219
  160. package/tests/console.test.ts +0 -506
  161. package/tests/executor.test.ts +0 -339
  162. package/tests/init.test.ts +0 -86
  163. package/tests/logger.test.ts +0 -110
  164. package/tests/parser.test.ts +0 -290
  165. package/tests/progress.test.ts +0 -345
  166. package/tests/ralph-loop.test.ts +0 -418
  167. package/tests/renderer.test.ts +0 -350
  168. package/tests/runner.test.ts +0 -497
  169. package/tests/setup.test.ts +0 -7
  170. package/tests/signal-manager.test.ts +0 -26
  171. package/tests/steps.test.ts +0 -412
  172. package/tests/worktree.test.ts +0 -411
  173. package/tsconfig.json +0 -18
  174. package/vitest.config.ts +0 -8
  175. /package/{src → dist}/agents/explorer.md +0 -0
  176. /package/{src → dist}/agents/reviewer.md +0 -0
  177. /package/{src → dist}/claude/.claude/skills/analyze/references/bug.md +0 -0
  178. /package/{src → dist}/claude/.claude/skills/analyze/references/debt.md +0 -0
  179. /package/{src → dist}/claude/.claude/skills/analyze/references/doc.md +0 -0
  180. /package/{src → dist}/claude/.claude/skills/analyze/references/security.md +0 -0
  181. /package/{src → dist}/claude/.claude/skills/analyze/references/style.md +0 -0
  182. /package/{src → dist}/claude/.claude/skills/orchestrate/SKILL.md +0 -0
  183. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/bug.md +0 -0
  184. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/chore.md +0 -0
  185. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/feature.md +0 -0
  186. /package/{src → dist}/claude/.claude/skills/workflow-builder/references/REFERENCE.md +0 -0
  187. /package/{src → dist}/claude/.claude/skills/workflow-builder/references/workflow-example.yaml +0 -0
  188. /package/{src → dist}/prompts/agentic-system.md +0 -0
  189. /package/{src → dist}/templates/analysis/bug.md.j2 +0 -0
  190. /package/{src → dist}/templates/analysis/debt.md.j2 +0 -0
  191. /package/{src → dist}/templates/analysis/doc.md.j2 +0 -0
  192. /package/{src → dist}/templates/analysis/security.md.j2 +0 -0
  193. /package/{src → dist}/templates/analysis/style.md.j2 +0 -0
  194. /package/{src → dist}/templates/analysis-summary.md.j2 +0 -0
  195. /package/{src → dist}/templates/checkpoint.md.j2 +0 -0
  196. /package/{src → dist}/templates/implementation-report.md.j2 +0 -0
  197. /package/{src → dist}/templates/memory.md.j2 +0 -0
  198. /package/{src → dist}/templates/plan-bug.md.j2 +0 -0
  199. /package/{src → dist}/templates/plan-chore.md.j2 +0 -0
  200. /package/{src → dist}/templates/plan-feature.md.j2 +0 -0
  201. /package/{src → dist}/templates/progress.json.j2 +0 -0
  202. /package/{src → dist}/templates/ralph-report.md.j2 +0 -0
  203. /package/{src → dist}/workflows/analyze-codebase-merge.yaml +0 -0
  204. /package/{src → dist}/workflows/analyze-codebase.yaml +0 -0
  205. /package/{src → dist}/workflows/analyze-single.yaml +0 -0
  206. /package/{src → dist}/workflows/demo.yaml +0 -0
  207. /package/{src → dist}/workflows/one-shot.yaml +0 -0
  208. /package/{src → dist}/workflows/plan-build-review.yaml +0 -0
  209. /package/{src → dist}/workflows/ralph-loop.yaml +0 -0
@@ -1,412 +0,0 @@
1
- /** Tests for step executors. */
2
-
3
- import { mkdirSync, mkdtempSync } from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import { Writable } from "node:stream";
7
- import { beforeEach, describe, expect, it, vi } from "vitest";
8
-
9
- import { ConsoleOutput } from "../src/console.js";
10
- import { WorkflowLogger } from "../src/logging/logger.js";
11
- import { createProgress } from "../src/progress.js";
12
- import { TemplateRenderer } from "../src/renderer.js";
13
- import {
14
- type BranchStepExecutor,
15
- type StepContext,
16
- StepExecutor,
17
- buildTemplateContext,
18
- resolveModel,
19
- } from "../src/steps/base.js";
20
- import { ConditionalStepExecutor } from "../src/steps/conditional-step.js";
21
- import { SerialStepExecutor } from "../src/steps/serial-step.js";
22
- import type { StepDefinition, WorkflowProgress, WorkflowSettings } from "../src/types.js";
23
-
24
- // Mock runClaude at module level
25
- const mockRunClaude = vi.fn();
26
- vi.mock("../src/runner.js", async (importOriginal) => {
27
- const original = await importOriginal<typeof import("../src/runner.js")>();
28
- return {
29
- ...original,
30
- runClaude: (...args: unknown[]) => mockRunClaude(...args),
31
- };
32
- });
33
-
34
- // Now import PromptStepExecutor AFTER mock is set up
35
- const { PromptStepExecutor } = await import("../src/steps/prompt-step.js");
36
-
37
- // --- Helpers ---
38
-
39
- function createMockStream(): Writable {
40
- return new Writable({
41
- write(_chunk, _encoding, cb) {
42
- cb();
43
- },
44
- });
45
- }
46
-
47
- function makeStepDef(
48
- overrides: Partial<StepDefinition> & { name: string; type: StepDefinition["type"] },
49
- ): StepDefinition {
50
- return {
51
- steps: [],
52
- mergeStrategy: "wait-all",
53
- mergeMode: "merge",
54
- thenSteps: [],
55
- elseSteps: [],
56
- maxIterations: 5,
57
- pollingInterval: 30,
58
- onTimeout: "fail",
59
- onError: "fail",
60
- checkpoint: false,
61
- ...overrides,
62
- };
63
- }
64
-
65
- function defaultSettings(): WorkflowSettings {
66
- return {
67
- maxRetry: 3,
68
- timeoutMinutes: 60,
69
- trackProgress: true,
70
- autofix: "off",
71
- terminalOutput: "base",
72
- bypassPermissions: false,
73
- strictMode: false,
74
- model: null,
75
- requiredTools: [],
76
- git: {
77
- enabled: false,
78
- worktree: false,
79
- autoCommit: false,
80
- autoPr: false,
81
- branchPrefix: "agentic",
82
- },
83
- };
84
- }
85
-
86
- // --- Fixtures ---
87
-
88
- let tempDir: string;
89
- let mockLogger: WorkflowLogger;
90
-
91
- beforeEach(() => {
92
- vi.clearAllMocks();
93
- tempDir = mkdtempSync(path.join(os.tmpdir(), "steps-test-"));
94
- const logDir = path.join(tempDir, "agentic", "outputs", "test-workflow-id");
95
- mkdirSync(logDir, { recursive: true });
96
- mockLogger = new WorkflowLogger("test-workflow-id", tempDir);
97
- });
98
-
99
- function createStepContext(overrides?: Partial<StepContext>): StepContext {
100
- return {
101
- repoRoot: tempDir,
102
- config: {
103
- defaults: {
104
- model: "sonnet",
105
- maxRetry: 3,
106
- timeoutMinutes: 60,
107
- },
108
- },
109
- renderer: new TemplateRenderer(),
110
- workflowSettings: defaultSettings(),
111
- workflowId: "test-workflow-id",
112
- variables: { test_var: "test_value" },
113
- outputs: {},
114
- ...overrides,
115
- };
116
- }
117
-
118
- function createWorkflowProgress(): WorkflowProgress {
119
- return createProgress("test-workflow-id", "test-workflow", ["step1", "step2"], {});
120
- }
121
-
122
- // --- Tests ---
123
-
124
- describe("StepContext", () => {
125
- it("should build template context with workflow_id", () => {
126
- const context = createStepContext({
127
- workflowId: "test-workflow-456",
128
- variables: { task: "feature" },
129
- outputs: { plan: { summary: "test" } },
130
- });
131
-
132
- const templateCtx = buildTemplateContext(context);
133
-
134
- expect(templateCtx.workflow_id).toBe("test-workflow-456");
135
- expect((templateCtx.variables as Record<string, unknown>).task).toBe("feature");
136
- expect(
137
- ((templateCtx.outputs as Record<string, unknown>).plan as Record<string, unknown>).summary,
138
- ).toBe("test");
139
- // Variables should also be spread at top level
140
- expect(templateCtx.task).toBe("feature");
141
- });
142
- });
143
-
144
- describe("resolveModel", () => {
145
- it("should prioritize step model", () => {
146
- const context = createStepContext({
147
- workflowSettings: { ...defaultSettings(), model: "haiku" },
148
- });
149
- expect(resolveModel(context, "opus")).toBe("opus");
150
- });
151
-
152
- it("should use workflow model when step has none", () => {
153
- const context = createStepContext({
154
- workflowSettings: { ...defaultSettings(), model: "haiku" },
155
- });
156
- expect(resolveModel(context, null)).toBe("haiku");
157
- });
158
-
159
- it("should fall back to config default", () => {
160
- const context = createStepContext({
161
- config: { defaults: { model: "opus" } },
162
- workflowSettings: defaultSettings(),
163
- });
164
- expect(resolveModel(context, null)).toBe("opus");
165
- });
166
-
167
- it("should fall back to sonnet when nothing is configured", () => {
168
- const context = createStepContext({
169
- config: { defaults: {} },
170
- workflowSettings: defaultSettings(),
171
- });
172
- expect(resolveModel(context, null)).toBe("sonnet");
173
- });
174
- });
175
-
176
- describe("StepExecutor base", () => {
177
- it("should be an abstract class that subclasses extend", () => {
178
- // Verify that PromptStepExecutor extends StepExecutor
179
- const executor = new PromptStepExecutor();
180
- expect(executor).toBeInstanceOf(StepExecutor);
181
- });
182
- });
183
-
184
- describe("PromptStepExecutor", () => {
185
- it("should be instantiable", () => {
186
- const executor = new PromptStepExecutor();
187
- expect(executor).toBeDefined();
188
- });
189
-
190
- it("should execute a basic prompt", async () => {
191
- mockRunClaude.mockResolvedValue({
192
- success: true,
193
- stdout: "Output",
194
- stderr: "",
195
- sessionOutput: {
196
- isSuccess: true,
197
- context: "Done",
198
- sessionId: "test-session",
199
- },
200
- });
201
-
202
- const step = makeStepDef({
203
- name: "test-prompt",
204
- type: "prompt",
205
- prompt: "Test prompt content",
206
- });
207
-
208
- const context = createStepContext();
209
- const progress = createWorkflowProgress();
210
- const consoleOut = new ConsoleOutput(createMockStream());
211
-
212
- const executor = new PromptStepExecutor();
213
- await executor.execute(step, progress, context, mockLogger, consoleOut);
214
-
215
- expect(mockRunClaude).toHaveBeenCalledOnce();
216
- const callArgs = mockRunClaude.mock.calls[0][0];
217
- expect(callArgs.prompt).toContain("Test prompt content");
218
- });
219
-
220
- it("should execute a prompt with variable interpolation", async () => {
221
- mockRunClaude.mockResolvedValue({
222
- success: true,
223
- stdout: "Output",
224
- stderr: "",
225
- sessionOutput: {
226
- isSuccess: true,
227
- context: "Done",
228
- sessionId: null,
229
- },
230
- });
231
-
232
- const step = makeStepDef({
233
- name: "test-prompt",
234
- type: "prompt",
235
- prompt: "Value is {{ variables.test_var }}",
236
- });
237
-
238
- const context = createStepContext();
239
- const progress = createWorkflowProgress();
240
- const consoleOut = new ConsoleOutput(createMockStream());
241
-
242
- const executor = new PromptStepExecutor();
243
- await executor.execute(step, progress, context, mockLogger, consoleOut);
244
-
245
- const callArgs = mockRunClaude.mock.calls[0][0];
246
- expect(callArgs.prompt).toContain("test_value");
247
- });
248
- });
249
-
250
- describe("ConditionalStepExecutor", () => {
251
- it("should be instantiable", () => {
252
- const branchExecutor: BranchStepExecutor = vi.fn();
253
- const executor = new ConditionalStepExecutor(branchExecutor);
254
- expect(executor).toBeDefined();
255
- });
256
-
257
- it("should evaluate true condition", () => {
258
- const branchExecutor: BranchStepExecutor = vi.fn();
259
- const executor = new ConditionalStepExecutor(branchExecutor);
260
- const context = { variables: { enabled: true } };
261
- expect(executor.evaluateCondition("variables.enabled", context)).toBe(true);
262
- });
263
-
264
- it("should evaluate false condition", () => {
265
- const branchExecutor: BranchStepExecutor = vi.fn();
266
- const executor = new ConditionalStepExecutor(branchExecutor);
267
- const context = { variables: { enabled: false } };
268
- expect(executor.evaluateCondition("variables.enabled", context)).toBe(false);
269
- });
270
-
271
- it("should evaluate string comparison", () => {
272
- const branchExecutor: BranchStepExecutor = vi.fn();
273
- const executor = new ConditionalStepExecutor(branchExecutor);
274
- const context = { variables: { status: "active" } };
275
- expect(executor.evaluateCondition("variables.status == 'active'", context)).toBe(true);
276
- });
277
-
278
- it("should evaluate inequality comparison", () => {
279
- const branchExecutor: BranchStepExecutor = vi.fn();
280
- const executor = new ConditionalStepExecutor(branchExecutor);
281
- const context = { variables: { status: "active" } };
282
- expect(executor.evaluateCondition("variables.status != 'inactive'", context)).toBe(true);
283
- });
284
-
285
- it("should evaluate literal true/false", () => {
286
- const branchExecutor: BranchStepExecutor = vi.fn();
287
- const executor = new ConditionalStepExecutor(branchExecutor);
288
- expect(executor.evaluateCondition("true", {})).toBe(true);
289
- expect(executor.evaluateCondition("false", {})).toBe(false);
290
- expect(executor.evaluateCondition("1", {})).toBe(true);
291
- expect(executor.evaluateCondition("0", {})).toBe(false);
292
- });
293
- });
294
-
295
- describe("SerialStepExecutor", () => {
296
- it("should be instantiable", () => {
297
- const branchExecutor: BranchStepExecutor = vi.fn();
298
- const executor = new SerialStepExecutor(branchExecutor);
299
- expect(executor).toBeDefined();
300
- });
301
-
302
- it("should execute serial steps in order", async () => {
303
- const branchExecutor = vi.fn<BranchStepExecutor>().mockResolvedValue({
304
- success: true,
305
- outputSummary: "Done",
306
- });
307
-
308
- const executor = new SerialStepExecutor(branchExecutor);
309
- const innerSteps = [
310
- makeStepDef({ name: "inner1", type: "prompt", prompt: "Step 1" }),
311
- makeStepDef({ name: "inner2", type: "prompt", prompt: "Step 2" }),
312
- ];
313
-
314
- const step = makeStepDef({
315
- name: "serial-step",
316
- type: "serial",
317
- steps: innerSteps,
318
- });
319
-
320
- const context = createStepContext();
321
- const progress = createWorkflowProgress();
322
- const consoleOut = new ConsoleOutput(createMockStream());
323
-
324
- await executor.execute(step, progress, context, mockLogger, consoleOut);
325
-
326
- expect(branchExecutor).toHaveBeenCalledTimes(2);
327
- });
328
-
329
- it("should stop on first failure", async () => {
330
- const branchExecutor = vi
331
- .fn<BranchStepExecutor>()
332
- .mockResolvedValueOnce({ success: true, outputSummary: "Done" })
333
- .mockResolvedValueOnce({ success: false, error: "Failed" });
334
-
335
- const executor = new SerialStepExecutor(branchExecutor);
336
- const innerSteps = [
337
- makeStepDef({ name: "inner1", type: "prompt", prompt: "Step 1" }),
338
- makeStepDef({ name: "inner2", type: "prompt", prompt: "Step 2" }),
339
- makeStepDef({ name: "inner3", type: "prompt", prompt: "Step 3" }),
340
- ];
341
-
342
- const step = makeStepDef({
343
- name: "serial-step",
344
- type: "serial",
345
- steps: innerSteps,
346
- });
347
-
348
- const context = createStepContext();
349
- const progress = createWorkflowProgress();
350
- const consoleOut = new ConsoleOutput(createMockStream());
351
-
352
- const result = await executor.execute(step, progress, context, mockLogger, consoleOut);
353
-
354
- expect(result.success).toBe(false);
355
- expect(branchExecutor).toHaveBeenCalledTimes(2);
356
- });
357
-
358
- it("should handle empty steps", async () => {
359
- const branchExecutor: BranchStepExecutor = vi.fn();
360
- const executor = new SerialStepExecutor(branchExecutor);
361
-
362
- const step = makeStepDef({
363
- name: "serial-step",
364
- type: "serial",
365
- steps: [],
366
- });
367
-
368
- const context = createStepContext();
369
- const progress = createWorkflowProgress();
370
- const consoleOut = new ConsoleOutput(createMockStream());
371
-
372
- const result = await executor.execute(step, progress, context, mockLogger, consoleOut);
373
-
374
- expect(result.success).toBe(true);
375
- expect(result.outputSummary).toContain("No sub-steps");
376
- });
377
- });
378
-
379
- describe("StepExecutor retry", () => {
380
- it("should retry on failure then succeed", async () => {
381
- mockRunClaude
382
- .mockResolvedValueOnce({
383
- success: false,
384
- returncode: 1,
385
- stdout: "",
386
- stderr: "Error",
387
- sessionOutput: { isSuccess: false, context: "Failed" },
388
- })
389
- .mockResolvedValueOnce({
390
- success: true,
391
- stdout: "Output",
392
- stderr: "",
393
- sessionOutput: { isSuccess: true, context: "Done", sessionId: null },
394
- });
395
-
396
- const step = makeStepDef({
397
- name: "test-prompt",
398
- type: "prompt",
399
- prompt: "Test",
400
- stepMaxRetry: 2,
401
- });
402
-
403
- const context = createStepContext();
404
- const progress = createWorkflowProgress();
405
- const consoleOut = new ConsoleOutput(createMockStream());
406
-
407
- const executor = new PromptStepExecutor();
408
- await executor.execute(step, progress, context, mockLogger, consoleOut);
409
-
410
- expect(mockRunClaude).toHaveBeenCalledTimes(2);
411
- });
412
- });