@dzhechkov/harness-cli 0.7.3 → 0.7.5
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 +67 -7
- package/README.md +7 -5
- package/dist/boolean-flags.d.ts +26 -0
- package/dist/boolean-flags.d.ts.map +1 -0
- package/dist/boolean-flags.js +118 -0
- package/dist/boolean-flags.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +575 -19
- package/dist/cli.js.map +1 -1
- package/dist/install-spec.d.ts +36 -0
- package/dist/install-spec.d.ts.map +1 -0
- package/dist/install-spec.js +68 -0
- package/dist/install-spec.js.map +1 -0
- package/dist/known-flags.d.ts +13 -0
- package/dist/known-flags.d.ts.map +1 -0
- package/dist/known-flags.js +292 -0
- package/dist/known-flags.js.map +1 -0
- package/package.json +18 -18
- package/sbom.json +156 -6
- package/src/boolean-flags.ts +118 -0
- package/src/cli.ts +529 -19
- package/src/install-spec.ts +86 -0
- package/src/known-flags.ts +291 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dz install` argument resolution — feature install-spec-honesty (backlog 43a52cf2 + c999786b).
|
|
3
|
+
*
|
|
4
|
+
* cmdInstall used to splice the RAW argument into `join(projectRoot, 'node_modules', pkg)`.
|
|
5
|
+
* MEASURED, five broken forms: an absolute path (path.join CONCATENATES an absolute segment —
|
|
6
|
+
* `/proj/node_modules/home/dz/pkgs/...`); a versioned spec `@dzhechkov/health-advisor@1.9.0`
|
|
7
|
+
* (the WORST: `npm install` SUCCEEDS and mutates the project's package.json, then dz dies on an
|
|
8
|
+
* invented path with exit 1 — and the versioned form is exactly what the npm-lag README
|
|
9
|
+
* prescribes); a tarball `pack-1.0.0.tgz`; `github:user/repo`; an alias `ha@npm:...`.
|
|
10
|
+
*
|
|
11
|
+
* The cure is a PURE resolver that runs BEFORE any npm process: every accepted form yields the
|
|
12
|
+
* npm spec AND the node_modules directory name it will land under; everything else is a NAMED
|
|
13
|
+
* refusal carrying the working two-command form. A spec whose landing dir cannot be established
|
|
14
|
+
* must never reach npm — that is what made the versioned failure mutate state and then die.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export interface InstallSpecProbe {
|
|
18
|
+
/** Does `path` exist as a regular file? */
|
|
19
|
+
readonly isFile: (path: string) => boolean;
|
|
20
|
+
/** Does `path` exist as a directory? */
|
|
21
|
+
readonly isDir: (path: string) => boolean;
|
|
22
|
+
/** `name` from `package/package.json` inside a tarball, or null (unreadable/absent). */
|
|
23
|
+
readonly readTarballName: (path: string) => string | null;
|
|
24
|
+
/** `name` from `<dir>/package.json`, or null. */
|
|
25
|
+
readonly readDirName: (path: string) => string | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type InstallSpecResolution =
|
|
29
|
+
| { readonly kind: 'name' | 'versioned' | 'alias' | 'tarball' | 'dir'; readonly npmSpec: string; readonly dirName: string }
|
|
30
|
+
| { readonly kind: 'refused'; readonly reason: string; readonly hint: string };
|
|
31
|
+
|
|
32
|
+
const BARE_NAME_RE = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
33
|
+
|
|
34
|
+
/** The two-command fallback that always works, named in every refusal (43a52cf2's own ask). */
|
|
35
|
+
function twoCommandHint(raw: string): string {
|
|
36
|
+
return `working form: npm install ${raw} --save-dev && dz init --skills-dir node_modules/<installed-package-name>`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function resolveInstallSpec(raw: string, resolveAgainstCwd: (p: string) => string, probe: InstallSpecProbe): InstallSpecResolution {
|
|
40
|
+
const spec = raw.trim();
|
|
41
|
+
if (spec === '') return { kind: 'refused', reason: 'empty spec', hint: 'dz install <package|package@version|path/to.tgz|path/to/dir>' };
|
|
42
|
+
|
|
43
|
+
// Path forms first — a path is never an npm name. Absolute, explicit-relative, or an existing
|
|
44
|
+
// .tgz/.tar.gz/.dir anywhere: the OLD code spliced these into node_modules verbatim.
|
|
45
|
+
const looksPathy = spec.startsWith('/') || spec.startsWith('./') || spec.startsWith('../') || /\.(tgz|tar\.gz)$/.test(spec);
|
|
46
|
+
if (looksPathy) {
|
|
47
|
+
const abs = resolveAgainstCwd(spec);
|
|
48
|
+
if (/\.(tgz|tar\.gz)$/.test(spec)) {
|
|
49
|
+
if (!probe.isFile(abs)) return { kind: 'refused', reason: `tarball not found: ${abs}`, hint: twoCommandHint(spec) };
|
|
50
|
+
const name = probe.readTarballName(abs);
|
|
51
|
+
if (name === null) return { kind: 'refused', reason: `cannot read package/package.json name from ${abs}`, hint: twoCommandHint(spec) };
|
|
52
|
+
return { kind: 'tarball', npmSpec: abs, dirName: name };
|
|
53
|
+
}
|
|
54
|
+
if (probe.isDir(abs)) {
|
|
55
|
+
const name = probe.readDirName(abs);
|
|
56
|
+
if (name === null) return { kind: 'refused', reason: `${abs} has no readable package.json name`, hint: twoCommandHint(spec) };
|
|
57
|
+
return { kind: 'dir', npmSpec: abs, dirName: name };
|
|
58
|
+
}
|
|
59
|
+
return { kind: 'refused', reason: `path does not exist: ${abs}`, hint: twoCommandHint(spec) };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// git/github/url specs: the landing dir is not cheaply establishable — refuse with the cure.
|
|
63
|
+
if (/^(github:|git\+|git:|https?:\/\/)/.test(spec)) {
|
|
64
|
+
return { kind: 'refused', reason: `git/url specs are not resolvable to a node_modules dir up front (${spec})`, hint: twoCommandHint(spec) };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// alias@npm:real[@version] — installs under the ALIAS name.
|
|
68
|
+
const alias = /^([^@][^@]*|@[^/]+\/[^@]+)@npm:(.+)$/.exec(spec);
|
|
69
|
+
if (alias !== null) {
|
|
70
|
+
return { kind: 'alias', npmSpec: spec, dirName: alias[1] as string };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (BARE_NAME_RE.test(spec)) return { kind: 'name', npmSpec: spec, dirName: spec };
|
|
74
|
+
|
|
75
|
+
// name@version / @scope/name@version — the dir is the name WITHOUT the version.
|
|
76
|
+
const at = spec.lastIndexOf('@');
|
|
77
|
+
if (at > 0) {
|
|
78
|
+
const name = spec.slice(0, at);
|
|
79
|
+
const version = spec.slice(at + 1);
|
|
80
|
+
if (BARE_NAME_RE.test(name) && version !== '' && !version.includes('/')) {
|
|
81
|
+
return { kind: 'versioned', npmSpec: spec, dirName: name };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { kind: 'refused', reason: `unrecognized install spec: ${spec}`, hint: twoCommandHint(spec) };
|
|
86
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every `--name` this CLI knows: the union of every name its code READS and every name its help
|
|
3
|
+
* DOCUMENTS. GENERATED — `test/known-flags-drift.test.ts` re-derives it from `cli.ts` and fails if
|
|
4
|
+
* this list has gone stale, so a new flag cannot silently start warning at its own users.
|
|
5
|
+
*
|
|
6
|
+
* It is deliberately a FLAT set, not a per-command map. Both ways of building a per-command list
|
|
7
|
+
* were measured and both are unsafe: 53 of the names the CLI reads appear nowhere in help, and
|
|
8
|
+
* static extraction over the dispatch table lost `--week` from `dz recap` while picking up a
|
|
9
|
+
* neighbour's flags for `dz usage`. A refusal built on either would reject working commands.
|
|
10
|
+
* See harness-core/src/cli-flag-notice.ts for the full reasoning and the honest limit.
|
|
11
|
+
*/
|
|
12
|
+
export const KNOWN_CLI_FLAGS: readonly string[] = [
|
|
13
|
+
'affected',
|
|
14
|
+
'all',
|
|
15
|
+
'allow-same-family',
|
|
16
|
+
'allow-same-family-qe',
|
|
17
|
+
'any',
|
|
18
|
+
'append',
|
|
19
|
+
'apply',
|
|
20
|
+
'arg',
|
|
21
|
+
'artifact',
|
|
22
|
+
'at',
|
|
23
|
+
'audit-dev',
|
|
24
|
+
'author',
|
|
25
|
+
'auto',
|
|
26
|
+
'base',
|
|
27
|
+
'baseline',
|
|
28
|
+
'body',
|
|
29
|
+
'book',
|
|
30
|
+
'books',
|
|
31
|
+
'bto',
|
|
32
|
+
'budget',
|
|
33
|
+
'budget-extra',
|
|
34
|
+
'bump-only',
|
|
35
|
+
'by-stage',
|
|
36
|
+
'calibrate',
|
|
37
|
+
'candidate',
|
|
38
|
+
'candidates',
|
|
39
|
+
'canonical',
|
|
40
|
+
'category',
|
|
41
|
+
'channel',
|
|
42
|
+
'check',
|
|
43
|
+
'claim-check',
|
|
44
|
+
'claude-bin',
|
|
45
|
+
'cmd',
|
|
46
|
+
'coder-family',
|
|
47
|
+
'codex-home',
|
|
48
|
+
'command',
|
|
49
|
+
'commands',
|
|
50
|
+
'commit',
|
|
51
|
+
'compare',
|
|
52
|
+
'confirm',
|
|
53
|
+
'confirmed',
|
|
54
|
+
'context-only',
|
|
55
|
+
'corroborate',
|
|
56
|
+
'count-project',
|
|
57
|
+
'day',
|
|
58
|
+
'deep',
|
|
59
|
+
'default-family',
|
|
60
|
+
'desc',
|
|
61
|
+
'description',
|
|
62
|
+
'diff',
|
|
63
|
+
'diff-filter',
|
|
64
|
+
'dir',
|
|
65
|
+
'domain',
|
|
66
|
+
'done',
|
|
67
|
+
'draft',
|
|
68
|
+
'dry-run',
|
|
69
|
+
'effect',
|
|
70
|
+
'effort',
|
|
71
|
+
'emit',
|
|
72
|
+
'emit-policy',
|
|
73
|
+
'enrich',
|
|
74
|
+
'epsilon',
|
|
75
|
+
'expect',
|
|
76
|
+
'expect-commands',
|
|
77
|
+
'export',
|
|
78
|
+
'fa-record',
|
|
79
|
+
'fail-on',
|
|
80
|
+
'fail-on-undergrant',
|
|
81
|
+
'family',
|
|
82
|
+
'feature-dir',
|
|
83
|
+
'files',
|
|
84
|
+
'filter',
|
|
85
|
+
'finalize',
|
|
86
|
+
'findings',
|
|
87
|
+
'flag',
|
|
88
|
+
'force',
|
|
89
|
+
'forget',
|
|
90
|
+
'format',
|
|
91
|
+
'from',
|
|
92
|
+
'from-',
|
|
93
|
+
'from-json',
|
|
94
|
+
'from-kus',
|
|
95
|
+
'from-pack',
|
|
96
|
+
'from-slice',
|
|
97
|
+
'from-spec',
|
|
98
|
+
'full',
|
|
99
|
+
'gates',
|
|
100
|
+
'gen-critic',
|
|
101
|
+
'goal',
|
|
102
|
+
'guard',
|
|
103
|
+
'guards',
|
|
104
|
+
'half-year',
|
|
105
|
+
'harmonize',
|
|
106
|
+
'help',
|
|
107
|
+
'holdout',
|
|
108
|
+
'html',
|
|
109
|
+
'id',
|
|
110
|
+
'include-domain',
|
|
111
|
+
'include-pairs',
|
|
112
|
+
'init',
|
|
113
|
+
'input-hash',
|
|
114
|
+
'install',
|
|
115
|
+
'install-driver',
|
|
116
|
+
'install-hook',
|
|
117
|
+
'into',
|
|
118
|
+
'invariants',
|
|
119
|
+
'json',
|
|
120
|
+
'judge',
|
|
121
|
+
'k',
|
|
122
|
+
'keep-scratch',
|
|
123
|
+
'key',
|
|
124
|
+
'kind',
|
|
125
|
+
'ladder',
|
|
126
|
+
'legacy',
|
|
127
|
+
'lexical',
|
|
128
|
+
'license',
|
|
129
|
+
'limit',
|
|
130
|
+
'list',
|
|
131
|
+
'live-content',
|
|
132
|
+
'loc-cap',
|
|
133
|
+
'local-path',
|
|
134
|
+
'manifest',
|
|
135
|
+
'margin',
|
|
136
|
+
'mark',
|
|
137
|
+
'max',
|
|
138
|
+
'max-wall-clock',
|
|
139
|
+
'memory',
|
|
140
|
+
'mock',
|
|
141
|
+
'mode',
|
|
142
|
+
'model',
|
|
143
|
+
'module',
|
|
144
|
+
'month',
|
|
145
|
+
'n',
|
|
146
|
+
'name',
|
|
147
|
+
'name-only',
|
|
148
|
+
'night',
|
|
149
|
+
'no-audit',
|
|
150
|
+
'no-decorate',
|
|
151
|
+
'no-dry-run',
|
|
152
|
+
'no-evals',
|
|
153
|
+
'no-filters',
|
|
154
|
+
'no-fund',
|
|
155
|
+
'no-guard',
|
|
156
|
+
'no-hooks',
|
|
157
|
+
'no-index',
|
|
158
|
+
'no-issue',
|
|
159
|
+
'no-memory',
|
|
160
|
+
'no-merges',
|
|
161
|
+
'no-mirror',
|
|
162
|
+
'no-provenance',
|
|
163
|
+
'no-result',
|
|
164
|
+
'no-semantic',
|
|
165
|
+
'no-session-persistence',
|
|
166
|
+
'no-teach',
|
|
167
|
+
'no-verify',
|
|
168
|
+
'o',
|
|
169
|
+
'once',
|
|
170
|
+
'oneline',
|
|
171
|
+
'only',
|
|
172
|
+
'op',
|
|
173
|
+
'original',
|
|
174
|
+
'out',
|
|
175
|
+
'output',
|
|
176
|
+
'output-format',
|
|
177
|
+
'override',
|
|
178
|
+
'pack',
|
|
179
|
+
'pack-destination',
|
|
180
|
+
'package',
|
|
181
|
+
'pair',
|
|
182
|
+
'pattern',
|
|
183
|
+
'periods',
|
|
184
|
+
'pick',
|
|
185
|
+
'plan',
|
|
186
|
+
'plugin-dir',
|
|
187
|
+
'porcelain',
|
|
188
|
+
'preset',
|
|
189
|
+
'pretty',
|
|
190
|
+
'preview',
|
|
191
|
+
'project',
|
|
192
|
+
'promote',
|
|
193
|
+
'proposal',
|
|
194
|
+
'provenance',
|
|
195
|
+
'prune-noise',
|
|
196
|
+
'prune-quarantine',
|
|
197
|
+
'pubkey',
|
|
198
|
+
'publish',
|
|
199
|
+
'quarter',
|
|
200
|
+
'reason',
|
|
201
|
+
'rebaseline',
|
|
202
|
+
'recalled',
|
|
203
|
+
'reconcile',
|
|
204
|
+
'record-provisional',
|
|
205
|
+
'refresh-publishes',
|
|
206
|
+
'registry',
|
|
207
|
+
'reinforce',
|
|
208
|
+
'remove',
|
|
209
|
+
'report',
|
|
210
|
+
'require-plan',
|
|
211
|
+
'require-signing',
|
|
212
|
+
'rerank',
|
|
213
|
+
'result',
|
|
214
|
+
'resume',
|
|
215
|
+
'revise',
|
|
216
|
+
'reward',
|
|
217
|
+
'row',
|
|
218
|
+
'run',
|
|
219
|
+
'run-dir',
|
|
220
|
+
'run-id',
|
|
221
|
+
'runner',
|
|
222
|
+
'safe-mode',
|
|
223
|
+
'sandbox',
|
|
224
|
+
'save-dev',
|
|
225
|
+
'scenarios',
|
|
226
|
+
'scope-check',
|
|
227
|
+
'score',
|
|
228
|
+
'seed',
|
|
229
|
+
'select',
|
|
230
|
+
'semantic',
|
|
231
|
+
'send',
|
|
232
|
+
'session',
|
|
233
|
+
'sessions-dir',
|
|
234
|
+
'short',
|
|
235
|
+
'show-toplevel',
|
|
236
|
+
'sign-key',
|
|
237
|
+
'since',
|
|
238
|
+
'skill',
|
|
239
|
+
'skills-dir',
|
|
240
|
+
'slice',
|
|
241
|
+
'slug',
|
|
242
|
+
'source',
|
|
243
|
+
'split',
|
|
244
|
+
'stage',
|
|
245
|
+
'stage-timeout',
|
|
246
|
+
'stages-json',
|
|
247
|
+
'static',
|
|
248
|
+
'stats',
|
|
249
|
+
'status',
|
|
250
|
+
'stdin',
|
|
251
|
+
'step',
|
|
252
|
+
'stored',
|
|
253
|
+
'strict',
|
|
254
|
+
'strict-mcp-config',
|
|
255
|
+
'subsystem',
|
|
256
|
+
'success',
|
|
257
|
+
'tag',
|
|
258
|
+
'target',
|
|
259
|
+
'task',
|
|
260
|
+
'teach',
|
|
261
|
+
'test',
|
|
262
|
+
'test-cmd',
|
|
263
|
+
'text',
|
|
264
|
+
'threshold',
|
|
265
|
+
'tie-rate',
|
|
266
|
+
'tier',
|
|
267
|
+
'timeout',
|
|
268
|
+
'title',
|
|
269
|
+
'tolerance',
|
|
270
|
+
'tools',
|
|
271
|
+
'topics',
|
|
272
|
+
'type',
|
|
273
|
+
'usage',
|
|
274
|
+
'validate',
|
|
275
|
+
'verbose',
|
|
276
|
+
'verified',
|
|
277
|
+
'verify',
|
|
278
|
+
'version',
|
|
279
|
+
'wall-clock-extra',
|
|
280
|
+
'weak',
|
|
281
|
+
'week',
|
|
282
|
+
'weekly',
|
|
283
|
+
'window-days',
|
|
284
|
+
'window',
|
|
285
|
+
'with-pairs',
|
|
286
|
+
'with-references',
|
|
287
|
+
'work-order',
|
|
288
|
+
'write',
|
|
289
|
+
'year',
|
|
290
|
+
'yes',
|
|
291
|
+
];
|