@deksden-com/dd-flow-cli 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +2 -0
  2. package/dist/build-info.json +10 -6
  3. package/dist/cli/help.js +70 -13
  4. package/dist/cli/run-cli.js +140 -10
  5. package/dist/schemas/compatibility.schema.json +26 -0
  6. package/dist/schemas/flow-guidance.schema.json +73 -0
  7. package/dist/schemas/flow-run-index.schema.json +30 -4
  8. package/dist/schemas/global-dashboard-data.schema.json +68 -0
  9. package/dist/schemas/mb-sdlc-review-report.schema.json +242 -0
  10. package/dist/schemas/plan-stage-report.schema.json +83 -0
  11. package/dist/schemas/project-dashboard-data.schema.json +98 -0
  12. package/dist/schemas/project-flow-pack-manifest.schema.json +5 -1
  13. package/dist/schemas/protocol-dashboard-data.schema.json +89 -0
  14. package/dist/schemas/status-report.schema.json +34 -0
  15. package/dist/schemas/version-report.schema.json +22 -0
  16. package/dist/services/build-info.js +26 -3
  17. package/dist/services/canon.js +93 -22
  18. package/dist/services/cleanup.js +14 -1
  19. package/dist/services/config.js +25 -0
  20. package/dist/services/dashboard.js +655 -10
  21. package/dist/services/flow-guidance.js +214 -0
  22. package/dist/services/ids.js +106 -0
  23. package/dist/services/merge-queue.js +9 -1
  24. package/dist/services/merge-worker.js +31 -2
  25. package/dist/services/projects.js +7 -1
  26. package/dist/services/protocols.js +637 -6
  27. package/dist/services/runs.js +98 -21
  28. package/dist/services/schema-validation.js +84 -4
  29. package/dist/services/status.js +189 -1
  30. package/dist/services/version-status.js +28 -2
  31. package/dist/storage/database.js +6 -0
  32. package/dist/storage/paths.js +21 -0
  33. package/package.json +1 -1
package/README.md CHANGED
@@ -229,6 +229,8 @@ Defaults:
229
229
  ```text
230
230
  project dashboard: .tasks/dd-flow-dashboard.md
231
231
  global dashboard: ~/.dd-flow/dashboard.md
232
+ project HTML: ~/.dd-flow/projects/<PRJ-ID>/dashboard/project-dashboard.html
233
+ protocol HTML: ~/.dd-flow/projects/<PRJ-ID>/dashboard/protocols/<PRT-ID>.html
232
234
  ```
233
235
 
234
236
  Useful commands:
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "cli_package": "@deksden-com/dd-flow-cli",
3
- "cli_version": "0.2.0",
4
- "cli_commit": "8351d94bdeaca22d2fff503fd2b08071c86205cd",
5
- "built_at": "2026-06-15T14:46:55.550Z",
3
+ "cli_version": "0.3.0",
4
+ "cli_commit": "059594c2096e080548d470e789f594e20a1a8ebd",
5
+ "built_at": "2026-06-30T20:32:13.018Z",
6
6
  "built_with_canon": {
7
- "version": "2.4.0",
8
- "commit": "cf1f6d08e8d4775bf14334c6010b479d1c6fd73f",
9
- "flow_contract": "dd-flow-canonical-2026-05"
7
+ "version": "2.8.0",
8
+ "commit": "f549c33bd73d8d18b5cb07d9737b02fe638e61b8",
9
+ "flow_contract": "dd-flow-canonical-2026-06",
10
+ "repo_root": "/Users/deksden/Documents/_Projects/dd-memorybank",
11
+ "memorybank_root": "/Users/deksden/Documents/_Projects/dd-memorybank/.memory-bank",
12
+ "flow_root": "/Users/deksden/Documents/_Projects/dd-memorybank/.memory-bank/dd-flow",
13
+ "layout": "dot_memory_bank"
10
14
  }
11
15
  }
package/dist/cli/help.js CHANGED
@@ -9,21 +9,23 @@ Usage:
9
9
  dd-flow <command> --help
10
10
 
11
11
  Core commands:
12
+ version Print the installed dd-flow CLI package version.
12
13
  status Inspect dd-flow runtime, project and canon roots.
13
14
  canon register/status/resolve Register and resolve the canonical Memory Bank.
14
15
  project register/status Register a project and inspect runtime state.
15
16
  project resolve/archive Resolve typed ids and archive stale project roots.
16
17
  project migrate-ids Migrate old project ids.
17
- protocol register/status Register and inspect protocol state.
18
+ protocol register/status/transition Register, inspect, transition and repair protocol state.
18
19
  cleanup scan/apply Detect and repair stale local runtime state.
19
20
  run start/status/list Track concrete flow executions and stage artifacts.
21
+ id next Preview next typed ids for protocol or run allocation.
20
22
  plan set/status/item Attach and update protocol plans.
21
23
  lane workspace/lock/status Manage shared workspace lanes and leases.
22
24
  merge status/one-shot Inspect or claim one merge job from the current session.
