@shrkcrft/cli 0.1.0-alpha.28 → 0.1.0-alpha.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/baseline.command.d.ts +25 -0
- package/dist/commands/baseline.command.d.ts.map +1 -1
- package/dist/commands/baseline.command.js +64 -17
- package/dist/commands/changelog-data.d.ts.map +1 -1
- package/dist/commands/changelog-data.js +46 -0
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +137 -12
- package/dist/commands/code-intel.command.d.ts.map +1 -1
- package/dist/commands/code-intel.command.js +5 -1
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +8 -0
- package/dist/commands/daily.commands.d.ts.map +1 -1
- package/dist/commands/daily.commands.js +11 -1
- package/dist/commands/docs-references.command.d.ts +7 -0
- package/dist/commands/docs-references.command.d.ts.map +1 -0
- package/dist/commands/docs-references.command.js +323 -0
- package/dist/commands/doctor.command.d.ts.map +1 -1
- package/dist/commands/doctor.command.js +3 -2
- package/dist/commands/gates.command.d.ts +13 -1
- package/dist/commands/gates.command.d.ts.map +1 -1
- package/dist/commands/gates.command.js +693 -26
- package/dist/commands/generated.command.d.ts +32 -0
- package/dist/commands/generated.command.d.ts.map +1 -1
- package/dist/commands/generated.command.js +214 -53
- package/dist/commands/graph-code-subverbs.d.ts.map +1 -1
- package/dist/commands/graph-code-subverbs.js +5 -1
- package/dist/commands/help.command.d.ts.map +1 -1
- package/dist/commands/help.command.js +64 -2
- package/dist/commands/policy-lint.command.d.ts.map +1 -1
- package/dist/commands/policy-lint.command.js +52 -10
- package/dist/commands/registry.command.d.ts.map +1 -1
- package/dist/commands/registry.command.js +34 -9
- package/dist/commands/wiring.command.d.ts.map +1 -1
- package/dist/commands/wiring.command.js +4 -3
- package/dist/exit-codes.d.ts +27 -7
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +47 -8
- package/dist/gates/gate-envelope.d.ts +64 -0
- package/dist/gates/gate-envelope.d.ts.map +1 -0
- package/dist/gates/gate-envelope.js +26 -0
- package/dist/gates/gate-rule-globs.d.ts +33 -0
- package/dist/gates/gate-rule-globs.d.ts.map +1 -0
- package/dist/gates/gate-rule-globs.js +101 -0
- package/dist/gates/gate-rule-view.d.ts +9 -3
- package/dist/gates/gate-rule-view.d.ts.map +1 -1
- package/dist/gates/gate-rule-view.js +18 -4
- package/dist/gates/rule-coverage.d.ts +39 -1
- package/dist/gates/rule-coverage.d.ts.map +1 -1
- package/dist/gates/rule-coverage.js +123 -8
- package/dist/gates/run-gate-planes.d.ts +44 -0
- package/dist/gates/run-gate-planes.d.ts.map +1 -0
- package/dist/gates/run-gate-planes.js +261 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +7 -2
- package/package.json +33 -33
|
@@ -1,4 +1,29 @@
|
|
|
1
|
+
import type { IBaselineRule } from '@shrkcrft/core';
|
|
2
|
+
import { type IBaselineDiff } from '@shrkcrft/boundaries';
|
|
1
3
|
import { type ICommandHandler } from '../command-registry.js';
|
|
4
|
+
/**
|
|
5
|
+
* One rule's outcome, shared by check / diff / update — and by `shrk gates
|
|
6
|
+
* check`, which aggregates every plane. Exported so the aggregate runs the
|
|
7
|
+
* IDENTICAL evaluation as the per-plane verb: two implementations of "did this
|
|
8
|
+
* baseline drift?" would eventually disagree, and the one nobody runs would be
|
|
9
|
+
* the one that is wrong.
|
|
10
|
+
*/
|
|
11
|
+
export interface IBaselineOutcome {
|
|
12
|
+
readonly rule: IBaselineRule;
|
|
13
|
+
readonly status: 'passed' | 'failed' | 'skipped' | 'error';
|
|
14
|
+
readonly diff?: IBaselineDiff;
|
|
15
|
+
readonly committed?: string;
|
|
16
|
+
readonly current?: string;
|
|
17
|
+
readonly committedCount: number;
|
|
18
|
+
readonly currentCount: number;
|
|
19
|
+
readonly error?: string;
|
|
20
|
+
readonly skipReason?: string;
|
|
21
|
+
/** The recompute produced 0 entries while the baseline has some — likely a broken compute. */
|
|
22
|
+
readonly emptyCompute?: boolean;
|
|
23
|
+
/** No committed artifact exists yet — `committed` is absent, not zero. */
|
|
24
|
+
readonly missingBaseline?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function evaluateBaselineRule(cwd: string, rule: IBaselineRule, excludeDirs: readonly string[], changedFiles: readonly string[] | undefined): IBaselineOutcome;
|
|
2
27
|
export declare const baselineListCommand: ICommandHandler;
|
|
3
28
|
export declare const baselineCheckCommand: ICommandHandler;
|
|
4
29
|
export declare const baselineDiffCommand: ICommandHandler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baseline.command.d.ts","sourceRoot":"","sources":["../../src/commands/baseline.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"baseline.command.d.ts","sourceRoot":"","sources":["../../src/commands/baseline.command.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD,OAAO,EAML,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAuFhC;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;IAC3D,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,8FAA8F;IAC9F,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;CACpC;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,aAAa,EACnB,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAC1C,gBAAgB,CAiHlB;AA4FD,eAAO,MAAM,mBAAmB,EAAE,eAyCjC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eAyGlC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eA8BjC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAwDnC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eAiEpC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,eAc7B,CAAC"}
|
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
import { spawnSync } from 'node:child_process';
|
|
18
18
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
19
19
|
import * as nodePath from 'node:path';
|
|
20
|
+
import { resolveSourceGlobs, failsWhenEmpty } from '@shrkcrft/core';
|
|
20
21
|
import { baselineCount, baselineFails, computeBaselineFromExtractor, diffBaseline, matchesAny, } from '@shrkcrft/boundaries';
|
|
21
22
|
import { resolveChangedFiles, resolveProjectConfig } from '@shrkcrft/inspector';
|
|
22
23
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
23
24
|
import { ExitCode } from "../exit-codes.js";
|
|
24
25
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
26
|
+
import { buildGateEnvelope } from "../gates/gate-envelope.js";
|
|
25
27
|
const SCHEMA = 'sharkcraft.baseline/v1';
|
|
26
28
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
27
29
|
async function loadBaselines(cwd) {
|
|
@@ -85,7 +87,7 @@ function computeCurrent(cwd, rule, excludeDirs) {
|
|
|
85
87
|
}
|
|
86
88
|
return { text: String(child.stdout ?? '') };
|
|
87
89
|
}
|
|
88
|
-
function
|
|
90
|
+
export function evaluateBaselineRule(cwd, rule, excludeDirs, changedFiles) {
|
|
89
91
|
// --changed-only is honest about what it CANNOT scope: a command compute with
|
|
90
92
|
// no `watchFiles` has no file footprint, so it is reported as skipped rather
|
|
91
93
|
// than quietly passing.
|
|
@@ -93,7 +95,7 @@ function evaluateRule(cwd, rule, excludeDirs, changedFiles) {
|
|
|
93
95
|
const globs = rule.watchFiles && rule.watchFiles.length > 0
|
|
94
96
|
? rule.watchFiles
|
|
95
97
|
: rule.compute.kind === 'extractor'
|
|
96
|
-
? (rule.compute.source
|
|
98
|
+
? (resolveSourceGlobs(rule.compute.source ?? { files: [] }))
|
|
97
99
|
: undefined;
|
|
98
100
|
if (globs === undefined) {
|
|
99
101
|
return {
|
|
@@ -116,12 +118,19 @@ function evaluateRule(cwd, rule, excludeDirs, changedFiles) {
|
|
|
116
118
|
}
|
|
117
119
|
const abs = nodePath.resolve(cwd, rule.baseline);
|
|
118
120
|
if (!existsSync(abs)) {
|
|
121
|
+
// No artifact yet. `check` must still fail (nothing to compare against),
|
|
122
|
+
// but the CURRENT side is knowable and is exactly what the author needs to
|
|
123
|
+
// see before blessing — so compute it rather than reporting a false `0`.
|
|
124
|
+
const first = computeCurrent(cwd, rule, excludeDirs);
|
|
119
125
|
return {
|
|
120
126
|
rule,
|
|
121
127
|
status: 'error',
|
|
122
128
|
committedCount: 0,
|
|
123
|
-
|
|
124
|
-
|
|
129
|
+
...(first.error ? {} : { current: first.text }),
|
|
130
|
+
currentCount: first.error ? 0 : baselineCount(rule, first.text),
|
|
131
|
+
missingBaseline: true,
|
|
132
|
+
error: first.error ??
|
|
133
|
+
`committed baseline ${rule.baseline} does not exist — create it with \`shrk baseline update --id ${rule.id}\``,
|
|
125
134
|
};
|
|
126
135
|
}
|
|
127
136
|
let committed;
|
|
@@ -149,9 +158,22 @@ function evaluateRule(cwd, rule, excludeDirs, changedFiles) {
|
|
|
149
158
|
// the baseline has entries and the recompute has none, that is real drift
|
|
150
159
|
// (everything vanished) and must be reported as such, not swallowed as a skip.
|
|
151
160
|
if (currentCount === 0 && committedCount === 0) {
|
|
161
|
+
// A fence ASSERTS emptiness, so for it the empty case is the verified pass
|
|
162
|
+
// rather than "nothing was checked" — otherwise the rule could never be
|
|
163
|
+
// green and could not gate anything.
|
|
164
|
+
if (rule.expectEmpty === true) {
|
|
165
|
+
return {
|
|
166
|
+
rule,
|
|
167
|
+
status: 'passed',
|
|
168
|
+
committed,
|
|
169
|
+
current: computed.text,
|
|
170
|
+
committedCount,
|
|
171
|
+
currentCount,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
152
174
|
return {
|
|
153
175
|
rule,
|
|
154
|
-
status: rule
|
|
176
|
+
status: failsWhenEmpty(rule) ? 'failed' : 'skipped',
|
|
155
177
|
committed,
|
|
156
178
|
current: computed.text,
|
|
157
179
|
committedCount,
|
|
@@ -190,6 +212,7 @@ function outcomeJson(o) {
|
|
|
190
212
|
...(o.error ? { error: o.error } : {}),
|
|
191
213
|
...(o.skipReason ? { skipReason: o.skipReason } : {}),
|
|
192
214
|
...(o.emptyCompute ? { emptyCompute: true } : {}),
|
|
215
|
+
...(o.missingBaseline ? { missingBaseline: true } : {}),
|
|
193
216
|
hint: hintFor(o.rule),
|
|
194
217
|
};
|
|
195
218
|
}
|
|
@@ -216,12 +239,12 @@ async function prepare(args, opts) {
|
|
|
216
239
|
process.stdout.write(asJson({ schema: SCHEMA, error: loaded.message }) + '\n');
|
|
217
240
|
else
|
|
218
241
|
process.stderr.write(`Could not load config: ${loaded.message}\n Run \`shrk doctor\` for details.\n`);
|
|
219
|
-
return { ok: false, code: ExitCode.
|
|
242
|
+
return { ok: false, code: ExitCode.UsageError };
|
|
220
243
|
}
|
|
221
244
|
const selected = selectRules(loaded.value.rules, flagString(args, 'id') ?? undefined);
|
|
222
245
|
if (!selected.ok) {
|
|
223
246
|
process.stderr.write(selected.message + '\n');
|
|
224
|
-
return { ok: false, code: ExitCode.
|
|
247
|
+
return { ok: false, code: ExitCode.UsageError };
|
|
225
248
|
}
|
|
226
249
|
let changedFiles;
|
|
227
250
|
if (opts.changedAware) {
|
|
@@ -310,13 +333,15 @@ export const baselineCheckCommand = {
|
|
|
310
333
|
const json = flagBool(args, 'json');
|
|
311
334
|
if (prep.rules.length === 0)
|
|
312
335
|
return writeNoRules(json);
|
|
313
|
-
const outcomes = prep.rules.map((r) =>
|
|
336
|
+
const outcomes = prep.rules.map((r) => evaluateBaselineRule(prep.cwd, r, prep.excludeDirs, prep.changedFiles));
|
|
314
337
|
const failed = outcomes.filter((o) => o.status === 'failed' || (o.status === 'error' && (o.rule.severity ?? 'error') === 'error'));
|
|
315
338
|
const evaluated = outcomes.filter((o) => o.status !== 'skipped').length;
|
|
316
339
|
// 0 only when a NON-EMPTY scope was actually compared; 2 when nothing was.
|
|
340
|
+
// A skipped baseline is "partially verified", never a green 0.
|
|
341
|
+
const skippedCount = outcomes.filter((o) => o.status === 'skipped').length;
|
|
317
342
|
const exit = failed.length > 0
|
|
318
343
|
? ExitCode.Failure
|
|
319
|
-
: evaluated === 0
|
|
344
|
+
: evaluated === 0 || skippedCount > 0
|
|
320
345
|
? ExitCode.NotVerified
|
|
321
346
|
: ExitCode.VerifiedPass;
|
|
322
347
|
if (json) {
|
|
@@ -327,6 +352,19 @@ export const baselineCheckCommand = {
|
|
|
327
352
|
skipped: outcomes.filter((o) => o.status === 'skipped').length,
|
|
328
353
|
verdict: failed.length > 0 ? 'errors' : evaluated === 0 ? 'not-verified' : 'pass',
|
|
329
354
|
diagnostics: prep.planeDiagnostics,
|
|
355
|
+
gate: buildGateEnvelope('baseline check', exit, outcomes.map((o) => ({
|
|
356
|
+
id: o.rule.id,
|
|
357
|
+
type: 'baseline',
|
|
358
|
+
status: o.status,
|
|
359
|
+
severity: o.rule.severity ?? 'error',
|
|
360
|
+
counts: { committed: o.committedCount, current: o.currentCount },
|
|
361
|
+
violations: [
|
|
362
|
+
...(o.diff?.added ?? []).map((id) => ({ id, message: 'added', hint: hintFor(o.rule) })),
|
|
363
|
+
...(o.diff?.removed ?? []).map((id) => ({ id, message: 'removed', hint: hintFor(o.rule) })),
|
|
364
|
+
],
|
|
365
|
+
...(o.skipReason ? { skipReason: o.skipReason } : {}),
|
|
366
|
+
...(o.error ? { error: o.error } : {}),
|
|
367
|
+
}))),
|
|
330
368
|
}) + '\n');
|
|
331
369
|
return exit;
|
|
332
370
|
}
|
|
@@ -343,6 +381,10 @@ export const baselineCheckCommand = {
|
|
|
343
381
|
}
|
|
344
382
|
if (o.status === 'error') {
|
|
345
383
|
process.stdout.write(` ! ${o.rule.id} ${o.error}\n`);
|
|
384
|
+
if (o.missingBaseline && o.currentCount > 0) {
|
|
385
|
+
process.stdout.write(` the compute currently yields ${o.currentCount} entr${o.currentCount === 1 ? 'y' : 'ies'} — ` +
|
|
386
|
+
`run \`shrk baseline update --id ${o.rule.id}\` to bless them.\n`);
|
|
387
|
+
}
|
|
346
388
|
continue;
|
|
347
389
|
}
|
|
348
390
|
const added = o.diff?.added.length ?? 0;
|
|
@@ -378,7 +420,7 @@ export const baselineDiffCommand = {
|
|
|
378
420
|
const json = flagBool(args, 'json');
|
|
379
421
|
if (prep.rules.length === 0)
|
|
380
422
|
return writeNoRules(json);
|
|
381
|
-
const outcomes = prep.rules.map((r) =>
|
|
423
|
+
const outcomes = prep.rules.map((r) => evaluateBaselineRule(prep.cwd, r, prep.excludeDirs, undefined));
|
|
382
424
|
if (json) {
|
|
383
425
|
process.stdout.write(asJson({ schema: SCHEMA, results: outcomes.map(outcomeJson), inspection: true }) + '\n');
|
|
384
426
|
return ExitCode.VerifiedPass;
|
|
@@ -414,7 +456,7 @@ export const baselineUpdateCommand = {
|
|
|
414
456
|
errors.push({ id: rule.id, error: computed.error });
|
|
415
457
|
continue;
|
|
416
458
|
}
|
|
417
|
-
if (computed.text.trim() === '' && rule
|
|
459
|
+
if (computed.text.trim() === '' && failsWhenEmpty(rule)) {
|
|
418
460
|
errors.push({
|
|
419
461
|
id: rule.id,
|
|
420
462
|
error: 'refusing to write an EMPTY baseline for a `failOnEmpty` rule — fix the compute first',
|
|
@@ -455,7 +497,7 @@ export const baselineExplainCommand = {
|
|
|
455
497
|
const id = flagString(args, 'id') ?? args.positional[0];
|
|
456
498
|
if (!id) {
|
|
457
499
|
process.stderr.write('Usage: shrk baseline explain --id <id>\n');
|
|
458
|
-
return ExitCode.
|
|
500
|
+
return ExitCode.UsageError;
|
|
459
501
|
}
|
|
460
502
|
const prep = await prepare(args, { changedAware: false });
|
|
461
503
|
if (!prep.ok)
|
|
@@ -463,9 +505,9 @@ export const baselineExplainCommand = {
|
|
|
463
505
|
const rule = prep.all.find((r) => r.id === id);
|
|
464
506
|
if (!rule) {
|
|
465
507
|
process.stderr.write(`No baseline "${id}". Declared: ${prep.all.map((r) => r.id).join(', ') || '(none)'}\n`);
|
|
466
|
-
return ExitCode.
|
|
508
|
+
return ExitCode.UsageError;
|
|
467
509
|
}
|
|
468
|
-
const outcome =
|
|
510
|
+
const outcome = evaluateBaselineRule(prep.cwd, rule, prep.excludeDirs, undefined);
|
|
469
511
|
if (flagBool(args, 'json')) {
|
|
470
512
|
process.stdout.write(asJson({
|
|
471
513
|
schema: 'sharkcraft.baseline-explain/v1',
|
|
@@ -479,12 +521,17 @@ export const baselineExplainCommand = {
|
|
|
479
521
|
if (rule.description)
|
|
480
522
|
process.stdout.write(` ${rule.description}\n`);
|
|
481
523
|
process.stdout.write(kv('committed', rule.baseline) + '\n');
|
|
482
|
-
process.stdout.write(kv('compute', rule.compute.kind === 'command'
|
|
524
|
+
process.stdout.write(kv('compute', rule.compute.kind === 'command'
|
|
525
|
+
? `command · ${rule.compute.run}`
|
|
526
|
+
: `extractor · ${rule.compute.source?.extract ?? 'sugar'}` +
|
|
527
|
+
(rule.compute.source?.$use ? ` (via $use:${rule.compute.source.$use})` : '')) + '\n');
|
|
483
528
|
process.stdout.write(kv('direction', rule.direction ?? 'two-way') + '\n');
|
|
484
529
|
process.stdout.write(kv('canonical', outcome.diff?.canonical ?? rule.compute.canonical ?? 'auto') + '\n');
|
|
485
530
|
if (rule.keyBy)
|
|
486
531
|
process.stdout.write(kv('keyBy', rule.keyBy) + '\n');
|
|
487
|
-
process.stdout.write(kv('entries',
|
|
532
|
+
process.stdout.write(kv('entries', outcome.missingBaseline
|
|
533
|
+
? `committed (none yet) → ${outcome.currentCount} now`
|
|
534
|
+
: `${outcome.committedCount} committed → ${outcome.currentCount} now`) + '\n');
|
|
488
535
|
process.stdout.write(kv('status', outcome.status) + '\n');
|
|
489
536
|
if (outcome.error)
|
|
490
537
|
process.stdout.write(` ! ${outcome.error}\n`);
|
|
@@ -506,6 +553,6 @@ export const baselineCommand = {
|
|
|
506
553
|
const sub = args.positional[0];
|
|
507
554
|
process.stderr.write((sub ? `Unknown subcommand "${sub}". ` : '') +
|
|
508
555
|
'Usage: shrk baseline list | check [--id X] | diff | update | explain --id <id>\n');
|
|
509
|
-
return ExitCode.
|
|
556
|
+
return ExitCode.UsageError;
|
|
510
557
|
},
|
|
511
558
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changelog-data.d.ts","sourceRoot":"","sources":["../../src/commands/changelog-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,2CAA2C;AAC3C,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,eAAO,MAAM,sBAAsB,EAAE,SAAS,oBAAoB,
|
|
1
|
+
{"version":3,"file":"changelog-data.d.ts","sourceRoot":"","sources":["../../src/commands/changelog-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,2CAA2C;AAC3C,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,eAAO,MAAM,sBAAsB,EAAE,SAAS,oBAAoB,EA4KjE,CAAC"}
|
|
@@ -134,4 +134,50 @@ export const RELEASE_SURFACE_DELTAS = [
|
|
|
134
134
|
],
|
|
135
135
|
removed: [],
|
|
136
136
|
},
|
|
137
|
+
{
|
|
138
|
+
version: '0.1.0-alpha.30',
|
|
139
|
+
title: 'The engine taps the import graph, and reads its own prose',
|
|
140
|
+
added: [
|
|
141
|
+
'`shrk docs references check|explain|list` — the seventh rule plane (`docReferences[]`): ids cited in free-text PROSE (READMEs, docs, agent skill files) that no longer resolve. Markdown has no build, so these drift with zero signal. `requireContext` (default `backtick`), `exempt[]`, a per-line `<!-- ref-allow: why -->` marker, and a length-scaled `did you mean` keep it low-noise. Reads dot-directories a glob explicitly names, so `.claude/skills/**` works.',
|
|
142
|
+
'`shrk gates check` — every rule plane in ONE pass with one exit code (the CI / pre-commit primitive); `--changed-only` scopes by rule footprint, `--strict` promotes warning-severity findings to failures.',
|
|
143
|
+
'`shrk gates try --rule-file <f> [--full]` — dry-run a candidate rule against the tree without adding it to config. Dumps every captured token, so an over-matching `tokenPattern` is visible in one run instead of arriving as noise in CI.',
|
|
144
|
+
'`import-edges` extractor — the resolved dependency graph as a rule input (`emit: edge|symbol|from`, targetable by `to.module` / `to.modulePattern` / `to.files` / `to.match`). Existing planes now express adoption ledgers, orphan detection, deprecation ratchets and targeted fences. Alias-aware; no persisted index, so no staleness.',
|
|
145
|
+
'`filenames` extractor + `mode: parity` — companion-file invariants (every `X.ts` has an `X.test.ts`).',
|
|
146
|
+
'Shared extractors: a top-level `extractors` map plus `{ $use: "<id>" }` on any plane (local fields override). The planes describing one id set can no longer drift apart, and a typo\'d id fails the config load rather than silently matching nothing.',
|
|
147
|
+
'`generatedArtifacts[]` for MIXED trees: `sources[]` (N writers, each verifying its own sub-glob), `handMaintained[]` (literal filenames only, so the exemption cannot silently widen) and `handMaintainedMarker` for an in-file bless. A file under the tree owned by neither is an `unclassified` finding.',
|
|
148
|
+
'`expectEmpty: true` on `baselines[]` — a fence that fails the moment the edge set becomes non-empty.',
|
|
149
|
+
'`shrk check wiring --fix` now adds the missing IMPORT alongside the array member when the specifier is a pure function of the member name AND resolves to the declaring file; otherwise it refuses with `needs-import`.',
|
|
150
|
+
],
|
|
151
|
+
changed: [
|
|
152
|
+
'FIXED (P1): `--fix --write` could green the wiring gate over a file that no longer compiles. The common registry shape IMPORTS its array members, and appending the token alone left `TS2304: Cannot find name`. It now writes both edits or refuses.',
|
|
153
|
+
'FIXED (P1): reference resolution ran in THREE modules — the prose linter and both self-config doctors — each from its own sources, agreeing only by coincidence. The self-config doctor reported 17 of shrk\'s own correctly-registered ids as unknown (its hand-written "known ids" union omitted policies, decisions, scaffold patterns and paths). One resolver now answers for every kind, and each kind reads the source its `list` verb reads.',
|
|
154
|
+
'FIXED (P2): `shrk graph status` judged index freshness by working-tree DIVERGENCE while `shrk code-intel` judged it by wall-clock age — contradictory verdicts on the same index in the same second, with every arch/cycle count derived from it inheriting the staleness unmarked. Divergence is now the single verdict; age is a display detail (`current (indexed 5d ago)` / `STALE — N file(s) changed since index`).',
|
|
155
|
+
'A finding DERIVED from a stale graph index is reported `NOT VERIFIED` rather than as a count — the loud-skip contract the rule planes already honour, extended to the graph-derived surfaces. An unmeasured verdict is never a pass.',
|
|
156
|
+
'An ERRORED rule is no longer counted as `evaluated` by `gates check` / `docs references check`, so a warning-severity rule that could not run exits `2` instead of printing "not blocking" above exit `0`.',
|
|
157
|
+
'A doc-reference rule whose registries are ALL empty refuses loudly instead of reporting every id as unresolved — a gate that confidently flags correct usage is worse than no gate.',
|
|
158
|
+
'`detectGraphFreshness` no longer throws on a store whose `meta.json` exists but whose remaining parts do not (an interrupted index); it reports "could not measure", which reads as not-verified.',
|
|
159
|
+
'`shrk graph status` labels its snapshot size `files indexed` — it deliberately does not move when a new file appears on disk; that shows up on the `drift` line.',
|
|
160
|
+
],
|
|
161
|
+
removed: [],
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
version: '0.1.0-alpha.29',
|
|
165
|
+
title: 'The exit code has to agree with the sentence',
|
|
166
|
+
added: [
|
|
167
|
+
'`shrk check wiring --fix` — deterministic autofix for the mechanically-unambiguous `declared-but-not-registered` case: append the missing id to its sink array. Dry-run by default, `--write` applies. Refuses ambiguity (N sinks, non-array sink, non-unique file/array, chain rules, parity violations) and lists what it left alone WITH the reason — it never guesses.',
|
|
168
|
+
'`shrk explain <ruleId>` resolves a rule id across ALL planes and dispatches to the right explainer; a non-rule token still gets the original topic search. The per-plane forms remain.',
|
|
169
|
+
'A shared `gate` envelope (`sharkcraft.gate/v1`) inside every gate verb\'s `--json`: `verb` / `exit` / normalized per-rule `{id,type,status,severity,counts,violations,skipReason}`. Additive — the per-plane payloads are unchanged. See docs/gate-json.md.',
|
|
170
|
+
'Global `--no-hints` silences the advisory piped-exit note (the structured `--exit-trailer` channel is unaffected).',
|
|
171
|
+
'Exit code `3` = usage error (unloadable config / unknown rule id / bad flag value) on the gate verbs, split out of `2` so "the gate proved nothing" and "the gate never started" are distinguishable.',
|
|
172
|
+
],
|
|
173
|
+
changed: [
|
|
174
|
+
'A SKIPPED rule is no longer masked by a passing sibling. `failOnEmpty` now defaults to TRUE for `error`-severity rules (an error rule matching zero subjects is a bug in the rule, not a pass) and any skip with no failures exits `2`, never `0`. `warning`-severity rules still default to `false`. BEHAVIOUR CHANGE: an error-severity rule that matches nothing now exits `1` — fix the selector or set `failOnEmpty: false`.',
|
|
175
|
+
'`shrk help <multi-word verb>` resolves catalog-documented paths (`check wiring`, `wiring unprovided`, …) that are dispatched from a parent handler and therefore never appear in the command trie; it also lists sibling verbs. Previously answered "Unknown command".',
|
|
176
|
+
'`shrk baseline explain` always computes the CURRENT side, so a rule with no committed artifact yet reports `committed (none yet) → N now` instead of a false `0 now`. `baseline check` names what would be blessed.',
|
|
177
|
+
'`shrk registry` accepts both argument orders — `registry list <name>` now works alongside `registry <name> list`; a verb-first call with an unknown name names the correct grammar.',
|
|
178
|
+
'`shrk policy-lint` returns `3` (usage) rather than `1` (violations) when the config cannot be loaded, and `2` when it scanned nothing.',
|
|
179
|
+
'The piped-exit note is emitted at most once per process.',
|
|
180
|
+
],
|
|
181
|
+
removed: [],
|
|
182
|
+
},
|
|
137
183
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.command.d.ts","sourceRoot":"","sources":["../../src/commands/check.command.ts"],"names":[],"mappings":"AAoBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"check.command.d.ts","sourceRoot":"","sources":["../../src/commands/check.command.ts"],"names":[],"mappings":"AAoBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA2jChC,eAAO,MAAM,YAAY,EAAE,eA+G1B,CAAC"}
|
|
@@ -9,7 +9,8 @@ import { computeDeletedOrphans } from "../diff/deleted-orphans.js";
|
|
|
9
9
|
import { renderWiringExplain } from "./wiring.command.js";
|
|
10
10
|
import { validateTemplateVariables } from '@shrkcrft/templates';
|
|
11
11
|
import { FileChangeType, planGeneration } from '@shrkcrft/generator';
|
|
12
|
-
import { evaluateBoundaries, explainWiring, loadTsconfigPaths, runWiring, scanImports, summarizeImports, } from '@shrkcrft/boundaries';
|
|
12
|
+
import { evaluateBoundaries, explainWiring, loadTsconfigPaths, runWiring, scanImports, summarizeImports, planWiringFix, readMatchingFiles, wiringGlobsOf, } from '@shrkcrft/boundaries';
|
|
13
|
+
import { buildGateEnvelope } from "../gates/gate-envelope.js";
|
|
13
14
|
function knowledgeGroup(inspection) {
|
|
14
15
|
const dup = inspection.validationIssues.filter((i) => i.code === 'duplicate-id');
|
|
15
16
|
const missing = inspection.validationIssues.filter((i) => i.code !== 'duplicate-id');
|
|
@@ -523,7 +524,8 @@ async function checkBoundariesOnce(args) {
|
|
|
523
524
|
// ────────────────────────────────────────────────────────────────────────
|
|
524
525
|
// Subcommand: wiring — "declared but not wired" completeness checks
|
|
525
526
|
// ────────────────────────────────────────────────────────────────────────
|
|
526
|
-
const WIRING_CHECK_USAGE = 'shrk check wiring [--changed-only] [--since <ref>] [--base <ref>] [--only <ids>] [--explain <ruleId>]
|
|
527
|
+
const WIRING_CHECK_USAGE = 'shrk check wiring [--changed-only] [--since <ref>] [--base <ref>] [--only <ids>] [--explain <ruleId>]\n' +
|
|
528
|
+
' [--fix [--write]] [--json] [--strict]\n' +
|
|
527
529
|
' Cross-file "declared but not wired" completeness gate. Scope flags select rules by\n' +
|
|
528
530
|
' FOOTPRINT — a rule fires when the diff touches EITHER its declared or its registered\n' +
|
|
529
531
|
' side (so a registration edited in file B fires a rule declared in file A).\n' +
|
|
@@ -531,7 +533,105 @@ const WIRING_CHECK_USAGE = 'shrk check wiring [--changed-only] [--since <ref>] [
|
|
|
531
533
|
' --since <ref> scope the diff to changes since <ref> (--base is a synonym)\n' +
|
|
532
534
|
' --only <ids> run only these rule ids (comma-separated)\n' +
|
|
533
535
|
' --explain <id> dry-run ONE rule and print the declared/registered sets it extracts\n' +
|
|
536
|
+
' --fix plan the deterministic repair (append the token to its sink array);\n' +
|
|
537
|
+
' dry-run by default, prints every edit AND any import it must add.\n' +
|
|
538
|
+
' Refuses anything ambiguous — see `needs-import` in docs/wiring.md.\n' +
|
|
539
|
+
' --write apply the planned edits (only meaningful with --fix)\n' +
|
|
534
540
|
' Exit: 0 verified pass · 1 violations · 2 not-verified (0 rules evaluated in scope).';
|
|
541
|
+
/**
|
|
542
|
+
* The honest exit code for a wiring run.
|
|
543
|
+
*
|
|
544
|
+
* The banner already said "Not a full green" when some rules were skipped, but
|
|
545
|
+
* the code an agent chains on returned `0` — a rule that enforced NOTHING was
|
|
546
|
+
* masked by its passing siblings. The verdict must match the sentence:
|
|
547
|
+
*
|
|
548
|
+
* `1` violations (or a `failOnEmpty` skip, which the engine folds into the verdict)
|
|
549
|
+
* `2` nothing evaluated, OR anything skipped — partially verified is not verified
|
|
550
|
+
* `0` only when every configured rule actually ran and passed
|
|
551
|
+
*/
|
|
552
|
+
function wiringExitCode(report, evaluated) {
|
|
553
|
+
if (report.verdict === 'errors')
|
|
554
|
+
return ExitCode.Failure;
|
|
555
|
+
if (evaluated === 0)
|
|
556
|
+
return ExitCode.NotVerified;
|
|
557
|
+
if (report.skipped.length > 0)
|
|
558
|
+
return ExitCode.NotVerified;
|
|
559
|
+
return ExitCode.VerifiedPass;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* `check wiring --fix` — append a declared-but-unregistered token to its sink
|
|
563
|
+
* array, but ONLY where the edit is mechanically unambiguous.
|
|
564
|
+
*
|
|
565
|
+
* Dry-run by default: it prints the exact diff it would make and writes
|
|
566
|
+
* nothing. `--write` applies it. Anything the planner will not touch is listed
|
|
567
|
+
* with the reason, because a gate that silently half-fixes is worse than one
|
|
568
|
+
* that does nothing — the user must be able to see what was left.
|
|
569
|
+
*/
|
|
570
|
+
function runWiringFix(cwd, rules, report, write, wantJson) {
|
|
571
|
+
const allEdits = [];
|
|
572
|
+
const allSkips = [];
|
|
573
|
+
// Read every file a sink could live in, once.
|
|
574
|
+
const globs = [...new Set(rules.flatMap((r) => wiringGlobsOf(r)))];
|
|
575
|
+
const cache = readMatchingFiles(cwd, globs, new Set());
|
|
576
|
+
const files = [...cache.entries()].map(([path, content]) => ({ path, content }));
|
|
577
|
+
for (const rule of rules) {
|
|
578
|
+
const violations = report.violations.filter((v) => v.ruleId === rule.id);
|
|
579
|
+
if (violations.length === 0)
|
|
580
|
+
continue;
|
|
581
|
+
const plan = planWiringFix(rule, violations, files);
|
|
582
|
+
allEdits.push(...plan.edits);
|
|
583
|
+
allSkips.push(...plan.skipped);
|
|
584
|
+
}
|
|
585
|
+
// One write per file: the planner threads each file's content forward, so the
|
|
586
|
+
// LAST edit for a path carries every earlier insertion.
|
|
587
|
+
const finalByFile = new Map();
|
|
588
|
+
for (const e of allEdits)
|
|
589
|
+
finalByFile.set(e.file, e.nextContent);
|
|
590
|
+
let written = 0;
|
|
591
|
+
if (write) {
|
|
592
|
+
for (const [rel, content] of finalByFile) {
|
|
593
|
+
writeFileSync(nodePath.resolve(cwd, rel), content, 'utf8');
|
|
594
|
+
written += 1;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (wantJson) {
|
|
598
|
+
process.stdout.write(asJson({
|
|
599
|
+
schema: 'sharkcraft.wiring-fix/v1',
|
|
600
|
+
applied: write,
|
|
601
|
+
filesWritten: written,
|
|
602
|
+
edits: allEdits.map(({ nextContent: _drop, ...rest }) => rest),
|
|
603
|
+
skipped: allSkips,
|
|
604
|
+
}) + '\n');
|
|
605
|
+
return allEdits.length === 0 && allSkips.length > 0 ? ExitCode.Failure : ExitCode.VerifiedPass;
|
|
606
|
+
}
|
|
607
|
+
process.stdout.write(header(write ? 'Wiring fix (applied)' : 'Wiring fix (dry run)'));
|
|
608
|
+
if (allEdits.length === 0 && allSkips.length === 0) {
|
|
609
|
+
process.stdout.write(' Nothing to fix — no declared-but-unregistered tokens.\n');
|
|
610
|
+
return ExitCode.VerifiedPass;
|
|
611
|
+
}
|
|
612
|
+
for (const e of allEdits) {
|
|
613
|
+
process.stdout.write(` ${write ? 'wrote ' : 'would add'} ${e.token} → ${e.file}:${e.line}\n`);
|
|
614
|
+
// Show BOTH halves of the edit. An array append whose import is invisible
|
|
615
|
+
// in the preview is exactly how the reviewer approves a change they have
|
|
616
|
+
// not actually seen — and the import is the half that decides whether the
|
|
617
|
+
// result compiles.
|
|
618
|
+
if (e.importInsert !== undefined) {
|
|
619
|
+
process.stdout.write(` + ${e.importInsert}${e.importLine !== undefined ? ` (line ${e.importLine})` : ''}\n`);
|
|
620
|
+
}
|
|
621
|
+
process.stdout.write(` + ${e.insert}${e.importInsert !== undefined ? ` (line ${e.line})` : ''}\n`);
|
|
622
|
+
}
|
|
623
|
+
if (allSkips.length > 0) {
|
|
624
|
+
process.stdout.write(`\n Left untouched (${allSkips.length}) — not mechanically unambiguous:\n`);
|
|
625
|
+
for (const sk of allSkips) {
|
|
626
|
+
process.stdout.write(` • ${sk.token} [${sk.reason}] ${sk.detail}\n`);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
process.stdout.write(write
|
|
630
|
+
? `\n${written} file(s) written. Re-run \`shrk check wiring\` to confirm, and review the diff.\n`
|
|
631
|
+
: '\nDry run — nothing written. Re-run with `--write` to apply.\n');
|
|
632
|
+
// An unfixable violation still means the gate is red.
|
|
633
|
+
return allSkips.length > 0 ? ExitCode.Failure : ExitCode.VerifiedPass;
|
|
634
|
+
}
|
|
535
635
|
async function checkWiring(args) {
|
|
536
636
|
const cwd = resolveCwd(args);
|
|
537
637
|
// `--help`/`-h` on the subverb documents its own scoping flags instead of
|
|
@@ -570,7 +670,7 @@ async function checkWiring(args) {
|
|
|
570
670
|
const explainId = flagString(args, 'explain');
|
|
571
671
|
if (!explainId) {
|
|
572
672
|
process.stderr.write('Usage: shrk check wiring --explain <ruleId> [--json]\n');
|
|
573
|
-
return
|
|
673
|
+
return ExitCode.UsageError;
|
|
574
674
|
}
|
|
575
675
|
const rule = rules.find((r) => r.id === explainId);
|
|
576
676
|
if (!rule) {
|
|
@@ -580,7 +680,7 @@ async function checkWiring(args) {
|
|
|
580
680
|
return 2;
|
|
581
681
|
}
|
|
582
682
|
process.stderr.write(`No wiring rule "${explainId}". Configured rules: ${ids.length > 0 ? ids.join(', ') : '(none)'}\n`);
|
|
583
|
-
return
|
|
683
|
+
return ExitCode.UsageError;
|
|
584
684
|
}
|
|
585
685
|
return renderWiringExplain(explainWiring(cwd, rule), wantJson);
|
|
586
686
|
}
|
|
@@ -637,6 +737,10 @@ async function checkWiring(args) {
|
|
|
637
737
|
// blast radius, not just the rules whose literal files were edited (a25 §3.2).
|
|
638
738
|
const selectedRuleIds = report.rules.map((r) => r.ruleId);
|
|
639
739
|
const scoped = changedOnly || since !== undefined;
|
|
740
|
+
// ── --fix: deterministic, heavily-guarded autofix ─────────────────────
|
|
741
|
+
if (flagBool(args, 'fix')) {
|
|
742
|
+
return runWiringFix(cwd, rules, report, flagBool(args, 'write'), wantJson);
|
|
743
|
+
}
|
|
640
744
|
if (wantJson) {
|
|
641
745
|
// Carry the honest counts so a machine consumer can tell "0 evaluated" from
|
|
642
746
|
// a real green, and see how many rules the scope skipped + which fired.
|
|
@@ -650,15 +754,31 @@ async function checkWiring(args) {
|
|
|
650
754
|
notVerified,
|
|
651
755
|
selectedRuleIds,
|
|
652
756
|
// Distinguish verified-pass / failure / not-verified for a chained gate.
|
|
653
|
-
exitCode:
|
|
757
|
+
exitCode: wiringExitCode(report, evaluated),
|
|
758
|
+
// One shape across every plane — see docs/gate-json.md.
|
|
759
|
+
gate: buildGateEnvelope('check wiring', wiringExitCode(report, evaluated), report.rules.map((r) => {
|
|
760
|
+
const skip = report.skipped.find((s) => s.ruleId === r.ruleId);
|
|
761
|
+
return {
|
|
762
|
+
id: r.ruleId,
|
|
763
|
+
type: 'wiring',
|
|
764
|
+
status: r.status,
|
|
765
|
+
severity: r.severity,
|
|
766
|
+
counts: { declared: r.declaredCount, registered: r.registeredCount },
|
|
767
|
+
violations: r.violations.map((v) => ({
|
|
768
|
+
id: v.token,
|
|
769
|
+
file: v.file,
|
|
770
|
+
line: v.line,
|
|
771
|
+
...(v.message ? { message: v.message } : {}),
|
|
772
|
+
...(v.hint ? { hint: v.hint } : {}),
|
|
773
|
+
})),
|
|
774
|
+
...(skip ? { skipReason: skip.reason } : {}),
|
|
775
|
+
...(r.error ? { error: r.error } : {}),
|
|
776
|
+
};
|
|
777
|
+
})),
|
|
654
778
|
}) + '\n');
|
|
655
779
|
// `evaluated === 0` = nothing checked in scope → NOT verified (`2`), never a
|
|
656
780
|
// silent `0` an agent's `&& next` would march past (a25 §1.1).
|
|
657
|
-
return evaluated
|
|
658
|
-
? ExitCode.NotVerified
|
|
659
|
-
: report.verdict === 'errors'
|
|
660
|
-
? ExitCode.Failure
|
|
661
|
-
: ExitCode.VerifiedPass;
|
|
781
|
+
return wiringExitCode(report, evaluated);
|
|
662
782
|
}
|
|
663
783
|
process.stdout.write(header('Wiring check'));
|
|
664
784
|
// `evaluated` counts rules that actually ran a comparison (globs matched >0
|
|
@@ -707,6 +827,11 @@ async function checkWiring(args) {
|
|
|
707
827
|
continue;
|
|
708
828
|
process.stdout.write(`\n ! ${r.ruleId}: the registered side extracted 0 ids while ${r.declaredCount} were declared —\n` +
|
|
709
829
|
` every declared token "fails". Check the registered glob before chasing the tokens.\n`);
|
|
830
|
+
// When the engine knows WHY the sink came back empty, say it here — the
|
|
831
|
+
// generic advice above would otherwise send the reader to inspect a glob
|
|
832
|
+
// that is perfectly fine.
|
|
833
|
+
if (r.sinkHint)
|
|
834
|
+
process.stdout.write(` → ${r.sinkHint}\n`);
|
|
710
835
|
}
|
|
711
836
|
if (report.violations.length === 0 && report.diagnostics.length === 0) {
|
|
712
837
|
// A failOnEmpty skip produces no violation object but IS a failure.
|
|
@@ -722,10 +847,10 @@ async function checkWiring(args) {
|
|
|
722
847
|
process.stdout.write(`\nNo wiring violations among the ${evaluated} rule(s) evaluated — ` +
|
|
723
848
|
`${notVerified} of ${configured} NOT verified${breakdown}. Not a full green.\n`);
|
|
724
849
|
}
|
|
725
|
-
return
|
|
850
|
+
return wiringExitCode(report, evaluated);
|
|
726
851
|
}
|
|
727
852
|
if (report.violations.length === 0) {
|
|
728
|
-
return report
|
|
853
|
+
return wiringExitCode(report, evaluated);
|
|
729
854
|
}
|
|
730
855
|
// Group by rule for a readable report.
|
|
731
856
|
for (const r of report.rules) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-intel.command.d.ts","sourceRoot":"","sources":["../../src/commands/code-intel.command.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"code-intel.command.d.ts","sourceRoot":"","sources":["../../src/commands/code-intel.command.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAIhC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,EAAE,eA6E9B,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { buildCodeIntelligenceChecks, DoctorSeverity, } from '@shrkcrft/inspector';
|
|
2
2
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
3
|
+
import { detectGraphFreshness } from '@shrkcrft/graph';
|
|
3
4
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
4
5
|
/**
|
|
5
6
|
* `shrk code-intel` — one-shot view of the 14 code-intelligence
|
|
@@ -28,7 +29,10 @@ export const codeIntelCommand = {
|
|
|
28
29
|
const checkId = flagString(args, 'check');
|
|
29
30
|
const staleDaysRaw = flagString(args, 'stale-days');
|
|
30
31
|
const staleDays = staleDaysRaw ? Number.parseInt(staleDaysRaw, 10) : undefined;
|
|
31
|
-
|
|
32
|
+
// Divergence against the working tree — the SAME check `shrk graph status`
|
|
33
|
+
// reports, so the two surfaces cannot contradict each other. Injected
|
|
34
|
+
// because `@shrkcrft/graph` sits above inspector in the layer order.
|
|
35
|
+
const options = { graphDivergence: detectGraphFreshness(cwd) };
|
|
32
36
|
if (typeof staleDays === 'number' && Number.isFinite(staleDays) && staleDays > 0) {
|
|
33
37
|
options.staleThresholdDays = staleDays;
|
|
34
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-catalog.d.ts","sourceRoot":"","sources":["../../src/commands/command-catalog.ts"],"names":[],"mappings":"AAAA,oBAAY,WAAW;IACrB,QAAQ,cAAc;IACtB,iBAAiB,mBAAmB;IACpC,gBAAgB,kBAAkB;IAClC,YAAY,kBAAkB;IAC9B,SAAS,eAAe;IACxB,cAAc,oBAAoB;CACnC;AAED;;;;;;GAMG;AACH,oBAAY,cAAc;IACxB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;CAClB;AAED,8BAA8B;AAC9B,oBAAY,eAAe;IACzB,KAAK,UAAU;IACf,KAAK,UAAU;IACf,EAAE,OAAO;IACT,UAAU,gBAAgB;IAC1B,UAAU,eAAe;CAC1B;AAED;;;;;;GAMG;AACH,oBAAY,eAAe;IACzB,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,KAAK,UAAU;IACf,MAAM,WAAW;CAClB;AAED;;;;;;GAMG;AACH,oBAAY,gBAAgB;IAC1B,MAAM,WAAW;IACjB,SAAS,cAAc;IACvB,KAAK,UAAU;IACf,UAAU,eAAe;IACzB,OAAO,YAAY;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,oBAAY,WAAW;IACrB,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,YAAY,iBAAiB;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,kCAAkC;IAClC,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,oBAAoB,
|
|
1
|
+
{"version":3,"file":"command-catalog.d.ts","sourceRoot":"","sources":["../../src/commands/command-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -406,6 +406,14 @@ export const COMMAND_CATALOG = Object.freeze([
|
|
|
406
406
|
surface: CommandSurface.Common,
|
|
407
407
|
taskRole: CommandTaskRole.Validate,
|
|
408
408
|
}),
|
|
409
|
+
entry({
|
|
410
|
+
command: 'check wiring --fix',
|
|
411
|
+
description: 'Deterministic autofix for `declared-but-not-registered`: append the missing id to its sink array. Dry-run by default; `--write` applies. Refuses anything ambiguous (N sinks, non-array sink, non-unique file/array) and lists what it left alone with the reason — it never guesses. [--write] [--json]',
|
|
412
|
+
category: 'core',
|
|
413
|
+
safetyLevel: SafetyLevel.WritesSource,
|
|
414
|
+
surface: CommandSurface.Advanced,
|
|
415
|
+
taskRole: CommandTaskRole.Apply,
|
|
416
|
+
}),
|
|
409
417
|
entry({
|
|
410
418
|
command: 'policy-lint explain',
|
|
411
419
|
description: 'Dry-run ONE policyRule and print every hit with file:line — INCLUDING the hits an exemption (exemptFiles / exemptLines) or the lexical scan zone dropped, each labelled with which one applied. The author-loop view of what the gate sees. [--json]',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daily.commands.d.ts","sourceRoot":"","sources":["../../src/commands/daily.commands.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"daily.commands.d.ts","sourceRoot":"","sources":["../../src/commands/daily.commands.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAOhC,eAAO,MAAM,WAAW,EAAE,eAsFzB,CAAC;AAcF,eAAO,MAAM,WAAW,EAAE,eAoGzB,CAAC;AAKF,eAAO,MAAM,cAAc,EAAE,eAmH5B,CAAC"}
|
|
@@ -2,6 +2,7 @@ import { buildAiReadinessReport, buildTaskPacket, inspectSharkcraft, runDoctor,
|
|
|
2
2
|
import { recommendPresets } from '@shrkcrft/presets';
|
|
3
3
|
import { flagBool, flagNumber, resolveCwd, } from "../command-registry.js";
|
|
4
4
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
5
|
+
import { tryExplainGateRule } from "./gates.command.js";
|
|
5
6
|
// ────────────────────────────────────────────────────────────────────────
|
|
6
7
|
// shrk next — "what should I do right now in this repo?"
|
|
7
8
|
// ────────────────────────────────────────────────────────────────────────
|
|
@@ -177,9 +178,18 @@ export const explainCommand = {
|
|
|
177
178
|
async run(args) {
|
|
178
179
|
const topic = args.positional.join(' ').trim();
|
|
179
180
|
if (!topic) {
|
|
180
|
-
process.stderr.write('Usage: shrk explain "<topic>"\n');
|
|
181
|
+
process.stderr.write('Usage: shrk explain "<topic>" (a topic, or the id of any data-defined rule)\n');
|
|
181
182
|
return 2;
|
|
182
183
|
}
|
|
184
|
+
// A single token that exactly names a declared rule on ANY plane is
|
|
185
|
+
// explained as that rule — the one entrypoint the trust layer promises.
|
|
186
|
+
// Anything else keeps the original topic search, so no existing call
|
|
187
|
+
// changes meaning.
|
|
188
|
+
if (args.positional.length === 1) {
|
|
189
|
+
const asRule = await tryExplainGateRule(args, topic);
|
|
190
|
+
if (asRule !== undefined)
|
|
191
|
+
return asRule;
|
|
192
|
+
}
|
|
183
193
|
const inspection = await inspectSharkcraft({ cwd: resolveCwd(args) });
|
|
184
194
|
const maxEntries = flagNumber(args, 'max-entries') ?? 6;
|
|
185
195
|
const packet = buildTaskPacket(inspection, topic, { maxTokens: 2500 });
|