@skill-map/cli 0.20.0 → 0.21.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 (37) hide show
  1. package/dist/cli/tutorial/sm-tutorial.md +93 -14
  2. package/dist/cli.js +1332 -339
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.js +300 -238
  5. package/dist/index.js.map +1 -1
  6. package/dist/kernel/index.d.ts +91 -11
  7. package/dist/kernel/index.js +300 -238
  8. package/dist/kernel/index.js.map +1 -1
  9. package/dist/migrations/001_initial.sql +13 -0
  10. package/dist/ui/chunk-25AWRVIC.js +965 -0
  11. package/dist/ui/chunk-6FTVUS57.js +123 -0
  12. package/dist/ui/{chunk-LQTUSDHD.js → chunk-GXRWH2VL.js} +1 -1
  13. package/dist/ui/chunk-MF2M6GYF.js +1 -0
  14. package/dist/ui/{chunk-2W62S3FU.js → chunk-MPMBTIUR.js} +2 -2
  15. package/dist/ui/chunk-N366HMME.js +1 -0
  16. package/dist/ui/{chunk-QICH7GU2.js → chunk-OPPQMCMQ.js} +1 -1
  17. package/dist/ui/chunk-V3SZQETX.js +61 -0
  18. package/dist/ui/{chunk-HJSRMZTK.js → chunk-VVOEPDQD.js} +1 -1
  19. package/dist/ui/{chunk-DLT5AP43.js → chunk-W2EFGI3J.js} +1 -1
  20. package/dist/ui/chunk-W62WVNU4.js +251 -0
  21. package/dist/ui/index.html +2 -10
  22. package/dist/ui/main-NIYE2VFS.js +2 -0
  23. package/dist/ui/media/fa-brands-400-AHOAZHCU.woff2 +0 -0
  24. package/dist/ui/media/fa-regular-400-VRZYIBIZ.woff2 +0 -0
  25. package/dist/ui/media/fa-solid-900-MDEYK55F.woff2 +0 -0
  26. package/dist/ui/media/fa-v4compatibility-ETEVP6IB.woff2 +0 -0
  27. package/dist/ui/styles-M2FETVAG.css +1 -0
  28. package/migrations/001_initial.sql +13 -0
  29. package/package.json +2 -2
  30. package/dist/ui/chunk-C7QWBAYP.js +0 -247
  31. package/dist/ui/chunk-HOBQ4G4O.js +0 -125
  32. package/dist/ui/chunk-IBUV6OG2.js +0 -1
  33. package/dist/ui/chunk-UJRROL5X.js +0 -1
  34. package/dist/ui/chunk-VLNLJAUB.js +0 -61
  35. package/dist/ui/chunk-W3JLG7BI.js +0 -965
  36. package/dist/ui/main-QHE47BCM.js +0 -1
  37. package/dist/ui/styles-VJ5Q6D2X.css +0 -1
