@rasensio/aidlc 1.9.1 → 1.10.1

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 CHANGED
@@ -37,6 +37,8 @@ The CLI has two core jobs: **bootstrap** (installing and refreshing skills) and
37
37
 
38
38
  | Command | Purpose |
39
39
  |---------|---------|
40
+ | `aidlc` (no arguments) | Interactive menu of common commands; each entry shows the command it runs |
41
+ | `aidlc menu` | The same menu, addressable by name |
40
42
  | `aidlc init [--platform <id>]` | Set up AIDLC: interactive wizard, or non-interactive with `--platform` |
41
43
  | `aidlc update` | Update CLI and refresh project skills |
42
44
  | `aidlc gate <phase>` | CI gate check (exit 0 = pass, 1 = fail) |
package/dist/cli.d.ts CHANGED
@@ -7,5 +7,22 @@
7
7
  *
8
8
  * Requirements: 17.1
9
9
  */
10
- export {};
10
+ import { Command } from 'commander';
11
+ /**
12
+ * Build the fully-registered commander program.
13
+ *
14
+ * Exported so tests and the interactive menu can obtain the program without
15
+ * running the CLI. Registration order is load-bearing: commander renders the
16
+ * `--help` Commands section in this order (interactive-cli-menu/AC-5).
17
+ */
18
+ export declare function buildProgram(): Command;
19
+ /**
20
+ * Whether this module is the process entry point.
21
+ *
22
+ * `import.meta.main` expresses this directly but is not typed by TypeScript
23
+ * 5.8, so compare resolved real paths instead — verified to agree with
24
+ * `import.meta.main` when run directly, when imported, and when invoked through
25
+ * a symlink (how npm installs the `aidlc` bin).
26
+ */
27
+ export declare function isMainModule(metaUrl: string, entry?: string | undefined): boolean;
11
28
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG;AAeH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0BpC;;;;;;GAMG;AACH,wBAAgB,YAAY,IAAI,OAAO,CA+BtC;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAM,GAAG,SAA2B,GAC1C,OAAO,CAQT"}
package/dist/cli.js CHANGED
@@ -18,7 +18,7 @@ process.on('warning', (warning) => {
18
18
  for (const listener of defaultWarningListeners)
19
19
  listener(warning);
20
20
  });
21
- import { readFileSync } from 'node:fs';
21
+ import { readFileSync, realpathSync } from 'node:fs';
22
22
  import { fileURLToPath } from 'node:url';
23
23
  import { resolve, dirname } from 'node:path';
24
24
  import { Command } from 'commander';
@@ -39,30 +39,80 @@ import { registerClaim } from './commands/claim.js';
39
39
  import { registerMetrics } from './commands/metrics.js';
40
40
  import { registerDoctor } from './commands/doctor.js';
41
41
  import { registerAddSkill } from './commands/add-skill.js';
