@dzhechkov/harness-cli 0.8.10 → 0.8.16

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.
@@ -0,0 +1,149 @@
1
+ /**
2
+ * ONE definition of "a dz command", and the ONE enumeration every consumer derives from.
3
+ *
4
+ * WHY (backlog 3b2b05e0, ADR-001 in `features/command-count-triad/03_adr/`): three sources answered
5
+ * "how many dz commands are there" with three numbers — the dispatcher's `case` labels, the rendered
6
+ * `dz --help` list and `dz name-check`'s collision sweep — because each used its own definition, so
7
+ * adding a command meant guessing which number to move and which test would redden.
8
+ *
9
+ * THE DEFINITION (ADR-001 §1): *a dz command is a name that is a `case` label of the main
10
+ * `switch (command)` in `cli.ts` AND has a ` dz <name>` line in USAGE.* `DZ_COMMANDS` is the
11
+ * canonical enumeration of exactly those names. `dispatched` and `documented` are DERIVED from the
12
+ * SAME file text and must each equal `DZ_COMMANDS` as a SET — both directions — modulo two
13
+ * explicit, reasoned exception lists:
14
+ *
15
+ * dispatched == declared ∪ internal (INTERNAL_ENTRY_POINTS — runs, deliberately not in USAGE)
16
+ * documented == declared ∪ pseudo (PSEUDO_COMMANDS — in USAGE, handled pre-dispatch)
17
+ *
18
+ * Everything here is PURE: text in, name sets out. It reads no file and never imports `cli.js` (that
19
+ * would be a cycle — `cli.ts` imports THIS module), so the layer-1 test and `nameCheckScan` share one
20
+ * parser and cannot drift apart by construction (ADR-001 DD3).
21
+ *
22
+ * NAMES, NEVER ONLY COUNTS (ADR-001 DD2): no count literal lives here or in any consumer. A
23
+ * cardinality guard dies to add-one-delete-one — MEASURED live in this repo: the harness-cli README's
24
+ * inventory held 85 names and passed a `size === 85` guard while listing `help` and omitting
25
+ * `verify-pack`.
26
+ */
27
+ /**
28
+ * One reasoned exemption from the definition. `reason` is mandatory — an exemption without a written
29
+ * reason is the allowlist this feature closed; `since` ages it.
30
+ */
31
+ export interface CommandException {
32
+ readonly name: string;
33
+ readonly reason: string;
34
+ /** `YYYY-MM-DD`. */
35
+ readonly since: string;
36
+ }
37
+ /** The four name sets derived from one `cli.ts` text, plus the two exception lists in force. */
38
+ export interface CommandInventory {
39
+ /** `DZ_COMMANDS` — the canonical enumeration, parsed from the literal block. */
40
+ readonly declared: readonly string[];
41
+ /** `case` labels of the main `switch (command)` — what `dz <name>` will actually RUN. */
42
+ readonly dispatched: readonly string[];
43
+ /** ` dz <name>` lines in USAGE — what a user can DISCOVER. */
44
+ readonly documented: readonly string[];
45
+ /** Documented but not dispatched ({@link PSEUDO_COMMANDS}). */
46
+ readonly pseudo: readonly string[];
47
+ /** Dispatched but deliberately not documented ({@link INTERNAL_ENTRY_POINTS}). */
48
+ readonly internal: readonly string[];
49
+ }
50
+ /**
51
+ * Documented in USAGE, handled BEFORE the dispatch switch, therefore not canonical commands. `help`
52
+ * is the only member: it prints USAGE and takes no flags, and adding a `case 'help':` purely so a
53
+ * definition holds would make the definition serve the number (ADR-001 §2, option O1b).
54
+ */
55
+ export declare const PSEUDO_COMMANDS: readonly CommandException[];
56
+ /**
57
+ * Dispatched but deliberately NOT documented — EMPTY today, and that emptiness is the decision, not
58
+ * an oversight (ADR-001 §3, option O2a). The four names that sat in `command-count.test.ts`'s
59
+ * `UNDOCUMENTED_ALLOWLIST` — `mr-rakes`, `retro`, `feature-adr-setup`, `bto-optimize` — got USAGE
60
+ * lines instead of being parked here: discoverability IS existence, and an allowlist entry is a
61
+ * promise to fix later that already cost one field bug report (`project-skills`, 2026-08-25).
62
+ *
63
+ * The list survives as the MECHANISM: a future genuinely internal entry point gets a reasoned home
64
+ * instead of a silent regex exemption. Its members are NOT commands and are NOT in `DZ_COMMANDS`;
65
+ * joining it costs a reason and a date, checked by {@link validateExceptionList}.
66
+ */
67
+ export declare const INTERNAL_ENTRY_POINTS: readonly CommandException[];
68
+ /**
69
+ * Refuse an exemption that carries no reason or no usable date, NAMING the offending entry. Without
70
+ * it, "exception list with a reason" decays back into the allowlist it replaced. Proven to FIRE on a
71
+ * synthetic reason-less entry, not merely exercised on the valid real lists (ADR-001 §5 / AM-3) — a
72
+ * validator only ever fed valid input is dead code.
73
+ *
74
+ * @param list the exception list to check
75
+ * @param label how to name the list in the error (e.g. `PSEUDO_COMMANDS`)
76
+ * @throws Error naming the offending entry
77
+ */
78
+ export declare function validateExceptionList(list: readonly CommandException[], label?: string): readonly CommandException[];
79
+ /**
80
+ * Replace the CONTENT of every string, template literal, regex and comment with spaces (newlines
81
+ * preserved), leaving only real code, so a `}` inside a string, a regex or a comment in a case body
82
+ * cannot prematurely close the switch. Case LABELS are still matched on the ORIGINAL source. Promoted
83
+ * out of `test/command-count.test.ts` so the CLI and the test share ONE parser.
84
+ *
85
+ * REGEX LITERALS (QE round 1, Codex `gpt-5.6-sol`, `[P2]` on this function): a lone `}` inside `/}/`
86
+ * used to be counted as a real closing brace, so the brace walk in {@link dispatchedCommands} ended
87
+ * on the FIRST case body and the inventory was TRUNCATED — with the parity guard, `dz name-check`
88
+ * and the exported API all under-counting on valid source. Detection is {@link regexMayStart}, whose
89
+ * heuristic and limit are stated there; on top of it this scanner adds a hard SAME-LINE bound: a
90
+ * regex literal cannot contain an unescaped newline, so a candidate whose closing `/` is not on the
91
+ * same line is declared a false positive and the slash is emitted as ordinary code. That bound is
92
+ * what keeps a mis-read division from eating an unbounded span. `[...]` character classes are
93
+ * tracked so `/[/]/` and `/[{}]/` close where they really close.
94
+ */
95
+ export declare function stripNonCode(src: string): string;
96
+ /**
97
+ * The names `dz <name>` will actually RUN: `case` labels of the MAIN `switch (command)`, found by
98
+ * brace-aware scanning so extraction is indentation-independent (NFR-6).
99
+ *
100
+ * @throws Error when the main switch is absent — the "guard cannot run" signal: returning `[]` would
101
+ * let every set-equality above it pass at ∅ == ∅.
102
+ */
103
+ export declare function dispatchedCommands(src: string): string[];
104
+ /**
105
+ * Cut out the body of the `USAGE` template literal — the ONE block `dz --help` actually renders.
106
+ *
107
+ * WHY it is a function and not a regex (QE round 2, `[P2]`): the naive whole-file scan for
108
+ * ` dz <name>` also swallowed every OTHER help template in `cli.ts`. Scanning stops at the first
109
+ * unescaped backtick outside an `${…}` interpolation, so `${PRESET_NAMES.join(', ')}` on the last
110
+ * USAGE line does not end the literal early.
111
+ *
112
+ * LIMIT, named: a template literal NESTED inside an interpolation would need recursion and is not
113
+ * handled — `cli.ts` has none today, and if one appears this throws rather than guessing.
114
+ *
115
+ * @throws Error when the literal is absent or unterminated — same "cannot run" discipline as
116
+ * {@link dispatchedCommands}: silently returning `''` would let `documented == declared ∪ pseudo`
117
+ * pass at ∅ == ∅.
118
+ */
119
+ export declare function usageBlock(src: string): string;
120
+ /**
121
+ * The names a user can DISCOVER: the ` dz <name>` lines of USAGE, read from the SOURCE — the same
122
+ * regex the old count test ran over RENDERED help, deliberately, because the parity test asserts
123
+ * source-set == rendered-set: being right about the text and wrong about what users see is then a
124
+ * FAILURE, not an invisible drift (FR-3.3 / NFR-6).
125
+ *
126
+ * BOUNDED TO USAGE (QE round 2, `[P2]` on the old whole-file regex): a per-command help template
127
+ * such as `BRAIN_USAGE` (`cli.ts:5019+`) carries a dozen ` dz brain …` lines that global
128
+ * `dz --help` never renders. They were being counted; they deduped onto the `brain` already in
129
+ * USAGE, so the leak was invisible BY LUCK, and one unique name in such a block would have invented
130
+ * a documented command for both `dz name-check` and the parity guard.
131
+ */
132
+ export declare function documentedCommands(src: string): string[];
133
+ /**
134
+ * The canonical enumeration: the names inside the `DZ_COMMANDS` literal block, parsed from TEXT so
135
+ * all four sets come from ONE artefact. The runtime export is separately asserted equal to this,
136
+ * which is what catches a parser that drifts from the literal it reads.
137
+ *
138
+ * @throws Error when the block is absent — same "cannot run" discipline as {@link dispatchedCommands}.
139
+ */
140
+ export declare function declaredCommands(src: string): string[];
141
+ /**
142
+ * All four name sets from ONE `cli.ts` text, with both exception lists validated FIRST — deliberately
143
+ * up front, so a real entry with an empty reason breaks the whole inventory rather than one
144
+ * assertion, and the exemption channel cannot rot quietly (ADR-001 §5).
145
+ *
146
+ * @param src the full text of `packages/@dzhechkov/harness-cli/src/cli.ts`
147
+ */
148
+ export declare function commandInventory(src: string): CommandInventory;
149
+ //# sourceMappingURL=command-inventory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-inventory.d.ts","sourceRoot":"","sources":["../src/command-inventory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oBAAoB;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,gGAAgG;AAChG,MAAM,WAAW,gBAAgB;IAC/B,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,yFAAyF;IACzF,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,gBAAgB,EAMtD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,gBAAgB,EAAO,CAAC;AAIrE;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,KAAK,SAA2B,GAC/B,SAAS,gBAAgB,EAAE,CAkB7B;AA+CD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA+ChD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAyBxD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAe9C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAIxD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAQtD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAU9D"}
@@ -0,0 +1,405 @@
1
+ /**
2
+ * ONE definition of "a dz command", and the ONE enumeration every consumer derives from.
3
+ *
4
+ * WHY (backlog 3b2b05e0, ADR-001 in `features/command-count-triad/03_adr/`): three sources answered
5
+ * "how many dz commands are there" with three numbers — the dispatcher's `case` labels, the rendered
6
+ * `dz --help` list and `dz name-check`'s collision sweep — because each used its own definition, so
7
+ * adding a command meant guessing which number to move and which test would redden.
8
+ *
9
+ * THE DEFINITION (ADR-001 §1): *a dz command is a name that is a `case` label of the main
10
+ * `switch (command)` in `cli.ts` AND has a ` dz <name>` line in USAGE.* `DZ_COMMANDS` is the
11
+ * canonical enumeration of exactly those names. `dispatched` and `documented` are DERIVED from the
12
+ * SAME file text and must each equal `DZ_COMMANDS` as a SET — both directions — modulo two
13
+ * explicit, reasoned exception lists:
14
+ *
15
+ * dispatched == declared ∪ internal (INTERNAL_ENTRY_POINTS — runs, deliberately not in USAGE)
16
+ * documented == declared ∪ pseudo (PSEUDO_COMMANDS — in USAGE, handled pre-dispatch)
17
+ *
18
+ * Everything here is PURE: text in, name sets out. It reads no file and never imports `cli.js` (that
19
+ * would be a cycle — `cli.ts` imports THIS module), so the layer-1 test and `nameCheckScan` share one
20
+ * parser and cannot drift apart by construction (ADR-001 DD3).
21
+ *
22
+ * NAMES, NEVER ONLY COUNTS (ADR-001 DD2): no count literal lives here or in any consumer. A
23
+ * cardinality guard dies to add-one-delete-one — MEASURED live in this repo: the harness-cli README's
24
+ * inventory held 85 names and passed a `size === 85` guard while listing `help` and omitting
25
+ * `verify-pack`.
26
+ */
27
+ /**
28
+ * Documented in USAGE, handled BEFORE the dispatch switch, therefore not canonical commands. `help`
29
+ * is the only member: it prints USAGE and takes no flags, and adding a `case 'help':` purely so a
30
+ * definition holds would make the definition serve the number (ADR-001 §2, option O1b).
31
+ */
32
+ export const PSEUDO_COMMANDS = [
33
+ {
34
+ name: 'help',
35
+ reason: 'pre-dispatch built-in: prints USAGE and returns before the main switch (command) — no case label, no flags',
36
+ since: '2026-09-05',
37
+ },
38
+ ];
39
+ /**
40
+ * Dispatched but deliberately NOT documented — EMPTY today, and that emptiness is the decision, not
41
+ * an oversight (ADR-001 §3, option O2a). The four names that sat in `command-count.test.ts`'s
42
+ * `UNDOCUMENTED_ALLOWLIST` — `mr-rakes`, `retro`, `feature-adr-setup`, `bto-optimize` — got USAGE
43
+ * lines instead of being parked here: discoverability IS existence, and an allowlist entry is a
44
+ * promise to fix later that already cost one field bug report (`project-skills`, 2026-08-25).
45
+ *
46
+ * The list survives as the MECHANISM: a future genuinely internal entry point gets a reasoned home
47
+ * instead of a silent regex exemption. Its members are NOT commands and are NOT in `DZ_COMMANDS`;
48
+ * joining it costs a reason and a date, checked by {@link validateExceptionList}.
49
+ */
50
+ export const INTERNAL_ENTRY_POINTS = [];
51
+ const SINCE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
52
+ /**
53
+ * Refuse an exemption that carries no reason or no usable date, NAMING the offending entry. Without
54
+ * it, "exception list with a reason" decays back into the allowlist it replaced. Proven to FIRE on a
55
+ * synthetic reason-less entry, not merely exercised on the valid real lists (ADR-001 §5 / AM-3) — a
56
+ * validator only ever fed valid input is dead code.
57
+ *
58
+ * @param list the exception list to check
59
+ * @param label how to name the list in the error (e.g. `PSEUDO_COMMANDS`)
60
+ * @throws Error naming the offending entry
61
+ */
62
+ export function validateExceptionList(list, label = 'command exception list') {
63
+ list.forEach((entry, index) => {
64
+ const name = typeof entry?.name === 'string' ? entry.name.trim() : '';
65
+ if (name === '') {
66
+ throw new Error(`${label}[${index}] has no name — every exemption names the command it exempts`);
67
+ }
68
+ const reason = typeof entry.reason === 'string' ? entry.reason.trim() : '';
69
+ if (reason === '') {
70
+ throw new Error(`${label}: "${name}" has no reason — an exemption without a written reason is an allowlist, not a decision (ADR-001 §5)`);
71
+ }
72
+ const since = typeof entry.since === 'string' ? entry.since : '';
73
+ if (!SINCE_PATTERN.test(since)) {
74
+ throw new Error(`${label}: "${name}" has since="${since}" — expected a YYYY-MM-DD date so the exemption can be aged`);
75
+ }
76
+ });
77
+ return list;
78
+ }
79
+ /**
80
+ * Keywords after which a `/` can only open a REGEX literal, never a division: they end a statement or
81
+ * an operator position, so no value precedes the slash. Without them `return /}/.test(x)` would be
82
+ * read as a division and its braces counted (see {@link stripNonCode}).
83
+ */
84
+ const REGEX_PRECEDING_KEYWORDS = new Set([
85
+ 'return', 'case', 'typeof', 'instanceof', 'in', 'of', 'new', 'delete', 'void', 'throw', 'do',
86
+ 'else', 'yield', 'await',
87
+ ]);
88
+ /**
89
+ * Decide whether a `/` at this point opens a regex literal, given the code emitted so far.
90
+ *
91
+ * THE HEURISTIC, and it IS a heuristic (a real answer needs the TypeScript parser): a slash divides
92
+ * when a VALUE precedes it — an identifier, a number, or a closing `)`/`]`/`}`/quote — and otherwise
93
+ * opens a regex. The one exception is a preceding KEYWORD, which looks like an identifier but leaves
94
+ * an operator position, hence {@link REGEX_PRECEDING_KEYWORDS}.
95
+ *
96
+ * THE LIMIT, named because it is deliberate: after `)`, `]` and `}` this answers "division", so
97
+ * `if (x) /}/.test(y)` and `} /}/.test(y)` still leak their braces. That direction is chosen on
98
+ * ASYMMETRY of damage — a missed regex reproduces today's known under-count, while a division
99
+ * mistaken for a regex would SWALLOW real code and could hide or invent braces anywhere. The
100
+ * same-line bound in {@link stripNonCode} caps what a false positive can eat.
101
+ *
102
+ * COST (QE round 3, `[P2]`): this used to run an END-ANCHORED regex over the whole accumulated
103
+ * output, so every slash rescanned the entire prefix — QUADRATIC in a parser that is public API and
104
+ * runs inside every `dz name-check` (MEASURED pre-fix: 4 000 division-heavy lines took 10 366 ms,
105
+ * 21x longer than the 12x LARGER real `cli.ts`). Both inputs are now O(1): the caller tracks the
106
+ * preceding identifier incrementally, so this function looks at nothing but its two arguments.
107
+ *
108
+ * @param lastIdentifier the identifier ending at `lastSignificant`, or `''` when that is not a word
109
+ * @param lastSignificant the last non-whitespace CODE character emitted, or `''` at the start
110
+ */
111
+ function regexMayStart(lastIdentifier, lastSignificant) {
112
+ if (lastSignificant === '')
113
+ return true;
114
+ // The value test. `"` is the MARKER a closed string, template or regex leaves behind — it can
115
+ // never be a real code character, because a literal `"` in code opens a string instead. Omitting
116
+ // it (QE round 4, `[P2]`) made `'4' / d` read as a regex opener: the scanner then ate up to the
117
+ // next slash on the line, usually a trailing `//`, taking the case body's closing brace with it,
118
+ // and every later `case` was dropped. MEASURED pre-fix: two cases lost per body, for all four
119
+ // closers (`'`, `"`, backtick, flagless `/…/`).
120
+ if (!/[\w$)\]}"]/.test(lastSignificant))
121
+ return true;
122
+ return REGEX_PRECEDING_KEYWORDS.has(lastIdentifier);
123
+ }
124
+ /**
125
+ * Replace the CONTENT of every string, template literal, regex and comment with spaces (newlines
126
+ * preserved), leaving only real code, so a `}` inside a string, a regex or a comment in a case body
127
+ * cannot prematurely close the switch. Case LABELS are still matched on the ORIGINAL source. Promoted
128
+ * out of `test/command-count.test.ts` so the CLI and the test share ONE parser.
129
+ *
130
+ * REGEX LITERALS (QE round 1, Codex `gpt-5.6-sol`, `[P2]` on this function): a lone `}` inside `/}/`
131
+ * used to be counted as a real closing brace, so the brace walk in {@link dispatchedCommands} ended
132
+ * on the FIRST case body and the inventory was TRUNCATED — with the parity guard, `dz name-check`
133
+ * and the exported API all under-counting on valid source. Detection is {@link regexMayStart}, whose
134
+ * heuristic and limit are stated there; on top of it this scanner adds a hard SAME-LINE bound: a
135
+ * regex literal cannot contain an unescaped newline, so a candidate whose closing `/` is not on the
136
+ * same line is declared a false positive and the slash is emitted as ordinary code. That bound is
137
+ * what keeps a mis-read division from eating an unbounded span. `[...]` character classes are
138
+ * tracked so `/[/]/` and `/[{}]/` close where they really close.
139
+ */
140
+ export function stripNonCode(src) {
141
+ let out = '';
142
+ let state = 'code';
143
+ /** Last non-whitespace CODE character emitted; a closed string/regex reports as a value (`"`). */
144
+ let lastSignificant = '';
145
+ /**
146
+ * The identifier ending at {@link lastSignificant}, maintained in O(1) so {@link regexMayStart}
147
+ * never rescans the prefix. Whitespace LEAVES it alone (`return /re/` must still see `return`);
148
+ * any other character clears it; a closed string or regex clears it, because it is a value.
149
+ */
150
+ let lastIdentifier = '';
151
+ for (let i = 0; i < src.length; i++) {
152
+ const c = src[i];
153
+ const n = src[i + 1];
154
+ if (state === 'code') {
155
+ if (c === '/' && n === '/') {
156
+ state = 'line';
157
+ out += ' ';
158
+ i++;
159
+ continue;
160
+ }
161
+ if (c === '/' && n === '*') {
162
+ state = 'block';
163
+ out += ' ';
164
+ i++;
165
+ continue;
166
+ }
167
+ if (c === "'" || c === '"' || c === '`') {
168
+ state = c;
169
+ out += ' ';
170
+ continue;
171
+ }
172
+ if (c === '/' && regexMayStart(lastIdentifier, lastSignificant)) {
173
+ let j = i + 1;
174
+ let inClass = false;
175
+ let closed = false;
176
+ for (; j < src.length; j++) {
177
+ const d = src[j];
178
+ if (d === '\n')
179
+ break; // no closing `/` on this line
180
+ if (d === '\\') {
181
+ if (src[j + 1] === '\n' || j + 1 >= src.length)
182
+ break;
183
+ j++;
184
+ continue;
185
+ }
186
+ if (inClass) {
187
+ if (d === ']')
188
+ inClass = false;
189
+ continue;
190
+ }
191
+ if (d === '[') {
192
+ inClass = true;
193
+ continue;
194
+ }
195
+ if (d === '/') {
196
+ closed = true;
197
+ break;
198
+ }
199
+ }
200
+ if (closed) {
201
+ out += ' '.repeat(j - i + 1);
202
+ i = j;
203
+ lastSignificant = '"';
204
+ lastIdentifier = '';
205
+ continue;
206
+ }
207
+ // not a regex after all — fall through and emit the slash as ordinary code
208
+ }
209
+ out += c;
210
+ if (/[A-Za-z0-9_$]/.test(c)) {
211
+ lastSignificant = c;
212
+ lastIdentifier += c;
213
+ }
214
+ else if (!/\s/.test(c)) {
215
+ lastSignificant = c;
216
+ lastIdentifier = '';
217
+ }
218
+ continue;
219
+ }
220
+ if (state === 'line') {
221
+ if (c === '\n') {
222
+ state = 'code';
223
+ out += '\n';
224
+ }
225
+ else
226
+ out += ' ';
227
+ continue;
228
+ }
229
+ if (state === 'block') {
230
+ if (c === '*' && n === '/') {
231
+ state = 'code';
232
+ out += ' ';
233
+ i++;
234
+ }
235
+ else
236
+ out += (c === '\n' ? '\n' : ' ');
237
+ continue;
238
+ }
239
+ // inside a string/template: content → spaces, honour escapes, close on the matching quote.
240
+ // An escaped NEWLINE is a LINE CONTINUATION and its newline must SURVIVE (QE round 3, `[P2]`):
241
+ // emitting two spaces here destroyed a line, `stripNonCode` returned fewer lines than it was
242
+ // given, and `dispatchedCommands` — which indexes `codeLines` against `origLines` by the same
243
+ // `i` — then read every later label off the WRONG line and DROPPED real commands.
244
+ if (c === '\\') {
245
+ out += (src[i + 1] === '\n' ? ' \n' : ' ');
246
+ i++;
247
+ continue;
248
+ }
249
+ if (c === state) {
250
+ state = 'code';
251
+ out += ' ';
252
+ lastSignificant = '"';
253
+ lastIdentifier = '';
254
+ continue;
255
+ }
256
+ out += (c === '\n' ? '\n' : ' ');
257
+ }
258
+ return out;
259
+ }
260
+ /**
261
+ * The names `dz <name>` will actually RUN: `case` labels of the MAIN `switch (command)`, found by
262
+ * brace-aware scanning so extraction is indentation-independent (NFR-6).
263
+ *
264
+ * @throws Error when the main switch is absent — the "guard cannot run" signal: returning `[]` would
265
+ * let every set-equality above it pass at ∅ == ∅.
266
+ */
267
+ export function dispatchedCommands(src) {
268
+ const origLines = src.split('\n');
269
+ const codeLines = stripNonCode(src).split('\n'); // braces counted on this; NAME read from origLines
270
+ let start = -1;
271
+ for (let i = 0; i < codeLines.length; i++) {
272
+ if (/switch \(command\)/.test(codeLines[i])) {
273
+ start = i;
274
+ break;
275
+ }
276
+ }
277
+ if (start < 0)
278
+ throw new Error('main switch (command) not found — the command inventory cannot be derived from this source');
279
+ const names = new Set();
280
+ let depth = 0;
281
+ let started = false;
282
+ for (let i = start; i < codeLines.length; i++) {
283
+ // Depth BEFORE this line's own braces are counted. A label belonging DIRECTLY to
284
+ // `switch (command)` sits at depth 1 — inside the switch's braces and nothing else's.
285
+ const depthBefore = depth;
286
+ for (const ch of codeLines[i]) {
287
+ if (ch === '{') {
288
+ depth++;
289
+ started = true;
290
+ }
291
+ else if (ch === '}') {
292
+ depth--;
293
+ }
294
+ }
295
+ // TWO independent conditions, both load-bearing, both from QE round 2:
296
+ // (a) the word `case` must have SURVIVED stripping at the same column — otherwise the line is
297
+ // a comment or a template and its `case 'phantom':` is prose, not a dispatch;
298
+ // (b) the label must sit at switch-body depth — otherwise it belongs to a nested sub-verb
299
+ // switch and `dz <that name>` would never reach it.
300
+ const code = codeLines[i].match(/^(\s*)case\b/);
301
+ const orig = origLines[i].match(/^(\s*)case ['"]([a-z][a-z0-9-]*)['"]:/); // NAME from ORIGINAL
302
+ const isCode = code !== null && orig !== null && code[1].length === orig[1].length;
303
+ if (isCode && started && depthBefore === 1)
304
+ names.add(orig[2]);
305
+ if (started && depth <= 0 && i > start)
306
+ break;
307
+ }
308
+ return [...names].sort();
309
+ }
310
+ /**
311
+ * Cut out the body of the `USAGE` template literal — the ONE block `dz --help` actually renders.
312
+ *
313
+ * WHY it is a function and not a regex (QE round 2, `[P2]`): the naive whole-file scan for
314
+ * ` dz <name>` also swallowed every OTHER help template in `cli.ts`. Scanning stops at the first
315
+ * unescaped backtick outside an `${…}` interpolation, so `${PRESET_NAMES.join(', ')}` on the last
316
+ * USAGE line does not end the literal early.
317
+ *
318
+ * LIMIT, named: a template literal NESTED inside an interpolation would need recursion and is not
319
+ * handled — `cli.ts` has none today, and if one appears this throws rather than guessing.
320
+ *
321
+ * @throws Error when the literal is absent or unterminated — same "cannot run" discipline as
322
+ * {@link dispatchedCommands}: silently returning `''` would let `documented == declared ∪ pseudo`
323
+ * pass at ∅ == ∅.
324
+ */
325
+ export function usageBlock(src) {
326
+ const opener = src.match(/const USAGE\s*(?::[^=]*)?=\s*`/);
327
+ if (opener?.index === undefined) {
328
+ throw new Error('the USAGE template literal was not found — the documented commands cannot be derived from this source');
329
+ }
330
+ const from = opener.index + opener[0].length;
331
+ let interpolation = 0;
332
+ for (let i = from; i < src.length; i++) {
333
+ const c = src[i];
334
+ if (c === '\\') {
335
+ i++;
336
+ continue;
337
+ }
338
+ if (c === '$' && src[i + 1] === '{') {
339
+ interpolation++;
340
+ i++;
341
+ continue;
342
+ }
343
+ if (c === '}' && interpolation > 0) {
344
+ interpolation--;
345
+ continue;
346
+ }
347
+ if (c === '`' && interpolation === 0)
348
+ return src.slice(from, i);
349
+ }
350
+ throw new Error('the USAGE template literal is unterminated — the documented commands cannot be derived from this source');
351
+ }
352
+ /**
353
+ * The names a user can DISCOVER: the ` dz <name>` lines of USAGE, read from the SOURCE — the same
354
+ * regex the old count test ran over RENDERED help, deliberately, because the parity test asserts
355
+ * source-set == rendered-set: being right about the text and wrong about what users see is then a
356
+ * FAILURE, not an invisible drift (FR-3.3 / NFR-6).
357
+ *
358
+ * BOUNDED TO USAGE (QE round 2, `[P2]` on the old whole-file regex): a per-command help template
359
+ * such as `BRAIN_USAGE` (`cli.ts:5019+`) carries a dozen ` dz brain …` lines that global
360
+ * `dz --help` never renders. They were being counted; they deduped onto the `brain` already in
361
+ * USAGE, so the leak was invisible BY LUCK, and one unique name in such a block would have invented
362
+ * a documented command for both `dz name-check` and the parity guard.
363
+ */
364
+ export function documentedCommands(src) {
365
+ const names = new Set();
366
+ for (const m of usageBlock(src).matchAll(/^ {2}dz {1,}([a-z][a-z0-9-]*)/gm))
367
+ names.add(m[1]);
368
+ return [...names].sort();
369
+ }
370
+ /**
371
+ * The canonical enumeration: the names inside the `DZ_COMMANDS` literal block, parsed from TEXT so
372
+ * all four sets come from ONE artefact. The runtime export is separately asserted equal to this,
373
+ * which is what catches a parser that drifts from the literal it reads.
374
+ *
375
+ * @throws Error when the block is absent — same "cannot run" discipline as {@link dispatchedCommands}.
376
+ */
377
+ export function declaredCommands(src) {
378
+ const block = src.match(/export const DZ_COMMANDS: readonly string\[\] = \[([\s\S]*?)\];/);
379
+ if (block?.[1] === undefined) {
380
+ throw new Error('the DZ_COMMANDS literal block was not found — the canonical enumeration cannot be derived from this source');
381
+ }
382
+ const names = new Set();
383
+ for (const m of block[1].matchAll(/'([a-z][a-z0-9-]*)'/g))
384
+ names.add(m[1]);
385
+ return [...names].sort();
386
+ }
387
+ /**
388
+ * All four name sets from ONE `cli.ts` text, with both exception lists validated FIRST — deliberately
389
+ * up front, so a real entry with an empty reason breaks the whole inventory rather than one
390
+ * assertion, and the exemption channel cannot rot quietly (ADR-001 §5).
391
+ *
392
+ * @param src the full text of `packages/@dzhechkov/harness-cli/src/cli.ts`
393
+ */
394
+ export function commandInventory(src) {
395
+ validateExceptionList(PSEUDO_COMMANDS, 'PSEUDO_COMMANDS');
396
+ validateExceptionList(INTERNAL_ENTRY_POINTS, 'INTERNAL_ENTRY_POINTS');
397
+ return {
398
+ declared: declaredCommands(src),
399
+ dispatched: dispatchedCommands(src),
400
+ documented: documentedCommands(src),
401
+ pseudo: [...PSEUDO_COMMANDS.map((e) => e.name)].sort(),
402
+ internal: [...INTERNAL_ENTRY_POINTS.map((e) => e.name)].sort(),
403
+ };
404
+ }
405
+ //# sourceMappingURL=command-inventory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-inventory.js","sourceRoot":"","sources":["../src/command-inventory.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -6,6 +6,8 @@
6
6
  /** Package version. Kept in sync with `package.json`. */
7
7
  export declare const HARNESS_CLI_VERSION: string;
8
8
  export { DZ_COMMANDS, runCli } from './cli.js';
9
+ export { commandInventory, declaredCommands, dispatchedCommands, documentedCommands, stripNonCode, validateExceptionList, INTERNAL_ENTRY_POINTS, PSEUDO_COMMANDS, } from './command-inventory.js';
10
+ export type { CommandException, CommandInventory } from './command-inventory.js';
9
11
  export type { CliIo, ReleaseExecRunner } from './cli.js';
10
12
  export { codexHooksSummary, codexHooksSyncOptions, deliverCodexHooks, normalizeCodexHookOutcome } from './cli.js';
11
13
  export { withForeignStdoutOnStderr } from './cli.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,EAAE,MACkD,CAAC;AAErF,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAGzD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAGlH,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,EAAE,MACkD,CAAC;AAErF,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAI/C,OAAO,EACL,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,YAAY,EACxF,qBAAqB,EAAE,qBAAqB,EAAE,eAAe,GAC9D,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACjF,YAAY,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAGzD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAGlH,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -7,6 +7,10 @@ import { createRequire } from 'node:module';
7
7
  /** Package version. Kept in sync with `package.json`. */
8
8
  export const HARNESS_CLI_VERSION = createRequire(import.meta.url)('../package.json').version;
9
9
  export { DZ_COMMANDS, runCli } from './cli.js';
10
+ // ONE definition of "a dz command" (ADR-001, feature command-count-triad). Exported so every
11
+ // consumer — the layer-1 parity test, `dz name-check`, any future doc generator — derives the four
12
+ // name sets from the same pure parser instead of growing a fourth private regex and a fourth number.
13
+ export { commandInventory, declaredCommands, dispatchedCommands, documentedCommands, stripNonCode, validateExceptionList, INTERNAL_ENTRY_POINTS, PSEUDO_COMMANDS, } from './command-inventory.js';
10
14
  // The Codex hook DELIVERY seam (crossrt-2 fix round, findings 1+2): the argv→operation mapping and
11
15
  // the one place a success word may be printed, exported so both can be pinned without a live codex.
12
16
  export { codexHooksSummary, codexHooksSyncOptions, deliverCodexHooks, normalizeCodexHookOutcome } from './cli.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,yDAAyD;AACzD,MAAM,CAAC,MAAM,mBAAmB,GAC7B,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC;AAErF,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAE/C,mGAAmG;AACnG,oGAAoG;AACpG,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAClH,qGAAqG;AACrG,uGAAuG;AACvG,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,yDAAyD;AACzD,MAAM,CAAC,MAAM,mBAAmB,GAC7B,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC;AAErF,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAC/C,6FAA6F;AAC7F,mGAAmG;AACnG,qGAAqG;AACrG,OAAO,EACL,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,YAAY,EACxF,qBAAqB,EAAE,qBAAqB,EAAE,eAAe,GAC9D,MAAM,wBAAwB,CAAC;AAGhC,mGAAmG;AACnG,oGAAoG;AACpG,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAClH,qGAAqG;AACrG,uGAAuG;AACvG,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"known-flags.d.ts","sourceRoot":"","sources":["../src/known-flags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAwS5C,CAAC"}
1
+ {"version":3,"file":"known-flags.d.ts","sourceRoot":"","sources":["../src/known-flags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EA6S5C,CAAC"}
@@ -12,6 +12,7 @@
12
12
  export const KNOWN_CLI_FLAGS = [
13
13
  'affected',
14
14
  'all',
15
+ 'allow-cold-start',
15
16
  'allow-integrations',
16
17
  'allow-same-family',
17
18
  'allow-same-family-qe',
@@ -199,6 +200,7 @@ export const KNOWN_CLI_FLAGS = [
199
200
  'plan',
200
201
  'plugin-dir',
201
202
  'porcelain',
203
+ 'prefix',
202
204
  'preset',
203
205
  'pretty',
204
206
  'preview',
@@ -221,6 +223,7 @@ export const KNOWN_CLI_FLAGS = [
221
223
  'reinforce',
222
224
  'remove',
223
225
  'report',
226
+ 'reset',
224
227
  'require-plan',
225
228
  'require-signing',
226
229
  'rerank',
@@ -237,6 +240,7 @@ export const KNOWN_CLI_FLAGS = [
237
240
  'safe-mode',
238
241
  'sandbox',
239
242
  'save-dev',
243
+ 'scan-tail',
240
244
  'scenarios',
241
245
  'scope-check',
242
246
  'score',
@@ -285,6 +289,7 @@ export const KNOWN_CLI_FLAGS = [
285
289
  'tolerance',
286
290
  'tools',
287
291
  'topics',
292
+ 'transcript',
288
293
  'type',
289
294
  'usage',
290
295
  'validate',