glimmervoid 0.0.0-stage → 0.30.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.
Files changed (59) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +195 -2
  3. package/dist/bin/glimmervoid.js +275 -0
  4. package/dist/chunks/adapters-Dho1VSY5.js +2 -0
  5. package/dist/chunks/adapters-zPDx1Xox.js +575 -0
  6. package/dist/chunks/agent-api-cli-D0MmtEdz.js +71 -0
  7. package/dist/chunks/agent-api-core-Dz0r21y9.js +103 -0
  8. package/dist/chunks/agent-setup-cli-B5Atxr3F.js +139 -0
  9. package/dist/chunks/ascii-figure-core-pDAwtlKO.js +45 -0
  10. package/dist/chunks/child-process-safe-DRGsp0TE.js +69 -0
  11. package/dist/chunks/child-process-safe-DvQjXsgz.js +2 -0
  12. package/dist/chunks/config-path-core-DFt-TemS.js +38 -0
  13. package/dist/chunks/config-store-CC8T3okC.js +2 -0
  14. package/dist/chunks/config-store-mm7MgzoL.js +1412 -0
  15. package/dist/chunks/contracts-DGkKNh9W.js +3020 -0
  16. package/dist/chunks/grok-DuU8xVLZ.js +436 -0
  17. package/dist/chunks/hook-command-core-DclEoE9e.js +14 -0
  18. package/dist/chunks/hook-relay-core-BHfYFnYV.js +88 -0
  19. package/dist/chunks/hook-tools-pSJia7ks.js +74 -0
  20. package/dist/chunks/ingest-core-DWI1Mbu4.js +342 -0
  21. package/dist/chunks/ingest-editor-core-BuI1gMF_.js +445 -0
  22. package/dist/chunks/loopback-post-C7C22eYi.js +79 -0
  23. package/dist/chunks/main-CuIZQPhZ.js +43559 -0
  24. package/dist/chunks/node-pty-preflight-CK1knGIR.js +162 -0
  25. package/dist/chunks/pair-cli-w-gCAF-1.js +110 -0
  26. package/dist/chunks/path-doctor-C23VAqeu.js +63 -0
  27. package/dist/chunks/refocus-core-BKQKllq7.js +38 -0
  28. package/dist/chunks/relay-stdin-CpHvLu6M.js +20 -0
  29. package/dist/chunks/remote-config-C2ytQwrs.js +525 -0
  30. package/dist/chunks/rtk-hook-core-Bl6gYHPN.js +133 -0
  31. package/dist/chunks/rtk-resolver-CLo-gZzw.js +21 -0
  32. package/dist/chunks/rtk-resolver-CMQAidlI.js +2 -0
  33. package/dist/chunks/runtime-paths-Ci_ksSkj.js +75 -0
  34. package/dist/chunks/runtime-paths-JLrZ3wyi.js +2 -0
  35. package/dist/chunks/sandbox-deps-Dd5-kZnf.js +67 -0
  36. package/dist/chunks/session-Cez8aIWg.js +164 -0
  37. package/dist/chunks/spawn-command-Cc40FiZI.js +134 -0
  38. package/dist/chunks/visions-cli-BXQFIBCX.js +98 -0
  39. package/dist/chunks/visions-setup-CvVnOe1r.js +1753 -0
  40. package/dist/client/assets/favicon-CKygeLwW.svg +7 -0
  41. package/dist/client/assets/index-B1MFijjb.css +1 -0
  42. package/dist/client/assets/index-CU1iPPdn.js +249 -0
  43. package/dist/client/index.html +108 -0
  44. package/dist/package.json +3 -0
  45. package/dist/scripts/postinstall-path-check.js +36 -0
  46. package/dist/server/index.js +92 -0
  47. package/dist/session/command-hook-relay.js +16 -0
  48. package/dist/session/hook-relay.js +24 -0
  49. package/dist/session/hook-tool-relay.js +107 -0
  50. package/dist/session/statusline-relay.js +82 -0
  51. package/dist/session/visions-relay.js +627 -0
  52. package/dist/tools/vscode-visions/extension.js +289 -0
  53. package/dist/tools/vscode-visions/lsp-convert.js +80 -0
  54. package/dist/tools/vscode-visions/package.json +32 -0
  55. package/dist/tools/vscode-visions/visions-lsp-core.js +112 -0
  56. package/package.json +96 -4
  57. package/scripts/postinstall.mjs +4 -0
  58. package/scripts/prepare-build.js +38 -0
  59. package/scripts/recover-handoff.mjs +82 -0