42
+ import { registerMenu } from './commands/menu.js';
43
+ import { isBareInvocation } from './menu/invocation.js';
44
+ import { openMenu } from './menu/index.js';
42
45
  const pkg = JSON.parse(readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8'));
43
- const program = new Command();
44
- program
45
- .name('aidlc')
46
- .description('AI Development Lifecycle Framework')
47
- .version(pkg.version);
48
- registerInit(program);
49
- registerStart(program);
50
- registerContinue(program);
51
- registerStatus(program);
52
- registerGate(program);
53
- registerTransition(program);
54
- registerDiscover(program);
55
- registerAddAction(program);
56
- registerDocs(program);
57
- registerReview(program);
58
- registerUpdate(program);
59
- registerKnowledge(program);
60
- registerCost(program);
61
- registerClaim(program);
62
- registerMetrics(program);
63
- registerDoctor(program);
64
- registerAddSkill(program);
65
- // parseAsync: async command actions (doctor's confirm prompt, knowledge's
66
- // merge-driver install) must be awaited so exit codes land reliably.
67
- await program.parseAsync();
46
+ /**
47
+ * Build the fully-registered commander program.
48
+ *
49
+ * Exported so tests and the interactive menu can obtain the program without
50
+ * running the CLI. Registration order is load-bearing: commander renders the
51
+ * `--help` Commands section in this order (interactive-cli-menu/AC-5).
52
+ */
53
+ export function buildProgram() {
54
+ const program = new Command();
55
+ program
56
+ .name('aidlc')
57
+ .description('AI Development Lifecycle Framework')
58
+ .version(pkg.version);
59
+ registerInit(program);
60
+ registerStart(program);
61
+ registerContinue(program);
62
+ registerStatus(program);
63
+ registerGate(program);
64
+ registerTransition(program);
65
+ registerDiscover(program);
66
+ registerAddAction(program);
67
+ registerDocs(program);
68
+ registerReview(program);
69
+ registerUpdate(program);
70
+ registerKnowledge(program);
71
+ registerCost(program);
72
+ registerClaim(program);
73
+ registerMetrics(program);
74
+ registerDoctor(program);
75
+ registerAddSkill(program);
76
+ // Last, so `menu` renders last in `aidlc --help` (interactive-cli-menu/AC-5).
77
+ // Takes the factory, not `program`: the menu dispatches on a throwaway copy so
78
+ // the caller's program never carries an `exitOverride` it cannot undo.
79
+ registerMenu(program, buildProgram);
80
+ return program;
81
+ }
82
+ /**
83
+ * Whether this module is the process entry point.
84
+ *
85
+ * `import.meta.main` expresses this directly but is not typed by TypeScript
86
+ * 5.8, so compare resolved real paths instead — verified to agree with
87
+ * `import.meta.main` when run directly, when imported, and when invoked through
88
+ * a symlink (how npm installs the `aidlc` bin).
89
+ */
90
+ export function isMainModule(metaUrl, entry = process.argv[1]) {
91
+ if (!entry)
92
+ return false;
93
+ try {
94
+ return realpathSync(fileURLToPath(metaUrl)) === realpathSync(entry);
95
+ }
96
+ catch {
97
+ // Either path may not exist (bundled, deleted, or a virtual entry).
98
+ return false;
99
+ }
100
+ }
101
+ // The main-module guard is load-bearing, not tidiness
102
+ // (interactive-cli-menu/AC-38): without it, importing this module runs the CLI
103
+ // against the host process's argv — and a vitest worker's argv is length 2, the
104
+ // very shape the bare-invocation guard treats as "open the menu".
105
+ if (isMainModule(import.meta.url)) {
106
+ if (isBareInvocation(process.argv)) {
107
+ // Bare `aidlc` only. An argv guard rather than a root `.action()`: the
108
+ // latter changes commander's unknown-command diagnostic to "too many
109
+ // arguments" (interactive-cli-menu/AC-3, interactive-cli-menu/AC-8).
110
+ await openMenu(buildProgram, 'bare');
111
+ }
112
+ else {
113
+ // parseAsync: async command actions (doctor's confirm prompt, knowledge's
114
+ // merge-driver install) must be awaited so exit codes land reliably.
115
+ await buildProgram().parseAsync();
116
+ }
117
+ }
68
118
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `aidlc menu` — the interactive command launcher
3
+ * (interactive-cli-menu/AC-1, interactive-cli-menu/AC-2).
4
+ *
5
+ * Bare `aidlc` opens the same menu through the argv guard in `cli.ts`; this
6
+ * command makes it addressable, so the behaviour is testable without depending
7
+ * on how the process was invoked.
8
+ *
9
+ * @module
10
+ */
11
+ import type { Command } from 'commander';
12
+ /**
13
+ * Register the `menu` command.
14
+ *
15
+ * Registered **last** in `buildProgram()` so `menu` appears last in
16
+ * `aidlc --help`: commander renders the Commands section in registration order,
17
+ * which keeps the help-screen diff to a single appended line
18
+ * (interactive-cli-menu/AC-5).
19
+ */
20
+ export declare function registerMenu(program: Command, makeProgram: () => Command): void;
21
+ //# sourceMappingURL=menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../src/commands/menu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,GAAG,IAAI,CAc/E"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `aidlc menu` — the interactive command launcher
3
+ * (interactive-cli-menu/AC-1, interactive-cli-menu/AC-2).
4
+ *
5
+ * Bare `aidlc` opens the same menu through the argv guard in `cli.ts`; this
6
+ * command makes it addressable, so the behaviour is testable without depending
7
+ * on how the process was invoked.
8
+ *
9
+ * @module
10
+ */
11
+ import { openMenu } from '../menu/index.js';
12
+ /**
13
+ * Register the `menu` command.
14
+ *
15
+ * Registered **last** in `buildProgram()` so `menu` appears last in
16
+ * `aidlc --help`: commander renders the Commands section in registration order,
17
+ * which keeps the help-screen diff to a single appended line
18
+ * (interactive-cli-menu/AC-5).
19
+ */
20
+ export function registerMenu(program, makeProgram) {
21
+ program
22
+ .command('menu')
23
+ .description('Interactive menu of common commands (also: run `aidlc` with no arguments)')
24
+ .addHelpText('after', `
25
+ Every entry shows the command it runs, so the menu teaches the flag-driven CLI
26
+ rather than replacing it. Requires an interactive terminal.
27
+ `)
28
+ .action(async () => {
29
+ await openMenu(makeProgram, 'menu');
30
+ });
31
+ }
32
+ //# sourceMappingURL=menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu.js","sourceRoot":"","sources":["../../src/commands/menu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,OAAgB,EAAE,WAA0B;IACvE,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,2EAA2E,CAAC;SACxF,WAAW,CACV,OAAO,EACP;;;CAGL,CACI;SACA,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Argv construction and command re-dispatch (interactive-cli-menu/AC-22,
3
+ * interactive-cli-menu/AC-23, interactive-cli-menu/AC-24,
4
+ * interactive-cli-menu/AC-25, interactive-cli-menu/AC-36,
5
+ * interactive-cli-menu/AC-37).
6
+ *
7
+ * A selected entry runs through commander's own parser, so there is exactly one
8
+ * code path per command and every option default, coercion, and validation still
9
+ * applies. No command's logic is reimplemented here.
10
+ *
11
+ * @module
12
+ */
13
+ import type { Command } from 'commander';
14
+ import { CommanderError } from 'commander';
15
+ import type { MenuEntry } from './roster.js';
16
+ /** Every command in a tree, root first. */
17
+ export declare function allCommands(cmd: Command): Command[];
18
+ /**
19
+ * Build the argv for a selected entry (interactive-cli-menu/AC-37).
20
+ *
21
+ * Prompted values go after a `--` separator so commander can never parse one as
22
+ * an option: without it, a user typing `--json` at a term prompt produces
23
+ * `error: unknown option`, and one typing `--help` gets help printed with exit 0
24
+ * — a silent success that ran nothing. With it, both arrive as literal values.
25
+ *
26
+ * The `['node','aidlc']` prefix matches commander's default `from: 'node'`,
27
+ * which treats argv[0]/argv[1] as the exec path and script and parses from
28
+ * index 2. No separator is emitted when there are no values.
29
+ */
30
+ export declare function buildArgv(entry: MenuEntry, values?: readonly string[]): string[];
31
+ export interface RunOutcome {
32
+ /** The command's action ran to completion. */
33
+ ran: boolean;
34
+ /** Set when commander threw instead of dispatching. */
35
+ commanderError?: CommanderError;
36
+ /**
37
+ * Commander wrote help or version output and "failed" with exit code 0.
38
+ * Not an error — the user got what they asked for.
39
+ */
40
+ displayedHelp?: boolean;
41
+ }
42
+ /**
43
+ * Re-dispatch a synthesized argv through a commander program
44
+ * (interactive-cli-menu/AC-22, interactive-cli-menu/AC-36).
45
+ *
46
+ * A **fresh** program is built for the dispatch rather than reusing the caller's.
47
+ * `exitOverride()` has no public un-set, and commander copies inherited settings
48
+ * into a subcommand at *creation* time — so overriding a shared program means
49
+ * either leaving it permanently mutated (a later parse would throw where it used
50
+ * to exit) or reaching for the private `_exitCallback` to restore it. A
51
+ * throwaway program avoids both, and re-registers through the same `registerX`
52
+ * functions, so it is the same code path per command.
53
+ *
54
+ * The recursion is mandatory: applied to the root alone *after* the subcommands
55
+ * exist, `exitOverride` is a no-op for every one of them and the process exits
56
+ * from inside the nested parse.
57
+ */
58
+ export declare function runEntry(makeProgram: () => Command, argv: readonly string[]): Promise<RunOutcome>;
59
+ //# sourceMappingURL=dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/menu/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,2CAA2C;AAC3C,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,EAAE,CAEnD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,EAAE,CAOpF;AAED,MAAM,WAAW,UAAU;IACzB,8CAA8C;IAC9C,GAAG,EAAE,OAAO,CAAC;IACb,uDAAuD;IACvD,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,QAAQ,CAC5B,WAAW,EAAE,MAAM,OAAO,EAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,UAAU,CAAC,CAmBrB"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Argv construction and command re-dispatch (interactive-cli-menu/AC-22,
3
+ * interactive-cli-menu/AC-23, interactive-cli-menu/AC-24,
4
+ * interactive-cli-menu/AC-25, interactive-cli-menu/AC-36,
5
+ * interactive-cli-menu/AC-37).
6
+ *
7
+ * A selected entry runs through commander's own parser, so there is exactly one
8
+ * code path per command and every option default, coercion, and validation still
9
+ * applies. No command's logic is reimplemented here.
10
+ *
11
+ * @module
12
+ */
13
+ import { CommanderError } from 'commander';
14
+ /** Every command in a tree, root first. */
15
+ export function allCommands(cmd) {
16
+ return [cmd, ...cmd.commands.flatMap(allCommands)];
17
+ }
18
+ /**
19
+ * Build the argv for a selected entry (interactive-cli-menu/AC-37).
20
+ *
21
+ * Prompted values go after a `--` separator so commander can never parse one as
22
+ * an option: without it, a user typing `--json` at a term prompt produces
23
+ * `error: unknown option`, and one typing `--help` gets help printed with exit 0
24
+ * — a silent success that ran nothing. With it, both arrive as literal values.
25
+ *
26
+ * The `['node','aidlc']` prefix matches commander's default `from: 'node'`,
27
+ * which treats argv[0]/argv[1] as the exec path and script and parses from
28
+ * index 2. No separator is emitted when there are no values.
29
+ */
30
+ export function buildArgv(entry, values = []) {
31
+ return [
32
+ 'node',
33
+ 'aidlc',
34
+ ...entry.path,
35
+ ...(values.length > 0 ? ['--', ...values] : []),
36
+ ];
37
+ }
38
+ /**
39
+ * Re-dispatch a synthesized argv through a commander program
40
+ * (interactive-cli-menu/AC-22, interactive-cli-menu/AC-36).
41
+ *
42
+ * A **fresh** program is built for the dispatch rather than reusing the caller's.
43
+ * `exitOverride()` has no public un-set, and commander copies inherited settings
44
+ * into a subcommand at *creation* time — so overriding a shared program means
45
+ * either leaving it permanently mutated (a later parse would throw where it used
46
+ * to exit) or reaching for the private `_exitCallback` to restore it. A
47
+ * throwaway program avoids both, and re-registers through the same `registerX`
48
+ * functions, so it is the same code path per command.
49
+ *
50
+ * The recursion is mandatory: applied to the root alone *after* the subcommands
51
+ * exist, `exitOverride` is a no-op for every one of them and the process exits
52
+ * from inside the nested parse.
53
+ */
54
+ export async function runEntry(makeProgram, argv) {
55
+ const program = makeProgram();
56
+ for (const cmd of allCommands(program))
57
+ cmd.exitOverride();
58
+ try {
59
+ await program.parseAsync([...argv]);
60
+ return { ran: true };
61
+ }
62
+ catch (error) {
63
+ if (error instanceof CommanderError) {
64
+ // A CommanderError is not necessarily a failure: under exitOverride,
65
+ // `--help` throws commander.helpDisplayed and `--version` throws
66
+ // commander.version, both with exitCode 0. Honour the code commander
67
+ // chose rather than assuming 1 (interactive-cli-menu/AC-25).
68
+ const displayedHelp = error.exitCode === 0;
69
+ if (!displayedHelp)
70
+ process.exitCode = error.exitCode;
71
+ return { ran: false, commanderError: error, displayedHelp };
72
+ }
73
+ throw error;
74
+ }
75
+ }
76
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../src/menu/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAI3C,2CAA2C;AAC3C,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAC,KAAgB,EAAE,SAA4B,EAAE;IACxE,OAAO;QACL,MAAM;QACN,OAAO;QACP,GAAG,KAAK,CAAC,IAAI;QACb,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAChD,CAAC;AACJ,CAAC;AAcD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,WAA0B,EAC1B,IAAuB;IAEvB,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC;QAAE,GAAG,CAAC,YAAY,EAAE,CAAC;IAE3D,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACpC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,cAAc,EAAE,CAAC;YACpC,qEAAqE;YACrE,iEAAiE;YACjE,qEAAqE;YACrE,6DAA6D;YAC7D,MAAM,aAAa,GAAG,KAAK,CAAC,QAAQ,KAAK,CAAC,CAAC;YAC3C,IAAI,CAAC,aAAa;gBAAE,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YACtD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAC9D,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Interactive menu orchestration (interactive-cli-menu/AC-10,
3
+ * interactive-cli-menu/AC-13, interactive-cli-menu/AC-14,
4
+ * interactive-cli-menu/AC-18, interactive-cli-menu/AC-19,
5
+ * interactive-cli-menu/AC-20, interactive-cli-menu/AC-26,
6
+ * interactive-cli-menu/AC-29, interactive-cli-menu/AC-31,
7
+ * interactive-cli-menu/AC-35).
8
+ *
9
+ * Reads **no** lifecycle state: no `.aidlc/`, no git, no template loading, no
10
+ * network. The menu answers "what commands exist", never "what should I do
11
+ * next?" — that question belongs to the `aidlc` skill, which cannot depend on
12
+ * the CLI at runtime, so a second state-aware implementation here would drift
13
+ * from it permanently.
14
+ *
15
+ * @module
16
+ */
17
+ import type { Command } from 'commander';
18
+ import { type MenuDeps } from './prompt.js';
19
+ import { type MenuEntry } from './roster.js';
20
+ /** Which entry point asked for the menu; selects the non-interactive fallback. */
21
+ export type MenuMode = 'bare' | 'menu';
22
+ /**
23
+ * The equivalent command form for an entry (interactive-cli-menu/AC-14).
24
+ *
25
+ * This is the teaching string: what the user would type to do the same thing
26
+ * without the menu. The `--` separator `buildArgv` inserts is deliberately
27
+ * omitted — it is a parser safeguard, not part of a command anyone types.
28
+ */
29
+ export declare function commandForm(entry: MenuEntry, values?: readonly string[]): string;
30
+ /**
31
+ * Non-interactive fallbacks (interactive-cli-menu/AC-10,
32
+ * interactive-cli-menu/AC-31).
33
+ *
34
+ * `'bare'` preserves today's contract exactly — the program help on stderr, zero
35
+ * bytes on stdout, exit 1 — because the argv guard runs before `parseAsync`, so
36
+ * commander never emits its own no-args help. `outputHelp` is used rather than
37
+ * `help`, which calls `_exit`.
38
+ *
39
+ * `'menu'` is a deliberate non-replay: the user named a valid command, so the
40
+ * diagnostic is about the terminal, not the command surface.
41
+ */
42
+ export declare function writeNonInteractiveFallback(program: Command, mode: MenuMode): void;
43
+ /**
44
+ * Open the interactive menu.
45
+ *
46
+ * @param makeProgram Factory for a fully-registered program. A factory rather
47
+ * than an instance because `runEntry` dispatches on a throwaway copy, leaving
48
+ * the caller's program free of an `exitOverride` it could not undo.
49
+ */
50
+ export declare function openMenu(makeProgram: () => Command, mode?: MenuMode, deps?: MenuDeps): Promise<void>;
51
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/menu/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,OAAO,EAIL,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAErD,kFAAkF;AAClF,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvC;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAEpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,CASlF;AAgBD;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAC5B,WAAW,EAAE,MAAM,OAAO,EAC1B,IAAI,GAAE,QAAiB,EACvB,IAAI,GAAE,QAAsB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAmDf"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Interactive menu orchestration (interactive-cli-menu/AC-10,
3
+ * interactive-cli-menu/AC-13, interactive-cli-menu/AC-14,
4
+ * interactive-cli-menu/AC-18, interactive-cli-menu/AC-19,
5
+ * interactive-cli-menu/AC-20, interactive-cli-menu/AC-26,
6
+ * interactive-cli-menu/AC-29, interactive-cli-menu/AC-31,
7
+ * interactive-cli-menu/AC-35).
8
+ *
9
+ * Reads **no** lifecycle state: no `.aidlc/`, no git, no template loading, no
10
+ * network. The menu answers "what commands exist", never "what should I do
11
+ * next?" — that question belongs to the `aidlc` skill, which cannot depend on
12
+ * the CLI at runtime, so a second state-aware implementation here would drift
13
+ * from it permanently.
14
+ *
15
+ * @module
16
+ */
17
+ import { buildArgv, runEntry } from './dispatch.js';
18
+ import { CANCEL, defaultDeps, requireNonEmpty, } from './prompt.js';
19
+ import { hintFor, requiredArgs, resolveEntry } from './resolve.js';
20
+ import { ROSTER } from './roster.js';
21
+ /**
22
+ * The equivalent command form for an entry (interactive-cli-menu/AC-14).
23
+ *
24
+ * This is the teaching string: what the user would type to do the same thing
25
+ * without the menu. The `--` separator `buildArgv` inserts is deliberately
26
+ * omitted — it is a parser safeguard, not part of a command anyone types.
27
+ */
28
+ export function commandForm(entry, values = []) {
29
+ return ['aidlc', ...entry.path, ...values].join(' ');
30
+ }
31
+ /**
32
+ * Non-interactive fallbacks (interactive-cli-menu/AC-10,
33
+ * interactive-cli-menu/AC-31).
34
+ *
35
+ * `'bare'` preserves today's contract exactly — the program help on stderr, zero
36
+ * bytes on stdout, exit 1 — because the argv guard runs before `parseAsync`, so
37
+ * commander never emits its own no-args help. `outputHelp` is used rather than
38
+ * `help`, which calls `_exit`.
39
+ *
40
+ * `'menu'` is a deliberate non-replay: the user named a valid command, so the
41
+ * diagnostic is about the terminal, not the command surface.
42
+ */
43
+ export function writeNonInteractiveFallback(program, mode) {
44
+ if (mode === 'bare') {
45
+ program.outputHelp({ error: true });
46
+ }
47
+ else {
48
+ process.stderr.write('menu requires an interactive terminal; run `aidlc --help` for the command list\n');
49
+ }
50
+ process.exitCode = 1;
51
+ }
52
+ /** Build the select options: curated label + command form, description as hint. */
53
+ function optionsFor(program) {
54
+ return ROSTER.map((entry, index) => {
55
+ const cmd = resolveEntry(program, entry);
56
+ return {
57
+ value: String(index),
58
+ // AC-14: every entry carries its equivalent command form, not just the
59
+ // one the user ends up picking.
60
+ label: `${entry.label} · ${commandForm(entry)}`,
61
+ hint: cmd ? hintFor(cmd) : undefined,
62
+ };
63
+ });
64
+ }
65
+ /**
66
+ * Open the interactive menu.
67
+ *
68
+ * @param makeProgram Factory for a fully-registered program. A factory rather
69
+ * than an instance because `runEntry` dispatches on a throwaway copy, leaving
70
+ * the caller's program free of an `exitOverride` it could not undo.
71
+ */
72
+ export async function openMenu(makeProgram, mode = 'bare', deps = defaultDeps) {
73
+ const program = makeProgram();
74
+ if (!deps.interactive()) {
75
+ writeNonInteractiveFallback(program, mode);
76
+ return;
77
+ }
78
+ deps.intro(`aidlc v${program.version() ?? ''}`);
79
+ const choice = await deps.select({
80
+ message: 'What do you want to do?',
81
+ options: optionsFor(program),
82
+ });
83
+ if (choice === CANCEL) {
84
+ deps.outro('cancelled');
85
+ return;
86
+ }
87
+ const entry = ROSTER[Number(choice)];
88
+ if (!entry) {
89
+ deps.outro('cancelled');
90
+ return;
91
+ }
92
+ const values = [];
93
+ // `start` runs its own template picker and offers the isolation choice via
94
+ // the concurrency gate, so prompting here would duplicate it (AC-35).
95
+ if (!entry.delegatesArgs) {
96
+ const leaf = resolveEntry(program, entry);
97
+ for (const arg of leaf ? requiredArgs(leaf) : []) {
98
+ const value = await deps.text({
99
+ message: `${entry.label} — ${arg.name()}`,
100
+ validate: requireNonEmpty(arg.name()),
101
+ });
102
+ // Cancel at any depth returns before dispatch, so there is no partial
103
+ // side effect to unwind: nothing has run yet (AC-26).
104
+ if (value === CANCEL) {
105
+ deps.outro('cancelled');
106
+ return;
107
+ }
108
+ values.push(value);
109
+ }
110
+ }
111
+ // Close this clack session BEFORE dispatching: `aidlc init` opens its own
112
+ // intro, and nesting them stacks two session headers while leaving the
113
+ // menu's unclosed. The outro doubles as the AC-14 teaching line.
114
+ deps.outro(commandForm(entry, values));
115
+ await runEntry(makeProgram, buildArgv(entry, values));
116
+ }
117
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/menu/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Bare-invocation detection (interactive-cli-menu/AC-3).
3
+ *
4
+ * The zero-argument case is dispatched by an argv guard evaluated *before*
5
+ * `program.parseAsync()`, never by a root `.action()` handler. A root action
6
+ * changes commander's unknown-command diagnostic from
7
+ * `error: unknown command 'x'` to `error: too many arguments. Expected 0
8
+ * arguments but got 1: x.`, which would break the untouched-CLI contract
9
+ * (interactive-cli-menu/AC-8).
10
+ *
11
+ * @module
12
+ */
13
+ /**
14
+ * Whether this is a bare `aidlc` invocation — no arguments, no flags.
15
+ *
16
+ * `argv` is a full Node argv: `[execPath, scriptPath, ...args]`, so "no
17
+ * arguments" is length 2 exactly. Anything longer belongs to commander.
18
+ */
19
+ export declare function isBareInvocation(argv: readonly string[]): boolean;
20
+ //# sourceMappingURL=invocation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocation.d.ts","sourceRoot":"","sources":["../../src/menu/invocation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAEjE"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Bare-invocation detection (interactive-cli-menu/AC-3).
3
+ *
4
+ * The zero-argument case is dispatched by an argv guard evaluated *before*
5
+ * `program.parseAsync()`, never by a root `.action()` handler. A root action
6
+ * changes commander's unknown-command diagnostic from
7
+ * `error: unknown command 'x'` to `error: too many arguments. Expected 0
8
+ * arguments but got 1: x.`, which would break the untouched-CLI contract
9
+ * (interactive-cli-menu/AC-8).
10
+ *
11
+ * @module
12
+ */
13
+ /**
14
+ * Whether this is a bare `aidlc` invocation — no arguments, no flags.
15
+ *
16
+ * `argv` is a full Node argv: `[execPath, scriptPath, ...args]`, so "no
17
+ * arguments" is length 2 exactly. Anything longer belongs to commander.
18
+ */
19
+ export function isBareInvocation(argv) {
20
+ return argv.length === 2;
21
+ }
22
+ //# sourceMappingURL=invocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocation.js","sourceRoot":"","sources":["../../src/menu/invocation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAuB;IACtD,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Prompt seam for the interactive menu (interactive-cli-menu/AC-12,
3
+ * interactive-cli-menu/AC-13, interactive-cli-menu/AC-26).
4
+ *
5
+ * The only module that imports `@clack/prompts`. Everything the menu does that
6
+ * touches a terminal goes through `MenuDeps`, so tests drive both the
7
+ * interactive and non-interactive branches without emulating a TTY — the same
8
+ * shape as the doctor's injectable `ConfirmFn`.
9
+ *
10
+ * @module
11
+ */
12
+ /**
13
+ * Cancellation sentinel, owned by this module.
14
+ *
15
+ * Deliberately *not* a re-export: `@clack/prompts` exports no symbol at all. Its
16
+ * cancel value is a private `Symbol("clack:cancel")` that is not
17
+ * `Symbol.for`-registered, so it can be neither imported nor reconstructed —
18
+ * only the `isCancel()` predicate is public, which is why every call site in
19
+ * this package uses that. The clack-backed adapters below translate, so injected
20
+ * test doubles return exactly what production returns.
21
+ */
22
+ export declare const CANCEL: unique symbol;
23
+ export interface SelectRequest {
24
+ message: string;
25
+ options: {
26
+ value: string;
27
+ label: string;
28
+ hint?: string;
29
+ }[];
30
+ }
31
+ export interface TextRequest {
32
+ message: string;
33
+ placeholder?: string;
34
+ /**
35
+ * Return a message to reject the value and re-prompt.
36
+ *
37
+ * The value is optional because clack passes `undefined` before the field has
38
+ * been touched — which is exactly the empty case that must be rejected.
39
+ */
40
+ validate?: (value: string | undefined) => string | undefined;
41
+ }
42
+ export type SelectFn = (req: SelectRequest) => Promise<string | typeof CANCEL>;
43
+ export type TextFn = (req: TextRequest) => Promise<string | typeof CANCEL>;
44
+ export interface MenuDeps {
45
+ select: SelectFn;
46
+ text: TextFn;
47
+ /** Whether an interactive prompt can be rendered at all. */
48
+ interactive: () => boolean;
49
+ intro: (message: string) => void;
50
+ outro: (message: string) => void;
51
+ }
52
+ /**
53
+ * Both streams must be a TTY (interactive-cli-menu/AC-10).
54
+ *
55
+ * clack reads keys from stdin and renders to stdout, so either alone is
56
+ * insufficient: `aidlc | less` has a TTY stdin with a piped stdout and would
57
+ * paint escape sequences into the pipe, while `echo x | aidlc` has the reverse
58
+ * and would render a prompt nobody can answer. Both idioms exist in this package
59
+ * (`commands/cost.ts` keys on stdin, `doctor/context.ts` on stdout), so the
60
+ * conjunction is stated explicitly rather than inherited from a precedent.
61
+ */
62
+ export declare function isInteractive(): boolean;
63
+ /**
64
+ * Reject empty or whitespace-only input for a required positional.
65
+ *
66
+ * clack's text prompt yields `''` — not the cancel symbol — when Enter is
67
+ * pressed on an empty field with no default, so without this the menu would
68
+ * dispatch a meaningless invocation the user cannot distinguish from a cancel.
69
+ * Commander would reject a missing `<arg>`; this rejects it earlier, where the
70
+ * user can still fix it.
71
+ */
72
+ export declare function requireNonEmpty(label: string): (value: string | undefined) => string | undefined;
73
+ export declare const defaultDeps: MenuDeps;
74
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/menu/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;;;;;;;;GASG;AACH,eAAO,MAAM,MAAM,EAAE,OAAO,MAAoC,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC5D;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,SAAS,CAAC;CAC9D;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC;AAC/E,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC;AAE3E,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,WAAW,EAAE,MAAM,OAAO,CAAC;IAC3B,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAmBD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,GACZ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,SAAS,CAEnD;AAED,eAAO,MAAM,WAAW,EAAE,QAMzB,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Prompt seam for the interactive menu (interactive-cli-menu/AC-12,
3
+ * interactive-cli-menu/AC-13, interactive-cli-menu/AC-26).
4
+ *
5
+ * The only module that imports `@clack/prompts`. Everything the menu does that
6
+ * touches a terminal goes through `MenuDeps`, so tests drive both the
7
+ * interactive and non-interactive branches without emulating a TTY — the same
8
+ * shape as the doctor's injectable `ConfirmFn`.
9
+ *
10
+ * @module
11
+ */
12
+ import * as clack from '@clack/prompts';
13
+ /**
14
+ * Cancellation sentinel, owned by this module.
15
+ *
16
+ * Deliberately *not* a re-export: `@clack/prompts` exports no symbol at all. Its
17
+ * cancel value is a private `Symbol("clack:cancel")` that is not
18
+ * `Symbol.for`-registered, so it can be neither imported nor reconstructed —
19
+ * only the `isCancel()` predicate is public, which is why every call site in
20
+ * this package uses that. The clack-backed adapters below translate, so injected
21
+ * test doubles return exactly what production returns.
22
+ */
23
+ export const CANCEL = Symbol('aidlc:menu:cancel');
24
+ /**
25
+ * Both streams must be a TTY (interactive-cli-menu/AC-10).
26
+ *
27
+ * clack reads keys from stdin and renders to stdout, so either alone is
28
+ * insufficient: `aidlc | less` has a TTY stdin with a piped stdout and would
29
+ * paint escape sequences into the pipe, while `echo x | aidlc` has the reverse
30
+ * and would render a prompt nobody can answer. Both idioms exist in this package
31
+ * (`commands/cost.ts` keys on stdin, `doctor/context.ts` on stdout), so the
32
+ * conjunction is stated explicitly rather than inherited from a precedent.
33
+ */
34
+ export function isInteractive() {
35
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
36
+ }
37
+ const clackSelect = async (req) => {
38
+ const value = await clack.select({
39
+ message: req.message,
40
+ options: req.options,
41
+ });
42
+ return clack.isCancel(value) ? CANCEL : value;
43
+ };
44
+ const clackText = async (req) => {
45
+ const value = await clack.text({
46
+ message: req.message,
47
+ placeholder: req.placeholder,
48
+ validate: req.validate,
49
+ });
50
+ return clack.isCancel(value) ? CANCEL : value;
51
+ };
52
+ /**
53
+ * Reject empty or whitespace-only input for a required positional.
54
+ *
55
+ * clack's text prompt yields `''` — not the cancel symbol — when Enter is
56
+ * pressed on an empty field with no default, so without this the menu would
57
+ * dispatch a meaningless invocation the user cannot distinguish from a cancel.
58
+ * Commander would reject a missing `<arg>`; this rejects it earlier, where the
59
+ * user can still fix it.
60
+ */
61
+ export function requireNonEmpty(label) {
62
+ return (value) => ((value ?? '').trim() === '' ? `${label} is required` : undefined);
63
+ }
64
+ export const defaultDeps = {
65
+ select: clackSelect,
66
+ text: clackText,
67
+ interactive: isInteractive,
68
+ intro: (message) => clack.intro(message),
69
+ outro: (message) => clack.outro(message),
70
+ };
71
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/menu/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AAExC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,MAAM,GAAkB,MAAM,CAAC,mBAAmB,CAAC,CAAC;AA+BjE;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC;AACvE,CAAC;AAED,MAAM,WAAW,GAAa,KAAK,EAAE,GAAG,EAAE,EAAE;IAC1C,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC;QAC/B,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,OAAO,EAAE,GAAG,CAAC,OAAO;KACrB,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAE,KAAgB,CAAC;AAC5D,CAAC,CAAC;AAEF,MAAM,SAAS,GAAW,KAAK,EAAE,GAAG,EAAE,EAAE;IACtC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC;QAC7B,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,QAAQ,EAAE,GAAG,CAAC,QAAQ;KACvB,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAE,KAAgB,CAAC;AAC5D,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAa;IAEb,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAa;IACnC,MAAM,EAAE,WAAW;IACnB,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,aAAa;IAC1B,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;IACxC,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;CACzC,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Roster → commander resolution (interactive-cli-menu/AC-16,
3
+ * interactive-cli-menu/AC-17, interactive-cli-menu/AC-34).
4
+ *
5
+ * Pure functions over a commander program. Required positionals are derived
6
+ * structurally from `registeredArguments` rather than restated in the roster, so
7
+ * the prompt set cannot go stale when a command's signature changes.
8
+ *
9
+ * @module
10
+ */
11
+ import type { Argument, Command } from 'commander';
12
+ import { type MenuEntry } from './roster.js';
13
+ /**
14
+ * Walk an entry's path from the program root to its leaf command.
15
+ *
16
+ * @returns the leaf `Command`, or `null` if any segment is unregistered.
17
+ */
18
+ export declare function resolveEntry(program: Command, entry: MenuEntry): Command | null;
19
+ /**
20
+ * Required positional arguments of a command, in declaration order
21
+ * (interactive-cli-menu/AC-34).
22
+ *
23
+ * `registeredArguments` is public commander API. Across the roster only
24
+ * `review <instance> <artifact>` and `knowledge query <term>` have any.
25
+ */
26
+ export declare function requiredArgs(cmd: Command): readonly Argument[];
27
+ /**
28
+ * Secondary hint text for an entry: the leaf command's own description
29
+ * (interactive-cli-menu/AC-16). Never used as the label.
30
+ */
31
+ export declare function hintFor(cmd: Command): string;
32
+ /**
33
+ * Top-level command names on the program.
34
+ *
35
+ * Deliberately performs no hidden-command *detection*: `program.commands`
36
+ * already contains hidden commands (verified with the deprecated `setup`
37
+ * alias), `Command` exposes no public `hidden` accessor — the `hidden: boolean`
38
+ * in commander's typings belongs to `Option` — and the private `_hidden` must
39
+ * not be relied on. Commander's synthetic `help` command is absent from
40
+ * `program.commands` and needs no entry.
41
+ */
42
+ export declare function allTopLevelNames(program: Command): string[];
43
+ /**
44
+ * Top-level names the roster accounts for — first path segments only.
45
+ *
46
+ * A nested entry such as `['knowledge','query']` accounts for nothing beyond
47
+ * `knowledge`'s appearance as a *rostered* name, which is why `knowledge` is
48
+ * also carried in `EXCLUSIONS`: the parent itself is not on the menu.
49
+ */
50
+ export declare function rosterTopLevelNames(roster?: readonly MenuEntry[]): string[];
51
+ /**
52
+ * Top-level commands accounted for by neither the roster nor the exclusion list
53
+ * (interactive-cli-menu/AC-17). A non-empty result is a contract violation: a
54
+ * command was registered and nobody decided whether it belongs on the menu.
55
+ */
56
+ export declare function unaccountedCommands(program: Command, roster?: readonly MenuEntry[], exclusions?: Readonly<Record<string, string>>): string[];
57
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/menu/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,GAAG,IAAI,CAQ/E;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,QAAQ,EAAE,CAE9D;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAE5C;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAE3D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,GAAE,SAAS,SAAS,EAAW,GAAG,MAAM,EAAE,CAEnF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,MAAM,GAAE,SAAS,SAAS,EAAW,EACrC,UAAU,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAc,GACxD,MAAM,EAAE,CAOV"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Roster → commander resolution (interactive-cli-menu/AC-16,
3
+ * interactive-cli-menu/AC-17, interactive-cli-menu/AC-34).
4
+ *
5
+ * Pure functions over a commander program. Required positionals are derived
6
+ * structurally from `registeredArguments` rather than restated in the roster, so
7
+ * the prompt set cannot go stale when a command's signature changes.
8
+ *
9
+ * @module
10
+ */
11
+ import { EXCLUSIONS, ROSTER } from './roster.js';
12
+ /**
13
+ * Walk an entry's path from the program root to its leaf command.
14
+ *
15
+ * @returns the leaf `Command`, or `null` if any segment is unregistered.
16
+ */
17
+ export function resolveEntry(program, entry) {
18
+ let current = program;
19
+ for (const segment of entry.path) {
20
+ const next = current.commands.find((child) => child.name() === segment);
21
+ if (!next)
22
+ return null;
23
+ current = next;
24
+ }
25
+ return current === program ? null : current;
26
+ }
27
+ /**
28
+ * Required positional arguments of a command, in declaration order
29
+ * (interactive-cli-menu/AC-34).
30
+ *
31
+ * `registeredArguments` is public commander API. Across the roster only
32
+ * `review <instance> <artifact>` and `knowledge query <term>` have any.
33
+ */
34
+ export function requiredArgs(cmd) {
35
+ return cmd.registeredArguments.filter((arg) => arg.required);
36
+ }
37
+ /**
38
+ * Secondary hint text for an entry: the leaf command's own description
39
+ * (interactive-cli-menu/AC-16). Never used as the label.
40
+ */
41
+ export function hintFor(cmd) {
42
+ return cmd.description();
43
+ }
44
+ /**
45
+ * Top-level command names on the program.
46
+ *
47
+ * Deliberately performs no hidden-command *detection*: `program.commands`
48
+ * already contains hidden commands (verified with the deprecated `setup`
49
+ * alias), `Command` exposes no public `hidden` accessor — the `hidden: boolean`
50
+ * in commander's typings belongs to `Option` — and the private `_hidden` must
51
+ * not be relied on. Commander's synthetic `help` command is absent from
52
+ * `program.commands` and needs no entry.
53
+ */
54
+ export function allTopLevelNames(program) {
55
+ return program.commands.map((cmd) => cmd.name());
56
+ }
57
+ /**
58
+ * Top-level names the roster accounts for — first path segments only.
59
+ *
60
+ * A nested entry such as `['knowledge','query']` accounts for nothing beyond
61
+ * `knowledge`'s appearance as a *rostered* name, which is why `knowledge` is
62
+ * also carried in `EXCLUSIONS`: the parent itself is not on the menu.
63
+ */
64
+ export function rosterTopLevelNames(roster = ROSTER) {
65
+ return roster.map((entry) => entry.path[0]);
66
+ }
67
+ /**
68
+ * Top-level commands accounted for by neither the roster nor the exclusion list
69
+ * (interactive-cli-menu/AC-17). A non-empty result is a contract violation: a
70
+ * command was registered and nobody decided whether it belongs on the menu.
71
+ */
72
+ export function unaccountedCommands(program, roster = ROSTER, exclusions = EXCLUSIONS) {
73
+ const rostered = new Set(roster.filter((entry) => entry.path.length === 1).map((entry) => entry.path[0]));
74
+ return allTopLevelNames(program).filter((name) => !rostered.has(name) && !(name in exclusions));
75
+ }
76
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/menu/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,EAAkB,MAAM,aAAa,CAAC;AAEjE;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,OAAgB,EAAE,KAAgB;IAC7D,IAAI,OAAO,GAAY,OAAO,CAAC;IAC/B,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,OAAO,CAAC,CAAC;QACxE,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;IACD,OAAO,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;AAC9C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,OAAO,GAAG,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAY;IAClC,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,OAAO,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAA+B,MAAM;IACvE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAgB,EAChB,SAA+B,MAAM,EACrC,aAA+C,UAAU;IAEzD,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CACjF,CAAC;IACF,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAC,MAAM,CACrC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,UAAU,CAAC,CACvD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The menu roster — the single declaration site for what the menu offers
3
+ * (interactive-cli-menu/AC-32, interactive-cli-menu/AC-33,
4
+ * interactive-cli-menu/AC-15, interactive-cli-menu/AC-16).
5
+ *
6
+ * Data only, no logic. Two properties are pinned by tests rather than by
7
+ * convention: array order IS display order, and every top-level command on the
8
+ * program appears in exactly one of `ROSTER` (by first path segment) or
9
+ * `EXCLUSIONS`.
10
+ *
11
+ * Labels are curated here rather than read from commander. Several
12
+ * `.description()` strings read poorly as menu labels — `doctor`'s is 76
13
+ * characters, and `add`/`knowledge` describe a group rather than an action — so
14
+ * descriptions serve as secondary hint text only.
15
+ *
16
+ * @module
17
+ */
18
+ /** Lifecycle stage an entry belongs to; drives visual grouping only. */
19
+ export type MenuBucket = 'setup' | 'begin' | 'resume' | 'inspect' | 'maintain';
20
+ export interface MenuEntry {
21
+ /** Command path from the program root: `['start']`, `['knowledge','query']`. */
22
+ readonly path: readonly string[];
23
+ /** Curated label — never commander's `.description()`. */
24
+ readonly label: string;
25
+ readonly bucket: MenuBucket;
26
+ /**
27
+ * Skip menu-side prompting for required positionals and let the command run
28
+ * its own wizard. `start` only (interactive-cli-menu/AC-35).
29
+ */
30
+ readonly delegatesArgs?: true;
31
+ }
32
+ /**
33
+ * The 12 menu entries, in display order: setup → begin → resume → inspect →
34
+ * maintain (interactive-cli-menu/AC-32).
35
+ */
36
+ export declare const ROSTER: readonly MenuEntry[];
37
+ /**
38
+ * Every top-level command deliberately kept out of the menu, with its reason
39
+ * (interactive-cli-menu/AC-33). Excluded commands stay fully usable by typing
40
+ * them; there is no "more" entry.
41
+ *
42
+ * The exclusion criterion is **not** whether a command is a group — it is
43
+ * whether its bare invocation does something useful. `cost` is a group *and* a
44
+ * useful bare report, so it is rostered. `knowledge` must be listed even though
45
+ * `knowledge query` is rostered, because the contract test compares top-level
46
+ * names and `knowledge` is one.
47
+ */
48
+ export declare const EXCLUSIONS: Readonly<Record<string, string>>;
49
+ /** Bucket order for grouping; matches the order entries appear in `ROSTER`. */
50
+ export declare const BUCKET_ORDER: readonly MenuBucket[];
51
+ //# sourceMappingURL=roster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roster.d.ts","sourceRoot":"","sources":["../../src/menu/roster.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,wEAAwE;AACxE,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAE/E,MAAM,WAAW,SAAS;IACxB,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC;CAC/B;AAED;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,SAAS,SAAS,EAatC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUvD,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAM7C,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The menu roster — the single declaration site for what the menu offers
3
+ * (interactive-cli-menu/AC-32, interactive-cli-menu/AC-33,
4
+ * interactive-cli-menu/AC-15, interactive-cli-menu/AC-16).
5
+ *
6
+ * Data only, no logic. Two properties are pinned by tests rather than by
7
+ * convention: array order IS display order, and every top-level command on the
8
+ * program appears in exactly one of `ROSTER` (by first path segment) or
9
+ * `EXCLUSIONS`.
10
+ *
11
+ * Labels are curated here rather than read from commander. Several
12
+ * `.description()` strings read poorly as menu labels — `doctor`'s is 76
13
+ * characters, and `add`/`knowledge` describe a group rather than an action — so
14
+ * descriptions serve as secondary hint text only.
15
+ *
16
+ * @module
17
+ */
18
+ /**
19
+ * The 12 menu entries, in display order: setup → begin → resume → inspect →
20
+ * maintain (interactive-cli-menu/AC-32).
21
+ */
22
+ export const ROSTER = [
23
+ { path: ['init'], bucket: 'setup', label: 'Set up AIDLC in this project' },
24
+ { path: ['doctor'], bucket: 'setup', label: 'Run health checks' },
25
+ { path: ['update'], bucket: 'setup', label: 'Update AIDLC' },
26
+ { path: ['start'], bucket: 'begin', label: 'Begin new work', delegatesArgs: true },
27
+ { path: ['discover'], bucket: 'begin', label: 'Explore the codebase' },
28
+ { path: ['continue'], bucket: 'resume', label: 'Resume work' },
29
+ { path: ['status'], bucket: 'inspect', label: 'Show progress' },
30
+ { path: ['cost'], bucket: 'inspect', label: 'Cost report' },
31
+ { path: ['metrics'], bucket: 'inspect', label: 'Project metrics' },
32
+ { path: ['knowledge', 'query'], bucket: 'inspect', label: 'Search project knowledge' },
33
+ { path: ['review'], bucket: 'inspect', label: 'Review an artifact' },
34
+ { path: ['docs'], bucket: 'maintain', label: 'Generate onboarding docs' },
35
+ ];
36
+ /**
37
+ * Every top-level command deliberately kept out of the menu, with its reason
38
+ * (interactive-cli-menu/AC-33). Excluded commands stay fully usable by typing
39
+ * them; there is no "more" entry.
40
+ *
41
+ * The exclusion criterion is **not** whether a command is a group — it is
42
+ * whether its bare invocation does something useful. `cost` is a group *and* a
43
+ * useful bare report, so it is rostered. `knowledge` must be listed even though
44
+ * `knowledge query` is rostered, because the contract test compares top-level
45
+ * names and `knowledge` is one.
46
+ */
47
+ export const EXCLUSIONS = {
48
+ gate: 'CI/automation surface — consumed by scripts, not people',
49
+ transition: 'CI/automation surface; the phase skills drive it',
50
+ claim: 'session plumbing, not a user task',
51
+ release: 'session plumbing, not a user task',
52
+ add: 'bare `aidlc add` exits 1 with help on stderr — no useful bare form',
53
+ knowledge: 'bare `aidlc knowledge` exits 1; its useful leaf `knowledge query` is a roster entry',
54
+ setup: 'hidden deprecated alias of `init`',
55
+ menu: 'itself',
56
+ };
57
+ /** Bucket order for grouping; matches the order entries appear in `ROSTER`. */
58
+ export const BUCKET_ORDER = [
59
+ 'setup',
60
+ 'begin',
61
+ 'resume',
62
+ 'inspect',
63
+ 'maintain',
64
+ ];
65
+ //# sourceMappingURL=roster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roster.js","sourceRoot":"","sources":["../../src/menu/roster.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAkBH;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAyB;IAC1C,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,8BAA8B,EAAE;IAC1E,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE;IACjE,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE;IAC5D,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,IAAI,EAAE;IAClF,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,sBAAsB,EAAE;IACtE,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE;IAC9D,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE;IAC/D,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE;IAC3D,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE;IAClE,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,0BAA0B,EAAE;IACtF,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,oBAAoB,EAAE;IACpE,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,0BAA0B,EAAE;CAC1E,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAqC;IAC1D,IAAI,EAAE,yDAAyD;IAC/D,UAAU,EAAE,kDAAkD;IAC9D,KAAK,EAAE,mCAAmC;IAC1C,OAAO,EAAE,mCAAmC;IAC5C,GAAG,EAAE,oEAAoE;IACzE,SAAS,EACP,qFAAqF;IACvF,KAAK,EAAE,mCAAmC;IAC1C,IAAI,EAAE,QAAQ;CACf,CAAC;AAEF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAA0B;IACjD,OAAO;IACP,OAAO;IACP,QAAQ;IACR,SAAS;IACT,UAAU;CACX,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rasensio/aidlc",
3
- "version": "1.9.1",
3
+ "version": "1.10.1",
4
4
  "description": "AI Development Lifecycle Framework — structured lifecycle guidance for AI coding agents across platforms",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -24,7 +24,7 @@
24
24
  "commander": "15.0.0",
25
25
  "picomatch": "4.0.5",
26
26
  "yaml": "2.9.0",
27
- "@rasensio/aidlc-content": "1.9.1"
27
+ "@rasensio/aidlc-content": "1.10.1"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "22.15.32",