@optave/codegraph 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -190
- package/README.md +480 -311
- package/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-hcl.wasm +0 -0
- package/grammars/tree-sitter-java.wasm +0 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-php.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-ruby.wasm +0 -0
- package/grammars/tree-sitter-rust.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/package.json +90 -69
- package/src/builder.js +161 -162
- package/src/cli.js +284 -224
- package/src/config.js +61 -55
- package/src/constants.js +41 -28
- package/src/cycles.js +125 -104
- package/src/db.js +129 -117
- package/src/embedder.js +253 -59
- package/src/export.js +150 -138
- package/src/index.js +50 -39
- package/src/logger.js +24 -20
- package/src/mcp.js +145 -139
- package/src/native.js +68 -0
- package/src/parser.js +2214 -573
- package/src/queries.js +304 -128
- package/src/resolve.js +171 -0
- package/src/watcher.js +81 -53
package/src/cli.js
CHANGED
|
@@ -1,224 +1,284 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
.
|
|
30
|
-
.option('--
|
|
31
|
-
.
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
program
|
|
37
|
-
.command('
|
|
38
|
-
.description('
|
|
39
|
-
.option('-
|
|
40
|
-
.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
.
|
|
48
|
-
.
|
|
49
|
-
.option('-
|
|
50
|
-
.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.
|
|
57
|
-
.
|
|
58
|
-
.option('-
|
|
59
|
-
.option('-j, --json', 'Output as JSON')
|
|
60
|
-
.action((opts) => {
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
program
|
|
65
|
-
.command('
|
|
66
|
-
.description('
|
|
67
|
-
.option('-d, --db <path>', 'Path to graph.db')
|
|
68
|
-
.option('-
|
|
69
|
-
.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
.
|
|
76
|
-
.
|
|
77
|
-
.option('--
|
|
78
|
-
.option('-
|
|
79
|
-
.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
.
|
|
86
|
-
.
|
|
87
|
-
.option('
|
|
88
|
-
.option('--
|
|
89
|
-
.option('-
|
|
90
|
-
.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
.
|
|
100
|
-
.
|
|
101
|
-
.option('-
|
|
102
|
-
.option('
|
|
103
|
-
.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
.
|
|
115
|
-
.
|
|
116
|
-
.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
.
|
|
188
|
-
.
|
|
189
|
-
.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
.
|
|
198
|
-
.
|
|
199
|
-
.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
.action(async (dir) => {
|
|
220
|
-
const root = path.resolve(dir || '.');
|
|
221
|
-
await
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
program
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import Database from 'better-sqlite3';
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import { buildGraph } from './builder.js';
|
|
8
|
+
import { findCycles, formatCycles } from './cycles.js';
|
|
9
|
+
import { findDbPath } from './db.js';
|
|
10
|
+
import { buildEmbeddings, MODELS, search } from './embedder.js';
|
|
11
|
+
import { exportDOT, exportJSON, exportMermaid } from './export.js';
|
|
12
|
+
import { setVerbose } from './logger.js';
|
|
13
|
+
import {
|
|
14
|
+
diffImpact,
|
|
15
|
+
fileDeps,
|
|
16
|
+
fnDeps,
|
|
17
|
+
fnImpact,
|
|
18
|
+
impactAnalysis,
|
|
19
|
+
moduleMap,
|
|
20
|
+
queryName,
|
|
21
|
+
} from './queries.js';
|
|
22
|
+
import { watchProject } from './watcher.js';
|
|
23
|
+
|
|
24
|
+
const program = new Command();
|
|
25
|
+
program
|
|
26
|
+
.name('codegraph')
|
|
27
|
+
.description('Local code dependency graph tool')
|
|
28
|
+
.version('1.3.0')
|
|
29
|
+
.option('-v, --verbose', 'Enable verbose/debug output')
|
|
30
|
+
.option('--engine <engine>', 'Parser engine: native, wasm, or auto (default: auto)', 'auto')
|
|
31
|
+
.hook('preAction', (thisCommand) => {
|
|
32
|
+
const opts = thisCommand.opts();
|
|
33
|
+
if (opts.verbose) setVerbose(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
program
|
|
37
|
+
.command('build [dir]')
|
|
38
|
+
.description('Parse repo and build graph in .codegraph/graph.db')
|
|
39
|
+
.option('--no-incremental', 'Force full rebuild (ignore file hashes)')
|
|
40
|
+
.action(async (dir, opts) => {
|
|
41
|
+
const root = path.resolve(dir || '.');
|
|
42
|
+
const engine = program.opts().engine;
|
|
43
|
+
await buildGraph(root, { incremental: opts.incremental, engine });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
program
|
|
47
|
+
.command('query <name>')
|
|
48
|
+
.description('Find a function/class, show callers and callees')
|
|
49
|
+
.option('-d, --db <path>', 'Path to graph.db')
|
|
50
|
+
.option('-j, --json', 'Output as JSON')
|
|
51
|
+
.action((name, opts) => {
|
|
52
|
+
queryName(name, opts.db, { json: opts.json });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
program
|
|
56
|
+
.command('impact <file>')
|
|
57
|
+
.description('Show what depends on this file (transitive)')
|
|
58
|
+
.option('-d, --db <path>', 'Path to graph.db')
|
|
59
|
+
.option('-j, --json', 'Output as JSON')
|
|
60
|
+
.action((file, opts) => {
|
|
61
|
+
impactAnalysis(file, opts.db, { json: opts.json });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
program
|
|
65
|
+
.command('map')
|
|
66
|
+
.description('High-level module overview with most-connected nodes')
|
|
67
|
+
.option('-d, --db <path>', 'Path to graph.db')
|
|
68
|
+
.option('-n, --limit <number>', 'Number of top nodes', '20')
|
|
69
|
+
.option('-j, --json', 'Output as JSON')
|
|
70
|
+
.action((opts) => {
|
|
71
|
+
moduleMap(opts.db, parseInt(opts.limit, 10), { json: opts.json });
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
program
|
|
75
|
+
.command('deps <file>')
|
|
76
|
+
.description('Show what this file imports and what imports it')
|
|
77
|
+
.option('-d, --db <path>', 'Path to graph.db')
|
|
78
|
+
.option('-j, --json', 'Output as JSON')
|
|
79
|
+
.action((file, opts) => {
|
|
80
|
+
fileDeps(file, opts.db, { json: opts.json });
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
program
|
|
84
|
+
.command('fn <name>')
|
|
85
|
+
.description('Function-level dependencies: callers, callees, and transitive call chain')
|
|
86
|
+
.option('-d, --db <path>', 'Path to graph.db')
|
|
87
|
+
.option('--depth <n>', 'Transitive caller depth', '3')
|
|
88
|
+
.option('-T, --no-tests', 'Exclude test/spec files from results')
|
|
89
|
+
.option('-j, --json', 'Output as JSON')
|
|
90
|
+
.action((name, opts) => {
|
|
91
|
+
fnDeps(name, opts.db, {
|
|
92
|
+
depth: parseInt(opts.depth, 10),
|
|
93
|
+
noTests: !opts.tests,
|
|
94
|
+
json: opts.json,
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
program
|
|
99
|
+
.command('fn-impact <name>')
|
|
100
|
+
.description('Function-level impact: what functions break if this one changes')
|
|
101
|
+
.option('-d, --db <path>', 'Path to graph.db')
|
|
102
|
+
.option('--depth <n>', 'Max transitive depth', '5')
|
|
103
|
+
.option('-T, --no-tests', 'Exclude test/spec files from results')
|
|
104
|
+
.option('-j, --json', 'Output as JSON')
|
|
105
|
+
.action((name, opts) => {
|
|
106
|
+
fnImpact(name, opts.db, {
|
|
107
|
+
depth: parseInt(opts.depth, 10),
|
|
108
|
+
noTests: !opts.tests,
|
|
109
|
+
json: opts.json,
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
program
|
|
114
|
+
.command('diff-impact [ref]')
|
|
115
|
+
.description('Show impact of git changes (unstaged, staged, or vs a ref)')
|
|
116
|
+
.option('-d, --db <path>', 'Path to graph.db')
|
|
117
|
+
.option('--staged', 'Analyze staged changes instead of unstaged')
|
|
118
|
+
.option('--depth <n>', 'Max transitive caller depth', '3')
|
|
119
|
+
.option('-T, --no-tests', 'Exclude test/spec files from results')
|
|
120
|
+
.option('-j, --json', 'Output as JSON')
|
|
121
|
+
.action((ref, opts) => {
|
|
122
|
+
diffImpact(opts.db, {
|
|
123
|
+
ref,
|
|
124
|
+
staged: opts.staged,
|
|
125
|
+
depth: parseInt(opts.depth, 10),
|
|
126
|
+
noTests: !opts.tests,
|
|
127
|
+
json: opts.json,
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ─── New commands ────────────────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
program
|
|
134
|
+
.command('export')
|
|
135
|
+
.description('Export dependency graph as DOT (Graphviz), Mermaid, or JSON')
|
|
136
|
+
.option('-d, --db <path>', 'Path to graph.db')
|
|
137
|
+
.option('-f, --format <format>', 'Output format: dot, mermaid, json', 'dot')
|
|
138
|
+
.option('--functions', 'Function-level graph instead of file-level')
|
|
139
|
+
.option('-o, --output <file>', 'Write to file instead of stdout')
|
|
140
|
+
.action((opts) => {
|
|
141
|
+
const db = new Database(findDbPath(opts.db), { readonly: true });
|
|
142
|
+
const exportOpts = { fileLevel: !opts.functions };
|
|
143
|
+
|
|
144
|
+
let output;
|
|
145
|
+
switch (opts.format) {
|
|
146
|
+
case 'mermaid':
|
|
147
|
+
output = exportMermaid(db, exportOpts);
|
|
148
|
+
break;
|
|
149
|
+
case 'json':
|
|
150
|
+
output = JSON.stringify(exportJSON(db), null, 2);
|
|
151
|
+
break;
|
|
152
|
+
default:
|
|
153
|
+
output = exportDOT(db, exportOpts);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
db.close();
|
|
158
|
+
|
|
159
|
+
if (opts.output) {
|
|
160
|
+
fs.writeFileSync(opts.output, output, 'utf-8');
|
|
161
|
+
console.log(`Exported ${opts.format} to ${opts.output}`);
|
|
162
|
+
} else {
|
|
163
|
+
console.log(output);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
program
|
|
168
|
+
.command('cycles')
|
|
169
|
+
.description('Detect circular dependencies in the codebase')
|
|
170
|
+
.option('-d, --db <path>', 'Path to graph.db')
|
|
171
|
+
.option('--functions', 'Function-level cycle detection')
|
|
172
|
+
.option('-j, --json', 'Output as JSON')
|
|
173
|
+
.action((opts) => {
|
|
174
|
+
const db = new Database(findDbPath(opts.db), { readonly: true });
|
|
175
|
+
const cycles = findCycles(db, { fileLevel: !opts.functions });
|
|
176
|
+
db.close();
|
|
177
|
+
|
|
178
|
+
if (opts.json) {
|
|
179
|
+
console.log(JSON.stringify({ cycles, count: cycles.length }, null, 2));
|
|
180
|
+
} else {
|
|
181
|
+
console.log(formatCycles(cycles));
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
program
|
|
186
|
+
.command('mcp')
|
|
187
|
+
.description('Start MCP (Model Context Protocol) server for AI assistant integration')
|
|
188
|
+
.option('-d, --db <path>', 'Path to graph.db')
|
|
189
|
+
.action(async (opts) => {
|
|
190
|
+
const { startMCPServer } = await import('./mcp.js');
|
|
191
|
+
await startMCPServer(opts.db);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// ─── Embedding commands ─────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
program
|
|
197
|
+
.command('models')
|
|
198
|
+
.description('List available embedding models')
|
|
199
|
+
.action(() => {
|
|
200
|
+
console.log('\nAvailable embedding models:\n');
|
|
201
|
+
for (const [key, config] of Object.entries(MODELS)) {
|
|
202
|
+
const def = key === 'minilm' ? ' (default)' : '';
|
|
203
|
+
console.log(` ${key.padEnd(12)} ${String(config.dim).padStart(4)}d ${config.desc}${def}`);
|
|
204
|
+
}
|
|
205
|
+
console.log('\nUsage: codegraph embed --model <name>');
|
|
206
|
+
console.log(' codegraph search "query" --model <name>\n');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
program
|
|
210
|
+
.command('embed [dir]')
|
|
211
|
+
.description(
|
|
212
|
+
'Build semantic embeddings for all functions/methods/classes (requires prior `build`)',
|
|
213
|
+
)
|
|
214
|
+
.option(
|
|
215
|
+
'-m, --model <name>',
|
|
216
|
+
'Embedding model: minilm (default), jina-small, jina-base, jina-code, nomic, nomic-v1.5, bge-large. Run `codegraph models` for details',
|
|
217
|
+
'minilm',
|
|
218
|
+
)
|
|
219
|
+
.action(async (dir, opts) => {
|
|
220
|
+
const root = path.resolve(dir || '.');
|
|
221
|
+
await buildEmbeddings(root, opts.model);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
program
|
|
225
|
+
.command('search <query>')
|
|
226
|
+
.description('Semantic search: find functions by natural language description')
|
|
227
|
+
.option('-d, --db <path>', 'Path to graph.db')
|
|
228
|
+
.option('-m, --model <name>', 'Override embedding model (auto-detects from DB)')
|
|
229
|
+
.option('-n, --limit <number>', 'Max results', '15')
|
|
230
|
+
.option('-T, --no-tests', 'Exclude test/spec files')
|
|
231
|
+
.option('--min-score <score>', 'Minimum similarity threshold', '0.2')
|
|
232
|
+
.option('-k, --kind <kind>', 'Filter by kind: function, method, class')
|
|
233
|
+
.option('--file <pattern>', 'Filter by file path pattern')
|
|
234
|
+
.option('--rrf-k <number>', 'RRF k parameter for multi-query ranking', '60')
|
|
235
|
+
.action(async (query, opts) => {
|
|
236
|
+
await search(query, opts.db, {
|
|
237
|
+
limit: parseInt(opts.limit, 10),
|
|
238
|
+
noTests: !opts.tests,
|
|
239
|
+
minScore: parseFloat(opts.minScore),
|
|
240
|
+
model: opts.model,
|
|
241
|
+
kind: opts.kind,
|
|
242
|
+
filePattern: opts.file,
|
|
243
|
+
rrfK: parseInt(opts.rrfK, 10),
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
program
|
|
248
|
+
.command('watch [dir]')
|
|
249
|
+
.description('Watch project for file changes and incrementally update the graph')
|
|
250
|
+
.action(async (dir) => {
|
|
251
|
+
const root = path.resolve(dir || '.');
|
|
252
|
+
const engine = program.opts().engine;
|
|
253
|
+
await watchProject(root, { engine });
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
program
|
|
257
|
+
.command('info')
|
|
258
|
+
.description('Show codegraph engine info and diagnostics')
|
|
259
|
+
.action(async () => {
|
|
260
|
+
const { isNativeAvailable, loadNative } = await import('./native.js');
|
|
261
|
+
const { getActiveEngine } = await import('./parser.js');
|
|
262
|
+
|
|
263
|
+
const engine = program.opts().engine;
|
|
264
|
+
const { name: activeName, version: activeVersion } = getActiveEngine({ engine });
|
|
265
|
+
const nativeAvailable = isNativeAvailable();
|
|
266
|
+
|
|
267
|
+
console.log('\nCodegraph Diagnostics');
|
|
268
|
+
console.log('====================');
|
|
269
|
+
console.log(` Version : ${program.version()}`);
|
|
270
|
+
console.log(` Node.js : ${process.version}`);
|
|
271
|
+
console.log(` Platform : ${process.platform}-${process.arch}`);
|
|
272
|
+
console.log(` Native engine : ${nativeAvailable ? 'available' : 'unavailable'}`);
|
|
273
|
+
if (nativeAvailable) {
|
|
274
|
+
const native = loadNative();
|
|
275
|
+
const nativeVersion =
|
|
276
|
+
typeof native.engineVersion === 'function' ? native.engineVersion() : 'unknown';
|
|
277
|
+
console.log(` Native version: ${nativeVersion}`);
|
|
278
|
+
}
|
|
279
|
+
console.log(` Engine flag : --engine ${engine}`);
|
|
280
|
+
console.log(` Active engine : ${activeName}${activeVersion ? ` (v${activeVersion})` : ''}`);
|
|
281
|
+
console.log();
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
program.parse();
|
package/src/config.js
CHANGED
|
@@ -1,55 +1,61 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { debug } from './logger.js';
|
|
4
|
-
|
|
5
|
-
export const CONFIG_FILES = ['.codegraphrc.json', '.codegraphrc', 'codegraph.config.json'];
|
|
6
|
-
|
|
7
|
-
export const DEFAULTS = {
|
|
8
|
-
include: [],
|
|
9
|
-
exclude: [],
|
|
10
|
-
ignoreDirs: [],
|
|
11
|
-
extensions: [],
|
|
12
|
-
aliases: {},
|
|
13
|
-
build: {
|
|
14
|
-
incremental: true,
|
|
15
|
-
dbPath: '.codegraph/graph.db'
|
|
16
|
-
},
|
|
17
|
-
query: {
|
|
18
|
-
defaultDepth: 3,
|
|
19
|
-
defaultLimit: 20
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Load project configuration from a .codegraphrc.json or similar file.
|
|
25
|
-
* Returns merged config with defaults.
|
|
26
|
-
*/
|
|
27
|
-
export function loadConfig(cwd) {
|
|
28
|
-
cwd = cwd || process.cwd();
|
|
29
|
-
for (const name of CONFIG_FILES) {
|
|
30
|
-
const filePath = path.join(cwd, name);
|
|
31
|
-
if (fs.existsSync(filePath)) {
|
|
32
|
-
try {
|
|
33
|
-
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
34
|
-
const config = JSON.parse(raw);
|
|
35
|
-
debug(`Loaded config from ${filePath}`);
|
|
36
|
-
return mergeConfig(DEFAULTS, config);
|
|
37
|
-
} catch (err) {
|
|
38
|
-
debug(`Failed to parse config ${filePath}: ${err.message}`);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return { ...DEFAULTS };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function mergeConfig(defaults, overrides) {
|
|
46
|
-
const result = { ...defaults };
|
|
47
|
-
for (const [key, value] of Object.entries(overrides)) {
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { debug } from './logger.js';
|
|
4
|
+
|
|
5
|
+
export const CONFIG_FILES = ['.codegraphrc.json', '.codegraphrc', 'codegraph.config.json'];
|
|
6
|
+
|
|
7
|
+
export const DEFAULTS = {
|
|
8
|
+
include: [],
|
|
9
|
+
exclude: [],
|
|
10
|
+
ignoreDirs: [],
|
|
11
|
+
extensions: [],
|
|
12
|
+
aliases: {},
|
|
13
|
+
build: {
|
|
14
|
+
incremental: true,
|
|
15
|
+
dbPath: '.codegraph/graph.db',
|
|
16
|
+
},
|
|
17
|
+
query: {
|
|
18
|
+
defaultDepth: 3,
|
|
19
|
+
defaultLimit: 20,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Load project configuration from a .codegraphrc.json or similar file.
|
|
25
|
+
* Returns merged config with defaults.
|
|
26
|
+
*/
|
|
27
|
+
export function loadConfig(cwd) {
|
|
28
|
+
cwd = cwd || process.cwd();
|
|
29
|
+
for (const name of CONFIG_FILES) {
|
|
30
|
+
const filePath = path.join(cwd, name);
|
|
31
|
+
if (fs.existsSync(filePath)) {
|
|
32
|
+
try {
|
|
33
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
34
|
+
const config = JSON.parse(raw);
|
|
35
|
+
debug(`Loaded config from ${filePath}`);
|
|
36
|
+
return mergeConfig(DEFAULTS, config);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
debug(`Failed to parse config ${filePath}: ${err.message}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { ...DEFAULTS };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function mergeConfig(defaults, overrides) {
|
|
46
|
+
const result = { ...defaults };
|
|
47
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
48
|
+
if (
|
|
49
|
+
value &&
|
|
50
|
+
typeof value === 'object' &&
|
|
51
|
+
!Array.isArray(value) &&
|
|
52
|
+
defaults[key] &&
|
|
53
|
+
typeof defaults[key] === 'object'
|
|
54
|
+
) {
|
|
55
|
+
result[key] = { ...defaults[key], ...value };
|
|
56
|
+
} else {
|
|
57
|
+
result[key] = value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|