ctxloom-pro 1.7.7 → 1.7.9
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 +2 -2
- package/apps/dashboard/dist/server/index.js +5 -2
- package/dist/VectorStore-ZA4EEL6C.js +11 -0
- package/dist/{chunk-OEDNX3CV.js → chunk-73ZZ6O4B.js} +25 -12
- package/dist/{chunk-XQEQLXY5.js → chunk-UWQKNIZO.js} +147 -5
- package/dist/{chunk-JZOJC3S7.js → chunk-XJJHX227.js} +2 -2
- package/dist/{embedder-4MM7D3UE.js → embedder-WQMRK5T7.js} +2 -2
- package/dist/index.js +10 -10
- package/dist/{src-HFPCEHYB.js → src-SNHT3SQF.js} +7 -5
- package/dist/workers/indexerWorker.js +2 -2
- package/package.json +1 -1
- package/dist/VectorStore-5ALWL6XF.js +0 -9
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ The full first-run flow is **one install + one trial + one init per project.** E
|
|
|
69
69
|
npm install -g ctxloom-pro
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
> **For local trial / dev use the unpinned command above is fine.** For unattended CI usage, pin to the exact version (`ctxloom-pro@1.7.
|
|
72
|
+
> **For local trial / dev use the unpinned command above is fine.** For unattended CI usage, pin to the exact version (`ctxloom-pro@1.7.9`) so future CLI releases don't silently desync your agent-spec coverage — see the workflow example below.
|
|
73
73
|
|
|
74
74
|
### 2 — Start your free trial (once per email)
|
|
75
75
|
|
|
@@ -383,7 +383,7 @@ jobs:
|
|
|
383
383
|
# Exact pin (not `@^1`) so future CLI releases that add/remove MCP
|
|
384
384
|
# tools don't silently desync your reviewer-agent specs. Bump on
|
|
385
385
|
# every release; see CHANGELOG.md for the live version table.
|
|
386
|
-
- run: npm install -g ctxloom-pro@1.7.
|
|
386
|
+
- run: npm install -g ctxloom-pro@1.7.9
|
|
387
387
|
- run: ctxloom index
|
|
388
388
|
- run: ctxloom rules check --json
|
|
389
389
|
```
|
|
@@ -2929,7 +2929,7 @@ var CallGraphIndex = class _CallGraphIndex {
|
|
|
2929
2929
|
var TS_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".vue"]);
|
|
2930
2930
|
var PY_EXTENSIONS = /* @__PURE__ */ new Set([".py", ".ipynb"]);
|
|
2931
2931
|
var AST_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".py", ".go", ".rs", ".java", ".cs", ".rb", ".kt", ".kts", ".swift", ".ipynb", ".php", ".dart"]);
|
|
2932
|
-
var CTXLOOM_VERSION = "1.7.
|
|
2932
|
+
var CTXLOOM_VERSION = "1.7.9".length > 0 ? "1.7.9" : "dev";
|
|
2933
2933
|
var SNAPSHOT_SCHEMA_VERSION = 2;
|
|
2934
2934
|
function compareCtxloomVersions(snapshotVer, currentVer) {
|
|
2935
2935
|
if (snapshotVer === currentVer) return "same";
|
|
@@ -9113,6 +9113,9 @@ var NEVER = INVALID;
|
|
|
9113
9113
|
var PROJECT_ROOT_DESCRIPTION = "Absolute path or registered alias of the project to operate on. Falls back to CTXLOOM_ROOT env, then server cwd. Register aliases with `ctxloom register <path> --alias <name>`.";
|
|
9114
9114
|
var ProjectRootField = external_exports.string().optional().describe(PROJECT_ROOT_DESCRIPTION);
|
|
9115
9115
|
|
|
9116
|
+
// ../../packages/core/src/tools/status.ts
|
|
9117
|
+
init_VectorStore();
|
|
9118
|
+
|
|
9116
9119
|
// ../../packages/core/src/tools/registry.ts
|
|
9117
9120
|
init_logger();
|
|
9118
9121
|
|
|
@@ -12335,7 +12338,7 @@ function resolveTelemetryLevel() {
|
|
|
12335
12338
|
}
|
|
12336
12339
|
var TELEMETRY_LEVEL = resolveTelemetryLevel();
|
|
12337
12340
|
var TELEMETRY_DISABLED = TELEMETRY_LEVEL === "off";
|
|
12338
|
-
var CTXLOOM_VERSION2 = "1.7.
|
|
12341
|
+
var CTXLOOM_VERSION2 = "1.7.9".length > 0 ? "1.7.9" : "dev";
|
|
12339
12342
|
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
12340
12343
|
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
|
|
12341
12344
|
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
VectorStore
|
|
3
|
-
|
|
2
|
+
VectorStore,
|
|
3
|
+
isCorruptionError
|
|
4
|
+
} from "./chunk-UWQKNIZO.js";
|
|
4
5
|
import {
|
|
5
6
|
collectFiles,
|
|
6
7
|
generateEmbedding,
|
|
7
8
|
isIgnoredDir
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XJJHX227.js";
|
|
9
10
|
import {
|
|
10
11
|
diskSink,
|
|
11
12
|
readEvents
|
|
@@ -2705,7 +2706,7 @@ var CallGraphIndex = class _CallGraphIndex {
|
|
|
2705
2706
|
var TS_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".vue"]);
|
|
2706
2707
|
var PY_EXTENSIONS = /* @__PURE__ */ new Set([".py", ".ipynb"]);
|
|
2707
2708
|
var AST_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".py", ".go", ".rs", ".java", ".cs", ".rb", ".kt", ".kts", ".swift", ".ipynb", ".php", ".dart"]);
|
|
2708
|
-
var CTXLOOM_VERSION = "1.7.
|
|
2709
|
+
var CTXLOOM_VERSION = "1.7.9".length > 0 ? "1.7.9" : "dev";
|
|
2709
2710
|
var SNAPSHOT_SCHEMA_VERSION = 2;
|
|
2710
2711
|
function compareCtxloomVersions(snapshotVer, currentVer) {
|
|
2711
2712
|
if (snapshotVer === currentVer) return "same";
|
|
@@ -5341,7 +5342,7 @@ var ProjectRootField = z.string().optional().describe(PROJECT_ROOT_DESCRIPTION);
|
|
|
5341
5342
|
function escapeXML2(text) {
|
|
5342
5343
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5343
5344
|
}
|
|
5344
|
-
function renderStatusXml(input) {
|
|
5345
|
+
async function renderStatusXml(input) {
|
|
5345
5346
|
const { defaultRoot, manager, registry } = input;
|
|
5346
5347
|
const lines = ["<ctx_status>"];
|
|
5347
5348
|
if (defaultRoot) {
|
|
@@ -5361,9 +5362,21 @@ function renderStatusXml(input) {
|
|
|
5361
5362
|
const reg = registry.list().find((r) => r.root === s.projectRoot);
|
|
5362
5363
|
const alias = reg?.alias ? ` alias="${escapeXML2(reg.alias)}"` : "";
|
|
5363
5364
|
const graphState = s.graphInitialized ? "ready" : s.graphPromise ? "building" : "cold";
|
|
5364
|
-
|
|
5365
|
+
let vectorsState = s.vectorsInitialized ? "ready" : s.storePromise ? "building" : "cold";
|
|
5366
|
+
let vectorsHint = "";
|
|
5367
|
+
if (s.vectorsInitialized && s.storePromise) {
|
|
5368
|
+
try {
|
|
5369
|
+
const store = await s.storePromise;
|
|
5370
|
+
await store.probe();
|
|
5371
|
+
} catch (probeErr) {
|
|
5372
|
+
if (isCorruptionError(probeErr)) {
|
|
5373
|
+
vectorsState = "corrupt";
|
|
5374
|
+
vectorsHint = ' vectors_hint="run: ctxloom vectors-cleanup && ctxloom index"';
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
}
|
|
5365
5378
|
lines.push(
|
|
5366
|
-
` <project root="${escapeXML2(s.projectRoot)}"${alias} pinned="${s.pinned}" graph="${graphState}" vectors="${vectorsState}" last_touched_at="${new Date(s.lastTouchedAt).toISOString()}" />`
|
|
5379
|
+
` <project root="${escapeXML2(s.projectRoot)}"${alias} pinned="${s.pinned}" graph="${graphState}" vectors="${vectorsState}"${vectorsHint} last_touched_at="${new Date(s.lastTouchedAt).toISOString()}" />`
|
|
5367
5380
|
);
|
|
5368
5381
|
}
|
|
5369
5382
|
lines.push(" </active_projects>");
|
|
@@ -5394,7 +5407,7 @@ function registerStatusTool(registry, ctx) {
|
|
|
5394
5407
|
async (args) => {
|
|
5395
5408
|
const { project_root } = Schema31.parse(args ?? {});
|
|
5396
5409
|
void project_root;
|
|
5397
|
-
return renderStatusXml({
|
|
5410
|
+
return await renderStatusXml({
|
|
5398
5411
|
defaultRoot: ctx.noDefaultMode ? null : ctx.projectRoot,
|
|
5399
5412
|
manager: ctx.stateManager,
|
|
5400
5413
|
registry: ctx.registry
|
|
@@ -8867,7 +8880,7 @@ function registerFullTextSearchTool(registry, ctx) {
|
|
|
8867
8880
|
};
|
|
8868
8881
|
if (mode === "semantic") {
|
|
8869
8882
|
try {
|
|
8870
|
-
const { generateEmbedding: generateEmbedding2 } = await import("./embedder-
|
|
8883
|
+
const { generateEmbedding: generateEmbedding2 } = await import("./embedder-WQMRK5T7.js");
|
|
8871
8884
|
const store = await ctx.getStore(project_root);
|
|
8872
8885
|
const embedding = await generateEmbedding2(query);
|
|
8873
8886
|
const results = await store.search(embedding, limit);
|
|
@@ -8905,7 +8918,7 @@ function registerFullTextSearchTool(registry, ctx) {
|
|
|
8905
8918
|
let merged = keywordResults.slice(0, limit);
|
|
8906
8919
|
if (mode === "hybrid") {
|
|
8907
8920
|
try {
|
|
8908
|
-
const { generateEmbedding: generateEmbedding2 } = await import("./embedder-
|
|
8921
|
+
const { generateEmbedding: generateEmbedding2 } = await import("./embedder-WQMRK5T7.js");
|
|
8909
8922
|
const store = await ctx.getStore(project_root);
|
|
8910
8923
|
const embedding = await generateEmbedding2(query);
|
|
8911
8924
|
const vectorResults = await store.search(embedding, Math.ceil(limit / 2));
|
|
@@ -11142,7 +11155,7 @@ var TELEMETRY_DISABLED = TELEMETRY_LEVEL === "off";
|
|
|
11142
11155
|
function getTelemetryLevel() {
|
|
11143
11156
|
return TELEMETRY_LEVEL;
|
|
11144
11157
|
}
|
|
11145
|
-
var CTXLOOM_VERSION2 = "1.7.
|
|
11158
|
+
var CTXLOOM_VERSION2 = "1.7.9".length > 0 ? "1.7.9" : "dev";
|
|
11146
11159
|
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
11147
11160
|
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
|
|
11148
11161
|
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
|
|
@@ -12828,4 +12841,4 @@ export {
|
|
|
12828
12841
|
skillFilePath,
|
|
12829
12842
|
installHarness
|
|
12830
12843
|
};
|
|
12831
|
-
//# sourceMappingURL=chunk-
|
|
12844
|
+
//# sourceMappingURL=chunk-73ZZ6O4B.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EMBEDDING_DIMENSION,
|
|
3
3
|
EMBEDDING_MODEL_ID
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XJJHX227.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
7
|
} from "./chunk-TYDMSHV7.js";
|
|
@@ -12,6 +12,11 @@ import fs from "fs";
|
|
|
12
12
|
function sanitizeFilterPath(filePath) {
|
|
13
13
|
return filePath.replace(/[^a-zA-Z0-9/._\- ]/g, "_");
|
|
14
14
|
}
|
|
15
|
+
var VECTOR_RECOVERY_CMD = "ctxloom vectors-cleanup && ctxloom index";
|
|
16
|
+
function isCorruptionError(err) {
|
|
17
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
18
|
+
return /not found/i.test(msg) && /\.lance|\.arrow|_deletions|_versions|\/data\//.test(msg);
|
|
19
|
+
}
|
|
15
20
|
var VectorStore = class {
|
|
16
21
|
dbPath;
|
|
17
22
|
db = null;
|
|
@@ -26,6 +31,27 @@ var VectorStore = class {
|
|
|
26
31
|
upsertsSinceCompact = 0;
|
|
27
32
|
compactEvery;
|
|
28
33
|
cleanupOlderThanMs;
|
|
34
|
+
/**
|
|
35
|
+
* Hot-reload bookkeeping (v1.7.8). A live MCP server pins one open
|
|
36
|
+
* LanceDB table handle. When a terminal `ctxloom index` rewrites the
|
|
37
|
+
* store on the same path, the pinned handle keeps serving the OLD
|
|
38
|
+
* version — `ctx_search` returns stale/empty results until restart
|
|
39
|
+
* (the vector analogue of the graph hot-reload shipped in v1.7.5).
|
|
40
|
+
*
|
|
41
|
+
* Fix: before each read we cheaply stat the LanceDB `_versions`
|
|
42
|
+
* directory mtime. If it advanced past the last write WE made, an
|
|
43
|
+
* external writer touched the store → call `table.checkoutLatest()`
|
|
44
|
+
* to re-point the handle at the newest version. The mtime gate makes
|
|
45
|
+
* idle searches free and — critically — prevents the server's OWN
|
|
46
|
+
* continuous upserts (incremental file-watch indexing) from
|
|
47
|
+
* triggering a refresh storm: every own write updates
|
|
48
|
+
* `lastKnownMtime`, so the next read sees no advance.
|
|
49
|
+
*
|
|
50
|
+
* `externalRefreshCount` is exposed for tests + telemetry to prove
|
|
51
|
+
* own-writes don't cause redundant refreshes.
|
|
52
|
+
*/
|
|
53
|
+
lastKnownMtimeMs = 0;
|
|
54
|
+
externalRefreshCount = 0;
|
|
29
55
|
constructor(dbPath, options = {}) {
|
|
30
56
|
this.dbPath = dbPath ?? path.join(process.cwd(), ".ctxloom", "vectors.lancedb");
|
|
31
57
|
this.compactEvery = options.compactEvery ?? 200;
|
|
@@ -60,6 +86,87 @@ var VectorStore = class {
|
|
|
60
86
|
await this.table.delete("id = '__seed__'");
|
|
61
87
|
}
|
|
62
88
|
this.initialized = true;
|
|
89
|
+
this.markSynced();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Absolute path to the LanceDB table's `_versions` manifest directory.
|
|
93
|
+
* A new manifest file lands here on every committed write, so the
|
|
94
|
+
* directory's mtime is a cheap external-change signal.
|
|
95
|
+
*/
|
|
96
|
+
versionsDir() {
|
|
97
|
+
return path.join(this.dbPath, "code_embeddings.lance", "_versions");
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Newest mtime (ms) among the manifest FILES inside `_versions`, or 0
|
|
101
|
+
* if unreadable. We scan files, NOT the directory itself: LanceDB does
|
|
102
|
+
* NOT bump the `_versions` directory's own mtime when it adds a new
|
|
103
|
+
* manifest (verified empirically against @lancedb/lancedb 0.27.x —
|
|
104
|
+
* the dir mtime stays frozen at creation time), so statting the dir is
|
|
105
|
+
* a dead signal. Each committed version writes a fresh manifest file,
|
|
106
|
+
* so max-file-mtime advances monotonically per version and survives
|
|
107
|
+
* compaction (which writes new manifests with current timestamps).
|
|
108
|
+
*/
|
|
109
|
+
versionsMtimeMs() {
|
|
110
|
+
const dir = this.versionsDir();
|
|
111
|
+
let newest = 0;
|
|
112
|
+
try {
|
|
113
|
+
for (const name of fs.readdirSync(dir)) {
|
|
114
|
+
if (name.startsWith(".")) continue;
|
|
115
|
+
try {
|
|
116
|
+
const st = fs.statSync(path.join(dir, name));
|
|
117
|
+
if (st.isFile() && st.mtimeMs > newest) newest = st.mtimeMs;
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
return newest;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Mark the store as in-sync with the current on-disk state. Called
|
|
128
|
+
* after init and after every mutation WE perform (upsert, upsertBatch,
|
|
129
|
+
* remove, compact) so a subsequent refreshIfStale() does not mistake
|
|
130
|
+
* our own write for an external one.
|
|
131
|
+
*/
|
|
132
|
+
markSynced() {
|
|
133
|
+
this.lastKnownMtimeMs = this.versionsMtimeMs();
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Re-point the pinned table handle at the newest on-disk version IF an
|
|
137
|
+
* external writer (a terminal `ctxloom index` on the same path) has
|
|
138
|
+
* advanced the store since our last write. Cheap mtime gate first; the
|
|
139
|
+
* LanceDB `checkoutLatest()` call only happens on a real external
|
|
140
|
+
* change. Best-effort — a failed refresh keeps the current handle and
|
|
141
|
+
* logs a warning rather than breaking the read.
|
|
142
|
+
*/
|
|
143
|
+
async refreshIfStale() {
|
|
144
|
+
if (!this.table) return;
|
|
145
|
+
const diskMtime = this.versionsMtimeMs();
|
|
146
|
+
if (diskMtime === 0 || diskMtime <= this.lastKnownMtimeMs) return;
|
|
147
|
+
try {
|
|
148
|
+
const table = this.table;
|
|
149
|
+
if (typeof table.checkoutLatest === "function") {
|
|
150
|
+
await table.checkoutLatest();
|
|
151
|
+
}
|
|
152
|
+
this.lastKnownMtimeMs = this.versionsMtimeMs();
|
|
153
|
+
this.externalRefreshCount += 1;
|
|
154
|
+
logger.info("VectorStore: external write detected, handle refreshed", {
|
|
155
|
+
refreshCount: this.externalRefreshCount
|
|
156
|
+
});
|
|
157
|
+
} catch (err) {
|
|
158
|
+
logger.warn("VectorStore.refreshIfStale failed; keeping current handle", {
|
|
159
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Number of times the pinned handle was refreshed due to an EXTERNAL
|
|
165
|
+
* write. Exposed for tests (own-writes must not increment this) and
|
|
166
|
+
* for telemetry. @internal
|
|
167
|
+
*/
|
|
168
|
+
getExternalRefreshCount() {
|
|
169
|
+
return this.externalRefreshCount;
|
|
63
170
|
}
|
|
64
171
|
/**
|
|
65
172
|
* Compare the active embedding model against the marker file written
|
|
@@ -96,8 +203,7 @@ var VectorStore = class {
|
|
|
96
203
|
throw new Error(
|
|
97
204
|
`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
205
|
|
|
99
|
-
|
|
100
|
-
ctxloom index
|
|
206
|
+
${VECTOR_RECOVERY_CMD}
|
|
101
207
|
|
|
102
208
|
Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing index.`
|
|
103
209
|
);
|
|
@@ -125,6 +231,7 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
125
231
|
this.db = null;
|
|
126
232
|
this.table = null;
|
|
127
233
|
this.initialized = false;
|
|
234
|
+
this.lastKnownMtimeMs = 0;
|
|
128
235
|
}
|
|
129
236
|
/**
|
|
130
237
|
* Insert or update a code record.
|
|
@@ -144,6 +251,7 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
144
251
|
content: content.slice(0, 512)
|
|
145
252
|
};
|
|
146
253
|
await this.table.add([record]);
|
|
254
|
+
this.markSynced();
|
|
147
255
|
this.upsertsSinceCompact++;
|
|
148
256
|
if (this.upsertsSinceCompact >= this.compactEvery) {
|
|
149
257
|
this.upsertsSinceCompact = 0;
|
|
@@ -186,6 +294,7 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
186
294
|
content: r.content.slice(0, 512)
|
|
187
295
|
}));
|
|
188
296
|
await this.table.add(rows);
|
|
297
|
+
this.markSynced();
|
|
189
298
|
this.upsertsSinceCompact += records.length;
|
|
190
299
|
if (this.upsertsSinceCompact >= this.compactEvery) {
|
|
191
300
|
this.upsertsSinceCompact = 0;
|
|
@@ -216,6 +325,7 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
216
325
|
bytesRemoved: result.prune?.bytesRemoved ?? 0
|
|
217
326
|
});
|
|
218
327
|
}
|
|
328
|
+
this.markSynced();
|
|
219
329
|
} catch (err) {
|
|
220
330
|
logger.warn("VectorStore.compact failed (non-fatal)", {
|
|
221
331
|
detail: err instanceof Error ? err.message : String(err)
|
|
@@ -230,6 +340,7 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
230
340
|
*/
|
|
231
341
|
async findEmbeddingByPath(filePath) {
|
|
232
342
|
if (!this.table) throw new Error("VectorStore not initialized. Call init() first.");
|
|
343
|
+
await this.refreshIfStale();
|
|
233
344
|
try {
|
|
234
345
|
const safe = sanitizeFilterPath(filePath);
|
|
235
346
|
const rows = await this.table.query().where(`filePath = '${safe}'`).limit(1).toArray();
|
|
@@ -244,6 +355,11 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
244
355
|
}
|
|
245
356
|
return null;
|
|
246
357
|
} catch (err) {
|
|
358
|
+
if (isCorruptionError(err)) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
`VectorStore corrupt at ${this.dbPath}: manifest references a missing fragment (${err instanceof Error ? err.message : String(err)}). Recover with: ${VECTOR_RECOVERY_CMD}`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
247
363
|
logger.warn("VectorStore.findEmbeddingByPath failed", {
|
|
248
364
|
detail: err instanceof Error ? err.message : String(err),
|
|
249
365
|
filePath
|
|
@@ -256,6 +372,7 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
256
372
|
*/
|
|
257
373
|
async search(queryEmbedding, limit = 10) {
|
|
258
374
|
if (!this.table) throw new Error("VectorStore not initialized. Call init() first.");
|
|
375
|
+
await this.refreshIfStale();
|
|
259
376
|
try {
|
|
260
377
|
const results = await this.table.vectorSearch(queryEmbedding).limit(limit).toArray();
|
|
261
378
|
return results.filter((r) => r.id !== "__seed__").map((r) => ({
|
|
@@ -264,6 +381,11 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
264
381
|
score: Number(r._distance ?? 0)
|
|
265
382
|
}));
|
|
266
383
|
} catch (err) {
|
|
384
|
+
if (isCorruptionError(err)) {
|
|
385
|
+
throw new Error(
|
|
386
|
+
`VectorStore corrupt at ${this.dbPath}: manifest references a missing fragment (${err instanceof Error ? err.message : String(err)}). Recover with: ${VECTOR_RECOVERY_CMD}`
|
|
387
|
+
);
|
|
388
|
+
}
|
|
267
389
|
logger.warn("Search failed, attempting to create index", { detail: String(err) });
|
|
268
390
|
try {
|
|
269
391
|
await this.table.createIndex("vector");
|
|
@@ -273,7 +395,12 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
273
395
|
content: String(r.content ?? ""),
|
|
274
396
|
score: Number(r._distance ?? 0)
|
|
275
397
|
}));
|
|
276
|
-
} catch {
|
|
398
|
+
} catch (retryErr) {
|
|
399
|
+
if (isCorruptionError(retryErr)) {
|
|
400
|
+
throw new Error(
|
|
401
|
+
`VectorStore corrupt at ${this.dbPath}: manifest references a missing fragment (${retryErr instanceof Error ? retryErr.message : String(retryErr)}). Recover with: ${VECTOR_RECOVERY_CMD}`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
277
404
|
return [];
|
|
278
405
|
}
|
|
279
406
|
}
|
|
@@ -286,6 +413,7 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
286
413
|
const safe = sanitizeFilterPath(filePath);
|
|
287
414
|
try {
|
|
288
415
|
await this.table.delete(`filePath = '${safe}'`);
|
|
416
|
+
this.markSynced();
|
|
289
417
|
} catch (err) {
|
|
290
418
|
logger.error("Remove failed", { detail: err instanceof Error ? err.message : String(err) });
|
|
291
419
|
}
|
|
@@ -295,6 +423,7 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
295
423
|
*/
|
|
296
424
|
async count() {
|
|
297
425
|
if (!this.table) return 0;
|
|
426
|
+
await this.refreshIfStale();
|
|
298
427
|
try {
|
|
299
428
|
return await this.table.countRows();
|
|
300
429
|
} catch (err) {
|
|
@@ -302,9 +431,22 @@ Or revert CTXLOOM_EMBEDDING_MODEL to "${existing.model}" to keep the existing in
|
|
|
302
431
|
return 0;
|
|
303
432
|
}
|
|
304
433
|
}
|
|
434
|
+
/**
|
|
435
|
+
* Liveness probe for ctx_status. Performs an actual 1-row READ (not a
|
|
436
|
+
* metadata `count()`, which reads the intact manifest and returns a
|
|
437
|
+
* stale count on a corrupt store). Throws the underlying lance error
|
|
438
|
+
* if a referenced fragment is missing — callers use isCorruptionError()
|
|
439
|
+
* to classify. Resolves silently on a healthy store (including an
|
|
440
|
+
* empty one, which simply yields zero rows). Cheap: a single-row scan.
|
|
441
|
+
*/
|
|
442
|
+
async probe() {
|
|
443
|
+
if (!this.table) throw new Error("VectorStore not initialized. Call init() first.");
|
|
444
|
+
await this.table.query().limit(1).toArray();
|
|
445
|
+
}
|
|
305
446
|
};
|
|
306
447
|
|
|
307
448
|
export {
|
|
449
|
+
isCorruptionError,
|
|
308
450
|
VectorStore
|
|
309
451
|
};
|
|
310
|
-
//# sourceMappingURL=chunk-
|
|
452
|
+
//# sourceMappingURL=chunk-UWQKNIZO.js.map
|
|
@@ -253,7 +253,7 @@ var INDEX_SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
253
253
|
".ipynb"
|
|
254
254
|
]);
|
|
255
255
|
async function indexDirectory(rootDir, onProgress) {
|
|
256
|
-
const { VectorStore } = await import("./VectorStore-
|
|
256
|
+
const { VectorStore } = await import("./VectorStore-ZA4EEL6C.js");
|
|
257
257
|
const store = new VectorStore(path.join(rootDir, ".ctxloom", "vectors.lancedb"));
|
|
258
258
|
await store.init();
|
|
259
259
|
let indexed = 0;
|
|
@@ -366,4 +366,4 @@ export {
|
|
|
366
366
|
EMBEDDING_MODEL_ID,
|
|
367
367
|
getActiveEmbeddingModel
|
|
368
368
|
};
|
|
369
|
-
//# sourceMappingURL=chunk-
|
|
369
|
+
//# sourceMappingURL=chunk-XJJHX227.js.map
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
indexDirectory,
|
|
11
11
|
isIgnoredDir,
|
|
12
12
|
resolveEmbeddingModel
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-XJJHX227.js";
|
|
14
14
|
import "./chunk-TYDMSHV7.js";
|
|
15
15
|
export {
|
|
16
16
|
EMBEDDING_DIMENSION,
|
|
@@ -25,4 +25,4 @@ export {
|
|
|
25
25
|
isIgnoredDir,
|
|
26
26
|
resolveEmbeddingModel
|
|
27
27
|
};
|
|
28
|
-
//# sourceMappingURL=embedder-
|
|
28
|
+
//# sourceMappingURL=embedder-WQMRK5T7.js.map
|
package/dist/index.js
CHANGED
|
@@ -47,19 +47,19 @@ import {
|
|
|
47
47
|
validateDefaultRoot,
|
|
48
48
|
wrapWithIndexingEnvelope,
|
|
49
49
|
writeCODEOWNERS
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-73ZZ6O4B.js";
|
|
51
51
|
import {
|
|
52
52
|
addCtxloomToConfig,
|
|
53
53
|
detectInstalledClients
|
|
54
54
|
} from "./chunk-YHLMQVBV.js";
|
|
55
55
|
import {
|
|
56
56
|
VectorStore
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-UWQKNIZO.js";
|
|
58
58
|
import {
|
|
59
59
|
collectFiles,
|
|
60
60
|
generateEmbedding,
|
|
61
61
|
indexDirectory
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-XJJHX227.js";
|
|
63
63
|
import "./chunk-5I6CJITG.js";
|
|
64
64
|
import {
|
|
65
65
|
logger
|
|
@@ -1068,7 +1068,7 @@ try {
|
|
|
1068
1068
|
} catch {
|
|
1069
1069
|
}
|
|
1070
1070
|
var args = process.argv.slice(2);
|
|
1071
|
-
var ctxloomVersion = "1.7.
|
|
1071
|
+
var ctxloomVersion = "1.7.9".length > 0 ? "1.7.9" : "dev";
|
|
1072
1072
|
if (args.includes("--version") || args.includes("-v")) {
|
|
1073
1073
|
process.stdout.write(`ctxloom ${ctxloomVersion}
|
|
1074
1074
|
`);
|
|
@@ -1163,7 +1163,7 @@ async function checkLicense() {
|
|
|
1163
1163
|
if (command !== void 0 && LICENSE_GATE_BYPASS_COMMANDS.has(command)) return;
|
|
1164
1164
|
const ciKey = process.env["CTXLOOM_LICENSE_KEY"];
|
|
1165
1165
|
if (ciKey) {
|
|
1166
|
-
const { ApiClient } = await import("./src-
|
|
1166
|
+
const { ApiClient } = await import("./src-SNHT3SQF.js");
|
|
1167
1167
|
const client = new ApiClient(process.env["CTXLOOM_API_BASE"]);
|
|
1168
1168
|
try {
|
|
1169
1169
|
const result = await client.validate(ciKey, "ci-ephemeral");
|
|
@@ -1556,7 +1556,7 @@ async function main() {
|
|
|
1556
1556
|
}
|
|
1557
1557
|
if (!skipHarness) {
|
|
1558
1558
|
process.stdout.write("\n");
|
|
1559
|
-
const { installHarness } = await import("./src-
|
|
1559
|
+
const { installHarness } = await import("./src-SNHT3SQF.js");
|
|
1560
1560
|
const h = installHarness({ cwd: initRoot, dryRun, force, extraHosts });
|
|
1561
1561
|
const harnessFiles = [
|
|
1562
1562
|
h.claudeMd,
|
|
@@ -1619,7 +1619,7 @@ async function main() {
|
|
|
1619
1619
|
process.exit(1);
|
|
1620
1620
|
}
|
|
1621
1621
|
if (alias !== void 0) {
|
|
1622
|
-
const { validateAlias } = await import("./src-
|
|
1622
|
+
const { validateAlias } = await import("./src-SNHT3SQF.js");
|
|
1623
1623
|
const v = validateAlias(alias);
|
|
1624
1624
|
if (!v.ok) {
|
|
1625
1625
|
console.error(`[ctxloom] Invalid alias: ${v.reason}`);
|
|
@@ -1978,7 +1978,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1978
1978
|
process.stderr.write("[ctxloom] --limit must be a non-negative integer (0 for unlimited)\n");
|
|
1979
1979
|
process.exit(2);
|
|
1980
1980
|
}
|
|
1981
|
-
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-
|
|
1981
|
+
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-SNHT3SQF.js");
|
|
1982
1982
|
let config;
|
|
1983
1983
|
try {
|
|
1984
1984
|
config = await loadRulesConfig(root);
|
|
@@ -2002,7 +2002,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
2002
2002
|
}
|
|
2003
2003
|
let graph;
|
|
2004
2004
|
if (useSnapshot) {
|
|
2005
|
-
const { DependencyGraph: DG } = await import("./src-
|
|
2005
|
+
const { DependencyGraph: DG } = await import("./src-SNHT3SQF.js");
|
|
2006
2006
|
graph = new DG();
|
|
2007
2007
|
const loaded = await graph.loadSnapshotOnly(root);
|
|
2008
2008
|
if (!loaded) {
|
|
@@ -2011,7 +2011,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
2011
2011
|
}
|
|
2012
2012
|
} else {
|
|
2013
2013
|
process.stderr.write("[ctxloom] Building dependency graph...\n");
|
|
2014
|
-
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-
|
|
2014
|
+
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-SNHT3SQF.js");
|
|
2015
2015
|
let parser;
|
|
2016
2016
|
try {
|
|
2017
2017
|
parser = new ASTParser2();
|
|
@@ -132,10 +132,11 @@ import {
|
|
|
132
132
|
wrapBlock,
|
|
133
133
|
wrapWithIndexingEnvelope,
|
|
134
134
|
writeCODEOWNERS
|
|
135
|
-
} from "./chunk-
|
|
135
|
+
} from "./chunk-73ZZ6O4B.js";
|
|
136
136
|
import {
|
|
137
|
-
VectorStore
|
|
138
|
-
|
|
137
|
+
VectorStore,
|
|
138
|
+
isCorruptionError
|
|
139
|
+
} from "./chunk-UWQKNIZO.js";
|
|
139
140
|
import {
|
|
140
141
|
EMBEDDING_DIMENSION,
|
|
141
142
|
INDEXER_IGNORED_DIRS,
|
|
@@ -143,7 +144,7 @@ import {
|
|
|
143
144
|
generateEmbedding,
|
|
144
145
|
indexDirectory,
|
|
145
146
|
isIgnoredDir
|
|
146
|
-
} from "./chunk-
|
|
147
|
+
} from "./chunk-XJJHX227.js";
|
|
147
148
|
import {
|
|
148
149
|
filenameForDate,
|
|
149
150
|
readEvents,
|
|
@@ -259,6 +260,7 @@ export {
|
|
|
259
260
|
inspectVectorsDb,
|
|
260
261
|
installHarness,
|
|
261
262
|
isActive,
|
|
263
|
+
isCorruptionError,
|
|
262
264
|
isIgnoredDir,
|
|
263
265
|
isSiloed,
|
|
264
266
|
learnSuggestionsFromTelemetry,
|
|
@@ -304,4 +306,4 @@ export {
|
|
|
304
306
|
wrapWithIndexingEnvelope,
|
|
305
307
|
writeCODEOWNERS
|
|
306
308
|
};
|
|
307
|
-
//# sourceMappingURL=src-
|
|
309
|
+
//# sourceMappingURL=src-SNHT3SQF.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VectorStore
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-UWQKNIZO.js";
|
|
4
4
|
import {
|
|
5
5
|
generateEmbedding
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-XJJHX227.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.7.
|
|
3
|
+
"version": "1.7.9",
|
|
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",
|