@optave/codegraph 3.0.4 → 3.1.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.
Files changed (49) hide show
  1. package/README.md +59 -52
  2. package/grammars/tree-sitter-go.wasm +0 -0
  3. package/package.json +9 -10
  4. package/src/ast-analysis/rules/csharp.js +201 -0
  5. package/src/ast-analysis/rules/go.js +182 -0
  6. package/src/ast-analysis/rules/index.js +82 -0
  7. package/src/ast-analysis/rules/java.js +175 -0
  8. package/src/ast-analysis/rules/javascript.js +246 -0
  9. package/src/ast-analysis/rules/php.js +219 -0
  10. package/src/ast-analysis/rules/python.js +196 -0
  11. package/src/ast-analysis/rules/ruby.js +204 -0
  12. package/src/ast-analysis/rules/rust.js +173 -0
  13. package/src/ast-analysis/shared.js +223 -0
  14. package/src/ast.js +15 -28
  15. package/src/audit.js +4 -5
  16. package/src/boundaries.js +1 -1
  17. package/src/branch-compare.js +84 -79
  18. package/src/builder.js +274 -159
  19. package/src/cfg.js +111 -341
  20. package/src/check.js +3 -3
  21. package/src/cli.js +122 -167
  22. package/src/cochange.js +1 -1
  23. package/src/communities.js +13 -16
  24. package/src/complexity.js +196 -1239
  25. package/src/cycles.js +1 -1
  26. package/src/dataflow.js +274 -697
  27. package/src/db/connection.js +88 -0
  28. package/src/db/migrations.js +312 -0
  29. package/src/db/query-builder.js +280 -0
  30. package/src/db/repository.js +134 -0
  31. package/src/db.js +19 -392
  32. package/src/embedder.js +145 -141
  33. package/src/export.js +1 -1
  34. package/src/flow.js +160 -228
  35. package/src/index.js +36 -2
  36. package/src/kinds.js +49 -0
  37. package/src/manifesto.js +3 -8
  38. package/src/mcp.js +97 -20
  39. package/src/owners.js +132 -132
  40. package/src/parser.js +58 -131
  41. package/src/queries-cli.js +866 -0
  42. package/src/queries.js +1356 -2261
  43. package/src/resolve.js +11 -2
  44. package/src/result-formatter.js +21 -0
  45. package/src/sequence.js +364 -0
  46. package/src/structure.js +200 -199
  47. package/src/test-filter.js +7 -0
  48. package/src/triage.js +120 -162
  49. package/src/viewer.js +1 -1
