@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.
- package/dist/cli/tutorial/sm-tutorial.md +3 -3
- package/dist/cli.js +7476 -6447
- package/dist/cli.js.map +1 -1
- package/dist/conformance/index.js +1 -1
- package/dist/conformance/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +182 -141
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +268 -109
- package/dist/kernel/index.js +182 -141
- package/dist/kernel/index.js.map +1 -1
- package/dist/migrations/001_initial.sql +17 -17
- package/dist/ui/chunk-4NLC7QD2.js +124 -0
- package/dist/ui/{chunk-VWAUXWQX.js → chunk-6BZZQV42.js} +1 -1
- package/dist/ui/chunk-6GUHSAP5.js +5 -0
- package/dist/ui/chunk-E4ALROJS.js +450 -0
- package/dist/ui/{chunk-7CAK6MVK.js → chunk-EZZF5RL5.js} +10 -10
- package/dist/ui/chunk-FWX4RRDF.js +125 -0
- package/dist/ui/chunk-GGMXMGRJ.js +1 -0
- package/dist/ui/chunk-K5PULFK7.js +1 -0
- package/dist/ui/chunk-OJ6W6OIB.js +61 -0
- package/dist/ui/{chunk-BORRASJB.js → chunk-PTCD42GB.js} +6 -6
- package/dist/ui/{chunk-LFIE4SCX.js → chunk-ZSRIBCAW.js} +13 -13
- package/dist/ui/index.html +2 -2
- package/dist/ui/main-5FJWWH5I.js +1 -0
- package/dist/ui/{styles-UAABA7VK.css → styles-VJ5Q6D2X.css} +1 -1
- package/migrations/001_initial.sql +17 -17
- package/package.json +2 -2
- package/dist/ui/chunk-CZSS4D6J.js +0 -454
- package/dist/ui/chunk-EQD7AYYJ.js +0 -227
- package/dist/ui/chunk-ETTRVTFV.js +0 -1
- package/dist/ui/chunk-OKO3QOH6.js +0 -1
- package/dist/ui/chunk-PMIMYHBM.js +0 -61
- package/dist/ui/chunk-UHFGCO24.js +0 -1
- package/dist/ui/chunk-VHIPW3TH.js +0 -1
- 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
|
-
"
|
|
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.
|
|
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.
|
|
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
|
-
"
|
|
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", "
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
|
605
|
-
|
|
606
|
-
|
|
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 = `${
|
|
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(
|
|
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(
|
|
640
|
-
const validator = getContributionValidator(
|
|
658
|
+
validateContributionPayload(slot, payload) {
|
|
659
|
+
const validator = getContributionValidator(slot);
|
|
641
660
|
if (!validator) {
|
|
642
|
-
return { ok: false, errors: "unknown-
|
|
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 "{{
|
|
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 "{{
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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: [],
|
|
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
|
|
982
|
-
exts.
|
|
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 =
|
|
993
|
-
for (const c of
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1172
|
-
if (typeof
|
|
1173
|
-
out.set(id, {
|
|
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
|
|
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
|
|
1613
|
+
const analyzerOrphans = orphanSidecars.map((o) => ({
|
|
1501
1614
|
relativePath: o.relativePath,
|
|
1502
1615
|
expectedMdPath: o.expectedMdPath
|
|
1503
1616
|
}));
|
|
1504
|
-
for (const
|
|
1505
|
-
const qualifiedId = qualifiedExtensionId(
|
|
1506
|
-
const declaredContributions = readDeclaredContributions(
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
1646
|
+
slot: declared.slot,
|
|
1534
1647
|
errors: result.errors
|
|
1535
1648
|
})
|
|
1536
1649
|
});
|
|
1537
1650
|
return;
|
|
1538
1651
|
}
|
|
1539
1652
|
contributions.push({
|
|
1540
|
-
pluginId:
|
|
1541
|
-
extensionId:
|
|
1653
|
+
pluginId: analyzer.pluginId,
|
|
1654
|
+
extensionId: analyzer.id,
|
|
1542
1655
|
nodePath,
|
|
1543
1656
|
contributionId,
|
|
1544
|
-
|
|
1657
|
+
slot: declared.slot,
|
|
1545
1658
|
payload,
|
|
1546
1659
|
emittedAt: Date.now()
|
|
1547
1660
|
});
|
|
1548
1661
|
};
|
|
1549
|
-
const emitted = await
|
|
1662
|
+
const emitted = await analyzer.evaluate({
|
|
1550
1663
|
nodes,
|
|
1551
1664
|
links: internalLinks,
|
|
1552
|
-
orphanSidecars:
|
|
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(
|
|
1675
|
+
const validated = validateIssue(analyzer, issue, emitter);
|
|
1560
1676
|
if (validated) issues.push(validated);
|
|
1561
1677
|
}
|
|
1562
|
-
const evt = makeEvent("
|
|
1678
|
+
const evt = makeEvent("analyzer.completed", { analyzerId: qualifiedId });
|
|
1563
1679
|
emitter.emit(evt);
|
|
1564
|
-
await hookDispatcher.dispatch("
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
2005
|
+
function validateIssue(analyzer, issue, emitter) {
|
|
1967
2006
|
const severity = issue.severity;
|
|
1968
2007
|
if (severity !== "error" && severity !== "warn" && severity !== "info") {
|
|
1969
|
-
const qualifiedId = `${
|
|
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: {
|
|
2014
|
+
issue: { analyzerId: issue.analyzerId || analyzer.id, message: issue.message, nodeIds: issue.nodeIds },
|
|
1976
2015
|
message: tx(ORCHESTRATOR_TEXTS.extensionErrorIssueInvalidSeverity, {
|
|
1977
|
-
|
|
2016
|
+
analyzerId: qualifiedId,
|
|
1978
2017
|
severity: JSON.stringify(severity)
|
|
1979
2018
|
})
|
|
1980
2019
|
})
|
|
1981
2020
|
);
|
|
1982
2021
|
return null;
|
|
1983
2022
|
}
|
|
1984
|
-
return { ...issue,
|
|
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.
|
|
2260
|
+
return `${issue.analyzerId}\0${ids}\0${issue.message}`;
|
|
2222
2261
|
}
|
|
2223
2262
|
function byIssueSort(a, b) {
|
|
2224
|
-
if (a.
|
|
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,
|