23
25
  merge-worker status/start/stop Manage long-lived project merge workers.
24
26
  merge-queue status/next Claim, wait for, complete, or fail merge jobs.
25
27
  session register/status/stop Register flow sessions and stop workers.
26
- dashboard render/open/refresh Render markdown project/global dashboards.
28
+ dashboard data/render/open/refresh Render markdown or HTML project/global dashboards.
27
29
  schema validate Validate canonical dd-flow JSON data contracts.
28
30
  memory permissions preflight Check Memory Bank write/read permissions.
29
31
  integration cmux status Inspect optional cmux integration.
@@ -32,6 +34,7 @@ Core commands:
32
34
 
33
35
  Examples:
34
36
  dd-flow project register --root "$PWD"
37
+ dd-flow version
35
38
  dd-flow status --project-root "$PWD" --json
36
39
  dd-flow canon register --root "$DD_MEMORYBANK" --json
37
40
  dd-flow canon resolve --json
@@ -39,7 +42,9 @@ Examples:
39
42
  dd-flow project resolve PRJ-001 --json
40
43
  dd-flow project archive PRJ-001 --reason "old missing worktree root" --json
41
44
  dd-flow project migrate-ids --root "$PWD" --apply --json
42
- dd-flow run start --project-root "$PWD" --flow-kind coding --subject-type protocol --subject-id PRT-001-demo --slug demo --json
45
+ dd-flow run start --project-root "$PWD" --flow-kind mb_sdlc --subject-type protocol --subject-id PRT-001-demo --slug demo --json
46
+ dd-flow id next --type protocol --project-root "$PWD" --slug demo --json
47
+ dd-flow protocol transition PRT-001-demo --to implementation --payload-file transition.json --json
43
48
  dd-flow lane workspace set --project-root "$PWD" --lane merge --path "$PWD" --branch main --json
44
49
  dd-flow lane lock acquire --project-root "$PWD" --lane merge --worker-id worker-1 --path "$PWD" --ttl 300 --reason "merge worker" --json
45
50
  dd-flow merge status --project-root "$PWD" --json
@@ -48,6 +53,21 @@ Examples:
48
53
  Default output is human-readable. Use --json for hooks, prompts, scripts and other agent automation; JSON mode never mixes human progress text into stdout.
49
54
 
50
55
  project_root is the stable repository identity. workspace_path is the concrete checkout where an agent is about to work.`
56
+ ],
57
+ [
58
+ "version",
59
+ `dd-flow version - print CLI package version
60
+
61
+ Usage:
62
+ dd-flow version
63
+ dd-flow version --json
64
+ dd-flow --version
65
+
66
+ version reports the installed dd-flow CLI package version from package metadata. Use status for richer canon/project compatibility diagnostics.
67
+
68
+ Examples:
69
+ dd-flow version
70
+ dd-flow version --json`
51
71
  ],
52
72
  [
53
73
  "status",
@@ -56,9 +76,12 @@ project_root is the stable repository identity. workspace_path is the concrete c
56
76
  Usage:
57
77
  dd-flow status [--project-root <root>] --json
58
78
  dd-flow status [--root <root>] --json
79
+ dd-flow status [--project-root <root>] [--check-registry] --json
59
80
 
60
81
  status reports DD_FLOW_HOME, the requested or current project root, registered project identity when available, and canonical Memory Bank discovery. It does not require the project to be registered and does not mutate state. In JSON mode canon discovery is embedded as a structured assessment so agents can detect missing or ambiguous canon roots without parsing stderr.
61
82
 
83
+ By default status does not call the network. Use --check-registry for an explicit cached npm latest check.
84
+
62
85
  Examples:
63
86
  dd-flow status --project-root "$PWD" --json
64
87
  dd-flow status --json`
@@ -87,18 +110,33 @@ Examples:
87
110
 
88
111
  Usage:
89
112
  dd-flow run start --project-root <root> [--workspace-root <checkout>] --flow-kind <kind> --subject-type <type> --subject-id <id> --slug <slug> [--next-action <text>] --json
113
+ dd-flow id next --type protocol|run --project-root <root> --slug <slug> --json
90
114
  dd-flow run status <RUN-ID|RUN-NNN> --project-root <root> --json
91
115
  dd-flow run list --project-root <root> --json
92
116
  dd-flow run attach-stage <RUN-ID|RUN-NNN> --project-root <root> --stage <name> --dir <NN-stage-slug> --status <status> [--data-schema-id <id>] --json
93
117
  dd-flow run complete-stage <RUN-ID|RUN-NNN> --project-root <root> --stage <name> --status <status> [--stage-report <path>] [--data <path>] [--data-schema-id <id>] [--report <path>] [--alias <path>] --json
94
118
  dd-flow run complete <RUN-ID|RUN-NNN> --project-root <root> --status done|blocked|cancelled|failed [--verdict <text>] [--next-action <text>] --json
95
119
 
