@skill-map/cli 0.19.0 → 0.20.1

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 (36) hide show
  1. package/dist/cli/tutorial/sm-tutorial.md +3 -3
  2. package/dist/cli.js +7476 -6447
  3. package/dist/cli.js.map +1 -1
  4. package/dist/conformance/index.js +1 -1
  5. package/dist/conformance/index.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.js +182 -141
  8. package/dist/index.js.map +1 -1
  9. package/dist/kernel/index.d.ts +268 -109
  10. package/dist/kernel/index.js +182 -141
  11. package/dist/kernel/index.js.map +1 -1
  12. package/dist/migrations/001_initial.sql +17 -17
  13. package/dist/ui/chunk-4NLC7QD2.js +124 -0
  14. package/dist/ui/{chunk-VWAUXWQX.js → chunk-6BZZQV42.js} +1 -1
  15. package/dist/ui/chunk-6GUHSAP5.js +5 -0
  16. package/dist/ui/chunk-E4ALROJS.js +450 -0
  17. package/dist/ui/{chunk-7CAK6MVK.js → chunk-EZZF5RL5.js} +10 -10
  18. package/dist/ui/chunk-FWX4RRDF.js +125 -0
  19. package/dist/ui/chunk-GGMXMGRJ.js +1 -0
  20. package/dist/ui/chunk-K5PULFK7.js +1 -0
  21. package/dist/ui/chunk-OJ6W6OIB.js +61 -0
  22. package/dist/ui/{chunk-BORRASJB.js → chunk-PTCD42GB.js} +6 -6
  23. package/dist/ui/{chunk-LFIE4SCX.js → chunk-ZSRIBCAW.js} +13 -13
  24. package/dist/ui/index.html +2 -2
  25. package/dist/ui/main-5FJWWH5I.js +1 -0
  26. package/dist/ui/{styles-UAABA7VK.css → styles-VJ5Q6D2X.css} +1 -1
  27. package/migrations/001_initial.sql +17 -17
  28. package/package.json +2 -2
  29. package/dist/ui/chunk-CZSS4D6J.js +0 -454
  30. package/dist/ui/chunk-EQD7AYYJ.js +0 -227
  31. package/dist/ui/chunk-ETTRVTFV.js +0 -1
  32. package/dist/ui/chunk-OKO3QOH6.js +0 -1
  33. package/dist/ui/chunk-PMIMYHBM.js +0 -61
  34. package/dist/ui/chunk-UHFGCO24.js +0 -1
  35. package/dist/ui/chunk-VHIPW3TH.js +0 -1
  36. package/dist/ui/main-BSYMJKTL.js +0 -1
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ function tx(template, vars = {}) {
28
28
  var EXTENSION_KINDS = Object.freeze([
29
29
  "provider",
30
30
  "extractor",
31
- "rule",
31
+ "analyzer",
32
32
  "action",
33
33
  "formatter",
34
34
  "hook"
@@ -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.19.0",
106
+ version: "0.20.1",
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.19.0",
172
+ "@skill-map/spec": "0.20.0",
173
173
  ajv: "8.18.0",
174
174
  "ajv-formats": "3.0.1",
175
175
  chokidar: "5.0.0",
@@ -508,18 +508,20 @@ function formatAjvErrors(errors) {
508
508
 
509
509
  // kernel/extensions/hook.ts
510
510
  var HOOK_TRIGGERS = Object.freeze([
511
+ "boot",
511
512
  "scan.started",
512
513
  "scan.completed",
513
514
  "extractor.completed",
514
- "rule.completed",
515
+ "analyzer.completed",
515
516
  "action.completed",
516
517
  "job.spawning",
517
518
  "job.completed",
518
- "job.failed"
519
+ "job.failed",
520
+ "shutdown"
519
521
  ]);
520
522
 
521
523
  // kernel/adapters/plugin-loader.ts
522
- var KNOWN_KINDS = /* @__PURE__ */ new Set(["provider", "extractor", "rule", "action", "formatter", "hook"]);
524
+ var KNOWN_KINDS = /* @__PURE__ */ new Set(["provider", "extractor", "analyzer", "action", "formatter", "hook"]);
523
525
  var KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(" / ");
524
526
  var HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(", ");
525
527
  function installedSpecVersion() {
@@ -549,7 +551,7 @@ var SCHEMA_FILES = {
549
551
  "history-stats": "schemas/history-stats.schema.json",
550
552
  "extension-provider": "schemas/extensions/provider.schema.json",
551
553
  "extension-extractor": "schemas/extensions/extractor.schema.json",
552
- "extension-rule": "schemas/extensions/rule.schema.json",
554
+ "extension-analyzer": "schemas/extensions/analyzer.schema.json",
553
555
  "extension-action": "schemas/extensions/action.schema.json",
554
556
  "extension-formatter": "schemas/extensions/formatter.schema.json",
555
557
  "extension-hook": "schemas/extensions/hook.schema.json",
@@ -559,7 +561,7 @@ var SUPPORTING_SCHEMAS = [
559
561
  "schemas/extensions/base.schema.json",
560
562
  "schemas/frontmatter/base.schema.json",
561
563
  "schemas/summaries/security-scanner.schema.json",
562
- "schemas/view-contracts.schema.json",
564
+ "schemas/view-slots.schema.json",
563
565
  "schemas/input-types.schema.json"
564
566
  ];
565
567
  var cachedValidators = null;
@@ -592,7 +594,7 @@ function buildSchemaValidators() {
592
594
  const extensionByKind = {
593
595
  provider: "extension-provider",
594
596
  extractor: "extension-extractor",
595
- rule: "extension-rule",
597
+ analyzer: "extension-analyzer",
596
598
  action: "extension-action",
597
599
  formatter: "extension-formatter",
598
600
  hook: "extension-hook"
@@ -601,18 +603,35 @@ function buildSchemaValidators() {
601
603
  $ref: "https://skill-map.dev/spec/v0/plugins-registry.schema.json#/$defs/PluginManifest"
602
604
  });
603
605
  const contributionValidators = /* @__PURE__ */ new Map();
604
- const VIEW_CONTRACTS_ID = "https://skill-map.dev/spec/v0/view-contracts.schema.json";
605
- function getContributionValidator(contract) {
606
- const existing = contributionValidators.get(contract);
606
+ const VIEW_SLOTS_ID = "https://skill-map.dev/spec/v0/view-slots.schema.json";
607
+ const KNOWN_SLOTS = /* @__PURE__ */ new Set([
608
+ "card.title.right",
609
+ "card.subtitle.left",
610
+ "card.footer.left.counter",
611
+ "card.footer.right",
612
+ "graph.node.alert",
613
+ "inspector.header.badge.counter",
614
+ "inspector.header.badge.tag",
615
+ "inspector.body.panel.breakdown",
616
+ "inspector.body.panel.records",
617
+ "inspector.body.panel.tree",
618
+ "inspector.body.panel.key-values",
619
+ "inspector.body.panel.link-list",
620
+ "inspector.body.panel.markdown",
621
+ "topbar.actions.indicator"
622
+ ]);
623
+ function getContributionValidator(slot) {
624
+ if (!KNOWN_SLOTS.has(slot)) return null;
625
+ const existing = contributionValidators.get(slot);
607
626
  if (existing) return existing;
608
- const ref = `${VIEW_CONTRACTS_ID}#/$defs/payloads/${contract}`;
627
+ const ref = `${VIEW_SLOTS_ID}#/$defs/payloads/${slot}`;
609
628
  let compiled;
610
629
  try {
611
630
  compiled = ajv.compile({ $ref: ref });
612
631
  } catch {
613
632
  return null;
614
633
  }
615
- contributionValidators.set(contract, compiled);
634
+ contributionValidators.set(slot, compiled);
616
635
  return compiled;
617
636
  }
618
637
  return {
@@ -636,10 +655,10 @@ function buildSchemaValidators() {
636
655
  const errors = (pluginManifestValidator.errors ?? []).map(formatError).join("; ");
637
656
  return { ok: false, errors };
638
657
  },
639
- validateContributionPayload(contract, payload) {
640
- const validator = getContributionValidator(contract);
658
+ validateContributionPayload(slot, payload) {
659
+ const validator = getContributionValidator(slot);
641
660
  if (!validator) {
642
- return { ok: false, errors: "unknown-contract" };
661
+ return { ok: false, errors: "unknown-slot" };
643
662
  }
644
663
  if (validator(payload)) return { ok: true };
645
664
  const errors = (validator.errors ?? []).map(formatError).join("; ");
@@ -720,9 +739,9 @@ var ORCHESTRATOR_TEXTS = {
720
739
  frontmatterMalformedByteOrderMark: "Frontmatter fence in {{path}} is preceded by a UTF-8 byte-order mark (BOM); the file was scanned as body-only. Re-save the file as UTF-8 without BOM. The metadata block was silently lost.",
721
740
  frontmatterMalformedMissingClose: "Frontmatter in {{path}} opens with `---` but never closes \u2014 no matching `---` line at column 0 was found. The file was scanned as body-only and every metadata field was silently lost. Add a closing `---` line below the metadata block.",
722
741
  extensionErrorLinkKindNotDeclared: 'Extractor "{{extractorId}}" emitted a link of kind "{{linkKind}}" outside its declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',
723
- extensionErrorIssueInvalidSeverity: `Rule "{{ruleId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
742
+ extensionErrorIssueInvalidSeverity: `Rule "{{analyzerId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
724
743
  extensionErrorContributionUnknownId: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}} but did not declare it in its `viewContributions` map. Contribution dropped.',
725
- extensionErrorContributionPayloadInvalid: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}}; payload failed the "{{contract}}" schema: {{errors}}. Contribution dropped.',
744
+ extensionErrorContributionPayloadInvalid: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}}; payload failed the "{{slot}}" schema: {{errors}}. Contribution dropped.',
726
745
  runScanRootEmptyArray: "runScan: roots must contain at least one path (spec requires minItems: 1)",
727
746
  runScanRootMissing: "runScan: root path '{{root}}' does not exist or is not a directory"
728
747
  };
@@ -789,7 +808,7 @@ function readDefaultsFromDisk() {
789
808
  return "";
790
809
  }
791
810
 
792
- // kernel/scan/parsers/frontmatter-yaml.ts
811
+ // built-in-plugins/parsers/frontmatter-yaml/index.ts
793
812
  import yaml3 from "js-yaml";
794
813
  var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
795
814
  var FORBIDDEN_FRONTMATTER_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
@@ -815,7 +834,7 @@ var frontmatterYamlParser = {
815
834
  }
816
835
  };
817
836
 
818
- // kernel/scan/parsers/plain.ts
837
+ // built-in-plugins/parsers/plain/index.ts
819
838
  var plainParser = {
820
839
  id: "plain",
821
840
  parse(raw, _path) {
@@ -916,6 +935,85 @@ function resolveProviderWalk(provider) {
916
935
  };
917
936
  }
918
937
 
938
+ // kernel/extensions/hook-dispatcher.ts
939
+ function makeHookDispatcher(hooks, emitter) {
940
+ if (hooks.length === 0) {
941
+ return { dispatch: async () => {
942
+ } };
943
+ }
944
+ const byTrigger = /* @__PURE__ */ new Map();
945
+ for (const hook of hooks) {
946
+ if (hook.mode === "probabilistic") {
947
+ const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);
948
+ log.warn(
949
+ `Probabilistic hook ${qualifiedId} deferred to job subsystem (future job subsystem). The hook is registered but will not dispatch in-scan.`,
950
+ { hookId: qualifiedId, mode: "probabilistic" }
951
+ );
952
+ continue;
953
+ }
954
+ for (const trig of hook.triggers) {
955
+ const bucket = byTrigger.get(trig);
956
+ if (bucket) bucket.push(hook);
957
+ else byTrigger.set(trig, [hook]);
958
+ }
959
+ }
960
+ return {
961
+ async dispatch(trigger, event) {
962
+ const subs = byTrigger.get(trigger);
963
+ if (!subs || subs.length === 0) return;
964
+ for (const hook of subs) {
965
+ if (!matchesFilter(hook, event)) continue;
966
+ const ctx = buildHookContext(hook, trigger, event);
967
+ try {
968
+ await hook.on(ctx);
969
+ } catch (err) {
970
+ const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);
971
+ const message = formatErrorMessage(err);
972
+ emitter.emit(
973
+ makeEvent("extension.error", {
974
+ kind: "hook-error",
975
+ extensionId: qualifiedId,
976
+ trigger,
977
+ message
978
+ })
979
+ );
980
+ }
981
+ }
982
+ }
983
+ };
984
+ }
985
+ function makeEvent(type, data) {
986
+ return { type, timestamp: (/* @__PURE__ */ new Date()).toISOString(), data };
987
+ }
988
+ function matchesFilter(hook, event) {
989
+ if (!hook.filter) return true;
990
+ const data = event.data ?? {};
991
+ for (const [key, expected] of Object.entries(hook.filter)) {
992
+ if (data[key] !== expected) return false;
993
+ }
994
+ return true;
995
+ }
996
+ function buildHookContext(_hook, trigger, event) {
997
+ const data = event.data ?? {};
998
+ const ctx = {
999
+ event: {
1000
+ type: trigger,
1001
+ timestamp: event.timestamp,
1002
+ ...event.runId !== void 0 ? { runId: event.runId } : {},
1003
+ ...event.jobId !== void 0 ? { jobId: event.jobId } : {},
1004
+ data: event.data
1005
+ }
1006
+ };
1007
+ if (typeof data["extractorId"] === "string") ctx.extractorId = data["extractorId"];
1008
+ if (typeof data["analyzerId"] === "string") ctx.analyzerId = data["analyzerId"];
1009
+ if (typeof data["actionId"] === "string") ctx.actionId = data["actionId"];
1010
+ if (data["node"] && typeof data["node"] === "object") {
1011
+ ctx.node = data["node"];
1012
+ }
1013
+ if (data["jobResult"] !== void 0) ctx.jobResult = data["jobResult"];
1014
+ return ctx;
1015
+ }
1016
+
919
1017
  // kernel/orchestrator.ts
920
1018
  var SCANNED_BY = {
921
1019
  name: "skill-map",
@@ -941,7 +1039,7 @@ async function runScanInternal(_kernel, options) {
941
1039
  const start = Date.now();
942
1040
  const scannedAt = start;
943
1041
  const emitter = options.emitter ?? new InMemoryProgressEmitter();
944
- const exts = options.extensions ?? { providers: [], extractors: [], rules: [] };
1042
+ const exts = options.extensions ?? { providers: [], extractors: [], analyzers: [] };
945
1043
  const hookDispatcher = makeHookDispatcher(exts.hooks ?? [], emitter);
946
1044
  const tokenize = options.tokenize !== false;
947
1045
  const scope = options.scope ?? "project";
@@ -978,19 +1076,28 @@ async function runScanInternal(_kernel, options) {
978
1076
  emitter.emit(evt);
979
1077
  await hookDispatcher.dispatch("extractor.completed", evt);
980
1078
  }
981
- const ruleResult = await runRules(
982
- exts.rules,
1079
+ const analyzerResult = await runAnalyzers(
1080
+ exts.analyzers,
983
1081
  walked.nodes,
984
1082
  walked.internalLinks,
985
1083
  walked.orphanSidecars,
986
1084
  walked.sidecarRoots,
987
1085
  options.annotationContributions ?? [],
988
1086
  options.viewContributions ?? [],
1087
+ options.orphanJobFiles ?? [],
1088
+ options.referenceablePaths,
1089
+ options.cwd,
989
1090
  emitter,
990
1091
  hookDispatcher
991
1092
  );
992
- const issues = ruleResult.issues;
993
- for (const c of ruleResult.contributions) walked.contributions.push(c);
1093
+ const issues = analyzerResult.issues;
1094
+ for (const c of analyzerResult.contributions) walked.contributions.push(c);
1095
+ for (const analyzer of exts.analyzers ?? []) {
1096
+ if (analyzer.viewContributions === void 0) continue;
1097
+ for (const node of walked.nodes) {
1098
+ walked.freshlyRunTuples.add(`${analyzer.pluginId}/${analyzer.id}/${node.path}`);
1099
+ }
1100
+ }
994
1101
  for (const issue of walked.frontmatterIssues) issues.push(issue);
995
1102
  const renameOps = prior ? detectRenamesAndOrphans(prior, walked.nodes, issues) : [];
996
1103
  const stats = {
@@ -1025,7 +1132,8 @@ async function runScanInternal(_kernel, options) {
1025
1132
  renameOps,
1026
1133
  extractorRuns: walked.extractorRuns,
1027
1134
  enrichments: walked.enrichments,
1028
- contributions: walked.contributions
1135
+ contributions: walked.contributions,
1136
+ freshlyRunTuples: walked.freshlyRunTuples
1029
1137
  };
1030
1138
  }
1031
1139
  function validateRoots(roots) {
@@ -1057,7 +1165,7 @@ function indexPriorSnapshot(prior) {
1057
1165
  else priorLinksByOriginating.set(key, [link]);
1058
1166
  }
1059
1167
  for (const issue of prior.issues) {
1060
- if (issue.ruleId !== "frontmatter-invalid" && issue.ruleId !== "frontmatter-malformed") continue;
1168
+ if (issue.analyzerId !== "frontmatter-invalid" && issue.analyzerId !== "frontmatter-malformed") continue;
1061
1169
  if (issue.nodeIds.length !== 1) continue;
1062
1170
  const path = issue.nodeIds[0];
1063
1171
  const list = priorFrontmatterIssuesByNode.get(path);
@@ -1115,18 +1223,18 @@ async function runExtractorsForNode(opts) {
1115
1223
  });
1116
1224
  return;
1117
1225
  }
1118
- const result = validators.validateContributionPayload(declared.contract, payload);
1226
+ const result = validators.validateContributionPayload(declared.slot, payload);
1119
1227
  if (!result.ok) {
1120
1228
  emitExtensionError(opts.emitter, qualifiedId, opts.node.path, {
1121
1229
  phase: "emitContribution",
1122
1230
  contributionId,
1123
- contract: declared.contract,
1231
+ slot: declared.slot,
1124
1232
  reason: result.errors,
1125
1233
  message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
1126
1234
  extractorId: qualifiedId,
1127
1235
  contributionId,
1128
1236
  nodePath: opts.node.path,
1129
- contract: declared.contract,
1237
+ slot: declared.slot,
1130
1238
  errors: result.errors
1131
1239
  })
1132
1240
  });
@@ -1137,7 +1245,7 @@ async function runExtractorsForNode(opts) {
1137
1245
  extensionId: extractor.id,
1138
1246
  nodePath: opts.node.path,
1139
1247
  contributionId,
1140
- contract: declared.contract,
1248
+ slot: declared.slot,
1141
1249
  payload,
1142
1250
  emittedAt: Date.now()
1143
1251
  });
@@ -1168,9 +1276,9 @@ function readDeclaredContributions(extension) {
1168
1276
  if (typeof raw !== "object" || raw === null) return out;
1169
1277
  for (const [id, value] of Object.entries(raw)) {
1170
1278
  if (typeof value !== "object" || value === null) continue;
1171
- const contract = value.contract;
1172
- if (typeof contract !== "string") continue;
1173
- out.set(id, { contract });
1279
+ const slot = value.slot;
1280
+ if (typeof slot !== "string") continue;
1281
+ out.set(id, { slot });
1174
1282
  }
1175
1283
  return out;
1176
1284
  }
@@ -1307,6 +1415,7 @@ async function walkAndExtract(opts) {
1307
1415
  const frontmatterIssues = [];
1308
1416
  const enrichmentBuffer = /* @__PURE__ */ new Map();
1309
1417
  const contributionsBuffer = [];
1418
+ const freshlyRunTuples = /* @__PURE__ */ new Set();
1310
1419
  const extractorRuns = [];
1311
1420
  const sidecarRoots = /* @__PURE__ */ new Map();
1312
1421
  let filesWalked = 0;
@@ -1425,6 +1534,9 @@ async function walkAndExtract(opts) {
1425
1534
  ...partialCacheHit ? { partialCache: true } : {}
1426
1535
  }));
1427
1536
  const extractorsToRun = partialCacheHit ? missingExtractors : applicableExtractors;
1537
+ for (const ex of extractorsToRun) {
1538
+ freshlyRunTuples.add(`${ex.pluginId}/${ex.id}/${node.path}`);
1539
+ }
1428
1540
  const extractResult = await runExtractorsForNode({
1429
1541
  extractors: extractorsToRun,
1430
1542
  node,
@@ -1463,6 +1575,7 @@ async function walkAndExtract(opts) {
1463
1575
  enrichments: [...enrichmentBuffer.values()],
1464
1576
  extractorRuns,
1465
1577
  contributions: contributionsBuffer,
1578
+ freshlyRunTuples,
1466
1579
  orphanSidecars,
1467
1580
  sidecarRoots
1468
1581
  };
@@ -1493,17 +1606,17 @@ function reuseCachedLink(link, shortIdToQualified, cachedQualifiedIds, applicabl
1493
1606
  if (obsoleteSources.length === 0) return link;
1494
1607
  return { ...link, sources: cachedSources };
1495
1608
  }
1496
- async function runRules(rules, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, emitter, hookDispatcher) {
1609
+ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, emitter, hookDispatcher) {
1497
1610
  const issues = [];
1498
1611
  const contributions = [];
1499
1612
  const validators = loadSchemaValidators();
1500
- const ruleOrphans = orphanSidecars.map((o) => ({
1613
+ const analyzerOrphans = orphanSidecars.map((o) => ({
1501
1614
  relativePath: o.relativePath,
1502
1615
  expectedMdPath: o.expectedMdPath
1503
1616
  }));
1504
- for (const rule of rules) {
1505
- const qualifiedId = qualifiedExtensionId(rule.pluginId, rule.id);
1506
- const declaredContributions = readDeclaredContributions(rule);
1617
+ for (const analyzer of analyzers) {
1618
+ const qualifiedId = qualifiedExtensionId(analyzer.pluginId, analyzer.id);
1619
+ const declaredContributions = readDeclaredContributions(analyzer);
1507
1620
  const emitContribution = (nodePath, contributionId, payload) => {
1508
1621
  const declared = declaredContributions.get(contributionId);
1509
1622
  if (!declared) {
@@ -1519,49 +1632,52 @@ async function runRules(rules, nodes, internalLinks, orphanSidecars, sidecarRoot
1519
1632
  });
1520
1633
  return;
1521
1634
  }
1522
- const result = validators.validateContributionPayload(declared.contract, payload);
1635
+ const result = validators.validateContributionPayload(declared.slot, payload);
1523
1636
  if (!result.ok) {
1524
1637
  emitExtensionError(emitter, qualifiedId, nodePath, {
1525
1638
  phase: "emitContribution",
1526
1639
  contributionId,
1527
- contract: declared.contract,
1640
+ slot: declared.slot,
1528
1641
  reason: result.errors,
1529
1642
  message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
1530
1643
  extractorId: qualifiedId,
1531
1644
  contributionId,
1532
1645
  nodePath,
1533
- contract: declared.contract,
1646
+ slot: declared.slot,
1534
1647
  errors: result.errors
1535
1648
  })
1536
1649
  });
1537
1650
  return;
1538
1651
  }
1539
1652
  contributions.push({
1540
- pluginId: rule.pluginId,
1541
- extensionId: rule.id,
1653
+ pluginId: analyzer.pluginId,
1654
+ extensionId: analyzer.id,
1542
1655
  nodePath,
1543
1656
  contributionId,
1544
- contract: declared.contract,
1657
+ slot: declared.slot,
1545
1658
  payload,
1546
1659
  emittedAt: Date.now()
1547
1660
  });
1548
1661
  };
1549
- const emitted = await rule.evaluate({
1662
+ const emitted = await analyzer.evaluate({
1550
1663
  nodes,
1551
1664
  links: internalLinks,
1552
- orphanSidecars: ruleOrphans,
1665
+ orphanSidecars: analyzerOrphans,
1553
1666
  sidecarRoots,
1554
1667
  annotationContributions,
1555
1668
  viewContributions,
1669
+ orphanJobFiles,
1670
+ ...referenceablePaths ? { referenceablePaths } : {},
1671
+ ...cwd ? { cwd } : {},
1556
1672
  emitContribution
1557
1673
  });
1558
1674
  for (const issue of emitted) {
1559
- const validated = validateIssue(rule, issue, emitter);
1675
+ const validated = validateIssue(analyzer, issue, emitter);
1560
1676
  if (validated) issues.push(validated);
1561
1677
  }
1562
- const evt = makeEvent("rule.completed", { ruleId: qualifiedId });
1678
+ const evt = makeEvent("analyzer.completed", { analyzerId: qualifiedId });
1563
1679
  emitter.emit(evt);
1564
- await hookDispatcher.dispatch("rule.completed", evt);
1680
+ await hookDispatcher.dispatch("analyzer.completed", evt);
1565
1681
  }
1566
1682
  return { issues, contributions };
1567
1683
  }
@@ -1617,7 +1733,7 @@ function claimSingletonRenames(opts) {
1617
1733
  const fromPath = remaining[0];
1618
1734
  ops.push({ from: fromPath, to: toPath, confidence: "medium" });
1619
1735
  opts.issues.push({
1620
- ruleId: "auto-rename-medium",
1736
+ analyzerId: "auto-rename-medium",
1621
1737
  severity: "warn",
1622
1738
  nodeIds: [toPath],
1623
1739
  message: `Auto-rename (medium confidence): ${fromPath} \u2192 ${toPath}`,
@@ -1637,7 +1753,7 @@ function flagAmbiguousRenames(opts) {
1637
1753
  const remaining = candidates.filter((p) => !opts.claimedDeleted.has(p));
1638
1754
  if (remaining.length > 1) {
1639
1755
  opts.issues.push({
1640
- ruleId: "auto-rename-ambiguous",
1756
+ analyzerId: "auto-rename-ambiguous",
1641
1757
  severity: "warn",
1642
1758
  nodeIds: [toPath],
1643
1759
  message: `Auto-rename ambiguous: ${toPath} matches ${remaining.length} prior frontmatters \u2014 pick one with \`sm orphans undo-rename ${toPath} --from <old.path>\`.`,
@@ -1650,7 +1766,7 @@ function flagOrphans(opts) {
1650
1766
  for (const fromPath of opts.deletedPaths) {
1651
1767
  if (opts.claimedDeleted.has(fromPath)) continue;
1652
1768
  opts.issues.push({
1653
- ruleId: "orphan",
1769
+ analyzerId: "orphan",
1654
1770
  severity: "info",
1655
1771
  nodeIds: [fromPath],
1656
1772
  message: `Orphan history: ${fromPath} was deleted; no rename match found.`,
@@ -1699,83 +1815,6 @@ var EXTERNAL_URL_SCHEME_RE = /^[a-z][a-z0-9+\-.]+:/i;
1699
1815
  function isExternalUrlLink(link) {
1700
1816
  return EXTERNAL_URL_SCHEME_RE.test(link.target);
1701
1817
  }
1702
- function makeEvent(type, data) {
1703
- return { type, timestamp: (/* @__PURE__ */ new Date()).toISOString(), data };
1704
- }
1705
- function makeHookDispatcher(hooks, emitter) {
1706
- if (hooks.length === 0) {
1707
- return { dispatch: async () => {
1708
- } };
1709
- }
1710
- const byTrigger = /* @__PURE__ */ new Map();
1711
- for (const hook of hooks) {
1712
- if (hook.mode === "probabilistic") {
1713
- const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);
1714
- log.warn(
1715
- `Probabilistic hook ${qualifiedId} deferred to job subsystem (future job subsystem). The hook is registered but will not dispatch in-scan.`,
1716
- { hookId: qualifiedId, mode: "probabilistic" }
1717
- );
1718
- continue;
1719
- }
1720
- for (const trig of hook.triggers) {
1721
- const bucket = byTrigger.get(trig);
1722
- if (bucket) bucket.push(hook);
1723
- else byTrigger.set(trig, [hook]);
1724
- }
1725
- }
1726
- return {
1727
- async dispatch(trigger, event) {
1728
- const subs = byTrigger.get(trigger);
1729
- if (!subs || subs.length === 0) return;
1730
- for (const hook of subs) {
1731
- if (!matchesFilter(hook, event)) continue;
1732
- const ctx = buildHookContext(hook, trigger, event);
1733
- try {
1734
- await hook.on(ctx);
1735
- } catch (err) {
1736
- const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);
1737
- const message = formatErrorMessage(err);
1738
- emitter.emit(
1739
- makeEvent("extension.error", {
1740
- kind: "hook-error",
1741
- extensionId: qualifiedId,
1742
- trigger,
1743
- message
1744
- })
1745
- );
1746
- }
1747
- }
1748
- }
1749
- };
1750
- }
1751
- function matchesFilter(hook, event) {
1752
- if (!hook.filter) return true;
1753
- const data = event.data ?? {};
1754
- for (const [key, expected] of Object.entries(hook.filter)) {
1755
- if (data[key] !== expected) return false;
1756
- }
1757
- return true;
1758
- }
1759
- function buildHookContext(_hook, trigger, event) {
1760
- const data = event.data ?? {};
1761
- const ctx = {
1762
- event: {
1763
- type: trigger,
1764
- timestamp: event.timestamp,
1765
- ...event.runId !== void 0 ? { runId: event.runId } : {},
1766
- ...event.jobId !== void 0 ? { jobId: event.jobId } : {},
1767
- data: event.data
1768
- }
1769
- };
1770
- if (typeof data["extractorId"] === "string") ctx.extractorId = data["extractorId"];
1771
- if (typeof data["ruleId"] === "string") ctx.ruleId = data["ruleId"];
1772
- if (typeof data["actionId"] === "string") ctx.actionId = data["actionId"];
1773
- if (data["node"] && typeof data["node"] === "object") {
1774
- ctx.node = data["node"];
1775
- }
1776
- if (data["jobResult"] !== void 0) ctx.jobResult = data["jobResult"];
1777
- return ctx;
1778
- }
1779
1818
  function buildNode(args) {
1780
1819
  const bytesFrontmatter = Buffer.byteLength(args.frontmatterRaw, "utf8");
1781
1820
  const bytesBody = Buffer.byteLength(args.body, "utf8");
@@ -1837,7 +1876,7 @@ function resolveAndApplySidecar(node, relativePath, roots, liveBodyHash, liveFro
1837
1876
  node.sidecar = { present: true, status: null, annotations: null, root: null };
1838
1877
  for (const parseIssue of result.issues) {
1839
1878
  issues.push({
1840
- ruleId: "invalid-sidecar",
1879
+ analyzerId: "invalid-sidecar",
1841
1880
  severity: "warn",
1842
1881
  nodeIds: [node.path],
1843
1882
  message: parseIssue.message,
@@ -1918,7 +1957,7 @@ function validateFrontmatter(providerFrontmatter, provider, kind, frontmatter, p
1918
1957
  const result = providerFrontmatter.validate(provider, kind, frontmatter);
1919
1958
  if (result.ok) return null;
1920
1959
  return {
1921
- ruleId: "frontmatter-invalid",
1960
+ analyzerId: "frontmatter-invalid",
1922
1961
  severity: strict ? "error" : "warn",
1923
1962
  nodeIds: [path],
1924
1963
  message: tx(ORCHESTRATOR_TEXTS.frontmatterInvalid, { path, kind, errors: result.errors }),
@@ -1929,7 +1968,7 @@ function detectMalformedFrontmatter(body, path, strict) {
1929
1968
  const hint = classifyMalformedFrontmatter(body);
1930
1969
  if (!hint) return null;
1931
1970
  return {
1932
- ruleId: "frontmatter-malformed",
1971
+ analyzerId: "frontmatter-malformed",
1933
1972
  severity: strict ? "error" : "warn",
1934
1973
  nodeIds: [path],
1935
1974
  message: malformedMessage(hint, path),
@@ -1963,25 +2002,25 @@ function malformedMessage(hint, path) {
1963
2002
  return tx(ORCHESTRATOR_TEXTS.frontmatterMalformedMissingClose, { path });
1964
2003
  }
1965
2004
  }
1966
- function validateIssue(rule, issue, emitter) {
2005
+ function validateIssue(analyzer, issue, emitter) {
1967
2006
  const severity = issue.severity;
1968
2007
  if (severity !== "error" && severity !== "warn" && severity !== "info") {
1969
- const qualifiedId = `${rule.pluginId}/${rule.id}`;
2008
+ const qualifiedId = `${analyzer.pluginId}/${analyzer.id}`;
1970
2009
  emitter.emit(
1971
2010
  makeEvent("extension.error", {
1972
2011
  kind: "issue-invalid-severity",
1973
2012
  extensionId: qualifiedId,
1974
2013
  severity,
1975
- issue: { ruleId: issue.ruleId || rule.id, message: issue.message, nodeIds: issue.nodeIds },
2014
+ issue: { analyzerId: issue.analyzerId || analyzer.id, message: issue.message, nodeIds: issue.nodeIds },
1976
2015
  message: tx(ORCHESTRATOR_TEXTS.extensionErrorIssueInvalidSeverity, {
1977
- ruleId: qualifiedId,
2016
+ analyzerId: qualifiedId,
1978
2017
  severity: JSON.stringify(severity)
1979
2018
  })
1980
2019
  })
1981
2020
  );
1982
2021
  return null;
1983
2022
  }
1984
- return { ...issue, ruleId: issue.ruleId || rule.id };
2023
+ return { ...issue, analyzerId: issue.analyzerId || analyzer.id };
1985
2024
  }
1986
2025
  function recomputeLinkCounts(nodes, links) {
1987
2026
  const byPath2 = /* @__PURE__ */ new Map();
@@ -2218,10 +2257,10 @@ function diffIssues(priorIssues, currentIssues) {
2218
2257
  }
2219
2258
  function issueIdentity(issue) {
2220
2259
  const ids = [...issue.nodeIds].sort().join(",");
2221
- return `${issue.ruleId}\0${ids}\0${issue.message}`;
2260
+ return `${issue.analyzerId}\0${ids}\0${issue.message}`;
2222
2261
  }
2223
2262
  function byIssueSort(a, b) {
2224
- if (a.ruleId !== b.ruleId) return a.ruleId.localeCompare(b.ruleId);
2263
+ if (a.analyzerId !== b.analyzerId) return a.analyzerId.localeCompare(b.analyzerId);
2225
2264
  return a.message.localeCompare(b.message);
2226
2265
  }
2227
2266
 
@@ -2416,6 +2455,8 @@ export {
2416
2455
  log,
2417
2456
  logLevelRank,
2418
2457
  makeDedicatedStoreWrapper,
2458
+ makeEvent,
2459
+ makeHookDispatcher,
2419
2460
  makeKvStoreWrapper,
2420
2461
  makePluginStore,
2421
2462
  mergeNodeWithEnrichments,