@esneiderbravo/speclaw 2.0.8 → 2.0.9

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/dist/cli/index.js CHANGED
@@ -2,71 +2,7 @@
2
2
  import { parseFlags } from "./lib/args.js";
3
3
  import { ui, header } from "./lib/ui.js";
4
4
  import { maybeNotifyUpdate } from "./lib/update-check.js";
5
- const HELP = `speclaw — spec-driven, agent-ready projects (Foundation + Compass + Lawbook + Cortex)
6
-
7
- Usage: speclaw <command> [options]
8
-
9
- Install globally so the command is always available:
10
- npm i -g @esneiderbravo/speclaw
11
-
12
- Setup
13
- init Interactive setup: pick agents, scaffold, index, get the prompt
14
- (--minimal omits setup/lifecycle MCP tools)
15
- update Apply project migrations (advisory if a newer binary exists)
16
- (--check reports version only; --minimal persists minimal exposure)
17
- agent list Show which agents are configured
18
- agent add <id> Configure another agent later (symlinks + MCP)
19
-
20
- Compass (code intelligence — the same surface agents use via MCP)
21
- index (Re)build the local code graph (--force / --prune / --json)
22
- session-start Silent, fail-safe refresh of an existing index (SessionStart hook)
23
- watch Keep the index fresh on file changes
24
- explore <node> A node's source + callers/callees
25
- search <query> Hybrid find (BM25+vector+name); --focus --max-tokens --explain
26
- recall "<query>" Hybrid find with concept weights; same flags as search
27
- impact <node> Blast radius (grouped by module; --flat / --json)
28
- affected-tests Tests affected by a change (--file / --from-diff / --json)
29
- diff-context Change context for a diff (--file / --rev / --worktree / --json)
30
- hotspots Rank files by recent churn × AST complexity (--json / --sort)
31
- coupling <file> Temporal co-change partners for a file (--json)
32
- trace <from> <to> A call path between two nodes
33
- visualize [node] Interactive HTML graph → .speclaw/graph.html
34
-
35
- Cortex (One brain. Many agents. — multi-agent loop)
36
- cortex <op> status|start|advance|rework|brief — drive harness.json (--change)
37
-
38
- Lawbook (spec-driven workflow)
39
- quick <name> Scaffold a level-0 change (record.md + reports)
40
- lawbook init Create the lawbook/ workspace
41
- lawbook list Active/archived changes and capabilities
42
- lawbook level <mode> Propose/set/promote/explain ceremony level (--json)
43
- lawbook draft <name> Scaffold a feature change (--level N, --capability C, --json)
44
- lawbook draft --bug <c> Scaffold a bug change (bugfix.md + reports)
45
- lawbook investigate Rank bug suspects from graph (--symptom / --stack-trace, --json)
46
- lawbook validate <c> Validate a change's artifacts
47
- lawbook sync <c> Promote delta specs to canonical
48
- lawbook archive <c> Finalize and archive a change
49
- lawbook harness <op> Deprecated alias for \`speclaw cortex\` (compat)
50
-
51
- Other
52
- doctor Verify the installation (--json, --offline, --strict)
53
- budget Measure always-on context cost (tools, skills, instructions)
54
- coverage Requirement → impl → test coverage (--json, --tap, --adopt, --write)
55
- drift Spec↔code drift (--json, --reseal, --reverse, --fail-on)
56
- owners Compile team.owners → .github/CODEOWNERS (--write / --check / --diff)
57
- telemetry status Confirm speclaw ships no telemetry
58
- check Evaluate an action against the laws (hooks call this; --dry-run to preview)
59
- laws verify Verify the deterministic dependency/graph laws against the index
60
- laws compile Compile laws into agent rule dialects (AGENTS / Claude / Cursor / …)
61
- laws import Import third-party rules as draft laws (--from rulesync)
62
- laws lock Create/refresh committed speclaw.lock digests for rule files
63
- laws accept <path> Interactively accept a changed rule-file digest (TTY only)
64
- laws scan Scan rule/skill files for prompt-injection patterns
65
- verify Verify laws + integrity for CI: exit codes, --sarif, --json, --strict-engines
66
- mcp Start the MCP server (used by your agent's config)
67
- help Show this help
68
- --version Print the installed speclaw version
69
- `;
5
+ import { GLOBAL_HELP as HELP, helpFor, knownCommands, wantsHelp } from "./lib/help.js";
70
6
  // Commands that open with the one-line branded header. These are the
