@skill-map/cli 0.45.1 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/tutorial/sm-master/SKILL.md +29 -29
- package/dist/cli/tutorial/sm-master/references/fixture-templates.md +18 -13
- package/dist/cli/tutorial/sm-master/references/tour-authoring.md +35 -40
- package/dist/cli/tutorial/sm-master/references/tour-plugins.md +32 -32
- package/dist/cli/tutorial/sm-master/references/tour-settings.md +156 -75
- package/dist/cli/tutorial/sm-tutorial/SKILL.md +3 -3
- package/dist/cli.js +998 -458
- package/dist/conformance/index.js +4 -1
- package/dist/index.js +67 -22
- package/dist/kernel/index.d.ts +93 -13
- package/dist/kernel/index.js +67 -22
- package/dist/migrations/001_initial.sql +23 -0
- package/dist/ui/chunk-22CKFAEU.js +1 -0
- package/dist/ui/chunk-3AKR33GE.js +1 -0
- package/dist/ui/{chunk-QNTAOR2L.js → chunk-3HLMBEDX.js} +1 -1
- package/dist/ui/{chunk-T3IVIRRJ.js → chunk-7K36273M.js} +1 -1
- package/dist/ui/{chunk-MFLFIA7C.js → chunk-CO2ZOUSD.js} +1 -1
- package/dist/ui/{chunk-2RAE3FAN.js → chunk-CRWK2NFZ.js} +1 -1
- package/dist/ui/{chunk-VGPYYAVI.js → chunk-EPBUSS3I.js} +1 -1
- package/dist/ui/chunk-HAWX5WNM.js +4 -0
- package/dist/ui/{chunk-QDUSFOBE.js → chunk-K365TVPA.js} +1 -1
- package/dist/ui/chunk-PO2VZMOB.js +123 -0
- package/dist/ui/{chunk-X227ITGS.js → chunk-RT7E4S5B.js} +1 -1
- package/dist/ui/{chunk-5AD5ZV4I.js → chunk-UIUGLD7F.js} +1 -1
- package/dist/ui/{chunk-IYM26L3O.js → chunk-UV3QRBRR.js} +1 -1
- package/dist/ui/chunk-VNA3TMIO.js +1 -0
- package/dist/ui/{chunk-F7I6KMHX.js → chunk-VW2A6WZ3.js} +1 -1
- package/dist/ui/{chunk-A7PRWMQD.js → chunk-WPUUCIS3.js} +11 -11
- package/dist/ui/{chunk-MS6B7344.js → chunk-XWU3YFSM.js} +7 -7
- package/dist/ui/{chunk-I5AX4U2N.js → chunk-YOF6HQCQ.js} +1 -1
- package/dist/ui/chunk-ZZJ7XWDX.js +1 -0
- package/dist/ui/index.html +1 -1
- package/dist/ui/main-55GYZX6C.js +4 -0
- package/migrations/001_initial.sql +23 -0
- package/package.json +2 -2
- package/dist/cli.js.map +0 -1
- package/dist/conformance/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/kernel/index.js.map +0 -1
- package/dist/ui/chunk-27WQPOXP.js +0 -1
- package/dist/ui/chunk-555ST76V.js +0 -1
- package/dist/ui/chunk-PZQHB7GS.js +0 -4
- package/dist/ui/chunk-ZIGUUDUX.js +0 -123
- package/dist/ui/main-KMSUFJ6Y.js +0 -3
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
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]="926243f9-3707-54bc-9aa6-c68955118161")}catch(e){}}();
|
|
4
|
+
import { existsSync as existsSync33 } from "fs";
|
|
3
5
|
import { Builtins, Cli as Cli2 } from "clipanion";
|
|
4
6
|
|
|
5
7
|
// kernel/adapters/in-memory-progress.ts
|
|
@@ -244,7 +246,7 @@ function bucketByKind(kind, instance, bag) {
|
|
|
244
246
|
// package.json
|
|
245
247
|
var package_default = {
|
|
246
248
|
name: "@skill-map/cli",
|
|
247
|
-
version: "0.
|
|
249
|
+
version: "0.47.0",
|
|
248
250
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
249
251
|
license: "MIT",
|
|
250
252
|
type: "module",
|
|
@@ -598,7 +600,7 @@ var claudeProvider = {
|
|
|
598
600
|
read: { extensions: [".md"], parser: "frontmatter-yaml" },
|
|
599
601
|
// Per spec § A.6, defaultRefreshAction values MUST be qualified action
|
|
600
602
|
// ids. The summarize-* actions are not yet implemented as registry
|
|
601
|
-
// entries (they ship later under the Claude
|
|
603
|
+
// entries (they ship later under the Claude plugin), but the qualified
|
|
602
604
|
// form is the contract: when those actions land, they will register
|
|
603
605
|
// under `claude/summarize-<kind>` and the Provider resolves them
|
|
604
606
|
// deterministically.
|
|
@@ -1309,7 +1311,7 @@ var markdown_schema_default = {
|
|
|
1309
1311
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1310
1312
|
$id: "https://skill-map.ai/providers/core/v1/frontmatter/markdown.schema.json",
|
|
1311
1313
|
title: "FrontmatterMarkdown",
|
|
1312
|
-
description: "Frontmatter shape for nodes classified as `markdown` by the built-in `core/markdown` Provider, the universal fallback for any markdown file no vendor-specific Provider claims (Claude, OpenAI Codex, agent-skills, plus the metadata-only Antigravity
|
|
1314
|
+
description: "Frontmatter shape for nodes classified as `markdown` by the built-in `core/markdown` Provider, the universal fallback for any markdown file no vendor-specific Provider claims (Claude, OpenAI Codex, agent-skills, plus the metadata-only Antigravity plugin). The kind is named after the format because the file is a generic fallback; format-named kinds apply only as the generic fallback, a TOML file that IS a Codex agent still classifies as `agent`, not `toml`. Extends the spec's universal `frontmatter/base.schema.json` via $ref-by-$id with no additional fields. Ownership relocated from the Claude Provider in spec 0.18.0, markdown is provider-agnostic and lives under `core` so adding new vendor Providers does not require choosing which one owns the universal fallback.",
|
|
1313
1315
|
allOf: [
|
|
1314
1316
|
{ $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
|
|
1315
1317
|
],
|
|
@@ -1340,7 +1342,7 @@ var coreMarkdownProvider = {
|
|
|
1340
1342
|
read: { extensions: [".md"], parser: "frontmatter-yaml" },
|
|
1341
1343
|
// Per spec § A.6, defaultRefreshAction values MUST be qualified
|
|
1342
1344
|
// action ids. The summarize-markdown action is not yet implemented
|
|
1343
|
-
// as a registry entry (it ships later under the core
|
|
1345
|
+
// as a registry entry (it ships later under the core plugin), but
|
|
1344
1346
|
// the qualified form is the contract.
|
|
1345
1347
|
//
|
|
1346
1348
|
// UI presentation: same neutral teal that the per-vendor Providers
|
|
@@ -4464,7 +4466,7 @@ var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: VERSION };
|
|
|
4464
4466
|
var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: VERSION };
|
|
4465
4467
|
var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: VERSION };
|
|
4466
4468
|
var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: VERSION };
|
|
4467
|
-
var
|
|
4469
|
+
var builtInPlugins = [
|
|
4468
4470
|
{
|
|
4469
4471
|
id: "claude",
|
|
4470
4472
|
description: "Claude Code platform integration. Classifies files under `.claude/{agents,commands,skills}` and detects Claude-flavored slash commands and at-directives in their bodies.",
|
|
@@ -4539,8 +4541,8 @@ function builtIns() {
|
|
|
4539
4541
|
actions: [],
|
|
4540
4542
|
hooks: []
|
|
4541
4543
|
};
|
|
4542
|
-
for (const
|
|
4543
|
-
for (const ext of
|
|
4544
|
+
for (const plugin of builtInPlugins) {
|
|
4545
|
+
for (const ext of plugin.extensions) {
|
|
4544
4546
|
bucketBuiltIn(ext, out);
|
|
4545
4547
|
}
|
|
4546
4548
|
}
|
|
@@ -4548,8 +4550,8 @@ function builtIns() {
|
|
|
4548
4550
|
}
|
|
4549
4551
|
function listBuiltIns() {
|
|
4550
4552
|
const out = [];
|
|
4551
|
-
for (const
|
|
4552
|
-
for (const x of
|
|
4553
|
+
for (const plugin of builtInPlugins) {
|
|
4554
|
+
for (const x of plugin.extensions) {
|
|
4553
4555
|
out.push(toExtensionRow(x));
|
|
4554
4556
|
}
|
|
4555
4557
|
}
|
|
@@ -4603,6 +4605,8 @@ var ENTRY_TEXTS = {
|
|
|
4603
4605
|
parseErrorFlagSuggestion: "Did you mean '{{suggestion}}'?",
|
|
4604
4606
|
parseErrorVerbSuggestion: "Did you mean {{suggestions}}?",
|
|
4605
4607
|
parseErrorVerbHelpHint: "Run 'sm help {{verb}}' for usage.",
|
|
4608
|
+
/** Footer for the incomplete-namespace error, points at that namespace's overview. */
|
|
4609
|
+
parseErrorNamespaceHelpHint: "Run 'sm help {{name}}' to see all subcommands.",
|
|
4606
4610
|
parseErrorFooter: "Run 'sm help' to see the full command list."
|
|
4607
4611
|
};
|
|
4608
4612
|
|
|
@@ -4891,7 +4895,8 @@ function formatParseError(params) {
|
|
|
4891
4895
|
const suggestion2 = tx(ENTRY_TEXTS.parseErrorSubcommandList, {
|
|
4892
4896
|
suggestions: formatSuggestionList(subcommands)
|
|
4893
4897
|
});
|
|
4894
|
-
|
|
4898
|
+
const footer = tx(ENTRY_TEXTS.parseErrorNamespaceHelpHint, { name: firstToken });
|
|
4899
|
+
return renderError(headline, suggestion2, footer);
|
|
4895
4900
|
}
|
|
4896
4901
|
const candidates = closestVerbs(firstToken, verbPaths);
|
|
4897
4902
|
const suggestion = candidates.length > 0 ? tx(ENTRY_TEXTS.parseErrorVerbSuggestion, { suggestions: formatSuggestionList(candidates) }) : null;
|
|
@@ -4932,10 +4937,10 @@ function matchedVerbPrefix(args2, verbPaths) {
|
|
|
4932
4937
|
}
|
|
4933
4938
|
return best.join(" ");
|
|
4934
4939
|
}
|
|
4935
|
-
function renderError(headline, suggestion) {
|
|
4940
|
+
function renderError(headline, suggestion, footer = ENTRY_TEXTS.parseErrorFooter) {
|
|
4936
4941
|
const lines = [tx(ENTRY_TEXTS.parseErrorHeadline, { message: headline })];
|
|
4937
4942
|
if (suggestion) lines.push(suggestion);
|
|
4938
|
-
lines.push(
|
|
4943
|
+
lines.push(footer);
|
|
4939
4944
|
return lines.join("\n") + "\n";
|
|
4940
4945
|
}
|
|
4941
4946
|
function extractOffendingFlag(message) {
|
|
@@ -5365,16 +5370,13 @@ function kernelLocalSettingsPath(scopeRoot) {
|
|
|
5365
5370
|
// config/defaults.json
|
|
5366
5371
|
var defaults_default = {
|
|
5367
5372
|
schemaVersion: 1,
|
|
5368
|
-
autoMigrate: true,
|
|
5369
5373
|
allowEditSmFiles: false,
|
|
5370
5374
|
tokenizer: "cl100k_base",
|
|
5371
|
-
providers: [],
|
|
5372
5375
|
roots: [],
|
|
5373
5376
|
ignore: [],
|
|
5374
5377
|
scan: {
|
|
5375
5378
|
tokenize: true,
|
|
5376
5379
|
strict: false,
|
|
5377
|
-
followSymlinks: false,
|
|
5378
5380
|
maxFileSizeBytes: 1048576,
|
|
5379
5381
|
maxNodes: 256,
|
|
5380
5382
|
watch: {
|
|
@@ -5383,9 +5385,6 @@ var defaults_default = {
|
|
|
5383
5385
|
referencePaths: []
|
|
5384
5386
|
},
|
|
5385
5387
|
plugins: {},
|
|
5386
|
-
history: {
|
|
5387
|
-
share: false
|
|
5388
|
-
},
|
|
5389
5388
|
jobs: {
|
|
5390
5389
|
ttlSeconds: 3600,
|
|
5391
5390
|
graceMultiplier: 3,
|
|
@@ -5396,9 +5395,6 @@ var defaults_default = {
|
|
|
5396
5395
|
completed: 2592e3,
|
|
5397
5396
|
failed: null
|
|
5398
5397
|
}
|
|
5399
|
-
},
|
|
5400
|
-
i18n: {
|
|
5401
|
-
locale: "en"
|
|
5402
5398
|
}
|
|
5403
5399
|
};
|
|
5404
5400
|
|
|
@@ -6224,12 +6220,12 @@ var SmCommand = class extends Command {
|
|
|
6224
6220
|
};
|
|
6225
6221
|
|
|
6226
6222
|
// core/sqlite/with-sqlite.ts
|
|
6227
|
-
import { existsSync as
|
|
6223
|
+
import { existsSync as existsSync11 } from "fs";
|
|
6228
6224
|
|
|
6229
6225
|
// kernel/adapters/sqlite/storage-adapter.ts
|
|
6230
6226
|
import { mkdirSync as mkdirSync4 } from "fs";
|
|
6231
6227
|
import { dirname as dirname8, resolve as resolve12 } from "path";
|
|
6232
|
-
import { DatabaseSync as
|
|
6228
|
+
import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
|
|
6233
6229
|
import { CamelCasePlugin, Kysely, sql as sql3 } from "kysely";
|
|
6234
6230
|
|
|
6235
6231
|
// kernel/i18n/storage.texts.ts
|
|
@@ -7510,20 +7506,29 @@ async function loadScanResult(db) {
|
|
|
7510
7506
|
version: metaRow.scannedByVersion,
|
|
7511
7507
|
specVersion: metaRow.scannedBySpecVersion
|
|
7512
7508
|
};
|
|
7509
|
+
const oversizedFiles = parseJsonArray(metaRow.oversizedFilesJson);
|
|
7513
7510
|
return {
|
|
7514
7511
|
schemaVersion: 1,
|
|
7515
7512
|
scannedAt: metaRow.scannedAt,
|
|
7516
7513
|
roots: parseJsonArray(metaRow.rootsJson),
|
|
7517
7514
|
providers: parseJsonArray(metaRow.providersJson),
|
|
7518
7515
|
scannedBy,
|
|
7516
|
+
// Resolved encoder of the prior scan (see project-config.schema.json
|
|
7517
|
+
// §tokenizer). NULL column → `undefined` domain field; the
|
|
7518
|
+
// orchestrator's tokenizer-change check compares this against the
|
|
7519
|
+
// freshly-resolved encoder and treats a missing prior value as a
|
|
7520
|
+
// change (forcing a token recompute).
|
|
7521
|
+
...metaRow.tokenizer !== null ? { tokenizer: metaRow.tokenizer } : {},
|
|
7519
7522
|
recommendedNodeLimit: metaRow.recommendedNodeLimit,
|
|
7520
7523
|
overrideMaxNodes: metaRow.overrideMaxNodes,
|
|
7524
|
+
oversizedFiles,
|
|
7521
7525
|
nodes,
|
|
7522
7526
|
links,
|
|
7523
7527
|
issues,
|
|
7524
7528
|
stats: {
|
|
7525
7529
|
filesWalked: metaRow.statsFilesWalked,
|
|
7526
7530
|
filesSkipped: metaRow.statsFilesSkipped,
|
|
7531
|
+
filesOversized: metaRow.filesOversized,
|
|
7527
7532
|
nodesCount: nodes.length,
|
|
7528
7533
|
linksCount: links.length,
|
|
7529
7534
|
issuesCount: issues.length,
|
|
@@ -7546,12 +7551,14 @@ async function loadScanResult(db) {
|
|
|
7546
7551
|
// scan overwrites scan_meta with the live values on next run.
|
|
7547
7552
|
recommendedNodeLimit: 256,
|
|
7548
7553
|
overrideMaxNodes: null,
|
|
7554
|
+
oversizedFiles: [],
|
|
7549
7555
|
nodes,
|
|
7550
7556
|
links,
|
|
7551
7557
|
issues,
|
|
7552
7558
|
stats: {
|
|
7553
7559
|
filesWalked: 0,
|
|
7554
7560
|
filesSkipped: 0,
|
|
7561
|
+
filesOversized: 0,
|
|
7555
7562
|
nodesCount: nodes.length,
|
|
7556
7563
|
linksCount: links.length,
|
|
7557
7564
|
issuesCount: issues.length,
|
|
@@ -7845,6 +7852,58 @@ function rowToContribution(row) {
|
|
|
7845
7852
|
};
|
|
7846
7853
|
}
|
|
7847
7854
|
|
|
7855
|
+
// core/sqlite/schema-fingerprint.ts
|
|
7856
|
+
import { createHash } from "crypto";
|
|
7857
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
7858
|
+
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
7859
|
+
var memoized = null;
|
|
7860
|
+
function schemaFingerprint(files) {
|
|
7861
|
+
if (files === void 0 && memoized !== null) return memoized;
|
|
7862
|
+
const migrations = files ?? discoverMigrations();
|
|
7863
|
+
const hash = createHash("sha256");
|
|
7864
|
+
for (const m of migrations) {
|
|
7865
|
+
const sql4 = existsSync10(m.filePath) ? readFileSync10(m.filePath, "utf8") : "";
|
|
7866
|
+
hash.update(`${m.version}\0${m.description}\0${Buffer.byteLength(sql4)}\0`);
|
|
7867
|
+
hash.update(sql4);
|
|
7868
|
+
}
|
|
7869
|
+
const digest = hash.digest("hex");
|
|
7870
|
+
if (files === void 0) memoized = digest;
|
|
7871
|
+
return digest;
|
|
7872
|
+
}
|
|
7873
|
+
function readStoredFingerprint(dbPath) {
|
|
7874
|
+
if (dbPath === ":memory:" || !existsSync10(dbPath)) return { kind: "no-meta" };
|
|
7875
|
+
let raw = null;
|
|
7876
|
+
try {
|
|
7877
|
+
raw = new DatabaseSync3(dbPath, { readOnly: true });
|
|
7878
|
+
return readStoredFingerprintFrom(raw);
|
|
7879
|
+
} catch {
|
|
7880
|
+
return { kind: "no-meta" };
|
|
7881
|
+
} finally {
|
|
7882
|
+
raw?.close();
|
|
7883
|
+
}
|
|
7884
|
+
}
|
|
7885
|
+
function readStoredFingerprintFrom(raw) {
|
|
7886
|
+
if (!columnExists(raw, "scan_meta", "schema_fingerprint")) {
|
|
7887
|
+
const hasRow = raw.prepare("SELECT 1 AS present FROM scan_meta LIMIT 1").get();
|
|
7888
|
+
return hasRow ? { kind: "absent" } : { kind: "no-meta" };
|
|
7889
|
+
}
|
|
7890
|
+
const row = raw.prepare("SELECT schema_fingerprint AS fp FROM scan_meta LIMIT 1").get();
|
|
7891
|
+
if (!row) return { kind: "no-meta" };
|
|
7892
|
+
const fp = row.fp;
|
|
7893
|
+
if (typeof fp !== "string" || fp.length === 0) return { kind: "absent" };
|
|
7894
|
+
return { kind: "value", fingerprint: fp };
|
|
7895
|
+
}
|
|
7896
|
+
function columnExists(db, table, column) {
|
|
7897
|
+
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
7898
|
+
return rows.some((r) => r.name === column);
|
|
7899
|
+
}
|
|
7900
|
+
function classifyFingerprint(dbPath) {
|
|
7901
|
+
const stored = readStoredFingerprint(dbPath);
|
|
7902
|
+
if (stored.kind === "no-meta") return { kind: "no-meta" };
|
|
7903
|
+
if (stored.kind === "absent") return { kind: "drift" };
|
|
7904
|
+
return stored.fingerprint === schemaFingerprint() ? { kind: "ok" } : { kind: "drift" };
|
|
7905
|
+
}
|
|
7906
|
+
|
|
7848
7907
|
// kernel/adapters/sqlite/tags.ts
|
|
7849
7908
|
async function replaceAllScanTags(trx, records, livePaths = /* @__PURE__ */ new Set()) {
|
|
7850
7909
|
if (livePaths.size > 0) {
|
|
@@ -8124,11 +8183,30 @@ function metaToRow(result) {
|
|
|
8124
8183
|
scannedByName: result.scannedBy?.name ?? "skill-map",
|
|
8125
8184
|
scannedByVersion: result.scannedBy?.version ?? "unknown",
|
|
8126
8185
|
scannedBySpecVersion: result.scannedBy?.specVersion ?? "unknown",
|
|
8186
|
+
// Schema-drift fingerprint: sha256 over the bundled migration DDL.
|
|
8187
|
+
// Sibling of `scannedByVersion`, the second drift axis the next
|
|
8188
|
+
// write-side open compares (see spec/db-schema.md §Schema drift
|
|
8189
|
+
// (pre-1.0)). Internal DB metadata, never on the ScanResult wire.
|
|
8190
|
+
schemaFingerprint: schemaFingerprint(),
|
|
8127
8191
|
providersJson: JSON.stringify(result.providers),
|
|
8128
8192
|
statsFilesWalked: result.stats.filesWalked,
|
|
8129
8193
|
statsFilesSkipped: result.stats.filesSkipped,
|
|
8130
8194
|
statsDurationMs: result.stats.durationMs,
|
|
8131
|
-
|
|
8195
|
+
// Resolved encoder that produced the token counts (see
|
|
8196
|
+
// project-config.schema.json §tokenizer). NULL on synthetic results
|
|
8197
|
+
// that bypass the orchestrator / skip tokenization; a real scan always
|
|
8198
|
+
// carries it. The next incremental scan reads this back to detect an
|
|
8199
|
+
// encoder switch and force a token recompute.
|
|
8200
|
+
tokenizer: result.tokenizer ?? null,
|
|
8201
|
+
...projectNodeLimitColumns(result),
|
|
8202
|
+
...projectOversizedColumns(result)
|
|
8203
|
+
};
|
|
8204
|
+
}
|
|
8205
|
+
function projectOversizedColumns(result) {
|
|
8206
|
+
const oversized = result.oversizedFiles ?? [];
|
|
8207
|
+
return {
|
|
8208
|
+
filesOversized: result.stats.filesOversized ?? oversized.length,
|
|
8209
|
+
oversizedFilesJson: oversized.length > 0 ? JSON.stringify(oversized) : null
|
|
8132
8210
|
};
|
|
8133
8211
|
}
|
|
8134
8212
|
function projectNodeLimitColumns(result) {
|
|
@@ -8254,7 +8332,7 @@ var SqliteStorageAdapter = class {
|
|
|
8254
8332
|
if (this.#options.autoMigrate !== false) {
|
|
8255
8333
|
const files = discoverMigrations();
|
|
8256
8334
|
if (files.length > 0) {
|
|
8257
|
-
const raw = new
|
|
8335
|
+
const raw = new DatabaseSync4(path);
|
|
8258
8336
|
try {
|
|
8259
8337
|
raw.exec("PRAGMA foreign_keys = ON");
|
|
8260
8338
|
applyMigrations(
|
|
@@ -8641,7 +8719,7 @@ async function listFavoritePaths(db) {
|
|
|
8641
8719
|
return new Set(rows.map((r) => r.nodePath));
|
|
8642
8720
|
}
|
|
8643
8721
|
function withRawDb(path, fn) {
|
|
8644
|
-
const raw = new
|
|
8722
|
+
const raw = new DatabaseSync4(path);
|
|
8645
8723
|
try {
|
|
8646
8724
|
return fn(raw);
|
|
8647
8725
|
} finally {
|
|
@@ -8720,7 +8798,14 @@ var DB_VERSION_TEXTS = {
|
|
|
8720
8798
|
dbVersionMajorMismatch: "{{glyph}} This DB was written by skill-map {{dbVersion}}; the CLI you are running ({{currentVersion}}) is on a different major series.\n {{hint}}\n",
|
|
8721
8799
|
dbVersionMajorMismatchHint: "Delete the `.skill-map/` directory and re-scan, or revert to a CLI in the {{dbMajor}}.x series.",
|
|
8722
8800
|
dbVersionOlder: "{{glyph}} This DB was last written by an older skill-map ({{dbVersion}}, you have {{currentVersion}}).\n {{hint}}\n",
|
|
8723
|
-
dbVersionOlderHint: "Behaviour may differ until the next `sm scan` rewrites the metadata; downstream parse errors are likely a symptom of this skew."
|
|
8801
|
+
dbVersionOlderHint: "Behaviour may differ until the next `sm scan` rewrites the metadata; downstream parse errors are likely a symptom of this skew.",
|
|
8802
|
+
// Schema-fingerprint drift on a same-version DB (pre-1.0 greenfield:
|
|
8803
|
+
// a column was added inline to a migration with no version bump, so
|
|
8804
|
+
// the version axis reads as compatible but the on-disk schema is
|
|
8805
|
+
// older). WARN, `⚠` yellow, the read continues but a query may hit a
|
|
8806
|
+
// missing column. Has no `dbVersion` placeholder, the version matched.
|
|
8807
|
+
dbSchemaDrift: "{{glyph}} This DB predates a schema change in skill-map {{currentVersion}} (same version, older columns).\n {{hint}}\n",
|
|
8808
|
+
dbSchemaDriftHint: "Run `sm scan` to rebuild the local cache (your .sm sidecars are untouched), or `sm db reset`; some columns may be missing until then."
|
|
8724
8809
|
// The defensive wrapper for `parseConfidence` / `parseLinkKind` /
|
|
8725
8810
|
// `parseSeverity` failures during `loadScanResult` (when the meta
|
|
8726
8811
|
// row was wiped and the version check returned `no-meta`) lives in
|
|
@@ -8735,10 +8820,17 @@ var DB_VERSION_TEXTS = {
|
|
|
8735
8820
|
// core/sqlite/db-version-runner.ts
|
|
8736
8821
|
var WARN_SEEN = /* @__PURE__ */ new Set();
|
|
8737
8822
|
async function runDbVersionCheck(db, opts) {
|
|
8738
|
-
const
|
|
8823
|
+
const versionOutcome = await detectDbVersionSkew(db, opts.currentVersion);
|
|
8824
|
+
const outcome = layerFingerprintOutcome(versionOutcome, opts);
|
|
8739
8825
|
applyDbVersionOutcome(outcome, opts);
|
|
8740
8826
|
return outcome;
|
|
8741
8827
|
}
|
|
8828
|
+
function layerFingerprintOutcome(versionOutcome, opts) {
|
|
8829
|
+
if (versionOutcome.kind !== "ok") return versionOutcome;
|
|
8830
|
+
const fp = classifyFingerprint(opts.dbPath);
|
|
8831
|
+
if (fp.kind === "drift") return { kind: "warn-schema", currentVersion: opts.currentVersion };
|
|
8832
|
+
return versionOutcome;
|
|
8833
|
+
}
|
|
8742
8834
|
function applyDbVersionOutcome(outcome, opts) {
|
|
8743
8835
|
switch (outcome.kind) {
|
|
8744
8836
|
case "ok":
|
|
@@ -8751,6 +8843,9 @@ function applyDbVersionOutcome(outcome, opts) {
|
|
|
8751
8843
|
case "warn-older":
|
|
8752
8844
|
renderWarnOlder(outcome, opts);
|
|
8753
8845
|
break;
|
|
8846
|
+
case "warn-schema":
|
|
8847
|
+
renderWarnSchema(outcome, opts);
|
|
8848
|
+
break;
|
|
8754
8849
|
}
|
|
8755
8850
|
}
|
|
8756
8851
|
function renderErrorNewer(outcome, opts) {
|
|
@@ -8804,6 +8899,21 @@ function renderWarnOlder(outcome, opts) {
|
|
|
8804
8899
|
})
|
|
8805
8900
|
);
|
|
8806
8901
|
}
|
|
8902
|
+
function renderWarnSchema(outcome, opts) {
|
|
8903
|
+
const seen = opts.warnSeen ?? WARN_SEEN;
|
|
8904
|
+
if (seen.has(opts.dbPath)) return;
|
|
8905
|
+
seen.add(opts.dbPath);
|
|
8906
|
+
if (!opts.printer) return;
|
|
8907
|
+
const warnGlyph = opts.style?.warnGlyph ?? "\u26A0";
|
|
8908
|
+
const dim = opts.style?.dim ?? ((s) => s);
|
|
8909
|
+
opts.printer.warn(
|
|
8910
|
+
tx(DB_VERSION_TEXTS.dbSchemaDrift, {
|
|
8911
|
+
glyph: warnGlyph,
|
|
8912
|
+
currentVersion: outcome.currentVersion,
|
|
8913
|
+
hint: dim(DB_VERSION_TEXTS.dbSchemaDriftHint)
|
|
8914
|
+
})
|
|
8915
|
+
);
|
|
8916
|
+
}
|
|
8807
8917
|
|
|
8808
8918
|
// core/sqlite/with-sqlite.ts
|
|
8809
8919
|
async function withSqlite(options, fn) {
|
|
@@ -8823,7 +8933,7 @@ async function withSqlite(options, fn) {
|
|
|
8823
8933
|
}
|
|
8824
8934
|
}
|
|
8825
8935
|
async function tryWithSqlite(options, fn) {
|
|
8826
|
-
if (options.databasePath !== ":memory:" && !
|
|
8936
|
+
if (options.databasePath !== ":memory:" && !existsSync11(options.databasePath)) {
|
|
8827
8937
|
return null;
|
|
8828
8938
|
}
|
|
8829
8939
|
return withSqlite(options, fn);
|
|
@@ -9424,6 +9534,19 @@ var CHECK_TEXTS = {
|
|
|
9424
9534
|
unknownAnalyzerIds: "sm check: unknown analyzer id(s) in --analyzers: {{unknown}}.\nValid ids (qualified or short form accepted):\n{{known}}\n"
|
|
9425
9535
|
};
|
|
9426
9536
|
|
|
9537
|
+
// cli/util/db-version-check.ts
|
|
9538
|
+
function buildReadVersionCheck(printer, stderrAnsi) {
|
|
9539
|
+
return {
|
|
9540
|
+
currentVersion: VERSION,
|
|
9541
|
+
printer,
|
|
9542
|
+
style: {
|
|
9543
|
+
warnGlyph: stderrAnsi.yellow("\u26A0"),
|
|
9544
|
+
errorGlyph: stderrAnsi.red("\u2715"),
|
|
9545
|
+
dim: stderrAnsi.dim
|
|
9546
|
+
}
|
|
9547
|
+
};
|
|
9548
|
+
}
|
|
9549
|
+
|
|
9427
9550
|
// cli/util/conformance-env.ts
|
|
9428
9551
|
var ENV_DISABLE_PROVIDERS = "SKILL_MAP_DISABLE_ALL_PROVIDERS";
|
|
9429
9552
|
var ENV_DISABLE_EXTRACTORS = "SKILL_MAP_DISABLE_ALL_EXTRACTORS";
|
|
@@ -9452,6 +9575,7 @@ var PLUGIN_LOADER_TEXTS = {
|
|
|
9452
9575
|
invalidManifestDirMismatch: "directory name '{{dirName}}' does not match manifest id '{{manifestId}}'. Rename the directory to match the id, or update the manifest id to match the directory.",
|
|
9453
9576
|
idCollision: "Plugin '{{id}}' at {{pathA}} collides with the plugin at {{pathB}}. Rename one and rerun.",
|
|
9454
9577
|
loadErrorPluginIdMismatch: "{{relEntry}}: extension declares pluginId '{{declared}}' but its plugin.json declares id '{{manifestId}}'. Remove the explicit pluginId from the extension; the loader injects it from plugin.json#/id.",
|
|
9578
|
+
invalidManifestRedeclaredField: "{{relEntry}}: extension manifest declares {{fields}}, derived from the folder layout (structure-as-truth) and not a manifest field. Remove it: id is the leaf folder, kind the parent folder, provider kinds the `kinds/` catalog, formatter formatId the formatter folder name.",
|
|
9455
9579
|
loadErrorStorageSchemaRead: "plugin '{{pluginId}}' failed to load schema for table '{{table}}': {{schemaPath}}: {{errDescription}}",
|
|
9456
9580
|
loadErrorStorageSchemaCompile: "plugin '{{pluginId}}' failed to compile schema for table '{{table}}': {{schemaPath}}: {{errDescription}}",
|
|
9457
9581
|
loadErrorStorageKvSchemaRead: "plugin '{{pluginId}}' failed to load KV schema: {{schemaPath}}: {{errDescription}}",
|
|
@@ -9467,7 +9591,7 @@ var PLUGIN_LOADER_TEXTS = {
|
|
|
9467
9591
|
|
|
9468
9592
|
// kernel/adapters/plugin-loader/index.ts
|
|
9469
9593
|
import { createRequire as createRequire5 } from "module";
|
|
9470
|
-
import { existsSync as
|
|
9594
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
9471
9595
|
import { join as join8, resolve as resolve17 } from "path";
|
|
9472
9596
|
import { pathToFileURL } from "url";
|
|
9473
9597
|
import semver from "semver";
|
|
@@ -9556,7 +9680,7 @@ function extractDefault(mod) {
|
|
|
9556
9680
|
if (!isRecord(mod)) return mod;
|
|
9557
9681
|
return "default" in mod ? mod["default"] : mod;
|
|
9558
9682
|
}
|
|
9559
|
-
var LOADER_INJECTED_KEYS = /* @__PURE__ */ new Set(["pluginId"
|
|
9683
|
+
var LOADER_INJECTED_KEYS = /* @__PURE__ */ new Set(["pluginId"]);
|
|
9560
9684
|
function stripFunctionsAndPluginId(input) {
|
|
9561
9685
|
if (!isRecord(input)) return input;
|
|
9562
9686
|
const out = {};
|
|
@@ -9570,7 +9694,7 @@ function stripFunctionsAndPluginId(input) {
|
|
|
9570
9694
|
|
|
9571
9695
|
// kernel/adapters/plugin-loader/validation.ts
|
|
9572
9696
|
import * as nodeFs from "fs";
|
|
9573
|
-
import { existsSync as
|
|
9697
|
+
import { existsSync as existsSync12 } from "fs";
|
|
9574
9698
|
import { dirname as dirname9, join as join7 } from "path";
|
|
9575
9699
|
import { Ajv2020 as Ajv20205 } from "ajv/dist/2020.js";
|
|
9576
9700
|
|
|
@@ -9696,7 +9820,7 @@ function validateActionFileConventions(pluginPath, pluginId, manifest, relEntry,
|
|
|
9696
9820
|
const reportSchemaPath = join7(actionDir, "report.schema.json");
|
|
9697
9821
|
const promptPath = join7(actionDir, "prompt.md");
|
|
9698
9822
|
const mode = isRecord(manifestView) && typeof manifestView["mode"] === "string" ? manifestView["mode"] : "deterministic";
|
|
9699
|
-
if (!
|
|
9823
|
+
if (!existsSync12(reportSchemaPath)) {
|
|
9700
9824
|
return {
|
|
9701
9825
|
...fail(
|
|
9702
9826
|
pluginPath,
|
|
@@ -9707,7 +9831,7 @@ function validateActionFileConventions(pluginPath, pluginId, manifest, relEntry,
|
|
|
9707
9831
|
manifest
|
|
9708
9832
|
};
|
|
9709
9833
|
}
|
|
9710
|
-
const promptExists =
|
|
9834
|
+
const promptExists = existsSync12(promptPath);
|
|
9711
9835
|
if (mode === "probabilistic" && !promptExists) {
|
|
9712
9836
|
return {
|
|
9713
9837
|
...fail(
|
|
@@ -9819,7 +9943,7 @@ function isDirectorySafe(path, statSync12) {
|
|
|
9819
9943
|
}
|
|
9820
9944
|
|
|
9821
9945
|
// kernel/adapters/plugin-loader/storage-schemas.ts
|
|
9822
|
-
import { readFileSync as
|
|
9946
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
9823
9947
|
import { resolve as resolve16 } from "path";
|
|
9824
9948
|
import { Ajv2020 as Ajv20206 } from "ajv/dist/2020.js";
|
|
9825
9949
|
|
|
@@ -9887,7 +10011,7 @@ function compilePluginSchema(pluginPath, relPath) {
|
|
|
9887
10011
|
const abs = resolve16(pluginPath, relPath);
|
|
9888
10012
|
let raw;
|
|
9889
10013
|
try {
|
|
9890
|
-
raw = JSON.parse(
|
|
10014
|
+
raw = JSON.parse(readFileSync12(abs, "utf8"));
|
|
9891
10015
|
} catch (err) {
|
|
9892
10016
|
return { ok: false, phase: "read", errDescription: describe(err) };
|
|
9893
10017
|
}
|
|
@@ -9921,11 +10045,11 @@ var PluginLoader = class {
|
|
|
9921
10045
|
discoverPaths() {
|
|
9922
10046
|
const out = [];
|
|
9923
10047
|
for (const root of this.#options.searchPaths) {
|
|
9924
|
-
if (!
|
|
10048
|
+
if (!existsSync13(root)) continue;
|
|
9925
10049
|
for (const entry of readdirSync4(root, { withFileTypes: true })) {
|
|
9926
10050
|
if (!entry.isDirectory()) continue;
|
|
9927
10051
|
const candidate = join8(root, entry.name);
|
|
9928
|
-
if (
|
|
10052
|
+
if (existsSync13(join8(candidate, "plugin.json"))) {
|
|
9929
10053
|
out.push(resolve17(candidate));
|
|
9930
10054
|
}
|
|
9931
10055
|
}
|
|
@@ -10009,7 +10133,7 @@ var PluginLoader = class {
|
|
|
10009
10133
|
const manifestPath = join8(pluginPath, "plugin.json");
|
|
10010
10134
|
let raw;
|
|
10011
10135
|
try {
|
|
10012
|
-
raw = JSON.parse(
|
|
10136
|
+
raw = JSON.parse(readFileSync13(manifestPath, "utf8"));
|
|
10013
10137
|
} catch (err) {
|
|
10014
10138
|
return { ok: false, failure: fail(
|
|
10015
10139
|
pluginPath,
|
|
@@ -10086,7 +10210,7 @@ var PluginLoader = class {
|
|
|
10086
10210
|
} };
|
|
10087
10211
|
}
|
|
10088
10212
|
const abs = resolve17(pluginPath, relEntry);
|
|
10089
|
-
if (!
|
|
10213
|
+
if (!existsSync13(abs)) {
|
|
10090
10214
|
return { ok: false, failure: {
|
|
10091
10215
|
...fail(
|
|
10092
10216
|
pluginPath,
|
|
@@ -10177,6 +10301,21 @@ var PluginLoader = class {
|
|
|
10177
10301
|
manifest
|
|
10178
10302
|
} };
|
|
10179
10303
|
}
|
|
10304
|
+
const redeclared = DERIVED_MANIFEST_KEYS.filter((field) => field in exported);
|
|
10305
|
+
if (redeclared.length > 0) {
|
|
10306
|
+
return { ok: false, failure: {
|
|
10307
|
+
...fail(
|
|
10308
|
+
pluginPath,
|
|
10309
|
+
pluginId,
|
|
10310
|
+
"invalid-manifest",
|
|
10311
|
+
tx(PLUGIN_LOADER_TEXTS.invalidManifestRedeclaredField, {
|
|
10312
|
+
relEntry,
|
|
10313
|
+
fields: redeclared.map((field) => `\`${field}\``).join(", ")
|
|
10314
|
+
})
|
|
10315
|
+
),
|
|
10316
|
+
manifest
|
|
10317
|
+
} };
|
|
10318
|
+
}
|
|
10180
10319
|
const manifestView = stripFunctionsAndPluginId(exported);
|
|
10181
10320
|
if (kind === "hook") {
|
|
10182
10321
|
const hookFailure = validateHookTriggers(pluginPath, pluginId, manifest, relEntry, exported, manifestView);
|
|
@@ -10233,8 +10372,7 @@ var PluginLoader = class {
|
|
|
10233
10372
|
const instance = { ...exported, pluginId, id: pathId2, kind };
|
|
10234
10373
|
if (kind === "formatter") instance["formatId"] = pathId2;
|
|
10235
10374
|
if (kind === "provider" && discoveredKinds) {
|
|
10236
|
-
|
|
10237
|
-
instance["kinds"] = { ...inlineKinds, ...discoveredKinds };
|
|
10375
|
+
instance["kinds"] = discoveredKinds;
|
|
10238
10376
|
}
|
|
10239
10377
|
return { ok: true, extension: {
|
|
10240
10378
|
kind,
|
|
@@ -10247,6 +10385,7 @@ var PluginLoader = class {
|
|
|
10247
10385
|
} };
|
|
10248
10386
|
}
|
|
10249
10387
|
};
|
|
10388
|
+
var DERIVED_MANIFEST_KEYS = ["id", "kind", "kinds", "formatId"];
|
|
10250
10389
|
var KIND_DIR_NAMES = [
|
|
10251
10390
|
"providers",
|
|
10252
10391
|
"extractors",
|
|
@@ -10269,7 +10408,7 @@ function discoverExtensionEntries(pluginPath) {
|
|
|
10269
10408
|
}
|
|
10270
10409
|
function collectKindEntries(pluginPath, kindDir, out) {
|
|
10271
10410
|
const kindAbs = resolve17(pluginPath, kindDir);
|
|
10272
|
-
if (!
|
|
10411
|
+
if (!existsSync13(kindAbs)) return;
|
|
10273
10412
|
let entries;
|
|
10274
10413
|
try {
|
|
10275
10414
|
entries = readdirSync4(kindAbs);
|
|
@@ -10296,7 +10435,7 @@ function isDirectorySafe2(path) {
|
|
|
10296
10435
|
}
|
|
10297
10436
|
function findIndexCandidate(entryAbs) {
|
|
10298
10437
|
for (const candidate of INDEX_CANDIDATES) {
|
|
10299
|
-
if (
|
|
10438
|
+
if (existsSync13(resolve17(entryAbs, candidate))) return candidate;
|
|
10300
10439
|
}
|
|
10301
10440
|
return null;
|
|
10302
10441
|
}
|
|
@@ -10304,7 +10443,7 @@ function installedSpecVersion() {
|
|
|
10304
10443
|
const require2 = createRequire5(import.meta.url);
|
|
10305
10444
|
const indexPath = require2.resolve("@skill-map/spec/index.json");
|
|
10306
10445
|
const pkgPath = resolve17(indexPath, "..", "package.json");
|
|
10307
|
-
const pkg = JSON.parse(
|
|
10446
|
+
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
10308
10447
|
return pkg.version;
|
|
10309
10448
|
}
|
|
10310
10449
|
|
|
@@ -10365,11 +10504,11 @@ function makeEnabledResolver(cfg, dbOverrides) {
|
|
|
10365
10504
|
function defaultResolveEnabled(_id) {
|
|
10366
10505
|
return true;
|
|
10367
10506
|
}
|
|
10368
|
-
function isBuiltInExtensionEnabled(
|
|
10369
|
-
return
|
|
10507
|
+
function isBuiltInExtensionEnabled(plugin, ext, resolveEnabled) {
|
|
10508
|
+
return isPluginEntryEnabled(plugin, ext.id, resolveEnabled);
|
|
10370
10509
|
}
|
|
10371
|
-
function
|
|
10372
|
-
return resolveEnabled(qualifiedExtensionId(
|
|
10510
|
+
function isPluginEntryEnabled(plugin, extId, resolveEnabled) {
|
|
10511
|
+
return resolveEnabled(qualifiedExtensionId(plugin.id, extId));
|
|
10373
10512
|
}
|
|
10374
10513
|
function isPluginExtensionEnabled(ext, resolveEnabled) {
|
|
10375
10514
|
return resolveEnabled(qualifiedExtensionId(ext.pluginId, ext.id));
|
|
@@ -10392,7 +10531,7 @@ import { readFile, readdir, lstat } from "fs/promises";
|
|
|
10392
10531
|
import { join as join9, relative as relative2, sep as sep3 } from "path";
|
|
10393
10532
|
|
|
10394
10533
|
// kernel/scan/ignore.ts
|
|
10395
|
-
import { existsSync as
|
|
10534
|
+
import { existsSync as existsSync14, readFileSync as readFileSync14 } from "fs";
|
|
10396
10535
|
import { dirname as dirname10, resolve as resolve18 } from "path";
|
|
10397
10536
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10398
10537
|
import ignoreFactory from "ignore";
|
|
@@ -10423,9 +10562,9 @@ function loadBundledIgnoreText() {
|
|
|
10423
10562
|
}
|
|
10424
10563
|
function readIgnoreFileText(scopeRoot) {
|
|
10425
10564
|
const path = resolve18(scopeRoot, ".skillmapignore");
|
|
10426
|
-
if (!
|
|
10565
|
+
if (!existsSync14(path)) return void 0;
|
|
10427
10566
|
try {
|
|
10428
|
-
return
|
|
10567
|
+
return readFileSync14(path, "utf8");
|
|
10429
10568
|
} catch {
|
|
10430
10569
|
return void 0;
|
|
10431
10570
|
}
|
|
@@ -10458,9 +10597,9 @@ function readDefaultsFromDisk() {
|
|
|
10458
10597
|
resolve18(here, "config/defaults/skillmapignore")
|
|
10459
10598
|
];
|
|
10460
10599
|
for (const candidate of candidates) {
|
|
10461
|
-
if (
|
|
10600
|
+
if (existsSync14(candidate)) {
|
|
10462
10601
|
try {
|
|
10463
|
-
return
|
|
10602
|
+
return readFileSync14(candidate, "utf8");
|
|
10464
10603
|
} catch {
|
|
10465
10604
|
}
|
|
10466
10605
|
}
|
|
@@ -10568,8 +10707,9 @@ async function* walkContent(roots, options) {
|
|
|
10568
10707
|
if (!parser) throw new UnknownParserError(options.parser);
|
|
10569
10708
|
const filter = options.ignoreFilter ?? buildIgnoreFilter();
|
|
10570
10709
|
const extensions = options.extensions;
|
|
10710
|
+
const sizeLimit = buildSizeLimit(options);
|
|
10571
10711
|
for (const root of roots) {
|
|
10572
|
-
for await (const file of walkRoot(root, root, filter, extensions)) {
|
|
10712
|
+
for await (const file of walkRoot(root, root, filter, extensions, sizeLimit)) {
|
|
10573
10713
|
const relPath = relative2(root, file).split(sep3).join("/");
|
|
10574
10714
|
let raw;
|
|
10575
10715
|
try {
|
|
@@ -10592,7 +10732,15 @@ async function* walkContent(roots, options) {
|
|
|
10592
10732
|
}
|
|
10593
10733
|
}
|
|
10594
10734
|
}
|
|
10595
|
-
|
|
10735
|
+
function buildSizeLimit(options) {
|
|
10736
|
+
const sizeLimit = {};
|
|
10737
|
+
if (options.maxFileSizeBytes !== void 0) {
|
|
10738
|
+
sizeLimit.maxFileSizeBytes = options.maxFileSizeBytes;
|
|
10739
|
+
}
|
|
10740
|
+
if (options.onOversizedFile) sizeLimit.onOversizedFile = options.onOversizedFile;
|
|
10741
|
+
return sizeLimit;
|
|
10742
|
+
}
|
|
10743
|
+
async function* walkRoot(root, current, filter, extensions, sizeLimit) {
|
|
10596
10744
|
let entries;
|
|
10597
10745
|
try {
|
|
10598
10746
|
entries = await readdir(current, { withFileTypes: true, encoding: "utf8" });
|
|
@@ -10606,11 +10754,16 @@ async function* walkRoot(root, current, filter, extensions) {
|
|
|
10606
10754
|
if (filter.ignores(rel)) continue;
|
|
10607
10755
|
if (entry.isSymbolicLink()) continue;
|
|
10608
10756
|
if (entry.isDirectory()) {
|
|
10609
|
-
yield* walkRoot(root, full, filter, extensions);
|
|
10757
|
+
yield* walkRoot(root, full, filter, extensions, sizeLimit);
|
|
10610
10758
|
} else if (entry.isFile() && hasMatchingExtension(name, extensions)) {
|
|
10611
10759
|
try {
|
|
10612
10760
|
const s = await lstat(full);
|
|
10613
|
-
if (s.isFile())
|
|
10761
|
+
if (!s.isFile()) continue;
|
|
10762
|
+
if (sizeLimit.maxFileSizeBytes !== void 0 && s.size > sizeLimit.maxFileSizeBytes) {
|
|
10763
|
+
sizeLimit.onOversizedFile?.({ path: rel, bytes: s.size });
|
|
10764
|
+
continue;
|
|
10765
|
+
}
|
|
10766
|
+
yield full;
|
|
10614
10767
|
} catch {
|
|
10615
10768
|
}
|
|
10616
10769
|
}
|
|
@@ -10640,6 +10793,10 @@ function resolveProviderWalk(provider) {
|
|
|
10640
10793
|
parser: read.parser
|
|
10641
10794
|
};
|
|
10642
10795
|
if (options?.ignoreFilter) walkOptions.ignoreFilter = options.ignoreFilter;
|
|
10796
|
+
if (options?.maxFileSizeBytes !== void 0) {
|
|
10797
|
+
walkOptions.maxFileSizeBytes = options.maxFileSizeBytes;
|
|
10798
|
+
}
|
|
10799
|
+
if (options?.onOversizedFile) walkOptions.onOversizedFile = options.onOversizedFile;
|
|
10643
10800
|
return walkContent(roots, walkOptions);
|
|
10644
10801
|
};
|
|
10645
10802
|
}
|
|
@@ -10674,19 +10831,19 @@ function collectViewContributions(pluginId, extensionId, instance, out, options
|
|
|
10674
10831
|
}
|
|
10675
10832
|
|
|
10676
10833
|
// core/runtime/plugin-runtime/bucketing.ts
|
|
10677
|
-
function bucketLoaded(loaded,
|
|
10834
|
+
function bucketLoaded(loaded, runtime) {
|
|
10678
10835
|
for (const ext of loaded) {
|
|
10679
10836
|
const instance = ext.instance;
|
|
10680
10837
|
if (!isExtensionInstance(instance)) continue;
|
|
10681
10838
|
bucketByKind(ext.kind, instance, {
|
|
10682
|
-
provider:
|
|
10683
|
-
extractor:
|
|
10684
|
-
analyzer:
|
|
10685
|
-
formatter:
|
|
10686
|
-
hook:
|
|
10839
|
+
provider: runtime.extensions.providers,
|
|
10840
|
+
extractor: runtime.extensions.extractors,
|
|
10841
|
+
analyzer: runtime.extensions.analyzers,
|
|
10842
|
+
formatter: runtime.extensions.formatters,
|
|
10843
|
+
hook: runtime.extensions.hooks
|
|
10687
10844
|
// `action` intentionally absent, see docstring.
|
|
10688
10845
|
});
|
|
10689
|
-
|
|
10846
|
+
runtime.manifests.push({
|
|
10690
10847
|
id: ext.id,
|
|
10691
10848
|
pluginId: ext.pluginId,
|
|
10692
10849
|
kind: ext.kind,
|
|
@@ -10694,8 +10851,8 @@ function bucketLoaded(loaded, bundle) {
|
|
|
10694
10851
|
description: instance.description ?? "",
|
|
10695
10852
|
...ext.entryPath ? { entry: ext.entryPath } : {}
|
|
10696
10853
|
});
|
|
10697
|
-
collectAnnotationContributions(ext.pluginId, instance,
|
|
10698
|
-
collectViewContributions(ext.pluginId, ext.id, instance,
|
|
10854
|
+
collectAnnotationContributions(ext.pluginId, instance, runtime.annotationContributions);
|
|
10855
|
+
collectViewContributions(ext.pluginId, ext.id, instance, runtime.viewContributions);
|
|
10699
10856
|
}
|
|
10700
10857
|
}
|
|
10701
10858
|
function collectAnnotationContributions(pluginId, instance, out) {
|
|
@@ -10753,8 +10910,8 @@ var PLUGIN_RUNTIME_TEXTS = {
|
|
|
10753
10910
|
// core/runtime/plugin-runtime/warnings.ts
|
|
10754
10911
|
var PLUGIN_ID_DISPLAY_CAP = 200;
|
|
10755
10912
|
var PLUGIN_REASON_DISPLAY_CAP = 1e3;
|
|
10756
|
-
function emitWarnings(
|
|
10757
|
-
for (const warn of
|
|
10913
|
+
function emitWarnings(runtime, printer) {
|
|
10914
|
+
for (const warn of runtime.warnings) {
|
|
10758
10915
|
printer.warn(`${warn}
|
|
10759
10916
|
`);
|
|
10760
10917
|
}
|
|
@@ -10793,7 +10950,7 @@ async function loadPluginRuntime(opts = {}) {
|
|
|
10793
10950
|
if (resolveEnabled) loaderOpts.resolveEnabled = resolveEnabled;
|
|
10794
10951
|
const loader = createPluginLoader(loaderOpts);
|
|
10795
10952
|
const discovered = await loader.discoverAndLoadAll();
|
|
10796
|
-
const
|
|
10953
|
+
const runtime = {
|
|
10797
10954
|
extensions: { providers: [], extractors: [], analyzers: [], formatters: [], hooks: [] },
|
|
10798
10955
|
annotationContributions: [],
|
|
10799
10956
|
viewContributions: [],
|
|
@@ -10807,14 +10964,14 @@ async function loadPluginRuntime(opts = {}) {
|
|
|
10807
10964
|
};
|
|
10808
10965
|
for (const plugin of discovered) {
|
|
10809
10966
|
if (plugin.status === "enabled") {
|
|
10810
|
-
bucketLoaded(plugin.extensions ?? [],
|
|
10967
|
+
bucketLoaded(plugin.extensions ?? [], runtime);
|
|
10811
10968
|
continue;
|
|
10812
10969
|
}
|
|
10813
10970
|
if (plugin.status === "disabled") continue;
|
|
10814
|
-
|
|
10971
|
+
runtime.warnings.push(formatWarning(plugin));
|
|
10815
10972
|
}
|
|
10816
|
-
enforceRootExclusivity(
|
|
10817
|
-
return
|
|
10973
|
+
enforceRootExclusivity(runtime.annotationContributions);
|
|
10974
|
+
return runtime;
|
|
10818
10975
|
}
|
|
10819
10976
|
var AnnotationContributionConflictError = class extends Error {
|
|
10820
10977
|
/** The colliding root-exclusive key. */
|
|
@@ -10848,7 +11005,7 @@ function enforceRootExclusivity(catalog) {
|
|
|
10848
11005
|
}
|
|
10849
11006
|
}
|
|
10850
11007
|
function emptyPluginRuntime() {
|
|
10851
|
-
const
|
|
11008
|
+
const runtime = {
|
|
10852
11009
|
extensions: { providers: [], extractors: [], analyzers: [], formatters: [], hooks: [] },
|
|
10853
11010
|
annotationContributions: [],
|
|
10854
11011
|
viewContributions: [],
|
|
@@ -10860,7 +11017,7 @@ function emptyPluginRuntime() {
|
|
|
10860
11017
|
emitWarnings(this, printer);
|
|
10861
11018
|
}
|
|
10862
11019
|
};
|
|
10863
|
-
return
|
|
11020
|
+
return runtime;
|
|
10864
11021
|
}
|
|
10865
11022
|
|
|
10866
11023
|
// core/runtime/plugin-runtime/catalogs.ts
|
|
@@ -10878,12 +11035,12 @@ function collectRegisteredContributionKeys(composed) {
|
|
|
10878
11035
|
return keys;
|
|
10879
11036
|
}
|
|
10880
11037
|
function filterBuiltInManifests(manifests, resolveEnabled) {
|
|
10881
|
-
const
|
|
10882
|
-
for (const
|
|
11038
|
+
const pluginById = /* @__PURE__ */ new Map();
|
|
11039
|
+
for (const plugin of builtInPlugins) pluginById.set(plugin.id, plugin);
|
|
10883
11040
|
return manifests.filter((m) => {
|
|
10884
|
-
const
|
|
10885
|
-
if (!
|
|
10886
|
-
return
|
|
11041
|
+
const plugin = pluginById.get(m.pluginId);
|
|
11042
|
+
if (!plugin) return true;
|
|
11043
|
+
return isPluginEntryEnabled(plugin, m.id, resolveEnabled);
|
|
10887
11044
|
});
|
|
10888
11045
|
}
|
|
10889
11046
|
|
|
@@ -10926,9 +11083,9 @@ function composeScanExtensions(opts) {
|
|
|
10926
11083
|
};
|
|
10927
11084
|
}
|
|
10928
11085
|
function accumulateBuiltInScanExtensions(buckets, resolveEnabled) {
|
|
10929
|
-
for (const
|
|
10930
|
-
for (const ext of
|
|
10931
|
-
if (!isBuiltInExtensionEnabled(
|
|
11086
|
+
for (const plugin of builtInPlugins) {
|
|
11087
|
+
for (const ext of plugin.extensions) {
|
|
11088
|
+
if (!isBuiltInExtensionEnabled(plugin, ext, resolveEnabled)) continue;
|
|
10932
11089
|
switch (ext.kind) {
|
|
10933
11090
|
case "provider":
|
|
10934
11091
|
buckets.providers.push(ext);
|
|
@@ -10959,10 +11116,10 @@ function composeFormatters(opts) {
|
|
|
10959
11116
|
const resolveEnabled = opts.resolveEnabled ?? opts.pluginRuntime.resolveEnabled;
|
|
10960
11117
|
const out = [];
|
|
10961
11118
|
if (!noBuiltIns) {
|
|
10962
|
-
for (const
|
|
10963
|
-
for (const ext of
|
|
11119
|
+
for (const plugin of builtInPlugins) {
|
|
11120
|
+
for (const ext of plugin.extensions) {
|
|
10964
11121
|
if (ext.kind !== "formatter") continue;
|
|
10965
|
-
if (!isBuiltInExtensionEnabled(
|
|
11122
|
+
if (!isBuiltInExtensionEnabled(plugin, ext, resolveEnabled)) continue;
|
|
10966
11123
|
out.push(ext);
|
|
10967
11124
|
}
|
|
10968
11125
|
}
|
|
@@ -10998,9 +11155,9 @@ function registerEnabledExtensions(kernel, pluginRuntime, options = {}) {
|
|
|
10998
11155
|
);
|
|
10999
11156
|
const merged = [...userContribs];
|
|
11000
11157
|
if (!noBuiltIns) {
|
|
11001
|
-
for (const
|
|
11002
|
-
for (const ext of
|
|
11003
|
-
if (!
|
|
11158
|
+
for (const plugin of builtInPlugins) {
|
|
11159
|
+
for (const ext of plugin.extensions) {
|
|
11160
|
+
if (!isPluginEntryEnabled(plugin, ext.id, resolveEnabled)) continue;
|
|
11004
11161
|
collectViewContributions(ext.pluginId, ext.id, ext, merged);
|
|
11005
11162
|
}
|
|
11006
11163
|
}
|
|
@@ -11060,27 +11217,35 @@ var CheckCommand = class extends SmCommand {
|
|
|
11060
11217
|
const analyzerFilter = parseAnalyzersFlag(this.analyzers);
|
|
11061
11218
|
const preflight = await this.#preflightAnalyzerCatalog(analyzerFilter);
|
|
11062
11219
|
if (preflight.exit !== null) return preflight.exit;
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
|
|
11069
|
-
|
|
11070
|
-
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
this.
|
|
11220
|
+
const stderrAnsi = this.ansiFor("stderr");
|
|
11221
|
+
return withSqlite(
|
|
11222
|
+
{
|
|
11223
|
+
databasePath: dbPath,
|
|
11224
|
+
autoBackup: false,
|
|
11225
|
+
versionCheck: buildReadVersionCheck(this.printer, stderrAnsi)
|
|
11226
|
+
},
|
|
11227
|
+
async (adapter) => {
|
|
11228
|
+
let issues = await adapter.issues.listAll();
|
|
11229
|
+
if (this.node !== void 0) {
|
|
11230
|
+
const nodePath = this.node;
|
|
11231
|
+
issues = issues.filter((i) => i.nodeIds.includes(nodePath));
|
|
11232
|
+
}
|
|
11233
|
+
if (analyzerFilter !== void 0) {
|
|
11234
|
+
issues = issues.filter((i) => matchesAnalyzerFilter(i.analyzerId, analyzerFilter));
|
|
11235
|
+
}
|
|
11236
|
+
const ansi = this.ansiFor("stdout");
|
|
11237
|
+
if (this.json) {
|
|
11238
|
+
this.printer.data(JSON.stringify(issues) + "\n");
|
|
11239
|
+
} else if (issues.length === 0) {
|
|
11240
|
+
this.printer.data(
|
|
11241
|
+
tx(CHECK_TEXTS.noIssues, { glyph: ansi.green("\u2713") })
|
|
11242
|
+
);
|
|
11243
|
+
} else {
|
|
11244
|
+
this.printer.data(renderHuman(issues, ansi));
|
|
11245
|
+
}
|
|
11246
|
+
return issues.some((i) => i.severity === "error") ? ExitCode.Issues : ExitCode.Ok;
|
|
11081
11247
|
}
|
|
11082
|
-
|
|
11083
|
-
});
|
|
11248
|
+
);
|
|
11084
11249
|
}
|
|
11085
11250
|
/**
|
|
11086
11251
|
* Either an explicit `--analyzers` list or `--include-prob` forces a
|
|
@@ -11249,11 +11414,11 @@ function trimRedundantPath(message, primary) {
|
|
|
11249
11414
|
}
|
|
11250
11415
|
|
|
11251
11416
|
// cli/commands/config.ts
|
|
11252
|
-
import { existsSync as
|
|
11417
|
+
import { existsSync as existsSync16 } from "fs";
|
|
11253
11418
|
import { Command as Command4, Option as Option4 } from "clipanion";
|
|
11254
11419
|
|
|
11255
11420
|
// core/config/active-provider.ts
|
|
11256
|
-
import { existsSync as
|
|
11421
|
+
import { existsSync as existsSync15 } from "fs";
|
|
11257
11422
|
import { join as join10 } from "path";
|
|
11258
11423
|
function resolveActiveProvider(cwd, providers = []) {
|
|
11259
11424
|
const detected = detectProvidersFromFilesystem(cwd, providers);
|
|
@@ -11273,7 +11438,7 @@ function detectProvidersFromFilesystem(cwd, providers) {
|
|
|
11273
11438
|
if (seen.has(provider.id)) continue;
|
|
11274
11439
|
const markers = provider.detect?.markers;
|
|
11275
11440
|
if (!markers || markers.length === 0) continue;
|
|
11276
|
-
if (!markers.some((marker) =>
|
|
11441
|
+
if (!markers.some((marker) => existsSync15(join10(cwd, marker)))) continue;
|
|
11277
11442
|
seen.add(provider.id);
|
|
11278
11443
|
out.push(provider.id);
|
|
11279
11444
|
}
|
|
@@ -11290,7 +11455,7 @@ function relativeIfBelow(path, cwd) {
|
|
|
11290
11455
|
}
|
|
11291
11456
|
|
|
11292
11457
|
// cli/util/scan-zone-drop.ts
|
|
11293
|
-
import { DatabaseSync as
|
|
11458
|
+
import { DatabaseSync as DatabaseSync5 } from "node:sqlite";
|
|
11294
11459
|
|
|
11295
11460
|
// cli/commands/db/shared.ts
|
|
11296
11461
|
var SAFE_SQL_IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
@@ -11302,7 +11467,7 @@ function assertSafeIdentifier(name) {
|
|
|
11302
11467
|
|
|
11303
11468
|
// cli/util/scan-zone-drop.ts
|
|
11304
11469
|
function dropScanZone(dbPath) {
|
|
11305
|
-
const db = new
|
|
11470
|
+
const db = new DatabaseSync5(dbPath);
|
|
11306
11471
|
try {
|
|
11307
11472
|
const rows = db.prepare(
|
|
11308
11473
|
"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'scan\\_%' ESCAPE '\\'"
|
|
@@ -11408,7 +11573,6 @@ var CONFIG_TEXTS = {
|
|
|
11408
11573
|
listSectionScan: "Scan",
|
|
11409
11574
|
listSectionJobs: "Jobs",
|
|
11410
11575
|
listSectionRootsAndPlugins: "Roots & plugins",
|
|
11411
|
-
listSectionHistory: "History",
|
|
11412
11576
|
listSectionOther: "Other"
|
|
11413
11577
|
};
|
|
11414
11578
|
|
|
@@ -11525,15 +11689,14 @@ var ConfigListCommand = class extends SmCommand {
|
|
|
11525
11689
|
var SECTION_DEFS = [
|
|
11526
11690
|
{
|
|
11527
11691
|
title: CONFIG_TEXTS.listSectionGeneral,
|
|
11528
|
-
exactKeys: ["
|
|
11692
|
+
exactKeys: ["schemaVersion", "tokenizer"]
|
|
11529
11693
|
},
|
|
11530
11694
|
{ title: CONFIG_TEXTS.listSectionScan, prefix: "scan.", stripPrefix: true },
|
|
11531
11695
|
{ title: CONFIG_TEXTS.listSectionJobs, prefix: "jobs.", stripPrefix: true },
|
|
11532
11696
|
{
|
|
11533
11697
|
title: CONFIG_TEXTS.listSectionRootsAndPlugins,
|
|
11534
|
-
exactKeys: ["roots", "
|
|
11535
|
-
}
|
|
11536
|
-
{ title: CONFIG_TEXTS.listSectionHistory, prefix: "history.", stripPrefix: true }
|
|
11698
|
+
exactKeys: ["roots", "plugins", "ignore"]
|
|
11699
|
+
}
|
|
11537
11700
|
];
|
|
11538
11701
|
function renderConfigSections(rows, ansi) {
|
|
11539
11702
|
const out = [];
|
|
@@ -11895,7 +12058,7 @@ var ConfigSetCommand = class extends SmCommand {
|
|
|
11895
12058
|
announceLensSwitch(cwd, ansi) {
|
|
11896
12059
|
const dbPath = resolveDbPath({ db: void 0, cwd });
|
|
11897
12060
|
const okGlyph = ansi.green("\u2713");
|
|
11898
|
-
if (!
|
|
12061
|
+
if (!existsSync16(dbPath)) {
|
|
11899
12062
|
this.printer.info(tx(CONFIG_TEXTS.lensSwitchedNoDb, { glyph: okGlyph }));
|
|
11900
12063
|
return;
|
|
11901
12064
|
}
|
|
@@ -11935,7 +12098,7 @@ var ConfigResetCommand = class extends SmCommand {
|
|
|
11935
12098
|
const path = targetSettingsPath2(target, ctx.cwd);
|
|
11936
12099
|
const ansi = this.ansiFor("stdout");
|
|
11937
12100
|
const okGlyph = ansi.green("\u2713");
|
|
11938
|
-
if (!
|
|
12101
|
+
if (!existsSync16(path)) {
|
|
11939
12102
|
this.printer.data(
|
|
11940
12103
|
tx(CONFIG_TEXTS.unsetNoOverride, {
|
|
11941
12104
|
glyph: okGlyph,
|
|
@@ -12010,14 +12173,14 @@ var CONFIG_COMMANDS = [
|
|
|
12010
12173
|
];
|
|
12011
12174
|
|
|
12012
12175
|
// cli/commands/conformance.ts
|
|
12013
|
-
import { existsSync as
|
|
12176
|
+
import { existsSync as existsSync19, readFileSync as readFileSync16 } from "fs";
|
|
12014
12177
|
import { dirname as dirname12, resolve as resolve22 } from "path";
|
|
12015
12178
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12016
12179
|
import { Command as Command5, Option as Option5 } from "clipanion";
|
|
12017
12180
|
|
|
12018
12181
|
// conformance/index.ts
|
|
12019
12182
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
12020
|
-
import { cpSync, existsSync as
|
|
12183
|
+
import { cpSync, existsSync as existsSync17, mkdtempSync, readdirSync as readdirSync5, readFileSync as readFileSync15, rmSync, statSync as statSync3 } from "fs";
|
|
12021
12184
|
import { tmpdir } from "os";
|
|
12022
12185
|
import { isAbsolute as isAbsolute5, join as join11, relative as relative3, resolve as resolve20 } from "path";
|
|
12023
12186
|
|
|
@@ -12099,7 +12262,7 @@ function pickSafeEnv(source) {
|
|
|
12099
12262
|
return out;
|
|
12100
12263
|
}
|
|
12101
12264
|
function runConformanceCase(options) {
|
|
12102
|
-
const raw =
|
|
12265
|
+
const raw = readFileSync15(options.casePath, "utf8");
|
|
12103
12266
|
const c = JSON.parse(raw);
|
|
12104
12267
|
const fixturesRoot = options.fixturesRoot ?? join11(options.specRoot, "conformance", "fixtures");
|
|
12105
12268
|
const safeId = c.id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 32);
|
|
@@ -12214,7 +12377,7 @@ function evaluateAssertion(a, ctx) {
|
|
|
12214
12377
|
return { ok: false, type: a.type, reason: formatErrorMessage(err) };
|
|
12215
12378
|
}
|
|
12216
12379
|
const abs = resolve20(ctx.scope, a.path);
|
|
12217
|
-
return
|
|
12380
|
+
return existsSync17(abs) ? { ok: true, type: a.type } : {
|
|
12218
12381
|
ok: false,
|
|
12219
12382
|
type: a.type,
|
|
12220
12383
|
reason: tx(CONFORMANCE_RUNNER_TEXTS.fileNotFound, { path: a.path })
|
|
@@ -12229,15 +12392,15 @@ function evaluateAssertion(a, ctx) {
|
|
|
12229
12392
|
}
|
|
12230
12393
|
const fixturePath = join11(ctx.fixturesRoot, a.fixture);
|
|
12231
12394
|
const targetPath = resolve20(ctx.scope, a.path);
|
|
12232
|
-
if (!
|
|
12395
|
+
if (!existsSync17(targetPath)) {
|
|
12233
12396
|
return {
|
|
12234
12397
|
ok: false,
|
|
12235
12398
|
type: a.type,
|
|
12236
12399
|
reason: tx(CONFORMANCE_RUNNER_TEXTS.targetNotFound, { path: a.path })
|
|
12237
12400
|
};
|
|
12238
12401
|
}
|
|
12239
|
-
const needle =
|
|
12240
|
-
const haystack =
|
|
12402
|
+
const needle = readFileSync15(fixturePath);
|
|
12403
|
+
const haystack = readFileSync15(targetPath);
|
|
12241
12404
|
return haystack.includes(needle) ? { ok: true, type: a.type } : {
|
|
12242
12405
|
ok: false,
|
|
12243
12406
|
type: a.type,
|
|
@@ -12410,7 +12573,7 @@ var CONFORMANCE_TEXTS = {
|
|
|
12410
12573
|
};
|
|
12411
12574
|
|
|
12412
12575
|
// cli/util/conformance-scopes.ts
|
|
12413
|
-
import { existsSync as
|
|
12576
|
+
import { existsSync as existsSync18, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
12414
12577
|
import { dirname as dirname11, resolve as resolve21 } from "path";
|
|
12415
12578
|
import { createRequire as createRequire6 } from "module";
|
|
12416
12579
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -12430,7 +12593,7 @@ function resolveCliWorkspaceRoot() {
|
|
|
12430
12593
|
let cursor = here;
|
|
12431
12594
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
12432
12595
|
const candidate = resolve21(cursor, "plugins");
|
|
12433
|
-
if (
|
|
12596
|
+
if (existsSync18(candidate) && statSync4(candidate).isDirectory()) {
|
|
12434
12597
|
return cursor;
|
|
12435
12598
|
}
|
|
12436
12599
|
const parent = dirname11(cursor);
|
|
@@ -12450,32 +12613,32 @@ function collectProviderScopes(specRoot) {
|
|
|
12450
12613
|
return out;
|
|
12451
12614
|
}
|
|
12452
12615
|
const pluginsRoot = resolve21(workspaceRoot, "plugins");
|
|
12453
|
-
if (!
|
|
12454
|
-
for (const
|
|
12455
|
-
const
|
|
12456
|
-
if (!isDir(
|
|
12457
|
-
const providersRoot = resolve21(
|
|
12616
|
+
if (!existsSync18(pluginsRoot)) return out;
|
|
12617
|
+
for (const pluginEntry of readdirSync6(pluginsRoot)) {
|
|
12618
|
+
const pluginDir = resolve21(pluginsRoot, pluginEntry);
|
|
12619
|
+
if (!isDir(pluginDir)) continue;
|
|
12620
|
+
const providersRoot = resolve21(pluginDir, "providers");
|
|
12458
12621
|
if (!isDir(providersRoot)) continue;
|
|
12459
|
-
|
|
12622
|
+
collectPluginProviderScopes(providersRoot, specRoot, out);
|
|
12460
12623
|
}
|
|
12461
12624
|
return out;
|
|
12462
12625
|
}
|
|
12463
12626
|
function isDir(path) {
|
|
12464
12627
|
try {
|
|
12465
|
-
return
|
|
12628
|
+
return existsSync18(path) && statSync4(path).isDirectory();
|
|
12466
12629
|
} catch {
|
|
12467
12630
|
return false;
|
|
12468
12631
|
}
|
|
12469
12632
|
}
|
|
12470
|
-
function
|
|
12633
|
+
function collectPluginProviderScopes(providersRoot, specRoot, out) {
|
|
12471
12634
|
for (const entry of readdirSync6(providersRoot)) {
|
|
12472
12635
|
const providerDir = resolve21(providersRoot, entry);
|
|
12473
12636
|
if (!isDir(providerDir)) continue;
|
|
12474
12637
|
const conformanceDir = resolve21(providerDir, "conformance");
|
|
12475
|
-
if (!
|
|
12638
|
+
if (!existsSync18(conformanceDir)) continue;
|
|
12476
12639
|
const casesDir = resolve21(conformanceDir, "cases");
|
|
12477
12640
|
const fixturesDir = resolve21(conformanceDir, "fixtures");
|
|
12478
|
-
if (!
|
|
12641
|
+
if (!existsSync18(casesDir) || !existsSync18(fixturesDir)) continue;
|
|
12479
12642
|
out.push({
|
|
12480
12643
|
id: `provider:${entry}`,
|
|
12481
12644
|
kind: "provider",
|
|
@@ -12513,7 +12676,7 @@ function selectConformanceScopes(scope) {
|
|
|
12513
12676
|
return [match];
|
|
12514
12677
|
}
|
|
12515
12678
|
function listCaseFiles(scope) {
|
|
12516
|
-
if (!
|
|
12679
|
+
if (!existsSync18(scope.casesDir)) return [];
|
|
12517
12680
|
return readdirSync6(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) => resolve21(scope.casesDir, entry));
|
|
12518
12681
|
}
|
|
12519
12682
|
|
|
@@ -12532,7 +12695,7 @@ function resolveBinary() {
|
|
|
12532
12695
|
let cursor = here;
|
|
12533
12696
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
12534
12697
|
const candidate = resolve22(cursor, "bin", "sm.js");
|
|
12535
|
-
if (
|
|
12698
|
+
if (existsSync19(candidate)) return candidate;
|
|
12536
12699
|
const parent = dirname12(cursor);
|
|
12537
12700
|
if (parent === cursor) break;
|
|
12538
12701
|
cursor = parent;
|
|
@@ -12598,7 +12761,7 @@ var ConformanceRunCommand = class extends SmCommand {
|
|
|
12598
12761
|
return ExitCode.Error;
|
|
12599
12762
|
}
|
|
12600
12763
|
const binary = resolveBinary();
|
|
12601
|
-
if (!
|
|
12764
|
+
if (!existsSync19(binary)) {
|
|
12602
12765
|
if (this.json) {
|
|
12603
12766
|
this.#emitJsonError(
|
|
12604
12767
|
"internal",
|
|
@@ -12772,7 +12935,7 @@ function projectAssertionFailures(assertions) {
|
|
|
12772
12935
|
}
|
|
12773
12936
|
function readCaseId(casePath) {
|
|
12774
12937
|
try {
|
|
12775
|
-
const raw =
|
|
12938
|
+
const raw = readFileSync16(casePath, "utf8");
|
|
12776
12939
|
const parsed = JSON.parse(raw);
|
|
12777
12940
|
if (typeof parsed.id === "string") return parsed.id;
|
|
12778
12941
|
} catch {
|
|
@@ -13027,18 +13190,18 @@ var DbRestoreCommand = class extends SmCommand {
|
|
|
13027
13190
|
};
|
|
13028
13191
|
|
|
13029
13192
|
// cli/commands/db/reset.ts
|
|
13030
|
-
import { DatabaseSync as
|
|
13193
|
+
import { DatabaseSync as DatabaseSync6 } from "node:sqlite";
|
|
13031
13194
|
import { Command as Command8, Option as Option8 } from "clipanion";
|
|
13032
13195
|
|
|
13033
13196
|
// core/sqlite/db-files.ts
|
|
13034
|
-
import { existsSync as
|
|
13197
|
+
import { existsSync as existsSync20 } from "fs";
|
|
13035
13198
|
import { rm as rm2 } from "fs/promises";
|
|
13036
13199
|
var DB_FILE_SUFFIXES = ["", "-wal", "-shm"];
|
|
13037
13200
|
async function removeDbFiles(dbPath) {
|
|
13038
13201
|
if (dbPath === ":memory:") return;
|
|
13039
13202
|
for (const suffix of DB_FILE_SUFFIXES) {
|
|
13040
13203
|
const p = `${dbPath}${suffix}`;
|
|
13041
|
-
if (
|
|
13204
|
+
if (existsSync20(p)) await rm2(p);
|
|
13042
13205
|
}
|
|
13043
13206
|
}
|
|
13044
13207
|
|
|
@@ -13124,7 +13287,7 @@ var DbResetCommand = class extends SmCommand {
|
|
|
13124
13287
|
return ExitCode.Error;
|
|
13125
13288
|
}
|
|
13126
13289
|
}
|
|
13127
|
-
const db = new
|
|
13290
|
+
const db = new DatabaseSync6(path);
|
|
13128
13291
|
try {
|
|
13129
13292
|
const rows = db.prepare(
|
|
13130
13293
|
"SELECT name FROM sqlite_master WHERE type='table' AND (name LIKE 'scan\\_%' ESCAPE '\\'" + (this.state ? " OR name LIKE 'state\\_%' ESCAPE '\\'" : "") + ")"
|
|
@@ -13267,7 +13430,7 @@ var DbBrowserCommand = class extends SmCommand {
|
|
|
13267
13430
|
};
|
|
13268
13431
|
|
|
13269
13432
|
// cli/commands/db/dump.ts
|
|
13270
|
-
import { DatabaseSync as
|
|
13433
|
+
import { DatabaseSync as DatabaseSync7 } from "node:sqlite";
|
|
13271
13434
|
import { Command as Command11, Option as Option10 } from "clipanion";
|
|
13272
13435
|
var DbDumpCommand = class extends SmCommand {
|
|
13273
13436
|
static paths = [["db", "dump"]];
|
|
@@ -13309,7 +13472,7 @@ var DbDumpCommand = class extends SmCommand {
|
|
|
13309
13472
|
}
|
|
13310
13473
|
};
|
|
13311
13474
|
function dumpDatabaseToStream(dbPath, out, tables) {
|
|
13312
|
-
const db = new
|
|
13475
|
+
const db = new DatabaseSync7(dbPath, { readOnly: true });
|
|
13313
13476
|
try {
|
|
13314
13477
|
out.write("PRAGMA foreign_keys=OFF;\n");
|
|
13315
13478
|
out.write("BEGIN TRANSACTION;\n");
|
|
@@ -14157,7 +14320,7 @@ var GraphCommand = class extends SmCommand {
|
|
|
14157
14320
|
};
|
|
14158
14321
|
|
|
14159
14322
|
// cli/commands/help.ts
|
|
14160
|
-
import { readFileSync as
|
|
14323
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
14161
14324
|
import { createRequire as createRequire7 } from "module";
|
|
14162
14325
|
import { resolve as resolve26 } from "path";
|
|
14163
14326
|
import { Command as Command15, Option as Option14 } from "clipanion";
|
|
@@ -14214,6 +14377,22 @@ var HELP_TEXTS = {
|
|
|
14214
14377
|
/** Trailing fragment for `humanFlagRow`'s `{{required}}` slot. */
|
|
14215
14378
|
humanFlagRowRequiredFragment: " (required)",
|
|
14216
14379
|
humanFooter: "Run `sm help {{name}} --format md` for the full reference.",
|
|
14380
|
+
// --- human group renderer (sm <namespace> --help, sm help <namespace>) ---
|
|
14381
|
+
/**
|
|
14382
|
+
* USAGE row for a command namespace (a prefix that owns subcommands but
|
|
14383
|
+
* is not itself a runnable verb, e.g. `plugins`, `db`). Mirrors
|
|
14384
|
+
* `humanUsageRow` but advertises the `<command>` slot instead of
|
|
14385
|
+
* positionals.
|
|
14386
|
+
*/
|
|
14387
|
+
humanGroupUsageRow: " sm {{name}} <command> [options]",
|
|
14388
|
+
/** Section heading listing the subcommands of a namespace. */
|
|
14389
|
+
humanCommandsHeading: "COMMANDS",
|
|
14390
|
+
/** Aligned subcommand row; `{{name}}` is the subcommand relative to the namespace. */
|
|
14391
|
+
humanCommandRow: " {{name}}{{padding}} {{description}}",
|
|
14392
|
+
/** Footer for the namespace overview, points at per-subcommand help. */
|
|
14393
|
+
humanGroupFooter: "Run `sm {{name}} <command> --help` for flags and arguments.",
|
|
14394
|
+
/** Fallback header description when a namespace has no curated entry in `HELP_GROUPS`. */
|
|
14395
|
+
groupFallbackDescription: "{{category}} commands",
|
|
14217
14396
|
// --- human compact overview (sm / sm --help / sm help, no verb) ---------
|
|
14218
14397
|
/**
|
|
14219
14398
|
* Compact-overview header. Replaces the Clipanion default ANSI banner.
|
|
@@ -14244,6 +14423,40 @@ var HELP_TEXTS = {
|
|
|
14244
14423
|
compactExampleRow: " {{command}}{{padding}} {{description}}",
|
|
14245
14424
|
compactFooter: "Run `sm <command> --help` for flags and arguments."
|
|
14246
14425
|
};
|
|
14426
|
+
var HELP_GROUPS = {
|
|
14427
|
+
plugins: {
|
|
14428
|
+
description: "Discover, inspect, and toggle plugins",
|
|
14429
|
+
details: "A plugin is a directory of extensions (extractors, analyzers, actions,\nhooks, formatters, providers) discovered under the project plugins dir.\n\nUse `list` and `show` to inspect what loaded, `doctor` to diagnose load\nfailures, `enable` / `disable` to toggle extensions (persisted in the DB),\nand `create` / `upgrade` to scaffold and migrate your own."
|
|
14430
|
+
},
|
|
14431
|
+
config: {
|
|
14432
|
+
description: "Read and write project configuration",
|
|
14433
|
+
details: "Configuration is a layered merge: library defaults, the committed\n`settings.json`, the gitignored `settings.local.json`, env vars, then\nCLI flags, with later layers winning.\n\nUse `list` / `get` to read the effective values, `show --source` to see\nwhich layer set a key, and `set` / `reset` to write or revert one.\nPrivacy-sensitive keys (paths outside the project) require `--yes`."
|
|
14434
|
+
},
|
|
14435
|
+
db: {
|
|
14436
|
+
description: "Inspect and maintain the project database",
|
|
14437
|
+
details: "The project database is a single SQLite file at\n`.skill-map/skill-map.db`, holding the scan graph and plugin state.\n\nUse `backup` / `restore` around risky operations, `migrate` to apply\npending kernel and plugin migrations, `reset` to drop tables (or the\nwhole file), and `dump` / `shell` / `browser` to inspect the data."
|
|
14438
|
+
},
|
|
14439
|
+
job: {
|
|
14440
|
+
description: "Manage the background job queue",
|
|
14441
|
+
details: "Probabilistic and long-running work runs as jobs: queued, persisted in\nthe database, and resumable across restarts.\n\nUse `submit` to enqueue (or `--all` to fan out across nodes), `run` to\nexecute the claim-spawn-record loop, `status` / `list` / `show` to\ninspect, `preview` to render a job without executing it, and\n`cancel` / `prune` to clean up."
|
|
14442
|
+
},
|
|
14443
|
+
actions: {
|
|
14444
|
+
description: "Inspect the registered Action catalog",
|
|
14445
|
+
details: "An Action operates on one or more nodes and is either deterministic\n(in-process code) or probabilistic (a rendered prompt a runner executes).\n\nUse `list` for the catalog of registered action types and `show` for a\nsingle action's full manifest, including its preconditions and expected\nduration."
|
|
14446
|
+
},
|
|
14447
|
+
sidecar: {
|
|
14448
|
+
description: "Manage `.sm` annotation sidecars",
|
|
14449
|
+
details: "Skill-map's annotation layer lives in co-located `.sm` YAML sidecars\nnext to each node, leaving the vendor file untouched.\n\nUse `annotate` to scaffold an empty sidecar ready for editing, `refresh`\nto realign its drift hashes with the live node, and `prune` to delete\nsidecars whose `.md` no longer exists."
|
|
14450
|
+
},
|
|
14451
|
+
hooks: {
|
|
14452
|
+
description: "Install git hooks for sidecar drift",
|
|
14453
|
+
details: "Git hooks keep your sidecars in sync with the repo as you commit.\n\n`install` writes a pre-commit hook that auto-bumps staged sidecar drift\nbefore each commit."
|
|
14454
|
+
},
|
|
14455
|
+
conformance: {
|
|
14456
|
+
description: "Run the spec conformance suite",
|
|
14457
|
+
details: "The conformance suite checks an implementation against the spec.\n\n`run` executes the spec-owned cases plus every built-in Provider and\nreports the results."
|
|
14458
|
+
}
|
|
14459
|
+
};
|
|
14247
14460
|
|
|
14248
14461
|
// cli/commands/help.ts
|
|
14249
14462
|
var HelpCommand = class extends Command15 {
|
|
@@ -14282,18 +14495,23 @@ var HelpCommand = class extends Command15 {
|
|
|
14282
14495
|
const verb = this.verbParts.join(" ").trim();
|
|
14283
14496
|
if (verb) {
|
|
14284
14497
|
const target = verbs.find((v) => v.name === verb);
|
|
14285
|
-
if (
|
|
14286
|
-
this.context.
|
|
14287
|
-
|
|
14288
|
-
glyph: errGlyph,
|
|
14289
|
-
verb,
|
|
14290
|
-
hint: ansi.dim(HELP_TEXTS.unknownVerbHint)
|
|
14291
|
-
})
|
|
14292
|
-
);
|
|
14293
|
-
return ExitCode.NotFound;
|
|
14498
|
+
if (target) {
|
|
14499
|
+
this.context.stdout.write(renderSingle(target, format));
|
|
14500
|
+
return ExitCode.Ok;
|
|
14294
14501
|
}
|
|
14295
|
-
|
|
14296
|
-
|
|
14502
|
+
const subcommands = verbs.filter((v) => v.name.startsWith(verb + " "));
|
|
14503
|
+
if (subcommands.length > 0) {
|
|
14504
|
+
this.context.stdout.write(renderGroup(verb, subcommands, format));
|
|
14505
|
+
return ExitCode.Ok;
|
|
14506
|
+
}
|
|
14507
|
+
this.context.stderr.write(
|
|
14508
|
+
tx(HELP_TEXTS.unknownVerb, {
|
|
14509
|
+
glyph: errGlyph,
|
|
14510
|
+
verb,
|
|
14511
|
+
hint: ansi.dim(HELP_TEXTS.unknownVerbHint)
|
|
14512
|
+
})
|
|
14513
|
+
);
|
|
14514
|
+
return ExitCode.NotFound;
|
|
14297
14515
|
}
|
|
14298
14516
|
if (format === "human") {
|
|
14299
14517
|
this.context.stdout.write(renderCompactOverview(verbs));
|
|
@@ -14403,7 +14621,7 @@ function resolveSpecVersion() {
|
|
|
14403
14621
|
const req = createRequire7(import.meta.url);
|
|
14404
14622
|
const indexPath = req.resolve("@skill-map/spec/index.json");
|
|
14405
14623
|
const pkgPath = resolve26(indexPath, "..", "package.json");
|
|
14406
|
-
const pkg = JSON.parse(
|
|
14624
|
+
const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
|
|
14407
14625
|
return pkg.version;
|
|
14408
14626
|
} catch {
|
|
14409
14627
|
return "unknown";
|
|
@@ -14480,6 +14698,51 @@ function renderSingle(verb, format) {
|
|
|
14480
14698
|
}
|
|
14481
14699
|
return renderSingleHuman(verb);
|
|
14482
14700
|
}
|
|
14701
|
+
function renderGroup(group, subcommands, format) {
|
|
14702
|
+
if (format === "json" || format === "md") {
|
|
14703
|
+
const doc = {
|
|
14704
|
+
cliVersion: VERSION,
|
|
14705
|
+
specVersion: resolveSpecVersion(),
|
|
14706
|
+
globalFlags: [],
|
|
14707
|
+
verbs: subcommands
|
|
14708
|
+
};
|
|
14709
|
+
return format === "json" ? JSON.stringify(doc, null, 2) + "\n" : renderMarkdown2(doc);
|
|
14710
|
+
}
|
|
14711
|
+
return renderGroupHuman(group, subcommands);
|
|
14712
|
+
}
|
|
14713
|
+
function renderGroupHuman(group, subcommands) {
|
|
14714
|
+
const meta = HELP_GROUPS[group];
|
|
14715
|
+
const description = meta?.description ?? tx(HELP_TEXTS.groupFallbackDescription, { category: subcommands[0]?.category ?? "Other" });
|
|
14716
|
+
const out = [];
|
|
14717
|
+
out.push(tx(HELP_TEXTS.humanVerbHeader, { name: group, description }));
|
|
14718
|
+
out.push("");
|
|
14719
|
+
out.push(HELP_TEXTS.humanUsageHeading);
|
|
14720
|
+
out.push(tx(HELP_TEXTS.humanGroupUsageRow, { name: group }));
|
|
14721
|
+
if (meta?.details) out.push(...renderHumanDescription(meta.details));
|
|
14722
|
+
out.push(...renderGroupCommands(group, subcommands));
|
|
14723
|
+
out.push("");
|
|
14724
|
+
out.push(tx(HELP_TEXTS.humanGroupFooter, { name: group }));
|
|
14725
|
+
return out.join("\n") + "\n";
|
|
14726
|
+
}
|
|
14727
|
+
function renderGroupCommands(group, subcommands) {
|
|
14728
|
+
const out = ["", HELP_TEXTS.humanCommandsHeading];
|
|
14729
|
+
const sorted = [...subcommands].sort((a, b) => a.name.localeCompare(b.name));
|
|
14730
|
+
const labels = sorted.map((v) => v.name.slice(group.length + 1));
|
|
14731
|
+
const width = Math.max(...labels.map((l) => l.length));
|
|
14732
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
14733
|
+
const verb = sorted[i];
|
|
14734
|
+
const label = labels[i];
|
|
14735
|
+
const { isStub, clean } = classifyDescription(verb.description);
|
|
14736
|
+
const description = (isStub ? HELP_TEXTS.compactStubMarker : "") + firstSentence(clean);
|
|
14737
|
+
const row = tx(HELP_TEXTS.humanCommandRow, {
|
|
14738
|
+
name: label,
|
|
14739
|
+
padding: padRight("", width - label.length),
|
|
14740
|
+
description
|
|
14741
|
+
});
|
|
14742
|
+
out.push(truncate2(row, COMPACT_ROW_MAX));
|
|
14743
|
+
}
|
|
14744
|
+
return out;
|
|
14745
|
+
}
|
|
14483
14746
|
function renderSingleHuman(verb) {
|
|
14484
14747
|
const out = [];
|
|
14485
14748
|
out.push(buildHumanHeader(verb));
|
|
@@ -14611,7 +14874,7 @@ function routeHelpArgs(args2, cli2) {
|
|
|
14611
14874
|
if (!shouldRouteHelp(args2)) return args2;
|
|
14612
14875
|
const leading = leadingPositionals(args2);
|
|
14613
14876
|
if (leading.length === 0) return args2;
|
|
14614
|
-
const verbPath =
|
|
14877
|
+
const verbPath = longestVerbOrGroupPrefix(leading, registeredVerbPaths(cli2));
|
|
14615
14878
|
if (verbPath.length === 0) return args2;
|
|
14616
14879
|
return ["help", ...verbPath];
|
|
14617
14880
|
}
|
|
@@ -14630,14 +14893,15 @@ function leadingPositionals(args2) {
|
|
|
14630
14893
|
}
|
|
14631
14894
|
return out;
|
|
14632
14895
|
}
|
|
14633
|
-
function
|
|
14634
|
-
let
|
|
14635
|
-
|
|
14636
|
-
|
|
14637
|
-
|
|
14638
|
-
|
|
14896
|
+
function longestVerbOrGroupPrefix(positionals, verbPaths) {
|
|
14897
|
+
for (let len = positionals.length; len >= 1; len--) {
|
|
14898
|
+
const prefix = positionals.slice(0, len);
|
|
14899
|
+
const matches = verbPaths.some(
|
|
14900
|
+
(path) => path.length >= len && prefix.every((tok, i) => path[i] === tok)
|
|
14901
|
+
);
|
|
14902
|
+
if (matches) return prefix;
|
|
14639
14903
|
}
|
|
14640
|
-
return
|
|
14904
|
+
return [];
|
|
14641
14905
|
}
|
|
14642
14906
|
function registeredVerbPaths(cli2) {
|
|
14643
14907
|
const rawDefs = cli2.definitions();
|
|
@@ -14842,10 +15106,9 @@ import { join as join17 } from "path";
|
|
|
14842
15106
|
import { Command as Command17, Option as Option16 } from "clipanion";
|
|
14843
15107
|
|
|
14844
15108
|
// kernel/orchestrator/index.ts
|
|
14845
|
-
import { existsSync as
|
|
15109
|
+
import { existsSync as existsSync23, statSync as statSync6 } from "fs";
|
|
14846
15110
|
import { isAbsolute as isAbsolute7, resolve as resolve28 } from "path";
|
|
14847
15111
|
import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
|
|
14848
|
-
import cl100k_base from "js-tiktoken/ranks/cl100k_base";
|
|
14849
15112
|
|
|
14850
15113
|
// kernel/i18n/orchestrator.texts.ts
|
|
14851
15114
|
var ORCHESTRATOR_TEXTS = {
|
|
@@ -15889,7 +16152,7 @@ function computeDriftStatus(args2) {
|
|
|
15889
16152
|
}
|
|
15890
16153
|
|
|
15891
16154
|
// kernel/sidecar/discover-orphans.ts
|
|
15892
|
-
import { existsSync as
|
|
16155
|
+
import { existsSync as existsSync21, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
|
|
15893
16156
|
import { join as join13, relative as relative4, sep as sep4 } from "path";
|
|
15894
16157
|
function discoverOrphanSidecars(roots, shouldSkip) {
|
|
15895
16158
|
const out = [];
|
|
@@ -15917,7 +16180,7 @@ function walk2(root, current, shouldSkip, out) {
|
|
|
15917
16180
|
if (!entry.isFile()) continue;
|
|
15918
16181
|
if (!entry.name.endsWith(".sm")) continue;
|
|
15919
16182
|
const expectedMd = `${full.slice(0, -".sm".length)}.md`;
|
|
15920
|
-
if (
|
|
16183
|
+
if (existsSync21(expectedMd) && safeIsFile(expectedMd)) continue;
|
|
15921
16184
|
out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
|
|
15922
16185
|
}
|
|
15923
16186
|
}
|
|
@@ -15930,8 +16193,8 @@ function safeIsFile(path) {
|
|
|
15930
16193
|
}
|
|
15931
16194
|
|
|
15932
16195
|
// kernel/orchestrator/node-build.ts
|
|
15933
|
-
import { createHash } from "crypto";
|
|
15934
|
-
import { existsSync as
|
|
16196
|
+
import { createHash as createHash2 } from "crypto";
|
|
16197
|
+
import { existsSync as existsSync22 } from "fs";
|
|
15935
16198
|
import { isAbsolute as isAbsolute6, resolve as resolvePath } from "path";
|
|
15936
16199
|
import "js-tiktoken/lite";
|
|
15937
16200
|
import yaml4 from "js-yaml";
|
|
@@ -16018,7 +16281,7 @@ function countTokens(encoder, frontmatterRaw, body) {
|
|
|
16018
16281
|
return { frontmatter, body: bodyTokens, total: frontmatter + bodyTokens };
|
|
16019
16282
|
}
|
|
16020
16283
|
function sha256(input) {
|
|
16021
|
-
return
|
|
16284
|
+
return createHash2("sha256").update(input, "utf8").digest("hex");
|
|
16022
16285
|
}
|
|
16023
16286
|
function canonicalFrontmatter(parsed, raw) {
|
|
16024
16287
|
const hasParsedKeys = Object.keys(parsed).length > 0;
|
|
@@ -16095,11 +16358,11 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
|
|
|
16095
16358
|
}
|
|
16096
16359
|
function resolveAbsoluteMdPath(relativePath2, roots) {
|
|
16097
16360
|
if (isAbsolute6(relativePath2)) {
|
|
16098
|
-
return
|
|
16361
|
+
return existsSync22(relativePath2) ? relativePath2 : null;
|
|
16099
16362
|
}
|
|
16100
16363
|
for (const root of roots) {
|
|
16101
16364
|
const candidate = resolvePath(root, relativePath2);
|
|
16102
|
-
if (
|
|
16365
|
+
if (existsSync22(candidate)) return candidate;
|
|
16103
16366
|
}
|
|
16104
16367
|
return null;
|
|
16105
16368
|
}
|
|
@@ -16157,7 +16420,18 @@ async function walkAndExtract(opts) {
|
|
|
16157
16420
|
const accum = createWalkAccumulators();
|
|
16158
16421
|
const wctx = buildWalkContext(opts);
|
|
16159
16422
|
const claimedPaths = /* @__PURE__ */ new Set();
|
|
16160
|
-
const
|
|
16423
|
+
const oversizedFiles = [];
|
|
16424
|
+
const oversizedSeen = /* @__PURE__ */ new Set();
|
|
16425
|
+
const onOversizedFile = (info) => {
|
|
16426
|
+
if (oversizedSeen.has(info.path)) return;
|
|
16427
|
+
oversizedSeen.add(info.path);
|
|
16428
|
+
oversizedFiles.push(info);
|
|
16429
|
+
};
|
|
16430
|
+
const walkOptions = {
|
|
16431
|
+
...opts.ignoreFilter ? { ignoreFilter: opts.ignoreFilter } : {},
|
|
16432
|
+
onOversizedFile,
|
|
16433
|
+
...opts.maxFileSizeBytes !== void 0 ? { maxFileSizeBytes: opts.maxFileSizeBytes } : {}
|
|
16434
|
+
};
|
|
16161
16435
|
let filesWalked = 0;
|
|
16162
16436
|
let index = 0;
|
|
16163
16437
|
const effectiveMaxNodes = opts.overrideMaxNodes ?? opts.recommendedNodeLimit;
|
|
@@ -16190,6 +16464,7 @@ async function walkAndExtract(opts) {
|
|
|
16190
16464
|
cachedPaths: accum.cachedPaths,
|
|
16191
16465
|
frontmatterIssues: accum.frontmatterIssues,
|
|
16192
16466
|
filesWalked,
|
|
16467
|
+
oversizedFiles,
|
|
16193
16468
|
recommendedNodeLimit: opts.recommendedNodeLimit,
|
|
16194
16469
|
overrideMaxNodes: opts.overrideMaxNodes,
|
|
16195
16470
|
capReached,
|
|
@@ -16240,7 +16515,11 @@ async function processRawNode(raw, provider, wctx, accum, claimedPaths, nextInde
|
|
|
16240
16515
|
}
|
|
16241
16516
|
claimedPaths.add(raw.path);
|
|
16242
16517
|
const priorNode = wctx.priorNodesByPath.get(raw.path);
|
|
16243
|
-
const nodeHashCacheEligible = wctx.opts.enableCache &&
|
|
16518
|
+
const nodeHashCacheEligible = wctx.opts.enableCache && // Tokenizer-change invalidation: when the resolved encoder differs
|
|
16519
|
+
// from the one that produced the prior snapshot's counts, no node is
|
|
16520
|
+
// cache-eligible, every node rebuilds so `buildNode` re-tokenizes
|
|
16521
|
+
// with the current encoder. See `tokenizerChanged` on the options.
|
|
16522
|
+
!wctx.opts.tokenizerChanged && wctx.opts.prior !== null && priorNode !== void 0 && priorNode.bodyHash === bodyHash && priorNode.frontmatterHash === frontmatterHash;
|
|
16244
16523
|
const sidecarResolution = resolveSidecarOverlay(
|
|
16245
16524
|
raw.path,
|
|
16246
16525
|
raw.path,
|
|
@@ -16442,6 +16721,16 @@ function resolveSpecVersionSafe() {
|
|
|
16442
16721
|
return "unknown";
|
|
16443
16722
|
}
|
|
16444
16723
|
}
|
|
16724
|
+
var DEFAULT_TOKENIZER = "cl100k_base";
|
|
16725
|
+
function resolveTokenizerName(name) {
|
|
16726
|
+
return name === "o200k_base" ? "o200k_base" : DEFAULT_TOKENIZER;
|
|
16727
|
+
}
|
|
16728
|
+
async function loadTokenizerRanks(name) {
|
|
16729
|
+
if (name === "o200k_base") {
|
|
16730
|
+
return (await import("js-tiktoken/ranks/o200k_base")).default;
|
|
16731
|
+
}
|
|
16732
|
+
return (await import("js-tiktoken/ranks/cl100k_base")).default;
|
|
16733
|
+
}
|
|
16445
16734
|
async function runScanWithRenames(_kernel, options) {
|
|
16446
16735
|
return runScanInternal(_kernel, options);
|
|
16447
16736
|
}
|
|
@@ -16451,7 +16740,7 @@ async function runScan(_kernel, options) {
|
|
|
16451
16740
|
}
|
|
16452
16741
|
async function runScanInternal(_kernel, options) {
|
|
16453
16742
|
validateRoots(options.roots);
|
|
16454
|
-
const setup = buildScanSetup(options);
|
|
16743
|
+
const setup = await buildScanSetup(options);
|
|
16455
16744
|
const { emitter, exts, hookDispatcher, encoder, prior, start } = setup;
|
|
16456
16745
|
const scanStartedEvent = makeEvent("scan.started", { roots: options.roots });
|
|
16457
16746
|
emitter.emit(scanStartedEvent);
|
|
@@ -16461,6 +16750,7 @@ async function runScanInternal(_kernel, options) {
|
|
|
16461
16750
|
options.roots,
|
|
16462
16751
|
exts.providers
|
|
16463
16752
|
);
|
|
16753
|
+
const tokenizerChanged = encoder !== null && prior !== null && prior.tokenizer !== setup.tokenizer;
|
|
16464
16754
|
const walked = await walkAndExtract({
|
|
16465
16755
|
providers: exts.providers,
|
|
16466
16756
|
extractors: exts.extractors,
|
|
@@ -16470,6 +16760,7 @@ async function runScanInternal(_kernel, options) {
|
|
|
16470
16760
|
encoder,
|
|
16471
16761
|
strict: setup.strict,
|
|
16472
16762
|
enableCache: setup.enableCache,
|
|
16763
|
+
tokenizerChanged,
|
|
16473
16764
|
prior,
|
|
16474
16765
|
priorIndex: setup.priorIndex,
|
|
16475
16766
|
priorExtractorRuns: setup.priorExtractorRuns,
|
|
@@ -16477,7 +16768,8 @@ async function runScanInternal(_kernel, options) {
|
|
|
16477
16768
|
pluginStores: options.pluginStores,
|
|
16478
16769
|
activeProvider: activeProviderId,
|
|
16479
16770
|
recommendedNodeLimit: options.recommendedNodeLimit ?? 256,
|
|
16480
|
-
overrideMaxNodes: options.overrideMaxNodes ?? null
|
|
16771
|
+
overrideMaxNodes: options.overrideMaxNodes ?? null,
|
|
16772
|
+
...options.maxFileSizeBytes !== void 0 ? { maxFileSizeBytes: options.maxFileSizeBytes } : {}
|
|
16481
16773
|
});
|
|
16482
16774
|
const activeProvider = activeProviderId ? exts.providers.find((p) => p.id === activeProviderId) ?? null : null;
|
|
16483
16775
|
const resolved = resolveSignals({
|
|
@@ -16584,13 +16876,14 @@ function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind
|
|
|
16584
16876
|
function hasEntries(set) {
|
|
16585
16877
|
return set !== void 0 && set.size > 0;
|
|
16586
16878
|
}
|
|
16587
|
-
function buildScanSetup(options) {
|
|
16879
|
+
async function buildScanSetup(options) {
|
|
16588
16880
|
const start = Date.now();
|
|
16589
16881
|
const emitter = options.emitter ?? new InMemoryProgressEmitter();
|
|
16590
16882
|
const exts = options.extensions ?? { providers: [], extractors: [], analyzers: [] };
|
|
16591
16883
|
const hookDispatcher = makeHookDispatcher(exts.hooks ?? [], emitter);
|
|
16592
16884
|
const tokenize = options.tokenize !== false;
|
|
16593
|
-
const
|
|
16885
|
+
const tokenizer = resolveTokenizerName(options.tokenizer);
|
|
16886
|
+
const encoder = tokenize ? new Tiktoken2(await loadTokenizerRanks(tokenizer)) : null;
|
|
16594
16887
|
const prior = options.priorSnapshot ?? null;
|
|
16595
16888
|
const priorIndex = indexPriorSnapshot(prior);
|
|
16596
16889
|
const providerFrontmatter = buildProviderFrontmatterValidator(exts.providers);
|
|
@@ -16601,6 +16894,7 @@ function buildScanSetup(options) {
|
|
|
16601
16894
|
exts,
|
|
16602
16895
|
hookDispatcher,
|
|
16603
16896
|
encoder,
|
|
16897
|
+
tokenizer,
|
|
16604
16898
|
prior,
|
|
16605
16899
|
priorIndex,
|
|
16606
16900
|
priorExtractorRuns: options.priorExtractorRuns,
|
|
@@ -16636,6 +16930,7 @@ function buildScanStats(walked, issues, start) {
|
|
|
16636
16930
|
// Providers compete.
|
|
16637
16931
|
filesWalked: walked.filesWalked,
|
|
16638
16932
|
filesSkipped: 0,
|
|
16933
|
+
filesOversized: walked.oversizedFiles.length,
|
|
16639
16934
|
nodesCount: walked.nodes.length,
|
|
16640
16935
|
linksCount: walked.internalLinks.length,
|
|
16641
16936
|
issuesCount: issues.length,
|
|
@@ -16650,8 +16945,10 @@ function buildScanReturn(walked, issues, renameOps, stats, options, setup) {
|
|
|
16650
16945
|
roots: options.roots,
|
|
16651
16946
|
providers: setup.exts.providers.map((a) => a.id),
|
|
16652
16947
|
scannedBy: SCANNED_BY,
|
|
16948
|
+
tokenizer: setup.tokenizer,
|
|
16653
16949
|
recommendedNodeLimit: walked.recommendedNodeLimit,
|
|
16654
16950
|
overrideMaxNodes: walked.overrideMaxNodes,
|
|
16951
|
+
oversizedFiles: walked.oversizedFiles,
|
|
16655
16952
|
nodes: walked.nodes,
|
|
16656
16953
|
links: walked.internalLinks,
|
|
16657
16954
|
issues,
|
|
@@ -16669,7 +16966,7 @@ function validateRoots(roots) {
|
|
|
16669
16966
|
throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
|
|
16670
16967
|
}
|
|
16671
16968
|
for (const root of roots) {
|
|
16672
|
-
if (!
|
|
16969
|
+
if (!existsSync23(root) || !statSync6(root).isDirectory()) {
|
|
16673
16970
|
throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
|
|
16674
16971
|
}
|
|
16675
16972
|
}
|
|
@@ -16678,7 +16975,7 @@ function resolveActiveProviderOption(optionValue, roots, providers) {
|
|
|
16678
16975
|
if (optionValue !== void 0) return optionValue;
|
|
16679
16976
|
for (const root of roots) {
|
|
16680
16977
|
const absRoot = isAbsolute7(root) ? root : resolve28(root);
|
|
16681
|
-
if (!
|
|
16978
|
+
if (!existsSync23(absRoot)) continue;
|
|
16682
16979
|
const detected = resolveActiveProvider(absRoot, providers).resolved;
|
|
16683
16980
|
if (detected !== null) return detected;
|
|
16684
16981
|
}
|
|
@@ -17047,13 +17344,13 @@ var SCAN_RUNNER_TEXTS = {
|
|
|
17047
17344
|
activeProviderAmbiguousUnderYes: "{{glyph}} Multiple provider markers detected ({{candidates}}) and --yes is set.\n {{hint}}\n",
|
|
17048
17345
|
activeProviderAmbiguousUnderYesHint: "Set the lens explicitly with `sm config set activeProvider <id>` and re-run, or omit --yes for interactive selection.",
|
|
17049
17346
|
/**
|
|
17050
|
-
* Active lens points at a
|
|
17347
|
+
* Active lens points at a plugin the operator has disabled (via
|
|
17051
17348
|
* `sm plugins disable <id>` or the Settings UI). Classification keeps
|
|
17052
17349
|
* running because it's provider-driven, but the lens-gated extractors
|
|
17053
|
-
* for the disabled
|
|
17350
|
+
* for the disabled plugin silently no-op. Without this warning the
|
|
17054
17351
|
* graph quietly differs from what the lens implies.
|
|
17055
17352
|
*/
|
|
17056
|
-
|
|
17353
|
+
activeProviderPluginDisabledWarning: 'activeProvider = "{{id}}" but the "{{id}}" plugin is currently disabled; provider-specific extractors will not run. Re-enable the plugin with `sm plugins enable {{id}}` or switch the lens with `sm config set activeProvider <id>` to silence this warning.',
|
|
17057
17354
|
/**
|
|
17058
17355
|
* Active-provider drift: the snapshot of provider markers persisted
|
|
17059
17356
|
* when `activeProvider` was set (`activeProviderMarkers`) no longer
|
|
@@ -17222,7 +17519,6 @@ function persistActiveProvider(cwd, id, markers, printer) {
|
|
|
17222
17519
|
target: "project",
|
|
17223
17520
|
cwd
|
|
17224
17521
|
});
|
|
17225
|
-
printer.info(tx(SCAN_RUNNER_TEXTS.activeProviderAutodetected, { id }));
|
|
17226
17522
|
} catch (err) {
|
|
17227
17523
|
const message = err instanceof Error ? err.message : String(err);
|
|
17228
17524
|
printer.warn(
|
|
@@ -17279,11 +17575,11 @@ function diffMarkers(snapshot, current) {
|
|
|
17279
17575
|
}
|
|
17280
17576
|
return { added, removed };
|
|
17281
17577
|
}
|
|
17282
|
-
function
|
|
17578
|
+
function warnIfLensPluginDisabled(args2) {
|
|
17283
17579
|
if (args2.activeProvider === null) return;
|
|
17284
17580
|
if (args2.resolveEnabled(args2.activeProvider)) return;
|
|
17285
17581
|
args2.printer.warn(
|
|
17286
|
-
tx(SCAN_RUNNER_TEXTS.
|
|
17582
|
+
tx(SCAN_RUNNER_TEXTS.activeProviderPluginDisabledWarning, {
|
|
17287
17583
|
id: args2.activeProvider
|
|
17288
17584
|
})
|
|
17289
17585
|
);
|
|
@@ -17319,45 +17615,61 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
|
|
|
17319
17615
|
}
|
|
17320
17616
|
|
|
17321
17617
|
// core/sqlite/db-drift-reset.ts
|
|
17322
|
-
import { existsSync as
|
|
17618
|
+
import { existsSync as existsSync24 } from "fs";
|
|
17323
17619
|
import { createInterface as createInterface4 } from "readline";
|
|
17324
|
-
import { DatabaseSync as
|
|
17620
|
+
import { DatabaseSync as DatabaseSync8 } from "node:sqlite";
|
|
17325
17621
|
|
|
17326
17622
|
// core/sqlite/i18n/db-drift.texts.ts
|
|
17327
17623
|
var DB_DRIFT_TEXTS = {
|
|
17328
17624
|
// Interactive confirm (TTY `sm scan`, no `--yes`). The block is
|
|
17329
17625
|
// written to stderr, then the question line drives `readline`.
|
|
17330
|
-
|
|
17331
|
-
|
|
17626
|
+
// `{{reason}}` is one of the `driftReason*` strings below so the
|
|
17627
|
+
// operator sees WHY the cache is being rebuilt (version skew vs an
|
|
17628
|
+
// inline schema change the version did not bump).
|
|
17629
|
+
driftPrompt: "{{glyph}} The local cache was built by skill-map {{dbVersion}} and you are on {{currentVersion}} ({{reason}}).\n {{hint}}\n",
|
|
17630
|
+
driftPromptHint: "Pre-1.0 the DB is a derived cache (your .sm sidecars hold the real data); it cannot be carried across a schema change and has to be rebuilt.",
|
|
17332
17631
|
driftPromptQuestion: "Delete the local cache and rebuild it on this scan? [y/N] ",
|
|
17333
17632
|
// Receipt after the rebuild (printed by the scan / refresh path).
|
|
17334
|
-
driftReset: "{{glyph}} Local cache rebuilt: it was written by skill-map {{dbVersion}}, you are on {{currentVersion}}.\n {{hint}}\n",
|
|
17633
|
+
driftReset: "{{glyph}} Local cache rebuilt ({{reason}}): it was written by skill-map {{dbVersion}}, you are on {{currentVersion}}.\n {{hint}}\n",
|
|
17335
17634
|
driftResetHint: "The DB was deleted and is being regenerated by this scan; .sm sidecars were not touched.",
|
|
17336
17635
|
// Abort headline when the operator declines (wrapped by the caller's
|
|
17337
17636
|
// `sm scan: {message}` shell, so it carries no glyph / verb prefix).
|
|
17338
|
-
driftAborted: "cache rebuild declined: the {{dbVersion}} cache cannot be reused on {{currentVersion}}. {{hint}}",
|
|
17339
|
-
driftAbortedHint: "Re-run with --yes, or run `sm db reset --hard` then `sm scan`."
|
|
17637
|
+
driftAborted: "cache rebuild declined: the {{dbVersion}} cache cannot be reused on {{currentVersion}} ({{reason}}). {{hint}}",
|
|
17638
|
+
driftAbortedHint: "Re-run with --yes, or run `sm db reset --hard` then `sm scan`.",
|
|
17639
|
+
// Drift reason fragments, interpolated as `{{reason}}` above. Version
|
|
17640
|
+
// skew = the recorded scanned_by_version differs at major.minor.
|
|
17641
|
+
// Schema fingerprint = an inline migration edit (no version bump,
|
|
17642
|
+
// greenfield posture) changed the DDL.
|
|
17643
|
+
driftReasonVersion: "version skew",
|
|
17644
|
+
driftReasonSchema: "schema change in this version"
|
|
17340
17645
|
};
|
|
17341
17646
|
|
|
17342
17647
|
// core/sqlite/db-drift-reset.ts
|
|
17343
17648
|
async function maybeResetOnDrift(dbPath, policy) {
|
|
17344
|
-
const
|
|
17345
|
-
if (
|
|
17346
|
-
const
|
|
17347
|
-
|
|
17348
|
-
const confirmed = await confirmDriftReset(dbVersion, policy);
|
|
17649
|
+
const reason = detectDriftReason(dbPath, policy.currentVersion);
|
|
17650
|
+
if (reason === null) return { kind: "no-drift" };
|
|
17651
|
+
const dbVersion = readScannedByVersion(dbPath) ?? "unknown";
|
|
17652
|
+
const confirmed = await confirmDriftReset(dbVersion, reason, policy);
|
|
17349
17653
|
if (!confirmed) {
|
|
17350
|
-
return { kind: "aborted", dbVersion, currentVersion: policy.currentVersion };
|
|
17654
|
+
return { kind: "aborted", dbVersion, currentVersion: policy.currentVersion, reason };
|
|
17351
17655
|
}
|
|
17352
17656
|
await removeDbFiles(dbPath);
|
|
17353
|
-
renderResetReceipt(dbVersion, policy);
|
|
17354
|
-
return { kind: "reset", dbVersion, currentVersion: policy.currentVersion };
|
|
17657
|
+
renderResetReceipt(dbVersion, reason, policy);
|
|
17658
|
+
return { kind: "reset", dbVersion, currentVersion: policy.currentVersion, reason };
|
|
17659
|
+
}
|
|
17660
|
+
function detectDriftReason(dbPath, currentVersion) {
|
|
17661
|
+
const dbVersion = readScannedByVersion(dbPath);
|
|
17662
|
+
if (dbVersion !== null) {
|
|
17663
|
+
const skew = classifyVersionSkew(dbVersion, currentVersion);
|
|
17664
|
+
if (skew.kind !== "ok" && skew.kind !== "no-meta") return "version";
|
|
17665
|
+
}
|
|
17666
|
+
return classifyFingerprint(dbPath).kind === "drift" ? "schema" : null;
|
|
17355
17667
|
}
|
|
17356
17668
|
function readScannedByVersion(dbPath) {
|
|
17357
|
-
if (dbPath === ":memory:" || !
|
|
17669
|
+
if (dbPath === ":memory:" || !existsSync24(dbPath)) return null;
|
|
17358
17670
|
let raw = null;
|
|
17359
17671
|
try {
|
|
17360
|
-
raw = new
|
|
17672
|
+
raw = new DatabaseSync8(dbPath, { readOnly: true });
|
|
17361
17673
|
const row = raw.prepare("SELECT scanned_by_version AS v FROM scan_meta LIMIT 1").get();
|
|
17362
17674
|
const v = row?.v;
|
|
17363
17675
|
return typeof v === "string" && v.length > 0 ? v : null;
|
|
@@ -17367,16 +17679,19 @@ function readScannedByVersion(dbPath) {
|
|
|
17367
17679
|
raw?.close();
|
|
17368
17680
|
}
|
|
17369
17681
|
}
|
|
17370
|
-
async function confirmDriftReset(dbVersion, policy) {
|
|
17682
|
+
async function confirmDriftReset(dbVersion, reason, policy) {
|
|
17371
17683
|
if (!shouldPromptForReset(policy)) return true;
|
|
17372
|
-
return askDriftReset(dbVersion, policy);
|
|
17684
|
+
return askDriftReset(dbVersion, reason, policy);
|
|
17685
|
+
}
|
|
17686
|
+
function reasonText(reason) {
|
|
17687
|
+
return reason === "version" ? DB_DRIFT_TEXTS.driftReasonVersion : DB_DRIFT_TEXTS.driftReasonSchema;
|
|
17373
17688
|
}
|
|
17374
17689
|
function shouldPromptForReset(policy) {
|
|
17375
17690
|
if (policy.assumeYes) return false;
|
|
17376
17691
|
if (!policy.stdin || !policy.stderr) return false;
|
|
17377
17692
|
return policy.stdin.isTTY === true;
|
|
17378
17693
|
}
|
|
17379
|
-
async function askDriftReset(dbVersion, policy) {
|
|
17694
|
+
async function askDriftReset(dbVersion, reason, policy) {
|
|
17380
17695
|
const warnGlyph = policy.style?.warnGlyph ?? "\u26A0";
|
|
17381
17696
|
const dim = policy.style?.dim ?? ((s) => s);
|
|
17382
17697
|
policy.stderr.write(
|
|
@@ -17384,6 +17699,7 @@ async function askDriftReset(dbVersion, policy) {
|
|
|
17384
17699
|
glyph: warnGlyph,
|
|
17385
17700
|
dbVersion,
|
|
17386
17701
|
currentVersion: policy.currentVersion,
|
|
17702
|
+
reason: reasonText(reason),
|
|
17387
17703
|
hint: dim(DB_DRIFT_TEXTS.driftPromptHint)
|
|
17388
17704
|
})
|
|
17389
17705
|
);
|
|
@@ -17397,7 +17713,7 @@ async function askDriftReset(dbVersion, policy) {
|
|
|
17397
17713
|
rl.close();
|
|
17398
17714
|
}
|
|
17399
17715
|
}
|
|
17400
|
-
function renderResetReceipt(dbVersion, policy) {
|
|
17716
|
+
function renderResetReceipt(dbVersion, reason, policy) {
|
|
17401
17717
|
if (!policy.printer) return;
|
|
17402
17718
|
const warnGlyph = policy.style?.warnGlyph ?? "\u26A0";
|
|
17403
17719
|
const dim = policy.style?.dim ?? ((s) => s);
|
|
@@ -17406,6 +17722,7 @@ function renderResetReceipt(dbVersion, policy) {
|
|
|
17406
17722
|
glyph: warnGlyph,
|
|
17407
17723
|
dbVersion,
|
|
17408
17724
|
currentVersion: policy.currentVersion,
|
|
17725
|
+
reason: reasonText(reason),
|
|
17409
17726
|
hint: dim(DB_DRIFT_TEXTS.driftResetHint)
|
|
17410
17727
|
})
|
|
17411
17728
|
);
|
|
@@ -17448,10 +17765,13 @@ async function runScanForCommand(opts) {
|
|
|
17448
17765
|
referenceablePaths,
|
|
17449
17766
|
ctx.cwd,
|
|
17450
17767
|
activeProvider,
|
|
17451
|
-
cfg.scan.maxNodes
|
|
17768
|
+
cfg.scan.maxNodes,
|
|
17769
|
+
cfg.scan.maxFileSizeBytes,
|
|
17770
|
+
cfg.tokenizer
|
|
17452
17771
|
);
|
|
17453
17772
|
const willPersist = !opts.noBuiltIns && !opts.dryRun;
|
|
17454
|
-
|
|
17773
|
+
const scanned = await (willPersist ? runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanWith, extensions) : runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith));
|
|
17774
|
+
return scanned.kind === "ok" ? { ...scanned, lensAutoDetected: lens.autoDetected } : scanned;
|
|
17455
17775
|
}
|
|
17456
17776
|
function detectionProviders(extensions) {
|
|
17457
17777
|
return extensions?.providers ?? [];
|
|
@@ -17480,12 +17800,20 @@ async function resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime, provi
|
|
|
17480
17800
|
})
|
|
17481
17801
|
};
|
|
17482
17802
|
}
|
|
17483
|
-
|
|
17803
|
+
warnIfLensPluginDisabled({
|
|
17484
17804
|
activeProvider: bootstrap.activeProvider,
|
|
17485
17805
|
resolveEnabled: opts.resolveEnabledOverride ?? pluginRuntime.resolveEnabled,
|
|
17486
17806
|
printer: opts.printer
|
|
17487
17807
|
});
|
|
17488
|
-
return {
|
|
17808
|
+
return {
|
|
17809
|
+
kind: "ok",
|
|
17810
|
+
activeProvider: bootstrap.activeProvider,
|
|
17811
|
+
// Only when the lens was freshly auto-detected (not read from
|
|
17812
|
+
// config) does the caller announce it. The bootstrap no longer
|
|
17813
|
+
// prints it itself, to avoid interleaving stderr with the
|
|
17814
|
+
// stdout scan summary on a tty.
|
|
17815
|
+
autoDetected: bootstrap.source === "autodetect" ? bootstrap.activeProvider : null
|
|
17816
|
+
};
|
|
17489
17817
|
}
|
|
17490
17818
|
function emitReferenceWalkAdvisory(walk3, opts) {
|
|
17491
17819
|
if (walk3.truncated) {
|
|
@@ -17553,7 +17881,7 @@ function makePriorLoader(noBuiltIns, strict) {
|
|
|
17553
17881
|
return loaded;
|
|
17554
17882
|
};
|
|
17555
17883
|
}
|
|
17556
|
-
function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, extensions, referenceablePaths, scanCwd, activeProvider, recommendedNodeLimit) {
|
|
17884
|
+
function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, extensions, referenceablePaths, scanCwd, activeProvider, recommendedNodeLimit, maxFileSizeBytes, tokenizer) {
|
|
17557
17885
|
return async (prior, priorExtractorRuns, orphanJobFiles) => {
|
|
17558
17886
|
if (opts.changed && prior === null) {
|
|
17559
17887
|
opts.stderr.write(SCAN_RUNNER_TEXTS.changedNoPriorWarning);
|
|
@@ -17569,6 +17897,8 @@ function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, exte
|
|
|
17569
17897
|
prior,
|
|
17570
17898
|
activeProvider,
|
|
17571
17899
|
recommendedNodeLimit,
|
|
17900
|
+
maxFileSizeBytes,
|
|
17901
|
+
tokenizer,
|
|
17572
17902
|
...priorExtractorRuns ? { priorExtractorRuns } : {},
|
|
17573
17903
|
...orphanJobFiles ? { orphanJobFiles } : {}
|
|
17574
17904
|
});
|
|
@@ -17580,6 +17910,7 @@ function buildRunScanOptions(args2) {
|
|
|
17580
17910
|
const runOptions = {
|
|
17581
17911
|
roots: args2.effectiveRoots.slice(),
|
|
17582
17912
|
tokenize: !opts.noTokens,
|
|
17913
|
+
tokenizer: args2.tokenizer,
|
|
17583
17914
|
ignoreFilter: args2.ignoreFilter,
|
|
17584
17915
|
strict: args2.strict,
|
|
17585
17916
|
emitter: buildRunScanEmitter(opts),
|
|
@@ -17590,7 +17921,8 @@ function buildRunScanOptions(args2) {
|
|
|
17590
17921
|
orphanJobFiles: orphanJobFiles ?? [],
|
|
17591
17922
|
activeProvider: args2.activeProvider,
|
|
17592
17923
|
recommendedNodeLimit: args2.recommendedNodeLimit,
|
|
17593
|
-
overrideMaxNodes: opts.maxNodes ?? null
|
|
17924
|
+
overrideMaxNodes: opts.maxNodes ?? null,
|
|
17925
|
+
maxFileSizeBytes: args2.maxFileSizeBytes
|
|
17594
17926
|
};
|
|
17595
17927
|
if (args2.extensions) runOptions.extensions = args2.extensions;
|
|
17596
17928
|
if (prior) {
|
|
@@ -17624,6 +17956,7 @@ async function rebuildOnDrift(opts, dbPath) {
|
|
|
17624
17956
|
message: tx(DB_DRIFT_TEXTS.driftAborted, {
|
|
17625
17957
|
dbVersion: drift.dbVersion,
|
|
17626
17958
|
currentVersion: drift.currentVersion,
|
|
17959
|
+
reason: drift.reason === "version" ? DB_DRIFT_TEXTS.driftReasonVersion : DB_DRIFT_TEXTS.driftReasonSchema,
|
|
17627
17960
|
hint: dim(DB_DRIFT_TEXTS.driftAbortedHint)
|
|
17628
17961
|
})
|
|
17629
17962
|
};
|
|
@@ -17962,6 +18295,11 @@ async function runFirstScan(scopeRoot, strict, printer, stderr, stdin, ansi) {
|
|
|
17962
18295
|
return ExitCode.Ok;
|
|
17963
18296
|
}
|
|
17964
18297
|
const result = outcome.result;
|
|
18298
|
+
if (outcome.lensAutoDetected) {
|
|
18299
|
+
printer.info(
|
|
18300
|
+
tx(SCAN_RUNNER_TEXTS.activeProviderAutodetected, { id: outcome.lensAutoDetected })
|
|
18301
|
+
);
|
|
18302
|
+
}
|
|
17965
18303
|
const hasErrors = result.issues.some((i) => i.severity === "error");
|
|
17966
18304
|
printer.info(
|
|
17967
18305
|
tx(INIT_TEXTS.firstScanSummary, {
|
|
@@ -18788,7 +19126,11 @@ var ListCommand = class extends SmCommand {
|
|
|
18788
19126
|
const exit = requireDbOrExit(dbPath, this.context.stderr);
|
|
18789
19127
|
if (exit !== null) return exit;
|
|
18790
19128
|
return withSqlite(
|
|
18791
|
-
{
|
|
19129
|
+
{
|
|
19130
|
+
databasePath: dbPath,
|
|
19131
|
+
autoBackup: false,
|
|
19132
|
+
versionCheck: buildReadVersionCheck(this.printer, stderrAnsi)
|
|
19133
|
+
},
|
|
18792
19134
|
(adapter) => this.#runQuery(adapter, flags)
|
|
18793
19135
|
);
|
|
18794
19136
|
}
|
|
@@ -19480,10 +19822,10 @@ var PLUGINS_TEXTS = {
|
|
|
19480
19822
|
pluginNotFoundHint: "Run `sm plugins list` for discovered ids and the qualified extension ids.",
|
|
19481
19823
|
pluginLocked: '{{glyph}} Plugin "{{id}}" is locked by the host and cannot be toggled.\n {{hint}}\n',
|
|
19482
19824
|
pluginLockedHint: "Locked plugins are mandatory for correct operation. To remove the lock, edit `src/kernel/config/locked-plugins.ts`.",
|
|
19483
|
-
qualifiedIdNotFound: "{{glyph}} Qualified extension id not found: {{id}}\n The owning
|
|
19484
|
-
qualifiedIdNotFoundHint: "Run `sm plugins list` to see what each
|
|
19485
|
-
|
|
19486
|
-
|
|
19825
|
+
qualifiedIdNotFound: "{{glyph}} Qualified extension id not found: {{id}}\n The owning plugin '{{pluginId}}' does not declare an extension with id '{{extId}}'.\n {{hint}}\n",
|
|
19826
|
+
qualifiedIdNotFoundHint: "Run `sm plugins list` to see what each plugin ships.",
|
|
19827
|
+
qualifiedIdUnknownPlugin: "{{glyph}} Qualified extension id references unknown plugin: {{pluginId}}\n {{hint}}\n",
|
|
19828
|
+
qualifiedIdUnknownPluginHint: "Run `sm plugins list` for known plugin ids.",
|
|
19487
19829
|
// Spec § A.10, `applicableKinds` filter on Extractors. When an extractor
|
|
19488
19830
|
// declares a kind that no installed Provider emits, the load succeeds
|
|
19489
19831
|
// (the Provider may arrive later) but `sm plugins doctor` surfaces a
|
|
@@ -19508,7 +19850,7 @@ var PLUGINS_TEXTS = {
|
|
|
19508
19850
|
// --- doctor verb -----------------------------------------------------
|
|
19509
19851
|
/**
|
|
19510
19852
|
* One-line summary that opens the human doctor output. `enabled` is
|
|
19511
|
-
* the count of enabled extensions across every
|
|
19853
|
+
* the count of enabled extensions across every plugin (every
|
|
19512
19854
|
* extension is independently toggle-able by its qualified id); the
|
|
19513
19855
|
* value matches the row count rendered by `sm plugins list` once
|
|
19514
19856
|
* disabled extensions are filtered out.
|
|
@@ -19552,10 +19894,10 @@ var PLUGINS_TEXTS = {
|
|
|
19552
19894
|
/**
|
|
19553
19895
|
* Macro expansion summary printed on stderr before the confirm
|
|
19554
19896
|
* prompt (or before the `--yes` rejection). The block lists every
|
|
19555
|
-
* qualified extension id the bare
|
|
19897
|
+
* qualified extension id the bare plugin id resolves to, so the
|
|
19556
19898
|
* user sees the exact set that would flip.
|
|
19557
19899
|
*/
|
|
19558
|
-
bundleMacroHeader: "sm plugins {{verb}} {{
|
|
19900
|
+
bundleMacroHeader: "sm plugins {{verb}} {{pluginId}}: this will affect {{count}} extensions:\n",
|
|
19559
19901
|
bundleMacroRow: " - {{id}}\n",
|
|
19560
19902
|
/**
|
|
19561
19903
|
* Interactive prompt rendered to a TTY by the macro path. The
|
|
@@ -19575,7 +19917,7 @@ var PLUGINS_TEXTS = {
|
|
|
19575
19917
|
* the directed re-run hint.
|
|
19576
19918
|
*/
|
|
19577
19919
|
bundleMacroRequiresYes: "{{glyph}} Refusing to {{verb}} multiple extensions without confirmation.\n {{hint}}\n",
|
|
19578
|
-
bundleMacroRequiresYesHint: "Re-run with --yes to apply, or pass a qualified id `<
|
|
19920
|
+
bundleMacroRequiresYesHint: "Re-run with --yes to apply, or pass a qualified id `<plugin>/<extension>` for a single extension.",
|
|
19579
19921
|
// --- list / show renderers ------------------------------------------
|
|
19580
19922
|
rowStatusOk: "ok",
|
|
19581
19923
|
rowStatusOff: "off",
|
|
@@ -19588,18 +19930,18 @@ var PLUGINS_TEXTS = {
|
|
|
19588
19930
|
sourceBuiltIn: "built-in",
|
|
19589
19931
|
sourceUser: "user",
|
|
19590
19932
|
/**
|
|
19591
|
-
* Compact
|
|
19933
|
+
* Compact plugin row: ` GLYPH ID(pad) N ext SOURCE`.
|
|
19592
19934
|
* Padding for `id` and `count` is computed at render time so all rows
|
|
19593
19935
|
* align regardless of length. The glyph is wrapped in color before the
|
|
19594
19936
|
* template substitution.
|
|
19595
19937
|
*/
|
|
19596
|
-
|
|
19938
|
+
pluginRow: " {{glyph}} {{id}}{{count}} ext {{source}}",
|
|
19597
19939
|
/**
|
|
19598
|
-
* Indent applied to the names / reason lines under each
|
|
19940
|
+
* Indent applied to the names / reason lines under each plugin row.
|
|
19599
19941
|
* Kept as a single source of truth so the wrap math (`wrapNames`) and
|
|
19600
19942
|
* the visible output stay in sync.
|
|
19601
19943
|
*/
|
|
19602
|
-
|
|
19944
|
+
pluginSubIndent: " ",
|
|
19603
19945
|
listTipShow: "\nTip: `sm plugins show <id>` for kinds, versions, and per-extension status.\n",
|
|
19604
19946
|
/** Show command, built-in header (no version row, no path). */
|
|
19605
19947
|
detailHeaderBuiltIn: " {{glyph}} {{id}} {{source}} {{count}} extension{{plural}}\n",
|
|
@@ -19621,11 +19963,11 @@ var PLUGINS_TEXTS = {
|
|
|
19621
19963
|
/** Extensions block heading, separated from the header by a blank line. */
|
|
19622
19964
|
detailExtensionsBlock: "\n",
|
|
19623
19965
|
/**
|
|
19624
|
-
* Extension row inside the
|
|
19966
|
+
* Extension row inside the plugin detail. Every extension is
|
|
19625
19967
|
* independently toggle-able, so every row carries its own glyph
|
|
19626
19968
|
* (✓ / ✕). Padding for {{kind}} and {{name}} is computed at render
|
|
19627
19969
|
* time so columns align inside the block. `{{versionSuffix}}` is
|
|
19628
|
-
* either ` v<x.y.z>` (user plugins) or empty (built-in
|
|
19970
|
+
* either ` v<x.y.z>` (user plugins) or empty (built-in plugins,
|
|
19629
19971
|
* which inherit the CLI version and do not maintain per-extension
|
|
19630
19972
|
* versions of their own).
|
|
19631
19973
|
*/
|
|
@@ -19633,7 +19975,7 @@ var PLUGINS_TEXTS = {
|
|
|
19633
19975
|
detailVersionUnknown: "?",
|
|
19634
19976
|
detailCompatUnknown: "?",
|
|
19635
19977
|
/**
|
|
19636
|
-
* Show command, single-extension header (qualified `<
|
|
19978
|
+
* Show command, single-extension header (qualified `<plugin>/<ext>` id
|
|
19637
19979
|
* shape). Mirrors `detailHeaderBuiltIn` but the count slot is replaced
|
|
19638
19980
|
* by the kind so the reader sees at a glance whether they are looking
|
|
19639
19981
|
* at an extractor, analyzer, etc. Version moves down into the field
|
|
@@ -19680,19 +20022,19 @@ var PLUGINS_TEXTS = {
|
|
|
19680
20022
|
};
|
|
19681
20023
|
|
|
19682
20024
|
// plugins/presentation-order.ts
|
|
19683
|
-
var
|
|
20025
|
+
var BUILT_IN_PLUGIN_PRESENTATION_ORDER = [
|
|
19684
20026
|
"core",
|
|
19685
20027
|
"claude",
|
|
19686
20028
|
"antigravity",
|
|
19687
20029
|
"openai",
|
|
19688
20030
|
"agent-skills"
|
|
19689
20031
|
];
|
|
19690
|
-
function
|
|
20032
|
+
function sortPluginsForPresentation(plugins) {
|
|
19691
20033
|
const orderIndex = (id) => {
|
|
19692
|
-
const idx =
|
|
19693
|
-
return idx >= 0 ? idx :
|
|
20034
|
+
const idx = BUILT_IN_PLUGIN_PRESENTATION_ORDER.indexOf(id);
|
|
20035
|
+
return idx >= 0 ? idx : BUILT_IN_PLUGIN_PRESENTATION_ORDER.length;
|
|
19694
20036
|
};
|
|
19695
|
-
return [...
|
|
20037
|
+
return [...plugins].sort((a, b) => {
|
|
19696
20038
|
const ai = orderIndex(a.id);
|
|
19697
20039
|
const bi = orderIndex(b.id);
|
|
19698
20040
|
if (ai !== bi) return ai - bi;
|
|
@@ -19729,24 +20071,24 @@ async function loadAll(opts) {
|
|
|
19729
20071
|
return loader.discoverAndLoadAll();
|
|
19730
20072
|
}
|
|
19731
20073
|
function builtInRows(resolveEnabled) {
|
|
19732
|
-
return
|
|
19733
|
-
const extensions =
|
|
19734
|
-
const manifestSummary =
|
|
20074
|
+
return sortPluginsForPresentation(builtInPlugins).map((plugin) => {
|
|
20075
|
+
const extensions = plugin.extensions.map((ext) => extensionRowFromBuiltIn(ext, plugin, resolveEnabled));
|
|
20076
|
+
const manifestSummary = plugin.extensions.map((ext) => `${ext.kind}:${qualifiedExtensionId(plugin.id, ext.id)}@${ext.version}`).join(", ");
|
|
19735
20077
|
return {
|
|
19736
|
-
id:
|
|
20078
|
+
id: plugin.id,
|
|
19737
20079
|
enabled: extensions.some((e) => e.enabled),
|
|
19738
|
-
description:
|
|
20080
|
+
description: plugin.description,
|
|
19739
20081
|
extensions,
|
|
19740
20082
|
manifestSummary
|
|
19741
20083
|
};
|
|
19742
20084
|
});
|
|
19743
20085
|
}
|
|
19744
|
-
function extensionRowFromBuiltIn(ext,
|
|
20086
|
+
function extensionRowFromBuiltIn(ext, plugin, resolveEnabled) {
|
|
19745
20087
|
const row = {
|
|
19746
20088
|
id: ext.id,
|
|
19747
20089
|
kind: ext.kind,
|
|
19748
20090
|
version: ext.version,
|
|
19749
|
-
enabled: resolveEnabled(qualifiedExtensionId(
|
|
20091
|
+
enabled: resolveEnabled(qualifiedExtensionId(plugin.id, ext.id)),
|
|
19750
20092
|
description: ext.description ?? ""
|
|
19751
20093
|
};
|
|
19752
20094
|
if (ext.entry !== void 0) row.entry = ext.entry;
|
|
@@ -19782,7 +20124,7 @@ var PluginsListCommand = class extends SmCommand {
|
|
|
19782
20124
|
static usage = Command22.Usage({
|
|
19783
20125
|
category: "Plugins",
|
|
19784
20126
|
description: "List discovered plugins and their load status.",
|
|
19785
|
-
details: "Scans <cwd>/.skill-map/plugins (or --plugin-dir <path>). Built-in
|
|
20127
|
+
details: "Scans <cwd>/.skill-map/plugins (or --plugin-dir <path>). Built-in plugins (claude, core) are listed alongside user plugins."
|
|
19786
20128
|
});
|
|
19787
20129
|
pluginDir = Option21.String("--plugin-dir", { required: false });
|
|
19788
20130
|
async run() {
|
|
@@ -19819,14 +20161,14 @@ function renderListHuman(builtIns2, plugins, resolveEnabled, ansi) {
|
|
|
19819
20161
|
const idCol = row.id.padEnd(idWidth);
|
|
19820
20162
|
const countCol = String(row.names.length).padStart(countWidth);
|
|
19821
20163
|
lines.push(
|
|
19822
|
-
tx(PLUGINS_TEXTS.
|
|
20164
|
+
tx(PLUGINS_TEXTS.pluginRow, {
|
|
19823
20165
|
glyph,
|
|
19824
20166
|
id: idCol,
|
|
19825
20167
|
count: ` ${countCol}`,
|
|
19826
20168
|
source: ansi.dim(row.source)
|
|
19827
20169
|
})
|
|
19828
20170
|
);
|
|
19829
|
-
const indent = PLUGINS_TEXTS.
|
|
20171
|
+
const indent = PLUGINS_TEXTS.pluginSubIndent;
|
|
19830
20172
|
if (row.reason) {
|
|
19831
20173
|
lines.push(`${indent}${ansi.dim(row.reason)}`);
|
|
19832
20174
|
} else if (row.names.length > 0) {
|
|
@@ -19890,11 +20232,11 @@ var PluginsShowCommand = class extends SmCommand {
|
|
|
19890
20232
|
category: "Plugins",
|
|
19891
20233
|
description: "Show a single plugin's manifest + loaded extensions.",
|
|
19892
20234
|
details: `
|
|
19893
|
-
Accepts a
|
|
20235
|
+
Accepts a plugin id (\`core\`, \`claude\`, \`my-plugin\`)
|
|
19894
20236
|
or a qualified extension id (\`core/<ext-id>\`,
|
|
19895
20237
|
\`<plugin>/<ext-id>\`). When given a qualified id, validates the
|
|
19896
20238
|
extension exists and renders a single-extension detail block.
|
|
19897
|
-
The bare form renders the parent
|
|
20239
|
+
The bare form renders the parent plugin's detail with per-extension
|
|
19898
20240
|
status. The same id shapes \`sm plugins enable\` and
|
|
19899
20241
|
\`sm plugins disable\` accept resolve cleanly here too.
|
|
19900
20242
|
`
|
|
@@ -19911,9 +20253,9 @@ var PluginsShowCommand = class extends SmCommand {
|
|
|
19911
20253
|
this.printer.error(lookupResult.error);
|
|
19912
20254
|
return ExitCode.NotFound;
|
|
19913
20255
|
}
|
|
19914
|
-
const {
|
|
19915
|
-
const builtIn = builtIns2.find((b) => b.id ===
|
|
19916
|
-
const match = plugins.find((p) => p.id ===
|
|
20256
|
+
const { pluginId, extId } = lookupResult;
|
|
20257
|
+
const builtIn = builtIns2.find((b) => b.id === pluginId);
|
|
20258
|
+
const match = plugins.find((p) => p.id === pluginId);
|
|
19917
20259
|
if (!builtIn && !match) {
|
|
19918
20260
|
this.printer.error(
|
|
19919
20261
|
tx(PLUGINS_TEXTS.pluginNotFound, {
|
|
@@ -19925,7 +20267,7 @@ var PluginsShowCommand = class extends SmCommand {
|
|
|
19925
20267
|
return ExitCode.NotFound;
|
|
19926
20268
|
}
|
|
19927
20269
|
if (extId !== void 0) {
|
|
19928
|
-
return this.renderExtensionDetail({ extId,
|
|
20270
|
+
return this.renderExtensionDetail({ extId, pluginId, builtIn, match });
|
|
19929
20271
|
}
|
|
19930
20272
|
if (this.json) {
|
|
19931
20273
|
const payload = builtIn ?? match;
|
|
@@ -19939,23 +20281,23 @@ var PluginsShowCommand = class extends SmCommand {
|
|
|
19939
20281
|
}
|
|
19940
20282
|
/**
|
|
19941
20283
|
* Render the single-extension detail block, the path taken when the
|
|
19942
|
-
* user supplies a qualified `<
|
|
19943
|
-
* single extension row (no surrounding
|
|
20284
|
+
* user supplies a qualified `<plugin>/<ext>` id. `--json` emits the
|
|
20285
|
+
* single extension row (no surrounding plugin envelope) so tooling
|
|
19944
20286
|
* can pipe straight into `jq`; human mode renders a focused header
|
|
19945
20287
|
* plus a Kind / Version / Stability / Description / Preconditions /
|
|
19946
20288
|
* Entry field block.
|
|
19947
20289
|
*/
|
|
19948
20290
|
renderExtensionDetail(args2) {
|
|
19949
|
-
const { extId,
|
|
20291
|
+
const { extId, pluginId, builtIn, match } = args2;
|
|
19950
20292
|
const ansi = this.ansiFor("stdout");
|
|
19951
20293
|
if (builtIn) {
|
|
19952
20294
|
const ext = builtIn.extensions.find((e) => e.id === extId);
|
|
19953
20295
|
if (!ext) return ExitCode.NotFound;
|
|
19954
20296
|
if (this.json) {
|
|
19955
|
-
this.printer.data(JSON.stringify({ pluginId
|
|
20297
|
+
this.printer.data(JSON.stringify({ pluginId, ...ext }, omitModule, 2) + "\n");
|
|
19956
20298
|
return ExitCode.Ok;
|
|
19957
20299
|
}
|
|
19958
|
-
this.printer.data(renderBuiltInExtensionDetail(
|
|
20300
|
+
this.printer.data(renderBuiltInExtensionDetail(pluginId, ext, ansi));
|
|
19959
20301
|
return ExitCode.Ok;
|
|
19960
20302
|
}
|
|
19961
20303
|
const userExt = match?.extensions?.find((e) => e.id === extId);
|
|
@@ -19964,53 +20306,53 @@ var PluginsShowCommand = class extends SmCommand {
|
|
|
19964
20306
|
this.printer.data(JSON.stringify(userExt, omitModule, 2) + "\n");
|
|
19965
20307
|
return ExitCode.Ok;
|
|
19966
20308
|
}
|
|
19967
|
-
this.printer.data(renderUserExtensionDetail(
|
|
20309
|
+
this.printer.data(renderUserExtensionDetail(pluginId, userExt, ansi));
|
|
19968
20310
|
return ExitCode.Ok;
|
|
19969
20311
|
}
|
|
19970
20312
|
};
|
|
19971
20313
|
function resolveShowLookupId(id, builtIns2, plugins, ansi) {
|
|
19972
|
-
if (!id.includes("/")) return {
|
|
20314
|
+
if (!id.includes("/")) return { pluginId: id };
|
|
19973
20315
|
const parsed = parseQualifiedId(id);
|
|
19974
20316
|
if ("error" in parsed) return { error: malformedQualifiedError(id, ansi) };
|
|
19975
|
-
const {
|
|
19976
|
-
const knownExts = collectKnownExtensions(
|
|
19977
|
-
if (knownExts === null) return { error:
|
|
20317
|
+
const { pluginId, extId } = parsed;
|
|
20318
|
+
const knownExts = collectKnownExtensions(pluginId, builtIns2, plugins);
|
|
20319
|
+
if (knownExts === null) return { error: unknownPluginError(pluginId, ansi) };
|
|
19978
20320
|
if (!knownExts.includes(extId)) {
|
|
19979
|
-
return { error: unknownExtensionError(id,
|
|
20321
|
+
return { error: unknownExtensionError(id, pluginId, extId, ansi) };
|
|
19980
20322
|
}
|
|
19981
|
-
return {
|
|
20323
|
+
return { pluginId, extId };
|
|
19982
20324
|
}
|
|
19983
20325
|
function parseQualifiedId(id) {
|
|
19984
|
-
const [
|
|
19985
|
-
if (!
|
|
19986
|
-
return {
|
|
20326
|
+
const [pluginId, extId, ...rest] = id.split("/");
|
|
20327
|
+
if (!pluginId || !extId || rest.length > 0) return { error: true };
|
|
20328
|
+
return { pluginId, extId };
|
|
19987
20329
|
}
|
|
19988
|
-
function collectKnownExtensions(
|
|
19989
|
-
const builtIn = builtIns2.find((b) => b.id ===
|
|
20330
|
+
function collectKnownExtensions(pluginId, builtIns2, plugins) {
|
|
20331
|
+
const builtIn = builtIns2.find((b) => b.id === pluginId);
|
|
19990
20332
|
if (builtIn) return builtIn.extensions.map((e) => e.id);
|
|
19991
|
-
const userPlugin = plugins.find((p) => p.id ===
|
|
20333
|
+
const userPlugin = plugins.find((p) => p.id === pluginId);
|
|
19992
20334
|
if (userPlugin) return userPlugin.extensions?.map((e) => e.id) ?? [];
|
|
19993
20335
|
return null;
|
|
19994
20336
|
}
|
|
19995
20337
|
function malformedQualifiedError(id, ansi) {
|
|
19996
|
-
return tx(PLUGINS_TEXTS.
|
|
20338
|
+
return tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
|
|
19997
20339
|
glyph: ansi.red("\u2715"),
|
|
19998
|
-
|
|
19999
|
-
hint: ansi.dim(PLUGINS_TEXTS.
|
|
20340
|
+
pluginId: sanitizeForTerminal(id),
|
|
20341
|
+
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
|
|
20000
20342
|
});
|
|
20001
20343
|
}
|
|
20002
|
-
function
|
|
20003
|
-
return tx(PLUGINS_TEXTS.
|
|
20344
|
+
function unknownPluginError(pluginId, ansi) {
|
|
20345
|
+
return tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
|
|
20004
20346
|
glyph: ansi.red("\u2715"),
|
|
20005
|
-
|
|
20006
|
-
hint: ansi.dim(PLUGINS_TEXTS.
|
|
20347
|
+
pluginId: sanitizeForTerminal(pluginId),
|
|
20348
|
+
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
|
|
20007
20349
|
});
|
|
20008
20350
|
}
|
|
20009
|
-
function unknownExtensionError(id,
|
|
20351
|
+
function unknownExtensionError(id, pluginId, extId, ansi) {
|
|
20010
20352
|
return tx(PLUGINS_TEXTS.qualifiedIdNotFound, {
|
|
20011
20353
|
glyph: ansi.red("\u2715"),
|
|
20012
20354
|
id: sanitizeForTerminal(id),
|
|
20013
|
-
|
|
20355
|
+
pluginId: sanitizeForTerminal(pluginId),
|
|
20014
20356
|
extId: sanitizeForTerminal(extId),
|
|
20015
20357
|
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdNotFoundHint)
|
|
20016
20358
|
});
|
|
@@ -20101,18 +20443,18 @@ function renderPluginDetailFields(match) {
|
|
|
20101
20443
|
function collectPluginExtensionItems(match, ansi) {
|
|
20102
20444
|
const enabled = match.status === "enabled";
|
|
20103
20445
|
if (!enabled || !match.extensions) return [];
|
|
20104
|
-
const
|
|
20446
|
+
const safePluginId = sanitizeForTerminal(match.id);
|
|
20105
20447
|
const sorted = sortExtensionsCanonical(match.extensions);
|
|
20106
20448
|
return sorted.map((ext) => {
|
|
20107
20449
|
const safeExtId = sanitizeForTerminal(ext.id);
|
|
20108
20450
|
return {
|
|
20109
20451
|
// User plugins surfaced via `loadAll` already filter on the
|
|
20110
20452
|
// resolver, so a reachable extension on this surface is enabled
|
|
20111
|
-
// by construction. The disabled path goes through the
|
|
20453
|
+
// by construction. The disabled path goes through the plugin
|
|
20112
20454
|
// status header above (✕ on the row).
|
|
20113
20455
|
glyph: ansi.green(PLUGINS_TEXTS.rowGlyphOk),
|
|
20114
20456
|
kind: sanitizeForTerminal(ext.kind),
|
|
20115
|
-
name: `${
|
|
20457
|
+
name: `${safePluginId}/${safeExtId}`,
|
|
20116
20458
|
version: sanitizeForTerminal(ext.version)
|
|
20117
20459
|
};
|
|
20118
20460
|
});
|
|
@@ -20138,11 +20480,11 @@ function renderExtensionItems(items) {
|
|
|
20138
20480
|
}
|
|
20139
20481
|
return out.join("");
|
|
20140
20482
|
}
|
|
20141
|
-
function renderBuiltInExtensionDetail(
|
|
20483
|
+
function renderBuiltInExtensionDetail(pluginId, ext, ansi) {
|
|
20142
20484
|
const glyph = ext.enabled ? ansi.green(PLUGINS_TEXTS.rowGlyphOk) : ansi.red(PLUGINS_TEXTS.rowGlyphOff);
|
|
20143
20485
|
const header = tx(PLUGINS_TEXTS.detailHeaderExtensionBuiltIn, {
|
|
20144
20486
|
glyph,
|
|
20145
|
-
qualifiedId: sanitizeForTerminal(`${
|
|
20487
|
+
qualifiedId: sanitizeForTerminal(`${pluginId}/${ext.id}`),
|
|
20146
20488
|
source: ansi.dim(PLUGINS_TEXTS.sourceBuiltIn)
|
|
20147
20489
|
});
|
|
20148
20490
|
const meta = { kind: ext.kind };
|
|
@@ -20150,11 +20492,11 @@ function renderBuiltInExtensionDetail(bundleId, ext, ansi) {
|
|
|
20150
20492
|
if (ext.entry !== void 0) meta.entry = ext.entry;
|
|
20151
20493
|
return header + "\n" + renderExtensionFields(meta);
|
|
20152
20494
|
}
|
|
20153
|
-
function renderUserExtensionDetail(
|
|
20495
|
+
function renderUserExtensionDetail(pluginId, ext, ansi) {
|
|
20154
20496
|
const glyph = ansi.green(PLUGINS_TEXTS.rowGlyphOk);
|
|
20155
20497
|
const header = tx(PLUGINS_TEXTS.detailHeaderExtensionUser, {
|
|
20156
20498
|
glyph,
|
|
20157
|
-
qualifiedId: sanitizeForTerminal(`${
|
|
20499
|
+
qualifiedId: sanitizeForTerminal(`${pluginId}/${ext.id}`),
|
|
20158
20500
|
source: ansi.dim(PLUGINS_TEXTS.sourceUser)
|
|
20159
20501
|
});
|
|
20160
20502
|
const meta = readInstanceMeta(ext.instance);
|
|
@@ -20408,13 +20750,13 @@ function forEachProviderInstance(plugins, callback) {
|
|
|
20408
20750
|
forEachUserPluginProvider(plugins, callback);
|
|
20409
20751
|
}
|
|
20410
20752
|
function forEachBuiltInProvider(callback) {
|
|
20411
|
-
for (const
|
|
20412
|
-
for (const ext of
|
|
20753
|
+
for (const plugin of builtInPlugins) {
|
|
20754
|
+
for (const ext of plugin.extensions) {
|
|
20413
20755
|
if (ext.kind !== "provider") continue;
|
|
20414
20756
|
const provider = ext;
|
|
20415
20757
|
callback({
|
|
20416
20758
|
id: provider.id,
|
|
20417
|
-
pluginId:
|
|
20759
|
+
pluginId: plugin.id,
|
|
20418
20760
|
instance: provider
|
|
20419
20761
|
});
|
|
20420
20762
|
}
|
|
@@ -20457,15 +20799,15 @@ function collectApplicableKindWarnings(plugins, knownKinds) {
|
|
|
20457
20799
|
return out;
|
|
20458
20800
|
}
|
|
20459
20801
|
function collectBuiltInApplicableKindWarnings(out, knownKinds) {
|
|
20460
|
-
for (const
|
|
20461
|
-
for (const ext of
|
|
20802
|
+
for (const plugin of builtInPlugins) {
|
|
20803
|
+
for (const ext of plugin.extensions) {
|
|
20462
20804
|
if (ext.kind !== "extractor") continue;
|
|
20463
20805
|
const extractor = ext;
|
|
20464
20806
|
const kinds = extractor.precondition?.kind;
|
|
20465
20807
|
if (!kinds || kinds.length === 0) continue;
|
|
20466
20808
|
appendUnknownKindWarnings(
|
|
20467
20809
|
out,
|
|
20468
|
-
qualifiedExtensionId(
|
|
20810
|
+
qualifiedExtensionId(plugin.id, extractor.id),
|
|
20469
20811
|
kinds,
|
|
20470
20812
|
knownKinds
|
|
20471
20813
|
);
|
|
@@ -20508,11 +20850,11 @@ function collectUnknownSlotWarnings(plugins, knownSlots) {
|
|
|
20508
20850
|
return out;
|
|
20509
20851
|
}
|
|
20510
20852
|
function collectBuiltInUnknownSlotWarnings(out, knownSlots) {
|
|
20511
|
-
for (const
|
|
20512
|
-
for (const ext of
|
|
20853
|
+
for (const plugin of builtInPlugins) {
|
|
20854
|
+
for (const ext of plugin.extensions) {
|
|
20513
20855
|
const vc = ext.viewContributions;
|
|
20514
20856
|
if (!vc) continue;
|
|
20515
|
-
appendUnknownSlotWarnings(out, qualifiedExtensionId(
|
|
20857
|
+
appendUnknownSlotWarnings(out, qualifiedExtensionId(plugin.id, ext.id), vc, knownSlots);
|
|
20516
20858
|
}
|
|
20517
20859
|
}
|
|
20518
20860
|
}
|
|
@@ -20594,7 +20936,7 @@ import { Command as Command25, Option as Option24 } from "clipanion";
|
|
|
20594
20936
|
var TogglePluginsBase = class extends SmCommand {
|
|
20595
20937
|
all = Option24.Boolean("--all", false);
|
|
20596
20938
|
yes = Option24.Boolean("--yes,-y", false, {
|
|
20597
|
-
description: "Skip the interactive confirm when a bare
|
|
20939
|
+
description: "Skip the interactive confirm when a bare plugin id (or --all) fans the toggle out across multiple extensions."
|
|
20598
20940
|
});
|
|
20599
20941
|
ids = Option24.Rest({ name: "ids" });
|
|
20600
20942
|
async toggle(enabled) {
|
|
@@ -20603,7 +20945,7 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
20603
20945
|
const argError = this.#validateArgs(stderrAnsi, verb);
|
|
20604
20946
|
if (argError !== null) return argError;
|
|
20605
20947
|
const plugins = await loadAll({ pluginDir: void 0 });
|
|
20606
|
-
const catalogue =
|
|
20948
|
+
const catalogue = pluginCatalogue(plugins);
|
|
20607
20949
|
const targetsResult = this.#pickTargets(catalogue, stderrAnsi);
|
|
20608
20950
|
if (typeof targetsResult === "number") return targetsResult;
|
|
20609
20951
|
let targets = targetsResult;
|
|
@@ -20661,7 +21003,7 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
20661
21003
|
if (this.all) {
|
|
20662
21004
|
return catalogue.map((b) => ({
|
|
20663
21005
|
origin: "bare",
|
|
20664
|
-
|
|
21006
|
+
pluginId: b.id,
|
|
20665
21007
|
keys: b.extensionIds.map((extId) => qualifiedExtensionId(b.id, extId))
|
|
20666
21008
|
}));
|
|
20667
21009
|
}
|
|
@@ -20682,8 +21024,8 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
20682
21024
|
}
|
|
20683
21025
|
/**
|
|
20684
21026
|
* Macro gate: when the request would fan a single user input out
|
|
20685
|
-
* across more than one extension (either `--all` or a bare
|
|
20686
|
-
* id whose
|
|
21027
|
+
* across more than one extension (either `--all` or a bare plugin
|
|
21028
|
+
* id whose plugin holds ≥2 extensions), confirm the intent.
|
|
20687
21029
|
*
|
|
20688
21030
|
* Resolution order:
|
|
20689
21031
|
* 1. `--yes` flag: skip the prompt entirely.
|
|
@@ -20692,7 +21034,7 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
20692
21034
|
* message that names the extensions and points at `--yes`.
|
|
20693
21035
|
*
|
|
20694
21036
|
* Returns `true` when the verb should proceed, `false` when it
|
|
20695
|
-
* should abort. Single-extension targets (bare
|
|
21037
|
+
* should abort. Single-extension targets (bare plugin id mapping to
|
|
20696
21038
|
* one child, or qualified ids) skip the gate uniformly.
|
|
20697
21039
|
*/
|
|
20698
21040
|
// Cyclomatic count comes from the three-stage gate (--yes shortcut,
|
|
@@ -20706,11 +21048,11 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
20706
21048
|
if (this.yes) return true;
|
|
20707
21049
|
const isTty = Boolean(this.context.stdin && "isTTY" in this.context.stdin && this.context.stdin.isTTY);
|
|
20708
21050
|
for (const target of macroTargets) {
|
|
20709
|
-
const
|
|
21051
|
+
const pluginLabel = target.origin === "bare" ? target.pluginId ?? "--all" : "--all";
|
|
20710
21052
|
this.printer.info(
|
|
20711
21053
|
tx(PLUGINS_TEXTS.bundleMacroHeader, {
|
|
20712
21054
|
verb,
|
|
20713
|
-
|
|
21055
|
+
pluginId: sanitizeForTerminal(pluginLabel),
|
|
20714
21056
|
count: target.keys.length
|
|
20715
21057
|
})
|
|
20716
21058
|
);
|
|
@@ -20766,7 +21108,7 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
20766
21108
|
* Persist every qualified id in `config_plugins`. On disable, also
|
|
20767
21109
|
* purge the plugin's `scan_contributions` rows immediately (matches
|
|
20768
21110
|
* the BFF route, see `server/routes/plugins.ts:applyChangeToAdapter`).
|
|
20769
|
-
* Every key is `<
|
|
21111
|
+
* Every key is `<plugin>/<ext>` shape so the contribution purge can
|
|
20770
21112
|
* split into `(pluginId, extensionId)` cleanly.
|
|
20771
21113
|
*/
|
|
20772
21114
|
async #persistKeys(keys, enabled) {
|
|
@@ -20829,11 +21171,11 @@ var PluginsEnableCommand = class extends TogglePluginsBase {
|
|
|
20829
21171
|
flip; sm config reset plugins.<id>.enabled drops the settings.json
|
|
20830
21172
|
baseline.
|
|
20831
21173
|
|
|
20832
|
-
Accepts qualified ids (\`claude/at-directive\`) and bare
|
|
21174
|
+
Accepts qualified ids (\`claude/at-directive\`) and bare plugin
|
|
20833
21175
|
ids (\`claude\`, which fans the toggle out across every extension
|
|
20834
|
-
inside the
|
|
21176
|
+
inside the plugin). Multi-extension plugins need --yes (or an
|
|
20835
21177
|
interactive TTY confirm) to avoid flipping 27 core extensions by
|
|
20836
|
-
accident. Single-extension
|
|
21178
|
+
accident. Single-extension plugins (openai, agent-skills,
|
|
20837
21179
|
antigravity) apply without prompting.
|
|
20838
21180
|
|
|
20839
21181
|
Batches are all-or-nothing: a single unknown id aborts before
|
|
@@ -20856,11 +21198,11 @@ var PluginsDisableCommand = class extends TogglePluginsBase {
|
|
|
20856
21198
|
sm plugins list, but with status=disabled; the kernel will not
|
|
20857
21199
|
run any of its disabled extensions.
|
|
20858
21200
|
|
|
20859
|
-
Accepts qualified ids (\`core/markdown-link\`) and bare
|
|
21201
|
+
Accepts qualified ids (\`core/markdown-link\`) and bare plugin
|
|
20860
21202
|
ids (\`core\`, which fans the toggle out across every extension
|
|
20861
|
-
inside the
|
|
21203
|
+
inside the plugin). Multi-extension plugins need --yes (or an
|
|
20862
21204
|
interactive TTY confirm) to avoid flipping 27 core extensions by
|
|
20863
|
-
accident. Single-extension
|
|
21205
|
+
accident. Single-extension plugins (openai, agent-skills,
|
|
20864
21206
|
antigravity) apply without prompting.
|
|
20865
21207
|
|
|
20866
21208
|
Batches are all-or-nothing: a single unknown id aborts before
|
|
@@ -20872,12 +21214,12 @@ var PluginsDisableCommand = class extends TogglePluginsBase {
|
|
|
20872
21214
|
return this.toggle(false);
|
|
20873
21215
|
}
|
|
20874
21216
|
};
|
|
20875
|
-
function
|
|
21217
|
+
function pluginCatalogue(plugins) {
|
|
20876
21218
|
const out = [];
|
|
20877
|
-
for (const
|
|
21219
|
+
for (const plugin of builtInPlugins) {
|
|
20878
21220
|
out.push({
|
|
20879
|
-
id:
|
|
20880
|
-
extensionIds:
|
|
21221
|
+
id: plugin.id,
|
|
21222
|
+
extensionIds: plugin.extensions.map((e) => e.id)
|
|
20881
21223
|
});
|
|
20882
21224
|
}
|
|
20883
21225
|
for (const p of plugins) {
|
|
@@ -20893,32 +21235,32 @@ function resolveToggleTarget(id, catalogue, ansi) {
|
|
|
20893
21235
|
}
|
|
20894
21236
|
function resolveQualifiedToggle(id, catalogue, ansi) {
|
|
20895
21237
|
const errGlyph = ansi.red("\u2715");
|
|
20896
|
-
const [
|
|
20897
|
-
if (!
|
|
21238
|
+
const [pluginId, extId, ...rest] = id.split("/");
|
|
21239
|
+
if (!pluginId || !extId || rest.length > 0) {
|
|
20898
21240
|
return {
|
|
20899
|
-
error: tx(PLUGINS_TEXTS.
|
|
21241
|
+
error: tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
|
|
20900
21242
|
glyph: errGlyph,
|
|
20901
|
-
|
|
20902
|
-
hint: ansi.dim(PLUGINS_TEXTS.
|
|
21243
|
+
pluginId: sanitizeForTerminal(id),
|
|
21244
|
+
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
|
|
20903
21245
|
})
|
|
20904
21246
|
};
|
|
20905
21247
|
}
|
|
20906
|
-
const
|
|
20907
|
-
if (!
|
|
21248
|
+
const plugin = catalogue.find((b) => b.id === pluginId);
|
|
21249
|
+
if (!plugin) {
|
|
20908
21250
|
return {
|
|
20909
|
-
error: tx(PLUGINS_TEXTS.
|
|
21251
|
+
error: tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
|
|
20910
21252
|
glyph: errGlyph,
|
|
20911
|
-
|
|
20912
|
-
hint: ansi.dim(PLUGINS_TEXTS.
|
|
21253
|
+
pluginId: sanitizeForTerminal(pluginId),
|
|
21254
|
+
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
|
|
20913
21255
|
})
|
|
20914
21256
|
};
|
|
20915
21257
|
}
|
|
20916
|
-
if (!
|
|
21258
|
+
if (!plugin.extensionIds.includes(extId)) {
|
|
20917
21259
|
return {
|
|
20918
21260
|
error: tx(PLUGINS_TEXTS.qualifiedIdNotFound, {
|
|
20919
21261
|
glyph: errGlyph,
|
|
20920
21262
|
id: sanitizeForTerminal(id),
|
|
20921
|
-
|
|
21263
|
+
pluginId: sanitizeForTerminal(pluginId),
|
|
20922
21264
|
extId: sanitizeForTerminal(extId),
|
|
20923
21265
|
hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdNotFoundHint)
|
|
20924
21266
|
})
|
|
@@ -20926,12 +21268,12 @@ function resolveQualifiedToggle(id, catalogue, ansi) {
|
|
|
20926
21268
|
}
|
|
20927
21269
|
return {
|
|
20928
21270
|
origin: "qualified",
|
|
20929
|
-
keys: [qualifiedExtensionId(
|
|
21271
|
+
keys: [qualifiedExtensionId(pluginId, extId)]
|
|
20930
21272
|
};
|
|
20931
21273
|
}
|
|
20932
21274
|
function resolveBareToggle(id, catalogue) {
|
|
20933
|
-
const
|
|
20934
|
-
if (!
|
|
21275
|
+
const plugin = catalogue.find((b) => b.id === id);
|
|
21276
|
+
if (!plugin) {
|
|
20935
21277
|
return {
|
|
20936
21278
|
error: tx(PLUGINS_TEXTS.pluginNotFound, {
|
|
20937
21279
|
glyph: "\u2715",
|
|
@@ -20942,13 +21284,13 @@ function resolveBareToggle(id, catalogue) {
|
|
|
20942
21284
|
}
|
|
20943
21285
|
return {
|
|
20944
21286
|
origin: "bare",
|
|
20945
|
-
|
|
20946
|
-
keys:
|
|
21287
|
+
pluginId: plugin.id,
|
|
21288
|
+
keys: plugin.extensionIds.map((extId) => qualifiedExtensionId(plugin.id, extId))
|
|
20947
21289
|
};
|
|
20948
21290
|
}
|
|
20949
21291
|
|
|
20950
21292
|
// cli/commands/plugins/create.ts
|
|
20951
|
-
import { existsSync as
|
|
21293
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync5, writeFileSync } from "fs";
|
|
20952
21294
|
import { join as join18, resolve as resolve33 } from "path";
|
|
20953
21295
|
import { Command as Command26, Option as Option25 } from "clipanion";
|
|
20954
21296
|
var PluginsCreateCommand = class extends SmCommand {
|
|
@@ -20977,7 +21319,7 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
20977
21319
|
const ctx = defaultRuntimeContext();
|
|
20978
21320
|
const baseDir = defaultProjectPluginsDir(ctx);
|
|
20979
21321
|
const targetDir = this.at ? resolve33(this.at) : join18(baseDir, this.pluginId);
|
|
20980
|
-
if (
|
|
21322
|
+
if (existsSync25(targetDir) && !this.force) {
|
|
20981
21323
|
this.printer.error(
|
|
20982
21324
|
tx(PLUGINS_TEXTS.createRefuseOverwrite, {
|
|
20983
21325
|
glyph: errGlyph,
|
|
@@ -20991,20 +21333,10 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
20991
21333
|
mkdirSync5(join18(targetDir, "extractors", extractorName), { recursive: true });
|
|
20992
21334
|
const specVersion = installedSpecVersion();
|
|
20993
21335
|
const manifest = {
|
|
20994
|
-
id: this.pluginId,
|
|
20995
21336
|
version: "0.1.0",
|
|
20996
21337
|
specCompat: `^${specVersion}`,
|
|
20997
21338
|
catalogCompat: "^1.0.0",
|
|
20998
|
-
description: "Generated by `sm plugins create`. Edit to taste."
|
|
20999
|
-
settings: {
|
|
21000
|
-
keywords: {
|
|
21001
|
-
type: "string-list",
|
|
21002
|
-
label: "Keywords to track",
|
|
21003
|
-
description: "Words counted across each scanned node body.",
|
|
21004
|
-
default: ["TODO", "FIXME"],
|
|
21005
|
-
min: 1
|
|
21006
|
-
}
|
|
21007
|
-
}
|
|
21339
|
+
description: "Generated by `sm plugins create`. Edit to taste."
|
|
21008
21340
|
};
|
|
21009
21341
|
writeFileSync(
|
|
21010
21342
|
join18(targetDir, "plugin.json"),
|
|
@@ -21029,36 +21361,44 @@ function scaffolderExtractorStub(extractorId) {
|
|
|
21029
21361
|
* Generated by \`sm plugins create\`. Edit the extract() body.
|
|
21030
21362
|
*
|
|
21031
21363
|
* Loader contract: the plugin loader resolves the extension via the
|
|
21032
|
-
* MODULE'S DEFAULT EXPORT (\`export default { ... }\`).
|
|
21033
|
-
* splitting into a named export
|
|
21034
|
-
*
|
|
21364
|
+
* MODULE'S DEFAULT EXPORT (\`export default { ... }\`). It must be an
|
|
21365
|
+
* object literal; renaming or splitting into a named export surfaces as
|
|
21366
|
+
* a \`load-error\`.
|
|
21035
21367
|
*
|
|
21036
21368
|
* Folder convention: this file lives at
|
|
21037
|
-
* \`extractors/${extractorId}/index.js\`. The
|
|
21038
|
-
*
|
|
21039
|
-
*
|
|
21369
|
+
* \`extractors/${extractorId}/index.js\`. The folder layout is the
|
|
21370
|
+
* source of truth (structure-as-truth): the loader derives \`kind\`
|
|
21371
|
+
* (\`extractor\`) from the parent folder and the id (\`${extractorId}\`)
|
|
21372
|
+
* from the leaf folder, and injects \`pluginId\` from the plugin, so none
|
|
21373
|
+
* of them are declared here. Re-declaring \`kind\` / \`id\` is rejected as
|
|
21374
|
+
* \`invalid-manifest\`.
|
|
21040
21375
|
*
|
|
21041
|
-
* Declared view contributions (
|
|
21376
|
+
* Declared view contributions (\`ui\`):
|
|
21042
21377
|
* - 'count' \u2192 slot \`card.footer.left\` (renders as a chip
|
|
21043
21378
|
* in the left footer of the node card)
|
|
21044
21379
|
*
|
|
21045
|
-
* Declared settings:
|
|
21380
|
+
* Declared settings (\`settings\`):
|
|
21046
21381
|
* - 'keywords' (string-list) \u2192 exposed as ctx.settings.keywords
|
|
21047
21382
|
*
|
|
21048
21383
|
* See: spec/plugin-author-guide.md \xA7View contributions
|
|
21049
21384
|
* spec/view-slots.md
|
|
21050
21385
|
*/
|
|
21051
21386
|
export default {
|
|
21052
|
-
id: '${extractorId}',
|
|
21053
|
-
kind: 'extractor',
|
|
21054
21387
|
version: '0.1.0',
|
|
21055
21388
|
description: 'Counts configured keywords per node.',
|
|
21056
|
-
stability: 'experimental',
|
|
21057
|
-
emitsLinkKinds: [],
|
|
21058
|
-
defaultConfidence: 'high',
|
|
21059
21389
|
scope: 'body',
|
|
21060
21390
|
|
|
21061
|
-
|
|
21391
|
+
settings: {
|
|
21392
|
+
keywords: {
|
|
21393
|
+
type: 'string-list',
|
|
21394
|
+
label: 'Keywords to track',
|
|
21395
|
+
description: 'Words counted across each scanned node body.',
|
|
21396
|
+
default: ['TODO', 'FIXME'],
|
|
21397
|
+
min: 1,
|
|
21398
|
+
},
|
|
21399
|
+
},
|
|
21400
|
+
|
|
21401
|
+
ui: {
|
|
21062
21402
|
count: {
|
|
21063
21403
|
slot: 'card.footer.left',
|
|
21064
21404
|
icon: '\u{1F50D}',
|
|
@@ -21084,7 +21424,7 @@ export default {
|
|
|
21084
21424
|
function scaffolderReadme(pluginId) {
|
|
21085
21425
|
return `# ${pluginId}
|
|
21086
21426
|
|
|
21087
|
-
Generated by \`sm plugins create\`. Edit \`
|
|
21427
|
+
Generated by \`sm plugins create\`. Edit \`extractors/${pluginId}-extractor/index.js\` to taste.
|
|
21088
21428
|
|
|
21089
21429
|
## Verbs
|
|
21090
21430
|
|
|
@@ -21604,6 +21944,31 @@ var IntentionalFailCommand = class extends SmCommand {
|
|
|
21604
21944
|
// cli/commands/scan.ts
|
|
21605
21945
|
import { Command as Command31, Option as Option29 } from "clipanion";
|
|
21606
21946
|
|
|
21947
|
+
// kernel/util/format-bytes.ts
|
|
21948
|
+
var UNITS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
|
|
21949
|
+
function formatBytes(bytes) {
|
|
21950
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
|
|
21951
|
+
if (bytes < 1024) return `${Math.round(bytes)} B`;
|
|
21952
|
+
let value = bytes;
|
|
21953
|
+
let unitIndex = 0;
|
|
21954
|
+
while (value >= 1024 && unitIndex < UNITS.length - 1) {
|
|
21955
|
+
value /= 1024;
|
|
21956
|
+
unitIndex += 1;
|
|
21957
|
+
}
|
|
21958
|
+
const rounded = Math.round(value * 10) / 10;
|
|
21959
|
+
const text = Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
21960
|
+
return `${text} ${UNITS[unitIndex]}`;
|
|
21961
|
+
}
|
|
21962
|
+
|
|
21963
|
+
// kernel/util/format-oversized.ts
|
|
21964
|
+
function formatOversizedFilePair(file) {
|
|
21965
|
+
return `${file.path} (${formatBytes(file.bytes)})`;
|
|
21966
|
+
}
|
|
21967
|
+
function formatOversizedFileRows(files) {
|
|
21968
|
+
return files.map((file) => ` - ${formatOversizedFilePair(file)}
|
|
21969
|
+
`);
|
|
21970
|
+
}
|
|
21971
|
+
|
|
21607
21972
|
// cli/i18n/scan.texts.ts
|
|
21608
21973
|
var SCAN_TEXTS = {
|
|
21609
21974
|
// --- scan command ----------------------------------------------------
|
|
@@ -21651,6 +22016,20 @@ var SCAN_TEXTS = {
|
|
|
21651
22016
|
*/
|
|
21652
22017
|
scanCappedNotice: "{{glyph}} Scan capped at {{limit}} nodes ({{source}}).\n {{hint}}\n",
|
|
21653
22018
|
scanCappedNoticeHint: "Trim .skillmapignore to exclude noisy paths (preferred), or re-run with --max-nodes <N> to raise the cap. Past the recommended limit the graph is hard to read and analyzer signal drops.",
|
|
22019
|
+
/**
|
|
22020
|
+
* File-size skip notice, printed (WARN, stderr) when the walker
|
|
22021
|
+
* skipped one or more files for exceeding `scan.maxFileSizeBytes`.
|
|
22022
|
+
* `{{glyph}}` is the yellow warning glyph, `{{count}}`/`{{noun}}` the
|
|
22023
|
+
* skipped-file tally, `{{files}}` the pre-rendered list of
|
|
22024
|
+
* `path (size)` rows, `{{hint}}` the dim escape-route line.
|
|
22025
|
+
*/
|
|
22026
|
+
scanSkippedFilesNotice: "{{glyph}} Skipped {{count}} {{noun}} over the size limit (scan.maxFileSizeBytes):\n{{files}} {{hint}}\n",
|
|
22027
|
+
// The per-file ` - path (size)\n` rows that fill `{{files}}` are
|
|
22028
|
+
// rendered by `kernel/util/format-oversized.ts:formatOversizedFileRows`,
|
|
22029
|
+
// shared with `sm watch` / `sm serve` so the three surfaces never drift.
|
|
22030
|
+
scanSkippedFileNounSingular: "file",
|
|
22031
|
+
scanSkippedFileNounPlural: "files",
|
|
22032
|
+
scanSkippedFilesNoticeHint: "Raise scan.maxFileSizeBytes to include these, or add them to .skillmapignore to skip them on purpose.",
|
|
21654
22033
|
/**
|
|
21655
22034
|
* Validation message for an invalid `--max-nodes` value. Surfaced as a
|
|
21656
22035
|
* §3.1b two-line block.
|
|
@@ -21825,11 +22204,13 @@ function createWatcherRuntime(opts) {
|
|
|
21825
22204
|
const runOptions = {
|
|
21826
22205
|
roots: opts.roots,
|
|
21827
22206
|
tokenize,
|
|
22207
|
+
tokenizer: cfg.tokenizer,
|
|
21828
22208
|
ignoreFilter,
|
|
21829
22209
|
strict,
|
|
21830
22210
|
emitter,
|
|
21831
22211
|
recommendedNodeLimit: cfg.scan.maxNodes,
|
|
21832
|
-
overrideMaxNodes: opts.maxNodesOverride ?? null
|
|
22212
|
+
overrideMaxNodes: opts.maxNodesOverride ?? null,
|
|
22213
|
+
maxFileSizeBytes: cfg.scan.maxFileSizeBytes
|
|
21833
22214
|
};
|
|
21834
22215
|
if (cfg.scan.referencePaths.length > 0) {
|
|
21835
22216
|
const walk3 = walkReferencePaths(cfg.scan.referencePaths, cwd);
|
|
@@ -22047,7 +22428,20 @@ var WATCH_TEXTS = {
|
|
|
22047
22428
|
* §3.1b two-line block. Validation rejection for `--max-nodes`.
|
|
22048
22429
|
*/
|
|
22049
22430
|
maxNodesInvalid: "{{glyph}} sm watch: --max-nodes must be an integer >= 1 (got {{raw}}).\n {{hint}}\n",
|
|
22050
|
-
maxNodesInvalidHint: "Pass a positive integer, e.g. --max-nodes 256."
|
|
22431
|
+
maxNodesInvalidHint: "Pass a positive integer, e.g. --max-nodes 256.",
|
|
22432
|
+
/**
|
|
22433
|
+
* File-size skip WARN, emitted per batch (stderr) when the walker
|
|
22434
|
+
* skipped one or more files for exceeding `scan.maxFileSizeBytes`.
|
|
22435
|
+
* Mirrors `sm scan`'s notice. `{{files}}` is the pre-rendered list of
|
|
22436
|
+
* `path (size)` rows.
|
|
22437
|
+
*/
|
|
22438
|
+
skippedFilesNotice: "{{glyph}} Skipped {{count}} {{noun}} over the size limit (scan.maxFileSizeBytes):\n{{files}} {{hint}}\n",
|
|
22439
|
+
// The per-file ` - path (size)\n` rows that fill `{{files}}` are
|
|
22440
|
+
// rendered by `kernel/util/format-oversized.ts:formatOversizedFileRows`,
|
|
22441
|
+
// shared with `sm scan` / `sm serve` so the three surfaces never drift.
|
|
22442
|
+
skippedFileNounSingular: "file",
|
|
22443
|
+
skippedFileNounPlural: "files",
|
|
22444
|
+
skippedFilesNoticeHint: "Raise scan.maxFileSizeBytes to include these, or add them to .skillmapignore to skip them on purpose."
|
|
22051
22445
|
};
|
|
22052
22446
|
|
|
22053
22447
|
// cli/commands/watch.ts
|
|
@@ -22088,6 +22482,21 @@ async function runWatchLoop(opts) {
|
|
|
22088
22482
|
})
|
|
22089
22483
|
);
|
|
22090
22484
|
}
|
|
22485
|
+
renderOversizedWarning(result);
|
|
22486
|
+
};
|
|
22487
|
+
const renderOversizedWarning = (result) => {
|
|
22488
|
+
const oversized = result.oversizedFiles ?? [];
|
|
22489
|
+
if ((result.stats.filesOversized ?? oversized.length) <= 0) return;
|
|
22490
|
+
const files = formatOversizedFileRows(oversized).join("");
|
|
22491
|
+
context.stderr.write(
|
|
22492
|
+
tx(WATCH_TEXTS.skippedFilesNotice, {
|
|
22493
|
+
glyph: stderrAnsi.yellow("\u26A0"),
|
|
22494
|
+
count: oversized.length,
|
|
22495
|
+
noun: oversized.length === 1 ? WATCH_TEXTS.skippedFileNounSingular : WATCH_TEXTS.skippedFileNounPlural,
|
|
22496
|
+
files,
|
|
22497
|
+
hint: stderrAnsi.dim(WATCH_TEXTS.skippedFilesNoticeHint)
|
|
22498
|
+
})
|
|
22499
|
+
);
|
|
22091
22500
|
};
|
|
22092
22501
|
const runtimeOpts = {
|
|
22093
22502
|
dbPath,
|
|
@@ -22414,7 +22823,13 @@ var ScanCommand = class extends SmCommand {
|
|
|
22414
22823
|
});
|
|
22415
22824
|
if (outcome.kind === "ok") {
|
|
22416
22825
|
setScanExtensions(buildScanExtensionSet(outcome.executedExtensionIds));
|
|
22417
|
-
return this.renderOutcome(
|
|
22826
|
+
return this.renderOutcome(
|
|
22827
|
+
outcome.result,
|
|
22828
|
+
outcome.persistedTo,
|
|
22829
|
+
outcome.dbPath,
|
|
22830
|
+
outcome.strict,
|
|
22831
|
+
outcome.lensAutoDetected
|
|
22832
|
+
);
|
|
22418
22833
|
}
|
|
22419
22834
|
return this.renderFailure(outcome);
|
|
22420
22835
|
}
|
|
@@ -22523,12 +22938,13 @@ var ScanCommand = class extends SmCommand {
|
|
|
22523
22938
|
* the exit code. Exit 1 only when at least one issue is at `error`
|
|
22524
22939
|
* severity (mirrors `sm check`, per spec § Exit codes).
|
|
22525
22940
|
*/
|
|
22526
|
-
renderOutcome(result, persistedTo, dbPath, strict) {
|
|
22941
|
+
renderOutcome(result, persistedTo, dbPath, strict, lensAutoDetected) {
|
|
22527
22942
|
const exitCode2 = result.issues.some((i) => i.severity === "error") ? ExitCode.Issues : ExitCode.Ok;
|
|
22528
22943
|
if (this.json) {
|
|
22529
22944
|
return this.#renderJsonOutcome(result, exitCode2, strict);
|
|
22530
22945
|
}
|
|
22531
22946
|
const ansi = this.ansiFor("stdout");
|
|
22947
|
+
this.#announceAutoDetectedLens(lensAutoDetected);
|
|
22532
22948
|
const cwd = defaultRuntimeContext().cwd;
|
|
22533
22949
|
const hasErrors = exitCode2 === ExitCode.Issues;
|
|
22534
22950
|
const severityCounts = countBySeverity(result.issues);
|
|
@@ -22558,8 +22974,32 @@ var ScanCommand = class extends SmCommand {
|
|
|
22558
22974
|
);
|
|
22559
22975
|
}
|
|
22560
22976
|
this.maybePrintCapNotice(result, ansi);
|
|
22977
|
+
this.maybePrintSkippedFilesNotice(result, ansi);
|
|
22561
22978
|
return exitCode2;
|
|
22562
22979
|
}
|
|
22980
|
+
/**
|
|
22981
|
+
* Surface a WARN when the walker skipped one or more files for
|
|
22982
|
+
* exceeding `scan.maxFileSizeBytes`. Lists every skipped file as
|
|
22983
|
+
* `path (humanSize)` and points the user at the two levers
|
|
22984
|
+
* (`scan.maxFileSizeBytes` to raise the limit, `.skillmapignore` to
|
|
22985
|
+
* exclude the path). Routed through `printer.warn` (stderr) because a
|
|
22986
|
+
* silently dropped file is degraded state the operator should read,
|
|
22987
|
+
* not a mid-flight progress line.
|
|
22988
|
+
*/
|
|
22989
|
+
maybePrintSkippedFilesNotice(result, ansi) {
|
|
22990
|
+
const oversized = result.oversizedFiles ?? [];
|
|
22991
|
+
if ((result.stats.filesOversized ?? oversized.length) <= 0) return;
|
|
22992
|
+
const lines = formatOversizedFileRows(oversized).join("");
|
|
22993
|
+
this.printer.warn(
|
|
22994
|
+
tx(SCAN_TEXTS.scanSkippedFilesNotice, {
|
|
22995
|
+
glyph: ansi.yellow("\u26A0"),
|
|
22996
|
+
count: String(oversized.length),
|
|
22997
|
+
noun: oversized.length === 1 ? SCAN_TEXTS.scanSkippedFileNounSingular : SCAN_TEXTS.scanSkippedFileNounPlural,
|
|
22998
|
+
files: lines,
|
|
22999
|
+
hint: ansi.dim(SCAN_TEXTS.scanSkippedFilesNoticeHint)
|
|
23000
|
+
})
|
|
23001
|
+
);
|
|
23002
|
+
}
|
|
22563
23003
|
/**
|
|
22564
23004
|
* Surface the §Node cap notice when the walker actually stopped
|
|
22565
23005
|
* accepting files because of the cap. Derivation: `filesWalked >
|
|
@@ -22583,6 +23023,21 @@ var ScanCommand = class extends SmCommand {
|
|
|
22583
23023
|
})
|
|
22584
23024
|
);
|
|
22585
23025
|
}
|
|
23026
|
+
/**
|
|
23027
|
+
* Print the lens auto-detect line on stdout (the SAME stream as the
|
|
23028
|
+
* scan summary) so the two never interleave on a tty. The bootstrap
|
|
23029
|
+
* deliberately no longer prints this to stderr; the runner threads
|
|
23030
|
+
* `lensAutoDetected` through so the CLI announces it here, in order,
|
|
23031
|
+
* right before the summary. The text ends in a newline, so the
|
|
23032
|
+
* summary lands cleanly on its own line. No-op when the lens came
|
|
23033
|
+
* from config or no marker matched (`null` / `undefined`).
|
|
23034
|
+
*/
|
|
23035
|
+
#announceAutoDetectedLens(lensAutoDetected) {
|
|
23036
|
+
if (!lensAutoDetected) return;
|
|
23037
|
+
this.printer.data(
|
|
23038
|
+
tx(SCAN_RUNNER_TEXTS.activeProviderAutodetected, { id: lensAutoDetected })
|
|
23039
|
+
);
|
|
23040
|
+
}
|
|
22586
23041
|
/**
|
|
22587
23042
|
* `--json` output path. Under `--strict` (H4) self-validates the
|
|
22588
23043
|
* ScanResult against `scan-result.schema.json` before emitting it,
|
|
@@ -22900,7 +23355,7 @@ function renderDeltaIssues(issues) {
|
|
|
22900
23355
|
|
|
22901
23356
|
// cli/commands/serve.ts
|
|
22902
23357
|
import { spawn as spawn2 } from "child_process";
|
|
22903
|
-
import { existsSync as
|
|
23358
|
+
import { existsSync as existsSync31 } from "fs";
|
|
22904
23359
|
import { Command as Command33, Option as Option31 } from "clipanion";
|
|
22905
23360
|
|
|
22906
23361
|
// kernel/util/dev-mode.ts
|
|
@@ -23055,6 +23510,12 @@ var SERVER_TEXTS = {
|
|
|
23055
23510
|
// watcher loop continues, a transient FS error must not kill the
|
|
23056
23511
|
// broadcaster.
|
|
23057
23512
|
watcherBatchFailed: "skill-map server: watcher batch failed ({{message}}).\n",
|
|
23513
|
+
// Logged on the server pane when a scan batch (initial or follow-up)
|
|
23514
|
+
// skipped one or more files for exceeding `scan.maxFileSizeBytes`.
|
|
23515
|
+
// `{{files}}` is the comma-separated `path (size)` list. The SPA
|
|
23516
|
+
// raises its own banner from the persisted `oversizedFiles`, so this
|
|
23517
|
+
// is log-only (no WS advisory).
|
|
23518
|
+
watcherFilesOversized: "skill-map server: skipped {{count}} file(s) over the size limit (scan.maxFileSizeBytes): {{files}}. Raise the limit or add them to .skillmapignore.\n",
|
|
23058
23519
|
// Logged once when the pre-1.0 schema-drift check rebuilt the DB on
|
|
23059
23520
|
// watcher boot (the on-disk cache was written by a different
|
|
23060
23521
|
// major.minor). The scan that follows repopulates it; .sm sidecars
|
|
@@ -23140,19 +23601,19 @@ var SERVER_TEXTS = {
|
|
|
23140
23601
|
// 400, cascade route rejects qualified ids: the bare-id PATCH is the
|
|
23141
23602
|
// bundle macro endpoint. Anything containing `/` needs the dedicated
|
|
23142
23603
|
// per-extension route below.
|
|
23143
|
-
pluginsCascadeRouteQualifiedRejected: 'Plugin id "{{id}}" contains "/"; toggle individual extensions via PATCH /api/plugins/<
|
|
23604
|
+
pluginsCascadeRouteQualifiedRejected: 'Plugin id "{{id}}" contains "/"; toggle individual extensions via PATCH /api/plugins/<plugin>/extensions/<extensionId>.',
|
|
23144
23605
|
// 404, unknown plugin / extension.
|
|
23145
23606
|
pluginsUnknown: 'No plugin with id "{{id}}".',
|
|
23146
|
-
pluginsExtensionUnknown: 'Plugin "{{
|
|
23607
|
+
pluginsExtensionUnknown: 'Plugin "{{pluginId}}" has no extension named "{{extensionId}}".',
|
|
23147
23608
|
// 500, DB missing on a write path. Read paths degrade to empty
|
|
23148
23609
|
// shapes, but mutations cannot persist without a DB so they fail fast.
|
|
23149
23610
|
pluginsDbMissing: "Cannot persist plugin override: project DB not found at {{path}}. Run `sm scan` first or pass --db <path>.",
|
|
23150
23611
|
// 403, host-enforced lock from `src/server/locked-plugins.ts`. The
|
|
23151
|
-
//
|
|
23612
|
+
// plugin (or qualified extension) is in the hardcoded lock-list and
|
|
23152
23613
|
// its enabled state is fixed; the UI mirrors the same rule by
|
|
23153
23614
|
// disabling the toggle.
|
|
23154
23615
|
pluginsLocked: 'Plugin "{{id}}" is locked by the host and cannot be toggled.',
|
|
23155
|
-
pluginsExtensionLocked: 'Extension "{{
|
|
23616
|
+
pluginsExtensionLocked: 'Extension "{{pluginId}}/{{extensionId}}" is locked by the host and cannot be toggled.',
|
|
23156
23617
|
// 400 envelopes specific to the bulk `PATCH /api/plugins` endpoint.
|
|
23157
23618
|
// The single-id variants above still apply for per-entry validation
|
|
23158
23619
|
// (unknown id, granularity mismatch, lock); these cover the
|
|
@@ -23691,7 +24152,7 @@ function contentTypeFor(format) {
|
|
|
23691
24152
|
}
|
|
23692
24153
|
|
|
23693
24154
|
// server/health.ts
|
|
23694
|
-
import { existsSync as
|
|
24155
|
+
import { existsSync as existsSync26 } from "fs";
|
|
23695
24156
|
var FALLBACK_SCHEMA_VERSION = "1";
|
|
23696
24157
|
function buildHealth(deps) {
|
|
23697
24158
|
const dev = isDevBuild();
|
|
@@ -23700,7 +24161,7 @@ function buildHealth(deps) {
|
|
|
23700
24161
|
schemaVersion: FALLBACK_SCHEMA_VERSION,
|
|
23701
24162
|
specVersion: deps.specVersion,
|
|
23702
24163
|
implVersion: VERSION,
|
|
23703
|
-
db:
|
|
24164
|
+
db: existsSync26(deps.dbPath) ? "present" : "missing",
|
|
23704
24165
|
cwd: deps.cwd,
|
|
23705
24166
|
dbPath: deps.dbPath,
|
|
23706
24167
|
// Only emit when truthy so a published install keeps the wire
|
|
@@ -24219,28 +24680,28 @@ function registerPluginsRoute(app, deps) {
|
|
|
24219
24680
|
});
|
|
24220
24681
|
}
|
|
24221
24682
|
const body = await parsePatchBody(c.req.raw);
|
|
24222
|
-
const childIds =
|
|
24683
|
+
const childIds = pluginExtensionIds(handle).map((extId) => qualifiedExtensionId(id, extId));
|
|
24223
24684
|
const writable = childIds.filter((q) => !isPluginLocked(q));
|
|
24224
24685
|
return await persistManyAndProject(c, deps, writable, body.enabled);
|
|
24225
24686
|
});
|
|
24226
|
-
app.patch("/api/plugins/:
|
|
24227
|
-
const
|
|
24687
|
+
app.patch("/api/plugins/:pluginId/extensions/:extensionId", async (c) => {
|
|
24688
|
+
const pluginId = c.req.param("pluginId");
|
|
24228
24689
|
const extensionId = c.req.param("extensionId");
|
|
24229
|
-
const handle = findHandle(
|
|
24690
|
+
const handle = findHandle(pluginId, deps);
|
|
24230
24691
|
if (!handle) {
|
|
24231
24692
|
throw new HTTPException9(404, {
|
|
24232
|
-
message: tx(SERVER_TEXTS.pluginsUnknown, { id:
|
|
24693
|
+
message: tx(SERVER_TEXTS.pluginsUnknown, { id: pluginId })
|
|
24233
24694
|
});
|
|
24234
24695
|
}
|
|
24235
24696
|
if (!hasExtension(handle, extensionId)) {
|
|
24236
24697
|
throw new HTTPException9(404, {
|
|
24237
|
-
message: tx(SERVER_TEXTS.pluginsExtensionUnknown, {
|
|
24698
|
+
message: tx(SERVER_TEXTS.pluginsExtensionUnknown, { pluginId, extensionId })
|
|
24238
24699
|
});
|
|
24239
24700
|
}
|
|
24240
|
-
const qualified = qualifiedExtensionId(
|
|
24241
|
-
if (isPluginLocked(qualified) || isPluginLocked(
|
|
24701
|
+
const qualified = qualifiedExtensionId(pluginId, extensionId);
|
|
24702
|
+
if (isPluginLocked(qualified) || isPluginLocked(pluginId)) {
|
|
24242
24703
|
throw new HTTPException9(403, {
|
|
24243
|
-
message: tx(SERVER_TEXTS.pluginsExtensionLocked, {
|
|
24704
|
+
message: tx(SERVER_TEXTS.pluginsExtensionLocked, { pluginId, extensionId })
|
|
24244
24705
|
});
|
|
24245
24706
|
}
|
|
24246
24707
|
const body = await parsePatchBody(c.req.raw);
|
|
@@ -24269,11 +24730,11 @@ function listItems(deps, resolveEnabled) {
|
|
|
24269
24730
|
];
|
|
24270
24731
|
}
|
|
24271
24732
|
function buildBuiltInItems(resolveEnabled) {
|
|
24272
|
-
return
|
|
24273
|
-
const
|
|
24274
|
-
const extensions =
|
|
24275
|
-
const qualified = qualifiedExtensionId(
|
|
24276
|
-
const extLocked =
|
|
24733
|
+
return sortPluginsForPresentation(builtInPlugins).map((plugin) => {
|
|
24734
|
+
const pluginLocked = isPluginLocked(plugin.id);
|
|
24735
|
+
const extensions = plugin.extensions.map((ext) => {
|
|
24736
|
+
const qualified = qualifiedExtensionId(plugin.id, ext.id);
|
|
24737
|
+
const extLocked = pluginLocked || isPluginLocked(qualified);
|
|
24277
24738
|
return {
|
|
24278
24739
|
id: ext.id,
|
|
24279
24740
|
kind: ext.kind,
|
|
@@ -24283,17 +24744,17 @@ function buildBuiltInItems(resolveEnabled) {
|
|
|
24283
24744
|
...extLocked ? { locked: true } : {}
|
|
24284
24745
|
};
|
|
24285
24746
|
});
|
|
24286
|
-
const
|
|
24747
|
+
const pluginEnabled = extensions.some((e) => e.enabled);
|
|
24287
24748
|
return {
|
|
24288
|
-
id:
|
|
24289
|
-
version: firstVersion(
|
|
24290
|
-
kinds: uniqueKinds(
|
|
24291
|
-
status:
|
|
24749
|
+
id: plugin.id,
|
|
24750
|
+
version: firstVersion(plugin.extensions),
|
|
24751
|
+
kinds: uniqueKinds(plugin.extensions.map((e) => e.kind)),
|
|
24752
|
+
status: pluginEnabled ? "enabled" : "disabled",
|
|
24292
24753
|
reason: null,
|
|
24293
24754
|
source: "built-in",
|
|
24294
|
-
description:
|
|
24755
|
+
description: plugin.description,
|
|
24295
24756
|
...extensions.length > 0 ? { extensions } : {},
|
|
24296
|
-
...
|
|
24757
|
+
...pluginLocked ? { locked: true } : {}
|
|
24297
24758
|
};
|
|
24298
24759
|
});
|
|
24299
24760
|
}
|
|
@@ -24301,8 +24762,8 @@ function buildDiscoveredItems(discovered, deps, resolveEnabled) {
|
|
|
24301
24762
|
return discovered.map((plugin) => buildDiscoveredItem(plugin, deps, resolveEnabled));
|
|
24302
24763
|
}
|
|
24303
24764
|
function buildDiscoveredItem(plugin, deps, resolveEnabled) {
|
|
24304
|
-
const
|
|
24305
|
-
const extensions = projectExtensionRows(plugin, resolveEnabled,
|
|
24765
|
+
const pluginLocked = isPluginLocked(plugin.id);
|
|
24766
|
+
const extensions = projectExtensionRows(plugin, resolveEnabled, pluginLocked);
|
|
24306
24767
|
const optional = optionalDiscoveredFields(plugin, extensions);
|
|
24307
24768
|
return {
|
|
24308
24769
|
id: plugin.id,
|
|
@@ -24312,7 +24773,7 @@ function buildDiscoveredItem(plugin, deps, resolveEnabled) {
|
|
|
24312
24773
|
reason: plugin.reason ?? null,
|
|
24313
24774
|
source: classifyPluginSource(plugin.path, deps),
|
|
24314
24775
|
...optional,
|
|
24315
|
-
...
|
|
24776
|
+
...pluginLocked ? { locked: true } : {},
|
|
24316
24777
|
...plugin.status === "disabled" ? { startsAsDisabled: true } : {}
|
|
24317
24778
|
};
|
|
24318
24779
|
}
|
|
@@ -24323,12 +24784,12 @@ function optionalDiscoveredFields(plugin, extensions) {
|
|
|
24323
24784
|
if (extensions) out.extensions = extensions;
|
|
24324
24785
|
return out;
|
|
24325
24786
|
}
|
|
24326
|
-
function projectExtensionRows(plugin, resolveEnabled,
|
|
24787
|
+
function projectExtensionRows(plugin, resolveEnabled, pluginLocked) {
|
|
24327
24788
|
if (!plugin.extensions || plugin.extensions.length === 0) return void 0;
|
|
24328
24789
|
return plugin.extensions.map((ext) => {
|
|
24329
24790
|
const description = readInstanceDescription(ext.instance);
|
|
24330
24791
|
const qualified = qualifiedExtensionId(plugin.id, ext.id);
|
|
24331
|
-
const extLocked =
|
|
24792
|
+
const extLocked = pluginLocked || isPluginLocked(qualified);
|
|
24332
24793
|
return {
|
|
24333
24794
|
id: ext.id,
|
|
24334
24795
|
kind: ext.kind,
|
|
@@ -24437,28 +24898,28 @@ function validateBulkChange(change, deps) {
|
|
|
24437
24898
|
}
|
|
24438
24899
|
return null;
|
|
24439
24900
|
}
|
|
24440
|
-
const
|
|
24901
|
+
const pluginId = change.id.slice(0, slash);
|
|
24441
24902
|
const extensionId = change.id.slice(slash + 1);
|
|
24442
|
-
const handle = findHandle(
|
|
24903
|
+
const handle = findHandle(pluginId, deps);
|
|
24443
24904
|
if (!handle) {
|
|
24444
24905
|
return {
|
|
24445
24906
|
status: 404,
|
|
24446
24907
|
code: "not-found",
|
|
24447
|
-
message: tx(SERVER_TEXTS.pluginsUnknown, { id:
|
|
24908
|
+
message: tx(SERVER_TEXTS.pluginsUnknown, { id: pluginId })
|
|
24448
24909
|
};
|
|
24449
24910
|
}
|
|
24450
24911
|
if (!hasExtension(handle, extensionId)) {
|
|
24451
24912
|
return {
|
|
24452
24913
|
status: 404,
|
|
24453
24914
|
code: "not-found",
|
|
24454
|
-
message: tx(SERVER_TEXTS.pluginsExtensionUnknown, {
|
|
24915
|
+
message: tx(SERVER_TEXTS.pluginsExtensionUnknown, { pluginId, extensionId })
|
|
24455
24916
|
};
|
|
24456
24917
|
}
|
|
24457
|
-
if (isPluginLocked(change.id) || isPluginLocked(
|
|
24918
|
+
if (isPluginLocked(change.id) || isPluginLocked(pluginId)) {
|
|
24458
24919
|
return {
|
|
24459
24920
|
status: 403,
|
|
24460
24921
|
code: "locked",
|
|
24461
|
-
message: tx(SERVER_TEXTS.pluginsExtensionLocked, {
|
|
24922
|
+
message: tx(SERVER_TEXTS.pluginsExtensionLocked, { pluginId, extensionId })
|
|
24462
24923
|
};
|
|
24463
24924
|
}
|
|
24464
24925
|
return null;
|
|
@@ -24482,7 +24943,7 @@ function expandBulkChangeKeys(change, deps) {
|
|
|
24482
24943
|
if (change.id.includes("/")) return [change.id];
|
|
24483
24944
|
const handle = findHandle(change.id, deps);
|
|
24484
24945
|
if (!handle) return [];
|
|
24485
|
-
return
|
|
24946
|
+
return pluginExtensionIds(handle).map((extId) => qualifiedExtensionId(change.id, extId)).filter((q) => !isPluginLocked(q));
|
|
24486
24947
|
}
|
|
24487
24948
|
async function buildFreshResolver2(deps) {
|
|
24488
24949
|
return buildFreshResolver({
|
|
@@ -24495,21 +24956,21 @@ function composeResolver2(deps, overrides) {
|
|
|
24495
24956
|
return composeResolver(deps.configService.effective(), overrides);
|
|
24496
24957
|
}
|
|
24497
24958
|
function findHandle(id, deps) {
|
|
24498
|
-
const builtIn =
|
|
24499
|
-
if (builtIn) return { kind: "built-in",
|
|
24959
|
+
const builtIn = builtInPlugins.find((b) => b.id === id);
|
|
24960
|
+
if (builtIn) return { kind: "built-in", plugin: builtIn };
|
|
24500
24961
|
const discovered = deps.pluginRuntime.discovered.find((p) => p.id === id);
|
|
24501
24962
|
if (discovered) return { kind: "discovered", plugin: discovered };
|
|
24502
24963
|
return null;
|
|
24503
24964
|
}
|
|
24504
|
-
function
|
|
24965
|
+
function pluginExtensionIds(handle) {
|
|
24505
24966
|
if (handle.kind === "built-in") {
|
|
24506
|
-
return handle.
|
|
24967
|
+
return handle.plugin.extensions.map((e) => e.id);
|
|
24507
24968
|
}
|
|
24508
24969
|
return (handle.plugin.extensions ?? []).map((e) => e.id);
|
|
24509
24970
|
}
|
|
24510
24971
|
function hasExtension(handle, extensionId) {
|
|
24511
24972
|
if (handle.kind === "built-in") {
|
|
24512
|
-
return handle.
|
|
24973
|
+
return handle.plugin.extensions.some((e) => e.id === extensionId);
|
|
24513
24974
|
}
|
|
24514
24975
|
return (handle.plugin.extensions ?? []).some((e) => e.id === extensionId);
|
|
24515
24976
|
}
|
|
@@ -24609,15 +25070,15 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
|
|
|
24609
25070
|
import { HTTPException as HTTPException11 } from "hono/http-exception";
|
|
24610
25071
|
|
|
24611
25072
|
// server/util/skillmapignore-io.ts
|
|
24612
|
-
import { existsSync as
|
|
25073
|
+
import { existsSync as existsSync27, readFileSync as readFileSync18, writeFileSync as writeFileSync2 } from "fs";
|
|
24613
25074
|
import { resolve as resolve35 } from "path";
|
|
24614
25075
|
var IGNORE_FILENAME2 = ".skillmapignore";
|
|
24615
25076
|
function readPatterns(cwd) {
|
|
24616
25077
|
const path = resolve35(cwd, IGNORE_FILENAME2);
|
|
24617
|
-
if (!
|
|
25078
|
+
if (!existsSync27(path)) return [];
|
|
24618
25079
|
let raw;
|
|
24619
25080
|
try {
|
|
24620
|
-
raw =
|
|
25081
|
+
raw = readFileSync18(path, "utf8");
|
|
24621
25082
|
} catch {
|
|
24622
25083
|
return [];
|
|
24623
25084
|
}
|
|
@@ -24625,13 +25086,13 @@ function readPatterns(cwd) {
|
|
|
24625
25086
|
}
|
|
24626
25087
|
function writePatterns(cwd, nextPatterns) {
|
|
24627
25088
|
const path = resolve35(cwd, IGNORE_FILENAME2);
|
|
24628
|
-
const prior =
|
|
25089
|
+
const prior = existsSync27(path) ? safeRead(path) : "";
|
|
24629
25090
|
const content = buildContent(prior, nextPatterns);
|
|
24630
25091
|
writeFileSync2(path, content, "utf8");
|
|
24631
25092
|
}
|
|
24632
25093
|
function safeRead(path) {
|
|
24633
25094
|
try {
|
|
24634
|
-
return
|
|
25095
|
+
return readFileSync18(path, "utf8");
|
|
24635
25096
|
} catch {
|
|
24636
25097
|
return "";
|
|
24637
25098
|
}
|
|
@@ -24975,7 +25436,7 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
|
|
|
24975
25436
|
});
|
|
24976
25437
|
|
|
24977
25438
|
// server/routes/active-provider.ts
|
|
24978
|
-
import { existsSync as
|
|
25439
|
+
import { existsSync as existsSync28 } from "fs";
|
|
24979
25440
|
import { HTTPException as HTTPException13 } from "hono/http-exception";
|
|
24980
25441
|
function registerActiveProviderRoute(app, deps) {
|
|
24981
25442
|
app.get("/api/active-provider", (c) => {
|
|
@@ -25008,7 +25469,7 @@ function applyLensSwitch(deps, newValue) {
|
|
|
25008
25469
|
});
|
|
25009
25470
|
}
|
|
25010
25471
|
const dbPath = resolveDbPath({ db: void 0, cwd });
|
|
25011
|
-
if (!
|
|
25472
|
+
if (!existsSync28(dbPath)) return { dropped: null };
|
|
25012
25473
|
const dropResult = dropScanZone(dbPath);
|
|
25013
25474
|
return {
|
|
25014
25475
|
dropped: {
|
|
@@ -25116,7 +25577,9 @@ function createWatcherService(opts) {
|
|
|
25116
25577
|
message: sanitizeForTerminal(outcome.message)
|
|
25117
25578
|
})
|
|
25118
25579
|
);
|
|
25580
|
+
return;
|
|
25119
25581
|
}
|
|
25582
|
+
warnOversizedFiles(outcome.result);
|
|
25120
25583
|
},
|
|
25121
25584
|
onWatcherError: (message) => {
|
|
25122
25585
|
log.warn(
|
|
@@ -25179,6 +25642,17 @@ function createWatcherService(opts) {
|
|
|
25179
25642
|
}
|
|
25180
25643
|
};
|
|
25181
25644
|
}
|
|
25645
|
+
function warnOversizedFiles(result) {
|
|
25646
|
+
const oversized = result.oversizedFiles ?? [];
|
|
25647
|
+
if ((result.stats.filesOversized ?? oversized.length) <= 0) return;
|
|
25648
|
+
const files = oversized.map((f) => formatOversizedFilePair({ path: sanitizeForTerminal(f.path), bytes: f.bytes })).join(", ");
|
|
25649
|
+
log.warn(
|
|
25650
|
+
tx(SERVER_TEXTS.watcherFilesOversized, {
|
|
25651
|
+
count: String(oversized.length),
|
|
25652
|
+
files
|
|
25653
|
+
})
|
|
25654
|
+
);
|
|
25655
|
+
}
|
|
25182
25656
|
function buildBroadcasterEmitter(broadcaster) {
|
|
25183
25657
|
return {
|
|
25184
25658
|
emit(event) {
|
|
@@ -25262,7 +25736,16 @@ async function buildBffResolverOverride(deps) {
|
|
|
25262
25736
|
}
|
|
25263
25737
|
async function loadPersistedScan(deps) {
|
|
25264
25738
|
const opened = await tryWithSqlite(
|
|
25265
|
-
{
|
|
25739
|
+
{
|
|
25740
|
+
databasePath: deps.options.dbPath,
|
|
25741
|
+
autoBackup: false,
|
|
25742
|
+
// Read-side drift advisory (version skew + schema fingerprint).
|
|
25743
|
+
// The BFF has no TTY, warnings go to the server log; a newer /
|
|
25744
|
+
// different-major DB throws `DbVersionMismatchError`, which the
|
|
25745
|
+
// global `app.onError` maps to a 500 so the SPA surfaces it
|
|
25746
|
+
// rather than crashing on a cryptic missing-column read.
|
|
25747
|
+
versionCheck: { currentVersion: VERSION, printer: bffVersionCheckPrinter }
|
|
25748
|
+
},
|
|
25266
25749
|
async (adapter) => {
|
|
25267
25750
|
const [loaded, favSet] = await Promise.all([
|
|
25268
25751
|
adapter.scans.load(),
|
|
@@ -25357,6 +25840,13 @@ var bffScanRunnerPrinter = {
|
|
|
25357
25840
|
warn: (text) => log.warn(sanitizeForTerminal(text.trimEnd())),
|
|
25358
25841
|
error: (text) => log.warn(sanitizeForTerminal(text.trimEnd()))
|
|
25359
25842
|
};
|
|
25843
|
+
var bffVersionCheckPrinter = {
|
|
25844
|
+
data: () => {
|
|
25845
|
+
},
|
|
25846
|
+
info: (text) => log.warn(sanitizeForTerminal(text.trimEnd())),
|
|
25847
|
+
warn: (text) => log.warn(sanitizeForTerminal(text.trimEnd())),
|
|
25848
|
+
error: (text) => log.warn(sanitizeForTerminal(text.trimEnd()))
|
|
25849
|
+
};
|
|
25360
25850
|
function emptyScanResult() {
|
|
25361
25851
|
return {
|
|
25362
25852
|
schemaVersion: 1,
|
|
@@ -25543,7 +26033,7 @@ function registerUpdateStatusRoute(app, deps) {
|
|
|
25543
26033
|
}
|
|
25544
26034
|
|
|
25545
26035
|
// server/static.ts
|
|
25546
|
-
import { existsSync as
|
|
26036
|
+
import { existsSync as existsSync29 } from "fs";
|
|
25547
26037
|
import { readFile as readFile6 } from "fs/promises";
|
|
25548
26038
|
import { extname, join as join19 } from "path";
|
|
25549
26039
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
@@ -25598,7 +26088,7 @@ function createSpaFallback(opts) {
|
|
|
25598
26088
|
if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
|
|
25599
26089
|
if (opts.uiDist === null) return htmlResponse(c, placeholder);
|
|
25600
26090
|
const indexPath = join19(opts.uiDist, INDEX_HTML);
|
|
25601
|
-
if (!
|
|
26091
|
+
if (!existsSync29(indexPath)) return htmlResponse(c, placeholder);
|
|
25602
26092
|
return fileResponse(c, indexPath);
|
|
25603
26093
|
};
|
|
25604
26094
|
}
|
|
@@ -26208,7 +26698,7 @@ function validateNoUi(noUi, uiDist) {
|
|
|
26208
26698
|
}
|
|
26209
26699
|
|
|
26210
26700
|
// server/paths.ts
|
|
26211
|
-
import { existsSync as
|
|
26701
|
+
import { existsSync as existsSync30, statSync as statSync10 } from "fs";
|
|
26212
26702
|
import { dirname as dirname18, isAbsolute as isAbsolute11, join as join20, resolve as resolve37 } from "path";
|
|
26213
26703
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
26214
26704
|
var DEFAULT_UI_REL = join20("ui", "dist", "ui", "browser");
|
|
@@ -26223,10 +26713,10 @@ function resolveExplicitUiDist(ctx, raw) {
|
|
|
26223
26713
|
return isAbsolute11(raw) ? raw : resolve37(ctx.cwd, raw);
|
|
26224
26714
|
}
|
|
26225
26715
|
function isUiBundleDir(path) {
|
|
26226
|
-
if (!
|
|
26716
|
+
if (!existsSync30(path)) return false;
|
|
26227
26717
|
try {
|
|
26228
26718
|
if (!statSync10(path).isDirectory()) return false;
|
|
26229
|
-
return
|
|
26719
|
+
return existsSync30(join20(path, INDEX_HTML2));
|
|
26230
26720
|
} catch {
|
|
26231
26721
|
return false;
|
|
26232
26722
|
}
|
|
@@ -26354,8 +26844,8 @@ function assembleKernel(pluginRuntime, noBuiltIns) {
|
|
|
26354
26844
|
(c) => `${c.pluginId}/${c.extensionId}/${c.contributionId}`
|
|
26355
26845
|
)
|
|
26356
26846
|
);
|
|
26357
|
-
for (const
|
|
26358
|
-
for (const ext of
|
|
26847
|
+
for (const plugin of builtInPlugins) {
|
|
26848
|
+
for (const ext of plugin.extensions) {
|
|
26359
26849
|
collectViewContributions(ext.pluginId, ext.id, ext, mergedViewContributions, {
|
|
26360
26850
|
excludeQualifiedIds: userKey
|
|
26361
26851
|
});
|
|
@@ -26368,8 +26858,8 @@ function assembleKernel(pluginRuntime, noBuiltIns) {
|
|
|
26368
26858
|
}
|
|
26369
26859
|
function collectBuiltInProviders() {
|
|
26370
26860
|
const out = [];
|
|
26371
|
-
for (const
|
|
26372
|
-
for (const ext of
|
|
26861
|
+
for (const plugin of builtInPlugins) {
|
|
26862
|
+
for (const ext of plugin.extensions) {
|
|
26373
26863
|
if (ext.kind === "provider") {
|
|
26374
26864
|
out.push(ext);
|
|
26375
26865
|
}
|
|
@@ -26520,7 +27010,14 @@ var SERVE_TEXTS = {
|
|
|
26520
27010
|
// Shutdown trace, printed once the listener has closed. Informational
|
|
26521
27011
|
// (`ℹ` cyan) per §3.1: no failure, no action; just a marker that the
|
|
26522
27012
|
// long-running daemon has wound down cleanly.
|
|
26523
|
-
shutdown: "{{glyph}} sm serve: shutdown complete.\n"
|
|
27013
|
+
shutdown: "{{glyph}} sm serve: shutdown complete.\n",
|
|
27014
|
+
/**
|
|
27015
|
+
* §3.1b error block when the operator declines the pre-boot
|
|
27016
|
+
* schema-drift rebuild (TTY, no `--yes`). The server never starts;
|
|
27017
|
+
* the cache is left untouched. `{{reason}}` names the drift axis
|
|
27018
|
+
* (version skew vs an inline schema change).
|
|
27019
|
+
*/
|
|
27020
|
+
driftDeclined: "{{glyph}} sm serve: cache rebuild declined; the {{dbVersion}} cache cannot be reused on {{currentVersion}} ({{reason}}).\n {{hint}}\n"
|
|
26524
27021
|
};
|
|
26525
27022
|
|
|
26526
27023
|
// cli/util/serve-banner.ts
|
|
@@ -26747,6 +27244,9 @@ var ServeCommand = class extends SmCommand {
|
|
|
26747
27244
|
noWatcher = Option31.Boolean("--no-watcher", false, {
|
|
26748
27245
|
description: "Disable the chokidar-fed scan-and-broadcast loop. Use only for CI / read-only deployments."
|
|
26749
27246
|
});
|
|
27247
|
+
yes = Option31.Boolean("--yes", false, {
|
|
27248
|
+
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."
|
|
27249
|
+
});
|
|
26750
27250
|
// `--watcher-debounce-ms` is undocumented sugar for advanced users
|
|
26751
27251
|
// who want to tighten / relax the watcher's batching window without
|
|
26752
27252
|
// editing settings.json. Hidden flag, the Usage block omits it.
|
|
@@ -26781,7 +27281,7 @@ var ServeCommand = class extends SmCommand {
|
|
|
26781
27281
|
return ExitCode.Error;
|
|
26782
27282
|
}
|
|
26783
27283
|
const dbPath = resolveDbPath({ db: this.db, ...runtimeCtx });
|
|
26784
|
-
if (this.db !== void 0 && !
|
|
27284
|
+
if (this.db !== void 0 && !existsSync31(dbPath)) {
|
|
26785
27285
|
this.printer.info(
|
|
26786
27286
|
tx(SERVE_TEXTS.dbNotFound, {
|
|
26787
27287
|
glyph: errGlyph,
|
|
@@ -26863,6 +27363,8 @@ var ServeCommand = class extends SmCommand {
|
|
|
26863
27363
|
this.printer.info(formatValidationError(validation.error, stderrAnsi));
|
|
26864
27364
|
return ExitCode.Error;
|
|
26865
27365
|
}
|
|
27366
|
+
const driftAbort = await this.#rebuildOnDrift(dbPath, stderrAnsi, warnGlyph);
|
|
27367
|
+
if (driftAbort !== null) return driftAbort;
|
|
26866
27368
|
await initSentryBff(VERSION);
|
|
26867
27369
|
let handle;
|
|
26868
27370
|
try {
|
|
@@ -26915,6 +27417,35 @@ var ServeCommand = class extends SmCommand {
|
|
|
26915
27417
|
this.printer.info(tx(SERVE_TEXTS.shutdown, { glyph: infoGlyph }));
|
|
26916
27418
|
return ExitCode.Ok;
|
|
26917
27419
|
}
|
|
27420
|
+
/**
|
|
27421
|
+
* Pre-1.0 schema-drift rebuild for `sm serve`, run before boot. Reuses
|
|
27422
|
+
* the shared `maybeResetOnDrift` (same prompt / `--yes` / non-TTY
|
|
27423
|
+
* policy as `sm scan`), threading the verb's stdin / stderr so a TTY
|
|
27424
|
+
* operator is asked y/N. Returns `null` to proceed (no drift, or the
|
|
27425
|
+
* cache was rebuilt) or an `ExitCode` to abort boot when the operator
|
|
27426
|
+
* declines the rebuild.
|
|
27427
|
+
*/
|
|
27428
|
+
async #rebuildOnDrift(dbPath, stderrAnsi, warnGlyph) {
|
|
27429
|
+
const outcome = await maybeResetOnDrift(dbPath, {
|
|
27430
|
+
currentVersion: VERSION,
|
|
27431
|
+
assumeYes: this.yes,
|
|
27432
|
+
stdin: this.context.stdin,
|
|
27433
|
+
stderr: this.context.stderr,
|
|
27434
|
+
printer: this.printer,
|
|
27435
|
+
style: { warnGlyph, dim: stderrAnsi.dim }
|
|
27436
|
+
});
|
|
27437
|
+
if (outcome.kind !== "aborted") return null;
|
|
27438
|
+
this.printer.error(
|
|
27439
|
+
tx(SERVE_TEXTS.driftDeclined, {
|
|
27440
|
+
glyph: stderrAnsi.red("\u2715"),
|
|
27441
|
+
dbVersion: outcome.dbVersion,
|
|
27442
|
+
currentVersion: outcome.currentVersion,
|
|
27443
|
+
reason: outcome.reason === "version" ? DB_DRIFT_TEXTS.driftReasonVersion : DB_DRIFT_TEXTS.driftReasonSchema,
|
|
27444
|
+
hint: stderrAnsi.dim(DB_DRIFT_TEXTS.driftAbortedHint)
|
|
27445
|
+
})
|
|
27446
|
+
);
|
|
27447
|
+
return ExitCode.Error;
|
|
27448
|
+
}
|
|
26918
27449
|
};
|
|
26919
27450
|
function parsePort(raw) {
|
|
26920
27451
|
if (raw === void 0) return { ok: true, port: void 0 };
|
|
@@ -27130,26 +27661,34 @@ var ShowCommand = class extends SmCommand {
|
|
|
27130
27661
|
const dbPath = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
|
|
27131
27662
|
const exit = requireDbOrExit(dbPath, this.context.stderr);
|
|
27132
27663
|
if (exit !== null) return exit;
|
|
27133
|
-
|
|
27134
|
-
|
|
27135
|
-
|
|
27136
|
-
|
|
27137
|
-
|
|
27138
|
-
|
|
27139
|
-
|
|
27140
|
-
|
|
27141
|
-
|
|
27142
|
-
|
|
27143
|
-
|
|
27144
|
-
|
|
27145
|
-
|
|
27146
|
-
|
|
27664
|
+
const stderrAnsi = this.ansiFor("stderr");
|
|
27665
|
+
return withSqlite(
|
|
27666
|
+
{
|
|
27667
|
+
databasePath: dbPath,
|
|
27668
|
+
autoBackup: false,
|
|
27669
|
+
versionCheck: buildReadVersionCheck(this.printer, stderrAnsi)
|
|
27670
|
+
},
|
|
27671
|
+
async (adapter) => {
|
|
27672
|
+
const bundle = await adapter.scans.findNode(this.nodePath);
|
|
27673
|
+
if (!bundle) {
|
|
27674
|
+
this.printer.error(tx(SHOW_TEXTS.nodeNotFound, { nodePath: this.nodePath }));
|
|
27675
|
+
return ExitCode.NotFound;
|
|
27676
|
+
}
|
|
27677
|
+
const doc = {
|
|
27678
|
+
node: bundle.node,
|
|
27679
|
+
linksOut: bundle.linksOut,
|
|
27680
|
+
linksIn: bundle.linksIn,
|
|
27681
|
+
issues: bundle.issues
|
|
27682
|
+
};
|
|
27683
|
+
if (this.json) {
|
|
27684
|
+
this.printer.data(JSON.stringify(doc) + "\n");
|
|
27685
|
+
return ExitCode.Ok;
|
|
27686
|
+
}
|
|
27687
|
+
const ansi = this.ansiFor("stdout");
|
|
27688
|
+
this.printer.data(renderHuman2(doc, ansi));
|
|
27147
27689
|
return ExitCode.Ok;
|
|
27148
27690
|
}
|
|
27149
|
-
|
|
27150
|
-
this.printer.data(renderHuman2(doc, ansi));
|
|
27151
|
-
return ExitCode.Ok;
|
|
27152
|
-
});
|
|
27691
|
+
);
|
|
27153
27692
|
}
|
|
27154
27693
|
};
|
|
27155
27694
|
function renderHuman2(doc, ansi) {
|
|
@@ -28047,7 +28586,7 @@ var STUB_COMMANDS = [
|
|
|
28047
28586
|
];
|
|
28048
28587
|
|
|
28049
28588
|
// cli/commands/tutorial.ts
|
|
28050
|
-
import { cpSync as cpSync2, existsSync as
|
|
28589
|
+
import { cpSync as cpSync2, existsSync as existsSync32, mkdirSync as mkdirSync6, readdirSync as readdirSync10, rmSync as rmSync2, statSync as statSync11 } from "fs";
|
|
28051
28590
|
import { dirname as dirname19, join as join21, resolve as resolve39 } from "path";
|
|
28052
28591
|
import { createInterface as createInterface5 } from "readline";
|
|
28053
28592
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
@@ -28391,7 +28930,7 @@ function resolveSkillSourceDir(variant) {
|
|
|
28391
28930
|
resolve39(here, "../cli/tutorial", spec.slug)
|
|
28392
28931
|
];
|
|
28393
28932
|
for (const candidate of candidates) {
|
|
28394
|
-
if (
|
|
28933
|
+
if (existsSync32(candidate) && statSync11(candidate).isDirectory()) {
|
|
28395
28934
|
cachedSourceDirs.set(variant, candidate);
|
|
28396
28935
|
return candidate;
|
|
28397
28936
|
}
|
|
@@ -28592,7 +29131,7 @@ function resolveBareInvocation(rawArgs) {
|
|
|
28592
29131
|
if (first !== void 0 && first.startsWith("-") && !passthrough.has(first)) {
|
|
28593
29132
|
const isSingleDashLong = !first.startsWith("--") && first.length > 2;
|
|
28594
29133
|
if (isSingleDashLong) return null;
|
|
28595
|
-
if (
|
|
29134
|
+
if (existsSync33(defaultProjectDbPath(defaultRuntimeContext()))) {
|
|
28596
29135
|
return ["serve", ...rawArgs];
|
|
28597
29136
|
}
|
|
28598
29137
|
return resolveBareDefault();
|
|
@@ -28601,7 +29140,7 @@ function resolveBareInvocation(rawArgs) {
|
|
|
28601
29140
|
}
|
|
28602
29141
|
function resolveBareDefault() {
|
|
28603
29142
|
const ctx = defaultRuntimeContext();
|
|
28604
|
-
if (
|
|
29143
|
+
if (existsSync33(defaultProjectDbPath(ctx))) {
|
|
28605
29144
|
return ["serve"];
|
|
28606
29145
|
}
|
|
28607
29146
|
const stderr = process.stderr;
|
|
@@ -28615,4 +29154,5 @@ function resolveBareDefault() {
|
|
|
28615
29154
|
);
|
|
28616
29155
|
process.exit(ExitCode.Error);
|
|
28617
29156
|
}
|
|
28618
|
-
//# sourceMappingURL=cli.js.map
|
|
29157
|
+
//# sourceMappingURL=cli.js.map
|
|
29158
|
+
//# debugId=926243f9-3707-54bc-9aa6-c68955118161
|