@vohongtho.infotech/code-intel 0.8.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,3 @@
1
- import { createRequire } from 'module';
2
- import { fileURLToPath } from 'url';
3
- import path31 from 'path';
4
- import fs24, { existsSync } from 'fs';
5
- import { Parser, Language, Query } from 'web-tree-sitter';
6
1
  import { NodeSDK } from '@opentelemetry/sdk-node';
7
2
  import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
8
3
  import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
@@ -11,8 +6,14 @@ import { SEMRESATTRS_DEPLOYMENT_ENVIRONMENT, SEMRESATTRS_SERVICE_NAME } from '@o
11
6
  import { trace, context, SpanStatusCode } from '@opentelemetry/api';
12
7
  import winston from 'winston';
13
8
  import DailyRotateFile from 'winston-daily-rotate-file';
14
- import os12 from 'os';
15
- import Database3 from 'better-sqlite3';
9
+ import fs25, { existsSync } from 'fs';
10
+ import path32 from 'path';
11
+ import os13 from 'os';
12
+ import { createRequire } from 'module';
13
+ import { fileURLToPath } from 'url';
14
+ import { Parser, Language, Query } from 'web-tree-sitter';
15
+ import { execSync } from 'child_process';
16
+ import Database2 from 'better-sqlite3';
16
17
  import bcrypt from 'bcrypt';
17
18
  import crypto5 from 'crypto';
18
19
  import { v4 } from 'uuid';
@@ -24,7 +25,6 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
24
25
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
25
26
  import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
26
27
  import { Database, Connection } from '@ladybugdb/core';
27
- import { execSync } from 'child_process';
28
28
  import express from 'express';
29
29
  import compression from 'compression';
30
30
  import cors from 'cors';
@@ -68,218 +68,551 @@ var init_id_generator = __esm({
68
68
  }
69
69
  });
70
70
 
