@shrkcrft/inspector 0.1.0-alpha.19 → 0.1.0-alpha.20

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.
@@ -1 +1 @@
1
- {"version":3,"file":"changes-summary.d.ts","sourceRoot":"","sources":["../src/changes-summary.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"changes-summary.d.ts","sourceRoot":"","sources":["../src/changes-summary.ts"],"names":[],"mappings":"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"}
@@ -90,6 +90,11 @@ function classifyArea(file) {
90
90
  if (/(?:^|\/)[\w-]*sharkcraft-pack\//.test(file)) {
91
91
  return ChangeArea.PackContrib;
92
92
  }
93
+ // Extension fallback so the summary stays useful in non-SharkCraft repos
94
+ // (e.g. a foreign monorepo's `architecture/*.md`) instead of bucketing every
95
+ // doc as `unknown`.
96
+ if (/\.mdx?$/i.test(file))
97
+ return ChangeArea.Docs;
93
98
  return ChangeArea.Unknown;
94
99
  }
95
100
  function classifyFile(file) {
@@ -229,7 +234,11 @@ export async function buildChangesSummary(inspection, options = {}) {
229
234
  ref = options.since;
230
235
  }
231
236
  else {
232
- changedFiles = getChangedFiles(cwd, {});
237
+ // Default working-tree view must include untracked files — otherwise a
238
+ // brand-new (never-staged) file is invisible to the summary, which made
239
+ // `totalFiles` undercount whole untracked directories. Mirrors every other
240
+ // working-tree caller (boundaries / propose-knowledge / architecture).
241
+ changedFiles = getChangedFiles(cwd, { includeWorktree: true });
233
242
  source = 'working-tree';
234
243
  }
235
244
  const classified = changedFiles.map(classifyFile);
@@ -39,10 +39,17 @@ export interface IContradictionReport {
39
39
  }
40
40
  export interface IBuildContradictionReportOptions {
41
41
  inspection: ISharkcraftInspection;
42
- /** Max doc files to scan. Default 80. */
42
+ /** Max doc files to scan. Default 5000 (effectively the whole docs tree). */
43
43
  docScanLimit?: number;
44
44
  /** Max bytes per doc. Default 200_000. */
45
45
  bytesPerDoc?: number;
46
+ /**
47
+ * Authoritative set of CLI command names (top-level verbs) used to flag
48
+ * `shrk <verb>` references to non-existent commands. Injected by the CLI from
49
+ * its own command catalogue (inspector must not import cli). When omitted,
50
+ * missing-command findings are suppressed — the safe failure mode.
51
+ */
52
+ cliCommandNames?: ReadonlySet<string>;
46
53
  }
47
54
  export declare function buildContradictionReport(options: IBuildContradictionReportOptions): IContradictionReport;
48
55
  export declare function renderContradictionReportText(report: IContradictionReport): string;
@@ -1 +1 @@
1
- {"version":3,"file":"contradictions.d.ts","sourceRoot":"","sources":["../src/contradictions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEvE,eAAO,MAAM,qBAAqB,iCAAiC,CAAC;AAEpE,oBAAY,iBAAiB;IAC3B,WAAW,iBAAiB;IAC5B,cAAc,oBAAoB;IAClC,SAAS,eAAe;IACxB,UAAU,iBAAiB;IAC3B,aAAa,mBAAmB;IAChC,mBAAmB,2BAA2B;IAC9C,wBAAwB,8BAA8B;CACvD;AAED,oBAAY,qBAAqB;IAC/B,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,IAAI,SAAS;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,qBAAqB,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,qBAAqB,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC3C,qBAAqB;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,gCAAgC;IAC/C,UAAU,EAAE,qBAAqB,CAAC;IAClC,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA2BD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,gCAAgC,GACxC,oBAAoB,CAqKtB;AA0GD,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAgClF;AAED,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CA2BtF;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAOlF;AAMD,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAElF"}
1
+ {"version":3,"file":"contradictions.d.ts","sourceRoot":"","sources":["../src/contradictions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEvE,eAAO,MAAM,qBAAqB,iCAAiC,CAAC;AAEpE,oBAAY,iBAAiB;IAC3B,WAAW,iBAAiB;IAC5B,cAAc,oBAAoB;IAClC,SAAS,eAAe;IACxB,UAAU,iBAAiB;IAC3B,aAAa,mBAAmB;IAChC,mBAAmB,2BAA2B;IAC9C,wBAAwB,8BAA8B;CACvD;AAED,oBAAY,qBAAqB;IAC/B,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,IAAI,SAAS;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,qBAAqB,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,qBAAqB,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC3C,qBAAqB;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,gCAAgC;IAC/C,UAAU,EAAE,qBAAqB,CAAC;IAClC,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACvC;AA8BD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,gCAAgC,GACxC,oBAAoB,CA8LtB;AAmFD,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAgClF;AAED,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CA2BtF;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAOlF;AAMD,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAElF"}
@@ -17,7 +17,10 @@ export var ContradictionSeverity;
17
17
  ContradictionSeverity["Warning"] = "warning";
18
18
  ContradictionSeverity["Info"] = "info";
19
19
  })(ContradictionSeverity || (ContradictionSeverity = {}));
20
- const DEFAULT_DOC_SCAN_LIMIT = 80;
20
+ // High ceiling, not a sampling cap: docs are byte-bounded already, so scanning
21
+ // the whole tree is cheap and avoids the silent "scanned an arbitrary 80-file
22
+ // prefix" failure mode.
23
+ const DEFAULT_DOC_SCAN_LIMIT = 5000;
21
24
  const DEFAULT_BYTES_PER_DOC = 200_000;
22
25
  const DOC_EXTS = new Set(['.md', '.mdx', '.rst', '.txt']);
23
26
  const DOC_DIR_NAMES = new Set(['docs', 'documentation', 'doc']);
@@ -50,7 +53,7 @@ export function buildContradictionReport(options) {
50
53
  const knownScripts = new Set();
51
54
  for (const script of Object.keys(inspection.workspace.scripts ?? {}))
52
55
  knownScripts.add(script);
53
- const cliCommandNames = collectShrkCommandNames();
56
+ const cliCommandNames = options.cliCommandNames ?? new Set();
54
57
  for (const doc of docs) {
55
58
  let body = '';
56
59
  try {
@@ -91,20 +94,30 @@ export function buildContradictionReport(options) {
91
94
  continue;
92
95
  if (cleaned.startsWith('http://') || cleaned.startsWith('https://'))
93
96
  continue;
94
- const probe = cleaned.startsWith('./') ? cleaned.slice(2) : cleaned;
95
- if (seenOnLine.has(probe))
97
+ // Strip a trailing source-location suffix (`:91`, `:91-125`, `#L91`,
98
+ // `#L91-L99`) — a `file.ts:91` reference is about file.ts, not a path
99
+ // literally named "file.ts:91".
100
+ const bare = (cleaned.startsWith('./') ? cleaned.slice(2) : cleaned).replace(/[:#]L?\d+(?:[-:]L?\d+)?$/i, '');
101
+ if (!bare)
96
102
  continue;
97
- seenOnLine.add(probe);
98
- const abs = nodePath.join(projectRoot, probe);
99
- if (!existsSync(abs)) {
103
+ if (seenOnLine.has(bare))
104
+ continue;
105
+ seenOnLine.add(bare);
106
+ // Resolve against the project root OR — for bare sibling links that
107
+ // aren't source-tree-prefixed (e.g. a `./a.md` link inside docs/) —
108
+ // relative to the referencing doc's directory.
109
+ const resolvesAtRoot = existsSync(nodePath.join(projectRoot, bare));
110
+ const resolvesDocRelative = !SOURCE_PREFIX_RE.test(bare) &&
111
+ existsSync(nodePath.resolve(nodePath.dirname(doc.abs), bare));
112
+ if (!resolvesAtRoot && !resolvesDocRelative) {
100
113
  findings.push({
101
- id: `missing-path:${doc.rel}:${i + 1}:${probe}`,
114
+ id: `missing-path:${doc.rel}:${i + 1}:${bare}`,
102
115
  kind: ContradictionKind.MissingPath,
103
116
  severity: ContradictionSeverity.Warning,
104
- message: `Doc references missing path \`${probe}\`.`,
117
+ message: `Doc references missing path \`${bare}\`.`,
105
118
  source: doc.rel,
106
119
  line: i + 1,
107
- reference: probe,
120
+ reference: bare,
108
121
  suggestion: 'Update the doc or restore the file.',
109
122
  reason: 'Doc-vs-tree consistency.',
110
123
  });
@@ -153,7 +166,16 @@ export function buildContradictionReport(options) {
153
166
  // npm/bun script references.
154
167
  if ((head === 'bun' || head === 'npm' || head === 'pnpm' || head === 'yarn') && (next === 'run' || next === 'x')) {
155
168
  const script = tokens[2];
156
- if (script && !script.startsWith('-') && next === 'run' && knownScripts.size > 0 && !knownScripts.has(script)) {
169
+ // A real `bun run <script>` target is a bare script id, never a path.
170
+ // Skip path-shaped candidates (`bun run ../scripts/x.ts`) to avoid the
171
+ // false positive of treating a script-path as a missing package script.
172
+ const scriptLooksLikePath = !!script && (script.includes('/') || script.startsWith('.') || script.includes('.'));
173
+ if (script &&
174
+ !script.startsWith('-') &&
175
+ !scriptLooksLikePath &&
176
+ next === 'run' &&
177
+ knownScripts.size > 0 &&
178
+ !knownScripts.has(script)) {
157
179
  findings.push({
158
180
  id: `missing-command:${doc.rel}:${i + 1}:${script}`,
159
181
  kind: ContradictionKind.MissingCommand,
@@ -264,6 +286,10 @@ function looksLikePath(s) {
264
286
  return false;
265
287
  if (s.startsWith('http'))
266
288
  return false;
289
+ if (/^[a-z][\w.+-]*:/i.test(s))
290
+ return false; // URI scheme (file:, mailto:, …), not a tree path
291
+ if (/\/:[A-Za-z]/.test(s))
292
+ return false; // URL route param (`/api/:id/…`)
267
293
  if (s.includes(' '))
268
294
  return false;
269
295
  if (/[<>{}]/.test(s))
@@ -288,30 +314,6 @@ function looksLikePath(s) {
288
314
  return false;
289
315
  return true;
290
316
  }
291
- function collectShrkCommandNames() {
292
- // Hard-coded list of well-known top-level commands. Importing the CLI here
293
- // would create a layering violation (inspector → cli) — instead we mirror
294
- // the catalogue that `shrk commands` would print. Missing entries simply
295
- // suppress contradiction findings for those commands, which is the safe
296
- // failure mode.
297
- return new Set([
298
- 'init', 'inspect', 'doctor', 'context', 'gen', 'apply', 'export', 'import',
299
- 'task', 'next', 'find', 'explain', 'check', 'watch', 'drift', 'graph',
300
- 'coverage', 'review', 'onboard', 'test', 'plan', 'session', 'dev', 'ask',
301
- 'mcp', 'version', 'quality', 'ci', 'commands', 'safety', 'infer', 'report',
302
- 'dashboard', 'bundle', 'impact', 'search', 'brief', 'demo', 'release',
303
- 'start-here', 'handoff', 'map', 'intent', 'orchestrate', 'simulate', 'view',
304
- 'recommend', 'risk', 'migration', 'contract', 'languages', 'help',
305
- 'memory', 'heal', 'agent', 'docs', 'examples', 'self', 'install',
306
- 'diagnostics', 'intelligence', 'architecture', 'decisions', 'compliance',
307
- 'policy', 'product', 'reposet', 'packs', 'upgrade', 'api', 'boundaries',
308
- 'repo', 'owners', 'ownership', 'runtime', 'constructs', 'playbooks',
309
- 'presets', 'pipelines', 'paths', 'rules', 'templates', 'knowledge',
310
- 'schemas', 'scaffolds', 'tests',
311
- // Additions
312
- 'ingest', 'understand-task', 'validate-change', 'contradictions', 'generated', 'stability',
313
- ]);
314
- }
315
317
  export function renderContradictionReportText(report) {
316
318
  const lines = [];
317
319
  lines.push('=== Contradictions report ===');
@@ -1 +1 @@
1
- {"version":3,"file":"git-helpers.d.ts","sourceRoot":"","sources":["../src/git-helpers.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB;AAWD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAI9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKrD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKvD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,MAAM,EAAE,CAkBpF;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAyC/D;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,EAAE,CA6BrF;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAKjD"}
1
+ {"version":3,"file":"git-helpers.d.ts","sourceRoot":"","sources":["../src/git-helpers.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB;AAWD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAI9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKrD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKvD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,MAAM,EAAE,CAwBpF;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAyC/D;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,EAAE,CA6BrF;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAKjD"}
@@ -41,12 +41,18 @@ export function getChangedFiles(cwd, opts = {}) {
41
41
  const set = new Set(parseLines(a.stdout));
42
42
  if (opts.includeWorktree && !opts.staged && !opts.since) {
43
43
  // Include untracked + working-tree changes via `git status --porcelain`.
44
- const s = runGit(cwd, ['status', '--porcelain']);
44
+ // `-uall` expands untracked directories to individual files (default
45
+ // `--porcelain` collapses them to a single `dir/` entry, which undercounts);
46
+ // `.gitignore` is still honored.
47
+ const s = runGit(cwd, ['status', '--porcelain', '-uall']);
45
48
  if (s.ok) {
46
49
  for (const line of s.stdout.split('\n')) {
47
- const trimmed = line.slice(3).trim();
48
- if (trimmed)
49
- set.add(trimmed);
50
+ // Strip the two-char XY status + leading space, then take the new path
51
+ // for rename/copy entries (`R old -> new`).
52
+ const raw = line.slice(3).trim();
53
+ const path = raw.includes(' -> ') ? raw.slice(raw.indexOf(' -> ') + 4).trim() : raw;
54
+ if (path)
55
+ set.add(path);
50
56
  }
51
57
  }
52
58
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plan-review.d.ts","sourceRoot":"","sources":["../src/plan-review.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,8FAA8F;IAC9F,IAAI,EACA,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,eAAe,GACf,SAAS,GACT,QAAQ,GACR,MAAM,GACN,UAAU,GACV,SAAS,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4FAA4F;IAC5F,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC7C,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,GAAG,eAAe,CAAC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,iBAAiB,EAAE,kBAAkB,GAAG,sCAAsC,CAAC;CAChF;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,6CAA6C;IAC7C,SAAS,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC1C,oDAAoD;IACpD,SAAS,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,iEAAiE;IACjE,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,8EAA8E;IAC9E,yBAAyB,EAAE,SAAS;QAClC,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,EAAE,CAAC;IACJ;;;;OAIG;IACH,8BAA8B,EAAE,SAAS,8BAA8B,EAAE,CAAC;IAC1E,4DAA4D;IAC5D,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,eAAe,CAC7B,UAAU,EAAE,qBAAqB,EACjC,QAAQ,EAAE,MAAM,GACf,iBAAiB,CAsLnB"}
1
+ {"version":3,"file":"plan-review.d.ts","sourceRoot":"","sources":["../src/plan-review.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,8FAA8F;IAC9F,IAAI,EACA,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,eAAe,GACf,SAAS,GACT,QAAQ,GACR,MAAM,GACN,UAAU,GACV,SAAS,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4FAA4F;IAC5F,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC7C,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,GAAG,eAAe,CAAC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,iBAAiB,EAAE,kBAAkB,GAAG,sCAAsC,CAAC;CAChF;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,6CAA6C;IAC7C,SAAS,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC1C,oDAAoD;IACpD,SAAS,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,iEAAiE;IACjE,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,8EAA8E;IAC9E,yBAAyB,EAAE,SAAS;QAClC,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,EAAE,CAAC;IACJ;;;;OAIG;IACH,8BAA8B,EAAE,SAAS,8BAA8B,EAAE,CAAC;IAC1E,4DAA4D;IAC5D,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,eAAe,CAC7B,UAAU,EAAE,qBAAqB,EACjC,QAAQ,EAAE,MAAM,GACf,iBAAiB,CAkLnB"}
@@ -1,6 +1,7 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { evaluateBoundaries, loadTsconfigPaths, scanImports, } from '@shrkcrft/boundaries';
3
3
  import { checkFolderOpSafety, FolderOpSafety, planGeneration, verifyPlan, } from '@shrkcrft/generator';
4
+ import { matchAffectedConventions } from '@shrkcrft/paths';
4
5
  export function reviewSavedPlan(inspection, planPath) {
5
6
  const raw = readFileSync(planPath, 'utf8');
6
7
  const plan = JSON.parse(raw);
@@ -64,16 +65,10 @@ export function reviewSavedPlan(inspection, planPath) {
64
65
  signatureMessage = 'verified';
65
66
  }
66
67
  }
67
- // Affected paths heuristic.
68
- const segments = new Set();
69
- for (const f of files) {
70
- for (const seg of f.relativePath.split('/'))
71
- segments.add(seg.toLowerCase());
72
- }
73
- const affectedPaths = inspection.pathService
74
- .list()
75
- .filter((p) => [...segments].some((s) => (p.title + ' ' + p.content).toLowerCase().includes(s)))
76
- .map((p) => p.id);
68
+ // Affected paths: conventions the plan's files structurally fall under
69
+ // (directory-prefix match against each convention's metadata.path), not a
70
+ // free-text substring of the description (which matched nearly the whole set).
71
+ const affectedPaths = matchAffectedConventions(inspection.pathService.list(), files.map((f) => f.relativePath)).map((p) => p.id);
77
72
  // Missing-tests heuristic.
78
73
  const missingTestsHeuristic = [];
79
74
  const allPaths = new Set(files.map((f) => f.relativePath));
@@ -36,6 +36,12 @@ export interface IRegistryLifecycleReport {
36
36
  registersFound: number;
37
37
  matchedPairs: ReadonlyArray<IRegistryPair>;
38
38
  missingRemovers: ReadonlyArray<IRegistryMissingRemover>;
39
+ /**
40
+ * `register*` declarations in a file with NO teardown-shaped API — treated as
41
+ * one-shot / bootstrap registrations that legitimately need no remover. Kept
42
+ * out of `missingRemovers` so the check isn't a wall of false positives.
43
+ */
44
+ oneShotBootstrap: ReadonlyArray<IRegistryIgnored>;
39
45
  ignored: ReadonlyArray<IRegistryIgnored>;
40
46
  recommendations: ReadonlyArray<string>;
41
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"registry-lifecycle.d.ts","sourceRoot":"","sources":["../src/registry-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,kCAAkC,CAAC;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC3C,eAAe,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;IACxD,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IACzC,eAAe,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACxC;AAyHD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,wBAAwB,CAoE3B;AAED,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,wBAAwB,GAAG,MAAM,CA6B1F"}
1
+ {"version":3,"file":"registry-lifecycle.d.ts","sourceRoot":"","sources":["../src/registry-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,kCAAkC,CAAC;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC3C,eAAe,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;IACxD;;;;OAIG;IACH,gBAAgB,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAClD,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IACzC,eAAe,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACxC;AAuLD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,wBAAwB,CAsF3B;AAED,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,wBAAwB,GAAG,MAAM,CA8B1F"}
@@ -59,11 +59,111 @@ function walk(dir, projectRoot, out) {
59
59
  const REGISTER_PATTERNS = Object.freeze([
60
60
  // export function registerX(
61
61
  /(?:export\s+)?(?:async\s+)?function\s+(register[A-Z]\w*)\s*\(/g,
62
- // public/private registerX(
63
- /(?:public|private|protected)?\s*(?:async\s+)?(register[A-Z]\w*)\s*\(/g,
64
- // someRegistry.registerX = (...) =>
65
- /\b(register[A-Z]\w*)\s*=\s*\(/g,
62
+ // class method DECLARATION: requires a body `{` or return-type `:` after the
63
+ // params, which a bare call site (`registry.registerX(...)`) never has.
64
+ /(?:^|\n)[\t ]*(?:public|private|protected)?\s*(?:static\s+)?(?:async\s+)?(register[A-Z]\w*)\s*\([^;]*?\)\s*[:{]/g,
65
+ // assigned arrow / function expression: `registerX = () =>` / `registerX = function`
66
+ /\b(register[A-Z]\w*)\s*=\s*(?:async\s+)?(?:function\b|\([^)]*\)\s*(?::[^=]*)?=>)/g,
66
67
  ]);
68
+ // Accumulation of removable per-entry state — a registry that ADDS entries one
69
+ // at a time plausibly needs a way to remove them.
70
+ const ACCUMULATION_RE = /\.(?:set|add|push)\s*\(|\[[^\]]+\]\s*=[^=]/;
71
+ // A teardown-shaped API somewhere in the file — evidence the file is in the
72
+ // business of removing things, so a missing per-register remover is suspicious.
73
+ const TEARDOWN_RE = /\b(?:remove|unregister|clear|dispose|unsubscribe)\w*\s*\(|\boff\s*\(/i;
74
+ /**
75
+ * Blank out comments and string / template-literal bodies (preserving newlines
76
+ * so line numbers stay accurate) before scanning for `register*` declarations.
77
+ * Deterministic char-scan — no TS parser, consistent with this module's no-AST
78
+ * posture. Stops `register*` mentions inside comments / strings / docs from
79
+ * being counted as code.
80
+ */
81
+ function stripCommentsAndLiterals(content) {
82
+ const out = [];
83
+ const n = content.length;
84
+ let state = 'code';
85
+ const blank = (ch) => (ch === '\n' ? '\n' : ' ');
86
+ let i = 0;
87
+ while (i < n) {
88
+ const ch = content[i];
89
+ const next = content[i + 1];
90
+ if (state === 'code') {
91
+ if (ch === '/' && next === '/') {
92
+ state = 'line';
93
+ out.push(' ');
94
+ i += 2;
95
+ continue;
96
+ }
97
+ if (ch === '/' && next === '*') {
98
+ state = 'block';
99
+ out.push(' ');
100
+ i += 2;
101
+ continue;
102
+ }
103
+ if (ch === "'") {
104
+ state = 'sq';
105
+ out.push(' ');
106
+ i += 1;
107
+ continue;
108
+ }
109
+ if (ch === '"') {
110
+ state = 'dq';
111
+ out.push(' ');
112
+ i += 1;
113
+ continue;
114
+ }
115
+ if (ch === '`') {
116
+ state = 'tpl';
117
+ out.push(' ');
118
+ i += 1;
119
+ continue;
120
+ }
121
+ out.push(ch);
122
+ i += 1;
123
+ continue;
124
+ }
125
+ if (state === 'line') {
126
+ if (ch === '\n') {
127
+ state = 'code';
128
+ out.push('\n');
129
+ i += 1;
130
+ continue;
131
+ }
132
+ out.push(blank(ch));
133
+ i += 1;
134
+ continue;
135
+ }
136
+ if (state === 'block') {
137
+ if (ch === '*' && next === '/') {
138
+ state = 'code';
139
+ out.push(' ');
140
+ i += 2;
141
+ continue;
142
+ }
143
+ out.push(blank(ch));
144
+ i += 1;
145
+ continue;
146
+ }
147
+ // string / template literal body
148
+ const quote = state === 'sq' ? "'" : state === 'dq' ? '"' : '`';
149
+ if (ch === '\\') {
150
+ out.push(' ');
151
+ out.push(next === undefined ? '' : blank(next));
152
+ i += 2;
153
+ continue;
154
+ }
155
+ if (ch === quote) {
156
+ state = 'code';
157
+ out.push(' ');
158
+ i += 1;
159
+ continue;
160
+ }
161
+ out.push(blank(ch));
162
+ i += 1;
163
+ continue;
164
+ }
165
+ return out.join('');
166
+ }
67
167
  function findRegistersInFile(content) {
68
168
  const out = [];
69
169
  const seen = new Set();
@@ -95,10 +195,17 @@ function findIgnoreAnnotations(content, registerName) {
95
195
  return { ignore: false };
96
196
  }
97
197
  function expectedRemoverNames(registerName) {
98
- // registerX → removeX / unregisterX / clearX
99
- // registerXByScope → removeXByScope / unregisterXByScope / clearXByScope
198
+ // registerX → removeX / unregisterX / clearX / disposeX / unsubscribeX / Xoff
199
+ // registerXByScope → removeXByScope / (stem carries the scope suffix)
100
200
  const stem = registerName.slice('register'.length);
101
- return [`remove${stem}`, `unregister${stem}`, `clear${stem}`];
201
+ return [
202
+ `remove${stem}`,
203
+ `unregister${stem}`,
204
+ `clear${stem}`,
205
+ `dispose${stem}`,
206
+ `unsubscribe${stem}`,
207
+ `${stem}Off`,
208
+ ];
102
209
  }
103
210
  function findRemoverInContent(content, candidates) {
104
211
  for (const name of candidates) {
@@ -119,6 +226,7 @@ export function buildRegistryLifecycleReport(input) {
119
226
  const scanFiles = files.slice(0, limit);
120
227
  const matchedPairs = [];
121
228
  const missingRemovers = [];
229
+ const oneShotBootstrap = [];
122
230
  const ignored = [];
123
231
  let registersFound = 0;
124
232
  for (const file of scanFiles) {
@@ -131,7 +239,15 @@ export function buildRegistryLifecycleReport(input) {
131
239
  }
132
240
  if (isGeneratedFile(file, content))
133
241
  continue;
134
- const registers = findRegistersInFile(content);
242
+ // Scan declarations on code with comments/strings blanked out; keep the raw
243
+ // content only for the comment-based @shrkcrft annotations.
244
+ const code = stripCommentsAndLiterals(content);
245
+ const registers = findRegistersInFile(code);
246
+ // Per-file lifecycle evidence: only a file that BOTH accumulates removable
247
+ // state AND has a teardown-shaped API plausibly owes a per-register remover.
248
+ const hasAccumulation = ACCUMULATION_RE.test(code);
249
+ const hasTeardown = TEARDOWN_RE.test(code);
250
+ const ownsLifecycle = hasAccumulation && hasTeardown;
135
251
  for (const reg of registers) {
136
252
  registersFound += 1;
137
253
  const ann = findIgnoreAnnotations(content, reg.name);
@@ -146,7 +262,7 @@ export function buildRegistryLifecycleReport(input) {
146
262
  continue;
147
263
  }
148
264
  const candidates = expectedRemoverNames(reg.name);
149
- const remover = findRemoverInContent(content, candidates);
265
+ const remover = findRemoverInContent(code, candidates);
150
266
  if (remover) {
151
267
  matchedPairs.push({
152
268
  registerName: reg.name,
@@ -156,7 +272,7 @@ export function buildRegistryLifecycleReport(input) {
156
272
  removerLine: remover.line,
157
273
  });
158
274
  }
159
- else {
275
+ else if (ownsLifecycle) {
160
276
  missingRemovers.push({
161
277
  registerName: reg.name,
162
278
  expectedRemoverNames: candidates,
@@ -165,6 +281,15 @@ export function buildRegistryLifecycleReport(input) {
165
281
  suggestion: `Add ${candidates[0]}() / ${candidates[1]}() / ${candidates[2]}() — or annotate with \`@shrkcrft lifecycle-ignore <reason>\` / \`@shrkcrft lifecycle-managed-by <name>\` if cleanup is owned elsewhere.`,
166
282
  });
167
283
  }
284
+ else {
285
+ // No teardown-shaped API in the file → one-shot / bootstrap registration.
286
+ oneShotBootstrap.push({
287
+ registerName: reg.name,
288
+ file: relative(projectRoot, file),
289
+ line: reg.line,
290
+ reason: 'no teardown-shaped API in file — treated as one-shot bootstrap',
291
+ });
292
+ }
168
293
  }
169
294
  }
170
295
  const recommendations = [];
@@ -177,6 +302,7 @@ export function buildRegistryLifecycleReport(input) {
177
302
  registersFound,
178
303
  matchedPairs,
179
304
  missingRemovers,
305
+ oneShotBootstrap,
180
306
  ignored,
181
307
  recommendations,
182
308
  };
@@ -188,6 +314,7 @@ export function renderRegistryLifecycleReportText(report) {
188
314
  lines.push(` registers found ${report.registersFound}`);
189
315
  lines.push(` matched pairs ${report.matchedPairs.length}`);
190
316
  lines.push(` missing removers ${report.missingRemovers.length}`);
317
+ lines.push(` one-shot bootstrap ${report.oneShotBootstrap.length}`);
191
318
  lines.push(` ignored ${report.ignored.length}`);
192
319
  lines.push('');
193
320
  if (report.missingRemovers.length > 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"review-packet.d.ts","sourceRoot":"","sources":["../src/review-packet.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAGvE,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,kEAAkE;IAClE,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,8CAA8C;IAC9C,aAAa,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxE,2CAA2C;IAC3C,iBAAiB,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3D,iBAAiB,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5D,2DAA2D;IAC3D,kBAAkB,EAAE,SAAS;QAC3B,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;KACjB,EAAE,CAAC;IACJ,+DAA+D;IAC/D,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,4DAA4D;IAC5D,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,mEAAmE;IACnE,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,yBAAyB;IACxC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AAyCD,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,qBAAqB,EACjC,OAAO,GAAE,yBAA8B,GACtC,aAAa,CAqFf"}
1
+ {"version":3,"file":"review-packet.d.ts","sourceRoot":"","sources":["../src/review-packet.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAIvE,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,kEAAkE;IAClE,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,8CAA8C;IAC9C,aAAa,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxE,2CAA2C;IAC3C,iBAAiB,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3D,iBAAiB,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5D,2DAA2D;IAC3D,kBAAkB,EAAE,SAAS;QAC3B,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;KACjB,EAAE,CAAC;IACJ,+DAA+D;IAC/D,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,4DAA4D;IAC5D,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,mEAAmE;IACnE,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,yBAAyB;IACxC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AAyCD,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,qBAAqB,EACjC,OAAO,GAAE,yBAA8B,GACtC,aAAa,CA+Ef"}
@@ -1,5 +1,7 @@
1
1
  import { spawnSync } from 'node:child_process';
2
2
  import { evaluateBoundaries, loadTsconfigPaths, scanImports } from '@shrkcrft/boundaries';
3
+ import { matchAffectedConventions } from '@shrkcrft/paths';
4
+ import { resolveVerificationCommands } from "./resolve-verification-commands.js";
3
5
  import { rankAll } from "./task-ranker.js";
4
6
  function gitDiffFiles(cwd, opts) {
5
7
  if (opts.files && opts.files.length > 0)
@@ -50,17 +52,10 @@ export function buildReviewPacket(inspection, options = {}) {
50
52
  const changedFiles = gitDiffFiles(inspection.projectRoot, options);
51
53
  const pseudoTask = buildPseudoTask(changedFiles);
52
54
  const ranking = rankAll(inspection, pseudoTask, 8);
53
- // Affected paths: any path-convention whose title or content mentions any
54
- // path segment from the changed files.
55
- const segments = new Set();
56
- for (const f of changedFiles) {
57
- for (const seg of f.split('/'))
58
- segments.add(seg.toLowerCase());
59
- }
60
- const affectedPaths = inspection.pathService
61
- .list()
62
- .filter((p) => [...segments].some((s) => (p.title + ' ' + p.content).toLowerCase().includes(s)))
63
- .map((p) => p.id);
55
+ // Affected paths: conventions the changed files structurally fall under
56
+ // (directory-prefix match against each convention's metadata.path), not a
57
+ // free-text substring of the description.
58
+ const affectedPaths = matchAffectedConventions(inspection.pathService.list(), changedFiles).map((p) => p.id);
64
59
  // Boundary violations restricted to changed files.
65
60
  let boundaryViolations = [];
66
61
  if (inspection.boundaryRegistry.size() > 0 && changedFiles.length > 0) {
@@ -81,12 +76,14 @@ export function buildReviewPacket(inspection, options = {}) {
81
76
  message: v.message,
82
77
  }));
83
78
  }
84
- const verificationCommands = unique([
85
- 'shrk doctor',
86
- 'shrk check boundaries',
87
- 'bun x tsc -p tsconfig.base.json --noEmit',
88
- 'bun test',
89
- ]);
79
+ // Verification: prefer the project's configured verificationCommands (resolved
80
+ // from config + matched pipelines); fall back to the generic tsc/test pair only
81
+ // when nothing is configured. SharkCraft's own meta-checks stay as a prefix.
82
+ const resolvedGates = resolveVerificationCommands(inspection, {
83
+ pipelineIds: ranking.pipelines.slice(0, 3).map((p) => p.item.id),
84
+ knowledgeDefaults: ['bun x tsc -p tsconfig.base.json --noEmit', 'bun test'],
85
+ });
86
+ const verificationCommands = unique(['shrk doctor', 'shrk check boundaries', ...resolvedGates]);
90
87
  const reviewerInstructions = [
91
88
  `# AI reviewer instructions`,
92
89
  ``,
package/dist/why-file.js CHANGED
@@ -23,7 +23,7 @@ export function buildWhyReport(input) {
23
23
  const inferredPackage = inferPackage(target.relativePath);
24
24
  const inferredLayer = inferLayer(target.relativePath);
25
25
  const pathConventions = matchPathConventions(input.inspection, target);
26
- const rules = matchRules(input.inspection, target, limit);
26
+ const rules = matchRules(input.inspection, target, limit, inferredPackage, inferredLayer);
27
27
  const boundaries = matchBoundaries(input.inspection, target);
28
28
  const knowledge = matchKnowledge(input.inspection, target, limit);
29
29
  const suggestedNext = buildSuggestions(target, rules, knowledge);
@@ -78,14 +78,20 @@ function inferLayer(relPath) {
78
78
  return segments[1];
79
79
  return undefined;
80
80
  }
81
+ /** Does `targetRel` equal, or live under, the canonical directory/file path? */
82
+ function pathCovers(targetRel, canonical) {
83
+ const normalized = canonical.replace(/^\.\//, '').replace(/\/+$/, '');
84
+ if (!normalized)
85
+ return false;
86
+ return targetRel === normalized || targetRel.startsWith(`${normalized}/`);
87
+ }
81
88
  function matchPathConventions(inspection, target) {
82
89
  const out = [];
83
90
  for (const p of inspection.pathService.list()) {
84
91
  const canonical = p.metadata?.path ?? '';
85
92
  if (!canonical)
86
93
  continue;
87
- const normalized = canonical.replace(/^\.\//, '');
88
- if (target.relativePath === normalized || target.relativePath.startsWith(normalized.endsWith('/') ? normalized : `${normalized}/`)) {
94
+ if (pathCovers(target.relativePath, canonical)) {
89
95
  out.push({
90
96
  id: p.id,
91
97
  title: p.title,
@@ -96,30 +102,74 @@ function matchPathConventions(inspection, target) {
96
102
  }
97
103
  return out;
98
104
  }
99
- function matchRules(inspection, target, limit) {
100
- const tokens = pathTokens(target.relativePath);
105
+ /**
106
+ * Precise PATH evidence that a rule applies to a file — not topical token
107
+ * overlap (scope/tags/appliesWhen are free-form labels, so the old
108
+ * intersection attached nearly every rule to every file). Priority order:
109
+ * explicit file/dir references → path-glob scope/tag → package reference →
110
+ * scope/tag equal to the inferred package or layer.
111
+ */
112
+ function ruleReasonForFile(r, target, inferredPackage, inferredLayer) {
113
+ const rel = target.relativePath;
114
+ // 1. Explicit file/directory references that cover the target.
115
+ for (const ref of r.references ?? []) {
116
+ const p = (ref.path ?? '').replace(/^\.\//, '');
117
+ if ((ref.kind === 'file' || ref.kind === 'directory') && p && pathCovers(rel, p)) {
118
+ return ref.kind === 'directory' ? `directory reference ${p}` : `file reference ${p}`;
119
+ }
120
+ }
121
+ // 1b. Anchors that point at a covering path.
122
+ for (const a of r.anchors ?? []) {
123
+ const p = (a.path ?? '').replace(/^\.\//, '');
124
+ if (p && pathCovers(rel, p))
125
+ return `anchor path ${p}`;
126
+ }
127
+ // 2. Scope/tag values that are themselves path globs matching the target.
128
+ const pathish = [...(r.scope ?? []), ...(r.tags ?? [])].filter((s) => /[/*?[]/.test(s));
129
+ if (pathish.length > 0) {
130
+ const hit = pathish.find((g) => matchesAny(rel, [g]));
131
+ if (hit)
132
+ return `path pattern ${hit}`;
133
+ }
134
+ // 3. Package reference / scope-or-tag equal to the inferred package or layer.
135
+ const labels = [...(r.scope ?? []), ...(r.tags ?? [])];
136
+ if (inferredPackage) {
137
+ for (const ref of r.references ?? []) {
138
+ if (ref.kind === 'package' && ref.id && samePath(ref.id, inferredPackage)) {
139
+ return `package reference ${ref.id}`;
140
+ }
141
+ }
142
+ if (labels.some((l) => samePath(l, inferredPackage)))
143
+ return `scoped to package ${inferredPackage}`;
144
+ }
145
+ if (inferredLayer && labels.some((l) => l.toLowerCase() === inferredLayer.toLowerCase())) {
146
+ return `scoped to layer ${inferredLayer}`;
147
+ }
148
+ return undefined;
149
+ }
150
+ function samePath(a, b) {
151
+ const n = (s) => s.replace(/^\.?\/+/, '').replace(/\/+$/, '').toLowerCase();
152
+ return n(a) === n(b);
153
+ }
154
+ function matchRules(inspection, target, limit, inferredPackage, inferredLayer) {
101
155
  const out = [];
102
156
  for (const rule of inspection.ruleService.list()) {
103
157
  const r = rule;
104
- const scope = r.scope ?? [];
105
- const tags = r.tags ?? [];
106
- const appliesWhen = r.appliesWhen ?? [];
107
- const allTokens = new Set([...scope, ...tags, ...appliesWhen].map((t) => t.toLowerCase()));
108
- const hits = tokens.filter((t) => allTokens.has(t.toLowerCase()));
109
- if (hits.length === 0)
158
+ const reason = ruleReasonForFile(r, target, inferredPackage, inferredLayer);
159
+ if (!reason)
110
160
  continue;
111
161
  out.push({
112
162
  id: r.id,
113
163
  title: r.title,
114
164
  priority: r.priority ?? 'medium',
115
- scope,
116
- tags,
117
- appliesWhen,
118
- reason: `Matches on: ${hits.join(', ')}`,
165
+ scope: r.scope ?? [],
166
+ tags: r.tags ?? [],
167
+ appliesWhen: r.appliesWhen ?? [],
168
+ reason,
119
169
  ...sourceField(inspection, r.id),
120
170
  });
121
171
  }
122
- // Sort by priority weight desc, then by hit count desc.
172
+ // Sort by priority weight desc (stable for equal weights).
123
173
  const weight = { critical: 4, high: 3, medium: 2, low: 1 };
124
174
  out.sort((a, b) => (weight[b.priority] ?? 2) - (weight[a.priority] ?? 2));
125
175
  return out.slice(0, limit);
@@ -183,12 +233,6 @@ function buildSuggestions(target, rules, knowledge) {
183
233
  out.push(`shrk impact "${target.relativePath}"`);
184
234
  return out;
185
235
  }
186
- function pathTokens(relPath) {
187
- return relPath
188
- .replace(/\.[^./]+$/, '') // drop extension
189
- .split(/[/\\.\-_]/)
190
- .filter((t) => t.length > 0);
191
- }
192
236
  function sourceField(inspection, id) {
193
237
  let map;
194
238
  if (inspection && 'entrySources' in inspection) {
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@shrkcrft/inspector",
3
- "version": "0.1.0-alpha.19",
3
+ "version": "0.1.0-alpha.20",
4
4
  "description": "SharkCraft inspector: project overview, doctor checks, AI-agent instructions.",
5
5
  "license": "MIT",
6
6
  "author": "SharkCraft contributors",
7
7
  "type": "module",
8
8
  "main": "./dist/index.js",
9
- "types": "./dist/index.d.d.ts",
9
+ "types": "./dist/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
12
  "types": "./dist/index.d.ts",
@@ -42,22 +42,22 @@
42
42
  "typecheck": "tsc --noEmit -p tsconfig.json"
43
43
  },
44
44
  "dependencies": {
45
- "@shrkcrft/core": "^0.1.0-alpha.19",
46
- "@shrkcrft/config": "^0.1.0-alpha.19",
47
- "@shrkcrft/workspace": "^0.1.0-alpha.19",
48
- "@shrkcrft/knowledge": "^0.1.0-alpha.19",
49
- "@shrkcrft/rules": "^0.1.0-alpha.19",
50
- "@shrkcrft/paths": "^0.1.0-alpha.19",
51
- "@shrkcrft/templates": "^0.1.0-alpha.19",
52
- "@shrkcrft/context": "^0.1.0-alpha.19",
53
- "@shrkcrft/pipelines": "^0.1.0-alpha.19",
54
- "@shrkcrft/packs": "^0.1.0-alpha.19",
55
- "@shrkcrft/presets": "^0.1.0-alpha.19",
56
- "@shrkcrft/boundaries": "^0.1.0-alpha.19",
57
- "@shrkcrft/generator": "^0.1.0-alpha.19",
58
- "@shrkcrft/importer": "^0.1.0-alpha.19",
59
- "@shrkcrft/plugin-api": "^0.1.0-alpha.19",
60
- "@shrkcrft/dashboard-api": "^0.1.0-alpha.19",
45
+ "@shrkcrft/core": "^0.1.0-alpha.20",
46
+ "@shrkcrft/config": "^0.1.0-alpha.20",
47
+ "@shrkcrft/workspace": "^0.1.0-alpha.20",
48
+ "@shrkcrft/knowledge": "^0.1.0-alpha.20",
49
+ "@shrkcrft/rules": "^0.1.0-alpha.20",
50
+ "@shrkcrft/paths": "^0.1.0-alpha.20",
51
+ "@shrkcrft/templates": "^0.1.0-alpha.20",
52
+ "@shrkcrft/context": "^0.1.0-alpha.20",
53
+ "@shrkcrft/pipelines": "^0.1.0-alpha.20",
54
+ "@shrkcrft/packs": "^0.1.0-alpha.20",
55
+ "@shrkcrft/presets": "^0.1.0-alpha.20",
56
+ "@shrkcrft/boundaries": "^0.1.0-alpha.20",
57
+ "@shrkcrft/generator": "^0.1.0-alpha.20",
58
+ "@shrkcrft/importer": "^0.1.0-alpha.20",
59
+ "@shrkcrft/plugin-api": "^0.1.0-alpha.20",
60
+ "@shrkcrft/dashboard-api": "^0.1.0-alpha.20",
61
61
  "typescript": "^5.6.0"
62
62
  },
63
63
  "publishConfig": {