96
- RUN-* is the execution envelope for one concrete flow launch. Semantic truth remains in protocol, experiment, DEF, scenario, evidence, and Memory Bank documents. Runtime state is stored under DD_FLOW_HOME/projects/<PRJ-ID-slug>/runtime/runs/<RUN-ID-slug>/, while human-facing artifacts are indexed through <workspace>/.tasks/dd-flow-runs/<RUN-ID-slug>/run-index.json.
120
+ RUN-* is the execution envelope for one concrete flow launch. Semantic truth remains in protocol, experiment, DEF, scenario, evidence, and Memory Bank documents. New run artifacts are stored under DD_FLOW_HOME/projects/<PRJ-ID-slug>/runs/<RUN-ID-slug>/ with run.json and run-index.json together. Legacy <workspace>/.tasks/dd-flow-runs/<RUN-ID-slug>/ indexes remain readable.
97
121
 
98
122
  Examples:
99
- dd-flow run start --project-root "$PWD" --flow-kind coding --subject-type protocol --subject-id PRT-001-demo --slug demo --json
123
+ dd-flow run start --project-root "$PWD" --flow-kind mb_sdlc --subject-type protocol --subject-id PRT-001-demo --slug demo --json
124
+ dd-flow id next --type run --project-root "$PWD" --slug demo --json
100
125
  dd-flow run attach-stage RUN-001 --project-root "$PWD" --stage plan --dir 01-plan --status running --json
101
126
  dd-flow run complete-stage RUN-001 --project-root "$PWD" --stage plan --status done --data 01-plan/stage-report.json --stage-report 01-plan/stage-report.html --report 01-plan/report.md --json`
127
+ ],
128
+ [
129
+ "id",
130
+ `dd-flow id - preview next typed ids
131
+
132
+ Usage:
133
+ dd-flow id next --type protocol|run --project-root <root> --slug <slug> --json
134
+
135
+ id next is read-only: it scans runtime records and known project files, then returns the next available TYPE-NNN-slug without reserving it. Use run start or protocol register to create durable state.
136
+
137
+ Examples:
138
+ dd-flow id next --type protocol --project-root "$PWD" --slug demo --json
139
+ dd-flow id next --type run --project-root "$PWD" --slug demo --json`
102
140
  ],
103
141
  [
104
142
  "protocol",
@@ -107,10 +145,27 @@ Examples:
107
145
  Usage:
108
146
  dd-flow protocol register <handshake-id> --project-root <root> [--workspace-path <checkout>] --json
109
147
  dd-flow protocol status <protocol-id> --json
148
+ dd-flow protocol ready --project-root <root> --json
149
+ dd-flow protocol blockers <protocol-id> --project-root <root> --json
150
+ dd-flow protocol implement <protocol-id> --project-root <root> [--force --reason <text>] --json
151
+ dd-flow protocol transition <protocol-id> --to <stage> --payload-file <file> [--force --reason <text>] --json
152
+ dd-flow protocol sync-from-run <protocol-id> --run <RUN-ID|RUN-NNN> [--target auto|<stage>] --json
110
153
  dd-flow protocol ready-for-merge <protocol-id> --json
111
154
  dd-flow protocol cancel <protocol-id> --reason <text> [--close-sessions true|false] [--cancel-queue true|false] [--release-locks true|false] [--worktree keep|remove] [--force] --json
112
155
 
156
+ ready, blockers and implement read protocol markdown frontmatter plus runtime state. implement is a preflight/guidance command: it never performs implementation, refuses unresolved blocked_by_protocols unless --force --reason is supplied, refuses terminal protocols and reports the expected next prompt/stage. transition validates the snapshotted flow contract stored in protocol runtime state, writes an audit event and updates lifecycle fields from the payload. --json-file is accepted as a compatibility alias for --payload-file. sync-from-run repairs legacy protocol/run mismatch from concrete RUN evidence instead of requiring manual runtime JSON edits.
157
+
113
158
  The project_root is the stable project identity used for queues, sessions, lanes, and dashboards. Runtime state is stored under DD_FLOW_HOME/projects/<PRJ-ID-slug>/runtime so feature worktree removal does not break status, cancel, or merge finalization. The optional workspace_path records the concrete checkout where the agent works. With --worktree remove, cancel removes the disposable git worktree recorded in runtime state or worktree records; with --force it also force-deletes the local feature branch when present.`
159
+ ],
160
+ [
161
+ "transition",
162
+ `dd-flow transition - compatibility alias for protocol transition
163
+
164
+ Usage:
165
+ dd-flow transition <protocol-id> --to <stage> --payload-file <file> [--force --reason <text>] --json
166
+ dd-flow transition <protocol-id> --to <stage> --json-file <file> [--force --reason <text>] --json
167
+
168
+ Prefer dd-flow protocol transition in new prompts and scripts. This top-level alias remains for older canonical prompts and experiments.`
114
169
  ],
115
170
  [
116
171
  "project",
@@ -277,16 +332,18 @@ Usage:
277
332
  ],
