@shrkcrft/inspector 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.
Files changed (36) hide show
  1. package/dist/code-intelligence-doctor.d.ts +34 -0
  2. package/dist/code-intelligence-doctor.d.ts.map +1 -1
  3. package/dist/code-intelligence-doctor.js +68 -9
  4. package/dist/doc-references.d.ts +59 -0
  5. package/dist/doc-references.d.ts.map +1 -0
  6. package/dist/doc-references.js +190 -0
  7. package/dist/fuzzy-impact.d.ts.map +1 -1
  8. package/dist/fuzzy-impact.js +11 -20
  9. package/dist/index.d.ts +4 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +4 -0
  12. package/dist/knowledge-stale.d.ts.map +1 -1
  13. package/dist/knowledge-stale.js +18 -37
  14. package/dist/nearest-id.d.ts +30 -0
  15. package/dist/nearest-id.d.ts.map +1 -0
  16. package/dist/nearest-id.js +62 -0
  17. package/dist/policy-registry.d.ts +15 -0
  18. package/dist/policy-registry.d.ts.map +1 -0
  19. package/dist/policy-registry.js +64 -0
  20. package/dist/query-resolver.d.ts.map +1 -1
  21. package/dist/query-resolver.js +8 -8
  22. package/dist/reference-registry.d.ts +112 -0
  23. package/dist/reference-registry.d.ts.map +1 -0
  24. package/dist/reference-registry.js +239 -0
  25. package/dist/resolve-project-config.d.ts.map +1 -1
  26. package/dist/resolve-project-config.js +24 -6
  27. package/dist/self-config-doctor-v2.d.ts.map +1 -1
  28. package/dist/self-config-doctor-v2.js +37 -86
  29. package/dist/self-config-doctor.d.ts.map +1 -1
  30. package/dist/self-config-doctor.js +20 -37
  31. package/dist/sharkcraft-inspector.d.ts +13 -1
  32. package/dist/sharkcraft-inspector.d.ts.map +1 -1
  33. package/dist/sharkcraft-inspector.js +5 -3
  34. package/dist/test-runner.d.ts.map +1 -1
  35. package/dist/test-runner.js +10 -20
  36. package/package.json +17 -17
@@ -13,6 +13,7 @@
13
13
  import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
14
14
  import * as nodePath from 'node:path';
15
15
  import { HELPERS } from "./helper-registry.js";
16
+ import { referenceIdExists } from "./reference-registry.js";
16
17
  import { resolveSymbolInFile, SymbolResolution } from "./symbol-index.js";
17
18
  export const KNOWLEDGE_STALE_SCHEMA = 'sharkcraft.knowledge-stale/v1';
18
19
  export var ReferenceCheckOutcome;
@@ -64,59 +65,39 @@ function dirExists(projectRoot, rel) {
64
65
  return false;
65
66
  }
66
67
  }
68
+ /*
69
+ * Existence checks delegate to the SHARED reference registry.
70
+ *
71
+ * They used to be private copies here, each reading a differently-shaped
72
+ * structural cast off the inspection (`playbookRegistry`, `constructRegistry`,
73
+ * `policyChecks`) — properties nothing ever attached, so those kinds answered
74
+ * "does not exist" for every id including correct ones. An unchecked cast to a
75
+ * hoped-for shape compiles perfectly and fails silently forever; the registry
76
+ * imports the real accessors so the compiler can see a rename.
77
+ */
67
78
  function commandExistsInInspection(inspection, id) {
68
- // Both `shrk` commands and pack-contributed commands live on the
69
- // inspection. We do a permissive lookup against the recommendation
70
- // catalog if available.
71
- const commands = inspection.commandCatalog;
72
- if (Array.isArray(commands)) {
73
- if (commands.some((c) => c.id === id))
74
- return true;
75
- }
76
- // Fallback — accept anything that looks like a valid `shrk` command.
77
- return id.startsWith('shrk ') || id.startsWith('bun ');
79
+ return referenceIdExists(inspection, 'command', id);
78
80
  }