71
7
  // interactive, human-facing commands whose stdout is prose. Deliberately
72
8
  // excluded: `version`/`--version`/`-v` (bare scriptable value), the Compass
@@ -107,6 +43,9 @@ function maybeHeader(cmd, flags) {
107
43
  return;
108
44
  if (!HEADER_COMMANDS.has(cmd))
109
45
  return;
46
+ // A self-update child re-runs `update`; the parent already printed the header.
47
+ if (cmd === "update" && process.env.SPECLAW_SELF_UPDATED)
48
+ return;
110
49
  if (cmd === "budget" && flags.json)
111
50
  return;
112
51
  if (cmd === "doctor" && flags.json)
@@ -129,8 +68,16 @@ function maybeHeader(cmd, flags) {
129
68
  return;
130
69
  header();
131
70
  }
71
+ // The dispatcher's known commands come from the help registry, so a command
72
+ // without usage text is rejected here before it can ship.
73
+ const KNOWN_COMMANDS = knownCommands();
132
74
  /** Run the handler for a single command. Returns when the command completes. */
133
75
  async function dispatch(cmd, flags) {
76
+ if (cmd !== undefined && !KNOWN_COMMANDS.has(cmd)) {
77
+ ui.err(`Unknown command: ${cmd}`);
78
+ console.log(HELP);
79
+ process.exit(1);
80
+ }
134
81
  switch (cmd) {
135
82
  case undefined:
136
83
  case "help":
@@ -201,9 +148,20 @@ async function dispatch(cmd, flags) {
201
148
  process.exit(1);
202
149
  }
203
150
  }
204
- /** Parse argv, run the command, then surface an update notice if one is due. */
151
+ /**
152
+ * Parse argv, run the command, then surface an update notice if one is due.
153
+ * `<command> --help` / `-h` short-circuits first: it prints the command's usage
154
+ * and runs nothing — no header, no notifier, no handler, no file writes.
155
+ */
205
156
  async function main() {
206
157
  const [cmd, ...rest] = process.argv.slice(2);
158
+ // Covers: req~per-command-help~1
159
+ const usage = cmd !== undefined && wantsHelp(rest) ? helpFor(cmd) : null;
160
+ if (usage !== null) {
161
+ process.stdout.write(usage.endsWith("\n") ? usage : usage + "\n");
162
+ process.exitCode = 0;
163
+ return;
164
+ }
207
165
  const flags = parseFlags(rest);
208
166
  maybeHeader(cmd, flags);
209
167
  await dispatch(cmd, flags);
@@ -0,0 +1,489 @@
1
+ /**
2
+ * The CLI's help registry: the global usage text, the one list of dispatchable
3
+ * commands, and each command's own usage text. It lives apart from
4
+ * `src/cli/index.ts` because that module runs `main()` on import, and it is
5
+ * shared with the dispatcher so a command cannot ship without usage text.
6
+ */
7
+ /** The global usage text printed by `speclaw help`, `--help`, `-h`, and no arguments. */
8
+ export const GLOBAL_HELP = `speclaw — spec-driven, agent-ready projects (Foundation + Compass + Lawbook + Cortex)
9
+
10
+ Usage: speclaw <command> [options] (speclaw <command> --help for one command)
11
+
12
+ Install globally so the command is always available:
13
+ npm i -g @esneiderbravo/speclaw (or run npx @esneiderbravo/speclaw@latest init)
14
+
15
+ Setup
16
+ init Interactive setup: pick agents, scaffold, index, get the prompt
17
+ (--minimal omits setup/lifecycle MCP tools)
18
+ update Re-run itself at the latest version via npx, then apply project
19
+ migrations (--check reports version only; --no-self-update
20
+ migrates with this binary; --minimal persists minimal exposure)
21
+ agent list Show which agents are configured
22
+ agent add <id> Configure another agent later (symlinks + MCP)
23
+
24
+ Compass (code intelligence — the same surface agents use via MCP)
25
+ index (Re)build the local code graph (--force / --prune / --json)
26
+ session-start Silent, fail-safe refresh of an existing index (SessionStart hook)
27
+ watch Keep the index fresh on file changes
28
+ explore <node> A node's source + callers/callees
29
+ search <query> Hybrid find (BM25+vector+name); --focus --max-tokens --explain
30
+ recall "<query>" Hybrid find with concept weights; same flags as search
31
+ impact <node> Blast radius (grouped by module; --flat / --json)
32
+ affected-tests Tests affected by a change (--file / --from-diff / --json)
33
+ diff-context Change context for a diff (--file / --rev / --worktree / --json)
34
+ hotspots Rank files by recent churn × AST complexity (--json / --sort)
35
+ coupling <file> Temporal co-change partners for a file (--json)
36
+ trace <from> <to> A call path between two nodes
37
+ visualize [node] Interactive HTML graph → .speclaw/graph.html
38
+
39
+ Cortex (One brain. Many agents. — multi-agent loop)
40
+ cortex <op> status|start|advance|rework|brief — drive harness.json (--change)
41
+
42
+ Lawbook (spec-driven workflow)
43
+ quick <name> Scaffold a level-0 change (record.md + reports)
44
+ lawbook init Create the lawbook/ workspace
45
+ lawbook list Active/archived changes and capabilities
46
+ lawbook level <mode> Propose/set/promote/explain ceremony level (--json)
47
+ lawbook draft <name> Scaffold a feature change (--level N, --capability C, --json)
48
+ lawbook draft --bug <c> Scaffold a bug change (bugfix.md + reports)
49
+ lawbook investigate Rank bug suspects from graph (--symptom / --stack-trace, --json)
50
+ lawbook validate <c> Validate a change's artifacts
51
+ lawbook sync <c> Promote delta specs to canonical
52
+ lawbook archive <c> Finalize and archive a change
53
+ lawbook harness <op> Deprecated alias for \`speclaw cortex\` (compat)
54
+
55
+ Other
56
+ doctor Verify the installation (--json, --offline, --strict)
57
+ budget Measure always-on context cost (tools, skills, instructions)
58
+ coverage Requirement → impl → test coverage (--json, --tap, --adopt, --write)
59
+ drift Spec↔code drift (--json, --reseal, --reverse, --fail-on)
60
+ owners Compile team.owners → .github/CODEOWNERS (--write / --check / --diff)
61
+ telemetry status Confirm speclaw ships no telemetry
62
+ check Evaluate an action against the laws (hooks call this; --dry-run to preview)
63
+ laws verify Verify the deterministic dependency/graph laws against the index
64
+ laws compile Compile laws into agent rule dialects (AGENTS / Claude / Cursor / …)
65
+ laws import Import third-party rules as draft laws (--from rulesync)
66
+ laws lock Create/refresh committed speclaw.lock digests for rule files
67
+ (keeps drifted strict digests; --force re-baselines, TTY only)
68
+ laws accept <path> Interactively accept a changed rule-file digest (TTY only)
69
+ laws scan Scan rule/skill files for prompt-injection patterns
70
+ verify Verify laws + integrity for CI: exit codes, --sarif, --json, --strict-engines
71
+ mcp Start the MCP server (used by your agent's config)
72
+ help Show this help
73
+ --version Print the installed speclaw version
74
+ `;
75
+ const SEARCH_FLAGS = `Options
76
+ --focus <files> Comma-separated files to boost (files you are editing)
77
+ --max-tokens <n> Token budget for the returned context
78
+ --explain Show how each result was scored
79
+ --json Print the result as JSON
80
+ `;
81
+ /** Usage text for `speclaw index --help`. */
82
+ const INDEX_USAGE = `Usage: speclaw index [options]
83
+
84
+ (Re)build the local Compass code graph for the current directory. Runs are
85
+ incremental: unchanged files are skipped, and an unchanged project skips the
86
+ global post-processing.
87
+
88
+ Options
89
+ --force Re-extract every file
90
+ --prune Also evict embedding-cache rows unused for --retention days
91
+ --retention <days> Retention window for --prune (default 30)
92
+ --max-cache-mb <mb> Embedding-cache size cap (default 256)
93
+ --json Print the index statistics as JSON
94
+ `;
95
+ // Covers: req~per-command-help~1
96
+ /**
97
+ * Every command the CLI dispatches, with its usage text. The dispatcher in
98
+ * `src/cli/index.ts` rejects any command missing from this list, so a new
99
+ * command cannot ship without `--help` output.
100
+ */
101
+ export const COMMANDS = [
102
+ { name: "help", aliases: ["--help", "-h"], usage: GLOBAL_HELP },
103
+ {
104
+ name: "version",
105
+ aliases: ["--version", "-v"],
106
+ usage: `Usage: speclaw version (also --version, -v)
107
+
108
+ Print the installed speclaw version as a bare string.
109
+ `,
110
+ },
111
+ {
112
+ name: "mcp",
113
+ usage: `Usage: speclaw mcp
114
+
115
+ Start the speclaw MCP server over stdio. Your agent's MCP config launches it;
116
+ you rarely run it by hand.
117
+ `,
118
+ },
119
+ {
120
+ name: "init",
121
+ usage: `Usage: speclaw init [options]
122
+
123
+ Interactive setup: pick agents, scaffold the constitution and lawbook, build the
124
+ Compass index, and print the starting prompt. Prefer
125
+ npx @esneiderbravo/speclaw@latest init so the scaffold matches the latest release.
126
+
127
+ Options
128
+ --yes, -y Accept the defaults without prompting
129
+ --agents <ids> Comma-separated agents to configure (claude, cursor, …)
130
+ --packs <ids> Comma-separated tool packs to install
131
+ --project-name <name> Project name for the templates
132
+ --minimal Omit setup/lifecycle MCP tools
133
+ --no-index Skip the initial Compass index
134
+ `,
135
+ },
136
+ {
137
+ name: "update",
138
+ usage: `Usage: speclaw update [options]
139
+
140
+ Bring this project up to date. When npm reports a newer speclaw during this run,
141
+ update re-runs itself as npx -y @esneiderbravo/speclaw@<latest> update with the
142
+ same flags and exits with its code; otherwise it applies the project migrations
143
+ with the running binary. Opt out with --no-self-update or SPECLAW_NO_SELF_UPDATE=1.
144
+
145
+ Options
146
+ --check Report the version status only (no re-run, no migration)
147
+ --backup Keep a .bak of each locally edited managed file before refreshing
148
+ --minimal Persist minimal MCP exposure
149
+ --no-self-update Never re-run at the latest version; migrate with this binary
150
+ `,
151
+ },
152
+ {
153
+ name: "agent",
154
+ usage: `Usage: speclaw agent <list|add <id>>
155
+
156
+ list Show which agents are configured
157
+ add <id> Configure another agent (symlinks + MCP entry)
158
+ `,
159
+ },
160
+ { name: "index", usage: INDEX_USAGE },
161
+ {
162
+ name: "watch",
163
+ usage: `Usage: speclaw watch
164
+
165
+ Keep the Compass index fresh: re-index changed files until interrupted (Ctrl-C).
166
+ `,
167
+ },
168
+ {
169
+ name: "session-start",
170
+ usage: `Usage: speclaw session-start
171
+
172
+ Silent, fail-safe refresh of an existing Compass index. The SessionStart hook
173
+ runs it; it prints nothing and never fails the session.
174
+ `,
175
+ },
176
+ {
177
+ name: "explore",
178
+ usage: `Usage: speclaw explore <node> [options]
179
+
180
+ A symbol's source plus its callers and callees.
181
+
182
+ Options
183
+ --json Print the result as JSON
184
+ `,
185
+ },
186
+ {
187
+ name: "search",
188
+ usage: `Usage: speclaw search <query> [options]
189
+
190
+ Hybrid find over the code graph (BM25 + vectors + name match).
191
+
192
+ ${SEARCH_FLAGS}`,
193
+ },
194
+ {
195
+ name: "recall",
196
+ usage: `Usage: speclaw recall "<query>" [options]
197
+
198
+ Hybrid find with concept weights (same flags as search).
199
+
200
+ ${SEARCH_FLAGS}`,
201
+ },
202
+ {
203
+ name: "impact",
204
+ usage: `Usage: speclaw impact <node> [options]
205
+
206
+ Blast radius of a symbol, grouped by module.
207
+
208
+ Options
209
+ --flat Flat list instead of module groups
210
+ --depth <n> Maximum traversal depth (default 4)
211
+ --file <paths> Seed from files instead of a symbol
212
+ --json Print the report as JSON
213
+ `,
214
+ },
215
+ {
216
+ name: "trace",
217
+ usage: `Usage: speclaw trace <from> <to> [options]
218
+
219
+ A call path between two symbols (Compass call-path query, not requirement
220
+ coverage — that is speclaw coverage).
221
+
222
+ Options
223
+ --json Print the path as JSON
224
+ `,
225
+ },
226
+ {
227
+ name: "affected-tests",
228
+ usage: `Usage: speclaw affected-tests [options]
229
+
230
+ Tests affected by a change, with a runnable command.
231
+
232
+ Options
233
+ --file <paths> Comma-separated changed files
234
+ --from-diff <ref> Seed from git files changed against <ref> (default main)
235
+ --json Print the selection as JSON
236
+ `,
237
+ },
238
+ {
239
+ name: "diff-context",
240
+ usage: `Usage: speclaw diff-context [options]
241
+
242
+ Graph context of a change: symbols, blast radius, tests, hotspots.
243
+
244
+ Options
245
+ --file <paths> Comma-separated changed files
246
+ --rev <rev> A git revision to describe
247
+ --worktree Use the working tree changes
248
+ --full Full mode instead of brief
249
+ --json Print the context as JSON
250
+ `,
251
+ },
252
+ {
253
+ name: "hotspots",
254
+ usage: `Usage: speclaw hotspots [options]
255
+
256
+ Rank files by recent churn × AST complexity.
257
+
258
+ Options
259
+ --days <n> History window in days (default 90)
260
+ --since <date> History start date
261
+ --sort <key> Sort key
262
+ --limit <n> Maximum rows
263
+ --json Print the report as JSON
264
+ `,
265
+ },
266
+ {
267
+ name: "coupling",
268
+ usage: `Usage: speclaw coupling <file> [options]
269
+
270
+ Temporal co-change partners for a file.
271
+
272
+ Options
273
+ --days <n> History window in days (default 90)
274
+ --since <date> History start date
275
+ --min-shared <n> Minimum shared commits
276
+ --max-files <n> Ignore commits touching more files than this
277
+ --limit <n> Maximum rows
278
+ --json Print the report as JSON
279
+ `,
280
+ },
281
+ {
282
+ name: "visualize",
283
+ usage: `Usage: speclaw visualize [node] [options]
284
+
285
+ Write an interactive HTML graph to .speclaw/graph.html.
286
+
287
+ Options
288
+ --depth <n> Neighborhood depth around <node>
289
+ --limit <n> Maximum nodes
290
+ --no-open Do not open the browser
291
+ `,
292
+ },
293
+ {
294
+ name: "quick",
295
+ usage: `Usage: speclaw quick <name> [options]
296
+
297
+ Scaffold a level-0 change (record.md + change.json + reports/).
298
+
299
+ Options
300
+ --path <files> Files the change touches
301
+ --symbol <names> Symbols the change touches
302
+ --json Print the scaffold as JSON
303
+ `,
304
+ },
305
+ {
306
+ name: "cortex",
307
+ usage: `Usage: speclaw cortex <status|start|advance|rework|brief> [change] [options]
308
+
309
+ Drive a change's Cortex harness (harness.json).
310
+
311
+ Options
312
+ --change <name> The change (or pass it as the second argument)
313
+ --verdict <v> PASS or FAIL (advance from reviewing/testing)
314
+ --note <text> A history note
315
+ --question <text> An open question (repeatable)
316
+ --pause-questions Pause the loop for open questions
317
+ --json Print the result as JSON
318
+ `,
319
+ },
320
+ {
321
+ name: "lawbook",
322
+ usage: `Usage: speclaw lawbook <subcommand> [options]
323
+
324
+ init Create the lawbook/ workspace
325
+ list Active/archived changes and capabilities
326
+ level <mode> [change] propose|set|promote|explain ceremony level (--level, --reason)
327
+ draft <name> Scaffold a feature change (--level N, --capability C)
328
+ draft --bug <name> Scaffold a bug change (bugfix.md + reports)
329
+ investigate Rank bug suspects (--symptom, --stack-trace, --symbol, --path)
330
+ validate <change> Validate a change's artifacts
331
+ sync <change> Promote delta specs to canonical
332
+ archive <change> Finalize and archive a change
333
+ harness <op> Deprecated alias for speclaw cortex
334
+
335
+ Options
336
+ --json Print the result as JSON
337
+ `,
338
+ },
339
+ {
340
+ name: "doctor",
341
+ usage: `Usage: speclaw doctor [options]
342
+
343
+ Verify the installation and report structured diagnostics.
344
+
345
+ Options
346
+ --json Print the versioned DoctorReport
347
+ --offline Skip network checks
348
+ --strict Exit non-zero on warnings
349
+ --redact Redact paths and secrets (default)
350
+ --no-redact Do not redact
351
+ `,
352
+ },
353
+ {
354
+ name: "budget",
355
+ usage: `Usage: speclaw budget [options]
356
+
357
+ Measure always-on context cost (tools, skills, instructions).
358
+
359
+ Options
360
+ --minimal Measure the minimal MCP profile
361
+ --json Print the measurement as JSON
362
+ `,
363
+ },
364
+ {
365
+ name: "coverage",
366
+ usage: `Usage: speclaw coverage [options]
367
+
368
+ Requirement → impl → test coverage.
369
+
370
+ Options
371
+ --change <name> Include a change's delta specs
372
+ --json Print the versioned coverage report
373
+ --tap Print TAP
374
+ --adopt Propose requirement ids for unidentified requirements
375
+ --write With --adopt, write the ids
376
+ `,
377
+ },
378
+ {
379
+ name: "drift",
380
+ usage: `Usage: speclaw drift [options]
381
+
382
+ Sealed spec↔code drift.
383
+
384
+ Options
385
+ --capability <name> Limit to one capability
386
+ --fail-on <level> Exit non-zero at this level (default semantic)
387
+ --reverse Report code without spec anchors
388
+ --reseal Photograph current bodies into the anchors
389
+ --explain Explain each finding
390
+ --json Print the versioned drift report
391
+ `,
392
+ },
393
+ {
394
+ name: "telemetry",
395
+ usage: `Usage: speclaw telemetry status
396
+
397
+ Confirm speclaw ships no telemetry.
398
+ `,
399
+ },
400
+ {
401
+ name: "owners",
402
+ usage: `Usage: speclaw owners [options]
403
+
404
+ Compile team.owners from lawbook/config.yaml into .github/CODEOWNERS.
405
+
406
+ Options
407
+ --write Write the managed block
408
+ --check Exit non-zero when the block is stale
409
+ --diff Show the pending change
410
+ `,
411
+ },
412
+ {
413
+ name: "check",
414
+ usage: `Usage: speclaw check [options]
415
+
416
+ Evaluate an action against the laws (agent hooks call this).
417
+
418
+ Options
419
+ --hook-payload <f> Read the hook payload from a file (- for stdin)
420
+ --event <name> The hook event
421
+ --path <file> The file the action touches
422
+ --dry-run Preview the decision without enforcing it
423
+ `,
424
+ },
425
+ {
426
+ name: "laws",
427
+ usage: `Usage: speclaw laws <subcommand> [options]
428
+
429
+ verify Verify the dependency/graph laws (--engine, --law, --json)
430
+ compile Compile laws into agent rule dialects (--agent)
431
+ import --from <src> Import third-party rules as draft laws (rulesync)
432
+ lock [--force] Create/refresh speclaw.lock. A strict file that drifted from
433
+ the lock keeps its locked digest (run laws accept <path>);
434
+ --force lists the drifted files and, once confirmed,
435
+ re-baselines them (interactive TTY only; --note)
436
+ accept <path> Accept a changed rule-file digest (interactive TTY only; --note)
437
+ scan Scan rule/skill files for prompt-injection patterns
438
+ `,
439
+ },
440
+ {
441
+ name: "verify",
442
+ usage: `Usage: speclaw verify [options]
443
+
444
+ Verify laws and rule-file integrity for CI.
445
+
446
+ Options
447
+ --ci CI mode
448
+ --engine <e> deps, graph (comma-separated)
449
+ --law <id> Limit to one law
450
+ --fail-on <level> Exit non-zero at this severity
451
+ --format <f> Output format
452
+ --sarif <file> Write SARIF
453
+ --json [file] Print or write the JSON report
454
+ --strict-engines Exit non-zero when an engine cannot run
455
+ `,
456
+ },
457
+ ];
458
+ const BY_NAME = new Map();
459
+ for (const cmd of COMMANDS) {
460
+ BY_NAME.set(cmd.name, cmd);
461
+ for (const alias of cmd.aliases ?? [])
462
+ BY_NAME.set(alias, cmd);
463
+ }
464
+ /**
465
+ * Every argv spelling the dispatcher accepts: command names plus aliases.
466
+ *
467
+ * @returns The set of known command tokens.
468
+ */
469
+ export function knownCommands() {
470
+ return new Set(BY_NAME.keys());
471
+ }
472
+ /**
473
+ * The usage text for a command or alias.
474
+ *
475
+ * @param cmd - The command token as typed after `speclaw`.
476
+ * @returns The usage text, or `null` for an unknown command.
477
+ */
478
+ export function helpFor(cmd) {
479
+ return BY_NAME.get(cmd)?.usage ?? null;
480
+ }
481
+ /**
482
+ * Whether a command's arguments ask for help.
483
+ *
484
+ * @param args - The arguments after the command name.
485
+ * @returns True when any argument is `--help` or `-h`.
486
+ */
487
+ export function wantsHelp(args) {
488
+ return args.some((a) => a === "--help" || a === "-h");
489
+ }