@skill-map/cli 0.52.0 → 0.53.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/tutorial/sm-tutorial/SKILL.md +248 -1657
- package/dist/cli/tutorial/sm-tutorial/references/_core.md +374 -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 +395 -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 +1222 -563
- package/dist/index.d.ts +1 -1
- package/dist/index.js +334 -207
- package/dist/kernel/index.d.ts +320 -15
- package/dist/kernel/index.js +334 -207
- 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-ZNDMBION.js → chunk-HGM5UYEG.js} +28 -30
- package/dist/ui/chunk-IUZM6XLN.js +2 -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-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-REAKWFJX.js +123 -0
- package/dist/ui/chunk-UBQUCSQ4.js +1 -0
- package/dist/ui/chunk-WFLPMCK4.js +392 -0
- package/dist/ui/index.html +2 -2
- package/dist/ui/{main-2DWVSRRX.js → main-XRWWHCSW.js} +3 -3
- package/dist/ui/{styles-QBTVKEVX.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-5MCXQKRN.js +0 -1066
- package/dist/ui/chunk-6B5EAHIM.js +0 -1110
- package/dist/ui/chunk-AEA5GIA7.js +0 -1
- package/dist/ui/chunk-AQN27TN2.js +0 -123
- package/dist/ui/chunk-CAJ7ZI44.js +0 -1
- package/dist/ui/chunk-E2XO4JVD.js +0 -1
- package/dist/ui/chunk-VJ57LHDR.js +0 -4
- package/dist/ui/chunk-WMGW2UAL.js +0 -2
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]="dd2e1877-e17a-5d0b-86e5-6416814d7a78")}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.1",
|
|
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,64 +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
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)
|
|
1979
1978
|
});
|
|
1979
|
+
ctx.emitContribution(node.path, staleBadge, {
|
|
1980
|
+
icon: "pi-clock",
|
|
1981
|
+
tooltip: tooltipFor(status)
|
|
1982
|
+
});
|
|
1980
1983
|
}
|
|
1981
1984
|
return issues;
|
|
1982
1985
|
}
|
|
1983
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
|
+
}
|
|
1984
2011
|
function tooltipFor(status) {
|
|
1985
2012
|
switch (status) {
|
|
1986
2013
|
case "stale-body":
|
|
@@ -2015,6 +2042,18 @@ var ISSUE_COUNTER_TEXTS = {
|
|
|
2015
2042
|
|
|
2016
2043
|
// plugins/core/analyzers/issue-counter/index.ts
|
|
2017
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
|
+
};
|
|
2018
2057
|
function countByTier(issues) {
|
|
2019
2058
|
const errors = /* @__PURE__ */ new Map();
|
|
2020
2059
|
const warns = /* @__PURE__ */ new Map();
|
|
@@ -2027,13 +2066,13 @@ function countByTier(issues) {
|
|
|
2027
2066
|
}
|
|
2028
2067
|
return { errors, warns };
|
|
2029
2068
|
}
|
|
2030
|
-
function emitTierChips(ctx,
|
|
2031
|
-
for (const [nodePath,
|
|
2032
|
-
const capped = Math.min(
|
|
2033
|
-
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, {
|
|
2034
2073
|
value: capped,
|
|
2035
2074
|
severity,
|
|
2036
|
-
tooltip:
|
|
2075
|
+
tooltip: count3 === 1 ? singleTooltip : tx(manyTooltip, { count: count3 })
|
|
2037
2076
|
});
|
|
2038
2077
|
}
|
|
2039
2078
|
}
|
|
@@ -2044,33 +2083,14 @@ var issueCounterAnalyzer = {
|
|
|
2044
2083
|
description: "Emits one aggregate severity chip per node (error + warn counts) from the live issue accumulator.",
|
|
2045
2084
|
mode: "deterministic",
|
|
2046
2085
|
phase: "aggregate",
|
|
2047
|
-
ui: {
|
|
2048
|
-
// Third in the footer-right cluster, after the drift chip
|
|
2049
|
-
// (priority 10) and the stability badge (priority 20). The warn
|
|
2050
|
-
// counter sits before the error counter so the operator reads
|
|
2051
|
-
// "advisory → blocking" left-to-right.
|
|
2052
|
-
warnCount: {
|
|
2053
|
-
slot: "card.footer.right",
|
|
2054
|
-
icon: "pi-exclamation-triangle",
|
|
2055
|
-
emitWhenEmpty: false,
|
|
2056
|
-
priority: 30
|
|
2057
|
-
},
|
|
2058
|
-
// Last in the cluster, the red chip pins to the right edge so the
|
|
2059
|
-
// most severe signal anchors the row's reading position.
|
|
2060
|
-
errorCount: {
|
|
2061
|
-
slot: "card.footer.right",
|
|
2062
|
-
icon: "pi-times-circle",
|
|
2063
|
-
emitWhenEmpty: false,
|
|
2064
|
-
priority: 40
|
|
2065
|
-
}
|
|
2066
|
-
},
|
|
2086
|
+
ui: { warnCount, errorCount },
|
|
2067
2087
|
evaluate(ctx) {
|
|
2068
2088
|
const accumulator = ctx.accumulatedIssues ?? [];
|
|
2069
2089
|
if (accumulator.length === 0) return [];
|
|
2070
2090
|
const { errors, warns } = countByTier(accumulator);
|
|
2071
2091
|
emitTierChips(
|
|
2072
2092
|
ctx,
|
|
2073
|
-
|
|
2093
|
+
errorCount,
|
|
2074
2094
|
"danger",
|
|
2075
2095
|
errors,
|
|
2076
2096
|
ISSUE_COUNTER_TEXTS.errorTooltipSingle,
|
|
@@ -2078,7 +2098,7 @@ var issueCounterAnalyzer = {
|
|
|
2078
2098
|
);
|
|
2079
2099
|
emitTierChips(
|
|
2080
2100
|
ctx,
|
|
2081
|
-
|
|
2101
|
+
warnCount,
|
|
2082
2102
|
"warn",
|
|
2083
2103
|
warns,
|
|
2084
2104
|
ISSUE_COUNTER_TEXTS.warnTooltipSingle,
|
|
@@ -2246,28 +2266,27 @@ function resolveLinkTargetToPath(link, nameIndex) {
|
|
|
2246
2266
|
|
|
2247
2267
|
// plugins/core/analyzers/link-counter/index.ts
|
|
2248
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
|
+
};
|
|
2249
2283
|
var linkCounterAnalyzer = {
|
|
2250
2284
|
id: ID15,
|
|
2251
2285
|
pluginId: CORE_PLUGIN_ID,
|
|
2252
2286
|
kind: "analyzer",
|
|
2253
2287
|
description: "Counts incoming and outgoing links per node.",
|
|
2254
2288
|
mode: "deterministic",
|
|
2255
|
-
ui: {
|
|
2256
|
-
linksIn: {
|
|
2257
|
-
slot: "card.footer.left",
|
|
2258
|
-
icon: "pi-download",
|
|
2259
|
-
label: "incoming links",
|
|
2260
|
-
emitWhenEmpty: false,
|
|
2261
|
-
priority: 10
|
|
2262
|
-
},
|
|
2263
|
-
linksOut: {
|
|
2264
|
-
slot: "card.footer.left",
|
|
2265
|
-
icon: "pi-upload",
|
|
2266
|
-
label: "outgoing links",
|
|
2267
|
-
emitWhenEmpty: false,
|
|
2268
|
-
priority: 20
|
|
2269
|
-
}
|
|
2270
|
-
},
|
|
2289
|
+
ui: { linksIn, linksOut },
|
|
2271
2290
|
evaluate(ctx) {
|
|
2272
2291
|
const nameIndex = buildNameIndex(ctx.nodes);
|
|
2273
2292
|
const perTarget = /* @__PURE__ */ new Map();
|
|
@@ -2279,8 +2298,8 @@ var linkCounterAnalyzer = {
|
|
|
2279
2298
|
bump(perSource, link.source, link.kind);
|
|
2280
2299
|
}
|
|
2281
2300
|
for (const node of ctx.nodes) {
|
|
2282
|
-
emitChip(ctx, node.path,
|
|
2283
|
-
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));
|
|
2284
2303
|
}
|
|
2285
2304
|
return [];
|
|
2286
2305
|
}
|
|
@@ -2293,14 +2312,13 @@ function bump(map, key, kind) {
|
|
|
2293
2312
|
}
|
|
2294
2313
|
byKind.set(kind, (byKind.get(kind) ?? 0) + 1);
|
|
2295
2314
|
}
|
|
2296
|
-
function emitChip(ctx, nodePath,
|
|
2315
|
+
function emitChip(ctx, nodePath, ref, direction, byKind) {
|
|
2297
2316
|
if (!byKind) return;
|
|
2298
2317
|
let total = 0;
|
|
2299
2318
|
for (const n of byKind.values()) total += n;
|
|
2300
2319
|
if (total === 0) return;
|
|
2301
2320
|
const capped = Math.min(total, 99);
|
|
2302
|
-
|
|
2303
|
-
ctx.emitContribution(nodePath, contributionId, {
|
|
2321
|
+
ctx.emitContribution(nodePath, ref, {
|
|
2304
2322
|
value: capped,
|
|
2305
2323
|
tooltip: formatBreakdown(byKind, direction)
|
|
2306
2324
|
});
|
|
@@ -2580,42 +2598,58 @@ function normaliseId(raw) {
|
|
|
2580
2598
|
return raw.normalize("NFD").replace(new RegExp("\\p{Mn}+", "gu"), "").toLowerCase().replace(/[-_\s]+/g, " ").replace(/ +/g, " ").trim();
|
|
2581
2599
|
}
|
|
2582
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
|
+
|
|
2583
2615
|
// plugins/core/analyzers/node-stability/index.ts
|
|
2584
2616
|
var ID18 = "node-stability";
|
|
2585
2617
|
var EXPERIMENTAL_TOOLTIP = "Experimental: API may change";
|
|
2586
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
|
+
};
|
|
2587
2637
|
var nodeStabilityAnalyzer = {
|
|
2588
2638
|
id: ID18,
|
|
2589
2639
|
pluginId: CORE_PLUGIN_ID,
|
|
2590
2640
|
kind: "analyzer",
|
|
2591
2641
|
description: "Reports a node's stability stage (`experimental`, `deprecated`) on the card.",
|
|
2592
2642
|
mode: "deterministic",
|
|
2593
|
-
ui: {
|
|
2594
|
-
// First in the footer-right cluster: stability is the node's
|
|
2595
|
-
// declared lifecycle state, so it leads, followed by the drift
|
|
2596
|
-
// chip and then the severity counters. It's a state badge, not a
|
|
2597
|
-
// count, so it stays left of the numeric zone.
|
|
2598
|
-
experimental: {
|
|
2599
|
-
slot: "card.footer.right",
|
|
2600
|
-
icon: "fa-solid fa-flask",
|
|
2601
|
-
label: "experimental",
|
|
2602
|
-
emitWhenEmpty: false,
|
|
2603
|
-
priority: 10
|
|
2604
|
-
},
|
|
2605
|
-
deprecated: {
|
|
2606
|
-
slot: "card.footer.right",
|
|
2607
|
-
icon: "pi-ban",
|
|
2608
|
-
label: "deprecated",
|
|
2609
|
-
emitWhenEmpty: false,
|
|
2610
|
-
priority: 10
|
|
2611
|
-
}
|
|
2612
|
-
},
|
|
2643
|
+
ui: { experimental, deprecated, setStabilityButton },
|
|
2613
2644
|
evaluate(ctx) {
|
|
2614
2645
|
const issues = [];
|
|
2615
2646
|
for (const node of ctx.nodes) {
|
|
2616
2647
|
const stability = readStability(node);
|
|
2648
|
+
if (node.sidecar?.present === true) {
|
|
2649
|
+
emitSetStabilityButton(ctx, node.path, stability ?? "stable");
|
|
2650
|
+
}
|
|
2617
2651
|
if (stability === "experimental") {
|
|
2618
|
-
ctx.emitContribution(node.path,
|
|
2652
|
+
ctx.emitContribution(node.path, experimental, {
|
|
2619
2653
|
value: 0,
|
|
2620
2654
|
tooltip: EXPERIMENTAL_TOOLTIP
|
|
2621
2655
|
});
|
|
@@ -2627,7 +2661,7 @@ var nodeStabilityAnalyzer = {
|
|
|
2627
2661
|
data: { stability }
|
|
2628
2662
|
});
|
|
2629
2663
|
} else if (stability === "deprecated") {
|
|
2630
|
-
ctx.emitContribution(node.path,
|
|
2664
|
+
ctx.emitContribution(node.path, deprecated, {
|
|
2631
2665
|
value: 0,
|
|
2632
2666
|
tooltip: DEPRECATED_TOOLTIP,
|
|
2633
2667
|
severity: "warn"
|
|
@@ -2659,6 +2693,25 @@ function readLegacyMetadataStability(fm) {
|
|
|
2659
2693
|
function isStability(value) {
|
|
2660
2694
|
return value === "experimental" || value === "deprecated" || value === "stable";
|
|
2661
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
|
+
}
|
|
2662
2715
|
|
|
2663
2716
|
// plugins/core/analyzers/node-superseded/text.ts
|
|
2664
2717
|
var NODE_SUPERSEDED_TEXTS = {
|
|
@@ -3021,8 +3074,8 @@ var ALL_SLOT_NAMES = [
|
|
|
3021
3074
|
"card.footer.left",
|
|
3022
3075
|
"card.footer.right",
|
|
3023
3076
|
"graph.node.alert",
|
|
3024
|
-
"inspector.header.badge
|
|
3025
|
-
"inspector.
|
|
3077
|
+
"inspector.header.badge",
|
|
3078
|
+
"inspector.action.button",
|
|
3026
3079
|
"inspector.body.panel.breakdown",
|
|
3027
3080
|
"inspector.body.panel.records",
|
|
3028
3081
|
"inspector.body.panel.tree",
|
|
@@ -3130,12 +3183,12 @@ function buildSchemaValidators() {
|
|
|
3130
3183
|
const v = validators.get(name);
|
|
3131
3184
|
if (!v) throw new Error(`Unknown schema: ${name}`);
|
|
3132
3185
|
if (v(data)) return { ok: true, data };
|
|
3133
|
-
const errors = (v.errors
|
|
3186
|
+
const errors = formatAjvErrors(v.errors);
|
|
3134
3187
|
return { ok: false, errors };
|
|
3135
3188
|
},
|
|
3136
3189
|
validatePluginManifest(data) {
|
|
3137
3190
|
if (pluginManifestValidator(data)) return { ok: true, data };
|
|
3138
|
-
const errors = (pluginManifestValidator.errors
|
|
3191
|
+
const errors = formatAjvErrors(pluginManifestValidator.errors);
|
|
3139
3192
|
return { ok: false, errors };
|
|
3140
3193
|
},
|
|
3141
3194
|
validateContributionPayload(slot, payload) {
|
|
@@ -3144,7 +3197,7 @@ function buildSchemaValidators() {
|
|
|
3144
3197
|
return { ok: false, errors: "unknown-slot" };
|
|
3145
3198
|
}
|
|
3146
3199
|
if (validator(payload)) return { ok: true };
|
|
3147
|
-
const errors = (validator.errors
|
|
3200
|
+
const errors = formatAjvErrors(validator.errors);
|
|
3148
3201
|
return { ok: false, errors };
|
|
3149
3202
|
}
|
|
3150
3203
|
};
|
|
@@ -3176,7 +3229,7 @@ function buildProviderFrontmatterValidator(providers) {
|
|
|
3176
3229
|
const v = compiled.get(key);
|
|
3177
3230
|
if (!v) return { ok: false, errors: "no-schema" };
|
|
3178
3231
|
if (v(data)) return { ok: true };
|
|
3179
|
-
const errors = (v.errors
|
|
3232
|
+
const errors = formatAjvErrors(v.errors);
|
|
3180
3233
|
return { ok: false, errors };
|
|
3181
3234
|
}
|
|
3182
3235
|
};
|
|
@@ -3185,6 +3238,47 @@ function formatError(err) {
|
|
|
3185
3238
|
const path = err.instancePath || "(root)";
|
|
3186
3239
|
return `${path} ${err.message ?? err.keyword}`;
|
|
3187
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
|
+
}
|
|
3188
3282
|
function registerProviderAuxiliarySchemas(ajv, providers) {
|
|
3189
3283
|
for (const provider of providers) {
|
|
3190
3284
|
if (!provider.schemas) continue;
|
|
@@ -3497,6 +3591,122 @@ function makeIssue(signal) {
|
|
|
3497
3591
|
return null;
|
|
3498
3592
|
}
|
|
3499
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
|
+
|
|
3500
3710
|
// plugins/core/analyzers/trigger-collision/text.ts
|
|
3501
3711
|
var TRIGGER_COLLISION_TEXTS = {
|
|
3502
3712
|
/**
|
|
@@ -3523,14 +3733,14 @@ var TRIGGER_COLLISION_TEXTS = {
|
|
|
3523
3733
|
};
|
|
3524
3734
|
|
|
3525
3735
|
// plugins/core/analyzers/trigger-collision/index.ts
|
|
3526
|
-
var
|
|
3736
|
+
var ID26 = "trigger-collision";
|
|
3527
3737
|
var ADVERTISING_KINDS = /* @__PURE__ */ new Set([
|
|
3528
3738
|
"command",
|
|
3529
3739
|
"skill",
|
|
3530
3740
|
"agent"
|
|
3531
3741
|
]);
|
|
3532
3742
|
var triggerCollisionAnalyzer = {
|
|
3533
|
-
id:
|
|
3743
|
+
id: ID26,
|
|
3534
3744
|
pluginId: CORE_PLUGIN_ID,
|
|
3535
3745
|
kind: "analyzer",
|
|
3536
3746
|
mode: "deterministic",
|
|
@@ -3628,7 +3838,7 @@ function analyzeTriggerBucket(normalized, claims) {
|
|
|
3628
3838
|
part: parts[0]
|
|
3629
3839
|
});
|
|
3630
3840
|
return {
|
|
3631
|
-
analyzerId:
|
|
3841
|
+
analyzerId: ID26,
|
|
3632
3842
|
severity: "error",
|
|
3633
3843
|
nodeIds,
|
|
3634
3844
|
message,
|
|
@@ -3668,13 +3878,13 @@ var ASCII_FORMATTER_TEXTS = {
|
|
|
3668
3878
|
};
|
|
3669
3879
|
|
|
3670
3880
|
// plugins/core/formatters/ascii/index.ts
|
|
3671
|
-
var
|
|
3881
|
+
var ID27 = "ascii";
|
|
3672
3882
|
var KIND_ORDER = ["agent", "command", "skill", "markdown"];
|
|
3673
3883
|
var asciiFormatter = {
|
|
3674
|
-
id:
|
|
3884
|
+
id: ID27,
|
|
3675
3885
|
pluginId: CORE_PLUGIN_ID,
|
|
3676
3886
|
kind: "formatter",
|
|
3677
|
-
formatId:
|
|
3887
|
+
formatId: ID27,
|
|
3678
3888
|
description: "Renders the scan as plain text in three sections: nodes (grouped by kind), arrows, and issues. Used by `sm scan --format ascii`.",
|
|
3679
3889
|
// ASCII tree formatter, header + per-kind sections + per-issue
|
|
3680
3890
|
// section. Each section iterates and renders; splitting per section
|
|
@@ -3768,13 +3978,13 @@ function renderSection(out, kind, group) {
|
|
|
3768
3978
|
}
|
|
3769
3979
|
|
|
3770
3980
|
// plugins/core/formatters/json/index.ts
|
|
3771
|
-
var
|
|
3981
|
+
var ID28 = "json";
|
|
3772
3982
|
var jsonFormatter = {
|
|
3773
|
-
id:
|
|
3983
|
+
id: ID28,
|
|
3774
3984
|
pluginId: CORE_PLUGIN_ID,
|
|
3775
3985
|
kind: "formatter",
|
|
3776
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`.",
|
|
3777
|
-
formatId:
|
|
3987
|
+
formatId: ID28,
|
|
3778
3988
|
format(ctx) {
|
|
3779
3989
|
if (ctx.scanResult !== void 0) {
|
|
3780
3990
|
return JSON.stringify(ctx.scanResult);
|
|
@@ -3913,9 +4123,9 @@ function resolveSpecRoot2() {
|
|
|
3913
4123
|
}
|
|
3914
4124
|
|
|
3915
4125
|
// plugins/core/actions/node-bump/index.ts
|
|
3916
|
-
var
|
|
4126
|
+
var ID29 = "node-bump";
|
|
3917
4127
|
var nodeBumpAction = {
|
|
3918
|
-
id:
|
|
4128
|
+
id: ID29,
|
|
3919
4129
|
pluginId: CORE_PLUGIN_ID,
|
|
3920
4130
|
kind: "action",
|
|
3921
4131
|
description: "Marks a node as updated: bumps `annotations.version`, refreshes sidecar hashes, and records the timestamp.",
|
|
@@ -3972,45 +4182,166 @@ function pickCurrentVersion(overlay) {
|
|
|
3972
4182
|
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
3973
4183
|
}
|
|
3974
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
|
+
|
|
3975
4267
|
// plugins/core/actions/node-supersede/index.ts
|
|
3976
|
-
var
|
|
4268
|
+
var ID32 = "node-supersede";
|
|
3977
4269
|
var nodeSupersedeAction = {
|
|
3978
|
-
id:
|
|
4270
|
+
id: ID32,
|
|
3979
4271
|
pluginId: CORE_PLUGIN_ID,
|
|
3980
4272
|
kind: "action",
|
|
3981
4273
|
description: "Declares the current node as superseded by another (writes `supersededBy` to the sidecar).",
|
|
3982
4274
|
mode: "deterministic",
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
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);
|
|
3986
4281
|
}
|
|
3987
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
|
+
}
|
|
3988
4308
|
|
|
3989
4309
|
// kernel/update-check/index.ts
|
|
3990
4310
|
var SEMVER_SHAPE_RE = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
3991
4311
|
async function fetchLatestVersion(pkg, opts) {
|
|
3992
4312
|
const controller = new AbortController();
|
|
3993
|
-
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
|
+
});
|
|
3994
4321
|
try {
|
|
3995
|
-
|
|
3996
|
-
const response = await fetch(url, {
|
|
3997
|
-
signal: controller.signal,
|
|
3998
|
-
headers: { accept: "application/json" }
|
|
3999
|
-
});
|
|
4000
|
-
if (!response.ok) {
|
|
4001
|
-
throw new Error(`registry returned status ${response.status}`);
|
|
4002
|
-
}
|
|
4003
|
-
const payload = await response.json();
|
|
4004
|
-
if (typeof payload.version !== "string" || payload.version.length === 0) {
|
|
4005
|
-
throw new Error("registry payload missing string `version`");
|
|
4006
|
-
}
|
|
4007
|
-
if (!SEMVER_SHAPE_RE.test(payload.version)) {
|
|
4008
|
-
throw new Error("registry payload `version` is not a semver-shaped string");
|
|
4009
|
-
}
|
|
4010
|
-
return payload.version;
|
|
4322
|
+
return await Promise.race([fetchVersion(pkg, controller.signal), hardCap]);
|
|
4011
4323
|
} finally {
|
|
4012
|
-
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}`);
|
|
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`");
|
|
4013
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;
|
|
4014
4345
|
}
|
|
4015
4346
|
function compareVersions(a, b) {
|
|
4016
4347
|
const pa = parseSemver(a);
|
|
@@ -4479,10 +4810,14 @@ var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", v
|
|
|
4479
4810
|
var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: VERSION };
|
|
4480
4811
|
var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: VERSION };
|
|
4481
4812
|
var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: VERSION };
|
|
4813
|
+
var supersedeAnalyzer2 = { ...supersedeAnalyzer, pluginId: "core", version: VERSION };
|
|
4814
|
+
var tagsAnalyzer2 = { ...tagsAnalyzer, pluginId: "core", version: VERSION };
|
|
4482
4815
|
var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: VERSION };
|
|
4483
4816
|
var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: VERSION };
|
|
4484
4817
|
var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: VERSION };
|
|
4485
4818
|
var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: VERSION };
|
|
4819
|
+
var nodeSetStabilityAction2 = { ...nodeSetStabilityAction, pluginId: "core", version: VERSION };
|
|
4820
|
+
var nodeSetTagsAction2 = { ...nodeSetTagsAction, pluginId: "core", version: VERSION };
|
|
4486
4821
|
var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: VERSION };
|
|
4487
4822
|
var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: VERSION };
|
|
4488
4823
|
var builtInPlugins = [
|
|
@@ -4542,10 +4877,14 @@ var builtInPlugins = [
|
|
|
4542
4877
|
referenceRedundantAnalyzer2,
|
|
4543
4878
|
schemaViolationAnalyzer2,
|
|
4544
4879
|
signalCollisionAnalyzer2,
|
|
4880
|
+
supersedeAnalyzer2,
|
|
4881
|
+
tagsAnalyzer2,
|
|
4545
4882
|
triggerCollisionAnalyzer2,
|
|
4546
4883
|
asciiFormatter2,
|
|
4547
4884
|
jsonFormatter2,
|
|
4548
4885
|
nodeBumpAction2,
|
|
4886
|
+
nodeSetStabilityAction2,
|
|
4887
|
+
nodeSetTagsAction2,
|
|
4549
4888
|
nodeSupersedeAction2,
|
|
4550
4889
|
updateCheckHook2
|
|
4551
4890
|
]
|
|
@@ -5802,13 +6141,16 @@ function ensureSidecarWritesAllowed(opts) {
|
|
|
5802
6141
|
default: false
|
|
5803
6142
|
});
|
|
5804
6143
|
if (allowed === true) return;
|
|
5805
|
-
if (opts.
|
|
6144
|
+
if (opts.always === true) {
|
|
5806
6145
|
writeConfigValue("allowEditSmFiles", true, {
|
|
5807
6146
|
target: "project-local",
|
|
5808
6147
|
cwd: opts.cwd
|
|
5809
6148
|
});
|
|
5810
6149
|
return;
|
|
5811
6150
|
}
|
|
6151
|
+
if (opts.confirm === true) {
|
|
6152
|
+
return;
|
|
6153
|
+
}
|
|
5812
6154
|
throw new EConsentRequiredError({
|
|
5813
6155
|
key: "allowEditSmFiles",
|
|
5814
6156
|
hintTarget: "project-local"
|
|
@@ -7433,7 +7775,7 @@ async function loadPluginOverrideMap(db) {
|
|
|
7433
7775
|
}
|
|
7434
7776
|
|
|
7435
7777
|
// kernel/util/enum-parsers.ts
|
|
7436
|
-
var
|
|
7778
|
+
var STABILITY_VALUES2 = Object.freeze([
|
|
7437
7779
|
"experimental",
|
|
7438
7780
|
"stable",
|
|
7439
7781
|
"deprecated"
|
|
@@ -7756,6 +8098,41 @@ async function replaceAllScanContributions(trx, contributions, livePaths = /* @_
|
|
|
7756
8098
|
await sweepPerTupleContributions(trx, contributions, freshlyRunTuples);
|
|
7757
8099
|
await upsertContributionsBuffer(trx, contributions);
|
|
7758
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
|
+
}
|
|
7759
8136
|
async function sweepOrphanContributions(trx, livePaths) {
|
|
7760
8137
|
if (livePaths.size > 0) {
|
|
7761
8138
|
await trx.deleteFrom("scan_contributions").where("nodePath", "not in", [...livePaths]).execute();
|
|
@@ -7965,7 +8342,7 @@ async function findNodesByTag(db, tag) {
|
|
|
7965
8342
|
}
|
|
7966
8343
|
|
|
7967
8344
|
// kernel/adapters/sqlite/scan-persistence.ts
|
|
7968
|
-
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 = []) {
|
|
7969
8346
|
const scannedAt = validateScannedAt(result.scannedAt);
|
|
7970
8347
|
const renames = [];
|
|
7971
8348
|
await db.transaction().execute(async (trx) => {
|
|
@@ -7980,6 +8357,7 @@ async function persistScanResult(db, result, renameOps = [], extractorRuns = [],
|
|
|
7980
8357
|
registeredContributionKeys,
|
|
7981
8358
|
freshlyRunTuples
|
|
7982
8359
|
);
|
|
8360
|
+
await replaceAllScanContributionErrors(trx, contributionErrors);
|
|
7983
8361
|
const tagRecords = nodesToTagRecords(result.nodes);
|
|
7984
8362
|
await replaceAllScanTags(trx, tagRecords, livePathsForContrib);
|
|
7985
8363
|
await upsertEnrichmentLayer(trx, result, renameOps, enrichments);
|
|
@@ -8424,7 +8802,8 @@ var SqliteStorageAdapter = class {
|
|
|
8424
8802
|
listForNode: (nodePath) => loadContributionsForNode(this.db, nodePath),
|
|
8425
8803
|
listForPaths: (paths) => loadContributionsForPaths(this.db, paths),
|
|
8426
8804
|
lookup: (pluginId, contributionId, nodePath, extensionId) => loadContributionLookup(this.db, pluginId, contributionId, nodePath, extensionId),
|
|
8427
|
-
purgeByPlugin: (pluginId, extensionId) => purgeContributionsByPlugin(this.db, pluginId, extensionId)
|
|
8805
|
+
purgeByPlugin: (pluginId, extensionId) => purgeContributionsByPlugin(this.db, pluginId, extensionId),
|
|
8806
|
+
listAllErrors: () => listAllContributionErrors(this.db)
|
|
8428
8807
|
};
|
|
8429
8808
|
this.tags = {
|
|
8430
8809
|
listForNode: (nodePath) => loadTagsForNode(this.db, nodePath),
|
|
@@ -8497,7 +8876,8 @@ async function persistScansThroughNonTx(db, result, opts) {
|
|
|
8497
8876
|
defaults.enrichments,
|
|
8498
8877
|
defaults.contributions,
|
|
8499
8878
|
defaults.registeredContributionKeys,
|
|
8500
|
-
defaults.freshlyRunTuples
|
|
8879
|
+
defaults.freshlyRunTuples,
|
|
8880
|
+
defaults.contributionErrors
|
|
8501
8881
|
);
|
|
8502
8882
|
}
|
|
8503
8883
|
function applyPersistDefaults(opts) {
|
|
@@ -8508,6 +8888,7 @@ function applyPersistDefaults(opts) {
|
|
|
8508
8888
|
contributions: [],
|
|
8509
8889
|
registeredContributionKeys: /* @__PURE__ */ new Set(),
|
|
8510
8890
|
freshlyRunTuples: /* @__PURE__ */ new Set(),
|
|
8891
|
+
contributionErrors: [],
|
|
8511
8892
|
...opts
|
|
8512
8893
|
};
|
|
8513
8894
|
}
|
|
@@ -8672,7 +9053,8 @@ function buildTxSubset(trx) {
|
|
|
8672
9053
|
d.enrichments,
|
|
8673
9054
|
d.contributions,
|
|
8674
9055
|
d.registeredContributionKeys,
|
|
8675
|
-
d.freshlyRunTuples
|
|
9056
|
+
d.freshlyRunTuples,
|
|
9057
|
+
d.contributionErrors
|
|
8676
9058
|
).then(() => void 0);
|
|
8677
9059
|
}
|
|
8678
9060
|
},
|
|
@@ -9167,10 +9549,11 @@ var BumpCommand = class extends SmCommand {
|
|
|
9167
9549
|
* `EConsentRequiredError` thrown by `FilesystemSidecarStore.applyPatch`
|
|
9168
9550
|
* (via `ensureSidecarWritesAllowed`), prompt the operator if stdin is
|
|
9169
9551
|
* a TTY and `--yes` was not passed. On accept, flip `this.yes` to
|
|
9170
|
-
* true and re-run `dispatch` (the second pass passes `
|
|
9171
|
-
* to the store and the gate persists the flag to project-local
|
|
9172
|
-
*
|
|
9173
|
-
*
|
|
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`.
|
|
9174
9557
|
*/
|
|
9175
9558
|
async #runWithConsent(ansi, dispatch) {
|
|
9176
9559
|
try {
|
|
@@ -9269,7 +9652,11 @@ var BumpCommand = class extends SmCommand {
|
|
|
9269
9652
|
async #applyBumpedSingle(item, node, ansi) {
|
|
9270
9653
|
const ctx = defaultRuntimeContext();
|
|
9271
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`.
|
|
9272
9658
|
confirm: this.yes,
|
|
9659
|
+
always: this.yes,
|
|
9273
9660
|
cwd: ctx.cwd
|
|
9274
9661
|
};
|
|
9275
9662
|
const applied = await applyBumpWrites(item, consent);
|
|
@@ -9359,7 +9746,9 @@ var BumpCommand = class extends SmCommand {
|
|
|
9359
9746
|
const store = new FilesystemSidecarStore(ensureSidecarWritesAllowed);
|
|
9360
9747
|
const ctx = defaultRuntimeContext();
|
|
9361
9748
|
const consent = {
|
|
9749
|
+
// Step 17 split: CLI accept / `--yes` persists (see #runSingle).
|
|
9362
9750
|
confirm: this.yes,
|
|
9751
|
+
always: this.yes,
|
|
9363
9752
|
cwd: ctx.cwd
|
|
9364
9753
|
};
|
|
9365
9754
|
const outcomes = [];
|
|
@@ -9588,16 +9977,23 @@ function readConformanceKillSwitches(env = process.env) {
|
|
|
9588
9977
|
}
|
|
9589
9978
|
|
|
9590
9979
|
// kernel/i18n/plugin-loader.texts.ts
|
|
9980
|
+
var SPEC_GITHUB_BASE = "https://github.com/crystian/skill-map/blob/main";
|
|
9591
9981
|
var PLUGIN_LOADER_TEXTS = {
|
|
9592
9982
|
invalidManifestJsonParse: "{{manifestPath}}: {{errDescription}}. Validate the JSON (e.g. `npx jsonlint plugin.json`).",
|
|
9593
|
-
invalidManifestAjv:
|
|
9983
|
+
invalidManifestAjv: `{{manifestPath}}: {{errors}}. See ${SPEC_GITHUB_BASE}/spec/schemas/plugins-registry.schema.json#/$defs/PluginManifest.`,
|
|
9594
9984
|
invalidSpecCompat: 'specCompat "{{specCompat}}" is not a valid semver range. Use a range like "^1.0.0".',
|
|
9595
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.`,
|
|
9596
9986
|
loadErrorFileNotFound: "extension file not found: {{relEntry}} (resolved to {{abs}}). Check plugin.json#/extensions paths.",
|
|
9597
9987
|
loadErrorImportFailed: "{{relEntry}}: import failed: {{errDescription}}",
|
|
9598
9988
|
loadErrorMissingKind: "{{relEntry}}: default export missing a string `kind` field. Expected one of: {{knownKindsList}}.",
|
|
9599
9989
|
loadErrorUnknownKind: '{{relEntry}}: unknown extension kind "{{kindReceived}}". Expected one of: {{knownKindsList}}.',
|
|
9600
|
-
|
|
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}}.",
|
|
9601
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.).",
|
|
9602
9998
|
disabledByConfig: "disabled by config_plugins or settings.json",
|
|
9603
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.",
|
|
@@ -9929,7 +10325,7 @@ function loadOneProviderKind(opts) {
|
|
|
9929
10325
|
opts.pluginPath,
|
|
9930
10326
|
opts.pluginId,
|
|
9931
10327
|
"invalid-manifest",
|
|
9932
|
-
`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.`
|
|
9933
10329
|
),
|
|
9934
10330
|
manifest: opts.manifest
|
|
9935
10331
|
}
|
|
@@ -10351,13 +10747,17 @@ var PluginLoader = class {
|
|
|
10351
10747
|
}
|
|
10352
10748
|
const extValidator = this.#options.validators.validatorForExtension(kind);
|
|
10353
10749
|
if (!extValidator(manifestView)) {
|
|
10354
|
-
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`;
|
|
10355
10755
|
return { ok: false, failure: {
|
|
10356
10756
|
...fail(
|
|
10357
10757
|
pluginPath,
|
|
10358
10758
|
pluginId,
|
|
10359
10759
|
"invalid-manifest",
|
|
10360
|
-
tx(PLUGIN_LOADER_TEXTS.invalidManifestExtensionShape, { relEntry,
|
|
10760
|
+
tx(PLUGIN_LOADER_TEXTS.invalidManifestExtensionShape, { relEntry, errors, docUrl })
|
|
10361
10761
|
),
|
|
10362
10762
|
manifest
|
|
10363
10763
|
} };
|
|
@@ -10835,6 +11235,9 @@ function collectViewContributions(pluginId, extensionId, instance, out, options
|
|
|
10835
11235
|
const raw = instance["ui"];
|
|
10836
11236
|
if (typeof raw !== "object" || raw === null) return;
|
|
10837
11237
|
const exclude = options.excludeQualifiedIds;
|
|
11238
|
+
const pluginOrder = options.pluginOrder;
|
|
11239
|
+
const extOrder = instance["order"];
|
|
11240
|
+
const extensionOrder = typeof extOrder === "number" ? extOrder : void 0;
|
|
10838
11241
|
for (const [contributionId, value] of Object.entries(raw)) {
|
|
10839
11242
|
if (typeof value !== "object" || value === null) continue;
|
|
10840
11243
|
const entry = value;
|
|
@@ -10853,13 +11256,15 @@ function collectViewContributions(pluginId, extensionId, instance, out, options
|
|
|
10853
11256
|
...typeof entry.icon === "string" ? { icon: entry.icon } : {},
|
|
10854
11257
|
...typeof entry.emptyText === "string" ? { emptyText: entry.emptyText } : {},
|
|
10855
11258
|
...typeof entry.priority === "number" ? { priority: entry.priority } : {},
|
|
11259
|
+
...typeof pluginOrder === "number" ? { pluginOrder } : {},
|
|
11260
|
+
...typeof extensionOrder === "number" ? { extensionOrder } : {},
|
|
10856
11261
|
emitWhenEmpty: entry.emitWhenEmpty === true
|
|
10857
11262
|
});
|
|
10858
11263
|
}
|
|
10859
11264
|
}
|
|
10860
11265
|
|
|
10861
11266
|
// core/runtime/plugin-runtime/bucketing.ts
|
|
10862
|
-
function bucketLoaded(loaded, runtime) {
|
|
11267
|
+
function bucketLoaded(loaded, runtime, pluginOrder) {
|
|
10863
11268
|
for (const ext of loaded) {
|
|
10864
11269
|
const instance = ext.instance;
|
|
10865
11270
|
if (!isExtensionInstance(instance)) continue;
|
|
@@ -10880,7 +11285,9 @@ function bucketLoaded(loaded, runtime) {
|
|
|
10880
11285
|
...ext.entryPath ? { entry: ext.entryPath } : {}
|
|
10881
11286
|
});
|
|
10882
11287
|
collectAnnotationContributions(ext.pluginId, instance, runtime.annotationContributions);
|
|
10883
|
-
collectViewContributions(ext.pluginId, ext.id, instance, runtime.viewContributions
|
|
11288
|
+
collectViewContributions(ext.pluginId, ext.id, instance, runtime.viewContributions, {
|
|
11289
|
+
...typeof pluginOrder === "number" ? { pluginOrder } : {}
|
|
11290
|
+
});
|
|
10884
11291
|
}
|
|
10885
11292
|
}
|
|
10886
11293
|
function collectAnnotationContributions(pluginId, instance, out) {
|
|
@@ -10926,11 +11333,14 @@ function truncateTail(s, max) {
|
|
|
10926
11333
|
// core/runtime/i18n/plugin-runtime.texts.ts
|
|
10927
11334
|
var PLUGIN_RUNTIME_TEXTS = {
|
|
10928
11335
|
/**
|
|
10929
|
-
* Stderr-ready warning for one non-loaded plugin.
|
|
10930
|
-
*
|
|
10931
|
-
* `
|
|
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").
|
|
10932
11342
|
*/
|
|
10933
|
-
warningRow: "plugin {{id}}
|
|
11343
|
+
warningRow: "plugin {{id}} ({{status}}), all extensions skipped: {{reason}}",
|
|
10934
11344
|
/** Placeholder when a non-loaded plugin record carries no `reason`. */
|
|
10935
11345
|
warningReasonMissing: "(no reason recorded)"
|
|
10936
11346
|
};
|
|
@@ -10992,7 +11402,7 @@ async function loadPluginRuntime(opts = {}) {
|
|
|
10992
11402
|
};
|
|
10993
11403
|
for (const plugin of discovered) {
|
|
10994
11404
|
if (plugin.status === "enabled") {
|
|
10995
|
-
bucketLoaded(plugin.extensions ?? [], runtime);
|
|
11405
|
+
bucketLoaded(plugin.extensions ?? [], runtime, plugin.manifest?.order);
|
|
10996
11406
|
continue;
|
|
10997
11407
|
}
|
|
10998
11408
|
if (plugin.status === "disabled") continue;
|
|
@@ -13330,8 +13740,8 @@ var DbResetCommand = class extends SmCommand {
|
|
|
13330
13740
|
return ExitCode.Ok;
|
|
13331
13741
|
}
|
|
13332
13742
|
const withCounts = rows.map((r) => {
|
|
13333
|
-
const
|
|
13334
|
-
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) };
|
|
13335
13745
|
});
|
|
13336
13746
|
const totalRows = withCounts.reduce((acc, r) => acc + r.rowCount, 0);
|
|
13337
13747
|
const lines = withCounts.map((r) => ` - ${r.name}: ${r.rowCount} row(s)`).join("\n");
|
|
@@ -15148,7 +15558,7 @@ var ORCHESTRATOR_TEXTS = {
|
|
|
15148
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.",
|
|
15149
15559
|
extensionErrorLinkKindNotDeclared: 'Extractor "{{extractorId}}" emitted a link of kind "{{linkKind}}" outside its declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',
|
|
15150
15560
|
extensionErrorIssueInvalidSeverity: `Rule "{{analyzerId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
|
|
15151
|
-
|
|
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.',
|
|
15152
15562
|
extensionErrorContributionPayloadInvalid: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}}; payload failed the "{{slot}}" schema: {{errors}}. Contribution dropped.',
|
|
15153
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.',
|
|
15154
15564
|
runScanRootEmptyArray: "runScan: roots must contain at least one path (spec requires minItems: 1)",
|
|
@@ -15168,6 +15578,7 @@ async function runExtractorsForNode(opts) {
|
|
|
15168
15578
|
const externalLinks = [];
|
|
15169
15579
|
const enrichmentBuffer = /* @__PURE__ */ new Map();
|
|
15170
15580
|
const contributions = [];
|
|
15581
|
+
const contributionErrors = [];
|
|
15171
15582
|
const signals = [];
|
|
15172
15583
|
const virtualNodes = [];
|
|
15173
15584
|
const virtualNodePaths = /* @__PURE__ */ new Set();
|
|
@@ -15199,36 +15610,54 @@ async function runExtractorsForNode(opts) {
|
|
|
15199
15610
|
});
|
|
15200
15611
|
}
|
|
15201
15612
|
};
|
|
15202
|
-
const declaredContributions =
|
|
15203
|
-
const emitContribution = (
|
|
15204
|
-
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;
|
|
15205
15616
|
if (!declared) {
|
|
15617
|
+
const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUndeclaredRef, {
|
|
15618
|
+
extractorId: qualifiedId2,
|
|
15619
|
+
nodePath: opts.node.path
|
|
15620
|
+
});
|
|
15206
15621
|
emitExtensionError(opts.emitter, qualifiedId2, opts.node.path, {
|
|
15207
15622
|
phase: "emitContribution",
|
|
15208
|
-
|
|
15209
|
-
|
|
15210
|
-
|
|
15211
|
-
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
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()
|
|
15215
15633
|
});
|
|
15216
15634
|
return;
|
|
15217
15635
|
}
|
|
15218
15636
|
const result = validators.validateContributionPayload(declared.slot, payload);
|
|
15219
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
|
+
});
|
|
15220
15645
|
emitExtensionError(opts.emitter, qualifiedId2, opts.node.path, {
|
|
15221
15646
|
phase: "emitContribution",
|
|
15222
|
-
contributionId,
|
|
15647
|
+
contributionId: declared.id,
|
|
15223
15648
|
slot: declared.slot,
|
|
15224
15649
|
reason: result.errors,
|
|
15225
|
-
message
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
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()
|
|
15232
15661
|
});
|
|
15233
15662
|
return;
|
|
15234
15663
|
}
|
|
@@ -15236,7 +15665,7 @@ async function runExtractorsForNode(opts) {
|
|
|
15236
15665
|
pluginId: extractor.pluginId,
|
|
15237
15666
|
extensionId: extractor.id,
|
|
15238
15667
|
nodePath: opts.node.path,
|
|
15239
|
-
contributionId,
|
|
15668
|
+
contributionId: declared.id,
|
|
15240
15669
|
slot: declared.slot,
|
|
15241
15670
|
payload,
|
|
15242
15671
|
emittedAt: Date.now()
|
|
@@ -15274,11 +15703,12 @@ async function runExtractorsForNode(opts) {
|
|
|
15274
15703
|
externalLinks,
|
|
15275
15704
|
enrichments: Array.from(enrichmentBuffer.values()),
|
|
15276
15705
|
contributions,
|
|
15706
|
+
contributionErrors,
|
|
15277
15707
|
signals,
|
|
15278
15708
|
virtualNodes
|
|
15279
15709
|
};
|
|
15280
15710
|
}
|
|
15281
|
-
function
|
|
15711
|
+
function readDeclaredContributionRefs(extension) {
|
|
15282
15712
|
const out = /* @__PURE__ */ new Map();
|
|
15283
15713
|
const raw = extension.ui;
|
|
15284
15714
|
if (typeof raw !== "object" || raw === null) return out;
|
|
@@ -15286,7 +15716,7 @@ function readDeclaredContributions(extension) {
|
|
|
15286
15716
|
if (typeof value !== "object" || value === null) continue;
|
|
15287
15717
|
const slot = value.slot;
|
|
15288
15718
|
if (typeof slot !== "string") continue;
|
|
15289
|
-
out.set(
|
|
15719
|
+
out.set(value, { id, slot });
|
|
15290
15720
|
}
|
|
15291
15721
|
return out;
|
|
15292
15722
|
}
|
|
@@ -15534,6 +15964,7 @@ function isExternalUrlLink(link) {
|
|
|
15534
15964
|
async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, registeredActionIds, emitter, hookDispatcher, reservedNodePaths, signals, seedIssues = []) {
|
|
15535
15965
|
const issues = [...seedIssues];
|
|
15536
15966
|
const contributions = [];
|
|
15967
|
+
const contributionErrors = [];
|
|
15537
15968
|
const validators = loadSchemaValidators();
|
|
15538
15969
|
void registeredActionIds;
|
|
15539
15970
|
const analyzerOrphans = orphanSidecars.map((o) => ({
|
|
@@ -15543,36 +15974,54 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
15543
15974
|
const scheduled = orderAnalyzersByPhase(analyzers);
|
|
15544
15975
|
for (const analyzer of scheduled) {
|
|
15545
15976
|
const qualifiedId2 = qualifiedExtensionId(analyzer.pluginId, analyzer.id);
|
|
15546
|
-
const declaredContributions =
|
|
15547
|
-
const emitContribution = (nodePath,
|
|
15548
|
-
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;
|
|
15549
15980
|
if (!declared) {
|
|
15981
|
+
const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUndeclaredRef, {
|
|
15982
|
+
extractorId: qualifiedId2,
|
|
15983
|
+
nodePath
|
|
15984
|
+
});
|
|
15550
15985
|
emitExtensionError(emitter, qualifiedId2, nodePath, {
|
|
15551
15986
|
phase: "emitContribution",
|
|
15552
|
-
|
|
15553
|
-
|
|
15554
|
-
|
|
15555
|
-
|
|
15556
|
-
|
|
15557
|
-
|
|
15558
|
-
|
|
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()
|
|
15559
15997
|
});
|
|
15560
15998
|
return;
|
|
15561
15999
|
}
|
|
15562
16000
|
const result = validators.validateContributionPayload(declared.slot, payload);
|
|
15563
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
|
+
});
|
|
15564
16009
|
emitExtensionError(emitter, qualifiedId2, nodePath, {
|
|
15565
16010
|
phase: "emitContribution",
|
|
15566
|
-
contributionId,
|
|
16011
|
+
contributionId: declared.id,
|
|
15567
16012
|
slot: declared.slot,
|
|
15568
16013
|
reason: result.errors,
|
|
15569
|
-
message
|
|
15570
|
-
|
|
15571
|
-
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
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()
|
|
15576
16025
|
});
|
|
15577
16026
|
return;
|
|
15578
16027
|
}
|
|
@@ -15580,7 +16029,7 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
15580
16029
|
pluginId: analyzer.pluginId,
|
|
15581
16030
|
extensionId: analyzer.id,
|
|
15582
16031
|
nodePath,
|
|
15583
|
-
contributionId,
|
|
16032
|
+
contributionId: declared.id,
|
|
15584
16033
|
slot: declared.slot,
|
|
15585
16034
|
payload,
|
|
15586
16035
|
emittedAt: Date.now()
|
|
@@ -15613,7 +16062,7 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
15613
16062
|
emitter.emit(evt);
|
|
15614
16063
|
await hookDispatcher.dispatch("analyzer.completed", evt);
|
|
15615
16064
|
}
|
|
15616
|
-
return { issues, contributions };
|
|
16065
|
+
return { issues, contributions, contributionErrors };
|
|
15617
16066
|
}
|
|
15618
16067
|
function orderAnalyzersByPhase(analyzers) {
|
|
15619
16068
|
return analyzers.slice().sort((a, b) => phaseRank(a) - phaseRank(b));
|
|
@@ -16501,6 +16950,7 @@ async function walkAndExtract(opts) {
|
|
|
16501
16950
|
enrichments: [...accum.enrichmentBuffer.values()],
|
|
16502
16951
|
extractorRuns: accum.extractorRuns,
|
|
16503
16952
|
contributions: accum.contributionsBuffer,
|
|
16953
|
+
contributionErrors: accum.contributionErrorsBuffer,
|
|
16504
16954
|
freshlyRunTuples: accum.freshlyRunTuples,
|
|
16505
16955
|
orphanSidecars,
|
|
16506
16956
|
sidecarRoots: accum.sidecarRoots,
|
|
@@ -16517,6 +16967,7 @@ function createWalkAccumulators() {
|
|
|
16517
16967
|
frontmatterIssues: [],
|
|
16518
16968
|
enrichmentBuffer: /* @__PURE__ */ new Map(),
|
|
16519
16969
|
contributionsBuffer: [],
|
|
16970
|
+
contributionErrorsBuffer: [],
|
|
16520
16971
|
freshlyRunTuples: /* @__PURE__ */ new Set(),
|
|
16521
16972
|
extractorRuns: [],
|
|
16522
16973
|
sidecarRoots: /* @__PURE__ */ new Map()
|
|
@@ -16667,7 +17118,11 @@ function mergeExtractResult(extractResult, accum) {
|
|
|
16667
17118
|
accum.enrichmentBuffer.set(`${enr.nodePath}\0${enr.extractorId}`, enr);
|
|
16668
17119
|
}
|
|
16669
17120
|
for (const c of extractResult.contributions) accum.contributionsBuffer.push(c);
|
|
16670
|
-
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) {
|
|
16671
17126
|
if (accum.nodes.some((n) => n.path === vn.path)) continue;
|
|
16672
17127
|
accum.nodes.push(vn);
|
|
16673
17128
|
}
|
|
@@ -16943,6 +17398,7 @@ async function dispatchExtractorCompleted(extractors, emitter, hookDispatcher) {
|
|
|
16943
17398
|
}
|
|
16944
17399
|
function mergeAnalyzerEmissions(walked, analyzerResult, analyzers) {
|
|
16945
17400
|
for (const c of analyzerResult.contributions) walked.contributions.push(c);
|
|
17401
|
+
for (const e of analyzerResult.contributionErrors) walked.contributionErrors.push(e);
|
|
16946
17402
|
for (const analyzer of analyzers ?? []) {
|
|
16947
17403
|
if (analyzer.ui === void 0) continue;
|
|
16948
17404
|
for (const node of walked.nodes) {
|
|
@@ -16988,6 +17444,7 @@ function buildScanReturn(walked, issues, renameOps, stats, options, setup) {
|
|
|
16988
17444
|
extractorRuns: walked.extractorRuns,
|
|
16989
17445
|
enrichments: walked.enrichments,
|
|
16990
17446
|
contributions: walked.contributions,
|
|
17447
|
+
contributionErrors: walked.contributionErrors,
|
|
16991
17448
|
freshlyRunTuples: walked.freshlyRunTuples
|
|
16992
17449
|
};
|
|
16993
17450
|
}
|
|
@@ -17656,12 +18113,12 @@ var DB_DRIFT_TEXTS = {
|
|
|
17656
18113
|
// `{{reason}}` is one of the `driftReason*` strings below so the
|
|
17657
18114
|
// operator sees WHY the cache is being rebuilt (version skew vs an
|
|
17658
18115
|
// inline schema change the version did not bump).
|
|
17659
|
-
driftPrompt: "{{glyph}}
|
|
17660
|
-
driftPromptHint: "
|
|
17661
|
-
driftPromptQuestion: "
|
|
18116
|
+
driftPrompt: "{{glyph}} Local cache is from skill-map {{dbVersion}}, you are on {{currentVersion}} ({{reason}}).\n {{hint}}\n",
|
|
18117
|
+
driftPromptHint: "It will be rebuilt from your .sm files on this scan; nothing of yours is touched.",
|
|
18118
|
+
driftPromptQuestion: "Rebuild the local cache now? [y/N] ",
|
|
17662
18119
|
// Receipt after the rebuild (printed by the scan / refresh path).
|
|
17663
|
-
driftReset: "{{glyph}} Local cache rebuilt
|
|
17664
|
-
driftResetHint: "
|
|
18120
|
+
driftReset: "{{glyph}} Local cache rebuilt: was from skill-map {{dbVersion}}, you are on {{currentVersion}}.\n {{hint}}\n",
|
|
18121
|
+
driftResetHint: "Rebuilt from your .sm files; nothing of yours was touched.",
|
|
17665
18122
|
// Abort headline when the operator declines (wrapped by the caller's
|
|
17666
18123
|
// `sm scan: {message}` shell, so it carries no glyph / verb prefix).
|
|
17667
18124
|
driftAborted: "cache rebuild declined: the {{dbVersion}} cache cannot be reused on {{currentVersion}} ({{reason}}). {{hint}}",
|
|
@@ -17679,12 +18136,13 @@ async function maybeResetOnDrift(dbPath, policy) {
|
|
|
17679
18136
|
const reason = detectDriftReason(dbPath, policy.currentVersion);
|
|
17680
18137
|
if (reason === null) return { kind: "no-drift" };
|
|
17681
18138
|
const dbVersion = readScannedByVersion(dbPath) ?? "unknown";
|
|
17682
|
-
const
|
|
18139
|
+
const prompted = shouldPromptForReset(policy);
|
|
18140
|
+
const confirmed = prompted ? await askDriftReset(dbVersion, reason, policy) : true;
|
|
17683
18141
|
if (!confirmed) {
|
|
17684
18142
|
return { kind: "aborted", dbVersion, currentVersion: policy.currentVersion, reason };
|
|
17685
18143
|
}
|
|
17686
18144
|
await removeDbFiles(dbPath);
|
|
17687
|
-
renderResetReceipt(dbVersion,
|
|
18145
|
+
if (!prompted) renderResetReceipt(dbVersion, policy);
|
|
17688
18146
|
return { kind: "reset", dbVersion, currentVersion: policy.currentVersion, reason };
|
|
17689
18147
|
}
|
|
17690
18148
|
function detectDriftReason(dbPath, currentVersion) {
|
|
@@ -17709,10 +18167,6 @@ function readScannedByVersion(dbPath) {
|
|
|
17709
18167
|
raw?.close();
|
|
17710
18168
|
}
|
|
17711
18169
|
}
|
|
17712
|
-
async function confirmDriftReset(dbVersion, reason, policy) {
|
|
17713
|
-
if (!shouldPromptForReset(policy)) return true;
|
|
17714
|
-
return askDriftReset(dbVersion, reason, policy);
|
|
17715
|
-
}
|
|
17716
18170
|
function reasonText(reason) {
|
|
17717
18171
|
return reason === "version" ? DB_DRIFT_TEXTS.driftReasonVersion : DB_DRIFT_TEXTS.driftReasonSchema;
|
|
17718
18172
|
}
|
|
@@ -17743,7 +18197,7 @@ async function askDriftReset(dbVersion, reason, policy) {
|
|
|
17743
18197
|
rl.close();
|
|
17744
18198
|
}
|
|
17745
18199
|
}
|
|
17746
|
-
function renderResetReceipt(dbVersion,
|
|
18200
|
+
function renderResetReceipt(dbVersion, policy) {
|
|
17747
18201
|
if (!policy.printer) return;
|
|
17748
18202
|
const warnGlyph = policy.style?.warnGlyph ?? "\u26A0";
|
|
17749
18203
|
const dim = policy.style?.dim ?? ((s) => s);
|
|
@@ -17752,7 +18206,6 @@ function renderResetReceipt(dbVersion, reason, policy) {
|
|
|
17752
18206
|
glyph: warnGlyph,
|
|
17753
18207
|
dbVersion,
|
|
17754
18208
|
currentVersion: policy.currentVersion,
|
|
17755
|
-
reason: reasonText(reason),
|
|
17756
18209
|
hint: dim(DB_DRIFT_TEXTS.driftResetHint)
|
|
17757
18210
|
})
|
|
17758
18211
|
);
|
|
@@ -18017,6 +18470,7 @@ async function runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanW
|
|
|
18017
18470
|
extractorRuns: scanned.extractorRuns,
|
|
18018
18471
|
enrichments: scanned.enrichments,
|
|
18019
18472
|
contributions: scanned.contributions,
|
|
18473
|
+
contributionErrors: scanned.contributionErrors,
|
|
18020
18474
|
registeredContributionKeys: collectRegisteredContributionKeys(extensions),
|
|
18021
18475
|
freshlyRunTuples: scanned.freshlyRunTuples
|
|
18022
18476
|
});
|
|
@@ -18873,11 +19327,11 @@ function renderStatsFailures(failures, ansi) {
|
|
|
18873
19327
|
tx(HISTORY_TEXTS.statsSectionHeader, { title: HISTORY_TEXTS.statsSectionTitleFailures })
|
|
18874
19328
|
];
|
|
18875
19329
|
const reasonWidth = Math.max(...failures.map(([reason]) => reason.length));
|
|
18876
|
-
for (const [reason,
|
|
19330
|
+
for (const [reason, count3] of failures) {
|
|
18877
19331
|
lines.push(
|
|
18878
19332
|
tx(HISTORY_TEXTS.statsFailuresRow, {
|
|
18879
19333
|
reason: sanitizeForTerminal(reason).padEnd(reasonWidth),
|
|
18880
|
-
count: ansi.red(String(
|
|
19334
|
+
count: ansi.red(String(count3))
|
|
18881
19335
|
})
|
|
18882
19336
|
);
|
|
18883
19337
|
}
|
|
@@ -19579,14 +20033,14 @@ var OrphansReconcileCommand = class extends SmCommand {
|
|
|
19579
20033
|
tx(ORPHANS_TEXTS.reconcileSuccessBody, { breakdown: ansi.dim(breakdown) })
|
|
19580
20034
|
);
|
|
19581
20035
|
if (summary.collisions.length > 0) {
|
|
19582
|
-
const
|
|
20036
|
+
const count3 = summary.collisions.length;
|
|
19583
20037
|
this.printer.info(
|
|
19584
20038
|
tx(
|
|
19585
20039
|
dryRun ? ORPHANS_TEXTS.reconcileCollisionsNoteDryRun : ORPHANS_TEXTS.reconcileCollisionsNote,
|
|
19586
20040
|
{
|
|
19587
20041
|
glyph: ansi.yellow("\u26A0"),
|
|
19588
|
-
count,
|
|
19589
|
-
plural:
|
|
20042
|
+
count: count3,
|
|
20043
|
+
plural: count3 === 1 ? "" : "s"
|
|
19590
20044
|
}
|
|
19591
20045
|
)
|
|
19592
20046
|
);
|
|
@@ -19903,6 +20357,23 @@ var PLUGINS_TEXTS = {
|
|
|
19903
20357
|
doctorIssuesHeader: "\n Issues ({{count}})\n",
|
|
19904
20358
|
doctorIssueEntry: " {{glyph}} {{id}} {{status}}\n",
|
|
19905
20359
|
doctorIssueBody: " {{line}}\n",
|
|
20360
|
+
// --- runtime contribution errors (last scan) -------------------------
|
|
20361
|
+
/**
|
|
20362
|
+
* "off-shape visible" follow-up. Section heading for view
|
|
20363
|
+
* contributions the last scan REJECTED at emit time (undeclared ref,
|
|
20364
|
+
* or payload failed the slot's AJV schema). Rendered only when at
|
|
20365
|
+
* least one error was persisted; promotes the exit code to 1.
|
|
20366
|
+
* `count` is the total error row count across every plugin.
|
|
20367
|
+
*/
|
|
20368
|
+
doctorContribErrorsHeader: "\n Runtime contribution errors (last scan) ({{count}})\n",
|
|
20369
|
+
/** Per-plugin group header: red glyph + plugin id + this plugin's error count. */
|
|
20370
|
+
doctorContribErrorEntry: " {{glyph}} {{pluginId}} ({{count}})\n",
|
|
20371
|
+
/** Sample line under a plugin group: wrapped, dimmed diagnostic message. */
|
|
20372
|
+
doctorContribErrorBody: " {{line}}\n",
|
|
20373
|
+
/** Trailing dimmed note when a plugin has more errors than the sample cap shows. */
|
|
20374
|
+
doctorContribErrorMore: " {{line}}\n",
|
|
20375
|
+
/** Body of the "more" note: the count of samples omitted under this plugin. */
|
|
20376
|
+
doctorContribErrorMoreText: "... and {{count}} more",
|
|
19906
20377
|
// --- enable / disable -----------------------------------------------
|
|
19907
20378
|
/**
|
|
19908
20379
|
* §3.1b two-line block. Mutex between explicit ids and `--all`; the
|
|
@@ -20406,7 +20877,7 @@ function sortExtensionsCanonical(exts) {
|
|
|
20406
20877
|
}
|
|
20407
20878
|
function renderBuiltInDetail(b, ansi) {
|
|
20408
20879
|
const glyph = b.enabled ? ansi.green(PLUGINS_TEXTS.rowGlyphOk) : ansi.red(PLUGINS_TEXTS.rowGlyphOff);
|
|
20409
|
-
const
|
|
20880
|
+
const count3 = b.extensions.length;
|
|
20410
20881
|
const sorted = sortExtensionsCanonical(b.extensions);
|
|
20411
20882
|
const items = sorted.map((ext) => ({
|
|
20412
20883
|
glyph: ext.enabled ? ansi.green(PLUGINS_TEXTS.rowGlyphOk) : ansi.red(PLUGINS_TEXTS.rowGlyphOff),
|
|
@@ -20417,8 +20888,8 @@ function renderBuiltInDetail(b, ansi) {
|
|
|
20417
20888
|
glyph,
|
|
20418
20889
|
id: b.id,
|
|
20419
20890
|
source: ansi.dim(PLUGINS_TEXTS.sourceBuiltIn),
|
|
20420
|
-
count,
|
|
20421
|
-
plural:
|
|
20891
|
+
count: count3,
|
|
20892
|
+
plural: count3 === 1 ? "" : "s"
|
|
20422
20893
|
}) + PLUGINS_TEXTS.detailExtensionsBlock + renderExtensionItems(items);
|
|
20423
20894
|
}
|
|
20424
20895
|
function renderPluginDetail(match, ansi) {
|
|
@@ -20600,6 +21071,7 @@ function renderExtensionFields(meta) {
|
|
|
20600
21071
|
|
|
20601
21072
|
// cli/commands/plugins/doctor.ts
|
|
20602
21073
|
import { Command as Command24, Option as Option23 } from "clipanion";
|
|
21074
|
+
var CONTRIB_ERROR_SAMPLE_CAP = 3;
|
|
20603
21075
|
var STATUS_ORDER = [
|
|
20604
21076
|
"enabled",
|
|
20605
21077
|
"disabled",
|
|
@@ -20625,6 +21097,8 @@ var PluginsDoctorCommand = class extends SmCommand {
|
|
|
20625
21097
|
const knownKinds = collectKnownKinds(plugins);
|
|
20626
21098
|
const applicableKindWarnings = collectApplicableKindWarnings(plugins, knownKinds);
|
|
20627
21099
|
const unknownSlotWarnings = collectUnknownSlotWarnings(plugins, KNOWN_SLOT_NAMES);
|
|
21100
|
+
const contribErrors = await loadContributionErrors();
|
|
21101
|
+
const contribErrorGroups = groupContributionErrorsByPlugin(contribErrors);
|
|
20628
21102
|
const bad = plugins.filter((p) => p.status !== "enabled" && p.status !== "disabled");
|
|
20629
21103
|
const totalWarnings = applicableKindWarnings.length + unknownSlotWarnings.length;
|
|
20630
21104
|
if (this.json) {
|
|
@@ -20634,23 +21108,51 @@ var PluginsDoctorCommand = class extends SmCommand {
|
|
|
20634
21108
|
applicableKindWarnings,
|
|
20635
21109
|
unknownSlotWarnings,
|
|
20636
21110
|
totalWarnings,
|
|
21111
|
+
contribErrors,
|
|
20637
21112
|
elapsedMs: this.elapsed.ms()
|
|
20638
21113
|
});
|
|
20639
21114
|
this.printer.data(JSON.stringify(envelope) + "\n");
|
|
20640
|
-
return bad.length > 0 ? ExitCode.Issues : ExitCode.Ok;
|
|
20641
|
-
}
|
|
21115
|
+
return bad.length > 0 || contribErrors.length > 0 ? ExitCode.Issues : ExitCode.Ok;
|
|
21116
|
+
}
|
|
21117
|
+
this.#renderHumanReport({
|
|
21118
|
+
counts,
|
|
21119
|
+
builtInCount: builtIns2.length,
|
|
21120
|
+
userCount: plugins.length,
|
|
21121
|
+
applicableKindWarnings,
|
|
21122
|
+
unknownSlotWarnings,
|
|
21123
|
+
totalWarnings,
|
|
21124
|
+
bad,
|
|
21125
|
+
contribErrorGroups,
|
|
21126
|
+
contribErrorCount: contribErrors.length
|
|
21127
|
+
});
|
|
21128
|
+
return bad.length > 0 || contribErrors.length > 0 ? ExitCode.Issues : ExitCode.Ok;
|
|
21129
|
+
}
|
|
21130
|
+
/**
|
|
21131
|
+
* Render the full human-mode report in section order: summary header,
|
|
21132
|
+
* source + status tables, then the gated warnings / issues / runtime
|
|
21133
|
+
* contribution-error sections. Pulled out of `run` so the verb body
|
|
21134
|
+
* stays a linear pipeline (load → aggregate → render → exit code)
|
|
21135
|
+
* under the complexity cap.
|
|
21136
|
+
*/
|
|
21137
|
+
#renderHumanReport(args2) {
|
|
20642
21138
|
const ansi = this.ansiFor("stdout");
|
|
20643
|
-
this.#renderSummaryHeader(counts.enabled, bad.length, totalWarnings);
|
|
20644
|
-
this.#renderSourceBreakdown(
|
|
20645
|
-
this.#renderStatusBreakdown(counts, ansi);
|
|
20646
|
-
if (totalWarnings > 0) {
|
|
20647
|
-
this.#renderWarnings(
|
|
21139
|
+
this.#renderSummaryHeader(args2.counts.enabled, args2.bad.length, args2.totalWarnings);
|
|
21140
|
+
this.#renderSourceBreakdown(args2.builtInCount, args2.userCount);
|
|
21141
|
+
this.#renderStatusBreakdown(args2.counts, ansi);
|
|
21142
|
+
if (args2.totalWarnings > 0) {
|
|
21143
|
+
this.#renderWarnings(
|
|
21144
|
+
args2.applicableKindWarnings,
|
|
21145
|
+
args2.unknownSlotWarnings,
|
|
21146
|
+
args2.totalWarnings,
|
|
21147
|
+
ansi
|
|
21148
|
+
);
|
|
20648
21149
|
}
|
|
20649
|
-
if (bad.length > 0) {
|
|
20650
|
-
this.#renderIssues(bad, ansi);
|
|
20651
|
-
|
|
21150
|
+
if (args2.bad.length > 0) {
|
|
21151
|
+
this.#renderIssues(args2.bad, ansi);
|
|
21152
|
+
}
|
|
21153
|
+
if (args2.contribErrorCount > 0) {
|
|
21154
|
+
this.#renderContributionErrors(args2.contribErrorGroups, args2.contribErrorCount, ansi);
|
|
20652
21155
|
}
|
|
20653
|
-
return ExitCode.Ok;
|
|
20654
21156
|
}
|
|
20655
21157
|
#renderSummaryHeader(enabled, badCount, warnings) {
|
|
20656
21158
|
this.printer.data(
|
|
@@ -20687,10 +21189,10 @@ var PluginsDoctorCommand = class extends SmCommand {
|
|
|
20687
21189
|
const statusLabelWidth = Math.max(...STATUS_ORDER.map((s) => s.length));
|
|
20688
21190
|
this.printer.data(PLUGINS_TEXTS.doctorStatusHeader);
|
|
20689
21191
|
for (const status of STATUS_ORDER) {
|
|
20690
|
-
const
|
|
20691
|
-
const isProblem = status !== "enabled" && status !== "disabled" &&
|
|
21192
|
+
const count3 = counts[status];
|
|
21193
|
+
const isProblem = status !== "enabled" && status !== "disabled" && count3 > 0;
|
|
20692
21194
|
const label = status.padEnd(statusLabelWidth);
|
|
20693
|
-
const formattedCount = isProblem ? ansi.red(String(
|
|
21195
|
+
const formattedCount = isProblem ? ansi.red(String(count3)) : String(count3);
|
|
20694
21196
|
this.printer.data(
|
|
20695
21197
|
tx(PLUGINS_TEXTS.doctorStatusRow, {
|
|
20696
21198
|
label: isProblem ? ansi.red(label) : label,
|
|
@@ -20753,6 +21255,42 @@ var PluginsDoctorCommand = class extends SmCommand {
|
|
|
20753
21255
|
}
|
|
20754
21256
|
}
|
|
20755
21257
|
}
|
|
21258
|
+
/**
|
|
21259
|
+
* "off-shape visible" follow-up. Render the last scan's runtime
|
|
21260
|
+
* contribution rejections grouped by plugin: one red entry per plugin
|
|
21261
|
+
* (id + this plugin's error count), then up to
|
|
21262
|
+
* `CONTRIB_ERROR_SAMPLE_CAP` wrapped sample messages, then a dimmed
|
|
21263
|
+
* "... and N more" note when the group overflows the cap. The full
|
|
21264
|
+
* set is always available via `--json`.
|
|
21265
|
+
*/
|
|
21266
|
+
#renderContributionErrors(groups, total, ansi) {
|
|
21267
|
+
this.printer.data(tx(PLUGINS_TEXTS.doctorContribErrorsHeader, { count: total }));
|
|
21268
|
+
const glyph = ansi.red(PLUGINS_TEXTS.rowGlyphOff);
|
|
21269
|
+
for (const group of groups) {
|
|
21270
|
+
this.printer.data(
|
|
21271
|
+
tx(PLUGINS_TEXTS.doctorContribErrorEntry, {
|
|
21272
|
+
glyph,
|
|
21273
|
+
pluginId: sanitizeForTerminal(group.pluginId),
|
|
21274
|
+
count: group.errors.length
|
|
21275
|
+
})
|
|
21276
|
+
);
|
|
21277
|
+
for (const err of group.errors.slice(0, CONTRIB_ERROR_SAMPLE_CAP)) {
|
|
21278
|
+
for (const line of wrapText(sanitizeForTerminal(err.message), 64)) {
|
|
21279
|
+
this.printer.data(
|
|
21280
|
+
tx(PLUGINS_TEXTS.doctorContribErrorBody, { line: ansi.dim(line) })
|
|
21281
|
+
);
|
|
21282
|
+
}
|
|
21283
|
+
}
|
|
21284
|
+
const hidden = group.errors.length - CONTRIB_ERROR_SAMPLE_CAP;
|
|
21285
|
+
if (hidden > 0) {
|
|
21286
|
+
this.printer.data(
|
|
21287
|
+
tx(PLUGINS_TEXTS.doctorContribErrorMore, {
|
|
21288
|
+
line: ansi.dim(tx(PLUGINS_TEXTS.doctorContribErrorMoreText, { count: hidden }))
|
|
21289
|
+
})
|
|
21290
|
+
);
|
|
21291
|
+
}
|
|
21292
|
+
}
|
|
21293
|
+
}
|
|
20756
21294
|
};
|
|
20757
21295
|
function countByStatus(builtIns2, plugins, resolveEnabled) {
|
|
20758
21296
|
const counts = {
|
|
@@ -20949,6 +21487,15 @@ function buildDoctorJsonEnvelope(args2) {
|
|
|
20949
21487
|
})
|
|
20950
21488
|
});
|
|
20951
21489
|
}
|
|
21490
|
+
const contributionErrors = args2.contribErrors.map((e) => ({
|
|
21491
|
+
pluginId: sanitizeForTerminal(e.pluginId),
|
|
21492
|
+
extensionId: sanitizeForTerminal(e.extensionId),
|
|
21493
|
+
nodePath: sanitizeForTerminal(e.nodePath),
|
|
21494
|
+
reason: sanitizeForTerminal(e.reason),
|
|
21495
|
+
message: sanitizeForTerminal(e.message),
|
|
21496
|
+
...e.contributionId !== void 0 ? { contributionId: sanitizeForTerminal(e.contributionId) } : {},
|
|
21497
|
+
...e.slot !== void 0 ? { slot: sanitizeForTerminal(e.slot) } : {}
|
|
21498
|
+
}));
|
|
20952
21499
|
return {
|
|
20953
21500
|
ok: true,
|
|
20954
21501
|
kind: "plugins.doctor",
|
|
@@ -20963,9 +21510,35 @@ function buildDoctorJsonEnvelope(args2) {
|
|
|
20963
21510
|
},
|
|
20964
21511
|
issues,
|
|
20965
21512
|
warnings,
|
|
21513
|
+
contributionErrors,
|
|
20966
21514
|
elapsedMs: args2.elapsedMs
|
|
20967
21515
|
};
|
|
20968
21516
|
}
|
|
21517
|
+
async function loadContributionErrors() {
|
|
21518
|
+
const ctx = defaultRuntimeContext();
|
|
21519
|
+
const dbPath = resolveDbPath({ db: void 0, cwd: ctx.cwd });
|
|
21520
|
+
try {
|
|
21521
|
+
const rows = await tryWithSqlite(
|
|
21522
|
+
{ databasePath: dbPath, autoBackup: false },
|
|
21523
|
+
(adapter) => adapter.contributions.listAllErrors()
|
|
21524
|
+
);
|
|
21525
|
+
return rows ?? [];
|
|
21526
|
+
} catch {
|
|
21527
|
+
return [];
|
|
21528
|
+
}
|
|
21529
|
+
}
|
|
21530
|
+
function groupContributionErrorsByPlugin(errors) {
|
|
21531
|
+
const byPlugin = /* @__PURE__ */ new Map();
|
|
21532
|
+
for (const err of errors) {
|
|
21533
|
+
let group = byPlugin.get(err.pluginId);
|
|
21534
|
+
if (!group) {
|
|
21535
|
+
group = { pluginId: err.pluginId, errors: [] };
|
|
21536
|
+
byPlugin.set(err.pluginId, group);
|
|
21537
|
+
}
|
|
21538
|
+
group.errors.push(err);
|
|
21539
|
+
}
|
|
21540
|
+
return [...byPlugin.values()];
|
|
21541
|
+
}
|
|
20969
21542
|
|
|
20970
21543
|
// cli/commands/plugins/toggle.ts
|
|
20971
21544
|
import { Command as Command25, Option as Option24 } from "clipanion";
|
|
@@ -21481,9 +22054,22 @@ function stub2(extId) {
|
|
|
21481
22054
|
* Declared settings (\`settings\`):
|
|
21482
22055
|
* - 'keywords' (${DEFAULT_INPUT_TYPE}) \u2192 exposed as ctx.settings.keywords
|
|
21483
22056
|
*
|
|
22057
|
+
* View contributions: declare each one as a const, list it in \`ui\` by
|
|
22058
|
+
* shorthand, and pass that SAME const (by reference, not a string id) to
|
|
22059
|
+
* \`ctx.emitContribution\`. The kernel recovers the contribution from the
|
|
22060
|
+
* object identity. (Write the plugin in TypeScript and add
|
|
22061
|
+
* \`satisfies IViewContribution\` from '@skill-map/cli' to get a typed payload.)
|
|
22062
|
+
*
|
|
21484
22063
|
* See: spec/plugin-author-guide.md \xA7View contributions
|
|
21485
22064
|
* spec/view-slots.md
|
|
21486
22065
|
*/
|
|
22066
|
+
const count = {
|
|
22067
|
+
slot: '${DEFAULT_SLOT}',
|
|
22068
|
+
icon: '\u{1F50D}',
|
|
22069
|
+
label: 'kw',
|
|
22070
|
+
emitWhenEmpty: false,
|
|
22071
|
+
};
|
|
22072
|
+
|
|
21487
22073
|
export default {
|
|
21488
22074
|
version: '0.1.0',
|
|
21489
22075
|
description: 'Counts configured keywords per node.',
|
|
@@ -21499,14 +22085,7 @@ export default {
|
|
|
21499
22085
|
},
|
|
21500
22086
|
},
|
|
21501
22087
|
|
|
21502
|
-
ui: {
|
|
21503
|
-
count: {
|
|
21504
|
-
slot: '${DEFAULT_SLOT}',
|
|
21505
|
-
icon: '\u{1F50D}',
|
|
21506
|
-
label: 'kw',
|
|
21507
|
-
emitWhenEmpty: false,
|
|
21508
|
-
},
|
|
21509
|
-
},
|
|
22088
|
+
ui: { count },
|
|
21510
22089
|
|
|
21511
22090
|
extract(ctx) {
|
|
21512
22091
|
const keywords = (ctx.settings && ctx.settings.keywords) || ['TODO', 'FIXME'];
|
|
@@ -21516,7 +22095,7 @@ export default {
|
|
|
21516
22095
|
total += (ctx.body.match(re) || []).length;
|
|
21517
22096
|
}
|
|
21518
22097
|
if (total > 0) {
|
|
21519
|
-
ctx.emitContribution(
|
|
22098
|
+
ctx.emitContribution(count, { value: total });
|
|
21520
22099
|
}
|
|
21521
22100
|
},
|
|
21522
22101
|
};
|
|
@@ -21810,8 +22389,8 @@ var VIEW_SLOTS_CATALOG = [
|
|
|
21810
22389
|
{ id: "card.footer.left", summary: "Counter chip in the left footer of the card." },
|
|
21811
22390
|
{ id: "card.footer.right", summary: "Counter chip in the right footer of the card." },
|
|
21812
22391
|
{ 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`.' },
|
|
21813
|
-
{ id: "inspector.header.badge
|
|
21814
|
-
{ id: "inspector.
|
|
22392
|
+
{ 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." },
|
|
22393
|
+
{ 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)." },
|
|
21815
22394
|
{ id: "inspector.body.panel.breakdown", summary: "Top-N labeled values rendered as a bar chart in the inspector body." },
|
|
21816
22395
|
{ id: "inspector.body.panel.records", summary: "Tabular data (rows \xD7 columns \u2264 50 \xD7 6) in the inspector body." },
|
|
21817
22396
|
{ id: "inspector.body.panel.tree", summary: "Recursive label/children hierarchy (depth \u2264 6, total \u2264 200) in the inspector body." },
|
|
@@ -22109,15 +22688,15 @@ var RefreshCommand = class extends SmCommand {
|
|
|
22109
22688
|
return ExitCode.Ok;
|
|
22110
22689
|
}
|
|
22111
22690
|
const glyph = ansi.green("\u2713");
|
|
22112
|
-
const
|
|
22113
|
-
const noun =
|
|
22691
|
+
const count3 = freshEnrichments.length;
|
|
22692
|
+
const noun = count3 === 1 ? REFRESH_TEXTS.refreshNounSingular : REFRESH_TEXTS.refreshNounPlural;
|
|
22114
22693
|
if (this.stale) {
|
|
22115
22694
|
const nodeCount = targetNodes.length;
|
|
22116
22695
|
const nodeNoun = nodeCount === 1 ? REFRESH_TEXTS.refreshNodeNounSingular : REFRESH_TEXTS.refreshNodeNounPlural;
|
|
22117
22696
|
this.printer.data(
|
|
22118
22697
|
tx(REFRESH_TEXTS.refreshSuccessStale, {
|
|
22119
22698
|
glyph,
|
|
22120
|
-
count,
|
|
22699
|
+
count: count3,
|
|
22121
22700
|
noun,
|
|
22122
22701
|
nodeCount,
|
|
22123
22702
|
nodeNoun
|
|
@@ -22127,7 +22706,7 @@ var RefreshCommand = class extends SmCommand {
|
|
|
22127
22706
|
this.printer.data(
|
|
22128
22707
|
tx(REFRESH_TEXTS.refreshSuccessSingle, {
|
|
22129
22708
|
glyph,
|
|
22130
|
-
count,
|
|
22709
|
+
count: count3,
|
|
22131
22710
|
noun,
|
|
22132
22711
|
nodePath: this.nodePath
|
|
22133
22712
|
})
|
|
@@ -22604,7 +23183,15 @@ function createWatcherRuntime(opts) {
|
|
|
22604
23183
|
runOptions.priorExtractorRuns = priorState.extractorRuns;
|
|
22605
23184
|
}
|
|
22606
23185
|
const ran = await runScanWithRenames(kernel, runOptions);
|
|
22607
|
-
const {
|
|
23186
|
+
const {
|
|
23187
|
+
result,
|
|
23188
|
+
renameOps,
|
|
23189
|
+
extractorRuns,
|
|
23190
|
+
enrichments,
|
|
23191
|
+
contributions,
|
|
23192
|
+
contributionErrors,
|
|
23193
|
+
freshlyRunTuples
|
|
23194
|
+
} = ran;
|
|
22608
23195
|
await withSqlite(
|
|
22609
23196
|
{ databasePath: opts.dbPath },
|
|
22610
23197
|
(writer) => writer.scans.persist(result, {
|
|
@@ -22612,6 +23199,7 @@ function createWatcherRuntime(opts) {
|
|
|
22612
23199
|
extractorRuns,
|
|
22613
23200
|
enrichments,
|
|
22614
23201
|
contributions,
|
|
23202
|
+
contributionErrors,
|
|
22615
23203
|
registeredContributionKeys: collectRegisteredContributionKeys(composed),
|
|
22616
23204
|
freshlyRunTuples
|
|
22617
23205
|
})
|
|
@@ -22940,11 +23528,11 @@ async function runWatchLoop(opts) {
|
|
|
22940
23528
|
}
|
|
22941
23529
|
void info;
|
|
22942
23530
|
},
|
|
22943
|
-
onBreakerTripped: (
|
|
23531
|
+
onBreakerTripped: (count3, message) => {
|
|
22944
23532
|
context.stderr.write(
|
|
22945
23533
|
tx(WATCH_TEXTS.breakerTripped, {
|
|
22946
23534
|
glyph: errGlyph,
|
|
22947
|
-
count,
|
|
23535
|
+
count: count3,
|
|
22948
23536
|
hint: stderrAnsi.dim(tx(WATCH_TEXTS.breakerTrippedHint, { message }))
|
|
22949
23537
|
})
|
|
22950
23538
|
);
|
|
@@ -23486,8 +24074,8 @@ function formatScanCounts(opts) {
|
|
|
23486
24074
|
}
|
|
23487
24075
|
return parts.join(" \xB7 ");
|
|
23488
24076
|
}
|
|
23489
|
-
function countNoun(
|
|
23490
|
-
return
|
|
24077
|
+
function countNoun(count3, singular, plural) {
|
|
24078
|
+
return count3 === 1 ? singular : plural;
|
|
23491
24079
|
}
|
|
23492
24080
|
|
|
23493
24081
|
// cli/commands/scan-compare.ts
|
|
@@ -23769,7 +24357,7 @@ import { WebSocketServer } from "ws";
|
|
|
23769
24357
|
// server/app.ts
|
|
23770
24358
|
import { Hono } from "hono";
|
|
23771
24359
|
import { bodyLimit } from "hono/body-limit";
|
|
23772
|
-
import { HTTPException as
|
|
24360
|
+
import { HTTPException as HTTPException17 } from "hono/http-exception";
|
|
23773
24361
|
|
|
23774
24362
|
// core/config/service.ts
|
|
23775
24363
|
var ConfigService = class {
|
|
@@ -23932,33 +24520,33 @@ var SERVER_TEXTS = {
|
|
|
23932
24520
|
// Hono's `app.notFound` fallback, every other unmatched path funnels
|
|
23933
24521
|
// here (after static + SPA fallback have had their turn).
|
|
23934
24522
|
unknownPath: "Not found: {{path}}.",
|
|
23935
|
-
// ----
|
|
23936
|
-
//
|
|
23937
|
-
//
|
|
23938
|
-
//
|
|
23939
|
-
//
|
|
23940
|
-
|
|
24523
|
+
// ---- generic action-dispatch route (routes/actions.ts, Step 17) ----------
|
|
24524
|
+
//
|
|
24525
|
+
// POST /api/actions/:qualifiedId. Generalises the legacy bump route:
|
|
24526
|
+
// resolve the qualified action id off the kernel registry, invoke it,
|
|
24527
|
+
// materialise any sidecar writes through the same consent-gated store,
|
|
24528
|
+
// broadcast `action.applied`. Each error keeps its own message key so
|
|
24529
|
+
// the UI / log can disambiguate without regex on the message.
|
|
23941
24530
|
// 400 envelopes thrown by `parseBody` when the request payload is
|
|
23942
|
-
// malformed.
|
|
23943
|
-
|
|
23944
|
-
|
|
23945
|
-
|
|
23946
|
-
|
|
23947
|
-
|
|
23948
|
-
|
|
23949
|
-
|
|
23950
|
-
|
|
23951
|
-
|
|
23952
|
-
|
|
23953
|
-
|
|
23954
|
-
|
|
23955
|
-
|
|
23956
|
-
|
|
23957
|
-
//
|
|
23958
|
-
//
|
|
23959
|
-
//
|
|
23960
|
-
|
|
23961
|
-
sidecarBumpInvokeMissing: "built-in bump action is missing its invoke().",
|
|
24531
|
+
// malformed.
|
|
24532
|
+
actionBodyNotJson: "Request body must be valid JSON.",
|
|
24533
|
+
actionBodyNotObject: "Request body must be a JSON object.",
|
|
24534
|
+
actionNodePathRequired: "`nodePath` is required and must be a non-empty string.",
|
|
24535
|
+
actionInputMustBeObject: "`input` must be an object when present.",
|
|
24536
|
+
actionConfirmMustBeBoolean: "`confirm` must be a boolean when present.",
|
|
24537
|
+
actionAlwaysMustBeBoolean: "`always` must be a boolean when present.",
|
|
24538
|
+
// 404 envelope when `:qualifiedId` does not resolve to a registered
|
|
24539
|
+
// action with an `invoke()`. Covers both "no such action" and "action
|
|
24540
|
+
// exists but ships no deterministic entry point" (a probabilistic
|
|
24541
|
+
// action that cannot be dispatched over this synchronous route). The
|
|
24542
|
+
// id is sanitised before interpolation.
|
|
24543
|
+
actionUnknown: 'No invokable action with id "{{actionId}}".',
|
|
24544
|
+
// 409 envelope when an action's report comes back `ok: false`. The
|
|
24545
|
+
// refusal `reason` (when the report carries one) becomes the envelope
|
|
24546
|
+
// `code`; the fallback `action-refused` covers reports that refuse
|
|
24547
|
+
// without naming a reason. Dispatch is via the typed
|
|
24548
|
+
// `ActionRefusedError`; the message is informational only.
|
|
24549
|
+
actionRefused: 'Action "{{actionId}}" refused to run on "{{nodePath}}".',
|
|
23962
24550
|
// ---- POST /api/scan (manual refresh) ------------------------------------
|
|
23963
24551
|
// 400, runtime cannot persist a meaningful scan because the boot
|
|
23964
24552
|
// dropped half the pipeline. Same gate the `?fresh=1` GET applies.
|
|
@@ -25029,6 +25617,8 @@ function registerPluginsRoute(app, deps) {
|
|
|
25029
25617
|
app.get("/api/plugins", async (c) => {
|
|
25030
25618
|
const resolveEnabled = await buildFreshResolver2(deps);
|
|
25031
25619
|
const items = listItems(deps, resolveEnabled);
|
|
25620
|
+
const errorsByPlugin = await loadRuntimeContributionErrors(deps);
|
|
25621
|
+
attachRuntimeContributionErrors(items, errorsByPlugin);
|
|
25032
25622
|
return c.json(
|
|
25033
25623
|
buildListEnvelope({
|
|
25034
25624
|
kind: "plugins",
|
|
@@ -25203,6 +25793,41 @@ function firstVersion(extensions) {
|
|
|
25203
25793
|
function classifyPluginSource(_pluginPath, _deps) {
|
|
25204
25794
|
return "project";
|
|
25205
25795
|
}
|
|
25796
|
+
async function loadRuntimeContributionErrors(deps) {
|
|
25797
|
+
try {
|
|
25798
|
+
const rows = await tryWithSqlite(
|
|
25799
|
+
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
25800
|
+
(adapter) => adapter.contributions.listAllErrors()
|
|
25801
|
+
);
|
|
25802
|
+
if (rows === null) return /* @__PURE__ */ new Map();
|
|
25803
|
+
return groupContributionErrorsByPlugin2(rows);
|
|
25804
|
+
} catch {
|
|
25805
|
+
return /* @__PURE__ */ new Map();
|
|
25806
|
+
}
|
|
25807
|
+
}
|
|
25808
|
+
function groupContributionErrorsByPlugin2(rows) {
|
|
25809
|
+
const out = /* @__PURE__ */ new Map();
|
|
25810
|
+
for (const row of rows) {
|
|
25811
|
+
const projected = {
|
|
25812
|
+
extensionId: row.extensionId,
|
|
25813
|
+
nodePath: row.nodePath,
|
|
25814
|
+
reason: row.reason,
|
|
25815
|
+
message: row.message,
|
|
25816
|
+
...row.contributionId !== void 0 ? { contributionId: row.contributionId } : {},
|
|
25817
|
+
...row.slot !== void 0 ? { slot: row.slot } : {}
|
|
25818
|
+
};
|
|
25819
|
+
const list = out.get(row.pluginId);
|
|
25820
|
+
if (list) list.push(projected);
|
|
25821
|
+
else out.set(row.pluginId, [projected]);
|
|
25822
|
+
}
|
|
25823
|
+
return out;
|
|
25824
|
+
}
|
|
25825
|
+
function attachRuntimeContributionErrors(items, errorsByPlugin) {
|
|
25826
|
+
for (const item of items) {
|
|
25827
|
+
const errors = errorsByPlugin.get(item.id);
|
|
25828
|
+
if (errors && errors.length > 0) item.runtimeContributionErrors = errors;
|
|
25829
|
+
}
|
|
25830
|
+
}
|
|
25206
25831
|
async function persistAndProject(c, deps, configKey, enabled) {
|
|
25207
25832
|
const overrides = await tryWithSqlite(
|
|
25208
25833
|
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
@@ -25892,8 +26517,168 @@ var parsePatchBody5 = makeBodyValidator(PATCH_BODY_SCHEMA4, {
|
|
|
25892
26517
|
}
|
|
25893
26518
|
});
|
|
25894
26519
|
|
|
25895
|
-
// server/routes/
|
|
26520
|
+
// server/routes/actions.ts
|
|
26521
|
+
import { HTTPException as HTTPException15 } from "hono/http-exception";
|
|
26522
|
+
import { resolve as resolve36 } from "path";
|
|
26523
|
+
|
|
26524
|
+
// server/routes/node-loader.ts
|
|
25896
26525
|
import { HTTPException as HTTPException14 } from "hono/http-exception";
|
|
26526
|
+
async function loadNode(deps, nodePath) {
|
|
26527
|
+
const persisted = await tryWithSqlite(
|
|
26528
|
+
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
26529
|
+
async (adapter) => adapter.scans.load()
|
|
26530
|
+
);
|
|
26531
|
+
const node = persisted?.nodes.find((n) => n.path === nodePath);
|
|
26532
|
+
if (!node) {
|
|
26533
|
+
throw new HTTPException14(404, {
|
|
26534
|
+
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
26535
|
+
});
|
|
26536
|
+
}
|
|
26537
|
+
return node;
|
|
26538
|
+
}
|
|
26539
|
+
|
|
26540
|
+
// server/routes/actions.ts
|
|
26541
|
+
var ENVELOPE_KIND2 = "action.applied";
|
|
26542
|
+
var REFUSED_CODE = "action-refused";
|
|
26543
|
+
var INVOKER_FALLBACK = "ui";
|
|
26544
|
+
var QUALIFIED_ID_SEGMENT2 = /^[A-Za-z0-9._-]+$/;
|
|
26545
|
+
var ACTION_BODY_SCHEMA = {
|
|
26546
|
+
type: "object",
|
|
26547
|
+
additionalProperties: false,
|
|
26548
|
+
required: ["nodePath"],
|
|
26549
|
+
properties: {
|
|
26550
|
+
nodePath: { type: "string", minLength: 1 },
|
|
26551
|
+
input: { type: "object" },
|
|
26552
|
+
confirm: { type: "boolean" },
|
|
26553
|
+
always: { type: "boolean" }
|
|
26554
|
+
}
|
|
26555
|
+
};
|
|
26556
|
+
var parseBody = makeBodyValidator(ACTION_BODY_SCHEMA, {
|
|
26557
|
+
notJson: SERVER_TEXTS.actionBodyNotJson,
|
|
26558
|
+
notObject: SERVER_TEXTS.actionBodyNotObject,
|
|
26559
|
+
invalid: SERVER_TEXTS.actionBodyNotObject,
|
|
26560
|
+
mapping: {
|
|
26561
|
+
"/nodePath:required": SERVER_TEXTS.actionNodePathRequired,
|
|
26562
|
+
":type:object": SERVER_TEXTS.actionBodyNotObject,
|
|
26563
|
+
"/nodePath:type:string": SERVER_TEXTS.actionNodePathRequired,
|
|
26564
|
+
"/nodePath:minLength": SERVER_TEXTS.actionNodePathRequired,
|
|
26565
|
+
"/input:type:object": SERVER_TEXTS.actionInputMustBeObject,
|
|
26566
|
+
"/confirm:type:boolean": SERVER_TEXTS.actionConfirmMustBeBoolean,
|
|
26567
|
+
"/always:type:boolean": SERVER_TEXTS.actionAlwaysMustBeBoolean
|
|
26568
|
+
}
|
|
26569
|
+
});
|
|
26570
|
+
function registerActionsRoutes(app, deps) {
|
|
26571
|
+
app.post("/api/actions/:pluginId/:actionId", async (c) => {
|
|
26572
|
+
const startedAt = Date.now();
|
|
26573
|
+
const pluginId = parseSegment(c.req.param("pluginId"), "pluginId");
|
|
26574
|
+
const shortId = parseSegment(c.req.param("actionId"), "actionId");
|
|
26575
|
+
const actionId = qualifiedExtensionId(pluginId, shortId);
|
|
26576
|
+
const action = resolveInvokableAction(deps.kernel, actionId);
|
|
26577
|
+
const body = await parseBody(c.req.raw);
|
|
26578
|
+
const node = await loadNode(deps, body.nodePath);
|
|
26579
|
+
let absPath;
|
|
26580
|
+
try {
|
|
26581
|
+
assertContained(deps.runtimeContext.cwd, node.path);
|
|
26582
|
+
absPath = resolve36(deps.runtimeContext.cwd, node.path);
|
|
26583
|
+
} catch (err) {
|
|
26584
|
+
throw new HTTPException15(400, { message: formatErrorMessage(err) });
|
|
26585
|
+
}
|
|
26586
|
+
const result = invokeAction(action, absPath, node, body, deps.runtimeContext.cwd);
|
|
26587
|
+
const report = result.report;
|
|
26588
|
+
if (report.ok === false) {
|
|
26589
|
+
const reason = typeof report.reason === "string" && report.reason.length > 0 ? sanitizeForTerminal(report.reason) : REFUSED_CODE;
|
|
26590
|
+
throw new ActionRefusedError({
|
|
26591
|
+
code: reason,
|
|
26592
|
+
message: tx(SERVER_TEXTS.actionRefused, {
|
|
26593
|
+
actionId: sanitizeForTerminal(actionId),
|
|
26594
|
+
nodePath: sanitizeForTerminal(node.path)
|
|
26595
|
+
}),
|
|
26596
|
+
actionId,
|
|
26597
|
+
nodePath: node.path,
|
|
26598
|
+
report: result.report
|
|
26599
|
+
});
|
|
26600
|
+
}
|
|
26601
|
+
if (report.ok === true && report.noop === true) {
|
|
26602
|
+
return c.json(buildEnvelope5(actionId, node.path, result.report, startedAt));
|
|
26603
|
+
}
|
|
26604
|
+
await materializeWrites(result.writes, body, deps.runtimeContext.cwd);
|
|
26605
|
+
if (body.always === true) {
|
|
26606
|
+
deps.configService.reload();
|
|
26607
|
+
}
|
|
26608
|
+
const eventData = {
|
|
26609
|
+
actionId,
|
|
26610
|
+
nodePath: node.path,
|
|
26611
|
+
report: result.report
|
|
26612
|
+
};
|
|
26613
|
+
const wsEnvelope = {
|
|
26614
|
+
type: ENVELOPE_KIND2,
|
|
26615
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26616
|
+
data: eventData
|
|
26617
|
+
};
|
|
26618
|
+
deps.broadcaster.broadcast(wsEnvelope);
|
|
26619
|
+
return c.json(buildEnvelope5(actionId, node.path, result.report, startedAt));
|
|
26620
|
+
});
|
|
26621
|
+
}
|
|
26622
|
+
function parseSegment(value, name) {
|
|
26623
|
+
if (!QUALIFIED_ID_SEGMENT2.test(value)) {
|
|
26624
|
+
throw new HTTPException15(400, {
|
|
26625
|
+
message: tx(SERVER_TEXTS.qualifiedIdMalformed, {
|
|
26626
|
+
name,
|
|
26627
|
+
value: sanitizeForTerminal(value)
|
|
26628
|
+
})
|
|
26629
|
+
});
|
|
26630
|
+
}
|
|
26631
|
+
return value;
|
|
26632
|
+
}
|
|
26633
|
+
function resolveInvokableAction(kernel, actionId) {
|
|
26634
|
+
const ext = kernel.registry.get("action", actionId);
|
|
26635
|
+
const action = ext;
|
|
26636
|
+
if (!action || typeof action.invoke !== "function") {
|
|
26637
|
+
throw new HTTPException15(404, {
|
|
26638
|
+
message: tx(SERVER_TEXTS.actionUnknown, { actionId: sanitizeForTerminal(actionId) })
|
|
26639
|
+
});
|
|
26640
|
+
}
|
|
26641
|
+
return action;
|
|
26642
|
+
}
|
|
26643
|
+
function invokeAction(action, absPath, node, body, cwd) {
|
|
26644
|
+
const invoke = action.invoke;
|
|
26645
|
+
const ctx = {
|
|
26646
|
+
node,
|
|
26647
|
+
nodeAbsolutePath: absPath,
|
|
26648
|
+
invoker: resolveGitAuthorName(cwd) ?? INVOKER_FALLBACK,
|
|
26649
|
+
now: () => /* @__PURE__ */ new Date(),
|
|
26650
|
+
settings: {}
|
|
26651
|
+
};
|
|
26652
|
+
return invoke(body.input ?? {}, ctx);
|
|
26653
|
+
}
|
|
26654
|
+
async function materializeWrites(writes, body, cwd) {
|
|
26655
|
+
const store = new FilesystemSidecarStore(ensureSidecarWritesAllowed);
|
|
26656
|
+
try {
|
|
26657
|
+
for (const w of writes ?? []) {
|
|
26658
|
+
if (w.kind === "sidecar") {
|
|
26659
|
+
await store.applyPatch(w.path, w.changes, {
|
|
26660
|
+
confirm: body.confirm === true,
|
|
26661
|
+
always: body.always === true,
|
|
26662
|
+
cwd
|
|
26663
|
+
});
|
|
26664
|
+
}
|
|
26665
|
+
}
|
|
26666
|
+
} catch (err) {
|
|
26667
|
+
if (err instanceof EConsentRequiredError) throw err;
|
|
26668
|
+
throw new HTTPException15(500, { message: formatErrorMessage(err) });
|
|
26669
|
+
}
|
|
26670
|
+
}
|
|
26671
|
+
function buildEnvelope5(actionId, nodePath, report, startedAt) {
|
|
26672
|
+
return {
|
|
26673
|
+
schemaVersion: "1",
|
|
26674
|
+
kind: ENVELOPE_KIND2,
|
|
26675
|
+
value: { actionId, nodePath, report },
|
|
26676
|
+
elapsedMs: Date.now() - startedAt
|
|
26677
|
+
};
|
|
26678
|
+
}
|
|
26679
|
+
|
|
26680
|
+
// server/routes/scan.ts
|
|
26681
|
+
import { HTTPException as HTTPException16 } from "hono/http-exception";
|
|
25897
26682
|
|
|
25898
26683
|
// server/scan-mutex.ts
|
|
25899
26684
|
var inFlight = null;
|
|
@@ -26072,7 +26857,7 @@ function registerScanRoute(app, deps) {
|
|
|
26072
26857
|
}
|
|
26073
26858
|
async function runPersistedScan(c, deps) {
|
|
26074
26859
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
26075
|
-
throw new
|
|
26860
|
+
throw new HTTPException16(400, { message: SERVER_TEXTS.scanPostRequiresFullPipeline });
|
|
26076
26861
|
}
|
|
26077
26862
|
const dbExists = await tryWithSqlite(
|
|
26078
26863
|
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
@@ -26109,7 +26894,7 @@ async function runPersistedScan(c, deps) {
|
|
|
26109
26894
|
...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
|
|
26110
26895
|
});
|
|
26111
26896
|
if (outcome.kind !== "ok") {
|
|
26112
|
-
throw new
|
|
26897
|
+
throw new HTTPException16(500, {
|
|
26113
26898
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.scanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
26114
26899
|
});
|
|
26115
26900
|
}
|
|
@@ -26187,7 +26972,7 @@ function groupTagsByPath2(rows) {
|
|
|
26187
26972
|
}
|
|
26188
26973
|
async function runFreshScan(deps) {
|
|
26189
26974
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
26190
|
-
throw new
|
|
26975
|
+
throw new HTTPException16(400, { message: SERVER_TEXTS.freshScanRequiresPipeline });
|
|
26191
26976
|
}
|
|
26192
26977
|
const resolveEnabledOverride = await buildBffResolverOverride(deps);
|
|
26193
26978
|
const outcome = await runScanForCommand({
|
|
@@ -26222,7 +27007,7 @@ async function runFreshScan(deps) {
|
|
|
26222
27007
|
...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
|
|
26223
27008
|
});
|
|
26224
27009
|
if (outcome.kind !== "ok") {
|
|
26225
|
-
throw new
|
|
27010
|
+
throw new HTTPException16(500, {
|
|
26226
27011
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.freshScanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
26227
27012
|
});
|
|
26228
27013
|
}
|
|
@@ -26268,141 +27053,6 @@ function emptyScanResult() {
|
|
|
26268
27053
|
};
|
|
26269
27054
|
}
|
|
26270
27055
|
|
|
26271
|
-
// server/routes/sidecar.ts
|
|
26272
|
-
import { HTTPException as HTTPException15 } from "hono/http-exception";
|
|
26273
|
-
import { resolve as resolve36 } from "path";
|
|
26274
|
-
var STATUS_FRESH = "fresh";
|
|
26275
|
-
var ENVELOPE_KIND2 = "sidecar.bumped";
|
|
26276
|
-
var BUMP_BODY_SCHEMA = {
|
|
26277
|
-
type: "object",
|
|
26278
|
-
additionalProperties: false,
|
|
26279
|
-
required: ["nodePath"],
|
|
26280
|
-
properties: {
|
|
26281
|
-
nodePath: { type: "string", minLength: 1 },
|
|
26282
|
-
force: { type: "boolean" },
|
|
26283
|
-
confirm: { type: "boolean" }
|
|
26284
|
-
}
|
|
26285
|
-
};
|
|
26286
|
-
var parseBody = makeBodyValidator(BUMP_BODY_SCHEMA, {
|
|
26287
|
-
notJson: SERVER_TEXTS.sidecarBodyNotJson,
|
|
26288
|
-
notObject: SERVER_TEXTS.sidecarBodyNotObject,
|
|
26289
|
-
invalid: SERVER_TEXTS.sidecarBodyNotObject,
|
|
26290
|
-
mapping: {
|
|
26291
|
-
"/nodePath:required": SERVER_TEXTS.sidecarNodePathRequired,
|
|
26292
|
-
":type:object": SERVER_TEXTS.sidecarBodyNotObject,
|
|
26293
|
-
"/nodePath:type:string": SERVER_TEXTS.sidecarNodePathRequired,
|
|
26294
|
-
"/nodePath:minLength": SERVER_TEXTS.sidecarNodePathRequired,
|
|
26295
|
-
"/force:type:boolean": SERVER_TEXTS.sidecarForceMustBeBoolean,
|
|
26296
|
-
"/confirm:type:boolean": SERVER_TEXTS.sidecarConfirmMustBeBoolean
|
|
26297
|
-
}
|
|
26298
|
-
});
|
|
26299
|
-
function registerSidecarRoutes(app, deps) {
|
|
26300
|
-
app.post("/api/sidecar/bump", async (c) => {
|
|
26301
|
-
const startedAt = Date.now();
|
|
26302
|
-
const body = await parseBody(c.req.raw);
|
|
26303
|
-
const node = await loadNode(deps, body.nodePath);
|
|
26304
|
-
let absPath;
|
|
26305
|
-
try {
|
|
26306
|
-
assertContained(deps.runtimeContext.cwd, node.path);
|
|
26307
|
-
absPath = resolve36(deps.runtimeContext.cwd, node.path);
|
|
26308
|
-
} catch (err) {
|
|
26309
|
-
throw new HTTPException15(400, { message: formatErrorMessage(err) });
|
|
26310
|
-
}
|
|
26311
|
-
const result = invokeBump2(node, absPath, body, deps.runtimeContext.cwd);
|
|
26312
|
-
if (result.report.ok === false && result.report.reason === "fresh") {
|
|
26313
|
-
throw new ConflictError({ code: "sidecar-fresh", message: SERVER_TEXTS.sidecarFreshRefusal });
|
|
26314
|
-
}
|
|
26315
|
-
if (result.report.ok === true && result.report.noop === true) {
|
|
26316
|
-
const envelope2 = {
|
|
26317
|
-
schemaVersion: "1",
|
|
26318
|
-
kind: ENVELOPE_KIND2,
|
|
26319
|
-
value: {
|
|
26320
|
-
nodePath: node.path,
|
|
26321
|
-
version: pickExistingVersion(node),
|
|
26322
|
-
status: STATUS_FRESH
|
|
26323
|
-
},
|
|
26324
|
-
elapsedMs: Date.now() - startedAt
|
|
26325
|
-
};
|
|
26326
|
-
return c.json(envelope2);
|
|
26327
|
-
}
|
|
26328
|
-
const store = new FilesystemSidecarStore(ensureSidecarWritesAllowed);
|
|
26329
|
-
try {
|
|
26330
|
-
for (const w of result.writes ?? []) {
|
|
26331
|
-
if (w.kind === "sidecar") {
|
|
26332
|
-
await store.applyPatch(w.path, w.changes, {
|
|
26333
|
-
confirm: body.confirm === true,
|
|
26334
|
-
cwd: deps.runtimeContext.cwd
|
|
26335
|
-
});
|
|
26336
|
-
}
|
|
26337
|
-
}
|
|
26338
|
-
} catch (err) {
|
|
26339
|
-
if (err instanceof EConsentRequiredError) throw err;
|
|
26340
|
-
throw new HTTPException15(500, { message: formatErrorMessage(err) });
|
|
26341
|
-
}
|
|
26342
|
-
if (body.confirm === true) {
|
|
26343
|
-
deps.configService.reload();
|
|
26344
|
-
}
|
|
26345
|
-
const newVersion = result.report.version ?? null;
|
|
26346
|
-
const eventData = {
|
|
26347
|
-
nodePath: node.path,
|
|
26348
|
-
version: newVersion,
|
|
26349
|
-
status: STATUS_FRESH
|
|
26350
|
-
};
|
|
26351
|
-
const wsEnvelope = {
|
|
26352
|
-
type: ENVELOPE_KIND2,
|
|
26353
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26354
|
-
data: eventData
|
|
26355
|
-
};
|
|
26356
|
-
deps.broadcaster.broadcast(wsEnvelope);
|
|
26357
|
-
const envelope = {
|
|
26358
|
-
schemaVersion: "1",
|
|
26359
|
-
kind: ENVELOPE_KIND2,
|
|
26360
|
-
value: {
|
|
26361
|
-
nodePath: node.path,
|
|
26362
|
-
version: newVersion,
|
|
26363
|
-
status: STATUS_FRESH
|
|
26364
|
-
},
|
|
26365
|
-
elapsedMs: Date.now() - startedAt
|
|
26366
|
-
};
|
|
26367
|
-
return c.json(envelope);
|
|
26368
|
-
});
|
|
26369
|
-
}
|
|
26370
|
-
async function loadNode(deps, nodePath) {
|
|
26371
|
-
const persisted = await tryWithSqlite(
|
|
26372
|
-
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
26373
|
-
async (adapter) => adapter.scans.load()
|
|
26374
|
-
);
|
|
26375
|
-
const node = persisted?.nodes.find((n) => n.path === nodePath);
|
|
26376
|
-
if (!node) {
|
|
26377
|
-
throw new HTTPException15(404, {
|
|
26378
|
-
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
26379
|
-
});
|
|
26380
|
-
}
|
|
26381
|
-
return node;
|
|
26382
|
-
}
|
|
26383
|
-
function invokeBump2(node, absPath, body, cwd) {
|
|
26384
|
-
if (!nodeBumpAction.invoke) {
|
|
26385
|
-
throw new HTTPException15(500, { message: SERVER_TEXTS.sidecarBumpInvokeMissing });
|
|
26386
|
-
}
|
|
26387
|
-
const input = {};
|
|
26388
|
-
if (body.force === true) input.force = true;
|
|
26389
|
-
return nodeBumpAction.invoke(input, {
|
|
26390
|
-
node,
|
|
26391
|
-
nodeAbsolutePath: absPath,
|
|
26392
|
-
invoker: resolveGitAuthorName(cwd) ?? "ui",
|
|
26393
|
-
now: () => /* @__PURE__ */ new Date(),
|
|
26394
|
-
settings: {}
|
|
26395
|
-
});
|
|
26396
|
-
}
|
|
26397
|
-
function pickExistingVersion(node) {
|
|
26398
|
-
const overlay = node.sidecar;
|
|
26399
|
-
if (!overlay || overlay.present !== true) return null;
|
|
26400
|
-
const annotations = overlay.annotations;
|
|
26401
|
-
if (!annotations) return null;
|
|
26402
|
-
const v = annotations["version"];
|
|
26403
|
-
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
26404
|
-
}
|
|
26405
|
-
|
|
26406
27056
|
// server/routes/update-status.ts
|
|
26407
27057
|
function registerUpdateStatusRoute(app, deps) {
|
|
26408
27058
|
app.get("/api/update-status", async (c) => {
|
|
@@ -26569,13 +27219,13 @@ function attachBroadcasterRoute(app, broadcaster) {
|
|
|
26569
27219
|
|
|
26570
27220
|
// server/app.ts
|
|
26571
27221
|
var BODY_LIMIT_BYTES = 1024 * 1024;
|
|
26572
|
-
var DbMissingError = class extends
|
|
27222
|
+
var DbMissingError = class extends HTTPException17 {
|
|
26573
27223
|
constructor(message) {
|
|
26574
27224
|
super(500, { message });
|
|
26575
27225
|
this.name = "DbMissingError";
|
|
26576
27226
|
}
|
|
26577
27227
|
};
|
|
26578
|
-
var BulkValidationError = class extends
|
|
27228
|
+
var BulkValidationError = class extends HTTPException17 {
|
|
26579
27229
|
id;
|
|
26580
27230
|
code;
|
|
26581
27231
|
constructor(init) {
|
|
@@ -26585,7 +27235,7 @@ var BulkValidationError = class extends HTTPException16 {
|
|
|
26585
27235
|
this.code = init.code;
|
|
26586
27236
|
}
|
|
26587
27237
|
};
|
|
26588
|
-
var LoopbackGateError = class extends
|
|
27238
|
+
var LoopbackGateError = class extends HTTPException17 {
|
|
26589
27239
|
code;
|
|
26590
27240
|
constructor(init) {
|
|
26591
27241
|
super(403, { message: init.message });
|
|
@@ -26593,7 +27243,7 @@ var LoopbackGateError = class extends HTTPException16 {
|
|
|
26593
27243
|
this.code = init.code;
|
|
26594
27244
|
}
|
|
26595
27245
|
};
|
|
26596
|
-
var ConflictError = class extends
|
|
27246
|
+
var ConflictError = class extends HTTPException17 {
|
|
26597
27247
|
code;
|
|
26598
27248
|
constructor(init) {
|
|
26599
27249
|
super(409, { message: init.message });
|
|
@@ -26601,6 +27251,21 @@ var ConflictError = class extends HTTPException16 {
|
|
|
26601
27251
|
this.code = init.code;
|
|
26602
27252
|
}
|
|
26603
27253
|
};
|
|
27254
|
+
var ActionRefusedError = class extends HTTPException17 {
|
|
27255
|
+
/** Refusal code: the report's `reason` when present, else `'action-refused'`. */
|
|
27256
|
+
code;
|
|
27257
|
+
details;
|
|
27258
|
+
constructor(init) {
|
|
27259
|
+
super(409, { message: init.message });
|
|
27260
|
+
this.name = "ActionRefusedError";
|
|
27261
|
+
this.code = init.code;
|
|
27262
|
+
this.details = {
|
|
27263
|
+
actionId: init.actionId,
|
|
27264
|
+
nodePath: init.nodePath,
|
|
27265
|
+
report: init.report
|
|
27266
|
+
};
|
|
27267
|
+
}
|
|
27268
|
+
};
|
|
26604
27269
|
function createApp(deps) {
|
|
26605
27270
|
const app = new Hono();
|
|
26606
27271
|
const configService = new ConfigService({
|
|
@@ -26614,7 +27279,7 @@ function createApp(deps) {
|
|
|
26614
27279
|
bodyLimit({
|
|
26615
27280
|
maxSize: BODY_LIMIT_BYTES,
|
|
26616
27281
|
onError: () => {
|
|
26617
|
-
throw new
|
|
27282
|
+
throw new HTTPException17(413, { message: tx(SERVER_TEXTS.bodyTooLarge, { maxBytes: String(BODY_LIMIT_BYTES) }) });
|
|
26618
27283
|
}
|
|
26619
27284
|
})
|
|
26620
27285
|
);
|
|
@@ -26650,7 +27315,7 @@ function createApp(deps) {
|
|
|
26650
27315
|
registerGraphRoute(app, routeDeps);
|
|
26651
27316
|
registerConfigRoute(app, routeDeps);
|
|
26652
27317
|
registerPluginsRoute(app, routeDeps);
|
|
26653
|
-
|
|
27318
|
+
registerActionsRoutes(app, { ...routeDeps, broadcaster: deps.broadcaster, kernel: deps.kernel });
|
|
26654
27319
|
registerFavoritesRoutes(app, routeDeps);
|
|
26655
27320
|
registerAnnotationsRoute(app, { kernel: deps.kernel });
|
|
26656
27321
|
registerContributionsRoutes(app, { ...routeDeps, kernel: deps.kernel });
|
|
@@ -26660,7 +27325,7 @@ function createApp(deps) {
|
|
|
26660
27325
|
registerActiveProviderRoute(app, routeDeps);
|
|
26661
27326
|
registerProjectIgnoreRoute(app, routeDeps);
|
|
26662
27327
|
app.all("/api/*", (c) => {
|
|
26663
|
-
throw new
|
|
27328
|
+
throw new HTTPException17(404, {
|
|
26664
27329
|
message: tx(SERVER_TEXTS.unknownApiEndpoint, { path: sanitizeForTerminal(c.req.path) })
|
|
26665
27330
|
});
|
|
26666
27331
|
});
|
|
@@ -26668,7 +27333,7 @@ function createApp(deps) {
|
|
|
26668
27333
|
app.use("*", createStaticHandler({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
26669
27334
|
app.get("*", createSpaFallback({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
26670
27335
|
app.notFound((c) => {
|
|
26671
|
-
throw new
|
|
27336
|
+
throw new HTTPException17(404, {
|
|
26672
27337
|
message: tx(SERVER_TEXTS.unknownPath, { path: sanitizeForTerminal(c.req.path) })
|
|
26673
27338
|
});
|
|
26674
27339
|
});
|
|
@@ -26719,18 +27384,9 @@ function formatError2(err, c) {
|
|
|
26719
27384
|
};
|
|
26720
27385
|
return c.json(envelope, 403);
|
|
26721
27386
|
}
|
|
26722
|
-
|
|
26723
|
-
|
|
26724
|
-
|
|
26725
|
-
error: {
|
|
26726
|
-
code: err.code,
|
|
26727
|
-
message: err.message,
|
|
26728
|
-
details: null
|
|
26729
|
-
}
|
|
26730
|
-
};
|
|
26731
|
-
return c.json(envelope, 409);
|
|
26732
|
-
}
|
|
26733
|
-
if (err instanceof HTTPException16) {
|
|
27387
|
+
const conflict = formatConflict(err, c);
|
|
27388
|
+
if (conflict) return conflict;
|
|
27389
|
+
if (err instanceof HTTPException17) {
|
|
26734
27390
|
const status = err.status;
|
|
26735
27391
|
const envelope = {
|
|
26736
27392
|
ok: false,
|
|
@@ -26766,6 +27422,23 @@ function formatError2(err, c) {
|
|
|
26766
27422
|
}
|
|
26767
27423
|
return formatInternalErrorFallThrough(err, c);
|
|
26768
27424
|
}
|
|
27425
|
+
function formatConflict(err, c) {
|
|
27426
|
+
if (err instanceof ActionRefusedError) {
|
|
27427
|
+
const envelope = {
|
|
27428
|
+
ok: false,
|
|
27429
|
+
error: { code: err.code, message: err.message, details: err.details }
|
|
27430
|
+
};
|
|
27431
|
+
return c.json(envelope, 409);
|
|
27432
|
+
}
|
|
27433
|
+
if (err instanceof ConflictError) {
|
|
27434
|
+
const envelope = {
|
|
27435
|
+
ok: false,
|
|
27436
|
+
error: { code: err.code, message: err.message, details: null }
|
|
27437
|
+
};
|
|
27438
|
+
return c.json(envelope, 409);
|
|
27439
|
+
}
|
|
27440
|
+
return null;
|
|
27441
|
+
}
|
|
26769
27442
|
function formatInternalErrorFallThrough(err, c) {
|
|
26770
27443
|
const detail = formatErrorMessage(err);
|
|
26771
27444
|
const stack = err instanceof Error && typeof err.stack === "string" ? err.stack : void 0;
|
|
@@ -26991,6 +27664,8 @@ function entryFromRegistered(c) {
|
|
|
26991
27664
|
if (c.icon !== void 0) entry.icon = c.icon;
|
|
26992
27665
|
if (c.emptyText !== void 0) entry.emptyText = c.emptyText;
|
|
26993
27666
|
if (c.priority !== void 0) entry.priority = c.priority;
|
|
27667
|
+
if (c.pluginOrder !== void 0) entry.pluginOrder = c.pluginOrder;
|
|
27668
|
+
if (c.extensionOrder !== void 0) entry.extensionOrder = c.extensionOrder;
|
|
26994
27669
|
return entry;
|
|
26995
27670
|
}
|
|
26996
27671
|
|
|
@@ -27248,8 +27923,10 @@ async function createServer(options, extra = {}) {
|
|
|
27248
27923
|
}
|
|
27249
27924
|
async function assemblePluginRuntime(options, runtimeContext) {
|
|
27250
27925
|
const pluginRuntime = options.noPlugins ? emptyPluginRuntime() : await loadPluginRuntime({ runtimeContext });
|
|
27251
|
-
|
|
27252
|
-
|
|
27926
|
+
if (options.noWatcher) {
|
|
27927
|
+
for (const warn of pluginRuntime.warnings) {
|
|
27928
|
+
log.warn(sanitizeForTerminal(warn));
|
|
27929
|
+
}
|
|
27253
27930
|
}
|
|
27254
27931
|
const builtInProviders = options.noBuiltIns ? [] : collectBuiltInProviders();
|
|
27255
27932
|
const allProviders = [...builtInProviders, ...pluginRuntime.extensions.providers];
|
|
@@ -27260,6 +27937,11 @@ async function assemblePluginRuntime(options, runtimeContext) {
|
|
|
27260
27937
|
function assembleKernel(pluginRuntime, noBuiltIns) {
|
|
27261
27938
|
const kernel = createKernel();
|
|
27262
27939
|
kernel.setRegisteredAnnotationKeys(pluginRuntime.annotationContributions);
|
|
27940
|
+
if (!noBuiltIns) {
|
|
27941
|
+
for (const action of builtIns().actions) {
|
|
27942
|
+
kernel.registry.register(action);
|
|
27943
|
+
}
|
|
27944
|
+
}
|
|
27263
27945
|
const mergedViewContributions = [...pluginRuntime.viewContributions];
|
|
27264
27946
|
if (!noBuiltIns) {
|
|
27265
27947
|
const userKey = new Set(
|
|
@@ -28129,13 +28811,13 @@ var ShowCommand = class extends SmCommand {
|
|
|
28129
28811
|
}
|
|
28130
28812
|
};
|
|
28131
28813
|
function renderHuman2(doc, ansi) {
|
|
28132
|
-
const { node, linksOut, linksIn, issues } = doc;
|
|
28814
|
+
const { node, linksOut: linksOut2, linksIn: linksIn2, issues } = doc;
|
|
28133
28815
|
const out = [];
|
|
28134
28816
|
out.push(renderHeader(node, ansi));
|
|
28135
28817
|
out.push(renderFieldBlock(node, ansi));
|
|
28136
28818
|
out.push(renderFrontmatter(node, ansi));
|
|
28137
|
-
if (
|
|
28138
|
-
if (
|
|
28819
|
+
if (linksOut2.length > 0) out.push(renderLinksSection("out", linksOut2, ansi));
|
|
28820
|
+
if (linksIn2.length > 0) out.push(renderLinksSection("in", linksIn2, ansi));
|
|
28139
28821
|
if (issues.length > 0) out.push(renderIssuesSection(issues, node.path, ansi));
|
|
28140
28822
|
return out.join("");
|
|
28141
28823
|
}
|
|
@@ -28527,7 +29209,9 @@ var SidecarRefreshCommand = class extends SmCommand {
|
|
|
28527
29209
|
frontmatterHash: node.frontmatterHash
|
|
28528
29210
|
}
|
|
28529
29211
|
},
|
|
28530
|
-
|
|
29212
|
+
// Step 17 split: CLI accept / `--yes` persists the grant, so it
|
|
29213
|
+
// threads `always`, not the new one-shot `confirm`.
|
|
29214
|
+
{ confirm: this.yes, always: this.yes, cwd: ctx.cwd }
|
|
28531
29215
|
);
|
|
28532
29216
|
} catch (err) {
|
|
28533
29217
|
if (err instanceof EConsentRequiredError) throw err;
|
|
@@ -28808,7 +29492,9 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
28808
29492
|
await store.applyPatch(
|
|
28809
29493
|
sidecarAbsPath,
|
|
28810
29494
|
scaffoldSidecarObject(node),
|
|
28811
|
-
|
|
29495
|
+
// Step 17 split: CLI accept / `--yes` persists the grant, so it
|
|
29496
|
+
// threads `always`, not the new one-shot `confirm`.
|
|
29497
|
+
{ confirm: this.yes, always: this.yes, cwd: ctx.cwd }
|
|
28812
29498
|
);
|
|
28813
29499
|
} catch (err) {
|
|
28814
29500
|
if (err instanceof EConsentRequiredError) throw err;
|
|
@@ -29069,11 +29755,13 @@ var TUTORIAL_TEXTS = {
|
|
|
29069
29755
|
// the error shape: glyph + headline + dim hint.
|
|
29070
29756
|
notEmpty: "{{glyph}} sm tutorial: the current directory is not empty (found {{entries}})\n {{hint}}\n",
|
|
29071
29757
|
notEmptyHint: "sm tutorial seeds a self-contained scenario; run it in a fresh empty directory, or pass `--force` to use this one anyway.",
|
|
29072
|
-
//
|
|
29073
|
-
//
|
|
29074
|
-
//
|
|
29075
|
-
|
|
29076
|
-
|
|
29758
|
+
// Legacy positional argument (e.g. a stale `sm tutorial master`). The
|
|
29759
|
+
// verb no longer takes a positional: there is a single umbrella skill
|
|
29760
|
+
// and the advanced walkthrough is a menu choice inside it, not a
|
|
29761
|
+
// separate install. Goes to stderr, exit code 2. Mirrors the error
|
|
29762
|
+
// shape: glyph + headline + dim hint.
|
|
29763
|
+
legacyPositional: "{{glyph}} sm tutorial: unexpected argument '{{arg}}'\n {{hint}}\n",
|
|
29764
|
+
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.",
|
|
29077
29765
|
// I/O failure on write or on reading the bundled skill source.
|
|
29078
29766
|
writeFailed: "{{glyph}} sm tutorial: failed to write {{target}}: {{message}}\n",
|
|
29079
29767
|
sourceMissing: "{{glyph}} sm tutorial: could not read the bundled skill payload for {{target}} from the install.\n {{hint}}\n",
|
|
@@ -29081,47 +29769,36 @@ var TUTORIAL_TEXTS = {
|
|
|
29081
29769
|
};
|
|
29082
29770
|
|
|
29083
29771
|
// cli/commands/tutorial.ts
|
|
29084
|
-
var
|
|
29085
|
-
var
|
|
29086
|
-
var
|
|
29087
|
-
|
|
29088
|
-
slug: "sm-tutorial",
|
|
29089
|
-
sourceDir: ".claude/skills/sm-tutorial",
|
|
29090
|
-
triggerEn: "run the tutorial",
|
|
29091
|
-
triggerEs: "ejecuta el tutorial"
|
|
29092
|
-
},
|
|
29093
|
-
master: {
|
|
29094
|
-
slug: "sm-master",
|
|
29095
|
-
sourceDir: ".claude/skills/sm-master",
|
|
29096
|
-
triggerEn: "run the master tutorial",
|
|
29097
|
-
triggerEs: "ejecuta el tutorial maestro"
|
|
29098
|
-
}
|
|
29099
|
-
};
|
|
29772
|
+
var SKILL_SLUG = "sm-tutorial";
|
|
29773
|
+
var SKILL_SOURCE_DIR = ".claude/skills/sm-tutorial";
|
|
29774
|
+
var TRIGGER_EN = "run the tutorial";
|
|
29775
|
+
var TRIGGER_ES = "ejecuta el tutorial";
|
|
29100
29776
|
var TutorialCommand = class extends SmCommand {
|
|
29101
29777
|
static paths = [["tutorial"]];
|
|
29102
29778
|
static usage = Command37.Usage({
|
|
29103
29779
|
category: "Setup",
|
|
29104
29780
|
description: "Materialize an interactive tester tutorial as a Claude Code skill folder under `<cwd>/.claude/skills/`.",
|
|
29105
29781
|
details: `
|
|
29106
|
-
Drops the canonical skill directory (SKILL.md +
|
|
29107
|
-
sub-folder) under \`<cwd>/.claude/skills/sm-tutorial
|
|
29108
|
-
|
|
29109
|
-
|
|
29110
|
-
|
|
29111
|
-
speaking one of its trigger phrases.
|
|
29782
|
+
Drops the canonical skill directory (SKILL.md + its references/
|
|
29783
|
+
sub-folder) under \`<cwd>/.claude/skills/sm-tutorial/\`. Claude
|
|
29784
|
+
Code auto-discovers the skill the next time it boots in this
|
|
29785
|
+
directory; the tester invokes it by speaking one of its trigger
|
|
29786
|
+
phrases and picks the advanced parts from the in-skill menu.
|
|
29112
29787
|
|
|
29113
29788
|
Does NOT require an initialized .skill-map/ project. Refuses to
|
|
29114
|
-
overwrite the target directory unless --force is passed.
|
|
29115
|
-
|
|
29116
|
-
master.
|
|
29789
|
+
overwrite the target directory unless --force is passed. Takes no
|
|
29790
|
+
positional argument.
|
|
29117
29791
|
`,
|
|
29118
29792
|
examples: [
|
|
29119
|
-
["Materialize the
|
|
29120
|
-
["Materialize the advanced tutorial skill in the cwd", "$0 tutorial master"],
|
|
29793
|
+
["Materialize the tutorial skill in the cwd", "$0 tutorial"],
|
|
29121
29794
|
["Overwrite an existing target directory", "$0 tutorial --force"]
|
|
29122
29795
|
]
|
|
29123
29796
|
});
|
|
29124
|
-
|
|
29797
|
+
// Legacy positional catcher: the verb takes no positional argument any
|
|
29798
|
+
// more. Accept one so a stale `sm tutorial master` lands on a friendly
|
|
29799
|
+
// usage error (guarded in `run()`) instead of clipanion's generic
|
|
29800
|
+
// "extraneous argument" message.
|
|
29801
|
+
legacyPositional = Option35.String({ required: false });
|
|
29125
29802
|
// Named `forProvider`, NOT `for` (reserved word). The CLI surface stays
|
|
29126
29803
|
// `--for`; selects the destination Provider whose `scaffold.skillDir`
|
|
29127
29804
|
// the skill is materialised under, skipping the interactive prompt.
|
|
@@ -29137,9 +29814,16 @@ var TutorialCommand = class extends SmCommand {
|
|
|
29137
29814
|
const stderr = this.context.stderr;
|
|
29138
29815
|
const stderrAnsi = this.ansiFor("stderr");
|
|
29139
29816
|
const errGlyph = stderrAnsi.red("\u2715");
|
|
29140
|
-
|
|
29141
|
-
|
|
29142
|
-
|
|
29817
|
+
if (this.legacyPositional !== void 0) {
|
|
29818
|
+
this.printer.error(
|
|
29819
|
+
tx(TUTORIAL_TEXTS.legacyPositional, {
|
|
29820
|
+
glyph: errGlyph,
|
|
29821
|
+
arg: this.legacyPositional,
|
|
29822
|
+
hint: stderrAnsi.dim(TUTORIAL_TEXTS.legacyPositionalHint)
|
|
29823
|
+
})
|
|
29824
|
+
);
|
|
29825
|
+
return ExitCode.Error;
|
|
29826
|
+
}
|
|
29143
29827
|
if (!this.force && !isDirEmpty(ctx.cwd)) {
|
|
29144
29828
|
this.printer.error(
|
|
29145
29829
|
tx(TUTORIAL_TEXTS.notEmpty, {
|
|
@@ -29153,11 +29837,11 @@ var TutorialCommand = class extends SmCommand {
|
|
|
29153
29837
|
const targets = listScaffoldTargets();
|
|
29154
29838
|
const target = await this.resolveScaffoldTarget(targets, stderrAnsi, errGlyph);
|
|
29155
29839
|
if (target === null) return ExitCode.Error;
|
|
29156
|
-
const targetDir = join21(ctx.cwd, target.skillDir,
|
|
29157
|
-
const targetDisplay = `${target.skillDir}/${
|
|
29840
|
+
const targetDir = join21(ctx.cwd, target.skillDir, SKILL_SLUG);
|
|
29841
|
+
const targetDisplay = `${target.skillDir}/${SKILL_SLUG}/`;
|
|
29158
29842
|
let sourceDir;
|
|
29159
29843
|
try {
|
|
29160
|
-
sourceDir = resolveSkillSourceDir(
|
|
29844
|
+
sourceDir = resolveSkillSourceDir();
|
|
29161
29845
|
} catch {
|
|
29162
29846
|
this.printer.error(
|
|
29163
29847
|
tx(TUTORIAL_TEXTS.sourceMissing, {
|
|
@@ -29192,38 +29876,18 @@ var TutorialCommand = class extends SmCommand {
|
|
|
29192
29876
|
this.printer.data(
|
|
29193
29877
|
tx(TUTORIAL_TEXTS.written, {
|
|
29194
29878
|
glyph: ansi.green("\u2713"),
|
|
29195
|
-
slug:
|
|
29879
|
+
slug: SKILL_SLUG,
|
|
29196
29880
|
target: targetDisplay,
|
|
29197
29881
|
provider: ansi.dim(target.label),
|
|
29198
29882
|
cwd: ansi.dim(displayCwd(ctx.cwd)),
|
|
29199
29883
|
enLabel: ansi.dim(TUTORIAL_TEXTS.writtenLabelEn),
|
|
29200
29884
|
esLabel: ansi.dim(TUTORIAL_TEXTS.writtenLabelEs),
|
|
29201
|
-
enTrigger:
|
|
29202
|
-
esTrigger:
|
|
29885
|
+
enTrigger: TRIGGER_EN,
|
|
29886
|
+
esTrigger: TRIGGER_ES
|
|
29203
29887
|
})
|
|
29204
29888
|
);
|
|
29205
29889
|
return ExitCode.Ok;
|
|
29206
29890
|
}
|
|
29207
|
-
/**
|
|
29208
|
-
* Validate the positional `variant` arg against the closed catalog.
|
|
29209
|
-
* Returns the resolved variant, or `null` after printing the
|
|
29210
|
-
* `invalidVariant` error (caller exits non-zero). Extracted from
|
|
29211
|
-
* `run()` to keep its cyclomatic complexity within the lint budget.
|
|
29212
|
-
*/
|
|
29213
|
-
resolveVariantArg(errGlyph, stderrAnsi) {
|
|
29214
|
-
const rawVariant = this.variant;
|
|
29215
|
-
if (rawVariant !== void 0 && !isTutorialVariant(rawVariant)) {
|
|
29216
|
-
this.printer.error(
|
|
29217
|
-
tx(TUTORIAL_TEXTS.invalidVariant, {
|
|
29218
|
-
glyph: errGlyph,
|
|
29219
|
-
variant: rawVariant,
|
|
29220
|
-
hint: stderrAnsi.dim(TUTORIAL_TEXTS.invalidVariantHint)
|
|
29221
|
-
})
|
|
29222
|
-
);
|
|
29223
|
-
return null;
|
|
29224
|
-
}
|
|
29225
|
-
return rawVariant ?? DEFAULT_VARIANT;
|
|
29226
|
-
}
|
|
29227
29891
|
/**
|
|
29228
29892
|
* Resolve the destination Provider. Precedence:
|
|
29229
29893
|
* 1. `--for <id>` (validated against the scaffold-capable catalog).
|
|
@@ -29283,9 +29947,6 @@ var TutorialCommand = class extends SmCommand {
|
|
|
29283
29947
|
return picked;
|
|
29284
29948
|
}
|
|
29285
29949
|
};
|
|
29286
|
-
function isTutorialVariant(value) {
|
|
29287
|
-
return VALID_VARIANTS.includes(value);
|
|
29288
|
-
}
|
|
29289
29950
|
function toScaffoldTarget(provider) {
|
|
29290
29951
|
const scaffold = provider.scaffold;
|
|
29291
29952
|
if (!scaffold || !scaffold.skillDir) return null;
|
|
@@ -29352,23 +30013,21 @@ function listCwdEntries(dir) {
|
|
|
29352
30013
|
const more = entries.length > shown.length ? ", ..." : "";
|
|
29353
30014
|
return shown.join(", ") + more;
|
|
29354
30015
|
}
|
|
29355
|
-
var
|
|
29356
|
-
function resolveSkillSourceDir(
|
|
29357
|
-
|
|
29358
|
-
if (cached !== void 0) return cached;
|
|
29359
|
-
const spec = VARIANT_SPECS[variant];
|
|
30016
|
+
var cachedSourceDir;
|
|
30017
|
+
function resolveSkillSourceDir() {
|
|
30018
|
+
if (cachedSourceDir !== void 0) return cachedSourceDir;
|
|
29360
30019
|
const here = dirname20(fileURLToPath7(import.meta.url));
|
|
29361
30020
|
const candidates = [
|
|
29362
|
-
// dev: src/cli/commands/ → repo-root .claude/skills
|
|
29363
|
-
resolve39(here, "../../..",
|
|
29364
|
-
// bundled: dist/cli.js → dist/cli/tutorial
|
|
29365
|
-
resolve39(here, "cli/tutorial",
|
|
29366
|
-
// bundled fallback: any-depth → cli/tutorial
|
|
29367
|
-
resolve39(here, "../cli/tutorial",
|
|
30021
|
+
// dev: src/cli/commands/ → repo-root .claude/skills/sm-tutorial/
|
|
30022
|
+
resolve39(here, "../../..", SKILL_SOURCE_DIR),
|
|
30023
|
+
// bundled: dist/cli.js → dist/cli/tutorial/sm-tutorial (sibling)
|
|
30024
|
+
resolve39(here, "cli/tutorial", SKILL_SLUG),
|
|
30025
|
+
// bundled fallback: any-depth → cli/tutorial/sm-tutorial
|
|
30026
|
+
resolve39(here, "../cli/tutorial", SKILL_SLUG)
|
|
29368
30027
|
];
|
|
29369
30028
|
for (const candidate of candidates) {
|
|
29370
30029
|
if (existsSync32(candidate) && statSync11(candidate).isDirectory()) {
|
|
29371
|
-
|
|
30030
|
+
cachedSourceDir = candidate;
|
|
29372
30031
|
return candidate;
|
|
29373
30032
|
}
|
|
29374
30033
|
}
|
|
@@ -29592,4 +30251,4 @@ function resolveBareDefault() {
|
|
|
29592
30251
|
process.exit(ExitCode.Error);
|
|
29593
30252
|
}
|
|
29594
30253
|
//# sourceMappingURL=cli.js.map
|
|
29595
|
-
//# debugId=
|
|
30254
|
+
//# debugId=dd2e1877-e17a-5d0b-86e5-6416814d7a78
|