@skill-map/cli 0.51.0 → 0.53.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 +239 -1659
- package/dist/cli/tutorial/sm-tutorial/references/_core.md +332 -0
- package/dist/cli/tutorial/sm-tutorial/references/_manifest.yml +175 -0
- package/dist/cli/tutorial/sm-tutorial/references/fixtures.md +251 -0
- package/dist/cli/tutorial/{sm-master/references/tour-authoring.md → sm-tutorial/references/part-authoring.md} +14 -15
- package/dist/cli/tutorial/sm-tutorial/references/part-cli.md +267 -0
- package/dist/cli/tutorial/sm-tutorial/references/part-connect-harness.md +180 -0
- package/dist/cli/tutorial/sm-tutorial/references/part-fundamentals.md +424 -0
- package/dist/cli/tutorial/sm-tutorial/references/part-live-site.md +156 -0
- package/dist/cli/tutorial/sm-tutorial/references/part-maintain.md +286 -0
- package/dist/cli/tutorial/sm-tutorial/references/part-mcp.md +78 -0
- package/dist/cli/tutorial/{sm-master/references/tour-plugins.md → sm-tutorial/references/part-plugins.md} +11 -11
- package/dist/cli/tutorial/sm-tutorial/references/part-project-kickoff.md +186 -0
- package/dist/cli/tutorial/{sm-master/references/tour-settings.md → sm-tutorial/references/part-settings.md} +22 -24
- package/dist/cli.js +1253 -564
- package/dist/index.d.ts +1 -1
- package/dist/index.js +335 -208
- package/dist/kernel/index.d.ts +320 -15
- package/dist/kernel/index.js +335 -208
- package/dist/migrations/001_initial.sql +36 -0
- package/dist/ui/chunk-EQ72PEHT.js +1 -0
- package/dist/ui/chunk-GBKHMJ4B.js +1110 -0
- package/dist/ui/chunk-GEI6INVH.js +515 -0
- package/dist/ui/chunk-JXRIGHET.js +552 -0
- package/dist/ui/{chunk-WQMZOINB.js → chunk-K2MAVAHG.js} +1 -1
- package/dist/ui/{chunk-BV323KTK.js → chunk-KHARMPTZ.js} +1 -1
- package/dist/ui/chunk-L4NIF75A.js +2 -0
- package/dist/ui/chunk-LCOYSPKE.js +1 -0
- package/dist/ui/chunk-OFDQMBSJ.js +1 -0
- package/dist/ui/chunk-P2DAPRK7.js +2 -0
- package/dist/ui/chunk-Q2A6FWC7.js +4 -0
- package/dist/ui/chunk-TXTY24G4.js +2204 -0
- package/dist/ui/chunk-UBQUCSQ4.js +1 -0
- package/dist/ui/chunk-WFLPMCK4.js +392 -0
- package/dist/ui/chunk-WHZVGOS3.js +5 -0
- package/dist/ui/chunk-YQFIXHKM.js +123 -0
- package/dist/ui/index.html +2 -2
- package/dist/ui/main-OYITFJ7B.js +4 -0
- package/dist/ui/{styles-RG7Y33BT.css → styles-Q4NCOJQY.css} +1 -1
- package/migrations/001_initial.sql +36 -0
- package/package.json +10 -8
- package/dist/cli/tutorial/sm-master/SKILL.md +0 -688
- package/dist/cli/tutorial/sm-master/references/fixture-templates.md +0 -212
- package/dist/ui/chunk-2GXE52AJ.js +0 -123
- package/dist/ui/chunk-AEA5GIA7.js +0 -1
- package/dist/ui/chunk-KHRNVLJW.js +0 -1
- package/dist/ui/chunk-OZTRR4M7.js +0 -2312
- package/dist/ui/chunk-Q5YJKCTP.js +0 -1066
- package/dist/ui/chunk-RCT3JSFL.js +0 -1
- package/dist/ui/chunk-VBTLX7GH.js +0 -1110
- package/dist/ui/chunk-VJ57LHDR.js +0 -4
- package/dist/ui/chunk-WMGW2UAL.js +0 -2
- package/dist/ui/main-N7D2YBEX.js +0 -4
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]="82aaa5c6-6dae-53a6-8d4a-1a673357051c")}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.53.0",
|
|
250
250
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
251
251
|
license: "MIT",
|
|
252
252
|
type: "module",
|
|
@@ -304,15 +304,16 @@ var package_default = {
|
|
|
304
304
|
prebuild: "pnpm build-built-ins",
|
|
305
305
|
validate: "pnpm validate:compile && pnpm validate:test",
|
|
306
306
|
"validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm built-ins:check && pnpm view-catalog:check",
|
|
307
|
-
"validate:test": "pnpm test:ci",
|
|
307
|
+
"validate:test": "pnpm test:ci && pnpm conformance",
|
|
308
|
+
conformance: "SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 node --import tsx cli/entry.ts conformance run",
|
|
308
309
|
pretest: "tsup",
|
|
309
310
|
"pretest:coverage": "tsup",
|
|
310
311
|
"pretest:coverage:html": "tsup",
|
|
311
|
-
test: "tsc --noEmit && SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
312
|
-
"test:ci": "FORCE_COLOR=1 SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
313
|
-
"test:spec": "SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=spec '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
314
|
-
"test:coverage": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SKILL_MAP_SKIP_BENCHMARK=1 node --experimental-default-config-file --import tsx --test --experimental-test-coverage '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
315
|
-
"test:coverage:html": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SKILL_MAP_SKIP_BENCHMARK=1 c8 node --import tsx --test '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
312
|
+
test: "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
313
|
+
"test:ci": "FORCE_COLOR=1 SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
314
|
+
"test:spec": "SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 node --import tsx --test --test-reporter=spec '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
315
|
+
"test:coverage": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 SKILL_MAP_SKIP_BENCHMARK=1 node --experimental-default-config-file --import tsx --test --experimental-test-coverage '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
316
|
+
"test:coverage:html": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SM_NO_UPDATE_CHECK=1 SKILL_MAP_SKIP_BENCHMARK=1 c8 node --import tsx --test '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
316
317
|
clean: "rm -rf dist coverage"
|
|
317
318
|
},
|
|
318
319
|
dependencies: {
|
|
@@ -344,6 +345,7 @@ var package_default = {
|
|
|
344
345
|
c8: "11.0.0",
|
|
345
346
|
eslint: "10.2.1",
|
|
346
347
|
"eslint-plugin-import-x": "4.16.2",
|
|
348
|
+
"json-schema-to-typescript": "15.0.4",
|
|
347
349
|
tsup: "8.5.1",
|
|
348
350
|
tsx: "4.22.3",
|
|
349
351
|
typescript: "5.9.3",
|
|
@@ -1449,6 +1451,13 @@ function stringArray(value) {
|
|
|
1449
1451
|
|
|
1450
1452
|
// plugins/core/extractors/external-url-counter/index.ts
|
|
1451
1453
|
var ID4 = "external-url-counter";
|
|
1454
|
+
var count = {
|
|
1455
|
+
slot: "card.footer.left",
|
|
1456
|
+
icon: "pi-link",
|
|
1457
|
+
label: "urls",
|
|
1458
|
+
emitWhenEmpty: false,
|
|
1459
|
+
priority: 30
|
|
1460
|
+
};
|
|
1452
1461
|
var URL_RE = /https?:\/\/[^\s<>"'`)\]]+/g;
|
|
1453
1462
|
var TRAILING_PUNCT = /[.,;:!?]+$/;
|
|
1454
1463
|
var externalUrlCounterExtractor = {
|
|
@@ -1473,15 +1482,7 @@ var externalUrlCounterExtractor = {
|
|
|
1473
1482
|
* inherited from the footer `.sm-gnode__stat` styles cloned by
|
|
1474
1483
|
* the `NodeCounter` renderer.
|
|
1475
1484
|
*/
|
|
1476
|
-
ui: {
|
|
1477
|
-
count: {
|
|
1478
|
-
slot: "card.footer.left",
|
|
1479
|
-
icon: "pi-link",
|
|
1480
|
-
label: "urls",
|
|
1481
|
-
emitWhenEmpty: false,
|
|
1482
|
-
priority: 30
|
|
1483
|
-
}
|
|
1484
|
-
},
|
|
1485
|
+
ui: { count },
|
|
1485
1486
|
extract(ctx) {
|
|
1486
1487
|
const seen = /* @__PURE__ */ new Set();
|
|
1487
1488
|
const body = stripCodeBlocks(ctx.body);
|
|
@@ -1516,7 +1517,7 @@ var externalUrlCounterExtractor = {
|
|
|
1516
1517
|
});
|
|
1517
1518
|
}
|
|
1518
1519
|
if (seen.size > 0) {
|
|
1519
|
-
ctx.emitContribution(
|
|
1520
|
+
ctx.emitContribution(count, { value: seen.size });
|
|
1520
1521
|
}
|
|
1521
1522
|
}
|
|
1522
1523
|
};
|
|
@@ -1660,6 +1661,13 @@ function collectMcpServers(tools) {
|
|
|
1660
1661
|
|
|
1661
1662
|
// plugins/core/extractors/tools-counter/index.ts
|
|
1662
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
|
+
};
|
|
1663
1671
|
var TOOLTIP_MAX = 255;
|
|
1664
1672
|
var toolsCounterExtractor = {
|
|
1665
1673
|
id: ID7,
|
|
@@ -1668,15 +1676,7 @@ var toolsCounterExtractor = {
|
|
|
1668
1676
|
description: "Counts the tools an agent declares in its frontmatter and shows the count on the agent card.",
|
|
1669
1677
|
scope: "frontmatter",
|
|
1670
1678
|
precondition: { kind: ["claude/agent"] },
|
|
1671
|
-
ui: {
|
|
1672
|
-
count: {
|
|
1673
|
-
slot: "card.footer.left",
|
|
1674
|
-
icon: "pi-wrench",
|
|
1675
|
-
label: "tools",
|
|
1676
|
-
emitWhenEmpty: false,
|
|
1677
|
-
priority: 40
|
|
1678
|
-
}
|
|
1679
|
-
},
|
|
1679
|
+
ui: { count: count2 },
|
|
1680
1680
|
extract(ctx) {
|
|
1681
1681
|
const raw = ctx.frontmatter["tools"];
|
|
1682
1682
|
if (!Array.isArray(raw)) return;
|
|
@@ -1685,7 +1685,7 @@ var toolsCounterExtractor = {
|
|
|
1685
1685
|
if (typeof t === "string" && t.length > 0) names.push(t);
|
|
1686
1686
|
}
|
|
1687
1687
|
if (names.length === 0) return;
|
|
1688
|
-
ctx.emitContribution(
|
|
1688
|
+
ctx.emitContribution(count2, {
|
|
1689
1689
|
value: names.length,
|
|
1690
1690
|
tooltip: buildTooltip(names)
|
|
1691
1691
|
});
|
|
@@ -1923,65 +1923,91 @@ var ANNOTATION_STALE_TEXTS = {
|
|
|
1923
1923
|
// a literal placeholder the operator substitutes.
|
|
1924
1924
|
bodyTooltip: "Sidecar drift since last bump:\n \u2022 body\nRun `sm bump <path>` to refresh.",
|
|
1925
1925
|
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."
|
|
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."
|
|
1927
1931
|
};
|
|
1928
1932
|
|
|
1929
1933
|
// plugins/core/analyzers/annotation-stale/index.ts
|
|
1930
1934
|
var ID10 = "annotation-stale";
|
|
1935
|
+
var staleIcon = {
|
|
1936
|
+
slot: "card.footer.right",
|
|
1937
|
+
icon: "pi-clock",
|
|
1938
|
+
emitWhenEmpty: true,
|
|
1939
|
+
priority: 20
|
|
1940
|
+
};
|
|
1941
|
+
var staleBadge = {
|
|
1942
|
+
slot: "inspector.header.badge",
|
|
1943
|
+
emitWhenEmpty: false,
|
|
1944
|
+
priority: 20
|
|
1945
|
+
};
|
|
1946
|
+
var bumpButton = {
|
|
1947
|
+
slot: "inspector.action.button",
|
|
1948
|
+
priority: 10
|
|
1949
|
+
};
|
|
1931
1950
|
var annotationStaleAnalyzer = {
|
|
1932
1951
|
id: ID10,
|
|
1933
1952
|
pluginId: CORE_PLUGIN_ID,
|
|
1934
1953
|
kind: "analyzer",
|
|
1935
1954
|
description: "Marks sidecars (`.sm`) that are out of date with their `.md`.",
|
|
1936
1955
|
mode: "deterministic",
|
|
1937
|
-
// The natural fix is to bump the node: refreshes
|
|
1956
|
+
// The natural fix is to bump the node: refreshes the sidecar hashes,
|
|
1938
1957
|
// increments `annotations.version`, and stamps the audit block. The
|
|
1939
|
-
//
|
|
1940
|
-
|
|
1941
|
-
ui: {
|
|
1942
|
-
// A `pi-clock` chip in the footer-right cluster so the operator
|
|
1943
|
-
// spots drift in the list / inspector view (and on the graph card
|
|
1944
|
-
// body). Emitted with `value: 0` and `emitWhenEmpty: true` so the
|
|
1945
|
-
// renderer treats it as icon-only, drift severity is binary at
|
|
1946
|
-
// this surface (the tooltip carries the per-face detail body /
|
|
1947
|
-
// frontmatter / both). The corner badge on `graph.node.alert` was
|
|
1948
|
-
// dropped on purpose: a tooltip on the footer chip is enough, and
|
|
1949
|
-
// the corner badge stacked on top of broken-ref / unknown-field
|
|
1950
|
-
// alerts produced visual noise.
|
|
1951
|
-
staleIcon: {
|
|
1952
|
-
slot: "card.footer.right",
|
|
1953
|
-
icon: "pi-clock",
|
|
1954
|
-
emitWhenEmpty: true,
|
|
1955
|
-
// Sits right after the stability badge and before the severity
|
|
1956
|
-
// counters: stability is the node's declared lifecycle state,
|
|
1957
|
-
// drift is "this node disagrees with its sidecar", then the
|
|
1958
|
-
// warn / error counts anchor the right edge.
|
|
1959
|
-
priority: 20
|
|
1960
|
-
}
|
|
1961
|
-
},
|
|
1958
|
+
// inspector surfaces `core/node-bump` as the `bumpButton` contribution.
|
|
1959
|
+
ui: { staleIcon, staleBadge, bumpButton },
|
|
1962
1960
|
evaluate(ctx) {
|
|
1963
1961
|
const issues = [];
|
|
1964
1962
|
for (const node of ctx.nodes) {
|
|
1965
|
-
const status = node.sidecar
|
|
1966
|
-
if (
|
|
1967
|
-
|
|
1968
|
-
|
|
1963
|
+
const status = staleStatus(node.sidecar);
|
|
1964
|
+
if (node.sidecar?.present === true) {
|
|
1965
|
+
emitBumpButton(ctx, node.path, status !== null);
|
|
1966
|
+
}
|
|
1967
|
+
if (status === null) continue;
|
|
1969
1968
|
issues.push({
|
|
1970
1969
|
analyzerId: ID10,
|
|
1971
|
-
severity: "
|
|
1970
|
+
severity: "info",
|
|
1972
1971
|
nodeIds: [node.path],
|
|
1973
|
-
message,
|
|
1972
|
+
message: messageFor(status, node.path),
|
|
1974
1973
|
data: { status }
|
|
1975
1974
|
});
|
|
1976
|
-
ctx.emitContribution(node.path,
|
|
1975
|
+
ctx.emitContribution(node.path, staleIcon, {
|
|
1977
1976
|
value: 0,
|
|
1978
|
-
|
|
1977
|
+
tooltip: tooltipFor(status)
|
|
1978
|
+
});
|
|
1979
|
+
ctx.emitContribution(node.path, staleBadge, {
|
|
1980
|
+
icon: "pi-clock",
|
|
1979
1981
|
tooltip: tooltipFor(status)
|
|
1980
1982
|
});
|
|
1981
1983
|
}
|
|
1982
1984
|
return issues;
|
|
1983
1985
|
}
|
|
1984
1986
|
};
|
|
1987
|
+
function staleStatus(overlay) {
|
|
1988
|
+
const status = overlay?.status;
|
|
1989
|
+
if (status === void 0 || status === null || status === "fresh") return null;
|
|
1990
|
+
return status;
|
|
1991
|
+
}
|
|
1992
|
+
function messageFor(status, path) {
|
|
1993
|
+
switch (status) {
|
|
1994
|
+
case "stale-body":
|
|
1995
|
+
return tx(ANNOTATION_STALE_TEXTS.bodyDrift, { path });
|
|
1996
|
+
case "stale-frontmatter":
|
|
1997
|
+
return tx(ANNOTATION_STALE_TEXTS.frontmatterDrift, { path });
|
|
1998
|
+
case "stale-both":
|
|
1999
|
+
return tx(ANNOTATION_STALE_TEXTS.bothDrift, { path });
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
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
|
+
}
|
|
1985
2011
|
function tooltipFor(status) {
|
|
1986
2012
|
switch (status) {
|
|
1987
2013
|
case "stale-body":
|
|
@@ -2016,6 +2042,18 @@ var ISSUE_COUNTER_TEXTS = {
|
|
|
2016
2042
|
|
|
2017
2043
|
// plugins/core/analyzers/issue-counter/index.ts
|
|
2018
2044
|
var ID12 = "issue-counter";
|
|
2045
|
+
var warnCount = {
|
|
2046
|
+
slot: "card.footer.right",
|
|
2047
|
+
icon: "pi-exclamation-triangle",
|
|
2048
|
+
emitWhenEmpty: false,
|
|
2049
|
+
priority: 30
|
|
2050
|
+
};
|
|
2051
|
+
var errorCount = {
|
|
2052
|
+
slot: "card.footer.right",
|
|
2053
|
+
icon: "pi-times-circle",
|
|
2054
|
+
emitWhenEmpty: false,
|
|
2055
|
+
priority: 40
|
|
2056
|
+
};
|
|
2019
2057
|
function countByTier(issues) {
|
|
2020
2058
|
const errors = /* @__PURE__ */ new Map();
|
|
2021
2059
|
const warns = /* @__PURE__ */ new Map();
|
|
@@ -2028,13 +2066,13 @@ function countByTier(issues) {
|
|
|
2028
2066
|
}
|
|
2029
2067
|
return { errors, warns };
|
|
2030
2068
|
}
|
|
2031
|
-
function emitTierChips(ctx,
|
|
2032
|
-
for (const [nodePath,
|
|
2033
|
-
const capped = Math.min(
|
|
2034
|
-
ctx.emitContribution(nodePath,
|
|
2069
|
+
function emitTierChips(ctx, ref, severity, counts, singleTooltip, manyTooltip) {
|
|
2070
|
+
for (const [nodePath, count3] of counts) {
|
|
2071
|
+
const capped = Math.min(count3, 99);
|
|
2072
|
+
ctx.emitContribution(nodePath, ref, {
|
|
2035
2073
|
value: capped,
|
|
2036
2074
|
severity,
|
|
2037
|
-
tooltip:
|
|
2075
|
+
tooltip: count3 === 1 ? singleTooltip : tx(manyTooltip, { count: count3 })
|
|
2038
2076
|
});
|
|
2039
2077
|
}
|
|
2040
2078
|
}
|
|
@@ -2045,33 +2083,14 @@ var issueCounterAnalyzer = {
|
|
|
2045
2083
|
description: "Emits one aggregate severity chip per node (error + warn counts) from the live issue accumulator.",
|
|
2046
2084
|
mode: "deterministic",
|
|
2047
2085
|
phase: "aggregate",
|
|
2048
|
-
ui: {
|
|
2049
|
-
// Third in the footer-right cluster, after the drift chip
|
|
2050
|
-
// (priority 10) and the stability badge (priority 20). The warn
|
|
2051
|
-
// counter sits before the error counter so the operator reads
|
|
2052
|
-
// "advisory → blocking" left-to-right.
|
|
2053
|
-
warnCount: {
|
|
2054
|
-
slot: "card.footer.right",
|
|
2055
|
-
icon: "pi-exclamation-triangle",
|
|
2056
|
-
emitWhenEmpty: false,
|
|
2057
|
-
priority: 30
|
|
2058
|
-
},
|
|
2059
|
-
// Last in the cluster, the red chip pins to the right edge so the
|
|
2060
|
-
// most severe signal anchors the row's reading position.
|
|
2061
|
-
errorCount: {
|
|
2062
|
-
slot: "card.footer.right",
|
|
2063
|
-
icon: "pi-times-circle",
|
|
2064
|
-
emitWhenEmpty: false,
|
|
2065
|
-
priority: 40
|
|
2066
|
-
}
|
|
2067
|
-
},
|
|
2086
|
+
ui: { warnCount, errorCount },
|
|
2068
2087
|
evaluate(ctx) {
|
|
2069
2088
|
const accumulator = ctx.accumulatedIssues ?? [];
|
|
2070
2089
|
if (accumulator.length === 0) return [];
|
|
2071
2090
|
const { errors, warns } = countByTier(accumulator);
|
|
2072
2091
|
emitTierChips(
|
|
2073
2092
|
ctx,
|
|
2074
|
-
|
|
2093
|
+
errorCount,
|
|
2075
2094
|
"danger",
|
|
2076
2095
|
errors,
|
|
2077
2096
|
ISSUE_COUNTER_TEXTS.errorTooltipSingle,
|
|
@@ -2079,7 +2098,7 @@ var issueCounterAnalyzer = {
|
|
|
2079
2098
|
);
|
|
2080
2099
|
emitTierChips(
|
|
2081
2100
|
ctx,
|
|
2082
|
-
|
|
2101
|
+
warnCount,
|
|
2083
2102
|
"warn",
|
|
2084
2103
|
warns,
|
|
2085
2104
|
ISSUE_COUNTER_TEXTS.warnTooltipSingle,
|
|
@@ -2247,28 +2266,27 @@ function resolveLinkTargetToPath(link, nameIndex) {
|
|
|
2247
2266
|
|
|
2248
2267
|
// plugins/core/analyzers/link-counter/index.ts
|
|
2249
2268
|
var ID15 = "link-counter";
|
|
2269
|
+
var linksIn = {
|
|
2270
|
+
slot: "card.footer.left",
|
|
2271
|
+
icon: "pi-download",
|
|
2272
|
+
label: "incoming links",
|
|
2273
|
+
emitWhenEmpty: false,
|
|
2274
|
+
priority: 10
|
|
2275
|
+
};
|
|
2276
|
+
var linksOut = {
|
|
2277
|
+
slot: "card.footer.left",
|
|
2278
|
+
icon: "pi-upload",
|
|
2279
|
+
label: "outgoing links",
|
|
2280
|
+
emitWhenEmpty: false,
|
|
2281
|
+
priority: 20
|
|
2282
|
+
};
|
|
2250
2283
|
var linkCounterAnalyzer = {
|
|
2251
2284
|
id: ID15,
|
|
2252
2285
|
pluginId: CORE_PLUGIN_ID,
|
|
2253
2286
|
kind: "analyzer",
|
|
2254
2287
|
description: "Counts incoming and outgoing links per node.",
|
|
2255
2288
|
mode: "deterministic",
|
|
2256
|
-
ui: {
|
|
2257
|
-
linksIn: {
|
|
2258
|
-
slot: "card.footer.left",
|
|
2259
|
-
icon: "pi-download",
|
|
2260
|
-
label: "incoming links",
|
|
2261
|
-
emitWhenEmpty: false,
|
|
2262
|
-
priority: 10
|
|
2263
|
-
},
|
|
2264
|
-
linksOut: {
|
|
2265
|
-
slot: "card.footer.left",
|
|
2266
|
-
icon: "pi-upload",
|
|
2267
|
-
label: "outgoing links",
|
|
2268
|
-
emitWhenEmpty: false,
|
|
2269
|
-
priority: 20
|
|
2270
|
-
}
|
|
2271
|
-
},
|
|
2289
|
+
ui: { linksIn, linksOut },
|
|
2272
2290
|
evaluate(ctx) {
|
|
2273
2291
|
const nameIndex = buildNameIndex(ctx.nodes);
|
|
2274
2292
|
const perTarget = /* @__PURE__ */ new Map();
|
|
@@ -2280,8 +2298,8 @@ var linkCounterAnalyzer = {
|
|
|
2280
2298
|
bump(perSource, link.source, link.kind);
|
|
2281
2299
|
}
|
|
2282
2300
|
for (const node of ctx.nodes) {
|
|
2283
|
-
emitChip(ctx, node.path,
|
|
2284
|
-
emitChip(ctx, node.path,
|
|
2301
|
+
emitChip(ctx, node.path, linksIn, "in", perTarget.get(node.path));
|
|
2302
|
+
emitChip(ctx, node.path, linksOut, "out", perSource.get(node.path));
|
|
2285
2303
|
}
|
|
2286
2304
|
return [];
|
|
2287
2305
|
}
|
|
@@ -2294,14 +2312,13 @@ function bump(map, key, kind) {
|
|
|
2294
2312
|
}
|
|
2295
2313
|
byKind.set(kind, (byKind.get(kind) ?? 0) + 1);
|
|
2296
2314
|
}
|
|
2297
|
-
function emitChip(ctx, nodePath,
|
|
2315
|
+
function emitChip(ctx, nodePath, ref, direction, byKind) {
|
|
2298
2316
|
if (!byKind) return;
|
|
2299
2317
|
let total = 0;
|
|
2300
2318
|
for (const n of byKind.values()) total += n;
|
|
2301
2319
|
if (total === 0) return;
|
|
2302
2320
|
const capped = Math.min(total, 99);
|
|
2303
|
-
|
|
2304
|
-
ctx.emitContribution(nodePath, contributionId, {
|
|
2321
|
+
ctx.emitContribution(nodePath, ref, {
|
|
2305
2322
|
value: capped,
|
|
2306
2323
|
tooltip: formatBreakdown(byKind, direction)
|
|
2307
2324
|
});
|
|
@@ -2581,42 +2598,58 @@ function normaliseId(raw) {
|
|
|
2581
2598
|
return raw.normalize("NFD").replace(new RegExp("\\p{Mn}+", "gu"), "").toLowerCase().replace(/[-_\s]+/g, " ").replace(/ +/g, " ").trim();
|
|
2582
2599
|
}
|
|
2583
2600
|
|
|
2601
|
+
// plugins/core/analyzers/node-stability/text.ts
|
|
2602
|
+
var NODE_STABILITY_TEXTS = {
|
|
2603
|
+
/** Label of the inspector action button that sets the lifecycle stage. */
|
|
2604
|
+
setLabel: "Set stability",
|
|
2605
|
+
/** Prompt label for the enum-pick stability input. */
|
|
2606
|
+
promptLabel: "Stability",
|
|
2607
|
+
/** Prompt option label for the `experimental` stage. */
|
|
2608
|
+
optionExperimental: "Experimental",
|
|
2609
|
+
/** Prompt option label for the `stable` stage. */
|
|
2610
|
+
optionStable: "Stable",
|
|
2611
|
+
/** Prompt option label for the `deprecated` stage. */
|
|
2612
|
+
optionDeprecated: "Deprecated"
|
|
2613
|
+
};
|
|
2614
|
+
|
|
2584
2615
|
// plugins/core/analyzers/node-stability/index.ts
|
|
2585
2616
|
var ID18 = "node-stability";
|
|
2586
2617
|
var EXPERIMENTAL_TOOLTIP = "Experimental: API may change";
|
|
2587
2618
|
var DEPRECATED_TOOLTIP = "Deprecated: avoid in new code";
|
|
2619
|
+
var experimental = {
|
|
2620
|
+
slot: "card.footer.right",
|
|
2621
|
+
icon: "fa-solid fa-flask",
|
|
2622
|
+
label: "experimental",
|
|
2623
|
+
emitWhenEmpty: false,
|
|
2624
|
+
priority: 10
|
|
2625
|
+
};
|
|
2626
|
+
var deprecated = {
|
|
2627
|
+
slot: "card.footer.right",
|
|
2628
|
+
icon: "pi-ban",
|
|
2629
|
+
label: "deprecated",
|
|
2630
|
+
emitWhenEmpty: false,
|
|
2631
|
+
priority: 10
|
|
2632
|
+
};
|
|
2633
|
+
var setStabilityButton = {
|
|
2634
|
+
slot: "inspector.action.button",
|
|
2635
|
+
priority: 15
|
|
2636
|
+
};
|
|
2588
2637
|
var nodeStabilityAnalyzer = {
|
|
2589
2638
|
id: ID18,
|
|
2590
2639
|
pluginId: CORE_PLUGIN_ID,
|
|
2591
2640
|
kind: "analyzer",
|
|
2592
2641
|
description: "Reports a node's stability stage (`experimental`, `deprecated`) on the card.",
|
|
2593
2642
|
mode: "deterministic",
|
|
2594
|
-
ui: {
|
|
2595
|
-
// First in the footer-right cluster: stability is the node's
|
|
2596
|
-
// declared lifecycle state, so it leads, followed by the drift
|
|
2597
|
-
// chip and then the severity counters. It's a state badge, not a
|
|
2598
|
-
// count, so it stays left of the numeric zone.
|
|
2599
|
-
experimental: {
|
|
2600
|
-
slot: "card.footer.right",
|
|
2601
|
-
icon: "fa-solid fa-flask",
|
|
2602
|
-
label: "experimental",
|
|
2603
|
-
emitWhenEmpty: false,
|
|
2604
|
-
priority: 10
|
|
2605
|
-
},
|
|
2606
|
-
deprecated: {
|
|
2607
|
-
slot: "card.footer.right",
|
|
2608
|
-
icon: "pi-ban",
|
|
2609
|
-
label: "deprecated",
|
|
2610
|
-
emitWhenEmpty: false,
|
|
2611
|
-
priority: 10
|
|
2612
|
-
}
|
|
2613
|
-
},
|
|
2643
|
+
ui: { experimental, deprecated, setStabilityButton },
|
|
2614
2644
|
evaluate(ctx) {
|
|
2615
2645
|
const issues = [];
|
|
2616
2646
|
for (const node of ctx.nodes) {
|
|
2617
2647
|
const stability = readStability(node);
|
|
2648
|
+
if (node.sidecar?.present === true) {
|
|
2649
|
+
emitSetStabilityButton(ctx, node.path, stability ?? "stable");
|
|
2650
|
+
}
|
|
2618
2651
|
if (stability === "experimental") {
|
|
2619
|
-
ctx.emitContribution(node.path,
|
|
2652
|
+
ctx.emitContribution(node.path, experimental, {
|
|
2620
2653
|
value: 0,
|
|
2621
2654
|
tooltip: EXPERIMENTAL_TOOLTIP
|
|
2622
2655
|
});
|
|
@@ -2628,7 +2661,7 @@ var nodeStabilityAnalyzer = {
|
|
|
2628
2661
|
data: { stability }
|
|
2629
2662
|
});
|
|
2630
2663
|
} else if (stability === "deprecated") {
|
|
2631
|
-
ctx.emitContribution(node.path,
|
|
2664
|
+
ctx.emitContribution(node.path, deprecated, {
|
|
2632
2665
|
value: 0,
|
|
2633
2666
|
tooltip: DEPRECATED_TOOLTIP,
|
|
2634
2667
|
severity: "warn"
|
|
@@ -2660,6 +2693,25 @@ function readLegacyMetadataStability(fm) {
|
|
|
2660
2693
|
function isStability(value) {
|
|
2661
2694
|
return value === "experimental" || value === "deprecated" || value === "stable";
|
|
2662
2695
|
}
|
|
2696
|
+
function emitSetStabilityButton(ctx, nodePath, current) {
|
|
2697
|
+
ctx.emitContribution(nodePath, setStabilityButton, {
|
|
2698
|
+
actionId: "core/node-set-stability",
|
|
2699
|
+
label: NODE_STABILITY_TEXTS.setLabel,
|
|
2700
|
+
icon: "pi-flag",
|
|
2701
|
+
enabled: true,
|
|
2702
|
+
prompt: {
|
|
2703
|
+
inputType: "enum-pick",
|
|
2704
|
+
paramKey: "stability",
|
|
2705
|
+
label: NODE_STABILITY_TEXTS.promptLabel,
|
|
2706
|
+
options: [
|
|
2707
|
+
{ value: "experimental", label: NODE_STABILITY_TEXTS.optionExperimental },
|
|
2708
|
+
{ value: "stable", label: NODE_STABILITY_TEXTS.optionStable },
|
|
2709
|
+
{ value: "deprecated", label: NODE_STABILITY_TEXTS.optionDeprecated }
|
|
2710
|
+
],
|
|
2711
|
+
defaultValue: current
|
|
2712
|
+
}
|
|
2713
|
+
});
|
|
2714
|
+
}
|
|
2663
2715
|
|
|
2664
2716
|
// plugins/core/analyzers/node-superseded/text.ts
|
|
2665
2717
|
var NODE_SUPERSEDED_TEXTS = {
|
|
@@ -3022,8 +3074,8 @@ var ALL_SLOT_NAMES = [
|
|
|
3022
3074
|
"card.footer.left",
|
|
3023
3075
|
"card.footer.right",
|
|
3024
3076
|
"graph.node.alert",
|
|
3025
|
-
"inspector.header.badge
|
|
3026
|
-
"inspector.
|
|
3077
|
+
"inspector.header.badge",
|
|
3078
|
+
"inspector.action.button",
|
|
3027
3079
|
"inspector.body.panel.breakdown",
|
|
3028
3080
|
"inspector.body.panel.records",
|
|
3029
3081
|
"inspector.body.panel.tree",
|
|
@@ -3131,12 +3183,12 @@ function buildSchemaValidators() {
|
|
|
3131
3183
|
const v = validators.get(name);
|
|
3132
3184
|
if (!v) throw new Error(`Unknown schema: ${name}`);
|
|
3133
3185
|
if (v(data)) return { ok: true, data };
|
|
3134
|
-
const errors = (v.errors
|
|
3186
|
+
const errors = formatAjvErrors(v.errors);
|
|
3135
3187
|
return { ok: false, errors };
|
|
3136
3188
|
},
|
|
3137
3189
|
validatePluginManifest(data) {
|
|
3138
3190
|
if (pluginManifestValidator(data)) return { ok: true, data };
|
|
3139
|
-
const errors = (pluginManifestValidator.errors
|
|
3191
|
+
const errors = formatAjvErrors(pluginManifestValidator.errors);
|
|
3140
3192
|
return { ok: false, errors };
|
|
3141
3193
|
},
|
|
3142
3194
|
validateContributionPayload(slot, payload) {
|
|
@@ -3145,7 +3197,7 @@ function buildSchemaValidators() {
|
|
|
3145
3197
|
return { ok: false, errors: "unknown-slot" };
|
|
3146
3198
|
}
|
|
3147
3199
|
if (validator(payload)) return { ok: true };
|
|
3148
|
-
const errors = (validator.errors
|
|
3200
|
+
const errors = formatAjvErrors(validator.errors);
|
|
3149
3201
|
return { ok: false, errors };
|
|
3150
3202
|
}
|
|
3151
3203
|
};
|
|
@@ -3177,7 +3229,7 @@ function buildProviderFrontmatterValidator(providers) {
|
|
|
3177
3229
|
const v = compiled.get(key);
|
|
3178
3230
|
if (!v) return { ok: false, errors: "no-schema" };
|
|
3179
3231
|
if (v(data)) return { ok: true };
|
|
3180
|
-
const errors = (v.errors
|
|
3232
|
+
const errors = formatAjvErrors(v.errors);
|
|
3181
3233
|
return { ok: false, errors };
|
|
3182
3234
|
}
|
|
3183
3235
|
};
|
|
@@ -3186,6 +3238,47 @@ function formatError(err) {
|
|
|
3186
3238
|
const path = err.instancePath || "(root)";
|
|
3187
3239
|
return `${path} ${err.message ?? err.keyword}`;
|
|
3188
3240
|
}
|
|
3241
|
+
function formatAjvErrors(errors) {
|
|
3242
|
+
const list = errors ?? [];
|
|
3243
|
+
if (list.length === 0) return "";
|
|
3244
|
+
const byPath3 = /* @__PURE__ */ new Map();
|
|
3245
|
+
for (const e of list) {
|
|
3246
|
+
const path = e.instancePath || "(root)";
|
|
3247
|
+
const bucket = byPath3.get(path);
|
|
3248
|
+
if (bucket) bucket.push(e);
|
|
3249
|
+
else byPath3.set(path, [e]);
|
|
3250
|
+
}
|
|
3251
|
+
const parts = [];
|
|
3252
|
+
for (const [path, errs] of byPath3) parts.push(...formatPathErrors(path, errs));
|
|
3253
|
+
return [...new Set(parts)].join("; ");
|
|
3254
|
+
}
|
|
3255
|
+
function constBranchValues(errs) {
|
|
3256
|
+
let count3 = 0;
|
|
3257
|
+
for (const e of errs) {
|
|
3258
|
+
const isConst = e.keyword === "const" && typeof e.params === "object" && e.params !== null && "allowedValue" in e.params;
|
|
3259
|
+
if (isConst) count3 += 1;
|
|
3260
|
+
}
|
|
3261
|
+
return count3;
|
|
3262
|
+
}
|
|
3263
|
+
function formatPathErrors(path, errs) {
|
|
3264
|
+
if (constBranchValues(errs) >= 2) {
|
|
3265
|
+
const parts2 = [`${path} is not a valid value`];
|
|
3266
|
+
for (const e of errs) {
|
|
3267
|
+
if (e.keyword !== "const" && e.keyword !== "oneOf") parts2.push(formatError(e));
|
|
3268
|
+
}
|
|
3269
|
+
return parts2;
|
|
3270
|
+
}
|
|
3271
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3272
|
+
const parts = [];
|
|
3273
|
+
for (const e of errs) {
|
|
3274
|
+
const msg = formatError(e);
|
|
3275
|
+
if (!seen.has(msg)) {
|
|
3276
|
+
seen.add(msg);
|
|
3277
|
+
parts.push(msg);
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
return parts;
|
|
3281
|
+
}
|
|
3189
3282
|
function registerProviderAuxiliarySchemas(ajv, providers) {
|
|
3190
3283
|
for (const provider of providers) {
|
|
3191
3284
|
if (!provider.schemas) continue;
|
|
@@ -3498,6 +3591,122 @@ function makeIssue(signal) {
|
|
|
3498
3591
|
return null;
|
|
3499
3592
|
}
|
|
3500
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 [];
|
|
3686
|
+
}
|
|
3687
|
+
};
|
|
3688
|
+
function emitSetTagsButton(ctx, node) {
|
|
3689
|
+
ctx.emitContribution(node.path, setTagsButton, {
|
|
3690
|
+
actionId: "core/node-set-tags",
|
|
3691
|
+
label: TAGS_TEXTS.editLabel,
|
|
3692
|
+
icon: "pi-tags",
|
|
3693
|
+
enabled: true,
|
|
3694
|
+
prompt: {
|
|
3695
|
+
inputType: "string-list",
|
|
3696
|
+
paramKey: "tags",
|
|
3697
|
+
label: TAGS_TEXTS.promptLabel,
|
|
3698
|
+
defaultValue: currentTags(node)
|
|
3699
|
+
}
|
|
3700
|
+
});
|
|
3701
|
+
}
|
|
3702
|
+
function currentTags(node) {
|
|
3703
|
+
const ann = node.sidecar?.annotations;
|
|
3704
|
+
if (!ann || typeof ann !== "object" || Array.isArray(ann)) return [];
|
|
3705
|
+
const value = ann["tags"];
|
|
3706
|
+
if (!Array.isArray(value)) return [];
|
|
3707
|
+
return value.filter((t) => typeof t === "string");
|
|
3708
|
+
}
|
|
3709
|
+
|
|
3501
3710
|
// plugins/core/analyzers/trigger-collision/text.ts
|
|
3502
3711
|
var TRIGGER_COLLISION_TEXTS = {
|
|
3503
3712
|
/**
|
|
@@ -3524,14 +3733,14 @@ var TRIGGER_COLLISION_TEXTS = {
|
|
|
3524
3733
|
};
|
|
3525
3734
|
|
|
3526
3735
|
// plugins/core/analyzers/trigger-collision/index.ts
|
|
3527
|
-
var
|
|
3736
|
+
var ID26 = "trigger-collision";
|
|
3528
3737
|
var ADVERTISING_KINDS = /* @__PURE__ */ new Set([
|
|
3529
3738
|
"command",
|
|
3530
3739
|
"skill",
|
|
3531
3740
|
"agent"
|
|
3532
3741
|
]);
|
|
3533
3742
|
var triggerCollisionAnalyzer = {
|
|
3534
|
-
id:
|
|
3743
|
+
id: ID26,
|
|
3535
3744
|
pluginId: CORE_PLUGIN_ID,
|
|
3536
3745
|
kind: "analyzer",
|
|
3537
3746
|
mode: "deterministic",
|
|
@@ -3629,7 +3838,7 @@ function analyzeTriggerBucket(normalized, claims) {
|
|
|
3629
3838
|
part: parts[0]
|
|
3630
3839
|
});
|
|
3631
3840
|
return {
|
|
3632
|
-
analyzerId:
|
|
3841
|
+
analyzerId: ID26,
|
|
3633
3842
|
severity: "error",
|
|
3634
3843
|
nodeIds,
|
|
3635
3844
|
message,
|
|
@@ -3669,13 +3878,13 @@ var ASCII_FORMATTER_TEXTS = {
|
|
|
3669
3878
|
};
|
|
3670
3879
|
|
|
3671
3880
|
// plugins/core/formatters/ascii/index.ts
|
|
3672
|
-
var
|
|
3881
|
+
var ID27 = "ascii";
|
|
3673
3882
|
var KIND_ORDER = ["agent", "command", "skill", "markdown"];
|
|
3674
3883
|
var asciiFormatter = {
|
|
3675
|
-
id:
|
|
3884
|
+
id: ID27,
|
|
3676
3885
|
pluginId: CORE_PLUGIN_ID,
|
|
3677
3886
|
kind: "formatter",
|
|
3678
|
-
formatId:
|
|
3887
|
+
formatId: ID27,
|
|
3679
3888
|
description: "Renders the scan as plain text in three sections: nodes (grouped by kind), arrows, and issues. Used by `sm scan --format ascii`.",
|
|
3680
3889
|
// ASCII tree formatter, header + per-kind sections + per-issue
|
|
3681
3890
|
// section. Each section iterates and renders; splitting per section
|
|
@@ -3769,13 +3978,13 @@ function renderSection(out, kind, group) {
|
|
|
3769
3978
|
}
|
|
3770
3979
|
|
|
3771
3980
|
// plugins/core/formatters/json/index.ts
|
|
3772
|
-
var
|
|
3981
|
+
var ID28 = "json";
|
|
3773
3982
|
var jsonFormatter = {
|
|
3774
|
-
id:
|
|
3983
|
+
id: ID28,
|
|
3775
3984
|
pluginId: CORE_PLUGIN_ID,
|
|
3776
3985
|
kind: "formatter",
|
|
3777
3986
|
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`.",
|
|
3778
|
-
formatId:
|
|
3987
|
+
formatId: ID28,
|
|
3779
3988
|
format(ctx) {
|
|
3780
3989
|
if (ctx.scanResult !== void 0) {
|
|
3781
3990
|
return JSON.stringify(ctx.scanResult);
|
|
@@ -3914,9 +4123,9 @@ function resolveSpecRoot2() {
|
|
|
3914
4123
|
}
|
|
3915
4124
|
|
|
3916
4125
|
// plugins/core/actions/node-bump/index.ts
|
|
3917
|
-
var
|
|
4126
|
+
var ID29 = "node-bump";
|
|
3918
4127
|
var nodeBumpAction = {
|
|
3919
|
-
id:
|
|
4128
|
+
id: ID29,
|
|
3920
4129
|
pluginId: CORE_PLUGIN_ID,
|
|
3921
4130
|
kind: "action",
|
|
3922
4131
|
description: "Marks a node as updated: bumps `annotations.version`, refreshes sidecar hashes, and records the timestamp.",
|
|
@@ -3973,45 +4182,166 @@ function pickCurrentVersion(overlay) {
|
|
|
3973
4182
|
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
3974
4183
|
}
|
|
3975
4184
|
|
|
4185
|
+
// plugins/core/actions/node-set-stability/index.ts
|
|
4186
|
+
var STABILITY_VALUES = ["experimental", "stable", "deprecated"];
|
|
4187
|
+
var ID30 = "node-set-stability";
|
|
4188
|
+
var nodeSetStabilityAction = {
|
|
4189
|
+
id: ID30,
|
|
4190
|
+
pluginId: CORE_PLUGIN_ID,
|
|
4191
|
+
kind: "action",
|
|
4192
|
+
description: "Sets the lifecycle stage of the current node (writes `stability` to the sidecar).",
|
|
4193
|
+
mode: "deterministic",
|
|
4194
|
+
// The runtime contract uses generic <TInput, TReport>; this narrows
|
|
4195
|
+
// both. The cast is the standard pattern for built-ins that want
|
|
4196
|
+
// typed local I/O while staying compatible with the open generic.
|
|
4197
|
+
invoke(rawInput, ctx) {
|
|
4198
|
+
const input = rawInput ?? {};
|
|
4199
|
+
return invokeSetStability(input, ctx);
|
|
4200
|
+
}
|
|
4201
|
+
};
|
|
4202
|
+
function invokeSetStability(input, ctx) {
|
|
4203
|
+
const stability = input.stability;
|
|
4204
|
+
if (!STABILITY_VALUES.includes(stability)) {
|
|
4205
|
+
return { report: { ok: false, reason: "invalid" } };
|
|
4206
|
+
}
|
|
4207
|
+
const timestamp = ctx.now().toISOString();
|
|
4208
|
+
const write = {
|
|
4209
|
+
kind: "sidecar",
|
|
4210
|
+
path: sidecarPathFor(ctx.nodeAbsolutePath),
|
|
4211
|
+
changes: {
|
|
4212
|
+
identity: {
|
|
4213
|
+
path: ctx.node.path,
|
|
4214
|
+
bodyHash: ctx.node.bodyHash,
|
|
4215
|
+
frontmatterHash: ctx.node.frontmatterHash
|
|
4216
|
+
},
|
|
4217
|
+
annotations: { stability },
|
|
4218
|
+
audit: {
|
|
4219
|
+
lastBumpedAt: timestamp,
|
|
4220
|
+
lastBumpedBy: ctx.invoker
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
};
|
|
4224
|
+
const report = { ok: true, stability };
|
|
4225
|
+
return { report, writes: [write] };
|
|
4226
|
+
}
|
|
4227
|
+
|
|
4228
|
+
// plugins/core/actions/node-set-tags/index.ts
|
|
4229
|
+
var ID31 = "node-set-tags";
|
|
4230
|
+
var nodeSetTagsAction = {
|
|
4231
|
+
id: ID31,
|
|
4232
|
+
pluginId: CORE_PLUGIN_ID,
|
|
4233
|
+
kind: "action",
|
|
4234
|
+
description: "Sets the taxonomy tags of the current node (writes `tags` to the sidecar; whole-array replace).",
|
|
4235
|
+
mode: "deterministic",
|
|
4236
|
+
// The runtime contract uses generic <TInput, TReport>; this narrows
|
|
4237
|
+
// both. The cast is the standard pattern for built-ins that want
|
|
4238
|
+
// typed local I/O while staying compatible with the open generic.
|
|
4239
|
+
invoke(rawInput, ctx) {
|
|
4240
|
+
const input = rawInput ?? {};
|
|
4241
|
+
return invokeSetTags(input, ctx);
|
|
4242
|
+
}
|
|
4243
|
+
};
|
|
4244
|
+
function invokeSetTags(input, ctx) {
|
|
4245
|
+
const tags = Array.isArray(input.tags) ? input.tags : [];
|
|
4246
|
+
const timestamp = ctx.now().toISOString();
|
|
4247
|
+
const write = {
|
|
4248
|
+
kind: "sidecar",
|
|
4249
|
+
path: sidecarPathFor(ctx.nodeAbsolutePath),
|
|
4250
|
+
changes: {
|
|
4251
|
+
identity: {
|
|
4252
|
+
path: ctx.node.path,
|
|
4253
|
+
bodyHash: ctx.node.bodyHash,
|
|
4254
|
+
frontmatterHash: ctx.node.frontmatterHash
|
|
4255
|
+
},
|
|
4256
|
+
annotations: { tags },
|
|
4257
|
+
audit: {
|
|
4258
|
+
lastBumpedAt: timestamp,
|
|
4259
|
+
lastBumpedBy: ctx.invoker
|
|
4260
|
+
}
|
|
4261
|
+
}
|
|
4262
|
+
};
|
|
4263
|
+
const report = { ok: true, tags };
|
|
4264
|
+
return { report, writes: [write] };
|
|
4265
|
+
}
|
|
4266
|
+
|
|
3976
4267
|
// plugins/core/actions/node-supersede/index.ts
|
|
3977
|
-
var
|
|
4268
|
+
var ID32 = "node-supersede";
|
|
3978
4269
|
var nodeSupersedeAction = {
|
|
3979
|
-
id:
|
|
4270
|
+
id: ID32,
|
|
3980
4271
|
pluginId: CORE_PLUGIN_ID,
|
|
3981
4272
|
kind: "action",
|
|
3982
4273
|
description: "Declares the current node as superseded by another (writes `supersededBy` to the sidecar).",
|
|
3983
4274
|
mode: "deterministic",
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
4275
|
+
// The runtime contract uses generic <TInput, TReport>; supersede
|
|
4276
|
+
// narrows both. The cast is the standard pattern for built-ins that
|
|
4277
|
+
// want typed local I/O while staying compatible with the open generic.
|
|
4278
|
+
invoke(rawInput, ctx) {
|
|
4279
|
+
const input = rawInput ?? {};
|
|
4280
|
+
return invokeSupersede(input, ctx);
|
|
3987
4281
|
}
|
|
3988
4282
|
};
|
|
4283
|
+
function invokeSupersede(input, ctx) {
|
|
4284
|
+
const supersededBy = input.supersededBy;
|
|
4285
|
+
if (supersededBy === ctx.node.path) {
|
|
4286
|
+
return { report: { ok: false, reason: "self" } };
|
|
4287
|
+
}
|
|
4288
|
+
const timestamp = ctx.now().toISOString();
|
|
4289
|
+
const write = {
|
|
4290
|
+
kind: "sidecar",
|
|
4291
|
+
path: sidecarPathFor(ctx.nodeAbsolutePath),
|
|
4292
|
+
changes: {
|
|
4293
|
+
identity: {
|
|
4294
|
+
path: ctx.node.path,
|
|
4295
|
+
bodyHash: ctx.node.bodyHash,
|
|
4296
|
+
frontmatterHash: ctx.node.frontmatterHash
|
|
4297
|
+
},
|
|
4298
|
+
annotations: { supersededBy },
|
|
4299
|
+
audit: {
|
|
4300
|
+
lastBumpedAt: timestamp,
|
|
4301
|
+
lastBumpedBy: ctx.invoker
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4304
|
+
};
|
|
4305
|
+
const report = { ok: true, supersededBy };
|
|
4306
|
+
return { report, writes: [write] };
|
|
4307
|
+
}
|
|
3989
4308
|
|
|
3990
4309
|
// kernel/update-check/index.ts
|
|
3991
4310
|
var SEMVER_SHAPE_RE = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
3992
4311
|
async function fetchLatestVersion(pkg, opts) {
|
|
3993
4312
|
const controller = new AbortController();
|
|
3994
|
-
const
|
|
4313
|
+
const abortTimer = setTimeout(() => controller.abort(), opts.timeoutMs);
|
|
4314
|
+
let capTimer;
|
|
4315
|
+
const hardCap = new Promise((_resolve, reject) => {
|
|
4316
|
+
capTimer = setTimeout(
|
|
4317
|
+
() => reject(new Error(`update check timed out after ${opts.timeoutMs}ms`)),
|
|
4318
|
+
opts.timeoutMs
|
|
4319
|
+
);
|
|
4320
|
+
});
|
|
3995
4321
|
try {
|
|
3996
|
-
|
|
3997
|
-
const response = await fetch(url, {
|
|
3998
|
-
signal: controller.signal,
|
|
3999
|
-
headers: { accept: "application/json" }
|
|
4000
|
-
});
|
|
4001
|
-
if (!response.ok) {
|
|
4002
|
-
throw new Error(`registry returned status ${response.status}`);
|
|
4003
|
-
}
|
|
4004
|
-
const payload = await response.json();
|
|
4005
|
-
if (typeof payload.version !== "string" || payload.version.length === 0) {
|
|
4006
|
-
throw new Error("registry payload missing string `version`");
|
|
4007
|
-
}
|
|
4008
|
-
if (!SEMVER_SHAPE_RE.test(payload.version)) {
|
|
4009
|
-
throw new Error("registry payload `version` is not a semver-shaped string");
|
|
4010
|
-
}
|
|
4011
|
-
return payload.version;
|
|
4322
|
+
return await Promise.race([fetchVersion(pkg, controller.signal), hardCap]);
|
|
4012
4323
|
} finally {
|
|
4013
|
-
clearTimeout(
|
|
4324
|
+
clearTimeout(abortTimer);
|
|
4325
|
+
if (capTimer !== void 0) clearTimeout(capTimer);
|
|
4326
|
+
}
|
|
4327
|
+
}
|
|
4328
|
+
async function fetchVersion(pkg, signal) {
|
|
4329
|
+
const url = `https://registry.npmjs.org/${pkg}/latest`;
|
|
4330
|
+
const response = await fetch(url, {
|
|
4331
|
+
signal,
|
|
4332
|
+
headers: { accept: "application/json" }
|
|
4333
|
+
});
|
|
4334
|
+
if (!response.ok) {
|
|
4335
|
+
throw new Error(`registry returned status ${response.status}`);
|
|
4014
4336
|
}
|
|
4337
|
+
const payload = await response.json();
|
|
4338
|
+
if (typeof payload.version !== "string" || payload.version.length === 0) {
|
|
4339
|
+
throw new Error("registry payload missing string `version`");
|
|
4340
|
+
}
|
|
4341
|
+
if (!SEMVER_SHAPE_RE.test(payload.version)) {
|
|
4342
|
+
throw new Error("registry payload `version` is not a semver-shaped string");
|
|
4343
|
+
}
|
|
4344
|
+
return payload.version;
|
|
4015
4345
|
}
|
|
4016
4346
|
function compareVersions(a, b) {
|
|
4017
4347
|
const pa = parseSemver(a);
|
|
@@ -4480,10 +4810,14 @@ var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", v
|
|
|
4480
4810
|
var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: VERSION };
|
|
4481
4811
|
var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: VERSION };
|
|
4482
4812
|
var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: VERSION };
|
|
4813
|
+
var supersedeAnalyzer2 = { ...supersedeAnalyzer, pluginId: "core", version: VERSION };
|
|
4814
|
+
var tagsAnalyzer2 = { ...tagsAnalyzer, pluginId: "core", version: VERSION };
|
|
4483
4815
|
var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: VERSION };
|
|
4484
4816
|
var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: VERSION };
|
|
4485
4817
|
var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: VERSION };
|
|
4486
4818
|
var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: VERSION };
|
|
4819
|
+
var nodeSetStabilityAction2 = { ...nodeSetStabilityAction, pluginId: "core", version: VERSION };
|
|
4820
|
+
var nodeSetTagsAction2 = { ...nodeSetTagsAction, pluginId: "core", version: VERSION };
|
|
4487
4821
|
var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: VERSION };
|
|
4488
4822
|
var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: VERSION };
|
|
4489
4823
|
var builtInPlugins = [
|
|
@@ -4543,10 +4877,14 @@ var builtInPlugins = [
|
|
|
4543
4877
|
referenceRedundantAnalyzer2,
|
|
4544
4878
|
schemaViolationAnalyzer2,
|
|
4545
4879
|
signalCollisionAnalyzer2,
|
|
4880
|
+
supersedeAnalyzer2,
|
|
4881
|
+
tagsAnalyzer2,
|
|
4546
4882
|
triggerCollisionAnalyzer2,
|
|
4547
4883
|
asciiFormatter2,
|
|
4548
4884
|
jsonFormatter2,
|
|
4549
4885
|
nodeBumpAction2,
|
|
4886
|
+
nodeSetStabilityAction2,
|
|
4887
|
+
nodeSetTagsAction2,
|
|
4550
4888
|
nodeSupersedeAction2,
|
|
4551
4889
|
updateCheckHook2
|
|
4552
4890
|
]
|
|
@@ -5803,13 +6141,16 @@ function ensureSidecarWritesAllowed(opts) {
|
|
|
5803
6141
|
default: false
|
|
5804
6142
|
});
|
|
5805
6143
|
if (allowed === true) return;
|
|
5806
|
-
if (opts.
|
|
6144
|
+
if (opts.always === true) {
|
|
5807
6145
|
writeConfigValue("allowEditSmFiles", true, {
|
|
5808
6146
|
target: "project-local",
|
|
5809
6147
|
cwd: opts.cwd
|
|
5810
6148
|
});
|
|
5811
6149
|
return;
|
|
5812
6150
|
}
|
|
6151
|
+
if (opts.confirm === true) {
|
|
6152
|
+
return;
|
|
6153
|
+
}
|
|
5813
6154
|
throw new EConsentRequiredError({
|
|
5814
6155
|
key: "allowEditSmFiles",
|
|
5815
6156
|
hintTarget: "project-local"
|
|
@@ -6069,6 +6410,17 @@ function ensureGitForStaged(cwd) {
|
|
|
6069
6410
|
}
|
|
6070
6411
|
return "ok";
|
|
6071
6412
|
}
|
|
6413
|
+
function resolveGitAuthorName(cwd) {
|
|
6414
|
+
if (!isInsideGitRepo(cwd)) return null;
|
|
6415
|
+
const result = spawnSync("git", ["config", "user.name"], {
|
|
6416
|
+
cwd,
|
|
6417
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
6418
|
+
encoding: "utf8"
|
|
6419
|
+
});
|
|
6420
|
+
if (result.error !== void 0 || result.status !== 0) return null;
|
|
6421
|
+
const name = (result.stdout ?? "").trim();
|
|
6422
|
+
return name.length > 0 ? name : null;
|
|
6423
|
+
}
|
|
6072
6424
|
function stageSidecar(cwd, sidecarAbsPath) {
|
|
6073
6425
|
const result = spawnSync("git", ["add", "--", sidecarAbsPath], {
|
|
6074
6426
|
cwd,
|
|
@@ -7423,7 +7775,7 @@ async function loadPluginOverrideMap(db) {
|
|
|
7423
7775
|
}
|
|
7424
7776
|
|
|
7425
7777
|
// kernel/util/enum-parsers.ts
|
|
7426
|
-
var
|
|
7778
|
+
var STABILITY_VALUES2 = Object.freeze([
|
|
7427
7779
|
"experimental",
|
|
7428
7780
|
"stable",
|
|
7429
7781
|
"deprecated"
|
|
@@ -7746,6 +8098,41 @@ async function replaceAllScanContributions(trx, contributions, livePaths = /* @_
|
|
|
7746
8098
|
await sweepPerTupleContributions(trx, contributions, freshlyRunTuples);
|
|
7747
8099
|
await upsertContributionsBuffer(trx, contributions);
|
|
7748
8100
|
}
|
|
8101
|
+
async function replaceAllScanContributionErrors(trx, contributionErrors) {
|
|
8102
|
+
await trx.deleteFrom("scan_contribution_errors").execute();
|
|
8103
|
+
if (contributionErrors.length === 0) return;
|
|
8104
|
+
const CHUNK = 400;
|
|
8105
|
+
for (let i = 0; i < contributionErrors.length; i += CHUNK) {
|
|
8106
|
+
const slice = contributionErrors.slice(i, i + CHUNK);
|
|
8107
|
+
const rows = slice.map((e) => ({
|
|
8108
|
+
pluginId: e.pluginId,
|
|
8109
|
+
extensionId: e.extensionId,
|
|
8110
|
+
nodePath: e.nodePath,
|
|
8111
|
+
reason: e.reason,
|
|
8112
|
+
message: e.message,
|
|
8113
|
+
contributionId: e.contributionId ?? null,
|
|
8114
|
+
slot: e.slot ?? null,
|
|
8115
|
+
emittedAt: e.emittedAt
|
|
8116
|
+
}));
|
|
8117
|
+
await trx.insertInto("scan_contribution_errors").values(rows).execute();
|
|
8118
|
+
}
|
|
8119
|
+
}
|
|
8120
|
+
async function listAllContributionErrors(db) {
|
|
8121
|
+
const rows = await db.selectFrom("scan_contribution_errors").selectAll().orderBy("pluginId", "asc").orderBy("extensionId", "asc").orderBy("nodePath", "asc").orderBy("emittedAt", "asc").execute();
|
|
8122
|
+
return rows.map(rowToContributionError);
|
|
8123
|
+
}
|
|
8124
|
+
function rowToContributionError(row) {
|
|
8125
|
+
return {
|
|
8126
|
+
pluginId: row.pluginId,
|
|
8127
|
+
extensionId: row.extensionId,
|
|
8128
|
+
nodePath: row.nodePath,
|
|
8129
|
+
reason: row.reason,
|
|
8130
|
+
message: row.message,
|
|
8131
|
+
...row.contributionId !== null ? { contributionId: row.contributionId } : {},
|
|
8132
|
+
...row.slot !== null ? { slot: row.slot } : {},
|
|
8133
|
+
emittedAt: row.emittedAt
|
|
8134
|
+
};
|
|
8135
|
+
}
|
|
7749
8136
|
async function sweepOrphanContributions(trx, livePaths) {
|
|
7750
8137
|
if (livePaths.size > 0) {
|
|
7751
8138
|
await trx.deleteFrom("scan_contributions").where("nodePath", "not in", [...livePaths]).execute();
|
|
@@ -7955,7 +8342,7 @@ async function findNodesByTag(db, tag) {
|
|
|
7955
8342
|
}
|
|
7956
8343
|
|
|
7957
8344
|
// kernel/adapters/sqlite/scan-persistence.ts
|
|
7958
|
-
async function persistScanResult(db, result, renameOps = [], extractorRuns = [], enrichments = [], contributions = [], registeredContributionKeys = /* @__PURE__ */ new Set(), freshlyRunTuples = /* @__PURE__ */ new Set()) {
|
|
8345
|
+
async function persistScanResult(db, result, renameOps = [], extractorRuns = [], enrichments = [], contributions = [], registeredContributionKeys = /* @__PURE__ */ new Set(), freshlyRunTuples = /* @__PURE__ */ new Set(), contributionErrors = []) {
|
|
7959
8346
|
const scannedAt = validateScannedAt(result.scannedAt);
|
|
7960
8347
|
const renames = [];
|
|
7961
8348
|
await db.transaction().execute(async (trx) => {
|
|
@@ -7970,6 +8357,7 @@ async function persistScanResult(db, result, renameOps = [], extractorRuns = [],
|
|
|
7970
8357
|
registeredContributionKeys,
|
|
7971
8358
|
freshlyRunTuples
|
|
7972
8359
|
);
|
|
8360
|
+
await replaceAllScanContributionErrors(trx, contributionErrors);
|
|
7973
8361
|
const tagRecords = nodesToTagRecords(result.nodes);
|
|
7974
8362
|
await replaceAllScanTags(trx, tagRecords, livePathsForContrib);
|
|
7975
8363
|
await upsertEnrichmentLayer(trx, result, renameOps, enrichments);
|
|
@@ -8414,7 +8802,8 @@ var SqliteStorageAdapter = class {
|
|
|
8414
8802
|
listForNode: (nodePath) => loadContributionsForNode(this.db, nodePath),
|
|
8415
8803
|
listForPaths: (paths) => loadContributionsForPaths(this.db, paths),
|
|
8416
8804
|
lookup: (pluginId, contributionId, nodePath, extensionId) => loadContributionLookup(this.db, pluginId, contributionId, nodePath, extensionId),
|
|
8417
|
-
purgeByPlugin: (pluginId, extensionId) => purgeContributionsByPlugin(this.db, pluginId, extensionId)
|
|
8805
|
+
purgeByPlugin: (pluginId, extensionId) => purgeContributionsByPlugin(this.db, pluginId, extensionId),
|
|
8806
|
+
listAllErrors: () => listAllContributionErrors(this.db)
|
|
8418
8807
|
};
|
|
8419
8808
|
this.tags = {
|
|
8420
8809
|
listForNode: (nodePath) => loadTagsForNode(this.db, nodePath),
|
|
@@ -8487,7 +8876,8 @@ async function persistScansThroughNonTx(db, result, opts) {
|
|
|
8487
8876
|
defaults.enrichments,
|
|
8488
8877
|
defaults.contributions,
|
|
8489
8878
|
defaults.registeredContributionKeys,
|
|
8490
|
-
defaults.freshlyRunTuples
|
|
8879
|
+
defaults.freshlyRunTuples,
|
|
8880
|
+
defaults.contributionErrors
|
|
8491
8881
|
);
|
|
8492
8882
|
}
|
|
8493
8883
|
function applyPersistDefaults(opts) {
|
|
@@ -8498,6 +8888,7 @@ function applyPersistDefaults(opts) {
|
|
|
8498
8888
|
contributions: [],
|
|
8499
8889
|
registeredContributionKeys: /* @__PURE__ */ new Set(),
|
|
8500
8890
|
freshlyRunTuples: /* @__PURE__ */ new Set(),
|
|
8891
|
+
contributionErrors: [],
|
|
8501
8892
|
...opts
|
|
8502
8893
|
};
|
|
8503
8894
|
}
|
|
@@ -8662,7 +9053,8 @@ function buildTxSubset(trx) {
|
|
|
8662
9053
|
d.enrichments,
|
|
8663
9054
|
d.contributions,
|
|
8664
9055
|
d.registeredContributionKeys,
|
|
8665
|
-
d.freshlyRunTuples
|
|
9056
|
+
d.freshlyRunTuples,
|
|
9057
|
+
d.contributionErrors
|
|
8666
9058
|
).then(() => void 0);
|
|
8667
9059
|
}
|
|
8668
9060
|
},
|
|
@@ -8990,13 +9382,14 @@ function isAllowedLstatError(err) {
|
|
|
8990
9382
|
|
|
8991
9383
|
// cli/commands/bump-plan.ts
|
|
8992
9384
|
function computeBumpPlan(nodes, options) {
|
|
9385
|
+
const invoker = resolveGitAuthorName(options.cwd) ?? "cli";
|
|
8993
9386
|
const items = [];
|
|
8994
9387
|
for (const node of nodes) {
|
|
8995
|
-
items.push(planOne(node, options));
|
|
9388
|
+
items.push(planOne(node, options, invoker));
|
|
8996
9389
|
}
|
|
8997
9390
|
return { items };
|
|
8998
9391
|
}
|
|
8999
|
-
function planOne(node, options) {
|
|
9392
|
+
function planOne(node, options, invoker) {
|
|
9000
9393
|
let absPath;
|
|
9001
9394
|
try {
|
|
9002
9395
|
assertContained(options.cwd, node.path);
|
|
@@ -9010,7 +9403,7 @@ function planOne(node, options) {
|
|
|
9010
9403
|
}
|
|
9011
9404
|
let result;
|
|
9012
9405
|
try {
|
|
9013
|
-
result = invokeBumpFor(node, absPath, options.force);
|
|
9406
|
+
result = invokeBumpFor(node, absPath, options.force, invoker);
|
|
9014
9407
|
} catch (err) {
|
|
9015
9408
|
return {
|
|
9016
9409
|
nodePath: node.path,
|
|
@@ -9032,7 +9425,7 @@ function planOne(node, options) {
|
|
|
9032
9425
|
report: result.report
|
|
9033
9426
|
};
|
|
9034
9427
|
}
|
|
9035
|
-
function invokeBumpFor(node, absPath, force) {
|
|
9428
|
+
function invokeBumpFor(node, absPath, force, invoker) {
|
|
9036
9429
|
if (!nodeBumpAction.invoke) {
|
|
9037
9430
|
throw new Error("built-in bump action is missing its invoke()");
|
|
9038
9431
|
}
|
|
@@ -9041,7 +9434,7 @@ function invokeBumpFor(node, absPath, force) {
|
|
|
9041
9434
|
return nodeBumpAction.invoke(input, {
|
|
9042
9435
|
node,
|
|
9043
9436
|
nodeAbsolutePath: absPath,
|
|
9044
|
-
invoker
|
|
9437
|
+
invoker,
|
|
9045
9438
|
now: () => /* @__PURE__ */ new Date(),
|
|
9046
9439
|
settings: {}
|
|
9047
9440
|
});
|
|
@@ -9156,10 +9549,11 @@ var BumpCommand = class extends SmCommand {
|
|
|
9156
9549
|
* `EConsentRequiredError` thrown by `FilesystemSidecarStore.applyPatch`
|
|
9157
9550
|
* (via `ensureSidecarWritesAllowed`), prompt the operator if stdin is
|
|
9158
9551
|
* a TTY and `--yes` was not passed. On accept, flip `this.yes` to
|
|
9159
|
-
* true and re-run `dispatch` (the second pass passes `
|
|
9160
|
-
* to the store and the gate persists the flag to project-local
|
|
9161
|
-
*
|
|
9162
|
-
*
|
|
9552
|
+
* true and re-run `dispatch` (the second pass passes `always: true`
|
|
9553
|
+
* to the store and the gate persists the flag to project-local, the
|
|
9554
|
+
* CLI's documented "never asked again" contract, Step 17 consent
|
|
9555
|
+
* split). On decline or non-TTY without `--yes`, print a directed
|
|
9556
|
+
* message and return `ExitCode.Error`.
|
|
9163
9557
|
*/
|
|
9164
9558
|
async #runWithConsent(ansi, dispatch) {
|
|
9165
9559
|
try {
|
|
@@ -9258,7 +9652,11 @@ var BumpCommand = class extends SmCommand {
|
|
|
9258
9652
|
async #applyBumpedSingle(item, node, ansi) {
|
|
9259
9653
|
const ctx = defaultRuntimeContext();
|
|
9260
9654
|
const consent = {
|
|
9655
|
+
// Step 17 split: the CLI's accept / `--yes` persists the grant
|
|
9656
|
+
// (its documented "never asked again" contract), so it threads
|
|
9657
|
+
// `always`, not the new one-shot `confirm`.
|
|
9261
9658
|
confirm: this.yes,
|
|
9659
|
+
always: this.yes,
|
|
9262
9660
|
cwd: ctx.cwd
|
|
9263
9661
|
};
|
|
9264
9662
|
const applied = await applyBumpWrites(item, consent);
|
|
@@ -9348,7 +9746,9 @@ var BumpCommand = class extends SmCommand {
|
|
|
9348
9746
|
const store = new FilesystemSidecarStore(ensureSidecarWritesAllowed);
|
|
9349
9747
|
const ctx = defaultRuntimeContext();
|
|
9350
9748
|
const consent = {
|
|
9749
|
+
// Step 17 split: CLI accept / `--yes` persists (see #runSingle).
|
|
9351
9750
|
confirm: this.yes,
|
|
9751
|
+
always: this.yes,
|
|
9352
9752
|
cwd: ctx.cwd
|
|
9353
9753
|
};
|
|
9354
9754
|
const outcomes = [];
|
|
@@ -9577,16 +9977,23 @@ function readConformanceKillSwitches(env = process.env) {
|
|
|
9577
9977
|
}
|
|
9578
9978
|
|
|
9579
9979
|
// kernel/i18n/plugin-loader.texts.ts
|
|
9980
|
+
var SPEC_GITHUB_BASE = "https://github.com/crystian/skill-map/blob/main";
|
|
9580
9981
|
var PLUGIN_LOADER_TEXTS = {
|
|
9581
9982
|
invalidManifestJsonParse: "{{manifestPath}}: {{errDescription}}. Validate the JSON (e.g. `npx jsonlint plugin.json`).",
|
|
9582
|
-
invalidManifestAjv:
|
|
9983
|
+
invalidManifestAjv: `{{manifestPath}}: {{errors}}. See ${SPEC_GITHUB_BASE}/spec/schemas/plugins-registry.schema.json#/$defs/PluginManifest.`,
|
|
9583
9984
|
invalidSpecCompat: 'specCompat "{{specCompat}}" is not a valid semver range. Use a range like "^1.0.0".',
|
|
9584
9985
|
incompatibleSpec: `@skill-map/spec {{installedSpecVersion}} does not satisfy specCompat "{{specCompat}}". Either update the plugin's specCompat (and re-test) or pin sm to a compatible spec version.`,
|
|
9585
9986
|
loadErrorFileNotFound: "extension file not found: {{relEntry}} (resolved to {{abs}}). Check plugin.json#/extensions paths.",
|
|
9586
9987
|
loadErrorImportFailed: "{{relEntry}}: import failed: {{errDescription}}",
|
|
9587
9988
|
loadErrorMissingKind: "{{relEntry}}: default export missing a string `kind` field. Expected one of: {{knownKindsList}}.",
|
|
9588
9989
|
loadErrorUnknownKind: '{{relEntry}}: unknown extension kind "{{kindReceived}}". Expected one of: {{knownKindsList}}.',
|
|
9589
|
-
|
|
9990
|
+
// No "manifest invalid" framing here: the warning wrapper already
|
|
9991
|
+
// carries the `(invalid-manifest)` status, so this reason is just the
|
|
9992
|
+
// file + the specific error + the doc link. `{{docUrl}}` is chosen by
|
|
9993
|
+
// the caller: a bad view-slot value points to the slot catalog
|
|
9994
|
+
// (`spec/view-slots.md`), every other manifest-shape error to the kind
|
|
9995
|
+
// schema. Both are GitHub blob URLs.
|
|
9996
|
+
invalidManifestExtensionShape: "{{relEntry}}: {{errors}}. See {{docUrl}}.",
|
|
9590
9997
|
importExceededTimeout: "import exceeded {{timeoutMs}}ms; likely a top-level side effect (network call, infinite loop, large blocking work). Move side effects into the runtime methods (`detect` / `evaluate` / `render` / etc.).",
|
|
9591
9998
|
disabledByConfig: "disabled by config_plugins or settings.json",
|
|
9592
9999
|
invalidManifestDirMismatch: "directory name '{{dirName}}' does not match manifest id '{{manifestId}}'. Rename the directory to match the id, or update the manifest id to match the directory.",
|
|
@@ -9918,7 +10325,7 @@ function loadOneProviderKind(opts) {
|
|
|
9918
10325
|
opts.pluginPath,
|
|
9919
10326
|
opts.pluginId,
|
|
9920
10327
|
"invalid-manifest",
|
|
9921
|
-
`Provider kind \`${opts.entry}\` (declared at \`${opts.relEntry}\`) failed validation in \`kinds/${opts.entry}/kind.json\`: ${validation.errors}. See spec/schemas/extensions/provider-kind.schema.json.`
|
|
10328
|
+
`Provider kind \`${opts.entry}\` (declared at \`${opts.relEntry}\`) failed validation in \`kinds/${opts.entry}/kind.json\`: ${validation.errors}. See ${SPEC_GITHUB_BASE}/spec/schemas/extensions/provider-kind.schema.json.`
|
|
9922
10329
|
),
|
|
9923
10330
|
manifest: opts.manifest
|
|
9924
10331
|
}
|
|
@@ -10340,13 +10747,17 @@ var PluginLoader = class {
|
|
|
10340
10747
|
}
|
|
10341
10748
|
const extValidator = this.#options.validators.validatorForExtension(kind);
|
|
10342
10749
|
if (!extValidator(manifestView)) {
|
|
10343
|
-
const errors = (extValidator.errors
|
|
10750
|
+
const errors = formatAjvErrors(extValidator.errors);
|
|
10751
|
+
const slotError = (extValidator.errors ?? []).some(
|
|
10752
|
+
(e) => (e.instancePath ?? "").endsWith("/slot")
|
|
10753
|
+
);
|
|
10754
|
+
const docUrl = slotError ? `${SPEC_GITHUB_BASE}/spec/view-slots.md` : `${SPEC_GITHUB_BASE}/spec/schemas/extensions/${kind}.schema.json`;
|
|
10344
10755
|
return { ok: false, failure: {
|
|
10345
10756
|
...fail(
|
|
10346
10757
|
pluginPath,
|
|
10347
10758
|
pluginId,
|
|
10348
10759
|
"invalid-manifest",
|
|
10349
|
-
tx(PLUGIN_LOADER_TEXTS.invalidManifestExtensionShape, { relEntry,
|
|
10760
|
+
tx(PLUGIN_LOADER_TEXTS.invalidManifestExtensionShape, { relEntry, errors, docUrl })
|
|
10350
10761
|
),
|
|
10351
10762
|
manifest
|
|
10352
10763
|
} };
|
|
@@ -10824,6 +11235,9 @@ function collectViewContributions(pluginId, extensionId, instance, out, options
|
|
|
10824
11235
|
const raw = instance["ui"];
|
|
10825
11236
|
if (typeof raw !== "object" || raw === null) return;
|
|
10826
11237
|
const exclude = options.excludeQualifiedIds;
|
|
11238
|
+
const pluginOrder = options.pluginOrder;
|
|
11239
|
+
const extOrder = instance["order"];
|
|
11240
|
+
const extensionOrder = typeof extOrder === "number" ? extOrder : void 0;
|
|
10827
11241
|
for (const [contributionId, value] of Object.entries(raw)) {
|
|
10828
11242
|
if (typeof value !== "object" || value === null) continue;
|
|
10829
11243
|
const entry = value;
|
|
@@ -10842,13 +11256,15 @@ function collectViewContributions(pluginId, extensionId, instance, out, options
|
|
|
10842
11256
|
...typeof entry.icon === "string" ? { icon: entry.icon } : {},
|
|
10843
11257
|
...typeof entry.emptyText === "string" ? { emptyText: entry.emptyText } : {},
|
|
10844
11258
|
...typeof entry.priority === "number" ? { priority: entry.priority } : {},
|
|
11259
|
+
...typeof pluginOrder === "number" ? { pluginOrder } : {},
|
|
11260
|
+
...typeof extensionOrder === "number" ? { extensionOrder } : {},
|
|
10845
11261
|
emitWhenEmpty: entry.emitWhenEmpty === true
|
|
10846
11262
|
});
|
|
10847
11263
|
}
|
|
10848
11264
|
}
|
|
10849
11265
|
|
|
10850
11266
|
// core/runtime/plugin-runtime/bucketing.ts
|
|
10851
|
-
function bucketLoaded(loaded, runtime) {
|
|
11267
|
+
function bucketLoaded(loaded, runtime, pluginOrder) {
|
|
10852
11268
|
for (const ext of loaded) {
|
|
10853
11269
|
const instance = ext.instance;
|
|
10854
11270
|
if (!isExtensionInstance(instance)) continue;
|
|
@@ -10869,7 +11285,9 @@ function bucketLoaded(loaded, runtime) {
|
|
|
10869
11285
|
...ext.entryPath ? { entry: ext.entryPath } : {}
|
|
10870
11286
|
});
|
|
10871
11287
|
collectAnnotationContributions(ext.pluginId, instance, runtime.annotationContributions);
|
|
10872
|
-
collectViewContributions(ext.pluginId, ext.id, instance, runtime.viewContributions
|
|
11288
|
+
collectViewContributions(ext.pluginId, ext.id, instance, runtime.viewContributions, {
|
|
11289
|
+
...typeof pluginOrder === "number" ? { pluginOrder } : {}
|
|
11290
|
+
});
|
|
10873
11291
|
}
|
|
10874
11292
|
}
|
|
10875
11293
|
function collectAnnotationContributions(pluginId, instance, out) {
|
|
@@ -10915,11 +11333,14 @@ function truncateTail(s, max) {
|
|
|
10915
11333
|
// core/runtime/i18n/plugin-runtime.texts.ts
|
|
10916
11334
|
var PLUGIN_RUNTIME_TEXTS = {
|
|
10917
11335
|
/**
|
|
10918
|
-
* Stderr-ready warning for one non-loaded plugin.
|
|
10919
|
-
*
|
|
10920
|
-
* `
|
|
11336
|
+
* Stderr-ready warning for one non-loaded plugin. The status in parens
|
|
11337
|
+
* stays greppable (`invalid-manifest` / `incompatible-spec` /
|
|
11338
|
+
* `load-error`); `all extensions skipped` states the consequence (the
|
|
11339
|
+
* loader rejects the plugin whole, aborting on the first bad
|
|
11340
|
+
* extension). The reason carries the specifics, so it must NOT restate
|
|
11341
|
+
* the status (no second "manifest invalid").
|
|
10921
11342
|
*/
|
|
10922
|
-
warningRow: "plugin {{id}}
|
|
11343
|
+
warningRow: "plugin {{id}} ({{status}}), all extensions skipped: {{reason}}",
|
|
10923
11344
|
/** Placeholder when a non-loaded plugin record carries no `reason`. */
|
|
10924
11345
|
warningReasonMissing: "(no reason recorded)"
|
|
10925
11346
|
};
|
|
@@ -10981,7 +11402,7 @@ async function loadPluginRuntime(opts = {}) {
|
|
|
10981
11402
|
};
|
|
10982
11403
|
for (const plugin of discovered) {
|
|
10983
11404
|
if (plugin.status === "enabled") {
|
|
10984
|
-
bucketLoaded(plugin.extensions ?? [], runtime);
|
|
11405
|
+
bucketLoaded(plugin.extensions ?? [], runtime, plugin.manifest?.order);
|
|
10985
11406
|
continue;
|
|
10986
11407
|
}
|
|
10987
11408
|
if (plugin.status === "disabled") continue;
|
|
@@ -13319,8 +13740,8 @@ var DbResetCommand = class extends SmCommand {
|
|
|
13319
13740
|
return ExitCode.Ok;
|
|
13320
13741
|
}
|
|
13321
13742
|
const withCounts = rows.map((r) => {
|
|
13322
|
-
const
|
|
13323
|
-
return { name: r.name, rowCount: Number(
|
|
13743
|
+
const count3 = db.prepare(`SELECT COUNT(*) AS c FROM "${r.name}"`).get();
|
|
13744
|
+
return { name: r.name, rowCount: Number(count3.c) };
|
|
13324
13745
|
});
|
|
13325
13746
|
const totalRows = withCounts.reduce((acc, r) => acc + r.rowCount, 0);
|
|
13326
13747
|
const lines = withCounts.map((r) => ` - ${r.name}: ${r.rowCount} row(s)`).join("\n");
|
|
@@ -15137,7 +15558,7 @@ var ORCHESTRATOR_TEXTS = {
|
|
|
15137
15558
|
frontmatterMalformedMissingClose: "Frontmatter in {{path}} opens with `---` but never closes (no matching `---` line at column 0 was found). The file was scanned as body-only and every metadata field was silently lost. Add a closing `---` line below the metadata block.",
|
|
15138
15559
|
extensionErrorLinkKindNotDeclared: 'Extractor "{{extractorId}}" emitted a link of kind "{{linkKind}}" outside its declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',
|
|
15139
15560
|
extensionErrorIssueInvalidSeverity: `Rule "{{analyzerId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
|
|
15140
|
-
|
|
15561
|
+
extensionErrorContributionUndeclaredRef: 'Extension "{{extractorId}}" emitted a view contribution on {{nodePath}} whose object is not one declared in its `ui` map (pass the declared const by reference, do not spread or inline it). Contribution dropped.',
|
|
15141
15562
|
extensionErrorContributionPayloadInvalid: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}}; payload failed the "{{slot}}" schema: {{errors}}. Contribution dropped.',
|
|
15142
15563
|
extensionErrorRecommendedActionMissing: 'Analyzer "{{analyzerId}}" declares recommendedAction "{{actionId}}" but no Action is registered under that qualified id. The analyzer stays registered; the recommendation will not surface in the inspector.',
|
|
15143
15564
|
runScanRootEmptyArray: "runScan: roots must contain at least one path (spec requires minItems: 1)",
|
|
@@ -15157,6 +15578,7 @@ async function runExtractorsForNode(opts) {
|
|
|
15157
15578
|
const externalLinks = [];
|
|
15158
15579
|
const enrichmentBuffer = /* @__PURE__ */ new Map();
|
|
15159
15580
|
const contributions = [];
|
|
15581
|
+
const contributionErrors = [];
|
|
15160
15582
|
const signals = [];
|
|
15161
15583
|
const virtualNodes = [];
|
|
15162
15584
|
const virtualNodePaths = /* @__PURE__ */ new Set();
|
|
@@ -15188,36 +15610,54 @@ async function runExtractorsForNode(opts) {
|
|
|
15188
15610
|
});
|
|
15189
15611
|
}
|
|
15190
15612
|
};
|
|
15191
|
-
const declaredContributions =
|
|
15192
|
-
const emitContribution = (
|
|
15193
|
-
const declared = declaredContributions.get(
|
|
15613
|
+
const declaredContributions = readDeclaredContributionRefs(extractor);
|
|
15614
|
+
const emitContribution = (ref, payload) => {
|
|
15615
|
+
const declared = typeof ref === "object" && ref !== null ? declaredContributions.get(ref) : void 0;
|
|
15194
15616
|
if (!declared) {
|
|
15617
|
+
const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUndeclaredRef, {
|
|
15618
|
+
extractorId: qualifiedId2,
|
|
15619
|
+
nodePath: opts.node.path
|
|
15620
|
+
});
|
|
15195
15621
|
emitExtensionError(opts.emitter, qualifiedId2, opts.node.path, {
|
|
15196
15622
|
phase: "emitContribution",
|
|
15197
|
-
|
|
15198
|
-
|
|
15199
|
-
|
|
15200
|
-
|
|
15201
|
-
|
|
15202
|
-
|
|
15203
|
-
|
|
15623
|
+
reason: "undeclared-contribution-ref",
|
|
15624
|
+
message
|
|
15625
|
+
});
|
|
15626
|
+
contributionErrors.push({
|
|
15627
|
+
pluginId: extractor.pluginId,
|
|
15628
|
+
extensionId: extractor.id,
|
|
15629
|
+
nodePath: opts.node.path,
|
|
15630
|
+
reason: "undeclared-contribution-ref",
|
|
15631
|
+
message,
|
|
15632
|
+
emittedAt: Date.now()
|
|
15204
15633
|
});
|
|
15205
15634
|
return;
|
|
15206
15635
|
}
|
|
15207
15636
|
const result = validators.validateContributionPayload(declared.slot, payload);
|
|
15208
15637
|
if (!result.ok) {
|
|
15638
|
+
const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
|
|
15639
|
+
extractorId: qualifiedId2,
|
|
15640
|
+
contributionId: declared.id,
|
|
15641
|
+
nodePath: opts.node.path,
|
|
15642
|
+
slot: declared.slot,
|
|
15643
|
+
errors: result.errors
|
|
15644
|
+
});
|
|
15209
15645
|
emitExtensionError(opts.emitter, qualifiedId2, opts.node.path, {
|
|
15210
15646
|
phase: "emitContribution",
|
|
15211
|
-
contributionId,
|
|
15647
|
+
contributionId: declared.id,
|
|
15212
15648
|
slot: declared.slot,
|
|
15213
15649
|
reason: result.errors,
|
|
15214
|
-
message
|
|
15215
|
-
|
|
15216
|
-
|
|
15217
|
-
|
|
15218
|
-
|
|
15219
|
-
|
|
15220
|
-
|
|
15650
|
+
message
|
|
15651
|
+
});
|
|
15652
|
+
contributionErrors.push({
|
|
15653
|
+
pluginId: extractor.pluginId,
|
|
15654
|
+
extensionId: extractor.id,
|
|
15655
|
+
nodePath: opts.node.path,
|
|
15656
|
+
reason: result.errors,
|
|
15657
|
+
message,
|
|
15658
|
+
contributionId: declared.id,
|
|
15659
|
+
slot: declared.slot,
|
|
15660
|
+
emittedAt: Date.now()
|
|
15221
15661
|
});
|
|
15222
15662
|
return;
|
|
15223
15663
|
}
|
|
@@ -15225,7 +15665,7 @@ async function runExtractorsForNode(opts) {
|
|
|
15225
15665
|
pluginId: extractor.pluginId,
|
|
15226
15666
|
extensionId: extractor.id,
|
|
15227
15667
|
nodePath: opts.node.path,
|
|
15228
|
-
contributionId,
|
|
15668
|
+
contributionId: declared.id,
|
|
15229
15669
|
slot: declared.slot,
|
|
15230
15670
|
payload,
|
|
15231
15671
|
emittedAt: Date.now()
|
|
@@ -15263,11 +15703,12 @@ async function runExtractorsForNode(opts) {
|
|
|
15263
15703
|
externalLinks,
|
|
15264
15704
|
enrichments: Array.from(enrichmentBuffer.values()),
|
|
15265
15705
|
contributions,
|
|
15706
|
+
contributionErrors,
|
|
15266
15707
|
signals,
|
|
15267
15708
|
virtualNodes
|
|
15268
15709
|
};
|
|
15269
15710
|
}
|
|
15270
|
-
function
|
|
15711
|
+
function readDeclaredContributionRefs(extension) {
|
|
15271
15712
|
const out = /* @__PURE__ */ new Map();
|
|
15272
15713
|
const raw = extension.ui;
|
|
15273
15714
|
if (typeof raw !== "object" || raw === null) return out;
|
|
@@ -15275,7 +15716,7 @@ function readDeclaredContributions(extension) {
|
|
|
15275
15716
|
if (typeof value !== "object" || value === null) continue;
|
|
15276
15717
|
const slot = value.slot;
|
|
15277
15718
|
if (typeof slot !== "string") continue;
|
|
15278
|
-
out.set(
|
|
15719
|
+
out.set(value, { id, slot });
|
|
15279
15720
|
}
|
|
15280
15721
|
return out;
|
|
15281
15722
|
}
|
|
@@ -15523,6 +15964,7 @@ function isExternalUrlLink(link) {
|
|
|
15523
15964
|
async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, registeredActionIds, emitter, hookDispatcher, reservedNodePaths, signals, seedIssues = []) {
|
|
15524
15965
|
const issues = [...seedIssues];
|
|
15525
15966
|
const contributions = [];
|
|
15967
|
+
const contributionErrors = [];
|
|
15526
15968
|
const validators = loadSchemaValidators();
|
|
15527
15969
|
void registeredActionIds;
|
|
15528
15970
|
const analyzerOrphans = orphanSidecars.map((o) => ({
|
|
@@ -15532,36 +15974,54 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
15532
15974
|
const scheduled = orderAnalyzersByPhase(analyzers);
|
|
15533
15975
|
for (const analyzer of scheduled) {
|
|
15534
15976
|
const qualifiedId2 = qualifiedExtensionId(analyzer.pluginId, analyzer.id);
|
|
15535
|
-
const declaredContributions =
|
|
15536
|
-
const emitContribution = (nodePath,
|
|
15537
|
-
const declared = declaredContributions.get(
|
|
15977
|
+
const declaredContributions = readDeclaredContributionRefs(analyzer);
|
|
15978
|
+
const emitContribution = (nodePath, ref, payload) => {
|
|
15979
|
+
const declared = typeof ref === "object" && ref !== null ? declaredContributions.get(ref) : void 0;
|
|
15538
15980
|
if (!declared) {
|
|
15981
|
+
const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUndeclaredRef, {
|
|
15982
|
+
extractorId: qualifiedId2,
|
|
15983
|
+
nodePath
|
|
15984
|
+
});
|
|
15539
15985
|
emitExtensionError(emitter, qualifiedId2, nodePath, {
|
|
15540
15986
|
phase: "emitContribution",
|
|
15541
|
-
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
15547
|
-
|
|
15987
|
+
reason: "undeclared-contribution-ref",
|
|
15988
|
+
message
|
|
15989
|
+
});
|
|
15990
|
+
contributionErrors.push({
|
|
15991
|
+
pluginId: analyzer.pluginId,
|
|
15992
|
+
extensionId: analyzer.id,
|
|
15993
|
+
nodePath,
|
|
15994
|
+
reason: "undeclared-contribution-ref",
|
|
15995
|
+
message,
|
|
15996
|
+
emittedAt: Date.now()
|
|
15548
15997
|
});
|
|
15549
15998
|
return;
|
|
15550
15999
|
}
|
|
15551
16000
|
const result = validators.validateContributionPayload(declared.slot, payload);
|
|
15552
16001
|
if (!result.ok) {
|
|
16002
|
+
const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
|
|
16003
|
+
extractorId: qualifiedId2,
|
|
16004
|
+
contributionId: declared.id,
|
|
16005
|
+
nodePath,
|
|
16006
|
+
slot: declared.slot,
|
|
16007
|
+
errors: result.errors
|
|
16008
|
+
});
|
|
15553
16009
|
emitExtensionError(emitter, qualifiedId2, nodePath, {
|
|
15554
16010
|
phase: "emitContribution",
|
|
15555
|
-
contributionId,
|
|
16011
|
+
contributionId: declared.id,
|
|
15556
16012
|
slot: declared.slot,
|
|
15557
16013
|
reason: result.errors,
|
|
15558
|
-
message
|
|
15559
|
-
|
|
15560
|
-
|
|
15561
|
-
|
|
15562
|
-
|
|
15563
|
-
|
|
15564
|
-
|
|
16014
|
+
message
|
|
16015
|
+
});
|
|
16016
|
+
contributionErrors.push({
|
|
16017
|
+
pluginId: analyzer.pluginId,
|
|
16018
|
+
extensionId: analyzer.id,
|
|
16019
|
+
nodePath,
|
|
16020
|
+
reason: result.errors,
|
|
16021
|
+
message,
|
|
16022
|
+
contributionId: declared.id,
|
|
16023
|
+
slot: declared.slot,
|
|
16024
|
+
emittedAt: Date.now()
|
|
15565
16025
|
});
|
|
15566
16026
|
return;
|
|
15567
16027
|
}
|
|
@@ -15569,7 +16029,7 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
15569
16029
|
pluginId: analyzer.pluginId,
|
|
15570
16030
|
extensionId: analyzer.id,
|
|
15571
16031
|
nodePath,
|
|
15572
|
-
contributionId,
|
|
16032
|
+
contributionId: declared.id,
|
|
15573
16033
|
slot: declared.slot,
|
|
15574
16034
|
payload,
|
|
15575
16035
|
emittedAt: Date.now()
|
|
@@ -15602,7 +16062,7 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
15602
16062
|
emitter.emit(evt);
|
|
15603
16063
|
await hookDispatcher.dispatch("analyzer.completed", evt);
|
|
15604
16064
|
}
|
|
15605
|
-
return { issues, contributions };
|
|
16065
|
+
return { issues, contributions, contributionErrors };
|
|
15606
16066
|
}
|
|
15607
16067
|
function orderAnalyzersByPhase(analyzers) {
|
|
15608
16068
|
return analyzers.slice().sort((a, b) => phaseRank(a) - phaseRank(b));
|
|
@@ -16340,7 +16800,7 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
|
|
|
16340
16800
|
for (const parseIssue of result.issues) {
|
|
16341
16801
|
issues.push({
|
|
16342
16802
|
analyzerId: "invalid-sidecar",
|
|
16343
|
-
severity: "
|
|
16803
|
+
severity: "error",
|
|
16344
16804
|
nodeIds: [nodePathForIssue],
|
|
16345
16805
|
message: parseIssue.message,
|
|
16346
16806
|
data: { sidecarPath: relativePathFromRoots(mdAbs, roots) }
|
|
@@ -16490,6 +16950,7 @@ async function walkAndExtract(opts) {
|
|
|
16490
16950
|
enrichments: [...accum.enrichmentBuffer.values()],
|
|
16491
16951
|
extractorRuns: accum.extractorRuns,
|
|
16492
16952
|
contributions: accum.contributionsBuffer,
|
|
16953
|
+
contributionErrors: accum.contributionErrorsBuffer,
|
|
16493
16954
|
freshlyRunTuples: accum.freshlyRunTuples,
|
|
16494
16955
|
orphanSidecars,
|
|
16495
16956
|
sidecarRoots: accum.sidecarRoots,
|
|
@@ -16506,6 +16967,7 @@ function createWalkAccumulators() {
|
|
|
16506
16967
|
frontmatterIssues: [],
|
|
16507
16968
|
enrichmentBuffer: /* @__PURE__ */ new Map(),
|
|
16508
16969
|
contributionsBuffer: [],
|
|
16970
|
+
contributionErrorsBuffer: [],
|
|
16509
16971
|
freshlyRunTuples: /* @__PURE__ */ new Set(),
|
|
16510
16972
|
extractorRuns: [],
|
|
16511
16973
|
sidecarRoots: /* @__PURE__ */ new Map()
|
|
@@ -16656,7 +17118,11 @@ function mergeExtractResult(extractResult, accum) {
|
|
|
16656
17118
|
accum.enrichmentBuffer.set(`${enr.nodePath}\0${enr.extractorId}`, enr);
|
|
16657
17119
|
}
|
|
16658
17120
|
for (const c of extractResult.contributions) accum.contributionsBuffer.push(c);
|
|
16659
|
-
for (const
|
|
17121
|
+
for (const e of extractResult.contributionErrors) accum.contributionErrorsBuffer.push(e);
|
|
17122
|
+
mergeVirtualNodes(extractResult.virtualNodes, accum);
|
|
17123
|
+
}
|
|
17124
|
+
function mergeVirtualNodes(virtualNodes, accum) {
|
|
17125
|
+
for (const vn of virtualNodes) {
|
|
16660
17126
|
if (accum.nodes.some((n) => n.path === vn.path)) continue;
|
|
16661
17127
|
accum.nodes.push(vn);
|
|
16662
17128
|
}
|
|
@@ -16932,6 +17398,7 @@ async function dispatchExtractorCompleted(extractors, emitter, hookDispatcher) {
|
|
|
16932
17398
|
}
|
|
16933
17399
|
function mergeAnalyzerEmissions(walked, analyzerResult, analyzers) {
|
|
16934
17400
|
for (const c of analyzerResult.contributions) walked.contributions.push(c);
|
|
17401
|
+
for (const e of analyzerResult.contributionErrors) walked.contributionErrors.push(e);
|
|
16935
17402
|
for (const analyzer of analyzers ?? []) {
|
|
16936
17403
|
if (analyzer.ui === void 0) continue;
|
|
16937
17404
|
for (const node of walked.nodes) {
|
|
@@ -16977,6 +17444,7 @@ function buildScanReturn(walked, issues, renameOps, stats, options, setup) {
|
|
|
16977
17444
|
extractorRuns: walked.extractorRuns,
|
|
16978
17445
|
enrichments: walked.enrichments,
|
|
16979
17446
|
contributions: walked.contributions,
|
|
17447
|
+
contributionErrors: walked.contributionErrors,
|
|
16980
17448
|
freshlyRunTuples: walked.freshlyRunTuples
|
|
16981
17449
|
};
|
|
16982
17450
|
}
|
|
@@ -18006,6 +18474,7 @@ async function runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanW
|
|
|
18006
18474
|
extractorRuns: scanned.extractorRuns,
|
|
18007
18475
|
enrichments: scanned.enrichments,
|
|
18008
18476
|
contributions: scanned.contributions,
|
|
18477
|
+
contributionErrors: scanned.contributionErrors,
|
|
18009
18478
|
registeredContributionKeys: collectRegisteredContributionKeys(extensions),
|
|
18010
18479
|
freshlyRunTuples: scanned.freshlyRunTuples
|
|
18011
18480
|
});
|
|
@@ -18862,11 +19331,11 @@ function renderStatsFailures(failures, ansi) {
|
|
|
18862
19331
|
tx(HISTORY_TEXTS.statsSectionHeader, { title: HISTORY_TEXTS.statsSectionTitleFailures })
|
|
18863
19332
|
];
|
|
18864
19333
|
const reasonWidth = Math.max(...failures.map(([reason]) => reason.length));
|
|
18865
|
-
for (const [reason,
|
|
19334
|
+
for (const [reason, count3] of failures) {
|
|
18866
19335
|
lines.push(
|
|
18867
19336
|
tx(HISTORY_TEXTS.statsFailuresRow, {
|
|
18868
19337
|
reason: sanitizeForTerminal(reason).padEnd(reasonWidth),
|
|
18869
|
-
count: ansi.red(String(
|
|
19338
|
+
count: ansi.red(String(count3))
|
|
18870
19339
|
})
|
|
18871
19340
|
);
|
|
18872
19341
|
}
|
|
@@ -19568,14 +20037,14 @@ var OrphansReconcileCommand = class extends SmCommand {
|
|
|
19568
20037
|
tx(ORPHANS_TEXTS.reconcileSuccessBody, { breakdown: ansi.dim(breakdown) })
|
|
19569
20038
|
);
|
|
19570
20039
|
if (summary.collisions.length > 0) {
|
|
19571
|
-
const
|
|
20040
|
+
const count3 = summary.collisions.length;
|
|
19572
20041
|
this.printer.info(
|
|
19573
20042
|
tx(
|
|
19574
20043
|
dryRun ? ORPHANS_TEXTS.reconcileCollisionsNoteDryRun : ORPHANS_TEXTS.reconcileCollisionsNote,
|
|
19575
20044
|
{
|
|
19576
20045
|
glyph: ansi.yellow("\u26A0"),
|
|
19577
|
-
count,
|
|
19578
|
-
plural:
|
|
20046
|
+
count: count3,
|
|
20047
|
+
plural: count3 === 1 ? "" : "s"
|
|
19579
20048
|
}
|
|
19580
20049
|
)
|
|
19581
20050
|
);
|
|
@@ -19892,6 +20361,23 @@ var PLUGINS_TEXTS = {
|
|
|
19892
20361
|
doctorIssuesHeader: "\n Issues ({{count}})\n",
|
|
19893
20362
|
doctorIssueEntry: " {{glyph}} {{id}} {{status}}\n",
|
|
19894
20363
|
doctorIssueBody: " {{line}}\n",
|
|
20364
|
+
// --- runtime contribution errors (last scan) -------------------------
|
|
20365
|
+
/**
|
|
20366
|
+
* "off-shape visible" follow-up. Section heading for view
|
|
20367
|
+
* contributions the last scan REJECTED at emit time (undeclared ref,
|
|
20368
|
+
* or payload failed the slot's AJV schema). Rendered only when at
|
|
20369
|
+
* least one error was persisted; promotes the exit code to 1.
|
|
20370
|
+
* `count` is the total error row count across every plugin.
|
|
20371
|
+
*/
|
|
20372
|
+
doctorContribErrorsHeader: "\n Runtime contribution errors (last scan) ({{count}})\n",
|
|
20373
|
+
/** Per-plugin group header: red glyph + plugin id + this plugin's error count. */
|
|
20374
|
+
doctorContribErrorEntry: " {{glyph}} {{pluginId}} ({{count}})\n",
|
|
20375
|
+
/** Sample line under a plugin group: wrapped, dimmed diagnostic message. */
|
|
20376
|
+
doctorContribErrorBody: " {{line}}\n",
|
|
20377
|
+
/** Trailing dimmed note when a plugin has more errors than the sample cap shows. */
|
|
20378
|
+
doctorContribErrorMore: " {{line}}\n",
|
|
20379
|
+
/** Body of the "more" note: the count of samples omitted under this plugin. */
|
|
20380
|
+
doctorContribErrorMoreText: "... and {{count}} more",
|
|
19895
20381
|
// --- enable / disable -----------------------------------------------
|
|
19896
20382
|
/**
|
|
19897
20383
|
* §3.1b two-line block. Mutex between explicit ids and `--all`; the
|
|
@@ -20395,7 +20881,7 @@ function sortExtensionsCanonical(exts) {
|
|
|
20395
20881
|
}
|
|
20396
20882
|
function renderBuiltInDetail(b, ansi) {
|
|
20397
20883
|
const glyph = b.enabled ? ansi.green(PLUGINS_TEXTS.rowGlyphOk) : ansi.red(PLUGINS_TEXTS.rowGlyphOff);
|
|
20398
|
-
const
|
|
20884
|
+
const count3 = b.extensions.length;
|
|
20399
20885
|
const sorted = sortExtensionsCanonical(b.extensions);
|
|
20400
20886
|
const items = sorted.map((ext) => ({
|
|
20401
20887
|
glyph: ext.enabled ? ansi.green(PLUGINS_TEXTS.rowGlyphOk) : ansi.red(PLUGINS_TEXTS.rowGlyphOff),
|
|
@@ -20406,8 +20892,8 @@ function renderBuiltInDetail(b, ansi) {
|
|
|
20406
20892
|
glyph,
|
|
20407
20893
|
id: b.id,
|
|
20408
20894
|
source: ansi.dim(PLUGINS_TEXTS.sourceBuiltIn),
|
|
20409
|
-
count,
|
|
20410
|
-
plural:
|
|
20895
|
+
count: count3,
|
|
20896
|
+
plural: count3 === 1 ? "" : "s"
|
|
20411
20897
|
}) + PLUGINS_TEXTS.detailExtensionsBlock + renderExtensionItems(items);
|
|
20412
20898
|
}
|
|
20413
20899
|
function renderPluginDetail(match, ansi) {
|
|
@@ -20589,6 +21075,7 @@ function renderExtensionFields(meta) {
|
|
|
20589
21075
|
|
|
20590
21076
|
// cli/commands/plugins/doctor.ts
|
|
20591
21077
|
import { Command as Command24, Option as Option23 } from "clipanion";
|
|
21078
|
+
var CONTRIB_ERROR_SAMPLE_CAP = 3;
|
|
20592
21079
|
var STATUS_ORDER = [
|
|
20593
21080
|
"enabled",
|
|
20594
21081
|
"disabled",
|
|
@@ -20614,6 +21101,8 @@ var PluginsDoctorCommand = class extends SmCommand {
|
|
|
20614
21101
|
const knownKinds = collectKnownKinds(plugins);
|
|
20615
21102
|
const applicableKindWarnings = collectApplicableKindWarnings(plugins, knownKinds);
|
|
20616
21103
|
const unknownSlotWarnings = collectUnknownSlotWarnings(plugins, KNOWN_SLOT_NAMES);
|
|
21104
|
+
const contribErrors = await loadContributionErrors();
|
|
21105
|
+
const contribErrorGroups = groupContributionErrorsByPlugin(contribErrors);
|
|
20617
21106
|
const bad = plugins.filter((p) => p.status !== "enabled" && p.status !== "disabled");
|
|
20618
21107
|
const totalWarnings = applicableKindWarnings.length + unknownSlotWarnings.length;
|
|
20619
21108
|
if (this.json) {
|
|
@@ -20623,23 +21112,51 @@ var PluginsDoctorCommand = class extends SmCommand {
|
|
|
20623
21112
|
applicableKindWarnings,
|
|
20624
21113
|
unknownSlotWarnings,
|
|
20625
21114
|
totalWarnings,
|
|
21115
|
+
contribErrors,
|
|
20626
21116
|
elapsedMs: this.elapsed.ms()
|
|
20627
21117
|
});
|
|
20628
21118
|
this.printer.data(JSON.stringify(envelope) + "\n");
|
|
20629
|
-
return bad.length > 0 ? ExitCode.Issues : ExitCode.Ok;
|
|
20630
|
-
}
|
|
21119
|
+
return bad.length > 0 || contribErrors.length > 0 ? ExitCode.Issues : ExitCode.Ok;
|
|
21120
|
+
}
|
|
21121
|
+
this.#renderHumanReport({
|
|
21122
|
+
counts,
|
|
21123
|
+
builtInCount: builtIns2.length,
|
|
21124
|
+
userCount: plugins.length,
|
|
21125
|
+
applicableKindWarnings,
|
|
21126
|
+
unknownSlotWarnings,
|
|
21127
|
+
totalWarnings,
|
|
21128
|
+
bad,
|
|
21129
|
+
contribErrorGroups,
|
|
21130
|
+
contribErrorCount: contribErrors.length
|
|
21131
|
+
});
|
|
21132
|
+
return bad.length > 0 || contribErrors.length > 0 ? ExitCode.Issues : ExitCode.Ok;
|
|
21133
|
+
}
|
|
21134
|
+
/**
|
|
21135
|
+
* Render the full human-mode report in section order: summary header,
|
|
21136
|
+
* source + status tables, then the gated warnings / issues / runtime
|
|
21137
|
+
* contribution-error sections. Pulled out of `run` so the verb body
|
|
21138
|
+
* stays a linear pipeline (load → aggregate → render → exit code)
|
|
21139
|
+
* under the complexity cap.
|
|
21140
|
+
*/
|
|
21141
|
+
#renderHumanReport(args2) {
|
|
20631
21142
|
const ansi = this.ansiFor("stdout");
|
|
20632
|
-
this.#renderSummaryHeader(counts.enabled, bad.length, totalWarnings);
|
|
20633
|
-
this.#renderSourceBreakdown(
|
|
20634
|
-
this.#renderStatusBreakdown(counts, ansi);
|
|
20635
|
-
if (totalWarnings > 0) {
|
|
20636
|
-
this.#renderWarnings(
|
|
21143
|
+
this.#renderSummaryHeader(args2.counts.enabled, args2.bad.length, args2.totalWarnings);
|
|
21144
|
+
this.#renderSourceBreakdown(args2.builtInCount, args2.userCount);
|
|
21145
|
+
this.#renderStatusBreakdown(args2.counts, ansi);
|
|
21146
|
+
if (args2.totalWarnings > 0) {
|
|
21147
|
+
this.#renderWarnings(
|
|
21148
|
+
args2.applicableKindWarnings,
|
|
21149
|
+
args2.unknownSlotWarnings,
|
|
21150
|
+
args2.totalWarnings,
|
|
21151
|
+
ansi
|
|
21152
|
+
);
|
|
20637
21153
|
}
|
|
20638
|
-
if (bad.length > 0) {
|
|
20639
|
-
this.#renderIssues(bad, ansi);
|
|
20640
|
-
|
|
21154
|
+
if (args2.bad.length > 0) {
|
|
21155
|
+
this.#renderIssues(args2.bad, ansi);
|
|
21156
|
+
}
|
|
21157
|
+
if (args2.contribErrorCount > 0) {
|
|
21158
|
+
this.#renderContributionErrors(args2.contribErrorGroups, args2.contribErrorCount, ansi);
|
|
20641
21159
|
}
|
|
20642
|
-
return ExitCode.Ok;
|
|
20643
21160
|
}
|
|
20644
21161
|
#renderSummaryHeader(enabled, badCount, warnings) {
|
|
20645
21162
|
this.printer.data(
|
|
@@ -20676,10 +21193,10 @@ var PluginsDoctorCommand = class extends SmCommand {
|
|
|
20676
21193
|
const statusLabelWidth = Math.max(...STATUS_ORDER.map((s) => s.length));
|
|
20677
21194
|
this.printer.data(PLUGINS_TEXTS.doctorStatusHeader);
|
|
20678
21195
|
for (const status of STATUS_ORDER) {
|
|
20679
|
-
const
|
|
20680
|
-
const isProblem = status !== "enabled" && status !== "disabled" &&
|
|
21196
|
+
const count3 = counts[status];
|
|
21197
|
+
const isProblem = status !== "enabled" && status !== "disabled" && count3 > 0;
|
|
20681
21198
|
const label = status.padEnd(statusLabelWidth);
|
|
20682
|
-
const formattedCount = isProblem ? ansi.red(String(
|
|
21199
|
+
const formattedCount = isProblem ? ansi.red(String(count3)) : String(count3);
|
|
20683
21200
|
this.printer.data(
|
|
20684
21201
|
tx(PLUGINS_TEXTS.doctorStatusRow, {
|
|
20685
21202
|
label: isProblem ? ansi.red(label) : label,
|
|
@@ -20742,6 +21259,42 @@ var PluginsDoctorCommand = class extends SmCommand {
|
|
|
20742
21259
|
}
|
|
20743
21260
|
}
|
|
20744
21261
|
}
|
|
21262
|
+
/**
|
|
21263
|
+
* "off-shape visible" follow-up. Render the last scan's runtime
|
|
21264
|
+
* contribution rejections grouped by plugin: one red entry per plugin
|
|
21265
|
+
* (id + this plugin's error count), then up to
|
|
21266
|
+
* `CONTRIB_ERROR_SAMPLE_CAP` wrapped sample messages, then a dimmed
|
|
21267
|
+
* "... and N more" note when the group overflows the cap. The full
|
|
21268
|
+
* set is always available via `--json`.
|
|
21269
|
+
*/
|
|
21270
|
+
#renderContributionErrors(groups, total, ansi) {
|
|
21271
|
+
this.printer.data(tx(PLUGINS_TEXTS.doctorContribErrorsHeader, { count: total }));
|
|
21272
|
+
const glyph = ansi.red(PLUGINS_TEXTS.rowGlyphOff);
|
|
21273
|
+
for (const group of groups) {
|
|
21274
|
+
this.printer.data(
|
|
21275
|
+
tx(PLUGINS_TEXTS.doctorContribErrorEntry, {
|
|
21276
|
+
glyph,
|
|
21277
|
+
pluginId: sanitizeForTerminal(group.pluginId),
|
|
21278
|
+
count: group.errors.length
|
|
21279
|
+
})
|
|
21280
|
+
);
|
|
21281
|
+
for (const err of group.errors.slice(0, CONTRIB_ERROR_SAMPLE_CAP)) {
|
|
21282
|
+
for (const line of wrapText(sanitizeForTerminal(err.message), 64)) {
|
|
21283
|
+
this.printer.data(
|
|
21284
|
+
tx(PLUGINS_TEXTS.doctorContribErrorBody, { line: ansi.dim(line) })
|
|
21285
|
+
);
|
|
21286
|
+
}
|
|
21287
|
+
}
|
|
21288
|
+
const hidden = group.errors.length - CONTRIB_ERROR_SAMPLE_CAP;
|
|
21289
|
+
if (hidden > 0) {
|
|
21290
|
+
this.printer.data(
|
|
21291
|
+
tx(PLUGINS_TEXTS.doctorContribErrorMore, {
|
|
21292
|
+
line: ansi.dim(tx(PLUGINS_TEXTS.doctorContribErrorMoreText, { count: hidden }))
|
|
21293
|
+
})
|
|
21294
|
+
);
|
|
21295
|
+
}
|
|
21296
|
+
}
|
|
21297
|
+
}
|
|
20745
21298
|
};
|
|
20746
21299
|
function countByStatus(builtIns2, plugins, resolveEnabled) {
|
|
20747
21300
|
const counts = {
|
|
@@ -20938,6 +21491,15 @@ function buildDoctorJsonEnvelope(args2) {
|
|
|
20938
21491
|
})
|
|
20939
21492
|
});
|
|
20940
21493
|
}
|
|
21494
|
+
const contributionErrors = args2.contribErrors.map((e) => ({
|
|
21495
|
+
pluginId: sanitizeForTerminal(e.pluginId),
|
|
21496
|
+
extensionId: sanitizeForTerminal(e.extensionId),
|
|
21497
|
+
nodePath: sanitizeForTerminal(e.nodePath),
|
|
21498
|
+
reason: sanitizeForTerminal(e.reason),
|
|
21499
|
+
message: sanitizeForTerminal(e.message),
|
|
21500
|
+
...e.contributionId !== void 0 ? { contributionId: sanitizeForTerminal(e.contributionId) } : {},
|
|
21501
|
+
...e.slot !== void 0 ? { slot: sanitizeForTerminal(e.slot) } : {}
|
|
21502
|
+
}));
|
|
20941
21503
|
return {
|
|
20942
21504
|
ok: true,
|
|
20943
21505
|
kind: "plugins.doctor",
|
|
@@ -20952,10 +21514,36 @@ function buildDoctorJsonEnvelope(args2) {
|
|
|
20952
21514
|
},
|
|
20953
21515
|
issues,
|
|
20954
21516
|
warnings,
|
|
21517
|
+
contributionErrors,
|
|
20955
21518
|
elapsedMs: args2.elapsedMs
|
|
20956
21519
|
};
|
|
20957
21520
|
}
|
|
20958
|
-
|
|
21521
|
+
async function loadContributionErrors() {
|
|
21522
|
+
const ctx = defaultRuntimeContext();
|
|
21523
|
+
const dbPath = resolveDbPath({ db: void 0, cwd: ctx.cwd });
|
|
21524
|
+
try {
|
|
21525
|
+
const rows = await tryWithSqlite(
|
|
21526
|
+
{ databasePath: dbPath, autoBackup: false },
|
|
21527
|
+
(adapter) => adapter.contributions.listAllErrors()
|
|
21528
|
+
);
|
|
21529
|
+
return rows ?? [];
|
|
21530
|
+
} catch {
|
|
21531
|
+
return [];
|
|
21532
|
+
}
|
|
21533
|
+
}
|
|
21534
|
+
function groupContributionErrorsByPlugin(errors) {
|
|
21535
|
+
const byPlugin = /* @__PURE__ */ new Map();
|
|
21536
|
+
for (const err of errors) {
|
|
21537
|
+
let group = byPlugin.get(err.pluginId);
|
|
21538
|
+
if (!group) {
|
|
21539
|
+
group = { pluginId: err.pluginId, errors: [] };
|
|
21540
|
+
byPlugin.set(err.pluginId, group);
|
|
21541
|
+
}
|
|
21542
|
+
group.errors.push(err);
|
|
21543
|
+
}
|
|
21544
|
+
return [...byPlugin.values()];
|
|
21545
|
+
}
|
|
21546
|
+
|
|
20959
21547
|
// cli/commands/plugins/toggle.ts
|
|
20960
21548
|
import { Command as Command25, Option as Option24 } from "clipanion";
|
|
20961
21549
|
var TogglePluginsBase = class extends SmCommand {
|
|
@@ -21470,9 +22058,22 @@ function stub2(extId) {
|
|
|
21470
22058
|
* Declared settings (\`settings\`):
|
|
21471
22059
|
* - 'keywords' (${DEFAULT_INPUT_TYPE}) \u2192 exposed as ctx.settings.keywords
|
|
21472
22060
|
*
|
|
22061
|
+
* View contributions: declare each one as a const, list it in \`ui\` by
|
|
22062
|
+
* shorthand, and pass that SAME const (by reference, not a string id) to
|
|
22063
|
+
* \`ctx.emitContribution\`. The kernel recovers the contribution from the
|
|
22064
|
+
* object identity. (Write the plugin in TypeScript and add
|
|
22065
|
+
* \`satisfies IViewContribution\` from '@skill-map/cli' to get a typed payload.)
|
|
22066
|
+
*
|
|
21473
22067
|
* See: spec/plugin-author-guide.md \xA7View contributions
|
|
21474
22068
|
* spec/view-slots.md
|
|
21475
22069
|
*/
|
|
22070
|
+
const count = {
|
|
22071
|
+
slot: '${DEFAULT_SLOT}',
|
|
22072
|
+
icon: '\u{1F50D}',
|
|
22073
|
+
label: 'kw',
|
|
22074
|
+
emitWhenEmpty: false,
|
|
22075
|
+
};
|
|
22076
|
+
|
|
21476
22077
|
export default {
|
|
21477
22078
|
version: '0.1.0',
|
|
21478
22079
|
description: 'Counts configured keywords per node.',
|
|
@@ -21488,14 +22089,7 @@ export default {
|
|
|
21488
22089
|
},
|
|
21489
22090
|
},
|
|
21490
22091
|
|
|
21491
|
-
ui: {
|
|
21492
|
-
count: {
|
|
21493
|
-
slot: '${DEFAULT_SLOT}',
|
|
21494
|
-
icon: '\u{1F50D}',
|
|
21495
|
-
label: 'kw',
|
|
21496
|
-
emitWhenEmpty: false,
|
|
21497
|
-
},
|
|
21498
|
-
},
|
|
22092
|
+
ui: { count },
|
|
21499
22093
|
|
|
21500
22094
|
extract(ctx) {
|
|
21501
22095
|
const keywords = (ctx.settings && ctx.settings.keywords) || ['TODO', 'FIXME'];
|
|
@@ -21505,7 +22099,7 @@ export default {
|
|
|
21505
22099
|
total += (ctx.body.match(re) || []).length;
|
|
21506
22100
|
}
|
|
21507
22101
|
if (total > 0) {
|
|
21508
|
-
ctx.emitContribution(
|
|
22102
|
+
ctx.emitContribution(count, { value: total });
|
|
21509
22103
|
}
|
|
21510
22104
|
},
|
|
21511
22105
|
};
|
|
@@ -21799,8 +22393,8 @@ var VIEW_SLOTS_CATALOG = [
|
|
|
21799
22393
|
{ id: "card.footer.left", summary: "Counter chip in the left footer of the card." },
|
|
21800
22394
|
{ id: "card.footer.right", summary: "Counter chip in the right footer of the card." },
|
|
21801
22395
|
{ id: "graph.node.alert", summary: 'Reserved corner badge on the graph node, special-case signals only. No core analyzer emits here; routine "this node has a problem" findings belong in `card.footer.right`.' },
|
|
21802
|
-
{ id: "inspector.header.badge
|
|
21803
|
-
{ id: "inspector.
|
|
22396
|
+
{ id: "inspector.header.badge", summary: "Unified badge in the inspector header cluster: icon and/or label and/or count, optional severity. Multi-cardinality, priority order, modeled on card.footer.left. Replaces the retired inspector.header.badge.counter and inspector.header.badge.tag sub-slots." },
|
|
22397
|
+
{ id: "inspector.action.button", summary: "Action button in the inspector. Click dispatches a kernel Action by qualified id via POST /api/actions/:id. Always emitted; the payload `enabled` flag carries the dynamic condition (e.g. isStale for the bump button)." },
|
|
21804
22398
|
{ id: "inspector.body.panel.breakdown", summary: "Top-N labeled values rendered as a bar chart in the inspector body." },
|
|
21805
22399
|
{ id: "inspector.body.panel.records", summary: "Tabular data (rows \xD7 columns \u2264 50 \xD7 6) in the inspector body." },
|
|
21806
22400
|
{ id: "inspector.body.panel.tree", summary: "Recursive label/children hierarchy (depth \u2264 6, total \u2264 200) in the inspector body." },
|
|
@@ -22098,15 +22692,15 @@ var RefreshCommand = class extends SmCommand {
|
|
|
22098
22692
|
return ExitCode.Ok;
|
|
22099
22693
|
}
|
|
22100
22694
|
const glyph = ansi.green("\u2713");
|
|
22101
|
-
const
|
|
22102
|
-
const noun =
|
|
22695
|
+
const count3 = freshEnrichments.length;
|
|
22696
|
+
const noun = count3 === 1 ? REFRESH_TEXTS.refreshNounSingular : REFRESH_TEXTS.refreshNounPlural;
|
|
22103
22697
|
if (this.stale) {
|
|
22104
22698
|
const nodeCount = targetNodes.length;
|
|
22105
22699
|
const nodeNoun = nodeCount === 1 ? REFRESH_TEXTS.refreshNodeNounSingular : REFRESH_TEXTS.refreshNodeNounPlural;
|
|
22106
22700
|
this.printer.data(
|
|
22107
22701
|
tx(REFRESH_TEXTS.refreshSuccessStale, {
|
|
22108
22702
|
glyph,
|
|
22109
|
-
count,
|
|
22703
|
+
count: count3,
|
|
22110
22704
|
noun,
|
|
22111
22705
|
nodeCount,
|
|
22112
22706
|
nodeNoun
|
|
@@ -22116,7 +22710,7 @@ var RefreshCommand = class extends SmCommand {
|
|
|
22116
22710
|
this.printer.data(
|
|
22117
22711
|
tx(REFRESH_TEXTS.refreshSuccessSingle, {
|
|
22118
22712
|
glyph,
|
|
22119
|
-
count,
|
|
22713
|
+
count: count3,
|
|
22120
22714
|
noun,
|
|
22121
22715
|
nodePath: this.nodePath
|
|
22122
22716
|
})
|
|
@@ -22593,7 +23187,15 @@ function createWatcherRuntime(opts) {
|
|
|
22593
23187
|
runOptions.priorExtractorRuns = priorState.extractorRuns;
|
|
22594
23188
|
}
|
|
22595
23189
|
const ran = await runScanWithRenames(kernel, runOptions);
|
|
22596
|
-
const {
|
|
23190
|
+
const {
|
|
23191
|
+
result,
|
|
23192
|
+
renameOps,
|
|
23193
|
+
extractorRuns,
|
|
23194
|
+
enrichments,
|
|
23195
|
+
contributions,
|
|
23196
|
+
contributionErrors,
|
|
23197
|
+
freshlyRunTuples
|
|
23198
|
+
} = ran;
|
|
22597
23199
|
await withSqlite(
|
|
22598
23200
|
{ databasePath: opts.dbPath },
|
|
22599
23201
|
(writer) => writer.scans.persist(result, {
|
|
@@ -22601,6 +23203,7 @@ function createWatcherRuntime(opts) {
|
|
|
22601
23203
|
extractorRuns,
|
|
22602
23204
|
enrichments,
|
|
22603
23205
|
contributions,
|
|
23206
|
+
contributionErrors,
|
|
22604
23207
|
registeredContributionKeys: collectRegisteredContributionKeys(composed),
|
|
22605
23208
|
freshlyRunTuples
|
|
22606
23209
|
})
|
|
@@ -22929,11 +23532,11 @@ async function runWatchLoop(opts) {
|
|
|
22929
23532
|
}
|
|
22930
23533
|
void info;
|
|
22931
23534
|
},
|
|
22932
|
-
onBreakerTripped: (
|
|
23535
|
+
onBreakerTripped: (count3, message) => {
|
|
22933
23536
|
context.stderr.write(
|
|
22934
23537
|
tx(WATCH_TEXTS.breakerTripped, {
|
|
22935
23538
|
glyph: errGlyph,
|
|
22936
|
-
count,
|
|
23539
|
+
count: count3,
|
|
22937
23540
|
hint: stderrAnsi.dim(tx(WATCH_TEXTS.breakerTrippedHint, { message }))
|
|
22938
23541
|
})
|
|
22939
23542
|
);
|
|
@@ -23475,8 +24078,8 @@ function formatScanCounts(opts) {
|
|
|
23475
24078
|
}
|
|
23476
24079
|
return parts.join(" \xB7 ");
|
|
23477
24080
|
}
|
|
23478
|
-
function countNoun(
|
|
23479
|
-
return
|
|
24081
|
+
function countNoun(count3, singular, plural) {
|
|
24082
|
+
return count3 === 1 ? singular : plural;
|
|
23480
24083
|
}
|
|
23481
24084
|
|
|
23482
24085
|
// cli/commands/scan-compare.ts
|
|
@@ -23758,7 +24361,7 @@ import { WebSocketServer } from "ws";
|
|
|
23758
24361
|
// server/app.ts
|
|
23759
24362
|
import { Hono } from "hono";
|
|
23760
24363
|
import { bodyLimit } from "hono/body-limit";
|
|
23761
|
-
import { HTTPException as
|
|
24364
|
+
import { HTTPException as HTTPException17 } from "hono/http-exception";
|
|
23762
24365
|
|
|
23763
24366
|
// core/config/service.ts
|
|
23764
24367
|
var ConfigService = class {
|
|
@@ -23921,33 +24524,33 @@ var SERVER_TEXTS = {
|
|
|
23921
24524
|
// Hono's `app.notFound` fallback, every other unmatched path funnels
|
|
23922
24525
|
// here (after static + SPA fallback have had their turn).
|
|
23923
24526
|
unknownPath: "Not found: {{path}}.",
|
|
23924
|
-
// ----
|
|
23925
|
-
//
|
|
23926
|
-
//
|
|
23927
|
-
//
|
|
23928
|
-
//
|
|
23929
|
-
|
|
24527
|
+
// ---- generic action-dispatch route (routes/actions.ts, Step 17) ----------
|
|
24528
|
+
//
|
|
24529
|
+
// POST /api/actions/:qualifiedId. Generalises the legacy bump route:
|
|
24530
|
+
// resolve the qualified action id off the kernel registry, invoke it,
|
|
24531
|
+
// materialise any sidecar writes through the same consent-gated store,
|
|
24532
|
+
// broadcast `action.applied`. Each error keeps its own message key so
|
|
24533
|
+
// the UI / log can disambiguate without regex on the message.
|
|
23930
24534
|
// 400 envelopes thrown by `parseBody` when the request payload is
|
|
23931
|
-
// malformed.
|
|
23932
|
-
|
|
23933
|
-
|
|
23934
|
-
|
|
23935
|
-
|
|
23936
|
-
|
|
23937
|
-
|
|
23938
|
-
|
|
23939
|
-
|
|
23940
|
-
|
|
23941
|
-
|
|
23942
|
-
|
|
23943
|
-
|
|
23944
|
-
|
|
23945
|
-
|
|
23946
|
-
//
|
|
23947
|
-
//
|
|
23948
|
-
//
|
|
23949
|
-
|
|
23950
|
-
sidecarBumpInvokeMissing: "built-in bump action is missing its invoke().",
|
|
24535
|
+
// malformed.
|
|
24536
|
+
actionBodyNotJson: "Request body must be valid JSON.",
|
|
24537
|
+
actionBodyNotObject: "Request body must be a JSON object.",
|
|
24538
|
+
actionNodePathRequired: "`nodePath` is required and must be a non-empty string.",
|
|
24539
|
+
actionInputMustBeObject: "`input` must be an object when present.",
|
|
24540
|
+
actionConfirmMustBeBoolean: "`confirm` must be a boolean when present.",
|
|
24541
|
+
actionAlwaysMustBeBoolean: "`always` must be a boolean when present.",
|
|
24542
|
+
// 404 envelope when `:qualifiedId` does not resolve to a registered
|
|
24543
|
+
// action with an `invoke()`. Covers both "no such action" and "action
|
|
24544
|
+
// exists but ships no deterministic entry point" (a probabilistic
|
|
24545
|
+
// action that cannot be dispatched over this synchronous route). The
|
|
24546
|
+
// id is sanitised before interpolation.
|
|
24547
|
+
actionUnknown: 'No invokable action with id "{{actionId}}".',
|
|
24548
|
+
// 409 envelope when an action's report comes back `ok: false`. The
|
|
24549
|
+
// refusal `reason` (when the report carries one) becomes the envelope
|
|
24550
|
+
// `code`; the fallback `action-refused` covers reports that refuse
|
|
24551
|
+
// without naming a reason. Dispatch is via the typed
|
|
24552
|
+
// `ActionRefusedError`; the message is informational only.
|
|
24553
|
+
actionRefused: 'Action "{{actionId}}" refused to run on "{{nodePath}}".',
|
|
23951
24554
|
// ---- POST /api/scan (manual refresh) ------------------------------------
|
|
23952
24555
|
// 400, runtime cannot persist a meaningful scan because the boot
|
|
23953
24556
|
// dropped half the pipeline. Same gate the `?fresh=1` GET applies.
|
|
@@ -25018,6 +25621,8 @@ function registerPluginsRoute(app, deps) {
|
|
|
25018
25621
|
app.get("/api/plugins", async (c) => {
|
|
25019
25622
|
const resolveEnabled = await buildFreshResolver2(deps);
|
|
25020
25623
|
const items = listItems(deps, resolveEnabled);
|
|
25624
|
+
const errorsByPlugin = await loadRuntimeContributionErrors(deps);
|
|
25625
|
+
attachRuntimeContributionErrors(items, errorsByPlugin);
|
|
25021
25626
|
return c.json(
|
|
25022
25627
|
buildListEnvelope({
|
|
25023
25628
|
kind: "plugins",
|
|
@@ -25192,6 +25797,41 @@ function firstVersion(extensions) {
|
|
|
25192
25797
|
function classifyPluginSource(_pluginPath, _deps) {
|
|
25193
25798
|
return "project";
|
|
25194
25799
|
}
|
|
25800
|
+
async function loadRuntimeContributionErrors(deps) {
|
|
25801
|
+
try {
|
|
25802
|
+
const rows = await tryWithSqlite(
|
|
25803
|
+
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
25804
|
+
(adapter) => adapter.contributions.listAllErrors()
|
|
25805
|
+
);
|
|
25806
|
+
if (rows === null) return /* @__PURE__ */ new Map();
|
|
25807
|
+
return groupContributionErrorsByPlugin2(rows);
|
|
25808
|
+
} catch {
|
|
25809
|
+
return /* @__PURE__ */ new Map();
|
|
25810
|
+
}
|
|
25811
|
+
}
|
|
25812
|
+
function groupContributionErrorsByPlugin2(rows) {
|
|
25813
|
+
const out = /* @__PURE__ */ new Map();
|
|
25814
|
+
for (const row of rows) {
|
|
25815
|
+
const projected = {
|
|
25816
|
+
extensionId: row.extensionId,
|
|
25817
|
+
nodePath: row.nodePath,
|
|
25818
|
+
reason: row.reason,
|
|
25819
|
+
message: row.message,
|
|
25820
|
+
...row.contributionId !== void 0 ? { contributionId: row.contributionId } : {},
|
|
25821
|
+
...row.slot !== void 0 ? { slot: row.slot } : {}
|
|
25822
|
+
};
|
|
25823
|
+
const list = out.get(row.pluginId);
|
|
25824
|
+
if (list) list.push(projected);
|
|
25825
|
+
else out.set(row.pluginId, [projected]);
|
|
25826
|
+
}
|
|
25827
|
+
return out;
|
|
25828
|
+
}
|
|
25829
|
+
function attachRuntimeContributionErrors(items, errorsByPlugin) {
|
|
25830
|
+
for (const item of items) {
|
|
25831
|
+
const errors = errorsByPlugin.get(item.id);
|
|
25832
|
+
if (errors && errors.length > 0) item.runtimeContributionErrors = errors;
|
|
25833
|
+
}
|
|
25834
|
+
}
|
|
25195
25835
|
async function persistAndProject(c, deps, configKey, enabled) {
|
|
25196
25836
|
const overrides = await tryWithSqlite(
|
|
25197
25837
|
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
@@ -25808,24 +26448,39 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
|
|
|
25808
26448
|
import { existsSync as existsSync28 } from "fs";
|
|
25809
26449
|
import { HTTPException as HTTPException13 } from "hono/http-exception";
|
|
25810
26450
|
function registerActiveProviderRoute(app, deps) {
|
|
25811
|
-
app.get("/api/active-provider", (c) => {
|
|
25812
|
-
return c.json(buildEnvelope4(deps));
|
|
26451
|
+
app.get("/api/active-provider", async (c) => {
|
|
26452
|
+
return c.json(await buildEnvelope4(deps));
|
|
25813
26453
|
});
|
|
25814
26454
|
app.patch("/api/active-provider", async (c) => {
|
|
25815
26455
|
const body = await parsePatchBody5(c.req.raw);
|
|
25816
26456
|
const result = applyLensSwitch(deps, body.activeProvider);
|
|
25817
26457
|
deps.configService.reload();
|
|
25818
|
-
return c.json({ ...buildEnvelope4(deps), switch: result });
|
|
26458
|
+
return c.json({ ...await buildEnvelope4(deps), switch: result });
|
|
25819
26459
|
});
|
|
25820
26460
|
}
|
|
25821
|
-
function buildEnvelope4(deps) {
|
|
26461
|
+
async function buildEnvelope4(deps) {
|
|
25822
26462
|
const r = resolveActiveProvider(deps.runtimeContext.cwd, deps.providers);
|
|
25823
26463
|
return {
|
|
25824
26464
|
activeProvider: r.resolved,
|
|
25825
26465
|
detected: r.detected,
|
|
25826
|
-
source: r.source
|
|
26466
|
+
source: r.source,
|
|
26467
|
+
selectable: await resolveSelectableProviders(deps)
|
|
25827
26468
|
};
|
|
25828
26469
|
}
|
|
26470
|
+
async function resolveSelectableProviders(deps) {
|
|
26471
|
+
const resolveEnabled = await buildFreshResolver({
|
|
26472
|
+
databasePath: deps.options.dbPath,
|
|
26473
|
+
effectiveConfig: () => deps.configService.effective(),
|
|
26474
|
+
fallbackResolver: deps.pluginRuntime.resolveEnabled
|
|
26475
|
+
});
|
|
26476
|
+
const selectable = /* @__PURE__ */ new Set();
|
|
26477
|
+
for (const provider of deps.providers) {
|
|
26478
|
+
if (isPluginExtensionEnabled(provider, resolveEnabled)) {
|
|
26479
|
+
selectable.add(provider.id);
|
|
26480
|
+
}
|
|
26481
|
+
}
|
|
26482
|
+
return [...selectable];
|
|
26483
|
+
}
|
|
25829
26484
|
function applyLensSwitch(deps, newValue) {
|
|
25830
26485
|
const cwd = deps.runtimeContext.cwd;
|
|
25831
26486
|
try {
|
|
@@ -25866,8 +26521,168 @@ var parsePatchBody5 = makeBodyValidator(PATCH_BODY_SCHEMA4, {
|
|
|
25866
26521
|
}
|
|
25867
26522
|
});
|
|
25868
26523
|
|
|
25869
|
-
// server/routes/
|
|
26524
|
+
// server/routes/actions.ts
|
|
26525
|
+
import { HTTPException as HTTPException15 } from "hono/http-exception";
|
|
26526
|
+
import { resolve as resolve36 } from "path";
|
|
26527
|
+
|
|
26528
|
+
// server/routes/node-loader.ts
|
|
25870
26529
|
import { HTTPException as HTTPException14 } from "hono/http-exception";
|
|
26530
|
+
async function loadNode(deps, nodePath) {
|
|
26531
|
+
const persisted = await tryWithSqlite(
|
|
26532
|
+
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
26533
|
+
async (adapter) => adapter.scans.load()
|
|
26534
|
+
);
|
|
26535
|
+
const node = persisted?.nodes.find((n) => n.path === nodePath);
|
|
26536
|
+
if (!node) {
|
|
26537
|
+
throw new HTTPException14(404, {
|
|
26538
|
+
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
26539
|
+
});
|
|
26540
|
+
}
|
|
26541
|
+
return node;
|
|
26542
|
+
}
|
|
26543
|
+
|
|
26544
|
+
// server/routes/actions.ts
|
|
26545
|
+
var ENVELOPE_KIND2 = "action.applied";
|
|
26546
|
+
var REFUSED_CODE = "action-refused";
|
|
26547
|
+
var INVOKER_FALLBACK = "ui";
|
|
26548
|
+
var QUALIFIED_ID_SEGMENT2 = /^[A-Za-z0-9._-]+$/;
|
|
26549
|
+
var ACTION_BODY_SCHEMA = {
|
|
26550
|
+
type: "object",
|
|
26551
|
+
additionalProperties: false,
|
|
26552
|
+
required: ["nodePath"],
|
|
26553
|
+
properties: {
|
|
26554
|
+
nodePath: { type: "string", minLength: 1 },
|
|
26555
|
+
input: { type: "object" },
|
|
26556
|
+
confirm: { type: "boolean" },
|
|
26557
|
+
always: { type: "boolean" }
|
|
26558
|
+
}
|
|
26559
|
+
};
|
|
26560
|
+
var parseBody = makeBodyValidator(ACTION_BODY_SCHEMA, {
|
|
26561
|
+
notJson: SERVER_TEXTS.actionBodyNotJson,
|
|
26562
|
+
notObject: SERVER_TEXTS.actionBodyNotObject,
|
|
26563
|
+
invalid: SERVER_TEXTS.actionBodyNotObject,
|
|
26564
|
+
mapping: {
|
|
26565
|
+
"/nodePath:required": SERVER_TEXTS.actionNodePathRequired,
|
|
26566
|
+
":type:object": SERVER_TEXTS.actionBodyNotObject,
|
|
26567
|
+
"/nodePath:type:string": SERVER_TEXTS.actionNodePathRequired,
|
|
26568
|
+
"/nodePath:minLength": SERVER_TEXTS.actionNodePathRequired,
|
|
26569
|
+
"/input:type:object": SERVER_TEXTS.actionInputMustBeObject,
|
|
26570
|
+
"/confirm:type:boolean": SERVER_TEXTS.actionConfirmMustBeBoolean,
|
|
26571
|
+
"/always:type:boolean": SERVER_TEXTS.actionAlwaysMustBeBoolean
|
|
26572
|
+
}
|
|
26573
|
+
});
|
|
26574
|
+
function registerActionsRoutes(app, deps) {
|
|
26575
|
+
app.post("/api/actions/:pluginId/:actionId", async (c) => {
|
|
26576
|
+
const startedAt = Date.now();
|
|
26577
|
+
const pluginId = parseSegment(c.req.param("pluginId"), "pluginId");
|
|
26578
|
+
const shortId = parseSegment(c.req.param("actionId"), "actionId");
|
|
26579
|
+
const actionId = qualifiedExtensionId(pluginId, shortId);
|
|
26580
|
+
const action = resolveInvokableAction(deps.kernel, actionId);
|
|
26581
|
+
const body = await parseBody(c.req.raw);
|
|
26582
|
+
const node = await loadNode(deps, body.nodePath);
|
|
26583
|
+
let absPath;
|
|
26584
|
+
try {
|
|
26585
|
+
assertContained(deps.runtimeContext.cwd, node.path);
|
|
26586
|
+
absPath = resolve36(deps.runtimeContext.cwd, node.path);
|
|
26587
|
+
} catch (err) {
|
|
26588
|
+
throw new HTTPException15(400, { message: formatErrorMessage(err) });
|
|
26589
|
+
}
|
|
26590
|
+
const result = invokeAction(action, absPath, node, body, deps.runtimeContext.cwd);
|
|
26591
|
+
const report = result.report;
|
|
26592
|
+
if (report.ok === false) {
|
|
26593
|
+
const reason = typeof report.reason === "string" && report.reason.length > 0 ? sanitizeForTerminal(report.reason) : REFUSED_CODE;
|
|
26594
|
+
throw new ActionRefusedError({
|
|
26595
|
+
code: reason,
|
|
26596
|
+
message: tx(SERVER_TEXTS.actionRefused, {
|
|
26597
|
+
actionId: sanitizeForTerminal(actionId),
|
|
26598
|
+
nodePath: sanitizeForTerminal(node.path)
|
|
26599
|
+
}),
|
|
26600
|
+
actionId,
|
|
26601
|
+
nodePath: node.path,
|
|
26602
|
+
report: result.report
|
|
26603
|
+
});
|
|
26604
|
+
}
|
|
26605
|
+
if (report.ok === true && report.noop === true) {
|
|
26606
|
+
return c.json(buildEnvelope5(actionId, node.path, result.report, startedAt));
|
|
26607
|
+
}
|
|
26608
|
+
await materializeWrites(result.writes, body, deps.runtimeContext.cwd);
|
|
26609
|
+
if (body.always === true) {
|
|
26610
|
+
deps.configService.reload();
|
|
26611
|
+
}
|
|
26612
|
+
const eventData = {
|
|
26613
|
+
actionId,
|
|
26614
|
+
nodePath: node.path,
|
|
26615
|
+
report: result.report
|
|
26616
|
+
};
|
|
26617
|
+
const wsEnvelope = {
|
|
26618
|
+
type: ENVELOPE_KIND2,
|
|
26619
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26620
|
+
data: eventData
|
|
26621
|
+
};
|
|
26622
|
+
deps.broadcaster.broadcast(wsEnvelope);
|
|
26623
|
+
return c.json(buildEnvelope5(actionId, node.path, result.report, startedAt));
|
|
26624
|
+
});
|
|
26625
|
+
}
|
|
26626
|
+
function parseSegment(value, name) {
|
|
26627
|
+
if (!QUALIFIED_ID_SEGMENT2.test(value)) {
|
|
26628
|
+
throw new HTTPException15(400, {
|
|
26629
|
+
message: tx(SERVER_TEXTS.qualifiedIdMalformed, {
|
|
26630
|
+
name,
|
|
26631
|
+
value: sanitizeForTerminal(value)
|
|
26632
|
+
})
|
|
26633
|
+
});
|
|
26634
|
+
}
|
|
26635
|
+
return value;
|
|
26636
|
+
}
|
|
26637
|
+
function resolveInvokableAction(kernel, actionId) {
|
|
26638
|
+
const ext = kernel.registry.get("action", actionId);
|
|
26639
|
+
const action = ext;
|
|
26640
|
+
if (!action || typeof action.invoke !== "function") {
|
|
26641
|
+
throw new HTTPException15(404, {
|
|
26642
|
+
message: tx(SERVER_TEXTS.actionUnknown, { actionId: sanitizeForTerminal(actionId) })
|
|
26643
|
+
});
|
|
26644
|
+
}
|
|
26645
|
+
return action;
|
|
26646
|
+
}
|
|
26647
|
+
function invokeAction(action, absPath, node, body, cwd) {
|
|
26648
|
+
const invoke = action.invoke;
|
|
26649
|
+
const ctx = {
|
|
26650
|
+
node,
|
|
26651
|
+
nodeAbsolutePath: absPath,
|
|
26652
|
+
invoker: resolveGitAuthorName(cwd) ?? INVOKER_FALLBACK,
|
|
26653
|
+
now: () => /* @__PURE__ */ new Date(),
|
|
26654
|
+
settings: {}
|
|
26655
|
+
};
|
|
26656
|
+
return invoke(body.input ?? {}, ctx);
|
|
26657
|
+
}
|
|
26658
|
+
async function materializeWrites(writes, body, cwd) {
|
|
26659
|
+
const store = new FilesystemSidecarStore(ensureSidecarWritesAllowed);
|
|
26660
|
+
try {
|
|
26661
|
+
for (const w of writes ?? []) {
|
|
26662
|
+
if (w.kind === "sidecar") {
|
|
26663
|
+
await store.applyPatch(w.path, w.changes, {
|
|
26664
|
+
confirm: body.confirm === true,
|
|
26665
|
+
always: body.always === true,
|
|
26666
|
+
cwd
|
|
26667
|
+
});
|
|
26668
|
+
}
|
|
26669
|
+
}
|
|
26670
|
+
} catch (err) {
|
|
26671
|
+
if (err instanceof EConsentRequiredError) throw err;
|
|
26672
|
+
throw new HTTPException15(500, { message: formatErrorMessage(err) });
|
|
26673
|
+
}
|
|
26674
|
+
}
|
|
26675
|
+
function buildEnvelope5(actionId, nodePath, report, startedAt) {
|
|
26676
|
+
return {
|
|
26677
|
+
schemaVersion: "1",
|
|
26678
|
+
kind: ENVELOPE_KIND2,
|
|
26679
|
+
value: { actionId, nodePath, report },
|
|
26680
|
+
elapsedMs: Date.now() - startedAt
|
|
26681
|
+
};
|
|
26682
|
+
}
|
|
26683
|
+
|
|
26684
|
+
// server/routes/scan.ts
|
|
26685
|
+
import { HTTPException as HTTPException16 } from "hono/http-exception";
|
|
25871
26686
|
|
|
25872
26687
|
// server/scan-mutex.ts
|
|
25873
26688
|
var inFlight = null;
|
|
@@ -26046,7 +26861,7 @@ function registerScanRoute(app, deps) {
|
|
|
26046
26861
|
}
|
|
26047
26862
|
async function runPersistedScan(c, deps) {
|
|
26048
26863
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
26049
|
-
throw new
|
|
26864
|
+
throw new HTTPException16(400, { message: SERVER_TEXTS.scanPostRequiresFullPipeline });
|
|
26050
26865
|
}
|
|
26051
26866
|
const dbExists = await tryWithSqlite(
|
|
26052
26867
|
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
@@ -26083,7 +26898,7 @@ async function runPersistedScan(c, deps) {
|
|
|
26083
26898
|
...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
|
|
26084
26899
|
});
|
|
26085
26900
|
if (outcome.kind !== "ok") {
|
|
26086
|
-
throw new
|
|
26901
|
+
throw new HTTPException16(500, {
|
|
26087
26902
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.scanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
26088
26903
|
});
|
|
26089
26904
|
}
|
|
@@ -26161,7 +26976,7 @@ function groupTagsByPath2(rows) {
|
|
|
26161
26976
|
}
|
|
26162
26977
|
async function runFreshScan(deps) {
|
|
26163
26978
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
26164
|
-
throw new
|
|
26979
|
+
throw new HTTPException16(400, { message: SERVER_TEXTS.freshScanRequiresPipeline });
|
|
26165
26980
|
}
|
|
26166
26981
|
const resolveEnabledOverride = await buildBffResolverOverride(deps);
|
|
26167
26982
|
const outcome = await runScanForCommand({
|
|
@@ -26196,7 +27011,7 @@ async function runFreshScan(deps) {
|
|
|
26196
27011
|
...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
|
|
26197
27012
|
});
|
|
26198
27013
|
if (outcome.kind !== "ok") {
|
|
26199
|
-
throw new
|
|
27014
|
+
throw new HTTPException16(500, {
|
|
26200
27015
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.freshScanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
26201
27016
|
});
|
|
26202
27017
|
}
|
|
@@ -26242,141 +27057,6 @@ function emptyScanResult() {
|
|
|
26242
27057
|
};
|
|
26243
27058
|
}
|
|
26244
27059
|
|
|
26245
|
-
// server/routes/sidecar.ts
|
|
26246
|
-
import { HTTPException as HTTPException15 } from "hono/http-exception";
|
|
26247
|
-
import { resolve as resolve36 } from "path";
|
|
26248
|
-
var STATUS_FRESH = "fresh";
|
|
26249
|
-
var ENVELOPE_KIND2 = "sidecar.bumped";
|
|
26250
|
-
var BUMP_BODY_SCHEMA = {
|
|
26251
|
-
type: "object",
|
|
26252
|
-
additionalProperties: false,
|
|
26253
|
-
required: ["nodePath"],
|
|
26254
|
-
properties: {
|
|
26255
|
-
nodePath: { type: "string", minLength: 1 },
|
|
26256
|
-
force: { type: "boolean" },
|
|
26257
|
-
confirm: { type: "boolean" }
|
|
26258
|
-
}
|
|
26259
|
-
};
|
|
26260
|
-
var parseBody = makeBodyValidator(BUMP_BODY_SCHEMA, {
|
|
26261
|
-
notJson: SERVER_TEXTS.sidecarBodyNotJson,
|
|
26262
|
-
notObject: SERVER_TEXTS.sidecarBodyNotObject,
|
|
26263
|
-
invalid: SERVER_TEXTS.sidecarBodyNotObject,
|
|
26264
|
-
mapping: {
|
|
26265
|
-
"/nodePath:required": SERVER_TEXTS.sidecarNodePathRequired,
|
|
26266
|
-
":type:object": SERVER_TEXTS.sidecarBodyNotObject,
|
|
26267
|
-
"/nodePath:type:string": SERVER_TEXTS.sidecarNodePathRequired,
|
|
26268
|
-
"/nodePath:minLength": SERVER_TEXTS.sidecarNodePathRequired,
|
|
26269
|
-
"/force:type:boolean": SERVER_TEXTS.sidecarForceMustBeBoolean,
|
|
26270
|
-
"/confirm:type:boolean": SERVER_TEXTS.sidecarConfirmMustBeBoolean
|
|
26271
|
-
}
|
|
26272
|
-
});
|
|
26273
|
-
function registerSidecarRoutes(app, deps) {
|
|
26274
|
-
app.post("/api/sidecar/bump", async (c) => {
|
|
26275
|
-
const startedAt = Date.now();
|
|
26276
|
-
const body = await parseBody(c.req.raw);
|
|
26277
|
-
const node = await loadNode(deps, body.nodePath);
|
|
26278
|
-
let absPath;
|
|
26279
|
-
try {
|
|
26280
|
-
assertContained(deps.runtimeContext.cwd, node.path);
|
|
26281
|
-
absPath = resolve36(deps.runtimeContext.cwd, node.path);
|
|
26282
|
-
} catch (err) {
|
|
26283
|
-
throw new HTTPException15(400, { message: formatErrorMessage(err) });
|
|
26284
|
-
}
|
|
26285
|
-
const result = invokeBump2(node, absPath, body);
|
|
26286
|
-
if (result.report.ok === false && result.report.reason === "fresh") {
|
|
26287
|
-
throw new ConflictError({ code: "sidecar-fresh", message: SERVER_TEXTS.sidecarFreshRefusal });
|
|
26288
|
-
}
|
|
26289
|
-
if (result.report.ok === true && result.report.noop === true) {
|
|
26290
|
-
const envelope2 = {
|
|
26291
|
-
schemaVersion: "1",
|
|
26292
|
-
kind: ENVELOPE_KIND2,
|
|
26293
|
-
value: {
|
|
26294
|
-
nodePath: node.path,
|
|
26295
|
-
version: pickExistingVersion(node),
|
|
26296
|
-
status: STATUS_FRESH
|
|
26297
|
-
},
|
|
26298
|
-
elapsedMs: Date.now() - startedAt
|
|
26299
|
-
};
|
|
26300
|
-
return c.json(envelope2);
|
|
26301
|
-
}
|
|
26302
|
-
const store = new FilesystemSidecarStore(ensureSidecarWritesAllowed);
|
|
26303
|
-
try {
|
|
26304
|
-
for (const w of result.writes ?? []) {
|
|
26305
|
-
if (w.kind === "sidecar") {
|
|
26306
|
-
await store.applyPatch(w.path, w.changes, {
|
|
26307
|
-
confirm: body.confirm === true,
|
|
26308
|
-
cwd: deps.runtimeContext.cwd
|
|
26309
|
-
});
|
|
26310
|
-
}
|
|
26311
|
-
}
|
|
26312
|
-
} catch (err) {
|
|
26313
|
-
if (err instanceof EConsentRequiredError) throw err;
|
|
26314
|
-
throw new HTTPException15(500, { message: formatErrorMessage(err) });
|
|
26315
|
-
}
|
|
26316
|
-
if (body.confirm === true) {
|
|
26317
|
-
deps.configService.reload();
|
|
26318
|
-
}
|
|
26319
|
-
const newVersion = result.report.version ?? null;
|
|
26320
|
-
const eventData = {
|
|
26321
|
-
nodePath: node.path,
|
|
26322
|
-
version: newVersion,
|
|
26323
|
-
status: STATUS_FRESH
|
|
26324
|
-
};
|
|
26325
|
-
const wsEnvelope = {
|
|
26326
|
-
type: ENVELOPE_KIND2,
|
|
26327
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26328
|
-
data: eventData
|
|
26329
|
-
};
|
|
26330
|
-
deps.broadcaster.broadcast(wsEnvelope);
|
|
26331
|
-
const envelope = {
|
|
26332
|
-
schemaVersion: "1",
|
|
26333
|
-
kind: ENVELOPE_KIND2,
|
|
26334
|
-
value: {
|
|
26335
|
-
nodePath: node.path,
|
|
26336
|
-
version: newVersion,
|
|
26337
|
-
status: STATUS_FRESH
|
|
26338
|
-
},
|
|
26339
|
-
elapsedMs: Date.now() - startedAt
|
|
26340
|
-
};
|
|
26341
|
-
return c.json(envelope);
|
|
26342
|
-
});
|
|
26343
|
-
}
|
|
26344
|
-
async function loadNode(deps, nodePath) {
|
|
26345
|
-
const persisted = await tryWithSqlite(
|
|
26346
|
-
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
26347
|
-
async (adapter) => adapter.scans.load()
|
|
26348
|
-
);
|
|
26349
|
-
const node = persisted?.nodes.find((n) => n.path === nodePath);
|
|
26350
|
-
if (!node) {
|
|
26351
|
-
throw new HTTPException15(404, {
|
|
26352
|
-
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
26353
|
-
});
|
|
26354
|
-
}
|
|
26355
|
-
return node;
|
|
26356
|
-
}
|
|
26357
|
-
function invokeBump2(node, absPath, body) {
|
|
26358
|
-
if (!nodeBumpAction.invoke) {
|
|
26359
|
-
throw new HTTPException15(500, { message: SERVER_TEXTS.sidecarBumpInvokeMissing });
|
|
26360
|
-
}
|
|
26361
|
-
const input = {};
|
|
26362
|
-
if (body.force === true) input.force = true;
|
|
26363
|
-
return nodeBumpAction.invoke(input, {
|
|
26364
|
-
node,
|
|
26365
|
-
nodeAbsolutePath: absPath,
|
|
26366
|
-
invoker: "ui",
|
|
26367
|
-
now: () => /* @__PURE__ */ new Date(),
|
|
26368
|
-
settings: {}
|
|
26369
|
-
});
|
|
26370
|
-
}
|
|
26371
|
-
function pickExistingVersion(node) {
|
|
26372
|
-
const overlay = node.sidecar;
|
|
26373
|
-
if (!overlay || overlay.present !== true) return null;
|
|
26374
|
-
const annotations = overlay.annotations;
|
|
26375
|
-
if (!annotations) return null;
|
|
26376
|
-
const v = annotations["version"];
|
|
26377
|
-
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
26378
|
-
}
|
|
26379
|
-
|
|
26380
27060
|
// server/routes/update-status.ts
|
|
26381
27061
|
function registerUpdateStatusRoute(app, deps) {
|
|
26382
27062
|
app.get("/api/update-status", async (c) => {
|
|
@@ -26543,13 +27223,13 @@ function attachBroadcasterRoute(app, broadcaster) {
|
|
|
26543
27223
|
|
|
26544
27224
|
// server/app.ts
|
|
26545
27225
|
var BODY_LIMIT_BYTES = 1024 * 1024;
|
|
26546
|
-
var DbMissingError = class extends
|
|
27226
|
+
var DbMissingError = class extends HTTPException17 {
|
|
26547
27227
|
constructor(message) {
|
|
26548
27228
|
super(500, { message });
|
|
26549
27229
|
this.name = "DbMissingError";
|
|
26550
27230
|
}
|
|
26551
27231
|
};
|
|
26552
|
-
var BulkValidationError = class extends
|
|
27232
|
+
var BulkValidationError = class extends HTTPException17 {
|
|
26553
27233
|
id;
|
|
26554
27234
|
code;
|
|
26555
27235
|
constructor(init) {
|
|
@@ -26559,7 +27239,7 @@ var BulkValidationError = class extends HTTPException16 {
|
|
|
26559
27239
|
this.code = init.code;
|
|
26560
27240
|
}
|
|
26561
27241
|
};
|
|
26562
|
-
var LoopbackGateError = class extends
|
|
27242
|
+
var LoopbackGateError = class extends HTTPException17 {
|
|
26563
27243
|
code;
|
|
26564
27244
|
constructor(init) {
|
|
26565
27245
|
super(403, { message: init.message });
|
|
@@ -26567,7 +27247,7 @@ var LoopbackGateError = class extends HTTPException16 {
|
|
|
26567
27247
|
this.code = init.code;
|
|
26568
27248
|
}
|
|
26569
27249
|
};
|
|
26570
|
-
var ConflictError = class extends
|
|
27250
|
+
var ConflictError = class extends HTTPException17 {
|
|
26571
27251
|
code;
|
|
26572
27252
|
constructor(init) {
|
|
26573
27253
|
super(409, { message: init.message });
|
|
@@ -26575,6 +27255,21 @@ var ConflictError = class extends HTTPException16 {
|
|
|
26575
27255
|
this.code = init.code;
|
|
26576
27256
|
}
|
|
26577
27257
|
};
|
|
27258
|
+
var ActionRefusedError = class extends HTTPException17 {
|
|
27259
|
+
/** Refusal code: the report's `reason` when present, else `'action-refused'`. */
|
|
27260
|
+
code;
|
|
27261
|
+
details;
|
|
27262
|
+
constructor(init) {
|
|
27263
|
+
super(409, { message: init.message });
|
|
27264
|
+
this.name = "ActionRefusedError";
|
|
27265
|
+
this.code = init.code;
|
|
27266
|
+
this.details = {
|
|
27267
|
+
actionId: init.actionId,
|
|
27268
|
+
nodePath: init.nodePath,
|
|
27269
|
+
report: init.report
|
|
27270
|
+
};
|
|
27271
|
+
}
|
|
27272
|
+
};
|
|
26578
27273
|
function createApp(deps) {
|
|
26579
27274
|
const app = new Hono();
|
|
26580
27275
|
const configService = new ConfigService({
|
|
@@ -26588,7 +27283,7 @@ function createApp(deps) {
|
|
|
26588
27283
|
bodyLimit({
|
|
26589
27284
|
maxSize: BODY_LIMIT_BYTES,
|
|
26590
27285
|
onError: () => {
|
|
26591
|
-
throw new
|
|
27286
|
+
throw new HTTPException17(413, { message: tx(SERVER_TEXTS.bodyTooLarge, { maxBytes: String(BODY_LIMIT_BYTES) }) });
|
|
26592
27287
|
}
|
|
26593
27288
|
})
|
|
26594
27289
|
);
|
|
@@ -26624,7 +27319,7 @@ function createApp(deps) {
|
|
|
26624
27319
|
registerGraphRoute(app, routeDeps);
|
|
26625
27320
|
registerConfigRoute(app, routeDeps);
|
|
26626
27321
|
registerPluginsRoute(app, routeDeps);
|
|
26627
|
-
|
|
27322
|
+
registerActionsRoutes(app, { ...routeDeps, broadcaster: deps.broadcaster, kernel: deps.kernel });
|
|
26628
27323
|
registerFavoritesRoutes(app, routeDeps);
|
|
26629
27324
|
registerAnnotationsRoute(app, { kernel: deps.kernel });
|
|
26630
27325
|
registerContributionsRoutes(app, { ...routeDeps, kernel: deps.kernel });
|
|
@@ -26634,7 +27329,7 @@ function createApp(deps) {
|
|
|
26634
27329
|
registerActiveProviderRoute(app, routeDeps);
|
|
26635
27330
|
registerProjectIgnoreRoute(app, routeDeps);
|
|
26636
27331
|
app.all("/api/*", (c) => {
|
|
26637
|
-
throw new
|
|
27332
|
+
throw new HTTPException17(404, {
|
|
26638
27333
|
message: tx(SERVER_TEXTS.unknownApiEndpoint, { path: sanitizeForTerminal(c.req.path) })
|
|
26639
27334
|
});
|
|
26640
27335
|
});
|
|
@@ -26642,7 +27337,7 @@ function createApp(deps) {
|
|
|
26642
27337
|
app.use("*", createStaticHandler({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
26643
27338
|
app.get("*", createSpaFallback({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
26644
27339
|
app.notFound((c) => {
|
|
26645
|
-
throw new
|
|
27340
|
+
throw new HTTPException17(404, {
|
|
26646
27341
|
message: tx(SERVER_TEXTS.unknownPath, { path: sanitizeForTerminal(c.req.path) })
|
|
26647
27342
|
});
|
|
26648
27343
|
});
|
|
@@ -26693,18 +27388,9 @@ function formatError2(err, c) {
|
|
|
26693
27388
|
};
|
|
26694
27389
|
return c.json(envelope, 403);
|
|
26695
27390
|
}
|
|
26696
|
-
|
|
26697
|
-
|
|
26698
|
-
|
|
26699
|
-
error: {
|
|
26700
|
-
code: err.code,
|
|
26701
|
-
message: err.message,
|
|
26702
|
-
details: null
|
|
26703
|
-
}
|
|
26704
|
-
};
|
|
26705
|
-
return c.json(envelope, 409);
|
|
26706
|
-
}
|
|
26707
|
-
if (err instanceof HTTPException16) {
|
|
27391
|
+
const conflict = formatConflict(err, c);
|
|
27392
|
+
if (conflict) return conflict;
|
|
27393
|
+
if (err instanceof HTTPException17) {
|
|
26708
27394
|
const status = err.status;
|
|
26709
27395
|
const envelope = {
|
|
26710
27396
|
ok: false,
|
|
@@ -26740,6 +27426,23 @@ function formatError2(err, c) {
|
|
|
26740
27426
|
}
|
|
26741
27427
|
return formatInternalErrorFallThrough(err, c);
|
|
26742
27428
|
}
|
|
27429
|
+
function formatConflict(err, c) {
|
|
27430
|
+
if (err instanceof ActionRefusedError) {
|
|
27431
|
+
const envelope = {
|
|
27432
|
+
ok: false,
|
|
27433
|
+
error: { code: err.code, message: err.message, details: err.details }
|
|
27434
|
+
};
|
|
27435
|
+
return c.json(envelope, 409);
|
|
27436
|
+
}
|
|
27437
|
+
if (err instanceof ConflictError) {
|
|
27438
|
+
const envelope = {
|
|
27439
|
+
ok: false,
|
|
27440
|
+
error: { code: err.code, message: err.message, details: null }
|
|
27441
|
+
};
|
|
27442
|
+
return c.json(envelope, 409);
|
|
27443
|
+
}
|
|
27444
|
+
return null;
|
|
27445
|
+
}
|
|
26743
27446
|
function formatInternalErrorFallThrough(err, c) {
|
|
26744
27447
|
const detail = formatErrorMessage(err);
|
|
26745
27448
|
const stack = err instanceof Error && typeof err.stack === "string" ? err.stack : void 0;
|
|
@@ -26965,6 +27668,8 @@ function entryFromRegistered(c) {
|
|
|
26965
27668
|
if (c.icon !== void 0) entry.icon = c.icon;
|
|
26966
27669
|
if (c.emptyText !== void 0) entry.emptyText = c.emptyText;
|
|
26967
27670
|
if (c.priority !== void 0) entry.priority = c.priority;
|
|
27671
|
+
if (c.pluginOrder !== void 0) entry.pluginOrder = c.pluginOrder;
|
|
27672
|
+
if (c.extensionOrder !== void 0) entry.extensionOrder = c.extensionOrder;
|
|
26968
27673
|
return entry;
|
|
26969
27674
|
}
|
|
26970
27675
|
|
|
@@ -27222,8 +27927,10 @@ async function createServer(options, extra = {}) {
|
|
|
27222
27927
|
}
|
|
27223
27928
|
async function assemblePluginRuntime(options, runtimeContext) {
|
|
27224
27929
|
const pluginRuntime = options.noPlugins ? emptyPluginRuntime() : await loadPluginRuntime({ runtimeContext });
|
|
27225
|
-
|
|
27226
|
-
|
|
27930
|
+
if (options.noWatcher) {
|
|
27931
|
+
for (const warn of pluginRuntime.warnings) {
|
|
27932
|
+
log.warn(sanitizeForTerminal(warn));
|
|
27933
|
+
}
|
|
27227
27934
|
}
|
|
27228
27935
|
const builtInProviders = options.noBuiltIns ? [] : collectBuiltInProviders();
|
|
27229
27936
|
const allProviders = [...builtInProviders, ...pluginRuntime.extensions.providers];
|
|
@@ -27234,6 +27941,11 @@ async function assemblePluginRuntime(options, runtimeContext) {
|
|
|
27234
27941
|
function assembleKernel(pluginRuntime, noBuiltIns) {
|
|
27235
27942
|
const kernel = createKernel();
|
|
27236
27943
|
kernel.setRegisteredAnnotationKeys(pluginRuntime.annotationContributions);
|
|
27944
|
+
if (!noBuiltIns) {
|
|
27945
|
+
for (const action of builtIns().actions) {
|
|
27946
|
+
kernel.registry.register(action);
|
|
27947
|
+
}
|
|
27948
|
+
}
|
|
27237
27949
|
const mergedViewContributions = [...pluginRuntime.viewContributions];
|
|
27238
27950
|
if (!noBuiltIns) {
|
|
27239
27951
|
const userKey = new Set(
|
|
@@ -28103,13 +28815,13 @@ var ShowCommand = class extends SmCommand {
|
|
|
28103
28815
|
}
|
|
28104
28816
|
};
|
|
28105
28817
|
function renderHuman2(doc, ansi) {
|
|
28106
|
-
const { node, linksOut, linksIn, issues } = doc;
|
|
28818
|
+
const { node, linksOut: linksOut2, linksIn: linksIn2, issues } = doc;
|
|
28107
28819
|
const out = [];
|
|
28108
28820
|
out.push(renderHeader(node, ansi));
|
|
28109
28821
|
out.push(renderFieldBlock(node, ansi));
|
|
28110
28822
|
out.push(renderFrontmatter(node, ansi));
|
|
28111
|
-
if (
|
|
28112
|
-
if (
|
|
28823
|
+
if (linksOut2.length > 0) out.push(renderLinksSection("out", linksOut2, ansi));
|
|
28824
|
+
if (linksIn2.length > 0) out.push(renderLinksSection("in", linksIn2, ansi));
|
|
28113
28825
|
if (issues.length > 0) out.push(renderIssuesSection(issues, node.path, ansi));
|
|
28114
28826
|
return out.join("");
|
|
28115
28827
|
}
|
|
@@ -28501,7 +29213,9 @@ var SidecarRefreshCommand = class extends SmCommand {
|
|
|
28501
29213
|
frontmatterHash: node.frontmatterHash
|
|
28502
29214
|
}
|
|
28503
29215
|
},
|
|
28504
|
-
|
|
29216
|
+
// Step 17 split: CLI accept / `--yes` persists the grant, so it
|
|
29217
|
+
// threads `always`, not the new one-shot `confirm`.
|
|
29218
|
+
{ confirm: this.yes, always: this.yes, cwd: ctx.cwd }
|
|
28505
29219
|
);
|
|
28506
29220
|
} catch (err) {
|
|
28507
29221
|
if (err instanceof EConsentRequiredError) throw err;
|
|
@@ -28782,7 +29496,9 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
28782
29496
|
await store.applyPatch(
|
|
28783
29497
|
sidecarAbsPath,
|
|
28784
29498
|
scaffoldSidecarObject(node),
|
|
28785
|
-
|
|
29499
|
+
// Step 17 split: CLI accept / `--yes` persists the grant, so it
|
|
29500
|
+
// threads `always`, not the new one-shot `confirm`.
|
|
29501
|
+
{ confirm: this.yes, always: this.yes, cwd: ctx.cwd }
|
|
28786
29502
|
);
|
|
28787
29503
|
} catch (err) {
|
|
28788
29504
|
if (err instanceof EConsentRequiredError) throw err;
|
|
@@ -29043,11 +29759,13 @@ var TUTORIAL_TEXTS = {
|
|
|
29043
29759
|
// the error shape: glyph + headline + dim hint.
|
|
29044
29760
|
notEmpty: "{{glyph}} sm tutorial: the current directory is not empty (found {{entries}})\n {{hint}}\n",
|
|
29045
29761
|
notEmptyHint: "sm tutorial seeds a self-contained scenario; run it in a fresh empty directory, or pass `--force` to use this one anyway.",
|
|
29046
|
-
//
|
|
29047
|
-
//
|
|
29048
|
-
//
|
|
29049
|
-
|
|
29050
|
-
|
|
29762
|
+
// Legacy positional argument (e.g. a stale `sm tutorial master`). The
|
|
29763
|
+
// verb no longer takes a positional: there is a single umbrella skill
|
|
29764
|
+
// and the advanced walkthrough is a menu choice inside it, not a
|
|
29765
|
+
// separate install. Goes to stderr, exit code 2. Mirrors the error
|
|
29766
|
+
// shape: glyph + headline + dim hint.
|
|
29767
|
+
legacyPositional: "{{glyph}} sm tutorial: unexpected argument '{{arg}}'\n {{hint}}\n",
|
|
29768
|
+
legacyPositionalHint: "sm tutorial takes no positional argument. The master walkthrough is no longer a separate install; run `sm tutorial` and pick the advanced parts from the in-skill menu.",
|
|
29051
29769
|
// I/O failure on write or on reading the bundled skill source.
|
|
29052
29770
|
writeFailed: "{{glyph}} sm tutorial: failed to write {{target}}: {{message}}\n",
|
|
29053
29771
|
sourceMissing: "{{glyph}} sm tutorial: could not read the bundled skill payload for {{target}} from the install.\n {{hint}}\n",
|
|
@@ -29055,47 +29773,36 @@ var TUTORIAL_TEXTS = {
|
|
|
29055
29773
|
};
|
|
29056
29774
|
|
|
29057
29775
|
// cli/commands/tutorial.ts
|
|
29058
|
-
var
|
|
29059
|
-
var
|
|
29060
|
-
var
|
|
29061
|
-
|
|
29062
|
-
slug: "sm-tutorial",
|
|
29063
|
-
sourceDir: ".claude/skills/sm-tutorial",
|
|
29064
|
-
triggerEn: "run the tutorial",
|
|
29065
|
-
triggerEs: "ejecuta el tutorial"
|
|
29066
|
-
},
|
|
29067
|
-
master: {
|
|
29068
|
-
slug: "sm-master",
|
|
29069
|
-
sourceDir: ".claude/skills/sm-master",
|
|
29070
|
-
triggerEn: "run the master tutorial",
|
|
29071
|
-
triggerEs: "ejecuta el tutorial maestro"
|
|
29072
|
-
}
|
|
29073
|
-
};
|
|
29776
|
+
var SKILL_SLUG = "sm-tutorial";
|
|
29777
|
+
var SKILL_SOURCE_DIR = ".claude/skills/sm-tutorial";
|
|
29778
|
+
var TRIGGER_EN = "run the tutorial";
|
|
29779
|
+
var TRIGGER_ES = "ejecuta el tutorial";
|
|
29074
29780
|
var TutorialCommand = class extends SmCommand {
|
|
29075
29781
|
static paths = [["tutorial"]];
|
|
29076
29782
|
static usage = Command37.Usage({
|
|
29077
29783
|
category: "Setup",
|
|
29078
29784
|
description: "Materialize an interactive tester tutorial as a Claude Code skill folder under `<cwd>/.claude/skills/`.",
|
|
29079
29785
|
details: `
|
|
29080
|
-
Drops the canonical skill directory (SKILL.md +
|
|
29081
|
-
sub-folder) under \`<cwd>/.claude/skills/sm-tutorial
|
|
29082
|
-
|
|
29083
|
-
|
|
29084
|
-
|
|
29085
|
-
speaking one of its trigger phrases.
|
|
29786
|
+
Drops the canonical skill directory (SKILL.md + its references/
|
|
29787
|
+
sub-folder) under \`<cwd>/.claude/skills/sm-tutorial/\`. Claude
|
|
29788
|
+
Code auto-discovers the skill the next time it boots in this
|
|
29789
|
+
directory; the tester invokes it by speaking one of its trigger
|
|
29790
|
+
phrases and picks the advanced parts from the in-skill menu.
|
|
29086
29791
|
|
|
29087
29792
|
Does NOT require an initialized .skill-map/ project. Refuses to
|
|
29088
|
-
overwrite the target directory unless --force is passed.
|
|
29089
|
-
|
|
29090
|
-
master.
|
|
29793
|
+
overwrite the target directory unless --force is passed. Takes no
|
|
29794
|
+
positional argument.
|
|
29091
29795
|
`,
|
|
29092
29796
|
examples: [
|
|
29093
|
-
["Materialize the
|
|
29094
|
-
["Materialize the advanced tutorial skill in the cwd", "$0 tutorial master"],
|
|
29797
|
+
["Materialize the tutorial skill in the cwd", "$0 tutorial"],
|
|
29095
29798
|
["Overwrite an existing target directory", "$0 tutorial --force"]
|
|
29096
29799
|
]
|
|
29097
29800
|
});
|
|
29098
|
-
|
|
29801
|
+
// Legacy positional catcher: the verb takes no positional argument any
|
|
29802
|
+
// more. Accept one so a stale `sm tutorial master` lands on a friendly
|
|
29803
|
+
// usage error (guarded in `run()`) instead of clipanion's generic
|
|
29804
|
+
// "extraneous argument" message.
|
|
29805
|
+
legacyPositional = Option35.String({ required: false });
|
|
29099
29806
|
// Named `forProvider`, NOT `for` (reserved word). The CLI surface stays
|
|
29100
29807
|
// `--for`; selects the destination Provider whose `scaffold.skillDir`
|
|
29101
29808
|
// the skill is materialised under, skipping the interactive prompt.
|
|
@@ -29111,9 +29818,16 @@ var TutorialCommand = class extends SmCommand {
|
|
|
29111
29818
|
const stderr = this.context.stderr;
|
|
29112
29819
|
const stderrAnsi = this.ansiFor("stderr");
|
|
29113
29820
|
const errGlyph = stderrAnsi.red("\u2715");
|
|
29114
|
-
|
|
29115
|
-
|
|
29116
|
-
|
|
29821
|
+
if (this.legacyPositional !== void 0) {
|
|
29822
|
+
this.printer.error(
|
|
29823
|
+
tx(TUTORIAL_TEXTS.legacyPositional, {
|
|
29824
|
+
glyph: errGlyph,
|
|
29825
|
+
arg: this.legacyPositional,
|
|
29826
|
+
hint: stderrAnsi.dim(TUTORIAL_TEXTS.legacyPositionalHint)
|
|
29827
|
+
})
|
|
29828
|
+
);
|
|
29829
|
+
return ExitCode.Error;
|
|
29830
|
+
}
|
|
29117
29831
|
if (!this.force && !isDirEmpty(ctx.cwd)) {
|
|
29118
29832
|
this.printer.error(
|
|
29119
29833
|
tx(TUTORIAL_TEXTS.notEmpty, {
|
|
@@ -29127,11 +29841,11 @@ var TutorialCommand = class extends SmCommand {
|
|
|
29127
29841
|
const targets = listScaffoldTargets();
|
|
29128
29842
|
const target = await this.resolveScaffoldTarget(targets, stderrAnsi, errGlyph);
|
|
29129
29843
|
if (target === null) return ExitCode.Error;
|
|
29130
|
-
const targetDir = join21(ctx.cwd, target.skillDir,
|
|
29131
|
-
const targetDisplay = `${target.skillDir}/${
|
|
29844
|
+
const targetDir = join21(ctx.cwd, target.skillDir, SKILL_SLUG);
|
|
29845
|
+
const targetDisplay = `${target.skillDir}/${SKILL_SLUG}/`;
|
|
29132
29846
|
let sourceDir;
|
|
29133
29847
|
try {
|
|
29134
|
-
sourceDir = resolveSkillSourceDir(
|
|
29848
|
+
sourceDir = resolveSkillSourceDir();
|
|
29135
29849
|
} catch {
|
|
29136
29850
|
this.printer.error(
|
|
29137
29851
|
tx(TUTORIAL_TEXTS.sourceMissing, {
|
|
@@ -29166,38 +29880,18 @@ var TutorialCommand = class extends SmCommand {
|
|
|
29166
29880
|
this.printer.data(
|
|
29167
29881
|
tx(TUTORIAL_TEXTS.written, {
|
|
29168
29882
|
glyph: ansi.green("\u2713"),
|
|
29169
|
-
slug:
|
|
29883
|
+
slug: SKILL_SLUG,
|
|
29170
29884
|
target: targetDisplay,
|
|
29171
29885
|
provider: ansi.dim(target.label),
|
|
29172
29886
|
cwd: ansi.dim(displayCwd(ctx.cwd)),
|
|
29173
29887
|
enLabel: ansi.dim(TUTORIAL_TEXTS.writtenLabelEn),
|
|
29174
29888
|
esLabel: ansi.dim(TUTORIAL_TEXTS.writtenLabelEs),
|
|
29175
|
-
enTrigger:
|
|
29176
|
-
esTrigger:
|
|
29889
|
+
enTrigger: TRIGGER_EN,
|
|
29890
|
+
esTrigger: TRIGGER_ES
|
|
29177
29891
|
})
|
|
29178
29892
|
);
|
|
29179
29893
|
return ExitCode.Ok;
|
|
29180
29894
|
}
|
|
29181
|
-
/**
|
|
29182
|
-
* Validate the positional `variant` arg against the closed catalog.
|
|
29183
|
-
* Returns the resolved variant, or `null` after printing the
|
|
29184
|
-
* `invalidVariant` error (caller exits non-zero). Extracted from
|
|
29185
|
-
* `run()` to keep its cyclomatic complexity within the lint budget.
|
|
29186
|
-
*/
|
|
29187
|
-
resolveVariantArg(errGlyph, stderrAnsi) {
|
|
29188
|
-
const rawVariant = this.variant;
|
|
29189
|
-
if (rawVariant !== void 0 && !isTutorialVariant(rawVariant)) {
|
|
29190
|
-
this.printer.error(
|
|
29191
|
-
tx(TUTORIAL_TEXTS.invalidVariant, {
|
|
29192
|
-
glyph: errGlyph,
|
|
29193
|
-
variant: rawVariant,
|
|
29194
|
-
hint: stderrAnsi.dim(TUTORIAL_TEXTS.invalidVariantHint)
|
|
29195
|
-
})
|
|
29196
|
-
);
|
|
29197
|
-
return null;
|
|
29198
|
-
}
|
|
29199
|
-
return rawVariant ?? DEFAULT_VARIANT;
|
|
29200
|
-
}
|
|
29201
29895
|
/**
|
|
29202
29896
|
* Resolve the destination Provider. Precedence:
|
|
29203
29897
|
* 1. `--for <id>` (validated against the scaffold-capable catalog).
|
|
@@ -29257,9 +29951,6 @@ var TutorialCommand = class extends SmCommand {
|
|
|
29257
29951
|
return picked;
|
|
29258
29952
|
}
|
|
29259
29953
|
};
|
|
29260
|
-
function isTutorialVariant(value) {
|
|
29261
|
-
return VALID_VARIANTS.includes(value);
|
|
29262
|
-
}
|
|
29263
29954
|
function toScaffoldTarget(provider) {
|
|
29264
29955
|
const scaffold = provider.scaffold;
|
|
29265
29956
|
if (!scaffold || !scaffold.skillDir) return null;
|
|
@@ -29326,23 +30017,21 @@ function listCwdEntries(dir) {
|
|
|
29326
30017
|
const more = entries.length > shown.length ? ", ..." : "";
|
|
29327
30018
|
return shown.join(", ") + more;
|
|
29328
30019
|
}
|
|
29329
|
-
var
|
|
29330
|
-
function resolveSkillSourceDir(
|
|
29331
|
-
|
|
29332
|
-
if (cached !== void 0) return cached;
|
|
29333
|
-
const spec = VARIANT_SPECS[variant];
|
|
30020
|
+
var cachedSourceDir;
|
|
30021
|
+
function resolveSkillSourceDir() {
|
|
30022
|
+
if (cachedSourceDir !== void 0) return cachedSourceDir;
|
|
29334
30023
|
const here = dirname20(fileURLToPath7(import.meta.url));
|
|
29335
30024
|
const candidates = [
|
|
29336
|
-
// dev: src/cli/commands/ → repo-root .claude/skills
|
|
29337
|
-
resolve39(here, "../../..",
|
|
29338
|
-
// bundled: dist/cli.js → dist/cli/tutorial
|
|
29339
|
-
resolve39(here, "cli/tutorial",
|
|
29340
|
-
// bundled fallback: any-depth → cli/tutorial
|
|
29341
|
-
resolve39(here, "../cli/tutorial",
|
|
30025
|
+
// dev: src/cli/commands/ → repo-root .claude/skills/sm-tutorial/
|
|
30026
|
+
resolve39(here, "../../..", SKILL_SOURCE_DIR),
|
|
30027
|
+
// bundled: dist/cli.js → dist/cli/tutorial/sm-tutorial (sibling)
|
|
30028
|
+
resolve39(here, "cli/tutorial", SKILL_SLUG),
|
|
30029
|
+
// bundled fallback: any-depth → cli/tutorial/sm-tutorial
|
|
30030
|
+
resolve39(here, "../cli/tutorial", SKILL_SLUG)
|
|
29342
30031
|
];
|
|
29343
30032
|
for (const candidate of candidates) {
|
|
29344
30033
|
if (existsSync32(candidate) && statSync11(candidate).isDirectory()) {
|
|
29345
|
-
|
|
30034
|
+
cachedSourceDir = candidate;
|
|
29346
30035
|
return candidate;
|
|
29347
30036
|
}
|
|
29348
30037
|
}
|
|
@@ -29566,4 +30255,4 @@ function resolveBareDefault() {
|
|
|
29566
30255
|
process.exit(ExitCode.Error);
|
|
29567
30256
|
}
|
|
29568
30257
|
//# sourceMappingURL=cli.js.map
|
|
29569
|
-
//# debugId=
|
|
30258
|
+
//# debugId=82aaa5c6-6dae-53a6-8d4a-1a673357051c
|