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/steps/base.ts DELETED
@@ -1,71 +0,0 @@
1
- /** Base class and types for step executors. */
2
-
3
- import type { ConsoleOutput } from "../console.js";
4
- import type { WorkflowLogger } from "../logging/logger.js";
5
- import type { TemplateRenderer } from "../renderer.js";
6
- import type { StepDefinition, WorkflowProgress, WorkflowSettings } from "../types.js";
7
-
8
- // --- Step context ---
9
-
10
- export interface StepContext {
11
- repoRoot: string;
12
- config: Record<string, unknown>;
13
- renderer: TemplateRenderer;
14
- workflowSettings: WorkflowSettings | null;
15
- workflowId: string;
16
- variables: Record<string, unknown>;
17
- outputs: Record<string, unknown>;
18
- cwdOverride?: string | null;
19
- }
20
-
21
- export function buildTemplateContext(context: StepContext): Record<string, unknown> {
22
- return {
23
- variables: context.variables,
24
- outputs: context.outputs,
25
- workflow_id: context.workflowId,
26
- ...(context.variables as Record<string, unknown>),
27
- };
28
- }
29
-
30
- export function resolveModel(context: StepContext, stepModel?: string | null): string {
31
- if (stepModel) {
32
- return stepModel;
33
- }
34
- if (context.workflowSettings?.model) {
35
- return context.workflowSettings.model;
36
- }
37
- const defaults = context.config.defaults as Record<string, unknown> | undefined;
38
- return (defaults?.model as string) ?? "sonnet";
39
- }
40
-
41
- // --- Step result ---
42
-
43
- export interface StepResult {
44
- success: boolean;
45
- outputSummary?: string;
46
- fullOutput?: string;
47
- error?: string;
48
- }
49
-
50
- // --- Abstract base ---
51
-
52
- export abstract class StepExecutor {
53
- abstract execute(
54
- step: StepDefinition,
55
- progress: WorkflowProgress,
56
- context: StepContext,
57
- logger: WorkflowLogger,
58
- console: ConsoleOutput,
59
- ): Promise<StepResult>;
60
- }
61
-
62
- // --- Branch executor type ---
63
-
64
- /** Callable type for executing a single step within a composite executor. */
65
- export type BranchStepExecutor = (
66
- step: StepDefinition,
67
- progress: WorkflowProgress,
68
- context: StepContext,
69
- logger: WorkflowLogger,
70
- console: ConsoleOutput,
71
- ) => Promise<StepResult>;
@@ -1,144 +0,0 @@
1
- /** Conditional step executor. */
2
-
3
- import type { ConsoleOutput } from "../console.js";
4
- import type { WorkflowLogger } from "../logging/logger.js";
5
- import { WORKFLOW_STATUS, updateStepCompleted, updateStepFailed } from "../progress.js";
6
- import type { StepDefinition, WorkflowProgress } from "../types.js";
7
- import {
8
- type BranchStepExecutor,
9
- type StepContext,
10
- StepExecutor,
11
- type StepResult,
12
- buildTemplateContext,
13
- } from "./base.js";
14
-
15
- export class ConditionalStepExecutor extends StepExecutor {
16
- constructor(private branchExecutor: BranchStepExecutor) {
17
- super();
18
- }
19
-
20
- async execute(
21
- step: StepDefinition,
22
- progress: WorkflowProgress,
23
- context: StepContext,
24
- logger: WorkflowLogger,
25
- console: ConsoleOutput,
26
- ): Promise<StepResult> {
27
- let condition = step.condition ?? "false";
28
- logger.info(step.name, `Evaluating condition: ${condition}`);
29
-
30
- const templateContext = buildTemplateContext(context);
31
- if (context.renderer.hasVariables(condition)) {
32
- condition = context.renderer.renderString(condition, templateContext);
33
- }
34
-
35
- const conditionResult = this.evaluateCondition(condition, templateContext);
36
- logger.info(step.name, `Condition evaluated to: ${conditionResult}`);
37
- console.info(`Conditional '${step.name}': ${condition} = ${conditionResult}`);
38
-
39
- const stepsToRun = conditionResult ? step.thenSteps : step.elseSteps;
40
- const branchName = conditionResult ? "then" : "else";
41
-
42
- if (!stepsToRun || stepsToRun.length === 0) {
43
- const outputSummary = `Condition ${conditionResult}, no '${branchName}' branch to execute`;
44
- updateStepCompleted(progress, step.name, outputSummary);
45
- console.stepComplete(step.name, outputSummary);
46
- return { success: true, outputSummary };
47
- }
48
-
49
- logger.info(step.name, `Executing '${branchName}' branch with ${stepsToRun.length} steps`);
50
-
51
- for (const subStep of stepsToRun) {
52
- try {
53
- const result = await this.branchExecutor(subStep, progress, context, logger, console);
54
-
55
- if (!result.success) {
56
- logger.warning(step.name, `Conditional '${branchName}' branch stopped due to failure`);
57
- const errorMsg = `Step '${subStep.name}' failed: ${result.error}`;
58
- updateStepFailed(progress, step.name, errorMsg);
59
- console.stepFailed(step.name, `Step '${subStep.name}' failed`);
60
- progress.status = WORKFLOW_STATUS.FAILED;
61
- return { success: false, error: errorMsg };
62
- }
63
-
64
- if (progress.status === WORKFLOW_STATUS.FAILED) {
65
- logger.warning(step.name, `Conditional '${branchName}' branch stopped due to failure`);
66
- break;
67
- }
68
- } catch (e) {
69
- const errStr = e instanceof Error ? e.message : String(e);
70
- logger.error(subStep.name, `Step failed in conditional branch: ${errStr}`);
71
- const errorMsg = `Step '${subStep.name}' failed: ${errStr}`;
72
- updateStepFailed(progress, step.name, errorMsg);
73
- console.stepFailed(step.name, `Step '${subStep.name}' failed`);
74
- progress.status = WORKFLOW_STATUS.FAILED;
75
- return { success: false, error: errorMsg };
76
- }
77
- }
78
-
79
- if (progress.status !== WORKFLOW_STATUS.FAILED) {
80
- const outputSummary = `Executed '${branchName}' branch (${stepsToRun.length} steps)`;
81
- updateStepCompleted(progress, step.name, outputSummary);
82
- console.stepComplete(step.name, outputSummary);
83
- logger.info(step.name, outputSummary);
84
- return { success: true, outputSummary };
85
- }
86
-
87
- return { success: false, error: "Conditional execution failed" };
88
- }
89
-
90
- evaluateCondition(condition: string, context: Record<string, unknown>): boolean {
91
- const trimmed = condition.trim();
92
-
93
- if (["true", "1", "yes"].includes(trimmed.toLowerCase())) {
94
- return true;
95
- }
96
- if (["false", "0", "no", "none", ""].includes(trimmed.toLowerCase())) {
97
- return false;
98
- }
99
-
100
- if (trimmed.includes("!=")) {
101
- const [left, right] = trimmed.split("!=", 2);
102
- const leftVal = this.resolveValue(left.trim(), context);
103
- const rightVal = this.resolveValue(right.trim(), context);
104
- return leftVal !== rightVal;
105
- }
106
-
107
- if (trimmed.includes("==")) {
108
- const [left, right] = trimmed.split("==", 2);
109
- const leftVal = this.resolveValue(left.trim(), context);
110
- const rightVal = this.resolveValue(right.trim(), context);
111
- return leftVal === rightVal;
112
- }
113
-
114
- const value = this.resolveValue(trimmed, context);
115
- return (
116
- Boolean(value) && value !== "none" && value !== "None" && value !== "null" && value !== ""
117
- );
118
- }
119
-
120
- resolveValue(expr: string, context: Record<string, unknown>): unknown {
121
- const trimmed = expr.trim();
122
-
123
- if (
124
- (trimmed.startsWith("'") && trimmed.endsWith("'")) ||
125
- (trimmed.startsWith('"') && trimmed.endsWith('"'))
126
- ) {
127
- return trimmed.slice(1, -1);
128
- }
129
-
130
- if (trimmed.startsWith("variables.")) {
131
- const varName = trimmed.slice(10);
132
- const vars = context.variables as Record<string, unknown> | undefined;
133
- return vars?.[varName];
134
- }
135
-
136
- if (trimmed.startsWith("outputs.")) {
137
- const outputName = trimmed.slice(8);
138
- const outputs = context.outputs as Record<string, unknown> | undefined;
139
- return outputs?.[outputName];
140
- }
141
-
142
- return (context as Record<string, unknown>)[trimmed] ?? trimmed;
143
- }
144
- }
@@ -1,213 +0,0 @@
1
- /** Parallel step executor. */
2
-
3
- import type { ConsoleOutput } from "../console.js";
4
- import { type Worktree, createWorktree, removeWorktree, runGit } from "../git/worktree.js";
5
- import type { WorkflowLogger } from "../logging/logger.js";
6
- import { WORKFLOW_STATUS, updateStepCompleted, updateStepFailed } from "../progress.js";
7
- import type { StepDefinition, WorkflowProgress } from "../types.js";
8
- import {
9
- type BranchStepExecutor,
10
- type StepContext,
11
- StepExecutor,
12
- type StepResult,
13
- } from "./base.js";
14
-
15
- export class ParallelStepExecutor extends StepExecutor {
16
- constructor(private branchExecutor: BranchStepExecutor) {
17
- super();
18
- }
19
-
20
- async execute(
21
- step: StepDefinition,
22
- progress: WorkflowProgress,
23
- context: StepContext,
24
- logger: WorkflowLogger,
25
- console: ConsoleOutput,
26
- ): Promise<StepResult> {
27
- if (!step.steps || step.steps.length === 0) {
28
- logger.warning(step.name, "Parallel step has no sub-steps");
29
- updateStepCompleted(progress, step.name, "No sub-steps to execute");
30
- console.stepComplete(step.name, "No sub-steps to execute");
31
- return { success: true, outputSummary: "No sub-steps to execute" };
32
- }
33
-
34
- const useWorktree = step.git?.worktree ?? false;
35
-
36
- logger.info(step.name, `Starting parallel execution of ${step.steps.length} branches`);
37
- if (useWorktree) {
38
- console.info(`Parallel: starting ${step.steps.length} branches (worktree isolation)`);
39
- } else {
40
- console.info(`Parallel: starting ${step.steps.length} branches`);
41
- }
42
-
43
- // Register branches for parallel display
44
- const branchNames = step.steps.map((s) => s.name);
45
- console.registerParallelBranches(branchNames);
46
- console.enterParallelMode();
47
-
48
- const branchResults: Record<string, { success: boolean; output: string }> = {};
49
- const failedBranches: string[] = [];
50
- const worktrees: Record<string, Worktree> = {};
51
-
52
- const executeBranch = async (
53
- branchStep: StepDefinition,
54
- ): Promise<[string, boolean, string, Worktree | null]> => {
55
- const branchContext: StepContext = {
56
- ...context,
57
- variables: { ...context.variables },
58
- };
59
- let worktree: Worktree | null = null;
60
-
61
- try {
62
- console.setParallelBranch(branchStep.name);
63
-
64
- if (useWorktree) {
65
- worktree = createWorktree(
66
- progress.workflowName,
67
- branchStep.name,
68
- undefined,
69
- context.repoRoot,
70
- );
71
- logger.info(branchStep.name, `Created worktree: ${worktree.path}`);
72
- console.info(` Branch '${branchStep.name}' worktree: ${worktree.branch}`);
73
- branchContext.cwdOverride = worktree.path;
74
- }
75
-
76
- const result = await this.branchExecutor(
77
- branchStep,
78
- progress,
79
- branchContext,
80
- logger,
81
- console,
82
- );
83
-
84
- console.flushParallelBranch(branchStep.name);
85
-
86
- return [branchStep.name, result.success, result.outputSummary ?? "", worktree];
87
- } catch (e) {
88
- const errStr = e instanceof Error ? (e.stack ?? e.message) : String(e);
89
- logger.error(branchStep.name, `Branch failed with exception: ${errStr}`);
90
- console.flushParallelBranch(branchStep.name);
91
- return [branchStep.name, false, String(e), worktree];
92
- }
93
- };
94
-
95
- // Execute branches with concurrency cap from config
96
- const execution = context.config?.execution as Record<string, unknown> | undefined;
97
- const maxWorkers = (execution?.maxWorkers as number) ?? 4;
98
- const settled: PromiseSettledResult<[string, boolean, string, Worktree | null]>[] = [];
99
- const branches = [...step.steps];
100
- let idx = 0;
101
-
102
- const runNext = async (): Promise<void> => {
103
- while (idx < branches.length) {
104
- const branchStep = branches[idx++];
105
- try {
106
- const value = await executeBranch(branchStep);
107
- settled.push({ status: "fulfilled", value });
108
- } catch (reason) {
109
- settled.push({ status: "rejected", reason });
110
- }
111
- }
112
- };
113
-
114
- const workers = Array.from({ length: Math.min(maxWorkers, branches.length) }, () => runNext());
115
- await Promise.all(workers);
116
- const results = settled;
117
-
118
- for (const result of results) {
119
- if (result.status === "fulfilled") {
120
- const [name, success, output, worktree] = result.value;
121
- branchResults[name] = { success, output };
122
- if (worktree) {
123
- worktrees[name] = worktree;
124
- }
125
- if (success) {
126
- console.info(` Branch '${name}' completed`);
127
- } else {
128
- console.error(` Branch '${name}' failed: ${output}`);
129
- failedBranches.push(name);
130
- }
131
- } else {
132
- // Promise rejected — should be rare since executeBranch catches errors
133
- const reason = String(result.reason);
134
- // We can't easily get the branch name from a rejected promise
135
- // but this case is already handled by the try/catch inside executeBranch
136
- logger.error(step.name, `Branch promise rejected: ${reason}`);
137
- }
138
- }
139
-
140
- // Exit parallel mode
141
- console.exitParallelMode();
142
-
143
- // Handle merge modes
144
- if (step.mergeMode === "merge" && Object.keys(worktrees).length > 0) {
145
- this.mergeWorktreeBranches(step, worktrees, failedBranches, context, logger, console);
146
- }
147
-
148
- if (step.mergeMode === "independent" && Object.keys(worktrees).length > 0) {
149
- for (const [name, worktree] of Object.entries(worktrees)) {
150
- removeWorktree(worktree, context.repoRoot, false);
151
- logger.info(name, `Worktree removed, branch preserved: ${worktree.branch}`);
152
- }
153
- }
154
-
155
- // Handle completion based on merge strategy
156
- if (step.mergeStrategy === "wait-all") {
157
- if (failedBranches.length > 0 && step.mergeMode !== "independent") {
158
- const errorMsg = `Parallel branches failed: ${failedBranches.join(", ")}`;
159
- updateStepFailed(progress, step.name, errorMsg);
160
- console.stepFailed(step.name, errorMsg);
161
- progress.status = WORKFLOW_STATUS.FAILED;
162
- return { success: false, error: errorMsg };
163
- }
164
-
165
- const completed = step.steps.length - failedBranches.length;
166
- let outputSummary = `Completed ${completed}/${step.steps.length} branches`;
167
- if (failedBranches.length > 0) {
168
- outputSummary += ` (failed: ${failedBranches.join(", ")})`;
169
- }
170
- updateStepCompleted(progress, step.name, outputSummary);
171
- console.stepComplete(step.name, outputSummary);
172
- logger.info(step.name, outputSummary);
173
- return { success: true, outputSummary };
174
- }
175
-
176
- return { success: true, outputSummary: "Parallel execution completed" };
177
- }
178
-
179
- private mergeWorktreeBranches(
180
- step: StepDefinition,
181
- worktrees: Record<string, Worktree>,
182
- failedBranches: string[],
183
- context: StepContext,
184
- logger: WorkflowLogger,
185
- console: ConsoleOutput,
186
- ): void {
187
- console.info("Merging parallel branches...");
188
-
189
- for (const [name, worktree] of Object.entries(worktrees)) {
190
- if (failedBranches.includes(name)) {
191
- removeWorktree(worktree, context.repoRoot, true);
192
- logger.info(name, "Failed branch worktree removed");
193
- continue;
194
- }
195
-
196
- try {
197
- runGit(["checkout", worktree.baseBranch], context.repoRoot);
198
- runGit(
199
- ["merge", "--no-ff", "-m", `Merge parallel branch: ${name}`, worktree.branch],
200
- context.repoRoot,
201
- );
202
- logger.info(name, `Merged branch ${worktree.branch} into ${worktree.baseBranch}`);
203
- console.info(` Merged '${name}'`);
204
- } catch (e) {
205
- logger.error(name, `Merge failed: ${e}`);
206
- console.error(` Merge failed for '${name}': ${e}`);
207
- }
208
-
209
- removeWorktree(worktree, context.repoRoot, true);
210
- logger.info(name, "Worktree and branch cleaned up");
211
- }
212
- }
213
- }
@@ -1,121 +0,0 @@
1
- /** Prompt step executor. */
2
-
3
- import { existsSync, readFileSync } from "node:fs";
4
- import path from "node:path";
5
- import { fileURLToPath } from "node:url";
6
-
7
- import type { ConsoleOutput } from "../console.js";
8
- import { extractJson, extractSummary } from "../console.js";
9
- import type { WorkflowLogger } from "../logging/logger.js";
10
- import { WORKFLOW_STATUS, updateStepCompleted, updateStepFailed } from "../progress.js";
11
- import { runClaude } from "../runner.js";
12
- import type { StepDefinition, WorkflowProgress } from "../types.js";
13
- import {
14
- type StepContext,
15
- StepExecutor,
16
- type StepResult,
17
- buildTemplateContext,
18
- resolveModel,
19
- } from "./base.js";
20
-
21
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
-
23
- export class PromptStepExecutor extends StepExecutor {
24
- async execute(
25
- step: StepDefinition,
26
- progress: WorkflowProgress,
27
- context: StepContext,
28
- logger: WorkflowLogger,
29
- console: ConsoleOutput,
30
- ): Promise<StepResult> {
31
- let prompt = step.prompt ?? "";
32
- const templateContext = buildTemplateContext(context);
33
-
34
- if (context.renderer.hasVariables(prompt)) {
35
- prompt = context.renderer.renderString(prompt, templateContext);
36
- }
37
-
38
- const cwd = context.cwdOverride ?? context.repoRoot;
39
-
40
- // Load agent file if specified (fallback to bundled agents)
41
- if (step.agent) {
42
- let agentPath = path.join(context.repoRoot, step.agent);
43
- if (!existsSync(agentPath)) {
44
- agentPath = path.join(__dirname, "..", "agents", path.basename(step.agent));
45
- }
46
- if (existsSync(agentPath)) {
47
- const agentContent = readFileSync(agentPath, "utf-8");
48
- prompt = `${agentContent}\n\n${prompt}`;
49
- }
50
- }
51
-
52
- const timeout = (step.stepTimeoutMinutes ?? 60) * 60;
53
- const defaults = context.config.defaults as Record<string, unknown> | undefined;
54
- const maxRetry = step.stepMaxRetry ?? (defaults?.maxRetry as number) ?? 3;
55
- const bypassPermissions = context.workflowSettings?.bypassPermissions ?? false;
56
- const allowedTools = context.workflowSettings?.requiredTools?.length
57
- ? context.workflowSettings.requiredTools
58
- : null;
59
-
60
- // Always enable streaming when console is provided
61
- const printOutput = true;
62
-
63
- for (let attempt = 0; attempt <= maxRetry; attempt++) {
64
- const result = await runClaude({
65
- prompt,
66
- cwd,
67
- model: resolveModel(context, step.model),
68
- timeout,
69
- printOutput,
70
- skipPermissions: bypassPermissions,
71
- allowedTools,
72
- console,
73
- workflowId: context.workflowId,
74
- });
75
-
76
- if (result.success) {
77
- // Use session output context if available, fall back to extracted summary
78
- const sessionOut = result.sessionOutput;
79
- let outputSummary: string;
80
- if (sessionOut.isSuccess && sessionOut.context) {
81
- outputSummary = sessionOut.context;
82
- if (sessionOut.sessionId) {
83
- logger.info(step.name, `Session ID: ${sessionOut.sessionId}`);
84
- }
85
- } else {
86
- outputSummary = result.stdout ? extractSummary(result.stdout) : "";
87
- }
88
-
89
- // Try to extract structured JSON from skill output
90
- const stepOutput = result.stdout ? extractJson(result.stdout) : null;
91
- const outputValue = stepOutput ?? result.stdout;
92
-
93
- updateStepCompleted(progress, step.name, outputSummary, outputValue);
94
- console.stepComplete(step.name, outputSummary);
95
- logger.info(step.name, "Step completed successfully");
96
- return { success: true, outputSummary, fullOutput: result.stdout };
97
- }
98
-
99
- if (attempt < maxRetry) {
100
- const errorSummary = result.stderr ? extractSummary(result.stderr) : "Unknown error";
101
- console.stepRetry(step.name, attempt + 1, maxRetry + 1, errorSummary);
102
- logger.warning(step.name, `Attempt ${attempt + 1} failed, retrying...`, {
103
- error: result.stderr,
104
- });
105
- if (progress.currentStep) {
106
- (progress.currentStep as Record<string, unknown>).retry_count = attempt + 1;
107
- }
108
- } else {
109
- const errorMsg = result.stderr || "Step failed";
110
- const errorSummary = errorMsg ? extractSummary(errorMsg) : "Unknown error";
111
- console.stepFailed(step.name, errorSummary);
112
- updateStepFailed(progress, step.name, errorMsg);
113
- progress.status = WORKFLOW_STATUS.FAILED;
114
- logger.error(step.name, `Step failed after ${maxRetry + 1} attempts`);
115
- return { success: false, error: errorMsg };
116
- }
117
- }
118
-
119
- return { success: false, error: "Unexpected exit from retry loop" };
120
- }
121
- }