package/dist/index.js CHANGED
@@ -94,8 +94,8 @@ var Registry = class {
94
94
 
95
95
  // kernel/orchestrator.ts
96
96
  import { createHash } from "crypto";
97
- import { existsSync as existsSync6, statSync as statSync2 } from "fs";
98
- import { isAbsolute as isAbsolute2, resolve as resolvePath } from "path";
97
+ import { existsSync as existsSync8, statSync as statSync2 } from "fs";
98
+ import { isAbsolute as isAbsolute3, resolve as resolvePath } from "path";
99
99
  import { Tiktoken } from "js-tiktoken/lite";
100
100
  import cl100k_base from "js-tiktoken/ranks/cl100k_base";
101
101
  import yaml4 from "js-yaml";
@@ -103,7 +103,7 @@ import yaml4 from "js-yaml";
103
103
  // package.json
104
104
  var package_default = {
105
105
  name: "@skill-map/cli",
106
- version: "0.20.0",
106
+ version: "0.21.0",
107
107
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
108
108
  license: "MIT",
109
109
  type: "module",
@@ -169,7 +169,7 @@ var package_default = {
169
169
  },
170
170
  dependencies: {
171
171
  "@hono/node-server": "2.0.1",
172
- "@skill-map/spec": "0.20.0",
172
+ "@skill-map/spec": "0.21.0",
173
173
  ajv: "8.18.0",
174
174
  "ajv-formats": "3.0.1",
175
175
  chokidar: "5.0.0",
@@ -368,175 +368,23 @@ function safeIsFile(path) {
368
368
  }
369
369
 
370
370
  // kernel/sidecar/store.ts
371
- import { existsSync as existsSync3, readFileSync as readFileSync2, renameSync, writeFileSync, unlinkSync } from "fs";
372
- import { dirname as dirname2, resolve as resolve2 } from "path";
373
- import { createRequire as createRequire2 } from "module";
374
- import { Ajv2020 as Ajv20202 } from "ajv/dist/2020.js";
375
- import yaml2 from "js-yaml";
376
-
377
- // kernel/adapters/in-memory-progress.ts
378
- var InMemoryProgressEmitter = class {
379
- #listeners = /* @__PURE__ */ new Set();
380
- emit(event) {
381
- for (const listener of this.#listeners) listener(event);
382
- }
383
- subscribe(listener) {
384
- this.#listeners.add(listener);
385
- return () => {
386
- this.#listeners.delete(listener);
387
- };
388
- }
389
- };
390
-
391
- // kernel/adapters/silent-logger.ts
392
- var SilentLogger = class {
393
- trace() {
394
- }
395
- debug() {
396
- }
397
- info() {
398
- }
399
- warn() {
400
- }
401
- error() {
402
- }
403
- };
404
-
405
- // kernel/util/logger.ts
406
- var active = new SilentLogger();
407
- var log = {
408
- trace: (message, context) => active.trace(message, context),
409
- debug: (message, context) => active.debug(message, context),
410
- info: (message, context) => active.info(message, context),
411
- warn: (message, context) => active.warn(message, context),
412
- error: (message, context) => active.error(message, context)
413
- };
414
- function configureLogger(impl) {
415
- active = impl;
416
- }
417
- function resetLogger() {
418
- active = new SilentLogger();
419
- }
420
- function getActiveLogger() {
421
- return active;
422
- }
423
-
424
- // kernel/adapters/plugin-loader.ts
371
+ import { existsSync as existsSync5, readFileSync as readFileSync5, renameSync as renameSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
372
+ import { dirname as dirname4, resolve as resolve5 } from "path";
425
373
  import { createRequire as createRequire3 } from "module";
426
- import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
427
- import { isAbsolute, join as join2, relative as relative2, resolve as resolve3 } from "path";
428
- import { pathToFileURL } from "url";
429
374
  import { Ajv2020 as Ajv20203 } from "ajv/dist/2020.js";
430
- import semver from "semver";
431
-
432
- // kernel/i18n/plugin-store.texts.ts
433
- var PLUGIN_STORE_TEXTS = {
434
- kvValidationFailed: "plugin '{{pluginId}}' ctx.store.set('{{key}}', value): value violates declared schema ({{schemaPath}}) \u2014 {{errors}}",
435
- dedicatedValidationFailed: "plugin '{{pluginId}}' ctx.store.write('{{table}}', row): row violates declared schema ({{schemaPath}}) \u2014 {{errors}}"
436
- };
437
-
438
- // kernel/adapters/plugin-store.ts
439
- var KV_SCHEMA_KEY = "__kv__";
440
- function makeKvStoreWrapper(opts) {
441
- const { pluginId, schema, persist } = opts;
442
- return {
443
- async set(key, value) {
444
- if (schema) {
445
- if (!schema.validate(value)) {
446
- throw new Error(
447
- tx(PLUGIN_STORE_TEXTS.kvValidationFailed, {
448
- pluginId,
449
- schemaPath: schema.schemaPath,
450
- key,
451
- errors: formatAjvErrors(schema.validate.errors ?? null)
452
- })
453
- );
454
- }
455
- }
456
- await persist(key, value);
457
- }
458
- };
459
- }
460
- function makeDedicatedStoreWrapper(opts) {
461
- const { pluginId, schemas, persist } = opts;
462
- return {
463
- async write(table, row) {
464
- const schema = schemas?.[table];
465
- if (schema) {
466
- if (!schema.validate(row)) {
467
- throw new Error(
468
- tx(PLUGIN_STORE_TEXTS.dedicatedValidationFailed, {
469
- pluginId,
470
- table,
471
- schemaPath: schema.schemaPath,
472
- errors: formatAjvErrors(schema.validate.errors ?? null)
473
- })
474
- );
475
- }
476
- }
477
- await persist(table, row);
478
- }
479
- };
480
- }
481
- function makePluginStore(opts) {
482
- const manifest = opts.plugin.manifest;
483
- if (!manifest?.storage) return void 0;
484
- const storageSchemas = opts.plugin.storageSchemas;
485
- if (manifest.storage.mode === "kv") {
486
- if (!opts.persistKv) return void 0;
487
- const schema = storageSchemas?.[KV_SCHEMA_KEY];
488
- return makeKvStoreWrapper({
489
- pluginId: manifest.id,
490
- schema,
491
- persist: opts.persistKv
492
- });
493
- }
494
- if (manifest.storage.mode === "dedicated") {
495
- if (!opts.persistDedicated) return void 0;
496
- return makeDedicatedStoreWrapper({
497
- pluginId: manifest.id,
498
- schemas: storageSchemas,
499
- persist: opts.persistDedicated
500
- });
501
- }
502
- return void 0;
503
- }
504
- function formatAjvErrors(errors) {
505
- if (!errors || errors.length === 0) return "(no AJV details)";
506
- return errors.map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
507
- }
375
+ import yaml2 from "js-yaml";
508
376
 
509
- // kernel/extensions/hook.ts
510
- var HOOK_TRIGGERS = Object.freeze([
511
- "boot",
512
- "scan.started",
513
- "scan.completed",
514
- "extractor.completed",
515
- "analyzer.completed",
516
- "action.completed",
517
- "job.spawning",
518
- "job.completed",
519
- "job.failed",
520
- "shutdown"
521
- ]);
377
+ // core/config/helper.ts
378
+ import { isAbsolute, resolve as resolve4 } from "path";
522
379
 
523
- // kernel/adapters/plugin-loader.ts
524
- var KNOWN_KINDS = /* @__PURE__ */ new Set(["provider", "extractor", "analyzer", "action", "formatter", "hook"]);
525
- var KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(" / ");
526
- var HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(", ");
527
- function installedSpecVersion() {
528
- const require2 = createRequire3(import.meta.url);
529
- const indexPath = require2.resolve("@skill-map/spec/index.json");
530
- const pkgPath = resolve3(indexPath, "..", "package.json");
531
- const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
532
- return pkg.version;
533
- }
380
+ // kernel/config/loader.ts
381
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
534
382
 
535
383
  // kernel/adapters/schema-validators.ts
536
- import { readFileSync as readFileSync4 } from "fs";
537
- import { dirname as dirname3, resolve as resolve4 } from "path";
538
- import { createRequire as createRequire4 } from "module";
539
- import { Ajv2020 as Ajv20204 } from "ajv/dist/2020.js";
384
+ import { readFileSync as readFileSync2 } from "fs";
385
+ import { dirname as dirname2, resolve as resolve2 } from "path";
386
+ import { createRequire as createRequire2 } from "module";
387
+ import { Ajv2020 as Ajv20202 } from "ajv/dist/2020.js";
540
388
  var SCHEMA_FILES = {
541
389
  node: "schemas/node.schema.json",
542
390
  link: "schemas/link.schema.json",
@@ -572,22 +420,22 @@ function loadSchemaValidators() {
572
420
  }
573
421
  function buildSchemaValidators() {
574
422
  const specRoot = resolveSpecRoot2();
575
- const ajv = new Ajv20204({
423
+ const ajv = new Ajv20202({
576
424
  strict: false,
577
425
  allErrors: true,
578
426
  allowUnionTypes: true
579
427
  });
580
428
  applyAjvFormats(ajv);
581
429
  for (const rel of SUPPORTING_SCHEMAS) {
582
- const file = resolve4(specRoot, rel);
430
+ const file = resolve2(specRoot, rel);
583
431
  if (!existsSyncSafe(file)) continue;
584
- const schema = JSON.parse(readFileSync4(file, "utf8"));
432
+ const schema = JSON.parse(readFileSync2(file, "utf8"));
585
433
  ajv.addSchema(schema);
586
434
  }
587
435
  const validators = /* @__PURE__ */ new Map();
588
436
  for (const [name, rel] of Object.entries(SCHEMA_FILES)) {
589
- const file = resolve4(specRoot, rel);
590
- const schema = JSON.parse(readFileSync4(file, "utf8"));
437
+ const file = resolve2(specRoot, rel);
438
+ const schema = JSON.parse(readFileSync2(file, "utf8"));
591
439
  const byId = typeof schema.$id === "string" ? ajv.getSchema(schema.$id) : void 0;
592
440
  validators.set(name, byId ?? ajv.compile(schema));
593
441
  }
@@ -607,7 +455,7 @@ function buildSchemaValidators() {
607
455
  const KNOWN_SLOTS = /* @__PURE__ */ new Set([
608
456
  "card.title.right",
609
457
  "card.subtitle.left",
610
- "card.footer.left.counter",
458
+ "card.footer.left",
611
459
  "card.footer.right",
612
460
  "graph.node.alert",
613
461
  "inspector.header.badge.counter",
@@ -618,7 +466,7 @@ function buildSchemaValidators() {
618
466
  "inspector.body.panel.key-values",
619
467
  "inspector.body.panel.link-list",
620
468
  "inspector.body.panel.markdown",
621
- "topbar.actions.indicator"
469
+ "topbar.nav.start"
622
470
  ]);
623
471
  function getContributionValidator(slot) {
624
472
  if (!KNOWN_SLOTS.has(slot)) return null;
@@ -668,14 +516,14 @@ function buildSchemaValidators() {
668
516
  }
669
517
  function buildProviderFrontmatterValidator(providers) {
670
518
  const specRoot = resolveSpecRoot2();
671
- const ajv = new Ajv20204({
519
+ const ajv = new Ajv20202({
672
520
  strict: false,
673
521
  allErrors: true,
674
522
  allowUnionTypes: true
675
523
  });
676
524
  applyAjvFormats(ajv);
677
- const baseFile = resolve4(specRoot, "schemas/frontmatter/base.schema.json");
678
- const baseSchema = JSON.parse(readFileSync4(baseFile, "utf8"));
525
+ const baseFile = resolve2(specRoot, "schemas/frontmatter/base.schema.json");
526
+ const baseSchema = JSON.parse(readFileSync2(baseFile, "utf8"));
679
527
  ajv.addSchema(baseSchema);
680
528
  registerProviderAuxiliarySchemas(ajv, providers);
681
529
  const compiled = /* @__PURE__ */ new Map();
@@ -713,10 +561,10 @@ function registerProviderAuxiliarySchemas(ajv, providers) {
713
561
  }
714
562
  }
715
563
  function resolveSpecRoot2() {
716
- const require2 = createRequire4(import.meta.url);
564
+ const require2 = createRequire2(import.meta.url);
717
565
  try {
718
566
  const indexPath = require2.resolve("@skill-map/spec/index.json");
719
- return dirname3(indexPath);
567
+ return dirname2(indexPath);
720
568
  } catch {
721
569
  throw new Error(
722
570
  "@skill-map/spec not resolvable \u2014 ensure the workspace is linked or the package is installed."
@@ -725,13 +573,201 @@ function resolveSpecRoot2() {
725
573
  }
726
574
  function existsSyncSafe(path) {
727
575
  try {
728
- readFileSync4(path, "utf8");
576
+ readFileSync2(path, "utf8");
729
577
  return true;
730
578
  } catch {
731
579
  return false;
732
580
  }
733
581
  }
734
582
 
583
+ // kernel/util/format-error.ts
584
+ function formatErrorMessage(err) {
585
+ return err instanceof Error ? err.message : String(err);
586
+ }
587
+
588
+ // kernel/util/skill-map-paths.ts
589
+ import { join as join3 } from "path";
590
+
591
+ // core/paths/db-path.ts
592
+ import { join as join2, resolve as resolve3 } from "path";
593
+ var SKILL_MAP_DIR = ".skill-map";
594
+ var DB_FILENAME = "skill-map.db";
595
+ var LOCAL_SETTINGS_FILENAME = "settings.local.json";
596
+ var DEFAULT_DB_REL = `${SKILL_MAP_DIR}/${DB_FILENAME}`;
597
+ var GITIGNORE_ENTRIES = [
598
+ `${SKILL_MAP_DIR}/${LOCAL_SETTINGS_FILENAME}`,
599
+ `${SKILL_MAP_DIR}/${DB_FILENAME}`
600
+ ];
601
+
602
+ // core/config/atomic-write.ts
603
+ import {
604
+ existsSync as existsSync4,
605
+ mkdirSync,
606
+ readFileSync as readFileSync4,
607
+ renameSync,
608
+ unlinkSync,
609
+ writeFileSync
610
+ } from "fs";
611
+ import { dirname as dirname3 } from "path";
612
+
613
+ // kernel/adapters/in-memory-progress.ts
614
+ var InMemoryProgressEmitter = class {
615
+ #listeners = /* @__PURE__ */ new Set();
616
+ emit(event) {
617
+ for (const listener of this.#listeners) listener(event);
618
+ }
619
+ subscribe(listener) {
620
+ this.#listeners.add(listener);
621
+ return () => {
622
+ this.#listeners.delete(listener);
623
+ };
624
+ }
625
+ };
626
+
627
+ // kernel/adapters/silent-logger.ts
628
+ var SilentLogger = class {
629
+ trace() {
630
+ }
631
+ debug() {
632
+ }
633
+ info() {
634
+ }
635
+ warn() {
636
+ }
637
+ error() {
638
+ }
639
+ };
640
+
641
+ // kernel/util/logger.ts
642
+ var active = new SilentLogger();
643
+ var log = {
644
+ trace: (message, context) => active.trace(message, context),
645
+ debug: (message, context) => active.debug(message, context),
646
+ info: (message, context) => active.info(message, context),
647
+ warn: (message, context) => active.warn(message, context),
648
+ error: (message, context) => active.error(message, context)
649
+ };
650
+ function configureLogger(impl) {
651
+ active = impl;
652
+ }
653
+ function resetLogger() {
654
+ active = new SilentLogger();
655
+ }
656
+ function getActiveLogger() {
657
+ return active;
658
+ }
659
+
660
+ // kernel/adapters/plugin-loader.ts
661
+ import { createRequire as createRequire4 } from "module";
662
+ import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync2 } from "fs";
663
+ import { isAbsolute as isAbsolute2, join as join4, relative as relative2, resolve as resolve6 } from "path";
664
+ import { pathToFileURL } from "url";
665
+ import { Ajv2020 as Ajv20204 } from "ajv/dist/2020.js";
666
+ import semver from "semver";
667
+
668
+ // kernel/i18n/plugin-store.texts.ts
669
+ var PLUGIN_STORE_TEXTS = {
670
+ kvValidationFailed: "plugin '{{pluginId}}' ctx.store.set('{{key}}', value): value violates declared schema ({{schemaPath}}) \u2014 {{errors}}",
671
+ dedicatedValidationFailed: "plugin '{{pluginId}}' ctx.store.write('{{table}}', row): row violates declared schema ({{schemaPath}}) \u2014 {{errors}}"
672
+ };
673
+
674
+ // kernel/adapters/plugin-store.ts
675
+ var KV_SCHEMA_KEY = "__kv__";
676
+ function makeKvStoreWrapper(opts) {
677
+ const { pluginId, schema, persist } = opts;
678
+ return {
679
+ async set(key, value) {
680
+ if (schema) {
681
+ if (!schema.validate(value)) {
682
+ throw new Error(
683
+ tx(PLUGIN_STORE_TEXTS.kvValidationFailed, {
684
+ pluginId,
685
+ schemaPath: schema.schemaPath,
686
+ key,
687
+ errors: formatAjvErrors(schema.validate.errors ?? null)
688
+ })
689
+ );
690
+ }
691
+ }
692
+ await persist(key, value);
693
+ }
694
+ };
695
+ }
696
+ function makeDedicatedStoreWrapper(opts) {
697
+ const { pluginId, schemas, persist } = opts;
698
+ return {
699
+ async write(table, row) {
700
+ const schema = schemas?.[table];
701
+ if (schema) {
702
+ if (!schema.validate(row)) {
703
+ throw new Error(
704
+ tx(PLUGIN_STORE_TEXTS.dedicatedValidationFailed, {
705
+ pluginId,
706
+ table,
707
+ schemaPath: schema.schemaPath,
708
+ errors: formatAjvErrors(schema.validate.errors ?? null)
709
+ })
710
+ );
711
+ }
712
+ }
713
+ await persist(table, row);
714
+ }
715
+ };
716
+ }
717
+ function makePluginStore(opts) {
718
+ const manifest = opts.plugin.manifest;
719
+ if (!manifest?.storage) return void 0;
720
+ const storageSchemas = opts.plugin.storageSchemas;
721
+ if (manifest.storage.mode === "kv") {
722
+ if (!opts.persistKv) return void 0;
723
+ const schema = storageSchemas?.[KV_SCHEMA_KEY];
724
+ return makeKvStoreWrapper({
725
+ pluginId: manifest.id,
726
+ schema,
727
+ persist: opts.persistKv
728
+ });
729
+ }
730
+ if (manifest.storage.mode === "dedicated") {
731
+ if (!opts.persistDedicated) return void 0;
732
+ return makeDedicatedStoreWrapper({
733
+ pluginId: manifest.id,
734
+ schemas: storageSchemas,
735
+ persist: opts.persistDedicated
736
+ });
737
+ }
738
+ return void 0;
739
+ }
740
+ function formatAjvErrors(errors) {
741
+ if (!errors || errors.length === 0) return "(no AJV details)";
742
+ return errors.map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
743
+ }
744
+
745
+ // kernel/extensions/hook.ts
746
+ var HOOK_TRIGGERS = Object.freeze([
747
+ "boot",
748
+ "scan.started",
749
+ "scan.completed",
750
+ "extractor.completed",
751
+ "analyzer.completed",
752
+ "action.completed",
753
+ "job.spawning",
754
+ "job.completed",
755
+ "job.failed",
756
+ "shutdown"
757
+ ]);
758
+
759
+ // kernel/adapters/plugin-loader.ts
760
+ var KNOWN_KINDS = /* @__PURE__ */ new Set(["provider", "extractor", "analyzer", "action", "formatter", "hook"]);
761
+ var KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(" / ");
762
+ var HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(", ");
763
+ function installedSpecVersion() {
764
+ const require2 = createRequire4(import.meta.url);
765
+ const indexPath = require2.resolve("@skill-map/spec/index.json");
766
+ const pkgPath = resolve6(indexPath, "..", "package.json");
767
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
768
+ return pkg.version;
769
+ }
770
+
735
771
  // kernel/i18n/orchestrator.texts.ts
736
772
  var ORCHESTRATOR_TEXTS = {
737
773
  frontmatterInvalid: "Frontmatter for {{path}} ({{kind}}) failed schema validation: {{errors}}",
@@ -746,18 +782,13 @@ var ORCHESTRATOR_TEXTS = {
746
782
  runScanRootMissing: "runScan: root path '{{root}}' does not exist or is not a directory"
747
783
  };
748
784
 
749
- // kernel/util/format-error.ts
750
- function formatErrorMessage(err) {
751
- return err instanceof Error ? err.message : String(err);
752
- }
753
-
754
785
  // kernel/scan/walk-content.ts
755
786
  import { readFile, readdir, stat } from "fs/promises";
756
- import { join as join3, relative as relative3, sep as sep2 } from "path";
787
+ import { join as join5, relative as relative3, sep as sep2 } from "path";
757
788
 
758
789
  // kernel/scan/ignore.ts
759
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
760
- import { dirname as dirname4, resolve as resolve5 } from "path";
790
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
791
+ import { dirname as dirname5, resolve as resolve7 } from "path";
761
792
  import { fileURLToPath } from "url";
762
793
  import ignoreFactory from "ignore";
763
794
  function buildIgnoreFilter(opts = {}) {
@@ -789,18 +820,18 @@ function loadDefaultsText() {
789
820
  return cachedDefaults;
790
821
  }
791
822
  function readDefaultsFromDisk() {
792
- const here = dirname4(fileURLToPath(import.meta.url));
823
+ const here = dirname5(fileURLToPath(import.meta.url));
793
824
  const candidates = [
794
- resolve5(here, "../../config/defaults/skillmapignore"),
825
+ resolve7(here, "../../config/defaults/skillmapignore"),
795
826
  // src/kernel/scan/ → src/config/defaults/
796
- resolve5(here, "../config/defaults/skillmapignore"),
827
+ resolve7(here, "../config/defaults/skillmapignore"),
797
828
  // dist/cli.js → dist/config/defaults/ (siblings)
798
- resolve5(here, "config/defaults/skillmapignore")
829
+ resolve7(here, "config/defaults/skillmapignore")
799
830
  ];
800
831
  for (const candidate of candidates) {
801
- if (existsSync5(candidate)) {
832
+ if (existsSync7(candidate)) {
802
833
  try {
803
- return readFileSync5(candidate, "utf8");
834
+ return readFileSync7(candidate, "utf8");
804
835
  } catch {
805
836
  }
806
837
  }
@@ -892,7 +923,7 @@ async function* walkRoot(root, current, filter, extensions) {
892
923
  }
893
924
  for (const entry of entries) {
894
925
  const name = entry.name;
895
- const full = join3(current, name);
926
+ const full = join5(current, name);
896
927
  const rel = relative3(root, full).split(sep2).join("/");
897
928
  if (filter.ignores(rel)) continue;
898
929
  if (entry.isSymbolicLink()) continue;
@@ -1095,7 +1126,7 @@ async function runScanInternal(_kernel, options) {
1095
1126
  for (const analyzer of exts.analyzers ?? []) {
1096
1127
  if (analyzer.viewContributions === void 0) continue;
1097
1128
  for (const node of walked.nodes) {
1098
- walked.freshlyRunTuples.add(`${analyzer.pluginId}/${analyzer.id}/${node.path}`);
1129
+ walked.freshlyRunTuples.add(`${analyzer.pluginId}\0${analyzer.id}\0${node.path}`);
1099
1130
  }
1100
1131
  }
1101
1132
  for (const issue of walked.frontmatterIssues) issues.push(issue);
@@ -1141,7 +1172,7 @@ function validateRoots(roots) {
1141
1172
  throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
1142
1173
  }
1143
1174
  for (const root of roots) {
1144
- if (!existsSync6(root) || !statSync2(root).isDirectory()) {
1175
+ if (!existsSync8(root) || !statSync2(root).isDirectory()) {
1145
1176
  throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
1146
1177
  }
1147
1178
  }
@@ -1311,8 +1342,10 @@ function computeCacheDecision(opts) {
1311
1342
  const priorRunsForNode = opts.priorExtractorRuns.get(opts.nodePath) ?? /* @__PURE__ */ new Map();
1312
1343
  for (const ex of applicableExtractors) {
1313
1344
  const qualified = qualifiedExtensionId(ex.pluginId, ex.id);
1314
- const priorBody = priorRunsForNode.get(qualified);
1315
- if (opts.nodeHashCacheEligible && priorBody === opts.bodyHash) {
1345
+ const prior = priorRunsForNode.get(qualified);
1346
+ const bodyMatch = prior !== void 0 && prior.bodyHash === opts.bodyHash;
1347
+ const sidecarOk = prior !== void 0 && prior.sidecarAnnotationsHash === opts.sidecarAnnotationsHash;
1348
+ if (opts.nodeHashCacheEligible && bodyMatch && sidecarOk) {
1316
1349
  cachedQualifiedIds.add(qualified);
1317
1350
  } else {
1318
1351
  missingExtractors.push(ex);
@@ -1357,7 +1390,8 @@ function reusePriorNode(opts) {
1357
1390
  nodePath: opts.priorNode.path,
1358
1391
  extractorId: qualified,
1359
1392
  bodyHashAtRun: opts.bodyHash,
1360
- ranAt
1393
+ ranAt,
1394
+ sidecarAnnotationsHashAtRun: opts.sidecarAnnotationsHash
1361
1395
  });
1362
1396
  }
1363
1397
  return { ...base, extractorRuns };
@@ -1443,11 +1477,22 @@ async function walkAndExtract(opts) {
1443
1477
  }
1444
1478
  claimedPaths.add(raw.path);
1445
1479
  index += 1;
1480
+ const sidecarResolution = resolveSidecarOverlay(
1481
+ raw.path,
1482
+ raw.path,
1483
+ roots,
1484
+ bodyHash,
1485
+ frontmatterHash
1486
+ );
1487
+ const sidecarAnnotationsHash = sha256(
1488
+ canonicalSidecarAnnotations(sidecarResolution.overlay.annotations)
1489
+ );
1446
1490
  const cacheDecision = computeCacheDecision({
1447
1491
  extractors,
1448
1492
  kind,
1449
1493
  nodePath: raw.path,
1450
1494
  bodyHash,
1495
+ sidecarAnnotationsHash,
1451
1496
  nodeHashCacheEligible,
1452
1497
  priorExtractorRuns
1453
1498
  });
@@ -1458,10 +1503,20 @@ async function walkAndExtract(opts) {
1458
1503
  missingExtractors,
1459
1504
  fullCacheHit
1460
1505
  } = cacheDecision;
1506
+ const attachSidecar = (node2) => {
1507
+ node2.sidecar = sidecarResolution.overlay;
1508
+ if (sidecarResolution.parsedRoot !== null) {
1509
+ sidecarRoots.set(node2.path, sidecarResolution.parsedRoot);
1510
+ }
1511
+ return sidecarResolution.issues.map(
1512
+ (i) => i.nodeIds.length > 0 ? i : { ...i, nodeIds: [node2.path] }
1513
+ );
1514
+ };
1461
1515
  if (fullCacheHit && priorNode) {
1462
1516
  const reused = reusePriorNode({
1463
1517
  priorNode,
1464
1518
  bodyHash,
1519
+ sidecarAnnotationsHash,
1465
1520
  strict,
1466
1521
  cachedQualifiedIds,
1467
1522
  applicableQualifiedIds,
@@ -1469,14 +1524,7 @@ async function walkAndExtract(opts) {
1469
1524
  priorLinksByOriginating,
1470
1525
  priorFrontmatterIssuesByNode
1471
1526
  });
1472
- const reusedSidecarIssues = resolveAndApplySidecar(
1473
- reused.node,
1474
- raw.path,
1475
- roots,
1476
- bodyHash,
1477
- frontmatterHash,
1478
- sidecarRoots
1479
- );
1527
+ const reusedSidecarIssues = attachSidecar(reused.node);
1480
1528
  nodes.push(reused.node);
1481
1529
  cachedPaths.add(reused.node.path);
1482
1530
  for (const link of reused.internalLinks) internalLinks.push(link);
@@ -1517,14 +1565,7 @@ async function walkAndExtract(opts) {
1517
1565
  nodes.push(node);
1518
1566
  for (const issue of fresh.frontmatterIssues) frontmatterIssues.push(issue);
1519
1567
  }
1520
- const sidecarIssues = resolveAndApplySidecar(
1521
- node,
1522
- raw.path,
1523
- roots,
1524
- bodyHash,
1525
- frontmatterHash,
1526
- sidecarRoots
1527
- );
1568
+ const sidecarIssues = attachSidecar(node);
1528
1569
  for (const issue of sidecarIssues) frontmatterIssues.push(issue);
1529
1570
  emitter.emit(makeEvent("scan.progress", {
1530
1571
  index,
@@ -1535,7 +1576,7 @@ async function walkAndExtract(opts) {
1535
1576
  }));
1536
1577
  const extractorsToRun = partialCacheHit ? missingExtractors : applicableExtractors;
1537
1578
  for (const ex of extractorsToRun) {
1538
- freshlyRunTuples.add(`${ex.pluginId}/${ex.id}/${node.path}`);
1579
+ freshlyRunTuples.add(`${ex.pluginId}\0${ex.id}\0${node.path}`);
1539
1580
  }
1540
1581
  const extractResult = await runExtractorsForNode({
1541
1582
  extractors: extractorsToRun,
@@ -1559,7 +1600,8 @@ async function walkAndExtract(opts) {
1559
1600
  nodePath: node.path,
1560
1601
  extractorId: qualified,
1561
1602
  bodyHashAtRun: bodyHash,
1562
- ranAt
1603
+ ranAt,
1604
+ sidecarAnnotationsHashAtRun: sidecarAnnotationsHash
1563
1605
  });
1564
1606
  }
1565
1607
  }
@@ -1860,30 +1902,42 @@ function canonicalFrontmatter(parsed, raw) {
1860
1902
  noCompatMode: true
1861
1903
  });
1862
1904
  }
1863
- function resolveAndApplySidecar(node, relativePath, roots, liveBodyHash, liveFrontmatterHash, sidecarRoots) {
1905
+ function canonicalSidecarAnnotations(annotations) {
1906
+ if (!annotations || typeof annotations !== "object" || Array.isArray(annotations)) {
1907
+ return yaml4.dump({}, { sortKeys: true, lineWidth: -1, noRefs: true, noCompatMode: true });
1908
+ }
1909
+ return yaml4.dump(annotations, {
1910
+ sortKeys: true,
1911
+ lineWidth: -1,
1912
+ noRefs: true,
1913
+ noCompatMode: true
1914
+ });
1915
+ }
1916
+ function resolveSidecarOverlay(relativePath, nodePathForIssue, roots, liveBodyHash, liveFrontmatterHash) {
1864
1917
  const issues = [];
1865
1918
  const mdAbs = resolveAbsoluteMdPath(relativePath, roots);
1866
1919
  if (mdAbs === null) {
1867
- node.sidecar = { present: false };
1868
- return issues;
1920
+ return { overlay: { present: false }, issues, parsedRoot: null };
1869
1921
  }
1870
1922
  const result = readSidecarFor(mdAbs);
1871
1923
  if (!result.present) {
1872
- node.sidecar = { present: false };
1873
- return issues;
1924
+ return { overlay: { present: false }, issues, parsedRoot: null };
1874
1925
  }
1875
1926
  if (result.parsed === null) {
1876
- node.sidecar = { present: true, status: null, annotations: null, root: null };
1877
1927
  for (const parseIssue of result.issues) {
1878
1928
  issues.push({
1879
1929
  analyzerId: "invalid-sidecar",
1880
1930
  severity: "warn",
1881
- nodeIds: [node.path],
1931
+ nodeIds: [nodePathForIssue],
1882
1932
  message: parseIssue.message,
1883
1933
  data: { sidecarPath: relativePathFromRoots(mdAbs, roots) }
1884
1934
  });
1885
1935
  }
1886
- return issues;
1936
+ return {
1937
+ overlay: { present: true, status: null, annotations: null, root: null },
1938
+ issues,
1939
+ parsedRoot: null
1940
+ };
1887
1941
  }
1888
1942
  const status = computeDriftStatus({
1889
1943
  storedBodyHash: result.parsed.identityBodyHash,
@@ -1891,22 +1945,30 @@ function resolveAndApplySidecar(node, relativePath, roots, liveBodyHash, liveFro
1891
1945
  liveBodyHash,
1892
1946
  liveFrontmatterHash
1893
1947
  });
1894
- node.sidecar = {
1895
- present: true,
1896
- status,
1897
- annotations: result.parsed.annotations,
1898
- root: result.parsed.raw
1948
+ return {
1949
+ // R15 closure (2026-05-07) — surface the full parsed root on the
1950
+ // overlay so BFF consumers (UI inspector audit / plugin-contributions
1951
+ // / debug panels) can read `for.*`, `audit.*`, `settings.*`, and
1952
+ // plugin-namespaced sub-keys without re-reading the file. The
1953
+ // `annotations` field above stays — it duplicates `root.annotations`
1954
+ // by design so existing consumers keep working unchanged.
1955
+ overlay: {
1956
+ present: true,
1957
+ status,
1958
+ annotations: result.parsed.annotations,
1959
+ root: result.parsed.raw
1960
+ },
1961
+ issues,
1962
+ parsedRoot: result.parsed.raw
1899
1963
  };
1900
- sidecarRoots.set(node.path, result.parsed.raw);
1901
- return issues;
1902
1964
  }
1903
1965
  function resolveAbsoluteMdPath(relativePath, roots) {
1904
- if (isAbsolute2(relativePath)) {
1905
- return existsSync6(relativePath) ? relativePath : null;
1966
+ if (isAbsolute3(relativePath)) {
1967
+ return existsSync8(relativePath) ? relativePath : null;
1906
1968
  }
1907
1969
  for (const root of roots) {
1908
1970
  const candidate = resolvePath(root, relativePath);
1909
- if (existsSync6(candidate)) return candidate;
1971
+ if (existsSync8(candidate)) return candidate;
1910
1972
  }
1911
1973
  return null;
1912
1974
  }
@@ -2066,10 +2128,10 @@ function assignSafe(target, source) {
2066
2128
  }
2067
2129
 
2068
2130
  // kernel/scan/watcher.ts
2069
- import { resolve as resolve6, relative as relative4, sep as sep3 } from "path";
2131
+ import { resolve as resolve8, relative as relative4, sep as sep3 } from "path";
2070
2132
  import chokidar from "chokidar";
2071
2133
  function createChokidarWatcher(opts) {
2072
- const absRoots = opts.roots.map((r) => resolve6(opts.cwd, r));
2134
+ const absRoots = opts.roots.map((r) => resolve8(opts.cwd, r));
2073
2135
  const ignoreFilterOpt = opts.ignoreFilter;
2074
2136
  const getFilter = ignoreFilterOpt === void 0 ? void 0 : typeof ignoreFilterOpt === "function" ? ignoreFilterOpt : () => ignoreFilterOpt;
2075
2137
  const ignored = getFilter ? (path) => {