@vohongtho.infotech/code-intel 0.9.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,9 +6,15 @@ 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';
9
+ import fs26, { existsSync } from 'fs';
10
+ import path32 from 'path';
14
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 { Database, Connection } from '@ladybugdb/core';
15
16
  import { execSync } from 'child_process';
16
- import Database3 from 'better-sqlite3';
17
+ import Database2 from 'better-sqlite3';
17
18
  import bcrypt from 'bcrypt';
18
19
  import crypto5 from 'crypto';
19
20
  import { v4 } from 'uuid';
@@ -24,7 +25,6 @@ import 'events';
24
25
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
25
26
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
26
27
  import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
27
- import { Database, Connection } from '@ladybugdb/core';
28
28
  import express from 'express';
29
29
  import compression from 'compression';
30
30
  import cors from 'cors';
@@ -56,6 +56,135 @@ var __copyProps = (to, from, except, desc) => {
56
56
  };
57
57
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
58
58
 
59
+ // src/graph/knowledge-graph.ts
60
+ var knowledge_graph_exports = {};
61
+ __export(knowledge_graph_exports, {
62
+ createKnowledgeGraph: () => createKnowledgeGraph
63
+ });
64
+ function createKnowledgeGraph() {
65
+ const nodes = /* @__PURE__ */ new Map();
66
+ const edges = /* @__PURE__ */ new Map();
67
+ const edgesByKind = /* @__PURE__ */ new Map();
68
+ const edgesFromNode = /* @__PURE__ */ new Map();
69
+ const edgesToNode = /* @__PURE__ */ new Map();
70
+ function indexEdge(edge) {
71
+ let kindSet = edgesByKind.get(edge.kind);
72
+ if (!kindSet) {
73
+ kindSet = /* @__PURE__ */ new Set();
74
+ edgesByKind.set(edge.kind, kindSet);
75
+ }
76
+ kindSet.add(edge.id);
77
+ let fromSet = edgesFromNode.get(edge.source);
78
+ if (!fromSet) {
79
+ fromSet = /* @__PURE__ */ new Set();
80
+ edgesFromNode.set(edge.source, fromSet);
81
+ }
82
+ fromSet.add(edge.id);
83
+ let toSet = edgesToNode.get(edge.target);
84
+ if (!toSet) {
85
+ toSet = /* @__PURE__ */ new Set();
86
+ edgesToNode.set(edge.target, toSet);
87
+ }
88
+ toSet.add(edge.id);
89
+ }
90
+ function unindexEdge(edge) {
91
+ edgesByKind.get(edge.kind)?.delete(edge.id);
92
+ edgesFromNode.get(edge.source)?.delete(edge.id);
93
+ edgesToNode.get(edge.target)?.delete(edge.id);
94
+ }
95
+ return {
96
+ addNode(node) {
97
+ nodes.set(node.id, node);
98
+ },
99
+ addEdge(edge) {
100
+ edges.set(edge.id, edge);
101
+ indexEdge(edge);
102
+ },
103
+ getNode(id) {
104
+ return nodes.get(id);
105
+ },
106
+ getEdge(id) {
107
+ return edges.get(id);
108
+ },
109
+ *findEdgesByKind(kind) {
110
+ const ids = edgesByKind.get(kind);
111
+ if (!ids) return;
112
+ for (const id of ids) {
113
+ const edge = edges.get(id);
114
+ if (edge) yield edge;
115
+ }
116
+ },
117
+ *findEdgesFrom(sourceId) {
118
+ const ids = edgesFromNode.get(sourceId);
119
+ if (!ids) return;
120
+ for (const id of ids) {
121
+ const edge = edges.get(id);
122
+ if (edge) yield edge;
123
+ }
124
+ },
125
+ *findEdgesTo(targetId) {
126
+ const ids = edgesToNode.get(targetId);
127
+ if (!ids) return;
128
+ for (const id of ids) {
129
+ const edge = edges.get(id);
130
+ if (edge) yield edge;
131
+ }
132
+ },
133
+ removeNodeCascade(id) {
134
+ const fromEdges = edgesFromNode.get(id);
135
+ if (fromEdges) {
136
+ for (const edgeId of [...fromEdges]) {
137
+ const edge = edges.get(edgeId);
138
+ if (edge) {
139
+ unindexEdge(edge);
140
+ edges.delete(edgeId);
141
+ }
142
+ }
143
+ }
144
+ const toEdges = edgesToNode.get(id);
145
+ if (toEdges) {
146
+ for (const edgeId of [...toEdges]) {
147
+ const edge = edges.get(edgeId);
148
+ if (edge) {
149
+ unindexEdge(edge);
150
+ edges.delete(edgeId);
151
+ }
152
+ }
153
+ }
154
+ edgesFromNode.delete(id);
155
+ edgesToNode.delete(id);
156
+ nodes.delete(id);
157
+ },
158
+ removeEdge(id) {
159
+ const edge = edges.get(id);
160
+ if (edge) {
161
+ unindexEdge(edge);
162
+ edges.delete(id);
163
+ }
164
+ },
165
+ *allNodes() {
166
+ yield* nodes.values();
167
+ },
168
+ *allEdges() {
169
+ yield* edges.values();
170
+ },
171
+ get size() {
172
+ return { nodes: nodes.size, edges: edges.size };
173
+ },
174
+ clear() {
175
+ nodes.clear();
176
+ edges.clear();
177
+ edgesByKind.clear();
178
+ edgesFromNode.clear();
179
+ edgesToNode.clear();
180
+ }
181
+ };
182
+ }
183
+ var init_knowledge_graph = __esm({
184
+ "src/graph/knowledge-graph.ts"() {
185
+ }
186
+ });
187
+
59
188
  // src/graph/id-generator.ts
60
189
  function generateNodeId(kind, filePath, qualifiedName) {
61
190
  return `${kind}:${filePath}:${qualifiedName}`;
@@ -68,310 +197,701 @@ var init_id_generator = __esm({
68
197
  }
69
198
  });
70
199
 
71
- // src/shared/languages.ts
72
- var init_languages = __esm({
73
- "src/shared/languages.ts"() {
74
- }
75
- });
76
-
77
- // src/shared/detection.ts
78
- function detectLanguage(filePath) {
79
- const ext = filePath.slice(filePath.lastIndexOf("."));
80
- return EXTENSION_MAP[ext] ?? null;
200
+ // src/storage/schema.ts
201
+ function getCreateNodeTableDDL(tableName) {
202
+ return `CREATE NODE TABLE IF NOT EXISTS ${tableName} (
203
+ id STRING,
204
+ name STRING,
205
+ file_path STRING,
206
+ start_line INT64,
207
+ end_line INT64,
208
+ exported BOOLEAN,
209
+ content STRING,
210
+ metadata STRING,
211
+ PRIMARY KEY (id)
212
+ )`;
81
213
  }
82
- function getSupportedExtensions() {
83
- return Object.keys(EXTENSION_MAP);
214
+ function getCreateEdgeTableDDL() {
215
+ const uniqueTables = ALL_NODE_TABLES;
216
+ const fromToPairs = [];
217
+ for (const from of uniqueTables) {
218
+ for (const to of uniqueTables) {
219
+ fromToPairs.push(`FROM ${from} TO ${to}`);
220
+ }
221
+ }
222
+ return [`CREATE REL TABLE GROUP IF NOT EXISTS code_edges (
223
+ ${fromToPairs.join(",\n ")},
224
+ kind STRING,
225
+ weight DOUBLE,
226
+ label STRING
227
+ )`];
84
228
  }
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 */
229
+ var NODE_TABLE_MAP, ALL_NODE_TABLES;
230
+ var init_schema = __esm({
231
+ "src/storage/schema.ts"() {
232
+ NODE_TABLE_MAP = {
233
+ file: "file_nodes",
234
+ directory: "dir_nodes",
235
+ function: "func_nodes",
236
+ class: "class_nodes",
237
+ interface: "iface_nodes",
238
+ method: "method_nodes",
239
+ constructor: "ctor_nodes",
240
+ variable: "var_nodes",
241
+ property: "prop_nodes",
242
+ struct: "struct_nodes",
243
+ enum: "enum_nodes",
244
+ trait: "trait_nodes",
245
+ namespace: "ns_nodes",
246
+ module: "mod_nodes",
247
+ type_alias: "type_nodes",
248
+ constant: "const_nodes",
249
+ route: "route_nodes",
250
+ cluster: "cluster_nodes",
251
+ flow: "flow_nodes",
252
+ vulnerability: "vuln_nodes"
117
253
  };
254
+ ALL_NODE_TABLES = [...new Set(Object.values(NODE_TABLE_MAP))];
118
255
  }
119
256
  });
120
257
 
121
- // src/shared/index.ts
122
- var init_shared = __esm({
123
- "src/shared/index.ts"() {
124
- init_languages();
125
- init_detection();
126
- }
258
+ // src/observability/tracing.ts
259
+ var tracing_exports = {};
260
+ __export(tracing_exports, {
261
+ SpanStatusCode: () => SpanStatusCode,
262
+ context: () => context,
263
+ getActiveTraceContext: () => getActiveTraceContext,
264
+ getTracer: () => getTracer,
265
+ initTracing: () => initTracing,
266
+ isTracingEnabled: () => isTracingEnabled,
267
+ sanitizeAttrs: () => sanitizeAttrs,
268
+ shutdownTracing: () => shutdownTracing,
269
+ trace: () => trace,
270
+ withSpan: () => withSpan
127
271
  });
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;
138
- }
139
- return candidates[0];
272
+ function isTracingEnabled() {
273
+ return process.env["CODE_INTEL_OTEL_ENABLED"] === "true";
140
274
  }
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) {
167
- try {
168
- return _require.resolve(relative);
169
- } catch {
170
- }
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
- }
275
+ function initTracing() {
276
+ if (!isTracingEnabled()) return;
277
+ if (_sdk) return;
278
+ const endpoint = process.env["CODE_INTEL_OTEL_ENDPOINT"] ?? "http://localhost:4318";
279
+ const serviceName = process.env["CODE_INTEL_OTEL_SERVICE"] ?? "code-intel";
280
+ const deploymentEnv = process.env["CODE_INTEL_OTEL_ENV"] ?? process.env["NODE_ENV"] ?? "development";
281
+ const exporter = new OTLPTraceExporter({ url: `${endpoint}/v1/traces` });
282
+ _sdk = new NodeSDK({
283
+ resource: resourceFromAttributes({
284
+ [SEMRESATTRS_SERVICE_NAME]: serviceName,
285
+ [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: deploymentEnv
286
+ }),
287
+ traceExporter: exporter,
288
+ instrumentations: [
289
+ getNodeAutoInstrumentations({
290
+ // Disable noisy file-system instrumentation
291
+ "@opentelemetry/instrumentation-fs": { enabled: false }
292
+ })
293
+ ]
294
+ });
295
+ _sdk.start();
201
296
  }
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);
297
+ async function shutdownTracing() {
298
+ if (_sdk) {
299
+ await _sdk.shutdown();
300
+ _sdk = null;
209
301
  }
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);
217
302
  }
218
- async function isTreeSitterAvailable(lang) {
219
- return await getLanguage(lang) !== null;
220
- }
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();
230
- }
231
- });
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;
303
+ function getTracer() {
304
+ return trace.getTracer(TRACER_NAME);
244
305
  }
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
306
+ async function withSpan(name, attrs, fn) {
307
+ return getTracer().startActiveSpan(name, { attributes: sanitizeAttrs(attrs) }, async (span) => {
308
+ try {
309
+ const result = await fn(span);
310
+ span.setStatus({ code: SpanStatusCode.OK });
311
+ return result;
312
+ } catch (err) {
313
+ span.setStatus({
314
+ code: SpanStatusCode.ERROR,
315
+ message: err instanceof Error ? err.message : String(err)
255
316
  });
317
+ span.recordException(err);
318
+ throw err;
319
+ } finally {
320
+ span.end();
256
321
  }
322
+ });
323
+ }
324
+ function sanitizeAttrs(attrs) {
325
+ const safe = {};
326
+ for (const [k, v] of Object.entries(attrs)) {
327
+ if (BLOCKED_ATTR_KEYS.test(k)) continue;
328
+ safe[k] = v;
257
329
  }
258
- return captures;
330
+ return safe;
259
331
  }
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
- }));
332
+ function getActiveTraceContext() {
333
+ const span = trace.getActiveSpan();
334
+ if (!span) return { traceId: "", spanId: "" };
335
+ const ctx = span.spanContext();
336
+ return { traceId: ctx.traceId, spanId: ctx.spanId };
271
337
  }
