@deksden-com/dd-flow-cli 0.1.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.
- package/README.md +13 -0
- package/dist/build-info.json +15 -0
- package/dist/cli/help.js +133 -14
- package/dist/cli/run-cli.js +242 -8
- package/dist/schemas/archived-flow-manifest.schema.json +112 -0
- package/dist/schemas/compatibility.schema.json +26 -0
- package/dist/schemas/flow-guidance.schema.json +73 -0
- package/dist/schemas/flow-run-index.schema.json +30 -4
- package/dist/schemas/global-dashboard-data.schema.json +68 -0
- package/dist/schemas/mb-sdlc-review-report.schema.json +242 -0
- package/dist/schemas/merge-stage-report.schema.json +61 -1
- package/dist/schemas/plan-stage-report.schema.json +255 -0
- package/dist/schemas/project-dashboard-data.schema.json +98 -0
- package/dist/schemas/project-flow-pack-manifest.schema.json +127 -0
- package/dist/schemas/protocol-dashboard-data.schema.json +89 -0
- package/dist/schemas/status-report.schema.json +186 -0
- package/dist/schemas/version-report.schema.json +22 -0
- package/dist/services/build-info.js +114 -0
- package/dist/services/canon.js +298 -0
- package/dist/services/cleanup.js +14 -1
- package/dist/services/config.js +25 -0
- package/dist/services/dashboard.js +655 -10
- package/dist/services/flow-guidance.js +214 -0
- package/dist/services/ids.js +106 -0
- package/dist/services/lanes.js +6 -2
- package/dist/services/merge-queue.js +68 -4
- package/dist/services/merge-worker.js +177 -0
- package/dist/services/projects.js +7 -1
- package/dist/services/protocols.js +648 -17
- package/dist/services/runs.js +98 -21
- package/dist/services/schema-validation.js +88 -9
- package/dist/services/sessions.js +3 -2
- package/dist/services/status.js +306 -0
- package/dist/services/version-status.js +284 -0
- package/dist/storage/database.js +13 -0
- package/dist/storage/paths.js +21 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -41,6 +41,17 @@ The flow contract defines mechanical protocol stages, allowed transitions, the `
|
|
|
41
41
|
|
|
42
42
|
## Basic Commands
|
|
43
43
|
|
|
44
|
+
Inspect runtime and canonical Memory Bank discovery:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
dd-flow status --project-root "$PWD" --json
|
|
48
|
+
dd-flow canon resolve --json
|
|
49
|
+
dd-flow canon register --root "$DD_MEMORYBANK" --json
|
|
50
|
+
dd-flow canon status --json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`canon resolve` checks explicit `--root`, then `DD_MEMORYBANK`, then the globally registered value in `DD_FLOW_HOME`, then known nearby `dd-memorybank` checkouts from the current working directory. If two different valid canonical roots are discovered, it fails closed with structured blockers and bootstrap hints. Use `DD_MEMORYBANK` for the local canonical `dd-memorybank` checkout.
|
|
54
|
+
|
|
44
55
|
Register a project:
|
|
45
56
|
|
|
46
57
|
```bash
|
|
@@ -218,6 +229,8 @@ Defaults:
|
|
|
218
229
|
```text
|
|
219
230
|
project dashboard: .tasks/dd-flow-dashboard.md
|
|
220
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
|
|
221
234
|
```
|
|
222
235
|
|
|
223
236
|
Useful commands:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli_package": "@deksden-com/dd-flow-cli",
|
|
3
|
+
"cli_version": "0.3.0",
|
|
4
|
+
"cli_commit": "059594c2096e080548d470e789f594e20a1a8ebd",
|
|
5
|
+
"built_at": "2026-06-30T20:32:13.018Z",
|
|
6
|
+
"built_with_canon": {
|
|
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"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/dist/cli/help.js
CHANGED
|
@@ -9,17 +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.
|
|
13
|
+
status Inspect dd-flow runtime, project and canon roots.
|
|
14
|
+
canon register/status/resolve Register and resolve the canonical Memory Bank.
|
|
12
15
|
project register/status Register a project and inspect runtime state.
|
|
13
16
|
project resolve/archive Resolve typed ids and archive stale project roots.
|
|
14
17
|
project migrate-ids Migrate old project ids.
|
|
15
|
-
protocol register/status
|
|
18
|
+
protocol register/status/transition Register, inspect, transition and repair protocol state.
|
|
16
19
|
cleanup scan/apply Detect and repair stale local runtime state.
|
|
17
20
|
run start/status/list Track concrete flow executions and stage artifacts.
|
|
21
|
+
id next Preview next typed ids for protocol or run allocation.
|
|
18
22
|
plan set/status/item Attach and update protocol plans.
|
|
19
23
|
lane workspace/lock/status Manage shared workspace lanes and leases.
|
|
24
|
+
merge status/one-shot Inspect or claim one merge job from the current session.
|
|
25
|
+
merge-worker status/start/stop Manage long-lived project merge workers.
|
|
20
26
|
merge-queue status/next Claim, wait for, complete, or fail merge jobs.
|
|
21
27
|
session register/status/stop Register flow sessions and stop workers.
|
|
22
|
-
dashboard render/open/refresh Render markdown project/global dashboards.
|
|
28
|
+
dashboard data/render/open/refresh Render markdown or HTML project/global dashboards.
|
|
23
29
|
schema validate Validate canonical dd-flow JSON data contracts.
|
|
24
30
|
memory permissions preflight Check Memory Bank write/read permissions.
|
|
25
31
|
integration cmux status Inspect optional cmux integration.
|
|
@@ -28,17 +34,75 @@ Core commands:
|
|
|
28
34
|
|
|
29
35
|
Examples:
|
|
30
36
|
dd-flow project register --root "$PWD"
|
|
37
|
+
dd-flow version
|
|
38
|
+
dd-flow status --project-root "$PWD" --json
|
|
39
|
+
dd-flow canon register --root "$DD_MEMORYBANK" --json
|
|
40
|
+
dd-flow canon resolve --json
|
|
31
41
|
dd-flow project status --root "$PWD" --json
|
|
32
42
|
dd-flow project resolve PRJ-001 --json
|
|
33
43
|
dd-flow project archive PRJ-001 --reason "old missing worktree root" --json
|
|
34
44
|
dd-flow project migrate-ids --root "$PWD" --apply --json
|
|
35
|
-
dd-flow run start --project-root "$PWD" --flow-kind
|
|
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
|
|
36
48
|
dd-flow lane workspace set --project-root "$PWD" --lane merge --path "$PWD" --branch main --json
|
|
37
49
|
dd-flow lane lock acquire --project-root "$PWD" --lane merge --worker-id worker-1 --path "$PWD" --ttl 300 --reason "merge worker" --json
|
|
50
|
+
dd-flow merge status --project-root "$PWD" --json
|
|
51
|
+
dd-flow merge-worker start --project-root "$PWD" --worker-id merge-worker --path "$PWD" --json
|
|
38
52
|
|
|
39
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.
|
|
40
54
|
|
|
41
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`
|
|
71
|
+
],
|
|
72
|
+
[
|
|
73
|
+
"status",
|
|
74
|
+
`dd-flow status - inspect runtime roots and active discovery state
|
|
75
|
+
|
|
76
|
+
Usage:
|
|
77
|
+
dd-flow status [--project-root <root>] --json
|
|
78
|
+
dd-flow status [--root <root>] --json
|
|
79
|
+
dd-flow status [--project-root <root>] [--check-registry] --json
|
|
80
|
+
|
|
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.
|
|
82
|
+
|
|
83
|
+
By default status does not call the network. Use --check-registry for an explicit cached npm latest check.
|
|
84
|
+
|
|
85
|
+
Examples:
|
|
86
|
+
dd-flow status --project-root "$PWD" --json
|
|
87
|
+
dd-flow status --json`
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
"canon",
|
|
91
|
+
`dd-flow canon - register and resolve the canonical Memory Bank checkout
|
|
92
|
+
|
|
93
|
+
Usage:
|
|
94
|
+
dd-flow canon register --root <canonical-dd-memorybank-root> --json
|
|
95
|
+
dd-flow canon status [--root <canonical-dd-memorybank-root>] --json
|
|
96
|
+
dd-flow canon resolve [--root <canonical-dd-memorybank-root>] --json
|
|
97
|
+
|
|
98
|
+
Discovery order is explicit --root, then DD_MEMORYBANK, then the globally registered runtime value in DD_FLOW_HOME, then known nearby dd-memorybank checkouts from the current working directory. Explicit --root is the only source considered for that command. If multiple different valid canonical checkouts are found, resolve fails closed with blockers so the caller chooses intentionally.
|
|
99
|
+
|
|
100
|
+
A valid canonical root contains dd-flow/README.md, mbb/index.md, protocol/index.md, and canonical-only entrypoints such as dd-flow/mb-init.md, dd-flow/mb-upgrade.md, dd-flow/mb-upgrade-review.md, and dd-flow/mb-distill.md.
|
|
101
|
+
|
|
102
|
+
Examples:
|
|
103
|
+
dd-flow canon register --root "$DD_MEMORYBANK" --json
|
|
104
|
+
dd-flow canon resolve --json
|
|
105
|
+
dd-flow canon status --json`
|
|
42
106
|
],
|
|
43
107
|
[
|
|
44
108
|
"run",
|
|
@@ -46,18 +110,33 @@ project_root is the stable repository identity. workspace_path is the concrete c
|
|
|
46
110
|
|
|
47
111
|
Usage:
|
|
48
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
|
|
49
114
|
dd-flow run status <RUN-ID|RUN-NNN> --project-root <root> --json
|
|
50
115
|
dd-flow run list --project-root <root> --json
|
|
51
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
|
|
52
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
|
|
53
118
|
dd-flow run complete <RUN-ID|RUN-NNN> --project-root <root> --status done|blocked|cancelled|failed [--verdict <text>] [--next-action <text>] --json
|
|
54
119
|
|
|
55
|
-
RUN-* is the execution envelope for one concrete flow launch. Semantic truth remains in protocol, experiment, DEF, scenario, evidence, and Memory Bank documents.
|
|
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.
|
|
56
121
|
|
|
57
122
|
Examples:
|
|
58
|
-
dd-flow run start --project-root "$PWD" --flow-kind
|
|
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
|
|
59
125
|
dd-flow run attach-stage RUN-001 --project-root "$PWD" --stage plan --dir 01-plan --status running --json
|
|
60
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`
|
|
61
140
|
],
|
|
62
141
|
[
|
|
63
142
|
"protocol",
|
|
@@ -66,10 +145,27 @@ Examples:
|
|
|
66
145
|
Usage:
|
|
67
146
|
dd-flow protocol register <handshake-id> --project-root <root> [--workspace-path <checkout>] --json
|
|
68
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
|
|
69
153
|
dd-flow protocol ready-for-merge <protocol-id> --json
|
|
70
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
|
|
71
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
|
+
|
|
72
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.`
|
|
73
169
|
],
|
|
74
170
|
[
|
|
75
171
|
"project",
|
|
@@ -132,6 +228,27 @@ Examples:
|
|
|
132
228
|
dd-flow lane lock acquire --project-root "$PWD" --lane merge --worker-id worker-1 --path "$PWD" --ttl 300 --reason "merge worker" --json
|
|
133
229
|
dd-flow lane lock heartbeat --project-root "$PWD" --lane merge --worker-id worker-1 --path "$PWD" --lease-token "$TOKEN" --json
|
|
134
230
|
dd-flow lane lock release --project-root "$PWD" --lane merge --worker-id worker-1 --path "$PWD" --lease-token "$TOKEN" --reason done --json`
|
|
231
|
+
],
|
|
232
|
+
[
|
|
233
|
+
"merge",
|
|
234
|
+
`dd-flow merge - current-session merge entrypoint helpers
|
|
235
|
+
|
|
236
|
+
Usage:
|
|
237
|
+
dd-flow merge status --project-root <root> --json
|
|
238
|
+
dd-flow merge one-shot --project-root <root> --worker-id <id> [--path <workspace>] --json
|
|
239
|
+
|
|
240
|
+
merge status reports active merge worker, claimed jobs, active merge lane lock and queue state. merge one-shot is status-only when a long-lived worker, claimed job or active lock exists; otherwise it acquires the merge lane, claims at most one ready job, and returns that job for the shared merge job prompt. If no job is ready, it releases the lock and returns job: null.`
|
|
241
|
+
],
|
|
242
|
+
[
|
|
243
|
+
"merge-worker",
|
|
244
|
+
`dd-flow merge-worker - manage long-lived project merge workers
|
|
245
|
+
|
|
246
|
+
Usage:
|
|
247
|
+
dd-flow merge-worker status --project-root <root> --json
|
|
248
|
+
dd-flow merge-worker start --project-root <root> --worker-id <id> [--path <workspace>] [--branch <branch>] --json
|
|
249
|
+
dd-flow merge-worker stop --project-root <root> [--worker-id <id>] --reason <text> --json
|
|
250
|
+
|
|
251
|
+
start is idempotent: if a worker, claimed job, or active merge lock already exists it returns status instead of creating a duplicate worker. stop is no-op/status when no worker exists, refuses ambiguous multiple workers unless --worker-id is provided, stops idle workers immediately, and marks busy workers as stop-after-current while preserving the claimed job and lock.`
|
|
135
252
|
],
|
|
136
253
|
[
|
|
137
254
|
"merge-queue",
|
|
@@ -215,16 +332,18 @@ Usage:
|
|
|
215
332
|
],
|
|
216
333
|
[
|
|
217
334
|
"dashboard",
|
|
218
|
-
`dd-flow dashboard - render or open markdown dashboards
|
|
335
|
+
`dd-flow dashboard - render or open markdown and HTML dashboards
|
|
219
336
|
|
|
220
337
|
Usage:
|
|
221
|
-
dd-flow dashboard
|
|
222
|
-
dd-flow dashboard
|
|
223
|
-
dd-flow dashboard
|
|
224
|
-
dd-flow dashboard
|
|
225
|
-
dd-flow dashboard
|
|
226
|
-
|
|
227
|
-
|
|
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.`
|
|
228
347
|
],
|
|
229
348
|
[
|
|
230
349
|
"schema",
|
|
@@ -233,7 +352,7 @@ Dashboards are rendered views of dd-flow state. State-changing commands refresh
|
|
|
233
352
|
Usage:
|
|
234
353
|
dd-flow schema validate --schema <name> --file <json-file> [--project-root <root>] [--schema-dir <dir>] --json
|
|
235
354
|
|
|
236
|
-
Schema names resolve to <name>.schema.json. Lookup order is --schema-dir, then <project-root>/.memory-bank/dd-flow/schemas/, then bundled CLI schemas. If --project-root is omitted, the current working directory is used. JSON mode writes valid results to stdout and structured validation/usage errors to stderr.`
|
|
355
|
+
Schema names resolve to <name>.schema.json. Lookup order is --schema-dir, then <project-root>/.memory-bank/dd-flow/schemas/, then <project-root>/dd-flow/schemas/ for canonical checkouts, then bundled CLI schemas. If --project-root is omitted, the current working directory is used. JSON mode writes valid results to stdout and structured validation/usage errors to stderr.`
|
|
237
356
|
],
|
|
238
357
|
[
|
|
239
358
|
"schema validate",
|
package/dist/cli/run-cli.js
CHANGED
|
@@ -3,17 +3,22 @@ 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
|
+
import { getMergeWorkerStatus, oneShotMergeClaim, startMergeWorker, stopProjectMergeWorker } from "../services/merge-worker.js";
|
|
9
10
|
import { cleanupApply, cleanupScan } from "../services/cleanup.js";
|
|
10
11
|
import { getCodexHomeStatus, getCodexHooksStatus, handleCodexHook, initCodexHome, installCodexHooks, planCodexHome, printCodexHomeEnv, printCodexHooks, removeCodexHome, removeCodexHooks } from "../services/hooks.js";
|
|
11
12
|
import { bootstrapWorktree, closeWorktree, createWorktreeRecord, getWorktreeStatus, planWorktree } from "../services/worktrees.js";
|
|
12
13
|
import { acquireLaneLock, checkLaneWorkspace, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, waitForLaneLock } from "../services/lanes.js";
|
|
13
14
|
import { getProjectConfigStatus, setProjectConfigValue } from "../services/config.js";
|
|
14
|
-
import {
|
|
15
|
+
import { getCanonStatus, registerCanonRoot, resolveCanonRoot } from "../services/canon.js";
|
|
16
|
+
import { getRuntimeStatus } from "../services/status.js";
|
|
17
|
+
import { getCliVersionReport } from "../services/build-info.js";
|
|
18
|
+
import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
|
|
15
19
|
import { getFlowSessionStatus, registerFlowSession, stopFlowSession, stopMergeWorker } from "../services/sessions.js";
|
|
16
20
|
import { attachFlowRunStage, completeFlowRun, completeFlowRunStage, getFlowRunStatus, listFlowRuns, startFlowRun } from "../services/runs.js";
|
|
21
|
+
import { previewNextEntityId } from "../services/ids.js";
|
|
17
22
|
import { validateSchema } from "../services/schema-validation.js";
|
|
18
23
|
import { preflightMemoryPermissions } from "../services/memory-permissions.js";
|
|
19
24
|
import { requireProjectByRoot } from "../services/projects.js";
|
|
@@ -31,6 +36,14 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
31
36
|
if (args.includes("--progress-jsonl")) {
|
|
32
37
|
throw new AppError("usage", "--progress-jsonl is not implemented yet; use --json for final structured progress", 2);
|
|
33
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
|
+
}
|
|
34
47
|
const help = helpForArgs(output.args);
|
|
35
48
|
if (help) {
|
|
36
49
|
io.stdout.write(`${help}\n`);
|
|
@@ -91,6 +104,8 @@ function progressForCommand(args) {
|
|
|
91
104
|
];
|
|
92
105
|
}
|
|
93
106
|
if ((family === "cleanup" && command === "apply") ||
|
|
107
|
+
(family === "merge" && command === "one-shot") ||
|
|
108
|
+
family === "merge-worker" ||
|
|
94
109
|
(family === "protocol" && command === "cancel") ||
|
|
95
110
|
(family === "project" && command === "archive") ||
|
|
96
111
|
family === "run") {
|
|
@@ -144,6 +159,13 @@ function renderHumanResult(args, result) {
|
|
|
144
159
|
lines.push(`errors: ${errors.length}`);
|
|
145
160
|
return `${lines.join("\n")}\n`;
|
|
146
161
|
}
|
|
162
|
+
if (family === "status") {
|
|
163
|
+
return renderStatusHuman(record);
|
|
164
|
+
}
|
|
165
|
+
if (family === "version") {
|
|
166
|
+
const cli = recordObject(record.cli);
|
|
167
|
+
return `dd-flow ${stringValue(cli?.version) ?? "unknown"}\n`;
|
|
168
|
+
}
|
|
147
169
|
const lines = [`dd-flow ${family ?? ""}${command ? ` ${command}` : ""}: ${ok}`.trim()];
|
|
148
170
|
const project = record.project && typeof record.project === "object" ? record.project : null;
|
|
149
171
|
const protocol = record.protocol && typeof record.protocol === "object" ? record.protocol : null;
|
|
@@ -168,11 +190,90 @@ function renderHumanResult(args, result) {
|
|
|
168
190
|
if (run.run_index_path)
|
|
169
191
|
lines.push(`run_index: ${String(run.run_index_path)}`);
|
|
170
192
|
}
|
|
193
|
+
appendFlowGuidanceHuman(lines, record);
|
|
194
|
+
return `${lines.join("\n")}\n`;
|
|
195
|
+
}
|
|
196
|
+
function renderStatusHuman(record) {
|
|
197
|
+
const lines = [`dd-flow status: ${record.ok === false ? "failed" : "ok"}`];
|
|
198
|
+
const cli = recordObject(record.cli);
|
|
199
|
+
const cliBuild = recordObject(cli?.build);
|
|
200
|
+
const builtWithCanon = recordObject(cliBuild?.built_with_canon);
|
|
201
|
+
const cliCompatibility = recordObject(cli?.compatibility);
|
|
202
|
+
const registry = recordObject(cli?.registry);
|
|
203
|
+
const canon = recordObject(record.canon);
|
|
204
|
+
const resolvedCanon = recordObject(canon?.resolved);
|
|
205
|
+
const project = recordObject(record.project);
|
|
206
|
+
const memoryBank = recordObject(project?.memory_bank);
|
|
207
|
+
const flowPack = recordObject(project?.flow_pack);
|
|
208
|
+
const drift = recordObject(project?.drift);
|
|
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
|
+
}
|
|
218
|
+
lines.push(`CLI tested with canon: ${stringValue(builtWithCanon?.version) ?? "unknown"} @ ${shortSha(stringValue(builtWithCanon?.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)}`);
|
|
224
|
+
lines.push(`Project: ${stringValue(project?.root) ?? "unknown"}${project?.registered === true ? " (registered)" : " (unregistered)"}`);
|
|
225
|
+
lines.push(`Project Memory Bank: ${stringValue(memoryBank?.version) ?? stringValue(memoryBank?.status) ?? "unknown"}`);
|
|
226
|
+
lines.push(`Project flow pack: ${stringValue(flowPack?.pack_version) ?? stringValue(flowPack?.status) ?? "unknown"} @ ${shortSha(stringValue(flowPack?.source_commit))}`);
|
|
227
|
+
lines.push(`Drift: ${stringValue(drift?.overall) ?? "unknown"}`);
|
|
228
|
+
if (drift?.next_action)
|
|
229
|
+
lines.push(`Next action: ${String(drift.next_action)}`);
|
|
230
|
+
appendFlowGuidanceHuman(lines, record);
|
|
171
231
|
return `${lines.join("\n")}\n`;
|
|
172
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
|
+
}
|
|
252
|
+
function recordObject(value) {
|
|
253
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
254
|
+
}
|
|
255
|
+
function stringValue(value) {
|
|
256
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
257
|
+
}
|
|
258
|
+
function shortSha(value) {
|
|
259
|
+
return value ? value.slice(0, 12) : "unknown";
|
|
260
|
+
}
|
|
173
261
|
async function dispatch(args, context, io) {
|
|
174
262
|
const [family, command, ...rest] = args;
|
|
175
263
|
const parsed = parseArgs(rest);
|
|
264
|
+
if (family === "status") {
|
|
265
|
+
const rootParsed = parseArgs([command ?? "", ...rest]);
|
|
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();
|
|
273
|
+
}
|
|
274
|
+
if (family === "canon") {
|
|
275
|
+
return dispatchCanon(context, command, parsed);
|
|
276
|
+
}
|
|
176
277
|
if (family === "project" && command === "register") {
|
|
177
278
|
return registerProject(context, { root: requiredOption(parsed, "root") });
|
|
178
279
|
}
|
|
@@ -209,9 +310,44 @@ async function dispatch(args, context, io) {
|
|
|
209
310
|
if (family === "protocol" && command === "status") {
|
|
210
311
|
return getProtocolStatus(context, { protocolId: requiredPosition(parsed, 0, "protocol-id") });
|
|
211
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
|
+
}
|
|
212
331
|
if (family === "protocol" && command === "ready-for-merge") {
|
|
213
332
|
return readyForMerge(context, { protocolId: requiredPosition(parsed, 0, "protocol-id") });
|
|
214
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
|
+
}
|
|
215
351
|
if (family === "protocol" && command === "cancel") {
|
|
216
352
|
return cancelProtocol(context, {
|
|
217
353
|
protocolId: requiredPosition(parsed, 0, "protocol-id"),
|
|
@@ -229,7 +365,7 @@ async function dispatch(args, context, io) {
|
|
|
229
365
|
return transitionProtocol(context, {
|
|
230
366
|
protocolId: requiredPosition(transitionParsed, 0, "protocol-id"),
|
|
231
367
|
to: requiredOption(transitionParsed, "to"),
|
|
232
|
-
jsonFile:
|
|
368
|
+
jsonFile: requiredOptionAlias(transitionParsed, ["payload-file", "json-file"]),
|
|
233
369
|
force: hasOption(transitionParsed, "force"),
|
|
234
370
|
...(reason ? { reason } : {})
|
|
235
371
|
});
|
|
@@ -252,6 +388,12 @@ async function dispatch(args, context, io) {
|
|
|
252
388
|
if (family === "merge-queue") {
|
|
253
389
|
return dispatchMergeQueue(context, command, parsed);
|
|
254
390
|
}
|
|
391
|
+
if (family === "merge") {
|
|
392
|
+
return dispatchMerge(context, command, parsed);
|
|
393
|
+
}
|
|
394
|
+
if (family === "merge-worker") {
|
|
395
|
+
return dispatchMergeWorker(context, command, parsed);
|
|
396
|
+
}
|
|
255
397
|
if (family === "cleanup") {
|
|
256
398
|
return dispatchCleanup(context, command, parsed);
|
|
257
399
|
}
|
|
@@ -261,6 +403,9 @@ async function dispatch(args, context, io) {
|
|
|
261
403
|
if (family === "run") {
|
|
262
404
|
return dispatchRun(context, command, parsed);
|
|
263
405
|
}
|
|
406
|
+
if (family === "id") {
|
|
407
|
+
return dispatchId(context, command, parsed);
|
|
408
|
+
}
|
|
264
409
|
if (family === "integration" && command === "cmux") {
|
|
265
410
|
return dispatchCmux(context, parsed);
|
|
266
411
|
}
|
|
@@ -287,6 +432,18 @@ async function dispatch(args, context, io) {
|
|
|
287
432
|
}
|
|
288
433
|
throw new AppError("usage", `Unknown command: ${args.join(" ") || "<empty>"}`, 2);
|
|
289
434
|
}
|
|
435
|
+
function dispatchCanon(context, command, parsed) {
|
|
436
|
+
if (command === "register") {
|
|
437
|
+
return registerCanonRoot(context, { root: requiredOption(parsed, "root") });
|
|
438
|
+
}
|
|
439
|
+
if (command === "status") {
|
|
440
|
+
return getCanonStatus(context, { root: optionalOption(parsed, "root") });
|
|
441
|
+
}
|
|
442
|
+
if (command === "resolve") {
|
|
443
|
+
return resolveCanonRoot(context, { explicitRoot: optionalOption(parsed, "root") });
|
|
444
|
+
}
|
|
445
|
+
throw new AppError("usage", `Unknown canon command: ${command ?? "<empty>"}`, 2);
|
|
446
|
+
}
|
|
290
447
|
function dispatchMergeQueue(context, command, parsed) {
|
|
291
448
|
if (hasOption(parsed, "session-id")) {
|
|
292
449
|
throw new AppError("usage", "--session-id is no longer accepted on merge-queue commands; use --worker-id", 2);
|
|
@@ -346,6 +503,40 @@ function dispatchMergeQueue(context, command, parsed) {
|
|
|
346
503
|
}
|
|
347
504
|
throw new AppError("usage", `Unknown merge-queue command: ${command ?? "<empty>"}`, 2);
|
|
348
505
|
}
|
|
506
|
+
function dispatchMerge(context, command, parsed) {
|
|
507
|
+
if (command === "status") {
|
|
508
|
+
return getMergeWorkerStatus(context, { projectRoot: requiredOption(parsed, "project-root") });
|
|
509
|
+
}
|
|
510
|
+
if (command === "one-shot") {
|
|
511
|
+
return oneShotMergeClaim(context, {
|
|
512
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
513
|
+
workerId: requiredWorkerId(parsed),
|
|
514
|
+
workspacePath: workspacePathOption(parsed)
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
throw new AppError("usage", `Unknown merge command: ${command ?? "<empty>"}`, 2);
|
|
518
|
+
}
|
|
519
|
+
function dispatchMergeWorker(context, command, parsed) {
|
|
520
|
+
if (command === "status") {
|
|
521
|
+
return getMergeWorkerStatus(context, { projectRoot: requiredOption(parsed, "project-root") });
|
|
522
|
+
}
|
|
523
|
+
if (command === "start") {
|
|
524
|
+
return startMergeWorker(context, {
|
|
525
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
526
|
+
workerId: requiredWorkerId(parsed),
|
|
527
|
+
workspacePath: workspacePathOption(parsed),
|
|
528
|
+
branch: optionalOption(parsed, "branch")
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
if (command === "stop") {
|
|
532
|
+
return stopProjectMergeWorker(context, {
|
|
533
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
534
|
+
workerId: optionalOption(parsed, "worker-id"),
|
|
535
|
+
reason: requiredOption(parsed, "reason")
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
throw new AppError("usage", `Unknown merge-worker command: ${command ?? "<empty>"}`, 2);
|
|
539
|
+
}
|
|
349
540
|
function dispatchMemory(command, parsed) {
|
|
350
541
|
const subcommand = requiredPosition(parsed, 0, "memory subcommand");
|
|
351
542
|
if (command === "permissions" && subcommand === "preflight") {
|
|
@@ -481,6 +672,16 @@ function dispatchRun(context, command, parsed) {
|
|
|
481
672
|
}
|
|
482
673
|
throw new AppError("usage", `Unknown run command: ${command ?? "<empty>"}`, 2);
|
|
483
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
|
+
}
|
|
484
685
|
function dispatchCmux(context, parsed) {
|
|
485
686
|
const action = requiredPosition(parsed, 0, "integration cmux action");
|
|
486
687
|
if (action === "status") {
|
|
@@ -489,14 +690,31 @@ function dispatchCmux(context, parsed) {
|
|
|
489
690
|
throw new AppError("usage", `Unknown integration cmux action: ${action}`, 2);
|
|
490
691
|
}
|
|
491
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
|
+
}
|
|
492
701
|
if (command === "render") {
|
|
493
|
-
return renderDashboard(context, {
|
|
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
|
+
});
|
|
494
708
|
}
|
|
495
709
|
if (command === "render-global") {
|
|
496
|
-
return renderGlobalDashboard(context, { output: optionalOption(parsed, "output") });
|
|
710
|
+
return renderGlobalDashboard(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") });
|
|
497
711
|
}
|
|
498
712
|
if (command === "open") {
|
|
499
|
-
return openDashboard(context, {
|
|
713
|
+
return openDashboard(context, {
|
|
714
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
715
|
+
viewer: optionalOption(parsed, "viewer"),
|
|
716
|
+
format: optionalOption(parsed, "format")
|
|
717
|
+
});
|
|
500
718
|
}
|
|
501
719
|
if (command === "refresh") {
|
|
502
720
|
const open = optionalOption(parsed, "open");
|
|
@@ -505,11 +723,13 @@ function dispatchDashboard(context, command, parsed) {
|
|
|
505
723
|
}
|
|
506
724
|
return refreshDashboard(context, {
|
|
507
725
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
508
|
-
open: open
|
|
726
|
+
open: open,
|
|
727
|
+
format: optionalOption(parsed, "format"),
|
|
728
|
+
protocol: optionalOption(parsed, "protocol")
|
|
509
729
|
});
|
|
510
730
|
}
|
|
511
731
|
if (command === "refresh-global") {
|
|
512
|
-
return refreshGlobalDashboard(context, { output: optionalOption(parsed, "output") });
|
|
732
|
+
return refreshGlobalDashboard(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") });
|
|
513
733
|
}
|
|
514
734
|
throw new AppError("usage", `Unknown dashboard command: ${command ?? "<empty>"}`, 2);
|
|
515
735
|
}
|
|
@@ -784,6 +1004,14 @@ function requiredOption(parsed, key) {
|
|
|
784
1004
|
}
|
|
785
1005
|
return value;
|
|
786
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
|
+
}
|
|
787
1015
|
function hasOption(parsed, key) {
|
|
788
1016
|
return parsed.options.has(key);
|
|
789
1017
|
}
|
|
@@ -882,6 +1110,12 @@ function projectRootForMutation(context, args, result) {
|
|
|
882
1110
|
if (family === "merge-queue") {
|
|
883
1111
|
return projectRootForMergeQueueMutation(context, command, parsed);
|
|
884
1112
|
}
|
|
1113
|
+
if (family === "merge" && command === "one-shot") {
|
|
1114
|
+
return requiredOption(parsed, "project-root");
|
|
1115
|
+
}
|
|
1116
|
+
if (family === "merge-worker" && ["start", "stop"].includes(command ?? "")) {
|
|
1117
|
+
return requiredOption(parsed, "project-root");
|
|
1118
|
+
}
|
|
885
1119
|
if (family === "cleanup" && command === "apply") {
|
|
886
1120
|
return requiredOption(parsed, "project-root");
|
|
887
1121
|
}
|