@@ -0,0 +1,1753 @@
1
+ import { a as packageRoot, i as extensionDir, n as cliPath, o as relayPath, t as bundled } from "./runtime-paths-Ci_ksSkj.js";
2
+ import { i as execSync, n as execFileAsync } from "./child-process-safe-DRGsp0TE.js";
3
+ import { r as resolvePathCommandMatches } from "./spawn-command-Cc40FiZI.js";
4
+ import { d as scrubText, n as SOURCE_DEFAULTS } from "./ingest-core-DWI1Mbu4.js";
5
+ import { stripTypeScriptTypes } from "node:module";
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import os from "node:os";
9
+ import crypto from "node:crypto";
10
+ var KIND = "file-change";
11
+ var DEFAULT_BATCH_MS = SOURCE_DEFAULTS.fs.batchMs;
12
+ var MAX_FILES_PER_BATCH = SOURCE_DEFAULTS.fs.digestQuota;
13
+ var UNTRACKED_KEY_CHARS = 16;
14
+ var MAX_ROOT_ENTRIES = 32;
15
+ var MAX_SAMPLE_FILES = 5;
16
+ var MAX_REL_PATH_CHARS = 200;
17
+ var IGNORED_DIR_NAMES = Object.freeze([
18
+ ".git",
19
+ "node_modules",
20
+ ".glimmervoid",
21
+ ".cache",
22
+ ".parcel-cache",
23
+ ".turbo",
24
+ ".next",
25
+ ".nuxt",
26
+ ".svelte-kit",
27
+ ".gradle",
28
+ ".tox",
29
+ ".venv",
30
+ ".pytest_cache",
31
+ ".mypy_cache",
32
+ ".ruff_cache",
33
+ ".idea",
34
+ "__pycache__",
35
+ "dist",
36
+ "build",
37
+ "target",
38
+ "coverage"
39
+ ]);
40
+ var IGNORED_FILE_SUFFIXES = Object.freeze([
41
+ "~",
42
+ ".swp",
43
+ ".swx",
44
+ ".swo",
45
+ ".tmp",
46
+ ".temp",
47
+ ".lock"
48
+ ]);
49
+ var IGNORED_FILE_NAMES = Object.freeze([
50
+ ".DS_Store",
51
+ "Thumbs.db",
52
+ "desktop.ini",
53
+ "4913"
54
+ ]);
55
+ var DAEMON_CONFIG_SIBLINGS = Object.freeze([
56
+ "usage-lanes.json",
57
+ "usage-warehouse.json",
58
+ "usage-budget-state.json",
59
+ "pr-review-state.json",
60
+ "team-review-state.json",
61
+ "pairings.json",
62
+ "pairings-seen.json",
63
+ "litellm-pricing.json",
64
+ "update-check.json",
65
+ "recordings",
66
+ "uploads",
67
+ "memory"
68
+ ]);
69
+ var ACTIVE_SESSION_STATES = Object.freeze([
70
+ "INITIALIZING",
71
+ "STARTING",
72
+ "RUNNING",
73
+ "WAITING",
74
+ "IDLE",
75
+ "COMPLETE"
76
+ ]);
77
+ var CHANGE_KINDS = Object.freeze([
78
+ "create",
79
+ "update",
80
+ "delete"
81
+ ]);
82
+ var CHANGE_VERBS = Object.freeze({
83
+ create: "created",
84
+ update: "updated",
85
+ delete: "deleted"
86
+ });
87
+ function toPosix(value) {
88
+ return String(value == null ? "" : value).split("\\").join("/");
89
+ }
90
+ function nonEmptyString(value) {
91
+ return typeof value === "string" && value.trim() ? value.trim() : null;
92
+ }
93
+ function buildIgnorePatterns(extraDirNames = []) {
94
+ const names = [];
95
+ for (const raw of [...IGNORED_DIR_NAMES, ...Array.isArray(extraDirNames) ? extraDirNames : []]) {
96
+ const name = nonEmptyString(raw);
97
+ if (!name || names.includes(name)) continue;
98
+ names.push(name);
99
+ }
100
+ const patterns = [];
101
+ for (const name of names) patterns.push(name, `**/${name}`, `**/${name}/**`);
102
+ for (const suffix of IGNORED_FILE_SUFFIXES) patterns.push(`**/*${suffix}`);
103
+ for (const name of IGNORED_FILE_NAMES) patterns.push(`**/${name}`);
104
+ return patterns;
105
+ }
106
+ function hasIgnoredSegment(relPath) {
107
+ for (const segment of toPosix(relPath).split("/")) {
108
+ if (!segment) continue;
109
+ if (IGNORED_DIR_NAMES.includes(segment)) return true;
110
+ }
111
+ return false;
112
+ }
113
+ function isIgnoredFileName(base) {
114
+ if (IGNORED_FILE_NAMES.includes(base)) return true;
115
+ return IGNORED_FILE_SUFFIXES.some((suffix) => base.endsWith(suffix));
116
+ }
117
+ function daemonWriteRules(configPath) {
118
+ const resolvedPath = nonEmptyString(configPath);
119
+ if (!resolvedPath) return null;
120
+ const configFile = path.resolve(resolvedPath);
121
+ const dir = path.dirname(configFile);
122
+ return {
123
+ paths: [configFile, ...DAEMON_CONFIG_SIBLINGS.map((name) => path.join(dir, name))],
124
+ dir,
125
+ prefix: `${path.basename(configFile)}.`
126
+ };
127
+ }
128
+ function isDaemonDerivedSibling(rules, absolutePath) {
129
+ const resolved = path.resolve(absolutePath);
130
+ if (foldCase(path.dirname(resolved)) !== foldCase(rules.dir)) return false;
131
+ return foldCase(path.basename(resolved)).startsWith(foldCase(rules.prefix));
132
+ }
133
+ function foldCase(value) {
134
+ return process.platform === "win32" ? String(value).toLowerCase() : String(value);
135
+ }
136
+ function isPathInside(parent, child) {
137
+ const from = nonEmptyString(parent);
138
+ const to = nonEmptyString(child);
139
+ if (!from || !to) return false;
140
+ const resolvedParent = path.resolve(from);
141
+ const resolvedChild = path.resolve(to);
142
+ if (foldCase(resolvedParent) === foldCase(resolvedChild)) return true;
143
+ const relative = path.relative(resolvedParent, resolvedChild);
144
+ if (!relative || path.isAbsolute(relative)) return false;
145
+ return !toPosix(relative).startsWith("../");
146
+ }
147
+ function relativeWithin(root, absolutePath) {
148
+ const from = nonEmptyString(root);
149
+ const to = nonEmptyString(absolutePath);
150
+ if (!from || !to) return null;
151
+ const relative = path.relative(path.resolve(from), path.resolve(to));
152
+ if (!relative || path.isAbsolute(relative)) return null;
153
+ const posix = toPosix(relative);
154
+ if (posix.startsWith("../")) return null;
155
+ return posix;
156
+ }
157
+ function isIgnoredChange({ relPath, absolutePath = null, daemonRules = null } = {}) {
158
+ const relative = nonEmptyString(relPath);
159
+ if (!relative) return true;
160
+ if (hasIgnoredSegment(relative)) return true;
161
+ const base = toPosix(relative).split("/").pop();
162
+ if (base && isIgnoredFileName(base)) return true;
163
+ const absolute = nonEmptyString(absolutePath);
164
+ if (!absolute || !daemonRules) return false;
165
+ if (daemonRules.paths.some((daemonPath) => isPathInside(daemonPath, absolute))) return true;
166
+ return isDaemonDerivedSibling(daemonRules, absolute);
167
+ }
168
+ function normalizeRoots(raw) {
169
+ if (!Array.isArray(raw)) return [];
170
+ const roots = [];
171
+ for (const entry of raw) {
172
+ const value = nonEmptyString(entry);
173
+ if (!value || roots.includes(value)) continue;
174
+ roots.push(value);
175
+ if (roots.length >= MAX_ROOT_ENTRIES) break;
176
+ }
177
+ return roots;
178
+ }
179
+ function dedupeRoots(roots) {
180
+ const named = [];
181
+ for (const entry of Array.isArray(roots) ? roots : []) {
182
+ const value = nonEmptyString(entry);
183
+ if (!value) continue;
184
+ if (named.some((kept) => foldCase(path.resolve(kept)) === foldCase(path.resolve(value)))) continue;
185
+ named.push(value);
186
+ }
187
+ named.sort((left, right) => left.length - right.length || left.localeCompare(right));
188
+ const kept = [];
189
+ for (const root of named) {
190
+ if (kept.some((outer) => isPathInside(outer, root))) continue;
191
+ kept.push(root);
192
+ }
193
+ return kept;
194
+ }
195
+ function deriveSessionRoots(session) {
196
+ const dirs = [];
197
+ for (const dir of [session?.path, session?.worktreeDir]) {
198
+ const value = nonEmptyString(dir);
199
+ if (!value || dirs.includes(value)) continue;
200
+ dirs.push(value);
201
+ }
202
+ return dirs;
203
+ }
204
+ function isActiveSessionState(state) {
205
+ return ACTIVE_SESSION_STATES.includes(nonEmptyString(state) || "");
206
+ }
207
+ function createBatch() {
208
+ return {
209
+ files: /* @__PURE__ */ new Map(),
210
+ untracked: /* @__PURE__ */ new Set(),
211
+ floored: false
212
+ };
213
+ }
214
+ function untrackedKey(relPath) {
215
+ return crypto.createHash("sha1").update(relPath, "utf8").digest("hex").slice(0, UNTRACKED_KEY_CHARS);
216
+ }
217
+ function truncatePath(relPath) {
218
+ if (relPath.length <= MAX_REL_PATH_CHARS) return relPath;
219
+ const scrubbed = scrubText(relPath);
220
+ if (scrubbed.length <= MAX_REL_PATH_CHARS) return scrubbed;
221
+ return `${scrubbed.slice(0, MAX_REL_PATH_CHARS)}...`;
222
+ }
223
+ function normalizeChangeKind(type) {
224
+ const kind = nonEmptyString(type);
225
+ if (!kind || !CHANGE_KINDS.includes(kind)) return null;
226
+ return kind;
227
+ }
228
+ function mergeChange(previous, next) {
229
+ if (!previous) return next;
230
+ if (previous === "create" && next === "delete") return null;
231
+ if (next === "delete") return "delete";
232
+ if (previous === "create") return "create";
233
+ return "update";
234
+ }
235
+ function recordChange(batch, relPath, type) {
236
+ const kind = normalizeChangeKind(type);
237
+ const relative = nonEmptyString(relPath);
238
+ if (!kind || !relative) return false;
239
+ const key = truncatePath(relative);
240
+ if (batch.files.has(key)) {
241
+ const merged = mergeChange(batch.files.get(key), kind);
242
+ if (merged === null) batch.files.delete(key);
243
+ if (merged !== null) batch.files.set(key, merged);
244
+ return true;
245
+ }
246
+ if (batch.files.size >= 2e3) {
247
+ const digest = untrackedKey(key);
248
+ if (batch.untracked.has(digest)) return true;
249
+ if (batch.untracked.size >= 1e4) {
250
+ batch.floored = true;
251
+ return true;
252
+ }
253
+ batch.untracked.add(digest);
254
+ return true;
255
+ }
256
+ batch.files.set(key, kind);
257
+ return true;
258
+ }
259
+ function batchSize(batch) {
260
+ return batch.files.size + batch.untracked.size;
261
+ }
262
+ function fileChangeEvent({ root, relPath, change, now }) {
263
+ return {
264
+ source: "fs",
265
+ kind: KIND,
266
+ ts: now,
267
+ scope: {
268
+ root,
269
+ sessionId: null
270
+ },
271
+ summary: `${CHANGE_VERBS[change]} ${relPath}`,
272
+ detail: {
273
+ path: relPath,
274
+ change
275
+ }
276
+ };
277
+ }
278
+ function countByKind(batch) {
279
+ const counts = {
280
+ create: 0,
281
+ update: 0,
282
+ delete: 0
283
+ };
284
+ for (const change of batch.files.values()) counts[change] += 1;
285
+ return counts;
286
+ }
287
+ function burstSummary(batch) {
288
+ const counts = countByKind(batch);
289
+ const parts = [];
290
+ if (counts.create) parts.push(`${counts.create} created`);
291
+ if (counts.update) parts.push(`${counts.update} updated`);
292
+ if (counts.delete) parts.push(`${counts.delete} deleted`);
293
+ const total = batchSize(batch);
294
+ const tail = parts.length > 0 ? `: ${parts.join(", ")}` : "";
295
+ return {
296
+ summary: `${batch.floored ? `at least ${total}` : `${total}`} files changed${tail}`,
297
+ counts,
298
+ total
299
+ };
300
+ }
301
+ function sampleOf(batch) {
302
+ return [...batch.files.keys()].slice(0, MAX_SAMPLE_FILES).join(", ");
303
+ }
304
+ function decideFsEvents(batch, { root = null, now = 0 } = {}) {
305
+ const total = batchSize(batch);
306
+ if (total === 0) return [];
307
+ if (total <= MAX_FILES_PER_BATCH && batch.untracked.size === 0) {
308
+ const events = [];
309
+ for (const [relPath, change] of batch.files) events.push(fileChangeEvent({
310
+ root,
311
+ relPath,
312
+ change,
313
+ now
314
+ }));
315
+ return events;
316
+ }
317
+ const { summary, counts } = burstSummary(batch);
318
+ return [{
319
+ source: "fs",
320
+ kind: KIND,
321
+ ts: now,
322
+ scope: {
323
+ root,
324
+ sessionId: null
325
+ },
326
+ summary,
327
+ detail: {
328
+ files: total,
329
+ atLeast: batch.floored,
330
+ created: counts.create,
331
+ updated: counts.update,
332
+ deleted: counts.delete,
333
+ sample: sampleOf(batch)
334
+ }
335
+ }];
336
+ }
337
+ //#endregion
338
+ //#region server/lane-log.ts
339
+ var MAX_ONCE_KEYS = 256;
340
+ function renderFieldValue(value) {
341
+ if (typeof value !== "string") return String(value);
342
+ if (value !== "" && !/[ ="\u0000-\u001f\u007f]/.test(value)) return value;
343
+ return JSON.stringify(value);
344
+ }
345
+ function renderLine(prefix, message, fields) {
346
+ const renderedFields = Object.entries(fields ?? {}).flatMap(([key, value]) => {
347
+ if (value === null || value === void 0) return [];
348
+ return `${key}=${renderFieldValue(value)}`;
349
+ });
350
+ if (renderedFields.length === 0) return `${prefix} ${message}`;
351
+ return `${prefix} ${message} ${renderedFields.join(" ")}`;
352
+ }
353
+ function createLaneLog({ prefix = "", logger = console, debugFlag = false } = {}) {
354
+ const onceKeys = /* @__PURE__ */ new Set();
355
+ function note(message, fields) {
356
+ if (!logger || typeof logger.log !== "function") return;
357
+ logger.log(renderLine(prefix, message, fields));
358
+ }
359
+ function warn(message, fields) {
360
+ if (!logger || typeof logger.warn !== "function") return;
361
+ logger.warn(renderLine(prefix, message, fields));
362
+ }
363
+ function warnOnce(key, message, fields) {
364
+ if (onceKeys.has(key)) return;
365
+ onceKeys.add(key);
366
+ const [oldestKey] = onceKeys;
367
+ if (onceKeys.size > MAX_ONCE_KEYS && oldestKey !== void 0) onceKeys.delete(oldestKey);
368
+ warn(message, fields);
369
+ }
370
+ function isDebug() {
371
+ if (typeof debugFlag !== "function") return debugFlag === true;
372
+ try {
373
+ return debugFlag() === true;
374
+ } catch {
375
+ return false;
376
+ }
377
+ }
378
+ function debugNote(buildMessage, buildFields) {
379
+ if (!isDebug()) return;
380
+ note(buildMessage(), buildFields?.());
381
+ }
382
+ return {
383
+ note,
384
+ warn,
385
+ warnOnce,
386
+ debugNote
387
+ };
388
+ }
389
+ //#endregion
390
+ //#region server/core/editor-extension-core.ts
391
+ var EDITOR_CANDIDATES = [
392
+ {
393
+ command: "codium",
394
+ label: "VSCodium",
395
+ macAppBundleName: "VSCodium"
396
+ },
397
+ {
398
+ command: "code",
399
+ label: "VS Code",
400
+ macAppBundleName: "Visual Studio Code"
401
+ },
402
+ {
403
+ command: "code-insiders",
404
+ label: "VS Code Insiders",
405
+ macAppBundleName: "Visual Studio Code - Insiders"
406
+ },
407
+ {
408
+ command: "cursor",
409
+ label: "Cursor",
410
+ macAppBundleName: "Cursor"
411
+ },
412
+ {
413
+ command: "windsurf",
414
+ label: "Windsurf",
415
+ macAppBundleName: "Windsurf"
416
+ }
417
+ ];
418
+ var MACOS_BUNDLE_CLI_DIR = "Contents/Resources/app/bin";
419
+ function editorBundleCandidatePaths(candidate, { platform, homeDir = null }) {
420
+ if (platform !== "darwin") return [];
421
+ const bundleName = candidate.macAppBundleName;
422
+ if (!bundleName) return [];
423
+ const applicationDirs = ["/Applications"];
424
+ if (homeDir) applicationDirs.push(path.posix.join(homeDir, "Applications"));
425
+ return applicationDirs.map((applicationDir) => path.posix.join(applicationDir, `${bundleName}.app`, MACOS_BUNDLE_CLI_DIR, candidate.command));
426
+ }
427
+ function editorLabelFor(command, platform) {
428
+ const byCommand = EDITOR_CANDIDATES.find((candidate) => candidate.command === command);
429
+ if (byCommand) return byCommand.label;
430
+ const basename = (platform === "win32" ? path.win32 : path.posix).basename(command);
431
+ return EDITOR_CANDIDATES.find((candidate) => candidate.command === basename)?.label || command;
432
+ }
433
+ function isAbsoluteEditorPath(requested, platform) {
434
+ return (platform === "win32" ? path.win32 : path.posix).isAbsolute(requested);
435
+ }
436
+ function resolveEditorPathsFor({ platform, homeDir = null, matchesOnPath, exists }) {
437
+ const resolved = {};
438
+ for (const candidate of EDITOR_CANDIDATES) {
439
+ const [firstOnPath] = matchesOnPath(candidate.command);
440
+ const commandPath = firstOnPath || editorBundleCandidatePaths(candidate, {
441
+ platform,
442
+ homeDir
443
+ }).find(exists);
444
+ if (!commandPath) continue;
445
+ resolved[candidate.command] = commandPath;
446
+ }
447
+ return resolved;
448
+ }
449
+ function decideEditorTargets({ requested = null, resolvedByCommand = {}, probe }) {
450
+ if (requested) {
451
+ const label = editorLabelFor(requested, probe.platform);
452
+ if (isAbsoluteEditorPath(requested, probe.platform)) {
453
+ if (!probe.exists(requested)) return {
454
+ targets: [],
455
+ reason: `editor path does not exist on disk: ${requested}`
456
+ };
457
+ return {
458
+ targets: [{
459
+ command: requested,
460
+ label,
461
+ commandPath: requested
462
+ }],
463
+ reason: "requested"
464
+ };
465
+ }
466
+ const commandPath = resolvedByCommand[requested];
467
+ if (!commandPath) return {
468
+ targets: [],
469
+ reason: `editor not found on PATH: ${requested}`
470
+ };
471
+ return {
472
+ targets: [{
473
+ command: requested,
474
+ label,
475
+ commandPath
476
+ }],
477
+ reason: "requested"
478
+ };
479
+ }
480
+ const targets = [];
481
+ for (const candidate of EDITOR_CANDIDATES) {
482
+ const commandPath = resolvedByCommand[candidate.command];
483
+ if (!commandPath) continue;
484
+ targets.push({
485
+ command: candidate.command,
486
+ label: candidate.label,
487
+ commandPath
488
+ });
489
+ }
490
+ if (targets.length === 0) return {
491
+ targets: [],
492
+ reason: "no VS Code family editor found on PATH or on disk"
493
+ };
494
+ return {
495
+ targets,
496
+ reason: "detected"
497
+ };
498
+ }
499
+ function relayStamp(relayPath) {
500
+ return `${JSON.stringify({ relayPath }, null, 2)}\n`;
501
+ }
502
+ function visionsExtensionFiles({ manifestJson, extensionJs, convertJs, lspCoreJs, relayPath }) {
503
+ return [
504
+ {
505
+ path: "package.json",
506
+ data: manifestJson
507
+ },
508
+ {
509
+ path: "extension.js",
510
+ data: extensionJs
511
+ },
512
+ {
513
+ path: "lsp-convert.js",
514
+ data: convertJs
515
+ },
516
+ {
517
+ path: "visions-lsp-core.js",
518
+ data: lspCoreJs
519
+ },
520
+ {
521
+ path: "relay-path.json",
522
+ data: relayStamp(relayPath)
523
+ }
524
+ ];
525
+ }
526
+ function parseInstalledExtensions(stdout) {
527
+ return String(stdout || "").split(/\r?\n/).map((line) => line.trim()).filter((line) => line !== "");
528
+ }
529
+ function isExtensionInstalled(stdout, extensionId) {
530
+ const wanted = String(extensionId).toLowerCase();
531
+ return parseInstalledExtensions(stdout).some((line) => line.toLowerCase() === wanted);
532
+ }
533
+ //#endregion
534
+ //#region server/core/editor-setup-core.ts
535
+ function relayInvocation({ glimmervoidOnPath = true, cliPath = "", nodePath = "node" } = {}) {
536
+ if (glimmervoidOnPath) return {
537
+ command: "glimmervoid",
538
+ args: ["visions", "relay"]
539
+ };
540
+ return {
541
+ command: nodePath,
542
+ args: [
543
+ cliPath,
544
+ "visions",
545
+ "relay"
546
+ ]
547
+ };
548
+ }
549
+ function invocationParts(invocation) {
550
+ return [invocation.command, ...invocation.args];
551
+ }
552
+ function commandLine(invocation) {
553
+ return invocationParts(invocation).join(" ");
554
+ }
555
+ function jsonCommandArray(invocation) {
556
+ return JSON.stringify(invocationParts(invocation));
557
+ }
558
+ function neovimSnippet(invocation) {
559
+ return [
560
+ "vim.api.nvim_create_autocmd('FileType', {",
561
+ " pattern = 'markdown',",
562
+ " callback = function()",
563
+ ` vim.lsp.start({ name = 'glimmervoid-visions', cmd = { ${invocationParts(invocation).map((part) => `'${part}'`).join(", ")} } })`,
564
+ " end,",
565
+ "})"
566
+ ].join("\n");
567
+ }
568
+ function helixSnippet(invocation) {
569
+ return [
570
+ "[language-server.glimmervoid-visions]",
571
+ `command = "${invocation.command}"`,
572
+ `args = ${JSON.stringify(invocation.args)}`,
573
+ "",
574
+ "[[language]]",
575
+ "name = \"markdown\"",
576
+ "language-servers = [\"marksman\", \"glimmervoid-visions\"]"
577
+ ].join("\n");
578
+ }
579
+ function emacsSnippet(invocation) {
580
+ return [
581
+ "(with-eval-after-load 'eglot",
582
+ " (add-to-list 'eglot-server-programs",
583
+ ` '(markdown-mode . (${invocationParts(invocation).map((part) => `"${part}"`).join(" ")}))))`
584
+ ].join("\n");
585
+ }
586
+ function sublimeSnippet(invocation) {
587
+ return [
588
+ "{",
589
+ " \"clients\": {",
590
+ " \"glimmervoid-visions\": {",
591
+ " \"enabled\": true,",
592
+ ` "command": ${jsonCommandArray(invocation)},`,
593
+ " \"selector\": \"text.html.markdown\"",
594
+ " }",
595
+ " }",
596
+ "}"
597
+ ].join("\n");
598
+ }
599
+ function kateSnippet(invocation) {
600
+ return [
601
+ "{",
602
+ " \"servers\": {",
603
+ " \"markdown\": {",
604
+ ` "command": ${jsonCommandArray(invocation)},`,
605
+ " \"highlightingModeRegex\": \"^Markdown$\"",
606
+ " }",
607
+ " }",
608
+ "}"
609
+ ].join("\n");
610
+ }
611
+ function genericSnippet(invocation) {
612
+ return `command: ${commandLine(invocation)}\ntransport: stdio\nlanguages: markdown`;
613
+ }
614
+ var EDITOR_RECIPES = [
615
+ {
616
+ id: "vscode",
617
+ label: "VS Code, VSCodium, Cursor, Windsurf",
618
+ where: "installed for you",
619
+ build: () => "glimmervoid visions install"
620
+ },
621
+ {
622
+ id: "neovim",
623
+ label: "Neovim",
624
+ where: "init.lua (or any file it loads)",
625
+ build: neovimSnippet
626
+ },
627
+ {
628
+ id: "helix",
629
+ label: "Helix",
630
+ where: "~/.config/helix/languages.toml",
631
+ build: helixSnippet
632
+ },
633
+ {
634
+ id: "emacs",
635
+ label: "Emacs (eglot)",
636
+ where: "init.el",
637
+ build: emacsSnippet
638
+ },
639
+ {
640
+ id: "sublime",
641
+ label: "Sublime Text (LSP package)",
642
+ where: "Preferences: LSP Settings",
643
+ build: sublimeSnippet
644
+ },
645
+ {
646
+ id: "kate",
647
+ label: "Kate",
648
+ where: "Settings > LSP Client > User Server Settings",
649
+ build: kateSnippet
650
+ },
651
+ {
652
+ id: "jetbrains",
653
+ label: "JetBrains IDEs (LSP4IJ plugin)",
654
+ where: "Settings > Language Servers > New > Command",
655
+ build: commandLine
656
+ },
657
+ {
658
+ id: "other",
659
+ label: "Any other LSP client",
660
+ where: "wherever it registers a language server",
661
+ build: genericSnippet
662
+ }
663
+ ];
664
+ function recipeIds() {
665
+ return EDITOR_RECIPES.map((recipe) => recipe.id);
666
+ }
667
+ function buildSetupGuide({ editorId = null, invocation }) {
668
+ const recipes = editorId ? EDITOR_RECIPES.filter((recipe) => recipe.id === editorId) : EDITOR_RECIPES;
669
+ if (recipes.length === 0) return {
670
+ ok: false,
671
+ reason: `unknown editor: ${editorId}`,
672
+ sections: []
673
+ };
674
+ return {
675
+ ok: true,
676
+ reason: "ok",
677
+ sections: recipes.map((recipe) => ({
678
+ id: recipe.id,
679
+ label: recipe.label,
680
+ where: recipe.where,
681
+ snippet: recipe.build(invocation)
682
+ }))
683
+ };
684
+ }
685
+ //#endregion
686
+ //#region server/core/visions-defaults-core.ts
687
+ var IMPLIED_INGEST = Object.freeze({
688
+ enabled: true,
689
+ sources: Object.freeze({
690
+ fs: Object.freeze({ enabled: true }),
691
+ git: Object.freeze({ enabled: true }),
692
+ editor: Object.freeze({ enabled: true })
693
+ })
694
+ });
695
+ function isPlainObject(value) {
696
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
697
+ }
698
+ function impliedIngestChanges(config) {
699
+ if (!isPlainObject(config.ingest)) return [{
700
+ path: ["ingest"],
701
+ value: JSON.parse(JSON.stringify(IMPLIED_INGEST)),
702
+ why: "visions needs machine context"
703
+ }];
704
+ const changes = [];
705
+ if (config.ingest.enabled !== true && config.ingest.enabled !== false) changes.push({
706
+ path: ["ingest", "enabled"],
707
+ value: true,
708
+ why: "visions needs machine context"
709
+ });
710
+ if (config.ingest.enabled === false) return changes;
711
+ const sources = isPlainObject(config.ingest.sources) ? config.ingest.sources : {};
712
+ for (const [name, value] of Object.entries(IMPLIED_INGEST.sources)) {
713
+ if (isPlainObject(sources[name])) continue;
714
+ changes.push({
715
+ path: [
716
+ "ingest",
717
+ "sources",
718
+ name
719
+ ],
720
+ value: JSON.parse(JSON.stringify(value)),
721
+ why: `visions implies the ${name} source`
722
+ });
723
+ }
724
+ return changes;
725
+ }
726
+ function decideImpliedDefaults(config) {
727
+ if (!config) return { changes: [] };
728
+ const visions = config.visions;
729
+ if (!isPlainObject(visions) || visions.enabled !== true) return { changes: [] };
730
+ const changes = impliedIngestChanges(config);
731
+ if (!isPlainObject(visions.dispatch)) changes.push({
732
+ path: ["visions", "dispatch"],
733
+ value: { enabled: true },
734
+ why: "visions implies its model dispatch"
735
+ });
736
+ return { changes };
737
+ }
738
+ function applyChanges(config, changes) {
739
+ for (const change of changes) {
740
+ let cursor = config;
741
+ for (const key of change.path.slice(0, -1)) {
742
+ const existing = cursor[key];
743
+ if (isPlainObject(existing)) {
744
+ cursor = existing;
745
+ continue;
746
+ }
747
+ const created = {};
748
+ cursor[key] = created;
749
+ cursor = created;
750
+ }
751
+ cursor[change.path[change.path.length - 1]] = change.value;
752
+ }
753
+ return config;
754
+ }
755
+ //#endregion
756
+ //#region server/core/vsix-core.ts
757
+ var CRC_TABLE = buildCrcTable();
758
+ var LOCAL_HEADER_SIG = 67324752;
759
+ var CENTRAL_HEADER_SIG = 33639248;
760
+ var END_OF_CENTRAL_SIG = 101010256;
761
+ var STORED = 0;
762
+ var ZIP_VERSION = 20;
763
+ var UTF8_FLAG = 2048;
764
+ var DOS_TIME = 0;
765
+ var DOS_DATE = 33;
766
+ function buildCrcTable() {
767
+ const table = /* @__PURE__ */ new Uint32Array(256);
768
+ for (let index = 0; index < 256; index += 1) {
769
+ let value = index;
770
+ for (let bit = 0; bit < 8; bit += 1) value = value & 1 ? 3988292384 ^ value >>> 1 : value >>> 1;
771
+ table[index] = value >>> 0;
772
+ }
773
+ return table;
774
+ }
775
+ function crc32(buffer) {
776
+ let crc = 4294967295;
777
+ for (let index = 0; index < buffer.length; index += 1) crc = CRC_TABLE[(crc ^ buffer[index]) & 255] ^ crc >>> 8;
778
+ return (crc ^ 4294967295) >>> 0;
779
+ }
780
+ function localHeader(entry, nameBytes) {
781
+ const header = Buffer.alloc(30);
782
+ header.writeUInt32LE(LOCAL_HEADER_SIG, 0);
783
+ header.writeUInt16LE(ZIP_VERSION, 4);
784
+ header.writeUInt16LE(UTF8_FLAG, 6);
785
+ header.writeUInt16LE(STORED, 8);
786
+ header.writeUInt16LE(DOS_TIME, 10);
787
+ header.writeUInt16LE(DOS_DATE, 12);
788
+ header.writeUInt32LE(entry.crc, 14);
789
+ header.writeUInt32LE(entry.size, 18);
790
+ header.writeUInt32LE(entry.size, 22);
791
+ header.writeUInt16LE(nameBytes.length, 26);
792
+ header.writeUInt16LE(0, 28);
793
+ return header;
794
+ }
795
+ function centralHeader(entry, nameBytes) {
796
+ const header = Buffer.alloc(46);
797
+ header.writeUInt32LE(CENTRAL_HEADER_SIG, 0);
798
+ header.writeUInt16LE(ZIP_VERSION, 4);
799
+ header.writeUInt16LE(ZIP_VERSION, 6);
800
+ header.writeUInt16LE(UTF8_FLAG, 8);
801
+ header.writeUInt16LE(STORED, 10);
802
+ header.writeUInt16LE(DOS_TIME, 12);
803
+ header.writeUInt16LE(DOS_DATE, 14);
804
+ header.writeUInt32LE(entry.crc, 16);
805
+ header.writeUInt32LE(entry.size, 20);
806
+ header.writeUInt32LE(entry.size, 24);
807
+ header.writeUInt16LE(nameBytes.length, 28);
808
+ header.writeUInt16LE(0, 30);
809
+ header.writeUInt16LE(0, 32);
810
+ header.writeUInt16LE(0, 34);
811
+ header.writeUInt16LE(0, 36);
812
+ header.writeUInt32LE(0, 38);
813
+ header.writeUInt32LE(entry.offset, 42);
814
+ return header;
815
+ }
816
+ function buildZip(files) {
817
+ const chunks = [];
818
+ const central = [];
819
+ let offset = 0;
820
+ for (const file of files) {
821
+ const nameBytes = Buffer.from(file.path, "utf8");
822
+ const data = Buffer.isBuffer(file.data) ? file.data : Buffer.from(String(file.data), "utf8");
823
+ const entry = {
824
+ crc: crc32(data),
825
+ size: data.length,
826
+ offset
827
+ };
828
+ const header = localHeader(entry, nameBytes);
829
+ chunks.push(header, nameBytes, data);
830
+ offset += header.length + nameBytes.length + data.length;
831
+ central.push({
832
+ entry,
833
+ nameBytes
834
+ });
835
+ }
836
+ const centralOffset = offset;
837
+ let centralSize = 0;
838
+ for (const { entry, nameBytes } of central) {
839
+ const header = centralHeader(entry, nameBytes);
840
+ chunks.push(header, nameBytes);
841
+ centralSize += header.length + nameBytes.length;
842
+ }
843
+ const end = Buffer.alloc(22);
844
+ end.writeUInt32LE(END_OF_CENTRAL_SIG, 0);
845
+ end.writeUInt16LE(0, 4);
846
+ end.writeUInt16LE(0, 6);
847
+ end.writeUInt16LE(central.length, 8);
848
+ end.writeUInt16LE(central.length, 10);
849
+ end.writeUInt32LE(centralSize, 12);
850
+ end.writeUInt32LE(centralOffset, 16);
851
+ end.writeUInt16LE(0, 20);
852
+ chunks.push(end);
853
+ return Buffer.concat(chunks);
854
+ }
855
+ function escapeXml(value) {
856
+ return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
857
+ }
858
+ function contentTypesXml() {
859
+ return [
860
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>",
861
+ "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">",
862
+ "<Default Extension=\"json\" ContentType=\"application/json\"/>",
863
+ "<Default Extension=\"js\" ContentType=\"application/javascript\"/>",
864
+ "<Default Extension=\"md\" ContentType=\"text/markdown\"/>",
865
+ "<Default Extension=\"vsixmanifest\" ContentType=\"text/xml\"/>",
866
+ "</Types>"
867
+ ].join("");
868
+ }
869
+ function vsixManifestXml({ id, publisher, version, displayName, description, engine }) {
870
+ return [
871
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>",
872
+ "<PackageManifest Version=\"2.0.0\" xmlns=\"http://schemas.microsoft.com/developer/vsx-schema/2011\">",
873
+ "<Metadata>",
874
+ `<Identity Language="en-US" Id="${escapeXml(id)}" Version="${escapeXml(version)}" Publisher="${escapeXml(publisher)}"/>`,
875
+ `<DisplayName>${escapeXml(displayName)}</DisplayName>`,
876
+ `<Description xml:space="preserve">${escapeXml(description)}</Description>`,
877
+ "<Tags></Tags>",
878
+ "<Categories>Other</Categories>",
879
+ "<GalleryFlags>Public</GalleryFlags>",
880
+ "<Properties>",
881
+ `<Property Id="Microsoft.VisualStudio.Code.Engine" Value="${escapeXml(engine)}"/>`,
882
+ "<Property Id=\"Microsoft.VisualStudio.Code.ExtensionDependencies\" Value=\"\"/>",
883
+ "<Property Id=\"Microsoft.VisualStudio.Code.ExtensionPack\" Value=\"\"/>",
884
+ "<Property Id=\"Microsoft.VisualStudio.Code.ExtensionKind\" Value=\"workspace\"/>",
885
+ "</Properties>",
886
+ "</Metadata>",
887
+ "<Installation><InstallationTarget Id=\"Microsoft.VisualStudio.Code\"/></Installation>",
888
+ "<Dependencies/>",
889
+ "<Assets><Asset Type=\"Microsoft.VisualStudio.Code.Manifest\" Path=\"extension/package.json\" Addressable=\"true\"/></Assets>",
890
+ "</PackageManifest>"
891
+ ].join("");
892
+ }
893
+ function buildVsix({ manifest, extensionFiles = [] }) {
894
+ const files = [{
895
+ path: "extension.vsixmanifest",
896
+ data: vsixManifestXml({
897
+ id: manifest.name,
898
+ publisher: manifest.publisher,
899
+ version: manifest.version,
900
+ displayName: manifest.displayName || manifest.name,
901
+ description: manifest.description || manifest.displayName || manifest.name,
902
+ engine: manifest.engines?.vscode || "*"
903
+ })
904
+ }, {
905
+ path: "[Content_Types].xml",
906
+ data: contentTypesXml()
907
+ }];
908
+ for (const file of extensionFiles) files.push({
909
+ path: `extension/${file.path}`,
910
+ data: file.data
911
+ });
912
+ return buildZip(files);
913
+ }
914
+ function extensionIdOf(manifest) {
915
+ return `${manifest.publisher}.${manifest.name}`;
916
+ }
917
+ //#endregion
918
+ //#region server/core/editor-wire-core.ts
919
+ var MARKER_NAME = "glimmervoid-visions";
920
+ var SERVER_ID = "glimmervoid-visions";
921
+ var HELIX_BEGIN = `# >>> ${MARKER_NAME}`;
922
+ var HELIX_END = `# <<< ${MARKER_NAME}`;
923
+ var EMACS_BEGIN = `;; >>> ${MARKER_NAME}`;
924
+ var EMACS_END = `;; <<< ${MARKER_NAME}`;
925
+ var UNCHANGED = "unchanged";
926
+ function parts(invocation) {
927
+ return [invocation.command, ...invocation.args];
928
+ }
929
+ function splitMarked(text, begin, end) {
930
+ const startAt = text.indexOf(begin);
931
+ if (startAt === -1) return {
932
+ hasBlock: false,
933
+ before: text,
934
+ after: ""
935
+ };
936
+ const endAt = text.indexOf(end, startAt);
937
+ if (endAt === -1) return {
938
+ hasBlock: false,
939
+ unterminated: true,
940
+ before: text,
941
+ after: ""
942
+ };
943
+ return {
944
+ hasBlock: true,
945
+ before: text.slice(0, startAt),
946
+ after: text.slice(endAt + end.length)
947
+ };
948
+ }
949
+ function appendBlock(text, block) {
950
+ const body = text.trimEnd();
951
+ return `${body}${body === "" ? "" : "\n\n"}${block}\n`;
952
+ }
953
+ function replaceMarkedBlock(text, begin, end, block) {
954
+ const split = splitMarked(text, begin, end);
955
+ if (split.unterminated) return {
956
+ text,
957
+ changed: false,
958
+ reason: "unterminated-block"
959
+ };
960
+ if (!split.hasBlock) return {
961
+ text: appendBlock(text, block),
962
+ changed: true,
963
+ reason: "appended"
964
+ };
965
+ const merged = `${split.before}${block}${split.after}`;
966
+ if (merged === text) return {
967
+ text,
968
+ changed: false,
969
+ reason: UNCHANGED
970
+ };
971
+ return {
972
+ text: merged,
973
+ changed: true,
974
+ reason: "updated"
975
+ };
976
+ }
977
+ function neovimDropIn(invocation) {
978
+ return [
979
+ `-- Written by glimmervoid; edit glimmervoid's config, not this file. Delete it to unwire ${MARKER_NAME}.`,
980
+ "vim.api.nvim_create_autocmd('FileType', {",
981
+ " pattern = 'markdown',",
982
+ ` callback = function()`,
983
+ ` vim.lsp.start({ name = '${SERVER_ID}', cmd = { ${parts(invocation).map((part) => `'${part}'`).join(", ")} } })`,
984
+ " end,",
985
+ "})",
986
+ ""
987
+ ].join("\n");
988
+ }
989
+ function helixServerLines(invocation) {
990
+ return [
991
+ HELIX_BEGIN,
992
+ `[language-server.${SERVER_ID}]`,
993
+ `command = "${invocation.command}"`,
994
+ `args = ${JSON.stringify(invocation.args)}`
995
+ ];
996
+ }
997
+ function helixServerBlock(invocation) {
998
+ return [...helixServerLines(invocation), HELIX_END].join("\n");
999
+ }
1000
+ function helixFullBlock(invocation) {
1001
+ return [
1002
+ ...helixServerLines(invocation),
1003
+ "",
1004
+ "[[language]]",
1005
+ "name = \"markdown\"",
1006
+ `language-servers = ["marksman", "${SERVER_ID}"]`,
1007
+ HELIX_END
1008
+ ].join("\n");
1009
+ }
1010
+ function addToHelixMarkdown(text) {
1011
+ const lines = text.split("\n");
1012
+ let inMarkdown = false;
1013
+ let sawMarkdownLanguage = false;
1014
+ for (let index = 0; index < lines.length; index += 1) {
1015
+ const line = lines[index].trim();
1016
+ if (line === "[[language]]") {
1017
+ inMarkdown = false;
1018
+ continue;
1019
+ }
1020
+ if (line.startsWith("[") && line !== "[[language]]") {
1021
+ inMarkdown = false;
1022
+ continue;
1023
+ }
1024
+ if (/^name\s*=\s*"markdown"\s*$/.test(line)) {
1025
+ inMarkdown = true;
1026
+ sawMarkdownLanguage = true;
1027
+ continue;
1028
+ }
1029
+ if (!inMarkdown || !/^language-servers\s*=\s*\[/.test(line)) continue;
1030
+ if (line.includes(`"glimmervoid-visions"`)) return {
1031
+ lines,
1032
+ sawMarkdownLanguage,
1033
+ changed: false
1034
+ };
1035
+ const isEmptyArray = /\[\s*\]\s*$/.test(line);
1036
+ lines[index] = isEmptyArray ? lines[index].replace(/\[\s*\]\s*$/, `["${SERVER_ID}"]`) : lines[index].replace(/\]\s*$/, `, "${SERVER_ID}"]`);
1037
+ return {
1038
+ lines,
1039
+ sawMarkdownLanguage,
1040
+ changed: true
1041
+ };
1042
+ }
1043
+ return {
1044
+ lines,
1045
+ sawMarkdownLanguage,
1046
+ changed: false
1047
+ };
1048
+ }
1049
+ function helixMerge(existingText, invocation) {
1050
+ const text = typeof existingText === "string" ? existingText : "";
1051
+ const split = splitMarked(text, HELIX_BEGIN, HELIX_END);
1052
+ if (split.unterminated) return {
1053
+ text,
1054
+ changed: false,
1055
+ reason: "unterminated-block"
1056
+ };
1057
+ const beforeEdit = addToHelixMarkdown(split.before);
1058
+ const afterEdit = addToHelixMarkdown(split.after);
1059
+ const block = beforeEdit.sawMarkdownLanguage || afterEdit.sawMarkdownLanguage ? helixServerBlock(invocation) : helixFullBlock(invocation);
1060
+ const before = beforeEdit.lines.join("\n");
1061
+ const after = afterEdit.lines.join("\n");
1062
+ if (split.hasBlock) {
1063
+ const merged = `${before}${block}${after}`;
1064
+ if (merged === text) return {
1065
+ text,
1066
+ changed: false,
1067
+ reason: UNCHANGED
1068
+ };
1069
+ return {
1070
+ text: merged,
1071
+ changed: true,
1072
+ reason: "updated"
1073
+ };
1074
+ }
1075
+ const merged = appendBlock(`${before}${after}`, block);
1076
+ if (merged === text) return {
1077
+ text,
1078
+ changed: false,
1079
+ reason: UNCHANGED
1080
+ };
1081
+ return {
1082
+ text: merged,
1083
+ changed: true,
1084
+ reason: "appended"
1085
+ };
1086
+ }
1087
+ function removeFromHelixMarkdown(text) {
1088
+ const lines = text.split("\n");
1089
+ let changed = false;
1090
+ for (let index = 0; index < lines.length; index += 1) {
1091
+ if (!/^\s*language-servers\s*=\s*\[/.test(lines[index]) || !lines[index].includes(`"glimmervoid-visions"`)) continue;
1092
+ lines[index] = lines[index].replace(new RegExp(`\\s*,\\s*"${SERVER_ID}"|"${SERVER_ID}"\\s*,\\s*|"${SERVER_ID}"`), "");
1093
+ changed = true;
1094
+ }
1095
+ return {
1096
+ text: lines.join("\n"),
1097
+ changed
1098
+ };
1099
+ }
1100
+ function helixRemove(existingText) {
1101
+ const text = typeof existingText === "string" ? existingText : "";
1102
+ const split = splitMarked(text, HELIX_BEGIN, HELIX_END);
1103
+ if (split.unterminated) return {
1104
+ text,
1105
+ changed: false,
1106
+ reason: "unterminated-block"
1107
+ };
1108
+ const cleaned = removeFromHelixMarkdown(split.hasBlock ? `${split.before.trimEnd()}\n${split.after.replace(/^\n+/, "")}` : text);
1109
+ if (cleaned.text === text) return {
1110
+ text,
1111
+ changed: false,
1112
+ reason: UNCHANGED
1113
+ };
1114
+ return {
1115
+ text: cleaned.text,
1116
+ changed: true,
1117
+ reason: "removed"
1118
+ };
1119
+ }
1120
+ function emacsRemove(existingText) {
1121
+ const text = typeof existingText === "string" ? existingText : "";
1122
+ const split = splitMarked(text, EMACS_BEGIN, EMACS_END);
1123
+ if (split.unterminated) return {
1124
+ text,
1125
+ changed: false,
1126
+ reason: "unterminated-block"
1127
+ };
1128
+ if (!split.hasBlock) return {
1129
+ text,
1130
+ changed: false,
1131
+ reason: UNCHANGED
1132
+ };
1133
+ return {
1134
+ text: `${split.before.trimEnd()}\n${split.after.replace(/^\n+/, "")}`,
1135
+ changed: true,
1136
+ reason: "removed"
1137
+ };
1138
+ }
1139
+ function jsonSettingsRemove(existingText, { path: keyPath }) {
1140
+ const parsed = parseJsonSettings(existingText);
1141
+ if (!parsed.ok) return {
1142
+ text: existingText,
1143
+ changed: false,
1144
+ reason: parsed.reason
1145
+ };
1146
+ let cursor = parsed.value;
1147
+ for (const key of keyPath.slice(0, -1)) {
1148
+ if (!cursor[key] || typeof cursor[key] !== "object") return {
1149
+ text: existingText,
1150
+ changed: false,
1151
+ reason: UNCHANGED
1152
+ };
1153
+ cursor = cursor[key];
1154
+ }
1155
+ const lastKey = keyPath[keyPath.length - 1];
1156
+ if (!(lastKey in cursor)) return {
1157
+ text: existingText,
1158
+ changed: false,
1159
+ reason: UNCHANGED
1160
+ };
1161
+ delete cursor[lastKey];
1162
+ return {
1163
+ text: `${JSON.stringify(parsed.value, null, 2)}\n`,
1164
+ changed: true,
1165
+ reason: "removed"
1166
+ };
1167
+ }
1168
+ function emacsMerge(existingText, invocation) {
1169
+ return replaceMarkedBlock(typeof existingText === "string" ? existingText : "", EMACS_BEGIN, EMACS_END, [
1170
+ EMACS_BEGIN,
1171
+ ";; Written by glimmervoid. Delete this block to unwire glimmervoid-visions.",
1172
+ "(with-eval-after-load 'eglot",
1173
+ " (add-to-list 'eglot-server-programs",
1174
+ ` '(markdown-mode . (${parts(invocation).map((part) => `"${part}"`).join(" ")}))))`,
1175
+ EMACS_END
1176
+ ].join("\n"));
1177
+ }
1178
+ function parseJsonSettings(existingText) {
1179
+ const text = typeof existingText === "string" ? existingText.trim() : "";
1180
+ if (text === "") return {
1181
+ ok: true,
1182
+ value: {}
1183
+ };
1184
+ try {
1185
+ const value = JSON.parse(text);
1186
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {
1187
+ ok: false,
1188
+ reason: "not-an-object"
1189
+ };
1190
+ return {
1191
+ ok: true,
1192
+ value
1193
+ };
1194
+ } catch {
1195
+ return {
1196
+ ok: false,
1197
+ reason: "unparseable"
1198
+ };
1199
+ }
1200
+ }
1201
+ function jsonSettingsMerge(existingText, { path: keyPath, value }) {
1202
+ const parsed = parseJsonSettings(existingText);
1203
+ if (!parsed.ok) return {
1204
+ text: existingText,
1205
+ changed: false,
1206
+ reason: parsed.reason
1207
+ };
1208
+ const settings = parsed.value;
1209
+ let cursor = settings;
1210
+ for (const key of keyPath.slice(0, -1)) {
1211
+ if (!cursor[key] || typeof cursor[key] !== "object" || Array.isArray(cursor[key])) cursor[key] = {};
1212
+ cursor = cursor[key];
1213
+ }
1214
+ const lastKey = keyPath[keyPath.length - 1];
1215
+ if (JSON.stringify(cursor[lastKey]) === JSON.stringify(value)) return {
1216
+ text: existingText,
1217
+ changed: false,
1218
+ reason: UNCHANGED
1219
+ };
1220
+ cursor[lastKey] = value;
1221
+ return {
1222
+ text: `${JSON.stringify(settings, null, 2)}\n`,
1223
+ changed: true,
1224
+ reason: "merged"
1225
+ };
1226
+ }
1227
+ function sublimeSettings(invocation) {
1228
+ return {
1229
+ path: ["clients", SERVER_ID],
1230
+ value: {
1231
+ enabled: true,
1232
+ command: parts(invocation),
1233
+ selector: "text.html.markdown"
1234
+ }
1235
+ };
1236
+ }
1237
+ function kateSettings(invocation) {
1238
+ return {
1239
+ path: ["servers", "markdown"],
1240
+ value: {
1241
+ command: parts(invocation),
1242
+ highlightingModeRegex: "^Markdown$"
1243
+ }
1244
+ };
1245
+ }
1246
+ //#endregion
1247
+ //#region server/editor-wire.ts
1248
+ function configHome(homeDir, env) {
1249
+ if (env.XDG_CONFIG_HOME) return env.XDG_CONFIG_HOME;
1250
+ return path.join(homeDir, ".config");
1251
+ }
1252
+ function sublimeUserDir(homeDir, platform, env) {
1253
+ if (platform === "darwin") return path.join(homeDir, "Library", "Application Support", "Sublime Text", "Packages", "User");
1254
+ if (platform === "win32") return path.join(env.APPDATA || path.join(homeDir, "AppData", "Roaming"), "Sublime Text", "Packages", "User");
1255
+ return path.join(configHome(homeDir, env), "sublime-text", "Packages", "User");
1256
+ }
1257
+ function neovimConfigDir(homeDir, platform, env) {
1258
+ if (platform === "win32") return path.join(env.LOCALAPPDATA || path.join(homeDir, "AppData", "Local"), "nvim");
1259
+ return path.join(configHome(homeDir, env), "nvim");
1260
+ }
1261
+ function helixConfigDir(homeDir, platform, env) {
1262
+ if (platform === "win32") return path.join(env.APPDATA || path.join(homeDir, "AppData", "Roaming"), "helix");
1263
+ return path.join(configHome(homeDir, env), "helix");
1264
+ }
1265
+ function exists(filePath) {
1266
+ try {
1267
+ return fs.existsSync(filePath);
1268
+ } catch {
1269
+ return false;
1270
+ }
1271
+ }
1272
+ function onPath(command, { platform, exec }) {
1273
+ return resolvePathCommandMatches(command, {
1274
+ platform,
1275
+ exec
1276
+ }).length > 0;
1277
+ }
1278
+ function editorTargets({ homeDir = os.homedir(), platform = process.platform, env = process.env, exec = execSync } = {}) {
1279
+ const nvimDir = neovimConfigDir(homeDir, platform, env);
1280
+ const helixDir = helixConfigDir(homeDir, platform, env);
1281
+ const kateDir = path.join(configHome(homeDir, env), "kate", "lspclient");
1282
+ const sublimeDir = sublimeUserDir(homeDir, platform, env);
1283
+ const emacsInits = [path.join(homeDir, ".emacs.d", "init.el"), path.join(configHome(homeDir, env), "emacs", "init.el")];
1284
+ const targets = [];
1285
+ if (exists(nvimDir) || onPath("nvim", {
1286
+ platform,
1287
+ exec
1288
+ })) targets.push({
1289
+ id: "neovim",
1290
+ label: "Neovim",
1291
+ filePath: path.join(nvimDir, "plugin", "glimmervoid-visions.lua"),
1292
+ owned: true,
1293
+ create: true
1294
+ });
1295
+ if (exists(helixDir) || onPath("hx", {
1296
+ platform,
1297
+ exec
1298
+ })) targets.push({
1299
+ id: "helix",
1300
+ label: "Helix",
1301
+ filePath: path.join(helixDir, "languages.toml"),
1302
+ merge: helixMerge,
1303
+ unmerge: helixRemove,
1304
+ create: true
1305
+ });
1306
+ if (exists(kateDir) || onPath("kate", {
1307
+ platform,
1308
+ exec
1309
+ })) targets.push({
1310
+ id: "kate",
1311
+ label: "Kate",
1312
+ filePath: path.join(kateDir, "settings.json"),
1313
+ json: kateSettings,
1314
+ create: true
1315
+ });
1316
+ if (exists(sublimeDir)) targets.push({
1317
+ id: "sublime",
1318
+ label: "Sublime Text",
1319
+ filePath: path.join(sublimeDir, "LSP.sublime-settings"),
1320
+ json: sublimeSettings,
1321
+ create: true
1322
+ });
1323
+ const emacsInit = emacsInits.find((candidate) => exists(candidate));
1324
+ if (emacsInit) targets.push({
1325
+ id: "emacs",
1326
+ label: "Emacs",
1327
+ filePath: emacsInit,
1328
+ merge: emacsMerge,
1329
+ unmerge: emacsRemove,
1330
+ create: false
1331
+ });
1332
+ return targets;
1333
+ }
1334
+ function readIfPresent(filePath) {
1335
+ try {
1336
+ return fs.readFileSync(filePath, "utf8");
1337
+ } catch {
1338
+ return null;
1339
+ }
1340
+ }
1341
+ function backupOnce(filePath, existingText) {
1342
+ const backupPath = `${filePath}.glimmervoid.bak`;
1343
+ if (existingText === null || exists(backupPath)) return null;
1344
+ fs.writeFileSync(backupPath, existingText);
1345
+ return backupPath;
1346
+ }
1347
+ function planFor(target, existingText, invocation) {
1348
+ if (target.owned) {
1349
+ const text = neovimDropIn(invocation);
1350
+ if (existingText === text) return {
1351
+ text,
1352
+ changed: false,
1353
+ reason: "unchanged"
1354
+ };
1355
+ return {
1356
+ text,
1357
+ changed: true,
1358
+ reason: existingText === null ? "created" : "updated"
1359
+ };
1360
+ }
1361
+ if (target.merge) return target.merge(existingText, invocation);
1362
+ return jsonSettingsMerge(existingText ?? "", target.json(invocation));
1363
+ }
1364
+ function unwirePlanFor(target, existingText) {
1365
+ if (target.owned) return {
1366
+ text: null,
1367
+ changed: existingText !== null,
1368
+ reason: existingText === null ? "unchanged" : "deleted"
1369
+ };
1370
+ if (target.unmerge) return target.unmerge(existingText);
1371
+ return jsonSettingsRemove(existingText ?? "", target.json({
1372
+ command: "",
1373
+ args: []
1374
+ }));
1375
+ }
1376
+ function applyPlan(target, existingText, plan, { dryRun }) {
1377
+ if (!plan.changed) return {
1378
+ ...outcome(target),
1379
+ action: plan.reason === "unchanged" ? "unchanged" : "skipped",
1380
+ reason: plan.reason
1381
+ };
1382
+ if (dryRun) return {
1383
+ ...outcome(target),
1384
+ action: "would-write",
1385
+ reason: plan.reason
1386
+ };
1387
+ try {
1388
+ if (plan.text === null) {
1389
+ fs.rmSync(target.filePath, { force: true });
1390
+ return {
1391
+ ...outcome(target),
1392
+ action: "removed",
1393
+ reason: plan.reason
1394
+ };
1395
+ }
1396
+ fs.mkdirSync(path.dirname(target.filePath), { recursive: true });
1397
+ backupOnce(target.filePath, existingText);
1398
+ fs.writeFileSync(target.filePath, plan.text);
1399
+ } catch (error) {
1400
+ return {
1401
+ ...outcome(target),
1402
+ action: "failed",
1403
+ reason: error instanceof Error ? error.message : String(error)
1404
+ };
1405
+ }
1406
+ return {
1407
+ ...outcome(target),
1408
+ action: plan.text === null ? "removed" : "wrote",
1409
+ reason: plan.reason
1410
+ };
1411
+ }
1412
+ function unwireEditor(target, { dryRun = false } = {}) {
1413
+ const existingText = readIfPresent(target.filePath);
1414
+ if (existingText === null && !target.owned) return {
1415
+ ...outcome(target),
1416
+ action: "skipped",
1417
+ reason: "file-absent"
1418
+ };
1419
+ return applyPlan(target, existingText, unwirePlanFor(target, existingText), { dryRun });
1420
+ }
1421
+ function wireEditor(target, invocation, { dryRun = false } = {}) {
1422
+ const existingText = readIfPresent(target.filePath);
1423
+ if (existingText === null && !target.create) return {
1424
+ ...outcome(target),
1425
+ action: "skipped",
1426
+ reason: "file-absent"
1427
+ };
1428
+ return applyPlan(target, existingText, planFor(target, existingText, invocation), { dryRun });
1429
+ }
1430
+ function outcome(target) {
1431
+ return {
1432
+ id: target.id,
1433
+ label: target.label,
1434
+ filePath: target.filePath
1435
+ };
1436
+ }
1437
+ function wireEditors({ invocation, dryRun = false, targets = null, ...detection }) {
1438
+ return (targets || editorTargets(detection)).map((target) => wireEditor(target, invocation, { dryRun }));
1439
+ }
1440
+ function unwireEditors({ dryRun = false, targets = null, ...detection } = {}) {
1441
+ return (targets || editorTargets(detection)).map((target) => unwireEditor(target, { dryRun }));
1442
+ }
1443
+ //#endregion
1444
+ //#region server/visions-setup.ts
1445
+ var EXTENSION_DIR = extensionDir;
1446
+ var RELAY_PATH = relayPath("visions-relay");
1447
+ var LSP_CORE_PATH = bundled ? path.join(EXTENSION_DIR, "visions-lsp-core.js") : path.join(packageRoot, "server", "core", "visions-lsp-core.ts");
1448
+ var CLI_PATH = cliPath;
1449
+ var EDITOR_TIMEOUT_MS = 6e4;
1450
+ function underTestRunner(env) {
1451
+ return typeof env.NODE_TEST_CONTEXT === "string" && env.NODE_TEST_CONTEXT !== "";
1452
+ }
1453
+ function errorMessage(error) {
1454
+ return error instanceof Error ? error.message : String(error);
1455
+ }
1456
+ function commandFailureDetail(error) {
1457
+ const failure = error ?? {};
1458
+ return String(failure.stderr || failure.message || error).trim().split("\n").pop();
1459
+ }
1460
+ function packedSource(filePath) {
1461
+ return stripTypeScriptTypes(fs.readFileSync(filePath, "utf8"), { mode: "strip" });
1462
+ }
1463
+ function packedLspCore(filePath) {
1464
+ const stripped = packedSource(filePath);
1465
+ const exportStatement = /^export \{([^}]*)\};/m;
1466
+ if (!exportStatement.test(stripped)) throw new Error(`${filePath} has no single-line export block to rewrite for the extension host`);
1467
+ return stripped.replace(exportStatement, "module.exports = {$1};");
1468
+ }
1469
+ function packedExtensionSources() {
1470
+ if (bundled) return {
1471
+ extensionJs: fs.readFileSync(path.join(EXTENSION_DIR, "extension.js"), "utf8"),
1472
+ convertJs: fs.readFileSync(path.join(EXTENSION_DIR, "lsp-convert.js"), "utf8"),
1473
+ lspCoreJs: fs.readFileSync(LSP_CORE_PATH, "utf8")
1474
+ };
1475
+ return {
1476
+ extensionJs: packedSource(path.join(EXTENSION_DIR, "extension.ts")),
1477
+ convertJs: packedSource(path.join(EXTENSION_DIR, "lsp-convert.ts")),
1478
+ lspCoreJs: packedLspCore(LSP_CORE_PATH)
1479
+ };
1480
+ }
1481
+ function packVsix() {
1482
+ const manifestJson = fs.readFileSync(path.join(EXTENSION_DIR, "package.json"), "utf8");
1483
+ const manifest = JSON.parse(manifestJson);
1484
+ return {
1485
+ manifest,
1486
+ vsix: buildVsix({
1487
+ manifest,
1488
+ extensionFiles: visionsExtensionFiles({
1489
+ manifestJson,
1490
+ ...packedExtensionSources(),
1491
+ relayPath: RELAY_PATH
1492
+ })
1493
+ })
1494
+ };
1495
+ }
1496
+ function missingInstallFiles() {
1497
+ return [RELAY_PATH, LSP_CORE_PATH].filter((filePath) => !fs.existsSync(filePath));
1498
+ }
1499
+ function resolvedEditorPaths({ platform = process.platform, exec = execSync, homeDir = os.homedir(), exists = fs.existsSync } = {}) {
1500
+ return resolveEditorPathsFor({
1501
+ platform,
1502
+ homeDir,
1503
+ matchesOnPath: (command) => resolvePathCommandMatches(command, {
1504
+ platform,
1505
+ exec
1506
+ }),
1507
+ exists
1508
+ });
1509
+ }
1510
+ function relayInvocationOptions({ platform = process.platform, exec = execSync } = {}) {
1511
+ const onPath = resolvePathCommandMatches("glimmervoid", {
1512
+ platform,
1513
+ exec
1514
+ }).length > 0;
1515
+ return {
1516
+ chosen: relayInvocation({
1517
+ glimmervoidOnPath: onPath,
1518
+ cliPath: CLI_PATH,
1519
+ nodePath: process.execPath
1520
+ }),
1521
+ absolute: relayInvocation({
1522
+ glimmervoidOnPath: false,
1523
+ cliPath: CLI_PATH,
1524
+ nodePath: process.execPath
1525
+ }),
1526
+ onPath
1527
+ };
1528
+ }
1529
+ function resolveRelayInvocation(options = {}) {
1530
+ return relayInvocationOptions(options).chosen;
1531
+ }
1532
+ async function editorExtensions(commandPath) {
1533
+ try {
1534
+ const { stdout } = await execFileAsync(commandPath, ["--list-extensions"], { timeout: EDITOR_TIMEOUT_MS });
1535
+ return stdout;
1536
+ } catch (error) {
1537
+ return `${(error ?? {}).stdout || ""}`;
1538
+ }
1539
+ }
1540
+ async function installInto(target, vsixPath, extensionId) {
1541
+ try {
1542
+ await execFileAsync(target.commandPath, [
1543
+ "--install-extension",
1544
+ vsixPath,
1545
+ "--force"
1546
+ ], { timeout: EDITOR_TIMEOUT_MS });
1547
+ } catch (error) {
1548
+ return {
1549
+ ...target,
1550
+ ok: false,
1551
+ detail: commandFailureDetail(error)
1552
+ };
1553
+ }
1554
+ if (!isExtensionInstalled(await editorExtensions(target.commandPath), extensionId)) return {
1555
+ ...target,
1556
+ ok: false,
1557
+ detail: "the editor reported no error but does not list the extension"
1558
+ };
1559
+ return {
1560
+ ...target,
1561
+ ok: true,
1562
+ detail: "installed"
1563
+ };
1564
+ }
1565
+ async function uninstallFrom(target, extensionId) {
1566
+ if (!isExtensionInstalled(await editorExtensions(target.commandPath), extensionId)) return {
1567
+ ...target,
1568
+ ok: true,
1569
+ detail: "not installed"
1570
+ };
1571
+ try {
1572
+ await execFileAsync(target.commandPath, ["--uninstall-extension", extensionId], { timeout: EDITOR_TIMEOUT_MS });
1573
+ } catch (error) {
1574
+ return {
1575
+ ...target,
1576
+ ok: false,
1577
+ detail: commandFailureDetail(error)
1578
+ };
1579
+ }
1580
+ return {
1581
+ ...target,
1582
+ ok: true,
1583
+ detail: "removed"
1584
+ };
1585
+ }
1586
+ async function installExtensions({ requested = null, resolvedByCommand = null, platform = process.platform, exists = fs.existsSync } = {}) {
1587
+ const { targets, reason } = decideEditorTargets({
1588
+ requested,
1589
+ resolvedByCommand: resolvedByCommand || resolvedEditorPaths({
1590
+ platform,
1591
+ exists
1592
+ }),
1593
+ probe: {
1594
+ platform,
1595
+ exists
1596
+ }
1597
+ });
1598
+ if (targets.length === 0) return {
1599
+ targets: [],
1600
+ reason,
1601
+ results: []
1602
+ };
1603
+ const { manifest, vsix } = packVsix();
1604
+ const extensionId = extensionIdOf(manifest);
1605
+ const vsixPath = path.join(os.tmpdir(), `${extensionId}-${manifest.version}.vsix`);
1606
+ fs.writeFileSync(vsixPath, vsix);
1607
+ const results = [];
1608
+ for (const target of targets) results.push(await installInto(target, vsixPath, extensionId));
1609
+ return {
1610
+ targets,
1611
+ reason,
1612
+ results
1613
+ };
1614
+ }
1615
+ async function uninstallExtensions({ resolvedByCommand = null, platform = process.platform, exists = fs.existsSync } = {}) {
1616
+ const { targets } = decideEditorTargets({
1617
+ resolvedByCommand: resolvedByCommand || resolvedEditorPaths({
1618
+ platform,
1619
+ exists
1620
+ }),
1621
+ probe: {
1622
+ platform,
1623
+ exists
1624
+ }
1625
+ });
1626
+ const { manifest } = packVsix();
1627
+ const extensionId = extensionIdOf(manifest);
1628
+ const results = [];
1629
+ for (const target of targets) results.push(await uninstallFrom(target, extensionId));
1630
+ return {
1631
+ targets,
1632
+ results
1633
+ };
1634
+ }
1635
+ async function wireEverything({ requested = null, dryRun = false } = {}) {
1636
+ const missing = missingInstallFiles();
1637
+ if (missing.length > 0) return {
1638
+ ok: false,
1639
+ reason: `missing from this install: ${missing.join(", ")}`,
1640
+ extensions: {
1641
+ targets: [],
1642
+ reason: "not-run",
1643
+ results: []
1644
+ },
1645
+ files: []
1646
+ };
1647
+ const invocation = resolveRelayInvocation();
1648
+ return {
1649
+ ok: true,
1650
+ reason: "ok",
1651
+ invocation,
1652
+ extensions: await installExtensions({ requested }),
1653
+ files: wireEditors({
1654
+ invocation,
1655
+ dryRun
1656
+ })
1657
+ };
1658
+ }
1659
+ async function unwireEverything() {
1660
+ return {
1661
+ extensions: await uninstallExtensions(),
1662
+ files: unwireEditors()
1663
+ };
1664
+ }
1665
+ function createVisionsSetup({ getConfig = () => ({}), configStore = null, logger = console, debug = false, env = process.env, onConfigChanged = null, wire = wireEverything, unwire = unwireEverything } = {}) {
1666
+ const { note, warn } = createLaneLog({
1667
+ prefix: "[visions-setup]",
1668
+ logger,
1669
+ debugFlag: debug
1670
+ });
1671
+ let appliedState = false;
1672
+ let inFlight = null;
1673
+ function isEnabled() {
1674
+ return (getConfig()?.visions)?.enabled === true;
1675
+ }
1676
+ function reportFiles(files) {
1677
+ for (const file of files) {
1678
+ if (file.action === "unchanged" || file.action === "skipped") continue;
1679
+ if (file.action === "failed") {
1680
+ warn(`${file.label}: ${file.reason} (${file.filePath})`);
1681
+ continue;
1682
+ }
1683
+ note(`${file.label}: ${file.action} ${file.filePath}`);
1684
+ }
1685
+ }
1686
+ function reportConfigChanged() {
1687
+ if (typeof onConfigChanged !== "function") return;
1688
+ Promise.resolve().then(onConfigChanged).catch((error) => warn(`lane rebuild failed: ${errorMessage(error)}`));
1689
+ }
1690
+ function isEditorWiringRefused() {
1691
+ return underTestRunner(env) && wire === wireEverything && unwire === unwireEverything;
1692
+ }
1693
+ function isConfigWriteRefused() {
1694
+ if (!underTestRunner(env)) return false;
1695
+ const configPath = configStore?.configPath;
1696
+ if (typeof configPath !== "string" || !configPath) return false;
1697
+ return isPathInside(os.homedir(), configPath) && !isPathInside(os.tmpdir(), configPath);
1698
+ }
1699
+ function writeImpliedDefaults() {
1700
+ if (!configStore || isConfigWriteRefused()) return null;
1701
+ if (decideImpliedDefaults(getConfig()).changes.length === 0) return null;
1702
+ const written = [];
1703
+ const saved = configStore.save((freshConfig) => {
1704
+ const changes = decideImpliedDefaults(freshConfig).changes;
1705
+ written.push(...changes);
1706
+ applyChanges(freshConfig, changes);
1707
+ });
1708
+ if (!saved || written.length === 0) return null;
1709
+ applyChanges(getConfig(), written);
1710
+ for (const change of written) note(`config: ${change.path.join(".")} on (${change.why})`);
1711
+ reportConfigChanged();
1712
+ return saved;
1713
+ }
1714
+ async function apply(enabled) {
1715
+ if (!enabled) {
1716
+ if (isEditorWiringRefused()) return null;
1717
+ const report = await unwire();
1718
+ reportFiles(report.files);
1719
+ for (const result of report.extensions.results) note(`${result.label}: extension ${result.detail}`);
1720
+ return report;
1721
+ }
1722
+ writeImpliedDefaults();
1723
+ if (isEditorWiringRefused()) return null;
1724
+ const report = await wire({});
1725
+ if (!report.ok) {
1726
+ warn(report.reason);
1727
+ return report;
1728
+ }
1729
+ reportFiles(report.files);
1730
+ for (const result of report.extensions.results) {
1731
+ if (result.ok) note(`${result.label}: extension ${result.detail}`);
1732
+ if (!result.ok) warn(`${result.label}: extension install failed: ${result.detail}`);
1733
+ }
1734
+ return report;
1735
+ }
1736
+ async function maybeApply() {
1737
+ const enabled = isEnabled();
1738
+ if (appliedState === enabled) return null;
1739
+ if (inFlight) return inFlight;
1740
+ appliedState = enabled;
1741
+ inFlight = apply(enabled).catch((error) => {
1742
+ appliedState = null;
1743
+ warn(`editor wiring failed: ${errorMessage(error)}`);
1744
+ return null;
1745
+ }).finally(() => {
1746
+ inFlight = null;
1747
+ });
1748
+ return inFlight;
1749
+ }
1750
+ return { maybeApply };
1751
+ }
1752
+ //#endregion
1753
+ export { deriveSessionRoots as C, recordChange as D, normalizeRoots as E, relativeWithin as O, dedupeRoots as S, isIgnoredChange as T, DEFAULT_BATCH_MS as _, relayInvocationOptions as a, daemonWriteRules as b, unwireEverything as c, extensionIdOf as d, buildSetupGuide as f, createLaneLog as g, isExtensionInstalled as h, packVsix as i, wireEverything as l, recipeIds as m, createVisionsSetup as n, resolveRelayInvocation as o, commandLine as p, editorExtensions as r, resolvedEditorPaths as s, RELAY_PATH as t, editorTargets as u, buildIgnorePatterns as v, isActiveSessionState as w, decideFsEvents as x, createBatch as y };