@tekyzinc/gsd-t 2.50.12 → 2.53.10
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/CHANGELOG.md +24 -0
- package/README.md +379 -372
- package/bin/component-registry.js +250 -0
- package/bin/graph-cgc.js +510 -510
- package/bin/graph-indexer.js +147 -147
- package/bin/graph-overlay.js +195 -195
- package/bin/graph-parsers.js +327 -327
- package/bin/graph-query.js +453 -452
- package/bin/graph-store.js +154 -154
- package/bin/qa-calibrator.js +194 -0
- package/bin/scan-data-collector.js +153 -153
- package/bin/scan-diagrams-generators.js +187 -187
- package/bin/scan-diagrams.js +79 -79
- package/bin/scan-renderer.js +92 -92
- package/bin/scan-report-sections.js +121 -121
- package/bin/scan-report.js +184 -184
- package/bin/scan-schema-parsers.js +199 -199
- package/bin/scan-schema.js +103 -103
- package/bin/token-budget.js +246 -0
- package/commands/Claude-md.md +10 -10
- package/commands/branch.md +15 -15
- package/commands/checkin.md +45 -45
- package/commands/global-change.md +209 -209
- package/commands/gsd-t-audit.md +199 -0
- package/commands/gsd-t-backlog-add.md +94 -94
- package/commands/gsd-t-backlog-edit.md +111 -111
- package/commands/gsd-t-backlog-list.md +63 -63
- package/commands/gsd-t-backlog-move.md +94 -94
- package/commands/gsd-t-backlog-promote.md +123 -123
- package/commands/gsd-t-backlog-remove.md +86 -86
- package/commands/gsd-t-backlog-settings.md +158 -158
- package/commands/gsd-t-complete-milestone.md +528 -515
- package/commands/gsd-t-debug.md +506 -399
- package/commands/gsd-t-discuss.md +174 -174
- package/commands/gsd-t-execute.md +758 -634
- package/commands/gsd-t-feature.md +276 -276
- package/commands/gsd-t-health.md +142 -142
- package/commands/gsd-t-help.md +465 -457
- package/commands/gsd-t-impact.md +302 -302
- package/commands/gsd-t-init.md +320 -280
- package/commands/gsd-t-integrate.md +365 -249
- package/commands/gsd-t-milestone.md +87 -87
- package/commands/gsd-t-partition.md +442 -361
- package/commands/gsd-t-pause.md +82 -82
- package/commands/gsd-t-plan.md +345 -344
- package/commands/gsd-t-populate.md +111 -111
- package/commands/gsd-t-prd.md +326 -326
- package/commands/gsd-t-project.md +211 -211
- package/commands/gsd-t-promote-debt.md +123 -123
- package/commands/gsd-t-prompt.md +137 -137
- package/commands/gsd-t-qa.md +266 -266
- package/commands/gsd-t-quick.md +357 -234
- package/commands/gsd-t-reflect.md +134 -134
- package/commands/gsd-t-resume.md +72 -72
- package/commands/gsd-t-scan.md +615 -615
- package/commands/gsd-t-setup.md +76 -0
- package/commands/gsd-t-status.md +192 -166
- package/commands/gsd-t-test-sync.md +381 -381
- package/commands/gsd-t-triage-and-merge.md +171 -171
- package/commands/gsd-t-verify.md +382 -382
- package/commands/gsd-t-visualize.md +118 -118
- package/commands/gsd-t-wave.md +401 -378
- package/docs/GSD-T-README.md +425 -422
- package/docs/architecture.md +385 -369
- package/docs/harness-design-analysis.md +371 -0
- package/docs/infrastructure.md +205 -205
- package/docs/prd-graph-engine.md +398 -398
- package/docs/prd-gsd2-hybrid.md +559 -559
- package/docs/prd-harness-evolution.md +583 -0
- package/docs/requirements.md +14 -0
- package/docs/workflows.md +226 -226
- package/examples/.gsd-t/domains/example-domain/scope.md +13 -13
- package/package.json +40 -40
- package/scripts/gsd-t-auto-route.js +39 -39
- package/scripts/gsd-t-dashboard-mockup.html +1143 -1143
- package/scripts/gsd-t-dashboard-server.js +171 -171
- package/scripts/gsd-t-dashboard.html +262 -262
- package/scripts/gsd-t-event-writer.js +128 -128
- package/scripts/gsd-t-statusline.js +94 -94
- package/scripts/gsd-t-tools.js +175 -175
- package/templates/CLAUDE-global.md +639 -614
- package/templates/CLAUDE-project.md +24 -0
- package/templates/backlog-settings.md +18 -18
- package/templates/backlog.md +1 -1
- package/templates/progress.md +40 -40
- package/templates/shared-services-contract.md +60 -60
- package/templates/stacks/desktop.ini +2 -2
- package/bin/desktop.ini +0 -2
- package/commands/desktop.ini +0 -2
- package/docs/ci-examples/desktop.ini +0 -2
- package/docs/desktop.ini +0 -2
- package/examples/.gsd-t/contracts/desktop.ini +0 -2
- package/examples/.gsd-t/desktop.ini +0 -2
- package/examples/.gsd-t/domains/desktop.ini +0 -2
- package/examples/.gsd-t/domains/example-domain/desktop.ini +0 -2
- package/examples/desktop.ini +0 -2
- package/examples/rules/desktop.ini +0 -2
- package/scripts/desktop.ini +0 -2
- package/templates/desktop.ini +0 -2
package/bin/graph-cgc.js
CHANGED
|
@@ -1,510 +1,510 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const { execFileSync, spawn } = require('child_process');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const store = require('./graph-store');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* CGC (CodeGraphContext) MCP provider.
|
|
8
|
-
* Communicates with CGC via JSON-RPC over stdio (MCP protocol).
|
|
9
|
-
* Falls back gracefully when CGC is unavailable.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
let healthCache = null;
|
|
13
|
-
let cgcProcess = null;
|
|
14
|
-
let requestId = 0;
|
|
15
|
-
|
|
16
|
-
// --- CGC Process Management ---
|
|
17
|
-
|
|
18
|
-
function findCgcCommand() {
|
|
19
|
-
// Check if 'cgc' is available on PATH
|
|
20
|
-
try {
|
|
21
|
-
execFileSync('cgc', ['--version'], {
|
|
22
|
-
encoding: 'utf8',
|
|
23
|
-
timeout: 3000,
|
|
24
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
25
|
-
});
|
|
26
|
-
return 'cgc';
|
|
27
|
-
} catch { /* not found */ }
|
|
28
|
-
|
|
29
|
-
// Check common Python locations
|
|
30
|
-
const paths = [
|
|
31
|
-
'codegraphcontext',
|
|
32
|
-
'python -m codegraphcontext'
|
|
33
|
-
];
|
|
34
|
-
for (const cmd of paths) {
|
|
35
|
-
try {
|
|
36
|
-
const [bin, ...args] = cmd.split(' ');
|
|
37
|
-
execFileSync(bin, [...args, '--version'], {
|
|
38
|
-
encoding: 'utf8',
|
|
39
|
-
timeout: 3000,
|
|
40
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
41
|
-
});
|
|
42
|
-
return cmd;
|
|
43
|
-
} catch { /* not found */ }
|
|
44
|
-
}
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function startCgcServer() {
|
|
49
|
-
const cmd = findCgcCommand();
|
|
50
|
-
if (!cmd) return null;
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
const [bin, ...baseArgs] = cmd.split(' ');
|
|
54
|
-
const args = [...baseArgs, 'mcp', 'start'];
|
|
55
|
-
const proc = spawn(bin, args, {
|
|
56
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
57
|
-
env: { ...process.env }
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
proc.on('error', () => { cgcProcess = null; });
|
|
61
|
-
proc.on('exit', () => { cgcProcess = null; });
|
|
62
|
-
|
|
63
|
-
return proc;
|
|
64
|
-
} catch { return null; }
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function stopCgcServer() {
|
|
68
|
-
if (cgcProcess) {
|
|
69
|
-
try { cgcProcess.kill(); } catch { /* ignore */ }
|
|
70
|
-
cgcProcess = null;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// --- JSON-RPC over stdio ---
|
|
75
|
-
|
|
76
|
-
function sendRequest(proc, method, params) {
|
|
77
|
-
return new Promise((resolve, reject) => {
|
|
78
|
-
const id = ++requestId;
|
|
79
|
-
const request = JSON.stringify({
|
|
80
|
-
jsonrpc: '2.0',
|
|
81
|
-
id,
|
|
82
|
-
method,
|
|
83
|
-
params: params || {}
|
|
84
|
-
}) + '\n';
|
|
85
|
-
|
|
86
|
-
let responseData = '';
|
|
87
|
-
const timeout = setTimeout(() => {
|
|
88
|
-
proc.stdout.removeListener('data', onData);
|
|
89
|
-
reject(new Error('CGC request timeout'));
|
|
90
|
-
}, 10000);
|
|
91
|
-
|
|
92
|
-
function onData(chunk) {
|
|
93
|
-
responseData += chunk.toString();
|
|
94
|
-
const lines = responseData.split('\n');
|
|
95
|
-
for (const line of lines) {
|
|
96
|
-
if (!line.trim()) continue;
|
|
97
|
-
try {
|
|
98
|
-
const resp = JSON.parse(line.trim());
|
|
99
|
-
if (resp.id === id) {
|
|
100
|
-
clearTimeout(timeout);
|
|
101
|
-
proc.stdout.removeListener('data', onData);
|
|
102
|
-
if (resp.error) {
|
|
103
|
-
reject(new Error(resp.error.message));
|
|
104
|
-
} else {
|
|
105
|
-
resolve(resp.result);
|
|
106
|
-
}
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
} catch { /* partial line, keep reading */ }
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
proc.stdout.on('data', onData);
|
|
114
|
-
proc.stdin.write(request);
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function sendRequestSync(proc, method, params, timeoutMs) {
|
|
119
|
-
// Synchronous wrapper using execFileSync workaround
|
|
120
|
-
// For the sync API that GSD-T commands expect,
|
|
121
|
-
// we use a helper script pattern
|
|
122
|
-
const ms = timeoutMs || 10000;
|
|
123
|
-
try {
|
|
124
|
-
const script = `
|
|
125
|
-
const net = require('net');
|
|
126
|
-
const req = ${JSON.stringify({
|
|
127
|
-
jsonrpc: '2.0',
|
|
128
|
-
id: 1,
|
|
129
|
-
method,
|
|
130
|
-
params: params || {}
|
|
131
|
-
})};
|
|
132
|
-
process.stdin.resume();
|
|
133
|
-
process.stdout.write(JSON.stringify(req) + '\\n');
|
|
134
|
-
`;
|
|
135
|
-
// This won't work for stdio — we need the async approach
|
|
136
|
-
// Fall back to spawning a one-shot process
|
|
137
|
-
return sendToolCallSync(method, params, ms);
|
|
138
|
-
} catch { return null; }
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function sendToolCallSync(toolName, args, timeoutMs) {
|
|
142
|
-
const cmd = findCgcCommand();
|
|
143
|
-
if (!cmd) return null;
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
const [bin, ...baseArgs] = cmd.split(' ');
|
|
147
|
-
const request = JSON.stringify({
|
|
148
|
-
jsonrpc: '2.0',
|
|
149
|
-
id: 1,
|
|
150
|
-
method: 'tools/call',
|
|
151
|
-
params: { name: toolName, arguments: args || {} }
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Spawn CGC, send request, read response
|
|
155
|
-
const result = execFileSync(bin, [...baseArgs, 'mcp', 'start'], {
|
|
156
|
-
input: JSON.stringify({
|
|
157
|
-
jsonrpc: '2.0', id: 0,
|
|
158
|
-
method: 'initialize', params: {}
|
|
159
|
-
}) + '\n' +
|
|
160
|
-
JSON.stringify({
|
|
161
|
-
jsonrpc: '2.0', id: 1,
|
|
162
|
-
method: 'notifications/initialized', params: {}
|
|
163
|
-
}) + '\n' + request + '\n',
|
|
164
|
-
encoding: 'utf8',
|
|
165
|
-
timeout: timeoutMs || 10000,
|
|
166
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// Parse response lines — find the one with id: 1
|
|
170
|
-
const lines = result.split('\n');
|
|
171
|
-
for (const line of lines) {
|
|
172
|
-
if (!line.trim()) continue;
|
|
173
|
-
try {
|
|
174
|
-
const resp = JSON.parse(line.trim());
|
|
175
|
-
if (resp.id === 1 && resp.result) {
|
|
176
|
-
// Extract text content from MCP response
|
|
177
|
-
const content = resp.result.content;
|
|
178
|
-
if (Array.isArray(content) && content[0]) {
|
|
179
|
-
return JSON.parse(content[0].text);
|
|
180
|
-
}
|
|
181
|
-
return resp.result;
|
|
182
|
-
}
|
|
183
|
-
} catch { /* skip non-JSON lines */ }
|
|
184
|
-
}
|
|
185
|
-
return null;
|
|
186
|
-
} catch { return null; }
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// --- Health Detection ---
|
|
190
|
-
|
|
191
|
-
function checkCgcHealth() {
|
|
192
|
-
if (healthCache) return healthCache;
|
|
193
|
-
|
|
194
|
-
const cmd = findCgcCommand();
|
|
195
|
-
if (!cmd) {
|
|
196
|
-
healthCache = {
|
|
197
|
-
available: false,
|
|
198
|
-
version: null,
|
|
199
|
-
capabilities: [],
|
|
200
|
-
command: null
|
|
201
|
-
};
|
|
202
|
-
return healthCache;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// CGC binary exists — try to get version
|
|
206
|
-
let version = null;
|
|
207
|
-
try {
|
|
208
|
-
const [bin, ...args] = cmd.split(' ');
|
|
209
|
-
version = execFileSync(bin, [...args, '--version'], {
|
|
210
|
-
encoding: 'utf8',
|
|
211
|
-
timeout: 3000,
|
|
212
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
213
|
-
}).trim();
|
|
214
|
-
} catch { /* version unknown */ }
|
|
215
|
-
|
|
216
|
-
healthCache = {
|
|
217
|
-
available: true,
|
|
218
|
-
version,
|
|
219
|
-
command: cmd,
|
|
220
|
-
capabilities: [
|
|
221
|
-
'analyze_code_relationships',
|
|
222
|
-
'find_dead_code',
|
|
223
|
-
'find_code',
|
|
224
|
-
'find_most_complex_functions',
|
|
225
|
-
'calculate_cyclomatic_complexity',
|
|
226
|
-
'execute_cypher_query',
|
|
227
|
-
'add_code_to_graph',
|
|
228
|
-
'get_repository_stats',
|
|
229
|
-
'watch_directory',
|
|
230
|
-
'visualize_graph_query'
|
|
231
|
-
]
|
|
232
|
-
};
|
|
233
|
-
return healthCache;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function resetHealthCache() {
|
|
237
|
-
healthCache = null;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// --- Query Translation ---
|
|
241
|
-
|
|
242
|
-
function cgcQuery(toolName, args) {
|
|
243
|
-
const health = checkCgcHealth();
|
|
244
|
-
if (!health.available) return null;
|
|
245
|
-
return sendToolCallSync(toolName, args);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// --- GSD-T Overlay Enrichment ---
|
|
249
|
-
|
|
250
|
-
function enrichWithOverlay(cgcResults, projectRoot) {
|
|
251
|
-
if (!cgcResults || !Array.isArray(cgcResults)) return [];
|
|
252
|
-
|
|
253
|
-
const contracts = store.readContracts(projectRoot);
|
|
254
|
-
const requirements = store.readRequirements(projectRoot);
|
|
255
|
-
const tests = store.readTests(projectRoot);
|
|
256
|
-
const surfaces = store.readSurfaces(projectRoot);
|
|
257
|
-
|
|
258
|
-
return cgcResults.map(entity => {
|
|
259
|
-
const id = entity.id || `${entity.file}:${entity.line}:${entity.name}`;
|
|
260
|
-
const contractMap = contracts.mappings.find(
|
|
261
|
-
m => m.entity === id
|
|
262
|
-
);
|
|
263
|
-
const reqMap = requirements.mappings.find(
|
|
264
|
-
m => m.entity === id
|
|
265
|
-
);
|
|
266
|
-
const testMaps = tests.mappings.filter(
|
|
267
|
-
m => m.entity === id
|
|
268
|
-
);
|
|
269
|
-
const surfaceMap = surfaces.mappings.find(
|
|
270
|
-
m => m.entity === id
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
return {
|
|
274
|
-
...entity,
|
|
275
|
-
id,
|
|
276
|
-
contract: contractMap ? contractMap.contract : null,
|
|
277
|
-
requirement: reqMap ? reqMap.requirement : null,
|
|
278
|
-
tests: testMaps,
|
|
279
|
-
surfaces: surfaceMap ? surfaceMap.surfaces : []
|
|
280
|
-
};
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// --- Normalize CGC Results to GSD-T Entity Shape ---
|
|
285
|
-
|
|
286
|
-
function normalizeEntity(cgcEntity) {
|
|
287
|
-
const name = cgcEntity.name || cgcEntity.function_name
|
|
288
|
-
|| cgcEntity.caller_function || cgcEntity.symbol || '';
|
|
289
|
-
const file = cgcEntity.file_path || cgcEntity.caller_file_path
|
|
290
|
-
|| cgcEntity.path || '';
|
|
291
|
-
const line = cgcEntity.line_number || cgcEntity.caller_line_number
|
|
292
|
-
|| cgcEntity.line || 0;
|
|
293
|
-
return {
|
|
294
|
-
id: `${file}:${line}:${name}`,
|
|
295
|
-
name,
|
|
296
|
-
type: cgcEntity.type || cgcEntity.kind || 'function',
|
|
297
|
-
file,
|
|
298
|
-
line,
|
|
299
|
-
domain: null,
|
|
300
|
-
exported: cgcEntity.exported !== false,
|
|
301
|
-
complexity: cgcEntity.complexity || cgcEntity.cyclomatic_complexity || null,
|
|
302
|
-
source: cgcEntity.source || null,
|
|
303
|
-
callArgs: cgcEntity.call_args || null,
|
|
304
|
-
callLine: cgcEntity.call_line_number || null
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
function normalizeResults(cgcResult) {
|
|
309
|
-
if (!cgcResult) return null;
|
|
310
|
-
|
|
311
|
-
// Direct array
|
|
312
|
-
if (Array.isArray(cgcResult)) {
|
|
313
|
-
return cgcResult.map(normalizeEntity);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// CGC wraps in .results which can be object or array
|
|
317
|
-
const r = cgcResult.results;
|
|
318
|
-
if (r) {
|
|
319
|
-
// find_callers/callees: results.results[]
|
|
320
|
-
if (r.results && Array.isArray(r.results)) {
|
|
321
|
-
return r.results.map(normalizeEntity);
|
|
322
|
-
}
|
|
323
|
-
// dead_code: results.potentially_unused_functions[]
|
|
324
|
-
if (r.potentially_unused_functions) {
|
|
325
|
-
return r.potentially_unused_functions.map(normalizeEntity);
|
|
326
|
-
}
|
|
327
|
-
// find_code: results.functions_by_name[]
|
|
328
|
-
if (r.functions_by_name) {
|
|
329
|
-
return r.functions_by_name.map(normalizeEntity);
|
|
330
|
-
}
|
|
331
|
-
// find_code: results.functions_containing[]
|
|
332
|
-
if (r.functions_containing) {
|
|
333
|
-
return r.functions_containing.map(normalizeEntity);
|
|
334
|
-
}
|
|
335
|
-
// Direct array of results (complexity)
|
|
336
|
-
if (Array.isArray(r)) {
|
|
337
|
-
return r.map(normalizeEntity);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Legacy shapes
|
|
342
|
-
if (cgcResult.functions) {
|
|
343
|
-
return cgcResult.functions.map(normalizeEntity);
|
|
344
|
-
}
|
|
345
|
-
if (cgcResult.dead_code) {
|
|
346
|
-
return cgcResult.dead_code.map(normalizeEntity);
|
|
347
|
-
}
|
|
348
|
-
if (cgcResult.matches) {
|
|
349
|
-
return cgcResult.matches.map(normalizeEntity);
|
|
350
|
-
}
|
|
351
|
-
return null;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// --- Provider Interface ---
|
|
355
|
-
|
|
356
|
-
const cgcProvider = {
|
|
357
|
-
name: 'cgc',
|
|
358
|
-
priority: 1,
|
|
359
|
-
_projectRoot: null,
|
|
360
|
-
setProjectRoot(root) { this._projectRoot = root; },
|
|
361
|
-
|
|
362
|
-
available() {
|
|
363
|
-
return checkCgcHealth().available;
|
|
364
|
-
},
|
|
365
|
-
|
|
366
|
-
query(type, params, projectRoot) {
|
|
367
|
-
const root = projectRoot || this._projectRoot;
|
|
368
|
-
|
|
369
|
-
switch (type) {
|
|
370
|
-
case 'getCallers': {
|
|
371
|
-
const result = cgcQuery('analyze_code_relationships', {
|
|
372
|
-
query_type: 'find_callers',
|
|
373
|
-
target: params.entity
|
|
374
|
-
});
|
|
375
|
-
const entities = normalizeResults(result);
|
|
376
|
-
return entities
|
|
377
|
-
? enrichWithOverlay(entities, root) : null;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
case 'getTransitiveCallers': {
|
|
381
|
-
const result = cgcQuery('analyze_code_relationships', {
|
|
382
|
-
query_type: 'find_all_callers',
|
|
383
|
-
target: params.entity
|
|
384
|
-
});
|
|
385
|
-
const entities = normalizeResults(result);
|
|
386
|
-
return entities
|
|
387
|
-
? enrichWithOverlay(entities, root) : null;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
case 'getCallees': {
|
|
391
|
-
const result = cgcQuery('analyze_code_relationships', {
|
|
392
|
-
query_type: 'find_callees',
|
|
393
|
-
target: params.entity
|
|
394
|
-
});
|
|
395
|
-
const entities = normalizeResults(result);
|
|
396
|
-
return entities
|
|
397
|
-
? enrichWithOverlay(entities, root) : null;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
case 'getTransitiveCallees': {
|
|
401
|
-
const result = cgcQuery('analyze_code_relationships', {
|
|
402
|
-
query_type: 'find_all_callees',
|
|
403
|
-
target: params.entity
|
|
404
|
-
});
|
|
405
|
-
const entities = normalizeResults(result);
|
|
406
|
-
return entities
|
|
407
|
-
? enrichWithOverlay(entities, root) : null;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
case 'findDeadCode': {
|
|
411
|
-
const result = cgcQuery('find_dead_code', {
|
|
412
|
-
exclude_decorated_with: params.exclude || []
|
|
413
|
-
});
|
|
414
|
-
const entities = normalizeResults(result);
|
|
415
|
-
return entities
|
|
416
|
-
? enrichWithOverlay(entities, root) : null;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
case 'findComplexFunctions': {
|
|
420
|
-
const result = cgcQuery('find_most_complex_functions', {
|
|
421
|
-
top_n: params.limit || 20
|
|
422
|
-
});
|
|
423
|
-
return normalizeResults(result);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
case 'getComplexity': {
|
|
427
|
-
const result = cgcQuery('calculate_cyclomatic_complexity', {
|
|
428
|
-
function_name: params.name,
|
|
429
|
-
file_path: params.file || undefined
|
|
430
|
-
});
|
|
431
|
-
return result;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
case 'findDuplicates': {
|
|
435
|
-
const result = cgcQuery('find_code', {
|
|
436
|
-
query: params.target || '*',
|
|
437
|
-
fuzzy_search: true,
|
|
438
|
-
edit_distance: 2
|
|
439
|
-
});
|
|
440
|
-
return normalizeResults(result);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
case 'findCircularDeps': {
|
|
444
|
-
const result = cgcQuery('execute_cypher_query', {
|
|
445
|
-
cypher_query: `MATCH path = (a:Function)-[:CALLS*2..${params.maxDepth || 5}]->(a) RETURN a.name AS name, a.path AS file, a.line_number AS line LIMIT 20`
|
|
446
|
-
});
|
|
447
|
-
if (!result) return null;
|
|
448
|
-
return Array.isArray(result)
|
|
449
|
-
? result.map(normalizeEntity)
|
|
450
|
-
: [];
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
case 'getEntity': {
|
|
454
|
-
const result = cgcQuery('find_code', {
|
|
455
|
-
query: params.name,
|
|
456
|
-
fuzzy_search: false
|
|
457
|
-
});
|
|
458
|
-
const entities = normalizeResults(result);
|
|
459
|
-
return entities && entities.length > 0
|
|
460
|
-
? enrichWithOverlay([entities[0]], root)[0]
|
|
461
|
-
: null;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
case 'getCallChain': {
|
|
465
|
-
const result = cgcQuery('analyze_code_relationships', {
|
|
466
|
-
query_type: 'call_chain',
|
|
467
|
-
target: params.from,
|
|
468
|
-
context: params.to
|
|
469
|
-
});
|
|
470
|
-
return normalizeResults(result);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
case 'getModuleDeps': {
|
|
474
|
-
const result = cgcQuery('analyze_code_relationships', {
|
|
475
|
-
query_type: 'module_deps',
|
|
476
|
-
target: params.module
|
|
477
|
-
});
|
|
478
|
-
return normalizeResults(result);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
case 'getClassHierarchy': {
|
|
482
|
-
const result = cgcQuery('analyze_code_relationships', {
|
|
483
|
-
query_type: 'class_hierarchy',
|
|
484
|
-
target: params.className
|
|
485
|
-
});
|
|
486
|
-
return normalizeResults(result);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
case 'getStats': {
|
|
490
|
-
return cgcQuery('get_repository_stats', {});
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
case 'cypher': {
|
|
494
|
-
return cgcQuery('execute_cypher_query', {
|
|
495
|
-
cypher_query: params.query
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
default:
|
|
500
|
-
return null;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
};
|
|
504
|
-
|
|
505
|
-
module.exports = {
|
|
506
|
-
cgcProvider, checkCgcHealth, resetHealthCache,
|
|
507
|
-
cgcQuery, enrichWithOverlay, normalizeEntity,
|
|
508
|
-
normalizeResults, findCgcCommand, sendToolCallSync,
|
|
509
|
-
stopCgcServer
|
|
510
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
const { execFileSync, spawn } = require('child_process');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const store = require('./graph-store');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* CGC (CodeGraphContext) MCP provider.
|
|
8
|
+
* Communicates with CGC via JSON-RPC over stdio (MCP protocol).
|
|
9
|
+
* Falls back gracefully when CGC is unavailable.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
let healthCache = null;
|
|
13
|
+
let cgcProcess = null;
|
|
14
|
+
let requestId = 0;
|
|
15
|
+
|
|
16
|
+
// --- CGC Process Management ---
|
|
17
|
+
|
|
18
|
+
function findCgcCommand() {
|
|
19
|
+
// Check if 'cgc' is available on PATH
|
|
20
|
+
try {
|
|
21
|
+
execFileSync('cgc', ['--version'], {
|
|
22
|
+
encoding: 'utf8',
|
|
23
|
+
timeout: 3000,
|
|
24
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
25
|
+
});
|
|
26
|
+
return 'cgc';
|
|
27
|
+
} catch { /* not found */ }
|
|
28
|
+
|
|
29
|
+
// Check common Python locations
|
|
30
|
+
const paths = [
|
|
31
|
+
'codegraphcontext',
|
|
32
|
+
'python -m codegraphcontext'
|
|
33
|
+
];
|
|
34
|
+
for (const cmd of paths) {
|
|
35
|
+
try {
|
|
36
|
+
const [bin, ...args] = cmd.split(' ');
|
|
37
|
+
execFileSync(bin, [...args, '--version'], {
|
|
38
|
+
encoding: 'utf8',
|
|
39
|
+
timeout: 3000,
|
|
40
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
41
|
+
});
|
|
42
|
+
return cmd;
|
|
43
|
+
} catch { /* not found */ }
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function startCgcServer() {
|
|
49
|
+
const cmd = findCgcCommand();
|
|
50
|
+
if (!cmd) return null;
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const [bin, ...baseArgs] = cmd.split(' ');
|
|
54
|
+
const args = [...baseArgs, 'mcp', 'start'];
|
|
55
|
+
const proc = spawn(bin, args, {
|
|
56
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
57
|
+
env: { ...process.env }
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
proc.on('error', () => { cgcProcess = null; });
|
|
61
|
+
proc.on('exit', () => { cgcProcess = null; });
|
|
62
|
+
|
|
63
|
+
return proc;
|
|
64
|
+
} catch { return null; }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function stopCgcServer() {
|
|
68
|
+
if (cgcProcess) {
|
|
69
|
+
try { cgcProcess.kill(); } catch { /* ignore */ }
|
|
70
|
+
cgcProcess = null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// --- JSON-RPC over stdio ---
|
|
75
|
+
|
|
76
|
+
function sendRequest(proc, method, params) {
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
const id = ++requestId;
|
|
79
|
+
const request = JSON.stringify({
|
|
80
|
+
jsonrpc: '2.0',
|
|
81
|
+
id,
|
|
82
|
+
method,
|
|
83
|
+
params: params || {}
|
|
84
|
+
}) + '\n';
|
|
85
|
+
|
|
86
|
+
let responseData = '';
|
|
87
|
+
const timeout = setTimeout(() => {
|
|
88
|
+
proc.stdout.removeListener('data', onData);
|
|
89
|
+
reject(new Error('CGC request timeout'));
|
|
90
|
+
}, 10000);
|
|
91
|
+
|
|
92
|
+
function onData(chunk) {
|
|
93
|
+
responseData += chunk.toString();
|
|
94
|
+
const lines = responseData.split('\n');
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
if (!line.trim()) continue;
|
|
97
|
+
try {
|
|
98
|
+
const resp = JSON.parse(line.trim());
|
|
99
|
+
if (resp.id === id) {
|
|
100
|
+
clearTimeout(timeout);
|
|
101
|
+
proc.stdout.removeListener('data', onData);
|
|
102
|
+
if (resp.error) {
|
|
103
|
+
reject(new Error(resp.error.message));
|
|
104
|
+
} else {
|
|
105
|
+
resolve(resp.result);
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
} catch { /* partial line, keep reading */ }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
proc.stdout.on('data', onData);
|
|
114
|
+
proc.stdin.write(request);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function sendRequestSync(proc, method, params, timeoutMs) {
|
|
119
|
+
// Synchronous wrapper using execFileSync workaround
|
|
120
|
+
// For the sync API that GSD-T commands expect,
|
|
121
|
+
// we use a helper script pattern
|
|
122
|
+
const ms = timeoutMs || 10000;
|
|
123
|
+
try {
|
|
124
|
+
const script = `
|
|
125
|
+
const net = require('net');
|
|
126
|
+
const req = ${JSON.stringify({
|
|
127
|
+
jsonrpc: '2.0',
|
|
128
|
+
id: 1,
|
|
129
|
+
method,
|
|
130
|
+
params: params || {}
|
|
131
|
+
})};
|
|
132
|
+
process.stdin.resume();
|
|
133
|
+
process.stdout.write(JSON.stringify(req) + '\\n');
|
|
134
|
+
`;
|
|
135
|
+
// This won't work for stdio — we need the async approach
|
|
136
|
+
// Fall back to spawning a one-shot process
|
|
137
|
+
return sendToolCallSync(method, params, ms);
|
|
138
|
+
} catch { return null; }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function sendToolCallSync(toolName, args, timeoutMs) {
|
|
142
|
+
const cmd = findCgcCommand();
|
|
143
|
+
if (!cmd) return null;
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const [bin, ...baseArgs] = cmd.split(' ');
|
|
147
|
+
const request = JSON.stringify({
|
|
148
|
+
jsonrpc: '2.0',
|
|
149
|
+
id: 1,
|
|
150
|
+
method: 'tools/call',
|
|
151
|
+
params: { name: toolName, arguments: args || {} }
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Spawn CGC, send request, read response
|
|
155
|
+
const result = execFileSync(bin, [...baseArgs, 'mcp', 'start'], {
|
|
156
|
+
input: JSON.stringify({
|
|
157
|
+
jsonrpc: '2.0', id: 0,
|
|
158
|
+
method: 'initialize', params: {}
|
|
159
|
+
}) + '\n' +
|
|
160
|
+
JSON.stringify({
|
|
161
|
+
jsonrpc: '2.0', id: 1,
|
|
162
|
+
method: 'notifications/initialized', params: {}
|
|
163
|
+
}) + '\n' + request + '\n',
|
|
164
|
+
encoding: 'utf8',
|
|
165
|
+
timeout: timeoutMs || 10000,
|
|
166
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Parse response lines — find the one with id: 1
|
|
170
|
+
const lines = result.split('\n');
|
|
171
|
+
for (const line of lines) {
|
|
172
|
+
if (!line.trim()) continue;
|
|
173
|
+
try {
|
|
174
|
+
const resp = JSON.parse(line.trim());
|
|
175
|
+
if (resp.id === 1 && resp.result) {
|
|
176
|
+
// Extract text content from MCP response
|
|
177
|
+
const content = resp.result.content;
|
|
178
|
+
if (Array.isArray(content) && content[0]) {
|
|
179
|
+
return JSON.parse(content[0].text);
|
|
180
|
+
}
|
|
181
|
+
return resp.result;
|
|
182
|
+
}
|
|
183
|
+
} catch { /* skip non-JSON lines */ }
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
} catch { return null; }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// --- Health Detection ---
|
|
190
|
+
|
|
191
|
+
function checkCgcHealth() {
|
|
192
|
+
if (healthCache) return healthCache;
|
|
193
|
+
|
|
194
|
+
const cmd = findCgcCommand();
|
|
195
|
+
if (!cmd) {
|
|
196
|
+
healthCache = {
|
|
197
|
+
available: false,
|
|
198
|
+
version: null,
|
|
199
|
+
capabilities: [],
|
|
200
|
+
command: null
|
|
201
|
+
};
|
|
202
|
+
return healthCache;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// CGC binary exists — try to get version
|
|
206
|
+
let version = null;
|
|
207
|
+
try {
|
|
208
|
+
const [bin, ...args] = cmd.split(' ');
|
|
209
|
+
version = execFileSync(bin, [...args, '--version'], {
|
|
210
|
+
encoding: 'utf8',
|
|
211
|
+
timeout: 3000,
|
|
212
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
213
|
+
}).trim();
|
|
214
|
+
} catch { /* version unknown */ }
|
|
215
|
+
|
|
216
|
+
healthCache = {
|
|
217
|
+
available: true,
|
|
218
|
+
version,
|
|
219
|
+
command: cmd,
|
|
220
|
+
capabilities: [
|
|
221
|
+
'analyze_code_relationships',
|
|
222
|
+
'find_dead_code',
|
|
223
|
+
'find_code',
|
|
224
|
+
'find_most_complex_functions',
|
|
225
|
+
'calculate_cyclomatic_complexity',
|
|
226
|
+
'execute_cypher_query',
|
|
227
|
+
'add_code_to_graph',
|
|
228
|
+
'get_repository_stats',
|
|
229
|
+
'watch_directory',
|
|
230
|
+
'visualize_graph_query'
|
|
231
|
+
]
|
|
232
|
+
};
|
|
233
|
+
return healthCache;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function resetHealthCache() {
|
|
237
|
+
healthCache = null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// --- Query Translation ---
|
|
241
|
+
|
|
242
|
+
function cgcQuery(toolName, args) {
|
|
243
|
+
const health = checkCgcHealth();
|
|
244
|
+
if (!health.available) return null;
|
|
245
|
+
return sendToolCallSync(toolName, args);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// --- GSD-T Overlay Enrichment ---
|
|
249
|
+
|
|
250
|
+
function enrichWithOverlay(cgcResults, projectRoot) {
|
|
251
|
+
if (!cgcResults || !Array.isArray(cgcResults)) return [];
|
|
252
|
+
|
|
253
|
+
const contracts = store.readContracts(projectRoot);
|
|
254
|
+
const requirements = store.readRequirements(projectRoot);
|
|
255
|
+
const tests = store.readTests(projectRoot);
|
|
256
|
+
const surfaces = store.readSurfaces(projectRoot);
|
|
257
|
+
|
|
258
|
+
return cgcResults.map(entity => {
|
|
259
|
+
const id = entity.id || `${entity.file}:${entity.line}:${entity.name}`;
|
|
260
|
+
const contractMap = contracts.mappings.find(
|
|
261
|
+
m => m.entity === id
|
|
262
|
+
);
|
|
263
|
+
const reqMap = requirements.mappings.find(
|
|
264
|
+
m => m.entity === id
|
|
265
|
+
);
|
|
266
|
+
const testMaps = tests.mappings.filter(
|
|
267
|
+
m => m.entity === id
|
|
268
|
+
);
|
|
269
|
+
const surfaceMap = surfaces.mappings.find(
|
|
270
|
+
m => m.entity === id
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
...entity,
|
|
275
|
+
id,
|
|
276
|
+
contract: contractMap ? contractMap.contract : null,
|
|
277
|
+
requirement: reqMap ? reqMap.requirement : null,
|
|
278
|
+
tests: testMaps,
|
|
279
|
+
surfaces: surfaceMap ? surfaceMap.surfaces : []
|
|
280
|
+
};
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// --- Normalize CGC Results to GSD-T Entity Shape ---
|
|
285
|
+
|
|
286
|
+
function normalizeEntity(cgcEntity) {
|
|
287
|
+
const name = cgcEntity.name || cgcEntity.function_name
|
|
288
|
+
|| cgcEntity.caller_function || cgcEntity.symbol || '';
|
|
289
|
+
const file = cgcEntity.file_path || cgcEntity.caller_file_path
|
|
290
|
+
|| cgcEntity.path || '';
|
|
291
|
+
const line = cgcEntity.line_number || cgcEntity.caller_line_number
|
|
292
|
+
|| cgcEntity.line || 0;
|
|
293
|
+
return {
|
|
294
|
+
id: `${file}:${line}:${name}`,
|
|
295
|
+
name,
|
|
296
|
+
type: cgcEntity.type || cgcEntity.kind || 'function',
|
|
297
|
+
file,
|
|
298
|
+
line,
|
|
299
|
+
domain: null,
|
|
300
|
+
exported: cgcEntity.exported !== false,
|
|
301
|
+
complexity: cgcEntity.complexity || cgcEntity.cyclomatic_complexity || null,
|
|
302
|
+
source: cgcEntity.source || null,
|
|
303
|
+
callArgs: cgcEntity.call_args || null,
|
|
304
|
+
callLine: cgcEntity.call_line_number || null
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function normalizeResults(cgcResult) {
|
|
309
|
+
if (!cgcResult) return null;
|
|
310
|
+
|
|
311
|
+
// Direct array
|
|
312
|
+
if (Array.isArray(cgcResult)) {
|
|
313
|
+
return cgcResult.map(normalizeEntity);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// CGC wraps in .results which can be object or array
|
|
317
|
+
const r = cgcResult.results;
|
|
318
|
+
if (r) {
|
|
319
|
+
// find_callers/callees: results.results[]
|
|
320
|
+
if (r.results && Array.isArray(r.results)) {
|
|
321
|
+
return r.results.map(normalizeEntity);
|
|
322
|
+
}
|
|
323
|
+
// dead_code: results.potentially_unused_functions[]
|
|
324
|
+
if (r.potentially_unused_functions) {
|
|
325
|
+
return r.potentially_unused_functions.map(normalizeEntity);
|
|
326
|
+
}
|
|
327
|
+
// find_code: results.functions_by_name[]
|
|
328
|
+
if (r.functions_by_name) {
|
|
329
|
+
return r.functions_by_name.map(normalizeEntity);
|
|
330
|
+
}
|
|
331
|
+
// find_code: results.functions_containing[]
|
|
332
|
+
if (r.functions_containing) {
|
|
333
|
+
return r.functions_containing.map(normalizeEntity);
|
|
334
|
+
}
|
|
335
|
+
// Direct array of results (complexity)
|
|
336
|
+
if (Array.isArray(r)) {
|
|
337
|
+
return r.map(normalizeEntity);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Legacy shapes
|
|
342
|
+
if (cgcResult.functions) {
|
|
343
|
+
return cgcResult.functions.map(normalizeEntity);
|
|
344
|
+
}
|
|
345
|
+
if (cgcResult.dead_code) {
|
|
346
|
+
return cgcResult.dead_code.map(normalizeEntity);
|
|
347
|
+
}
|
|
348
|
+
if (cgcResult.matches) {
|
|
349
|
+
return cgcResult.matches.map(normalizeEntity);
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// --- Provider Interface ---
|
|
355
|
+
|
|
356
|
+
const cgcProvider = {
|
|
357
|
+
name: 'cgc',
|
|
358
|
+
priority: 1,
|
|
359
|
+
_projectRoot: null,
|
|
360
|
+
setProjectRoot(root) { this._projectRoot = root; },
|
|
361
|
+
|
|
362
|
+
available() {
|
|
363
|
+
return checkCgcHealth().available;
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
query(type, params, projectRoot) {
|
|
367
|
+
const root = projectRoot || this._projectRoot;
|
|
368
|
+
|
|
369
|
+
switch (type) {
|
|
370
|
+
case 'getCallers': {
|
|
371
|
+
const result = cgcQuery('analyze_code_relationships', {
|
|
372
|
+
query_type: 'find_callers',
|
|
373
|
+
target: params.entity
|
|
374
|
+
});
|
|
375
|
+
const entities = normalizeResults(result);
|
|
376
|
+
return entities
|
|
377
|
+
? enrichWithOverlay(entities, root) : null;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
case 'getTransitiveCallers': {
|
|
381
|
+
const result = cgcQuery('analyze_code_relationships', {
|
|
382
|
+
query_type: 'find_all_callers',
|
|
383
|
+
target: params.entity
|
|
384
|
+
});
|
|
385
|
+
const entities = normalizeResults(result);
|
|
386
|
+
return entities
|
|
387
|
+
? enrichWithOverlay(entities, root) : null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
case 'getCallees': {
|
|
391
|
+
const result = cgcQuery('analyze_code_relationships', {
|
|
392
|
+
query_type: 'find_callees',
|
|
393
|
+
target: params.entity
|
|
394
|
+
});
|
|
395
|
+
const entities = normalizeResults(result);
|
|
396
|
+
return entities
|
|
397
|
+
? enrichWithOverlay(entities, root) : null;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
case 'getTransitiveCallees': {
|
|
401
|
+
const result = cgcQuery('analyze_code_relationships', {
|
|
402
|
+
query_type: 'find_all_callees',
|
|
403
|
+
target: params.entity
|
|
404
|
+
});
|
|
405
|
+
const entities = normalizeResults(result);
|
|
406
|
+
return entities
|
|
407
|
+
? enrichWithOverlay(entities, root) : null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
case 'findDeadCode': {
|
|
411
|
+
const result = cgcQuery('find_dead_code', {
|
|
412
|
+
exclude_decorated_with: params.exclude || []
|
|
413
|
+
});
|
|
414
|
+
const entities = normalizeResults(result);
|
|
415
|
+
return entities
|
|
416
|
+
? enrichWithOverlay(entities, root) : null;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
case 'findComplexFunctions': {
|
|
420
|
+
const result = cgcQuery('find_most_complex_functions', {
|
|
421
|
+
top_n: params.limit || 20
|
|
422
|
+
});
|
|
423
|
+
return normalizeResults(result);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
case 'getComplexity': {
|
|
427
|
+
const result = cgcQuery('calculate_cyclomatic_complexity', {
|
|
428
|
+
function_name: params.name,
|
|
429
|
+
file_path: params.file || undefined
|
|
430
|
+
});
|
|
431
|
+
return result;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
case 'findDuplicates': {
|
|
435
|
+
const result = cgcQuery('find_code', {
|
|
436
|
+
query: params.target || '*',
|
|
437
|
+
fuzzy_search: true,
|
|
438
|
+
edit_distance: 2
|
|
439
|
+
});
|
|
440
|
+
return normalizeResults(result);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
case 'findCircularDeps': {
|
|
444
|
+
const result = cgcQuery('execute_cypher_query', {
|
|
445
|
+
cypher_query: `MATCH path = (a:Function)-[:CALLS*2..${params.maxDepth || 5}]->(a) RETURN a.name AS name, a.path AS file, a.line_number AS line LIMIT 20`
|
|
446
|
+
});
|
|
447
|
+
if (!result) return null;
|
|
448
|
+
return Array.isArray(result)
|
|
449
|
+
? result.map(normalizeEntity)
|
|
450
|
+
: [];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
case 'getEntity': {
|
|
454
|
+
const result = cgcQuery('find_code', {
|
|
455
|
+
query: params.name,
|
|
456
|
+
fuzzy_search: false
|
|
457
|
+
});
|
|
458
|
+
const entities = normalizeResults(result);
|
|
459
|
+
return entities && entities.length > 0
|
|
460
|
+
? enrichWithOverlay([entities[0]], root)[0]
|
|
461
|
+
: null;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
case 'getCallChain': {
|
|
465
|
+
const result = cgcQuery('analyze_code_relationships', {
|
|
466
|
+
query_type: 'call_chain',
|
|
467
|
+
target: params.from,
|
|
468
|
+
context: params.to
|
|
469
|
+
});
|
|
470
|
+
return normalizeResults(result);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
case 'getModuleDeps': {
|
|
474
|
+
const result = cgcQuery('analyze_code_relationships', {
|
|
475
|
+
query_type: 'module_deps',
|
|
476
|
+
target: params.module
|
|
477
|
+
});
|
|
478
|
+
return normalizeResults(result);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
case 'getClassHierarchy': {
|
|
482
|
+
const result = cgcQuery('analyze_code_relationships', {
|
|
483
|
+
query_type: 'class_hierarchy',
|
|
484
|
+
target: params.className
|
|
485
|
+
});
|
|
486
|
+
return normalizeResults(result);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
case 'getStats': {
|
|
490
|
+
return cgcQuery('get_repository_stats', {});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
case 'cypher': {
|
|
494
|
+
return cgcQuery('execute_cypher_query', {
|
|
495
|
+
cypher_query: params.query
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
default:
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
module.exports = {
|
|
506
|
+
cgcProvider, checkCgcHealth, resetHealthCache,
|
|
507
|
+
cgcQuery, enrichWithOverlay, normalizeEntity,
|
|
508
|
+
normalizeResults, findCgcCommand, sendToolCallSync,
|
|
509
|
+
stopCgcServer
|
|
510
|
+
};
|