@skill-map/cli 0.56.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/tutorial/sm-tutorial/references/part-cli.md +1 -1
- package/dist/cli.js +969 -912
- package/dist/index.js +120 -33
- package/dist/kernel/index.d.ts +139 -63
- package/dist/kernel/index.js +120 -33
- package/dist/migrations/001_initial.sql +45 -0
- package/dist/ui/{chunk-W3Z3CZL4.js → chunk-5MK5Y6FQ.js} +2 -2
- package/dist/ui/{chunk-GHOVZAAV.js → chunk-EEDP2D5A.js} +1 -1
- package/dist/ui/chunk-EL6RUV24.js +3 -0
- package/dist/ui/chunk-P3SSH5JE.js +1 -0
- package/dist/ui/{chunk-DWBJCNC7.js → chunk-PTNXJKIX.js} +1 -1
- package/dist/ui/index.html +1 -1
- package/dist/ui/{main-PL3BEVQI.js → main-ZNXBC7R5.js} +3 -3
- package/migrations/001_initial.sql +45 -0
- package/package.json +2 -2
- package/dist/ui/chunk-4ITL7E6U.js +0 -1
- package/dist/ui/chunk-VUNP5KNI.js +0 -3
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// kernel/i18n/registry.texts.ts
|
|
2
2
|
|
|
3
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2d36a27f-0bac-51aa-8fb3-3b3e43eff4e8")}catch(e){}}();
|
|
4
4
|
var REGISTRY_TEXTS = {
|
|
5
5
|
duplicateExtension: "Extension already registered: {{kind}}:{{qualifiedId}}",
|
|
6
6
|
unknownKind: "Unknown extension kind: {{kind}}",
|
|
@@ -102,7 +102,7 @@ import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
|
|
|
102
102
|
// package.json
|
|
103
103
|
var package_default = {
|
|
104
104
|
name: "@skill-map/cli",
|
|
105
|
-
version: "0.
|
|
105
|
+
version: "0.57.0",
|
|
106
106
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
107
107
|
license: "MIT",
|
|
108
108
|
type: "module",
|
|
@@ -1269,8 +1269,49 @@ function runActionProjections(actions, nodes, links, emitter) {
|
|
|
1269
1269
|
return { contributions, contributionErrors };
|
|
1270
1270
|
}
|
|
1271
1271
|
|
|
1272
|
+
// kernel/orchestrator/confidence-score.ts
|
|
1273
|
+
function foldConfidence(base, ops) {
|
|
1274
|
+
let running = base;
|
|
1275
|
+
for (const op of ops) {
|
|
1276
|
+
if (op.kind === "set") running = op.value;
|
|
1277
|
+
}
|
|
1278
|
+
for (const op of ops) {
|
|
1279
|
+
if (op.kind === "delta") running += op.value;
|
|
1280
|
+
}
|
|
1281
|
+
for (const op of ops) {
|
|
1282
|
+
if (op.kind === "floor") running = Math.max(running, op.value);
|
|
1283
|
+
}
|
|
1284
|
+
for (const op of ops) {
|
|
1285
|
+
if (op.kind === "ceil") running = Math.min(running, op.value);
|
|
1286
|
+
}
|
|
1287
|
+
return clamp01(running);
|
|
1288
|
+
}
|
|
1289
|
+
function clamp01(n) {
|
|
1290
|
+
if (n < 0) return 0;
|
|
1291
|
+
if (n > 1) return 1;
|
|
1292
|
+
return n;
|
|
1293
|
+
}
|
|
1294
|
+
function applyConfidenceAdjustments(adjustments) {
|
|
1295
|
+
if (adjustments.length === 0) return;
|
|
1296
|
+
const byLink = /* @__PURE__ */ new Map();
|
|
1297
|
+
for (const adj of adjustments) {
|
|
1298
|
+
const bucket = byLink.get(adj.link);
|
|
1299
|
+
if (bucket) bucket.push(adj);
|
|
1300
|
+
else byLink.set(adj.link, [adj]);
|
|
1301
|
+
}
|
|
1302
|
+
for (const [link, adjs] of byLink) {
|
|
1303
|
+
const ops = [...adjs].sort(compareAdjustments).map((a) => a.op);
|
|
1304
|
+
link.confidence = foldConfidence(link.confidence, ops);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
function compareAdjustments(a, b) {
|
|
1308
|
+
if (a.pluginId !== b.pluginId) return a.pluginId < b.pluginId ? -1 : 1;
|
|
1309
|
+
if (a.extensionId !== b.extensionId) return a.extensionId < b.extensionId ? -1 : 1;
|
|
1310
|
+
return 0;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1272
1313
|
// kernel/orchestrator/analyzers.ts
|
|
1273
|
-
async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions,
|
|
1314
|
+
async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, referenceablePaths, cwd, registeredActionIds, emitter, hookDispatcher, reservedNodePaths, brokenLinks, nameCollisions, signals, seedIssues = []) {
|
|
1274
1315
|
const issues = [...seedIssues];
|
|
1275
1316
|
const contributions = [];
|
|
1276
1317
|
const contributionErrors = [];
|
|
@@ -1281,7 +1322,16 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
1281
1322
|
expectedMdPath: o.expectedMdPath
|
|
1282
1323
|
}));
|
|
1283
1324
|
const scheduled = orderAnalyzersByPhase(analyzers);
|
|
1325
|
+
const scoreAdjustments = [];
|
|
1326
|
+
const scorableLinks = new Set(internalLinks);
|
|
1327
|
+
let scoresFolded = false;
|
|
1328
|
+
const foldScores = () => {
|
|
1329
|
+
if (scoresFolded) return;
|
|
1330
|
+
scoresFolded = true;
|
|
1331
|
+
applyConfidenceAdjustments(scoreAdjustments);
|
|
1332
|
+
};
|
|
1284
1333
|
for (const analyzer of scheduled) {
|
|
1334
|
+
if (analyzer.phase !== "score") foldScores();
|
|
1285
1335
|
const qualifiedId = qualifiedExtensionId(analyzer.pluginId, analyzer.id);
|
|
1286
1336
|
const declaredContributions = readDeclaredContributionRefs(analyzer);
|
|
1287
1337
|
const emitContribution = (nodePath, ref, payload) => {
|
|
@@ -1344,6 +1394,16 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
1344
1394
|
emittedAt: Date.now()
|
|
1345
1395
|
});
|
|
1346
1396
|
};
|
|
1397
|
+
const adjustConfidence = analyzer.phase === "score" ? (link, op) => {
|
|
1398
|
+
if (scorableLinks.has(link)) {
|
|
1399
|
+
scoreAdjustments.push({
|
|
1400
|
+
link,
|
|
1401
|
+
pluginId: analyzer.pluginId,
|
|
1402
|
+
extensionId: analyzer.id,
|
|
1403
|
+
op
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
} : void 0;
|
|
1347
1407
|
const emitted = await analyzer.evaluate({
|
|
1348
1408
|
nodes,
|
|
1349
1409
|
links: internalLinks,
|
|
@@ -1355,7 +1415,6 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
1355
1415
|
sidecarRoots,
|
|
1356
1416
|
annotationContributions,
|
|
1357
1417
|
viewContributions,
|
|
1358
|
-
orphanJobFiles,
|
|
1359
1418
|
// `issues` is the live accumulator, mutated by `issues.push(...)`
|
|
1360
1419
|
// below as each analyzer's emission lands. Late-phase analyzers
|
|
1361
1420
|
// (`core/issue-counter`) read it to compute cross-analyzer
|
|
@@ -1365,7 +1424,9 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
1365
1424
|
...cwd ? { cwd } : {},
|
|
1366
1425
|
...reservedNodePaths ? { reservedNodePaths } : {},
|
|
1367
1426
|
...brokenLinks ? { brokenLinks } : {},
|
|
1427
|
+
...nameCollisions && nameCollisions.size > 0 ? { nameCollisions } : {},
|
|
1368
1428
|
...signals && signals.length > 0 ? { signals } : {},
|
|
1429
|
+
...adjustConfidence ? { adjustConfidence } : {},
|
|
1369
1430
|
emitContribution
|
|
1370
1431
|
});
|
|
1371
1432
|
for (const issue of emitted) {
|
|
@@ -1376,13 +1437,16 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
1376
1437
|
emitter.emit(evt);
|
|
1377
1438
|
await hookDispatcher.dispatch("analyzer.completed", evt);
|
|
1378
1439
|
}
|
|
1379
|
-
|
|
1440
|
+
foldScores();
|
|
1441
|
+
return { issues, contributions, contributionErrors, linkScores: scoreAdjustments };
|
|
1380
1442
|
}
|
|
1381
1443
|
function orderAnalyzersByPhase(analyzers) {
|
|
1382
1444
|
return analyzers.slice().sort((a, b) => phaseRank(a) - phaseRank(b));
|
|
1383
1445
|
}
|
|
1384
1446
|
function phaseRank(a) {
|
|
1385
|
-
|
|
1447
|
+
if (a.phase === "score") return 0;
|
|
1448
|
+
if (a.phase === "aggregate") return 2;
|
|
1449
|
+
return 1;
|
|
1386
1450
|
}
|
|
1387
1451
|
function validateIssue(analyzer, issue, emitter) {
|
|
1388
1452
|
const severity = issue.severity;
|
|
@@ -1405,6 +1469,13 @@ function validateIssue(analyzer, issue, emitter) {
|
|
|
1405
1469
|
return { ...issue, analyzerId: issue.analyzerId || analyzer.id };
|
|
1406
1470
|
}
|
|
1407
1471
|
|
|
1472
|
+
// kernel/orchestrator/frontmatter-issue-ids.ts
|
|
1473
|
+
var FRONTMATTER_ISSUE_ANALYZERS = /* @__PURE__ */ new Set([
|
|
1474
|
+
"frontmatter-invalid",
|
|
1475
|
+
"frontmatter-malformed",
|
|
1476
|
+
"frontmatter-parse-error"
|
|
1477
|
+
]);
|
|
1478
|
+
|
|
1408
1479
|
// kernel/orchestrator/cache.ts
|
|
1409
1480
|
function indexPriorSnapshot(prior) {
|
|
1410
1481
|
const priorNodesByPath = /* @__PURE__ */ new Map();
|
|
@@ -1433,17 +1504,6 @@ function indexPriorLinks(links, priorNodePaths, byOriginating) {
|
|
|
1433
1504
|
else byOriginating.set(key, [link]);
|
|
1434
1505
|
}
|
|
1435
1506
|
}
|
|
1436
|
-
var FRONTMATTER_ISSUE_ANALYZERS = /* @__PURE__ */ new Set([
|
|
1437
|
-
"frontmatter-invalid",
|
|
1438
|
-
"frontmatter-malformed",
|
|
1439
|
-
// Audit L1: parser parse-error is emitted by
|
|
1440
|
-
// `buildFreshNodeAndValidateFrontmatter` from `raw.parseIssues`. The
|
|
1441
|
-
// raw.parseIssues only flows through the non-cache path; a cached
|
|
1442
|
-
// node skips the rebuild, so the prior issue MUST survive the
|
|
1443
|
-
// incremental scan or the warning silently disappears on a clean
|
|
1444
|
-
// re-scan of an unchanged file.
|
|
1445
|
-
"frontmatter-parse-error"
|
|
1446
|
-
]);
|
|
1447
1507
|
function indexPriorFrontmatterIssues(issues, byNode) {
|
|
1448
1508
|
for (const issue of issues) {
|
|
1449
1509
|
if (!FRONTMATTER_ISSUE_ANALYZERS.has(issue.analyzerId)) continue;
|
|
@@ -1641,15 +1701,47 @@ function readDirname(node) {
|
|
|
1641
1701
|
const base = pathPosix.basename(dir);
|
|
1642
1702
|
return base.length > 0 ? base : null;
|
|
1643
1703
|
}
|
|
1704
|
+
function collectNameCollisions(nodes, kindRegistry) {
|
|
1705
|
+
const byName = indexNameClaims(nodes, kindRegistry);
|
|
1706
|
+
const collisions = /* @__PURE__ */ new Map();
|
|
1707
|
+
for (const [name, claims] of byName) {
|
|
1708
|
+
const distinct = dedupeClaimsByPath(claims);
|
|
1709
|
+
if (distinct.length >= 2) collisions.set(name, distinct);
|
|
1710
|
+
}
|
|
1711
|
+
return collisions;
|
|
1712
|
+
}
|
|
1713
|
+
function indexNameClaims(nodes, kindRegistry) {
|
|
1714
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1715
|
+
for (const node of nodes) {
|
|
1716
|
+
const name = resolvableName(node, kindRegistry);
|
|
1717
|
+
if (name === null) continue;
|
|
1718
|
+
const bucket = byName.get(name) ?? [];
|
|
1719
|
+
bucket.push({ path: node.path, kind: node.kind });
|
|
1720
|
+
byName.set(name, bucket);
|
|
1721
|
+
}
|
|
1722
|
+
return byName;
|
|
1723
|
+
}
|
|
1724
|
+
function resolvableName(node, kindRegistry) {
|
|
1725
|
+
const descriptor = kindRegistry.get(`${node.provider}/${node.kind}`);
|
|
1726
|
+
if (!descriptor?.identifiers?.includes("frontmatter.name")) return null;
|
|
1727
|
+
const raw = node.frontmatter?.["name"];
|
|
1728
|
+
if (typeof raw !== "string" || raw.length === 0) return null;
|
|
1729
|
+
const normalised = normalizeTrigger(raw);
|
|
1730
|
+
return normalised.length > 0 ? normalised : null;
|
|
1731
|
+
}
|
|
1732
|
+
function dedupeClaimsByPath(claims) {
|
|
1733
|
+
return [...new Map(claims.map((c) => [c.path, c])).values()].sort(
|
|
1734
|
+
(a, b) => a.path.localeCompare(b.path)
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1644
1737
|
|
|
1645
1738
|
// kernel/orchestrator/lift-resolved-link-confidence.ts
|
|
1646
|
-
var RESERVED_TARGET_CONFIDENCE = 0.1;
|
|
1647
|
-
var BROKEN_TARGET_CONFIDENCE = 0.5;
|
|
1648
1739
|
function liftResolvedLinkConfidence(links, nodes, ctx) {
|
|
1649
|
-
if (
|
|
1740
|
+
if (links.length === 0) return;
|
|
1650
1741
|
const indexes = buildIndexes(nodes, ctx);
|
|
1651
1742
|
for (const link of links) {
|
|
1652
|
-
|
|
1743
|
+
link.confidence = 1;
|
|
1744
|
+
applyResolution(link, indexes, ctx);
|
|
1653
1745
|
}
|
|
1654
1746
|
}
|
|
1655
1747
|
function collectBrokenLinks(links, nodes, ctx) {
|
|
@@ -1663,15 +1755,8 @@ function collectBrokenLinks(links, nodes, ctx) {
|
|
|
1663
1755
|
}
|
|
1664
1756
|
function applyResolution(link, indexes, ctx) {
|
|
1665
1757
|
const resolution = resolve5(link, indexes, ctx);
|
|
1666
|
-
if (resolution === "none")
|
|
1667
|
-
if (isGenuinelyBroken(link, indexes)) {
|
|
1668
|
-
link.confidence = Math.min(link.confidence, BROKEN_TARGET_CONFIDENCE);
|
|
1669
|
-
}
|
|
1670
|
-
return;
|
|
1671
|
-
}
|
|
1758
|
+
if (resolution === "none") return;
|
|
1672
1759
|
link.resolvedTarget = resolution;
|
|
1673
|
-
if (indexes.nodeByPath.get(resolution)?.virtual) return;
|
|
1674
|
-
link.confidence = ctx.reservedNodePaths.has(resolution) ? RESERVED_TARGET_CONFIDENCE : 1;
|
|
1675
1760
|
}
|
|
1676
1761
|
function buildIndexes(nodes, ctx) {
|
|
1677
1762
|
const byPath2 = /* @__PURE__ */ new Set();
|
|
@@ -3143,6 +3228,7 @@ async function runScanInternal(_kernel, options) {
|
|
|
3143
3228
|
const postWalkCtx = buildPostWalkTransformCtx(exts.providers, walked.nodes, activeProviderId);
|
|
3144
3229
|
walked.internalLinks = applyPostWalkTransforms(walked.internalLinks, walked.nodes, postWalkCtx);
|
|
3145
3230
|
const brokenLinks = collectBrokenLinks(walked.internalLinks, walked.nodes, postWalkCtx);
|
|
3231
|
+
const nameCollisions = collectNameCollisions(walked.nodes, postWalkCtx.kindRegistry);
|
|
3146
3232
|
recomputeLinkCounts(walked.nodes, walked.internalLinks);
|
|
3147
3233
|
recomputeExternalRefsCount(walked.nodes, walked.externalLinks, walked.cachedPaths);
|
|
3148
3234
|
await dispatchExtractorCompleted(exts.extractors, emitter, hookDispatcher);
|
|
@@ -3157,7 +3243,6 @@ async function runScanInternal(_kernel, options) {
|
|
|
3157
3243
|
walked.sidecarRoots,
|
|
3158
3244
|
options.annotationContributions ?? [],
|
|
3159
3245
|
options.viewContributions ?? [],
|
|
3160
|
-
options.orphanJobFiles ?? [],
|
|
3161
3246
|
options.referenceablePaths,
|
|
3162
3247
|
options.cwd,
|
|
3163
3248
|
registeredActionIds,
|
|
@@ -3165,6 +3250,7 @@ async function runScanInternal(_kernel, options) {
|
|
|
3165
3250
|
hookDispatcher,
|
|
3166
3251
|
postWalkCtx.reservedNodePaths,
|
|
3167
3252
|
brokenLinks,
|
|
3253
|
+
nameCollisions,
|
|
3168
3254
|
walked.signals,
|
|
3169
3255
|
// Seed the accumulator with orchestrator-emitted frontmatter
|
|
3170
3256
|
// issues so the aggregate phase (`core/issue-counter`) counts
|
|
@@ -3187,7 +3273,7 @@ async function runScanInternal(_kernel, options) {
|
|
|
3187
3273
|
const scanCompletedEvent = makeEvent("scan.completed", { stats });
|
|
3188
3274
|
emitter.emit(scanCompletedEvent);
|
|
3189
3275
|
await hookDispatcher.dispatch("scan.completed", scanCompletedEvent);
|
|
3190
|
-
return buildScanReturn(walked, issues, renameOps, stats, options, setup);
|
|
3276
|
+
return buildScanReturn(walked, issues, renameOps, stats, options, setup, analyzerResult.linkScores);
|
|
3191
3277
|
}
|
|
3192
3278
|
function buildPostWalkTransformCtx(providers, nodes, activeProvider) {
|
|
3193
3279
|
const { kindRegistry, providerResolution, reservedNamesByProviderKind } = buildProviderIndexes(providers);
|
|
@@ -3315,7 +3401,7 @@ function buildScanStats(walked, issues, start) {
|
|
|
3315
3401
|
durationMs: Date.now() - start
|
|
3316
3402
|
};
|
|
3317
3403
|
}
|
|
3318
|
-
function buildScanReturn(walked, issues, renameOps, stats, options, setup) {
|
|
3404
|
+
function buildScanReturn(walked, issues, renameOps, stats, options, setup, linkScores) {
|
|
3319
3405
|
return {
|
|
3320
3406
|
result: {
|
|
3321
3407
|
schemaVersion: 1,
|
|
@@ -3337,6 +3423,7 @@ function buildScanReturn(walked, issues, renameOps, stats, options, setup) {
|
|
|
3337
3423
|
enrichments: walked.enrichments,
|
|
3338
3424
|
contributions: walked.contributions,
|
|
3339
3425
|
contributionErrors: walked.contributionErrors,
|
|
3426
|
+
linkScores,
|
|
3340
3427
|
freshlyRunTuples: walked.freshlyRunTuples
|
|
3341
3428
|
};
|
|
3342
3429
|
}
|
|
@@ -3766,4 +3853,4 @@ export {
|
|
|
3766
3853
|
runScanWithRenames
|
|
3767
3854
|
};
|
|
3768
3855
|
//# sourceMappingURL=index.js.map
|
|
3769
|
-
//# debugId=
|
|
3856
|
+
//# debugId=2d36a27f-0bac-51aa-8fb3-3b3e43eff4e8
|
package/dist/kernel/index.d.ts
CHANGED
|
@@ -816,6 +816,29 @@ type LinkKind = 'invokes' | 'references' | 'mentions' | 'supersedes' | 'points';
|
|
|
816
816
|
* enum check. Missing confidence defaults to `ConfidenceTier.MEDIUM`.
|
|
817
817
|
*/
|
|
818
818
|
type Confidence = number;
|
|
819
|
+
/**
|
|
820
|
+
* A single confidence-scoring operation a `score`-phase analyzer
|
|
821
|
+
* contributes via `ctx.adjustConfidence(link, op)`. The orchestrator
|
|
822
|
+
* folds every op on a link into the final `confidence` (see
|
|
823
|
+
* `orchestrator/confidence-score.ts` for the algebra):
|
|
824
|
+
* - `set` hard override (resolved → 1.0, reserved → 0.1)
|
|
825
|
+
* - `delta` additive, may be negative (third-party heuristics)
|
|
826
|
+
* - `ceil` upper cap, lowers only (broken → 0.5)
|
|
827
|
+
* - `floor` lower bound, raises only
|
|
828
|
+
*/
|
|
829
|
+
type TConfidenceOp = {
|
|
830
|
+
readonly kind: 'set';
|
|
831
|
+
readonly value: number;
|
|
832
|
+
} | {
|
|
833
|
+
readonly kind: 'delta';
|
|
834
|
+
readonly value: number;
|
|
835
|
+
} | {
|
|
836
|
+
readonly kind: 'ceil';
|
|
837
|
+
readonly value: number;
|
|
838
|
+
} | {
|
|
839
|
+
readonly kind: 'floor';
|
|
840
|
+
readonly value: number;
|
|
841
|
+
};
|
|
819
842
|
type Severity = 'error' | 'warn' | 'info';
|
|
820
843
|
type Stability = 'experimental' | 'stable' | 'deprecated';
|
|
821
844
|
/**
|
|
@@ -1132,11 +1155,10 @@ interface Signal {
|
|
|
1132
1155
|
* raw extractor emissions (before the resolver runs). When
|
|
1133
1156
|
* `outcome === 'materialised'`, `winnerIndex` points into `candidates[]`
|
|
1134
1157
|
* of the candidate the resolver chose; a corresponding `Link` was added
|
|
1135
|
-
* to the graph. When `outcome === 'rejected'`,
|
|
1136
|
-
*
|
|
1137
|
-
*
|
|
1138
|
-
*
|
|
1139
|
-
* surface losers as `warn` issues. Mirrors
|
|
1158
|
+
* to the graph. When `outcome === 'rejected'`, `rejectedBy` is set and
|
|
1159
|
+
* no Link materialised. Both materialised and rejected Signals remain on
|
|
1160
|
+
* `IAnalyzerContext.signals` so the `core/extractor-collision` analyzer
|
|
1161
|
+
* can surface losers as `warn` issues. Mirrors
|
|
1140
1162
|
* `signal.schema.json#/properties/resolution`.
|
|
1141
1163
|
*/
|
|
1142
1164
|
resolution?: ISignalResolution;
|
|
@@ -1162,24 +1184,6 @@ interface ISignalResolution {
|
|
|
1162
1184
|
extractorId: string;
|
|
1163
1185
|
reason: 'kind-priority' | 'higher-confidence' | 'longer-range' | 'earlier-declaration';
|
|
1164
1186
|
};
|
|
1165
|
-
/**
|
|
1166
|
-
* Phase 4+ stub: populated when every candidate of this Signal came from
|
|
1167
|
-
* an extractor the operator disabled via
|
|
1168
|
-
* `plugins.<id>.extensions.<extId>.enabled`. Today the resolver never
|
|
1169
|
-
* sets this; documented so analyzer surfaces can be built when the filter
|
|
1170
|
-
* lands.
|
|
1171
|
-
*/
|
|
1172
|
-
extractorDisabled?: {
|
|
1173
|
-
extractorId: string;
|
|
1174
|
-
};
|
|
1175
|
-
/**
|
|
1176
|
-
* Phase 4+ stub: populated when every candidate's `confidence` fell below
|
|
1177
|
-
* the configured floor. Today the resolver materialises every Signal that
|
|
1178
|
-
* survives overlap regardless of confidence.
|
|
1179
|
-
*/
|
|
1180
|
-
belowFloor?: {
|
|
1181
|
-
threshold: number;
|
|
1182
|
-
};
|
|
1183
1187
|
}
|
|
1184
1188
|
interface IssueFix {
|
|
1185
1189
|
summary?: string;
|
|
@@ -1679,6 +1683,55 @@ declare function makePluginStore(opts: {
|
|
|
1679
1683
|
persistDedicated?: IDedicatedStorePersist;
|
|
1680
1684
|
}): TPluginStore | undefined;
|
|
1681
1685
|
|
|
1686
|
+
/**
|
|
1687
|
+
* Combination algebra for plugin-contributed link-confidence adjustments.
|
|
1688
|
+
*
|
|
1689
|
+
* Confidence ([0,1]) starts at the kernel's 1.0 baseline (seeded on every
|
|
1690
|
+
* link by `liftResolvedLinkConfidence`). `score`-phase analyzers then
|
|
1691
|
+
* contribute attributed operations via `ctx.adjustConfidence(link, op)`;
|
|
1692
|
+
* the orchestrator buffers them and folds all ops for a link into a final
|
|
1693
|
+
* value with `foldConfidence`. The kernel dogfoods this exact API through
|
|
1694
|
+
* two built-in score-phase detectors, each co-locating its penalty op
|
|
1695
|
+
* with the finding it reports: `core/name-reserved`
|
|
1696
|
+
* (reserved → `delta -0.9` → 0.1), `core/reference-broken`
|
|
1697
|
+
* (broken → `delta -0.5` → 0.5). A clean-resolved or untouched link folds
|
|
1698
|
+
* to `clamp(base)` and keeps the 1.0 baseline.
|
|
1699
|
+
*
|
|
1700
|
+
* The fold is deterministic and order-independent across the four
|
|
1701
|
+
* buckets (set / delta / floor / ceil are each commutative):
|
|
1702
|
+
* 1. base = the extractor-emitted confidence.
|
|
1703
|
+
* 2. `set`: a hard override. When more than one `set` lands on a link
|
|
1704
|
+
* the LAST in the caller's canonical order wins (the caller pre-
|
|
1705
|
+
* sorts ops by `(pluginId, extensionId)` so the winner is stable);
|
|
1706
|
+
* a single `set` simply replaces the base.
|
|
1707
|
+
* 3. `delta`: additive (may be negative), summed.
|
|
1708
|
+
* 4. `floor`: raise to at least the value (`max`).
|
|
1709
|
+
* 5. `ceil`: lower to at most the value (`min`) — today's broken cap.
|
|
1710
|
+
* Applied AFTER floor so a cap dominates a floor/ceil collision.
|
|
1711
|
+
* 6. clamp to [0,1] ONCE at the end, so opposing deltas round-trip
|
|
1712
|
+
* (e.g. `-0.4` then `+0.4` returns to base, never clipped midway).
|
|
1713
|
+
*
|
|
1714
|
+
* A link no scorer touches folds to `clamp(base)` (the kernel's 1.0
|
|
1715
|
+
* baseline), identical to a clean-resolved link. With only the built-in
|
|
1716
|
+
* detectors active a link is at most reserved OR broken (mutually
|
|
1717
|
+
* exclusive), so it gets at most one penalty delta and folds to 0.1 / 0.5
|
|
1718
|
+
* respectively; a clean link keeps the 1.0 base. Third-party scorers layer
|
|
1719
|
+
* additional ops on top, summed deterministically before the single clamp.
|
|
1720
|
+
*/
|
|
1721
|
+
|
|
1722
|
+
/**
|
|
1723
|
+
* One attributed adjustment, buffered by the orchestrator as a scorer
|
|
1724
|
+
* calls `adjustConfidence`. `link` is held by object identity (the same
|
|
1725
|
+
* link objects flow through the post-walk pipeline). Persisted to
|
|
1726
|
+
* `scan_link_scores` for the "why is this link at X?" audit trail.
|
|
1727
|
+
*/
|
|
1728
|
+
interface IConfidenceAdjustment {
|
|
1729
|
+
readonly link: Link;
|
|
1730
|
+
readonly pluginId: string;
|
|
1731
|
+
readonly extensionId: string;
|
|
1732
|
+
readonly op: TConfidenceOp;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1682
1735
|
/**
|
|
1683
1736
|
* Row-level filter for `port.scans.findNodes(...)` (driven by
|
|
1684
1737
|
* `sm list`'s flags). All fields are optional, an empty filter
|
|
@@ -1727,11 +1780,11 @@ interface INodeCounts {
|
|
|
1727
1780
|
issues: number;
|
|
1728
1781
|
}
|
|
1729
1782
|
/**
|
|
1730
|
-
* Lightweight option bag for `port.scans.persist`. Mirrors the
|
|
1731
|
-
*
|
|
1732
|
-
*
|
|
1733
|
-
* implementation is a one-line delegation
|
|
1734
|
-
*
|
|
1783
|
+
* Lightweight option bag for `port.scans.persist`. Mirrors the optional
|
|
1784
|
+
* inputs of the `persistScanResult(db, result, inputs)` free function
|
|
1785
|
+
* (`IPersistScanInputs` in `kernel/adapters/sqlite/scan-persistence.ts`),
|
|
1786
|
+
* so the adapter implementation is a one-line delegation; the named-bag
|
|
1787
|
+
* shape lets new optional inputs land without breaking callers.
|
|
1735
1788
|
*/
|
|
1736
1789
|
interface IPersistOptions {
|
|
1737
1790
|
renameOps?: RenameOp[];
|
|
@@ -1747,6 +1800,18 @@ interface IPersistOptions {
|
|
|
1747
1800
|
* scan clears any stale rows). Surfaced by `sm plugins doctor`.
|
|
1748
1801
|
*/
|
|
1749
1802
|
contributionErrors?: IContributionErrorRecord[];
|
|
1803
|
+
/**
|
|
1804
|
+
* Per-op confidence-attribution audit trail for `scan_link_scores`.
|
|
1805
|
+
* One entry per attributed `ctx.adjustConfidence(link, op)` call a
|
|
1806
|
+
* `score`-phase analyzer buffered this scan; the orchestrator already
|
|
1807
|
+
* folded them into `link.confidence`, so these rows are the attribution
|
|
1808
|
+
* (which plugin / extension / op moved a given link, plus the folded
|
|
1809
|
+
* `result_confidence`). Plain REPLACE-ALL into `scan_link_scores`
|
|
1810
|
+
* (delete all, then insert), the same posture as `scan_issues`. Empty /
|
|
1811
|
+
* absent wipes the table (a scan whose scorers touched nothing clears
|
|
1812
|
+
* any stale rows).
|
|
1813
|
+
*/
|
|
1814
|
+
linkScores?: IConfidenceAdjustment[];
|
|
1750
1815
|
/**
|
|
1751
1816
|
* Phase 3 / View contribution system, active runtime catalog of
|
|
1752
1817
|
* registered view contributions, keyed by qualified id
|
|
@@ -2983,18 +3048,6 @@ interface IAnalyzerContext {
|
|
|
2983
3048
|
* runScan sites that never wired the catalog through).
|
|
2984
3049
|
*/
|
|
2985
3050
|
viewContributions?: readonly IRegisteredViewContribution[];
|
|
2986
|
-
/**
|
|
2987
|
-
* Absolute paths of `*.md` files under the project's
|
|
2988
|
-
* `.skill-map/jobs/` that no `state_jobs.filePath` references, the
|
|
2989
|
-
* built-in `core/job-file-orphan` analyzer projects each as a `warn`
|
|
2990
|
-
* issue. Pre-computed by the driving adapter (CLI / BFF) inside its
|
|
2991
|
-
* already-open storage transaction (mirrors the `orphanSidecars`
|
|
2992
|
-
* pattern: detection lives outside the analyzer, the analyzer only
|
|
2993
|
-
* projects). Absent (or empty) when the caller does not maintain a
|
|
2994
|
-
* jobs directory, when the storage path is unavailable, or when no
|
|
2995
|
-
* orphan files exist. Treat as read-only.
|
|
2996
|
-
*/
|
|
2997
|
-
orphanJobFiles?: readonly string[];
|
|
2998
3051
|
/**
|
|
2999
3052
|
* Issues emitted by analyzers that already ran in the current pass.
|
|
3000
3053
|
* Lets a late-phase analyzer (`core/issue-counter`) compute
|
|
@@ -3051,6 +3104,23 @@ interface IAnalyzerContext {
|
|
|
3051
3104
|
* that never wired the field through, the rule then emits nothing.
|
|
3052
3105
|
*/
|
|
3053
3106
|
brokenLinks?: ReadonlySet<Link>;
|
|
3107
|
+
/**
|
|
3108
|
+
* Names claimed by two or more distinct nodes, keyed by the normalised
|
|
3109
|
+
* name. A node contributes only when its kind declares `frontmatter.name`
|
|
3110
|
+
* as a resolution identifier (so plain `core/markdown` nodes, addressed
|
|
3111
|
+
* by path, never collide) and it carries a non-empty `name`. Names that
|
|
3112
|
+
* normalise to the same value (e.g. `Deploy` / `deploy`) collide, mirroring
|
|
3113
|
+
* how the resolver keys on the normalised identifier. Computed once per
|
|
3114
|
+
* scan by the orchestrator from the same kind registry the resolver uses,
|
|
3115
|
+
* so analyzers project it without re-deriving (the `brokenLinks` /
|
|
3116
|
+
* `reservedNodePaths` precompute-and-project pattern). The single consumer
|
|
3117
|
+
* is `core/name-collision`, which emits one `error` per entry. Absent for
|
|
3118
|
+
* legacy callers that never wired the field through.
|
|
3119
|
+
*/
|
|
3120
|
+
nameCollisions?: ReadonlyMap<string, readonly {
|
|
3121
|
+
readonly path: string;
|
|
3122
|
+
readonly kind: string;
|
|
3123
|
+
}[]>;
|
|
3054
3124
|
/**
|
|
3055
3125
|
* Absolute path of the scan's project root (cwd of the invocation).
|
|
3056
3126
|
* Threaded into the analyzer pass so an analyzer that needs to
|
|
@@ -3095,6 +3165,17 @@ interface IAnalyzerContext {
|
|
|
3095
3165
|
* emissions (`scan_contributions`).
|
|
3096
3166
|
*/
|
|
3097
3167
|
emitContribution<C extends IViewContribution>(nodePath: string, ref: C, payload: SlotPayload<C['slot']>): void;
|
|
3168
|
+
/**
|
|
3169
|
+
* Contribute a confidence adjustment to a link. Usable ONLY from a
|
|
3170
|
+
* `score`-phase analyzer; the orchestrator records it attributed to
|
|
3171
|
+
* the calling extension (`pluginId` / `extensionId`, like
|
|
3172
|
+
* `emitContribution`) and folds every op on a link into the final
|
|
3173
|
+
* `link.confidence` before the `detect` phase. `link` must be one of
|
|
3174
|
+
* `ctx.links` (matched by object identity). Present ONLY in the
|
|
3175
|
+
* `score` phase (absent for `detect` / `aggregate` and legacy
|
|
3176
|
+
* callers), mirroring the other orchestrator-injected ctx fields.
|
|
3177
|
+
*/
|
|
3178
|
+
adjustConfidence?(link: Link, op: TConfidenceOp): void;
|
|
3098
3179
|
}
|
|
3099
3180
|
interface IAnalyzer extends IExtensionBase {
|
|
3100
3181
|
/** Discriminant injected by the loader from the folder structure. */
|
|
@@ -3121,22 +3202,31 @@ interface IAnalyzer extends IExtensionBase {
|
|
|
3121
3202
|
* Execution phase. Drives the order the orchestrator schedules
|
|
3122
3203
|
* analyzers in:
|
|
3123
3204
|
*
|
|
3205
|
+
* - `'score'`, runs strictly BEFORE every `detect`-phase analyzer.
|
|
3206
|
+
* The ONLY phase permitted to write: it adjusts link confidence
|
|
3207
|
+
* via `ctx.adjustConfidence(link, op)`. The orchestrator folds
|
|
3208
|
+
* every score-phase op into `link.confidence` before the read-
|
|
3209
|
+
* only `detect` phase runs, so the `detect` analyzers see the
|
|
3210
|
+
* final value. The kernel seeds the 1.0 confidence baseline on
|
|
3211
|
+
* every link, then dogfoods this phase via two built-in score-phase
|
|
3212
|
+
* detectors (`core/name-reserved`, `core/reference-broken`), each
|
|
3213
|
+
* co-locating its penalty `delta` with the finding it reports.
|
|
3124
3214
|
* - `'detect'` (default), the main pass. Walks nodes / links and
|
|
3125
|
-
* emits its own findings. Most analyzers live here.
|
|
3215
|
+
* emits its own findings. Most analyzers live here. Read-only.
|
|
3126
3216
|
* - `'aggregate'`, runs strictly AFTER every `detect`-phase
|
|
3127
3217
|
* analyzer has finished. The orchestrator passes the full
|
|
3128
3218
|
* issue accumulator on `ctx.accumulatedIssues`, so an
|
|
3129
3219
|
* aggregator can compute cross-analyzer summaries (per-node
|
|
3130
3220
|
* severity totals, etc.) without re-reading the persisted DB.
|
|
3131
3221
|
* Aggregators emit contributions; emitting issues is allowed
|
|
3132
|
-
* but uncommon.
|
|
3222
|
+
* but uncommon. Read-only.
|
|
3133
3223
|
*
|
|
3134
|
-
*
|
|
3135
|
-
*
|
|
3136
|
-
*
|
|
3137
|
-
*
|
|
3224
|
+
* Phase scheduling is the clean alternative to ordering analyzers by
|
|
3225
|
+
* hand in the built-ins registry: filesystem-sorted generators can
|
|
3226
|
+
* keep their alphabetical output, the orchestrator applies the phase
|
|
3227
|
+
* sort (`score` < `detect` < `aggregate`) at run-time.
|
|
3138
3228
|
*/
|
|
3139
|
-
phase?: 'detect' | 'aggregate';
|
|
3229
|
+
phase?: 'score' | 'detect' | 'aggregate';
|
|
3140
3230
|
evaluate(ctx: IAnalyzerContext): Issue[] | Promise<Issue[]>;
|
|
3141
3231
|
}
|
|
3142
3232
|
|
|
@@ -3921,21 +4011,6 @@ interface RunScanOptions {
|
|
|
3921
4011
|
* mock without spinning up a DB.
|
|
3922
4012
|
*/
|
|
3923
4013
|
pluginStores?: ReadonlyMap<string, TPluginStore>;
|
|
3924
|
-
/**
|
|
3925
|
-
* Pre-computed absolute paths of orphan job MD files (files under
|
|
3926
|
-
* `.skill-map/jobs/` whose absolute path appears nowhere in
|
|
3927
|
-
* `state_jobs.filePath`). Threaded into the rule pass so the
|
|
3928
|
-
* built-in `core/job-file-orphan` rule can project each as a `warn`
|
|
3929
|
-
* issue without the kernel reaching for the storage port or doing
|
|
3930
|
-
* its own FS walk. The driving adapter (CLI, BFF) computes this
|
|
3931
|
-
* inside its already-open storage transaction via
|
|
3932
|
-
* `findOrphanJobFiles(jobsDir, await port.jobs.listReferencedFilePaths())`
|
|
3933
|
-
* mirrors the `orphanSidecars` model where detection lives
|
|
3934
|
-
* outside the rule and the rule only projects. Absent / empty when
|
|
3935
|
-
* the caller has no jobs context (out-of-band tests, fresh DB,
|
|
3936
|
-
* `--no-built-ins`).
|
|
3937
|
-
*/
|
|
3938
|
-
orphanJobFiles?: readonly string[];
|
|
3939
4014
|
/**
|
|
3940
4015
|
* Side set of absolute file paths the operator opted into for
|
|
3941
4016
|
* link-validation purposes via `scan.referencePaths`. Threaded
|
|
@@ -4019,6 +4094,7 @@ declare function runScanWithRenames(_kernel: Kernel, options: RunScanOptions): P
|
|
|
4019
4094
|
enrichments: IEnrichmentRecord[];
|
|
4020
4095
|
contributions: IContributionRecord[];
|
|
4021
4096
|
contributionErrors: IContributionErrorRecord[];
|
|
4097
|
+
linkScores: IConfidenceAdjustment[];
|
|
4022
4098
|
freshlyRunTuples: ReadonlySet<string>;
|
|
4023
4099
|
}>;
|
|
4024
4100
|
declare function runScan(_kernel: Kernel, options: RunScanOptions): Promise<ScanResult>;
|
|
@@ -4217,7 +4293,7 @@ declare function createChokidarWatcher(opts: ICreateFsWatcherOptions): IFsWatche
|
|
|
4217
4293
|
* a reason narrowing what diverged.
|
|
4218
4294
|
*
|
|
4219
4295
|
* - **Link**: `(source, target, kind, normalizedTrigger ?? '')`. This
|
|
4220
|
-
* mirrors the link-conflict rule and `sm show` aggregation,
|
|
4296
|
+
* mirrors the link-kind-conflict rule and `sm show` aggregation,
|
|
4221
4297
|
* two links with identical endpoints, kind, and (optional) trigger
|
|
4222
4298
|
* are the same link, even if emitted by different extractors. The
|
|
4223
4299
|
* `sources[]` union and confidence are NOT part of identity; they
|