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
package/src/executor.ts DELETED
@@ -1,354 +0,0 @@
1
- /** Workflow executor for running workflows. */
2
-
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- import { loadConfig } from "./config.js";
7
- import type { ConsoleOutput } from "./console.js";
8
- import { OutputLevel } from "./console.js";
9
- import { ConsoleOutput as ConsoleOutputClass } from "./console.js";
10
- import type { WorkflowLogger } from "./logging/logger.js";
11
- import { WorkflowLogger as WorkflowLoggerClass } from "./logging/logger.js";
12
- import {
13
- STEP_STATUS,
14
- WORKFLOW_STATUS,
15
- createProgress,
16
- generateWorkflowId,
17
- saveProgress,
18
- updateStepFailed,
19
- updateStepStarted,
20
- } from "./progress.js";
21
- import { TemplateRenderer, buildTemplateContext, renderWorkflowOutput } from "./renderer.js";
22
- import { type StepContext, type StepResult, resolveModel } from "./steps/base.js";
23
- import { ConditionalStepExecutor } from "./steps/conditional-step.js";
24
- import { ParallelStepExecutor } from "./steps/parallel-step.js";
25
- import { PromptStepExecutor } from "./steps/prompt-step.js";
26
- import { RalphLoopStepExecutor } from "./steps/ralph-loop-step.js";
27
- import { SerialStepExecutor } from "./steps/serial-step.js";
28
- import type {
29
- OutputDefinition,
30
- StepDefinition,
31
- StepType,
32
- WorkflowDefinition,
33
- WorkflowProgress,
34
- WorkflowSettings,
35
- } from "./types.js";
36
-
37
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
38
-
39
- export class WorkflowExecutor {
40
- repoRoot: string;
41
- config: Record<string, unknown>;
42
- strictMode: boolean;
43
- renderer: TemplateRenderer;
44
- workflowSettings: WorkflowSettings | null = null;
45
- executors: Record<
46
- string,
47
- | PromptStepExecutor
48
- | ParallelStepExecutor
49
- | SerialStepExecutor
50
- | ConditionalStepExecutor
51
- | RalphLoopStepExecutor
52
- >;
53
-
54
- promptExecutor: PromptStepExecutor;
55
- ralphLoopExecutor: RalphLoopStepExecutor;
56
- parallelExecutor: ParallelStepExecutor;
57
- serialExecutor: SerialStepExecutor;
58
- conditionalExecutor: ConditionalStepExecutor;
59
-
60
- constructor(repoRoot?: string, strictMode = false) {
61
- this.repoRoot = repoRoot ?? process.cwd();
62
- this.config = loadConfig(this.repoRoot);
63
- this.strictMode = strictMode;
64
- this.renderer = new TemplateRenderer(undefined, strictMode);
65
-
66
- // Initialize step executors
67
- this.promptExecutor = new PromptStepExecutor();
68
- this.ralphLoopExecutor = new RalphLoopStepExecutor();
69
-
70
- // Branch executor function for nested steps
71
- const branchExecutors: Record<
72
- string,
73
- {
74
- execute: (
75
- step: StepDefinition,
76
- progress: WorkflowProgress,
77
- context: StepContext,
78
- logger: WorkflowLogger,
79
- console: ConsoleOutput,
80
- ) => Promise<StepResult>;
81
- }
82
- > = {
83
- prompt: this.promptExecutor,
84
- "ralph-loop": this.ralphLoopExecutor,
85
- };
86
-
87
- const branchExecute = async (
88
- step: StepDefinition,
89
- progress: WorkflowProgress,
90
- context: StepContext,
91
- logger: WorkflowLogger,
92
- console: ConsoleOutput,
93
- ): Promise<StepResult> => {
94
- const executor = branchExecutors[step.type] ?? this.executors[step.type];
95
- if (!executor) {
96
- throw new Error(`Step type not yet implemented: ${step.type}`);
97
- }
98
- return executor.execute(step, progress, context, logger, console);
99
- };
100
-
101
- this.parallelExecutor = new ParallelStepExecutor(branchExecute);
102
- this.serialExecutor = new SerialStepExecutor(branchExecute);
103
- this.conditionalExecutor = new ConditionalStepExecutor(branchExecute);
104
-
105
- // Add complex executors to branch lookup
106
- branchExecutors.serial = this.serialExecutor;
107
- branchExecutors.conditional = this.conditionalExecutor;
108
-
109
- this.executors = {
110
- prompt: this.promptExecutor,
111
- parallel: this.parallelExecutor,
112
- serial: this.serialExecutor,
113
- conditional: this.conditionalExecutor,
114
- "ralph-loop": this.ralphLoopExecutor,
115
- "wait-for-human": {
116
- execute: async (
117
- step: StepDefinition,
118
- progress: WorkflowProgress,
119
- _context: StepContext,
120
- logger: WorkflowLogger,
121
- ): Promise<StepResult> => {
122
- logger.info(step.name, `Waiting for human input: ${step.message}`);
123
- progress.status = WORKFLOW_STATUS.PAUSED;
124
- progress.currentStep = {
125
- name: step.name,
126
- type: "wait-for-human",
127
- message: step.message,
128
- started_at: new Date().toISOString(),
129
- timeout_minutes: step.stepTimeoutMinutes ?? 5,
130
- on_timeout: step.onTimeout,
131
- };
132
- return { success: true, outputSummary: "Paused for human input" };
133
- },
134
- },
135
- };
136
- }
137
-
138
- async run(
139
- workflow: WorkflowDefinition,
140
- variables?: Record<string, unknown>,
141
- fromStep?: string | null,
142
- terminalOutput = "base",
143
- workflowFile = "",
144
- resumeProgress?: WorkflowProgress | null,
145
- ): Promise<WorkflowProgress> {
146
- let vars = variables ? { ...variables } : {};
147
- this.workflowSettings = workflow.settings;
148
-
149
- // Update renderer with workflow's strict_mode setting
150
- this.renderer = new TemplateRenderer(undefined, workflow.settings.strictMode);
151
-
152
- // Create console output handler
153
- const outputLevel = terminalOutput === "all" ? OutputLevel.ALL : OutputLevel.BASE;
154
- const console = new ConsoleOutputClass(outputLevel);
155
-
156
- let progress: WorkflowProgress;
157
- let workflowId: string;
158
-
159
- if (resumeProgress) {
160
- progress = resumeProgress;
161
- // Seed variables from stored progress, then overlay any new CLI vars
162
- const merged = { ...progress.variables };
163
- Object.assign(merged, vars);
164
- vars = merged;
165
- progress.variables = vars;
166
- workflowId = progress.workflowId;
167
- if (!progress.workflowFile && workflowFile) {
168
- progress.workflowFile = workflowFile;
169
- }
170
- } else {
171
- for (const v of workflow.variables) {
172
- if (!(v.name in vars)) {
173
- if (v.required && v.default === undefined) {
174
- throw new Error(`Missing required variable: ${v.name}`);
175
- }
176
- vars[v.name] = v.default;
177
- }
178
- }
179
-
180
- workflowId = generateWorkflowId(workflow.name);
181
- const stepNames = workflow.steps.map((s) => s.name);
182
- progress = createProgress(workflowId, workflow.name, stepNames, vars, workflowFile);
183
- }
184
-
185
- saveProgress(progress, this.repoRoot);
186
-
187
- const logger = new WorkflowLoggerClass(workflowId, this.repoRoot);
188
- logger.info("workflow", `Started workflow: ${workflow.name}`);
189
-
190
- console.workflowStart(workflow.name, workflowId);
191
-
192
- // Build set of completed step names to skip on resume
193
- const completedStepNames = new Set(
194
- progress.completedSteps
195
- .filter((s) => s.status === STEP_STATUS.COMPLETED || s.status === STEP_STATUS.SKIPPED)
196
- .map((s) => s.name),
197
- );
198
-
199
- let skipUntil = fromStep ?? null;
200
-
201
- for (const step of workflow.steps) {
202
- if (skipUntil) {
203
- if (step.name === skipUntil) {
204
- skipUntil = null;
205
- } else {
206
- continue;
207
- }
208
- }
209
-
210
- // Skip steps already completed/skipped during resume
211
- if (completedStepNames.has(step.name)) {
212
- logger.info(step.name, `Skipping already completed step: ${step.name}`);
213
- console.info(`Skipping completed step: ${step.name}`);
214
- continue;
215
- }
216
-
217
- try {
218
- await this.executeStep(step, progress, vars, logger, console);
219
- saveProgress(progress, this.repoRoot);
220
-
221
- if (progress.status === WORKFLOW_STATUS.FAILED) {
222
- break;
223
- }
224
- } catch (e: unknown) {
225
- const errorMsg = e instanceof Error ? e.message : String(e);
226
- logger.error(step.name, `Step failed: ${errorMsg}`);
227
- console.stepFailed(step.name, errorMsg);
228
- updateStepFailed(progress, step.name, errorMsg);
229
- progress.status = WORKFLOW_STATUS.FAILED;
230
- saveProgress(progress, this.repoRoot);
231
- break;
232
- }
233
- }
234
-
235
- if (progress.status === WORKFLOW_STATUS.RUNNING) {
236
- progress.status = WORKFLOW_STATUS.COMPLETED;
237
- }
238
- progress.completedAt = new Date().toISOString();
239
- saveProgress(progress, this.repoRoot);
240
-
241
- this.renderOutputs(workflow, progress, vars, logger);
242
-
243
- console.workflowComplete(workflow.name, progress.status);
244
- logger.info("workflow", `Workflow ${progress.status}: ${workflow.name}`);
245
- return progress;
246
- }
247
-
248
- private renderOutputs(
249
- workflow: WorkflowDefinition,
250
- progress: WorkflowProgress,
251
- variables: Record<string, unknown>,
252
- logger: WorkflowLogger,
253
- ): void {
254
- if (!workflow.outputs || workflow.outputs.length === 0) {
255
- return;
256
- }
257
-
258
- const stepOutputs: Record<string, Record<string, unknown>> = {};
259
- for (const step of progress.completedSteps) {
260
- const entry: Record<string, unknown> = {
261
- status: step.status,
262
- started_at: step.startedAt,
263
- completed_at: step.completedAt,
264
- output_summary: step.outputSummary,
265
- error: step.error,
266
- };
267
- if (step.name in progress.stepOutputs) {
268
- entry.output = progress.stepOutputs[step.name];
269
- }
270
- stepOutputs[step.name] = entry;
271
- }
272
-
273
- const context = buildTemplateContext(
274
- workflow.name,
275
- progress.workflowId,
276
- progress.startedAt ?? "",
277
- progress.completedAt,
278
- stepOutputs,
279
- [],
280
- [],
281
- [],
282
- variables,
283
- );
284
-
285
- const pluginTemplates = path.join(__dirname, "templates");
286
- const templateDirs = [pluginTemplates, this.repoRoot];
287
-
288
- const outputDir = path.join(this.repoRoot, "agentic", "outputs", progress.workflowId);
289
-
290
- for (const output of workflow.outputs) {
291
- if (output.when === "completed" && progress.status !== WORKFLOW_STATUS.COMPLETED) {
292
- continue;
293
- }
294
- if (output.when === "failed" && progress.status !== WORKFLOW_STATUS.FAILED) {
295
- continue;
296
- }
297
-
298
- try {
299
- let outputPathStr = output.path;
300
- if (this.renderer.hasVariables(outputPathStr)) {
301
- outputPathStr = this.renderer.renderString(outputPathStr, {
302
- workflow_id: progress.workflowId,
303
- ...variables,
304
- });
305
- }
306
- const outputPath = path.join(outputDir, outputPathStr);
307
-
308
- renderWorkflowOutput(
309
- output.template,
310
- outputPath,
311
- context,
312
- templateDirs,
313
- workflow.settings.strictMode,
314
- );
315
- logger.info("workflow", `Generated output: ${outputPath}`);
316
- } catch (e: unknown) {
317
- const errorMsg = e instanceof Error ? e.message : String(e);
318
- logger.error("workflow", `Failed to render output '${output.name}': ${errorMsg}`);
319
- }
320
- }
321
- }
322
-
323
- async executeStep(
324
- step: StepDefinition,
325
- progress: WorkflowProgress,
326
- variables: Record<string, unknown>,
327
- logger: WorkflowLogger,
328
- console: ConsoleOutput,
329
- ): Promise<void> {
330
- const context: StepContext = {
331
- repoRoot: this.repoRoot,
332
- config: this.config,
333
- renderer: this.renderer,
334
- workflowSettings: this.workflowSettings,
335
- workflowId: progress.workflowId,
336
- variables,
337
- outputs: progress.stepOutputs,
338
- };
339
-
340
- const resolvedModel = resolveModel(context, step.model);
341
-
342
- logger.info(step.name, `Starting step: ${step.name}`);
343
- console.stepStart(step.name, step.type, resolvedModel);
344
- updateStepStarted(progress, step.name);
345
- saveProgress(progress, this.repoRoot);
346
-
347
- const executor = this.executors[step.type];
348
- if (!executor) {
349
- throw new Error(`Step type not yet implemented: ${step.type}`);
350
- }
351
-
352
- await executor.execute(step, progress, context, logger, console);
353
- }
354
- }
@@ -1,236 +0,0 @@
1
- /** Git worktree management for parallel workflow execution. */
2
-
3
- import { execFileSync } from "node:child_process";
4
- import { randomBytes } from "node:crypto";
5
- import { existsSync, mkdirSync, readdirSync, rmSync, statSync } from "node:fs";
6
- import path from "node:path";
7
- import { getExecutable } from "../runner.js";
8
-
9
- // --- Worktree data ---
10
-
11
- export interface Worktree {
12
- path: string;
13
- branch: string;
14
- baseBranch: string;
15
- }
16
-
17
- // --- Git command helper ---
18
-
19
- export function runGit(
20
- args: string[],
21
- cwd?: string | null,
22
- check = true,
23
- ): { stdout: string; stderr: string; returncode: number } {
24
- const gitPath = getExecutable("git");
25
- const cmd = [gitPath, ...args];
26
- try {
27
- const stdout = execFileSync(gitPath, args, {
28
- encoding: "utf-8",
29
- cwd: cwd ?? undefined,
30
- stdio: ["pipe", "pipe", "pipe"],
31
- });
32
- return { stdout, stderr: "", returncode: 0 };
33
- } catch (e: unknown) {
34
- const err = e as { stdout?: string; stderr?: string; status?: number };
35
- if (check) {
36
- throw new Error(`Git command failed: ${cmd.join(" ")}\n${err.stderr ?? ""}`);
37
- }
38
- return {
39
- stdout: (err.stdout as string) ?? "",
40
- stderr: (err.stderr as string) ?? "",
41
- returncode: (err.status as number) ?? 1,
42
- };
43
- }
44
- }
45
-
46
- // --- Name helpers ---
47
-
48
- function truncate(name: string, maxLen = 30): string {
49
- return name.length > maxLen ? name.slice(0, maxLen) : name;
50
- }
51
-
52
- function generateSuffix(): string {
53
- return randomBytes(3).toString("hex");
54
- }
55
-
56
- function sanitizeName(name: string): string {
57
- return name.replace(/\//g, "-").replace(/ /g, "-").replace(/_/g, "-").toLowerCase();
58
- }
59
-
60
- // --- Repository helpers ---
61
-
62
- export function getRepoRoot(cwd?: string | null): string {
63
- const result = runGit(["rev-parse", "--show-toplevel"], cwd);
64
- return result.stdout.trim();
65
- }
66
-
67
- export function getDefaultBranch(cwd?: string | null): string {
68
- const result = runGit(["symbolic-ref", "refs/remotes/origin/HEAD"], cwd, false);
69
- if (result.returncode === 0) {
70
- const parts = result.stdout.trim().split("/");
71
- return parts[parts.length - 1] ?? "main";
72
- }
73
- for (const branch of ["main", "master"]) {
74
- const check = runGit(["rev-parse", "--verify", branch], cwd, false);
75
- if (check.returncode === 0) {
76
- return branch;
77
- }
78
- }
79
- return "main";
80
- }
81
-
82
- export function getCurrentBranch(cwd?: string | null): string {
83
- const result = runGit(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
84
- return result.stdout.trim();
85
- }
86
-
87
- // --- Worktree operations ---
88
-
89
- export function createWorktree(
90
- workflowName: string,
91
- stepName: string,
92
- baseBranch?: string | null,
93
- repoRoot?: string | null,
94
- ): Worktree {
95
- const root = repoRoot || getRepoRoot();
96
- const base = baseBranch || getDefaultBranch(root);
97
-
98
- const suffix = generateSuffix();
99
- const wfName = truncate(sanitizeName(workflowName));
100
- const stName = truncate(sanitizeName(stepName));
101
-
102
- const dirName = `agentic-${wfName}-${stName}-${suffix}`;
103
- const branchName = `agentic/${wfName}-${stName}-${suffix}`;
104
-
105
- const worktreePath = path.join(root, ".worktrees", dirName);
106
- const parentDir = path.dirname(worktreePath);
107
- mkdirSync(parentDir, { recursive: true });
108
-
109
- if (existsSync(worktreePath)) {
110
- rmSync(worktreePath, { recursive: true, force: true });
111
- runGit(["worktree", "prune"], root, false);
112
- }
113
-
114
- runGit(["worktree", "add", "-b", branchName, worktreePath, base], root);
115
-
116
- return { path: worktreePath, branch: branchName, baseBranch: base };
117
- }
118
-
119
- export function removeWorktree(
120
- worktree: Worktree,
121
- repoRoot?: string | null,
122
- deleteBranch = true,
123
- ): void {
124
- const root = repoRoot || getRepoRoot();
125
-
126
- const result = runGit(["worktree", "remove", "--force", worktree.path], root, false);
127
-
128
- if (result.returncode !== 0 && existsSync(worktree.path)) {
129
- rmSync(worktree.path, { recursive: true, force: true });
130
- runGit(["worktree", "prune"], root, false);
131
- }
132
-
133
- if (deleteBranch && worktree.branch) {
134
- runGit(["branch", "-D", worktree.branch], root, false);
135
- }
136
- }
137
-
138
- export function listWorktrees(repoRoot?: string | null): Worktree[] {
139
- const root = repoRoot || getRepoRoot();
140
-
141
- const result = runGit(["worktree", "list", "--porcelain"], root);
142
-
143
- const worktrees: Worktree[] = [];
144
- let currentPath: string | null = null;
145
- let currentBranch = "";
146
-
147
- for (const line of result.stdout.trim().split("\n")) {
148
- if (line.startsWith("worktree ")) {
149
- currentPath = line.slice(9);
150
- } else if (line.startsWith("branch ")) {
151
- currentBranch = line.replace("branch refs/heads/", "");
152
- } else if (line === "" && currentPath) {
153
- worktrees.push({ path: currentPath, branch: currentBranch, baseBranch: "" });
154
- currentPath = null;
155
- currentBranch = "";
156
- }
157
- }
158
-
159
- if (currentPath) {
160
- worktrees.push({ path: currentPath, branch: currentBranch, baseBranch: "" });
161
- }
162
-
163
- return worktrees;
164
- }
165
-
166
- export function listAgenticWorktrees(repoRoot?: string | null): Worktree[] {
167
- return listWorktrees(repoRoot).filter((wt) => wt.branch.startsWith("agentic/"));
168
- }
169
-
170
- export function pruneOrphaned(repoRoot?: string | null): number {
171
- const root = repoRoot || getRepoRoot();
172
-
173
- runGit(["worktree", "prune"], root, false);
174
-
175
- let cleaned = 0;
176
- const worktreesDir = path.join(root, ".worktrees");
177
- if (existsSync(worktreesDir)) {
178
- for (const entry of readdirSync(worktreesDir)) {
179
- const wtDir = path.join(worktreesDir, entry);
180
- if (statSync(wtDir).isDirectory() && entry.startsWith("agentic-")) {
181
- const gitFile = path.join(wtDir, ".git");
182
- if (!existsSync(gitFile)) {
183
- rmSync(wtDir, { recursive: true, force: true });
184
- cleaned++;
185
- }
186
- }
187
- }
188
- }
189
-
190
- return cleaned;
191
- }
192
-
193
- // --- Branch operations ---
194
-
195
- export function createBranch(
196
- branchName: string,
197
- baseBranch?: string | null,
198
- cwd?: string | null,
199
- ): string {
200
- const base = baseBranch || getDefaultBranch(cwd);
201
- runGit(["checkout", "-b", branchName, base], cwd);
202
- return branchName;
203
- }
204
-
205
- export function checkoutBranch(branchName: string, cwd?: string | null): void {
206
- runGit(["checkout", branchName], cwd);
207
- }
208
-
209
- export function commitChanges(message: string, cwd?: string | null, addAll = true): boolean {
210
- if (addAll) {
211
- runGit(["add", "-A"], cwd);
212
- }
213
-
214
- const result = runGit(["status", "--porcelain"], cwd);
215
- if (!result.stdout.trim()) {
216
- return false;
217
- }
218
-
219
- runGit(["commit", "-m", message], cwd);
220
- return true;
221
- }
222
-
223
- export function pushBranch(
224
- branchName: string,
225
- remote = "origin",
226
- cwd?: string | null,
227
- setUpstream = true,
228
- ): void {
229
- const args = ["push"];
230
- if (setUpstream) {
231
- args.push("-u", remote, branchName);
232
- } else {
233
- args.push(remote, branchName);
234
- }
235
- runGit(args, cwd);
236
- }
@@ -1,95 +0,0 @@
1
- /** NDJSON structured logging for workflows. */
2
-
3
- import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
4
- import path from "node:path";
5
-
6
- // --- Log levels ---
7
-
8
- export const LOG_LEVEL = {
9
- CRITICAL: "Critical",
10
- ERROR: "Error",
11
- WARNING: "Warning",
12
- INFORMATION: "Information",
13
- } as const;
14
-
15
- export type LogLevel = (typeof LOG_LEVEL)[keyof typeof LOG_LEVEL];
16
-
17
- // --- Log entry ---
18
-
19
- export interface LogEntry {
20
- timestamp: string;
21
- level: string;
22
- step: string;
23
- message: string;
24
- context: Record<string, unknown> | null;
25
- }
26
-
27
- // --- Logger class ---
28
-
29
- export class WorkflowLogger {
30
- readonly workflowId: string;
31
- readonly logPath: string;
32
-
33
- constructor(workflowId: string, repoRoot?: string) {
34
- this.workflowId = workflowId;
35
- const root = repoRoot ?? process.cwd();
36
- this.logPath = path.join(root, "agentic", "outputs", workflowId, "logs.ndjson");
37
- mkdirSync(path.dirname(this.logPath), { recursive: true });
38
- }
39
-
40
- log(
41
- level: LogLevel,
42
- step: string,
43
- message: string,
44
- context?: Record<string, unknown> | null,
45
- ): void {
46
- const entry: LogEntry = {
47
- timestamp: new Date().toISOString(),
48
- level,
49
- step,
50
- message,
51
- context: context ?? null,
52
- };
53
- appendFileSync(this.logPath, `${JSON.stringify(entry)}\n`, "utf-8");
54
- }
55
-
56
- critical(step: string, message: string, context?: Record<string, unknown>): void {
57
- this.log(LOG_LEVEL.CRITICAL, step, message, context || null);
58
- }
59
-
60
- error(step: string, message: string, context?: Record<string, unknown>): void {
61
- this.log(LOG_LEVEL.ERROR, step, message, context || null);
62
- }
63
-
64
- warning(step: string, message: string, context?: Record<string, unknown>): void {
65
- this.log(LOG_LEVEL.WARNING, step, message, context || null);
66
- }
67
-
68
- info(step: string, message: string, context?: Record<string, unknown>): void {
69
- this.log(LOG_LEVEL.INFORMATION, step, message, context || null);
70
- }
71
- }
72
-
73
- // --- Utility functions ---
74
-
75
- export function readLogs(workflowId: string, repoRoot?: string): LogEntry[] {
76
- const logPath = getLogPath(workflowId, repoRoot);
77
- if (!existsSync(logPath)) {
78
- return [];
79
- }
80
-
81
- const content = readFileSync(logPath, "utf-8");
82
- const entries: LogEntry[] = [];
83
- for (const line of content.split("\n")) {
84
- const trimmed = line.trim();
85
- if (trimmed) {
86
- entries.push(JSON.parse(trimmed) as LogEntry);
87
- }
88
- }
89
- return entries;
90
- }
91
-
92
- export function getLogPath(workflowId: string, repoRoot?: string): string {
93
- const root = repoRoot ?? process.cwd();
94
- return path.join(root, "agentic", "outputs", workflowId, "logs.ndjson");
95
- }