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.
- package/LICENSE +21 -21
- package/README.md +64 -24
- package/{src/claude → dist/authoring}/.claude/skills/workflow-builder/SKILL.md +2 -2
- package/{src/claude → dist/authoring}/.claude/skills/workflow-builder/references/REFERENCE.md +9 -3
- package/{src/claude → dist/authoring}/.claude/skills/workflow-builder/references/workflow-example.yaml +15 -8
- package/dist/checkpoints/manager.d.ts +5 -0
- package/dist/checkpoints/manager.js +87 -0
- package/dist/checkpoints/manager.js.map +1 -0
- package/{src → dist}/claude/.claude/skills/analyze/SKILL.md +1 -1
- package/{src → dist}/claude/.claude/skills/create-checkpoint/SKILL.md +1 -1
- package/{src → dist}/claude/.claude/skills/create-log/SKILL.md +1 -1
- package/{src → dist}/claude/.claude/skills/fix-analyze/SKILL.md +1 -1
- package/{src → dist}/claude/.claude/skills/git-branch/SKILL.md +1 -1
- package/{src → dist}/claude/.claude/skills/git-commit/SKILL.md +1 -1
- package/{src → dist}/claude/.claude/skills/git-pr/SKILL.md +1 -1
- package/{src → dist}/claude/.claude/skills/sdlc-plan/SKILL.md +1 -1
- package/{src → dist}/claude/.claude/skills/sdlc-review/SKILL.md +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +173 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/authoring-dir.d.ts +2 -0
- package/dist/commands/authoring-dir.js +9 -0
- package/dist/commands/authoring-dir.js.map +1 -0
- package/dist/commands/config-cmd.d.ts +2 -0
- package/dist/commands/config-cmd.js +30 -0
- package/dist/commands/config-cmd.js.map +1 -0
- package/{src/commands/index.ts → dist/commands/index.d.ts} +2 -10
- package/dist/commands/index.js +14 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.js +83 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/release-notes.d.ts +5 -0
- package/dist/commands/release-notes.js +68 -0
- package/dist/commands/release-notes.js.map +1 -0
- package/dist/commands/resume.d.ts +5 -0
- package/dist/commands/resume.js +79 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/run.d.ts +27 -0
- package/dist/commands/run.js +243 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/shortcuts.d.ts +2 -0
- package/dist/commands/shortcuts.js +11 -0
- package/dist/commands/shortcuts.js.map +1 -0
- package/dist/commands/skills-dir.d.ts +2 -0
- package/dist/commands/skills-dir.js +9 -0
- package/dist/commands/skills-dir.js.map +1 -0
- package/dist/commands/status.d.ts +4 -0
- package/dist/commands/status.js +99 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +4 -0
- package/dist/commands/update.js +65 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/version.d.ts +3 -0
- package/dist/commands/version.js +26 -0
- package/dist/commands/version.js.map +1 -0
- package/dist/commands/workflows.d.ts +4 -0
- package/dist/commands/workflows.js +111 -0
- package/dist/commands/workflows.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +110 -0
- package/dist/config.js.map +1 -0
- package/dist/console.d.ts +103 -0
- package/dist/console.js +670 -0
- package/dist/console.js.map +1 -0
- package/dist/executor.d.ts +27 -0
- package/dist/executor.js +236 -0
- package/dist/executor.js.map +1 -0
- package/dist/git/worktree.d.ts +23 -0
- package/dist/git/worktree.js +170 -0
- package/dist/git/worktree.js.map +1 -0
- package/dist/logging/logger.d.ts +27 -0
- package/dist/logging/logger.js +69 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/orchestrator.d.ts +44 -0
- package/dist/orchestrator.js +587 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/parser.d.ts +17 -0
- package/dist/parser.js +184 -0
- package/dist/parser.js.map +1 -0
- package/dist/progress.d.ts +29 -0
- package/dist/progress.js +275 -0
- package/dist/progress.js.map +1 -0
- package/dist/ralph-loop.d.ts +26 -0
- package/dist/ralph-loop.js +194 -0
- package/dist/ralph-loop.js.map +1 -0
- package/dist/renderer.d.ts +15 -0
- package/dist/renderer.js +123 -0
- package/dist/renderer.js.map +1 -0
- package/dist/runner.d.ts +84 -0
- package/dist/runner.js +529 -0
- package/dist/runner.js.map +1 -0
- package/dist/signal-manager.d.ts +16 -0
- package/dist/signal-manager.js +50 -0
- package/dist/signal-manager.js.map +1 -0
- package/dist/steps/base.d.ts +28 -0
- package/dist/steps/base.js +23 -0
- package/dist/steps/base.js.map +1 -0
- package/dist/steps/conditional-step.d.ts +12 -0
- package/dist/steps/conditional-step.js +106 -0
- package/dist/steps/conditional-step.js.map +1 -0
- package/{src/steps/index.ts → dist/steps/index.d.ts} +1 -9
- package/dist/steps/index.js +8 -0
- package/dist/steps/index.js.map +1 -0
- package/dist/steps/parallel-step.d.ts +11 -0
- package/dist/steps/parallel-step.js +166 -0
- package/dist/steps/parallel-step.js.map +1 -0
- package/dist/steps/prompt-step.d.ts +8 -0
- package/dist/steps/prompt-step.js +94 -0
- package/dist/steps/prompt-step.js.map +1 -0
- package/dist/steps/ralph-loop-step.d.ts +8 -0
- package/dist/steps/ralph-loop-step.js +132 -0
- package/dist/steps/ralph-loop-step.js.map +1 -0
- package/dist/steps/serial-step.d.ts +10 -0
- package/dist/steps/serial-step.js +57 -0
- package/dist/steps/serial-step.js.map +1 -0
- package/dist/types.d.ts +118 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +59 -2
- package/.gitattributes +0 -24
- package/.github/workflows/ci.yml +0 -70
- package/.markdownlint-cli2.jsonc +0 -16
- package/.prettierignore +0 -3
- package/.prettierrc +0 -6
- package/.vscode/agentic-forge.code-workspace +0 -26
- package/CHANGELOG.md +0 -100
- package/CLAUDE.md +0 -158
- package/CONTRIBUTING.md +0 -152
- package/biome.json +0 -21
- package/scripts/copy-assets.js +0 -21
- package/src/checkpoints/manager.ts +0 -119
- package/src/cli.ts +0 -182
- package/src/commands/config-cmd.ts +0 -28
- package/src/commands/init.ts +0 -96
- package/src/commands/release-notes.ts +0 -85
- package/src/commands/resume.ts +0 -103
- package/src/commands/run.ts +0 -234
- package/src/commands/shortcuts.ts +0 -11
- package/src/commands/skills-dir.ts +0 -11
- package/src/commands/status.ts +0 -112
- package/src/commands/update.ts +0 -64
- package/src/commands/version.ts +0 -27
- package/src/commands/workflows.ts +0 -129
- package/src/config.ts +0 -129
- package/src/console.ts +0 -790
- package/src/executor.ts +0 -354
- package/src/git/worktree.ts +0 -236
- package/src/logging/logger.ts +0 -95
- package/src/orchestrator.ts +0 -815
- package/src/parser.ts +0 -225
- package/src/progress.ts +0 -306
- package/src/ralph-loop.ts +0 -260
- package/src/renderer.ts +0 -164
- package/src/runner.ts +0 -634
- package/src/signal-manager.ts +0 -55
- package/src/steps/base.ts +0 -71
- package/src/steps/conditional-step.ts +0 -144
- package/src/steps/parallel-step.ts +0 -213
- package/src/steps/prompt-step.ts +0 -121
- package/src/steps/ralph-loop-step.ts +0 -186
- package/src/steps/serial-step.ts +0 -84
- package/src/types.ts +0 -141
- package/tests/config.test.ts +0 -219
- package/tests/console.test.ts +0 -506
- package/tests/executor.test.ts +0 -339
- package/tests/init.test.ts +0 -86
- package/tests/logger.test.ts +0 -110
- package/tests/parser.test.ts +0 -290
- package/tests/progress.test.ts +0 -345
- package/tests/ralph-loop.test.ts +0 -418
- package/tests/renderer.test.ts +0 -350
- package/tests/runner.test.ts +0 -497
- package/tests/setup.test.ts +0 -7
- package/tests/signal-manager.test.ts +0 -26
- package/tests/steps.test.ts +0 -412
- package/tests/worktree.test.ts +0 -411
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -8
- /package/{src → dist}/agents/explorer.md +0 -0
- /package/{src → dist}/agents/reviewer.md +0 -0
- /package/{src → dist}/claude/.claude/skills/analyze/references/bug.md +0 -0
- /package/{src → dist}/claude/.claude/skills/analyze/references/debt.md +0 -0
- /package/{src → dist}/claude/.claude/skills/analyze/references/doc.md +0 -0
- /package/{src → dist}/claude/.claude/skills/analyze/references/security.md +0 -0
- /package/{src → dist}/claude/.claude/skills/analyze/references/style.md +0 -0
- /package/{src → dist}/claude/.claude/skills/orchestrate/SKILL.md +0 -0
- /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/bug.md +0 -0
- /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/chore.md +0 -0
- /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/feature.md +0 -0
- /package/{src → dist}/prompts/agentic-system.md +0 -0
- /package/{src → dist}/templates/analysis/bug.md.j2 +0 -0
- /package/{src → dist}/templates/analysis/debt.md.j2 +0 -0
- /package/{src → dist}/templates/analysis/doc.md.j2 +0 -0
- /package/{src → dist}/templates/analysis/security.md.j2 +0 -0
- /package/{src → dist}/templates/analysis/style.md.j2 +0 -0
- /package/{src → dist}/templates/analysis-summary.md.j2 +0 -0
- /package/{src → dist}/templates/checkpoint.md.j2 +0 -0
- /package/{src → dist}/templates/implementation-report.md.j2 +0 -0
- /package/{src → dist}/templates/memory.md.j2 +0 -0
- /package/{src → dist}/templates/plan-bug.md.j2 +0 -0
- /package/{src → dist}/templates/plan-chore.md.j2 +0 -0
- /package/{src → dist}/templates/plan-feature.md.j2 +0 -0
- /package/{src → dist}/templates/progress.json.j2 +0 -0
- /package/{src → dist}/templates/ralph-report.md.j2 +0 -0
- /package/{src → dist}/workflows/analyze-codebase-merge.yaml +0 -0
- /package/{src → dist}/workflows/analyze-codebase.yaml +0 -0
- /package/{src → dist}/workflows/analyze-single.yaml +0 -0
- /package/{src → dist}/workflows/demo.yaml +0 -0
- /package/{src → dist}/workflows/one-shot.yaml +0 -0
- /package/{src → dist}/workflows/plan-build-review.yaml +0 -0
- /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
|
-
}
|
package/src/steps/serial-step.ts
DELETED
|
@@ -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
|
-
}
|
package/tests/config.test.ts
DELETED
|
@@ -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
|
-
});
|