71
- // src/shared/languages.ts
72
- var init_languages = __esm({
73
- "src/shared/languages.ts"() {
74
- }
71
+ // src/observability/tracing.ts
72
+ var tracing_exports = {};
73
+ __export(tracing_exports, {
74
+ SpanStatusCode: () => SpanStatusCode,
75
+ context: () => context,
76
+ getActiveTraceContext: () => getActiveTraceContext,
77
+ getTracer: () => getTracer,
78
+ initTracing: () => initTracing,
79
+ isTracingEnabled: () => isTracingEnabled,
80
+ sanitizeAttrs: () => sanitizeAttrs,
81
+ shutdownTracing: () => shutdownTracing,
82
+ trace: () => trace,
83
+ withSpan: () => withSpan
75
84
  });
76
-
77
- // src/shared/detection.ts
78
- function detectLanguage(filePath) {
79
- const ext = filePath.slice(filePath.lastIndexOf("."));
80
- return EXTENSION_MAP[ext] ?? null;
85
+ function isTracingEnabled() {
86
+ return process.env["CODE_INTEL_OTEL_ENABLED"] === "true";
81
87
  }
82
- function getSupportedExtensions() {
83
- return Object.keys(EXTENSION_MAP);
88
+ function initTracing() {
89
+ if (!isTracingEnabled()) return;
90
+ if (_sdk) return;
91
+ const endpoint = process.env["CODE_INTEL_OTEL_ENDPOINT"] ?? "http://localhost:4318";
92
+ const serviceName = process.env["CODE_INTEL_OTEL_SERVICE"] ?? "code-intel";
93
+ const deploymentEnv = process.env["CODE_INTEL_OTEL_ENV"] ?? process.env["NODE_ENV"] ?? "development";
94
+ const exporter = new OTLPTraceExporter({ url: `${endpoint}/v1/traces` });
95
+ _sdk = new NodeSDK({
96
+ resource: resourceFromAttributes({
97
+ [SEMRESATTRS_SERVICE_NAME]: serviceName,
98
+ [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: deploymentEnv
99
+ }),
100
+ traceExporter: exporter,
101
+ instrumentations: [
102
+ getNodeAutoInstrumentations({
103
+ // Disable noisy file-system instrumentation
104
+ "@opentelemetry/instrumentation-fs": { enabled: false }
105
+ })
106
+ ]
107
+ });
108
+ _sdk.start();
84
109
  }
85
- var EXTENSION_MAP;
86
- var init_detection = __esm({
87
- "src/shared/detection.ts"() {
88
- init_languages();
89
- EXTENSION_MAP = {
90
- ".ts": "typescript" /* TypeScript */,
91
- ".tsx": "typescript" /* TypeScript */,
92
- ".mts": "typescript" /* TypeScript */,
93
- ".cts": "typescript" /* TypeScript */,
94
- ".js": "javascript" /* JavaScript */,
95
- ".jsx": "javascript" /* JavaScript */,
96
- ".mjs": "javascript" /* JavaScript */,
97
- ".cjs": "javascript" /* JavaScript */,
98
- ".py": "python" /* Python */,
99
- ".pyi": "python" /* Python */,
100
- ".java": "java" /* Java */,
101
- ".go": "go" /* Go */,
102
- ".c": "c" /* C */,
103
- ".h": "c" /* C */,
104
- ".cpp": "cpp" /* Cpp */,
105
- ".cxx": "cpp" /* Cpp */,
106
- ".cc": "cpp" /* Cpp */,
107
- ".hpp": "cpp" /* Cpp */,
108
- ".hxx": "cpp" /* Cpp */,
109
- ".cs": "csharp" /* CSharp */,
110
- ".rs": "rust" /* Rust */,
111
- ".php": "php" /* PHP */,
112
- ".kt": "kotlin" /* Kotlin */,
113
- ".kts": "kotlin" /* Kotlin */,
114
- ".rb": "ruby" /* Ruby */,
115
- ".swift": "swift" /* Swift */,
116
- ".dart": "dart" /* Dart */
117
- };
118
- }
119
- });
120
-
121
- // src/shared/index.ts
122
- var init_shared = __esm({
123
- "src/shared/index.ts"() {
124
- init_languages();
125
- init_detection();
126
- }
127
- });
128
- function findBundledWasmDir() {
129
- const fileDir = path31.dirname(fileURLToPath(import.meta.url));
130
- const candidates = [
131
- path31.join(fileDir, "wasm"),
132
- // dist/index.js → dist/wasm/
133
- path31.join(fileDir, "../wasm")
134
- // dist/cli/main.js → dist/wasm/
135
- ];
136
- for (const candidate of candidates) {
137
- if (existsSync(candidate)) return candidate;
110
+ async function shutdownTracing() {
111
+ if (_sdk) {
112
+ await _sdk.shutdown();
113
+ _sdk = null;
138
114
  }
139
- return candidates[0];
140
115
  }
141
- function wasmPath(lang) {
142
- const WASM_PACKAGE_MAP = {
143
- ["typescript" /* TypeScript */]: "tree-sitter-typescript/tree-sitter-typescript.wasm",
144
- ["javascript" /* JavaScript */]: "tree-sitter-javascript/tree-sitter-javascript.wasm",
145
- ["python" /* Python */]: "tree-sitter-python/tree-sitter-python.wasm",
146
- ["java" /* Java */]: "tree-sitter-java/tree-sitter-java.wasm",
147
- ["go" /* Go */]: "tree-sitter-go/tree-sitter-go.wasm",
148
- ["c" /* C */]: "tree-sitter-c/tree-sitter-c.wasm",
149
- ["cpp" /* Cpp */]: "tree-sitter-cpp/tree-sitter-cpp.wasm",
150
- ["csharp" /* CSharp */]: "tree-sitter-c-sharp/tree-sitter-c_sharp.wasm",
151
- ["rust" /* Rust */]: "tree-sitter-rust/tree-sitter-rust.wasm",
152
- ["php" /* PHP */]: "tree-sitter-php/tree-sitter-php.wasm",
153
- ["ruby" /* Ruby */]: "tree-sitter-ruby/tree-sitter-ruby.wasm",
154
- // These are optional dependencies; their packages may or may not include
155
- // a WASM. If require.resolve fails we fall back to the bundled wasm/.
156
- ["swift" /* Swift */]: "tree-sitter-swift/tree-sitter-swift.wasm",
157
- ["kotlin" /* Kotlin */]: "tree-sitter-kotlin/tree-sitter-kotlin.wasm",
158
- ["dart" /* Dart */]: "tree-sitter-dart/tree-sitter-dart.wasm"
159
- };
160
- const BUNDLED_WASM_MAP = {
161
- ["swift" /* Swift */]: "tree-sitter-swift.wasm",
162
- ["kotlin" /* Kotlin */]: "tree-sitter-kotlin.wasm",
163
- ["dart" /* Dart */]: "tree-sitter-dart.wasm"
164
- };
165
- const relative = WASM_PACKAGE_MAP[lang];
166
- if (relative) {
116
+ function getTracer() {
117
+ return trace.getTracer(TRACER_NAME);
118
+ }
119
+ async function withSpan(name, attrs, fn) {
120
+ return getTracer().startActiveSpan(name, { attributes: sanitizeAttrs(attrs) }, async (span) => {
167
121
  try {
168
- return _require.resolve(relative);
169
- } catch {
122
+ const result = await fn(span);
123
+ span.setStatus({ code: SpanStatusCode.OK });
124
+ return result;
125
+ } catch (err) {
126
+ span.setStatus({
127
+ code: SpanStatusCode.ERROR,
128
+ message: err instanceof Error ? err.message : String(err)
129
+ });
130
+ span.recordException(err);
131
+ throw err;
132
+ } finally {
133
+ span.end();
170
134
  }
171
- }
172
- const bundled = BUNDLED_WASM_MAP[lang];
173
- if (bundled) {
174
- const bundledPath = path31.join(_bundledWasmDir, bundled);
175
- if (existsSync(bundledPath)) return bundledPath;
176
- }
177
- return null;
178
- }
179
- async function initParser() {
180
- if (!initPromise) {
181
- initPromise = Parser.init();
182
- }
183
- return initPromise;
184
- }
185
- async function getLanguage(lang) {
186
- if (languageCache.has(lang)) return languageCache.get(lang);
187
- const path32 = wasmPath(lang);
188
- if (!path32) {
189
- languageCache.set(lang, null);
190
- return null;
191
- }
192
- try {
193
- await initParser();
194
- const language = await Language.load(path32);
195
- languageCache.set(lang, language);
196
- return language;
197
- } catch {
198
- languageCache.set(lang, null);
199
- return null;
200
- }
135
+ });
201
136
  }
202
- async function getParser(lang) {
203
- const language = await getLanguage(lang);
204
- if (!language) return null;
205
- let parser = parserCache.get(lang);
206
- if (!parser) {
207
- parser = new Parser();
208
- parserCache.set(lang, parser);
137
+ function sanitizeAttrs(attrs) {
138
+ const safe = {};
139
+ for (const [k, v] of Object.entries(attrs)) {
140
+ if (BLOCKED_ATTR_KEYS.test(k)) continue;
141
+ safe[k] = v;
209
142
  }
210
- parser.setLanguage(language);
211
- return parser;
212
- }
213
- async function parseSource(lang, source) {
214
- const parser = await getParser(lang);
215
- if (!parser) return null;
216
- return parser.parse(source);
143
+ return safe;
217
144
  }
218
- async function isTreeSitterAvailable(lang) {
219
- return await getLanguage(lang) !== null;
145
+ function getActiveTraceContext() {
146
+ const span = trace.getActiveSpan();
147
+ if (!span) return { traceId: "", spanId: "" };
148
+ const ctx = span.spanContext();
149
+ return { traceId: ctx.traceId, spanId: ctx.spanId };
220
150
  }
221
- var _require, _bundledWasmDir, initPromise, languageCache, parserCache;
222
- var init_parser_manager = __esm({
223
- "src/parsing/parser-manager.ts"() {
224
- init_shared();
225
- _require = createRequire(import.meta.url);
226
- _bundledWasmDir = findBundledWasmDir();
227
- initPromise = null;
228
- languageCache = /* @__PURE__ */ new Map();
229
- parserCache = /* @__PURE__ */ new Map();
151
+ var _sdk, TRACER_NAME, BLOCKED_ATTR_KEYS;
152
+ var init_tracing = __esm({
153
+ "src/observability/tracing.ts"() {
154
+ _sdk = null;
155
+ TRACER_NAME = "code-intel";
156
+ BLOCKED_ATTR_KEYS = /secret|password|token|key|auth|credential/i;
230
157
  }
231
158
  });
232
- function getOrCompileQuery(language, querySource) {
233
- let langMap = _queryCache.get(language);
234
- if (!langMap) {
235
- langMap = /* @__PURE__ */ new Map();
236
- _queryCache.set(language, langMap);
237
- }
238
- let q = langMap.get(querySource);
239
- if (!q) {
240
- q = new Query(language, querySource);
241
- langMap.set(querySource, q);
242
- }
243
- return q;
159
+ function getActiveTraceCtx() {
160
+ if (_getTraceCtx === "pending") return { traceId: "", spanId: "" };
161
+ if (_getTraceCtx) return _getTraceCtx();
162
+ _getTraceCtx = "pending";
163
+ Promise.resolve().then(() => (init_tracing(), tracing_exports)).then((mod) => {
164
+ _getTraceCtx = mod.getActiveTraceContext;
165
+ }).catch(() => {
166
+ _getTraceCtx = null;
167
+ });
168
+ return { traceId: "", spanId: "" };
244
169
  }
245
- function runQuery(tree, language, querySource) {
246
- const query = getOrCompileQuery(language, querySource);
247
- const matches = query.matches(tree.rootNode);
248
- const captures = [];
249
- for (const match of matches) {
250
- for (const capture of match.captures) {
251
- captures.push({
252
- name: capture.name,
253
- node: capture.node,
254
- text: capture.node.text
255
- });
256
- }
170
+ var _getTraceCtx, SENSITIVE_KEYS, SENSITIVE_PATTERNS, SENSITIVE_KEYS_REGEX, Logger, logger_default;
171
+ var init_logger = __esm({
172
+ "src/shared/logger.ts"() {
173
+ _getTraceCtx = null;
174
+ SENSITIVE_KEYS = [
175
+ "password",
176
+ "passwd",
177
+ "pass",
178
+ "pwd",
179
+ "secret",
180
+ "secretkey",
181
+ "secret_key",
182
+ "secretaccesskey",
183
+ "accesskeyid",
184
+ "credentials",
185
+ "auth",
186
+ "authentication",
187
+ "login",
188
+ "api_key",
189
+ "apikey",
190
+ "api",
191
+ "access_key",
192
+ "access_token",
193
+ "accesskey",
194
+ "auth_key",
195
+ "auth_token",
196
+ "authkey",
197
+ "token",
198
+ "jwt",
199
+ "bearer_token",
200
+ "refresh_token",
201
+ "session_token",
202
+ "session_key",
203
+ "oauth_token",
204
+ "connection_string",
205
+ "conn_string",
206
+ "db_uri",
207
+ "db_url",
208
+ "database_url",
209
+ "mongodb_uri",
210
+ "mysql_uri",
211
+ "postgres_uri",
212
+ "sql_uri",
213
+ "db_username",
214
+ "db_password",
215
+ "db_host",
216
+ "db_port",
217
+ "db_name",
218
+ "encryption_key",
219
+ "crypto_key",
220
+ "private_key",
221
+ "public_key",
222
+ "ssl_key",
223
+ "ssh_key",
224
+ "pgp_key",
225
+ "rsa_key",
226
+ "aes_key",
227
+ "email",
228
+ "phone",
229
+ "telephone",
230
+ "mobile",
231
+ "ssn",
232
+ "social_security",
233
+ "credit_card",
234
+ "cc_number",
235
+ "card_number",
236
+ "cvv",
237
+ "expiry_date",
238
+ "birth_date",
239
+ "dob",
240
+ "address",
241
+ "zip_code",
242
+ "postal_code",
243
+ "bank_account",
244
+ "iban",
245
+ "swift_code",
246
+ "routing_number",
247
+ "tax_id",
248
+ "vat_number",
249
+ "financial_id",
250
+ "certificate",
251
+ "client_cert",
252
+ "server_cert",
253
+ "ca_cert",
254
+ "aws_key",
255
+ "aws_secret",
256
+ "azure_key",
257
+ "gcp_key",
258
+ "s3_key",
259
+ "cloudinary_key",
260
+ "stripe_key",
261
+ "paypal_key",
262
+ "twilio_key",
263
+ "app_secret",
264
+ "client_secret",
265
+ "consumer_secret",
266
+ "encryption_secret",
267
+ "master_key",
268
+ "root_password",
269
+ "admin_password",
270
+ "config_secret",
271
+ "env_secret",
272
+ "deploy_key",
273
+ "ci_key",
274
+ "session_id",
275
+ "cookie_secret",
276
+ "csrf_token",
277
+ "xsrf_token",
278
+ "license_key",
279
+ "product_key",
280
+ "serial_number",
281
+ "activation_code"
282
+ ];
283
+ SENSITIVE_PATTERNS = [
284
+ /(?:password|passwd|secret|api_key|access_token|auth_token|token)\s*[:=]\s*([^\s,]+)/gi,
285
+ /\b\d{16}\b/gi,
286
+ /\b\d{3}-\d{2}-\d{4}\b/gi,
287
+ /\b[A-Za-z0-9]{32}\b/gi,
288
+ /\b[A-Za-z0-9_-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b/gi,
289
+ /\b\d{10}\b/gi,
290
+ /\b[A-Za-z0-9]{64}\b/gi,
291
+ /(?:connection_string|db_uri|db_url|mongodb_uri)\s*[:=]\s*([^\s,]+)/gi,
292
+ /(?:apikey|api_key|auth_key)\s*[:=]\s*([^\s,]+)/gi,
293
+ /(?:bearer\s+)([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)/gi
294
+ ];
295
+ SENSITIVE_KEYS_REGEX = new RegExp(`^(${SENSITIVE_KEYS.join("|")})$`, "i");
296
+ Logger = class _Logger {
297
+ static instance = null;
298
+ static maskSensitiveData(value) {
299
+ if (typeof value === "string" && value.length > 5) {
300
+ const firstChar = value.at(0);
301
+ const lastChar = value.at(-1);
302
+ return firstChar + "*".repeat(value.length - 2) + lastChar;
303
+ }
304
+ return value;
305
+ }
306
+ static maskSensitive(message, args = []) {
307
+ const maskString = (input) => {
308
+ if (typeof input !== "string") return input;
309
+ return SENSITIVE_PATTERNS.reduce((str, pattern) => {
310
+ return str.replace(
311
+ pattern,
312
+ (match, value) => value ? match.replace(value, _Logger.maskSensitiveData(value)) : match
313
+ );
314
+ }, input);
315
+ };
316
+ const deepMask = (obj) => {
317
+ if (typeof obj === "string") return maskString(obj);
318
+ if (Array.isArray(obj)) return obj.map((item) => deepMask(item));
319
+ if (typeof obj === "object" && obj !== null) {
320
+ return Object.entries(obj).reduce(
321
+ (acc, [key, value]) => {
322
+ if (value === void 0) return acc;
323
+ const isSensitiveKey = SENSITIVE_KEYS_REGEX.test(key);
324
+ acc[key] = isSensitiveKey && typeof value === "string" ? _Logger.maskSensitiveData(value) : deepMask(value);
325
+ return acc;
326
+ },
327
+ {}
328
+ );
329
+ }
330
+ return obj;
331
+ };
332
+ return {
333
+ maskedMessage: maskString(message),
334
+ maskedArgs: args.map((arg) => deepMask(arg))
335
+ };
336
+ }
337
+ /** Global log directory: ~/.code-intel/logs */
338
+ static LOG_DIR = path32.join(os13.homedir(), ".code-intel", "logs");
339
+ static getLogger() {
340
+ if (!_Logger.instance) {
341
+ const isProduction = process.env.NODE_ENV === "production";
342
+ const logLevel = process.env.LOG_LEVEL ?? "info";
343
+ const transports = [];
344
+ transports.push(new winston.transports.Console());
345
+ if (!isProduction) {
346
+ try {
347
+ if (!fs25.existsSync(_Logger.LOG_DIR)) {
348
+ fs25.mkdirSync(_Logger.LOG_DIR, { recursive: true });
349
+ }
350
+ transports.push(
351
+ new DailyRotateFile({
352
+ filename: path32.join(_Logger.LOG_DIR, "%DATE%-code-intel.log"),
353
+ datePattern: "YYYY-MM-DD",
354
+ maxSize: "20m",
355
+ maxFiles: "14d"
356
+ })
357
+ );
358
+ } catch {
359
+ }
360
+ }
361
+ _Logger.instance = winston.createLogger({
362
+ level: logLevel,
363
+ format: winston.format.combine(
364
+ winston.format.timestamp(),
365
+ winston.format.printf(({ timestamp, level, message, ...meta }) => {
366
+ const args = meta[/* @__PURE__ */ Symbol.for("splat")] || [];
367
+ const { maskedMessage, maskedArgs } = _Logger.maskSensitive(message, args);
368
+ const formattedArgs = maskedArgs.map(
369
+ (arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
370
+ );
371
+ const suffix = formattedArgs.length ? " " + formattedArgs.join(" ") : "";
372
+ let traceCtx = "";
373
+ try {
374
+ const { traceId, spanId } = getActiveTraceCtx();
375
+ if (traceId) traceCtx = ` [trace=${traceId} span=${spanId}]`;
376
+ } catch {
377
+ }
378
+ return `${timestamp} [${level.toUpperCase()}]${traceCtx}: ${maskedMessage}${suffix}`;
379
+ })
380
+ ),
381
+ transports
382
+ });
383
+ }
384
+ return _Logger.instance;
385
+ }
386
+ static info(message, ...args) {
387
+ _Logger.getLogger().info(message, ...args);
388
+ }
389
+ static warn(message, ...args) {
390
+ _Logger.getLogger().warn(message, ...args);
391
+ }
392
+ static error(message, ...args) {
393
+ _Logger.getLogger().error(message, ...args);
394
+ }
395
+ static debug(message, ...args) {
396
+ _Logger.getLogger().debug(message, ...args);
397
+ }
398
+ };
399
+ logger_default = Logger;
400
+ Logger.getLogger();
257
401
  }
258
- return captures;
259
- }
260
- function runQueryMatches(tree, language, querySource) {
261
- const query = getOrCompileQuery(language, querySource);
262
- const raw = query.matches(tree.rootNode);
263
- return raw.map((m) => ({
264
- patternIndex: m.patternIndex,
265
- captures: m.captures.map((c) => ({
266
- name: c.name,
267
- node: c.node,
268
- text: c.node.text
269
- }))
270
- }));
271
- }
272
- var _queryCache;
273
- var init_query_runner = __esm({
274
- "src/parsing/query-runner.ts"() {
275
- _queryCache = /* @__PURE__ */ new WeakMap();
402
+ });
403
+
404
+ // src/shared/languages.ts
405
+ var init_languages = __esm({
406
+ "src/shared/languages.ts"() {
276
407
  }
277
408
  });
278
409
 
279
- // src/parsing/queries/typescript.ts
280
- var typescriptQueries;
281
- var init_typescript = __esm({
282
- "src/parsing/queries/typescript.ts"() {
410
+ // src/shared/detection.ts
411
+ function detectLanguage(filePath) {
412
+ const ext = filePath.slice(filePath.lastIndexOf("."));
413
+ return EXTENSION_MAP[ext] ?? null;
414
+ }
415
+ function getSupportedExtensions() {
416
+ return Object.keys(EXTENSION_MAP);
417
+ }
418
+ var EXTENSION_MAP;
419
+ var init_detection = __esm({
420
+ "src/shared/detection.ts"() {
421
+ init_languages();
422
+ EXTENSION_MAP = {
423
+ ".ts": "typescript" /* TypeScript */,
424
+ ".tsx": "typescript" /* TypeScript */,
425
+ ".mts": "typescript" /* TypeScript */,
426
+ ".cts": "typescript" /* TypeScript */,
427
+ ".js": "javascript" /* JavaScript */,
428
+ ".jsx": "javascript" /* JavaScript */,
429
+ ".mjs": "javascript" /* JavaScript */,
430
+ ".cjs": "javascript" /* JavaScript */,
431
+ ".py": "python" /* Python */,
432
+ ".pyi": "python" /* Python */,
433
+ ".java": "java" /* Java */,
434
+ ".go": "go" /* Go */,
435
+ ".c": "c" /* C */,
436
+ ".h": "c" /* C */,
437
+ ".cpp": "cpp" /* Cpp */,
438
+ ".cxx": "cpp" /* Cpp */,
439
+ ".cc": "cpp" /* Cpp */,
440
+ ".hpp": "cpp" /* Cpp */,
441
+ ".hxx": "cpp" /* Cpp */,
442
+ ".cs": "csharp" /* CSharp */,
443
+ ".rs": "rust" /* Rust */,
444
+ ".php": "php" /* PHP */,
445
+ ".kt": "kotlin" /* Kotlin */,
446
+ ".kts": "kotlin" /* Kotlin */,
447
+ ".rb": "ruby" /* Ruby */,
448
+ ".swift": "swift" /* Swift */,
449
+ ".dart": "dart" /* Dart */
450
+ };
451
+ }
452
+ });
453
+
454
+ // src/shared/index.ts
455
+ var init_shared = __esm({
456
+ "src/shared/index.ts"() {
457
+ init_languages();
458
+ init_detection();
459
+ }
460
+ });
461
+ function findBundledWasmDir() {
462
+ const fileDir = path32.dirname(fileURLToPath(import.meta.url));
463
+ const candidates = [
464
+ path32.join(fileDir, "wasm"),
465
+ // dist/index.js → dist/wasm/
466
+ path32.join(fileDir, "../wasm")
467
+ // dist/cli/main.js → dist/wasm/
468
+ ];
469
+ for (const candidate of candidates) {
470
+ if (existsSync(candidate)) return candidate;
471
+ }
472
+ return candidates[0];
473
+ }
474
+ function wasmPath(lang) {
475
+ const WASM_PACKAGE_MAP = {
476
+ ["typescript" /* TypeScript */]: "tree-sitter-typescript/tree-sitter-typescript.wasm",
477
+ ["javascript" /* JavaScript */]: "tree-sitter-javascript/tree-sitter-javascript.wasm",
478
+ ["python" /* Python */]: "tree-sitter-python/tree-sitter-python.wasm",
479
+ ["java" /* Java */]: "tree-sitter-java/tree-sitter-java.wasm",
480
+ ["go" /* Go */]: "tree-sitter-go/tree-sitter-go.wasm",
481
+ ["c" /* C */]: "tree-sitter-c/tree-sitter-c.wasm",
482
+ ["cpp" /* Cpp */]: "tree-sitter-cpp/tree-sitter-cpp.wasm",
483
+ ["csharp" /* CSharp */]: "tree-sitter-c-sharp/tree-sitter-c_sharp.wasm",
484
+ ["rust" /* Rust */]: "tree-sitter-rust/tree-sitter-rust.wasm",
485
+ ["php" /* PHP */]: "tree-sitter-php/tree-sitter-php.wasm",
486
+ ["ruby" /* Ruby */]: "tree-sitter-ruby/tree-sitter-ruby.wasm",
487
+ // These are optional dependencies; their packages may or may not include
488
+ // a WASM. If require.resolve fails we fall back to the bundled wasm/.
489
+ ["swift" /* Swift */]: "tree-sitter-swift/tree-sitter-swift.wasm",
490
+ ["kotlin" /* Kotlin */]: "tree-sitter-kotlin/tree-sitter-kotlin.wasm",
491
+ ["dart" /* Dart */]: "tree-sitter-dart/tree-sitter-dart.wasm"
492
+ };
493
+ const BUNDLED_WASM_MAP = {
494
+ ["swift" /* Swift */]: "tree-sitter-swift.wasm",
495
+ ["kotlin" /* Kotlin */]: "tree-sitter-kotlin.wasm",
496
+ ["dart" /* Dart */]: "tree-sitter-dart.wasm"
497
+ };
498
+ const relative = WASM_PACKAGE_MAP[lang];
499
+ if (relative) {
500
+ try {
501
+ return _require.resolve(relative);
502
+ } catch {
503
+ }
504
+ }
505
+ const bundled = BUNDLED_WASM_MAP[lang];
506
+ if (bundled) {
507
+ const bundledPath = path32.join(_bundledWasmDir, bundled);
508
+ if (existsSync(bundledPath)) return bundledPath;
509
+ }
510
+ return null;
511
+ }
512
+ async function initParser() {
513
+ if (!initPromise) {
514
+ initPromise = Parser.init();
515
+ }
516
+ return initPromise;
517
+ }
518
+ async function getLanguage(lang) {
519
+ if (languageCache.has(lang)) return languageCache.get(lang);
520
+ const path33 = wasmPath(lang);
521
+ if (!path33) {
522
+ languageCache.set(lang, null);
523
+ return null;
524
+ }
525
+ try {
526
+ await initParser();
527
+ const language = await Language.load(path33);
528
+ languageCache.set(lang, language);
529
+ return language;
530
+ } catch {
531
+ languageCache.set(lang, null);
532
+ return null;
533
+ }
534
+ }
535
+ async function getParser(lang) {
536
+ const language = await getLanguage(lang);
537
+ if (!language) return null;
538
+ let parser = parserCache.get(lang);
539
+ if (!parser) {
540
+ parser = new Parser();
541
+ parserCache.set(lang, parser);
542
+ }
543
+ parser.setLanguage(language);
544
+ return parser;
545
+ }
546
+ async function parseSource(lang, source) {
547
+ const parser = await getParser(lang);
548
+ if (!parser) return null;
549
+ return parser.parse(source);
550
+ }
551
+ async function isTreeSitterAvailable(lang) {
552
+ return await getLanguage(lang) !== null;
553
+ }
554
+ var _require, _bundledWasmDir, initPromise, languageCache, parserCache;
555
+ var init_parser_manager = __esm({
556
+ "src/parsing/parser-manager.ts"() {
557
+ init_shared();
558
+ _require = createRequire(import.meta.url);
559
+ _bundledWasmDir = findBundledWasmDir();
560
+ initPromise = null;
561
+ languageCache = /* @__PURE__ */ new Map();
562
+ parserCache = /* @__PURE__ */ new Map();
563
+ }
564
+ });
565
+ function getOrCompileQuery(language, querySource) {
566
+ let langMap = _queryCache.get(language);
567
+ if (!langMap) {
568
+ langMap = /* @__PURE__ */ new Map();
569
+ _queryCache.set(language, langMap);
570
+ }
571
+ let q = langMap.get(querySource);
572
+ if (!q) {
573
+ q = new Query(language, querySource);
574
+ langMap.set(querySource, q);
575
+ }
576
+ return q;
577
+ }
578
+ function runQuery(tree, language, querySource) {
579
+ const query = getOrCompileQuery(language, querySource);
580
+ const matches = query.matches(tree.rootNode);
581
+ const captures = [];
582
+ for (const match of matches) {
583
+ for (const capture of match.captures) {
584
+ captures.push({
585
+ name: capture.name,
586
+ node: capture.node,
587
+ text: capture.node.text
588
+ });
589
+ }
590
+ }
591
+ return captures;
592
+ }
593
+ function runQueryMatches(tree, language, querySource) {
594
+ const query = getOrCompileQuery(language, querySource);
595
+ const raw = query.matches(tree.rootNode);
596
+ return raw.map((m) => ({
597
+ patternIndex: m.patternIndex,
598
+ captures: m.captures.map((c) => ({
599
+ name: c.name,
600
+ node: c.node,
601
+ text: c.node.text
602
+ }))
603
+ }));
604
+ }
605
+ var _queryCache;
606
+ var init_query_runner = __esm({
607
+ "src/parsing/query-runner.ts"() {
608
+ _queryCache = /* @__PURE__ */ new WeakMap();
609
+ }
610
+ });
611
+
612
+ // src/parsing/queries/typescript.ts
613
+ var typescriptQueries;
614
+ var init_typescript = __esm({
615
+ "src/parsing/queries/typescript.ts"() {
283
616
  typescriptQueries = `
284
617
  ;; Class declaration
285
618
  (class_declaration
@@ -890,341 +1223,8 @@ var init_swift = __esm({
890
1223
  ;; Property declaration
891
1224
  (property_declaration
892
1225
  (pattern
893
- (simple_identifier) @def.property.name)) @def.property
894
- `;
895
- }
896
- });
897
-
898
- // src/observability/tracing.ts
899
- var tracing_exports = {};
900
- __export(tracing_exports, {
901
- SpanStatusCode: () => SpanStatusCode,
902
- context: () => context,
903
- getActiveTraceContext: () => getActiveTraceContext,
904
- getTracer: () => getTracer,
905
- initTracing: () => initTracing,
906
- isTracingEnabled: () => isTracingEnabled,
907
- sanitizeAttrs: () => sanitizeAttrs,
908
- shutdownTracing: () => shutdownTracing,
909
- trace: () => trace,
910
- withSpan: () => withSpan
911
- });
912
- function isTracingEnabled() {
913
- return process.env["CODE_INTEL_OTEL_ENABLED"] === "true";
914
- }
915
- function initTracing() {
916
- if (!isTracingEnabled()) return;
917
- if (_sdk) return;
918
- const endpoint = process.env["CODE_INTEL_OTEL_ENDPOINT"] ?? "http://localhost:4318";
919
- const serviceName = process.env["CODE_INTEL_OTEL_SERVICE"] ?? "code-intel";
920
- const deploymentEnv = process.env["CODE_INTEL_OTEL_ENV"] ?? process.env["NODE_ENV"] ?? "development";
921
- const exporter = new OTLPTraceExporter({ url: `${endpoint}/v1/traces` });
922
- _sdk = new NodeSDK({
923
- resource: resourceFromAttributes({
924
- [SEMRESATTRS_SERVICE_NAME]: serviceName,
925
- [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: deploymentEnv
926
- }),
927
- traceExporter: exporter,
928
- instrumentations: [
929
- getNodeAutoInstrumentations({
930
- // Disable noisy file-system instrumentation
931
- "@opentelemetry/instrumentation-fs": { enabled: false }
932
- })
933
- ]
934
- });
935
- _sdk.start();
936
- }
937
- async function shutdownTracing() {
938
- if (_sdk) {
939
- await _sdk.shutdown();
940
- _sdk = null;
941
- }
942
- }
943
- function getTracer() {
944
- return trace.getTracer(TRACER_NAME);
945
- }
946
- async function withSpan(name, attrs, fn) {
947
- return getTracer().startActiveSpan(name, { attributes: sanitizeAttrs(attrs) }, async (span) => {
948
- try {
949
- const result = await fn(span);
950
- span.setStatus({ code: SpanStatusCode.OK });
951
- return result;
952
- } catch (err) {
953
- span.setStatus({
954
- code: SpanStatusCode.ERROR,
955
- message: err instanceof Error ? err.message : String(err)
956
- });
957
- span.recordException(err);
958
- throw err;
959
- } finally {
960
- span.end();
961
- }
962
- });
963
- }
964
- function sanitizeAttrs(attrs) {
965
- const safe = {};
966
- for (const [k, v] of Object.entries(attrs)) {
967
- if (BLOCKED_ATTR_KEYS.test(k)) continue;
968
- safe[k] = v;
969
- }
970
- return safe;
971
- }
972
- function getActiveTraceContext() {
973
- const span = trace.getActiveSpan();
974
- if (!span) return { traceId: "", spanId: "" };
975
- const ctx = span.spanContext();
976
- return { traceId: ctx.traceId, spanId: ctx.spanId };
977
- }
978
- var _sdk, TRACER_NAME, BLOCKED_ATTR_KEYS;
979
- var init_tracing = __esm({
980
- "src/observability/tracing.ts"() {
981
- _sdk = null;
982
- TRACER_NAME = "code-intel";
983
- BLOCKED_ATTR_KEYS = /secret|password|token|key|auth|credential/i;
984
- }
985
- });
986
- function getActiveTraceCtx() {
987
- if (_getTraceCtx === "pending") return { traceId: "", spanId: "" };
988
- if (_getTraceCtx) return _getTraceCtx();
989
- _getTraceCtx = "pending";
990
- Promise.resolve().then(() => (init_tracing(), tracing_exports)).then((mod) => {
991
- _getTraceCtx = mod.getActiveTraceContext;
992
- }).catch(() => {
993
- _getTraceCtx = null;
994
- });
995
- return { traceId: "", spanId: "" };
996
- }
997
- var _getTraceCtx, SENSITIVE_KEYS, SENSITIVE_PATTERNS, SENSITIVE_KEYS_REGEX, Logger, logger_default;
998
- var init_logger = __esm({
999
- "src/shared/logger.ts"() {
1000
- _getTraceCtx = null;
1001
- SENSITIVE_KEYS = [
1002
- "password",
1003
- "passwd",
1004
- "pass",
1005
- "pwd",
1006
- "secret",
1007
- "secretkey",
1008
- "secret_key",
1009
- "secretaccesskey",
1010
- "accesskeyid",
1011
- "credentials",
1012
- "auth",
1013
- "authentication",
1014
- "login",
1015
- "api_key",
1016
- "apikey",
1017
- "api",
1018
- "access_key",
1019
- "access_token",
1020
- "accesskey",
1021
- "auth_key",
1022
- "auth_token",
1023
- "authkey",
1024
- "token",
1025
- "jwt",
1026
- "bearer_token",
1027
- "refresh_token",
1028
- "session_token",
1029
- "session_key",
1030
- "oauth_token",
1031
- "connection_string",
1032
- "conn_string",
1033
- "db_uri",
1034
- "db_url",
1035
- "database_url",
1036
- "mongodb_uri",
1037
- "mysql_uri",
1038
- "postgres_uri",
1039
- "sql_uri",
1040
- "db_username",
1041
- "db_password",
1042
- "db_host",
1043
- "db_port",
1044
- "db_name",
1045
- "encryption_key",
1046
- "crypto_key",
1047
- "private_key",
1048
- "public_key",
1049
- "ssl_key",
1050
- "ssh_key",
1051
- "pgp_key",
1052
- "rsa_key",
1053
- "aes_key",
1054
- "email",
1055
- "phone",
1056
- "telephone",
1057
- "mobile",
1058
- "ssn",
1059
- "social_security",
1060
- "credit_card",
1061
- "cc_number",
1062
- "card_number",
1063
- "cvv",
1064
- "expiry_date",
1065
- "birth_date",
1066
- "dob",
1067
- "address",
1068
- "zip_code",
1069
- "postal_code",
1070
- "bank_account",
1071
- "iban",
1072
- "swift_code",
1073
- "routing_number",
1074
- "tax_id",
1075
- "vat_number",
1076
- "financial_id",
1077
- "certificate",
1078
- "client_cert",
1079
- "server_cert",
1080
- "ca_cert",
1081
- "aws_key",
1082
- "aws_secret",
1083
- "azure_key",
1084
- "gcp_key",
1085
- "s3_key",
1086
- "cloudinary_key",
1087
- "stripe_key",
1088
- "paypal_key",
1089
- "twilio_key",
1090
- "app_secret",
1091
- "client_secret",
1092
- "consumer_secret",
1093
- "encryption_secret",
1094
- "master_key",
1095
- "root_password",
1096
- "admin_password",
1097
- "config_secret",
1098
- "env_secret",
1099
- "deploy_key",
1100
- "ci_key",
1101
- "session_id",
1102
- "cookie_secret",
1103
- "csrf_token",
1104
- "xsrf_token",
1105
- "license_key",
1106
- "product_key",
1107
- "serial_number",
1108
- "activation_code"
1109
- ];
1110
- SENSITIVE_PATTERNS = [
1111
- /(?:password|passwd|secret|api_key|access_token|auth_token|token)\s*[:=]\s*([^\s,]+)/gi,
1112
- /\b\d{16}\b/gi,
1113
- /\b\d{3}-\d{2}-\d{4}\b/gi,
1114
- /\b[A-Za-z0-9]{32}\b/gi,
1115
- /\b[A-Za-z0-9_-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b/gi,
1116
- /\b\d{10}\b/gi,
1117
- /\b[A-Za-z0-9]{64}\b/gi,
1118
- /(?:connection_string|db_uri|db_url|mongodb_uri)\s*[:=]\s*([^\s,]+)/gi,
1119
- /(?:apikey|api_key|auth_key)\s*[:=]\s*([^\s,]+)/gi,
1120
- /(?:bearer\s+)([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)/gi
1121
- ];
1122
- SENSITIVE_KEYS_REGEX = new RegExp(`^(${SENSITIVE_KEYS.join("|")})$`, "i");
1123
- Logger = class _Logger {
1124
- static instance = null;
1125
- static maskSensitiveData(value) {
1126
- if (typeof value === "string" && value.length > 5) {
1127
- const firstChar = value.at(0);
1128
- const lastChar = value.at(-1);
1129
- return firstChar + "*".repeat(value.length - 2) + lastChar;
1130
- }
1131
- return value;
1132
- }
1133
- static maskSensitive(message, args = []) {
1134
- const maskString = (input) => {
1135
- if (typeof input !== "string") return input;
1136
- return SENSITIVE_PATTERNS.reduce((str, pattern) => {
1137
- return str.replace(
1138
- pattern,
1139
- (match, value) => value ? match.replace(value, _Logger.maskSensitiveData(value)) : match
1140
- );
1141
- }, input);
1142
- };
1143
- const deepMask = (obj) => {
1144
- if (typeof obj === "string") return maskString(obj);
1145
- if (Array.isArray(obj)) return obj.map((item) => deepMask(item));
1146
- if (typeof obj === "object" && obj !== null) {
1147
- return Object.entries(obj).reduce(
1148
- (acc, [key, value]) => {
1149
- if (value === void 0) return acc;
1150
- const isSensitiveKey = SENSITIVE_KEYS_REGEX.test(key);
1151
- acc[key] = isSensitiveKey && typeof value === "string" ? _Logger.maskSensitiveData(value) : deepMask(value);
1152
- return acc;
1153
- },
1154
- {}
1155
- );
1156
- }
1157
- return obj;
1158
- };
1159
- return {
1160
- maskedMessage: maskString(message),
1161
- maskedArgs: args.map((arg) => deepMask(arg))
1162
- };
1163
- }
1164
- /** Global log directory: ~/.code-intel/logs */
1165
- static LOG_DIR = path31.join(os12.homedir(), ".code-intel", "logs");
1166
- static getLogger() {
1167
- if (!_Logger.instance) {
1168
- const isProduction = process.env.NODE_ENV === "production";
1169
- const logLevel = process.env.LOG_LEVEL ?? "info";
1170
- const transports = [];
1171
- transports.push(new winston.transports.Console());
1172
- if (!isProduction) {
1173
- try {
1174
- if (!fs24.existsSync(_Logger.LOG_DIR)) {
1175
- fs24.mkdirSync(_Logger.LOG_DIR, { recursive: true });
1176
- }
1177
- transports.push(
1178
- new DailyRotateFile({
1179
- filename: path31.join(_Logger.LOG_DIR, "%DATE%-code-intel.log"),
1180
- datePattern: "YYYY-MM-DD",
1181
- maxSize: "20m",
1182
- maxFiles: "14d"
1183
- })
1184
- );
1185
- } catch {
1186
- }
1187
- }
1188
- _Logger.instance = winston.createLogger({
1189
- level: logLevel,
1190
- format: winston.format.combine(
1191
- winston.format.timestamp(),
1192
- winston.format.printf(({ timestamp, level, message, ...meta }) => {
1193
- const args = meta[/* @__PURE__ */ Symbol.for("splat")] || [];
1194
- const { maskedMessage, maskedArgs } = _Logger.maskSensitive(message, args);
1195
- const formattedArgs = maskedArgs.map(
1196
- (arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
1197
- );
1198
- const suffix = formattedArgs.length ? " " + formattedArgs.join(" ") : "";
1199
- let traceCtx = "";
1200
- try {
1201
- const { traceId, spanId } = getActiveTraceCtx();
1202
- if (traceId) traceCtx = ` [trace=${traceId} span=${spanId}]`;
1203
- } catch {
1204
- }
1205
- return `${timestamp} [${level.toUpperCase()}]${traceCtx}: ${maskedMessage}${suffix}`;
1206
- })
1207
- ),
1208
- transports
1209
- });
1210
- }
1211
- return _Logger.instance;
1212
- }
1213
- static info(message, ...args) {
1214
- _Logger.getLogger().info(message, ...args);
1215
- }
1216
- static warn(message, ...args) {
1217
- _Logger.getLogger().warn(message, ...args);
1218
- }
1219
- static error(message, ...args) {
1220
- _Logger.getLogger().error(message, ...args);
1221
- }
1222
- static debug(message, ...args) {
1223
- _Logger.getLogger().debug(message, ...args);
1224
- }
1225
- };
1226
- logger_default = Logger;
1227
- Logger.getLogger();
1226
+ (simple_identifier) @def.property.name)) @def.property
1227
+ `;
1228
1228
  }
1229
1229
  });
1230
1230
 
@@ -1961,7 +1961,7 @@ var init_parse_phase = __esm({
1961
1961
  const batch = filePaths.slice(i, i + CONCURRENCY);
1962
1962
  await Promise.all(batch.map(async (filePath) => {
1963
1963
  try {
1964
- const source = await fs24.promises.readFile(filePath, "utf-8");
1964
+ const source = await fs25.promises.readFile(filePath, "utf-8");
1965
1965
  context2.fileCache.set(filePath, source);
1966
1966
  } catch {
1967
1967
  }
@@ -1974,14 +1974,14 @@ var init_parse_phase = __esm({
1974
1974
  const lang = detectLanguage(filePath);
1975
1975
  if (!lang) {
1976
1976
  if (context2.verbose) {
1977
- const relativePath2 = path31.relative(context2.workspaceRoot, filePath);
1977
+ const relativePath2 = path32.relative(context2.workspaceRoot, filePath);
1978
1978
  logger_default.info(` [parse] skipped (no parser): ${relativePath2}`);
1979
1979
  }
1980
1980
  continue;
1981
1981
  }
1982
1982
  const source = context2.fileCache.get(filePath);
1983
1983
  if (!source) continue;
1984
- const relativePath = path31.relative(context2.workspaceRoot, filePath);
1984
+ const relativePath = path32.relative(context2.workspaceRoot, filePath);
1985
1985
  const fileNodeId = generateNodeId("file", relativePath, relativePath);
1986
1986
  const fileNode = context2.graph.getNode(fileNodeId);
1987
1987
  if (fileNode) {
@@ -2221,11 +2221,11 @@ var init_resolve_phase = __esm({
2221
2221
  let heritageEdges = 0;
2222
2222
  const fileIndex = /* @__PURE__ */ new Map();
2223
2223
  for (const fp of filePaths) {
2224
- const rel = path31.relative(workspaceRoot, fp);
2224
+ const rel = path32.relative(workspaceRoot, fp);
2225
2225
  fileIndex.set(rel, fp);
2226
2226
  const noExt = rel.replace(/\.\w+$/, "");
2227
2227
  if (!fileIndex.has(noExt)) fileIndex.set(noExt, fp);
2228
- const base = path31.basename(rel, path31.extname(rel));
2228
+ const base = path32.basename(rel, path32.extname(rel));
2229
2229
  if (!fileIndex.has(base)) fileIndex.set(base, fp);
2230
2230
  }
2231
2231
  const symbolIndex = /* @__PURE__ */ new Map();
@@ -2256,7 +2256,7 @@ var init_resolve_phase = __esm({
2256
2256
  for (const filePath of filePaths) {
2257
2257
  const lang = detectLanguage(filePath);
2258
2258
  if (!lang) continue;
2259
- const relativePath = path31.relative(workspaceRoot, filePath);
2259
+ const relativePath = path32.relative(workspaceRoot, filePath);
2260
2260
  const fileNodeId = generateNodeId("file", relativePath, relativePath);
2261
2261
  const source = fileCache.get(filePath);
2262
2262
  if (!source) continue;
@@ -2269,13 +2269,13 @@ var init_resolve_phase = __esm({
2269
2269
  let resolvedRelPath = null;
2270
2270
  if (cleaned.startsWith(".")) {
2271
2271
  const cleanedNoJs = cleaned.replace(/\.(js|jsx)$/, "");
2272
- const fromDir = path31.dirname(relativePath);
2272
+ const fromDir = path32.dirname(relativePath);
2273
2273
  for (const ext of ["", ".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", "/index.ts", "/index.js"]) {
2274
- const candidate = path31.join(fromDir, cleanedNoJs + ext);
2275
- const normalized = path31.normalize(candidate);
2274
+ const candidate = path32.join(fromDir, cleanedNoJs + ext);
2275
+ const normalized = path32.normalize(candidate);
2276
2276
  if (fileIndex.has(normalized)) {
2277
2277
  const absPath = fileIndex.get(normalized);
2278
- resolvedRelPath = path31.relative(workspaceRoot, absPath);
2278
+ resolvedRelPath = path32.relative(workspaceRoot, absPath);
2279
2279
  break;
2280
2280
  }
2281
2281
  }
@@ -2589,27 +2589,27 @@ __export(group_registry_exports, {
2589
2589
  saveSyncResult: () => saveSyncResult
2590
2590
  });
2591
2591
  function groupFile(name) {
2592
- return path31.join(GROUPS_DIR, `${name}.json`);
2592
+ return path32.join(GROUPS_DIR, `${name}.json`);
2593
2593
  }
2594
2594
  function loadGroup(name) {
2595
2595
  try {
2596
- return JSON.parse(fs24.readFileSync(groupFile(name), "utf-8"));
2596
+ return JSON.parse(fs25.readFileSync(groupFile(name), "utf-8"));
2597
2597
  } catch {
2598
2598
  return null;
2599
2599
  }
2600
2600
  }
2601
2601
  function saveGroup(group) {
2602
- fs24.mkdirSync(GROUPS_DIR, { recursive: true });
2603
- fs24.writeFileSync(groupFile(group.name), JSON.stringify(group, null, 2) + "\n");
2602
+ fs25.mkdirSync(GROUPS_DIR, { recursive: true });
2603
+ fs25.writeFileSync(groupFile(group.name), JSON.stringify(group, null, 2) + "\n");
2604
2604
  }
2605
2605
  function listGroups() {
2606
2606
  const groups = [];
2607
2607
  try {
2608
- for (const file of fs24.readdirSync(GROUPS_DIR)) {
2608
+ for (const file of fs25.readdirSync(GROUPS_DIR)) {
2609
2609
  if (!file.endsWith(".json") || file.endsWith(".sync.json")) continue;
2610
2610
  try {
2611
2611
  const g = JSON.parse(
2612
- fs24.readFileSync(path31.join(GROUPS_DIR, file), "utf-8")
2612
+ fs25.readFileSync(path32.join(GROUPS_DIR, file), "utf-8")
2613
2613
  );
2614
2614
  groups.push(g);
2615
2615
  } catch {
@@ -2621,16 +2621,16 @@ function listGroups() {
2621
2621
  }
2622
2622
  function deleteGroup(name) {
2623
2623
  try {
2624
- fs24.unlinkSync(groupFile(name));
2624
+ fs25.unlinkSync(groupFile(name));
2625
2625
  } catch {
2626
2626
  }
2627
2627
  try {
2628
- fs24.unlinkSync(path31.join(GROUPS_DIR, `${name}.sync.json`));
2628
+ fs25.unlinkSync(path32.join(GROUPS_DIR, `${name}.sync.json`));
2629
2629
  } catch {
2630
2630
  }
2631
2631
  }
2632
2632
  function groupExists(name) {
2633
- return fs24.existsSync(groupFile(name));
2633
+ return fs25.existsSync(groupFile(name));
2634
2634
  }
2635
2635
  function addMember(groupName, member) {
2636
2636
  const group = loadGroup(groupName);
@@ -2656,16 +2656,16 @@ function removeMember(groupName, groupPath) {
2656
2656
  return group;
2657
2657
  }
2658
2658
  function saveSyncResult(result) {
2659
- fs24.mkdirSync(GROUPS_DIR, { recursive: true });
2660
- fs24.writeFileSync(
2661
- path31.join(GROUPS_DIR, `${result.groupName}.sync.json`),
2659
+ fs25.mkdirSync(GROUPS_DIR, { recursive: true });
2660
+ fs25.writeFileSync(
2661
+ path32.join(GROUPS_DIR, `${result.groupName}.sync.json`),
2662
2662
  JSON.stringify(result, null, 2) + "\n"
2663
2663
  );
2664
2664
  }
2665
2665
  function loadSyncResult(groupName) {
2666
2666
  try {
2667
2667
  return JSON.parse(
2668
- fs24.readFileSync(path31.join(GROUPS_DIR, `${groupName}.sync.json`), "utf-8")
2668
+ fs25.readFileSync(path32.join(GROUPS_DIR, `${groupName}.sync.json`), "utf-8")
2669
2669
  );
2670
2670
  } catch {
2671
2671
  return null;
@@ -2674,7 +2674,7 @@ function loadSyncResult(groupName) {
2674
2674
  var GROUPS_DIR;
2675
2675
  var init_group_registry = __esm({
2676
2676
  "src/multi-repo/group-registry.ts"() {
2677
- GROUPS_DIR = path31.join(os12.homedir(), ".code-intel", "groups");
2677
+ GROUPS_DIR = path32.join(os13.homedir(), ".code-intel", "groups");
2678
2678
  }
2679
2679
  });
2680
2680
 
@@ -2964,7 +2964,7 @@ __export(gql_parser_exports, {
2964
2964
  function isGQLParseError(v) {
2965
2965
  return v.type === "GQLParseError";
2966
2966
  }
2967
- function tokenize(input) {
2967
+ function tokenize2(input) {
2968
2968
  const tokens = [];
2969
2969
  let i = 0;
2970
2970
  const len = input.length;
@@ -3059,7 +3059,7 @@ function tokenize(input) {
3059
3059
  return tokens;
3060
3060
  }
3061
3061
  function parseGQL(input) {
3062
- const tokens = tokenize(input.trim());
3062
+ const tokens = tokenize2(input.trim());
3063
3063
  if (!Array.isArray(tokens)) return tokens;
3064
3064
  const parser = new Parser2(tokens);
3065
3065
  return parser.parse();
@@ -3852,7 +3852,7 @@ function isTestFile(filePath) {
3852
3852
  if (filePath.includes(".test.") || filePath.includes(".spec.")) return true;
3853
3853
  if (filePath.includes("_test.") || filePath.endsWith("_test.go")) return true;
3854
3854
  if (filePath.includes("__tests__")) return true;
3855
- const base = path31.basename(filePath);
3855
+ const base = path32.basename(filePath);
3856
3856
  if (base.startsWith("Test") && filePath.endsWith(".java")) return true;
3857
3857
  return false;
3858
3858
  }
@@ -3897,7 +3897,7 @@ function computeCoverage(graph, scope) {
3897
3897
  }
3898
3898
  const baseNameToTestFiles = /* @__PURE__ */ new Map();
3899
3899
  for (const testPath of testFilePaths) {
3900
- const base = path31.basename(testPath);
3900
+ const base = path32.basename(testPath);
3901
3901
  const stripped = base.replace(/\.test\.[^.]+$/, "").replace(/\.spec\.[^.]+$/, "").replace(/_test\.[^.]+$/, "").replace(/_test$/, "");
3902
3902
  const existing = baseNameToTestFiles.get(stripped) ?? [];
3903
3903
  existing.push(testPath);
@@ -3930,7 +3930,7 @@ function computeCoverage(graph, scope) {
3930
3930
  }
3931
3931
  }
3932
3932
  }
3933
- const nodeBase = path31.basename(node.filePath).replace(/\.[^.]+$/, "");
3933
+ const nodeBase = path32.basename(node.filePath).replace(/\.[^.]+$/, "");
3934
3934
  const matchingTestFiles = baseNameToTestFiles.get(nodeBase) ?? [];
3935
3935
  for (const tf of matchingTestFiles) {
3936
3936
  if (!testFiles.includes(tf)) testFiles.push(tf);
@@ -4000,7 +4000,7 @@ var init_secret_scanner = __esm({
4000
4000
  const ignorePatterns = [...options?.ignorePatterns ?? []];
4001
4001
  if (options?.workspaceRoot) {
4002
4002
  try {
4003
- const raw = fs24.readFileSync(path31.join(options.workspaceRoot, ".codeintelignore"), "utf-8");
4003
+ const raw = fs25.readFileSync(path32.join(options.workspaceRoot, ".codeintelignore"), "utf-8");
4004
4004
  for (const line of raw.split("\n")) {
4005
4005
  const trimmed = line.trim();
4006
4006
  if (trimmed && !trimmed.startsWith("#")) ignorePatterns.push(trimmed);
@@ -4245,12 +4245,11 @@ var init_vulnerability_detector = __esm({
4245
4245
  };
4246
4246
  }
4247
4247
  });
4248
-
4249
- // src/errors/codes.ts
4250
4248
  var ErrorCodes, AppError;
4251
4249
  var init_codes = __esm({
4252
4250
  "src/errors/codes.ts"() {
4253
4251
  ErrorCodes = {
4252
+ // Auth (CI-1xxx)
4254
4253
  UNAUTHORIZED: "CI-1000",
4255
4254
  FORBIDDEN: "CI-1001",
4256
4255
  NOT_FOUND: "CI-1002",
@@ -4260,6 +4259,16 @@ var init_codes = __esm({
4260
4259
  RATE_LIMIT_EXCEEDED: "CI-1100",
4261
4260
  PAYLOAD_TOO_LARGE: "CI-1101",
4262
4261
  INVALID_REQUEST: "CI-1200",
4262
+ // Config (CI-2xxx)
4263
+ CONFIG_INVALID: "CI-2000",
4264
+ CONFIG_NOT_FOUND: "CI-2001",
4265
+ // Analysis (CI-3xxx)
4266
+ ANALYSIS_FAILED: "CI-3000",
4267
+ // DB (CI-4xxx)
4268
+ DB_ERROR: "CI-4000",
4269
+ // Network (CI-5xxx)
4270
+ NETWORK_ERROR: "CI-5001",
4271
+ // Internal (CI-9xxx)
4263
4272
  INTERNAL_ERROR: "CI-5000"
4264
4273
  };
4265
4274
  AppError = class extends Error {
@@ -4279,10 +4288,10 @@ var init_codes = __esm({
4279
4288
  }
4280
4289
  });
4281
4290
  function secureMkdir(dir) {
4282
- fs24.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
4291
+ fs25.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
4283
4292
  if (process.platform !== "win32") {
4284
4293
  try {
4285
- fs24.chmodSync(dir, SECURE_DIR_MODE);
4294
+ fs25.chmodSync(dir, SECURE_DIR_MODE);
4286
4295
  } catch {
4287
4296
  }
4288
4297
  }
@@ -4290,17 +4299,17 @@ function secureMkdir(dir) {
4290
4299
  function secureChmodFile(file) {
4291
4300
  if (process.platform === "win32") return;
4292
4301
  try {
4293
- fs24.chmodSync(file, SECURE_FILE_MODE);
4302
+ fs25.chmodSync(file, SECURE_FILE_MODE);
4294
4303
  } catch {
4295
4304
  }
4296
4305
  }
4297
4306
  function tightenDbFiles(dir) {
4298
4307
  if (process.platform === "win32") return;
4299
- if (!fs24.existsSync(dir)) return;
4300
- for (const name of fs24.readdirSync(dir)) {
4308
+ if (!fs25.existsSync(dir)) return;
4309
+ for (const name of fs25.readdirSync(dir)) {
4301
4310
  if (name.endsWith(".db") || name.endsWith(".db-wal") || name.endsWith(".db-shm")) {
4302
4311
  try {
4303
- fs24.chmodSync(path31.join(dir, name), SECURE_FILE_MODE);
4312
+ fs25.chmodSync(path32.join(dir, name), SECURE_FILE_MODE);
4304
4313
  } catch {
4305
4314
  }
4306
4315
  }
@@ -4314,7 +4323,7 @@ var init_fs_secure = __esm({
4314
4323
  }
4315
4324
  });
4316
4325
  function getUsersDBPath() {
4317
- return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path31.join(os12.homedir(), ".code-intel", "users.db");
4326
+ return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path32.join(os13.homedir(), ".code-intel", "users.db");
4318
4327
  }
4319
4328
  function getOrCreateUsersDB() {
4320
4329
  if (!_usersDB) {
@@ -4330,9 +4339,9 @@ var init_users_db = __esm({
4330
4339
  UsersDB = class {
4331
4340
  db;
4332
4341
  constructor(dbPath) {
4333
- const dir = path31.dirname(dbPath);
4342
+ const dir = path32.dirname(dbPath);
4334
4343
  secureMkdir(dir);
4335
- this.db = new Database3(dbPath);
4344
+ this.db = new Database2(dbPath);
4336
4345
  this.db.pragma("journal_mode = WAL");
4337
4346
  this.db.pragma("foreign_keys = ON");
4338
4347
  this.createTables();
@@ -4607,17 +4616,17 @@ function getScryptN() {
4607
4616
  return Number.isInteger(v) && v >= 1024 ? v : 1 << 14;
4608
4617
  }
4609
4618
  function getSecretsPath() {
4610
- return process.env["CODE_INTEL_SECRETS_PATH"] ?? path31.join(os12.homedir(), ".code-intel", ".secrets");
4619
+ return process.env["CODE_INTEL_SECRETS_PATH"] ?? path32.join(os13.homedir(), ".code-intel", ".secrets");
4611
4620
  }
4612
4621
  function getMasterPassword() {
4613
4622
  const fromEnv = process.env["CODE_INTEL_SECRET_KEY"];
4614
4623
  if (fromEnv && fromEnv.length >= 16) return fromEnv;
4615
4624
  let username = "unknown";
4616
4625
  try {
4617
- username = os12.userInfo().username;
4626
+ username = os13.userInfo().username;
4618
4627
  } catch {
4619
4628
  }
4620
- return `code-intel-machine:${os12.hostname()}:${username}:${os12.platform()}`;
4629
+ return `code-intel-machine:${os13.hostname()}:${username}:${os13.platform()}`;
4621
4630
  }
4622
4631
  function deriveKey(password, salt) {
4623
4632
  return crypto5.scryptSync(password, salt, KEY_LEN, { N: getScryptN(), r: 8, p: 1 });
@@ -4638,8 +4647,8 @@ function decryptSecrets(encrypted) {
4638
4647
  return JSON.parse(plaintext.toString("utf8"));
4639
4648
  }
4640
4649
  function loadSecrets(secretsPath = getSecretsPath()) {
4641
- if (!fs24.existsSync(secretsPath)) return {};
4642
- const blob = fs24.readFileSync(secretsPath);
4650
+ if (!fs25.existsSync(secretsPath)) return {};
4651
+ const blob = fs25.readFileSync(secretsPath);
4643
4652
  return decryptSecrets(blob);
4644
4653
  }
4645
4654
  function getSecret(key, secretsPath = getSecretsPath()) {
@@ -5149,6 +5158,71 @@ function createKnowledgeGraph() {
5149
5158
  // src/graph/index.ts
5150
5159
  init_id_generator();
5151
5160
 
5161
+ // src/storage/schema.ts
5162
+ var NODE_TABLE_MAP = {
5163
+ file: "file_nodes",
5164
+ directory: "dir_nodes",
5165
+ function: "func_nodes",
5166
+ class: "class_nodes",
5167
+ interface: "iface_nodes",
5168
+ method: "method_nodes",
5169
+ constructor: "ctor_nodes",
5170
+ variable: "var_nodes",
5171
+ property: "prop_nodes",
5172
+ struct: "struct_nodes",
5173
+ enum: "enum_nodes",
5174
+ trait: "trait_nodes",
5175
+ namespace: "ns_nodes",
5176
+ module: "mod_nodes",
5177
+ type_alias: "type_nodes",
5178
+ constant: "const_nodes",
5179
+ route: "route_nodes",
5180
+ cluster: "cluster_nodes",
5181
+ flow: "flow_nodes",
5182
+ vulnerability: "vuln_nodes"
5183
+ };
5184
+ var ALL_NODE_TABLES = [...new Set(Object.values(NODE_TABLE_MAP))];
5185
+ function getCreateNodeTableDDL(tableName) {
5186
+ return `CREATE NODE TABLE IF NOT EXISTS ${tableName} (
5187
+ id STRING,
5188
+ name STRING,
5189
+ file_path STRING,
5190
+ start_line INT64,
5191
+ end_line INT64,
5192
+ exported BOOLEAN,
5193
+ content STRING,
5194
+ metadata STRING,
5195
+ PRIMARY KEY (id)
5196
+ )`;
5197
+ }
5198
+ function getCreateEdgeTableDDL() {
5199
+ const uniqueTables = ALL_NODE_TABLES;
5200
+ const fromToPairs = [];
5201
+ for (const from of uniqueTables) {
5202
+ for (const to of uniqueTables) {
5203
+ fromToPairs.push(`FROM ${from} TO ${to}`);
5204
+ }
5205
+ }
5206
+ return [`CREATE REL TABLE GROUP IF NOT EXISTS code_edges (
5207
+ ${fromToPairs.join(",\n ")},
5208
+ kind STRING,
5209
+ weight DOUBLE,
5210
+ label STRING
5211
+ )`];
5212
+ }
5213
+
5214
+ // src/graph/lazy-knowledge-graph.ts
5215
+ init_logger();
5216
+ Object.fromEntries(
5217
+ Object.entries(NODE_TABLE_MAP).map(([kind, table]) => [table, kind])
5218
+ );
5219
+ function isLazyGraph(g) {
5220
+ return "lazy" in g && g.lazy === true;
5221
+ }
5222
+
5223
+ // src/graph/compact-knowledge-graph.ts
5224
+ init_logger();
5225
+
5152
5226
  // src/parsing/index.ts
5153
5227
  init_parser_manager();
5154
5228
  init_query_runner();
@@ -5206,7 +5280,7 @@ init_shared();
5206
5280
  init_shared();
5207
5281
  init_typescript();
5208
5282
  function resolveRelative(rawPath, fromFile, workspace) {
5209
- const fromDir = path31.dirname(fromFile);
5283
+ const fromDir = path32.dirname(fromFile);
5210
5284
  const cleaned = rawPath.replace(/['"]/g, "");
5211
5285
  const extensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.js"];
5212
5286
  const resolved = workspace.resolve(fromDir, cleaned);
@@ -5258,7 +5332,7 @@ var pythonModule = {
5258
5332
  resolveImport(rawPath, fromFile, workspace) {
5259
5333
  const cleaned = rawPath.replace(/['"]/g, "");
5260
5334
  const parts = cleaned.split(".");
5261
- const fromDir = path31.dirname(fromFile);
5335
+ const fromDir = path32.dirname(fromFile);
5262
5336
  const relPath = parts.join("/");
5263
5337
  for (const suffix of ["/__init__.py", ".py"]) {
5264
5338
  const r = workspace.resolve(fromDir, relPath + suffix);
@@ -5337,7 +5411,7 @@ var cModule = {
5337
5411
  inheritanceStrategy: "none",
5338
5412
  resolveImport(rawPath, fromFile, workspace) {
5339
5413
  const cleaned = rawPath.replace(/[<>"']/g, "");
5340
- const fromDir = path31.dirname(fromFile);
5414
+ const fromDir = path32.dirname(fromFile);
5341
5415
  return workspace.resolve(fromDir, cleaned);
5342
5416
  },
5343
5417
  isExported(_node) {
@@ -5360,7 +5434,7 @@ var cppModule = {
5360
5434
  inheritanceStrategy: "depth-first",
5361
5435
  resolveImport(rawPath, fromFile, workspace) {
5362
5436
  const cleaned = rawPath.replace(/[<>"']/g, "");
5363
- const fromDir = path31.dirname(fromFile);
5437
+ const fromDir = path32.dirname(fromFile);
5364
5438
  return workspace.resolve(fromDir, cleaned);
5365
5439
  },
5366
5440
  isExported(_node) {
@@ -5522,7 +5596,7 @@ var dartModule = {
5522
5596
  const pkg = cleaned.replace("package:", "");
5523
5597
  return workspace.findByPackage(pkg);
5524
5598
  }
5525
- const fromDir = path31.dirname(fromFile);
5599
+ const fromDir = path32.dirname(fromFile);
5526
5600
  return workspace.resolve(fromDir, cleaned);
5527
5601
  },
5528
5602
  isExported(node) {
@@ -5877,25 +5951,25 @@ function validateDAG(phases) {
5877
5951
  const visiting = /* @__PURE__ */ new Set();
5878
5952
  const visited = /* @__PURE__ */ new Set();
5879
5953
  const phaseMap = new Map(phases.map((p) => [p.name, p]));
5880
- function dfs(name, path32) {
5954
+ function dfs(name, path33) {
5881
5955
  if (visiting.has(name)) {
5882
- const cycleStart = path32.indexOf(name);
5883
- const cycle = path32.slice(cycleStart).concat(name);
5956
+ const cycleStart = path33.indexOf(name);
5957
+ const cycle = path33.slice(cycleStart).concat(name);
5884
5958
  errors.push({ type: "cycle", message: `Cycle detected: ${cycle.join(" \u2192 ")}` });
5885
5959
  return true;
5886
5960
  }
5887
5961
  if (visited.has(name)) return false;
5888
5962
  visiting.add(name);
5889
- path32.push(name);
5963
+ path33.push(name);
5890
5964
  const phase = phaseMap.get(name);
5891
5965
  if (phase) {
5892
5966
  for (const dep of phase.dependencies) {
5893
- if (dfs(dep, path32)) return true;
5967
+ if (dfs(dep, path33)) return true;
5894
5968
  }
5895
5969
  }
5896
5970
  visiting.delete(name);
5897
5971
  visited.add(name);
5898
- path32.pop();
5972
+ path33.pop();
5899
5973
  return false;
5900
5974
  }
5901
5975
  for (const phase of phases) {
@@ -6023,6 +6097,7 @@ ${errors.map((e) => e.message).join("\n")}`);
6023
6097
  for (const phase of sorted) {
6024
6098
  context2.onProgress?.(phase.name, "running");
6025
6099
  const phaseStart = Date.now();
6100
+ const memBefore = context2.profile ? Math.round(process.memoryUsage().heapUsed / 1024 / 1024) : void 0;
6026
6101
  const runPhase = async () => {
6027
6102
  const depResults = /* @__PURE__ */ new Map();
6028
6103
  for (const dep of phase.dependencies) {
@@ -6046,6 +6121,10 @@ ${errors.map((e) => e.message).join("\n")}`);
6046
6121
  }
6047
6122
  const durationSec = (Date.now() - phaseStart) / 1e3;
6048
6123
  pipelinePhaseDurationSeconds.observe({ phase: phase.name, status: result.status }, durationSec);
6124
+ if (memBefore !== void 0) {
6125
+ result.memoryBeforeMB = memBefore;
6126
+ result.memoryAfterMB = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
6127
+ }
6049
6128
  results.set(phase.name, result);
6050
6129
  context2.onProgress?.(phase.name, result.status);
6051
6130
  if (result.status === "failed") {
@@ -6056,7 +6135,9 @@ ${errors.map((e) => e.message).join("\n")}`);
6056
6135
  const result = {
6057
6136
  status: "failed",
6058
6137
  duration: Date.now() - phaseStart,
6059
- message: err instanceof Error ? err.message : String(err)
6138
+ message: err instanceof Error ? err.message : String(err),
6139
+ memoryBeforeMB: memBefore,
6140
+ memoryAfterMB: memBefore !== void 0 ? Math.round(process.memoryUsage().heapUsed / 1024 / 1024) : void 0
6060
6141
  };
6061
6142
  pipelinePhaseDurationSeconds.observe({ phase: phase.name, status: "failed" }, (Date.now() - phaseStart) / 1e3);
6062
6143
  results.set(phase.name, result);
@@ -6108,7 +6189,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
6108
6189
  ]);
6109
6190
  function loadIgnorePatterns(workspaceRoot) {
6110
6191
  try {
6111
- const raw = fs24.readFileSync(path31.join(workspaceRoot, ".codeintelignore"), "utf-8");
6192
+ const raw = fs25.readFileSync(path32.join(workspaceRoot, ".codeintelignore"), "utf-8");
6112
6193
  const extras = /* @__PURE__ */ new Set();
6113
6194
  for (const line of raw.split("\n")) {
6114
6195
  const trimmed = line.trim();
@@ -6132,7 +6213,7 @@ var scanPhase = {
6132
6213
  function walk(dir) {
6133
6214
  let entries;
6134
6215
  try {
6135
- entries = fs24.readdirSync(dir, { withFileTypes: true });
6216
+ entries = fs25.readdirSync(dir, { withFileTypes: true });
6136
6217
  } catch {
6137
6218
  return;
6138
6219
  }
@@ -6141,15 +6222,15 @@ var scanPhase = {
6141
6222
  if (entry.name.startsWith(".")) continue;
6142
6223
  if (IGNORED_DIRS.has(entry.name)) continue;
6143
6224
  if (extraIgnore.has(entry.name)) continue;
6144
- walk(path31.join(dir, entry.name));
6225
+ walk(path32.join(dir, entry.name));
6145
6226
  } else if (entry.isFile()) {
6146
6227
  const name = entry.name;
6147
6228
  if (IGNORED_FILE_SUFFIXES.some((s) => name.endsWith(s))) continue;
6148
- const ext = path31.extname(name);
6229
+ const ext = path32.extname(name);
6149
6230
  if (!extensions.has(ext)) continue;
6150
- const fullPath = path31.join(dir, name);
6231
+ const fullPath = path32.join(dir, name);
6151
6232
  try {
6152
- const stat = fs24.statSync(fullPath);
6233
+ const stat = fs25.statSync(fullPath);
6153
6234
  if (stat.size > MAX_FILE_SIZE_BYTES) continue;
6154
6235
  } catch {
6155
6236
  continue;
@@ -6176,20 +6257,20 @@ var structurePhase = {
6176
6257
  const dirs = /* @__PURE__ */ new Set();
6177
6258
  let structDone = 0;
6178
6259
  for (const filePath of context2.filePaths) {
6179
- const relativePath = path31.relative(context2.workspaceRoot, filePath);
6260
+ const relativePath = path32.relative(context2.workspaceRoot, filePath);
6180
6261
  const lang = detectLanguage(filePath);
6181
6262
  context2.graph.addNode({
6182
6263
  id: generateNodeId("file", relativePath, relativePath),
6183
6264
  kind: "file",
6184
- name: path31.basename(filePath),
6265
+ name: path32.basename(filePath),
6185
6266
  filePath: relativePath,
6186
6267
  metadata: lang ? { language: lang } : void 0
6187
6268
  });
6188
- let dir = path31.dirname(relativePath);
6269
+ let dir = path32.dirname(relativePath);
6189
6270
  while (dir && dir !== "." && dir !== "") {
6190
6271
  if (dirs.has(dir)) break;
6191
6272
  dirs.add(dir);
6192
- dir = path31.dirname(dir);
6273
+ dir = path32.dirname(dir);
6193
6274
  }
6194
6275
  structDone++;
6195
6276
  context2.onPhaseProgress?.("structure", structDone, context2.filePaths.length);
@@ -6198,7 +6279,7 @@ var structurePhase = {
6198
6279
  context2.graph.addNode({
6199
6280
  id: generateNodeId("directory", dir, dir),
6200
6281
  kind: "directory",
6201
- name: path31.basename(dir),
6282
+ name: path32.basename(dir),
6202
6283
  filePath: dir
6203
6284
  });
6204
6285
  }
@@ -6309,22 +6390,22 @@ var flowPhase = {
6309
6390
  const queue = [{ nodeId: ep.id, path: [ep.id] }];
6310
6391
  const visited = /* @__PURE__ */ new Set();
6311
6392
  while (queue.length > 0 && flowCount < maxFlows) {
6312
- const { nodeId, path: path32 } = queue.shift();
6313
- if (path32.length > maxDepth) continue;
6393
+ const { nodeId, path: path33 } = queue.shift();
6394
+ if (path33.length > maxDepth) continue;
6314
6395
  const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
6315
- if (callEdges.length === 0 && path32.length >= 3) {
6396
+ if (callEdges.length === 0 && path33.length >= 3) {
6316
6397
  const flowId = generateNodeId("flow", ep.filePath, `flow-${flowCount}`);
6317
6398
  graph.addNode({
6318
6399
  id: flowId,
6319
6400
  kind: "flow",
6320
6401
  name: `${ep.name} flow ${flowCount}`,
6321
6402
  filePath: ep.filePath,
6322
- metadata: { steps: path32, entryPoint: ep.name }
6403
+ metadata: { steps: path33, entryPoint: ep.name }
6323
6404
  });
6324
- for (let i = 0; i < path32.length; i++) {
6405
+ for (let i = 0; i < path33.length; i++) {
6325
6406
  graph.addEdge({
6326
- id: generateEdgeId(path32[i], flowId, `step_of_${i}`),
6327
- source: path32[i],
6407
+ id: generateEdgeId(path33[i], flowId, `step_of_${i}`),
6408
+ source: path33[i],
6328
6409
  target: flowId,
6329
6410
  kind: "step_of",
6330
6411
  weight: 1,
@@ -6337,7 +6418,7 @@ var flowPhase = {
6337
6418
  for (const edge of callEdges) {
6338
6419
  if (visited.has(edge.target)) continue;
6339
6420
  visited.add(edge.target);
6340
- queue.push({ nodeId: edge.target, path: [...path32, edge.target] });
6421
+ queue.push({ nodeId: edge.target, path: [...path33, edge.target] });
6341
6422
  }
6342
6423
  }
6343
6424
  }
@@ -6355,7 +6436,7 @@ var LLMGovernanceLogger = class {
6355
6436
  }
6356
6437
  /** Path to the JSONL log file. */
6357
6438
  getLogPath() {
6358
- return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path31.join(os12.homedir(), ".code-intel", "llm-governance.jsonl");
6439
+ return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path32.join(os13.homedir(), ".code-intel", "llm-governance.jsonl");
6359
6440
  }
6360
6441
  /**
6361
6442
  * Append an entry to the governance log.
@@ -6371,8 +6452,8 @@ var LLMGovernanceLogger = class {
6371
6452
  ...entry
6372
6453
  };
6373
6454
  const logPath = this.getLogPath();
6374
- fs24.mkdirSync(path31.dirname(logPath), { recursive: true });
6375
- fs24.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
6455
+ fs25.mkdirSync(path32.dirname(logPath), { recursive: true });
6456
+ fs25.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
6376
6457
  } catch {
6377
6458
  }
6378
6459
  }
@@ -6382,7 +6463,7 @@ var LLMGovernanceLogger = class {
6382
6463
  */
6383
6464
  readLog(limit = 100) {
6384
6465
  try {
6385
- const raw = fs24.readFileSync(this.getLogPath(), "utf-8");
6466
+ const raw = fs25.readFileSync(this.getLogPath(), "utf-8");
6386
6467
  const lines = raw.split("\n").filter((l) => l.trim().length > 0).slice(-limit);
6387
6468
  return lines.map((l) => JSON.parse(l));
6388
6469
  } catch {
@@ -6392,6 +6473,9 @@ var LLMGovernanceLogger = class {
6392
6473
  };
6393
6474
  var governanceLogger = new LLMGovernanceLogger();
6394
6475
 
6476
+ // src/pipeline/phases/summarize-phase.ts
6477
+ init_logger();
6478
+
6395
6479
  // src/pipeline/workers/parse-phase-parallel.ts
6396
6480
  init_shared();
6397
6481
  init_id_generator();
@@ -6526,17 +6610,17 @@ function traceFlow(entryId, graph, maxDepth = 10, maxBranching = 4) {
6526
6610
  const queue = [{ nodeId: entryId, path: [entryId] }];
6527
6611
  const visited = /* @__PURE__ */ new Set();
6528
6612
  while (queue.length > 0 && flows.length < maxFlows) {
6529
- const { nodeId, path: path32 } = queue.shift();
6530
- if (path32.length > maxDepth) continue;
6613
+ const { nodeId, path: path33 } = queue.shift();
6614
+ if (path33.length > maxDepth) continue;
6531
6615
  const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
6532
- if (callEdges.length === 0 && path32.length >= 3) {
6533
- flows.push({ entryPointId: entryId, steps: [...path32] });
6616
+ if (callEdges.length === 0 && path33.length >= 3) {
6617
+ flows.push({ entryPointId: entryId, steps: [...path33] });
6534
6618
  continue;
6535
6619
  }
6536
6620
  for (const edge of callEdges) {
6537
6621
  if (visited.has(edge.target)) continue;
6538
6622
  visited.add(edge.target);
6539
- queue.push({ nodeId: edge.target, path: [...path32, edge.target] });
6623
+ queue.push({ nodeId: edge.target, path: [...path33, edge.target] });
6540
6624
  }
6541
6625
  }
6542
6626
  }
@@ -6637,7 +6721,7 @@ var VectorIndex = class {
6637
6721
  this.sqlitePath = sqlitePath;
6638
6722
  }
6639
6723
  async init() {
6640
- this.db = new Database3(this.sqlitePath);
6724
+ this.db = new Database2(this.sqlitePath);
6641
6725
  this.db.pragma("journal_mode = WAL");
6642
6726
  this.db.exec(`
6643
6727
  CREATE TABLE IF NOT EXISTS ${EMBED_TABLE} (
@@ -6768,9 +6852,9 @@ function siftDown(arr, i, score) {
6768
6852
  }
6769
6853
  init_embedder();
6770
6854
  async function hybridSearch(graph, query, limit, options = {}) {
6771
- const { vectorDbPath, bm25Limit = 50, vectorLimit = 50 } = options;
6772
- const bm25Promise = Promise.resolve(textSearch(graph, query, bm25Limit));
6773
- const hasVectorDb = Boolean(vectorDbPath && fs24.existsSync(vectorDbPath));
6855
+ const { vectorDbPath, bm25Limit = 50, vectorLimit = 50, bm25Results: precomputedBm25 } = options;
6856
+ const bm25Promise = precomputedBm25 ? Promise.resolve(precomputedBm25) : Promise.resolve(textSearch(graph, query, bm25Limit));
6857
+ const hasVectorDb = Boolean(vectorDbPath && fs25.existsSync(vectorDbPath));
6774
6858
  if (!hasVectorDb) {
6775
6859
  const bm25Results2 = await bm25Promise;
6776
6860
  return {
@@ -6819,16 +6903,282 @@ async function runVectorSearch(vectorDbPath, query, topK) {
6819
6903
  return null;
6820
6904
  }
6821
6905
  }
6906
+
6907
+ // src/search/bm25-index.ts
6908
+ init_logger();
6909
+ var K1 = 1.2;
6910
+ var B = 0.75;
6911
+ function tokenize(text) {
6912
+ return text.toLowerCase().split(/[\s\-_./\\:(){}[\]<>,"'`~!@#$%^&*+=|;?]+/).filter((t) => t.length >= 2 && t.length <= 64);
6913
+ }
6914
+ function nodeToDoc(node) {
6915
+ return [
6916
+ node.name,
6917
+ node.kind,
6918
+ node.filePath,
6919
+ (node.content ?? "").slice(0, 1e3)
6920
+ ].join(" ");
6921
+ }
6922
+ var Bm25Index = class {
6923
+ constructor(dbPath) {
6924
+ this.dbPath = dbPath;
6925
+ }
6926
+ dbPath;
6927
+ /** In-memory inverted index (populated after `load()`). */
6928
+ invertedIndex = /* @__PURE__ */ new Map();
6929
+ docLengths = /* @__PURE__ */ new Map();
6930
+ nodeMeta = /* @__PURE__ */ new Map();
6931
+ avgdl = 1;
6932
+ docCount = 0;
6933
+ _loaded = false;
6934
+ get isLoaded() {
6935
+ return this._loaded;
6936
+ }
6937
+ // ── Build ───────────────────────────────────────────────────────────────────
6938
+ /**
6939
+ * Build the inverted index from a KnowledgeGraph and persist to SQLite.
6940
+ * Called once at analysis time after the main pipeline completes.
6941
+ */
6942
+ build(graph) {
6943
+ const nodeTermFreqs = /* @__PURE__ */ new Map();
6944
+ const docLengths = /* @__PURE__ */ new Map();
6945
+ const nodeMeta = /* @__PURE__ */ new Map();
6946
+ for (const node of graph.allNodes()) {
6947
+ if (["directory", "cluster", "flow"].includes(node.kind)) continue;
6948
+ const terms = tokenize(nodeToDoc(node));
6949
+ const tf = /* @__PURE__ */ new Map();
6950
+ for (const t of terms) tf.set(t, (tf.get(t) ?? 0) + 1);
6951
+ nodeTermFreqs.set(node.id, tf);
6952
+ docLengths.set(node.id, terms.length);
6953
+ nodeMeta.set(node.id, {
6954
+ name: node.name,
6955
+ kind: node.kind,
6956
+ filePath: node.filePath,
6957
+ snippet: node.content?.slice(0, 200)
6958
+ });
6959
+ }
6960
+ const docCount = nodeTermFreqs.size;
6961
+ const totalLen = [...docLengths.values()].reduce((a, b) => a + b, 0);
6962
+ const avgdl = docCount > 0 ? totalLen / docCount : 1;
6963
+ const invertedIndex = /* @__PURE__ */ new Map();
6964
+ for (const [nodeId, tf] of nodeTermFreqs) {
6965
+ for (const [term, count] of tf) {
6966
+ let postings = invertedIndex.get(term);
6967
+ if (!postings) {
6968
+ postings = [];
6969
+ invertedIndex.set(term, postings);
6970
+ }
6971
+ postings.push({ nodeId, tf: count });
6972
+ }
6973
+ }
6974
+ fs25.mkdirSync(path32.dirname(this.dbPath), { recursive: true });
6975
+ for (const f of [this.dbPath, `${this.dbPath}-shm`, `${this.dbPath}-wal`]) {
6976
+ try {
6977
+ if (fs25.existsSync(f)) fs25.unlinkSync(f);
6978
+ } catch {
6979
+ }
6980
+ }
6981
+ const db = new Database2(this.dbPath);
6982
+ db.pragma("journal_mode = WAL");
6983
+ db.exec(`
6984
+ CREATE TABLE bm25_index (term TEXT PRIMARY KEY, postings TEXT NOT NULL);
6985
+ CREATE TABLE bm25_doclen (node_id TEXT PRIMARY KEY, doclen INTEGER NOT NULL);
6986
+ CREATE TABLE bm25_nodemeta(node_id TEXT PRIMARY KEY, name TEXT, kind TEXT, file_path TEXT, snippet TEXT);
6987
+ CREATE TABLE bm25_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
6988
+ `);
6989
+ const insPosting = db.prepare("INSERT OR REPLACE INTO bm25_index VALUES (?, ?)");
6990
+ const insDoclen = db.prepare("INSERT OR REPLACE INTO bm25_doclen VALUES (?, ?)");
6991
+ const insNodeMeta = db.prepare("INSERT OR REPLACE INTO bm25_nodemeta VALUES (?, ?, ?, ?, ?)");
6992
+ const insMeta = db.prepare("INSERT OR REPLACE INTO bm25_meta VALUES (?, ?)");
6993
+ db.transaction(() => {
6994
+ for (const [term, postings] of invertedIndex) {
6995
+ insPosting.run(term, JSON.stringify(postings));
6996
+ }
6997
+ for (const [nodeId, len] of docLengths) {
6998
+ insDoclen.run(nodeId, len);
6999
+ }
7000
+ for (const [nodeId, meta] of nodeMeta) {
7001
+ insNodeMeta.run(nodeId, meta.name, meta.kind, meta.filePath, meta.snippet ?? null);
7002
+ }
7003
+ insMeta.run("avgdl", String(avgdl));
7004
+ insMeta.run("docCount", String(docCount));
7005
+ })();
7006
+ db.close();
7007
+ logger_default.info(` [bm25] Index built: ${invertedIndex.size} terms, ${docCount} documents`);
7008
+ }
7009
+ // ── Load into memory ────────────────────────────────────────────────────────
7010
+ /**
7011
+ * Load the full inverted index into memory.
7012
+ * Called once on `serve` startup.
7013
+ */
7014
+ load() {
7015
+ if (!fs25.existsSync(this.dbPath)) return;
7016
+ const db = new Database2(this.dbPath, { readonly: true });
7017
+ try {
7018
+ const getMeta = db.prepare("SELECT value FROM bm25_meta WHERE key = ?");
7019
+ this.avgdl = parseFloat(getMeta.get("avgdl")?.value ?? "1");
7020
+ this.docCount = parseInt(getMeta.get("docCount")?.value ?? "0", 10);
7021
+ this.invertedIndex.clear();
7022
+ const postingRows = db.prepare("SELECT term, postings FROM bm25_index").all();
7023
+ for (const row of postingRows) {
7024
+ this.invertedIndex.set(row.term, JSON.parse(row.postings));
7025
+ }
7026
+ this.docLengths.clear();
7027
+ const dlRows = db.prepare("SELECT node_id, doclen FROM bm25_doclen").all();
7028
+ for (const row of dlRows) {
7029
+ this.docLengths.set(row.node_id, row.doclen);
7030
+ }
7031
+ this.nodeMeta.clear();
7032
+ const metaRows = db.prepare("SELECT node_id, name, kind, file_path, snippet FROM bm25_nodemeta").all();
7033
+ for (const row of metaRows) {
7034
+ this.nodeMeta.set(row.node_id, {
7035
+ name: row.name,
7036
+ kind: row.kind,
7037
+ filePath: row.file_path,
7038
+ snippet: row.snippet ?? void 0
7039
+ });
7040
+ }
7041
+ this._loaded = true;
7042
+ logger_default.info(` [bm25] Index loaded (${this.invertedIndex.size} terms)`);
7043
+ } finally {
7044
+ db.close();
7045
+ }
7046
+ }
7047
+ // ── Search ──────────────────────────────────────────────────────────────────
7048
+ /**
7049
+ * BM25 search. LIMIT pushdown: scores only the posting lists for query terms,
7050
+ * then partial-sorts to return only the top `limit` results.
7051
+ */
7052
+ search(query, limit) {
7053
+ if (!this._loaded || this.invertedIndex.size === 0) return [];
7054
+ const queryTerms = [...new Set(tokenize(query))];
7055
+ if (queryTerms.length === 0) return [];
7056
+ const scores = /* @__PURE__ */ new Map();
7057
+ const N = this.docCount;
7058
+ const avgdl = this.avgdl;
7059
+ for (const term of queryTerms) {
7060
+ const postings = this.invertedIndex.get(term);
7061
+ if (!postings) continue;
7062
+ const df = postings.length;
7063
+ const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
7064
+ for (const { nodeId, tf } of postings) {
7065
+ const dl = this.docLengths.get(nodeId) ?? avgdl;
7066
+ const score = idf * (tf * (K1 + 1)) / (tf + K1 * (1 - B + B * (dl / avgdl)));
7067
+ scores.set(nodeId, (scores.get(nodeId) ?? 0) + score);
7068
+ }
7069
+ }
7070
+ const entries = [...scores.entries()];
7071
+ entries.sort((a, b) => b[1] - a[1]);
7072
+ const topK = entries.slice(0, limit);
7073
+ return topK.map(([nodeId, score]) => {
7074
+ const meta = this.nodeMeta.get(nodeId);
7075
+ return {
7076
+ nodeId,
7077
+ name: meta?.name ?? nodeId,
7078
+ kind: meta?.kind ?? "unknown",
7079
+ filePath: meta?.filePath ?? "",
7080
+ score,
7081
+ snippet: meta?.snippet
7082
+ };
7083
+ });
7084
+ }
7085
+ // ── Incremental update ──────────────────────────────────────────────────────
7086
+ /**
7087
+ * Incrementally update index for a set of changed/added nodes.
7088
+ * Only terms that overlap with the changed nodes are rewritten.
7089
+ * Works even if `load()` was not called (reads affected terms directly from DB).
7090
+ */
7091
+ updateNodes(nodes) {
7092
+ if (!fs25.existsSync(this.dbPath)) return;
7093
+ if (nodes.length === 0) return;
7094
+ const changedIds = new Set(nodes.map((n) => n.id));
7095
+ const newTermFreqs = /* @__PURE__ */ new Map();
7096
+ for (const node of nodes) {
7097
+ if (["directory", "cluster", "flow"].includes(node.kind)) continue;
7098
+ const terms = tokenize(nodeToDoc(node));
7099
+ const tf = /* @__PURE__ */ new Map();
7100
+ for (const t of terms) tf.set(t, (tf.get(t) ?? 0) + 1);
7101
+ newTermFreqs.set(node.id, tf);
7102
+ }
7103
+ const newTermSet = new Set([...newTermFreqs.values()].flatMap((m) => [...m.keys()]));
7104
+ const db = new Database2(this.dbPath);
7105
+ db.pragma("journal_mode = WAL");
7106
+ const termsToRewrite = /* @__PURE__ */ new Map();
7107
+ for (const term of newTermSet) {
7108
+ const row = db.prepare("SELECT postings FROM bm25_index WHERE term = ?").get(term);
7109
+ const existing = row ? JSON.parse(row.postings) : [];
7110
+ termsToRewrite.set(term, existing.filter((p) => !changedIds.has(p.nodeId)));
7111
+ }
7112
+ for (const nodeId of changedIds) {
7113
+ const rows = db.prepare("SELECT term, postings FROM bm25_index WHERE postings LIKE ?").all(`%${nodeId}%`);
7114
+ for (const row of rows) {
7115
+ if (termsToRewrite.has(row.term)) continue;
7116
+ const postings = JSON.parse(row.postings);
7117
+ if (postings.some((p) => changedIds.has(p.nodeId))) {
7118
+ termsToRewrite.set(row.term, postings.filter((p) => !changedIds.has(p.nodeId)));
7119
+ }
7120
+ }
7121
+ }
7122
+ for (const [nodeId, tf] of newTermFreqs) {
7123
+ for (const [term, count] of tf) {
7124
+ const postings = termsToRewrite.get(term) ?? [];
7125
+ postings.push({ nodeId, tf: count });
7126
+ termsToRewrite.set(term, postings);
7127
+ }
7128
+ }
7129
+ const upsertPosting = db.prepare("INSERT OR REPLACE INTO bm25_index VALUES (?, ?)");
7130
+ const upsertDoclen = db.prepare("INSERT OR REPLACE INTO bm25_doclen VALUES (?, ?)");
7131
+ const upsertNodeMeta = db.prepare("INSERT OR REPLACE INTO bm25_nodemeta VALUES (?, ?, ?, ?, ?)");
7132
+ db.transaction(() => {
7133
+ for (const [term, postings] of termsToRewrite) {
7134
+ if (postings.length === 0) {
7135
+ db.prepare("DELETE FROM bm25_index WHERE term = ?").run(term);
7136
+ } else {
7137
+ upsertPosting.run(term, JSON.stringify(postings));
7138
+ }
7139
+ }
7140
+ for (const node of nodes) {
7141
+ const terms = tokenize(nodeToDoc(node));
7142
+ upsertDoclen.run(node.id, terms.length);
7143
+ upsertNodeMeta.run(node.id, node.name, node.kind, node.filePath, node.content?.slice(0, 200) ?? null);
7144
+ }
7145
+ })();
7146
+ db.close();
7147
+ if (this._loaded) {
7148
+ for (const [term, postings] of termsToRewrite) {
7149
+ if (postings.length === 0) this.invertedIndex.delete(term);
7150
+ else this.invertedIndex.set(term, postings);
7151
+ }
7152
+ for (const node of nodes) {
7153
+ const terms = tokenize(nodeToDoc(node));
7154
+ this.docLengths.set(node.id, terms.length);
7155
+ this.nodeMeta.set(node.id, {
7156
+ name: node.name,
7157
+ kind: node.kind,
7158
+ filePath: node.filePath,
7159
+ snippet: node.content?.slice(0, 200)
7160
+ });
7161
+ }
7162
+ }
7163
+ }
7164
+ };
7165
+ function getBm25DbPath(workspaceRoot) {
7166
+ return path32.join(workspaceRoot, ".code-intel", "bm25.db");
7167
+ }
6822
7168
  var DbManager = class {
6823
7169
  db = null;
6824
7170
  conn = null;
6825
7171
  dbPath;
6826
- constructor(dbPath) {
7172
+ readOnly;
7173
+ constructor(dbPath, readOnly = false) {
6827
7174
  this.dbPath = dbPath;
7175
+ this.readOnly = readOnly;
6828
7176
  }
6829
7177
  async init() {
6830
- fs24.mkdirSync(path31.dirname(this.dbPath), { recursive: true });
6831
- this.db = new Database(this.dbPath);
7178
+ if (!this.readOnly) {
7179
+ fs25.mkdirSync(path32.dirname(this.dbPath), { recursive: true });
7180
+ }
7181
+ this.db = new Database(this.dbPath, 0, true, this.readOnly);
6832
7182
  await this.db.init();
6833
7183
  this.conn = new Connection(this.db);
6834
7184
  await this.conn.init();
@@ -6863,61 +7213,8 @@ var DbManager = class {
6863
7213
  return this.conn !== null;
6864
7214
  }
6865
7215
  };
6866
-
6867
- // src/storage/schema.ts
6868
- var NODE_TABLE_MAP = {
6869
- file: "file_nodes",
6870
- directory: "dir_nodes",
6871
- function: "func_nodes",
6872
- class: "class_nodes",
6873
- interface: "iface_nodes",
6874
- method: "method_nodes",
6875
- constructor: "ctor_nodes",
6876
- variable: "var_nodes",
6877
- property: "prop_nodes",
6878
- struct: "struct_nodes",
6879
- enum: "enum_nodes",
6880
- trait: "trait_nodes",
6881
- namespace: "ns_nodes",
6882
- module: "mod_nodes",
6883
- type_alias: "type_nodes",
6884
- constant: "const_nodes",
6885
- route: "route_nodes",
6886
- cluster: "cluster_nodes",
6887
- flow: "flow_nodes",
6888
- vulnerability: "vuln_nodes"
6889
- };
6890
- var ALL_NODE_TABLES = [...new Set(Object.values(NODE_TABLE_MAP))];
6891
- function getCreateNodeTableDDL(tableName) {
6892
- return `CREATE NODE TABLE IF NOT EXISTS ${tableName} (
6893
- id STRING,
6894
- name STRING,
6895
- file_path STRING,
6896
- start_line INT64,
6897
- end_line INT64,
6898
- exported BOOLEAN,
6899
- content STRING,
6900
- metadata STRING,
6901
- PRIMARY KEY (id)
6902
- )`;
6903
- }
6904
- function getCreateEdgeTableDDL() {
6905
- const uniqueTables = ALL_NODE_TABLES;
6906
- const fromToPairs = [];
6907
- for (const from of uniqueTables) {
6908
- for (const to of uniqueTables) {
6909
- fromToPairs.push(`FROM ${from} TO ${to}`);
6910
- }
6911
- }
6912
- return [`CREATE REL TABLE GROUP IF NOT EXISTS code_edges (
6913
- ${fromToPairs.join(",\n ")},
6914
- kind STRING,
6915
- weight DOUBLE,
6916
- label STRING
6917
- )`];
6918
- }
6919
7216
  function writeNodeCSVs(graph, outputDir) {
6920
- fs24.mkdirSync(outputDir, { recursive: true });
7217
+ fs25.mkdirSync(outputDir, { recursive: true });
6921
7218
  const header = "id,name,file_path,start_line,end_line,exported,content,metadata\n";
6922
7219
  const tableBuffers = /* @__PURE__ */ new Map();
6923
7220
  const tableFilePaths = /* @__PURE__ */ new Map();
@@ -6925,7 +7222,7 @@ function writeNodeCSVs(graph, outputDir) {
6925
7222
  const table = NODE_TABLE_MAP[node.kind];
6926
7223
  if (!tableBuffers.has(table)) {
6927
7224
  tableBuffers.set(table, [header]);
6928
- tableFilePaths.set(table, path31.join(outputDir, `${table}.csv`));
7225
+ tableFilePaths.set(table, path32.join(outputDir, `${table}.csv`));
6929
7226
  }
6930
7227
  tableBuffers.get(table).push(
6931
7228
  csvRow([
@@ -6945,12 +7242,12 @@ function writeNodeCSVs(graph, outputDir) {
6945
7242
  );
6946
7243
  }
6947
7244
  for (const [table, lines] of tableBuffers) {
6948
- fs24.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
7245
+ fs25.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
6949
7246
  }
6950
7247
  return tableFilePaths;
6951
7248
  }
6952
7249
  function writeEdgeCSV(graph, outputDir) {
6953
- fs24.mkdirSync(outputDir, { recursive: true });
7250
+ fs25.mkdirSync(outputDir, { recursive: true });
6954
7251
  const header = "from_id,to_id,kind,weight,label\n";
6955
7252
  const groups = /* @__PURE__ */ new Map();
6956
7253
  for (const edge of graph.allEdges()) {
@@ -6961,7 +7258,7 @@ function writeEdgeCSV(graph, outputDir) {
6961
7258
  const toTable = NODE_TABLE_MAP[targetNode.kind];
6962
7259
  const key = `${fromTable}->${toTable}`;
6963
7260
  if (!groups.has(key)) {
6964
- const filePath = path31.join(outputDir, `edges_${fromTable}_${toTable}.csv`);
7261
+ const filePath = path32.join(outputDir, `edges_${fromTable}_${toTable}.csv`);
6965
7262
  groups.set(key, { lines: [header], from: fromTable, to: toTable, filePath });
6966
7263
  }
6967
7264
  groups.get(key).lines.push(
@@ -6976,7 +7273,7 @@ function writeEdgeCSV(graph, outputDir) {
6976
7273
  }
6977
7274
  const result = [];
6978
7275
  for (const group of groups.values()) {
6979
- fs24.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
7276
+ fs25.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
6980
7277
  result.push({ fromTable: group.from, toTable: group.to, filePath: group.filePath });
6981
7278
  }
6982
7279
  return result;
@@ -7004,7 +7301,7 @@ async function loadGraphToDB(graph, dbManager) {
7004
7301
  } catch {
7005
7302
  }
7006
7303
  }
7007
- const tmpDir = fs24.mkdtempSync(path31.join(os12.tmpdir(), "code-intel-csv-"));
7304
+ const tmpDir = fs25.mkdtempSync(path32.join(os13.tmpdir(), "code-intel-csv-"));
7008
7305
  try {
7009
7306
  const nodeTableFiles = writeNodeCSVs(graph, tmpDir);
7010
7307
  const edgeGroups = writeEdgeCSV(graph, tmpDir);
@@ -7023,8 +7320,8 @@ async function loadGraphToDB(graph, dbManager) {
7023
7320
  }
7024
7321
  let nodeCount = 0;
7025
7322
  for (const [table, csvPath] of nodeTableFiles) {
7026
- if (!fs24.existsSync(csvPath)) continue;
7027
- const stat = fs24.statSync(csvPath);
7323
+ if (!fs25.existsSync(csvPath)) continue;
7324
+ const stat = fs25.statSync(csvPath);
7028
7325
  if (stat.size < 50) continue;
7029
7326
  try {
7030
7327
  await dbManager.execute(
@@ -7037,8 +7334,8 @@ async function loadGraphToDB(graph, dbManager) {
7037
7334
  }
7038
7335
  let edgeCount = 0;
7039
7336
  for (const group of edgeGroups) {
7040
- if (!fs24.existsSync(group.filePath)) continue;
7041
- const stat = fs24.statSync(group.filePath);
7337
+ if (!fs25.existsSync(group.filePath)) continue;
7338
+ const stat = fs25.statSync(group.filePath);
7042
7339
  if (stat.size < 50) continue;
7043
7340
  try {
7044
7341
  await dbManager.execute(
@@ -7052,7 +7349,7 @@ async function loadGraphToDB(graph, dbManager) {
7052
7349
  return { nodeCount, edgeCount };
7053
7350
  } finally {
7054
7351
  try {
7055
- fs24.rmSync(tmpDir, { recursive: true, force: true });
7352
+ fs25.rmSync(tmpDir, { recursive: true, force: true });
7056
7353
  } catch {
7057
7354
  }
7058
7355
  }
@@ -7104,19 +7401,19 @@ function buildNodeProps(node) {
7104
7401
  function escCypher(s) {
7105
7402
  return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "");
7106
7403
  }
7107
- var GLOBAL_DIR = path31.join(os12.homedir(), ".code-intel");
7108
- var REPOS_FILE = path31.join(GLOBAL_DIR, "repos.json");
7404
+ var GLOBAL_DIR = path32.join(os13.homedir(), ".code-intel");
7405
+ var REPOS_FILE = path32.join(GLOBAL_DIR, "repos.json");
7109
7406
  function loadRegistry() {
7110
7407
  try {
7111
- const data = fs24.readFileSync(REPOS_FILE, "utf-8");
7408
+ const data = fs25.readFileSync(REPOS_FILE, "utf-8");
7112
7409
  return JSON.parse(data);
7113
7410
  } catch {
7114
7411
  return [];
7115
7412
  }
7116
7413
  }
7117
7414
  function saveRegistry(entries) {
7118
- fs24.mkdirSync(GLOBAL_DIR, { recursive: true });
7119
- fs24.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
7415
+ fs25.mkdirSync(GLOBAL_DIR, { recursive: true });
7416
+ fs25.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
7120
7417
  }
7121
7418
  function upsertRepo(entry) {
7122
7419
  const entries = loadRegistry();
@@ -7133,30 +7430,30 @@ function removeRepo(repoPath) {
7133
7430
  saveRegistry(entries);
7134
7431
  }
7135
7432
  function saveMetadata(repoDir, metadata) {
7136
- const metaDir = path31.join(repoDir, ".code-intel");
7137
- fs24.mkdirSync(metaDir, { recursive: true });
7138
- fs24.writeFileSync(path31.join(metaDir, "meta.json"), JSON.stringify(metadata, null, 2));
7433
+ const metaDir = path32.join(repoDir, ".code-intel");
7434
+ fs25.mkdirSync(metaDir, { recursive: true });
7435
+ fs25.writeFileSync(path32.join(metaDir, "meta.json"), JSON.stringify(metadata, null, 2));
7139
7436
  }
7140
7437
  function loadMetadata(repoDir) {
7141
7438
  try {
7142
- const data = fs24.readFileSync(path31.join(repoDir, ".code-intel", "meta.json"), "utf-8");
7439
+ const data = fs25.readFileSync(path32.join(repoDir, ".code-intel", "meta.json"), "utf-8");
7143
7440
  return JSON.parse(data);
7144
7441
  } catch {
7145
7442
  return null;
7146
7443
  }
7147
7444
  }
7148
7445
  function getDbPath(repoDir) {
7149
- return path31.join(repoDir, ".code-intel", "graph.db");
7446
+ return path32.join(repoDir, ".code-intel", "graph.db");
7150
7447
  }
7151
7448
  function getVectorDbPath(repoDir) {
7152
- return path31.join(repoDir, ".code-intel", "vector.db");
7449
+ return path32.join(repoDir, ".code-intel", "vector.db");
7153
7450
  }
7154
7451
 
7155
7452
  // src/mcp-server/server.ts
7156
7453
  init_group_registry();
7157
7454
 
7158
7455
  // src/multi-repo/graph-from-db.ts
7159
- var TABLE_TO_KIND = Object.fromEntries(
7456
+ var TABLE_TO_KIND2 = Object.fromEntries(
7160
7457
  Object.entries(NODE_TABLE_MAP).map(([kind, table]) => [table, kind])
7161
7458
  );
7162
7459
  function parseRow(row, kind) {
@@ -7180,7 +7477,7 @@ function parseRow(row, kind) {
7180
7477
  }
7181
7478
  async function loadGraphFromDB(graph, db) {
7182
7479
  for (const table of ALL_NODE_TABLES) {
7183
- const kind = TABLE_TO_KIND[table];
7480
+ const kind = TABLE_TO_KIND2[table];
7184
7481
  if (!kind) continue;
7185
7482
  let rows = [];
7186
7483
  try {
@@ -7233,12 +7530,12 @@ function scanForFiles(root, matcher, maxDepth = 2) {
7233
7530
  if (depth > maxDepth) return;
7234
7531
  let entries;
7235
7532
  try {
7236
- entries = fs24.readdirSync(dir, { withFileTypes: true });
7533
+ entries = fs25.readdirSync(dir, { withFileTypes: true });
7237
7534
  } catch {
7238
7535
  return;
7239
7536
  }
7240
7537
  for (const entry of entries) {
7241
- const full = path31.join(dir, entry.name);
7538
+ const full = path32.join(dir, entry.name);
7242
7539
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
7243
7540
  walk(full, depth + 1);
7244
7541
  } else if (entry.isFile() && matcher(entry.name)) {
@@ -7261,8 +7558,8 @@ var OPENAPI_FILENAMES = /* @__PURE__ */ new Set([
7261
7558
  ]);
7262
7559
  var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
7263
7560
  function tryParseFile(filePath) {
7264
- const ext = path31.extname(filePath).toLowerCase();
7265
- const content = fs24.readFileSync(filePath, "utf-8");
7561
+ const ext = path32.extname(filePath).toLowerCase();
7562
+ const content = fs25.readFileSync(filePath, "utf-8");
7266
7563
  if (ext === ".json") {
7267
7564
  try {
7268
7565
  return JSON.parse(content);
@@ -7316,7 +7613,7 @@ async function parseGraphQLContracts(repoRoot) {
7316
7613
  const files = scanForFiles(repoRoot, (name) => name.endsWith(".graphql") || name.endsWith(".gql"));
7317
7614
  const contracts = [];
7318
7615
  for (const filePath of files) {
7319
- const content = fs24.readFileSync(filePath, "utf-8");
7616
+ const content = fs25.readFileSync(filePath, "utf-8");
7320
7617
  const typeRegex = /type\s+(\w+)\s*\{([^}]+)\}/g;
7321
7618
  let match;
7322
7619
  while ((match = typeRegex.exec(content)) !== null) {
@@ -7347,7 +7644,7 @@ async function parseProtoContracts(repoRoot) {
7347
7644
  const files = scanForFiles(repoRoot, (name) => name.endsWith(".proto"));
7348
7645
  const contracts = [];
7349
7646
  for (const filePath of files) {
7350
- const content = fs24.readFileSync(filePath, "utf-8");
7647
+ const content = fs25.readFileSync(filePath, "utf-8");
7351
7648
  const serviceRegex = /service\s+(\w+)\s*\{([^}]+)\}/g;
7352
7649
  let serviceMatch;
7353
7650
  while ((serviceMatch = serviceRegex.exec(content)) !== null) {
@@ -7552,13 +7849,13 @@ async function syncGroup(group) {
7552
7849
  logger_default.warn(` \u26A0 Registry entry "${member.registryName}" not found \u2014 skipping ${member.groupPath}`);
7553
7850
  continue;
7554
7851
  }
7555
- const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
7556
- if (!fs24.existsSync(dbPath)) {
7852
+ const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
7853
+ if (!fs25.existsSync(dbPath)) {
7557
7854
  logger_default.warn(` \u26A0 No index at ${dbPath} \u2014 run \`code-intel analyze ${regEntry.path}\` first`);
7558
7855
  continue;
7559
7856
  }
7560
7857
  const graph = createKnowledgeGraph();
7561
- const db = new DbManager(dbPath);
7858
+ const db = new DbManager(dbPath, true);
7562
7859
  try {
7563
7860
  await db.init();
7564
7861
  await loadGraphFromDB(graph, db);
@@ -7626,10 +7923,10 @@ async function queryGroup(group, query, limit = 20) {
7626
7923
  for (const member of group.members) {
7627
7924
  const regEntry = registry.find((r) => r.name === member.registryName);
7628
7925
  if (!regEntry) continue;
7629
- const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
7630
- if (!fs24.existsSync(dbPath)) continue;
7926
+ const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
7927
+ if (!fs25.existsSync(dbPath)) continue;
7631
7928
  const graph = createKnowledgeGraph();
7632
- const db = new DbManager(dbPath);
7929
+ const db = new DbManager(dbPath, true);
7633
7930
  try {
7634
7931
  await db.init();
7635
7932
  await loadGraphFromDB(graph, db);
@@ -8127,22 +8424,22 @@ function suggestTests(graph, symbolName) {
8127
8424
  const callPaths = [];
8128
8425
  const pathQueue = [{ id: targetId, path: [symbolName], depth: 0 }];
8129
8426
  while (pathQueue.length > 0 && callPaths.length < 5) {
8130
- const { id, path: path32, depth } = pathQueue.shift();
8427
+ const { id, path: path33, depth } = pathQueue.shift();
8131
8428
  let hasCallers2 = false;
8132
8429
  for (const edge of graph.findEdgesTo(id)) {
8133
8430
  if (edge.kind !== "calls") continue;
8134
8431
  const callerNode = graph.getNode(edge.source);
8135
8432
  if (!callerNode) continue;
8136
8433
  hasCallers2 = true;
8137
- const newPath = [callerNode.name, ...path32];
8434
+ const newPath = [callerNode.name, ...path33];
8138
8435
  if (depth + 1 >= 3 || callPaths.length >= 5) {
8139
8436
  if (callPaths.length < 5) callPaths.push(newPath);
8140
8437
  continue;
8141
8438
  }
8142
8439
  pathQueue.push({ id: edge.source, path: newPath, depth: depth + 1 });
8143
8440
  }
8144
- if (!hasCallers2 && path32.length > 1) {
8145
- callPaths.push(path32);
8441
+ if (!hasCallers2 && path33.length > 1) {
8442
+ callPaths.push(path33);
8146
8443
  }
8147
8444
  }
8148
8445
  if (callPaths.length === 0) {
@@ -8699,24 +8996,44 @@ function createMcpServer(graph, repoName, workspaceRoot) {
8699
8996
  }
8700
8997
  const startMs = Date.now();
8701
8998
  const dispatch = () => dispatchTool(name, a, graph, repoName, workspaceRoot);
8999
+ const MCP_TIMEOUT_MS = parseInt(process.env["CODE_INTEL_MCP_TIMEOUT_MS"] ?? "30000", 10);
9000
+ let timeoutHandle = null;
9001
+ let timedOut = false;
9002
+ const timeoutPromise = new Promise((_, reject) => {
9003
+ timeoutHandle = setTimeout(() => {
9004
+ timedOut = true;
9005
+ reject(new Error(`MCP tool '${name}' timed out after ${MCP_TIMEOUT_MS}ms`));
9006
+ }, MCP_TIMEOUT_MS);
9007
+ });
8702
9008
  let result;
8703
9009
  let status = "success";
8704
9010
  try {
8705
9011
  if (isTracingEnabled()) {
8706
- result = await withSpan(
8707
- `mcp.tool.${name}`,
8708
- sanitizeAttrs({ "mcp.tool": name, "mcp.repo": repoName }),
8709
- dispatch
8710
- );
9012
+ result = await Promise.race([
9013
+ withSpan(
9014
+ `mcp.tool.${name}`,
9015
+ sanitizeAttrs({ "mcp.tool": name, "mcp.repo": repoName }),
9016
+ dispatch
9017
+ ),
9018
+ timeoutPromise
9019
+ ]);
8711
9020
  } else {
8712
- result = await dispatch();
9021
+ result = await Promise.race([dispatch(), timeoutPromise]);
8713
9022
  }
8714
9023
  if (result.isError) status = "error";
8715
9024
  } catch (err) {
8716
9025
  status = "error";
8717
9026
  mcpToolCallsTotal.inc({ tool: name, status });
8718
9027
  mcpToolDurationSeconds.observe({ tool: name }, (Date.now() - startMs) / 1e3);
9028
+ if (timedOut) {
9029
+ return {
9030
+ content: [{ type: "text", text: JSON.stringify({ truncated: true, reason: `Tool '${name}' timed out after ${MCP_TIMEOUT_MS}ms`, partialResults: [] }) }],
9031
+ isError: false
9032
+ };
9033
+ }
8719
9034
  throw err;
9035
+ } finally {
9036
+ if (timeoutHandle) clearTimeout(timeoutHandle);
8720
9037
  }
8721
9038
  mcpToolCallsTotal.inc({ tool: name, status });
8722
9039
  mcpToolDurationSeconds.observe({ tool: name }, (Date.now() - startMs) / 1e3);
@@ -9104,7 +9421,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9104
9421
  for (const { filePath: changedFile, changedLines } of changedFiles) {
9105
9422
  for (const node of graph.allNodes()) {
9106
9423
  if (!node.filePath) continue;
9107
- const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot + path31.sep, "");
9424
+ const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot + path32.sep, "");
9108
9425
  const normChanged = changedFile.replace(/^a\/|^b\//, "");
9109
9426
  if (!normNode.endsWith(normChanged) && !normChanged.endsWith(normNode)) continue;
9110
9427
  if (node.startLine !== void 0 && node.endLine !== void 0) {
@@ -9527,8 +9844,8 @@ var STUCK_THRESHOLD_MINUTES = 30;
9527
9844
  var JobsDB = class {
9528
9845
  db;
9529
9846
  constructor(dbPath) {
9530
- fs24.mkdirSync(path31.dirname(dbPath), { recursive: true });
9531
- this.db = new Database3(dbPath);
9847
+ fs25.mkdirSync(path32.dirname(dbPath), { recursive: true });
9848
+ this.db = new Database2(dbPath);
9532
9849
  this.db.pragma("journal_mode = WAL");
9533
9850
  this.db.pragma("foreign_keys = ON");
9534
9851
  this.createTables();
@@ -9669,7 +9986,7 @@ var JobsDB = class {
9669
9986
  }
9670
9987
  };
9671
9988
  function getJobsDBPath() {
9672
- return path31.join(os12.homedir(), ".code-intel", "jobs.db");
9989
+ return path32.join(os13.homedir(), ".code-intel", "jobs.db");
9673
9990
  }
9674
9991
  var _jobsDB = null;
9675
9992
  function getOrCreateJobsDB() {
@@ -9761,14 +10078,14 @@ var BACKUP_VERSION = "1.0";
9761
10078
  var ALGORITHM = "aes-256-gcm";
9762
10079
  var IV_LENGTH = 16;
9763
10080
  function getBackupDir() {
9764
- return path31.join(os12.homedir(), ".code-intel", "backups");
10081
+ return path32.join(os13.homedir(), ".code-intel", "backups");
9765
10082
  }
9766
10083
  function getBackupKey() {
9767
10084
  const keyHex = process.env["CODE_INTEL_BACKUP_KEY"];
9768
10085
  if (keyHex && keyHex.length >= 64) {
9769
10086
  return Buffer.from(keyHex.slice(0, 64), "hex");
9770
10087
  }
9771
- const seed = `code-intel-backup-${os12.hostname()}-${os12.homedir()}`;
10088
+ const seed = `code-intel-backup-${os13.hostname()}-${os13.homedir()}`;
9772
10089
  return crypto5.createHash("sha256").update(seed).digest();
9773
10090
  }
9774
10091
  function encryptBuffer(data, key) {
@@ -9792,30 +10109,30 @@ var BackupService = class {
9792
10109
  constructor(backupDir) {
9793
10110
  this.backupDir = backupDir ?? getBackupDir();
9794
10111
  this.key = getBackupKey();
9795
- fs24.mkdirSync(this.backupDir, { recursive: true });
10112
+ fs25.mkdirSync(this.backupDir, { recursive: true });
9796
10113
  }
9797
10114
  /**
9798
10115
  * Create a backup for a repository.
9799
10116
  * Returns the backup entry.
9800
10117
  */
9801
10118
  createBackup(repoPath) {
9802
- const codeIntelDir = path31.join(repoPath, ".code-intel");
10119
+ const codeIntelDir = path32.join(repoPath, ".code-intel");
9803
10120
  const id = v4();
9804
10121
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
9805
10122
  const filesToBackup = [];
9806
10123
  const candidates = ["graph.db", "vector.db", "meta.json"];
9807
10124
  for (const f of candidates) {
9808
- const fp = path31.join(codeIntelDir, f);
9809
- if (fs24.existsSync(fp)) {
10125
+ const fp = path32.join(codeIntelDir, f);
10126
+ if (fs25.existsSync(fp)) {
9810
10127
  filesToBackup.push({ name: f, localPath: fp });
9811
10128
  }
9812
10129
  }
9813
- const registryPath = path31.join(os12.homedir(), ".code-intel", "registry.json");
9814
- if (fs24.existsSync(registryPath)) {
10130
+ const registryPath = path32.join(os13.homedir(), ".code-intel", "registry.json");
10131
+ if (fs25.existsSync(registryPath)) {
9815
10132
  filesToBackup.push({ name: "registry.json", localPath: registryPath });
9816
10133
  }
9817
- const usersDbPath = path31.join(os12.homedir(), ".code-intel", "users.db");
9818
- if (fs24.existsSync(usersDbPath)) {
10134
+ const usersDbPath = path32.join(os13.homedir(), ".code-intel", "users.db");
10135
+ if (fs25.existsSync(usersDbPath)) {
9819
10136
  filesToBackup.push({ name: "users.db", localPath: usersDbPath });
9820
10137
  }
9821
10138
  if (filesToBackup.length === 0) {
@@ -9826,7 +10143,7 @@ var BackupService = class {
9826
10143
  createdAt,
9827
10144
  version: BACKUP_VERSION,
9828
10145
  files: filesToBackup.map((f) => {
9829
- const data = fs24.readFileSync(f.localPath);
10146
+ const data = fs25.readFileSync(f.localPath);
9830
10147
  return {
9831
10148
  name: f.name,
9832
10149
  sha256: crypto5.createHash("sha256").update(data).digest("hex"),
@@ -9840,7 +10157,7 @@ var BackupService = class {
9840
10157
  manifestLenBuf.writeUInt32BE(manifestBuf.length, 0);
9841
10158
  parts.push(manifestLenBuf, manifestBuf);
9842
10159
  for (const f of filesToBackup) {
9843
- const data = fs24.readFileSync(f.localPath);
10160
+ const data = fs25.readFileSync(f.localPath);
9844
10161
  const nameBuf = Buffer.from(f.name, "utf-8");
9845
10162
  const nameLenBuf = Buffer.alloc(2);
9846
10163
  nameLenBuf.writeUInt16BE(nameBuf.length, 0);
@@ -9851,8 +10168,8 @@ var BackupService = class {
9851
10168
  const plaintext = Buffer.concat(parts);
9852
10169
  const encrypted = encryptBuffer(plaintext, this.key);
9853
10170
  const backupFileName = `backup-${id}.cib`;
9854
- const backupPath = path31.join(this.backupDir, backupFileName);
9855
- fs24.writeFileSync(backupPath, encrypted);
10171
+ const backupPath = path32.join(this.backupDir, backupFileName);
10172
+ fs25.writeFileSync(backupPath, encrypted);
9856
10173
  const entry = {
9857
10174
  id,
9858
10175
  createdAt,
@@ -9879,9 +10196,9 @@ var BackupService = class {
9879
10196
  async uploadToS3(entry) {
9880
10197
  const cfg = getS3Config();
9881
10198
  if (!cfg) throw new Error("S3 not configured. Set CODE_INTEL_BACKUP_S3_BUCKET, CODE_INTEL_BACKUP_S3_ACCESS_KEY_ID, CODE_INTEL_BACKUP_S3_SECRET_ACCESS_KEY.");
9882
- const fileName = path31.basename(entry.path);
10199
+ const fileName = path32.basename(entry.path);
9883
10200
  const s3Key = `${cfg.prefix}${fileName}`;
9884
- const body = fs24.readFileSync(entry.path);
10201
+ const body = fs25.readFileSync(entry.path);
9885
10202
  const result = await s3Request({ method: "PUT", cfg, key: s3Key, body });
9886
10203
  if (result.statusCode < 200 || result.statusCode >= 300) {
9887
10204
  throw new Error(`S3 upload failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
@@ -9898,8 +10215,8 @@ var BackupService = class {
9898
10215
  if (result.statusCode < 200 || result.statusCode >= 300) {
9899
10216
  throw new Error(`S3 download failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
9900
10217
  }
9901
- fs24.mkdirSync(path31.dirname(destPath), { recursive: true });
9902
- fs24.writeFileSync(destPath, Buffer.from(result.body, "binary"));
10218
+ fs25.mkdirSync(path32.dirname(destPath), { recursive: true });
10219
+ fs25.writeFileSync(destPath, Buffer.from(result.body, "binary"));
9903
10220
  }
9904
10221
  /**
9905
10222
  * List backup objects in S3 with the configured prefix.
@@ -9945,10 +10262,10 @@ var BackupService = class {
9945
10262
  if (!entry) {
9946
10263
  throw new Error(`Backup "${backupId}" not found.`);
9947
10264
  }
9948
- if (!fs24.existsSync(entry.path)) {
10265
+ if (!fs25.existsSync(entry.path)) {
9949
10266
  throw new Error(`Backup file not found at: ${entry.path}`);
9950
10267
  }
9951
- const encrypted = fs24.readFileSync(entry.path);
10268
+ const encrypted = fs25.readFileSync(entry.path);
9952
10269
  let plaintext;
9953
10270
  try {
9954
10271
  plaintext = decryptBuffer(encrypted, this.key);
@@ -9962,8 +10279,8 @@ var BackupService = class {
9962
10279
  offset += manifestLen;
9963
10280
  const manifest = JSON.parse(manifestStr);
9964
10281
  const restoreBase = targetRepoPath ?? entry.repoPath;
9965
- const codeIntelDir = path31.join(restoreBase, ".code-intel");
9966
- fs24.mkdirSync(codeIntelDir, { recursive: true });
10282
+ const codeIntelDir = path32.join(restoreBase, ".code-intel");
10283
+ fs25.mkdirSync(codeIntelDir, { recursive: true });
9967
10284
  for (const fileEntry of manifest.files) {
9968
10285
  const nameLen = plaintext.readUInt16BE(offset);
9969
10286
  offset += 2;
@@ -9980,18 +10297,18 @@ var BackupService = class {
9980
10297
  }
9981
10298
  let destPath;
9982
10299
  if (name === "registry.json" || name === "users.db") {
9983
- destPath = path31.join(os12.homedir(), ".code-intel", name);
10300
+ destPath = path32.join(os13.homedir(), ".code-intel", name);
9984
10301
  } else {
9985
- destPath = path31.join(codeIntelDir, name);
10302
+ destPath = path32.join(codeIntelDir, name);
9986
10303
  }
9987
- fs24.writeFileSync(destPath, data);
10304
+ fs25.writeFileSync(destPath, data);
9988
10305
  }
9989
10306
  }
9990
10307
  /**
9991
10308
  * Apply retention policy: keep N daily, M weekly, L monthly backups.
9992
10309
  */
9993
10310
  applyRetention(options = { daily: 7, weekly: 4, monthly: 12 }) {
9994
- const entries = this._loadIndex().filter((e) => fs24.existsSync(e.path)).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
10311
+ const entries = this._loadIndex().filter((e) => fs25.existsSync(e.path)).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
9995
10312
  const keep = /* @__PURE__ */ new Set();
9996
10313
  const now = /* @__PURE__ */ new Date();
9997
10314
  const dailyCutoff = new Date(now);
@@ -10021,7 +10338,7 @@ var BackupService = class {
10021
10338
  for (const e of entries) {
10022
10339
  if (!keep.has(e.id)) {
10023
10340
  try {
10024
- fs24.unlinkSync(e.path);
10341
+ fs25.unlinkSync(e.path);
10025
10342
  deleted++;
10026
10343
  } catch {
10027
10344
  }
@@ -10033,17 +10350,17 @@ var BackupService = class {
10033
10350
  }
10034
10351
  // ── Index helpers ──────────────────────────────────────────────────────────
10035
10352
  _indexPath() {
10036
- return path31.join(this.backupDir, "index.json");
10353
+ return path32.join(this.backupDir, "index.json");
10037
10354
  }
10038
10355
  _loadIndex() {
10039
10356
  try {
10040
- return JSON.parse(fs24.readFileSync(this._indexPath(), "utf-8"));
10357
+ return JSON.parse(fs25.readFileSync(this._indexPath(), "utf-8"));
10041
10358
  } catch {
10042
10359
  return [];
10043
10360
  }
10044
10361
  }
10045
10362
  _saveIndex(entries) {
10046
- fs24.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
10363
+ fs25.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
10047
10364
  }
10048
10365
  _appendIndex(entry) {
10049
10366
  const entries = this._loadIndex();
@@ -10836,11 +11153,11 @@ var openApiSpec = {
10836
11153
  };
10837
11154
 
10838
11155
  // src/http/app.ts
10839
- var __dirname$1 = path31.dirname(fileURLToPath(import.meta.url));
11156
+ var __dirname$1 = path32.dirname(fileURLToPath(import.meta.url));
10840
11157
  var WEB_DIST = (() => {
10841
- const bundled = path31.resolve(__dirname$1, "..", "web");
10842
- if (fs24.existsSync(bundled)) return bundled;
10843
- return path31.resolve(__dirname$1, "..", "..", "..", "web", "dist");
11158
+ const bundled = path32.resolve(__dirname$1, "..", "web");
11159
+ if (fs25.existsSync(bundled)) return bundled;
11160
+ return path32.resolve(__dirname$1, "..", "..", "..", "web", "dist");
10844
11161
  })();
10845
11162
  function getAllowedOrigins() {
10846
11163
  const env = process.env["CODE_INTEL_CORS_ORIGINS"];
@@ -10849,13 +11166,17 @@ function getAllowedOrigins() {
10849
11166
  }
10850
11167
  function createDefaultLimiter() {
10851
11168
  const max = parseInt(process.env["CODE_INTEL_RATE_LIMIT_MAX"] ?? "100", 10);
10852
- const windowMs = parseInt(process.env["CODE_INTEL_RATE_LIMIT_WINDOW_MS"] ?? `${15 * 60 * 1e3}`, 10);
11169
+ const windowMs = parseInt(process.env["CODE_INTEL_RATE_LIMIT_WINDOW_MS"] ?? `${60 * 1e3}`, 10);
10853
11170
  return rateLimit({
10854
11171
  windowMs,
10855
11172
  max,
10856
11173
  standardHeaders: true,
10857
11174
  legacyHeaders: false,
10858
- skip: (req) => req.path.startsWith("/health") || req.path === "/metrics",
11175
+ // Skip health checks, metrics, and read-only listing/pagination endpoints.
11176
+ // The node pagination and group/repo listing endpoints are hit many times
11177
+ // when loading a large graph — rate-limiting them only hurts the user's own
11178
+ // session without providing meaningful abuse protection.
11179
+ skip: (req) => req.path.startsWith("/health") || req.path === "/metrics" || req.path === "/api/v1/repos" || req.path === "/api/v1/groups" || req.method === "GET" && req.path.startsWith("/api/v1/groups/") || /^\/api\/v1\/graph\/[^/]+\/nodes$/.test(req.path),
10859
11180
  message: {
10860
11181
  error: {
10861
11182
  code: ErrorCodes.RATE_LIMIT_EXCEEDED,
@@ -10914,12 +11235,31 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
10914
11235
  });
10915
11236
  app.use(requestIdMiddleware);
10916
11237
  app.use(authMiddleware);
11238
+ let dbUnavailableSince = null;
10917
11239
  app.use((_req, res, next) => {
10918
11240
  if (workspaceRoot) {
11241
+ const metaFilePath = path32.join(workspaceRoot, ".code-intel", "meta.json");
11242
+ let metaOk = false;
10919
11243
  try {
10920
- const meta = loadMetadata(workspaceRoot);
10921
- if (meta?.indexVersion) res.setHeader("X-Index-Version", meta.indexVersion);
10922
- } catch {
11244
+ if (fs25.existsSync(metaFilePath)) {
11245
+ const raw = fs25.readFileSync(metaFilePath, "utf-8");
11246
+ const meta = JSON.parse(raw);
11247
+ if (meta?.indexVersion) res.setHeader("X-Index-Version", meta.indexVersion);
11248
+ }
11249
+ metaOk = true;
11250
+ if (dbUnavailableSince !== null) {
11251
+ dbUnavailableSince = null;
11252
+ logger_default.info("[serve] DB back online \u2014 cleared stale flag");
11253
+ }
11254
+ } catch (err) {
11255
+ if (dbUnavailableSince === null) {
11256
+ dbUnavailableSince = (/* @__PURE__ */ new Date()).toISOString();
11257
+ logger_default.warn(`[serve] DB unavailable since ${dbUnavailableSince}: ${err instanceof Error ? err.message : String(err)}`);
11258
+ }
11259
+ }
11260
+ if (!metaOk) {
11261
+ res.setHeader("X-Stale", "true");
11262
+ res.setHeader("X-Stale-Since", dbUnavailableSince);
10923
11263
  }
10924
11264
  }
10925
11265
  next();
@@ -10975,6 +11315,21 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
10975
11315
  let vectorIndex = null;
10976
11316
  let vectorIndexBuilding = false;
10977
11317
  let vectorIndexReady = false;
11318
+ let bm25Index = null;
11319
+ function ensureBm25Index() {
11320
+ if (bm25Index) return bm25Index;
11321
+ if (!workspaceRoot) return null;
11322
+ const idx = new Bm25Index(getBm25DbPath(workspaceRoot));
11323
+ idx.load();
11324
+ if (idx.isLoaded) {
11325
+ bm25Index = idx;
11326
+ return idx;
11327
+ }
11328
+ return null;
11329
+ }
11330
+ if (workspaceRoot && process.env["NODE_ENV"] !== "test") {
11331
+ setImmediate(() => ensureBm25Index());
11332
+ }
10978
11333
  async function ensureVectorIndex() {
10979
11334
  if (vectorIndexReady && vectorIndex) return vectorIndex;
10980
11335
  if (!workspaceRoot || vectorIndexBuilding) return null;
@@ -11371,10 +11726,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11371
11726
  const registry = loadRegistry();
11372
11727
  const entry = registry.find((r) => r.name === requestedRepo || r.path === requestedRepo);
11373
11728
  if (!entry) return null;
11374
- const dbPath = path31.join(entry.path, ".code-intel", "graph.db");
11375
- if (!fs24.existsSync(dbPath)) return null;
11729
+ const dbPath = path32.join(entry.path, ".code-intel", "graph.db");
11730
+ if (!fs25.existsSync(dbPath)) return null;
11376
11731
  const repoGraph = createKnowledgeGraph();
11377
- const db = new DbManager(dbPath);
11732
+ const db = new DbManager(dbPath, true);
11378
11733
  try {
11379
11734
  await db.init();
11380
11735
  await loadGraphFromDB(repoGraph, db);
@@ -11385,10 +11740,33 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11385
11740
  return null;
11386
11741
  }
11387
11742
  }
11743
+ async function loadGroupGraph(groupName) {
11744
+ const group = loadGroup(groupName);
11745
+ if (!group) return null;
11746
+ const registry = loadRegistry();
11747
+ const mergedGraph = createKnowledgeGraph();
11748
+ for (const member of group.members) {
11749
+ const regEntry = registry.find((r) => r.name === member.registryName);
11750
+ if (!regEntry) continue;
11751
+ const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
11752
+ if (!fs25.existsSync(dbPath)) continue;
11753
+ const db = new DbManager(dbPath, true);
11754
+ try {
11755
+ await db.init();
11756
+ await loadGraphFromDB(mergedGraph, db);
11757
+ db.close();
11758
+ } catch {
11759
+ db.close();
11760
+ }
11761
+ }
11762
+ return mergedGraph.size.nodes > 0 ? mergedGraph : null;
11763
+ }
11388
11764
  async function getGraphForRepo(requestedRepo) {
11389
11765
  if (!requestedRepo || requestedRepo === repoName) return graph;
11390
11766
  const g = await loadRepoGraph(requestedRepo);
11391
- return g ?? graph;
11767
+ if (g) return g;
11768
+ const gg = await loadGroupGraph(requestedRepo);
11769
+ return gg ?? graph;
11392
11770
  }
11393
11771
  app.get("/api/v1/graph/:repo", requireRepoAccess((req) => {
11394
11772
  const p = req.params["repo"];
@@ -11412,11 +11790,56 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11412
11790
  }
11413
11791
  res.json({ nodes: [...g.allNodes()], edges: [...g.allEdges()] });
11414
11792
  });
11793
+ app.get("/api/v1/graph/:repo/nodes", requireRepoAccess((req) => {
11794
+ const p = req.params["repo"];
11795
+ const repo = Array.isArray(p) ? p[0] : p;
11796
+ return repo ? decodeURIComponent(repo) : void 0;
11797
+ }), async (req, res) => {
11798
+ const rawRepo = req.params["repo"];
11799
+ const requestedRepo = decodeURIComponent(Array.isArray(rawRepo) ? rawRepo[0] ?? "" : rawRepo ?? "");
11800
+ const limit = Math.min(parseInt(req.query["limit"] ?? "200", 10), 1e3);
11801
+ const offset = Math.max(parseInt(req.query["offset"] ?? "0", 10), 0);
11802
+ const g = requestedRepo === repoName ? graph : await loadRepoGraph(requestedRepo);
11803
+ if (!g) {
11804
+ res.status(404).json({
11805
+ error: {
11806
+ code: ErrorCodes.NOT_FOUND,
11807
+ message: `Repo "${requestedRepo}" not found or not indexed`,
11808
+ hint: `Run: code-intel analyze <path>`,
11809
+ requestId: req.requestId,
11810
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
11811
+ }
11812
+ });
11813
+ return;
11814
+ }
11815
+ let nodes;
11816
+ if (isLazyGraph(g)) {
11817
+ nodes = await g.getNodePage(offset, limit);
11818
+ } else {
11819
+ const eager = g;
11820
+ if (!eager._nodeArray) {
11821
+ eager._nodeArray = [...g.allNodes()];
11822
+ }
11823
+ nodes = eager._nodeArray.slice(offset, offset + limit);
11824
+ }
11825
+ res.json({
11826
+ nodes,
11827
+ offset,
11828
+ limit,
11829
+ total: g.size.nodes,
11830
+ hasMore: offset + nodes.length < g.size.nodes
11831
+ });
11832
+ });
11415
11833
  app.post("/api/v1/search", requireToolScope("search"), async (req, res) => {
11416
11834
  const { query, limit, repo } = req.body;
11417
11835
  const g = await getGraphForRepo(repo);
11418
11836
  const vdbPath = workspaceRoot ? getVectorDbPath(workspaceRoot) : void 0;
11419
- const { results, searchMode } = await hybridSearch(g, query ?? "", limit ?? 20, { vectorDbPath: vdbPath });
11837
+ const bm25 = !repo || repo === repoName ? ensureBm25Index() : null;
11838
+ const bm25Results = bm25 ? bm25.search(query ?? "", (limit ?? 20) * 3) : null;
11839
+ const { results, searchMode } = await hybridSearch(g, query ?? "", limit ?? 20, {
11840
+ vectorDbPath: vdbPath,
11841
+ bm25Results: bm25Results ?? void 0
11842
+ });
11420
11843
  res.json({ results, searchMode });
11421
11844
  });
11422
11845
  app.post("/api/v1/vector-search", async (req, res) => {
@@ -11459,7 +11882,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11459
11882
  return;
11460
11883
  }
11461
11884
  try {
11462
- const content = fs24.readFileSync(file_path, "utf-8");
11885
+ const content = fs25.readFileSync(file_path, "utf-8");
11463
11886
  res.json({ content });
11464
11887
  } catch {
11465
11888
  res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "File not found" } });
@@ -11496,7 +11919,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11496
11919
  if (workspaceRoot) {
11497
11920
  try {
11498
11921
  const dbPath = getDbPath(workspaceRoot);
11499
- const dbm = new DbManager(dbPath);
11922
+ const dbm = new DbManager(dbPath, true);
11500
11923
  await dbm.init();
11501
11924
  const rows = await dbm.query(q);
11502
11925
  dbm.close();
@@ -11537,33 +11960,53 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11537
11960
  app.get("/api/v1/nodes/:id", async (req, res) => {
11538
11961
  const nodeId = decodeURIComponent(req.params.id);
11539
11962
  const g = await getGraphForRepo(req.query["repo"]);
11540
- const node = g.getNode(nodeId);
11963
+ const node = isLazyGraph(g) ? await g.getNodeAsync(nodeId) : g.getNode(nodeId);
11541
11964
  if (!node) {
11542
11965
  res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "Node not found", requestId: req.requestId } });
11543
11966
  return;
11544
11967
  }
11545
11968
  const incoming = [...g.findEdgesTo(nodeId)];
11546
11969
  const outgoing = [...g.findEdgesFrom(nodeId)];
11970
+ const resolveName = isLazyGraph(g) ? async (id) => {
11971
+ const n = g.getNode(id) ?? await g.getNodeAsync(id);
11972
+ return n?.name;
11973
+ } : (id) => Promise.resolve(g.getNode(id)?.name);
11974
+ const resolveKind = isLazyGraph(g) ? async (id) => {
11975
+ const n = g.getNode(id) ?? await g.getNodeAsync(id);
11976
+ return n?.kind;
11977
+ } : (id) => Promise.resolve(g.getNode(id)?.kind);
11547
11978
  res.json({
11548
11979
  node,
11549
- callers: incoming.filter((e) => e.kind === "calls").map((e) => ({ id: e.source, name: g.getNode(e.source)?.name, weight: e.weight })),
11550
- callees: outgoing.filter((e) => e.kind === "calls").map((e) => ({ id: e.target, name: g.getNode(e.target)?.name, weight: e.weight })),
11551
- imports: outgoing.filter((e) => e.kind === "imports").map((e) => ({ id: e.target, name: g.getNode(e.target)?.name })),
11552
- importedBy: incoming.filter((e) => e.kind === "imports").map((e) => ({ id: e.source, name: g.getNode(e.source)?.name })),
11553
- extends: outgoing.filter((e) => e.kind === "extends").map((e) => ({ id: e.target, name: g.getNode(e.target)?.name })),
11554
- implementsEdges: outgoing.filter((e) => e.kind === "implements").map((e) => ({ id: e.target, name: g.getNode(e.target)?.name })),
11555
- members: outgoing.filter((e) => e.kind === "has_member").map((e) => ({ id: e.target, name: g.getNode(e.target)?.name, kind: g.getNode(e.target)?.kind })),
11556
- cluster: incoming.filter((e) => e.kind === "belongs_to").map((e) => g.getNode(e.target)?.name)[0]
11980
+ callers: await Promise.all(incoming.filter((e) => e.kind === "calls").map(async (e) => ({ id: e.source, name: await resolveName(e.source), weight: e.weight }))),
11981
+ callees: await Promise.all(outgoing.filter((e) => e.kind === "calls").map(async (e) => ({ id: e.target, name: await resolveName(e.target), weight: e.weight }))),
11982
+ imports: await Promise.all(outgoing.filter((e) => e.kind === "imports").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
11983
+ importedBy: await Promise.all(incoming.filter((e) => e.kind === "imports").map(async (e) => ({ id: e.source, name: await resolveName(e.source) }))),
11984
+ extends: await Promise.all(outgoing.filter((e) => e.kind === "extends").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
11985
+ implementsEdges: await Promise.all(outgoing.filter((e) => e.kind === "implements").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
11986
+ members: await Promise.all(outgoing.filter((e) => e.kind === "has_member").map(async (e) => ({ id: e.target, name: await resolveName(e.target), kind: await resolveKind(e.target) }))),
11987
+ cluster: (await Promise.all(incoming.filter((e) => e.kind === "belongs_to").map(async (e) => resolveName(e.target))))[0]
11557
11988
  });
11558
11989
  });
11559
11990
  app.post("/api/v1/blast-radius", async (req, res) => {
11560
11991
  const { target, direction = "both", max_hops = 5, repo } = req.body;
11561
11992
  const g = await getGraphForRepo(repo);
11562
11993
  let targetNode = null;
11563
- for (const node of g.allNodes()) {
11564
- if (node.name === target || node.id === target) {
11565
- targetNode = node;
11566
- break;
11994
+ if (isLazyGraph(g) && target) {
11995
+ targetNode = g.getNode(target) ?? await g.getNodeAsync(target) ?? null;
11996
+ if (!targetNode) {
11997
+ for await (const node of g.allNodesAsync()) {
11998
+ if (node.name === target || node.id === target) {
11999
+ targetNode = node;
12000
+ break;
12001
+ }
12002
+ }
12003
+ }
12004
+ } else {
12005
+ for (const node of g.allNodes()) {
12006
+ if (node.name === target || node.id === target) {
12007
+ targetNode = node;
12008
+ break;
12009
+ }
11567
12010
  }
11568
12011
  }
11569
12012
  if (!targetNode) {
@@ -11717,9 +12160,9 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11717
12160
  for (const member of group.members) {
11718
12161
  const regEntry = registry.find((r) => r.name === member.registryName);
11719
12162
  if (!regEntry) continue;
11720
- const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
11721
- if (!fs24.existsSync(dbPath)) continue;
11722
- const db = new DbManager(dbPath);
12163
+ const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
12164
+ if (!fs25.existsSync(dbPath)) continue;
12165
+ const db = new DbManager(dbPath, true);
11723
12166
  try {
11724
12167
  await db.init();
11725
12168
  await loadGraphFromDB(mergedGraph, db);
@@ -11744,10 +12187,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11744
12187
  let nodeCount = 0;
11745
12188
  let edgeCount = 0;
11746
12189
  if (regEntry) {
11747
- const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
11748
- if (fs24.existsSync(dbPath)) {
12190
+ const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
12191
+ if (fs25.existsSync(dbPath)) {
11749
12192
  try {
11750
- const db = new DbManager(dbPath);
12193
+ const db = new DbManager(dbPath, true);
11751
12194
  await db.init();
11752
12195
  const g = createKnowledgeGraph();
11753
12196
  await loadGraphFromDB(g, db);
@@ -11770,7 +12213,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11770
12213
  res.json({ repos, edges });
11771
12214
  });
11772
12215
  app.get("/api/v1/source", requireAuth, requireRole("viewer"), (req, res) => {
11773
- const { file, startLine: startLineStr, endLine: endLineStr } = req.query;
12216
+ const { file, startLine: startLineStr, endLine: endLineStr, repo } = req.query;
11774
12217
  if (!file) {
11775
12218
  res.status(400).json({
11776
12219
  error: {
@@ -11794,14 +12237,36 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11794
12237
  });
11795
12238
  return;
11796
12239
  }
11797
- let rawResolved = path31.normalize(file);
11798
- if (!path31.isAbsolute(rawResolved) && workspaceRoot) {
11799
- rawResolved = path31.join(workspaceRoot, rawResolved);
12240
+ let baseDir = workspaceRoot;
12241
+ if (repo && repo !== repoName) {
12242
+ const registry = loadRegistry();
12243
+ const entry = registry.find((r) => r.name === repo || r.path === repo);
12244
+ if (entry) {
12245
+ baseDir = entry.path;
12246
+ } else {
12247
+ const group = loadGroup(repo);
12248
+ if (group) {
12249
+ const normalizedFile = path32.normalize(file);
12250
+ for (const member of group.members) {
12251
+ const regEntry = registry.find((r) => r.name === member.registryName);
12252
+ if (!regEntry) continue;
12253
+ const candidate = path32.resolve(path32.join(regEntry.path, normalizedFile));
12254
+ if (fs25.existsSync(candidate)) {
12255
+ baseDir = regEntry.path;
12256
+ break;
12257
+ }
12258
+ }
12259
+ }
12260
+ }
12261
+ }
12262
+ let rawResolved = path32.normalize(file);
12263
+ if (!path32.isAbsolute(rawResolved) && baseDir) {
12264
+ rawResolved = path32.join(baseDir, rawResolved);
11800
12265
  }
11801
- const resolvedFile = path31.resolve(rawResolved);
12266
+ const resolvedFile = path32.resolve(rawResolved);
11802
12267
  function isInsideDir(fileAbs, dir) {
11803
- const rel = path31.relative(path31.resolve(dir), fileAbs);
11804
- return !rel.startsWith("..") && !path31.isAbsolute(rel);
12268
+ const rel = path32.relative(path32.resolve(dir), fileAbs);
12269
+ return !rel.startsWith("..") && !path32.isAbsolute(rel);
11805
12270
  }
11806
12271
  if (workspaceRoot) {
11807
12272
  if (!isInsideDir(resolvedFile, workspaceRoot)) {
@@ -11838,7 +12303,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11838
12303
  }
11839
12304
  let fileContent;
11840
12305
  try {
11841
- fileContent = fs24.readFileSync(resolvedFile, "utf-8");
12306
+ fileContent = fs25.readFileSync(resolvedFile, "utf-8");
11842
12307
  } catch {
11843
12308
  res.status(404).json({
11844
12309
  error: {
@@ -11869,7 +12334,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11869
12334
  const contextStart = Math.max(1, startLine - 20);
11870
12335
  const contextEnd = Math.min(lines.length, endLine + 20);
11871
12336
  const content = lines.slice(contextStart - 1, contextEnd).join("\n");
11872
- const ext = path31.extname(resolvedFile).toLowerCase();
12337
+ const ext = path32.extname(resolvedFile).toLowerCase();
11873
12338
  const languageMap = {
11874
12339
  ".ts": "typescript",
11875
12340
  ".tsx": "typescript",
@@ -12004,10 +12469,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
12004
12469
  res.status(500).json({ error: { code: ErrorCodes.INTERNAL_ERROR, message: err instanceof Error ? err.message : String(err), requestId: req.requestId, timestamp: (/* @__PURE__ */ new Date()).toISOString() } });
12005
12470
  }
12006
12471
  });
12007
- if (fs24.existsSync(WEB_DIST)) {
12472
+ if (fs25.existsSync(WEB_DIST)) {
12008
12473
  app.use(express.static(WEB_DIST));
12009
12474
  app.get("/{*path}", (_req, res) => {
12010
- res.sendFile(path31.join(WEB_DIST, "index.html"));
12475
+ res.sendFile(path32.join(WEB_DIST, "index.html"));
12011
12476
  });
12012
12477
  }
12013
12478
  app.use("/admin", requireRole("admin"));
@@ -12167,6 +12632,6 @@ function mergeSearchResults(...perRepoResults) {
12167
12632
  return reciprocalRankFusion(...perRepoResults);
12168
12633
  }
12169
12634
 
12170
- export { AstCache, BindingTracker, DbManager, addBinding, addClustersToGraph, addMember, buildCallEdges, buildHeritageEdges, classifyCall, clusterPhase, computeMRO, createApp, createKnowledgeGraph, createMcpServer, createScope, deleteGroup, detectCommunities, detectOverrides, findEntryPoints, flowPhase, generateEdgeId, generateNodeId, getAllLanguageModules, getDbPath, getLanguage, getLanguageModule, getParser, groupExists, initParser, isTreeSitterAvailable, listGroups, loadGraphToDB, loadGroup, loadMetadata, loadRegistry, loadSyncResult, mergeSearchResults, parsePhase, parseSource, queryGroup, reciprocalRankFusion, removeMember, removeRepo, resolveBinding, resolveImports, resolvePhase, runPipeline, runQuery, runQueryMatches, saveGroup, saveMetadata, saveSyncResult, scanPhase, startHttpServer, startMcpStdio, structurePhase, syncGroup, textSearch, topologicalSort, traceFlow, upsertRepo, validateDAG };
12635
+ export { AstCache, BindingTracker, Bm25Index, DbManager, addBinding, addClustersToGraph, addMember, buildCallEdges, buildHeritageEdges, classifyCall, clusterPhase, computeMRO, createApp, createKnowledgeGraph, createMcpServer, createScope, deleteGroup, detectCommunities, detectOverrides, findEntryPoints, flowPhase, generateEdgeId, generateNodeId, getAllLanguageModules, getBm25DbPath, getDbPath, getLanguage, getLanguageModule, getParser, groupExists, initParser, isTreeSitterAvailable, listGroups, loadGraphToDB, loadGroup, loadMetadata, loadRegistry, loadSyncResult, mergeSearchResults, parsePhase, parseSource, queryGroup, reciprocalRankFusion, removeMember, removeRepo, resolveBinding, resolveImports, resolvePhase, runPipeline, runQuery, runQueryMatches, saveGroup, saveMetadata, saveSyncResult, scanPhase, startHttpServer, startMcpStdio, structurePhase, syncGroup, textSearch, topologicalSort, traceFlow, upsertRepo, validateDAG };
12171
12636
  //# sourceMappingURL=index.js.map
12172
12637
  //# sourceMappingURL=index.js.map