ctxloom-pro 1.5.6 → 1.7.1
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 +544 -26
- package/apps/dashboard/package.json +2 -2
- package/dist/VectorStore-WDL3H7QT.js +9 -0
- package/dist/chunk-6FGTNOCP.js +397 -0
- package/dist/{chunk-FPMNXF4D.js → chunk-6W4DFPP2.js} +685 -43
- package/dist/{chunk-JULFFD7O.js → chunk-7S2ELKNU.js} +123 -3
- package/dist/{chunk-II2DPYRJ.js → chunk-YHLMQVBV.js} +200 -10
- package/dist/embedder-2JWDJUE2.js +26 -0
- package/dist/index.js +11 -11
- package/dist/setup/postinstall.js +1 -1
- package/dist/{src-DL44T55H.js → src-FQQOURSD.js} +6 -4
- package/dist/workers/indexerWorker.js +2 -2
- package/package.json +1 -1
- package/dist/VectorStore-2LVECRTY.js +0 -8
- package/dist/chunk-WDX4PJGL.js +0 -214
- package/dist/embedder-3AE4CSR7.js +0 -14
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EMBEDDING_DIMENSION,
|
|
3
|
+
EMBEDDING_MODEL_ID
|
|
4
|
+
} from "./chunk-6FGTNOCP.js";
|
|
1
5
|
import {
|
|
2
6
|
logger
|
|
3
7
|
} from "./chunk-TYDMSHV7.js";
|
|
@@ -34,8 +38,12 @@ var VectorStore = class {
|
|
|
34
38
|
fs.mkdirSync(dir, { recursive: true });
|
|
35
39
|
}
|
|
36
40
|
const lancedb = await import("@lancedb/lancedb");
|
|
41
|
+
const lanceModule = lancedb.default ?? lancedb;
|
|
42
|
+
const lance = lanceModule;
|
|
37
43
|
const { makeArrowTable } = lancedb;
|
|
38
|
-
this.db = await
|
|
44
|
+
this.db = await lance.connect(this.dbPath);
|
|
45
|
+
const markerPath = path.join(path.dirname(this.dbPath), "embedding-model.json");
|
|
46
|
+
this.assertModelCompatibility(markerPath);
|
|
39
47
|
const existingTables = await this.db.tableNames();
|
|
40
48
|
if (existingTables.includes("code_embeddings")) {
|
|
41
49
|
this.table = await this.db.openTable("code_embeddings");
|
|
@@ -44,7 +52,7 @@ var VectorStore = class {
|
|
|
44
52
|
{
|
|
45
53
|
id: "__seed__",
|
|
46
54
|
filePath: "__seed__",
|
|
47
|
-
vector: new Array(
|
|
55
|
+
vector: new Array(EMBEDDING_DIMENSION).fill(0),
|
|
48
56
|
content: ""
|
|
49
57
|
}
|
|
50
58
|
]);
|
|
@@ -53,6 +61,47 @@ var VectorStore = class {
|
|
|
53
61
|
}
|
|
54
62
|
this.initialized = true;
|
|
55
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Compare the active embedding model against the marker file written
|
|
66
|
+
* when the index was first built. Three cases:
|
|
67
|
+
*
|
|
68
|
+
* 1. Marker missing → legacy index (pre-v1.7.0). Assume MiniLM
|
|
69
|
+
* and write the marker so future runs are guarded.
|
|
70
|
+
* 2. Marker matches → proceed silently.
|
|
71
|
+
* 3. Marker differs → throw with a clear migration instruction.
|
|
72
|
+
* We don't auto-wipe — silently dropping a user's index because
|
|
73
|
+
* they set an env var is exactly the footgun the marker exists
|
|
74
|
+
* to prevent.
|
|
75
|
+
*/
|
|
76
|
+
assertModelCompatibility(markerPath) {
|
|
77
|
+
const active = { model: EMBEDDING_MODEL_ID, dim: EMBEDDING_DIMENSION };
|
|
78
|
+
let existing = null;
|
|
79
|
+
if (fs.existsSync(markerPath)) {
|
|
80
|
+
try {
|
|
81
|
+
existing = JSON.parse(fs.readFileSync(markerPath, "utf-8"));
|
|
82
|
+
} catch (err) {
|
|
83
|
+
logger.warn("Embedding-model marker is corrupt; treating as missing", {
|
|
84
|
+
path: markerPath,
|
|
85
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (!existing) {
|
|
90
|
+
fs.writeFileSync(markerPath, JSON.stringify(active, null, 2));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (existing.model === active.model && existing.dim === active.dim) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Embedding-model mismatch: vector index at ${this.dbPath} was built with "${existing.model}" (${existing.dim}-dim) but the active model is "${active.model}" (${active.dim}-dim). Re-index required:
|
|
98
|
+
|
|
99
|
+
ctxloom vectors-cleanup --reset
|
|
100
|
+
ctxloom index
|
|
101
|
+
|
|
102
|
+
Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing index.`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
56
105
|
/**
|
|
57
106
|
* Release LanceDB resources (file descriptors held by the underlying
|
|
58
107
|
* connection / table handles). Must be called at the end of long-lived
|
|
@@ -101,6 +150,48 @@ var VectorStore = class {
|
|
|
101
150
|
await this.compact();
|
|
102
151
|
}
|
|
103
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Batch upsert — one LanceDB transaction for N file records.
|
|
155
|
+
*
|
|
156
|
+
* Why this exists (v1.7.0 monorepo support): the per-file `upsert()`
|
|
157
|
+
* does 2 LanceDB transactions (delete + add). On a 50k-file repo
|
|
158
|
+
* that's 100k transactions, each writing a manifest + transaction
|
|
159
|
+
* file. Compaction reclaims them every 200 upserts, but during the
|
|
160
|
+
* indexing burst the FD churn is enormous (observed: ~10k FDs/sec
|
|
161
|
+
* peak on Next.js, hitting the 256-default macOS process limit).
|
|
162
|
+
*
|
|
163
|
+
* With BATCH_SIZE=50 callers (the streaming indexer), this drops to
|
|
164
|
+
* ~2k transactions across the same corpus — 50× fewer manifests
|
|
165
|
+
* written, ~5× faster wall time, and the FD ceiling stops mattering.
|
|
166
|
+
*
|
|
167
|
+
* Compaction semantics unchanged: `upsertsSinceCompact` increments by
|
|
168
|
+
* one per RECORD (not per batch), so the compact trigger fires at
|
|
169
|
+
* the same total-record cadence as the per-file path.
|
|
170
|
+
*/
|
|
171
|
+
async upsertBatch(records) {
|
|
172
|
+
if (!this.table) throw new Error("VectorStore not initialized. Call init() first.");
|
|
173
|
+
if (records.length === 0) return;
|
|
174
|
+
const filter = records.map((r) => `filePath = '${sanitizeFilterPath(r.filePath)}'`).join(" OR ");
|
|
175
|
+
try {
|
|
176
|
+
await this.table.delete(filter);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
logger.warn("Batch delete before upsert failed, continuing", {
|
|
179
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
const rows = records.map((r) => ({
|
|
183
|
+
id: r.filePath,
|
|
184
|
+
filePath: r.filePath,
|
|
185
|
+
vector: r.embedding,
|
|
186
|
+
content: r.content.slice(0, 512)
|
|
187
|
+
}));
|
|
188
|
+
await this.table.add(rows);
|
|
189
|
+
this.upsertsSinceCompact += records.length;
|
|
190
|
+
if (this.upsertsSinceCompact >= this.compactEvery) {
|
|
191
|
+
this.upsertsSinceCompact = 0;
|
|
192
|
+
await this.compact();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
104
195
|
/**
|
|
105
196
|
* Merge fragments and prune old LanceDB versions. Idempotent and safe to
|
|
106
197
|
* call mid-flight; the Table API serializes writes internally. Called
|
|
@@ -131,6 +222,35 @@ var VectorStore = class {
|
|
|
131
222
|
});
|
|
132
223
|
}
|
|
133
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Retrieve the stored embedding for a known file. Returns null if the
|
|
227
|
+
* file isn't indexed. Used by blast-radius semantic-similarity search
|
|
228
|
+
* (v1.6.x) to find files semantically related to a seed without
|
|
229
|
+
* re-embedding the seed at query time.
|
|
230
|
+
*/
|
|
231
|
+
async findEmbeddingByPath(filePath) {
|
|
232
|
+
if (!this.table) throw new Error("VectorStore not initialized. Call init() first.");
|
|
233
|
+
try {
|
|
234
|
+
const safe = sanitizeFilterPath(filePath);
|
|
235
|
+
const rows = await this.table.query().where(`filePath = '${safe}'`).limit(1).toArray();
|
|
236
|
+
const first = rows[0];
|
|
237
|
+
if (!first) return null;
|
|
238
|
+
const vec = first.vector;
|
|
239
|
+
if (vec === null || vec === void 0) return null;
|
|
240
|
+
if (Array.isArray(vec)) return vec;
|
|
241
|
+
if (vec instanceof Float32Array) return Array.from(vec);
|
|
242
|
+
if (typeof vec.length === "number") {
|
|
243
|
+
return Array.from(vec);
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
} catch (err) {
|
|
247
|
+
logger.warn("VectorStore.findEmbeddingByPath failed", {
|
|
248
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
249
|
+
filePath
|
|
250
|
+
});
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
134
254
|
/**
|
|
135
255
|
* Search for the top-K most similar code records using vector search.
|
|
136
256
|
*/
|
|
@@ -187,4 +307,4 @@ var VectorStore = class {
|
|
|
187
307
|
export {
|
|
188
308
|
VectorStore
|
|
189
309
|
};
|
|
190
|
-
//# sourceMappingURL=chunk-
|
|
310
|
+
//# sourceMappingURL=chunk-7S2ELKNU.js.map
|
|
@@ -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
|
@@ -47,18 +47,18 @@ import {
|
|
|
47
47
|
validateDefaultRoot,
|
|
48
48
|
wrapWithIndexingEnvelope,
|
|
49
49
|
writeCODEOWNERS
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-6W4DFPP2.js";
|
|
51
51
|
import {
|
|
52
52
|
addCtxloomToConfig,
|
|
53
53
|
detectInstalledClients
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-YHLMQVBV.js";
|
|
55
55
|
import {
|
|
56
56
|
VectorStore
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-7S2ELKNU.js";
|
|
58
58
|
import {
|
|
59
59
|
generateEmbedding,
|
|
60
60
|
indexDirectory
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-6FGTNOCP.js";
|
|
62
62
|
import "./chunk-5I6CJITG.js";
|
|
63
63
|
import {
|
|
64
64
|
logger
|
|
@@ -1039,7 +1039,7 @@ try {
|
|
|
1039
1039
|
} catch {
|
|
1040
1040
|
}
|
|
1041
1041
|
var args = process.argv.slice(2);
|
|
1042
|
-
var ctxloomVersion = "1.
|
|
1042
|
+
var ctxloomVersion = "1.7.1".length > 0 ? "1.7.1" : "dev";
|
|
1043
1043
|
if (args.includes("--version") || args.includes("-v")) {
|
|
1044
1044
|
process.stdout.write(`ctxloom ${ctxloomVersion}
|
|
1045
1045
|
`);
|
|
@@ -1112,7 +1112,7 @@ async function checkLicense() {
|
|
|
1112
1112
|
if (command !== void 0 && LICENSE_GATE_BYPASS_COMMANDS.has(command)) return;
|
|
1113
1113
|
const ciKey = process.env["CTXLOOM_LICENSE_KEY"];
|
|
1114
1114
|
if (ciKey) {
|
|
1115
|
-
const { ApiClient } = await import("./src-
|
|
1115
|
+
const { ApiClient } = await import("./src-FQQOURSD.js");
|
|
1116
1116
|
const client = new ApiClient(process.env["CTXLOOM_API_BASE"]);
|
|
1117
1117
|
try {
|
|
1118
1118
|
const result = await client.validate(ciKey, "ci-ephemeral");
|
|
@@ -1490,7 +1490,7 @@ async function main() {
|
|
|
1490
1490
|
}
|
|
1491
1491
|
if (!skipHarness) {
|
|
1492
1492
|
process.stdout.write("\n");
|
|
1493
|
-
const { installHarness } = await import("./src-
|
|
1493
|
+
const { installHarness } = await import("./src-FQQOURSD.js");
|
|
1494
1494
|
const h = installHarness({ cwd: initRoot, dryRun, force, extraHosts });
|
|
1495
1495
|
const harnessFiles = [
|
|
1496
1496
|
h.claudeMd,
|
|
@@ -1553,7 +1553,7 @@ async function main() {
|
|
|
1553
1553
|
process.exit(1);
|
|
1554
1554
|
}
|
|
1555
1555
|
if (alias !== void 0) {
|
|
1556
|
-
const { validateAlias } = await import("./src-
|
|
1556
|
+
const { validateAlias } = await import("./src-FQQOURSD.js");
|
|
1557
1557
|
const v = validateAlias(alias);
|
|
1558
1558
|
if (!v.ok) {
|
|
1559
1559
|
console.error(`[ctxloom] Invalid alias: ${v.reason}`);
|
|
@@ -1912,7 +1912,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1912
1912
|
process.stderr.write("[ctxloom] --limit must be a non-negative integer (0 for unlimited)\n");
|
|
1913
1913
|
process.exit(2);
|
|
1914
1914
|
}
|
|
1915
|
-
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-
|
|
1915
|
+
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-FQQOURSD.js");
|
|
1916
1916
|
let config;
|
|
1917
1917
|
try {
|
|
1918
1918
|
config = await loadRulesConfig(root);
|
|
@@ -1936,7 +1936,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1936
1936
|
}
|
|
1937
1937
|
let graph;
|
|
1938
1938
|
if (useSnapshot) {
|
|
1939
|
-
const { DependencyGraph: DG } = await import("./src-
|
|
1939
|
+
const { DependencyGraph: DG } = await import("./src-FQQOURSD.js");
|
|
1940
1940
|
graph = new DG();
|
|
1941
1941
|
const loaded = await graph.loadSnapshotOnly(root);
|
|
1942
1942
|
if (!loaded) {
|
|
@@ -1945,7 +1945,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1945
1945
|
}
|
|
1946
1946
|
} else {
|
|
1947
1947
|
process.stderr.write("[ctxloom] Building dependency graph...\n");
|
|
1948
|
-
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-
|
|
1948
|
+
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-FQQOURSD.js");
|
|
1949
1949
|
let parser;
|
|
1950
1950
|
try {
|
|
1951
1951
|
parser = new ASTParser2();
|
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
computeBlockHmac,
|
|
66
66
|
computeRiskBreakdown,
|
|
67
67
|
computeRiskCaps,
|
|
68
|
+
computeSemanticSimilar,
|
|
68
69
|
createProjectState,
|
|
69
70
|
createToolRegistry,
|
|
70
71
|
deactivateLicense,
|
|
@@ -131,16 +132,16 @@ import {
|
|
|
131
132
|
wrapBlock,
|
|
132
133
|
wrapWithIndexingEnvelope,
|
|
133
134
|
writeCODEOWNERS
|
|
134
|
-
} from "./chunk-
|
|
135
|
+
} from "./chunk-6W4DFPP2.js";
|
|
135
136
|
import {
|
|
136
137
|
VectorStore
|
|
137
|
-
} from "./chunk-
|
|
138
|
+
} from "./chunk-7S2ELKNU.js";
|
|
138
139
|
import {
|
|
139
140
|
EMBEDDING_DIMENSION,
|
|
140
141
|
collectFiles,
|
|
141
142
|
generateEmbedding,
|
|
142
143
|
indexDirectory
|
|
143
|
-
} from "./chunk-
|
|
144
|
+
} from "./chunk-6FGTNOCP.js";
|
|
144
145
|
import {
|
|
145
146
|
filenameForDate,
|
|
146
147
|
readEvents,
|
|
@@ -223,6 +224,7 @@ export {
|
|
|
223
224
|
computeBlockHmac,
|
|
224
225
|
computeRiskBreakdown,
|
|
225
226
|
computeRiskCaps,
|
|
227
|
+
computeSemanticSimilar,
|
|
226
228
|
createProjectState,
|
|
227
229
|
createToolRegistry,
|
|
228
230
|
deactivateLicense,
|
|
@@ -298,4 +300,4 @@ export {
|
|
|
298
300
|
wrapWithIndexingEnvelope,
|
|
299
301
|
writeCODEOWNERS
|
|
300
302
|
};
|
|
301
|
-
//# sourceMappingURL=src-
|
|
303
|
+
//# sourceMappingURL=src-FQQOURSD.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.1",
|
|
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",
|