@deksden-com/dd-flow-cli 0.9.0-beta.1 → 0.9.0-beta.100
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 +561 -0
- package/README.md +68 -0
- package/dist/build-info.json +8 -8
- package/dist/cli/command-inputs.js +305 -0
- package/dist/cli/help.js +91 -15
- package/dist/cli/hook-ingress.js +82 -0
- package/dist/cli/input-preparation.js +116 -0
- package/dist/cli/run-cli.js +1506 -334
- package/dist/cli.js +6 -2
- package/dist/harness-runtime/DD-ZCODE.md +99 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +39 -0
- package/dist/harness-runtime/bin/dd-codex.mjs +25 -0
- package/dist/harness-runtime/bin/dd-droid.mjs +33 -0
- package/dist/harness-runtime/bin/dd-grok.mjs +30 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +21 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +83 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +215 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +449 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +59 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +146 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +562 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +125 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +468 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +300 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +174 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +217 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +98 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +644 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +941 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +10 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +130 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +129 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +267 -0
- package/dist/harness-runtime/lib/model-observations.mjs +77 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +1 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +34 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +36 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +19 -0
- package/dist/harness-runtime/lib/process-json.mjs +70 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/runner-events.mjs +200 -0
- package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
- package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
- package/dist/harness-runtime/lib/tool-observations.d.mts +17 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +153 -0
- package/dist/harness-runtime/lib/zcode-usage.mjs +54 -0
- package/dist/runtime/context.js +3 -1
- package/dist/schemas/agent-profile.schema.json +1 -1
- package/dist/schemas/code-review-result.schema.json +2 -2
- package/dist/schemas/code-work-batch.schema.json +4 -3
- package/dist/schemas/code-work-result.schema.json +4 -4
- package/dist/schemas/harness-config.schema.json +23 -0
- package/dist/schemas/plan-review-decision.schema.json +1 -1
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/run-control-receipt.schema.json +99 -0
- package/dist/schemas/run-control-request.schema.json +36 -0
- package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
- package/dist/services/canon.js +11 -8
- package/dist/services/cleanup.js +75 -21
- package/dist/services/cli-operation-classifier.js +22 -24
- package/dist/services/code-checks.js +676 -79
- package/dist/services/codex-hook-delivery.js +28 -0
- package/dist/services/command-context.js +80 -0
- package/dist/services/config.js +11 -8
- package/dist/services/continuation-outcome.js +20 -0
- package/dist/services/controller-fanout.js +134 -0
- package/dist/services/dashboard.js +103 -31
- package/dist/services/engines.js +125 -80
- package/dist/services/eval-snapshots.js +838 -74
- package/dist/services/execution-policy.js +170 -0
- package/dist/services/external-work-launch.js +110 -0
- package/dist/services/harness-adapter.js +147 -24
- package/dist/services/harness-config.js +68 -0
- package/dist/services/hooks.js +609 -310
- package/dist/services/lanes.js +62 -53
- package/dist/services/lifecycle-command.js +103 -17
- package/dist/services/lifecycle-contract.js +30 -0
- package/dist/services/lifecycle-invocations.js +1128 -0
- package/dist/services/managed-daemon-binding.js +38 -0
- package/dist/services/managed-processes.js +425 -0
- package/dist/services/merge-queue.js +179 -104
- package/dist/services/merge-server.js +78 -27
- package/dist/services/migrations.js +14 -9
- package/dist/services/native-daemon-history.js +49 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/plan-runtime.js +19 -8
- package/dist/services/plans.js +28 -25
- package/dist/services/portable-refs.js +57 -0
- package/dist/services/projects.js +18 -5
- package/dist/services/prompts.js +27 -18
- package/dist/services/protocols.js +64 -20
- package/dist/services/recovery-observation-budget.js +61 -0
- package/dist/services/recovery-snapshot-database.js +107 -0
- package/dist/services/repair-intents.js +40 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +363 -0
- package/dist/services/run-control.js +876 -0
- package/dist/services/run-controller-adapter.js +222 -0
- package/dist/services/run-controller-capture.js +136 -0
- package/dist/services/run-controller-process.js +189 -0
- package/dist/services/run-controller-recovery.js +222 -0
- package/dist/services/run-controller-state.js +36 -0
- package/dist/services/run-controller.js +803 -0
- package/dist/services/run-engine-bindings.js +20 -62
- package/dist/services/run-fork.js +138 -0
- package/dist/services/run-observations.js +112 -0
- package/dist/services/run-projection.js +10 -8
- package/dist/services/run-recovery-runtime.js +69 -0
- package/dist/services/run-recovery.js +343 -0
- package/dist/services/runs.js +437 -81
- package/dist/services/runtime-budget.js +439 -0
- package/dist/services/runtime-command.js +43 -0
- package/dist/services/runtime-scope-capture.js +50 -0
- package/dist/services/runtime-scope-control.js +450 -0
- package/dist/services/runtime-scope-resume.js +565 -0
- package/dist/services/runtime-scope-stop.js +99 -0
- package/dist/services/runtime-scope-worker.js +243 -0
- package/dist/services/runtime-service.js +97 -0
- package/dist/services/schema-validation.js +21 -19
- package/dist/services/session-identity.js +19 -0
- package/dist/services/sessions.js +61 -80
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +44 -18
- package/dist/services/stage-lifecycle.js +112 -108
- package/dist/services/stage-pause.js +117 -73
- package/dist/services/stage-work-graph.js +35 -0
- package/dist/services/usage.js +108 -132
- package/dist/services/vnext-code-review.js +245 -98
- package/dist/services/vnext-code.js +355 -257
- package/dist/services/vnext-execution-profile.js +5 -3
- package/dist/services/vnext-fanout.js +166 -26
- package/dist/services/vnext-merge.js +413 -147
- package/dist/services/vnext-plan-review.js +182 -109
- package/dist/services/vnext-plan.js +199 -82
- package/dist/services/vnext-protocolize.js +133 -60
- package/dist/services/vnext-specify.js +94 -71
- package/dist/services/work-registry.js +813 -160
- package/dist/services/workspace-bootstrap.js +76 -0
- package/dist/services/worktrees.js +85 -35
- package/dist/shared/entity-references.js +8 -0
- package/dist/shared/errors.js +12 -0
- package/dist/storage/database.js +569 -16
- package/dist/storage/paths.js +17 -4
- package/dist/storage/work-references.js +23 -0
- package/dist/storage/writer-contract.js +77 -0
- package/dist/storage/writer-migration.js +102 -0
- package/package.json +24 -13
- package/tools/audit-runtime-fix-boundaries.mjs +96 -0
- package/tools/repair-paused-run-status.mjs +59 -0
package/dist/services/cleanup.js
CHANGED
|
@@ -2,12 +2,12 @@ import fs from "node:fs";
|
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { AppError } from "../shared/errors.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { projectCheckoutRoot, resolveProjectRoot } from "../storage/paths.js";
|
|
5
|
+
import { prepareJsonFile } from "../cli/input-preparation.js";
|
|
6
|
+
import { projectCheckoutRoot, resolveProjectRoot, runtimeStateJsonPath } from "../storage/paths.js";
|
|
8
7
|
import { appendAudit } from "./audit.js";
|
|
9
8
|
import { requireProjectByRoot } from "./projects.js";
|
|
10
|
-
import { protocolRunDiagnostics, readProtocolRuntimeState, requireProtocol } from "./protocols.js";
|
|
9
|
+
import { protocolRunDiagnostics, readProtocolRuntimeState, repairProtocolRuntimeState, requireProtocol } from "./protocols.js";
|
|
10
|
+
import { publicSessionIdentity } from "./session-identity.js";
|
|
11
11
|
const actionKinds = new Set([
|
|
12
12
|
"repair_protocol_state",
|
|
13
13
|
"cancel_queue_job",
|
|
@@ -24,7 +24,8 @@ export function cleanupScan(context, input) {
|
|
|
24
24
|
const protocols = context.db.all("SELECT id, status, stage, state_path FROM protocols WHERE project_id = ? ORDER BY id ASC", [project.id]);
|
|
25
25
|
for (const protocol of protocols) {
|
|
26
26
|
const terminal = ["closed", "cancelled"].includes(protocol.status) || ["closed", "cancelled"].includes(protocol.stage);
|
|
27
|
-
|
|
27
|
+
const requiresMigration = protocol.state_path !== runtimeStateJsonPath(context.ddFlowHome, project.id, protocol.id);
|
|
28
|
+
if (!fs.existsSync(protocol.state_path) || requiresMigration) {
|
|
28
29
|
pushFinding(findings, actions, "missing_protocol_runtime_state", "warning", {
|
|
29
30
|
protocol_id: protocol.id,
|
|
30
31
|
path: protocol.state_path,
|
|
@@ -42,6 +43,8 @@ export function cleanupScan(context, input) {
|
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
const fullProtocol = requireProtocol(context, protocol.id, project.id);
|
|
46
|
+
if (requiresMigration)
|
|
47
|
+
continue;
|
|
45
48
|
const state = readProtocolRuntimeState(context, fullProtocol).state;
|
|
46
49
|
const diagnostics = protocolRunDiagnostics(context, fullProtocol, state).diagnostics;
|
|
47
50
|
for (const diagnostic of diagnostics.filter((entry) => entry.code === "protocol_run_stage_mismatch")) {
|
|
@@ -68,7 +71,7 @@ export function cleanupScan(context, input) {
|
|
|
68
71
|
action: { kind: "cancel_queue_job", project_id: project.id, protocol_id: job.protocol_id }
|
|
69
72
|
});
|
|
70
73
|
}
|
|
71
|
-
const staleSessions = context.db.all(`SELECT session_id, protocol_id, workspace_path FROM sessions
|
|
74
|
+
const staleSessions = context.db.all(`SELECT session_id, harness, provider_session_id, protocol_id, workspace_path FROM sessions
|
|
72
75
|
WHERE project_id = ? AND status IN ('pending', 'active', 'waiting_user', 'blocked', 'stopping')
|
|
73
76
|
ORDER BY updated_at ASC, session_id ASC`, [project.id]);
|
|
74
77
|
for (const session of staleSessions) {
|
|
@@ -79,11 +82,11 @@ export function cleanupScan(context, input) {
|
|
|
79
82
|
const terminalProtocol = Boolean(protocol && (["closed", "cancelled"].includes(protocol.status) || ["closed", "cancelled"].includes(protocol.stage)));
|
|
80
83
|
if (missingWorkspace || terminalProtocol) {
|
|
81
84
|
pushFinding(findings, actions, "stale_flow_session", "warning", {
|
|
82
|
-
|
|
85
|
+
session: publicSessionIdentity(session),
|
|
83
86
|
protocol_id: session.protocol_id,
|
|
84
87
|
missing_workspace: missingWorkspace,
|
|
85
88
|
terminal_protocol: terminalProtocol,
|
|
86
|
-
action: { kind: "close_flow_session", project_id: project.id,
|
|
89
|
+
action: { kind: "close_flow_session", project_id: project.id, session: publicSessionIdentity(session) }
|
|
87
90
|
});
|
|
88
91
|
}
|
|
89
92
|
}
|
|
@@ -169,20 +172,29 @@ export function cleanupScan(context, input) {
|
|
|
169
172
|
plan: { project_root: project.root, project_id: project.id, actions }
|
|
170
173
|
};
|
|
171
174
|
}
|
|
172
|
-
export function
|
|
173
|
-
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
175
|
+
export function prepareCleanupApply(context, input) {
|
|
174
176
|
const reason = input.reason.trim();
|
|
175
177
|
if (!reason) {
|
|
176
178
|
throw new AppError("validation", "cleanup apply requires --reason", 2);
|
|
177
179
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
const value = prepareJsonFile(input.planFile, "cleanup plan").value;
|
|
181
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
182
|
+
throw new AppError("validation", "cleanup plan must be an object", 2);
|
|
183
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
184
|
+
const plan = validateCleanupPlan(value, project);
|
|
180
185
|
validateNoDestructiveSurprises(context, plan, input.force);
|
|
186
|
+
return { project, reason, plan };
|
|
187
|
+
}
|
|
188
|
+
export function cleanupApply(context, input) {
|
|
189
|
+
const { project, reason, plan } = input.prepared ?? prepareCleanupApply(context, input);
|
|
181
190
|
const now = context.now();
|
|
182
191
|
const results = [];
|
|
183
|
-
context.db.
|
|
192
|
+
context.db.beginWriteTransaction();
|
|
193
|
+
let executing = false;
|
|
184
194
|
try {
|
|
195
|
+
validateNoDestructiveSurprises(context, plan, input.force);
|
|
185
196
|
for (const action of plan.actions) {
|
|
197
|
+
executing = true;
|
|
186
198
|
results.push(applyAction(context, action, reason, input.force, now));
|
|
187
199
|
}
|
|
188
200
|
const changed = results.filter((result) => result.changed).length;
|
|
@@ -199,6 +211,8 @@ export function cleanupApply(context, input) {
|
|
|
199
211
|
}
|
|
200
212
|
catch (error) {
|
|
201
213
|
context.db.exec("ROLLBACK");
|
|
214
|
+
if (executing)
|
|
215
|
+
throw new AppError("cleanup_apply_failed", "Cleanup failed after execution began; inspect retained artifacts before retrying", 1, { effect: "unknown", database_rolled_back: true, attempted_results: results, cause: error instanceof Error ? error.message : String(error), ...(error instanceof AppError ? { cause_code: error.code } : {}) });
|
|
202
216
|
throw error;
|
|
203
217
|
}
|
|
204
218
|
return {
|
|
@@ -241,11 +255,35 @@ function validateCleanupPlan(value, project) {
|
|
|
241
255
|
actual_project_id: action.project_id
|
|
242
256
|
});
|
|
243
257
|
}
|
|
258
|
+
if (action.kind === "close_flow_session") {
|
|
259
|
+
const session = action.session;
|
|
260
|
+
if (!session || typeof session.harness_id !== "string" || !session.harness_id || typeof session.session_id !== "string" || !session.session_id) {
|
|
261
|
+
throw new AppError("validation", "close_flow_session requires a native session identity", 2);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const requiredField = action.kind === "discard_stale_run" ? "run_id"
|
|
265
|
+
: ["repair_protocol_state", "cancel_queue_job", "close_worktree_record"].includes(String(action.kind)) ? "protocol_id" : undefined;
|
|
266
|
+
if (requiredField && (typeof action[requiredField] !== "string" || !action[requiredField].trim()))
|
|
267
|
+
throw new AppError("validation", `cleanup ${String(action.kind)} requires ${requiredField}`, 2);
|
|
268
|
+
const numericField = action.kind === "expire_lane_lock" ? "lock_id" : action.kind === "expire_lane_waiter" ? "waiter_id" : undefined;
|
|
269
|
+
if (numericField && (!Number.isSafeInteger(action[numericField]) || action[numericField] < 1))
|
|
270
|
+
throw new AppError("validation", `cleanup ${String(action.kind)} requires a positive integer ${numericField}`, 2);
|
|
271
|
+
if (action.worktree_path !== undefined && (typeof action.worktree_path !== "string" || !path.isAbsolute(action.worktree_path)))
|
|
272
|
+
throw new AppError("validation", "cleanup worktree_path must be an absolute path", 2);
|
|
244
273
|
return action;
|
|
245
274
|
});
|
|
246
275
|
return { project_root: project.root, project_id: project.id, actions };
|
|
247
276
|
}
|
|
248
277
|
function validateNoDestructiveSurprises(context, plan, force) {
|
|
278
|
+
for (const action of plan.actions) {
|
|
279
|
+
if (action.kind === "repair_protocol_state")
|
|
280
|
+
requireProtocol(context, action.protocol_id, action.project_id);
|
|
281
|
+
if (action.kind === "discard_stale_run") {
|
|
282
|
+
const run = context.db.get("SELECT index_json, status FROM runs WHERE project_id = ? AND id = ?", [action.project_id, action.run_id]);
|
|
283
|
+
if (run && ["running", "discarded"].includes(run.status))
|
|
284
|
+
cleanupRunIndex(run.index_json);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
249
287
|
const destructive = plan.actions.find((action) => action.kind === "close_worktree_record" && action.worktree_path && dirtyGit(action.worktree_path));
|
|
250
288
|
if (destructive && !force) {
|
|
251
289
|
throw new AppError("worktree_dirty", "cleanup apply refuses dirty worktree actions without --force --reason", 1, {
|
|
@@ -276,7 +314,7 @@ function applyAction(context, action, reason, force, now) {
|
|
|
276
314
|
const protocol = requireProtocol(context, action.protocol_id, action.project_id);
|
|
277
315
|
const beforePath = protocol.state_path;
|
|
278
316
|
const existed = fs.existsSync(beforePath);
|
|
279
|
-
|
|
317
|
+
repairProtocolRuntimeState(context, protocol);
|
|
280
318
|
return {
|
|
281
319
|
kind: action.kind,
|
|
282
320
|
changed: !existed || beforePath !== protocol.state_path,
|
|
@@ -305,20 +343,23 @@ function applyAction(context, action, reason, force, now) {
|
|
|
305
343
|
});
|
|
306
344
|
return { kind: action.kind, changed: true, protocol_id: action.protocol_id };
|
|
307
345
|
}
|
|
308
|
-
if (action.kind === "close_flow_session" && action.
|
|
346
|
+
if (action.kind === "close_flow_session" && action.session) {
|
|
347
|
+
const stored = context.db.get("SELECT session_id FROM sessions WHERE project_id = ? AND harness = ? AND COALESCE(provider_session_id, session_id) = ?", [action.project_id, action.session.harness_id, action.session.session_id]);
|
|
348
|
+
if (!stored)
|
|
349
|
+
return { kind: action.kind, changed: false, skipped: true, reason: "session_not_found", session: action.session };
|
|
309
350
|
const result = context.db.run(`UPDATE sessions SET status = 'stopped', stop_reason = ?, updated_at = ?, stopped_at = ?
|
|
310
|
-
WHERE project_id = ? AND session_id = ? AND status IN ('pending', 'active', 'waiting_user', 'blocked', 'stopping')`, [reason, now, now, action.project_id,
|
|
351
|
+
WHERE project_id = ? AND session_id = ? AND status IN ('pending', 'active', 'waiting_user', 'blocked', 'stopping')`, [reason, now, now, action.project_id, stored.session_id]);
|
|
311
352
|
if (result.changes !== 1) {
|
|
312
|
-
return { kind: action.kind, changed: false, skipped: true, reason: "already_stopped",
|
|
353
|
+
return { kind: action.kind, changed: false, skipped: true, reason: "already_stopped", session: action.session };
|
|
313
354
|
}
|
|
314
355
|
appendAudit(context, {
|
|
315
356
|
projectId: action.project_id,
|
|
316
357
|
eventType: "flow_session.stopped",
|
|
317
358
|
reason,
|
|
318
359
|
forced: force,
|
|
319
|
-
payload: { session_id:
|
|
360
|
+
payload: { session_id: stored.session_id, source: "cleanup.apply" }
|
|
320
361
|
});
|
|
321
|
-
return { kind: action.kind, changed: true,
|
|
362
|
+
return { kind: action.kind, changed: true, session: action.session };
|
|
322
363
|
}
|
|
323
364
|
if (action.kind === "discard_stale_run" && action.run_id) {
|
|
324
365
|
const run = context.db.get("SELECT status, runtime_path, run_dir, workspace_root, index_json FROM runs WHERE project_id = ? AND id = ?", [action.project_id, action.run_id]);
|
|
@@ -397,7 +438,7 @@ function applyAction(context, action, reason, force, now) {
|
|
|
397
438
|
return { kind: action.kind, changed: false, skipped: true, reason: "invalid_action" };
|
|
398
439
|
}
|
|
399
440
|
function discardedRunIndex(indexJson, reason, now) {
|
|
400
|
-
const index =
|
|
441
|
+
const index = cleanupRunIndex(indexJson);
|
|
401
442
|
index.schema_id = "dd-flow/flow-run@3";
|
|
402
443
|
index.status = "discarded";
|
|
403
444
|
index.verdict = "discarded";
|
|
@@ -407,12 +448,25 @@ function discardedRunIndex(indexJson, reason, now) {
|
|
|
407
448
|
index.finished_at ??= now;
|
|
408
449
|
return index;
|
|
409
450
|
}
|
|
451
|
+
function cleanupRunIndex(indexJson) {
|
|
452
|
+
try {
|
|
453
|
+
const index = JSON.parse(indexJson);
|
|
454
|
+
if (!index || typeof index !== "object" || Array.isArray(index))
|
|
455
|
+
throw new Error("RUN index must be an object");
|
|
456
|
+
return index;
|
|
457
|
+
}
|
|
458
|
+
catch (error) {
|
|
459
|
+
throw new AppError("runtime_state_invalid", "Cleanup cannot read retained RUN state", 1, { cause: String(error) });
|
|
460
|
+
}
|
|
461
|
+
}
|
|
410
462
|
function dirtyGit(worktreePath) {
|
|
411
463
|
if (!fs.existsSync(worktreePath)) {
|
|
412
464
|
return false;
|
|
413
465
|
}
|
|
414
466
|
const result = spawnSync("git", ["-C", worktreePath, "status", "--porcelain"], { encoding: "utf8" });
|
|
415
|
-
|
|
467
|
+
if (result.error || result.status !== 0)
|
|
468
|
+
throw new AppError("workspace_status_failed", "Cannot establish worktree status before cleanup", 1, { path: worktreePath, cause: result.error?.message ?? result.stderr });
|
|
469
|
+
return result.stdout.trim().length > 0;
|
|
416
470
|
}
|
|
417
471
|
function safeCleanupWorktreePath(context, projectId, worktreePath) {
|
|
418
472
|
if (!fs.existsSync(worktreePath)) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseCommandArgs } from "./lifecycle-command.js";
|
|
1
2
|
export function classifyCliOperation(args, env = process.env) {
|
|
2
3
|
const positional = positionalArgs(args);
|
|
3
4
|
const [family = null, command = null, action = null] = positional;
|
|
@@ -14,19 +15,7 @@ export function classifyCliOperation(args, env = process.env) {
|
|
|
14
15
|
return { mode: "normal_write", operation, family, command, action };
|
|
15
16
|
}
|
|
16
17
|
function positionalArgs(args) {
|
|
17
|
-
|
|
18
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
19
|
-
const value = args[index];
|
|
20
|
-
if (!value)
|
|
21
|
-
continue;
|
|
22
|
-
if (value.startsWith("--")) {
|
|
23
|
-
if (!value.includes("=") && args[index + 1] && !args[index + 1]?.startsWith("--"))
|
|
24
|
-
index += 1;
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
positional.push(value);
|
|
28
|
-
}
|
|
29
|
-
return positional;
|
|
18
|
+
return parseCommandArgs(args).positional;
|
|
30
19
|
}
|
|
31
20
|
export function isReadOnlyCliOperation(args, env = process.env) {
|
|
32
21
|
return classifyCliOperation(args, env).mode === "read_only_diagnostics";
|
|
@@ -57,7 +46,7 @@ function isMbUpgradeMode(args, env) {
|
|
|
57
46
|
}
|
|
58
47
|
return marked && isUpgradeAllowlisted(args);
|
|
59
48
|
}
|
|
60
|
-
function isUpgradeAllowlisted(args) {
|
|
49
|
+
export function isUpgradeAllowlisted(args) {
|
|
61
50
|
const positional = positionalArgs(args);
|
|
62
51
|
const [family, command] = positional;
|
|
63
52
|
if (family === "status")
|
|
@@ -82,14 +71,9 @@ function hasRunBinding(args) {
|
|
|
82
71
|
return Boolean(optionValue(args, "run") || positionalArgs(args).some((value) => value.startsWith("RUN-")));
|
|
83
72
|
}
|
|
84
73
|
function optionValue(args, name) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return args[index + 1] ?? null;
|
|
89
|
-
if (arg?.startsWith(`--${name}=`))
|
|
90
|
-
return arg.slice(name.length + 3);
|
|
91
|
-
}
|
|
92
|
-
return null;
|
|
74
|
+
const values = parseCommandArgs(args).options.get(name);
|
|
75
|
+
const value = values?.[values.length - 1];
|
|
76
|
+
return value && value !== "__dd_flow_flag__" ? value : null;
|
|
93
77
|
}
|
|
94
78
|
function isRouterNative(args) {
|
|
95
79
|
const first = args[0];
|
|
@@ -101,11 +85,17 @@ function isRouterNative(args) {
|
|
|
101
85
|
return true;
|
|
102
86
|
if (["engine", "version", "schema"].includes(first))
|
|
103
87
|
return true;
|
|
88
|
+
if (first === "storage" && args[1] === "migrate-writer")
|
|
89
|
+
return true;
|
|
104
90
|
return (first === "codex" && args[1] === "hook" && args[2] === "handle")
|
|
91
|
+
|| (first === "zcode" && args[1] === "hook" && args[2] === "handle")
|
|
92
|
+
|| (first === "zcode" && args[1] === "invocation" && args[2] === "prepare")
|
|
105
93
|
|| (first === "zcode" && args[1] === "event" && args[2] === "handle")
|
|
106
94
|
|| (first === "zcode" && args[1] === "usage" && args[2] === "ingest")
|
|
107
95
|
|| (first === "grok" && args[1] === "event" && args[2] === "handle")
|
|
108
96
|
|| (first === "grok" && args[1] === "usage" && args[2] === "ingest")
|
|
97
|
+
|| (first === "droid" && args[1] === "event" && args[2] === "handle")
|
|
98
|
+
|| (first === "droid" && args[1] === "usage" && args[2] === "ingest")
|
|
109
99
|
|| (first === "opencode" && args[1] === "event" && args[2] === "handle")
|
|
110
100
|
|| (first === "opencode" && args[1] === "usage" && args[2] === "ingest")
|
|
111
101
|
|| (first === "agy" && args[1] === "event" && args[2] === "handle")
|
|
@@ -119,13 +109,21 @@ function isReadOnlyDiagnostic(family, command, action) {
|
|
|
119
109
|
if (family === "canon")
|
|
120
110
|
return command === "status" || command === "resolve";
|
|
121
111
|
if (family === "project")
|
|
122
|
-
return command === "status" || command === "resolve";
|
|
112
|
+
return command === "status" || command === "resolve" || (command === "config" && action === "status");
|
|
123
113
|
if (family === "protocol")
|
|
124
114
|
return ["status", "branch-status", "ready", "blockers", "implement"].includes(command ?? "");
|
|
125
115
|
if (family === "plan")
|
|
126
116
|
return command === "status";
|
|
127
117
|
if (family === "run")
|
|
128
|
-
return command === "status" || command === "
|
|
118
|
+
return ["status", "list", "timeline"].includes(command ?? "") || (["config", "flags", "drive", "control"].includes(command ?? "") && action === "status") || (command === "vars" && ["ls", "get"].includes(action ?? "")) || (command === "recovery" && action === "inspect");
|
|
119
|
+
if (family === "runtime" && command === "process" && ["status", "check-admission"].includes(action ?? ""))
|
|
120
|
+
return true;
|
|
121
|
+
if (family === "runtime" && command === "scope" && action === "status")
|
|
122
|
+
return true;
|
|
123
|
+
if (family === "work")
|
|
124
|
+
return ["status", "ls", "show"].includes(command ?? "");
|
|
125
|
+
if (family === "stage")
|
|
126
|
+
return command === "fanout" && action === "status";
|
|
129
127
|
if (family === "lane") {
|
|
130
128
|
return (command === "status" ||
|
|
131
129
|
command === "waiters" ||
|