@skill-map/cli 0.61.5 → 0.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1346 -479
- package/dist/index.js +368 -96
- package/dist/kernel/index.d.ts +232 -25
- package/dist/kernel/index.js +368 -96
- package/dist/migrations/001_initial.sql +18 -8
- package/dist/ui/{chunk-4N3NRZEH.js → chunk-276RLZR4.js} +1 -1
- package/dist/ui/{chunk-MGWGV4VD.js → chunk-34ZZDYNQ.js} +1 -1
- package/dist/ui/chunk-56CBK7LB.js +1 -0
- package/dist/ui/{chunk-OVVTCPBJ.js → chunk-7ANZW2OI.js} +1 -1
- package/dist/ui/chunk-BJ6X6WBO.js +4 -0
- package/dist/ui/{chunk-5SSKJ7AM.js → chunk-BOVJVOLH.js} +1 -1
- package/dist/ui/chunk-C42H2UHU.js +3 -0
- package/dist/ui/{chunk-GKQA75EF.js → chunk-CJURGJTN.js} +1 -1
- package/dist/ui/chunk-CM4YB7L4.js +2 -0
- package/dist/ui/{chunk-Q4PXVDJA.js → chunk-CZSLV6YD.js} +1 -1
- package/dist/ui/{chunk-7X3DZNG4.js → chunk-DLYJHLJX.js} +2 -2
- package/dist/ui/chunk-ECKRC6XD.js +1843 -0
- package/dist/ui/{chunk-JTCIY3SL.js → chunk-FC22ZJQZ.js} +1 -1
- package/dist/ui/{chunk-FRUHVCND.js → chunk-FYATUDAH.js} +1 -1
- package/dist/ui/chunk-IYC5ZW4L.js +2 -0
- package/dist/ui/{chunk-MBBJJEUX.js → chunk-JZ2YF7EL.js} +1 -1
- package/dist/ui/{chunk-HQ6M2HXK.js → chunk-LPDD2DHE.js} +1 -1
- package/dist/ui/{chunk-I52OQIZQ.js → chunk-NC3HOVDG.js} +1 -1
- package/dist/ui/{chunk-N6MUHKWR.js → chunk-UTRZTB6V.js} +1 -1
- package/dist/ui/chunk-VHEFRMK3.js +1 -0
- package/dist/ui/chunk-Y2Z26SRI.js +1 -0
- package/dist/ui/index.html +1 -1
- package/dist/ui/main-RW5YGD6H.js +4 -0
- package/migrations/001_initial.sql +18 -8
- package/package.json +2 -2
- package/dist/ui/chunk-6NYH7LND.js +0 -3
- package/dist/ui/chunk-7VUEZZFJ.js +0 -1
- package/dist/ui/chunk-AKKFFP7Y.js +0 -1
- package/dist/ui/chunk-L34EUS75.js +0 -2
- package/dist/ui/chunk-UTGLW5ON.js +0 -1843
- package/dist/ui/chunk-ZYPXVXYF.js +0 -4
- package/dist/ui/main-OTDMPZHD.js +0 -4
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// cli/entry.ts
|
|
2
2
|
|
|
3
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d7ccbbda-dc56-582c-966a-4822e4bdcd83")}catch(e){}}();
|
|
4
4
|
import { existsSync as existsSync33 } from "fs";
|
|
5
5
|
import { Builtins, Cli as Cli2 } from "clipanion";
|
|
6
6
|
|
|
@@ -250,7 +250,7 @@ function bucketByKind(kind, instance, bag) {
|
|
|
250
250
|
// package.json
|
|
251
251
|
var package_default = {
|
|
252
252
|
name: "@skill-map/cli",
|
|
253
|
-
version: "0.
|
|
253
|
+
version: "0.62.0",
|
|
254
254
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
255
255
|
license: "MIT",
|
|
256
256
|
type: "module",
|
|
@@ -812,6 +812,15 @@ function extractCodeRegions(input) {
|
|
|
812
812
|
}
|
|
813
813
|
return out;
|
|
814
814
|
}
|
|
815
|
+
var HTML_COMMENT_RE = /<!--[\s\S]*?-->/g;
|
|
816
|
+
var HTML_TAG_RE = /<\/?[a-zA-Z][a-zA-Z0-9-]*(?:\s+(?:"[^"]*"|'[^']*'|[^"'>])*)?\/?\s*>/g;
|
|
817
|
+
function stripHtml(input) {
|
|
818
|
+
if (!input) return input;
|
|
819
|
+
return input.replace(HTML_COMMENT_RE, (m) => blank(m)).replace(HTML_TAG_RE, (m) => blank(m));
|
|
820
|
+
}
|
|
821
|
+
function stripCodeAndHtml(input) {
|
|
822
|
+
return stripHtml(stripCodeBlocks(input));
|
|
823
|
+
}
|
|
815
824
|
function stripFences(input) {
|
|
816
825
|
const out = [];
|
|
817
826
|
const lines = input.split("\n");
|
|
@@ -896,7 +905,7 @@ var atDirectiveExtractor = {
|
|
|
896
905
|
extract(ctx) {
|
|
897
906
|
const seenMentions = /* @__PURE__ */ new Set();
|
|
898
907
|
const seenReferences = /* @__PURE__ */ new Set();
|
|
899
|
-
const body =
|
|
908
|
+
const body = stripCodeAndHtml(ctx.body);
|
|
900
909
|
const lineStarts = computeLineStarts(body);
|
|
901
910
|
const sourceDir = pathPosix.dirname(ctx.node.path);
|
|
902
911
|
for (const match of body.matchAll(AT_RE)) {
|
|
@@ -982,7 +991,7 @@ var slashCommandExtractor = {
|
|
|
982
991
|
precondition: { provider: ["claude"] },
|
|
983
992
|
extract(ctx) {
|
|
984
993
|
const seen = /* @__PURE__ */ new Set();
|
|
985
|
-
const body =
|
|
994
|
+
const body = stripCodeAndHtml(ctx.body);
|
|
986
995
|
const lineStarts = computeLineStarts(body);
|
|
987
996
|
for (const match of body.matchAll(SLASH_RE)) {
|
|
988
997
|
const original = match[1];
|
|
@@ -1566,7 +1575,7 @@ var externalUrlCounterExtractor = {
|
|
|
1566
1575
|
extract(ctx) {
|
|
1567
1576
|
const seen = /* @__PURE__ */ new Set();
|
|
1568
1577
|
const ignoredDomains = readIgnoredDomains(ctx.settings[SETTING_IGNORED_DOMAINS]);
|
|
1569
|
-
const body =
|
|
1578
|
+
const body = stripCodeAndHtml(ctx.body);
|
|
1570
1579
|
const lineStarts = computeLineStarts(body);
|
|
1571
1580
|
for (const match of body.matchAll(URL_RE)) {
|
|
1572
1581
|
const original = stripTrailingPunctuation(match[0]);
|
|
@@ -1638,7 +1647,7 @@ var markdownLinkExtractor = {
|
|
|
1638
1647
|
scope: "body",
|
|
1639
1648
|
extract(ctx) {
|
|
1640
1649
|
const seen = /* @__PURE__ */ new Set();
|
|
1641
|
-
const body =
|
|
1650
|
+
const body = stripCodeAndHtml(ctx.body);
|
|
1642
1651
|
const lineStarts = computeLineStarts(body);
|
|
1643
1652
|
const sourceDir = pathPosix3.dirname(ctx.node.path);
|
|
1644
1653
|
for (const match of body.matchAll(LINK_RE)) {
|
|
@@ -5312,6 +5321,7 @@ var defaults_default = {
|
|
|
5312
5321
|
tokenize: true,
|
|
5313
5322
|
strict: false,
|
|
5314
5323
|
maxFileSizeBytes: 1048576,
|
|
5324
|
+
maxScan: 5e4,
|
|
5315
5325
|
maxNodes: 256,
|
|
5316
5326
|
watch: {
|
|
5317
5327
|
debounceMs: 300
|
|
@@ -6207,7 +6217,7 @@ import { existsSync as existsSync11 } from "fs";
|
|
|
6207
6217
|
import { mkdirSync as mkdirSync4 } from "fs";
|
|
6208
6218
|
import { dirname as dirname8, resolve as resolve11 } from "path";
|
|
6209
6219
|
import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
|
|
6210
|
-
import { CamelCasePlugin, Kysely, sql as
|
|
6220
|
+
import { CamelCasePlugin, Kysely, sql as sql4 } from "kysely";
|
|
6211
6221
|
|
|
6212
6222
|
// kernel/i18n/storage.texts.ts
|
|
6213
6223
|
var STORAGE_TEXTS = {
|
|
@@ -6304,8 +6314,8 @@ var NodeSqliteConnection = class {
|
|
|
6304
6314
|
constructor(db) {
|
|
6305
6315
|
this.#db = db;
|
|
6306
6316
|
}
|
|
6307
|
-
exec(
|
|
6308
|
-
this.#db.exec(
|
|
6317
|
+
exec(sql5) {
|
|
6318
|
+
this.#db.exec(sql5);
|
|
6309
6319
|
}
|
|
6310
6320
|
async executeQuery(query) {
|
|
6311
6321
|
const stmt = this.#db.prepare(query.sql);
|
|
@@ -6338,7 +6348,7 @@ var AsyncMutex = class {
|
|
|
6338
6348
|
this.#locked = true;
|
|
6339
6349
|
return;
|
|
6340
6350
|
}
|
|
6341
|
-
await new Promise((
|
|
6351
|
+
await new Promise((resolve43) => this.#waiters.push(resolve43));
|
|
6342
6352
|
this.#locked = true;
|
|
6343
6353
|
}
|
|
6344
6354
|
unlock() {
|
|
@@ -6882,10 +6892,10 @@ function applyOneMigration(db, migration) {
|
|
|
6882
6892
|
tx(MIGRATIONS_TEXTS.invalidVersion, { value: String(migration.version) })
|
|
6883
6893
|
);
|
|
6884
6894
|
}
|
|
6885
|
-
const
|
|
6895
|
+
const sql5 = readFileSync8(migration.filePath, "utf8");
|
|
6886
6896
|
try {
|
|
6887
6897
|
db.exec("BEGIN");
|
|
6888
|
-
db.exec(
|
|
6898
|
+
db.exec(sql5);
|
|
6889
6899
|
db.prepare(
|
|
6890
6900
|
`INSERT INTO config_schema_versions (scope, owner_id, version, description, applied_at)
|
|
6891
6901
|
VALUES ('kernel', 'kernel', ?, ?, ?)`
|
|
@@ -6929,37 +6939,37 @@ import { join as join6 } from "path";
|
|
|
6929
6939
|
function normalizePluginId(id) {
|
|
6930
6940
|
return id.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
6931
6941
|
}
|
|
6932
|
-
function stripComments(
|
|
6933
|
-
return
|
|
6942
|
+
function stripComments(sql5) {
|
|
6943
|
+
return sql5.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\n\r]*/g, " ");
|
|
6934
6944
|
}
|
|
6935
|
-
function detectCommentMarkerInLiteral(
|
|
6945
|
+
function detectCommentMarkerInLiteral(sql5) {
|
|
6936
6946
|
let i = 0;
|
|
6937
|
-
while (i <
|
|
6938
|
-
const ch =
|
|
6947
|
+
while (i < sql5.length) {
|
|
6948
|
+
const ch = sql5[i];
|
|
6939
6949
|
if (ch === "'") {
|
|
6940
|
-
const end = scanCheckedLiteral(
|
|
6950
|
+
const end = scanCheckedLiteral(sql5, i + 1, "'", "string literal");
|
|
6941
6951
|
if (typeof end === "string") return end;
|
|
6942
6952
|
i = end;
|
|
6943
6953
|
} else if (ch === '"') {
|
|
6944
|
-
const end = scanCheckedLiteral(
|
|
6954
|
+
const end = scanCheckedLiteral(sql5, i + 1, '"', "double-quoted identifier");
|
|
6945
6955
|
if (typeof end === "string") return end;
|
|
6946
6956
|
i = end;
|
|
6947
6957
|
} else if (ch === "`") {
|
|
6948
|
-
i = skipUntilCloser(
|
|
6958
|
+
i = skipUntilCloser(sql5, i + 1, "`");
|
|
6949
6959
|
} else if (ch === "[") {
|
|
6950
|
-
i = skipUntilCloser(
|
|
6960
|
+
i = skipUntilCloser(sql5, i + 1, "]");
|
|
6951
6961
|
} else {
|
|
6952
6962
|
i++;
|
|
6953
6963
|
}
|
|
6954
6964
|
}
|
|
6955
6965
|
return null;
|
|
6956
6966
|
}
|
|
6957
|
-
function scanCheckedLiteral(
|
|
6967
|
+
function scanCheckedLiteral(sql5, start, closer, label) {
|
|
6958
6968
|
const allowStack = closer === "'";
|
|
6959
6969
|
let i = start;
|
|
6960
|
-
while (i <
|
|
6961
|
-
const ch =
|
|
6962
|
-
const next =
|
|
6970
|
+
while (i < sql5.length) {
|
|
6971
|
+
const ch = sql5[i];
|
|
6972
|
+
const next = sql5[i + 1];
|
|
6963
6973
|
if (allowStack && ch === closer && next === closer) {
|
|
6964
6974
|
i += 2;
|
|
6965
6975
|
continue;
|
|
@@ -6980,10 +6990,10 @@ function findCommentMarker(ch, next, label) {
|
|
|
6980
6990
|
}
|
|
6981
6991
|
return null;
|
|
6982
6992
|
}
|
|
6983
|
-
function skipUntilCloser(
|
|
6993
|
+
function skipUntilCloser(sql5, start, closer) {
|
|
6984
6994
|
let i = start;
|
|
6985
|
-
while (i <
|
|
6986
|
-
if (
|
|
6995
|
+
while (i < sql5.length) {
|
|
6996
|
+
if (sql5[i] === closer) return i + 1;
|
|
6987
6997
|
i++;
|
|
6988
6998
|
}
|
|
6989
6999
|
return i;
|
|
@@ -7094,14 +7104,14 @@ function stripIdentifierWrapper(raw) {
|
|
|
7094
7104
|
return raw;
|
|
7095
7105
|
}
|
|
7096
7106
|
var QUOTE_OPENERS = /* @__PURE__ */ new Set(["'", '"', "`", "["]);
|
|
7097
|
-
function splitStatements(
|
|
7107
|
+
function splitStatements(sql5) {
|
|
7098
7108
|
const out = [];
|
|
7099
7109
|
let current = "";
|
|
7100
7110
|
let i = 0;
|
|
7101
|
-
while (i <
|
|
7102
|
-
const ch =
|
|
7111
|
+
while (i < sql5.length) {
|
|
7112
|
+
const ch = sql5[i];
|
|
7103
7113
|
if (QUOTE_OPENERS.has(ch)) {
|
|
7104
|
-
const consumed = copyQuotedRegion(
|
|
7114
|
+
const consumed = copyQuotedRegion(sql5, i, ch);
|
|
7105
7115
|
current += consumed.text;
|
|
7106
7116
|
i = consumed.next;
|
|
7107
7117
|
continue;
|
|
@@ -7120,16 +7130,16 @@ function splitStatements(sql4) {
|
|
|
7120
7130
|
if (tail.length > 0) out.push(tail);
|
|
7121
7131
|
return out;
|
|
7122
7132
|
}
|
|
7123
|
-
function copyQuotedRegion(
|
|
7133
|
+
function copyQuotedRegion(sql5, start, opener) {
|
|
7124
7134
|
const closer = opener === "[" ? "]" : opener;
|
|
7125
7135
|
const allowStack = opener === "'";
|
|
7126
7136
|
let text = opener;
|
|
7127
7137
|
let i = start + 1;
|
|
7128
|
-
while (i <
|
|
7129
|
-
const ch =
|
|
7138
|
+
while (i < sql5.length) {
|
|
7139
|
+
const ch = sql5[i];
|
|
7130
7140
|
text += ch;
|
|
7131
7141
|
if (ch === closer) {
|
|
7132
|
-
if (allowStack &&
|
|
7142
|
+
if (allowStack && sql5[i + 1] === closer) {
|
|
7133
7143
|
text += closer;
|
|
7134
7144
|
i += 2;
|
|
7135
7145
|
continue;
|
|
@@ -7140,11 +7150,11 @@ function copyQuotedRegion(sql4, start, opener) {
|
|
|
7140
7150
|
}
|
|
7141
7151
|
return { text, next: i };
|
|
7142
7152
|
}
|
|
7143
|
-
function validatePluginMigrationSql(
|
|
7144
|
-
const literalIssue = detectCommentMarkerInLiteral(
|
|
7153
|
+
function validatePluginMigrationSql(sql5, normalizedId) {
|
|
7154
|
+
const literalIssue = detectCommentMarkerInLiteral(sql5);
|
|
7145
7155
|
if (literalIssue) return { ok: false, violations: [literalIssue] };
|
|
7146
7156
|
const prefix = `plugin_${normalizedId}_`;
|
|
7147
|
-
const stripped = stripComments(
|
|
7157
|
+
const stripped = stripComments(sql5);
|
|
7148
7158
|
const violations = [
|
|
7149
7159
|
...detectForbiddenKeywords(stripped),
|
|
7150
7160
|
...detectStatementViolations(stripped, prefix)
|
|
@@ -7305,9 +7315,9 @@ function applyPluginMigrations(db, plugin, options = {}, files = discoverPluginM
|
|
|
7305
7315
|
function preflightValidateAll(pending, normalizedId, pluginId) {
|
|
7306
7316
|
const sources = /* @__PURE__ */ new Map();
|
|
7307
7317
|
for (const m of pending) {
|
|
7308
|
-
const
|
|
7309
|
-
sources.set(m.filePath,
|
|
7310
|
-
const result = validatePluginMigrationSql(
|
|
7318
|
+
const sql5 = readFileSync9(m.filePath, "utf8");
|
|
7319
|
+
sources.set(m.filePath, sql5);
|
|
7320
|
+
const result = validatePluginMigrationSql(sql5, normalizedId);
|
|
7311
7321
|
if (!result.ok) {
|
|
7312
7322
|
throw new Error(
|
|
7313
7323
|
`Plugin ${pluginId}: migration ${formatMigrationName(m)} failed validation:
|
|
@@ -7317,8 +7327,8 @@ function preflightValidateAll(pending, normalizedId, pluginId) {
|
|
|
7317
7327
|
}
|
|
7318
7328
|
return sources;
|
|
7319
7329
|
}
|
|
7320
|
-
function applyOnePluginMigration(db, plugin, migration,
|
|
7321
|
-
const result = validatePluginMigrationSql(
|
|
7330
|
+
function applyOnePluginMigration(db, plugin, migration, sql5, normalizedId) {
|
|
7331
|
+
const result = validatePluginMigrationSql(sql5, normalizedId);
|
|
7322
7332
|
if (!result.ok) {
|
|
7323
7333
|
throw new Error(
|
|
7324
7334
|
`Plugin ${plugin.id}: migration ${formatMigrationName(migration)} failed Layer-2 validation:
|
|
@@ -7327,7 +7337,7 @@ function applyOnePluginMigration(db, plugin, migration, sql4, normalizedId) {
|
|
|
7327
7337
|
}
|
|
7328
7338
|
try {
|
|
7329
7339
|
db.exec("BEGIN");
|
|
7330
|
-
db.exec(
|
|
7340
|
+
db.exec(sql5);
|
|
7331
7341
|
db.prepare(
|
|
7332
7342
|
`INSERT INTO config_schema_versions (scope, owner_id, version, description, applied_at)
|
|
7333
7343
|
VALUES ('plugin', ?, ?, ?, ?)`
|
|
@@ -7386,6 +7396,9 @@ async function loadPluginOverrideMap(db) {
|
|
|
7386
7396
|
return out;
|
|
7387
7397
|
}
|
|
7388
7398
|
|
|
7399
|
+
// kernel/adapters/sqlite/scan-load.ts
|
|
7400
|
+
import { sql as sql2 } from "kysely";
|
|
7401
|
+
|
|
7389
7402
|
// kernel/util/enum-parsers.ts
|
|
7390
7403
|
var STABILITY_VALUES2 = Object.freeze([
|
|
7391
7404
|
"experimental",
|
|
@@ -7482,40 +7495,11 @@ async function loadScanResult(db) {
|
|
|
7482
7495
|
);
|
|
7483
7496
|
}
|
|
7484
7497
|
if (metaRow) {
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
};
|
|
7490
|
-
const oversizedFiles = parseJsonArray(metaRow.oversizedFilesJson);
|
|
7491
|
-
return {
|
|
7492
|
-
schemaVersion: 1,
|
|
7493
|
-
scannedAt: metaRow.scannedAt,
|
|
7494
|
-
roots: parseJsonArray(metaRow.rootsJson),
|
|
7495
|
-
providers: parseJsonArray(metaRow.providersJson),
|
|
7496
|
-
scannedBy,
|
|
7497
|
-
// Resolved encoder of the prior scan (see project-config.schema.json
|
|
7498
|
-
// §tokenizer). NULL column → `undefined` domain field; the
|
|
7499
|
-
// orchestrator's tokenizer-change check compares this against the
|
|
7500
|
-
// freshly-resolved encoder and treats a missing prior value as a
|
|
7501
|
-
// change (forcing a token recompute).
|
|
7502
|
-
...metaRow.tokenizer !== null ? { tokenizer: metaRow.tokenizer } : {},
|
|
7503
|
-
recommendedNodeLimit: metaRow.recommendedNodeLimit,
|
|
7504
|
-
overrideMaxNodes: metaRow.overrideMaxNodes,
|
|
7505
|
-
oversizedFiles,
|
|
7506
|
-
nodes,
|
|
7507
|
-
links,
|
|
7508
|
-
issues,
|
|
7509
|
-
stats: {
|
|
7510
|
-
filesWalked: metaRow.statsFilesWalked,
|
|
7511
|
-
filesSkipped: metaRow.statsFilesSkipped,
|
|
7512
|
-
filesOversized: metaRow.filesOversized,
|
|
7513
|
-
nodesCount: nodes.length,
|
|
7514
|
-
linksCount: links.length,
|
|
7515
|
-
issuesCount: issues.length,
|
|
7516
|
-
durationMs: metaRow.statsDurationMs
|
|
7517
|
-
}
|
|
7518
|
-
};
|
|
7498
|
+
return buildScanResultFromMeta(metaRow, nodes, links, issues, {
|
|
7499
|
+
nodesCount: nodes.length,
|
|
7500
|
+
linksCount: links.length,
|
|
7501
|
+
issuesCount: issues.length
|
|
7502
|
+
});
|
|
7519
7503
|
}
|
|
7520
7504
|
let scannedAt = 0;
|
|
7521
7505
|
for (const row of nodeRows) {
|
|
@@ -7527,11 +7511,13 @@ async function loadScanResult(db) {
|
|
|
7527
7511
|
scannedAt,
|
|
7528
7512
|
roots: ["."],
|
|
7529
7513
|
providers: [],
|
|
7530
|
-
// Synthetic envelope, default to the design
|
|
7531
|
-
//
|
|
7532
|
-
//
|
|
7533
|
-
|
|
7534
|
-
|
|
7514
|
+
// Synthetic envelope, default to the design knobs (corpus ceiling
|
|
7515
|
+
// 50000, render cap 256, not truncated) so the SPA reads the same
|
|
7516
|
+
// shape across cold-boot and never-scanned scopes. A real scan
|
|
7517
|
+
// overwrites scan_meta with the live values on next run.
|
|
7518
|
+
scanCeiling: 5e4,
|
|
7519
|
+
scanTruncated: false,
|
|
7520
|
+
maxRenderNodes: 256,
|
|
7535
7521
|
oversizedFiles: [],
|
|
7536
7522
|
nodes,
|
|
7537
7523
|
links,
|
|
@@ -7547,6 +7533,175 @@ async function loadScanResult(db) {
|
|
|
7547
7533
|
}
|
|
7548
7534
|
};
|
|
7549
7535
|
}
|
|
7536
|
+
function buildScanResultFromMeta(metaRow, nodes, links, issues, counts) {
|
|
7537
|
+
const scannedBy = {
|
|
7538
|
+
name: metaRow.scannedByName,
|
|
7539
|
+
version: metaRow.scannedByVersion,
|
|
7540
|
+
specVersion: metaRow.scannedBySpecVersion
|
|
7541
|
+
};
|
|
7542
|
+
const oversizedFiles = parseJsonArray(metaRow.oversizedFilesJson);
|
|
7543
|
+
return {
|
|
7544
|
+
schemaVersion: 1,
|
|
7545
|
+
scannedAt: metaRow.scannedAt,
|
|
7546
|
+
roots: parseJsonArray(metaRow.rootsJson),
|
|
7547
|
+
providers: parseJsonArray(metaRow.providersJson),
|
|
7548
|
+
scannedBy,
|
|
7549
|
+
// Resolved encoder of the prior scan (see project-config.schema.json
|
|
7550
|
+
// §tokenizer). A NULL column maps to an absent domain field; the
|
|
7551
|
+
// orchestrator's tokenizer-change check treats a missing prior value
|
|
7552
|
+
// as a change (forcing a token recompute).
|
|
7553
|
+
...metaRow.tokenizer !== null ? { tokenizer: metaRow.tokenizer } : {},
|
|
7554
|
+
scanCeiling: metaRow.scanCeiling,
|
|
7555
|
+
scanTruncated: metaRow.scanTruncated === 1,
|
|
7556
|
+
maxRenderNodes: metaRow.maxRenderNodes,
|
|
7557
|
+
oversizedFiles,
|
|
7558
|
+
nodes,
|
|
7559
|
+
links,
|
|
7560
|
+
issues,
|
|
7561
|
+
stats: {
|
|
7562
|
+
filesWalked: metaRow.statsFilesWalked,
|
|
7563
|
+
filesSkipped: metaRow.statsFilesSkipped,
|
|
7564
|
+
filesOversized: metaRow.filesOversized,
|
|
7565
|
+
nodesCount: counts.nodesCount,
|
|
7566
|
+
linksCount: counts.linksCount,
|
|
7567
|
+
issuesCount: counts.issuesCount,
|
|
7568
|
+
durationMs: metaRow.statsDurationMs
|
|
7569
|
+
}
|
|
7570
|
+
};
|
|
7571
|
+
}
|
|
7572
|
+
async function loadScanMeta(db, counts) {
|
|
7573
|
+
const metaRow = await db.selectFrom("scan_meta").selectAll().executeTakeFirst();
|
|
7574
|
+
const c = {
|
|
7575
|
+
nodesCount: counts.nodes,
|
|
7576
|
+
linksCount: counts.links,
|
|
7577
|
+
issuesCount: counts.issues
|
|
7578
|
+
};
|
|
7579
|
+
if (metaRow) {
|
|
7580
|
+
return buildScanResultFromMeta(metaRow, [], [], [], c);
|
|
7581
|
+
}
|
|
7582
|
+
return {
|
|
7583
|
+
schemaVersion: 1,
|
|
7584
|
+
scannedAt: Date.now(),
|
|
7585
|
+
roots: ["."],
|
|
7586
|
+
providers: [],
|
|
7587
|
+
scanCeiling: 5e4,
|
|
7588
|
+
scanTruncated: false,
|
|
7589
|
+
maxRenderNodes: 256,
|
|
7590
|
+
oversizedFiles: [],
|
|
7591
|
+
nodes: [],
|
|
7592
|
+
links: [],
|
|
7593
|
+
issues: [],
|
|
7594
|
+
stats: {
|
|
7595
|
+
filesWalked: 0,
|
|
7596
|
+
filesSkipped: 0,
|
|
7597
|
+
filesOversized: 0,
|
|
7598
|
+
nodesCount: c.nodesCount,
|
|
7599
|
+
linksCount: c.linksCount,
|
|
7600
|
+
issuesCount: c.issuesCount,
|
|
7601
|
+
durationMs: 0
|
|
7602
|
+
}
|
|
7603
|
+
};
|
|
7604
|
+
}
|
|
7605
|
+
var DEFAULT_MAX_RENDER_NODES = 256;
|
|
7606
|
+
async function loadLiteNodes(db) {
|
|
7607
|
+
const rows = await db.selectFrom("scan_nodes").select([
|
|
7608
|
+
"path",
|
|
7609
|
+
"kind",
|
|
7610
|
+
"linksInCount",
|
|
7611
|
+
"linksOutCount",
|
|
7612
|
+
"tokensTotal",
|
|
7613
|
+
"modifiedAtMs",
|
|
7614
|
+
"sidecarStatus"
|
|
7615
|
+
]).orderBy("path", "asc").execute();
|
|
7616
|
+
return rows.map((r) => ({
|
|
7617
|
+
path: r.path,
|
|
7618
|
+
kind: r.kind,
|
|
7619
|
+
linksInCount: r.linksInCount,
|
|
7620
|
+
linksOutCount: r.linksOutCount,
|
|
7621
|
+
tokensTotal: r.tokensTotal,
|
|
7622
|
+
modifiedAtMs: r.modifiedAtMs,
|
|
7623
|
+
sidecarStatus: r.sidecarStatus
|
|
7624
|
+
}));
|
|
7625
|
+
}
|
|
7626
|
+
async function loadIssueCountsByPath(db) {
|
|
7627
|
+
const rows = await db.selectFrom(
|
|
7628
|
+
sql2`(
|
|
7629
|
+
SELECT je.value AS value, si.severity AS severity, COUNT(*) AS c
|
|
7630
|
+
FROM scan_issues si, json_each(si.node_ids_json) je
|
|
7631
|
+
WHERE si.severity IN ('error', 'warn')
|
|
7632
|
+
GROUP BY je.value, si.severity
|
|
7633
|
+
)`.as("incidence")
|
|
7634
|
+
).select(["value", "severity", "c"]).execute();
|
|
7635
|
+
const out = /* @__PURE__ */ new Map();
|
|
7636
|
+
for (const row of rows) {
|
|
7637
|
+
const bucket = out.get(row.value) ?? { error: 0, warn: 0 };
|
|
7638
|
+
if (row.severity === "error") bucket.error = Number(row.c);
|
|
7639
|
+
else if (row.severity === "warn") bucket.warn = Number(row.c);
|
|
7640
|
+
out.set(row.value, bucket);
|
|
7641
|
+
}
|
|
7642
|
+
return out;
|
|
7643
|
+
}
|
|
7644
|
+
async function loadEffectiveMaxRenderNodes(db) {
|
|
7645
|
+
const metaRow = await db.selectFrom("scan_meta").select(["maxRenderNodes"]).executeTakeFirst();
|
|
7646
|
+
return metaRow?.maxRenderNodes ?? DEFAULT_MAX_RENDER_NODES;
|
|
7647
|
+
}
|
|
7648
|
+
async function loadBranch(db, prefixes, limit) {
|
|
7649
|
+
const uniquePrefixes = [...new Set(prefixes)];
|
|
7650
|
+
const total = await countBranchNodes(db, uniquePrefixes);
|
|
7651
|
+
const nodeRows = await applyBranchScope(
|
|
7652
|
+
db.selectFrom("scan_nodes").selectAll(),
|
|
7653
|
+
uniquePrefixes
|
|
7654
|
+
).orderBy("path", "asc").limit(limit).execute();
|
|
7655
|
+
const nodes = nodeRows.map(rowToNode);
|
|
7656
|
+
const pathSet = new Set(nodes.map((n) => n.path));
|
|
7657
|
+
if (pathSet.size === 0) {
|
|
7658
|
+
return { nodes, links: [], issues: [], total, paths: uniquePrefixes };
|
|
7659
|
+
}
|
|
7660
|
+
const paths = [...pathSet];
|
|
7661
|
+
const [linkRows, issueRows] = await Promise.all([
|
|
7662
|
+
db.selectFrom("scan_links").selectAll().where("sourcePath", "in", paths).where("targetPath", "in", paths).execute(),
|
|
7663
|
+
db.selectFrom("scan_issues").selectAll().where(
|
|
7664
|
+
({ exists, selectFrom }) => exists(
|
|
7665
|
+
selectFrom(
|
|
7666
|
+
sql2`json_each(scan_issues.node_ids_json)`.as("je")
|
|
7667
|
+
).select(sql2`1`.as("one")).where(sql2.ref("je.value"), "in", paths)
|
|
7668
|
+
)
|
|
7669
|
+
).execute()
|
|
7670
|
+
]);
|
|
7671
|
+
return {
|
|
7672
|
+
nodes,
|
|
7673
|
+
links: linkRows.map(rowToLink),
|
|
7674
|
+
issues: issueRows.map(rowToIssue),
|
|
7675
|
+
total,
|
|
7676
|
+
paths: uniquePrefixes
|
|
7677
|
+
};
|
|
7678
|
+
}
|
|
7679
|
+
async function countBranchNodes(db, prefixes) {
|
|
7680
|
+
const row = await applyBranchScope(
|
|
7681
|
+
db.selectFrom("scan_nodes"),
|
|
7682
|
+
prefixes
|
|
7683
|
+
).select(({ fn }) => fn.countAll().as("c")).executeTakeFirst();
|
|
7684
|
+
return Number(row?.c ?? 0);
|
|
7685
|
+
}
|
|
7686
|
+
function applyBranchScope(query, prefixes) {
|
|
7687
|
+
if (prefixes.length === 0) return query;
|
|
7688
|
+
return query.where(
|
|
7689
|
+
({ eb, or }) => or(
|
|
7690
|
+
prefixes.map(
|
|
7691
|
+
(prefix) => (
|
|
7692
|
+
// Per-prefix subtree predicate. `prefix || '/%'`: the `/%` lives
|
|
7693
|
+
// in the template, `prefix` binds separately, so no user input is
|
|
7694
|
+
// interpolated into the SQL. The two clauses are ORed so the
|
|
7695
|
+
// prefix matches the folder node itself plus every descendant.
|
|
7696
|
+
eb.or([
|
|
7697
|
+
eb("path", "=", prefix),
|
|
7698
|
+
eb("path", "like", sql2`${prefix} || '/%'`)
|
|
7699
|
+
])
|
|
7700
|
+
)
|
|
7701
|
+
)
|
|
7702
|
+
)
|
|
7703
|
+
);
|
|
7704
|
+
}
|
|
7550
7705
|
function rowToNode(row) {
|
|
7551
7706
|
const bytes = {
|
|
7552
7707
|
frontmatter: row.bytesFrontmatter,
|
|
@@ -7702,7 +7857,7 @@ function parseJsonArray(s) {
|
|
|
7702
7857
|
}
|
|
7703
7858
|
|
|
7704
7859
|
// kernel/adapters/sqlite/scan-persistence.ts
|
|
7705
|
-
import { sql as
|
|
7860
|
+
import { sql as sql3 } from "kysely";
|
|
7706
7861
|
|
|
7707
7862
|
// kernel/adapters/sqlite/contributions.ts
|
|
7708
7863
|
async function replaceAllScanContributions(trx, contributions, livePaths = /* @__PURE__ */ new Set(), registeredKeys = /* @__PURE__ */ new Set(), freshlyRunTuples = /* @__PURE__ */ new Set()) {
|
|
@@ -7767,9 +7922,9 @@ async function sweepPerTupleContributions(trx, contributions, freshlyRunTuples)
|
|
|
7767
7922
|
const bufferKeys = buildContributionsBufferKeys(contributions);
|
|
7768
7923
|
const tuplesByPluginExt = groupFreshlyRunTuplesByPluginExt(freshlyRunTuples);
|
|
7769
7924
|
for (const [pe, nodes] of tuplesByPluginExt) {
|
|
7770
|
-
const
|
|
7771
|
-
if (
|
|
7772
|
-
await deleteStaleTupleRows(trx, pe.slice(0,
|
|
7925
|
+
const sep9 = pe.indexOf("\0");
|
|
7926
|
+
if (sep9 < 0) continue;
|
|
7927
|
+
await deleteStaleTupleRows(trx, pe.slice(0, sep9), pe.slice(sep9 + 1), [...nodes], bufferKeys);
|
|
7773
7928
|
}
|
|
7774
7929
|
}
|
|
7775
7930
|
function buildContributionsBufferKeys(contributions) {
|
|
@@ -7909,9 +8064,9 @@ function schemaFingerprint(files) {
|
|
|
7909
8064
|
const migrations = files ?? discoverMigrations();
|
|
7910
8065
|
const hash = createHash("sha256");
|
|
7911
8066
|
for (const m of migrations) {
|
|
7912
|
-
const
|
|
7913
|
-
hash.update(`${m.version}\0${m.description}\0${Buffer.byteLength(
|
|
7914
|
-
hash.update(
|
|
8067
|
+
const sql5 = existsSync10(m.filePath) ? readFileSync10(m.filePath, "utf8") : "";
|
|
8068
|
+
hash.update(`${m.version}\0${m.description}\0${Buffer.byteLength(sql5)}\0`);
|
|
8069
|
+
hash.update(sql5);
|
|
7915
8070
|
}
|
|
7916
8071
|
const digest = hash.digest("hex");
|
|
7917
8072
|
if (files === void 0) memoized = digest;
|
|
@@ -8017,7 +8172,7 @@ async function persistScanResult(db, result, inputs = {}) {
|
|
|
8017
8172
|
await upsertEnrichmentLayer(trx, result, renameOps, enrichments);
|
|
8018
8173
|
await flagStaleProbabilisticEnrichments(trx, result, enrichments);
|
|
8019
8174
|
});
|
|
8020
|
-
await
|
|
8175
|
+
await sql3`PRAGMA wal_checkpoint(TRUNCATE)`.execute(db);
|
|
8021
8176
|
return { renames };
|
|
8022
8177
|
}
|
|
8023
8178
|
function resolvePersistInputs(inputs) {
|
|
@@ -8072,36 +8227,36 @@ function collectKnownOrphanPaths(issues) {
|
|
|
8072
8227
|
}
|
|
8073
8228
|
return out;
|
|
8074
8229
|
}
|
|
8230
|
+
var MAX_SQL_VARS = 2e4;
|
|
8231
|
+
async function chunkedInsert(trx, table, rows) {
|
|
8232
|
+
if (rows.length === 0) return;
|
|
8233
|
+
const columns = Object.keys(rows[0]).length || 1;
|
|
8234
|
+
const batchSize = Math.max(1, Math.floor(MAX_SQL_VARS / columns));
|
|
8235
|
+
for (let start = 0; start < rows.length; start += batchSize) {
|
|
8236
|
+
await trx.insertInto(table).values(rows.slice(start, start + batchSize)).execute();
|
|
8237
|
+
}
|
|
8238
|
+
}
|
|
8075
8239
|
async function replaceAllScanZone(trx, result, scannedAt, extractorRuns) {
|
|
8076
8240
|
await trx.deleteFrom("scan_issues").execute();
|
|
8077
8241
|
await trx.deleteFrom("scan_links").execute();
|
|
8078
8242
|
await trx.deleteFrom("scan_nodes").execute();
|
|
8079
8243
|
await trx.deleteFrom("scan_meta").execute();
|
|
8080
8244
|
await trx.deleteFrom("scan_extractor_runs").execute();
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
if (result.links.length > 0) {
|
|
8085
|
-
await trx.insertInto("scan_links").values(result.links.map(linkToRow)).execute();
|
|
8086
|
-
}
|
|
8087
|
-
if (result.issues.length > 0) {
|
|
8088
|
-
await trx.insertInto("scan_issues").values(result.issues.map(issueToRow)).execute();
|
|
8089
|
-
}
|
|
8245
|
+
await chunkedInsert(trx, "scan_nodes", result.nodes.map((n) => nodeToRow(n, scannedAt)));
|
|
8246
|
+
await chunkedInsert(trx, "scan_links", result.links.map(linkToRow));
|
|
8247
|
+
await chunkedInsert(trx, "scan_issues", result.issues.map(issueToRow));
|
|
8090
8248
|
await trx.insertInto("scan_meta").values(metaToRow(result)).execute();
|
|
8091
|
-
|
|
8092
|
-
await trx.insertInto("scan_extractor_runs").values(extractorRuns.map(extractorRunToRow)).execute();
|
|
8093
|
-
}
|
|
8249
|
+
await chunkedInsert(trx, "scan_extractor_runs", extractorRuns.map(extractorRunToRow));
|
|
8094
8250
|
}
|
|
8095
8251
|
async function upsertEnrichmentLayer(trx, result, renameOps, enrichments) {
|
|
8096
8252
|
const enrichmentLivePaths = new Set(result.nodes.map((n) => n.path));
|
|
8097
8253
|
for (const op of renameOps) {
|
|
8098
8254
|
await trx.updateTable("node_enrichments").set({ nodePath: op.to }).where("nodePath", "=", op.from).execute();
|
|
8099
8255
|
}
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
await trx.deleteFrom("node_enrichments").execute();
|
|
8256
|
+
const existingEnrichmentPaths = await trx.selectFrom("node_enrichments").select("nodePath").distinct().execute();
|
|
8257
|
+
const deadEnrichmentPaths = existingEnrichmentPaths.map((r) => r.nodePath).filter((p) => !enrichmentLivePaths.has(p));
|
|
8258
|
+
for (let start = 0; start < deadEnrichmentPaths.length; start += MAX_SQL_VARS) {
|
|
8259
|
+
await trx.deleteFrom("node_enrichments").where("nodePath", "in", deadEnrichmentPaths.slice(start, start + MAX_SQL_VARS)).execute();
|
|
8105
8260
|
}
|
|
8106
8261
|
for (const enrichment of enrichments) {
|
|
8107
8262
|
const row = enrichmentToRow(enrichment);
|
|
@@ -8286,8 +8441,9 @@ function projectOversizedColumns(result) {
|
|
|
8286
8441
|
}
|
|
8287
8442
|
function projectNodeLimitColumns(result) {
|
|
8288
8443
|
return {
|
|
8289
|
-
|
|
8290
|
-
|
|
8444
|
+
scanCeiling: result.scanCeiling ?? 5e4,
|
|
8445
|
+
scanTruncated: result.scanTruncated ? 1 : 0,
|
|
8446
|
+
maxRenderNodes: result.maxRenderNodes ?? 256
|
|
8291
8447
|
};
|
|
8292
8448
|
}
|
|
8293
8449
|
function extractorRunToRow(record) {
|
|
@@ -8462,11 +8618,16 @@ var SqliteStorageAdapter = class {
|
|
|
8462
8618
|
this.scans = {
|
|
8463
8619
|
persist: (result, opts) => persistScansThroughNonTx(this.db, result, opts),
|
|
8464
8620
|
load: () => loadScanResult(this.db),
|
|
8621
|
+
loadMeta: async () => loadScanMeta(this.db, await countRows(this.db)),
|
|
8465
8622
|
loadExtractorRuns: () => loadExtractorRuns(this.db),
|
|
8466
8623
|
loadNodeEnrichments: () => loadNodeEnrichments(this.db),
|
|
8467
8624
|
countRows: () => countRows(this.db),
|
|
8468
8625
|
findNodes: (filter) => findNodes(this.db, filter),
|
|
8469
|
-
findNode: (path2) => findNode(this.db, path2)
|
|
8626
|
+
findNode: (path2) => findNode(this.db, path2),
|
|
8627
|
+
listLiteNodes: () => loadLiteNodes(this.db),
|
|
8628
|
+
issueCountsByPath: () => loadIssueCountsByPath(this.db),
|
|
8629
|
+
effectiveMaxRenderNodes: () => loadEffectiveMaxRenderNodes(this.db),
|
|
8630
|
+
loadBranch: (prefixes, limit) => loadBranch(this.db, prefixes, limit)
|
|
8470
8631
|
};
|
|
8471
8632
|
this.contributions = {
|
|
8472
8633
|
listForNode: (nodePath) => loadContributionsForNode(this.db, nodePath),
|
|
@@ -8601,8 +8762,8 @@ async function findNodes(db, filter) {
|
|
|
8601
8762
|
query = query.where(
|
|
8602
8763
|
({ exists, selectFrom, ref }) => exists(
|
|
8603
8764
|
selectFrom(
|
|
8604
|
-
|
|
8605
|
-
).innerJoin("scan_issues", (j) => j.onTrue()).select(
|
|
8765
|
+
sql4`json_each(scan_issues.node_ids_json)`.as("je")
|
|
8766
|
+
).innerJoin("scan_issues", (j) => j.onTrue()).select(sql4`1`.as("one")).whereRef(sql4.ref("je.value"), "=", ref("scan_nodes.path"))
|
|
8606
8767
|
)
|
|
8607
8768
|
);
|
|
8608
8769
|
}
|
|
@@ -8674,8 +8835,8 @@ function applyIssueFilters(query, filter) {
|
|
|
8674
8835
|
q = q.where(
|
|
8675
8836
|
({ exists, selectFrom }) => exists(
|
|
8676
8837
|
selectFrom(
|
|
8677
|
-
|
|
8678
|
-
).select(
|
|
8838
|
+
sql4`json_each(scan_issues.node_ids_json)`.as("je")
|
|
8839
|
+
).select(sql4`1`.as("one")).where(sql4.ref("je.value"), "=", target)
|
|
8679
8840
|
)
|
|
8680
8841
|
);
|
|
8681
8842
|
}
|
|
@@ -8686,14 +8847,14 @@ function applyIssueFilters(query, filter) {
|
|
|
8686
8847
|
}
|
|
8687
8848
|
function applyNodePathsFilter(query, nodePaths) {
|
|
8688
8849
|
if (nodePaths.length === 0) {
|
|
8689
|
-
return query.where(
|
|
8850
|
+
return query.where(sql4`0`, "=", 1);
|
|
8690
8851
|
}
|
|
8691
8852
|
const targets = [...nodePaths];
|
|
8692
8853
|
return query.where(
|
|
8693
8854
|
({ exists, selectFrom }) => exists(
|
|
8694
8855
|
selectFrom(
|
|
8695
|
-
|
|
8696
|
-
).select(
|
|
8856
|
+
sql4`json_each(scan_issues.node_ids_json)`.as("je")
|
|
8857
|
+
).select(sql4`1`.as("one")).where(sql4.ref("je.value"), "in", targets)
|
|
8697
8858
|
)
|
|
8698
8859
|
);
|
|
8699
8860
|
}
|
|
@@ -10632,7 +10793,7 @@ async function buildEnabledResolver(ctx) {
|
|
|
10632
10793
|
|
|
10633
10794
|
// kernel/scan/walk-content.ts
|
|
10634
10795
|
import { readFile, readdir, lstat } from "fs/promises";
|
|
10635
|
-
import { join as join9, relative as relative2, sep as sep3 } from "path";
|
|
10796
|
+
import { isAbsolute as isAbsolute4, join as join9, relative as relative2, resolve as resolve18, sep as sep3 } from "path";
|
|
10636
10797
|
|
|
10637
10798
|
// kernel/scan/ignore.ts
|
|
10638
10799
|
import { existsSync as existsSync14, readFileSync as readFileSync14 } from "fs";
|
|
@@ -10812,32 +10973,115 @@ async function* walkContent(roots, options) {
|
|
|
10812
10973
|
const filter = options.ignoreFilter ?? buildIgnoreFilter();
|
|
10813
10974
|
const extensions = options.extensions;
|
|
10814
10975
|
const sizeLimit = buildSizeLimit(options);
|
|
10976
|
+
if (options.scopedPaths !== void 0) {
|
|
10977
|
+
yield* walkScoped(roots, options.scopedPaths, extensions, sizeLimit, parser);
|
|
10978
|
+
return;
|
|
10979
|
+
}
|
|
10815
10980
|
for (const root of roots) {
|
|
10816
10981
|
for await (const entry of walkRoot(root, root, filter, extensions, sizeLimit)) {
|
|
10817
10982
|
const relPath = relative2(root, entry.full).split(sep3).join("/");
|
|
10818
|
-
|
|
10819
|
-
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10830
|
-
|
|
10831
|
-
|
|
10832
|
-
|
|
10833
|
-
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
10983
|
+
const rec = await traversedEntryToNode(entry, relPath, options.priorMtimes, parser);
|
|
10984
|
+
if (rec !== null) yield rec;
|
|
10985
|
+
}
|
|
10986
|
+
}
|
|
10987
|
+
}
|
|
10988
|
+
async function traversedEntryToNode(entry, relPath, priorMtimes, parser) {
|
|
10989
|
+
const priorMtime = priorMtimes?.get(relPath);
|
|
10990
|
+
if (priorMtime !== void 0 && priorMtime === entry.modifiedAtMs) {
|
|
10991
|
+
return buildUnchangedRecord(entry.full, relPath, entry.modifiedAtMs, parser);
|
|
10992
|
+
}
|
|
10993
|
+
const parsed = await readAndParse(entry.full, relPath, parser);
|
|
10994
|
+
if (parsed === null) return null;
|
|
10995
|
+
return {
|
|
10996
|
+
path: relPath,
|
|
10997
|
+
body: parsed.body,
|
|
10998
|
+
frontmatterRaw: parsed.frontmatterRaw,
|
|
10999
|
+
frontmatter: parsed.frontmatter,
|
|
11000
|
+
// File mtime from the TOCTOU `lstat` (zero extra syscalls).
|
|
11001
|
+
// Threaded onto the persisted `Node` as `modifiedAtMs`.
|
|
11002
|
+
modifiedAtMs: entry.modifiedAtMs,
|
|
11003
|
+
// Audit L1: forward parser diagnostics (e.g. malformed YAML)
|
|
11004
|
+
// through the IRawNode surface so the orchestrator can
|
|
11005
|
+
// convert them into warn-level kernel `Issue` rows. Omitted
|
|
11006
|
+
// when the parser reported no issues (happy path).
|
|
11007
|
+
...parsed.parseIssues ? { parseIssues: parsed.parseIssues } : {}
|
|
11008
|
+
};
|
|
11009
|
+
}
|
|
11010
|
+
function buildUnchangedRecord(full, relPath, modifiedAtMs, parser) {
|
|
11011
|
+
return {
|
|
11012
|
+
path: relPath,
|
|
11013
|
+
body: "",
|
|
11014
|
+
frontmatterRaw: "",
|
|
11015
|
+
frontmatter: {},
|
|
11016
|
+
modifiedAtMs,
|
|
11017
|
+
unchanged: true,
|
|
11018
|
+
reread: async () => {
|
|
11019
|
+
const re = await readAndParse(full, relPath, parser);
|
|
11020
|
+
return re ?? { body: "", frontmatterRaw: "", frontmatter: {} };
|
|
10839
11021
|
}
|
|
11022
|
+
};
|
|
11023
|
+
}
|
|
11024
|
+
async function* walkScoped(roots, scopedPaths, extensions, sizeLimit, parser) {
|
|
11025
|
+
const absRoots = roots.map((r) => isAbsolute4(r) ? r : resolve18(r));
|
|
11026
|
+
for (const scoped of scopedPaths) {
|
|
11027
|
+
const rec = await scopedPathToNode(scoped, absRoots, extensions, sizeLimit, parser);
|
|
11028
|
+
if (rec !== null) yield rec;
|
|
11029
|
+
}
|
|
11030
|
+
}
|
|
11031
|
+
async function scopedPathToNode(scoped, absRoots, extensions, sizeLimit, parser) {
|
|
11032
|
+
const full = isAbsolute4(scoped) ? scoped : resolve18(scoped);
|
|
11033
|
+
const relPath = relativeFromRoots(full, absRoots);
|
|
11034
|
+
if (relPath === null) return null;
|
|
11035
|
+
if (!hasMatchingExtension(full, extensions)) return null;
|
|
11036
|
+
const s = await statRegularFile(full, relPath, sizeLimit);
|
|
11037
|
+
if (s === null) return null;
|
|
11038
|
+
const parsed = await readAndParse(full, relPath, parser);
|
|
11039
|
+
if (parsed === null) return null;
|
|
11040
|
+
return {
|
|
11041
|
+
path: relPath,
|
|
11042
|
+
body: parsed.body,
|
|
11043
|
+
frontmatterRaw: parsed.frontmatterRaw,
|
|
11044
|
+
frontmatter: parsed.frontmatter,
|
|
11045
|
+
modifiedAtMs: Math.round(s.mtimeMs),
|
|
11046
|
+
...parsed.parseIssues ? { parseIssues: parsed.parseIssues } : {}
|
|
11047
|
+
};
|
|
11048
|
+
}
|
|
11049
|
+
async function statRegularFile(full, relPath, sizeLimit) {
|
|
11050
|
+
let s;
|
|
11051
|
+
try {
|
|
11052
|
+
s = await lstat(full);
|
|
11053
|
+
} catch {
|
|
11054
|
+
return null;
|
|
11055
|
+
}
|
|
11056
|
+
if (!s.isFile()) return null;
|
|
11057
|
+
if (sizeLimit.maxFileSizeBytes !== void 0 && s.size > sizeLimit.maxFileSizeBytes) {
|
|
11058
|
+
sizeLimit.onOversizedFile?.({ path: relPath, bytes: s.size });
|
|
11059
|
+
return null;
|
|
11060
|
+
}
|
|
11061
|
+
return s;
|
|
11062
|
+
}
|
|
11063
|
+
function relativeFromRoots(full, absRoots) {
|
|
11064
|
+
for (const root of absRoots) {
|
|
11065
|
+
const rel = relative2(root, full);
|
|
11066
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute4(rel)) continue;
|
|
11067
|
+
return rel.split(sep3).join("/");
|
|
11068
|
+
}
|
|
11069
|
+
return null;
|
|
11070
|
+
}
|
|
11071
|
+
async function readAndParse(full, relPath, parser) {
|
|
11072
|
+
let raw;
|
|
11073
|
+
try {
|
|
11074
|
+
raw = await readFile(full, "utf8");
|
|
11075
|
+
} catch {
|
|
11076
|
+
return null;
|
|
10840
11077
|
}
|
|
11078
|
+
const parsed = parser.parse(raw, relPath);
|
|
11079
|
+
return {
|
|
11080
|
+
body: parsed.body,
|
|
11081
|
+
frontmatterRaw: parsed.frontmatterRaw,
|
|
11082
|
+
frontmatter: parsed.frontmatter,
|
|
11083
|
+
...parsed.issues && parsed.issues.length > 0 ? { parseIssues: parsed.issues } : {}
|
|
11084
|
+
};
|
|
10841
11085
|
}
|
|
10842
11086
|
function buildSizeLimit(options) {
|
|
10843
11087
|
const sizeLimit = {};
|
|
@@ -10894,18 +11138,24 @@ function resolveProviderWalk(provider) {
|
|
|
10894
11138
|
return walk3;
|
|
10895
11139
|
}
|
|
10896
11140
|
const read = provider.read ?? DEFAULT_READ_CONFIG;
|
|
10897
|
-
return (roots, options) =>
|
|
10898
|
-
|
|
10899
|
-
|
|
10900
|
-
|
|
10901
|
-
|
|
10902
|
-
|
|
10903
|
-
if (options?.maxFileSizeBytes !== void 0) {
|
|
10904
|
-
walkOptions.maxFileSizeBytes = options.maxFileSizeBytes;
|
|
10905
|
-
}
|
|
10906
|
-
if (options?.onOversizedFile) walkOptions.onOversizedFile = options.onOversizedFile;
|
|
10907
|
-
return walkContent(roots, walkOptions);
|
|
11141
|
+
return (roots, options) => walkContent(roots, buildWalkContentOptions(read, options));
|
|
11142
|
+
}
|
|
11143
|
+
function buildWalkContentOptions(read, options) {
|
|
11144
|
+
const walkOptions = {
|
|
11145
|
+
extensions: read.extensions,
|
|
11146
|
+
parser: read.parser
|
|
10908
11147
|
};
|
|
11148
|
+
if (options) copyOptionalWalkOptions(walkOptions, options);
|
|
11149
|
+
return walkOptions;
|
|
11150
|
+
}
|
|
11151
|
+
function copyOptionalWalkOptions(walkOptions, options) {
|
|
11152
|
+
if (options.ignoreFilter) walkOptions.ignoreFilter = options.ignoreFilter;
|
|
11153
|
+
if (options.maxFileSizeBytes !== void 0) {
|
|
11154
|
+
walkOptions.maxFileSizeBytes = options.maxFileSizeBytes;
|
|
11155
|
+
}
|
|
11156
|
+
if (options.onOversizedFile) walkOptions.onOversizedFile = options.onOversizedFile;
|
|
11157
|
+
if (options.priorMtimes) walkOptions.priorMtimes = options.priorMtimes;
|
|
11158
|
+
if (options.scopedPaths) walkOptions.scopedPaths = options.scopedPaths;
|
|
10909
11159
|
}
|
|
10910
11160
|
|
|
10911
11161
|
// kernel/extensions/collect-view-contributions.ts
|
|
@@ -10995,7 +11245,7 @@ function isExtensionInstance(v) {
|
|
|
10995
11245
|
}
|
|
10996
11246
|
|
|
10997
11247
|
// core/runtime/plugin-runtime/warnings.ts
|
|
10998
|
-
import { resolve as
|
|
11248
|
+
import { resolve as resolve19 } from "path";
|
|
10999
11249
|
|
|
11000
11250
|
// kernel/util/text.ts
|
|
11001
11251
|
function truncateHead(s, max) {
|
|
@@ -11045,7 +11295,7 @@ function resolveRuntimeContext(opts) {
|
|
|
11045
11295
|
return opts.runtimeContext ?? defaultRuntimeContext();
|
|
11046
11296
|
}
|
|
11047
11297
|
function resolveSearchPaths(opts, ctx) {
|
|
11048
|
-
if (opts.pluginDir) return [
|
|
11298
|
+
if (opts.pluginDir) return [resolve19(opts.pluginDir)];
|
|
11049
11299
|
return [defaultProjectPluginsDir(ctx)];
|
|
11050
11300
|
}
|
|
11051
11301
|
|
|
@@ -11596,11 +11846,11 @@ function resolveActiveProvider(cwd, providers = []) {
|
|
|
11596
11846
|
}
|
|
11597
11847
|
|
|
11598
11848
|
// cli/util/path-display.ts
|
|
11599
|
-
import { isAbsolute as
|
|
11849
|
+
import { isAbsolute as isAbsolute5, relative as pathRelative } from "path";
|
|
11600
11850
|
function relativeIfBelow(path, cwd) {
|
|
11601
|
-
if (!
|
|
11851
|
+
if (!isAbsolute5(path)) return path;
|
|
11602
11852
|
const rel = pathRelative(cwd, path);
|
|
11603
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
11853
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute5(rel)) return path;
|
|
11604
11854
|
return rel;
|
|
11605
11855
|
}
|
|
11606
11856
|
|
|
@@ -12324,7 +12574,7 @@ var CONFIG_COMMANDS = [
|
|
|
12324
12574
|
|
|
12325
12575
|
// cli/commands/conformance.ts
|
|
12326
12576
|
import { existsSync as existsSync19, readFileSync as readFileSync16 } from "fs";
|
|
12327
|
-
import { dirname as dirname12, resolve as
|
|
12577
|
+
import { dirname as dirname12, resolve as resolve22 } from "path";
|
|
12328
12578
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12329
12579
|
import { Command as Command5, Option as Option5 } from "clipanion";
|
|
12330
12580
|
|
|
@@ -12332,7 +12582,7 @@ import { Command as Command5, Option as Option5 } from "clipanion";
|
|
|
12332
12582
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
12333
12583
|
import { cpSync, existsSync as existsSync17, mkdtempSync, readdirSync as readdirSync5, readFileSync as readFileSync15, rmSync, statSync as statSync3 } from "fs";
|
|
12334
12584
|
import { tmpdir } from "os";
|
|
12335
|
-
import { isAbsolute as
|
|
12585
|
+
import { isAbsolute as isAbsolute6, join as join11, relative as relative3, resolve as resolve20 } from "path";
|
|
12336
12586
|
|
|
12337
12587
|
// conformance/i18n/runner.texts.ts
|
|
12338
12588
|
var CONFORMANCE_RUNNER_TEXTS = {
|
|
@@ -12494,14 +12744,14 @@ function replaceFixture(scope, fixturesRoot, fixture) {
|
|
|
12494
12744
|
cpSync(src, scope, { recursive: true });
|
|
12495
12745
|
}
|
|
12496
12746
|
function assertContained2(root, rel, label) {
|
|
12497
|
-
if (
|
|
12747
|
+
if (isAbsolute6(rel)) {
|
|
12498
12748
|
throw new Error(
|
|
12499
12749
|
tx(CONFORMANCE_RUNNER_TEXTS.pathMustBeRelative, { label, path: rel, anchor: root })
|
|
12500
12750
|
);
|
|
12501
12751
|
}
|
|
12502
|
-
const abs =
|
|
12752
|
+
const abs = resolve20(root, rel);
|
|
12503
12753
|
const r = relative3(root, abs);
|
|
12504
|
-
if (r.startsWith("..") ||
|
|
12754
|
+
if (r.startsWith("..") || isAbsolute6(r)) {
|
|
12505
12755
|
throw new Error(
|
|
12506
12756
|
tx(CONFORMANCE_RUNNER_TEXTS.pathEscapesAnchor, { label, path: rel, anchor: root })
|
|
12507
12757
|
);
|
|
@@ -12526,7 +12776,7 @@ function evaluateAssertion(a, ctx) {
|
|
|
12526
12776
|
} catch (err) {
|
|
12527
12777
|
return { ok: false, type: a.type, reason: formatErrorMessage(err) };
|
|
12528
12778
|
}
|
|
12529
|
-
const abs =
|
|
12779
|
+
const abs = resolve20(ctx.scope, a.path);
|
|
12530
12780
|
return existsSync17(abs) ? { ok: true, type: a.type } : {
|
|
12531
12781
|
ok: false,
|
|
12532
12782
|
type: a.type,
|
|
@@ -12541,7 +12791,7 @@ function evaluateAssertion(a, ctx) {
|
|
|
12541
12791
|
return { ok: false, type: a.type, reason: formatErrorMessage(err) };
|
|
12542
12792
|
}
|
|
12543
12793
|
const fixturePath = join11(ctx.fixturesRoot, a.fixture);
|
|
12544
|
-
const targetPath =
|
|
12794
|
+
const targetPath = resolve20(ctx.scope, a.path);
|
|
12545
12795
|
if (!existsSync17(targetPath)) {
|
|
12546
12796
|
return {
|
|
12547
12797
|
ok: false,
|
|
@@ -12724,7 +12974,7 @@ var CONFORMANCE_TEXTS = {
|
|
|
12724
12974
|
|
|
12725
12975
|
// cli/util/conformance-scopes.ts
|
|
12726
12976
|
import { existsSync as existsSync18, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
12727
|
-
import { dirname as dirname11, resolve as
|
|
12977
|
+
import { dirname as dirname11, resolve as resolve21 } from "path";
|
|
12728
12978
|
import { createRequire as createRequire6 } from "module";
|
|
12729
12979
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
12730
12980
|
function resolveSpecRoot4() {
|
|
@@ -12742,7 +12992,7 @@ function resolveCliWorkspaceRoot() {
|
|
|
12742
12992
|
const here = dirname11(fileURLToPath3(import.meta.url));
|
|
12743
12993
|
let cursor = here;
|
|
12744
12994
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
12745
|
-
const candidate =
|
|
12995
|
+
const candidate = resolve21(cursor, "plugins");
|
|
12746
12996
|
if (existsSync18(candidate) && statSync4(candidate).isDirectory()) {
|
|
12747
12997
|
return cursor;
|
|
12748
12998
|
}
|
|
@@ -12762,12 +13012,12 @@ function collectProviderScopes(specRoot) {
|
|
|
12762
13012
|
} catch {
|
|
12763
13013
|
return out;
|
|
12764
13014
|
}
|
|
12765
|
-
const pluginsRoot =
|
|
13015
|
+
const pluginsRoot = resolve21(workspaceRoot, "plugins");
|
|
12766
13016
|
if (!existsSync18(pluginsRoot)) return out;
|
|
12767
13017
|
for (const pluginEntry of readdirSync6(pluginsRoot)) {
|
|
12768
|
-
const pluginDir =
|
|
13018
|
+
const pluginDir = resolve21(pluginsRoot, pluginEntry);
|
|
12769
13019
|
if (!isDir(pluginDir)) continue;
|
|
12770
|
-
const providersRoot =
|
|
13020
|
+
const providersRoot = resolve21(pluginDir, "providers");
|
|
12771
13021
|
if (!isDir(providersRoot)) continue;
|
|
12772
13022
|
collectPluginProviderScopes(providersRoot, specRoot, out);
|
|
12773
13023
|
}
|
|
@@ -12782,12 +13032,12 @@ function isDir(path) {
|
|
|
12782
13032
|
}
|
|
12783
13033
|
function collectPluginProviderScopes(providersRoot, specRoot, out) {
|
|
12784
13034
|
for (const entry of readdirSync6(providersRoot)) {
|
|
12785
|
-
const providerDir =
|
|
13035
|
+
const providerDir = resolve21(providersRoot, entry);
|
|
12786
13036
|
if (!isDir(providerDir)) continue;
|
|
12787
|
-
const conformanceDir =
|
|
13037
|
+
const conformanceDir = resolve21(providerDir, "conformance");
|
|
12788
13038
|
if (!existsSync18(conformanceDir)) continue;
|
|
12789
|
-
const casesDir =
|
|
12790
|
-
const fixturesDir =
|
|
13039
|
+
const casesDir = resolve21(conformanceDir, "cases");
|
|
13040
|
+
const fixturesDir = resolve21(conformanceDir, "fixtures");
|
|
12791
13041
|
if (!existsSync18(casesDir) || !existsSync18(fixturesDir)) continue;
|
|
12792
13042
|
out.push({
|
|
12793
13043
|
id: `provider:${entry}`,
|
|
@@ -12804,8 +13054,8 @@ function specScope(specRoot) {
|
|
|
12804
13054
|
id: "spec",
|
|
12805
13055
|
kind: "spec",
|
|
12806
13056
|
label: "spec",
|
|
12807
|
-
casesDir:
|
|
12808
|
-
fixturesDir:
|
|
13057
|
+
casesDir: resolve21(specRoot, "conformance", "cases"),
|
|
13058
|
+
fixturesDir: resolve21(specRoot, "conformance", "fixtures"),
|
|
12809
13059
|
specRoot
|
|
12810
13060
|
};
|
|
12811
13061
|
}
|
|
@@ -12827,7 +13077,7 @@ function selectConformanceScopes(scope) {
|
|
|
12827
13077
|
}
|
|
12828
13078
|
function listCaseFiles(scope) {
|
|
12829
13079
|
if (!existsSync18(scope.casesDir)) return [];
|
|
12830
|
-
return readdirSync6(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) =>
|
|
13080
|
+
return readdirSync6(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) => resolve21(scope.casesDir, entry));
|
|
12831
13081
|
}
|
|
12832
13082
|
|
|
12833
13083
|
// cli/commands/conformance.ts
|
|
@@ -12844,13 +13094,13 @@ function resolveBinary() {
|
|
|
12844
13094
|
const here = dirname12(fileURLToPath4(import.meta.url));
|
|
12845
13095
|
let cursor = here;
|
|
12846
13096
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
12847
|
-
const candidate =
|
|
13097
|
+
const candidate = resolve22(cursor, "bin", "sm.js");
|
|
12848
13098
|
if (existsSync19(candidate)) return candidate;
|
|
12849
13099
|
const parent = dirname12(cursor);
|
|
12850
13100
|
if (parent === cursor) break;
|
|
12851
13101
|
cursor = parent;
|
|
12852
13102
|
}
|
|
12853
|
-
return
|
|
13103
|
+
return resolve22(here, "..", "..", "bin", "sm.js");
|
|
12854
13104
|
}
|
|
12855
13105
|
var ConformanceRunCommand = class extends SmCommand {
|
|
12856
13106
|
static paths = [["conformance", "run"]];
|
|
@@ -13103,7 +13353,7 @@ function writeStreamSnippet(stream, header, text) {
|
|
|
13103
13353
|
var CONFORMANCE_COMMANDS = [ConformanceRunCommand];
|
|
13104
13354
|
|
|
13105
13355
|
// cli/commands/db/backup.ts
|
|
13106
|
-
import { dirname as dirname13, join as join12, resolve as
|
|
13356
|
+
import { dirname as dirname13, join as join12, resolve as resolve23 } from "path";
|
|
13107
13357
|
import { Command as Command6, Option as Option6 } from "clipanion";
|
|
13108
13358
|
|
|
13109
13359
|
// cli/i18n/db.texts.ts
|
|
@@ -13216,7 +13466,7 @@ var DbBackupCommand = class extends SmCommand {
|
|
|
13216
13466
|
const exit = requireDbOrExit(path, this.context.stderr);
|
|
13217
13467
|
if (exit !== null) return exit;
|
|
13218
13468
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13219
|
-
const outPath = this.out ?
|
|
13469
|
+
const outPath = this.out ? resolve23(this.out) : join12(dirname13(path), "backups", `${ts}.db`);
|
|
13220
13470
|
await withSqlite({ databasePath: path, autoMigrate: false }, async (storage) => {
|
|
13221
13471
|
storage.migrations.writeBackup(outPath);
|
|
13222
13472
|
});
|
|
@@ -13233,7 +13483,7 @@ var DbBackupCommand = class extends SmCommand {
|
|
|
13233
13483
|
|
|
13234
13484
|
// cli/commands/db/restore.ts
|
|
13235
13485
|
import { chmod, copyFile, mkdir, rm } from "fs/promises";
|
|
13236
|
-
import { dirname as dirname14, resolve as
|
|
13486
|
+
import { dirname as dirname14, resolve as resolve24 } from "path";
|
|
13237
13487
|
import { Command as Command7, Option as Option7 } from "clipanion";
|
|
13238
13488
|
|
|
13239
13489
|
// cli/util/fs.ts
|
|
@@ -13283,7 +13533,7 @@ var DbRestoreCommand = class extends SmCommand {
|
|
|
13283
13533
|
});
|
|
13284
13534
|
async run() {
|
|
13285
13535
|
const target = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
|
|
13286
|
-
const sourcePath =
|
|
13536
|
+
const sourcePath = resolve24(this.source);
|
|
13287
13537
|
const stderrAnsi = this.ansiFor("stderr");
|
|
13288
13538
|
const sourceStat = await statOrNull(sourcePath);
|
|
13289
13539
|
if (!sourceStat) {
|
|
@@ -13524,7 +13774,7 @@ var DbShellCommand = class extends SmCommand {
|
|
|
13524
13774
|
|
|
13525
13775
|
// cli/commands/db/browser.ts
|
|
13526
13776
|
import { spawn, spawnSync as spawnSync4 } from "child_process";
|
|
13527
|
-
import { resolve as
|
|
13777
|
+
import { resolve as resolve25 } from "path";
|
|
13528
13778
|
import { Command as Command10, Option as Option9 } from "clipanion";
|
|
13529
13779
|
var DbBrowserCommand = class extends SmCommand {
|
|
13530
13780
|
static paths = [["db", "browser"]];
|
|
@@ -13557,7 +13807,7 @@ var DbBrowserCommand = class extends SmCommand {
|
|
|
13557
13807
|
});
|
|
13558
13808
|
positional = Option9.String({ required: false });
|
|
13559
13809
|
async run() {
|
|
13560
|
-
const path = this.positional ?
|
|
13810
|
+
const path = this.positional ? resolve25(this.positional) : resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
|
|
13561
13811
|
if (!assertDbExists(path, this.context.stderr)) {
|
|
13562
13812
|
this.printer.error(DB_TEXTS.browserRunScanFirstHint);
|
|
13563
13813
|
return ExitCode.NotFound;
|
|
@@ -13647,8 +13897,8 @@ function listSchemaObjects(db, tables) {
|
|
|
13647
13897
|
return db.prepare(`${baseQuery} ORDER BY rootpage`).all();
|
|
13648
13898
|
}
|
|
13649
13899
|
const placeholders = tables.map(() => "?").join(",");
|
|
13650
|
-
const
|
|
13651
|
-
return db.prepare(
|
|
13900
|
+
const sql5 = `${baseQuery} AND (name IN (${placeholders}) OR tbl_name IN (${placeholders})) ORDER BY rootpage`;
|
|
13901
|
+
return db.prepare(sql5).all(...tables, ...tables);
|
|
13652
13902
|
}
|
|
13653
13903
|
function writeTableData(db, out, tableName) {
|
|
13654
13904
|
const quoted = `"${tableName.replace(/"/g, '""')}"`;
|
|
@@ -14475,7 +14725,7 @@ var GraphCommand = class extends SmCommand {
|
|
|
14475
14725
|
// cli/commands/help.ts
|
|
14476
14726
|
import { readFileSync as readFileSync17 } from "fs";
|
|
14477
14727
|
import { createRequire as createRequire7 } from "module";
|
|
14478
|
-
import { resolve as
|
|
14728
|
+
import { resolve as resolve26 } from "path";
|
|
14479
14729
|
import { Command as Command15, Option as Option14 } from "clipanion";
|
|
14480
14730
|
|
|
14481
14731
|
// cli/i18n/help.texts.ts
|
|
@@ -14789,7 +15039,7 @@ function resolveSpecVersion() {
|
|
|
14789
15039
|
try {
|
|
14790
15040
|
const req = createRequire7(import.meta.url);
|
|
14791
15041
|
const indexPath = req.resolve("@skill-map/spec/index.json");
|
|
14792
|
-
const pkgPath =
|
|
15042
|
+
const pkgPath = resolve26(indexPath, "..", "package.json");
|
|
14793
15043
|
const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
|
|
14794
15044
|
return pkg.version;
|
|
14795
15045
|
} catch {
|
|
@@ -15097,7 +15347,7 @@ function registeredVerbPaths(cli2) {
|
|
|
15097
15347
|
|
|
15098
15348
|
// cli/commands/hooks.ts
|
|
15099
15349
|
import { chmod as chmod2, mkdir as mkdir3, readFile as readFile2, stat as stat2, writeFile } from "fs/promises";
|
|
15100
|
-
import { dirname as dirname16, resolve as
|
|
15350
|
+
import { dirname as dirname16, resolve as resolve27 } from "path";
|
|
15101
15351
|
import { Command as Command16, Option as Option15 } from "clipanion";
|
|
15102
15352
|
|
|
15103
15353
|
// cli/i18n/hooks.texts.ts
|
|
@@ -15200,8 +15450,8 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
15200
15450
|
);
|
|
15201
15451
|
return ExitCode.NotFound;
|
|
15202
15452
|
}
|
|
15203
|
-
const hooksDir =
|
|
15204
|
-
const hookPath =
|
|
15453
|
+
const hooksDir = resolve27(repoRoot, ".git", "hooks");
|
|
15454
|
+
const hookPath = resolve27(hooksDir, "pre-commit");
|
|
15205
15455
|
const existing = await pathExists(hookPath) ? await readFile2(hookPath, "utf8") : null;
|
|
15206
15456
|
const planned2 = computePlannedHookContent(existing);
|
|
15207
15457
|
if (planned2.kind === "already-installed") {
|
|
@@ -15259,7 +15509,7 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
15259
15509
|
async function findGitRepoRoot(cwd) {
|
|
15260
15510
|
let current = cwd;
|
|
15261
15511
|
while (true) {
|
|
15262
|
-
if (await pathExists(
|
|
15512
|
+
if (await pathExists(resolve27(current, ".git"))) return current;
|
|
15263
15513
|
const parent = dirname16(current);
|
|
15264
15514
|
if (parent === current) return null;
|
|
15265
15515
|
current = parent;
|
|
@@ -15270,8 +15520,8 @@ function computePlannedHookContent(existing) {
|
|
|
15270
15520
|
if (existing.includes(SKILL_MAP_MARKER)) {
|
|
15271
15521
|
return { kind: "already-installed", content: existing };
|
|
15272
15522
|
}
|
|
15273
|
-
const
|
|
15274
|
-
return { kind: "chained", content: existing +
|
|
15523
|
+
const sep9 = existing.endsWith("\n") ? "" : "\n";
|
|
15524
|
+
return { kind: "chained", content: existing + sep9 + "\n" + SKILL_MAP_BLOCK };
|
|
15275
15525
|
}
|
|
15276
15526
|
async function ensureExecutableBit(path) {
|
|
15277
15527
|
const mode = (await stat2(path)).mode;
|
|
@@ -15286,7 +15536,7 @@ import { Command as Command17, Option as Option16 } from "clipanion";
|
|
|
15286
15536
|
|
|
15287
15537
|
// kernel/orchestrator/index.ts
|
|
15288
15538
|
import { existsSync as existsSync23, statSync as statSync6 } from "fs";
|
|
15289
|
-
import { isAbsolute as
|
|
15539
|
+
import { isAbsolute as isAbsolute9, resolve as resolve30 } from "path";
|
|
15290
15540
|
import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
|
|
15291
15541
|
|
|
15292
15542
|
// kernel/i18n/orchestrator.texts.ts
|
|
@@ -16232,7 +16482,7 @@ function collectBrokenLinks(links, nodes, ctx) {
|
|
|
16232
16482
|
return broken;
|
|
16233
16483
|
}
|
|
16234
16484
|
function applyResolution(link, indexes, ctx) {
|
|
16235
|
-
const resolution =
|
|
16485
|
+
const resolution = resolve28(link, indexes, ctx);
|
|
16236
16486
|
if (resolution === "none") return;
|
|
16237
16487
|
link.resolvedTarget = resolution;
|
|
16238
16488
|
}
|
|
@@ -16247,7 +16497,7 @@ function buildIndexes(nodes, ctx) {
|
|
|
16247
16497
|
}
|
|
16248
16498
|
return { byPath: byPath3, byName, nodeByPath };
|
|
16249
16499
|
}
|
|
16250
|
-
function
|
|
16500
|
+
function resolve28(link, indexes, ctx) {
|
|
16251
16501
|
if (indexes.byPath.has(link.target)) return link.target;
|
|
16252
16502
|
return resolveByName(link, indexes, ctx);
|
|
16253
16503
|
}
|
|
@@ -16635,6 +16885,9 @@ function detectRenamesAndOrphans(prior, current, issues, silenced) {
|
|
|
16635
16885
|
return ops;
|
|
16636
16886
|
}
|
|
16637
16887
|
|
|
16888
|
+
// kernel/orchestrator/walk.ts
|
|
16889
|
+
import { isAbsolute as isAbsolute8, resolve as resolve29 } from "path";
|
|
16890
|
+
|
|
16638
16891
|
// kernel/sidecar/drift.ts
|
|
16639
16892
|
function computeDriftStatus(args2) {
|
|
16640
16893
|
const bodyDrift = args2.storedBodyHash !== args2.liveBodyHash;
|
|
@@ -16689,7 +16942,7 @@ function safeIsFile(path) {
|
|
|
16689
16942
|
// kernel/orchestrator/node-build.ts
|
|
16690
16943
|
import { createHash as createHash2 } from "crypto";
|
|
16691
16944
|
import { existsSync as existsSync22 } from "fs";
|
|
16692
|
-
import { isAbsolute as
|
|
16945
|
+
import { isAbsolute as isAbsolute7, resolve as resolvePath } from "path";
|
|
16693
16946
|
import "js-tiktoken/lite";
|
|
16694
16947
|
import yaml4 from "js-yaml";
|
|
16695
16948
|
|
|
@@ -16852,7 +17105,7 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
|
|
|
16852
17105
|
};
|
|
16853
17106
|
}
|
|
16854
17107
|
function resolveAbsoluteMdPath(relativePath2, roots) {
|
|
16855
|
-
if (
|
|
17108
|
+
if (isAbsolute7(relativePath2)) {
|
|
16856
17109
|
return existsSync22(relativePath2) ? relativePath2 : null;
|
|
16857
17110
|
}
|
|
16858
17111
|
for (const root of roots) {
|
|
@@ -16925,34 +17178,46 @@ async function walkAndExtract(opts) {
|
|
|
16925
17178
|
oversizedSeen.add(info.path);
|
|
16926
17179
|
oversizedFiles.push(info);
|
|
16927
17180
|
};
|
|
17181
|
+
const priorMtimes = buildPriorMtimes(opts);
|
|
16928
17182
|
const walkOptions = {
|
|
16929
17183
|
...opts.ignoreFilter ? { ignoreFilter: opts.ignoreFilter } : {},
|
|
16930
17184
|
onOversizedFile,
|
|
16931
|
-
...opts.maxFileSizeBytes !== void 0 ? { maxFileSizeBytes: opts.maxFileSizeBytes } : {}
|
|
17185
|
+
...opts.maxFileSizeBytes !== void 0 ? { maxFileSizeBytes: opts.maxFileSizeBytes } : {},
|
|
17186
|
+
...priorMtimes ? { priorMtimes } : {}
|
|
16932
17187
|
};
|
|
16933
|
-
let filesWalked
|
|
17188
|
+
let filesWalked;
|
|
16934
17189
|
let index = 0;
|
|
16935
|
-
const
|
|
17190
|
+
const { effectiveScanCeiling, effectiveMaxRenderNodes } = resolveEffectiveCaps(opts);
|
|
16936
17191
|
let capReached = false;
|
|
16937
|
-
const activeProviders = opts.providers.filter(
|
|
16938
|
-
|
|
16939
|
-
|
|
16940
|
-
return provider.id === opts.activeProvider;
|
|
16941
|
-
});
|
|
17192
|
+
const activeProviders = opts.providers.filter(
|
|
17193
|
+
(provider) => providerParticipates(provider, opts.activeProvider)
|
|
17194
|
+
);
|
|
16942
17195
|
const advance = async (raw, provider) => {
|
|
16943
17196
|
const advanced = await processRawNode(raw, provider, wctx, accum, claimedPaths, index + 1);
|
|
16944
17197
|
if (advanced) index += 1;
|
|
16945
17198
|
};
|
|
16946
|
-
|
|
16947
|
-
|
|
16948
|
-
|
|
16949
|
-
|
|
16950
|
-
|
|
16951
|
-
|
|
16952
|
-
|
|
16953
|
-
|
|
16954
|
-
|
|
16955
|
-
}
|
|
17199
|
+
if (opts.incrementalChangedPaths !== void 0) {
|
|
17200
|
+
filesWalked = await walkIncremental({
|
|
17201
|
+
changedPaths: opts.incrementalChangedPaths,
|
|
17202
|
+
activeProviders,
|
|
17203
|
+
walkOptions,
|
|
17204
|
+
wctx,
|
|
17205
|
+
accum,
|
|
17206
|
+
claimedPaths,
|
|
17207
|
+
advance
|
|
17208
|
+
});
|
|
17209
|
+
} else {
|
|
17210
|
+
const full = await walkFullTraversal({
|
|
17211
|
+
activeProviders,
|
|
17212
|
+
roots: opts.roots,
|
|
17213
|
+
walkOptions,
|
|
17214
|
+
accum,
|
|
17215
|
+
claimedPaths,
|
|
17216
|
+
effectiveScanCeiling,
|
|
17217
|
+
advance
|
|
17218
|
+
});
|
|
17219
|
+
filesWalked = full.filesWalked;
|
|
17220
|
+
capReached = full.capReached;
|
|
16956
17221
|
}
|
|
16957
17222
|
const orphanSidecars = discoverOrphanSidecars(opts.roots);
|
|
16958
17223
|
return {
|
|
@@ -16963,9 +17228,9 @@ async function walkAndExtract(opts) {
|
|
|
16963
17228
|
frontmatterIssues: accum.frontmatterIssues,
|
|
16964
17229
|
filesWalked,
|
|
16965
17230
|
oversizedFiles,
|
|
16966
|
-
|
|
16967
|
-
|
|
16968
|
-
|
|
17231
|
+
scanCeiling: effectiveScanCeiling,
|
|
17232
|
+
scanTruncated: capReached,
|
|
17233
|
+
maxRenderNodes: effectiveMaxRenderNodes,
|
|
16969
17234
|
enrichments: [...accum.enrichmentBuffer.values()],
|
|
16970
17235
|
extractorRuns: accum.extractorRuns,
|
|
16971
17236
|
contributions: accum.contributionsBuffer,
|
|
@@ -16976,6 +17241,119 @@ async function walkAndExtract(opts) {
|
|
|
16976
17241
|
signals: accum.signals
|
|
16977
17242
|
};
|
|
16978
17243
|
}
|
|
17244
|
+
async function walkFullTraversal(args2) {
|
|
17245
|
+
let filesWalked = 0;
|
|
17246
|
+
let capReached = false;
|
|
17247
|
+
outer: for (const provider of args2.activeProviders) {
|
|
17248
|
+
for await (const raw of resolveProviderWalk(provider)(args2.roots, args2.walkOptions)) {
|
|
17249
|
+
filesWalked += 1;
|
|
17250
|
+
if (args2.claimedPaths.has(raw.path)) continue;
|
|
17251
|
+
if (args2.accum.nodes.length >= args2.effectiveScanCeiling) {
|
|
17252
|
+
capReached = true;
|
|
17253
|
+
break outer;
|
|
17254
|
+
}
|
|
17255
|
+
await args2.advance(raw, provider);
|
|
17256
|
+
}
|
|
17257
|
+
}
|
|
17258
|
+
return { filesWalked, capReached };
|
|
17259
|
+
}
|
|
17260
|
+
async function walkIncremental(args2) {
|
|
17261
|
+
const changed = expandSidecarPaths(args2.changedPaths.changed, args2.wctx.priorNodesByPath);
|
|
17262
|
+
const removed = expandSidecarPaths(args2.changedPaths.removed, args2.wctx.priorNodesByPath);
|
|
17263
|
+
let filesWalked = 0;
|
|
17264
|
+
const scopedAbs = [...changed].map((rel) => toAbsolute(rel, args2.wctx.opts.roots));
|
|
17265
|
+
if (scopedAbs.length > 0) {
|
|
17266
|
+
const scopedWalkOptions = { ...args2.walkOptions, scopedPaths: scopedAbs };
|
|
17267
|
+
for (const provider of args2.activeProviders) {
|
|
17268
|
+
for await (const raw of resolveProviderWalk(provider)(args2.wctx.opts.roots, scopedWalkOptions)) {
|
|
17269
|
+
filesWalked += 1;
|
|
17270
|
+
if (args2.claimedPaths.has(raw.path)) continue;
|
|
17271
|
+
await args2.advance(raw, provider);
|
|
17272
|
+
}
|
|
17273
|
+
}
|
|
17274
|
+
}
|
|
17275
|
+
filesWalked += await injectUnchangedPriorNodes(args2, changed, removed);
|
|
17276
|
+
return filesWalked;
|
|
17277
|
+
}
|
|
17278
|
+
async function injectUnchangedPriorNodes(args2, changed, removed) {
|
|
17279
|
+
const providerById = new Map(args2.activeProviders.map((p) => [p.id, p]));
|
|
17280
|
+
const universalFallback = args2.activeProviders.find((p) => !p.gatedByActiveLens) ?? args2.activeProviders[0];
|
|
17281
|
+
let injected = 0;
|
|
17282
|
+
for (const priorNode of args2.wctx.opts.prior?.nodes ?? []) {
|
|
17283
|
+
if (!shouldInjectPriorNode(priorNode, changed, removed, args2.claimedPaths)) continue;
|
|
17284
|
+
const provider = providerById.get(priorNode.provider) ?? universalFallback;
|
|
17285
|
+
if (!provider) continue;
|
|
17286
|
+
const raw = buildUnchangedRawNode(priorNode, provider, args2.wctx.opts.roots);
|
|
17287
|
+
await args2.advance(raw, provider);
|
|
17288
|
+
injected += 1;
|
|
17289
|
+
}
|
|
17290
|
+
return injected;
|
|
17291
|
+
}
|
|
17292
|
+
function shouldInjectPriorNode(priorNode, changed, removed, claimedPaths) {
|
|
17293
|
+
const path = priorNode.path;
|
|
17294
|
+
if (changed.has(path) || removed.has(path) || claimedPaths.has(path)) return false;
|
|
17295
|
+
if (priorNode.virtual === true) return false;
|
|
17296
|
+
return true;
|
|
17297
|
+
}
|
|
17298
|
+
function buildUnchangedRawNode(priorNode, provider, roots) {
|
|
17299
|
+
const path = priorNode.path;
|
|
17300
|
+
return {
|
|
17301
|
+
path,
|
|
17302
|
+
body: "",
|
|
17303
|
+
frontmatterRaw: "",
|
|
17304
|
+
frontmatter: {},
|
|
17305
|
+
...typeof priorNode.modifiedAtMs === "number" ? { modifiedAtMs: priorNode.modifiedAtMs } : {},
|
|
17306
|
+
unchanged: true,
|
|
17307
|
+
reread: async () => {
|
|
17308
|
+
const abs = toAbsolute(path, roots);
|
|
17309
|
+
for await (const re of resolveProviderWalk(provider)(roots, { scopedPaths: [abs] })) {
|
|
17310
|
+
return {
|
|
17311
|
+
body: re.body,
|
|
17312
|
+
frontmatterRaw: re.frontmatterRaw,
|
|
17313
|
+
frontmatter: re.frontmatter,
|
|
17314
|
+
...re.parseIssues ? { parseIssues: re.parseIssues } : {}
|
|
17315
|
+
};
|
|
17316
|
+
}
|
|
17317
|
+
return { body: "", frontmatterRaw: "", frontmatter: {} };
|
|
17318
|
+
}
|
|
17319
|
+
};
|
|
17320
|
+
}
|
|
17321
|
+
function expandSidecarPaths(paths, priorNodesByPath) {
|
|
17322
|
+
const out = /* @__PURE__ */ new Set();
|
|
17323
|
+
for (const path of paths) {
|
|
17324
|
+
if (path.endsWith(".sm")) {
|
|
17325
|
+
const mdPath = `${path.slice(0, -".sm".length)}.md`;
|
|
17326
|
+
if (priorNodesByPath.has(mdPath)) out.add(mdPath);
|
|
17327
|
+
continue;
|
|
17328
|
+
}
|
|
17329
|
+
out.add(path);
|
|
17330
|
+
}
|
|
17331
|
+
return out;
|
|
17332
|
+
}
|
|
17333
|
+
function toAbsolute(relPath, roots) {
|
|
17334
|
+
const root = roots[0] ?? ".";
|
|
17335
|
+
const absRoot = isAbsolute8(root) ? root : resolve29(root);
|
|
17336
|
+
return resolve29(absRoot, relPath);
|
|
17337
|
+
}
|
|
17338
|
+
function resolveEffectiveCaps(opts) {
|
|
17339
|
+
return {
|
|
17340
|
+
effectiveScanCeiling: opts.overrideScanCeiling ?? opts.scanCeiling,
|
|
17341
|
+
effectiveMaxRenderNodes: opts.overrideMaxRenderNodes ?? opts.maxRenderNodes
|
|
17342
|
+
};
|
|
17343
|
+
}
|
|
17344
|
+
function buildPriorMtimes(opts) {
|
|
17345
|
+
if (!opts.enableCache || opts.prior === null || opts.tokenizerChanged) return void 0;
|
|
17346
|
+
const map = /* @__PURE__ */ new Map();
|
|
17347
|
+
for (const node of opts.prior.nodes) {
|
|
17348
|
+
if (typeof node.modifiedAtMs === "number") map.set(node.path, node.modifiedAtMs);
|
|
17349
|
+
}
|
|
17350
|
+
return map.size > 0 ? map : void 0;
|
|
17351
|
+
}
|
|
17352
|
+
function providerParticipates(provider, activeProvider) {
|
|
17353
|
+
if (!provider.gatedByActiveLens) return true;
|
|
17354
|
+
if (activeProvider === null) return true;
|
|
17355
|
+
return provider.id === activeProvider;
|
|
17356
|
+
}
|
|
16979
17357
|
function createWalkAccumulators() {
|
|
16980
17358
|
return {
|
|
16981
17359
|
nodes: [],
|
|
@@ -17004,61 +17382,106 @@ function buildWalkContext(opts) {
|
|
|
17004
17382
|
return { opts, priorNodesByPath, priorLinksByOriginating, priorFrontmatterIssuesByNode, shortIdToQualified };
|
|
17005
17383
|
}
|
|
17006
17384
|
async function processRawNode(raw, provider, wctx, accum, claimedPaths, nextIndex) {
|
|
17007
|
-
const bodyHash = sha256(raw.body);
|
|
17008
|
-
const frontmatterHash = sha256(canonicalFrontmatter(raw.frontmatter, raw.frontmatterRaw));
|
|
17009
17385
|
if (Array.isArray(provider.roots) && provider.roots.length > 0) {
|
|
17010
17386
|
if (!matchesAnyRoot(raw.path, provider.roots)) return false;
|
|
17011
17387
|
}
|
|
17388
|
+
if (raw.unchanged === true) {
|
|
17389
|
+
const handled = await handleUnchangedRawNode(raw, provider, wctx, accum, claimedPaths, nextIndex);
|
|
17390
|
+
if (handled) return true;
|
|
17391
|
+
}
|
|
17392
|
+
const bodyHash = sha256(raw.body);
|
|
17393
|
+
const frontmatterHash = sha256(canonicalFrontmatter(raw.frontmatter, raw.frontmatterRaw));
|
|
17012
17394
|
const kind = provider.classify(raw.path, raw.frontmatter);
|
|
17013
17395
|
if (kind === null) {
|
|
17014
17396
|
return false;
|
|
17015
17397
|
}
|
|
17016
17398
|
claimedPaths.add(raw.path);
|
|
17017
17399
|
const priorNode = wctx.priorNodesByPath.get(raw.path);
|
|
17018
|
-
const nodeHashCacheEligible = wctx
|
|
17019
|
-
|
|
17020
|
-
|
|
17021
|
-
|
|
17022
|
-
|
|
17400
|
+
const nodeHashCacheEligible = isNodeHashCacheEligible(wctx, priorNode, bodyHash, frontmatterHash);
|
|
17401
|
+
await dispatchNode(
|
|
17402
|
+
{ raw, provider, kind, bodyHash, frontmatterHash, nodeHashCacheEligible, priorNode },
|
|
17403
|
+
wctx,
|
|
17404
|
+
accum,
|
|
17405
|
+
nextIndex
|
|
17406
|
+
);
|
|
17407
|
+
return true;
|
|
17408
|
+
}
|
|
17409
|
+
function isNodeHashCacheEligible(wctx, priorNode, bodyHash, frontmatterHash) {
|
|
17410
|
+
return wctx.opts.enableCache && !wctx.opts.tokenizerChanged && wctx.opts.prior !== null && priorNode !== void 0 && priorNode.bodyHash === bodyHash && priorNode.frontmatterHash === frontmatterHash;
|
|
17411
|
+
}
|
|
17412
|
+
async function handleUnchangedRawNode(raw, provider, wctx, accum, claimedPaths, nextIndex) {
|
|
17413
|
+
const prior = wctx.priorNodesByPath.get(raw.path);
|
|
17414
|
+
if (prior) {
|
|
17415
|
+
claimedPaths.add(raw.path);
|
|
17416
|
+
await dispatchNode(
|
|
17417
|
+
{
|
|
17418
|
+
raw,
|
|
17419
|
+
provider,
|
|
17420
|
+
kind: prior.kind,
|
|
17421
|
+
bodyHash: prior.bodyHash,
|
|
17422
|
+
frontmatterHash: prior.frontmatterHash,
|
|
17423
|
+
nodeHashCacheEligible: true,
|
|
17424
|
+
priorNode: prior,
|
|
17425
|
+
ensureBody: () => rereadInto(raw)
|
|
17426
|
+
},
|
|
17427
|
+
wctx,
|
|
17428
|
+
accum,
|
|
17429
|
+
nextIndex
|
|
17430
|
+
);
|
|
17431
|
+
return true;
|
|
17432
|
+
}
|
|
17433
|
+
await rereadInto(raw);
|
|
17434
|
+
return false;
|
|
17435
|
+
}
|
|
17436
|
+
async function dispatchNode(args2, wctx, accum, nextIndex) {
|
|
17023
17437
|
const sidecarResolution = resolveSidecarOverlay(
|
|
17024
|
-
raw.path,
|
|
17025
|
-
raw.path,
|
|
17438
|
+
args2.raw.path,
|
|
17439
|
+
args2.raw.path,
|
|
17026
17440
|
wctx.opts.roots,
|
|
17027
|
-
bodyHash,
|
|
17028
|
-
frontmatterHash
|
|
17441
|
+
args2.bodyHash,
|
|
17442
|
+
args2.frontmatterHash
|
|
17029
17443
|
);
|
|
17030
17444
|
const sidecarAnnotationsHash = sha256(
|
|
17031
17445
|
canonicalSidecarAnnotations(sidecarResolution.overlay.annotations)
|
|
17032
17446
|
);
|
|
17033
17447
|
const cacheDecision = computeCacheDecision({
|
|
17034
17448
|
extractors: wctx.opts.extractors,
|
|
17035
|
-
kind,
|
|
17449
|
+
kind: args2.kind,
|
|
17036
17450
|
activeProvider: wctx.opts.activeProvider,
|
|
17037
|
-
nodePath: raw.path,
|
|
17038
|
-
bodyHash,
|
|
17451
|
+
nodePath: args2.raw.path,
|
|
17452
|
+
bodyHash: args2.bodyHash,
|
|
17039
17453
|
sidecarAnnotationsHash,
|
|
17040
|
-
nodeHashCacheEligible,
|
|
17454
|
+
nodeHashCacheEligible: args2.nodeHashCacheEligible,
|
|
17041
17455
|
priorExtractorRuns: wctx.opts.priorExtractorRuns
|
|
17042
17456
|
});
|
|
17043
17457
|
const ctx = {
|
|
17044
|
-
raw,
|
|
17045
|
-
provider,
|
|
17046
|
-
kind,
|
|
17047
|
-
bodyHash,
|
|
17048
|
-
frontmatterHash,
|
|
17458
|
+
raw: args2.raw,
|
|
17459
|
+
provider: args2.provider,
|
|
17460
|
+
kind: args2.kind,
|
|
17461
|
+
bodyHash: args2.bodyHash,
|
|
17462
|
+
frontmatterHash: args2.frontmatterHash,
|
|
17049
17463
|
sidecarResolution,
|
|
17050
17464
|
sidecarAnnotationsHash,
|
|
17051
|
-
nodeHashCacheEligible,
|
|
17465
|
+
nodeHashCacheEligible: args2.nodeHashCacheEligible,
|
|
17052
17466
|
cacheDecision,
|
|
17053
|
-
priorNode,
|
|
17467
|
+
priorNode: args2.priorNode,
|
|
17054
17468
|
index: nextIndex
|
|
17055
17469
|
};
|
|
17056
|
-
if (cacheDecision.fullCacheHit && priorNode) {
|
|
17470
|
+
if (cacheDecision.fullCacheHit && args2.priorNode) {
|
|
17057
17471
|
applyFullCacheHit(ctx, wctx, accum);
|
|
17058
17472
|
} else {
|
|
17473
|
+
if (args2.ensureBody) await args2.ensureBody();
|
|
17059
17474
|
await applyExtractPath(ctx, wctx, accum);
|
|
17060
17475
|
}
|
|
17061
|
-
|
|
17476
|
+
}
|
|
17477
|
+
async function rereadInto(raw) {
|
|
17478
|
+
if (!raw.reread) return;
|
|
17479
|
+
const re = await raw.reread();
|
|
17480
|
+
raw.body = re.body;
|
|
17481
|
+
raw.frontmatter = re.frontmatter;
|
|
17482
|
+
raw.frontmatterRaw = re.frontmatterRaw;
|
|
17483
|
+
raw.unchanged = false;
|
|
17484
|
+
if (re.parseIssues) raw.parseIssues = re.parseIssues;
|
|
17062
17485
|
}
|
|
17063
17486
|
function attachSidecar(node, resolution, sidecarRoots) {
|
|
17064
17487
|
node.sidecar = resolution.overlay;
|
|
@@ -17271,9 +17694,18 @@ async function runScanInternal(_kernel, options) {
|
|
|
17271
17694
|
providerFrontmatter: setup.providerFrontmatter,
|
|
17272
17695
|
pluginStores: options.pluginStores,
|
|
17273
17696
|
activeProvider: activeProviderId,
|
|
17274
|
-
|
|
17275
|
-
|
|
17276
|
-
|
|
17697
|
+
scanCeiling: options.scanCeiling ?? 5e4,
|
|
17698
|
+
overrideScanCeiling: options.overrideScanCeiling ?? null,
|
|
17699
|
+
maxRenderNodes: options.maxRenderNodes ?? 256,
|
|
17700
|
+
overrideMaxRenderNodes: options.overrideMaxRenderNodes ?? null,
|
|
17701
|
+
...options.maxFileSizeBytes !== void 0 ? { maxFileSizeBytes: options.maxFileSizeBytes } : {},
|
|
17702
|
+
// Watcher incremental fast path: only honoured when a prior exists,
|
|
17703
|
+
// cache reuse is on, and the tokenizer is unchanged (else a scoped
|
|
17704
|
+
// read would skip nodes whose token counts must be recomputed). The
|
|
17705
|
+
// walker enumerates from the prior snapshot + reads only the changed
|
|
17706
|
+
// files instead of traversing the corpus. Falls back to the full
|
|
17707
|
+
// traversal + mtime-gate when the gate does not hold.
|
|
17708
|
+
...options.incrementalChangedPaths !== void 0 && prior !== null && setup.enableCache && !tokenizerChanged ? { incrementalChangedPaths: options.incrementalChangedPaths } : {}
|
|
17277
17709
|
});
|
|
17278
17710
|
const activeProvider = activeProviderId ? exts.providers.find((p) => p.id === activeProviderId) ?? null : null;
|
|
17279
17711
|
const resolved = resolveSignals({
|
|
@@ -17471,8 +17903,9 @@ function buildScanReturn(walked, issues, renameOps, stats, options, setup, linkS
|
|
|
17471
17903
|
providers: setup.exts.providers.map((a) => a.id),
|
|
17472
17904
|
scannedBy: SCANNED_BY,
|
|
17473
17905
|
tokenizer: setup.tokenizer,
|
|
17474
|
-
|
|
17475
|
-
|
|
17906
|
+
scanCeiling: walked.scanCeiling,
|
|
17907
|
+
scanTruncated: walked.scanTruncated,
|
|
17908
|
+
maxRenderNodes: walked.maxRenderNodes,
|
|
17476
17909
|
oversizedFiles: walked.oversizedFiles,
|
|
17477
17910
|
nodes: walked.nodes,
|
|
17478
17911
|
links: walked.internalLinks,
|
|
@@ -17501,7 +17934,7 @@ function validateRoots(roots) {
|
|
|
17501
17934
|
function resolveActiveProviderOption(optionValue, roots, providers) {
|
|
17502
17935
|
if (optionValue !== void 0) return optionValue;
|
|
17503
17936
|
for (const root of roots) {
|
|
17504
|
-
const absRoot =
|
|
17937
|
+
const absRoot = isAbsolute9(root) ? root : resolve30(root);
|
|
17505
17938
|
if (!existsSync23(absRoot)) continue;
|
|
17506
17939
|
const detected = detectProvidersFromFilesystem(absRoot, providers)[0] ?? null;
|
|
17507
17940
|
if (detected !== null) return detected;
|
|
@@ -17510,10 +17943,10 @@ function resolveActiveProviderOption(optionValue, roots, providers) {
|
|
|
17510
17943
|
}
|
|
17511
17944
|
|
|
17512
17945
|
// kernel/scan/watcher.ts
|
|
17513
|
-
import { resolve as
|
|
17946
|
+
import { resolve as resolve31, relative as relative5, sep as sep5 } from "path";
|
|
17514
17947
|
import chokidar from "chokidar";
|
|
17515
17948
|
function createChokidarWatcher(opts) {
|
|
17516
|
-
const absRoots = opts.roots.map((r) =>
|
|
17949
|
+
const absRoots = opts.roots.map((r) => resolve31(opts.cwd, r));
|
|
17517
17950
|
const ignoreFilterOpt = opts.ignoreFilter;
|
|
17518
17951
|
const getFilter = ignoreFilterOpt === void 0 ? void 0 : typeof ignoreFilterOpt === "function" ? ignoreFilterOpt : () => ignoreFilterOpt;
|
|
17519
17952
|
const ignored = getFilter ? (path) => {
|
|
@@ -18036,7 +18469,7 @@ function resolveScanRoots(inputs) {
|
|
|
18036
18469
|
// core/runtime/reference-paths-walker.ts
|
|
18037
18470
|
import { readdirSync as readdirSync8, statSync as statSync7 } from "fs";
|
|
18038
18471
|
import { homedir as osHomedir2 } from "os";
|
|
18039
|
-
import { isAbsolute as
|
|
18472
|
+
import { isAbsolute as isAbsolute10, join as join14, resolve as resolve32 } from "path";
|
|
18040
18473
|
var REFERENCE_WALK_MAX_FILES = 5e4;
|
|
18041
18474
|
var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
18042
18475
|
"node_modules",
|
|
@@ -18044,10 +18477,10 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
|
18044
18477
|
SKILL_MAP_DIR
|
|
18045
18478
|
]);
|
|
18046
18479
|
function resolveScanPath(raw, cwd) {
|
|
18047
|
-
if (raw.startsWith("~/")) return
|
|
18048
|
-
if (raw === "~") return
|
|
18049
|
-
if (
|
|
18050
|
-
return
|
|
18480
|
+
if (raw.startsWith("~/")) return resolve32(join14(osHomedir2(), raw.slice(2)));
|
|
18481
|
+
if (raw === "~") return resolve32(osHomedir2());
|
|
18482
|
+
if (isAbsolute10(raw)) return resolve32(raw);
|
|
18483
|
+
return resolve32(cwd, raw);
|
|
18051
18484
|
}
|
|
18052
18485
|
function walkReferencePaths(rawRoots, cwd) {
|
|
18053
18486
|
const paths = /* @__PURE__ */ new Set();
|
|
@@ -18096,7 +18529,7 @@ function safeStat(path) {
|
|
|
18096
18529
|
|
|
18097
18530
|
// core/runtime/active-provider-bootstrap.ts
|
|
18098
18531
|
import { createInterface as createInterface3 } from "readline";
|
|
18099
|
-
import { isAbsolute as
|
|
18532
|
+
import { isAbsolute as isAbsolute11, join as join15 } from "path";
|
|
18100
18533
|
async function bootstrapActiveProvider(opts) {
|
|
18101
18534
|
const fromCwd = resolveActiveProvider(opts.cwd, opts.providers);
|
|
18102
18535
|
if (fromCwd.source === "config") {
|
|
@@ -18155,7 +18588,7 @@ function aggregateDetected(cwd, effectiveRoots, cwdDetected, providers) {
|
|
|
18155
18588
|
out.push(id);
|
|
18156
18589
|
}
|
|
18157
18590
|
for (const root of effectiveRoots) {
|
|
18158
|
-
const absRoot =
|
|
18591
|
+
const absRoot = isAbsolute11(root) ? root : join15(cwd, root);
|
|
18159
18592
|
const r = resolveActiveProvider(absRoot, providers);
|
|
18160
18593
|
for (const id of r.detected) {
|
|
18161
18594
|
if (seen.has(id)) continue;
|
|
@@ -18413,6 +18846,7 @@ async function runScanForCommand(opts) {
|
|
|
18413
18846
|
referenceablePaths,
|
|
18414
18847
|
ctx.cwd,
|
|
18415
18848
|
activeProvider,
|
|
18849
|
+
cfg.scan.maxScan,
|
|
18416
18850
|
cfg.scan.maxNodes,
|
|
18417
18851
|
cfg.scan.maxFileSizeBytes,
|
|
18418
18852
|
cfg.tokenizer
|
|
@@ -18531,7 +18965,7 @@ function makePriorLoader(noBuiltIns, strict) {
|
|
|
18531
18965
|
return loaded;
|
|
18532
18966
|
};
|
|
18533
18967
|
}
|
|
18534
|
-
function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, extensions, referenceablePaths, scanCwd, activeProvider,
|
|
18968
|
+
function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, extensions, referenceablePaths, scanCwd, activeProvider, scanCeiling, maxRenderNodes, maxFileSizeBytes, tokenizer) {
|
|
18535
18969
|
return async (prior, priorExtractorRuns) => {
|
|
18536
18970
|
if (opts.changed && prior === null) {
|
|
18537
18971
|
opts.stderr.write(SCAN_RUNNER_TEXTS.changedNoPriorWarning);
|
|
@@ -18546,7 +18980,8 @@ function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, exte
|
|
|
18546
18980
|
cwd: scanCwd,
|
|
18547
18981
|
prior,
|
|
18548
18982
|
activeProvider,
|
|
18549
|
-
|
|
18983
|
+
scanCeiling,
|
|
18984
|
+
maxRenderNodes,
|
|
18550
18985
|
maxFileSizeBytes,
|
|
18551
18986
|
tokenizer,
|
|
18552
18987
|
...priorExtractorRuns ? { priorExtractorRuns } : {}
|
|
@@ -18564,8 +18999,10 @@ function buildRunScanOptions(args2) {
|
|
|
18564
18999
|
strict: args2.strict,
|
|
18565
19000
|
emitter: buildRunScanEmitter(opts),
|
|
18566
19001
|
activeProvider: args2.activeProvider,
|
|
18567
|
-
|
|
18568
|
-
|
|
19002
|
+
scanCeiling: args2.scanCeiling,
|
|
19003
|
+
overrideScanCeiling: opts.maxScan ?? null,
|
|
19004
|
+
maxRenderNodes: args2.maxRenderNodes,
|
|
19005
|
+
overrideMaxRenderNodes: opts.maxNodes ?? null,
|
|
18569
19006
|
maxFileSizeBytes: args2.maxFileSizeBytes
|
|
18570
19007
|
};
|
|
18571
19008
|
if (args2.extensions) runOptions.extensions = args2.extensions;
|
|
@@ -19510,7 +19947,7 @@ import { Command as Command19, Option as Option18 } from "clipanion";
|
|
|
19510
19947
|
|
|
19511
19948
|
// kernel/jobs/orphan-files.ts
|
|
19512
19949
|
import { readdirSync as readdirSync9, statSync as statSync8 } from "fs";
|
|
19513
|
-
import { join as join17, resolve as
|
|
19950
|
+
import { join as join17, resolve as resolve33 } from "path";
|
|
19514
19951
|
function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
19515
19952
|
let entries;
|
|
19516
19953
|
try {
|
|
@@ -19528,7 +19965,7 @@ function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
|
19528
19965
|
if (!entry.isFile()) continue;
|
|
19529
19966
|
const name = entry.name;
|
|
19530
19967
|
if (!name.endsWith(".md")) continue;
|
|
19531
|
-
const abs =
|
|
19968
|
+
const abs = resolve33(join17(jobsDir, name));
|
|
19532
19969
|
if (!referencedPaths.has(abs)) orphans.push(abs);
|
|
19533
19970
|
}
|
|
19534
19971
|
orphans.sort();
|
|
@@ -20755,9 +21192,9 @@ function sortPluginsForPresentation(plugins) {
|
|
|
20755
21192
|
}
|
|
20756
21193
|
|
|
20757
21194
|
// cli/commands/plugins/shared.ts
|
|
20758
|
-
import { resolve as
|
|
21195
|
+
import { resolve as resolve34 } from "path";
|
|
20759
21196
|
function resolveSearchPaths2(opts, cwd) {
|
|
20760
|
-
if (opts.pluginDir) return [
|
|
21197
|
+
if (opts.pluginDir) return [resolve34(opts.pluginDir)];
|
|
20761
21198
|
return [defaultProjectPluginsDir({ cwd })];
|
|
20762
21199
|
}
|
|
20763
21200
|
async function buildResolver() {
|
|
@@ -22071,7 +22508,7 @@ function resolveBareToggle(id, catalogue) {
|
|
|
22071
22508
|
|
|
22072
22509
|
// cli/commands/plugins/create.ts
|
|
22073
22510
|
import { existsSync as existsSync25, mkdirSync as mkdirSync5, writeFileSync } from "fs";
|
|
22074
|
-
import { dirname as dirname17, join as join18, resolve as
|
|
22511
|
+
import { dirname as dirname17, join as join18, resolve as resolve35 } from "path";
|
|
22075
22512
|
import { Command as Command26, Option as Option25 } from "clipanion";
|
|
22076
22513
|
|
|
22077
22514
|
// cli/commands/plugins/scaffold/action.ts
|
|
@@ -22521,7 +22958,7 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
22521
22958
|
const kind = this.kind;
|
|
22522
22959
|
const ctx = defaultRuntimeContext();
|
|
22523
22960
|
const baseDir = defaultProjectPluginsDir(ctx);
|
|
22524
|
-
const targetDir = this.at ?
|
|
22961
|
+
const targetDir = this.at ? resolve35(this.at) : join18(baseDir, this.pluginId);
|
|
22525
22962
|
if (existsSync25(targetDir) && !this.force) {
|
|
22526
22963
|
this.printer.error(
|
|
22527
22964
|
tx(PLUGINS_TEXTS.createRefuseOverwrite, {
|
|
@@ -23037,7 +23474,7 @@ var PLUGIN_COMMANDS = [
|
|
|
23037
23474
|
|
|
23038
23475
|
// cli/commands/refresh.ts
|
|
23039
23476
|
import { readFile as readFile4 } from "fs/promises";
|
|
23040
|
-
import { resolve as
|
|
23477
|
+
import { resolve as resolve36 } from "path";
|
|
23041
23478
|
import { Command as Command30, Option as Option28 } from "clipanion";
|
|
23042
23479
|
|
|
23043
23480
|
// cli/i18n/refresh.texts.ts
|
|
@@ -23345,7 +23782,7 @@ var RefreshCommand = class extends SmCommand {
|
|
|
23345
23782
|
let body;
|
|
23346
23783
|
try {
|
|
23347
23784
|
assertContained(cwd, node.path);
|
|
23348
|
-
const raw = await readFile4(
|
|
23785
|
+
const raw = await readFile4(resolve36(cwd, node.path), "utf8");
|
|
23349
23786
|
body = stripFrontmatterFence(raw);
|
|
23350
23787
|
} catch (err) {
|
|
23351
23788
|
if (!this.json) {
|
|
@@ -23418,7 +23855,7 @@ var IntentionalFailCommand = class extends SmCommand {
|
|
|
23418
23855
|
setTimeout(() => {
|
|
23419
23856
|
throw new Error(INTENTIONAL_FAIL_TEXTS.errorMessage);
|
|
23420
23857
|
}, 0);
|
|
23421
|
-
await new Promise((
|
|
23858
|
+
await new Promise((resolve43) => setTimeout(resolve43, 5e3));
|
|
23422
23859
|
return ExitCode.Issues;
|
|
23423
23860
|
}
|
|
23424
23861
|
};
|
|
@@ -23508,15 +23945,16 @@ var SCAN_TEXTS = {
|
|
|
23508
23945
|
countInfoNoun: "info",
|
|
23509
23946
|
countNoIssues: "0 issues",
|
|
23510
23947
|
/**
|
|
23511
|
-
*
|
|
23512
|
-
* because `--max-
|
|
23513
|
-
*
|
|
23514
|
-
*
|
|
23515
|
-
*
|
|
23516
|
-
*
|
|
23948
|
+
* Truncation notice, printed when the walker stopped accepting files
|
|
23949
|
+
* because the walk ceiling `--max-scan` (or the `scan.maxScan`
|
|
23950
|
+
* setting) was reached and extra files were dropped. `{{glyph}}` is
|
|
23951
|
+
* the yellow warning glyph, `{{limit}}` the effective ceiling,
|
|
23952
|
+
* `{{source}}` either `--max-scan` or `scan.maxScan`. The hint names
|
|
23953
|
+
* both escape routes the user has: editing `.skillmapignore`
|
|
23954
|
+
* (preferred) or raising the ceiling with `--max-scan <N>`.
|
|
23517
23955
|
*/
|
|
23518
|
-
scanCappedNotice: "{{glyph}} Scan
|
|
23519
|
-
scanCappedNoticeHint: "
|
|
23956
|
+
scanCappedNotice: "{{glyph}} Scan truncated at {{limit}} files ({{source}}); extra files were dropped.\n {{hint}}\n",
|
|
23957
|
+
scanCappedNoticeHint: "Edit .skillmapignore to exclude noisy paths (preferred), or re-run with --max-scan <N> to raise the ceiling. Files past the ceiling are not parsed, analyzed, or reference-validated.",
|
|
23520
23958
|
/**
|
|
23521
23959
|
* File-size skip notice, printed (WARN, stderr) when the walker
|
|
23522
23960
|
* skipped one or more files for exceeding `scan.maxFileSizeBytes`.
|
|
@@ -23531,6 +23969,12 @@ var SCAN_TEXTS = {
|
|
|
23531
23969
|
scanSkippedFileNounSingular: "file",
|
|
23532
23970
|
scanSkippedFileNounPlural: "files",
|
|
23533
23971
|
scanSkippedFilesNoticeHint: "Raise scan.maxFileSizeBytes to include these, or add them to .skillmapignore to skip them on purpose.",
|
|
23972
|
+
/**
|
|
23973
|
+
* Validation message for an invalid `--max-scan` value. Surfaced as a
|
|
23974
|
+
* §3.1b two-line block.
|
|
23975
|
+
*/
|
|
23976
|
+
maxScanInvalid: "{{glyph}} --max-scan must be an integer >= 1 (got `{{value}}`).\n {{hint}}\n",
|
|
23977
|
+
maxScanInvalidHint: "Pass a positive integer, e.g. --max-scan 50000.",
|
|
23534
23978
|
/**
|
|
23535
23979
|
* Validation message for an invalid `--max-nodes` value. Surfaced as a
|
|
23536
23980
|
* §3.1b two-line block.
|
|
@@ -23579,7 +24023,7 @@ var SCAN_TEXTS = {
|
|
|
23579
24023
|
import { Command as Command31, Option as Option29 } from "clipanion";
|
|
23580
24024
|
|
|
23581
24025
|
// core/watcher/runtime.ts
|
|
23582
|
-
import { dirname as dirname18 } from "path";
|
|
24026
|
+
import { dirname as dirname18, isAbsolute as isAbsolute12, relative as relative7, resolve as resolve37, sep as sep6 } from "path";
|
|
23583
24027
|
|
|
23584
24028
|
// core/runtime/fresh-resolver.ts
|
|
23585
24029
|
async function buildFreshResolver(deps) {
|
|
@@ -23612,6 +24056,39 @@ async function rebuildWatcherDbOnDrift(dbPath, events) {
|
|
|
23612
24056
|
events.onDriftReset?.({ dbVersion: drift.dbVersion, currentVersion: drift.currentVersion });
|
|
23613
24057
|
}
|
|
23614
24058
|
}
|
|
24059
|
+
function applyPriorStateToRunOptions(runOptions, priorState, changedPaths) {
|
|
24060
|
+
if (!priorState) return;
|
|
24061
|
+
runOptions.priorSnapshot = priorState.snapshot;
|
|
24062
|
+
runOptions.enableCache = true;
|
|
24063
|
+
runOptions.priorExtractorRuns = priorState.extractorRuns;
|
|
24064
|
+
if (changedPaths) {
|
|
24065
|
+
runOptions.incrementalChangedPaths = {
|
|
24066
|
+
changed: changedPaths.changed,
|
|
24067
|
+
removed: changedPaths.removed
|
|
24068
|
+
};
|
|
24069
|
+
}
|
|
24070
|
+
}
|
|
24071
|
+
function toIncrementalPaths(events, roots, cwd) {
|
|
24072
|
+
const absRoots = roots.map((r) => isAbsolute12(r) ? r : resolve37(cwd, r));
|
|
24073
|
+
const changed = /* @__PURE__ */ new Set();
|
|
24074
|
+
const removed = /* @__PURE__ */ new Set();
|
|
24075
|
+
for (const ev of events) {
|
|
24076
|
+
const rel = relativeFromRoots2(ev.absolutePath, absRoots);
|
|
24077
|
+
if (rel === null) continue;
|
|
24078
|
+
if (ev.kind === "unlink") removed.add(rel);
|
|
24079
|
+
else changed.add(rel);
|
|
24080
|
+
}
|
|
24081
|
+
if (changed.size === 0 && removed.size === 0) return null;
|
|
24082
|
+
return { changed, removed };
|
|
24083
|
+
}
|
|
24084
|
+
function relativeFromRoots2(absolute, absRoots) {
|
|
24085
|
+
for (const root of absRoots) {
|
|
24086
|
+
const rel = relative7(root, absolute);
|
|
24087
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute12(rel)) continue;
|
|
24088
|
+
return rel.split(sep6).join("/");
|
|
24089
|
+
}
|
|
24090
|
+
return null;
|
|
24091
|
+
}
|
|
23615
24092
|
function createWatcherRuntime(opts) {
|
|
23616
24093
|
const events = opts.events ?? {};
|
|
23617
24094
|
const cwd = opts.runtimeContext.cwd;
|
|
@@ -23665,7 +24142,11 @@ function createWatcherRuntime(opts) {
|
|
|
23665
24142
|
for (const warn of pluginRuntime.warnings) {
|
|
23666
24143
|
events.onPluginWarning?.(warn);
|
|
23667
24144
|
}
|
|
23668
|
-
const
|
|
24145
|
+
const notifyBatchStart = () => {
|
|
24146
|
+
events.onBatchStart?.();
|
|
24147
|
+
};
|
|
24148
|
+
const runOnePass = async (changedPaths) => {
|
|
24149
|
+
notifyBatchStart();
|
|
23669
24150
|
const resolveEnabledOverride = await buildFreshResolver({
|
|
23670
24151
|
databasePath: opts.dbPath,
|
|
23671
24152
|
effectiveConfig: () => cfg,
|
|
@@ -23711,8 +24192,10 @@ function createWatcherRuntime(opts) {
|
|
|
23711
24192
|
ignoreFilter,
|
|
23712
24193
|
strict,
|
|
23713
24194
|
emitter,
|
|
23714
|
-
|
|
23715
|
-
|
|
24195
|
+
scanCeiling: cfg.scan.maxScan,
|
|
24196
|
+
overrideScanCeiling: opts.maxScanOverride ?? null,
|
|
24197
|
+
maxRenderNodes: cfg.scan.maxNodes,
|
|
24198
|
+
overrideMaxRenderNodes: opts.maxNodesOverride ?? null,
|
|
23716
24199
|
maxFileSizeBytes: cfg.scan.maxFileSizeBytes
|
|
23717
24200
|
};
|
|
23718
24201
|
if (cfg.scan.referencePaths.length > 0) {
|
|
@@ -23723,11 +24206,7 @@ function createWatcherRuntime(opts) {
|
|
|
23723
24206
|
}
|
|
23724
24207
|
}
|
|
23725
24208
|
if (composed) runOptions.extensions = composed;
|
|
23726
|
-
|
|
23727
|
-
runOptions.priorSnapshot = priorState.snapshot;
|
|
23728
|
-
runOptions.enableCache = true;
|
|
23729
|
-
runOptions.priorExtractorRuns = priorState.extractorRuns;
|
|
23730
|
-
}
|
|
24209
|
+
applyPriorStateToRunOptions(runOptions, priorState, changedPaths);
|
|
23731
24210
|
const ran = await runScanWithRenames(kernel, runOptions);
|
|
23732
24211
|
const {
|
|
23733
24212
|
result,
|
|
@@ -23754,11 +24233,11 @@ function createWatcherRuntime(opts) {
|
|
|
23754
24233
|
);
|
|
23755
24234
|
return result;
|
|
23756
24235
|
};
|
|
23757
|
-
handleBatch = async () => {
|
|
24236
|
+
handleBatch = async (changedPaths) => {
|
|
23758
24237
|
if (stopped) return;
|
|
23759
24238
|
batchCount++;
|
|
23760
24239
|
try {
|
|
23761
|
-
const result = await runOnePass();
|
|
24240
|
+
const result = await runOnePass(changedPaths);
|
|
23762
24241
|
consecutiveFailures = 0;
|
|
23763
24242
|
events.onBatch?.({ kind: "ok", result });
|
|
23764
24243
|
} catch (err) {
|
|
@@ -23805,8 +24284,11 @@ function createWatcherRuntime(opts) {
|
|
|
23805
24284
|
// `.skill-map/settings.json` edit, and chokidar's `ignored`
|
|
23806
24285
|
// predicate must read the current value on every event.
|
|
23807
24286
|
ignoreFilter: () => ignoreFilter,
|
|
23808
|
-
onBatch: async () => {
|
|
23809
|
-
if (handleBatch)
|
|
24287
|
+
onBatch: async ({ events: batchEvents }) => {
|
|
24288
|
+
if (!handleBatch) return;
|
|
24289
|
+
const changedPaths = toIncrementalPaths(batchEvents, opts.roots, cwd);
|
|
24290
|
+
if (changedPaths) await handleBatch(changedPaths);
|
|
24291
|
+
else await handleBatch();
|
|
23810
24292
|
},
|
|
23811
24293
|
onError: (err) => {
|
|
23812
24294
|
events.onWatcherError?.(err.message);
|
|
@@ -23938,6 +24420,11 @@ var WATCH_TEXTS = {
|
|
|
23938
24420
|
*/
|
|
23939
24421
|
maxConsecutiveFailuresInvalid: "{{glyph}} sm watch: --max-consecutive-failures must be a non-negative integer (got {{raw}}).\n {{hint}}\n",
|
|
23940
24422
|
maxConsecutiveFailuresInvalidHint: "Pass an integer >= 0 (0 disables the circuit-breaker; the default is 5).",
|
|
24423
|
+
/**
|
|
24424
|
+
* §3.1b two-line block. Validation rejection for `--max-scan`.
|
|
24425
|
+
*/
|
|
24426
|
+
maxScanInvalid: "{{glyph}} sm watch: --max-scan must be an integer >= 1 (got {{raw}}).\n {{hint}}\n",
|
|
24427
|
+
maxScanInvalidHint: "Pass a positive integer, e.g. --max-scan 50000.",
|
|
23941
24428
|
/**
|
|
23942
24429
|
* §3.1b two-line block. Validation rejection for `--max-nodes`.
|
|
23943
24430
|
*/
|
|
@@ -24033,6 +24520,7 @@ async function runWatchLoop(opts) {
|
|
|
24033
24520
|
circuitBreaker: { maxConsecutiveFailures: breakerLimit },
|
|
24034
24521
|
killSwitches: readConformanceKillSwitches(),
|
|
24035
24522
|
...opts.maxBatches !== void 0 ? { maxBatches: opts.maxBatches } : {},
|
|
24523
|
+
...opts.maxScan !== void 0 ? { maxScanOverride: opts.maxScan } : {},
|
|
24036
24524
|
...opts.maxNodes !== void 0 ? { maxNodesOverride: opts.maxNodes } : {},
|
|
24037
24525
|
events: {
|
|
24038
24526
|
onBatch: (outcome) => {
|
|
@@ -24157,9 +24645,13 @@ var WatchCommand = class extends SmCommand {
|
|
|
24157
24645
|
required: false,
|
|
24158
24646
|
description: "Shut down with exit 2 after N consecutive batch failures (default 5; 0 disables the breaker)."
|
|
24159
24647
|
});
|
|
24648
|
+
maxScan = Option29.String("--max-scan", {
|
|
24649
|
+
required: false,
|
|
24650
|
+
description: "Per-batch override of scan.maxScan (default 50000), the WALK-INTAKE ceiling. The scan walks, parses, analyzes, and reference-validates the full corpus up to this number. Bidirectional: raises OR lowers the ceiling. When a batch hits it, additional files are dropped in stable order and the UI surfaces the persistent truncation banner. Validation: integer >= 1."
|
|
24651
|
+
});
|
|
24160
24652
|
maxNodes = Option29.String("--max-nodes", {
|
|
24161
24653
|
required: false,
|
|
24162
|
-
description: "Per-batch override of scan.maxNodes (default 256)
|
|
24654
|
+
description: "Per-batch override of scan.maxNodes (default 256), the MAP RENDER cap (pure metadata): it does NOT bound the scan, only the graph projection. Bidirectional: raises OR lowers the render cap. Validation: integer >= 1."
|
|
24163
24655
|
});
|
|
24164
24656
|
// Long-running verb, the watcher prints its own "stopped" line on
|
|
24165
24657
|
// SIGINT / SIGTERM. Adding `done in <…>` after that would be noise.
|
|
@@ -24168,6 +24660,8 @@ var WatchCommand = class extends SmCommand {
|
|
|
24168
24660
|
const roots = this.roots.length > 0 ? this.roots : ["."];
|
|
24169
24661
|
const breaker = parseBreakerLimit(this.maxConsecutiveFailures, this.context.stderr, this.noColor);
|
|
24170
24662
|
if (breaker === null) return ExitCode.Error;
|
|
24663
|
+
const maxScan = parseMaxScanLimit(this.maxScan, this.context.stderr, this.noColor);
|
|
24664
|
+
if (maxScan === null) return ExitCode.Error;
|
|
24171
24665
|
const maxNodes = parseMaxNodesLimit(this.maxNodes, this.context.stderr, this.noColor);
|
|
24172
24666
|
if (maxNodes === null) return ExitCode.Error;
|
|
24173
24667
|
const watchOpts = {
|
|
@@ -24182,6 +24676,7 @@ var WatchCommand = class extends SmCommand {
|
|
|
24182
24676
|
printer: this.printer
|
|
24183
24677
|
};
|
|
24184
24678
|
if (breaker !== void 0) watchOpts.maxConsecutiveFailures = breaker;
|
|
24679
|
+
if (maxScan !== void 0) watchOpts.maxScan = maxScan;
|
|
24185
24680
|
if (maxNodes !== void 0) watchOpts.maxNodes = maxNodes;
|
|
24186
24681
|
return runWatchLoop(watchOpts);
|
|
24187
24682
|
}
|
|
@@ -24203,6 +24698,23 @@ function parseBreakerLimit(raw, stderr, noColor) {
|
|
|
24203
24698
|
}
|
|
24204
24699
|
return parsed;
|
|
24205
24700
|
}
|
|
24701
|
+
function parseMaxScanLimit(raw, stderr, noColor) {
|
|
24702
|
+
if (raw === void 0) return void 0;
|
|
24703
|
+
const n = Number(raw);
|
|
24704
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
24705
|
+
const stderrTty = stderr;
|
|
24706
|
+
const ansi = ansiFor({ isTTY: stderrTty.isTTY === true, noColorFlag: noColor });
|
|
24707
|
+
stderr.write(
|
|
24708
|
+
tx(WATCH_TEXTS.maxScanInvalid, {
|
|
24709
|
+
glyph: ansi.red("\u2715"),
|
|
24710
|
+
raw,
|
|
24711
|
+
hint: ansi.dim(WATCH_TEXTS.maxScanInvalidHint)
|
|
24712
|
+
})
|
|
24713
|
+
);
|
|
24714
|
+
return null;
|
|
24715
|
+
}
|
|
24716
|
+
return n;
|
|
24717
|
+
}
|
|
24206
24718
|
function parseMaxNodesLimit(raw, stderr, noColor) {
|
|
24207
24719
|
if (raw === void 0) return void 0;
|
|
24208
24720
|
const n = Number(raw);
|
|
@@ -24287,16 +24799,20 @@ var ScanCommand = class extends SmCommand {
|
|
|
24287
24799
|
yes = Option30.Boolean("--yes", false, {
|
|
24288
24800
|
description: "Non-interactive mode. For ambiguous activeProvider auto-detect, multiple provider markers (.claude/, .codex/, AGENTS.md, .cursor/) under the scan tree exit non-zero instead of prompting; set the lens manually via `sm config set activeProvider <id>` and re-run. Also auto-confirms the pre-1.0 schema-drift rebuild (when the DB was written by a different skill-map major.minor it is deleted and regenerated) instead of prompting."
|
|
24289
24801
|
});
|
|
24802
|
+
maxScan = Option30.String("--max-scan", {
|
|
24803
|
+
required: false,
|
|
24804
|
+
description: "Per-invocation override of `scan.maxScan` (default 50000). The WALK-INTAKE ceiling: the scan walks, parses, analyzes, and reference-validates the full corpus up to this number. Bidirectional: raises OR lowers the ceiling. When the walker hits it, additional files are dropped in stable order and the scan is marked truncated in scan_meta (the UI raises a persistent banner pointing at the .skillmapignore editor in Settings \u2192 Project). Validation: integer >= 1."
|
|
24805
|
+
});
|
|
24290
24806
|
maxNodes = Option30.String("--max-nodes", {
|
|
24291
24807
|
required: false,
|
|
24292
|
-
description: "Per-invocation override of `scan.maxNodes` (default 256).
|
|
24808
|
+
description: "Per-invocation override of `scan.maxNodes` (default 256). The MAP RENDER cap (pure metadata): it does NOT bound the scan, only how many nodes the graph view projects onto the canvas. Bidirectional: raises OR lowers the render cap. Validation: integer >= 1."
|
|
24293
24809
|
});
|
|
24294
24810
|
// Each branch in the orchestrator maps to one validation gate
|
|
24295
24811
|
// (--watch alias / --changed mutex / -g mutex / dispatch).
|
|
24296
24812
|
// Splitting per branch scatters the gate from the value it gates.
|
|
24297
24813
|
async run() {
|
|
24298
|
-
const
|
|
24299
|
-
if (
|
|
24814
|
+
const caps = this.parseCapFlags();
|
|
24815
|
+
if (caps.kind === "error") return caps.exit;
|
|
24300
24816
|
if (this.watch) return this.runWatchAlias();
|
|
24301
24817
|
if (this.changed && this.noBuiltIns) {
|
|
24302
24818
|
const ansi = this.ansiFor("stderr");
|
|
@@ -24333,7 +24849,7 @@ var ScanCommand = class extends SmCommand {
|
|
|
24333
24849
|
colorEnabled,
|
|
24334
24850
|
yes: this.yes,
|
|
24335
24851
|
style,
|
|
24336
|
-
...
|
|
24852
|
+
...capOverrides(caps)
|
|
24337
24853
|
});
|
|
24338
24854
|
if (outcome.kind === "ok") {
|
|
24339
24855
|
setScanExtensions(buildScanExtensionSet(outcome.executedExtensionIds));
|
|
@@ -24348,21 +24864,35 @@ var ScanCommand = class extends SmCommand {
|
|
|
24348
24864
|
return this.renderFailure(outcome);
|
|
24349
24865
|
}
|
|
24350
24866
|
/**
|
|
24351
|
-
* Parse `--max-
|
|
24352
|
-
*
|
|
24353
|
-
*
|
|
24354
|
-
*
|
|
24867
|
+
* Parse both cap flags in one pass: `--max-scan <N>` (the WALK-INTAKE
|
|
24868
|
+
* ceiling) and `--max-nodes <N>` (the MAP RENDER cap). Returns both
|
|
24869
|
+
* resolved values (each `undefined` when its flag was omitted) or an
|
|
24870
|
+
* error sentinel after printing the §3.1b validation block for the
|
|
24871
|
+
* first offending flag. Invalid (non-integer, < 1) exits 2 per
|
|
24872
|
+
* spec/cli-contract.md §Scan.
|
|
24873
|
+
*/
|
|
24874
|
+
parseCapFlags() {
|
|
24875
|
+
const scan = this.parseIntegerFlag(this.maxScan, SCAN_TEXTS.maxScanInvalid, SCAN_TEXTS.maxScanInvalidHint);
|
|
24876
|
+
if (scan.kind === "error") return scan;
|
|
24877
|
+
const nodes = this.parseIntegerFlag(this.maxNodes, SCAN_TEXTS.maxNodesInvalid, SCAN_TEXTS.maxNodesInvalidHint);
|
|
24878
|
+
if (nodes.kind === "error") return nodes;
|
|
24879
|
+
return { kind: "ok", maxScan: scan.value, maxNodes: nodes.value };
|
|
24880
|
+
}
|
|
24881
|
+
/**
|
|
24882
|
+
* Shared integer-flag parser for `--max-scan` / `--max-nodes`. Both
|
|
24883
|
+
* accept the same shape (integer >= 1) and render the same §3.1b
|
|
24884
|
+
* validation block; only the template + hint differ.
|
|
24355
24885
|
*/
|
|
24356
|
-
|
|
24357
|
-
if (
|
|
24358
|
-
const n = Number(
|
|
24886
|
+
parseIntegerFlag(raw, invalidTemplate, invalidHint) {
|
|
24887
|
+
if (raw === void 0) return { kind: "ok", value: void 0 };
|
|
24888
|
+
const n = Number(raw);
|
|
24359
24889
|
if (!Number.isInteger(n) || n < 1) {
|
|
24360
24890
|
const ansi = this.ansiFor("stderr");
|
|
24361
24891
|
this.printer.info(
|
|
24362
|
-
tx(
|
|
24892
|
+
tx(invalidTemplate, {
|
|
24363
24893
|
glyph: ansi.red("\u2715"),
|
|
24364
|
-
value:
|
|
24365
|
-
hint: ansi.dim(
|
|
24894
|
+
value: raw,
|
|
24895
|
+
hint: ansi.dim(invalidHint)
|
|
24366
24896
|
})
|
|
24367
24897
|
);
|
|
24368
24898
|
return { kind: "error", exit: ExitCode.Error };
|
|
@@ -24388,7 +24918,7 @@ var ScanCommand = class extends SmCommand {
|
|
|
24388
24918
|
}
|
|
24389
24919
|
this.emitElapsed = false;
|
|
24390
24920
|
const roots = this.roots.length > 0 ? this.roots : ["."];
|
|
24391
|
-
const
|
|
24921
|
+
const caps = this.parseCapFlags();
|
|
24392
24922
|
return runWatchLoop({
|
|
24393
24923
|
roots,
|
|
24394
24924
|
json: this.json,
|
|
@@ -24399,7 +24929,7 @@ var ScanCommand = class extends SmCommand {
|
|
|
24399
24929
|
noPlugins: this.noPlugins,
|
|
24400
24930
|
context: this.context,
|
|
24401
24931
|
printer: this.printer,
|
|
24402
|
-
...
|
|
24932
|
+
...caps.kind === "ok" ? capOverrides(caps) : {}
|
|
24403
24933
|
});
|
|
24404
24934
|
}
|
|
24405
24935
|
/**
|
|
@@ -24515,24 +25045,24 @@ var ScanCommand = class extends SmCommand {
|
|
|
24515
25045
|
);
|
|
24516
25046
|
}
|
|
24517
25047
|
/**
|
|
24518
|
-
* Surface the §
|
|
24519
|
-
* accepting files because
|
|
24520
|
-
*
|
|
24521
|
-
*
|
|
24522
|
-
* the project
|
|
24523
|
-
*
|
|
25048
|
+
* Surface the §Scan truncation notice when the walker actually
|
|
25049
|
+
* stopped accepting files because the walk ceiling (`scan.maxScan` or
|
|
25050
|
+
* the `--max-scan` override) was reached and extra files were dropped.
|
|
25051
|
+
* Fires on `result.scanTruncated`, the kernel sets it when the walker
|
|
25052
|
+
* hit the ceiling; a project with at most the ceiling many files ends
|
|
25053
|
+
* the loop naturally with `scanTruncated: false`, so the notice stays
|
|
25054
|
+
* silent.
|
|
24524
25055
|
*/
|
|
24525
25056
|
maybePrintCapNotice(result, ansi) {
|
|
24526
|
-
|
|
24527
|
-
|
|
24528
|
-
|
|
24529
|
-
const
|
|
24530
|
-
if (result.stats.filesWalked <= effectiveLimit) return;
|
|
25057
|
+
if (result.scanTruncated !== true) return;
|
|
25058
|
+
const ceiling = result.scanCeiling;
|
|
25059
|
+
if (ceiling === void 0) return;
|
|
25060
|
+
const source = this.maxScan !== void 0 ? "--max-scan" : "scan.maxScan";
|
|
24531
25061
|
this.printer.info(
|
|
24532
25062
|
tx(SCAN_TEXTS.scanCappedNotice, {
|
|
24533
25063
|
glyph: ansi.yellow("\u26A0"),
|
|
24534
|
-
limit: String(
|
|
24535
|
-
source
|
|
25064
|
+
limit: String(ceiling),
|
|
25065
|
+
source,
|
|
24536
25066
|
hint: ansi.dim(SCAN_TEXTS.scanCappedNoticeHint)
|
|
24537
25067
|
})
|
|
24538
25068
|
);
|
|
@@ -24625,6 +25155,12 @@ function formatScanCounts(opts) {
|
|
|
24625
25155
|
function countNoun(count3, singular, plural) {
|
|
24626
25156
|
return count3 === 1 ? singular : plural;
|
|
24627
25157
|
}
|
|
25158
|
+
function capOverrides(caps) {
|
|
25159
|
+
const out = {};
|
|
25160
|
+
if (caps.maxScan !== void 0) out.maxScan = caps.maxScan;
|
|
25161
|
+
if (caps.maxNodes !== void 0) out.maxNodes = caps.maxNodes;
|
|
25162
|
+
return out;
|
|
25163
|
+
}
|
|
24628
25164
|
|
|
24629
25165
|
// cli/commands/scan-compare.ts
|
|
24630
25166
|
import { access, readFile as readFile5 } from "fs/promises";
|
|
@@ -24877,11 +25413,11 @@ import { existsSync as existsSync31 } from "fs";
|
|
|
24877
25413
|
import { Command as Command34, Option as Option32 } from "clipanion";
|
|
24878
25414
|
|
|
24879
25415
|
// kernel/util/dev-mode.ts
|
|
24880
|
-
import { sep as
|
|
25416
|
+
import { sep as sep7 } from "path";
|
|
24881
25417
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
24882
25418
|
var SELF_PATH = fileURLToPath5(import.meta.url);
|
|
24883
|
-
var IS_DEV_BUILD = isDevBuildFromPath(SELF_PATH,
|
|
24884
|
-
function isDevBuildFromPath(filePath, separator =
|
|
25419
|
+
var IS_DEV_BUILD = isDevBuildFromPath(SELF_PATH, sep7);
|
|
25420
|
+
function isDevBuildFromPath(filePath, separator = sep7) {
|
|
24885
25421
|
return !filePath.includes(`${separator}node_modules${separator}`);
|
|
24886
25422
|
}
|
|
24887
25423
|
function isDevBuild() {
|
|
@@ -24907,7 +25443,7 @@ import { WebSocketServer } from "ws";
|
|
|
24907
25443
|
// server/app.ts
|
|
24908
25444
|
import { Hono } from "hono";
|
|
24909
25445
|
import { bodyLimit } from "hono/body-limit";
|
|
24910
|
-
import { HTTPException as
|
|
25446
|
+
import { HTTPException as HTTPException18 } from "hono/http-exception";
|
|
24911
25447
|
|
|
24912
25448
|
// core/config/service.ts
|
|
24913
25449
|
var ConfigService = class {
|
|
@@ -24987,6 +25523,12 @@ var SERVER_TEXTS = {
|
|
|
24987
25523
|
// Pagination caps on /api/nodes.
|
|
24988
25524
|
paginationLimitTooLarge: "limit={{value}} exceeds the maximum of {{max}}.",
|
|
24989
25525
|
paginationInvalidInteger: "{{name}}={{value}} is not a non-negative integer.",
|
|
25526
|
+
// /api/branch, the `limit` query param must be a positive integer
|
|
25527
|
+
// (>= 1). Non-integer / zero / negative rejects with 400 bad-query
|
|
25528
|
+
// before the branch projection runs. A value above the scan's
|
|
25529
|
+
// effective maxRenderNodes is silently clamped down (never an error),
|
|
25530
|
+
// so this message only ever fires on a malformed / < 1 input.
|
|
25531
|
+
branchInvalidLimit: "limit={{value}} is not a positive integer (>= 1).",
|
|
24990
25532
|
// Required-query-param miss (used by `parseRequiredString`). The
|
|
24991
25533
|
// route names the offending parameter so the operator gets a useful
|
|
24992
25534
|
// 400 instead of a generic "missing input".
|
|
@@ -25393,20 +25935,148 @@ function registerAnnotationsRoute(app, deps) {
|
|
|
25393
25935
|
});
|
|
25394
25936
|
}
|
|
25395
25937
|
|
|
25396
|
-
// server/routes/
|
|
25397
|
-
import { HTTPException as HTTPException3 } from "hono/http-exception";
|
|
25398
|
-
|
|
25399
|
-
// server/util/parse-query.ts
|
|
25938
|
+
// server/routes/branch.ts
|
|
25400
25939
|
import { HTTPException as HTTPException2 } from "hono/http-exception";
|
|
25401
|
-
|
|
25402
|
-
|
|
25403
|
-
|
|
25404
|
-
|
|
25940
|
+
|
|
25941
|
+
// server/envelope.ts
|
|
25942
|
+
var REST_ENVELOPE_SCHEMA_VERSION = "1";
|
|
25943
|
+
function buildListEnvelope(opts) {
|
|
25944
|
+
const counts = {
|
|
25945
|
+
total: opts.total,
|
|
25946
|
+
returned: opts.items.length
|
|
25947
|
+
};
|
|
25948
|
+
if (opts.page) counts.page = opts.page;
|
|
25949
|
+
return {
|
|
25950
|
+
schemaVersion: REST_ENVELOPE_SCHEMA_VERSION,
|
|
25951
|
+
kind: opts.kind,
|
|
25952
|
+
items: opts.items,
|
|
25953
|
+
filters: opts.filters,
|
|
25954
|
+
counts,
|
|
25955
|
+
kindRegistry: opts.kindRegistry,
|
|
25956
|
+
providerRegistry: opts.providerRegistry,
|
|
25957
|
+
contributionsRegistry: opts.contributionsRegistry
|
|
25958
|
+
};
|
|
25959
|
+
}
|
|
25960
|
+
function buildValueEnvelope(kind, value, kindRegistry, providerRegistry, contributionsRegistry) {
|
|
25961
|
+
return {
|
|
25962
|
+
schemaVersion: REST_ENVELOPE_SCHEMA_VERSION,
|
|
25963
|
+
kind,
|
|
25964
|
+
value,
|
|
25965
|
+
kindRegistry,
|
|
25966
|
+
providerRegistry,
|
|
25967
|
+
contributionsRegistry
|
|
25968
|
+
};
|
|
25969
|
+
}
|
|
25970
|
+
|
|
25971
|
+
// server/routes/branch.ts
|
|
25972
|
+
var DEFAULT_MAX_RENDER_NODES2 = 256;
|
|
25973
|
+
function registerBranchRoute(app, deps) {
|
|
25974
|
+
app.get("/api/branch", async (c) => {
|
|
25975
|
+
const prefixes = c.req.queries("path")?.filter((p) => p.length > 0) ?? [];
|
|
25976
|
+
const limitOverride = parseLimit(c.req.query("limit"));
|
|
25977
|
+
const loaded = await tryWithSqlite(
|
|
25978
|
+
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
25979
|
+
async (adapter) => {
|
|
25980
|
+
const maxRenderNodes = await adapter.scans.effectiveMaxRenderNodes();
|
|
25981
|
+
const cap = limitOverride === void 0 ? maxRenderNodes : Math.min(limitOverride, maxRenderNodes);
|
|
25982
|
+
const [branch, favSet] = await Promise.all([
|
|
25983
|
+
adapter.scans.loadBranch(prefixes, cap),
|
|
25984
|
+
adapter.favorites.listPaths()
|
|
25985
|
+
]);
|
|
25986
|
+
const paths = branch.nodes.map((n) => n.path);
|
|
25987
|
+
const [tagRows, contribRows] = await Promise.all([
|
|
25988
|
+
adapter.tags.listForPaths(paths),
|
|
25989
|
+
adapter.contributions.listForPaths(paths)
|
|
25990
|
+
]);
|
|
25991
|
+
return { branch, favSet, tagRows, contribRows, cap };
|
|
25992
|
+
}
|
|
25993
|
+
);
|
|
25994
|
+
return c.json(buildBranchResponse(prefixes, loaded));
|
|
25995
|
+
});
|
|
25996
|
+
}
|
|
25997
|
+
function buildBranchResponse(prefixes, loaded) {
|
|
25998
|
+
if (loaded === null) {
|
|
25999
|
+
return {
|
|
26000
|
+
schemaVersion: REST_ENVELOPE_SCHEMA_VERSION,
|
|
26001
|
+
kind: "branch",
|
|
26002
|
+
branch: { paths: [...new Set(prefixes)], total: 0, rendered: 0, truncated: false, cap: DEFAULT_MAX_RENDER_NODES2 },
|
|
26003
|
+
nodes: [],
|
|
26004
|
+
links: [],
|
|
26005
|
+
issues: []
|
|
26006
|
+
};
|
|
26007
|
+
}
|
|
26008
|
+
const { branch, favSet, tagRows, contribRows, cap } = loaded;
|
|
26009
|
+
const tagsByPath = groupTagsByPath(tagRows);
|
|
26010
|
+
const contribByPath = groupContribsByPath(contribRows);
|
|
26011
|
+
const nodes = branch.nodes.map((n) => ({
|
|
26012
|
+
...n,
|
|
26013
|
+
isFavorite: favSet.has(n.path),
|
|
26014
|
+
tags: tagsByPath.get(n.path) ?? [],
|
|
26015
|
+
contributions: contribByPath.get(n.path) ?? []
|
|
26016
|
+
}));
|
|
26017
|
+
const rendered = nodes.length;
|
|
26018
|
+
return {
|
|
26019
|
+
schemaVersion: REST_ENVELOPE_SCHEMA_VERSION,
|
|
26020
|
+
kind: "branch",
|
|
26021
|
+
branch: {
|
|
26022
|
+
// Echo the de-duped prefixes the storage layer actually scoped on.
|
|
26023
|
+
paths: branch.paths,
|
|
26024
|
+
total: branch.total,
|
|
26025
|
+
rendered,
|
|
26026
|
+
truncated: branch.total > cap,
|
|
26027
|
+
cap
|
|
26028
|
+
},
|
|
26029
|
+
nodes,
|
|
26030
|
+
links: branch.links,
|
|
26031
|
+
issues: branch.issues
|
|
26032
|
+
};
|
|
26033
|
+
}
|
|
26034
|
+
function parseLimit(raw) {
|
|
26035
|
+
if (raw === void 0 || raw.length === 0) return void 0;
|
|
26036
|
+
const trimmed = raw.trim();
|
|
26037
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
26038
|
+
if (!Number.isInteger(parsed) || parsed < 1 || String(parsed) !== trimmed) {
|
|
26039
|
+
throw new HTTPException2(400, {
|
|
26040
|
+
message: tx(SERVER_TEXTS.branchInvalidLimit, { value: raw })
|
|
26041
|
+
});
|
|
26042
|
+
}
|
|
26043
|
+
return parsed;
|
|
26044
|
+
}
|
|
26045
|
+
function groupTagsByPath(rows) {
|
|
26046
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
26047
|
+
for (const r of rows) {
|
|
26048
|
+
const set = buckets.get(r.nodePath);
|
|
26049
|
+
if (set) set.add(r.tag);
|
|
26050
|
+
else buckets.set(r.nodePath, /* @__PURE__ */ new Set([r.tag]));
|
|
26051
|
+
}
|
|
26052
|
+
const out = /* @__PURE__ */ new Map();
|
|
26053
|
+
for (const [path, set] of buckets) out.set(path, [...set].sort());
|
|
26054
|
+
return out;
|
|
26055
|
+
}
|
|
26056
|
+
function groupContribsByPath(rows) {
|
|
26057
|
+
const out = /* @__PURE__ */ new Map();
|
|
26058
|
+
for (const r of rows) {
|
|
26059
|
+
const list = out.get(r.nodePath);
|
|
26060
|
+
if (list) list.push(r);
|
|
26061
|
+
else out.set(r.nodePath, [r]);
|
|
26062
|
+
}
|
|
26063
|
+
return out;
|
|
26064
|
+
}
|
|
26065
|
+
|
|
26066
|
+
// server/routes/contributions.ts
|
|
26067
|
+
import { HTTPException as HTTPException4 } from "hono/http-exception";
|
|
26068
|
+
|
|
26069
|
+
// server/util/parse-query.ts
|
|
26070
|
+
import { HTTPException as HTTPException3 } from "hono/http-exception";
|
|
26071
|
+
function parseCsv(value) {
|
|
26072
|
+
if (value === void 0) return [];
|
|
26073
|
+
return value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
26074
|
+
}
|
|
25405
26075
|
function parsePagination(query, defaults) {
|
|
25406
26076
|
const offset = parseNonNegativeInt(query.offset, "offset", 0);
|
|
25407
26077
|
const limit = parseNonNegativeInt(query.limit, "limit", defaults.limit);
|
|
25408
26078
|
if (limit > defaults.max) {
|
|
25409
|
-
throw new
|
|
26079
|
+
throw new HTTPException3(400, {
|
|
25410
26080
|
message: tx(SERVER_TEXTS.paginationLimitTooLarge, {
|
|
25411
26081
|
value: limit,
|
|
25412
26082
|
max: defaults.max
|
|
@@ -25420,7 +26090,7 @@ function parseBooleanFlag(value) {
|
|
|
25420
26090
|
}
|
|
25421
26091
|
function parseRequiredString(value, name) {
|
|
25422
26092
|
if (typeof value !== "string" || value.length === 0) {
|
|
25423
|
-
throw new
|
|
26093
|
+
throw new HTTPException3(400, {
|
|
25424
26094
|
message: tx(SERVER_TEXTS.queryRequiredString, { name })
|
|
25425
26095
|
});
|
|
25426
26096
|
}
|
|
@@ -25431,7 +26101,7 @@ function parseNonNegativeInt(raw, name, fallback) {
|
|
|
25431
26101
|
const trimmed = raw.trim();
|
|
25432
26102
|
const parsed = Number.parseInt(trimmed, 10);
|
|
25433
26103
|
if (!Number.isInteger(parsed) || parsed < 0 || String(parsed) !== trimmed) {
|
|
25434
|
-
throw new
|
|
26104
|
+
throw new HTTPException3(400, {
|
|
25435
26105
|
message: tx(SERVER_TEXTS.paginationInvalidInteger, { name, value: raw })
|
|
25436
26106
|
});
|
|
25437
26107
|
}
|
|
@@ -25442,7 +26112,7 @@ function parseNonNegativeInt(raw, name, fallback) {
|
|
|
25442
26112
|
var QUALIFIED_ID_SEGMENT = /^[A-Za-z0-9._-]+$/;
|
|
25443
26113
|
function parseQualifiedIdSegment(value, name) {
|
|
25444
26114
|
if (!QUALIFIED_ID_SEGMENT.test(value)) {
|
|
25445
|
-
throw new
|
|
26115
|
+
throw new HTTPException4(400, {
|
|
25446
26116
|
message: tx(SERVER_TEXTS.qualifiedIdMalformed, {
|
|
25447
26117
|
name,
|
|
25448
26118
|
value: sanitizeForTerminal(value)
|
|
@@ -25472,7 +26142,7 @@ function registerContributionsRoutes(app, deps) {
|
|
|
25472
26142
|
(e) => e.pluginId === pluginId && e.extensionId === extensionId && e.contributionId === contributionId
|
|
25473
26143
|
);
|
|
25474
26144
|
if (!catalogEntry) {
|
|
25475
|
-
throw new
|
|
26145
|
+
throw new HTTPException4(404, {
|
|
25476
26146
|
message: tx(SERVER_TEXTS.contributionUnknown, {
|
|
25477
26147
|
pluginId: sanitizeForTerminal(pluginId),
|
|
25478
26148
|
extensionId: sanitizeForTerminal(extensionId),
|
|
@@ -25503,46 +26173,14 @@ function registerContributionsRoutes(app, deps) {
|
|
|
25503
26173
|
}
|
|
25504
26174
|
|
|
25505
26175
|
// server/routes/config.ts
|
|
25506
|
-
import { HTTPException as
|
|
25507
|
-
|
|
25508
|
-
// server/envelope.ts
|
|
25509
|
-
var REST_ENVELOPE_SCHEMA_VERSION = "1";
|
|
25510
|
-
function buildListEnvelope(opts) {
|
|
25511
|
-
const counts = {
|
|
25512
|
-
total: opts.total,
|
|
25513
|
-
returned: opts.items.length
|
|
25514
|
-
};
|
|
25515
|
-
if (opts.page) counts.page = opts.page;
|
|
25516
|
-
return {
|
|
25517
|
-
schemaVersion: REST_ENVELOPE_SCHEMA_VERSION,
|
|
25518
|
-
kind: opts.kind,
|
|
25519
|
-
items: opts.items,
|
|
25520
|
-
filters: opts.filters,
|
|
25521
|
-
counts,
|
|
25522
|
-
kindRegistry: opts.kindRegistry,
|
|
25523
|
-
providerRegistry: opts.providerRegistry,
|
|
25524
|
-
contributionsRegistry: opts.contributionsRegistry
|
|
25525
|
-
};
|
|
25526
|
-
}
|
|
25527
|
-
function buildValueEnvelope(kind, value, kindRegistry, providerRegistry, contributionsRegistry) {
|
|
25528
|
-
return {
|
|
25529
|
-
schemaVersion: REST_ENVELOPE_SCHEMA_VERSION,
|
|
25530
|
-
kind,
|
|
25531
|
-
value,
|
|
25532
|
-
kindRegistry,
|
|
25533
|
-
providerRegistry,
|
|
25534
|
-
contributionsRegistry
|
|
25535
|
-
};
|
|
25536
|
-
}
|
|
25537
|
-
|
|
25538
|
-
// server/routes/config.ts
|
|
26176
|
+
import { HTTPException as HTTPException5 } from "hono/http-exception";
|
|
25539
26177
|
function registerConfigRoute(app, deps) {
|
|
25540
26178
|
app.get("/api/config", (c) => {
|
|
25541
26179
|
let loaded;
|
|
25542
26180
|
try {
|
|
25543
26181
|
loaded = deps.configService.get();
|
|
25544
26182
|
} catch (err) {
|
|
25545
|
-
throw new
|
|
26183
|
+
throw new HTTPException5(500, { message: formatErrorMessage(err) });
|
|
25546
26184
|
}
|
|
25547
26185
|
for (const warn of loaded.warnings) {
|
|
25548
26186
|
log.warn(sanitizeForTerminal(warn));
|
|
@@ -25560,7 +26198,7 @@ function registerConfigRoute(app, deps) {
|
|
|
25560
26198
|
}
|
|
25561
26199
|
|
|
25562
26200
|
// server/routes/favorites.ts
|
|
25563
|
-
import { HTTPException as
|
|
26201
|
+
import { HTTPException as HTTPException6 } from "hono/http-exception";
|
|
25564
26202
|
|
|
25565
26203
|
// server/path-codec.ts
|
|
25566
26204
|
var PathCodecError = class extends Error {
|
|
@@ -25600,7 +26238,7 @@ function registerFavoritesRoutes(app, deps) {
|
|
|
25600
26238
|
}
|
|
25601
26239
|
);
|
|
25602
26240
|
if (!result || !result.found) {
|
|
25603
|
-
throw new
|
|
26241
|
+
throw new HTTPException6(404, {
|
|
25604
26242
|
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
25605
26243
|
});
|
|
25606
26244
|
}
|
|
@@ -25620,14 +26258,57 @@ function decodePath(pathB64) {
|
|
|
25620
26258
|
return decodeNodePath(pathB64);
|
|
25621
26259
|
} catch (err) {
|
|
25622
26260
|
if (err instanceof PathCodecError) {
|
|
25623
|
-
throw new
|
|
26261
|
+
throw new HTTPException6(404, { message: SERVER_TEXTS.pathB64Malformed });
|
|
25624
26262
|
}
|
|
25625
26263
|
throw err;
|
|
25626
26264
|
}
|
|
25627
26265
|
}
|
|
25628
26266
|
|
|
26267
|
+
// server/routes/folders.ts
|
|
26268
|
+
function registerFoldersRoute(app, deps) {
|
|
26269
|
+
app.get("/api/folders", async (c) => {
|
|
26270
|
+
const loaded = await tryWithSqlite(
|
|
26271
|
+
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
26272
|
+
async (adapter) => {
|
|
26273
|
+
const [liteNodes2, issueCounts2] = await Promise.all([
|
|
26274
|
+
adapter.scans.listLiteNodes(),
|
|
26275
|
+
adapter.scans.issueCountsByPath()
|
|
26276
|
+
]);
|
|
26277
|
+
return { liteNodes: liteNodes2, issueCounts: issueCounts2 };
|
|
26278
|
+
}
|
|
26279
|
+
);
|
|
26280
|
+
const liteNodes = loaded?.liteNodes ?? [];
|
|
26281
|
+
const issueCounts = loaded?.issueCounts ?? /* @__PURE__ */ new Map();
|
|
26282
|
+
const items = liteNodes.map((n) => {
|
|
26283
|
+
const counts = issueCounts.get(n.path);
|
|
26284
|
+
return {
|
|
26285
|
+
path: n.path,
|
|
26286
|
+
kind: n.kind,
|
|
26287
|
+
linksInCount: n.linksInCount,
|
|
26288
|
+
linksOutCount: n.linksOutCount,
|
|
26289
|
+
tokensTotal: n.tokensTotal,
|
|
26290
|
+
modifiedAtMs: n.modifiedAtMs,
|
|
26291
|
+
errorCount: counts?.error ?? 0,
|
|
26292
|
+
warnCount: counts?.warn ?? 0,
|
|
26293
|
+
sidecarStatus: n.sidecarStatus
|
|
26294
|
+
};
|
|
26295
|
+
});
|
|
26296
|
+
return c.json(
|
|
26297
|
+
buildListEnvelope({
|
|
26298
|
+
kind: "folders",
|
|
26299
|
+
items,
|
|
26300
|
+
filters: {},
|
|
26301
|
+
total: items.length,
|
|
26302
|
+
kindRegistry: deps.kindRegistry,
|
|
26303
|
+
providerRegistry: deps.providerRegistry,
|
|
26304
|
+
contributionsRegistry: deps.contributionsRegistry
|
|
26305
|
+
})
|
|
26306
|
+
);
|
|
26307
|
+
});
|
|
26308
|
+
}
|
|
26309
|
+
|
|
25629
26310
|
// server/routes/graph.ts
|
|
25630
|
-
import { HTTPException as
|
|
26311
|
+
import { HTTPException as HTTPException7 } from "hono/http-exception";
|
|
25631
26312
|
var DEFAULT_FORMAT2 = "ascii";
|
|
25632
26313
|
var FORMAT_ID_PATTERN = /^[a-z0-9-]+$/;
|
|
25633
26314
|
var FORMAT_ID_MAX = 32;
|
|
@@ -25635,7 +26316,7 @@ function registerGraphRoute(app, deps) {
|
|
|
25635
26316
|
app.get("/api/graph", async (c) => {
|
|
25636
26317
|
const format = c.req.query("format") ?? DEFAULT_FORMAT2;
|
|
25637
26318
|
if (format.length > FORMAT_ID_MAX || !FORMAT_ID_PATTERN.test(format)) {
|
|
25638
|
-
throw new
|
|
26319
|
+
throw new HTTPException7(400, {
|
|
25639
26320
|
// Sanitize defensively, the regex above already rejects ANSI
|
|
25640
26321
|
// and control bytes, but the message interpolates user input
|
|
25641
26322
|
// and the BFF mirrors error envelopes into the server log.
|
|
@@ -25651,7 +26332,7 @@ function registerGraphRoute(app, deps) {
|
|
|
25651
26332
|
const formatter = formatters.find((f) => f.formatId === format);
|
|
25652
26333
|
if (!formatter) {
|
|
25653
26334
|
const available = formatters.map((f) => f.formatId).sort().join(", ");
|
|
25654
|
-
throw new
|
|
26335
|
+
throw new HTTPException7(400, {
|
|
25655
26336
|
message: tx(SERVER_TEXTS.graphUnknownFormat, {
|
|
25656
26337
|
format,
|
|
25657
26338
|
available: available || "(none)"
|
|
@@ -25827,18 +26508,18 @@ function registerLinksRoute(app, deps) {
|
|
|
25827
26508
|
}
|
|
25828
26509
|
|
|
25829
26510
|
// server/routes/nodes.ts
|
|
25830
|
-
import { HTTPException as
|
|
26511
|
+
import { HTTPException as HTTPException8 } from "hono/http-exception";
|
|
25831
26512
|
|
|
25832
26513
|
// server/node-body.ts
|
|
25833
26514
|
import { constants as fsConstants2 } from "fs";
|
|
25834
26515
|
import { open } from "fs/promises";
|
|
25835
|
-
import { isAbsolute as
|
|
26516
|
+
import { isAbsolute as isAbsolute13, resolve as resolvePath2, relative as relativePath, sep as sep8 } from "path";
|
|
25836
26517
|
async function readNodeBody(cwd, relPath) {
|
|
25837
|
-
if (
|
|
26518
|
+
if (isAbsolute13(relPath)) return null;
|
|
25838
26519
|
const absRoot = resolvePath2(cwd);
|
|
25839
26520
|
const absFile = resolvePath2(absRoot, relPath);
|
|
25840
26521
|
const rel = relativePath(absRoot, absFile);
|
|
25841
|
-
if (rel.startsWith("..") || rel.startsWith(
|
|
26522
|
+
if (rel.startsWith("..") || rel.startsWith(sep8) || rel.length === 0) {
|
|
25842
26523
|
return null;
|
|
25843
26524
|
}
|
|
25844
26525
|
let raw;
|
|
@@ -25938,7 +26619,7 @@ function registerNodesRoutes(app, deps) {
|
|
|
25938
26619
|
nodePath = decodeNodePath(pathB64);
|
|
25939
26620
|
} catch (err) {
|
|
25940
26621
|
if (err instanceof PathCodecError) {
|
|
25941
|
-
throw new
|
|
26622
|
+
throw new HTTPException8(404, { message: SERVER_TEXTS.pathB64Malformed });
|
|
25942
26623
|
}
|
|
25943
26624
|
throw err;
|
|
25944
26625
|
}
|
|
@@ -25970,7 +26651,7 @@ function registerNodesRoutes(app, deps) {
|
|
|
25970
26651
|
const contributions = result?.contributions ?? [];
|
|
25971
26652
|
const tags = result?.tags ?? [];
|
|
25972
26653
|
if (!bundle) {
|
|
25973
|
-
throw new
|
|
26654
|
+
throw new HTTPException8(404, {
|
|
25974
26655
|
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
25975
26656
|
});
|
|
25976
26657
|
}
|
|
@@ -26022,7 +26703,7 @@ function registerNodesRoutes(app, deps) {
|
|
|
26022
26703
|
const contribByPath = contributionsOmitted ? /* @__PURE__ */ new Map() : await groupContributionsByPath(
|
|
26023
26704
|
await adapter.contributions.listForPaths(pagePaths)
|
|
26024
26705
|
);
|
|
26025
|
-
const tagByPath =
|
|
26706
|
+
const tagByPath = groupTagsByPath2(
|
|
26026
26707
|
await adapter.tags.listForPaths(pagePaths)
|
|
26027
26708
|
);
|
|
26028
26709
|
return { contributionsByPath: contribByPath, tagsByPath: tagByPath };
|
|
@@ -26058,7 +26739,7 @@ function registerNodesRoutes(app, deps) {
|
|
|
26058
26739
|
function parseIncludes(raw) {
|
|
26059
26740
|
return new Set(parseCsv(raw));
|
|
26060
26741
|
}
|
|
26061
|
-
function
|
|
26742
|
+
function groupTagsByPath2(rows) {
|
|
26062
26743
|
const buckets = /* @__PURE__ */ new Map();
|
|
26063
26744
|
for (const r of rows) {
|
|
26064
26745
|
const set = buckets.get(r.nodePath);
|
|
@@ -26080,11 +26761,11 @@ async function groupContributionsByPath(rows) {
|
|
|
26080
26761
|
}
|
|
26081
26762
|
|
|
26082
26763
|
// server/routes/plugins.ts
|
|
26083
|
-
import { HTTPException as
|
|
26764
|
+
import { HTTPException as HTTPException10 } from "hono/http-exception";
|
|
26084
26765
|
|
|
26085
26766
|
// server/util/parse-body.ts
|
|
26086
26767
|
import { Ajv2020 as Ajv20207 } from "ajv/dist/2020.js";
|
|
26087
|
-
import { HTTPException as
|
|
26768
|
+
import { HTTPException as HTTPException9 } from "hono/http-exception";
|
|
26088
26769
|
function makeBodyValidator(schema, messages) {
|
|
26089
26770
|
const ajv = new Ajv20207({ strict: false, allErrors: false });
|
|
26090
26771
|
const validate = ajv.compile(schema);
|
|
@@ -26093,16 +26774,16 @@ function makeBodyValidator(schema, messages) {
|
|
|
26093
26774
|
try {
|
|
26094
26775
|
raw = await req.json();
|
|
26095
26776
|
} catch {
|
|
26096
|
-
throw new
|
|
26777
|
+
throw new HTTPException9(400, { message: messages.notJson });
|
|
26097
26778
|
}
|
|
26098
26779
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
26099
|
-
throw new
|
|
26780
|
+
throw new HTTPException9(400, { message: messages.notObject });
|
|
26100
26781
|
}
|
|
26101
26782
|
if (validate(raw)) {
|
|
26102
26783
|
return raw;
|
|
26103
26784
|
}
|
|
26104
26785
|
const message = resolveErrorMessage(validate.errors, messages);
|
|
26105
|
-
throw new
|
|
26786
|
+
throw new HTTPException9(400, { message });
|
|
26106
26787
|
};
|
|
26107
26788
|
}
|
|
26108
26789
|
function resolveErrorMessage(errors, messages) {
|
|
@@ -26285,18 +26966,18 @@ function registerPluginsRoute(app, deps) {
|
|
|
26285
26966
|
app.patch("/api/plugins/:id", async (c) => {
|
|
26286
26967
|
const id = c.req.param("id");
|
|
26287
26968
|
if (id.includes("/")) {
|
|
26288
|
-
throw new
|
|
26969
|
+
throw new HTTPException10(400, {
|
|
26289
26970
|
message: tx(SERVER_TEXTS.pluginsCascadeRouteQualifiedRejected, { id })
|
|
26290
26971
|
});
|
|
26291
26972
|
}
|
|
26292
26973
|
const handle = findHandle(id, deps);
|
|
26293
26974
|
if (!handle) {
|
|
26294
|
-
throw new
|
|
26975
|
+
throw new HTTPException10(404, {
|
|
26295
26976
|
message: tx(SERVER_TEXTS.pluginsUnknown, { id })
|
|
26296
26977
|
});
|
|
26297
26978
|
}
|
|
26298
26979
|
if (isPluginLocked(id)) {
|
|
26299
|
-
throw new
|
|
26980
|
+
throw new HTTPException10(403, {
|
|
26300
26981
|
message: tx(SERVER_TEXTS.pluginsLocked, { id })
|
|
26301
26982
|
});
|
|
26302
26983
|
}
|
|
@@ -26310,18 +26991,18 @@ function registerPluginsRoute(app, deps) {
|
|
|
26310
26991
|
const extensionId = c.req.param("extensionId");
|
|
26311
26992
|
const handle = findHandle(pluginId, deps);
|
|
26312
26993
|
if (!handle) {
|
|
26313
|
-
throw new
|
|
26994
|
+
throw new HTTPException10(404, {
|
|
26314
26995
|
message: tx(SERVER_TEXTS.pluginsUnknown, { id: pluginId })
|
|
26315
26996
|
});
|
|
26316
26997
|
}
|
|
26317
26998
|
if (!hasExtension(handle, extensionId)) {
|
|
26318
|
-
throw new
|
|
26999
|
+
throw new HTTPException10(404, {
|
|
26319
27000
|
message: tx(SERVER_TEXTS.pluginsExtensionUnknown, { pluginId, extensionId })
|
|
26320
27001
|
});
|
|
26321
27002
|
}
|
|
26322
27003
|
const qualified = qualifiedExtensionId(pluginId, extensionId);
|
|
26323
27004
|
if (isPluginLocked(qualified) || isPluginLocked(pluginId)) {
|
|
26324
|
-
throw new
|
|
27005
|
+
throw new HTTPException10(403, {
|
|
26325
27006
|
message: tx(SERVER_TEXTS.pluginsExtensionLocked, { pluginId, extensionId })
|
|
26326
27007
|
});
|
|
26327
27008
|
}
|
|
@@ -26679,7 +27360,7 @@ function persistBulkSettings(deps, changes) {
|
|
|
26679
27360
|
try {
|
|
26680
27361
|
persistSettingsPatch(pluginId, extensionId, declarations, change.settings, cwd);
|
|
26681
27362
|
} catch (err) {
|
|
26682
|
-
throw new
|
|
27363
|
+
throw new HTTPException10(500, {
|
|
26683
27364
|
message: tx(SERVER_TEXTS.pluginsSettingsPersistFailed, {
|
|
26684
27365
|
id: change.id,
|
|
26685
27366
|
message: err instanceof Error ? err.message : String(err)
|
|
@@ -26727,7 +27408,7 @@ function hasExtension(handle, extensionId) {
|
|
|
26727
27408
|
}
|
|
26728
27409
|
|
|
26729
27410
|
// server/routes/preferences.ts
|
|
26730
|
-
import { HTTPException as
|
|
27411
|
+
import { HTTPException as HTTPException11 } from "hono/http-exception";
|
|
26731
27412
|
function registerPreferencesRoute(app, _deps) {
|
|
26732
27413
|
app.get("/api/preferences", (c) => {
|
|
26733
27414
|
return c.json(buildEnvelope());
|
|
@@ -26759,7 +27440,7 @@ function applyPatch(body) {
|
|
|
26759
27440
|
applyTelemetryPatch(body.telemetry);
|
|
26760
27441
|
}
|
|
26761
27442
|
} catch (err) {
|
|
26762
|
-
throw new
|
|
27443
|
+
throw new HTTPException11(400, {
|
|
26763
27444
|
message: tx(SERVER_TEXTS.preferencesPersistFailed, {
|
|
26764
27445
|
message: formatErrorMessage(err)
|
|
26765
27446
|
})
|
|
@@ -26818,14 +27499,14 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
|
|
|
26818
27499
|
});
|
|
26819
27500
|
|
|
26820
27501
|
// server/routes/project-ignore.ts
|
|
26821
|
-
import { HTTPException as
|
|
27502
|
+
import { HTTPException as HTTPException12 } from "hono/http-exception";
|
|
26822
27503
|
|
|
26823
27504
|
// server/util/skillmapignore-io.ts
|
|
26824
27505
|
import { existsSync as existsSync27, readFileSync as readFileSync18, writeFileSync as writeFileSync2 } from "fs";
|
|
26825
|
-
import { resolve as
|
|
27506
|
+
import { resolve as resolve38 } from "path";
|
|
26826
27507
|
var IGNORE_FILENAME2 = ".skillmapignore";
|
|
26827
27508
|
function readPatterns(cwd) {
|
|
26828
|
-
const path =
|
|
27509
|
+
const path = resolve38(cwd, IGNORE_FILENAME2);
|
|
26829
27510
|
if (!existsSync27(path)) return [];
|
|
26830
27511
|
let raw;
|
|
26831
27512
|
try {
|
|
@@ -26836,7 +27517,7 @@ function readPatterns(cwd) {
|
|
|
26836
27517
|
return raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
26837
27518
|
}
|
|
26838
27519
|
function writePatterns(cwd, nextPatterns) {
|
|
26839
|
-
const path =
|
|
27520
|
+
const path = resolve38(cwd, IGNORE_FILENAME2);
|
|
26840
27521
|
const prior = existsSync27(path) ? safeRead(path) : "";
|
|
26841
27522
|
const content = buildContent(prior, nextPatterns);
|
|
26842
27523
|
writeFileSync2(path, content, "utf8");
|
|
@@ -26905,12 +27586,12 @@ async function applyPatch2(deps, body) {
|
|
|
26905
27586
|
for (const raw of body.patterns) {
|
|
26906
27587
|
const t = raw.trim();
|
|
26907
27588
|
if (t.length === 0) {
|
|
26908
|
-
throw new
|
|
27589
|
+
throw new HTTPException12(400, {
|
|
26909
27590
|
message: SERVER_TEXTS.projectIgnorePatternEmpty
|
|
26910
27591
|
});
|
|
26911
27592
|
}
|
|
26912
27593
|
if (seen.has(t)) {
|
|
26913
|
-
throw new
|
|
27594
|
+
throw new HTTPException12(400, {
|
|
26914
27595
|
message: tx(SERVER_TEXTS.projectIgnorePatternDuplicate, { pattern: t })
|
|
26915
27596
|
});
|
|
26916
27597
|
}
|
|
@@ -26921,7 +27602,7 @@ async function applyPatch2(deps, body) {
|
|
|
26921
27602
|
try {
|
|
26922
27603
|
writePatterns(cwd, trimmed);
|
|
26923
27604
|
} catch (err) {
|
|
26924
|
-
throw new
|
|
27605
|
+
throw new HTTPException12(400, {
|
|
26925
27606
|
message: tx(SERVER_TEXTS.projectIgnorePersistFailed, {
|
|
26926
27607
|
message: formatErrorMessage(err)
|
|
26927
27608
|
})
|
|
@@ -26999,7 +27680,7 @@ var parsePatchBody3 = makeBodyValidator(PATCH_BODY_SCHEMA2, {
|
|
|
26999
27680
|
|
|
27000
27681
|
// server/routes/project-preferences.ts
|
|
27001
27682
|
import { statSync as statSync9 } from "fs";
|
|
27002
|
-
import { HTTPException as
|
|
27683
|
+
import { HTTPException as HTTPException13 } from "hono/http-exception";
|
|
27003
27684
|
function registerProjectPreferencesRoute(app, deps) {
|
|
27004
27685
|
app.get("/api/project-preferences", (c) => {
|
|
27005
27686
|
return c.json(buildEnvelope3(deps));
|
|
@@ -27037,7 +27718,7 @@ function applyScanWrites(body, cwd) {
|
|
|
27037
27718
|
if (writes.length === 0) return { attempted: false, mutated: false };
|
|
27038
27719
|
const missingPaths = collectMissingPaths(writes, cwd);
|
|
27039
27720
|
if (missingPaths.length > 0) {
|
|
27040
|
-
throw new
|
|
27721
|
+
throw new HTTPException13(400, {
|
|
27041
27722
|
message: tx(SERVER_TEXTS.projectPrefsPathNotFound, {
|
|
27042
27723
|
paths: missingPaths.join(", ")
|
|
27043
27724
|
})
|
|
@@ -27046,7 +27727,7 @@ function applyScanWrites(body, cwd) {
|
|
|
27046
27727
|
const exposures = writes.map((w) => projectPathExposure({ key: w.key, value: w.value, cwd })).filter((e) => e.expandsSurface);
|
|
27047
27728
|
if (exposures.length > 0 && body.confirm !== true) {
|
|
27048
27729
|
const exposed = exposures.flatMap((e) => e.exposedPaths);
|
|
27049
|
-
throw new
|
|
27730
|
+
throw new HTTPException13(412, {
|
|
27050
27731
|
message: tx(SERVER_TEXTS.projectPrefsConfirmRequired, {
|
|
27051
27732
|
paths: exposed.join(", ")
|
|
27052
27733
|
})
|
|
@@ -27064,7 +27745,7 @@ function writeSidecarWritersPolicy(value, cwd) {
|
|
|
27064
27745
|
try {
|
|
27065
27746
|
writeConfigValue("allowSidecarWriters", value, { target: "project", cwd });
|
|
27066
27747
|
} catch (err) {
|
|
27067
|
-
throw new
|
|
27748
|
+
throw new HTTPException13(400, {
|
|
27068
27749
|
message: tx(SERVER_TEXTS.projectPrefsPersistFailed, {
|
|
27069
27750
|
key: "allowSidecarWriters",
|
|
27070
27751
|
message: formatErrorMessage(err)
|
|
@@ -27115,7 +27796,7 @@ function runWrite(w, cwd) {
|
|
|
27115
27796
|
try {
|
|
27116
27797
|
writeConfigValue(w.key, w.value, { target: "project-local", cwd });
|
|
27117
27798
|
} catch (err) {
|
|
27118
|
-
throw new
|
|
27799
|
+
throw new HTTPException13(400, {
|
|
27119
27800
|
message: tx(SERVER_TEXTS.projectPrefsPersistFailed, {
|
|
27120
27801
|
key: w.key,
|
|
27121
27802
|
message: formatErrorMessage(err)
|
|
@@ -27217,7 +27898,7 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
|
|
|
27217
27898
|
|
|
27218
27899
|
// server/routes/active-provider.ts
|
|
27219
27900
|
import { existsSync as existsSync28 } from "fs";
|
|
27220
|
-
import { HTTPException as
|
|
27901
|
+
import { HTTPException as HTTPException14 } from "hono/http-exception";
|
|
27221
27902
|
function registerActiveProviderRoute(app, deps) {
|
|
27222
27903
|
app.get("/api/active-provider", async (c) => {
|
|
27223
27904
|
return c.json(await buildEnvelope4(deps));
|
|
@@ -27258,7 +27939,7 @@ function applyLensSwitch(deps, newValue) {
|
|
|
27258
27939
|
try {
|
|
27259
27940
|
writeConfigValue("activeProvider", newValue, { target: "project", cwd });
|
|
27260
27941
|
} catch (err) {
|
|
27261
|
-
throw new
|
|
27942
|
+
throw new HTTPException14(400, {
|
|
27262
27943
|
message: tx(SERVER_TEXTS.activeProviderPersistFailed, {
|
|
27263
27944
|
message: formatErrorMessage(err)
|
|
27264
27945
|
})
|
|
@@ -27294,11 +27975,11 @@ var parsePatchBody5 = makeBodyValidator(PATCH_BODY_SCHEMA4, {
|
|
|
27294
27975
|
});
|
|
27295
27976
|
|
|
27296
27977
|
// server/routes/actions.ts
|
|
27297
|
-
import { HTTPException as
|
|
27298
|
-
import { resolve as
|
|
27978
|
+
import { HTTPException as HTTPException16 } from "hono/http-exception";
|
|
27979
|
+
import { resolve as resolve39 } from "path";
|
|
27299
27980
|
|
|
27300
27981
|
// server/routes/node-loader.ts
|
|
27301
|
-
import { HTTPException as
|
|
27982
|
+
import { HTTPException as HTTPException15 } from "hono/http-exception";
|
|
27302
27983
|
async function loadNode(deps, nodePath) {
|
|
27303
27984
|
const persisted = await tryWithSqlite(
|
|
27304
27985
|
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
@@ -27306,7 +27987,7 @@ async function loadNode(deps, nodePath) {
|
|
|
27306
27987
|
);
|
|
27307
27988
|
const node = persisted?.nodes.find((n) => n.path === nodePath);
|
|
27308
27989
|
if (!node) {
|
|
27309
|
-
throw new
|
|
27990
|
+
throw new HTTPException15(404, {
|
|
27310
27991
|
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
27311
27992
|
});
|
|
27312
27993
|
}
|
|
@@ -27355,9 +28036,9 @@ function registerActionsRoutes(app, deps) {
|
|
|
27355
28036
|
let absPath;
|
|
27356
28037
|
try {
|
|
27357
28038
|
assertContained(deps.runtimeContext.cwd, node.path);
|
|
27358
|
-
absPath =
|
|
28039
|
+
absPath = resolve39(deps.runtimeContext.cwd, node.path);
|
|
27359
28040
|
} catch (err) {
|
|
27360
|
-
throw new
|
|
28041
|
+
throw new HTTPException16(400, { message: formatErrorMessage(err) });
|
|
27361
28042
|
}
|
|
27362
28043
|
const result = invokeAction(action, absPath, node, body, deps.runtimeContext.cwd);
|
|
27363
28044
|
const report = result.report;
|
|
@@ -27397,7 +28078,7 @@ function registerActionsRoutes(app, deps) {
|
|
|
27397
28078
|
}
|
|
27398
28079
|
function parseSegment(value, name) {
|
|
27399
28080
|
if (!QUALIFIED_ID_SEGMENT2.test(value)) {
|
|
27400
|
-
throw new
|
|
28081
|
+
throw new HTTPException16(400, {
|
|
27401
28082
|
message: tx(SERVER_TEXTS.qualifiedIdMalformed, {
|
|
27402
28083
|
name,
|
|
27403
28084
|
value: sanitizeForTerminal(value)
|
|
@@ -27410,7 +28091,7 @@ function resolveInvokableAction(kernel, actionId) {
|
|
|
27410
28091
|
const ext = kernel.registry.get("action", actionId);
|
|
27411
28092
|
const action = ext;
|
|
27412
28093
|
if (!action || typeof action.invoke !== "function") {
|
|
27413
|
-
throw new
|
|
28094
|
+
throw new HTTPException16(404, {
|
|
27414
28095
|
message: tx(SERVER_TEXTS.actionUnknown, { actionId: sanitizeForTerminal(actionId) })
|
|
27415
28096
|
});
|
|
27416
28097
|
}
|
|
@@ -27442,7 +28123,7 @@ async function materializeWrites(writes, body, cwd) {
|
|
|
27442
28123
|
} catch (err) {
|
|
27443
28124
|
if (err instanceof EConsentRequiredError) throw err;
|
|
27444
28125
|
if (err instanceof ESidecarWritersForbiddenError) throw err;
|
|
27445
|
-
throw new
|
|
28126
|
+
throw new HTTPException16(500, { message: formatErrorMessage(err) });
|
|
27446
28127
|
}
|
|
27447
28128
|
}
|
|
27448
28129
|
function buildEnvelope5(actionId, nodePath, report, startedAt) {
|
|
@@ -27455,7 +28136,7 @@ function buildEnvelope5(actionId, nodePath, report, startedAt) {
|
|
|
27455
28136
|
}
|
|
27456
28137
|
|
|
27457
28138
|
// server/routes/scan.ts
|
|
27458
|
-
import { HTTPException as
|
|
28139
|
+
import { HTTPException as HTTPException17 } from "hono/http-exception";
|
|
27459
28140
|
|
|
27460
28141
|
// server/scan-mutex.ts
|
|
27461
28142
|
var inFlight = null;
|
|
@@ -27463,14 +28144,14 @@ async function withScanMutex(fn) {
|
|
|
27463
28144
|
if (inFlight !== null) {
|
|
27464
28145
|
throw new ScanBusyError();
|
|
27465
28146
|
}
|
|
27466
|
-
let
|
|
28147
|
+
let resolve43;
|
|
27467
28148
|
inFlight = new Promise((r) => {
|
|
27468
|
-
|
|
28149
|
+
resolve43 = r;
|
|
27469
28150
|
});
|
|
27470
28151
|
try {
|
|
27471
28152
|
return await fn();
|
|
27472
28153
|
} finally {
|
|
27473
|
-
|
|
28154
|
+
resolve43();
|
|
27474
28155
|
inFlight = null;
|
|
27475
28156
|
}
|
|
27476
28157
|
}
|
|
@@ -27491,6 +28172,99 @@ function noopWritable() {
|
|
|
27491
28172
|
});
|
|
27492
28173
|
}
|
|
27493
28174
|
|
|
28175
|
+
// core/runtime/i18n/scan-spinner.texts.ts
|
|
28176
|
+
var SCAN_SPINNER_TEXTS = {
|
|
28177
|
+
/**
|
|
28178
|
+
* Animated (TTY) / single-line (non-TTY) label shown while a batch is
|
|
28179
|
+
* in flight. No em dash, the trailing ellipsis is three ASCII dots.
|
|
28180
|
+
*/
|
|
28181
|
+
scanning: "Scanning...",
|
|
28182
|
+
/**
|
|
28183
|
+
* Headline word for the completion line, framed by the caller as
|
|
28184
|
+
* `{{glyph}} {{updated}}` (+ optional ` · {{stats}}` segment).
|
|
28185
|
+
*/
|
|
28186
|
+
updated: "Map updated",
|
|
28187
|
+
/**
|
|
28188
|
+
* Optional stats segment appended after the headline when the batch
|
|
28189
|
+
* outcome carried `nodesCount` / `durationMs`. The caller composes
|
|
28190
|
+
* whichever of `{{nodes}}` / `{{durationMs}}` were defined and joins
|
|
28191
|
+
* them with the middle-dot separator before passing the finished line.
|
|
28192
|
+
*/
|
|
28193
|
+
nodesSegment: "{{nodes}} nodes",
|
|
28194
|
+
durationSegment: "{{durationMs}}ms"
|
|
28195
|
+
};
|
|
28196
|
+
|
|
28197
|
+
// core/runtime/scan-spinner.ts
|
|
28198
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
28199
|
+
var FRAME_INTERVAL_MS = 80;
|
|
28200
|
+
var CLEAR_LINE = "\r\x1B[2K";
|
|
28201
|
+
var ESC_CYAN = "\x1B[36m";
|
|
28202
|
+
var ESC_GREEN = "\x1B[32m";
|
|
28203
|
+
var ESC_RESET2 = "\x1B[0m";
|
|
28204
|
+
function createScanSpinner(stream, opts = {}) {
|
|
28205
|
+
const colorEnabled = opts.colorEnabled === true;
|
|
28206
|
+
const label = opts.label ?? SCAN_SPINNER_TEXTS.scanning;
|
|
28207
|
+
const isTty = stream.isTTY === true;
|
|
28208
|
+
let active2 = false;
|
|
28209
|
+
let frameIndex = 0;
|
|
28210
|
+
let timer = null;
|
|
28211
|
+
const safeWrite = (chunk) => {
|
|
28212
|
+
try {
|
|
28213
|
+
stream.write(chunk);
|
|
28214
|
+
} catch {
|
|
28215
|
+
}
|
|
28216
|
+
};
|
|
28217
|
+
const tintFrame = (frame) => colorEnabled ? `${ESC_CYAN}${frame}${ESC_RESET2}` : frame;
|
|
28218
|
+
const drawFrame = () => {
|
|
28219
|
+
const frame = FRAMES[frameIndex % FRAMES.length];
|
|
28220
|
+
frameIndex += 1;
|
|
28221
|
+
safeWrite(`${CLEAR_LINE}${tintFrame(frame)} ${label}`);
|
|
28222
|
+
};
|
|
28223
|
+
const start = () => {
|
|
28224
|
+
if (active2) return;
|
|
28225
|
+
active2 = true;
|
|
28226
|
+
if (!isTty) {
|
|
28227
|
+
safeWrite(`${label}
|
|
28228
|
+
`);
|
|
28229
|
+
return;
|
|
28230
|
+
}
|
|
28231
|
+
frameIndex = 0;
|
|
28232
|
+
drawFrame();
|
|
28233
|
+
timer = setInterval(drawFrame, FRAME_INTERVAL_MS);
|
|
28234
|
+
timer.unref?.();
|
|
28235
|
+
};
|
|
28236
|
+
const buildStatsSegment = (stats) => {
|
|
28237
|
+
const segments = [];
|
|
28238
|
+
if (stats.nodesCount !== void 0) {
|
|
28239
|
+
segments.push(tx(SCAN_SPINNER_TEXTS.nodesSegment, { nodes: stats.nodesCount }));
|
|
28240
|
+
}
|
|
28241
|
+
if (stats.durationMs !== void 0) {
|
|
28242
|
+
segments.push(tx(SCAN_SPINNER_TEXTS.durationSegment, { durationMs: stats.durationMs }));
|
|
28243
|
+
}
|
|
28244
|
+
return segments.length > 0 ? ` \xB7 ${segments.join(" \xB7 ")}` : "";
|
|
28245
|
+
};
|
|
28246
|
+
const stop = (stats) => {
|
|
28247
|
+
if (!active2) return;
|
|
28248
|
+
active2 = false;
|
|
28249
|
+
if (timer !== null) {
|
|
28250
|
+
clearInterval(timer);
|
|
28251
|
+
timer = null;
|
|
28252
|
+
}
|
|
28253
|
+
if (isTty) safeWrite(CLEAR_LINE);
|
|
28254
|
+
const check = colorEnabled ? `${ESC_GREEN}\u2713${ESC_RESET2}` : "\u2713";
|
|
28255
|
+
const statsSegment = stats ? buildStatsSegment(stats) : "";
|
|
28256
|
+
safeWrite(`${check} ${SCAN_SPINNER_TEXTS.updated}${statsSegment}
|
|
28257
|
+
`);
|
|
28258
|
+
};
|
|
28259
|
+
return {
|
|
28260
|
+
start,
|
|
28261
|
+
stop,
|
|
28262
|
+
get active() {
|
|
28263
|
+
return active2;
|
|
28264
|
+
}
|
|
28265
|
+
};
|
|
28266
|
+
}
|
|
28267
|
+
|
|
27494
28268
|
// server/events.ts
|
|
27495
28269
|
function buildWatcherStartedEvent(data) {
|
|
27496
28270
|
return {
|
|
@@ -27512,6 +28286,9 @@ function buildWatcherErrorEvent(data) {
|
|
|
27512
28286
|
// server/watcher.ts
|
|
27513
28287
|
function createWatcherService(opts) {
|
|
27514
28288
|
let currentRuntime = null;
|
|
28289
|
+
const spinner = opts.scanProgress ? createScanSpinner(opts.scanProgress.stream, {
|
|
28290
|
+
colorEnabled: opts.scanProgress.colorEnabled
|
|
28291
|
+
}) : void 0;
|
|
27515
28292
|
const buildRuntimeOpts = () => {
|
|
27516
28293
|
const runtimeOpts = {
|
|
27517
28294
|
dbPath: opts.options.dbPath,
|
|
@@ -27527,7 +28304,16 @@ function createWatcherService(opts) {
|
|
|
27527
28304
|
subscribeBeforeInitial: true,
|
|
27528
28305
|
failOnInitialBatchError: false,
|
|
27529
28306
|
events: {
|
|
28307
|
+
// Light the "scanning" indicator the moment a batch begins (file
|
|
28308
|
+
// save to re-scan). No-op when no `scanProgress` was passed.
|
|
28309
|
+
onBatchStart: () => spinner?.start(),
|
|
27530
28310
|
onBatch: (outcome) => {
|
|
28311
|
+
spinner?.stop(
|
|
28312
|
+
outcome.kind === "ok" ? {
|
|
28313
|
+
nodesCount: outcome.result.stats.nodesCount,
|
|
28314
|
+
durationMs: outcome.result.stats.durationMs
|
|
28315
|
+
} : void 0
|
|
28316
|
+
);
|
|
27531
28317
|
if (outcome.kind === "error") {
|
|
27532
28318
|
log.warn(
|
|
27533
28319
|
tx(SERVER_TEXTS.watcherBatchFailed, {
|
|
@@ -27573,6 +28359,9 @@ function createWatcherService(opts) {
|
|
|
27573
28359
|
if (opts.debounceMsOverride !== void 0) {
|
|
27574
28360
|
runtimeOpts.debounceMsOverride = opts.debounceMsOverride;
|
|
27575
28361
|
}
|
|
28362
|
+
if (opts.options.maxScan !== void 0) {
|
|
28363
|
+
runtimeOpts.maxScanOverride = opts.options.maxScan;
|
|
28364
|
+
}
|
|
27576
28365
|
if (opts.options.maxNodes !== void 0) {
|
|
27577
28366
|
runtimeOpts.maxNodesOverride = opts.options.maxNodes;
|
|
27578
28367
|
}
|
|
@@ -27628,13 +28417,16 @@ function registerScanRoute(app, deps) {
|
|
|
27628
28417
|
if (parseBooleanFlag(c.req.query("fresh"))) {
|
|
27629
28418
|
return c.json(await runFreshScan(deps));
|
|
27630
28419
|
}
|
|
28420
|
+
if (parseBooleanFlag(c.req.query("meta"))) {
|
|
28421
|
+
return c.json(await loadPersistedScanMeta(deps));
|
|
28422
|
+
}
|
|
27631
28423
|
return c.json(await loadPersistedScan(deps));
|
|
27632
28424
|
});
|
|
27633
28425
|
app.post("/api/scan", async (c) => runPersistedScan(c, deps));
|
|
27634
28426
|
}
|
|
27635
28427
|
async function runPersistedScan(c, deps) {
|
|
27636
28428
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
27637
|
-
throw new
|
|
28429
|
+
throw new HTTPException17(400, { message: SERVER_TEXTS.scanPostRequiresFullPipeline });
|
|
27638
28430
|
}
|
|
27639
28431
|
const dbExists = await tryWithSqlite(
|
|
27640
28432
|
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
@@ -27665,13 +28457,15 @@ async function runPersistedScan(c, deps) {
|
|
|
27665
28457
|
// the operator via the Settings UI (PATCH /api/active-provider)
|
|
27666
28458
|
// before the scan, not via interactive prompt here.
|
|
27667
28459
|
yes: true,
|
|
27668
|
-
// `--max-
|
|
27669
|
-
// `sm
|
|
27670
|
-
//
|
|
28460
|
+
// `--max-scan` (walk ceiling) and `--max-nodes` (render cap)
|
|
28461
|
+
// from the `sm serve` invocation (or the bare `sm --max-scan
|
|
28462
|
+
// <N>` / `sm --max-nodes <N>` shortcut) flow through to every
|
|
28463
|
+
// scan the BFF runs so both overrides are honoured end-to-end.
|
|
28464
|
+
...deps.options.maxScan !== void 0 ? { maxScan: deps.options.maxScan } : {},
|
|
27671
28465
|
...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
|
|
27672
28466
|
});
|
|
27673
28467
|
if (outcome.kind !== "ok") {
|
|
27674
|
-
throw new
|
|
28468
|
+
throw new HTTPException17(500, {
|
|
27675
28469
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.scanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
27676
28470
|
});
|
|
27677
28471
|
}
|
|
@@ -27691,6 +28485,20 @@ async function buildBffResolverOverride(deps) {
|
|
|
27691
28485
|
fallbackResolver: deps.pluginRuntime.resolveEnabled
|
|
27692
28486
|
});
|
|
27693
28487
|
}
|
|
28488
|
+
async function loadPersistedScanMeta(deps) {
|
|
28489
|
+
const opened = await tryWithSqlite(
|
|
28490
|
+
{
|
|
28491
|
+
databasePath: deps.options.dbPath,
|
|
28492
|
+
autoBackup: false,
|
|
28493
|
+
versionCheck: { currentVersion: VERSION, printer: bffVersionCheckPrinter }
|
|
28494
|
+
},
|
|
28495
|
+
async (adapter) => adapter.scans.loadMeta()
|
|
28496
|
+
);
|
|
28497
|
+
if (opened === null) {
|
|
28498
|
+
return emptyScanResult();
|
|
28499
|
+
}
|
|
28500
|
+
return opened;
|
|
28501
|
+
}
|
|
27694
28502
|
async function loadPersistedScan(deps) {
|
|
27695
28503
|
const opened = await tryWithSqlite(
|
|
27696
28504
|
{
|
|
@@ -27719,7 +28527,7 @@ async function loadPersistedScan(deps) {
|
|
|
27719
28527
|
if (list) list.push(r);
|
|
27720
28528
|
else byPath3.set(r.nodePath, [r]);
|
|
27721
28529
|
}
|
|
27722
|
-
const tagsByPath =
|
|
28530
|
+
const tagsByPath = groupTagsByPath3(tagRows);
|
|
27723
28531
|
return { loaded, favSet, contribByPath: byPath3, tagsByPath };
|
|
27724
28532
|
}
|
|
27725
28533
|
);
|
|
@@ -27736,7 +28544,7 @@ async function loadPersistedScan(deps) {
|
|
|
27736
28544
|
}))
|
|
27737
28545
|
};
|
|
27738
28546
|
}
|
|
27739
|
-
function
|
|
28547
|
+
function groupTagsByPath3(rows) {
|
|
27740
28548
|
const buckets = /* @__PURE__ */ new Map();
|
|
27741
28549
|
for (const r of rows) {
|
|
27742
28550
|
const set = buckets.get(r.nodePath);
|
|
@@ -27749,7 +28557,7 @@ function groupTagsByPath2(rows) {
|
|
|
27749
28557
|
}
|
|
27750
28558
|
async function runFreshScan(deps) {
|
|
27751
28559
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
27752
|
-
throw new
|
|
28560
|
+
throw new HTTPException17(400, { message: SERVER_TEXTS.freshScanRequiresPipeline });
|
|
27753
28561
|
}
|
|
27754
28562
|
const resolveEnabledOverride = await buildBffResolverOverride(deps);
|
|
27755
28563
|
const outcome = await runScanForCommand({
|
|
@@ -27779,12 +28587,14 @@ async function runFreshScan(deps) {
|
|
|
27779
28587
|
// BFF has no TTY; ambiguous activeProvider is the operator's
|
|
27780
28588
|
// problem to resolve via the Settings UI, not via prompt here.
|
|
27781
28589
|
yes: true,
|
|
27782
|
-
// Carry `--max-
|
|
27783
|
-
//
|
|
28590
|
+
// Carry `--max-scan` (walk ceiling) and `--max-nodes` (render cap)
|
|
28591
|
+
// from `sm serve` into the fresh-scan path too so a UI-driven
|
|
28592
|
+
// refresh honours the same knobs as the watcher.
|
|
28593
|
+
...deps.options.maxScan !== void 0 ? { maxScan: deps.options.maxScan } : {},
|
|
27784
28594
|
...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
|
|
27785
28595
|
});
|
|
27786
28596
|
if (outcome.kind !== "ok") {
|
|
27787
|
-
throw new
|
|
28597
|
+
throw new HTTPException17(500, {
|
|
27788
28598
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.freshScanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
27789
28599
|
});
|
|
27790
28600
|
}
|
|
@@ -27810,12 +28620,14 @@ function emptyScanResult() {
|
|
|
27810
28620
|
scannedAt: Date.now(),
|
|
27811
28621
|
roots: ["."],
|
|
27812
28622
|
providers: [],
|
|
27813
|
-
// Surface the design
|
|
27814
|
-
// on cold boot as on populated DBs.
|
|
27815
|
-
//
|
|
27816
|
-
//
|
|
27817
|
-
|
|
27818
|
-
|
|
28623
|
+
// Surface the design defaults so the SPA reads the same field shape
|
|
28624
|
+
// on cold boot as on populated DBs. 50000 mirrors `scan.maxScan`
|
|
28625
|
+
// (the walk ceiling) and 256 mirrors `scan.maxNodes` (the render
|
|
28626
|
+
// cap), both from `src/config/defaults.json`. A real scan
|
|
28627
|
+
// overwrites these with the live values on next run.
|
|
28628
|
+
scanCeiling: 5e4,
|
|
28629
|
+
scanTruncated: false,
|
|
28630
|
+
maxRenderNodes: 256,
|
|
27819
28631
|
nodes: [],
|
|
27820
28632
|
links: [],
|
|
27821
28633
|
issues: [],
|
|
@@ -27996,13 +28808,13 @@ function attachBroadcasterRoute(app, broadcaster) {
|
|
|
27996
28808
|
|
|
27997
28809
|
// server/app.ts
|
|
27998
28810
|
var BODY_LIMIT_BYTES = 1024 * 1024;
|
|
27999
|
-
var DbMissingError = class extends
|
|
28811
|
+
var DbMissingError = class extends HTTPException18 {
|
|
28000
28812
|
constructor(message) {
|
|
28001
28813
|
super(500, { message });
|
|
28002
28814
|
this.name = "DbMissingError";
|
|
28003
28815
|
}
|
|
28004
28816
|
};
|
|
28005
|
-
var BulkValidationError = class extends
|
|
28817
|
+
var BulkValidationError = class extends HTTPException18 {
|
|
28006
28818
|
id;
|
|
28007
28819
|
code;
|
|
28008
28820
|
constructor(init) {
|
|
@@ -28012,7 +28824,7 @@ var BulkValidationError = class extends HTTPException17 {
|
|
|
28012
28824
|
this.code = init.code;
|
|
28013
28825
|
}
|
|
28014
28826
|
};
|
|
28015
|
-
var LoopbackGateError = class extends
|
|
28827
|
+
var LoopbackGateError = class extends HTTPException18 {
|
|
28016
28828
|
code;
|
|
28017
28829
|
constructor(init) {
|
|
28018
28830
|
super(403, { message: init.message });
|
|
@@ -28020,7 +28832,7 @@ var LoopbackGateError = class extends HTTPException17 {
|
|
|
28020
28832
|
this.code = init.code;
|
|
28021
28833
|
}
|
|
28022
28834
|
};
|
|
28023
|
-
var ConflictError = class extends
|
|
28835
|
+
var ConflictError = class extends HTTPException18 {
|
|
28024
28836
|
code;
|
|
28025
28837
|
constructor(init) {
|
|
28026
28838
|
super(409, { message: init.message });
|
|
@@ -28028,7 +28840,7 @@ var ConflictError = class extends HTTPException17 {
|
|
|
28028
28840
|
this.code = init.code;
|
|
28029
28841
|
}
|
|
28030
28842
|
};
|
|
28031
|
-
var ActionRefusedError = class extends
|
|
28843
|
+
var ActionRefusedError = class extends HTTPException18 {
|
|
28032
28844
|
/** Refusal code: the report's `reason` when present, else `'action-refused'`. */
|
|
28033
28845
|
code;
|
|
28034
28846
|
details;
|
|
@@ -28056,7 +28868,7 @@ function createApp(deps) {
|
|
|
28056
28868
|
bodyLimit({
|
|
28057
28869
|
maxSize: BODY_LIMIT_BYTES,
|
|
28058
28870
|
onError: () => {
|
|
28059
|
-
throw new
|
|
28871
|
+
throw new HTTPException18(413, { message: tx(SERVER_TEXTS.bodyTooLarge, { maxBytes: String(BODY_LIMIT_BYTES) }) });
|
|
28060
28872
|
}
|
|
28061
28873
|
})
|
|
28062
28874
|
);
|
|
@@ -28089,6 +28901,8 @@ function createApp(deps) {
|
|
|
28089
28901
|
registerNodesRoutes(app, routeDeps);
|
|
28090
28902
|
registerLinksRoute(app, routeDeps);
|
|
28091
28903
|
registerIssuesRoute(app, routeDeps);
|
|
28904
|
+
registerFoldersRoute(app, routeDeps);
|
|
28905
|
+
registerBranchRoute(app, routeDeps);
|
|
28092
28906
|
registerGraphRoute(app, routeDeps);
|
|
28093
28907
|
registerConfigRoute(app, routeDeps);
|
|
28094
28908
|
registerPluginsRoute(app, routeDeps);
|
|
@@ -28102,7 +28916,7 @@ function createApp(deps) {
|
|
|
28102
28916
|
registerActiveProviderRoute(app, routeDeps);
|
|
28103
28917
|
registerProjectIgnoreRoute(app, routeDeps);
|
|
28104
28918
|
app.all("/api/*", (c) => {
|
|
28105
|
-
throw new
|
|
28919
|
+
throw new HTTPException18(404, {
|
|
28106
28920
|
message: tx(SERVER_TEXTS.unknownApiEndpoint, { path: sanitizeForTerminal(c.req.path) })
|
|
28107
28921
|
});
|
|
28108
28922
|
});
|
|
@@ -28110,7 +28924,7 @@ function createApp(deps) {
|
|
|
28110
28924
|
app.use("*", createStaticHandler({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
28111
28925
|
app.get("*", createSpaFallback({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
28112
28926
|
app.notFound((c) => {
|
|
28113
|
-
throw new
|
|
28927
|
+
throw new HTTPException18(404, {
|
|
28114
28928
|
message: tx(SERVER_TEXTS.unknownPath, { path: sanitizeForTerminal(c.req.path) })
|
|
28115
28929
|
});
|
|
28116
28930
|
});
|
|
@@ -28163,7 +28977,7 @@ function formatError2(err, c) {
|
|
|
28163
28977
|
}
|
|
28164
28978
|
const conflict = formatConflict(err, c);
|
|
28165
28979
|
if (conflict) return conflict;
|
|
28166
|
-
if (err instanceof
|
|
28980
|
+
if (err instanceof HTTPException18) {
|
|
28167
28981
|
const status = err.status;
|
|
28168
28982
|
const envelope = {
|
|
28169
28983
|
ok: false,
|
|
@@ -28509,6 +29323,8 @@ function validateServerOptions(input) {
|
|
|
28509
29323
|
if (watcherError !== null) return { ok: false, error: watcherError };
|
|
28510
29324
|
const debounceError = validateWatcherDebounce(input.watcherDebounceMs);
|
|
28511
29325
|
if (debounceError !== null) return { ok: false, error: debounceError };
|
|
29326
|
+
const maxScanError = validateMaxScan(input.maxScan);
|
|
29327
|
+
if (maxScanError !== null) return { ok: false, error: maxScanError };
|
|
28512
29328
|
const maxNodesError = validateMaxNodes(input.maxNodes);
|
|
28513
29329
|
if (maxNodesError !== null) return { ok: false, error: maxNodesError };
|
|
28514
29330
|
const noUiError = validateNoUi(filled.noUi, filled.uiDist);
|
|
@@ -28528,6 +29344,9 @@ function validateServerOptions(input) {
|
|
|
28528
29344
|
if (input.watcherDebounceMs !== void 0) {
|
|
28529
29345
|
options.watcherDebounceMs = input.watcherDebounceMs;
|
|
28530
29346
|
}
|
|
29347
|
+
if (input.maxScan !== void 0) {
|
|
29348
|
+
options.maxScan = input.maxScan;
|
|
29349
|
+
}
|
|
28531
29350
|
if (input.maxNodes !== void 0) {
|
|
28532
29351
|
options.maxNodes = input.maxNodes;
|
|
28533
29352
|
}
|
|
@@ -28596,6 +29415,17 @@ function validateWatcherDebounce(value) {
|
|
|
28596
29415
|
}
|
|
28597
29416
|
return null;
|
|
28598
29417
|
}
|
|
29418
|
+
function validateMaxScan(value) {
|
|
29419
|
+
if (value === void 0) return null;
|
|
29420
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
29421
|
+
return {
|
|
29422
|
+
code: "max-scan-invalid",
|
|
29423
|
+
message: `--max-scan must be an integer >= 1 (got ${value})`,
|
|
29424
|
+
value: String(value)
|
|
29425
|
+
};
|
|
29426
|
+
}
|
|
29427
|
+
return null;
|
|
29428
|
+
}
|
|
28599
29429
|
function validateMaxNodes(value) {
|
|
28600
29430
|
if (value === void 0) return null;
|
|
28601
29431
|
if (!Number.isInteger(value) || value < 1) {
|
|
@@ -28620,7 +29450,7 @@ function validateNoUi(noUi, uiDist) {
|
|
|
28620
29450
|
|
|
28621
29451
|
// server/paths.ts
|
|
28622
29452
|
import { existsSync as existsSync30, statSync as statSync10 } from "fs";
|
|
28623
|
-
import { dirname as dirname19, isAbsolute as
|
|
29453
|
+
import { dirname as dirname19, isAbsolute as isAbsolute14, join as join20, resolve as resolve40 } from "path";
|
|
28624
29454
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
28625
29455
|
var DEFAULT_UI_REL = join20("ui", "dist", "ui", "browser");
|
|
28626
29456
|
var PACKAGE_UI_REL = "ui";
|
|
@@ -28631,7 +29461,7 @@ function resolveDefaultUiDist(ctx) {
|
|
|
28631
29461
|
return walkUpForUi(ctx.cwd);
|
|
28632
29462
|
}
|
|
28633
29463
|
function resolveExplicitUiDist(ctx, raw) {
|
|
28634
|
-
return
|
|
29464
|
+
return isAbsolute14(raw) ? raw : resolve40(ctx.cwd, raw);
|
|
28635
29465
|
}
|
|
28636
29466
|
function isUiBundleDir(path) {
|
|
28637
29467
|
if (!existsSync30(path)) return false;
|
|
@@ -28665,7 +29495,7 @@ function resolvePackageBundledUiFrom(here) {
|
|
|
28665
29495
|
return null;
|
|
28666
29496
|
}
|
|
28667
29497
|
function walkUpForUi(startDir) {
|
|
28668
|
-
let current =
|
|
29498
|
+
let current = resolve40(startDir);
|
|
28669
29499
|
for (let i = 0; i < 64; i++) {
|
|
28670
29500
|
const candidate = join20(current, DEFAULT_UI_REL);
|
|
28671
29501
|
if (isUiBundleDir(candidate)) return candidate;
|
|
@@ -28711,6 +29541,7 @@ async function createServer(options, extra = {}) {
|
|
|
28711
29541
|
broadcaster
|
|
28712
29542
|
};
|
|
28713
29543
|
if (debounce !== void 0) svcOpts.debounceMsOverride = debounce;
|
|
29544
|
+
if (extra.scanProgress) svcOpts.scanProgress = extra.scanProgress;
|
|
28714
29545
|
const candidate = createWatcherService(svcOpts);
|
|
28715
29546
|
try {
|
|
28716
29547
|
await candidate.start();
|
|
@@ -28904,6 +29735,12 @@ var SERVE_TEXTS = {
|
|
|
28904
29735
|
*/
|
|
28905
29736
|
watcherDebounceInvalid: "{{glyph}} sm serve: --watcher-debounce-ms must be a non-negative integer (got {{value}}).\n {{hint}}\n",
|
|
28906
29737
|
watcherDebounceInvalidHint: "Pass an integer >= 0 (e.g. 250).",
|
|
29738
|
+
/**
|
|
29739
|
+
* §3.1b error block for an invalid `--max-scan <N>`. Same shape as
|
|
29740
|
+
* the watcher-debounce template family.
|
|
29741
|
+
*/
|
|
29742
|
+
maxScanInvalid: "{{glyph}} sm serve: --max-scan must be an integer >= 1 (got {{value}}).\n {{hint}}\n",
|
|
29743
|
+
maxScanInvalidHint: "Pass a positive integer, e.g. --max-scan 50000.",
|
|
28907
29744
|
/**
|
|
28908
29745
|
* §3.1b error block for an invalid `--max-nodes <N>`. Same shape as
|
|
28909
29746
|
* the watcher-debounce template family.
|
|
@@ -28959,7 +29796,7 @@ var SERVE_TEXTS = {
|
|
|
28959
29796
|
};
|
|
28960
29797
|
|
|
28961
29798
|
// cli/util/serve-banner.ts
|
|
28962
|
-
import { relative as
|
|
29799
|
+
import { relative as relative8, isAbsolute as isAbsolute15 } from "path";
|
|
28963
29800
|
var ESC2 = {
|
|
28964
29801
|
reset: "\x1B[0m",
|
|
28965
29802
|
bold: "\x1B[1m",
|
|
@@ -29106,9 +29943,9 @@ function resolveAnsi(colorEnabled) {
|
|
|
29106
29943
|
}
|
|
29107
29944
|
function formatDbPath(dbPath, cwd) {
|
|
29108
29945
|
const safe = sanitizeForTerminal(dbPath);
|
|
29109
|
-
if (!
|
|
29110
|
-
const rel =
|
|
29111
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
29946
|
+
if (!isAbsolute15(safe)) return safe;
|
|
29947
|
+
const rel = relative8(cwd, safe);
|
|
29948
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute15(rel)) {
|
|
29112
29949
|
return safe;
|
|
29113
29950
|
}
|
|
29114
29951
|
return rel;
|
|
@@ -29189,9 +30026,13 @@ var ServeCommand = class extends SmCommand {
|
|
|
29189
30026
|
// who want to tighten / relax the watcher's batching window without
|
|
29190
30027
|
// editing settings.json. Hidden flag, the Usage block omits it.
|
|
29191
30028
|
watcherDebounceMs = Option32.String("--watcher-debounce-ms", { required: false, hidden: true });
|
|
30029
|
+
maxScan = Option32.String("--max-scan", {
|
|
30030
|
+
required: false,
|
|
30031
|
+
description: "Per-invocation override of scan.maxScan (default 50000), the WALK-INTAKE ceiling. The scan walks, parses, analyzes, and reference-validates the full corpus up to this number. Bidirectional: raises OR lowers the ceiling. Applies to every scan the server runs (initial watcher pass, debounced batches, POST /api/scan, GET /api/scan?fresh=1). Same flag is honoured on the bare `sm` invocation, which routes to `sm serve`."
|
|
30032
|
+
});
|
|
29192
30033
|
maxNodes = Option32.String("--max-nodes", {
|
|
29193
30034
|
required: false,
|
|
29194
|
-
description: "Per-invocation override of scan.maxNodes (default 256)
|
|
30035
|
+
description: "Per-invocation override of scan.maxNodes (default 256), the MAP RENDER cap (pure metadata): it does NOT bound the scan, only how many nodes the graph view projects onto the canvas. Bidirectional: raises OR lowers the render cap. Same flag is honoured on the bare `sm` invocation, which routes to `sm serve`."
|
|
29195
30036
|
});
|
|
29196
30037
|
// Long-running daemon, `done in <…>` after a graceful shutdown is
|
|
29197
30038
|
// noise. Mirrors `sm watch`'s opt-out.
|
|
@@ -29271,6 +30112,17 @@ var ServeCommand = class extends SmCommand {
|
|
|
29271
30112
|
);
|
|
29272
30113
|
return ExitCode.Error;
|
|
29273
30114
|
}
|
|
30115
|
+
const maxScanResult = parseMaxScan(this.maxScan);
|
|
30116
|
+
if (!maxScanResult.ok) {
|
|
30117
|
+
this.printer.info(
|
|
30118
|
+
tx(SERVE_TEXTS.maxScanInvalid, {
|
|
30119
|
+
glyph: errGlyph,
|
|
30120
|
+
value: sanitizeForTerminal(maxScanResult.value),
|
|
30121
|
+
hint: stderrAnsi.dim(SERVE_TEXTS.maxScanInvalidHint)
|
|
30122
|
+
})
|
|
30123
|
+
);
|
|
30124
|
+
return ExitCode.Error;
|
|
30125
|
+
}
|
|
29274
30126
|
const maxNodesResult = parseMaxNodes(this.maxNodes);
|
|
29275
30127
|
if (!maxNodesResult.ok) {
|
|
29276
30128
|
this.printer.info(
|
|
@@ -29295,6 +30147,7 @@ var ServeCommand = class extends SmCommand {
|
|
|
29295
30147
|
if (portResult.port !== void 0) input.port = portResult.port;
|
|
29296
30148
|
if (this.host !== void 0) input.host = this.host;
|
|
29297
30149
|
if (debounceResult.value !== void 0) input.watcherDebounceMs = debounceResult.value;
|
|
30150
|
+
if (maxScanResult.value !== void 0) input.maxScan = maxScanResult.value;
|
|
29298
30151
|
if (maxNodesResult.value !== void 0) input.maxNodes = maxNodesResult.value;
|
|
29299
30152
|
const validation = validateServerOptions(input);
|
|
29300
30153
|
if (!validation.ok) {
|
|
@@ -29303,10 +30156,19 @@ var ServeCommand = class extends SmCommand {
|
|
|
29303
30156
|
}
|
|
29304
30157
|
const driftAbort = await this.#rebuildOnDrift(dbPath, stderrAnsi, warnGlyph);
|
|
29305
30158
|
if (driftAbort !== null) return driftAbort;
|
|
30159
|
+
const stderr = this.context.stderr;
|
|
30160
|
+
const isTTY = stderr.isTTY === true;
|
|
30161
|
+
const colorEnabled = resolveColorEnabled({
|
|
30162
|
+
isTTY,
|
|
30163
|
+
noColorFlag: this.noColor,
|
|
30164
|
+
env: process.env
|
|
30165
|
+
});
|
|
29306
30166
|
await initSentryBff(VERSION);
|
|
29307
30167
|
let handle;
|
|
29308
30168
|
try {
|
|
29309
|
-
handle = await createServer(validation.options
|
|
30169
|
+
handle = await createServer(validation.options, {
|
|
30170
|
+
scanProgress: { stream: stderr, colorEnabled }
|
|
30171
|
+
});
|
|
29310
30172
|
} catch (err) {
|
|
29311
30173
|
const message = formatErrorMessage(err);
|
|
29312
30174
|
this.printer.info(
|
|
@@ -29319,13 +30181,6 @@ var ServeCommand = class extends SmCommand {
|
|
|
29319
30181
|
);
|
|
29320
30182
|
return ExitCode.Error;
|
|
29321
30183
|
}
|
|
29322
|
-
const stderr = this.context.stderr;
|
|
29323
|
-
const isTTY = stderr.isTTY === true;
|
|
29324
|
-
const colorEnabled = resolveColorEnabled({
|
|
29325
|
-
isTTY,
|
|
29326
|
-
noColorFlag: this.noColor,
|
|
29327
|
-
env: process.env
|
|
29328
|
-
});
|
|
29329
30184
|
let referencePaths = [];
|
|
29330
30185
|
try {
|
|
29331
30186
|
const cfg = loadConfig({ cwd: runtimeCtx.cwd }).effective;
|
|
@@ -29397,6 +30252,12 @@ function parseDebounce(raw) {
|
|
|
29397
30252
|
if (parsed === null) return { ok: false, value: raw };
|
|
29398
30253
|
return { ok: true, value: parsed };
|
|
29399
30254
|
}
|
|
30255
|
+
function parseMaxScan(raw) {
|
|
30256
|
+
if (raw === void 0) return { ok: true, value: void 0 };
|
|
30257
|
+
const n = Number(raw);
|
|
30258
|
+
if (!Number.isInteger(n) || n < 1) return { ok: false, value: raw };
|
|
30259
|
+
return { ok: true, value: n };
|
|
30260
|
+
}
|
|
29400
30261
|
function parseMaxNodes(raw) {
|
|
29401
30262
|
if (raw === void 0) return { ok: true, value: void 0 };
|
|
29402
30263
|
const n = Number(raw);
|
|
@@ -29455,6 +30316,12 @@ function formatValidationError(err, ansi) {
|
|
|
29455
30316
|
value: sanitizeForTerminal(err.value),
|
|
29456
30317
|
hint: ansi.dim(SERVE_TEXTS.watcherDebounceInvalidHint)
|
|
29457
30318
|
});
|
|
30319
|
+
case "max-scan-invalid":
|
|
30320
|
+
return tx(SERVE_TEXTS.maxScanInvalid, {
|
|
30321
|
+
glyph: errGlyph,
|
|
30322
|
+
value: sanitizeForTerminal(err.value),
|
|
30323
|
+
hint: ansi.dim(SERVE_TEXTS.maxScanInvalidHint)
|
|
30324
|
+
});
|
|
29458
30325
|
case "max-nodes-invalid":
|
|
29459
30326
|
return tx(SERVE_TEXTS.maxNodesInvalid, {
|
|
29460
30327
|
glyph: errGlyph,
|
|
@@ -29841,7 +30708,7 @@ function rankConfidenceForGrouping(c) {
|
|
|
29841
30708
|
|
|
29842
30709
|
// cli/commands/sidecar.ts
|
|
29843
30710
|
import { unlink as unlink3 } from "fs/promises";
|
|
29844
|
-
import { resolve as
|
|
30711
|
+
import { resolve as resolve41 } from "path";
|
|
29845
30712
|
import { Command as Command36, Option as Option34 } from "clipanion";
|
|
29846
30713
|
|
|
29847
30714
|
// cli/i18n/sidecar.texts.ts
|
|
@@ -29999,7 +30866,7 @@ var SidecarRefreshCommand = class extends SmCommand {
|
|
|
29999
30866
|
let absPath;
|
|
30000
30867
|
try {
|
|
30001
30868
|
assertContained(ctx.cwd, node.path);
|
|
30002
|
-
absPath =
|
|
30869
|
+
absPath = resolve41(ctx.cwd, node.path);
|
|
30003
30870
|
} catch (err) {
|
|
30004
30871
|
this.printer.error(
|
|
30005
30872
|
tx(SIDECAR_TEXTS.refreshFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
|
|
@@ -30283,7 +31150,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
30283
31150
|
let absPath;
|
|
30284
31151
|
try {
|
|
30285
31152
|
assertContained(ctx.cwd, node.path);
|
|
30286
|
-
absPath =
|
|
31153
|
+
absPath = resolve41(ctx.cwd, node.path);
|
|
30287
31154
|
} catch (err) {
|
|
30288
31155
|
this.printer.error(
|
|
30289
31156
|
tx(SIDECAR_TEXTS.annotateFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
|
|
@@ -30535,7 +31402,7 @@ var STUB_COMMANDS = [
|
|
|
30535
31402
|
|
|
30536
31403
|
// cli/commands/tutorial.ts
|
|
30537
31404
|
import { cpSync as cpSync2, existsSync as existsSync32, mkdirSync as mkdirSync6, readdirSync as readdirSync10, rmSync as rmSync2, statSync as statSync11 } from "fs";
|
|
30538
|
-
import { dirname as dirname20, join as join21, resolve as
|
|
31405
|
+
import { dirname as dirname20, join as join21, resolve as resolve42 } from "path";
|
|
30539
31406
|
import { createInterface as createInterface5 } from "readline";
|
|
30540
31407
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
30541
31408
|
import { Command as Command38, Option as Option36 } from "clipanion";
|
|
@@ -30881,11 +31748,11 @@ function resolveSkillSourceDir() {
|
|
|
30881
31748
|
const here = dirname20(fileURLToPath7(import.meta.url));
|
|
30882
31749
|
const candidates = [
|
|
30883
31750
|
// dev: src/cli/commands/ → repo-root .claude/skills/sm-tutorial/
|
|
30884
|
-
|
|
31751
|
+
resolve42(here, "../../..", SKILL_SOURCE_DIR),
|
|
30885
31752
|
// bundled: dist/cli.js → dist/cli/tutorial/sm-tutorial (sibling)
|
|
30886
|
-
|
|
31753
|
+
resolve42(here, "cli/tutorial", SKILL_SLUG),
|
|
30887
31754
|
// bundled fallback: any-depth → cli/tutorial/sm-tutorial
|
|
30888
|
-
|
|
31755
|
+
resolve42(here, "../cli/tutorial", SKILL_SLUG)
|
|
30889
31756
|
];
|
|
30890
31757
|
for (const candidate of candidates) {
|
|
30891
31758
|
if (existsSync32(candidate) && statSync11(candidate).isDirectory()) {
|
|
@@ -31110,4 +31977,4 @@ function resolveBareDefault() {
|
|
|
31110
31977
|
process.exit(ExitCode.Error);
|
|
31111
31978
|
}
|
|
31112
31979
|
//# sourceMappingURL=cli.js.map
|
|
31113
|
-
//# debugId=
|
|
31980
|
+
//# debugId=d7ccbbda-dc56-582c-966a-4822e4bdcd83
|