codesift-mcp 0.11.0 → 0.13.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 +44 -1
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +80 -8
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/embed-child.js +3 -0
- package/dist/cli/embed-child.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +25 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/orphan-guard.d.ts +34 -0
- package/dist/cli/orphan-guard.d.ts.map +1 -0
- package/dist/cli/orphan-guard.js +52 -0
- package/dist/cli/orphan-guard.js.map +1 -0
- package/dist/cli/owner-only-file.d.ts +51 -0
- package/dist/cli/owner-only-file.d.ts.map +1 -0
- package/dist/cli/owner-only-file.js +121 -0
- package/dist/cli/owner-only-file.js.map +1 -0
- package/dist/cli/service.d.ts +17 -1
- package/dist/cli/service.d.ts.map +1 -1
- package/dist/cli/service.js +28 -12
- package/dist/cli/service.js.map +1 -1
- package/dist/cli/setup/codex.d.ts.map +1 -1
- package/dist/cli/setup/codex.js +14 -7
- package/dist/cli/setup/codex.js.map +1 -1
- package/dist/cli/setup/fs.d.ts +12 -0
- package/dist/cli/setup/fs.d.ts.map +1 -1
- package/dist/cli/setup/fs.js +17 -2
- package/dist/cli/setup/fs.js.map +1 -1
- package/dist/cli/setup/mcp.d.ts +37 -1
- package/dist/cli/setup/mcp.d.ts.map +1 -1
- package/dist/cli/setup/mcp.js +78 -3
- package/dist/cli/setup/mcp.js.map +1 -1
- package/dist/cli/setup/types.d.ts +24 -0
- package/dist/cli/setup/types.d.ts.map +1 -1
- package/dist/register-tool-groups/handler-wrappers.d.ts.map +1 -1
- package/dist/register-tool-groups/handler-wrappers.js +20 -6
- package/dist/register-tool-groups/handler-wrappers.js.map +1 -1
- package/dist/register-tools/runtime.d.ts +18 -9
- package/dist/register-tools/runtime.d.ts.map +1 -1
- package/dist/register-tools/runtime.js +54 -20
- package/dist/register-tools/runtime.js.map +1 -1
- package/dist/register-tools.d.ts +1 -1
- package/dist/register-tools.d.ts.map +1 -1
- package/dist/register-tools.js +12 -6
- package/dist/register-tools.js.map +1 -1
- package/dist/server-helpers/repo-resolution.d.ts.map +1 -1
- package/dist/server-helpers/repo-resolution.js +22 -1
- package/dist/server-helpers/repo-resolution.js.map +1 -1
- package/dist/server-helpers/request-context.d.ts +59 -0
- package/dist/server-helpers/request-context.d.ts.map +1 -0
- package/dist/server-helpers/request-context.js +31 -0
- package/dist/server-helpers/request-context.js.map +1 -0
- package/dist/server-helpers/response-hints.d.ts.map +1 -1
- package/dist/server-helpers/response-hints.js +2 -1
- package/dist/server-helpers/response-hints.js.map +1 -1
- package/dist/server.d.ts +1 -11
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +124 -76
- package/dist/server.js.map +1 -1
- package/dist/storage/embedding-store.d.ts +26 -6
- package/dist/storage/embedding-store.d.ts.map +1 -1
- package/dist/storage/embedding-store.js +85 -4
- package/dist/storage/embedding-store.js.map +1 -1
- package/dist/storage/index-store.d.ts +31 -0
- package/dist/storage/index-store.d.ts.map +1 -1
- package/dist/storage/index-store.js +293 -10
- package/dist/storage/index-store.js.map +1 -1
- package/dist/storage/shared-embedding-cache.d.ts +29 -0
- package/dist/storage/shared-embedding-cache.d.ts.map +1 -0
- package/dist/storage/shared-embedding-cache.js +120 -0
- package/dist/storage/shared-embedding-cache.js.map +1 -0
- package/dist/storage/sqlite-index-store.d.ts +89 -0
- package/dist/storage/sqlite-index-store.d.ts.map +1 -0
- package/dist/storage/sqlite-index-store.js +593 -0
- package/dist/storage/sqlite-index-store.js.map +1 -0
- package/dist/tools/index-tools/file-indexer.js +19 -19
- package/dist/tools/index-tools/file-indexer.js.map +1 -1
- package/dist/tools/index-tools/folder-indexer.d.ts.map +1 -1
- package/dist/tools/index-tools/folder-indexer.js +29 -1
- package/dist/tools/index-tools/folder-indexer.js.map +1 -1
- package/dist/tools/index-tools/folder-merge.d.ts.map +1 -1
- package/dist/tools/index-tools/folder-merge.js +47 -1
- package/dist/tools/index-tools/folder-merge.js.map +1 -1
- package/dist/tools/index-tools/parse.d.ts.map +1 -1
- package/dist/tools/index-tools/parse.js +10 -3
- package/dist/tools/index-tools/parse.js.map +1 -1
- package/dist/tools/index-tools/registry.d.ts.map +1 -1
- package/dist/tools/index-tools/registry.js +47 -1
- package/dist/tools/index-tools/registry.js.map +1 -1
- package/dist/tools/index-tools/types.d.ts +7 -0
- package/dist/tools/index-tools/types.d.ts.map +1 -1
- package/dist/tools/search-tools/ripgrep.d.ts.map +1 -1
- package/dist/tools/search-tools/ripgrep.js +32 -2
- package/dist/tools/search-tools/ripgrep.js.map +1 -1
- package/dist/tools/status-tools.d.ts +10 -0
- package/dist/tools/status-tools.d.ts.map +1 -1
- package/dist/tools/status-tools.js +57 -16
- package/dist/tools/status-tools.js.map +1 -1
- package/dist/tools/workspace-scope-helper.d.ts.map +1 -1
- package/dist/tools/workspace-scope-helper.js +8 -1
- package/dist/tools/workspace-scope-helper.js.map +1 -1
- package/dist/tools/workspace-tools.d.ts.map +1 -1
- package/dist/tools/workspace-tools.js +9 -1
- package/dist/tools/workspace-tools.js.map +1 -1
- package/dist/utils/loopback.d.ts +23 -0
- package/dist/utils/loopback.d.ts.map +1 -0
- package/dist/utils/loopback.js +71 -0
- package/dist/utils/loopback.js.map +1 -0
- package/dist/utils/walk/filters.d.ts.map +1 -1
- package/dist/utils/walk/filters.js +22 -1
- package/dist/utils/walk/filters.js.map +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* SQLite backend for the per-repo index. See docs/adr/ADR-003-index-storage-format.md.
|
|
5
|
+
*
|
|
6
|
+
* The point of this module is NOT that SQL is nicer than JSON. It is that the JSON blob
|
|
7
|
+
* forced every write to cost the size of the whole repo: `saveIncremental` re-parsed and
|
|
8
|
+
* re-serialised 262 MB to record one edited file, and the PostToolUse hook — a fresh process
|
|
9
|
+
* per edit, so it can never batch — paid that on every single Write/Edit an agent made.
|
|
10
|
+
* Here a one-file re-index touches one row.
|
|
11
|
+
*
|
|
12
|
+
* Fidelity over elegance: hot fields get real columns (so a full load builds objects without
|
|
13
|
+
* parsing anything), and the rarely-populated tail (`tokens`, `decorators`, `extends`,
|
|
14
|
+
* `implements`, `meta`) is kept as one JSON `extras` column. That keeps the round-trip
|
|
15
|
+
* lossless for language-specific fields nobody here should have to know about, while the
|
|
16
|
+
* common symbol costs zero JSON.parse calls.
|
|
17
|
+
*/
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Runtime availability
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/** `node:sqlite` landed in Node 22.5. The engines floor is still >=20, so its absence is a
|
|
22
|
+
* supported state, not an error — callers fall back to the JSON backend. */
|
|
23
|
+
let sqliteCtor;
|
|
24
|
+
export async function loadSqliteCtor() {
|
|
25
|
+
if (sqliteCtor !== undefined)
|
|
26
|
+
return sqliteCtor;
|
|
27
|
+
try {
|
|
28
|
+
const mod = await import("node:sqlite");
|
|
29
|
+
sqliteCtor = mod.DatabaseSync;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
sqliteCtor = null;
|
|
33
|
+
}
|
|
34
|
+
return sqliteCtor;
|
|
35
|
+
}
|
|
36
|
+
export async function isSqliteAvailable() {
|
|
37
|
+
return (await loadSqliteCtor()) !== null;
|
|
38
|
+
}
|
|
39
|
+
/** Tests need to prove the JSON fallback still works on a runtime that has sqlite. */
|
|
40
|
+
export function setSqliteCtorForTesting(ctor) {
|
|
41
|
+
sqliteCtor = ctor;
|
|
42
|
+
}
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Operational failures
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
/**
|
|
47
|
+
* A storage fault, as distinct from "this repo has no index".
|
|
48
|
+
*
|
|
49
|
+
* Both used to arrive at callers as `null`, so a locked or corrupt database was indistinguishable
|
|
50
|
+
* from an unindexed repo: tools reported "not indexed" (prompting a pointless full reindex over a
|
|
51
|
+
* database that was merely busy) or returned empty results that read as an authoritative "no
|
|
52
|
+
* matches". Empty-because-broken is the worst answer an index can give, because nothing about it
|
|
53
|
+
* looks wrong.
|
|
54
|
+
*/
|
|
55
|
+
export class IndexStorageError extends Error {
|
|
56
|
+
code;
|
|
57
|
+
path;
|
|
58
|
+
constructor(message, code, path, options) {
|
|
59
|
+
super(message, options);
|
|
60
|
+
this.code = code;
|
|
61
|
+
this.path = path;
|
|
62
|
+
this.name = "IndexStorageError";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Codes that mean "the store is there but unusable right now", never "there is nothing here".
|
|
67
|
+
*
|
|
68
|
+
* Deliberately a tight allowlist. Classifying too broadly would convert ordinary absence into a
|
|
69
|
+
* thrown error on a hot path used by ~every tool, which is a worse failure than the one being
|
|
70
|
+
* fixed — so anything unrecognised keeps the previous null-ish behaviour.
|
|
71
|
+
*/
|
|
72
|
+
const OPERATIONAL_CODES = new Set([
|
|
73
|
+
"SQLITE_BUSY",
|
|
74
|
+
"SQLITE_LOCKED",
|
|
75
|
+
"SQLITE_CORRUPT",
|
|
76
|
+
"SQLITE_NOTADB",
|
|
77
|
+
"SQLITE_CANTOPEN",
|
|
78
|
+
"SQLITE_IOERR",
|
|
79
|
+
"SQLITE_READONLY",
|
|
80
|
+
"SQLITE_PERM",
|
|
81
|
+
"SQLITE_FULL",
|
|
82
|
+
"EACCES",
|
|
83
|
+
"EPERM",
|
|
84
|
+
"EIO",
|
|
85
|
+
"EBUSY",
|
|
86
|
+
"ENOSPC",
|
|
87
|
+
"EMFILE",
|
|
88
|
+
]);
|
|
89
|
+
/**
|
|
90
|
+
* Structural check, not `instanceof`.
|
|
91
|
+
*
|
|
92
|
+
* A duplicated module instance (bundler, worker/thread boundary, a test importing through a
|
|
93
|
+
* different specifier) makes `instanceof` false for an object that is in every observable way
|
|
94
|
+
* the right error — and the failure mode is silent: the fault falls into the "unexpected error"
|
|
95
|
+
* branch and gets reported as an unindexed repo, which is the exact bug this file exists to
|
|
96
|
+
* prevent.
|
|
97
|
+
*/
|
|
98
|
+
export function isIndexStorageError(err) {
|
|
99
|
+
return (typeof err === "object" &&
|
|
100
|
+
err !== null &&
|
|
101
|
+
err.name === "IndexStorageError" &&
|
|
102
|
+
typeof err.code === "string");
|
|
103
|
+
}
|
|
104
|
+
/** The operational code for `err`, or null when it is not an operational failure. */
|
|
105
|
+
export function classifyStorageError(err) {
|
|
106
|
+
if (err instanceof IndexStorageError)
|
|
107
|
+
return err.code;
|
|
108
|
+
if (typeof err !== "object" || err === null)
|
|
109
|
+
return null;
|
|
110
|
+
const code = err.code;
|
|
111
|
+
if (typeof code === "string") {
|
|
112
|
+
if (OPERATIONAL_CODES.has(code))
|
|
113
|
+
return code;
|
|
114
|
+
// node:sqlite surfaces extended result codes (SQLITE_IOERR_READ, SQLITE_BUSY_SNAPSHOT...).
|
|
115
|
+
for (const known of OPERATIONAL_CODES) {
|
|
116
|
+
if (known.startsWith("SQLITE_") && code.startsWith(`${known}_`))
|
|
117
|
+
return code;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Some node:sqlite builds only carry the reason in the message.
|
|
121
|
+
const message = err.message;
|
|
122
|
+
if (typeof message === "string") {
|
|
123
|
+
if (/database disk image is malformed|file is not a database/i.test(message)) {
|
|
124
|
+
return "SQLITE_CORRUPT";
|
|
125
|
+
}
|
|
126
|
+
if (/database is locked|database table is locked/i.test(message))
|
|
127
|
+
return "SQLITE_BUSY";
|
|
128
|
+
if (/unable to open database/i.test(message))
|
|
129
|
+
return "SQLITE_CANTOPEN";
|
|
130
|
+
if (/disk I\/O error/i.test(message))
|
|
131
|
+
return "SQLITE_IOERR";
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
/** Rethrow operational faults as IndexStorageError; leave everything else untouched. */
|
|
136
|
+
function rethrowOperational(err, path) {
|
|
137
|
+
const code = classifyStorageError(err);
|
|
138
|
+
if (code === null)
|
|
139
|
+
throw err;
|
|
140
|
+
if (err instanceof IndexStorageError)
|
|
141
|
+
throw err;
|
|
142
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
143
|
+
throw new IndexStorageError(`index storage at ${path} is unreadable (${code}): ${detail}`, code, path, { cause: err });
|
|
144
|
+
}
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// Schema
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
export const SCHEMA_VERSION = 1;
|
|
149
|
+
const SCHEMA_SQL = `
|
|
150
|
+
CREATE TABLE IF NOT EXISTS meta (
|
|
151
|
+
key TEXT PRIMARY KEY,
|
|
152
|
+
value TEXT NOT NULL
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
156
|
+
path TEXT PRIMARY KEY,
|
|
157
|
+
language TEXT NOT NULL,
|
|
158
|
+
symbol_count INTEGER NOT NULL,
|
|
159
|
+
last_modified INTEGER NOT NULL,
|
|
160
|
+
mtime_ms INTEGER,
|
|
161
|
+
stale INTEGER
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
165
|
+
id TEXT PRIMARY KEY,
|
|
166
|
+
file TEXT NOT NULL,
|
|
167
|
+
name TEXT NOT NULL,
|
|
168
|
+
kind TEXT NOT NULL,
|
|
169
|
+
start_line INTEGER NOT NULL,
|
|
170
|
+
end_line INTEGER NOT NULL,
|
|
171
|
+
start_col INTEGER,
|
|
172
|
+
end_col INTEGER,
|
|
173
|
+
start_byte INTEGER,
|
|
174
|
+
end_byte INTEGER,
|
|
175
|
+
signature TEXT,
|
|
176
|
+
docstring TEXT,
|
|
177
|
+
source TEXT,
|
|
178
|
+
parent TEXT,
|
|
179
|
+
is_async INTEGER,
|
|
180
|
+
is_exported INTEGER,
|
|
181
|
+
extras TEXT
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_file ON symbols(file);
|
|
185
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);
|
|
186
|
+
`;
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
// Connection handling
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
const connections = new Map();
|
|
191
|
+
/**
|
|
192
|
+
* Open (and cache) a connection, creating the schema on first use.
|
|
193
|
+
*
|
|
194
|
+
* WAL is the reason this migration solves a problem a sharded JSON layout would not: the MCP
|
|
195
|
+
* server and the `codesift postindex-file` hook are separate processes writing the same
|
|
196
|
+
* index, and WAL gives them concurrent readers with a single writer instead of a corruption
|
|
197
|
+
* window. `busy_timeout` makes a writer wait its turn rather than throwing SQLITE_BUSY when
|
|
198
|
+
* an agent edits files faster than the previous write settles.
|
|
199
|
+
*/
|
|
200
|
+
export async function openIndexDb(dbPath) {
|
|
201
|
+
const cached = connections.get(dbPath);
|
|
202
|
+
if (cached)
|
|
203
|
+
return cached;
|
|
204
|
+
const Ctor = await loadSqliteCtor();
|
|
205
|
+
if (!Ctor)
|
|
206
|
+
throw new Error("node:sqlite is unavailable (requires Node >= 22.5)");
|
|
207
|
+
if (dbPath !== ":memory:")
|
|
208
|
+
await mkdir(dirname(dbPath), { recursive: true });
|
|
209
|
+
let db;
|
|
210
|
+
try {
|
|
211
|
+
db = new Ctor(dbPath);
|
|
212
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
213
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
214
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
215
|
+
db.exec(SCHEMA_SQL);
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
// Close before rethrowing: the constructor can succeed and a PRAGMA still fail, and this
|
|
219
|
+
// handle never reaches the `connections` cache, so nothing else would ever close it. On a
|
|
220
|
+
// retry loop against a sick database that leaks a descriptor and a lock per attempt —
|
|
221
|
+
// which makes the very BUSY/EMFILE conditions being reported worse.
|
|
222
|
+
try {
|
|
223
|
+
db?.close();
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
/* already dead — the original fault is the one worth reporting */
|
|
227
|
+
}
|
|
228
|
+
// A corrupt or unopenable file fails here, before any row is read — the one place where
|
|
229
|
+
// "cannot open" would otherwise look exactly like "nothing indexed yet".
|
|
230
|
+
rethrowOperational(err, dbPath);
|
|
231
|
+
}
|
|
232
|
+
const stored = readMetaValue(db, "schema_version");
|
|
233
|
+
if (stored === undefined) {
|
|
234
|
+
writeMetaValue(db, "schema_version", String(SCHEMA_VERSION));
|
|
235
|
+
}
|
|
236
|
+
else if (Number(stored) > SCHEMA_VERSION) {
|
|
237
|
+
db.close();
|
|
238
|
+
throw new Error(`Index at ${dbPath} was written by a newer CodeSift (schema v${stored} > v${SCHEMA_VERSION}). Upgrade codesift-mcp.`);
|
|
239
|
+
}
|
|
240
|
+
connections.set(dbPath, db);
|
|
241
|
+
return db;
|
|
242
|
+
}
|
|
243
|
+
/** Close one cached connection (tests, and the shutdown path). */
|
|
244
|
+
export function closeIndexDb(dbPath) {
|
|
245
|
+
const db = connections.get(dbPath);
|
|
246
|
+
if (!db)
|
|
247
|
+
return;
|
|
248
|
+
connections.delete(dbPath);
|
|
249
|
+
try {
|
|
250
|
+
db.close();
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
/* already closed — nothing to protect here */
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
export function closeAllIndexDbs() {
|
|
257
|
+
for (const path of [...connections.keys()])
|
|
258
|
+
closeIndexDb(path);
|
|
259
|
+
}
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
// meta helpers
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
function readMetaValue(db, key) {
|
|
264
|
+
const row = db.prepare("SELECT value FROM meta WHERE key = ?").get(key);
|
|
265
|
+
return row?.value;
|
|
266
|
+
}
|
|
267
|
+
function writeMetaValue(db, key, value) {
|
|
268
|
+
db.prepare("INSERT INTO meta(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(key, value);
|
|
269
|
+
}
|
|
270
|
+
function symbolToRow(sym) {
|
|
271
|
+
const extras = {};
|
|
272
|
+
if (sym.tokens !== undefined)
|
|
273
|
+
extras.tokens = sym.tokens;
|
|
274
|
+
if (sym.decorators !== undefined)
|
|
275
|
+
extras.decorators = sym.decorators;
|
|
276
|
+
if (sym.extends !== undefined)
|
|
277
|
+
extras.extends = sym.extends;
|
|
278
|
+
if (sym.implements !== undefined)
|
|
279
|
+
extras.implements = sym.implements;
|
|
280
|
+
if (sym.meta !== undefined)
|
|
281
|
+
extras.meta = sym.meta;
|
|
282
|
+
const hasExtras = Object.keys(extras).length > 0;
|
|
283
|
+
return [
|
|
284
|
+
sym.id,
|
|
285
|
+
sym.file,
|
|
286
|
+
sym.name,
|
|
287
|
+
sym.kind,
|
|
288
|
+
sym.start_line,
|
|
289
|
+
sym.end_line,
|
|
290
|
+
sym.start_col ?? null,
|
|
291
|
+
sym.end_col ?? null,
|
|
292
|
+
sym.start_byte ?? null,
|
|
293
|
+
sym.end_byte ?? null,
|
|
294
|
+
sym.signature ?? null,
|
|
295
|
+
sym.docstring ?? null,
|
|
296
|
+
sym.source ?? null,
|
|
297
|
+
sym.parent ?? null,
|
|
298
|
+
sym.is_async === undefined ? null : sym.is_async ? 1 : 0,
|
|
299
|
+
sym.is_exported === undefined ? null : sym.is_exported ? 1 : 0,
|
|
300
|
+
hasExtras ? JSON.stringify(extras) : null,
|
|
301
|
+
];
|
|
302
|
+
}
|
|
303
|
+
/** `repo` is identical for every symbol in an index, so it lives in `meta` and is stamped
|
|
304
|
+
* back on here instead of being written 100k times. */
|
|
305
|
+
function rowToSymbol(row, repo) {
|
|
306
|
+
const sym = {
|
|
307
|
+
id: row.id,
|
|
308
|
+
repo,
|
|
309
|
+
name: row.name,
|
|
310
|
+
kind: row.kind,
|
|
311
|
+
file: row.file,
|
|
312
|
+
start_line: row.start_line,
|
|
313
|
+
end_line: row.end_line,
|
|
314
|
+
};
|
|
315
|
+
if (row.start_col !== null)
|
|
316
|
+
sym.start_col = row.start_col;
|
|
317
|
+
if (row.end_col !== null)
|
|
318
|
+
sym.end_col = row.end_col;
|
|
319
|
+
if (row.start_byte !== null)
|
|
320
|
+
sym.start_byte = row.start_byte;
|
|
321
|
+
if (row.end_byte !== null)
|
|
322
|
+
sym.end_byte = row.end_byte;
|
|
323
|
+
if (row.signature !== null)
|
|
324
|
+
sym.signature = row.signature;
|
|
325
|
+
if (row.docstring !== null)
|
|
326
|
+
sym.docstring = row.docstring;
|
|
327
|
+
if (row.source !== null)
|
|
328
|
+
sym.source = row.source;
|
|
329
|
+
if (row.parent !== null)
|
|
330
|
+
sym.parent = row.parent;
|
|
331
|
+
if (row.is_async !== null)
|
|
332
|
+
sym.is_async = row.is_async === 1;
|
|
333
|
+
if (row.is_exported !== null)
|
|
334
|
+
sym.is_exported = row.is_exported === 1;
|
|
335
|
+
if (row.extras !== null) {
|
|
336
|
+
const extras = JSON.parse(row.extras);
|
|
337
|
+
if (extras.tokens !== undefined)
|
|
338
|
+
sym.tokens = extras.tokens;
|
|
339
|
+
if (extras.decorators !== undefined)
|
|
340
|
+
sym.decorators = extras.decorators;
|
|
341
|
+
if (extras.extends !== undefined)
|
|
342
|
+
sym.extends = extras.extends;
|
|
343
|
+
if (extras.implements !== undefined)
|
|
344
|
+
sym.implements = extras.implements;
|
|
345
|
+
if (extras.meta !== undefined)
|
|
346
|
+
sym.meta = extras.meta;
|
|
347
|
+
}
|
|
348
|
+
return sym;
|
|
349
|
+
}
|
|
350
|
+
function rowToFileEntry(row) {
|
|
351
|
+
const entry = {
|
|
352
|
+
path: row.path,
|
|
353
|
+
language: row.language,
|
|
354
|
+
symbol_count: row.symbol_count,
|
|
355
|
+
last_modified: row.last_modified,
|
|
356
|
+
};
|
|
357
|
+
if (row.mtime_ms !== null)
|
|
358
|
+
entry.mtime_ms = row.mtime_ms;
|
|
359
|
+
if (row.stale !== null)
|
|
360
|
+
entry.stale = row.stale === 1;
|
|
361
|
+
return entry;
|
|
362
|
+
}
|
|
363
|
+
function fileEntryToRow(entry) {
|
|
364
|
+
return [
|
|
365
|
+
entry.path,
|
|
366
|
+
entry.language,
|
|
367
|
+
entry.symbol_count,
|
|
368
|
+
entry.last_modified,
|
|
369
|
+
entry.mtime_ms ?? null,
|
|
370
|
+
entry.stale === undefined ? null : entry.stale ? 1 : 0,
|
|
371
|
+
];
|
|
372
|
+
}
|
|
373
|
+
const INSERT_SYMBOL_SQL = `
|
|
374
|
+
INSERT INTO symbols (
|
|
375
|
+
id, file, name, kind, start_line, end_line, start_col, end_col,
|
|
376
|
+
start_byte, end_byte, signature, docstring, source, parent,
|
|
377
|
+
is_async, is_exported, extras
|
|
378
|
+
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
379
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
380
|
+
file=excluded.file, name=excluded.name, kind=excluded.kind,
|
|
381
|
+
start_line=excluded.start_line, end_line=excluded.end_line,
|
|
382
|
+
start_col=excluded.start_col, end_col=excluded.end_col,
|
|
383
|
+
start_byte=excluded.start_byte, end_byte=excluded.end_byte,
|
|
384
|
+
signature=excluded.signature, docstring=excluded.docstring,
|
|
385
|
+
source=excluded.source, parent=excluded.parent,
|
|
386
|
+
is_async=excluded.is_async, is_exported=excluded.is_exported,
|
|
387
|
+
extras=excluded.extras
|
|
388
|
+
`;
|
|
389
|
+
const INSERT_FILE_SQL = `
|
|
390
|
+
INSERT INTO files (path, language, symbol_count, last_modified, mtime_ms, stale)
|
|
391
|
+
VALUES (?,?,?,?,?,?)
|
|
392
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
393
|
+
language=excluded.language, symbol_count=excluded.symbol_count,
|
|
394
|
+
last_modified=excluded.last_modified, mtime_ms=excluded.mtime_ms,
|
|
395
|
+
stale=excluded.stale
|
|
396
|
+
`;
|
|
397
|
+
// ---------------------------------------------------------------------------
|
|
398
|
+
// Whole-index read / write
|
|
399
|
+
// ---------------------------------------------------------------------------
|
|
400
|
+
/** Replace the entire index. Used by full (re)indexing and by JSON->SQLite migration. */
|
|
401
|
+
export async function saveIndexSqlite(dbPath, index) {
|
|
402
|
+
const db = await openIndexDb(dbPath);
|
|
403
|
+
db.exec("BEGIN");
|
|
404
|
+
try {
|
|
405
|
+
writeIndexRows(db, index);
|
|
406
|
+
db.exec("COMMIT");
|
|
407
|
+
}
|
|
408
|
+
catch (err) {
|
|
409
|
+
db.exec("ROLLBACK");
|
|
410
|
+
throw err;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Replace all rows + meta. Caller owns the transaction.
|
|
415
|
+
*
|
|
416
|
+
* `repo` is written here, inside that transaction, and `loadIndexSqlite` treats its absence
|
|
417
|
+
* as "no index" — so a write killed midway rolls back and the db reads as empty rather than
|
|
418
|
+
* as a partially-populated index that later looks complete.
|
|
419
|
+
*/
|
|
420
|
+
function writeIndexRows(db, index) {
|
|
421
|
+
db.exec("DELETE FROM symbols");
|
|
422
|
+
db.exec("DELETE FROM files");
|
|
423
|
+
const insSym = db.prepare(INSERT_SYMBOL_SQL);
|
|
424
|
+
for (const sym of index.symbols)
|
|
425
|
+
insSym.run(...symbolToRow(sym));
|
|
426
|
+
const insFile = db.prepare(INSERT_FILE_SQL);
|
|
427
|
+
for (const file of index.files)
|
|
428
|
+
insFile.run(...fileEntryToRow(file));
|
|
429
|
+
writeMetaValue(db, "repo", index.repo);
|
|
430
|
+
writeMetaValue(db, "root", index.root);
|
|
431
|
+
writeMetaValue(db, "created_at", String(index.created_at));
|
|
432
|
+
writeMetaValue(db, "updated_at", String(index.updated_at));
|
|
433
|
+
writeMetaValue(db, "extractor_version", JSON.stringify(index.extractor_version ?? null));
|
|
434
|
+
writeMetaValue(db, "workspaces", JSON.stringify(index.workspaces ?? null));
|
|
435
|
+
writeMetaValue(db, "schema_version", String(SCHEMA_VERSION));
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Import a legacy JSON index, but only if this db is still empty — decided and written
|
|
439
|
+
* inside ONE `BEGIN IMMEDIATE` transaction.
|
|
440
|
+
*
|
|
441
|
+
* The check-then-act cannot be done in JS: `codesift postindex-file` is a fresh process per
|
|
442
|
+
* edit, so two of them (or one plus the server) can both observe an empty db, both read the
|
|
443
|
+
* same legacy JSON, and the slower writer then overwrites an incremental update the faster
|
|
444
|
+
* one already committed. An in-process guard cannot see the other process at all.
|
|
445
|
+
*
|
|
446
|
+
* `BEGIN IMMEDIATE` takes the write lock before the emptiness check, so the second writer
|
|
447
|
+
* either waits and then sees the committed import (returns false), or fails on busy_timeout —
|
|
448
|
+
* never silently clobbers. Returns true when this call performed the import.
|
|
449
|
+
*/
|
|
450
|
+
export async function importLegacyIndexIfEmpty(dbPath, index) {
|
|
451
|
+
const db = await openIndexDb(dbPath);
|
|
452
|
+
db.exec("BEGIN IMMEDIATE");
|
|
453
|
+
try {
|
|
454
|
+
if (readMetaValue(db, "repo") !== undefined) {
|
|
455
|
+
db.exec("ROLLBACK");
|
|
456
|
+
return false; // another process got there first
|
|
457
|
+
}
|
|
458
|
+
writeIndexRows(db, index);
|
|
459
|
+
db.exec("COMMIT");
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
462
|
+
catch (err) {
|
|
463
|
+
db.exec("ROLLBACK");
|
|
464
|
+
throw err;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/** Materialise the whole index. Returns null when the db has never been written to. */
|
|
468
|
+
export async function loadIndexSqlite(dbPath) {
|
|
469
|
+
const db = await openIndexDb(dbPath);
|
|
470
|
+
let repo;
|
|
471
|
+
let root;
|
|
472
|
+
let symbolRows;
|
|
473
|
+
let fileRows;
|
|
474
|
+
try {
|
|
475
|
+
repo = readMetaValue(db, "repo");
|
|
476
|
+
root = readMetaValue(db, "root");
|
|
477
|
+
if (repo === undefined || root === undefined)
|
|
478
|
+
return null; // genuinely empty — not a fault
|
|
479
|
+
symbolRows = db.prepare("SELECT * FROM symbols").all();
|
|
480
|
+
fileRows = db.prepare("SELECT * FROM files").all();
|
|
481
|
+
}
|
|
482
|
+
catch (err) {
|
|
483
|
+
// Corruption often only surfaces on the first page read, not at open time.
|
|
484
|
+
rethrowOperational(err, dbPath);
|
|
485
|
+
}
|
|
486
|
+
const symbols = symbolRows.map((row) => rowToSymbol(row, repo));
|
|
487
|
+
const files = fileRows.map(rowToFileEntry);
|
|
488
|
+
const index = {
|
|
489
|
+
repo,
|
|
490
|
+
root,
|
|
491
|
+
symbols,
|
|
492
|
+
files,
|
|
493
|
+
created_at: Number(readMetaValue(db, "created_at") ?? 0),
|
|
494
|
+
updated_at: Number(readMetaValue(db, "updated_at") ?? 0),
|
|
495
|
+
symbol_count: symbols.length,
|
|
496
|
+
file_count: files.length,
|
|
497
|
+
};
|
|
498
|
+
const extractorRaw = readMetaValue(db, "extractor_version");
|
|
499
|
+
if (extractorRaw !== undefined) {
|
|
500
|
+
const parsed = JSON.parse(extractorRaw);
|
|
501
|
+
if (parsed !== null)
|
|
502
|
+
index.extractor_version = parsed;
|
|
503
|
+
}
|
|
504
|
+
const workspacesRaw = readMetaValue(db, "workspaces");
|
|
505
|
+
if (workspacesRaw !== undefined) {
|
|
506
|
+
const parsed = JSON.parse(workspacesRaw);
|
|
507
|
+
if (parsed !== null)
|
|
508
|
+
index.workspaces = parsed;
|
|
509
|
+
}
|
|
510
|
+
return index;
|
|
511
|
+
}
|
|
512
|
+
// ---------------------------------------------------------------------------
|
|
513
|
+
// Narrow accessors — the reason this backend exists
|
|
514
|
+
// ---------------------------------------------------------------------------
|
|
515
|
+
/**
|
|
516
|
+
* One file's `mtime_ms`, without materialising the index.
|
|
517
|
+
*
|
|
518
|
+
* `file-indexer.ts` used to parse the whole blob for exactly this value and then
|
|
519
|
+
* `saveIncremental` parsed it a second time — two full parses of 262 MB to decide whether a
|
|
520
|
+
* 40-line file had changed.
|
|
521
|
+
*/
|
|
522
|
+
export async function getFileMtimeSqlite(dbPath, filePath) {
|
|
523
|
+
const db = await openIndexDb(dbPath);
|
|
524
|
+
const row = db.prepare("SELECT mtime_ms FROM files WHERE path = ?").get(filePath);
|
|
525
|
+
return row?.mtime_ms ?? undefined;
|
|
526
|
+
}
|
|
527
|
+
/** One file's `files[]` entry, without materialising the index. */
|
|
528
|
+
export async function getFileEntrySqlite(dbPath, filePath) {
|
|
529
|
+
const db = await openIndexDb(dbPath);
|
|
530
|
+
const row = db.prepare("SELECT * FROM files WHERE path = ?").get(filePath);
|
|
531
|
+
return row ? rowToFileEntry(row) : undefined;
|
|
532
|
+
}
|
|
533
|
+
/** Replace one file's symbols (and optionally its files[] entry) in a single transaction. */
|
|
534
|
+
export async function saveIncrementalSqlite(dbPath, updatedFile, newSymbols, fileEntry) {
|
|
535
|
+
const db = await openIndexDb(dbPath);
|
|
536
|
+
if (readMetaValue(db, "repo") === undefined) {
|
|
537
|
+
throw new Error(`Cannot incrementally update: index not found at ${dbPath}`);
|
|
538
|
+
}
|
|
539
|
+
db.exec("BEGIN");
|
|
540
|
+
try {
|
|
541
|
+
db.prepare("DELETE FROM symbols WHERE file = ?").run(updatedFile);
|
|
542
|
+
const insSym = db.prepare(INSERT_SYMBOL_SQL);
|
|
543
|
+
for (const sym of newSymbols)
|
|
544
|
+
insSym.run(...symbolToRow(sym));
|
|
545
|
+
if (fileEntry)
|
|
546
|
+
db.prepare(INSERT_FILE_SQL).run(...fileEntryToRow(fileEntry));
|
|
547
|
+
writeMetaValue(db, "updated_at", String(Date.now()));
|
|
548
|
+
db.exec("COMMIT");
|
|
549
|
+
}
|
|
550
|
+
catch (err) {
|
|
551
|
+
db.exec("ROLLBACK");
|
|
552
|
+
throw err;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
/** Drop a deleted file's symbols and its files[] entry. */
|
|
556
|
+
export async function removeFileFromIndexSqlite(dbPath, deletedFile) {
|
|
557
|
+
const db = await openIndexDb(dbPath);
|
|
558
|
+
if (readMetaValue(db, "repo") === undefined)
|
|
559
|
+
return; // nothing to remove
|
|
560
|
+
db.exec("BEGIN");
|
|
561
|
+
try {
|
|
562
|
+
db.prepare("DELETE FROM symbols WHERE file = ?").run(deletedFile);
|
|
563
|
+
db.prepare("DELETE FROM files WHERE path = ?").run(deletedFile);
|
|
564
|
+
writeMetaValue(db, "updated_at", String(Date.now()));
|
|
565
|
+
db.exec("COMMIT");
|
|
566
|
+
}
|
|
567
|
+
catch (err) {
|
|
568
|
+
db.exec("ROLLBACK");
|
|
569
|
+
throw err;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
/** All symbols for one file — lets per-file tools skip the whole-index load. */
|
|
573
|
+
export async function getSymbolsForFileSqlite(dbPath, filePath) {
|
|
574
|
+
const db = await openIndexDb(dbPath);
|
|
575
|
+
const repo = readMetaValue(db, "repo");
|
|
576
|
+
if (repo === undefined)
|
|
577
|
+
return [];
|
|
578
|
+
const rows = db
|
|
579
|
+
.prepare("SELECT * FROM symbols WHERE file = ?")
|
|
580
|
+
.all(filePath);
|
|
581
|
+
return rows.map((row) => rowToSymbol(row, repo));
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Cross-process change counter. SQLite bumps `data_version` when *another* connection
|
|
585
|
+
* commits, which is the invalidation signal a plain JSON file never had — and the reason an
|
|
586
|
+
* in-memory index cache becomes safe here despite the hook writing from its own process.
|
|
587
|
+
*/
|
|
588
|
+
export async function getDataVersion(dbPath) {
|
|
589
|
+
const db = await openIndexDb(dbPath);
|
|
590
|
+
const row = db.prepare("PRAGMA data_version").get();
|
|
591
|
+
return row.data_version;
|
|
592
|
+
}
|
|
593
|
+
//# sourceMappingURL=sqlite-index-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-index-store.js","sourceRoot":"","sources":["../../src/storage/sqlite-index-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { execFileSync } from "node:child_process";
|
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { clearTsconfigCache } from "../../utils/tsconfig-paths.js";
|
|
6
6
|
import { getRepo, listRepos as listRegistryRepos, updateRepoMeta, } from "../../storage/registry.js";
|
|
7
|
-
import {
|
|
7
|
+
import { getFileEntry, saveIncremental } from "../../storage/index-store.js";
|
|
8
8
|
import { loadConfig } from "../../config.js";
|
|
9
9
|
import { scanFileForSecrets } from "../secret-scan-shared.js";
|
|
10
10
|
import { parseOneFile } from "./parse.js";
|
|
@@ -83,25 +83,25 @@ export async function indexFile(filePath) {
|
|
|
83
83
|
// hook invocations, fresh server). Skips files unchanged since the last
|
|
84
84
|
// full index, and seeds the in-process state for subsequent calls.
|
|
85
85
|
if (!mem) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
return {
|
|
98
|
-
repo: matchingRepo.name,
|
|
99
|
-
file: relPath,
|
|
100
|
-
symbol_count: prevEntry.symbol_count,
|
|
101
|
-
duration_ms: Date.now() - startTime,
|
|
102
|
-
skipped: true,
|
|
103
|
-
};
|
|
86
|
+
// One row, not the whole index: this check runs on the first touch of every file, and
|
|
87
|
+
// the CLI hook is a fresh process per edit, so loading the full index here was the
|
|
88
|
+
// single most-repeated whole-blob parse in the system (ADR-003).
|
|
89
|
+
const prevEntry = await getFileEntry(matchingRepo.index_path, relPath);
|
|
90
|
+
if (prevEntry?.mtime_ms && Math.round(st.mtimeMs) === prevEntry.mtime_ms) {
|
|
91
|
+
if (contentHash !== null) {
|
|
92
|
+
lastIndexedState.set(absPath, {
|
|
93
|
+
mtimeMs: Math.round(st.mtimeMs),
|
|
94
|
+
contentHash,
|
|
95
|
+
symbolCount: prevEntry.symbol_count,
|
|
96
|
+
});
|
|
104
97
|
}
|
|
98
|
+
return {
|
|
99
|
+
repo: matchingRepo.name,
|
|
100
|
+
file: relPath,
|
|
101
|
+
symbol_count: prevEntry.symbol_count,
|
|
102
|
+
duration_ms: Date.now() - startTime,
|
|
103
|
+
skipped: true,
|
|
104
|
+
};
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
const result = await parseOneFile(absPath, matchingRepo.root, matchingRepo.name);
|