@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
@@ -12,7 +12,9 @@ import os from 'node:os';
12
12
  import path from 'node:path';
13
13
  import Database from 'better-sqlite3';
14
14
  import { buildGraph } from './builder.js';
15
- import { isTestFile, kindIcon } from './queries.js';
15
+ import { kindIcon } from './queries.js';
16
+ import { outputResult } from './result-formatter.js';
17
+ import { isTestFile } from './test-filter.js';
16
18
 
17
19
  // ─── Git Helpers ────────────────────────────────────────────────────────
18
20
 
@@ -81,55 +83,57 @@ function makeSymbolKey(kind, file, name) {
81
83
 
82
84
  function loadSymbolsFromDb(dbPath, changedFiles, noTests) {
83
85
  const db = new Database(dbPath, { readonly: true });
84
- const symbols = new Map();
86
+ try {
87
+ const symbols = new Map();
85
88
 
86
- if (changedFiles.length === 0) {
87
- db.close();
88
- return symbols;
89
- }
89
+ if (changedFiles.length === 0) {
90
+ return symbols;
91
+ }
90
92
 
91
- // Query nodes in changed files
92
- const placeholders = changedFiles.map(() => '?').join(', ');
93
- const rows = db
94
- .prepare(
95
- `SELECT n.id, n.name, n.kind, n.file, n.line, n.end_line
96
- FROM nodes n
97
- WHERE n.file IN (${placeholders})
98
- AND n.kind NOT IN ('file', 'directory')
99
- ORDER BY n.file, n.line`,
100
- )
101
- .all(...changedFiles);
102
-
103
- // Compute fan_in and fan_out for each node
104
- const fanInStmt = db.prepare(
105
- `SELECT COUNT(*) AS cnt FROM edges WHERE target_id = ? AND kind = 'calls'`,
106
- );
107
- const fanOutStmt = db.prepare(
108
- `SELECT COUNT(*) AS cnt FROM edges WHERE source_id = ? AND kind = 'calls'`,
109
- );
93
+ // Query nodes in changed files
94
+ const placeholders = changedFiles.map(() => '?').join(', ');
95
+ const rows = db
96
+ .prepare(
97
+ `SELECT n.id, n.name, n.kind, n.file, n.line, n.end_line
98
+ FROM nodes n
99
+ WHERE n.file IN (${placeholders})
100
+ AND n.kind NOT IN ('file', 'directory')
101
+ ORDER BY n.file, n.line`,
102
+ )
103
+ .all(...changedFiles);
104
+
105
+ // Compute fan_in and fan_out for each node
106
+ const fanInStmt = db.prepare(
107
+ `SELECT COUNT(*) AS cnt FROM edges WHERE target_id = ? AND kind = 'calls'`,
108
+ );
109
+ const fanOutStmt = db.prepare(
110
+ `SELECT COUNT(*) AS cnt FROM edges WHERE source_id = ? AND kind = 'calls'`,
111
+ );
110
112
 
111
- for (const row of rows) {
112
- if (noTests && isTestFile(row.file)) continue;
113
-
114
- const lineCount = row.end_line ? row.end_line - row.line + 1 : 0;
115
- const fanIn = fanInStmt.get(row.id).cnt;
116
- const fanOut = fanOutStmt.get(row.id).cnt;
117
- const key = makeSymbolKey(row.kind, row.file, row.name);
118
-
119
- symbols.set(key, {
120
- id: row.id,
121
- name: row.name,
122
- kind: row.kind,
123
- file: row.file,
124
- line: row.line,
125
- lineCount,
126
- fanIn,
127
- fanOut,
128
- });
129
- }
113
+ for (const row of rows) {
114
+ if (noTests && isTestFile(row.file)) continue;
115
+
116
+ const lineCount = row.end_line ? row.end_line - row.line + 1 : 0;
117
+ const fanIn = fanInStmt.get(row.id).cnt;
118
+ const fanOut = fanOutStmt.get(row.id).cnt;
119
+ const key = makeSymbolKey(row.kind, row.file, row.name);
120
+
121
+ symbols.set(key, {
122
+ id: row.id,
123
+ name: row.name,
124
+ kind: row.kind,
125
+ file: row.file,
126
+ line: row.line,
127
+ lineCount,
128
+ fanIn,
129
+ fanOut,
130
+ });
131
+ }
130
132
 
131
- db.close();
132
- return symbols;
133
+ return symbols;
134
+ } finally {
135
+ db.close();
136
+ }
133
137
  }
134
138
 
135
139
  // ─── Caller BFS ─────────────────────────────────────────────────────────
@@ -138,40 +142,43 @@ function loadCallersFromDb(dbPath, nodeIds, maxDepth, noTests) {
138
142
  if (nodeIds.length === 0) return [];
139
143
 
140
144
  const db = new Database(dbPath, { readonly: true });
141
- const allCallers = new Set();
142
-
143
- for (const startId of nodeIds) {
144
- const visited = new Set([startId]);
145
- let frontier = [startId];
146
-
147
- for (let d = 1; d <= maxDepth; d++) {
148
- const nextFrontier = [];
149
- for (const fid of frontier) {
150
- const callers = db
151
- .prepare(
152
- `SELECT DISTINCT n.id, n.name, n.kind, n.file, n.line
153
- FROM edges e JOIN nodes n ON e.source_id = n.id
154
- WHERE e.target_id = ? AND e.kind = 'calls'`,
155
- )
156
- .all(fid);
157
-
158
- for (const c of callers) {
159
- if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
160
- visited.add(c.id);
161
- nextFrontier.push(c.id);
162
- allCallers.add(
163
- JSON.stringify({ name: c.name, kind: c.kind, file: c.file, line: c.line }),
164
- );
145
+ try {
146
+ const allCallers = new Set();
147
+
148
+ for (const startId of nodeIds) {
149
+ const visited = new Set([startId]);
150
+ let frontier = [startId];
151
+
152
+ for (let d = 1; d <= maxDepth; d++) {
153
+ const nextFrontier = [];
154
+ for (const fid of frontier) {
155
+ const callers = db
156
+ .prepare(
157
+ `SELECT DISTINCT n.id, n.name, n.kind, n.file, n.line
158
+ FROM edges e JOIN nodes n ON e.source_id = n.id
159
+ WHERE e.target_id = ? AND e.kind = 'calls'`,
160
+ )
161
+ .all(fid);
162
+
163
+ for (const c of callers) {
164
+ if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
165
+ visited.add(c.id);
166
+ nextFrontier.push(c.id);
167
+ allCallers.add(
168
+ JSON.stringify({ name: c.name, kind: c.kind, file: c.file, line: c.line }),
169
+ );
170
+ }
165
171
  }
166
172
  }
173
+ frontier = nextFrontier;
174
+ if (frontier.length === 0) break;
167
175
  }
168
- frontier = nextFrontier;
169
- if (frontier.length === 0) break;
170
176
  }
171
- }
172
177
 
173
- db.close();
174
- return [...allCallers].map((s) => JSON.parse(s));
178
+ return [...allCallers].map((s) => JSON.parse(s));
179
+ } finally {
180
+ db.close();
181
+ }
175
182
  }
176
183
 
177
184
  // ─── Symbol Comparison ──────────────────────────────────────────────────
@@ -554,10 +561,8 @@ function formatText(data) {
554
561
  export async function branchCompare(baseRef, targetRef, opts = {}) {
555
562
  const data = await branchCompareData(baseRef, targetRef, opts);
556
563
 
557
- if (opts.json || opts.format === 'json') {
558
- console.log(JSON.stringify(data, null, 2));
559
- return;
560
- }
564
+ if (opts.format === 'json') opts = { ...opts, json: true };
565
+ if (outputResult(data, null, opts)) return;
561
566
 
562
567
  if (opts.format === 'mermaid') {
563
568
  console.log(branchCompareMermaid(data));