@skill-map/cli 0.31.0 → 0.32.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 +531 -199
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +1 -1
- package/dist/kernel/index.js +1 -1
- package/dist/kernel/index.js.map +1 -1
- package/dist/ui/chunk-BCQZKYOD.js +1 -0
- package/dist/ui/chunk-WCE7MTK5.js +123 -0
- package/dist/ui/index.html +1 -1
- package/dist/ui/main-LJIHL73M.js +2 -0
- package/package.json +2 -2
- package/dist/ui/chunk-LNRQ7VKE.js +0 -1
- package/dist/ui/chunk-PVKIT7DW.js +0 -123
- package/dist/ui/main-N23S66NJ.js +0 -2
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// cli/entry.ts
|
|
2
|
-
import { existsSync as
|
|
2
|
+
import { existsSync as existsSync31 } from "fs";
|
|
3
3
|
import { Builtins, Cli as Cli2 } from "clipanion";
|
|
4
4
|
|
|
5
5
|
// kernel/adapters/in-memory-progress.ts
|
|
@@ -2963,7 +2963,7 @@ var UPDATE_CHECK_TEXTS = {
|
|
|
2963
2963
|
// package.json
|
|
2964
2964
|
var package_default = {
|
|
2965
2965
|
name: "@skill-map/cli",
|
|
2966
|
-
version: "0.
|
|
2966
|
+
version: "0.32.0",
|
|
2967
2967
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
2968
2968
|
license: "MIT",
|
|
2969
2969
|
type: "module",
|
|
@@ -3893,7 +3893,6 @@ var defaults_default = {
|
|
|
3893
3893
|
watch: {
|
|
3894
3894
|
debounceMs: 300
|
|
3895
3895
|
},
|
|
3896
|
-
extraFolders: [],
|
|
3897
3896
|
referencePaths: []
|
|
3898
3897
|
},
|
|
3899
3898
|
plugins: {},
|
|
@@ -3919,7 +3918,6 @@ var defaults_default = {
|
|
|
3919
3918
|
// kernel/config/loader.ts
|
|
3920
3919
|
var PROJECT_LOCAL_ONLY_KEYS = /* @__PURE__ */ new Set([
|
|
3921
3920
|
"allowEditSmFiles",
|
|
3922
|
-
"scan.extraFolders",
|
|
3923
3921
|
"scan.referencePaths"
|
|
3924
3922
|
]);
|
|
3925
3923
|
var DEFAULTS = defaults_default;
|
|
@@ -4258,7 +4256,6 @@ function writeJsonAtomic(path, content) {
|
|
|
4258
4256
|
|
|
4259
4257
|
// core/config/helper.ts
|
|
4260
4258
|
var PRIVACY_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
4261
|
-
"scan.extraFolders",
|
|
4262
4259
|
"scan.referencePaths"
|
|
4263
4260
|
]);
|
|
4264
4261
|
var ProjectLocalOnlyKeyError = class extends Error {
|
|
@@ -4330,19 +4327,17 @@ var ConfigValidationError = class extends Error {
|
|
|
4330
4327
|
function projectPathExposure(inputs) {
|
|
4331
4328
|
const empty = { expandsSurface: false, exposedPaths: [] };
|
|
4332
4329
|
if (!PRIVACY_SENSITIVE_KEYS.has(inputs.key)) return empty;
|
|
4333
|
-
if (inputs.key
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
}
|
|
4345
|
-
return empty;
|
|
4330
|
+
if (inputs.key !== "scan.referencePaths") return empty;
|
|
4331
|
+
if (!Array.isArray(inputs.value)) return empty;
|
|
4332
|
+
const before = readConfigValue(inputs.key, {
|
|
4333
|
+
cwd: inputs.cwd,
|
|
4334
|
+
default: []
|
|
4335
|
+
}) ?? [];
|
|
4336
|
+
const beforeSet = new Set(before);
|
|
4337
|
+
const added = inputs.value.filter((entry) => typeof entry === "string").filter((entry) => !beforeSet.has(entry));
|
|
4338
|
+
const exposed = added.map((entry) => resolveScanPathForExposure(entry, inputs.cwd)).filter((abs) => abs !== null && !isUnderProject(abs, inputs.cwd));
|
|
4339
|
+
if (exposed.length === 0) return empty;
|
|
4340
|
+
return { expandsSurface: true, exposedPaths: exposed };
|
|
4346
4341
|
}
|
|
4347
4342
|
function resolveScanPathForExposure(raw, cwd) {
|
|
4348
4343
|
if (raw.startsWith("~/")) return resolve6(join4(osHomedir(), raw.slice(2)));
|
|
@@ -4890,7 +4885,7 @@ var AsyncMutex = class {
|
|
|
4890
4885
|
this.#locked = true;
|
|
4891
4886
|
return;
|
|
4892
4887
|
}
|
|
4893
|
-
await new Promise((
|
|
4888
|
+
await new Promise((resolve38) => this.#waiters.push(resolve38));
|
|
4894
4889
|
this.#locked = true;
|
|
4895
4890
|
}
|
|
4896
4891
|
unlock() {
|
|
@@ -8081,9 +8076,9 @@ function providerKindFailure(opts, status, fileName, errDescription) {
|
|
|
8081
8076
|
}
|
|
8082
8077
|
};
|
|
8083
8078
|
}
|
|
8084
|
-
function isDirectorySafe(path,
|
|
8079
|
+
function isDirectorySafe(path, statSync13) {
|
|
8085
8080
|
try {
|
|
8086
|
-
return
|
|
8081
|
+
return statSync13(path).isDirectory();
|
|
8087
8082
|
} catch {
|
|
8088
8083
|
return false;
|
|
8089
8084
|
}
|
|
@@ -9879,7 +9874,7 @@ var ConfigSetCommand = class extends SmCommand {
|
|
|
9879
9874
|
key = Option4.String({ required: true });
|
|
9880
9875
|
value = Option4.String({ required: true });
|
|
9881
9876
|
yes = Option4.Boolean("--yes", false, {
|
|
9882
|
-
description: "Confirm a privacy-sensitive write that opens disk access outside the project (scan.
|
|
9877
|
+
description: "Confirm a privacy-sensitive write that opens disk access outside the project (scan.referencePaths)."
|
|
9883
9878
|
});
|
|
9884
9879
|
// CLI orchestrator: each branch is one validation gate (forbidden
|
|
9885
9880
|
// segment / privacy guard / schema violation) or output dispatch.
|
|
@@ -14346,12 +14341,6 @@ var SCAN_RUNNER_TEXTS = {
|
|
|
14346
14341
|
* validation.
|
|
14347
14342
|
*/
|
|
14348
14343
|
priorSchemaValidationFailed: "prior scan-result loaded from DB failed schema validation: {{errors}}. Run `sm db backup` then re-scan without --strict to rebuild from disk.",
|
|
14349
|
-
/**
|
|
14350
|
-
* Honest disclosure when the scan surface expanded beyond the cwd
|
|
14351
|
-
* via `scan.extraFolders`. The list of paths makes it obvious which
|
|
14352
|
-
* extra folders the operator just opted into.
|
|
14353
|
-
*/
|
|
14354
|
-
includingExtraFoldersAdvisory: "Including extra folders: {{paths}}",
|
|
14355
14344
|
/**
|
|
14356
14345
|
* Reference-paths walker hit `REFERENCE_WALK_MAX_FILES` and stopped
|
|
14357
14346
|
* early. The set may be incomplete for link validation; `core/broken-ref`
|
|
@@ -14366,6 +14355,14 @@ var SCAN_RUNNER_TEXTS = {
|
|
|
14366
14355
|
referenceWalkMissingRoot: 'scan.referencePaths: configured path "{{path}}" does not exist; skipped.'
|
|
14367
14356
|
};
|
|
14368
14357
|
|
|
14358
|
+
// core/runtime/scan-roots.ts
|
|
14359
|
+
function resolveScanRoots(inputs) {
|
|
14360
|
+
if (inputs.positionalRoots.length > 0) {
|
|
14361
|
+
return inputs.positionalRoots.slice();
|
|
14362
|
+
}
|
|
14363
|
+
return ["."];
|
|
14364
|
+
}
|
|
14365
|
+
|
|
14369
14366
|
// core/runtime/reference-paths-walker.ts
|
|
14370
14367
|
import { readdirSync as readdirSync9, statSync as statSync9 } from "fs";
|
|
14371
14368
|
import { homedir as osHomedir2 } from "os";
|
|
@@ -14427,26 +14424,6 @@ function safeStat(path) {
|
|
|
14427
14424
|
}
|
|
14428
14425
|
}
|
|
14429
14426
|
|
|
14430
|
-
// core/runtime/scan-roots.ts
|
|
14431
|
-
function resolveScanRoots(inputs) {
|
|
14432
|
-
if (inputs.positionalRoots.length > 0) {
|
|
14433
|
-
return {
|
|
14434
|
-
roots: inputs.positionalRoots.slice(),
|
|
14435
|
-
fromExtra: []
|
|
14436
|
-
};
|
|
14437
|
-
}
|
|
14438
|
-
const cwdRoot = ".";
|
|
14439
|
-
const extra = inputs.extraFolders.map((r) => resolveScanPath(r, inputs.cwd));
|
|
14440
|
-
const seen = /* @__PURE__ */ new Set();
|
|
14441
|
-
const out = [cwdRoot];
|
|
14442
|
-
for (const root of extra) {
|
|
14443
|
-
if (seen.has(root)) continue;
|
|
14444
|
-
seen.add(root);
|
|
14445
|
-
out.push(root);
|
|
14446
|
-
}
|
|
14447
|
-
return { roots: out, fromExtra: extra };
|
|
14448
|
-
}
|
|
14449
|
-
|
|
14450
14427
|
// core/runtime/scan-runner.ts
|
|
14451
14428
|
async function runScanForCommand(opts) {
|
|
14452
14429
|
const ctx = opts.ctx ?? defaultRuntimeContext();
|
|
@@ -14464,13 +14441,7 @@ async function runScanForCommand(opts) {
|
|
|
14464
14441
|
const strict = opts.strict || cfg.scan.strict === true;
|
|
14465
14442
|
let effectiveRoots;
|
|
14466
14443
|
try {
|
|
14467
|
-
|
|
14468
|
-
positionalRoots: opts.roots,
|
|
14469
|
-
cwd: ctx.cwd,
|
|
14470
|
-
extraFolders: cfg.scan.extraFolders
|
|
14471
|
-
});
|
|
14472
|
-
effectiveRoots = resolution.roots;
|
|
14473
|
-
emitRootsAdvisory(resolution.fromExtra, opts);
|
|
14444
|
+
effectiveRoots = resolveScanRoots({ positionalRoots: opts.roots });
|
|
14474
14445
|
} catch (err) {
|
|
14475
14446
|
return { kind: "config-error", message: formatErrorMessage(err) };
|
|
14476
14447
|
}
|
|
@@ -14495,12 +14466,6 @@ async function runScanForCommand(opts) {
|
|
|
14495
14466
|
const willPersist = !opts.noBuiltIns && !opts.dryRun;
|
|
14496
14467
|
return willPersist ? runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanWith, extensions) : runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith);
|
|
14497
14468
|
}
|
|
14498
|
-
function emitRootsAdvisory(fromExtra, opts) {
|
|
14499
|
-
if (fromExtra.length === 0) return;
|
|
14500
|
-
opts.printer.info(
|
|
14501
|
-
tx(SCAN_RUNNER_TEXTS.includingExtraFoldersAdvisory, { paths: fromExtra.join(", ") }) + "\n"
|
|
14502
|
-
);
|
|
14503
|
-
}
|
|
14504
14469
|
function emitReferenceWalkAdvisory(walk2, opts) {
|
|
14505
14470
|
if (walk2.truncated) {
|
|
14506
14471
|
opts.printer.warn(SCAN_RUNNER_TEXTS.referenceWalkTruncated);
|
|
@@ -18518,6 +18483,13 @@ function createWatcherRuntime(opts) {
|
|
|
18518
18483
|
strict,
|
|
18519
18484
|
emitter
|
|
18520
18485
|
};
|
|
18486
|
+
if (cfg.scan.referencePaths.length > 0) {
|
|
18487
|
+
const walk2 = walkReferencePaths(cfg.scan.referencePaths, cwd);
|
|
18488
|
+
if (walk2.paths.size > 0) {
|
|
18489
|
+
runOptions.referenceablePaths = walk2.paths;
|
|
18490
|
+
runOptions.cwd = cwd;
|
|
18491
|
+
}
|
|
18492
|
+
}
|
|
18521
18493
|
if (composed) runOptions.extensions = composed;
|
|
18522
18494
|
if (priorState) {
|
|
18523
18495
|
runOptions.priorSnapshot = priorState.snapshot;
|
|
@@ -18952,12 +18924,11 @@ var ScanCommand = class extends SmCommand {
|
|
|
18952
18924
|
the prior snapshot from the DB, reuse unchanged nodes, and only
|
|
18953
18925
|
reprocess new / modified files.
|
|
18954
18926
|
|
|
18955
|
-
Scans honour scan.
|
|
18956
|
-
only
|
|
18957
|
-
(walk the configured dirs for link-validation only; files there
|
|
18958
|
-
are not indexed). Both are
|
|
18927
|
+
Scans honour scan.referencePaths (walk the configured dirs for
|
|
18928
|
+
link-validation only; files there are not indexed). The key is
|
|
18959
18929
|
privacy-sensitive; see "sm config set --help" for the --yes
|
|
18960
|
-
gate.
|
|
18930
|
+
gate. To extend the indexed scan beyond cwd, pass extra roots
|
|
18931
|
+
positionally.
|
|
18961
18932
|
`,
|
|
18962
18933
|
examples: [
|
|
18963
18934
|
["Scan the current directory", "$0 scan"],
|
|
@@ -19418,7 +19389,7 @@ function renderDeltaIssues(issues) {
|
|
|
19418
19389
|
|
|
19419
19390
|
// cli/commands/serve.ts
|
|
19420
19391
|
import { spawn as spawn2 } from "child_process";
|
|
19421
|
-
import { existsSync as
|
|
19392
|
+
import { existsSync as existsSync28 } from "fs";
|
|
19422
19393
|
import { Command as Command33, Option as Option31 } from "clipanion";
|
|
19423
19394
|
|
|
19424
19395
|
// cli/util/browser-launch.ts
|
|
@@ -19440,7 +19411,7 @@ import { WebSocketServer } from "ws";
|
|
|
19440
19411
|
// server/app.ts
|
|
19441
19412
|
import { Hono } from "hono";
|
|
19442
19413
|
import { bodyLimit } from "hono/body-limit";
|
|
19443
|
-
import { HTTPException as
|
|
19414
|
+
import { HTTPException as HTTPException14 } from "hono/http-exception";
|
|
19444
19415
|
|
|
19445
19416
|
// core/config/service.ts
|
|
19446
19417
|
var ConfigService = class {
|
|
@@ -19682,13 +19653,60 @@ var SERVER_TEXTS = {
|
|
|
19682
19653
|
// silently widen the scan surface.
|
|
19683
19654
|
projectPrefsBodyNotJson: "Request body must be valid JSON.",
|
|
19684
19655
|
projectPrefsBodyNotObject: "Request body must be a JSON object.",
|
|
19685
|
-
projectPrefsBodyEmpty: "Request body must contain a `scan` block with
|
|
19656
|
+
projectPrefsBodyEmpty: "Request body must contain a `scan` block with `referencePaths`.",
|
|
19686
19657
|
projectPrefsConfirmNotBoolean: "`confirm` must be a boolean.",
|
|
19687
|
-
projectPrefsScanNotObject: '`scan` must be an object (e.g. `{"scan": {"
|
|
19658
|
+
projectPrefsScanNotObject: '`scan` must be an object (e.g. `{"scan": {"referencePaths": ["~/Documents"]}}`).',
|
|
19688
19659
|
projectPrefsListNotArray: "`{{key}}` must be an array of strings.",
|
|
19689
19660
|
projectPrefsListEntryNotString: "`{{key}}` entries must be strings.",
|
|
19690
19661
|
projectPrefsConfirmRequired: "This change opens disk access outside the project: {{paths}}. Re-issue the request with `confirm: true` to proceed.",
|
|
19691
19662
|
projectPrefsPersistFailed: "Could not persist `{{key}}`: {{message}}",
|
|
19663
|
+
// Returned for every NEW entry that does not resolve to an existing
|
|
19664
|
+
// directory on disk. The list is comma-separated; pre-existing
|
|
19665
|
+
// entries are not re-validated.
|
|
19666
|
+
projectPrefsPathNotFound: "These folders do not exist on disk: {{paths}}. Add only paths that already exist.",
|
|
19667
|
+
// AJV `pattern` violation, an entry contains a comma. The UI rejects
|
|
19668
|
+
// comma input client-side; this message is the server-side safety
|
|
19669
|
+
// net (defense in depth).
|
|
19670
|
+
projectPrefsEntryHasComma: "Folder entries must not contain commas. Add one folder per entry.",
|
|
19671
|
+
// Server-stderr advisories emitted by `PATCH /api/project-preferences`
|
|
19672
|
+
// after a successful write. The operator running `sm serve` sees
|
|
19673
|
+
// each add / remove on the console without opening the config file.
|
|
19674
|
+
// `{{detail}}` is composed in JS (see `formatPathDetail`) so the
|
|
19675
|
+
// single template covers all three path shapes (home / relative /
|
|
19676
|
+
// absolute) without a template explosion.
|
|
19677
|
+
projectPrefsPathAdded: "project-prefs: + {{key}} {{detail}}",
|
|
19678
|
+
projectPrefsPathRemoved: "project-prefs: - {{key}} {{detail}}",
|
|
19679
|
+
// `PATCH /api/project-preferences` mutated `scan.*` and the
|
|
19680
|
+
// post-write `watcherService.restart()` call threw. The on-disk
|
|
19681
|
+
// write itself succeeded; the operator sees this advisory and
|
|
19682
|
+
// restarts the server to pick up the new root list manually.
|
|
19683
|
+
projectPrefsWatcherRestartFailed: "project-prefs: watcher restart after scan-config write failed ({{message}}). Restart `sm serve` to pick up the new roots.",
|
|
19684
|
+
// ---- project-ignore route (routes/project-ignore.ts) -------------------
|
|
19685
|
+
//
|
|
19686
|
+
// GET / PATCH /api/project-ignore. Backing is the project-root
|
|
19687
|
+
// `.skillmapignore` file (gitignore-syntax). Comments and blank
|
|
19688
|
+
// lines are preserved on write; only the active pattern list is
|
|
19689
|
+
// exchanged over the wire. No privacy gate, the patterns narrow the
|
|
19690
|
+
// scan surface and never widen disk access.
|
|
19691
|
+
projectIgnoreBodyNotJson: "Request body must be valid JSON.",
|
|
19692
|
+
projectIgnoreBodyNotObject: "Request body must be a JSON object.",
|
|
19693
|
+
projectIgnoreBodyEmpty: "Request body must contain a `patterns` array.",
|
|
19694
|
+
projectIgnoreListNotArray: "`patterns` must be an array of strings.",
|
|
19695
|
+
projectIgnoreEntryNotString: "`patterns` entries must be strings.",
|
|
19696
|
+
// AJV `minLength: 1` on each pattern after the route trims server-side;
|
|
19697
|
+
// surfaces when the operator sends `" "` or an empty string.
|
|
19698
|
+
projectIgnorePatternEmpty: "Pattern entries must not be empty or whitespace-only.",
|
|
19699
|
+
// AJV `pattern` violation: a single pattern carried a newline,
|
|
19700
|
+
// carriage return, or other ASCII control character. The UI rejects
|
|
19701
|
+
// these client-side; this message is the server-side safety net.
|
|
19702
|
+
projectIgnorePatternHasControlChar: "Pattern entries must be a single line without control characters.",
|
|
19703
|
+
// Duplicate detection runs after trim; the UI rejects duplicates
|
|
19704
|
+
// client-side, this is the server-side safety net.
|
|
19705
|
+
projectIgnorePatternDuplicate: 'Duplicate pattern: "{{pattern}}". Each pattern must be unique.',
|
|
19706
|
+
projectIgnorePersistFailed: "Could not persist `.skillmapignore`: {{message}}",
|
|
19707
|
+
projectIgnorePatternAdded: "project-ignore: + {{pattern}}",
|
|
19708
|
+
projectIgnorePatternRemoved: "project-ignore: - {{pattern}}",
|
|
19709
|
+
projectIgnoreWatcherRestartFailed: "project-ignore: watcher restart after `.skillmapignore` write failed ({{message}}). Restart `sm serve` to pick up the new filter.",
|
|
19692
19710
|
// A connected client's outbound buffer exceeded the backpressure
|
|
19693
19711
|
// threshold. The broadcaster closes the client with code 1009 and
|
|
19694
19712
|
// unregisters it. Logged so operators can spot a wedged consumer.
|
|
@@ -20922,26 +20940,203 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
|
|
|
20922
20940
|
}
|
|
20923
20941
|
});
|
|
20924
20942
|
|
|
20925
|
-
// server/routes/project-
|
|
20943
|
+
// server/routes/project-ignore.ts
|
|
20926
20944
|
import { HTTPException as HTTPException10 } from "hono/http-exception";
|
|
20927
|
-
|
|
20928
|
-
|
|
20945
|
+
|
|
20946
|
+
// server/util/skillmapignore-io.ts
|
|
20947
|
+
import { existsSync as existsSync25, readFileSync as readFileSync19, writeFileSync as writeFileSync4 } from "fs";
|
|
20948
|
+
import { resolve as resolve33 } from "path";
|
|
20949
|
+
var IGNORE_FILENAME2 = ".skillmapignore";
|
|
20950
|
+
function readPatterns(cwd) {
|
|
20951
|
+
const path = resolve33(cwd, IGNORE_FILENAME2);
|
|
20952
|
+
if (!existsSync25(path)) return [];
|
|
20953
|
+
let raw;
|
|
20954
|
+
try {
|
|
20955
|
+
raw = readFileSync19(path, "utf8");
|
|
20956
|
+
} catch {
|
|
20957
|
+
return [];
|
|
20958
|
+
}
|
|
20959
|
+
return raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
20960
|
+
}
|
|
20961
|
+
function writePatterns(cwd, nextPatterns) {
|
|
20962
|
+
const path = resolve33(cwd, IGNORE_FILENAME2);
|
|
20963
|
+
const prior = existsSync25(path) ? safeRead(path) : "";
|
|
20964
|
+
const content = buildContent(prior, nextPatterns);
|
|
20965
|
+
writeFileSync4(path, content, "utf8");
|
|
20966
|
+
}
|
|
20967
|
+
function safeRead(path) {
|
|
20968
|
+
try {
|
|
20969
|
+
return readFileSync19(path, "utf8");
|
|
20970
|
+
} catch {
|
|
20971
|
+
return "";
|
|
20972
|
+
}
|
|
20973
|
+
}
|
|
20974
|
+
function buildContent(prior, nextPatterns) {
|
|
20975
|
+
const wanted = new Set(nextPatterns);
|
|
20976
|
+
const kept = /* @__PURE__ */ new Set();
|
|
20977
|
+
const outLines = [];
|
|
20978
|
+
for (const line of splitLines(prior)) {
|
|
20979
|
+
pushPriorLine(line, wanted, kept, outLines);
|
|
20980
|
+
}
|
|
20981
|
+
appendNewPatterns(nextPatterns, kept, outLines);
|
|
20982
|
+
return outLines.length === 0 ? "" : outLines.join("\n") + "\n";
|
|
20983
|
+
}
|
|
20984
|
+
function splitLines(prior) {
|
|
20985
|
+
if (prior.length === 0) return [];
|
|
20986
|
+
const lines = prior.split(/\r?\n/);
|
|
20987
|
+
if (lines[lines.length - 1] === "") lines.pop();
|
|
20988
|
+
return lines;
|
|
20989
|
+
}
|
|
20990
|
+
function pushPriorLine(line, wanted, kept, outLines) {
|
|
20991
|
+
const trimmed = line.trim();
|
|
20992
|
+
if (trimmed.length === 0 || trimmed.startsWith("#")) {
|
|
20993
|
+
outLines.push(line);
|
|
20994
|
+
return;
|
|
20995
|
+
}
|
|
20996
|
+
if (wanted.has(trimmed)) {
|
|
20997
|
+
outLines.push(trimmed);
|
|
20998
|
+
kept.add(trimmed);
|
|
20999
|
+
}
|
|
21000
|
+
}
|
|
21001
|
+
function appendNewPatterns(nextPatterns, kept, outLines) {
|
|
21002
|
+
for (const p of nextPatterns) {
|
|
21003
|
+
if (kept.has(p)) continue;
|
|
21004
|
+
outLines.push(p);
|
|
21005
|
+
kept.add(p);
|
|
21006
|
+
}
|
|
21007
|
+
}
|
|
21008
|
+
|
|
21009
|
+
// server/routes/project-ignore.ts
|
|
21010
|
+
function registerProjectIgnoreRoute(app, deps) {
|
|
21011
|
+
app.get("/api/project-ignore", (c) => {
|
|
20929
21012
|
return c.json(buildEnvelope2(deps));
|
|
20930
21013
|
});
|
|
20931
|
-
app.patch("/api/project-
|
|
21014
|
+
app.patch("/api/project-ignore", async (c) => {
|
|
20932
21015
|
const body = await parsePatchBody3(c.req.raw);
|
|
20933
|
-
applyPatch2(deps, body);
|
|
21016
|
+
await applyPatch2(deps, body);
|
|
20934
21017
|
return c.json(buildEnvelope2(deps));
|
|
20935
21018
|
});
|
|
20936
21019
|
}
|
|
20937
21020
|
function buildEnvelope2(deps) {
|
|
21021
|
+
const cwd = deps.runtimeContext.cwd;
|
|
21022
|
+
return { patterns: readPatterns(cwd) };
|
|
21023
|
+
}
|
|
21024
|
+
async function applyPatch2(deps, body) {
|
|
21025
|
+
const cwd = deps.runtimeContext.cwd;
|
|
21026
|
+
const trimmed = [];
|
|
21027
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21028
|
+
for (const raw of body.patterns) {
|
|
21029
|
+
const t = raw.trim();
|
|
21030
|
+
if (t.length === 0) {
|
|
21031
|
+
throw new HTTPException10(400, {
|
|
21032
|
+
message: SERVER_TEXTS.projectIgnorePatternEmpty
|
|
21033
|
+
});
|
|
21034
|
+
}
|
|
21035
|
+
if (seen.has(t)) {
|
|
21036
|
+
throw new HTTPException10(400, {
|
|
21037
|
+
message: tx(SERVER_TEXTS.projectIgnorePatternDuplicate, { pattern: t })
|
|
21038
|
+
});
|
|
21039
|
+
}
|
|
21040
|
+
seen.add(t);
|
|
21041
|
+
trimmed.push(t);
|
|
21042
|
+
}
|
|
21043
|
+
const before = readPatterns(cwd);
|
|
21044
|
+
try {
|
|
21045
|
+
writePatterns(cwd, trimmed);
|
|
21046
|
+
} catch (err) {
|
|
21047
|
+
throw new HTTPException10(400, {
|
|
21048
|
+
message: tx(SERVER_TEXTS.projectIgnorePersistFailed, {
|
|
21049
|
+
message: formatErrorMessage(err)
|
|
21050
|
+
})
|
|
21051
|
+
});
|
|
21052
|
+
}
|
|
21053
|
+
logPatternChanges(before, trimmed);
|
|
21054
|
+
if (arrayChanged(before, trimmed)) await maybeRestartWatcher(deps);
|
|
21055
|
+
}
|
|
21056
|
+
function arrayChanged(before, next) {
|
|
21057
|
+
if (before.length !== next.length) return true;
|
|
21058
|
+
const beforeSet = new Set(before);
|
|
21059
|
+
for (const p of next) {
|
|
21060
|
+
if (!beforeSet.has(p)) return true;
|
|
21061
|
+
}
|
|
21062
|
+
return false;
|
|
21063
|
+
}
|
|
21064
|
+
function logPatternChanges(before, next) {
|
|
21065
|
+
const beforeSet = new Set(before);
|
|
21066
|
+
const nextSet = new Set(next);
|
|
21067
|
+
for (const p of next) {
|
|
21068
|
+
if (beforeSet.has(p)) continue;
|
|
21069
|
+
log.warn(
|
|
21070
|
+
tx(SERVER_TEXTS.projectIgnorePatternAdded, {
|
|
21071
|
+
pattern: sanitizeForTerminal(p)
|
|
21072
|
+
})
|
|
21073
|
+
);
|
|
21074
|
+
}
|
|
21075
|
+
for (const p of before) {
|
|
21076
|
+
if (nextSet.has(p)) continue;
|
|
21077
|
+
log.warn(
|
|
21078
|
+
tx(SERVER_TEXTS.projectIgnorePatternRemoved, {
|
|
21079
|
+
pattern: sanitizeForTerminal(p)
|
|
21080
|
+
})
|
|
21081
|
+
);
|
|
21082
|
+
}
|
|
21083
|
+
}
|
|
21084
|
+
async function maybeRestartWatcher(deps) {
|
|
21085
|
+
const watcher = deps.watcherHolder.current;
|
|
21086
|
+
if (!watcher) return;
|
|
21087
|
+
try {
|
|
21088
|
+
await watcher.restart();
|
|
21089
|
+
} catch (err) {
|
|
21090
|
+
log.warn(
|
|
21091
|
+
tx(SERVER_TEXTS.projectIgnoreWatcherRestartFailed, {
|
|
21092
|
+
message: formatErrorMessage(err)
|
|
21093
|
+
})
|
|
21094
|
+
);
|
|
21095
|
+
}
|
|
21096
|
+
}
|
|
21097
|
+
var PATCH_BODY_SCHEMA2 = {
|
|
21098
|
+
type: "object",
|
|
21099
|
+
additionalProperties: false,
|
|
21100
|
+
required: ["patterns"],
|
|
21101
|
+
properties: {
|
|
21102
|
+
patterns: {
|
|
21103
|
+
type: "array",
|
|
21104
|
+
items: {
|
|
21105
|
+
type: "string",
|
|
21106
|
+
pattern: "^[^\\n\\r\\x00-\\x1F\\x7F]+$"
|
|
21107
|
+
}
|
|
21108
|
+
}
|
|
21109
|
+
}
|
|
21110
|
+
};
|
|
21111
|
+
var parsePatchBody3 = makeBodyValidator(PATCH_BODY_SCHEMA2, {
|
|
21112
|
+
notJson: SERVER_TEXTS.projectIgnoreBodyNotJson,
|
|
21113
|
+
notObject: SERVER_TEXTS.projectIgnoreBodyNotObject,
|
|
21114
|
+
invalid: SERVER_TEXTS.projectIgnoreBodyEmpty,
|
|
21115
|
+
mapping: {
|
|
21116
|
+
"/patterns:required": SERVER_TEXTS.projectIgnoreBodyEmpty,
|
|
21117
|
+
"/patterns:type:array": SERVER_TEXTS.projectIgnoreListNotArray,
|
|
21118
|
+
"/patterns/*:type:string": SERVER_TEXTS.projectIgnoreEntryNotString,
|
|
21119
|
+
"/patterns/*:pattern": SERVER_TEXTS.projectIgnorePatternHasControlChar
|
|
21120
|
+
}
|
|
21121
|
+
});
|
|
21122
|
+
|
|
21123
|
+
// server/routes/project-preferences.ts
|
|
21124
|
+
import { statSync as statSync10 } from "fs";
|
|
21125
|
+
import { HTTPException as HTTPException11 } from "hono/http-exception";
|
|
21126
|
+
function registerProjectPreferencesRoute(app, deps) {
|
|
21127
|
+
app.get("/api/project-preferences", (c) => {
|
|
21128
|
+
return c.json(buildEnvelope3(deps));
|
|
21129
|
+
});
|
|
21130
|
+
app.patch("/api/project-preferences", async (c) => {
|
|
21131
|
+
const body = await parsePatchBody4(c.req.raw);
|
|
21132
|
+
await applyPatch3(deps, body);
|
|
21133
|
+
return c.json(buildEnvelope3(deps));
|
|
21134
|
+
});
|
|
21135
|
+
}
|
|
21136
|
+
function buildEnvelope3(deps) {
|
|
20938
21137
|
const cwd = deps.runtimeContext.cwd;
|
|
20939
21138
|
return {
|
|
20940
21139
|
scan: {
|
|
20941
|
-
extraFolders: readConfigValue("scan.extraFolders", {
|
|
20942
|
-
cwd,
|
|
20943
|
-
default: []
|
|
20944
|
-
}) ?? [],
|
|
20945
21140
|
referencePaths: readConfigValue("scan.referencePaths", {
|
|
20946
21141
|
cwd,
|
|
20947
21142
|
default: []
|
|
@@ -20949,46 +21144,138 @@ function buildEnvelope2(deps) {
|
|
|
20949
21144
|
}
|
|
20950
21145
|
};
|
|
20951
21146
|
}
|
|
20952
|
-
function
|
|
21147
|
+
async function applyPatch3(deps, body) {
|
|
20953
21148
|
const writes = collectWrites(body);
|
|
20954
21149
|
if (writes.length === 0) return;
|
|
20955
21150
|
const cwd = deps.runtimeContext.cwd;
|
|
21151
|
+
const missingPaths = collectMissingPaths(writes, cwd);
|
|
21152
|
+
if (missingPaths.length > 0) {
|
|
21153
|
+
throw new HTTPException11(400, {
|
|
21154
|
+
message: tx(SERVER_TEXTS.projectPrefsPathNotFound, {
|
|
21155
|
+
paths: missingPaths.join(", ")
|
|
21156
|
+
})
|
|
21157
|
+
});
|
|
21158
|
+
}
|
|
20956
21159
|
const exposures = writes.map((w) => projectPathExposure({ key: w.key, value: w.value, cwd })).filter((e) => e.expandsSurface);
|
|
20957
21160
|
if (exposures.length > 0 && body.confirm !== true) {
|
|
20958
21161
|
const exposed = exposures.flatMap((e) => e.exposedPaths);
|
|
20959
|
-
throw new
|
|
21162
|
+
throw new HTTPException11(412, {
|
|
20960
21163
|
message: tx(SERVER_TEXTS.projectPrefsConfirmRequired, {
|
|
20961
21164
|
paths: exposed.join(", ")
|
|
20962
21165
|
})
|
|
20963
21166
|
});
|
|
20964
21167
|
}
|
|
21168
|
+
let scanSurfaceMutated = false;
|
|
20965
21169
|
for (const w of writes) {
|
|
20966
|
-
|
|
20967
|
-
writeConfigValue(w.key, w.value, { target: "project-local", cwd });
|
|
20968
|
-
} catch (err) {
|
|
20969
|
-
const status = err instanceof ConfigValidationError ? 400 : 400;
|
|
20970
|
-
throw new HTTPException10(status, {
|
|
20971
|
-
message: tx(SERVER_TEXTS.projectPrefsPersistFailed, {
|
|
20972
|
-
key: w.key,
|
|
20973
|
-
message: formatErrorMessage(err)
|
|
20974
|
-
})
|
|
20975
|
-
});
|
|
20976
|
-
}
|
|
21170
|
+
if (runWrite(w, cwd)) scanSurfaceMutated = true;
|
|
20977
21171
|
}
|
|
21172
|
+
if (scanSurfaceMutated) await maybeRestartWatcher2(deps);
|
|
20978
21173
|
deps.configService.reload();
|
|
20979
21174
|
}
|
|
20980
21175
|
function collectWrites(body) {
|
|
20981
21176
|
if (!body.scan) return [];
|
|
20982
21177
|
const out = [];
|
|
20983
|
-
if (Array.isArray(body.scan.extraFolders)) {
|
|
20984
|
-
out.push({ key: "scan.extraFolders", value: body.scan.extraFolders });
|
|
20985
|
-
}
|
|
20986
21178
|
if (Array.isArray(body.scan.referencePaths)) {
|
|
20987
21179
|
out.push({ key: "scan.referencePaths", value: body.scan.referencePaths });
|
|
20988
21180
|
}
|
|
20989
21181
|
return out;
|
|
20990
21182
|
}
|
|
20991
|
-
|
|
21183
|
+
function collectMissingPaths(writes, cwd) {
|
|
21184
|
+
const missing = [];
|
|
21185
|
+
for (const w of writes) {
|
|
21186
|
+
if (!Array.isArray(w.value)) continue;
|
|
21187
|
+
const current = readConfigValue(w.key, { cwd, default: [] }) ?? [];
|
|
21188
|
+
const currentSet = new Set(current);
|
|
21189
|
+
for (const entry of w.value) {
|
|
21190
|
+
if (currentSet.has(entry)) continue;
|
|
21191
|
+
if (!isExistingDirectory(entry, cwd)) missing.push(entry);
|
|
21192
|
+
}
|
|
21193
|
+
}
|
|
21194
|
+
return missing;
|
|
21195
|
+
}
|
|
21196
|
+
async function maybeRestartWatcher2(deps) {
|
|
21197
|
+
const watcher = deps.watcherHolder.current;
|
|
21198
|
+
if (!watcher) return;
|
|
21199
|
+
try {
|
|
21200
|
+
await watcher.restart();
|
|
21201
|
+
} catch (err) {
|
|
21202
|
+
log.warn(
|
|
21203
|
+
tx(SERVER_TEXTS.projectPrefsWatcherRestartFailed, {
|
|
21204
|
+
message: formatErrorMessage(err)
|
|
21205
|
+
})
|
|
21206
|
+
);
|
|
21207
|
+
}
|
|
21208
|
+
}
|
|
21209
|
+
function runWrite(w, cwd) {
|
|
21210
|
+
const before = readConfigValue(w.key, { cwd, default: [] }) ?? [];
|
|
21211
|
+
try {
|
|
21212
|
+
writeConfigValue(w.key, w.value, { target: "project-local", cwd });
|
|
21213
|
+
} catch (err) {
|
|
21214
|
+
throw new HTTPException11(400, {
|
|
21215
|
+
message: tx(SERVER_TEXTS.projectPrefsPersistFailed, {
|
|
21216
|
+
key: w.key,
|
|
21217
|
+
message: formatErrorMessage(err)
|
|
21218
|
+
})
|
|
21219
|
+
});
|
|
21220
|
+
}
|
|
21221
|
+
logPathChanges(w.key, before, w.value, cwd);
|
|
21222
|
+
return arrayChanged2(before, w.value);
|
|
21223
|
+
}
|
|
21224
|
+
function arrayChanged2(before, nextValue) {
|
|
21225
|
+
if (!Array.isArray(nextValue)) return false;
|
|
21226
|
+
const next = nextValue;
|
|
21227
|
+
if (before.length !== next.length) return true;
|
|
21228
|
+
const beforeSet = new Set(before);
|
|
21229
|
+
for (const p of next) {
|
|
21230
|
+
if (!beforeSet.has(p)) return true;
|
|
21231
|
+
}
|
|
21232
|
+
return false;
|
|
21233
|
+
}
|
|
21234
|
+
function logPathChanges(key, before, nextValue, cwd) {
|
|
21235
|
+
if (!Array.isArray(nextValue)) return;
|
|
21236
|
+
const next = nextValue;
|
|
21237
|
+
const beforeSet = new Set(before);
|
|
21238
|
+
const nextSet = new Set(next);
|
|
21239
|
+
for (const path of next) {
|
|
21240
|
+
if (beforeSet.has(path)) continue;
|
|
21241
|
+
log.warn(
|
|
21242
|
+
tx(SERVER_TEXTS.projectPrefsPathAdded, {
|
|
21243
|
+
key,
|
|
21244
|
+
detail: formatPathDetail(path, cwd)
|
|
21245
|
+
})
|
|
21246
|
+
);
|
|
21247
|
+
}
|
|
21248
|
+
for (const path of before) {
|
|
21249
|
+
if (nextSet.has(path)) continue;
|
|
21250
|
+
log.warn(
|
|
21251
|
+
tx(SERVER_TEXTS.projectPrefsPathRemoved, {
|
|
21252
|
+
key,
|
|
21253
|
+
detail: formatPathDetail(path, cwd)
|
|
21254
|
+
})
|
|
21255
|
+
);
|
|
21256
|
+
}
|
|
21257
|
+
}
|
|
21258
|
+
function formatPathDetail(path, cwd) {
|
|
21259
|
+
const safePath = sanitizeForTerminal(path);
|
|
21260
|
+
if (path.startsWith("~/") || path === "~") {
|
|
21261
|
+
const abs2 = sanitizeForTerminal(resolveScanPath(path, cwd));
|
|
21262
|
+
return `${safePath} (home) \u2192 ${abs2}`;
|
|
21263
|
+
}
|
|
21264
|
+
if (path.startsWith("/")) {
|
|
21265
|
+
return `${safePath} (absolute)`;
|
|
21266
|
+
}
|
|
21267
|
+
const abs = sanitizeForTerminal(resolveScanPath(path, cwd));
|
|
21268
|
+
return `${safePath} (relative) \u2192 ${abs}`;
|
|
21269
|
+
}
|
|
21270
|
+
function isExistingDirectory(entry, cwd) {
|
|
21271
|
+
const abs = resolveScanPath(entry, cwd);
|
|
21272
|
+
try {
|
|
21273
|
+
return statSync10(abs).isDirectory();
|
|
21274
|
+
} catch {
|
|
21275
|
+
return false;
|
|
21276
|
+
}
|
|
21277
|
+
}
|
|
21278
|
+
var PATCH_BODY_SCHEMA3 = {
|
|
20992
21279
|
type: "object",
|
|
20993
21280
|
additionalProperties: false,
|
|
20994
21281
|
required: ["scan"],
|
|
@@ -20999,13 +21286,15 @@ var PATCH_BODY_SCHEMA2 = {
|
|
|
20999
21286
|
additionalProperties: false,
|
|
21000
21287
|
minProperties: 1,
|
|
21001
21288
|
properties: {
|
|
21002
|
-
|
|
21003
|
-
|
|
21289
|
+
referencePaths: {
|
|
21290
|
+
type: "array",
|
|
21291
|
+
items: { type: "string", pattern: "^[^,]+$" }
|
|
21292
|
+
}
|
|
21004
21293
|
}
|
|
21005
21294
|
}
|
|
21006
21295
|
}
|
|
21007
21296
|
};
|
|
21008
|
-
var
|
|
21297
|
+
var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
|
|
21009
21298
|
notJson: SERVER_TEXTS.projectPrefsBodyNotJson,
|
|
21010
21299
|
notObject: SERVER_TEXTS.projectPrefsBodyNotObject,
|
|
21011
21300
|
invalid: SERVER_TEXTS.projectPrefsBodyEmpty,
|
|
@@ -21014,15 +21303,14 @@ var parsePatchBody3 = makeBodyValidator(PATCH_BODY_SCHEMA2, {
|
|
|
21014
21303
|
"/scan:minProperties": SERVER_TEXTS.projectPrefsBodyEmpty,
|
|
21015
21304
|
"/scan:type:object": SERVER_TEXTS.projectPrefsScanNotObject,
|
|
21016
21305
|
"/confirm:type:boolean": SERVER_TEXTS.projectPrefsConfirmNotBoolean,
|
|
21017
|
-
"/scan/extraFolders:type:array": tx(SERVER_TEXTS.projectPrefsListNotArray, { key: "scan.extraFolders" }),
|
|
21018
21306
|
"/scan/referencePaths:type:array": tx(SERVER_TEXTS.projectPrefsListNotArray, { key: "scan.referencePaths" }),
|
|
21019
|
-
"/scan/
|
|
21020
|
-
"/scan/referencePaths/*:
|
|
21307
|
+
"/scan/referencePaths/*:type:string": tx(SERVER_TEXTS.projectPrefsListEntryNotString, { key: "scan.referencePaths" }),
|
|
21308
|
+
"/scan/referencePaths/*:pattern": SERVER_TEXTS.projectPrefsEntryHasComma
|
|
21021
21309
|
}
|
|
21022
21310
|
});
|
|
21023
21311
|
|
|
21024
21312
|
// server/routes/scan.ts
|
|
21025
|
-
import { HTTPException as
|
|
21313
|
+
import { HTTPException as HTTPException12 } from "hono/http-exception";
|
|
21026
21314
|
|
|
21027
21315
|
// server/scan-mutex.ts
|
|
21028
21316
|
var inFlight = null;
|
|
@@ -21030,14 +21318,14 @@ async function withScanMutex(fn) {
|
|
|
21030
21318
|
if (inFlight !== null) {
|
|
21031
21319
|
throw new ScanBusyError();
|
|
21032
21320
|
}
|
|
21033
|
-
let
|
|
21321
|
+
let resolve38;
|
|
21034
21322
|
inFlight = new Promise((r) => {
|
|
21035
|
-
|
|
21323
|
+
resolve38 = r;
|
|
21036
21324
|
});
|
|
21037
21325
|
try {
|
|
21038
21326
|
return await fn();
|
|
21039
21327
|
} finally {
|
|
21040
|
-
|
|
21328
|
+
resolve38();
|
|
21041
21329
|
inFlight = null;
|
|
21042
21330
|
}
|
|
21043
21331
|
}
|
|
@@ -21077,61 +21365,80 @@ function buildWatcherErrorEvent(data) {
|
|
|
21077
21365
|
}
|
|
21078
21366
|
|
|
21079
21367
|
// server/watcher.ts
|
|
21080
|
-
var WATCH_ROOT = ".";
|
|
21081
21368
|
function createWatcherService(opts) {
|
|
21082
|
-
|
|
21083
|
-
|
|
21084
|
-
|
|
21085
|
-
|
|
21086
|
-
|
|
21087
|
-
|
|
21088
|
-
|
|
21089
|
-
|
|
21090
|
-
|
|
21091
|
-
|
|
21092
|
-
|
|
21093
|
-
|
|
21094
|
-
|
|
21095
|
-
|
|
21096
|
-
|
|
21369
|
+
let currentRuntime = null;
|
|
21370
|
+
const buildRuntimeOpts = () => {
|
|
21371
|
+
const runtimeOpts = {
|
|
21372
|
+
dbPath: opts.options.dbPath,
|
|
21373
|
+
roots: ["."],
|
|
21374
|
+
runtimeContext: opts.runtimeContext,
|
|
21375
|
+
noBuiltIns: opts.options.noBuiltIns,
|
|
21376
|
+
noPlugins: opts.options.noPlugins,
|
|
21377
|
+
emitterFactory: () => buildBroadcasterEmitter(opts.broadcaster),
|
|
21378
|
+
runInitialBatch: true,
|
|
21379
|
+
// BFF ordering: subscribe first so edits arriving during the
|
|
21380
|
+
// initial scan queue against the armed chokidar and fire a
|
|
21381
|
+
// follow-up batch.
|
|
21382
|
+
subscribeBeforeInitial: true,
|
|
21383
|
+
failOnInitialBatchError: false,
|
|
21384
|
+
events: {
|
|
21385
|
+
onBatch: (outcome) => {
|
|
21386
|
+
if (outcome.kind === "error") {
|
|
21387
|
+
log.warn(
|
|
21388
|
+
tx(SERVER_TEXTS.watcherBatchFailed, {
|
|
21389
|
+
message: sanitizeForTerminal(outcome.message)
|
|
21390
|
+
})
|
|
21391
|
+
);
|
|
21392
|
+
}
|
|
21393
|
+
},
|
|
21394
|
+
onWatcherError: (message) => {
|
|
21097
21395
|
log.warn(
|
|
21098
|
-
tx(SERVER_TEXTS.
|
|
21099
|
-
message: sanitizeForTerminal(
|
|
21396
|
+
tx(SERVER_TEXTS.watcherError, {
|
|
21397
|
+
message: sanitizeForTerminal(message)
|
|
21398
|
+
})
|
|
21399
|
+
);
|
|
21400
|
+
opts.broadcaster.broadcast(buildWatcherErrorEvent({ message }));
|
|
21401
|
+
},
|
|
21402
|
+
onPluginWarning: (message) => {
|
|
21403
|
+
log.warn(sanitizeForTerminal(message));
|
|
21404
|
+
},
|
|
21405
|
+
onReady: (info) => {
|
|
21406
|
+
opts.broadcaster.broadcast(
|
|
21407
|
+
buildWatcherStartedEvent({ roots: info.roots, debounceMs: info.debounceMs })
|
|
21408
|
+
);
|
|
21409
|
+
log.info(
|
|
21410
|
+
tx(SERVER_TEXTS.watcherReady, {
|
|
21411
|
+
roots: info.roots.join(","),
|
|
21412
|
+
debounceMs: String(info.debounceMs)
|
|
21100
21413
|
})
|
|
21101
21414
|
);
|
|
21102
21415
|
}
|
|
21103
|
-
},
|
|
21104
|
-
onWatcherError: (message) => {
|
|
21105
|
-
log.warn(
|
|
21106
|
-
tx(SERVER_TEXTS.watcherError, {
|
|
21107
|
-
message: sanitizeForTerminal(message)
|
|
21108
|
-
})
|
|
21109
|
-
);
|
|
21110
|
-
opts.broadcaster.broadcast(buildWatcherErrorEvent({ message }));
|
|
21111
|
-
},
|
|
21112
|
-
onPluginWarning: (message) => {
|
|
21113
|
-
log.warn(sanitizeForTerminal(message));
|
|
21114
|
-
},
|
|
21115
|
-
onReady: (info) => {
|
|
21116
|
-
opts.broadcaster.broadcast(
|
|
21117
|
-
buildWatcherStartedEvent({ roots: info.roots, debounceMs: info.debounceMs })
|
|
21118
|
-
);
|
|
21119
|
-
log.info(
|
|
21120
|
-
tx(SERVER_TEXTS.watcherReady, {
|
|
21121
|
-
roots: info.roots.join(","),
|
|
21122
|
-
debounceMs: String(info.debounceMs)
|
|
21123
|
-
})
|
|
21124
|
-
);
|
|
21125
21416
|
}
|
|
21417
|
+
};
|
|
21418
|
+
if (opts.debounceMsOverride !== void 0) {
|
|
21419
|
+
runtimeOpts.debounceMsOverride = opts.debounceMsOverride;
|
|
21126
21420
|
}
|
|
21421
|
+
return runtimeOpts;
|
|
21127
21422
|
};
|
|
21128
|
-
if (opts.debounceMsOverride !== void 0) {
|
|
21129
|
-
runtimeOpts.debounceMsOverride = opts.debounceMsOverride;
|
|
21130
|
-
}
|
|
21131
|
-
const handle = createWatcherRuntime(runtimeOpts);
|
|
21132
21423
|
return {
|
|
21133
|
-
start
|
|
21134
|
-
|
|
21424
|
+
async start() {
|
|
21425
|
+
currentRuntime = createWatcherRuntime(buildRuntimeOpts());
|
|
21426
|
+
await currentRuntime.start();
|
|
21427
|
+
},
|
|
21428
|
+
async stop() {
|
|
21429
|
+
if (currentRuntime) {
|
|
21430
|
+
await currentRuntime.stop();
|
|
21431
|
+
currentRuntime = null;
|
|
21432
|
+
}
|
|
21433
|
+
},
|
|
21434
|
+
async restart() {
|
|
21435
|
+
if (currentRuntime) {
|
|
21436
|
+
await currentRuntime.stop();
|
|
21437
|
+
currentRuntime = null;
|
|
21438
|
+
}
|
|
21439
|
+
currentRuntime = createWatcherRuntime(buildRuntimeOpts());
|
|
21440
|
+
await currentRuntime.start();
|
|
21441
|
+
}
|
|
21135
21442
|
};
|
|
21136
21443
|
}
|
|
21137
21444
|
function buildBroadcasterEmitter(broadcaster) {
|
|
@@ -21158,7 +21465,7 @@ function registerScanRoute(app, deps) {
|
|
|
21158
21465
|
}
|
|
21159
21466
|
async function runPersistedScan(c, deps) {
|
|
21160
21467
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
21161
|
-
throw new
|
|
21468
|
+
throw new HTTPException12(400, { message: SERVER_TEXTS.scanPostRequiresFullPipeline });
|
|
21162
21469
|
}
|
|
21163
21470
|
const dbExists = await tryWithSqlite(
|
|
21164
21471
|
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
@@ -21187,7 +21494,7 @@ async function runPersistedScan(c, deps) {
|
|
|
21187
21494
|
emitterFactory: () => buildBroadcasterEmitter(deps.broadcaster)
|
|
21188
21495
|
});
|
|
21189
21496
|
if (outcome.kind !== "ok") {
|
|
21190
|
-
throw new
|
|
21497
|
+
throw new HTTPException12(500, {
|
|
21191
21498
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.scanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
21192
21499
|
});
|
|
21193
21500
|
}
|
|
@@ -21195,7 +21502,7 @@ async function runPersistedScan(c, deps) {
|
|
|
21195
21502
|
});
|
|
21196
21503
|
} catch (err) {
|
|
21197
21504
|
if (err instanceof ScanBusyError) {
|
|
21198
|
-
throw new
|
|
21505
|
+
throw new HTTPException12(409, { message: SERVER_TEXTS.scanPostBusy });
|
|
21199
21506
|
}
|
|
21200
21507
|
throw err;
|
|
21201
21508
|
}
|
|
@@ -21259,7 +21566,7 @@ function groupTagsBySource2(rows) {
|
|
|
21259
21566
|
}
|
|
21260
21567
|
async function runFreshScan(deps) {
|
|
21261
21568
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
21262
|
-
throw new
|
|
21569
|
+
throw new HTTPException12(400, { message: SERVER_TEXTS.freshScanRequiresPipeline });
|
|
21263
21570
|
}
|
|
21264
21571
|
const resolveEnabledOverride = await buildBffResolverOverride(deps);
|
|
21265
21572
|
const outcome = await runScanForCommand({
|
|
@@ -21288,7 +21595,7 @@ async function runFreshScan(deps) {
|
|
|
21288
21595
|
printer: bffScanRunnerPrinter
|
|
21289
21596
|
});
|
|
21290
21597
|
if (outcome.kind !== "ok") {
|
|
21291
|
-
throw new
|
|
21598
|
+
throw new HTTPException12(500, {
|
|
21292
21599
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.freshScanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
21293
21600
|
});
|
|
21294
21601
|
}
|
|
@@ -21322,8 +21629,8 @@ function emptyScanResult() {
|
|
|
21322
21629
|
}
|
|
21323
21630
|
|
|
21324
21631
|
// server/routes/sidecar.ts
|
|
21325
|
-
import { HTTPException as
|
|
21326
|
-
import { resolve as
|
|
21632
|
+
import { HTTPException as HTTPException13 } from "hono/http-exception";
|
|
21633
|
+
import { resolve as resolve34 } from "path";
|
|
21327
21634
|
var STATUS_FRESH = "fresh";
|
|
21328
21635
|
var ENVELOPE_KIND2 = "sidecar.bumped";
|
|
21329
21636
|
var BUMP_BODY_SCHEMA = {
|
|
@@ -21357,13 +21664,13 @@ function registerSidecarRoutes(app, deps) {
|
|
|
21357
21664
|
let absPath;
|
|
21358
21665
|
try {
|
|
21359
21666
|
assertContained(deps.runtimeContext.cwd, node.path);
|
|
21360
|
-
absPath =
|
|
21667
|
+
absPath = resolve34(deps.runtimeContext.cwd, node.path);
|
|
21361
21668
|
} catch (err) {
|
|
21362
|
-
throw new
|
|
21669
|
+
throw new HTTPException13(500, { message: formatErrorMessage(err) });
|
|
21363
21670
|
}
|
|
21364
21671
|
const result = invokeBump2(node, absPath, body);
|
|
21365
21672
|
if (result.report.ok === false && result.report.reason === "fresh") {
|
|
21366
|
-
throw new
|
|
21673
|
+
throw new HTTPException13(409, { message: SERVER_TEXTS.sidecarFreshRefusal });
|
|
21367
21674
|
}
|
|
21368
21675
|
if (result.report.ok === true && result.report.noop === true) {
|
|
21369
21676
|
const envelope2 = {
|
|
@@ -21390,7 +21697,7 @@ function registerSidecarRoutes(app, deps) {
|
|
|
21390
21697
|
}
|
|
21391
21698
|
} catch (err) {
|
|
21392
21699
|
if (err instanceof EConsentRequiredError) throw err;
|
|
21393
|
-
throw new
|
|
21700
|
+
throw new HTTPException13(500, { message: formatErrorMessage(err) });
|
|
21394
21701
|
}
|
|
21395
21702
|
if (body.confirm === true) {
|
|
21396
21703
|
deps.configService.reload();
|
|
@@ -21427,7 +21734,7 @@ async function loadNode(deps, nodePath) {
|
|
|
21427
21734
|
);
|
|
21428
21735
|
const node = persisted?.nodes.find((n) => n.path === nodePath);
|
|
21429
21736
|
if (!node) {
|
|
21430
|
-
throw new
|
|
21737
|
+
throw new HTTPException13(404, {
|
|
21431
21738
|
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
21432
21739
|
});
|
|
21433
21740
|
}
|
|
@@ -21435,7 +21742,7 @@ async function loadNode(deps, nodePath) {
|
|
|
21435
21742
|
}
|
|
21436
21743
|
function invokeBump2(node, absPath, body) {
|
|
21437
21744
|
if (!bumpAction.invoke) {
|
|
21438
|
-
throw new
|
|
21745
|
+
throw new HTTPException13(500, { message: SERVER_TEXTS.sidecarBumpInvokeMissing });
|
|
21439
21746
|
}
|
|
21440
21747
|
const input = {};
|
|
21441
21748
|
if (body.force === true) input.force = true;
|
|
@@ -21481,7 +21788,7 @@ function registerUpdateStatusRoute(app, deps) {
|
|
|
21481
21788
|
}
|
|
21482
21789
|
|
|
21483
21790
|
// server/static.ts
|
|
21484
|
-
import { existsSync as
|
|
21791
|
+
import { existsSync as existsSync26 } from "fs";
|
|
21485
21792
|
import { readFile as readFile5 } from "fs/promises";
|
|
21486
21793
|
import { extname, join as join17 } from "path";
|
|
21487
21794
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
@@ -21536,7 +21843,7 @@ function createSpaFallback(opts) {
|
|
|
21536
21843
|
if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
|
|
21537
21844
|
if (opts.uiDist === null) return htmlResponse(c, placeholder);
|
|
21538
21845
|
const indexPath = join17(opts.uiDist, INDEX_HTML);
|
|
21539
|
-
if (!
|
|
21846
|
+
if (!existsSync26(indexPath)) return htmlResponse(c, placeholder);
|
|
21540
21847
|
return fileResponse(c, indexPath);
|
|
21541
21848
|
};
|
|
21542
21849
|
}
|
|
@@ -21622,13 +21929,13 @@ function attachBroadcasterRoute(app, broadcaster) {
|
|
|
21622
21929
|
|
|
21623
21930
|
// server/app.ts
|
|
21624
21931
|
var BODY_LIMIT_BYTES = 1024 * 1024;
|
|
21625
|
-
var DbMissingError = class extends
|
|
21932
|
+
var DbMissingError = class extends HTTPException14 {
|
|
21626
21933
|
constructor(message) {
|
|
21627
21934
|
super(500, { message });
|
|
21628
21935
|
this.name = "DbMissingError";
|
|
21629
21936
|
}
|
|
21630
21937
|
};
|
|
21631
|
-
var BulkValidationError = class extends
|
|
21938
|
+
var BulkValidationError = class extends HTTPException14 {
|
|
21632
21939
|
id;
|
|
21633
21940
|
code;
|
|
21634
21941
|
constructor(init) {
|
|
@@ -21638,7 +21945,7 @@ var BulkValidationError = class extends HTTPException13 {
|
|
|
21638
21945
|
this.code = init.code;
|
|
21639
21946
|
}
|
|
21640
21947
|
};
|
|
21641
|
-
var LoopbackGateError = class extends
|
|
21948
|
+
var LoopbackGateError = class extends HTTPException14 {
|
|
21642
21949
|
code;
|
|
21643
21950
|
constructor(init) {
|
|
21644
21951
|
super(403, { message: init.message });
|
|
@@ -21658,7 +21965,7 @@ function createApp(deps) {
|
|
|
21658
21965
|
bodyLimit({
|
|
21659
21966
|
maxSize: BODY_LIMIT_BYTES,
|
|
21660
21967
|
onError: () => {
|
|
21661
|
-
throw new
|
|
21968
|
+
throw new HTTPException14(413, { message: tx(SERVER_TEXTS.bodyTooLarge, { maxBytes: String(BODY_LIMIT_BYTES) }) });
|
|
21662
21969
|
}
|
|
21663
21970
|
})
|
|
21664
21971
|
);
|
|
@@ -21682,7 +21989,8 @@ function createApp(deps) {
|
|
|
21682
21989
|
kindRegistry: deps.kindRegistry,
|
|
21683
21990
|
contributionsRegistry: deps.contributionsRegistry,
|
|
21684
21991
|
pluginRuntime: deps.pluginRuntime,
|
|
21685
|
-
configService
|
|
21992
|
+
configService,
|
|
21993
|
+
watcherHolder: deps.watcherHolder
|
|
21686
21994
|
};
|
|
21687
21995
|
registerScanRoute(app, { ...routeDeps, broadcaster: deps.broadcaster });
|
|
21688
21996
|
registerNodesRoutes(app, routeDeps);
|
|
@@ -21698,8 +22006,9 @@ function createApp(deps) {
|
|
|
21698
22006
|
registerUpdateStatusRoute(app, routeDeps);
|
|
21699
22007
|
registerPreferencesRoute(app, routeDeps);
|
|
21700
22008
|
registerProjectPreferencesRoute(app, routeDeps);
|
|
22009
|
+
registerProjectIgnoreRoute(app, routeDeps);
|
|
21701
22010
|
app.all("/api/*", (c) => {
|
|
21702
|
-
throw new
|
|
22011
|
+
throw new HTTPException14(404, {
|
|
21703
22012
|
message: tx(SERVER_TEXTS.unknownApiEndpoint, { path: sanitizeForTerminal(c.req.path) })
|
|
21704
22013
|
});
|
|
21705
22014
|
});
|
|
@@ -21707,7 +22016,7 @@ function createApp(deps) {
|
|
|
21707
22016
|
app.use("*", createStaticHandler({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
21708
22017
|
app.get("*", createSpaFallback({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
21709
22018
|
app.notFound((c) => {
|
|
21710
|
-
throw new
|
|
22019
|
+
throw new HTTPException14(404, {
|
|
21711
22020
|
message: tx(SERVER_TEXTS.unknownPath, { path: sanitizeForTerminal(c.req.path) })
|
|
21712
22021
|
});
|
|
21713
22022
|
});
|
|
@@ -21762,7 +22071,7 @@ function formatError2(err, c) {
|
|
|
21762
22071
|
};
|
|
21763
22072
|
return c.json(envelope, 403);
|
|
21764
22073
|
}
|
|
21765
|
-
if (err instanceof
|
|
22074
|
+
if (err instanceof HTTPException14) {
|
|
21766
22075
|
const status = err.status;
|
|
21767
22076
|
const envelope = {
|
|
21768
22077
|
ok: false,
|
|
@@ -22105,8 +22414,8 @@ function validateNoUi(noUi, uiDist) {
|
|
|
22105
22414
|
}
|
|
22106
22415
|
|
|
22107
22416
|
// server/paths.ts
|
|
22108
|
-
import { existsSync as
|
|
22109
|
-
import { dirname as dirname18, isAbsolute as isAbsolute9, join as join18, resolve as
|
|
22417
|
+
import { existsSync as existsSync27, statSync as statSync11 } from "fs";
|
|
22418
|
+
import { dirname as dirname18, isAbsolute as isAbsolute9, join as join18, resolve as resolve35 } from "path";
|
|
22110
22419
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
22111
22420
|
var DEFAULT_UI_REL = join18("ui", "dist", "ui", "browser");
|
|
22112
22421
|
var PACKAGE_UI_REL = "ui";
|
|
@@ -22117,13 +22426,13 @@ function resolveDefaultUiDist(ctx) {
|
|
|
22117
22426
|
return walkUpForUi(ctx.cwd);
|
|
22118
22427
|
}
|
|
22119
22428
|
function resolveExplicitUiDist(ctx, raw) {
|
|
22120
|
-
return isAbsolute9(raw) ? raw :
|
|
22429
|
+
return isAbsolute9(raw) ? raw : resolve35(ctx.cwd, raw);
|
|
22121
22430
|
}
|
|
22122
22431
|
function isUiBundleDir(path) {
|
|
22123
|
-
if (!
|
|
22432
|
+
if (!existsSync27(path)) return false;
|
|
22124
22433
|
try {
|
|
22125
|
-
if (!
|
|
22126
|
-
return
|
|
22434
|
+
if (!statSync11(path).isDirectory()) return false;
|
|
22435
|
+
return existsSync27(join18(path, INDEX_HTML2));
|
|
22127
22436
|
} catch {
|
|
22128
22437
|
return false;
|
|
22129
22438
|
}
|
|
@@ -22151,7 +22460,7 @@ function resolvePackageBundledUiFrom(here) {
|
|
|
22151
22460
|
return null;
|
|
22152
22461
|
}
|
|
22153
22462
|
function walkUpForUi(startDir) {
|
|
22154
|
-
let current =
|
|
22463
|
+
let current = resolve35(startDir);
|
|
22155
22464
|
for (let i = 0; i < 64; i++) {
|
|
22156
22465
|
const candidate = join18(current, DEFAULT_UI_REL);
|
|
22157
22466
|
if (isUiBundleDir(candidate)) return candidate;
|
|
@@ -22169,6 +22478,7 @@ async function createServer(options, extra = {}) {
|
|
|
22169
22478
|
const broadcaster = new WsBroadcaster();
|
|
22170
22479
|
const { pluginRuntime, kindRegistry } = await assemblePluginRuntime(options, runtimeContext);
|
|
22171
22480
|
const { kernel, contributionsRegistry } = assembleKernel(pluginRuntime, options.noBuiltIns);
|
|
22481
|
+
const watcherHolder = { current: null };
|
|
22172
22482
|
const app = createApp({
|
|
22173
22483
|
options,
|
|
22174
22484
|
specVersion,
|
|
@@ -22177,6 +22487,7 @@ async function createServer(options, extra = {}) {
|
|
|
22177
22487
|
kindRegistry,
|
|
22178
22488
|
contributionsRegistry,
|
|
22179
22489
|
pluginRuntime,
|
|
22490
|
+
watcherHolder,
|
|
22180
22491
|
kernel
|
|
22181
22492
|
});
|
|
22182
22493
|
const wss = new WebSocketServer({ noServer: true });
|
|
@@ -22196,6 +22507,7 @@ async function createServer(options, extra = {}) {
|
|
|
22196
22507
|
try {
|
|
22197
22508
|
await candidate.start();
|
|
22198
22509
|
watcherService = candidate;
|
|
22510
|
+
watcherHolder.current = candidate;
|
|
22199
22511
|
} catch (err) {
|
|
22200
22512
|
const message = formatErrorMessage(err);
|
|
22201
22513
|
log.warn(
|
|
@@ -22390,7 +22702,8 @@ function renderBanner(input) {
|
|
|
22390
22702
|
dbDisplay,
|
|
22391
22703
|
pathDisplay: formatCwdPath(input.cwd),
|
|
22392
22704
|
browserLine,
|
|
22393
|
-
colorEnabled: input.colorEnabled
|
|
22705
|
+
colorEnabled: input.colorEnabled,
|
|
22706
|
+
referencePaths: input.referencePaths ?? []
|
|
22394
22707
|
});
|
|
22395
22708
|
}
|
|
22396
22709
|
function resolveColorEnabled(opts) {
|
|
@@ -22447,11 +22760,23 @@ function renderFiglet(input) {
|
|
|
22447
22760
|
lines.push(` ${dimOpen}Server${dimClose} ${greenUnderline}${input.url}${greenUnderlineClose}`);
|
|
22448
22761
|
lines.push(` ${dimOpen}Path${dimClose} ${input.pathDisplay}`);
|
|
22449
22762
|
lines.push(` ${dimOpen}DB${dimClose} ${input.dbDisplay}`);
|
|
22763
|
+
lines.push(...renderListRows("Refs", input.referencePaths, dimOpen, dimClose));
|
|
22450
22764
|
lines.push("");
|
|
22451
22765
|
lines.push(` ${dimOpen}${input.browserLine}${dimClose}`);
|
|
22452
22766
|
lines.push("");
|
|
22453
22767
|
return lines.join("\n") + "\n";
|
|
22454
22768
|
}
|
|
22769
|
+
function renderListRows(label, values, dimOpen, dimClose) {
|
|
22770
|
+
if (values.length === 0) return [];
|
|
22771
|
+
const out = [];
|
|
22772
|
+
const labelPad = " ".repeat(Math.max(1, 9 - label.length));
|
|
22773
|
+
const continuationPad = " ".repeat(11);
|
|
22774
|
+
out.push(` ${dimOpen}${label}${dimClose}${labelPad}${sanitizeForTerminal(values[0])}`);
|
|
22775
|
+
for (let i = 1; i < values.length; i += 1) {
|
|
22776
|
+
out.push(`${continuationPad}${sanitizeForTerminal(values[i])}`);
|
|
22777
|
+
}
|
|
22778
|
+
return out;
|
|
22779
|
+
}
|
|
22455
22780
|
var EMPTY_ANSI = {
|
|
22456
22781
|
dimOpen: "",
|
|
22457
22782
|
dimClose: "",
|
|
@@ -22571,7 +22896,7 @@ var ServeCommand = class extends SmCommand {
|
|
|
22571
22896
|
return ExitCode.Error;
|
|
22572
22897
|
}
|
|
22573
22898
|
const dbPath = resolveDbPath({ db: this.db, ...runtimeCtx });
|
|
22574
|
-
if (this.db !== void 0 && !
|
|
22899
|
+
if (this.db !== void 0 && !existsSync28(dbPath)) {
|
|
22575
22900
|
this.printer.info(
|
|
22576
22901
|
tx(SERVE_TEXTS.dbNotFound, { path: sanitizeForTerminal(dbPath) })
|
|
22577
22902
|
);
|
|
@@ -22647,6 +22972,12 @@ var ServeCommand = class extends SmCommand {
|
|
|
22647
22972
|
noColorFlag: this.noColor,
|
|
22648
22973
|
env: process.env
|
|
22649
22974
|
});
|
|
22975
|
+
let referencePaths = [];
|
|
22976
|
+
try {
|
|
22977
|
+
const cfg = loadConfig({ cwd: runtimeCtx.cwd }).effective;
|
|
22978
|
+
referencePaths = cfg.scan.referencePaths;
|
|
22979
|
+
} catch {
|
|
22980
|
+
}
|
|
22650
22981
|
this.printer.info(
|
|
22651
22982
|
renderBanner({
|
|
22652
22983
|
version: VERSION,
|
|
@@ -22656,7 +22987,8 @@ var ServeCommand = class extends SmCommand {
|
|
|
22656
22987
|
cwd: runtimeCtx.cwd,
|
|
22657
22988
|
openBrowser: validation.options.open,
|
|
22658
22989
|
isTTY,
|
|
22659
|
-
colorEnabled
|
|
22990
|
+
colorEnabled,
|
|
22991
|
+
referencePaths
|
|
22660
22992
|
})
|
|
22661
22993
|
);
|
|
22662
22994
|
if (validation.options.open) {
|
|
@@ -23067,8 +23399,8 @@ function rankConfidenceForGrouping(c) {
|
|
|
23067
23399
|
}
|
|
23068
23400
|
|
|
23069
23401
|
// cli/commands/sidecar.ts
|
|
23070
|
-
import { existsSync as
|
|
23071
|
-
import { resolve as
|
|
23402
|
+
import { existsSync as existsSync29, unlinkSync as unlinkSync2 } from "fs";
|
|
23403
|
+
import { resolve as resolve36 } from "path";
|
|
23072
23404
|
import { Command as Command35, Option as Option33 } from "clipanion";
|
|
23073
23405
|
|
|
23074
23406
|
// cli/i18n/sidecar.texts.ts
|
|
@@ -23219,7 +23551,7 @@ var SidecarRefreshCommand = class extends SmCommand {
|
|
|
23219
23551
|
let absPath;
|
|
23220
23552
|
try {
|
|
23221
23553
|
assertContained(ctx.cwd, node.path);
|
|
23222
|
-
absPath =
|
|
23554
|
+
absPath = resolve36(ctx.cwd, node.path);
|
|
23223
23555
|
} catch (err) {
|
|
23224
23556
|
this.printer.error(
|
|
23225
23557
|
tx(SIDECAR_TEXTS.refreshFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
|
|
@@ -23500,7 +23832,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
23500
23832
|
let absPath;
|
|
23501
23833
|
try {
|
|
23502
23834
|
assertContained(ctx.cwd, node.path);
|
|
23503
|
-
absPath =
|
|
23835
|
+
absPath = resolve36(ctx.cwd, node.path);
|
|
23504
23836
|
} catch (err) {
|
|
23505
23837
|
this.printer.error(
|
|
23506
23838
|
tx(SIDECAR_TEXTS.annotateFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
|
|
@@ -23508,7 +23840,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
23508
23840
|
return ExitCode.Error;
|
|
23509
23841
|
}
|
|
23510
23842
|
const sidecarAbsPath = sidecarPathFor(absPath);
|
|
23511
|
-
if (
|
|
23843
|
+
if (existsSync29(sidecarAbsPath) && this.force !== true) {
|
|
23512
23844
|
this.printer.error(
|
|
23513
23845
|
tx(SIDECAR_TEXTS.annotateExists, {
|
|
23514
23846
|
glyph: errGlyph,
|
|
@@ -23518,7 +23850,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
23518
23850
|
);
|
|
23519
23851
|
return ExitCode.Error;
|
|
23520
23852
|
}
|
|
23521
|
-
if (
|
|
23853
|
+
if (existsSync29(sidecarAbsPath) && this.force === true) {
|
|
23522
23854
|
try {
|
|
23523
23855
|
unlinkSync2(sidecarAbsPath);
|
|
23524
23856
|
} catch (err) {
|
|
@@ -23748,8 +24080,8 @@ var STUB_COMMANDS = [
|
|
|
23748
24080
|
];
|
|
23749
24081
|
|
|
23750
24082
|
// cli/commands/tutorial.ts
|
|
23751
|
-
import { cpSync as cpSync2, existsSync as
|
|
23752
|
-
import { dirname as dirname19, join as join19, resolve as
|
|
24083
|
+
import { cpSync as cpSync2, existsSync as existsSync30, mkdirSync as mkdirSync7, rmSync as rmSync2, statSync as statSync12 } from "fs";
|
|
24084
|
+
import { dirname as dirname19, join as join19, resolve as resolve37 } from "path";
|
|
23753
24085
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
23754
24086
|
import { Command as Command37, Option as Option35 } from "clipanion";
|
|
23755
24087
|
|
|
@@ -23847,7 +24179,7 @@ var TutorialCommand = class extends SmCommand {
|
|
|
23847
24179
|
const spec = VARIANT_SPECS[variant];
|
|
23848
24180
|
const targetDir = join19(ctx.cwd, ".claude", "skills", spec.slug);
|
|
23849
24181
|
const targetDisplay = `.claude/skills/${spec.slug}/`;
|
|
23850
|
-
if (
|
|
24182
|
+
if (existsSync30(targetDir) && !this.force) {
|
|
23851
24183
|
this.printer.error(
|
|
23852
24184
|
tx(TUTORIAL_TEXTS.alreadyExists, {
|
|
23853
24185
|
glyph: errGlyph,
|
|
@@ -23923,14 +24255,14 @@ function resolveSkillSourceDir(variant) {
|
|
|
23923
24255
|
const here = dirname19(fileURLToPath6(import.meta.url));
|
|
23924
24256
|
const candidates = [
|
|
23925
24257
|
// dev: src/cli/commands/ → repo-root .claude/skills/<slug>/
|
|
23926
|
-
|
|
24258
|
+
resolve37(here, "../../..", spec.sourceDir),
|
|
23927
24259
|
// bundled: dist/cli.js → dist/cli/tutorial/<slug> (sibling)
|
|
23928
|
-
|
|
24260
|
+
resolve37(here, "cli/tutorial", spec.slug),
|
|
23929
24261
|
// bundled fallback: any-depth → cli/tutorial/<slug>
|
|
23930
|
-
|
|
24262
|
+
resolve37(here, "../cli/tutorial", spec.slug)
|
|
23931
24263
|
];
|
|
23932
24264
|
for (const candidate of candidates) {
|
|
23933
|
-
if (
|
|
24265
|
+
if (existsSync30(candidate) && statSync12(candidate).isDirectory()) {
|
|
23934
24266
|
cachedSourceDirs.set(variant, candidate);
|
|
23935
24267
|
return candidate;
|
|
23936
24268
|
}
|
|
@@ -24107,7 +24439,7 @@ await lifecycleDispatcher.dispatch(
|
|
|
24107
24439
|
process.exit(exitCode);
|
|
24108
24440
|
function resolveBareDefault() {
|
|
24109
24441
|
const ctx = defaultRuntimeContext();
|
|
24110
|
-
if (
|
|
24442
|
+
if (existsSync31(defaultProjectDbPath(ctx))) {
|
|
24111
24443
|
return ["serve"];
|
|
24112
24444
|
}
|
|
24113
24445
|
process.stderr.write(tx(ENTRY_TEXTS.bareNoProject, { cwd: ctx.cwd }));
|