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.
- package/README.md +25 -3
- package/apps/dashboard/dist/server/index.js +653 -130
- package/apps/dashboard/package.json +2 -2
- package/dist/VectorStore-WDL3H7QT.js +9 -0
- package/dist/chunk-6FGTNOCP.js +397 -0
- package/dist/{chunk-R56D54Y7.js → chunk-7S2ELKNU.js} +132 -4
- package/dist/{chunk-5R4P7VEE.js → chunk-FFCLVZCO.js} +913 -214
- package/dist/{chunk-II2DPYRJ.js → chunk-YHLMQVBV.js} +200 -10
- package/dist/embedder-2JWDJUE2.js +26 -0
- package/dist/index.js +139 -22
- package/dist/setup/postinstall.js +1 -1
- package/dist/{src-QMDQDATD.js → src-QAYZWPSL.js} +10 -4
- package/dist/workers/indexerWorker.js +2 -2
- package/package.json +1 -1
- package/README.md.bak +0 -832
- package/dist/VectorStore-4VWT2ZMW.js +0 -8
- package/dist/chunk-COH5WYZS.js +0 -214
- package/dist/embedder-7YOG4DFN.js +0 -14
|
@@ -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(
|
|
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: "
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
args
|
|
142
|
-
|
|
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:
|
|
171
|
-
serversPath: "
|
|
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-
|
|
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-
|
|
50
|
+
} from "./chunk-FFCLVZCO.js";
|
|
49
51
|
import {
|
|
50
52
|
addCtxloomToConfig,
|
|
51
53
|
detectInstalledClients
|
|
52
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-YHLMQVBV.js";
|
|
53
55
|
import {
|
|
54
56
|
VectorStore
|
|
55
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-7S2ELKNU.js";
|
|
56
58
|
import {
|
|
57
59
|
generateEmbedding,
|
|
58
60
|
indexDirectory
|
|
59
|
-
} from "./chunk-
|
|
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
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
-
|
|
550
|
-
|
|
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.
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
|
@@ -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-
|
|
135
|
+
} from "./chunk-FFCLVZCO.js";
|
|
133
136
|
import {
|
|
134
137
|
VectorStore
|
|
135
|
-
} from "./chunk-
|
|
138
|
+
} from "./chunk-7S2ELKNU.js";
|
|
136
139
|
import {
|
|
137
140
|
EMBEDDING_DIMENSION,
|
|
138
141
|
collectFiles,
|
|
139
142
|
generateEmbedding,
|
|
140
143
|
indexDirectory
|
|
141
|
-
} from "./chunk-
|
|
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-
|
|
303
|
+
//# sourceMappingURL=src-QAYZWPSL.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VectorStore
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-7S2ELKNU.js";
|
|
4
4
|
import {
|
|
5
5
|
generateEmbedding
|
|
6
|
-
} from "../chunk-
|
|
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.
|
|
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",
|