ctxloom-pro 1.5.5 → 1.7.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.
@@ -15,6 +15,15 @@ var CTXLOOM_SERVER = {
15
15
  args: ["-y", "ctxloom"],
16
16
  env: {}
17
17
  };
18
+ function tomlString(s) {
19
+ const escaped = s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/[\x00-\x1f\x7f]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
20
+ return `"${escaped}"`;
21
+ }
22
+ function yamlEscape(s) {
23
+ if (/^[A-Za-z0-9_./-][A-Za-z0-9_./@-]*$/.test(s)) return s;
24
+ const escaped = s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
25
+ return `"${escaped}"`;
26
+ }
18
27
  var _serverEntry;
19
28
  function getServerEntry() {
20
29
  if (_serverEntry) return _serverEntry;
@@ -55,9 +64,16 @@ var MCP_CLIENTS = [
55
64
  id: "cursor",
56
65
  name: "Cursor",
57
66
  description: "Cursor AI code editor",
67
+ // Vendor docs (Cursor team, verified 2026-05) list TWO canonical paths:
68
+ // - `<repo>/.cursor/mcp.json` (project-scoped — the documented default)
69
+ // - `~/.cursor/mcp.json` (user-scoped, applies to every project)
70
+ // The XDG-style paths in the old list were speculative; Cursor never
71
+ // shipped them. Project-root scope goes FIRST so a per-project install
72
+ // matches the canonical workflow before we touch the user-wide config.
58
73
  configPaths: [
59
- path.join(xdgConfig(), "Cursor", "User", "globalStorage", "cursor-mcp", "mcp.json"),
74
+ path.join(process.cwd(), ".cursor", "mcp.json"),
60
75
  path.join(HOME, ".cursor", "mcp.json"),
76
+ path.join(xdgConfig(), "Cursor", "User", "globalStorage", "cursor-mcp", "mcp.json"),
61
77
  path.join(xdgConfig(), "Cursor", "mcp.json")
62
78
  ],
63
79
  cliBinaries: ["cursor"],
@@ -124,23 +140,41 @@ var MCP_CLIENTS = [
124
140
  serversPath: "mcpServers"
125
141
  },
126
142
  // ─── Continue.dev ───────────────────────────────────────
143
+ // v1.7.0 fix: current Continue (2026+) uses **per-server YAML files**
144
+ // at `.continue/mcpServers/<name>.yaml` (workspace-scoped), NOT the
145
+ // old `~/.continue/config.json` with embedded `experimental.mcpServers`.
146
+ // Writing to the old path silently fails on current Continue — the
147
+ // file is parsed but the section is ignored. Verified against
148
+ // docs.continue.dev/customize/deep-dives/mcp (2026-05).
149
+ //
150
+ // The customWriter renders the entire YAML file because each MCP
151
+ // server gets its own file; there's no merge step.
127
152
  {
128
153
  id: "continue",
129
154
  name: "Continue.dev",
130
155
  description: "Continue open-source AI code assistant",
156
+ // Workspace-scoped path FIRST (the canonical/correct path on
157
+ // current Continue). Legacy user-scoped paths kept as detection
158
+ // fallback so we still detect Continue's presence on machines
159
+ // that haven't migrated their config yet.
131
160
  configPaths: [
161
+ path.join(process.cwd(), ".continue", "mcpServers", "ctxloom.yaml"),
132
162
  path.join(HOME, ".continue", "config.json"),
133
163
  path.join(xdgConfig(), "continue", "config.json")
134
164
  ],
135
165
  cliBinaries: ["continue"],
136
166
  appBundles: [],
137
167
  usesMcpServersFormat: false,
138
- serversPath: "experimental.mcpServers",
139
- formatConfig: (entry) => ({
140
- command: entry.command,
141
- args: entry.args,
142
- transport: "stdio"
143
- })
168
+ serversPath: "mcpServers",
169
+ customWriter: (_targetPath, entry, _existingContent) => {
170
+ const envLines = entry.env && Object.keys(entry.env).length > 0 ? "\n env:\n" + Object.entries(entry.env).map(([k, v]) => ` ${k}: ${yamlEscape(v)}`).join("\n") : "";
171
+ const argsLines = entry.args && entry.args.length > 0 ? "\n args:\n" + entry.args.map((a) => ` - ${yamlEscape(a)}`).join("\n") : "\n args: []";
172
+ return `# Generated by \`ctxloom setup\` \u2014 Continue MCP server registration.
173
+ # Format: docs.continue.dev/customize/deep-dives/mcp
174
+ mcpServers:
175
+ - name: ctxloom
176
+ command: ${yamlEscape(entry.command)}` + argsLines + envLines + "\n";
177
+ }
144
178
  },
145
179
  // ─── Aider ──────────────────────────────────────────────
146
180
  {
@@ -157,18 +191,83 @@ var MCP_CLIENTS = [
157
191
  serversPath: "mcpServers"
158
192
  },
159
193
  // ─── Codex CLI (OpenAI) ─────────────────────────────────
194
+ // v1.7.0 fix: current Codex (2026+) uses **TOML at config.toml**,
195
+ // NOT JSON at mcp.json. The schema key is `mcp_servers` (snake_case
196
+ // TOML), NOT `mcpServers`. Writing JSON to the old `.codex/mcp.json`
197
+ // path silently fails on current Codex — the file is never read.
198
+ // Verified against developers.openai.com/codex/config-reference
199
+ // (2026-05).
200
+ //
201
+ // config.toml is SHARED with other Codex settings (model selection,
202
+ // auth, sandbox prefs), so the writer reads existing content,
203
+ // appends/updates ONLY the `[mcp_servers.ctxloom]` block, and
204
+ // preserves everything else. We deliberately avoid pulling in a
205
+ // TOML parser library — the only mutation is a single named-table
206
+ // block, which append-or-replace by string match handles safely.
160
207
  {
161
208
  id: "codex",
162
209
  name: "Codex CLI",
163
210
  description: "OpenAI Codex CLI agent",
211
+ // Workspace path FIRST (canonical per Codex docs); user-scoped
212
+ // path second (preserves detection on machines with Codex
213
+ // installed but no project-local config yet).
164
214
  configPaths: [
215
+ path.join(process.cwd(), ".codex", "config.toml"),
216
+ path.join(HOME, ".codex", "config.toml"),
217
+ // Legacy detection only — won't be written to. Kept so users
218
+ // who created these in earlier ctxloom versions still get
219
+ // detected (they need to migrate, but at least we surface them).
165
220
  path.join(HOME, ".codex", "mcp.json"),
166
221
  path.join(xdgConfig(), "codex", "mcp.json")
167
222
  ],
168
223
  cliBinaries: ["codex"],
169
224
  appBundles: [],
170
- usesMcpServersFormat: true,
171
- serversPath: "mcpServers"
225
+ usesMcpServersFormat: false,
226
+ serversPath: "mcp_servers",
227
+ customInstalledCheck: (target) => {
228
+ try {
229
+ const content = fs.readFileSync(target, "utf-8");
230
+ return /^\s*\[mcp_servers\.ctxloom\]\s*$/m.test(content);
231
+ } catch {
232
+ return false;
233
+ }
234
+ },
235
+ customWriter: (_targetPath, entry, existingContent) => {
236
+ const lines = [
237
+ "# ctxloom \u2014 added by `ctxloom setup`. Safe to edit; the",
238
+ "# installer only ever modifies the [mcp_servers.ctxloom]",
239
+ "# block and never touches the rest of this file.",
240
+ "[mcp_servers.ctxloom]",
241
+ `command = ${tomlString(entry.command)}`,
242
+ `args = [${(entry.args ?? []).map(tomlString).join(", ")}]`
243
+ ];
244
+ if (entry.env && Object.keys(entry.env).length > 0) {
245
+ lines.push("");
246
+ lines.push("[mcp_servers.ctxloom.env]");
247
+ for (const [k, v] of Object.entries(entry.env)) {
248
+ lines.push(`${k} = ${tomlString(v)}`);
249
+ }
250
+ }
251
+ const newBlock = lines.join("\n") + "\n";
252
+ if (!existingContent) {
253
+ return newBlock;
254
+ }
255
+ const blockRegex = /(^|\n)(?:#[^\n]*\n)*\[mcp_servers\.ctxloom\][\s\S]*?(?=\n\[(?!mcp_servers\.ctxloom)|$)/;
256
+ if (blockRegex.test(existingContent)) {
257
+ return existingContent.replace(
258
+ blockRegex,
259
+ (_m, leading) => (leading === "\n" ? "\n" : "") + newBlock.trimEnd()
260
+ );
261
+ }
262
+ const sep = existingContent.endsWith("\n") ? "\n" : "\n\n";
263
+ return existingContent + sep + newBlock;
264
+ },
265
+ customRemove: (existingContent) => {
266
+ const blockWithLeadingComments = /(^|\n)(?:#[^\n]*\n)*\[mcp_servers\.ctxloom\][\s\S]*?(?=\n\[(?!mcp_servers\.ctxloom)|$)/;
267
+ const stripped = existingContent.replace(blockWithLeadingComments, (_m, leading) => leading);
268
+ if (stripped.trim() === "") return null;
269
+ return stripped;
270
+ }
172
271
  },
173
272
  // ─── Kimi ───────────────────────────────────────────────
174
273
  {
@@ -210,6 +309,77 @@ var MCP_CLIENTS = [
210
309
  appBundles: ["com.jetbrains.intellij"],
211
310
  usesMcpServersFormat: true,
212
311
  serversPath: "mcpServers"
312
+ },
313
+ // ─── Zed ────────────────────────────────────────────────
314
+ // Zed's MCP config lives under the main settings.json, NOT a
315
+ // dedicated mcp.json. Critical wrinkle: the key is `context_servers`,
316
+ // not the conventional `mcpServers` — silently ignored otherwise.
317
+ // Verified against zed.dev/docs/ai/mcp (2026-05).
318
+ {
319
+ id: "zed",
320
+ name: "Zed",
321
+ description: "Zed high-performance code editor",
322
+ configPaths: [
323
+ path.join(xdgConfig(), "zed", "settings.json"),
324
+ path.join(HOME, ".config", "zed", "settings.json")
325
+ ],
326
+ cliBinaries: ["zed"],
327
+ appBundles: ["dev.zed.Zed", "dev.zed.Zed-Preview"],
328
+ usesMcpServersFormat: true,
329
+ serversPath: "context_servers"
330
+ },
331
+ // ─── Gemini CLI ─────────────────────────────────────────
332
+ // Google's Gemini CLI tool. Workspace config wins over user config
333
+ // when both exist; we list the workspace path first so a project
334
+ // install lands where the user expects. Schema is standard
335
+ // `mcpServers` per google-gemini/gemini-cli docs/cli/settings.md.
336
+ {
337
+ id: "gemini-cli",
338
+ name: "Gemini CLI",
339
+ description: "Google Gemini command-line AI agent",
340
+ configPaths: [
341
+ path.join(process.cwd(), ".gemini", "settings.json"),
342
+ path.join(HOME, ".gemini", "settings.json")
343
+ ],
344
+ cliBinaries: ["gemini"],
345
+ appBundles: [],
346
+ usesMcpServersFormat: true,
347
+ serversPath: "mcpServers"
348
+ },
349
+ // ─── Kiro ───────────────────────────────────────────────
350
+ // Kiro IDE (kiro.dev). Both workspace + user configs are honored;
351
+ // workspace overrides user per Kiro's docs. Schema is standard
352
+ // `mcpServers`.
353
+ {
354
+ id: "kiro",
355
+ name: "Kiro",
356
+ description: "Kiro AI-first IDE",
357
+ configPaths: [
358
+ path.join(process.cwd(), ".kiro", "settings", "mcp.json"),
359
+ path.join(HOME, ".kiro", "settings", "mcp.json")
360
+ ],
361
+ cliBinaries: ["kiro"],
362
+ appBundles: ["dev.kiro.Kiro"],
363
+ usesMcpServersFormat: true,
364
+ serversPath: "mcpServers"
365
+ },
366
+ // ─── OpenCode ───────────────────────────────────────────
367
+ // Project-root configured. Critical wrinkle: the key is `mcp`
368
+ // (not `mcpServers`) — code-review-graph's PLATFORMS dict had
369
+ // this wrong; verified against opencode.ai/docs/mcp-servers.
370
+ // Supports both .json and .jsonc extensions.
371
+ {
372
+ id: "opencode",
373
+ name: "OpenCode",
374
+ description: "OpenCode agentic coding tool",
375
+ configPaths: [
376
+ path.join(process.cwd(), "opencode.json"),
377
+ path.join(process.cwd(), "opencode.jsonc")
378
+ ],
379
+ cliBinaries: ["opencode"],
380
+ appBundles: [],
381
+ usesMcpServersFormat: true,
382
+ serversPath: "mcp"
213
383
  }
214
384
  ];
215
385
  function detectInstalledClients() {
@@ -224,6 +394,12 @@ function detectInstalledClients() {
224
394
  detected = true;
225
395
  configPath = cp;
226
396
  configExists = true;
397
+ if (client.customWriter) {
398
+ if (cp === client.configPaths[0]) {
399
+ alreadyConfigured = client.customInstalledCheck ? client.customInstalledCheck(cp) : true;
400
+ }
401
+ break;
402
+ }
227
403
  try {
228
404
  const content = fs.readFileSync(cp, "utf-8");
229
405
  const config = JSON.parse(content);
@@ -314,6 +490,20 @@ function addCtxloomToConfig(detected) {
314
490
  if (alreadyConfigured) {
315
491
  return { success: true, message: `ctxloom is already configured in ${client.name}` };
316
492
  }
493
+ if (client.customWriter) {
494
+ const targetPath = client.configPaths[0];
495
+ const serverEntry2 = getServerEntry();
496
+ const existingContent = fs.existsSync(targetPath) ? fs.readFileSync(targetPath, "utf-8") : null;
497
+ const content = client.customWriter(targetPath, serverEntry2, existingContent);
498
+ const dir2 = path.dirname(targetPath);
499
+ if (!fs.existsSync(dir2)) fs.mkdirSync(dir2, { recursive: true });
500
+ try {
501
+ fs.writeFileSync(targetPath, content, "utf-8");
502
+ return { success: true, message: `Added ctxloom to ${client.name} (${targetPath})` };
503
+ } catch (err) {
504
+ return { success: false, message: `Failed to write config at ${targetPath}: ${err}` };
505
+ }
506
+ }
317
507
  let config;
318
508
  if (configExists) {
319
509
  try {
@@ -349,4 +539,4 @@ export {
349
539
  detectInstalledClients,
350
540
  addCtxloomToConfig
351
541
  };
352
- //# sourceMappingURL=chunk-II2DPYRJ.js.map
542
+ //# sourceMappingURL=chunk-YHLMQVBV.js.map
@@ -0,0 +1,26 @@
1
+ import {
2
+ EMBEDDING_DIMENSION,
3
+ EMBEDDING_MODEL_ID,
4
+ INDEXER_IGNORED_DIRS,
5
+ collectFiles,
6
+ collectFilesStream,
7
+ generateEmbedding,
8
+ generateEmbeddingBatch,
9
+ getActiveEmbeddingModel,
10
+ indexDirectory,
11
+ resolveEmbeddingModel
12
+ } from "./chunk-6FGTNOCP.js";
13
+ import "./chunk-TYDMSHV7.js";
14
+ export {
15
+ EMBEDDING_DIMENSION,
16
+ EMBEDDING_MODEL_ID,
17
+ INDEXER_IGNORED_DIRS,
18
+ collectFiles,
19
+ collectFilesStream,
20
+ generateEmbedding,
21
+ generateEmbeddingBatch,
22
+ getActiveEmbeddingModel,
23
+ indexDirectory,
24
+ resolveEmbeddingModel
25
+ };
26
+ //# sourceMappingURL=embedder-2JWDJUE2.js.map
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  activateLicense,
23
23
  aliasNotFoundError,
24
24
  captureError,
25
+ cleanupVectors,
25
26
  createToolRegistry,
26
27
  deactivateLicense,
27
28
  ensureVectorsInitialized,
@@ -29,6 +30,7 @@ import {
29
30
  getLicenseInfo,
30
31
  getTelemetryLevel,
31
32
  hashProjectRoot,
33
+ inspectVectorsDb,
32
34
  isActive,
33
35
  loadReviewConfig,
34
36
  noDefaultProjectError,
@@ -45,18 +47,18 @@ import {
45
47
  validateDefaultRoot,
46
48
  wrapWithIndexingEnvelope,
47
49
  writeCODEOWNERS
48
- } from "./chunk-5R4P7VEE.js";
50
+ } from "./chunk-FFCLVZCO.js";
49
51
  import {
50
52
  addCtxloomToConfig,
51
53
  detectInstalledClients
52
- } from "./chunk-II2DPYRJ.js";
54
+ } from "./chunk-YHLMQVBV.js";
53
55
  import {
54
56
  VectorStore
55
- } from "./chunk-R56D54Y7.js";
57
+ } from "./chunk-7S2ELKNU.js";
56
58
  import {
57
59
  generateEmbedding,
58
60
  indexDirectory
59
- } from "./chunk-COH5WYZS.js";
61
+ } from "./chunk-6FGTNOCP.js";
60
62
  import "./chunk-5I6CJITG.js";
61
63
  import {
62
64
  logger
@@ -535,19 +537,36 @@ async function startServer(opts = {}) {
535
537
  });
536
538
  watcher.start();
537
539
  logger.info("File watcher active");
538
- process.on("SIGINT", () => {
539
- if (overlayRefreshTimer) clearTimeout(overlayRefreshTimer);
540
- watcher.stop();
541
- process.exit(0);
542
- });
543
- process.on("SIGTERM", () => {
544
- if (overlayRefreshTimer) clearTimeout(overlayRefreshTimer);
545
- watcher.stop();
540
+ const shutdown = (signal) => {
541
+ const forceExit = setTimeout(() => {
542
+ logger.warn("Shutdown timeout reached, force-exiting", { signal });
543
+ process.exit(1);
544
+ }, 5e3);
545
+ forceExit.unref();
546
+ try {
547
+ if (overlayRefreshTimer) clearTimeout(overlayRefreshTimer);
548
+ watcher.stop();
549
+ } catch (err) {
550
+ logger.warn("Shutdown step failed (non-fatal)", {
551
+ signal,
552
+ detail: err instanceof Error ? err.message : String(err)
553
+ });
554
+ }
546
555
  process.exit(0);
547
- });
556
+ };
557
+ process.on("SIGINT", () => shutdown("SIGINT"));
558
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
548
559
  } else {
549
- process.on("SIGINT", () => process.exit(0));
550
- process.on("SIGTERM", () => process.exit(0));
560
+ const shutdown = (signal) => {
561
+ const forceExit = setTimeout(() => {
562
+ logger.warn("Shutdown timeout reached, force-exiting", { signal });
563
+ process.exit(1);
564
+ }, 5e3);
565
+ forceExit.unref();
566
+ process.exit(0);
567
+ };
568
+ process.on("SIGINT", () => shutdown("SIGINT"));
569
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
551
570
  }
552
571
  }
553
572
 
@@ -1020,7 +1039,7 @@ try {
1020
1039
  } catch {
1021
1040
  }
1022
1041
  var args = process.argv.slice(2);
1023
- var ctxloomVersion = "1.5.5".length > 0 ? "1.5.5" : "dev";
1042
+ var ctxloomVersion = "1.7.0".length > 0 ? "1.7.0" : "dev";
1024
1043
  if (args.includes("--version") || args.includes("-v")) {
1025
1044
  process.stdout.write(`ctxloom ${ctxloomVersion}
1026
1045
  `);
@@ -1093,7 +1112,7 @@ async function checkLicense() {
1093
1112
  if (command !== void 0 && LICENSE_GATE_BYPASS_COMMANDS.has(command)) return;
1094
1113
  const ciKey = process.env["CTXLOOM_LICENSE_KEY"];
1095
1114
  if (ciKey) {
1096
- const { ApiClient } = await import("./src-QMDQDATD.js");
1115
+ const { ApiClient } = await import("./src-QAYZWPSL.js");
1097
1116
  const client = new ApiClient(process.env["CTXLOOM_API_BASE"]);
1098
1117
  try {
1099
1118
  const result = await client.validate(ciKey, "ci-ephemeral");
@@ -1471,7 +1490,7 @@ async function main() {
1471
1490
  }
1472
1491
  if (!skipHarness) {
1473
1492
  process.stdout.write("\n");
1474
- const { installHarness } = await import("./src-QMDQDATD.js");
1493
+ const { installHarness } = await import("./src-QAYZWPSL.js");
1475
1494
  const h = installHarness({ cwd: initRoot, dryRun, force, extraHosts });
1476
1495
  const harnessFiles = [
1477
1496
  h.claudeMd,
@@ -1534,7 +1553,7 @@ async function main() {
1534
1553
  process.exit(1);
1535
1554
  }
1536
1555
  if (alias !== void 0) {
1537
- const { validateAlias } = await import("./src-QMDQDATD.js");
1556
+ const { validateAlias } = await import("./src-QAYZWPSL.js");
1538
1557
  const v = validateAlias(alias);
1539
1558
  if (!v.ok) {
1540
1559
  console.error(`[ctxloom] Invalid alias: ${v.reason}`);
@@ -1607,6 +1626,101 @@ async function main() {
1607
1626
  }
1608
1627
  break;
1609
1628
  }
1629
+ case "vectors-cleanup": {
1630
+ const root = process.cwd();
1631
+ const dryRun = hasFlag("--dry-run");
1632
+ const force = hasFlag("--force");
1633
+ const before = inspectVectorsDb(root);
1634
+ if (before.txn + before.manifest + before.lance === 0) {
1635
+ process.stdout.write(` ${success("No vectors.lancedb to clean up \u2014 nothing to do.")}
1636
+ `);
1637
+ break;
1638
+ }
1639
+ const mb = (before.totalBytes / (1024 * 1024)).toFixed(1);
1640
+ process.stdout.write(header("Vectors cleanup"));
1641
+ process.stdout.write(` ${style.dim("Root:")} ${root}
1642
+ `);
1643
+ process.stdout.write(` ${style.dim("On-disk state:")}
1644
+ `);
1645
+ process.stdout.write(` ${style.bold(String(before.txn).padStart(6))} .txn files
1646
+ `);
1647
+ process.stdout.write(` ${style.bold(String(before.manifest).padStart(6))} .manifest files
1648
+ `);
1649
+ process.stdout.write(` ${style.bold(String(before.lance).padStart(6))} .lance fragments
1650
+ `);
1651
+ process.stdout.write(` ${style.bold(mb.padStart(6))} MB total
1652
+
1653
+ `);
1654
+ const activePids = [];
1655
+ if (!force) {
1656
+ try {
1657
+ const dbPath = `${root}/.ctxloom/vectors.lancedb`;
1658
+ const out = execSync(`lsof +D "${dbPath}" -F p 2>/dev/null || true`, {
1659
+ encoding: "utf-8"
1660
+ });
1661
+ for (const line of out.split("\n")) {
1662
+ if (line.startsWith("p")) {
1663
+ const pid = parseInt(line.slice(1), 10);
1664
+ if (Number.isFinite(pid) && pid !== process.pid) activePids.push(pid);
1665
+ }
1666
+ }
1667
+ } catch {
1668
+ }
1669
+ }
1670
+ if (activePids.length > 0) {
1671
+ const uniq = [...new Set(activePids)];
1672
+ process.stdout.write(
1673
+ ` ${warn(`Refusing to clean \u2014 ${uniq.length} process(es) have files open:`)}
1674
+ `
1675
+ );
1676
+ for (const pid of uniq) {
1677
+ process.stdout.write(` PID ${pid}
1678
+ `);
1679
+ }
1680
+ process.stdout.write(
1681
+ `
1682
+ ${style.dim("Stop those ctxloom MCP servers first (close Claude Code windows or `kill <pid>`),")}
1683
+ `
1684
+ );
1685
+ process.stdout.write(
1686
+ ` ${style.dim("then re-run. Use --force to override (not recommended \u2014 may corrupt the DB).")}
1687
+ `
1688
+ );
1689
+ process.exitCode = 1;
1690
+ break;
1691
+ }
1692
+ const result = cleanupVectors({ rootDir: root, dryRun }, force ? [] : activePids);
1693
+ if (!result.cleaned) {
1694
+ process.stdout.write(` ${warn(`Cleanup skipped: ${result.reason ?? "unknown"}`)}
1695
+ `);
1696
+ break;
1697
+ }
1698
+ if (dryRun) {
1699
+ process.stdout.write(
1700
+ ` ${success(`Dry run \u2014 would have freed ${mb} MB across ${before.txn + before.manifest + before.lance} files.`)}
1701
+ `
1702
+ );
1703
+ process.stdout.write(` ${style.dim("Re-run without --dry-run to actually clean up.")}
1704
+ `);
1705
+ } else {
1706
+ process.stdout.write(` ${success(`Cleanup complete \u2014 freed ${mb} MB.`)}
1707
+ `);
1708
+ if (result.backupPath) {
1709
+ process.stdout.write(` ${style.dim(`Backup: ${result.backupPath}`)}
1710
+ `);
1711
+ process.stdout.write(
1712
+ ` ${style.dim("Delete the backup with `rm -rf` once you confirm the next index works.")}
1713
+ `
1714
+ );
1715
+ }
1716
+ process.stdout.write(
1717
+ `
1718
+ ${style.dim("Next ctxloom run will rebuild embeddings (~30-60s on a mid-sized repo).")}
1719
+ `
1720
+ );
1721
+ }
1722
+ break;
1723
+ }
1610
1724
  case "budget-stats": {
1611
1725
  const windowArg = args.find((a) => a.startsWith("--window="))?.split("=")[1] ?? "14d";
1612
1726
  const toolArg = args.find((a) => a.startsWith("--tool="))?.split("=")[1];
@@ -1798,7 +1912,7 @@ Suggested reviewers for ${files.length} file(s):`);
1798
1912
  process.stderr.write("[ctxloom] --limit must be a non-negative integer (0 for unlimited)\n");
1799
1913
  process.exit(2);
1800
1914
  }
1801
- const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-QMDQDATD.js");
1915
+ const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-QAYZWPSL.js");
1802
1916
  let config;
1803
1917
  try {
1804
1918
  config = await loadRulesConfig(root);
@@ -1822,7 +1936,7 @@ Suggested reviewers for ${files.length} file(s):`);
1822
1936
  }
1823
1937
  let graph;
1824
1938
  if (useSnapshot) {
1825
- const { DependencyGraph: DG } = await import("./src-QMDQDATD.js");
1939
+ const { DependencyGraph: DG } = await import("./src-QAYZWPSL.js");
1826
1940
  graph = new DG();
1827
1941
  const loaded = await graph.loadSnapshotOnly(root);
1828
1942
  if (!loaded) {
@@ -1831,7 +1945,7 @@ Suggested reviewers for ${files.length} file(s):`);
1831
1945
  }
1832
1946
  } else {
1833
1947
  process.stderr.write("[ctxloom] Building dependency graph...\n");
1834
- const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-QMDQDATD.js");
1948
+ const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-QAYZWPSL.js");
1835
1949
  let parser;
1836
1950
  try {
1837
1951
  parser = new ASTParser2();
@@ -1892,6 +2006,9 @@ Usage:
1892
2006
  ctxloom rules check --json Output violations as JSON
1893
2007
  ctxloom rules check --use-snapshot Fast mode: use existing graph snapshot
1894
2008
  ctxloom rules check --limit=N Show first N violations (default 50, 0=unlimited)
2009
+ ctxloom vectors-cleanup Clear accumulated LanceDB version state to free FDs
2010
+ (use --dry-run to preview, --force to skip the
2011
+ active-process safety check)
1895
2012
  ctxloom --version Print installed version and exit
1896
2013
  ctxloom --help Show this help
1897
2014
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  detectInstalledClients
4
- } from "../chunk-II2DPYRJ.js";
4
+ } from "../chunk-YHLMQVBV.js";
5
5
 
6
6
  // src/setup/postinstall.ts
7
7
  var C = {
@@ -61,9 +61,11 @@ import {
61
61
  buildBlastRadiusXml,
62
62
  buildCodeownersBlock,
63
63
  captureError,
64
+ cleanupVectors,
64
65
  computeBlockHmac,
65
66
  computeRiskBreakdown,
66
67
  computeRiskCaps,
68
+ computeSemanticSimilar,
67
69
  createProjectState,
68
70
  createToolRegistry,
69
71
  deactivateLicense,
@@ -89,6 +91,7 @@ import {
89
91
  getTaskBudgetTracker,
90
92
  getTelemetryLevel,
91
93
  hashProjectRoot,
94
+ inspectVectorsDb,
92
95
  installHarness,
93
96
  isActive,
94
97
  isSiloed,
@@ -129,16 +132,16 @@ import {
129
132
  wrapBlock,
130
133
  wrapWithIndexingEnvelope,
131
134
  writeCODEOWNERS
132
- } from "./chunk-5R4P7VEE.js";
135
+ } from "./chunk-FFCLVZCO.js";
133
136
  import {
134
137
  VectorStore
135
- } from "./chunk-R56D54Y7.js";
138
+ } from "./chunk-7S2ELKNU.js";
136
139
  import {
137
140
  EMBEDDING_DIMENSION,
138
141
  collectFiles,
139
142
  generateEmbedding,
140
143
  indexDirectory
141
- } from "./chunk-COH5WYZS.js";
144
+ } from "./chunk-6FGTNOCP.js";
142
145
  import {
143
146
  filenameForDate,
144
147
  readEvents,
@@ -216,10 +219,12 @@ export {
216
219
  buildBlastRadiusXml,
217
220
  buildCodeownersBlock,
218
221
  captureError,
222
+ cleanupVectors,
219
223
  collectFiles,
220
224
  computeBlockHmac,
221
225
  computeRiskBreakdown,
222
226
  computeRiskCaps,
227
+ computeSemanticSimilar,
223
228
  createProjectState,
224
229
  createToolRegistry,
225
230
  deactivateLicense,
@@ -248,6 +253,7 @@ export {
248
253
  getTelemetryLevel,
249
254
  hashProjectRoot,
250
255
  indexDirectory,
256
+ inspectVectorsDb,
251
257
  installHarness,
252
258
  isActive,
253
259
  isSiloed,
@@ -294,4 +300,4 @@ export {
294
300
  wrapWithIndexingEnvelope,
295
301
  writeCODEOWNERS
296
302
  };
297
- //# sourceMappingURL=src-QMDQDATD.js.map
303
+ //# sourceMappingURL=src-QAYZWPSL.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  VectorStore
3
- } from "../chunk-R56D54Y7.js";
3
+ } from "../chunk-7S2ELKNU.js";
4
4
  import {
5
5
  generateEmbedding
6
- } from "../chunk-COH5WYZS.js";
6
+ } from "../chunk-6FGTNOCP.js";
7
7
  import "../chunk-TYDMSHV7.js";
8
8
 
9
9
  // packages/core/src/workers/indexerWorker.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ctxloom-pro",
3
- "version": "1.5.5",
3
+ "version": "1.7.0",
4
4
  "description": "ctxloom — The Universal Code Context Engine. A local-first MCP server providing intelligent code context via hybrid Vector + AST + Graph search with Skeletonization (92% token reduction).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",