@skill-map/cli 0.7.0 → 0.9.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/index.js ADDED
@@ -0,0 +1,1698 @@
1
+ // kernel/i18n/registry.texts.ts
2
+ var REGISTRY_TEXTS = {
3
+ duplicateExtension: "Extension already registered: {{kind}}:{{qualifiedId}}",
4
+ unknownKind: "Unknown extension kind: {{kind}}",
5
+ missingPluginId: "Extension {{kind}}:{{id}} is missing pluginId; built-ins declare it directly, user plugins have it injected by PluginLoader."
6
+ };
7
+
8
+ // kernel/util/tx.ts
9
+ var TOKEN_RE = /\{\{\s*([A-Za-z][A-Za-z0-9_]*)\s*\}\}/g;
10
+ function tx(template, vars = {}) {
11
+ return template.replace(TOKEN_RE, (_match, name) => {
12
+ if (!Object.prototype.hasOwnProperty.call(vars, name)) {
13
+ throw new Error(
14
+ `tx: missing variable "${name}" for template "${template.slice(0, 80)}${template.length > 80 ? "\u2026" : ""}"`
15
+ );
16
+ }
17
+ const value = vars[name];
18
+ if (value === null || value === void 0) {
19
+ throw new Error(
20
+ `tx: variable "${name}" is null/undefined for template "${template.slice(0, 80)}${template.length > 80 ? "\u2026" : ""}"`
21
+ );
22
+ }
23
+ return String(value);
24
+ });
25
+ }
26
+
27
+ // kernel/registry.ts
28
+ var EXTENSION_KINDS = Object.freeze([
29
+ "provider",
30
+ "extractor",
31
+ "rule",
32
+ "action",
33
+ "formatter",
34
+ "hook"
35
+ ]);
36
+ function qualifiedExtensionId(pluginId, id) {
37
+ return `${pluginId}/${id}`;
38
+ }
39
+ var DuplicateExtensionError = class extends Error {
40
+ constructor(kind, qualifiedId) {
41
+ super(tx(REGISTRY_TEXTS.duplicateExtension, { kind, qualifiedId }));
42
+ this.name = "DuplicateExtensionError";
43
+ }
44
+ };
45
+ var Registry = class {
46
+ /** kind → qualifiedId → Extension. */
47
+ #byKind;
48
+ constructor() {
49
+ this.#byKind = new Map(
50
+ EXTENSION_KINDS.map((k) => [k, /* @__PURE__ */ new Map()])
51
+ );
52
+ }
53
+ register(ext) {
54
+ const bucket = this.#byKind.get(ext.kind);
55
+ if (!bucket) {
56
+ throw new Error(tx(REGISTRY_TEXTS.unknownKind, { kind: ext.kind }));
57
+ }
58
+ if (typeof ext.pluginId !== "string" || ext.pluginId.length === 0) {
59
+ throw new Error(tx(REGISTRY_TEXTS.missingPluginId, { kind: ext.kind, id: ext.id }));
60
+ }
61
+ const key = qualifiedExtensionId(ext.pluginId, ext.id);
62
+ if (bucket.has(key)) {
63
+ throw new DuplicateExtensionError(ext.kind, key);
64
+ }
65
+ bucket.set(key, ext);
66
+ }
67
+ /**
68
+ * Lookup by qualified id (`<pluginId>/<id>`). Returns `undefined` when
69
+ * no extension of that kind is registered under the qualifier.
70
+ */
71
+ get(kind, qualifiedId) {
72
+ return this.#byKind.get(kind)?.get(qualifiedId);
73
+ }
74
+ /**
75
+ * Convenience wrapper that composes the qualified id for the caller.
76
+ * Equivalent to `get(kind, qualifiedExtensionId(pluginId, id))`.
77
+ */
78
+ find(kind, pluginId, id) {
79
+ return this.get(kind, qualifiedExtensionId(pluginId, id));
80
+ }
81
+ all(kind) {
82
+ const bucket = this.#byKind.get(kind);
83
+ return bucket ? [...bucket.values()] : [];
84
+ }
85
+ count(kind) {
86
+ return this.#byKind.get(kind)?.size ?? 0;
87
+ }
88
+ totalCount() {
89
+ let n = 0;
90
+ for (const bucket of this.#byKind.values()) n += bucket.size;
91
+ return n;
92
+ }
93
+ };
94
+
95
+ // kernel/orchestrator.ts
96
+ import { createHash } from "crypto";
97
+ import { existsSync as existsSync2, statSync } from "fs";
98
+ import { Tiktoken } from "js-tiktoken/lite";
99
+ import cl100k_base from "js-tiktoken/ranks/cl100k_base";
100
+ import yaml from "js-yaml";
101
+
102
+ // package.json
103
+ var package_default = {
104
+ name: "@skill-map/cli",
105
+ version: "0.9.0",
106
+ description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
107
+ license: "MIT",
108
+ type: "module",
109
+ homepage: "https://skill-map.dev",
110
+ repository: {
111
+ type: "git",
112
+ url: "git+https://github.com/crystian/skill-map.git",
113
+ directory: "src"
114
+ },
115
+ bugs: {
116
+ url: "https://github.com/crystian/skill-map/issues"
117
+ },
118
+ keywords: [
119
+ "skill-map",
120
+ "markdown",
121
+ "ai-agents",
122
+ "claude-code",
123
+ "graph"
124
+ ],
125
+ bin: {
126
+ sm: "bin/sm.js",
127
+ "skill-map": "bin/sm.js"
128
+ },
129
+ exports: {
130
+ ".": {
131
+ types: "./dist/index.d.ts",
132
+ import: "./dist/index.js"
133
+ },
134
+ "./kernel": {
135
+ types: "./dist/kernel/index.d.ts",
136
+ import: "./dist/kernel/index.js"
137
+ },
138
+ "./conformance": {
139
+ types: "./dist/conformance/index.d.ts",
140
+ import: "./dist/conformance/index.js"
141
+ }
142
+ },
143
+ files: [
144
+ "bin/",
145
+ "dist/",
146
+ "migrations/",
147
+ "README.md"
148
+ ],
149
+ scripts: {
150
+ build: "tsup",
151
+ dev: "tsup --watch",
152
+ typecheck: "tsc --noEmit",
153
+ lint: "eslint .",
154
+ "lint:fix": "eslint . --fix",
155
+ test: "tsc --noEmit && node --import tsx --test --test-reporter=spec 'test/**/*.test.ts' 'built-in-plugins/**/*.test.ts' 'kernel/**/*.test.ts'",
156
+ "test:ci": "tsc --noEmit && node --import tsx --test 'test/**/*.test.ts' 'built-in-plugins/**/*.test.ts' 'kernel/**/*.test.ts'",
157
+ "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'",
158
+ "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'",
159
+ clean: "rm -rf dist coverage"
160
+ },
161
+ dependencies: {
162
+ "@skill-map/spec": "*",
163
+ ajv: "8.18.0",
164
+ "ajv-formats": "3.0.1",
165
+ chokidar: "5.0.0",
166
+ clipanion: "4.0.0-rc.4",
167
+ ignore: "7.0.5",
168
+ "js-tiktoken": "1.0.21",
169
+ "js-yaml": "4.1.1",
170
+ kysely: "0.28.16",
171
+ semver: "7.7.4",
172
+ typanion: "3.14.0"
173
+ },
174
+ devDependencies: {
175
+ "@eslint/js": "10.0.1",
176
+ "@stylistic/eslint-plugin": "5.10.0",
177
+ "@types/js-yaml": "4.0.9",
178
+ "@types/node": "24.12.2",
179
+ "@types/semver": "7.7.1",
180
+ c8: "11.0.0",
181
+ eslint: "10.2.1",
182
+ "eslint-plugin-import-x": "4.16.2",
183
+ tsup: "8.5.1",
184
+ tsx: "4.21.0",
185
+ typescript: "5.9.3",
186
+ "typescript-eslint": "8.59.1"
187
+ },
188
+ engines: {
189
+ node: ">=24.0"
190
+ },
191
+ publishConfig: {
192
+ access: "public"
193
+ }
194
+ };
195
+
196
+ // kernel/adapters/in-memory-progress.ts
197
+ var InMemoryProgressEmitter = class {
198
+ #listeners = /* @__PURE__ */ new Set();
199
+ emit(event) {
200
+ for (const listener of this.#listeners) listener(event);
201
+ }
202
+ subscribe(listener) {
203
+ this.#listeners.add(listener);
204
+ return () => {
205
+ this.#listeners.delete(listener);
206
+ };
207
+ }
208
+ };
209
+
210
+ // kernel/adapters/silent-logger.ts
211
+ var SilentLogger = class {
212
+ trace() {
213
+ }
214
+ debug() {
215
+ }
216
+ info() {
217
+ }
218
+ warn() {
219
+ }
220
+ error() {
221
+ }
222
+ };
223
+
224
+ // kernel/util/logger.ts
225
+ var active = new SilentLogger();
226
+ var log = {
227
+ trace: (message, context) => active.trace(message, context),
228
+ debug: (message, context) => active.debug(message, context),
229
+ info: (message, context) => active.info(message, context),
230
+ warn: (message, context) => active.warn(message, context),
231
+ error: (message, context) => active.error(message, context)
232
+ };
233
+ function configureLogger(impl) {
234
+ active = impl;
235
+ }
236
+ function resetLogger() {
237
+ active = new SilentLogger();
238
+ }
239
+ function getActiveLogger() {
240
+ return active;
241
+ }
242
+
243
+ // kernel/adapters/plugin-loader.ts
244
+ import { createRequire } from "module";
245
+ import { existsSync, readFileSync, readdirSync } from "fs";
246
+ import { isAbsolute, join, relative, resolve } from "path";
247
+ import { pathToFileURL } from "url";
248
+ import { Ajv2020 } from "ajv/dist/2020.js";
249
+ import addFormatsModule from "ajv-formats";
250
+ import semver from "semver";
251
+
252
+ // kernel/i18n/plugin-store.texts.ts
253
+ var PLUGIN_STORE_TEXTS = {
254
+ kvValidationFailed: "plugin '{{pluginId}}' ctx.store.set('{{key}}', value): value violates declared schema ({{schemaPath}}) \u2014 {{errors}}",
255
+ dedicatedValidationFailed: "plugin '{{pluginId}}' ctx.store.write('{{table}}', row): row violates declared schema ({{schemaPath}}) \u2014 {{errors}}"
256
+ };
257
+
258
+ // kernel/adapters/plugin-store.ts
259
+ var KV_SCHEMA_KEY = "__kv__";
260
+ function makeKvStoreWrapper(opts) {
261
+ const { pluginId, schema, persist } = opts;
262
+ return {
263
+ async set(key, value) {
264
+ if (schema) {
265
+ if (!schema.validate(value)) {
266
+ throw new Error(
267
+ tx(PLUGIN_STORE_TEXTS.kvValidationFailed, {
268
+ pluginId,
269
+ schemaPath: schema.schemaPath,
270
+ key,
271
+ errors: formatAjvErrors(schema.validate.errors ?? null)
272
+ })
273
+ );
274
+ }
275
+ }
276
+ await persist(key, value);
277
+ }
278
+ };
279
+ }
280
+ function makeDedicatedStoreWrapper(opts) {
281
+ const { pluginId, schemas, persist } = opts;
282
+ return {
283
+ async write(table, row) {
284
+ const schema = schemas?.[table];
285
+ if (schema) {
286
+ if (!schema.validate(row)) {
287
+ throw new Error(
288
+ tx(PLUGIN_STORE_TEXTS.dedicatedValidationFailed, {
289
+ pluginId,
290
+ table,
291
+ schemaPath: schema.schemaPath,
292
+ errors: formatAjvErrors(schema.validate.errors ?? null)
293
+ })
294
+ );
295
+ }
296
+ }
297
+ await persist(table, row);
298
+ }
299
+ };
300
+ }
301
+ function makePluginStore(opts) {
302
+ const manifest = opts.plugin.manifest;
303
+ if (!manifest?.storage) return void 0;
304
+ const storageSchemas = opts.plugin.storageSchemas;
305
+ if (manifest.storage.mode === "kv") {
306
+ if (!opts.persistKv) return void 0;
307
+ const schema = storageSchemas?.[KV_SCHEMA_KEY];
308
+ return makeKvStoreWrapper({
309
+ pluginId: manifest.id,
310
+ schema,
311
+ persist: opts.persistKv
312
+ });
313
+ }
314
+ if (manifest.storage.mode === "dedicated") {
315
+ if (!opts.persistDedicated) return void 0;
316
+ return makeDedicatedStoreWrapper({
317
+ pluginId: manifest.id,
318
+ schemas: storageSchemas,
319
+ persist: opts.persistDedicated
320
+ });
321
+ }
322
+ return void 0;
323
+ }
324
+ function formatAjvErrors(errors) {
325
+ if (!errors || errors.length === 0) return "(no AJV details)";
326
+ return errors.map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
327
+ }
328
+
329
+ // kernel/extensions/hook.ts
330
+ var HOOK_TRIGGERS = Object.freeze([
331
+ "scan.started",
332
+ "scan.completed",
333
+ "extractor.completed",
334
+ "rule.completed",
335
+ "action.completed",
336
+ "job.spawning",
337
+ "job.completed",
338
+ "job.failed"
339
+ ]);
340
+
341
+ // kernel/adapters/plugin-loader.ts
342
+ var addFormats = addFormatsModule.default ?? addFormatsModule;
343
+ var KNOWN_KINDS = /* @__PURE__ */ new Set(["provider", "extractor", "rule", "action", "formatter", "hook"]);
344
+ var KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(" / ");
345
+ var HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(", ");
346
+ function installedSpecVersion() {
347
+ const require2 = createRequire(import.meta.url);
348
+ const indexPath = require2.resolve("@skill-map/spec/index.json");
349
+ const pkgPath = resolve(indexPath, "..", "package.json");
350
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
351
+ return pkg.version;
352
+ }
353
+
354
+ // kernel/adapters/schema-validators.ts
355
+ import { readFileSync as readFileSync2 } from "fs";
356
+ import { dirname, resolve as resolve2 } from "path";
357
+ import { createRequire as createRequire2 } from "module";
358
+ import { Ajv2020 as Ajv20202 } from "ajv/dist/2020.js";
359
+ import addFormatsModule2 from "ajv-formats";
360
+ var addFormats2 = addFormatsModule2.default ?? addFormatsModule2;
361
+ function buildProviderFrontmatterValidator(providers) {
362
+ const specRoot = resolveSpecRoot();
363
+ const ajv = new Ajv20202({
364
+ strict: false,
365
+ allErrors: true,
366
+ allowUnionTypes: true
367
+ });
368
+ addFormats2(ajv);
369
+ const baseFile = resolve2(specRoot, "schemas/frontmatter/base.schema.json");
370
+ const baseSchema = JSON.parse(readFileSync2(baseFile, "utf8"));
371
+ ajv.addSchema(baseSchema);
372
+ const compiled = /* @__PURE__ */ new Map();
373
+ for (const provider of providers) {
374
+ for (const [kind, entry] of Object.entries(provider.kinds)) {
375
+ const key = `${provider.id}::${kind}`;
376
+ const json = entry.schemaJson;
377
+ const existing = typeof json.$id === "string" ? ajv.getSchema(json.$id) : void 0;
378
+ compiled.set(key, existing ?? ajv.compile(entry.schemaJson));
379
+ }
380
+ }
381
+ return {
382
+ validate(provider, kind, data) {
383
+ const key = `${provider.id}::${kind}`;
384
+ const v = compiled.get(key);
385
+ if (!v) return { ok: false, errors: "no-schema" };
386
+ if (v(data)) return { ok: true };
387
+ const errors = (v.errors ?? []).map(formatError).join("; ");
388
+ return { ok: false, errors };
389
+ }
390
+ };
391
+ }
392
+ function formatError(err) {
393
+ const path = err.instancePath || "(root)";
394
+ return `${path} ${err.message ?? err.keyword}`;
395
+ }
396
+ function resolveSpecRoot() {
397
+ const require2 = createRequire2(import.meta.url);
398
+ try {
399
+ const indexPath = require2.resolve("@skill-map/spec/index.json");
400
+ return dirname(indexPath);
401
+ } catch {
402
+ throw new Error(
403
+ "@skill-map/spec not resolvable \u2014 ensure the workspace is linked or the package is installed."
404
+ );
405
+ }
406
+ }
407
+
408
+ // kernel/i18n/orchestrator.texts.ts
409
+ var ORCHESTRATOR_TEXTS = {
410
+ frontmatterInvalid: "Frontmatter for {{path}} ({{kind}}) failed schema validation: {{errors}}",
411
+ 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.",
412
+ 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.",
413
+ 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.",
414
+ extensionErrorLinkKindNotDeclared: 'Extractor "{{extractorId}}" emitted a link of kind "{{linkKind}}" outside its declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',
415
+ extensionErrorIssueInvalidSeverity: `Rule "{{ruleId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
416
+ runScanRootEmptyArray: "runScan: roots must contain at least one path (spec requires minItems: 1)",
417
+ runScanRootMissing: "runScan: root path '{{root}}' does not exist or is not a directory"
418
+ };
419
+
420
+ // kernel/orchestrator.ts
421
+ var SCANNED_BY = {
422
+ name: "skill-map",
423
+ version: package_default.version,
424
+ specVersion: resolveSpecVersionSafe()
425
+ };
426
+ function resolveSpecVersionSafe() {
427
+ try {
428
+ return installedSpecVersion();
429
+ } catch {
430
+ return "unknown";
431
+ }
432
+ }
433
+ async function runScanWithRenames(_kernel, options) {
434
+ return runScanInternal(_kernel, options);
435
+ }
436
+ async function runScan(_kernel, options) {
437
+ const { result } = await runScanInternal(_kernel, options);
438
+ return result;
439
+ }
440
+ async function runScanInternal(_kernel, options) {
441
+ validateRoots(options.roots);
442
+ const start = Date.now();
443
+ const scannedAt = start;
444
+ const emitter = options.emitter ?? new InMemoryProgressEmitter();
445
+ const exts = options.extensions ?? { providers: [], extractors: [], rules: [] };
446
+ const hookDispatcher = makeHookDispatcher(exts.hooks ?? [], emitter);
447
+ const tokenize = options.tokenize !== false;
448
+ const scope = options.scope ?? "project";
449
+ const strict = options.strict === true;
450
+ const encoder = tokenize ? new Tiktoken(cl100k_base) : null;
451
+ const prior = options.priorSnapshot ?? null;
452
+ const enableCache = options.enableCache === true;
453
+ const priorExtractorRuns = options.priorExtractorRuns;
454
+ const priorIndex = indexPriorSnapshot(prior);
455
+ const providerFrontmatter = buildProviderFrontmatterValidator(exts.providers);
456
+ const scanStartedEvent = makeEvent("scan.started", { roots: options.roots });
457
+ emitter.emit(scanStartedEvent);
458
+ await hookDispatcher.dispatch("scan.started", scanStartedEvent);
459
+ const walked = await walkAndExtract({
460
+ providers: exts.providers,
461
+ extractors: exts.extractors,
462
+ roots: options.roots,
463
+ ...options.ignoreFilter ? { ignoreFilter: options.ignoreFilter } : {},
464
+ emitter,
465
+ encoder,
466
+ strict,
467
+ enableCache,
468
+ prior,
469
+ priorIndex,
470
+ priorExtractorRuns,
471
+ providerFrontmatter,
472
+ pluginStores: options.pluginStores
473
+ });
474
+ recomputeLinkCounts(walked.nodes, walked.internalLinks);
475
+ recomputeExternalRefsCount(walked.nodes, walked.externalLinks, walked.cachedPaths);
476
+ for (const extractor of exts.extractors) {
477
+ const extractorId = qualifiedExtensionId(extractor.pluginId, extractor.id);
478
+ const evt = makeEvent("extractor.completed", { extractorId });
479
+ emitter.emit(evt);
480
+ await hookDispatcher.dispatch("extractor.completed", evt);
481
+ }
482
+ const issues = await runRules(exts.rules, walked.nodes, walked.internalLinks, emitter, hookDispatcher);
483
+ for (const issue of walked.frontmatterIssues) issues.push(issue);
484
+ const renameOps = prior ? detectRenamesAndOrphans(prior, walked.nodes, issues) : [];
485
+ const stats = {
486
+ // `filesSkipped` is "files walked but not classified by any Provider".
487
+ // Today every walked file IS classified by its Provider (the `claude`
488
+ // Provider's `classify()` always returns a kind, falling back to
489
+ // `'note'`), so this is always 0. Wired now so the field shape is
490
+ // spec-conformant; meaningful once multiple Providers compete.
491
+ filesWalked: walked.filesWalked,
492
+ filesSkipped: 0,
493
+ nodesCount: walked.nodes.length,
494
+ linksCount: walked.internalLinks.length,
495
+ issuesCount: issues.length,
496
+ durationMs: Date.now() - start
497
+ };
498
+ const scanCompletedEvent = makeEvent("scan.completed", { stats });
499
+ emitter.emit(scanCompletedEvent);
500
+ await hookDispatcher.dispatch("scan.completed", scanCompletedEvent);
501
+ return {
502
+ result: {
503
+ schemaVersion: 1,
504
+ scannedAt,
505
+ scope,
506
+ roots: options.roots,
507
+ providers: exts.providers.map((a) => a.id),
508
+ scannedBy: SCANNED_BY,
509
+ nodes: walked.nodes,
510
+ links: walked.internalLinks,
511
+ issues,
512
+ stats
513
+ },
514
+ renameOps,
515
+ extractorRuns: walked.extractorRuns,
516
+ enrichments: walked.enrichments
517
+ };
518
+ }
519
+ function validateRoots(roots) {
520
+ if (roots.length === 0) {
521
+ throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
522
+ }
523
+ for (const root of roots) {
524
+ if (!existsSync2(root) || !statSync(root).isDirectory()) {
525
+ throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
526
+ }
527
+ }
528
+ }
529
+ function indexPriorSnapshot(prior) {
530
+ const priorNodesByPath = /* @__PURE__ */ new Map();
531
+ const priorNodePaths = /* @__PURE__ */ new Set();
532
+ const priorLinksByOriginating = /* @__PURE__ */ new Map();
533
+ const priorFrontmatterIssuesByNode = /* @__PURE__ */ new Map();
534
+ if (!prior) {
535
+ return { priorNodesByPath, priorNodePaths, priorLinksByOriginating, priorFrontmatterIssuesByNode };
536
+ }
537
+ for (const node of prior.nodes) {
538
+ priorNodesByPath.set(node.path, node);
539
+ priorNodePaths.add(node.path);
540
+ }
541
+ for (const link of prior.links) {
542
+ const key = originatingNodeOf(link, priorNodePaths);
543
+ const list = priorLinksByOriginating.get(key);
544
+ if (list) list.push(link);
545
+ else priorLinksByOriginating.set(key, [link]);
546
+ }
547
+ for (const issue of prior.issues) {
548
+ if (issue.ruleId !== "frontmatter-invalid" && issue.ruleId !== "frontmatter-malformed") continue;
549
+ if (issue.nodeIds.length !== 1) continue;
550
+ const path = issue.nodeIds[0];
551
+ const list = priorFrontmatterIssuesByNode.get(path);
552
+ if (list) list.push(issue);
553
+ else priorFrontmatterIssuesByNode.set(path, [issue]);
554
+ }
555
+ return { priorNodesByPath, priorNodePaths, priorLinksByOriginating, priorFrontmatterIssuesByNode };
556
+ }
557
+ async function runExtractorsForNode(opts) {
558
+ const internalLinks = [];
559
+ const externalLinks = [];
560
+ const enrichmentBuffer = /* @__PURE__ */ new Map();
561
+ for (const extractor of opts.extractors) {
562
+ const qualifiedId = qualifiedExtensionId(extractor.pluginId, extractor.id);
563
+ const isProb = extractor.mode === "probabilistic";
564
+ const emitLink = (link) => {
565
+ const validated = validateLink(extractor, link, opts.emitter);
566
+ if (!validated) return;
567
+ if (isExternalUrlLink(validated)) externalLinks.push(validated);
568
+ else internalLinks.push(validated);
569
+ };
570
+ const enrichNode = (partial) => {
571
+ const key = `${opts.node.path}\0${qualifiedId}`;
572
+ const existing = enrichmentBuffer.get(key);
573
+ if (existing) {
574
+ existing.value = { ...existing.value, ...partial };
575
+ existing.enrichedAt = Date.now();
576
+ } else {
577
+ enrichmentBuffer.set(key, {
578
+ nodePath: opts.node.path,
579
+ extractorId: qualifiedId,
580
+ bodyHashAtEnrichment: opts.bodyHash,
581
+ value: { ...partial },
582
+ enrichedAt: Date.now(),
583
+ isProbabilistic: isProb
584
+ });
585
+ }
586
+ };
587
+ const store = opts.pluginStores?.get(extractor.pluginId);
588
+ const ctx = buildExtractorContext(
589
+ extractor,
590
+ opts.node,
591
+ opts.body,
592
+ opts.frontmatter,
593
+ emitLink,
594
+ enrichNode,
595
+ store
596
+ );
597
+ await extractor.extract(ctx);
598
+ }
599
+ return {
600
+ internalLinks,
601
+ externalLinks,
602
+ enrichments: Array.from(enrichmentBuffer.values())
603
+ };
604
+ }
605
+ function computeCacheDecision(opts) {
606
+ const applicableExtractors = opts.extractors.filter(
607
+ (ex) => ex.applicableKinds === void 0 || ex.applicableKinds.includes(opts.kind)
608
+ );
609
+ const applicableQualifiedIds = new Set(
610
+ applicableExtractors.map((ex) => qualifiedExtensionId(ex.pluginId, ex.id))
611
+ );
612
+ const cachedQualifiedIds = /* @__PURE__ */ new Set();
613
+ const missingExtractors = [];
614
+ if (opts.priorExtractorRuns === void 0) {
615
+ if (opts.nodeHashCacheEligible) {
616
+ for (const id of applicableQualifiedIds) cachedQualifiedIds.add(id);
617
+ } else {
618
+ for (const ex of applicableExtractors) missingExtractors.push(ex);
619
+ }
620
+ } else {
621
+ const priorRunsForNode = opts.priorExtractorRuns.get(opts.nodePath) ?? /* @__PURE__ */ new Map();
622
+ for (const ex of applicableExtractors) {
623
+ const qualified = qualifiedExtensionId(ex.pluginId, ex.id);
624
+ const priorBody = priorRunsForNode.get(qualified);
625
+ if (opts.nodeHashCacheEligible && priorBody === opts.bodyHash) {
626
+ cachedQualifiedIds.add(qualified);
627
+ } else {
628
+ missingExtractors.push(ex);
629
+ }
630
+ }
631
+ }
632
+ return {
633
+ applicableExtractors,
634
+ applicableQualifiedIds,
635
+ cachedQualifiedIds,
636
+ missingExtractors,
637
+ fullCacheHit: opts.nodeHashCacheEligible && missingExtractors.length === 0
638
+ };
639
+ }
640
+ function cloneNodeAndReshapeLinks(opts) {
641
+ const node = { ...opts.priorNode, bytes: { ...opts.priorNode.bytes } };
642
+ if (opts.priorNode.tokens) node.tokens = { ...opts.priorNode.tokens };
643
+ const internalLinks = [];
644
+ const reusedLinks = opts.priorLinksByOriginating.get(opts.priorNode.path) ?? [];
645
+ for (const link of reusedLinks) {
646
+ const reshaped = reuseCachedLink(
647
+ link,
648
+ opts.shortIdToQualified,
649
+ opts.cachedQualifiedIds,
650
+ opts.applicableQualifiedIds
651
+ );
652
+ if (reshaped) internalLinks.push(reshaped);
653
+ }
654
+ const frontmatterIssues = [];
655
+ const reusedFm = opts.priorFrontmatterIssuesByNode.get(opts.priorNode.path) ?? [];
656
+ for (const issue of reusedFm) {
657
+ frontmatterIssues.push({ ...issue, severity: opts.strict ? "error" : "warn" });
658
+ }
659
+ return { node, internalLinks, frontmatterIssues };
660
+ }
661
+ function reusePriorNode(opts) {
662
+ const base = cloneNodeAndReshapeLinks(opts);
663
+ const ranAt = Date.now();
664
+ const extractorRuns = [];
665
+ for (const qualified of opts.cachedQualifiedIds) {
666
+ extractorRuns.push({
667
+ nodePath: opts.priorNode.path,
668
+ extractorId: qualified,
669
+ bodyHashAtRun: opts.bodyHash,
670
+ ranAt
671
+ });
672
+ }
673
+ return { ...base, extractorRuns };
674
+ }
675
+ function buildFreshNodeAndValidateFrontmatter(opts) {
676
+ const node = buildNode({
677
+ path: opts.raw.path,
678
+ kind: opts.kind,
679
+ providerId: opts.provider.id,
680
+ frontmatterRaw: opts.raw.frontmatterRaw,
681
+ body: opts.raw.body,
682
+ frontmatter: opts.raw.frontmatter,
683
+ bodyHash: opts.bodyHash,
684
+ frontmatterHash: opts.frontmatterHash,
685
+ encoder: opts.encoder
686
+ });
687
+ const frontmatterIssues = [];
688
+ if (opts.raw.frontmatterRaw.length > 0) {
689
+ const fmIssue = validateFrontmatter(
690
+ opts.providerFrontmatter,
691
+ opts.provider,
692
+ opts.kind,
693
+ opts.raw.frontmatter,
694
+ opts.raw.path,
695
+ opts.strict
696
+ );
697
+ if (fmIssue) frontmatterIssues.push(fmIssue);
698
+ } else {
699
+ const malformed = detectMalformedFrontmatter(opts.raw.body, opts.raw.path, opts.strict);
700
+ if (malformed) frontmatterIssues.push(malformed);
701
+ }
702
+ return { node, frontmatterIssues };
703
+ }
704
+ async function walkAndExtract(opts) {
705
+ const {
706
+ providers,
707
+ extractors,
708
+ roots,
709
+ ignoreFilter,
710
+ emitter,
711
+ encoder,
712
+ strict,
713
+ enableCache,
714
+ prior,
715
+ priorIndex,
716
+ priorExtractorRuns,
717
+ providerFrontmatter,
718
+ pluginStores
719
+ } = opts;
720
+ const { priorNodesByPath, priorLinksByOriginating, priorFrontmatterIssuesByNode } = priorIndex;
721
+ const nodes = [];
722
+ const internalLinks = [];
723
+ const externalLinks = [];
724
+ const cachedPaths = /* @__PURE__ */ new Set();
725
+ const frontmatterIssues = [];
726
+ const enrichmentBuffer = /* @__PURE__ */ new Map();
727
+ const extractorRuns = [];
728
+ let filesWalked = 0;
729
+ let index = 0;
730
+ const walkOptions = ignoreFilter ? { ignoreFilter } : {};
731
+ const shortIdToQualified = /* @__PURE__ */ new Map();
732
+ for (const ex of extractors) {
733
+ const qualified = qualifiedExtensionId(ex.pluginId, ex.id);
734
+ const list = shortIdToQualified.get(ex.id);
735
+ if (list) list.push(qualified);
736
+ else shortIdToQualified.set(ex.id, [qualified]);
737
+ }
738
+ for (const provider of providers) {
739
+ for await (const raw of provider.walk(roots, walkOptions)) {
740
+ filesWalked += 1;
741
+ const bodyHash = sha256(raw.body);
742
+ const frontmatterHash = sha256(canonicalFrontmatter(raw.frontmatter, raw.frontmatterRaw));
743
+ const priorNode = priorNodesByPath.get(raw.path);
744
+ const nodeHashCacheEligible = enableCache && prior !== null && priorNode !== void 0 && priorNode.bodyHash === bodyHash && priorNode.frontmatterHash === frontmatterHash;
745
+ const kind = provider.classify(raw.path, raw.frontmatter);
746
+ index += 1;
747
+ const cacheDecision = computeCacheDecision({
748
+ extractors,
749
+ kind,
750
+ nodePath: raw.path,
751
+ bodyHash,
752
+ nodeHashCacheEligible,
753
+ priorExtractorRuns
754
+ });
755
+ const {
756
+ applicableExtractors,
757
+ applicableQualifiedIds,
758
+ cachedQualifiedIds,
759
+ missingExtractors,
760
+ fullCacheHit
761
+ } = cacheDecision;
762
+ if (fullCacheHit && priorNode) {
763
+ const reused = reusePriorNode({
764
+ priorNode,
765
+ bodyHash,
766
+ strict,
767
+ cachedQualifiedIds,
768
+ applicableQualifiedIds,
769
+ shortIdToQualified,
770
+ priorLinksByOriginating,
771
+ priorFrontmatterIssuesByNode
772
+ });
773
+ nodes.push(reused.node);
774
+ cachedPaths.add(reused.node.path);
775
+ for (const link of reused.internalLinks) internalLinks.push(link);
776
+ for (const issue of reused.frontmatterIssues) frontmatterIssues.push(issue);
777
+ for (const run of reused.extractorRuns) extractorRuns.push(run);
778
+ emitter.emit(makeEvent("scan.progress", { index, path: raw.path, kind, cached: true }));
779
+ continue;
780
+ }
781
+ let node;
782
+ const partialCacheHit = nodeHashCacheEligible && cachedQualifiedIds.size > 0 && priorNode !== void 0;
783
+ if (partialCacheHit && priorNode) {
784
+ const partial = cloneNodeAndReshapeLinks({
785
+ priorNode,
786
+ strict,
787
+ cachedQualifiedIds,
788
+ applicableQualifiedIds,
789
+ shortIdToQualified,
790
+ priorLinksByOriginating,
791
+ priorFrontmatterIssuesByNode
792
+ });
793
+ node = partial.node;
794
+ for (const link of partial.internalLinks) internalLinks.push(link);
795
+ for (const issue of partial.frontmatterIssues) frontmatterIssues.push(issue);
796
+ nodes.push(node);
797
+ } else {
798
+ const fresh = buildFreshNodeAndValidateFrontmatter({
799
+ raw,
800
+ kind,
801
+ provider,
802
+ bodyHash,
803
+ frontmatterHash,
804
+ encoder,
805
+ providerFrontmatter,
806
+ strict
807
+ });
808
+ node = fresh.node;
809
+ nodes.push(node);
810
+ for (const issue of fresh.frontmatterIssues) frontmatterIssues.push(issue);
811
+ }
812
+ emitter.emit(makeEvent("scan.progress", {
813
+ index,
814
+ path: raw.path,
815
+ kind,
816
+ cached: false,
817
+ ...partialCacheHit ? { partialCache: true } : {}
818
+ }));
819
+ const extractorsToRun = partialCacheHit ? missingExtractors : applicableExtractors;
820
+ const extractResult = await runExtractorsForNode({
821
+ extractors: extractorsToRun,
822
+ node,
823
+ body: raw.body,
824
+ frontmatter: raw.frontmatter,
825
+ bodyHash,
826
+ emitter,
827
+ ...pluginStores ? { pluginStores } : {}
828
+ });
829
+ for (const link of extractResult.internalLinks) internalLinks.push(link);
830
+ for (const link of extractResult.externalLinks) externalLinks.push(link);
831
+ for (const enr of extractResult.enrichments) {
832
+ enrichmentBuffer.set(`${enr.nodePath}\0${enr.extractorId}`, enr);
833
+ }
834
+ const ranAt = Date.now();
835
+ for (const ex of applicableExtractors) {
836
+ const qualified = qualifiedExtensionId(ex.pluginId, ex.id);
837
+ extractorRuns.push({
838
+ nodePath: node.path,
839
+ extractorId: qualified,
840
+ bodyHashAtRun: bodyHash,
841
+ ranAt
842
+ });
843
+ }
844
+ }
845
+ }
846
+ return {
847
+ nodes,
848
+ internalLinks,
849
+ externalLinks,
850
+ cachedPaths,
851
+ frontmatterIssues,
852
+ filesWalked,
853
+ enrichments: [...enrichmentBuffer.values()],
854
+ extractorRuns
855
+ };
856
+ }
857
+ function reuseCachedLink(link, shortIdToQualified, cachedQualifiedIds, applicableQualifiedIds) {
858
+ if (!Array.isArray(link.sources) || link.sources.length === 0) return null;
859
+ const cachedSources = [];
860
+ const obsoleteSources = [];
861
+ let hasMissing = false;
862
+ for (const source of link.sources) {
863
+ const candidates = shortIdToQualified.get(source);
864
+ if (!candidates || candidates.length === 0) {
865
+ obsoleteSources.push(source);
866
+ continue;
867
+ }
868
+ if (candidates.some((q) => cachedQualifiedIds.has(q))) {
869
+ cachedSources.push(source);
870
+ continue;
871
+ }
872
+ if (candidates.some((q) => applicableQualifiedIds.has(q))) {
873
+ hasMissing = true;
874
+ continue;
875
+ }
876
+ obsoleteSources.push(source);
877
+ }
878
+ if (hasMissing) return null;
879
+ if (cachedSources.length === 0) return null;
880
+ if (obsoleteSources.length === 0) return link;
881
+ return { ...link, sources: cachedSources };
882
+ }
883
+ async function runRules(rules, nodes, internalLinks, emitter, hookDispatcher) {
884
+ const issues = [];
885
+ for (const rule of rules) {
886
+ const emitted = await rule.evaluate({ nodes, links: internalLinks });
887
+ for (const issue of emitted) {
888
+ const validated = validateIssue(rule, issue, emitter);
889
+ if (validated) issues.push(validated);
890
+ }
891
+ const ruleId = qualifiedExtensionId(rule.pluginId, rule.id);
892
+ const evt = makeEvent("rule.completed", { ruleId });
893
+ emitter.emit(evt);
894
+ await hookDispatcher.dispatch("rule.completed", evt);
895
+ }
896
+ return issues;
897
+ }
898
+ function originatingNodeOf(link, priorNodePaths) {
899
+ if (link.kind === "supersedes" && !priorNodePaths.has(link.source)) {
900
+ return link.target;
901
+ }
902
+ return link.source;
903
+ }
904
+ function findHighConfidenceRenames(opts) {
905
+ const ops = [];
906
+ for (const fromPath of opts.deletedPaths) {
907
+ if (opts.claimedDeleted.has(fromPath)) continue;
908
+ const fromNode = opts.priorByPath.get(fromPath);
909
+ for (const toPath of opts.newPaths) {
910
+ if (opts.claimedNew.has(toPath)) continue;
911
+ const toNode = opts.currentByPath.get(toPath);
912
+ if (toNode.bodyHash === fromNode.bodyHash) {
913
+ ops.push({ from: fromPath, to: toPath, confidence: "high" });
914
+ opts.claimedDeleted.add(fromPath);
915
+ opts.claimedNew.add(toPath);
916
+ break;
917
+ }
918
+ }
919
+ }
920
+ return ops;
921
+ }
922
+ function buildFrontmatterRenameCandidates(opts) {
923
+ const candidatesByNew = /* @__PURE__ */ new Map();
924
+ for (const toPath of opts.newPaths) {
925
+ if (opts.claimedNew.has(toPath)) continue;
926
+ const toNode = opts.currentByPath.get(toPath);
927
+ const matches = [];
928
+ for (const fromPath of opts.deletedPaths) {
929
+ if (opts.claimedDeleted.has(fromPath)) continue;
930
+ const fromNode = opts.priorByPath.get(fromPath);
931
+ if (toNode.frontmatterHash === fromNode.frontmatterHash) {
932
+ matches.push(fromPath);
933
+ }
934
+ }
935
+ if (matches.length > 0) candidatesByNew.set(toPath, matches);
936
+ }
937
+ return candidatesByNew;
938
+ }
939
+ function claimSingletonRenames(opts) {
940
+ const ops = [];
941
+ for (const toPath of opts.newPaths) {
942
+ if (opts.claimedNew.has(toPath)) continue;
943
+ const candidates = opts.candidatesByNew.get(toPath);
944
+ if (!candidates) continue;
945
+ const remaining = candidates.filter((p) => !opts.claimedDeleted.has(p));
946
+ if (remaining.length === 1) {
947
+ const fromPath = remaining[0];
948
+ ops.push({ from: fromPath, to: toPath, confidence: "medium" });
949
+ opts.issues.push({
950
+ ruleId: "auto-rename-medium",
951
+ severity: "warn",
952
+ nodeIds: [toPath],
953
+ message: `Auto-rename (medium confidence): ${fromPath} \u2192 ${toPath}`,
954
+ data: { from: fromPath, to: toPath, confidence: "medium" }
955
+ });
956
+ opts.claimedDeleted.add(fromPath);
957
+ opts.claimedNew.add(toPath);
958
+ }
959
+ }
960
+ return ops;
961
+ }
962
+ function flagAmbiguousRenames(opts) {
963
+ for (const toPath of opts.newPaths) {
964
+ if (opts.claimedNew.has(toPath)) continue;
965
+ const candidates = opts.candidatesByNew.get(toPath);
966
+ if (!candidates) continue;
967
+ const remaining = candidates.filter((p) => !opts.claimedDeleted.has(p));
968
+ if (remaining.length > 1) {
969
+ opts.issues.push({
970
+ ruleId: "auto-rename-ambiguous",
971
+ severity: "warn",
972
+ nodeIds: [toPath],
973
+ message: `Auto-rename ambiguous: ${toPath} matches ${remaining.length} prior frontmatters \u2014 pick one with \`sm orphans undo-rename ${toPath} --from <old.path>\`.`,
974
+ data: { to: toPath, candidates: remaining }
975
+ });
976
+ }
977
+ }
978
+ }
979
+ function flagOrphans(opts) {
980
+ for (const fromPath of opts.deletedPaths) {
981
+ if (opts.claimedDeleted.has(fromPath)) continue;
982
+ opts.issues.push({
983
+ ruleId: "orphan",
984
+ severity: "info",
985
+ nodeIds: [fromPath],
986
+ message: `Orphan history: ${fromPath} was deleted; no rename match found.`,
987
+ data: { path: fromPath }
988
+ });
989
+ }
990
+ }
991
+ function detectRenamesAndOrphans(prior, current, issues) {
992
+ const priorByPath = /* @__PURE__ */ new Map();
993
+ for (const n of prior.nodes) priorByPath.set(n.path, n);
994
+ const currentByPath = /* @__PURE__ */ new Map();
995
+ for (const n of current) currentByPath.set(n.path, n);
996
+ const deletedPaths = [...priorByPath.keys()].filter((p) => !currentByPath.has(p)).sort();
997
+ const newPaths = [...currentByPath.keys()].filter((p) => !priorByPath.has(p)).sort();
998
+ const claimedDeleted = /* @__PURE__ */ new Set();
999
+ const claimedNew = /* @__PURE__ */ new Set();
1000
+ const ops = [];
1001
+ ops.push(...findHighConfidenceRenames({
1002
+ deletedPaths,
1003
+ newPaths,
1004
+ priorByPath,
1005
+ currentByPath,
1006
+ claimedDeleted,
1007
+ claimedNew
1008
+ }));
1009
+ const candidatesByNew = buildFrontmatterRenameCandidates({
1010
+ deletedPaths,
1011
+ newPaths,
1012
+ priorByPath,
1013
+ currentByPath,
1014
+ claimedDeleted,
1015
+ claimedNew
1016
+ });
1017
+ ops.push(...claimSingletonRenames({
1018
+ newPaths,
1019
+ candidatesByNew,
1020
+ claimedDeleted,
1021
+ claimedNew,
1022
+ issues
1023
+ }));
1024
+ flagAmbiguousRenames({ newPaths, candidatesByNew, claimedDeleted, claimedNew, issues });
1025
+ flagOrphans({ deletedPaths, claimedDeleted, issues });
1026
+ return ops;
1027
+ }
1028
+ var EXTERNAL_URL_SCHEME_RE = /^[a-z][a-z0-9+\-.]+:/i;
1029
+ function isExternalUrlLink(link) {
1030
+ return EXTERNAL_URL_SCHEME_RE.test(link.target);
1031
+ }
1032
+ function makeEvent(type, data) {
1033
+ return { type, timestamp: (/* @__PURE__ */ new Date()).toISOString(), data };
1034
+ }
1035
+ function makeHookDispatcher(hooks, emitter) {
1036
+ if (hooks.length === 0) {
1037
+ return { dispatch: async () => {
1038
+ } };
1039
+ }
1040
+ const byTrigger = /* @__PURE__ */ new Map();
1041
+ for (const hook of hooks) {
1042
+ if (hook.mode === "probabilistic") {
1043
+ const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);
1044
+ log.warn(
1045
+ `Probabilistic hook ${qualifiedId} deferred to job subsystem (future job subsystem). The hook is registered but will not dispatch in-scan.`,
1046
+ { hookId: qualifiedId, mode: "probabilistic" }
1047
+ );
1048
+ continue;
1049
+ }
1050
+ for (const trig of hook.triggers) {
1051
+ const bucket = byTrigger.get(trig);
1052
+ if (bucket) bucket.push(hook);
1053
+ else byTrigger.set(trig, [hook]);
1054
+ }
1055
+ }
1056
+ return {
1057
+ async dispatch(trigger, event) {
1058
+ const subs = byTrigger.get(trigger);
1059
+ if (!subs || subs.length === 0) return;
1060
+ for (const hook of subs) {
1061
+ if (!matchesFilter(hook, event)) continue;
1062
+ const ctx = buildHookContext(hook, trigger, event);
1063
+ try {
1064
+ await hook.on(ctx);
1065
+ } catch (err) {
1066
+ const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);
1067
+ const message = err instanceof Error ? err.message : String(err);
1068
+ emitter.emit(
1069
+ makeEvent("extension.error", {
1070
+ kind: "hook-error",
1071
+ extensionId: qualifiedId,
1072
+ trigger,
1073
+ message
1074
+ })
1075
+ );
1076
+ }
1077
+ }
1078
+ }
1079
+ };
1080
+ }
1081
+ function matchesFilter(hook, event) {
1082
+ if (!hook.filter) return true;
1083
+ const data = event.data ?? {};
1084
+ for (const [key, expected] of Object.entries(hook.filter)) {
1085
+ if (data[key] !== expected) return false;
1086
+ }
1087
+ return true;
1088
+ }
1089
+ function buildHookContext(_hook, trigger, event) {
1090
+ const data = event.data ?? {};
1091
+ const ctx = {
1092
+ event: {
1093
+ type: trigger,
1094
+ timestamp: event.timestamp,
1095
+ ...event.runId !== void 0 ? { runId: event.runId } : {},
1096
+ ...event.jobId !== void 0 ? { jobId: event.jobId } : {},
1097
+ data: event.data
1098
+ }
1099
+ };
1100
+ if (typeof data["extractorId"] === "string") ctx.extractorId = data["extractorId"];
1101
+ if (typeof data["ruleId"] === "string") ctx.ruleId = data["ruleId"];
1102
+ if (typeof data["actionId"] === "string") ctx.actionId = data["actionId"];
1103
+ if (data["node"] && typeof data["node"] === "object") {
1104
+ ctx.node = data["node"];
1105
+ }
1106
+ if (data["jobResult"] !== void 0) ctx.jobResult = data["jobResult"];
1107
+ return ctx;
1108
+ }
1109
+ function buildNode(args) {
1110
+ const bytesFrontmatter = Buffer.byteLength(args.frontmatterRaw, "utf8");
1111
+ const bytesBody = Buffer.byteLength(args.body, "utf8");
1112
+ const metadata = pickMetadata(args.frontmatter);
1113
+ const node = {
1114
+ path: args.path,
1115
+ kind: args.kind,
1116
+ provider: args.providerId,
1117
+ bodyHash: args.bodyHash,
1118
+ frontmatterHash: args.frontmatterHash,
1119
+ bytes: {
1120
+ frontmatter: bytesFrontmatter,
1121
+ body: bytesBody,
1122
+ total: bytesFrontmatter + bytesBody
1123
+ },
1124
+ linksOutCount: 0,
1125
+ linksInCount: 0,
1126
+ externalRefsCount: 0,
1127
+ frontmatter: args.frontmatter,
1128
+ title: pickString(args.frontmatter["name"]),
1129
+ description: pickString(args.frontmatter["description"]),
1130
+ stability: pickStability(metadata?.["stability"]),
1131
+ version: pickString(metadata?.["version"]),
1132
+ author: pickString(args.frontmatter["author"])
1133
+ };
1134
+ if (args.encoder) {
1135
+ node.tokens = countTokens(args.encoder, args.frontmatterRaw, args.body);
1136
+ }
1137
+ return node;
1138
+ }
1139
+ function countTokens(encoder, frontmatterRaw, body) {
1140
+ const frontmatter = frontmatterRaw.length > 0 ? encoder.encode(frontmatterRaw).length : 0;
1141
+ const bodyTokens = body.length > 0 ? encoder.encode(body).length : 0;
1142
+ return { frontmatter, body: bodyTokens, total: frontmatter + bodyTokens };
1143
+ }
1144
+ function sha256(input) {
1145
+ return createHash("sha256").update(input, "utf8").digest("hex");
1146
+ }
1147
+ function canonicalFrontmatter(parsed, raw) {
1148
+ const hasParsedKeys = Object.keys(parsed).length > 0;
1149
+ const hasRawText = raw.length > 0;
1150
+ if (!hasParsedKeys && hasRawText) {
1151
+ return raw;
1152
+ }
1153
+ return yaml.dump(parsed, {
1154
+ sortKeys: true,
1155
+ lineWidth: -1,
1156
+ noRefs: true,
1157
+ noCompatMode: true
1158
+ });
1159
+ }
1160
+ function pickMetadata(fm) {
1161
+ const m = fm["metadata"];
1162
+ return m && typeof m === "object" && !Array.isArray(m) ? m : null;
1163
+ }
1164
+ function pickString(value) {
1165
+ return typeof value === "string" && value.length > 0 ? value : null;
1166
+ }
1167
+ function pickStability(value) {
1168
+ if (value === "experimental" || value === "stable" || value === "deprecated") return value;
1169
+ return null;
1170
+ }
1171
+ function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enrichNode, store) {
1172
+ const scope = extractor.scope;
1173
+ return {
1174
+ node,
1175
+ body: scope === "frontmatter" ? "" : body,
1176
+ frontmatter: scope === "body" ? {} : frontmatter,
1177
+ emitLink,
1178
+ enrichNode,
1179
+ ...store !== void 0 ? { store } : {}
1180
+ };
1181
+ }
1182
+ function validateLink(extractor, link, emitter) {
1183
+ if (!extractor.emitsLinkKinds.includes(link.kind)) {
1184
+ const qualifiedId = `${extractor.pluginId}/${extractor.id}`;
1185
+ emitter.emit(
1186
+ makeEvent("extension.error", {
1187
+ kind: "link-kind-not-declared",
1188
+ extensionId: qualifiedId,
1189
+ linkKind: link.kind,
1190
+ declaredKinds: extractor.emitsLinkKinds,
1191
+ link: { source: link.source, target: link.target, kind: link.kind },
1192
+ message: tx(ORCHESTRATOR_TEXTS.extensionErrorLinkKindNotDeclared, {
1193
+ extractorId: qualifiedId,
1194
+ linkKind: link.kind,
1195
+ declaredKinds: extractor.emitsLinkKinds.join(", ")
1196
+ })
1197
+ })
1198
+ );
1199
+ return null;
1200
+ }
1201
+ const confidence = link.confidence ?? extractor.defaultConfidence;
1202
+ return { ...link, confidence };
1203
+ }
1204
+ function validateFrontmatter(providerFrontmatter, provider, kind, frontmatter, path, strict) {
1205
+ const result = providerFrontmatter.validate(provider, kind, frontmatter);
1206
+ if (result.ok) return null;
1207
+ return {
1208
+ ruleId: "frontmatter-invalid",
1209
+ severity: strict ? "error" : "warn",
1210
+ nodeIds: [path],
1211
+ message: tx(ORCHESTRATOR_TEXTS.frontmatterInvalid, { path, kind, errors: result.errors }),
1212
+ data: { kind, errors: result.errors }
1213
+ };
1214
+ }
1215
+ function detectMalformedFrontmatter(body, path, strict) {
1216
+ const hint = classifyMalformedFrontmatter(body);
1217
+ if (!hint) return null;
1218
+ return {
1219
+ ruleId: "frontmatter-malformed",
1220
+ severity: strict ? "error" : "warn",
1221
+ nodeIds: [path],
1222
+ message: malformedMessage(hint, path),
1223
+ data: { hint }
1224
+ };
1225
+ }
1226
+ function classifyMalformedFrontmatter(body) {
1227
+ if (body.startsWith("\uFEFF")) {
1228
+ if (/^---\r?\n[\s\S]*?[A-Za-z0-9_-]+\s*:/.test(body)) {
1229
+ return "byte-order-mark";
1230
+ }
1231
+ }
1232
+ if (/^[ \t]+---\r?\n[ \t]*[A-Za-z0-9_-]+\s*:/.test(body)) {
1233
+ return "paste-with-indent";
1234
+ }
1235
+ if (/^---\r?\n[ \t]*[A-Za-z0-9_-]+\s*:/.test(body)) {
1236
+ const hasCloseFence = /\r?\n---(?:\r?\n|$)/.test(body);
1237
+ if (!hasCloseFence) {
1238
+ return "missing-close";
1239
+ }
1240
+ }
1241
+ return null;
1242
+ }
1243
+ function malformedMessage(hint, path) {
1244
+ switch (hint) {
1245
+ case "paste-with-indent":
1246
+ return tx(ORCHESTRATOR_TEXTS.frontmatterMalformedPasteWithIndent, { path });
1247
+ case "byte-order-mark":
1248
+ return tx(ORCHESTRATOR_TEXTS.frontmatterMalformedByteOrderMark, { path });
1249
+ case "missing-close":
1250
+ return tx(ORCHESTRATOR_TEXTS.frontmatterMalformedMissingClose, { path });
1251
+ }
1252
+ }
1253
+ function validateIssue(rule, issue, emitter) {
1254
+ const severity = issue.severity;
1255
+ if (severity !== "error" && severity !== "warn" && severity !== "info") {
1256
+ const qualifiedId = `${rule.pluginId}/${rule.id}`;
1257
+ emitter.emit(
1258
+ makeEvent("extension.error", {
1259
+ kind: "issue-invalid-severity",
1260
+ extensionId: qualifiedId,
1261
+ severity,
1262
+ issue: { ruleId: issue.ruleId || rule.id, message: issue.message, nodeIds: issue.nodeIds },
1263
+ message: tx(ORCHESTRATOR_TEXTS.extensionErrorIssueInvalidSeverity, {
1264
+ ruleId: qualifiedId,
1265
+ severity: JSON.stringify(severity)
1266
+ })
1267
+ })
1268
+ );
1269
+ return null;
1270
+ }
1271
+ return { ...issue, ruleId: issue.ruleId || rule.id };
1272
+ }
1273
+ function recomputeLinkCounts(nodes, links) {
1274
+ const byPath2 = /* @__PURE__ */ new Map();
1275
+ for (const node of nodes) {
1276
+ node.linksOutCount = 0;
1277
+ node.linksInCount = 0;
1278
+ byPath2.set(node.path, node);
1279
+ }
1280
+ for (const link of links) {
1281
+ const source = byPath2.get(link.source);
1282
+ if (source) source.linksOutCount += 1;
1283
+ const target = byPath2.get(link.target);
1284
+ if (target) target.linksInCount += 1;
1285
+ }
1286
+ }
1287
+ function recomputeExternalRefsCount(nodes, externalLinks, cachedPaths) {
1288
+ const byPath2 = /* @__PURE__ */ new Map();
1289
+ for (const node of nodes) {
1290
+ if (!cachedPaths.has(node.path)) node.externalRefsCount = 0;
1291
+ byPath2.set(node.path, node);
1292
+ }
1293
+ for (const link of externalLinks) {
1294
+ const source = byPath2.get(link.source);
1295
+ if (source && !cachedPaths.has(source.path)) source.externalRefsCount += 1;
1296
+ }
1297
+ }
1298
+ function mergeNodeWithEnrichments(node, enrichments, opts = {}) {
1299
+ const includeStale = opts.includeStale === true;
1300
+ const applicable = enrichments.filter((e) => e.nodePath === node.path).filter((e) => includeStale || !e.stale).sort((a, b) => a.enrichedAt - b.enrichedAt);
1301
+ const base = {};
1302
+ assignSafe(base, node.frontmatter ?? {});
1303
+ for (const row of applicable) {
1304
+ assignSafe(base, row.value);
1305
+ }
1306
+ return base;
1307
+ }
1308
+ var FORBIDDEN_MERGE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1309
+ function assignSafe(target, source) {
1310
+ for (const [k, v] of Object.entries(source)) {
1311
+ if (FORBIDDEN_MERGE_KEYS.has(k)) continue;
1312
+ target[k] = v;
1313
+ }
1314
+ }
1315
+
1316
+ // kernel/scan/watcher.ts
1317
+ import { resolve as resolve3, relative as relative2, sep } from "path";
1318
+ import chokidar from "chokidar";
1319
+ function createChokidarWatcher(opts) {
1320
+ const absRoots = opts.roots.map((r) => resolve3(opts.cwd, r));
1321
+ const ignoreFilter = opts.ignoreFilter;
1322
+ const ignored = ignoreFilter ? (path) => {
1323
+ const rel = relativePathFromRoots(path, absRoots);
1324
+ if (rel === null) return false;
1325
+ return ignoreFilter.ignores(rel);
1326
+ } : void 0;
1327
+ const watcher = chokidar.watch(absRoots, {
1328
+ ignoreInitial: true,
1329
+ persistent: true,
1330
+ ...ignored ? { ignored } : {}
1331
+ });
1332
+ let pending = [];
1333
+ let timer = null;
1334
+ let inFlight = null;
1335
+ let closed = false;
1336
+ const fire = async () => {
1337
+ timer = null;
1338
+ if (pending.length === 0) return;
1339
+ if (inFlight) {
1340
+ return;
1341
+ }
1342
+ const events = pending;
1343
+ pending = [];
1344
+ const seen = /* @__PURE__ */ new Set();
1345
+ const paths = [];
1346
+ for (const ev of events) {
1347
+ if (!seen.has(ev.absolutePath)) {
1348
+ seen.add(ev.absolutePath);
1349
+ paths.push(ev.absolutePath);
1350
+ }
1351
+ }
1352
+ inFlight = Promise.resolve(opts.onBatch({ events, paths })).catch((err) => {
1353
+ if (opts.onError) {
1354
+ opts.onError(err instanceof Error ? err : new Error(String(err)));
1355
+ }
1356
+ }).finally(() => {
1357
+ inFlight = null;
1358
+ if (!closed && pending.length > 0 && timer === null) {
1359
+ schedule();
1360
+ }
1361
+ });
1362
+ };
1363
+ const schedule = () => {
1364
+ if (closed) return;
1365
+ if (opts.debounceMs <= 0) {
1366
+ void fire();
1367
+ return;
1368
+ }
1369
+ if (timer !== null) clearTimeout(timer);
1370
+ timer = setTimeout(() => {
1371
+ void fire();
1372
+ }, opts.debounceMs);
1373
+ };
1374
+ const enqueue = (kind, absolutePath) => {
1375
+ if (closed) return;
1376
+ pending.push({ kind, absolutePath });
1377
+ schedule();
1378
+ };
1379
+ watcher.on("add", (p) => enqueue("add", p));
1380
+ watcher.on("change", (p) => enqueue("change", p));
1381
+ watcher.on("unlink", (p) => enqueue("unlink", p));
1382
+ if (opts.onError) {
1383
+ watcher.on("error", (err) => {
1384
+ opts.onError?.(err instanceof Error ? err : new Error(String(err)));
1385
+ });
1386
+ }
1387
+ const ready = new Promise((resolveReady) => {
1388
+ watcher.once("ready", () => resolveReady());
1389
+ });
1390
+ const close = async () => {
1391
+ closed = true;
1392
+ if (timer !== null) {
1393
+ clearTimeout(timer);
1394
+ timer = null;
1395
+ }
1396
+ pending = [];
1397
+ if (inFlight) {
1398
+ try {
1399
+ await inFlight;
1400
+ } catch {
1401
+ }
1402
+ }
1403
+ await watcher.close();
1404
+ };
1405
+ return { ready, close };
1406
+ }
1407
+ function relativePathFromRoots(absolute, absRoots) {
1408
+ for (const root of absRoots) {
1409
+ const rel = relative2(root, absolute);
1410
+ if (rel === "" || rel === ".") return "";
1411
+ if (!rel.startsWith("..") && !rel.startsWith(`..${sep}`)) {
1412
+ return rel.split(sep).join("/");
1413
+ }
1414
+ }
1415
+ return null;
1416
+ }
1417
+
1418
+ // kernel/scan/delta.ts
1419
+ function computeScanDelta(prior, current, comparedWith) {
1420
+ return {
1421
+ comparedWith,
1422
+ nodes: diffNodes(prior.nodes, current.nodes),
1423
+ links: diffLinks(prior.links, current.links),
1424
+ issues: diffIssues(prior.issues, current.issues)
1425
+ };
1426
+ }
1427
+ function isEmptyDelta(delta) {
1428
+ return delta.nodes.added.length === 0 && delta.nodes.removed.length === 0 && delta.nodes.changed.length === 0 && delta.links.added.length === 0 && delta.links.removed.length === 0 && delta.issues.added.length === 0 && delta.issues.removed.length === 0;
1429
+ }
1430
+ function diffNodes(priorNodes, currentNodes) {
1431
+ const priorByPath = new Map(priorNodes.map((n) => [n.path, n]));
1432
+ const currentByPath = new Map(currentNodes.map((n) => [n.path, n]));
1433
+ const added = [];
1434
+ const removed = [];
1435
+ const changed = [];
1436
+ for (const [path, after] of currentByPath) {
1437
+ const before = priorByPath.get(path);
1438
+ if (!before) {
1439
+ added.push(after);
1440
+ continue;
1441
+ }
1442
+ const reason = compareNodeHashes(before, after);
1443
+ if (reason !== null) changed.push({ before, after, reason });
1444
+ }
1445
+ for (const [path, before] of priorByPath) {
1446
+ if (!currentByPath.has(path)) removed.push(before);
1447
+ }
1448
+ added.sort(byPath);
1449
+ removed.sort(byPath);
1450
+ changed.sort((a, b) => byPath(a.after, b.after));
1451
+ return { added, removed, changed };
1452
+ }
1453
+ function compareNodeHashes(before, after) {
1454
+ const bodyChanged = before.bodyHash !== after.bodyHash;
1455
+ const fmChanged = before.frontmatterHash !== after.frontmatterHash;
1456
+ if (bodyChanged && fmChanged) return "both";
1457
+ if (bodyChanged) return "body";
1458
+ if (fmChanged) return "frontmatter";
1459
+ return null;
1460
+ }
1461
+ function byPath(a, b) {
1462
+ return a.path.localeCompare(b.path);
1463
+ }
1464
+ function diffLinks(priorLinks, currentLinks) {
1465
+ const priorKeys = new Set(priorLinks.map(linkIdentity));
1466
+ const currentKeys = new Set(currentLinks.map(linkIdentity));
1467
+ const added = [];
1468
+ const removed = [];
1469
+ for (const link of currentLinks) {
1470
+ if (!priorKeys.has(linkIdentity(link))) added.push(link);
1471
+ }
1472
+ for (const link of priorLinks) {
1473
+ if (!currentKeys.has(linkIdentity(link))) removed.push(link);
1474
+ }
1475
+ added.sort(byLinkSort);
1476
+ removed.sort(byLinkSort);
1477
+ return { added, removed };
1478
+ }
1479
+ function linkIdentity(link) {
1480
+ const trigger = link.trigger?.normalizedTrigger ?? "";
1481
+ return `${link.source}\0${link.target}\0${link.kind}\0${trigger}`;
1482
+ }
1483
+ function byLinkSort(a, b) {
1484
+ if (a.source !== b.source) return a.source.localeCompare(b.source);
1485
+ if (a.target !== b.target) return a.target.localeCompare(b.target);
1486
+ return a.kind.localeCompare(b.kind);
1487
+ }
1488
+ function diffIssues(priorIssues, currentIssues) {
1489
+ const priorKeys = new Set(priorIssues.map(issueIdentity));
1490
+ const currentKeys = new Set(currentIssues.map(issueIdentity));
1491
+ const added = [];
1492
+ const removed = [];
1493
+ for (const issue of currentIssues) {
1494
+ if (!priorKeys.has(issueIdentity(issue))) added.push(issue);
1495
+ }
1496
+ for (const issue of priorIssues) {
1497
+ if (!currentKeys.has(issueIdentity(issue))) removed.push(issue);
1498
+ }
1499
+ added.sort(byIssueSort);
1500
+ removed.sort(byIssueSort);
1501
+ return { added, removed };
1502
+ }
1503
+ function issueIdentity(issue) {
1504
+ const ids = [...issue.nodeIds].sort().join(",");
1505
+ return `${issue.ruleId}\0${ids}\0${issue.message}`;
1506
+ }
1507
+ function byIssueSort(a, b) {
1508
+ if (a.ruleId !== b.ruleId) return a.ruleId.localeCompare(b.ruleId);
1509
+ return a.message.localeCompare(b.message);
1510
+ }
1511
+
1512
+ // kernel/i18n/storage.texts.ts
1513
+ var QUERY_TEXTS = {
1514
+ exportQueryInvalidToken: 'invalid token "{{token}}": expected key=value (e.g. kind=skill, has=issues, path=foo/*).',
1515
+ exportQueryDuplicateKey: 'key "{{key}}" appears more than once; combine values with a comma instead (e.g. kind=skill,agent).',
1516
+ exportQueryEmptyValues: 'key "{{key}}" has no values.',
1517
+ exportQueryUnknownKey: 'unknown key "{{key}}". Valid keys: kind, has, path.',
1518
+ exportQueryEmptyKind: 'kind="" is not a valid node kind (empty).',
1519
+ exportQueryUnsupportedHas: 'has="{{value}}" is not supported. Valid: {{allowed}}. (findings / summary land at Steps 10 / 11.)'
1520
+ };
1521
+
1522
+ // kernel/scan/query.ts
1523
+ var HAS_VALUES = /* @__PURE__ */ new Set(["issues"]);
1524
+ var ExportQueryError = class extends Error {
1525
+ constructor(message) {
1526
+ super(message);
1527
+ this.name = "ExportQueryError";
1528
+ }
1529
+ };
1530
+ function parseExportQuery(raw) {
1531
+ const trimmed = raw.trim();
1532
+ const out = { raw: trimmed };
1533
+ if (trimmed.length === 0) return out;
1534
+ const seen = /* @__PURE__ */ new Set();
1535
+ for (const token of trimmed.split(/\s+/)) {
1536
+ const eq = token.indexOf("=");
1537
+ if (eq <= 0 || eq === token.length - 1) {
1538
+ throw new ExportQueryError(
1539
+ tx(QUERY_TEXTS.exportQueryInvalidToken, { token })
1540
+ );
1541
+ }
1542
+ const key = token.slice(0, eq).toLowerCase();
1543
+ const valuePart = token.slice(eq + 1);
1544
+ if (seen.has(key)) {
1545
+ throw new ExportQueryError(
1546
+ tx(QUERY_TEXTS.exportQueryDuplicateKey, { key })
1547
+ );
1548
+ }
1549
+ seen.add(key);
1550
+ const values = valuePart.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
1551
+ if (values.length === 0) {
1552
+ throw new ExportQueryError(tx(QUERY_TEXTS.exportQueryEmptyValues, { key }));
1553
+ }
1554
+ switch (key) {
1555
+ case "kind":
1556
+ out.kinds = parseKindValues(values);
1557
+ break;
1558
+ case "has":
1559
+ if (parseHasValues(values)) out.hasIssues = true;
1560
+ break;
1561
+ case "path":
1562
+ out.pathGlobs = values;
1563
+ break;
1564
+ default:
1565
+ throw new ExportQueryError(
1566
+ tx(QUERY_TEXTS.exportQueryUnknownKey, { key })
1567
+ );
1568
+ }
1569
+ }
1570
+ return out;
1571
+ }
1572
+ function parseKindValues(values) {
1573
+ for (const v of values) {
1574
+ if (v.length === 0) {
1575
+ throw new ExportQueryError(QUERY_TEXTS.exportQueryEmptyKind);
1576
+ }
1577
+ }
1578
+ return values;
1579
+ }
1580
+ function parseHasValues(values) {
1581
+ for (const v of values) {
1582
+ if (!HAS_VALUES.has(v)) {
1583
+ throw new ExportQueryError(
1584
+ tx(QUERY_TEXTS.exportQueryUnsupportedHas, {
1585
+ value: v,
1586
+ allowed: [...HAS_VALUES].join(", ")
1587
+ })
1588
+ );
1589
+ }
1590
+ }
1591
+ return values.includes("issues");
1592
+ }
1593
+ function applyExportQuery(scan, query) {
1594
+ const nodesWithIssues = query.hasIssues ? collectNodesWithIssues(scan.issues) : null;
1595
+ const compiledGlobs = query.pathGlobs ? query.pathGlobs.map(compileGlob) : null;
1596
+ const filteredNodes = scan.nodes.filter((node) => {
1597
+ if (query.kinds && !query.kinds.includes(node.kind)) return false;
1598
+ if (nodesWithIssues && !nodesWithIssues.has(node.path)) return false;
1599
+ if (compiledGlobs && !compiledGlobs.some((re) => re.test(node.path))) return false;
1600
+ return true;
1601
+ });
1602
+ const survivingPaths = new Set(filteredNodes.map((n) => n.path));
1603
+ const filteredLinks = scan.links.filter(
1604
+ (link) => survivingPaths.has(link.source) && survivingPaths.has(link.target)
1605
+ );
1606
+ const filteredIssues = scan.issues.filter(
1607
+ (issue) => issue.nodeIds.some((id) => survivingPaths.has(id))
1608
+ );
1609
+ return {
1610
+ query,
1611
+ nodes: filteredNodes,
1612
+ links: filteredLinks,
1613
+ issues: filteredIssues
1614
+ };
1615
+ }
1616
+ function collectNodesWithIssues(issues) {
1617
+ const out = /* @__PURE__ */ new Set();
1618
+ for (const issue of issues) {
1619
+ for (const nodeId of issue.nodeIds) out.add(nodeId);
1620
+ }
1621
+ return out;
1622
+ }
1623
+ function compileGlob(pattern) {
1624
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
1625
+ const withDouble = escaped.replace(/\*\*/g, "\0DOUBLESTAR\0");
1626
+ const withSingle = withDouble.replace(/\*/g, "[^/]*");
1627
+ const final = withSingle.replace(/DOUBLESTAR/g, ".*");
1628
+ return new RegExp(`^${final}$`);
1629
+ }
1630
+
1631
+ // kernel/ports/logger.ts
1632
+ var LOG_LEVELS = [
1633
+ "trace",
1634
+ "debug",
1635
+ "info",
1636
+ "warn",
1637
+ "error",
1638
+ "silent"
1639
+ ];
1640
+ var LEVEL_RANK = {
1641
+ trace: 0,
1642
+ debug: 1,
1643
+ info: 2,
1644
+ warn: 3,
1645
+ error: 4,
1646
+ silent: 5
1647
+ };
1648
+ function logLevelRank(level) {
1649
+ return LEVEL_RANK[level];
1650
+ }
1651
+ function isLogLevel(value) {
1652
+ return typeof value === "string" && Object.prototype.hasOwnProperty.call(LEVEL_RANK, value);
1653
+ }
1654
+ function parseLogLevel(value) {
1655
+ if (value === void 0 || value === null) return null;
1656
+ const normalized = value.trim().toLowerCase();
1657
+ if (normalized === "") return null;
1658
+ return isLogLevel(normalized) ? normalized : null;
1659
+ }
1660
+
1661
+ // kernel/index.ts
1662
+ function createKernel() {
1663
+ return { registry: new Registry() };
1664
+ }
1665
+ export {
1666
+ DuplicateExtensionError,
1667
+ EXTENSION_KINDS,
1668
+ ExportQueryError,
1669
+ HOOK_TRIGGERS,
1670
+ InMemoryProgressEmitter,
1671
+ KV_SCHEMA_KEY,
1672
+ LOG_LEVELS,
1673
+ Registry,
1674
+ SilentLogger,
1675
+ applyExportQuery,
1676
+ computeScanDelta,
1677
+ configureLogger,
1678
+ createChokidarWatcher,
1679
+ createKernel,
1680
+ detectRenamesAndOrphans,
1681
+ getActiveLogger,
1682
+ isEmptyDelta,
1683
+ isLogLevel,
1684
+ log,
1685
+ logLevelRank,
1686
+ makeDedicatedStoreWrapper,
1687
+ makeKvStoreWrapper,
1688
+ makePluginStore,
1689
+ mergeNodeWithEnrichments,
1690
+ parseExportQuery,
1691
+ parseLogLevel,
1692
+ qualifiedExtensionId,
1693
+ resetLogger,
1694
+ runExtractorsForNode,
1695
+ runScan,
1696
+ runScanWithRenames
1697
+ };
1698
+ //# sourceMappingURL=index.js.map