@dzhechkov/harness-cli 0.8.9 → 0.8.15
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/.dz-manifest.json +42 -22
- package/LICENSE +13 -0
- package/README.md +291 -20
- package/dist/boolean-flags.d.ts.map +1 -1
- package/dist/boolean-flags.js +3 -0
- package/dist/boolean-flags.js.map +1 -1
- package/dist/cli.d.ts +44 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1635 -191
- package/dist/cli.js.map +1 -1
- package/dist/command-inventory.d.ts +149 -0
- package/dist/command-inventory.d.ts.map +1 -0
- package/dist/command-inventory.js +405 -0
- package/dist/command-inventory.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/known-flags.d.ts.map +1 -1
- package/dist/known-flags.js +5 -0
- package/dist/known-flags.js.map +1 -1
- package/package.json +14 -13
- package/sbom.json +71 -21
- package/src/boolean-flags.ts +3 -0
- package/src/cli.ts +1681 -138
- package/src/command-inventory.ts +342 -0
- package/src/index.ts +8 -0
- package/src/known-flags.ts +5 -0
|
@@ -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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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;
|
|
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,
|
|
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"}
|
package/dist/known-flags.js
CHANGED
|
@@ -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',
|