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,411 @@
|
|
|
1
|
+
/** Tests for git worktree module and parallel step executor. */
|
|
2
|
+
|
|
3
|
+
import { mkdirSync, mkdtempSync, writeFileSync } 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 type { BranchStepExecutor, StepContext } from "../src/steps/base.js";
|
|
14
|
+
import type { StepDefinition, WorkflowProgress, WorkflowSettings } from "../src/types.js";
|
|
15
|
+
|
|
16
|
+
// Mock the worktree module for parallel step tests
|
|
17
|
+
const mockCreateWorktree = vi.fn();
|
|
18
|
+
const mockRemoveWorktree = vi.fn();
|
|
19
|
+
const mockRunGit = vi.fn();
|
|
20
|
+
|
|
21
|
+
vi.mock("../src/git/worktree.js", async (importOriginal) => {
|
|
22
|
+
const original = await importOriginal<typeof import("../src/git/worktree.js")>();
|
|
23
|
+
return {
|
|
24
|
+
...original,
|
|
25
|
+
createWorktree: (...args: unknown[]) => mockCreateWorktree(...args),
|
|
26
|
+
removeWorktree: (...args: unknown[]) => mockRemoveWorktree(...args),
|
|
27
|
+
runGit: (...args: unknown[]) => mockRunGit(...args),
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Import ParallelStepExecutor after mock setup
|
|
32
|
+
const { ParallelStepExecutor } = await import("../src/steps/parallel-step.js");
|
|
33
|
+
|
|
34
|
+
// --- Helpers ---
|
|
35
|
+
|
|
36
|
+
function createMockStream(): Writable {
|
|
37
|
+
return new Writable({
|
|
38
|
+
write(_chunk, _encoding, cb) {
|
|
39
|
+
cb();
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function makeStepDef(
|
|
45
|
+
overrides: Partial<StepDefinition> & { name: string; type: StepDefinition["type"] },
|
|
46
|
+
): StepDefinition {
|
|
47
|
+
return {
|
|
48
|
+
steps: [],
|
|
49
|
+
mergeStrategy: "wait-all",
|
|
50
|
+
mergeMode: "merge",
|
|
51
|
+
thenSteps: [],
|
|
52
|
+
elseSteps: [],
|
|
53
|
+
maxIterations: 5,
|
|
54
|
+
pollingInterval: 30,
|
|
55
|
+
onTimeout: "fail",
|
|
56
|
+
onError: "fail",
|
|
57
|
+
checkpoint: false,
|
|
58
|
+
...overrides,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function defaultSettings(): WorkflowSettings {
|
|
63
|
+
return {
|
|
64
|
+
maxRetry: 3,
|
|
65
|
+
timeoutMinutes: 60,
|
|
66
|
+
trackProgress: true,
|
|
67
|
+
autofix: "off",
|
|
68
|
+
terminalOutput: "base",
|
|
69
|
+
bypassPermissions: false,
|
|
70
|
+
strictMode: false,
|
|
71
|
+
model: null,
|
|
72
|
+
requiredTools: [],
|
|
73
|
+
git: {
|
|
74
|
+
enabled: false,
|
|
75
|
+
worktree: false,
|
|
76
|
+
autoCommit: false,
|
|
77
|
+
autoPr: false,
|
|
78
|
+
branchPrefix: "agentic",
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let tempDir: string;
|
|
84
|
+
let mockLogger: WorkflowLogger;
|
|
85
|
+
|
|
86
|
+
beforeEach(() => {
|
|
87
|
+
vi.clearAllMocks();
|
|
88
|
+
tempDir = mkdtempSync(path.join(os.tmpdir(), "worktree-test-"));
|
|
89
|
+
const logDir = path.join(tempDir, "agentic", "outputs", "test-workflow-id");
|
|
90
|
+
mkdirSync(logDir, { recursive: true });
|
|
91
|
+
mockLogger = new WorkflowLogger("test-workflow-id", tempDir);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
function createStepContext(overrides?: Partial<StepContext>): StepContext {
|
|
95
|
+
return {
|
|
96
|
+
repoRoot: tempDir,
|
|
97
|
+
config: { defaults: { model: "sonnet", maxRetry: 3, timeoutMinutes: 60 } },
|
|
98
|
+
renderer: new TemplateRenderer(),
|
|
99
|
+
workflowSettings: defaultSettings(),
|
|
100
|
+
workflowId: "test-workflow-id",
|
|
101
|
+
variables: { test_var: "test_value" },
|
|
102
|
+
outputs: {},
|
|
103
|
+
...overrides,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function createWorkflowProgress(): WorkflowProgress {
|
|
108
|
+
return createProgress("test-workflow-id", "test-workflow", ["step1", "step2"], {});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// --- Worktree name helper tests ---
|
|
112
|
+
|
|
113
|
+
describe("worktree name sanitization", () => {
|
|
114
|
+
// Test the sanitization logic indirectly through createWorktree mock behavior
|
|
115
|
+
// The actual sanitization is internal, so we test the exported functions
|
|
116
|
+
|
|
117
|
+
it("should export createWorktree function", async () => {
|
|
118
|
+
const mod = await import("../src/git/worktree.js");
|
|
119
|
+
expect(mod.createWorktree).toBeDefined();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should export removeWorktree function", async () => {
|
|
123
|
+
const mod = await import("../src/git/worktree.js");
|
|
124
|
+
expect(mod.removeWorktree).toBeDefined();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should export listWorktrees function", async () => {
|
|
128
|
+
const mod = await import("../src/git/worktree.js");
|
|
129
|
+
expect(mod.listWorktrees).toBeDefined();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should export listAgenticWorktrees function", async () => {
|
|
133
|
+
const mod = await import("../src/git/worktree.js");
|
|
134
|
+
expect(mod.listAgenticWorktrees).toBeDefined();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should export pruneOrphaned function", async () => {
|
|
138
|
+
const mod = await import("../src/git/worktree.js");
|
|
139
|
+
expect(mod.pruneOrphaned).toBeDefined();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("should export branch operation functions", async () => {
|
|
143
|
+
const mod = await import("../src/git/worktree.js");
|
|
144
|
+
expect(mod.createBranch).toBeDefined();
|
|
145
|
+
expect(mod.checkoutBranch).toBeDefined();
|
|
146
|
+
expect(mod.commitChanges).toBeDefined();
|
|
147
|
+
expect(mod.pushBranch).toBeDefined();
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// --- ParallelStepExecutor tests ---
|
|
152
|
+
|
|
153
|
+
describe("ParallelStepExecutor", () => {
|
|
154
|
+
it("should be instantiable", () => {
|
|
155
|
+
const branchExecutor: BranchStepExecutor = vi.fn();
|
|
156
|
+
const executor = new ParallelStepExecutor(branchExecutor);
|
|
157
|
+
expect(executor).toBeDefined();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should handle empty steps", async () => {
|
|
161
|
+
const branchExecutor: BranchStepExecutor = vi.fn();
|
|
162
|
+
const executor = new ParallelStepExecutor(branchExecutor);
|
|
163
|
+
|
|
164
|
+
const step = makeStepDef({
|
|
165
|
+
name: "parallel-step",
|
|
166
|
+
type: "parallel",
|
|
167
|
+
steps: [],
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const context = createStepContext();
|
|
171
|
+
const progress = createWorkflowProgress();
|
|
172
|
+
const consoleOut = new ConsoleOutput(createMockStream());
|
|
173
|
+
|
|
174
|
+
const result = await executor.execute(step, progress, context, mockLogger, consoleOut);
|
|
175
|
+
|
|
176
|
+
expect(result.success).toBe(true);
|
|
177
|
+
expect(result.outputSummary).toContain("No sub-steps");
|
|
178
|
+
expect(branchExecutor).not.toHaveBeenCalled();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should execute branches in parallel without worktrees", async () => {
|
|
182
|
+
const branchExecutor = vi.fn<BranchStepExecutor>().mockResolvedValue({
|
|
183
|
+
success: true,
|
|
184
|
+
outputSummary: "Done",
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const executor = new ParallelStepExecutor(branchExecutor);
|
|
188
|
+
const innerSteps = [
|
|
189
|
+
makeStepDef({ name: "branch-a", type: "prompt", prompt: "Task A" }),
|
|
190
|
+
makeStepDef({ name: "branch-b", type: "prompt", prompt: "Task B" }),
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
const step = makeStepDef({
|
|
194
|
+
name: "parallel-step",
|
|
195
|
+
type: "parallel",
|
|
196
|
+
steps: innerSteps,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const context = createStepContext();
|
|
200
|
+
const progress = createWorkflowProgress();
|
|
201
|
+
const consoleOut = new ConsoleOutput(createMockStream());
|
|
202
|
+
|
|
203
|
+
const result = await executor.execute(step, progress, context, mockLogger, consoleOut);
|
|
204
|
+
|
|
205
|
+
expect(result.success).toBe(true);
|
|
206
|
+
expect(branchExecutor).toHaveBeenCalledTimes(2);
|
|
207
|
+
expect(result.outputSummary).toContain("2/2");
|
|
208
|
+
expect(mockCreateWorktree).not.toHaveBeenCalled();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("should execute branches with worktree isolation", async () => {
|
|
212
|
+
const branchExecutor = vi.fn<BranchStepExecutor>().mockResolvedValue({
|
|
213
|
+
success: true,
|
|
214
|
+
outputSummary: "Done",
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
mockCreateWorktree.mockReturnValue({
|
|
218
|
+
path: "/tmp/worktree-a",
|
|
219
|
+
branch: "agentic/test-branch-a",
|
|
220
|
+
baseBranch: "main",
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
mockRunGit.mockReturnValue({ stdout: "", stderr: "", returncode: 0 });
|
|
224
|
+
|
|
225
|
+
const executor = new ParallelStepExecutor(branchExecutor);
|
|
226
|
+
const innerSteps = [
|
|
227
|
+
makeStepDef({ name: "branch-a", type: "prompt", prompt: "Task A" }),
|
|
228
|
+
makeStepDef({ name: "branch-b", type: "prompt", prompt: "Task B" }),
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
const step = makeStepDef({
|
|
232
|
+
name: "parallel-step",
|
|
233
|
+
type: "parallel",
|
|
234
|
+
steps: innerSteps,
|
|
235
|
+
git: { worktree: true, autoPr: false, branchPrefix: "agentic" },
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const context = createStepContext();
|
|
239
|
+
const progress = createWorkflowProgress();
|
|
240
|
+
const consoleOut = new ConsoleOutput(createMockStream());
|
|
241
|
+
|
|
242
|
+
const result = await executor.execute(step, progress, context, mockLogger, consoleOut);
|
|
243
|
+
|
|
244
|
+
expect(result.success).toBe(true);
|
|
245
|
+
expect(mockCreateWorktree).toHaveBeenCalledTimes(2);
|
|
246
|
+
// Merge mode is "merge" by default, so branches get merged and cleaned up
|
|
247
|
+
expect(mockRunGit).toHaveBeenCalled();
|
|
248
|
+
expect(mockRemoveWorktree).toHaveBeenCalled();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("should handle branch failures with wait-all strategy", async () => {
|
|
252
|
+
const branchExecutor = vi
|
|
253
|
+
.fn<BranchStepExecutor>()
|
|
254
|
+
.mockResolvedValueOnce({ success: true, outputSummary: "Done" })
|
|
255
|
+
.mockResolvedValueOnce({ success: false, error: "Branch B failed" });
|
|
256
|
+
|
|
257
|
+
const executor = new ParallelStepExecutor(branchExecutor);
|
|
258
|
+
const innerSteps = [
|
|
259
|
+
makeStepDef({ name: "branch-a", type: "prompt", prompt: "Task A" }),
|
|
260
|
+
makeStepDef({ name: "branch-b", type: "prompt", prompt: "Task B" }),
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
const step = makeStepDef({
|
|
264
|
+
name: "parallel-step",
|
|
265
|
+
type: "parallel",
|
|
266
|
+
steps: innerSteps,
|
|
267
|
+
mergeStrategy: "wait-all",
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const context = createStepContext();
|
|
271
|
+
const progress = createWorkflowProgress();
|
|
272
|
+
const consoleOut = new ConsoleOutput(createMockStream());
|
|
273
|
+
|
|
274
|
+
const result = await executor.execute(step, progress, context, mockLogger, consoleOut);
|
|
275
|
+
|
|
276
|
+
expect(result.success).toBe(false);
|
|
277
|
+
expect(result.error).toContain("branch-b");
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("should tolerate failures in independent merge mode", async () => {
|
|
281
|
+
const branchExecutor = vi
|
|
282
|
+
.fn<BranchStepExecutor>()
|
|
283
|
+
.mockResolvedValueOnce({ success: true, outputSummary: "Done" })
|
|
284
|
+
.mockResolvedValueOnce({ success: false, error: "Failed" });
|
|
285
|
+
|
|
286
|
+
const executor = new ParallelStepExecutor(branchExecutor);
|
|
287
|
+
const innerSteps = [
|
|
288
|
+
makeStepDef({ name: "branch-a", type: "prompt", prompt: "Task A" }),
|
|
289
|
+
makeStepDef({ name: "branch-b", type: "prompt", prompt: "Task B" }),
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
const step = makeStepDef({
|
|
293
|
+
name: "parallel-step",
|
|
294
|
+
type: "parallel",
|
|
295
|
+
steps: innerSteps,
|
|
296
|
+
mergeStrategy: "wait-all",
|
|
297
|
+
mergeMode: "independent",
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const context = createStepContext();
|
|
301
|
+
const progress = createWorkflowProgress();
|
|
302
|
+
const consoleOut = new ConsoleOutput(createMockStream());
|
|
303
|
+
|
|
304
|
+
const result = await executor.execute(step, progress, context, mockLogger, consoleOut);
|
|
305
|
+
|
|
306
|
+
// Independent mode tolerates failures in wait-all
|
|
307
|
+
expect(result.success).toBe(true);
|
|
308
|
+
expect(result.outputSummary).toContain("1/2");
|
|
309
|
+
expect(result.outputSummary).toContain("failed");
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("should handle branch exceptions gracefully", async () => {
|
|
313
|
+
const branchExecutor = vi
|
|
314
|
+
.fn<BranchStepExecutor>()
|
|
315
|
+
.mockResolvedValueOnce({ success: true, outputSummary: "Done" })
|
|
316
|
+
.mockRejectedValueOnce(new Error("Unexpected error"));
|
|
317
|
+
|
|
318
|
+
const executor = new ParallelStepExecutor(branchExecutor);
|
|
319
|
+
const innerSteps = [
|
|
320
|
+
makeStepDef({ name: "branch-a", type: "prompt", prompt: "Task A" }),
|
|
321
|
+
makeStepDef({ name: "branch-b", type: "prompt", prompt: "Task B" }),
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
const step = makeStepDef({
|
|
325
|
+
name: "parallel-step",
|
|
326
|
+
type: "parallel",
|
|
327
|
+
steps: innerSteps,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const context = createStepContext();
|
|
331
|
+
const progress = createWorkflowProgress();
|
|
332
|
+
const consoleOut = new ConsoleOutput(createMockStream());
|
|
333
|
+
|
|
334
|
+
const result = await executor.execute(step, progress, context, mockLogger, consoleOut);
|
|
335
|
+
|
|
336
|
+
// Exception in branch-b should be caught and handled
|
|
337
|
+
expect(result.success).toBe(false);
|
|
338
|
+
expect(result.error).toContain("branch-b");
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("should pass cwdOverride to branch context when using worktrees", async () => {
|
|
342
|
+
let capturedContext: StepContext | null = null;
|
|
343
|
+
const branchExecutor = vi
|
|
344
|
+
.fn<BranchStepExecutor>()
|
|
345
|
+
.mockImplementation(async (_step, _progress, ctx) => {
|
|
346
|
+
capturedContext = ctx;
|
|
347
|
+
return { success: true, outputSummary: "Done" };
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
mockCreateWorktree.mockReturnValue({
|
|
351
|
+
path: "/tmp/test-worktree",
|
|
352
|
+
branch: "agentic/test-branch",
|
|
353
|
+
baseBranch: "main",
|
|
354
|
+
});
|
|
355
|
+
mockRunGit.mockReturnValue({ stdout: "", stderr: "", returncode: 0 });
|
|
356
|
+
|
|
357
|
+
const executor = new ParallelStepExecutor(branchExecutor);
|
|
358
|
+
const innerSteps = [makeStepDef({ name: "branch-a", type: "prompt", prompt: "Task A" })];
|
|
359
|
+
|
|
360
|
+
const step = makeStepDef({
|
|
361
|
+
name: "parallel-step",
|
|
362
|
+
type: "parallel",
|
|
363
|
+
steps: innerSteps,
|
|
364
|
+
git: { worktree: true, autoPr: false, branchPrefix: "agentic" },
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const context = createStepContext();
|
|
368
|
+
const progress = createWorkflowProgress();
|
|
369
|
+
const consoleOut = new ConsoleOutput(createMockStream());
|
|
370
|
+
|
|
371
|
+
await executor.execute(step, progress, context, mockLogger, consoleOut);
|
|
372
|
+
|
|
373
|
+
expect(capturedContext).not.toBeNull();
|
|
374
|
+
expect((capturedContext as StepContext).cwdOverride).toBe("/tmp/test-worktree");
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it("should clone variables for each branch context", async () => {
|
|
378
|
+
const capturedContexts: StepContext[] = [];
|
|
379
|
+
const branchExecutor = vi
|
|
380
|
+
.fn<BranchStepExecutor>()
|
|
381
|
+
.mockImplementation(async (_step, _progress, ctx) => {
|
|
382
|
+
capturedContexts.push(ctx);
|
|
383
|
+
// Mutate variables in this branch
|
|
384
|
+
ctx.variables.modified = true;
|
|
385
|
+
return { success: true, outputSummary: "Done" };
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
const executor = new ParallelStepExecutor(branchExecutor);
|
|
389
|
+
const innerSteps = [
|
|
390
|
+
makeStepDef({ name: "branch-a", type: "prompt", prompt: "Task A" }),
|
|
391
|
+
makeStepDef({ name: "branch-b", type: "prompt", prompt: "Task B" }),
|
|
392
|
+
];
|
|
393
|
+
|
|
394
|
+
const step = makeStepDef({
|
|
395
|
+
name: "parallel-step",
|
|
396
|
+
type: "parallel",
|
|
397
|
+
steps: innerSteps,
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
const context = createStepContext();
|
|
401
|
+
const progress = createWorkflowProgress();
|
|
402
|
+
const consoleOut = new ConsoleOutput(createMockStream());
|
|
403
|
+
|
|
404
|
+
await executor.execute(step, progress, context, mockLogger, consoleOut);
|
|
405
|
+
|
|
406
|
+
// Original context should not be mutated
|
|
407
|
+
expect(context.variables.modified).toBeUndefined();
|
|
408
|
+
// Each branch should get its own variables copy
|
|
409
|
+
expect(capturedContexts).toHaveLength(2);
|
|
410
|
+
});
|
|
411
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"resolveJsonModule": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*.ts"],
|
|
17
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
18
|
+
}
|