agentic-forge 0.0.0 → 0.7.1

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 (212) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +64 -24
  3. package/{src/claude → dist/authoring}/.claude/skills/workflow-builder/SKILL.md +2 -2
  4. package/{src/claude → dist/authoring}/.claude/skills/workflow-builder/references/REFERENCE.md +9 -3
  5. package/{src/claude → dist/authoring}/.claude/skills/workflow-builder/references/workflow-example.yaml +15 -8
  6. package/dist/checkpoints/manager.d.ts +5 -0
  7. package/dist/checkpoints/manager.js +87 -0
  8. package/dist/checkpoints/manager.js.map +1 -0
  9. package/{src → dist}/claude/.claude/skills/analyze/SKILL.md +1 -1
  10. package/{src → dist}/claude/.claude/skills/create-checkpoint/SKILL.md +1 -1
  11. package/{src → dist}/claude/.claude/skills/create-log/SKILL.md +1 -1
  12. package/{src → dist}/claude/.claude/skills/fix-analyze/SKILL.md +1 -1
  13. package/{src → dist}/claude/.claude/skills/git-branch/SKILL.md +1 -1
  14. package/{src → dist}/claude/.claude/skills/git-commit/SKILL.md +1 -1
  15. package/{src → dist}/claude/.claude/skills/git-pr/SKILL.md +1 -1
  16. package/{src → dist}/claude/.claude/skills/sdlc-plan/SKILL.md +1 -1
  17. package/{src → dist}/claude/.claude/skills/sdlc-review/SKILL.md +1 -1
  18. package/dist/cli.d.ts +3 -0
  19. package/dist/cli.js +173 -0
  20. package/dist/cli.js.map +1 -0
  21. package/dist/commands/authoring-dir.d.ts +2 -0
  22. package/dist/commands/authoring-dir.js +9 -0
  23. package/dist/commands/authoring-dir.js.map +1 -0
  24. package/dist/commands/config-cmd.d.ts +2 -0
  25. package/dist/commands/config-cmd.js +30 -0
  26. package/dist/commands/config-cmd.js.map +1 -0
  27. package/{src/commands/index.ts → dist/commands/index.d.ts} +2 -10
  28. package/dist/commands/index.js +14 -0
  29. package/dist/commands/index.js.map +1 -0
  30. package/dist/commands/init.d.ts +6 -0
  31. package/dist/commands/init.js +83 -0
  32. package/dist/commands/init.js.map +1 -0
  33. package/dist/commands/release-notes.d.ts +5 -0
  34. package/dist/commands/release-notes.js +68 -0
  35. package/dist/commands/release-notes.js.map +1 -0
  36. package/dist/commands/resume.d.ts +5 -0
  37. package/dist/commands/resume.js +79 -0
  38. package/dist/commands/resume.js.map +1 -0
  39. package/dist/commands/run.d.ts +27 -0
  40. package/dist/commands/run.js +243 -0
  41. package/dist/commands/run.js.map +1 -0
  42. package/dist/commands/shortcuts.d.ts +2 -0
  43. package/dist/commands/shortcuts.js +11 -0
  44. package/dist/commands/shortcuts.js.map +1 -0
  45. package/dist/commands/skills-dir.d.ts +2 -0
  46. package/dist/commands/skills-dir.js +9 -0
  47. package/dist/commands/skills-dir.js.map +1 -0
  48. package/dist/commands/status.d.ts +4 -0
  49. package/dist/commands/status.js +99 -0
  50. package/dist/commands/status.js.map +1 -0
  51. package/dist/commands/update.d.ts +4 -0
  52. package/dist/commands/update.js +65 -0
  53. package/dist/commands/update.js.map +1 -0
  54. package/dist/commands/version.d.ts +3 -0
  55. package/dist/commands/version.js +26 -0
  56. package/dist/commands/version.js.map +1 -0
  57. package/dist/commands/workflows.d.ts +4 -0
  58. package/dist/commands/workflows.js +111 -0
  59. package/dist/commands/workflows.js.map +1 -0
  60. package/dist/config.d.ts +8 -0
  61. package/dist/config.js +110 -0
  62. package/dist/config.js.map +1 -0
  63. package/dist/console.d.ts +103 -0
  64. package/dist/console.js +670 -0
  65. package/dist/console.js.map +1 -0
  66. package/dist/executor.d.ts +27 -0
  67. package/dist/executor.js +236 -0
  68. package/dist/executor.js.map +1 -0
  69. package/dist/git/worktree.d.ts +23 -0
  70. package/dist/git/worktree.js +170 -0
  71. package/dist/git/worktree.js.map +1 -0
  72. package/dist/logging/logger.d.ts +27 -0
  73. package/dist/logging/logger.js +69 -0
  74. package/dist/logging/logger.js.map +1 -0
  75. package/dist/orchestrator.d.ts +44 -0
  76. package/dist/orchestrator.js +587 -0
  77. package/dist/orchestrator.js.map +1 -0
  78. package/dist/parser.d.ts +17 -0
  79. package/dist/parser.js +184 -0
  80. package/dist/parser.js.map +1 -0
  81. package/dist/progress.d.ts +29 -0
  82. package/dist/progress.js +275 -0
  83. package/dist/progress.js.map +1 -0
  84. package/dist/ralph-loop.d.ts +26 -0
  85. package/dist/ralph-loop.js +194 -0
  86. package/dist/ralph-loop.js.map +1 -0
  87. package/dist/renderer.d.ts +15 -0
  88. package/dist/renderer.js +123 -0
  89. package/dist/renderer.js.map +1 -0
  90. package/dist/runner.d.ts +84 -0
  91. package/dist/runner.js +529 -0
  92. package/dist/runner.js.map +1 -0
  93. package/dist/signal-manager.d.ts +16 -0
  94. package/dist/signal-manager.js +50 -0
  95. package/dist/signal-manager.js.map +1 -0
  96. package/dist/steps/base.d.ts +28 -0
  97. package/dist/steps/base.js +23 -0
  98. package/dist/steps/base.js.map +1 -0
  99. package/dist/steps/conditional-step.d.ts +12 -0
  100. package/dist/steps/conditional-step.js +106 -0
  101. package/dist/steps/conditional-step.js.map +1 -0
  102. package/{src/steps/index.ts → dist/steps/index.d.ts} +1 -9
  103. package/dist/steps/index.js +8 -0
  104. package/dist/steps/index.js.map +1 -0
  105. package/dist/steps/parallel-step.d.ts +11 -0
  106. package/dist/steps/parallel-step.js +166 -0
  107. package/dist/steps/parallel-step.js.map +1 -0
  108. package/dist/steps/prompt-step.d.ts +8 -0
  109. package/dist/steps/prompt-step.js +94 -0
  110. package/dist/steps/prompt-step.js.map +1 -0
  111. package/dist/steps/ralph-loop-step.d.ts +8 -0
  112. package/dist/steps/ralph-loop-step.js +132 -0
  113. package/dist/steps/ralph-loop-step.js.map +1 -0
  114. package/dist/steps/serial-step.d.ts +10 -0
  115. package/dist/steps/serial-step.js +57 -0
  116. package/dist/steps/serial-step.js.map +1 -0
  117. package/dist/types.d.ts +118 -0
  118. package/dist/types.js +10 -0
  119. package/dist/types.js.map +1 -0
  120. package/package.json +59 -2
  121. package/.gitattributes +0 -24
  122. package/.github/workflows/ci.yml +0 -70
  123. package/.markdownlint-cli2.jsonc +0 -16
  124. package/.prettierignore +0 -3
  125. package/.prettierrc +0 -6
  126. package/.vscode/agentic-forge.code-workspace +0 -26
  127. package/CHANGELOG.md +0 -100
  128. package/CLAUDE.md +0 -158
  129. package/CONTRIBUTING.md +0 -152
  130. package/biome.json +0 -21
  131. package/scripts/copy-assets.js +0 -21
  132. package/src/checkpoints/manager.ts +0 -119
  133. package/src/cli.ts +0 -182
  134. package/src/commands/config-cmd.ts +0 -28
  135. package/src/commands/init.ts +0 -96
  136. package/src/commands/release-notes.ts +0 -85
  137. package/src/commands/resume.ts +0 -103
  138. package/src/commands/run.ts +0 -234
  139. package/src/commands/shortcuts.ts +0 -11
  140. package/src/commands/skills-dir.ts +0 -11
  141. package/src/commands/status.ts +0 -112
  142. package/src/commands/update.ts +0 -64
  143. package/src/commands/version.ts +0 -27
  144. package/src/commands/workflows.ts +0 -129
  145. package/src/config.ts +0 -129
  146. package/src/console.ts +0 -790
  147. package/src/executor.ts +0 -354
  148. package/src/git/worktree.ts +0 -236
  149. package/src/logging/logger.ts +0 -95
  150. package/src/orchestrator.ts +0 -815
  151. package/src/parser.ts +0 -225
  152. package/src/progress.ts +0 -306
  153. package/src/ralph-loop.ts +0 -260
  154. package/src/renderer.ts +0 -164
  155. package/src/runner.ts +0 -634
  156. package/src/signal-manager.ts +0 -55
  157. package/src/steps/base.ts +0 -71
  158. package/src/steps/conditional-step.ts +0 -144
  159. package/src/steps/parallel-step.ts +0 -213
  160. package/src/steps/prompt-step.ts +0 -121
  161. package/src/steps/ralph-loop-step.ts +0 -186
  162. package/src/steps/serial-step.ts +0 -84
  163. package/src/types.ts +0 -141
  164. package/tests/config.test.ts +0 -219
  165. package/tests/console.test.ts +0 -506
  166. package/tests/executor.test.ts +0 -339
  167. package/tests/init.test.ts +0 -86
  168. package/tests/logger.test.ts +0 -110
  169. package/tests/parser.test.ts +0 -290
  170. package/tests/progress.test.ts +0 -345
  171. package/tests/ralph-loop.test.ts +0 -418
  172. package/tests/renderer.test.ts +0 -350
  173. package/tests/runner.test.ts +0 -497
  174. package/tests/setup.test.ts +0 -7
  175. package/tests/signal-manager.test.ts +0 -26
  176. package/tests/steps.test.ts +0 -412
  177. package/tests/worktree.test.ts +0 -411
  178. package/tsconfig.json +0 -18
  179. package/vitest.config.ts +0 -8
  180. /package/{src → dist}/agents/explorer.md +0 -0
  181. /package/{src → dist}/agents/reviewer.md +0 -0
  182. /package/{src → dist}/claude/.claude/skills/analyze/references/bug.md +0 -0
  183. /package/{src → dist}/claude/.claude/skills/analyze/references/debt.md +0 -0
  184. /package/{src → dist}/claude/.claude/skills/analyze/references/doc.md +0 -0
  185. /package/{src → dist}/claude/.claude/skills/analyze/references/security.md +0 -0
  186. /package/{src → dist}/claude/.claude/skills/analyze/references/style.md +0 -0
  187. /package/{src → dist}/claude/.claude/skills/orchestrate/SKILL.md +0 -0
  188. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/bug.md +0 -0
  189. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/chore.md +0 -0
  190. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/feature.md +0 -0
  191. /package/{src → dist}/prompts/agentic-system.md +0 -0
  192. /package/{src → dist}/templates/analysis/bug.md.j2 +0 -0
  193. /package/{src → dist}/templates/analysis/debt.md.j2 +0 -0
  194. /package/{src → dist}/templates/analysis/doc.md.j2 +0 -0
  195. /package/{src → dist}/templates/analysis/security.md.j2 +0 -0
  196. /package/{src → dist}/templates/analysis/style.md.j2 +0 -0
  197. /package/{src → dist}/templates/analysis-summary.md.j2 +0 -0
  198. /package/{src → dist}/templates/checkpoint.md.j2 +0 -0
  199. /package/{src → dist}/templates/implementation-report.md.j2 +0 -0
  200. /package/{src → dist}/templates/memory.md.j2 +0 -0
  201. /package/{src → dist}/templates/plan-bug.md.j2 +0 -0
  202. /package/{src → dist}/templates/plan-chore.md.j2 +0 -0
  203. /package/{src → dist}/templates/plan-feature.md.j2 +0 -0
  204. /package/{src → dist}/templates/progress.json.j2 +0 -0
  205. /package/{src → dist}/templates/ralph-report.md.j2 +0 -0
  206. /package/{src → dist}/workflows/analyze-codebase-merge.yaml +0 -0
  207. /package/{src → dist}/workflows/analyze-codebase.yaml +0 -0
  208. /package/{src → dist}/workflows/analyze-single.yaml +0 -0
  209. /package/{src → dist}/workflows/demo.yaml +0 -0
  210. /package/{src → dist}/workflows/one-shot.yaml +0 -0
  211. /package/{src → dist}/workflows/plan-build-review.yaml +0 -0
  212. /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
- }