@@ -0,0 +1,134 @@
1
+ import { EVERY_SYMBOL_KIND, VALID_ROLES } from '../kinds.js';
2
+ import { NodeQuery } from './query-builder.js';
3
+
4
+ /**
5
+ * Find nodes matching a name pattern, with fan-in count.
6
+ * Used by findMatchingNodes in queries.js.
7
+ *
8
+ * @param {object} db - Database instance
9
+ * @param {string} namePattern - LIKE pattern (already wrapped with %)
10
+ * @param {object} [opts]
11
+ * @param {string[]} [opts.kinds] - Node kinds to match
12
+ * @param {string} [opts.file] - File filter (partial match)
13
+ * @returns {object[]}
14
+ */
15
+ export function findNodesWithFanIn(db, namePattern, opts = {}) {
16
+ const q = new NodeQuery()
17
+ .select('n.*, COALESCE(fi.cnt, 0) AS fan_in')
18
+ .withFanIn()
19
+ .where('n.name LIKE ?', namePattern);
20
+
21
+ if (opts.kinds) {
22
+ q.kinds(opts.kinds);
23
+ }
24
+ if (opts.file) {
25
+ q.fileFilter(opts.file);
26
+ }
27
+
28
+ return q.all(db);
29
+ }
30
+
31
+ /**
32
+ * Fetch nodes for triage scoring: fan-in + complexity + churn.
33
+ * Used by triageData in triage.js.
34
+ *
35
+ * @param {object} db
36
+ * @param {object} [opts]
37
+ * @returns {object[]}
38
+ */
39
+ export function findNodesForTriage(db, opts = {}) {
40
+ if (opts.kind && !EVERY_SYMBOL_KIND.includes(opts.kind)) {
41
+ throw new Error(`Invalid kind: ${opts.kind} (expected one of ${EVERY_SYMBOL_KIND.join(', ')})`);
42
+ }
43
+ if (opts.role && !VALID_ROLES.includes(opts.role)) {
44
+ throw new Error(`Invalid role: ${opts.role} (expected one of ${VALID_ROLES.join(', ')})`);
45
+ }
46
+
47
+ const kindsToUse = opts.kind ? [opts.kind] : ['function', 'method', 'class'];
48
+ const q = new NodeQuery()
49
+ .select(
50
+ `n.id, n.name, n.kind, n.file, n.line, n.end_line, n.role,
51
+ COALESCE(fi.cnt, 0) AS fan_in,
52
+ COALESCE(fc.cognitive, 0) AS cognitive,
53
+ COALESCE(fc.maintainability_index, 0) AS mi,
54
+ COALESCE(fc.cyclomatic, 0) AS cyclomatic,
55
+ COALESCE(fc.max_nesting, 0) AS max_nesting,
56
+ COALESCE(fcc.commit_count, 0) AS churn`,
57
+ )
58
+ .kinds(kindsToUse)
59
+ .withFanIn()
60
+ .withComplexity()
61
+ .withChurn()
62
+ .excludeTests(opts.noTests)
63
+ .fileFilter(opts.file)
64
+ .roleFilter(opts.role)
65
+ .orderBy('n.file, n.line');
66
+
67
+ return q.all(db);
68
+ }
69
+
70
+ /**
71
+ * Shared query builder for function/method/class node listing.
72
+ * @param {object} [opts]
73
+ * @returns {NodeQuery}
74
+ */
75
+ function _functionNodeQuery(opts = {}) {
76
+ return new NodeQuery()
77
+ .select('name, kind, file, line, end_line, role')
78
+ .kinds(['function', 'method', 'class'])
79
+ .fileFilter(opts.file)
80
+ .nameLike(opts.pattern)
81
+ .excludeTests(opts.noTests)
82
+ .orderBy('file, line');
83
+ }
84
+
85
+ /**
86
+ * List function/method/class nodes with basic info.
87
+ * Used by listFunctionsData in queries.js.
88
+ *
89
+ * @param {object} db
90
+ * @param {object} [opts]
91
+ * @returns {object[]}
92
+ */
93
+ export function listFunctionNodes(db, opts = {}) {
94
+ return _functionNodeQuery(opts).all(db);
95
+ }
96
+
97
+ /**
98
+ * Iterator version of listFunctionNodes for memory efficiency.
99
+ * Used by iterListFunctions in queries.js.
100
+ *
101
+ * @param {object} db
102
+ * @param {object} [opts]
103
+ * @returns {IterableIterator}
104
+ */
105
+ export function iterateFunctionNodes(db, opts = {}) {
106
+ return _functionNodeQuery(opts).iterate(db);
107
+ }
108
+
109
+ /**
110
+ * Count total nodes.
111
+ * @param {object} db
112
+ * @returns {number}
113
+ */
114
+ export function countNodes(db) {
115
+ return db.prepare('SELECT COUNT(*) AS cnt FROM nodes').get().cnt;
116
+ }
117
+
118
+ /**
119
+ * Count total edges.
120
+ * @param {object} db
121
+ * @returns {number}
122
+ */
123
+ export function countEdges(db) {
124
+ return db.prepare('SELECT COUNT(*) AS cnt FROM edges').get().cnt;
125
+ }
126
+
127
+ /**
128
+ * Count distinct files.
129
+ * @param {object} db
130
+ * @returns {number}
131
+ */
132
+ export function countFiles(db) {
133
+ return db.prepare('SELECT COUNT(DISTINCT file) AS cnt FROM nodes').get().cnt;
134
+ }
package/src/db.js CHANGED
@@ -1,392 +1,19 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import Database from 'better-sqlite3';
4
- import { debug, warn } from './logger.js';
5
-
6
- // ─── Schema Migrations ─────────────────────────────────────────────────
7
- export const MIGRATIONS = [
8
- {
9
- version: 1,
10
- up: `
11
- CREATE TABLE IF NOT EXISTS nodes (
12
- id INTEGER PRIMARY KEY AUTOINCREMENT,
13
- name TEXT NOT NULL,
14
- kind TEXT NOT NULL,
15
- file TEXT NOT NULL,
16
- line INTEGER,
17
- end_line INTEGER,
18
- UNIQUE(name, kind, file, line)
19
- );
20
- CREATE TABLE IF NOT EXISTS edges (
21
- id INTEGER PRIMARY KEY AUTOINCREMENT,
22
- source_id INTEGER NOT NULL,
23
- target_id INTEGER NOT NULL,
24
- kind TEXT NOT NULL,
25
- confidence REAL DEFAULT 1.0,
26
- dynamic INTEGER DEFAULT 0,
27
- FOREIGN KEY(source_id) REFERENCES nodes(id),
28
- FOREIGN KEY(target_id) REFERENCES nodes(id)
29
- );
30
- CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
31
- CREATE INDEX IF NOT EXISTS idx_nodes_file ON nodes(file);
32
- CREATE INDEX IF NOT EXISTS idx_nodes_kind ON nodes(kind);
33
- CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_id);
34
- CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_id);
35
- CREATE INDEX IF NOT EXISTS idx_edges_kind ON edges(kind);
36
- CREATE TABLE IF NOT EXISTS node_metrics (
37
- node_id INTEGER PRIMARY KEY,
38
- line_count INTEGER,
39
- symbol_count INTEGER,
40
- import_count INTEGER,
41
- export_count INTEGER,
42
- fan_in INTEGER,
43
- fan_out INTEGER,
44
- cohesion REAL,
45
- file_count INTEGER,
46
- FOREIGN KEY(node_id) REFERENCES nodes(id)
47
- );
48
- CREATE INDEX IF NOT EXISTS idx_node_metrics_node ON node_metrics(node_id);
49
- `,
50
- },
51
- {
52
- version: 2,
53
- up: `
54
- CREATE INDEX IF NOT EXISTS idx_nodes_name_kind_file ON nodes(name, kind, file);
55
- CREATE INDEX IF NOT EXISTS idx_nodes_file_kind ON nodes(file, kind);
56
- CREATE INDEX IF NOT EXISTS idx_edges_source_kind ON edges(source_id, kind);
57
- CREATE INDEX IF NOT EXISTS idx_edges_target_kind ON edges(target_id, kind);
58
- `,
59
- },
60
- {
61
- version: 3,
62
- up: `
63
- CREATE TABLE IF NOT EXISTS file_hashes (
64
- file TEXT PRIMARY KEY,
65
- hash TEXT NOT NULL,
66
- mtime INTEGER NOT NULL
67
- );
68
- `,
69
- },
70
- {
71
- version: 4,
72
- up: `ALTER TABLE file_hashes ADD COLUMN size INTEGER DEFAULT 0;`,
73
- },
74
- {
75
- version: 5,
76
- up: `
77
- CREATE TABLE IF NOT EXISTS co_changes (
78
- id INTEGER PRIMARY KEY AUTOINCREMENT,
79
- file_a TEXT NOT NULL,
80
- file_b TEXT NOT NULL,
81
- commit_count INTEGER NOT NULL,
82
- jaccard REAL NOT NULL,
83
- last_commit_epoch INTEGER,
84
- UNIQUE(file_a, file_b)
85
- );
86
- CREATE INDEX IF NOT EXISTS idx_co_changes_file_a ON co_changes(file_a);
87
- CREATE INDEX IF NOT EXISTS idx_co_changes_file_b ON co_changes(file_b);
88
- CREATE INDEX IF NOT EXISTS idx_co_changes_jaccard ON co_changes(jaccard DESC);
89
- CREATE TABLE IF NOT EXISTS co_change_meta (
90
- key TEXT PRIMARY KEY,
91
- value TEXT NOT NULL
92
- );
93
- `,
94
- },
95
- {
96
- version: 6,
97
- up: `
98
- CREATE TABLE IF NOT EXISTS file_commit_counts (
99
- file TEXT PRIMARY KEY,
100
- commit_count INTEGER NOT NULL DEFAULT 0
101
- );
102
- `,
103
- },
104
- {
105
- version: 7,
106
- up: `
107
- CREATE TABLE IF NOT EXISTS build_meta (
108
- key TEXT PRIMARY KEY,
109
- value TEXT NOT NULL
110
- );
111
- `,
112
- },
113
- {
114
- version: 8,
115
- up: `
116
- CREATE TABLE IF NOT EXISTS function_complexity (
117
- node_id INTEGER PRIMARY KEY,
118
- cognitive INTEGER NOT NULL,
119
- cyclomatic INTEGER NOT NULL,
120
- max_nesting INTEGER NOT NULL,
121
- FOREIGN KEY(node_id) REFERENCES nodes(id)
122
- );
123
- CREATE INDEX IF NOT EXISTS idx_fc_cognitive ON function_complexity(cognitive DESC);
124
- CREATE INDEX IF NOT EXISTS idx_fc_cyclomatic ON function_complexity(cyclomatic DESC);
125
- `,
126
- },
127
- {
128
- version: 9,
129
- up: `
130
- ALTER TABLE function_complexity ADD COLUMN loc INTEGER DEFAULT 0;
131
- ALTER TABLE function_complexity ADD COLUMN sloc INTEGER DEFAULT 0;
132
- ALTER TABLE function_complexity ADD COLUMN comment_lines INTEGER DEFAULT 0;
133
- ALTER TABLE function_complexity ADD COLUMN halstead_n1 INTEGER DEFAULT 0;
134
- ALTER TABLE function_complexity ADD COLUMN halstead_n2 INTEGER DEFAULT 0;
135
- ALTER TABLE function_complexity ADD COLUMN halstead_big_n1 INTEGER DEFAULT 0;
136
- ALTER TABLE function_complexity ADD COLUMN halstead_big_n2 INTEGER DEFAULT 0;
137
- ALTER TABLE function_complexity ADD COLUMN halstead_vocabulary INTEGER DEFAULT 0;
138
- ALTER TABLE function_complexity ADD COLUMN halstead_length INTEGER DEFAULT 0;
139
- ALTER TABLE function_complexity ADD COLUMN halstead_volume REAL DEFAULT 0;
140
- ALTER TABLE function_complexity ADD COLUMN halstead_difficulty REAL DEFAULT 0;
141
- ALTER TABLE function_complexity ADD COLUMN halstead_effort REAL DEFAULT 0;
142
- ALTER TABLE function_complexity ADD COLUMN halstead_bugs REAL DEFAULT 0;
143
- ALTER TABLE function_complexity ADD COLUMN maintainability_index REAL DEFAULT 0;
144
- CREATE INDEX IF NOT EXISTS idx_fc_mi ON function_complexity(maintainability_index ASC);
145
- `,
146
- },
147
- {
148
- version: 10,
149
- up: `
150
- CREATE TABLE IF NOT EXISTS dataflow (
151
- id INTEGER PRIMARY KEY AUTOINCREMENT,
152
- source_id INTEGER NOT NULL,
153
- target_id INTEGER NOT NULL,
154
- kind TEXT NOT NULL,
155
- param_index INTEGER,
156
- expression TEXT,
157
- line INTEGER,
158
- confidence REAL DEFAULT 1.0,
159
- FOREIGN KEY(source_id) REFERENCES nodes(id),
160
- FOREIGN KEY(target_id) REFERENCES nodes(id)
161
- );
162
- CREATE INDEX IF NOT EXISTS idx_dataflow_source ON dataflow(source_id);
163
- CREATE INDEX IF NOT EXISTS idx_dataflow_target ON dataflow(target_id);
164
- CREATE INDEX IF NOT EXISTS idx_dataflow_kind ON dataflow(kind);
165
- CREATE INDEX IF NOT EXISTS idx_dataflow_source_kind ON dataflow(source_id, kind);
166
- `,
167
- },
168
- {
169
- version: 11,
170
- up: `
171
- ALTER TABLE nodes ADD COLUMN parent_id INTEGER REFERENCES nodes(id);
172
- CREATE INDEX IF NOT EXISTS idx_nodes_parent ON nodes(parent_id);
173
- CREATE INDEX IF NOT EXISTS idx_nodes_kind_parent ON nodes(kind, parent_id);
174
- `,
175
- },
176
- {
177
- version: 12,
178
- up: `
179
- CREATE TABLE IF NOT EXISTS cfg_blocks (
180
- id INTEGER PRIMARY KEY AUTOINCREMENT,
181
- function_node_id INTEGER NOT NULL,
182
- block_index INTEGER NOT NULL,
183
- block_type TEXT NOT NULL,
184
- start_line INTEGER,
185
- end_line INTEGER,
186
- label TEXT,
187
- FOREIGN KEY(function_node_id) REFERENCES nodes(id),
188
- UNIQUE(function_node_id, block_index)
189
- );
190
- CREATE INDEX IF NOT EXISTS idx_cfg_blocks_fn ON cfg_blocks(function_node_id);
191
-
192
- CREATE TABLE IF NOT EXISTS cfg_edges (
193
- id INTEGER PRIMARY KEY AUTOINCREMENT,
194
- function_node_id INTEGER NOT NULL,
195
- source_block_id INTEGER NOT NULL,
196
- target_block_id INTEGER NOT NULL,
197
- kind TEXT NOT NULL,
198
- FOREIGN KEY(function_node_id) REFERENCES nodes(id),
199
- FOREIGN KEY(source_block_id) REFERENCES cfg_blocks(id),
200
- FOREIGN KEY(target_block_id) REFERENCES cfg_blocks(id)
201
- );
202
- CREATE INDEX IF NOT EXISTS idx_cfg_edges_fn ON cfg_edges(function_node_id);
203
- CREATE INDEX IF NOT EXISTS idx_cfg_edges_src ON cfg_edges(source_block_id);
204
- CREATE INDEX IF NOT EXISTS idx_cfg_edges_tgt ON cfg_edges(target_block_id);
205
- `,
206
- },
207
- {
208
- version: 13,
209
- up: `
210
- CREATE TABLE IF NOT EXISTS ast_nodes (
211
- id INTEGER PRIMARY KEY AUTOINCREMENT,
212
- file TEXT NOT NULL,
213
- line INTEGER NOT NULL,
214
- kind TEXT NOT NULL,
215
- name TEXT NOT NULL,
216
- text TEXT,
217
- receiver TEXT,
218
- parent_node_id INTEGER,
219
- FOREIGN KEY(parent_node_id) REFERENCES nodes(id)
220
- );
221
- CREATE INDEX IF NOT EXISTS idx_ast_kind ON ast_nodes(kind);
222
- CREATE INDEX IF NOT EXISTS idx_ast_name ON ast_nodes(name);
223
- CREATE INDEX IF NOT EXISTS idx_ast_file ON ast_nodes(file);
224
- CREATE INDEX IF NOT EXISTS idx_ast_parent ON ast_nodes(parent_node_id);
225
- CREATE INDEX IF NOT EXISTS idx_ast_kind_name ON ast_nodes(kind, name);
226
- `,
227
- },
228
- ];
229
-
230
- export function getBuildMeta(db, key) {
231
- try {
232
- const row = db.prepare('SELECT value FROM build_meta WHERE key = ?').get(key);
233
- return row ? row.value : null;
234
- } catch {
235
- return null;
236
- }
237
- }
238
-
239
- export function setBuildMeta(db, entries) {
240
- const upsert = db.prepare('INSERT OR REPLACE INTO build_meta (key, value) VALUES (?, ?)');
241
- const tx = db.transaction(() => {
242
- for (const [key, value] of Object.entries(entries)) {
243
- upsert.run(key, String(value));
244
- }
245
- });
246
- tx();
247
- }
248
-
249
- export function openDb(dbPath) {
250
- const dir = path.dirname(dbPath);
251
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
252
- acquireAdvisoryLock(dbPath);
253
- const db = new Database(dbPath);
254
- db.pragma('journal_mode = WAL');
255
- db.pragma('busy_timeout = 5000');
256
- db.__lockPath = `${dbPath}.lock`;
257
- return db;
258
- }
259
-
260
- export function closeDb(db) {
261
- db.close();
262
- if (db.__lockPath) releaseAdvisoryLock(db.__lockPath);
263
- }
264
-
265
- function isProcessAlive(pid) {
266
- try {
267
- process.kill(pid, 0);
268
- return true;
269
- } catch {
270
- return false;
271
- }
272
- }
273
-
274
- function acquireAdvisoryLock(dbPath) {
275
- const lockPath = `${dbPath}.lock`;
276
- try {
277
- if (fs.existsSync(lockPath)) {
278
- const content = fs.readFileSync(lockPath, 'utf-8').trim();
279
- const pid = Number(content);
280
- if (pid && pid !== process.pid && isProcessAlive(pid)) {
281
- warn(`Another process (PID ${pid}) may be using this database. Proceeding with caution.`);
282
- }
283
- }
284
- } catch {
285
- /* ignore read errors */
286
- }
287
- try {
288
- fs.writeFileSync(lockPath, String(process.pid), 'utf-8');
289
- } catch {
290
- /* best-effort */
291
- }
292
- }
293
-
294
- function releaseAdvisoryLock(lockPath) {
295
- try {
296
- const content = fs.readFileSync(lockPath, 'utf-8').trim();
297
- if (Number(content) === process.pid) {
298
- fs.unlinkSync(lockPath);
299
- }
300
- } catch {
301
- /* ignore */
302
- }
303
- }
304
-
305
- export function initSchema(db) {
306
- db.exec(`CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL DEFAULT 0)`);
307
-
308
- const row = db.prepare('SELECT version FROM schema_version').get();
309
- let currentVersion = row ? row.version : 0;
310
-
311
- if (!row) {
312
- db.prepare('INSERT INTO schema_version (version) VALUES (0)').run();
313
- }
314
-
315
- for (const migration of MIGRATIONS) {
316
- if (migration.version > currentVersion) {
317
- debug(`Running migration v${migration.version}`);
318
- db.exec(migration.up);
319
- db.prepare('UPDATE schema_version SET version = ?').run(migration.version);
320
- currentVersion = migration.version;
321
- }
322
- }
323
-
324
- try {
325
- db.exec('ALTER TABLE nodes ADD COLUMN end_line INTEGER');
326
- } catch {
327
- /* already exists */
328
- }
329
- try {
330
- db.exec('ALTER TABLE edges ADD COLUMN confidence REAL DEFAULT 1.0');
331
- } catch {
332
- /* already exists */
333
- }
334
- try {
335
- db.exec('ALTER TABLE edges ADD COLUMN dynamic INTEGER DEFAULT 0');
336
- } catch {
337
- /* already exists */
338
- }
339
- try {
340
- db.exec('ALTER TABLE nodes ADD COLUMN role TEXT');
341
- } catch {
342
- /* already exists */
343
- }
344
- try {
345
- db.exec('CREATE INDEX IF NOT EXISTS idx_nodes_role ON nodes(role)');
346
- } catch {
347
- /* already exists */
348
- }
349
- try {
350
- db.exec('ALTER TABLE nodes ADD COLUMN parent_id INTEGER REFERENCES nodes(id)');
351
- } catch {
352
- /* already exists */
353
- }
354
- try {
355
- db.exec('CREATE INDEX IF NOT EXISTS idx_nodes_parent ON nodes(parent_id)');
356
- } catch {
357
- /* already exists */
358
- }
359
- try {
360
- db.exec('CREATE INDEX IF NOT EXISTS idx_nodes_kind_parent ON nodes(kind, parent_id)');
361
- } catch {
362
- /* already exists */
363
- }
364
- }
365
-
366
- export function findDbPath(customPath) {
367
- if (customPath) return path.resolve(customPath);
368
- let dir = process.cwd();
369
- while (true) {
370
- const candidate = path.join(dir, '.codegraph', 'graph.db');
371
- if (fs.existsSync(candidate)) return candidate;
372
- const parent = path.dirname(dir);
373
- if (parent === dir) break;
374
- dir = parent;
375
- }
376
- return path.join(process.cwd(), '.codegraph', 'graph.db');
377
- }
378
-
379
- /**
380
- * Open a database in readonly mode, with a user-friendly error if the DB doesn't exist.
381
- */
382
- export function openReadonlyOrFail(customPath) {
383
- const dbPath = findDbPath(customPath);
384
- if (!fs.existsSync(dbPath)) {
385
- console.error(
386
- `No codegraph database found at ${dbPath}.\n` +
387
- `Run "codegraph build" first to analyze your codebase.`,
388
- );
389
- process.exit(1);
390
- }
391
- return new Database(dbPath, { readonly: true });
392
- }
1
+ // Barrel re-export — keeps all existing `import { ... } from './db.js'` working.
2
+ export { closeDb, findDbPath, openDb, openReadonlyOrFail } from './db/connection.js';
3
+ export { getBuildMeta, initSchema, MIGRATIONS, setBuildMeta } from './db/migrations.js';
4
+ export {
5
+ fanInJoinSQL,
6
+ fanOutJoinSQL,
7
+ kindInClause,
8
+ NodeQuery,
9
+ testFilterSQL,
10
+ } from './db/query-builder.js';
11
+ export {
12
+ countEdges,
13
+ countFiles,
14
+ countNodes,
15
+ findNodesForTriage,
16
+ findNodesWithFanIn,
17
+ iterateFunctionNodes,
18
+ listFunctionNodes,
19
+ } from './db/repository.js';