@shrkcrft/cli 0.1.0-alpha.27 → 0.1.0-alpha.29
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 +8 -0
- package/dist/commands/baseline.command.d.ts.map +1 -0
- package/dist/commands/baseline.command.js +542 -0
- package/dist/commands/changelog-data.d.ts.map +1 -1
- package/dist/commands/changelog-data.js +42 -0
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +147 -12
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +120 -0
- package/dist/commands/daily.commands.d.ts.map +1 -1
- package/dist/commands/daily.commands.js +11 -1
- package/dist/commands/gates.command.d.ts +16 -0
- package/dist/commands/gates.command.d.ts.map +1 -0
- package/dist/commands/gates.command.js +377 -0
- package/dist/commands/generated.command.d.ts +6 -0
- package/dist/commands/generated.command.d.ts.map +1 -0
- package/dist/commands/generated.command.js +544 -0
- package/dist/commands/help.command.d.ts.map +1 -1
- package/dist/commands/help.command.js +64 -2
- package/dist/commands/ingest.command.d.ts +11 -0
- package/dist/commands/ingest.command.d.ts.map +1 -1
- package/dist/commands/ingest.command.js +49 -23
- package/dist/commands/policy-lint.command.d.ts +37 -0
- package/dist/commands/policy-lint.command.d.ts.map +1 -1
- package/dist/commands/policy-lint.command.js +167 -8
- package/dist/commands/registry.command.d.ts.map +1 -1
- package/dist/commands/registry.command.js +70 -13
- package/dist/commands/wiring.command.d.ts.map +1 -1
- package/dist/commands/wiring.command.js +25 -0
- 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/finish/run-finish.d.ts.map +1 -1
- package/dist/finish/run-finish.js +9 -3
- 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-view.d.ts +35 -0
- package/dist/gates/gate-rule-view.d.ts.map +1 -0
- package/dist/gates/gate-rule-view.js +81 -0
- package/dist/gates/rule-coverage.d.ts +53 -0
- package/dist/gates/rule-coverage.d.ts.map +1 -0
- package/dist/gates/rule-coverage.js +165 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +27 -3
- package/package.json +33 -33
|
@@ -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');
|
|
@@ -532,6 +533,93 @@ const WIRING_CHECK_USAGE = 'shrk check wiring [--changed-only] [--since <ref>] [
|
|
|
532
533
|
' --only <ids> run only these rule ids (comma-separated)\n' +
|
|
533
534
|
' --explain <id> dry-run ONE rule and print the declared/registered sets it extracts\n' +
|
|
534
535
|
' Exit: 0 verified pass · 1 violations · 2 not-verified (0 rules evaluated in scope).';
|
|
536
|
+
/**
|
|
537
|
+
* The honest exit code for a wiring run.
|
|
538
|
+
*
|
|
539
|
+
* The banner already said "Not a full green" when some rules were skipped, but
|
|
540
|
+
* the code an agent chains on returned `0` — a rule that enforced NOTHING was
|
|
541
|
+
* masked by its passing siblings. The verdict must match the sentence:
|
|
542
|
+
*
|
|
543
|
+
* `1` violations (or a `failOnEmpty` skip, which the engine folds into the verdict)
|
|
544
|
+
* `2` nothing evaluated, OR anything skipped — partially verified is not verified
|
|
545
|
+
* `0` only when every configured rule actually ran and passed
|
|
546
|
+
*/
|
|
547
|
+
function wiringExitCode(report, evaluated) {
|
|
548
|
+
if (report.verdict === 'errors')
|
|
549
|
+
return ExitCode.Failure;
|
|
550
|
+
if (evaluated === 0)
|
|
551
|
+
return ExitCode.NotVerified;
|
|
552
|
+
if (report.skipped.length > 0)
|
|
553
|
+
return ExitCode.NotVerified;
|
|
554
|
+
return ExitCode.VerifiedPass;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* `check wiring --fix` — append a declared-but-unregistered token to its sink
|
|
558
|
+
* array, but ONLY where the edit is mechanically unambiguous.
|
|
559
|
+
*
|
|
560
|
+
* Dry-run by default: it prints the exact diff it would make and writes
|
|
561
|
+
* nothing. `--write` applies it. Anything the planner will not touch is listed
|
|
562
|
+
* with the reason, because a gate that silently half-fixes is worse than one
|
|
563
|
+
* that does nothing — the user must be able to see what was left.
|
|
564
|
+
*/
|
|
565
|
+
function runWiringFix(cwd, rules, report, write, wantJson) {
|
|
566
|
+
const allEdits = [];
|
|
567
|
+
const allSkips = [];
|
|
568
|
+
// Read every file a sink could live in, once.
|
|
569
|
+
const globs = [...new Set(rules.flatMap((r) => wiringGlobsOf(r)))];
|
|
570
|
+
const cache = readMatchingFiles(cwd, globs, new Set());
|
|
571
|
+
const files = [...cache.entries()].map(([path, content]) => ({ path, content }));
|
|
572
|
+
for (const rule of rules) {
|
|
573
|
+
const violations = report.violations.filter((v) => v.ruleId === rule.id);
|
|
574
|
+
if (violations.length === 0)
|
|
575
|
+
continue;
|
|
576
|
+
const plan = planWiringFix(rule, violations, files);
|
|
577
|
+
allEdits.push(...plan.edits);
|
|
578
|
+
allSkips.push(...plan.skipped);
|
|
579
|
+
}
|
|
580
|
+
// One write per file: the planner threads each file's content forward, so the
|
|
581
|
+
// LAST edit for a path carries every earlier insertion.
|
|
582
|
+
const finalByFile = new Map();
|
|
583
|
+
for (const e of allEdits)
|
|
584
|
+
finalByFile.set(e.file, e.nextContent);
|
|
585
|
+
let written = 0;
|
|
586
|
+
if (write) {
|
|
587
|
+
for (const [rel, content] of finalByFile) {
|
|
588
|
+
writeFileSync(nodePath.resolve(cwd, rel), content, 'utf8');
|
|
589
|
+
written += 1;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (wantJson) {
|
|
593
|
+
process.stdout.write(asJson({
|
|
594
|
+
schema: 'sharkcraft.wiring-fix/v1',
|
|
595
|
+
applied: write,
|
|
596
|
+
filesWritten: written,
|
|
597
|
+
edits: allEdits.map(({ nextContent: _drop, ...rest }) => rest),
|
|
598
|
+
skipped: allSkips,
|
|
599
|
+
}) + '\n');
|
|
600
|
+
return allEdits.length === 0 && allSkips.length > 0 ? ExitCode.Failure : ExitCode.VerifiedPass;
|
|
601
|
+
}
|
|
602
|
+
process.stdout.write(header(write ? 'Wiring fix (applied)' : 'Wiring fix (dry run)'));
|
|
603
|
+
if (allEdits.length === 0 && allSkips.length === 0) {
|
|
604
|
+
process.stdout.write(' Nothing to fix — no declared-but-unregistered tokens.\n');
|
|
605
|
+
return ExitCode.VerifiedPass;
|
|
606
|
+
}
|
|
607
|
+
for (const e of allEdits) {
|
|
608
|
+
process.stdout.write(` ${write ? 'wrote ' : 'would add'} ${e.token} → ${e.file}:${e.line}\n`);
|
|
609
|
+
process.stdout.write(` + ${e.insert}\n`);
|
|
610
|
+
}
|
|
611
|
+
if (allSkips.length > 0) {
|
|
612
|
+
process.stdout.write(`\n Left untouched (${allSkips.length}) — not mechanically unambiguous:\n`);
|
|
613
|
+
for (const sk of allSkips) {
|
|
614
|
+
process.stdout.write(` • ${sk.token} [${sk.reason}] ${sk.detail}\n`);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
process.stdout.write(write
|
|
618
|
+
? `\n${written} file(s) written. Re-run \`shrk check wiring\` to confirm, and review the diff.\n`
|
|
619
|
+
: '\nDry run — nothing written. Re-run with `--write` to apply.\n');
|
|
620
|
+
// An unfixable violation still means the gate is red.
|
|
621
|
+
return allSkips.length > 0 ? ExitCode.Failure : ExitCode.VerifiedPass;
|
|
622
|
+
}
|
|
535
623
|
async function checkWiring(args) {
|
|
536
624
|
const cwd = resolveCwd(args);
|
|
537
625
|
// `--help`/`-h` on the subverb documents its own scoping flags instead of
|
|
@@ -570,7 +658,7 @@ async function checkWiring(args) {
|
|
|
570
658
|
const explainId = flagString(args, 'explain');
|
|
571
659
|
if (!explainId) {
|
|
572
660
|
process.stderr.write('Usage: shrk check wiring --explain <ruleId> [--json]\n');
|
|
573
|
-
return
|
|
661
|
+
return ExitCode.UsageError;
|
|
574
662
|
}
|
|
575
663
|
const rule = rules.find((r) => r.id === explainId);
|
|
576
664
|
if (!rule) {
|
|
@@ -580,7 +668,7 @@ async function checkWiring(args) {
|
|
|
580
668
|
return 2;
|
|
581
669
|
}
|
|
582
670
|
process.stderr.write(`No wiring rule "${explainId}". Configured rules: ${ids.length > 0 ? ids.join(', ') : '(none)'}\n`);
|
|
583
|
-
return
|
|
671
|
+
return ExitCode.UsageError;
|
|
584
672
|
}
|
|
585
673
|
return renderWiringExplain(explainWiring(cwd, rule), wantJson);
|
|
586
674
|
}
|
|
@@ -637,6 +725,10 @@ async function checkWiring(args) {
|
|
|
637
725
|
// blast radius, not just the rules whose literal files were edited (a25 §3.2).
|
|
638
726
|
const selectedRuleIds = report.rules.map((r) => r.ruleId);
|
|
639
727
|
const scoped = changedOnly || since !== undefined;
|
|
728
|
+
// ── --fix: deterministic, heavily-guarded autofix ─────────────────────
|
|
729
|
+
if (flagBool(args, 'fix')) {
|
|
730
|
+
return runWiringFix(cwd, rules, report, flagBool(args, 'write'), wantJson);
|
|
731
|
+
}
|
|
640
732
|
if (wantJson) {
|
|
641
733
|
// Carry the honest counts so a machine consumer can tell "0 evaluated" from
|
|
642
734
|
// a real green, and see how many rules the scope skipped + which fired.
|
|
@@ -650,15 +742,31 @@ async function checkWiring(args) {
|
|
|
650
742
|
notVerified,
|
|
651
743
|
selectedRuleIds,
|
|
652
744
|
// Distinguish verified-pass / failure / not-verified for a chained gate.
|
|
653
|
-
exitCode:
|
|
745
|
+
exitCode: wiringExitCode(report, evaluated),
|
|
746
|
+
// One shape across every plane — see docs/gate-json.md.
|
|
747
|
+
gate: buildGateEnvelope('check wiring', wiringExitCode(report, evaluated), report.rules.map((r) => {
|
|
748
|
+
const skip = report.skipped.find((s) => s.ruleId === r.ruleId);
|
|
749
|
+
return {
|
|
750
|
+
id: r.ruleId,
|
|
751
|
+
type: 'wiring',
|
|
752
|
+
status: r.status,
|
|
753
|
+
severity: r.severity,
|
|
754
|
+
counts: { declared: r.declaredCount, registered: r.registeredCount },
|
|
755
|
+
violations: r.violations.map((v) => ({
|
|
756
|
+
id: v.token,
|
|
757
|
+
file: v.file,
|
|
758
|
+
line: v.line,
|
|
759
|
+
...(v.message ? { message: v.message } : {}),
|
|
760
|
+
...(v.hint ? { hint: v.hint } : {}),
|
|
761
|
+
})),
|
|
762
|
+
...(skip ? { skipReason: skip.reason } : {}),
|
|
763
|
+
...(r.error ? { error: r.error } : {}),
|
|
764
|
+
};
|
|
765
|
+
})),
|
|
654
766
|
}) + '\n');
|
|
655
767
|
// `evaluated === 0` = nothing checked in scope → NOT verified (`2`), never a
|
|
656
768
|
// 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;
|
|
769
|
+
return wiringExitCode(report, evaluated);
|
|
662
770
|
}
|
|
663
771
|
process.stdout.write(header('Wiring check'));
|
|
664
772
|
// `evaluated` counts rules that actually ran a comparison (globs matched >0
|
|
@@ -686,7 +794,32 @@ async function checkWiring(args) {
|
|
|
686
794
|
for (const d of report.diagnostics)
|
|
687
795
|
process.stdout.write(` ! ${d}\n`);
|
|
688
796
|
}
|
|
797
|
+
// Rules that checked NOTHING, reported per-rule. `matchedNothing` gives the
|
|
798
|
+
// count; this names them, because "which rule went stale" is the actionable
|
|
799
|
+
// half. A `failOnEmpty` rule turns its own skip into a hard failure.
|
|
800
|
+
if (report.skipped.length > 0) {
|
|
801
|
+
process.stdout.write('\nRules that checked nothing:\n');
|
|
802
|
+
for (const sk of report.skipped) {
|
|
803
|
+
process.stdout.write(` ${sk.failed ? '✗' : '–'} ${sk.ruleId} ${sk.failed ? 'FAILED' : 'SKIPPED'} — ${sk.reason}\n`);
|
|
804
|
+
}
|
|
805
|
+
if (report.skipped.some((sk) => !sk.failed)) {
|
|
806
|
+
process.stdout.write(' Fix the selector, or set `failOnEmpty: true` once the rule is known to have\n' +
|
|
807
|
+
' real subjects. Run `shrk gates coverage` to audit every plane at once.\n');
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
// A sink that extracted 0 ids while the source had some is a real failure, but
|
|
811
|
+
// almost always a stale SINK glob — say so instead of listing N "unwired"
|
|
812
|
+
// tokens with no explanation.
|
|
813
|
+
for (const r of report.rules) {
|
|
814
|
+
if (!r.emptySink)
|
|
815
|
+
continue;
|
|
816
|
+
process.stdout.write(`\n ! ${r.ruleId}: the registered side extracted 0 ids while ${r.declaredCount} were declared —\n` +
|
|
817
|
+
` every declared token "fails". Check the registered glob before chasing the tokens.\n`);
|
|
818
|
+
}
|
|
689
819
|
if (report.violations.length === 0 && report.diagnostics.length === 0) {
|
|
820
|
+
// A failOnEmpty skip produces no violation object but IS a failure.
|
|
821
|
+
if (report.verdict === 'errors')
|
|
822
|
+
return ExitCode.Failure;
|
|
690
823
|
if (allEvaluated) {
|
|
691
824
|
// Every configured rule ran — the earned full green.
|
|
692
825
|
process.stdout.write('\nNo wiring violations — every declared token is registered. ✓\n');
|
|
@@ -697,10 +830,10 @@ async function checkWiring(args) {
|
|
|
697
830
|
process.stdout.write(`\nNo wiring violations among the ${evaluated} rule(s) evaluated — ` +
|
|
698
831
|
`${notVerified} of ${configured} NOT verified${breakdown}. Not a full green.\n`);
|
|
699
832
|
}
|
|
700
|
-
return
|
|
833
|
+
return wiringExitCode(report, evaluated);
|
|
701
834
|
}
|
|
702
835
|
if (report.violations.length === 0) {
|
|
703
|
-
return report
|
|
836
|
+
return wiringExitCode(report, evaluated);
|
|
704
837
|
}
|
|
705
838
|
// Group by rule for a readable report.
|
|
706
839
|
for (const r of report.rules) {
|
|
@@ -709,7 +842,9 @@ async function checkWiring(args) {
|
|
|
709
842
|
process.stdout.write(`\n[${r.severity}] ${r.ruleId}${r.description ? ' — ' + r.description : ''}\n`);
|
|
710
843
|
process.stdout.write(` declared ${r.declaredCount} / registered ${r.registeredCount} — ${r.violations.length} not wired:\n`);
|
|
711
844
|
for (const v of r.violations.slice(0, 50)) {
|
|
712
|
-
|
|
845
|
+
const where = `(${v.file}:${v.line})`;
|
|
846
|
+
const hop = v.hop !== undefined ? ` [hop ${v.hop}]` : '';
|
|
847
|
+
process.stdout.write(v.message ? ` • ${v.message} ${where}${hop}\n` : ` • ${v.token} ${where}${hop}\n`);
|
|
713
848
|
}
|
|
714
849
|
if (r.violations.length > 50) {
|
|
715
850
|
process.stdout.write(` … (${r.violations.length - 50} more)\n`);
|
|
@@ -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":"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,EAw/GzD,CAAC;AAEH,4DAA4D;AAC5D,wBAAgB,yBAAyB,IAAI,MAAM,EAAE,CAWpD;AA0DD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,GAAG,QAAQ,CAAC;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAgGjE,CAAC;AAEH,iDAAiD;AACjD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAExE;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,oBAAoB,GAAG,cAAc,CAItE;AAED,+DAA+D;AAC/D,wBAAgB,eAAe,CAAC,CAAC,EAAE,oBAAoB,GAAG,SAAS,eAAe,EAAE,CAKnF;AAED,sDAAsD;AACtD,wBAAgB,eAAe,CAAC,CAAC,EAAE,oBAAoB,GAAG,eAAe,GAAG,SAAS,CAEpF;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,oBAAoB,GAAG,gBAAgB,CAQ1E;AAwFD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAclE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,IAAI,SAAS,oBAAoB,EAAE,CAgBnE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAwC9D;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,wBAAwB,IAAI,SAAS,uBAAuB,EAAE,CAsB7E;AAED,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,SAAS,uBAAuB,EAAE,GACvC,MAAM,CAaR;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,GAAG,CAAC,MAAM,CAAC,CAW/C"}
|
|
@@ -406,6 +406,126 @@ 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
|
+
}),
|
|
417
|
+
entry({
|
|
418
|
+
command: 'policy-lint explain',
|
|
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]',
|
|
420
|
+
category: 'core',
|
|
421
|
+
safetyLevel: SafetyLevel.ReadOnly,
|
|
422
|
+
surface: CommandSurface.Advanced,
|
|
423
|
+
taskRole: CommandTaskRole.Explain,
|
|
424
|
+
}),
|
|
425
|
+
entry({
|
|
426
|
+
command: 'registry duplicates',
|
|
427
|
+
description: 'Ids declared more than once across roots in a config-defined registry — a latent bug the compiler cannot see (whichever registration wins at runtime is an accident of load order). Every declaration site is reported. Exit 1 on duplicates, 2 when the registry matched 0 ids. [--json]',
|
|
428
|
+
category: 'core',
|
|
429
|
+
safetyLevel: SafetyLevel.ReadOnly,
|
|
430
|
+
surface: CommandSurface.Common,
|
|
431
|
+
taskRole: CommandTaskRole.Validate,
|
|
432
|
+
}),
|
|
433
|
+
entry({
|
|
434
|
+
command: 'gates list',
|
|
435
|
+
description: 'Every data-defined rule across every plane (wiring / policy / registry / registration / baseline / generated) with its severity and empty-match policy. [--plane <p>] [--json]',
|
|
436
|
+
category: 'core',
|
|
437
|
+
safetyLevel: SafetyLevel.ReadOnly,
|
|
438
|
+
surface: CommandSurface.Common,
|
|
439
|
+
taskRole: CommandTaskRole.Inspect,
|
|
440
|
+
}),
|
|
441
|
+
entry({
|
|
442
|
+
command: 'gates coverage',
|
|
443
|
+
description: 'The stale-selector detector: what every data-defined rule actually MATCHED against the live tree, flagging each rule that matched 0 files/ids (a rule matching nothing is a bug in the rule, never a pass) and running each rule\'s declared selfTest expectations. Run it in CI so a rule quietly dying is itself a failure. Exit 2 when a rule matched nothing, 1 when it set failOnEmpty. [--plane <p>] [--json]',
|
|
444
|
+
category: 'core',
|
|
445
|
+
safetyLevel: SafetyLevel.ReadOnly,
|
|
446
|
+
surface: CommandSurface.Common,
|
|
447
|
+
taskRole: CommandTaskRole.Validate,
|
|
448
|
+
}),
|
|
449
|
+
entry({
|
|
450
|
+
command: 'gates explain',
|
|
451
|
+
description: 'Universal rule introspection: for a rule of ANY plane, print the concrete inputs it resolved — files matched, ids extracted with file:line, and the computed diff. Dispatches to the plane-specific explainer. [--plane <p>] [--json]',
|
|
452
|
+
category: 'core',
|
|
453
|
+
safetyLevel: SafetyLevel.ReadOnly,
|
|
454
|
+
surface: CommandSurface.Advanced,
|
|
455
|
+
taskRole: CommandTaskRole.Explain,
|
|
456
|
+
}),
|
|
457
|
+
entry({
|
|
458
|
+
command: 'baseline list',
|
|
459
|
+
description: 'Every declared baseline: what it pins, how it recomputes, and which direction of drift fails. [--json]',
|
|
460
|
+
category: 'core',
|
|
461
|
+
safetyLevel: SafetyLevel.ReadOnly,
|
|
462
|
+
surface: CommandSurface.Common,
|
|
463
|
+
taskRole: CommandTaskRole.Inspect,
|
|
464
|
+
}),
|
|
465
|
+
entry({
|
|
466
|
+
command: 'baseline check',
|
|
467
|
+
description: 'Recompute every committed baseline (adoption ledger / API digest / coverage ratchet / allow-list) and fail on drift. TWO-WAY by default: a silently LOST entry fails exactly like a gained one. Rules from sharkcraft.config.ts baselines[]. Runs the declared compute command for `kind: "command"` rules. [--id X] [--changed-only] [--json]',
|
|
468
|
+
category: 'core',
|
|
469
|
+
safetyLevel: SafetyLevel.RunsShell,
|
|
470
|
+
surface: CommandSurface.Common,
|
|
471
|
+
taskRole: CommandTaskRole.Validate,
|
|
472
|
+
}),
|
|
473
|
+
entry({
|
|
474
|
+
command: 'baseline diff',
|
|
475
|
+
description: 'Human-readable +added / −removed for each baseline, without a verdict — the inspection verb. [--id X] [--json]',
|
|
476
|
+
category: 'core',
|
|
477
|
+
safetyLevel: SafetyLevel.RunsShell,
|
|
478
|
+
surface: CommandSurface.Advanced,
|
|
479
|
+
taskRole: CommandTaskRole.Explain,
|
|
480
|
+
}),
|
|
481
|
+
entry({
|
|
482
|
+
command: 'baseline update',
|
|
483
|
+
description: 'Rewrite a committed baseline from the current value — the explicit, reviewable bless step (deliberately a separate verb from `check` so a drift can never be blessed by accident). Writes files. [--id X] [--dry-run] [--json]',
|
|
484
|
+
category: 'core',
|
|
485
|
+
safetyLevel: SafetyLevel.WritesSource,
|
|
486
|
+
surface: CommandSurface.Common,
|
|
487
|
+
taskRole: CommandTaskRole.Apply,
|
|
488
|
+
}),
|
|
489
|
+
entry({
|
|
490
|
+
command: 'baseline explain',
|
|
491
|
+
description: 'What ONE baseline computes and compares — the command or extractor, the canonical form, both entry counts, and the diff — without turning it into a verdict. [--id X] [--json]',
|
|
492
|
+
category: 'core',
|
|
493
|
+
safetyLevel: SafetyLevel.RunsShell,
|
|
494
|
+
surface: CommandSurface.Advanced,
|
|
495
|
+
taskRole: CommandTaskRole.Explain,
|
|
496
|
+
}),
|
|
497
|
+
entry({
|
|
498
|
+
command: 'generated list',
|
|
499
|
+
description: 'Every declared generated-artifact rule: its globs, regen command, and "do not edit" header contract. [--json]',
|
|
500
|
+
category: 'core',
|
|
501
|
+
safetyLevel: SafetyLevel.ReadOnly,
|
|
502
|
+
surface: CommandSurface.Common,
|
|
503
|
+
taskRole: CommandTaskRole.Inspect,
|
|
504
|
+
}),
|
|
505
|
+
entry({
|
|
506
|
+
command: 'generated check',
|
|
507
|
+
description: 'Regenerate into a temp dir and diff BOTH ways — catching a hand-edited generated file AND a regen that writes a subset — plus the provenance-header contract (missing header on a generated file, header on a hand-written one). `--headers-only` never spawns. Rules from sharkcraft.config.ts generatedArtifacts[]. [--id X] [--headers-only] [--json]',
|
|
508
|
+
category: 'core',
|
|
509
|
+
safetyLevel: SafetyLevel.RunsShell,
|
|
510
|
+
surface: CommandSurface.Common,
|
|
511
|
+
taskRole: CommandTaskRole.Validate,
|
|
512
|
+
}),
|
|
513
|
+
entry({
|
|
514
|
+
command: 'generated update',
|
|
515
|
+
description: 'Run the declared regen command in place — the one-command bless step after an intentional source change. Writes files. [--id X] [--json]',
|
|
516
|
+
category: 'core',
|
|
517
|
+
safetyLevel: SafetyLevel.WritesSource,
|
|
518
|
+
surface: CommandSurface.Common,
|
|
519
|
+
taskRole: CommandTaskRole.Apply,
|
|
520
|
+
}),
|
|
521
|
+
entry({
|
|
522
|
+
command: 'generated explain',
|
|
523
|
+
description: 'What ONE generated-artifact rule sees right now: files matched, header-contract results, mislabel candidates — without running the regen. [--id X] [--json]',
|
|
524
|
+
category: 'core',
|
|
525
|
+
safetyLevel: SafetyLevel.ReadOnly,
|
|
526
|
+
surface: CommandSurface.Advanced,
|
|
527
|
+
taskRole: CommandTaskRole.Explain,
|
|
528
|
+
}),
|
|
409
529
|
entry({
|
|
410
530
|
command: 'wiring explain',
|
|
411
531
|
description: 'Dry-run ONE configured wiringRule and print the declared set + registered set it extracts (token + file:line), the alias-resolved set-difference, and the verdict — the author-loop view of what `check wiring` sees, without re-running the gate. [--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 });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ICommandHandler, type ParsedArgs } from '../command-registry.js';
|
|
2
|
+
export declare const gatesListCommand: ICommandHandler;
|
|
3
|
+
export declare const gatesCoverageCommand: ICommandHandler;
|
|
4
|
+
export declare const gatesExplainCommand: ICommandHandler;
|
|
5
|
+
/**
|
|
6
|
+
* Try to explain `id` as a data-defined rule on ANY plane.
|
|
7
|
+
*
|
|
8
|
+
* Returns the exit code when the id resolves to exactly one declared rule, or
|
|
9
|
+
* `undefined` when it is not a rule id at all — which lets `shrk explain` keep
|
|
10
|
+
* its original topic-search behaviour for everything else. This is the D2
|
|
11
|
+
* unification: a user holding a rule id no longer has to know which plane owns
|
|
12
|
+
* it, and no existing invocation changes meaning.
|
|
13
|
+
*/
|
|
14
|
+
export declare function tryExplainGateRule(args: ParsedArgs, id: string): Promise<number | undefined>;
|
|
15
|
+
export declare const gatesCommand: ICommandHandler;
|
|
16
|
+
//# sourceMappingURL=gates.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gates.command.d.ts","sourceRoot":"","sources":["../../src/commands/gates.command.ts"],"names":[],"mappings":"AAyBA,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,wBAAwB,CAAC;AAgFhC,eAAO,MAAM,gBAAgB,EAAE,eAsD9B,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eAuGlC,CAAC;AAuCF,eAAO,MAAM,mBAAmB,EAAE,eA4FjC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,UAAU,EAChB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAQ7B;AAED,eAAO,MAAM,YAAY,EAAE,eAe1B,CAAC"}
|