@skill-map/cli 0.20.1 → 0.22.0
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/cli/tutorial/sm-tutorial.md +93 -14
- package/dist/cli.js +7660 -6354
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1244 -1065
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +300 -194
- package/dist/kernel/index.js +1244 -1065
- package/dist/kernel/index.js.map +1 -1
- package/dist/migrations/001_initial.sql +13 -0
- package/dist/ui/chunk-25AWRVIC.js +965 -0
- package/dist/ui/chunk-GETTEQ3S.js +123 -0
- package/dist/ui/{chunk-4NLC7QD2.js → chunk-GXRWH2VL.js} +1 -1
- package/dist/ui/chunk-HC6PNQMW.js +251 -0
- package/dist/ui/chunk-HJHWJTFH.js +1 -0
- package/dist/ui/chunk-MF2M6GYF.js +1 -0
- package/dist/ui/{chunk-EZZF5RL5.js → chunk-MPMBTIUR.js} +2 -2
- package/dist/ui/{chunk-6GUHSAP5.js → chunk-OPPQMCMQ.js} +1 -1
- package/dist/ui/chunk-V3SZQETX.js +61 -0
- package/dist/ui/{chunk-E4ALROJS.js → chunk-VVOEPDQD.js} +1 -1
- package/dist/ui/{chunk-6BZZQV42.js → chunk-W2EFGI3J.js} +1 -1
- package/dist/ui/index.html +2 -10
- package/dist/ui/main-Q2WC254P.js +2 -0
- package/dist/ui/media/fa-brands-400-AHOAZHCU.woff2 +0 -0
- package/dist/ui/media/fa-regular-400-VRZYIBIZ.woff2 +0 -0
- package/dist/ui/media/fa-solid-900-MDEYK55F.woff2 +0 -0
- package/dist/ui/media/fa-v4compatibility-ETEVP6IB.woff2 +0 -0
- package/dist/ui/styles-M2FETVAG.css +1 -0
- package/migrations/001_initial.sql +13 -0
- package/package.json +6 -5
- package/dist/ui/chunk-FWX4RRDF.js +0 -125
- package/dist/ui/chunk-GGMXMGRJ.js +0 -1
- package/dist/ui/chunk-K5PULFK7.js +0 -1
- package/dist/ui/chunk-OJ6W6OIB.js +0 -61
- package/dist/ui/chunk-PTCD42GB.js +0 -247
- package/dist/ui/chunk-ZSRIBCAW.js +0 -965
- package/dist/ui/main-5FJWWH5I.js +0 -1
- package/dist/ui/styles-VJ5Q6D2X.css +0 -1
package/dist/kernel/index.js
CHANGED
|
@@ -92,18 +92,15 @@ var Registry = class {
|
|
|
92
92
|
}
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
-
// kernel/orchestrator.ts
|
|
96
|
-
import {
|
|
97
|
-
import {
|
|
98
|
-
import { isAbsolute as isAbsolute2, resolve as resolvePath } from "path";
|
|
99
|
-
import { Tiktoken } from "js-tiktoken/lite";
|
|
95
|
+
// kernel/orchestrator/index.ts
|
|
96
|
+
import { existsSync as existsSync9, statSync as statSync2 } from "fs";
|
|
97
|
+
import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
|
|
100
98
|
import cl100k_base from "js-tiktoken/ranks/cl100k_base";
|
|
101
|
-
import yaml4 from "js-yaml";
|
|
102
99
|
|
|
103
100
|
// package.json
|
|
104
101
|
var package_default = {
|
|
105
102
|
name: "@skill-map/cli",
|
|
106
|
-
version: "0.
|
|
103
|
+
version: "0.22.0",
|
|
107
104
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
108
105
|
license: "MIT",
|
|
109
106
|
type: "module",
|
|
@@ -156,20 +153,21 @@ var package_default = {
|
|
|
156
153
|
"lint:fix": "eslint . --fix",
|
|
157
154
|
reference: "node scripts/build-reference.js",
|
|
158
155
|
"reference:check": "node scripts/build-reference.js --check",
|
|
159
|
-
validate: "npm run
|
|
156
|
+
validate: "npm run validate:compile && npm run validate:test",
|
|
157
|
+
"validate:compile": "npm run typecheck && npm run lint && npm run build && npm run reference:check",
|
|
158
|
+
"validate:test": "npm run test:ci",
|
|
160
159
|
pretest: "tsup",
|
|
161
|
-
"pretest:ci": "tsup",
|
|
162
160
|
"pretest:coverage": "tsup",
|
|
163
161
|
"pretest:coverage:html": "tsup",
|
|
164
162
|
test: "tsc --noEmit && node --import tsx --test --test-reporter=spec 'test/**/*.test.ts' 'built-in-plugins/**/*.test.ts' 'kernel/**/*.test.ts' 'server/**/*.test.ts'",
|
|
165
|
-
"test:ci": "
|
|
163
|
+
"test:ci": "node --import tsx --test 'test/**/*.test.ts' 'built-in-plugins/**/*.test.ts' 'kernel/**/*.test.ts' 'server/**/*.test.ts'",
|
|
166
164
|
"test:coverage": "tsc --noEmit && SKILL_MAP_SKIP_BENCHMARK=1 node --experimental-default-config-file --import tsx --test --experimental-test-coverage 'test/**/*.test.ts' 'built-in-plugins/**/*.test.ts' 'kernel/**/*.test.ts' 'server/**/*.test.ts'",
|
|
167
165
|
"test:coverage:html": "tsc --noEmit && SKILL_MAP_SKIP_BENCHMARK=1 c8 node --import tsx --test 'test/**/*.test.ts' 'built-in-plugins/**/*.test.ts' 'kernel/**/*.test.ts' 'server/**/*.test.ts'",
|
|
168
166
|
clean: "rm -rf dist coverage"
|
|
169
167
|
},
|
|
170
168
|
dependencies: {
|
|
171
169
|
"@hono/node-server": "2.0.1",
|
|
172
|
-
"@skill-map/spec": "0.
|
|
170
|
+
"@skill-map/spec": "0.22.0",
|
|
173
171
|
ajv: "8.18.0",
|
|
174
172
|
"ajv-formats": "3.0.1",
|
|
175
173
|
chokidar: "5.0.0",
|
|
@@ -206,174 +204,6 @@ var package_default = {
|
|
|
206
204
|
}
|
|
207
205
|
};
|
|
208
206
|
|
|
209
|
-
// kernel/sidecar/parse.ts
|
|
210
|
-
import { existsSync, readFileSync } from "fs";
|
|
211
|
-
import { dirname, resolve } from "path";
|
|
212
|
-
import { createRequire } from "module";
|
|
213
|
-
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
214
|
-
import yaml from "js-yaml";
|
|
215
|
-
|
|
216
|
-
// kernel/util/ajv-interop.ts
|
|
217
|
-
import addFormatsModule from "ajv-formats";
|
|
218
|
-
var addFormats = addFormatsModule.default ?? addFormatsModule;
|
|
219
|
-
function applyAjvFormats(ajv) {
|
|
220
|
-
addFormats(ajv);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// kernel/sidecar/parse.ts
|
|
224
|
-
function readSidecarFor(mdAbsolutePath) {
|
|
225
|
-
const sidecarPath = sidecarPathFor(mdAbsolutePath);
|
|
226
|
-
if (!existsSync(sidecarPath)) {
|
|
227
|
-
return { parsed: null, present: false, issues: [] };
|
|
228
|
-
}
|
|
229
|
-
let raw;
|
|
230
|
-
try {
|
|
231
|
-
raw = readFileSync(sidecarPath, "utf8");
|
|
232
|
-
} catch (err) {
|
|
233
|
-
return {
|
|
234
|
-
parsed: null,
|
|
235
|
-
present: true,
|
|
236
|
-
issues: [{ message: `cannot read ${sidecarPath}: ${err.message}` }]
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
let parsedYaml;
|
|
240
|
-
try {
|
|
241
|
-
parsedYaml = yaml.load(raw);
|
|
242
|
-
} catch (err) {
|
|
243
|
-
return {
|
|
244
|
-
parsed: null,
|
|
245
|
-
present: true,
|
|
246
|
-
issues: [{ message: `malformed YAML in ${sidecarPath}: ${err.message}` }]
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
if (!isPlainObject(parsedYaml)) {
|
|
250
|
-
return {
|
|
251
|
-
parsed: null,
|
|
252
|
-
present: true,
|
|
253
|
-
issues: [{ message: `sidecar root must be a YAML mapping at ${sidecarPath}` }]
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
const sidecarValidator = getSidecarValidator();
|
|
257
|
-
if (!sidecarValidator(parsedYaml)) {
|
|
258
|
-
const errors = (sidecarValidator.errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
|
|
259
|
-
return {
|
|
260
|
-
parsed: null,
|
|
261
|
-
present: true,
|
|
262
|
-
issues: [{ message: `sidecar schema validation failed at ${sidecarPath}: ${errors}` }]
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
const root = parsedYaml;
|
|
266
|
-
const identityBlock = root["identity"];
|
|
267
|
-
const annotationsRaw = root["annotations"];
|
|
268
|
-
const annotations = isPlainObject(annotationsRaw) ? Object.keys(annotationsRaw).length === 0 ? null : annotationsRaw : null;
|
|
269
|
-
return {
|
|
270
|
-
parsed: {
|
|
271
|
-
filePath: sidecarPath,
|
|
272
|
-
identityBodyHash: String(identityBlock["bodyHash"]),
|
|
273
|
-
identityFrontmatterHash: String(identityBlock["frontmatterHash"]),
|
|
274
|
-
identityPath: String(identityBlock["path"]),
|
|
275
|
-
annotations,
|
|
276
|
-
raw: root
|
|
277
|
-
},
|
|
278
|
-
present: true,
|
|
279
|
-
issues: []
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
function sidecarPathFor(mdAbsolutePath) {
|
|
283
|
-
if (mdAbsolutePath.endsWith(".md")) {
|
|
284
|
-
return `${mdAbsolutePath.slice(0, -".md".length)}.sm`;
|
|
285
|
-
}
|
|
286
|
-
return `${mdAbsolutePath}.sm`;
|
|
287
|
-
}
|
|
288
|
-
function isPlainObject(value) {
|
|
289
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
290
|
-
}
|
|
291
|
-
var cachedSidecarValidator = null;
|
|
292
|
-
function getSidecarValidator() {
|
|
293
|
-
if (cachedSidecarValidator) return cachedSidecarValidator;
|
|
294
|
-
const ajv = new Ajv2020({ strict: false, allErrors: true, allowUnionTypes: true });
|
|
295
|
-
applyAjvFormats(ajv);
|
|
296
|
-
const specRoot = resolveSpecRoot();
|
|
297
|
-
const annotationsSchema = JSON.parse(
|
|
298
|
-
readFileSync(resolve(specRoot, "schemas/annotations.schema.json"), "utf8")
|
|
299
|
-
);
|
|
300
|
-
const sidecarSchema = JSON.parse(
|
|
301
|
-
readFileSync(resolve(specRoot, "schemas/sidecar.schema.json"), "utf8")
|
|
302
|
-
);
|
|
303
|
-
ajv.addSchema(annotationsSchema);
|
|
304
|
-
cachedSidecarValidator = ajv.compile(sidecarSchema);
|
|
305
|
-
return cachedSidecarValidator;
|
|
306
|
-
}
|
|
307
|
-
function resolveSpecRoot() {
|
|
308
|
-
const require2 = createRequire(import.meta.url);
|
|
309
|
-
try {
|
|
310
|
-
const indexPath = require2.resolve("@skill-map/spec/index.json");
|
|
311
|
-
return dirname(indexPath);
|
|
312
|
-
} catch {
|
|
313
|
-
throw new Error(
|
|
314
|
-
"@skill-map/spec not resolvable \u2014 sidecar reader cannot load schemas."
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// kernel/sidecar/drift.ts
|
|
320
|
-
function computeDriftStatus(args) {
|
|
321
|
-
const bodyDrift = args.storedBodyHash !== args.liveBodyHash;
|
|
322
|
-
const fmDrift = args.storedFrontmatterHash !== args.liveFrontmatterHash;
|
|
323
|
-
if (bodyDrift && fmDrift) return "stale-both";
|
|
324
|
-
if (bodyDrift) return "stale-body";
|
|
325
|
-
if (fmDrift) return "stale-frontmatter";
|
|
326
|
-
return "fresh";
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// kernel/sidecar/discover-orphans.ts
|
|
330
|
-
import { existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
331
|
-
import { join, relative, sep } from "path";
|
|
332
|
-
function discoverOrphanSidecars(roots, shouldSkip) {
|
|
333
|
-
const out = [];
|
|
334
|
-
for (const root of roots) {
|
|
335
|
-
walk(root, root, shouldSkip ?? (() => false), out);
|
|
336
|
-
}
|
|
337
|
-
return out;
|
|
338
|
-
}
|
|
339
|
-
function walk(root, current, shouldSkip, out) {
|
|
340
|
-
let entries;
|
|
341
|
-
try {
|
|
342
|
-
entries = readdirSync(current, { withFileTypes: true, encoding: "utf8" });
|
|
343
|
-
} catch {
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
for (const entry of entries) {
|
|
347
|
-
const full = join(current, entry.name);
|
|
348
|
-
const rel = relative(root, full).split(sep).join("/");
|
|
349
|
-
if (shouldSkip(rel)) continue;
|
|
350
|
-
if (entry.isSymbolicLink()) continue;
|
|
351
|
-
if (entry.isDirectory()) {
|
|
352
|
-
walk(root, full, shouldSkip, out);
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
355
|
-
if (!entry.isFile()) continue;
|
|
356
|
-
if (!entry.name.endsWith(".sm")) continue;
|
|
357
|
-
const expectedMd = `${full.slice(0, -".sm".length)}.md`;
|
|
358
|
-
if (existsSync2(expectedMd) && safeIsFile(expectedMd)) continue;
|
|
359
|
-
out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
function safeIsFile(path) {
|
|
363
|
-
try {
|
|
364
|
-
return statSync(path).isFile();
|
|
365
|
-
} catch {
|
|
366
|
-
return false;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// kernel/sidecar/store.ts
|
|
371
|
-
import { existsSync as existsSync3, readFileSync as readFileSync2, renameSync, writeFileSync, unlinkSync } from "fs";
|
|
372
|
-
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
373
|
-
import { createRequire as createRequire2 } from "module";
|
|
374
|
-
import { Ajv2020 as Ajv20202 } from "ajv/dist/2020.js";
|
|
375
|
-
import yaml2 from "js-yaml";
|
|
376
|
-
|
|
377
207
|
// kernel/adapters/in-memory-progress.ts
|
|
378
208
|
var InMemoryProgressEmitter = class {
|
|
379
209
|
#listeners = /* @__PURE__ */ new Set();
|
|
@@ -388,46 +218,56 @@ var InMemoryProgressEmitter = class {
|
|
|
388
218
|
}
|
|
389
219
|
};
|
|
390
220
|
|
|
391
|
-
// kernel/adapters/
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
info() {
|
|
398
|
-
}
|
|
399
|
-
warn() {
|
|
400
|
-
}
|
|
401
|
-
error() {
|
|
402
|
-
}
|
|
403
|
-
};
|
|
221
|
+
// kernel/adapters/plugin-loader/index.ts
|
|
222
|
+
import { createRequire } from "module";
|
|
223
|
+
import { existsSync, readFileSync as readFileSync2, readdirSync } from "fs";
|
|
224
|
+
import { join, resolve as resolve3 } from "path";
|
|
225
|
+
import { pathToFileURL } from "url";
|
|
226
|
+
import semver from "semver";
|
|
404
227
|
|
|
405
|
-
// kernel/
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
function
|
|
415
|
-
|
|
416
|
-
}
|
|
417
|
-
function resetLogger() {
|
|
418
|
-
active = new SilentLogger();
|
|
419
|
-
}
|
|
420
|
-
function getActiveLogger() {
|
|
421
|
-
return active;
|
|
228
|
+
// kernel/adapters/plugin-loader/id-utils.ts
|
|
229
|
+
import { isAbsolute, relative, resolve } from "path";
|
|
230
|
+
|
|
231
|
+
// kernel/adapters/plugin-loader/validation.ts
|
|
232
|
+
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
233
|
+
|
|
234
|
+
// kernel/util/ajv-interop.ts
|
|
235
|
+
import addFormatsModule from "ajv-formats";
|
|
236
|
+
var addFormats = addFormatsModule.default ?? addFormatsModule;
|
|
237
|
+
function applyAjvFormats(ajv) {
|
|
238
|
+
addFormats(ajv);
|
|
422
239
|
}
|
|
423
240
|
|
|
424
|
-
// kernel/
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
241
|
+
// kernel/extensions/hook.ts
|
|
242
|
+
var HOOK_TRIGGERS = Object.freeze([
|
|
243
|
+
"boot",
|
|
244
|
+
"scan.started",
|
|
245
|
+
"scan.completed",
|
|
246
|
+
"extractor.completed",
|
|
247
|
+
"analyzer.completed",
|
|
248
|
+
"action.completed",
|
|
249
|
+
"job.spawning",
|
|
250
|
+
"job.completed",
|
|
251
|
+
"job.failed",
|
|
252
|
+
"shutdown"
|
|
253
|
+
]);
|
|
254
|
+
|
|
255
|
+
// kernel/adapters/plugin-loader/validation.ts
|
|
256
|
+
var KNOWN_KINDS = /* @__PURE__ */ new Set([
|
|
257
|
+
"provider",
|
|
258
|
+
"extractor",
|
|
259
|
+
"analyzer",
|
|
260
|
+
"action",
|
|
261
|
+
"formatter",
|
|
262
|
+
"hook"
|
|
263
|
+
]);
|
|
264
|
+
var KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(" / ");
|
|
265
|
+
var HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(", ");
|
|
266
|
+
|
|
267
|
+
// kernel/adapters/plugin-loader/storage-schemas.ts
|
|
268
|
+
import { readFileSync } from "fs";
|
|
269
|
+
import { resolve as resolve2 } from "path";
|
|
270
|
+
import { Ajv2020 as Ajv20202 } from "ajv/dist/2020.js";
|
|
431
271
|
|
|
432
272
|
// kernel/i18n/plugin-store.texts.ts
|
|
433
273
|
var PLUGIN_STORE_TEXTS = {
|
|
@@ -506,37 +346,20 @@ function formatAjvErrors(errors) {
|
|
|
506
346
|
return errors.map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
|
|
507
347
|
}
|
|
508
348
|
|
|
509
|
-
// kernel/
|
|
510
|
-
var HOOK_TRIGGERS = Object.freeze([
|
|
511
|
-
"boot",
|
|
512
|
-
"scan.started",
|
|
513
|
-
"scan.completed",
|
|
514
|
-
"extractor.completed",
|
|
515
|
-
"analyzer.completed",
|
|
516
|
-
"action.completed",
|
|
517
|
-
"job.spawning",
|
|
518
|
-
"job.completed",
|
|
519
|
-
"job.failed",
|
|
520
|
-
"shutdown"
|
|
521
|
-
]);
|
|
522
|
-
|
|
523
|
-
// kernel/adapters/plugin-loader.ts
|
|
524
|
-
var KNOWN_KINDS = /* @__PURE__ */ new Set(["provider", "extractor", "analyzer", "action", "formatter", "hook"]);
|
|
525
|
-
var KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(" / ");
|
|
526
|
-
var HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(", ");
|
|
349
|
+
// kernel/adapters/plugin-loader/index.ts
|
|
527
350
|
function installedSpecVersion() {
|
|
528
|
-
const require2 =
|
|
351
|
+
const require2 = createRequire(import.meta.url);
|
|
529
352
|
const indexPath = require2.resolve("@skill-map/spec/index.json");
|
|
530
353
|
const pkgPath = resolve3(indexPath, "..", "package.json");
|
|
531
|
-
const pkg = JSON.parse(
|
|
354
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
|
|
532
355
|
return pkg.version;
|
|
533
356
|
}
|
|
534
357
|
|
|
535
358
|
// kernel/adapters/schema-validators.ts
|
|
536
|
-
import { readFileSync as
|
|
537
|
-
import { dirname
|
|
538
|
-
import { createRequire as
|
|
539
|
-
import { Ajv2020 as
|
|
359
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
360
|
+
import { dirname, resolve as resolve4 } from "path";
|
|
361
|
+
import { createRequire as createRequire2 } from "module";
|
|
362
|
+
import { Ajv2020 as Ajv20203 } from "ajv/dist/2020.js";
|
|
540
363
|
var SCHEMA_FILES = {
|
|
541
364
|
node: "schemas/node.schema.json",
|
|
542
365
|
link: "schemas/link.schema.json",
|
|
@@ -571,8 +394,8 @@ function loadSchemaValidators() {
|
|
|
571
394
|
return cachedValidators;
|
|
572
395
|
}
|
|
573
396
|
function buildSchemaValidators() {
|
|
574
|
-
const specRoot =
|
|
575
|
-
const ajv = new
|
|
397
|
+
const specRoot = resolveSpecRoot();
|
|
398
|
+
const ajv = new Ajv20203({
|
|
576
399
|
strict: false,
|
|
577
400
|
allErrors: true,
|
|
578
401
|
allowUnionTypes: true
|
|
@@ -581,13 +404,13 @@ function buildSchemaValidators() {
|
|
|
581
404
|
for (const rel of SUPPORTING_SCHEMAS) {
|
|
582
405
|
const file = resolve4(specRoot, rel);
|
|
583
406
|
if (!existsSyncSafe(file)) continue;
|
|
584
|
-
const schema = JSON.parse(
|
|
407
|
+
const schema = JSON.parse(readFileSync3(file, "utf8"));
|
|
585
408
|
ajv.addSchema(schema);
|
|
586
409
|
}
|
|
587
410
|
const validators = /* @__PURE__ */ new Map();
|
|
588
411
|
for (const [name, rel] of Object.entries(SCHEMA_FILES)) {
|
|
589
412
|
const file = resolve4(specRoot, rel);
|
|
590
|
-
const schema = JSON.parse(
|
|
413
|
+
const schema = JSON.parse(readFileSync3(file, "utf8"));
|
|
591
414
|
const byId = typeof schema.$id === "string" ? ajv.getSchema(schema.$id) : void 0;
|
|
592
415
|
validators.set(name, byId ?? ajv.compile(schema));
|
|
593
416
|
}
|
|
@@ -607,7 +430,7 @@ function buildSchemaValidators() {
|
|
|
607
430
|
const KNOWN_SLOTS = /* @__PURE__ */ new Set([
|
|
608
431
|
"card.title.right",
|
|
609
432
|
"card.subtitle.left",
|
|
610
|
-
"card.footer.left
|
|
433
|
+
"card.footer.left",
|
|
611
434
|
"card.footer.right",
|
|
612
435
|
"graph.node.alert",
|
|
613
436
|
"inspector.header.badge.counter",
|
|
@@ -618,7 +441,7 @@ function buildSchemaValidators() {
|
|
|
618
441
|
"inspector.body.panel.key-values",
|
|
619
442
|
"inspector.body.panel.link-list",
|
|
620
443
|
"inspector.body.panel.markdown",
|
|
621
|
-
"topbar.
|
|
444
|
+
"topbar.nav.start"
|
|
622
445
|
]);
|
|
623
446
|
function getContributionValidator(slot) {
|
|
624
447
|
if (!KNOWN_SLOTS.has(slot)) return null;
|
|
@@ -667,15 +490,15 @@ function buildSchemaValidators() {
|
|
|
667
490
|
};
|
|
668
491
|
}
|
|
669
492
|
function buildProviderFrontmatterValidator(providers) {
|
|
670
|
-
const specRoot =
|
|
671
|
-
const ajv = new
|
|
493
|
+
const specRoot = resolveSpecRoot();
|
|
494
|
+
const ajv = new Ajv20203({
|
|
672
495
|
strict: false,
|
|
673
496
|
allErrors: true,
|
|
674
497
|
allowUnionTypes: true
|
|
675
498
|
});
|
|
676
499
|
applyAjvFormats(ajv);
|
|
677
500
|
const baseFile = resolve4(specRoot, "schemas/frontmatter/base.schema.json");
|
|
678
|
-
const baseSchema = JSON.parse(
|
|
501
|
+
const baseSchema = JSON.parse(readFileSync3(baseFile, "utf8"));
|
|
679
502
|
ajv.addSchema(baseSchema);
|
|
680
503
|
registerProviderAuxiliarySchemas(ajv, providers);
|
|
681
504
|
const compiled = /* @__PURE__ */ new Map();
|
|
@@ -712,11 +535,11 @@ function registerProviderAuxiliarySchemas(ajv, providers) {
|
|
|
712
535
|
}
|
|
713
536
|
}
|
|
714
537
|
}
|
|
715
|
-
function
|
|
716
|
-
const require2 =
|
|
538
|
+
function resolveSpecRoot() {
|
|
539
|
+
const require2 = createRequire2(import.meta.url);
|
|
717
540
|
try {
|
|
718
541
|
const indexPath = require2.resolve("@skill-map/spec/index.json");
|
|
719
|
-
return
|
|
542
|
+
return dirname(indexPath);
|
|
720
543
|
} catch {
|
|
721
544
|
throw new Error(
|
|
722
545
|
"@skill-map/spec not resolvable \u2014 ensure the workspace is linked or the package is installed."
|
|
@@ -725,214 +548,49 @@ function resolveSpecRoot2() {
|
|
|
725
548
|
}
|
|
726
549
|
function existsSyncSafe(path) {
|
|
727
550
|
try {
|
|
728
|
-
|
|
551
|
+
readFileSync3(path, "utf8");
|
|
729
552
|
return true;
|
|
730
553
|
} catch {
|
|
731
554
|
return false;
|
|
732
555
|
}
|
|
733
556
|
}
|
|
734
557
|
|
|
735
|
-
// kernel/i18n/orchestrator.texts.ts
|
|
736
|
-
var ORCHESTRATOR_TEXTS = {
|
|
737
|
-
frontmatterInvalid: "Frontmatter for {{path}} ({{kind}}) failed schema validation: {{errors}}",
|
|
738
|
-
frontmatterMalformedPasteWithIndent: "Frontmatter fence in {{path}} appears indented; YAML frontmatter MUST start with `---` at column 0. The file was scanned as body-only \u2014 the metadata block was silently lost. Move the `---` lines to the start of the line.",
|
|
739
|
-
frontmatterMalformedByteOrderMark: "Frontmatter fence in {{path}} is preceded by a UTF-8 byte-order mark (BOM); the file was scanned as body-only. Re-save the file as UTF-8 without BOM. The metadata block was silently lost.",
|
|
740
|
-
frontmatterMalformedMissingClose: "Frontmatter in {{path}} opens with `---` but never closes \u2014 no matching `---` line at column 0 was found. The file was scanned as body-only and every metadata field was silently lost. Add a closing `---` line below the metadata block.",
|
|
741
|
-
extensionErrorLinkKindNotDeclared: 'Extractor "{{extractorId}}" emitted a link of kind "{{linkKind}}" outside its declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',
|
|
742
|
-
extensionErrorIssueInvalidSeverity: `Rule "{{analyzerId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
|
|
743
|
-
extensionErrorContributionUnknownId: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}} but did not declare it in its `viewContributions` map. Contribution dropped.',
|
|
744
|
-
extensionErrorContributionPayloadInvalid: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}}; payload failed the "{{slot}}" schema: {{errors}}. Contribution dropped.',
|
|
745
|
-
runScanRootEmptyArray: "runScan: roots must contain at least one path (spec requires minItems: 1)",
|
|
746
|
-
runScanRootMissing: "runScan: root path '{{root}}' does not exist or is not a directory"
|
|
747
|
-
};
|
|
748
|
-
|
|
749
558
|
// kernel/util/format-error.ts
|
|
750
559
|
function formatErrorMessage(err) {
|
|
751
560
|
return err instanceof Error ? err.message : String(err);
|
|
752
561
|
}
|
|
753
562
|
|
|
754
|
-
// kernel/
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
// kernel/scan/ignore.ts
|
|
759
|
-
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
760
|
-
import { dirname as dirname4, resolve as resolve5 } from "path";
|
|
761
|
-
import { fileURLToPath } from "url";
|
|
762
|
-
import ignoreFactory from "ignore";
|
|
763
|
-
function buildIgnoreFilter(opts = {}) {
|
|
764
|
-
const ig = ignoreFactory();
|
|
765
|
-
if (opts.includeDefaults !== false) {
|
|
766
|
-
ig.add(loadDefaultsText());
|
|
767
|
-
}
|
|
768
|
-
if (opts.configIgnore && opts.configIgnore.length > 0) {
|
|
769
|
-
ig.add(opts.configIgnore);
|
|
563
|
+
// kernel/adapters/silent-logger.ts
|
|
564
|
+
var SilentLogger = class {
|
|
565
|
+
trace() {
|
|
770
566
|
}
|
|
771
|
-
|
|
772
|
-
ig.add(opts.ignoreFileText);
|
|
567
|
+
debug() {
|
|
773
568
|
}
|
|
774
|
-
|
|
775
|
-
ignores(relativePath) {
|
|
776
|
-
if (relativePath === "" || relativePath === "." || relativePath === "./") {
|
|
777
|
-
return false;
|
|
778
|
-
}
|
|
779
|
-
const normalised = relativePath.replace(/^\.\//, "").replace(/\\/g, "/").replace(/^\//, "");
|
|
780
|
-
if (normalised === "") return false;
|
|
781
|
-
return ig.ignores(normalised);
|
|
782
|
-
}
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
var cachedDefaults = null;
|
|
786
|
-
function loadDefaultsText() {
|
|
787
|
-
if (cachedDefaults !== null) return cachedDefaults;
|
|
788
|
-
cachedDefaults = readDefaultsFromDisk();
|
|
789
|
-
return cachedDefaults;
|
|
790
|
-
}
|
|
791
|
-
function readDefaultsFromDisk() {
|
|
792
|
-
const here = dirname4(fileURLToPath(import.meta.url));
|
|
793
|
-
const candidates = [
|
|
794
|
-
resolve5(here, "../../config/defaults/skillmapignore"),
|
|
795
|
-
// src/kernel/scan/ → src/config/defaults/
|
|
796
|
-
resolve5(here, "../config/defaults/skillmapignore"),
|
|
797
|
-
// dist/cli.js → dist/config/defaults/ (siblings)
|
|
798
|
-
resolve5(here, "config/defaults/skillmapignore")
|
|
799
|
-
];
|
|
800
|
-
for (const candidate of candidates) {
|
|
801
|
-
if (existsSync5(candidate)) {
|
|
802
|
-
try {
|
|
803
|
-
return readFileSync5(candidate, "utf8");
|
|
804
|
-
} catch {
|
|
805
|
-
}
|
|
806
|
-
}
|
|
569
|
+
info() {
|
|
807
570
|
}
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
// built-in-plugins/parsers/frontmatter-yaml/index.ts
|
|
812
|
-
import yaml3 from "js-yaml";
|
|
813
|
-
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
814
|
-
var FORBIDDEN_FRONTMATTER_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
815
|
-
var frontmatterYamlParser = {
|
|
816
|
-
id: "frontmatter-yaml",
|
|
817
|
-
parse(raw, _path) {
|
|
818
|
-
const match = FRONTMATTER_RE.exec(raw);
|
|
819
|
-
if (!match) return { frontmatterRaw: "", frontmatter: {}, body: raw };
|
|
820
|
-
const frontmatterRaw = match[1];
|
|
821
|
-
const body = match[2];
|
|
822
|
-
const parsed = {};
|
|
823
|
-
try {
|
|
824
|
-
const doc = yaml3.load(frontmatterRaw, { schema: yaml3.JSON_SCHEMA });
|
|
825
|
-
if (doc && typeof doc === "object" && !Array.isArray(doc)) {
|
|
826
|
-
for (const [k, v] of Object.entries(doc)) {
|
|
827
|
-
if (FORBIDDEN_FRONTMATTER_KEYS.has(k)) continue;
|
|
828
|
-
parsed[k] = v;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
} catch {
|
|
832
|
-
}
|
|
833
|
-
return { frontmatterRaw, frontmatter: parsed, body };
|
|
571
|
+
warn() {
|
|
834
572
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
// built-in-plugins/parsers/plain/index.ts
|
|
838
|
-
var plainParser = {
|
|
839
|
-
id: "plain",
|
|
840
|
-
parse(raw, _path) {
|
|
841
|
-
return { frontmatter: {}, frontmatterRaw: "", body: raw };
|
|
573
|
+
error() {
|
|
842
574
|
}
|
|
843
575
|
};
|
|
844
576
|
|
|
845
|
-
// kernel/
|
|
846
|
-
var
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// kernel/scan/walk-content.ts
|
|
856
|
-
var UnknownParserError = class extends Error {
|
|
857
|
-
constructor(parserId) {
|
|
858
|
-
super(`Unknown parser id '${parserId}'. Built-in parsers: 'frontmatter-yaml', 'plain'.`);
|
|
859
|
-
this.name = "UnknownParserError";
|
|
860
|
-
}
|
|
577
|
+
// kernel/util/logger.ts
|
|
578
|
+
var active = new SilentLogger();
|
|
579
|
+
var log = {
|
|
580
|
+
trace: (message, context) => active.trace(message, context),
|
|
581
|
+
debug: (message, context) => active.debug(message, context),
|
|
582
|
+
info: (message, context) => active.info(message, context),
|
|
583
|
+
warn: (message, context) => active.warn(message, context),
|
|
584
|
+
error: (message, context) => active.error(message, context)
|
|
861
585
|
};
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
if (!parser) throw new UnknownParserError(options.parser);
|
|
865
|
-
const filter = options.ignoreFilter ?? buildIgnoreFilter();
|
|
866
|
-
const extensions = options.extensions;
|
|
867
|
-
for (const root of roots) {
|
|
868
|
-
for await (const file of walkRoot(root, root, filter, extensions)) {
|
|
869
|
-
const relPath = relative3(root, file).split(sep2).join("/");
|
|
870
|
-
let raw;
|
|
871
|
-
try {
|
|
872
|
-
raw = await readFile(file, "utf8");
|
|
873
|
-
} catch {
|
|
874
|
-
continue;
|
|
875
|
-
}
|
|
876
|
-
const parsed = parser.parse(raw, relPath);
|
|
877
|
-
yield {
|
|
878
|
-
path: relPath,
|
|
879
|
-
body: parsed.body,
|
|
880
|
-
frontmatterRaw: parsed.frontmatterRaw,
|
|
881
|
-
frontmatter: parsed.frontmatter
|
|
882
|
-
};
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
async function* walkRoot(root, current, filter, extensions) {
|
|
887
|
-
let entries;
|
|
888
|
-
try {
|
|
889
|
-
entries = await readdir(current, { withFileTypes: true, encoding: "utf8" });
|
|
890
|
-
} catch {
|
|
891
|
-
return;
|
|
892
|
-
}
|
|
893
|
-
for (const entry of entries) {
|
|
894
|
-
const name = entry.name;
|
|
895
|
-
const full = join3(current, name);
|
|
896
|
-
const rel = relative3(root, full).split(sep2).join("/");
|
|
897
|
-
if (filter.ignores(rel)) continue;
|
|
898
|
-
if (entry.isSymbolicLink()) continue;
|
|
899
|
-
if (entry.isDirectory()) {
|
|
900
|
-
yield* walkRoot(root, full, filter, extensions);
|
|
901
|
-
} else if (entry.isFile() && hasMatchingExtension(name, extensions)) {
|
|
902
|
-
try {
|
|
903
|
-
const s = await stat(full);
|
|
904
|
-
if (s.isFile()) yield full;
|
|
905
|
-
} catch {
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
}
|
|
586
|
+
function configureLogger(impl) {
|
|
587
|
+
active = impl;
|
|
909
588
|
}
|
|
910
|
-
function
|
|
911
|
-
|
|
912
|
-
if (name.endsWith(ext)) return true;
|
|
913
|
-
}
|
|
914
|
-
return false;
|
|
589
|
+
function resetLogger() {
|
|
590
|
+
active = new SilentLogger();
|
|
915
591
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
var DEFAULT_READ_CONFIG = Object.freeze({
|
|
919
|
-
extensions: Object.freeze([".md"]),
|
|
920
|
-
parser: "frontmatter-yaml"
|
|
921
|
-
});
|
|
922
|
-
function resolveProviderWalk(provider) {
|
|
923
|
-
if (provider.walk) {
|
|
924
|
-
const walk2 = provider.walk.bind(provider);
|
|
925
|
-
return walk2;
|
|
926
|
-
}
|
|
927
|
-
const read = provider.read ?? DEFAULT_READ_CONFIG;
|
|
928
|
-
return (roots, options) => {
|
|
929
|
-
const walkOptions = {
|
|
930
|
-
extensions: read.extensions,
|
|
931
|
-
parser: read.parser
|
|
932
|
-
};
|
|
933
|
-
if (options?.ignoreFilter) walkOptions.ignoreFilter = options.ignoreFilter;
|
|
934
|
-
return walkContent(roots, walkOptions);
|
|
935
|
-
};
|
|
592
|
+
function getActiveLogger() {
|
|
593
|
+
return active;
|
|
936
594
|
}
|
|
937
595
|
|
|
938
596
|
// kernel/extensions/hook-dispatcher.ts
|
|
@@ -1014,166 +672,21 @@ function buildHookContext(_hook, trigger, event) {
|
|
|
1014
672
|
return ctx;
|
|
1015
673
|
}
|
|
1016
674
|
|
|
1017
|
-
// kernel/orchestrator.ts
|
|
1018
|
-
var
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
675
|
+
// kernel/i18n/orchestrator.texts.ts
|
|
676
|
+
var ORCHESTRATOR_TEXTS = {
|
|
677
|
+
frontmatterInvalid: "Frontmatter for {{path}} ({{kind}}) failed schema validation: {{errors}}",
|
|
678
|
+
frontmatterMalformedPasteWithIndent: "Frontmatter fence in {{path}} appears indented; YAML frontmatter MUST start with `---` at column 0. The file was scanned as body-only \u2014 the metadata block was silently lost. Move the `---` lines to the start of the line.",
|
|
679
|
+
frontmatterMalformedByteOrderMark: "Frontmatter fence in {{path}} is preceded by a UTF-8 byte-order mark (BOM); the file was scanned as body-only. Re-save the file as UTF-8 without BOM. The metadata block was silently lost.",
|
|
680
|
+
frontmatterMalformedMissingClose: "Frontmatter in {{path}} opens with `---` but never closes \u2014 no matching `---` line at column 0 was found. The file was scanned as body-only and every metadata field was silently lost. Add a closing `---` line below the metadata block.",
|
|
681
|
+
extensionErrorLinkKindNotDeclared: 'Extractor "{{extractorId}}" emitted a link of kind "{{linkKind}}" outside its declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',
|
|
682
|
+
extensionErrorIssueInvalidSeverity: `Rule "{{analyzerId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
|
|
683
|
+
extensionErrorContributionUnknownId: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}} but did not declare it in its `viewContributions` map. Contribution dropped.',
|
|
684
|
+
extensionErrorContributionPayloadInvalid: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}}; payload failed the "{{slot}}" schema: {{errors}}. Contribution dropped.',
|
|
685
|
+
runScanRootEmptyArray: "runScan: roots must contain at least one path (spec requires minItems: 1)",
|
|
686
|
+
runScanRootMissing: "runScan: root path '{{root}}' does not exist or is not a directory"
|
|
1022
687
|
};
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
return installedSpecVersion();
|
|
1026
|
-
} catch {
|
|
1027
|
-
return "unknown";
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
async function runScanWithRenames(_kernel, options) {
|
|
1031
|
-
return runScanInternal(_kernel, options);
|
|
1032
|
-
}
|
|
1033
|
-
async function runScan(_kernel, options) {
|
|
1034
|
-
const { result } = await runScanInternal(_kernel, options);
|
|
1035
|
-
return result;
|
|
1036
|
-
}
|
|
1037
|
-
async function runScanInternal(_kernel, options) {
|
|
1038
|
-
validateRoots(options.roots);
|
|
1039
|
-
const start = Date.now();
|
|
1040
|
-
const scannedAt = start;
|
|
1041
|
-
const emitter = options.emitter ?? new InMemoryProgressEmitter();
|
|
1042
|
-
const exts = options.extensions ?? { providers: [], extractors: [], analyzers: [] };
|
|
1043
|
-
const hookDispatcher = makeHookDispatcher(exts.hooks ?? [], emitter);
|
|
1044
|
-
const tokenize = options.tokenize !== false;
|
|
1045
|
-
const scope = options.scope ?? "project";
|
|
1046
|
-
const strict = options.strict === true;
|
|
1047
|
-
const encoder = tokenize ? new Tiktoken(cl100k_base) : null;
|
|
1048
|
-
const prior = options.priorSnapshot ?? null;
|
|
1049
|
-
const enableCache = options.enableCache === true;
|
|
1050
|
-
const priorExtractorRuns = options.priorExtractorRuns;
|
|
1051
|
-
const priorIndex = indexPriorSnapshot(prior);
|
|
1052
|
-
const providerFrontmatter = buildProviderFrontmatterValidator(exts.providers);
|
|
1053
|
-
const scanStartedEvent = makeEvent("scan.started", { roots: options.roots });
|
|
1054
|
-
emitter.emit(scanStartedEvent);
|
|
1055
|
-
await hookDispatcher.dispatch("scan.started", scanStartedEvent);
|
|
1056
|
-
const walked = await walkAndExtract({
|
|
1057
|
-
providers: exts.providers,
|
|
1058
|
-
extractors: exts.extractors,
|
|
1059
|
-
roots: options.roots,
|
|
1060
|
-
...options.ignoreFilter ? { ignoreFilter: options.ignoreFilter } : {},
|
|
1061
|
-
emitter,
|
|
1062
|
-
encoder,
|
|
1063
|
-
strict,
|
|
1064
|
-
enableCache,
|
|
1065
|
-
prior,
|
|
1066
|
-
priorIndex,
|
|
1067
|
-
priorExtractorRuns,
|
|
1068
|
-
providerFrontmatter,
|
|
1069
|
-
pluginStores: options.pluginStores
|
|
1070
|
-
});
|
|
1071
|
-
recomputeLinkCounts(walked.nodes, walked.internalLinks);
|
|
1072
|
-
recomputeExternalRefsCount(walked.nodes, walked.externalLinks, walked.cachedPaths);
|
|
1073
|
-
for (const extractor of exts.extractors) {
|
|
1074
|
-
const extractorId = qualifiedExtensionId(extractor.pluginId, extractor.id);
|
|
1075
|
-
const evt = makeEvent("extractor.completed", { extractorId });
|
|
1076
|
-
emitter.emit(evt);
|
|
1077
|
-
await hookDispatcher.dispatch("extractor.completed", evt);
|
|
1078
|
-
}
|
|
1079
|
-
const analyzerResult = await runAnalyzers(
|
|
1080
|
-
exts.analyzers,
|
|
1081
|
-
walked.nodes,
|
|
1082
|
-
walked.internalLinks,
|
|
1083
|
-
walked.orphanSidecars,
|
|
1084
|
-
walked.sidecarRoots,
|
|
1085
|
-
options.annotationContributions ?? [],
|
|
1086
|
-
options.viewContributions ?? [],
|
|
1087
|
-
options.orphanJobFiles ?? [],
|
|
1088
|
-
options.referenceablePaths,
|
|
1089
|
-
options.cwd,
|
|
1090
|
-
emitter,
|
|
1091
|
-
hookDispatcher
|
|
1092
|
-
);
|
|
1093
|
-
const issues = analyzerResult.issues;
|
|
1094
|
-
for (const c of analyzerResult.contributions) walked.contributions.push(c);
|
|
1095
|
-
for (const analyzer of exts.analyzers ?? []) {
|
|
1096
|
-
if (analyzer.viewContributions === void 0) continue;
|
|
1097
|
-
for (const node of walked.nodes) {
|
|
1098
|
-
walked.freshlyRunTuples.add(`${analyzer.pluginId}/${analyzer.id}/${node.path}`);
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
for (const issue of walked.frontmatterIssues) issues.push(issue);
|
|
1102
|
-
const renameOps = prior ? detectRenamesAndOrphans(prior, walked.nodes, issues) : [];
|
|
1103
|
-
const stats = {
|
|
1104
|
-
// `filesSkipped` is "files walked but not classified by any Provider".
|
|
1105
|
-
// Today every walked file IS classified by its Provider (the `claude`
|
|
1106
|
-
// Provider's `classify()` always returns a kind, falling back to
|
|
1107
|
-
// `'markdown'`), so this is always 0. Wired now so the field shape is
|
|
1108
|
-
// spec-conformant; meaningful once multiple Providers compete.
|
|
1109
|
-
filesWalked: walked.filesWalked,
|
|
1110
|
-
filesSkipped: 0,
|
|
1111
|
-
nodesCount: walked.nodes.length,
|
|
1112
|
-
linksCount: walked.internalLinks.length,
|
|
1113
|
-
issuesCount: issues.length,
|
|
1114
|
-
durationMs: Date.now() - start
|
|
1115
|
-
};
|
|
1116
|
-
const scanCompletedEvent = makeEvent("scan.completed", { stats });
|
|
1117
|
-
emitter.emit(scanCompletedEvent);
|
|
1118
|
-
await hookDispatcher.dispatch("scan.completed", scanCompletedEvent);
|
|
1119
|
-
return {
|
|
1120
|
-
result: {
|
|
1121
|
-
schemaVersion: 1,
|
|
1122
|
-
scannedAt,
|
|
1123
|
-
scope,
|
|
1124
|
-
roots: options.roots,
|
|
1125
|
-
providers: exts.providers.map((a) => a.id),
|
|
1126
|
-
scannedBy: SCANNED_BY,
|
|
1127
|
-
nodes: walked.nodes,
|
|
1128
|
-
links: walked.internalLinks,
|
|
1129
|
-
issues,
|
|
1130
|
-
stats
|
|
1131
|
-
},
|
|
1132
|
-
renameOps,
|
|
1133
|
-
extractorRuns: walked.extractorRuns,
|
|
1134
|
-
enrichments: walked.enrichments,
|
|
1135
|
-
contributions: walked.contributions,
|
|
1136
|
-
freshlyRunTuples: walked.freshlyRunTuples
|
|
1137
|
-
};
|
|
1138
|
-
}
|
|
1139
|
-
function validateRoots(roots) {
|
|
1140
|
-
if (roots.length === 0) {
|
|
1141
|
-
throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
|
|
1142
|
-
}
|
|
1143
|
-
for (const root of roots) {
|
|
1144
|
-
if (!existsSync6(root) || !statSync2(root).isDirectory()) {
|
|
1145
|
-
throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
function indexPriorSnapshot(prior) {
|
|
1150
|
-
const priorNodesByPath = /* @__PURE__ */ new Map();
|
|
1151
|
-
const priorNodePaths = /* @__PURE__ */ new Set();
|
|
1152
|
-
const priorLinksByOriginating = /* @__PURE__ */ new Map();
|
|
1153
|
-
const priorFrontmatterIssuesByNode = /* @__PURE__ */ new Map();
|
|
1154
|
-
if (!prior) {
|
|
1155
|
-
return { priorNodesByPath, priorNodePaths, priorLinksByOriginating, priorFrontmatterIssuesByNode };
|
|
1156
|
-
}
|
|
1157
|
-
for (const node of prior.nodes) {
|
|
1158
|
-
priorNodesByPath.set(node.path, node);
|
|
1159
|
-
priorNodePaths.add(node.path);
|
|
1160
|
-
}
|
|
1161
|
-
for (const link of prior.links) {
|
|
1162
|
-
const key = originatingNodeOf(link, priorNodePaths);
|
|
1163
|
-
const list = priorLinksByOriginating.get(key);
|
|
1164
|
-
if (list) list.push(link);
|
|
1165
|
-
else priorLinksByOriginating.set(key, [link]);
|
|
1166
|
-
}
|
|
1167
|
-
for (const issue of prior.issues) {
|
|
1168
|
-
if (issue.analyzerId !== "frontmatter-invalid" && issue.analyzerId !== "frontmatter-malformed") continue;
|
|
1169
|
-
if (issue.nodeIds.length !== 1) continue;
|
|
1170
|
-
const path = issue.nodeIds[0];
|
|
1171
|
-
const list = priorFrontmatterIssuesByNode.get(path);
|
|
1172
|
-
if (list) list.push(issue);
|
|
1173
|
-
else priorFrontmatterIssuesByNode.set(path, [issue]);
|
|
1174
|
-
}
|
|
1175
|
-
return { priorNodesByPath, priorNodePaths, priorLinksByOriginating, priorFrontmatterIssuesByNode };
|
|
1176
|
-
}
|
|
688
|
+
|
|
689
|
+
// kernel/orchestrator/extractors.ts
|
|
1177
690
|
async function runExtractorsForNode(opts) {
|
|
1178
691
|
const internalLinks = [];
|
|
1179
692
|
const externalLinks = [];
|
|
@@ -1292,320 +805,71 @@ function emitExtensionError(emitter, qualifiedId, nodePath, data) {
|
|
|
1292
805
|
})
|
|
1293
806
|
);
|
|
1294
807
|
}
|
|
1295
|
-
function
|
|
1296
|
-
const
|
|
1297
|
-
(ex) => ex.applicableKinds === void 0 || ex.applicableKinds.includes(opts.kind)
|
|
1298
|
-
);
|
|
1299
|
-
const applicableQualifiedIds = new Set(
|
|
1300
|
-
applicableExtractors.map((ex) => qualifiedExtensionId(ex.pluginId, ex.id))
|
|
1301
|
-
);
|
|
1302
|
-
const cachedQualifiedIds = /* @__PURE__ */ new Set();
|
|
1303
|
-
const missingExtractors = [];
|
|
1304
|
-
if (opts.priorExtractorRuns === void 0) {
|
|
1305
|
-
if (opts.nodeHashCacheEligible) {
|
|
1306
|
-
for (const id of applicableQualifiedIds) cachedQualifiedIds.add(id);
|
|
1307
|
-
} else {
|
|
1308
|
-
for (const ex of applicableExtractors) missingExtractors.push(ex);
|
|
1309
|
-
}
|
|
1310
|
-
} else {
|
|
1311
|
-
const priorRunsForNode = opts.priorExtractorRuns.get(opts.nodePath) ?? /* @__PURE__ */ new Map();
|
|
1312
|
-
for (const ex of applicableExtractors) {
|
|
1313
|
-
const qualified = qualifiedExtensionId(ex.pluginId, ex.id);
|
|
1314
|
-
const priorBody = priorRunsForNode.get(qualified);
|
|
1315
|
-
if (opts.nodeHashCacheEligible && priorBody === opts.bodyHash) {
|
|
1316
|
-
cachedQualifiedIds.add(qualified);
|
|
1317
|
-
} else {
|
|
1318
|
-
missingExtractors.push(ex);
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
808
|
+
function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enrichNode, emitContribution, store) {
|
|
809
|
+
const scope = extractor.scope;
|
|
1322
810
|
return {
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
811
|
+
node,
|
|
812
|
+
body: scope === "frontmatter" ? "" : body,
|
|
813
|
+
frontmatter: scope === "body" ? {} : frontmatter,
|
|
814
|
+
emitLink,
|
|
815
|
+
enrichNode,
|
|
816
|
+
emitContribution,
|
|
817
|
+
...store !== void 0 ? { store } : {}
|
|
1328
818
|
};
|
|
1329
819
|
}
|
|
1330
|
-
function
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
820
|
+
function validateLink(extractor, link, emitter) {
|
|
821
|
+
if (!extractor.emitsLinkKinds.includes(link.kind)) {
|
|
822
|
+
const qualifiedId = `${extractor.pluginId}/${extractor.id}`;
|
|
823
|
+
emitter.emit(
|
|
824
|
+
makeEvent("extension.error", {
|
|
825
|
+
kind: "link-kind-not-declared",
|
|
826
|
+
extensionId: qualifiedId,
|
|
827
|
+
linkKind: link.kind,
|
|
828
|
+
declaredKinds: extractor.emitsLinkKinds,
|
|
829
|
+
link: { source: link.source, target: link.target, kind: link.kind },
|
|
830
|
+
message: tx(ORCHESTRATOR_TEXTS.extensionErrorLinkKindNotDeclared, {
|
|
831
|
+
extractorId: qualifiedId,
|
|
832
|
+
linkKind: link.kind,
|
|
833
|
+
declaredKinds: extractor.emitsLinkKinds.join(", ")
|
|
834
|
+
})
|
|
835
|
+
})
|
|
1341
836
|
);
|
|
1342
|
-
|
|
1343
|
-
}
|
|
1344
|
-
const frontmatterIssues = [];
|
|
1345
|
-
const reusedFm = opts.priorFrontmatterIssuesByNode.get(opts.priorNode.path) ?? [];
|
|
1346
|
-
for (const issue of reusedFm) {
|
|
1347
|
-
frontmatterIssues.push({ ...issue, severity: opts.strict ? "error" : "warn" });
|
|
837
|
+
return null;
|
|
1348
838
|
}
|
|
1349
|
-
|
|
839
|
+
const confidence = link.confidence ?? extractor.defaultConfidence;
|
|
840
|
+
return { ...link, confidence };
|
|
1350
841
|
}
|
|
1351
|
-
function
|
|
1352
|
-
const
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
nodePath: opts.priorNode.path,
|
|
1358
|
-
extractorId: qualified,
|
|
1359
|
-
bodyHashAtRun: opts.bodyHash,
|
|
1360
|
-
ranAt
|
|
1361
|
-
});
|
|
842
|
+
function recomputeLinkCounts(nodes, links) {
|
|
843
|
+
const byPath2 = /* @__PURE__ */ new Map();
|
|
844
|
+
for (const node of nodes) {
|
|
845
|
+
node.linksOutCount = 0;
|
|
846
|
+
node.linksInCount = 0;
|
|
847
|
+
byPath2.set(node.path, node);
|
|
1362
848
|
}
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
kind: opts.kind,
|
|
1369
|
-
providerId: opts.provider.id,
|
|
1370
|
-
frontmatterRaw: opts.raw.frontmatterRaw,
|
|
1371
|
-
body: opts.raw.body,
|
|
1372
|
-
frontmatter: opts.raw.frontmatter,
|
|
1373
|
-
bodyHash: opts.bodyHash,
|
|
1374
|
-
frontmatterHash: opts.frontmatterHash,
|
|
1375
|
-
encoder: opts.encoder
|
|
1376
|
-
});
|
|
1377
|
-
const frontmatterIssues = [];
|
|
1378
|
-
if (opts.raw.frontmatterRaw.length > 0) {
|
|
1379
|
-
const fmIssue = validateFrontmatter(
|
|
1380
|
-
opts.providerFrontmatter,
|
|
1381
|
-
opts.provider,
|
|
1382
|
-
opts.kind,
|
|
1383
|
-
opts.raw.frontmatter,
|
|
1384
|
-
opts.raw.path,
|
|
1385
|
-
opts.strict
|
|
1386
|
-
);
|
|
1387
|
-
if (fmIssue) frontmatterIssues.push(fmIssue);
|
|
1388
|
-
} else {
|
|
1389
|
-
const malformed = detectMalformedFrontmatter(opts.raw.body, opts.raw.path, opts.strict);
|
|
1390
|
-
if (malformed) frontmatterIssues.push(malformed);
|
|
849
|
+
for (const link of links) {
|
|
850
|
+
const source = byPath2.get(link.source);
|
|
851
|
+
if (source) source.linksOutCount += 1;
|
|
852
|
+
const target = byPath2.get(link.target);
|
|
853
|
+
if (target) target.linksInCount += 1;
|
|
1391
854
|
}
|
|
1392
|
-
return { node, frontmatterIssues };
|
|
1393
855
|
}
|
|
1394
|
-
|
|
1395
|
-
const
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
ignoreFilter,
|
|
1400
|
-
emitter,
|
|
1401
|
-
encoder,
|
|
1402
|
-
strict,
|
|
1403
|
-
enableCache,
|
|
1404
|
-
prior,
|
|
1405
|
-
priorIndex,
|
|
1406
|
-
priorExtractorRuns,
|
|
1407
|
-
providerFrontmatter,
|
|
1408
|
-
pluginStores
|
|
1409
|
-
} = opts;
|
|
1410
|
-
const { priorNodesByPath, priorLinksByOriginating, priorFrontmatterIssuesByNode } = priorIndex;
|
|
1411
|
-
const nodes = [];
|
|
1412
|
-
const internalLinks = [];
|
|
1413
|
-
const externalLinks = [];
|
|
1414
|
-
const cachedPaths = /* @__PURE__ */ new Set();
|
|
1415
|
-
const frontmatterIssues = [];
|
|
1416
|
-
const enrichmentBuffer = /* @__PURE__ */ new Map();
|
|
1417
|
-
const contributionsBuffer = [];
|
|
1418
|
-
const freshlyRunTuples = /* @__PURE__ */ new Set();
|
|
1419
|
-
const extractorRuns = [];
|
|
1420
|
-
const sidecarRoots = /* @__PURE__ */ new Map();
|
|
1421
|
-
let filesWalked = 0;
|
|
1422
|
-
let index = 0;
|
|
1423
|
-
const walkOptions = ignoreFilter ? { ignoreFilter } : {};
|
|
1424
|
-
const shortIdToQualified = /* @__PURE__ */ new Map();
|
|
1425
|
-
for (const ex of extractors) {
|
|
1426
|
-
const qualified = qualifiedExtensionId(ex.pluginId, ex.id);
|
|
1427
|
-
const list = shortIdToQualified.get(ex.id);
|
|
1428
|
-
if (list) list.push(qualified);
|
|
1429
|
-
else shortIdToQualified.set(ex.id, [qualified]);
|
|
856
|
+
function recomputeExternalRefsCount(nodes, externalLinks, cachedPaths) {
|
|
857
|
+
const byPath2 = /* @__PURE__ */ new Map();
|
|
858
|
+
for (const node of nodes) {
|
|
859
|
+
if (!cachedPaths.has(node.path)) node.externalRefsCount = 0;
|
|
860
|
+
byPath2.set(node.path, node);
|
|
1430
861
|
}
|
|
1431
|
-
const
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
filesWalked += 1;
|
|
1435
|
-
if (claimedPaths.has(raw.path)) continue;
|
|
1436
|
-
const bodyHash = sha256(raw.body);
|
|
1437
|
-
const frontmatterHash = sha256(canonicalFrontmatter(raw.frontmatter, raw.frontmatterRaw));
|
|
1438
|
-
const priorNode = priorNodesByPath.get(raw.path);
|
|
1439
|
-
const nodeHashCacheEligible = enableCache && prior !== null && priorNode !== void 0 && priorNode.bodyHash === bodyHash && priorNode.frontmatterHash === frontmatterHash;
|
|
1440
|
-
const kind = provider.classify(raw.path, raw.frontmatter);
|
|
1441
|
-
if (kind === null) {
|
|
1442
|
-
continue;
|
|
1443
|
-
}
|
|
1444
|
-
claimedPaths.add(raw.path);
|
|
1445
|
-
index += 1;
|
|
1446
|
-
const cacheDecision = computeCacheDecision({
|
|
1447
|
-
extractors,
|
|
1448
|
-
kind,
|
|
1449
|
-
nodePath: raw.path,
|
|
1450
|
-
bodyHash,
|
|
1451
|
-
nodeHashCacheEligible,
|
|
1452
|
-
priorExtractorRuns
|
|
1453
|
-
});
|
|
1454
|
-
const {
|
|
1455
|
-
applicableExtractors,
|
|
1456
|
-
applicableQualifiedIds,
|
|
1457
|
-
cachedQualifiedIds,
|
|
1458
|
-
missingExtractors,
|
|
1459
|
-
fullCacheHit
|
|
1460
|
-
} = cacheDecision;
|
|
1461
|
-
if (fullCacheHit && priorNode) {
|
|
1462
|
-
const reused = reusePriorNode({
|
|
1463
|
-
priorNode,
|
|
1464
|
-
bodyHash,
|
|
1465
|
-
strict,
|
|
1466
|
-
cachedQualifiedIds,
|
|
1467
|
-
applicableQualifiedIds,
|
|
1468
|
-
shortIdToQualified,
|
|
1469
|
-
priorLinksByOriginating,
|
|
1470
|
-
priorFrontmatterIssuesByNode
|
|
1471
|
-
});
|
|
1472
|
-
const reusedSidecarIssues = resolveAndApplySidecar(
|
|
1473
|
-
reused.node,
|
|
1474
|
-
raw.path,
|
|
1475
|
-
roots,
|
|
1476
|
-
bodyHash,
|
|
1477
|
-
frontmatterHash,
|
|
1478
|
-
sidecarRoots
|
|
1479
|
-
);
|
|
1480
|
-
nodes.push(reused.node);
|
|
1481
|
-
cachedPaths.add(reused.node.path);
|
|
1482
|
-
for (const link of reused.internalLinks) internalLinks.push(link);
|
|
1483
|
-
for (const issue of reused.frontmatterIssues) frontmatterIssues.push(issue);
|
|
1484
|
-
for (const issue of reusedSidecarIssues) frontmatterIssues.push(issue);
|
|
1485
|
-
for (const run of reused.extractorRuns) extractorRuns.push(run);
|
|
1486
|
-
emitter.emit(makeEvent("scan.progress", { index, path: raw.path, kind, cached: true }));
|
|
1487
|
-
continue;
|
|
1488
|
-
}
|
|
1489
|
-
let node;
|
|
1490
|
-
const partialCacheHit = nodeHashCacheEligible && cachedQualifiedIds.size > 0 && priorNode !== void 0;
|
|
1491
|
-
if (partialCacheHit && priorNode) {
|
|
1492
|
-
const partial = cloneNodeAndReshapeLinks({
|
|
1493
|
-
priorNode,
|
|
1494
|
-
strict,
|
|
1495
|
-
cachedQualifiedIds,
|
|
1496
|
-
applicableQualifiedIds,
|
|
1497
|
-
shortIdToQualified,
|
|
1498
|
-
priorLinksByOriginating,
|
|
1499
|
-
priorFrontmatterIssuesByNode
|
|
1500
|
-
});
|
|
1501
|
-
node = partial.node;
|
|
1502
|
-
for (const link of partial.internalLinks) internalLinks.push(link);
|
|
1503
|
-
for (const issue of partial.frontmatterIssues) frontmatterIssues.push(issue);
|
|
1504
|
-
nodes.push(node);
|
|
1505
|
-
} else {
|
|
1506
|
-
const fresh = buildFreshNodeAndValidateFrontmatter({
|
|
1507
|
-
raw,
|
|
1508
|
-
kind,
|
|
1509
|
-
provider,
|
|
1510
|
-
bodyHash,
|
|
1511
|
-
frontmatterHash,
|
|
1512
|
-
encoder,
|
|
1513
|
-
providerFrontmatter,
|
|
1514
|
-
strict
|
|
1515
|
-
});
|
|
1516
|
-
node = fresh.node;
|
|
1517
|
-
nodes.push(node);
|
|
1518
|
-
for (const issue of fresh.frontmatterIssues) frontmatterIssues.push(issue);
|
|
1519
|
-
}
|
|
1520
|
-
const sidecarIssues = resolveAndApplySidecar(
|
|
1521
|
-
node,
|
|
1522
|
-
raw.path,
|
|
1523
|
-
roots,
|
|
1524
|
-
bodyHash,
|
|
1525
|
-
frontmatterHash,
|
|
1526
|
-
sidecarRoots
|
|
1527
|
-
);
|
|
1528
|
-
for (const issue of sidecarIssues) frontmatterIssues.push(issue);
|
|
1529
|
-
emitter.emit(makeEvent("scan.progress", {
|
|
1530
|
-
index,
|
|
1531
|
-
path: raw.path,
|
|
1532
|
-
kind,
|
|
1533
|
-
cached: false,
|
|
1534
|
-
...partialCacheHit ? { partialCache: true } : {}
|
|
1535
|
-
}));
|
|
1536
|
-
const extractorsToRun = partialCacheHit ? missingExtractors : applicableExtractors;
|
|
1537
|
-
for (const ex of extractorsToRun) {
|
|
1538
|
-
freshlyRunTuples.add(`${ex.pluginId}/${ex.id}/${node.path}`);
|
|
1539
|
-
}
|
|
1540
|
-
const extractResult = await runExtractorsForNode({
|
|
1541
|
-
extractors: extractorsToRun,
|
|
1542
|
-
node,
|
|
1543
|
-
body: raw.body,
|
|
1544
|
-
frontmatter: raw.frontmatter,
|
|
1545
|
-
bodyHash,
|
|
1546
|
-
emitter,
|
|
1547
|
-
...pluginStores ? { pluginStores } : {}
|
|
1548
|
-
});
|
|
1549
|
-
for (const link of extractResult.internalLinks) internalLinks.push(link);
|
|
1550
|
-
for (const link of extractResult.externalLinks) externalLinks.push(link);
|
|
1551
|
-
for (const enr of extractResult.enrichments) {
|
|
1552
|
-
enrichmentBuffer.set(`${enr.nodePath}\0${enr.extractorId}`, enr);
|
|
1553
|
-
}
|
|
1554
|
-
for (const c of extractResult.contributions) contributionsBuffer.push(c);
|
|
1555
|
-
const ranAt = Date.now();
|
|
1556
|
-
for (const ex of applicableExtractors) {
|
|
1557
|
-
const qualified = qualifiedExtensionId(ex.pluginId, ex.id);
|
|
1558
|
-
extractorRuns.push({
|
|
1559
|
-
nodePath: node.path,
|
|
1560
|
-
extractorId: qualified,
|
|
1561
|
-
bodyHashAtRun: bodyHash,
|
|
1562
|
-
ranAt
|
|
1563
|
-
});
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
862
|
+
for (const link of externalLinks) {
|
|
863
|
+
const source = byPath2.get(link.source);
|
|
864
|
+
if (source && !cachedPaths.has(source.path)) source.externalRefsCount += 1;
|
|
1566
865
|
}
|
|
1567
|
-
const orphanSidecars = discoverOrphanSidecars(roots);
|
|
1568
|
-
return {
|
|
1569
|
-
nodes,
|
|
1570
|
-
internalLinks,
|
|
1571
|
-
externalLinks,
|
|
1572
|
-
cachedPaths,
|
|
1573
|
-
frontmatterIssues,
|
|
1574
|
-
filesWalked,
|
|
1575
|
-
enrichments: [...enrichmentBuffer.values()],
|
|
1576
|
-
extractorRuns,
|
|
1577
|
-
contributions: contributionsBuffer,
|
|
1578
|
-
freshlyRunTuples,
|
|
1579
|
-
orphanSidecars,
|
|
1580
|
-
sidecarRoots
|
|
1581
|
-
};
|
|
1582
866
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
const obsoleteSources = [];
|
|
1587
|
-
let hasMissing = false;
|
|
1588
|
-
for (const source of link.sources) {
|
|
1589
|
-
const candidates = shortIdToQualified.get(source);
|
|
1590
|
-
if (!candidates || candidates.length === 0) {
|
|
1591
|
-
obsoleteSources.push(source);
|
|
1592
|
-
continue;
|
|
1593
|
-
}
|
|
1594
|
-
if (candidates.some((q) => cachedQualifiedIds.has(q))) {
|
|
1595
|
-
cachedSources.push(source);
|
|
1596
|
-
continue;
|
|
1597
|
-
}
|
|
1598
|
-
if (candidates.some((q) => applicableQualifiedIds.has(q))) {
|
|
1599
|
-
hasMissing = true;
|
|
1600
|
-
continue;
|
|
1601
|
-
}
|
|
1602
|
-
obsoleteSources.push(source);
|
|
1603
|
-
}
|
|
1604
|
-
if (hasMissing) return null;
|
|
1605
|
-
if (cachedSources.length === 0) return null;
|
|
1606
|
-
if (obsoleteSources.length === 0) return link;
|
|
1607
|
-
return { ...link, sources: cachedSources };
|
|
867
|
+
var EXTERNAL_URL_SCHEME_RE = /^[a-z][a-z0-9+\-.]+:/i;
|
|
868
|
+
function isExternalUrlLink(link) {
|
|
869
|
+
return EXTERNAL_URL_SCHEME_RE.test(link.target);
|
|
1608
870
|
}
|
|
871
|
+
|
|
872
|
+
// kernel/orchestrator/analyzers.ts
|
|
1609
873
|
async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, emitter, hookDispatcher) {
|
|
1610
874
|
const issues = [];
|
|
1611
875
|
const contributions = [];
|
|
@@ -1681,12 +945,187 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
1681
945
|
}
|
|
1682
946
|
return { issues, contributions };
|
|
1683
947
|
}
|
|
948
|
+
function validateIssue(analyzer, issue, emitter) {
|
|
949
|
+
const severity = issue.severity;
|
|
950
|
+
if (severity !== "error" && severity !== "warn" && severity !== "info") {
|
|
951
|
+
const qualifiedId = `${analyzer.pluginId}/${analyzer.id}`;
|
|
952
|
+
emitter.emit(
|
|
953
|
+
makeEvent("extension.error", {
|
|
954
|
+
kind: "issue-invalid-severity",
|
|
955
|
+
extensionId: qualifiedId,
|
|
956
|
+
severity,
|
|
957
|
+
issue: { analyzerId: issue.analyzerId || analyzer.id, message: issue.message, nodeIds: issue.nodeIds },
|
|
958
|
+
message: tx(ORCHESTRATOR_TEXTS.extensionErrorIssueInvalidSeverity, {
|
|
959
|
+
analyzerId: qualifiedId,
|
|
960
|
+
severity: JSON.stringify(severity)
|
|
961
|
+
})
|
|
962
|
+
})
|
|
963
|
+
);
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
return { ...issue, analyzerId: issue.analyzerId || analyzer.id };
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// kernel/orchestrator/cache.ts
|
|
970
|
+
function indexPriorSnapshot(prior) {
|
|
971
|
+
const priorNodesByPath = /* @__PURE__ */ new Map();
|
|
972
|
+
const priorNodePaths = /* @__PURE__ */ new Set();
|
|
973
|
+
const priorLinksByOriginating = /* @__PURE__ */ new Map();
|
|
974
|
+
const priorFrontmatterIssuesByNode = /* @__PURE__ */ new Map();
|
|
975
|
+
if (!prior) {
|
|
976
|
+
return { priorNodesByPath, priorNodePaths, priorLinksByOriginating, priorFrontmatterIssuesByNode };
|
|
977
|
+
}
|
|
978
|
+
indexPriorNodes(prior.nodes, priorNodesByPath, priorNodePaths);
|
|
979
|
+
indexPriorLinks(prior.links, priorNodePaths, priorLinksByOriginating);
|
|
980
|
+
indexPriorFrontmatterIssues(prior.issues, priorFrontmatterIssuesByNode);
|
|
981
|
+
return { priorNodesByPath, priorNodePaths, priorLinksByOriginating, priorFrontmatterIssuesByNode };
|
|
982
|
+
}
|
|
983
|
+
function indexPriorNodes(nodes, byPath2, paths) {
|
|
984
|
+
for (const node of nodes) {
|
|
985
|
+
byPath2.set(node.path, node);
|
|
986
|
+
paths.add(node.path);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
function indexPriorLinks(links, priorNodePaths, byOriginating) {
|
|
990
|
+
for (const link of links) {
|
|
991
|
+
const key = originatingNodeOf(link, priorNodePaths);
|
|
992
|
+
const list = byOriginating.get(key);
|
|
993
|
+
if (list) list.push(link);
|
|
994
|
+
else byOriginating.set(key, [link]);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
function indexPriorFrontmatterIssues(issues, byNode) {
|
|
998
|
+
for (const issue of issues) {
|
|
999
|
+
if (issue.analyzerId !== "frontmatter-invalid" && issue.analyzerId !== "frontmatter-malformed") continue;
|
|
1000
|
+
if (issue.nodeIds.length !== 1) continue;
|
|
1001
|
+
const path = issue.nodeIds[0];
|
|
1002
|
+
const list = byNode.get(path);
|
|
1003
|
+
if (list) list.push(issue);
|
|
1004
|
+
else byNode.set(path, [issue]);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1684
1007
|
function originatingNodeOf(link, priorNodePaths) {
|
|
1685
1008
|
if (link.kind === "supersedes" && !priorNodePaths.has(link.source)) {
|
|
1686
1009
|
return link.target;
|
|
1687
1010
|
}
|
|
1688
1011
|
return link.source;
|
|
1689
1012
|
}
|
|
1013
|
+
function computeCacheDecision(opts) {
|
|
1014
|
+
const applicableExtractors = opts.extractors.filter(
|
|
1015
|
+
(ex) => ex.applicableKinds === void 0 || ex.applicableKinds.includes(opts.kind)
|
|
1016
|
+
);
|
|
1017
|
+
const applicableQualifiedIds = new Set(
|
|
1018
|
+
applicableExtractors.map((ex) => qualifiedExtensionId(ex.pluginId, ex.id))
|
|
1019
|
+
);
|
|
1020
|
+
const split = opts.priorExtractorRuns === void 0 ? splitLegacy(applicableExtractors, applicableQualifiedIds, opts.nodeHashCacheEligible) : splitFineGrained(applicableExtractors, opts);
|
|
1021
|
+
return {
|
|
1022
|
+
applicableExtractors,
|
|
1023
|
+
applicableQualifiedIds,
|
|
1024
|
+
cachedQualifiedIds: split.cachedQualifiedIds,
|
|
1025
|
+
missingExtractors: split.missingExtractors,
|
|
1026
|
+
fullCacheHit: opts.nodeHashCacheEligible && split.missingExtractors.length === 0
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
function splitLegacy(applicableExtractors, applicableQualifiedIds, nodeHashCacheEligible) {
|
|
1030
|
+
const cachedQualifiedIds = /* @__PURE__ */ new Set();
|
|
1031
|
+
const missingExtractors = [];
|
|
1032
|
+
if (nodeHashCacheEligible) {
|
|
1033
|
+
for (const id of applicableQualifiedIds) cachedQualifiedIds.add(id);
|
|
1034
|
+
} else {
|
|
1035
|
+
for (const ex of applicableExtractors) missingExtractors.push(ex);
|
|
1036
|
+
}
|
|
1037
|
+
return { cachedQualifiedIds, missingExtractors };
|
|
1038
|
+
}
|
|
1039
|
+
function splitFineGrained(applicableExtractors, opts) {
|
|
1040
|
+
const cachedQualifiedIds = /* @__PURE__ */ new Set();
|
|
1041
|
+
const missingExtractors = [];
|
|
1042
|
+
const priorRunsForNode = opts.priorExtractorRuns.get(opts.nodePath) ?? /* @__PURE__ */ new Map();
|
|
1043
|
+
for (const ex of applicableExtractors) {
|
|
1044
|
+
const qualified = qualifiedExtensionId(ex.pluginId, ex.id);
|
|
1045
|
+
const prior = priorRunsForNode.get(qualified);
|
|
1046
|
+
if (opts.nodeHashCacheEligible && prior !== void 0 && prior.bodyHash === opts.bodyHash && prior.sidecarAnnotationsHash === opts.sidecarAnnotationsHash) {
|
|
1047
|
+
cachedQualifiedIds.add(qualified);
|
|
1048
|
+
} else {
|
|
1049
|
+
missingExtractors.push(ex);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return { cachedQualifiedIds, missingExtractors };
|
|
1053
|
+
}
|
|
1054
|
+
function cloneNodeAndReshapeLinks(opts) {
|
|
1055
|
+
const node = { ...opts.priorNode, bytes: { ...opts.priorNode.bytes } };
|
|
1056
|
+
if (opts.priorNode.tokens) node.tokens = { ...opts.priorNode.tokens };
|
|
1057
|
+
const internalLinks = [];
|
|
1058
|
+
const reusedLinks = opts.priorLinksByOriginating.get(opts.priorNode.path) ?? [];
|
|
1059
|
+
for (const link of reusedLinks) {
|
|
1060
|
+
const reshaped = reuseCachedLink(
|
|
1061
|
+
link,
|
|
1062
|
+
opts.shortIdToQualified,
|
|
1063
|
+
opts.cachedQualifiedIds,
|
|
1064
|
+
opts.applicableQualifiedIds
|
|
1065
|
+
);
|
|
1066
|
+
if (reshaped) internalLinks.push(reshaped);
|
|
1067
|
+
}
|
|
1068
|
+
const frontmatterIssues = [];
|
|
1069
|
+
const reusedFm = opts.priorFrontmatterIssuesByNode.get(opts.priorNode.path) ?? [];
|
|
1070
|
+
for (const issue of reusedFm) {
|
|
1071
|
+
frontmatterIssues.push({ ...issue, severity: opts.strict ? "error" : "warn" });
|
|
1072
|
+
}
|
|
1073
|
+
return { node, internalLinks, frontmatterIssues };
|
|
1074
|
+
}
|
|
1075
|
+
function reusePriorNode(opts) {
|
|
1076
|
+
const base = cloneNodeAndReshapeLinks(opts);
|
|
1077
|
+
const ranAt = Date.now();
|
|
1078
|
+
const extractorRuns = [];
|
|
1079
|
+
for (const qualified of opts.cachedQualifiedIds) {
|
|
1080
|
+
extractorRuns.push({
|
|
1081
|
+
nodePath: opts.priorNode.path,
|
|
1082
|
+
extractorId: qualified,
|
|
1083
|
+
bodyHashAtRun: opts.bodyHash,
|
|
1084
|
+
ranAt,
|
|
1085
|
+
sidecarAnnotationsHashAtRun: opts.sidecarAnnotationsHash
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
return { ...base, extractorRuns };
|
|
1089
|
+
}
|
|
1090
|
+
function reuseCachedLink(link, shortIdToQualified, cachedQualifiedIds, applicableQualifiedIds) {
|
|
1091
|
+
if (!Array.isArray(link.sources) || link.sources.length === 0) return null;
|
|
1092
|
+
const partition = partitionLinkSources(
|
|
1093
|
+
link.sources,
|
|
1094
|
+
shortIdToQualified,
|
|
1095
|
+
cachedQualifiedIds,
|
|
1096
|
+
applicableQualifiedIds
|
|
1097
|
+
);
|
|
1098
|
+
if (partition.hasMissing) return null;
|
|
1099
|
+
if (partition.cached.length === 0) return null;
|
|
1100
|
+
if (partition.obsolete.length === 0) return link;
|
|
1101
|
+
return { ...link, sources: partition.cached };
|
|
1102
|
+
}
|
|
1103
|
+
function partitionLinkSources(sources, shortIdToQualified, cachedQualifiedIds, applicableQualifiedIds) {
|
|
1104
|
+
const cached = [];
|
|
1105
|
+
const obsolete = [];
|
|
1106
|
+
let hasMissing = false;
|
|
1107
|
+
for (const source of sources) {
|
|
1108
|
+
const category = classifyLinkSource(
|
|
1109
|
+
source,
|
|
1110
|
+
shortIdToQualified,
|
|
1111
|
+
cachedQualifiedIds,
|
|
1112
|
+
applicableQualifiedIds
|
|
1113
|
+
);
|
|
1114
|
+
if (category === "cached") cached.push(source);
|
|
1115
|
+
else if (category === "missing") hasMissing = true;
|
|
1116
|
+
else obsolete.push(source);
|
|
1117
|
+
}
|
|
1118
|
+
return { cached, obsolete, hasMissing };
|
|
1119
|
+
}
|
|
1120
|
+
function classifyLinkSource(source, shortIdToQualified, cachedQualifiedIds, applicableQualifiedIds) {
|
|
1121
|
+
const candidates = shortIdToQualified.get(source);
|
|
1122
|
+
if (!candidates || candidates.length === 0) return "obsolete";
|
|
1123
|
+
if (candidates.some((q) => cachedQualifiedIds.has(q))) return "cached";
|
|
1124
|
+
if (candidates.some((q) => applicableQualifiedIds.has(q))) return "missing";
|
|
1125
|
+
return "obsolete";
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// kernel/orchestrator/renames.ts
|
|
1690
1129
|
function findHighConfidenceRenames(opts) {
|
|
1691
1130
|
const ops = [];
|
|
1692
1131
|
for (const fromPath of opts.deletedPaths) {
|
|
@@ -1811,148 +1250,389 @@ function detectRenamesAndOrphans(prior, current, issues) {
|
|
|
1811
1250
|
flagOrphans({ deletedPaths, claimedDeleted, issues });
|
|
1812
1251
|
return ops;
|
|
1813
1252
|
}
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
frontmatter: bytesFrontmatter,
|
|
1829
|
-
body: bytesBody,
|
|
1830
|
-
total: bytesFrontmatter + bytesBody
|
|
1831
|
-
},
|
|
1832
|
-
linksOutCount: 0,
|
|
1833
|
-
linksInCount: 0,
|
|
1834
|
-
externalRefsCount: 0,
|
|
1835
|
-
frontmatter: args.frontmatter
|
|
1836
|
-
};
|
|
1837
|
-
if (args.encoder) {
|
|
1838
|
-
node.tokens = countTokens(args.encoder, args.frontmatterRaw, args.body);
|
|
1839
|
-
}
|
|
1840
|
-
return node;
|
|
1841
|
-
}
|
|
1842
|
-
function countTokens(encoder, frontmatterRaw, body) {
|
|
1843
|
-
const frontmatter = frontmatterRaw.length > 0 ? encoder.encode(frontmatterRaw).length : 0;
|
|
1844
|
-
const bodyTokens = body.length > 0 ? encoder.encode(body).length : 0;
|
|
1845
|
-
return { frontmatter, body: bodyTokens, total: frontmatter + bodyTokens };
|
|
1846
|
-
}
|
|
1847
|
-
function sha256(input) {
|
|
1848
|
-
return createHash("sha256").update(input, "utf8").digest("hex");
|
|
1849
|
-
}
|
|
1850
|
-
function canonicalFrontmatter(parsed, raw) {
|
|
1851
|
-
const hasParsedKeys = Object.keys(parsed).length > 0;
|
|
1852
|
-
const hasRawText = raw.length > 0;
|
|
1853
|
-
if (!hasParsedKeys && hasRawText) {
|
|
1854
|
-
return raw;
|
|
1253
|
+
|
|
1254
|
+
// kernel/scan/walk-content.ts
|
|
1255
|
+
import { readFile, readdir, stat } from "fs/promises";
|
|
1256
|
+
import { join as join2, relative as relative2, sep } from "path";
|
|
1257
|
+
|
|
1258
|
+
// kernel/scan/ignore.ts
|
|
1259
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
|
|
1260
|
+
import { dirname as dirname2, resolve as resolve5 } from "path";
|
|
1261
|
+
import { fileURLToPath } from "url";
|
|
1262
|
+
import ignoreFactory from "ignore";
|
|
1263
|
+
function buildIgnoreFilter(opts = {}) {
|
|
1264
|
+
const ig = ignoreFactory();
|
|
1265
|
+
if (opts.includeDefaults !== false) {
|
|
1266
|
+
ig.add(loadDefaultsText());
|
|
1855
1267
|
}
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
lineWidth: -1,
|
|
1859
|
-
noRefs: true,
|
|
1860
|
-
noCompatMode: true
|
|
1861
|
-
});
|
|
1862
|
-
}
|
|
1863
|
-
function resolveAndApplySidecar(node, relativePath, roots, liveBodyHash, liveFrontmatterHash, sidecarRoots) {
|
|
1864
|
-
const issues = [];
|
|
1865
|
-
const mdAbs = resolveAbsoluteMdPath(relativePath, roots);
|
|
1866
|
-
if (mdAbs === null) {
|
|
1867
|
-
node.sidecar = { present: false };
|
|
1868
|
-
return issues;
|
|
1268
|
+
if (opts.configIgnore && opts.configIgnore.length > 0) {
|
|
1269
|
+
ig.add(opts.configIgnore);
|
|
1869
1270
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
node.sidecar = { present: false };
|
|
1873
|
-
return issues;
|
|
1271
|
+
if (opts.ignoreFileText && opts.ignoreFileText.length > 0) {
|
|
1272
|
+
ig.add(opts.ignoreFileText);
|
|
1874
1273
|
}
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
data: { sidecarPath: relativePathFromRoots(mdAbs, roots) }
|
|
1884
|
-
});
|
|
1274
|
+
return {
|
|
1275
|
+
ignores(relativePath) {
|
|
1276
|
+
if (relativePath === "" || relativePath === "." || relativePath === "./") {
|
|
1277
|
+
return false;
|
|
1278
|
+
}
|
|
1279
|
+
const normalised = relativePath.replace(/^\.\//, "").replace(/\\/g, "/").replace(/^\//, "");
|
|
1280
|
+
if (normalised === "") return false;
|
|
1281
|
+
return ig.ignores(normalised);
|
|
1885
1282
|
}
|
|
1886
|
-
return issues;
|
|
1887
|
-
}
|
|
1888
|
-
const status = computeDriftStatus({
|
|
1889
|
-
storedBodyHash: result.parsed.identityBodyHash,
|
|
1890
|
-
storedFrontmatterHash: result.parsed.identityFrontmatterHash,
|
|
1891
|
-
liveBodyHash,
|
|
1892
|
-
liveFrontmatterHash
|
|
1893
|
-
});
|
|
1894
|
-
node.sidecar = {
|
|
1895
|
-
present: true,
|
|
1896
|
-
status,
|
|
1897
|
-
annotations: result.parsed.annotations,
|
|
1898
|
-
root: result.parsed.raw
|
|
1899
1283
|
};
|
|
1900
|
-
sidecarRoots.set(node.path, result.parsed.raw);
|
|
1901
|
-
return issues;
|
|
1902
1284
|
}
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
const candidate = resolvePath(root, relativePath);
|
|
1909
|
-
if (existsSync6(candidate)) return candidate;
|
|
1910
|
-
}
|
|
1911
|
-
return null;
|
|
1285
|
+
var cachedDefaults = null;
|
|
1286
|
+
function loadDefaultsText() {
|
|
1287
|
+
if (cachedDefaults !== null) return cachedDefaults;
|
|
1288
|
+
cachedDefaults = readDefaultsFromDisk();
|
|
1289
|
+
return cachedDefaults;
|
|
1912
1290
|
}
|
|
1913
|
-
function
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1291
|
+
function readDefaultsFromDisk() {
|
|
1292
|
+
const here = dirname2(fileURLToPath(import.meta.url));
|
|
1293
|
+
const candidates = [
|
|
1294
|
+
resolve5(here, "../../config/defaults/skillmapignore"),
|
|
1295
|
+
// src/kernel/scan/ → src/config/defaults/
|
|
1296
|
+
resolve5(here, "../config/defaults/skillmapignore"),
|
|
1297
|
+
// dist/cli.js → dist/config/defaults/ (siblings)
|
|
1298
|
+
resolve5(here, "config/defaults/skillmapignore")
|
|
1299
|
+
];
|
|
1300
|
+
for (const candidate of candidates) {
|
|
1301
|
+
if (existsSync2(candidate)) {
|
|
1302
|
+
try {
|
|
1303
|
+
return readFileSync4(candidate, "utf8");
|
|
1304
|
+
} catch {
|
|
1305
|
+
}
|
|
1918
1306
|
}
|
|
1919
1307
|
}
|
|
1920
|
-
return
|
|
1308
|
+
return "";
|
|
1921
1309
|
}
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1310
|
+
|
|
1311
|
+
// built-in-plugins/parsers/frontmatter-yaml/index.ts
|
|
1312
|
+
import yaml from "js-yaml";
|
|
1313
|
+
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
1314
|
+
var FORBIDDEN_FRONTMATTER_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1315
|
+
var frontmatterYamlParser = {
|
|
1316
|
+
id: "frontmatter-yaml",
|
|
1317
|
+
parse(raw, _path) {
|
|
1318
|
+
const match = FRONTMATTER_RE.exec(raw);
|
|
1319
|
+
if (!match) return { frontmatterRaw: "", frontmatter: {}, body: raw };
|
|
1320
|
+
const frontmatterRaw = match[1];
|
|
1321
|
+
const body = match[2];
|
|
1322
|
+
const parsed = {};
|
|
1323
|
+
try {
|
|
1324
|
+
const doc = yaml.load(frontmatterRaw, { schema: yaml.JSON_SCHEMA });
|
|
1325
|
+
if (doc && typeof doc === "object" && !Array.isArray(doc)) {
|
|
1326
|
+
for (const [k, v] of Object.entries(doc)) {
|
|
1327
|
+
if (FORBIDDEN_FRONTMATTER_KEYS.has(k)) continue;
|
|
1328
|
+
parsed[k] = v;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
} catch {
|
|
1332
|
+
}
|
|
1333
|
+
return { frontmatterRaw, frontmatter: parsed, body };
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
|
|
1337
|
+
// built-in-plugins/parsers/plain/index.ts
|
|
1338
|
+
var plainParser = {
|
|
1339
|
+
id: "plain",
|
|
1340
|
+
parse(raw, _path) {
|
|
1341
|
+
return { frontmatter: {}, frontmatterRaw: "", body: raw };
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
// kernel/scan/parsers/index.ts
|
|
1346
|
+
var REGISTRY = /* @__PURE__ */ new Map([
|
|
1347
|
+
[frontmatterYamlParser.id, frontmatterYamlParser],
|
|
1348
|
+
[plainParser.id, plainParser]
|
|
1349
|
+
]);
|
|
1350
|
+
var FROZEN_IDS = new Set(REGISTRY.keys());
|
|
1351
|
+
function getParser(id) {
|
|
1352
|
+
return REGISTRY.get(id);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
// kernel/scan/walk-content.ts
|
|
1356
|
+
var UnknownParserError = class extends Error {
|
|
1357
|
+
constructor(parserId) {
|
|
1358
|
+
super(`Unknown parser id '${parserId}'. Built-in parsers: 'frontmatter-yaml', 'plain'.`);
|
|
1359
|
+
this.name = "UnknownParserError";
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
async function* walkContent(roots, options) {
|
|
1363
|
+
const parser = getParser(options.parser);
|
|
1364
|
+
if (!parser) throw new UnknownParserError(options.parser);
|
|
1365
|
+
const filter = options.ignoreFilter ?? buildIgnoreFilter();
|
|
1366
|
+
const extensions = options.extensions;
|
|
1367
|
+
for (const root of roots) {
|
|
1368
|
+
for await (const file of walkRoot(root, root, filter, extensions)) {
|
|
1369
|
+
const relPath = relative2(root, file).split(sep).join("/");
|
|
1370
|
+
let raw;
|
|
1371
|
+
try {
|
|
1372
|
+
raw = await readFile(file, "utf8");
|
|
1373
|
+
} catch {
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
const parsed = parser.parse(raw, relPath);
|
|
1377
|
+
yield {
|
|
1378
|
+
path: relPath,
|
|
1379
|
+
body: parsed.body,
|
|
1380
|
+
frontmatterRaw: parsed.frontmatterRaw,
|
|
1381
|
+
frontmatter: parsed.frontmatter
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
async function* walkRoot(root, current, filter, extensions) {
|
|
1387
|
+
let entries;
|
|
1388
|
+
try {
|
|
1389
|
+
entries = await readdir(current, { withFileTypes: true, encoding: "utf8" });
|
|
1390
|
+
} catch {
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
for (const entry of entries) {
|
|
1394
|
+
const name = entry.name;
|
|
1395
|
+
const full = join2(current, name);
|
|
1396
|
+
const rel = relative2(root, full).split(sep).join("/");
|
|
1397
|
+
if (filter.ignores(rel)) continue;
|
|
1398
|
+
if (entry.isSymbolicLink()) continue;
|
|
1399
|
+
if (entry.isDirectory()) {
|
|
1400
|
+
yield* walkRoot(root, full, filter, extensions);
|
|
1401
|
+
} else if (entry.isFile() && hasMatchingExtension(name, extensions)) {
|
|
1402
|
+
try {
|
|
1403
|
+
const s = await stat(full);
|
|
1404
|
+
if (s.isFile()) yield full;
|
|
1405
|
+
} catch {
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
function hasMatchingExtension(name, extensions) {
|
|
1411
|
+
for (const ext of extensions) {
|
|
1412
|
+
if (name.endsWith(ext)) return true;
|
|
1413
|
+
}
|
|
1414
|
+
return false;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// kernel/extensions/provider.ts
|
|
1418
|
+
var DEFAULT_READ_CONFIG = Object.freeze({
|
|
1419
|
+
extensions: Object.freeze([".md"]),
|
|
1420
|
+
parser: "frontmatter-yaml"
|
|
1421
|
+
});
|
|
1422
|
+
function resolveProviderWalk(provider) {
|
|
1423
|
+
if (provider.walk) {
|
|
1424
|
+
const walk2 = provider.walk.bind(provider);
|
|
1425
|
+
return walk2;
|
|
1426
|
+
}
|
|
1427
|
+
const read = provider.read ?? DEFAULT_READ_CONFIG;
|
|
1428
|
+
return (roots, options) => {
|
|
1429
|
+
const walkOptions = {
|
|
1430
|
+
extensions: read.extensions,
|
|
1431
|
+
parser: read.parser
|
|
1432
|
+
};
|
|
1433
|
+
if (options?.ignoreFilter) walkOptions.ignoreFilter = options.ignoreFilter;
|
|
1434
|
+
return walkContent(roots, walkOptions);
|
|
1932
1435
|
};
|
|
1933
1436
|
}
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1437
|
+
|
|
1438
|
+
// kernel/sidecar/parse.ts
|
|
1439
|
+
import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
|
|
1440
|
+
import { dirname as dirname3, resolve as resolve6 } from "path";
|
|
1441
|
+
import { createRequire as createRequire3 } from "module";
|
|
1442
|
+
import { Ajv2020 as Ajv20204 } from "ajv/dist/2020.js";
|
|
1443
|
+
import yaml2 from "js-yaml";
|
|
1444
|
+
function readSidecarFor(mdAbsolutePath) {
|
|
1445
|
+
const sidecarPath = sidecarPathFor(mdAbsolutePath);
|
|
1446
|
+
if (!existsSync3(sidecarPath)) {
|
|
1447
|
+
return { parsed: null, present: false, issues: [] };
|
|
1448
|
+
}
|
|
1449
|
+
let raw;
|
|
1450
|
+
try {
|
|
1451
|
+
raw = readFileSync5(sidecarPath, "utf8");
|
|
1452
|
+
} catch (err) {
|
|
1453
|
+
return {
|
|
1454
|
+
parsed: null,
|
|
1455
|
+
present: true,
|
|
1456
|
+
issues: [{ message: `cannot read ${sidecarPath}: ${err.message}` }]
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
let parsedYaml;
|
|
1460
|
+
try {
|
|
1461
|
+
parsedYaml = yaml2.load(raw);
|
|
1462
|
+
} catch (err) {
|
|
1463
|
+
return {
|
|
1464
|
+
parsed: null,
|
|
1465
|
+
present: true,
|
|
1466
|
+
issues: [{ message: `malformed YAML in ${sidecarPath}: ${err.message}` }]
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
if (!isPlainObject(parsedYaml)) {
|
|
1470
|
+
return {
|
|
1471
|
+
parsed: null,
|
|
1472
|
+
present: true,
|
|
1473
|
+
issues: [{ message: `sidecar root must be a YAML mapping at ${sidecarPath}` }]
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
const sidecarValidator = getSidecarValidator();
|
|
1477
|
+
if (!sidecarValidator(parsedYaml)) {
|
|
1478
|
+
const errors = (sidecarValidator.errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
|
|
1479
|
+
return {
|
|
1480
|
+
parsed: null,
|
|
1481
|
+
present: true,
|
|
1482
|
+
issues: [{ message: `sidecar schema validation failed at ${sidecarPath}: ${errors}` }]
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
const root = parsedYaml;
|
|
1486
|
+
const identityBlock = root["identity"];
|
|
1487
|
+
const annotationsRaw = root["annotations"];
|
|
1488
|
+
const annotations = isPlainObject(annotationsRaw) ? Object.keys(annotationsRaw).length === 0 ? null : annotationsRaw : null;
|
|
1489
|
+
return {
|
|
1490
|
+
parsed: {
|
|
1491
|
+
filePath: sidecarPath,
|
|
1492
|
+
identityBodyHash: String(identityBlock["bodyHash"]),
|
|
1493
|
+
identityFrontmatterHash: String(identityBlock["frontmatterHash"]),
|
|
1494
|
+
identityPath: String(identityBlock["path"]),
|
|
1495
|
+
annotations,
|
|
1496
|
+
raw: root
|
|
1497
|
+
},
|
|
1498
|
+
present: true,
|
|
1499
|
+
issues: []
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
function sidecarPathFor(mdAbsolutePath) {
|
|
1503
|
+
if (mdAbsolutePath.endsWith(".md")) {
|
|
1504
|
+
return `${mdAbsolutePath.slice(0, -".md".length)}.sm`;
|
|
1505
|
+
}
|
|
1506
|
+
return `${mdAbsolutePath}.sm`;
|
|
1507
|
+
}
|
|
1508
|
+
function isPlainObject(value) {
|
|
1509
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1510
|
+
}
|
|
1511
|
+
var cachedSidecarValidator = null;
|
|
1512
|
+
function getSidecarValidator() {
|
|
1513
|
+
if (cachedSidecarValidator) return cachedSidecarValidator;
|
|
1514
|
+
const ajv = new Ajv20204({ strict: false, allErrors: true, allowUnionTypes: true });
|
|
1515
|
+
applyAjvFormats(ajv);
|
|
1516
|
+
const specRoot = resolveSpecRoot2();
|
|
1517
|
+
const annotationsSchema = JSON.parse(
|
|
1518
|
+
readFileSync5(resolve6(specRoot, "schemas/annotations.schema.json"), "utf8")
|
|
1519
|
+
);
|
|
1520
|
+
const sidecarSchema = JSON.parse(
|
|
1521
|
+
readFileSync5(resolve6(specRoot, "schemas/sidecar.schema.json"), "utf8")
|
|
1522
|
+
);
|
|
1523
|
+
ajv.addSchema(annotationsSchema);
|
|
1524
|
+
cachedSidecarValidator = ajv.compile(sidecarSchema);
|
|
1525
|
+
return cachedSidecarValidator;
|
|
1526
|
+
}
|
|
1527
|
+
function resolveSpecRoot2() {
|
|
1528
|
+
const require2 = createRequire3(import.meta.url);
|
|
1529
|
+
try {
|
|
1530
|
+
const indexPath = require2.resolve("@skill-map/spec/index.json");
|
|
1531
|
+
return dirname3(indexPath);
|
|
1532
|
+
} catch {
|
|
1533
|
+
throw new Error(
|
|
1534
|
+
"@skill-map/spec not resolvable \u2014 sidecar reader cannot load schemas."
|
|
1950
1535
|
);
|
|
1951
|
-
return null;
|
|
1952
1536
|
}
|
|
1953
|
-
const confidence = link.confidence ?? extractor.defaultConfidence;
|
|
1954
|
-
return { ...link, confidence };
|
|
1955
1537
|
}
|
|
1538
|
+
|
|
1539
|
+
// kernel/sidecar/drift.ts
|
|
1540
|
+
function computeDriftStatus(args) {
|
|
1541
|
+
const bodyDrift = args.storedBodyHash !== args.liveBodyHash;
|
|
1542
|
+
const fmDrift = args.storedFrontmatterHash !== args.liveFrontmatterHash;
|
|
1543
|
+
if (bodyDrift && fmDrift) return "stale-both";
|
|
1544
|
+
if (bodyDrift) return "stale-body";
|
|
1545
|
+
if (fmDrift) return "stale-frontmatter";
|
|
1546
|
+
return "fresh";
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
// kernel/sidecar/discover-orphans.ts
|
|
1550
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, statSync } from "fs";
|
|
1551
|
+
import { join as join3, relative as relative3, sep as sep2 } from "path";
|
|
1552
|
+
function discoverOrphanSidecars(roots, shouldSkip) {
|
|
1553
|
+
const out = [];
|
|
1554
|
+
for (const root of roots) {
|
|
1555
|
+
walk(root, root, shouldSkip ?? (() => false), out);
|
|
1556
|
+
}
|
|
1557
|
+
return out;
|
|
1558
|
+
}
|
|
1559
|
+
function walk(root, current, shouldSkip, out) {
|
|
1560
|
+
let entries;
|
|
1561
|
+
try {
|
|
1562
|
+
entries = readdirSync2(current, { withFileTypes: true, encoding: "utf8" });
|
|
1563
|
+
} catch {
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
for (const entry of entries) {
|
|
1567
|
+
const full = join3(current, entry.name);
|
|
1568
|
+
const rel = relative3(root, full).split(sep2).join("/");
|
|
1569
|
+
if (shouldSkip(rel)) continue;
|
|
1570
|
+
if (entry.isSymbolicLink()) continue;
|
|
1571
|
+
if (entry.isDirectory()) {
|
|
1572
|
+
walk(root, full, shouldSkip, out);
|
|
1573
|
+
continue;
|
|
1574
|
+
}
|
|
1575
|
+
if (!entry.isFile()) continue;
|
|
1576
|
+
if (!entry.name.endsWith(".sm")) continue;
|
|
1577
|
+
const expectedMd = `${full.slice(0, -".sm".length)}.md`;
|
|
1578
|
+
if (existsSync4(expectedMd) && safeIsFile(expectedMd)) continue;
|
|
1579
|
+
out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
function safeIsFile(path) {
|
|
1583
|
+
try {
|
|
1584
|
+
return statSync(path).isFile();
|
|
1585
|
+
} catch {
|
|
1586
|
+
return false;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// kernel/sidecar/store.ts
|
|
1591
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, renameSync as renameSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
1592
|
+
import { dirname as dirname5, resolve as resolve9 } from "path";
|
|
1593
|
+
import { createRequire as createRequire4 } from "module";
|
|
1594
|
+
import { Ajv2020 as Ajv20205 } from "ajv/dist/2020.js";
|
|
1595
|
+
import yaml3 from "js-yaml";
|
|
1596
|
+
|
|
1597
|
+
// core/config/helper.ts
|
|
1598
|
+
import { isAbsolute as isAbsolute2, resolve as resolve8 } from "path";
|
|
1599
|
+
|
|
1600
|
+
// kernel/config/loader.ts
|
|
1601
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
1602
|
+
|
|
1603
|
+
// kernel/util/skill-map-paths.ts
|
|
1604
|
+
import { join as join5 } from "path";
|
|
1605
|
+
|
|
1606
|
+
// core/paths/db-path.ts
|
|
1607
|
+
import { join as join4, resolve as resolve7 } from "path";
|
|
1608
|
+
var SKILL_MAP_DIR = ".skill-map";
|
|
1609
|
+
var DB_FILENAME = "skill-map.db";
|
|
1610
|
+
var LOCAL_SETTINGS_FILENAME = "settings.local.json";
|
|
1611
|
+
var DEFAULT_DB_REL = `${SKILL_MAP_DIR}/${DB_FILENAME}`;
|
|
1612
|
+
var GITIGNORE_ENTRIES = [
|
|
1613
|
+
`${SKILL_MAP_DIR}/${LOCAL_SETTINGS_FILENAME}`,
|
|
1614
|
+
`${SKILL_MAP_DIR}/${DB_FILENAME}`
|
|
1615
|
+
];
|
|
1616
|
+
|
|
1617
|
+
// core/config/atomic-write.ts
|
|
1618
|
+
import {
|
|
1619
|
+
existsSync as existsSync6,
|
|
1620
|
+
mkdirSync,
|
|
1621
|
+
readFileSync as readFileSync7,
|
|
1622
|
+
renameSync,
|
|
1623
|
+
unlinkSync,
|
|
1624
|
+
writeFileSync
|
|
1625
|
+
} from "fs";
|
|
1626
|
+
import { dirname as dirname4 } from "path";
|
|
1627
|
+
|
|
1628
|
+
// kernel/orchestrator/node-build.ts
|
|
1629
|
+
import { createHash } from "crypto";
|
|
1630
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1631
|
+
import { isAbsolute as isAbsolute3, resolve as resolvePath } from "path";
|
|
1632
|
+
import "js-tiktoken/lite";
|
|
1633
|
+
import yaml4 from "js-yaml";
|
|
1634
|
+
|
|
1635
|
+
// kernel/orchestrator/frontmatter.ts
|
|
1956
1636
|
function validateFrontmatter(providerFrontmatter, provider, kind, frontmatter, path, strict) {
|
|
1957
1637
|
const result = providerFrontmatter.validate(provider, kind, frontmatter);
|
|
1958
1638
|
if (result.ok) return null;
|
|
@@ -2002,50 +1682,160 @@ function malformedMessage(hint, path) {
|
|
|
2002
1682
|
return tx(ORCHESTRATOR_TEXTS.frontmatterMalformedMissingClose, { path });
|
|
2003
1683
|
}
|
|
2004
1684
|
}
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
1685
|
+
|
|
1686
|
+
// kernel/orchestrator/node-build.ts
|
|
1687
|
+
function buildNode(args) {
|
|
1688
|
+
const bytesFrontmatter = Buffer.byteLength(args.frontmatterRaw, "utf8");
|
|
1689
|
+
const bytesBody = Buffer.byteLength(args.body, "utf8");
|
|
1690
|
+
const node = {
|
|
1691
|
+
path: args.path,
|
|
1692
|
+
kind: args.kind,
|
|
1693
|
+
provider: args.providerId,
|
|
1694
|
+
bodyHash: args.bodyHash,
|
|
1695
|
+
frontmatterHash: args.frontmatterHash,
|
|
1696
|
+
bytes: {
|
|
1697
|
+
frontmatter: bytesFrontmatter,
|
|
1698
|
+
body: bytesBody,
|
|
1699
|
+
total: bytesFrontmatter + bytesBody
|
|
1700
|
+
},
|
|
1701
|
+
linksOutCount: 0,
|
|
1702
|
+
linksInCount: 0,
|
|
1703
|
+
externalRefsCount: 0,
|
|
1704
|
+
frontmatter: args.frontmatter
|
|
1705
|
+
};
|
|
1706
|
+
if (args.encoder) {
|
|
1707
|
+
node.tokens = countTokens(args.encoder, args.frontmatterRaw, args.body);
|
|
1708
|
+
}
|
|
1709
|
+
return node;
|
|
1710
|
+
}
|
|
1711
|
+
function countTokens(encoder, frontmatterRaw, body) {
|
|
1712
|
+
const frontmatter = frontmatterRaw.length > 0 ? encoder.encode(frontmatterRaw).length : 0;
|
|
1713
|
+
const bodyTokens = body.length > 0 ? encoder.encode(body).length : 0;
|
|
1714
|
+
return { frontmatter, body: bodyTokens, total: frontmatter + bodyTokens };
|
|
1715
|
+
}
|
|
1716
|
+
function sha256(input) {
|
|
1717
|
+
return createHash("sha256").update(input, "utf8").digest("hex");
|
|
1718
|
+
}
|
|
1719
|
+
function canonicalFrontmatter(parsed, raw) {
|
|
1720
|
+
const hasParsedKeys = Object.keys(parsed).length > 0;
|
|
1721
|
+
const hasRawText = raw.length > 0;
|
|
1722
|
+
if (!hasParsedKeys && hasRawText) {
|
|
1723
|
+
return raw;
|
|
1724
|
+
}
|
|
1725
|
+
return yaml4.dump(parsed, {
|
|
1726
|
+
sortKeys: true,
|
|
1727
|
+
lineWidth: -1,
|
|
1728
|
+
noRefs: true,
|
|
1729
|
+
noCompatMode: true
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
function canonicalSidecarAnnotations(annotations) {
|
|
1733
|
+
if (!annotations || typeof annotations !== "object" || Array.isArray(annotations)) {
|
|
1734
|
+
return yaml4.dump({}, { sortKeys: true, lineWidth: -1, noRefs: true, noCompatMode: true });
|
|
1735
|
+
}
|
|
1736
|
+
return yaml4.dump(annotations, {
|
|
1737
|
+
sortKeys: true,
|
|
1738
|
+
lineWidth: -1,
|
|
1739
|
+
noRefs: true,
|
|
1740
|
+
noCompatMode: true
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
function resolveSidecarOverlay(relativePath, nodePathForIssue, roots, liveBodyHash, liveFrontmatterHash) {
|
|
1744
|
+
const issues = [];
|
|
1745
|
+
const mdAbs = resolveAbsoluteMdPath(relativePath, roots);
|
|
1746
|
+
if (mdAbs === null) {
|
|
1747
|
+
return { overlay: { present: false }, issues, parsedRoot: null };
|
|
1748
|
+
}
|
|
1749
|
+
const result = readSidecarFor(mdAbs);
|
|
1750
|
+
if (!result.present) {
|
|
1751
|
+
return { overlay: { present: false }, issues, parsedRoot: null };
|
|
1752
|
+
}
|
|
1753
|
+
if (result.parsed === null) {
|
|
1754
|
+
for (const parseIssue of result.issues) {
|
|
1755
|
+
issues.push({
|
|
1756
|
+
analyzerId: "invalid-sidecar",
|
|
1757
|
+
severity: "warn",
|
|
1758
|
+
nodeIds: [nodePathForIssue],
|
|
1759
|
+
message: parseIssue.message,
|
|
1760
|
+
data: { sidecarPath: relativePathFromRoots(mdAbs, roots) }
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1763
|
+
return {
|
|
1764
|
+
overlay: { present: true, status: null, annotations: null, root: null },
|
|
1765
|
+
issues,
|
|
1766
|
+
parsedRoot: null
|
|
1767
|
+
};
|
|
2022
1768
|
}
|
|
2023
|
-
|
|
1769
|
+
const status = computeDriftStatus({
|
|
1770
|
+
storedBodyHash: result.parsed.identityBodyHash,
|
|
1771
|
+
storedFrontmatterHash: result.parsed.identityFrontmatterHash,
|
|
1772
|
+
liveBodyHash,
|
|
1773
|
+
liveFrontmatterHash
|
|
1774
|
+
});
|
|
1775
|
+
return {
|
|
1776
|
+
// R15 closure (2026-05-07) — surface the full parsed root on the
|
|
1777
|
+
// overlay so BFF consumers (UI inspector audit / plugin-contributions
|
|
1778
|
+
// / debug panels) can read `for.*`, `audit.*`, `settings.*`, and
|
|
1779
|
+
// plugin-namespaced sub-keys without re-reading the file. The
|
|
1780
|
+
// `annotations` field above stays — it duplicates `root.annotations`
|
|
1781
|
+
// by design so existing consumers keep working unchanged.
|
|
1782
|
+
overlay: {
|
|
1783
|
+
present: true,
|
|
1784
|
+
status,
|
|
1785
|
+
annotations: result.parsed.annotations,
|
|
1786
|
+
root: result.parsed.raw
|
|
1787
|
+
},
|
|
1788
|
+
issues,
|
|
1789
|
+
parsedRoot: result.parsed.raw
|
|
1790
|
+
};
|
|
2024
1791
|
}
|
|
2025
|
-
function
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
node.linksOutCount = 0;
|
|
2029
|
-
node.linksInCount = 0;
|
|
2030
|
-
byPath2.set(node.path, node);
|
|
1792
|
+
function resolveAbsoluteMdPath(relativePath, roots) {
|
|
1793
|
+
if (isAbsolute3(relativePath)) {
|
|
1794
|
+
return existsSync8(relativePath) ? relativePath : null;
|
|
2031
1795
|
}
|
|
2032
|
-
for (const
|
|
2033
|
-
const
|
|
2034
|
-
if (
|
|
2035
|
-
const target = byPath2.get(link.target);
|
|
2036
|
-
if (target) target.linksInCount += 1;
|
|
1796
|
+
for (const root of roots) {
|
|
1797
|
+
const candidate = resolvePath(root, relativePath);
|
|
1798
|
+
if (existsSync8(candidate)) return candidate;
|
|
2037
1799
|
}
|
|
1800
|
+
return null;
|
|
2038
1801
|
}
|
|
2039
|
-
function
|
|
2040
|
-
const
|
|
2041
|
-
|
|
2042
|
-
if (
|
|
2043
|
-
|
|
1802
|
+
function relativePathFromRoots(absolutePath, roots) {
|
|
1803
|
+
for (const root of roots) {
|
|
1804
|
+
const abs = resolvePath(root);
|
|
1805
|
+
if (absolutePath.startsWith(`${abs}/`) || absolutePath.startsWith(`${abs}\\`)) {
|
|
1806
|
+
return absolutePath.slice(abs.length + 1).split(/[\\/]/).join("/");
|
|
1807
|
+
}
|
|
2044
1808
|
}
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
1809
|
+
return absolutePath;
|
|
1810
|
+
}
|
|
1811
|
+
function buildFreshNodeAndValidateFrontmatter(opts) {
|
|
1812
|
+
const node = buildNode({
|
|
1813
|
+
path: opts.raw.path,
|
|
1814
|
+
kind: opts.kind,
|
|
1815
|
+
providerId: opts.provider.id,
|
|
1816
|
+
frontmatterRaw: opts.raw.frontmatterRaw,
|
|
1817
|
+
body: opts.raw.body,
|
|
1818
|
+
frontmatter: opts.raw.frontmatter,
|
|
1819
|
+
bodyHash: opts.bodyHash,
|
|
1820
|
+
frontmatterHash: opts.frontmatterHash,
|
|
1821
|
+
encoder: opts.encoder
|
|
1822
|
+
});
|
|
1823
|
+
const frontmatterIssues = [];
|
|
1824
|
+
if (opts.raw.frontmatterRaw.length > 0) {
|
|
1825
|
+
const fmIssue = validateFrontmatter(
|
|
1826
|
+
opts.providerFrontmatter,
|
|
1827
|
+
opts.provider,
|
|
1828
|
+
opts.kind,
|
|
1829
|
+
opts.raw.frontmatter,
|
|
1830
|
+
opts.raw.path,
|
|
1831
|
+
opts.strict
|
|
1832
|
+
);
|
|
1833
|
+
if (fmIssue) frontmatterIssues.push(fmIssue);
|
|
1834
|
+
} else {
|
|
1835
|
+
const malformed = detectMalformedFrontmatter(opts.raw.body, opts.raw.path, opts.strict);
|
|
1836
|
+
if (malformed) frontmatterIssues.push(malformed);
|
|
2048
1837
|
}
|
|
1838
|
+
return { node, frontmatterIssues };
|
|
2049
1839
|
}
|
|
2050
1840
|
function mergeNodeWithEnrichments(node, enrichments, opts = {}) {
|
|
2051
1841
|
const includeStale = opts.includeStale === true;
|
|
@@ -2065,11 +1855,400 @@ function assignSafe(target, source) {
|
|
|
2065
1855
|
}
|
|
2066
1856
|
}
|
|
2067
1857
|
|
|
1858
|
+
// kernel/orchestrator/walk.ts
|
|
1859
|
+
async function walkAndExtract(opts) {
|
|
1860
|
+
const accum = createWalkAccumulators();
|
|
1861
|
+
const wctx = buildWalkContext(opts);
|
|
1862
|
+
const claimedPaths = /* @__PURE__ */ new Set();
|
|
1863
|
+
const walkOptions = opts.ignoreFilter ? { ignoreFilter: opts.ignoreFilter } : {};
|
|
1864
|
+
let filesWalked = 0;
|
|
1865
|
+
let index = 0;
|
|
1866
|
+
for (const provider of opts.providers) {
|
|
1867
|
+
for await (const raw of resolveProviderWalk(provider)(opts.roots, walkOptions)) {
|
|
1868
|
+
filesWalked += 1;
|
|
1869
|
+
if (claimedPaths.has(raw.path)) continue;
|
|
1870
|
+
const advanced = await processRawNode(raw, provider, wctx, accum, claimedPaths, index + 1);
|
|
1871
|
+
if (advanced) index += 1;
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
const orphanSidecars = discoverOrphanSidecars(opts.roots);
|
|
1875
|
+
return {
|
|
1876
|
+
nodes: accum.nodes,
|
|
1877
|
+
internalLinks: accum.internalLinks,
|
|
1878
|
+
externalLinks: accum.externalLinks,
|
|
1879
|
+
cachedPaths: accum.cachedPaths,
|
|
1880
|
+
frontmatterIssues: accum.frontmatterIssues,
|
|
1881
|
+
filesWalked,
|
|
1882
|
+
enrichments: [...accum.enrichmentBuffer.values()],
|
|
1883
|
+
extractorRuns: accum.extractorRuns,
|
|
1884
|
+
contributions: accum.contributionsBuffer,
|
|
1885
|
+
freshlyRunTuples: accum.freshlyRunTuples,
|
|
1886
|
+
orphanSidecars,
|
|
1887
|
+
sidecarRoots: accum.sidecarRoots
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
function createWalkAccumulators() {
|
|
1891
|
+
return {
|
|
1892
|
+
nodes: [],
|
|
1893
|
+
internalLinks: [],
|
|
1894
|
+
externalLinks: [],
|
|
1895
|
+
cachedPaths: /* @__PURE__ */ new Set(),
|
|
1896
|
+
frontmatterIssues: [],
|
|
1897
|
+
enrichmentBuffer: /* @__PURE__ */ new Map(),
|
|
1898
|
+
contributionsBuffer: [],
|
|
1899
|
+
freshlyRunTuples: /* @__PURE__ */ new Set(),
|
|
1900
|
+
extractorRuns: [],
|
|
1901
|
+
sidecarRoots: /* @__PURE__ */ new Map()
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
function buildWalkContext(opts) {
|
|
1905
|
+
const { priorNodesByPath, priorLinksByOriginating, priorFrontmatterIssuesByNode } = opts.priorIndex;
|
|
1906
|
+
const shortIdToQualified = /* @__PURE__ */ new Map();
|
|
1907
|
+
for (const ex of opts.extractors) {
|
|
1908
|
+
const qualified = qualifiedExtensionId(ex.pluginId, ex.id);
|
|
1909
|
+
const list = shortIdToQualified.get(ex.id);
|
|
1910
|
+
if (list) list.push(qualified);
|
|
1911
|
+
else shortIdToQualified.set(ex.id, [qualified]);
|
|
1912
|
+
}
|
|
1913
|
+
return { opts, priorNodesByPath, priorLinksByOriginating, priorFrontmatterIssuesByNode, shortIdToQualified };
|
|
1914
|
+
}
|
|
1915
|
+
async function processRawNode(raw, provider, wctx, accum, claimedPaths, nextIndex) {
|
|
1916
|
+
const bodyHash = sha256(raw.body);
|
|
1917
|
+
const frontmatterHash = sha256(canonicalFrontmatter(raw.frontmatter, raw.frontmatterRaw));
|
|
1918
|
+
const kind = provider.classify(raw.path, raw.frontmatter);
|
|
1919
|
+
if (kind === null) {
|
|
1920
|
+
return false;
|
|
1921
|
+
}
|
|
1922
|
+
claimedPaths.add(raw.path);
|
|
1923
|
+
const priorNode = wctx.priorNodesByPath.get(raw.path);
|
|
1924
|
+
const nodeHashCacheEligible = wctx.opts.enableCache && wctx.opts.prior !== null && priorNode !== void 0 && priorNode.bodyHash === bodyHash && priorNode.frontmatterHash === frontmatterHash;
|
|
1925
|
+
const sidecarResolution = resolveSidecarOverlay(
|
|
1926
|
+
raw.path,
|
|
1927
|
+
raw.path,
|
|
1928
|
+
wctx.opts.roots,
|
|
1929
|
+
bodyHash,
|
|
1930
|
+
frontmatterHash
|
|
1931
|
+
);
|
|
1932
|
+
const sidecarAnnotationsHash = sha256(
|
|
1933
|
+
canonicalSidecarAnnotations(sidecarResolution.overlay.annotations)
|
|
1934
|
+
);
|
|
1935
|
+
const cacheDecision = computeCacheDecision({
|
|
1936
|
+
extractors: wctx.opts.extractors,
|
|
1937
|
+
kind,
|
|
1938
|
+
nodePath: raw.path,
|
|
1939
|
+
bodyHash,
|
|
1940
|
+
sidecarAnnotationsHash,
|
|
1941
|
+
nodeHashCacheEligible,
|
|
1942
|
+
priorExtractorRuns: wctx.opts.priorExtractorRuns
|
|
1943
|
+
});
|
|
1944
|
+
const ctx = {
|
|
1945
|
+
raw,
|
|
1946
|
+
provider,
|
|
1947
|
+
kind,
|
|
1948
|
+
bodyHash,
|
|
1949
|
+
frontmatterHash,
|
|
1950
|
+
sidecarResolution,
|
|
1951
|
+
sidecarAnnotationsHash,
|
|
1952
|
+
nodeHashCacheEligible,
|
|
1953
|
+
cacheDecision,
|
|
1954
|
+
priorNode,
|
|
1955
|
+
index: nextIndex
|
|
1956
|
+
};
|
|
1957
|
+
if (cacheDecision.fullCacheHit && priorNode) {
|
|
1958
|
+
applyFullCacheHit(ctx, wctx, accum);
|
|
1959
|
+
} else {
|
|
1960
|
+
await applyExtractPath(ctx, wctx, accum);
|
|
1961
|
+
}
|
|
1962
|
+
return true;
|
|
1963
|
+
}
|
|
1964
|
+
function attachSidecar(node, resolution, sidecarRoots) {
|
|
1965
|
+
node.sidecar = resolution.overlay;
|
|
1966
|
+
if (resolution.parsedRoot !== null) {
|
|
1967
|
+
sidecarRoots.set(node.path, resolution.parsedRoot);
|
|
1968
|
+
}
|
|
1969
|
+
return resolution.issues.map(
|
|
1970
|
+
(i) => i.nodeIds.length > 0 ? i : { ...i, nodeIds: [node.path] }
|
|
1971
|
+
);
|
|
1972
|
+
}
|
|
1973
|
+
function applyFullCacheHit(ctx, wctx, accum) {
|
|
1974
|
+
const reused = reusePriorNode({
|
|
1975
|
+
priorNode: ctx.priorNode,
|
|
1976
|
+
bodyHash: ctx.bodyHash,
|
|
1977
|
+
sidecarAnnotationsHash: ctx.sidecarAnnotationsHash,
|
|
1978
|
+
strict: wctx.opts.strict,
|
|
1979
|
+
cachedQualifiedIds: ctx.cacheDecision.cachedQualifiedIds,
|
|
1980
|
+
applicableQualifiedIds: ctx.cacheDecision.applicableQualifiedIds,
|
|
1981
|
+
shortIdToQualified: wctx.shortIdToQualified,
|
|
1982
|
+
priorLinksByOriginating: wctx.priorLinksByOriginating,
|
|
1983
|
+
priorFrontmatterIssuesByNode: wctx.priorFrontmatterIssuesByNode
|
|
1984
|
+
});
|
|
1985
|
+
const reusedSidecarIssues = attachSidecar(reused.node, ctx.sidecarResolution, accum.sidecarRoots);
|
|
1986
|
+
accum.nodes.push(reused.node);
|
|
1987
|
+
accum.cachedPaths.add(reused.node.path);
|
|
1988
|
+
for (const link of reused.internalLinks) accum.internalLinks.push(link);
|
|
1989
|
+
for (const issue of reused.frontmatterIssues) accum.frontmatterIssues.push(issue);
|
|
1990
|
+
for (const issue of reusedSidecarIssues) accum.frontmatterIssues.push(issue);
|
|
1991
|
+
for (const run of reused.extractorRuns) accum.extractorRuns.push(run);
|
|
1992
|
+
wctx.opts.emitter.emit(makeEvent("scan.progress", {
|
|
1993
|
+
index: ctx.index,
|
|
1994
|
+
path: ctx.raw.path,
|
|
1995
|
+
kind: ctx.kind,
|
|
1996
|
+
cached: true
|
|
1997
|
+
}));
|
|
1998
|
+
}
|
|
1999
|
+
async function applyExtractPath(ctx, wctx, accum) {
|
|
2000
|
+
const node = buildOrReuseNode(ctx, wctx, accum);
|
|
2001
|
+
const sidecarIssues = attachSidecar(node, ctx.sidecarResolution, accum.sidecarRoots);
|
|
2002
|
+
for (const issue of sidecarIssues) accum.frontmatterIssues.push(issue);
|
|
2003
|
+
const partialCacheHit = isPartialCacheHit(ctx);
|
|
2004
|
+
emitExtractProgress(ctx, wctx, partialCacheHit);
|
|
2005
|
+
const extractorsToRun = partialCacheHit ? ctx.cacheDecision.missingExtractors : ctx.cacheDecision.applicableExtractors;
|
|
2006
|
+
recordFreshlyRunTuples(extractorsToRun, node.path, accum);
|
|
2007
|
+
const extractResult = await runExtractorsForNode({
|
|
2008
|
+
extractors: extractorsToRun,
|
|
2009
|
+
node,
|
|
2010
|
+
body: ctx.raw.body,
|
|
2011
|
+
frontmatter: ctx.raw.frontmatter,
|
|
2012
|
+
bodyHash: ctx.bodyHash,
|
|
2013
|
+
emitter: wctx.opts.emitter,
|
|
2014
|
+
...wctx.opts.pluginStores ? { pluginStores: wctx.opts.pluginStores } : {}
|
|
2015
|
+
});
|
|
2016
|
+
mergeExtractResult(extractResult, accum);
|
|
2017
|
+
recordExtractorRuns(node.path, ctx, accum);
|
|
2018
|
+
}
|
|
2019
|
+
function emitExtractProgress(ctx, wctx, partialCacheHit) {
|
|
2020
|
+
wctx.opts.emitter.emit(makeEvent("scan.progress", {
|
|
2021
|
+
index: ctx.index,
|
|
2022
|
+
path: ctx.raw.path,
|
|
2023
|
+
kind: ctx.kind,
|
|
2024
|
+
cached: false,
|
|
2025
|
+
...partialCacheHit ? { partialCache: true } : {}
|
|
2026
|
+
}));
|
|
2027
|
+
}
|
|
2028
|
+
function recordFreshlyRunTuples(extractors, nodePath, accum) {
|
|
2029
|
+
for (const ex of extractors) {
|
|
2030
|
+
accum.freshlyRunTuples.add(`${ex.pluginId}\0${ex.id}\0${nodePath}`);
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
function mergeExtractResult(extractResult, accum) {
|
|
2034
|
+
for (const link of extractResult.internalLinks) accum.internalLinks.push(link);
|
|
2035
|
+
for (const link of extractResult.externalLinks) accum.externalLinks.push(link);
|
|
2036
|
+
for (const enr of extractResult.enrichments) {
|
|
2037
|
+
accum.enrichmentBuffer.set(`${enr.nodePath}\0${enr.extractorId}`, enr);
|
|
2038
|
+
}
|
|
2039
|
+
for (const c of extractResult.contributions) accum.contributionsBuffer.push(c);
|
|
2040
|
+
}
|
|
2041
|
+
function isPartialCacheHit(ctx) {
|
|
2042
|
+
return ctx.nodeHashCacheEligible && ctx.cacheDecision.cachedQualifiedIds.size > 0 && ctx.priorNode !== void 0;
|
|
2043
|
+
}
|
|
2044
|
+
function buildOrReuseNode(ctx, wctx, accum) {
|
|
2045
|
+
if (isPartialCacheHit(ctx) && ctx.priorNode) {
|
|
2046
|
+
const partial = cloneNodeAndReshapeLinks({
|
|
2047
|
+
priorNode: ctx.priorNode,
|
|
2048
|
+
strict: wctx.opts.strict,
|
|
2049
|
+
cachedQualifiedIds: ctx.cacheDecision.cachedQualifiedIds,
|
|
2050
|
+
applicableQualifiedIds: ctx.cacheDecision.applicableQualifiedIds,
|
|
2051
|
+
shortIdToQualified: wctx.shortIdToQualified,
|
|
2052
|
+
priorLinksByOriginating: wctx.priorLinksByOriginating,
|
|
2053
|
+
priorFrontmatterIssuesByNode: wctx.priorFrontmatterIssuesByNode
|
|
2054
|
+
});
|
|
2055
|
+
for (const link of partial.internalLinks) accum.internalLinks.push(link);
|
|
2056
|
+
for (const issue of partial.frontmatterIssues) accum.frontmatterIssues.push(issue);
|
|
2057
|
+
accum.nodes.push(partial.node);
|
|
2058
|
+
return partial.node;
|
|
2059
|
+
}
|
|
2060
|
+
const fresh = buildFreshNodeAndValidateFrontmatter({
|
|
2061
|
+
raw: ctx.raw,
|
|
2062
|
+
kind: ctx.kind,
|
|
2063
|
+
provider: ctx.provider,
|
|
2064
|
+
bodyHash: ctx.bodyHash,
|
|
2065
|
+
frontmatterHash: ctx.frontmatterHash,
|
|
2066
|
+
encoder: wctx.opts.encoder,
|
|
2067
|
+
providerFrontmatter: wctx.opts.providerFrontmatter,
|
|
2068
|
+
strict: wctx.opts.strict
|
|
2069
|
+
});
|
|
2070
|
+
accum.nodes.push(fresh.node);
|
|
2071
|
+
for (const issue of fresh.frontmatterIssues) accum.frontmatterIssues.push(issue);
|
|
2072
|
+
return fresh.node;
|
|
2073
|
+
}
|
|
2074
|
+
function recordExtractorRuns(nodePath, ctx, accum) {
|
|
2075
|
+
const ranAt = Date.now();
|
|
2076
|
+
for (const ex of ctx.cacheDecision.applicableExtractors) {
|
|
2077
|
+
accum.extractorRuns.push({
|
|
2078
|
+
nodePath,
|
|
2079
|
+
extractorId: qualifiedExtensionId(ex.pluginId, ex.id),
|
|
2080
|
+
bodyHashAtRun: ctx.bodyHash,
|
|
2081
|
+
ranAt,
|
|
2082
|
+
sidecarAnnotationsHashAtRun: ctx.sidecarAnnotationsHash
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
// kernel/orchestrator/index.ts
|
|
2088
|
+
var SCANNED_BY = {
|
|
2089
|
+
name: "skill-map",
|
|
2090
|
+
version: package_default.version,
|
|
2091
|
+
specVersion: resolveSpecVersionSafe()
|
|
2092
|
+
};
|
|
2093
|
+
function resolveSpecVersionSafe() {
|
|
2094
|
+
try {
|
|
2095
|
+
return installedSpecVersion();
|
|
2096
|
+
} catch {
|
|
2097
|
+
return "unknown";
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
async function runScanWithRenames(_kernel, options) {
|
|
2101
|
+
return runScanInternal(_kernel, options);
|
|
2102
|
+
}
|
|
2103
|
+
async function runScan(_kernel, options) {
|
|
2104
|
+
const { result } = await runScanInternal(_kernel, options);
|
|
2105
|
+
return result;
|
|
2106
|
+
}
|
|
2107
|
+
async function runScanInternal(_kernel, options) {
|
|
2108
|
+
validateRoots(options.roots);
|
|
2109
|
+
const setup = buildScanSetup(options);
|
|
2110
|
+
const { emitter, exts, hookDispatcher, encoder, prior, start } = setup;
|
|
2111
|
+
const scanStartedEvent = makeEvent("scan.started", { roots: options.roots });
|
|
2112
|
+
emitter.emit(scanStartedEvent);
|
|
2113
|
+
await hookDispatcher.dispatch("scan.started", scanStartedEvent);
|
|
2114
|
+
const walked = await walkAndExtract({
|
|
2115
|
+
providers: exts.providers,
|
|
2116
|
+
extractors: exts.extractors,
|
|
2117
|
+
roots: options.roots,
|
|
2118
|
+
...options.ignoreFilter ? { ignoreFilter: options.ignoreFilter } : {},
|
|
2119
|
+
emitter,
|
|
2120
|
+
encoder,
|
|
2121
|
+
strict: setup.strict,
|
|
2122
|
+
enableCache: setup.enableCache,
|
|
2123
|
+
prior,
|
|
2124
|
+
priorIndex: setup.priorIndex,
|
|
2125
|
+
priorExtractorRuns: setup.priorExtractorRuns,
|
|
2126
|
+
providerFrontmatter: setup.providerFrontmatter,
|
|
2127
|
+
pluginStores: options.pluginStores
|
|
2128
|
+
});
|
|
2129
|
+
recomputeLinkCounts(walked.nodes, walked.internalLinks);
|
|
2130
|
+
recomputeExternalRefsCount(walked.nodes, walked.externalLinks, walked.cachedPaths);
|
|
2131
|
+
await dispatchExtractorCompleted(exts.extractors, emitter, hookDispatcher);
|
|
2132
|
+
const analyzerResult = await runAnalyzers(
|
|
2133
|
+
exts.analyzers,
|
|
2134
|
+
walked.nodes,
|
|
2135
|
+
walked.internalLinks,
|
|
2136
|
+
walked.orphanSidecars,
|
|
2137
|
+
walked.sidecarRoots,
|
|
2138
|
+
options.annotationContributions ?? [],
|
|
2139
|
+
options.viewContributions ?? [],
|
|
2140
|
+
options.orphanJobFiles ?? [],
|
|
2141
|
+
options.referenceablePaths,
|
|
2142
|
+
options.cwd,
|
|
2143
|
+
emitter,
|
|
2144
|
+
hookDispatcher
|
|
2145
|
+
);
|
|
2146
|
+
mergeAnalyzerEmissions(walked, analyzerResult, exts.analyzers);
|
|
2147
|
+
const issues = analyzerResult.issues;
|
|
2148
|
+
for (const issue of walked.frontmatterIssues) issues.push(issue);
|
|
2149
|
+
const renameOps = prior ? detectRenamesAndOrphans(prior, walked.nodes, issues) : [];
|
|
2150
|
+
const stats = buildScanStats(walked, issues, start);
|
|
2151
|
+
const scanCompletedEvent = makeEvent("scan.completed", { stats });
|
|
2152
|
+
emitter.emit(scanCompletedEvent);
|
|
2153
|
+
await hookDispatcher.dispatch("scan.completed", scanCompletedEvent);
|
|
2154
|
+
return buildScanReturn(walked, issues, renameOps, stats, options, setup);
|
|
2155
|
+
}
|
|
2156
|
+
function buildScanSetup(options) {
|
|
2157
|
+
const start = Date.now();
|
|
2158
|
+
const emitter = options.emitter ?? new InMemoryProgressEmitter();
|
|
2159
|
+
const exts = options.extensions ?? { providers: [], extractors: [], analyzers: [] };
|
|
2160
|
+
const hookDispatcher = makeHookDispatcher(exts.hooks ?? [], emitter);
|
|
2161
|
+
const tokenize = options.tokenize !== false;
|
|
2162
|
+
const encoder = tokenize ? new Tiktoken2(cl100k_base) : null;
|
|
2163
|
+
const prior = options.priorSnapshot ?? null;
|
|
2164
|
+
const priorIndex = indexPriorSnapshot(prior);
|
|
2165
|
+
const providerFrontmatter = buildProviderFrontmatterValidator(exts.providers);
|
|
2166
|
+
return {
|
|
2167
|
+
start,
|
|
2168
|
+
scannedAt: start,
|
|
2169
|
+
emitter,
|
|
2170
|
+
exts,
|
|
2171
|
+
hookDispatcher,
|
|
2172
|
+
encoder,
|
|
2173
|
+
prior,
|
|
2174
|
+
priorIndex,
|
|
2175
|
+
priorExtractorRuns: options.priorExtractorRuns,
|
|
2176
|
+
providerFrontmatter,
|
|
2177
|
+
scope: options.scope ?? "project",
|
|
2178
|
+
strict: options.strict === true,
|
|
2179
|
+
enableCache: options.enableCache === true
|
|
2180
|
+
};
|
|
2181
|
+
}
|
|
2182
|
+
async function dispatchExtractorCompleted(extractors, emitter, hookDispatcher) {
|
|
2183
|
+
for (const extractor of extractors) {
|
|
2184
|
+
const extractorId = qualifiedExtensionId(extractor.pluginId, extractor.id);
|
|
2185
|
+
const evt = makeEvent("extractor.completed", { extractorId });
|
|
2186
|
+
emitter.emit(evt);
|
|
2187
|
+
await hookDispatcher.dispatch("extractor.completed", evt);
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
function mergeAnalyzerEmissions(walked, analyzerResult, analyzers) {
|
|
2191
|
+
for (const c of analyzerResult.contributions) walked.contributions.push(c);
|
|
2192
|
+
for (const analyzer of analyzers ?? []) {
|
|
2193
|
+
if (analyzer.viewContributions === void 0) continue;
|
|
2194
|
+
for (const node of walked.nodes) {
|
|
2195
|
+
walked.freshlyRunTuples.add(`${analyzer.pluginId}\0${analyzer.id}\0${node.path}`);
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
function buildScanStats(walked, issues, start) {
|
|
2200
|
+
return {
|
|
2201
|
+
// `filesSkipped` is "files walked but not classified by any
|
|
2202
|
+
// Provider". Today every walked file IS classified by its Provider
|
|
2203
|
+
// (the `claude` Provider's `classify()` always returns a kind,
|
|
2204
|
+
// falling back to `'markdown'`), so this is always 0. Wired now
|
|
2205
|
+
// so the field shape is spec-conformant; meaningful once multiple
|
|
2206
|
+
// Providers compete.
|
|
2207
|
+
filesWalked: walked.filesWalked,
|
|
2208
|
+
filesSkipped: 0,
|
|
2209
|
+
nodesCount: walked.nodes.length,
|
|
2210
|
+
linksCount: walked.internalLinks.length,
|
|
2211
|
+
issuesCount: issues.length,
|
|
2212
|
+
durationMs: Date.now() - start
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
function buildScanReturn(walked, issues, renameOps, stats, options, setup) {
|
|
2216
|
+
return {
|
|
2217
|
+
result: {
|
|
2218
|
+
schemaVersion: 1,
|
|
2219
|
+
scannedAt: setup.scannedAt,
|
|
2220
|
+
scope: setup.scope,
|
|
2221
|
+
roots: options.roots,
|
|
2222
|
+
providers: setup.exts.providers.map((a) => a.id),
|
|
2223
|
+
scannedBy: SCANNED_BY,
|
|
2224
|
+
nodes: walked.nodes,
|
|
2225
|
+
links: walked.internalLinks,
|
|
2226
|
+
issues,
|
|
2227
|
+
stats
|
|
2228
|
+
},
|
|
2229
|
+
renameOps,
|
|
2230
|
+
extractorRuns: walked.extractorRuns,
|
|
2231
|
+
enrichments: walked.enrichments,
|
|
2232
|
+
contributions: walked.contributions,
|
|
2233
|
+
freshlyRunTuples: walked.freshlyRunTuples
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
function validateRoots(roots) {
|
|
2237
|
+
if (roots.length === 0) {
|
|
2238
|
+
throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
|
|
2239
|
+
}
|
|
2240
|
+
for (const root of roots) {
|
|
2241
|
+
if (!existsSync9(root) || !statSync2(root).isDirectory()) {
|
|
2242
|
+
throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2068
2247
|
// kernel/scan/watcher.ts
|
|
2069
|
-
import { resolve as
|
|
2248
|
+
import { resolve as resolve10, relative as relative4, sep as sep3 } from "path";
|
|
2070
2249
|
import chokidar from "chokidar";
|
|
2071
2250
|
function createChokidarWatcher(opts) {
|
|
2072
|
-
const absRoots = opts.roots.map((r) =>
|
|
2251
|
+
const absRoots = opts.roots.map((r) => resolve10(opts.cwd, r));
|
|
2073
2252
|
const ignoreFilterOpt = opts.ignoreFilter;
|
|
2074
2253
|
const getFilter = ignoreFilterOpt === void 0 ? void 0 : typeof ignoreFilterOpt === "function" ? ignoreFilterOpt : () => ignoreFilterOpt;
|
|
2075
2254
|
const ignored = getFilter ? (path) => {
|