@skill-map/cli 0.53.6 → 0.55.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/SKILL.md +22 -24
- package/dist/cli/tutorial/sm-tutorial/references/_core.md +8 -7
- package/dist/cli/tutorial/sm-tutorial/references/_manifest.yml +21 -42
- package/dist/cli/tutorial/sm-tutorial/references/fixtures.md +15 -7
- package/dist/cli/tutorial/sm-tutorial/references/part-authoring.md +2 -2
- package/dist/cli/tutorial/sm-tutorial/references/part-cli.md +1 -1
- package/dist/cli/tutorial/sm-tutorial/references/part-connect-harness.md +9 -10
- package/dist/cli/tutorial/sm-tutorial/references/part-daily-loop.md +563 -0
- package/dist/cli/tutorial/sm-tutorial/references/part-mcp.md +5 -1
- package/dist/cli/tutorial/sm-tutorial/references/part-plugins.md +7 -7
- package/dist/cli/tutorial/sm-tutorial/references/part-project-kickoff.md +24 -12
- package/dist/cli/tutorial/sm-tutorial/references/part-settings.md +2 -2
- package/dist/cli.js +1019 -718
- package/dist/index.js +129 -15
- package/dist/kernel/index.d.ts +209 -89
- package/dist/kernel/index.js +129 -15
- package/dist/migrations/001_initial.sql +6 -1
- package/dist/ui/chunk-CN6IOM67.js +2 -0
- package/dist/ui/chunk-HPKRDGLH.js +123 -0
- package/dist/ui/{chunk-NBXEOYS4.js → chunk-LREXXX7T.js} +1 -1
- package/dist/ui/{chunk-7OJPO3XD.js → chunk-RGB5FBZL.js} +31 -30
- package/dist/ui/{chunk-4CXAL43H.js → chunk-XAM6VKXM.js} +1 -1
- package/dist/ui/index.html +2 -2
- package/dist/ui/{main-O4DGYJ62.js → main-7YXBWYHE.js} +3 -3
- package/dist/ui/{styles-L6FZYH7X.css → styles-HRJG67XW.css} +1 -1
- package/migrations/001_initial.sql +6 -1
- package/package.json +2 -2
- package/dist/cli/tutorial/sm-tutorial/references/part-live-site.md +0 -155
- package/dist/cli/tutorial/sm-tutorial/references/part-maintain.md +0 -284
- package/dist/cli/tutorial/sm-tutorial/references/part-run-harness.md +0 -181
- package/dist/ui/chunk-HWQTV6ZL.js +0 -2
- package/dist/ui/chunk-PRP3JSUU.js +0 -123
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]="e21852cd-d92e-5bdf-abc7-02fffaa89502")}catch(e){}}();
|
|
4
4
|
import { existsSync as existsSync33 } from "fs";
|
|
5
5
|
import { Builtins, Cli as Cli2 } from "clipanion";
|
|
6
6
|
|
|
@@ -246,7 +246,7 @@ function bucketByKind(kind, instance, bag) {
|
|
|
246
246
|
// package.json
|
|
247
247
|
var package_default = {
|
|
248
248
|
name: "@skill-map/cli",
|
|
249
|
-
version: "0.
|
|
249
|
+
version: "0.55.0",
|
|
250
250
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
251
251
|
license: "MIT",
|
|
252
252
|
type: "module",
|
|
@@ -796,6 +796,16 @@ function stripCodeBlocks(input) {
|
|
|
796
796
|
const fenceless = stripFences(input);
|
|
797
797
|
return stripInline(fenceless);
|
|
798
798
|
}
|
|
799
|
+
function extractCodeRegions(input) {
|
|
800
|
+
if (!input) return input;
|
|
801
|
+
const stripped = stripCodeBlocks(input);
|
|
802
|
+
let out = "";
|
|
803
|
+
for (let i = 0; i < input.length; i++) {
|
|
804
|
+
const ch = input[i];
|
|
805
|
+
out += ch === stripped[i] ? ch === "\n" ? "\n" : " " : ch;
|
|
806
|
+
}
|
|
807
|
+
return out;
|
|
808
|
+
}
|
|
799
809
|
function stripFences(input) {
|
|
800
810
|
const out = [];
|
|
801
811
|
const lines = input.split("\n");
|
|
@@ -873,7 +883,7 @@ var atDirectiveExtractor = {
|
|
|
873
883
|
id: ID,
|
|
874
884
|
pluginId: CLAUDE_PLUGIN_ID,
|
|
875
885
|
kind: "extractor",
|
|
876
|
-
description: "Detects `@<token>` directives in a node's body using Claude Code rules.
|
|
886
|
+
description: "Detects `@<token>` directives in a node's body using Claude Code rules, choosing the link kind by token shape. Example: a bare handle `@team` becomes a `mentions` link, while a file-flavoured token `@docs/api.md` becomes a `references` link.",
|
|
877
887
|
scope: "body",
|
|
878
888
|
precondition: { provider: ["claude"] },
|
|
879
889
|
// eslint-disable-next-line complexity
|
|
@@ -961,7 +971,7 @@ var slashCommandExtractor = {
|
|
|
961
971
|
id: ID2,
|
|
962
972
|
pluginId: CLAUDE_PLUGIN_ID,
|
|
963
973
|
kind: "extractor",
|
|
964
|
-
description: "Turns `/command` invocations in a node's body into arrows that point at the resolved slash command or skill, using Claude Code routing rules.",
|
|
974
|
+
description: "Turns `/command` invocations in a node's body into arrows that point at the resolved slash command or skill, using Claude Code routing rules. Example: `/deploy` in the body draws an arrow to the `deploy` command.",
|
|
965
975
|
scope: "body",
|
|
966
976
|
precondition: { provider: ["claude"] },
|
|
967
977
|
extract(ctx) {
|
|
@@ -1005,6 +1015,44 @@ var slashCommandExtractor = {
|
|
|
1005
1015
|
}
|
|
1006
1016
|
};
|
|
1007
1017
|
|
|
1018
|
+
// plugins/claude/extractors/tools-counter/index.ts
|
|
1019
|
+
var ID3 = "tools-counter";
|
|
1020
|
+
var count = {
|
|
1021
|
+
slot: "card.footer.left",
|
|
1022
|
+
icon: "pi-wrench",
|
|
1023
|
+
label: "tools",
|
|
1024
|
+
emitWhenEmpty: false,
|
|
1025
|
+
priority: 40
|
|
1026
|
+
};
|
|
1027
|
+
var TOOLTIP_MAX = 255;
|
|
1028
|
+
var toolsCounterExtractor = {
|
|
1029
|
+
id: ID3,
|
|
1030
|
+
pluginId: CLAUDE_PLUGIN_ID,
|
|
1031
|
+
kind: "extractor",
|
|
1032
|
+
description: "Counts the tools an agent declares in its frontmatter and shows the count on the agent card. Example: an agent with `tools: [Bash, Read, Grep]` shows a count of 3.",
|
|
1033
|
+
scope: "frontmatter",
|
|
1034
|
+
precondition: { kind: ["claude/agent"] },
|
|
1035
|
+
ui: { count },
|
|
1036
|
+
extract(ctx) {
|
|
1037
|
+
const raw = ctx.frontmatter["tools"];
|
|
1038
|
+
if (!Array.isArray(raw)) return;
|
|
1039
|
+
const names = [];
|
|
1040
|
+
for (const t of raw) {
|
|
1041
|
+
if (typeof t === "string" && t.length > 0) names.push(t);
|
|
1042
|
+
}
|
|
1043
|
+
if (names.length === 0) return;
|
|
1044
|
+
ctx.emitContribution(count, {
|
|
1045
|
+
value: names.length,
|
|
1046
|
+
tooltip: buildTooltip(names)
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
function buildTooltip(names) {
|
|
1051
|
+
const joined = names.join(" \xB7 ");
|
|
1052
|
+
if (joined.length <= TOOLTIP_MAX) return joined;
|
|
1053
|
+
return `${joined.slice(0, TOOLTIP_MAX - 1)}\u2026`;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1008
1056
|
// plugins/antigravity/providers/antigravity/index.ts
|
|
1009
1057
|
var antigravityProvider = {
|
|
1010
1058
|
id: "antigravity",
|
|
@@ -1391,12 +1439,12 @@ var coreMarkdownProvider = {
|
|
|
1391
1439
|
};
|
|
1392
1440
|
|
|
1393
1441
|
// plugins/core/extractors/annotations/index.ts
|
|
1394
|
-
var
|
|
1442
|
+
var ID4 = "annotations";
|
|
1395
1443
|
var annotationsExtractor = {
|
|
1396
|
-
id:
|
|
1444
|
+
id: ID4,
|
|
1397
1445
|
pluginId: CORE_PLUGIN_ID,
|
|
1398
1446
|
kind: "extractor",
|
|
1399
|
-
description: "Turns the `supersedes` and `supersededBy` entries from a node's `.sm` sidecar into arrows between nodes in the graph.",
|
|
1447
|
+
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`.",
|
|
1400
1448
|
scope: "frontmatter",
|
|
1401
1449
|
extract(ctx) {
|
|
1402
1450
|
const sourcePath = ctx.node.path;
|
|
@@ -1412,7 +1460,7 @@ var annotationsExtractor = {
|
|
|
1412
1460
|
raw: target,
|
|
1413
1461
|
candidates: [
|
|
1414
1462
|
{
|
|
1415
|
-
extractorId:
|
|
1463
|
+
extractorId: ID4,
|
|
1416
1464
|
kind: "supersedes",
|
|
1417
1465
|
target,
|
|
1418
1466
|
confidence: 1,
|
|
@@ -1449,9 +1497,67 @@ function stringArray(value) {
|
|
|
1449
1497
|
return value.filter((v) => typeof v === "string" && v.length > 0);
|
|
1450
1498
|
}
|
|
1451
1499
|
|
|
1500
|
+
// plugins/core/extractors/backtick-path/index.ts
|
|
1501
|
+
import { posix as pathPosix2 } from "path";
|
|
1502
|
+
var ID5 = "backtick-path";
|
|
1503
|
+
var PATH_RE = /(?<![\w/:.-])(?:\.{1,2}\/)?[\w][\w.-]*(?:\/[\w.-]+)*\.md\b(?![\w/])/g;
|
|
1504
|
+
var backtickPathExtractor = {
|
|
1505
|
+
id: ID5,
|
|
1506
|
+
pluginId: CORE_PLUGIN_ID,
|
|
1507
|
+
kind: "extractor",
|
|
1508
|
+
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.",
|
|
1509
|
+
scope: "body",
|
|
1510
|
+
extract(ctx) {
|
|
1511
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1512
|
+
const body = extractCodeRegions(ctx.body);
|
|
1513
|
+
const lineStarts = computeLineStarts(body);
|
|
1514
|
+
const sourceDir = pathPosix2.dirname(ctx.node.path);
|
|
1515
|
+
for (const match of body.matchAll(PATH_RE)) {
|
|
1516
|
+
const original = match[0];
|
|
1517
|
+
const resolved = resolveTarget(sourceDir, original);
|
|
1518
|
+
if (resolved === null) continue;
|
|
1519
|
+
if (seen.has(resolved)) continue;
|
|
1520
|
+
seen.add(resolved);
|
|
1521
|
+
const offset = match.index ?? 0;
|
|
1522
|
+
const line = lineFor(lineStarts, offset);
|
|
1523
|
+
ctx.emitSignal({
|
|
1524
|
+
source: ctx.node.path,
|
|
1525
|
+
scope: "body",
|
|
1526
|
+
range: { start: offset, end: offset + original.length, line },
|
|
1527
|
+
raw: original,
|
|
1528
|
+
candidates: [
|
|
1529
|
+
{
|
|
1530
|
+
extractorId: ID5,
|
|
1531
|
+
kind: "points",
|
|
1532
|
+
target: resolved,
|
|
1533
|
+
// 0.85: a strong file signal with one degree of inference,
|
|
1534
|
+
// the author wrote a path inside a code region rather than
|
|
1535
|
+
// an explicit `[text](path)` link. Whether the path resolves
|
|
1536
|
+
// to a real node is a separate concern (`core/reference-broken`
|
|
1537
|
+
// flags unresolved targets), not a confidence question.
|
|
1538
|
+
confidence: 0.85,
|
|
1539
|
+
rationale: "relative .md path inside a code region",
|
|
1540
|
+
trigger: {
|
|
1541
|
+
originalTrigger: original,
|
|
1542
|
+
normalizedTrigger: resolved
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
]
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
|
+
function resolveTarget(sourceDir, raw) {
|
|
1551
|
+
const trimmed = raw.trim();
|
|
1552
|
+
if (trimmed.length === 0) return null;
|
|
1553
|
+
if (trimmed.startsWith("/")) return null;
|
|
1554
|
+
const joined = sourceDir === "." ? trimmed : `${sourceDir}/${trimmed}`;
|
|
1555
|
+
return pathPosix2.normalize(joined);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1452
1558
|
// plugins/core/extractors/external-url-counter/index.ts
|
|
1453
|
-
var
|
|
1454
|
-
var
|
|
1559
|
+
var ID6 = "external-url-counter";
|
|
1560
|
+
var count2 = {
|
|
1455
1561
|
slot: "card.footer.left",
|
|
1456
1562
|
icon: "pi-link",
|
|
1457
1563
|
label: "urls",
|
|
@@ -1461,10 +1567,10 @@ var count = {
|
|
|
1461
1567
|
var URL_RE = /https?:\/\/[^\s<>"'`)\]]+/g;
|
|
1462
1568
|
var TRAILING_PUNCT = /[.,;:!?]+$/;
|
|
1463
1569
|
var externalUrlCounterExtractor = {
|
|
1464
|
-
id:
|
|
1570
|
+
id: ID6,
|
|
1465
1571
|
pluginId: CORE_PLUGIN_ID,
|
|
1466
1572
|
kind: "extractor",
|
|
1467
|
-
description: "Counts the distinct external URLs in a node's body and shows the count on the card.",
|
|
1573
|
+
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.",
|
|
1468
1574
|
scope: "body",
|
|
1469
1575
|
/**
|
|
1470
1576
|
* Phase 6 / View contribution system, surface the distinct-URL
|
|
@@ -1482,7 +1588,7 @@ var externalUrlCounterExtractor = {
|
|
|
1482
1588
|
* inherited from the footer `.sm-gnode__stat` styles cloned by
|
|
1483
1589
|
* the `NodeCounter` renderer.
|
|
1484
1590
|
*/
|
|
1485
|
-
ui: { count },
|
|
1591
|
+
ui: { count: count2 },
|
|
1486
1592
|
extract(ctx) {
|
|
1487
1593
|
const seen = /* @__PURE__ */ new Set();
|
|
1488
1594
|
const body = stripCodeBlocks(ctx.body);
|
|
@@ -1503,7 +1609,7 @@ var externalUrlCounterExtractor = {
|
|
|
1503
1609
|
raw: original,
|
|
1504
1610
|
candidates: [
|
|
1505
1611
|
{
|
|
1506
|
-
extractorId:
|
|
1612
|
+
extractorId: ID6,
|
|
1507
1613
|
kind: "references",
|
|
1508
1614
|
target: normalized,
|
|
1509
1615
|
confidence: 0.3,
|
|
@@ -1517,7 +1623,7 @@ var externalUrlCounterExtractor = {
|
|
|
1517
1623
|
});
|
|
1518
1624
|
}
|
|
1519
1625
|
if (seen.size > 0) {
|
|
1520
|
-
ctx.emitContribution(
|
|
1626
|
+
ctx.emitContribution(count2, { value: seen.size });
|
|
1521
1627
|
}
|
|
1522
1628
|
}
|
|
1523
1629
|
};
|
|
@@ -1536,24 +1642,24 @@ function normalizeUrl(raw) {
|
|
|
1536
1642
|
}
|
|
1537
1643
|
|
|
1538
1644
|
// plugins/core/extractors/markdown-link/index.ts
|
|
1539
|
-
import { posix as
|
|
1540
|
-
var
|
|
1645
|
+
import { posix as pathPosix3 } from "path";
|
|
1646
|
+
var ID7 = "markdown-link";
|
|
1541
1647
|
var LINK_RE = /(?<!!)\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
1542
1648
|
var URL_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
|
|
1543
1649
|
var markdownLinkExtractor = {
|
|
1544
|
-
id:
|
|
1650
|
+
id: ID7,
|
|
1545
1651
|
pluginId: CORE_PLUGIN_ID,
|
|
1546
1652
|
kind: "extractor",
|
|
1547
|
-
description: "Turns markdown links (`[text](path)`) in a node's body into arrows between nodes in the graph.",
|
|
1653
|
+
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`.",
|
|
1548
1654
|
scope: "body",
|
|
1549
1655
|
extract(ctx) {
|
|
1550
1656
|
const seen = /* @__PURE__ */ new Set();
|
|
1551
1657
|
const body = stripCodeBlocks(ctx.body);
|
|
1552
1658
|
const lineStarts = computeLineStarts(body);
|
|
1553
|
-
const sourceDir =
|
|
1659
|
+
const sourceDir = pathPosix3.dirname(ctx.node.path);
|
|
1554
1660
|
for (const match of body.matchAll(LINK_RE)) {
|
|
1555
1661
|
const original = match[2];
|
|
1556
|
-
const resolved =
|
|
1662
|
+
const resolved = resolveTarget2(sourceDir, original);
|
|
1557
1663
|
if (resolved === null) continue;
|
|
1558
1664
|
if (seen.has(resolved)) continue;
|
|
1559
1665
|
seen.add(resolved);
|
|
@@ -1566,16 +1672,18 @@ var markdownLinkExtractor = {
|
|
|
1566
1672
|
raw: match[0],
|
|
1567
1673
|
candidates: [
|
|
1568
1674
|
{
|
|
1569
|
-
extractorId:
|
|
1675
|
+
extractorId: ID7,
|
|
1570
1676
|
kind: "references",
|
|
1571
1677
|
target: resolved,
|
|
1572
|
-
//
|
|
1573
|
-
//
|
|
1574
|
-
//
|
|
1575
|
-
//
|
|
1576
|
-
//
|
|
1577
|
-
//
|
|
1578
|
-
|
|
1678
|
+
// 0.95: the `[text](path)` syntax is unambiguous (the spec's
|
|
1679
|
+
// "unambiguous syntax" tier), but NOT 1.0. `1.0` is reserved
|
|
1680
|
+
// for structured input and is earned post-walk by the
|
|
1681
|
+
// confidence-lift transform when `target` resolves to a real
|
|
1682
|
+
// node; an unresolved (broken) target is downgraded to the
|
|
1683
|
+
// broken floor (0.5) by the same transform. Emitting 1.0 here
|
|
1684
|
+
// would short-circuit the lift and make a broken link
|
|
1685
|
+
// indistinguishable from a resolved one.
|
|
1686
|
+
confidence: 0.95,
|
|
1579
1687
|
rationale: "unambiguous markdown link syntax",
|
|
1580
1688
|
trigger: {
|
|
1581
1689
|
originalTrigger: original,
|
|
@@ -1587,7 +1695,7 @@ var markdownLinkExtractor = {
|
|
|
1587
1695
|
}
|
|
1588
1696
|
}
|
|
1589
1697
|
};
|
|
1590
|
-
function
|
|
1698
|
+
function resolveTarget2(sourceDir, raw) {
|
|
1591
1699
|
const noFragment = raw.split("#", 1)[0];
|
|
1592
1700
|
const noQuery = noFragment.split("?", 1)[0];
|
|
1593
1701
|
const trimmed = noQuery.trim();
|
|
@@ -1595,17 +1703,21 @@ function resolveTarget(sourceDir, raw) {
|
|
|
1595
1703
|
if (URL_SCHEME_RE.test(trimmed)) return null;
|
|
1596
1704
|
if (trimmed.startsWith("/")) return null;
|
|
1597
1705
|
const joined = sourceDir === "." ? trimmed : `${sourceDir}/${trimmed}`;
|
|
1598
|
-
return
|
|
1706
|
+
return pathPosix3.normalize(joined);
|
|
1599
1707
|
}
|
|
1600
1708
|
|
|
1601
1709
|
// plugins/core/extractors/mcp-tools/index.ts
|
|
1602
|
-
var
|
|
1710
|
+
var ID8 = "mcp-tools";
|
|
1603
1711
|
var MCP_PATTERN = /^mcp__([a-z0-9][a-z0-9_-]*)__[a-z0-9_-]+$/i;
|
|
1604
1712
|
var mcpToolsExtractor = {
|
|
1605
|
-
id:
|
|
1713
|
+
id: ID8,
|
|
1606
1714
|
pluginId: CORE_PLUGIN_ID,
|
|
1607
1715
|
kind: "extractor",
|
|
1608
|
-
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.",
|
|
1716
|
+
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.",
|
|
1717
|
+
// Claude-convention pattern only; per-vendor flavours and the
|
|
1718
|
+
// config-side MCP declaration (Phase 5b) are still pending, so the
|
|
1719
|
+
// extractor ships flagged as experimental in list / show / Settings.
|
|
1720
|
+
stability: "experimental",
|
|
1609
1721
|
scope: "frontmatter",
|
|
1610
1722
|
extract(ctx) {
|
|
1611
1723
|
const raw = ctx.frontmatter["tools"];
|
|
@@ -1629,7 +1741,7 @@ var mcpToolsExtractor = {
|
|
|
1629
1741
|
raw: `mcp__${server}__*`,
|
|
1630
1742
|
candidates: [
|
|
1631
1743
|
{
|
|
1632
|
-
extractorId:
|
|
1744
|
+
extractorId: ID8,
|
|
1633
1745
|
kind: "references",
|
|
1634
1746
|
target: mcpPath,
|
|
1635
1747
|
confidence: 0.85,
|
|
@@ -1659,44 +1771,6 @@ function collectMcpServers(tools) {
|
|
|
1659
1771
|
return out;
|
|
1660
1772
|
}
|
|
1661
1773
|
|
|
1662
|
-
// plugins/core/extractors/tools-counter/index.ts
|
|
1663
|
-
var ID7 = "tools-counter";
|
|
1664
|
-
var count2 = {
|
|
1665
|
-
slot: "card.footer.left",
|
|
1666
|
-
icon: "pi-wrench",
|
|
1667
|
-
label: "tools",
|
|
1668
|
-
emitWhenEmpty: false,
|
|
1669
|
-
priority: 40
|
|
1670
|
-
};
|
|
1671
|
-
var TOOLTIP_MAX = 255;
|
|
1672
|
-
var toolsCounterExtractor = {
|
|
1673
|
-
id: ID7,
|
|
1674
|
-
pluginId: CORE_PLUGIN_ID,
|
|
1675
|
-
kind: "extractor",
|
|
1676
|
-
description: "Counts the tools an agent declares in its frontmatter and shows the count on the agent card.",
|
|
1677
|
-
scope: "frontmatter",
|
|
1678
|
-
precondition: { kind: ["claude/agent"] },
|
|
1679
|
-
ui: { count: count2 },
|
|
1680
|
-
extract(ctx) {
|
|
1681
|
-
const raw = ctx.frontmatter["tools"];
|
|
1682
|
-
if (!Array.isArray(raw)) return;
|
|
1683
|
-
const names = [];
|
|
1684
|
-
for (const t of raw) {
|
|
1685
|
-
if (typeof t === "string" && t.length > 0) names.push(t);
|
|
1686
|
-
}
|
|
1687
|
-
if (names.length === 0) return;
|
|
1688
|
-
ctx.emitContribution(count2, {
|
|
1689
|
-
value: names.length,
|
|
1690
|
-
tooltip: buildTooltip(names)
|
|
1691
|
-
});
|
|
1692
|
-
}
|
|
1693
|
-
};
|
|
1694
|
-
function buildTooltip(names) {
|
|
1695
|
-
const joined = names.join(" \xB7 ");
|
|
1696
|
-
if (joined.length <= TOOLTIP_MAX) return joined;
|
|
1697
|
-
return `${joined.slice(0, TOOLTIP_MAX - 1)}\u2026`;
|
|
1698
|
-
}
|
|
1699
|
-
|
|
1700
1774
|
// plugins/core/analyzers/annotation-field-unknown/index.ts
|
|
1701
1775
|
import { readFileSync } from "fs";
|
|
1702
1776
|
import { dirname, resolve } from "path";
|
|
@@ -1712,12 +1786,14 @@ function applyAjvFormats(ajv) {
|
|
|
1712
1786
|
|
|
1713
1787
|
// plugins/core/analyzers/annotation-field-unknown/text.ts
|
|
1714
1788
|
var ANNOTATION_FIELD_UNKNOWN_TEXTS = {
|
|
1789
|
+
// Compact finding grammar: the affected node is the finding's own
|
|
1790
|
+
// node, so its path never appears in the message.
|
|
1715
1791
|
/** Key inside `annotations:` is not in the curated catalog. */
|
|
1716
|
-
unknownAnnotationKey: "
|
|
1792
|
+
unknownAnnotationKey: "Unknown sidecar key '{{key}}'; not in the annotations catalog.",
|
|
1717
1793
|
/** Top-level key is neither reserved, nor a registered plugin namespace, nor a registered root key. */
|
|
1718
|
-
unknownRootKey: "
|
|
1794
|
+
unknownRootKey: "Unknown sidecar top-level key '{{key}}'; not a reserved block, a plugin namespace, or a root contribution.",
|
|
1719
1795
|
/** Value under a registered plugin namespace fails the contributed schema. */
|
|
1720
|
-
pluginNamespaceInvalid: "
|
|
1796
|
+
pluginNamespaceInvalid: "Sidecar block '{{pluginId}}.{{key}}' fails the schema from plugin '{{pluginId}}': {{errors}}.",
|
|
1721
1797
|
// Tooltips for the per-node view-contribution badges. Singular vs
|
|
1722
1798
|
// plural keeps the count grammar correct without a sub-template.
|
|
1723
1799
|
alertTooltipSingle: "This node has 1 unknown field in its sidecar. Open the inspector for details.",
|
|
@@ -1725,10 +1801,10 @@ var ANNOTATION_FIELD_UNKNOWN_TEXTS = {
|
|
|
1725
1801
|
};
|
|
1726
1802
|
|
|
1727
1803
|
// plugins/core/analyzers/annotation-field-unknown/index.ts
|
|
1728
|
-
var
|
|
1804
|
+
var ID9 = "annotation-field-unknown";
|
|
1729
1805
|
var RESERVED_ROOT_BLOCKS = /* @__PURE__ */ new Set(["identity", "annotations", "settings", "audit"]);
|
|
1730
1806
|
var annotationFieldUnknownAnalyzer = {
|
|
1731
|
-
id:
|
|
1807
|
+
id: ID9,
|
|
1732
1808
|
pluginId: CORE_PLUGIN_ID,
|
|
1733
1809
|
kind: "analyzer",
|
|
1734
1810
|
description: "Flags typos or unrecognized keys in sidecars (`.sm`).",
|
|
@@ -1767,7 +1843,7 @@ var annotationFieldUnknownAnalyzer = {
|
|
|
1767
1843
|
for (const key of Object.keys(annotations)) {
|
|
1768
1844
|
if (!knownAnnotationKeys.has(key)) {
|
|
1769
1845
|
issues.push({
|
|
1770
|
-
analyzerId:
|
|
1846
|
+
analyzerId: ID9,
|
|
1771
1847
|
severity: "warn",
|
|
1772
1848
|
nodeIds: [node.path],
|
|
1773
1849
|
message: tx(ANNOTATION_FIELD_UNKNOWN_TEXTS.unknownAnnotationKey, {
|
|
@@ -1794,7 +1870,7 @@ var annotationFieldUnknownAnalyzer = {
|
|
|
1794
1870
|
if (validator(value)) continue;
|
|
1795
1871
|
const errors = (validator.errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
|
|
1796
1872
|
issues.push({
|
|
1797
|
-
analyzerId:
|
|
1873
|
+
analyzerId: ID9,
|
|
1798
1874
|
severity: "warn",
|
|
1799
1875
|
nodeIds: [node.path],
|
|
1800
1876
|
message: tx(ANNOTATION_FIELD_UNKNOWN_TEXTS.pluginNamespaceInvalid, {
|
|
@@ -1810,7 +1886,7 @@ var annotationFieldUnknownAnalyzer = {
|
|
|
1810
1886
|
continue;
|
|
1811
1887
|
}
|
|
1812
1888
|
issues.push({
|
|
1813
|
-
analyzerId:
|
|
1889
|
+
analyzerId: ID9,
|
|
1814
1890
|
severity: "warn",
|
|
1815
1891
|
nodeIds: [node.path],
|
|
1816
1892
|
message: tx(ANNOTATION_FIELD_UNKNOWN_TEXTS.unknownRootKey, {
|
|
@@ -1871,14 +1947,19 @@ function collectPluginIds(contributions) {
|
|
|
1871
1947
|
|
|
1872
1948
|
// plugins/core/analyzers/annotation-orphan/text.ts
|
|
1873
1949
|
var ANNOTATION_ORPHAN_TEXTS = {
|
|
1874
|
-
/**
|
|
1875
|
-
|
|
1950
|
+
/**
|
|
1951
|
+
* Compact finding grammar: line 1 = the orphan sidecar file, line 2
|
|
1952
|
+
* = the diagnosis. The expected markdown path IS the finding's
|
|
1953
|
+
* `nodeIds[0]` (the issue files under the path the sidecar points
|
|
1954
|
+
* at), so it never appears in the message.
|
|
1955
|
+
*/
|
|
1956
|
+
message: "{{sidecarPath}}:\nOrphan sidecar; no matching markdown node."
|
|
1876
1957
|
};
|
|
1877
1958
|
|
|
1878
1959
|
// plugins/core/analyzers/annotation-orphan/index.ts
|
|
1879
|
-
var
|
|
1960
|
+
var ID10 = "annotation-orphan";
|
|
1880
1961
|
var annotationOrphanAnalyzer = {
|
|
1881
|
-
id:
|
|
1962
|
+
id: ID10,
|
|
1882
1963
|
pluginId: CORE_PLUGIN_ID,
|
|
1883
1964
|
kind: "analyzer",
|
|
1884
1965
|
description: "Flags sidecars (`.sm`) whose `.md` file no longer exists.",
|
|
@@ -1890,7 +1971,7 @@ var annotationOrphanAnalyzer = {
|
|
|
1890
1971
|
for (const orphan of orphans) {
|
|
1891
1972
|
const expectedMdRelative = orphan.relativePath.endsWith(".sm") ? `${orphan.relativePath.slice(0, -".sm".length)}.md` : `${orphan.relativePath}.md`;
|
|
1892
1973
|
issues.push({
|
|
1893
|
-
analyzerId:
|
|
1974
|
+
analyzerId: ID10,
|
|
1894
1975
|
severity: "warn",
|
|
1895
1976
|
nodeIds: [expectedMdRelative],
|
|
1896
1977
|
message: tx(ANNOTATION_ORPHAN_TEXTS.message, {
|
|
@@ -1909,12 +1990,14 @@ var annotationOrphanAnalyzer = {
|
|
|
1909
1990
|
|
|
1910
1991
|
// plugins/core/analyzers/annotation-stale/text.ts
|
|
1911
1992
|
var ANNOTATION_STALE_TEXTS = {
|
|
1993
|
+
// Compact finding grammar: the affected node is the finding's own
|
|
1994
|
+
// node, so its path never appears in the message.
|
|
1912
1995
|
/** body changed since last bump */
|
|
1913
|
-
bodyDrift: "
|
|
1996
|
+
bodyDrift: "Sidecar `.sm` is stale: body changed since last bump.",
|
|
1914
1997
|
/** frontmatter changed since last bump */
|
|
1915
|
-
frontmatterDrift: "
|
|
1998
|
+
frontmatterDrift: "Sidecar `.sm` is stale: frontmatter changed since last bump.",
|
|
1916
1999
|
/** both body and frontmatter changed */
|
|
1917
|
-
bothDrift: "
|
|
2000
|
+
bothDrift: "Sidecar `.sm` is stale: body and frontmatter changed since last bump.",
|
|
1918
2001
|
// Tooltips for the `card.footer.right` clock chip emitted alongside
|
|
1919
2002
|
// the issue. Lists only the drifted face(s), in-sync faces are
|
|
1920
2003
|
// omitted so the operator immediately sees what's modified without
|
|
@@ -1923,15 +2006,11 @@ var ANNOTATION_STALE_TEXTS = {
|
|
|
1923
2006
|
// a literal placeholder the operator substitutes.
|
|
1924
2007
|
bodyTooltip: "Sidecar drift since last bump:\n \u2022 body\nRun `sm bump <path>` to refresh.",
|
|
1925
2008
|
frontmatterTooltip: "Sidecar drift since last bump:\n \u2022 frontmatter\nRun `sm bump <path>` to refresh.",
|
|
1926
|
-
bothTooltip: "Sidecar drift since last bump:\n \u2022 body\n \u2022 frontmatter\nRun `sm bump <path>` to refresh."
|
|
1927
|
-
/** Label of the inspector action button that dispatches a bump. */
|
|
1928
|
-
bumpLabel: "Bump",
|
|
1929
|
-
/** Tooltip shown when the bump button is disabled (the node is fresh, no drift). */
|
|
1930
|
-
bumpDisabledReason: "No drift to bump."
|
|
2009
|
+
bothTooltip: "Sidecar drift since last bump:\n \u2022 body\n \u2022 frontmatter\nRun `sm bump <path>` to refresh."
|
|
1931
2010
|
};
|
|
1932
2011
|
|
|
1933
2012
|
// plugins/core/analyzers/annotation-stale/index.ts
|
|
1934
|
-
var
|
|
2013
|
+
var ID11 = "annotation-stale";
|
|
1935
2014
|
var staleIcon = {
|
|
1936
2015
|
slot: "card.footer.right",
|
|
1937
2016
|
icon: "pi-clock",
|
|
@@ -1943,30 +2022,24 @@ var staleBadge = {
|
|
|
1943
2022
|
emitWhenEmpty: false,
|
|
1944
2023
|
priority: 20
|
|
1945
2024
|
};
|
|
1946
|
-
var bumpButton = {
|
|
1947
|
-
slot: "inspector.action.button",
|
|
1948
|
-
priority: 10
|
|
1949
|
-
};
|
|
1950
2025
|
var annotationStaleAnalyzer = {
|
|
1951
|
-
id:
|
|
2026
|
+
id: ID11,
|
|
1952
2027
|
pluginId: CORE_PLUGIN_ID,
|
|
1953
2028
|
kind: "analyzer",
|
|
1954
2029
|
description: "Marks sidecars (`.sm`) that are out of date with their `.md`.",
|
|
1955
2030
|
mode: "deterministic",
|
|
1956
2031
|
// The natural fix is to bump the node: refreshes the sidecar hashes,
|
|
1957
2032
|
// increments `annotations.version`, and stamps the audit block. The
|
|
1958
|
-
// inspector surfaces `core/node-bump`
|
|
1959
|
-
|
|
2033
|
+
// inspector surfaces that affordance via the `core/node-bump` action's
|
|
2034
|
+
// own scan-time `project()` self-projection, not from this analyzer.
|
|
2035
|
+
ui: { staleIcon, staleBadge },
|
|
1960
2036
|
evaluate(ctx) {
|
|
1961
2037
|
const issues = [];
|
|
1962
2038
|
for (const node of ctx.nodes) {
|
|
1963
2039
|
const status = staleStatus(node.sidecar);
|
|
1964
|
-
if (node.sidecar?.present === true) {
|
|
1965
|
-
emitBumpButton(ctx, node.path, status !== null);
|
|
1966
|
-
}
|
|
1967
2040
|
if (status === null) continue;
|
|
1968
2041
|
issues.push({
|
|
1969
|
-
analyzerId:
|
|
2042
|
+
analyzerId: ID11,
|
|
1970
2043
|
severity: "info",
|
|
1971
2044
|
nodeIds: [node.path],
|
|
1972
2045
|
message: messageFor(status, node.path),
|
|
@@ -1999,15 +2072,6 @@ function messageFor(status, path) {
|
|
|
1999
2072
|
return tx(ANNOTATION_STALE_TEXTS.bothDrift, { path });
|
|
2000
2073
|
}
|
|
2001
2074
|
}
|
|
2002
|
-
function emitBumpButton(ctx, nodePath, enabled) {
|
|
2003
|
-
ctx.emitContribution(nodePath, bumpButton, {
|
|
2004
|
-
actionId: "core/node-bump",
|
|
2005
|
-
label: ANNOTATION_STALE_TEXTS.bumpLabel,
|
|
2006
|
-
icon: "pi-arrow-up-right",
|
|
2007
|
-
enabled,
|
|
2008
|
-
...enabled ? {} : { disabledReason: ANNOTATION_STALE_TEXTS.bumpDisabledReason }
|
|
2009
|
-
});
|
|
2010
|
-
}
|
|
2011
2075
|
function tooltipFor(status) {
|
|
2012
2076
|
switch (status) {
|
|
2013
2077
|
case "stale-body":
|
|
@@ -2020,9 +2084,9 @@ function tooltipFor(status) {
|
|
|
2020
2084
|
}
|
|
2021
2085
|
|
|
2022
2086
|
// plugins/core/analyzers/contribution-orphan/index.ts
|
|
2023
|
-
var
|
|
2087
|
+
var ID12 = "contribution-orphan";
|
|
2024
2088
|
var contributionOrphanAnalyzer = {
|
|
2025
|
-
id:
|
|
2089
|
+
id: ID12,
|
|
2026
2090
|
pluginId: CORE_PLUGIN_ID,
|
|
2027
2091
|
kind: "analyzer",
|
|
2028
2092
|
description: "Warns about plugin data referencing nodes renamed or deleted in the latest scan.",
|
|
@@ -2041,7 +2105,7 @@ var ISSUE_COUNTER_TEXTS = {
|
|
|
2041
2105
|
};
|
|
2042
2106
|
|
|
2043
2107
|
// plugins/core/analyzers/issue-counter/index.ts
|
|
2044
|
-
var
|
|
2108
|
+
var ID13 = "issue-counter";
|
|
2045
2109
|
var warnCount = {
|
|
2046
2110
|
slot: "card.footer.right",
|
|
2047
2111
|
icon: "pi-exclamation-triangle",
|
|
@@ -2077,7 +2141,7 @@ function emitTierChips(ctx, ref, severity, counts, singleTooltip, manyTooltip) {
|
|
|
2077
2141
|
}
|
|
2078
2142
|
}
|
|
2079
2143
|
var issueCounterAnalyzer = {
|
|
2080
|
-
id:
|
|
2144
|
+
id: ID13,
|
|
2081
2145
|
pluginId: CORE_PLUGIN_ID,
|
|
2082
2146
|
kind: "analyzer",
|
|
2083
2147
|
description: "Emits one aggregate severity chip per node (error + warn counts) from the live issue accumulator.",
|
|
@@ -2112,15 +2176,16 @@ var issueCounterAnalyzer = {
|
|
|
2112
2176
|
var JOB_FILE_ORPHAN_TEXTS = {
|
|
2113
2177
|
/**
|
|
2114
2178
|
* `<path>.md` lives under `.skill-map/jobs/` but no `state_jobs.filePath`
|
|
2115
|
-
* row references it.
|
|
2179
|
+
* row references it. Compact finding grammar: the file IS the
|
|
2180
|
+
* finding's own node, so its path never appears in the message.
|
|
2116
2181
|
*/
|
|
2117
|
-
message: "Orphan job file
|
|
2182
|
+
message: "Orphan job file; not referenced by any job. Run `sm job prune --orphan-files` to remove it."
|
|
2118
2183
|
};
|
|
2119
2184
|
|
|
2120
2185
|
// plugins/core/analyzers/job-file-orphan/index.ts
|
|
2121
|
-
var
|
|
2186
|
+
var ID14 = "job-file-orphan";
|
|
2122
2187
|
var jobFileOrphanAnalyzer = {
|
|
2123
|
-
id:
|
|
2188
|
+
id: ID14,
|
|
2124
2189
|
pluginId: CORE_PLUGIN_ID,
|
|
2125
2190
|
kind: "analyzer",
|
|
2126
2191
|
description: "Flags leftover job result files (no live job references them). Clean up via `sm job prune --orphan-files`.",
|
|
@@ -2131,7 +2196,7 @@ var jobFileOrphanAnalyzer = {
|
|
|
2131
2196
|
const issues = [];
|
|
2132
2197
|
for (const filePath of orphans) {
|
|
2133
2198
|
issues.push({
|
|
2134
|
-
analyzerId:
|
|
2199
|
+
analyzerId: ID14,
|
|
2135
2200
|
severity: "warn",
|
|
2136
2201
|
nodeIds: [filePath],
|
|
2137
2202
|
message: tx(JOB_FILE_ORPHAN_TEXTS.message, { filePath }),
|
|
@@ -2144,14 +2209,19 @@ var jobFileOrphanAnalyzer = {
|
|
|
2144
2209
|
|
|
2145
2210
|
// plugins/core/analyzers/link-conflict/text.ts
|
|
2146
2211
|
var LINK_CONFLICT_TEXTS = {
|
|
2147
|
-
/**
|
|
2148
|
-
|
|
2212
|
+
/**
|
|
2213
|
+
* Compact finding grammar: line 1 = the disputed target, line 2 =
|
|
2214
|
+
* the short diagnosis. The source is the finding's own node, so it
|
|
2215
|
+
* never appears in the message.
|
|
2216
|
+
*/
|
|
2217
|
+
message: "{{target}}:\nDetectors disagree on link kind ({{kindList}})."
|
|
2149
2218
|
};
|
|
2150
2219
|
|
|
2151
2220
|
// plugins/core/analyzers/link-conflict/index.ts
|
|
2152
|
-
var
|
|
2221
|
+
var ID15 = "link-conflict";
|
|
2222
|
+
var NON_CONFLICTING_KINDS = /* @__PURE__ */ new Set(["points"]);
|
|
2153
2223
|
var linkConflictAnalyzer = {
|
|
2154
|
-
id:
|
|
2224
|
+
id: ID15,
|
|
2155
2225
|
pluginId: "core",
|
|
2156
2226
|
kind: "analyzer",
|
|
2157
2227
|
description: "Flags conflicting arrow meanings between extractors (e.g. `references` vs `invokes`).",
|
|
@@ -2163,6 +2233,7 @@ var linkConflictAnalyzer = {
|
|
|
2163
2233
|
evaluate(ctx) {
|
|
2164
2234
|
const groups = /* @__PURE__ */ new Map();
|
|
2165
2235
|
for (const link of ctx.links) {
|
|
2236
|
+
if (NON_CONFLICTING_KINDS.has(link.kind)) continue;
|
|
2166
2237
|
const key = `${link.source}\0${link.target}`;
|
|
2167
2238
|
const bucket = groups.get(key);
|
|
2168
2239
|
if (bucket) bucket.push(link);
|
|
@@ -2198,7 +2269,7 @@ var linkConflictAnalyzer = {
|
|
|
2198
2269
|
const [source, target] = key.split("\0");
|
|
2199
2270
|
const kindList = variants.map((v) => v.kind).join(" / ");
|
|
2200
2271
|
issues.push({
|
|
2201
|
-
analyzerId:
|
|
2272
|
+
analyzerId: ID15,
|
|
2202
2273
|
severity: "warn",
|
|
2203
2274
|
nodeIds: [source, target],
|
|
2204
2275
|
message: tx(LINK_CONFLICT_TEXTS.message, {
|
|
@@ -2265,7 +2336,7 @@ function resolveLinkTargetToPath(link, nameIndex) {
|
|
|
2265
2336
|
}
|
|
2266
2337
|
|
|
2267
2338
|
// plugins/core/analyzers/link-counter/index.ts
|
|
2268
|
-
var
|
|
2339
|
+
var ID16 = "link-counter";
|
|
2269
2340
|
var linksIn = {
|
|
2270
2341
|
slot: "card.footer.left",
|
|
2271
2342
|
icon: "pi-download",
|
|
@@ -2281,7 +2352,7 @@ var linksOut = {
|
|
|
2281
2352
|
priority: 20
|
|
2282
2353
|
};
|
|
2283
2354
|
var linkCounterAnalyzer = {
|
|
2284
|
-
id:
|
|
2355
|
+
id: ID16,
|
|
2285
2356
|
pluginId: CORE_PLUGIN_ID,
|
|
2286
2357
|
kind: "analyzer",
|
|
2287
2358
|
description: "Counts incoming and outgoing links per node.",
|
|
@@ -2328,6 +2399,27 @@ function formatBreakdown(byKind, direction) {
|
|
|
2328
2399
|
return [direction, ...lines].join("\n");
|
|
2329
2400
|
}
|
|
2330
2401
|
|
|
2402
|
+
// kernel/util/link-lines.ts
|
|
2403
|
+
function linkLines(link) {
|
|
2404
|
+
const lines = /* @__PURE__ */ new Set();
|
|
2405
|
+
for (const occ of link.occurrences ?? []) {
|
|
2406
|
+
const line = occ.location?.line;
|
|
2407
|
+
if (typeof line === "number") lines.add(line);
|
|
2408
|
+
}
|
|
2409
|
+
if (lines.size === 0) {
|
|
2410
|
+
const line = link.location?.line;
|
|
2411
|
+
if (typeof line === "number") lines.add(line);
|
|
2412
|
+
}
|
|
2413
|
+
return [...lines].sort((a, b) => a - b);
|
|
2414
|
+
}
|
|
2415
|
+
function linkWhere(link, texts) {
|
|
2416
|
+
const lines = linkLines(link);
|
|
2417
|
+
if (lines.length === 0) return "";
|
|
2418
|
+
return tx(lines.length === 1 ? texts.single : texts.plural, {
|
|
2419
|
+
lines: lines.join(", ")
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2331
2423
|
// plugins/core/analyzers/link-self-loop/text.ts
|
|
2332
2424
|
var LINK_SELF_LOOP_TEXTS = {
|
|
2333
2425
|
/**
|
|
@@ -2338,13 +2430,17 @@ var LINK_SELF_LOOP_TEXTS = {
|
|
|
2338
2430
|
* the operator's intent; UI consumers MAY hide it by default and
|
|
2339
2431
|
* surface a count.
|
|
2340
2432
|
*/
|
|
2341
|
-
message: "
|
|
2433
|
+
message: "`{{trigger}}`:\nSelf-reference ({{kind}}{{where}}); typically the file's own heading or label. Remove the token or ignore deliberately.",
|
|
2434
|
+
/** Location suffix inside the kind parens, one detection site. */
|
|
2435
|
+
whereSingle: ", line {{lines}}",
|
|
2436
|
+
/** Location suffix inside the kind parens, several detection sites. */
|
|
2437
|
+
wherePlural: ", lines {{lines}}"
|
|
2342
2438
|
};
|
|
2343
2439
|
|
|
2344
2440
|
// plugins/core/analyzers/link-self-loop/index.ts
|
|
2345
|
-
var
|
|
2441
|
+
var ID17 = "link-self-loop";
|
|
2346
2442
|
var linkSelfLoopAnalyzer = {
|
|
2347
|
-
id:
|
|
2443
|
+
id: ID17,
|
|
2348
2444
|
pluginId: CORE_PLUGIN_ID,
|
|
2349
2445
|
kind: "analyzer",
|
|
2350
2446
|
description: "Flags links whose source is also their own resolved target (e.g. a body heading like `# /deploy` inside the file that defines `/deploy`).",
|
|
@@ -2355,13 +2451,16 @@ var linkSelfLoopAnalyzer = {
|
|
|
2355
2451
|
for (const link of ctx.links) {
|
|
2356
2452
|
if (!isSelfLoop(link)) continue;
|
|
2357
2453
|
issues.push({
|
|
2358
|
-
analyzerId:
|
|
2454
|
+
analyzerId: ID17,
|
|
2359
2455
|
severity: "warn",
|
|
2360
2456
|
nodeIds: [link.source],
|
|
2361
2457
|
message: tx(LINK_SELF_LOOP_TEXTS.message, {
|
|
2362
|
-
source: link.source,
|
|
2363
2458
|
trigger: link.trigger?.originalTrigger ?? link.target,
|
|
2364
|
-
kind: link.kind
|
|
2459
|
+
kind: link.kind,
|
|
2460
|
+
where: linkWhere(link, {
|
|
2461
|
+
single: LINK_SELF_LOOP_TEXTS.whereSingle,
|
|
2462
|
+
plural: LINK_SELF_LOOP_TEXTS.wherePlural
|
|
2463
|
+
})
|
|
2365
2464
|
}),
|
|
2366
2465
|
data: {
|
|
2367
2466
|
target: link.target,
|
|
@@ -2384,7 +2483,7 @@ function isSelfLoop(link) {
|
|
|
2384
2483
|
}
|
|
2385
2484
|
|
|
2386
2485
|
// kernel/orchestrator/node-identifiers.ts
|
|
2387
|
-
import { posix as
|
|
2486
|
+
import { posix as pathPosix4 } from "path";
|
|
2388
2487
|
function deriveNodeIdentifiers(node, kindDescriptor) {
|
|
2389
2488
|
const sources = kindDescriptor?.identifiers;
|
|
2390
2489
|
if (!sources || sources.length === 0) return [];
|
|
@@ -2408,31 +2507,40 @@ function readFrontmatterName(node) {
|
|
|
2408
2507
|
return raw.length > 0 ? raw : null;
|
|
2409
2508
|
}
|
|
2410
2509
|
function readFilenameBasename(node) {
|
|
2411
|
-
const base =
|
|
2510
|
+
const base = pathPosix4.basename(node.path);
|
|
2412
2511
|
if (!base) return null;
|
|
2413
|
-
const ext =
|
|
2512
|
+
const ext = pathPosix4.extname(base);
|
|
2414
2513
|
const stem = ext ? base.slice(0, -ext.length) : base;
|
|
2415
2514
|
return stem.length > 0 ? stem : null;
|
|
2416
2515
|
}
|
|
2417
2516
|
function readDirname(node) {
|
|
2418
|
-
const dir =
|
|
2517
|
+
const dir = pathPosix4.dirname(node.path);
|
|
2419
2518
|
if (!dir || dir === "." || dir === "/") return null;
|
|
2420
|
-
const base =
|
|
2519
|
+
const base = pathPosix4.basename(dir);
|
|
2421
2520
|
return base.length > 0 ? base : null;
|
|
2422
2521
|
}
|
|
2423
2522
|
|
|
2424
2523
|
// kernel/orchestrator/lift-resolved-link-confidence.ts
|
|
2425
2524
|
var RESERVED_TARGET_CONFIDENCE = 0.1;
|
|
2525
|
+
var BROKEN_TARGET_CONFIDENCE = 0.5;
|
|
2426
2526
|
function liftResolvedLinkConfidence(links, nodes, ctx) {
|
|
2427
2527
|
if (!links.some((l) => l.confidence < 1)) return;
|
|
2428
2528
|
const indexes = buildIndexes(nodes, ctx);
|
|
2429
2529
|
for (const link of links) {
|
|
2430
|
-
if (link.confidence
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2530
|
+
if (link.confidence < 1) applyResolution(link, indexes, ctx);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
function applyResolution(link, indexes, ctx) {
|
|
2534
|
+
const resolution = resolve2(link, indexes, ctx);
|
|
2535
|
+
if (resolution === "none") {
|
|
2536
|
+
if (isGenuinelyBroken(link, indexes)) {
|
|
2537
|
+
link.confidence = Math.min(link.confidence, BROKEN_TARGET_CONFIDENCE);
|
|
2538
|
+
}
|
|
2539
|
+
return;
|
|
2435
2540
|
}
|
|
2541
|
+
link.resolvedTarget = resolution;
|
|
2542
|
+
if (indexes.nodeByPath.get(resolution)?.virtual) return;
|
|
2543
|
+
link.confidence = ctx.reservedNodePaths.has(resolution) ? RESERVED_TARGET_CONFIDENCE : 1;
|
|
2436
2544
|
}
|
|
2437
2545
|
function buildIndexes(nodes, ctx) {
|
|
2438
2546
|
const byPath3 = /* @__PURE__ */ new Set();
|
|
@@ -2449,6 +2557,12 @@ function resolve2(link, indexes, ctx) {
|
|
|
2449
2557
|
if (indexes.byPath.has(link.target)) return link.target;
|
|
2450
2558
|
return resolveByName(link, indexes, ctx);
|
|
2451
2559
|
}
|
|
2560
|
+
function isGenuinelyBroken(link, indexes) {
|
|
2561
|
+
if (indexes.byPath.has(link.target)) return false;
|
|
2562
|
+
const stripped = stripTriggerSigil(link.trigger?.normalizedTrigger);
|
|
2563
|
+
if (stripped !== null && indexes.byName.has(stripped)) return false;
|
|
2564
|
+
return true;
|
|
2565
|
+
}
|
|
2452
2566
|
function resolveByName(link, indexes, ctx) {
|
|
2453
2567
|
const stripped = stripTriggerSigil(link.trigger?.normalizedTrigger);
|
|
2454
2568
|
if (stripped === null) return "none";
|
|
@@ -2492,7 +2606,7 @@ var NAME_RESERVED_TEXTS = {
|
|
|
2492
2606
|
* a runtime built-in. Same wording skill-map shipped before the
|
|
2493
2607
|
* source-side link finding landed.
|
|
2494
2608
|
*/
|
|
2495
|
-
message: "
|
|
2609
|
+
message: "Built-in {{provider}} {{kind}}:\nShadowed by this file; the runtime uses its built-in instead. Rename the file or its `frontmatter.name`.",
|
|
2496
2610
|
/**
|
|
2497
2611
|
* Source-side message: emitted on the node that AUTHORED a link
|
|
2498
2612
|
* whose target resolves to a reserved name. Explains WHY the link's
|
|
@@ -2500,13 +2614,17 @@ var NAME_RESERVED_TEXTS = {
|
|
|
2500
2614
|
* the kernel saw the target match a runtime built-in and downgraded
|
|
2501
2615
|
* the edge so the operator notices.
|
|
2502
2616
|
*/
|
|
2503
|
-
linkMessage: "
|
|
2617
|
+
linkMessage: "{{target}}:\nResolves to a {{provider}} built-in ({{reservedKind}} `{{reservedPath}}`){{where}}; edge downgraded to confidence {{confidence}}. Rename the target file or its `frontmatter.name`.",
|
|
2618
|
+
/** Location suffix after the built-in parens, one detection site. */
|
|
2619
|
+
whereSingle: " (line {{lines}})",
|
|
2620
|
+
/** Location suffix after the built-in parens, several detection sites. */
|
|
2621
|
+
wherePlural: " (lines {{lines}})"
|
|
2504
2622
|
};
|
|
2505
2623
|
|
|
2506
2624
|
// plugins/core/analyzers/name-reserved/index.ts
|
|
2507
|
-
var
|
|
2625
|
+
var ID18 = "name-reserved";
|
|
2508
2626
|
var nameReservedAnalyzer = {
|
|
2509
|
-
id:
|
|
2627
|
+
id: ID18,
|
|
2510
2628
|
pluginId: CORE_PLUGIN_ID,
|
|
2511
2629
|
kind: "analyzer",
|
|
2512
2630
|
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.",
|
|
@@ -2522,7 +2640,7 @@ var nameReservedAnalyzer = {
|
|
|
2522
2640
|
const node = byPath3.get(path);
|
|
2523
2641
|
if (!node) continue;
|
|
2524
2642
|
issues.push({
|
|
2525
|
-
analyzerId:
|
|
2643
|
+
analyzerId: ID18,
|
|
2526
2644
|
severity: "warn",
|
|
2527
2645
|
nodeIds: [node.path],
|
|
2528
2646
|
message: tx(NAME_RESERVED_TEXTS.message, {
|
|
@@ -2538,16 +2656,16 @@ var nameReservedAnalyzer = {
|
|
|
2538
2656
|
const reservedNode = findReservedNodeForLink(link, reserved, byPath3);
|
|
2539
2657
|
if (!reservedNode) continue;
|
|
2540
2658
|
issues.push({
|
|
2541
|
-
analyzerId:
|
|
2659
|
+
analyzerId: ID18,
|
|
2542
2660
|
severity: "warn",
|
|
2543
2661
|
nodeIds: [link.source],
|
|
2544
2662
|
message: tx(NAME_RESERVED_TEXTS.linkMessage, {
|
|
2545
|
-
kind: link.kind,
|
|
2546
2663
|
target: link.target,
|
|
2547
2664
|
provider: reservedNode.provider,
|
|
2548
2665
|
reservedKind: reservedNode.kind,
|
|
2549
2666
|
reservedPath: reservedNode.path,
|
|
2550
|
-
confidence: RESERVED_TARGET_CONFIDENCE.toFixed(2)
|
|
2667
|
+
confidence: RESERVED_TARGET_CONFIDENCE.toFixed(2),
|
|
2668
|
+
where: linkWhereSuffix(link)
|
|
2551
2669
|
}),
|
|
2552
2670
|
data: {
|
|
2553
2671
|
target: link.target,
|
|
@@ -2562,6 +2680,12 @@ var nameReservedAnalyzer = {
|
|
|
2562
2680
|
return issues;
|
|
2563
2681
|
}
|
|
2564
2682
|
};
|
|
2683
|
+
function linkWhereSuffix(link) {
|
|
2684
|
+
return linkWhere(link, {
|
|
2685
|
+
single: NAME_RESERVED_TEXTS.whereSingle,
|
|
2686
|
+
plural: NAME_RESERVED_TEXTS.wherePlural
|
|
2687
|
+
});
|
|
2688
|
+
}
|
|
2565
2689
|
function findReservedNodeForLink(link, reserved, byPath3) {
|
|
2566
2690
|
if (reserved.has(link.target)) {
|
|
2567
2691
|
const node = byPath3.get(link.target);
|
|
@@ -2613,7 +2737,7 @@ var NODE_STABILITY_TEXTS = {
|
|
|
2613
2737
|
};
|
|
2614
2738
|
|
|
2615
2739
|
// plugins/core/analyzers/node-stability/index.ts
|
|
2616
|
-
var
|
|
2740
|
+
var ID19 = "node-stability";
|
|
2617
2741
|
var EXPERIMENTAL_TOOLTIP = "Experimental: API may change";
|
|
2618
2742
|
var DEPRECATED_TOOLTIP = "Deprecated: avoid in new code";
|
|
2619
2743
|
var experimental = {
|
|
@@ -2635,7 +2759,7 @@ var setStabilityButton = {
|
|
|
2635
2759
|
priority: 15
|
|
2636
2760
|
};
|
|
2637
2761
|
var nodeStabilityAnalyzer = {
|
|
2638
|
-
id:
|
|
2762
|
+
id: ID19,
|
|
2639
2763
|
pluginId: CORE_PLUGIN_ID,
|
|
2640
2764
|
kind: "analyzer",
|
|
2641
2765
|
description: "Reports a node's stability stage (`experimental`, `deprecated`) on the card.",
|
|
@@ -2654,7 +2778,7 @@ var nodeStabilityAnalyzer = {
|
|
|
2654
2778
|
tooltip: EXPERIMENTAL_TOOLTIP
|
|
2655
2779
|
});
|
|
2656
2780
|
issues.push({
|
|
2657
|
-
analyzerId:
|
|
2781
|
+
analyzerId: ID19,
|
|
2658
2782
|
severity: "info",
|
|
2659
2783
|
nodeIds: [node.path],
|
|
2660
2784
|
message: `Node '${node.path}' is marked experimental: API may change.`,
|
|
@@ -2667,7 +2791,7 @@ var nodeStabilityAnalyzer = {
|
|
|
2667
2791
|
severity: "warn"
|
|
2668
2792
|
});
|
|
2669
2793
|
issues.push({
|
|
2670
|
-
analyzerId:
|
|
2794
|
+
analyzerId: ID19,
|
|
2671
2795
|
severity: "warn",
|
|
2672
2796
|
nodeIds: [node.path],
|
|
2673
2797
|
message: `Node '${node.path}' is marked deprecated: avoid in new code.`,
|
|
@@ -2715,17 +2839,27 @@ function emitSetStabilityButton(ctx, nodePath, current) {
|
|
|
2715
2839
|
|
|
2716
2840
|
// plugins/core/analyzers/node-superseded/text.ts
|
|
2717
2841
|
var NODE_SUPERSEDED_TEXTS = {
|
|
2718
|
-
/**
|
|
2719
|
-
|
|
2842
|
+
/**
|
|
2843
|
+
* Compact finding grammar: line 1 = the superseding artifact, line
|
|
2844
|
+
* 2 = what it means. The superseded node is the finding's own node,
|
|
2845
|
+
* so its path never appears in the message.
|
|
2846
|
+
*/
|
|
2847
|
+
message: "{{supersededBy}}:\nSupersedes this node."
|
|
2720
2848
|
};
|
|
2721
2849
|
|
|
2722
2850
|
// plugins/core/analyzers/node-superseded/index.ts
|
|
2723
|
-
var
|
|
2851
|
+
var ID20 = "node-superseded";
|
|
2724
2852
|
var nodeSupersededAnalyzer = {
|
|
2725
|
-
id:
|
|
2853
|
+
id: ID20,
|
|
2726
2854
|
pluginId: CORE_PLUGIN_ID,
|
|
2727
2855
|
kind: "analyzer",
|
|
2728
2856
|
description: "Marks nodes replaced by a newer one via `supersededBy`.",
|
|
2857
|
+
// Part of the experimental supersession feature: ships disabled by
|
|
2858
|
+
// default alongside the declarer (`core/supersede` button +
|
|
2859
|
+
// `core/node-supersede` action). With the declarer off by default a
|
|
2860
|
+
// user rarely produces `supersededBy` data, so surfacing it stays
|
|
2861
|
+
// experimental too; the operator opts the whole family in together.
|
|
2862
|
+
stability: "experimental",
|
|
2729
2863
|
mode: "deterministic",
|
|
2730
2864
|
evaluate(ctx) {
|
|
2731
2865
|
const issues = [];
|
|
@@ -2733,7 +2867,7 @@ var nodeSupersededAnalyzer = {
|
|
|
2733
2867
|
const supersededBy = pickSupersededBy(node);
|
|
2734
2868
|
if (supersededBy === null) continue;
|
|
2735
2869
|
issues.push({
|
|
2736
|
-
analyzerId:
|
|
2870
|
+
analyzerId: ID20,
|
|
2737
2871
|
severity: "info",
|
|
2738
2872
|
nodeIds: [node.path],
|
|
2739
2873
|
message: tx(NODE_SUPERSEDED_TEXTS.message, {
|
|
@@ -2757,12 +2891,34 @@ function pickSupersededBy(node) {
|
|
|
2757
2891
|
}
|
|
2758
2892
|
|
|
2759
2893
|
// plugins/core/analyzers/reference-broken/index.ts
|
|
2760
|
-
import { posix as
|
|
2894
|
+
import { posix as pathPosix5, resolve as resolve3 } from "path";
|
|
2761
2895
|
|
|
2762
2896
|
// plugins/core/analyzers/reference-broken/text.ts
|
|
2763
2897
|
var REFERENCE_BROKEN_TEXTS = {
|
|
2764
|
-
/**
|
|
2765
|
-
|
|
2898
|
+
/**
|
|
2899
|
+
* Compact finding grammar: line 1 = the unresolved target, line 2 =
|
|
2900
|
+
* the short diagnosis plus WHERE the reference sits (`{{where}}` is
|
|
2901
|
+
* the pre-rendered location suffix below, or empty when the link
|
|
2902
|
+
* carries no line info). The source is the finding's own node, so it
|
|
2903
|
+
* never appears in the message.
|
|
2904
|
+
*/
|
|
2905
|
+
message: "{{target}}:\nBroken {{kindLabel}}{{where}}.",
|
|
2906
|
+
/** Location suffix, one detection site. */
|
|
2907
|
+
whereSingle: " (line {{lines}})",
|
|
2908
|
+
/** Location suffix, several detection sites. */
|
|
2909
|
+
wherePlural: " (lines {{lines}})",
|
|
2910
|
+
/**
|
|
2911
|
+
* Human noun per link kind for the message above. Fallback for an
|
|
2912
|
+
* off-catalog kind: `<kind> link` (composed in the analyzer).
|
|
2913
|
+
*/
|
|
2914
|
+
kindLabels: {
|
|
2915
|
+
references: "reference",
|
|
2916
|
+
mentions: "mention",
|
|
2917
|
+
invokes: "invocation",
|
|
2918
|
+
supersedes: "supersession",
|
|
2919
|
+
points: "pointer"
|
|
2920
|
+
},
|
|
2921
|
+
kindLabelFallback: "{{kind}} link",
|
|
2766
2922
|
// Tooltips for the per-node view-contribution badges. Singular vs
|
|
2767
2923
|
// plural keeps the count grammar correct without a sub-template.
|
|
2768
2924
|
alertTooltipSingle: "This node has a broken reference. Open the inspector for details.",
|
|
@@ -2776,9 +2932,9 @@ var REFERENCE_BROKEN_TEXTS = {
|
|
|
2776
2932
|
};
|
|
2777
2933
|
|
|
2778
2934
|
// plugins/core/analyzers/reference-broken/index.ts
|
|
2779
|
-
var
|
|
2935
|
+
var ID21 = "reference-broken";
|
|
2780
2936
|
var referenceBrokenAnalyzer = {
|
|
2781
|
-
id:
|
|
2937
|
+
id: ID21,
|
|
2782
2938
|
pluginId: CORE_PLUGIN_ID,
|
|
2783
2939
|
kind: "analyzer",
|
|
2784
2940
|
description: "Flags arrows pointing at a node not part of the current scan.",
|
|
@@ -2815,7 +2971,7 @@ function buildIssue(link, hintCandidates = []) {
|
|
|
2815
2971
|
trigger: link.trigger?.normalizedTrigger ?? null
|
|
2816
2972
|
};
|
|
2817
2973
|
const issue = {
|
|
2818
|
-
analyzerId:
|
|
2974
|
+
analyzerId: ID21,
|
|
2819
2975
|
// `error`, not `warn`: a link whose target is not in the scan is a
|
|
2820
2976
|
// structural defect the operator must notice, and the card chip
|
|
2821
2977
|
// paints `danger` (red) to match. Per the chip-vs-issue policy in
|
|
@@ -2825,33 +2981,37 @@ function buildIssue(link, hintCandidates = []) {
|
|
|
2825
2981
|
severity: "error",
|
|
2826
2982
|
nodeIds: [link.source],
|
|
2827
2983
|
message: tx(REFERENCE_BROKEN_TEXTS.message, {
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2984
|
+
target: link.target,
|
|
2985
|
+
kindLabel: REFERENCE_BROKEN_TEXTS.kindLabels[link.kind] ?? tx(REFERENCE_BROKEN_TEXTS.kindLabelFallback, { kind: link.kind }),
|
|
2986
|
+
where: linkWhere(link, {
|
|
2987
|
+
single: REFERENCE_BROKEN_TEXTS.whereSingle,
|
|
2988
|
+
plural: REFERENCE_BROKEN_TEXTS.wherePlural
|
|
2989
|
+
})
|
|
2831
2990
|
}),
|
|
2832
2991
|
data
|
|
2833
2992
|
};
|
|
2834
|
-
if (hintCandidates.length > 0)
|
|
2835
|
-
const suggestedName = (link.trigger?.normalizedTrigger ?? "").replace(/^[/@]/, "").trim();
|
|
2836
|
-
const candidatePaths = hintCandidates.map((n) => n.path);
|
|
2837
|
-
data["hint"] = {
|
|
2838
|
-
kind: "missing-frontmatter-name",
|
|
2839
|
-
suggestedName,
|
|
2840
|
-
candidates: candidatePaths
|
|
2841
|
-
};
|
|
2842
|
-
issue.fix = {
|
|
2843
|
-
summary: candidatePaths.length === 1 ? tx(REFERENCE_BROKEN_TEXTS.hintSummarySingle, {
|
|
2844
|
-
name: suggestedName,
|
|
2845
|
-
candidate: candidatePaths[0]
|
|
2846
|
-
}) : tx(REFERENCE_BROKEN_TEXTS.hintSummaryMany, {
|
|
2847
|
-
name: suggestedName,
|
|
2848
|
-
candidates: candidatePaths.join(", ")
|
|
2849
|
-
}),
|
|
2850
|
-
autofixable: false
|
|
2851
|
-
};
|
|
2852
|
-
}
|
|
2993
|
+
if (hintCandidates.length > 0) attachHint(issue, data, link, hintCandidates);
|
|
2853
2994
|
return issue;
|
|
2854
2995
|
}
|
|
2996
|
+
function attachHint(issue, data, link, hintCandidates) {
|
|
2997
|
+
const suggestedName = (link.trigger?.normalizedTrigger ?? "").replace(/^[/@]/, "").trim();
|
|
2998
|
+
const candidatePaths = hintCandidates.map((n) => n.path);
|
|
2999
|
+
data["hint"] = {
|
|
3000
|
+
kind: "missing-frontmatter-name",
|
|
3001
|
+
suggestedName,
|
|
3002
|
+
candidates: candidatePaths
|
|
3003
|
+
};
|
|
3004
|
+
issue.fix = {
|
|
3005
|
+
summary: candidatePaths.length === 1 ? tx(REFERENCE_BROKEN_TEXTS.hintSummarySingle, {
|
|
3006
|
+
name: suggestedName,
|
|
3007
|
+
candidate: candidatePaths[0]
|
|
3008
|
+
}) : tx(REFERENCE_BROKEN_TEXTS.hintSummaryMany, {
|
|
3009
|
+
name: suggestedName,
|
|
3010
|
+
candidates: candidatePaths.join(", ")
|
|
3011
|
+
}),
|
|
3012
|
+
autofixable: false
|
|
3013
|
+
};
|
|
3014
|
+
}
|
|
2855
3015
|
function resolvesViaReferencePaths(link, refIndex) {
|
|
2856
3016
|
if (!isPathStyleLink(link)) return false;
|
|
2857
3017
|
return refIndex.paths.has(resolve3(refIndex.cwd, link.target));
|
|
@@ -2870,8 +3030,8 @@ function indexByNormalizedName(nodes) {
|
|
|
2870
3030
|
return out;
|
|
2871
3031
|
}
|
|
2872
3032
|
function basenameWithoutExt(path) {
|
|
2873
|
-
const base =
|
|
2874
|
-
const ext =
|
|
3033
|
+
const base = pathPosix5.basename(path);
|
|
3034
|
+
const ext = pathPosix5.extname(base);
|
|
2875
3035
|
return ext ? base.slice(0, -ext.length) : base;
|
|
2876
3036
|
}
|
|
2877
3037
|
function indexByBasenameWithoutName(nodes) {
|
|
@@ -2917,25 +3077,29 @@ function isPathStyleLink(link) {
|
|
|
2917
3077
|
// plugins/core/analyzers/reference-redundant/text.ts
|
|
2918
3078
|
var REFERENCE_REDUNDANT_TEXTS = {
|
|
2919
3079
|
/**
|
|
2920
|
-
*
|
|
2921
|
-
*
|
|
2922
|
-
*
|
|
2923
|
-
*
|
|
2924
|
-
*
|
|
3080
|
+
* Compact finding grammar (subject first, `\n` renders as a line
|
|
3081
|
+
* break in the inspector and flattens to a space in `sm check`):
|
|
3082
|
+
*
|
|
3083
|
+
* <resolvedTarget>:
|
|
3084
|
+
* Duplicate reference (2): `references/x.md` (124, 145).
|
|
3085
|
+
*
|
|
3086
|
+
* Occurrences are grouped BY TRIGGER: each distinct trigger text
|
|
3087
|
+
* appears once with its line numbers collapsed into one paren list.
|
|
3088
|
+
* The source node is the finding's own node, so it never appears.
|
|
2925
3089
|
*/
|
|
2926
|
-
message: "
|
|
2927
|
-
/** Inline separator between
|
|
3090
|
+
message: "{{resolvedTarget}}:\nDuplicate reference ({{count}}): {{occurrences}}.",
|
|
3091
|
+
/** Inline separator between trigger groups in the message. */
|
|
2928
3092
|
occurrenceSeparator: ", ",
|
|
2929
|
-
/** Per-
|
|
2930
|
-
occurrence: "`{{trigger}}` ({{
|
|
2931
|
-
/**
|
|
2932
|
-
|
|
3093
|
+
/** Per-trigger formatting: the trigger once, its lines grouped. */
|
|
3094
|
+
occurrence: "`{{trigger}}` ({{lines}})",
|
|
3095
|
+
/** Placeholder for an occurrence whose extractor recorded no line. */
|
|
3096
|
+
lineUnknown: "?"
|
|
2933
3097
|
};
|
|
2934
3098
|
|
|
2935
3099
|
// plugins/core/analyzers/reference-redundant/index.ts
|
|
2936
|
-
var
|
|
3100
|
+
var ID22 = "reference-redundant";
|
|
2937
3101
|
var referenceRedundantAnalyzer = {
|
|
2938
|
-
id:
|
|
3102
|
+
id: ID22,
|
|
2939
3103
|
pluginId: CORE_PLUGIN_ID,
|
|
2940
3104
|
kind: "analyzer",
|
|
2941
3105
|
description: "Flags when one node references the same target through two or more different links (e.g. a markdown link plus a `references:` entry).",
|
|
@@ -2961,14 +3125,13 @@ var referenceRedundantAnalyzer = {
|
|
|
2961
3125
|
const [source, resolvedTarget] = key.split("\0");
|
|
2962
3126
|
const flat = flattenOccurrences(links);
|
|
2963
3127
|
issues.push({
|
|
2964
|
-
analyzerId:
|
|
2965
|
-
severity: "
|
|
3128
|
+
analyzerId: ID22,
|
|
3129
|
+
severity: "info",
|
|
2966
3130
|
nodeIds: [source],
|
|
2967
3131
|
message: tx(REFERENCE_REDUNDANT_TEXTS.message, {
|
|
2968
|
-
source,
|
|
2969
3132
|
resolvedTarget,
|
|
2970
3133
|
count: flat.length,
|
|
2971
|
-
occurrences: flat
|
|
3134
|
+
occurrences: formatGroupedOccurrences(flat)
|
|
2972
3135
|
}),
|
|
2973
3136
|
data: {
|
|
2974
3137
|
target: resolvedTarget,
|
|
@@ -3015,11 +3178,19 @@ function flattenOccurrences(links) {
|
|
|
3015
3178
|
});
|
|
3016
3179
|
return out;
|
|
3017
3180
|
}
|
|
3018
|
-
function
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3181
|
+
function formatGroupedOccurrences(occurrences) {
|
|
3182
|
+
const byTrigger = /* @__PURE__ */ new Map();
|
|
3183
|
+
for (const occ of occurrences) {
|
|
3184
|
+
const bucket = byTrigger.get(occ.originalTrigger);
|
|
3185
|
+
if (bucket) bucket.push(occ);
|
|
3186
|
+
else byTrigger.set(occ.originalTrigger, [occ]);
|
|
3187
|
+
}
|
|
3188
|
+
return [...byTrigger.entries()].map(
|
|
3189
|
+
([trigger, occs]) => tx(REFERENCE_REDUNDANT_TEXTS.occurrence, {
|
|
3190
|
+
trigger,
|
|
3191
|
+
lines: occs.map((o) => o.line === null ? REFERENCE_REDUNDANT_TEXTS.lineUnknown : String(o.line)).join(", ")
|
|
3192
|
+
})
|
|
3193
|
+
).join(REFERENCE_REDUNDANT_TEXTS.occurrenceSeparator);
|
|
3023
3194
|
}
|
|
3024
3195
|
function buildNameIndex2(nodes) {
|
|
3025
3196
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -3311,12 +3482,14 @@ function existsSyncSafe(path) {
|
|
|
3311
3482
|
|
|
3312
3483
|
// plugins/core/analyzers/schema-violation/text.ts
|
|
3313
3484
|
var SCHEMA_VIOLATION_TEXTS = {
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
/** `
|
|
3317
|
-
|
|
3318
|
-
/**
|
|
3319
|
-
|
|
3485
|
+
// Compact finding grammar: the affected node (or the link's source)
|
|
3486
|
+
// is the finding's own node, so its path never appears.
|
|
3487
|
+
/** `Schema validation failed: <errors>` */
|
|
3488
|
+
nodeFailure: "Schema validation failed: {{errors}}",
|
|
3489
|
+
/** `<target>:\nLink failed schema validation: <errors>` */
|
|
3490
|
+
linkFailure: "{{target}}:\nLink failed schema validation: {{errors}}",
|
|
3491
|
+
/** `Missing required frontmatter: <missing>.` */
|
|
3492
|
+
frontmatterBaseFailure: "Missing required frontmatter: {{missing}}.",
|
|
3320
3493
|
/** Singular tooltip on the alert / chip when a node has exactly one validation failure. */
|
|
3321
3494
|
alertTooltipSingle: "Frontmatter or schema validation failed.",
|
|
3322
3495
|
/** Plural tooltip; `{{count}}` capped at 99 in the chip badge but the tooltip text shows the raw count. */
|
|
@@ -3324,9 +3497,9 @@ var SCHEMA_VIOLATION_TEXTS = {
|
|
|
3324
3497
|
};
|
|
3325
3498
|
|
|
3326
3499
|
// plugins/core/analyzers/schema-violation/index.ts
|
|
3327
|
-
var
|
|
3500
|
+
var ID23 = "schema-violation";
|
|
3328
3501
|
var schemaViolationAnalyzer = {
|
|
3329
|
-
id:
|
|
3502
|
+
id: ID23,
|
|
3330
3503
|
pluginId: CORE_PLUGIN_ID,
|
|
3331
3504
|
kind: "analyzer",
|
|
3332
3505
|
description: "Flags nodes or links that violate the project schemas.",
|
|
@@ -3377,7 +3550,7 @@ function collectNodeFindings(v, node, out) {
|
|
|
3377
3550
|
const result = v.validate("node", toNodeForSchema(node));
|
|
3378
3551
|
if (result.ok) return;
|
|
3379
3552
|
out.push({
|
|
3380
|
-
analyzerId:
|
|
3553
|
+
analyzerId: ID23,
|
|
3381
3554
|
severity: "error",
|
|
3382
3555
|
nodeIds: [node.path],
|
|
3383
3556
|
message: tx(SCHEMA_VIOLATION_TEXTS.nodeFailure, {
|
|
@@ -3396,7 +3569,7 @@ function collectFrontmatterBaseFindings(node, out) {
|
|
|
3396
3569
|
if (isMissingStringField(fm, "description")) missing.push("description");
|
|
3397
3570
|
if (missing.length === 0) return;
|
|
3398
3571
|
out.push({
|
|
3399
|
-
analyzerId:
|
|
3572
|
+
analyzerId: ID23,
|
|
3400
3573
|
// `warn` (not `error`) so the default `sm scan` exit code stays
|
|
3401
3574
|
// 0 even when nodes are missing frontmatter base fields. Strict
|
|
3402
3575
|
// mode (`sm scan --strict`) still escalates to exit 1. Matches
|
|
@@ -3418,7 +3591,7 @@ function collectLinkFindings(v, link, out) {
|
|
|
3418
3591
|
const result = v.validate("link", toLinkForSchema(link));
|
|
3419
3592
|
if (result.ok) return;
|
|
3420
3593
|
out.push({
|
|
3421
|
-
analyzerId:
|
|
3594
|
+
analyzerId: ID23,
|
|
3422
3595
|
severity: "error",
|
|
3423
3596
|
nodeIds: [link.source],
|
|
3424
3597
|
message: tx(SCHEMA_VIOLATION_TEXTS.linkFailure, {
|
|
@@ -3473,7 +3646,7 @@ var SIGNAL_COLLISION_TEXTS = {
|
|
|
3473
3646
|
* the same paragraph, the markdown-link wins and the at-directive
|
|
3474
3647
|
* silently disappears without this warning).
|
|
3475
3648
|
*/
|
|
3476
|
-
message: "{{
|
|
3649
|
+
message: "`{{loserRaw}}`:\nOverlap collision: {{loserExtractor}} (at {{loserRange}}) lost to {{winnerExtractor}} (at {{winnerRange}}) by {{reason}}; only the winning edge persists.",
|
|
3477
3650
|
/**
|
|
3478
3651
|
* Same warn but for the rare case the resolver rejected a Signal
|
|
3479
3652
|
* because the operator disabled its extractor via
|
|
@@ -3482,20 +3655,20 @@ var SIGNAL_COLLISION_TEXTS = {
|
|
|
3482
3655
|
* resolver; documented now so the analyzer stays forward-compatible
|
|
3483
3656
|
* with the upcoming filter pass.
|
|
3484
3657
|
*/
|
|
3485
|
-
messageExtractorDisabled: "
|
|
3658
|
+
messageExtractorDisabled: "`{{loserRaw}}`:\nDropped: extension `{{extractorId}}` is disabled. Re-enable it in Settings or via `sm plugins enable`.",
|
|
3486
3659
|
/**
|
|
3487
3660
|
* Same warn but for the future confidence floor case. Phase 4+ stub:
|
|
3488
3661
|
* today the resolver materialises every winning candidate regardless
|
|
3489
3662
|
* of confidence, so this template is unreachable; documented for
|
|
3490
3663
|
* forward compatibility.
|
|
3491
3664
|
*/
|
|
3492
|
-
messageBelowFloor: "
|
|
3665
|
+
messageBelowFloor: "`{{loserRaw}}`:\nDropped: confidence {{confidence}} is below the threshold {{threshold}}."
|
|
3493
3666
|
};
|
|
3494
3667
|
|
|
3495
3668
|
// plugins/core/analyzers/signal-collision/index.ts
|
|
3496
|
-
var
|
|
3669
|
+
var ID24 = "signal-collision";
|
|
3497
3670
|
var signalCollisionAnalyzer = {
|
|
3498
|
-
id:
|
|
3671
|
+
id: ID24,
|
|
3499
3672
|
pluginId: CORE_PLUGIN_ID,
|
|
3500
3673
|
kind: "analyzer",
|
|
3501
3674
|
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.",
|
|
@@ -3520,7 +3693,7 @@ function makeIssue(signal) {
|
|
|
3520
3693
|
const loserRange = signal.range ? `${signal.range.start}-${signal.range.end}` : "unknown";
|
|
3521
3694
|
const winnerRange = `${winner.range.start}-${winner.range.end}`;
|
|
3522
3695
|
return {
|
|
3523
|
-
analyzerId:
|
|
3696
|
+
analyzerId: ID24,
|
|
3524
3697
|
severity: "warn",
|
|
3525
3698
|
nodeIds: [signal.source],
|
|
3526
3699
|
message: tx(SIGNAL_COLLISION_TEXTS.message, {
|
|
@@ -3553,158 +3726,42 @@ function makeIssue(signal) {
|
|
|
3553
3726
|
if (resolution.extractorDisabled) {
|
|
3554
3727
|
const loserRange = signal.range ? `${signal.range.start}-${signal.range.end}` : "unknown";
|
|
3555
3728
|
return {
|
|
3556
|
-
analyzerId:
|
|
3729
|
+
analyzerId: ID24,
|
|
3557
3730
|
severity: "warn",
|
|
3558
3731
|
nodeIds: [signal.source],
|
|
3559
3732
|
message: tx(SIGNAL_COLLISION_TEXTS.messageExtractorDisabled, {
|
|
3560
3733
|
extractorId: resolution.extractorDisabled.extractorId,
|
|
3561
3734
|
loserRaw: signal.raw,
|
|
3562
|
-
loserRange
|
|
3563
|
-
}),
|
|
3564
|
-
data: {
|
|
3565
|
-
extractorDisabled: resolution.extractorDisabled,
|
|
3566
|
-
raw: signal.raw,
|
|
3567
|
-
range: signal.range ?? null
|
|
3568
|
-
}
|
|
3569
|
-
};
|
|
3570
|
-
}
|
|
3571
|
-
if (resolution.belowFloor) {
|
|
3572
|
-
const loserRange = signal.range ? `${signal.range.start}-${signal.range.end}` : "unknown";
|
|
3573
|
-
const topCandidate = signal.candidates[0];
|
|
3574
|
-
return {
|
|
3575
|
-
analyzerId: ID23,
|
|
3576
|
-
severity: "warn",
|
|
3577
|
-
nodeIds: [signal.source],
|
|
3578
|
-
message: tx(SIGNAL_COLLISION_TEXTS.messageBelowFloor, {
|
|
3579
|
-
loserRaw: signal.raw,
|
|
3580
|
-
loserRange,
|
|
3581
|
-
confidence: topCandidate.confidence,
|
|
3582
|
-
threshold: resolution.belowFloor.threshold
|
|
3583
|
-
}),
|
|
3584
|
-
data: {
|
|
3585
|
-
belowFloor: resolution.belowFloor,
|
|
3586
|
-
raw: signal.raw,
|
|
3587
|
-
range: signal.range ?? null
|
|
3588
|
-
}
|
|
3589
|
-
};
|
|
3590
|
-
}
|
|
3591
|
-
return null;
|
|
3592
|
-
}
|
|
3593
|
-
|
|
3594
|
-
// plugins/core/analyzers/supersede/text.ts
|
|
3595
|
-
var SUPERSEDE_TEXTS = {
|
|
3596
|
-
/** Label of the inspector action button that declares supersession. */
|
|
3597
|
-
supersedeLabel: "Supersede",
|
|
3598
|
-
/** Tooltip shown when the supersede button is disabled (already superseded). */
|
|
3599
|
-
supersedeDisabledReason: "Already superseded.",
|
|
3600
|
-
/** Tooltip shown when there is no other node to supersede this one. */
|
|
3601
|
-
supersedeNoTargetsReason: "No other node to supersede this one.",
|
|
3602
|
-
/** Prompt label for the target node-picker (enum-pick over the live node set). */
|
|
3603
|
-
supersedePromptLabel: "Superseded by"
|
|
3604
|
-
};
|
|
3605
|
-
|
|
3606
|
-
// plugins/core/analyzers/supersede/index.ts
|
|
3607
|
-
var ID24 = "supersede";
|
|
3608
|
-
var supersedeButton = {
|
|
3609
|
-
slot: "inspector.action.button",
|
|
3610
|
-
priority: 10
|
|
3611
|
-
};
|
|
3612
|
-
var supersedeAnalyzer = {
|
|
3613
|
-
id: ID24,
|
|
3614
|
-
pluginId: CORE_PLUGIN_ID,
|
|
3615
|
-
kind: "analyzer",
|
|
3616
|
-
description: 'Projects the inspector "Supersede" button (declares a node replaced by another).',
|
|
3617
|
-
mode: "deterministic",
|
|
3618
|
-
ui: { supersedeButton },
|
|
3619
|
-
evaluate(ctx) {
|
|
3620
|
-
const candidates = ctx.nodes.filter((n) => n.virtual !== true).map((n) => n.path);
|
|
3621
|
-
for (const node of ctx.nodes) {
|
|
3622
|
-
if (node.virtual === true) continue;
|
|
3623
|
-
const options = candidates.filter((p) => p !== node.path).map((p) => ({ value: p, label: p }));
|
|
3624
|
-
emitSupersedeButton(ctx, node, options);
|
|
3625
|
-
}
|
|
3626
|
-
return [];
|
|
3627
|
-
}
|
|
3628
|
-
};
|
|
3629
|
-
function emitSupersedeButton(ctx, node, options) {
|
|
3630
|
-
const disabledReason = resolveDisabledReason(node, options.length);
|
|
3631
|
-
ctx.emitContribution(node.path, supersedeButton, {
|
|
3632
|
-
actionId: "core/node-supersede",
|
|
3633
|
-
label: SUPERSEDE_TEXTS.supersedeLabel,
|
|
3634
|
-
icon: "pi-arrow-right-arrow-left",
|
|
3635
|
-
enabled: disabledReason === void 0,
|
|
3636
|
-
...disabledReason === void 0 ? {} : { disabledReason },
|
|
3637
|
-
prompt: {
|
|
3638
|
-
inputType: "enum-pick",
|
|
3639
|
-
paramKey: "supersededBy",
|
|
3640
|
-
label: SUPERSEDE_TEXTS.supersedePromptLabel,
|
|
3641
|
-
options
|
|
3642
|
-
}
|
|
3643
|
-
});
|
|
3644
|
-
}
|
|
3645
|
-
function resolveDisabledReason(node, optionCount) {
|
|
3646
|
-
if (alreadySuperseded(node)) return SUPERSEDE_TEXTS.supersedeDisabledReason;
|
|
3647
|
-
if (optionCount === 0) return SUPERSEDE_TEXTS.supersedeNoTargetsReason;
|
|
3648
|
-
return void 0;
|
|
3649
|
-
}
|
|
3650
|
-
function alreadySuperseded(node) {
|
|
3651
|
-
const sidecar = node.sidecar;
|
|
3652
|
-
if (!sidecar || sidecar.present !== true) return false;
|
|
3653
|
-
const ann = sidecar.annotations;
|
|
3654
|
-
if (!ann || typeof ann !== "object" || Array.isArray(ann)) return false;
|
|
3655
|
-
const value = ann["supersededBy"];
|
|
3656
|
-
return typeof value === "string" && value.length > 0;
|
|
3657
|
-
}
|
|
3658
|
-
|
|
3659
|
-
// plugins/core/analyzers/tags/text.ts
|
|
3660
|
-
var TAGS_TEXTS = {
|
|
3661
|
-
/** Label of the inspector action button that edits the node's tags. */
|
|
3662
|
-
editLabel: "Edit tags",
|
|
3663
|
-
/** Prompt label for the string-list tags input. */
|
|
3664
|
-
promptLabel: "Tags"
|
|
3665
|
-
};
|
|
3666
|
-
|
|
3667
|
-
// plugins/core/analyzers/tags/index.ts
|
|
3668
|
-
var ID25 = "tags";
|
|
3669
|
-
var setTagsButton = {
|
|
3670
|
-
slot: "inspector.action.button",
|
|
3671
|
-
priority: 15
|
|
3672
|
-
};
|
|
3673
|
-
var tagsAnalyzer = {
|
|
3674
|
-
id: ID25,
|
|
3675
|
-
pluginId: CORE_PLUGIN_ID,
|
|
3676
|
-
kind: "analyzer",
|
|
3677
|
-
description: `Projects the inspector "Edit tags" button (edits a node's taxonomy tags).`,
|
|
3678
|
-
mode: "deterministic",
|
|
3679
|
-
ui: { setTagsButton },
|
|
3680
|
-
evaluate(ctx) {
|
|
3681
|
-
for (const node of ctx.nodes) {
|
|
3682
|
-
if (node.sidecar?.present !== true) continue;
|
|
3683
|
-
emitSetTagsButton(ctx, node);
|
|
3684
|
-
}
|
|
3685
|
-
return [];
|
|
3735
|
+
loserRange
|
|
3736
|
+
}),
|
|
3737
|
+
data: {
|
|
3738
|
+
extractorDisabled: resolution.extractorDisabled,
|
|
3739
|
+
raw: signal.raw,
|
|
3740
|
+
range: signal.range ?? null
|
|
3741
|
+
}
|
|
3742
|
+
};
|
|
3686
3743
|
}
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
return
|
|
3744
|
+
if (resolution.belowFloor) {
|
|
3745
|
+
const loserRange = signal.range ? `${signal.range.start}-${signal.range.end}` : "unknown";
|
|
3746
|
+
const topCandidate = signal.candidates[0];
|
|
3747
|
+
return {
|
|
3748
|
+
analyzerId: ID24,
|
|
3749
|
+
severity: "warn",
|
|
3750
|
+
nodeIds: [signal.source],
|
|
3751
|
+
message: tx(SIGNAL_COLLISION_TEXTS.messageBelowFloor, {
|
|
3752
|
+
loserRaw: signal.raw,
|
|
3753
|
+
loserRange,
|
|
3754
|
+
confidence: topCandidate.confidence,
|
|
3755
|
+
threshold: resolution.belowFloor.threshold
|
|
3756
|
+
}),
|
|
3757
|
+
data: {
|
|
3758
|
+
belowFloor: resolution.belowFloor,
|
|
3759
|
+
raw: signal.raw,
|
|
3760
|
+
range: signal.range ?? null
|
|
3761
|
+
}
|
|
3762
|
+
};
|
|
3763
|
+
}
|
|
3764
|
+
return null;
|
|
3708
3765
|
}
|
|
3709
3766
|
|
|
3710
3767
|
// plugins/core/analyzers/trigger-collision/text.ts
|
|
@@ -3714,18 +3771,18 @@ var TRIGGER_COLLISION_TEXTS = {
|
|
|
3714
3771
|
* cause part. Used for the advertiser-ambiguous-only, invocation-
|
|
3715
3772
|
* ambiguous-only, and cross-kind-only branches.
|
|
3716
3773
|
*/
|
|
3717
|
-
messageOnePart: '
|
|
3774
|
+
messageOnePart: '"{{normalized}}":\nTrigger collision: {{part}}.',
|
|
3718
3775
|
/**
|
|
3719
3776
|
* Top-level message when `analyzeTriggerBucket` accumulated two cause
|
|
3720
3777
|
* parts (advertiser-ambiguous AND invocation-ambiguous fire together).
|
|
3721
3778
|
* The joiner lives inside the template so future locales can adapt it
|
|
3722
3779
|
* (e.g. `'; y '` in Spanish) without touching the rule code.
|
|
3723
3780
|
*/
|
|
3724
|
-
messageTwoParts: '
|
|
3725
|
-
/** `<n>
|
|
3726
|
-
partAdvertisers: "{{count}}
|
|
3727
|
-
/** `<n>
|
|
3728
|
-
partInvocations: "{{count}}
|
|
3781
|
+
messageTwoParts: '"{{normalized}}":\nTrigger collision: {{first}}; and {{second}}.',
|
|
3782
|
+
/** `<n> advertisers: <list>` part, fires on the advertiser-ambiguous branch. */
|
|
3783
|
+
partAdvertisers: "{{count}} advertisers: {{paths}}",
|
|
3784
|
+
/** `<n> invocation forms: <list>` part, fires on the invocation-ambiguous branch. */
|
|
3785
|
+
partInvocations: "{{count}} invocation forms: {{forms}}",
|
|
3729
3786
|
/** Singular cross-kind cause: `non-canonical invocation <form> against advertiser <path>`. */
|
|
3730
3787
|
partNonCanonicalSingular: "non-canonical invocation {{forms}} against advertiser {{advertiser}}",
|
|
3731
3788
|
/** Plural cross-kind cause: `non-canonical invocations <forms> against advertiser <path>`. */
|
|
@@ -3733,14 +3790,14 @@ var TRIGGER_COLLISION_TEXTS = {
|
|
|
3733
3790
|
};
|
|
3734
3791
|
|
|
3735
3792
|
// plugins/core/analyzers/trigger-collision/index.ts
|
|
3736
|
-
var
|
|
3793
|
+
var ID25 = "trigger-collision";
|
|
3737
3794
|
var ADVERTISING_KINDS = /* @__PURE__ */ new Set([
|
|
3738
3795
|
"command",
|
|
3739
3796
|
"skill",
|
|
3740
3797
|
"agent"
|
|
3741
3798
|
]);
|
|
3742
3799
|
var triggerCollisionAnalyzer = {
|
|
3743
|
-
id:
|
|
3800
|
+
id: ID25,
|
|
3744
3801
|
pluginId: CORE_PLUGIN_ID,
|
|
3745
3802
|
kind: "analyzer",
|
|
3746
3803
|
mode: "deterministic",
|
|
@@ -3838,7 +3895,7 @@ function analyzeTriggerBucket(normalized, claims) {
|
|
|
3838
3895
|
part: parts[0]
|
|
3839
3896
|
});
|
|
3840
3897
|
return {
|
|
3841
|
-
analyzerId:
|
|
3898
|
+
analyzerId: ID25,
|
|
3842
3899
|
severity: "error",
|
|
3843
3900
|
nodeIds,
|
|
3844
3901
|
message,
|
|
@@ -3878,13 +3935,13 @@ var ASCII_FORMATTER_TEXTS = {
|
|
|
3878
3935
|
};
|
|
3879
3936
|
|
|
3880
3937
|
// plugins/core/formatters/ascii/index.ts
|
|
3881
|
-
var
|
|
3938
|
+
var ID26 = "ascii";
|
|
3882
3939
|
var KIND_ORDER = ["agent", "command", "skill", "markdown"];
|
|
3883
3940
|
var asciiFormatter = {
|
|
3884
|
-
id:
|
|
3941
|
+
id: ID26,
|
|
3885
3942
|
pluginId: CORE_PLUGIN_ID,
|
|
3886
3943
|
kind: "formatter",
|
|
3887
|
-
formatId:
|
|
3944
|
+
formatId: ID26,
|
|
3888
3945
|
description: "Renders the scan as plain text in three sections: nodes (grouped by kind), arrows, and issues. Used by `sm scan --format ascii`.",
|
|
3889
3946
|
// ASCII tree formatter, header + per-kind sections + per-issue
|
|
3890
3947
|
// section. Each section iterates and renders; splitting per section
|
|
@@ -3978,13 +4035,13 @@ function renderSection(out, kind, group) {
|
|
|
3978
4035
|
}
|
|
3979
4036
|
|
|
3980
4037
|
// plugins/core/formatters/json/index.ts
|
|
3981
|
-
var
|
|
4038
|
+
var ID27 = "json";
|
|
3982
4039
|
var jsonFormatter = {
|
|
3983
|
-
id:
|
|
4040
|
+
id: ID27,
|
|
3984
4041
|
pluginId: CORE_PLUGIN_ID,
|
|
3985
4042
|
kind: "formatter",
|
|
3986
4043
|
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`.",
|
|
3987
|
-
formatId:
|
|
4044
|
+
formatId: ID27,
|
|
3988
4045
|
format(ctx) {
|
|
3989
4046
|
if (ctx.scanResult !== void 0) {
|
|
3990
4047
|
return JSON.stringify(ctx.scanResult);
|
|
@@ -4122,14 +4179,33 @@ function resolveSpecRoot2() {
|
|
|
4122
4179
|
}
|
|
4123
4180
|
}
|
|
4124
4181
|
|
|
4182
|
+
// plugins/core/actions/node-bump/text.ts
|
|
4183
|
+
var BUMP_TEXTS = {
|
|
4184
|
+
/** Label of the inspector action button that dispatches a bump. */
|
|
4185
|
+
bumpLabel: "Bump",
|
|
4186
|
+
/** Tooltip shown when the bump button is disabled (the node is fresh, no drift). */
|
|
4187
|
+
bumpDisabledReason: "No drift to bump."
|
|
4188
|
+
};
|
|
4189
|
+
|
|
4125
4190
|
// plugins/core/actions/node-bump/index.ts
|
|
4126
|
-
var
|
|
4191
|
+
var ID28 = "node-bump";
|
|
4192
|
+
var bumpButton = {
|
|
4193
|
+
slot: "inspector.action.button",
|
|
4194
|
+
priority: 10
|
|
4195
|
+
};
|
|
4127
4196
|
var nodeBumpAction = {
|
|
4128
|
-
id:
|
|
4197
|
+
id: ID28,
|
|
4129
4198
|
pluginId: CORE_PLUGIN_ID,
|
|
4130
4199
|
kind: "action",
|
|
4131
4200
|
description: "Marks a node as updated: bumps `annotations.version`, refreshes sidecar hashes, and records the timestamp.",
|
|
4132
4201
|
mode: "deterministic",
|
|
4202
|
+
ui: { bumpButton },
|
|
4203
|
+
project(ctx) {
|
|
4204
|
+
for (const node of ctx.nodes) {
|
|
4205
|
+
if (node.sidecar?.present !== true) continue;
|
|
4206
|
+
emitBumpButton(ctx, node.path, staleStatus2(node.sidecar) !== null);
|
|
4207
|
+
}
|
|
4208
|
+
},
|
|
4133
4209
|
// The runtime contract uses generic <TInput, TReport>; bump narrows
|
|
4134
4210
|
// both. The cast is the standard pattern for built-ins that want
|
|
4135
4211
|
// typed local I/O while staying compatible with the open generic.
|
|
@@ -4138,6 +4214,20 @@ var nodeBumpAction = {
|
|
|
4138
4214
|
return invokeBump(input, ctx);
|
|
4139
4215
|
}
|
|
4140
4216
|
};
|
|
4217
|
+
function emitBumpButton(ctx, nodePath, enabled) {
|
|
4218
|
+
ctx.emitContribution(nodePath, bumpButton, {
|
|
4219
|
+
actionId: "core/node-bump",
|
|
4220
|
+
label: BUMP_TEXTS.bumpLabel,
|
|
4221
|
+
icon: "pi-arrow-up-right",
|
|
4222
|
+
enabled,
|
|
4223
|
+
...enabled ? {} : { disabledReason: BUMP_TEXTS.bumpDisabledReason }
|
|
4224
|
+
});
|
|
4225
|
+
}
|
|
4226
|
+
function staleStatus2(overlay) {
|
|
4227
|
+
const status = overlay?.status;
|
|
4228
|
+
if (status === void 0 || status === null || status === "fresh") return null;
|
|
4229
|
+
return status;
|
|
4230
|
+
}
|
|
4141
4231
|
function invokeBump(input, ctx) {
|
|
4142
4232
|
const overlay = ctx.node.sidecar ?? null;
|
|
4143
4233
|
const isFresh = overlay?.present === true && overlay.status === "fresh";
|
|
@@ -4184,9 +4274,9 @@ function pickCurrentVersion(overlay) {
|
|
|
4184
4274
|
|
|
4185
4275
|
// plugins/core/actions/node-set-stability/index.ts
|
|
4186
4276
|
var STABILITY_VALUES = ["experimental", "stable", "deprecated"];
|
|
4187
|
-
var
|
|
4277
|
+
var ID29 = "node-set-stability";
|
|
4188
4278
|
var nodeSetStabilityAction = {
|
|
4189
|
-
id:
|
|
4279
|
+
id: ID29,
|
|
4190
4280
|
pluginId: CORE_PLUGIN_ID,
|
|
4191
4281
|
kind: "action",
|
|
4192
4282
|
description: "Sets the lifecycle stage of the current node (writes `stability` to the sidecar).",
|
|
@@ -4225,14 +4315,33 @@ function invokeSetStability(input, ctx) {
|
|
|
4225
4315
|
return { report, writes: [write] };
|
|
4226
4316
|
}
|
|
4227
4317
|
|
|
4318
|
+
// plugins/core/actions/node-set-tags/text.ts
|
|
4319
|
+
var TAGS_TEXTS = {
|
|
4320
|
+
/** Label of the inspector action button that edits the node's tags. */
|
|
4321
|
+
editLabel: "Edit tags",
|
|
4322
|
+
/** Prompt label for the string-list tags input. */
|
|
4323
|
+
promptLabel: "Tags"
|
|
4324
|
+
};
|
|
4325
|
+
|
|
4228
4326
|
// plugins/core/actions/node-set-tags/index.ts
|
|
4229
|
-
var
|
|
4327
|
+
var ID30 = "node-set-tags";
|
|
4328
|
+
var setTagsButton = {
|
|
4329
|
+
slot: "inspector.action.button",
|
|
4330
|
+
priority: 15
|
|
4331
|
+
};
|
|
4230
4332
|
var nodeSetTagsAction = {
|
|
4231
|
-
id:
|
|
4333
|
+
id: ID30,
|
|
4232
4334
|
pluginId: CORE_PLUGIN_ID,
|
|
4233
4335
|
kind: "action",
|
|
4234
4336
|
description: "Sets the taxonomy tags of the current node (writes `tags` to the sidecar; whole-array replace).",
|
|
4235
4337
|
mode: "deterministic",
|
|
4338
|
+
ui: { setTagsButton },
|
|
4339
|
+
project(ctx) {
|
|
4340
|
+
for (const node of ctx.nodes) {
|
|
4341
|
+
if (node.sidecar?.present !== true) continue;
|
|
4342
|
+
emitSetTagsButton(ctx, node);
|
|
4343
|
+
}
|
|
4344
|
+
},
|
|
4236
4345
|
// The runtime contract uses generic <TInput, TReport>; this narrows
|
|
4237
4346
|
// both. The cast is the standard pattern for built-ins that want
|
|
4238
4347
|
// typed local I/O while staying compatible with the open generic.
|
|
@@ -4241,6 +4350,27 @@ var nodeSetTagsAction = {
|
|
|
4241
4350
|
return invokeSetTags(input, ctx);
|
|
4242
4351
|
}
|
|
4243
4352
|
};
|
|
4353
|
+
function emitSetTagsButton(ctx, node) {
|
|
4354
|
+
ctx.emitContribution(node.path, setTagsButton, {
|
|
4355
|
+
actionId: "core/node-set-tags",
|
|
4356
|
+
label: TAGS_TEXTS.editLabel,
|
|
4357
|
+
icon: "pi-tags",
|
|
4358
|
+
enabled: true,
|
|
4359
|
+
prompt: {
|
|
4360
|
+
inputType: "string-list",
|
|
4361
|
+
paramKey: "tags",
|
|
4362
|
+
label: TAGS_TEXTS.promptLabel,
|
|
4363
|
+
defaultValue: currentTags(node)
|
|
4364
|
+
}
|
|
4365
|
+
});
|
|
4366
|
+
}
|
|
4367
|
+
function currentTags(node) {
|
|
4368
|
+
const ann = node.sidecar?.annotations;
|
|
4369
|
+
if (!ann || typeof ann !== "object" || Array.isArray(ann)) return [];
|
|
4370
|
+
const value = ann["tags"];
|
|
4371
|
+
if (!Array.isArray(value)) return [];
|
|
4372
|
+
return value.filter((t) => typeof t === "string");
|
|
4373
|
+
}
|
|
4244
4374
|
function invokeSetTags(input, ctx) {
|
|
4245
4375
|
const tags = Array.isArray(input.tags) ? input.tags : [];
|
|
4246
4376
|
const timestamp = ctx.now().toISOString();
|
|
@@ -4264,14 +4394,44 @@ function invokeSetTags(input, ctx) {
|
|
|
4264
4394
|
return { report, writes: [write] };
|
|
4265
4395
|
}
|
|
4266
4396
|
|
|
4397
|
+
// plugins/core/actions/node-supersede/text.ts
|
|
4398
|
+
var SUPERSEDE_TEXTS = {
|
|
4399
|
+
/** Label of the inspector action button that declares supersession. */
|
|
4400
|
+
supersedeLabel: "Supersede",
|
|
4401
|
+
/** Tooltip shown when the supersede button is disabled (already superseded). */
|
|
4402
|
+
supersedeDisabledReason: "Already superseded.",
|
|
4403
|
+
/** Tooltip shown when there is no other node to supersede this one. */
|
|
4404
|
+
supersedeNoTargetsReason: "No other node to supersede this one.",
|
|
4405
|
+
/** Prompt label for the target node-picker (enum-pick over the live node set). */
|
|
4406
|
+
supersedePromptLabel: "Superseded by"
|
|
4407
|
+
};
|
|
4408
|
+
|
|
4267
4409
|
// plugins/core/actions/node-supersede/index.ts
|
|
4268
|
-
var
|
|
4410
|
+
var ID31 = "node-supersede";
|
|
4411
|
+
var supersedeButton = {
|
|
4412
|
+
slot: "inspector.action.button",
|
|
4413
|
+
priority: 10
|
|
4414
|
+
};
|
|
4269
4415
|
var nodeSupersedeAction = {
|
|
4270
|
-
id:
|
|
4416
|
+
id: ID31,
|
|
4271
4417
|
pluginId: CORE_PLUGIN_ID,
|
|
4272
4418
|
kind: "action",
|
|
4273
4419
|
description: "Declares the current node as superseded by another (writes `supersededBy` to the sidecar).",
|
|
4420
|
+
// Ships disabled by default (the declarer feature is still settling its
|
|
4421
|
+
// node-picker UX). The button self-projection gates as a unit with the
|
|
4422
|
+
// invoke executor: an enabled button pointing at a disabled action
|
|
4423
|
+
// would error on click, so the whole action stays experimental.
|
|
4424
|
+
stability: "experimental",
|
|
4274
4425
|
mode: "deterministic",
|
|
4426
|
+
ui: { supersedeButton },
|
|
4427
|
+
project(ctx) {
|
|
4428
|
+
const candidates = ctx.nodes.filter((n) => n.virtual !== true).map((n) => n.path);
|
|
4429
|
+
for (const node of ctx.nodes) {
|
|
4430
|
+
if (node.virtual === true) continue;
|
|
4431
|
+
const options = candidates.filter((p) => p !== node.path).map((p) => ({ value: p, label: p }));
|
|
4432
|
+
emitSupersedeButton(ctx, node, options);
|
|
4433
|
+
}
|
|
4434
|
+
},
|
|
4275
4435
|
// The runtime contract uses generic <TInput, TReport>; supersede
|
|
4276
4436
|
// narrows both. The cast is the standard pattern for built-ins that
|
|
4277
4437
|
// want typed local I/O while staying compatible with the open generic.
|
|
@@ -4280,6 +4440,35 @@ var nodeSupersedeAction = {
|
|
|
4280
4440
|
return invokeSupersede(input, ctx);
|
|
4281
4441
|
}
|
|
4282
4442
|
};
|
|
4443
|
+
function emitSupersedeButton(ctx, node, options) {
|
|
4444
|
+
const disabledReason = resolveDisabledReason(node, options.length);
|
|
4445
|
+
ctx.emitContribution(node.path, supersedeButton, {
|
|
4446
|
+
actionId: "core/node-supersede",
|
|
4447
|
+
label: SUPERSEDE_TEXTS.supersedeLabel,
|
|
4448
|
+
icon: "pi-arrow-right-arrow-left",
|
|
4449
|
+
enabled: disabledReason === void 0,
|
|
4450
|
+
...disabledReason === void 0 ? {} : { disabledReason },
|
|
4451
|
+
prompt: {
|
|
4452
|
+
inputType: "enum-pick",
|
|
4453
|
+
paramKey: "supersededBy",
|
|
4454
|
+
label: SUPERSEDE_TEXTS.supersedePromptLabel,
|
|
4455
|
+
options
|
|
4456
|
+
}
|
|
4457
|
+
});
|
|
4458
|
+
}
|
|
4459
|
+
function resolveDisabledReason(node, optionCount) {
|
|
4460
|
+
if (alreadySuperseded(node)) return SUPERSEDE_TEXTS.supersedeDisabledReason;
|
|
4461
|
+
if (optionCount === 0) return SUPERSEDE_TEXTS.supersedeNoTargetsReason;
|
|
4462
|
+
return void 0;
|
|
4463
|
+
}
|
|
4464
|
+
function alreadySuperseded(node) {
|
|
4465
|
+
const sidecar = node.sidecar;
|
|
4466
|
+
if (!sidecar || sidecar.present !== true) return false;
|
|
4467
|
+
const ann = sidecar.annotations;
|
|
4468
|
+
if (!ann || typeof ann !== "object" || Array.isArray(ann)) return false;
|
|
4469
|
+
const value = ann["supersededBy"];
|
|
4470
|
+
return typeof value === "string" && value.length > 0;
|
|
4471
|
+
}
|
|
4283
4472
|
function invokeSupersede(input, ctx) {
|
|
4284
4473
|
const supersededBy = input.supersededBy;
|
|
4285
4474
|
if (supersededBy === ctx.node.path) {
|
|
@@ -4785,15 +4974,16 @@ var updateCheckHook = {
|
|
|
4785
4974
|
var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: VERSION };
|
|
4786
4975
|
var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: VERSION };
|
|
4787
4976
|
var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: VERSION };
|
|
4977
|
+
var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "claude", version: VERSION };
|
|
4788
4978
|
var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: VERSION };
|
|
4789
4979
|
var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: VERSION };
|
|
4790
4980
|
var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: VERSION };
|
|
4791
4981
|
var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: VERSION };
|
|
4792
4982
|
var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: VERSION };
|
|
4983
|
+
var backtickPathExtractor2 = { ...backtickPathExtractor, pluginId: "core", version: VERSION };
|
|
4793
4984
|
var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: VERSION };
|
|
4794
4985
|
var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: VERSION };
|
|
4795
4986
|
var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: VERSION };
|
|
4796
|
-
var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: VERSION };
|
|
4797
4987
|
var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: VERSION };
|
|
4798
4988
|
var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: VERSION };
|
|
4799
4989
|
var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: VERSION };
|
|
@@ -4810,8 +5000,6 @@ var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", v
|
|
|
4810
5000
|
var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: VERSION };
|
|
4811
5001
|
var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: VERSION };
|
|
4812
5002
|
var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: VERSION };
|
|
4813
|
-
var supersedeAnalyzer2 = { ...supersedeAnalyzer, pluginId: "core", version: VERSION };
|
|
4814
|
-
var tagsAnalyzer2 = { ...tagsAnalyzer, pluginId: "core", version: VERSION };
|
|
4815
5003
|
var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: VERSION };
|
|
4816
5004
|
var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: VERSION };
|
|
4817
5005
|
var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: VERSION };
|
|
@@ -4827,7 +5015,8 @@ var builtInPlugins = [
|
|
|
4827
5015
|
extensions: [
|
|
4828
5016
|
claudeProvider2,
|
|
4829
5017
|
atDirectiveExtractor2,
|
|
4830
|
-
slashCommandExtractor2
|
|
5018
|
+
slashCommandExtractor2,
|
|
5019
|
+
toolsCounterExtractor2
|
|
4831
5020
|
]
|
|
4832
5021
|
},
|
|
4833
5022
|
{
|
|
@@ -4857,10 +5046,10 @@ var builtInPlugins = [
|
|
|
4857
5046
|
extensions: [
|
|
4858
5047
|
coreMarkdownProvider2,
|
|
4859
5048
|
annotationsExtractor2,
|
|
5049
|
+
backtickPathExtractor2,
|
|
4860
5050
|
externalUrlCounterExtractor2,
|
|
4861
5051
|
markdownLinkExtractor2,
|
|
4862
5052
|
mcpToolsExtractor2,
|
|
4863
|
-
toolsCounterExtractor2,
|
|
4864
5053
|
annotationFieldUnknownAnalyzer2,
|
|
4865
5054
|
annotationOrphanAnalyzer2,
|
|
4866
5055
|
annotationStaleAnalyzer2,
|
|
@@ -4877,8 +5066,6 @@ var builtInPlugins = [
|
|
|
4877
5066
|
referenceRedundantAnalyzer2,
|
|
4878
5067
|
schemaViolationAnalyzer2,
|
|
4879
5068
|
signalCollisionAnalyzer2,
|
|
4880
|
-
supersedeAnalyzer2,
|
|
4881
|
-
tagsAnalyzer2,
|
|
4882
5069
|
triggerCollisionAnalyzer2,
|
|
4883
5070
|
asciiFormatter2,
|
|
4884
5071
|
jsonFormatter2,
|
|
@@ -6287,7 +6474,7 @@ function resolveSpecRoot3() {
|
|
|
6287
6474
|
}
|
|
6288
6475
|
|
|
6289
6476
|
// cli/i18n/bump.texts.ts
|
|
6290
|
-
var
|
|
6477
|
+
var BUMP_TEXTS2 = {
|
|
6291
6478
|
// --- argument validation --------------------------------------------------
|
|
6292
6479
|
/**
|
|
6293
6480
|
* §3.1b two-line block. Mutex between the positional <node.path> and
|
|
@@ -7784,7 +7971,8 @@ var LINK_KIND_VALUES = Object.freeze([
|
|
|
7784
7971
|
"invokes",
|
|
7785
7972
|
"references",
|
|
7786
7973
|
"mentions",
|
|
7787
|
-
"supersedes"
|
|
7974
|
+
"supersedes",
|
|
7975
|
+
"points"
|
|
7788
7976
|
]);
|
|
7789
7977
|
var SEVERITY_VALUES = Object.freeze([
|
|
7790
7978
|
"error",
|
|
@@ -7977,6 +8165,7 @@ function rowToNode(row) {
|
|
|
7977
8165
|
const parsed = JSON.parse(row.externalRefsJson);
|
|
7978
8166
|
if (Array.isArray(parsed) && parsed.length > 0) node.externalRefs = parsed;
|
|
7979
8167
|
}
|
|
8168
|
+
if (row.modifiedAtMs !== null) node.modifiedAtMs = row.modifiedAtMs;
|
|
7980
8169
|
return node;
|
|
7981
8170
|
}
|
|
7982
8171
|
function rowToLink(row) {
|
|
@@ -8493,7 +8682,10 @@ function nodeToRow(node, scannedAt) {
|
|
|
8493
8682
|
// the column stays sparse for nodes whose bodies have no http(s)
|
|
8494
8683
|
// URLs at all. Round-tripped by `rowToNode` on load.
|
|
8495
8684
|
externalRefsJson: node.externalRefs && node.externalRefs.length > 0 ? JSON.stringify(node.externalRefs) : null,
|
|
8496
|
-
scannedAt
|
|
8685
|
+
scannedAt,
|
|
8686
|
+
// File mtime (Unix ms) from the walker; NULL for virtual / derived
|
|
8687
|
+
// nodes that carry no backing file. Round-tripped by `rowToNode`.
|
|
8688
|
+
modifiedAtMs: node.modifiedAtMs ?? null
|
|
8497
8689
|
};
|
|
8498
8690
|
}
|
|
8499
8691
|
function projectAnnotationColumns(node) {
|
|
@@ -9494,10 +9686,10 @@ var BumpCommand = class extends SmCommand {
|
|
|
9494
9686
|
);
|
|
9495
9687
|
if (!persisted) {
|
|
9496
9688
|
this.printer.error(
|
|
9497
|
-
tx(
|
|
9689
|
+
tx(BUMP_TEXTS2.nodeNotFound, {
|
|
9498
9690
|
glyph: ansi.red("\u2715"),
|
|
9499
9691
|
nodePath: this.nodePath ?? "<pending>",
|
|
9500
|
-
hint: ansi.dim(
|
|
9692
|
+
hint: ansi.dim(BUMP_TEXTS2.nodeNotFoundHint)
|
|
9501
9693
|
})
|
|
9502
9694
|
);
|
|
9503
9695
|
return ExitCode.NotFound;
|
|
@@ -9517,27 +9709,27 @@ var BumpCommand = class extends SmCommand {
|
|
|
9517
9709
|
const errGlyph = ansi.red("\u2715");
|
|
9518
9710
|
if (this.pending && this.nodePath !== void 0) {
|
|
9519
9711
|
this.printer.error(
|
|
9520
|
-
tx(
|
|
9712
|
+
tx(BUMP_TEXTS2.nodeAndPendingMutex, {
|
|
9521
9713
|
glyph: errGlyph,
|
|
9522
|
-
hint: ansi.dim(
|
|
9714
|
+
hint: ansi.dim(BUMP_TEXTS2.nodeAndPendingMutexHint)
|
|
9523
9715
|
})
|
|
9524
9716
|
);
|
|
9525
9717
|
return ExitCode.Error;
|
|
9526
9718
|
}
|
|
9527
9719
|
if (!this.pending && this.nodePath === void 0) {
|
|
9528
9720
|
this.printer.error(
|
|
9529
|
-
tx(
|
|
9721
|
+
tx(BUMP_TEXTS2.noTargetSpecified, {
|
|
9530
9722
|
glyph: errGlyph,
|
|
9531
|
-
hint: ansi.dim(
|
|
9723
|
+
hint: ansi.dim(BUMP_TEXTS2.noTargetSpecifiedHint)
|
|
9532
9724
|
})
|
|
9533
9725
|
);
|
|
9534
9726
|
return ExitCode.Error;
|
|
9535
9727
|
}
|
|
9536
9728
|
if (this.staged && !this.pending) {
|
|
9537
9729
|
this.printer.error(
|
|
9538
|
-
tx(
|
|
9730
|
+
tx(BUMP_TEXTS2.stagedRequiresPending, {
|
|
9539
9731
|
glyph: errGlyph,
|
|
9540
|
-
hint: ansi.dim(
|
|
9732
|
+
hint: ansi.dim(BUMP_TEXTS2.stagedRequiresPendingHint)
|
|
9541
9733
|
})
|
|
9542
9734
|
);
|
|
9543
9735
|
return ExitCode.Error;
|
|
@@ -9596,10 +9788,10 @@ var BumpCommand = class extends SmCommand {
|
|
|
9596
9788
|
const node = nodes.find((n) => n.path === this.nodePath);
|
|
9597
9789
|
if (!node) {
|
|
9598
9790
|
this.printer.error(
|
|
9599
|
-
tx(
|
|
9791
|
+
tx(BUMP_TEXTS2.nodeNotFound, {
|
|
9600
9792
|
glyph: ansi.red("\u2715"),
|
|
9601
9793
|
nodePath: this.nodePath,
|
|
9602
|
-
hint: ansi.dim(
|
|
9794
|
+
hint: ansi.dim(BUMP_TEXTS2.nodeNotFoundHint)
|
|
9603
9795
|
})
|
|
9604
9796
|
);
|
|
9605
9797
|
return ExitCode.NotFound;
|
|
@@ -9621,9 +9813,9 @@ var BumpCommand = class extends SmCommand {
|
|
|
9621
9813
|
const errGlyph = ansi.red("\u2715");
|
|
9622
9814
|
if (item.status === "error") {
|
|
9623
9815
|
this.printer.error(
|
|
9624
|
-
tx(
|
|
9816
|
+
tx(BUMP_TEXTS2.bumpFailed, {
|
|
9625
9817
|
glyph: errGlyph,
|
|
9626
|
-
message: tx(
|
|
9818
|
+
message: tx(BUMP_TEXTS2.resolveAbsPathFailed, {
|
|
9627
9819
|
nodePath: node.path,
|
|
9628
9820
|
message: item.message
|
|
9629
9821
|
})
|
|
@@ -9633,10 +9825,10 @@ var BumpCommand = class extends SmCommand {
|
|
|
9633
9825
|
}
|
|
9634
9826
|
if (item.status === "refused") {
|
|
9635
9827
|
this.printer.error(
|
|
9636
|
-
tx(
|
|
9828
|
+
tx(BUMP_TEXTS2.refusedFresh, {
|
|
9637
9829
|
glyph: errGlyph,
|
|
9638
9830
|
nodePath: node.path,
|
|
9639
|
-
hint: ansi.dim(
|
|
9831
|
+
hint: ansi.dim(BUMP_TEXTS2.refusedFreshHint)
|
|
9640
9832
|
})
|
|
9641
9833
|
);
|
|
9642
9834
|
return ExitCode.Error;
|
|
@@ -9663,9 +9855,9 @@ var BumpCommand = class extends SmCommand {
|
|
|
9663
9855
|
if (applied.error !== void 0) {
|
|
9664
9856
|
if (applied.error instanceof EConsentRequiredError) throw applied.error;
|
|
9665
9857
|
this.printer.error(
|
|
9666
|
-
tx(
|
|
9858
|
+
tx(BUMP_TEXTS2.bumpFailed, {
|
|
9667
9859
|
glyph: ansi.red("\u2715"),
|
|
9668
|
-
message: tx(
|
|
9860
|
+
message: tx(BUMP_TEXTS2.storeFailedDetail, {
|
|
9669
9861
|
path: applied.sidecarPath ?? sidecarPathFor(item.absPath),
|
|
9670
9862
|
message: formatErrorMessage(applied.error)
|
|
9671
9863
|
})
|
|
@@ -9682,11 +9874,11 @@ var BumpCommand = class extends SmCommand {
|
|
|
9682
9874
|
const version = item.report.version ?? 1;
|
|
9683
9875
|
if (item.report.createdSidecar === true) {
|
|
9684
9876
|
this.printer.data(
|
|
9685
|
-
tx(
|
|
9877
|
+
tx(BUMP_TEXTS2.bumpedCreated, { glyph: okGlyph, sidecarPath, nodePath: node.path, version })
|
|
9686
9878
|
);
|
|
9687
9879
|
} else {
|
|
9688
9880
|
this.printer.data(
|
|
9689
|
-
tx(
|
|
9881
|
+
tx(BUMP_TEXTS2.bumped, { glyph: okGlyph, nodePath: node.path, version })
|
|
9690
9882
|
);
|
|
9691
9883
|
}
|
|
9692
9884
|
return ExitCode.Ok;
|
|
@@ -9698,7 +9890,7 @@ var BumpCommand = class extends SmCommand {
|
|
|
9698
9890
|
const stale = nodes.filter((n) => n.sidecar?.present === true && n.sidecar.status !== null && n.sidecar.status !== "fresh").sort((a, b) => a.path.localeCompare(b.path));
|
|
9699
9891
|
if (stale.length === 0) return this.#renderEmptyPending();
|
|
9700
9892
|
if (!this.json) {
|
|
9701
|
-
this.printer.info(tx(
|
|
9893
|
+
this.printer.info(tx(BUMP_TEXTS2.pendingBanner, { count: stale.length }));
|
|
9702
9894
|
}
|
|
9703
9895
|
const plan = computeBumpPlan(stale, { cwd, force: this.force });
|
|
9704
9896
|
const outcomes = await this.#executePending(plan, cwd, ansi);
|
|
@@ -9716,19 +9908,19 @@ var BumpCommand = class extends SmCommand {
|
|
|
9716
9908
|
const gitOk = ensureGitForStaged(cwd);
|
|
9717
9909
|
if (gitOk === "no-repo") {
|
|
9718
9910
|
this.printer.error(
|
|
9719
|
-
tx(
|
|
9911
|
+
tx(BUMP_TEXTS2.notInGitRepo, {
|
|
9720
9912
|
glyph: errGlyph,
|
|
9721
9913
|
cwd,
|
|
9722
|
-
hint: ansi.dim(
|
|
9914
|
+
hint: ansi.dim(BUMP_TEXTS2.notInGitRepoHint)
|
|
9723
9915
|
})
|
|
9724
9916
|
);
|
|
9725
9917
|
return ExitCode.NotFound;
|
|
9726
9918
|
}
|
|
9727
9919
|
if (gitOk === "no-binary") {
|
|
9728
9920
|
this.printer.error(
|
|
9729
|
-
tx(
|
|
9921
|
+
tx(BUMP_TEXTS2.gitBinaryMissing, {
|
|
9730
9922
|
glyph: errGlyph,
|
|
9731
|
-
hint: ansi.dim(
|
|
9923
|
+
hint: ansi.dim(BUMP_TEXTS2.gitBinaryMissingHint)
|
|
9732
9924
|
})
|
|
9733
9925
|
);
|
|
9734
9926
|
return ExitCode.Error;
|
|
@@ -9766,7 +9958,7 @@ var BumpCommand = class extends SmCommand {
|
|
|
9766
9958
|
return {
|
|
9767
9959
|
nodePath: item.nodePath,
|
|
9768
9960
|
status: "error",
|
|
9769
|
-
message: tx(
|
|
9961
|
+
message: tx(BUMP_TEXTS2.storeFailedDetail, {
|
|
9770
9962
|
path: applied.sidecarPath ?? sidecarPathFor(item.absPath),
|
|
9771
9963
|
message: formatErrorMessage(applied.error)
|
|
9772
9964
|
})
|
|
@@ -9786,11 +9978,11 @@ var BumpCommand = class extends SmCommand {
|
|
|
9786
9978
|
const addErr = stageSidecar(cwd, sidecarPath);
|
|
9787
9979
|
if (addErr === null || this.json) return;
|
|
9788
9980
|
this.printer.warn(
|
|
9789
|
-
tx(
|
|
9981
|
+
tx(BUMP_TEXTS2.gitAddFailed, {
|
|
9790
9982
|
glyph: ansi.yellow("\u26A0"),
|
|
9791
9983
|
path: sidecarPath,
|
|
9792
9984
|
message: addErr,
|
|
9793
|
-
hint: ansi.dim(tx(
|
|
9985
|
+
hint: ansi.dim(tx(BUMP_TEXTS2.gitAddFailedHint, { path: sidecarPath }))
|
|
9794
9986
|
})
|
|
9795
9987
|
);
|
|
9796
9988
|
}
|
|
@@ -9810,7 +10002,7 @@ var BumpCommand = class extends SmCommand {
|
|
|
9810
10002
|
this.printer.data(JSON.stringify(empty) + "\n");
|
|
9811
10003
|
return ExitCode.Ok;
|
|
9812
10004
|
}
|
|
9813
|
-
this.printer.data(
|
|
10005
|
+
this.printer.data(BUMP_TEXTS2.pendingNone);
|
|
9814
10006
|
return ExitCode.Ok;
|
|
9815
10007
|
}
|
|
9816
10008
|
// Complexity is from per-status rendering (4 status values) plus
|
|
@@ -9841,24 +10033,24 @@ var BumpCommand = class extends SmCommand {
|
|
|
9841
10033
|
for (const o of outcomes) {
|
|
9842
10034
|
if (o.status === "bumped") {
|
|
9843
10035
|
this.printer.data(
|
|
9844
|
-
tx(
|
|
10036
|
+
tx(BUMP_TEXTS2.bumpedItem, {
|
|
9845
10037
|
nodePath: o.nodePath,
|
|
9846
10038
|
version: o.version ?? 0,
|
|
9847
10039
|
createdSuffix: o.createdSidecar === true ? " (new sidecar)" : ""
|
|
9848
10040
|
})
|
|
9849
10041
|
);
|
|
9850
10042
|
} else if (o.status === "refused") {
|
|
9851
|
-
this.printer.data(tx(
|
|
10043
|
+
this.printer.data(tx(BUMP_TEXTS2.refusedItem, { nodePath: o.nodePath }));
|
|
9852
10044
|
} else if (o.status === "skipped") {
|
|
9853
10045
|
this.printer.data(
|
|
9854
|
-
tx(
|
|
10046
|
+
tx(BUMP_TEXTS2.skippedItem, {
|
|
9855
10047
|
nodePath: o.nodePath,
|
|
9856
10048
|
reason: o.reason ?? "unknown"
|
|
9857
10049
|
})
|
|
9858
10050
|
);
|
|
9859
10051
|
} else {
|
|
9860
10052
|
this.printer.data(
|
|
9861
|
-
tx(
|
|
10053
|
+
tx(BUMP_TEXTS2.errorItem, {
|
|
9862
10054
|
nodePath: o.nodePath,
|
|
9863
10055
|
message: o.message ?? ""
|
|
9864
10056
|
})
|
|
@@ -9866,7 +10058,7 @@ var BumpCommand = class extends SmCommand {
|
|
|
9866
10058
|
}
|
|
9867
10059
|
}
|
|
9868
10060
|
this.printer.info(
|
|
9869
|
-
tx(
|
|
10061
|
+
tx(BUMP_TEXTS2.pendingSummary, {
|
|
9870
10062
|
bumped: counts.bumped,
|
|
9871
10063
|
refused: counts.refused,
|
|
9872
10064
|
skipped: counts.skipped,
|
|
@@ -10802,11 +10994,13 @@ var PluginLoader = class {
|
|
|
10802
10994
|
if (kind === "provider" && discoveredKinds) {
|
|
10803
10995
|
instance["kinds"] = discoveredKinds;
|
|
10804
10996
|
}
|
|
10997
|
+
const stability = exported["stability"];
|
|
10805
10998
|
return { ok: true, extension: {
|
|
10806
10999
|
kind,
|
|
10807
11000
|
id: pathId2,
|
|
10808
11001
|
pluginId,
|
|
10809
11002
|
version: exported["version"],
|
|
11003
|
+
...stability !== void 0 ? { stability } : {},
|
|
10810
11004
|
entryPath: abs,
|
|
10811
11005
|
module: mod,
|
|
10812
11006
|
instance
|
|
@@ -10917,29 +11111,39 @@ function isPluginLocked(idOrQualified) {
|
|
|
10917
11111
|
}
|
|
10918
11112
|
|
|
10919
11113
|
// kernel/config/plugin-resolver.ts
|
|
10920
|
-
|
|
11114
|
+
var SHIPS_DISABLED = /* @__PURE__ */ new Set([
|
|
11115
|
+
"experimental",
|
|
11116
|
+
"deprecated"
|
|
11117
|
+
]);
|
|
11118
|
+
function installedDefaultEnabled(stability) {
|
|
11119
|
+
return stability === void 0 || !SHIPS_DISABLED.has(stability);
|
|
11120
|
+
}
|
|
11121
|
+
function resolvePluginEnabled(pluginId, cfg, dbOverrides, installedDefault = true) {
|
|
10921
11122
|
if (isPluginLocked(pluginId)) return true;
|
|
10922
11123
|
if (dbOverrides.has(pluginId)) return dbOverrides.get(pluginId) === true;
|
|
10923
11124
|
const settingsEntry = cfg.plugins[pluginId];
|
|
10924
11125
|
if (settingsEntry?.enabled !== void 0) return settingsEntry.enabled;
|
|
10925
|
-
return
|
|
11126
|
+
return installedDefault;
|
|
10926
11127
|
}
|
|
10927
11128
|
function makeEnabledResolver(cfg, dbOverrides) {
|
|
10928
|
-
return (pluginId) => resolvePluginEnabled(pluginId, cfg, dbOverrides);
|
|
11129
|
+
return (pluginId, installedDefault) => resolvePluginEnabled(pluginId, cfg, dbOverrides, installedDefault);
|
|
10929
11130
|
}
|
|
10930
11131
|
|
|
10931
11132
|
// core/runtime/plugin-runtime/resolver.ts
|
|
10932
|
-
function defaultResolveEnabled(_id) {
|
|
10933
|
-
return
|
|
11133
|
+
function defaultResolveEnabled(_id, installedDefault = true) {
|
|
11134
|
+
return installedDefault;
|
|
10934
11135
|
}
|
|
10935
11136
|
function isBuiltInExtensionEnabled(plugin, ext, resolveEnabled) {
|
|
10936
|
-
return isPluginEntryEnabled(plugin, ext.id, resolveEnabled);
|
|
11137
|
+
return isPluginEntryEnabled(plugin, ext.id, resolveEnabled, ext.stability);
|
|
10937
11138
|
}
|
|
10938
|
-
function isPluginEntryEnabled(plugin, extId, resolveEnabled) {
|
|
10939
|
-
return resolveEnabled(qualifiedExtensionId(plugin.id, extId));
|
|
11139
|
+
function isPluginEntryEnabled(plugin, extId, resolveEnabled, stability) {
|
|
11140
|
+
return resolveEnabled(qualifiedExtensionId(plugin.id, extId), installedDefaultEnabled(stability));
|
|
10940
11141
|
}
|
|
10941
11142
|
function isPluginExtensionEnabled(ext, resolveEnabled) {
|
|
10942
|
-
return resolveEnabled(
|
|
11143
|
+
return resolveEnabled(
|
|
11144
|
+
qualifiedExtensionId(ext.pluginId, ext.id),
|
|
11145
|
+
installedDefaultEnabled(ext.stability)
|
|
11146
|
+
);
|
|
10943
11147
|
}
|
|
10944
11148
|
async function buildEnabledResolver(ctx) {
|
|
10945
11149
|
const { effective: cfg } = loadConfig({ ...ctx });
|
|
@@ -11137,11 +11341,11 @@ async function* walkContent(roots, options) {
|
|
|
11137
11341
|
const extensions = options.extensions;
|
|
11138
11342
|
const sizeLimit = buildSizeLimit(options);
|
|
11139
11343
|
for (const root of roots) {
|
|
11140
|
-
for await (const
|
|
11141
|
-
const relPath = relative2(root,
|
|
11344
|
+
for await (const entry of walkRoot(root, root, filter, extensions, sizeLimit)) {
|
|
11345
|
+
const relPath = relative2(root, entry.full).split(sep3).join("/");
|
|
11142
11346
|
let raw;
|
|
11143
11347
|
try {
|
|
11144
|
-
raw = await readFile(
|
|
11348
|
+
raw = await readFile(entry.full, "utf8");
|
|
11145
11349
|
} catch {
|
|
11146
11350
|
continue;
|
|
11147
11351
|
}
|
|
@@ -11151,6 +11355,9 @@ async function* walkContent(roots, options) {
|
|
|
11151
11355
|
body: parsed.body,
|
|
11152
11356
|
frontmatterRaw: parsed.frontmatterRaw,
|
|
11153
11357
|
frontmatter: parsed.frontmatter,
|
|
11358
|
+
// File mtime from the TOCTOU `lstat` (zero extra syscalls).
|
|
11359
|
+
// Threaded onto the persisted `Node` as `modifiedAtMs`.
|
|
11360
|
+
modifiedAtMs: entry.modifiedAtMs,
|
|
11154
11361
|
// Audit L1: forward parser diagnostics (e.g. malformed YAML)
|
|
11155
11362
|
// through the IRawNode surface so the orchestrator can
|
|
11156
11363
|
// convert them into warn-level kernel `Issue` rows. Omitted
|
|
@@ -11191,7 +11398,7 @@ async function* walkRoot(root, current, filter, extensions, sizeLimit) {
|
|
|
11191
11398
|
sizeLimit.onOversizedFile?.({ path: rel, bytes: s.size });
|
|
11192
11399
|
continue;
|
|
11193
11400
|
}
|
|
11194
|
-
yield full;
|
|
11401
|
+
yield { full, modifiedAtMs: Math.round(s.mtimeMs) };
|
|
11195
11402
|
} catch {
|
|
11196
11403
|
}
|
|
11197
11404
|
}
|
|
@@ -11273,8 +11480,8 @@ function bucketLoaded(loaded, runtime, pluginOrder) {
|
|
|
11273
11480
|
extractor: runtime.extensions.extractors,
|
|
11274
11481
|
analyzer: runtime.extensions.analyzers,
|
|
11275
11482
|
formatter: runtime.extensions.formatters,
|
|
11276
|
-
hook: runtime.extensions.hooks
|
|
11277
|
-
|
|
11483
|
+
hook: runtime.extensions.hooks,
|
|
11484
|
+
action: runtime.extensions.actions
|
|
11278
11485
|
});
|
|
11279
11486
|
runtime.manifests.push({
|
|
11280
11487
|
id: ext.id,
|
|
@@ -11389,7 +11596,7 @@ async function loadPluginRuntime(opts = {}) {
|
|
|
11389
11596
|
const loader = createPluginLoader(loaderOpts);
|
|
11390
11597
|
const discovered = await loader.discoverAndLoadAll();
|
|
11391
11598
|
const runtime = {
|
|
11392
|
-
extensions: { providers: [], extractors: [], analyzers: [], formatters: [], hooks: [] },
|
|
11599
|
+
extensions: { providers: [], extractors: [], analyzers: [], formatters: [], hooks: [], actions: [] },
|
|
11393
11600
|
annotationContributions: [],
|
|
11394
11601
|
viewContributions: [],
|
|
11395
11602
|
manifests: [],
|
|
@@ -11444,7 +11651,7 @@ function enforceRootExclusivity(catalog) {
|
|
|
11444
11651
|
}
|
|
11445
11652
|
function emptyPluginRuntime() {
|
|
11446
11653
|
const runtime = {
|
|
11447
|
-
extensions: { providers: [], extractors: [], analyzers: [], formatters: [], hooks: [] },
|
|
11654
|
+
extensions: { providers: [], extractors: [], analyzers: [], formatters: [], hooks: [], actions: [] },
|
|
11448
11655
|
annotationContributions: [],
|
|
11449
11656
|
viewContributions: [],
|
|
11450
11657
|
manifests: [],
|
|
@@ -11462,23 +11669,26 @@ function emptyPluginRuntime() {
|
|
|
11462
11669
|
function collectRegisteredContributionKeys(composed) {
|
|
11463
11670
|
const keys = /* @__PURE__ */ new Set();
|
|
11464
11671
|
if (!composed) return keys;
|
|
11465
|
-
for (const ext of [...composed.extractors, ...composed.analyzers]) {
|
|
11466
|
-
|
|
11467
|
-
if (typeof raw !== "object" || raw === null) continue;
|
|
11468
|
-
for (const [contributionId, value] of Object.entries(raw)) {
|
|
11469
|
-
if (typeof value !== "object" || value === null) continue;
|
|
11470
|
-
keys.add(`${ext.pluginId}/${ext.id}/${contributionId}`);
|
|
11471
|
-
}
|
|
11672
|
+
for (const ext of [...composed.extractors, ...composed.analyzers, ...composed.actions ?? []]) {
|
|
11673
|
+
addContributionKeysForExtension(ext, keys);
|
|
11472
11674
|
}
|
|
11473
11675
|
return keys;
|
|
11474
11676
|
}
|
|
11677
|
+
function addContributionKeysForExtension(ext, keys) {
|
|
11678
|
+
const raw = ext.ui;
|
|
11679
|
+
if (typeof raw !== "object" || raw === null) return;
|
|
11680
|
+
for (const [contributionId, value] of Object.entries(raw)) {
|
|
11681
|
+
if (typeof value !== "object" || value === null) continue;
|
|
11682
|
+
keys.add(`${ext.pluginId}/${ext.id}/${contributionId}`);
|
|
11683
|
+
}
|
|
11684
|
+
}
|
|
11475
11685
|
function filterBuiltInManifests(manifests, resolveEnabled) {
|
|
11476
11686
|
const pluginById = /* @__PURE__ */ new Map();
|
|
11477
11687
|
for (const plugin of builtInPlugins) pluginById.set(plugin.id, plugin);
|
|
11478
11688
|
return manifests.filter((m) => {
|
|
11479
11689
|
const plugin = pluginById.get(m.pluginId);
|
|
11480
11690
|
if (!plugin) return true;
|
|
11481
|
-
return isPluginEntryEnabled(plugin, m.id, resolveEnabled);
|
|
11691
|
+
return isPluginEntryEnabled(plugin, m.id, resolveEnabled, m.stability);
|
|
11482
11692
|
});
|
|
11483
11693
|
}
|
|
11484
11694
|
|
|
@@ -11489,9 +11699,10 @@ function composeScanExtensions(opts) {
|
|
|
11489
11699
|
const extractors = [];
|
|
11490
11700
|
const analyzers = [];
|
|
11491
11701
|
const hooks = [];
|
|
11702
|
+
const actions = [];
|
|
11492
11703
|
if (!opts.noBuiltIns) {
|
|
11493
11704
|
accumulateBuiltInScanExtensions(
|
|
11494
|
-
{ providers, extractors, analyzers, hooks },
|
|
11705
|
+
{ providers, extractors, analyzers, hooks, actions },
|
|
11495
11706
|
resolveEnabled
|
|
11496
11707
|
);
|
|
11497
11708
|
}
|
|
@@ -11507,6 +11718,9 @@ function composeScanExtensions(opts) {
|
|
|
11507
11718
|
for (const ext of opts.pluginRuntime.extensions.hooks) {
|
|
11508
11719
|
if (isPluginExtensionEnabled(ext, resolveEnabled)) hooks.push(ext);
|
|
11509
11720
|
}
|
|
11721
|
+
for (const ext of opts.pluginRuntime.extensions.actions) {
|
|
11722
|
+
if (isPluginExtensionEnabled(ext, resolveEnabled)) actions.push(ext);
|
|
11723
|
+
}
|
|
11510
11724
|
const finalProviders = opts.killSwitches?.providers === true ? [] : providers;
|
|
11511
11725
|
const finalExtractors = opts.killSwitches?.extractors === true ? [] : extractors;
|
|
11512
11726
|
const finalAnalyzers = opts.killSwitches?.analyzers === true ? [] : analyzers;
|
|
@@ -11517,7 +11731,8 @@ function composeScanExtensions(opts) {
|
|
|
11517
11731
|
providers: finalProviders,
|
|
11518
11732
|
extractors: finalExtractors,
|
|
11519
11733
|
analyzers: finalAnalyzers,
|
|
11520
|
-
hooks
|
|
11734
|
+
hooks,
|
|
11735
|
+
actions
|
|
11521
11736
|
};
|
|
11522
11737
|
}
|
|
11523
11738
|
function accumulateBuiltInScanExtensions(buckets, resolveEnabled) {
|
|
@@ -11538,6 +11753,7 @@ function accumulateBuiltInScanExtensions(buckets, resolveEnabled) {
|
|
|
11538
11753
|
buckets.hooks.push(ext);
|
|
11539
11754
|
break;
|
|
11540
11755
|
case "action":
|
|
11756
|
+
buckets.actions.push(ext);
|
|
11541
11757
|
break;
|
|
11542
11758
|
case "formatter":
|
|
11543
11759
|
break;
|
|
@@ -11796,7 +12012,7 @@ function renderHuman(issues, ansi) {
|
|
|
11796
12012
|
tx(CHECK_TEXTS.issueRow, {
|
|
11797
12013
|
glyph: severityGlyph(row.severity, ansi),
|
|
11798
12014
|
analyzerId: ansi.dim(row.analyzerId.padEnd(analyzerWidth)),
|
|
11799
|
-
message: trimRedundantPath(row.message, row.primary)
|
|
12015
|
+
message: flattenMessage(trimRedundantPath(row.message, row.primary))
|
|
11800
12016
|
})
|
|
11801
12017
|
);
|
|
11802
12018
|
}
|
|
@@ -11850,6 +12066,9 @@ function trimRedundantPath(message, primary) {
|
|
|
11850
12066
|
if (!message.includes(needle)) return message;
|
|
11851
12067
|
return message.replace(needle, "");
|
|
11852
12068
|
}
|
|
12069
|
+
function flattenMessage(message) {
|
|
12070
|
+
return message.replace(/\n+/g, " ");
|
|
12071
|
+
}
|
|
11853
12072
|
|
|
11854
12073
|
// cli/commands/config.ts
|
|
11855
12074
|
import { existsSync as existsSync16 } from "fs";
|
|
@@ -15823,8 +16042,9 @@ function buildVirtualNode(extractor, emitted, emitter) {
|
|
|
15823
16042
|
if (emitted.frontmatter) node.frontmatter = emitted.frontmatter;
|
|
15824
16043
|
return node;
|
|
15825
16044
|
}
|
|
16045
|
+
var KNOWN_LINK_KINDS = ["invokes", "references", "mentions", "supersedes", "points"];
|
|
15826
16046
|
function validateLink(extractor, link, emitter) {
|
|
15827
|
-
const knownKinds =
|
|
16047
|
+
const knownKinds = KNOWN_LINK_KINDS;
|
|
15828
16048
|
if (!knownKinds.includes(link.kind)) {
|
|
15829
16049
|
const qualifiedId2 = `${extractor.pluginId}/${extractor.id}`;
|
|
15830
16050
|
emitter.emit(
|
|
@@ -15859,7 +16079,6 @@ function validateLink(extractor, link, emitter) {
|
|
|
15859
16079
|
const confidence = c ?? ConfidenceTier.MEDIUM;
|
|
15860
16080
|
return { ...link, confidence };
|
|
15861
16081
|
}
|
|
15862
|
-
var KNOWN_LINK_KINDS = ["invokes", "references", "mentions", "supersedes"];
|
|
15863
16082
|
function validateSignal(extractor, signal, emitter) {
|
|
15864
16083
|
const qualifiedId2 = qualifiedExtensionId(extractor.pluginId, extractor.id);
|
|
15865
16084
|
if (!Array.isArray(signal.candidates) || signal.candidates.length === 0) {
|
|
@@ -15978,12 +16197,87 @@ function recomputeExternalRefsCount(nodes, externalLinks, cachedPaths) {
|
|
|
15978
16197
|
});
|
|
15979
16198
|
source.externalRefs = refs;
|
|
15980
16199
|
}
|
|
15981
|
-
}
|
|
15982
|
-
var EXTERNAL_URL_SCHEME_RE = /^[a-z][a-z0-9+\-.]+:/i;
|
|
15983
|
-
var VIRTUAL_NODE_SCHEME_RE = /^mcp:\/\//i;
|
|
15984
|
-
function isExternalUrlLink(link) {
|
|
15985
|
-
if (VIRTUAL_NODE_SCHEME_RE.test(link.target)) return false;
|
|
15986
|
-
return EXTERNAL_URL_SCHEME_RE.test(link.target);
|
|
16200
|
+
}
|
|
16201
|
+
var EXTERNAL_URL_SCHEME_RE = /^[a-z][a-z0-9+\-.]+:/i;
|
|
16202
|
+
var VIRTUAL_NODE_SCHEME_RE = /^mcp:\/\//i;
|
|
16203
|
+
function isExternalUrlLink(link) {
|
|
16204
|
+
if (VIRTUAL_NODE_SCHEME_RE.test(link.target)) return false;
|
|
16205
|
+
return EXTERNAL_URL_SCHEME_RE.test(link.target);
|
|
16206
|
+
}
|
|
16207
|
+
|
|
16208
|
+
// kernel/orchestrator/action-projections.ts
|
|
16209
|
+
function runActionProjections(actions, nodes, links, emitter) {
|
|
16210
|
+
const contributions = [];
|
|
16211
|
+
const contributionErrors = [];
|
|
16212
|
+
const validators = loadSchemaValidators();
|
|
16213
|
+
for (const action of actions) {
|
|
16214
|
+
if (typeof action.project !== "function") continue;
|
|
16215
|
+
const qualifiedId2 = qualifiedExtensionId(action.pluginId, action.id);
|
|
16216
|
+
const declaredContributions = readDeclaredContributionRefs(action);
|
|
16217
|
+
const emitContribution = (nodePath, ref, payload) => {
|
|
16218
|
+
const declared = typeof ref === "object" && ref !== null ? declaredContributions.get(ref) : void 0;
|
|
16219
|
+
if (!declared) {
|
|
16220
|
+
const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUndeclaredRef, {
|
|
16221
|
+
extractorId: qualifiedId2,
|
|
16222
|
+
nodePath
|
|
16223
|
+
});
|
|
16224
|
+
emitExtensionError(emitter, qualifiedId2, nodePath, {
|
|
16225
|
+
phase: "emitContribution",
|
|
16226
|
+
reason: "undeclared-contribution-ref",
|
|
16227
|
+
message
|
|
16228
|
+
});
|
|
16229
|
+
contributionErrors.push({
|
|
16230
|
+
pluginId: action.pluginId,
|
|
16231
|
+
extensionId: action.id,
|
|
16232
|
+
nodePath,
|
|
16233
|
+
reason: "undeclared-contribution-ref",
|
|
16234
|
+
message,
|
|
16235
|
+
emittedAt: Date.now()
|
|
16236
|
+
});
|
|
16237
|
+
return;
|
|
16238
|
+
}
|
|
16239
|
+
const result = validators.validateContributionPayload(declared.slot, payload);
|
|
16240
|
+
if (!result.ok) {
|
|
16241
|
+
const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
|
|
16242
|
+
extractorId: qualifiedId2,
|
|
16243
|
+
contributionId: declared.id,
|
|
16244
|
+
nodePath,
|
|
16245
|
+
slot: declared.slot,
|
|
16246
|
+
errors: result.errors
|
|
16247
|
+
});
|
|
16248
|
+
emitExtensionError(emitter, qualifiedId2, nodePath, {
|
|
16249
|
+
phase: "emitContribution",
|
|
16250
|
+
contributionId: declared.id,
|
|
16251
|
+
slot: declared.slot,
|
|
16252
|
+
reason: result.errors,
|
|
16253
|
+
message
|
|
16254
|
+
});
|
|
16255
|
+
contributionErrors.push({
|
|
16256
|
+
pluginId: action.pluginId,
|
|
16257
|
+
extensionId: action.id,
|
|
16258
|
+
nodePath,
|
|
16259
|
+
reason: result.errors,
|
|
16260
|
+
message,
|
|
16261
|
+
contributionId: declared.id,
|
|
16262
|
+
slot: declared.slot,
|
|
16263
|
+
emittedAt: Date.now()
|
|
16264
|
+
});
|
|
16265
|
+
return;
|
|
16266
|
+
}
|
|
16267
|
+
contributions.push({
|
|
16268
|
+
pluginId: action.pluginId,
|
|
16269
|
+
extensionId: action.id,
|
|
16270
|
+
nodePath,
|
|
16271
|
+
contributionId: declared.id,
|
|
16272
|
+
slot: declared.slot,
|
|
16273
|
+
payload,
|
|
16274
|
+
emittedAt: Date.now()
|
|
16275
|
+
});
|
|
16276
|
+
};
|
|
16277
|
+
const ctx = { nodes, links, emitContribution };
|
|
16278
|
+
action.project(ctx);
|
|
16279
|
+
}
|
|
16280
|
+
return { contributions, contributionErrors };
|
|
15987
16281
|
}
|
|
15988
16282
|
|
|
15989
16283
|
// kernel/orchestrator/analyzers.ts
|
|
@@ -16775,6 +17069,7 @@ function buildNode(args2) {
|
|
|
16775
17069
|
externalRefsCount: 0,
|
|
16776
17070
|
frontmatter: args2.frontmatter
|
|
16777
17071
|
};
|
|
17072
|
+
if (args2.modifiedAtMs !== void 0) node.modifiedAtMs = args2.modifiedAtMs;
|
|
16778
17073
|
if (args2.encoder) {
|
|
16779
17074
|
node.tokens = countTokens(args2.encoder, args2.frontmatterRaw, args2.body);
|
|
16780
17075
|
}
|
|
@@ -16890,7 +17185,10 @@ function buildFreshNodeAndValidateFrontmatter(opts) {
|
|
|
16890
17185
|
frontmatter: opts.raw.frontmatter,
|
|
16891
17186
|
bodyHash: opts.bodyHash,
|
|
16892
17187
|
frontmatterHash: opts.frontmatterHash,
|
|
16893
|
-
encoder: opts.encoder
|
|
17188
|
+
encoder: opts.encoder,
|
|
17189
|
+
// Thread the walker's mtime through; `buildNode` only attaches it
|
|
17190
|
+
// when present, so virtual / walk()-without-stat sources stay absent.
|
|
17191
|
+
modifiedAtMs: opts.raw.modifiedAtMs
|
|
16894
17192
|
});
|
|
16895
17193
|
const frontmatterIssues = [];
|
|
16896
17194
|
if (opts.raw.parseIssues && opts.raw.parseIssues.length > 0) {
|
|
@@ -17324,6 +17622,13 @@ async function runScanInternal(_kernel, options) {
|
|
|
17324
17622
|
walked.frontmatterIssues
|
|
17325
17623
|
);
|
|
17326
17624
|
mergeAnalyzerEmissions(walked, analyzerResult, exts.analyzers);
|
|
17625
|
+
const projectionResult = runActionProjections(
|
|
17626
|
+
exts.actions ?? [],
|
|
17627
|
+
walked.nodes,
|
|
17628
|
+
walked.internalLinks,
|
|
17629
|
+
emitter
|
|
17630
|
+
);
|
|
17631
|
+
mergeActionProjections(walked, projectionResult, exts.actions);
|
|
17327
17632
|
const issues = analyzerResult.issues;
|
|
17328
17633
|
const silenced = options.ignoreFilter ? (path) => options.ignoreFilter.ignores(path) : void 0;
|
|
17329
17634
|
const renameOps = prior ? detectRenamesAndOrphans(prior, walked.nodes, issues, silenced) : [];
|
|
@@ -17432,6 +17737,16 @@ function mergeAnalyzerEmissions(walked, analyzerResult, analyzers) {
|
|
|
17432
17737
|
}
|
|
17433
17738
|
}
|
|
17434
17739
|
}
|
|
17740
|
+
function mergeActionProjections(walked, projectionResult, actions) {
|
|
17741
|
+
for (const c of projectionResult.contributions) walked.contributions.push(c);
|
|
17742
|
+
for (const e of projectionResult.contributionErrors) walked.contributionErrors.push(e);
|
|
17743
|
+
for (const action of actions ?? []) {
|
|
17744
|
+
if (action.ui === void 0 || typeof action.project !== "function") continue;
|
|
17745
|
+
for (const node of walked.nodes) {
|
|
17746
|
+
walked.freshlyRunTuples.add(`${action.pluginId}\0${action.id}\0${node.path}`);
|
|
17747
|
+
}
|
|
17748
|
+
}
|
|
17749
|
+
}
|
|
17435
17750
|
function buildScanStats(walked, issues, start) {
|
|
17436
17751
|
return {
|
|
17437
17752
|
// `filesSkipped` is "files walked but not classified by any
|
|
@@ -20336,6 +20651,17 @@ var PLUGINS_TEXTS = {
|
|
|
20336
20651
|
qualifiedIdNotFoundHint: "Run `sm plugins list` to see what each plugin ships.",
|
|
20337
20652
|
qualifiedIdUnknownPlugin: "{{glyph}} Qualified extension id references unknown plugin: {{pluginId}}\n {{hint}}\n",
|
|
20338
20653
|
qualifiedIdUnknownPluginHint: "Run `sm plugins list` for known plugin ids.",
|
|
20654
|
+
// --- verb-shape redirects (show is extension-only; list is plugin-only) ---
|
|
20655
|
+
// `sm plugins show` takes a qualified `<plugin>/<ext>` id and renders a
|
|
20656
|
+
// single extension. A bare plugin id is the wrong granularity, redirect
|
|
20657
|
+
// to `sm plugins list <id>`, which renders the whole plugin.
|
|
20658
|
+
showBareId: '{{glyph}} `sm plugins show` needs a qualified `<plugin>/<ext>` id; "{{id}}" is a plugin.\n {{hint}}\n',
|
|
20659
|
+
showBareIdHint: "Run `sm plugins list {{id}}` for the plugin and its extensions, then `sm plugins show {{id}}/<ext>` for one.",
|
|
20660
|
+
// `sm plugins list <id>` takes a bare plugin id. A qualified
|
|
20661
|
+
// `<plugin>/<ext>` id targets a single extension, redirect to
|
|
20662
|
+
// `sm plugins show`.
|
|
20663
|
+
listQualifiedId: "{{glyph}} `sm plugins list` takes a plugin id, not a qualified `<plugin>/<ext>` id: {{id}}\n {{hint}}\n",
|
|
20664
|
+
listQualifiedIdHint: "Run `sm plugins show {{id}}` for that extension, or `sm plugins list {{pluginId}}` for the whole plugin.",
|
|
20339
20665
|
// Spec § A.10, `applicableKinds` filter on Extractors. When an extractor
|
|
20340
20666
|
// declares a kind that no installed Provider emits, the load succeeds
|
|
20341
20667
|
// (the Provider may arrive later) but `sm plugins doctor` surfaces a
|
|
@@ -20469,7 +20795,14 @@ var PLUGINS_TEXTS = {
|
|
|
20469
20795
|
* the visible output stay in sync.
|
|
20470
20796
|
*/
|
|
20471
20797
|
pluginSubIndent: " ",
|
|
20472
|
-
|
|
20798
|
+
/**
|
|
20799
|
+
* Lifecycle tag appended to an extension name in list / show rows
|
|
20800
|
+
* when the manifest declares a non-default `stability` (anything but
|
|
20801
|
+
* `stable`). Inherits the surrounding line's color; `stable`
|
|
20802
|
+
* (declared or defaulted) renders no tag.
|
|
20803
|
+
*/
|
|
20804
|
+
stabilityTag: " ({{stability}})",
|
|
20805
|
+
listTipShow: "\nTip: `sm plugins list <id>` for a plugin's extensions (kinds, versions, per-extension status), `sm plugins show <plugin>/<ext>` for one extension.\n",
|
|
20473
20806
|
/** Show command, built-in header (no version row, no path). */
|
|
20474
20807
|
detailHeaderBuiltIn: " {{glyph}} {{id}} {{source}} {{count}} extension{{plural}}\n",
|
|
20475
20808
|
/**
|
|
@@ -20621,18 +20954,60 @@ function extensionRowFromBuiltIn(ext, plugin, resolveEnabled) {
|
|
|
20621
20954
|
id: ext.id,
|
|
20622
20955
|
kind: ext.kind,
|
|
20623
20956
|
version: ext.version,
|
|
20624
|
-
enabled: resolveEnabled(qualifiedExtensionId(plugin.id, ext.id)),
|
|
20957
|
+
enabled: resolveEnabled(qualifiedExtensionId(plugin.id, ext.id), installedDefaultEnabled(ext.stability)),
|
|
20625
20958
|
description: ext.description ?? ""
|
|
20626
20959
|
};
|
|
20960
|
+
if (ext.stability !== void 0) row.stability = ext.stability;
|
|
20627
20961
|
if (ext.entry !== void 0) row.entry = ext.entry;
|
|
20628
20962
|
return row;
|
|
20629
20963
|
}
|
|
20964
|
+
function withStabilityTag(name, stability) {
|
|
20965
|
+
if (!stability || stability === "stable") return name;
|
|
20966
|
+
return name + tx(PLUGINS_TEXTS.stabilityTag, { stability: sanitizeForTerminal(stability) });
|
|
20967
|
+
}
|
|
20630
20968
|
function omitModule(key, value) {
|
|
20631
20969
|
if (key !== "module") return value;
|
|
20632
20970
|
if (value === null || typeof value !== "object") return value;
|
|
20633
20971
|
const tag = value[Symbol.toStringTag];
|
|
20634
20972
|
return tag === "Module" ? void 0 : value;
|
|
20635
20973
|
}
|
|
20974
|
+
function pluginCatalogue(plugins) {
|
|
20975
|
+
const out = [];
|
|
20976
|
+
for (const plugin of builtInPlugins) {
|
|
20977
|
+
out.push({ id: plugin.id, extensionIds: plugin.extensions.map((e) => e.id) });
|
|
20978
|
+
}
|
|
20979
|
+
for (const p of plugins) {
|
|
20980
|
+
out.push({ id: p.id, extensionIds: p.extensions?.map((e) => e.id) ?? [] });
|
|
20981
|
+
}
|
|
20982
|
+
return out;
|
|
20983
|
+
}
|
|
20984
|
+
function parseQualifiedExtensionId(id, catalogue) {
|
|
20985
|
+
const [pluginId, extId, ...rest] = id.split("/");
|
|
20986
|
+
if (!pluginId || !extId || rest.length > 0) return { ok: false, reason: "malformed" };
|
|
20987
|
+
const plugin = catalogue.find((p) => p.id === pluginId);
|
|
20988
|
+
if (!plugin) return { ok: false, reason: "unknown-plugin", pluginId };
|
|
20989
|
+
if (!plugin.extensionIds.includes(extId)) {
|
|
20990
|
+
return { ok: false, reason: "unknown-extension", pluginId, extId };
|
|
20991
|
+
}
|
|
20992
|
+
return { ok: true, pluginId, extId };
|
|
20993
|
+
}
|
|
20994
|
+
function renderQualifiedIdError(result, rawId, ansi) {
|
|
20995
|
+
const glyph = ansi.red(PLUGINS_TEXTS.rowGlyphOff);
|
|
20996
|
+
if (result.reason === "unknown-extension") {
|
|
20997
|
+
return tx(PLUGINS_TEXTS.qualifiedIdNotFound, {
|
|
20998
|
+
glyph,
|
|
20999
|
+
id: sanitizeForTerminal(rawId),
|
|
21000
|
+
pluginId: sanitizeForTerminal(result.pluginId ?? ""),
|
|
21001
|
+
extId: sanitizeForTerminal(result.extId ?? ""),
|
|
21002
|
+
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdNotFoundHint)
|
|
21003
|
+
});
|
|
21004
|
+
}
|
|
21005
|
+
return tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
|
|
21006
|
+
glyph,
|
|
21007
|
+
pluginId: sanitizeForTerminal(result.reason === "unknown-plugin" ? result.pluginId ?? rawId : rawId),
|
|
21008
|
+
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
|
|
21009
|
+
});
|
|
21010
|
+
}
|
|
20636
21011
|
function wrapText(text, maxWidth) {
|
|
20637
21012
|
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
20638
21013
|
if (words.length === 0) return [];
|
|
@@ -20656,14 +21031,24 @@ var PluginsListCommand = class extends SmCommand {
|
|
|
20656
21031
|
static paths = [["plugins", "list"]];
|
|
20657
21032
|
static usage = Command22.Usage({
|
|
20658
21033
|
category: "Plugins",
|
|
20659
|
-
description: "List discovered plugins
|
|
20660
|
-
details:
|
|
21034
|
+
description: "List discovered plugins, or one plugin's extensions.",
|
|
21035
|
+
details: `
|
|
21036
|
+
No id: scans <cwd>/.skill-map/plugins (or --plugin-dir <path>) and
|
|
21037
|
+
lists every plugin (built-in + user) with status, one row each.
|
|
21038
|
+
With a bare plugin id: renders that plugin's manifest and its
|
|
21039
|
+
extensions (kind / version / per-extension status). A qualified
|
|
21040
|
+
\`<plugin>/<ext>\` id is rejected with a redirect to \`sm plugins show\`.
|
|
21041
|
+
`
|
|
20661
21042
|
});
|
|
21043
|
+
id = Option21.String({ required: false });
|
|
20662
21044
|
pluginDir = Option21.String("--plugin-dir", { required: false });
|
|
20663
21045
|
async run() {
|
|
20664
21046
|
const plugins = await loadAll({ pluginDir: this.pluginDir });
|
|
20665
21047
|
const resolveEnabled = await buildResolver();
|
|
20666
21048
|
const builtIns2 = builtInRows(resolveEnabled);
|
|
21049
|
+
if (this.id !== void 0) {
|
|
21050
|
+
return this.renderPluginDetailById(this.id, builtIns2, plugins);
|
|
21051
|
+
}
|
|
20667
21052
|
if (this.json) {
|
|
20668
21053
|
this.printer.data(
|
|
20669
21054
|
JSON.stringify({ builtIns: builtIns2, plugins }, omitModule, 2) + "\n"
|
|
@@ -20675,24 +21060,68 @@ var PluginsListCommand = class extends SmCommand {
|
|
|
20675
21060
|
return ExitCode.Ok;
|
|
20676
21061
|
}
|
|
20677
21062
|
const ansi = this.ansiFor("stdout");
|
|
20678
|
-
this.printer.data(
|
|
21063
|
+
this.printer.data(renderIndexHuman(builtIns2, plugins, resolveEnabled, ansi));
|
|
21064
|
+
return ExitCode.Ok;
|
|
21065
|
+
}
|
|
21066
|
+
/**
|
|
21067
|
+
* `sm plugins list <id>`, render one plugin's full detail. A qualified
|
|
21068
|
+
* `<plugin>/<ext>` id is the wrong granularity for `list` (it targets a
|
|
21069
|
+
* single extension), redirect to `sm plugins show`. A bare id that
|
|
21070
|
+
* matches no plugin is a NotFound.
|
|
21071
|
+
*/
|
|
21072
|
+
renderPluginDetailById(id, builtIns2, plugins) {
|
|
21073
|
+
const stderrAnsi = this.ansiFor("stderr");
|
|
21074
|
+
if (id.includes("/")) {
|
|
21075
|
+
const pluginId = id.split("/")[0] ?? id;
|
|
21076
|
+
this.printer.error(
|
|
21077
|
+
tx(PLUGINS_TEXTS.listQualifiedId, {
|
|
21078
|
+
glyph: stderrAnsi.red(PLUGINS_TEXTS.rowGlyphOff),
|
|
21079
|
+
id: sanitizeForTerminal(id),
|
|
21080
|
+
hint: stderrAnsi.dim(
|
|
21081
|
+
tx(PLUGINS_TEXTS.listQualifiedIdHint, {
|
|
21082
|
+
id: sanitizeForTerminal(id),
|
|
21083
|
+
pluginId: sanitizeForTerminal(pluginId)
|
|
21084
|
+
})
|
|
21085
|
+
)
|
|
21086
|
+
})
|
|
21087
|
+
);
|
|
21088
|
+
return ExitCode.Error;
|
|
21089
|
+
}
|
|
21090
|
+
const builtIn = builtIns2.find((b) => b.id === id);
|
|
21091
|
+
const match = plugins.find((p) => p.id === id);
|
|
21092
|
+
if (!builtIn && !match) {
|
|
21093
|
+
this.printer.error(
|
|
21094
|
+
tx(PLUGINS_TEXTS.pluginNotFound, {
|
|
21095
|
+
glyph: stderrAnsi.red(PLUGINS_TEXTS.rowGlyphOff),
|
|
21096
|
+
id: sanitizeForTerminal(id),
|
|
21097
|
+
hint: stderrAnsi.dim(PLUGINS_TEXTS.pluginNotFoundHint)
|
|
21098
|
+
})
|
|
21099
|
+
);
|
|
21100
|
+
return ExitCode.NotFound;
|
|
21101
|
+
}
|
|
21102
|
+
if (this.json) {
|
|
21103
|
+
const payload = builtIn ?? match;
|
|
21104
|
+
this.printer.data(JSON.stringify(payload, omitModule, 2) + "\n");
|
|
21105
|
+
return ExitCode.Ok;
|
|
21106
|
+
}
|
|
21107
|
+
const ansi = this.ansiFor("stdout");
|
|
21108
|
+
const text = builtIn ? renderBuiltInDetail(builtIn, ansi) : renderPluginDetail(match, ansi);
|
|
21109
|
+
this.printer.data(text);
|
|
20679
21110
|
return ExitCode.Ok;
|
|
20680
21111
|
}
|
|
20681
21112
|
};
|
|
20682
|
-
function
|
|
21113
|
+
function renderIndexHuman(builtIns2, plugins, resolveEnabled, ansi) {
|
|
20683
21114
|
const rows = [
|
|
20684
|
-
...builtIns2.map(
|
|
20685
|
-
...plugins.map((p) =>
|
|
21115
|
+
...builtIns2.map(builtInToIndexRow),
|
|
21116
|
+
...plugins.map((p) => pluginToIndexRow(p, resolveEnabled))
|
|
20686
21117
|
];
|
|
20687
21118
|
const idWidth = Math.max(...rows.map((r) => r.id.length));
|
|
20688
|
-
const countWidth = Math.max(
|
|
20689
|
-
...rows.map((r) => String(r.names.length).length)
|
|
20690
|
-
);
|
|
21119
|
+
const countWidth = Math.max(...rows.map((r) => String(r.extCount).length));
|
|
20691
21120
|
const lines = [];
|
|
20692
21121
|
for (const row of rows) {
|
|
20693
21122
|
const glyph = row.enabled ? ansi.green(PLUGINS_TEXTS.rowGlyphOk) : ansi.red(PLUGINS_TEXTS.rowGlyphOff);
|
|
20694
21123
|
const idCol = row.id.padEnd(idWidth);
|
|
20695
|
-
const countCol = String(row.
|
|
21124
|
+
const countCol = String(row.extCount).padStart(countWidth);
|
|
20696
21125
|
lines.push(
|
|
20697
21126
|
tx(PLUGINS_TEXTS.pluginRow, {
|
|
20698
21127
|
glyph,
|
|
@@ -20701,195 +21130,34 @@ function renderListHuman(builtIns2, plugins, resolveEnabled, ansi) {
|
|
|
20701
21130
|
source: ansi.dim(row.source)
|
|
20702
21131
|
})
|
|
20703
21132
|
);
|
|
20704
|
-
const indent = PLUGINS_TEXTS.pluginSubIndent;
|
|
20705
21133
|
if (row.reason) {
|
|
20706
|
-
lines.push(`${
|
|
20707
|
-
} else if (row.names.length > 0) {
|
|
20708
|
-
for (const wrapped of wrapNames(row.names, indent, 76)) {
|
|
20709
|
-
lines.push(`${indent}${ansi.dim(wrapped)}`);
|
|
20710
|
-
}
|
|
21134
|
+
lines.push(`${PLUGINS_TEXTS.pluginSubIndent}${ansi.dim(row.reason)}`);
|
|
20711
21135
|
}
|
|
20712
21136
|
}
|
|
20713
21137
|
return lines.join("\n") + "\n" + PLUGINS_TEXTS.listTipShow;
|
|
20714
21138
|
}
|
|
20715
|
-
function
|
|
20716
|
-
const names = b.extensions.map(
|
|
20717
|
-
(e) => e.enabled ? e.id : `${PLUGINS_TEXTS.rowGlyphOff} ${e.id}`
|
|
20718
|
-
);
|
|
21139
|
+
function builtInToIndexRow(b) {
|
|
20719
21140
|
return {
|
|
20720
21141
|
id: b.id,
|
|
20721
21142
|
enabled: b.enabled,
|
|
20722
21143
|
source: PLUGINS_TEXTS.sourceBuiltIn,
|
|
20723
|
-
|
|
21144
|
+
extCount: b.extensions.length
|
|
20724
21145
|
};
|
|
20725
21146
|
}
|
|
20726
|
-
function
|
|
21147
|
+
function pluginToIndexRow(p, resolveEnabled) {
|
|
20727
21148
|
const isLoaded = p.status === "enabled";
|
|
20728
21149
|
const extensions = p.extensions ?? [];
|
|
20729
|
-
const
|
|
20730
|
-
const
|
|
20731
|
-
const safeId = sanitizeForTerminal(e.id);
|
|
20732
|
-
return resolveEnabled(qualifiedExtensionId(p.id, e.id)) ? safeId : `${PLUGINS_TEXTS.rowGlyphOff} ${safeId}`;
|
|
20733
|
-
});
|
|
21150
|
+
const extEnabled = (e) => resolveEnabled(qualifiedExtensionId(p.id, e.id), installedDefaultEnabled(e.stability));
|
|
21151
|
+
const enabled = isLoaded ? extensions.length === 0 || extensions.some((e) => extEnabled(e)) : false;
|
|
20734
21152
|
const reason = p.status === "enabled" ? void 0 : sanitizeForTerminal(p.reason ?? "") || void 0;
|
|
20735
21153
|
return {
|
|
20736
21154
|
id: sanitizeForTerminal(p.id),
|
|
20737
21155
|
enabled,
|
|
20738
21156
|
source: PLUGINS_TEXTS.sourceUser,
|
|
20739
|
-
|
|
21157
|
+
extCount: extensions.length,
|
|
20740
21158
|
reason
|
|
20741
21159
|
};
|
|
20742
21160
|
}
|
|
20743
|
-
function wrapNames(names, indent, maxWidth) {
|
|
20744
|
-
const out = [];
|
|
20745
|
-
const sep8 = ", ";
|
|
20746
|
-
let current = "";
|
|
20747
|
-
for (const name of names) {
|
|
20748
|
-
const candidate = current === "" ? name : `${current}${sep8}${name}`;
|
|
20749
|
-
if (indent.length + candidate.length > maxWidth && current !== "") {
|
|
20750
|
-
out.push(`${current},`);
|
|
20751
|
-
current = name;
|
|
20752
|
-
} else {
|
|
20753
|
-
current = candidate;
|
|
20754
|
-
}
|
|
20755
|
-
}
|
|
20756
|
-
if (current !== "") out.push(current);
|
|
20757
|
-
return out;
|
|
20758
|
-
}
|
|
20759
|
-
|
|
20760
|
-
// cli/commands/plugins/show.ts
|
|
20761
|
-
import { Command as Command23, Option as Option22 } from "clipanion";
|
|
20762
|
-
var PluginsShowCommand = class extends SmCommand {
|
|
20763
|
-
static paths = [["plugins", "show"]];
|
|
20764
|
-
static usage = Command23.Usage({
|
|
20765
|
-
category: "Plugins",
|
|
20766
|
-
description: "Show a single plugin's manifest + loaded extensions.",
|
|
20767
|
-
details: `
|
|
20768
|
-
Accepts a plugin id (\`core\`, \`claude\`, \`my-plugin\`)
|
|
20769
|
-
or a qualified extension id (\`core/<ext-id>\`,
|
|
20770
|
-
\`<plugin>/<ext-id>\`). When given a qualified id, validates the
|
|
20771
|
-
extension exists and renders a single-extension detail block.
|
|
20772
|
-
The bare form renders the parent plugin's detail with per-extension
|
|
20773
|
-
status. The same id shapes \`sm plugins enable\` and
|
|
20774
|
-
\`sm plugins disable\` accept resolve cleanly here too.
|
|
20775
|
-
`
|
|
20776
|
-
});
|
|
20777
|
-
id = Option22.String({ required: true });
|
|
20778
|
-
pluginDir = Option22.String("--plugin-dir", { required: false });
|
|
20779
|
-
async run() {
|
|
20780
|
-
const plugins = await loadAll({ pluginDir: this.pluginDir });
|
|
20781
|
-
const resolveEnabled = await buildResolver();
|
|
20782
|
-
const builtIns2 = builtInRows(resolveEnabled);
|
|
20783
|
-
const stderrAnsi = this.ansiFor("stderr");
|
|
20784
|
-
const lookupResult = resolveShowLookupId(this.id, builtIns2, plugins, stderrAnsi);
|
|
20785
|
-
if ("error" in lookupResult) {
|
|
20786
|
-
this.printer.error(lookupResult.error);
|
|
20787
|
-
return ExitCode.NotFound;
|
|
20788
|
-
}
|
|
20789
|
-
const { pluginId, extId } = lookupResult;
|
|
20790
|
-
const builtIn = builtIns2.find((b) => b.id === pluginId);
|
|
20791
|
-
const match = plugins.find((p) => p.id === pluginId);
|
|
20792
|
-
if (!builtIn && !match) {
|
|
20793
|
-
this.printer.error(
|
|
20794
|
-
tx(PLUGINS_TEXTS.pluginNotFound, {
|
|
20795
|
-
glyph: stderrAnsi.red("\u2715"),
|
|
20796
|
-
id: sanitizeForTerminal(this.id),
|
|
20797
|
-
hint: stderrAnsi.dim(PLUGINS_TEXTS.pluginNotFoundHint)
|
|
20798
|
-
})
|
|
20799
|
-
);
|
|
20800
|
-
return ExitCode.NotFound;
|
|
20801
|
-
}
|
|
20802
|
-
if (extId !== void 0) {
|
|
20803
|
-
return this.renderExtensionDetail({ extId, pluginId, builtIn, match });
|
|
20804
|
-
}
|
|
20805
|
-
if (this.json) {
|
|
20806
|
-
const payload = builtIn ?? match;
|
|
20807
|
-
this.printer.data(JSON.stringify(payload, omitModule, 2) + "\n");
|
|
20808
|
-
return ExitCode.Ok;
|
|
20809
|
-
}
|
|
20810
|
-
const ansi = this.ansiFor("stdout");
|
|
20811
|
-
const text = builtIn ? renderBuiltInDetail(builtIn, ansi) : renderPluginDetail(match, ansi);
|
|
20812
|
-
this.printer.data(text);
|
|
20813
|
-
return ExitCode.Ok;
|
|
20814
|
-
}
|
|
20815
|
-
/**
|
|
20816
|
-
* Render the single-extension detail block, the path taken when the
|
|
20817
|
-
* user supplies a qualified `<plugin>/<ext>` id. `--json` emits the
|
|
20818
|
-
* single extension row (no surrounding plugin envelope) so tooling
|
|
20819
|
-
* can pipe straight into `jq`; human mode renders a focused header
|
|
20820
|
-
* plus a Kind / Version / Stability / Description / Preconditions /
|
|
20821
|
-
* Entry field block.
|
|
20822
|
-
*/
|
|
20823
|
-
renderExtensionDetail(args2) {
|
|
20824
|
-
const { extId, pluginId, builtIn, match } = args2;
|
|
20825
|
-
const ansi = this.ansiFor("stdout");
|
|
20826
|
-
if (builtIn) {
|
|
20827
|
-
const ext = builtIn.extensions.find((e) => e.id === extId);
|
|
20828
|
-
if (!ext) return ExitCode.NotFound;
|
|
20829
|
-
if (this.json) {
|
|
20830
|
-
this.printer.data(JSON.stringify({ pluginId, ...ext }, omitModule, 2) + "\n");
|
|
20831
|
-
return ExitCode.Ok;
|
|
20832
|
-
}
|
|
20833
|
-
this.printer.data(renderBuiltInExtensionDetail(pluginId, ext, ansi));
|
|
20834
|
-
return ExitCode.Ok;
|
|
20835
|
-
}
|
|
20836
|
-
const userExt = match?.extensions?.find((e) => e.id === extId);
|
|
20837
|
-
if (!userExt) return ExitCode.NotFound;
|
|
20838
|
-
if (this.json) {
|
|
20839
|
-
this.printer.data(JSON.stringify(userExt, omitModule, 2) + "\n");
|
|
20840
|
-
return ExitCode.Ok;
|
|
20841
|
-
}
|
|
20842
|
-
this.printer.data(renderUserExtensionDetail(pluginId, userExt, ansi));
|
|
20843
|
-
return ExitCode.Ok;
|
|
20844
|
-
}
|
|
20845
|
-
};
|
|
20846
|
-
function resolveShowLookupId(id, builtIns2, plugins, ansi) {
|
|
20847
|
-
if (!id.includes("/")) return { pluginId: id };
|
|
20848
|
-
const parsed = parseQualifiedId(id);
|
|
20849
|
-
if ("error" in parsed) return { error: malformedQualifiedError(id, ansi) };
|
|
20850
|
-
const { pluginId, extId } = parsed;
|
|
20851
|
-
const knownExts = collectKnownExtensions(pluginId, builtIns2, plugins);
|
|
20852
|
-
if (knownExts === null) return { error: unknownPluginError(pluginId, ansi) };
|
|
20853
|
-
if (!knownExts.includes(extId)) {
|
|
20854
|
-
return { error: unknownExtensionError(id, pluginId, extId, ansi) };
|
|
20855
|
-
}
|
|
20856
|
-
return { pluginId, extId };
|
|
20857
|
-
}
|
|
20858
|
-
function parseQualifiedId(id) {
|
|
20859
|
-
const [pluginId, extId, ...rest] = id.split("/");
|
|
20860
|
-
if (!pluginId || !extId || rest.length > 0) return { error: true };
|
|
20861
|
-
return { pluginId, extId };
|
|
20862
|
-
}
|
|
20863
|
-
function collectKnownExtensions(pluginId, builtIns2, plugins) {
|
|
20864
|
-
const builtIn = builtIns2.find((b) => b.id === pluginId);
|
|
20865
|
-
if (builtIn) return builtIn.extensions.map((e) => e.id);
|
|
20866
|
-
const userPlugin = plugins.find((p) => p.id === pluginId);
|
|
20867
|
-
if (userPlugin) return userPlugin.extensions?.map((e) => e.id) ?? [];
|
|
20868
|
-
return null;
|
|
20869
|
-
}
|
|
20870
|
-
function malformedQualifiedError(id, ansi) {
|
|
20871
|
-
return tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
|
|
20872
|
-
glyph: ansi.red("\u2715"),
|
|
20873
|
-
pluginId: sanitizeForTerminal(id),
|
|
20874
|
-
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
|
|
20875
|
-
});
|
|
20876
|
-
}
|
|
20877
|
-
function unknownPluginError(pluginId, ansi) {
|
|
20878
|
-
return tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
|
|
20879
|
-
glyph: ansi.red("\u2715"),
|
|
20880
|
-
pluginId: sanitizeForTerminal(pluginId),
|
|
20881
|
-
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
|
|
20882
|
-
});
|
|
20883
|
-
}
|
|
20884
|
-
function unknownExtensionError(id, pluginId, extId, ansi) {
|
|
20885
|
-
return tx(PLUGINS_TEXTS.qualifiedIdNotFound, {
|
|
20886
|
-
glyph: ansi.red("\u2715"),
|
|
20887
|
-
id: sanitizeForTerminal(id),
|
|
20888
|
-
pluginId: sanitizeForTerminal(pluginId),
|
|
20889
|
-
extId: sanitizeForTerminal(extId),
|
|
20890
|
-
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdNotFoundHint)
|
|
20891
|
-
});
|
|
20892
|
-
}
|
|
20893
21161
|
function kindIndex(kind) {
|
|
20894
21162
|
const idx = EXTENSION_KINDS.indexOf(kind);
|
|
20895
21163
|
return idx === -1 ? EXTENSION_KINDS.length : idx;
|
|
@@ -20908,7 +21176,7 @@ function renderBuiltInDetail(b, ansi) {
|
|
|
20908
21176
|
const items = sorted.map((ext) => ({
|
|
20909
21177
|
glyph: ext.enabled ? ansi.green(PLUGINS_TEXTS.rowGlyphOk) : ansi.red(PLUGINS_TEXTS.rowGlyphOff),
|
|
20910
21178
|
kind: ext.kind,
|
|
20911
|
-
name: `${b.id}/${ext.id}
|
|
21179
|
+
name: withStabilityTag(`${b.id}/${ext.id}`, ext.stability)
|
|
20912
21180
|
}));
|
|
20913
21181
|
return tx(PLUGINS_TEXTS.detailHeaderBuiltIn, {
|
|
20914
21182
|
glyph,
|
|
@@ -20987,7 +21255,7 @@ function collectPluginExtensionItems(match, ansi) {
|
|
|
20987
21255
|
// status header above (✕ on the row).
|
|
20988
21256
|
glyph: ansi.green(PLUGINS_TEXTS.rowGlyphOk),
|
|
20989
21257
|
kind: sanitizeForTerminal(ext.kind),
|
|
20990
|
-
name: `${safePluginId}/${safeExtId}`,
|
|
21258
|
+
name: withStabilityTag(`${safePluginId}/${safeExtId}`, ext.stability),
|
|
20991
21259
|
version: sanitizeForTerminal(ext.version)
|
|
20992
21260
|
};
|
|
20993
21261
|
});
|
|
@@ -21013,6 +21281,83 @@ function renderExtensionItems(items) {
|
|
|
21013
21281
|
}
|
|
21014
21282
|
return out.join("");
|
|
21015
21283
|
}
|
|
21284
|
+
|
|
21285
|
+
// cli/commands/plugins/show.ts
|
|
21286
|
+
import { Command as Command23, Option as Option22 } from "clipanion";
|
|
21287
|
+
var PluginsShowCommand = class extends SmCommand {
|
|
21288
|
+
static paths = [["plugins", "show"]];
|
|
21289
|
+
static usage = Command23.Usage({
|
|
21290
|
+
category: "Plugins",
|
|
21291
|
+
description: "Show a single extension's detail.",
|
|
21292
|
+
details: `
|
|
21293
|
+
Accepts a qualified extension id (\`core/<ext-id>\`,
|
|
21294
|
+
\`<plugin>/<ext-id>\`) and renders a single-extension detail block
|
|
21295
|
+
(Kind / Version / Stability / Description / Preconditions / Entry).
|
|
21296
|
+
A bare plugin id is rejected with a redirect to
|
|
21297
|
+
\`sm plugins list <id>\`, which renders the whole plugin. The same
|
|
21298
|
+
qualified id shape \`sm plugins enable\` and \`sm plugins disable\`
|
|
21299
|
+
accept resolves cleanly here too.
|
|
21300
|
+
`
|
|
21301
|
+
});
|
|
21302
|
+
id = Option22.String({ required: true });
|
|
21303
|
+
pluginDir = Option22.String("--plugin-dir", { required: false });
|
|
21304
|
+
async run() {
|
|
21305
|
+
const plugins = await loadAll({ pluginDir: this.pluginDir });
|
|
21306
|
+
const resolveEnabled = await buildResolver();
|
|
21307
|
+
const builtIns2 = builtInRows(resolveEnabled);
|
|
21308
|
+
const stderrAnsi = this.ansiFor("stderr");
|
|
21309
|
+
if (!this.id.includes("/")) {
|
|
21310
|
+
this.printer.error(
|
|
21311
|
+
tx(PLUGINS_TEXTS.showBareId, {
|
|
21312
|
+
glyph: stderrAnsi.red(PLUGINS_TEXTS.rowGlyphOff),
|
|
21313
|
+
id: sanitizeForTerminal(this.id),
|
|
21314
|
+
hint: stderrAnsi.dim(
|
|
21315
|
+
tx(PLUGINS_TEXTS.showBareIdHint, { id: sanitizeForTerminal(this.id) })
|
|
21316
|
+
)
|
|
21317
|
+
})
|
|
21318
|
+
);
|
|
21319
|
+
return ExitCode.Error;
|
|
21320
|
+
}
|
|
21321
|
+
const parsed = parseQualifiedExtensionId(this.id, pluginCatalogue(plugins));
|
|
21322
|
+
if (!parsed.ok) {
|
|
21323
|
+
this.printer.error(renderQualifiedIdError(parsed, this.id, stderrAnsi));
|
|
21324
|
+
return ExitCode.NotFound;
|
|
21325
|
+
}
|
|
21326
|
+
const { pluginId, extId } = parsed;
|
|
21327
|
+
const builtIn = builtIns2.find((b) => b.id === pluginId);
|
|
21328
|
+
const match = plugins.find((p) => p.id === pluginId);
|
|
21329
|
+
return this.renderExtensionDetail({ extId, pluginId, builtIn, match });
|
|
21330
|
+
}
|
|
21331
|
+
/**
|
|
21332
|
+
* Render the single-extension detail block. `--json` emits the single
|
|
21333
|
+
* extension row (no surrounding plugin envelope) so tooling can pipe
|
|
21334
|
+
* straight into `jq`; human mode renders a focused header plus a
|
|
21335
|
+
* Kind / Version / Stability / Description / Preconditions / Entry
|
|
21336
|
+
* field block.
|
|
21337
|
+
*/
|
|
21338
|
+
renderExtensionDetail(args2) {
|
|
21339
|
+
const { extId, pluginId, builtIn, match } = args2;
|
|
21340
|
+
const ansi = this.ansiFor("stdout");
|
|
21341
|
+
if (builtIn) {
|
|
21342
|
+
const ext = builtIn.extensions.find((e) => e.id === extId);
|
|
21343
|
+
if (!ext) return ExitCode.NotFound;
|
|
21344
|
+
if (this.json) {
|
|
21345
|
+
this.printer.data(JSON.stringify({ pluginId, ...ext }, omitModule, 2) + "\n");
|
|
21346
|
+
return ExitCode.Ok;
|
|
21347
|
+
}
|
|
21348
|
+
this.printer.data(renderBuiltInExtensionDetail(pluginId, ext, ansi));
|
|
21349
|
+
return ExitCode.Ok;
|
|
21350
|
+
}
|
|
21351
|
+
const userExt = match?.extensions?.find((e) => e.id === extId);
|
|
21352
|
+
if (!userExt) return ExitCode.NotFound;
|
|
21353
|
+
if (this.json) {
|
|
21354
|
+
this.printer.data(JSON.stringify(userExt, omitModule, 2) + "\n");
|
|
21355
|
+
return ExitCode.Ok;
|
|
21356
|
+
}
|
|
21357
|
+
this.printer.data(renderUserExtensionDetail(pluginId, userExt, ansi));
|
|
21358
|
+
return ExitCode.Ok;
|
|
21359
|
+
}
|
|
21360
|
+
};
|
|
21016
21361
|
function renderBuiltInExtensionDetail(pluginId, ext, ansi) {
|
|
21017
21362
|
const glyph = ext.enabled ? ansi.green(PLUGINS_TEXTS.rowGlyphOk) : ansi.red(PLUGINS_TEXTS.rowGlyphOff);
|
|
21018
21363
|
const header = tx(PLUGINS_TEXTS.detailHeaderExtensionBuiltIn, {
|
|
@@ -21021,6 +21366,7 @@ function renderBuiltInExtensionDetail(pluginId, ext, ansi) {
|
|
|
21021
21366
|
source: ansi.dim(PLUGINS_TEXTS.sourceBuiltIn)
|
|
21022
21367
|
});
|
|
21023
21368
|
const meta = { kind: ext.kind };
|
|
21369
|
+
if (ext.stability && ext.stability !== "stable") meta.stability = ext.stability;
|
|
21024
21370
|
if (ext.description) meta.description = ext.description;
|
|
21025
21371
|
if (ext.entry !== void 0) meta.entry = ext.entry;
|
|
21026
21372
|
return header + "\n" + renderExtensionFields(meta);
|
|
@@ -21038,7 +21384,7 @@ function renderUserExtensionDetail(pluginId, ext, ansi) {
|
|
|
21038
21384
|
version: ext.version,
|
|
21039
21385
|
entry: ext.entryPath
|
|
21040
21386
|
};
|
|
21041
|
-
if (
|
|
21387
|
+
if (ext.stability && ext.stability !== "stable") input.stability = ext.stability;
|
|
21042
21388
|
if (meta.description !== void 0) input.description = meta.description;
|
|
21043
21389
|
if (meta.preconditions !== void 0) input.preconditions = meta.preconditions;
|
|
21044
21390
|
return header + "\n" + renderExtensionFields(input);
|
|
@@ -21048,7 +21394,6 @@ function readInstanceMeta(instance) {
|
|
|
21048
21394
|
const obj = instance;
|
|
21049
21395
|
const out = {};
|
|
21050
21396
|
if (typeof obj["description"] === "string") out.description = obj["description"];
|
|
21051
|
-
if (typeof obj["stability"] === "string") out.stability = obj["stability"];
|
|
21052
21397
|
if (Array.isArray(obj["preconditions"])) {
|
|
21053
21398
|
out.preconditions = obj["preconditions"].filter(
|
|
21054
21399
|
(p) => typeof p === "string"
|
|
@@ -21849,61 +22194,15 @@ var PluginsDisableCommand = class extends TogglePluginsBase {
|
|
|
21849
22194
|
return this.toggle(false);
|
|
21850
22195
|
}
|
|
21851
22196
|
};
|
|
21852
|
-
function pluginCatalogue(plugins) {
|
|
21853
|
-
const out = [];
|
|
21854
|
-
for (const plugin of builtInPlugins) {
|
|
21855
|
-
out.push({
|
|
21856
|
-
id: plugin.id,
|
|
21857
|
-
extensionIds: plugin.extensions.map((e) => e.id)
|
|
21858
|
-
});
|
|
21859
|
-
}
|
|
21860
|
-
for (const p of plugins) {
|
|
21861
|
-
out.push({
|
|
21862
|
-
id: p.id,
|
|
21863
|
-
extensionIds: p.extensions?.map((e) => e.id) ?? []
|
|
21864
|
-
});
|
|
21865
|
-
}
|
|
21866
|
-
return out;
|
|
21867
|
-
}
|
|
21868
22197
|
function resolveToggleTarget(id, catalogue, ansi) {
|
|
21869
22198
|
return id.includes("/") ? resolveQualifiedToggle(id, catalogue, ansi) : resolveBareToggle(id, catalogue);
|
|
21870
22199
|
}
|
|
21871
22200
|
function resolveQualifiedToggle(id, catalogue, ansi) {
|
|
21872
|
-
const
|
|
21873
|
-
|
|
21874
|
-
if (!pluginId || !extId || rest.length > 0) {
|
|
21875
|
-
return {
|
|
21876
|
-
error: tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
|
|
21877
|
-
glyph: errGlyph,
|
|
21878
|
-
pluginId: sanitizeForTerminal(id),
|
|
21879
|
-
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
|
|
21880
|
-
})
|
|
21881
|
-
};
|
|
21882
|
-
}
|
|
21883
|
-
const plugin = catalogue.find((b) => b.id === pluginId);
|
|
21884
|
-
if (!plugin) {
|
|
21885
|
-
return {
|
|
21886
|
-
error: tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
|
|
21887
|
-
glyph: errGlyph,
|
|
21888
|
-
pluginId: sanitizeForTerminal(pluginId),
|
|
21889
|
-
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
|
|
21890
|
-
})
|
|
21891
|
-
};
|
|
21892
|
-
}
|
|
21893
|
-
if (!plugin.extensionIds.includes(extId)) {
|
|
21894
|
-
return {
|
|
21895
|
-
error: tx(PLUGINS_TEXTS.qualifiedIdNotFound, {
|
|
21896
|
-
glyph: errGlyph,
|
|
21897
|
-
id: sanitizeForTerminal(id),
|
|
21898
|
-
pluginId: sanitizeForTerminal(pluginId),
|
|
21899
|
-
extId: sanitizeForTerminal(extId),
|
|
21900
|
-
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdNotFoundHint)
|
|
21901
|
-
})
|
|
21902
|
-
};
|
|
21903
|
-
}
|
|
22201
|
+
const parsed = parseQualifiedExtensionId(id, catalogue);
|
|
22202
|
+
if (!parsed.ok) return { error: renderQualifiedIdError(parsed, id, ansi) };
|
|
21904
22203
|
return {
|
|
21905
22204
|
origin: "qualified",
|
|
21906
|
-
keys: [qualifiedExtensionId(pluginId, extId)]
|
|
22205
|
+
keys: [qualifiedExtensionId(parsed.pluginId, parsed.extId)]
|
|
21907
22206
|
};
|
|
21908
22207
|
}
|
|
21909
22208
|
function resolveBareToggle(id, catalogue) {
|
|
@@ -22283,7 +22582,7 @@ Generated by \`sm plugins create ${kind} ${pluginId}\`. Edit \`${mainFileRel}\`
|
|
|
22283
22582
|
|
|
22284
22583
|
## Verbs
|
|
22285
22584
|
|
|
22286
|
-
- \`sm plugins
|
|
22585
|
+
- \`sm plugins list ${pluginId}\`: manifest + extensions + load status
|
|
22287
22586
|
- \`sm plugins doctor\`: full plugin diagnostic
|
|
22288
22587
|
- \`sm scan\`: re-emit contributions / re-run analysis
|
|
22289
22588
|
|
|
@@ -25735,8 +26034,9 @@ function buildBuiltInItems(resolveEnabled) {
|
|
|
25735
26034
|
id: ext.id,
|
|
25736
26035
|
kind: ext.kind,
|
|
25737
26036
|
version: ext.version,
|
|
25738
|
-
enabled: resolveEnabled(qualified),
|
|
26037
|
+
enabled: resolveEnabled(qualified, installedDefaultEnabled(ext.stability)),
|
|
25739
26038
|
...ext.description ? { description: ext.description } : {},
|
|
26039
|
+
...ext.stability ? { stability: ext.stability } : {},
|
|
25740
26040
|
...extLocked ? { locked: true } : {}
|
|
25741
26041
|
};
|
|
25742
26042
|
});
|
|
@@ -25790,8 +26090,9 @@ function projectExtensionRows(plugin, resolveEnabled, pluginLocked) {
|
|
|
25790
26090
|
id: ext.id,
|
|
25791
26091
|
kind: ext.kind,
|
|
25792
26092
|
version: ext.version,
|
|
25793
|
-
enabled: resolveEnabled(qualified),
|
|
26093
|
+
enabled: resolveEnabled(qualified, installedDefaultEnabled(ext.stability)),
|
|
25794
26094
|
...description ? { description } : {},
|
|
26095
|
+
...ext.stability ? { stability: ext.stability } : {},
|
|
25795
26096
|
...extLocked ? { locked: true } : {}
|
|
25796
26097
|
};
|
|
25797
26098
|
});
|
|
@@ -30277,4 +30578,4 @@ function resolveBareDefault() {
|
|
|
30277
30578
|
process.exit(ExitCode.Error);
|
|
30278
30579
|
}
|
|
30279
30580
|
//# sourceMappingURL=cli.js.map
|
|
30280
|
-
//# debugId=
|
|
30581
|
+
//# debugId=e21852cd-d92e-5bdf-abc7-02fffaa89502
|