79
81
  function templateExists(inspection, id) {
80
- return inspection.templates.some((t) => t.id === id);
82
+ return referenceIdExists(inspection, 'template', id);
81
83
  }
82
84
  function playbookExists(inspection, id) {
83
- const inspectionAny = inspection;
84
- const reg = inspectionAny.playbookRegistry;
85
- if (reg && typeof reg.list === 'function') {
86
- return (reg.list() ?? []).some((p) => p.id === id);
87
- }
88
- return false;
85
+ return referenceIdExists(inspection, 'playbook', id);
89
86
  }
90
87
  function constructExists(inspection, id) {
91
- const inspectionAny = inspection;
92
- const reg = inspectionAny.constructRegistry;
93
- if (reg && typeof reg.list === 'function') {
94
- return (reg.list() ?? []).some((c) => c.id === id);
95
- }
96
- return false;
88
+ return referenceIdExists(inspection, 'construct', id);
97
89
  }
98
90
  function helperExists(id) {
99
91
  return HELPERS.some((h) => h.id === id);
100
92
  }
101
93
  function policyExists(inspection, id) {
102
- const inspectionAny = inspection;
103
- const checks = inspectionAny.policyChecks;
104
- if (Array.isArray(checks))
105
- return checks.some((c) => c.id === id);
106
- return false;
94
+ return referenceIdExists(inspection, 'policy', id);
107
95
  }
108
96
  function boundaryRuleExists(inspection, id) {
109
- const reg = inspection.boundaryRegistry;
110
- if (reg && typeof reg.list === 'function')
111
- return reg.list().some((b) => b.id === id);
112
- return false;
97
+ return referenceIdExists(inspection, 'boundary-rule', id);
113
98
  }
114
99
  function pathConventionExists(inspection, id) {
115
- const svc = inspection.pathService;
116
- if (svc && typeof svc.list === 'function') {
117
- return svc.list().some((p) => p.id === id);
118
- }
119
- return false;
100
+ return referenceIdExists(inspection, 'path-convention', id);
120
101
  }