272
- var _queryCache;
273
- var init_query_runner = __esm({
274
- "src/parsing/query-runner.ts"() {
275
- _queryCache = /* @__PURE__ */ new WeakMap();
276
- }
277
- });
278
-
279
- // src/parsing/queries/typescript.ts
280
- var typescriptQueries;
281
- var init_typescript = __esm({
282
- "src/parsing/queries/typescript.ts"() {
283
- typescriptQueries = `
284
- ;; Class declaration
285
- (class_declaration
286
- name: (type_identifier) @def.class.name) @def.class
287
-
288
- ;; Abstract class
289
- (abstract_class_declaration
290
- name: (type_identifier) @def.class.name) @def.class
291
-
292
- ;; Interface declaration
293
- (interface_declaration
294
- name: (type_identifier) @def.interface.name) @def.interface
295
-
296
- ;; Type alias
297
- (type_alias_declaration
298
- name: (type_identifier) @def.type_alias.name) @def.type_alias
299
-
300
- ;; Enum
301
- (enum_declaration
302
- name: (identifier) @def.enum.name) @def.enum
303
-
304
- ;; Function declaration
305
- (function_declaration
306
- name: (identifier) @def.func.name) @def.func
307
-
308
- ;; Arrow function in variable
309
- (lexical_declaration
310
- (variable_declarator
311
- name: (identifier) @def.func.name
312
- value: (arrow_function))) @def.func
313
-
314
- ;; Const/let/var variable (non-function)
315
- (lexical_declaration
316
- (variable_declarator
317
- name: (identifier) @def.var.name
318
- value: (_) @def.var.value)) @def.var
319
-
320
- ;; Method definition
321
- (method_definition
322
- name: (property_identifier) @def.method.name) @def.method
323
-
324
- ;; Public field definition
325
- (public_field_definition
326
- name: (property_identifier) @def.property.name) @def.property
327
-
328
- ;; Import statement
329
- (import_statement
330
- source: (string) @imp.source) @imp
331
-
332
- ;; Import specifier
333
- (import_specifier
334
- name: (identifier) @imp.name)
335
-
336
- ;; Namespace import
337
- (namespace_import (identifier) @imp.namespace)
338
-
339
- ;; Export statement
340
- (export_statement) @export
341
-
342
- ;; Call expression \u2014 function call
343
- (call_expression
344
- function: (identifier) @call.name) @call
345
-
346
- ;; Call expression \u2014 member call
347
- (call_expression
348
- function: (member_expression
349
- object: (_) @call.receiver
350
- property: (property_identifier) @call.method)) @call.member
351
-
352
- ;; New expression
353
- (new_expression
354
- constructor: (identifier) @call.constructor) @call.new
355
-
356
- ;; Class heritage \u2014 extends
357
- (class_heritage
358
- (extends_clause
359
- value: (identifier) @inherit.extends))
360
-
361
- ;; Class heritage \u2014 implements
362
- (class_heritage
363
- (implements_clause
364
- (type_identifier) @inherit.implements))
365
- `;
338
+ var _sdk, TRACER_NAME, BLOCKED_ATTR_KEYS;
339
+ var init_tracing = __esm({
340
+ "src/observability/tracing.ts"() {
341
+ _sdk = null;
342
+ TRACER_NAME = "code-intel";
343
+ BLOCKED_ATTR_KEYS = /secret|password|token|key|auth|credential/i;
366
344
  }
367
345
  });
368
-
369
- // src/parsing/queries/python.ts
370
- var pythonQueries;
371
- var init_python = __esm({
372
- "src/parsing/queries/python.ts"() {
373
- pythonQueries = `
374
- ;; Class definition
346
+ function getActiveTraceCtx() {
347
+ if (_getTraceCtx === "pending") return { traceId: "", spanId: "" };
348
+ if (_getTraceCtx) return _getTraceCtx();
349
+ _getTraceCtx = "pending";
350
+ Promise.resolve().then(() => (init_tracing(), tracing_exports)).then((mod) => {
351
+ _getTraceCtx = mod.getActiveTraceContext;
352
+ }).catch(() => {
353
+ _getTraceCtx = null;
354
+ });
355
+ return { traceId: "", spanId: "" };
356
+ }
357
+ var _getTraceCtx, SENSITIVE_KEYS, SENSITIVE_PATTERNS, SENSITIVE_KEYS_REGEX, Logger, logger_default;
358
+ var init_logger = __esm({
359
+ "src/shared/logger.ts"() {
360
+ _getTraceCtx = null;
361
+ SENSITIVE_KEYS = [
362
+ "password",
363
+ "passwd",
364
+ "pass",
365
+ "pwd",
366
+ "secret",
367
+ "secretkey",
368
+ "secret_key",
369
+ "secretaccesskey",
370
+ "accesskeyid",
371
+ "credentials",
372
+ "auth",
373
+ "authentication",
374
+ "login",
375
+ "api_key",
376
+ "apikey",
377
+ "api",
378
+ "access_key",
379
+ "access_token",
380
+ "accesskey",
381
+ "auth_key",
382
+ "auth_token",
383
+ "authkey",
384
+ "token",
385
+ "jwt",
386
+ "bearer_token",
387
+ "refresh_token",
388
+ "session_token",
389
+ "session_key",
390
+ "oauth_token",
391
+ "connection_string",
392
+ "conn_string",
393
+ "db_uri",
394
+ "db_url",
395
+ "database_url",
396
+ "mongodb_uri",
397
+ "mysql_uri",
398
+ "postgres_uri",
399
+ "sql_uri",
400
+ "db_username",
401
+ "db_password",
402
+ "db_host",
403
+ "db_port",
404
+ "db_name",
405
+ "encryption_key",
406
+ "crypto_key",
407
+ "private_key",
408
+ "public_key",
409
+ "ssl_key",
410
+ "ssh_key",
411
+ "pgp_key",
412
+ "rsa_key",
413
+ "aes_key",
414
+ "email",
415
+ "phone",
416
+ "telephone",
417
+ "mobile",
418
+ "ssn",
419
+ "social_security",
420
+ "credit_card",
421
+ "cc_number",
422
+ "card_number",
423
+ "cvv",
424
+ "expiry_date",
425
+ "birth_date",
426
+ "dob",
427
+ "address",
428
+ "zip_code",
429
+ "postal_code",
430
+ "bank_account",
431
+ "iban",
432
+ "swift_code",
433
+ "routing_number",
434
+ "tax_id",
435
+ "vat_number",
436
+ "financial_id",
437
+ "certificate",
438
+ "client_cert",
439
+ "server_cert",
440
+ "ca_cert",
441
+ "aws_key",
442
+ "aws_secret",
443
+ "azure_key",
444
+ "gcp_key",
445
+ "s3_key",
446
+ "cloudinary_key",
447
+ "stripe_key",
448
+ "paypal_key",
449
+ "twilio_key",
450
+ "app_secret",
451
+ "client_secret",
452
+ "consumer_secret",
453
+ "encryption_secret",
454
+ "master_key",
455
+ "root_password",
456
+ "admin_password",
457
+ "config_secret",
458
+ "env_secret",
459
+ "deploy_key",
460
+ "ci_key",
461
+ "session_id",
462
+ "cookie_secret",
463
+ "csrf_token",
464
+ "xsrf_token",
465
+ "license_key",
466
+ "product_key",
467
+ "serial_number",
468
+ "activation_code"
469
+ ];
470
+ SENSITIVE_PATTERNS = [
471
+ /(?:password|passwd|secret|api_key|access_token|auth_token|token)\s*[:=]\s*([^\s,]+)/gi,
472
+ /\b\d{16}\b/gi,
473
+ /\b\d{3}-\d{2}-\d{4}\b/gi,
474
+ /\b[A-Za-z0-9]{32}\b/gi,
475
+ /\b[A-Za-z0-9_-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b/gi,
476
+ /\b\d{10}\b/gi,
477
+ /\b[A-Za-z0-9]{64}\b/gi,
478
+ /(?:connection_string|db_uri|db_url|mongodb_uri)\s*[:=]\s*([^\s,]+)/gi,
479
+ /(?:apikey|api_key|auth_key)\s*[:=]\s*([^\s,]+)/gi,
480
+ /(?:bearer\s+)([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)/gi
481
+ ];
482
+ SENSITIVE_KEYS_REGEX = new RegExp(`^(${SENSITIVE_KEYS.join("|")})$`, "i");
483
+ Logger = class _Logger {
484
+ static instance = null;
485
+ static maskSensitiveData(value) {
486
+ if (typeof value === "string" && value.length > 5) {
487
+ const firstChar = value.at(0);
488
+ const lastChar = value.at(-1);
489
+ return firstChar + "*".repeat(value.length - 2) + lastChar;
490
+ }
491
+ return value;
492
+ }
493
+ static maskSensitive(message, args = []) {
494
+ const maskString = (input) => {
495
+ if (typeof input !== "string") return input;
496
+ return SENSITIVE_PATTERNS.reduce((str, pattern) => {
497
+ return str.replace(
498
+ pattern,
499
+ (match, value) => value ? match.replace(value, _Logger.maskSensitiveData(value)) : match
500
+ );
501
+ }, input);
502
+ };
503
+ const deepMask = (obj) => {
504
+ if (typeof obj === "string") return maskString(obj);
505
+ if (Array.isArray(obj)) return obj.map((item) => deepMask(item));
506
+ if (typeof obj === "object" && obj !== null) {
507
+ return Object.entries(obj).reduce(
508
+ (acc, [key, value]) => {
509
+ if (value === void 0) return acc;
510
+ const isSensitiveKey = SENSITIVE_KEYS_REGEX.test(key);
511
+ acc[key] = isSensitiveKey && typeof value === "string" ? _Logger.maskSensitiveData(value) : deepMask(value);
512
+ return acc;
513
+ },
514
+ {}
515
+ );
516
+ }
517
+ return obj;
518
+ };
519
+ return {
520
+ maskedMessage: maskString(message),
521
+ maskedArgs: args.map((arg) => deepMask(arg))
522
+ };
523
+ }
524
+ /** Global log directory: ~/.code-intel/logs */
525
+ static LOG_DIR = path32.join(os13.homedir(), ".code-intel", "logs");
526
+ static getLogger() {
527
+ if (!_Logger.instance) {
528
+ const isProduction = process.env.NODE_ENV === "production";
529
+ const logLevel = process.env.LOG_LEVEL ?? "info";
530
+ const transports = [];
531
+ transports.push(new winston.transports.Console());
532
+ if (!isProduction) {
533
+ try {
534
+ if (!fs26.existsSync(_Logger.LOG_DIR)) {
535
+ fs26.mkdirSync(_Logger.LOG_DIR, { recursive: true });
536
+ }
537
+ transports.push(
538
+ new DailyRotateFile({
539
+ filename: path32.join(_Logger.LOG_DIR, "%DATE%-code-intel.log"),
540
+ datePattern: "YYYY-MM-DD",
541
+ maxSize: "20m",
542
+ maxFiles: "14d"
543
+ })
544
+ );
545
+ } catch {
546
+ }
547
+ }
548
+ _Logger.instance = winston.createLogger({
549
+ level: logLevel,
550
+ format: winston.format.combine(
551
+ winston.format.timestamp(),
552
+ winston.format.printf(({ timestamp, level, message, ...meta }) => {
553
+ const args = meta[/* @__PURE__ */ Symbol.for("splat")] || [];
554
+ const { maskedMessage, maskedArgs } = _Logger.maskSensitive(message, args);
555
+ const formattedArgs = maskedArgs.map(
556
+ (arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
557
+ );
558
+ const suffix = formattedArgs.length ? " " + formattedArgs.join(" ") : "";
559
+ let traceCtx = "";
560
+ try {
561
+ const { traceId, spanId } = getActiveTraceCtx();
562
+ if (traceId) traceCtx = ` [trace=${traceId} span=${spanId}]`;
563
+ } catch {
564
+ }
565
+ return `${timestamp} [${level.toUpperCase()}]${traceCtx}: ${maskedMessage}${suffix}`;
566
+ })
567
+ ),
568
+ transports
569
+ });
570
+ }
571
+ return _Logger.instance;
572
+ }
573
+ static info(message, ...args) {
574
+ _Logger.getLogger().info(message, ...args);
575
+ }
576
+ static warn(message, ...args) {
577
+ _Logger.getLogger().warn(message, ...args);
578
+ }
579
+ static error(message, ...args) {
580
+ _Logger.getLogger().error(message, ...args);
581
+ }
582
+ static debug(message, ...args) {
583
+ _Logger.getLogger().debug(message, ...args);
584
+ }
585
+ };
586
+ logger_default = Logger;
587
+ Logger.getLogger();
588
+ }
589
+ });
590
+
591
+ // src/shared/languages.ts
592
+ var init_languages = __esm({
593
+ "src/shared/languages.ts"() {
594
+ }
595
+ });
596
+
597
+ // src/shared/detection.ts
598
+ function detectLanguage(filePath) {
599
+ const ext = filePath.slice(filePath.lastIndexOf("."));
600
+ return EXTENSION_MAP[ext] ?? null;
601
+ }
602
+ function getSupportedExtensions() {
603
+ return Object.keys(EXTENSION_MAP);
604
+ }
605
+ var EXTENSION_MAP;
606
+ var init_detection = __esm({
607
+ "src/shared/detection.ts"() {
608
+ init_languages();
609
+ EXTENSION_MAP = {
610
+ ".ts": "typescript" /* TypeScript */,
611
+ ".tsx": "typescript" /* TypeScript */,
612
+ ".mts": "typescript" /* TypeScript */,
613
+ ".cts": "typescript" /* TypeScript */,
614
+ ".js": "javascript" /* JavaScript */,
615
+ ".jsx": "javascript" /* JavaScript */,
616
+ ".mjs": "javascript" /* JavaScript */,
617
+ ".cjs": "javascript" /* JavaScript */,
618
+ ".py": "python" /* Python */,
619
+ ".pyi": "python" /* Python */,
620
+ ".java": "java" /* Java */,
621
+ ".go": "go" /* Go */,
622
+ ".c": "c" /* C */,
623
+ ".h": "c" /* C */,
624
+ ".cpp": "cpp" /* Cpp */,
625
+ ".cxx": "cpp" /* Cpp */,
626
+ ".cc": "cpp" /* Cpp */,
627
+ ".hpp": "cpp" /* Cpp */,
628
+ ".hxx": "cpp" /* Cpp */,
629
+ ".cs": "csharp" /* CSharp */,
630
+ ".rs": "rust" /* Rust */,
631
+ ".php": "php" /* PHP */,
632
+ ".kt": "kotlin" /* Kotlin */,
633
+ ".kts": "kotlin" /* Kotlin */,
634
+ ".rb": "ruby" /* Ruby */,
635
+ ".swift": "swift" /* Swift */,
636
+ ".dart": "dart" /* Dart */
637
+ };
638
+ }
639
+ });
640
+
641
+ // src/shared/index.ts
642
+ var init_shared = __esm({
643
+ "src/shared/index.ts"() {
644
+ init_languages();
645
+ init_detection();
646
+ }
647
+ });
648
+ function findBundledWasmDir() {
649
+ const fileDir = path32.dirname(fileURLToPath(import.meta.url));
650
+ const candidates = [
651
+ path32.join(fileDir, "wasm"),
652
+ // dist/index.js → dist/wasm/
653
+ path32.join(fileDir, "../wasm")
654
+ // dist/cli/main.js → dist/wasm/
655
+ ];
656
+ for (const candidate of candidates) {
657
+ if (existsSync(candidate)) return candidate;
658
+ }
659
+ return candidates[0];
660
+ }
661
+ function wasmPath(lang) {
662
+ const WASM_PACKAGE_MAP = {
663
+ ["typescript" /* TypeScript */]: "tree-sitter-typescript/tree-sitter-typescript.wasm",
664
+ ["javascript" /* JavaScript */]: "tree-sitter-javascript/tree-sitter-javascript.wasm",
665
+ ["python" /* Python */]: "tree-sitter-python/tree-sitter-python.wasm",
666
+ ["java" /* Java */]: "tree-sitter-java/tree-sitter-java.wasm",
667
+ ["go" /* Go */]: "tree-sitter-go/tree-sitter-go.wasm",
668
+ ["c" /* C */]: "tree-sitter-c/tree-sitter-c.wasm",
669
+ ["cpp" /* Cpp */]: "tree-sitter-cpp/tree-sitter-cpp.wasm",
670
+ ["csharp" /* CSharp */]: "tree-sitter-c-sharp/tree-sitter-c_sharp.wasm",
671
+ ["rust" /* Rust */]: "tree-sitter-rust/tree-sitter-rust.wasm",
672
+ ["php" /* PHP */]: "tree-sitter-php/tree-sitter-php.wasm",
673
+ ["ruby" /* Ruby */]: "tree-sitter-ruby/tree-sitter-ruby.wasm",
674
+ // These are optional dependencies; their packages may or may not include
675
+ // a WASM. If require.resolve fails we fall back to the bundled wasm/.
676
+ ["swift" /* Swift */]: "tree-sitter-swift/tree-sitter-swift.wasm",
677
+ ["kotlin" /* Kotlin */]: "tree-sitter-kotlin/tree-sitter-kotlin.wasm",
678
+ ["dart" /* Dart */]: "tree-sitter-dart/tree-sitter-dart.wasm"
679
+ };
680
+ const BUNDLED_WASM_MAP = {
681
+ ["swift" /* Swift */]: "tree-sitter-swift.wasm",
682
+ ["kotlin" /* Kotlin */]: "tree-sitter-kotlin.wasm",
683
+ ["dart" /* Dart */]: "tree-sitter-dart.wasm"
684
+ };
685
+ const relative = WASM_PACKAGE_MAP[lang];
686
+ if (relative) {
687
+ try {
688
+ return _require.resolve(relative);
689
+ } catch {
690
+ }
691
+ }
692
+ const bundled = BUNDLED_WASM_MAP[lang];
693
+ if (bundled) {
694
+ const bundledPath = path32.join(_bundledWasmDir, bundled);
695
+ if (existsSync(bundledPath)) return bundledPath;
696
+ }
697
+ return null;
698
+ }
699
+ async function initParser() {
700
+ if (!initPromise) {
701
+ initPromise = Parser.init();
702
+ }
703
+ return initPromise;
704
+ }
705
+ async function getLanguage(lang) {
706
+ if (languageCache.has(lang)) return languageCache.get(lang);
707
+ const path33 = wasmPath(lang);
708
+ if (!path33) {
709
+ languageCache.set(lang, null);
710
+ return null;
711
+ }
712
+ try {
713
+ await initParser();
714
+ const language = await Language.load(path33);
715
+ languageCache.set(lang, language);
716
+ return language;
717
+ } catch {
718
+ languageCache.set(lang, null);
719
+ return null;
720
+ }
721
+ }
722
+ async function getParser(lang) {
723
+ const language = await getLanguage(lang);
724
+ if (!language) return null;
725
+ let parser = parserCache.get(lang);
726
+ if (!parser) {
727
+ parser = new Parser();
728
+ parserCache.set(lang, parser);
729
+ }
730
+ parser.setLanguage(language);
731
+ return parser;
732
+ }
733
+ async function parseSource(lang, source) {
734
+ const parser = await getParser(lang);
735
+ if (!parser) return null;
736
+ return parser.parse(source);
737
+ }
738
+ async function isTreeSitterAvailable(lang) {
739
+ return await getLanguage(lang) !== null;
740
+ }
741
+ var _require, _bundledWasmDir, initPromise, languageCache, parserCache;
742
+ var init_parser_manager = __esm({
743
+ "src/parsing/parser-manager.ts"() {
744
+ init_shared();
745
+ _require = createRequire(import.meta.url);
746
+ _bundledWasmDir = findBundledWasmDir();
747
+ initPromise = null;
748
+ languageCache = /* @__PURE__ */ new Map();
749
+ parserCache = /* @__PURE__ */ new Map();
750
+ }
751
+ });
752
+ function getOrCompileQuery(language, querySource) {
753
+ let langMap = _queryCache.get(language);
754
+ if (!langMap) {
755
+ langMap = /* @__PURE__ */ new Map();
756
+ _queryCache.set(language, langMap);
757
+ }
758
+ let q = langMap.get(querySource);
759
+ if (!q) {
760
+ q = new Query(language, querySource);
761
+ langMap.set(querySource, q);
762
+ }
763
+ return q;
764
+ }
765
+ function runQuery(tree, language, querySource) {
766
+ const query = getOrCompileQuery(language, querySource);
767
+ const matches = query.matches(tree.rootNode);
768
+ const captures = [];
769
+ for (const match of matches) {
770
+ for (const capture of match.captures) {
771
+ captures.push({
772
+ name: capture.name,
773
+ node: capture.node,
774
+ text: capture.node.text
775
+ });
776
+ }
777
+ }
778
+ return captures;
779
+ }
780
+ function runQueryMatches(tree, language, querySource) {
781
+ const query = getOrCompileQuery(language, querySource);
782
+ const raw = query.matches(tree.rootNode);
783
+ return raw.map((m) => ({
784
+ patternIndex: m.patternIndex,
785
+ captures: m.captures.map((c) => ({
786
+ name: c.name,
787
+ node: c.node,
788
+ text: c.node.text
789
+ }))
790
+ }));
791
+ }
792
+ var _queryCache;
793
+ var init_query_runner = __esm({
794
+ "src/parsing/query-runner.ts"() {
795
+ _queryCache = /* @__PURE__ */ new WeakMap();
796
+ }
797
+ });
798
+
799
+ // src/parsing/queries/typescript.ts
800
+ var typescriptQueries;
801
+ var init_typescript = __esm({
802
+ "src/parsing/queries/typescript.ts"() {
803
+ typescriptQueries = `
804
+ ;; Class declaration
805
+ (class_declaration
806
+ name: (type_identifier) @def.class.name) @def.class
807
+
808
+ ;; Abstract class
809
+ (abstract_class_declaration
810
+ name: (type_identifier) @def.class.name) @def.class
811
+
812
+ ;; Interface declaration
813
+ (interface_declaration
814
+ name: (type_identifier) @def.interface.name) @def.interface
815
+
816
+ ;; Type alias
817
+ (type_alias_declaration
818
+ name: (type_identifier) @def.type_alias.name) @def.type_alias
819
+
820
+ ;; Enum
821
+ (enum_declaration
822
+ name: (identifier) @def.enum.name) @def.enum
823
+
824
+ ;; Function declaration
825
+ (function_declaration
826
+ name: (identifier) @def.func.name) @def.func
827
+
828
+ ;; Arrow function in variable
829
+ (lexical_declaration
830
+ (variable_declarator
831
+ name: (identifier) @def.func.name
832
+ value: (arrow_function))) @def.func
833
+
834
+ ;; Const/let/var variable (non-function)
835
+ (lexical_declaration
836
+ (variable_declarator
837
+ name: (identifier) @def.var.name
838
+ value: (_) @def.var.value)) @def.var
839
+
840
+ ;; Method definition
841
+ (method_definition
842
+ name: (property_identifier) @def.method.name) @def.method
843
+
844
+ ;; Public field definition
845
+ (public_field_definition
846
+ name: (property_identifier) @def.property.name) @def.property
847
+
848
+ ;; Import statement
849
+ (import_statement
850
+ source: (string) @imp.source) @imp
851
+
852
+ ;; Import specifier
853
+ (import_specifier
854
+ name: (identifier) @imp.name)
855
+
856
+ ;; Namespace import
857
+ (namespace_import (identifier) @imp.namespace)
858
+
859
+ ;; Export statement
860
+ (export_statement) @export
861
+
862
+ ;; Call expression \u2014 function call
863
+ (call_expression
864
+ function: (identifier) @call.name) @call
865
+
866
+ ;; Call expression \u2014 member call
867
+ (call_expression
868
+ function: (member_expression
869
+ object: (_) @call.receiver
870
+ property: (property_identifier) @call.method)) @call.member
871
+
872
+ ;; New expression
873
+ (new_expression
874
+ constructor: (identifier) @call.constructor) @call.new
875
+
876
+ ;; Class heritage \u2014 extends
877
+ (class_heritage
878
+ (extends_clause
879
+ value: (identifier) @inherit.extends))
880
+
881
+ ;; Class heritage \u2014 implements
882
+ (class_heritage
883
+ (implements_clause
884
+ (type_identifier) @inherit.implements))
885
+ `;
886
+ }
887
+ });
888
+
889
+ // src/parsing/queries/python.ts
890
+ var pythonQueries;
891
+ var init_python = __esm({
892
+ "src/parsing/queries/python.ts"() {
893
+ pythonQueries = `
894
+ ;; Class definition
375
895
  (class_definition
376
896
  name: (identifier) @def.class.name) @def.class
377
897
 
@@ -850,381 +1370,48 @@ var init_ruby = __esm({
850
1370
 
851
1371
  ;; Call
852
1372
  (call
853
- method: (identifier) @call.name) @call
854
-
855
- ;; Superclass
856
- (superclass
857
- (constant) @inherit.extends)
858
- `;
859
- }
860
- });
861
-
862
- // src/parsing/queries/swift.ts
863
- var swiftQueries;
864
- var init_swift = __esm({
865
- "src/parsing/queries/swift.ts"() {
866
- swiftQueries = `
867
- ;; In tree-sitter-swift, structs/classes/enums/actors all use class_declaration.
868
- ;; Distinguish them with a keyword anchor.
869
-
870
- ;; Struct declaration
871
- (class_declaration "struct"
872
- (type_identifier) @def.struct.name) @def.struct
873
-
874
- ;; Class declaration
875
- (class_declaration "class"
876
- (type_identifier) @def.class.name) @def.class
877
-
878
- ;; Enum declaration
879
- (class_declaration "enum"
880
- (type_identifier) @def.enum.name) @def.enum
881
-
882
- ;; Protocol declaration
883
- (protocol_declaration
884
- (type_identifier) @def.interface.name) @def.interface
885
-
886
- ;; Function declaration (no name: field in this grammar; positional match)
887
- (function_declaration
888
- (simple_identifier) @def.func.name) @def.func
889
-
890
- ;; Property declaration
891
- (property_declaration
892
- (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;
1373
+ method: (identifier) @call.name) @call
1374
+
1375
+ ;; Superclass
1376
+ (superclass
1377
+ (constant) @inherit.extends)
1378
+ `;
984
1379
  }
985
1380
  });
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(os13.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();
1381
+
1382
+ // src/parsing/queries/swift.ts
1383
+ var swiftQueries;
1384
+ var init_swift = __esm({
1385
+ "src/parsing/queries/swift.ts"() {
1386
+ swiftQueries = `
1387
+ ;; In tree-sitter-swift, structs/classes/enums/actors all use class_declaration.
1388
+ ;; Distinguish them with a keyword anchor.
1389
+
1390
+ ;; Struct declaration
1391
+ (class_declaration "struct"
1392
+ (type_identifier) @def.struct.name) @def.struct
1393
+
1394
+ ;; Class declaration
1395
+ (class_declaration "class"
1396
+ (type_identifier) @def.class.name) @def.class
1397
+
1398
+ ;; Enum declaration
1399
+ (class_declaration "enum"
1400
+ (type_identifier) @def.enum.name) @def.enum
1401
+
1402
+ ;; Protocol declaration
1403
+ (protocol_declaration
1404
+ (type_identifier) @def.interface.name) @def.interface
1405
+
1406
+ ;; Function declaration (no name: field in this grammar; positional match)
1407
+ (function_declaration
1408
+ (simple_identifier) @def.func.name) @def.func
1409
+
1410
+ ;; Property declaration
1411
+ (property_declaration
1412
+ (pattern
1413
+ (simple_identifier) @def.property.name)) @def.property
1414
+ `;
1228
1415
  }
1229
1416
  });
1230
1417
 
@@ -1961,7 +2148,7 @@ var init_parse_phase = __esm({
1961
2148
  const batch = filePaths.slice(i, i + CONCURRENCY);
1962
2149
  await Promise.all(batch.map(async (filePath) => {
1963
2150
  try {
1964
- const source = await fs24.promises.readFile(filePath, "utf-8");
2151
+ const source = await fs26.promises.readFile(filePath, "utf-8");
1965
2152
  context2.fileCache.set(filePath, source);
1966
2153
  } catch {
1967
2154
  }
@@ -1974,14 +2161,14 @@ var init_parse_phase = __esm({
1974
2161
  const lang = detectLanguage(filePath);
1975
2162
  if (!lang) {
1976
2163
  if (context2.verbose) {
1977
- const relativePath2 = path31.relative(context2.workspaceRoot, filePath);
2164
+ const relativePath2 = path32.relative(context2.workspaceRoot, filePath);
1978
2165
  logger_default.info(` [parse] skipped (no parser): ${relativePath2}`);
1979
2166
  }
1980
2167
  continue;
1981
2168
  }
1982
2169
  const source = context2.fileCache.get(filePath);
1983
2170
  if (!source) continue;
1984
- const relativePath = path31.relative(context2.workspaceRoot, filePath);
2171
+ const relativePath = path32.relative(context2.workspaceRoot, filePath);
1985
2172
  const fileNodeId = generateNodeId("file", relativePath, relativePath);
1986
2173
  const fileNode = context2.graph.getNode(fileNodeId);
1987
2174
  if (fileNode) {
@@ -2221,11 +2408,11 @@ var init_resolve_phase = __esm({
2221
2408
  let heritageEdges = 0;
2222
2409
  const fileIndex = /* @__PURE__ */ new Map();
2223
2410
  for (const fp of filePaths) {
2224
- const rel = path31.relative(workspaceRoot, fp);
2411
+ const rel = path32.relative(workspaceRoot, fp);
2225
2412
  fileIndex.set(rel, fp);
2226
2413
  const noExt = rel.replace(/\.\w+$/, "");
2227
2414
  if (!fileIndex.has(noExt)) fileIndex.set(noExt, fp);
2228
- const base = path31.basename(rel, path31.extname(rel));
2415
+ const base = path32.basename(rel, path32.extname(rel));
2229
2416
  if (!fileIndex.has(base)) fileIndex.set(base, fp);
2230
2417
  }
2231
2418
  const symbolIndex = /* @__PURE__ */ new Map();
@@ -2256,7 +2443,7 @@ var init_resolve_phase = __esm({
2256
2443
  for (const filePath of filePaths) {
2257
2444
  const lang = detectLanguage(filePath);
2258
2445
  if (!lang) continue;
2259
- const relativePath = path31.relative(workspaceRoot, filePath);
2446
+ const relativePath = path32.relative(workspaceRoot, filePath);
2260
2447
  const fileNodeId = generateNodeId("file", relativePath, relativePath);
2261
2448
  const source = fileCache.get(filePath);
2262
2449
  if (!source) continue;
@@ -2269,13 +2456,13 @@ var init_resolve_phase = __esm({
2269
2456
  let resolvedRelPath = null;
2270
2457
  if (cleaned.startsWith(".")) {
2271
2458
  const cleanedNoJs = cleaned.replace(/\.(js|jsx)$/, "");
2272
- const fromDir = path31.dirname(relativePath);
2459
+ const fromDir = path32.dirname(relativePath);
2273
2460
  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);
2461
+ const candidate = path32.join(fromDir, cleanedNoJs + ext);
2462
+ const normalized = path32.normalize(candidate);
2276
2463
  if (fileIndex.has(normalized)) {
2277
2464
  const absPath = fileIndex.get(normalized);
2278
- resolvedRelPath = path31.relative(workspaceRoot, absPath);
2465
+ resolvedRelPath = path32.relative(workspaceRoot, absPath);
2279
2466
  break;
2280
2467
  }
2281
2468
  }
@@ -2575,6 +2762,65 @@ var init_embedder = __esm({
2575
2762
  }
2576
2763
  });
2577
2764
 
2765
+ // src/storage/db-manager.ts
2766
+ var db_manager_exports = {};
2767
+ __export(db_manager_exports, {
2768
+ DbManager: () => DbManager
2769
+ });
2770
+ var DbManager;
2771
+ var init_db_manager = __esm({
2772
+ "src/storage/db-manager.ts"() {
2773
+ DbManager = class {
2774
+ db = null;
2775
+ conn = null;
2776
+ dbPath;
2777
+ readOnly;
2778
+ constructor(dbPath, readOnly = false) {
2779
+ this.dbPath = dbPath;
2780
+ this.readOnly = readOnly;
2781
+ }
2782
+ async init() {
2783
+ if (!this.readOnly) {
2784
+ fs26.mkdirSync(path32.dirname(this.dbPath), { recursive: true });
2785
+ }
2786
+ this.db = new Database(this.dbPath, 0, true, this.readOnly);
2787
+ await this.db.init();
2788
+ this.conn = new Connection(this.db);
2789
+ await this.conn.init();
2790
+ }
2791
+ async query(cypher) {
2792
+ if (!this.conn) throw new Error("Database not initialized");
2793
+ const result = await this.conn.query(cypher);
2794
+ const qr = Array.isArray(result) ? result[0] : result;
2795
+ const rows = await qr.getAll();
2796
+ qr.close();
2797
+ return rows;
2798
+ }
2799
+ async execute(cypher) {
2800
+ if (!this.conn) throw new Error("Database not initialized");
2801
+ const result = await this.conn.query(cypher);
2802
+ const qr = Array.isArray(result) ? result[0] : result;
2803
+ qr.close();
2804
+ }
2805
+ close() {
2806
+ try {
2807
+ this.conn?.closeSync();
2808
+ } catch {
2809
+ }
2810
+ try {
2811
+ this.db?.closeSync();
2812
+ } catch {
2813
+ }
2814
+ this.conn = null;
2815
+ this.db = null;
2816
+ }
2817
+ get isOpen() {
2818
+ return this.conn !== null;
2819
+ }
2820
+ };
2821
+ }
2822
+ });
2823
+
2578
2824
  // src/multi-repo/group-registry.ts
2579
2825
  var group_registry_exports = {};
2580
2826
  __export(group_registry_exports, {
@@ -2589,27 +2835,27 @@ __export(group_registry_exports, {
2589
2835
  saveSyncResult: () => saveSyncResult
2590
2836
  });
2591
2837
  function groupFile(name) {
2592
- return path31.join(GROUPS_DIR, `${name}.json`);
2838
+ return path32.join(GROUPS_DIR, `${name}.json`);
2593
2839
  }
2594
2840
  function loadGroup(name) {
2595
2841
  try {
2596
- return JSON.parse(fs24.readFileSync(groupFile(name), "utf-8"));
2842
+ return JSON.parse(fs26.readFileSync(groupFile(name), "utf-8"));
2597
2843
  } catch {
2598
2844
  return null;
2599
2845
  }
2600
2846
  }
2601
2847
  function saveGroup(group) {
2602
- fs24.mkdirSync(GROUPS_DIR, { recursive: true });
2603
- fs24.writeFileSync(groupFile(group.name), JSON.stringify(group, null, 2) + "\n");
2848
+ fs26.mkdirSync(GROUPS_DIR, { recursive: true });
2849
+ fs26.writeFileSync(groupFile(group.name), JSON.stringify(group, null, 2) + "\n");
2604
2850
  }
2605
2851
  function listGroups() {
2606
2852
  const groups = [];
2607
2853
  try {
2608
- for (const file of fs24.readdirSync(GROUPS_DIR)) {
2854
+ for (const file of fs26.readdirSync(GROUPS_DIR)) {
2609
2855
  if (!file.endsWith(".json") || file.endsWith(".sync.json")) continue;
2610
2856
  try {
2611
2857
  const g = JSON.parse(
2612
- fs24.readFileSync(path31.join(GROUPS_DIR, file), "utf-8")
2858
+ fs26.readFileSync(path32.join(GROUPS_DIR, file), "utf-8")
2613
2859
  );
2614
2860
  groups.push(g);
2615
2861
  } catch {
@@ -2621,16 +2867,16 @@ function listGroups() {
2621
2867
  }
2622
2868
  function deleteGroup(name) {
2623
2869
  try {
2624
- fs24.unlinkSync(groupFile(name));
2870
+ fs26.unlinkSync(groupFile(name));
2625
2871
  } catch {
2626
2872
  }
2627
2873
  try {
2628
- fs24.unlinkSync(path31.join(GROUPS_DIR, `${name}.sync.json`));
2874
+ fs26.unlinkSync(path32.join(GROUPS_DIR, `${name}.sync.json`));
2629
2875
  } catch {
2630
2876
  }
2631
2877
  }
2632
2878
  function groupExists(name) {
2633
- return fs24.existsSync(groupFile(name));
2879
+ return fs26.existsSync(groupFile(name));
2634
2880
  }
2635
2881
  function addMember(groupName, member) {
2636
2882
  const group = loadGroup(groupName);
@@ -2656,16 +2902,16 @@ function removeMember(groupName, groupPath) {
2656
2902
  return group;
2657
2903
  }
2658
2904
  function saveSyncResult(result) {
2659
- fs24.mkdirSync(GROUPS_DIR, { recursive: true });
2660
- fs24.writeFileSync(
2661
- path31.join(GROUPS_DIR, `${result.groupName}.sync.json`),
2905
+ fs26.mkdirSync(GROUPS_DIR, { recursive: true });
2906
+ fs26.writeFileSync(
2907
+ path32.join(GROUPS_DIR, `${result.groupName}.sync.json`),
2662
2908
  JSON.stringify(result, null, 2) + "\n"
2663
2909
  );
2664
2910
  }
2665
2911
  function loadSyncResult(groupName) {
2666
2912
  try {
2667
2913
  return JSON.parse(
2668
- fs24.readFileSync(path31.join(GROUPS_DIR, `${groupName}.sync.json`), "utf-8")
2914
+ fs26.readFileSync(path32.join(GROUPS_DIR, `${groupName}.sync.json`), "utf-8")
2669
2915
  );
2670
2916
  } catch {
2671
2917
  return null;
@@ -2674,7 +2920,87 @@ function loadSyncResult(groupName) {
2674
2920
  var GROUPS_DIR;
2675
2921
  var init_group_registry = __esm({
2676
2922
  "src/multi-repo/group-registry.ts"() {
2677
- GROUPS_DIR = path31.join(os13.homedir(), ".code-intel", "groups");
2923
+ GROUPS_DIR = path32.join(os13.homedir(), ".code-intel", "groups");
2924
+ }
2925
+ });
2926
+
2927
+ // src/multi-repo/graph-from-db.ts
2928
+ var graph_from_db_exports = {};
2929
+ __export(graph_from_db_exports, {
2930
+ loadGraphFromDB: () => loadGraphFromDB
2931
+ });
2932
+ function parseRow(row, kind) {
2933
+ return {
2934
+ id: String(row["id"] ?? ""),
2935
+ kind,
2936
+ name: String(row["name"] ?? ""),
2937
+ filePath: String(row["file_path"] ?? ""),
2938
+ startLine: row["start_line"] != null ? Number(row["start_line"]) : void 0,
2939
+ endLine: row["end_line"] != null ? Number(row["end_line"]) : void 0,
2940
+ exported: row["exported"] != null ? Boolean(row["exported"]) : void 0,
2941
+ content: row["content"] ? String(row["content"]) : void 0,
2942
+ metadata: row["metadata"] ? (() => {
2943
+ try {
2944
+ return JSON.parse(String(row["metadata"]));
2945
+ } catch {
2946
+ return void 0;
2947
+ }
2948
+ })() : void 0
2949
+ };
2950
+ }
2951
+ async function loadGraphFromDB(graph, db) {
2952
+ for (const table of ALL_NODE_TABLES) {
2953
+ const kind = TABLE_TO_KIND2[table];
2954
+ if (!kind) continue;
2955
+ let rows = [];
2956
+ try {
2957
+ rows = await db.query(`MATCH (n:${table}) RETURN n.id, n.name, n.file_path, n.start_line, n.end_line, n.exported, n.content, n.metadata`);
2958
+ } catch {
2959
+ continue;
2960
+ }
2961
+ for (const row of rows) {
2962
+ const node = parseRow({
2963
+ id: row["n.id"],
2964
+ name: row["n.name"],
2965
+ file_path: row["n.file_path"],
2966
+ start_line: row["n.start_line"],
2967
+ end_line: row["n.end_line"],
2968
+ exported: row["n.exported"],
2969
+ content: row["n.content"],
2970
+ metadata: row["n.metadata"]
2971
+ }, kind);
2972
+ if (node.id && node.name) graph.addNode(node);
2973
+ }
2974
+ }
2975
+ try {
2976
+ const edgeRows = await db.query(
2977
+ `MATCH (a)-[e:code_edges]->(b) RETURN a.id, b.id, e.kind, e.weight, e.label`
2978
+ );
2979
+ for (const row of edgeRows) {
2980
+ const sourceId = String(row["a.id"] ?? "");
2981
+ const targetId = String(row["b.id"] ?? "");
2982
+ const kind = String(row["e.kind"] ?? "");
2983
+ if (!sourceId || !targetId || !kind) continue;
2984
+ const edge = {
2985
+ id: `${sourceId}::${kind}::${targetId}`,
2986
+ source: sourceId,
2987
+ target: targetId,
2988
+ kind,
2989
+ weight: row["e.weight"] != null ? Number(row["e.weight"]) : void 0,
2990
+ label: row["e.label"] ? String(row["e.label"]) : void 0
2991
+ };
2992
+ graph.addEdge(edge);
2993
+ }
2994
+ } catch {
2995
+ }
2996
+ }
2997
+ var TABLE_TO_KIND2;
2998
+ var init_graph_from_db = __esm({
2999
+ "src/multi-repo/graph-from-db.ts"() {
3000
+ init_schema();
3001
+ TABLE_TO_KIND2 = Object.fromEntries(
3002
+ Object.entries(NODE_TABLE_MAP).map(([kind, table]) => [table, kind])
3003
+ );
2678
3004
  }
2679
3005
  });
2680
3006
 
@@ -2964,7 +3290,7 @@ __export(gql_parser_exports, {
2964
3290
  function isGQLParseError(v) {
2965
3291
  return v.type === "GQLParseError";
2966
3292
  }
2967
- function tokenize(input) {
3293
+ function tokenize2(input) {
2968
3294
  const tokens = [];
2969
3295
  let i = 0;
2970
3296
  const len = input.length;
@@ -3059,7 +3385,7 @@ function tokenize(input) {
3059
3385
  return tokens;
3060
3386
  }
3061
3387
  function parseGQL(input) {
3062
- const tokens = tokenize(input.trim());
3388
+ const tokens = tokenize2(input.trim());
3063
3389
  if (!Array.isArray(tokens)) return tokens;
3064
3390
  const parser = new Parser2(tokens);
3065
3391
  return parser.parse();
@@ -3852,7 +4178,7 @@ function isTestFile(filePath) {
3852
4178
  if (filePath.includes(".test.") || filePath.includes(".spec.")) return true;
3853
4179
  if (filePath.includes("_test.") || filePath.endsWith("_test.go")) return true;
3854
4180
  if (filePath.includes("__tests__")) return true;
3855
- const base = path31.basename(filePath);
4181
+ const base = path32.basename(filePath);
3856
4182
  if (base.startsWith("Test") && filePath.endsWith(".java")) return true;
3857
4183
  return false;
3858
4184
  }
@@ -3897,7 +4223,7 @@ function computeCoverage(graph, scope) {
3897
4223
  }
3898
4224
  const baseNameToTestFiles = /* @__PURE__ */ new Map();
3899
4225
  for (const testPath of testFilePaths) {
3900
- const base = path31.basename(testPath);
4226
+ const base = path32.basename(testPath);
3901
4227
  const stripped = base.replace(/\.test\.[^.]+$/, "").replace(/\.spec\.[^.]+$/, "").replace(/_test\.[^.]+$/, "").replace(/_test$/, "");
3902
4228
  const existing = baseNameToTestFiles.get(stripped) ?? [];
3903
4229
  existing.push(testPath);
@@ -3930,7 +4256,7 @@ function computeCoverage(graph, scope) {
3930
4256
  }
3931
4257
  }
3932
4258
  }
3933
- const nodeBase = path31.basename(node.filePath).replace(/\.[^.]+$/, "");
4259
+ const nodeBase = path32.basename(node.filePath).replace(/\.[^.]+$/, "");
3934
4260
  const matchingTestFiles = baseNameToTestFiles.get(nodeBase) ?? [];
3935
4261
  for (const tf of matchingTestFiles) {
3936
4262
  if (!testFiles.includes(tf)) testFiles.push(tf);
@@ -4000,7 +4326,7 @@ var init_secret_scanner = __esm({
4000
4326
  const ignorePatterns = [...options?.ignorePatterns ?? []];
4001
4327
  if (options?.workspaceRoot) {
4002
4328
  try {
4003
- const raw = fs24.readFileSync(path31.join(options.workspaceRoot, ".codeintelignore"), "utf-8");
4329
+ const raw = fs26.readFileSync(path32.join(options.workspaceRoot, ".codeintelignore"), "utf-8");
4004
4330
  for (const line of raw.split("\n")) {
4005
4331
  const trimmed = line.trim();
4006
4332
  if (trimmed && !trimmed.startsWith("#")) ignorePatterns.push(trimmed);
@@ -4288,10 +4614,10 @@ var init_codes = __esm({
4288
4614
  }
4289
4615
  });
4290
4616
  function secureMkdir(dir) {
4291
- fs24.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
4617
+ fs26.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
4292
4618
  if (process.platform !== "win32") {
4293
4619
  try {
4294
- fs24.chmodSync(dir, SECURE_DIR_MODE);
4620
+ fs26.chmodSync(dir, SECURE_DIR_MODE);
4295
4621
  } catch {
4296
4622
  }
4297
4623
  }
@@ -4299,17 +4625,17 @@ function secureMkdir(dir) {
4299
4625
  function secureChmodFile(file) {
4300
4626
  if (process.platform === "win32") return;
4301
4627
  try {
4302
- fs24.chmodSync(file, SECURE_FILE_MODE);
4628
+ fs26.chmodSync(file, SECURE_FILE_MODE);
4303
4629
  } catch {
4304
4630
  }
4305
4631
  }
4306
4632
  function tightenDbFiles(dir) {
4307
4633
  if (process.platform === "win32") return;
4308
- if (!fs24.existsSync(dir)) return;
4309
- for (const name of fs24.readdirSync(dir)) {
4634
+ if (!fs26.existsSync(dir)) return;
4635
+ for (const name of fs26.readdirSync(dir)) {
4310
4636
  if (name.endsWith(".db") || name.endsWith(".db-wal") || name.endsWith(".db-shm")) {
4311
4637
  try {
4312
- fs24.chmodSync(path31.join(dir, name), SECURE_FILE_MODE);
4638
+ fs26.chmodSync(path32.join(dir, name), SECURE_FILE_MODE);
4313
4639
  } catch {
4314
4640
  }
4315
4641
  }
@@ -4323,7 +4649,7 @@ var init_fs_secure = __esm({
4323
4649
  }
4324
4650
  });
4325
4651
  function getUsersDBPath() {
4326
- return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path31.join(os13.homedir(), ".code-intel", "users.db");
4652
+ return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path32.join(os13.homedir(), ".code-intel", "users.db");
4327
4653
  }
4328
4654
  function getOrCreateUsersDB() {
4329
4655
  if (!_usersDB) {
@@ -4339,9 +4665,9 @@ var init_users_db = __esm({
4339
4665
  UsersDB = class {
4340
4666
  db;
4341
4667
  constructor(dbPath) {
4342
- const dir = path31.dirname(dbPath);
4668
+ const dir = path32.dirname(dbPath);
4343
4669
  secureMkdir(dir);
4344
- this.db = new Database3(dbPath);
4670
+ this.db = new Database2(dbPath);
4345
4671
  this.db.pragma("journal_mode = WAL");
4346
4672
  this.db.pragma("foreign_keys = ON");
4347
4673
  this.createTables();
@@ -4616,7 +4942,7 @@ function getScryptN() {
4616
4942
  return Number.isInteger(v) && v >= 1024 ? v : 1 << 14;
4617
4943
  }
4618
4944
  function getSecretsPath() {
4619
- return process.env["CODE_INTEL_SECRETS_PATH"] ?? path31.join(os13.homedir(), ".code-intel", ".secrets");
4945
+ return process.env["CODE_INTEL_SECRETS_PATH"] ?? path32.join(os13.homedir(), ".code-intel", ".secrets");
4620
4946
  }
4621
4947
  function getMasterPassword() {
4622
4948
  const fromEnv = process.env["CODE_INTEL_SECRET_KEY"];
@@ -4647,8 +4973,8 @@ function decryptSecrets(encrypted) {
4647
4973
  return JSON.parse(plaintext.toString("utf8"));
4648
4974
  }
4649
4975
  function loadSecrets(secretsPath = getSecretsPath()) {
4650
- if (!fs24.existsSync(secretsPath)) return {};
4651
- const blob = fs24.readFileSync(secretsPath);
4976
+ if (!fs26.existsSync(secretsPath)) return {};
4977
+ const blob = fs26.readFileSync(secretsPath);
4652
4978
  return decryptSecrets(blob);
4653
4979
  }
4654
4980
  function getSecret(key, secretsPath = getSecretsPath()) {
@@ -4669,11 +4995,18 @@ function getSessionTtlMs() {
4669
4995
  const hours = parseInt(process.env["CODE_INTEL_SESSION_TTL_HOURS"] ?? "8", 10);
4670
4996
  return (isNaN(hours) ? 8 : hours) * 60 * 60 * 1e3;
4671
4997
  }
4672
- function createSession(user) {
4998
+ function createSession(user, rememberMe = false) {
4673
4999
  const sessionId = v4();
4674
- const expiresAt = Date.now() + getSessionTtlMs();
4675
- sessionStore.set(sessionId, { userId: user.id, username: user.username, role: user.role, expiresAt });
4676
- return sessionId;
5000
+ const ttlMs = rememberMe ? REMEMBER_ME_TTL_MS : getSessionTtlMs();
5001
+ const expiresAt = Date.now() + ttlMs;
5002
+ sessionStore.set(sessionId, {
5003
+ userId: user.id,
5004
+ username: user.username,
5005
+ role: user.role,
5006
+ expiresAt,
5007
+ ttlMs
5008
+ });
5009
+ return { sessionId, ttlMs };
4677
5010
  }
4678
5011
  function getSession(sessionId) {
4679
5012
  const entry = sessionStore.get(sessionId);
@@ -4682,10 +5015,9 @@ function getSession(sessionId) {
4682
5015
  sessionStore.delete(sessionId);
4683
5016
  return null;
4684
5017
  }
4685
- const ttlMs = getSessionTtlMs();
4686
5018
  const remaining = entry.expiresAt - Date.now();
4687
- if (remaining < ttlMs * 0.75) {
4688
- entry.expiresAt = Date.now() + ttlMs;
5019
+ if (remaining < entry.ttlMs * 0.75) {
5020
+ entry.expiresAt = Date.now() + entry.ttlMs;
4689
5021
  }
4690
5022
  return entry;
4691
5023
  }
@@ -4705,7 +5037,7 @@ function authMiddleware(req, res, next) {
4705
5037
  const session = getSession(sessionId);
4706
5038
  if (session) {
4707
5039
  req.user = { id: session.userId, username: session.username, role: session.role, authMethod: "session" };
4708
- res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
5040
+ res.setHeader("Set-Cookie", buildSessionCookie(sessionId, session.ttlMs));
4709
5041
  next();
4710
5042
  return;
4711
5043
  }
@@ -4875,9 +5207,9 @@ function parseCookies(cookieHeader) {
4875
5207
  }
4876
5208
  return result;
4877
5209
  }
4878
- function buildSessionCookie(sessionId) {
5210
+ function buildSessionCookie(sessionId, ttlMs) {
4879
5211
  const isProduction = process.env["NODE_ENV"] === "production";
4880
- const maxAge = Math.floor(getSessionTtlMs() / 1e3);
5212
+ const maxAge = Math.floor((ttlMs ?? getSessionTtlMs()) / 1e3);
4881
5213
  const parts = [
4882
5214
  `${SESSION_COOKIE_NAME}=${encodeURIComponent(sessionId)}`,
4883
5215
  `HttpOnly`,
@@ -4894,7 +5226,7 @@ function clearSessionCookie() {
4894
5226
  async function verifyPassword(plain, hash) {
4895
5227
  return bcrypt.compare(plain, hash);
4896
5228
  }
4897
- var sessionStore, SESSION_COOKIE_NAME, ROLE_RANK;
5229
+ var sessionStore, SESSION_COOKIE_NAME, REMEMBER_ME_TTL_MS, ROLE_RANK;
4898
5230
  var init_middleware = __esm({
4899
5231
  "src/auth/middleware.ts"() {
4900
5232
  init_users_db();
@@ -4902,6 +5234,7 @@ var init_middleware = __esm({
4902
5234
  init_secret_store();
4903
5235
  sessionStore = /* @__PURE__ */ new Map();
4904
5236
  SESSION_COOKIE_NAME = "code_intel_session";
5237
+ REMEMBER_ME_TTL_MS = 12 * 60 * 60 * 1e3;
4905
5238
  ROLE_RANK = {
4906
5239
  viewer: 1,
4907
5240
  "repo-owner": 2,
@@ -5034,130 +5367,23 @@ var init_websocket_server = __esm({
5034
5367
  }
5035
5368
  });
5036
5369
 
5037
- // src/graph/knowledge-graph.ts
5038
- function createKnowledgeGraph() {
5039
- const nodes = /* @__PURE__ */ new Map();
5040
- const edges = /* @__PURE__ */ new Map();
5041
- const edgesByKind = /* @__PURE__ */ new Map();
5042
- const edgesFromNode = /* @__PURE__ */ new Map();
5043
- const edgesToNode = /* @__PURE__ */ new Map();
5044
- function indexEdge(edge) {
5045
- let kindSet = edgesByKind.get(edge.kind);
5046
- if (!kindSet) {
5047
- kindSet = /* @__PURE__ */ new Set();
5048
- edgesByKind.set(edge.kind, kindSet);
5049
- }
5050
- kindSet.add(edge.id);
5051
- let fromSet = edgesFromNode.get(edge.source);
5052
- if (!fromSet) {
5053
- fromSet = /* @__PURE__ */ new Set();
5054
- edgesFromNode.set(edge.source, fromSet);
5055
- }
5056
- fromSet.add(edge.id);
5057
- let toSet = edgesToNode.get(edge.target);
5058
- if (!toSet) {
5059
- toSet = /* @__PURE__ */ new Set();
5060
- edgesToNode.set(edge.target, toSet);
5061
- }
5062
- toSet.add(edge.id);
5063
- }
5064
- function unindexEdge(edge) {
5065
- edgesByKind.get(edge.kind)?.delete(edge.id);
5066
- edgesFromNode.get(edge.source)?.delete(edge.id);
5067
- edgesToNode.get(edge.target)?.delete(edge.id);
5068
- }
5069
- return {
5070
- addNode(node) {
5071
- nodes.set(node.id, node);
5072
- },
5073
- addEdge(edge) {
5074
- edges.set(edge.id, edge);
5075
- indexEdge(edge);
5076
- },
5077
- getNode(id) {
5078
- return nodes.get(id);
5079
- },
5080
- getEdge(id) {
5081
- return edges.get(id);
5082
- },
5083
- *findEdgesByKind(kind) {
5084
- const ids = edgesByKind.get(kind);
5085
- if (!ids) return;
5086
- for (const id of ids) {
5087
- const edge = edges.get(id);
5088
- if (edge) yield edge;
5089
- }
5090
- },
5091
- *findEdgesFrom(sourceId) {
5092
- const ids = edgesFromNode.get(sourceId);
5093
- if (!ids) return;
5094
- for (const id of ids) {
5095
- const edge = edges.get(id);
5096
- if (edge) yield edge;
5097
- }
5098
- },
5099
- *findEdgesTo(targetId) {
5100
- const ids = edgesToNode.get(targetId);
5101
- if (!ids) return;
5102
- for (const id of ids) {
5103
- const edge = edges.get(id);
5104
- if (edge) yield edge;
5105
- }
5106
- },
5107
- removeNodeCascade(id) {
5108
- const fromEdges = edgesFromNode.get(id);
5109
- if (fromEdges) {
5110
- for (const edgeId of [...fromEdges]) {
5111
- const edge = edges.get(edgeId);
5112
- if (edge) {
5113
- unindexEdge(edge);
5114
- edges.delete(edgeId);
5115
- }
5116
- }
5117
- }
5118
- const toEdges = edgesToNode.get(id);
5119
- if (toEdges) {
5120
- for (const edgeId of [...toEdges]) {
5121
- const edge = edges.get(edgeId);
5122
- if (edge) {
5123
- unindexEdge(edge);
5124
- edges.delete(edgeId);
5125
- }
5126
- }
5127
- }
5128
- edgesFromNode.delete(id);
5129
- edgesToNode.delete(id);
5130
- nodes.delete(id);
5131
- },
5132
- removeEdge(id) {
5133
- const edge = edges.get(id);
5134
- if (edge) {
5135
- unindexEdge(edge);
5136
- edges.delete(id);
5137
- }
5138
- },
5139
- *allNodes() {
5140
- yield* nodes.values();
5141
- },
5142
- *allEdges() {
5143
- yield* edges.values();
5144
- },
5145
- get size() {
5146
- return { nodes: nodes.size, edges: edges.size };
5147
- },
5148
- clear() {
5149
- nodes.clear();
5150
- edges.clear();
5151
- edgesByKind.clear();
5152
- edgesFromNode.clear();
5153
- edgesToNode.clear();
5154
- }
5155
- };
5156
- }
5157
-
5158
5370
  // src/graph/index.ts
5371
+ init_knowledge_graph();
5159
5372
  init_id_generator();
5160
5373
 
5374
+ // src/graph/lazy-knowledge-graph.ts
5375
+ init_schema();
5376
+ init_logger();
5377
+ Object.fromEntries(
5378
+ Object.entries(NODE_TABLE_MAP).map(([kind, table]) => [table, kind])
5379
+ );
5380
+ function isLazyGraph(g) {
5381
+ return "lazy" in g && g.lazy === true;
5382
+ }
5383
+
5384
+ // src/graph/compact-knowledge-graph.ts
5385
+ init_logger();
5386
+
5161
5387
  // src/parsing/index.ts
5162
5388
  init_parser_manager();
5163
5389
  init_query_runner();
@@ -5215,7 +5441,7 @@ init_shared();
5215
5441
  init_shared();
5216
5442
  init_typescript();
5217
5443
  function resolveRelative(rawPath, fromFile, workspace) {
5218
- const fromDir = path31.dirname(fromFile);
5444
+ const fromDir = path32.dirname(fromFile);
5219
5445
  const cleaned = rawPath.replace(/['"]/g, "");
5220
5446
  const extensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.js"];
5221
5447
  const resolved = workspace.resolve(fromDir, cleaned);
@@ -5267,7 +5493,7 @@ var pythonModule = {
5267
5493
  resolveImport(rawPath, fromFile, workspace) {
5268
5494
  const cleaned = rawPath.replace(/['"]/g, "");
5269
5495
  const parts = cleaned.split(".");
5270
- const fromDir = path31.dirname(fromFile);
5496
+ const fromDir = path32.dirname(fromFile);
5271
5497
  const relPath = parts.join("/");
5272
5498
  for (const suffix of ["/__init__.py", ".py"]) {
5273
5499
  const r = workspace.resolve(fromDir, relPath + suffix);
@@ -5346,7 +5572,7 @@ var cModule = {
5346
5572
  inheritanceStrategy: "none",
5347
5573
  resolveImport(rawPath, fromFile, workspace) {
5348
5574
  const cleaned = rawPath.replace(/[<>"']/g, "");
5349
- const fromDir = path31.dirname(fromFile);
5575
+ const fromDir = path32.dirname(fromFile);
5350
5576
  return workspace.resolve(fromDir, cleaned);
5351
5577
  },
5352
5578
  isExported(_node) {
@@ -5369,7 +5595,7 @@ var cppModule = {
5369
5595
  inheritanceStrategy: "depth-first",
5370
5596
  resolveImport(rawPath, fromFile, workspace) {
5371
5597
  const cleaned = rawPath.replace(/[<>"']/g, "");
5372
- const fromDir = path31.dirname(fromFile);
5598
+ const fromDir = path32.dirname(fromFile);
5373
5599
  return workspace.resolve(fromDir, cleaned);
5374
5600
  },
5375
5601
  isExported(_node) {
@@ -5531,7 +5757,7 @@ var dartModule = {
5531
5757
  const pkg = cleaned.replace("package:", "");
5532
5758
  return workspace.findByPackage(pkg);
5533
5759
  }
5534
- const fromDir = path31.dirname(fromFile);
5760
+ const fromDir = path32.dirname(fromFile);
5535
5761
  return workspace.resolve(fromDir, cleaned);
5536
5762
  },
5537
5763
  isExported(node) {
@@ -5886,25 +6112,25 @@ function validateDAG(phases) {
5886
6112
  const visiting = /* @__PURE__ */ new Set();
5887
6113
  const visited = /* @__PURE__ */ new Set();
5888
6114
  const phaseMap = new Map(phases.map((p) => [p.name, p]));
5889
- function dfs(name, path32) {
6115
+ function dfs(name, path33) {
5890
6116
  if (visiting.has(name)) {
5891
- const cycleStart = path32.indexOf(name);
5892
- const cycle = path32.slice(cycleStart).concat(name);
6117
+ const cycleStart = path33.indexOf(name);
6118
+ const cycle = path33.slice(cycleStart).concat(name);
5893
6119
  errors.push({ type: "cycle", message: `Cycle detected: ${cycle.join(" \u2192 ")}` });
5894
6120
  return true;
5895
6121
  }
5896
6122
  if (visited.has(name)) return false;
5897
6123
  visiting.add(name);
5898
- path32.push(name);
6124
+ path33.push(name);
5899
6125
  const phase = phaseMap.get(name);
5900
6126
  if (phase) {
5901
6127
  for (const dep of phase.dependencies) {
5902
- if (dfs(dep, path32)) return true;
6128
+ if (dfs(dep, path33)) return true;
5903
6129
  }
5904
6130
  }
5905
6131
  visiting.delete(name);
5906
6132
  visited.add(name);
5907
- path32.pop();
6133
+ path33.pop();
5908
6134
  return false;
5909
6135
  }
5910
6136
  for (const phase of phases) {
@@ -6032,6 +6258,7 @@ ${errors.map((e) => e.message).join("\n")}`);
6032
6258
  for (const phase of sorted) {
6033
6259
  context2.onProgress?.(phase.name, "running");
6034
6260
  const phaseStart = Date.now();
6261
+ const memBefore = context2.profile ? Math.round(process.memoryUsage().heapUsed / 1024 / 1024) : void 0;
6035
6262
  const runPhase = async () => {
6036
6263
  const depResults = /* @__PURE__ */ new Map();
6037
6264
  for (const dep of phase.dependencies) {
@@ -6055,6 +6282,10 @@ ${errors.map((e) => e.message).join("\n")}`);
6055
6282
  }
6056
6283
  const durationSec = (Date.now() - phaseStart) / 1e3;
6057
6284
  pipelinePhaseDurationSeconds.observe({ phase: phase.name, status: result.status }, durationSec);
6285
+ if (memBefore !== void 0) {
6286
+ result.memoryBeforeMB = memBefore;
6287
+ result.memoryAfterMB = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
6288
+ }
6058
6289
  results.set(phase.name, result);
6059
6290
  context2.onProgress?.(phase.name, result.status);
6060
6291
  if (result.status === "failed") {
@@ -6065,7 +6296,9 @@ ${errors.map((e) => e.message).join("\n")}`);
6065
6296
  const result = {
6066
6297
  status: "failed",
6067
6298
  duration: Date.now() - phaseStart,
6068
- message: err instanceof Error ? err.message : String(err)
6299
+ message: err instanceof Error ? err.message : String(err),
6300
+ memoryBeforeMB: memBefore,
6301
+ memoryAfterMB: memBefore !== void 0 ? Math.round(process.memoryUsage().heapUsed / 1024 / 1024) : void 0
6069
6302
  };
6070
6303
  pipelinePhaseDurationSeconds.observe({ phase: phase.name, status: "failed" }, (Date.now() - phaseStart) / 1e3);
6071
6304
  results.set(phase.name, result);
@@ -6117,7 +6350,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
6117
6350
  ]);
6118
6351
  function loadIgnorePatterns(workspaceRoot) {
6119
6352
  try {
6120
- const raw = fs24.readFileSync(path31.join(workspaceRoot, ".codeintelignore"), "utf-8");
6353
+ const raw = fs26.readFileSync(path32.join(workspaceRoot, ".codeintelignore"), "utf-8");
6121
6354
  const extras = /* @__PURE__ */ new Set();
6122
6355
  for (const line of raw.split("\n")) {
6123
6356
  const trimmed = line.trim();
@@ -6141,7 +6374,7 @@ var scanPhase = {
6141
6374
  function walk(dir) {
6142
6375
  let entries;
6143
6376
  try {
6144
- entries = fs24.readdirSync(dir, { withFileTypes: true });
6377
+ entries = fs26.readdirSync(dir, { withFileTypes: true });
6145
6378
  } catch {
6146
6379
  return;
6147
6380
  }
@@ -6150,15 +6383,15 @@ var scanPhase = {
6150
6383
  if (entry.name.startsWith(".")) continue;
6151
6384
  if (IGNORED_DIRS.has(entry.name)) continue;
6152
6385
  if (extraIgnore.has(entry.name)) continue;
6153
- walk(path31.join(dir, entry.name));
6386
+ walk(path32.join(dir, entry.name));
6154
6387
  } else if (entry.isFile()) {
6155
6388
  const name = entry.name;
6156
6389
  if (IGNORED_FILE_SUFFIXES.some((s) => name.endsWith(s))) continue;
6157
- const ext = path31.extname(name);
6390
+ const ext = path32.extname(name);
6158
6391
  if (!extensions.has(ext)) continue;
6159
- const fullPath = path31.join(dir, name);
6392
+ const fullPath = path32.join(dir, name);
6160
6393
  try {
6161
- const stat = fs24.statSync(fullPath);
6394
+ const stat = fs26.statSync(fullPath);
6162
6395
  if (stat.size > MAX_FILE_SIZE_BYTES) continue;
6163
6396
  } catch {
6164
6397
  continue;
@@ -6185,20 +6418,20 @@ var structurePhase = {
6185
6418
  const dirs = /* @__PURE__ */ new Set();
6186
6419
  let structDone = 0;
6187
6420
  for (const filePath of context2.filePaths) {
6188
- const relativePath = path31.relative(context2.workspaceRoot, filePath);
6421
+ const relativePath = path32.relative(context2.workspaceRoot, filePath);
6189
6422
  const lang = detectLanguage(filePath);
6190
6423
  context2.graph.addNode({
6191
6424
  id: generateNodeId("file", relativePath, relativePath),
6192
6425
  kind: "file",
6193
- name: path31.basename(filePath),
6426
+ name: path32.basename(filePath),
6194
6427
  filePath: relativePath,
6195
6428
  metadata: lang ? { language: lang } : void 0
6196
6429
  });
6197
- let dir = path31.dirname(relativePath);
6430
+ let dir = path32.dirname(relativePath);
6198
6431
  while (dir && dir !== "." && dir !== "") {
6199
6432
  if (dirs.has(dir)) break;
6200
6433
  dirs.add(dir);
6201
- dir = path31.dirname(dir);
6434
+ dir = path32.dirname(dir);
6202
6435
  }
6203
6436
  structDone++;
6204
6437
  context2.onPhaseProgress?.("structure", structDone, context2.filePaths.length);
@@ -6207,7 +6440,7 @@ var structurePhase = {
6207
6440
  context2.graph.addNode({
6208
6441
  id: generateNodeId("directory", dir, dir),
6209
6442
  kind: "directory",
6210
- name: path31.basename(dir),
6443
+ name: path32.basename(dir),
6211
6444
  filePath: dir
6212
6445
  });
6213
6446
  }
@@ -6318,22 +6551,22 @@ var flowPhase = {
6318
6551
  const queue = [{ nodeId: ep.id, path: [ep.id] }];
6319
6552
  const visited = /* @__PURE__ */ new Set();
6320
6553
  while (queue.length > 0 && flowCount < maxFlows) {
6321
- const { nodeId, path: path32 } = queue.shift();
6322
- if (path32.length > maxDepth) continue;
6554
+ const { nodeId, path: path33 } = queue.shift();
6555
+ if (path33.length > maxDepth) continue;
6323
6556
  const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
6324
- if (callEdges.length === 0 && path32.length >= 3) {
6557
+ if (callEdges.length === 0 && path33.length >= 3) {
6325
6558
  const flowId = generateNodeId("flow", ep.filePath, `flow-${flowCount}`);
6326
6559
  graph.addNode({
6327
6560
  id: flowId,
6328
6561
  kind: "flow",
6329
6562
  name: `${ep.name} flow ${flowCount}`,
6330
6563
  filePath: ep.filePath,
6331
- metadata: { steps: path32, entryPoint: ep.name }
6564
+ metadata: { steps: path33, entryPoint: ep.name }
6332
6565
  });
6333
- for (let i = 0; i < path32.length; i++) {
6566
+ for (let i = 0; i < path33.length; i++) {
6334
6567
  graph.addEdge({
6335
- id: generateEdgeId(path32[i], flowId, `step_of_${i}`),
6336
- source: path32[i],
6568
+ id: generateEdgeId(path33[i], flowId, `step_of_${i}`),
6569
+ source: path33[i],
6337
6570
  target: flowId,
6338
6571
  kind: "step_of",
6339
6572
  weight: 1,
@@ -6346,7 +6579,7 @@ var flowPhase = {
6346
6579
  for (const edge of callEdges) {
6347
6580
  if (visited.has(edge.target)) continue;
6348
6581
  visited.add(edge.target);
6349
- queue.push({ nodeId: edge.target, path: [...path32, edge.target] });
6582
+ queue.push({ nodeId: edge.target, path: [...path33, edge.target] });
6350
6583
  }
6351
6584
  }
6352
6585
  }
@@ -6364,7 +6597,7 @@ var LLMGovernanceLogger = class {
6364
6597
  }
6365
6598
  /** Path to the JSONL log file. */
6366
6599
  getLogPath() {
6367
- return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path31.join(os13.homedir(), ".code-intel", "llm-governance.jsonl");
6600
+ return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path32.join(os13.homedir(), ".code-intel", "llm-governance.jsonl");
6368
6601
  }
6369
6602
  /**
6370
6603
  * Append an entry to the governance log.
@@ -6380,8 +6613,8 @@ var LLMGovernanceLogger = class {
6380
6613
  ...entry
6381
6614
  };
6382
6615
  const logPath = this.getLogPath();
6383
- fs24.mkdirSync(path31.dirname(logPath), { recursive: true });
6384
- fs24.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
6616
+ fs26.mkdirSync(path32.dirname(logPath), { recursive: true });
6617
+ fs26.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
6385
6618
  } catch {
6386
6619
  }
6387
6620
  }
@@ -6391,7 +6624,7 @@ var LLMGovernanceLogger = class {
6391
6624
  */
6392
6625
  readLog(limit = 100) {
6393
6626
  try {
6394
- const raw = fs24.readFileSync(this.getLogPath(), "utf-8");
6627
+ const raw = fs26.readFileSync(this.getLogPath(), "utf-8");
6395
6628
  const lines = raw.split("\n").filter((l) => l.trim().length > 0).slice(-limit);
6396
6629
  return lines.map((l) => JSON.parse(l));
6397
6630
  } catch {
@@ -6401,6 +6634,9 @@ var LLMGovernanceLogger = class {
6401
6634
  };
6402
6635
  var governanceLogger = new LLMGovernanceLogger();
6403
6636
 
6637
+ // src/pipeline/phases/summarize-phase.ts
6638
+ init_logger();
6639
+
6404
6640
  // src/pipeline/workers/parse-phase-parallel.ts
6405
6641
  init_shared();
6406
6642
  init_id_generator();
@@ -6535,17 +6771,17 @@ function traceFlow(entryId, graph, maxDepth = 10, maxBranching = 4) {
6535
6771
  const queue = [{ nodeId: entryId, path: [entryId] }];
6536
6772
  const visited = /* @__PURE__ */ new Set();
6537
6773
  while (queue.length > 0 && flows.length < maxFlows) {
6538
- const { nodeId, path: path32 } = queue.shift();
6539
- if (path32.length > maxDepth) continue;
6774
+ const { nodeId, path: path33 } = queue.shift();
6775
+ if (path33.length > maxDepth) continue;
6540
6776
  const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
6541
- if (callEdges.length === 0 && path32.length >= 3) {
6542
- flows.push({ entryPointId: entryId, steps: [...path32] });
6777
+ if (callEdges.length === 0 && path33.length >= 3) {
6778
+ flows.push({ entryPointId: entryId, steps: [...path33] });
6543
6779
  continue;
6544
6780
  }
6545
6781
  for (const edge of callEdges) {
6546
6782
  if (visited.has(edge.target)) continue;
6547
6783
  visited.add(edge.target);
6548
- queue.push({ nodeId: edge.target, path: [...path32, edge.target] });
6784
+ queue.push({ nodeId: edge.target, path: [...path33, edge.target] });
6549
6785
  }
6550
6786
  }
6551
6787
  }
@@ -6646,7 +6882,7 @@ var VectorIndex = class {
6646
6882
  this.sqlitePath = sqlitePath;
6647
6883
  }
6648
6884
  async init() {
6649
- this.db = new Database3(this.sqlitePath);
6885
+ this.db = new Database2(this.sqlitePath);
6650
6886
  this.db.pragma("journal_mode = WAL");
6651
6887
  this.db.exec(`
6652
6888
  CREATE TABLE IF NOT EXISTS ${EMBED_TABLE} (
@@ -6777,9 +7013,9 @@ function siftDown(arr, i, score) {
6777
7013
  }
6778
7014
  init_embedder();
6779
7015
  async function hybridSearch(graph, query, limit, options = {}) {
6780
- const { vectorDbPath, bm25Limit = 50, vectorLimit = 50 } = options;
6781
- const bm25Promise = Promise.resolve(textSearch(graph, query, bm25Limit));
6782
- const hasVectorDb = Boolean(vectorDbPath && fs24.existsSync(vectorDbPath));
7016
+ const { vectorDbPath, bm25Limit = 50, vectorLimit = 50, bm25Results: precomputedBm25 } = options;
7017
+ const bm25Promise = precomputedBm25 ? Promise.resolve(precomputedBm25) : Promise.resolve(textSearch(graph, query, bm25Limit));
7018
+ const hasVectorDb = Boolean(vectorDbPath && fs26.existsSync(vectorDbPath));
6783
7019
  if (!hasVectorDb) {
6784
7020
  const bm25Results2 = await bm25Promise;
6785
7021
  return {
@@ -6828,105 +7064,317 @@ async function runVectorSearch(vectorDbPath, query, topK) {
6828
7064
  return null;
6829
7065
  }
6830
7066
  }
6831
- var DbManager = class {
6832
- db = null;
6833
- conn = null;
6834
- dbPath;
7067
+
7068
+ // src/search/bm25-index.ts
7069
+ init_logger();
7070
+ var K1 = 1.2;
7071
+ var B = 0.75;
7072
+ function tokenize(text) {
7073
+ return text.toLowerCase().split(/[\s\-_./\\:(){}[\]<>,"'`~!@#$%^&*+=|;?]+/).filter((t) => t.length >= 2 && t.length <= 64);
7074
+ }
7075
+ function nodeToDoc(node) {
7076
+ return [
7077
+ node.name,
7078
+ node.kind,
7079
+ node.filePath,
7080
+ (node.content ?? "").slice(0, 1e3)
7081
+ ].join(" ");
7082
+ }
7083
+ function heapTopK(scores, k) {
7084
+ if (k <= 0) return [];
7085
+ const heap = [];
7086
+ function heapifyUp(i) {
7087
+ while (i > 0) {
7088
+ const parent = i - 1 >> 1;
7089
+ if (heap[parent][1] > heap[i][1]) {
7090
+ [heap[parent], heap[i]] = [heap[i], heap[parent]];
7091
+ i = parent;
7092
+ } else break;
7093
+ }
7094
+ }
7095
+ function heapifyDown(i) {
7096
+ const n = heap.length;
7097
+ while (true) {
7098
+ let smallest = i;
7099
+ const l = 2 * i + 1, r = 2 * i + 2;
7100
+ if (l < n && heap[l][1] < heap[smallest][1]) smallest = l;
7101
+ if (r < n && heap[r][1] < heap[smallest][1]) smallest = r;
7102
+ if (smallest === i) break;
7103
+ [heap[smallest], heap[i]] = [heap[i], heap[smallest]];
7104
+ i = smallest;
7105
+ }
7106
+ }
7107
+ for (const [nodeId, score] of scores) {
7108
+ if (heap.length < k) {
7109
+ heap.push([nodeId, score]);
7110
+ heapifyUp(heap.length - 1);
7111
+ } else if (score > heap[0][1]) {
7112
+ heap[0] = [nodeId, score];
7113
+ heapifyDown(0);
7114
+ }
7115
+ }
7116
+ return heap.sort((a, b) => b[1] - a[1]);
7117
+ }
7118
+ var Bm25Index = class {
6835
7119
  constructor(dbPath) {
6836
7120
  this.dbPath = dbPath;
6837
7121
  }
6838
- async init() {
6839
- fs24.mkdirSync(path31.dirname(this.dbPath), { recursive: true });
6840
- this.db = new Database(this.dbPath);
6841
- await this.db.init();
6842
- this.conn = new Connection(this.db);
6843
- await this.conn.init();
6844
- }
6845
- async query(cypher) {
6846
- if (!this.conn) throw new Error("Database not initialized");
6847
- const result = await this.conn.query(cypher);
6848
- const qr = Array.isArray(result) ? result[0] : result;
6849
- const rows = await qr.getAll();
6850
- qr.close();
6851
- return rows;
6852
- }
6853
- async execute(cypher) {
6854
- if (!this.conn) throw new Error("Database not initialized");
6855
- const result = await this.conn.query(cypher);
6856
- const qr = Array.isArray(result) ? result[0] : result;
6857
- qr.close();
6858
- }
6859
- close() {
6860
- try {
6861
- this.conn?.closeSync();
6862
- } catch {
7122
+ dbPath;
7123
+ /** In-memory inverted index (populated after `load()`). */
7124
+ invertedIndex = /* @__PURE__ */ new Map();
7125
+ docLengths = /* @__PURE__ */ new Map();
7126
+ nodeMeta = /* @__PURE__ */ new Map();
7127
+ avgdl = 1;
7128
+ docCount = 0;
7129
+ _loaded = false;
7130
+ get isLoaded() {
7131
+ return this._loaded;
7132
+ }
7133
+ // ── Build ───────────────────────────────────────────────────────────────────
7134
+ /**
7135
+ * Build the inverted index from a KnowledgeGraph and persist to SQLite.
7136
+ * Called once at analysis time after the main pipeline completes.
7137
+ */
7138
+ build(graph) {
7139
+ const nodeTermFreqs = /* @__PURE__ */ new Map();
7140
+ const docLengths = /* @__PURE__ */ new Map();
7141
+ const nodeMeta = /* @__PURE__ */ new Map();
7142
+ for (const node of graph.allNodes()) {
7143
+ if (["directory", "cluster", "flow"].includes(node.kind)) continue;
7144
+ const terms = tokenize(nodeToDoc(node));
7145
+ const tf = /* @__PURE__ */ new Map();
7146
+ for (const t of terms) tf.set(t, (tf.get(t) ?? 0) + 1);
7147
+ nodeTermFreqs.set(node.id, tf);
7148
+ docLengths.set(node.id, terms.length);
7149
+ nodeMeta.set(node.id, {
7150
+ name: node.name,
7151
+ kind: node.kind,
7152
+ filePath: node.filePath,
7153
+ snippet: node.content?.slice(0, 200)
7154
+ });
7155
+ }
7156
+ const docCount = nodeTermFreqs.size;
7157
+ const totalLen = [...docLengths.values()].reduce((a, b) => a + b, 0);
7158
+ const avgdl = docCount > 0 ? totalLen / docCount : 1;
7159
+ const invertedIndex = /* @__PURE__ */ new Map();
7160
+ for (const [nodeId, tf] of nodeTermFreqs) {
7161
+ for (const [term, count] of tf) {
7162
+ let postings = invertedIndex.get(term);
7163
+ if (!postings) {
7164
+ postings = [];
7165
+ invertedIndex.set(term, postings);
7166
+ }
7167
+ postings.push({ nodeId, tf: count });
7168
+ }
7169
+ }
7170
+ fs26.mkdirSync(path32.dirname(this.dbPath), { recursive: true });
7171
+ for (const f of [this.dbPath, `${this.dbPath}-shm`, `${this.dbPath}-wal`]) {
7172
+ try {
7173
+ if (fs26.existsSync(f)) fs26.unlinkSync(f);
7174
+ } catch {
7175
+ }
6863
7176
  }
7177
+ const db = new Database2(this.dbPath);
7178
+ db.pragma("journal_mode = WAL");
7179
+ db.exec(`
7180
+ CREATE TABLE bm25_index (term TEXT PRIMARY KEY, postings TEXT NOT NULL);
7181
+ CREATE TABLE bm25_doclen (node_id TEXT PRIMARY KEY, doclen INTEGER NOT NULL);
7182
+ CREATE TABLE bm25_nodemeta(node_id TEXT PRIMARY KEY, name TEXT, kind TEXT, file_path TEXT, snippet TEXT);
7183
+ CREATE TABLE bm25_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
7184
+ `);
7185
+ const insPosting = db.prepare("INSERT OR REPLACE INTO bm25_index VALUES (?, ?)");
7186
+ const insDoclen = db.prepare("INSERT OR REPLACE INTO bm25_doclen VALUES (?, ?)");
7187
+ const insNodeMeta = db.prepare("INSERT OR REPLACE INTO bm25_nodemeta VALUES (?, ?, ?, ?, ?)");
7188
+ const insMeta = db.prepare("INSERT OR REPLACE INTO bm25_meta VALUES (?, ?)");
7189
+ db.transaction(() => {
7190
+ for (const [term, postings] of invertedIndex) {
7191
+ insPosting.run(term, JSON.stringify(postings));
7192
+ }
7193
+ for (const [nodeId, len] of docLengths) {
7194
+ insDoclen.run(nodeId, len);
7195
+ }
7196
+ for (const [nodeId, meta] of nodeMeta) {
7197
+ insNodeMeta.run(nodeId, meta.name, meta.kind, meta.filePath, meta.snippet ?? null);
7198
+ }
7199
+ insMeta.run("avgdl", String(avgdl));
7200
+ insMeta.run("docCount", String(docCount));
7201
+ })();
7202
+ db.close();
7203
+ logger_default.info(` [bm25] Index built: ${invertedIndex.size} terms, ${docCount} documents`);
7204
+ }
7205
+ // ── Load into memory ────────────────────────────────────────────────────────
7206
+ /**
7207
+ * Load the full inverted index into memory.
7208
+ * Called once on `serve` startup.
7209
+ */
7210
+ load() {
7211
+ if (!fs26.existsSync(this.dbPath)) return;
7212
+ const db = new Database2(this.dbPath, { readonly: true });
6864
7213
  try {
6865
- this.db?.closeSync();
6866
- } catch {
7214
+ const getMeta = db.prepare("SELECT value FROM bm25_meta WHERE key = ?");
7215
+ this.avgdl = parseFloat(getMeta.get("avgdl")?.value ?? "1");
7216
+ this.docCount = parseInt(getMeta.get("docCount")?.value ?? "0", 10);
7217
+ this.invertedIndex.clear();
7218
+ const postingRows = db.prepare("SELECT term, postings FROM bm25_index").all();
7219
+ for (const row of postingRows) {
7220
+ this.invertedIndex.set(row.term, JSON.parse(row.postings));
7221
+ }
7222
+ this.docLengths.clear();
7223
+ const dlRows = db.prepare("SELECT node_id, doclen FROM bm25_doclen").all();
7224
+ for (const row of dlRows) {
7225
+ this.docLengths.set(row.node_id, row.doclen);
7226
+ }
7227
+ this.nodeMeta.clear();
7228
+ const metaRows = db.prepare("SELECT node_id, name, kind, file_path, snippet FROM bm25_nodemeta").all();
7229
+ for (const row of metaRows) {
7230
+ this.nodeMeta.set(row.node_id, {
7231
+ name: row.name,
7232
+ kind: row.kind,
7233
+ filePath: row.file_path,
7234
+ snippet: row.snippet ?? void 0
7235
+ });
7236
+ }
7237
+ this._loaded = true;
7238
+ logger_default.info(` [bm25] Index loaded (${this.invertedIndex.size} terms)`);
7239
+ } finally {
7240
+ db.close();
6867
7241
  }
6868
- this.conn = null;
6869
- this.db = null;
6870
7242
  }
6871
- get isOpen() {
6872
- return this.conn !== null;
7243
+ // ── Search ──────────────────────────────────────────────────────────────────
7244
+ /**
7245
+ * BM25 search.
7246
+ *
7247
+ * Performance strategy:
7248
+ * 1. Skip ultra-high-df terms (df/N > 0.6) — near-zero IDF, dominate posting
7249
+ * lists for common words like "function", "return", "export" in large repos.
7250
+ * 2. Min-heap top-K selection — O(n log k) instead of full O(n log n) sort.
7251
+ * For k=10 and n=30,000 candidates this is ~10× faster than Array.sort.
7252
+ */
7253
+ search(query, limit) {
7254
+ if (!this._loaded || this.invertedIndex.size === 0) return [];
7255
+ const queryTerms = [...new Set(tokenize(query))];
7256
+ if (queryTerms.length === 0) return [];
7257
+ const scores = /* @__PURE__ */ new Map();
7258
+ const N = this.docCount;
7259
+ const avgdl = this.avgdl;
7260
+ for (const term of queryTerms) {
7261
+ const postings = this.invertedIndex.get(term);
7262
+ if (!postings) continue;
7263
+ const df = postings.length;
7264
+ if (N > 100 && df / N > 0.6) continue;
7265
+ const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
7266
+ for (const { nodeId, tf } of postings) {
7267
+ const dl = this.docLengths.get(nodeId) ?? avgdl;
7268
+ const score = idf * (tf * (K1 + 1)) / (tf + K1 * (1 - B + B * (dl / avgdl)));
7269
+ scores.set(nodeId, (scores.get(nodeId) ?? 0) + score);
7270
+ }
7271
+ }
7272
+ if (scores.size === 0) return [];
7273
+ const topEntries = heapTopK(scores, limit);
7274
+ return topEntries.map(([nodeId, score]) => {
7275
+ const meta = this.nodeMeta.get(nodeId);
7276
+ return {
7277
+ nodeId,
7278
+ name: meta?.name ?? nodeId,
7279
+ kind: meta?.kind ?? "unknown",
7280
+ filePath: meta?.filePath ?? "",
7281
+ score,
7282
+ snippet: meta?.snippet
7283
+ };
7284
+ });
6873
7285
  }
6874
- };
6875
-
6876
- // src/storage/schema.ts
6877
- var NODE_TABLE_MAP = {
6878
- file: "file_nodes",
6879
- directory: "dir_nodes",
6880
- function: "func_nodes",
6881
- class: "class_nodes",
6882
- interface: "iface_nodes",
6883
- method: "method_nodes",
6884
- constructor: "ctor_nodes",
6885
- variable: "var_nodes",
6886
- property: "prop_nodes",
6887
- struct: "struct_nodes",
6888
- enum: "enum_nodes",
6889
- trait: "trait_nodes",
6890
- namespace: "ns_nodes",
6891
- module: "mod_nodes",
6892
- type_alias: "type_nodes",
6893
- constant: "const_nodes",
6894
- route: "route_nodes",
6895
- cluster: "cluster_nodes",
6896
- flow: "flow_nodes",
6897
- vulnerability: "vuln_nodes"
6898
- };
6899
- var ALL_NODE_TABLES = [...new Set(Object.values(NODE_TABLE_MAP))];
6900
- function getCreateNodeTableDDL(tableName) {
6901
- return `CREATE NODE TABLE IF NOT EXISTS ${tableName} (
6902
- id STRING,
6903
- name STRING,
6904
- file_path STRING,
6905
- start_line INT64,
6906
- end_line INT64,
6907
- exported BOOLEAN,
6908
- content STRING,
6909
- metadata STRING,
6910
- PRIMARY KEY (id)
6911
- )`;
6912
- }
6913
- function getCreateEdgeTableDDL() {
6914
- const uniqueTables = ALL_NODE_TABLES;
6915
- const fromToPairs = [];
6916
- for (const from of uniqueTables) {
6917
- for (const to of uniqueTables) {
6918
- fromToPairs.push(`FROM ${from} TO ${to}`);
7286
+ // ── Incremental update ──────────────────────────────────────────────────────
7287
+ /**
7288
+ * Incrementally update index for a set of changed/added nodes.
7289
+ * Only terms that overlap with the changed nodes are rewritten.
7290
+ * Works even if `load()` was not called (reads affected terms directly from DB).
7291
+ */
7292
+ updateNodes(nodes) {
7293
+ if (!fs26.existsSync(this.dbPath)) return;
7294
+ if (nodes.length === 0) return;
7295
+ const changedIds = new Set(nodes.map((n) => n.id));
7296
+ const newTermFreqs = /* @__PURE__ */ new Map();
7297
+ for (const node of nodes) {
7298
+ if (["directory", "cluster", "flow"].includes(node.kind)) continue;
7299
+ const terms = tokenize(nodeToDoc(node));
7300
+ const tf = /* @__PURE__ */ new Map();
7301
+ for (const t of terms) tf.set(t, (tf.get(t) ?? 0) + 1);
7302
+ newTermFreqs.set(node.id, tf);
7303
+ }
7304
+ const newTermSet = new Set([...newTermFreqs.values()].flatMap((m) => [...m.keys()]));
7305
+ const db = new Database2(this.dbPath);
7306
+ db.pragma("journal_mode = WAL");
7307
+ const termsToRewrite = /* @__PURE__ */ new Map();
7308
+ for (const term of newTermSet) {
7309
+ const row = db.prepare("SELECT postings FROM bm25_index WHERE term = ?").get(term);
7310
+ const existing = row ? JSON.parse(row.postings) : [];
7311
+ termsToRewrite.set(term, existing.filter((p) => !changedIds.has(p.nodeId)));
7312
+ }
7313
+ for (const nodeId of changedIds) {
7314
+ const rows = db.prepare("SELECT term, postings FROM bm25_index WHERE postings LIKE ?").all(`%${nodeId}%`);
7315
+ for (const row of rows) {
7316
+ if (termsToRewrite.has(row.term)) continue;
7317
+ const postings = JSON.parse(row.postings);
7318
+ if (postings.some((p) => changedIds.has(p.nodeId))) {
7319
+ termsToRewrite.set(row.term, postings.filter((p) => !changedIds.has(p.nodeId)));
7320
+ }
7321
+ }
7322
+ }
7323
+ for (const [nodeId, tf] of newTermFreqs) {
7324
+ for (const [term, count] of tf) {
7325
+ const postings = termsToRewrite.get(term) ?? [];
7326
+ postings.push({ nodeId, tf: count });
7327
+ termsToRewrite.set(term, postings);
7328
+ }
7329
+ }
7330
+ const upsertPosting = db.prepare("INSERT OR REPLACE INTO bm25_index VALUES (?, ?)");
7331
+ const upsertDoclen = db.prepare("INSERT OR REPLACE INTO bm25_doclen VALUES (?, ?)");
7332
+ const upsertNodeMeta = db.prepare("INSERT OR REPLACE INTO bm25_nodemeta VALUES (?, ?, ?, ?, ?)");
7333
+ db.transaction(() => {
7334
+ for (const [term, postings] of termsToRewrite) {
7335
+ if (postings.length === 0) {
7336
+ db.prepare("DELETE FROM bm25_index WHERE term = ?").run(term);
7337
+ } else {
7338
+ upsertPosting.run(term, JSON.stringify(postings));
7339
+ }
7340
+ }
7341
+ for (const node of nodes) {
7342
+ const terms = tokenize(nodeToDoc(node));
7343
+ upsertDoclen.run(node.id, terms.length);
7344
+ upsertNodeMeta.run(node.id, node.name, node.kind, node.filePath, node.content?.slice(0, 200) ?? null);
7345
+ }
7346
+ })();
7347
+ db.close();
7348
+ if (this._loaded) {
7349
+ for (const [term, postings] of termsToRewrite) {
7350
+ if (postings.length === 0) this.invertedIndex.delete(term);
7351
+ else this.invertedIndex.set(term, postings);
7352
+ }
7353
+ for (const node of nodes) {
7354
+ const terms = tokenize(nodeToDoc(node));
7355
+ this.docLengths.set(node.id, terms.length);
7356
+ this.nodeMeta.set(node.id, {
7357
+ name: node.name,
7358
+ kind: node.kind,
7359
+ filePath: node.filePath,
7360
+ snippet: node.content?.slice(0, 200)
7361
+ });
7362
+ }
6919
7363
  }
6920
7364
  }
6921
- return [`CREATE REL TABLE GROUP IF NOT EXISTS code_edges (
6922
- ${fromToPairs.join(",\n ")},
6923
- kind STRING,
6924
- weight DOUBLE,
6925
- label STRING
6926
- )`];
7365
+ };
7366
+ function getBm25DbPath(workspaceRoot) {
7367
+ return path32.join(workspaceRoot, ".code-intel", "bm25.db");
6927
7368
  }
7369
+
7370
+ // src/storage/index.ts
7371
+ init_db_manager();
7372
+ init_schema();
7373
+
7374
+ // src/storage/csv-writer.ts
7375
+ init_schema();
6928
7376
  function writeNodeCSVs(graph, outputDir) {
6929
- fs24.mkdirSync(outputDir, { recursive: true });
7377
+ fs26.mkdirSync(outputDir, { recursive: true });
6930
7378
  const header = "id,name,file_path,start_line,end_line,exported,content,metadata\n";
6931
7379
  const tableBuffers = /* @__PURE__ */ new Map();
6932
7380
  const tableFilePaths = /* @__PURE__ */ new Map();
@@ -6934,7 +7382,7 @@ function writeNodeCSVs(graph, outputDir) {
6934
7382
  const table = NODE_TABLE_MAP[node.kind];
6935
7383
  if (!tableBuffers.has(table)) {
6936
7384
  tableBuffers.set(table, [header]);
6937
- tableFilePaths.set(table, path31.join(outputDir, `${table}.csv`));
7385
+ tableFilePaths.set(table, path32.join(outputDir, `${table}.csv`));
6938
7386
  }
6939
7387
  tableBuffers.get(table).push(
6940
7388
  csvRow([
@@ -6954,12 +7402,12 @@ function writeNodeCSVs(graph, outputDir) {
6954
7402
  );
6955
7403
  }
6956
7404
  for (const [table, lines] of tableBuffers) {
6957
- fs24.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
7405
+ fs26.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
6958
7406
  }
6959
7407
  return tableFilePaths;
6960
7408
  }
6961
7409
  function writeEdgeCSV(graph, outputDir) {
6962
- fs24.mkdirSync(outputDir, { recursive: true });
7410
+ fs26.mkdirSync(outputDir, { recursive: true });
6963
7411
  const header = "from_id,to_id,kind,weight,label\n";
6964
7412
  const groups = /* @__PURE__ */ new Map();
6965
7413
  for (const edge of graph.allEdges()) {
@@ -6970,7 +7418,7 @@ function writeEdgeCSV(graph, outputDir) {
6970
7418
  const toTable = NODE_TABLE_MAP[targetNode.kind];
6971
7419
  const key = `${fromTable}->${toTable}`;
6972
7420
  if (!groups.has(key)) {
6973
- const filePath = path31.join(outputDir, `edges_${fromTable}_${toTable}.csv`);
7421
+ const filePath = path32.join(outputDir, `edges_${fromTable}_${toTable}.csv`);
6974
7422
  groups.set(key, { lines: [header], from: fromTable, to: toTable, filePath });
6975
7423
  }
6976
7424
  groups.get(key).lines.push(
@@ -6985,7 +7433,7 @@ function writeEdgeCSV(graph, outputDir) {
6985
7433
  }
6986
7434
  const result = [];
6987
7435
  for (const group of groups.values()) {
6988
- fs24.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
7436
+ fs26.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
6989
7437
  result.push({ fromTable: group.from, toTable: group.to, filePath: group.filePath });
6990
7438
  }
6991
7439
  return result;
@@ -7002,6 +7450,9 @@ function escapeNewlines(s) {
7002
7450
  if (!s.includes("\n") && !s.includes("\r")) return s;
7003
7451
  return s.replace(/\r/g, "\\r").replace(/\n/g, "\\n");
7004
7452
  }
7453
+
7454
+ // src/storage/graph-loader.ts
7455
+ init_schema();
7005
7456
  async function loadGraphToDB(graph, dbManager) {
7006
7457
  for (const table of ALL_NODE_TABLES) {
7007
7458
  await dbManager.execute(getCreateNodeTableDDL(table));
@@ -7013,7 +7464,7 @@ async function loadGraphToDB(graph, dbManager) {
7013
7464
  } catch {
7014
7465
  }
7015
7466
  }
7016
- const tmpDir = fs24.mkdtempSync(path31.join(os13.tmpdir(), "code-intel-csv-"));
7467
+ const tmpDir = fs26.mkdtempSync(path32.join(os13.tmpdir(), "code-intel-csv-"));
7017
7468
  try {
7018
7469
  const nodeTableFiles = writeNodeCSVs(graph, tmpDir);
7019
7470
  const edgeGroups = writeEdgeCSV(graph, tmpDir);
@@ -7032,8 +7483,8 @@ async function loadGraphToDB(graph, dbManager) {
7032
7483
  }
7033
7484
  let nodeCount = 0;
7034
7485
  for (const [table, csvPath] of nodeTableFiles) {
7035
- if (!fs24.existsSync(csvPath)) continue;
7036
- const stat = fs24.statSync(csvPath);
7486
+ if (!fs26.existsSync(csvPath)) continue;
7487
+ const stat = fs26.statSync(csvPath);
7037
7488
  if (stat.size < 50) continue;
7038
7489
  try {
7039
7490
  await dbManager.execute(
@@ -7046,8 +7497,8 @@ async function loadGraphToDB(graph, dbManager) {
7046
7497
  }
7047
7498
  let edgeCount = 0;
7048
7499
  for (const group of edgeGroups) {
7049
- if (!fs24.existsSync(group.filePath)) continue;
7050
- const stat = fs24.statSync(group.filePath);
7500
+ if (!fs26.existsSync(group.filePath)) continue;
7501
+ const stat = fs26.statSync(group.filePath);
7051
7502
  if (stat.size < 50) continue;
7052
7503
  try {
7053
7504
  await dbManager.execute(
@@ -7061,7 +7512,7 @@ async function loadGraphToDB(graph, dbManager) {
7061
7512
  return { nodeCount, edgeCount };
7062
7513
  } finally {
7063
7514
  try {
7064
- fs24.rmSync(tmpDir, { recursive: true, force: true });
7515
+ fs26.rmSync(tmpDir, { recursive: true, force: true });
7065
7516
  } catch {
7066
7517
  }
7067
7518
  }
@@ -7113,19 +7564,19 @@ function buildNodeProps(node) {
7113
7564
  function escCypher(s) {
7114
7565
  return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "");
7115
7566
  }
7116
- var GLOBAL_DIR = path31.join(os13.homedir(), ".code-intel");
7117
- var REPOS_FILE = path31.join(GLOBAL_DIR, "repos.json");
7567
+ var GLOBAL_DIR = path32.join(os13.homedir(), ".code-intel");
7568
+ var REPOS_FILE = path32.join(GLOBAL_DIR, "repos.json");
7118
7569
  function loadRegistry() {
7119
7570
  try {
7120
- const data = fs24.readFileSync(REPOS_FILE, "utf-8");
7571
+ const data = fs26.readFileSync(REPOS_FILE, "utf-8");
7121
7572
  return JSON.parse(data);
7122
7573
  } catch {
7123
7574
  return [];
7124
7575
  }
7125
7576
  }
7126
7577
  function saveRegistry(entries) {
7127
- fs24.mkdirSync(GLOBAL_DIR, { recursive: true });
7128
- fs24.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
7578
+ fs26.mkdirSync(GLOBAL_DIR, { recursive: true });
7579
+ fs26.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
7129
7580
  }
7130
7581
  function upsertRepo(entry) {
7131
7582
  const entries = loadRegistry();
@@ -7142,99 +7593,30 @@ function removeRepo(repoPath) {
7142
7593
  saveRegistry(entries);
7143
7594
  }
7144
7595
  function saveMetadata(repoDir, metadata) {
7145
- const metaDir = path31.join(repoDir, ".code-intel");
7146
- fs24.mkdirSync(metaDir, { recursive: true });
7147
- fs24.writeFileSync(path31.join(metaDir, "meta.json"), JSON.stringify(metadata, null, 2));
7596
+ const metaDir = path32.join(repoDir, ".code-intel");
7597
+ fs26.mkdirSync(metaDir, { recursive: true });
7598
+ fs26.writeFileSync(path32.join(metaDir, "meta.json"), JSON.stringify(metadata, null, 2));
7148
7599
  }
7149
7600
  function loadMetadata(repoDir) {
7150
7601
  try {
7151
- const data = fs24.readFileSync(path31.join(repoDir, ".code-intel", "meta.json"), "utf-8");
7602
+ const data = fs26.readFileSync(path32.join(repoDir, ".code-intel", "meta.json"), "utf-8");
7152
7603
  return JSON.parse(data);
7153
7604
  } catch {
7154
7605
  return null;
7155
7606
  }
7156
7607
  }
7157
7608
  function getDbPath(repoDir) {
7158
- return path31.join(repoDir, ".code-intel", "graph.db");
7609
+ return path32.join(repoDir, ".code-intel", "graph.db");
7159
7610
  }
7160
7611
  function getVectorDbPath(repoDir) {
7161
- return path31.join(repoDir, ".code-intel", "vector.db");
7612
+ return path32.join(repoDir, ".code-intel", "vector.db");
7162
7613
  }
7163
7614
 
7164
7615
  // src/mcp-server/server.ts
7165
7616
  init_group_registry();
7166
-
7167
- // src/multi-repo/graph-from-db.ts
7168
- var TABLE_TO_KIND = Object.fromEntries(
7169
- Object.entries(NODE_TABLE_MAP).map(([kind, table]) => [table, kind])
7170
- );
7171
- function parseRow(row, kind) {
7172
- return {
7173
- id: String(row["id"] ?? ""),
7174
- kind,
7175
- name: String(row["name"] ?? ""),
7176
- filePath: String(row["file_path"] ?? ""),
7177
- startLine: row["start_line"] != null ? Number(row["start_line"]) : void 0,
7178
- endLine: row["end_line"] != null ? Number(row["end_line"]) : void 0,
7179
- exported: row["exported"] != null ? Boolean(row["exported"]) : void 0,
7180
- content: row["content"] ? String(row["content"]) : void 0,
7181
- metadata: row["metadata"] ? (() => {
7182
- try {
7183
- return JSON.parse(String(row["metadata"]));
7184
- } catch {
7185
- return void 0;
7186
- }
7187
- })() : void 0
7188
- };
7189
- }
7190
- async function loadGraphFromDB(graph, db) {
7191
- for (const table of ALL_NODE_TABLES) {
7192
- const kind = TABLE_TO_KIND[table];
7193
- if (!kind) continue;
7194
- let rows = [];
7195
- try {
7196
- rows = await db.query(`MATCH (n:${table}) RETURN n.id, n.name, n.file_path, n.start_line, n.end_line, n.exported, n.content, n.metadata`);
7197
- } catch {
7198
- continue;
7199
- }
7200
- for (const row of rows) {
7201
- const node = parseRow({
7202
- id: row["n.id"],
7203
- name: row["n.name"],
7204
- file_path: row["n.file_path"],
7205
- start_line: row["n.start_line"],
7206
- end_line: row["n.end_line"],
7207
- exported: row["n.exported"],
7208
- content: row["n.content"],
7209
- metadata: row["n.metadata"]
7210
- }, kind);
7211
- if (node.id && node.name) graph.addNode(node);
7212
- }
7213
- }
7214
- try {
7215
- const edgeRows = await db.query(
7216
- `MATCH (a)-[e:code_edges]->(b) RETURN a.id, b.id, e.kind, e.weight, e.label`
7217
- );
7218
- for (const row of edgeRows) {
7219
- const sourceId = String(row["a.id"] ?? "");
7220
- const targetId = String(row["b.id"] ?? "");
7221
- const kind = String(row["e.kind"] ?? "");
7222
- if (!sourceId || !targetId || !kind) continue;
7223
- const edge = {
7224
- id: `${sourceId}::${kind}::${targetId}`,
7225
- source: sourceId,
7226
- target: targetId,
7227
- kind,
7228
- weight: row["e.weight"] != null ? Number(row["e.weight"]) : void 0,
7229
- label: row["e.label"] ? String(row["e.label"]) : void 0
7230
- };
7231
- graph.addEdge(edge);
7232
- }
7233
- } catch {
7234
- }
7235
- }
7236
-
7237
- // src/multi-repo/group-sync.ts
7617
+ init_db_manager();
7618
+ init_knowledge_graph();
7619
+ init_graph_from_db();
7238
7620
  init_logger();
7239
7621
  function scanForFiles(root, matcher, maxDepth = 2) {
7240
7622
  const results = [];
@@ -7242,12 +7624,12 @@ function scanForFiles(root, matcher, maxDepth = 2) {
7242
7624
  if (depth > maxDepth) return;
7243
7625
  let entries;
7244
7626
  try {
7245
- entries = fs24.readdirSync(dir, { withFileTypes: true });
7627
+ entries = fs26.readdirSync(dir, { withFileTypes: true });
7246
7628
  } catch {
7247
7629
  return;
7248
7630
  }
7249
7631
  for (const entry of entries) {
7250
- const full = path31.join(dir, entry.name);
7632
+ const full = path32.join(dir, entry.name);
7251
7633
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
7252
7634
  walk(full, depth + 1);
7253
7635
  } else if (entry.isFile() && matcher(entry.name)) {
@@ -7270,8 +7652,8 @@ var OPENAPI_FILENAMES = /* @__PURE__ */ new Set([
7270
7652
  ]);
7271
7653
  var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
7272
7654
  function tryParseFile(filePath) {
7273
- const ext = path31.extname(filePath).toLowerCase();
7274
- const content = fs24.readFileSync(filePath, "utf-8");
7655
+ const ext = path32.extname(filePath).toLowerCase();
7656
+ const content = fs26.readFileSync(filePath, "utf-8");
7275
7657
  if (ext === ".json") {
7276
7658
  try {
7277
7659
  return JSON.parse(content);
@@ -7325,7 +7707,7 @@ async function parseGraphQLContracts(repoRoot) {
7325
7707
  const files = scanForFiles(repoRoot, (name) => name.endsWith(".graphql") || name.endsWith(".gql"));
7326
7708
  const contracts = [];
7327
7709
  for (const filePath of files) {
7328
- const content = fs24.readFileSync(filePath, "utf-8");
7710
+ const content = fs26.readFileSync(filePath, "utf-8");
7329
7711
  const typeRegex = /type\s+(\w+)\s*\{([^}]+)\}/g;
7330
7712
  let match;
7331
7713
  while ((match = typeRegex.exec(content)) !== null) {
@@ -7356,7 +7738,7 @@ async function parseProtoContracts(repoRoot) {
7356
7738
  const files = scanForFiles(repoRoot, (name) => name.endsWith(".proto"));
7357
7739
  const contracts = [];
7358
7740
  for (const filePath of files) {
7359
- const content = fs24.readFileSync(filePath, "utf-8");
7741
+ const content = fs26.readFileSync(filePath, "utf-8");
7360
7742
  const serviceRegex = /service\s+(\w+)\s*\{([^}]+)\}/g;
7361
7743
  let serviceMatch;
7362
7744
  while ((serviceMatch = serviceRegex.exec(content)) !== null) {
@@ -7561,13 +7943,13 @@ async function syncGroup(group) {
7561
7943
  logger_default.warn(` \u26A0 Registry entry "${member.registryName}" not found \u2014 skipping ${member.groupPath}`);
7562
7944
  continue;
7563
7945
  }
7564
- const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
7565
- if (!fs24.existsSync(dbPath)) {
7946
+ const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
7947
+ if (!fs26.existsSync(dbPath)) {
7566
7948
  logger_default.warn(` \u26A0 No index at ${dbPath} \u2014 run \`code-intel analyze ${regEntry.path}\` first`);
7567
7949
  continue;
7568
7950
  }
7569
7951
  const graph = createKnowledgeGraph();
7570
- const db = new DbManager(dbPath);
7952
+ const db = new DbManager(dbPath, true);
7571
7953
  try {
7572
7954
  await db.init();
7573
7955
  await loadGraphFromDB(graph, db);
@@ -7628,6 +8010,9 @@ async function syncGroup(group) {
7628
8010
  links
7629
8011
  };
7630
8012
  }
8013
+ init_db_manager();
8014
+ init_knowledge_graph();
8015
+ init_graph_from_db();
7631
8016
  async function queryGroup(group, query, limit = 20) {
7632
8017
  const registry = loadRegistry();
7633
8018
  const perRepo = [];
@@ -7635,10 +8020,10 @@ async function queryGroup(group, query, limit = 20) {
7635
8020
  for (const member of group.members) {
7636
8021
  const regEntry = registry.find((r) => r.name === member.registryName);
7637
8022
  if (!regEntry) continue;
7638
- const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
7639
- if (!fs24.existsSync(dbPath)) continue;
8023
+ const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
8024
+ if (!fs26.existsSync(dbPath)) continue;
7640
8025
  const graph = createKnowledgeGraph();
7641
- const db = new DbManager(dbPath);
8026
+ const db = new DbManager(dbPath, true);
7642
8027
  try {
7643
8028
  await db.init();
7644
8029
  await loadGraphFromDB(graph, db);
@@ -8136,22 +8521,22 @@ function suggestTests(graph, symbolName) {
8136
8521
  const callPaths = [];
8137
8522
  const pathQueue = [{ id: targetId, path: [symbolName], depth: 0 }];
8138
8523
  while (pathQueue.length > 0 && callPaths.length < 5) {
8139
- const { id, path: path32, depth } = pathQueue.shift();
8524
+ const { id, path: path33, depth } = pathQueue.shift();
8140
8525
  let hasCallers2 = false;
8141
8526
  for (const edge of graph.findEdgesTo(id)) {
8142
8527
  if (edge.kind !== "calls") continue;
8143
8528
  const callerNode = graph.getNode(edge.source);
8144
8529
  if (!callerNode) continue;
8145
8530
  hasCallers2 = true;
8146
- const newPath = [callerNode.name, ...path32];
8531
+ const newPath = [callerNode.name, ...path33];
8147
8532
  if (depth + 1 >= 3 || callPaths.length >= 5) {
8148
8533
  if (callPaths.length < 5) callPaths.push(newPath);
8149
8534
  continue;
8150
8535
  }
8151
8536
  pathQueue.push({ id: edge.source, path: newPath, depth: depth + 1 });
8152
8537
  }
8153
- if (!hasCallers2 && path32.length > 1) {
8154
- callPaths.push(path32);
8538
+ if (!hasCallers2 && path33.length > 1) {
8539
+ callPaths.push(path33);
8155
8540
  }
8156
8541
  }
8157
8542
  if (callPaths.length === 0) {
@@ -8284,11 +8669,30 @@ function summarizeCluster(graph, cluster) {
8284
8669
  }
8285
8670
 
8286
8671
  // src/mcp-server/server.ts
8672
+ function compact(obj) {
8673
+ return JSON.stringify(obj, (_key, value) => value === null || value === void 0 ? void 0 : value);
8674
+ }
8287
8675
  function createMcpServer(graph, repoName, workspaceRoot) {
8288
8676
  const server = new Server(
8289
8677
  { name: "code-intel", version: "0.1.0" },
8290
8678
  { capabilities: { tools: {}, resources: {} } }
8291
8679
  );
8680
+ let bm25Index = null;
8681
+ function ensureBm25Index() {
8682
+ if (bm25Index) return bm25Index;
8683
+ if (!workspaceRoot) return null;
8684
+ try {
8685
+ const idx = new Bm25Index(getBm25DbPath(workspaceRoot));
8686
+ idx.load();
8687
+ bm25Index = idx;
8688
+ return bm25Index;
8689
+ } catch {
8690
+ return null;
8691
+ }
8692
+ }
8693
+ if (workspaceRoot) {
8694
+ setImmediate(() => ensureBm25Index());
8695
+ }
8292
8696
  const _tokenProp = {
8293
8697
  _token: { type: "string", description: "Required if CODE_INTEL_TOKEN is configured" }
8294
8698
  };
@@ -8308,13 +8712,15 @@ function createMcpServer(graph, repoName, workspaceRoot) {
8308
8712
  // ── Search & inspect ─────────────────────────────────────────────────
8309
8713
  {
8310
8714
  name: "search",
8311
- description: "BM25 keyword search across all indexed symbols \u2014 functions, classes, files, routes, etc.",
8715
+ description: "BM25 keyword search across all indexed symbols \u2014 functions, classes, files, routes, etc. Optionally scope to a specific repo or group.",
8312
8716
  inputSchema: {
8313
8717
  type: "object",
8314
8718
  properties: {
8315
8719
  query: { type: "string", description: "Search query (symbol name, keyword, or partial match)" },
8316
8720
  offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
8317
- limit: { type: "number", description: "Max results per page (default: 50, max: 500)" },
8721
+ limit: { type: "number", description: "Max results per page (default: 10, max: 500)" },
8722
+ repo: { type: "string", description: "Scope search to a specific indexed repo name (optional; defaults to current repo)" },
8723
+ group: { type: "string", description: "Scope search across all repos in a group via cross-repo RRF merge (optional; overrides repo)" },
8318
8724
  ..._tokenProp
8319
8725
  },
8320
8726
  required: ["query"]
@@ -8344,7 +8750,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
8344
8750
  enum: ["callers", "callees", "both"],
8345
8751
  description: "Which direction to trace \u2014 callers (who depends on it), callees (what it depends on), or both (default: both)"
8346
8752
  },
8347
- max_hops: { type: "number", description: "Maximum traversal depth (default: 5)" },
8753
+ max_hops: { type: "number", description: "Maximum traversal depth (default: 2, max: 10)" },
8348
8754
  ..._tokenProp
8349
8755
  },
8350
8756
  required: ["target"]
@@ -8358,7 +8764,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
8358
8764
  properties: {
8359
8765
  file_path: { type: "string", description: 'File path (partial match is supported, e.g. "auth/login.ts")' },
8360
8766
  offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
8361
- limit: { type: "number", description: "Max results per page (default: 50, max: 500)" },
8767
+ limit: { type: "number", description: "Max results per page (default: 10, max: 500)" },
8362
8768
  ..._tokenProp
8363
8769
  },
8364
8770
  required: ["file_path"]
@@ -8389,7 +8795,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
8389
8795
  description: "Filter by node kind: function | class | interface | method | type_alias | constant | enum (optional)"
8390
8796
  },
8391
8797
  offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
8392
- limit: { type: "number", description: "Max results per page (default: 50, max: 500)" },
8798
+ limit: { type: "number", description: "Max results per page (default: 10, max: 500)" },
8393
8799
  ..._tokenProp
8394
8800
  }
8395
8801
  }
@@ -8407,7 +8813,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
8407
8813
  type: "object",
8408
8814
  properties: {
8409
8815
  offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
8410
- limit: { type: "number", description: "Max clusters per page (default: 50, max: 500)" },
8816
+ limit: { type: "number", description: "Max clusters per page (default: 10, max: 500)" },
8411
8817
  ..._tokenProp
8412
8818
  }
8413
8819
  }
@@ -8419,7 +8825,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
8419
8825
  type: "object",
8420
8826
  properties: {
8421
8827
  offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
8422
- limit: { type: "number", description: "Max flows per page (default: 50, max: 500)" },
8828
+ limit: { type: "number", description: "Max flows per page (default: 10, max: 500)" },
8423
8829
  ..._tokenProp
8424
8830
  }
8425
8831
  }
@@ -8573,7 +8979,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
8573
8979
  },
8574
8980
  maxHops: {
8575
8981
  type: "number",
8576
- description: "Maximum BFS depth for blast radius (default: 5)"
8982
+ description: "Maximum BFS depth for blast radius (default: 2, max: 10)"
8577
8983
  },
8578
8984
  ..._tokenProp
8579
8985
  }
@@ -8701,31 +9107,51 @@ function createMcpServer(graph, repoName, workspaceRoot) {
8701
9107
  const providedToken = a._token;
8702
9108
  if (providedToken !== expectedToken) {
8703
9109
  return {
8704
- content: [{ type: "text", text: JSON.stringify({ error: "Unauthorized: invalid or missing CODE_INTEL_TOKEN" }) }],
9110
+ content: [{ type: "text", text: compact({ error: "Unauthorized: invalid or missing CODE_INTEL_TOKEN" }) }],
8705
9111
  isError: true
8706
9112
  };
8707
9113
  }
8708
9114
  }
8709
9115
  const startMs = Date.now();
8710
- const dispatch = () => dispatchTool(name, a, graph, repoName, workspaceRoot);
9116
+ const dispatch = () => dispatchTool(name, a, graph, repoName, workspaceRoot, ensureBm25Index);
9117
+ const MCP_TIMEOUT_MS = parseInt(process.env["CODE_INTEL_MCP_TIMEOUT_MS"] ?? "30000", 10);
9118
+ let timeoutHandle = null;
9119
+ let timedOut = false;
9120
+ const timeoutPromise = new Promise((_, reject) => {
9121
+ timeoutHandle = setTimeout(() => {
9122
+ timedOut = true;
9123
+ reject(new Error(`MCP tool '${name}' timed out after ${MCP_TIMEOUT_MS}ms`));
9124
+ }, MCP_TIMEOUT_MS);
9125
+ });
8711
9126
  let result;
8712
9127
  let status = "success";
8713
9128
  try {
8714
9129
  if (isTracingEnabled()) {
8715
- result = await withSpan(
8716
- `mcp.tool.${name}`,
8717
- sanitizeAttrs({ "mcp.tool": name, "mcp.repo": repoName }),
8718
- dispatch
8719
- );
9130
+ result = await Promise.race([
9131
+ withSpan(
9132
+ `mcp.tool.${name}`,
9133
+ sanitizeAttrs({ "mcp.tool": name, "mcp.repo": repoName }),
9134
+ dispatch
9135
+ ),
9136
+ timeoutPromise
9137
+ ]);
8720
9138
  } else {
8721
- result = await dispatch();
9139
+ result = await Promise.race([dispatch(), timeoutPromise]);
8722
9140
  }
8723
9141
  if (result.isError) status = "error";
8724
9142
  } catch (err) {
8725
9143
  status = "error";
8726
9144
  mcpToolCallsTotal.inc({ tool: name, status });
8727
9145
  mcpToolDurationSeconds.observe({ tool: name }, (Date.now() - startMs) / 1e3);
9146
+ if (timedOut) {
9147
+ return {
9148
+ content: [{ type: "text", text: compact({ truncated: true, reason: `Tool '${name}' timed out after ${MCP_TIMEOUT_MS}ms`, partialResults: [] }) }],
9149
+ isError: false
9150
+ };
9151
+ }
8728
9152
  throw err;
9153
+ } finally {
9154
+ if (timeoutHandle) clearTimeout(timeoutHandle);
8729
9155
  }
8730
9156
  mcpToolCallsTotal.inc({ tool: name, status });
8731
9157
  mcpToolDurationSeconds.observe({ tool: name }, (Date.now() - startMs) / 1e3);
@@ -8734,7 +9160,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
8734
9160
  registerResources(server, graph, repoName);
8735
9161
  return server;
8736
9162
  }
8737
- async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9163
+ async function dispatchTool(name, a, graph, repoName, workspaceRoot, bm25Resolver) {
8738
9164
  switch (name) {
8739
9165
  // ── repos ──────────────────────────────────────────────────────────────
8740
9166
  case "repos": {
@@ -8742,10 +9168,8 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8742
9168
  return {
8743
9169
  content: [{
8744
9170
  type: "text",
8745
- text: JSON.stringify(
8746
- registry.map((r) => ({ name: r.name, path: r.path, indexedAt: r.indexedAt, stats: r.stats })),
8747
- null,
8748
- 2
9171
+ text: compact(
9172
+ registry.map((r) => ({ name: r.name, path: r.path, indexedAt: r.indexedAt, stats: r.stats }))
8749
9173
  )
8750
9174
  }]
8751
9175
  };
@@ -8773,13 +9197,13 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8773
9197
  return {
8774
9198
  content: [{
8775
9199
  type: "text",
8776
- text: JSON.stringify({
9200
+ text: compact({
8777
9201
  repo: repoName,
8778
9202
  stats: graph.size,
8779
9203
  nodeCounts: kindCounts,
8780
9204
  edgeCounts,
8781
9205
  health
8782
- }, null, 2)
9206
+ })
8783
9207
  }]
8784
9208
  };
8785
9209
  }
@@ -8787,15 +9211,59 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8787
9211
  case "search": {
8788
9212
  const query = a.query;
8789
9213
  const offset = a.offset ?? 0;
8790
- const effectiveLimit = Math.min(a.limit ?? 50, 500);
9214
+ const effectiveLimit = Math.min(a.limit ?? 10, 500);
9215
+ if (a.group) {
9216
+ const grp = loadGroup(a.group);
9217
+ if (!grp) {
9218
+ return { content: [{ type: "text", text: `Group "${a.group}" not found. Use list_groups to see available groups.` }] };
9219
+ }
9220
+ const { perRepo, merged } = await queryGroup(grp, query, effectiveLimit + offset);
9221
+ const paged = merged.slice(offset, offset + effectiveLimit);
9222
+ return {
9223
+ content: [{
9224
+ type: "text",
9225
+ text: compact({
9226
+ results: paged,
9227
+ perRepo,
9228
+ searchMode: "bm25-cross-repo",
9229
+ group: a.group,
9230
+ total: merged.length,
9231
+ offset,
9232
+ limit: effectiveLimit,
9233
+ hasMore: offset + effectiveLimit < merged.length
9234
+ })
9235
+ }]
9236
+ };
9237
+ }
9238
+ const repoGraph = a.repo ? await (async () => {
9239
+ const registry = loadRegistry();
9240
+ const entry = registry.find((r) => r.name === a.repo || r.path === a.repo);
9241
+ if (!entry) return graph;
9242
+ const { DbManager: DbMgr } = await Promise.resolve().then(() => (init_db_manager(), db_manager_exports));
9243
+ const { loadGraphFromDB: loadG } = await Promise.resolve().then(() => (init_graph_from_db(), graph_from_db_exports));
9244
+ const { createKnowledgeGraph: createG } = await Promise.resolve().then(() => (init_knowledge_graph(), knowledge_graph_exports));
9245
+ const dbPath = path32.join(entry.path, ".code-intel", "graph.db");
9246
+ if (!fs26.existsSync(dbPath)) return graph;
9247
+ const db = new DbMgr(dbPath, true);
9248
+ await db.init();
9249
+ const g = createG();
9250
+ await loadG(g, db);
9251
+ db.close();
9252
+ return g;
9253
+ })() : graph;
8791
9254
  const vdbPath = workspaceRoot ? getVectorDbPath(workspaceRoot) : void 0;
8792
9255
  const fetchLimit = Math.min(offset + effectiveLimit, 500);
8793
- const { results: allResults, searchMode } = await hybridSearch(graph, query, fetchLimit, { vectorDbPath: vdbPath });
9256
+ const bm25 = !a.repo || a.repo === repoName ? bm25Resolver ? bm25Resolver() : null : null;
9257
+ const bm25Results = bm25 ? bm25.search(query, fetchLimit * 3) : void 0;
9258
+ const { results: allResults, searchMode } = await hybridSearch(repoGraph, query, fetchLimit, {
9259
+ vectorDbPath: vdbPath,
9260
+ bm25Results: bm25Results ?? void 0
9261
+ });
8794
9262
  const total = allResults.length;
8795
9263
  const results = allResults.slice(offset, offset + effectiveLimit);
8796
9264
  const hasMore = offset + effectiveLimit < total;
8797
9265
  const suggestNextTools = [];
8798
- const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] !== "false";
9266
+ const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] === "true";
8799
9267
  if (suggestEnabled && results.length > 0) {
8800
9268
  const topName = results[0].name;
8801
9269
  suggestNextTools.push(
@@ -8806,15 +9274,16 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8806
9274
  return {
8807
9275
  content: [{
8808
9276
  type: "text",
8809
- text: JSON.stringify({
9277
+ text: compact({
8810
9278
  results,
8811
9279
  searchMode,
9280
+ repo: a.repo ?? repoName,
8812
9281
  total,
8813
9282
  offset,
8814
9283
  limit: effectiveLimit,
8815
9284
  hasMore,
8816
9285
  ...suggestEnabled ? { suggested_next_tools: suggestNextTools } : {}
8817
- }, null, 2)
9286
+ })
8818
9287
  }]
8819
9288
  };
8820
9289
  }
@@ -8836,7 +9305,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8836
9305
  file: graph.getNode(e.target)?.filePath
8837
9306
  }));
8838
9307
  const cluster = incoming.filter((e) => e.kind === "belongs_to").map((e) => graph.getNode(e.target)?.name)[0];
8839
- const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] !== "false";
9308
+ const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] === "true";
8840
9309
  const suggestNextTools = [];
8841
9310
  if (suggestEnabled) {
8842
9311
  const topCallerName = callers[0]?.name;
@@ -8848,7 +9317,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8848
9317
  return {
8849
9318
  content: [{
8850
9319
  type: "text",
8851
- text: JSON.stringify({
9320
+ text: compact({
8852
9321
  node: {
8853
9322
  id: node.id,
8854
9323
  kind: node.kind,
@@ -8871,7 +9340,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8871
9340
  cluster,
8872
9341
  content: node.content?.slice(0, 500),
8873
9342
  ...suggestEnabled ? { suggested_next_tools: suggestNextTools } : {}
8874
- }, null, 2)
9343
+ })
8875
9344
  }]
8876
9345
  };
8877
9346
  }
@@ -8879,7 +9348,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8879
9348
  case "blast_radius": {
8880
9349
  const target = a.target;
8881
9350
  const direction = a.direction ?? "both";
8882
- const maxHops = a.max_hops ?? 5;
9351
+ const maxHops = a.max_hops ?? 2;
8883
9352
  const node = findNodeByName(graph, target);
8884
9353
  if (!node) return { content: [{ type: "text", text: `Symbol "${target}" not found.` }] };
8885
9354
  const affected = /* @__PURE__ */ new Set();
@@ -8906,7 +9375,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8906
9375
  return n ? { id, name: n.name, kind: n.kind, filePath: n.filePath } : { id };
8907
9376
  });
8908
9377
  const risk = affected.size > 10 ? "HIGH" : affected.size > 5 ? "MEDIUM" : "LOW";
8909
- const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] !== "false";
9378
+ const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] === "true";
8910
9379
  const suggestNextTools = [];
8911
9380
  if (suggestEnabled) {
8912
9381
  const highestRiskSymbol = node.name;
@@ -8919,13 +9388,13 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8919
9388
  return {
8920
9389
  content: [{
8921
9390
  type: "text",
8922
- text: JSON.stringify({
9391
+ text: compact({
8923
9392
  target: node.name,
8924
9393
  affectedCount: affected.size,
8925
9394
  riskLevel: risk,
8926
9395
  affected: affectedDetails,
8927
9396
  ...suggestEnabled ? { suggested_next_tools: suggestNextTools } : {}
8928
- }, null, 2)
9397
+ })
8929
9398
  }]
8930
9399
  };
8931
9400
  }
@@ -8933,7 +9402,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8933
9402
  case "file_symbols": {
8934
9403
  const filePath = a.file_path;
8935
9404
  const offset = a.offset ?? 0;
8936
- const effectiveLimit = Math.min(a.limit ?? 50, 500);
9405
+ const effectiveLimit = Math.min(a.limit ?? 10, 500);
8937
9406
  const allMatches = [];
8938
9407
  for (const node of graph.allNodes()) {
8939
9408
  if (node.filePath && node.filePath.includes(filePath)) {
@@ -8950,7 +9419,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8950
9419
  return {
8951
9420
  content: [{
8952
9421
  type: "text",
8953
- text: JSON.stringify({ symbols: matches, total, offset, limit: effectiveLimit, hasMore }, null, 2)
9422
+ text: compact({ symbols: matches, total, offset, limit: effectiveLimit, hasMore })
8954
9423
  }]
8955
9424
  };
8956
9425
  }
@@ -8991,7 +9460,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8991
9460
  return {
8992
9461
  content: [{
8993
9462
  type: "text",
8994
- text: JSON.stringify({ from: fromName, to: toName, hops: foundPath.length - 1, path: pathDetails }, null, 2)
9463
+ text: compact({ from: fromName, to: toName, hops: foundPath.length - 1, path: pathDetails })
8995
9464
  }]
8996
9465
  };
8997
9466
  }
@@ -8999,7 +9468,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8999
9468
  case "list_exports": {
9000
9469
  const kindFilter = a.kind;
9001
9470
  const offset = a.offset ?? 0;
9002
- const effectiveLimit = Math.min(a.limit ?? 50, 500);
9471
+ const effectiveLimit = Math.min(a.limit ?? 10, 500);
9003
9472
  const allExports = [];
9004
9473
  for (const node of graph.allNodes()) {
9005
9474
  if (!node.exported) continue;
@@ -9012,7 +9481,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9012
9481
  return {
9013
9482
  content: [{
9014
9483
  type: "text",
9015
- text: JSON.stringify({ exports: exports$1, total, offset, limit: effectiveLimit, hasMore }, null, 2)
9484
+ text: compact({ exports: exports$1, total, offset, limit: effectiveLimit, hasMore })
9016
9485
  }]
9017
9486
  };
9018
9487
  }
@@ -9024,12 +9493,12 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9024
9493
  routes.push({ name: node.name, filePath: node.filePath, startLine: node.startLine });
9025
9494
  }
9026
9495
  }
9027
- return { content: [{ type: "text", text: JSON.stringify(routes, null, 2) }] };
9496
+ return { content: [{ type: "text", text: compact(routes) }] };
9028
9497
  }
9029
9498
  // ── clusters ───────────────────────────────────────────────────────────
9030
9499
  case "clusters": {
9031
9500
  const offset = a.offset ?? 0;
9032
- const effectiveLimit = Math.min(a.limit ?? 50, 500);
9501
+ const effectiveLimit = Math.min(a.limit ?? 10, 500);
9033
9502
  const allClusters = [];
9034
9503
  for (const node of graph.allNodes()) {
9035
9504
  if (node.kind === "cluster") {
@@ -9056,14 +9525,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9056
9525
  return {
9057
9526
  content: [{
9058
9527
  type: "text",
9059
- text: JSON.stringify({ clusters, total, offset, limit: effectiveLimit, hasMore }, null, 2)
9528
+ text: compact({ clusters, total, offset, limit: effectiveLimit, hasMore })
9060
9529
  }]
9061
9530
  };
9062
9531
  }
9063
9532
  // ── flows ──────────────────────────────────────────────────────────────
9064
9533
  case "flows": {
9065
9534
  const offset = a.offset ?? 0;
9066
- const effectiveLimit = Math.min(a.limit ?? 50, 500);
9535
+ const effectiveLimit = Math.min(a.limit ?? 10, 500);
9067
9536
  const allFlows = [];
9068
9537
  for (const node of graph.allNodes()) {
9069
9538
  if (node.kind === "flow") {
@@ -9083,7 +9552,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9083
9552
  return {
9084
9553
  content: [{
9085
9554
  type: "text",
9086
- text: JSON.stringify({ flows, total, offset, limit: effectiveLimit, hasMore }, null, 2)
9555
+ text: compact({ flows, total, offset, limit: effectiveLimit, hasMore })
9087
9556
  }]
9088
9557
  };
9089
9558
  }
@@ -9113,7 +9582,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9113
9582
  for (const { filePath: changedFile, changedLines } of changedFiles) {
9114
9583
  for (const node of graph.allNodes()) {
9115
9584
  if (!node.filePath) continue;
9116
- const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot + path31.sep, "");
9585
+ const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot + path32.sep, "");
9117
9586
  const normChanged = changedFile.replace(/^a\/|^b\//, "");
9118
9587
  if (!normNode.endsWith(normChanged) && !normChanged.endsWith(normNode)) continue;
9119
9588
  if (node.startLine !== void 0 && node.endLine !== void 0) {
@@ -9151,14 +9620,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9151
9620
  return {
9152
9621
  content: [{
9153
9622
  type: "text",
9154
- text: JSON.stringify({
9623
+ text: compact({
9155
9624
  baseRef,
9156
9625
  changedFiles: changedFiles.map((f) => f.filePath),
9157
9626
  directlyChangedSymbols: changedSymbols,
9158
9627
  transitivelyAffectedSymbols: affectedSymbols,
9159
9628
  totalAffected: allAffected.size,
9160
9629
  riskLevel: risk
9161
- }, null, 2)
9630
+ })
9162
9631
  }]
9163
9632
  };
9164
9633
  }
@@ -9166,14 +9635,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9166
9635
  case "query": {
9167
9636
  const gqlInput = a.gql;
9168
9637
  if (!gqlInput) {
9169
- return { content: [{ type: "text", text: JSON.stringify({ error: "Missing required parameter: gql" }) }], isError: true };
9638
+ return { content: [{ type: "text", text: compact({ error: "Missing required parameter: gql" }) }], isError: true };
9170
9639
  }
9171
9640
  const { parseGQL: parseGQL2, isGQLParseError: isGQLParseError2 } = await Promise.resolve().then(() => (init_gql_parser(), gql_parser_exports));
9172
9641
  const { executeGQL: executeGQL2 } = await Promise.resolve().then(() => (init_gql_executor(), gql_executor_exports));
9173
9642
  const ast = parseGQL2(gqlInput);
9174
9643
  if (isGQLParseError2(ast)) {
9175
9644
  return {
9176
- content: [{ type: "text", text: JSON.stringify({ error: `GQL parse error: ${ast.message}`, pos: ast.pos, expected: ast.expected, got: ast.got }) }],
9645
+ content: [{ type: "text", text: compact({ error: `GQL parse error: ${ast.message}`, pos: ast.pos, expected: ast.expected, got: ast.got }) }],
9177
9646
  isError: true
9178
9647
  };
9179
9648
  }
@@ -9184,7 +9653,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9184
9653
  return {
9185
9654
  content: [{
9186
9655
  type: "text",
9187
- text: JSON.stringify({
9656
+ text: compact({
9188
9657
  nodes: result.nodes,
9189
9658
  edges: result.edges,
9190
9659
  groups: result.groups,
@@ -9192,7 +9661,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9192
9661
  executionTimeMs: result.executionTimeMs,
9193
9662
  truncated: result.truncated,
9194
9663
  totalCount: result.totalCount
9195
- }, null, 2)
9664
+ })
9196
9665
  }]
9197
9666
  };
9198
9667
  }
@@ -9206,7 +9675,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9206
9675
  for (const node of graph.allNodes()) {
9207
9676
  if (node.name === nameMatch[1]) results.push(node);
9208
9677
  }
9209
- return { content: [{ type: "text", text: JSON.stringify({ deprecation: deprecationWarning, results }, null, 2) }] };
9678
+ return { content: [{ type: "text", text: compact({ deprecation: deprecationWarning, results }) }] };
9210
9679
  }
9211
9680
  const kindMatch = q?.match(/:\s*(\w+)/);
9212
9681
  if (kindMatch) {
@@ -9215,9 +9684,9 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9215
9684
  if (node.kind === kindMatch[1]) results.push(node);
9216
9685
  if (results.length >= 50) break;
9217
9686
  }
9218
- return { content: [{ type: "text", text: JSON.stringify({ deprecation: deprecationWarning, results }, null, 2) }] };
9687
+ return { content: [{ type: "text", text: compact({ deprecation: deprecationWarning, results }) }] };
9219
9688
  }
9220
- return { content: [{ type: "text", text: JSON.stringify({ deprecation: deprecationWarning, error: "Query not recognized. Use name='X' or :kind syntax. Or use the query tool with GQL instead." }) }] };
9689
+ return { content: [{ type: "text", text: compact({ deprecation: deprecationWarning, error: "Query not recognized. Use name='X' or :kind syntax. Or use the query tool with GQL instead." }) }] };
9221
9690
  }
9222
9691
  // ── group_list ─────────────────────────────────────────────────────────
9223
9692
  case "group_list": {
@@ -9225,16 +9694,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9225
9694
  if (groupName) {
9226
9695
  const group = loadGroup(groupName);
9227
9696
  if (!group) return { content: [{ type: "text", text: `Group "${groupName}" not found.` }] };
9228
- return { content: [{ type: "text", text: JSON.stringify(group, null, 2) }] };
9697
+ return { content: [{ type: "text", text: compact(group) }] };
9229
9698
  }
9230
9699
  const groups = listGroups();
9231
9700
  return {
9232
9701
  content: [{
9233
9702
  type: "text",
9234
- text: JSON.stringify(
9235
- groups.map((g) => ({ name: g.name, createdAt: g.createdAt, lastSync: g.lastSync, memberCount: g.members.length, members: g.members })),
9236
- null,
9237
- 2
9703
+ text: compact(
9704
+ groups.map((g) => ({ name: g.name, createdAt: g.createdAt, lastSync: g.lastSync, memberCount: g.members.length, members: g.members }))
9238
9705
  )
9239
9706
  }]
9240
9707
  };
@@ -9252,14 +9719,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9252
9719
  return {
9253
9720
  content: [{
9254
9721
  type: "text",
9255
- text: JSON.stringify({
9722
+ text: compact({
9256
9723
  groupName: result.groupName,
9257
9724
  syncedAt: result.syncedAt,
9258
9725
  memberCount: result.memberCount,
9259
9726
  contractCount: result.contracts.length,
9260
9727
  linkCount: result.links.length,
9261
9728
  topLinks: result.links.slice(0, 20)
9262
- }, null, 2)
9729
+ })
9263
9730
  }]
9264
9731
  };
9265
9732
  }
@@ -9279,7 +9746,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9279
9746
  return {
9280
9747
  content: [{
9281
9748
  type: "text",
9282
- text: JSON.stringify({ syncedAt: result.syncedAt, contracts, links }, null, 2)
9749
+ text: compact({ syncedAt: result.syncedAt, contracts, links })
9283
9750
  }]
9284
9751
  };
9285
9752
  }
@@ -9294,7 +9761,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9294
9761
  return {
9295
9762
  content: [{
9296
9763
  type: "text",
9297
- text: JSON.stringify({ query, merged, perRepo }, null, 2)
9764
+ text: compact({ query, merged, perRepo })
9298
9765
  }]
9299
9766
  };
9300
9767
  }
@@ -9326,12 +9793,12 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9326
9793
  return {
9327
9794
  content: [{
9328
9795
  type: "text",
9329
- text: JSON.stringify({
9796
+ text: compact({
9330
9797
  group: groupName,
9331
9798
  lastSync: group.lastSync ?? null,
9332
9799
  syncAgeMinutes: syncAge,
9333
9800
  members: memberStatus
9334
- }, null, 2)
9801
+ })
9335
9802
  }]
9336
9803
  };
9337
9804
  }
@@ -9340,11 +9807,11 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9340
9807
  const fromName = a.from;
9341
9808
  const toName = a.to;
9342
9809
  const result = explainRelationship(graph, fromName, toName);
9343
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
9810
+ return { content: [{ type: "text", text: compact(result) }] };
9344
9811
  }
9345
9812
  // ── pr_impact ──────────────────────────────────────────────────────────
9346
9813
  case "pr_impact": {
9347
- const maxHops = a.maxHops ?? 5;
9814
+ const maxHops = a.maxHops ?? 2;
9348
9815
  let changedFiles = a.changedFiles ?? [];
9349
9816
  if (a.diff && typeof a.diff === "string") {
9350
9817
  const diffFiles = parseDiffFiles(a.diff);
@@ -9354,37 +9821,37 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9354
9821
  return {
9355
9822
  content: [{
9356
9823
  type: "text",
9357
- text: JSON.stringify({ error: 'No changed files provided. Supply "changedFiles" or "diff".' })
9824
+ text: compact({ error: 'No changed files provided. Supply "changedFiles" or "diff".' })
9358
9825
  }]
9359
9826
  };
9360
9827
  }
9361
9828
  const result = computePRImpact(graph, changedFiles, maxHops);
9362
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
9829
+ return { content: [{ type: "text", text: compact(result) }] };
9363
9830
  }
9364
9831
  // ── similar_symbols ────────────────────────────────────────────────────
9365
9832
  case "similar_symbols": {
9366
9833
  const symbolName = a.symbol;
9367
9834
  const limit = a.limit ?? 10;
9368
9835
  const result = findSimilarSymbols(graph, symbolName, limit);
9369
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
9836
+ return { content: [{ type: "text", text: compact(result) }] };
9370
9837
  }
9371
9838
  // ── health_report ──────────────────────────────────────────────────────
9372
9839
  case "health_report": {
9373
9840
  const scope = a.scope ?? ".";
9374
9841
  const result = computeHealthReport(graph, scope);
9375
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
9842
+ return { content: [{ type: "text", text: compact(result) }] };
9376
9843
  }
9377
9844
  // ── suggest_tests ──────────────────────────────────────────────────────
9378
9845
  case "suggest_tests": {
9379
9846
  const sym = a.symbol;
9380
9847
  const result = suggestTests(graph, sym);
9381
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
9848
+ return { content: [{ type: "text", text: compact(result) }] };
9382
9849
  }
9383
9850
  // ── cluster_summary ────────────────────────────────────────────────────
9384
9851
  case "cluster_summary": {
9385
9852
  const cluster = a.cluster;
9386
9853
  const result = summarizeCluster(graph, cluster);
9387
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
9854
+ return { content: [{ type: "text", text: compact(result) }] };
9388
9855
  }
9389
9856
  case "deprecated_usage": {
9390
9857
  const scope = a.scope;
@@ -9392,7 +9859,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9392
9859
  const detector = new DeprecatedDetector2();
9393
9860
  detector.tagDeprecated(graph);
9394
9861
  const findings = detector.detect(graph, scope);
9395
- return { content: [{ type: "text", text: JSON.stringify({ findings, total: findings.length }, null, 2) }] };
9862
+ return { content: [{ type: "text", text: compact({ findings, total: findings.length }) }] };
9396
9863
  }
9397
9864
  // ── complexity_hotspots ────────────────────────────────────────────────
9398
9865
  case "complexity_hotspots": {
@@ -9400,7 +9867,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9400
9867
  const scope = a.scope;
9401
9868
  const limit = typeof a.limit === "number" ? a.limit : 20;
9402
9869
  const hotspots = computeComplexity2(graph, scope).slice(0, limit);
9403
- return { content: [{ type: "text", text: JSON.stringify({ hotspots, total: hotspots.length }, null, 2) }] };
9870
+ return { content: [{ type: "text", text: compact({ hotspots, total: hotspots.length }) }] };
9404
9871
  }
9405
9872
  // ── coverage_gaps ──────────────────────────────────────────────────────
9406
9873
  case "coverage_gaps": {
@@ -9412,12 +9879,12 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9412
9879
  return {
9413
9880
  content: [{
9414
9881
  type: "text",
9415
- text: JSON.stringify({
9882
+ text: compact({
9416
9883
  untestedByRisk,
9417
9884
  coveragePct: summary.coveragePct,
9418
9885
  totalExported: summary.totalExported,
9419
9886
  testedExported: summary.testedExported
9420
- }, null, 2)
9887
+ })
9421
9888
  }]
9422
9889
  };
9423
9890
  }
@@ -9428,7 +9895,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9428
9895
  const scope = a.scope;
9429
9896
  const includeTestFiles = a.includeTestFiles ?? false;
9430
9897
  const findings = scanner.scan(graph, { scope, includeTestFiles });
9431
- return { content: [{ type: "text", text: JSON.stringify({ findings, total: findings.length }, null, 2) }] };
9898
+ return { content: [{ type: "text", text: compact({ findings, total: findings.length }) }] };
9432
9899
  }
9433
9900
  // ── vulnerability_scan ─────────────────────────────────────────────────
9434
9901
  case "vulnerability_scan": {
@@ -9441,7 +9908,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
9441
9908
  const minRank = sevRank[minSev] ?? 1;
9442
9909
  let findings = detector.detect(graph, { scope, types });
9443
9910
  findings = findings.filter((f) => (sevRank[f.severity] ?? 1) >= minRank);
9444
- return { content: [{ type: "text", text: JSON.stringify({ findings, total: findings.length }, null, 2) }] };
9911
+ return { content: [{ type: "text", text: compact({ findings, total: findings.length }) }] };
9445
9912
  }
9446
9913
  default:
9447
9914
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
@@ -9462,21 +9929,21 @@ function registerResources(server, graph, repoName) {
9462
9929
  for (const node of graph.allNodes()) {
9463
9930
  kindCounts[node.kind] = (kindCounts[node.kind] ?? 0) + 1;
9464
9931
  }
9465
- return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify({ repo: repoName, stats: graph.size, nodeCounts: kindCounts }) }] };
9932
+ return { contents: [{ uri, mimeType: "application/json", text: compact({ repo: repoName, stats: graph.size, nodeCounts: kindCounts }) }] };
9466
9933
  }
9467
9934
  if (uri.endsWith("/clusters")) {
9468
9935
  const clusters = [];
9469
9936
  for (const node of graph.allNodes()) {
9470
9937
  if (node.kind === "cluster") clusters.push({ id: node.id, name: node.name, memberCount: node.metadata?.memberCount });
9471
9938
  }
9472
- return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(clusters) }] };
9939
+ return { contents: [{ uri, mimeType: "application/json", text: compact(clusters) }] };
9473
9940
  }
9474
9941
  if (uri.endsWith("/flows")) {
9475
9942
  const flows = [];
9476
9943
  for (const node of graph.allNodes()) {
9477
9944
  if (node.kind === "flow") flows.push({ id: node.id, name: node.name, steps: node.metadata?.steps, entryPoint: node.metadata?.entryPoint });
9478
9945
  }
9479
- return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(flows) }] };
9946
+ return { contents: [{ uri, mimeType: "application/json", text: compact(flows) }] };
9480
9947
  }
9481
9948
  throw new Error(`Unknown resource: ${uri}`);
9482
9949
  });
@@ -9526,6 +9993,8 @@ function parseDiff(diffText) {
9526
9993
  return result;
9527
9994
  }
9528
9995
  init_group_registry();
9996
+ init_knowledge_graph();
9997
+ init_graph_from_db();
9529
9998
  init_logger();
9530
9999
  init_codes();
9531
10000
  init_middleware();
@@ -9536,8 +10005,8 @@ var STUCK_THRESHOLD_MINUTES = 30;
9536
10005
  var JobsDB = class {
9537
10006
  db;
9538
10007
  constructor(dbPath) {
9539
- fs24.mkdirSync(path31.dirname(dbPath), { recursive: true });
9540
- this.db = new Database3(dbPath);
10008
+ fs26.mkdirSync(path32.dirname(dbPath), { recursive: true });
10009
+ this.db = new Database2(dbPath);
9541
10010
  this.db.pragma("journal_mode = WAL");
9542
10011
  this.db.pragma("foreign_keys = ON");
9543
10012
  this.createTables();
@@ -9678,7 +10147,7 @@ var JobsDB = class {
9678
10147
  }
9679
10148
  };
9680
10149
  function getJobsDBPath() {
9681
- return path31.join(os13.homedir(), ".code-intel", "jobs.db");
10150
+ return path32.join(os13.homedir(), ".code-intel", "jobs.db");
9682
10151
  }
9683
10152
  var _jobsDB = null;
9684
10153
  function getOrCreateJobsDB() {
@@ -9770,7 +10239,7 @@ var BACKUP_VERSION = "1.0";
9770
10239
  var ALGORITHM = "aes-256-gcm";
9771
10240
  var IV_LENGTH = 16;
9772
10241
  function getBackupDir() {
9773
- return path31.join(os13.homedir(), ".code-intel", "backups");
10242
+ return path32.join(os13.homedir(), ".code-intel", "backups");
9774
10243
  }
9775
10244
  function getBackupKey() {
9776
10245
  const keyHex = process.env["CODE_INTEL_BACKUP_KEY"];
@@ -9801,30 +10270,30 @@ var BackupService = class {
9801
10270
  constructor(backupDir) {
9802
10271
  this.backupDir = backupDir ?? getBackupDir();
9803
10272
  this.key = getBackupKey();
9804
- fs24.mkdirSync(this.backupDir, { recursive: true });
10273
+ fs26.mkdirSync(this.backupDir, { recursive: true });
9805
10274
  }
9806
10275
  /**
9807
10276
  * Create a backup for a repository.
9808
10277
  * Returns the backup entry.
9809
10278
  */
9810
10279
  createBackup(repoPath) {
9811
- const codeIntelDir = path31.join(repoPath, ".code-intel");
10280
+ const codeIntelDir = path32.join(repoPath, ".code-intel");
9812
10281
  const id = v4();
9813
10282
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
9814
10283
  const filesToBackup = [];
9815
10284
  const candidates = ["graph.db", "vector.db", "meta.json"];
9816
10285
  for (const f of candidates) {
9817
- const fp = path31.join(codeIntelDir, f);
9818
- if (fs24.existsSync(fp)) {
10286
+ const fp = path32.join(codeIntelDir, f);
10287
+ if (fs26.existsSync(fp)) {
9819
10288
  filesToBackup.push({ name: f, localPath: fp });
9820
10289
  }
9821
10290
  }
9822
- const registryPath = path31.join(os13.homedir(), ".code-intel", "registry.json");
9823
- if (fs24.existsSync(registryPath)) {
10291
+ const registryPath = path32.join(os13.homedir(), ".code-intel", "registry.json");
10292
+ if (fs26.existsSync(registryPath)) {
9824
10293
  filesToBackup.push({ name: "registry.json", localPath: registryPath });
9825
10294
  }
9826
- const usersDbPath = path31.join(os13.homedir(), ".code-intel", "users.db");
9827
- if (fs24.existsSync(usersDbPath)) {
10295
+ const usersDbPath = path32.join(os13.homedir(), ".code-intel", "users.db");
10296
+ if (fs26.existsSync(usersDbPath)) {
9828
10297
  filesToBackup.push({ name: "users.db", localPath: usersDbPath });
9829
10298
  }
9830
10299
  if (filesToBackup.length === 0) {
@@ -9835,7 +10304,7 @@ var BackupService = class {
9835
10304
  createdAt,
9836
10305
  version: BACKUP_VERSION,
9837
10306
  files: filesToBackup.map((f) => {
9838
- const data = fs24.readFileSync(f.localPath);
10307
+ const data = fs26.readFileSync(f.localPath);
9839
10308
  return {
9840
10309
  name: f.name,
9841
10310
  sha256: crypto5.createHash("sha256").update(data).digest("hex"),
@@ -9849,7 +10318,7 @@ var BackupService = class {
9849
10318
  manifestLenBuf.writeUInt32BE(manifestBuf.length, 0);
9850
10319
  parts.push(manifestLenBuf, manifestBuf);
9851
10320
  for (const f of filesToBackup) {
9852
- const data = fs24.readFileSync(f.localPath);
10321
+ const data = fs26.readFileSync(f.localPath);
9853
10322
  const nameBuf = Buffer.from(f.name, "utf-8");
9854
10323
  const nameLenBuf = Buffer.alloc(2);
9855
10324
  nameLenBuf.writeUInt16BE(nameBuf.length, 0);
@@ -9860,8 +10329,8 @@ var BackupService = class {
9860
10329
  const plaintext = Buffer.concat(parts);
9861
10330
  const encrypted = encryptBuffer(plaintext, this.key);
9862
10331
  const backupFileName = `backup-${id}.cib`;
9863
- const backupPath = path31.join(this.backupDir, backupFileName);
9864
- fs24.writeFileSync(backupPath, encrypted);
10332
+ const backupPath = path32.join(this.backupDir, backupFileName);
10333
+ fs26.writeFileSync(backupPath, encrypted);
9865
10334
  const entry = {
9866
10335
  id,
9867
10336
  createdAt,
@@ -9888,9 +10357,9 @@ var BackupService = class {
9888
10357
  async uploadToS3(entry) {
9889
10358
  const cfg = getS3Config();
9890
10359
  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.");
9891
- const fileName = path31.basename(entry.path);
10360
+ const fileName = path32.basename(entry.path);
9892
10361
  const s3Key = `${cfg.prefix}${fileName}`;
9893
- const body = fs24.readFileSync(entry.path);
10362
+ const body = fs26.readFileSync(entry.path);
9894
10363
  const result = await s3Request({ method: "PUT", cfg, key: s3Key, body });
9895
10364
  if (result.statusCode < 200 || result.statusCode >= 300) {
9896
10365
  throw new Error(`S3 upload failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
@@ -9907,8 +10376,8 @@ var BackupService = class {
9907
10376
  if (result.statusCode < 200 || result.statusCode >= 300) {
9908
10377
  throw new Error(`S3 download failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
9909
10378
  }
9910
- fs24.mkdirSync(path31.dirname(destPath), { recursive: true });
9911
- fs24.writeFileSync(destPath, Buffer.from(result.body, "binary"));
10379
+ fs26.mkdirSync(path32.dirname(destPath), { recursive: true });
10380
+ fs26.writeFileSync(destPath, Buffer.from(result.body, "binary"));
9912
10381
  }
9913
10382
  /**
9914
10383
  * List backup objects in S3 with the configured prefix.
@@ -9954,10 +10423,10 @@ var BackupService = class {
9954
10423
  if (!entry) {
9955
10424
  throw new Error(`Backup "${backupId}" not found.`);
9956
10425
  }
9957
- if (!fs24.existsSync(entry.path)) {
10426
+ if (!fs26.existsSync(entry.path)) {
9958
10427
  throw new Error(`Backup file not found at: ${entry.path}`);
9959
10428
  }
9960
- const encrypted = fs24.readFileSync(entry.path);
10429
+ const encrypted = fs26.readFileSync(entry.path);
9961
10430
  let plaintext;
9962
10431
  try {
9963
10432
  plaintext = decryptBuffer(encrypted, this.key);
@@ -9971,8 +10440,8 @@ var BackupService = class {
9971
10440
  offset += manifestLen;
9972
10441
  const manifest = JSON.parse(manifestStr);
9973
10442
  const restoreBase = targetRepoPath ?? entry.repoPath;
9974
- const codeIntelDir = path31.join(restoreBase, ".code-intel");
9975
- fs24.mkdirSync(codeIntelDir, { recursive: true });
10443
+ const codeIntelDir = path32.join(restoreBase, ".code-intel");
10444
+ fs26.mkdirSync(codeIntelDir, { recursive: true });
9976
10445
  for (const fileEntry of manifest.files) {
9977
10446
  const nameLen = plaintext.readUInt16BE(offset);
9978
10447
  offset += 2;
@@ -9989,18 +10458,18 @@ var BackupService = class {
9989
10458
  }
9990
10459
  let destPath;
9991
10460
  if (name === "registry.json" || name === "users.db") {
9992
- destPath = path31.join(os13.homedir(), ".code-intel", name);
10461
+ destPath = path32.join(os13.homedir(), ".code-intel", name);
9993
10462
  } else {
9994
- destPath = path31.join(codeIntelDir, name);
10463
+ destPath = path32.join(codeIntelDir, name);
9995
10464
  }
9996
- fs24.writeFileSync(destPath, data);
10465
+ fs26.writeFileSync(destPath, data);
9997
10466
  }
9998
10467
  }
9999
10468
  /**
10000
10469
  * Apply retention policy: keep N daily, M weekly, L monthly backups.
10001
10470
  */
10002
10471
  applyRetention(options = { daily: 7, weekly: 4, monthly: 12 }) {
10003
- const entries = this._loadIndex().filter((e) => fs24.existsSync(e.path)).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
10472
+ const entries = this._loadIndex().filter((e) => fs26.existsSync(e.path)).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
10004
10473
  const keep = /* @__PURE__ */ new Set();
10005
10474
  const now = /* @__PURE__ */ new Date();
10006
10475
  const dailyCutoff = new Date(now);
@@ -10030,7 +10499,7 @@ var BackupService = class {
10030
10499
  for (const e of entries) {
10031
10500
  if (!keep.has(e.id)) {
10032
10501
  try {
10033
- fs24.unlinkSync(e.path);
10502
+ fs26.unlinkSync(e.path);
10034
10503
  deleted++;
10035
10504
  } catch {
10036
10505
  }
@@ -10042,17 +10511,17 @@ var BackupService = class {
10042
10511
  }
10043
10512
  // ── Index helpers ──────────────────────────────────────────────────────────
10044
10513
  _indexPath() {
10045
- return path31.join(this.backupDir, "index.json");
10514
+ return path32.join(this.backupDir, "index.json");
10046
10515
  }
10047
10516
  _loadIndex() {
10048
10517
  try {
10049
- return JSON.parse(fs24.readFileSync(this._indexPath(), "utf-8"));
10518
+ return JSON.parse(fs26.readFileSync(this._indexPath(), "utf-8"));
10050
10519
  } catch {
10051
10520
  return [];
10052
10521
  }
10053
10522
  }
10054
10523
  _saveIndex(entries) {
10055
- fs24.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
10524
+ fs26.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
10056
10525
  }
10057
10526
  _appendIndex(entry) {
10058
10527
  const entries = this._loadIndex();
@@ -10845,11 +11314,11 @@ var openApiSpec = {
10845
11314
  };
10846
11315
 
10847
11316
  // src/http/app.ts
10848
- var __dirname$1 = path31.dirname(fileURLToPath(import.meta.url));
11317
+ var __dirname$1 = path32.dirname(fileURLToPath(import.meta.url));
10849
11318
  var WEB_DIST = (() => {
10850
- const bundled = path31.resolve(__dirname$1, "..", "web");
10851
- if (fs24.existsSync(bundled)) return bundled;
10852
- return path31.resolve(__dirname$1, "..", "..", "..", "web", "dist");
11319
+ const bundled = path32.resolve(__dirname$1, "..", "web");
11320
+ if (fs26.existsSync(bundled)) return bundled;
11321
+ return path32.resolve(__dirname$1, "..", "..", "..", "web", "dist");
10853
11322
  })();
10854
11323
  function getAllowedOrigins() {
10855
11324
  const env = process.env["CODE_INTEL_CORS_ORIGINS"];
@@ -10858,13 +11327,17 @@ function getAllowedOrigins() {
10858
11327
  }
10859
11328
  function createDefaultLimiter() {
10860
11329
  const max = parseInt(process.env["CODE_INTEL_RATE_LIMIT_MAX"] ?? "100", 10);
10861
- const windowMs = parseInt(process.env["CODE_INTEL_RATE_LIMIT_WINDOW_MS"] ?? `${15 * 60 * 1e3}`, 10);
11330
+ const windowMs = parseInt(process.env["CODE_INTEL_RATE_LIMIT_WINDOW_MS"] ?? `${60 * 1e3}`, 10);
10862
11331
  return rateLimit({
10863
11332
  windowMs,
10864
11333
  max,
10865
11334
  standardHeaders: true,
10866
11335
  legacyHeaders: false,
10867
- skip: (req) => req.path.startsWith("/health") || req.path === "/metrics",
11336
+ // Skip health checks, metrics, and read-only listing/pagination endpoints.
11337
+ // The node pagination and group/repo listing endpoints are hit many times
11338
+ // when loading a large graph — rate-limiting them only hurts the user's own
11339
+ // session without providing meaningful abuse protection.
11340
+ 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),
10868
11341
  message: {
10869
11342
  error: {
10870
11343
  code: ErrorCodes.RATE_LIMIT_EXCEEDED,
@@ -10923,12 +11396,31 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
10923
11396
  });
10924
11397
  app.use(requestIdMiddleware);
10925
11398
  app.use(authMiddleware);
11399
+ let dbUnavailableSince = null;
10926
11400
  app.use((_req, res, next) => {
10927
11401
  if (workspaceRoot) {
11402
+ const metaFilePath = path32.join(workspaceRoot, ".code-intel", "meta.json");
11403
+ let metaOk = false;
10928
11404
  try {
10929
- const meta = loadMetadata(workspaceRoot);
10930
- if (meta?.indexVersion) res.setHeader("X-Index-Version", meta.indexVersion);
10931
- } catch {
11405
+ if (fs26.existsSync(metaFilePath)) {
11406
+ const raw = fs26.readFileSync(metaFilePath, "utf-8");
11407
+ const meta = JSON.parse(raw);
11408
+ if (meta?.indexVersion) res.setHeader("X-Index-Version", meta.indexVersion);
11409
+ }
11410
+ metaOk = true;
11411
+ if (dbUnavailableSince !== null) {
11412
+ dbUnavailableSince = null;
11413
+ logger_default.info("[serve] DB back online \u2014 cleared stale flag");
11414
+ }
11415
+ } catch (err) {
11416
+ if (dbUnavailableSince === null) {
11417
+ dbUnavailableSince = (/* @__PURE__ */ new Date()).toISOString();
11418
+ logger_default.warn(`[serve] DB unavailable since ${dbUnavailableSince}: ${err instanceof Error ? err.message : String(err)}`);
11419
+ }
11420
+ }
11421
+ if (!metaOk) {
11422
+ res.setHeader("X-Stale", "true");
11423
+ res.setHeader("X-Stale-Since", dbUnavailableSince);
10932
11424
  }
10933
11425
  }
10934
11426
  next();
@@ -10984,6 +11476,21 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
10984
11476
  let vectorIndex = null;
10985
11477
  let vectorIndexBuilding = false;
10986
11478
  let vectorIndexReady = false;
11479
+ let bm25Index = null;
11480
+ function ensureBm25Index() {
11481
+ if (bm25Index) return bm25Index;
11482
+ if (!workspaceRoot) return null;
11483
+ const idx = new Bm25Index(getBm25DbPath(workspaceRoot));
11484
+ idx.load();
11485
+ if (idx.isLoaded) {
11486
+ bm25Index = idx;
11487
+ return idx;
11488
+ }
11489
+ return null;
11490
+ }
11491
+ if (workspaceRoot && process.env["NODE_ENV"] !== "test") {
11492
+ setImmediate(() => ensureBm25Index());
11493
+ }
10987
11494
  async function ensureVectorIndex() {
10988
11495
  if (vectorIndexReady && vectorIndex) return vectorIndex;
10989
11496
  if (!workspaceRoot || vectorIndexBuilding) return null;
@@ -11087,12 +11594,12 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11087
11594
  return;
11088
11595
  }
11089
11596
  const user = db.createUser(username, password, "admin");
11090
- const sessionId = createSession({ id: user.id, username: user.username, role: user.role });
11091
- res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
11597
+ const { sessionId, ttlMs } = createSession({ id: user.id, username: user.username, role: user.role });
11598
+ res.setHeader("Set-Cookie", buildSessionCookie(sessionId, ttlMs));
11092
11599
  res.status(201).json({ user: { id: user.id, username: user.username, role: user.role } });
11093
11600
  });
11094
11601
  app.post("/auth/login", async (req, res) => {
11095
- const { username, password } = req.body;
11602
+ const { username, password, rememberMe } = req.body;
11096
11603
  if (!username || !password) {
11097
11604
  res.status(400).json({
11098
11605
  error: {
@@ -11136,10 +11643,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11136
11643
  });
11137
11644
  return;
11138
11645
  }
11139
- const sessionId = createSession({ id: user.id, username: user.username, role: user.role });
11646
+ const { sessionId, ttlMs } = createSession({ id: user.id, username: user.username, role: user.role }, rememberMe === true);
11140
11647
  db.logAccess(user.id, "/auth/login", "login", "allow", req.ip ?? "unknown");
11141
11648
  authAttemptsTotal.inc({ method: "local", outcome: "success" });
11142
- res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
11649
+ res.setHeader("Set-Cookie", buildSessionCookie(sessionId, ttlMs));
11143
11650
  res.json({ user: { id: user.id, username: user.username, role: user.role } });
11144
11651
  });
11145
11652
  app.post("/auth/logout", (req, res) => {
@@ -11261,9 +11768,9 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11261
11768
  authAttemptsTotal.inc({ method: "oidc", outcome: "success" });
11262
11769
  logger_default.info(`[oidc] Auto-provisioned new user: ${finalUsername} (${cfg.defaultRole})`);
11263
11770
  }
11264
- const sessionId = createSession({ id: user.id, username: user.username, role: user.role });
11771
+ const { sessionId, ttlMs } = createSession({ id: user.id, username: user.username, role: user.role });
11265
11772
  db.logAccess(user.id, "/auth/callback", "oidc-login", "allow", req.ip ?? "unknown");
11266
- res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
11773
+ res.setHeader("Set-Cookie", buildSessionCookie(sessionId, ttlMs));
11267
11774
  res.redirect(302, "/");
11268
11775
  } catch (err) {
11269
11776
  logger_default.warn("[oidc] Callback failed:", err instanceof Error ? err.message : err);
@@ -11380,10 +11887,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11380
11887
  const registry = loadRegistry();
11381
11888
  const entry = registry.find((r) => r.name === requestedRepo || r.path === requestedRepo);
11382
11889
  if (!entry) return null;
11383
- const dbPath = path31.join(entry.path, ".code-intel", "graph.db");
11384
- if (!fs24.existsSync(dbPath)) return null;
11890
+ const dbPath = path32.join(entry.path, ".code-intel", "graph.db");
11891
+ if (!fs26.existsSync(dbPath)) return null;
11385
11892
  const repoGraph = createKnowledgeGraph();
11386
- const db = new DbManager(dbPath);
11893
+ const db = new DbManager(dbPath, true);
11387
11894
  try {
11388
11895
  await db.init();
11389
11896
  await loadGraphFromDB(repoGraph, db);
@@ -11394,10 +11901,33 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11394
11901
  return null;
11395
11902
  }
11396
11903
  }
11904
+ async function loadGroupGraph(groupName) {
11905
+ const group = loadGroup(groupName);
11906
+ if (!group) return null;
11907
+ const registry = loadRegistry();
11908
+ const mergedGraph = createKnowledgeGraph();
11909
+ for (const member of group.members) {
11910
+ const regEntry = registry.find((r) => r.name === member.registryName);
11911
+ if (!regEntry) continue;
11912
+ const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
11913
+ if (!fs26.existsSync(dbPath)) continue;
11914
+ const db = new DbManager(dbPath, true);
11915
+ try {
11916
+ await db.init();
11917
+ await loadGraphFromDB(mergedGraph, db);
11918
+ db.close();
11919
+ } catch {
11920
+ db.close();
11921
+ }
11922
+ }
11923
+ return mergedGraph.size.nodes > 0 ? mergedGraph : null;
11924
+ }
11397
11925
  async function getGraphForRepo(requestedRepo) {
11398
11926
  if (!requestedRepo || requestedRepo === repoName) return graph;
11399
11927
  const g = await loadRepoGraph(requestedRepo);
11400
- return g ?? graph;
11928
+ if (g) return g;
11929
+ const gg = await loadGroupGraph(requestedRepo);
11930
+ return gg ?? graph;
11401
11931
  }
11402
11932
  app.get("/api/v1/graph/:repo", requireRepoAccess((req) => {
11403
11933
  const p = req.params["repo"];
@@ -11421,12 +11951,71 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11421
11951
  }
11422
11952
  res.json({ nodes: [...g.allNodes()], edges: [...g.allEdges()] });
11423
11953
  });
11954
+ app.get("/api/v1/graph/:repo/nodes", requireRepoAccess((req) => {
11955
+ const p = req.params["repo"];
11956
+ const repo = Array.isArray(p) ? p[0] : p;
11957
+ return repo ? decodeURIComponent(repo) : void 0;
11958
+ }), async (req, res) => {
11959
+ const rawRepo = req.params["repo"];
11960
+ const requestedRepo = decodeURIComponent(Array.isArray(rawRepo) ? rawRepo[0] ?? "" : rawRepo ?? "");
11961
+ const limit = Math.min(parseInt(req.query["limit"] ?? "200", 10), 1e3);
11962
+ const offset = Math.max(parseInt(req.query["offset"] ?? "0", 10), 0);
11963
+ const g = requestedRepo === repoName ? graph : await loadRepoGraph(requestedRepo);
11964
+ if (!g) {
11965
+ res.status(404).json({
11966
+ error: {
11967
+ code: ErrorCodes.NOT_FOUND,
11968
+ message: `Repo "${requestedRepo}" not found or not indexed`,
11969
+ hint: `Run: code-intel analyze <path>`,
11970
+ requestId: req.requestId,
11971
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
11972
+ }
11973
+ });
11974
+ return;
11975
+ }
11976
+ let nodes;
11977
+ if (isLazyGraph(g)) {
11978
+ nodes = await g.getNodePage(offset, limit);
11979
+ } else {
11980
+ const eager = g;
11981
+ if (!eager._nodeArray) {
11982
+ eager._nodeArray = [...g.allNodes()];
11983
+ }
11984
+ nodes = eager._nodeArray.slice(offset, offset + limit);
11985
+ }
11986
+ res.json({
11987
+ nodes,
11988
+ offset,
11989
+ limit,
11990
+ total: g.size.nodes,
11991
+ hasMore: offset + nodes.length < g.size.nodes
11992
+ });
11993
+ });
11424
11994
  app.post("/api/v1/search", requireToolScope("search"), async (req, res) => {
11425
- const { query, limit, repo } = req.body;
11995
+ const { query, limit, repo, group } = req.body;
11996
+ if (group) {
11997
+ const grp = loadGroup(group);
11998
+ if (!grp) {
11999
+ res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: `Group '${group}' not found`, hint: "Use /api/v1/groups to list available groups" } });
12000
+ return;
12001
+ }
12002
+ try {
12003
+ const { perRepo, merged } = await queryGroup(grp, query ?? "", limit ?? 20);
12004
+ res.json({ results: merged, perRepo, searchMode: "bm25", group });
12005
+ } catch (err) {
12006
+ res.status(500).json({ error: { code: ErrorCodes.INTERNAL_ERROR, message: err instanceof Error ? err.message : String(err) } });
12007
+ }
12008
+ return;
12009
+ }
11426
12010
  const g = await getGraphForRepo(repo);
11427
12011
  const vdbPath = workspaceRoot ? getVectorDbPath(workspaceRoot) : void 0;
11428
- const { results, searchMode } = await hybridSearch(g, query ?? "", limit ?? 20, { vectorDbPath: vdbPath });
11429
- res.json({ results, searchMode });
12012
+ const bm25 = !repo || repo === repoName ? ensureBm25Index() : null;
12013
+ const bm25Results = bm25 ? bm25.search(query ?? "", (limit ?? 20) * 3) : null;
12014
+ const { results, searchMode } = await hybridSearch(g, query ?? "", limit ?? 20, {
12015
+ vectorDbPath: vdbPath,
12016
+ bm25Results: bm25Results ?? void 0
12017
+ });
12018
+ res.json({ results, searchMode, repo: repo ?? repoName });
11430
12019
  });
11431
12020
  app.post("/api/v1/vector-search", async (req, res) => {
11432
12021
  const { query, limit = 10 } = req.body;
@@ -11468,7 +12057,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11468
12057
  return;
11469
12058
  }
11470
12059
  try {
11471
- const content = fs24.readFileSync(file_path, "utf-8");
12060
+ const content = fs26.readFileSync(file_path, "utf-8");
11472
12061
  res.json({ content });
11473
12062
  } catch {
11474
12063
  res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "File not found" } });
@@ -11505,7 +12094,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11505
12094
  if (workspaceRoot) {
11506
12095
  try {
11507
12096
  const dbPath = getDbPath(workspaceRoot);
11508
- const dbm = new DbManager(dbPath);
12097
+ const dbm = new DbManager(dbPath, true);
11509
12098
  await dbm.init();
11510
12099
  const rows = await dbm.query(q);
11511
12100
  dbm.close();
@@ -11546,33 +12135,53 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11546
12135
  app.get("/api/v1/nodes/:id", async (req, res) => {
11547
12136
  const nodeId = decodeURIComponent(req.params.id);
11548
12137
  const g = await getGraphForRepo(req.query["repo"]);
11549
- const node = g.getNode(nodeId);
12138
+ const node = isLazyGraph(g) ? await g.getNodeAsync(nodeId) : g.getNode(nodeId);
11550
12139
  if (!node) {
11551
12140
  res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "Node not found", requestId: req.requestId } });
11552
12141
  return;
11553
12142
  }
11554
12143
  const incoming = [...g.findEdgesTo(nodeId)];
11555
12144
  const outgoing = [...g.findEdgesFrom(nodeId)];
12145
+ const resolveName = isLazyGraph(g) ? async (id) => {
12146
+ const n = g.getNode(id) ?? await g.getNodeAsync(id);
12147
+ return n?.name;
12148
+ } : (id) => Promise.resolve(g.getNode(id)?.name);
12149
+ const resolveKind = isLazyGraph(g) ? async (id) => {
12150
+ const n = g.getNode(id) ?? await g.getNodeAsync(id);
12151
+ return n?.kind;
12152
+ } : (id) => Promise.resolve(g.getNode(id)?.kind);
11556
12153
  res.json({
11557
12154
  node,
11558
- callers: incoming.filter((e) => e.kind === "calls").map((e) => ({ id: e.source, name: g.getNode(e.source)?.name, weight: e.weight })),
11559
- callees: outgoing.filter((e) => e.kind === "calls").map((e) => ({ id: e.target, name: g.getNode(e.target)?.name, weight: e.weight })),
11560
- imports: outgoing.filter((e) => e.kind === "imports").map((e) => ({ id: e.target, name: g.getNode(e.target)?.name })),
11561
- importedBy: incoming.filter((e) => e.kind === "imports").map((e) => ({ id: e.source, name: g.getNode(e.source)?.name })),
11562
- extends: outgoing.filter((e) => e.kind === "extends").map((e) => ({ id: e.target, name: g.getNode(e.target)?.name })),
11563
- implementsEdges: outgoing.filter((e) => e.kind === "implements").map((e) => ({ id: e.target, name: g.getNode(e.target)?.name })),
11564
- 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 })),
11565
- cluster: incoming.filter((e) => e.kind === "belongs_to").map((e) => g.getNode(e.target)?.name)[0]
12155
+ callers: await Promise.all(incoming.filter((e) => e.kind === "calls").map(async (e) => ({ id: e.source, name: await resolveName(e.source), weight: e.weight }))),
12156
+ callees: await Promise.all(outgoing.filter((e) => e.kind === "calls").map(async (e) => ({ id: e.target, name: await resolveName(e.target), weight: e.weight }))),
12157
+ imports: await Promise.all(outgoing.filter((e) => e.kind === "imports").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
12158
+ importedBy: await Promise.all(incoming.filter((e) => e.kind === "imports").map(async (e) => ({ id: e.source, name: await resolveName(e.source) }))),
12159
+ extends: await Promise.all(outgoing.filter((e) => e.kind === "extends").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
12160
+ implementsEdges: await Promise.all(outgoing.filter((e) => e.kind === "implements").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
12161
+ 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) }))),
12162
+ cluster: (await Promise.all(incoming.filter((e) => e.kind === "belongs_to").map(async (e) => resolveName(e.target))))[0]
11566
12163
  });
11567
12164
  });
11568
12165
  app.post("/api/v1/blast-radius", async (req, res) => {
11569
12166
  const { target, direction = "both", max_hops = 5, repo } = req.body;
11570
12167
  const g = await getGraphForRepo(repo);
11571
12168
  let targetNode = null;
11572
- for (const node of g.allNodes()) {
11573
- if (node.name === target || node.id === target) {
11574
- targetNode = node;
11575
- break;
12169
+ if (isLazyGraph(g) && target) {
12170
+ targetNode = g.getNode(target) ?? await g.getNodeAsync(target) ?? null;
12171
+ if (!targetNode) {
12172
+ for await (const node of g.allNodesAsync()) {
12173
+ if (node.name === target || node.id === target) {
12174
+ targetNode = node;
12175
+ break;
12176
+ }
12177
+ }
12178
+ }
12179
+ } else {
12180
+ for (const node of g.allNodes()) {
12181
+ if (node.name === target || node.id === target) {
12182
+ targetNode = node;
12183
+ break;
12184
+ }
11576
12185
  }
11577
12186
  }
11578
12187
  if (!targetNode) {
@@ -11726,9 +12335,9 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11726
12335
  for (const member of group.members) {
11727
12336
  const regEntry = registry.find((r) => r.name === member.registryName);
11728
12337
  if (!regEntry) continue;
11729
- const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
11730
- if (!fs24.existsSync(dbPath)) continue;
11731
- const db = new DbManager(dbPath);
12338
+ const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
12339
+ if (!fs26.existsSync(dbPath)) continue;
12340
+ const db = new DbManager(dbPath, true);
11732
12341
  try {
11733
12342
  await db.init();
11734
12343
  await loadGraphFromDB(mergedGraph, db);
@@ -11753,10 +12362,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11753
12362
  let nodeCount = 0;
11754
12363
  let edgeCount = 0;
11755
12364
  if (regEntry) {
11756
- const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
11757
- if (fs24.existsSync(dbPath)) {
12365
+ const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
12366
+ if (fs26.existsSync(dbPath)) {
11758
12367
  try {
11759
- const db = new DbManager(dbPath);
12368
+ const db = new DbManager(dbPath, true);
11760
12369
  await db.init();
11761
12370
  const g = createKnowledgeGraph();
11762
12371
  await loadGraphFromDB(g, db);
@@ -11779,7 +12388,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11779
12388
  res.json({ repos, edges });
11780
12389
  });
11781
12390
  app.get("/api/v1/source", requireAuth, requireRole("viewer"), (req, res) => {
11782
- const { file, startLine: startLineStr, endLine: endLineStr } = req.query;
12391
+ const { file, startLine: startLineStr, endLine: endLineStr, repo } = req.query;
11783
12392
  if (!file) {
11784
12393
  res.status(400).json({
11785
12394
  error: {
@@ -11803,14 +12412,36 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11803
12412
  });
11804
12413
  return;
11805
12414
  }
11806
- let rawResolved = path31.normalize(file);
11807
- if (!path31.isAbsolute(rawResolved) && workspaceRoot) {
11808
- rawResolved = path31.join(workspaceRoot, rawResolved);
12415
+ let baseDir = workspaceRoot;
12416
+ if (repo && repo !== repoName) {
12417
+ const registry = loadRegistry();
12418
+ const entry = registry.find((r) => r.name === repo || r.path === repo);
12419
+ if (entry) {
12420
+ baseDir = entry.path;
12421
+ } else {
12422
+ const group = loadGroup(repo);
12423
+ if (group) {
12424
+ const normalizedFile = path32.normalize(file);
12425
+ for (const member of group.members) {
12426
+ const regEntry = registry.find((r) => r.name === member.registryName);
12427
+ if (!regEntry) continue;
12428
+ const candidate = path32.resolve(path32.join(regEntry.path, normalizedFile));
12429
+ if (fs26.existsSync(candidate)) {
12430
+ baseDir = regEntry.path;
12431
+ break;
12432
+ }
12433
+ }
12434
+ }
12435
+ }
12436
+ }
12437
+ let rawResolved = path32.normalize(file);
12438
+ if (!path32.isAbsolute(rawResolved) && baseDir) {
12439
+ rawResolved = path32.join(baseDir, rawResolved);
11809
12440
  }
11810
- const resolvedFile = path31.resolve(rawResolved);
12441
+ const resolvedFile = path32.resolve(rawResolved);
11811
12442
  function isInsideDir(fileAbs, dir) {
11812
- const rel = path31.relative(path31.resolve(dir), fileAbs);
11813
- return !rel.startsWith("..") && !path31.isAbsolute(rel);
12443
+ const rel = path32.relative(path32.resolve(dir), fileAbs);
12444
+ return !rel.startsWith("..") && !path32.isAbsolute(rel);
11814
12445
  }
11815
12446
  if (workspaceRoot) {
11816
12447
  if (!isInsideDir(resolvedFile, workspaceRoot)) {
@@ -11847,7 +12478,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11847
12478
  }
11848
12479
  let fileContent;
11849
12480
  try {
11850
- fileContent = fs24.readFileSync(resolvedFile, "utf-8");
12481
+ fileContent = fs26.readFileSync(resolvedFile, "utf-8");
11851
12482
  } catch {
11852
12483
  res.status(404).json({
11853
12484
  error: {
@@ -11878,7 +12509,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11878
12509
  const contextStart = Math.max(1, startLine - 20);
11879
12510
  const contextEnd = Math.min(lines.length, endLine + 20);
11880
12511
  const content = lines.slice(contextStart - 1, contextEnd).join("\n");
11881
- const ext = path31.extname(resolvedFile).toLowerCase();
12512
+ const ext = path32.extname(resolvedFile).toLowerCase();
11882
12513
  const languageMap = {
11883
12514
  ".ts": "typescript",
11884
12515
  ".tsx": "typescript",
@@ -12013,10 +12644,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
12013
12644
  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() } });
12014
12645
  }
12015
12646
  });
12016
- if (fs24.existsSync(WEB_DIST)) {
12647
+ if (fs26.existsSync(WEB_DIST)) {
12017
12648
  app.use(express.static(WEB_DIST));
12018
12649
  app.get("/{*path}", (_req, res) => {
12019
- res.sendFile(path31.join(WEB_DIST, "index.html"));
12650
+ res.sendFile(path32.join(WEB_DIST, "index.html"));
12020
12651
  });
12021
12652
  }
12022
12653
  app.use("/admin", requireRole("admin"));
@@ -12170,12 +12801,13 @@ async function startHttpServer(graph, repoName, port = 4747, workspaceRoot, watc
12170
12801
 
12171
12802
  // src/multi-repo/index.ts
12172
12803
  init_group_registry();
12804
+ init_graph_from_db();
12173
12805
 
12174
12806
  // src/multi-repo/cross-repo-search.ts
12175
12807
  function mergeSearchResults(...perRepoResults) {
12176
12808
  return reciprocalRankFusion(...perRepoResults);
12177
12809
  }
12178
12810
 
12179
- 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 };
12811
+ 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 };
12180
12812
  //# sourceMappingURL=index.js.map
12181
12813
  //# sourceMappingURL=index.js.map