@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.
- package/dist/code-intelligence-doctor.d.ts +34 -0
- package/dist/code-intelligence-doctor.d.ts.map +1 -1
- package/dist/code-intelligence-doctor.js +68 -9
- package/dist/doc-references.d.ts +59 -0
- package/dist/doc-references.d.ts.map +1 -0
- package/dist/doc-references.js +190 -0
- package/dist/fuzzy-impact.d.ts.map +1 -1
- package/dist/fuzzy-impact.js +11 -20
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/knowledge-stale.d.ts.map +1 -1
- package/dist/knowledge-stale.js +18 -37
- package/dist/nearest-id.d.ts +30 -0
- package/dist/nearest-id.d.ts.map +1 -0
- package/dist/nearest-id.js +62 -0
- package/dist/policy-registry.d.ts +15 -0
- package/dist/policy-registry.d.ts.map +1 -0
- package/dist/policy-registry.js +64 -0
- package/dist/query-resolver.d.ts.map +1 -1
- package/dist/query-resolver.js +8 -8
- package/dist/reference-registry.d.ts +112 -0
- package/dist/reference-registry.d.ts.map +1 -0
- package/dist/reference-registry.js +239 -0
- package/dist/resolve-project-config.d.ts.map +1 -1
- package/dist/resolve-project-config.js +24 -6
- package/dist/self-config-doctor-v2.d.ts.map +1 -1
- package/dist/self-config-doctor-v2.js +37 -86
- package/dist/self-config-doctor.d.ts.map +1 -1
- package/dist/self-config-doctor.js +20 -37
- package/dist/sharkcraft-inspector.d.ts +13 -1
- package/dist/sharkcraft-inspector.d.ts.map +1 -1
- package/dist/sharkcraft-inspector.js +5 -3
- package/dist/test-runner.d.ts.map +1 -1
- package/dist/test-runner.js +10 -20
- package/package.json +17 -17
package/dist/knowledge-stale.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
82
|
+
return referenceIdExists(inspection, 'template', id);
|
|
81
83
|
}
|
|
82
84
|
function playbookExists(inspection, id) {
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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"}
|
package/dist/query-resolver.js
CHANGED
|
@@ -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
|
-
|
|
107
|
-
|
|
108
|
-
return rankList(
|
|
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
|
-
|
|
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
|
-
|
|
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"}
|