278
333
  [
279
334
  "dashboard",
280
- `dd-flow dashboard - render or open markdown dashboards
335
+ `dd-flow dashboard - render or open markdown and HTML dashboards
281
336
 
282
337
  Usage:
283
- dd-flow dashboard render --project-root <root> [--output <path>] --json
284
- dd-flow dashboard render-global [--output <path>] --json
285
- dd-flow dashboard open --project-root <root> [--viewer cmux] --json
286
- dd-flow dashboard refresh --project-root <root> [--open auto|true|false] --json
287
- dd-flow dashboard refresh-global [--output <path>] --json
288
-
289
- Dashboards are rendered views of dd-flow state. State-changing commands refresh markdown automatically when dashboard.auto_refresh is enabled. dashboard refresh rewrites project and global markdown when enabled. cmux is used only by explicit open/refresh commands and only according to project config.`
338
+ dd-flow dashboard data --project-root <root> [--protocol <PRT-ID>] --json
339
+ dd-flow dashboard data --global --json
340
+ dd-flow dashboard render --project-root <root> [--format markdown|html] [--protocol <PRT-ID>] [--output <path>] --json
341
+ dd-flow dashboard render-global [--format markdown|html] [--output <path>] --json
342
+ dd-flow dashboard open --project-root <root> [--format markdown|html] [--viewer cmux] --json
343
+ dd-flow dashboard refresh --project-root <root> [--format markdown|html] [--protocol <PRT-ID>] [--open auto|true|false] --json
344
+ dd-flow dashboard refresh-global [--format markdown|html] [--output <path>] --json
345
+
346
+ Dashboards are rendered views of dd-flow state. Markdown remains the compatibility fallback. HTML dashboards write JSON next to self-contained local HTML: global under DD_FLOW_HOME/dashboard/ and project/protocol pages under DD_FLOW_HOME/projects/<PRJ-ID-slug>/dashboard/. State-changing commands still auto-refresh markdown during the transition. cmux is used only by explicit open/refresh commands and only according to project config.`
290
347
  ],
