agentic-forge 0.0.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +31 -21
- 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/{src → dist}/claude/.claude/skills/workflow-builder/SKILL.md +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +155 -0
- package/dist/cli.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} +1 -10
- package/dist/commands/index.js +13 -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 +14 -0
- package/dist/commands/run.js +193 -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 +109 -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 +56 -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}/claude/.claude/skills/workflow-builder/references/REFERENCE.md +0 -0
- /package/{src → dist}/claude/.claude/skills/workflow-builder/references/workflow-example.yaml +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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Async workflow orchestration with Claude decision loop. */
|
|
2
|
+
import { WorkflowExecutor } from "./executor.js";
|
|
3
|
+
import { TemplateRenderer } from "./renderer.js";
|
|
4
|
+
import type { WorkflowDefinition, WorkflowProgress } from "./types.js";
|
|
5
|
+
export interface OrchestratorAction {
|
|
6
|
+
type: string;
|
|
7
|
+
stepName?: string | null;
|
|
8
|
+
contextToPass?: string | null;
|
|
9
|
+
errorContext?: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface OrchestratorDecision {
|
|
12
|
+
workflowStatus: string;
|
|
13
|
+
action: OrchestratorAction;
|
|
14
|
+
reasoning: string;
|
|
15
|
+
progressUpdate: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class WorkflowOrchestrator {
|
|
18
|
+
repoRoot: string;
|
|
19
|
+
config: Record<string, unknown>;
|
|
20
|
+
renderer: TemplateRenderer;
|
|
21
|
+
executor: WorkflowExecutor;
|
|
22
|
+
private _signalManager;
|
|
23
|
+
constructor(repoRoot?: string);
|
|
24
|
+
dispose(): void;
|
|
25
|
+
private get _shutdownRequested();
|
|
26
|
+
private resolveModel;
|
|
27
|
+
run(workflow: WorkflowDefinition, variables?: Record<string, unknown> | null, fromStep?: string | null, terminalOutput?: string, workflowFile?: string, resumeProgress?: WorkflowProgress | null): Promise<WorkflowProgress>;
|
|
28
|
+
private initProgress;
|
|
29
|
+
private collectStepNames;
|
|
30
|
+
private getOrchestratorDecision;
|
|
31
|
+
private parseOrchestratorResponse;
|
|
32
|
+
private executeStepAction;
|
|
33
|
+
private executeParallelStep;
|
|
34
|
+
private executeInWorktree;
|
|
35
|
+
private executeConditionalStep;
|
|
36
|
+
private executeRalphLoopStep;
|
|
37
|
+
private executeWaitForHumanStep;
|
|
38
|
+
private retryStepAction;
|
|
39
|
+
private waitForHumanAction;
|
|
40
|
+
private findStep;
|
|
41
|
+
private handleShutdown;
|
|
42
|
+
private workflowToDict;
|
|
43
|
+
}
|
|
44
|
+
export declare function processHumanInput(workflowId: string, response: string, repoRoot?: string): boolean;
|
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
/** Async workflow orchestration with Claude decision loop. */
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import yaml from "js-yaml";
|
|
6
|
+
import { loadConfig } from "./config.js";
|
|
7
|
+
import { ConsoleOutput, OutputLevel, extractSummary } from "./console.js";
|
|
8
|
+
import { WorkflowExecutor } from "./executor.js";
|
|
9
|
+
import { createWorktree, pruneOrphaned, removeWorktree } from "./git/worktree.js";
|
|
10
|
+
import { WorkflowLogger } from "./logging/logger.js";
|
|
11
|
+
import { WORKFLOW_STATUS, createProgress, generateWorkflowId, loadProgress, progressToDict, saveProgress, updateStepCompleted, updateStepFailed, updateStepStarted, } from "./progress.js";
|
|
12
|
+
import { buildRalphSystemMessage, createRalphState, deactivateRalphState, detectCompletionPromise, loadRalphState, updateRalphIteration, } from "./ralph-loop.js";
|
|
13
|
+
import { TemplateRenderer } from "./renderer.js";
|
|
14
|
+
import { runClaude } from "./runner.js";
|
|
15
|
+
import { SignalManager, handleGracefulShutdown } from "./signal-manager.js";
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
// --- Main orchestrator ---
|
|
18
|
+
export class WorkflowOrchestrator {
|
|
19
|
+
repoRoot;
|
|
20
|
+
config;
|
|
21
|
+
renderer;
|
|
22
|
+
executor;
|
|
23
|
+
_signalManager;
|
|
24
|
+
constructor(repoRoot) {
|
|
25
|
+
this.repoRoot = repoRoot ?? process.cwd();
|
|
26
|
+
this.config = loadConfig(this.repoRoot);
|
|
27
|
+
this.renderer = new TemplateRenderer();
|
|
28
|
+
this.executor = new WorkflowExecutor(this.repoRoot);
|
|
29
|
+
this._signalManager = new SignalManager();
|
|
30
|
+
}
|
|
31
|
+
dispose() {
|
|
32
|
+
this._signalManager.dispose();
|
|
33
|
+
}
|
|
34
|
+
get _shutdownRequested() {
|
|
35
|
+
return this._signalManager.shutdownRequested;
|
|
36
|
+
}
|
|
37
|
+
resolveModel(stepModel) {
|
|
38
|
+
if (stepModel)
|
|
39
|
+
return stepModel;
|
|
40
|
+
const defaults = this.config.defaults;
|
|
41
|
+
return defaults?.model ?? "sonnet";
|
|
42
|
+
}
|
|
43
|
+
async run(workflow, variables, fromStep, terminalOutput = "base", workflowFile = "", resumeProgress) {
|
|
44
|
+
pruneOrphaned(this.repoRoot);
|
|
45
|
+
const vars = variables ? { ...variables } : {};
|
|
46
|
+
const outputLevel = terminalOutput === "all" ? OutputLevel.ALL : OutputLevel.BASE;
|
|
47
|
+
const console = new ConsoleOutput(outputLevel);
|
|
48
|
+
let progress;
|
|
49
|
+
if (resumeProgress) {
|
|
50
|
+
progress = resumeProgress;
|
|
51
|
+
const merged = { ...progress.variables };
|
|
52
|
+
Object.assign(merged, vars);
|
|
53
|
+
progress.variables = merged;
|
|
54
|
+
if (!progress.workflowFile && workflowFile) {
|
|
55
|
+
progress.workflowFile = workflowFile;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
progress = this.initProgress(workflow, vars, fromStep ?? null, workflowFile);
|
|
60
|
+
}
|
|
61
|
+
const logger = new WorkflowLogger(progress.workflowId, this.repoRoot);
|
|
62
|
+
logger.info("orchestrator", `Starting workflow: ${workflow.name}`);
|
|
63
|
+
console.workflowStart(workflow.name, progress.workflowId);
|
|
64
|
+
while (!this._shutdownRequested) {
|
|
65
|
+
const decision = await this.getOrchestratorDecision(workflow, progress, logger);
|
|
66
|
+
if (!decision) {
|
|
67
|
+
logger.error("orchestrator", "Failed to get orchestrator decision");
|
|
68
|
+
console.error("Failed to get orchestrator decision");
|
|
69
|
+
progress.status = WORKFLOW_STATUS.FAILED;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
logger.info("orchestrator", decision.reasoning);
|
|
73
|
+
if (decision.workflowStatus === "completed") {
|
|
74
|
+
progress.status = WORKFLOW_STATUS.COMPLETED;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
if (decision.workflowStatus === "failed") {
|
|
78
|
+
progress.status = WORKFLOW_STATUS.FAILED;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
if (decision.workflowStatus === "blocked") {
|
|
82
|
+
progress.status = WORKFLOW_STATUS.PAUSED;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
if (decision.action.type === "execute_step") {
|
|
86
|
+
await this.executeStepAction(workflow, progress, decision.action, logger, console);
|
|
87
|
+
}
|
|
88
|
+
else if (decision.action.type === "retry_step") {
|
|
89
|
+
await this.retryStepAction(workflow, progress, decision.action, logger, console);
|
|
90
|
+
}
|
|
91
|
+
else if (decision.action.type === "wait_for_human") {
|
|
92
|
+
this.waitForHumanAction(progress, decision.action, logger);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
else if (decision.action.type === "abort") {
|
|
96
|
+
progress.status = WORKFLOW_STATUS.FAILED;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
saveProgress(progress, this.repoRoot);
|
|
100
|
+
if (this._shutdownRequested) {
|
|
101
|
+
await this.handleShutdown(progress, logger);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
progress.completedAt = new Date().toISOString();
|
|
106
|
+
saveProgress(progress, this.repoRoot);
|
|
107
|
+
console.workflowComplete(workflow.name, progress.status);
|
|
108
|
+
this._signalManager.dispose();
|
|
109
|
+
return progress;
|
|
110
|
+
}
|
|
111
|
+
initProgress(workflow, variables, fromStep, workflowFile = "") {
|
|
112
|
+
const workflowId = generateWorkflowId(workflow.name);
|
|
113
|
+
const stepNames = this.collectStepNames(workflow.steps);
|
|
114
|
+
for (const v of workflow.variables) {
|
|
115
|
+
if (!(v.name in variables)) {
|
|
116
|
+
if (v.required && v.default === undefined) {
|
|
117
|
+
throw new Error(`Missing required variable: ${v.name}`);
|
|
118
|
+
}
|
|
119
|
+
variables[v.name] = v.default;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const progress = createProgress(workflowId, workflow.name, stepNames, variables, workflowFile);
|
|
123
|
+
if (fromStep) {
|
|
124
|
+
let skip = true;
|
|
125
|
+
const newPending = [];
|
|
126
|
+
for (const name of stepNames) {
|
|
127
|
+
if (name === fromStep) {
|
|
128
|
+
skip = false;
|
|
129
|
+
}
|
|
130
|
+
if (skip) {
|
|
131
|
+
progress.completedSteps.push({
|
|
132
|
+
name,
|
|
133
|
+
status: "skipped",
|
|
134
|
+
startedAt: null,
|
|
135
|
+
completedAt: null,
|
|
136
|
+
retryCount: 0,
|
|
137
|
+
outputSummary: "Skipped (resumed from later step)",
|
|
138
|
+
error: null,
|
|
139
|
+
humanInput: null,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
newPending.push(name);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
progress.pendingSteps = newPending;
|
|
147
|
+
}
|
|
148
|
+
saveProgress(progress, this.repoRoot);
|
|
149
|
+
return progress;
|
|
150
|
+
}
|
|
151
|
+
collectStepNames(steps) {
|
|
152
|
+
const names = [];
|
|
153
|
+
for (const step of steps) {
|
|
154
|
+
names.push(step.name);
|
|
155
|
+
if (step.steps && step.steps.length > 0) {
|
|
156
|
+
names.push(...this.collectStepNames(step.steps));
|
|
157
|
+
}
|
|
158
|
+
if (step.thenSteps && step.thenSteps.length > 0) {
|
|
159
|
+
names.push(...this.collectStepNames(step.thenSteps));
|
|
160
|
+
}
|
|
161
|
+
if (step.elseSteps && step.elseSteps.length > 0) {
|
|
162
|
+
names.push(...this.collectStepNames(step.elseSteps));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return names;
|
|
166
|
+
}
|
|
167
|
+
async getOrchestratorDecision(workflow, progress, logger) {
|
|
168
|
+
const cmdPath = path.join(__dirname, "..", "commands", "orchestrate.md");
|
|
169
|
+
let cmdTemplate;
|
|
170
|
+
try {
|
|
171
|
+
cmdTemplate = readFileSync(cmdPath, "utf-8");
|
|
172
|
+
}
|
|
173
|
+
catch (e) {
|
|
174
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
175
|
+
logger.error("orchestrator", `Orchestrate command not found at ${cmdPath}: ${msg}`);
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
const workflowYaml = yaml.dump(this.workflowToDict(workflow));
|
|
179
|
+
const progressJson = JSON.stringify(progressToDict(progress), null, 2);
|
|
180
|
+
let lastStepName = null;
|
|
181
|
+
let lastStepOutput = null;
|
|
182
|
+
if (progress.completedSteps.length > 0) {
|
|
183
|
+
const last = progress.completedSteps[progress.completedSteps.length - 1];
|
|
184
|
+
lastStepName = last.name;
|
|
185
|
+
lastStepOutput = progress.stepOutputs[lastStepName] ?? "";
|
|
186
|
+
}
|
|
187
|
+
const prompt = this.renderer.renderString(cmdTemplate, {
|
|
188
|
+
workflow_yaml: workflowYaml,
|
|
189
|
+
progress_json: progressJson,
|
|
190
|
+
last_step_name: lastStepName,
|
|
191
|
+
last_step_output: lastStepOutput ? String(lastStepOutput).slice(0, 2000) : null,
|
|
192
|
+
});
|
|
193
|
+
const defaults = this.config.defaults;
|
|
194
|
+
const maxRetry = defaults?.maxRetry ?? 3;
|
|
195
|
+
for (let attempt = 0; attempt < maxRetry; attempt++) {
|
|
196
|
+
const result = await runClaude({
|
|
197
|
+
prompt,
|
|
198
|
+
cwd: this.repoRoot,
|
|
199
|
+
model: "sonnet",
|
|
200
|
+
timeout: 120,
|
|
201
|
+
printOutput: false,
|
|
202
|
+
});
|
|
203
|
+
if (!result.success) {
|
|
204
|
+
logger.warning("orchestrator", `Orchestrator call failed: ${result.stderr}`);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
return this.parseOrchestratorResponse(result.stdout);
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
212
|
+
logger.warning("orchestrator", `Failed to parse response: ${errorMsg}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
parseOrchestratorResponse(output) {
|
|
218
|
+
let jsonStr = output;
|
|
219
|
+
if (output.includes("```json")) {
|
|
220
|
+
const start = output.indexOf("```json") + 7;
|
|
221
|
+
const end = output.indexOf("```", start);
|
|
222
|
+
jsonStr = output.slice(start, end).trim();
|
|
223
|
+
}
|
|
224
|
+
else if (output.includes("```")) {
|
|
225
|
+
const start = output.indexOf("```") + 3;
|
|
226
|
+
const end = output.indexOf("```", start);
|
|
227
|
+
jsonStr = output.slice(start, end).trim();
|
|
228
|
+
}
|
|
229
|
+
const parsed = JSON.parse(jsonStr);
|
|
230
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
231
|
+
throw new Error("Orchestrator response is not a JSON object");
|
|
232
|
+
}
|
|
233
|
+
const data = parsed;
|
|
234
|
+
const actionRaw = data.next_action;
|
|
235
|
+
const actionData = typeof actionRaw === "object" && actionRaw !== null && !Array.isArray(actionRaw)
|
|
236
|
+
? actionRaw
|
|
237
|
+
: {};
|
|
238
|
+
const action = {
|
|
239
|
+
type: typeof actionData.type === "string" ? actionData.type : "abort",
|
|
240
|
+
stepName: typeof actionData.step_name === "string" ? actionData.step_name : null,
|
|
241
|
+
contextToPass: typeof actionData.context_to_pass === "string" ? actionData.context_to_pass : null,
|
|
242
|
+
errorContext: typeof actionData.error_context === "string" ? actionData.error_context : null,
|
|
243
|
+
};
|
|
244
|
+
return {
|
|
245
|
+
workflowStatus: typeof data.workflow_status === "string" ? data.workflow_status : "failed",
|
|
246
|
+
action,
|
|
247
|
+
reasoning: typeof data.reasoning === "string" ? data.reasoning : "",
|
|
248
|
+
progressUpdate: typeof data.progress_update === "string" ? data.progress_update : "",
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
async executeStepAction(workflow, progress, action, logger, console) {
|
|
252
|
+
const step = this.findStep(workflow.steps, action.stepName ?? null);
|
|
253
|
+
if (!step) {
|
|
254
|
+
logger.error("orchestrator", `Step not found: ${action.stepName}`);
|
|
255
|
+
console.error(`Step not found: ${action.stepName}`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (step.type === "parallel") {
|
|
259
|
+
await this.executeParallelStep(workflow, step, progress, logger, console);
|
|
260
|
+
}
|
|
261
|
+
else if (step.type === "conditional") {
|
|
262
|
+
await this.executeConditionalStep(workflow, step, progress, logger, console);
|
|
263
|
+
}
|
|
264
|
+
else if (step.type === "ralph-loop") {
|
|
265
|
+
await this.executeRalphLoopStep(workflow, step, progress, logger, console);
|
|
266
|
+
}
|
|
267
|
+
else if (step.type === "wait-for-human") {
|
|
268
|
+
this.executeWaitForHumanStep(step, progress, logger);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
await this.executor.executeStep(step, progress, progress.variables, logger, console);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async executeParallelStep(workflow, step, progress, logger, console) {
|
|
275
|
+
logger.info(step.name, `Starting parallel execution with ${step.steps.length} branches`);
|
|
276
|
+
console.stepStart(step.name, "parallel");
|
|
277
|
+
console.info(`Parallel execution with ${step.steps.length} branches`);
|
|
278
|
+
updateStepStarted(progress, step.name);
|
|
279
|
+
const execution = this.config.execution;
|
|
280
|
+
const maxWorkers = execution?.maxWorkers ?? 4;
|
|
281
|
+
const worktrees = [];
|
|
282
|
+
try {
|
|
283
|
+
for (const subStep of step.steps) {
|
|
284
|
+
const wt = createWorktree(workflow.name, subStep.name, this.repoRoot);
|
|
285
|
+
worktrees.push(wt);
|
|
286
|
+
const branch = {
|
|
287
|
+
branchId: subStep.name,
|
|
288
|
+
status: "running",
|
|
289
|
+
worktreePath: wt.path,
|
|
290
|
+
progressFile: path.join(wt.path, "agentic", "outputs", progress.workflowId, "progress.json"),
|
|
291
|
+
};
|
|
292
|
+
progress.parallelBranches.push(branch);
|
|
293
|
+
}
|
|
294
|
+
saveProgress(progress, this.repoRoot);
|
|
295
|
+
const results = [];
|
|
296
|
+
const pending = worktrees.map((wt, i) => ({ wt, i }));
|
|
297
|
+
let idx = 0;
|
|
298
|
+
const runNext = async () => {
|
|
299
|
+
while (idx < pending.length) {
|
|
300
|
+
const current = pending[idx++];
|
|
301
|
+
const { wt, i } = current;
|
|
302
|
+
try {
|
|
303
|
+
const success = await this.executeInWorktree(workflow, step.steps[i], progress, wt, logger, console);
|
|
304
|
+
results.push({ success, subStep: step.steps[i], wt });
|
|
305
|
+
}
|
|
306
|
+
catch (e) {
|
|
307
|
+
results.push({ success: false, subStep: step.steps[i], wt, error: e });
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
const workers = Array.from({ length: Math.min(maxWorkers, pending.length) }, () => runNext().catch((e) => {
|
|
312
|
+
logger.error("parallel", `Worker error: ${e instanceof Error ? e.message : String(e)}`);
|
|
313
|
+
}));
|
|
314
|
+
await Promise.all(workers);
|
|
315
|
+
let allSuccess = true;
|
|
316
|
+
for (const result of results) {
|
|
317
|
+
if (!result.success) {
|
|
318
|
+
allSuccess = false;
|
|
319
|
+
const errorMsg = result.error instanceof Error ? result.error.message : "Parallel branch failed";
|
|
320
|
+
logger.error(result.subStep.name, errorMsg);
|
|
321
|
+
console.stepFailed(result.subStep.name, errorMsg);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (allSuccess) {
|
|
325
|
+
updateStepCompleted(progress, step.name, "All parallel branches completed");
|
|
326
|
+
console.stepComplete(step.name, "All parallel branches completed");
|
|
327
|
+
logger.info(step.name, "Parallel execution completed successfully");
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
updateStepFailed(progress, step.name, "One or more parallel branches failed");
|
|
331
|
+
console.stepFailed(step.name, "One or more parallel branches failed");
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
finally {
|
|
335
|
+
for (const wt of worktrees) {
|
|
336
|
+
try {
|
|
337
|
+
removeWorktree(wt, this.repoRoot, false);
|
|
338
|
+
}
|
|
339
|
+
catch (e) {
|
|
340
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
341
|
+
logger.warning(step.name, `Failed to clean worktree: ${errorMsg}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
progress.parallelBranches = [];
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async executeInWorktree(workflow, step, parentProgress, worktree, logger, console) {
|
|
348
|
+
const wtExecutor = new WorkflowExecutor(worktree.path);
|
|
349
|
+
const wtLogger = new WorkflowLogger(parentProgress.workflowId, worktree.path);
|
|
350
|
+
try {
|
|
351
|
+
await wtExecutor.executeStep(step, parentProgress, parentProgress.variables, wtLogger, console);
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
catch (e) {
|
|
355
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
356
|
+
logger.error(step.name, `Worktree execution failed: ${errorMsg}`);
|
|
357
|
+
console.stepFailed(step.name, `Worktree execution failed: ${errorMsg}`);
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async executeConditionalStep(workflow, step, progress, logger, console) {
|
|
362
|
+
logger.info(step.name, `Evaluating condition: ${step.condition}`);
|
|
363
|
+
console.stepStart(step.name, "conditional");
|
|
364
|
+
updateStepStarted(progress, step.name);
|
|
365
|
+
const context = { outputs: progress.stepOutputs, variables: progress.variables };
|
|
366
|
+
let isTrue;
|
|
367
|
+
try {
|
|
368
|
+
const conditionResult = this.renderer.renderString(`{{ ${step.condition ?? "false"} }}`, context);
|
|
369
|
+
isTrue = ["true", "1", "yes"].includes(conditionResult.toLowerCase());
|
|
370
|
+
}
|
|
371
|
+
catch (e) {
|
|
372
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
373
|
+
logger.error(step.name, `Condition evaluation failed: ${errorMsg}`);
|
|
374
|
+
console.stepFailed(step.name, `Condition evaluation failed: ${errorMsg}`);
|
|
375
|
+
updateStepFailed(progress, step.name, `Condition error: ${errorMsg}`);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const stepsToRun = isTrue ? step.thenSteps : step.elseSteps;
|
|
379
|
+
const branchName = isTrue ? "then" : "else";
|
|
380
|
+
logger.info(step.name, `Condition ${isTrue ? "met" : "not met"}, executing ${stepsToRun.length} steps`);
|
|
381
|
+
console.info(`Condition ${branchName} branch: executing ${stepsToRun.length} steps`);
|
|
382
|
+
for (const subStep of stepsToRun) {
|
|
383
|
+
await this.executor.executeStep(subStep, progress, progress.variables, logger, console);
|
|
384
|
+
if (progress.status === WORKFLOW_STATUS.FAILED) {
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (progress.status !== WORKFLOW_STATUS.FAILED) {
|
|
389
|
+
updateStepCompleted(progress, step.name, `Condition: ${isTrue}`);
|
|
390
|
+
console.stepComplete(step.name, `Condition ${branchName} branch completed`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async executeRalphLoopStep(workflow, step, progress, logger, console) {
|
|
394
|
+
const completionPromise = step.completionPromise ?? "COMPLETE";
|
|
395
|
+
const maxIterations = typeof step.maxIterations === "string"
|
|
396
|
+
? Number.parseInt(step.maxIterations, 10)
|
|
397
|
+
: step.maxIterations;
|
|
398
|
+
const resolvedModel = this.resolveModel(step.model);
|
|
399
|
+
logger.info(step.name, `Starting Ralph loop (max ${maxIterations} iterations, promise: ${completionPromise})`);
|
|
400
|
+
console.stepStart(step.name, "ralph-loop", resolvedModel);
|
|
401
|
+
console.info(`Ralph loop starting (max ${maxIterations} iterations)`);
|
|
402
|
+
updateStepStarted(progress, step.name);
|
|
403
|
+
// Render prompt template
|
|
404
|
+
const templateContext = {
|
|
405
|
+
variables: progress.variables,
|
|
406
|
+
outputs: progress.stepOutputs,
|
|
407
|
+
...progress.variables,
|
|
408
|
+
};
|
|
409
|
+
let prompt = step.prompt ?? "";
|
|
410
|
+
if (this.renderer.hasVariables(prompt)) {
|
|
411
|
+
prompt = this.renderer.renderString(prompt, templateContext);
|
|
412
|
+
}
|
|
413
|
+
// Check for existing state to resume
|
|
414
|
+
const existingState = loadRalphState(progress.workflowId, step.name, this.repoRoot);
|
|
415
|
+
let state;
|
|
416
|
+
if (existingState?.active) {
|
|
417
|
+
state = existingState;
|
|
418
|
+
logger.info(step.name, `Resuming Ralph loop from iteration ${state.iteration}`);
|
|
419
|
+
console.info(`Resuming Ralph loop from iteration ${state.iteration}`);
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
state = createRalphState(progress.workflowId, step.name, prompt, maxIterations, completionPromise, this.repoRoot);
|
|
423
|
+
}
|
|
424
|
+
const printOutput = console.level === OutputLevel.ALL;
|
|
425
|
+
let finalOutput = "";
|
|
426
|
+
let completed = false;
|
|
427
|
+
while (state.iteration <= maxIterations && !this._shutdownRequested) {
|
|
428
|
+
logger.info(step.name, `Ralph iteration ${state.iteration}/${maxIterations}`);
|
|
429
|
+
const ralphMessage = buildRalphSystemMessage(state.iteration, maxIterations, completionPromise);
|
|
430
|
+
const fullPrompt = ralphMessage + prompt;
|
|
431
|
+
console.ralphIterationStart(step.name, state.iteration, maxIterations);
|
|
432
|
+
const timeout = (step.stepTimeoutMinutes ?? 60) * 60;
|
|
433
|
+
const result = await runClaude({
|
|
434
|
+
prompt: fullPrompt,
|
|
435
|
+
cwd: this.repoRoot,
|
|
436
|
+
model: this.resolveModel(step.model),
|
|
437
|
+
timeout,
|
|
438
|
+
printOutput,
|
|
439
|
+
skipPermissions: true,
|
|
440
|
+
console,
|
|
441
|
+
});
|
|
442
|
+
if (!result.success) {
|
|
443
|
+
const errorSummary = result.stderr ? extractSummary(result.stderr) : "Unknown error";
|
|
444
|
+
logger.warning(step.name, `Iteration ${state.iteration} failed: ${result.stderr}`);
|
|
445
|
+
console.ralphIteration(step.name, state.iteration, maxIterations, `Failed: ${errorSummary}`);
|
|
446
|
+
const newState = updateRalphIteration(progress.workflowId, step.name, this.repoRoot);
|
|
447
|
+
if (!newState)
|
|
448
|
+
break;
|
|
449
|
+
state = newState;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
const iterationSummary = extractSummary(result.stdout);
|
|
453
|
+
console.ralphIteration(step.name, state.iteration, maxIterations, iterationSummary);
|
|
454
|
+
const completionResult = detectCompletionPromise(result.stdout, completionPromise);
|
|
455
|
+
if (completionResult.isComplete && completionResult.promiseMatched) {
|
|
456
|
+
logger.info(step.name, `Completion promise matched after ${state.iteration} iterations`);
|
|
457
|
+
finalOutput = result.stdout;
|
|
458
|
+
completed = true;
|
|
459
|
+
deactivateRalphState(progress.workflowId, step.name, this.repoRoot);
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
if (completionResult.isComplete && !completionResult.promiseMatched) {
|
|
463
|
+
logger.warning(step.name, `Completion signaled but promise mismatch: got '${completionResult.promiseValue}', expected '${completionPromise}'`);
|
|
464
|
+
console.warning(`Promise mismatch: got '${completionResult.promiseValue}', expected '${completionPromise}'`);
|
|
465
|
+
}
|
|
466
|
+
const newState = updateRalphIteration(progress.workflowId, step.name, this.repoRoot);
|
|
467
|
+
if (!newState) {
|
|
468
|
+
logger.error(step.name, "Failed to update Ralph state");
|
|
469
|
+
console.error("Failed to update Ralph state");
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
state = newState;
|
|
473
|
+
}
|
|
474
|
+
if (completed) {
|
|
475
|
+
updateStepCompleted(progress, step.name, `Completed after ${state.iteration} iterations`, finalOutput);
|
|
476
|
+
console.ralphComplete(step.name, state.iteration, maxIterations);
|
|
477
|
+
}
|
|
478
|
+
else if (this._shutdownRequested) {
|
|
479
|
+
logger.info(step.name, "Ralph loop interrupted by shutdown");
|
|
480
|
+
console.warning("Ralph loop interrupted by shutdown");
|
|
481
|
+
updateStepFailed(progress, step.name, "Interrupted by shutdown");
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
logger.warning(step.name, `Max iterations (${maxIterations}) reached without completion`);
|
|
485
|
+
deactivateRalphState(progress.workflowId, step.name, this.repoRoot);
|
|
486
|
+
updateStepFailed(progress, step.name, `Max iterations (${maxIterations}) reached without completion promise`);
|
|
487
|
+
console.ralphMaxIterations(step.name, maxIterations);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
executeWaitForHumanStep(step, progress, logger) {
|
|
491
|
+
logger.info(step.name, `Waiting for human input: ${step.message}`);
|
|
492
|
+
updateStepStarted(progress, step.name);
|
|
493
|
+
progress.currentStep = {
|
|
494
|
+
name: step.name,
|
|
495
|
+
retryCount: 0,
|
|
496
|
+
startedAt: new Date().toISOString(),
|
|
497
|
+
type: "wait-for-human",
|
|
498
|
+
message: step.message,
|
|
499
|
+
timeoutMinutes: step.stepTimeoutMinutes ?? 5,
|
|
500
|
+
onTimeout: step.onTimeout,
|
|
501
|
+
};
|
|
502
|
+
progress.status = WORKFLOW_STATUS.PAUSED;
|
|
503
|
+
const sep = "=".repeat(60);
|
|
504
|
+
process.stdout.write(`\n${sep}\n`);
|
|
505
|
+
process.stdout.write("HUMAN INPUT REQUIRED\n");
|
|
506
|
+
process.stdout.write(`${sep}\n`);
|
|
507
|
+
process.stdout.write(`\n${step.message}\n\n`);
|
|
508
|
+
process.stdout.write(`Provide input with: agentic-forge input ${progress.workflowId} "<your response>"\n`);
|
|
509
|
+
process.stdout.write(`${sep}\n\n`);
|
|
510
|
+
}
|
|
511
|
+
async retryStepAction(workflow, progress, action, logger, console) {
|
|
512
|
+
let step = this.findStep(workflow.steps, action.stepName ?? null);
|
|
513
|
+
if (!step) {
|
|
514
|
+
logger.error("orchestrator", `Step not found: ${action.stepName}`);
|
|
515
|
+
console.error(`Step not found: ${action.stepName}`);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
console.info(`Retrying step: ${action.stepName}`);
|
|
519
|
+
if (action.errorContext && step.prompt) {
|
|
520
|
+
step = {
|
|
521
|
+
...step,
|
|
522
|
+
prompt: `${step.prompt}\n\nPrevious attempt failed:\n${action.errorContext}`,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
await this.executor.executeStep(step, progress, progress.variables, logger, console);
|
|
526
|
+
}
|
|
527
|
+
waitForHumanAction(progress, action, logger) {
|
|
528
|
+
progress.status = WORKFLOW_STATUS.PAUSED;
|
|
529
|
+
logger.info("orchestrator", "Workflow paused waiting for human input");
|
|
530
|
+
}
|
|
531
|
+
findStep(steps, name) {
|
|
532
|
+
if (!name)
|
|
533
|
+
return null;
|
|
534
|
+
for (const step of steps) {
|
|
535
|
+
if (step.name === name)
|
|
536
|
+
return step;
|
|
537
|
+
if (step.steps?.length) {
|
|
538
|
+
const found = this.findStep(step.steps, name);
|
|
539
|
+
if (found)
|
|
540
|
+
return found;
|
|
541
|
+
}
|
|
542
|
+
if (step.thenSteps?.length) {
|
|
543
|
+
const found = this.findStep(step.thenSteps, name);
|
|
544
|
+
if (found)
|
|
545
|
+
return found;
|
|
546
|
+
}
|
|
547
|
+
if (step.elseSteps?.length) {
|
|
548
|
+
const found = this.findStep(step.elseSteps, name);
|
|
549
|
+
if (found)
|
|
550
|
+
return found;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
async handleShutdown(progress, logger) {
|
|
556
|
+
await handleGracefulShutdown(progress, logger, this.repoRoot);
|
|
557
|
+
}
|
|
558
|
+
workflowToDict(workflow) {
|
|
559
|
+
return {
|
|
560
|
+
name: workflow.name,
|
|
561
|
+
steps: workflow.steps.map((s) => ({ name: s.name, type: s.type })),
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
// --- Process human input ---
|
|
566
|
+
export function processHumanInput(workflowId, response, repoRoot) {
|
|
567
|
+
const root = repoRoot ?? process.cwd();
|
|
568
|
+
const progress = loadProgress(workflowId, root);
|
|
569
|
+
if (!progress) {
|
|
570
|
+
process.stdout.write(`Workflow not found: ${workflowId}\n`);
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
if (progress.status !== WORKFLOW_STATUS.PAUSED) {
|
|
574
|
+
process.stdout.write(`Workflow is not paused: ${progress.status}\n`);
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
if (!progress.currentStep || progress.currentStep.type !== "wait-for-human") {
|
|
578
|
+
process.stdout.write("Workflow is not waiting for human input\n");
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
progress.currentStep.humanInput = response;
|
|
582
|
+
progress.status = WORKFLOW_STATUS.RUNNING;
|
|
583
|
+
saveProgress(progress, root);
|
|
584
|
+
process.stdout.write(`Input received. Resume workflow with: agentic-forge resume ${workflowId}\n`);
|
|
585
|
+
return true;
|
|
586
|
+
}
|
|
587
|
+
//# sourceMappingURL=orchestrator.js.map
|