@skill-map/cli 0.56.0 → 0.58.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/tutorial/sm-tutorial/references/part-plugins.md +1 -1
- package/dist/cli.js +1073 -1252
- package/dist/index.js +128 -49
- package/dist/kernel/index.d.ts +144 -68
- package/dist/kernel/index.js +128 -49
- package/dist/migrations/001_initial.sql +46 -1
- package/dist/ui/chunk-DPLNM4UD.js +2 -0
- package/dist/ui/chunk-G3ZVF4TM.js +1 -0
- package/dist/ui/{chunk-HDKR6XHG.js → chunk-KJZQIUZA.js} +1 -1
- package/dist/ui/chunk-QVNZN5F7.js +1843 -0
- package/dist/ui/{chunk-GHOVZAAV.js → chunk-SXXFDP6V.js} +1 -1
- package/dist/ui/chunk-WB7FKIBP.js +1 -0
- package/dist/ui/chunk-ZJCU4CXS.js +3 -0
- package/dist/ui/index.html +2 -2
- package/dist/ui/{main-PL3BEVQI.js → main-KOFMNAVE.js} +3 -3
- package/dist/ui/{styles-RHEEXRHQ.css → styles-I4ULXD3V.css} +1 -1
- package/migrations/001_initial.sql +46 -1
- package/package.json +2 -2
- package/dist/ui/chunk-4ITL7E6U.js +0 -1
- package/dist/ui/chunk-DWBJCNC7.js +0 -2
- package/dist/ui/chunk-RS3ANRT5.js +0 -1
- package/dist/ui/chunk-VUNP5KNI.js +0 -3
- package/dist/ui/chunk-W3Z3CZL4.js +0 -1844
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// cli/entry.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]="7e4fdbab-f037-5030-b67a-732bb7ee46fd")}catch(e){}}();
|
|
4
4
|
import { existsSync as existsSync33 } from "fs";
|
|
5
5
|
import { Builtins, Cli as Cli2 } from "clipanion";
|
|
6
6
|
|
|
@@ -250,7 +250,7 @@ function bucketByKind(kind, instance, bag) {
|
|
|
250
250
|
// package.json
|
|
251
251
|
var package_default = {
|
|
252
252
|
name: "@skill-map/cli",
|
|
253
|
-
version: "0.
|
|
253
|
+
version: "0.58.0",
|
|
254
254
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
255
255
|
license: "MIT",
|
|
256
256
|
type: "module",
|
|
@@ -1442,71 +1442,12 @@ var coreMarkdownProvider = {
|
|
|
1442
1442
|
}
|
|
1443
1443
|
};
|
|
1444
1444
|
|
|
1445
|
-
// plugins/core/extractors/annotations/index.ts
|
|
1446
|
-
var ID4 = "annotations";
|
|
1447
|
-
var annotationsExtractor = {
|
|
1448
|
-
id: ID4,
|
|
1449
|
-
pluginId: CORE_PLUGIN_ID,
|
|
1450
|
-
kind: "extractor",
|
|
1451
|
-
description: "Turns the `supersedes` and `supersededBy` entries from a node's `.sm` sidecar into arrows between nodes in the graph. Example: `supersededBy: v1-skill.md` in a `.sm` sidecar draws an arrow to `v1-skill.md`.",
|
|
1452
|
-
scope: "frontmatter",
|
|
1453
|
-
extract(ctx) {
|
|
1454
|
-
const sourcePath = ctx.node.path;
|
|
1455
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1456
|
-
function emit(source, target, fieldPath) {
|
|
1457
|
-
const key = `${source} ${target}`;
|
|
1458
|
-
if (seen.has(key)) return;
|
|
1459
|
-
seen.add(key);
|
|
1460
|
-
ctx.emitSignal({
|
|
1461
|
-
source,
|
|
1462
|
-
scope: "sidecar",
|
|
1463
|
-
fieldPath,
|
|
1464
|
-
raw: target,
|
|
1465
|
-
candidates: [
|
|
1466
|
-
{
|
|
1467
|
-
extractorId: ID4,
|
|
1468
|
-
kind: "supersedes",
|
|
1469
|
-
target,
|
|
1470
|
-
confidence: 1,
|
|
1471
|
-
rationale: "structured sidecar annotation"
|
|
1472
|
-
}
|
|
1473
|
-
]
|
|
1474
|
-
});
|
|
1475
|
-
}
|
|
1476
|
-
const ann = pickAnnotations(ctx.node);
|
|
1477
|
-
if (ann) processBlock(ann, sourcePath, emit);
|
|
1478
|
-
}
|
|
1479
|
-
};
|
|
1480
|
-
function processBlock(block, sourcePath, emit) {
|
|
1481
|
-
const supersedes = stringArray(block["supersedes"]);
|
|
1482
|
-
for (let i = 0; i < supersedes.length; i += 1) {
|
|
1483
|
-
emit(sourcePath, supersedes[i], ["annotations", "supersedes", String(i)]);
|
|
1484
|
-
}
|
|
1485
|
-
const supersededBy = block["supersededBy"];
|
|
1486
|
-
if (typeof supersededBy === "string" && supersededBy.length > 0) {
|
|
1487
|
-
emit(supersededBy, sourcePath, ["annotations", "supersededBy"]);
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
function pickAnnotations(node) {
|
|
1491
|
-
const sidecar = node.sidecar;
|
|
1492
|
-
if (!sidecar || sidecar.present !== true) return null;
|
|
1493
|
-
const ann = sidecar.annotations;
|
|
1494
|
-
if (ann && typeof ann === "object" && !Array.isArray(ann)) {
|
|
1495
|
-
return ann;
|
|
1496
|
-
}
|
|
1497
|
-
return null;
|
|
1498
|
-
}
|
|
1499
|
-
function stringArray(value) {
|
|
1500
|
-
if (!Array.isArray(value)) return [];
|
|
1501
|
-
return value.filter((v) => typeof v === "string" && v.length > 0);
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
1445
|
// plugins/core/extractors/backtick-path/index.ts
|
|
1505
1446
|
import { posix as pathPosix2 } from "path";
|
|
1506
|
-
var
|
|
1447
|
+
var ID4 = "backtick-path";
|
|
1507
1448
|
var PATH_RE = /(?<![\w/:.-])(?:\.{1,2}\/)?[\w][\w.-]*(?:\/[\w.-]+)*\.md\b(?![\w/])/g;
|
|
1508
1449
|
var backtickPathExtractor = {
|
|
1509
|
-
id:
|
|
1450
|
+
id: ID4,
|
|
1510
1451
|
pluginId: CORE_PLUGIN_ID,
|
|
1511
1452
|
kind: "extractor",
|
|
1512
1453
|
description: "Turns relative .md paths written inside code spans and fenced blocks into arrows between nodes in the graph. Example: a backticked `references/rules.md` path draws an arrow to that file.",
|
|
@@ -1531,7 +1472,7 @@ var backtickPathExtractor = {
|
|
|
1531
1472
|
raw: original,
|
|
1532
1473
|
candidates: [
|
|
1533
1474
|
{
|
|
1534
|
-
extractorId:
|
|
1475
|
+
extractorId: ID4,
|
|
1535
1476
|
kind: "points",
|
|
1536
1477
|
target: resolved,
|
|
1537
1478
|
// 0.85: a strong file signal with one degree of inference,
|
|
@@ -1560,7 +1501,7 @@ function resolveTarget(sourceDir, raw) {
|
|
|
1560
1501
|
}
|
|
1561
1502
|
|
|
1562
1503
|
// plugins/core/extractors/external-url-counter/index.ts
|
|
1563
|
-
var
|
|
1504
|
+
var ID5 = "external-url-counter";
|
|
1564
1505
|
var count2 = {
|
|
1565
1506
|
slot: "card.footer.left",
|
|
1566
1507
|
icon: "pi-link",
|
|
@@ -1580,7 +1521,7 @@ var settings = {
|
|
|
1580
1521
|
var URL_RE = /https?:\/\/[^\s<>"'`)\]]+/g;
|
|
1581
1522
|
var TRAILING_PUNCT = /[.,;:!?]+$/;
|
|
1582
1523
|
var externalUrlCounterExtractor = {
|
|
1583
|
-
id:
|
|
1524
|
+
id: ID5,
|
|
1584
1525
|
pluginId: CORE_PLUGIN_ID,
|
|
1585
1526
|
kind: "extractor",
|
|
1586
1527
|
description: "Counts the distinct external URLs in a node's body and shows the count on the card. Example: a body linking `https://example.com` and `https://docs.rs` shows a count of 2.",
|
|
@@ -1632,7 +1573,7 @@ var externalUrlCounterExtractor = {
|
|
|
1632
1573
|
raw: original,
|
|
1633
1574
|
candidates: [
|
|
1634
1575
|
{
|
|
1635
|
-
extractorId:
|
|
1576
|
+
extractorId: ID5,
|
|
1636
1577
|
kind: "references",
|
|
1637
1578
|
target: normalized.href,
|
|
1638
1579
|
confidence: 0.3,
|
|
@@ -1674,11 +1615,11 @@ function normalizeUrl(raw) {
|
|
|
1674
1615
|
|
|
1675
1616
|
// plugins/core/extractors/markdown-link/index.ts
|
|
1676
1617
|
import { posix as pathPosix3 } from "path";
|
|
1677
|
-
var
|
|
1618
|
+
var ID6 = "markdown-link";
|
|
1678
1619
|
var LINK_RE = /(?<!!)\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
1679
1620
|
var URL_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
|
|
1680
1621
|
var markdownLinkExtractor = {
|
|
1681
|
-
id:
|
|
1622
|
+
id: ID6,
|
|
1682
1623
|
pluginId: CORE_PLUGIN_ID,
|
|
1683
1624
|
kind: "extractor",
|
|
1684
1625
|
description: "Turns markdown links (`[text](path)`) in a node's body into arrows between nodes in the graph. Example: `[the guide](docs/guide.md)` draws an arrow to `docs/guide.md`.",
|
|
@@ -1703,7 +1644,7 @@ var markdownLinkExtractor = {
|
|
|
1703
1644
|
raw: match[0],
|
|
1704
1645
|
candidates: [
|
|
1705
1646
|
{
|
|
1706
|
-
extractorId:
|
|
1647
|
+
extractorId: ID6,
|
|
1707
1648
|
kind: "references",
|
|
1708
1649
|
target: resolved,
|
|
1709
1650
|
// 0.95: the `[text](path)` syntax is unambiguous (the spec's
|
|
@@ -1738,10 +1679,10 @@ function resolveTarget2(sourceDir, raw) {
|
|
|
1738
1679
|
}
|
|
1739
1680
|
|
|
1740
1681
|
// plugins/core/extractors/mcp-tools/index.ts
|
|
1741
|
-
var
|
|
1682
|
+
var ID7 = "mcp-tools";
|
|
1742
1683
|
var MCP_PATTERN = /^mcp__([a-z0-9][a-z0-9_-]*)__[a-z0-9_-]+$/i;
|
|
1743
1684
|
var mcpToolsExtractor = {
|
|
1744
|
-
id:
|
|
1685
|
+
id: ID7,
|
|
1745
1686
|
pluginId: CORE_PLUGIN_ID,
|
|
1746
1687
|
kind: "extractor",
|
|
1747
1688
|
description: "Turns `tools: [mcp__<server>__<tool>]` entries in a node's frontmatter into an MCP node per unique server and an arrow from the source to each one. Example: `tools: [mcp__github__create_pr]` adds an `mcp://github` node and an arrow to it.",
|
|
@@ -1772,7 +1713,7 @@ var mcpToolsExtractor = {
|
|
|
1772
1713
|
raw: `mcp__${server}__*`,
|
|
1773
1714
|
candidates: [
|
|
1774
1715
|
{
|
|
1775
|
-
extractorId:
|
|
1716
|
+
extractorId: ID7,
|
|
1776
1717
|
kind: "references",
|
|
1777
1718
|
target: mcpPath,
|
|
1778
1719
|
confidence: 0.85,
|
|
@@ -1815,16 +1756,25 @@ function applyAjvFormats(ajv) {
|
|
|
1815
1756
|
addFormats(ajv);
|
|
1816
1757
|
}
|
|
1817
1758
|
|
|
1759
|
+
// kernel/util/finding-format.ts
|
|
1760
|
+
function formatFinding(parts) {
|
|
1761
|
+
const head = parts.subject ? `\`${parts.subject}\`:
|
|
1762
|
+
` : "";
|
|
1763
|
+
const loc = parts.lines && parts.lines.length > 0 ? `L${parts.lines.join(", ")}: ` : "";
|
|
1764
|
+
return `${head}${loc}${parts.body}`;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1818
1767
|
// plugins/core/analyzers/annotation-field-unknown/text.ts
|
|
1819
1768
|
var ANNOTATION_FIELD_UNKNOWN_TEXTS = {
|
|
1820
|
-
//
|
|
1821
|
-
//
|
|
1769
|
+
// Diagnosis bodies (`<what>; <why>`). The shared `formatFinding` helper
|
|
1770
|
+
// owns the subject (the offending key, built before the call); the
|
|
1771
|
+
// affected node is the finding's own node, so its path never appears.
|
|
1822
1772
|
/** Key inside `annotations:` is not in the curated catalog. */
|
|
1823
|
-
unknownAnnotationKey: "Unknown sidecar key
|
|
1773
|
+
unknownAnnotationKey: "Unknown sidecar key; not in the annotations catalog",
|
|
1824
1774
|
/** Top-level key is neither reserved, nor a registered plugin namespace, nor a registered root key. */
|
|
1825
|
-
unknownRootKey: "Unknown
|
|
1775
|
+
unknownRootKey: "Unknown top-level sidecar key; not a reserved block, plugin namespace, or root contribution",
|
|
1826
1776
|
/** Value under a registered plugin namespace fails the contributed schema. */
|
|
1827
|
-
pluginNamespaceInvalid: "Sidecar block
|
|
1777
|
+
pluginNamespaceInvalid: "Sidecar block fails the plugin schema; {{errors}}",
|
|
1828
1778
|
// Tooltips for the per-node view-contribution badges. Singular vs
|
|
1829
1779
|
// plural keeps the count grammar correct without a sub-template.
|
|
1830
1780
|
alertTooltipSingle: "This node has 1 unknown field in its sidecar. Open the inspector for details.",
|
|
@@ -1832,10 +1782,10 @@ var ANNOTATION_FIELD_UNKNOWN_TEXTS = {
|
|
|
1832
1782
|
};
|
|
1833
1783
|
|
|
1834
1784
|
// plugins/core/analyzers/annotation-field-unknown/index.ts
|
|
1835
|
-
var
|
|
1785
|
+
var ID8 = "annotation-field-unknown";
|
|
1836
1786
|
var RESERVED_ROOT_BLOCKS = /* @__PURE__ */ new Set(["identity", "annotations", "settings", "audit"]);
|
|
1837
1787
|
var annotationFieldUnknownAnalyzer = {
|
|
1838
|
-
id:
|
|
1788
|
+
id: ID8,
|
|
1839
1789
|
pluginId: CORE_PLUGIN_ID,
|
|
1840
1790
|
kind: "analyzer",
|
|
1841
1791
|
description: "Flags typos or unrecognized keys in sidecars (`.sm`).",
|
|
@@ -1874,12 +1824,12 @@ var annotationFieldUnknownAnalyzer = {
|
|
|
1874
1824
|
for (const key of Object.keys(annotations)) {
|
|
1875
1825
|
if (!knownAnnotationKeys.has(key)) {
|
|
1876
1826
|
issues.push({
|
|
1877
|
-
analyzerId:
|
|
1827
|
+
analyzerId: ID8,
|
|
1878
1828
|
severity: "warn",
|
|
1879
1829
|
nodeIds: [node.path],
|
|
1880
|
-
message:
|
|
1881
|
-
|
|
1882
|
-
|
|
1830
|
+
message: formatFinding({
|
|
1831
|
+
subject: key,
|
|
1832
|
+
body: tx(ANNOTATION_FIELD_UNKNOWN_TEXTS.unknownAnnotationKey)
|
|
1883
1833
|
}),
|
|
1884
1834
|
data: { surface: "annotations", key }
|
|
1885
1835
|
});
|
|
@@ -1901,14 +1851,14 @@ var annotationFieldUnknownAnalyzer = {
|
|
|
1901
1851
|
if (validator(value)) continue;
|
|
1902
1852
|
const errors = (validator.errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
|
|
1903
1853
|
issues.push({
|
|
1904
|
-
analyzerId:
|
|
1854
|
+
analyzerId: ID8,
|
|
1905
1855
|
severity: "warn",
|
|
1906
1856
|
nodeIds: [node.path],
|
|
1907
|
-
message:
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1857
|
+
message: formatFinding({
|
|
1858
|
+
subject: `${key}.${contribKey}`,
|
|
1859
|
+
body: tx(ANNOTATION_FIELD_UNKNOWN_TEXTS.pluginNamespaceInvalid, {
|
|
1860
|
+
errors
|
|
1861
|
+
})
|
|
1912
1862
|
}),
|
|
1913
1863
|
data: { surface: "plugin-namespace", pluginId: key, key: contribKey }
|
|
1914
1864
|
});
|
|
@@ -1917,12 +1867,12 @@ var annotationFieldUnknownAnalyzer = {
|
|
|
1917
1867
|
continue;
|
|
1918
1868
|
}
|
|
1919
1869
|
issues.push({
|
|
1920
|
-
analyzerId:
|
|
1870
|
+
analyzerId: ID8,
|
|
1921
1871
|
severity: "warn",
|
|
1922
1872
|
nodeIds: [node.path],
|
|
1923
|
-
message:
|
|
1924
|
-
|
|
1925
|
-
|
|
1873
|
+
message: formatFinding({
|
|
1874
|
+
subject: key,
|
|
1875
|
+
body: tx(ANNOTATION_FIELD_UNKNOWN_TEXTS.unknownRootKey)
|
|
1926
1876
|
}),
|
|
1927
1877
|
data: { surface: "root", key }
|
|
1928
1878
|
});
|
|
@@ -1979,18 +1929,21 @@ function collectPluginIds(contributions) {
|
|
|
1979
1929
|
// plugins/core/analyzers/annotation-orphan/text.ts
|
|
1980
1930
|
var ANNOTATION_ORPHAN_TEXTS = {
|
|
1981
1931
|
/**
|
|
1982
|
-
*
|
|
1983
|
-
*
|
|
1984
|
-
*
|
|
1985
|
-
*
|
|
1932
|
+
* Diagnosis body (`<what>; <why>`). The shared `formatFinding` helper
|
|
1933
|
+
* wraps it with the backtick subject (the orphan sidecar file); the
|
|
1934
|
+
* expected markdown path IS the finding's `nodeIds[0]`, so it never
|
|
1935
|
+
* appears in the message. The remediation hint moves to
|
|
1936
|
+
* `Issue.fix.summary` below.
|
|
1986
1937
|
*/
|
|
1987
|
-
message: "
|
|
1938
|
+
message: "Orphan sidecar; no matching markdown node",
|
|
1939
|
+
/** Remediation hint surfaced via `Issue.fix.summary`. */
|
|
1940
|
+
fixSummary: "Run `sm sidecar prune` to remove orphan sidecars."
|
|
1988
1941
|
};
|
|
1989
1942
|
|
|
1990
1943
|
// plugins/core/analyzers/annotation-orphan/index.ts
|
|
1991
|
-
var
|
|
1944
|
+
var ID9 = "annotation-orphan";
|
|
1992
1945
|
var annotationOrphanAnalyzer = {
|
|
1993
|
-
id:
|
|
1946
|
+
id: ID9,
|
|
1994
1947
|
pluginId: CORE_PLUGIN_ID,
|
|
1995
1948
|
kind: "analyzer",
|
|
1996
1949
|
description: "Flags sidecars (`.sm`) whose `.md` file no longer exists.",
|
|
@@ -2002,13 +1955,14 @@ var annotationOrphanAnalyzer = {
|
|
|
2002
1955
|
for (const orphan of orphans) {
|
|
2003
1956
|
const expectedMdRelative = orphan.relativePath.endsWith(".sm") ? `${orphan.relativePath.slice(0, -".sm".length)}.md` : `${orphan.relativePath}.md`;
|
|
2004
1957
|
issues.push({
|
|
2005
|
-
analyzerId:
|
|
1958
|
+
analyzerId: ID9,
|
|
2006
1959
|
severity: "warn",
|
|
2007
1960
|
nodeIds: [expectedMdRelative],
|
|
2008
|
-
message:
|
|
2009
|
-
|
|
2010
|
-
|
|
1961
|
+
message: formatFinding({
|
|
1962
|
+
subject: orphan.relativePath,
|
|
1963
|
+
body: tx(ANNOTATION_ORPHAN_TEXTS.message)
|
|
2011
1964
|
}),
|
|
1965
|
+
fix: { summary: tx(ANNOTATION_ORPHAN_TEXTS.fixSummary) },
|
|
2012
1966
|
data: {
|
|
2013
1967
|
sidecarPath: orphan.relativePath,
|
|
2014
1968
|
expectedMdPath: orphan.expectedMdPath
|
|
@@ -2021,14 +1975,17 @@ var annotationOrphanAnalyzer = {
|
|
|
2021
1975
|
|
|
2022
1976
|
// plugins/core/analyzers/annotation-stale/text.ts
|
|
2023
1977
|
var ANNOTATION_STALE_TEXTS = {
|
|
2024
|
-
//
|
|
2025
|
-
//
|
|
1978
|
+
// Diagnosis bodies (`<what>; <why>`). The shared `formatFinding` helper
|
|
1979
|
+
// emits no subject (the affected node IS the finding's own node); the
|
|
1980
|
+
// remediation hint moves to `Issue.fix.summary` below.
|
|
2026
1981
|
/** body changed since last bump */
|
|
2027
|
-
bodyDrift: "Sidecar
|
|
1982
|
+
bodyDrift: "Sidecar stale; body changed since last bump",
|
|
2028
1983
|
/** frontmatter changed since last bump */
|
|
2029
|
-
frontmatterDrift: "Sidecar
|
|
1984
|
+
frontmatterDrift: "Sidecar stale; frontmatter changed since last bump",
|
|
2030
1985
|
/** both body and frontmatter changed */
|
|
2031
|
-
bothDrift: "Sidecar
|
|
1986
|
+
bothDrift: "Sidecar stale; body and frontmatter changed since last bump",
|
|
1987
|
+
/** Remediation hint surfaced via `Issue.fix.summary`. */
|
|
1988
|
+
fixSummary: "Run `sm bump <path>` to refresh the sidecar.",
|
|
2032
1989
|
// Tooltips for the `card.footer.right` clock chip emitted alongside
|
|
2033
1990
|
// the issue. Lists only the drifted face(s), in-sync faces are
|
|
2034
1991
|
// omitted so the operator immediately sees what's modified without
|
|
@@ -2041,7 +1998,7 @@ var ANNOTATION_STALE_TEXTS = {
|
|
|
2041
1998
|
};
|
|
2042
1999
|
|
|
2043
2000
|
// plugins/core/analyzers/annotation-stale/index.ts
|
|
2044
|
-
var
|
|
2001
|
+
var ID10 = "annotation-stale";
|
|
2045
2002
|
var staleIcon = {
|
|
2046
2003
|
slot: "card.footer.right",
|
|
2047
2004
|
icon: "pi-clock",
|
|
@@ -2054,7 +2011,7 @@ var staleBadge = {
|
|
|
2054
2011
|
priority: 20
|
|
2055
2012
|
};
|
|
2056
2013
|
var annotationStaleAnalyzer = {
|
|
2057
|
-
id:
|
|
2014
|
+
id: ID10,
|
|
2058
2015
|
pluginId: CORE_PLUGIN_ID,
|
|
2059
2016
|
kind: "analyzer",
|
|
2060
2017
|
description: "Marks sidecars (`.sm`) that are out of date with their `.md`.",
|
|
@@ -2070,10 +2027,11 @@ var annotationStaleAnalyzer = {
|
|
|
2070
2027
|
const status = staleStatus(node.sidecar);
|
|
2071
2028
|
if (status === null) continue;
|
|
2072
2029
|
issues.push({
|
|
2073
|
-
analyzerId:
|
|
2030
|
+
analyzerId: ID10,
|
|
2074
2031
|
severity: "info",
|
|
2075
2032
|
nodeIds: [node.path],
|
|
2076
|
-
message: messageFor(status
|
|
2033
|
+
message: formatFinding({ body: messageFor(status) }),
|
|
2034
|
+
fix: { summary: tx(ANNOTATION_STALE_TEXTS.fixSummary) },
|
|
2077
2035
|
data: { status }
|
|
2078
2036
|
});
|
|
2079
2037
|
ctx.emitContribution(node.path, staleIcon, {
|
|
@@ -2093,14 +2051,14 @@ function staleStatus(overlay) {
|
|
|
2093
2051
|
if (status === void 0 || status === null || status === "fresh") return null;
|
|
2094
2052
|
return status;
|
|
2095
2053
|
}
|
|
2096
|
-
function messageFor(status
|
|
2054
|
+
function messageFor(status) {
|
|
2097
2055
|
switch (status) {
|
|
2098
2056
|
case "stale-body":
|
|
2099
|
-
return tx(ANNOTATION_STALE_TEXTS.bodyDrift
|
|
2057
|
+
return tx(ANNOTATION_STALE_TEXTS.bodyDrift);
|
|
2100
2058
|
case "stale-frontmatter":
|
|
2101
|
-
return tx(ANNOTATION_STALE_TEXTS.frontmatterDrift
|
|
2059
|
+
return tx(ANNOTATION_STALE_TEXTS.frontmatterDrift);
|
|
2102
2060
|
case "stale-both":
|
|
2103
|
-
return tx(ANNOTATION_STALE_TEXTS.bothDrift
|
|
2061
|
+
return tx(ANNOTATION_STALE_TEXTS.bothDrift);
|
|
2104
2062
|
}
|
|
2105
2063
|
}
|
|
2106
2064
|
function tooltipFor(status) {
|
|
@@ -2115,9 +2073,9 @@ function tooltipFor(status) {
|
|
|
2115
2073
|
}
|
|
2116
2074
|
|
|
2117
2075
|
// plugins/core/analyzers/contribution-orphan/index.ts
|
|
2118
|
-
var
|
|
2076
|
+
var ID11 = "contribution-orphan";
|
|
2119
2077
|
var contributionOrphanAnalyzer = {
|
|
2120
|
-
id:
|
|
2078
|
+
id: ID11,
|
|
2121
2079
|
pluginId: CORE_PLUGIN_ID,
|
|
2122
2080
|
kind: "analyzer",
|
|
2123
2081
|
description: "Warns about plugin data referencing nodes renamed or deleted in the latest scan.",
|
|
@@ -2127,6 +2085,92 @@ var contributionOrphanAnalyzer = {
|
|
|
2127
2085
|
}
|
|
2128
2086
|
};
|
|
2129
2087
|
|
|
2088
|
+
// plugins/core/analyzers/extractor-collision/text.ts
|
|
2089
|
+
var EXTRACTOR_COLLISION_TEXTS = {
|
|
2090
|
+
/**
|
|
2091
|
+
* Per-Signal warn issue: two extractors detected something at
|
|
2092
|
+
* overlapping byte ranges within the same node and the resolver
|
|
2093
|
+
* dropped the loser. Surfaces WHO lost, WHO won, and the tiebreak
|
|
2094
|
+
* reason so the operator can understand why a candidate edge did NOT
|
|
2095
|
+
* become a Link (e.g. a `[link](path)` with `@path` inside the bracket
|
|
2096
|
+
* text: markdown-link wins and the at-directive silently disappears
|
|
2097
|
+
* without this warning).
|
|
2098
|
+
*/
|
|
2099
|
+
message: "Overlap collision; {{loserExtractor}} (at {{loserRange}}) lost to {{winnerExtractor}} (at {{winnerRange}}) by {{reason}}, only the winning edge persists",
|
|
2100
|
+
/**
|
|
2101
|
+
* Remediation hint for the range-overlap rejection, surfaced via
|
|
2102
|
+
* `Issue.fix.summary`. Not autofixable: the rule cannot tell which
|
|
2103
|
+
* detection the author meant, so it offers the two resolutions
|
|
2104
|
+
* (rephrase one token, or accept the winner).
|
|
2105
|
+
*/
|
|
2106
|
+
rejectedFixSummary: "Rephrase one of the overlapping tokens, or accept the winner."
|
|
2107
|
+
};
|
|
2108
|
+
|
|
2109
|
+
// plugins/core/analyzers/extractor-collision/index.ts
|
|
2110
|
+
var ID12 = "extractor-collision";
|
|
2111
|
+
function signalLines(signal) {
|
|
2112
|
+
return signal.range && typeof signal.range.line === "number" ? [signal.range.line] : void 0;
|
|
2113
|
+
}
|
|
2114
|
+
var extractorCollisionAnalyzer = {
|
|
2115
|
+
id: ID12,
|
|
2116
|
+
pluginId: CORE_PLUGIN_ID,
|
|
2117
|
+
kind: "analyzer",
|
|
2118
|
+
description: "Reports when two extractors detect something at the same span of body text and the resolver drops one.",
|
|
2119
|
+
mode: "deterministic",
|
|
2120
|
+
evaluate(ctx) {
|
|
2121
|
+
const signals = ctx.signals;
|
|
2122
|
+
if (!signals || signals.length === 0) return [];
|
|
2123
|
+
const issues = [];
|
|
2124
|
+
for (const signal of signals) {
|
|
2125
|
+
const issue = makeIssue(signal);
|
|
2126
|
+
if (issue) issues.push(issue);
|
|
2127
|
+
}
|
|
2128
|
+
return issues;
|
|
2129
|
+
}
|
|
2130
|
+
};
|
|
2131
|
+
function makeIssue(signal) {
|
|
2132
|
+
const resolution = signal.resolution;
|
|
2133
|
+
if (!resolution || resolution.outcome !== "rejected" || !resolution.rejectedBy) return null;
|
|
2134
|
+
const winner = resolution.rejectedBy;
|
|
2135
|
+
const loserCandidate = signal.candidates[resolution.winnerIndex ?? 0];
|
|
2136
|
+
const loserRange = signal.range ? `${signal.range.start}-${signal.range.end}` : "unknown";
|
|
2137
|
+
const winnerRange = `${winner.range.start}-${winner.range.end}`;
|
|
2138
|
+
return {
|
|
2139
|
+
analyzerId: ID12,
|
|
2140
|
+
severity: "warn",
|
|
2141
|
+
nodeIds: [signal.source],
|
|
2142
|
+
message: formatFinding({
|
|
2143
|
+
subject: signal.raw,
|
|
2144
|
+
lines: signalLines(signal),
|
|
2145
|
+
body: tx(EXTRACTOR_COLLISION_TEXTS.message, {
|
|
2146
|
+
loserExtractor: loserCandidate.extractorId,
|
|
2147
|
+
loserRange,
|
|
2148
|
+
winnerExtractor: winner.extractorId,
|
|
2149
|
+
winnerRange,
|
|
2150
|
+
reason: winner.reason
|
|
2151
|
+
})
|
|
2152
|
+
}),
|
|
2153
|
+
fix: { summary: tx(EXTRACTOR_COLLISION_TEXTS.rejectedFixSummary) },
|
|
2154
|
+
data: {
|
|
2155
|
+
loser: {
|
|
2156
|
+
extractorId: loserCandidate.extractorId,
|
|
2157
|
+
raw: signal.raw,
|
|
2158
|
+
range: signal.range ?? null,
|
|
2159
|
+
candidate: {
|
|
2160
|
+
kind: loserCandidate.kind,
|
|
2161
|
+
target: loserCandidate.target,
|
|
2162
|
+
confidence: loserCandidate.confidence
|
|
2163
|
+
}
|
|
2164
|
+
},
|
|
2165
|
+
winner: {
|
|
2166
|
+
extractorId: winner.extractorId,
|
|
2167
|
+
range: winner.range
|
|
2168
|
+
},
|
|
2169
|
+
reason: winner.reason
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2130
2174
|
// plugins/core/analyzers/issue-counter/text.ts
|
|
2131
2175
|
var ISSUE_COUNTER_TEXTS = {
|
|
2132
2176
|
errorTooltipSingle: "1 error",
|
|
@@ -2203,55 +2247,122 @@ var issueCounterAnalyzer = {
|
|
|
2203
2247
|
}
|
|
2204
2248
|
};
|
|
2205
2249
|
|
|
2206
|
-
//
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2250
|
+
// kernel/util/link-lines.ts
|
|
2251
|
+
function isSelfLoop(link) {
|
|
2252
|
+
if (link.source === link.target) return true;
|
|
2253
|
+
if (link.resolvedTarget && link.source === link.resolvedTarget) return true;
|
|
2254
|
+
return false;
|
|
2255
|
+
}
|
|
2256
|
+
function linkLines(link) {
|
|
2257
|
+
const lines = /* @__PURE__ */ new Set();
|
|
2258
|
+
for (const occ of link.occurrences ?? []) {
|
|
2259
|
+
const line = occ.location?.line;
|
|
2260
|
+
if (typeof line === "number") lines.add(line);
|
|
2261
|
+
}
|
|
2262
|
+
if (lines.size === 0) {
|
|
2263
|
+
const line = link.location?.line;
|
|
2264
|
+
if (typeof line === "number") lines.add(line);
|
|
2265
|
+
}
|
|
2266
|
+
return [...lines].sort((a, b) => a - b);
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
// plugins/core/analyzers/link-counter/text.ts
|
|
2270
|
+
var LINK_COUNTER_TEXTS = {
|
|
2271
|
+
/** Accessible label for the incoming-links chip. */
|
|
2272
|
+
linksInLabel: "incoming links",
|
|
2273
|
+
/** Accessible label for the outgoing-links chip. */
|
|
2274
|
+
linksOutLabel: "outgoing links",
|
|
2275
|
+
/** Tooltip header for the incoming breakdown (first line). */
|
|
2276
|
+
directionIn: "in",
|
|
2277
|
+
/** Tooltip header for the outgoing breakdown (first line). */
|
|
2278
|
+
directionOut: "out"
|
|
2214
2279
|
};
|
|
2215
2280
|
|
|
2216
|
-
// plugins/core/analyzers/
|
|
2217
|
-
var ID14 = "
|
|
2218
|
-
var
|
|
2281
|
+
// plugins/core/analyzers/link-counter/index.ts
|
|
2282
|
+
var ID14 = "link-counter";
|
|
2283
|
+
var linksIn = {
|
|
2284
|
+
slot: "card.footer.left",
|
|
2285
|
+
icon: "pi-download",
|
|
2286
|
+
label: LINK_COUNTER_TEXTS.linksInLabel,
|
|
2287
|
+
emitWhenEmpty: false,
|
|
2288
|
+
priority: 10
|
|
2289
|
+
};
|
|
2290
|
+
var linksOut = {
|
|
2291
|
+
slot: "card.footer.left",
|
|
2292
|
+
icon: "pi-upload",
|
|
2293
|
+
label: LINK_COUNTER_TEXTS.linksOutLabel,
|
|
2294
|
+
emitWhenEmpty: false,
|
|
2295
|
+
priority: 20
|
|
2296
|
+
};
|
|
2297
|
+
var linkCounterAnalyzer = {
|
|
2219
2298
|
id: ID14,
|
|
2220
2299
|
pluginId: CORE_PLUGIN_ID,
|
|
2221
2300
|
kind: "analyzer",
|
|
2222
|
-
description: "
|
|
2301
|
+
description: "Counts incoming and outgoing links per node.",
|
|
2223
2302
|
mode: "deterministic",
|
|
2303
|
+
ui: { linksIn, linksOut },
|
|
2224
2304
|
evaluate(ctx) {
|
|
2225
|
-
const
|
|
2226
|
-
|
|
2227
|
-
const
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
nodeIds: [filePath],
|
|
2233
|
-
message: tx(JOB_FILE_ORPHAN_TEXTS.message, { filePath }),
|
|
2234
|
-
data: { filePath }
|
|
2235
|
-
});
|
|
2305
|
+
const perTarget = /* @__PURE__ */ new Map();
|
|
2306
|
+
const perSource = /* @__PURE__ */ new Map();
|
|
2307
|
+
for (const link of ctx.links) {
|
|
2308
|
+
if (isSelfLoop(link)) continue;
|
|
2309
|
+
const target = link.resolvedTarget ?? link.target;
|
|
2310
|
+
bump(perTarget, target, link.kind);
|
|
2311
|
+
bump(perSource, link.source, link.kind);
|
|
2236
2312
|
}
|
|
2237
|
-
|
|
2313
|
+
for (const node of ctx.nodes) {
|
|
2314
|
+
emitChip(ctx, node.path, linksIn, "in", perTarget.get(node.path));
|
|
2315
|
+
emitChip(ctx, node.path, linksOut, "out", perSource.get(node.path));
|
|
2316
|
+
}
|
|
2317
|
+
return [];
|
|
2238
2318
|
}
|
|
2239
2319
|
};
|
|
2320
|
+
function bump(map, key, kind) {
|
|
2321
|
+
let byKind = map.get(key);
|
|
2322
|
+
if (!byKind) {
|
|
2323
|
+
byKind = /* @__PURE__ */ new Map();
|
|
2324
|
+
map.set(key, byKind);
|
|
2325
|
+
}
|
|
2326
|
+
byKind.set(kind, (byKind.get(kind) ?? 0) + 1);
|
|
2327
|
+
}
|
|
2328
|
+
function emitChip(ctx, nodePath, ref, direction, byKind) {
|
|
2329
|
+
if (!byKind) return;
|
|
2330
|
+
let total = 0;
|
|
2331
|
+
for (const n of byKind.values()) total += n;
|
|
2332
|
+
if (total === 0) return;
|
|
2333
|
+
const capped = Math.min(total, 99);
|
|
2334
|
+
ctx.emitContribution(nodePath, ref, {
|
|
2335
|
+
value: capped,
|
|
2336
|
+
tooltip: formatBreakdown(byKind, direction)
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
function formatBreakdown(byKind, direction) {
|
|
2340
|
+
const lines = [...byKind.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([kind, n]) => `${kind}: ${n}`);
|
|
2341
|
+
const dirLabel = direction === "in" ? LINK_COUNTER_TEXTS.directionIn : LINK_COUNTER_TEXTS.directionOut;
|
|
2342
|
+
return [dirLabel, ...lines].join("\n");
|
|
2343
|
+
}
|
|
2240
2344
|
|
|
2241
|
-
// plugins/core/analyzers/link-conflict/text.ts
|
|
2242
|
-
var
|
|
2345
|
+
// plugins/core/analyzers/link-kind-conflict/text.ts
|
|
2346
|
+
var LINK_KIND_CONFLICT_TEXTS = {
|
|
2347
|
+
/**
|
|
2348
|
+
* Diagnosis body (`<what>; <why>`). The shared `formatFinding` helper
|
|
2349
|
+
* wraps it with the backtick subject (the disputed target); the source
|
|
2350
|
+
* is the finding's own node, so it never appears in the message.
|
|
2351
|
+
*/
|
|
2352
|
+
message: "Conflicting link kind; detectors disagree ({{kindList}})",
|
|
2243
2353
|
/**
|
|
2244
|
-
*
|
|
2245
|
-
* the
|
|
2246
|
-
*
|
|
2354
|
+
* Remediation hint surfaced via `Issue.fix.summary`. Not autofixable:
|
|
2355
|
+
* the rule cannot tell which kind the author meant, so it offers the
|
|
2356
|
+
* two valid resolutions (drop one source, or accept the overlap on
|
|
2357
|
+
* purpose). Mirrors the `warn`-severity `link-self-loop` hint shape.
|
|
2247
2358
|
*/
|
|
2248
|
-
|
|
2359
|
+
fixSummary: "Remove one of the conflicting sources to settle on a single kind, or ignore the conflict deliberately."
|
|
2249
2360
|
};
|
|
2250
2361
|
|
|
2251
|
-
// plugins/core/analyzers/link-conflict/index.ts
|
|
2252
|
-
var ID15 = "link-conflict";
|
|
2362
|
+
// plugins/core/analyzers/link-kind-conflict/index.ts
|
|
2363
|
+
var ID15 = "link-kind-conflict";
|
|
2253
2364
|
var NON_CONFLICTING_KINDS = /* @__PURE__ */ new Set(["points"]);
|
|
2254
|
-
var
|
|
2365
|
+
var linkKindConflictAnalyzer = {
|
|
2255
2366
|
id: ID15,
|
|
2256
2367
|
pluginId: CORE_PLUGIN_ID,
|
|
2257
2368
|
kind: "analyzer",
|
|
@@ -2303,11 +2414,13 @@ var linkConflictAnalyzer = {
|
|
|
2303
2414
|
analyzerId: ID15,
|
|
2304
2415
|
severity: "warn",
|
|
2305
2416
|
nodeIds: [source, target],
|
|
2306
|
-
message:
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2417
|
+
message: formatFinding({
|
|
2418
|
+
subject: target,
|
|
2419
|
+
body: tx(LINK_KIND_CONFLICT_TEXTS.message, {
|
|
2420
|
+
kindList
|
|
2421
|
+
})
|
|
2310
2422
|
}),
|
|
2423
|
+
fix: { summary: tx(LINK_KIND_CONFLICT_TEXTS.fixSummary) },
|
|
2311
2424
|
data: { source, target, variants }
|
|
2312
2425
|
});
|
|
2313
2426
|
}
|
|
@@ -2318,137 +2431,46 @@ function rankConfidence(c) {
|
|
|
2318
2431
|
return c;
|
|
2319
2432
|
}
|
|
2320
2433
|
|
|
2321
|
-
// kernel/util/link-lines.ts
|
|
2322
|
-
function isSelfLoop(link) {
|
|
2323
|
-
if (link.source === link.target) return true;
|
|
2324
|
-
if (link.resolvedTarget && link.source === link.resolvedTarget) return true;
|
|
2325
|
-
return false;
|
|
2326
|
-
}
|
|
2327
|
-
function linkLines(link) {
|
|
2328
|
-
const lines = /* @__PURE__ */ new Set();
|
|
2329
|
-
for (const occ of link.occurrences ?? []) {
|
|
2330
|
-
const line = occ.location?.line;
|
|
2331
|
-
if (typeof line === "number") lines.add(line);
|
|
2332
|
-
}
|
|
2333
|
-
if (lines.size === 0) {
|
|
2334
|
-
const line = link.location?.line;
|
|
2335
|
-
if (typeof line === "number") lines.add(line);
|
|
2336
|
-
}
|
|
2337
|
-
return [...lines].sort((a, b) => a - b);
|
|
2338
|
-
}
|
|
2339
|
-
function linkWhere(link, texts) {
|
|
2340
|
-
const lines = linkLines(link);
|
|
2341
|
-
if (lines.length === 0) return "";
|
|
2342
|
-
return tx(lines.length === 1 ? texts.single : texts.plural, {
|
|
2343
|
-
lines: lines.join(", ")
|
|
2344
|
-
});
|
|
2345
|
-
}
|
|
2346
|
-
|
|
2347
|
-
// plugins/core/analyzers/link-counter/index.ts
|
|
2348
|
-
var ID16 = "link-counter";
|
|
2349
|
-
var linksIn = {
|
|
2350
|
-
slot: "card.footer.left",
|
|
2351
|
-
icon: "pi-download",
|
|
2352
|
-
label: "incoming links",
|
|
2353
|
-
emitWhenEmpty: false,
|
|
2354
|
-
priority: 10
|
|
2355
|
-
};
|
|
2356
|
-
var linksOut = {
|
|
2357
|
-
slot: "card.footer.left",
|
|
2358
|
-
icon: "pi-upload",
|
|
2359
|
-
label: "outgoing links",
|
|
2360
|
-
emitWhenEmpty: false,
|
|
2361
|
-
priority: 20
|
|
2362
|
-
};
|
|
2363
|
-
var linkCounterAnalyzer = {
|
|
2364
|
-
id: ID16,
|
|
2365
|
-
pluginId: CORE_PLUGIN_ID,
|
|
2366
|
-
kind: "analyzer",
|
|
2367
|
-
description: "Counts incoming and outgoing links per node.",
|
|
2368
|
-
mode: "deterministic",
|
|
2369
|
-
ui: { linksIn, linksOut },
|
|
2370
|
-
evaluate(ctx) {
|
|
2371
|
-
const perTarget = /* @__PURE__ */ new Map();
|
|
2372
|
-
const perSource = /* @__PURE__ */ new Map();
|
|
2373
|
-
for (const link of ctx.links) {
|
|
2374
|
-
if (isSelfLoop(link)) continue;
|
|
2375
|
-
const target = link.resolvedTarget ?? link.target;
|
|
2376
|
-
bump(perTarget, target, link.kind);
|
|
2377
|
-
bump(perSource, link.source, link.kind);
|
|
2378
|
-
}
|
|
2379
|
-
for (const node of ctx.nodes) {
|
|
2380
|
-
emitChip(ctx, node.path, linksIn, "in", perTarget.get(node.path));
|
|
2381
|
-
emitChip(ctx, node.path, linksOut, "out", perSource.get(node.path));
|
|
2382
|
-
}
|
|
2383
|
-
return [];
|
|
2384
|
-
}
|
|
2385
|
-
};
|
|
2386
|
-
function bump(map, key, kind) {
|
|
2387
|
-
let byKind = map.get(key);
|
|
2388
|
-
if (!byKind) {
|
|
2389
|
-
byKind = /* @__PURE__ */ new Map();
|
|
2390
|
-
map.set(key, byKind);
|
|
2391
|
-
}
|
|
2392
|
-
byKind.set(kind, (byKind.get(kind) ?? 0) + 1);
|
|
2393
|
-
}
|
|
2394
|
-
function emitChip(ctx, nodePath, ref, direction, byKind) {
|
|
2395
|
-
if (!byKind) return;
|
|
2396
|
-
let total = 0;
|
|
2397
|
-
for (const n of byKind.values()) total += n;
|
|
2398
|
-
if (total === 0) return;
|
|
2399
|
-
const capped = Math.min(total, 99);
|
|
2400
|
-
ctx.emitContribution(nodePath, ref, {
|
|
2401
|
-
value: capped,
|
|
2402
|
-
tooltip: formatBreakdown(byKind, direction)
|
|
2403
|
-
});
|
|
2404
|
-
}
|
|
2405
|
-
function formatBreakdown(byKind, direction) {
|
|
2406
|
-
const lines = [...byKind.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([kind, n]) => `${kind}: ${n}`);
|
|
2407
|
-
return [direction, ...lines].join("\n");
|
|
2408
|
-
}
|
|
2409
|
-
|
|
2410
2434
|
// plugins/core/analyzers/link-self-loop/text.ts
|
|
2411
2435
|
var LINK_SELF_LOOP_TEXTS = {
|
|
2412
2436
|
/**
|
|
2413
|
-
* Per-edge warn: a node body references itself via the slash /
|
|
2437
|
+
* Per-edge warn body: a node body references itself via the slash /
|
|
2414
2438
|
* at-directive / markdown-link surface (most commonly because the
|
|
2415
2439
|
* file's heading IS the invocation token, e.g. `# /deploy` inside
|
|
2416
2440
|
* `commands/deploy.md`). The link is structurally valid but rarely
|
|
2417
2441
|
* the operator's intent; UI consumers MAY hide it by default and
|
|
2418
2442
|
* surface a count.
|
|
2419
2443
|
*/
|
|
2420
|
-
message: "
|
|
2421
|
-
/**
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2444
|
+
message: "Self-reference; the skill/command invokes itself, potential loop",
|
|
2445
|
+
/**
|
|
2446
|
+
* Remediation hint surfaced via `Issue.fix.summary` (the Inspector
|
|
2447
|
+
* renders it under the finding, separate from the diagnosis body).
|
|
2448
|
+
*/
|
|
2449
|
+
fixSummary: "Remove the token or ignore the self-reference deliberately."
|
|
2425
2450
|
};
|
|
2426
2451
|
|
|
2427
2452
|
// plugins/core/analyzers/link-self-loop/index.ts
|
|
2428
|
-
var
|
|
2453
|
+
var ID16 = "link-self-loop";
|
|
2429
2454
|
var linkSelfLoopAnalyzer = {
|
|
2430
|
-
id:
|
|
2455
|
+
id: ID16,
|
|
2431
2456
|
pluginId: CORE_PLUGIN_ID,
|
|
2432
2457
|
kind: "analyzer",
|
|
2433
2458
|
description: "Flags links whose source is also their own resolved target (e.g. a body heading like `# /deploy` inside the file that defines `/deploy`).",
|
|
2434
2459
|
mode: "deterministic",
|
|
2435
2460
|
evaluate(ctx) {
|
|
2436
|
-
if (ctx.links.length === 0) return [];
|
|
2437
2461
|
const issues = [];
|
|
2438
2462
|
for (const link of ctx.links) {
|
|
2439
2463
|
if (!isSelfLoop(link)) continue;
|
|
2440
2464
|
issues.push({
|
|
2441
|
-
analyzerId:
|
|
2465
|
+
analyzerId: ID16,
|
|
2442
2466
|
severity: "warn",
|
|
2443
2467
|
nodeIds: [link.source],
|
|
2444
|
-
message:
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
single: LINK_SELF_LOOP_TEXTS.whereSingle,
|
|
2449
|
-
plural: LINK_SELF_LOOP_TEXTS.wherePlural
|
|
2450
|
-
})
|
|
2468
|
+
message: formatFinding({
|
|
2469
|
+
subject: link.trigger?.originalTrigger ?? link.target,
|
|
2470
|
+
lines: linkLines(link),
|
|
2471
|
+
body: tx(LINK_SELF_LOOP_TEXTS.message)
|
|
2451
2472
|
}),
|
|
2473
|
+
fix: { summary: tx(LINK_SELF_LOOP_TEXTS.fixSummary) },
|
|
2452
2474
|
data: {
|
|
2453
2475
|
target: link.target,
|
|
2454
2476
|
resolvedTarget: link.resolvedTarget ?? link.target,
|
|
@@ -2460,152 +2482,81 @@ var linkSelfLoopAnalyzer = {
|
|
|
2460
2482
|
}
|
|
2461
2483
|
};
|
|
2462
2484
|
|
|
2463
|
-
//
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
if (normalised) out.push(normalised);
|
|
2474
|
-
}
|
|
2475
|
-
return out;
|
|
2476
|
-
}
|
|
2477
|
-
function readIdentifier(source, node) {
|
|
2478
|
-
if (source === "frontmatter.name") return readFrontmatterName(node);
|
|
2479
|
-
if (source === "filename-basename") return readFilenameBasename(node);
|
|
2480
|
-
return readDirname(node);
|
|
2481
|
-
}
|
|
2482
|
-
function readFrontmatterName(node) {
|
|
2483
|
-
const raw = node.frontmatter?.["name"];
|
|
2484
|
-
if (typeof raw !== "string") return null;
|
|
2485
|
-
return raw.length > 0 ? raw : null;
|
|
2486
|
-
}
|
|
2487
|
-
function readFilenameBasename(node) {
|
|
2488
|
-
const base = pathPosix4.basename(node.path);
|
|
2489
|
-
if (!base) return null;
|
|
2490
|
-
const ext = pathPosix4.extname(base);
|
|
2491
|
-
const stem = ext ? base.slice(0, -ext.length) : base;
|
|
2492
|
-
return stem.length > 0 ? stem : null;
|
|
2493
|
-
}
|
|
2494
|
-
function readDirname(node) {
|
|
2495
|
-
const dir = pathPosix4.dirname(node.path);
|
|
2496
|
-
if (!dir || dir === "." || dir === "/") return null;
|
|
2497
|
-
const base = pathPosix4.basename(dir);
|
|
2498
|
-
return base.length > 0 ? base : null;
|
|
2499
|
-
}
|
|
2485
|
+
// plugins/core/analyzers/name-collision/text.ts
|
|
2486
|
+
var NAME_COLLISION_TEXTS = {
|
|
2487
|
+
/**
|
|
2488
|
+
* Diagnosis body (`<what>; <why>: <evidence>`). The shared
|
|
2489
|
+
* `formatFinding` helper wraps it with the backtick subject (the
|
|
2490
|
+
* normalised name claimed by two or more nodes). The evidence is the
|
|
2491
|
+
* competing node paths.
|
|
2492
|
+
*/
|
|
2493
|
+
message: "Name collision; {{count}} nodes declare the same name: {{paths}}"
|
|
2494
|
+
};
|
|
2500
2495
|
|
|
2501
|
-
//
|
|
2502
|
-
var
|
|
2503
|
-
var
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
const byPath3 = /* @__PURE__ */ new Set();
|
|
2534
|
-
const byName = /* @__PURE__ */ new Map();
|
|
2535
|
-
const nodeByPath = /* @__PURE__ */ new Map();
|
|
2536
|
-
for (const node of nodes) {
|
|
2537
|
-
byPath3.add(node.path);
|
|
2538
|
-
nodeByPath.set(node.path, node);
|
|
2539
|
-
indexNode(node, ctx, byName);
|
|
2540
|
-
}
|
|
2541
|
-
return { byPath: byPath3, byName, nodeByPath };
|
|
2542
|
-
}
|
|
2543
|
-
function resolve2(link, indexes, ctx) {
|
|
2544
|
-
if (indexes.byPath.has(link.target)) return link.target;
|
|
2545
|
-
return resolveByName(link, indexes, ctx);
|
|
2546
|
-
}
|
|
2547
|
-
function isGenuinelyBroken(link, indexes) {
|
|
2548
|
-
if (indexes.byPath.has(link.target)) return false;
|
|
2549
|
-
const stripped = stripTriggerSigil(link.trigger?.normalizedTrigger);
|
|
2550
|
-
if (stripped !== null && indexes.byName.has(stripped)) return false;
|
|
2551
|
-
return true;
|
|
2552
|
-
}
|
|
2553
|
-
function resolveByName(link, indexes, ctx) {
|
|
2554
|
-
const stripped = stripTriggerSigil(link.trigger?.normalizedTrigger);
|
|
2555
|
-
if (stripped === null) return "none";
|
|
2556
|
-
const candidates = indexes.byName.get(stripped);
|
|
2557
|
-
if (!candidates?.length) return "none";
|
|
2558
|
-
const allowedKinds = lookupAllowedKinds(link, indexes, ctx);
|
|
2559
|
-
if (!allowedKinds?.length) return "none";
|
|
2560
|
-
const winner = candidates.find((c) => allowedKinds.includes(c.kind));
|
|
2561
|
-
return winner ? winner.path : "none";
|
|
2562
|
-
}
|
|
2563
|
-
function lookupAllowedKinds(link, _indexes, ctx) {
|
|
2564
|
-
if (ctx.activeProvider === null) return void 0;
|
|
2565
|
-
return ctx.providerResolution.get(ctx.activeProvider)?.[link.kind];
|
|
2566
|
-
}
|
|
2567
|
-
function stripTriggerSigil(normalized) {
|
|
2568
|
-
if (!normalized) return null;
|
|
2569
|
-
const trimmed = normalized.replace(/^[/@]/, "").trim();
|
|
2570
|
-
return trimmed.length === 0 ? null : trimmed;
|
|
2571
|
-
}
|
|
2572
|
-
function indexNode(node, ctx, byName) {
|
|
2573
|
-
const kindDescriptor = ctx.kindRegistry.get(kindKey(node));
|
|
2574
|
-
const normalised = deriveNodeIdentifiers(node, kindDescriptor);
|
|
2575
|
-
for (const name of normalised) {
|
|
2576
|
-
const entry = { kind: node.kind, path: node.path };
|
|
2577
|
-
const bucket = byName.get(name);
|
|
2578
|
-
if (bucket) {
|
|
2579
|
-
bucket.push(entry);
|
|
2580
|
-
} else {
|
|
2581
|
-
byName.set(name, [entry]);
|
|
2496
|
+
// plugins/core/analyzers/name-collision/index.ts
|
|
2497
|
+
var ID17 = "name-collision";
|
|
2498
|
+
var nameCollisionAnalyzer = {
|
|
2499
|
+
id: ID17,
|
|
2500
|
+
pluginId: CORE_PLUGIN_ID,
|
|
2501
|
+
kind: "analyzer",
|
|
2502
|
+
mode: "deterministic",
|
|
2503
|
+
description: "Flags two or more nodes that declare the same resolvable `name`.",
|
|
2504
|
+
// Pure projector of `ctx.nameCollisions` (computed once by the
|
|
2505
|
+
// orchestrator from the kind registry). One `error` per colliding name.
|
|
2506
|
+
evaluate(ctx) {
|
|
2507
|
+
const collisions = ctx.nameCollisions;
|
|
2508
|
+
if (!collisions || collisions.size === 0) return [];
|
|
2509
|
+
const issues = [];
|
|
2510
|
+
for (const [name, claims] of collisions) {
|
|
2511
|
+
const paths = claims.map((c) => c.path);
|
|
2512
|
+
issues.push({
|
|
2513
|
+
analyzerId: ID17,
|
|
2514
|
+
severity: "error",
|
|
2515
|
+
nodeIds: paths,
|
|
2516
|
+
message: formatFinding({
|
|
2517
|
+
subject: name,
|
|
2518
|
+
body: tx(NAME_COLLISION_TEXTS.message, {
|
|
2519
|
+
count: paths.length,
|
|
2520
|
+
paths: paths.join(", ")
|
|
2521
|
+
})
|
|
2522
|
+
}),
|
|
2523
|
+
data: {
|
|
2524
|
+
name,
|
|
2525
|
+
claims: claims.map((c) => ({ path: c.path, kind: c.kind }))
|
|
2526
|
+
}
|
|
2527
|
+
});
|
|
2582
2528
|
}
|
|
2529
|
+
return issues;
|
|
2583
2530
|
}
|
|
2584
|
-
}
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2531
|
+
};
|
|
2532
|
+
|
|
2533
|
+
// kernel/orchestrator/confidence-constants.ts
|
|
2534
|
+
var RESERVED_PENALTY = 0.9;
|
|
2535
|
+
var BROKEN_PENALTY = 0.5;
|
|
2588
2536
|
|
|
2589
2537
|
// plugins/core/analyzers/name-reserved/text.ts
|
|
2590
2538
|
var NAME_RESERVED_TEXTS = {
|
|
2591
2539
|
/**
|
|
2592
|
-
* Target-side
|
|
2593
|
-
* a runtime built-in.
|
|
2594
|
-
*
|
|
2540
|
+
* Target-side body (`<what>; <why>`): emitted on the user file that
|
|
2541
|
+
* collides with a runtime built-in. The shared `formatFinding` helper
|
|
2542
|
+
* adds no subject (the offending node IS the finding's own node); the
|
|
2543
|
+
* remediation hint moves to `Issue.fix.summary` below.
|
|
2595
2544
|
*/
|
|
2596
|
-
message: "
|
|
2545
|
+
message: "Reserved name; this {{kind}} name is shadowed by the {{provider}} runtime built-in",
|
|
2546
|
+
/** Remediation hint for the target-side finding. */
|
|
2547
|
+
fixSummary: "Rename the file or its frontmatter.name.",
|
|
2597
2548
|
/**
|
|
2598
|
-
* Source-side
|
|
2599
|
-
* whose target resolves to a reserved name.
|
|
2600
|
-
*
|
|
2601
|
-
*
|
|
2602
|
-
*
|
|
2549
|
+
* Source-side body (`<what>; <why>`): emitted on the node that
|
|
2550
|
+
* AUTHORED a link whose target resolves to a reserved name. Reports the
|
|
2551
|
+
* fact (the runtime built-in shadows this edge); it deliberately does
|
|
2552
|
+
* NOT assert a confidence number, since the value is owned by the
|
|
2553
|
+
* `score`-phase scorers and may vary or be absent. The shared
|
|
2554
|
+
* `formatFinding` helper wraps it with the backtick target subject and
|
|
2555
|
+
* the `L<line>:` location prefix.
|
|
2603
2556
|
*/
|
|
2604
|
-
linkMessage: "
|
|
2605
|
-
/**
|
|
2606
|
-
|
|
2607
|
-
/** Location suffix after the built-in parens, several detection sites. */
|
|
2608
|
-
wherePlural: " (lines {{lines}})"
|
|
2557
|
+
linkMessage: "Reserved name; resolves to the {{provider}} built-in ({{reservedKind}} `{{reservedPath}}`), the built-in shadows this edge",
|
|
2558
|
+
/** Remediation hint for the source-side finding. */
|
|
2559
|
+
linkFixSummary: "Rename the target file or its frontmatter.name."
|
|
2609
2560
|
};
|
|
2610
2561
|
|
|
2611
2562
|
// plugins/core/analyzers/name-reserved/index.ts
|
|
@@ -2616,10 +2567,12 @@ var nameReservedAnalyzer = {
|
|
|
2616
2567
|
kind: "analyzer",
|
|
2617
2568
|
description: "Flags two kinds of reserved-name collision: a file whose name shadows a built-in command of the active runtime, and a link that resolves to one of those reserved names.",
|
|
2618
2569
|
mode: "deterministic",
|
|
2570
|
+
phase: "score",
|
|
2619
2571
|
// eslint-disable-next-line complexity
|
|
2620
2572
|
evaluate(ctx) {
|
|
2621
2573
|
const reserved = ctx.reservedNodePaths;
|
|
2622
2574
|
if (!reserved || reserved.size === 0) return [];
|
|
2575
|
+
const adjust = ctx.adjustConfidence;
|
|
2623
2576
|
const byPath3 = /* @__PURE__ */ new Map();
|
|
2624
2577
|
for (const node of ctx.nodes) byPath3.set(node.path, node);
|
|
2625
2578
|
const issues = [];
|
|
@@ -2630,32 +2583,38 @@ var nameReservedAnalyzer = {
|
|
|
2630
2583
|
analyzerId: ID18,
|
|
2631
2584
|
severity: "warn",
|
|
2632
2585
|
nodeIds: [node.path],
|
|
2633
|
-
message:
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2586
|
+
message: formatFinding({
|
|
2587
|
+
body: tx(NAME_RESERVED_TEXTS.message, {
|
|
2588
|
+
provider: node.provider,
|
|
2589
|
+
kind: node.kind
|
|
2590
|
+
})
|
|
2637
2591
|
}),
|
|
2592
|
+
fix: { summary: tx(NAME_RESERVED_TEXTS.fixSummary) },
|
|
2638
2593
|
data: { provider: node.provider, kind: node.kind, surface: "target" }
|
|
2639
2594
|
});
|
|
2640
2595
|
}
|
|
2641
2596
|
for (const link of ctx.links) {
|
|
2642
|
-
if (link.confidence !== RESERVED_TARGET_CONFIDENCE) continue;
|
|
2643
2597
|
const reservedPath = link.resolvedTarget;
|
|
2644
2598
|
if (!reservedPath || !reserved.has(reservedPath)) continue;
|
|
2645
2599
|
const reservedNode = byPath3.get(reservedPath);
|
|
2646
2600
|
if (!reservedNode) continue;
|
|
2601
|
+
if (adjust) {
|
|
2602
|
+
adjust(link, { kind: "delta", value: -RESERVED_PENALTY });
|
|
2603
|
+
}
|
|
2647
2604
|
issues.push({
|
|
2648
2605
|
analyzerId: ID18,
|
|
2649
2606
|
severity: "warn",
|
|
2650
2607
|
nodeIds: [link.source],
|
|
2651
|
-
message:
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2608
|
+
message: formatFinding({
|
|
2609
|
+
subject: link.target,
|
|
2610
|
+
lines: linkLines(link),
|
|
2611
|
+
body: tx(NAME_RESERVED_TEXTS.linkMessage, {
|
|
2612
|
+
provider: reservedNode.provider,
|
|
2613
|
+
reservedKind: reservedNode.kind,
|
|
2614
|
+
reservedPath: reservedNode.path
|
|
2615
|
+
})
|
|
2658
2616
|
}),
|
|
2617
|
+
fix: { summary: tx(NAME_RESERVED_TEXTS.linkFixSummary) },
|
|
2659
2618
|
data: {
|
|
2660
2619
|
target: link.target,
|
|
2661
2620
|
kind: link.kind,
|
|
@@ -2669,25 +2628,29 @@ var nameReservedAnalyzer = {
|
|
|
2669
2628
|
return issues;
|
|
2670
2629
|
}
|
|
2671
2630
|
};
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2631
|
+
|
|
2632
|
+
// plugins/core/stability.ts
|
|
2633
|
+
var STABILITY_VALUES = ["experimental", "stable", "deprecated"];
|
|
2634
|
+
function isStability(value) {
|
|
2635
|
+
return value === "experimental" || value === "stable" || value === "deprecated";
|
|
2636
|
+
}
|
|
2637
|
+
function readEffectiveStability(node) {
|
|
2638
|
+
const fromAnn = node.sidecar?.annotations?.["stability"];
|
|
2639
|
+
if (isStability(fromAnn)) return fromAnn;
|
|
2640
|
+
const legacy = readLegacyMetadataStability(node.frontmatter);
|
|
2641
|
+
return isStability(legacy) ? legacy : null;
|
|
2642
|
+
}
|
|
2643
|
+
function readLegacyMetadataStability(fm) {
|
|
2644
|
+
if (!fm) return void 0;
|
|
2645
|
+
const meta = fm["metadata"];
|
|
2646
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return void 0;
|
|
2647
|
+
return meta["stability"];
|
|
2677
2648
|
}
|
|
2678
2649
|
|
|
2679
2650
|
// plugins/core/analyzers/node-stability/text.ts
|
|
2680
2651
|
var NODE_STABILITY_TEXTS = {
|
|
2681
|
-
/**
|
|
2682
|
-
|
|
2683
|
-
/** Prompt label for the enum-pick stability input. */
|
|
2684
|
-
promptLabel: "Stability",
|
|
2685
|
-
/** Prompt option label for the `experimental` stage. */
|
|
2686
|
-
optionExperimental: "Experimental",
|
|
2687
|
-
/** Prompt option label for the `stable` stage. */
|
|
2688
|
-
optionStable: "Stable",
|
|
2689
|
-
/** Prompt option label for the `deprecated` stage. */
|
|
2690
|
-
optionDeprecated: "Deprecated"
|
|
2652
|
+
/** Issue body (`<what>; <why>`) for a deprecated-marked node. */
|
|
2653
|
+
deprecated: "Marked deprecated; avoid using it"
|
|
2691
2654
|
};
|
|
2692
2655
|
|
|
2693
2656
|
// plugins/core/analyzers/node-stability/index.ts
|
|
@@ -2708,159 +2671,63 @@ var deprecated = {
|
|
|
2708
2671
|
emitWhenEmpty: false,
|
|
2709
2672
|
priority: 10
|
|
2710
2673
|
};
|
|
2711
|
-
var setStabilityButton = {
|
|
2712
|
-
slot: "inspector.action.button",
|
|
2713
|
-
priority: 15
|
|
2714
|
-
};
|
|
2715
2674
|
var nodeStabilityAnalyzer = {
|
|
2716
2675
|
id: ID19,
|
|
2717
2676
|
pluginId: CORE_PLUGIN_ID,
|
|
2718
2677
|
kind: "analyzer",
|
|
2719
|
-
description: "
|
|
2678
|
+
description: "Surfaces a node's stability stage on the card: `deprecated` as a chip plus a finding, `experimental` as a chip only; `stable` and unset stay silent.",
|
|
2720
2679
|
mode: "deterministic",
|
|
2721
|
-
ui: { experimental, deprecated
|
|
2680
|
+
ui: { experimental, deprecated },
|
|
2722
2681
|
evaluate(ctx) {
|
|
2723
2682
|
const issues = [];
|
|
2724
2683
|
for (const node of ctx.nodes) {
|
|
2725
|
-
const stability =
|
|
2726
|
-
if (node.sidecar?.present === true) {
|
|
2727
|
-
emitSetStabilityButton(ctx, node.path, stability ?? "stable");
|
|
2728
|
-
}
|
|
2684
|
+
const stability = readEffectiveStability(node);
|
|
2729
2685
|
if (stability === "experimental") {
|
|
2730
2686
|
ctx.emitContribution(node.path, experimental, {
|
|
2731
2687
|
value: 0,
|
|
2732
2688
|
tooltip: EXPERIMENTAL_TOOLTIP
|
|
2733
2689
|
});
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
});
|
|
2747
|
-
|
|
2748
|
-
analyzerId: ID19,
|
|
2749
|
-
severity: "warn",
|
|
2750
|
-
nodeIds: [node.path],
|
|
2751
|
-
message: `Node '${node.path}' is marked deprecated: avoid in new code.`,
|
|
2752
|
-
data: { stability }
|
|
2753
|
-
});
|
|
2754
|
-
}
|
|
2755
|
-
}
|
|
2756
|
-
return issues;
|
|
2757
|
-
}
|
|
2758
|
-
};
|
|
2759
|
-
function readStability(node) {
|
|
2760
|
-
const fromAnn = node.sidecar?.annotations?.["stability"];
|
|
2761
|
-
if (isStability(fromAnn)) return fromAnn;
|
|
2762
|
-
const legacy = readLegacyMetadataStability(node.frontmatter);
|
|
2763
|
-
return isStability(legacy) ? legacy : null;
|
|
2764
|
-
}
|
|
2765
|
-
function readLegacyMetadataStability(fm) {
|
|
2766
|
-
if (!fm) return void 0;
|
|
2767
|
-
const meta = fm["metadata"];
|
|
2768
|
-
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return void 0;
|
|
2769
|
-
return meta["stability"];
|
|
2770
|
-
}
|
|
2771
|
-
function isStability(value) {
|
|
2772
|
-
return value === "experimental" || value === "deprecated" || value === "stable";
|
|
2773
|
-
}
|
|
2774
|
-
function emitSetStabilityButton(ctx, nodePath, current) {
|
|
2775
|
-
ctx.emitContribution(nodePath, setStabilityButton, {
|
|
2776
|
-
actionId: "core/node-set-stability",
|
|
2777
|
-
label: NODE_STABILITY_TEXTS.setLabel,
|
|
2778
|
-
icon: "pi-flag",
|
|
2779
|
-
enabled: true,
|
|
2780
|
-
prompt: {
|
|
2781
|
-
inputType: "enum-pick",
|
|
2782
|
-
paramKey: "stability",
|
|
2783
|
-
label: NODE_STABILITY_TEXTS.promptLabel,
|
|
2784
|
-
options: [
|
|
2785
|
-
{ value: "experimental", label: NODE_STABILITY_TEXTS.optionExperimental },
|
|
2786
|
-
{ value: "stable", label: NODE_STABILITY_TEXTS.optionStable },
|
|
2787
|
-
{ value: "deprecated", label: NODE_STABILITY_TEXTS.optionDeprecated }
|
|
2788
|
-
],
|
|
2789
|
-
defaultValue: current
|
|
2790
|
-
}
|
|
2791
|
-
});
|
|
2792
|
-
}
|
|
2793
|
-
|
|
2794
|
-
// plugins/core/analyzers/node-superseded/text.ts
|
|
2795
|
-
var NODE_SUPERSEDED_TEXTS = {
|
|
2796
|
-
/**
|
|
2797
|
-
* Compact finding grammar: line 1 = the superseding artifact, line
|
|
2798
|
-
* 2 = what it means. The superseded node is the finding's own node,
|
|
2799
|
-
* so its path never appears in the message.
|
|
2800
|
-
*/
|
|
2801
|
-
message: "{{supersededBy}}:\nSupersedes this node."
|
|
2802
|
-
};
|
|
2803
|
-
|
|
2804
|
-
// plugins/core/analyzers/node-superseded/index.ts
|
|
2805
|
-
var ID20 = "node-superseded";
|
|
2806
|
-
var nodeSupersededAnalyzer = {
|
|
2807
|
-
id: ID20,
|
|
2808
|
-
pluginId: CORE_PLUGIN_ID,
|
|
2809
|
-
kind: "analyzer",
|
|
2810
|
-
description: "Marks nodes replaced by a newer one via `supersededBy`.",
|
|
2811
|
-
// Part of the experimental supersession feature: ships disabled by
|
|
2812
|
-
// default alongside the declarer (`core/supersede` button +
|
|
2813
|
-
// `core/node-supersede` action). With the declarer off by default a
|
|
2814
|
-
// user rarely produces `supersededBy` data, so surfacing it stays
|
|
2815
|
-
// experimental too; the operator opts the whole family in together.
|
|
2816
|
-
stability: "experimental",
|
|
2817
|
-
mode: "deterministic",
|
|
2818
|
-
evaluate(ctx) {
|
|
2819
|
-
const issues = [];
|
|
2820
|
-
for (const node of ctx.nodes) {
|
|
2821
|
-
const supersededBy = pickSupersededBy(node);
|
|
2822
|
-
if (supersededBy === null) continue;
|
|
2823
|
-
issues.push({
|
|
2824
|
-
analyzerId: ID20,
|
|
2825
|
-
severity: "info",
|
|
2826
|
-
nodeIds: [node.path],
|
|
2827
|
-
message: tx(NODE_SUPERSEDED_TEXTS.message, {
|
|
2828
|
-
path: node.path,
|
|
2829
|
-
supersededBy
|
|
2830
|
-
}),
|
|
2831
|
-
data: { supersededBy }
|
|
2832
|
-
});
|
|
2690
|
+
} else if (stability === "deprecated") {
|
|
2691
|
+
ctx.emitContribution(node.path, deprecated, {
|
|
2692
|
+
value: 0,
|
|
2693
|
+
tooltip: DEPRECATED_TOOLTIP,
|
|
2694
|
+
severity: "warn"
|
|
2695
|
+
});
|
|
2696
|
+
issues.push({
|
|
2697
|
+
analyzerId: ID19,
|
|
2698
|
+
severity: "warn",
|
|
2699
|
+
nodeIds: [node.path],
|
|
2700
|
+
message: formatFinding({ body: tx(NODE_STABILITY_TEXTS.deprecated) }),
|
|
2701
|
+
data: { stability }
|
|
2702
|
+
});
|
|
2703
|
+
}
|
|
2833
2704
|
}
|
|
2834
2705
|
return issues;
|
|
2835
2706
|
}
|
|
2836
2707
|
};
|
|
2837
|
-
function pickSupersededBy(node) {
|
|
2838
|
-
const sidecar = node.sidecar;
|
|
2839
|
-
if (!sidecar || sidecar.present !== true) return null;
|
|
2840
|
-
const ann = sidecar.annotations;
|
|
2841
|
-
if (!ann || typeof ann !== "object" || Array.isArray(ann)) return null;
|
|
2842
|
-
const value = ann["supersededBy"];
|
|
2843
|
-
if (typeof value !== "string" || value.length === 0) return null;
|
|
2844
|
-
return value;
|
|
2845
|
-
}
|
|
2846
2708
|
|
|
2847
2709
|
// plugins/core/analyzers/reference-broken/index.ts
|
|
2848
|
-
import { resolve as
|
|
2710
|
+
import { resolve as resolve2 } from "path";
|
|
2849
2711
|
|
|
2850
2712
|
// plugins/core/analyzers/reference-broken/text.ts
|
|
2851
2713
|
var REFERENCE_BROKEN_TEXTS = {
|
|
2852
2714
|
/**
|
|
2853
|
-
*
|
|
2854
|
-
*
|
|
2855
|
-
*
|
|
2856
|
-
*
|
|
2857
|
-
|
|
2715
|
+
* Diagnosis body (`<what>; <why>`). The shared `formatFinding` helper
|
|
2716
|
+
* wraps it with the backtick subject (the unresolved target) and the
|
|
2717
|
+
* `L<line>:` location prefix; the source is the finding's own node, so
|
|
2718
|
+
* it never appears in the message.
|
|
2719
|
+
*/
|
|
2720
|
+
message: "Broken {{kindLabel}}; target not found in the graph or on disk",
|
|
2721
|
+
/**
|
|
2722
|
+
* Remediation hint surfaced via `Issue.fix.summary`. Not autofixable:
|
|
2723
|
+
* the rule cannot tell which resolution the author wants. The folder
|
|
2724
|
+
* option maps to `scan.referencePaths` ("Folders for link validation"
|
|
2725
|
+
* in Settings), the rule's own escape hatch: it clears only PATH-style
|
|
2726
|
+
* breaks (the file exists on disk outside the indexed graph). A
|
|
2727
|
+
* trigger-style `/cmd` / `@agent` break is settled by the path/name or
|
|
2728
|
+
* removal options instead.
|
|
2858
2729
|
*/
|
|
2859
|
-
|
|
2860
|
-
/** Location suffix, one detection site. */
|
|
2861
|
-
whereSingle: " (line {{lines}})",
|
|
2862
|
-
/** Location suffix, several detection sites. */
|
|
2863
|
-
wherePlural: " (lines {{lines}})",
|
|
2730
|
+
fixSummary: "Fix the path or name, remove the broken link, or add its folder under Folders for link validation.",
|
|
2864
2731
|
/**
|
|
2865
2732
|
* Human noun per link kind for the message above. Fallback for an
|
|
2866
2733
|
* off-catalog kind: `<kind> link` (composed in the analyzer).
|
|
@@ -2869,7 +2736,6 @@ var REFERENCE_BROKEN_TEXTS = {
|
|
|
2869
2736
|
references: "reference",
|
|
2870
2737
|
mentions: "mention",
|
|
2871
2738
|
invokes: "invocation",
|
|
2872
|
-
supersedes: "supersession",
|
|
2873
2739
|
points: "pointer"
|
|
2874
2740
|
},
|
|
2875
2741
|
kindLabelFallback: "{{kind}} link",
|
|
@@ -2880,13 +2746,14 @@ var REFERENCE_BROKEN_TEXTS = {
|
|
|
2880
2746
|
};
|
|
2881
2747
|
|
|
2882
2748
|
// plugins/core/analyzers/reference-broken/index.ts
|
|
2883
|
-
var
|
|
2749
|
+
var ID20 = "reference-broken";
|
|
2884
2750
|
var referenceBrokenAnalyzer = {
|
|
2885
|
-
id:
|
|
2751
|
+
id: ID20,
|
|
2886
2752
|
pluginId: CORE_PLUGIN_ID,
|
|
2887
2753
|
kind: "analyzer",
|
|
2888
2754
|
description: "Flags arrows pointing at a node not part of the current scan.",
|
|
2889
2755
|
mode: "deterministic",
|
|
2756
|
+
phase: "score",
|
|
2890
2757
|
// No `ui` declaration: this analyzer used to emit a per-finding
|
|
2891
2758
|
// counter chip on `card.footer.right`, but that chip duplicated the
|
|
2892
2759
|
// aggregate severity counters now owned by `core/issue-counter`. The
|
|
@@ -2902,10 +2769,12 @@ var referenceBrokenAnalyzer = {
|
|
|
2902
2769
|
const broken = ctx.brokenLinks;
|
|
2903
2770
|
if (!broken || broken.size === 0) return [];
|
|
2904
2771
|
const refIndex = buildReferenceIndex(ctx);
|
|
2772
|
+
const adjust = ctx.adjustConfidence;
|
|
2905
2773
|
const issues = [];
|
|
2906
2774
|
for (const link of ctx.links) {
|
|
2907
2775
|
if (!broken.has(link)) continue;
|
|
2908
2776
|
if (refIndex && resolvesViaReferencePaths(link, refIndex)) continue;
|
|
2777
|
+
penalizeBrokenConfidence(adjust, link);
|
|
2909
2778
|
issues.push(buildIssue(link));
|
|
2910
2779
|
}
|
|
2911
2780
|
return issues;
|
|
@@ -2915,9 +2784,14 @@ function buildReferenceIndex(ctx) {
|
|
|
2915
2784
|
if (!ctx.referenceablePaths || ctx.referenceablePaths.size === 0 || !ctx.cwd) return null;
|
|
2916
2785
|
return { paths: ctx.referenceablePaths, cwd: ctx.cwd };
|
|
2917
2786
|
}
|
|
2787
|
+
function penalizeBrokenConfidence(adjust, link) {
|
|
2788
|
+
if (adjust) {
|
|
2789
|
+
adjust(link, { kind: "delta", value: -BROKEN_PENALTY });
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2918
2792
|
function buildIssue(link) {
|
|
2919
2793
|
return {
|
|
2920
|
-
analyzerId:
|
|
2794
|
+
analyzerId: ID20,
|
|
2921
2795
|
// `error`, not `warn`: a link whose target is not in the scan is a
|
|
2922
2796
|
// structural defect the operator must notice, and the card chip
|
|
2923
2797
|
// paints `danger` (red) to match. Per the chip-vs-issue policy in
|
|
@@ -2926,14 +2800,14 @@ function buildIssue(link) {
|
|
|
2926
2800
|
// and the global error count on the card.
|
|
2927
2801
|
severity: "error",
|
|
2928
2802
|
nodeIds: [link.source],
|
|
2929
|
-
message:
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
plural: REFERENCE_BROKEN_TEXTS.wherePlural
|
|
2803
|
+
message: formatFinding({
|
|
2804
|
+
subject: link.target,
|
|
2805
|
+
lines: linkLines(link),
|
|
2806
|
+
body: tx(REFERENCE_BROKEN_TEXTS.message, {
|
|
2807
|
+
kindLabel: REFERENCE_BROKEN_TEXTS.kindLabels[link.kind] ?? tx(REFERENCE_BROKEN_TEXTS.kindLabelFallback, { kind: link.kind })
|
|
2935
2808
|
})
|
|
2936
2809
|
}),
|
|
2810
|
+
fix: { summary: tx(REFERENCE_BROKEN_TEXTS.fixSummary) },
|
|
2937
2811
|
data: {
|
|
2938
2812
|
target: link.target,
|
|
2939
2813
|
kind: link.kind,
|
|
@@ -2943,7 +2817,7 @@ function buildIssue(link) {
|
|
|
2943
2817
|
}
|
|
2944
2818
|
function resolvesViaReferencePaths(link, refIndex) {
|
|
2945
2819
|
if (!isPathStyleLink(link)) return false;
|
|
2946
|
-
return refIndex.paths.has(
|
|
2820
|
+
return refIndex.paths.has(resolve2(refIndex.cwd, link.target));
|
|
2947
2821
|
}
|
|
2948
2822
|
function isPathStyleLink(link) {
|
|
2949
2823
|
const sigil = link.trigger?.normalizedTrigger?.charAt(0);
|
|
@@ -2954,29 +2828,36 @@ function isPathStyleLink(link) {
|
|
|
2954
2828
|
// plugins/core/analyzers/reference-redundant/text.ts
|
|
2955
2829
|
var REFERENCE_REDUNDANT_TEXTS = {
|
|
2956
2830
|
/**
|
|
2957
|
-
*
|
|
2958
|
-
*
|
|
2959
|
-
*
|
|
2960
|
-
*
|
|
2961
|
-
*
|
|
2962
|
-
*
|
|
2831
|
+
* Diagnosis body (`<what>; <why>`). Kind-agnostic wording ("links", not
|
|
2832
|
+
* "reference"): the redundancy can span different link kinds (e.g.
|
|
2833
|
+
* `invokes` + `references` to one node), so the message never names a
|
|
2834
|
+
* single kind. The shared `formatFinding` helper wraps it with the
|
|
2835
|
+
* backtick subject (the resolved target); no `L<line>:` prefix because
|
|
2836
|
+
* the per-occurrence line numbers stay inline in the rendered
|
|
2837
|
+
* occurrence list. Remediation lives in `fix.summary`, not the message.
|
|
2963
2838
|
* Occurrences are grouped BY TRIGGER: each distinct trigger text
|
|
2964
|
-
* appears once with its line numbers collapsed into one paren list.
|
|
2965
|
-
*
|
|
2839
|
+
* appears once with its line numbers collapsed into one paren list. The
|
|
2840
|
+
* source node is the finding's own node, so it never appears.
|
|
2966
2841
|
*/
|
|
2967
|
-
message: "
|
|
2842
|
+
message: "Redundant links; the target is reached {{count}} times: {{occurrences}}",
|
|
2843
|
+
/**
|
|
2844
|
+
* Remediation hint surfaced via `Issue.fix.summary`. Phrased as
|
|
2845
|
+
* optional: severity is `info` and keeping multiple forms can be
|
|
2846
|
+
* deliberate, so the hint offers consolidation OR keeping the overlap.
|
|
2847
|
+
*/
|
|
2848
|
+
fixSummary: "Consolidate the links into one, or keep the overlap deliberately.",
|
|
2968
2849
|
/** Inline separator between trigger groups in the message. */
|
|
2969
2850
|
occurrenceSeparator: ", ",
|
|
2970
2851
|
/** Per-trigger formatting: the trigger once, its lines grouped. */
|
|
2971
|
-
occurrence: "`{{trigger}}` ({{lines}})",
|
|
2852
|
+
occurrence: "`{{trigger}}` (L{{lines}})",
|
|
2972
2853
|
/** Placeholder for an occurrence whose extractor recorded no line. */
|
|
2973
2854
|
lineUnknown: "?"
|
|
2974
2855
|
};
|
|
2975
2856
|
|
|
2976
2857
|
// plugins/core/analyzers/reference-redundant/index.ts
|
|
2977
|
-
var
|
|
2858
|
+
var ID21 = "reference-redundant";
|
|
2978
2859
|
var referenceRedundantAnalyzer = {
|
|
2979
|
-
id:
|
|
2860
|
+
id: ID21,
|
|
2980
2861
|
pluginId: CORE_PLUGIN_ID,
|
|
2981
2862
|
kind: "analyzer",
|
|
2982
2863
|
description: "Flags when one node references the same target through two or more different links (e.g. a markdown link plus a `references:` entry).",
|
|
@@ -2999,14 +2880,17 @@ var referenceRedundantAnalyzer = {
|
|
|
2999
2880
|
const [source, resolvedTarget] = key.split("\0");
|
|
3000
2881
|
const flat = flattenOccurrences(links);
|
|
3001
2882
|
issues.push({
|
|
3002
|
-
analyzerId:
|
|
2883
|
+
analyzerId: ID21,
|
|
3003
2884
|
severity: "info",
|
|
3004
2885
|
nodeIds: [source],
|
|
3005
|
-
message:
|
|
3006
|
-
resolvedTarget,
|
|
3007
|
-
|
|
3008
|
-
|
|
2886
|
+
message: formatFinding({
|
|
2887
|
+
subject: resolvedTarget,
|
|
2888
|
+
body: tx(REFERENCE_REDUNDANT_TEXTS.message, {
|
|
2889
|
+
count: flat.length,
|
|
2890
|
+
occurrences: formatGroupedOccurrences(flat)
|
|
2891
|
+
})
|
|
3009
2892
|
}),
|
|
2893
|
+
fix: { summary: tx(REFERENCE_REDUNDANT_TEXTS.fixSummary) },
|
|
3010
2894
|
data: {
|
|
3011
2895
|
target: resolvedTarget,
|
|
3012
2896
|
resolvedTarget,
|
|
@@ -3069,7 +2953,7 @@ function formatGroupedOccurrences(occurrences) {
|
|
|
3069
2953
|
|
|
3070
2954
|
// kernel/adapters/schema-validators.ts
|
|
3071
2955
|
import { readFileSync as readFileSync2 } from "fs";
|
|
3072
|
-
import { dirname as dirname2, resolve as
|
|
2956
|
+
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
3073
2957
|
import { createRequire as createRequire2 } from "module";
|
|
3074
2958
|
import { Ajv2020 as Ajv20202 } from "ajv/dist/2020.js";
|
|
3075
2959
|
|
|
@@ -3137,14 +3021,14 @@ function buildSchemaValidators() {
|
|
|
3137
3021
|
});
|
|
3138
3022
|
applyAjvFormats(ajv);
|
|
3139
3023
|
for (const rel of SUPPORTING_SCHEMAS) {
|
|
3140
|
-
const file =
|
|
3024
|
+
const file = resolve3(specRoot, rel);
|
|
3141
3025
|
if (!existsSyncSafe(file)) continue;
|
|
3142
3026
|
const schema = JSON.parse(readFileSync2(file, "utf8"));
|
|
3143
3027
|
ajv.addSchema(schema);
|
|
3144
3028
|
}
|
|
3145
3029
|
const validators = /* @__PURE__ */ new Map();
|
|
3146
3030
|
for (const [name, rel] of Object.entries(SCHEMA_FILES)) {
|
|
3147
|
-
const file =
|
|
3031
|
+
const file = resolve3(specRoot, rel);
|
|
3148
3032
|
const schema = JSON.parse(readFileSync2(file, "utf8"));
|
|
3149
3033
|
const byId = typeof schema.$id === "string" ? ajv.getSchema(schema.$id) : void 0;
|
|
3150
3034
|
validators.set(name, byId ?? ajv.compile(schema));
|
|
@@ -3216,7 +3100,7 @@ function buildProviderFrontmatterValidator(providers) {
|
|
|
3216
3100
|
allowUnionTypes: true
|
|
3217
3101
|
});
|
|
3218
3102
|
applyAjvFormats(ajv);
|
|
3219
|
-
const baseFile =
|
|
3103
|
+
const baseFile = resolve3(specRoot, "schemas/frontmatter/base.schema.json");
|
|
3220
3104
|
const baseSchema = JSON.parse(readFileSync2(baseFile, "utf8"));
|
|
3221
3105
|
ajv.addSchema(baseSchema);
|
|
3222
3106
|
registerProviderAuxiliarySchemas(ajv, providers);
|
|
@@ -3315,16 +3199,25 @@ function existsSyncSafe(path) {
|
|
|
3315
3199
|
}
|
|
3316
3200
|
}
|
|
3317
3201
|
|
|
3202
|
+
// kernel/orchestrator/frontmatter-issue-ids.ts
|
|
3203
|
+
var FRONTMATTER_ISSUE_ANALYZERS = /* @__PURE__ */ new Set([
|
|
3204
|
+
"frontmatter-invalid",
|
|
3205
|
+
"frontmatter-malformed",
|
|
3206
|
+
"frontmatter-parse-error"
|
|
3207
|
+
]);
|
|
3208
|
+
|
|
3318
3209
|
// plugins/core/analyzers/schema-violation/text.ts
|
|
3319
3210
|
var SCHEMA_VIOLATION_TEXTS = {
|
|
3320
|
-
//
|
|
3321
|
-
//
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
/**
|
|
3325
|
-
|
|
3326
|
-
/** `
|
|
3327
|
-
|
|
3211
|
+
// Diagnosis bodies (`<what>; <why>`). The shared `formatFinding` helper
|
|
3212
|
+
// owns the subject / location chrome: the node + frontmatter findings
|
|
3213
|
+
// carry no subject (the affected node IS the finding's own node), the
|
|
3214
|
+
// link finding uses the link target as its subject.
|
|
3215
|
+
/** `Schema validation failed; <errors>` */
|
|
3216
|
+
nodeFailure: "Schema validation failed; {{errors}}",
|
|
3217
|
+
/** `<target>` subject + `Link failed schema validation; <errors>` */
|
|
3218
|
+
linkFailure: "Link failed schema validation; {{errors}}",
|
|
3219
|
+
/** `Missing required frontmatter; <missing>` */
|
|
3220
|
+
frontmatterBaseFailure: "Missing required frontmatter; {{missing}}",
|
|
3328
3221
|
/** Singular tooltip on the alert / chip when a node has exactly one validation failure. */
|
|
3329
3222
|
alertTooltipSingle: "Frontmatter or schema validation failed.",
|
|
3330
3223
|
/** Plural tooltip; `{{count}}` capped at 99 in the chip badge but the tooltip text shows the raw count. */
|
|
@@ -3332,9 +3225,9 @@ var SCHEMA_VIOLATION_TEXTS = {
|
|
|
3332
3225
|
};
|
|
3333
3226
|
|
|
3334
3227
|
// plugins/core/analyzers/schema-violation/index.ts
|
|
3335
|
-
var
|
|
3228
|
+
var ID22 = "schema-violation";
|
|
3336
3229
|
var schemaViolationAnalyzer = {
|
|
3337
|
-
id:
|
|
3230
|
+
id: ID22,
|
|
3338
3231
|
pluginId: CORE_PLUGIN_ID,
|
|
3339
3232
|
kind: "analyzer",
|
|
3340
3233
|
description: "Flags nodes or links that violate the project schemas.",
|
|
@@ -3355,10 +3248,11 @@ var schemaViolationAnalyzer = {
|
|
|
3355
3248
|
const validators = loadSchemaValidators();
|
|
3356
3249
|
const findings = [];
|
|
3357
3250
|
const perNode = /* @__PURE__ */ new Map();
|
|
3251
|
+
const kernelFlaggedNodes = collectKernelFlaggedNodes(ctx.accumulatedIssues);
|
|
3358
3252
|
for (const node of ctx.nodes) {
|
|
3359
3253
|
const before = findings.length;
|
|
3360
3254
|
collectNodeFindings(validators, node, findings);
|
|
3361
|
-
collectFrontmatterBaseFindings(node, findings);
|
|
3255
|
+
collectFrontmatterBaseFindings(node, findings, kernelFlaggedNodes);
|
|
3362
3256
|
if (findings.length > before) {
|
|
3363
3257
|
let worst = "warn";
|
|
3364
3258
|
for (let i = before; i < findings.length; i++) {
|
|
@@ -3385,17 +3279,27 @@ function collectNodeFindings(v, node, out) {
|
|
|
3385
3279
|
const result = v.validate("node", toNodeForSchema(node));
|
|
3386
3280
|
if (result.ok) return;
|
|
3387
3281
|
out.push({
|
|
3388
|
-
analyzerId:
|
|
3282
|
+
analyzerId: ID22,
|
|
3389
3283
|
severity: "error",
|
|
3390
3284
|
nodeIds: [node.path],
|
|
3391
|
-
message:
|
|
3392
|
-
|
|
3393
|
-
|
|
3285
|
+
message: formatFinding({
|
|
3286
|
+
body: tx(SCHEMA_VIOLATION_TEXTS.nodeFailure, {
|
|
3287
|
+
errors: result.errors
|
|
3288
|
+
})
|
|
3394
3289
|
}),
|
|
3395
3290
|
data: { target: "node", path: node.path }
|
|
3396
3291
|
});
|
|
3397
3292
|
}
|
|
3398
|
-
function
|
|
3293
|
+
function collectKernelFlaggedNodes(accumulated) {
|
|
3294
|
+
const flagged = /* @__PURE__ */ new Set();
|
|
3295
|
+
for (const issue of accumulated ?? []) {
|
|
3296
|
+
if (!FRONTMATTER_ISSUE_ANALYZERS.has(issue.analyzerId)) continue;
|
|
3297
|
+
for (const id of issue.nodeIds) flagged.add(id);
|
|
3298
|
+
}
|
|
3299
|
+
return flagged;
|
|
3300
|
+
}
|
|
3301
|
+
function collectFrontmatterBaseFindings(node, out, kernelFlagged) {
|
|
3302
|
+
if (kernelFlagged.has(node.path)) return;
|
|
3399
3303
|
if (node.provider === "markdown") return;
|
|
3400
3304
|
if (node.bytes.frontmatter === 0) return;
|
|
3401
3305
|
const fm = node.frontmatter ?? {};
|
|
@@ -3404,16 +3308,17 @@ function collectFrontmatterBaseFindings(node, out) {
|
|
|
3404
3308
|
if (isMissingStringField(fm, "description")) missing.push("description");
|
|
3405
3309
|
if (missing.length === 0) return;
|
|
3406
3310
|
out.push({
|
|
3407
|
-
analyzerId:
|
|
3311
|
+
analyzerId: ID22,
|
|
3408
3312
|
// `warn` (not `error`) so the default `sm scan` exit code stays
|
|
3409
3313
|
// 0 even when nodes are missing frontmatter base fields. Strict
|
|
3410
3314
|
// mode (`sm scan --strict`) still escalates to exit 1. Matches
|
|
3411
3315
|
// the `frontmatter-invalid` severity policy of the orchestrator.
|
|
3412
3316
|
severity: "warn",
|
|
3413
3317
|
nodeIds: [node.path],
|
|
3414
|
-
message:
|
|
3415
|
-
|
|
3416
|
-
|
|
3318
|
+
message: formatFinding({
|
|
3319
|
+
body: tx(SCHEMA_VIOLATION_TEXTS.frontmatterBaseFailure, {
|
|
3320
|
+
missing: missing.join(", ")
|
|
3321
|
+
})
|
|
3417
3322
|
}),
|
|
3418
3323
|
data: { target: "frontmatter", path: node.path, missing }
|
|
3419
3324
|
});
|
|
@@ -3426,13 +3331,14 @@ function collectLinkFindings(v, link, out) {
|
|
|
3426
3331
|
const result = v.validate("link", toLinkForSchema(link));
|
|
3427
3332
|
if (result.ok) return;
|
|
3428
3333
|
out.push({
|
|
3429
|
-
analyzerId:
|
|
3334
|
+
analyzerId: ID22,
|
|
3430
3335
|
severity: "error",
|
|
3431
3336
|
nodeIds: [link.source],
|
|
3432
|
-
message:
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3337
|
+
message: formatFinding({
|
|
3338
|
+
subject: link.target,
|
|
3339
|
+
body: tx(SCHEMA_VIOLATION_TEXTS.linkFailure, {
|
|
3340
|
+
errors: result.errors
|
|
3341
|
+
})
|
|
3436
3342
|
}),
|
|
3437
3343
|
data: { target: "link", source: link.source, to: link.target }
|
|
3438
3344
|
});
|
|
@@ -3466,282 +3372,6 @@ function toLinkForSchema(link) {
|
|
|
3466
3372
|
};
|
|
3467
3373
|
}
|
|
3468
3374
|
|
|
3469
|
-
// plugins/core/analyzers/signal-collision/text.ts
|
|
3470
|
-
var SIGNAL_COLLISION_TEXTS = {
|
|
3471
|
-
/**
|
|
3472
|
-
* Per-Signal warn issue: two extractors detected something at
|
|
3473
|
-
* overlapping byte ranges within the same node and the resolver
|
|
3474
|
-
* dropped the loser. Surfaces WHO lost, WHO won, and the tiebreak
|
|
3475
|
-
* reason so the operator can understand why a candidate edge did NOT
|
|
3476
|
-
* become a Link.
|
|
3477
|
-
*
|
|
3478
|
-
* Placeholders are deliberately verbose because this is one of the
|
|
3479
|
-
* few diagnostic surfaces where the operator may need to disambiguate
|
|
3480
|
-
* a confusing graph (e.g. a `[link](path)` followed by `@path` inside
|
|
3481
|
-
* the same paragraph, the markdown-link wins and the at-directive
|
|
3482
|
-
* silently disappears without this warning).
|
|
3483
|
-
*/
|
|
3484
|
-
message: "`{{loserRaw}}`:\nOverlap collision: {{loserExtractor}} (at {{loserRange}}) lost to {{winnerExtractor}} (at {{winnerRange}}) by {{reason}}; only the winning edge persists.",
|
|
3485
|
-
/**
|
|
3486
|
-
* Same warn but for the rare case the resolver rejected a Signal
|
|
3487
|
-
* because the operator disabled its extractor via
|
|
3488
|
-
* `plugins.<id>.extensions.<extId>.enabled`. Phase 4+ stub: today the
|
|
3489
|
-
* filter is not wired so this template is unreachable from the
|
|
3490
|
-
* resolver; documented now so the analyzer stays forward-compatible
|
|
3491
|
-
* with the upcoming filter pass.
|
|
3492
|
-
*/
|
|
3493
|
-
messageExtractorDisabled: "`{{loserRaw}}`:\nDropped: extension `{{extractorId}}` is disabled. Re-enable it in Settings or via `sm plugins enable`.",
|
|
3494
|
-
/**
|
|
3495
|
-
* Same warn but for the future confidence floor case. Phase 4+ stub:
|
|
3496
|
-
* today the resolver materialises every winning candidate regardless
|
|
3497
|
-
* of confidence, so this template is unreachable; documented for
|
|
3498
|
-
* forward compatibility.
|
|
3499
|
-
*/
|
|
3500
|
-
messageBelowFloor: "`{{loserRaw}}`:\nDropped: confidence {{confidence}} is below the threshold {{threshold}}."
|
|
3501
|
-
};
|
|
3502
|
-
|
|
3503
|
-
// plugins/core/analyzers/signal-collision/index.ts
|
|
3504
|
-
var ID24 = "signal-collision";
|
|
3505
|
-
var signalCollisionAnalyzer = {
|
|
3506
|
-
id: ID24,
|
|
3507
|
-
pluginId: CORE_PLUGIN_ID,
|
|
3508
|
-
kind: "analyzer",
|
|
3509
|
-
description: "Reports when two extractors fight over the same span of body text, or when a candidate link is dropped (extractor disabled, confidence too low) before it reaches the graph.",
|
|
3510
|
-
mode: "deterministic",
|
|
3511
|
-
evaluate(ctx) {
|
|
3512
|
-
const signals = ctx.signals;
|
|
3513
|
-
if (!signals || signals.length === 0) return [];
|
|
3514
|
-
const issues = [];
|
|
3515
|
-
for (const signal of signals) {
|
|
3516
|
-
const issue = makeIssue(signal);
|
|
3517
|
-
if (issue) issues.push(issue);
|
|
3518
|
-
}
|
|
3519
|
-
return issues;
|
|
3520
|
-
}
|
|
3521
|
-
};
|
|
3522
|
-
function makeIssue(signal) {
|
|
3523
|
-
const resolution = signal.resolution;
|
|
3524
|
-
if (!resolution || resolution.outcome !== "rejected") return null;
|
|
3525
|
-
if (resolution.rejectedBy) {
|
|
3526
|
-
const winner = resolution.rejectedBy;
|
|
3527
|
-
const winnerCandidate = signal.candidates[resolution.winnerIndex ?? 0];
|
|
3528
|
-
const loserRange = signal.range ? `${signal.range.start}-${signal.range.end}` : "unknown";
|
|
3529
|
-
const winnerRange = `${winner.range.start}-${winner.range.end}`;
|
|
3530
|
-
return {
|
|
3531
|
-
analyzerId: ID24,
|
|
3532
|
-
severity: "warn",
|
|
3533
|
-
nodeIds: [signal.source],
|
|
3534
|
-
message: tx(SIGNAL_COLLISION_TEXTS.message, {
|
|
3535
|
-
loserExtractor: winnerCandidate.extractorId,
|
|
3536
|
-
loserRaw: signal.raw,
|
|
3537
|
-
loserRange,
|
|
3538
|
-
winnerExtractor: winner.extractorId,
|
|
3539
|
-
winnerRange,
|
|
3540
|
-
reason: winner.reason
|
|
3541
|
-
}),
|
|
3542
|
-
data: {
|
|
3543
|
-
loser: {
|
|
3544
|
-
extractorId: winnerCandidate.extractorId,
|
|
3545
|
-
raw: signal.raw,
|
|
3546
|
-
range: signal.range ?? null,
|
|
3547
|
-
candidate: {
|
|
3548
|
-
kind: winnerCandidate.kind,
|
|
3549
|
-
target: winnerCandidate.target,
|
|
3550
|
-
confidence: winnerCandidate.confidence
|
|
3551
|
-
}
|
|
3552
|
-
},
|
|
3553
|
-
winner: {
|
|
3554
|
-
extractorId: winner.extractorId,
|
|
3555
|
-
range: winner.range
|
|
3556
|
-
},
|
|
3557
|
-
reason: winner.reason
|
|
3558
|
-
}
|
|
3559
|
-
};
|
|
3560
|
-
}
|
|
3561
|
-
if (resolution.extractorDisabled) {
|
|
3562
|
-
const loserRange = signal.range ? `${signal.range.start}-${signal.range.end}` : "unknown";
|
|
3563
|
-
return {
|
|
3564
|
-
analyzerId: ID24,
|
|
3565
|
-
severity: "warn",
|
|
3566
|
-
nodeIds: [signal.source],
|
|
3567
|
-
message: tx(SIGNAL_COLLISION_TEXTS.messageExtractorDisabled, {
|
|
3568
|
-
extractorId: resolution.extractorDisabled.extractorId,
|
|
3569
|
-
loserRaw: signal.raw,
|
|
3570
|
-
loserRange
|
|
3571
|
-
}),
|
|
3572
|
-
data: {
|
|
3573
|
-
extractorDisabled: resolution.extractorDisabled,
|
|
3574
|
-
raw: signal.raw,
|
|
3575
|
-
range: signal.range ?? null
|
|
3576
|
-
}
|
|
3577
|
-
};
|
|
3578
|
-
}
|
|
3579
|
-
if (resolution.belowFloor) {
|
|
3580
|
-
const loserRange = signal.range ? `${signal.range.start}-${signal.range.end}` : "unknown";
|
|
3581
|
-
const topCandidate = signal.candidates[0];
|
|
3582
|
-
return {
|
|
3583
|
-
analyzerId: ID24,
|
|
3584
|
-
severity: "warn",
|
|
3585
|
-
nodeIds: [signal.source],
|
|
3586
|
-
message: tx(SIGNAL_COLLISION_TEXTS.messageBelowFloor, {
|
|
3587
|
-
loserRaw: signal.raw,
|
|
3588
|
-
loserRange,
|
|
3589
|
-
confidence: topCandidate.confidence,
|
|
3590
|
-
threshold: resolution.belowFloor.threshold
|
|
3591
|
-
}),
|
|
3592
|
-
data: {
|
|
3593
|
-
belowFloor: resolution.belowFloor,
|
|
3594
|
-
raw: signal.raw,
|
|
3595
|
-
range: signal.range ?? null
|
|
3596
|
-
}
|
|
3597
|
-
};
|
|
3598
|
-
}
|
|
3599
|
-
return null;
|
|
3600
|
-
}
|
|
3601
|
-
|
|
3602
|
-
// plugins/core/analyzers/trigger-collision/text.ts
|
|
3603
|
-
var TRIGGER_COLLISION_TEXTS = {
|
|
3604
|
-
/**
|
|
3605
|
-
* Top-level message when `analyzeTriggerBucket` accumulated exactly one
|
|
3606
|
-
* cause part. Used for the advertiser-ambiguous-only, invocation-
|
|
3607
|
-
* ambiguous-only, and cross-kind-only branches.
|
|
3608
|
-
*/
|
|
3609
|
-
messageOnePart: '"{{normalized}}":\nTrigger collision: {{part}}.',
|
|
3610
|
-
/**
|
|
3611
|
-
* Top-level message when `analyzeTriggerBucket` accumulated two cause
|
|
3612
|
-
* parts (advertiser-ambiguous AND invocation-ambiguous fire together).
|
|
3613
|
-
* The joiner lives inside the template so future locales can adapt it
|
|
3614
|
-
* (e.g. `'; y '` in Spanish) without touching the rule code.
|
|
3615
|
-
*/
|
|
3616
|
-
messageTwoParts: '"{{normalized}}":\nTrigger collision: {{first}}; and {{second}}.',
|
|
3617
|
-
/** `<n> advertisers: <list>` part, fires on the advertiser-ambiguous branch. */
|
|
3618
|
-
partAdvertisers: "{{count}} advertisers: {{paths}}",
|
|
3619
|
-
/** `<n> invocation forms: <list>` part, fires on the invocation-ambiguous branch. */
|
|
3620
|
-
partInvocations: "{{count}} invocation forms: {{forms}}",
|
|
3621
|
-
/** Singular cross-kind cause: `non-canonical invocation <form> against advertiser <path>`. */
|
|
3622
|
-
partNonCanonicalSingular: "non-canonical invocation {{forms}} against advertiser {{advertiser}}",
|
|
3623
|
-
/** Plural cross-kind cause: `non-canonical invocations <forms> against advertiser <path>`. */
|
|
3624
|
-
partNonCanonicalPlural: "non-canonical invocations {{forms}} against advertiser {{advertiser}}"
|
|
3625
|
-
};
|
|
3626
|
-
|
|
3627
|
-
// plugins/core/analyzers/trigger-collision/index.ts
|
|
3628
|
-
var ID25 = "trigger-collision";
|
|
3629
|
-
var ADVERTISING_KINDS = /* @__PURE__ */ new Set([
|
|
3630
|
-
"command",
|
|
3631
|
-
"skill",
|
|
3632
|
-
"agent"
|
|
3633
|
-
]);
|
|
3634
|
-
var triggerCollisionAnalyzer = {
|
|
3635
|
-
id: ID25,
|
|
3636
|
-
pluginId: CORE_PLUGIN_ID,
|
|
3637
|
-
kind: "analyzer",
|
|
3638
|
-
mode: "deterministic",
|
|
3639
|
-
description: "Flags two or more nodes that claim the same `/command` or `@agent` name.",
|
|
3640
|
-
// Two claim-collection passes (advertisement + invocation) feeding
|
|
3641
|
-
// the bucket map. Per-bucket analysis lives in `analyzeTriggerBucket`.
|
|
3642
|
-
// eslint-disable-next-line complexity
|
|
3643
|
-
evaluate(ctx) {
|
|
3644
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
3645
|
-
const push = (key, claim) => {
|
|
3646
|
-
const bucket = buckets.get(key) ?? [];
|
|
3647
|
-
bucket.push(claim);
|
|
3648
|
-
buckets.set(key, bucket);
|
|
3649
|
-
};
|
|
3650
|
-
for (const node of ctx.nodes) {
|
|
3651
|
-
if (!ADVERTISING_KINDS.has(node.kind)) continue;
|
|
3652
|
-
const raw = node.frontmatter?.["name"];
|
|
3653
|
-
if (typeof raw !== "string" || raw.length === 0) continue;
|
|
3654
|
-
const normalized = `/${normalizeTrigger(raw)}`;
|
|
3655
|
-
if (normalized === "/") continue;
|
|
3656
|
-
push(normalized, {
|
|
3657
|
-
kind: "advertiser",
|
|
3658
|
-
token: node.path,
|
|
3659
|
-
nodeId: node.path,
|
|
3660
|
-
canonicalForm: `/${raw}`
|
|
3661
|
-
});
|
|
3662
|
-
}
|
|
3663
|
-
for (const link of ctx.links) {
|
|
3664
|
-
const normalized = link.trigger?.normalizedTrigger;
|
|
3665
|
-
if (!normalized) continue;
|
|
3666
|
-
push(normalized, {
|
|
3667
|
-
kind: "invocation",
|
|
3668
|
-
token: link.target,
|
|
3669
|
-
nodeId: link.source
|
|
3670
|
-
});
|
|
3671
|
-
}
|
|
3672
|
-
const issues = [];
|
|
3673
|
-
for (const [normalized, claims] of buckets) {
|
|
3674
|
-
const issue = analyzeTriggerBucket(normalized, claims);
|
|
3675
|
-
if (issue) issues.push(issue);
|
|
3676
|
-
}
|
|
3677
|
-
return issues;
|
|
3678
|
-
}
|
|
3679
|
-
};
|
|
3680
|
-
function analyzeTriggerBucket(normalized, claims) {
|
|
3681
|
-
const advertiserPaths = [
|
|
3682
|
-
...new Set(claims.filter((c) => c.kind === "advertiser").map((c) => c.token))
|
|
3683
|
-
].sort();
|
|
3684
|
-
const invocationTargets = [
|
|
3685
|
-
...new Set(claims.filter((c) => c.kind === "invocation").map((c) => c.token))
|
|
3686
|
-
].sort();
|
|
3687
|
-
const advertisers = claims.filter(
|
|
3688
|
-
(c) => c.kind === "advertiser"
|
|
3689
|
-
);
|
|
3690
|
-
const advertiserAmbiguous = advertiserPaths.length >= 2;
|
|
3691
|
-
const invocationAmbiguous = invocationTargets.length >= 2;
|
|
3692
|
-
const canonicalForms = new Set(advertisers.map((a) => a.canonicalForm));
|
|
3693
|
-
const nonCanonicalInvocations = invocationTargets.filter((t) => !canonicalForms.has(t));
|
|
3694
|
-
const crossKindAmbiguous = advertiserPaths.length === 1 && nonCanonicalInvocations.length >= 1;
|
|
3695
|
-
if (!advertiserAmbiguous && !invocationAmbiguous && !crossKindAmbiguous) {
|
|
3696
|
-
return null;
|
|
3697
|
-
}
|
|
3698
|
-
const nodeIds = [...new Set(claims.map((c) => c.nodeId))].sort();
|
|
3699
|
-
const parts = [];
|
|
3700
|
-
if (advertiserAmbiguous) {
|
|
3701
|
-
parts.push(
|
|
3702
|
-
tx(TRIGGER_COLLISION_TEXTS.partAdvertisers, {
|
|
3703
|
-
count: advertiserPaths.length,
|
|
3704
|
-
paths: advertiserPaths.join(", ")
|
|
3705
|
-
})
|
|
3706
|
-
);
|
|
3707
|
-
}
|
|
3708
|
-
if (invocationAmbiguous) {
|
|
3709
|
-
parts.push(
|
|
3710
|
-
tx(TRIGGER_COLLISION_TEXTS.partInvocations, {
|
|
3711
|
-
count: invocationTargets.length,
|
|
3712
|
-
forms: invocationTargets.join(", ")
|
|
3713
|
-
})
|
|
3714
|
-
);
|
|
3715
|
-
} else if (crossKindAmbiguous) {
|
|
3716
|
-
const template = nonCanonicalInvocations.length > 1 ? TRIGGER_COLLISION_TEXTS.partNonCanonicalPlural : TRIGGER_COLLISION_TEXTS.partNonCanonicalSingular;
|
|
3717
|
-
parts.push(
|
|
3718
|
-
tx(template, {
|
|
3719
|
-
forms: nonCanonicalInvocations.join(", "),
|
|
3720
|
-
advertiser: advertiserPaths[0]
|
|
3721
|
-
})
|
|
3722
|
-
);
|
|
3723
|
-
}
|
|
3724
|
-
const message = parts.length === 2 ? tx(TRIGGER_COLLISION_TEXTS.messageTwoParts, {
|
|
3725
|
-
normalized,
|
|
3726
|
-
first: parts[0],
|
|
3727
|
-
second: parts[1]
|
|
3728
|
-
}) : tx(TRIGGER_COLLISION_TEXTS.messageOnePart, {
|
|
3729
|
-
normalized,
|
|
3730
|
-
part: parts[0]
|
|
3731
|
-
});
|
|
3732
|
-
return {
|
|
3733
|
-
analyzerId: ID25,
|
|
3734
|
-
severity: "error",
|
|
3735
|
-
nodeIds,
|
|
3736
|
-
message,
|
|
3737
|
-
data: {
|
|
3738
|
-
normalizedTrigger: normalized,
|
|
3739
|
-
invocationTargets,
|
|
3740
|
-
advertiserPaths
|
|
3741
|
-
}
|
|
3742
|
-
};
|
|
3743
|
-
}
|
|
3744
|
-
|
|
3745
3375
|
// kernel/util/safe-text.ts
|
|
3746
3376
|
var ANSI_ESCAPE_RE = /[][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
|
|
3747
3377
|
var C0_CONTROL_RE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g;
|
|
@@ -3770,13 +3400,13 @@ var ASCII_FORMATTER_TEXTS = {
|
|
|
3770
3400
|
};
|
|
3771
3401
|
|
|
3772
3402
|
// plugins/core/formatters/ascii/index.ts
|
|
3773
|
-
var
|
|
3403
|
+
var ID23 = "ascii";
|
|
3774
3404
|
var KIND_ORDER = ["agent", "command", "skill", "markdown"];
|
|
3775
3405
|
var asciiFormatter = {
|
|
3776
|
-
id:
|
|
3406
|
+
id: ID23,
|
|
3777
3407
|
pluginId: CORE_PLUGIN_ID,
|
|
3778
3408
|
kind: "formatter",
|
|
3779
|
-
formatId:
|
|
3409
|
+
formatId: ID23,
|
|
3780
3410
|
description: "Renders the scan as plain text in three sections: nodes (grouped by kind), arrows, and issues. Used by `sm scan --format ascii`.",
|
|
3781
3411
|
// ASCII tree formatter, header + per-kind sections + per-issue
|
|
3782
3412
|
// section. Each section iterates and renders; splitting per section
|
|
@@ -3870,13 +3500,13 @@ function renderSection(out, kind, group) {
|
|
|
3870
3500
|
}
|
|
3871
3501
|
|
|
3872
3502
|
// plugins/core/formatters/json/index.ts
|
|
3873
|
-
var
|
|
3503
|
+
var ID24 = "json";
|
|
3874
3504
|
var jsonFormatter = {
|
|
3875
|
-
id:
|
|
3505
|
+
id: ID24,
|
|
3876
3506
|
pluginId: CORE_PLUGIN_ID,
|
|
3877
3507
|
kind: "formatter",
|
|
3878
3508
|
description: "Renders the persisted scan as JSON (conforms to `scan-result.schema.json`). Used by `sm graph --format json` and `GET /api/graph?format=json`.",
|
|
3879
|
-
formatId:
|
|
3509
|
+
formatId: ID24,
|
|
3880
3510
|
format(ctx) {
|
|
3881
3511
|
if (ctx.scanResult !== void 0) {
|
|
3882
3512
|
return JSON.stringify(ctx.scanResult);
|
|
@@ -3891,7 +3521,7 @@ var jsonFormatter = {
|
|
|
3891
3521
|
|
|
3892
3522
|
// kernel/sidecar/parse.ts
|
|
3893
3523
|
import { existsSync, readFileSync as readFileSync3 } from "fs";
|
|
3894
|
-
import { dirname as dirname3, resolve as
|
|
3524
|
+
import { dirname as dirname3, resolve as resolve4 } from "path";
|
|
3895
3525
|
import { createRequire as createRequire3 } from "module";
|
|
3896
3526
|
import { Ajv2020 as Ajv20203 } from "ajv/dist/2020.js";
|
|
3897
3527
|
import yaml from "js-yaml";
|
|
@@ -3993,10 +3623,10 @@ function getSidecarValidator() {
|
|
|
3993
3623
|
applyAjvFormats(ajv);
|
|
3994
3624
|
const specRoot = resolveSpecRoot2();
|
|
3995
3625
|
const annotationsSchema = JSON.parse(
|
|
3996
|
-
readFileSync3(
|
|
3626
|
+
readFileSync3(resolve4(specRoot, "schemas/annotations.schema.json"), "utf8")
|
|
3997
3627
|
);
|
|
3998
3628
|
const sidecarSchema = JSON.parse(
|
|
3999
|
-
readFileSync3(
|
|
3629
|
+
readFileSync3(resolve4(specRoot, "schemas/sidecar.schema.json"), "utf8")
|
|
4000
3630
|
);
|
|
4001
3631
|
ajv.addSchema(annotationsSchema);
|
|
4002
3632
|
cachedSidecarValidator = ajv.compile(sidecarSchema);
|
|
@@ -4023,13 +3653,13 @@ var BUMP_TEXTS = {
|
|
|
4023
3653
|
};
|
|
4024
3654
|
|
|
4025
3655
|
// plugins/core/actions/node-bump/index.ts
|
|
4026
|
-
var
|
|
3656
|
+
var ID25 = "node-bump";
|
|
4027
3657
|
var bumpButton = {
|
|
4028
3658
|
slot: "inspector.action.button",
|
|
4029
3659
|
priority: 10
|
|
4030
3660
|
};
|
|
4031
3661
|
var nodeBumpAction = {
|
|
4032
|
-
id:
|
|
3662
|
+
id: ID25,
|
|
4033
3663
|
pluginId: CORE_PLUGIN_ID,
|
|
4034
3664
|
kind: "action",
|
|
4035
3665
|
description: "Marks a node as updated: bumps `annotations.version`, refreshes sidecar hashes, and records the timestamp.",
|
|
@@ -4037,8 +3667,9 @@ var nodeBumpAction = {
|
|
|
4037
3667
|
ui: { bumpButton },
|
|
4038
3668
|
project(ctx) {
|
|
4039
3669
|
for (const node of ctx.nodes) {
|
|
4040
|
-
if (node.
|
|
4041
|
-
|
|
3670
|
+
if (node.virtual === true) continue;
|
|
3671
|
+
const enabled = node.sidecar?.present !== true || staleStatus2(node.sidecar) !== null;
|
|
3672
|
+
emitBumpButton(ctx, node.path, enabled);
|
|
4042
3673
|
}
|
|
4043
3674
|
},
|
|
4044
3675
|
// The runtime contract uses generic <TInput, TReport>; bump narrows
|
|
@@ -4107,15 +3738,39 @@ function pickCurrentVersion(overlay) {
|
|
|
4107
3738
|
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
4108
3739
|
}
|
|
4109
3740
|
|
|
3741
|
+
// plugins/core/actions/node-set-stability/text.ts
|
|
3742
|
+
var NODE_SET_STABILITY_TEXTS = {
|
|
3743
|
+
/** Label of the inspector action button that sets the lifecycle stage. */
|
|
3744
|
+
setLabel: "Set stability",
|
|
3745
|
+
/** Prompt label for the enum-pick stability input. */
|
|
3746
|
+
promptLabel: "Stability",
|
|
3747
|
+
/** Prompt option label for the `experimental` stage. */
|
|
3748
|
+
optionExperimental: "Experimental",
|
|
3749
|
+
/** Prompt option label for the `stable` stage. */
|
|
3750
|
+
optionStable: "Stable",
|
|
3751
|
+
/** Prompt option label for the `deprecated` stage. */
|
|
3752
|
+
optionDeprecated: "Deprecated"
|
|
3753
|
+
};
|
|
3754
|
+
|
|
4110
3755
|
// plugins/core/actions/node-set-stability/index.ts
|
|
4111
|
-
var
|
|
4112
|
-
var
|
|
3756
|
+
var ID26 = "node-set-stability";
|
|
3757
|
+
var setStabilityButton = {
|
|
3758
|
+
slot: "inspector.action.button",
|
|
3759
|
+
priority: 15
|
|
3760
|
+
};
|
|
4113
3761
|
var nodeSetStabilityAction = {
|
|
4114
|
-
id:
|
|
3762
|
+
id: ID26,
|
|
4115
3763
|
pluginId: CORE_PLUGIN_ID,
|
|
4116
3764
|
kind: "action",
|
|
4117
3765
|
description: "Sets the lifecycle stage of the current node (writes `stability` to the sidecar).",
|
|
4118
3766
|
mode: "deterministic",
|
|
3767
|
+
ui: { setStabilityButton },
|
|
3768
|
+
project(ctx) {
|
|
3769
|
+
for (const node of ctx.nodes) {
|
|
3770
|
+
if (node.virtual === true) continue;
|
|
3771
|
+
emitSetStabilityButton(ctx, node);
|
|
3772
|
+
}
|
|
3773
|
+
},
|
|
4119
3774
|
// The runtime contract uses generic <TInput, TReport>; this narrows
|
|
4120
3775
|
// both. The cast is the standard pattern for built-ins that want
|
|
4121
3776
|
// typed local I/O while staying compatible with the open generic.
|
|
@@ -4124,6 +3779,27 @@ var nodeSetStabilityAction = {
|
|
|
4124
3779
|
return invokeSetStability(input, ctx);
|
|
4125
3780
|
}
|
|
4126
3781
|
};
|
|
3782
|
+
function emitSetStabilityButton(ctx, node) {
|
|
3783
|
+
ctx.emitContribution(node.path, setStabilityButton, {
|
|
3784
|
+
actionId: "core/node-set-stability",
|
|
3785
|
+
label: NODE_SET_STABILITY_TEXTS.setLabel,
|
|
3786
|
+
icon: "pi-flag",
|
|
3787
|
+
enabled: true,
|
|
3788
|
+
prompt: {
|
|
3789
|
+
inputType: "enum-pick",
|
|
3790
|
+
paramKey: "stability",
|
|
3791
|
+
label: NODE_SET_STABILITY_TEXTS.promptLabel,
|
|
3792
|
+
options: [
|
|
3793
|
+
{ value: "experimental", label: NODE_SET_STABILITY_TEXTS.optionExperimental },
|
|
3794
|
+
{ value: "stable", label: NODE_SET_STABILITY_TEXTS.optionStable },
|
|
3795
|
+
{ value: "deprecated", label: NODE_SET_STABILITY_TEXTS.optionDeprecated }
|
|
3796
|
+
],
|
|
3797
|
+
// Pre-load the node's current stage so the picker opens on the active
|
|
3798
|
+
// value; `stable` when nothing is set yet.
|
|
3799
|
+
defaultValue: readEffectiveStability(node) ?? "stable"
|
|
3800
|
+
}
|
|
3801
|
+
});
|
|
3802
|
+
}
|
|
4127
3803
|
function invokeSetStability(input, ctx) {
|
|
4128
3804
|
const stability = input.stability;
|
|
4129
3805
|
if (!STABILITY_VALUES.includes(stability)) {
|
|
@@ -4159,13 +3835,13 @@ var TAGS_TEXTS = {
|
|
|
4159
3835
|
};
|
|
4160
3836
|
|
|
4161
3837
|
// plugins/core/actions/node-set-tags/index.ts
|
|
4162
|
-
var
|
|
3838
|
+
var ID27 = "node-set-tags";
|
|
4163
3839
|
var setTagsButton = {
|
|
4164
3840
|
slot: "inspector.action.button",
|
|
4165
3841
|
priority: 15
|
|
4166
3842
|
};
|
|
4167
3843
|
var nodeSetTagsAction = {
|
|
4168
|
-
id:
|
|
3844
|
+
id: ID27,
|
|
4169
3845
|
pluginId: CORE_PLUGIN_ID,
|
|
4170
3846
|
kind: "action",
|
|
4171
3847
|
description: "Sets the taxonomy tags of the current node (writes `tags` to the sidecar; whole-array replace).",
|
|
@@ -4173,7 +3849,7 @@ var nodeSetTagsAction = {
|
|
|
4173
3849
|
ui: { setTagsButton },
|
|
4174
3850
|
project(ctx) {
|
|
4175
3851
|
for (const node of ctx.nodes) {
|
|
4176
|
-
if (node.
|
|
3852
|
+
if (node.virtual === true) continue;
|
|
4177
3853
|
emitSetTagsButton(ctx, node);
|
|
4178
3854
|
}
|
|
4179
3855
|
},
|
|
@@ -4186,129 +3862,28 @@ var nodeSetTagsAction = {
|
|
|
4186
3862
|
}
|
|
4187
3863
|
};
|
|
4188
3864
|
function emitSetTagsButton(ctx, node) {
|
|
4189
|
-
ctx.emitContribution(node.path, setTagsButton, {
|
|
4190
|
-
actionId: "core/node-set-tags",
|
|
4191
|
-
label: TAGS_TEXTS.editLabel,
|
|
4192
|
-
icon: "pi-tags",
|
|
4193
|
-
enabled: true,
|
|
4194
|
-
prompt: {
|
|
4195
|
-
inputType: "string-list",
|
|
4196
|
-
paramKey: "tags",
|
|
4197
|
-
label: TAGS_TEXTS.promptLabel,
|
|
4198
|
-
defaultValue: currentTags(node)
|
|
4199
|
-
}
|
|
4200
|
-
});
|
|
4201
|
-
}
|
|
4202
|
-
function currentTags(node) {
|
|
4203
|
-
const ann = node.sidecar?.annotations;
|
|
4204
|
-
if (!ann || typeof ann !== "object" || Array.isArray(ann)) return [];
|
|
4205
|
-
const value = ann["tags"];
|
|
4206
|
-
if (!Array.isArray(value)) return [];
|
|
4207
|
-
return value.filter((t) => typeof t === "string");
|
|
4208
|
-
}
|
|
4209
|
-
function invokeSetTags(input, ctx) {
|
|
4210
|
-
const tags = Array.isArray(input.tags) ? input.tags : [];
|
|
4211
|
-
const timestamp = ctx.now().toISOString();
|
|
4212
|
-
const write = {
|
|
4213
|
-
kind: "sidecar",
|
|
4214
|
-
path: sidecarPathFor(ctx.nodeAbsolutePath),
|
|
4215
|
-
changes: {
|
|
4216
|
-
identity: {
|
|
4217
|
-
path: ctx.node.path,
|
|
4218
|
-
bodyHash: ctx.node.bodyHash,
|
|
4219
|
-
frontmatterHash: ctx.node.frontmatterHash
|
|
4220
|
-
},
|
|
4221
|
-
annotations: { tags },
|
|
4222
|
-
audit: {
|
|
4223
|
-
lastBumpedAt: timestamp,
|
|
4224
|
-
lastBumpedBy: ctx.invoker
|
|
4225
|
-
}
|
|
4226
|
-
}
|
|
4227
|
-
};
|
|
4228
|
-
const report = { ok: true, tags };
|
|
4229
|
-
return { report, writes: [write] };
|
|
4230
|
-
}
|
|
4231
|
-
|
|
4232
|
-
// plugins/core/actions/node-supersede/text.ts
|
|
4233
|
-
var SUPERSEDE_TEXTS = {
|
|
4234
|
-
/** Label of the inspector action button that declares supersession. */
|
|
4235
|
-
supersedeLabel: "Supersede",
|
|
4236
|
-
/** Tooltip shown when the supersede button is disabled (already superseded). */
|
|
4237
|
-
supersedeDisabledReason: "Already superseded.",
|
|
4238
|
-
/** Tooltip shown when there is no other node to supersede this one. */
|
|
4239
|
-
supersedeNoTargetsReason: "No other node to supersede this one.",
|
|
4240
|
-
/** Prompt label for the target node-picker (enum-pick over the live node set). */
|
|
4241
|
-
supersedePromptLabel: "Superseded by"
|
|
4242
|
-
};
|
|
4243
|
-
|
|
4244
|
-
// plugins/core/actions/node-supersede/index.ts
|
|
4245
|
-
var ID31 = "node-supersede";
|
|
4246
|
-
var supersedeButton = {
|
|
4247
|
-
slot: "inspector.action.button",
|
|
4248
|
-
priority: 10
|
|
4249
|
-
};
|
|
4250
|
-
var nodeSupersedeAction = {
|
|
4251
|
-
id: ID31,
|
|
4252
|
-
pluginId: CORE_PLUGIN_ID,
|
|
4253
|
-
kind: "action",
|
|
4254
|
-
description: "Declares the current node as superseded by another (writes `supersededBy` to the sidecar).",
|
|
4255
|
-
// Ships disabled by default (the declarer feature is still settling its
|
|
4256
|
-
// node-picker UX). The button self-projection gates as a unit with the
|
|
4257
|
-
// invoke executor: an enabled button pointing at a disabled action
|
|
4258
|
-
// would error on click, so the whole action stays experimental.
|
|
4259
|
-
stability: "experimental",
|
|
4260
|
-
mode: "deterministic",
|
|
4261
|
-
ui: { supersedeButton },
|
|
4262
|
-
project(ctx) {
|
|
4263
|
-
const candidates = ctx.nodes.filter((n) => n.virtual !== true).map((n) => n.path);
|
|
4264
|
-
for (const node of ctx.nodes) {
|
|
4265
|
-
if (node.virtual === true) continue;
|
|
4266
|
-
const options = candidates.filter((p) => p !== node.path).map((p) => ({ value: p, label: p }));
|
|
4267
|
-
emitSupersedeButton(ctx, node, options);
|
|
4268
|
-
}
|
|
4269
|
-
},
|
|
4270
|
-
// The runtime contract uses generic <TInput, TReport>; supersede
|
|
4271
|
-
// narrows both. The cast is the standard pattern for built-ins that
|
|
4272
|
-
// want typed local I/O while staying compatible with the open generic.
|
|
4273
|
-
invoke(rawInput, ctx) {
|
|
4274
|
-
const input = rawInput ?? {};
|
|
4275
|
-
return invokeSupersede(input, ctx);
|
|
4276
|
-
}
|
|
4277
|
-
};
|
|
4278
|
-
function emitSupersedeButton(ctx, node, options) {
|
|
4279
|
-
const disabledReason = resolveDisabledReason(node, options.length);
|
|
4280
|
-
ctx.emitContribution(node.path, supersedeButton, {
|
|
4281
|
-
actionId: "core/node-supersede",
|
|
4282
|
-
label: SUPERSEDE_TEXTS.supersedeLabel,
|
|
4283
|
-
icon: "pi-arrow-right-arrow-left",
|
|
4284
|
-
enabled: disabledReason === void 0,
|
|
4285
|
-
...disabledReason === void 0 ? {} : { disabledReason },
|
|
3865
|
+
ctx.emitContribution(node.path, setTagsButton, {
|
|
3866
|
+
actionId: "core/node-set-tags",
|
|
3867
|
+
label: TAGS_TEXTS.editLabel,
|
|
3868
|
+
icon: "pi-tags",
|
|
3869
|
+
enabled: true,
|
|
4286
3870
|
prompt: {
|
|
4287
|
-
inputType: "
|
|
4288
|
-
paramKey: "
|
|
4289
|
-
label:
|
|
4290
|
-
|
|
3871
|
+
inputType: "string-list",
|
|
3872
|
+
paramKey: "tags",
|
|
3873
|
+
label: TAGS_TEXTS.promptLabel,
|
|
3874
|
+
defaultValue: currentTags(node)
|
|
4291
3875
|
}
|
|
4292
3876
|
});
|
|
4293
3877
|
}
|
|
4294
|
-
function
|
|
4295
|
-
|
|
4296
|
-
if (
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
const sidecar = node.sidecar;
|
|
4301
|
-
if (!sidecar || sidecar.present !== true) return false;
|
|
4302
|
-
const ann = sidecar.annotations;
|
|
4303
|
-
if (!ann || typeof ann !== "object" || Array.isArray(ann)) return false;
|
|
4304
|
-
const value = ann["supersededBy"];
|
|
4305
|
-
return typeof value === "string" && value.length > 0;
|
|
3878
|
+
function currentTags(node) {
|
|
3879
|
+
const ann = node.sidecar?.annotations;
|
|
3880
|
+
if (!ann || typeof ann !== "object" || Array.isArray(ann)) return [];
|
|
3881
|
+
const value = ann["tags"];
|
|
3882
|
+
if (!Array.isArray(value)) return [];
|
|
3883
|
+
return value.filter((t) => typeof t === "string");
|
|
4306
3884
|
}
|
|
4307
|
-
function
|
|
4308
|
-
const
|
|
4309
|
-
if (supersededBy === ctx.node.path) {
|
|
4310
|
-
return { report: { ok: false, reason: "self" } };
|
|
4311
|
-
}
|
|
3885
|
+
function invokeSetTags(input, ctx) {
|
|
3886
|
+
const tags = Array.isArray(input.tags) ? input.tags : [];
|
|
4312
3887
|
const timestamp = ctx.now().toISOString();
|
|
4313
3888
|
const write = {
|
|
4314
3889
|
kind: "sidecar",
|
|
@@ -4319,14 +3894,14 @@ function invokeSupersede(input, ctx) {
|
|
|
4319
3894
|
bodyHash: ctx.node.bodyHash,
|
|
4320
3895
|
frontmatterHash: ctx.node.frontmatterHash
|
|
4321
3896
|
},
|
|
4322
|
-
annotations: {
|
|
3897
|
+
annotations: { tags },
|
|
4323
3898
|
audit: {
|
|
4324
3899
|
lastBumpedAt: timestamp,
|
|
4325
3900
|
lastBumpedBy: ctx.invoker
|
|
4326
3901
|
}
|
|
4327
3902
|
}
|
|
4328
3903
|
};
|
|
4329
|
-
const report = { ok: true,
|
|
3904
|
+
const report = { ok: true, tags };
|
|
4330
3905
|
return { report, writes: [write] };
|
|
4331
3906
|
}
|
|
4332
3907
|
|
|
@@ -4536,7 +4111,7 @@ function writeJsonAtomic(path, content) {
|
|
|
4536
4111
|
}
|
|
4537
4112
|
|
|
4538
4113
|
// core/paths/db-path.ts
|
|
4539
|
-
import { join as join2, resolve as
|
|
4114
|
+
import { join as join2, resolve as resolve5 } from "path";
|
|
4540
4115
|
|
|
4541
4116
|
// kernel/util/skill-map-paths.ts
|
|
4542
4117
|
import { join } from "path";
|
|
@@ -4564,17 +4139,17 @@ var GITIGNORE_ENTRIES = [
|
|
|
4564
4139
|
`${SKILL_MAP_DIR}/${DB_FILENAME}`
|
|
4565
4140
|
];
|
|
4566
4141
|
function resolveDbPath(options) {
|
|
4567
|
-
if (options.db) return
|
|
4568
|
-
return
|
|
4142
|
+
if (options.db) return resolve5(options.db);
|
|
4143
|
+
return resolve5(options.cwd, DEFAULT_DB_REL);
|
|
4569
4144
|
}
|
|
4570
4145
|
function defaultProjectDbPath(ctx) {
|
|
4571
|
-
return
|
|
4146
|
+
return resolve5(ctx.cwd, DEFAULT_DB_REL);
|
|
4572
4147
|
}
|
|
4573
4148
|
function defaultProjectJobsDir(ctx) {
|
|
4574
|
-
return
|
|
4149
|
+
return resolve5(ctx.cwd, SKILL_MAP_DIR, JOBS_DIRNAME);
|
|
4575
4150
|
}
|
|
4576
4151
|
function defaultProjectPluginsDir(ctx) {
|
|
4577
|
-
return
|
|
4152
|
+
return resolve5(ctx.cwd, SKILL_MAP_DIR, PLUGINS_DIRNAME);
|
|
4578
4153
|
}
|
|
4579
4154
|
function defaultDbPath(scopeRoot) {
|
|
4580
4155
|
return join2(scopeRoot, SKILL_MAP_DIR, DB_FILENAME);
|
|
@@ -4814,7 +4389,6 @@ var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", ve
|
|
|
4814
4389
|
var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: VERSION };
|
|
4815
4390
|
var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: VERSION };
|
|
4816
4391
|
var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: VERSION };
|
|
4817
|
-
var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: VERSION };
|
|
4818
4392
|
var backtickPathExtractor2 = { ...backtickPathExtractor, pluginId: "core", version: VERSION };
|
|
4819
4393
|
var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: VERSION };
|
|
4820
4394
|
var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: VERSION };
|
|
@@ -4823,25 +4397,22 @@ var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, plugi
|
|
|
4823
4397
|
var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: VERSION };
|
|
4824
4398
|
var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: VERSION };
|
|
4825
4399
|
var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: VERSION };
|
|
4400
|
+
var extractorCollisionAnalyzer2 = { ...extractorCollisionAnalyzer, pluginId: "core", version: VERSION };
|
|
4826
4401
|
var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: VERSION };
|
|
4827
|
-
var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: VERSION };
|
|
4828
|
-
var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: VERSION };
|
|
4829
4402
|
var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: VERSION };
|
|
4403
|
+
var linkKindConflictAnalyzer2 = { ...linkKindConflictAnalyzer, pluginId: "core", version: VERSION };
|
|
4830
4404
|
var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: VERSION };
|
|
4405
|
+
var nameCollisionAnalyzer2 = { ...nameCollisionAnalyzer, pluginId: "core", version: VERSION };
|
|
4831
4406
|
var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: VERSION };
|
|
4832
4407
|
var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: VERSION };
|
|
4833
|
-
var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: VERSION };
|
|
4834
4408
|
var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: VERSION };
|
|
4835
4409
|
var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: VERSION };
|
|
4836
4410
|
var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: VERSION };
|
|
4837
|
-
var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: VERSION };
|
|
4838
|
-
var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: VERSION };
|
|
4839
4411
|
var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: VERSION };
|
|
4840
4412
|
var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: VERSION };
|
|
4841
4413
|
var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: VERSION };
|
|
4842
4414
|
var nodeSetStabilityAction2 = { ...nodeSetStabilityAction, pluginId: "core", version: VERSION };
|
|
4843
4415
|
var nodeSetTagsAction2 = { ...nodeSetTagsAction, pluginId: "core", version: VERSION };
|
|
4844
|
-
var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: VERSION };
|
|
4845
4416
|
var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: VERSION };
|
|
4846
4417
|
var builtInPlugins = [
|
|
4847
4418
|
{
|
|
@@ -4880,7 +4451,6 @@ var builtInPlugins = [
|
|
|
4880
4451
|
description: "Core extensions shared across providers: parsers, extractors, analyzers, actions, hooks, formatters, and the universal `.md` fallback provider.",
|
|
4881
4452
|
extensions: [
|
|
4882
4453
|
coreMarkdownProvider2,
|
|
4883
|
-
annotationsExtractor2,
|
|
4884
4454
|
backtickPathExtractor2,
|
|
4885
4455
|
externalUrlCounterExtractor2,
|
|
4886
4456
|
markdownLinkExtractor2,
|
|
@@ -4889,25 +4459,22 @@ var builtInPlugins = [
|
|
|
4889
4459
|
annotationOrphanAnalyzer2,
|
|
4890
4460
|
annotationStaleAnalyzer2,
|
|
4891
4461
|
contributionOrphanAnalyzer2,
|
|
4462
|
+
extractorCollisionAnalyzer2,
|
|
4892
4463
|
issueCounterAnalyzer2,
|
|
4893
|
-
jobFileOrphanAnalyzer2,
|
|
4894
|
-
linkConflictAnalyzer2,
|
|
4895
4464
|
linkCounterAnalyzer2,
|
|
4465
|
+
linkKindConflictAnalyzer2,
|
|
4896
4466
|
linkSelfLoopAnalyzer2,
|
|
4467
|
+
nameCollisionAnalyzer2,
|
|
4897
4468
|
nameReservedAnalyzer2,
|
|
4898
4469
|
nodeStabilityAnalyzer2,
|
|
4899
|
-
nodeSupersededAnalyzer2,
|
|
4900
4470
|
referenceBrokenAnalyzer2,
|
|
4901
4471
|
referenceRedundantAnalyzer2,
|
|
4902
4472
|
schemaViolationAnalyzer2,
|
|
4903
|
-
signalCollisionAnalyzer2,
|
|
4904
|
-
triggerCollisionAnalyzer2,
|
|
4905
4473
|
asciiFormatter2,
|
|
4906
4474
|
jsonFormatter2,
|
|
4907
4475
|
nodeBumpAction2,
|
|
4908
4476
|
nodeSetStabilityAction2,
|
|
4909
4477
|
nodeSetTagsAction2,
|
|
4910
|
-
nodeSupersedeAction2,
|
|
4911
4478
|
updateCheckHook2
|
|
4912
4479
|
]
|
|
4913
4480
|
}
|
|
@@ -5720,7 +5287,7 @@ import { Command as Command2, Option as Option2 } from "clipanion";
|
|
|
5720
5287
|
|
|
5721
5288
|
// core/config/helper.ts
|
|
5722
5289
|
import { homedir as osHomedir } from "os";
|
|
5723
|
-
import { isAbsolute, join as join4, resolve as
|
|
5290
|
+
import { isAbsolute, join as join4, resolve as resolve6, sep } from "path";
|
|
5724
5291
|
|
|
5725
5292
|
// kernel/config/loader.ts
|
|
5726
5293
|
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
@@ -6138,13 +5705,13 @@ function projectPathExposure(inputs) {
|
|
|
6138
5705
|
return { expandsSurface: true, exposedPaths: exposed };
|
|
6139
5706
|
}
|
|
6140
5707
|
function resolveScanPathForExposure(raw, cwd) {
|
|
6141
|
-
if (raw.startsWith("~/")) return
|
|
6142
|
-
if (raw === "~") return
|
|
6143
|
-
if (isAbsolute(raw)) return
|
|
6144
|
-
return
|
|
5708
|
+
if (raw.startsWith("~/")) return resolve6(join4(osHomedir(), raw.slice(2)));
|
|
5709
|
+
if (raw === "~") return resolve6(osHomedir());
|
|
5710
|
+
if (isAbsolute(raw)) return resolve6(raw);
|
|
5711
|
+
return resolve6(cwd, raw);
|
|
6145
5712
|
}
|
|
6146
5713
|
function isUnderProject(absPath, cwd) {
|
|
6147
|
-
const projectRoot =
|
|
5714
|
+
const projectRoot = resolve6(cwd);
|
|
6148
5715
|
return absPath === projectRoot || absPath.startsWith(`${projectRoot}${sep}`);
|
|
6149
5716
|
}
|
|
6150
5717
|
|
|
@@ -6185,7 +5752,7 @@ function ensureSidecarWritesAllowed(opts) {
|
|
|
6185
5752
|
|
|
6186
5753
|
// kernel/sidecar/store.ts
|
|
6187
5754
|
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
|
|
6188
|
-
import { dirname as dirname5, resolve as
|
|
5755
|
+
import { dirname as dirname5, resolve as resolve7 } from "path";
|
|
6189
5756
|
import { createRequire as createRequire4 } from "module";
|
|
6190
5757
|
import { Ajv2020 as Ajv20204 } from "ajv/dist/2020.js";
|
|
6191
5758
|
import yaml2 from "js-yaml";
|
|
@@ -6293,10 +5860,10 @@ function getSidecarValidator2() {
|
|
|
6293
5860
|
applyAjvFormats(ajv);
|
|
6294
5861
|
const specRoot = resolveSpecRoot3();
|
|
6295
5862
|
const annotationsSchema = JSON.parse(
|
|
6296
|
-
readFileSync7(
|
|
5863
|
+
readFileSync7(resolve7(specRoot, "schemas/annotations.schema.json"), "utf8")
|
|
6297
5864
|
);
|
|
6298
5865
|
const sidecarSchema = JSON.parse(
|
|
6299
|
-
readFileSync7(
|
|
5866
|
+
readFileSync7(resolve7(specRoot, "schemas/sidecar.schema.json"), "utf8")
|
|
6300
5867
|
);
|
|
6301
5868
|
ajv.addSchema(annotationsSchema);
|
|
6302
5869
|
cachedValidator = ajv.compile(sidecarSchema);
|
|
@@ -6416,11 +5983,11 @@ async function confirm(question, streams, opts) {
|
|
|
6416
5983
|
// cli/util/git.ts
|
|
6417
5984
|
import { spawnSync } from "child_process";
|
|
6418
5985
|
import { existsSync as existsSync7 } from "fs";
|
|
6419
|
-
import { dirname as dirname6, resolve as
|
|
5986
|
+
import { dirname as dirname6, resolve as resolve8 } from "path";
|
|
6420
5987
|
function isInsideGitRepo(cwd) {
|
|
6421
5988
|
let current = cwd;
|
|
6422
5989
|
while (true) {
|
|
6423
|
-
if (existsSync7(
|
|
5990
|
+
if (existsSync7(resolve8(current, ".git"))) return true;
|
|
6424
5991
|
const parent = dirname6(current);
|
|
6425
5992
|
if (parent === current) return false;
|
|
6426
5993
|
current = parent;
|
|
@@ -6619,7 +6186,7 @@ import { existsSync as existsSync11 } from "fs";
|
|
|
6619
6186
|
|
|
6620
6187
|
// kernel/adapters/sqlite/storage-adapter.ts
|
|
6621
6188
|
import { mkdirSync as mkdirSync4 } from "fs";
|
|
6622
|
-
import { dirname as dirname8, resolve as
|
|
6189
|
+
import { dirname as dirname8, resolve as resolve11 } from "path";
|
|
6623
6190
|
import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
|
|
6624
6191
|
import { CamelCasePlugin, Kysely, sql as sql3 } from "kysely";
|
|
6625
6192
|
|
|
@@ -7178,7 +6745,7 @@ async function migrateNodeFavorites(trx, fromPath, toPath, report) {
|
|
|
7178
6745
|
}
|
|
7179
6746
|
|
|
7180
6747
|
// kernel/adapters/sqlite/jobs.ts
|
|
7181
|
-
import { resolve as
|
|
6748
|
+
import { resolve as resolve9 } from "path";
|
|
7182
6749
|
async function pruneTerminalJobs(db, status, cutoffMs) {
|
|
7183
6750
|
const rows = await db.selectFrom("state_jobs").select(["id", "filePath"]).where("status", "=", status).where("finishedAt", "is not", null).where("finishedAt", "<", cutoffMs).execute();
|
|
7184
6751
|
if (rows.length === 0) {
|
|
@@ -7193,14 +6760,14 @@ async function selectReferencedJobFilePaths(db) {
|
|
|
7193
6760
|
const rows = await db.selectFrom("state_jobs").select(["filePath"]).where("filePath", "is not", null).execute();
|
|
7194
6761
|
const out = /* @__PURE__ */ new Set();
|
|
7195
6762
|
for (const row of rows) {
|
|
7196
|
-
if (row.filePath !== null) out.add(
|
|
6763
|
+
if (row.filePath !== null) out.add(resolve9(row.filePath));
|
|
7197
6764
|
}
|
|
7198
6765
|
return out;
|
|
7199
6766
|
}
|
|
7200
6767
|
|
|
7201
6768
|
// kernel/adapters/sqlite/migrations.ts
|
|
7202
6769
|
import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync } from "fs";
|
|
7203
|
-
import { dirname as dirname7, join as join5, resolve as
|
|
6770
|
+
import { dirname as dirname7, join as join5, resolve as resolve10 } from "path";
|
|
7204
6771
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
7205
6772
|
import { fileURLToPath } from "url";
|
|
7206
6773
|
|
|
@@ -7215,9 +6782,9 @@ var MIGRATIONS_TEXTS = {
|
|
|
7215
6782
|
var FILE_RE = /^(\d{3})_([a-z0-9_]+)\.sql$/;
|
|
7216
6783
|
function defaultMigrationsDir() {
|
|
7217
6784
|
const here = dirname7(fileURLToPath(import.meta.url));
|
|
7218
|
-
const flatLayout =
|
|
6785
|
+
const flatLayout = resolve10(here, "migrations");
|
|
7219
6786
|
if (existsSync8(flatLayout)) return flatLayout;
|
|
7220
|
-
return
|
|
6787
|
+
return resolve10(here, "..", "..", "..", "migrations");
|
|
7221
6788
|
}
|
|
7222
6789
|
function discoverMigrations(dir = defaultMigrationsDir()) {
|
|
7223
6790
|
if (!existsSync8(dir)) return [];
|
|
@@ -7287,7 +6854,7 @@ function resolveMigrationTarget(to, files) {
|
|
|
7287
6854
|
function writePreMigrateBackup(dbPath, target) {
|
|
7288
6855
|
return writeBackup(
|
|
7289
6856
|
dbPath,
|
|
7290
|
-
join5(dirname7(
|
|
6857
|
+
join5(dirname7(resolve10(dbPath)), "backups", `skill-map-pre-migrate-v${target}.db`)
|
|
7291
6858
|
);
|
|
7292
6859
|
}
|
|
7293
6860
|
function applyOneMigration(db, migration) {
|
|
@@ -7322,8 +6889,8 @@ function applyOneMigration(db, migration) {
|
|
|
7322
6889
|
}
|
|
7323
6890
|
function writeBackup(dbPath, destPath) {
|
|
7324
6891
|
if (dbPath === ":memory:") return null;
|
|
7325
|
-
const absoluteSource =
|
|
7326
|
-
const absoluteDest =
|
|
6892
|
+
const absoluteSource = resolve10(dbPath);
|
|
6893
|
+
const absoluteDest = resolve10(destPath);
|
|
7327
6894
|
mkdirSync3(dirname7(absoluteDest), { recursive: true });
|
|
7328
6895
|
const db = new DatabaseSync2(absoluteSource);
|
|
7329
6896
|
try {
|
|
@@ -7810,7 +7377,6 @@ var LINK_KIND_VALUES = Object.freeze([
|
|
|
7810
7377
|
"invokes",
|
|
7811
7378
|
"references",
|
|
7812
7379
|
"mentions",
|
|
7813
|
-
"supersedes",
|
|
7814
7380
|
"points"
|
|
7815
7381
|
]);
|
|
7816
7382
|
var SEVERITY_VALUES = Object.freeze([
|
|
@@ -8284,6 +7850,36 @@ function rowToContribution(row) {
|
|
|
8284
7850
|
};
|
|
8285
7851
|
}
|
|
8286
7852
|
|
|
7853
|
+
// kernel/adapters/sqlite/link-scores.ts
|
|
7854
|
+
async function replaceAllScanLinkScores(trx, linkScores) {
|
|
7855
|
+
await trx.deleteFrom("scan_link_scores").execute();
|
|
7856
|
+
if (linkScores.length === 0) return;
|
|
7857
|
+
const CHUNK = 90;
|
|
7858
|
+
for (let i = 0; i < linkScores.length; i += CHUNK) {
|
|
7859
|
+
const slice = linkScores.slice(i, i + CHUNK);
|
|
7860
|
+
const rows = slice.map(adjustmentToRow);
|
|
7861
|
+
await trx.insertInto("scan_link_scores").values(rows).execute();
|
|
7862
|
+
}
|
|
7863
|
+
}
|
|
7864
|
+
function adjustmentToRow(adj) {
|
|
7865
|
+
return {
|
|
7866
|
+
pluginId: adj.pluginId,
|
|
7867
|
+
extensionId: adj.extensionId,
|
|
7868
|
+
sourcePath: adj.link.source,
|
|
7869
|
+
target: adj.link.target,
|
|
7870
|
+
kind: adj.link.kind,
|
|
7871
|
+
normalizedTrigger: adj.link.trigger?.normalizedTrigger ?? null,
|
|
7872
|
+
opKind: adj.op.kind,
|
|
7873
|
+
opValue: adj.op.value,
|
|
7874
|
+
// FOLDED final confidence: by the time this writer runs, the
|
|
7875
|
+
// orchestrator has already applied every buffered op into
|
|
7876
|
+
// `link.confidence` (see `applyConfidenceAdjustments`). Denormalised
|
|
7877
|
+
// per row so the audit read needs no join.
|
|
7878
|
+
resultConfidence: adj.link.confidence,
|
|
7879
|
+
emittedAt: Date.now()
|
|
7880
|
+
};
|
|
7881
|
+
}
|
|
7882
|
+
|
|
8287
7883
|
// kernel/adapters/sqlite/schema-fingerprint.ts
|
|
8288
7884
|
import { createHash } from "crypto";
|
|
8289
7885
|
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
@@ -8370,8 +7966,18 @@ async function findNodesByTag(db, tag) {
|
|
|
8370
7966
|
}
|
|
8371
7967
|
|
|
8372
7968
|
// kernel/adapters/sqlite/scan-persistence.ts
|
|
8373
|
-
async function persistScanResult(db, result,
|
|
7969
|
+
async function persistScanResult(db, result, inputs = {}) {
|
|
8374
7970
|
const scannedAt = validateScannedAt(result.scannedAt);
|
|
7971
|
+
const {
|
|
7972
|
+
renameOps,
|
|
7973
|
+
extractorRuns,
|
|
7974
|
+
enrichments,
|
|
7975
|
+
contributions,
|
|
7976
|
+
registeredContributionKeys,
|
|
7977
|
+
freshlyRunTuples,
|
|
7978
|
+
contributionErrors,
|
|
7979
|
+
linkScores
|
|
7980
|
+
} = resolvePersistInputs(inputs);
|
|
8375
7981
|
const renames = [];
|
|
8376
7982
|
await db.transaction().execute(async (trx) => {
|
|
8377
7983
|
await applyRenames(trx, renameOps, renames);
|
|
@@ -8386,6 +7992,7 @@ async function persistScanResult(db, result, renameOps = [], extractorRuns = [],
|
|
|
8386
7992
|
freshlyRunTuples
|
|
8387
7993
|
);
|
|
8388
7994
|
await replaceAllScanContributionErrors(trx, contributionErrors);
|
|
7995
|
+
await replaceAllScanLinkScores(trx, linkScores);
|
|
8389
7996
|
const tagRecords = nodesToTagRecords(result.nodes);
|
|
8390
7997
|
await replaceAllScanTags(trx, tagRecords, livePathsForContrib);
|
|
8391
7998
|
await upsertEnrichmentLayer(trx, result, renameOps, enrichments);
|
|
@@ -8394,6 +8001,19 @@ async function persistScanResult(db, result, renameOps = [], extractorRuns = [],
|
|
|
8394
8001
|
await sql2`PRAGMA wal_checkpoint(TRUNCATE)`.execute(db);
|
|
8395
8002
|
return { renames };
|
|
8396
8003
|
}
|
|
8004
|
+
function resolvePersistInputs(inputs) {
|
|
8005
|
+
return {
|
|
8006
|
+
renameOps: [],
|
|
8007
|
+
extractorRuns: [],
|
|
8008
|
+
enrichments: [],
|
|
8009
|
+
contributions: [],
|
|
8010
|
+
registeredContributionKeys: /* @__PURE__ */ new Set(),
|
|
8011
|
+
freshlyRunTuples: /* @__PURE__ */ new Set(),
|
|
8012
|
+
contributionErrors: [],
|
|
8013
|
+
linkScores: [],
|
|
8014
|
+
...inputs
|
|
8015
|
+
};
|
|
8016
|
+
}
|
|
8397
8017
|
function validateScannedAt(scannedAt) {
|
|
8398
8018
|
if (!Number.isInteger(scannedAt) || scannedAt < 0) {
|
|
8399
8019
|
throw new Error(
|
|
@@ -8762,7 +8382,7 @@ var SqliteStorageAdapter = class {
|
|
|
8762
8382
|
if (this.#db) return;
|
|
8763
8383
|
const path = this.#options.databasePath;
|
|
8764
8384
|
if (path !== ":memory:") {
|
|
8765
|
-
const absolute =
|
|
8385
|
+
const absolute = resolve11(path);
|
|
8766
8386
|
mkdirSync4(dirname8(absolute), { recursive: true });
|
|
8767
8387
|
}
|
|
8768
8388
|
if (this.#options.autoMigrate !== false) {
|
|
@@ -8899,17 +8519,7 @@ var SqliteStorageAdapter = class {
|
|
|
8899
8519
|
};
|
|
8900
8520
|
async function persistScansThroughNonTx(db, result, opts) {
|
|
8901
8521
|
const defaults = applyPersistDefaults(opts);
|
|
8902
|
-
await persistScanResult(
|
|
8903
|
-
db,
|
|
8904
|
-
result,
|
|
8905
|
-
defaults.renameOps,
|
|
8906
|
-
defaults.extractorRuns,
|
|
8907
|
-
defaults.enrichments,
|
|
8908
|
-
defaults.contributions,
|
|
8909
|
-
defaults.registeredContributionKeys,
|
|
8910
|
-
defaults.freshlyRunTuples,
|
|
8911
|
-
defaults.contributionErrors
|
|
8912
|
-
);
|
|
8522
|
+
await persistScanResult(db, result, defaults);
|
|
8913
8523
|
}
|
|
8914
8524
|
function applyPersistDefaults(opts) {
|
|
8915
8525
|
return {
|
|
@@ -8920,6 +8530,7 @@ function applyPersistDefaults(opts) {
|
|
|
8920
8530
|
registeredContributionKeys: /* @__PURE__ */ new Set(),
|
|
8921
8531
|
freshlyRunTuples: /* @__PURE__ */ new Set(),
|
|
8922
8532
|
contributionErrors: [],
|
|
8533
|
+
linkScores: [],
|
|
8923
8534
|
...opts
|
|
8924
8535
|
};
|
|
8925
8536
|
}
|
|
@@ -9076,17 +8687,7 @@ function buildTxSubset(trx) {
|
|
|
9076
8687
|
scans: {
|
|
9077
8688
|
persist: (result, opts) => {
|
|
9078
8689
|
const d = applyPersistDefaults(opts);
|
|
9079
|
-
return persistScanResult(
|
|
9080
|
-
trx,
|
|
9081
|
-
result,
|
|
9082
|
-
d.renameOps,
|
|
9083
|
-
d.extractorRuns,
|
|
9084
|
-
d.enrichments,
|
|
9085
|
-
d.contributions,
|
|
9086
|
-
d.registeredContributionKeys,
|
|
9087
|
-
d.freshlyRunTuples,
|
|
9088
|
-
d.contributionErrors
|
|
9089
|
-
).then(() => void 0);
|
|
8690
|
+
return persistScanResult(trx, result, d).then(() => void 0);
|
|
9090
8691
|
}
|
|
9091
8692
|
},
|
|
9092
8693
|
issues: {
|
|
@@ -9380,16 +8981,16 @@ async function tryWithSqlite(options, fn) {
|
|
|
9380
8981
|
}
|
|
9381
8982
|
|
|
9382
8983
|
// cli/commands/bump-plan.ts
|
|
9383
|
-
import { resolve as
|
|
8984
|
+
import { resolve as resolve13 } from "path";
|
|
9384
8985
|
|
|
9385
8986
|
// core/paths/path-guard.ts
|
|
9386
8987
|
import { lstatSync } from "fs";
|
|
9387
|
-
import { isAbsolute as isAbsolute2, resolve as
|
|
8988
|
+
import { isAbsolute as isAbsolute2, resolve as resolve12, sep as sep2 } from "path";
|
|
9388
8989
|
function assertContained(cwd, rel) {
|
|
9389
8990
|
if (isAbsolute2(rel)) {
|
|
9390
8991
|
throw new Error(`node path is absolute, refusing to read: ${rel}`);
|
|
9391
8992
|
}
|
|
9392
|
-
const abs =
|
|
8993
|
+
const abs = resolve12(cwd, rel);
|
|
9393
8994
|
if (abs !== cwd && !abs.startsWith(cwd + sep2)) {
|
|
9394
8995
|
throw new Error(`node path escapes repo root: ${rel}`);
|
|
9395
8996
|
}
|
|
@@ -9424,7 +9025,7 @@ function planOne(node, options, invoker) {
|
|
|
9424
9025
|
let absPath;
|
|
9425
9026
|
try {
|
|
9426
9027
|
assertContained(options.cwd, node.path);
|
|
9427
|
-
absPath =
|
|
9028
|
+
absPath = resolve13(options.cwd, node.path);
|
|
9428
9029
|
} catch (err) {
|
|
9429
9030
|
return {
|
|
9430
9031
|
nodePath: node.path,
|
|
@@ -10047,18 +9648,18 @@ var PLUGIN_LOADER_TEXTS = {
|
|
|
10047
9648
|
// kernel/adapters/plugin-loader/index.ts
|
|
10048
9649
|
import { createRequire as createRequire5 } from "module";
|
|
10049
9650
|
import { existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
10050
|
-
import { join as join8, resolve as
|
|
9651
|
+
import { join as join8, resolve as resolve16 } from "path";
|
|
10051
9652
|
import { pathToFileURL } from "url";
|
|
10052
9653
|
import semver from "semver";
|
|
10053
9654
|
|
|
10054
9655
|
// kernel/adapters/plugin-loader/id-utils.ts
|
|
10055
|
-
import { isAbsolute as isAbsolute3, relative, resolve as
|
|
9656
|
+
import { isAbsolute as isAbsolute3, relative, resolve as resolve14 } from "path";
|
|
10056
9657
|
function fail(path, id, status, reason) {
|
|
10057
9658
|
return { path, id, status, reason };
|
|
10058
9659
|
}
|
|
10059
9660
|
function isInsidePlugin(pluginPath, relEntry) {
|
|
10060
9661
|
if (isAbsolute3(relEntry)) return false;
|
|
10061
|
-
const abs =
|
|
9662
|
+
const abs = resolve14(pluginPath, relEntry);
|
|
10062
9663
|
const rel = relative(pluginPath, abs);
|
|
10063
9664
|
if (rel === "") return true;
|
|
10064
9665
|
if (rel.startsWith("..")) return false;
|
|
@@ -10399,7 +10000,7 @@ function isDirectorySafe(path, statSync12) {
|
|
|
10399
10000
|
|
|
10400
10001
|
// kernel/adapters/plugin-loader/storage-schemas.ts
|
|
10401
10002
|
import { readFileSync as readFileSync12 } from "fs";
|
|
10402
|
-
import { resolve as
|
|
10003
|
+
import { resolve as resolve15 } from "path";
|
|
10403
10004
|
import { Ajv2020 as Ajv20206 } from "ajv/dist/2020.js";
|
|
10404
10005
|
|
|
10405
10006
|
// kernel/adapters/plugin-store.ts
|
|
@@ -10463,7 +10064,7 @@ function compilePluginSchema(pluginPath, relPath) {
|
|
|
10463
10064
|
errDescription: tx(PLUGIN_LOADER_TEXTS.loadErrorSchemaPathEscapesPlugin, { relPath, pluginPath })
|
|
10464
10065
|
};
|
|
10465
10066
|
}
|
|
10466
|
-
const abs =
|
|
10067
|
+
const abs = resolve15(pluginPath, relPath);
|
|
10467
10068
|
let raw;
|
|
10468
10069
|
try {
|
|
10469
10070
|
raw = JSON.parse(readFileSync12(abs, "utf8"));
|
|
@@ -10505,7 +10106,7 @@ var PluginLoader = class {
|
|
|
10505
10106
|
if (!entry.isDirectory()) continue;
|
|
10506
10107
|
const candidate = join8(root, entry.name);
|
|
10507
10108
|
if (existsSync13(join8(candidate, "plugin.json"))) {
|
|
10508
|
-
out.push(
|
|
10109
|
+
out.push(resolve16(candidate));
|
|
10509
10110
|
}
|
|
10510
10111
|
}
|
|
10511
10112
|
}
|
|
@@ -10664,7 +10265,7 @@ var PluginLoader = class {
|
|
|
10664
10265
|
manifest
|
|
10665
10266
|
} };
|
|
10666
10267
|
}
|
|
10667
|
-
const abs =
|
|
10268
|
+
const abs = resolve16(pluginPath, relEntry);
|
|
10668
10269
|
if (!existsSync13(abs)) {
|
|
10669
10270
|
return { ok: false, failure: {
|
|
10670
10271
|
...fail(
|
|
@@ -10868,7 +10469,7 @@ function discoverExtensionEntries(pluginPath) {
|
|
|
10868
10469
|
return out;
|
|
10869
10470
|
}
|
|
10870
10471
|
function collectKindEntries(pluginPath, kindDir, out) {
|
|
10871
|
-
const kindAbs =
|
|
10472
|
+
const kindAbs = resolve16(pluginPath, kindDir);
|
|
10872
10473
|
if (!existsSync13(kindAbs)) return;
|
|
10873
10474
|
let entries;
|
|
10874
10475
|
try {
|
|
@@ -10879,7 +10480,7 @@ function collectKindEntries(pluginPath, kindDir, out) {
|
|
|
10879
10480
|
entries.sort();
|
|
10880
10481
|
for (const entry of entries) {
|
|
10881
10482
|
if (entry.startsWith(".")) continue;
|
|
10882
|
-
const entryAbs =
|
|
10483
|
+
const entryAbs = resolve16(kindAbs, entry);
|
|
10883
10484
|
if (!isDirectorySafe2(entryAbs)) continue;
|
|
10884
10485
|
const candidate = findIndexCandidate(entryAbs);
|
|
10885
10486
|
if (candidate !== null) {
|
|
@@ -10896,14 +10497,14 @@ function isDirectorySafe2(path) {
|
|
|
10896
10497
|
}
|
|
10897
10498
|
function findIndexCandidate(entryAbs) {
|
|
10898
10499
|
for (const candidate of INDEX_CANDIDATES) {
|
|
10899
|
-
if (existsSync13(
|
|
10500
|
+
if (existsSync13(resolve16(entryAbs, candidate))) return candidate;
|
|
10900
10501
|
}
|
|
10901
10502
|
return null;
|
|
10902
10503
|
}
|
|
10903
10504
|
function installedSpecVersion() {
|
|
10904
10505
|
const require2 = createRequire5(import.meta.url);
|
|
10905
10506
|
const indexPath = require2.resolve("@skill-map/spec/index.json");
|
|
10906
|
-
const pkgPath =
|
|
10507
|
+
const pkgPath = resolve16(indexPath, "..", "package.json");
|
|
10907
10508
|
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
10908
10509
|
return pkg.version;
|
|
10909
10510
|
}
|
|
@@ -10916,17 +10517,6 @@ var LOCKED_PLUGIN_IDS = /* @__PURE__ */ new Set([
|
|
|
10916
10517
|
// silently invisible, a foot-gun the host product does not want to
|
|
10917
10518
|
// expose. Lock it in the enabled state.
|
|
10918
10519
|
"core/markdown",
|
|
10919
|
-
// `core/annotations` turns the `supersedes` / `supersededBy` /
|
|
10920
|
-
// `requires` / `related` / `conflictsWith` entries of the sidecar
|
|
10921
|
-
// `annotations:` block into the arrows the graph draws between nodes.
|
|
10922
|
-
// It does NOT own the rest of the block (`version`, `stability`,
|
|
10923
|
-
// `tags`, `description`, those live on the node bundle directly and
|
|
10924
|
-
// keep rendering with the plugin off). Disabling it produces a
|
|
10925
|
-
// confusing "edges disappear but the sidecar metadata stays" split
|
|
10926
|
-
// that no operator actually wants; the lock makes the asymmetry
|
|
10927
|
-
// unreachable from CLI / BFF / UI. Re-evaluate if a third-party ever
|
|
10928
|
-
// ships a competing supersession extractor.
|
|
10929
|
-
"core/annotations",
|
|
10930
10520
|
// `core/schema-violation` validates every scanned Node against
|
|
10931
10521
|
// `node.schema.json` and every Link against `link.schema.json` (the
|
|
10932
10522
|
// authoritative @skill-map/spec). Disabling it makes the system
|
|
@@ -11003,7 +10593,7 @@ import { join as join9, relative as relative2, sep as sep3 } from "path";
|
|
|
11003
10593
|
|
|
11004
10594
|
// kernel/scan/ignore.ts
|
|
11005
10595
|
import { existsSync as existsSync14, readFileSync as readFileSync14 } from "fs";
|
|
11006
|
-
import { dirname as dirname10, resolve as
|
|
10596
|
+
import { dirname as dirname10, resolve as resolve17 } from "path";
|
|
11007
10597
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11008
10598
|
import ignoreFactory from "ignore";
|
|
11009
10599
|
function buildIgnoreFilter(opts = {}) {
|
|
@@ -11032,7 +10622,7 @@ function loadBundledIgnoreText() {
|
|
|
11032
10622
|
return loadDefaultsText();
|
|
11033
10623
|
}
|
|
11034
10624
|
function readIgnoreFileText(scopeRoot) {
|
|
11035
|
-
const path =
|
|
10625
|
+
const path = resolve17(scopeRoot, ".skillmapignore");
|
|
11036
10626
|
if (!existsSync14(path)) return void 0;
|
|
11037
10627
|
try {
|
|
11038
10628
|
return readFileSync14(path, "utf8");
|
|
@@ -11061,11 +10651,11 @@ function loadDefaultsText() {
|
|
|
11061
10651
|
function readDefaultsFromDisk() {
|
|
11062
10652
|
const here = dirname10(fileURLToPath2(import.meta.url));
|
|
11063
10653
|
const candidates = [
|
|
11064
|
-
|
|
10654
|
+
resolve17(here, "../../config/defaults/skillmapignore"),
|
|
11065
10655
|
// src/kernel/scan/ → src/config/defaults/
|
|
11066
|
-
|
|
10656
|
+
resolve17(here, "../config/defaults/skillmapignore"),
|
|
11067
10657
|
// dist/cli.js → dist/config/defaults/ (siblings)
|
|
11068
|
-
|
|
10658
|
+
resolve17(here, "config/defaults/skillmapignore")
|
|
11069
10659
|
];
|
|
11070
10660
|
for (const candidate of candidates) {
|
|
11071
10661
|
if (existsSync14(candidate)) {
|
|
@@ -11362,7 +10952,7 @@ function isExtensionInstance(v) {
|
|
|
11362
10952
|
}
|
|
11363
10953
|
|
|
11364
10954
|
// core/runtime/plugin-runtime/warnings.ts
|
|
11365
|
-
import { resolve as
|
|
10955
|
+
import { resolve as resolve18 } from "path";
|
|
11366
10956
|
|
|
11367
10957
|
// kernel/util/text.ts
|
|
11368
10958
|
function truncateHead(s, max) {
|
|
@@ -11412,7 +11002,7 @@ function resolveRuntimeContext(opts) {
|
|
|
11412
11002
|
return opts.runtimeContext ?? defaultRuntimeContext();
|
|
11413
11003
|
}
|
|
11414
11004
|
function resolveSearchPaths(opts, ctx) {
|
|
11415
|
-
if (opts.pluginDir) return [
|
|
11005
|
+
if (opts.pluginDir) return [resolve18(opts.pluginDir)];
|
|
11416
11006
|
return [defaultProjectPluginsDir(ctx)];
|
|
11417
11007
|
}
|
|
11418
11008
|
|
|
@@ -12679,7 +12269,7 @@ var CONFIG_COMMANDS = [
|
|
|
12679
12269
|
|
|
12680
12270
|
// cli/commands/conformance.ts
|
|
12681
12271
|
import { existsSync as existsSync19, readFileSync as readFileSync16 } from "fs";
|
|
12682
|
-
import { dirname as dirname12, resolve as
|
|
12272
|
+
import { dirname as dirname12, resolve as resolve21 } from "path";
|
|
12683
12273
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12684
12274
|
import { Command as Command5, Option as Option5 } from "clipanion";
|
|
12685
12275
|
|
|
@@ -12687,7 +12277,7 @@ import { Command as Command5, Option as Option5 } from "clipanion";
|
|
|
12687
12277
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
12688
12278
|
import { cpSync, existsSync as existsSync17, mkdtempSync, readdirSync as readdirSync5, readFileSync as readFileSync15, rmSync, statSync as statSync3 } from "fs";
|
|
12689
12279
|
import { tmpdir } from "os";
|
|
12690
|
-
import { isAbsolute as isAbsolute5, join as join11, relative as relative3, resolve as
|
|
12280
|
+
import { isAbsolute as isAbsolute5, join as join11, relative as relative3, resolve as resolve19 } from "path";
|
|
12691
12281
|
|
|
12692
12282
|
// conformance/i18n/runner.texts.ts
|
|
12693
12283
|
var CONFORMANCE_RUNNER_TEXTS = {
|
|
@@ -12854,7 +12444,7 @@ function assertContained2(root, rel, label) {
|
|
|
12854
12444
|
tx(CONFORMANCE_RUNNER_TEXTS.pathMustBeRelative, { label, path: rel, anchor: root })
|
|
12855
12445
|
);
|
|
12856
12446
|
}
|
|
12857
|
-
const abs =
|
|
12447
|
+
const abs = resolve19(root, rel);
|
|
12858
12448
|
const r = relative3(root, abs);
|
|
12859
12449
|
if (r.startsWith("..") || isAbsolute5(r)) {
|
|
12860
12450
|
throw new Error(
|
|
@@ -12881,7 +12471,7 @@ function evaluateAssertion(a, ctx) {
|
|
|
12881
12471
|
} catch (err) {
|
|
12882
12472
|
return { ok: false, type: a.type, reason: formatErrorMessage(err) };
|
|
12883
12473
|
}
|
|
12884
|
-
const abs =
|
|
12474
|
+
const abs = resolve19(ctx.scope, a.path);
|
|
12885
12475
|
return existsSync17(abs) ? { ok: true, type: a.type } : {
|
|
12886
12476
|
ok: false,
|
|
12887
12477
|
type: a.type,
|
|
@@ -12896,7 +12486,7 @@ function evaluateAssertion(a, ctx) {
|
|
|
12896
12486
|
return { ok: false, type: a.type, reason: formatErrorMessage(err) };
|
|
12897
12487
|
}
|
|
12898
12488
|
const fixturePath = join11(ctx.fixturesRoot, a.fixture);
|
|
12899
|
-
const targetPath =
|
|
12489
|
+
const targetPath = resolve19(ctx.scope, a.path);
|
|
12900
12490
|
if (!existsSync17(targetPath)) {
|
|
12901
12491
|
return {
|
|
12902
12492
|
ok: false,
|
|
@@ -13079,7 +12669,7 @@ var CONFORMANCE_TEXTS = {
|
|
|
13079
12669
|
|
|
13080
12670
|
// cli/util/conformance-scopes.ts
|
|
13081
12671
|
import { existsSync as existsSync18, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
13082
|
-
import { dirname as dirname11, resolve as
|
|
12672
|
+
import { dirname as dirname11, resolve as resolve20 } from "path";
|
|
13083
12673
|
import { createRequire as createRequire6 } from "module";
|
|
13084
12674
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
13085
12675
|
function resolveSpecRoot4() {
|
|
@@ -13097,7 +12687,7 @@ function resolveCliWorkspaceRoot() {
|
|
|
13097
12687
|
const here = dirname11(fileURLToPath3(import.meta.url));
|
|
13098
12688
|
let cursor = here;
|
|
13099
12689
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
13100
|
-
const candidate =
|
|
12690
|
+
const candidate = resolve20(cursor, "plugins");
|
|
13101
12691
|
if (existsSync18(candidate) && statSync4(candidate).isDirectory()) {
|
|
13102
12692
|
return cursor;
|
|
13103
12693
|
}
|
|
@@ -13117,12 +12707,12 @@ function collectProviderScopes(specRoot) {
|
|
|
13117
12707
|
} catch {
|
|
13118
12708
|
return out;
|
|
13119
12709
|
}
|
|
13120
|
-
const pluginsRoot =
|
|
12710
|
+
const pluginsRoot = resolve20(workspaceRoot, "plugins");
|
|
13121
12711
|
if (!existsSync18(pluginsRoot)) return out;
|
|
13122
12712
|
for (const pluginEntry of readdirSync6(pluginsRoot)) {
|
|
13123
|
-
const pluginDir =
|
|
12713
|
+
const pluginDir = resolve20(pluginsRoot, pluginEntry);
|
|
13124
12714
|
if (!isDir(pluginDir)) continue;
|
|
13125
|
-
const providersRoot =
|
|
12715
|
+
const providersRoot = resolve20(pluginDir, "providers");
|
|
13126
12716
|
if (!isDir(providersRoot)) continue;
|
|
13127
12717
|
collectPluginProviderScopes(providersRoot, specRoot, out);
|
|
13128
12718
|
}
|
|
@@ -13137,12 +12727,12 @@ function isDir(path) {
|
|
|
13137
12727
|
}
|
|
13138
12728
|
function collectPluginProviderScopes(providersRoot, specRoot, out) {
|
|
13139
12729
|
for (const entry of readdirSync6(providersRoot)) {
|
|
13140
|
-
const providerDir =
|
|
12730
|
+
const providerDir = resolve20(providersRoot, entry);
|
|
13141
12731
|
if (!isDir(providerDir)) continue;
|
|
13142
|
-
const conformanceDir =
|
|
12732
|
+
const conformanceDir = resolve20(providerDir, "conformance");
|
|
13143
12733
|
if (!existsSync18(conformanceDir)) continue;
|
|
13144
|
-
const casesDir =
|
|
13145
|
-
const fixturesDir =
|
|
12734
|
+
const casesDir = resolve20(conformanceDir, "cases");
|
|
12735
|
+
const fixturesDir = resolve20(conformanceDir, "fixtures");
|
|
13146
12736
|
if (!existsSync18(casesDir) || !existsSync18(fixturesDir)) continue;
|
|
13147
12737
|
out.push({
|
|
13148
12738
|
id: `provider:${entry}`,
|
|
@@ -13159,8 +12749,8 @@ function specScope(specRoot) {
|
|
|
13159
12749
|
id: "spec",
|
|
13160
12750
|
kind: "spec",
|
|
13161
12751
|
label: "spec",
|
|
13162
|
-
casesDir:
|
|
13163
|
-
fixturesDir:
|
|
12752
|
+
casesDir: resolve20(specRoot, "conformance", "cases"),
|
|
12753
|
+
fixturesDir: resolve20(specRoot, "conformance", "fixtures"),
|
|
13164
12754
|
specRoot
|
|
13165
12755
|
};
|
|
13166
12756
|
}
|
|
@@ -13182,7 +12772,7 @@ function selectConformanceScopes(scope) {
|
|
|
13182
12772
|
}
|
|
13183
12773
|
function listCaseFiles(scope) {
|
|
13184
12774
|
if (!existsSync18(scope.casesDir)) return [];
|
|
13185
|
-
return readdirSync6(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) =>
|
|
12775
|
+
return readdirSync6(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) => resolve20(scope.casesDir, entry));
|
|
13186
12776
|
}
|
|
13187
12777
|
|
|
13188
12778
|
// cli/commands/conformance.ts
|
|
@@ -13199,13 +12789,13 @@ function resolveBinary() {
|
|
|
13199
12789
|
const here = dirname12(fileURLToPath4(import.meta.url));
|
|
13200
12790
|
let cursor = here;
|
|
13201
12791
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
13202
|
-
const candidate =
|
|
12792
|
+
const candidate = resolve21(cursor, "bin", "sm.js");
|
|
13203
12793
|
if (existsSync19(candidate)) return candidate;
|
|
13204
12794
|
const parent = dirname12(cursor);
|
|
13205
12795
|
if (parent === cursor) break;
|
|
13206
12796
|
cursor = parent;
|
|
13207
12797
|
}
|
|
13208
|
-
return
|
|
12798
|
+
return resolve21(here, "..", "..", "bin", "sm.js");
|
|
13209
12799
|
}
|
|
13210
12800
|
var ConformanceRunCommand = class extends SmCommand {
|
|
13211
12801
|
static paths = [["conformance", "run"]];
|
|
@@ -13458,7 +13048,7 @@ function writeStreamSnippet(stream, header, text) {
|
|
|
13458
13048
|
var CONFORMANCE_COMMANDS = [ConformanceRunCommand];
|
|
13459
13049
|
|
|
13460
13050
|
// cli/commands/db/backup.ts
|
|
13461
|
-
import { dirname as dirname13, join as join12, resolve as
|
|
13051
|
+
import { dirname as dirname13, join as join12, resolve as resolve22 } from "path";
|
|
13462
13052
|
import { Command as Command6, Option as Option6 } from "clipanion";
|
|
13463
13053
|
|
|
13464
13054
|
// cli/i18n/db.texts.ts
|
|
@@ -13571,7 +13161,7 @@ var DbBackupCommand = class extends SmCommand {
|
|
|
13571
13161
|
const exit = requireDbOrExit(path, this.context.stderr);
|
|
13572
13162
|
if (exit !== null) return exit;
|
|
13573
13163
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13574
|
-
const outPath = this.out ?
|
|
13164
|
+
const outPath = this.out ? resolve22(this.out) : join12(dirname13(path), "backups", `${ts}.db`);
|
|
13575
13165
|
await withSqlite({ databasePath: path, autoMigrate: false }, async (storage) => {
|
|
13576
13166
|
storage.migrations.writeBackup(outPath);
|
|
13577
13167
|
});
|
|
@@ -13588,7 +13178,7 @@ var DbBackupCommand = class extends SmCommand {
|
|
|
13588
13178
|
|
|
13589
13179
|
// cli/commands/db/restore.ts
|
|
13590
13180
|
import { chmod, copyFile, mkdir, rm } from "fs/promises";
|
|
13591
|
-
import { dirname as dirname14, resolve as
|
|
13181
|
+
import { dirname as dirname14, resolve as resolve23 } from "path";
|
|
13592
13182
|
import { Command as Command7, Option as Option7 } from "clipanion";
|
|
13593
13183
|
|
|
13594
13184
|
// cli/util/fs.ts
|
|
@@ -13638,7 +13228,7 @@ var DbRestoreCommand = class extends SmCommand {
|
|
|
13638
13228
|
});
|
|
13639
13229
|
async run() {
|
|
13640
13230
|
const target = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
|
|
13641
|
-
const sourcePath =
|
|
13231
|
+
const sourcePath = resolve23(this.source);
|
|
13642
13232
|
const stderrAnsi = this.ansiFor("stderr");
|
|
13643
13233
|
const sourceStat = await statOrNull(sourcePath);
|
|
13644
13234
|
if (!sourceStat) {
|
|
@@ -13879,7 +13469,7 @@ var DbShellCommand = class extends SmCommand {
|
|
|
13879
13469
|
|
|
13880
13470
|
// cli/commands/db/browser.ts
|
|
13881
13471
|
import { spawn, spawnSync as spawnSync4 } from "child_process";
|
|
13882
|
-
import { resolve as
|
|
13472
|
+
import { resolve as resolve24 } from "path";
|
|
13883
13473
|
import { Command as Command10, Option as Option9 } from "clipanion";
|
|
13884
13474
|
var DbBrowserCommand = class extends SmCommand {
|
|
13885
13475
|
static paths = [["db", "browser"]];
|
|
@@ -13912,7 +13502,7 @@ var DbBrowserCommand = class extends SmCommand {
|
|
|
13912
13502
|
});
|
|
13913
13503
|
positional = Option9.String({ required: false });
|
|
13914
13504
|
async run() {
|
|
13915
|
-
const path = this.positional ?
|
|
13505
|
+
const path = this.positional ? resolve24(this.positional) : resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
|
|
13916
13506
|
if (!assertDbExists(path, this.context.stderr)) {
|
|
13917
13507
|
this.printer.error(DB_TEXTS.browserRunScanFirstHint);
|
|
13918
13508
|
return ExitCode.NotFound;
|
|
@@ -14830,7 +14420,7 @@ var GraphCommand = class extends SmCommand {
|
|
|
14830
14420
|
// cli/commands/help.ts
|
|
14831
14421
|
import { readFileSync as readFileSync17 } from "fs";
|
|
14832
14422
|
import { createRequire as createRequire7 } from "module";
|
|
14833
|
-
import { resolve as
|
|
14423
|
+
import { resolve as resolve25 } from "path";
|
|
14834
14424
|
import { Command as Command15, Option as Option14 } from "clipanion";
|
|
14835
14425
|
|
|
14836
14426
|
// cli/i18n/help.texts.ts
|
|
@@ -15144,7 +14734,7 @@ function resolveSpecVersion() {
|
|
|
15144
14734
|
try {
|
|
15145
14735
|
const req = createRequire7(import.meta.url);
|
|
15146
14736
|
const indexPath = req.resolve("@skill-map/spec/index.json");
|
|
15147
|
-
const pkgPath =
|
|
14737
|
+
const pkgPath = resolve25(indexPath, "..", "package.json");
|
|
15148
14738
|
const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
|
|
15149
14739
|
return pkg.version;
|
|
15150
14740
|
} catch {
|
|
@@ -15452,7 +15042,7 @@ function registeredVerbPaths(cli2) {
|
|
|
15452
15042
|
|
|
15453
15043
|
// cli/commands/hooks.ts
|
|
15454
15044
|
import { chmod as chmod2, mkdir as mkdir3, readFile as readFile2, stat as stat2, writeFile } from "fs/promises";
|
|
15455
|
-
import { dirname as dirname16, resolve as
|
|
15045
|
+
import { dirname as dirname16, resolve as resolve26 } from "path";
|
|
15456
15046
|
import { Command as Command16, Option as Option15 } from "clipanion";
|
|
15457
15047
|
|
|
15458
15048
|
// cli/i18n/hooks.texts.ts
|
|
@@ -15555,8 +15145,8 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
15555
15145
|
);
|
|
15556
15146
|
return ExitCode.NotFound;
|
|
15557
15147
|
}
|
|
15558
|
-
const hooksDir =
|
|
15559
|
-
const hookPath =
|
|
15148
|
+
const hooksDir = resolve26(repoRoot, ".git", "hooks");
|
|
15149
|
+
const hookPath = resolve26(hooksDir, "pre-commit");
|
|
15560
15150
|
const existing = await pathExists(hookPath) ? await readFile2(hookPath, "utf8") : null;
|
|
15561
15151
|
const planned2 = computePlannedHookContent(existing);
|
|
15562
15152
|
if (planned2.kind === "already-installed") {
|
|
@@ -15614,7 +15204,7 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
15614
15204
|
async function findGitRepoRoot(cwd) {
|
|
15615
15205
|
let current = cwd;
|
|
15616
15206
|
while (true) {
|
|
15617
|
-
if (await pathExists(
|
|
15207
|
+
if (await pathExists(resolve26(current, ".git"))) return current;
|
|
15618
15208
|
const parent = dirname16(current);
|
|
15619
15209
|
if (parent === current) return null;
|
|
15620
15210
|
current = parent;
|
|
@@ -15636,7 +15226,7 @@ var HOOKS_COMMANDS = [HooksInstallCommand];
|
|
|
15636
15226
|
|
|
15637
15227
|
// cli/commands/init.ts
|
|
15638
15228
|
import { mkdir as mkdir4, readFile as readFile3, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
15639
|
-
import { join as
|
|
15229
|
+
import { join as join16 } from "path";
|
|
15640
15230
|
import { Command as Command17, Option as Option16 } from "clipanion";
|
|
15641
15231
|
|
|
15642
15232
|
// kernel/orchestrator/index.ts
|
|
@@ -15891,7 +15481,7 @@ function buildVirtualNode(extractor, emitted, emitter) {
|
|
|
15891
15481
|
if (emitted.frontmatter) node.frontmatter = emitted.frontmatter;
|
|
15892
15482
|
return node;
|
|
15893
15483
|
}
|
|
15894
|
-
var KNOWN_LINK_KINDS = ["invokes", "references", "mentions", "
|
|
15484
|
+
var KNOWN_LINK_KINDS = ["invokes", "references", "mentions", "points"];
|
|
15895
15485
|
function validateLink(extractor, link, emitter) {
|
|
15896
15486
|
const knownKinds = KNOWN_LINK_KINDS;
|
|
15897
15487
|
if (!knownKinds.includes(link.kind)) {
|
|
@@ -16129,8 +15719,49 @@ function runActionProjections(actions, nodes, links, emitter) {
|
|
|
16129
15719
|
return { contributions, contributionErrors };
|
|
16130
15720
|
}
|
|
16131
15721
|
|
|
15722
|
+
// kernel/orchestrator/confidence-score.ts
|
|
15723
|
+
function foldConfidence(base, ops) {
|
|
15724
|
+
let running = base;
|
|
15725
|
+
for (const op of ops) {
|
|
15726
|
+
if (op.kind === "set") running = op.value;
|
|
15727
|
+
}
|
|
15728
|
+
for (const op of ops) {
|
|
15729
|
+
if (op.kind === "delta") running += op.value;
|
|
15730
|
+
}
|
|
15731
|
+
for (const op of ops) {
|
|
15732
|
+
if (op.kind === "floor") running = Math.max(running, op.value);
|
|
15733
|
+
}
|
|
15734
|
+
for (const op of ops) {
|
|
15735
|
+
if (op.kind === "ceil") running = Math.min(running, op.value);
|
|
15736
|
+
}
|
|
15737
|
+
return clamp01(running);
|
|
15738
|
+
}
|
|
15739
|
+
function clamp01(n) {
|
|
15740
|
+
if (n < 0) return 0;
|
|
15741
|
+
if (n > 1) return 1;
|
|
15742
|
+
return n;
|
|
15743
|
+
}
|
|
15744
|
+
function applyConfidenceAdjustments(adjustments) {
|
|
15745
|
+
if (adjustments.length === 0) return;
|
|
15746
|
+
const byLink = /* @__PURE__ */ new Map();
|
|
15747
|
+
for (const adj of adjustments) {
|
|
15748
|
+
const bucket = byLink.get(adj.link);
|
|
15749
|
+
if (bucket) bucket.push(adj);
|
|
15750
|
+
else byLink.set(adj.link, [adj]);
|
|
15751
|
+
}
|
|
15752
|
+
for (const [link, adjs] of byLink) {
|
|
15753
|
+
const ops = [...adjs].sort(compareAdjustments).map((a) => a.op);
|
|
15754
|
+
link.confidence = foldConfidence(link.confidence, ops);
|
|
15755
|
+
}
|
|
15756
|
+
}
|
|
15757
|
+
function compareAdjustments(a, b) {
|
|
15758
|
+
if (a.pluginId !== b.pluginId) return a.pluginId < b.pluginId ? -1 : 1;
|
|
15759
|
+
if (a.extensionId !== b.extensionId) return a.extensionId < b.extensionId ? -1 : 1;
|
|
15760
|
+
return 0;
|
|
15761
|
+
}
|
|
15762
|
+
|
|
16132
15763
|
// kernel/orchestrator/analyzers.ts
|
|
16133
|
-
async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions,
|
|
15764
|
+
async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, referenceablePaths, cwd, registeredActionIds, emitter, hookDispatcher, reservedNodePaths, brokenLinks, nameCollisions, signals, seedIssues = []) {
|
|
16134
15765
|
const issues = [...seedIssues];
|
|
16135
15766
|
const contributions = [];
|
|
16136
15767
|
const contributionErrors = [];
|
|
@@ -16141,7 +15772,16 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
16141
15772
|
expectedMdPath: o.expectedMdPath
|
|
16142
15773
|
}));
|
|
16143
15774
|
const scheduled = orderAnalyzersByPhase(analyzers);
|
|
15775
|
+
const scoreAdjustments = [];
|
|
15776
|
+
const scorableLinks = new Set(internalLinks);
|
|
15777
|
+
let scoresFolded = false;
|
|
15778
|
+
const foldScores = () => {
|
|
15779
|
+
if (scoresFolded) return;
|
|
15780
|
+
scoresFolded = true;
|
|
15781
|
+
applyConfidenceAdjustments(scoreAdjustments);
|
|
15782
|
+
};
|
|
16144
15783
|
for (const analyzer of scheduled) {
|
|
15784
|
+
if (analyzer.phase !== "score") foldScores();
|
|
16145
15785
|
const qualifiedId2 = qualifiedExtensionId(analyzer.pluginId, analyzer.id);
|
|
16146
15786
|
const declaredContributions = readDeclaredContributionRefs(analyzer);
|
|
16147
15787
|
const emitContribution = (nodePath, ref, payload) => {
|
|
@@ -16204,6 +15844,16 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
16204
15844
|
emittedAt: Date.now()
|
|
16205
15845
|
});
|
|
16206
15846
|
};
|
|
15847
|
+
const adjustConfidence = analyzer.phase === "score" ? (link, op) => {
|
|
15848
|
+
if (scorableLinks.has(link)) {
|
|
15849
|
+
scoreAdjustments.push({
|
|
15850
|
+
link,
|
|
15851
|
+
pluginId: analyzer.pluginId,
|
|
15852
|
+
extensionId: analyzer.id,
|
|
15853
|
+
op
|
|
15854
|
+
});
|
|
15855
|
+
}
|
|
15856
|
+
} : void 0;
|
|
16207
15857
|
const emitted = await analyzer.evaluate({
|
|
16208
15858
|
nodes,
|
|
16209
15859
|
links: internalLinks,
|
|
@@ -16215,7 +15865,6 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
16215
15865
|
sidecarRoots,
|
|
16216
15866
|
annotationContributions,
|
|
16217
15867
|
viewContributions,
|
|
16218
|
-
orphanJobFiles,
|
|
16219
15868
|
// `issues` is the live accumulator, mutated by `issues.push(...)`
|
|
16220
15869
|
// below as each analyzer's emission lands. Late-phase analyzers
|
|
16221
15870
|
// (`core/issue-counter`) read it to compute cross-analyzer
|
|
@@ -16225,7 +15874,9 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
16225
15874
|
...cwd ? { cwd } : {},
|
|
16226
15875
|
...reservedNodePaths ? { reservedNodePaths } : {},
|
|
16227
15876
|
...brokenLinks ? { brokenLinks } : {},
|
|
15877
|
+
...nameCollisions && nameCollisions.size > 0 ? { nameCollisions } : {},
|
|
16228
15878
|
...signals && signals.length > 0 ? { signals } : {},
|
|
15879
|
+
...adjustConfidence ? { adjustConfidence } : {},
|
|
16229
15880
|
emitContribution
|
|
16230
15881
|
});
|
|
16231
15882
|
for (const issue of emitted) {
|
|
@@ -16236,13 +15887,16 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
16236
15887
|
emitter.emit(evt);
|
|
16237
15888
|
await hookDispatcher.dispatch("analyzer.completed", evt);
|
|
16238
15889
|
}
|
|
16239
|
-
|
|
15890
|
+
foldScores();
|
|
15891
|
+
return { issues, contributions, contributionErrors, linkScores: scoreAdjustments };
|
|
16240
15892
|
}
|
|
16241
15893
|
function orderAnalyzersByPhase(analyzers) {
|
|
16242
15894
|
return analyzers.slice().sort((a, b) => phaseRank(a) - phaseRank(b));
|
|
16243
15895
|
}
|
|
16244
15896
|
function phaseRank(a) {
|
|
16245
|
-
|
|
15897
|
+
if (a.phase === "score") return 0;
|
|
15898
|
+
if (a.phase === "aggregate") return 2;
|
|
15899
|
+
return 1;
|
|
16246
15900
|
}
|
|
16247
15901
|
function validateIssue(analyzer, issue, emitter) {
|
|
16248
15902
|
const severity = issue.severity;
|
|
@@ -16268,42 +15922,29 @@ function validateIssue(analyzer, issue, emitter) {
|
|
|
16268
15922
|
// kernel/orchestrator/cache.ts
|
|
16269
15923
|
function indexPriorSnapshot(prior) {
|
|
16270
15924
|
const priorNodesByPath = /* @__PURE__ */ new Map();
|
|
16271
|
-
const priorNodePaths = /* @__PURE__ */ new Set();
|
|
16272
15925
|
const priorLinksByOriginating = /* @__PURE__ */ new Map();
|
|
16273
15926
|
const priorFrontmatterIssuesByNode = /* @__PURE__ */ new Map();
|
|
16274
15927
|
if (!prior) {
|
|
16275
|
-
return { priorNodesByPath,
|
|
15928
|
+
return { priorNodesByPath, priorLinksByOriginating, priorFrontmatterIssuesByNode };
|
|
16276
15929
|
}
|
|
16277
|
-
indexPriorNodes(prior.nodes, priorNodesByPath
|
|
16278
|
-
indexPriorLinks(prior.links,
|
|
15930
|
+
indexPriorNodes(prior.nodes, priorNodesByPath);
|
|
15931
|
+
indexPriorLinks(prior.links, priorLinksByOriginating);
|
|
16279
15932
|
indexPriorFrontmatterIssues(prior.issues, priorFrontmatterIssuesByNode);
|
|
16280
|
-
return { priorNodesByPath,
|
|
15933
|
+
return { priorNodesByPath, priorLinksByOriginating, priorFrontmatterIssuesByNode };
|
|
16281
15934
|
}
|
|
16282
|
-
function indexPriorNodes(nodes, byPath3
|
|
15935
|
+
function indexPriorNodes(nodes, byPath3) {
|
|
16283
15936
|
for (const node of nodes) {
|
|
16284
15937
|
byPath3.set(node.path, node);
|
|
16285
|
-
paths.add(node.path);
|
|
16286
15938
|
}
|
|
16287
15939
|
}
|
|
16288
|
-
function indexPriorLinks(links,
|
|
15940
|
+
function indexPriorLinks(links, byOriginating) {
|
|
16289
15941
|
for (const link of links) {
|
|
16290
|
-
const key =
|
|
15942
|
+
const key = link.source;
|
|
16291
15943
|
const list = byOriginating.get(key);
|
|
16292
15944
|
if (list) list.push(link);
|
|
16293
15945
|
else byOriginating.set(key, [link]);
|
|
16294
15946
|
}
|
|
16295
15947
|
}
|
|
16296
|
-
var FRONTMATTER_ISSUE_ANALYZERS = /* @__PURE__ */ new Set([
|
|
16297
|
-
"frontmatter-invalid",
|
|
16298
|
-
"frontmatter-malformed",
|
|
16299
|
-
// Audit L1: parser parse-error is emitted by
|
|
16300
|
-
// `buildFreshNodeAndValidateFrontmatter` from `raw.parseIssues`. The
|
|
16301
|
-
// raw.parseIssues only flows through the non-cache path; a cached
|
|
16302
|
-
// node skips the rebuild, so the prior issue MUST survive the
|
|
16303
|
-
// incremental scan or the warning silently disappears on a clean
|
|
16304
|
-
// re-scan of an unchanged file.
|
|
16305
|
-
"frontmatter-parse-error"
|
|
16306
|
-
]);
|
|
16307
15948
|
function indexPriorFrontmatterIssues(issues, byNode) {
|
|
16308
15949
|
for (const issue of issues) {
|
|
16309
15950
|
if (!FRONTMATTER_ISSUE_ANALYZERS.has(issue.analyzerId)) continue;
|
|
@@ -16314,12 +15955,6 @@ function indexPriorFrontmatterIssues(issues, byNode) {
|
|
|
16314
15955
|
else byNode.set(path, [issue]);
|
|
16315
15956
|
}
|
|
16316
15957
|
}
|
|
16317
|
-
function originatingNodeOf(link, priorNodePaths) {
|
|
16318
|
-
if (link.kind === "supersedes" && !priorNodePaths.has(link.source)) {
|
|
16319
|
-
return link.target;
|
|
16320
|
-
}
|
|
16321
|
-
return link.source;
|
|
16322
|
-
}
|
|
16323
15958
|
function computeCacheDecision(opts) {
|
|
16324
15959
|
const applicableExtractors = opts.extractors.filter((ex) => {
|
|
16325
15960
|
if (!matchesKindPrecondition(ex, opts.kind)) return false;
|
|
@@ -16452,6 +16087,157 @@ function classifyLinkSource(source, shortIdToQualified, cachedQualifiedIds, appl
|
|
|
16452
16087
|
return "obsolete";
|
|
16453
16088
|
}
|
|
16454
16089
|
|
|
16090
|
+
// kernel/orchestrator/node-identifiers.ts
|
|
16091
|
+
import { posix as pathPosix4 } from "path";
|
|
16092
|
+
function deriveNodeIdentifiers(node, kindDescriptor) {
|
|
16093
|
+
const sources = kindDescriptor?.identifiers;
|
|
16094
|
+
if (!sources || sources.length === 0) return [];
|
|
16095
|
+
const out = [];
|
|
16096
|
+
for (const source of sources) {
|
|
16097
|
+
const raw = readIdentifier(source, node);
|
|
16098
|
+
if (!raw) continue;
|
|
16099
|
+
const normalised = normalizeTrigger(raw);
|
|
16100
|
+
if (normalised) out.push(normalised);
|
|
16101
|
+
}
|
|
16102
|
+
return out;
|
|
16103
|
+
}
|
|
16104
|
+
function readIdentifier(source, node) {
|
|
16105
|
+
if (source === "frontmatter.name") return readFrontmatterName(node);
|
|
16106
|
+
if (source === "filename-basename") return readFilenameBasename(node);
|
|
16107
|
+
return readDirname(node);
|
|
16108
|
+
}
|
|
16109
|
+
function readFrontmatterName(node) {
|
|
16110
|
+
const raw = node.frontmatter?.["name"];
|
|
16111
|
+
if (typeof raw !== "string") return null;
|
|
16112
|
+
return raw.length > 0 ? raw : null;
|
|
16113
|
+
}
|
|
16114
|
+
function readFilenameBasename(node) {
|
|
16115
|
+
const base = pathPosix4.basename(node.path);
|
|
16116
|
+
if (!base) return null;
|
|
16117
|
+
const ext = pathPosix4.extname(base);
|
|
16118
|
+
const stem = ext ? base.slice(0, -ext.length) : base;
|
|
16119
|
+
return stem.length > 0 ? stem : null;
|
|
16120
|
+
}
|
|
16121
|
+
function readDirname(node) {
|
|
16122
|
+
const dir = pathPosix4.dirname(node.path);
|
|
16123
|
+
if (!dir || dir === "." || dir === "/") return null;
|
|
16124
|
+
const base = pathPosix4.basename(dir);
|
|
16125
|
+
return base.length > 0 ? base : null;
|
|
16126
|
+
}
|
|
16127
|
+
function collectNameCollisions(nodes, kindRegistry) {
|
|
16128
|
+
const byName = indexNameClaims(nodes, kindRegistry);
|
|
16129
|
+
const collisions = /* @__PURE__ */ new Map();
|
|
16130
|
+
for (const [name, claims] of byName) {
|
|
16131
|
+
const distinct = dedupeClaimsByPath(claims);
|
|
16132
|
+
if (distinct.length >= 2) collisions.set(name, distinct);
|
|
16133
|
+
}
|
|
16134
|
+
return collisions;
|
|
16135
|
+
}
|
|
16136
|
+
function indexNameClaims(nodes, kindRegistry) {
|
|
16137
|
+
const byName = /* @__PURE__ */ new Map();
|
|
16138
|
+
for (const node of nodes) {
|
|
16139
|
+
const name = resolvableName(node, kindRegistry);
|
|
16140
|
+
if (name === null) continue;
|
|
16141
|
+
const bucket = byName.get(name) ?? [];
|
|
16142
|
+
bucket.push({ path: node.path, kind: node.kind });
|
|
16143
|
+
byName.set(name, bucket);
|
|
16144
|
+
}
|
|
16145
|
+
return byName;
|
|
16146
|
+
}
|
|
16147
|
+
function resolvableName(node, kindRegistry) {
|
|
16148
|
+
const descriptor = kindRegistry.get(`${node.provider}/${node.kind}`);
|
|
16149
|
+
if (!descriptor?.identifiers?.includes("frontmatter.name")) return null;
|
|
16150
|
+
const raw = node.frontmatter?.["name"];
|
|
16151
|
+
if (typeof raw !== "string" || raw.length === 0) return null;
|
|
16152
|
+
const normalised = normalizeTrigger(raw);
|
|
16153
|
+
return normalised.length > 0 ? normalised : null;
|
|
16154
|
+
}
|
|
16155
|
+
function dedupeClaimsByPath(claims) {
|
|
16156
|
+
return [...new Map(claims.map((c) => [c.path, c])).values()].sort(
|
|
16157
|
+
(a, b) => a.path.localeCompare(b.path)
|
|
16158
|
+
);
|
|
16159
|
+
}
|
|
16160
|
+
|
|
16161
|
+
// kernel/orchestrator/lift-resolved-link-confidence.ts
|
|
16162
|
+
function liftResolvedLinkConfidence(links, nodes, ctx) {
|
|
16163
|
+
if (links.length === 0) return;
|
|
16164
|
+
const indexes = buildIndexes(nodes, ctx);
|
|
16165
|
+
for (const link of links) {
|
|
16166
|
+
link.confidence = 1;
|
|
16167
|
+
applyResolution(link, indexes, ctx);
|
|
16168
|
+
}
|
|
16169
|
+
}
|
|
16170
|
+
function collectBrokenLinks(links, nodes, ctx) {
|
|
16171
|
+
const broken = /* @__PURE__ */ new Set();
|
|
16172
|
+
if (links.length === 0) return broken;
|
|
16173
|
+
const indexes = buildIndexes(nodes, ctx);
|
|
16174
|
+
for (const link of links) {
|
|
16175
|
+
if (isGenuinelyBroken(link, indexes)) broken.add(link);
|
|
16176
|
+
}
|
|
16177
|
+
return broken;
|
|
16178
|
+
}
|
|
16179
|
+
function applyResolution(link, indexes, ctx) {
|
|
16180
|
+
const resolution = resolve27(link, indexes, ctx);
|
|
16181
|
+
if (resolution === "none") return;
|
|
16182
|
+
link.resolvedTarget = resolution;
|
|
16183
|
+
}
|
|
16184
|
+
function buildIndexes(nodes, ctx) {
|
|
16185
|
+
const byPath3 = /* @__PURE__ */ new Set();
|
|
16186
|
+
const byName = /* @__PURE__ */ new Map();
|
|
16187
|
+
const nodeByPath = /* @__PURE__ */ new Map();
|
|
16188
|
+
for (const node of nodes) {
|
|
16189
|
+
byPath3.add(node.path);
|
|
16190
|
+
nodeByPath.set(node.path, node);
|
|
16191
|
+
indexNode(node, ctx, byName);
|
|
16192
|
+
}
|
|
16193
|
+
return { byPath: byPath3, byName, nodeByPath };
|
|
16194
|
+
}
|
|
16195
|
+
function resolve27(link, indexes, ctx) {
|
|
16196
|
+
if (indexes.byPath.has(link.target)) return link.target;
|
|
16197
|
+
return resolveByName(link, indexes, ctx);
|
|
16198
|
+
}
|
|
16199
|
+
function isGenuinelyBroken(link, indexes) {
|
|
16200
|
+
if (indexes.byPath.has(link.target)) return false;
|
|
16201
|
+
const stripped = stripTriggerSigil(link.trigger?.normalizedTrigger);
|
|
16202
|
+
if (stripped !== null && indexes.byName.has(stripped)) return false;
|
|
16203
|
+
return true;
|
|
16204
|
+
}
|
|
16205
|
+
function resolveByName(link, indexes, ctx) {
|
|
16206
|
+
const stripped = stripTriggerSigil(link.trigger?.normalizedTrigger);
|
|
16207
|
+
if (stripped === null) return "none";
|
|
16208
|
+
const candidates = indexes.byName.get(stripped);
|
|
16209
|
+
if (!candidates?.length) return "none";
|
|
16210
|
+
const allowedKinds = lookupAllowedKinds(link, indexes, ctx);
|
|
16211
|
+
if (!allowedKinds?.length) return "none";
|
|
16212
|
+
const winner = candidates.find((c) => allowedKinds.includes(c.kind));
|
|
16213
|
+
return winner ? winner.path : "none";
|
|
16214
|
+
}
|
|
16215
|
+
function lookupAllowedKinds(link, _indexes, ctx) {
|
|
16216
|
+
if (ctx.activeProvider === null) return void 0;
|
|
16217
|
+
return ctx.providerResolution.get(ctx.activeProvider)?.[link.kind];
|
|
16218
|
+
}
|
|
16219
|
+
function stripTriggerSigil(normalized) {
|
|
16220
|
+
if (!normalized) return null;
|
|
16221
|
+
const trimmed = normalized.replace(/^[/@]/, "").trim();
|
|
16222
|
+
return trimmed.length === 0 ? null : trimmed;
|
|
16223
|
+
}
|
|
16224
|
+
function indexNode(node, ctx, byName) {
|
|
16225
|
+
const kindDescriptor = ctx.kindRegistry.get(kindKey(node));
|
|
16226
|
+
const normalised = deriveNodeIdentifiers(node, kindDescriptor);
|
|
16227
|
+
for (const name of normalised) {
|
|
16228
|
+
const entry = { kind: node.kind, path: node.path };
|
|
16229
|
+
const bucket = byName.get(name);
|
|
16230
|
+
if (bucket) {
|
|
16231
|
+
bucket.push(entry);
|
|
16232
|
+
} else {
|
|
16233
|
+
byName.set(name, [entry]);
|
|
16234
|
+
}
|
|
16235
|
+
}
|
|
16236
|
+
}
|
|
16237
|
+
function kindKey(node) {
|
|
16238
|
+
return `${node.provider}/${node.kind}`;
|
|
16239
|
+
}
|
|
16240
|
+
|
|
16455
16241
|
// kernel/orchestrator/post-walk-transforms.ts
|
|
16456
16242
|
var POST_WALK_TRANSFORMS = [
|
|
16457
16243
|
{
|
|
@@ -17448,6 +17234,7 @@ async function runScanInternal(_kernel, options) {
|
|
|
17448
17234
|
const postWalkCtx = buildPostWalkTransformCtx(exts.providers, walked.nodes, activeProviderId);
|
|
17449
17235
|
walked.internalLinks = applyPostWalkTransforms(walked.internalLinks, walked.nodes, postWalkCtx);
|
|
17450
17236
|
const brokenLinks = collectBrokenLinks(walked.internalLinks, walked.nodes, postWalkCtx);
|
|
17237
|
+
const nameCollisions = collectNameCollisions(walked.nodes, postWalkCtx.kindRegistry);
|
|
17451
17238
|
recomputeLinkCounts(walked.nodes, walked.internalLinks);
|
|
17452
17239
|
recomputeExternalRefsCount(walked.nodes, walked.externalLinks, walked.cachedPaths);
|
|
17453
17240
|
await dispatchExtractorCompleted(exts.extractors, emitter, hookDispatcher);
|
|
@@ -17462,7 +17249,6 @@ async function runScanInternal(_kernel, options) {
|
|
|
17462
17249
|
walked.sidecarRoots,
|
|
17463
17250
|
options.annotationContributions ?? [],
|
|
17464
17251
|
options.viewContributions ?? [],
|
|
17465
|
-
options.orphanJobFiles ?? [],
|
|
17466
17252
|
options.referenceablePaths,
|
|
17467
17253
|
options.cwd,
|
|
17468
17254
|
registeredActionIds,
|
|
@@ -17470,6 +17256,7 @@ async function runScanInternal(_kernel, options) {
|
|
|
17470
17256
|
hookDispatcher,
|
|
17471
17257
|
postWalkCtx.reservedNodePaths,
|
|
17472
17258
|
brokenLinks,
|
|
17259
|
+
nameCollisions,
|
|
17473
17260
|
walked.signals,
|
|
17474
17261
|
// Seed the accumulator with orchestrator-emitted frontmatter
|
|
17475
17262
|
// issues so the aggregate phase (`core/issue-counter`) counts
|
|
@@ -17492,7 +17279,7 @@ async function runScanInternal(_kernel, options) {
|
|
|
17492
17279
|
const scanCompletedEvent = makeEvent("scan.completed", { stats });
|
|
17493
17280
|
emitter.emit(scanCompletedEvent);
|
|
17494
17281
|
await hookDispatcher.dispatch("scan.completed", scanCompletedEvent);
|
|
17495
|
-
return buildScanReturn(walked, issues, renameOps, stats, options, setup);
|
|
17282
|
+
return buildScanReturn(walked, issues, renameOps, stats, options, setup, analyzerResult.linkScores);
|
|
17496
17283
|
}
|
|
17497
17284
|
function buildPostWalkTransformCtx(providers, nodes, activeProvider) {
|
|
17498
17285
|
const { kindRegistry, providerResolution, reservedNamesByProviderKind } = buildProviderIndexes(providers);
|
|
@@ -17620,7 +17407,7 @@ function buildScanStats(walked, issues, start) {
|
|
|
17620
17407
|
durationMs: Date.now() - start
|
|
17621
17408
|
};
|
|
17622
17409
|
}
|
|
17623
|
-
function buildScanReturn(walked, issues, renameOps, stats, options, setup) {
|
|
17410
|
+
function buildScanReturn(walked, issues, renameOps, stats, options, setup, linkScores) {
|
|
17624
17411
|
return {
|
|
17625
17412
|
result: {
|
|
17626
17413
|
schemaVersion: 1,
|
|
@@ -17642,6 +17429,7 @@ function buildScanReturn(walked, issues, renameOps, stats, options, setup) {
|
|
|
17642
17429
|
enrichments: walked.enrichments,
|
|
17643
17430
|
contributions: walked.contributions,
|
|
17644
17431
|
contributionErrors: walked.contributionErrors,
|
|
17432
|
+
linkScores,
|
|
17645
17433
|
freshlyRunTuples: walked.freshlyRunTuples
|
|
17646
17434
|
};
|
|
17647
17435
|
}
|
|
@@ -17887,33 +17675,6 @@ function createKernel() {
|
|
|
17887
17675
|
};
|
|
17888
17676
|
}
|
|
17889
17677
|
|
|
17890
|
-
// kernel/jobs/orphan-files.ts
|
|
17891
|
-
import { readdirSync as readdirSync8, statSync as statSync7 } from "fs";
|
|
17892
|
-
import { join as join14, resolve as resolve30 } from "path";
|
|
17893
|
-
function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
17894
|
-
let entries;
|
|
17895
|
-
try {
|
|
17896
|
-
const stat3 = statSync7(jobsDir);
|
|
17897
|
-
if (!stat3.isDirectory()) {
|
|
17898
|
-
return { orphanFilePaths: [], referencedCount: referencedPaths.size };
|
|
17899
|
-
}
|
|
17900
|
-
entries = readdirSync8(jobsDir, { withFileTypes: true });
|
|
17901
|
-
} catch {
|
|
17902
|
-
return { orphanFilePaths: [], referencedCount: referencedPaths.size };
|
|
17903
|
-
}
|
|
17904
|
-
const orphans = [];
|
|
17905
|
-
for (const entry of entries) {
|
|
17906
|
-
if (entry.isSymbolicLink()) continue;
|
|
17907
|
-
if (!entry.isFile()) continue;
|
|
17908
|
-
const name = entry.name;
|
|
17909
|
-
if (!name.endsWith(".md")) continue;
|
|
17910
|
-
const abs = resolve30(join14(jobsDir, name));
|
|
17911
|
-
if (!referencedPaths.has(abs)) orphans.push(abs);
|
|
17912
|
-
}
|
|
17913
|
-
orphans.sort();
|
|
17914
|
-
return { orphanFilePaths: orphans, referencedCount: referencedPaths.size };
|
|
17915
|
-
}
|
|
17916
|
-
|
|
17917
17678
|
// core/config/plugin-settings.ts
|
|
17918
17679
|
var defaultWarn = (message) => log.warn(message);
|
|
17919
17680
|
function resolveExtensionSettings(manifest, config, onWarn = defaultWarn) {
|
|
@@ -18218,9 +17979,9 @@ function resolveScanRoots(inputs) {
|
|
|
18218
17979
|
}
|
|
18219
17980
|
|
|
18220
17981
|
// core/runtime/reference-paths-walker.ts
|
|
18221
|
-
import { readdirSync as
|
|
17982
|
+
import { readdirSync as readdirSync8, statSync as statSync7 } from "fs";
|
|
18222
17983
|
import { homedir as osHomedir2 } from "os";
|
|
18223
|
-
import { isAbsolute as isAbsolute8, join as
|
|
17984
|
+
import { isAbsolute as isAbsolute8, join as join14, resolve as resolve30 } from "path";
|
|
18224
17985
|
var REFERENCE_WALK_MAX_FILES = 5e4;
|
|
18225
17986
|
var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
18226
17987
|
"node_modules",
|
|
@@ -18228,10 +17989,10 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
|
18228
17989
|
SKILL_MAP_DIR
|
|
18229
17990
|
]);
|
|
18230
17991
|
function resolveScanPath(raw, cwd) {
|
|
18231
|
-
if (raw.startsWith("~/")) return
|
|
18232
|
-
if (raw === "~") return
|
|
18233
|
-
if (isAbsolute8(raw)) return
|
|
18234
|
-
return
|
|
17992
|
+
if (raw.startsWith("~/")) return resolve30(join14(osHomedir2(), raw.slice(2)));
|
|
17993
|
+
if (raw === "~") return resolve30(osHomedir2());
|
|
17994
|
+
if (isAbsolute8(raw)) return resolve30(raw);
|
|
17995
|
+
return resolve30(cwd, raw);
|
|
18235
17996
|
}
|
|
18236
17997
|
function walkReferencePaths(rawRoots, cwd) {
|
|
18237
17998
|
const paths = /* @__PURE__ */ new Set();
|
|
@@ -18253,14 +18014,14 @@ function walkInto(dir, out) {
|
|
|
18253
18014
|
if (out.size >= REFERENCE_WALK_MAX_FILES) return true;
|
|
18254
18015
|
let entries;
|
|
18255
18016
|
try {
|
|
18256
|
-
entries =
|
|
18017
|
+
entries = readdirSync8(dir, { withFileTypes: true });
|
|
18257
18018
|
} catch {
|
|
18258
18019
|
return false;
|
|
18259
18020
|
}
|
|
18260
18021
|
for (const entry of entries) {
|
|
18261
18022
|
if (out.size >= REFERENCE_WALK_MAX_FILES) return true;
|
|
18262
18023
|
if (entry.isSymbolicLink()) continue;
|
|
18263
|
-
const full =
|
|
18024
|
+
const full = join14(dir, entry.name);
|
|
18264
18025
|
if (entry.isDirectory()) {
|
|
18265
18026
|
if (SKIPPED_DIR_NAMES.has(entry.name)) continue;
|
|
18266
18027
|
if (walkInto(full, out)) return true;
|
|
@@ -18272,7 +18033,7 @@ function walkInto(dir, out) {
|
|
|
18272
18033
|
}
|
|
18273
18034
|
function safeStat(path) {
|
|
18274
18035
|
try {
|
|
18275
|
-
return
|
|
18036
|
+
return statSync7(path);
|
|
18276
18037
|
} catch {
|
|
18277
18038
|
return null;
|
|
18278
18039
|
}
|
|
@@ -18280,7 +18041,7 @@ function safeStat(path) {
|
|
|
18280
18041
|
|
|
18281
18042
|
// core/runtime/active-provider-bootstrap.ts
|
|
18282
18043
|
import { createInterface as createInterface3 } from "readline";
|
|
18283
|
-
import { isAbsolute as isAbsolute9, join as
|
|
18044
|
+
import { isAbsolute as isAbsolute9, join as join15 } from "path";
|
|
18284
18045
|
async function bootstrapActiveProvider(opts) {
|
|
18285
18046
|
const fromCwd = resolveActiveProvider(opts.cwd, opts.providers);
|
|
18286
18047
|
if (fromCwd.source === "config") {
|
|
@@ -18339,7 +18100,7 @@ function aggregateDetected(cwd, effectiveRoots, cwdDetected, providers) {
|
|
|
18339
18100
|
out.push(id);
|
|
18340
18101
|
}
|
|
18341
18102
|
for (const root of effectiveRoots) {
|
|
18342
|
-
const absRoot = isAbsolute9(root) ? root :
|
|
18103
|
+
const absRoot = isAbsolute9(root) ? root : join15(cwd, root);
|
|
18343
18104
|
const r = resolveActiveProvider(absRoot, providers);
|
|
18344
18105
|
for (const id of r.detected) {
|
|
18345
18106
|
if (seen.has(id)) continue;
|
|
@@ -18578,7 +18339,6 @@ async function runScanForCommand(opts) {
|
|
|
18578
18339
|
emitReferenceWalkAdvisory(walk3, opts);
|
|
18579
18340
|
}
|
|
18580
18341
|
const loadPrior = makePriorLoader(opts.noBuiltIns, strict);
|
|
18581
|
-
const jobsDir = defaultProjectJobsDir(ctx);
|
|
18582
18342
|
const lens = await resolveActiveLens(
|
|
18583
18343
|
opts,
|
|
18584
18344
|
ctx,
|
|
@@ -18603,7 +18363,7 @@ async function runScanForCommand(opts) {
|
|
|
18603
18363
|
cfg.tokenizer
|
|
18604
18364
|
);
|
|
18605
18365
|
const willPersist = !opts.noBuiltIns && !opts.dryRun;
|
|
18606
|
-
const scanned = await (willPersist ? runPersistPath(opts, dbPath,
|
|
18366
|
+
const scanned = await (willPersist ? runPersistPath(opts, dbPath, strict, loadPrior, runScanWith, extensions) : runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith));
|
|
18607
18367
|
return scanned.kind === "ok" ? { ...scanned, lensAutoDetected: lens.autoDetected } : scanned;
|
|
18608
18368
|
}
|
|
18609
18369
|
function detectionProviders(extensions) {
|
|
@@ -18716,7 +18476,7 @@ function makePriorLoader(noBuiltIns, strict) {
|
|
|
18716
18476
|
};
|
|
18717
18477
|
}
|
|
18718
18478
|
function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, extensions, referenceablePaths, scanCwd, activeProvider, recommendedNodeLimit, maxFileSizeBytes, tokenizer) {
|
|
18719
|
-
return async (prior, priorExtractorRuns
|
|
18479
|
+
return async (prior, priorExtractorRuns) => {
|
|
18720
18480
|
if (opts.changed && prior === null) {
|
|
18721
18481
|
opts.stderr.write(SCAN_RUNNER_TEXTS.changedNoPriorWarning);
|
|
18722
18482
|
}
|
|
@@ -18733,14 +18493,13 @@ function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, exte
|
|
|
18733
18493
|
recommendedNodeLimit,
|
|
18734
18494
|
maxFileSizeBytes,
|
|
18735
18495
|
tokenizer,
|
|
18736
|
-
...priorExtractorRuns ? { priorExtractorRuns } : {}
|
|
18737
|
-
...orphanJobFiles ? { orphanJobFiles } : {}
|
|
18496
|
+
...priorExtractorRuns ? { priorExtractorRuns } : {}
|
|
18738
18497
|
});
|
|
18739
18498
|
return runScanWithRenames(kernel, runOptions);
|
|
18740
18499
|
};
|
|
18741
18500
|
}
|
|
18742
18501
|
function buildRunScanOptions(args2) {
|
|
18743
|
-
const { opts, prior, priorExtractorRuns,
|
|
18502
|
+
const { opts, prior, priorExtractorRuns, referenceablePaths } = args2;
|
|
18744
18503
|
const runOptions = {
|
|
18745
18504
|
roots: args2.effectiveRoots.slice(),
|
|
18746
18505
|
tokenize: !opts.noTokens,
|
|
@@ -18748,11 +18507,6 @@ function buildRunScanOptions(args2) {
|
|
|
18748
18507
|
ignoreFilter: args2.ignoreFilter,
|
|
18749
18508
|
strict: args2.strict,
|
|
18750
18509
|
emitter: buildRunScanEmitter(opts),
|
|
18751
|
-
// Orphan job-file detection, empty list means "no orphans
|
|
18752
|
-
// visible from this caller" (legacy behaviour). The orchestrator
|
|
18753
|
-
// defaults to `[]` when the field is absent; we always pass the
|
|
18754
|
-
// array (possibly empty) to keep the wiring uniform.
|
|
18755
|
-
orphanJobFiles: orphanJobFiles ?? [],
|
|
18756
18510
|
activeProvider: args2.activeProvider,
|
|
18757
18511
|
recommendedNodeLimit: args2.recommendedNodeLimit,
|
|
18758
18512
|
overrideMaxNodes: opts.maxNodes ?? null,
|
|
@@ -18795,7 +18549,7 @@ async function rebuildOnDrift(opts, dbPath) {
|
|
|
18795
18549
|
})
|
|
18796
18550
|
};
|
|
18797
18551
|
}
|
|
18798
|
-
async function runPersistPath(opts, dbPath,
|
|
18552
|
+
async function runPersistPath(opts, dbPath, strict, loadPrior, runScanWith, extensions) {
|
|
18799
18553
|
const driftError = await rebuildOnDrift(opts, dbPath);
|
|
18800
18554
|
if (driftError) return driftError;
|
|
18801
18555
|
let outcome;
|
|
@@ -18803,11 +18557,9 @@ async function runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanW
|
|
|
18803
18557
|
outcome = await withSqlite({ databasePath: dbPath }, async (adapter) => {
|
|
18804
18558
|
const prior = await loadPrior(adapter);
|
|
18805
18559
|
const priorExtractorRuns = opts.changed && prior ? await adapter.scans.loadExtractorRuns() : void 0;
|
|
18806
|
-
const referencedJobFiles = await adapter.jobs.listReferencedFilePaths();
|
|
18807
|
-
const orphanJobFiles = findOrphanJobFiles(jobsDir, referencedJobFiles).orphanFilePaths;
|
|
18808
18560
|
let scanned;
|
|
18809
18561
|
try {
|
|
18810
|
-
scanned = await runScanWith(prior, priorExtractorRuns
|
|
18562
|
+
scanned = await runScanWith(prior, priorExtractorRuns);
|
|
18811
18563
|
} catch (err) {
|
|
18812
18564
|
return { kind: "scan-error", message: formatErrorMessage(err) };
|
|
18813
18565
|
}
|
|
@@ -18822,6 +18574,7 @@ async function runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanW
|
|
|
18822
18574
|
enrichments: scanned.enrichments,
|
|
18823
18575
|
contributions: scanned.contributions,
|
|
18824
18576
|
contributionErrors: scanned.contributionErrors,
|
|
18577
|
+
linkScores: scanned.linkScores,
|
|
18825
18578
|
registeredContributionKeys: collectRegisteredContributionKeys(extensions),
|
|
18826
18579
|
freshlyRunTuples: scanned.freshlyRunTuples
|
|
18827
18580
|
});
|
|
@@ -18938,7 +18691,7 @@ var InitCommand = class extends SmCommand {
|
|
|
18938
18691
|
async run() {
|
|
18939
18692
|
const ctx = defaultRuntimeContext();
|
|
18940
18693
|
const scopeRoot = ctx.cwd;
|
|
18941
|
-
const skillMapDir =
|
|
18694
|
+
const skillMapDir = join16(scopeRoot, SKILL_MAP_DIR);
|
|
18942
18695
|
const settingsPath = defaultSettingsPath(scopeRoot);
|
|
18943
18696
|
const localPath = defaultLocalSettingsPath(scopeRoot);
|
|
18944
18697
|
const ignorePath = defaultIgnoreFilePath(scopeRoot);
|
|
@@ -18984,7 +18737,7 @@ var InitCommand = class extends SmCommand {
|
|
|
18984
18737
|
const okGlyph = ansi.green("\u2713");
|
|
18985
18738
|
const updated = await ensureGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
|
|
18986
18739
|
if (updated) {
|
|
18987
|
-
const gitignorePath =
|
|
18740
|
+
const gitignorePath = join16(scopeRoot, ".gitignore");
|
|
18988
18741
|
printer.info(
|
|
18989
18742
|
GITIGNORE_ENTRIES.length === 1 ? tx(INIT_TEXTS.gitignoreUpdatedSingular, { glyph: okGlyph, path: gitignorePath }) : tx(INIT_TEXTS.gitignoreUpdatedPlural, {
|
|
18990
18743
|
glyph: okGlyph,
|
|
@@ -19045,7 +18798,7 @@ async function safeUnlink(path) {
|
|
|
19045
18798
|
}
|
|
19046
18799
|
async function writeDryRunGitignorePlan(printer, scopeRoot) {
|
|
19047
18800
|
const wouldAdd = await previewGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
|
|
19048
|
-
const gitignorePath =
|
|
18801
|
+
const gitignorePath = join16(scopeRoot, ".gitignore");
|
|
19049
18802
|
if (wouldAdd.length === 0) {
|
|
19050
18803
|
printer.info(tx(INIT_TEXTS.dryRunWouldLeaveGitignoreUnchanged, { path: gitignorePath }));
|
|
19051
18804
|
} else if (wouldAdd.length === 1) {
|
|
@@ -19150,7 +18903,7 @@ async function runFirstScan(scopeRoot, strict, printer, stderr, stdin, ansi) {
|
|
|
19150
18903
|
return hasErrors ? ExitCode.Issues : ExitCode.Ok;
|
|
19151
18904
|
}
|
|
19152
18905
|
async function previewGitignoreEntries(scopeRoot, entries) {
|
|
19153
|
-
const path =
|
|
18906
|
+
const path = join16(scopeRoot, ".gitignore");
|
|
19154
18907
|
const body = await pathExists(path) ? await readFile3(path, "utf8") : "";
|
|
19155
18908
|
const present = new Set(
|
|
19156
18909
|
body.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"))
|
|
@@ -19158,7 +18911,7 @@ async function previewGitignoreEntries(scopeRoot, entries) {
|
|
|
19158
18911
|
return entries.filter((entry) => !present.has(entry));
|
|
19159
18912
|
}
|
|
19160
18913
|
async function ensureGitignoreEntries(scopeRoot, entries) {
|
|
19161
|
-
const path =
|
|
18914
|
+
const path = join16(scopeRoot, ".gitignore");
|
|
19162
18915
|
let body = "";
|
|
19163
18916
|
if (await pathExists(path)) {
|
|
19164
18917
|
body = await readFile3(path, "utf8");
|
|
@@ -19699,6 +19452,33 @@ import { unlink as unlink2 } from "fs/promises";
|
|
|
19699
19452
|
import { relative as relative6 } from "path";
|
|
19700
19453
|
import { Command as Command19, Option as Option18 } from "clipanion";
|
|
19701
19454
|
|
|
19455
|
+
// kernel/jobs/orphan-files.ts
|
|
19456
|
+
import { readdirSync as readdirSync9, statSync as statSync8 } from "fs";
|
|
19457
|
+
import { join as join17, resolve as resolve31 } from "path";
|
|
19458
|
+
function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
19459
|
+
let entries;
|
|
19460
|
+
try {
|
|
19461
|
+
const stat3 = statSync8(jobsDir);
|
|
19462
|
+
if (!stat3.isDirectory()) {
|
|
19463
|
+
return { orphanFilePaths: [], referencedCount: referencedPaths.size };
|
|
19464
|
+
}
|
|
19465
|
+
entries = readdirSync9(jobsDir, { withFileTypes: true });
|
|
19466
|
+
} catch {
|
|
19467
|
+
return { orphanFilePaths: [], referencedCount: referencedPaths.size };
|
|
19468
|
+
}
|
|
19469
|
+
const orphans = [];
|
|
19470
|
+
for (const entry of entries) {
|
|
19471
|
+
if (entry.isSymbolicLink()) continue;
|
|
19472
|
+
if (!entry.isFile()) continue;
|
|
19473
|
+
const name = entry.name;
|
|
19474
|
+
if (!name.endsWith(".md")) continue;
|
|
19475
|
+
const abs = resolve31(join17(jobsDir, name));
|
|
19476
|
+
if (!referencedPaths.has(abs)) orphans.push(abs);
|
|
19477
|
+
}
|
|
19478
|
+
orphans.sort();
|
|
19479
|
+
return { orphanFilePaths: orphans, referencedCount: referencedPaths.size };
|
|
19480
|
+
}
|
|
19481
|
+
|
|
19702
19482
|
// cli/i18n/jobs.texts.ts
|
|
19703
19483
|
var JOBS_TEXTS = {
|
|
19704
19484
|
pruneErrorPrefix: "{{glyph}} sm job prune: {{message}}\n",
|
|
@@ -23899,6 +23679,7 @@ function createWatcherRuntime(opts) {
|
|
|
23899
23679
|
enrichments,
|
|
23900
23680
|
contributions,
|
|
23901
23681
|
contributionErrors,
|
|
23682
|
+
linkScores,
|
|
23902
23683
|
freshlyRunTuples
|
|
23903
23684
|
} = ran;
|
|
23904
23685
|
await withSqlite(
|
|
@@ -23909,6 +23690,7 @@ function createWatcherRuntime(opts) {
|
|
|
23909
23690
|
enrichments,
|
|
23910
23691
|
contributions,
|
|
23911
23692
|
contributionErrors,
|
|
23693
|
+
linkScores,
|
|
23912
23694
|
registeredContributionKeys: collectRegisteredContributionKeys(composed),
|
|
23913
23695
|
freshlyRunTuples
|
|
23914
23696
|
})
|
|
@@ -24391,8 +24173,8 @@ var ScanCommand = class extends SmCommand {
|
|
|
24391
24173
|
details: `
|
|
24392
24174
|
Walks the given roots with the built-in claude Provider, runs the
|
|
24393
24175
|
frontmatter / slash / at-directive / external-url-counter
|
|
24394
|
-
extractors per node, then the
|
|
24395
|
-
|
|
24176
|
+
extractors per node, then the name-collision / broken-ref
|
|
24177
|
+
analyzers over the full graph. Emits a ScanResult
|
|
24396
24178
|
conforming to scan-result.schema.json.
|
|
24397
24179
|
|
|
24398
24180
|
The result is persisted into <cwd>/.skill-map/skill-map.db
|
|
@@ -28494,6 +28276,43 @@ var WsBroadcaster = class {
|
|
|
28494
28276
|
}
|
|
28495
28277
|
};
|
|
28496
28278
|
|
|
28279
|
+
// server/heartbeat.ts
|
|
28280
|
+
var WS_HEARTBEAT_INTERVAL_MS = 3e4;
|
|
28281
|
+
function startWsHeartbeat(wss, opts = {}) {
|
|
28282
|
+
const intervalMs = opts.intervalMs ?? WS_HEARTBEAT_INTERVAL_MS;
|
|
28283
|
+
const alive = /* @__PURE__ */ new WeakMap();
|
|
28284
|
+
const onConnection = (socket) => {
|
|
28285
|
+
alive.set(socket, true);
|
|
28286
|
+
socket.on("pong", () => {
|
|
28287
|
+
alive.set(socket, true);
|
|
28288
|
+
});
|
|
28289
|
+
};
|
|
28290
|
+
wss.on("connection", onConnection);
|
|
28291
|
+
const timer = setInterval(() => {
|
|
28292
|
+
for (const socket of wss.clients) {
|
|
28293
|
+
if (alive.get(socket) === false) {
|
|
28294
|
+
socket.terminate();
|
|
28295
|
+
continue;
|
|
28296
|
+
}
|
|
28297
|
+
alive.set(socket, false);
|
|
28298
|
+
try {
|
|
28299
|
+
socket.ping();
|
|
28300
|
+
} catch {
|
|
28301
|
+
}
|
|
28302
|
+
}
|
|
28303
|
+
}, intervalMs);
|
|
28304
|
+
timer.unref?.();
|
|
28305
|
+
let stopped = false;
|
|
28306
|
+
return {
|
|
28307
|
+
stop() {
|
|
28308
|
+
if (stopped) return;
|
|
28309
|
+
stopped = true;
|
|
28310
|
+
clearInterval(timer);
|
|
28311
|
+
wss.off("connection", onConnection);
|
|
28312
|
+
}
|
|
28313
|
+
};
|
|
28314
|
+
}
|
|
28315
|
+
|
|
28497
28316
|
// server/kind-registry.ts
|
|
28498
28317
|
function buildKindRegistry(providers) {
|
|
28499
28318
|
const registry = {};
|
|
@@ -28777,6 +28596,7 @@ async function createServer(options, extra = {}) {
|
|
|
28777
28596
|
});
|
|
28778
28597
|
const wss = new WebSocketServer({ noServer: true });
|
|
28779
28598
|
const server = await listenAsync(app.fetch, wss, options.host, options.port);
|
|
28599
|
+
const heartbeat = startWsHeartbeat(wss);
|
|
28780
28600
|
const addr = server.address();
|
|
28781
28601
|
const address = normalizeAddress(addr, options.host, options.port);
|
|
28782
28602
|
let watcherService = null;
|
|
@@ -28810,6 +28630,7 @@ async function createServer(options, extra = {}) {
|
|
|
28810
28630
|
const close = async () => {
|
|
28811
28631
|
if (closed) return;
|
|
28812
28632
|
closed = true;
|
|
28633
|
+
heartbeat.stop();
|
|
28813
28634
|
if (watcherService) {
|
|
28814
28635
|
try {
|
|
28815
28636
|
await watcherService.stop();
|
|
@@ -31152,4 +30973,4 @@ function resolveBareDefault() {
|
|
|
31152
30973
|
process.exit(ExitCode.Error);
|
|
31153
30974
|
}
|
|
31154
30975
|
//# sourceMappingURL=cli.js.map
|
|
31155
|
-
//# debugId=
|
|
30976
|
+
//# debugId=7e4fdbab-f037-5030-b67a-732bb7ee46fd
|