@skill-map/cli 0.51.0 → 0.53.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.
Files changed (53) hide show
  1. package/dist/cli/tutorial/sm-tutorial/SKILL.md +239 -1659
  2. package/dist/cli/tutorial/sm-tutorial/references/_core.md +332 -0
  3. package/dist/cli/tutorial/sm-tutorial/references/_manifest.yml +175 -0
  4. package/dist/cli/tutorial/sm-tutorial/references/fixtures.md +251 -0
  5. package/dist/cli/tutorial/{sm-master/references/tour-authoring.md → sm-tutorial/references/part-authoring.md} +14 -15
  6. package/dist/cli/tutorial/sm-tutorial/references/part-cli.md +267 -0
  7. package/dist/cli/tutorial/sm-tutorial/references/part-connect-harness.md +180 -0
  8. package/dist/cli/tutorial/sm-tutorial/references/part-fundamentals.md +424 -0
  9. package/dist/cli/tutorial/sm-tutorial/references/part-live-site.md +156 -0
  10. package/dist/cli/tutorial/sm-tutorial/references/part-maintain.md +286 -0
  11. package/dist/cli/tutorial/sm-tutorial/references/part-mcp.md +78 -0
  12. package/dist/cli/tutorial/{sm-master/references/tour-plugins.md → sm-tutorial/references/part-plugins.md} +11 -11
  13. package/dist/cli/tutorial/sm-tutorial/references/part-project-kickoff.md +186 -0
  14. package/dist/cli/tutorial/{sm-master/references/tour-settings.md → sm-tutorial/references/part-settings.md} +22 -24
  15. package/dist/cli.js +1253 -564
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.js +335 -208
  18. package/dist/kernel/index.d.ts +320 -15
  19. package/dist/kernel/index.js +335 -208
  20. package/dist/migrations/001_initial.sql +36 -0
  21. package/dist/ui/chunk-EQ72PEHT.js +1 -0
  22. package/dist/ui/chunk-GBKHMJ4B.js +1110 -0
  23. package/dist/ui/chunk-GEI6INVH.js +515 -0
  24. package/dist/ui/chunk-JXRIGHET.js +552 -0
  25. package/dist/ui/{chunk-WQMZOINB.js → chunk-K2MAVAHG.js} +1 -1
  26. package/dist/ui/{chunk-BV323KTK.js → chunk-KHARMPTZ.js} +1 -1
  27. package/dist/ui/chunk-L4NIF75A.js +2 -0
  28. package/dist/ui/chunk-LCOYSPKE.js +1 -0
  29. package/dist/ui/chunk-OFDQMBSJ.js +1 -0
  30. package/dist/ui/chunk-P2DAPRK7.js +2 -0
  31. package/dist/ui/chunk-Q2A6FWC7.js +4 -0
  32. package/dist/ui/chunk-TXTY24G4.js +2204 -0
  33. package/dist/ui/chunk-UBQUCSQ4.js +1 -0
  34. package/dist/ui/chunk-WFLPMCK4.js +392 -0
  35. package/dist/ui/chunk-WHZVGOS3.js +5 -0
  36. package/dist/ui/chunk-YQFIXHKM.js +123 -0
  37. package/dist/ui/index.html +2 -2
  38. package/dist/ui/main-OYITFJ7B.js +4 -0
  39. package/dist/ui/{styles-RG7Y33BT.css → styles-Q4NCOJQY.css} +1 -1
  40. package/migrations/001_initial.sql +36 -0
  41. package/package.json +10 -8
  42. package/dist/cli/tutorial/sm-master/SKILL.md +0 -688
  43. package/dist/cli/tutorial/sm-master/references/fixture-templates.md +0 -212
  44. package/dist/ui/chunk-2GXE52AJ.js +0 -123
  45. package/dist/ui/chunk-AEA5GIA7.js +0 -1
  46. package/dist/ui/chunk-KHRNVLJW.js +0 -1
  47. package/dist/ui/chunk-OZTRR4M7.js +0 -2312
  48. package/dist/ui/chunk-Q5YJKCTP.js +0 -1066
  49. package/dist/ui/chunk-RCT3JSFL.js +0 -1
  50. package/dist/ui/chunk-VBTLX7GH.js +0 -1110
  51. package/dist/ui/chunk-VJ57LHDR.js +0 -4
  52. package/dist/ui/chunk-WMGW2UAL.js +0 -2
  53. package/dist/ui/main-N7D2YBEX.js +0 -4
