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
@@ -1,186 +0,0 @@
1
- /** Ralph loop step executor. */
2
-
3
- import type { ConsoleOutput } from "../console.js";
4
- import { extractSummary } from "../console.js";
5
- import type { WorkflowLogger } from "../logging/logger.js";
6
- import { WORKFLOW_STATUS, updateStepCompleted, updateStepFailed } from "../progress.js";
7
- import {
8
- buildRalphSystemMessage,
9
- createRalphState,
10
- deactivateRalphState,
11
- detectCompletionPromise,
12
- loadRalphState,
13
- updateRalphIteration,
14
- } from "../ralph-loop.js";
15
- import { runClaude } from "../runner.js";
16
- import type { StepDefinition, WorkflowProgress } from "../types.js";
17
- import {
18
- type StepContext,
19
- StepExecutor,
20
- type StepResult,
21
- buildTemplateContext,
22
- resolveModel,
23
- } from "./base.js";
24
-
25
- export class RalphLoopStepExecutor extends StepExecutor {
26
- async execute(
27
- step: StepDefinition,
28
- progress: WorkflowProgress,
29
- context: StepContext,
30
- logger: WorkflowLogger,
31
- console: ConsoleOutput,
32
- ): Promise<StepResult> {
33
- const promptTemplate = step.prompt ?? "";
34
- const templateContext = buildTemplateContext(context);
35
-
36
- let completionPromise = step.completionPromise ?? "COMPLETE";
37
- if (context.renderer.hasVariables(completionPromise)) {
38
- completionPromise = context.renderer.renderString(completionPromise, templateContext);
39
- }
40
-
41
- // Handle max_iterations which may be a template string or integer
42
- const maxIterationsRaw = step.maxIterations;
43
- let maxIterations: number;
44
- if (typeof maxIterationsRaw === "string" && context.renderer.hasVariables(maxIterationsRaw)) {
45
- maxIterations = Number.parseInt(
46
- context.renderer.renderString(maxIterationsRaw, templateContext),
47
- 10,
48
- );
49
- } else {
50
- maxIterations = maxIterationsRaw ? Number.parseInt(String(maxIterationsRaw), 10) : 5;
51
- }
52
-
53
- const timeout = (step.stepTimeoutMinutes ?? 30) * 60;
54
- const bypassPermissions = context.workflowSettings?.bypassPermissions ?? false;
55
- const allowedTools = context.workflowSettings?.requiredTools?.length
56
- ? context.workflowSettings.requiredTools
57
- : null;
58
-
59
- // Always enable streaming when console is provided
60
- const printOutput = true;
61
-
62
- logger.info(step.name, `Starting Ralph loop (max ${maxIterations} iterations)`);
63
- console.info(`Ralph loop starting (max ${maxIterations} iterations)`);
64
-
65
- const allOutputs: string[] = [];
66
-
67
- // Check for existing ralph state to resume from
68
- const existingState = loadRalphState(progress.workflowId, step.name, context.repoRoot);
69
- let startIteration: number;
70
- if (existingState?.active) {
71
- startIteration = existingState.iteration;
72
- logger.info(step.name, `Resuming Ralph loop from iteration ${startIteration}`);
73
- console.info(`Resuming Ralph loop from iteration ${startIteration}`);
74
- } else {
75
- startIteration = 1;
76
- }
77
-
78
- for (let iteration = startIteration; iteration <= maxIterations; iteration++) {
79
- logger.info(step.name, `Ralph loop iteration ${iteration}/${maxIterations}`);
80
-
81
- // Render prompt with iteration context
82
- const iterationContext = {
83
- ...templateContext,
84
- iteration,
85
- max_iterations: maxIterations,
86
- };
87
- let prompt: string;
88
- if (context.renderer.hasVariables(promptTemplate)) {
89
- prompt = context.renderer.renderString(promptTemplate, iterationContext);
90
- } else {
91
- prompt = promptTemplate;
92
- }
93
-
94
- // Create ralph state on first iteration
95
- if (iteration === startIteration && !existingState?.active) {
96
- createRalphState(
97
- progress.workflowId,
98
- step.name,
99
- prompt,
100
- maxIterations,
101
- completionPromise,
102
- context.repoRoot,
103
- );
104
- }
105
-
106
- const systemMessage = buildRalphSystemMessage(iteration, maxIterations, completionPromise);
107
- const fullPrompt = `${systemMessage}${prompt}`;
108
-
109
- // Display iteration header BEFORE running Claude
110
- console.ralphIterationStart(step.name, iteration, maxIterations);
111
-
112
- const result = await runClaude({
113
- prompt: fullPrompt,
114
- cwd: context.repoRoot,
115
- model: resolveModel(context, step.model),
116
- timeout,
117
- printOutput,
118
- skipPermissions: bypassPermissions,
119
- allowedTools,
120
- console,
121
- workflowId: context.workflowId,
122
- });
123
-
124
- if (!result.success) {
125
- const errorSummary = result.stderr ? extractSummary(result.stderr) : "Unknown error";
126
- logger.warning(step.name, `Iteration ${iteration} failed: ${result.stderr}`);
127
- console.ralphIteration(step.name, iteration, maxIterations, `Failed: ${errorSummary}`);
128
- if (iteration < maxIterations) {
129
- updateRalphIteration(progress.workflowId, step.name, context.repoRoot);
130
- continue;
131
- }
132
- deactivateRalphState(progress.workflowId, step.name, context.repoRoot);
133
- const errorMsg = `Ralph loop failed after ${maxIterations} iterations`;
134
- console.stepFailed(step.name, errorMsg);
135
- updateStepFailed(progress, step.name, errorMsg);
136
- progress.status = WORKFLOW_STATUS.FAILED;
137
- return { success: false, error: errorMsg };
138
- }
139
-
140
- allOutputs.push(result.stdout);
141
-
142
- // Print iteration summary
143
- const iterationSummary = extractSummary(result.stdout);
144
- console.ralphIteration(step.name, iteration, maxIterations, iterationSummary);
145
-
146
- const completionResult = detectCompletionPromise(result.stdout, completionPromise);
147
-
148
- // Check for failure signal first
149
- if (completionResult.isFailed) {
150
- const errorMsg = `Ralph loop failed: ${completionResult.failureReason}`;
151
- logger.error(step.name, errorMsg);
152
- deactivateRalphState(progress.workflowId, step.name, context.repoRoot);
153
- const combinedOutput = allOutputs.join("\n\n---\n\n");
154
- console.stepFailed(step.name, errorMsg);
155
- updateStepFailed(progress, step.name, errorMsg);
156
- progress.status = WORKFLOW_STATUS.FAILED;
157
- return { success: false, error: errorMsg, fullOutput: combinedOutput };
158
- }
159
-
160
- if (completionResult.isComplete && completionResult.promiseMatched) {
161
- logger.info(step.name, `Ralph loop completed at iteration ${iteration}`);
162
- deactivateRalphState(progress.workflowId, step.name, context.repoRoot);
163
- const combinedOutput = allOutputs.join("\n\n---\n\n");
164
- const outputSummary = `Completed in ${iteration} iterations`;
165
- updateStepCompleted(progress, step.name, outputSummary, combinedOutput);
166
- console.ralphComplete(step.name, iteration, maxIterations);
167
- return { success: true, outputSummary, fullOutput: combinedOutput };
168
- }
169
-
170
- if (iteration < maxIterations) {
171
- updateRalphIteration(progress.workflowId, step.name, context.repoRoot);
172
- }
173
- }
174
-
175
- logger.warning(
176
- step.name,
177
- `Ralph loop reached max iterations (${maxIterations}) without completion`,
178
- );
179
- deactivateRalphState(progress.workflowId, step.name, context.repoRoot);
180
- const combinedOutput = allOutputs.join("\n\n---\n\n");
181
- const outputSummary = `Max iterations (${maxIterations}) reached without completion promise`;
182
- updateStepCompleted(progress, step.name, outputSummary, combinedOutput);
183
- console.ralphMaxIterations(step.name, maxIterations);
184
- return { success: true, outputSummary, fullOutput: combinedOutput };
185
- }
186
- }
@@ -1,84 +0,0 @@
1
- /** Serial 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
- } from "./base.js";
13
-
14
- export class SerialStepExecutor extends StepExecutor {
15
- constructor(private branchExecutor: BranchStepExecutor) {
16
- super();
17
- }
18
-
19
- async execute(
20
- step: StepDefinition,
21
- progress: WorkflowProgress,
22
- context: StepContext,
23
- logger: WorkflowLogger,
24
- console: ConsoleOutput,
25
- ): Promise<StepResult> {
26
- if (!step.steps || step.steps.length === 0) {
27
- logger.warning(step.name, "Serial step has no sub-steps");
28
- updateStepCompleted(progress, step.name, "No sub-steps to execute");
29
- console.stepComplete(step.name, "No sub-steps to execute");
30
- return { success: true, outputSummary: "No sub-steps to execute" };
31
- }
32
-
33
- logger.info(step.name, `Starting serial execution of ${step.steps.length} steps`);
34
- console.info(`Serial: executing ${step.steps.length} steps in sequence`);
35
-
36
- let completedCount = 0;
37
-
38
- for (const subStep of step.steps) {
39
- try {
40
- const result = await this.branchExecutor(subStep, progress, context, logger, console);
41
-
42
- if (!result.success) {
43
- logger.warning(
44
- step.name,
45
- `Serial block stopped at step '${subStep.name}' due to failure`,
46
- );
47
- const errorMsg = `Step '${subStep.name}' failed: ${result.error}`;
48
- updateStepFailed(progress, step.name, errorMsg);
49
- console.stepFailed(step.name, `Step '${subStep.name}' failed`);
50
- progress.status = WORKFLOW_STATUS.FAILED;
51
- return { success: false, error: errorMsg };
52
- }
53
-
54
- completedCount++;
55
-
56
- if (progress.status === WORKFLOW_STATUS.FAILED) {
57
- logger.warning(
58
- step.name,
59
- `Serial block stopped at step '${subStep.name}' due to failure`,
60
- );
61
- break;
62
- }
63
- } catch (e) {
64
- const errStr = e instanceof Error ? e.message : String(e);
65
- logger.error(subStep.name, `Step failed in serial block: ${errStr}`);
66
- const errorMsg = `Step '${subStep.name}' failed: ${errStr}`;
67
- updateStepFailed(progress, step.name, errorMsg);
68
- console.stepFailed(step.name, `Step '${subStep.name}' failed`);
69
- progress.status = WORKFLOW_STATUS.FAILED;
70
- return { success: false, error: errorMsg };
71
- }
72
- }
73
-
74
- if (progress.status !== WORKFLOW_STATUS.FAILED) {
75
- const outputSummary = `Completed ${completedCount}/${step.steps.length} steps`;
76
- updateStepCompleted(progress, step.name, outputSummary);
77
- console.stepComplete(step.name, outputSummary);
78
- logger.info(step.name, outputSummary);
79
- return { success: true, outputSummary };
80
- }
81
-
82
- return { success: false, error: "Serial execution failed" };
83
- }
84
- }
package/src/types.ts DELETED
@@ -1,141 +0,0 @@
1
- /** Core types for agentic-forge workflow engine. */
2
-
3
- // --- Step types ---
4
-
5
- export type StepType =
6
- | "prompt"
7
- | "parallel"
8
- | "serial"
9
- | "conditional"
10
- | "ralph-loop"
11
- | "wait-for-human";
12
-
13
- export const VALID_STEP_TYPES: StepType[] = [
14
- "prompt",
15
- "parallel",
16
- "serial",
17
- "conditional",
18
- "ralph-loop",
19
- "wait-for-human",
20
- ];
21
-
22
- // --- Workflow definition types ---
23
-
24
- export interface Variable {
25
- name: string;
26
- type: string;
27
- required: boolean;
28
- default?: unknown;
29
- description?: string;
30
- }
31
-
32
- export interface GitSettings {
33
- enabled: boolean;
34
- worktree: boolean;
35
- autoCommit: boolean;
36
- autoPr: boolean;
37
- branchPrefix: string;
38
- }
39
-
40
- export interface StepGitSettings {
41
- worktree: boolean;
42
- autoPr: boolean;
43
- branchPrefix: string;
44
- }
45
-
46
- export interface WorkflowSettings {
47
- maxRetry: number;
48
- timeoutMinutes: number;
49
- trackProgress: boolean;
50
- autofix: string;
51
- terminalOutput: string;
52
- bypassPermissions: boolean;
53
- strictMode: boolean;
54
- model: string | null;
55
- requiredTools: string[];
56
- git: GitSettings;
57
- }
58
-
59
- export interface StepDefinition {
60
- name: string;
61
- type: StepType;
62
- prompt?: string | null;
63
- agent?: string | null;
64
- steps: StepDefinition[];
65
- mergeStrategy: string;
66
- mergeMode: string;
67
- condition?: string | null;
68
- thenSteps: StepDefinition[];
69
- elseSteps: StepDefinition[];
70
- maxIterations: number | string;
71
- completionPromise?: string | null;
72
- message?: string | null;
73
- pollingInterval: number;
74
- onTimeout: string;
75
- model?: string | null;
76
- stepTimeoutMinutes?: number | null;
77
- stepMaxRetry?: number | null;
78
- onError: string;
79
- checkpoint: boolean;
80
- dependsOn?: string | null;
81
- git?: StepGitSettings | null;
82
- }
83
-
84
- export interface OutputDefinition {
85
- name: string;
86
- template: string;
87
- path: string;
88
- when: string;
89
- }
90
-
91
- export interface WorkflowDefinition {
92
- name: string;
93
- version: string;
94
- description: string;
95
- settings: WorkflowSettings;
96
- variables: Variable[];
97
- steps: StepDefinition[];
98
- outputs: OutputDefinition[];
99
- }
100
-
101
- // --- Progress tracking types ---
102
-
103
- export type WorkflowStatus = "pending" | "running" | "completed" | "failed" | "paused" | "canceled";
104
-
105
- export type StepStatus = "pending" | "running" | "completed" | "failed" | "skipped";
106
-
107
- export interface StepProgress {
108
- name: string;
109
- status: string;
110
- startedAt: string | null;
111
- completedAt: string | null;
112
- retryCount: number;
113
- outputSummary: string;
114
- error: string | null;
115
- humanInput: string | null;
116
- }
117
-
118
- export interface ParallelBranch {
119
- branchId: string;
120
- status: string;
121
- worktreePath: string;
122
- progressFile: string;
123
- }
124
-
125
- export interface WorkflowProgress {
126
- schemaVersion: string;
127
- workflowId: string;
128
- workflowName: string;
129
- status: string;
130
- startedAt: string | null;
131
- completedAt: string | null;
132
- currentStep: Record<string, unknown> | null;
133
- completedSteps: StepProgress[];
134
- pendingSteps: string[];
135
- runningSteps: string[];
136
- parallelBranches: ParallelBranch[];
137
- errors: Record<string, unknown>[];
138
- variables: Record<string, unknown>;
139
- stepOutputs: Record<string, unknown>;
140
- workflowFile: string;
141
- }
@@ -1,219 +0,0 @@
1
- import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import path from "node:path";
4
- import { beforeEach, describe, expect, it } from "vitest";
5
- import {
6
- deepMerge,
7
- getConfigPath,
8
- getConfigValue,
9
- getDefaultConfig,
10
- loadConfig,
11
- saveConfig,
12
- setConfigValue,
13
- } from "../src/config.js";
14
-
15
- function makeTempDir(): string {
16
- const dir = path.join(
17
- tmpdir(),
18
- `agentic-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
19
- );
20
- mkdirSync(dir, { recursive: true });
21
- return dir;
22
- }
23
-
24
- describe("Config defaults", () => {
25
- it("should return a copy from getDefaultConfig", () => {
26
- const config1 = getDefaultConfig();
27
- const config2 = getDefaultConfig();
28
-
29
- config1.outputDirectory = "modified";
30
-
31
- expect(config2.outputDirectory).toBe("agentic");
32
- expect(config1).not.toBe(config2);
33
- });
34
-
35
- it("should have correct default structure", () => {
36
- const config = getDefaultConfig();
37
-
38
- expect(config).toHaveProperty("outputDirectory");
39
- expect(config).toHaveProperty("logging");
40
- expect(config).toHaveProperty("git");
41
- expect(config).toHaveProperty("defaults");
42
- expect(config).toHaveProperty("execution");
43
- });
44
-
45
- it("should have sonnet as default model", () => {
46
- const config = getDefaultConfig();
47
- expect((config.defaults as Record<string, unknown>).model).toBe("sonnet");
48
- });
49
- });
50
-
51
- describe("Config path", () => {
52
- it("should construct correct config path", () => {
53
- const tempDir = makeTempDir();
54
- const configPath = getConfigPath(tempDir);
55
- expect(configPath).toBe(path.join(tempDir, "agentic", "config.json"));
56
- });
57
-
58
- it("should use cwd when no root specified", () => {
59
- const configPath = getConfigPath();
60
- expect(path.basename(configPath)).toBe("config.json");
61
- expect(path.basename(path.dirname(configPath))).toBe("agentic");
62
- });
63
- });
64
-
65
- describe("Load config", () => {
66
- let tempDir: string;
67
-
68
- beforeEach(() => {
69
- tempDir = makeTempDir();
70
- });
71
-
72
- it("should load default config when no file exists", () => {
73
- const config = loadConfig(tempDir);
74
-
75
- expect(config.outputDirectory).toBe("agentic");
76
- expect((config.defaults as Record<string, unknown>).model).toBe("sonnet");
77
- });
78
-
79
- it("should load existing config file", () => {
80
- const configDir = path.join(tempDir, "agentic");
81
- mkdirSync(configDir, { recursive: true });
82
- writeFileSync(
83
- path.join(configDir, "config.json"),
84
- JSON.stringify({
85
- outputDirectory: "custom-output",
86
- defaults: { model: "opus" },
87
- }),
88
- );
89
-
90
- const config = loadConfig(tempDir);
91
-
92
- expect(config.outputDirectory).toBe("custom-output");
93
- expect((config.defaults as Record<string, unknown>).model).toBe("opus");
94
- // Other defaults should still be present
95
- expect((config.defaults as Record<string, unknown>).maxRetry).toBe(3);
96
- });
97
-
98
- it("should merge loaded config with defaults", () => {
99
- const configDir = path.join(tempDir, "agentic");
100
- mkdirSync(configDir, { recursive: true });
101
- writeFileSync(
102
- path.join(configDir, "config.json"),
103
- JSON.stringify({ logging: { level: "Debug" } }),
104
- );
105
-
106
- const config = loadConfig(tempDir);
107
-
108
- expect((config.logging as Record<string, unknown>).level).toBe("Debug");
109
- expect((config.logging as Record<string, unknown>).enabled).toBe(true);
110
- });
111
- });
112
-
113
- describe("Save config", () => {
114
- let tempDir: string;
115
-
116
- beforeEach(() => {
117
- tempDir = makeTempDir();
118
- });
119
-
120
- it("should create directory if needed", () => {
121
- const config = { test: "value" };
122
- saveConfig(config, tempDir);
123
-
124
- const configPath = getConfigPath(tempDir);
125
- const content = readFileSync(configPath, "utf-8");
126
- expect(content).toBeTruthy();
127
- });
128
-
129
- it("should write valid JSON", () => {
130
- const config = { test: "value", nested: { key: 123 } };
131
- saveConfig(config, tempDir);
132
-
133
- const configPath = getConfigPath(tempDir);
134
- const loaded = JSON.parse(readFileSync(configPath, "utf-8"));
135
- expect(loaded).toEqual(config);
136
- });
137
- });
138
-
139
- describe("Config values", () => {
140
- let tempDir: string;
141
-
142
- beforeEach(() => {
143
- tempDir = makeTempDir();
144
- });
145
-
146
- it("should get simple config value", () => {
147
- const value = getConfigValue("outputDirectory", tempDir);
148
- expect(value).toBe("agentic");
149
- });
150
-
151
- it("should get nested config value", () => {
152
- const value = getConfigValue("defaults.model", tempDir);
153
- expect(value).toBe("sonnet");
154
- });
155
-
156
- it("should return null for missing key", () => {
157
- const value = getConfigValue("nonexistent.key", tempDir);
158
- expect(value).toBeNull();
159
- });
160
-
161
- it("should set simple config value", () => {
162
- setConfigValue("outputDirectory", "new-output", tempDir);
163
- const value = getConfigValue("outputDirectory", tempDir);
164
- expect(value).toBe("new-output");
165
- });
166
-
167
- it("should set nested config value", () => {
168
- setConfigValue("defaults.model", "opus", tempDir);
169
- const value = getConfigValue("defaults.model", tempDir);
170
- expect(value).toBe("opus");
171
- });
172
-
173
- it("should create nested structure when setting value", () => {
174
- setConfigValue("new.nested.key", "value", tempDir);
175
- const value = getConfigValue("new.nested.key", tempDir);
176
- expect(value).toBe("value");
177
- });
178
-
179
- it("should parse boolean true value", () => {
180
- setConfigValue("logging.enabled", "true", tempDir);
181
- const value = getConfigValue("logging.enabled", tempDir);
182
- expect(value).toBe(true);
183
- });
184
-
185
- it("should parse boolean false value", () => {
186
- setConfigValue("logging.enabled", "false", tempDir);
187
- const value = getConfigValue("logging.enabled", tempDir);
188
- expect(value).toBe(false);
189
- });
190
-
191
- it("should parse integer value", () => {
192
- setConfigValue("defaults.maxRetry", "10", tempDir);
193
- const value = getConfigValue("defaults.maxRetry", tempDir);
194
- expect(value).toBe(10);
195
- });
196
- });
197
-
198
- describe("Deep merge", () => {
199
- it("should merge simple objects", () => {
200
- const base = { a: 1, b: 2 };
201
- const override = { b: 3, c: 4 };
202
- const result = deepMerge(base as Record<string, unknown>, override as Record<string, unknown>);
203
- expect(result).toEqual({ a: 1, b: 3, c: 4 });
204
- });
205
-
206
- it("should merge nested objects", () => {
207
- const base = { a: { x: 1, y: 2 }, b: 3 };
208
- const override = { a: { y: 99, z: 100 } };
209
- const result = deepMerge(base as Record<string, unknown>, override as Record<string, unknown>);
210
- expect(result).toEqual({ a: { x: 1, y: 99, z: 100 }, b: 3 });
211
- });
212
-
213
- it("should not mutate base dictionary", () => {
214
- const base = { a: { x: 1 } };
215
- const override = { a: { y: 2 } };
216
- deepMerge(base as Record<string, unknown>, override as Record<string, unknown>);
217
- expect(base).toEqual({ a: { x: 1 } });
218
- });
219
- });