@deksden-com/dd-flow-cli 0.6.0 → 0.8.0-beta.135
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/CHANGELOG.md +636 -0
- package/README.md +13 -1
- package/dist/build-info.json +10 -10
- package/dist/cli/help.js +108 -18
- package/dist/cli/run-cli.js +626 -49
- package/dist/domain/flow-contract.js +11 -0
- package/dist/domain/stage-catalog.js +22 -0
- package/dist/runtime/context.js +8 -2
- package/dist/schemas/code-review-decision.schema.json +26 -0
- package/dist/schemas/code-review-result.schema.json +14 -0
- package/dist/schemas/code-stage-report.schema.json +7 -2
- package/dist/schemas/code-verification.schema.json +14 -0
- package/dist/schemas/code-work-batch.schema.json +24 -0
- package/dist/schemas/code-work-result.schema.json +16 -0
- package/dist/schemas/engine-manifest.schema.json +22 -0
- package/dist/schemas/flow-contract.schema.json +6 -3
- package/dist/schemas/flow-run.schema.json +16 -122
- package/dist/schemas/mb-upgrade-migration-report.schema.json +3 -1
- package/dist/schemas/merge-stage-report-legacy-0.4.2.schema.json +24 -0
- package/dist/schemas/plan-aspect-map.schema.json +22 -0
- package/dist/schemas/plan-review-decision.schema.json +14 -0
- package/dist/schemas/plan-review-result.schema.json +42 -0
- package/dist/schemas/protocol-plan.schema.json +15 -182
- package/dist/schemas/run-engine-binding.schema.json +37 -0
- package/dist/schemas/stage-finish-input.schema.json +16 -2
- package/dist/schemas/stage-prompt.schema.json +4 -4
- package/dist/schemas/stage-report.schema.json +8 -7
- package/dist/schemas/vnext-protocol-plan.schema.json +37 -0
- package/dist/schemas/vnext-protocolize-result.schema.json +29 -0
- package/dist/schemas/vnext-specify.schema.json +45 -0
- package/dist/services/branch-context.js +1 -1
- package/dist/services/canon.js +15 -1
- package/dist/services/cleanup.js +8 -8
- package/dist/services/cli-operation-classifier.js +60 -8
- package/dist/services/code-checks.js +244 -0
- package/dist/services/compatibility-preflight.js +1 -1
- package/dist/services/config.js +7 -1
- package/dist/services/dashboard.js +14 -14
- package/dist/services/engines.js +408 -30
- package/dist/services/eval-snapshots.js +404 -0
- package/dist/services/hooks.js +775 -23
- package/dist/services/ids.js +16 -6
- package/dist/services/lanes.js +1 -5
- package/dist/services/merge-queue.js +53 -5
- package/dist/services/merge-worker.js +5 -6
- package/dist/services/migrations.js +307 -44
- package/dist/services/plan-runtime.js +5 -5
- package/dist/services/plans.js +5 -3
- package/dist/services/projects.js +4 -4
- package/dist/services/prompts.js +1 -1
- package/dist/services/protocols.js +31 -10
- package/dist/services/run-engine-bindings.js +157 -0
- package/dist/services/run-projection.js +49 -13
- package/dist/services/runs.js +525 -58
- package/dist/services/schema-validation.js +116 -2
- package/dist/services/sessions.js +51 -12
- package/dist/services/stage-blocker.js +57 -0
- package/dist/services/stage-context.js +90 -0
- package/dist/services/stage-lifecycle.js +288 -77
- package/dist/services/stage-pause.js +175 -0
- package/dist/services/stage-report-renderer.js +65 -0
- package/dist/services/status.js +8 -3
- package/dist/services/usage.js +526 -18
- package/dist/services/vnext-code-review.js +308 -0
- package/dist/services/vnext-code.js +616 -0
- package/dist/services/vnext-contracts.js +1 -0
- package/dist/services/vnext-execution-profile.js +27 -0
- package/dist/services/vnext-fanout.js +79 -0
- package/dist/services/vnext-plan-review.js +499 -0
- package/dist/services/vnext-plan.js +576 -0
- package/dist/services/vnext-protocolize.js +542 -0
- package/dist/services/vnext-specify.js +595 -0
- package/dist/services/vnext-workspace-policy.js +87 -0
- package/dist/services/work-registry.js +499 -0
- package/dist/services/worktrees.js +58 -37
- package/dist/storage/database.js +292 -42
- package/dist/storage/paths.js +47 -1
- package/package.json +12 -12
package/dist/cli/run-cli.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createContext, createRouterContext } from "../runtime/context.js";
|
|
3
4
|
import { helpForArgs } from "./help.js";
|
|
4
5
|
import { AppError, isAppError } from "../shared/errors.js";
|
|
5
6
|
import { writeJson } from "../shared/json.js";
|
|
6
7
|
import { archiveProject, getProjectStatus, migrateProjectIds, registerProject, resolveProject } from "../services/projects.js";
|
|
7
8
|
import { cancelProtocol, getProtocolBlockers, getProtocolStatus, getReadyProtocols, implementProtocol, readyForMerge, registerProtocol, requireProtocol, syncProtocolFromRun, transitionProtocol } from "../services/protocols.js";
|
|
8
9
|
import { blockPlanItem, completePlanItem, getPlanStatus, skipPlanItem, startPlanItem } from "../services/plans.js";
|
|
9
|
-
import { cancelMergeQueueJob, claimNextMergeJob, claimMergeBundle, completeMergeBundle, completeMergeJob, failMergeBundle, failMergeJob, getMergeBundleStatus, getMergeQueueStatus, noteMergeJob, waitNextMergeJob } from "../services/merge-queue.js";
|
|
10
|
+
import { cancelMergeQueueJob, claimNextMergeJob, claimMergeJob, claimMergeBundle, completeMergeBundle, completeMergeJob, failMergeBundle, failMergeJob, getMergeBundleStatus, getMergeQueueStatus, noteMergeJob, waitNextMergeJob } from "../services/merge-queue.js";
|
|
10
11
|
import { getMergeWorkerStatus, oneShotMergeClaim, startMergeWorker, stopProjectMergeWorker } from "../services/merge-worker.js";
|
|
11
12
|
import { cleanupApply, cleanupScan } from "../services/cleanup.js";
|
|
12
|
-
import { getCodexHomeStatus, getCodexHooksStatus, handleCodexHook, initCodexHome, installCodexHooks, planCodexHome, printCodexHomeEnv, printCodexHooks, removeCodexHome, removeCodexHooks } from "../services/hooks.js";
|
|
13
|
+
import { getCodexHomeStatus, getCodexHooksStatus, handleCodexHook, handleAgyEvent, handleGrokEvent, handleOpenCodeEvent, handleZcodeEvent, isolatedDdFlowHomeFromHookStdin, claimBootstrapHookEvent, findRecentMatchingHookEvent, stageStartMatchKey, initCodexHome, installCodexHooks, planCodexHome, printCodexHomeEnv, printCodexHooks, removeCodexHome, removeCodexHooks } from "../services/hooks.js";
|
|
13
14
|
import { bootstrapWorktree, closeWorktree, createWorktreeRecord, getWorktreeStatus, planWorktree } from "../services/worktrees.js";
|
|
14
15
|
import { acquireLaneLock, cancelLaneWaiter, checkLaneWorkspace, getLaneWaiters, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, waitAcquireLaneLock, waitForLaneLock } from "../services/lanes.js";
|
|
15
16
|
import { getProjectConfigStatus, setProjectConfigValue } from "../services/config.js";
|
|
@@ -20,18 +21,32 @@ import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, ope
|
|
|
20
21
|
import { dashboardTargetSummary, preferredDashboardCommand, relatedDashboardCommands, resolveDashboardTarget } from "../services/dashboard-targets.js";
|
|
21
22
|
import { getFlowSessionStatus, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
|
|
22
23
|
import { getProtocolBranchContext } from "../services/branch-context.js";
|
|
23
|
-
import { attachFlowRunStage, completeFlowRun, completeFlowRunStage, getFlowRunTimeline,
|
|
24
|
+
import { attachFlowRunStage, completeFlowRun, completeFlowRunStage, getFlowRunSessions, getFlowRunTimeline, getStoredFlowRunUsage, getFlowRunFlagsStatus, getFlowRunStatus, getFlowRunConfig, getFlowRunVariables, listFlowRuns, reviseFlowRunFlags, setFlowRunConfig, setFlowRunVariable, startFlowRun } from "../services/runs.js";
|
|
24
25
|
import { previewNextEntityId } from "../services/ids.js";
|
|
25
26
|
import { validateSchema } from "../services/schema-validation.js";
|
|
26
27
|
import { preflightMemoryPermissions } from "../services/memory-permissions.js";
|
|
27
|
-
import {
|
|
28
|
+
import { ingestAgyUsage, ingestGrokUsage, ingestOpenCodeUsage, ingestZcodeUsage } from "../services/usage.js";
|
|
29
|
+
import { applyMigration, assessMigrationImpact, assertUpgradeRunStorageReady, planMigration, verifyMigrationReport } from "../services/migrations.js";
|
|
28
30
|
import { publishProjectSummary } from "../services/project-summary.js";
|
|
29
31
|
import { requireProjectByRoot } from "../services/projects.js";
|
|
30
32
|
import { resolveProjectRoot } from "../storage/paths.js";
|
|
31
|
-
import { doctorEngines, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
|
|
33
|
+
import { doctorEngines, bindRunEngine, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveExplicitProjectRoot, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
|
|
32
34
|
import { preflightCliCompatibility } from "../services/compatibility-preflight.js";
|
|
35
|
+
import { finishVnextSpecifyStage, getVnextWorkStatus, isVnextSpecifyFlow, isVnextSpecifyRun, launchVnextSpecify } from "../services/vnext-specify.js";
|
|
36
|
+
import { finishVnextProtocolize, isVnextProtocolizeRun, startVnextProtocolize } from "../services/vnext-protocolize.js";
|
|
37
|
+
import { finishVnextPlan, isVnextPlanRun, startVnextPlan } from "../services/vnext-plan.js";
|
|
38
|
+
import { dispatchVnextPlanReview, finishVnextPlanReview, isVnextPlanReviewRun, recordVnextPlanReviewCapacity, startVnextPlanReview } from "../services/vnext-plan-review.js";
|
|
39
|
+
import { addVnextCodeRepair, finishVnextCode, startVnextCode } from "../services/vnext-code.js";
|
|
40
|
+
import { addVnextCodeReviewRepair, finishVnextCodeReview, isVnextCodeReviewRun, startVnextCodeReview } from "../services/vnext-code-review.js";
|
|
41
|
+
import { getVnextFanoutStatus } from "../services/vnext-fanout.js";
|
|
42
|
+
import { pauseStageForUser, resumeStageAfterUser } from "../services/stage-pause.js";
|
|
43
|
+
import { blockStageForRuntime, unblockStageAfterRuntimeRepair } from "../services/stage-blocker.js";
|
|
44
|
+
import { classifyCliOperation, requiresExplicitUpgradeAuthorization } from "../services/cli-operation-classifier.js";
|
|
33
45
|
import { renderWorkerPrompt } from "../services/prompts.js";
|
|
34
46
|
import { finishStage, startStage } from "../services/stage-lifecycle.js";
|
|
47
|
+
import { addWorkBatch, cancelWork, deleteWork, failWork, finishWork, listWorks, mutateWorkDeps, retryWork, showWork, startWork } from "../services/work-registry.js";
|
|
48
|
+
import { createEvalBootstrapSnapshot, createEvalRunSnapshot, prepareVnextSpecifyRun, restoreEvalBootstrapSnapshot, restoreEvalRunSnapshot } from "../services/eval-snapshots.js";
|
|
49
|
+
import { loadExternalStageContext } from "../services/stage-context.js";
|
|
35
50
|
const defaultIo = {
|
|
36
51
|
stdout: process.stdout,
|
|
37
52
|
stderr: process.stderr,
|
|
@@ -41,10 +56,8 @@ const flagValue = "__dd_flow_flag__";
|
|
|
41
56
|
export async function runCli(args, io = defaultIo, env = process.env) {
|
|
42
57
|
const output = parseOutputOptions(args);
|
|
43
58
|
const progress = progressForCommand(output.args);
|
|
59
|
+
let heartbeat;
|
|
44
60
|
try {
|
|
45
|
-
if (args.includes("--progress-jsonl")) {
|
|
46
|
-
throw new AppError("usage", "--progress-jsonl is not implemented yet; use --json for final structured progress", 2);
|
|
47
|
-
}
|
|
48
61
|
if (output.args.length === 1 && output.args[0] === "--version") {
|
|
49
62
|
const payload = getCliVersionReport();
|
|
50
63
|
if (output.json)
|
|
@@ -58,21 +71,38 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
58
71
|
io.stdout.write(`${help}\n`);
|
|
59
72
|
return 0;
|
|
60
73
|
}
|
|
61
|
-
const
|
|
74
|
+
const classification = classifyCliOperation(output.args, env);
|
|
75
|
+
if (requiresExplicitUpgradeAuthorization(output.args) && classification.mode !== "mb_upgrade") {
|
|
76
|
+
throw new AppError("upgrade_authorization_required", "This mb-upgrade command requires the explicit mb-upgrade authorization marker and allowlisted target context", 1, {
|
|
77
|
+
operation: classification.operation,
|
|
78
|
+
required_marker: "DD_FLOW_COMPATIBILITY_MODE=mb-upgrade"
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const routerContext = createRouterContext(env);
|
|
62
82
|
emitHumanProgress(io, output, progress, "start");
|
|
63
|
-
|
|
64
|
-
|
|
83
|
+
if (output.progressJsonl) {
|
|
84
|
+
writeProgressJsonl(io, "start", `Starting ${output.args.slice(0, 3).join(" ")}`);
|
|
85
|
+
heartbeat = setInterval(() => writeProgressJsonl(io, "running", "Command is still running"), 15_000);
|
|
86
|
+
}
|
|
87
|
+
const scopeProjectRoot = resolveProjectRootBeforeRouting(output.args, env);
|
|
88
|
+
const routed = await routeArgsThroughEngine(routerContext, output.json ? [...output.args, "--json", ...(output.progressJsonl ? ["--progress-jsonl"] : [])] : output.args, io, io.stdin, env, scopeProjectRoot);
|
|
65
89
|
if (routed?.routed && !routed.inProcess) {
|
|
90
|
+
if (heartbeat)
|
|
91
|
+
clearInterval(heartbeat);
|
|
66
92
|
emitHumanProgress(io, output, progress, "done");
|
|
67
93
|
return routed.exitCode;
|
|
68
94
|
}
|
|
69
95
|
const dispatchContext = routed?.inProcess
|
|
70
|
-
? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version })
|
|
71
|
-
:
|
|
96
|
+
? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version }, databaseModeForCommand(output.args, classification))
|
|
97
|
+
: createContext(env, databaseModeForCommand(output.args, classification));
|
|
72
98
|
preflightCliCompatibility(dispatchContext, output.args, env, scopeProjectRoot);
|
|
73
|
-
const result = await dispatch(output.args, dispatchContext, io, scopeProjectRoot);
|
|
74
|
-
const refreshWarnings = refreshDashboardsAfterMutation(
|
|
99
|
+
const result = await dispatch(output.args, dispatchContext, io, scopeProjectRoot, classification, output);
|
|
100
|
+
const refreshWarnings = refreshDashboardsAfterMutation(dispatchContext, output.args, result, scopeProjectRoot);
|
|
75
101
|
const resultWithWarnings = addWarnings(result, refreshWarnings);
|
|
102
|
+
if (heartbeat)
|
|
103
|
+
clearInterval(heartbeat);
|
|
104
|
+
if (output.progressJsonl)
|
|
105
|
+
writeProgressJsonl(io, "done", "Command finished");
|
|
76
106
|
emitHumanProgress(io, output, progress, "done");
|
|
77
107
|
const payload = attachProgress(resultWithWarnings, progress, output);
|
|
78
108
|
if (output.json)
|
|
@@ -82,18 +112,52 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
82
112
|
return completedCommandExitCode(payload);
|
|
83
113
|
}
|
|
84
114
|
catch (error) {
|
|
115
|
+
if (heartbeat)
|
|
116
|
+
clearInterval(heartbeat);
|
|
85
117
|
const payload = isAppError(error)
|
|
86
118
|
? { ok: false, error: { code: error.code, message: error.message, details: error.details } }
|
|
87
119
|
: { ok: false, error: { code: "unexpected", message: String(error) } };
|
|
88
120
|
emitHumanProgress(io, output, progress, "failed");
|
|
121
|
+
if (output.progressJsonl)
|
|
122
|
+
writeProgressJsonl(io, "failed", payload.error.message, { code: payload.error.code });
|
|
89
123
|
const errorPayload = attachProgress(payload, progress, output);
|
|
90
|
-
if (output.json)
|
|
124
|
+
if (output.json && output.progressJsonl)
|
|
125
|
+
writeJson(io.stdout, errorPayload);
|
|
126
|
+
else if (output.json)
|
|
91
127
|
writeJson(io.stderr, errorPayload);
|
|
92
128
|
else
|
|
93
129
|
io.stderr.write(renderHumanError(payload));
|
|
94
130
|
return isAppError(error) ? error.exitCode : 1;
|
|
95
131
|
}
|
|
96
132
|
}
|
|
133
|
+
function resolveProjectRootBeforeRouting(args, env) {
|
|
134
|
+
const explicit = resolveExplicitProjectRoot(args);
|
|
135
|
+
if (explicit)
|
|
136
|
+
return explicit;
|
|
137
|
+
try {
|
|
138
|
+
// Router identity lookup is read-only; it must not initialize or migrate runtime storage.
|
|
139
|
+
return resolveOperationProjectRoot(createContext(env, "read_existing"), args);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function databaseModeForCommand(args, classification) {
|
|
146
|
+
const [family, command] = args;
|
|
147
|
+
if (family === "engine" ||
|
|
148
|
+
family === "version" ||
|
|
149
|
+
family === "schema" ||
|
|
150
|
+
family === "canon" && command === "resolve" ||
|
|
151
|
+
classification.mode === "read_only_diagnostics" ||
|
|
152
|
+
classification.mode === "mb_upgrade" && (family === "status" ||
|
|
153
|
+
family === "engine" ||
|
|
154
|
+
family === "migration" && ["impact", "plan", "report", "verify", "apply"].includes(command ?? "") ||
|
|
155
|
+
family === "memory" && command === "permissions" ||
|
|
156
|
+
family === "dashboard" && ["data", "open"].includes(command ?? "") ||
|
|
157
|
+
family === "session" && command === "status"))
|
|
158
|
+
return "read_existing";
|
|
159
|
+
return "initialize";
|
|
160
|
+
}
|
|
97
161
|
function renderHumanError(payload) {
|
|
98
162
|
const lines = [`dd-flow: ${payload.error.message}`];
|
|
99
163
|
const details = recordObject(payload.error.details);
|
|
@@ -121,15 +185,20 @@ function renderHumanError(payload) {
|
|
|
121
185
|
function parseOutputOptions(args) {
|
|
122
186
|
const stripped = [];
|
|
123
187
|
let json = false;
|
|
188
|
+
let progressJsonl = false;
|
|
124
189
|
for (const arg of args) {
|
|
125
190
|
if (arg === "--json") {
|
|
126
191
|
json = true;
|
|
127
192
|
continue;
|
|
128
193
|
}
|
|
194
|
+
if (arg === "--progress-jsonl") {
|
|
195
|
+
progressJsonl = true;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
129
198
|
stripped.push(arg);
|
|
130
199
|
}
|
|
131
200
|
const hookJsonOnly = stripped[0] === "codex" && stripped[1] === "hook" && stripped[2] === "handle";
|
|
132
|
-
return { json: json || hookJsonOnly, args: stripped, hookJsonOnly };
|
|
201
|
+
return { json: json || hookJsonOnly, progressJsonl, args: stripped, hookJsonOnly };
|
|
133
202
|
}
|
|
134
203
|
function progressForCommand(args) {
|
|
135
204
|
const [family, command, subcommand] = args;
|
|
@@ -175,6 +244,12 @@ function emitHumanProgress(io, output, progress, phase) {
|
|
|
175
244
|
}
|
|
176
245
|
}
|
|
177
246
|
}
|
|
247
|
+
function writeProgressJsonl(io, phase, message, detail) {
|
|
248
|
+
io.stderr.write(`${JSON.stringify({ schema_id: "dd-flow/progress@1", phase, message, at: new Date().toISOString(), ...(detail ? { detail } : {}) })}\n`);
|
|
249
|
+
}
|
|
250
|
+
function commandProgress(io, output) {
|
|
251
|
+
return (message) => output.progressJsonl ? writeProgressJsonl(io, "running", message) : io.stderr.write(`[running] ${message}\n`);
|
|
252
|
+
}
|
|
178
253
|
function attachProgress(result, progress, output) {
|
|
179
254
|
if (progress.length === 0 || output.hookJsonOnly)
|
|
180
255
|
return result;
|
|
@@ -380,11 +455,11 @@ function stringValue(value) {
|
|
|
380
455
|
function shortSha(value) {
|
|
381
456
|
return value ? value.slice(0, 12) : "unknown";
|
|
382
457
|
}
|
|
383
|
-
async function dispatch(args, context, io, scopeProjectRoot = null) {
|
|
458
|
+
async function dispatch(args, context, io, scopeProjectRoot = null, classification = classifyCliOperation(args, context.env), output = parseOutputOptions(args)) {
|
|
384
459
|
const [family, command, ...rest] = args;
|
|
385
460
|
const parsed = parseArgs(rest);
|
|
386
461
|
if (family === "engine") {
|
|
387
|
-
return dispatchEngine(context, command, parsed);
|
|
462
|
+
return dispatchEngine(context, command, parsed, classification);
|
|
388
463
|
}
|
|
389
464
|
if (family === "status") {
|
|
390
465
|
const rootParsed = parseArgs([command ?? "", ...rest]);
|
|
@@ -524,6 +599,32 @@ async function dispatch(args, context, io, scopeProjectRoot = null) {
|
|
|
524
599
|
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
525
600
|
return getPlanStatus(context, { protocolId, projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "plan status") });
|
|
526
601
|
}
|
|
602
|
+
if (family === "plan" && command === "reviews") {
|
|
603
|
+
throw new AppError("usage", "PLAN review dispatch moved to: dd-flow plan-review dispatch <RUN>", 2);
|
|
604
|
+
}
|
|
605
|
+
if (family === "plan-review" && command === "dispatch") {
|
|
606
|
+
return dispatchVnextPlanReview(context, { projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "plan-review dispatch"), runId: requiredPosition(parsed, 0, "run-id") });
|
|
607
|
+
}
|
|
608
|
+
if (family === "stage" && command === "fanout") {
|
|
609
|
+
const action = requiredPosition(parsed, 0, "fanout action");
|
|
610
|
+
const runId = requiredPosition(parsed, 1, "run-id");
|
|
611
|
+
const stage = requiredOption(parsed, "stage");
|
|
612
|
+
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "stage fanout");
|
|
613
|
+
if (action === "status")
|
|
614
|
+
return getVnextFanoutStatus(context, { projectRoot, runId, stage });
|
|
615
|
+
if (action === "dispatch") {
|
|
616
|
+
if (stage === "plan-review")
|
|
617
|
+
return dispatchVnextPlanReview(context, { projectRoot, runId });
|
|
618
|
+
return getVnextFanoutStatus(context, { projectRoot, runId, stage });
|
|
619
|
+
}
|
|
620
|
+
throw new AppError("usage", "Use: dd-flow stage fanout <status|dispatch> <RUN> --stage <stage> --project-root <path>", 2);
|
|
621
|
+
}
|
|
622
|
+
if (family === "run" && command === "capacity") {
|
|
623
|
+
if (requiredPosition(parsed, 0, "capacity action") !== "record")
|
|
624
|
+
throw new AppError("usage", "Use: dd-flow run capacity record <RUN> --available-slots <0..15> --project-root <path>", 2);
|
|
625
|
+
const availableSlots = Number(requiredOption(parsed, "available-slots"));
|
|
626
|
+
return recordVnextPlanReviewCapacity(context, { projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "run capacity record"), runId: requiredPosition(parsed, 1, "run-id"), availableSlots });
|
|
627
|
+
}
|
|
527
628
|
if (family === "plan" && command === "item") {
|
|
528
629
|
return dispatchPlanItem(context, parsed, requiredScopeProjectRoot(scopeProjectRoot, "plan item"));
|
|
529
630
|
}
|
|
@@ -539,31 +640,279 @@ async function dispatch(args, context, io, scopeProjectRoot = null) {
|
|
|
539
640
|
...(workspaceRoot ? { workspaceRoot } : {})
|
|
540
641
|
});
|
|
541
642
|
}
|
|
643
|
+
if (family === "work" && command === "status") {
|
|
644
|
+
return getVnextWorkStatus(context, {
|
|
645
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
646
|
+
workId: requiredPosition(parsed, 0, "work-id")
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
if (family === "work" && command === "add-batch") {
|
|
650
|
+
return addWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file: requiredOption(parsed, "file") });
|
|
651
|
+
}
|
|
652
|
+
if (family === "work" && command === "ls") {
|
|
653
|
+
return listWorks(context, { ...(optionalOption(parsed, "run") ? { runId: optionalOption(parsed, "run") } : {}), ...(optionalOption(parsed, "parent") ? { parentWorkId: optionalOption(parsed, "parent") } : {}), ...(optionalOption(parsed, "status") ? { status: optionalOption(parsed, "status") } : {}), ...(hasOption(parsed, "ready") ? { ready: true } : {}), ...(hasOption(parsed, "include-results") ? { includeResults: true } : {}), ...(optionalOption(parsed, "limit") ? { limit: Number(optionalOption(parsed, "limit")) } : {}) });
|
|
654
|
+
}
|
|
655
|
+
if (family === "work" && command === "show")
|
|
656
|
+
return showWork(context, requiredPosition(parsed, 0, "work-id"));
|
|
657
|
+
if (family === "work" && command === "repair" && requiredPosition(parsed, 0, "action") === "add") {
|
|
658
|
+
if (!hasOption(parsed, "task-stdin"))
|
|
659
|
+
throw new AppError("usage", "work repair add requires --task-stdin", 2);
|
|
660
|
+
const origins = parsed.options.get("origin-work") ?? [];
|
|
661
|
+
const findings = parsed.options.get("from-finding") ?? [];
|
|
662
|
+
if (!origins.length && !findings.length)
|
|
663
|
+
throw new AppError("usage", "work repair add requires --from-check with --origin-work or --from-finding", 2);
|
|
664
|
+
if (findings.length)
|
|
665
|
+
return addVnextCodeReviewRepair(context, {
|
|
666
|
+
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
|
|
667
|
+
runId: requiredOption(parsed, "run"),
|
|
668
|
+
findingIds: findings,
|
|
669
|
+
writeScope: parsed.options.get("write-scope") ?? [],
|
|
670
|
+
objective: await readStdin(io.stdin)
|
|
671
|
+
});
|
|
672
|
+
if (!hasOption(parsed, "from-check"))
|
|
673
|
+
throw new AppError("usage", "check repair requires --from-check", 2);
|
|
674
|
+
if (!origins.length)
|
|
675
|
+
throw new AppError("usage", "check repair requires at least one --origin-work", 2);
|
|
676
|
+
return addVnextCodeRepair(context, {
|
|
677
|
+
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
|
|
678
|
+
runId: requiredOption(parsed, "run"),
|
|
679
|
+
checkReceiptId: requiredOption(parsed, "from-check"),
|
|
680
|
+
originWorkIds: origins,
|
|
681
|
+
writeScope: parsed.options.get("write-scope") ?? [],
|
|
682
|
+
objective: await readStdin(io.stdin)
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
if (family === "work" && command === "deps") {
|
|
686
|
+
const on = parsed.options.get("on") ?? [];
|
|
687
|
+
return mutateWorkDeps(context, { workId: requiredPosition(parsed, 1, "work-id"), action: requiredPosition(parsed, 0, "action"), ...(on.length ? { on } : {}) });
|
|
688
|
+
}
|
|
689
|
+
if (family === "work" && command === "delete")
|
|
690
|
+
return deleteWork(context, requiredPosition(parsed, 0, "work-id"));
|
|
691
|
+
if (family === "work" && command === "start") {
|
|
692
|
+
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
693
|
+
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work start");
|
|
694
|
+
return startWork(context, requiredPosition(parsed, 0, "work-id"), { ...(hookEventId ? { hookEventId } : {}), projectRoot });
|
|
695
|
+
}
|
|
696
|
+
if (family === "work" && command === "finish") {
|
|
697
|
+
const resultFile = optionalOption(parsed, "result-file");
|
|
698
|
+
const resultStdin = hasOption(parsed, "result-stdin");
|
|
699
|
+
if (Boolean(resultFile) === resultStdin)
|
|
700
|
+
throw new AppError("usage", "work finish requires exactly one of --result-file or --result-stdin", 2);
|
|
701
|
+
const result = resultFile ? fs.readFileSync(path.resolve(resultFile), "utf8") : await readStdin(io.stdin);
|
|
702
|
+
return await finishWork(context, requiredPosition(parsed, 0, "work-id"), result, commandProgress(io, output));
|
|
703
|
+
}
|
|
704
|
+
if (family === "work" && command === "fail")
|
|
705
|
+
return failWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
706
|
+
if (family === "work" && command === "cancel")
|
|
707
|
+
return cancelWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
708
|
+
if (family === "work" && command === "retry")
|
|
709
|
+
return retryWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
542
710
|
if (family === "stage" && command === "start") {
|
|
543
711
|
const dir = optionalOption(parsed, "dir");
|
|
544
712
|
const bootstrap = hasOption(parsed, "bootstrap");
|
|
545
713
|
const subject = optionalOption(parsed, "subject");
|
|
546
714
|
const intakeFile = optionalOption(parsed, "intake-file");
|
|
547
|
-
const
|
|
715
|
+
const intakeStdin = hasOption(parsed, "intake-stdin");
|
|
716
|
+
const contextFile = optionalOption(parsed, "context-file");
|
|
717
|
+
const contextSha256 = optionalOption(parsed, "context-sha256");
|
|
718
|
+
if (Boolean(contextFile) !== Boolean(contextSha256))
|
|
719
|
+
throw new AppError("usage", "stage start requires --context-file together with --context-sha256", 2);
|
|
720
|
+
if (hasOption(parsed, "session-id")) {
|
|
721
|
+
throw new AppError("usage", "--session-id is managed by the harness lifecycle adapter and is not accepted", 2);
|
|
722
|
+
}
|
|
723
|
+
let hookEventId = optionalOption(parsed, "hook-event-id");
|
|
724
|
+
const requireSessionBinding = hasOption(parsed, "require-session-binding");
|
|
725
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
726
|
+
const stage = requiredOption(parsed, "stage");
|
|
727
|
+
const externalContext = contextFile && contextSha256 ? loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) : undefined;
|
|
728
|
+
const claimVnextStageStart = (runId) => {
|
|
729
|
+
registerProject(context, { root: projectRoot });
|
|
730
|
+
return findRecentMatchingHookEvent(context, {
|
|
731
|
+
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
732
|
+
matchKey: stageStartMatchKey(runId, stage, projectRoot, contextSha256),
|
|
733
|
+
errorCode: "trusted_session_binding_required",
|
|
734
|
+
operation: `vNext ${stage.toUpperCase()} start`
|
|
735
|
+
}).eventKey;
|
|
736
|
+
};
|
|
737
|
+
if ((bootstrap || (!bootstrap && stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") }))) && stage === "specify" && isVnextSpecifyFlow(projectRoot)) {
|
|
738
|
+
if ((intakeFile && intakeStdin) || (externalContext && (intakeFile || intakeStdin))) {
|
|
739
|
+
throw new AppError("usage", "Use exactly one of --intake-file or --intake-stdin", 2);
|
|
740
|
+
}
|
|
741
|
+
if (!intakeFile && !intakeStdin && !externalContext) {
|
|
742
|
+
throw new AppError("usage", "vNext SPECIFY start requires --intake-file, --intake-stdin or a runner context", 2);
|
|
743
|
+
}
|
|
744
|
+
if (!hookEventId) {
|
|
745
|
+
registerProject(context, { root: projectRoot });
|
|
746
|
+
hookEventId = bootstrap
|
|
747
|
+
? claimBootstrapHookEvent(context, {
|
|
748
|
+
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
749
|
+
projectRoot,
|
|
750
|
+
subject: subject ?? "specify",
|
|
751
|
+
intakeMode: externalContext ? "context" : intakeStdin ? "stdin" : "file",
|
|
752
|
+
...(contextSha256 ? { contextSha256 } : {})
|
|
753
|
+
})
|
|
754
|
+
: claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
755
|
+
}
|
|
756
|
+
const result = launchVnextSpecify(context, {
|
|
757
|
+
projectRoot,
|
|
758
|
+
...(externalContext ? { intakeMarkdown: runnerIntake(externalContext) } : intakeFile ? { intakeFile } : { intakeMarkdown: await readStdin(io.stdin) }),
|
|
759
|
+
hookEventId,
|
|
760
|
+
...(subject ? { slug: subject } : {}),
|
|
761
|
+
...(externalContext ? { externalContext } : {}),
|
|
762
|
+
...(!bootstrap ? { runId: requiredPosition(parsed, 0, "run-id") } : {})
|
|
763
|
+
});
|
|
764
|
+
return attachExternalStageContext(result, externalContext);
|
|
765
|
+
}
|
|
766
|
+
if (!bootstrap && stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
767
|
+
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
768
|
+
return attachExternalStageContext(startVnextProtocolize(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
|
|
769
|
+
}
|
|
770
|
+
if (!bootstrap && stage === "plan" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
771
|
+
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
772
|
+
return attachExternalStageContext(startVnextPlan(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
|
|
773
|
+
}
|
|
774
|
+
if (!bootstrap && stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
775
|
+
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
776
|
+
return attachExternalStageContext(startVnextPlanReview(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
|
|
777
|
+
}
|
|
778
|
+
if (!bootstrap && stage === "code" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
779
|
+
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
780
|
+
return attachExternalStageContext(await startVnextCode(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
|
|
781
|
+
}
|
|
782
|
+
if (!bootstrap && stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
783
|
+
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
784
|
+
return attachExternalStageContext(startVnextCodeReview(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
|
|
785
|
+
}
|
|
786
|
+
if (intakeStdin) {
|
|
787
|
+
throw new AppError("usage", "--intake-stdin is only available for vNext SPECIFY", 2);
|
|
788
|
+
}
|
|
789
|
+
if (externalContext) {
|
|
790
|
+
throw new AppError("usage", "--context-file is currently supported only by vNext flow stages", 2);
|
|
791
|
+
}
|
|
548
792
|
return startStage(context, {
|
|
549
|
-
projectRoot
|
|
793
|
+
projectRoot,
|
|
550
794
|
...(bootstrap ? { bootstrap: { subject: subject ?? requiredOption(parsed, "subject"), ...(intakeFile ? { intakeFile } : {}) } } : { runId: requiredPosition(parsed, 0, "run-id") }),
|
|
551
|
-
stage
|
|
552
|
-
...(
|
|
795
|
+
stage,
|
|
796
|
+
...(hookEventId ? { hookEventId } : {}),
|
|
797
|
+
...(requireSessionBinding ? { requireSessionBinding: true } : {}),
|
|
553
798
|
...(dir ? { dir } : {})
|
|
554
799
|
});
|
|
555
800
|
}
|
|
556
801
|
if (family === "stage" && command === "finish") {
|
|
557
802
|
const dir = optionalOption(parsed, "dir");
|
|
558
|
-
|
|
803
|
+
if (hasOption(parsed, "status")) {
|
|
804
|
+
throw new AppError("usage", "Stage status belongs in the semantic input file", 2);
|
|
805
|
+
}
|
|
806
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
807
|
+
const runId = requiredPosition(parsed, 0, "run-id");
|
|
808
|
+
const stage = requiredOption(parsed, "stage");
|
|
559
809
|
const semanticFile = optionalOption(parsed, "semantic-file") ?? optionalOption(parsed, "data");
|
|
810
|
+
const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
|
|
811
|
+
const resultStdin = hasOption(parsed, "result-stdin");
|
|
812
|
+
if (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId })) {
|
|
813
|
+
const outcome = optionalOption(parsed, "outcome");
|
|
814
|
+
if (!outcome || !["specified", "failed", "cancelled"].includes(outcome)) {
|
|
815
|
+
throw new AppError("usage", "vNext SPECIFY requires --outcome specified|failed|cancelled; use stage pause for a user question", 2);
|
|
816
|
+
}
|
|
817
|
+
if (resultFile && resultStdin) {
|
|
818
|
+
throw new AppError("usage", "Use exactly one of --result-file or --result-stdin", 2);
|
|
819
|
+
}
|
|
820
|
+
if (!resultFile && !resultStdin) {
|
|
821
|
+
throw new AppError("usage", "vNext SPECIFY finish requires --result-file or --result-stdin", 2);
|
|
822
|
+
}
|
|
823
|
+
return finishVnextSpecifyStage(context, {
|
|
824
|
+
projectRoot,
|
|
825
|
+
runId,
|
|
826
|
+
...(resultFile ? { resultFile } : { resultJson: await readStdin(io.stdin) }),
|
|
827
|
+
outcome: outcome
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId })) {
|
|
831
|
+
if (!resultFile || resultStdin || hasOption(parsed, "outcome")) {
|
|
832
|
+
throw new AppError("usage", "vNext PROTOCOLIZE requires exactly --result-file", 2);
|
|
833
|
+
}
|
|
834
|
+
return finishVnextProtocolize(context, { projectRoot, runId, resultFile });
|
|
835
|
+
}
|
|
836
|
+
if (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
837
|
+
if (optionalOption(parsed, "code-work-batch-file"))
|
|
838
|
+
throw new AppError("usage", "vNext PLAN derives its CODE batch from accepted semantic plans; do not supply a batch file", 2);
|
|
839
|
+
return finishVnextPlan(context, { projectRoot, runId });
|
|
840
|
+
}
|
|
841
|
+
if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId })) {
|
|
842
|
+
if (resultStdin || hasOption(parsed, "outcome"))
|
|
843
|
+
throw new AppError("usage", "vNext PLAN-REVIEW uses --decision-file", 2);
|
|
844
|
+
const decisionFile = optionalOption(parsed, "decision-file");
|
|
845
|
+
return finishVnextPlanReview(context, { projectRoot, runId, ...(decisionFile ? { decisionFile } : {}) });
|
|
846
|
+
}
|
|
847
|
+
if (stage === "code" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
848
|
+
if (semanticFile || resultFile || resultStdin || hasOption(parsed, "outcome")) {
|
|
849
|
+
throw new AppError("usage", "vNext CODE finish accepts only --verification-file", 2);
|
|
850
|
+
}
|
|
851
|
+
return await finishVnextCode(context, { projectRoot, runId, verificationFile: optionalOption(parsed, "verification-file") ?? path.join("<missing>", "code-verification.json"), progress: commandProgress(io, output) });
|
|
852
|
+
}
|
|
853
|
+
if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId })) {
|
|
854
|
+
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
855
|
+
throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
|
|
856
|
+
return await finishVnextCodeReview(context, { projectRoot, runId, ...(optionalOption(parsed, "decision-file") ? { decisionFile: optionalOption(parsed, "decision-file") } : {}), progress: commandProgress(io, output) });
|
|
857
|
+
}
|
|
858
|
+
if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin) {
|
|
859
|
+
throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
|
|
860
|
+
}
|
|
560
861
|
return finishStage(context, {
|
|
862
|
+
projectRoot,
|
|
863
|
+
runId,
|
|
864
|
+
stage,
|
|
865
|
+
...(dir ? { dir } : {}),
|
|
866
|
+
...(semanticFile ? { semanticFile } : {})
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
if (family === "stage" && command === "pause") {
|
|
870
|
+
if (!hasOption(parsed, "question-stdin"))
|
|
871
|
+
throw new AppError("usage", "stage pause requires --question-stdin", 2);
|
|
872
|
+
return pauseStageForUser(context, {
|
|
561
873
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
562
874
|
runId: requiredPosition(parsed, 0, "run-id"),
|
|
563
875
|
stage: requiredOption(parsed, "stage"),
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
876
|
+
workId: requiredOption(parsed, "work"),
|
|
877
|
+
question: await readStdin(io.stdin)
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
if (family === "stage" && command === "resume") {
|
|
881
|
+
if (!hasOption(parsed, "answer-stdin"))
|
|
882
|
+
throw new AppError("usage", "stage resume requires --answer-stdin", 2);
|
|
883
|
+
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
884
|
+
return resumeStageAfterUser(context, {
|
|
885
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
886
|
+
runId: requiredPosition(parsed, 0, "run-id"),
|
|
887
|
+
stage: requiredOption(parsed, "stage"),
|
|
888
|
+
workId: requiredOption(parsed, "work"),
|
|
889
|
+
answer: await readStdin(io.stdin),
|
|
890
|
+
...(hookEventId ? { hookEventId } : {})
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
if (family === "stage" && command === "block") {
|
|
894
|
+
if (!hasOption(parsed, "summary-stdin"))
|
|
895
|
+
throw new AppError("usage", "stage block requires --summary-stdin", 2);
|
|
896
|
+
const kind = requiredOption(parsed, "kind");
|
|
897
|
+
if (kind !== "engine" && kind !== "harness" && kind !== "environment")
|
|
898
|
+
throw new AppError("usage", "--kind must be engine, harness or environment", 2);
|
|
899
|
+
return blockStageForRuntime(context, {
|
|
900
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
901
|
+
runId: requiredPosition(parsed, 0, "run-id"),
|
|
902
|
+
stage: requiredOption(parsed, "stage"),
|
|
903
|
+
workId: requiredOption(parsed, "work"),
|
|
904
|
+
kind,
|
|
905
|
+
code: requiredOption(parsed, "code"),
|
|
906
|
+
summary: await readStdin(io.stdin),
|
|
907
|
+
retryable: hasOption(parsed, "retryable")
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
if (family === "stage" && command === "unblock") {
|
|
911
|
+
return unblockStageAfterRuntimeRepair(context, {
|
|
912
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
913
|
+
runId: requiredPosition(parsed, 0, "run-id"),
|
|
914
|
+
stage: requiredOption(parsed, "stage"),
|
|
915
|
+
workId: requiredOption(parsed, "work")
|
|
567
916
|
});
|
|
568
917
|
}
|
|
569
918
|
if (family === "lane") {
|
|
@@ -587,6 +936,9 @@ async function dispatch(args, context, io, scopeProjectRoot = null) {
|
|
|
587
936
|
if (family === "run") {
|
|
588
937
|
return dispatchRun(context, command, parsed);
|
|
589
938
|
}
|
|
939
|
+
if (family === "stat") {
|
|
940
|
+
return dispatchStat(context, command, parsed);
|
|
941
|
+
}
|
|
590
942
|
if (family === "id") {
|
|
591
943
|
return dispatchId(context, command, parsed);
|
|
592
944
|
}
|
|
@@ -597,13 +949,13 @@ async function dispatch(args, context, io, scopeProjectRoot = null) {
|
|
|
597
949
|
return dispatchDashboard(context, command, parsed);
|
|
598
950
|
}
|
|
599
951
|
if (family === "schema") {
|
|
600
|
-
return dispatchSchema(command, parsed);
|
|
952
|
+
return dispatchSchema(context, command, parsed);
|
|
601
953
|
}
|
|
602
954
|
if (family === "memory") {
|
|
603
955
|
return dispatchMemory(command, parsed);
|
|
604
956
|
}
|
|
605
957
|
if (family === "migration") {
|
|
606
|
-
return dispatchMigration(context, command, parsed);
|
|
958
|
+
return dispatchMigration(context, command, parsed, classification);
|
|
607
959
|
}
|
|
608
960
|
if (family === "codex" && command === "hooks") {
|
|
609
961
|
return dispatchCodexHooks(context, parsed);
|
|
@@ -614,12 +966,43 @@ async function dispatch(args, context, io, scopeProjectRoot = null) {
|
|
|
614
966
|
if (family === "codex" && command === "hook") {
|
|
615
967
|
return dispatchCodexHook(context, parsed, await readStdin(io.stdin));
|
|
616
968
|
}
|
|
969
|
+
if (family === "zcode" && command === "event") {
|
|
970
|
+
return dispatchZcodeEvent(context, parsed, await readStdin(io.stdin));
|
|
971
|
+
}
|
|
972
|
+
if (family === "zcode" && command === "usage") {
|
|
973
|
+
if (requiredPosition(parsed, 0, "zcode usage action") !== "ingest")
|
|
974
|
+
throw new AppError("usage", "Unknown zcode usage action", 2);
|
|
975
|
+
return ingestZcodeUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
976
|
+
}
|
|
977
|
+
if (family === "grok" && command === "event") {
|
|
978
|
+
return dispatchGrokEvent(context, parsed, await readStdin(io.stdin));
|
|
979
|
+
}
|
|
980
|
+
if (family === "grok" && command === "usage") {
|
|
981
|
+
if (requiredPosition(parsed, 0, "grok usage action") !== "ingest")
|
|
982
|
+
throw new AppError("usage", "Unknown grok usage action", 2);
|
|
983
|
+
return ingestGrokUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
984
|
+
}
|
|
985
|
+
if (family === "opencode" && command === "event") {
|
|
986
|
+
return dispatchOpenCodeEvent(context, parsed, await readStdin(io.stdin));
|
|
987
|
+
}
|
|
988
|
+
if (family === "opencode" && command === "usage") {
|
|
989
|
+
if (requiredPosition(parsed, 0, "opencode usage action") !== "ingest")
|
|
990
|
+
throw new AppError("usage", "Unknown opencode usage action", 2);
|
|
991
|
+
return ingestOpenCodeUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
992
|
+
}
|
|
993
|
+
if (family === "agy" && command === "event")
|
|
994
|
+
return dispatchAgyEvent(context, parsed, await readStdin(io.stdin));
|
|
995
|
+
if (family === "agy" && command === "usage") {
|
|
996
|
+
if (requiredPosition(parsed, 0, "agy usage action") !== "ingest")
|
|
997
|
+
throw new AppError("usage", "Unknown agy usage action", 2);
|
|
998
|
+
return ingestAgyUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
999
|
+
}
|
|
617
1000
|
if (family === "worktree") {
|
|
618
1001
|
return dispatchWorktree(context, command, parsed, requiredScopeProjectRoot(scopeProjectRoot, `worktree ${command ?? ""}`));
|
|
619
1002
|
}
|
|
620
1003
|
throw new AppError("usage", `Unknown command: ${args.join(" ") || "<empty>"}`, 2);
|
|
621
1004
|
}
|
|
622
|
-
function dispatchEngine(context, command, parsed) {
|
|
1005
|
+
function dispatchEngine(context, command, parsed, classification) {
|
|
623
1006
|
if (command === "install") {
|
|
624
1007
|
return installCurrentEngine(context, { force: hasOption(parsed, "force") });
|
|
625
1008
|
}
|
|
@@ -630,14 +1013,32 @@ function dispatchEngine(context, command, parsed) {
|
|
|
630
1013
|
return engineInfo(context, { packageName: optionalOption(parsed, "package"), version: optionalOption(parsed, "version") });
|
|
631
1014
|
}
|
|
632
1015
|
if (command === "resolve") {
|
|
633
|
-
return resolveEngine(context, {
|
|
1016
|
+
return resolveEngine(context, {
|
|
1017
|
+
projectRoot: optionalOption(parsed, "project-root") ?? optionalOption(parsed, "root"),
|
|
1018
|
+
env: context.env,
|
|
1019
|
+
classification
|
|
1020
|
+
});
|
|
634
1021
|
}
|
|
635
1022
|
if (command === "doctor") {
|
|
636
|
-
return doctorEngines(context, {
|
|
1023
|
+
return doctorEngines(context, {
|
|
1024
|
+
projectRoot: optionalOption(parsed, "project-root") ?? optionalOption(parsed, "root"),
|
|
1025
|
+
env: context.env,
|
|
1026
|
+
classification
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
if (command === "bind-run") {
|
|
1030
|
+
return bindRunEngine(context, {
|
|
1031
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
1032
|
+
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1033
|
+
packageName: optionalOption(parsed, "package"),
|
|
1034
|
+
engineVersion: requiredOption(parsed, "engine-version"),
|
|
1035
|
+
integrityChecksum: optionalOption(parsed, "integrity-checksum"),
|
|
1036
|
+
reason: requiredOption(parsed, "reason")
|
|
1037
|
+
});
|
|
637
1038
|
}
|
|
638
1039
|
throw new AppError("usage", `Unknown engine command: ${command ?? "<empty>"}`, 2);
|
|
639
1040
|
}
|
|
640
|
-
function dispatchMigration(context, command, parsed) {
|
|
1041
|
+
function dispatchMigration(context, command, parsed, classification) {
|
|
641
1042
|
if (command === "impact")
|
|
642
1043
|
return assessMigrationImpact(context, { projectRoot: requiredOption(parsed, "project-root"), ...(optionalOption(parsed, "target-version") ? { targetVersion: optionalOption(parsed, "target-version") } : {}), ...(optionalOption(parsed, "mode") ? { mode: optionalOption(parsed, "mode") } : {}) });
|
|
643
1044
|
if (command === "plan" || command === "report") {
|
|
@@ -648,12 +1049,26 @@ function dispatchMigration(context, command, parsed) {
|
|
|
648
1049
|
runId: optionalOption(parsed, "run"),
|
|
649
1050
|
backupPath: optionalOption(parsed, "backup-path"),
|
|
650
1051
|
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
651
|
-
allowActive:
|
|
1052
|
+
allowActive: false,
|
|
1053
|
+
classification
|
|
652
1054
|
});
|
|
653
1055
|
}
|
|
654
1056
|
if (command === "verify") {
|
|
655
1057
|
return verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
656
1058
|
}
|
|
1059
|
+
if (command === "apply") {
|
|
1060
|
+
return applyMigration(context, {
|
|
1061
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
1062
|
+
sourceVersion: optionalOption(parsed, "source-version"),
|
|
1063
|
+
targetVersion: optionalOption(parsed, "target-version"),
|
|
1064
|
+
runId: optionalOption(parsed, "run"),
|
|
1065
|
+
backupPath: optionalOption(parsed, "backup-path"),
|
|
1066
|
+
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
1067
|
+
reportFile: optionalOption(parsed, "file"),
|
|
1068
|
+
allowActive: false,
|
|
1069
|
+
classification
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
657
1072
|
throw new AppError("usage", `Unknown migration command: ${command ?? "<empty>"}`, 2);
|
|
658
1073
|
}
|
|
659
1074
|
function dispatchCanon(context, command, parsed) {
|
|
@@ -682,6 +1097,14 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
|
682
1097
|
workspacePath: workspacePathOption(parsed)
|
|
683
1098
|
});
|
|
684
1099
|
}
|
|
1100
|
+
if (command === "claim") {
|
|
1101
|
+
return claimMergeJob(context, {
|
|
1102
|
+
protocolId: requiredPosition(parsed, 0, "protocol-id"),
|
|
1103
|
+
projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"),
|
|
1104
|
+
workerId: requiredWorkerId(parsed),
|
|
1105
|
+
workspacePath: workspacePathOption(parsed)
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
685
1108
|
if (command === "wait-next") {
|
|
686
1109
|
return waitNextMergeJob(context, {
|
|
687
1110
|
projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"),
|
|
@@ -886,7 +1309,52 @@ function dispatchSession(context, command, parsed) {
|
|
|
886
1309
|
throw new AppError("usage", `Unknown session command: ${command ?? "<empty>"}`, 2);
|
|
887
1310
|
}
|
|
888
1311
|
function dispatchRun(context, command, parsed) {
|
|
1312
|
+
if (command === "snapshot") {
|
|
1313
|
+
const action = requiredPosition(parsed, 0, "snapshot action");
|
|
1314
|
+
if (action === "bootstrap") {
|
|
1315
|
+
const bootstrapAction = requiredPosition(parsed, 1, "bootstrap snapshot action");
|
|
1316
|
+
if (bootstrapAction === "create")
|
|
1317
|
+
return createEvalBootstrapSnapshot(context, {
|
|
1318
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
1319
|
+
output: requiredOption(parsed, "output")
|
|
1320
|
+
});
|
|
1321
|
+
if (bootstrapAction === "restore")
|
|
1322
|
+
return restoreEvalBootstrapSnapshot(context, {
|
|
1323
|
+
snapshot: requiredOption(parsed, "snapshot"),
|
|
1324
|
+
projectRoot: requiredOption(parsed, "project-root")
|
|
1325
|
+
});
|
|
1326
|
+
throw new AppError("usage", "Use: dd-flow run snapshot bootstrap create|restore", 2);
|
|
1327
|
+
}
|
|
1328
|
+
if (action === "create") {
|
|
1329
|
+
const candidate = parsed.options.has("candidate");
|
|
1330
|
+
const stageEntry = optionalOption(parsed, "stage-entry");
|
|
1331
|
+
if (candidate && stageEntry !== undefined)
|
|
1332
|
+
throw new AppError("usage", "Use either --candidate or --stage-entry, not both", 2);
|
|
1333
|
+
if (!candidate && stageEntry === undefined)
|
|
1334
|
+
throw new AppError("usage", "Snapshot create requires --candidate or --stage-entry", 2);
|
|
1335
|
+
const input = {
|
|
1336
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
1337
|
+
runId: requiredPosition(parsed, 1, "run-id"),
|
|
1338
|
+
output: requiredOption(parsed, "output")
|
|
1339
|
+
};
|
|
1340
|
+
return candidate
|
|
1341
|
+
? createEvalRunSnapshot(context, { ...input, candidate: true })
|
|
1342
|
+
: createEvalRunSnapshot(context, { ...input, stageEntry: stageEntry });
|
|
1343
|
+
}
|
|
1344
|
+
if (action === "restore")
|
|
1345
|
+
return restoreEvalRunSnapshot(context, {
|
|
1346
|
+
snapshot: requiredOption(parsed, "snapshot"),
|
|
1347
|
+
projectRoot: requiredOption(parsed, "project-root")
|
|
1348
|
+
});
|
|
1349
|
+
throw new AppError("usage", "Use: dd-flow run snapshot bootstrap create|restore | dd-flow run snapshot create <RUN> (--stage-entry <stage>|--candidate) --project-root <path> --output <directory> | dd-flow run snapshot restore --snapshot <directory> --project-root <path>", 2);
|
|
1350
|
+
}
|
|
1351
|
+
if (command === "prepare-vnext-specify") {
|
|
1352
|
+
return prepareVnextSpecifyRun(context, { projectRoot: requiredOption(parsed, "project-root"), slug: requiredOption(parsed, "slug") });
|
|
1353
|
+
}
|
|
889
1354
|
if (command === "start") {
|
|
1355
|
+
if (optionalOption(parsed, "flow-kind") === "mb-upgrade") {
|
|
1356
|
+
assertUpgradeRunStorageReady(context, requiredOption(parsed, "project-root"));
|
|
1357
|
+
}
|
|
890
1358
|
const protocolOverrides = readOptionalJsonFile(optionalOption(parsed, "protocol-override-file"));
|
|
891
1359
|
return startFlowRun(context, {
|
|
892
1360
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
@@ -918,12 +1386,33 @@ function dispatchRun(context, command, parsed) {
|
|
|
918
1386
|
hiddenSections: allOptions(parsed, "hide")
|
|
919
1387
|
});
|
|
920
1388
|
}
|
|
921
|
-
if (command === "
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
}
|
|
1389
|
+
if (command === "config") {
|
|
1390
|
+
const action = requiredPosition(parsed, 0, "run config action");
|
|
1391
|
+
const runId = requiredPosition(parsed, 1, "run-id");
|
|
1392
|
+
if (action === "status") {
|
|
1393
|
+
return getFlowRunConfig(context, { projectRoot: requiredOption(parsed, "project-root"), runId });
|
|
1394
|
+
}
|
|
1395
|
+
if (action === "set") {
|
|
1396
|
+
return setFlowRunConfig(context, {
|
|
1397
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
1398
|
+
runId,
|
|
1399
|
+
key: requiredOption(parsed, "key"),
|
|
1400
|
+
value: requiredOption(parsed, "value"),
|
|
1401
|
+
reason: requiredOption(parsed, "reason")
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
throw new AppError("usage", `Unknown run config action: ${action}`, 2);
|
|
1405
|
+
}
|
|
1406
|
+
if (command === "vars") {
|
|
1407
|
+
const action = requiredPosition(parsed, 0, "run vars action");
|
|
1408
|
+
const runId = requiredPosition(parsed, 1, "run-id");
|
|
1409
|
+
if (action === "ls")
|
|
1410
|
+
return getFlowRunVariables(context, { projectRoot: requiredOption(parsed, "project-root"), runId });
|
|
1411
|
+
if (action === "get")
|
|
1412
|
+
return getFlowRunVariables(context, { projectRoot: requiredOption(parsed, "project-root"), runId, key: requiredOption(parsed, "key") });
|
|
1413
|
+
if (action === "set")
|
|
1414
|
+
return setFlowRunVariable(context, { projectRoot: requiredOption(parsed, "project-root"), runId, key: requiredOption(parsed, "key"), value: requiredOption(parsed, "value") });
|
|
1415
|
+
throw new AppError("usage", `Unknown run vars command: ${action}`, 2);
|
|
927
1416
|
}
|
|
928
1417
|
if (command === "flags") {
|
|
929
1418
|
const action = requiredPosition(parsed, 0, "flow flags action");
|
|
@@ -973,16 +1462,39 @@ function dispatchRun(context, command, parsed) {
|
|
|
973
1462
|
});
|
|
974
1463
|
}
|
|
975
1464
|
if (command === "complete") {
|
|
1465
|
+
throw new AppError("usage", "run complete is retired; only an operator may use run override", 2);
|
|
1466
|
+
}
|
|
1467
|
+
if (command === "override") {
|
|
1468
|
+
const status = requiredOption(parsed, "status");
|
|
1469
|
+
if (!['cancelled', 'failed'].includes(status)) {
|
|
1470
|
+
throw new AppError("usage", "run override status must be cancelled or failed", 2, { status });
|
|
1471
|
+
}
|
|
976
1472
|
return completeFlowRun(context, {
|
|
977
1473
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
978
1474
|
runId: requiredPosition(parsed, 0, "run-id"),
|
|
979
|
-
status
|
|
1475
|
+
status,
|
|
980
1476
|
verdict: optionalOption(parsed, "verdict"),
|
|
981
|
-
nextAction: optionalOption(parsed, "next-action")
|
|
1477
|
+
nextAction: optionalOption(parsed, "next-action"),
|
|
1478
|
+
manualOverrideReason: requiredOption(parsed, "reason")
|
|
982
1479
|
});
|
|
983
1480
|
}
|
|
984
1481
|
throw new AppError("usage", `Unknown run command: ${command ?? "<empty>"}`, 2);
|
|
985
1482
|
}
|
|
1483
|
+
function dispatchStat(context, command, parsed) {
|
|
1484
|
+
if (command === "usage") {
|
|
1485
|
+
return getStoredFlowRunUsage(context, {
|
|
1486
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
1487
|
+
runId: requiredOption(parsed, "run"),
|
|
1488
|
+
groupBy: optionalOption(parsed, "group-by"),
|
|
1489
|
+
sessionId: optionalOption(parsed, "session"),
|
|
1490
|
+
stage: optionalOption(parsed, "stage")
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
if (command === "run" && parsed.positional[0] === "sessions" && parsed.positional[1] === "ls") {
|
|
1494
|
+
return getFlowRunSessions(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run") });
|
|
1495
|
+
}
|
|
1496
|
+
throw new AppError("usage", "Usage: dd-flow stat usage --run <RUN-ID> --project-root <root> | dd-flow stat run sessions ls --run <RUN-ID> --project-root <root>", 2);
|
|
1497
|
+
}
|
|
986
1498
|
function dispatchId(context, command, parsed) {
|
|
987
1499
|
if (command === "next") {
|
|
988
1500
|
return previewNextEntityId(context, {
|
|
@@ -1144,7 +1656,7 @@ function withDashboardGuidance(action, target, result, options = {}) {
|
|
|
1144
1656
|
related_commands: relatedDashboardCommands(action, target.kind)
|
|
1145
1657
|
};
|
|
1146
1658
|
}
|
|
1147
|
-
function dispatchSchema(command, parsed) {
|
|
1659
|
+
function dispatchSchema(context, command, parsed) {
|
|
1148
1660
|
if (command === "validate") {
|
|
1149
1661
|
const schemaDir = optionalOption(parsed, "schema-dir");
|
|
1150
1662
|
const projectRoot = optionalOption(parsed, "project-root");
|
|
@@ -1152,7 +1664,8 @@ function dispatchSchema(command, parsed) {
|
|
|
1152
1664
|
schemaName: requiredOption(parsed, "schema"),
|
|
1153
1665
|
file: requiredOption(parsed, "file"),
|
|
1154
1666
|
...(schemaDir ? { schemaDir } : {}),
|
|
1155
|
-
...(projectRoot ? { projectRoot } : {})
|
|
1667
|
+
...(projectRoot ? { projectRoot } : {}),
|
|
1668
|
+
ddFlowHome: context.ddFlowHome
|
|
1156
1669
|
});
|
|
1157
1670
|
}
|
|
1158
1671
|
throw new AppError("usage", `Unknown schema command: ${command ?? "<empty>"}`, 2);
|
|
@@ -1332,7 +1845,9 @@ function dispatchCodexHome(context, parsed) {
|
|
|
1332
1845
|
function dispatchCodexHook(context, parsed, stdin) {
|
|
1333
1846
|
const action = requiredPosition(parsed, 0, "codex hook action");
|
|
1334
1847
|
if (action === "handle") {
|
|
1335
|
-
const
|
|
1848
|
+
const isolatedHome = isolatedDdFlowHomeFromHookStdin(stdin);
|
|
1849
|
+
const hookContext = isolatedHome ? createContext({ ...context.env, DD_FLOW_HOME: isolatedHome }) : context;
|
|
1850
|
+
const result = handleCodexHook(hookContext, {
|
|
1336
1851
|
event: requiredOption(parsed, "event"),
|
|
1337
1852
|
projectRoot: optionalOption(parsed, "project-root"),
|
|
1338
1853
|
stdin
|
|
@@ -1344,6 +1859,39 @@ function dispatchCodexHook(context, parsed, stdin) {
|
|
|
1344
1859
|
}
|
|
1345
1860
|
throw new AppError("usage", `Unknown codex hook action: ${action}`, 2);
|
|
1346
1861
|
}
|
|
1862
|
+
function dispatchZcodeEvent(context, parsed, stdin) {
|
|
1863
|
+
const action = requiredPosition(parsed, 0, "zcode event action");
|
|
1864
|
+
if (action === "handle") {
|
|
1865
|
+
return handleZcodeEvent(context, {
|
|
1866
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
1867
|
+
stdin
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
throw new AppError("usage", `Unknown zcode event action: ${action}`, 2);
|
|
1871
|
+
}
|
|
1872
|
+
function dispatchGrokEvent(context, parsed, stdin) {
|
|
1873
|
+
const action = requiredPosition(parsed, 0, "grok event action");
|
|
1874
|
+
if (action === "handle") {
|
|
1875
|
+
const result = handleGrokEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
|
|
1876
|
+
if (result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result) {
|
|
1877
|
+
return { hookSpecificOutput: result.hookSpecificOutput };
|
|
1878
|
+
}
|
|
1879
|
+
return {};
|
|
1880
|
+
}
|
|
1881
|
+
throw new AppError("usage", `Unknown grok event action: ${action}`, 2);
|
|
1882
|
+
}
|
|
1883
|
+
function dispatchOpenCodeEvent(context, parsed, stdin) {
|
|
1884
|
+
const action = requiredPosition(parsed, 0, "opencode event action");
|
|
1885
|
+
if (action === "handle")
|
|
1886
|
+
return handleOpenCodeEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
|
|
1887
|
+
throw new AppError("usage", `Unknown opencode event action: ${action}`, 2);
|
|
1888
|
+
}
|
|
1889
|
+
function dispatchAgyEvent(context, parsed, stdin) {
|
|
1890
|
+
const action = requiredPosition(parsed, 0, "agy event action");
|
|
1891
|
+
if (action === "handle")
|
|
1892
|
+
return handleAgyEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
|
|
1893
|
+
throw new AppError("usage", `Unknown agy event action: ${action}`, 2);
|
|
1894
|
+
}
|
|
1347
1895
|
function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
1348
1896
|
if (command === "plan") {
|
|
1349
1897
|
return planWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
@@ -1646,7 +2194,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
|
|
|
1646
2194
|
if (family === "run" && ["start", "attach-stage", "complete-stage", "complete", "flags"].includes(command ?? "")) {
|
|
1647
2195
|
return requiredOption(parsed, "project-root");
|
|
1648
2196
|
}
|
|
1649
|
-
if (family === "stage" && ["start", "finish"].includes(command ?? "")) {
|
|
2197
|
+
if (family === "stage" && ["start", "pause", "resume", "finish"].includes(command ?? "")) {
|
|
1650
2198
|
return requiredOption(parsed, "project-root");
|
|
1651
2199
|
}
|
|
1652
2200
|
if (family === "worktree" && ["create", "bootstrap", "close"].includes(command ?? "")) {
|
|
@@ -1655,6 +2203,14 @@ function projectRootForMutation(context, args, result, resolvedScope) {
|
|
|
1655
2203
|
if (family === "codex" && command === "hook" && requiredPosition(parsed, 0, "codex hook action") === "handle") {
|
|
1656
2204
|
return optionalOption(parsed, "project-root");
|
|
1657
2205
|
}
|
|
2206
|
+
if (family === "zcode" && ((command === "event" && requiredPosition(parsed, 0, "zcode event action") === "handle") || command === "usage")) {
|
|
2207
|
+
return requiredOption(parsed, "project-root");
|
|
2208
|
+
}
|
|
2209
|
+
if (family === "opencode" && ((command === "event" && requiredPosition(parsed, 0, "opencode event action") === "handle") || command === "usage")) {
|
|
2210
|
+
return requiredOption(parsed, "project-root");
|
|
2211
|
+
}
|
|
2212
|
+
if (family === "agy" && ((command === "event" && requiredPosition(parsed, 0, "agy event action") === "handle") || command === "usage"))
|
|
2213
|
+
return requiredOption(parsed, "project-root");
|
|
1658
2214
|
if (family === "codex" && command === "hooks") {
|
|
1659
2215
|
const action = requiredPosition(parsed, 0, "codex hooks action");
|
|
1660
2216
|
return ["install", "remove"].includes(action) ? requiredOption(parsed, "project-root") : undefined;
|
|
@@ -1679,7 +2235,7 @@ function projectRootForLaneMutation(command, parsed) {
|
|
|
1679
2235
|
return ["acquire", "heartbeat", "release", "wait-acquire"].includes(action) ? requiredOption(parsed, "project-root") : undefined;
|
|
1680
2236
|
}
|
|
1681
2237
|
function projectRootForMergeQueueMutation(context, command, parsed) {
|
|
1682
|
-
if (["next", "wait-next"].includes(command ?? "")) {
|
|
2238
|
+
if (["next", "wait-next", "claim"].includes(command ?? "")) {
|
|
1683
2239
|
return requiredOption(parsed, "project-root");
|
|
1684
2240
|
}
|
|
1685
2241
|
if (["complete", "fail", "cancel"].includes(command ?? "")) {
|
|
@@ -1690,6 +2246,27 @@ function projectRootForMergeQueueMutation(context, command, parsed) {
|
|
|
1690
2246
|
function protocolProjectRoot(context, protocolId) {
|
|
1691
2247
|
return requireProtocol(context, protocolId).project_root;
|
|
1692
2248
|
}
|
|
2249
|
+
function runnerIntake(context) {
|
|
2250
|
+
const input = context.value.task_input?.[0];
|
|
2251
|
+
if (!input?.path)
|
|
2252
|
+
throw new AppError("validation", "Runner SPECIFY context requires one ordered task_input file", 2);
|
|
2253
|
+
const file = path.resolve(input.path);
|
|
2254
|
+
if (!fs.existsSync(file) || !fs.statSync(file).isFile())
|
|
2255
|
+
throw new AppError("not_found", "Runner SPECIFY task input is missing", 2, { task_input: file });
|
|
2256
|
+
return fs.readFileSync(file, "utf8");
|
|
2257
|
+
}
|
|
2258
|
+
/** Stage services install context before their lifecycle transition. Keeping
|
|
2259
|
+
* this check here makes an incomplete service migration fail closed instead of
|
|
2260
|
+
* silently materialising evidence after a Stage has become running. */
|
|
2261
|
+
function attachExternalStageContext(result, loaded) {
|
|
2262
|
+
if (!loaded)
|
|
2263
|
+
return result;
|
|
2264
|
+
const promptPath = typeof result.prompt_path === "string" ? result.prompt_path : null;
|
|
2265
|
+
if (!promptPath || !result.external_context || typeof result.external_context !== "object") {
|
|
2266
|
+
throw new AppError("stage_context_not_installed", "vNext stage service did not atomically install the supplied runner context", 1);
|
|
2267
|
+
}
|
|
2268
|
+
return { ...result, worker_prompt_markdown: fs.readFileSync(promptPath, "utf8") };
|
|
2269
|
+
}
|
|
1693
2270
|
function isScopedProtocolMutation(family, command) {
|
|
1694
2271
|
if (family === "transition")
|
|
1695
2272
|
return true;
|
|
@@ -1700,7 +2277,7 @@ function isScopedProtocolMutation(family, command) {
|
|
|
1700
2277
|
if (family === "worktree")
|
|
1701
2278
|
return ["create", "bootstrap", "close"].includes(command ?? "");
|
|
1702
2279
|
if (family === "merge-queue")
|
|
1703
|
-
return ["complete", "note", "fail", "cancel"].includes(command ?? "");
|
|
2280
|
+
return ["claim", "complete", "note", "fail", "cancel"].includes(command ?? "");
|
|
1704
2281
|
return false;
|
|
1705
2282
|
}
|
|
1706
2283
|
function projectRootFromResult(result) {
|