@skill-map/cli 0.55.0 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1075 -501
- package/dist/index.js +24 -4
- package/dist/kernel/index.d.ts +42 -3
- package/dist/kernel/index.js +24 -4
- package/dist/ui/chunk-4ITL7E6U.js +1 -0
- package/dist/ui/chunk-DWBJCNC7.js +2 -0
- package/dist/ui/{chunk-LREXXX7T.js → chunk-GHOVZAAV.js} +1 -1
- package/dist/ui/{chunk-GBKHMJ4B.js → chunk-H6O2DYVT.js} +13 -13
- package/dist/ui/chunk-HDKR6XHG.js +917 -0
- package/dist/ui/{chunk-GEI6INVH.js → chunk-JA4Z74I3.js} +1 -1
- package/dist/ui/chunk-RS3ANRT5.js +1 -0
- package/dist/ui/chunk-VUNP5KNI.js +3 -0
- package/dist/ui/chunk-W3Z3CZL4.js +1844 -0
- package/dist/ui/chunk-YHJL5LP3.js +913 -0
- package/dist/ui/index.html +2 -2
- package/dist/ui/{main-7YXBWYHE.js → main-PL3BEVQI.js} +3 -3
- package/dist/ui/{styles-HRJG67XW.css → styles-RHEEXRHQ.css} +1 -1
- package/package.json +2 -2
- package/dist/ui/chunk-CN6IOM67.js +0 -2
- package/dist/ui/chunk-HPKRDGLH.js +0 -123
- package/dist/ui/chunk-JXRIGHET.js +0 -552
- package/dist/ui/chunk-RGB5FBZL.js +0 -2205
- package/dist/ui/chunk-WFLPMCK4.js +0 -392
- package/dist/ui/chunk-XAM6VKXM.js +0 -1
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]="49fdc2af-2694-5cd0-ac19-33f8d1dffa9e")}catch(e){}}();
|
|
4
4
|
import { existsSync as existsSync33 } from "fs";
|
|
5
5
|
import { Builtins, Cli as Cli2 } from "clipanion";
|
|
6
6
|
|
|
@@ -197,6 +197,10 @@ function matchesFilter(hook, event) {
|
|
|
197
197
|
function buildHookContext(_hook, trigger, event) {
|
|
198
198
|
const data = event.data ?? {};
|
|
199
199
|
const ctx = {
|
|
200
|
+
// `settings` is always populated (possibly empty) so hooks can read
|
|
201
|
+
// `ctx.settings.<id>` without a presence check. The composer
|
|
202
|
+
// populated `resolvedSettings` on each composed hook.
|
|
203
|
+
settings: _hook.resolvedSettings ?? {},
|
|
200
204
|
event: {
|
|
201
205
|
type: trigger,
|
|
202
206
|
timestamp: event.timestamp,
|
|
@@ -246,7 +250,7 @@ function bucketByKind(kind, instance, bag) {
|
|
|
246
250
|
// package.json
|
|
247
251
|
var package_default = {
|
|
248
252
|
name: "@skill-map/cli",
|
|
249
|
-
version: "0.
|
|
253
|
+
version: "0.56.0",
|
|
250
254
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
251
255
|
license: "MIT",
|
|
252
256
|
type: "module",
|
|
@@ -567,6 +571,41 @@ var ANTIGRAVITY_PLUGIN_ID = "antigravity";
|
|
|
567
571
|
var AGENT_SKILLS_PLUGIN_ID = "agent-skills";
|
|
568
572
|
|
|
569
573
|
// plugins/claude/providers/claude/index.ts
|
|
574
|
+
var RESERVED_SLASH_NAMES = [
|
|
575
|
+
"help",
|
|
576
|
+
"clear",
|
|
577
|
+
"compact",
|
|
578
|
+
"cost",
|
|
579
|
+
"init",
|
|
580
|
+
"model",
|
|
581
|
+
"agents",
|
|
582
|
+
"login",
|
|
583
|
+
"logout",
|
|
584
|
+
"mcp",
|
|
585
|
+
"memory",
|
|
586
|
+
"config",
|
|
587
|
+
"doctor",
|
|
588
|
+
"permissions",
|
|
589
|
+
"add-dir",
|
|
590
|
+
"bug",
|
|
591
|
+
"pr-comments",
|
|
592
|
+
"release-notes",
|
|
593
|
+
"review",
|
|
594
|
+
"terminal-setup",
|
|
595
|
+
"vim",
|
|
596
|
+
"output-style",
|
|
597
|
+
"hooks",
|
|
598
|
+
"install-github-app",
|
|
599
|
+
"migrate-installer",
|
|
600
|
+
"upgrade",
|
|
601
|
+
"resume",
|
|
602
|
+
"exit",
|
|
603
|
+
"quit",
|
|
604
|
+
"security-review",
|
|
605
|
+
"statusline",
|
|
606
|
+
"usage",
|
|
607
|
+
"feedback"
|
|
608
|
+
];
|
|
570
609
|
var claudeProvider = {
|
|
571
610
|
id: "claude",
|
|
572
611
|
pluginId: CLAUDE_PLUGIN_ID,
|
|
@@ -718,53 +757,18 @@ var claudeProvider = {
|
|
|
718
757
|
// declaring one of these (e.g. `.claude/commands/help.md`) is
|
|
719
758
|
// silently shadowed: the runtime runs the built-in instead.
|
|
720
759
|
//
|
|
721
|
-
// - `command
|
|
722
|
-
//
|
|
723
|
-
//
|
|
724
|
-
//
|
|
725
|
-
//
|
|
726
|
-
// reflect
|
|
727
|
-
// - `agent`: built-in agents Anthropic ships with the CLI
|
|
760
|
+
// - `command` / `skill`: both reserve `RESERVED_SLASH_NAMES` (defined
|
|
761
|
+
// above). Per the resolution matrix they share the `/` invocation
|
|
762
|
+
// namespace, so a built-in slash command shadows a user node of
|
|
763
|
+
// EITHER kind that claims its name (a `skill` named `help` is as
|
|
764
|
+
// unreachable as a `command` named `help`). The catalog is API
|
|
765
|
+
// surface users rely on the analyzer to reflect.
|
|
766
|
+
// - `agent`: built-in agents Anthropic ships with the CLI, invoked
|
|
767
|
+
// through the `@` / Task namespace (separate from `/`). Smaller
|
|
728
768
|
// surface than commands today.
|
|
729
|
-
// - `skill`: no built-in skills today (skills are user-defined and
|
|
730
|
-
// discovered from disk); the key is omitted on purpose, defaulting
|
|
731
|
-
// to no reserved names for the kind.
|
|
732
769
|
reservedNames: {
|
|
733
|
-
command:
|
|
734
|
-
|
|
735
|
-
"clear",
|
|
736
|
-
"compact",
|
|
737
|
-
"cost",
|
|
738
|
-
"init",
|
|
739
|
-
"model",
|
|
740
|
-
"agents",
|
|
741
|
-
"login",
|
|
742
|
-
"logout",
|
|
743
|
-
"mcp",
|
|
744
|
-
"memory",
|
|
745
|
-
"config",
|
|
746
|
-
"doctor",
|
|
747
|
-
"permissions",
|
|
748
|
-
"add-dir",
|
|
749
|
-
"bug",
|
|
750
|
-
"pr-comments",
|
|
751
|
-
"release-notes",
|
|
752
|
-
"review",
|
|
753
|
-
"terminal-setup",
|
|
754
|
-
"vim",
|
|
755
|
-
"output-style",
|
|
756
|
-
"hooks",
|
|
757
|
-
"install-github-app",
|
|
758
|
-
"migrate-installer",
|
|
759
|
-
"upgrade",
|
|
760
|
-
"resume",
|
|
761
|
-
"exit",
|
|
762
|
-
"quit",
|
|
763
|
-
"security-review",
|
|
764
|
-
"statusline",
|
|
765
|
-
"usage",
|
|
766
|
-
"feedback"
|
|
767
|
-
],
|
|
770
|
+
command: RESERVED_SLASH_NAMES,
|
|
771
|
+
skill: RESERVED_SLASH_NAMES,
|
|
768
772
|
agent: [
|
|
769
773
|
"general-purpose",
|
|
770
774
|
"output-style-setup",
|
|
@@ -1564,6 +1568,15 @@ var count2 = {
|
|
|
1564
1568
|
emitWhenEmpty: false,
|
|
1565
1569
|
priority: 30
|
|
1566
1570
|
};
|
|
1571
|
+
var SETTING_IGNORED_DOMAINS = "ignored-domains";
|
|
1572
|
+
var settings = {
|
|
1573
|
+
[SETTING_IGNORED_DOMAINS]: {
|
|
1574
|
+
type: "string-list",
|
|
1575
|
+
label: "Ignored domains",
|
|
1576
|
+
description: "Hostnames to exclude from the external-URL count (e.g. internal mirrors, link shorteners).",
|
|
1577
|
+
default: []
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1567
1580
|
var URL_RE = /https?:\/\/[^\s<>"'`)\]]+/g;
|
|
1568
1581
|
var TRAILING_PUNCT = /[.,;:!?]+$/;
|
|
1569
1582
|
var externalUrlCounterExtractor = {
|
|
@@ -1572,6 +1585,14 @@ var externalUrlCounterExtractor = {
|
|
|
1572
1585
|
kind: "extractor",
|
|
1573
1586
|
description: "Counts the distinct external URLs in a node's body and shows the count on the card. Example: a body linking `https://example.com` and `https://docs.rs` shows a count of 2.",
|
|
1574
1587
|
scope: "body",
|
|
1588
|
+
/**
|
|
1589
|
+
* Operator-configurable hostnames to exclude from the count. A URL
|
|
1590
|
+
* whose normalized `hostname` is in this list is skipped entirely:
|
|
1591
|
+
* no Signal, no contribution increment. Default empty (count every
|
|
1592
|
+
* external URL). The operator sets it via
|
|
1593
|
+
* `sm plugins config core/external-url-counter ignored-domains '["…"]'`.
|
|
1594
|
+
*/
|
|
1595
|
+
settings,
|
|
1575
1596
|
/**
|
|
1576
1597
|
* Phase 6 / View contribution system, surface the distinct-URL
|
|
1577
1598
|
* count as a card-footer-left chip alongside the in/out link
|
|
@@ -1591,6 +1612,7 @@ var externalUrlCounterExtractor = {
|
|
|
1591
1612
|
ui: { count: count2 },
|
|
1592
1613
|
extract(ctx) {
|
|
1593
1614
|
const seen = /* @__PURE__ */ new Set();
|
|
1615
|
+
const ignoredDomains = readIgnoredDomains(ctx.settings[SETTING_IGNORED_DOMAINS]);
|
|
1594
1616
|
const body = stripCodeBlocks(ctx.body);
|
|
1595
1617
|
const lineStarts = computeLineStarts(body);
|
|
1596
1618
|
for (const match of body.matchAll(URL_RE)) {
|
|
@@ -1598,8 +1620,9 @@ var externalUrlCounterExtractor = {
|
|
|
1598
1620
|
if (original.length === 0) continue;
|
|
1599
1621
|
const normalized = normalizeUrl(original);
|
|
1600
1622
|
if (normalized === null) continue;
|
|
1601
|
-
if (
|
|
1602
|
-
seen.
|
|
1623
|
+
if (ignoredDomains.has(normalized.host)) continue;
|
|
1624
|
+
if (seen.has(normalized.href)) continue;
|
|
1625
|
+
seen.add(normalized.href);
|
|
1603
1626
|
const offset = match.index ?? 0;
|
|
1604
1627
|
const line = lineFor(lineStarts, offset);
|
|
1605
1628
|
ctx.emitSignal({
|
|
@@ -1611,12 +1634,12 @@ var externalUrlCounterExtractor = {
|
|
|
1611
1634
|
{
|
|
1612
1635
|
extractorId: ID6,
|
|
1613
1636
|
kind: "references",
|
|
1614
|
-
target: normalized,
|
|
1637
|
+
target: normalized.href,
|
|
1615
1638
|
confidence: 0.3,
|
|
1616
1639
|
rationale: "external URL pseudo-link, counted then dropped",
|
|
1617
1640
|
trigger: {
|
|
1618
1641
|
originalTrigger: original,
|
|
1619
|
-
normalizedTrigger: normalized
|
|
1642
|
+
normalizedTrigger: normalized.href
|
|
1620
1643
|
}
|
|
1621
1644
|
}
|
|
1622
1645
|
]
|
|
@@ -1630,12 +1653,20 @@ var externalUrlCounterExtractor = {
|
|
|
1630
1653
|
function stripTrailingPunctuation(raw) {
|
|
1631
1654
|
return raw.replace(TRAILING_PUNCT, "");
|
|
1632
1655
|
}
|
|
1656
|
+
function readIgnoredDomains(value) {
|
|
1657
|
+
if (!Array.isArray(value)) return /* @__PURE__ */ new Set();
|
|
1658
|
+
const out = /* @__PURE__ */ new Set();
|
|
1659
|
+
for (const entry of value) {
|
|
1660
|
+
if (typeof entry === "string" && entry.length > 0) out.add(entry.toLowerCase());
|
|
1661
|
+
}
|
|
1662
|
+
return out;
|
|
1663
|
+
}
|
|
1633
1664
|
function normalizeUrl(raw) {
|
|
1634
1665
|
try {
|
|
1635
1666
|
const url = new URL(raw);
|
|
1636
1667
|
url.hostname = url.hostname.toLowerCase();
|
|
1637
1668
|
url.hash = "";
|
|
1638
|
-
return url.href;
|
|
1669
|
+
return { href: url.href, host: url.hostname };
|
|
1639
1670
|
} catch {
|
|
1640
1671
|
return null;
|
|
1641
1672
|
}
|
|
@@ -2222,7 +2253,7 @@ var ID15 = "link-conflict";
|
|
|
2222
2253
|
var NON_CONFLICTING_KINDS = /* @__PURE__ */ new Set(["points"]);
|
|
2223
2254
|
var linkConflictAnalyzer = {
|
|
2224
2255
|
id: ID15,
|
|
2225
|
-
pluginId:
|
|
2256
|
+
pluginId: CORE_PLUGIN_ID,
|
|
2226
2257
|
kind: "analyzer",
|
|
2227
2258
|
description: "Flags conflicting arrow meanings between extractors (e.g. `references` vs `invokes`).",
|
|
2228
2259
|
mode: "deterministic",
|
|
@@ -2287,52 +2318,30 @@ function rankConfidence(c) {
|
|
|
2287
2318
|
return c;
|
|
2288
2319
|
}
|
|
2289
2320
|
|
|
2290
|
-
// kernel/util/
|
|
2291
|
-
function
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
return out;
|
|
2296
|
-
}
|
|
2297
|
-
function indexByCanonicalName(nodes, out) {
|
|
2298
|
-
for (const node of nodes) {
|
|
2299
|
-
const raw = canonicalName(node);
|
|
2300
|
-
if (raw === null) continue;
|
|
2301
|
-
const key = normalizeTrigger(raw);
|
|
2302
|
-
if (!out.has(key)) out.set(key, node.path);
|
|
2303
|
-
}
|
|
2321
|
+
// kernel/util/link-lines.ts
|
|
2322
|
+
function isSelfLoop(link) {
|
|
2323
|
+
if (link.source === link.target) return true;
|
|
2324
|
+
if (link.resolvedTarget && link.source === link.resolvedTarget) return true;
|
|
2325
|
+
return false;
|
|
2304
2326
|
}
|
|
2305
|
-
function
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
const
|
|
2309
|
-
if (
|
|
2310
|
-
const key = normalizeTrigger(derived);
|
|
2311
|
-
if (!out.has(key)) out.set(key, node.path);
|
|
2327
|
+
function linkLines(link) {
|
|
2328
|
+
const lines = /* @__PURE__ */ new Set();
|
|
2329
|
+
for (const occ of link.occurrences ?? []) {
|
|
2330
|
+
const line = occ.location?.line;
|
|
2331
|
+
if (typeof line === "number") lines.add(line);
|
|
2312
2332
|
}
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
if (typeof raw !== "string" || raw.length === 0) return null;
|
|
2317
|
-
return raw;
|
|
2318
|
-
}
|
|
2319
|
-
function pathBasenameForLink(path) {
|
|
2320
|
-
const segments = path.split("/").filter((s) => s.length > 0);
|
|
2321
|
-
if (segments.length === 0) return path;
|
|
2322
|
-
const last = segments[segments.length - 1];
|
|
2323
|
-
if (last === "SKILL.md" && segments.length >= 2) {
|
|
2324
|
-
return segments[segments.length - 2];
|
|
2333
|
+
if (lines.size === 0) {
|
|
2334
|
+
const line = link.location?.line;
|
|
2335
|
+
if (typeof line === "number") lines.add(line);
|
|
2325
2336
|
}
|
|
2326
|
-
return
|
|
2337
|
+
return [...lines].sort((a, b) => a - b);
|
|
2327
2338
|
}
|
|
2328
|
-
function
|
|
2329
|
-
const
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
const resolved = nameIndex.get(normalized);
|
|
2335
|
-
return resolved ?? raw;
|
|
2339
|
+
function linkWhere(link, texts) {
|
|
2340
|
+
const lines = linkLines(link);
|
|
2341
|
+
if (lines.length === 0) return "";
|
|
2342
|
+
return tx(lines.length === 1 ? texts.single : texts.plural, {
|
|
2343
|
+
lines: lines.join(", ")
|
|
2344
|
+
});
|
|
2336
2345
|
}
|
|
2337
2346
|
|
|
2338
2347
|
// plugins/core/analyzers/link-counter/index.ts
|
|
@@ -2359,13 +2368,12 @@ var linkCounterAnalyzer = {
|
|
|
2359
2368
|
mode: "deterministic",
|
|
2360
2369
|
ui: { linksIn, linksOut },
|
|
2361
2370
|
evaluate(ctx) {
|
|
2362
|
-
const nameIndex = buildNameIndex(ctx.nodes);
|
|
2363
2371
|
const perTarget = /* @__PURE__ */ new Map();
|
|
2364
2372
|
const perSource = /* @__PURE__ */ new Map();
|
|
2365
2373
|
for (const link of ctx.links) {
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
bump(perTarget,
|
|
2374
|
+
if (isSelfLoop(link)) continue;
|
|
2375
|
+
const target = link.resolvedTarget ?? link.target;
|
|
2376
|
+
bump(perTarget, target, link.kind);
|
|
2369
2377
|
bump(perSource, link.source, link.kind);
|
|
2370
2378
|
}
|
|
2371
2379
|
for (const node of ctx.nodes) {
|
|
@@ -2399,27 +2407,6 @@ function formatBreakdown(byKind, direction) {
|
|
|
2399
2407
|
return [direction, ...lines].join("\n");
|
|
2400
2408
|
}
|
|
2401
2409
|
|
|
2402
|
-
// kernel/util/link-lines.ts
|
|
2403
|
-
function linkLines(link) {
|
|
2404
|
-
const lines = /* @__PURE__ */ new Set();
|
|
2405
|
-
for (const occ of link.occurrences ?? []) {
|
|
2406
|
-
const line = occ.location?.line;
|
|
2407
|
-
if (typeof line === "number") lines.add(line);
|
|
2408
|
-
}
|
|
2409
|
-
if (lines.size === 0) {
|
|
2410
|
-
const line = link.location?.line;
|
|
2411
|
-
if (typeof line === "number") lines.add(line);
|
|
2412
|
-
}
|
|
2413
|
-
return [...lines].sort((a, b) => a - b);
|
|
2414
|
-
}
|
|
2415
|
-
function linkWhere(link, texts) {
|
|
2416
|
-
const lines = linkLines(link);
|
|
2417
|
-
if (lines.length === 0) return "";
|
|
2418
|
-
return tx(lines.length === 1 ? texts.single : texts.plural, {
|
|
2419
|
-
lines: lines.join(", ")
|
|
2420
|
-
});
|
|
2421
|
-
}
|
|
2422
|
-
|
|
2423
2410
|
// plugins/core/analyzers/link-self-loop/text.ts
|
|
2424
2411
|
var LINK_SELF_LOOP_TEXTS = {
|
|
2425
2412
|
/**
|
|
@@ -2465,22 +2452,13 @@ var linkSelfLoopAnalyzer = {
|
|
|
2465
2452
|
data: {
|
|
2466
2453
|
target: link.target,
|
|
2467
2454
|
resolvedTarget: link.resolvedTarget ?? link.target,
|
|
2468
|
-
kind: link.kind
|
|
2469
|
-
// Mark explicitly so UI / downstream consumers can read this
|
|
2470
|
-
// single field instead of re-computing the `source === target`
|
|
2471
|
-
// predicate themselves.
|
|
2472
|
-
selfLoop: true
|
|
2455
|
+
kind: link.kind
|
|
2473
2456
|
}
|
|
2474
2457
|
});
|
|
2475
2458
|
}
|
|
2476
2459
|
return issues;
|
|
2477
2460
|
}
|
|
2478
2461
|
};
|
|
2479
|
-
function isSelfLoop(link) {
|
|
2480
|
-
if (link.source === link.target) return true;
|
|
2481
|
-
if (link.resolvedTarget && link.source === link.resolvedTarget) return true;
|
|
2482
|
-
return false;
|
|
2483
|
-
}
|
|
2484
2462
|
|
|
2485
2463
|
// kernel/orchestrator/node-identifiers.ts
|
|
2486
2464
|
import { posix as pathPosix4 } from "path";
|
|
@@ -2530,6 +2508,15 @@ function liftResolvedLinkConfidence(links, nodes, ctx) {
|
|
|
2530
2508
|
if (link.confidence < 1) applyResolution(link, indexes, ctx);
|
|
2531
2509
|
}
|
|
2532
2510
|
}
|
|
2511
|
+
function collectBrokenLinks(links, nodes, ctx) {
|
|
2512
|
+
const broken = /* @__PURE__ */ new Set();
|
|
2513
|
+
if (links.length === 0) return broken;
|
|
2514
|
+
const indexes = buildIndexes(nodes, ctx);
|
|
2515
|
+
for (const link of links) {
|
|
2516
|
+
if (isGenuinelyBroken(link, indexes)) broken.add(link);
|
|
2517
|
+
}
|
|
2518
|
+
return broken;
|
|
2519
|
+
}
|
|
2533
2520
|
function applyResolution(link, indexes, ctx) {
|
|
2534
2521
|
const resolution = resolve2(link, indexes, ctx);
|
|
2535
2522
|
if (resolution === "none") {
|
|
@@ -2606,7 +2593,7 @@ var NAME_RESERVED_TEXTS = {
|
|
|
2606
2593
|
* a runtime built-in. Same wording skill-map shipped before the
|
|
2607
2594
|
* source-side link finding landed.
|
|
2608
2595
|
*/
|
|
2609
|
-
message: "
|
|
2596
|
+
message: "Name collision: this {{kind}} name is already used by the {{provider}} runtime built-in, which shadows this file. Rename the file or its `frontmatter.name`.",
|
|
2610
2597
|
/**
|
|
2611
2598
|
* Source-side message: emitted on the node that AUTHORED a link
|
|
2612
2599
|
* whose target resolves to a reserved name. Explains WHY the link's
|
|
@@ -2614,7 +2601,7 @@ var NAME_RESERVED_TEXTS = {
|
|
|
2614
2601
|
* the kernel saw the target match a runtime built-in and downgraded
|
|
2615
2602
|
* the edge so the operator notices.
|
|
2616
2603
|
*/
|
|
2617
|
-
linkMessage: "{{target}}:\
|
|
2604
|
+
linkMessage: "{{target}}:\nName collision: resolves to a {{provider}} built-in ({{reservedKind}} `{{reservedPath}}`){{where}}; the built-in wins, so this edge drops to confidence {{confidence}}. Rename the target file or its `frontmatter.name`.",
|
|
2618
2605
|
/** Location suffix after the built-in parens, one detection site. */
|
|
2619
2606
|
whereSingle: " (line {{lines}})",
|
|
2620
2607
|
/** Location suffix after the built-in parens, several detection sites. */
|
|
@@ -2653,7 +2640,9 @@ var nameReservedAnalyzer = {
|
|
|
2653
2640
|
}
|
|
2654
2641
|
for (const link of ctx.links) {
|
|
2655
2642
|
if (link.confidence !== RESERVED_TARGET_CONFIDENCE) continue;
|
|
2656
|
-
const
|
|
2643
|
+
const reservedPath = link.resolvedTarget;
|
|
2644
|
+
if (!reservedPath || !reserved.has(reservedPath)) continue;
|
|
2645
|
+
const reservedNode = byPath3.get(reservedPath);
|
|
2657
2646
|
if (!reservedNode) continue;
|
|
2658
2647
|
issues.push({
|
|
2659
2648
|
analyzerId: ID18,
|
|
@@ -2686,41 +2675,6 @@ function linkWhereSuffix(link) {
|
|
|
2686
2675
|
plural: NAME_RESERVED_TEXTS.wherePlural
|
|
2687
2676
|
});
|
|
2688
2677
|
}
|
|
2689
|
-
function findReservedNodeForLink(link, reserved, byPath3) {
|
|
2690
|
-
if (reserved.has(link.target)) {
|
|
2691
|
-
const node = byPath3.get(link.target);
|
|
2692
|
-
if (node) return node;
|
|
2693
|
-
}
|
|
2694
|
-
const trigger = link.trigger?.normalizedTrigger;
|
|
2695
|
-
if (!trigger) return null;
|
|
2696
|
-
const stripped = trigger.replace(/^[/@]/, "").trim();
|
|
2697
|
-
if (stripped.length === 0) return null;
|
|
2698
|
-
for (const path of reserved) {
|
|
2699
|
-
const node = byPath3.get(path);
|
|
2700
|
-
if (!node) continue;
|
|
2701
|
-
if (matchesNodeIdentifier(node, stripped)) return node;
|
|
2702
|
-
}
|
|
2703
|
-
return null;
|
|
2704
|
-
}
|
|
2705
|
-
function matchesNodeIdentifier(node, stripped) {
|
|
2706
|
-
const candidates = [];
|
|
2707
|
-
const fmName = node.frontmatter?.["name"];
|
|
2708
|
-
if (typeof fmName === "string" && fmName.length > 0) candidates.push(normaliseId(fmName));
|
|
2709
|
-
const basename = node.path.split("/").pop() ?? "";
|
|
2710
|
-
if (basename) {
|
|
2711
|
-
const stem = basename.replace(/\.[^.]+$/, "");
|
|
2712
|
-
if (stem) candidates.push(normaliseId(stem));
|
|
2713
|
-
}
|
|
2714
|
-
const segs = node.path.split("/");
|
|
2715
|
-
if (segs.length >= 2) {
|
|
2716
|
-
const dirBase = segs[segs.length - 2];
|
|
2717
|
-
if (dirBase) candidates.push(normaliseId(dirBase));
|
|
2718
|
-
}
|
|
2719
|
-
return candidates.includes(stripped);
|
|
2720
|
-
}
|
|
2721
|
-
function normaliseId(raw) {
|
|
2722
|
-
return raw.normalize("NFD").replace(new RegExp("\\p{Mn}+", "gu"), "").toLowerCase().replace(/[-_\s]+/g, " ").replace(/ +/g, " ").trim();
|
|
2723
|
-
}
|
|
2724
2678
|
|
|
2725
2679
|
// plugins/core/analyzers/node-stability/text.ts
|
|
2726
2680
|
var NODE_STABILITY_TEXTS = {
|
|
@@ -2891,7 +2845,7 @@ function pickSupersededBy(node) {
|
|
|
2891
2845
|
}
|
|
2892
2846
|
|
|
2893
2847
|
// plugins/core/analyzers/reference-broken/index.ts
|
|
2894
|
-
import {
|
|
2848
|
+
import { resolve as resolve3 } from "path";
|
|
2895
2849
|
|
|
2896
2850
|
// plugins/core/analyzers/reference-broken/text.ts
|
|
2897
2851
|
var REFERENCE_BROKEN_TEXTS = {
|
|
@@ -2922,13 +2876,7 @@ var REFERENCE_BROKEN_TEXTS = {
|
|
|
2922
2876
|
// Tooltips for the per-node view-contribution badges. Singular vs
|
|
2923
2877
|
// plural keeps the count grammar correct without a sub-template.
|
|
2924
2878
|
alertTooltipSingle: "This node has a broken reference. Open the inspector for details.",
|
|
2925
|
-
alertTooltipMany: "This node has {{count}} broken references. Open the inspector for details."
|
|
2926
|
-
// Fix-summary copy when the broken trigger has a same-named file on
|
|
2927
|
-
// disk that does not advertise `name:` in its frontmatter. Two
|
|
2928
|
-
// variants for single vs multiple candidates; same template family
|
|
2929
|
-
// as the alert tooltips above.
|
|
2930
|
-
hintSummarySingle: "Add `name: {{name}}` to the frontmatter of {{candidate}} so this reference resolves.",
|
|
2931
|
-
hintSummaryMany: "Add `name: {{name}}` to the frontmatter of one of these files so this reference resolves: {{candidates}}."
|
|
2879
|
+
alertTooltipMany: "This node has {{count}} broken references. Open the inspector for details."
|
|
2932
2880
|
};
|
|
2933
2881
|
|
|
2934
2882
|
// plugins/core/analyzers/reference-broken/index.ts
|
|
@@ -2944,33 +2892,31 @@ var referenceBrokenAnalyzer = {
|
|
|
2944
2892
|
// aggregate severity counters now owned by `core/issue-counter`. The
|
|
2945
2893
|
// detection logic stays intact, only the chip emission is gone.
|
|
2946
2894
|
ui: {},
|
|
2947
|
-
//
|
|
2948
|
-
//
|
|
2949
|
-
//
|
|
2950
|
-
//
|
|
2951
|
-
// emission) moved into
|
|
2895
|
+
// Pure projector of the orchestrator's genuinely-broken verdict
|
|
2896
|
+
// (`ctx.brokenLinks`, computed once from the same name index the
|
|
2897
|
+
// confidence lift uses), with the reference-paths escape hatch layered
|
|
2898
|
+
// on top. The per-source aggregation that historically lived alongside
|
|
2899
|
+
// (driving the now-retired chip emission) moved into
|
|
2900
|
+
// `core/issue-counter`.
|
|
2952
2901
|
evaluate(ctx) {
|
|
2953
|
-
const
|
|
2954
|
-
|
|
2955
|
-
const
|
|
2956
|
-
const refIndex = ctx.referenceablePaths && ctx.referenceablePaths.size > 0 && ctx.cwd ? { paths: ctx.referenceablePaths, cwd: ctx.cwd } : null;
|
|
2902
|
+
const broken = ctx.brokenLinks;
|
|
2903
|
+
if (!broken || broken.size === 0) return [];
|
|
2904
|
+
const refIndex = buildReferenceIndex(ctx);
|
|
2957
2905
|
const issues = [];
|
|
2958
2906
|
for (const link of ctx.links) {
|
|
2959
|
-
if (
|
|
2907
|
+
if (!broken.has(link)) continue;
|
|
2960
2908
|
if (refIndex && resolvesViaReferencePaths(link, refIndex)) continue;
|
|
2961
|
-
|
|
2962
|
-
issues.push(buildIssue(link, candidates));
|
|
2909
|
+
issues.push(buildIssue(link));
|
|
2963
2910
|
}
|
|
2964
2911
|
return issues;
|
|
2965
2912
|
}
|
|
2966
2913
|
};
|
|
2967
|
-
function
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
const issue = {
|
|
2914
|
+
function buildReferenceIndex(ctx) {
|
|
2915
|
+
if (!ctx.referenceablePaths || ctx.referenceablePaths.size === 0 || !ctx.cwd) return null;
|
|
2916
|
+
return { paths: ctx.referenceablePaths, cwd: ctx.cwd };
|
|
2917
|
+
}
|
|
2918
|
+
function buildIssue(link) {
|
|
2919
|
+
return {
|
|
2974
2920
|
analyzerId: ID21,
|
|
2975
2921
|
// `error`, not `warn`: a link whose target is not in the scan is a
|
|
2976
2922
|
// structural defect the operator must notice, and the card chip
|
|
@@ -2988,86 +2934,17 @@ function buildIssue(link, hintCandidates = []) {
|
|
|
2988
2934
|
plural: REFERENCE_BROKEN_TEXTS.wherePlural
|
|
2989
2935
|
})
|
|
2990
2936
|
}),
|
|
2991
|
-
data
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
}
|
|
2996
|
-
function attachHint(issue, data, link, hintCandidates) {
|
|
2997
|
-
const suggestedName = (link.trigger?.normalizedTrigger ?? "").replace(/^[/@]/, "").trim();
|
|
2998
|
-
const candidatePaths = hintCandidates.map((n) => n.path);
|
|
2999
|
-
data["hint"] = {
|
|
3000
|
-
kind: "missing-frontmatter-name",
|
|
3001
|
-
suggestedName,
|
|
3002
|
-
candidates: candidatePaths
|
|
3003
|
-
};
|
|
3004
|
-
issue.fix = {
|
|
3005
|
-
summary: candidatePaths.length === 1 ? tx(REFERENCE_BROKEN_TEXTS.hintSummarySingle, {
|
|
3006
|
-
name: suggestedName,
|
|
3007
|
-
candidate: candidatePaths[0]
|
|
3008
|
-
}) : tx(REFERENCE_BROKEN_TEXTS.hintSummaryMany, {
|
|
3009
|
-
name: suggestedName,
|
|
3010
|
-
candidates: candidatePaths.join(", ")
|
|
3011
|
-
}),
|
|
3012
|
-
autofixable: false
|
|
2937
|
+
data: {
|
|
2938
|
+
target: link.target,
|
|
2939
|
+
kind: link.kind,
|
|
2940
|
+
trigger: link.trigger?.normalizedTrigger ?? null
|
|
2941
|
+
}
|
|
3013
2942
|
};
|
|
3014
2943
|
}
|
|
3015
2944
|
function resolvesViaReferencePaths(link, refIndex) {
|
|
3016
2945
|
if (!isPathStyleLink(link)) return false;
|
|
3017
2946
|
return refIndex.paths.has(resolve3(refIndex.cwd, link.target));
|
|
3018
2947
|
}
|
|
3019
|
-
function indexByNormalizedName(nodes) {
|
|
3020
|
-
const out = /* @__PURE__ */ new Map();
|
|
3021
|
-
for (const node of nodes) {
|
|
3022
|
-
const raw = node.frontmatter?.["name"];
|
|
3023
|
-
const name = typeof raw === "string" ? raw : "";
|
|
3024
|
-
if (!name) continue;
|
|
3025
|
-
const key = normalizeTrigger(name);
|
|
3026
|
-
const bucket = out.get(key) ?? [];
|
|
3027
|
-
bucket.push(node);
|
|
3028
|
-
out.set(key, bucket);
|
|
3029
|
-
}
|
|
3030
|
-
return out;
|
|
3031
|
-
}
|
|
3032
|
-
function basenameWithoutExt(path) {
|
|
3033
|
-
const base = pathPosix5.basename(path);
|
|
3034
|
-
const ext = pathPosix5.extname(base);
|
|
3035
|
-
return ext ? base.slice(0, -ext.length) : base;
|
|
3036
|
-
}
|
|
3037
|
-
function indexByBasenameWithoutName(nodes) {
|
|
3038
|
-
const out = /* @__PURE__ */ new Map();
|
|
3039
|
-
for (const node of nodes) {
|
|
3040
|
-
const raw = node.frontmatter?.["name"];
|
|
3041
|
-
const name = typeof raw === "string" ? raw : "";
|
|
3042
|
-
if (name) continue;
|
|
3043
|
-
const bare = basenameWithoutExt(node.path);
|
|
3044
|
-
if (!bare) continue;
|
|
3045
|
-
const key = normalizeTrigger(bare);
|
|
3046
|
-
if (!key) continue;
|
|
3047
|
-
const bucket = out.get(key) ?? [];
|
|
3048
|
-
bucket.push(node);
|
|
3049
|
-
out.set(key, bucket);
|
|
3050
|
-
}
|
|
3051
|
-
return out;
|
|
3052
|
-
}
|
|
3053
|
-
function findHintCandidates(link, idx) {
|
|
3054
|
-
const normalized = link.trigger?.normalizedTrigger;
|
|
3055
|
-
if (!normalized) return [];
|
|
3056
|
-
const sigil = normalized.charAt(0);
|
|
3057
|
-
if (sigil !== "/" && sigil !== "@") return [];
|
|
3058
|
-
const withoutSigil = normalized.slice(1).trim();
|
|
3059
|
-
if (!withoutSigil) return [];
|
|
3060
|
-
return idx.get(withoutSigil) ?? [];
|
|
3061
|
-
}
|
|
3062
|
-
function isResolved(link, byPath3, byNormalizedName) {
|
|
3063
|
-
const normalized = link.trigger?.normalizedTrigger;
|
|
3064
|
-
if (normalized) {
|
|
3065
|
-
const withoutSigil = normalized.replace(/^[/@]/, "").trim();
|
|
3066
|
-
if (byNormalizedName.has(withoutSigil)) return true;
|
|
3067
|
-
}
|
|
3068
|
-
if (byPath3.has(link.target)) return true;
|
|
3069
|
-
return false;
|
|
3070
|
-
}
|
|
3071
2948
|
function isPathStyleLink(link) {
|
|
3072
2949
|
const sigil = link.trigger?.normalizedTrigger?.charAt(0);
|
|
3073
2950
|
if (sigil === "/" || sigil === "@") return false;
|
|
@@ -3106,12 +2983,9 @@ var referenceRedundantAnalyzer = {
|
|
|
3106
2983
|
mode: "deterministic",
|
|
3107
2984
|
evaluate(ctx) {
|
|
3108
2985
|
if (ctx.links.length === 0) return [];
|
|
3109
|
-
const byPath3 = /* @__PURE__ */ new Map();
|
|
3110
|
-
for (const node of ctx.nodes) byPath3.set(node.path, node);
|
|
3111
|
-
const byName = buildNameIndex2(ctx.nodes);
|
|
3112
2986
|
const groups = /* @__PURE__ */ new Map();
|
|
3113
2987
|
for (const link of ctx.links) {
|
|
3114
|
-
const resolved =
|
|
2988
|
+
const resolved = link.resolvedTarget;
|
|
3115
2989
|
if (!resolved) continue;
|
|
3116
2990
|
const key = `${link.source}\0${resolved}`;
|
|
3117
2991
|
const bucket = groups.get(key);
|
|
@@ -3192,45 +3066,6 @@ function formatGroupedOccurrences(occurrences) {
|
|
|
3192
3066
|
})
|
|
3193
3067
|
).join(REFERENCE_REDUNDANT_TEXTS.occurrenceSeparator);
|
|
3194
3068
|
}
|
|
3195
|
-
function buildNameIndex2(nodes) {
|
|
3196
|
-
const out = /* @__PURE__ */ new Map();
|
|
3197
|
-
for (const node of nodes) {
|
|
3198
|
-
for (const candidate of collectIdentifiers(node)) {
|
|
3199
|
-
const normalised = normalizeTrigger(candidate);
|
|
3200
|
-
if (!normalised) continue;
|
|
3201
|
-
const bucket = out.get(normalised);
|
|
3202
|
-
if (bucket) bucket.push(node.path);
|
|
3203
|
-
else out.set(normalised, [node.path]);
|
|
3204
|
-
}
|
|
3205
|
-
}
|
|
3206
|
-
return out;
|
|
3207
|
-
}
|
|
3208
|
-
function collectIdentifiers(node) {
|
|
3209
|
-
const out = [];
|
|
3210
|
-
const fmName = node.frontmatter?.["name"];
|
|
3211
|
-
if (typeof fmName === "string" && fmName.length > 0) out.push(fmName);
|
|
3212
|
-
const segs = node.path.split("/");
|
|
3213
|
-
const last = segs[segs.length - 1] ?? "";
|
|
3214
|
-
if (last) {
|
|
3215
|
-
const stem = last.replace(/\.[^.]+$/, "");
|
|
3216
|
-
if (stem) out.push(stem);
|
|
3217
|
-
}
|
|
3218
|
-
if (segs.length >= 2) {
|
|
3219
|
-
const dirBase = segs[segs.length - 2];
|
|
3220
|
-
if (dirBase) out.push(dirBase);
|
|
3221
|
-
}
|
|
3222
|
-
return out;
|
|
3223
|
-
}
|
|
3224
|
-
function resolveTargetPath(link, byPath3, byName) {
|
|
3225
|
-
if (byPath3.has(link.target)) return link.target;
|
|
3226
|
-
const trigger = link.trigger?.normalizedTrigger;
|
|
3227
|
-
if (!trigger) return null;
|
|
3228
|
-
const stripped = trigger.replace(/^[/@]/, "").trim();
|
|
3229
|
-
if (!stripped) return null;
|
|
3230
|
-
const candidates = byName.get(stripped);
|
|
3231
|
-
if (!candidates || candidates.length === 0) return null;
|
|
3232
|
-
return candidates[0] ?? null;
|
|
3233
|
-
}
|
|
3234
3069
|
|
|
3235
3070
|
// kernel/adapters/schema-validators.ts
|
|
3236
3071
|
import { readFileSync as readFileSync2 } from "fs";
|
|
@@ -4789,11 +4624,11 @@ function validateOrDefault(parsed) {
|
|
|
4789
4624
|
if (!result.ok) return defaultSettings();
|
|
4790
4625
|
return result.data;
|
|
4791
4626
|
}
|
|
4792
|
-
function backfillSubObjects(
|
|
4627
|
+
function backfillSubObjects(settings2) {
|
|
4793
4628
|
return {
|
|
4794
|
-
...
|
|
4795
|
-
updateCheck:
|
|
4796
|
-
telemetry:
|
|
4629
|
+
...settings2,
|
|
4630
|
+
updateCheck: settings2.updateCheck ?? {},
|
|
4631
|
+
telemetry: settings2.telemetry ?? {}
|
|
4797
4632
|
};
|
|
4798
4633
|
}
|
|
4799
4634
|
function writeUserSettings(patch) {
|
|
@@ -4813,24 +4648,24 @@ function writeUserSettings(patch) {
|
|
|
4813
4648
|
}
|
|
4814
4649
|
}
|
|
4815
4650
|
function isUpdateCheckEnabled() {
|
|
4816
|
-
const
|
|
4817
|
-
return
|
|
4651
|
+
const settings2 = readUserSettings();
|
|
4652
|
+
return settings2.updateCheck?.enabled !== false;
|
|
4818
4653
|
}
|
|
4819
4654
|
function isErrorTelemetryEnabled() {
|
|
4820
|
-
const
|
|
4821
|
-
return
|
|
4655
|
+
const settings2 = readUserSettings();
|
|
4656
|
+
return settings2.telemetry?.errorsEnabled === true;
|
|
4822
4657
|
}
|
|
4823
4658
|
function isUsageCliTelemetryEnabled() {
|
|
4824
|
-
const
|
|
4825
|
-
return
|
|
4659
|
+
const settings2 = readUserSettings();
|
|
4660
|
+
return settings2.telemetry?.usageCliEnabled === true;
|
|
4826
4661
|
}
|
|
4827
4662
|
function isUsageUiTelemetryEnabled() {
|
|
4828
|
-
const
|
|
4829
|
-
return
|
|
4663
|
+
const settings2 = readUserSettings();
|
|
4664
|
+
return settings2.telemetry?.usageUiEnabled === true;
|
|
4830
4665
|
}
|
|
4831
4666
|
function readAnonymousId() {
|
|
4832
|
-
const
|
|
4833
|
-
return
|
|
4667
|
+
const settings2 = readUserSettings();
|
|
4668
|
+
return settings2.telemetry?.anonymousId ?? null;
|
|
4834
4669
|
}
|
|
4835
4670
|
function ensureAnonymousId(generate = () => randomUUID()) {
|
|
4836
4671
|
const existing = readAnonymousId();
|
|
@@ -4840,12 +4675,12 @@ function ensureAnonymousId(generate = () => randomUUID()) {
|
|
|
4840
4675
|
return id;
|
|
4841
4676
|
}
|
|
4842
4677
|
function hasTelemetryPromptBeenShown() {
|
|
4843
|
-
const
|
|
4844
|
-
return typeof
|
|
4678
|
+
const settings2 = readUserSettings();
|
|
4679
|
+
return typeof settings2.telemetry?.promptedAt === "number";
|
|
4845
4680
|
}
|
|
4846
4681
|
function hasSeenFirstRun() {
|
|
4847
|
-
const
|
|
4848
|
-
return typeof
|
|
4682
|
+
const settings2 = readUserSettings();
|
|
4683
|
+
return typeof settings2.telemetry?.firstRunAt === "number";
|
|
4849
4684
|
}
|
|
4850
4685
|
function mergeSettings(current, patch) {
|
|
4851
4686
|
const merged = {
|
|
@@ -6255,6 +6090,10 @@ function removeConfigValue(key, opts) {
|
|
|
6255
6090
|
writeJsonAtomic(path, merged);
|
|
6256
6091
|
return true;
|
|
6257
6092
|
}
|
|
6093
|
+
function getValueSource(key, opts) {
|
|
6094
|
+
const loaded = loadConfigForScope(opts);
|
|
6095
|
+
return loaded.sources.get(key);
|
|
6096
|
+
}
|
|
6258
6097
|
function loadConfigForScope(opts) {
|
|
6259
6098
|
return loadConfig({
|
|
6260
6099
|
cwd: opts.cwd,
|
|
@@ -11695,6 +11534,7 @@ function filterBuiltInManifests(manifests, resolveEnabled) {
|
|
|
11695
11534
|
// core/runtime/plugin-runtime/composer.ts
|
|
11696
11535
|
function composeScanExtensions(opts) {
|
|
11697
11536
|
const resolveEnabled = opts.resolveEnabled ?? opts.pluginRuntime.resolveEnabled;
|
|
11537
|
+
const resolveSettings = opts.resolveSettings;
|
|
11698
11538
|
const providers = [];
|
|
11699
11539
|
const extractors = [];
|
|
11700
11540
|
const analyzers = [];
|
|
@@ -11703,20 +11543,21 @@ function composeScanExtensions(opts) {
|
|
|
11703
11543
|
if (!opts.noBuiltIns) {
|
|
11704
11544
|
accumulateBuiltInScanExtensions(
|
|
11705
11545
|
{ providers, extractors, analyzers, hooks, actions },
|
|
11706
|
-
resolveEnabled
|
|
11546
|
+
resolveEnabled,
|
|
11547
|
+
resolveSettings
|
|
11707
11548
|
);
|
|
11708
11549
|
}
|
|
11709
11550
|
for (const ext of opts.pluginRuntime.extensions.providers) {
|
|
11710
|
-
if (isPluginExtensionEnabled(ext, resolveEnabled)) providers.push(ext);
|
|
11551
|
+
if (isPluginExtensionEnabled(ext, resolveEnabled)) providers.push(withResolvedSettings(ext, resolveSettings));
|
|
11711
11552
|
}
|
|
11712
11553
|
for (const ext of opts.pluginRuntime.extensions.extractors) {
|
|
11713
|
-
if (isPluginExtensionEnabled(ext, resolveEnabled)) extractors.push(ext);
|
|
11554
|
+
if (isPluginExtensionEnabled(ext, resolveEnabled)) extractors.push(withResolvedSettings(ext, resolveSettings));
|
|
11714
11555
|
}
|
|
11715
11556
|
for (const ext of opts.pluginRuntime.extensions.analyzers) {
|
|
11716
|
-
if (isPluginExtensionEnabled(ext, resolveEnabled)) analyzers.push(ext);
|
|
11557
|
+
if (isPluginExtensionEnabled(ext, resolveEnabled)) analyzers.push(withResolvedSettings(ext, resolveSettings));
|
|
11717
11558
|
}
|
|
11718
11559
|
for (const ext of opts.pluginRuntime.extensions.hooks) {
|
|
11719
|
-
if (isPluginExtensionEnabled(ext, resolveEnabled)) hooks.push(ext);
|
|
11560
|
+
if (isPluginExtensionEnabled(ext, resolveEnabled)) hooks.push(withResolvedSettings(ext, resolveSettings));
|
|
11720
11561
|
}
|
|
11721
11562
|
for (const ext of opts.pluginRuntime.extensions.actions) {
|
|
11722
11563
|
if (isPluginExtensionEnabled(ext, resolveEnabled)) actions.push(ext);
|
|
@@ -11735,22 +11576,26 @@ function composeScanExtensions(opts) {
|
|
|
11735
11576
|
actions
|
|
11736
11577
|
};
|
|
11737
11578
|
}
|
|
11738
|
-
function
|
|
11579
|
+
function withResolvedSettings(ext, resolveSettings) {
|
|
11580
|
+
if (!resolveSettings) return ext;
|
|
11581
|
+
return { ...ext, resolvedSettings: resolveSettings(ext) };
|
|
11582
|
+
}
|
|
11583
|
+
function accumulateBuiltInScanExtensions(buckets, resolveEnabled, resolveSettings) {
|
|
11739
11584
|
for (const plugin of builtInPlugins) {
|
|
11740
11585
|
for (const ext of plugin.extensions) {
|
|
11741
11586
|
if (!isBuiltInExtensionEnabled(plugin, ext, resolveEnabled)) continue;
|
|
11742
11587
|
switch (ext.kind) {
|
|
11743
11588
|
case "provider":
|
|
11744
|
-
buckets.providers.push(ext);
|
|
11589
|
+
buckets.providers.push(withResolvedSettings(ext, resolveSettings));
|
|
11745
11590
|
break;
|
|
11746
11591
|
case "extractor":
|
|
11747
|
-
buckets.extractors.push(ext);
|
|
11592
|
+
buckets.extractors.push(withResolvedSettings(ext, resolveSettings));
|
|
11748
11593
|
break;
|
|
11749
11594
|
case "analyzer":
|
|
11750
|
-
buckets.analyzers.push(ext);
|
|
11595
|
+
buckets.analyzers.push(withResolvedSettings(ext, resolveSettings));
|
|
11751
11596
|
break;
|
|
11752
11597
|
case "hook":
|
|
11753
|
-
buckets.hooks.push(ext);
|
|
11598
|
+
buckets.hooks.push(withResolvedSettings(ext, resolveSettings));
|
|
11754
11599
|
break;
|
|
11755
11600
|
case "action":
|
|
11756
11601
|
buckets.actions.push(ext);
|
|
@@ -11768,18 +11613,19 @@ function accumulateBuiltInScanExtensions(buckets, resolveEnabled) {
|
|
|
11768
11613
|
function composeFormatters(opts) {
|
|
11769
11614
|
const noBuiltIns = opts.noBuiltIns ?? false;
|
|
11770
11615
|
const resolveEnabled = opts.resolveEnabled ?? opts.pluginRuntime.resolveEnabled;
|
|
11616
|
+
const resolveSettings = opts.resolveSettings;
|
|
11771
11617
|
const out = [];
|
|
11772
11618
|
if (!noBuiltIns) {
|
|
11773
11619
|
for (const plugin of builtInPlugins) {
|
|
11774
11620
|
for (const ext of plugin.extensions) {
|
|
11775
11621
|
if (ext.kind !== "formatter") continue;
|
|
11776
11622
|
if (!isBuiltInExtensionEnabled(plugin, ext, resolveEnabled)) continue;
|
|
11777
|
-
out.push(ext);
|
|
11623
|
+
out.push(withResolvedSettings(ext, resolveSettings));
|
|
11778
11624
|
}
|
|
11779
11625
|
}
|
|
11780
11626
|
}
|
|
11781
11627
|
for (const ext of opts.pluginRuntime.extensions.formatters) {
|
|
11782
|
-
if (isPluginExtensionEnabled(ext, resolveEnabled)) out.push(ext);
|
|
11628
|
+
if (isPluginExtensionEnabled(ext, resolveEnabled)) out.push(withResolvedSettings(ext, resolveSettings));
|
|
11783
11629
|
}
|
|
11784
11630
|
return out;
|
|
11785
11631
|
}
|
|
@@ -14966,6 +14812,9 @@ var GraphCommand = class extends SmCommand {
|
|
|
14966
14812
|
nodes: scan.nodes,
|
|
14967
14813
|
links: scan.links,
|
|
14968
14814
|
issues: scan.issues,
|
|
14815
|
+
// Resolved settings of the formatter (empty when the formatter
|
|
14816
|
+
// declares none, or when the composer did not populate them).
|
|
14817
|
+
settings: formatter.resolvedSettings ?? {},
|
|
14969
14818
|
// Pass the full persisted scan so format-specific renderers
|
|
14970
14819
|
// that mirror a `ScanResult` envelope (today: built-in `json`)
|
|
14971
14820
|
// can emit it verbatim without re-deriving fields like
|
|
@@ -15977,12 +15826,12 @@ function emitExtensionError(emitter, qualifiedId2, nodePath, data) {
|
|
|
15977
15826
|
}
|
|
15978
15827
|
function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enrichNode, emitContribution, emitSignal, emitNode, store) {
|
|
15979
15828
|
const scope = extractor.scope ?? "both";
|
|
15980
|
-
const
|
|
15829
|
+
const settings2 = extractor.resolvedSettings ?? {};
|
|
15981
15830
|
return {
|
|
15982
15831
|
node,
|
|
15983
15832
|
body: scope === "frontmatter" ? "" : body,
|
|
15984
15833
|
frontmatter: scope === "body" ? {} : frontmatter,
|
|
15985
|
-
settings,
|
|
15834
|
+
settings: settings2,
|
|
15986
15835
|
emitLink,
|
|
15987
15836
|
enrichNode,
|
|
15988
15837
|
emitContribution,
|
|
@@ -16281,7 +16130,7 @@ function runActionProjections(actions, nodes, links, emitter) {
|
|
|
16281
16130
|
}
|
|
16282
16131
|
|
|
16283
16132
|
// kernel/orchestrator/analyzers.ts
|
|
16284
|
-
async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, registeredActionIds, emitter, hookDispatcher, reservedNodePaths, signals, seedIssues = []) {
|
|
16133
|
+
async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, registeredActionIds, emitter, hookDispatcher, reservedNodePaths, brokenLinks, signals, seedIssues = []) {
|
|
16285
16134
|
const issues = [...seedIssues];
|
|
16286
16135
|
const contributions = [];
|
|
16287
16136
|
const contributionErrors = [];
|
|
@@ -16358,6 +16207,10 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
16358
16207
|
const emitted = await analyzer.evaluate({
|
|
16359
16208
|
nodes,
|
|
16360
16209
|
links: internalLinks,
|
|
16210
|
+
// `settings` is always populated (possibly empty) so analyzers can
|
|
16211
|
+
// read `ctx.settings.<id>` without a presence check. The composer
|
|
16212
|
+
// populated `resolvedSettings` on each composed analyzer.
|
|
16213
|
+
settings: analyzer.resolvedSettings ?? {},
|
|
16361
16214
|
orphanSidecars: analyzerOrphans,
|
|
16362
16215
|
sidecarRoots,
|
|
16363
16216
|
annotationContributions,
|
|
@@ -16371,6 +16224,7 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
16371
16224
|
...referenceablePaths ? { referenceablePaths } : {},
|
|
16372
16225
|
...cwd ? { cwd } : {},
|
|
16373
16226
|
...reservedNodePaths ? { reservedNodePaths } : {},
|
|
16227
|
+
...brokenLinks ? { brokenLinks } : {},
|
|
16374
16228
|
...signals && signals.length > 0 ? { signals } : {},
|
|
16375
16229
|
emitContribution
|
|
16376
16230
|
});
|
|
@@ -17593,6 +17447,7 @@ async function runScanInternal(_kernel, options) {
|
|
|
17593
17447
|
walked.signals = resolved.resolvedSignals;
|
|
17594
17448
|
const postWalkCtx = buildPostWalkTransformCtx(exts.providers, walked.nodes, activeProviderId);
|
|
17595
17449
|
walked.internalLinks = applyPostWalkTransforms(walked.internalLinks, walked.nodes, postWalkCtx);
|
|
17450
|
+
const brokenLinks = collectBrokenLinks(walked.internalLinks, walked.nodes, postWalkCtx);
|
|
17596
17451
|
recomputeLinkCounts(walked.nodes, walked.internalLinks);
|
|
17597
17452
|
recomputeExternalRefsCount(walked.nodes, walked.externalLinks, walked.cachedPaths);
|
|
17598
17453
|
await dispatchExtractorCompleted(exts.extractors, emitter, hookDispatcher);
|
|
@@ -17614,6 +17469,7 @@ async function runScanInternal(_kernel, options) {
|
|
|
17614
17469
|
emitter,
|
|
17615
17470
|
hookDispatcher,
|
|
17616
17471
|
postWalkCtx.reservedNodePaths,
|
|
17472
|
+
brokenLinks,
|
|
17617
17473
|
walked.signals,
|
|
17618
17474
|
// Seed the accumulator with orchestrator-emitted frontmatter
|
|
17619
17475
|
// issues so the aggregate phase (`core/issue-counter`) counts
|
|
@@ -18058,6 +17914,159 @@ function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
|
18058
17914
|
return { orphanFilePaths: orphans, referencedCount: referencedPaths.size };
|
|
18059
17915
|
}
|
|
18060
17916
|
|
|
17917
|
+
// core/config/plugin-settings.ts
|
|
17918
|
+
var defaultWarn = (message) => log.warn(message);
|
|
17919
|
+
function resolveExtensionSettings(manifest, config, onWarn = defaultWarn) {
|
|
17920
|
+
const declarations = manifest.settings;
|
|
17921
|
+
if (!declarations || Object.keys(declarations).length === 0) return {};
|
|
17922
|
+
const overrides = readSettingsOverrides(config, manifest.pluginId, manifest.id);
|
|
17923
|
+
const resolved = {};
|
|
17924
|
+
for (const [settingId, declaration] of Object.entries(declarations)) {
|
|
17925
|
+
const outcome = resolveOneSetting(manifest, settingId, declaration, overrides, onWarn);
|
|
17926
|
+
if (outcome.hasValue) resolved[settingId] = outcome.value;
|
|
17927
|
+
}
|
|
17928
|
+
return resolved;
|
|
17929
|
+
}
|
|
17930
|
+
function resolveOneSetting(manifest, settingId, declaration, overrides, onWarn) {
|
|
17931
|
+
const fallback = declarationDefault(declaration);
|
|
17932
|
+
const toFallback = () => fallback !== void 0 ? { hasValue: true, value: fallback } : { hasValue: false };
|
|
17933
|
+
if (!Object.prototype.hasOwnProperty.call(overrides, settingId)) return toFallback();
|
|
17934
|
+
const candidate = overrides[settingId];
|
|
17935
|
+
const check = validateSettingValue(declaration, candidate);
|
|
17936
|
+
if (check.ok) return { hasValue: true, value: candidate };
|
|
17937
|
+
onWarn(
|
|
17938
|
+
`Setting '${settingId}' for extension '${manifest.pluginId}/${manifest.id}' is invalid (${check.reason}); falling back to the declared default.`
|
|
17939
|
+
);
|
|
17940
|
+
return toFallback();
|
|
17941
|
+
}
|
|
17942
|
+
function buildSettingsResolver(config, onWarn = defaultWarn) {
|
|
17943
|
+
return (ext) => resolveExtensionSettings(ext, config, onWarn);
|
|
17944
|
+
}
|
|
17945
|
+
function declarationDefault(declaration) {
|
|
17946
|
+
return "default" in declaration ? declaration.default : void 0;
|
|
17947
|
+
}
|
|
17948
|
+
function readSettingsOverrides(config, pluginId, extId) {
|
|
17949
|
+
const entry = config.plugins?.[pluginId];
|
|
17950
|
+
const ext = entry?.extensions?.[extId];
|
|
17951
|
+
const settings2 = ext?.settings;
|
|
17952
|
+
if (settings2 && typeof settings2 === "object" && !Array.isArray(settings2)) {
|
|
17953
|
+
return settings2;
|
|
17954
|
+
}
|
|
17955
|
+
return {};
|
|
17956
|
+
}
|
|
17957
|
+
var OK = { ok: true, reason: "" };
|
|
17958
|
+
function fail2(reason) {
|
|
17959
|
+
return { ok: false, reason };
|
|
17960
|
+
}
|
|
17961
|
+
function validateSettingValue(declaration, value) {
|
|
17962
|
+
switch (declaration.type) {
|
|
17963
|
+
case "string-list":
|
|
17964
|
+
return validateStringList(value, declaration.min, declaration.max, declaration.itemMaxLength);
|
|
17965
|
+
case "single-string":
|
|
17966
|
+
return validateSingleString(value, declaration.minLength, declaration.maxLength, declaration.pattern);
|
|
17967
|
+
case "boolean-flag":
|
|
17968
|
+
return typeof value === "boolean" ? OK : fail2("expected a boolean");
|
|
17969
|
+
case "integer":
|
|
17970
|
+
return validateInteger(value, declaration.min, declaration.max);
|
|
17971
|
+
case "number":
|
|
17972
|
+
return validateNumber(value, declaration.min, declaration.max);
|
|
17973
|
+
case "enum-pick":
|
|
17974
|
+
return validateEnumPick(value, declaration.options.map((o) => o.value));
|
|
17975
|
+
case "enum-multipick":
|
|
17976
|
+
return validateEnumMultipick(value, declaration.options.map((o) => o.value), declaration.min, declaration.max);
|
|
17977
|
+
case "path-glob":
|
|
17978
|
+
return validatePathGlob(value, declaration.multiple === true);
|
|
17979
|
+
case "regex":
|
|
17980
|
+
return validateRegex(value, declaration.flags);
|
|
17981
|
+
case "secret":
|
|
17982
|
+
return typeof value === "string" ? OK : fail2("expected a string");
|
|
17983
|
+
case "key-value-list":
|
|
17984
|
+
return validateKeyValueList(value, declaration.min, declaration.max);
|
|
17985
|
+
default: {
|
|
17986
|
+
const _exhaustive = declaration;
|
|
17987
|
+
return fail2(`unknown input-type: ${String(_exhaustive.type)}`);
|
|
17988
|
+
}
|
|
17989
|
+
}
|
|
17990
|
+
}
|
|
17991
|
+
function isStringArray(value) {
|
|
17992
|
+
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
17993
|
+
}
|
|
17994
|
+
function validateStringList(value, min, max, itemMaxLength) {
|
|
17995
|
+
if (!isStringArray(value)) return fail2("expected an array of strings");
|
|
17996
|
+
if (min !== void 0 && value.length < min) return fail2(`expected at least ${min} item(s)`);
|
|
17997
|
+
if (max !== void 0 && value.length > max) return fail2(`expected at most ${max} item(s)`);
|
|
17998
|
+
const cap = itemMaxLength ?? 256;
|
|
17999
|
+
if (value.some((item) => item.length > cap)) return fail2(`item exceeds ${cap} characters`);
|
|
18000
|
+
return OK;
|
|
18001
|
+
}
|
|
18002
|
+
function validateSingleString(value, minLength, maxLength, pattern) {
|
|
18003
|
+
if (typeof value !== "string") return fail2("expected a string");
|
|
18004
|
+
if (minLength !== void 0 && value.length < minLength) return fail2(`expected at least ${minLength} characters`);
|
|
18005
|
+
if (maxLength !== void 0 && value.length > maxLength) return fail2(`expected at most ${maxLength} characters`);
|
|
18006
|
+
return matchesPattern(value, pattern);
|
|
18007
|
+
}
|
|
18008
|
+
function matchesPattern(value, pattern) {
|
|
18009
|
+
if (pattern === void 0) return OK;
|
|
18010
|
+
let re;
|
|
18011
|
+
try {
|
|
18012
|
+
re = new RegExp(pattern);
|
|
18013
|
+
} catch {
|
|
18014
|
+
return OK;
|
|
18015
|
+
}
|
|
18016
|
+
return re.test(value) ? OK : fail2(`does not match pattern ${pattern}`);
|
|
18017
|
+
}
|
|
18018
|
+
function validateInteger(value, min, max) {
|
|
18019
|
+
if (typeof value !== "number" || !Number.isInteger(value)) return fail2("expected an integer");
|
|
18020
|
+
if (!Number.isSafeInteger(value)) return fail2("integer out of safe range");
|
|
18021
|
+
if (min !== void 0 && value < min) return fail2(`expected >= ${min}`);
|
|
18022
|
+
if (max !== void 0 && value > max) return fail2(`expected <= ${max}`);
|
|
18023
|
+
return OK;
|
|
18024
|
+
}
|
|
18025
|
+
function validateNumber(value, min, max) {
|
|
18026
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fail2("expected a finite number");
|
|
18027
|
+
if (min !== void 0 && value < min) return fail2(`expected >= ${min}`);
|
|
18028
|
+
if (max !== void 0 && value > max) return fail2(`expected <= ${max}`);
|
|
18029
|
+
return OK;
|
|
18030
|
+
}
|
|
18031
|
+
function validateEnumPick(value, allowed) {
|
|
18032
|
+
if (typeof value !== "string") return fail2("expected a string");
|
|
18033
|
+
if (!allowed.includes(value)) return fail2(`expected one of: ${allowed.join(", ")}`);
|
|
18034
|
+
return OK;
|
|
18035
|
+
}
|
|
18036
|
+
function validateEnumMultipick(value, allowed, min, max) {
|
|
18037
|
+
if (!isStringArray(value)) return fail2("expected an array of strings");
|
|
18038
|
+
const allowedSet = new Set(allowed);
|
|
18039
|
+
if (value.some((v) => !allowedSet.has(v))) return fail2(`every entry must be one of: ${allowed.join(", ")}`);
|
|
18040
|
+
if (min !== void 0 && value.length < min) return fail2(`expected at least ${min} selection(s)`);
|
|
18041
|
+
if (max !== void 0 && value.length > max) return fail2(`expected at most ${max} selection(s)`);
|
|
18042
|
+
return OK;
|
|
18043
|
+
}
|
|
18044
|
+
function validatePathGlob(value, multiple) {
|
|
18045
|
+
if (multiple) {
|
|
18046
|
+
return isStringArray(value) ? OK : fail2("expected an array of glob strings");
|
|
18047
|
+
}
|
|
18048
|
+
return typeof value === "string" ? OK : fail2("expected a glob string");
|
|
18049
|
+
}
|
|
18050
|
+
function validateRegex(value, flags) {
|
|
18051
|
+
if (typeof value !== "string") return fail2("expected a string");
|
|
18052
|
+
try {
|
|
18053
|
+
new RegExp(value, flags ?? "");
|
|
18054
|
+
} catch (err) {
|
|
18055
|
+
return fail2(`is not a compilable regex (${err instanceof Error ? err.message : String(err)})`);
|
|
18056
|
+
}
|
|
18057
|
+
return OK;
|
|
18058
|
+
}
|
|
18059
|
+
function validateKeyValueList(value, min, max) {
|
|
18060
|
+
if (!Array.isArray(value)) return fail2("expected an array of { key, value } entries");
|
|
18061
|
+
const wellShaped = value.every(
|
|
18062
|
+
(entry) => entry !== null && typeof entry === "object" && typeof entry.key === "string" && typeof entry.value === "string"
|
|
18063
|
+
);
|
|
18064
|
+
if (!wellShaped) return fail2("every entry must be { key: string, value: string }");
|
|
18065
|
+
if (min !== void 0 && value.length < min) return fail2(`expected at least ${min} entr(y/ies)`);
|
|
18066
|
+
if (max !== void 0 && value.length > max) return fail2(`expected at most ${max} entr(y/ies)`);
|
|
18067
|
+
return OK;
|
|
18068
|
+
}
|
|
18069
|
+
|
|
18061
18070
|
// core/runtime/i18n/progress-emitter.texts.ts
|
|
18062
18071
|
var PROGRESS_EMITTER_TEXTS = {
|
|
18063
18072
|
/**
|
|
@@ -18558,10 +18567,10 @@ async function runScanForCommand(opts) {
|
|
|
18558
18567
|
const dbPath = resolveDbPath({ db: void 0, ...ctx });
|
|
18559
18568
|
const kernel = createKernel();
|
|
18560
18569
|
const pluginRuntime = await preparePluginRuntime(opts, opts.printer);
|
|
18561
|
-
const extensions = registerExtensions(kernel, pluginRuntime, opts);
|
|
18562
18570
|
const scanInputs = loadScanInputs(opts, ctx);
|
|
18563
18571
|
if ("kind" in scanInputs) return scanInputs;
|
|
18564
18572
|
const { cfg, ignoreFilter, strict, effectiveRoots } = scanInputs;
|
|
18573
|
+
const extensions = registerExtensions(kernel, pluginRuntime, opts, cfg);
|
|
18565
18574
|
let referenceablePaths;
|
|
18566
18575
|
if (cfg.scan.referencePaths.length > 0) {
|
|
18567
18576
|
const walk3 = walkReferencePaths(cfg.scan.referencePaths, ctx.cwd);
|
|
@@ -18657,10 +18666,11 @@ async function preparePluginRuntime(opts, printer) {
|
|
|
18657
18666
|
pluginRuntime.emitWarnings(printer);
|
|
18658
18667
|
return pluginRuntime;
|
|
18659
18668
|
}
|
|
18660
|
-
function registerExtensions(kernel, pluginRuntime, opts) {
|
|
18669
|
+
function registerExtensions(kernel, pluginRuntime, opts, cfg) {
|
|
18661
18670
|
const composeOpts = {
|
|
18662
18671
|
noBuiltIns: opts.noBuiltIns,
|
|
18663
|
-
pluginRuntime
|
|
18672
|
+
pluginRuntime,
|
|
18673
|
+
resolveSettings: buildSettingsResolver(cfg)
|
|
18664
18674
|
};
|
|
18665
18675
|
if (opts.killSwitches) composeOpts.killSwitches = opts.killSwitches;
|
|
18666
18676
|
if (opts.resolveEnabledOverride) composeOpts.resolveEnabled = opts.resolveEnabledOverride;
|
|
@@ -20204,7 +20214,7 @@ async function findActiveOrphanIssues(adapter, predicate) {
|
|
|
20204
20214
|
(issue) => ORPHAN_RULE_IDS.includes(issue.analyzerId) && predicate(issue)
|
|
20205
20215
|
);
|
|
20206
20216
|
}
|
|
20207
|
-
function
|
|
20217
|
+
function isStringArray2(v) {
|
|
20208
20218
|
return Array.isArray(v) && v.every((s) => typeof s === "string");
|
|
20209
20219
|
}
|
|
20210
20220
|
var OrphansCommand = class extends SmCommand {
|
|
@@ -20575,7 +20585,7 @@ var OrphansUndoRenameCommand = class extends SmCommand {
|
|
|
20575
20585
|
return { ok: false, exitCode: ExitCode.NotFound };
|
|
20576
20586
|
}
|
|
20577
20587
|
const dataCandidates = issue.data ? issue.data["candidates"] : void 0;
|
|
20578
|
-
if (!
|
|
20588
|
+
if (!isStringArray2(dataCandidates) || !dataCandidates.includes(this.from)) {
|
|
20579
20589
|
this.printer.error(
|
|
20580
20590
|
tx(ORPHANS_TEXTS.undoAmbiguousNotInCandidates, { glyph: errGlyph, from: this.from })
|
|
20581
20591
|
);
|
|
@@ -22729,11 +22739,12 @@ var INPUT_TYPES_CATALOG = [
|
|
|
22729
22739
|
{ id: "single-string", summary: "Single text input." },
|
|
22730
22740
|
{ id: "boolean-flag", summary: "On/off toggle." },
|
|
22731
22741
|
{ id: "integer", summary: "Integer with optional bounds." },
|
|
22742
|
+
{ id: "number", summary: "Decimal number with optional bounds." },
|
|
22732
22743
|
{ id: "enum-pick", summary: "Pick one from a closed set." },
|
|
22733
22744
|
{ id: "enum-multipick", summary: "Pick zero or more from a closed set." },
|
|
22734
22745
|
{ id: "path-glob", summary: "Glob pattern (single or multiple)." },
|
|
22735
22746
|
{ id: "regex", summary: "ECMAScript regex pattern body." },
|
|
22736
|
-
{ id: "secret", summary: "Sensitive string (
|
|
22747
|
+
{ id: "secret", summary: "Sensitive string, forced into project-local storage (gitignored), not encrypted." },
|
|
22737
22748
|
{ id: "key-value-list", summary: "Editable mapping of strings to strings." }
|
|
22738
22749
|
];
|
|
22739
22750
|
|
|
@@ -22806,6 +22817,375 @@ var PluginsUpgradeCommand = class extends SmCommand {
|
|
|
22806
22817
|
}
|
|
22807
22818
|
};
|
|
22808
22819
|
|
|
22820
|
+
// cli/commands/plugins/config.ts
|
|
22821
|
+
import { Command as Command29, Option as Option27 } from "clipanion";
|
|
22822
|
+
|
|
22823
|
+
// cli/i18n/plugins-config.texts.ts
|
|
22824
|
+
var PLUGINS_CONFIG_TEXTS = {
|
|
22825
|
+
// --- id-shape redirects ----------------------------------------------
|
|
22826
|
+
// `sm plugins config` operates on one extension. A bare plugin id is
|
|
22827
|
+
// the wrong granularity, redirect to `sm plugins list <id>`.
|
|
22828
|
+
bareId: '{{glyph}} `sm plugins config` needs a qualified `<plugin>/<ext>` id; "{{id}}" is a plugin.\n {{hint}}\n',
|
|
22829
|
+
bareIdHint: "Run `sm plugins list {{id}}` to see the extensions, then `sm plugins config {{id}}/<ext>`.",
|
|
22830
|
+
// --- no declared settings --------------------------------------------
|
|
22831
|
+
noSettings: '{{glyph}} Extension "{{id}}" declares no configurable settings.\n {{hint}}\n',
|
|
22832
|
+
noSettingsHint: "Run `sm plugins show {{id}}` to inspect the extension.",
|
|
22833
|
+
unknownSetting: '{{glyph}} Unknown setting "{{settingId}}" for extension "{{id}}".\n {{hint}}\n',
|
|
22834
|
+
unknownSettingHint: "Declared settings: {{declared}}.",
|
|
22835
|
+
// --- coercion / validation -------------------------------------------
|
|
22836
|
+
coerceFailed: '{{glyph}} Could not parse "{{value}}" as type {{type}} for setting "{{settingId}}".\n {{hint}}\n',
|
|
22837
|
+
coerceFailedHint: "{{detail}}",
|
|
22838
|
+
validationFailed: '{{glyph}} Invalid value for setting "{{settingId}}" ({{type}}): {{reason}}.\n',
|
|
22839
|
+
writeFailed: '{{glyph}} Failed to write setting "{{settingId}}": {{message}}\n',
|
|
22840
|
+
// --- table view (no settingId) ---------------------------------------
|
|
22841
|
+
/** Section header above the settings table. */
|
|
22842
|
+
tableHeader: " Settings for {{id}}\n",
|
|
22843
|
+
/** One table row: setting id, effective value, source layer tag. */
|
|
22844
|
+
tableRow: " {{settingId}} {{value}}{{sourceTag}}\n",
|
|
22845
|
+
/** Dim suffix showing which layer set the effective value. */
|
|
22846
|
+
tableSourceTag: " [{{source}}]",
|
|
22847
|
+
/** Redaction placeholder for `secret`-typed values in any output. */
|
|
22848
|
+
redacted: "<redacted>",
|
|
22849
|
+
// --- write / reset receipts ------------------------------------------
|
|
22850
|
+
setWritten: "{{glyph}} Set {{settingId}} = {{value}} for {{id}}{{wroteTag}}\n",
|
|
22851
|
+
setWroteTag: " (wrote {{path}})",
|
|
22852
|
+
resetRemoved: "{{glyph}} Cleared {{settingId}} for {{id}}; falls back to the declared default{{wroteTag}}\n",
|
|
22853
|
+
resetNoOverride: "{{glyph}} No override set for {{settingId}} on {{id}}; nothing to clear.\n",
|
|
22854
|
+
// --- re-scan footer ---------------------------------------------------
|
|
22855
|
+
rescanFooter: "{{hint}}\n",
|
|
22856
|
+
rescanFooterText: "Settings are read once per scan; run `sm scan` to apply."
|
|
22857
|
+
};
|
|
22858
|
+
|
|
22859
|
+
// cli/commands/plugins/config.ts
|
|
22860
|
+
var PluginsConfigCommand = class extends SmCommand {
|
|
22861
|
+
static paths = [["plugins", "config"]];
|
|
22862
|
+
static usage = Command29.Usage({
|
|
22863
|
+
category: "Plugins",
|
|
22864
|
+
description: "Read or write an extension's declared settings.",
|
|
22865
|
+
details: `
|
|
22866
|
+
Operates on a single extension by its qualified \`<plugin>/<ext>\`
|
|
22867
|
+
id. With no settingId it prints a table of each declared setting,
|
|
22868
|
+
its effective value, and the config layer that set it. With a
|
|
22869
|
+
settingId + value it coerces the shell string to the declared
|
|
22870
|
+
input-type, validates it, and writes
|
|
22871
|
+
\`plugins.<plugin>.extensions.<ext>.settings.<settingId>\` to
|
|
22872
|
+
settings.json (or settings.local.json for \`secret\` settings).
|
|
22873
|
+
\`--reset\` removes the override so the manifest default applies.
|
|
22874
|
+
Secret values are shown as <redacted>. Run \`sm scan\` to apply.
|
|
22875
|
+
`
|
|
22876
|
+
});
|
|
22877
|
+
id = Option27.String({ required: true });
|
|
22878
|
+
settingId = Option27.String({ required: false });
|
|
22879
|
+
value = Option27.String({ required: false });
|
|
22880
|
+
reset = Option27.Boolean("--reset", false, {
|
|
22881
|
+
description: "Remove the override for <settingId> so the manifest default applies."
|
|
22882
|
+
});
|
|
22883
|
+
pluginDir = Option27.String("--plugin-dir", { required: false });
|
|
22884
|
+
// Read-only when listing; the write / reset paths emit their own
|
|
22885
|
+
// receipt. `sm config` exempts the config family from "done in <…>";
|
|
22886
|
+
// mirror that here for the read path. The write path keeps the line.
|
|
22887
|
+
emitElapsed = true;
|
|
22888
|
+
// CLI orchestrator: each branch is one validation gate (bare id /
|
|
22889
|
+
// unknown extension / no settings / unknown setting) or a mode
|
|
22890
|
+
// dispatch (table vs set vs reset). Splitting per branch scatters the
|
|
22891
|
+
// gate from the value it gates.
|
|
22892
|
+
// eslint-disable-next-line complexity
|
|
22893
|
+
async run() {
|
|
22894
|
+
const ctx = defaultRuntimeContext();
|
|
22895
|
+
const stderrAnsi = this.ansiFor("stderr");
|
|
22896
|
+
if (!this.id.includes("/")) {
|
|
22897
|
+
this.printer.error(
|
|
22898
|
+
tx(PLUGINS_CONFIG_TEXTS.bareId, {
|
|
22899
|
+
glyph: stderrAnsi.red("\u2715"),
|
|
22900
|
+
id: sanitizeForTerminal(this.id),
|
|
22901
|
+
hint: stderrAnsi.dim(
|
|
22902
|
+
tx(PLUGINS_CONFIG_TEXTS.bareIdHint, { id: sanitizeForTerminal(this.id) })
|
|
22903
|
+
)
|
|
22904
|
+
})
|
|
22905
|
+
);
|
|
22906
|
+
return ExitCode.Error;
|
|
22907
|
+
}
|
|
22908
|
+
const plugins = await loadAll({ pluginDir: this.pluginDir });
|
|
22909
|
+
const parsed = parseQualifiedExtensionId(this.id, pluginCatalogue(plugins));
|
|
22910
|
+
if (!parsed.ok) {
|
|
22911
|
+
this.printer.error(renderQualifiedIdError(parsed, this.id, stderrAnsi));
|
|
22912
|
+
return ExitCode.NotFound;
|
|
22913
|
+
}
|
|
22914
|
+
const { pluginId, extId } = parsed;
|
|
22915
|
+
const declarations = resolveDeclaredSettings(pluginId, extId, plugins);
|
|
22916
|
+
if (!declarations || Object.keys(declarations).length === 0) {
|
|
22917
|
+
this.printer.error(
|
|
22918
|
+
tx(PLUGINS_CONFIG_TEXTS.noSettings, {
|
|
22919
|
+
glyph: stderrAnsi.red("\u2715"),
|
|
22920
|
+
id: sanitizeForTerminal(this.id),
|
|
22921
|
+
hint: stderrAnsi.dim(
|
|
22922
|
+
tx(PLUGINS_CONFIG_TEXTS.noSettingsHint, { id: sanitizeForTerminal(this.id) })
|
|
22923
|
+
)
|
|
22924
|
+
})
|
|
22925
|
+
);
|
|
22926
|
+
return ExitCode.NotFound;
|
|
22927
|
+
}
|
|
22928
|
+
if (this.settingId === void 0) {
|
|
22929
|
+
return this.renderTable(pluginId, extId, declarations, ctx.cwd);
|
|
22930
|
+
}
|
|
22931
|
+
const declaration = declarations[this.settingId];
|
|
22932
|
+
if (!declaration) {
|
|
22933
|
+
this.printer.error(
|
|
22934
|
+
tx(PLUGINS_CONFIG_TEXTS.unknownSetting, {
|
|
22935
|
+
glyph: stderrAnsi.red("\u2715"),
|
|
22936
|
+
settingId: sanitizeForTerminal(this.settingId),
|
|
22937
|
+
id: sanitizeForTerminal(this.id),
|
|
22938
|
+
hint: stderrAnsi.dim(
|
|
22939
|
+
tx(PLUGINS_CONFIG_TEXTS.unknownSettingHint, {
|
|
22940
|
+
declared: Object.keys(declarations).map((k) => `'${k}'`).join(", ")
|
|
22941
|
+
})
|
|
22942
|
+
)
|
|
22943
|
+
})
|
|
22944
|
+
);
|
|
22945
|
+
return ExitCode.NotFound;
|
|
22946
|
+
}
|
|
22947
|
+
if (this.reset) {
|
|
22948
|
+
return this.resetSetting(pluginId, extId, this.settingId, declaration, ctx.cwd);
|
|
22949
|
+
}
|
|
22950
|
+
if (this.value === void 0) {
|
|
22951
|
+
return this.renderTable(pluginId, extId, { [this.settingId]: declaration }, ctx.cwd);
|
|
22952
|
+
}
|
|
22953
|
+
return this.writeSetting(pluginId, extId, this.settingId, declaration, this.value, ctx.cwd);
|
|
22954
|
+
}
|
|
22955
|
+
/**
|
|
22956
|
+
* Render the settings table (human) or the resolved set (`--json`).
|
|
22957
|
+
* Effective values come from the kernel settings resolver so the table
|
|
22958
|
+
* shows exactly what `ctx.settings.<id>` would see at scan time
|
|
22959
|
+
* (default overlaid by the config override, validated). Secret values
|
|
22960
|
+
* are redacted.
|
|
22961
|
+
*/
|
|
22962
|
+
renderTable(pluginId, extId, declarations, cwd) {
|
|
22963
|
+
const { effective } = loadConfig({ cwd });
|
|
22964
|
+
const resolved = resolveExtensionSettings({ pluginId, id: extId, settings: declarations }, effective, () => {
|
|
22965
|
+
});
|
|
22966
|
+
if (this.json) {
|
|
22967
|
+
const payload = {};
|
|
22968
|
+
for (const [settingId, declaration] of Object.entries(declarations)) {
|
|
22969
|
+
payload[settingId] = declaration.type === "secret" ? PLUGINS_CONFIG_TEXTS.redacted : resolved[settingId] ?? null;
|
|
22970
|
+
}
|
|
22971
|
+
this.printer.data(JSON.stringify(payload) + "\n");
|
|
22972
|
+
return ExitCode.Ok;
|
|
22973
|
+
}
|
|
22974
|
+
const ansi = this.ansiFor("stdout");
|
|
22975
|
+
const lines = [tx(PLUGINS_CONFIG_TEXTS.tableHeader, { id: `${pluginId}/${extId}` })];
|
|
22976
|
+
const idWidth = Math.max(...Object.keys(declarations).map((k) => k.length));
|
|
22977
|
+
for (const [settingId, declaration] of Object.entries(declarations)) {
|
|
22978
|
+
const display = declaration.type === "secret" ? PLUGINS_CONFIG_TEXTS.redacted : formatValue2(resolved[settingId]);
|
|
22979
|
+
const dotKey = settingDotKey(pluginId, extId, settingId);
|
|
22980
|
+
const source = getValueSource(dotKey, { cwd });
|
|
22981
|
+
const sourceTag = source ? ansi.dim(tx(PLUGINS_CONFIG_TEXTS.tableSourceTag, { source: layerLabel(source) })) : "";
|
|
22982
|
+
lines.push(
|
|
22983
|
+
tx(PLUGINS_CONFIG_TEXTS.tableRow, {
|
|
22984
|
+
settingId: settingId.padEnd(idWidth),
|
|
22985
|
+
value: sanitizeForTerminal(display),
|
|
22986
|
+
sourceTag
|
|
22987
|
+
})
|
|
22988
|
+
);
|
|
22989
|
+
}
|
|
22990
|
+
this.printer.data(lines.join(""));
|
|
22991
|
+
return ExitCode.Ok;
|
|
22992
|
+
}
|
|
22993
|
+
writeSetting(pluginId, extId, settingId, declaration, raw, cwd) {
|
|
22994
|
+
const stderrAnsi = this.ansiFor("stderr");
|
|
22995
|
+
const errGlyph = stderrAnsi.red("\u2715");
|
|
22996
|
+
const coerced = coerceCliValue(declaration, raw);
|
|
22997
|
+
if (!coerced.ok) {
|
|
22998
|
+
this.printer.error(
|
|
22999
|
+
tx(PLUGINS_CONFIG_TEXTS.coerceFailed, {
|
|
23000
|
+
glyph: errGlyph,
|
|
23001
|
+
value: sanitizeForTerminal(raw),
|
|
23002
|
+
type: declaration.type,
|
|
23003
|
+
settingId: sanitizeForTerminal(settingId),
|
|
23004
|
+
hint: stderrAnsi.dim(tx(PLUGINS_CONFIG_TEXTS.coerceFailedHint, { detail: coerced.reason }))
|
|
23005
|
+
})
|
|
23006
|
+
);
|
|
23007
|
+
return ExitCode.Error;
|
|
23008
|
+
}
|
|
23009
|
+
let invalidReason = null;
|
|
23010
|
+
resolveExtensionSettings(
|
|
23011
|
+
{ pluginId, id: extId, settings: { [settingId]: declaration } },
|
|
23012
|
+
{ plugins: { [pluginId]: { extensions: { [extId]: { settings: { [settingId]: coerced.value } } } } } },
|
|
23013
|
+
(message) => {
|
|
23014
|
+
invalidReason = message;
|
|
23015
|
+
}
|
|
23016
|
+
);
|
|
23017
|
+
if (invalidReason !== null) {
|
|
23018
|
+
this.printer.error(
|
|
23019
|
+
tx(PLUGINS_CONFIG_TEXTS.validationFailed, {
|
|
23020
|
+
glyph: errGlyph,
|
|
23021
|
+
settingId: sanitizeForTerminal(settingId),
|
|
23022
|
+
type: declaration.type,
|
|
23023
|
+
reason: invalidReason
|
|
23024
|
+
})
|
|
23025
|
+
);
|
|
23026
|
+
return ExitCode.Error;
|
|
23027
|
+
}
|
|
23028
|
+
const target = declaration.type === "secret" ? "project-local" : "project";
|
|
23029
|
+
const dotKey = settingDotKey(pluginId, extId, settingId);
|
|
23030
|
+
try {
|
|
23031
|
+
writeConfigValue(dotKey, coerced.value, { target, cwd });
|
|
23032
|
+
} catch (err) {
|
|
23033
|
+
return this.renderWriteError(err, settingId);
|
|
23034
|
+
}
|
|
23035
|
+
const ansi = this.ansiFor("stdout");
|
|
23036
|
+
const display = declaration.type === "secret" ? PLUGINS_CONFIG_TEXTS.redacted : formatValue2(coerced.value);
|
|
23037
|
+
const path = target === "project-local" ? defaultLocalSettingsPath(cwd) : defaultSettingsPath(cwd);
|
|
23038
|
+
this.printer.data(
|
|
23039
|
+
tx(PLUGINS_CONFIG_TEXTS.setWritten, {
|
|
23040
|
+
glyph: ansi.green("\u2713"),
|
|
23041
|
+
settingId,
|
|
23042
|
+
value: sanitizeForTerminal(display),
|
|
23043
|
+
id: `${pluginId}/${extId}`,
|
|
23044
|
+
wroteTag: ansi.dim(tx(PLUGINS_CONFIG_TEXTS.setWroteTag, { path: relativeIfBelow(path, cwd) }))
|
|
23045
|
+
})
|
|
23046
|
+
);
|
|
23047
|
+
this.printRescanFooter();
|
|
23048
|
+
return ExitCode.Ok;
|
|
23049
|
+
}
|
|
23050
|
+
resetSetting(pluginId, extId, settingId, declaration, cwd) {
|
|
23051
|
+
const target = declaration.type === "secret" ? "project-local" : "project";
|
|
23052
|
+
const dotKey = settingDotKey(pluginId, extId, settingId);
|
|
23053
|
+
const ansi = this.ansiFor("stdout");
|
|
23054
|
+
let removed;
|
|
23055
|
+
try {
|
|
23056
|
+
removed = removeConfigValue(dotKey, { target, cwd });
|
|
23057
|
+
} catch (err) {
|
|
23058
|
+
return this.renderWriteError(err, settingId);
|
|
23059
|
+
}
|
|
23060
|
+
if (!removed) {
|
|
23061
|
+
this.printer.data(
|
|
23062
|
+
tx(PLUGINS_CONFIG_TEXTS.resetNoOverride, {
|
|
23063
|
+
glyph: ansi.green("\u2713"),
|
|
23064
|
+
settingId,
|
|
23065
|
+
id: `${pluginId}/${extId}`
|
|
23066
|
+
})
|
|
23067
|
+
);
|
|
23068
|
+
return ExitCode.Ok;
|
|
23069
|
+
}
|
|
23070
|
+
const path = target === "project-local" ? defaultLocalSettingsPath(cwd) : defaultSettingsPath(cwd);
|
|
23071
|
+
this.printer.data(
|
|
23072
|
+
tx(PLUGINS_CONFIG_TEXTS.resetRemoved, {
|
|
23073
|
+
glyph: ansi.green("\u2713"),
|
|
23074
|
+
settingId,
|
|
23075
|
+
id: `${pluginId}/${extId}`,
|
|
23076
|
+
wroteTag: ansi.dim(tx(PLUGINS_CONFIG_TEXTS.setWroteTag, { path: relativeIfBelow(path, cwd) }))
|
|
23077
|
+
})
|
|
23078
|
+
);
|
|
23079
|
+
this.printRescanFooter();
|
|
23080
|
+
return ExitCode.Ok;
|
|
23081
|
+
}
|
|
23082
|
+
renderWriteError(err, settingId) {
|
|
23083
|
+
const ansi = this.ansiFor("stderr");
|
|
23084
|
+
const glyph = ansi.red("\u2715");
|
|
23085
|
+
if (err instanceof ForbiddenSegmentError) {
|
|
23086
|
+
this.printer.error(
|
|
23087
|
+
tx(PLUGINS_CONFIG_TEXTS.writeFailed, { glyph, settingId, message: err.message })
|
|
23088
|
+
);
|
|
23089
|
+
return ExitCode.Error;
|
|
23090
|
+
}
|
|
23091
|
+
if (err instanceof ProjectLocalOnlyKeyError) {
|
|
23092
|
+
this.printer.error(
|
|
23093
|
+
tx(PLUGINS_CONFIG_TEXTS.writeFailed, { glyph, settingId, message: err.message })
|
|
23094
|
+
);
|
|
23095
|
+
return ExitCode.Error;
|
|
23096
|
+
}
|
|
23097
|
+
if (err instanceof ConfigValidationError) {
|
|
23098
|
+
this.printer.error(
|
|
23099
|
+
tx(PLUGINS_CONFIG_TEXTS.writeFailed, { glyph, settingId, message: err.errors })
|
|
23100
|
+
);
|
|
23101
|
+
return ExitCode.Error;
|
|
23102
|
+
}
|
|
23103
|
+
throw err;
|
|
23104
|
+
}
|
|
23105
|
+
printRescanFooter() {
|
|
23106
|
+
if (this.json) return;
|
|
23107
|
+
const ansi = this.ansiFor("stdout");
|
|
23108
|
+
this.printer.data(
|
|
23109
|
+
tx(PLUGINS_CONFIG_TEXTS.rescanFooter, {
|
|
23110
|
+
hint: ansi.dim(PLUGINS_CONFIG_TEXTS.rescanFooterText)
|
|
23111
|
+
})
|
|
23112
|
+
);
|
|
23113
|
+
}
|
|
23114
|
+
};
|
|
23115
|
+
function resolveDeclaredSettings(pluginId, extId, plugins) {
|
|
23116
|
+
for (const plugin of builtInPlugins) {
|
|
23117
|
+
if (plugin.id !== pluginId) continue;
|
|
23118
|
+
for (const ext of plugin.extensions) {
|
|
23119
|
+
if (ext.id === extId) return ext.settings;
|
|
23120
|
+
}
|
|
23121
|
+
}
|
|
23122
|
+
const match = plugins.find((p) => p.id === pluginId);
|
|
23123
|
+
const userExt = match?.extensions?.find((e) => e.id === extId);
|
|
23124
|
+
return readInstanceSettings(userExt?.instance);
|
|
23125
|
+
}
|
|
23126
|
+
function readInstanceSettings(instance) {
|
|
23127
|
+
if (instance === null || typeof instance !== "object") return void 0;
|
|
23128
|
+
const settings2 = instance.settings;
|
|
23129
|
+
if (settings2 && typeof settings2 === "object" && !Array.isArray(settings2)) {
|
|
23130
|
+
return settings2;
|
|
23131
|
+
}
|
|
23132
|
+
return void 0;
|
|
23133
|
+
}
|
|
23134
|
+
function coerceCliValue(declaration, raw) {
|
|
23135
|
+
switch (declaration.type) {
|
|
23136
|
+
case "integer":
|
|
23137
|
+
case "number": {
|
|
23138
|
+
const n = Number(raw);
|
|
23139
|
+
if (raw.trim() === "" || Number.isNaN(n)) return { ok: false, reason: "expected a numeric value" };
|
|
23140
|
+
return { ok: true, value: n };
|
|
23141
|
+
}
|
|
23142
|
+
case "boolean-flag": {
|
|
23143
|
+
if (raw === "true") return { ok: true, value: true };
|
|
23144
|
+
if (raw === "false") return { ok: true, value: false };
|
|
23145
|
+
return { ok: false, reason: "expected `true` or `false`" };
|
|
23146
|
+
}
|
|
23147
|
+
case "string-list":
|
|
23148
|
+
case "enum-multipick":
|
|
23149
|
+
case "key-value-list":
|
|
23150
|
+
return parseJsonValue(raw);
|
|
23151
|
+
case "path-glob":
|
|
23152
|
+
return declaration.multiple === true ? parseJsonValue(raw) : { ok: true, value: raw };
|
|
23153
|
+
case "single-string":
|
|
23154
|
+
case "enum-pick":
|
|
23155
|
+
case "regex":
|
|
23156
|
+
case "secret":
|
|
23157
|
+
return { ok: true, value: raw };
|
|
23158
|
+
default: {
|
|
23159
|
+
const _exhaustive = declaration;
|
|
23160
|
+
return { ok: false, reason: `unknown input-type: ${String(_exhaustive.type)}` };
|
|
23161
|
+
}
|
|
23162
|
+
}
|
|
23163
|
+
}
|
|
23164
|
+
function parseJsonValue(raw) {
|
|
23165
|
+
try {
|
|
23166
|
+
return { ok: true, value: JSON.parse(raw) };
|
|
23167
|
+
} catch {
|
|
23168
|
+
return { ok: false, reason: 'expected a JSON value (e.g. ["a","b"])' };
|
|
23169
|
+
}
|
|
23170
|
+
}
|
|
23171
|
+
function settingDotKey(pluginId, extId, settingId) {
|
|
23172
|
+
return `plugins.${pluginId}.extensions.${extId}.settings.${settingId}`;
|
|
23173
|
+
}
|
|
23174
|
+
function formatValue2(value) {
|
|
23175
|
+
if (value === void 0 || value === null) return "null";
|
|
23176
|
+
if (Array.isArray(value) || typeof value === "object") return JSON.stringify(value);
|
|
23177
|
+
return String(value);
|
|
23178
|
+
}
|
|
23179
|
+
var LAYER_LABEL = {
|
|
23180
|
+
defaults: "default",
|
|
23181
|
+
project: "settings.json",
|
|
23182
|
+
"project-local": "settings.local.json",
|
|
23183
|
+
override: "override"
|
|
23184
|
+
};
|
|
23185
|
+
function layerLabel(layer) {
|
|
23186
|
+
return LAYER_LABEL[layer];
|
|
23187
|
+
}
|
|
23188
|
+
|
|
22809
23189
|
// cli/commands/plugins.ts
|
|
22810
23190
|
var PLUGIN_COMMANDS = [
|
|
22811
23191
|
PluginsListCommand,
|
|
@@ -22815,13 +23195,14 @@ var PLUGIN_COMMANDS = [
|
|
|
22815
23195
|
PluginsDisableCommand,
|
|
22816
23196
|
PluginsCreateCommand,
|
|
22817
23197
|
PluginsSlotsListCommand,
|
|
22818
|
-
PluginsUpgradeCommand
|
|
23198
|
+
PluginsUpgradeCommand,
|
|
23199
|
+
PluginsConfigCommand
|
|
22819
23200
|
];
|
|
22820
23201
|
|
|
22821
23202
|
// cli/commands/refresh.ts
|
|
22822
23203
|
import { readFile as readFile4 } from "fs/promises";
|
|
22823
23204
|
import { resolve as resolve34 } from "path";
|
|
22824
|
-
import { Command as
|
|
23205
|
+
import { Command as Command30, Option as Option28 } from "clipanion";
|
|
22825
23206
|
|
|
22826
23207
|
// cli/i18n/refresh.texts.ts
|
|
22827
23208
|
var REFRESH_TEXTS = {
|
|
@@ -22877,7 +23258,7 @@ var REFRESH_TEXTS = {
|
|
|
22877
23258
|
// cli/commands/refresh.ts
|
|
22878
23259
|
var RefreshCommand = class extends SmCommand {
|
|
22879
23260
|
static paths = [["refresh"]];
|
|
22880
|
-
static usage =
|
|
23261
|
+
static usage = Command30.Usage({
|
|
22881
23262
|
category: "Scan",
|
|
22882
23263
|
description: "Refresh enrichment rows: granular (single node) or batch (every stale row).",
|
|
22883
23264
|
details: `
|
|
@@ -22899,11 +23280,11 @@ var RefreshCommand = class extends SmCommand {
|
|
|
22899
23280
|
["Refresh every node with stale enrichments", "$0 refresh --stale"]
|
|
22900
23281
|
]
|
|
22901
23282
|
});
|
|
22902
|
-
nodePath =
|
|
22903
|
-
stale =
|
|
23283
|
+
nodePath = Option28.String({ name: "node", required: false });
|
|
23284
|
+
stale = Option28.Boolean("--stale", false, {
|
|
22904
23285
|
description: "Refresh every node carrying a stale enrichment row (no-op in this revision; reserved for future Action-prob enrichments)."
|
|
22905
23286
|
});
|
|
22906
|
-
noPlugins =
|
|
23287
|
+
noPlugins = Option28.Boolean("--no-plugins", false, {
|
|
22907
23288
|
description: "Skip drop-in plugin discovery; use only the built-in extractor set."
|
|
22908
23289
|
});
|
|
22909
23290
|
// The remaining cyclomatic count comes from CLI ergonomics that don't
|
|
@@ -22937,9 +23318,11 @@ var RefreshCommand = class extends SmCommand {
|
|
|
22937
23318
|
const pluginRuntime = this.noPlugins ? emptyPluginRuntime() : await loadPluginRuntime();
|
|
22938
23319
|
pluginRuntime.emitWarnings(this.printer);
|
|
22939
23320
|
listBuiltIns();
|
|
23321
|
+
const refreshCfg = loadConfig({ cwd: ctx.cwd }).effective;
|
|
22940
23322
|
const composed = composeScanExtensions({
|
|
22941
23323
|
noBuiltIns: false,
|
|
22942
23324
|
pluginRuntime,
|
|
23325
|
+
resolveSettings: buildSettingsResolver(refreshCfg),
|
|
22943
23326
|
killSwitches: readConformanceKillSwitches()
|
|
22944
23327
|
});
|
|
22945
23328
|
const allExtractors = composed?.extractors ?? [];
|
|
@@ -23205,7 +23588,7 @@ var IntentionalFailCommand = class extends SmCommand {
|
|
|
23205
23588
|
};
|
|
23206
23589
|
|
|
23207
23590
|
// cli/commands/scan.ts
|
|
23208
|
-
import { Command as
|
|
23591
|
+
import { Command as Command32, Option as Option30 } from "clipanion";
|
|
23209
23592
|
|
|
23210
23593
|
// kernel/util/format-bytes.ts
|
|
23211
23594
|
var UNITS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
|
|
@@ -23357,7 +23740,7 @@ var SCAN_TEXTS = {
|
|
|
23357
23740
|
};
|
|
23358
23741
|
|
|
23359
23742
|
// cli/commands/watch.ts
|
|
23360
|
-
import { Command as
|
|
23743
|
+
import { Command as Command31, Option as Option29 } from "clipanion";
|
|
23361
23744
|
|
|
23362
23745
|
// core/watcher/runtime.ts
|
|
23363
23746
|
import { dirname as dirname18 } from "path";
|
|
@@ -23479,7 +23862,8 @@ function createWatcherRuntime(opts) {
|
|
|
23479
23862
|
const composeOpts = {
|
|
23480
23863
|
noBuiltIns: opts.noBuiltIns,
|
|
23481
23864
|
pluginRuntime,
|
|
23482
|
-
resolveEnabled: resolveEnabledOverride
|
|
23865
|
+
resolveEnabled: resolveEnabledOverride,
|
|
23866
|
+
resolveSettings: buildSettingsResolver(cfg)
|
|
23483
23867
|
};
|
|
23484
23868
|
if (opts.killSwitches) composeOpts.killSwitches = opts.killSwitches;
|
|
23485
23869
|
const composed = composeScanExtensions(composeOpts);
|
|
@@ -23896,7 +24280,7 @@ async function runWatchLoop(opts) {
|
|
|
23896
24280
|
}
|
|
23897
24281
|
var WatchCommand = class extends SmCommand {
|
|
23898
24282
|
static paths = [["watch"]];
|
|
23899
|
-
static usage =
|
|
24283
|
+
static usage = Command31.Usage({
|
|
23900
24284
|
category: "Scan",
|
|
23901
24285
|
description: "Watch roots and run an incremental scan after each debounced batch of filesystem events.",
|
|
23902
24286
|
details: `
|
|
@@ -23920,21 +24304,21 @@ var WatchCommand = class extends SmCommand {
|
|
|
23920
24304
|
["Stream ScanResult per batch as ndjson", "$0 watch --json"]
|
|
23921
24305
|
]
|
|
23922
24306
|
});
|
|
23923
|
-
roots =
|
|
23924
|
-
noTokens =
|
|
24307
|
+
roots = Option29.Rest({ name: "roots" });
|
|
24308
|
+
noTokens = Option29.Boolean("--no-tokens", false, {
|
|
23925
24309
|
description: "Skip per-node token counts (cl100k_base BPE)."
|
|
23926
24310
|
});
|
|
23927
|
-
strict =
|
|
24311
|
+
strict = Option29.Boolean("--strict", false, {
|
|
23928
24312
|
description: "Promote frontmatter-validation findings from warn to error inside each batch. Does not change the watcher exit code."
|
|
23929
24313
|
});
|
|
23930
|
-
noPlugins =
|
|
24314
|
+
noPlugins = Option29.Boolean("--no-plugins", false, {
|
|
23931
24315
|
description: "Skip drop-in plugin discovery for the watcher session."
|
|
23932
24316
|
});
|
|
23933
|
-
maxConsecutiveFailures =
|
|
24317
|
+
maxConsecutiveFailures = Option29.String("--max-consecutive-failures", {
|
|
23934
24318
|
required: false,
|
|
23935
24319
|
description: "Shut down with exit 2 after N consecutive batch failures (default 5; 0 disables the breaker)."
|
|
23936
24320
|
});
|
|
23937
|
-
maxNodes =
|
|
24321
|
+
maxNodes = Option29.String("--max-nodes", {
|
|
23938
24322
|
required: false,
|
|
23939
24323
|
description: "Per-batch override of scan.maxNodes (default 256). Bidirectional: raises OR lowers the recommended cap on classified nodes. When a batch hits the cap, additional files are dropped and the UI surfaces the persistent oversized banner. Validation: integer >= 1."
|
|
23940
24324
|
});
|
|
@@ -24001,7 +24385,7 @@ function parseMaxNodesLimit(raw, stderr, noColor) {
|
|
|
24001
24385
|
// cli/commands/scan.ts
|
|
24002
24386
|
var ScanCommand = class extends SmCommand {
|
|
24003
24387
|
static paths = [["scan"]];
|
|
24004
|
-
static usage =
|
|
24388
|
+
static usage = Command32.Usage({
|
|
24005
24389
|
category: "Scan",
|
|
24006
24390
|
description: "Scan roots for markdown nodes, run extractors and analyzers.",
|
|
24007
24391
|
details: `
|
|
@@ -24036,35 +24420,35 @@ var ScanCommand = class extends SmCommand {
|
|
|
24036
24420
|
["What would the next incremental scan persist?", "$0 scan --changed -n --json"]
|
|
24037
24421
|
]
|
|
24038
24422
|
});
|
|
24039
|
-
roots =
|
|
24040
|
-
noBuiltIns =
|
|
24423
|
+
roots = Option30.Rest({ name: "roots" });
|
|
24424
|
+
noBuiltIns = Option30.Boolean("--no-built-ins", false, {
|
|
24041
24425
|
description: "Skip the built-in extension set. Yields a zero-filled ScanResult (kernel-empty-boot parity); skips DB persistence."
|
|
24042
24426
|
});
|
|
24043
|
-
noPlugins =
|
|
24427
|
+
noPlugins = Option30.Boolean("--no-plugins", false, {
|
|
24044
24428
|
description: "Skip drop-in plugin discovery. Only the built-in set runs. Combine with --no-built-ins for a fully empty pipeline."
|
|
24045
24429
|
});
|
|
24046
|
-
noTokens =
|
|
24430
|
+
noTokens = Option30.Boolean("--no-tokens", false, {
|
|
24047
24431
|
description: "Skip per-node token counts (cl100k_base BPE). Leaves node.tokens undefined; spec-valid since the field is optional."
|
|
24048
24432
|
});
|
|
24049
|
-
dryRun =
|
|
24433
|
+
dryRun = Option30.Boolean("-n,--dry-run", false, {
|
|
24050
24434
|
description: "Run the scan in memory and skip every DB write. Combined with --changed, still opens the DB read-side to load the prior snapshot."
|
|
24051
24435
|
});
|
|
24052
|
-
changed =
|
|
24436
|
+
changed = Option30.Boolean("--changed", false, {
|
|
24053
24437
|
description: "Incremental scan: reuse unchanged nodes from the persisted prior snapshot. Degrades to a full scan if no prior snapshot exists."
|
|
24054
24438
|
});
|
|
24055
|
-
allowEmpty =
|
|
24439
|
+
allowEmpty = Option30.Boolean("--allow-empty", false, {
|
|
24056
24440
|
description: "Allow a zero-result scan to wipe an already-populated DB (replace-all replace by zero rows). Off by default to avoid the typo-trap where an invalid root silently clears your data."
|
|
24057
24441
|
});
|
|
24058
|
-
strict =
|
|
24442
|
+
strict = Option30.Boolean("--strict", false, {
|
|
24059
24443
|
description: "Promote frontmatter-validation findings from warn to error (exit code 1 on any violation). Overrides scan.strict from config when both are set."
|
|
24060
24444
|
});
|
|
24061
|
-
watch =
|
|
24445
|
+
watch = Option30.Boolean("--watch", false, {
|
|
24062
24446
|
description: "Long-running mode: watch the roots and trigger an incremental scan after each debounced batch of filesystem events. Alias of `sm watch`."
|
|
24063
24447
|
});
|
|
24064
|
-
yes =
|
|
24448
|
+
yes = Option30.Boolean("--yes", false, {
|
|
24065
24449
|
description: "Non-interactive mode. For ambiguous activeProvider auto-detect, multiple provider markers (.claude/, .codex/, AGENTS.md, .cursor/) under the scan tree exit non-zero instead of prompting; set the lens manually via `sm config set activeProvider <id>` and re-run. Also auto-confirms the pre-1.0 schema-drift rebuild (when the DB was written by a different skill-map major.minor it is deleted and regenerated) instead of prompting."
|
|
24066
24450
|
});
|
|
24067
|
-
maxNodes =
|
|
24451
|
+
maxNodes = Option30.String("--max-nodes", {
|
|
24068
24452
|
required: false,
|
|
24069
24453
|
description: "Per-invocation override of `scan.maxNodes` (default 256). Bidirectional: raises OR lowers the recommended cap on classified nodes. When the walker hits the cap, additional files are dropped and the scan is marked oversized in scan_meta (the UI raises a persistent banner pointing at the .skillmapignore editor in Settings \u2192 Project). Validation: integer >= 1."
|
|
24070
24454
|
});
|
|
@@ -24405,10 +24789,10 @@ function countNoun(count3, singular, plural) {
|
|
|
24405
24789
|
|
|
24406
24790
|
// cli/commands/scan-compare.ts
|
|
24407
24791
|
import { access, readFile as readFile5 } from "fs/promises";
|
|
24408
|
-
import { Command as
|
|
24792
|
+
import { Command as Command33, Option as Option31 } from "clipanion";
|
|
24409
24793
|
var ScanCompareCommand = class extends SmCommand {
|
|
24410
24794
|
static paths = [["scan", "compare-with"]];
|
|
24411
|
-
static usage =
|
|
24795
|
+
static usage = Command33.Usage({
|
|
24412
24796
|
category: "Scan",
|
|
24413
24797
|
description: "Run a fresh scan in memory and emit a delta against the saved ScanResult dump at <dump>. Read-only.",
|
|
24414
24798
|
details: `
|
|
@@ -24436,15 +24820,15 @@ var ScanCompareCommand = class extends SmCommand {
|
|
|
24436
24820
|
["JSON output for tooling", "$0 scan compare-with baseline.json --json"]
|
|
24437
24821
|
]
|
|
24438
24822
|
});
|
|
24439
|
-
dump =
|
|
24440
|
-
roots =
|
|
24441
|
-
noTokens =
|
|
24823
|
+
dump = Option31.String({ required: true });
|
|
24824
|
+
roots = Option31.Rest({ name: "roots" });
|
|
24825
|
+
noTokens = Option31.Boolean("--no-tokens", false, {
|
|
24442
24826
|
description: "Skip per-node token counts during the fresh scan."
|
|
24443
24827
|
});
|
|
24444
|
-
strict =
|
|
24828
|
+
strict = Option31.Boolean("--strict", false, {
|
|
24445
24829
|
description: "Promote layered-config warnings and frontmatter-validation findings from warn to error."
|
|
24446
24830
|
});
|
|
24447
|
-
noPlugins =
|
|
24831
|
+
noPlugins = Option31.Boolean("--no-plugins", false, {
|
|
24448
24832
|
description: "Skip drop-in plugin discovery."
|
|
24449
24833
|
});
|
|
24450
24834
|
// Cyclomatic count comes from CLI ergonomics: 3 distinct try/catch
|
|
@@ -24486,6 +24870,7 @@ var ScanCompareCommand = class extends SmCommand {
|
|
|
24486
24870
|
const composedExtensions = composeScanExtensions({
|
|
24487
24871
|
noBuiltIns: false,
|
|
24488
24872
|
pluginRuntime,
|
|
24873
|
+
resolveSettings: buildSettingsResolver(cfg),
|
|
24489
24874
|
killSwitches: readConformanceKillSwitches()
|
|
24490
24875
|
});
|
|
24491
24876
|
let current;
|
|
@@ -24649,7 +25034,7 @@ function renderDeltaIssues(issues) {
|
|
|
24649
25034
|
// cli/commands/serve.ts
|
|
24650
25035
|
import { spawn as spawn2 } from "child_process";
|
|
24651
25036
|
import { existsSync as existsSync31 } from "fs";
|
|
24652
|
-
import { Command as
|
|
25037
|
+
import { Command as Command34, Option as Option32 } from "clipanion";
|
|
24653
25038
|
|
|
24654
25039
|
// kernel/util/dev-mode.ts
|
|
24655
25040
|
import { sep as sep6 } from "path";
|
|
@@ -24910,8 +25295,22 @@ var SERVER_TEXTS = {
|
|
|
24910
25295
|
// The single-id variants above still apply for per-entry validation
|
|
24911
25296
|
// (unknown id, granularity mismatch, lock); these cover the
|
|
24912
25297
|
// body-shape level.
|
|
24913
|
-
pluginsChangesRequired: "Request body must include a `changes` array of `{ id, enabled }` entries.",
|
|
24914
|
-
pluginsChangeMalformed: "Each entry in `changes` must have a string `id`
|
|
25298
|
+
pluginsChangesRequired: "Request body must include a `changes` array of `{ id, enabled?, settings? }` entries.",
|
|
25299
|
+
pluginsChangeMalformed: "Each entry in `changes` must have a string `id` plus at least one of `enabled` (boolean) or `settings` (object).",
|
|
25300
|
+
// 400, a bulk change carries `settings` against a bare plugin id.
|
|
25301
|
+
// Settings are per-extension, so they require a qualified
|
|
25302
|
+
// `<plugin>/<ext>` id; a bare plugin id is the wrong granularity.
|
|
25303
|
+
pluginsSettingsRequireQualifiedId: 'Settings can only be written on a qualified `<plugin>/<ext>` id, not the bare plugin id "{{id}}".',
|
|
25304
|
+
// 400, a bulk change carries `settings` for an extension that declares
|
|
25305
|
+
// none in its manifest.
|
|
25306
|
+
pluginsSettingsNoneDeclared: 'Extension "{{pluginId}}/{{extensionId}}" declares no configurable settings.',
|
|
25307
|
+
// 400, a `settings` entry names a settingId the extension does not
|
|
25308
|
+
// declare, or its value fails the declared input-type's rules. The
|
|
25309
|
+
// `{{reason}}` is the resolver's per-type validation message.
|
|
25310
|
+
pluginsSettingsInvalid: 'Invalid setting "{{settingId}}" for "{{pluginId}}/{{extensionId}}": {{reason}}.',
|
|
25311
|
+
// 500, a settings write failed at persist time (AJV revalidation of
|
|
25312
|
+
// the merged config file rejected the result).
|
|
25313
|
+
pluginsSettingsPersistFailed: 'Failed to persist settings for "{{id}}": {{message}}.',
|
|
24915
25314
|
// ---- preferences route (routes/preferences.ts) --------------------------
|
|
24916
25315
|
//
|
|
24917
25316
|
// GET / PATCH /api/preferences. The PATCH body is shaped
|
|
@@ -25425,13 +25824,15 @@ function registerGraphRoute(app, deps) {
|
|
|
25425
25824
|
}
|
|
25426
25825
|
function renderGraphPayload(formatter, loaded) {
|
|
25427
25826
|
const scan = loaded ?? { nodes: [], links: [], issues: [] };
|
|
25827
|
+
const settings2 = formatter.resolvedSettings ?? {};
|
|
25428
25828
|
if (loaded === null) {
|
|
25429
|
-
return formatter.format({ nodes: scan.nodes, links: scan.links, issues: scan.issues });
|
|
25829
|
+
return formatter.format({ nodes: scan.nodes, links: scan.links, issues: scan.issues, settings: settings2 });
|
|
25430
25830
|
}
|
|
25431
25831
|
return formatter.format({
|
|
25432
25832
|
nodes: scan.nodes,
|
|
25433
25833
|
links: scan.links,
|
|
25434
25834
|
issues: scan.issues,
|
|
25835
|
+
settings: settings2,
|
|
25435
25836
|
scanResult: loaded
|
|
25436
25837
|
});
|
|
25437
25838
|
}
|
|
@@ -25891,6 +26292,72 @@ function normalizeArrayIndices(path) {
|
|
|
25891
26292
|
return path.replace(/\/\d+(?=\/|$)/g, "/*");
|
|
25892
26293
|
}
|
|
25893
26294
|
|
|
26295
|
+
// server/routes/plugins-settings.ts
|
|
26296
|
+
function readManifestSettings(manifestLike) {
|
|
26297
|
+
if (manifestLike === null || typeof manifestLike !== "object") return void 0;
|
|
26298
|
+
const settings2 = manifestLike.settings;
|
|
26299
|
+
if (settings2 && typeof settings2 === "object" && !Array.isArray(settings2)) {
|
|
26300
|
+
return settings2;
|
|
26301
|
+
}
|
|
26302
|
+
return void 0;
|
|
26303
|
+
}
|
|
26304
|
+
function projectExtensionSettings(pluginId, extId, declarations, config) {
|
|
26305
|
+
if (!declarations || Object.keys(declarations).length === 0) return {};
|
|
26306
|
+
const settings2 = Object.entries(declarations).map(
|
|
26307
|
+
([id, declaration]) => ({ ...declaration, id })
|
|
26308
|
+
);
|
|
26309
|
+
const ref = { pluginId, id: extId, settings: declarations };
|
|
26310
|
+
const resolved = resolveExtensionSettings(ref, config, () => {
|
|
26311
|
+
});
|
|
26312
|
+
const settingValues = {};
|
|
26313
|
+
const secretSet = [];
|
|
26314
|
+
for (const [id, declaration] of Object.entries(declarations)) {
|
|
26315
|
+
if (declaration.type === "secret") {
|
|
26316
|
+
if (Object.prototype.hasOwnProperty.call(resolved, id)) secretSet.push(id);
|
|
26317
|
+
continue;
|
|
26318
|
+
}
|
|
26319
|
+
if (Object.prototype.hasOwnProperty.call(resolved, id)) {
|
|
26320
|
+
settingValues[id] = resolved[id];
|
|
26321
|
+
}
|
|
26322
|
+
}
|
|
26323
|
+
return {
|
|
26324
|
+
settings: settings2,
|
|
26325
|
+
settingValues,
|
|
26326
|
+
...secretSet.length > 0 ? { secretSettingsSet: secretSet } : {}
|
|
26327
|
+
};
|
|
26328
|
+
}
|
|
26329
|
+
function validateSettingsPatch(pluginId, extId, declarations, patch) {
|
|
26330
|
+
for (const [settingId, value] of Object.entries(patch)) {
|
|
26331
|
+
const declaration = declarations?.[settingId];
|
|
26332
|
+
if (!declaration) {
|
|
26333
|
+
return {
|
|
26334
|
+
settingId,
|
|
26335
|
+
reason: `extension "${pluginId}/${extId}" declares no setting "${settingId}"`
|
|
26336
|
+
};
|
|
26337
|
+
}
|
|
26338
|
+
let invalidReason = null;
|
|
26339
|
+
resolveExtensionSettings(
|
|
26340
|
+
{ pluginId, id: extId, settings: { [settingId]: declaration } },
|
|
26341
|
+
{ plugins: { [pluginId]: { extensions: { [extId]: { settings: { [settingId]: value } } } } } },
|
|
26342
|
+
(message) => {
|
|
26343
|
+
invalidReason = message;
|
|
26344
|
+
}
|
|
26345
|
+
);
|
|
26346
|
+
if (invalidReason !== null) {
|
|
26347
|
+
return { settingId, reason: invalidReason };
|
|
26348
|
+
}
|
|
26349
|
+
}
|
|
26350
|
+
return null;
|
|
26351
|
+
}
|
|
26352
|
+
function persistSettingsPatch(pluginId, extId, declarations, patch, cwd) {
|
|
26353
|
+
for (const [settingId, value] of Object.entries(patch)) {
|
|
26354
|
+
const declaration = declarations?.[settingId];
|
|
26355
|
+
const target = declaration?.type === "secret" ? "project-local" : "project";
|
|
26356
|
+
const dotKey = `plugins.${pluginId}.extensions.${extId}.settings.${settingId}`;
|
|
26357
|
+
writeConfigValue(dotKey, value, { target, cwd });
|
|
26358
|
+
}
|
|
26359
|
+
}
|
|
26360
|
+
|
|
25894
26361
|
// server/routes/plugins.ts
|
|
25895
26362
|
var SINGLE_PATCH_BODY_SCHEMA = {
|
|
25896
26363
|
type: "object",
|
|
@@ -25920,10 +26387,18 @@ var BULK_PATCH_BODY_SCHEMA = {
|
|
|
25920
26387
|
items: {
|
|
25921
26388
|
type: "object",
|
|
25922
26389
|
additionalProperties: false,
|
|
25923
|
-
|
|
26390
|
+
// `id` is the only mandatory field; a change carries `enabled`,
|
|
26391
|
+
// `settings`, or both. `minProperties: 2` enforces at least one
|
|
26392
|
+
// of the two beyond `id` (an `{ id }`-only entry is a no-op the
|
|
26393
|
+
// client should not send). Per-setting type validation runs in
|
|
26394
|
+
// code against the manifest, so `settings` is a permissive
|
|
26395
|
+
// object here (the body schema only fixes its container shape).
|
|
26396
|
+
required: ["id"],
|
|
26397
|
+
minProperties: 2,
|
|
25924
26398
|
properties: {
|
|
25925
26399
|
id: { type: "string", minLength: 1 },
|
|
25926
|
-
enabled: { type: "boolean" }
|
|
26400
|
+
enabled: { type: "boolean" },
|
|
26401
|
+
settings: { type: "object" }
|
|
25927
26402
|
}
|
|
25928
26403
|
}
|
|
25929
26404
|
}
|
|
@@ -25935,7 +26410,13 @@ var parseBulkPatchBody = makeBodyValidator(BULK_PATCH_BODY_SCHEMA, {
|
|
|
25935
26410
|
invalid: SERVER_TEXTS.pluginsChangeMalformed,
|
|
25936
26411
|
mapping: {
|
|
25937
26412
|
"/changes:required": SERVER_TEXTS.pluginsChangesRequired,
|
|
25938
|
-
"/changes:type:array": SERVER_TEXTS.pluginsChangesRequired
|
|
26413
|
+
"/changes:type:array": SERVER_TEXTS.pluginsChangesRequired,
|
|
26414
|
+
// A change with neither `enabled` nor `settings` (just `id`) trips
|
|
26415
|
+
// `minProperties`; surface the same "malformed entry" message.
|
|
26416
|
+
"/changes/*:minProperties": SERVER_TEXTS.pluginsChangeMalformed,
|
|
26417
|
+
"/changes/*:type:object": SERVER_TEXTS.pluginsChangeMalformed,
|
|
26418
|
+
"/changes/*/settings:type:object": SERVER_TEXTS.pluginsChangeMalformed,
|
|
26419
|
+
"/changes/*/enabled:type:boolean": SERVER_TEXTS.pluginsChangeMalformed
|
|
25939
26420
|
}
|
|
25940
26421
|
});
|
|
25941
26422
|
function registerPluginsRoute(app, deps) {
|
|
@@ -26019,17 +26500,24 @@ function registerPluginsRoute(app, deps) {
|
|
|
26019
26500
|
});
|
|
26020
26501
|
}
|
|
26021
26502
|
function listItems(deps, resolveEnabled) {
|
|
26503
|
+
const config = deps.configService.effective();
|
|
26022
26504
|
return [
|
|
26023
|
-
...deps.options.noBuiltIns ? [] : buildBuiltInItems(resolveEnabled),
|
|
26024
|
-
...buildDiscoveredItems(deps.pluginRuntime.discovered, deps, resolveEnabled)
|
|
26505
|
+
...deps.options.noBuiltIns ? [] : buildBuiltInItems(resolveEnabled, config),
|
|
26506
|
+
...buildDiscoveredItems(deps.pluginRuntime.discovered, deps, resolveEnabled, config)
|
|
26025
26507
|
];
|
|
26026
26508
|
}
|
|
26027
|
-
function buildBuiltInItems(resolveEnabled) {
|
|
26509
|
+
function buildBuiltInItems(resolveEnabled, config) {
|
|
26028
26510
|
return sortPluginsForPresentation(builtInPlugins).map((plugin) => {
|
|
26029
26511
|
const pluginLocked = isPluginLocked(plugin.id);
|
|
26030
26512
|
const extensions = plugin.extensions.map((ext) => {
|
|
26031
26513
|
const qualified = qualifiedExtensionId(plugin.id, ext.id);
|
|
26032
26514
|
const extLocked = pluginLocked || isPluginLocked(qualified);
|
|
26515
|
+
const settings2 = projectExtensionSettings(
|
|
26516
|
+
plugin.id,
|
|
26517
|
+
ext.id,
|
|
26518
|
+
readManifestSettings(ext),
|
|
26519
|
+
config
|
|
26520
|
+
);
|
|
26033
26521
|
return {
|
|
26034
26522
|
id: ext.id,
|
|
26035
26523
|
kind: ext.kind,
|
|
@@ -26037,7 +26525,8 @@ function buildBuiltInItems(resolveEnabled) {
|
|
|
26037
26525
|
enabled: resolveEnabled(qualified, installedDefaultEnabled(ext.stability)),
|
|
26038
26526
|
...ext.description ? { description: ext.description } : {},
|
|
26039
26527
|
...ext.stability ? { stability: ext.stability } : {},
|
|
26040
|
-
...extLocked ? { locked: true } : {}
|
|
26528
|
+
...extLocked ? { locked: true } : {},
|
|
26529
|
+
...settings2
|
|
26041
26530
|
};
|
|
26042
26531
|
});
|
|
26043
26532
|
const pluginEnabled = extensions.some((e) => e.enabled);
|
|
@@ -26054,12 +26543,12 @@ function buildBuiltInItems(resolveEnabled) {
|
|
|
26054
26543
|
};
|
|
26055
26544
|
});
|
|
26056
26545
|
}
|
|
26057
|
-
function buildDiscoveredItems(discovered, deps, resolveEnabled) {
|
|
26058
|
-
return discovered.map((plugin) => buildDiscoveredItem(plugin, deps, resolveEnabled));
|
|
26546
|
+
function buildDiscoveredItems(discovered, deps, resolveEnabled, config) {
|
|
26547
|
+
return discovered.map((plugin) => buildDiscoveredItem(plugin, deps, resolveEnabled, config));
|
|
26059
26548
|
}
|
|
26060
|
-
function buildDiscoveredItem(plugin, deps, resolveEnabled) {
|
|
26549
|
+
function buildDiscoveredItem(plugin, deps, resolveEnabled, config) {
|
|
26061
26550
|
const pluginLocked = isPluginLocked(plugin.id);
|
|
26062
|
-
const extensions = projectExtensionRows(plugin, resolveEnabled, pluginLocked);
|
|
26551
|
+
const extensions = projectExtensionRows(plugin, resolveEnabled, pluginLocked, config);
|
|
26063
26552
|
const optional = optionalDiscoveredFields(plugin, extensions);
|
|
26064
26553
|
return {
|
|
26065
26554
|
id: plugin.id,
|
|
@@ -26080,12 +26569,18 @@ function optionalDiscoveredFields(plugin, extensions) {
|
|
|
26080
26569
|
if (extensions) out.extensions = extensions;
|
|
26081
26570
|
return out;
|
|
26082
26571
|
}
|
|
26083
|
-
function projectExtensionRows(plugin, resolveEnabled, pluginLocked) {
|
|
26572
|
+
function projectExtensionRows(plugin, resolveEnabled, pluginLocked, config) {
|
|
26084
26573
|
if (!plugin.extensions || plugin.extensions.length === 0) return void 0;
|
|
26085
26574
|
return plugin.extensions.map((ext) => {
|
|
26086
26575
|
const description = readInstanceDescription(ext.instance);
|
|
26087
26576
|
const qualified = qualifiedExtensionId(plugin.id, ext.id);
|
|
26088
26577
|
const extLocked = pluginLocked || isPluginLocked(qualified);
|
|
26578
|
+
const settings2 = projectExtensionSettings(
|
|
26579
|
+
plugin.id,
|
|
26580
|
+
ext.id,
|
|
26581
|
+
readManifestSettings(ext.instance),
|
|
26582
|
+
config
|
|
26583
|
+
);
|
|
26089
26584
|
return {
|
|
26090
26585
|
id: ext.id,
|
|
26091
26586
|
kind: ext.kind,
|
|
@@ -26093,7 +26588,8 @@ function projectExtensionRows(plugin, resolveEnabled, pluginLocked) {
|
|
|
26093
26588
|
enabled: resolveEnabled(qualified, installedDefaultEnabled(ext.stability)),
|
|
26094
26589
|
...description ? { description } : {},
|
|
26095
26590
|
...ext.stability ? { stability: ext.stability } : {},
|
|
26096
|
-
...extLocked ? { locked: true } : {}
|
|
26591
|
+
...extLocked ? { locked: true } : {},
|
|
26592
|
+
...settings2
|
|
26097
26593
|
};
|
|
26098
26594
|
});
|
|
26099
26595
|
}
|
|
@@ -26211,25 +26707,35 @@ function projectListResponse(c, deps, overrides) {
|
|
|
26211
26707
|
);
|
|
26212
26708
|
}
|
|
26213
26709
|
function validateBulkChange(change, deps) {
|
|
26214
|
-
|
|
26215
|
-
|
|
26216
|
-
|
|
26217
|
-
|
|
26218
|
-
|
|
26219
|
-
|
|
26220
|
-
|
|
26221
|
-
|
|
26222
|
-
|
|
26223
|
-
|
|
26224
|
-
|
|
26225
|
-
|
|
26226
|
-
|
|
26227
|
-
|
|
26228
|
-
|
|
26229
|
-
}
|
|
26230
|
-
}
|
|
26231
|
-
|
|
26710
|
+
return change.id.includes("/") ? validateQualifiedBulkChange(change, deps) : validateBareBulkChange(change, deps);
|
|
26711
|
+
}
|
|
26712
|
+
function validateBareBulkChange(change, deps) {
|
|
26713
|
+
if (change.settings !== void 0) {
|
|
26714
|
+
return {
|
|
26715
|
+
status: 400,
|
|
26716
|
+
code: "bad-query",
|
|
26717
|
+
message: tx(SERVER_TEXTS.pluginsSettingsRequireQualifiedId, { id: change.id })
|
|
26718
|
+
};
|
|
26719
|
+
}
|
|
26720
|
+
const handle = findHandle(change.id, deps);
|
|
26721
|
+
if (!handle) {
|
|
26722
|
+
return {
|
|
26723
|
+
status: 404,
|
|
26724
|
+
code: "not-found",
|
|
26725
|
+
message: tx(SERVER_TEXTS.pluginsUnknown, { id: change.id })
|
|
26726
|
+
};
|
|
26727
|
+
}
|
|
26728
|
+
if (isPluginLocked(change.id)) {
|
|
26729
|
+
return {
|
|
26730
|
+
status: 403,
|
|
26731
|
+
code: "locked",
|
|
26732
|
+
message: tx(SERVER_TEXTS.pluginsLocked, { id: change.id })
|
|
26733
|
+
};
|
|
26232
26734
|
}
|
|
26735
|
+
return null;
|
|
26736
|
+
}
|
|
26737
|
+
function validateQualifiedBulkChange(change, deps) {
|
|
26738
|
+
const slash = change.id.indexOf("/");
|
|
26233
26739
|
const pluginId = change.id.slice(0, slash);
|
|
26234
26740
|
const extensionId = change.id.slice(slash + 1);
|
|
26235
26741
|
const handle = findHandle(pluginId, deps);
|
|
@@ -26254,13 +26760,49 @@ function validateBulkChange(change, deps) {
|
|
|
26254
26760
|
message: tx(SERVER_TEXTS.pluginsExtensionLocked, { pluginId, extensionId })
|
|
26255
26761
|
};
|
|
26256
26762
|
}
|
|
26763
|
+
if (change.settings !== void 0) {
|
|
26764
|
+
return validateChangeSettings(handle, pluginId, extensionId, change.settings);
|
|
26765
|
+
}
|
|
26257
26766
|
return null;
|
|
26258
26767
|
}
|
|
26768
|
+
function validateChangeSettings(handle, pluginId, extensionId, patch) {
|
|
26769
|
+
const declarations = handleExtensionSettings(handle, extensionId);
|
|
26770
|
+
if (!declarations || Object.keys(declarations).length === 0) {
|
|
26771
|
+
return {
|
|
26772
|
+
status: 400,
|
|
26773
|
+
code: "bad-query",
|
|
26774
|
+
message: tx(SERVER_TEXTS.pluginsSettingsNoneDeclared, { pluginId, extensionId })
|
|
26775
|
+
};
|
|
26776
|
+
}
|
|
26777
|
+
const failure = validateSettingsPatch(pluginId, extensionId, declarations, patch);
|
|
26778
|
+
if (failure !== null) {
|
|
26779
|
+
return {
|
|
26780
|
+
status: 400,
|
|
26781
|
+
code: "bad-query",
|
|
26782
|
+
message: tx(SERVER_TEXTS.pluginsSettingsInvalid, {
|
|
26783
|
+
settingId: failure.settingId,
|
|
26784
|
+
pluginId,
|
|
26785
|
+
extensionId,
|
|
26786
|
+
reason: failure.reason
|
|
26787
|
+
})
|
|
26788
|
+
};
|
|
26789
|
+
}
|
|
26790
|
+
return null;
|
|
26791
|
+
}
|
|
26792
|
+
function handleExtensionSettings(handle, extensionId) {
|
|
26793
|
+
if (handle.kind === "built-in") {
|
|
26794
|
+
const ext2 = handle.plugin.extensions.find((e) => e.id === extensionId);
|
|
26795
|
+
return readManifestSettings(ext2);
|
|
26796
|
+
}
|
|
26797
|
+
const ext = (handle.plugin.extensions ?? []).find((e) => e.id === extensionId);
|
|
26798
|
+
return readManifestSettings(ext?.instance);
|
|
26799
|
+
}
|
|
26259
26800
|
async function persistBulkAndProject(c, deps, changes) {
|
|
26260
26801
|
const overrides = await tryWithSqlite(
|
|
26261
26802
|
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
26262
26803
|
async (adapter) => {
|
|
26263
26804
|
for (const change of changes) {
|
|
26805
|
+
if (change.enabled === void 0) continue;
|
|
26264
26806
|
const writeKeys = expandBulkChangeKeys(change, deps);
|
|
26265
26807
|
for (const key of writeKeys) {
|
|
26266
26808
|
await applyChangeToAdapter(adapter, key, change.enabled);
|
|
@@ -26269,8 +26811,40 @@ async function persistBulkAndProject(c, deps, changes) {
|
|
|
26269
26811
|
return await adapter.pluginConfig.loadOverrideMap();
|
|
26270
26812
|
}
|
|
26271
26813
|
);
|
|
26814
|
+
if (overrides === null) {
|
|
26815
|
+
throw new DbMissingError(
|
|
26816
|
+
tx(SERVER_TEXTS.pluginsDbMissing, { path: deps.options.dbPath })
|
|
26817
|
+
);
|
|
26818
|
+
}
|
|
26819
|
+
const settingsTouched = persistBulkSettings(deps, changes);
|
|
26820
|
+
if (settingsTouched) deps.configService.reload();
|
|
26272
26821
|
return projectListResponse(c, deps, overrides);
|
|
26273
26822
|
}
|
|
26823
|
+
function persistBulkSettings(deps, changes) {
|
|
26824
|
+
const cwd = deps.runtimeContext.cwd;
|
|
26825
|
+
let touched = false;
|
|
26826
|
+
for (const change of changes) {
|
|
26827
|
+
if (change.settings === void 0) continue;
|
|
26828
|
+
const slash = change.id.indexOf("/");
|
|
26829
|
+
if (slash < 0) continue;
|
|
26830
|
+
const pluginId = change.id.slice(0, slash);
|
|
26831
|
+
const extensionId = change.id.slice(slash + 1);
|
|
26832
|
+
const handle = findHandle(pluginId, deps);
|
|
26833
|
+
const declarations = handle ? handleExtensionSettings(handle, extensionId) : void 0;
|
|
26834
|
+
try {
|
|
26835
|
+
persistSettingsPatch(pluginId, extensionId, declarations, change.settings, cwd);
|
|
26836
|
+
} catch (err) {
|
|
26837
|
+
throw new HTTPException9(500, {
|
|
26838
|
+
message: tx(SERVER_TEXTS.pluginsSettingsPersistFailed, {
|
|
26839
|
+
id: change.id,
|
|
26840
|
+
message: err instanceof Error ? err.message : String(err)
|
|
26841
|
+
})
|
|
26842
|
+
});
|
|
26843
|
+
}
|
|
26844
|
+
if (Object.keys(change.settings).length > 0) touched = true;
|
|
26845
|
+
}
|
|
26846
|
+
return touched;
|
|
26847
|
+
}
|
|
26274
26848
|
function expandBulkChangeKeys(change, deps) {
|
|
26275
26849
|
if (change.id.includes("/")) return [change.id];
|
|
26276
26850
|
const handle = findHandle(change.id, deps);
|
|
@@ -28622,7 +29196,7 @@ function formatCwdPath(cwd) {
|
|
|
28622
29196
|
// cli/commands/serve.ts
|
|
28623
29197
|
var ServeCommand = class extends SmCommand {
|
|
28624
29198
|
static paths = [["serve"]];
|
|
28625
|
-
static usage =
|
|
29199
|
+
static usage = Command34.Usage({
|
|
28626
29200
|
category: "Setup",
|
|
28627
29201
|
description: "Start the Hono BFF (single-port: REST + WebSocket + SPA bundle).",
|
|
28628
29202
|
details: `
|
|
@@ -28646,18 +29220,18 @@ var ServeCommand = class extends SmCommand {
|
|
|
28646
29220
|
["Point at a pre-built UI bundle", "$0 serve --ui-dist ./ui/dist/browser"]
|
|
28647
29221
|
]
|
|
28648
29222
|
});
|
|
28649
|
-
port =
|
|
29223
|
+
port = Option32.String("--port", {
|
|
28650
29224
|
required: false,
|
|
28651
29225
|
description: "Listening port (default 4242). 0 = OS-assigned."
|
|
28652
29226
|
});
|
|
28653
|
-
host =
|
|
29227
|
+
host = Option32.String("--host", {
|
|
28654
29228
|
required: false,
|
|
28655
29229
|
description: "Listening host (default 127.0.0.1). Loopback-only enforced when --dev-cors is set."
|
|
28656
29230
|
});
|
|
28657
|
-
noBuiltIns =
|
|
29231
|
+
noBuiltIns = Option32.Boolean("--no-built-ins", false, {
|
|
28658
29232
|
description: "Skip built-in plugin registration (parity with sm scan --no-built-ins)."
|
|
28659
29233
|
});
|
|
28660
|
-
noPlugins =
|
|
29234
|
+
noPlugins = Option32.Boolean("--no-plugins", false, {
|
|
28661
29235
|
description: "Skip drop-in plugin discovery."
|
|
28662
29236
|
});
|
|
28663
29237
|
// `Option.Boolean('--open', true)`, Clipanion's parser auto-derives
|
|
@@ -28667,31 +29241,31 @@ var ServeCommand = class extends SmCommand {
|
|
|
28667
29241
|
// two registrations for the same flag and rejects the invocation
|
|
28668
29242
|
// with "Ambiguous Syntax Error". Same convention shipped by every
|
|
28669
29243
|
// other `--no-...` flag in the CLI tree.
|
|
28670
|
-
open =
|
|
29244
|
+
open = Option32.Boolean("--open", true, {
|
|
28671
29245
|
description: "Auto-open the SPA in the user's default browser after listen. --no-open opts out."
|
|
28672
29246
|
});
|
|
28673
|
-
devCors =
|
|
29247
|
+
devCors = Option32.Boolean("--dev-cors", false, {
|
|
28674
29248
|
description: "Enable permissive CORS for the Angular dev-server proxy workflow."
|
|
28675
29249
|
});
|
|
28676
29250
|
// `--ui-dist` is intentionally undocumented in the Usage block above
|
|
28677
29251
|
// (the demo build pipeline + tests rely on it; everyday users never
|
|
28678
29252
|
// need it). Clipanion still exposes it on the parser; the Usage
|
|
28679
29253
|
// omission is the "hidden" contract per the 14.1 brief.
|
|
28680
|
-
uiDist =
|
|
28681
|
-
noUi =
|
|
29254
|
+
uiDist = Option32.String("--ui-dist", { required: false, hidden: true });
|
|
29255
|
+
noUi = Option32.Boolean("--no-ui", false, {
|
|
28682
29256
|
description: "Don't serve the Angular UI bundle. Use this when running the BFF alongside `ui:dev` (Angular dev server with HMR). The root `/` then renders an inline placeholder pointing the user at the dev server."
|
|
28683
29257
|
});
|
|
28684
|
-
noWatcher =
|
|
29258
|
+
noWatcher = Option32.Boolean("--no-watcher", false, {
|
|
28685
29259
|
description: "Disable the chokidar-fed scan-and-broadcast loop. Use only for CI / read-only deployments."
|
|
28686
29260
|
});
|
|
28687
|
-
yes =
|
|
29261
|
+
yes = Option32.Boolean("--yes", false, {
|
|
28688
29262
|
description: "Skip the interactive prompt and rebuild the local cache when the on-disk DB has drifted (version skew or an inline schema change). Non-TTY invocations rebuild without asking regardless of this flag."
|
|
28689
29263
|
});
|
|
28690
29264
|
// `--watcher-debounce-ms` is undocumented sugar for advanced users
|
|
28691
29265
|
// who want to tighten / relax the watcher's batching window without
|
|
28692
29266
|
// editing settings.json. Hidden flag, the Usage block omits it.
|
|
28693
|
-
watcherDebounceMs =
|
|
28694
|
-
maxNodes =
|
|
29267
|
+
watcherDebounceMs = Option32.String("--watcher-debounce-ms", { required: false, hidden: true });
|
|
29268
|
+
maxNodes = Option32.String("--max-nodes", {
|
|
28695
29269
|
required: false,
|
|
28696
29270
|
description: "Per-invocation override of scan.maxNodes (default 256). Bidirectional: raises OR lowers the recommended cap on classified nodes. Applies to every scan the server runs (initial watcher pass, debounced batches, POST /api/scan, GET /api/scan?fresh=1). Same flag is honoured on the bare `sm` invocation, which routes to `sm serve`."
|
|
28697
29271
|
});
|
|
@@ -29035,7 +29609,7 @@ function tryOpenBrowser(url, stderr, warnGlyph) {
|
|
|
29035
29609
|
}
|
|
29036
29610
|
|
|
29037
29611
|
// cli/commands/show.ts
|
|
29038
|
-
import { Command as
|
|
29612
|
+
import { Command as Command35, Option as Option33 } from "clipanion";
|
|
29039
29613
|
|
|
29040
29614
|
// cli/i18n/show.texts.ts
|
|
29041
29615
|
var SHOW_TEXTS = {
|
|
@@ -29086,7 +29660,7 @@ var SHOW_TEXTS = {
|
|
|
29086
29660
|
// cli/commands/show.ts
|
|
29087
29661
|
var ShowCommand = class extends SmCommand {
|
|
29088
29662
|
static paths = [["show"]];
|
|
29089
|
-
static usage =
|
|
29663
|
+
static usage = Command35.Usage({
|
|
29090
29664
|
category: "Browse",
|
|
29091
29665
|
description: "Node detail: weight, frontmatter, links, issues.",
|
|
29092
29666
|
details: `
|
|
@@ -29102,7 +29676,7 @@ var ShowCommand = class extends SmCommand {
|
|
|
29102
29676
|
["Machine-readable detail", "$0 show .claude/agents/architect.md --json"]
|
|
29103
29677
|
]
|
|
29104
29678
|
});
|
|
29105
|
-
nodePath =
|
|
29679
|
+
nodePath = Option33.String({ required: true });
|
|
29106
29680
|
async run() {
|
|
29107
29681
|
const dbPath = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
|
|
29108
29682
|
const exit = requireDbOrExit(dbPath, this.context.stderr);
|
|
@@ -29344,7 +29918,7 @@ function rankConfidenceForGrouping(c) {
|
|
|
29344
29918
|
// cli/commands/sidecar.ts
|
|
29345
29919
|
import { unlink as unlink3 } from "fs/promises";
|
|
29346
29920
|
import { resolve as resolve38 } from "path";
|
|
29347
|
-
import { Command as
|
|
29921
|
+
import { Command as Command36, Option as Option34 } from "clipanion";
|
|
29348
29922
|
|
|
29349
29923
|
// cli/i18n/sidecar.texts.ts
|
|
29350
29924
|
var SIDECAR_TEXTS = {
|
|
@@ -29425,7 +29999,7 @@ async function runWithSidecarConsent(bag, ansi, dispatch) {
|
|
|
29425
29999
|
}
|
|
29426
30000
|
var SidecarRefreshCommand = class extends SmCommand {
|
|
29427
30001
|
static paths = [["sidecar", "refresh"]];
|
|
29428
|
-
static usage =
|
|
30002
|
+
static usage = Command36.Usage({
|
|
29429
30003
|
category: "Actions",
|
|
29430
30004
|
description: "Refresh a sidecar's `for.{bodyHash, frontmatterHash}` to match the live node. Does NOT bump the version.",
|
|
29431
30005
|
details: `
|
|
@@ -29442,8 +30016,8 @@ var SidecarRefreshCommand = class extends SmCommand {
|
|
|
29442
30016
|
["Refresh a node's sidecar hashes", "$0 sidecar refresh .claude/agents/architect.md"]
|
|
29443
30017
|
]
|
|
29444
30018
|
});
|
|
29445
|
-
nodePath =
|
|
29446
|
-
yes =
|
|
30019
|
+
nodePath = Option34.String({ required: true });
|
|
30020
|
+
yes = Option34.Boolean("--yes", false, {
|
|
29447
30021
|
description: "Confirm writing .sm sidecar files in this project (sets allowEditSmFiles=true on first run)."
|
|
29448
30022
|
});
|
|
29449
30023
|
async run() {
|
|
@@ -29565,7 +30139,7 @@ var SidecarRefreshCommand = class extends SmCommand {
|
|
|
29565
30139
|
};
|
|
29566
30140
|
var SidecarPruneCommand = class extends SmCommand {
|
|
29567
30141
|
static paths = [["sidecar", "prune"]];
|
|
29568
|
-
static usage =
|
|
30142
|
+
static usage = Command36.Usage({
|
|
29569
30143
|
category: "Actions",
|
|
29570
30144
|
description: "Delete orphan .sm files (sidecars whose accompanying .md no longer exists).",
|
|
29571
30145
|
details: `
|
|
@@ -29587,8 +30161,8 @@ var SidecarPruneCommand = class extends SmCommand {
|
|
|
29587
30161
|
["Delete every orphan .sm file (non-interactive)", "$0 sidecar prune --yes"]
|
|
29588
30162
|
]
|
|
29589
30163
|
});
|
|
29590
|
-
dryRun =
|
|
29591
|
-
yes =
|
|
30164
|
+
dryRun = Option34.Boolean("-n,--dry-run", false);
|
|
30165
|
+
yes = Option34.Boolean("--yes,--force", false, {
|
|
29592
30166
|
description: "Skip the interactive confirmation prompt. Required for non-interactive callers (CI, pre-commit hooks)."
|
|
29593
30167
|
});
|
|
29594
30168
|
// Complexity is from per-orphan handling, empty-set / dry-run /
|
|
@@ -29708,7 +30282,7 @@ var SidecarPruneCommand = class extends SmCommand {
|
|
|
29708
30282
|
};
|
|
29709
30283
|
var SidecarAnnotateCommand = class extends SmCommand {
|
|
29710
30284
|
static paths = [["sidecar", "annotate"]];
|
|
29711
|
-
static usage =
|
|
30285
|
+
static usage = Command36.Usage({
|
|
29712
30286
|
category: "Actions",
|
|
29713
30287
|
description: "Scaffold an empty `<basename>.sm` next to a node ready for editing.",
|
|
29714
30288
|
details: `
|
|
@@ -29726,9 +30300,9 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
29726
30300
|
["Overwrite an existing one", "$0 sidecar annotate .claude/agents/architect.md --force"]
|
|
29727
30301
|
]
|
|
29728
30302
|
});
|
|
29729
|
-
nodePath =
|
|
29730
|
-
force =
|
|
29731
|
-
yes =
|
|
30303
|
+
nodePath = Option34.String({ required: true });
|
|
30304
|
+
force = Option34.Boolean("--force", false);
|
|
30305
|
+
yes = Option34.Boolean("--yes", false, {
|
|
29732
30306
|
description: "Confirm writing .sm sidecar files in this project (sets allowEditSmFiles=true on first run)."
|
|
29733
30307
|
});
|
|
29734
30308
|
async run() {
|
|
@@ -29867,7 +30441,7 @@ var SIDECAR_COMMANDS = [
|
|
|
29867
30441
|
];
|
|
29868
30442
|
|
|
29869
30443
|
// cli/commands/stubs.ts
|
|
29870
|
-
import { Command as
|
|
30444
|
+
import { Command as Command37, Option as Option35 } from "clipanion";
|
|
29871
30445
|
|
|
29872
30446
|
// cli/i18n/stubs.texts.ts
|
|
29873
30447
|
var STUBS_TEXTS = {
|
|
@@ -29893,7 +30467,7 @@ var StubCommand = class extends SmCommand {
|
|
|
29893
30467
|
};
|
|
29894
30468
|
var DoctorCommand = class extends StubCommand {
|
|
29895
30469
|
static paths = [["doctor"]];
|
|
29896
|
-
static usage =
|
|
30470
|
+
static usage = Command37.Usage({
|
|
29897
30471
|
category: "Setup",
|
|
29898
30472
|
description: planned("Diagnostic report: DB integrity, pending migrations, orphan rows, plugin status, runner availability.")
|
|
29899
30473
|
});
|
|
@@ -29901,18 +30475,18 @@ var DoctorCommand = class extends StubCommand {
|
|
|
29901
30475
|
};
|
|
29902
30476
|
var FindingsCommand = class extends StubCommand {
|
|
29903
30477
|
static paths = [["findings"]];
|
|
29904
|
-
static usage =
|
|
30478
|
+
static usage = Command37.Usage({
|
|
29905
30479
|
category: "Browse",
|
|
29906
30480
|
description: planned("Probabilistic findings: injection, stale summaries, low confidence.")
|
|
29907
30481
|
});
|
|
29908
|
-
kind =
|
|
29909
|
-
since =
|
|
29910
|
-
threshold =
|
|
30482
|
+
kind = Option35.String("--kind", { required: false });
|
|
30483
|
+
since = Option35.String("--since", { required: false });
|
|
30484
|
+
threshold = Option35.String("--threshold", { required: false });
|
|
29911
30485
|
verbName = "findings";
|
|
29912
30486
|
};
|
|
29913
30487
|
var ActionsListCommand = class extends StubCommand {
|
|
29914
30488
|
static paths = [["actions", "list"]];
|
|
29915
|
-
static usage =
|
|
30489
|
+
static usage = Command37.Usage({
|
|
29916
30490
|
category: "Jobs",
|
|
29917
30491
|
description: planned("Registered action types (manifest view).")
|
|
29918
30492
|
});
|
|
@@ -29920,103 +30494,103 @@ var ActionsListCommand = class extends StubCommand {
|
|
|
29920
30494
|
};
|
|
29921
30495
|
var ActionsShowCommand = class extends StubCommand {
|
|
29922
30496
|
static paths = [["actions", "show"]];
|
|
29923
|
-
static usage =
|
|
30497
|
+
static usage = Command37.Usage({
|
|
29924
30498
|
category: "Jobs",
|
|
29925
30499
|
description: planned("Full action manifest, including preconditions and expected duration.")
|
|
29926
30500
|
});
|
|
29927
|
-
id =
|
|
30501
|
+
id = Option35.String({ required: true });
|
|
29928
30502
|
verbName = "actions show";
|
|
29929
30503
|
};
|
|
29930
30504
|
var JobSubmitCommand = class extends StubCommand {
|
|
29931
30505
|
static paths = [["job", "submit"]];
|
|
29932
|
-
static usage =
|
|
30506
|
+
static usage = Command37.Usage({
|
|
29933
30507
|
category: "Jobs",
|
|
29934
30508
|
description: planned("Enqueue a single job or fan out to every matching node (--all).")
|
|
29935
30509
|
});
|
|
29936
|
-
action =
|
|
29937
|
-
node =
|
|
29938
|
-
all =
|
|
30510
|
+
action = Option35.String({ required: true });
|
|
30511
|
+
node = Option35.String("-n", { required: false });
|
|
30512
|
+
all = Option35.Boolean("--all", false);
|
|
29939
30513
|
// CLI flag stays `--run`; field name is `runFlag` per the
|
|
29940
30514
|
// shadow-avoidance convention documented on `SmCommand`.
|
|
29941
|
-
runFlag =
|
|
29942
|
-
force =
|
|
29943
|
-
ttl =
|
|
29944
|
-
priority =
|
|
30515
|
+
runFlag = Option35.Boolean("--run", false);
|
|
30516
|
+
force = Option35.Boolean("--force", false);
|
|
30517
|
+
ttl = Option35.String("--ttl", { required: false });
|
|
30518
|
+
priority = Option35.String("--priority", { required: false });
|
|
29945
30519
|
verbName = "job submit";
|
|
29946
30520
|
};
|
|
29947
30521
|
var JobListCommand = class extends StubCommand {
|
|
29948
30522
|
static paths = [["job", "list"]];
|
|
29949
|
-
static usage =
|
|
29950
|
-
status =
|
|
29951
|
-
action =
|
|
29952
|
-
node =
|
|
30523
|
+
static usage = Command37.Usage({ category: "Jobs", description: planned("List jobs.") });
|
|
30524
|
+
status = Option35.String("--status", { required: false });
|
|
30525
|
+
action = Option35.String("--action", { required: false });
|
|
30526
|
+
node = Option35.String("--node", { required: false });
|
|
29953
30527
|
verbName = "job list";
|
|
29954
30528
|
};
|
|
29955
30529
|
var JobShowCommand = class extends StubCommand {
|
|
29956
30530
|
static paths = [["job", "show"]];
|
|
29957
|
-
static usage =
|
|
29958
|
-
id =
|
|
30531
|
+
static usage = Command37.Usage({ category: "Jobs", description: planned("Job detail: state, claim time, TTL, runner, content hash.") });
|
|
30532
|
+
id = Option35.String({ required: true });
|
|
29959
30533
|
verbName = "job show";
|
|
29960
30534
|
};
|
|
29961
30535
|
var JobPreviewCommand = class extends StubCommand {
|
|
29962
30536
|
static paths = [["job", "preview"]];
|
|
29963
|
-
static usage =
|
|
29964
|
-
id =
|
|
30537
|
+
static usage = Command37.Usage({ category: "Jobs", description: planned("Render the job MD file without executing.") });
|
|
30538
|
+
id = Option35.String({ required: true });
|
|
29965
30539
|
verbName = "job preview";
|
|
29966
30540
|
};
|
|
29967
30541
|
var JobClaimCommand = class extends StubCommand {
|
|
29968
30542
|
static paths = [["job", "claim"]];
|
|
29969
|
-
static usage =
|
|
30543
|
+
static usage = Command37.Usage({
|
|
29970
30544
|
category: "Jobs",
|
|
29971
30545
|
description: planned("Atomic primitive: return next queued job id, mark it running.")
|
|
29972
30546
|
});
|
|
29973
|
-
filter =
|
|
30547
|
+
filter = Option35.String("--filter", { required: false });
|
|
29974
30548
|
verbName = "job claim";
|
|
29975
30549
|
};
|
|
29976
30550
|
var JobRunCommand = class extends StubCommand {
|
|
29977
30551
|
static paths = [["job", "run"]];
|
|
29978
|
-
static usage =
|
|
30552
|
+
static usage = Command37.Usage({
|
|
29979
30553
|
category: "Jobs",
|
|
29980
30554
|
description: planned("Full CLI-runner loop: claim + spawn + record.")
|
|
29981
30555
|
});
|
|
29982
|
-
all =
|
|
29983
|
-
max =
|
|
30556
|
+
all = Option35.Boolean("--all", false);
|
|
30557
|
+
max = Option35.String("--max", { required: false });
|
|
29984
30558
|
verbName = "job run";
|
|
29985
30559
|
};
|
|
29986
30560
|
var JobStatusCommand = class extends StubCommand {
|
|
29987
30561
|
static paths = [["job", "status"]];
|
|
29988
|
-
static usage =
|
|
30562
|
+
static usage = Command37.Usage({
|
|
29989
30563
|
category: "Jobs",
|
|
29990
30564
|
description: planned("Counts (per status) or single-job status.")
|
|
29991
30565
|
});
|
|
29992
|
-
id =
|
|
30566
|
+
id = Option35.String({ required: false });
|
|
29993
30567
|
verbName = "job status";
|
|
29994
30568
|
};
|
|
29995
30569
|
var JobCancelCommand = class extends StubCommand {
|
|
29996
30570
|
static paths = [["job", "cancel"]];
|
|
29997
|
-
static usage =
|
|
30571
|
+
static usage = Command37.Usage({
|
|
29998
30572
|
category: "Jobs",
|
|
29999
30573
|
description: planned("Force a running job to failed with reason user-cancelled.")
|
|
30000
30574
|
});
|
|
30001
|
-
id =
|
|
30002
|
-
all =
|
|
30575
|
+
id = Option35.String({ required: false });
|
|
30576
|
+
all = Option35.Boolean("--all", false);
|
|
30003
30577
|
verbName = "job cancel";
|
|
30004
30578
|
};
|
|
30005
30579
|
var RecordCommand = class extends StubCommand {
|
|
30006
30580
|
static paths = [["record"]];
|
|
30007
|
-
static usage =
|
|
30581
|
+
static usage = Command37.Usage({
|
|
30008
30582
|
category: "Jobs",
|
|
30009
30583
|
description: planned("Close a running job with success or failure. Nonce is the sole credential.")
|
|
30010
30584
|
});
|
|
30011
|
-
id =
|
|
30012
|
-
nonce =
|
|
30013
|
-
status =
|
|
30014
|
-
report =
|
|
30015
|
-
tokensIn =
|
|
30016
|
-
tokensOut =
|
|
30017
|
-
durationMs =
|
|
30018
|
-
model =
|
|
30019
|
-
error =
|
|
30585
|
+
id = Option35.String("--id", { required: true });
|
|
30586
|
+
nonce = Option35.String("--nonce", { required: true });
|
|
30587
|
+
status = Option35.String("--status", { required: true });
|
|
30588
|
+
report = Option35.String("--report", { required: false });
|
|
30589
|
+
tokensIn = Option35.String("--tokens-in", { required: false });
|
|
30590
|
+
tokensOut = Option35.String("--tokens-out", { required: false });
|
|
30591
|
+
durationMs = Option35.String("--duration-ms", { required: false });
|
|
30592
|
+
model = Option35.String("--model", { required: false });
|
|
30593
|
+
error = Option35.String("--error", { required: false });
|
|
30020
30594
|
verbName = "record";
|
|
30021
30595
|
};
|
|
30022
30596
|
var STUB_COMMANDS = [
|
|
@@ -30040,7 +30614,7 @@ import { cpSync as cpSync2, existsSync as existsSync32, mkdirSync as mkdirSync6,
|
|
|
30040
30614
|
import { dirname as dirname20, join as join21, resolve as resolve39 } from "path";
|
|
30041
30615
|
import { createInterface as createInterface5 } from "readline";
|
|
30042
30616
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
30043
|
-
import { Command as
|
|
30617
|
+
import { Command as Command38, Option as Option36 } from "clipanion";
|
|
30044
30618
|
|
|
30045
30619
|
// cli/i18n/tutorial.texts.ts
|
|
30046
30620
|
var TUTORIAL_TEXTS = {
|
|
@@ -30102,7 +30676,7 @@ var TRIGGER_EN = "run the tutorial";
|
|
|
30102
30676
|
var TRIGGER_ES = "ejecuta el tutorial";
|
|
30103
30677
|
var TutorialCommand = class extends SmCommand {
|
|
30104
30678
|
static paths = [["tutorial"]];
|
|
30105
|
-
static usage =
|
|
30679
|
+
static usage = Command38.Usage({
|
|
30106
30680
|
category: "Setup",
|
|
30107
30681
|
description: "Materialize an interactive tester tutorial as a Claude Code skill folder under `<cwd>/.claude/skills/`.",
|
|
30108
30682
|
details: `
|
|
@@ -30125,15 +30699,15 @@ var TutorialCommand = class extends SmCommand {
|
|
|
30125
30699
|
// more. Accept one so a stale `sm tutorial master` lands on a friendly
|
|
30126
30700
|
// usage error (guarded in `run()`) instead of clipanion's generic
|
|
30127
30701
|
// "extraneous argument" message.
|
|
30128
|
-
legacyPositional =
|
|
30702
|
+
legacyPositional = Option36.String({ required: false });
|
|
30129
30703
|
// Named `forProvider`, NOT `for` (reserved word). The CLI surface stays
|
|
30130
30704
|
// `--for`; selects the destination Provider whose `scaffold.skillDir`
|
|
30131
30705
|
// the skill is materialised under, skipping the interactive prompt.
|
|
30132
|
-
forProvider =
|
|
30706
|
+
forProvider = Option36.String("--for", {
|
|
30133
30707
|
required: false,
|
|
30134
30708
|
description: "Destination provider id (e.g. claude, agent-skills). Skips the prompt."
|
|
30135
30709
|
});
|
|
30136
|
-
force =
|
|
30710
|
+
force = Option36.Boolean("--force", false, {
|
|
30137
30711
|
description: "Overwrite an existing target directory without prompting."
|
|
30138
30712
|
});
|
|
30139
30713
|
async run() {
|
|
@@ -30364,7 +30938,7 @@ function resolveSkillSourceDir() {
|
|
|
30364
30938
|
}
|
|
30365
30939
|
|
|
30366
30940
|
// cli/commands/version.ts
|
|
30367
|
-
import { Command as
|
|
30941
|
+
import { Command as Command39 } from "clipanion";
|
|
30368
30942
|
|
|
30369
30943
|
// cli/i18n/version.texts.ts
|
|
30370
30944
|
var VERSION_TEXTS = {
|
|
@@ -30379,7 +30953,7 @@ var VERSION_TEXTS = {
|
|
|
30379
30953
|
// cli/commands/version.ts
|
|
30380
30954
|
var VersionCommand = class extends SmCommand {
|
|
30381
30955
|
static paths = [["version"]];
|
|
30382
|
-
static usage =
|
|
30956
|
+
static usage = Command39.Usage({
|
|
30383
30957
|
category: "Introspection",
|
|
30384
30958
|
description: "Print the CLI / kernel / spec / runtime / db-schema version matrix."
|
|
30385
30959
|
});
|
|
@@ -30578,4 +31152,4 @@ function resolveBareDefault() {
|
|
|
30578
31152
|
process.exit(ExitCode.Error);
|
|
30579
31153
|
}
|
|
30580
31154
|
//# sourceMappingURL=cli.js.map
|
|
30581
|
-
//# debugId=
|
|
31155
|
+
//# debugId=49fdc2af-2694-5cd0-ac19-33f8d1dffa9e
|