291
348
  [
292
349
  "schema",
@@ -3,7 +3,7 @@ import { helpForArgs } from "./help.js";
3
3
  import { AppError, isAppError } from "../shared/errors.js";
4
4
  import { writeJson } from "../shared/json.js";
5
5
  import { archiveProject, getProjectStatus, migrateProjectIds, registerProject, resolveProject } from "../services/projects.js";
6
- import { cancelProtocol, getProtocolStatus, readyForMerge, registerProtocol, requireProtocol, transitionProtocol } from "../services/protocols.js";
6
+ import { cancelProtocol, getProtocolBlockers, getProtocolStatus, getReadyProtocols, implementProtocol, readyForMerge, registerProtocol, requireProtocol, syncProtocolFromRun, transitionProtocol } from "../services/protocols.js";
7
7
  import { blockPlanItem, completePlanItem, getPlanStatus, setProtocolPlan, skipPlanItem, startPlanItem } from "../services/plans.js";
8
8
  import { cancelMergeQueueJob, claimNextMergeJob, completeMergeJob, failMergeJob, getMergeQueueStatus, noteMergeJob, waitNextMergeJob } from "../services/merge-queue.js";
9
9
  import { getMergeWorkerStatus, oneShotMergeClaim, startMergeWorker, stopProjectMergeWorker } from "../services/merge-worker.js";
@@ -14,9 +14,11 @@ import { acquireLaneLock, checkLaneWorkspace, getLaneStatus, heartbeatLaneLock,
14
14
  import { getProjectConfigStatus, setProjectConfigValue } from "../services/config.js";
15
15
  import { getCanonStatus, registerCanonRoot, resolveCanonRoot } from "../services/canon.js";
16
16
  import { getRuntimeStatus } from "../services/status.js";
17
- import { autoRefreshDashboards, getCmuxStatus, openDashboard, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
17
+ import { getCliVersionReport } from "../services/build-info.js";
18
+ import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
18
19
  import { getFlowSessionStatus, registerFlowSession, stopFlowSession, stopMergeWorker } from "../services/sessions.js";
19
20
  import { attachFlowRunStage, completeFlowRun, completeFlowRunStage, getFlowRunStatus, listFlowRuns, startFlowRun } from "../services/runs.js";
21
+ import { previewNextEntityId } from "../services/ids.js";
20
22
  import { validateSchema } from "../services/schema-validation.js";
21
23
  import { preflightMemoryPermissions } from "../services/memory-permissions.js";
22
24
  import { requireProjectByRoot } from "../services/projects.js";
@@ -34,6 +36,14 @@ export async function runCli(args, io = defaultIo, env = process.env) {
34
36
  if (args.includes("--progress-jsonl")) {
35
37
  throw new AppError("usage", "--progress-jsonl is not implemented yet; use --json for final structured progress", 2);
36
38
  }
39
+ if (output.args.length === 1 && output.args[0] === "--version") {
40
+ const payload = getCliVersionReport();
41
+ if (output.json)
42
+ writeJson(io.stdout, payload);
43
+ else
44
+ io.stdout.write(`dd-flow ${payload.cli.version}\n`);
45
+ return 0;
46
+ }
37
47
  const help = helpForArgs(output.args);
38
48
  if (help) {
39
49
  io.stdout.write(`${help}\n`);
@@ -152,6 +162,10 @@ function renderHumanResult(args, result) {
152
162
  if (family === "status") {
153
163
  return renderStatusHuman(record);
154
164
  }
165
+ if (family === "version") {
166
+ const cli = recordObject(record.cli);
167
+ return `dd-flow ${stringValue(cli?.version) ?? "unknown"}\n`;
168
+ }
155
169
  const lines = [`dd-flow ${family ?? ""}${command ? ` ${command}` : ""}: ${ok}`.trim()];
156
170
  const project = record.project && typeof record.project === "object" ? record.project : null;
157
171
  const protocol = record.protocol && typeof record.protocol === "object" ? record.protocol : null;
@@ -176,6 +190,7 @@ function renderHumanResult(args, result) {
176
190
  if (run.run_index_path)
177
191
  lines.push(`run_index: ${String(run.run_index_path)}`);
178
192
  }
193
+ appendFlowGuidanceHuman(lines, record);
179
194
  return `${lines.join("\n")}\n`;
180
195
  }
181
196
  function renderStatusHuman(record) {
@@ -183,6 +198,8 @@ function renderStatusHuman(record) {
183
198
  const cli = recordObject(record.cli);
184
199
  const cliBuild = recordObject(cli?.build);
185
200
  const builtWithCanon = recordObject(cliBuild?.built_with_canon);
201
+ const cliCompatibility = recordObject(cli?.compatibility);
202
+ const registry = recordObject(cli?.registry);
186
203
  const canon = recordObject(record.canon);
187
204
  const resolvedCanon = recordObject(canon?.resolved);
188
205
  const project = recordObject(record.project);
@@ -190,16 +207,48 @@ function renderStatusHuman(record) {
190
207
  const flowPack = recordObject(project?.flow_pack);
191
208
  const drift = recordObject(project?.drift);
192
209
  lines.push(`CLI package: ${stringValue(cli?.package_name) ?? "unknown"} ${stringValue(cli?.version) ?? "unknown"}`);
210
+ if (cliCompatibility) {
211
+ lines.push(`CLI compatibility: ${stringValue(cliCompatibility.verdict) ?? "unknown"} (${stringValue(cliCompatibility.reason) ?? "no_reason"})`);
212
+ if (cliCompatibility.update_command)
213
+ lines.push(`CLI update: ${String(cliCompatibility.update_command)}`);
214
+ }
215
+ if (registry) {
216
+ lines.push(`CLI registry: ${stringValue(registry.latest) ?? "unknown"} (${stringValue(registry.status) ?? "unknown"})`);
217
+ }
193
218
  lines.push(`CLI tested with canon: ${stringValue(builtWithCanon?.version) ?? "unknown"} @ ${shortSha(stringValue(builtWithCanon?.commit))}`);
194
- lines.push(`Active local canon: ${stringValue(resolvedCanon?.root) ?? "unresolved"} ${stringValue(resolvedCanon?.version) ?? "unknown"} @ ${shortSha(stringValue(resolvedCanon?.commit))}`);
219
+ lines.push(`Active local canon: ${stringValue(resolvedCanon?.root) ?? "unresolved"} ${stringValue(resolvedCanon?.version) ?? "unknown"} @ ${shortSha(stringValue(resolvedCanon?.commit))} (${stringValue(resolvedCanon?.layout) ?? "unknown"})`);
220
+ if (resolvedCanon?.memorybank_root)
221
+ lines.push(`Canon Memory Bank: ${String(resolvedCanon.memorybank_root)}`);
222
+ if (resolvedCanon?.flow_root)
223
+ lines.push(`Canon flow root: ${String(resolvedCanon.flow_root)}`);
195
224
  lines.push(`Project: ${stringValue(project?.root) ?? "unknown"}${project?.registered === true ? " (registered)" : " (unregistered)"}`);
196
225
  lines.push(`Project Memory Bank: ${stringValue(memoryBank?.version) ?? stringValue(memoryBank?.status) ?? "unknown"}`);
197
226
  lines.push(`Project flow pack: ${stringValue(flowPack?.pack_version) ?? stringValue(flowPack?.status) ?? "unknown"} @ ${shortSha(stringValue(flowPack?.source_commit))}`);
198
227
  lines.push(`Drift: ${stringValue(drift?.overall) ?? "unknown"}`);
199
228
  if (drift?.next_action)
200
229
  lines.push(`Next action: ${String(drift.next_action)}`);
230
+ appendFlowGuidanceHuman(lines, record);
201
231
  return `${lines.join("\n")}\n`;
202
232
  }
233
+ function appendFlowGuidanceHuman(lines, record) {
234
+ const guidance = recordObject(record.flow_guidance);
235
+ if (!guidance)
236
+ return;
237
+ const currentStage = stringValue(guidance.current_stage);
238
+ const nextAction = stringValue(guidance.recommended_next_action);
239
+ const prompt = stringValue(guidance.recommended_prompt);
240
+ if (currentStage)
241
+ lines.push(`stage: ${currentStage}`);
242
+ if (nextAction)
243
+ lines.push(`next: ${nextAction}${prompt && prompt !== "none" ? ` (${prompt})` : ""}`);
244
+ const guards = Array.isArray(guidance.guards) ? guidance.guards : [];
245
+ const firstGuard = guards.find((item) => item && typeof item === "object" && !Array.isArray(item));
246
+ if (firstGuard?.id)
247
+ lines.push(`guard: ${String(firstGuard.id)} ${String(firstGuard.status ?? "unknown")}`);
248
+ const missing = Array.isArray(guidance.blocked_if_missing) ? guidance.blocked_if_missing : [];
249
+ if (missing.length > 0)
250
+ lines.push(`missing: ${missing.map(String).join(", ")}`);
251
+ }
203
252
  function recordObject(value) {
204
253
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
205
254
  }
@@ -214,7 +263,13 @@ async function dispatch(args, context, io) {
214
263
  const parsed = parseArgs(rest);
215
264
  if (family === "status") {
216
265
  const rootParsed = parseArgs([command ?? "", ...rest]);
217
- return getRuntimeStatus(context, { projectRoot: optionalOption(rootParsed, "project-root") ?? optionalOption(rootParsed, "root") });
266
+ return getRuntimeStatus(context, {
267
+ projectRoot: optionalOption(rootParsed, "project-root") ?? optionalOption(rootParsed, "root"),
268
+ checkRegistry: hasOption(rootParsed, "check-registry")
269
+ });
270
+ }
271
+ if (family === "version") {
272
+ return getCliVersionReport();
218
273
  }
219
274
  if (family === "canon") {
220
275
  return dispatchCanon(context, command, parsed);
@@ -255,9 +310,44 @@ async function dispatch(args, context, io) {
255
310
  if (family === "protocol" && command === "status") {
256
311
  return getProtocolStatus(context, { protocolId: requiredPosition(parsed, 0, "protocol-id") });
257
312
  }
313
+ if (family === "protocol" && command === "ready") {
314
+ return getReadyProtocols(context, { projectRoot: requiredOption(parsed, "project-root") });
315
+ }
316
+ if (family === "protocol" && command === "blockers") {
317
+ return getProtocolBlockers(context, {
318
+ protocolId: requiredPosition(parsed, 0, "protocol-id"),
319
+ projectRoot: requiredOption(parsed, "project-root")
320
+ });
321
+ }
322
+ if (family === "protocol" && command === "implement") {
323
+ const reason = optionalOption(parsed, "reason");
324
+ return implementProtocol(context, {
325
+ protocolId: requiredPosition(parsed, 0, "protocol-id"),
326
+ projectRoot: requiredOption(parsed, "project-root"),
327
+ force: hasOption(parsed, "force"),
328
+ ...(reason ? { reason } : {})
329
+ });
330
+ }
258
331
  if (family === "protocol" && command === "ready-for-merge") {
259
332
  return readyForMerge(context, { protocolId: requiredPosition(parsed, 0, "protocol-id") });
260
333
  }
334
+ if (family === "protocol" && command === "transition") {
335
+ const reason = optionalOption(parsed, "reason");
336
+ return transitionProtocol(context, {
337
+ protocolId: requiredPosition(parsed, 0, "protocol-id"),
338
+ to: requiredOption(parsed, "to"),
339
+ jsonFile: requiredOptionAlias(parsed, ["payload-file", "json-file"]),
340
+ force: hasOption(parsed, "force"),
341
+ ...(reason ? { reason } : {})
342
+ });
343
+ }
344
+ if (family === "protocol" && command === "sync-from-run") {
345
+ return syncProtocolFromRun(context, {
346
+ protocolId: requiredPosition(parsed, 0, "protocol-id"),
347
+ runId: requiredOption(parsed, "run"),
348
+ target: optionalOption(parsed, "target") ?? "auto"
349
+ });
350
+ }
261
351
  if (family === "protocol" && command === "cancel") {
262
352
  return cancelProtocol(context, {
263
353
  protocolId: requiredPosition(parsed, 0, "protocol-id"),
@@ -275,7 +365,7 @@ async function dispatch(args, context, io) {
275
365
  return transitionProtocol(context, {
276
366
  protocolId: requiredPosition(transitionParsed, 0, "protocol-id"),
277
367
  to: requiredOption(transitionParsed, "to"),
278
- jsonFile: requiredOption(transitionParsed, "json-file"),
368
+ jsonFile: requiredOptionAlias(transitionParsed, ["payload-file", "json-file"]),
279
369
  force: hasOption(transitionParsed, "force"),
280
370
  ...(reason ? { reason } : {})
281
371
  });
@@ -313,6 +403,9 @@ async function dispatch(args, context, io) {
313
403
  if (family === "run") {
314
404
  return dispatchRun(context, command, parsed);
315
405
  }
406
+ if (family === "id") {
407
+ return dispatchId(context, command, parsed);
408
+ }
316
409
  if (family === "integration" && command === "cmux") {
317
410
  return dispatchCmux(context, parsed);
318
411
  }
@@ -579,6 +672,16 @@ function dispatchRun(context, command, parsed) {
579
672
  }
580
673
  throw new AppError("usage", `Unknown run command: ${command ?? "<empty>"}`, 2);
581
674
  }
675
+ function dispatchId(context, command, parsed) {
676
+ if (command === "next") {
677
+ return previewNextEntityId(context, {
678
+ projectRoot: requiredOption(parsed, "project-root"),
679
+ type: requiredOption(parsed, "type"),
680
+ slug: requiredOption(parsed, "slug")
681
+ });
682
+ }
683
+ throw new AppError("usage", `Unknown id command: ${command ?? "<empty>"}`, 2);
684
+ }
582
685
  function dispatchCmux(context, parsed) {
583
686
  const action = requiredPosition(parsed, 0, "integration cmux action");
584
687
  if (action === "status") {
@@ -587,14 +690,31 @@ function dispatchCmux(context, parsed) {
587
690
  throw new AppError("usage", `Unknown integration cmux action: ${action}`, 2);
588
691
  }
589
692
  function dispatchDashboard(context, command, parsed) {
693
+ if (command === "data") {
694
+ const global = hasOption(parsed, "global");
695
+ return dashboardData(context, {
696
+ global,
697
+ projectRoot: global ? undefined : requiredOption(parsed, "project-root"),
698
+ protocol: optionalOption(parsed, "protocol")
699
+ });
700
+ }
590
701
  if (command === "render") {
591
- return renderDashboard(context, { projectRoot: requiredOption(parsed, "project-root"), output: optionalOption(parsed, "output") });
702
+ return renderDashboard(context, {
703
+ projectRoot: requiredOption(parsed, "project-root"),
704
+ output: optionalOption(parsed, "output"),
705
+ format: optionalOption(parsed, "format"),
706
+ protocol: optionalOption(parsed, "protocol")
707
+ });
592
708
  }
593
709
  if (command === "render-global") {
594
- return renderGlobalDashboard(context, { output: optionalOption(parsed, "output") });
710
+ return renderGlobalDashboard(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") });
595
711
  }
596
712
  if (command === "open") {
597
- return openDashboard(context, { projectRoot: requiredOption(parsed, "project-root"), viewer: optionalOption(parsed, "viewer") });
713
+ return openDashboard(context, {
714
+ projectRoot: requiredOption(parsed, "project-root"),
715
+ viewer: optionalOption(parsed, "viewer"),
716
+ format: optionalOption(parsed, "format")
717
+ });
598
718
  }
599
719
  if (command === "refresh") {
600
720
  const open = optionalOption(parsed, "open");
@@ -603,11 +723,13 @@ function dispatchDashboard(context, command, parsed) {
603
723
  }
604
724
  return refreshDashboard(context, {
605
725
  projectRoot: requiredOption(parsed, "project-root"),
606
- open: open
726
+ open: open,
727
+ format: optionalOption(parsed, "format"),
728
+ protocol: optionalOption(parsed, "protocol")
607
729
  });
608
730
  }
609
731
  if (command === "refresh-global") {
610
- return refreshGlobalDashboard(context, { output: optionalOption(parsed, "output") });
732
+ return refreshGlobalDashboard(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") });
611
733
  }
612
734
  throw new AppError("usage", `Unknown dashboard command: ${command ?? "<empty>"}`, 2);
613
735
  }
@@ -882,6 +1004,14 @@ function requiredOption(parsed, key) {
882
1004
  }
883
1005
  return value;
884
1006
  }
1007
+ function requiredOptionAlias(parsed, keys) {
1008
+ for (const key of keys) {
1009
+ const value = optionalOption(parsed, key);
1010
+ if (value)
1011
+ return value;
1012
+ }
1013
+ throw new AppError("usage", `Missing required option: ${keys.map((key) => `--${key}`).join(" or ")}`, 2);
1014
+ }
885
1015
  function hasOption(parsed, key) {
886
1016
  return parsed.options.has(key);
887
1017
  }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/compatibility@1",
4
+ "title": "dd-flow compatibility manifest",
5
+ "description": "Canonical compatibility map between a Memory Bank release and the dd-flow CLI package required by that release.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema_id", "memory_bank_version", "dd_flow_cli"],
9
+ "properties": {
10
+ "schema_id": { "const": "dd-flow/compatibility@1" },
11
+ "memory_bank_version": { "type": "string", "minLength": 1 },
12
+ "dd_flow_cli": {
13
+ "type": "object",
14
+ "additionalProperties": false,
15
+ "required": ["package_name", "min_version", "recommended_version", "status_contract", "version_contract", "flow_contract"],
16
+ "properties": {
17
+ "package_name": { "type": "string", "minLength": 1 },
18
+ "min_version": { "type": "string", "minLength": 1 },
19
+ "recommended_version": { "type": "string", "minLength": 1 },
20
+ "status_contract": { "type": "string", "minLength": 1 },
21
+ "version_contract": { "type": "string", "minLength": 1 },
22
+ "flow_contract": { "type": "string", "minLength": 1 }
23
+ }
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,73 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/flow-guidance@1",
4
+ "title": "dd-flow CLI flow guidance block",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "current_stage",
9
+ "allowed_next_stages",
10
+ "recommended_next_action",
11
+ "recommended_prompt",
12
+ "required_predecessor_evidence",
13
+ "guards",
14
+ "blocked_if_missing"
15
+ ],
16
+ "properties": {
17
+ "current_stage": {
18
+ "type": "string",
19
+ "minLength": 1
20
+ },
21
+ "allowed_next_stages": {
22
+ "type": "array",
23
+ "items": {
24
+ "type": "string",
25
+ "minLength": 1
26
+ }
27
+ },
28
+ "recommended_next_action": {
29
+ "type": "string",
30
+ "minLength": 1
31
+ },
32
+ "recommended_prompt": {
33
+ "type": "string",
34
+ "minLength": 1
35
+ },
36
+ "required_predecessor_evidence": {
37
+ "type": "array",
38
+ "items": {
39
+ "type": "string",
40
+ "minLength": 1
41
+ }
42
+ },
43
+ "guards": {
44
+ "type": "array",
45
+ "items": {
46
+ "type": "object",
47
+ "additionalProperties": false,
48
+ "required": ["id", "status", "summary"],
49
+ "properties": {
50
+ "id": {
51
+ "type": "string",
52
+ "minLength": 1
53
+ },
54
+ "status": {
55
+ "type": "string",
56
+ "enum": ["pass", "fail", "unknown", "not_applicable", "degraded"]
57
+ },
58
+ "summary": {
59
+ "type": "string",
60
+ "minLength": 1
61
+ }
62
+ }
63
+ }
64
+ },
65
+ "blocked_if_missing": {
66
+ "type": "array",
67
+ "items": {
68
+ "type": "string",
69
+ "minLength": 1
70
+ }
71
+ }
72
+ }
73
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "dd-flow/flow-run-index@1",
3
+ "$id": "dd-flow/flow-run-index@2",
4
4
  "title": "dd-flow run index",
5
5
  "description": "Navigation and artifact discovery contract for one concrete dd-flow execution.",
6
6
  "type": "object",
@@ -24,7 +24,7 @@
24
24
  ],
25
25
  "properties": {
26
26
  "schema_id": {
27
- "const": "dd-flow/flow-run-index@1"
27
+ "enum": ["dd-flow/flow-run-index@1", "dd-flow/flow-run-index@2"]
28
28
  },
29
29
  "run_id": {
30
30
  "type": "string",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "flow_kind": {
38
38
  "type": "string",
39
- "enum": ["coding", "experiment", "mb-init", "mb-upgrade", "mb-audit", "mb-distill", "mb-upgrade-review", "custom"]
39
+ "enum": ["mb_sdlc", "coding", "experiment", "mb-init", "mb-upgrade", "mb-audit", "mb-distill", "mb-upgrade-review", "mb-sdlc-review", "review", "custom"]
40
40
  },
41
41
  "subject": {
42
42
  "type": "object",
@@ -62,7 +62,33 @@
62
62
  "required": ["root", "run_dir"],
63
63
  "properties": {
64
64
  "root": { "type": "string", "minLength": 1 },
65
- "run_dir": { "type": "string", "pattern": "^\\.tasks/dd-flow-runs/RUN-[0-9]{3}-[a-z0-9]+(?:-[a-z0-9]+)*$" }
65
+ "run_dir": { "type": "string", "minLength": 1 }
66
+ }
67
+ },
68
+ "run_home": {
69
+ "type": "object",
70
+ "additionalProperties": false,
71
+ "required": ["storage", "path"],
72
+ "properties": {
73
+ "storage": { "type": "string", "enum": ["dd_flow_home", "workspace_tasks_legacy", "project_projection"] },
74
+ "path": { "type": "string", "minLength": 1 },
75
+ "relative_path": { "type": "string", "minLength": 1 }
76
+ }
77
+ },
78
+ "execution": {
79
+ "type": "object",
80
+ "additionalProperties": false,
81
+ "required": ["project_root", "workspace_root"],
82
+ "properties": {
83
+ "project_root": { "type": "string", "minLength": 1 },
84
+ "workspace_root": { "type": "string", "minLength": 1 }
85
+ }
86
+ },
87
+ "legacy": {
88
+ "type": "object",
89
+ "additionalProperties": false,
90
+ "properties": {
91
+ "project_tasks_run_dir": { "type": ["string", "null"] }
66
92
  }
67
93
  },
68
94
  "stage_runs": {