121
102
  function packageExists(inspection, id) {
122
103
  // Project packages live under `packages/<name>` for SharkCraft + many
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Nearest-id "did you mean" over an arbitrary id set.
3
+ *
4
+ * The command suggester already had a Levenshtein and a fuzzy ranker, but both
5
+ * were shaped around a command CATALOG (command paths, aliases, descriptions).
6
+ * A stale `nge.angular-renderer` in a doc needs the same help against a flat
7
+ * list of template ids, so the distance function moves here and both consume
8
+ * it — a second implementation would drift, and then two surfaces would
9
+ * disagree about what "close" means.
10
+ *
11
+ * A suggestion is only offered when it is actually close. Printing the
12
+ * alphabetically-first id next to every typo trains people to ignore the line.
13
+ */
14
+ /** Levenshtein edit distance. Iterative, one row of state. */
15
+ export declare function levenshtein(a: string, b: string): number;
16
+ /** One candidate the query might have meant. */
17
+ export interface INearestId {
18
+ readonly id: string;
19
+ readonly distance: number;
20
+ }
21
+ /**
22
+ * The closest ids to `query`, nearest first.
23
+ *
24
+ * The cutoff scales with the query's length — one edit is a lot in `foo` and
25
+ * very little in `nge.angular-renderer` — and is capped so a long id cannot
26
+ * drag in something unrelated. Ties break lexically so the output is stable
27
+ * across runs, which matters when it lands in a committed diff.
28
+ */
29
+ export declare function nearestIds(query: string, candidates: readonly string[], limit?: number): readonly INearestId[];
30
+ //# sourceMappingURL=nearest-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nearest-id.d.ts","sourceRoot":"","sources":["../src/nearest-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,8DAA8D;AAC9D,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAiBxD;AAED,gDAAgD;AAChD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,KAAK,SAAI,GACR,SAAS,UAAU,EAAE,CAiBvB"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Nearest-id "did you mean" over an arbitrary id set.
3
+ *
4
+ * The command suggester already had a Levenshtein and a fuzzy ranker, but both
5
+ * were shaped around a command CATALOG (command paths, aliases, descriptions).
6
+ * A stale `nge.angular-renderer` in a doc needs the same help against a flat
7
+ * list of template ids, so the distance function moves here and both consume
8
+ * it — a second implementation would drift, and then two surfaces would
9
+ * disagree about what "close" means.
10
+ *
11
+ * A suggestion is only offered when it is actually close. Printing the
12
+ * alphabetically-first id next to every typo trains people to ignore the line.
13
+ */
14
+ /** Levenshtein edit distance. Iterative, one row of state. */
15
+ export function levenshtein(a, b) {
16
+ const m = a.length;
17
+ const n = b.length;
18
+ if (m === 0)
19
+ return n;
20
+ if (n === 0)
21
+ return m;
22
+ const dp = Array.from({ length: n + 1 }, (_, j) => j);
23
+ for (let i = 1; i <= m; i += 1) {
24
+ let prev = dp[0];
25
+ dp[0] = i;
26
+ for (let j = 1; j <= n; j += 1) {
27
+ const tmp = dp[j];
28
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
29
+ dp[j] = Math.min(dp[j] + 1, dp[j - 1] + 1, prev + cost);
30
+ prev = tmp;
31
+ }
32
+ }
33
+ return dp[n];
34
+ }
35
+ /**
36
+ * The closest ids to `query`, nearest first.
37
+ *
38
+ * The cutoff scales with the query's length — one edit is a lot in `foo` and
39
+ * very little in `nge.angular-renderer` — and is capped so a long id cannot
40
+ * drag in something unrelated. Ties break lexically so the output is stable
41
+ * across runs, which matters when it lands in a committed diff.
42
+ */
43
+ export function nearestIds(query, candidates, limit = 3) {
44
+ const q = query.toLowerCase();
45
+ const cutoff = Math.min(6, Math.max(2, Math.floor(q.length * 0.4)));
46
+ const scored = [];
47
+ for (const candidate of candidates) {
48
+ const c = candidate.toLowerCase();
49
+ if (c === q)
50
+ continue;
51
+ // A shared prefix or suffix is the common real-world shape (`nge.foo-view`
52
+ // vs `nge.foo-viewer`), and raw edit distance under-rates it.
53
+ const affinity = c.startsWith(q) || q.startsWith(c) || c.includes(q) || q.includes(c) ? 0 : 1;
54
+ const distance = affinity === 0 ? 0 : levenshtein(q, c);
55
+ if (distance > cutoff)
56
+ continue;
57
+ scored.push({ id: candidate, distance });
58
+ }
59
+ return scored
60
+ .sort((x, y) => x.distance - y.distance || x.id.localeCompare(y.id))
61
+ .slice(0, limit);
62
+ }
@@ -0,0 +1,15 @@
1
+ import type { ISharkcraftInspection } from './sharkcraft-inspector.js';
2
+ /** Load every declared policy id — local files first, then pack contributions. */
3
+ export declare function loadPolicyIds(inspection: ISharkcraftInspection): Promise<readonly string[]>;
4
+ /**
5
+ * The cached policy ids.
6
+ *
7
+ * Synchronous by design, so sync consumers (the reference resolver, the query
8
+ * ranker) can use it. Returns `[]` until {@link warmPolicyCache} has run — the
9
+ * reference registry's empty-kind guard is what stops that reading as "no such
10
+ * policy" for every id.
11
+ */
12
+ export declare function listPolicyIds(inspection: ISharkcraftInspection): readonly string[];
13
+ /** Pre-warm the cache so subsequent sync reads see a populated list. */
14
+ export declare function warmPolicyCache(inspection: ISharkcraftInspection): Promise<void>;
15
+ //# sourceMappingURL=policy-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-registry.d.ts","sourceRoot":"","sources":["../src/policy-registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAoDvE,kFAAkF;AAClF,wBAAsB,aAAa,CAAC,UAAU,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CA6BjG;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,qBAAqB,GAAG,SAAS,MAAM,EAAE,CAElF;AAED,wEAAwE;AACxE,wBAAsB,eAAe,CAAC,UAAU,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtF"}
@@ -0,0 +1,64 @@
1
+ import { existsSync } from 'node:fs';
2
+ import * as nodePath from 'node:path';
3
+ import { importModuleViaLoader } from '@shrkcrft/core';
4
+ const CACHE = new Map();
5
+ async function idsFrom(file, namespace) {
6
+ try {
7
+ const mod = (await importModuleViaLoader(file));
8
+ const decls = mod.default ?? mod.policyChecks ?? [];
9
+ return decls
10
+ .filter((d) => typeof d?.id === 'string')
11
+ .flatMap((d) => [d.id, namespace(d.id)]);
12
+ }
13
+ catch {
14
+ // A policy file that will not load is `evaluatePolicy`'s problem to report
15
+ // (it emits a load-failed check). Listing must not turn that into a crash.
16
+ return [];
17
+ }
18
+ }
19
+ /** Load every declared policy id — local files first, then pack contributions. */
20
+ export async function loadPolicyIds(inspection) {
21
+ const validPacks = inspection.packs?.validPacks ?? [];
22
+ const cacheKey = `${inspection.projectRoot}:${validPacks
23
+ .map((p) => p.packageName + '@' + p.packageVersion)
24
+ .join(',')}`;
25
+ const cached = CACHE.get(inspection.projectRoot);
26
+ if (cached && cached.cacheKey === cacheKey)
27
+ return cached.list;
28
+ const out = [];
29
+ const cfg = inspection.config;
30
+ for (const rel of cfg?.localPolicyFiles ?? ['sharkcraft/policies.ts']) {
31
+ const full = nodePath.isAbsolute(rel) ? rel : nodePath.join(inspection.projectRoot, rel);
32
+ if (!existsSync(full))
33
+ continue;
34
+ out.push(...(await idsFrom(full, (id) => `local:${id}`)));
35
+ }
36
+ for (const pack of validPacks) {
37
+ const contributions = pack.manifest?.contributions;
38
+ if (!pack.packageRoot)
39
+ continue;
40
+ for (const rel of contributions?.policyCheckFiles ?? []) {
41
+ const full = nodePath.resolve(pack.packageRoot, rel);
42
+ if (!existsSync(full))
43
+ continue;
44
+ out.push(...(await idsFrom(full, (id) => `pack:${pack.packageName}:${id}`)));
45
+ }
46
+ }
47
+ CACHE.set(inspection.projectRoot, { cacheKey, list: out });
48
+ return out;
49
+ }
50
+ /**
51
+ * The cached policy ids.
52
+ *
53
+ * Synchronous by design, so sync consumers (the reference resolver, the query
54
+ * ranker) can use it. Returns `[]` until {@link warmPolicyCache} has run — the
55
+ * reference registry's empty-kind guard is what stops that reading as "no such
56
+ * policy" for every id.
57
+ */
58
+ export function listPolicyIds(inspection) {
59
+ return CACHE.get(inspection.projectRoot)?.list ?? [];
60
+ }
61
+ /** Pre-warm the cache so subsequent sync reads see a populated list. */
62
+ export async function warmPolicyCache(inspection) {
63
+ await loadPolicyIds(inspection);
64
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"query-resolver.d.ts","sourceRoot":"","sources":["../src/query-resolver.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAGvE,eAAO,MAAM,uBAAuB,mCAAmC,CAAC;AAExE,oBAAY,cAAc;IACxB,IAAI,SAAS;IACb,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,SAAS,eAAe;IACxB,UAAU,gBAAgB;IAC1B,OAAO,aAAa;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,cAAc,CAAC;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,uBAAuB,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,YAAY,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACzC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;CAC7D;AAED,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,KAAK,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;CACvC;AAoID,wBAAgB,YAAY,CAC1B,UAAU,EAAE,qBAAqB,EACjC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,oBAAyB,GACjC,gBAAgB,CA8BlB"}
1
+ {"version":3,"file":"query-resolver.d.ts","sourceRoot":"","sources":["../src/query-resolver.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAMvE,eAAO,MAAM,uBAAuB,mCAAmC,CAAC;AAExE,oBAAY,cAAc;IACxB,IAAI,SAAS;IACb,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,SAAS,eAAe;IACxB,UAAU,gBAAgB;IAC1B,OAAO,aAAa;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,cAAc,CAAC;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,uBAAuB,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,YAAY,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACzC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;CAC7D;AAED,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,KAAK,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;CACvC;AAqID,wBAAgB,YAAY,CAC1B,UAAU,EAAE,qBAAqB,EACjC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,oBAAyB,GACjC,gBAAgB,CA8BlB"}
@@ -12,6 +12,9 @@
12
12
  import { existsSync } from 'node:fs';
13
13
  import * as nodePath from 'node:path';
14
14
  import { HELPERS } from "./helper-registry.js";
15
+ import { listConstructs } from "./construct-registry.js";
16
+ import { listPlaybooks } from "./playbook-registry.js";
17
+ import { listPolicyIds } from "./policy-registry.js";
15
18
  export const QUERY_RESOLUTION_SCHEMA = 'sharkcraft.query-resolution/v1';
16
19
  export var QueryMatchKind;
17
20
  (function (QueryMatchKind) {
@@ -103,9 +106,9 @@ function rankList(list, query, kind, reason = (t) => `${kind} id matched`) {
103
106
  return out;
104
107
  }
105
108
  function rankConstructs(inspection, query) {
106
- const reg = inspection.constructRegistry;
107
- const list = getList(reg, 'list');
108
- return rankList(list, query, QueryMatchKind.Construct);
109
+ // The registry accessor, not a structural cast at a property nothing sets —
110
+ // the latter compiles and then silently ranks nothing forever.
111
+ return rankList(listConstructs(inspection), query, QueryMatchKind.Construct);
109
112
  }
110
113
  function rankKnowledge(inspection, query) {
111
114
  return rankList(inspection.knowledgeEntries.map((e) => ({ id: e.id, title: e.title })), query, QueryMatchKind.Knowledge);
@@ -117,13 +120,10 @@ function rankHelpers(query) {
117
120
  return rankList(HELPERS, query, QueryMatchKind.Helper);
118
121
  }
119
122
  function rankPlaybooks(inspection, query) {
120
- const reg = inspection.playbookRegistry;
121
- const list = getList(reg, 'list');
122
- return rankList(list, query, QueryMatchKind.Playbook);
123
+ return rankList(listPlaybooks(inspection), query, QueryMatchKind.Playbook);
123
124
  }
124
125
  function rankPolicies(inspection, query) {
125
- const checks = inspection.policyChecks ?? [];
126
- return rankList(checks, query, QueryMatchKind.Policy);
126
+ return rankList(listPolicyIds(inspection).map((id) => ({ id })), query, QueryMatchKind.Policy);
127
127
  }
128
128
  function rankCommands(inspection, query) {
129
129
  const cat = inspection.commandCatalog ?? [];
@@ -0,0 +1,112 @@
1
+ import type { KnowledgeReferenceKind } from '@shrkcrft/knowledge';
2
+ import type { ISharkcraftInspection } from './sharkcraft-inspector.js';
3
+ /**
4
+ * The id sets a knowledge/doc reference can resolve against.
5
+ *
6
+ * `knowledge-stale` already answered "does this id exist?" per kind, one
7
+ * private `*Exists` helper at a time. The doc-reference linter needs the same
8
+ * answer AND the candidate LIST (to suggest what the author meant), so the
9
+ * lookup moves here and both consume it. Two implementations of "which
10
+ * template ids exist" would eventually disagree, and the one nobody runs would
11
+ * be the wrong one.
12
+ *
13
+ * Every list is read defensively off the inspection: these registries are
14
+ * populated by different layers and a repo may legitimately have none of a
15
+ * given kind, which must read as "no candidates", never as a crash.
16
+ */
17
+ /**
18
+ * Kinds a DOC reference may resolve against.
19
+ *
20
+ * A superset of `KnowledgeReferenceKind`: `pipeline` is a real registry that
21
+ * prose cites constantly (`the spine pipelines (engine.feature-dev, …)`) but
22
+ * that a knowledge entry has never had a structured reference kind for. The
23
+ * doc plane owns its own list rather than widening the knowledge contract for
24
+ * a need only this surface has.
25
+ */
26
+ export type DocReferenceKind = KnowledgeReferenceKind | 'pipeline';
27
+ /**
28
+ * EVERY kind this engine can answer "does this id exist?" for.
29
+ *
30
+ * A superset of `DocReferenceKind`: the self-config doctor cross-references
31
+ * kinds prose never cites (a decision record, a scaffold pattern, a routing
32
+ * hint). It used to answer for them from its OWN lookup sets, built from its
33
+ * own sources — so "one definition of does-this-id-exist" was a doc claim, not
34
+ * a property of the code, and the two answers agreed only by coincidence. They
35
+ * did not always: `sharkcraft.mcp-read-only` is a declared policy that the
36
+ * doctor's pack-only policy set reported as unknown.
37
+ *
38
+ * `docReferences[].resolvesAs` is unchanged — it still accepts exactly the
39
+ * `DocReferenceKind`s. Widening the RESOLVER is not widening the config.
40
+ */
41
+ export type ReferenceKind = DocReferenceKind | 'knowledge' | 'rule' | 'decision' | 'convention' | 'contract-template' | 'migration-profile' | 'routing-hint' | 'registration-hint' | 'scaffold-pattern';
42
+ /**
43
+ * Every kind that resolves against a registry of ids.
44
+ *
45
+ * `command` is deliberately absent: the catalog lives in the CLI package,
46
+ * ABOVE this layer, so it resolves by shape instead of by list. `file` /
47
+ * `directory` / `symbol` / `package` / `url` are not id registries at all.
48
+ */
49
+ export declare const ALL_ID_REFERENCE_KINDS: readonly ReferenceKind[];
50
+ /**
51
+ * Populate the snapshot the synchronous accessors read.
52
+ *
53
+ * Call this once, from the async layer that owns the inspection, before
54
+ * resolving anything. Without it a correct playbook — or convention, or
55
+ * scaffold pattern — resolves to nothing, which is exactly how a correct id got
56
+ * reported as an error twice.
57
+ */
58
+ export declare function warmReferenceRegistries(inspection: ISharkcraftInspection): Promise<void>;
59
+ /**
60
+ * Every registered id of `kind`, for existence checks and for suggesting what
61
+ * an unresolved token might have meant.
62
+ *
63
+ * **The invariant:** each kind reads the SAME source its `list` verb reads.
64
+ * `template` goes through `templateRegistry` because that is what `shrk
65
+ * templates list` prints — not the `templates` array it happens to be built
66
+ * from today, which would agree only until someone filters one of them.
67
+ */
68
+ export declare function referenceIdsFor(inspection: ISharkcraftInspection, kind: ReferenceKind): readonly string[];
69
+ /**
70
+ * Kinds among `kinds` whose registry is EMPTY in this repo.
71
+ *
72
+ * This is the generalisation of the bug that prompted it: resolving against an
73
+ * empty registry cannot succeed, so every id checked against it is reported
74
+ * wrong — a gate confidently flagging CORRECT usage, which is the fastest way
75
+ * to get a gate switched off. Emptiness is repo-dependent (a project may simply
76
+ * have no playbooks), so it cannot be a config-time error; the caller surfaces
77
+ * it as a loud refusal at run time instead.
78
+ */
79
+ export declare function emptyReferenceKinds(inspection: ISharkcraftInspection, kinds: readonly ReferenceKind[]): readonly ReferenceKind[];
80
+ /** True when `kind`'s ids come from an async-populated cache. */
81
+ export declare function isCacheBackedKind(kind: ReferenceKind): boolean;
82
+ /**
83
+ * Whether `id` is registered under `kind`.
84
+ *
85
+ * `command` keeps its historical permissiveness: the catalog is not always
86
+ * populated, and a repo citing `shrk gen …` should not be told its own CLI
87
+ * does not exist because an optional catalog was absent.
88
+ */
89
+ export declare function referenceIdExists(inspection: ISharkcraftInspection, kind: ReferenceKind, id: string): boolean;
90
+ /**
91
+ * The union of candidate ids across `kinds`, deduped and sorted.
92
+ *
93
+ * A doc token is checked against several registries at once (a `nge.foo` might
94
+ * be a template OR a playbook), so the suggester needs one pool rather than
95
+ * per-kind lists that would each propose their own nearest miss.
96
+ */
97
+ export declare function referenceIdPool(inspection: ISharkcraftInspection, kinds: readonly ReferenceKind[]): readonly string[];
98
+ /**
99
+ * Whether `id` is registered under ANY kind.
100
+ *
101
+ * For cross-references that do not name a kind — a search-tuning entry boosts
102
+ * "some id", a decision record relates to "some id". That union used to be
103
+ * hand-written as a chain of `lookups.x.has(id) || lookups.y.has(id) || …`,
104
+ * which is a list that silently goes stale: it omitted policies, decisions and
105
+ * scaffold patterns, so seven of shrk's own correctly-registered ids were
106
+ * reported unknown. Reading the kind list means adding a kind widens the union
107
+ * automatically.
108
+ */
109
+ export declare function referenceIdExistsInAnyKind(inspection: ISharkcraftInspection, id: string): boolean;
110
+ /** The kind that accepted `id`, or `undefined` — for "resolved as what?" output. */
111
+ export declare function referenceKindOf(inspection: ISharkcraftInspection, id: string): ReferenceKind | undefined;
112
+ //# sourceMappingURL=reference-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-registry.d.ts","sourceRoot":"","sources":["../src/reference-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAalE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEvE;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG,sBAAsB,GAAG,UAAU,CAAC;AAEnE;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,aAAa,GACrB,gBAAgB,GAChB,WAAW,GACX,MAAM,GACN,UAAU,GACV,YAAY,GACZ,mBAAmB,GACnB,mBAAmB,GACnB,cAAc,GACd,mBAAmB,GACnB,kBAAkB,CAAC;AAEvB;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,aAAa,EAkB1D,CAAC;AAiDF;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAAC,UAAU,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwC9F;AAMD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,qBAAqB,EACjC,IAAI,EAAE,aAAa,GAClB,SAAS,MAAM,EAAE,CA6CnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,qBAAqB,EACjC,KAAK,EAAE,SAAS,aAAa,EAAE,GAC9B,SAAS,aAAa,EAAE,CAI1B;AAED,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAE9D;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,qBAAqB,EACjC,IAAI,EAAE,aAAa,EACnB,EAAE,EAAE,MAAM,GACT,OAAO,CAOT;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,qBAAqB,EACjC,KAAK,EAAE,SAAS,aAAa,EAAE,GAC9B,SAAS,MAAM,EAAE,CAMnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,qBAAqB,EACjC,EAAE,EAAE,MAAM,GACT,OAAO,CAET;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAC7B,UAAU,EAAE,qBAAqB,EACjC,EAAE,EAAE,MAAM,GACT,aAAa,GAAG,SAAS,CAE3B"}