@@ -1,6 +1,6 @@
1
1
  // kernel/i18n/registry.texts.ts
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="77a22e54-0917-5b5e-a29d-af0ad20bed19")}catch(e){}}();
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5a40a4c9-3996-540c-b687-874d383944d0")}catch(e){}}();
4
4
  var REGISTRY_TEXTS = {
5
5
  duplicateExtension: "Extension already registered: {{kind}}:{{qualifiedId}}",
6
6
  unknownKind: "Unknown extension kind: {{kind}}",
@@ -102,7 +102,7 @@ import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
102
102
  // package.json
103
103
  var package_default = {
104
104
  name: "@skill-map/cli",
105
- version: "0.51.0",
105
+ version: "0.53.0",
106
106
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
107
107
  license: "MIT",
108
108
  type: "module",
@@ -160,15 +160,16 @@ var package_default = {
160
160
  prebuild: "pnpm build-built-ins",
161
161
  validate: "pnpm validate:compile && pnpm validate:test",
162
162
  "validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm built-ins:check && pnpm view-catalog:check",
163
- "validate:test": "pnpm test:ci",
163
+ "validate:test": "pnpm test:ci && pnpm conformance",
164
+ conformance: "SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 node --import tsx cli/entry.ts conformance run",
164
165
  pretest: "tsup",
165
166
  "pretest:coverage": "tsup",
166
167
  "pretest:coverage:html": "tsup",
167
- test: "tsc --noEmit && SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
168
- "test:ci": "FORCE_COLOR=1 SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
169
- "test:spec": "SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=spec '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
170
- "test:coverage": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SKILL_MAP_SKIP_BENCHMARK=1 node --experimental-default-config-file --import tsx --test --experimental-test-coverage '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
171
- "test:coverage:html": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SKILL_MAP_SKIP_BENCHMARK=1 c8 node --import tsx --test '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
168
+ test: "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
169
+ "test:ci": "FORCE_COLOR=1 SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
170
+ "test:spec": "SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 node --import tsx --test --test-reporter=spec '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
171
+ "test:coverage": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 SKILL_MAP_SKIP_BENCHMARK=1 node --experimental-default-config-file --import tsx --test --experimental-test-coverage '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
172
+ "test:coverage:html": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 SKILL_MAP_SKIP_BENCHMARK=1 c8 node --import tsx --test '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
172
173
  clean: "rm -rf dist coverage"
173
174
  },
174
175
  dependencies: {
@@ -200,6 +201,7 @@ var package_default = {
200
201
  c8: "11.0.0",
201
202
  eslint: "10.2.1",
202
203
  "eslint-plugin-import-x": "4.16.2",
204
+ "json-schema-to-typescript": "15.0.4",
203
205
  tsup: "8.5.1",
204
206
  tsx: "4.22.3",
205
207
  typescript: "5.9.3",
@@ -228,150 +230,54 @@ var InMemoryProgressEmitter = class {
228
230
  };
229
231
 
230
232
  // kernel/adapters/plugin-loader/index.ts
231
- import { createRequire } from "module";
232
- import { existsSync as existsSync2, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
233
- import { join as join2, resolve as resolve3 } from "path";
233
+ import { createRequire as createRequire2 } from "module";
234
+ import { existsSync as existsSync2, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
235
+ import { join as join2, resolve as resolve4 } from "path";
234
236
  import { pathToFileURL } from "url";
235
237
  import semver from "semver";
236
238
 
237
- // kernel/adapters/plugin-loader/id-utils.ts
238
- import { isAbsolute, relative, resolve } from "path";
239
-
240
- // kernel/adapters/plugin-loader/validation.ts
241
- import * as nodeFs from "fs";
242
- import { existsSync } from "fs";
243
- import { dirname, join } from "path";
244
- import { Ajv2020 } from "ajv/dist/2020.js";
245
-
246
- // kernel/util/ajv-interop.ts
247
- import addFormatsModule from "ajv-formats";
248
- var addFormats = addFormatsModule.default ?? addFormatsModule;
249
- function applyAjvFormats(ajv) {
250
- addFormats(ajv);
251
- }
252
-
253
- // kernel/extensions/hook.ts
254
- var HOOK_TRIGGERS = Object.freeze([
255
- "boot",
256
- "scan.started",
257
- "scan.completed",
258
- "extractor.completed",
259
- "analyzer.completed",
260
- "action.completed",
261
- "job.spawning",
262
- "job.completed",
263
- "job.failed",
264
- "shutdown"
265
- ]);
266
-
267
- // kernel/adapters/plugin-loader/validation.ts
268
- var KNOWN_KINDS = /* @__PURE__ */ new Set([
269
- "provider",
270
- "extractor",
271
- "analyzer",
272
- "action",
273
- "formatter",
274
- "hook"
275
- ]);
276
- var KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(" / ");
277
- var HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(", ");
278
-
279
- // kernel/adapters/plugin-loader/storage-schemas.ts
280
- import { readFileSync as readFileSync2 } from "fs";
281
- import { resolve as resolve2 } from "path";
282
- import { Ajv2020 as Ajv20202 } from "ajv/dist/2020.js";
283
-
284
- // kernel/i18n/plugin-store.texts.ts
285
- var PLUGIN_STORE_TEXTS = {
286
- kvValidationFailed: "plugin '{{pluginId}}' ctx.store.set('{{key}}', value): value violates declared schema ({{schemaPath}}): {{errors}}",
287
- dedicatedValidationFailed: "plugin '{{pluginId}}' ctx.store.write('{{table}}', row): row violates declared schema ({{schemaPath}}): {{errors}}"
239
+ // kernel/i18n/plugin-loader.texts.ts
240
+ var SPEC_GITHUB_BASE = "https://github.com/crystian/skill-map/blob/main";
241
+ var PLUGIN_LOADER_TEXTS = {
242
+ invalidManifestJsonParse: "{{manifestPath}}: {{errDescription}}. Validate the JSON (e.g. `npx jsonlint plugin.json`).",
243
+ invalidManifestAjv: `{{manifestPath}}: {{errors}}. See ${SPEC_GITHUB_BASE}/spec/schemas/plugins-registry.schema.json#/$defs/PluginManifest.`,
244
+ invalidSpecCompat: 'specCompat "{{specCompat}}" is not a valid semver range. Use a range like "^1.0.0".',
245
+ incompatibleSpec: `@skill-map/spec {{installedSpecVersion}} does not satisfy specCompat "{{specCompat}}". Either update the plugin's specCompat (and re-test) or pin sm to a compatible spec version.`,
246
+ loadErrorFileNotFound: "extension file not found: {{relEntry}} (resolved to {{abs}}). Check plugin.json#/extensions paths.",
247
+ loadErrorImportFailed: "{{relEntry}}: import failed: {{errDescription}}",
248
+ loadErrorMissingKind: "{{relEntry}}: default export missing a string `kind` field. Expected one of: {{knownKindsList}}.",
249
+ loadErrorUnknownKind: '{{relEntry}}: unknown extension kind "{{kindReceived}}". Expected one of: {{knownKindsList}}.',
250
+ // No "manifest invalid" framing here: the warning wrapper already
251
+ // carries the `(invalid-manifest)` status, so this reason is just the
252
+ // file + the specific error + the doc link. `{{docUrl}}` is chosen by
253
+ // the caller: a bad view-slot value points to the slot catalog
254
+ // (`spec/view-slots.md`), every other manifest-shape error to the kind
255
+ // schema. Both are GitHub blob URLs.
256
+ invalidManifestExtensionShape: "{{relEntry}}: {{errors}}. See {{docUrl}}.",
257
+ importExceededTimeout: "import exceeded {{timeoutMs}}ms; likely a top-level side effect (network call, infinite loop, large blocking work). Move side effects into the runtime methods (`detect` / `evaluate` / `render` / etc.).",
258
+ disabledByConfig: "disabled by config_plugins or settings.json",
259
+ invalidManifestDirMismatch: "directory name '{{dirName}}' does not match manifest id '{{manifestId}}'. Rename the directory to match the id, or update the manifest id to match the directory.",
260
+ idCollision: "Plugin '{{id}}' at {{pathA}} collides with the plugin at {{pathB}}. Rename one and rerun.",
261
+ loadErrorPluginIdMismatch: "{{relEntry}}: extension declares pluginId '{{declared}}' but its plugin.json declares id '{{manifestId}}'. Remove the explicit pluginId from the extension; the loader injects it from plugin.json#/id.",
262
+ invalidManifestRedeclaredField: "{{relEntry}}: extension manifest declares {{fields}}, derived from the folder layout (structure-as-truth) and not a manifest field. Remove it: id is the leaf folder, kind the parent folder, provider kinds the `kinds/` catalog, formatter formatId the formatter folder name.",
263
+ loadErrorStorageSchemaRead: "plugin '{{pluginId}}' failed to load schema for table '{{table}}': {{schemaPath}}: {{errDescription}}",
264
+ loadErrorStorageSchemaCompile: "plugin '{{pluginId}}' failed to compile schema for table '{{table}}': {{schemaPath}}: {{errDescription}}",
265
+ loadErrorStorageKvSchemaRead: "plugin '{{pluginId}}' failed to load KV schema: {{schemaPath}}: {{errDescription}}",
266
+ loadErrorStorageKvSchemaCompile: "plugin '{{pluginId}}' failed to compile KV schema: {{schemaPath}}: {{errDescription}}",
267
+ invalidManifestHookUnknownTrigger: "Hook '{{hookId}}' declares unknown trigger '{{trigger}}'. Hookable triggers: {{hookableList}}.",
268
+ invalidManifestHookEmptyTriggers: "Hook '{{hookId}}' declares no triggers. At least one entry from the curated set is required.",
269
+ loadErrorPathEscapesPlugin: "extension entry '{{relEntry}}' resolves outside the plugin directory ({{pluginPath}}). Plugin entries must be relative paths inside the plugin tree.",
270
+ loadErrorSchemaPathEscapesPlugin: "schema path '{{relPath}}' resolves outside the plugin directory ({{pluginPath}}). Plugin schemas must be relative paths inside the plugin tree.",
271
+ invalidManifestRootSharedAnnotation: "{{relEntry}}: annotationContributions['{{key}}'] declares location: 'root' with ownership: '{{ownership}}'; root keys MUST be 'exclusive' (a top-level reserved key cannot be silently shared between plugins).",
272
+ invalidManifestAnnotationSchemaCompile: "{{relEntry}}: annotationContributions['{{key}}'].schema is not a valid JSON Schema: {{errDescription}}",
273
+ fatalAnnotationRootCollision: "Annotation root-key collision: '{{key}}' is claimed with ownership: 'exclusive' by multiple plugins ({{plugins}}). The kernel cannot boot with this configuration. Rename or merge the contributions and rerun."
288
274
  };
289
275
 
290
- // kernel/adapters/plugin-store.ts
291
- var KV_SCHEMA_KEY = "__kv__";
292
- function makeKvStoreWrapper(opts) {
293
- const { pluginId, schema, persist } = opts;
294
- return {
295
- async set(key, value) {
296
- if (schema) {
297
- if (!schema.validate(value)) {
298
- throw new Error(
299
- tx(PLUGIN_STORE_TEXTS.kvValidationFailed, {
300
- pluginId,
301
- schemaPath: schema.schemaPath,
302
- key,
303
- errors: formatAjvErrors(schema.validate.errors ?? null)
304
- })
305
- );
306
- }
307
- }
308
- await persist(key, value);
309
- }
310
- };
311
- }
312
- function makeDedicatedStoreWrapper(opts) {
313
- const { pluginId, schemas, persist } = opts;
314
- return {
315
- async write(table, row) {
316
- const schema = schemas?.[table];
317
- if (schema) {
318
- if (!schema.validate(row)) {
319
- throw new Error(
320
- tx(PLUGIN_STORE_TEXTS.dedicatedValidationFailed, {
321
- pluginId,
322
- table,
323
- schemaPath: schema.schemaPath,
324
- errors: formatAjvErrors(schema.validate.errors ?? null)
325
- })
326
- );
327
- }
328
- }
329
- await persist(table, row);
330
- }
331
- };
332
- }
333
- function makePluginStore(opts) {
334
- const manifest = opts.plugin.manifest;
335
- if (!manifest?.storage) return void 0;
336
- const storageSchemas = opts.plugin.storageSchemas;
337
- if (manifest.storage.mode === "kv") {
338
- if (!opts.persistKv) return void 0;
339
- const schema = storageSchemas?.[KV_SCHEMA_KEY];
340
- return makeKvStoreWrapper({
341
- pluginId: opts.plugin.id,
342
- schema,
343
- persist: opts.persistKv
344
- });
345
- }
346
- if (manifest.storage.mode === "dedicated") {
347
- if (!opts.persistDedicated) return void 0;
348
- return makeDedicatedStoreWrapper({
349
- pluginId: opts.plugin.id,
350
- schemas: storageSchemas,
351
- persist: opts.persistDedicated
352
- });
353
- }
354
- return void 0;
355
- }
356
- function formatAjvErrors(errors) {
357
- if (!errors || errors.length === 0) return "(no AJV details)";
358
- return errors.map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
359
- }
360
-
361
- // kernel/adapters/plugin-loader/index.ts
362
- function installedSpecVersion() {
363
- const require2 = createRequire(import.meta.url);
364
- const indexPath = require2.resolve("@skill-map/spec/index.json");
365
- const pkgPath = resolve3(indexPath, "..", "package.json");
366
- const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
367
- return pkg.version;
368
- }
369
-
370
276
  // kernel/adapters/schema-validators.ts
371
- import { readFileSync as readFileSync4 } from "fs";
372
- import { dirname as dirname2, resolve as resolve4 } from "path";
373
- import { createRequire as createRequire2 } from "module";
374
- import { Ajv2020 as Ajv20203 } from "ajv/dist/2020.js";
277
+ import { readFileSync } from "fs";
278
+ import { dirname, resolve } from "path";
279
+ import { createRequire } from "module";
280
+ import { Ajv2020 } from "ajv/dist/2020.js";
375
281
 
376
282
  // kernel/types/view-catalog.generated.ts
377
283
  var ALL_SLOT_NAMES = [
@@ -380,8 +286,8 @@ var ALL_SLOT_NAMES = [
380
286
  "card.footer.left",
381
287
  "card.footer.right",
382
288
  "graph.node.alert",
383
- "inspector.header.badge.counter",
384
- "inspector.header.badge.tag",
289
+ "inspector.header.badge",
290
+ "inspector.action.button",
385
291
  "inspector.body.panel.breakdown",
386
292
  "inspector.body.panel.records",
387
293
  "inspector.body.panel.tree",
@@ -392,6 +298,13 @@ var ALL_SLOT_NAMES = [
392
298
  ];
393
299
  var KNOWN_SLOT_NAMES = new Set(ALL_SLOT_NAMES);
394
300
 
301
+ // kernel/util/ajv-interop.ts
302
+ import addFormatsModule from "ajv-formats";
303
+ var addFormats = addFormatsModule.default ?? addFormatsModule;
304
+ function applyAjvFormats(ajv) {
305
+ addFormats(ajv);
306
+ }
307
+
395
308
  // kernel/adapters/schema-validators.ts
396
309
  var SCHEMA_FILES = {
397
310
  node: "schemas/node.schema.json",
@@ -430,22 +343,22 @@ function loadSchemaValidators() {
430
343
  }
431
344
  function buildSchemaValidators() {
432
345
  const specRoot = resolveSpecRoot();
433
- const ajv = new Ajv20203({
346
+ const ajv = new Ajv2020({
434
347
  strict: false,
435
348
  allErrors: true,
436
349
  allowUnionTypes: true
437
350
  });
438
351
  applyAjvFormats(ajv);
439
352
  for (const rel of SUPPORTING_SCHEMAS) {
440
- const file = resolve4(specRoot, rel);
353
+ const file = resolve(specRoot, rel);
441
354
  if (!existsSyncSafe(file)) continue;
442
- const schema = JSON.parse(readFileSync4(file, "utf8"));
355
+ const schema = JSON.parse(readFileSync(file, "utf8"));
443
356
  ajv.addSchema(schema);
444
357
  }
445
358
  const validators = /* @__PURE__ */ new Map();
446
359
  for (const [name, rel] of Object.entries(SCHEMA_FILES)) {
447
- const file = resolve4(specRoot, rel);
448
- const schema = JSON.parse(readFileSync4(file, "utf8"));
360
+ const file = resolve(specRoot, rel);
361
+ const schema = JSON.parse(readFileSync(file, "utf8"));
449
362
  const byId = typeof schema.$id === "string" ? ajv.getSchema(schema.$id) : void 0;
450
363
  validators.set(name, byId ?? ajv.compile(schema));
451
364
  }
@@ -489,12 +402,12 @@ function buildSchemaValidators() {
489
402
  const v = validators.get(name);
490
403
  if (!v) throw new Error(`Unknown schema: ${name}`);
491
404
  if (v(data)) return { ok: true, data };
492
- const errors = (v.errors ?? []).map(formatError).join("; ");
405
+ const errors = formatAjvErrors(v.errors);
493
406
  return { ok: false, errors };
494
407
  },
495
408
  validatePluginManifest(data) {
496
409
  if (pluginManifestValidator(data)) return { ok: true, data };
497
- const errors = (pluginManifestValidator.errors ?? []).map(formatError).join("; ");
410
+ const errors = formatAjvErrors(pluginManifestValidator.errors);
498
411
  return { ok: false, errors };
499
412
  },
500
413
  validateContributionPayload(slot, payload) {
@@ -503,21 +416,21 @@ function buildSchemaValidators() {
503
416
  return { ok: false, errors: "unknown-slot" };
504
417
  }
505
418
  if (validator(payload)) return { ok: true };
506
- const errors = (validator.errors ?? []).map(formatError).join("; ");
419
+ const errors = formatAjvErrors(validator.errors);
507
420
  return { ok: false, errors };
508
421
  }
509
422
  };
510
423
  }
511
424
  function buildProviderFrontmatterValidator(providers) {
512
425
  const specRoot = resolveSpecRoot();
513
- const ajv = new Ajv20203({
426
+ const ajv = new Ajv2020({
514
427
  strict: false,
515
428
  allErrors: true,
516
429
  allowUnionTypes: true
517
430
  });
518
431
  applyAjvFormats(ajv);
519
- const baseFile = resolve4(specRoot, "schemas/frontmatter/base.schema.json");
520
- const baseSchema = JSON.parse(readFileSync4(baseFile, "utf8"));
432
+ const baseFile = resolve(specRoot, "schemas/frontmatter/base.schema.json");
433
+ const baseSchema = JSON.parse(readFileSync(baseFile, "utf8"));
521
434
  ajv.addSchema(baseSchema);
522
435
  registerProviderAuxiliarySchemas(ajv, providers);
523
436
  const compiled = /* @__PURE__ */ new Map();
@@ -535,7 +448,7 @@ function buildProviderFrontmatterValidator(providers) {
535
448
  const v = compiled.get(key);
536
449
  if (!v) return { ok: false, errors: "no-schema" };
537
450
  if (v(data)) return { ok: true };
538
- const errors = (v.errors ?? []).map(formatError).join("; ");
451
+ const errors = formatAjvErrors(v.errors);
539
452
  return { ok: false, errors };
540
453
  }
541
454
  };
@@ -544,6 +457,47 @@ function formatError(err) {
544
457
  const path = err.instancePath || "(root)";
545
458
  return `${path} ${err.message ?? err.keyword}`;
546
459
  }
460
+ function formatAjvErrors(errors) {
461
+ const list = errors ?? [];
462
+ if (list.length === 0) return "";
463
+ const byPath2 = /* @__PURE__ */ new Map();
464
+ for (const e of list) {
465
+ const path = e.instancePath || "(root)";
466
+ const bucket = byPath2.get(path);
467
+ if (bucket) bucket.push(e);
468
+ else byPath2.set(path, [e]);
469
+ }
470
+ const parts = [];
471
+ for (const [path, errs] of byPath2) parts.push(...formatPathErrors(path, errs));
472
+ return [...new Set(parts)].join("; ");
473
+ }
474
+ function constBranchValues(errs) {
475
+ let count = 0;
476
+ for (const e of errs) {
477
+ const isConst = e.keyword === "const" && typeof e.params === "object" && e.params !== null && "allowedValue" in e.params;
478
+ if (isConst) count += 1;
479
+ }
480
+ return count;
481
+ }
482
+ function formatPathErrors(path, errs) {
483
+ if (constBranchValues(errs) >= 2) {
484
+ const parts2 = [`${path} is not a valid value`];
485
+ for (const e of errs) {
486
+ if (e.keyword !== "const" && e.keyword !== "oneOf") parts2.push(formatError(e));
487
+ }
488
+ return parts2;
489
+ }
490
+ const seen = /* @__PURE__ */ new Set();
491
+ const parts = [];
492
+ for (const e of errs) {
493
+ const msg = formatError(e);
494
+ if (!seen.has(msg)) {
495
+ seen.add(msg);
496
+ parts.push(msg);
497
+ }
498
+ }
499
+ return parts;
500
+ }
547
501
  function registerProviderAuxiliarySchemas(ajv, providers) {
548
502
  for (const provider of providers) {
549
503
  if (!provider.schemas) continue;
@@ -555,10 +509,10 @@ function registerProviderAuxiliarySchemas(ajv, providers) {
555
509
  }
556
510
  }
557
511
  function resolveSpecRoot() {
558
- const require2 = createRequire2(import.meta.url);
512
+ const require2 = createRequire(import.meta.url);
559
513
  try {
560
514
  const indexPath = require2.resolve("@skill-map/spec/index.json");
561
- return dirname2(indexPath);
515
+ return dirname(indexPath);
562
516
  } catch {
563
517
  throw new Error(
564
518
  "@skill-map/spec not resolvable: ensure the workspace is linked or the package is installed."
@@ -567,13 +521,139 @@ function resolveSpecRoot() {
567
521
  }
568
522
  function existsSyncSafe(path) {
569
523
  try {
570
- readFileSync4(path, "utf8");
524
+ readFileSync(path, "utf8");
571
525
  return true;
572
526
  } catch {
573
527
  return false;
574
528
  }
575
529
  }
576
530
 
531
+ // kernel/adapters/plugin-loader/id-utils.ts
532
+ import { isAbsolute, relative, resolve as resolve2 } from "path";
533
+
534
+ // kernel/adapters/plugin-loader/validation.ts
535
+ import * as nodeFs from "fs";
536
+ import { existsSync } from "fs";
537
+ import { dirname as dirname2, join } from "path";
538
+ import { Ajv2020 as Ajv20202 } from "ajv/dist/2020.js";
539
+
540
+ // kernel/extensions/hook.ts
541
+ var HOOK_TRIGGERS = Object.freeze([
542
+ "boot",
543
+ "scan.started",
544
+ "scan.completed",
545
+ "extractor.completed",
546
+ "analyzer.completed",
547
+ "action.completed",
548
+ "job.spawning",
549
+ "job.completed",
550
+ "job.failed",
551
+ "shutdown"
552
+ ]);
553
+
554
+ // kernel/adapters/plugin-loader/validation.ts
555
+ var KNOWN_KINDS = /* @__PURE__ */ new Set([
556
+ "provider",
557
+ "extractor",
558
+ "analyzer",
559
+ "action",
560
+ "formatter",
561
+ "hook"
562
+ ]);
563
+ var KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(" / ");
564
+ var HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(", ");
565
+
566
+ // kernel/adapters/plugin-loader/storage-schemas.ts
567
+ import { readFileSync as readFileSync3 } from "fs";
568
+ import { resolve as resolve3 } from "path";
569
+ import { Ajv2020 as Ajv20203 } from "ajv/dist/2020.js";
570
+
571
+ // kernel/i18n/plugin-store.texts.ts
572
+ var PLUGIN_STORE_TEXTS = {
573
+ kvValidationFailed: "plugin '{{pluginId}}' ctx.store.set('{{key}}', value): value violates declared schema ({{schemaPath}}): {{errors}}",
574
+ dedicatedValidationFailed: "plugin '{{pluginId}}' ctx.store.write('{{table}}', row): row violates declared schema ({{schemaPath}}): {{errors}}"
575
+ };
576
+
577
+ // kernel/adapters/plugin-store.ts
578
+ var KV_SCHEMA_KEY = "__kv__";
579
+ function makeKvStoreWrapper(opts) {
580
+ const { pluginId, schema, persist } = opts;
581
+ return {
582
+ async set(key, value) {
583
+ if (schema) {
584
+ if (!schema.validate(value)) {
585
+ throw new Error(
586
+ tx(PLUGIN_STORE_TEXTS.kvValidationFailed, {
587
+ pluginId,
588
+ schemaPath: schema.schemaPath,
589
+ key,
590
+ errors: formatAjvErrors2(schema.validate.errors ?? null)
591
+ })
592
+ );
593
+ }
594
+ }
595
+ await persist(key, value);
596
+ }
597
+ };
598
+ }
599
+ function makeDedicatedStoreWrapper(opts) {
600
+ const { pluginId, schemas, persist } = opts;
601
+ return {
602
+ async write(table, row) {
603
+ const schema = schemas?.[table];
604
+ if (schema) {
605
+ if (!schema.validate(row)) {
606
+ throw new Error(
607
+ tx(PLUGIN_STORE_TEXTS.dedicatedValidationFailed, {
608
+ pluginId,
609
+ table,
610
+ schemaPath: schema.schemaPath,
611
+ errors: formatAjvErrors2(schema.validate.errors ?? null)
612
+ })
613
+ );
614
+ }
615
+ }
616
+ await persist(table, row);
617
+ }
618
+ };
619
+ }
620
+ function makePluginStore(opts) {
621
+ const manifest = opts.plugin.manifest;
622
+ if (!manifest?.storage) return void 0;
623
+ const storageSchemas = opts.plugin.storageSchemas;
624
+ if (manifest.storage.mode === "kv") {
625
+ if (!opts.persistKv) return void 0;
626
+ const schema = storageSchemas?.[KV_SCHEMA_KEY];
627
+ return makeKvStoreWrapper({
628
+ pluginId: opts.plugin.id,
629
+ schema,
630
+ persist: opts.persistKv
631
+ });
632
+ }
633
+ if (manifest.storage.mode === "dedicated") {
634
+ if (!opts.persistDedicated) return void 0;
635
+ return makeDedicatedStoreWrapper({
636
+ pluginId: opts.plugin.id,
637
+ schemas: storageSchemas,
638
+ persist: opts.persistDedicated
639
+ });
640
+ }
641
+ return void 0;
642
+ }
643
+ function formatAjvErrors2(errors) {
644
+ if (!errors || errors.length === 0) return "(no AJV details)";
645
+ return errors.map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
646
+ }
647
+
648
+ // kernel/adapters/plugin-loader/index.ts
649
+ function installedSpecVersion() {
650
+ const require2 = createRequire2(import.meta.url);
651
+ const indexPath = require2.resolve("@skill-map/spec/index.json");
652
+ const pkgPath = resolve4(indexPath, "..", "package.json");
653
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
654
+ return pkg.version;
655
+ }
656
+
577
657
  // kernel/util/format-error.ts
578
658
  function formatErrorMessage(err) {
579
659
  return err instanceof Error ? err.message : String(err);
@@ -691,7 +771,7 @@ var ORCHESTRATOR_TEXTS = {
691
771
  frontmatterMalformedMissingClose: "Frontmatter in {{path}} opens with `---` but never closes (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.",
692
772
  extensionErrorLinkKindNotDeclared: 'Extractor "{{extractorId}}" emitted a link of kind "{{linkKind}}" outside its declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',
693
773
  extensionErrorIssueInvalidSeverity: `Rule "{{analyzerId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
694
- extensionErrorContributionUnknownId: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}} but did not declare it in its `viewContributions` map. Contribution dropped.',
774
+ extensionErrorContributionUndeclaredRef: 'Extension "{{extractorId}}" emitted a view contribution on {{nodePath}} whose object is not one declared in its `ui` map (pass the declared const by reference, do not spread or inline it). Contribution dropped.',
695
775
  extensionErrorContributionPayloadInvalid: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}}; payload failed the "{{slot}}" schema: {{errors}}. Contribution dropped.',
696
776
  extensionErrorRecommendedActionMissing: 'Analyzer "{{analyzerId}}" declares recommendedAction "{{actionId}}" but no Action is registered under that qualified id. The analyzer stays registered; the recommendation will not surface in the inspector.',
697
777
  runScanRootEmptyArray: "runScan: roots must contain at least one path (spec requires minItems: 1)",
@@ -728,6 +808,7 @@ async function runExtractorsForNode(opts) {
728
808
  const externalLinks = [];
729
809
  const enrichmentBuffer = /* @__PURE__ */ new Map();
730
810
  const contributions = [];
811
+ const contributionErrors = [];
731
812
  const signals = [];
732
813
  const virtualNodes = [];
733
814
  const virtualNodePaths = /* @__PURE__ */ new Set();
@@ -759,36 +840,54 @@ async function runExtractorsForNode(opts) {
759
840
  });
760
841
  }
761
842
  };
762
- const declaredContributions = readDeclaredContributions(extractor);
763
- const emitContribution = (contributionId, payload) => {
764
- const declared = declaredContributions.get(contributionId);
843
+ const declaredContributions = readDeclaredContributionRefs(extractor);
844
+ const emitContribution = (ref, payload) => {
845
+ const declared = typeof ref === "object" && ref !== null ? declaredContributions.get(ref) : void 0;
765
846
  if (!declared) {
847
+ const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUndeclaredRef, {
848
+ extractorId: qualifiedId,
849
+ nodePath: opts.node.path
850
+ });
766
851
  emitExtensionError(opts.emitter, qualifiedId, opts.node.path, {
767
852
  phase: "emitContribution",
768
- contributionId,
769
- reason: "unknown-contribution-id",
770
- message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUnknownId, {
771
- extractorId: qualifiedId,
772
- contributionId,
773
- nodePath: opts.node.path
774
- })
853
+ reason: "undeclared-contribution-ref",
854
+ message
855
+ });
856
+ contributionErrors.push({
857
+ pluginId: extractor.pluginId,
858
+ extensionId: extractor.id,
859
+ nodePath: opts.node.path,
860
+ reason: "undeclared-contribution-ref",
861
+ message,
862
+ emittedAt: Date.now()
775
863
  });
776
864
  return;
777
865
  }
778
866
  const result = validators.validateContributionPayload(declared.slot, payload);
779
867
  if (!result.ok) {
868
+ const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
869
+ extractorId: qualifiedId,
870
+ contributionId: declared.id,
871
+ nodePath: opts.node.path,
872
+ slot: declared.slot,
873
+ errors: result.errors
874
+ });
780
875
  emitExtensionError(opts.emitter, qualifiedId, opts.node.path, {
781
876
  phase: "emitContribution",
782
- contributionId,
877
+ contributionId: declared.id,
783
878
  slot: declared.slot,
784
879
  reason: result.errors,
785
- message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
786
- extractorId: qualifiedId,
787
- contributionId,
788
- nodePath: opts.node.path,
789
- slot: declared.slot,
790
- errors: result.errors
791
- })
880
+ message
881
+ });
882
+ contributionErrors.push({
883
+ pluginId: extractor.pluginId,
884
+ extensionId: extractor.id,
885
+ nodePath: opts.node.path,
886
+ reason: result.errors,
887
+ message,
888
+ contributionId: declared.id,
889
+ slot: declared.slot,
890
+ emittedAt: Date.now()
792
891
  });
793
892
  return;
794
893
  }
@@ -796,7 +895,7 @@ async function runExtractorsForNode(opts) {
796
895
  pluginId: extractor.pluginId,
797
896
  extensionId: extractor.id,
798
897
  nodePath: opts.node.path,
799
- contributionId,
898
+ contributionId: declared.id,
800
899
  slot: declared.slot,
801
900
  payload,
802
901
  emittedAt: Date.now()
@@ -834,11 +933,12 @@ async function runExtractorsForNode(opts) {
834
933
  externalLinks,
835
934
  enrichments: Array.from(enrichmentBuffer.values()),
836
935
  contributions,
936
+ contributionErrors,
837
937
  signals,
838
938
  virtualNodes
839
939
  };
840
940
  }
841
- function readDeclaredContributions(extension) {
941
+ function readDeclaredContributionRefs(extension) {
842
942
  const out = /* @__PURE__ */ new Map();
843
943
  const raw = extension.ui;
844
944
  if (typeof raw !== "object" || raw === null) return out;
@@ -846,7 +946,7 @@ function readDeclaredContributions(extension) {
846
946
  if (typeof value !== "object" || value === null) continue;
847
947
  const slot = value.slot;
848
948
  if (typeof slot !== "string") continue;
849
- out.set(id, { slot });
949
+ out.set(value, { id, slot });
850
950
  }
851
951
  return out;
852
952
  }
@@ -1094,6 +1194,7 @@ function isExternalUrlLink(link) {
1094
1194
  async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, registeredActionIds, emitter, hookDispatcher, reservedNodePaths, signals, seedIssues = []) {
1095
1195
  const issues = [...seedIssues];
1096
1196
  const contributions = [];
1197
+ const contributionErrors = [];
1097
1198
  const validators = loadSchemaValidators();
1098
1199
  void registeredActionIds;
1099
1200
  const analyzerOrphans = orphanSidecars.map((o) => ({
@@ -1103,36 +1204,54 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
1103
1204
  const scheduled = orderAnalyzersByPhase(analyzers);
1104
1205
  for (const analyzer of scheduled) {
1105
1206
  const qualifiedId = qualifiedExtensionId(analyzer.pluginId, analyzer.id);
1106
- const declaredContributions = readDeclaredContributions(analyzer);
1107
- const emitContribution = (nodePath, contributionId, payload) => {
1108
- const declared = declaredContributions.get(contributionId);
1207
+ const declaredContributions = readDeclaredContributionRefs(analyzer);
1208
+ const emitContribution = (nodePath, ref, payload) => {
1209
+ const declared = typeof ref === "object" && ref !== null ? declaredContributions.get(ref) : void 0;
1109
1210
  if (!declared) {
1211
+ const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUndeclaredRef, {
1212
+ extractorId: qualifiedId,
1213
+ nodePath
1214
+ });
1110
1215
  emitExtensionError(emitter, qualifiedId, nodePath, {
1111
1216
  phase: "emitContribution",
1112
- contributionId,
1113
- reason: "unknown-contribution-id",
1114
- message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUnknownId, {
1115
- extractorId: qualifiedId,
1116
- contributionId,
1117
- nodePath
1118
- })
1217
+ reason: "undeclared-contribution-ref",
1218
+ message
1219
+ });
1220
+ contributionErrors.push({
1221
+ pluginId: analyzer.pluginId,
1222
+ extensionId: analyzer.id,
1223
+ nodePath,
1224
+ reason: "undeclared-contribution-ref",
1225
+ message,
1226
+ emittedAt: Date.now()
1119
1227
  });
1120
1228
  return;
1121
1229
  }
1122
1230
  const result = validators.validateContributionPayload(declared.slot, payload);
1123
1231
  if (!result.ok) {
1232
+ const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
1233
+ extractorId: qualifiedId,
1234
+ contributionId: declared.id,
1235
+ nodePath,
1236
+ slot: declared.slot,
1237
+ errors: result.errors
1238
+ });
1124
1239
  emitExtensionError(emitter, qualifiedId, nodePath, {
1125
1240
  phase: "emitContribution",
1126
- contributionId,
1241
+ contributionId: declared.id,
1127
1242
  slot: declared.slot,
1128
1243
  reason: result.errors,
1129
- message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
1130
- extractorId: qualifiedId,
1131
- contributionId,
1132
- nodePath,
1133
- slot: declared.slot,
1134
- errors: result.errors
1135
- })
1244
+ message
1245
+ });
1246
+ contributionErrors.push({
1247
+ pluginId: analyzer.pluginId,
1248
+ extensionId: analyzer.id,
1249
+ nodePath,
1250
+ reason: result.errors,
1251
+ message,
1252
+ contributionId: declared.id,
1253
+ slot: declared.slot,
1254
+ emittedAt: Date.now()
1136
1255
  });
1137
1256
  return;
1138
1257
  }
@@ -1140,7 +1259,7 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
1140
1259
  pluginId: analyzer.pluginId,
1141
1260
  extensionId: analyzer.id,
1142
1261
  nodePath,
1143
- contributionId,
1262
+ contributionId: declared.id,
1144
1263
  slot: declared.slot,
1145
1264
  payload,
1146
1265
  emittedAt: Date.now()
@@ -1173,7 +1292,7 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
1173
1292
  emitter.emit(evt);
1174
1293
  await hookDispatcher.dispatch("analyzer.completed", evt);
1175
1294
  }
1176
- return { issues, contributions };
1295
+ return { issues, contributions, contributionErrors };
1177
1296
  }
1178
1297
  function orderAnalyzersByPhase(analyzers) {
1179
1298
  return analyzers.slice().sort((a, b) => phaseRank(a) - phaseRank(b));
@@ -2423,7 +2542,7 @@ function resolveSidecarOverlay(relativePath, nodePathForIssue, roots, liveBodyHa
2423
2542
  for (const parseIssue of result.issues) {
2424
2543
  issues.push({
2425
2544
  analyzerId: "invalid-sidecar",
2426
- severity: "warn",
2545
+ severity: "error",
2427
2546
  nodeIds: [nodePathForIssue],
2428
2547
  message: parseIssue.message,
2429
2548
  data: { sidecarPath: relativePathFromRoots(mdAbs, roots) }
@@ -2589,6 +2708,7 @@ async function walkAndExtract(opts) {
2589
2708
  enrichments: [...accum.enrichmentBuffer.values()],
2590
2709
  extractorRuns: accum.extractorRuns,
2591
2710
  contributions: accum.contributionsBuffer,
2711
+ contributionErrors: accum.contributionErrorsBuffer,
2592
2712
  freshlyRunTuples: accum.freshlyRunTuples,
2593
2713
  orphanSidecars,
2594
2714
  sidecarRoots: accum.sidecarRoots,
@@ -2605,6 +2725,7 @@ function createWalkAccumulators() {
2605
2725
  frontmatterIssues: [],
2606
2726
  enrichmentBuffer: /* @__PURE__ */ new Map(),
2607
2727
  contributionsBuffer: [],
2728
+ contributionErrorsBuffer: [],
2608
2729
  freshlyRunTuples: /* @__PURE__ */ new Set(),
2609
2730
  extractorRuns: [],
2610
2731
  sidecarRoots: /* @__PURE__ */ new Map()
@@ -2755,7 +2876,11 @@ function mergeExtractResult(extractResult, accum) {
2755
2876
  accum.enrichmentBuffer.set(`${enr.nodePath}\0${enr.extractorId}`, enr);
2756
2877
  }
2757
2878
  for (const c of extractResult.contributions) accum.contributionsBuffer.push(c);
2758
- for (const vn of extractResult.virtualNodes) {
2879
+ for (const e of extractResult.contributionErrors) accum.contributionErrorsBuffer.push(e);
2880
+ mergeVirtualNodes(extractResult.virtualNodes, accum);
2881
+ }
2882
+ function mergeVirtualNodes(virtualNodes, accum) {
2883
+ for (const vn of virtualNodes) {
2759
2884
  if (accum.nodes.some((n) => n.path === vn.path)) continue;
2760
2885
  accum.nodes.push(vn);
2761
2886
  }
@@ -3031,6 +3156,7 @@ async function dispatchExtractorCompleted(extractors, emitter, hookDispatcher) {
3031
3156
  }
3032
3157
  function mergeAnalyzerEmissions(walked, analyzerResult, analyzers) {
3033
3158
  for (const c of analyzerResult.contributions) walked.contributions.push(c);
3159
+ for (const e of analyzerResult.contributionErrors) walked.contributionErrors.push(e);
3034
3160
  for (const analyzer of analyzers ?? []) {
3035
3161
  if (analyzer.ui === void 0) continue;
3036
3162
  for (const node of walked.nodes) {
@@ -3076,6 +3202,7 @@ function buildScanReturn(walked, issues, renameOps, stats, options, setup) {
3076
3202
  extractorRuns: walked.extractorRuns,
3077
3203
  enrichments: walked.enrichments,
3078
3204
  contributions: walked.contributions,
3205
+ contributionErrors: walked.contributionErrors,
3079
3206
  freshlyRunTuples: walked.freshlyRunTuples
3080
3207
  };
3081
3208
  }
@@ -3505,4 +3632,4 @@ export {
3505
3632
  runScanWithRenames
3506
3633
  };
3507
3634
  //# sourceMappingURL=index.js.map
3508
- //# debugId=77a22e54-0917-5b5e-a29d-af0ad20bed19
3635
+ //# debugId=5a40a4c9-3996-540c-b687-874d383944d0