@tekyzinc/gsd-t 2.51.10 → 2.53.11

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 (100) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +379 -373
  3. package/bin/component-registry.js +250 -0
  4. package/bin/graph-cgc.js +510 -510
  5. package/bin/graph-indexer.js +147 -147
  6. package/bin/graph-overlay.js +195 -195
  7. package/bin/graph-parsers.js +327 -327
  8. package/bin/graph-query.js +453 -452
  9. package/bin/graph-store.js +154 -154
  10. package/bin/qa-calibrator.js +194 -0
  11. package/bin/scan-data-collector.js +153 -153
  12. package/bin/scan-diagrams-generators.js +187 -187
  13. package/bin/scan-diagrams.js +79 -79
  14. package/bin/scan-renderer.js +92 -92
  15. package/bin/scan-report-sections.js +121 -121
  16. package/bin/scan-report.js +184 -184
  17. package/bin/scan-schema-parsers.js +199 -199
  18. package/bin/scan-schema.js +103 -103
  19. package/bin/token-budget.js +246 -0
  20. package/commands/Claude-md.md +10 -10
  21. package/commands/branch.md +15 -15
  22. package/commands/checkin.md +45 -45
  23. package/commands/global-change.md +209 -209
  24. package/commands/gsd-t-audit.md +199 -0
  25. package/commands/gsd-t-backlog-add.md +94 -94
  26. package/commands/gsd-t-backlog-edit.md +111 -111
  27. package/commands/gsd-t-backlog-list.md +63 -63
  28. package/commands/gsd-t-backlog-move.md +94 -94
  29. package/commands/gsd-t-backlog-promote.md +123 -123
  30. package/commands/gsd-t-backlog-remove.md +86 -86
  31. package/commands/gsd-t-backlog-settings.md +158 -158
  32. package/commands/gsd-t-complete-milestone.md +528 -515
  33. package/commands/gsd-t-debug.md +506 -482
  34. package/commands/gsd-t-discuss.md +174 -174
  35. package/commands/gsd-t-execute.md +758 -715
  36. package/commands/gsd-t-feature.md +276 -276
  37. package/commands/gsd-t-health.md +142 -142
  38. package/commands/gsd-t-help.md +465 -457
  39. package/commands/gsd-t-impact.md +302 -302
  40. package/commands/gsd-t-init-scan-setup.md +1 -5
  41. package/commands/gsd-t-init.md +314 -280
  42. package/commands/gsd-t-integrate.md +365 -333
  43. package/commands/gsd-t-milestone.md +87 -87
  44. package/commands/gsd-t-partition.md +442 -361
  45. package/commands/gsd-t-pause.md +82 -82
  46. package/commands/gsd-t-plan.md +345 -344
  47. package/commands/gsd-t-populate.md +111 -111
  48. package/commands/gsd-t-prd.md +326 -326
  49. package/commands/gsd-t-project.md +211 -211
  50. package/commands/gsd-t-promote-debt.md +123 -123
  51. package/commands/gsd-t-prompt.md +137 -137
  52. package/commands/gsd-t-qa.md +266 -266
  53. package/commands/gsd-t-quick.md +357 -315
  54. package/commands/gsd-t-reflect.md +134 -134
  55. package/commands/gsd-t-resume.md +72 -72
  56. package/commands/gsd-t-scan.md +615 -615
  57. package/commands/gsd-t-setup.md +76 -0
  58. package/commands/gsd-t-status.md +192 -166
  59. package/commands/gsd-t-test-sync.md +381 -381
  60. package/commands/gsd-t-triage-and-merge.md +171 -171
  61. package/commands/gsd-t-verify.md +382 -382
  62. package/commands/gsd-t-visualize.md +118 -118
  63. package/commands/gsd-t-wave.md +401 -378
  64. package/docs/GSD-T-README.md +425 -424
  65. package/docs/architecture.md +385 -369
  66. package/docs/harness-design-analysis.md +371 -0
  67. package/docs/infrastructure.md +205 -205
  68. package/docs/prd-graph-engine.md +398 -398
  69. package/docs/prd-gsd2-hybrid.md +559 -559
  70. package/docs/prd-harness-evolution.md +583 -0
  71. package/docs/requirements.md +14 -0
  72. package/docs/workflows.md +226 -226
  73. package/examples/.gsd-t/domains/example-domain/scope.md +13 -13
  74. package/package.json +40 -40
  75. package/scripts/gsd-t-auto-route.js +39 -39
  76. package/scripts/gsd-t-dashboard-mockup.html +1143 -1143
  77. package/scripts/gsd-t-dashboard-server.js +171 -171
  78. package/scripts/gsd-t-dashboard.html +262 -262
  79. package/scripts/gsd-t-event-writer.js +128 -128
  80. package/scripts/gsd-t-statusline.js +94 -94
  81. package/scripts/gsd-t-tools.js +175 -175
  82. package/templates/CLAUDE-global.md +638 -634
  83. package/templates/CLAUDE-project.md +24 -0
  84. package/templates/backlog-settings.md +18 -18
  85. package/templates/backlog.md +1 -1
  86. package/templates/progress.md +40 -40
  87. package/templates/shared-services-contract.md +60 -60
  88. package/templates/stacks/desktop.ini +2 -2
  89. package/bin/desktop.ini +0 -2
  90. package/commands/desktop.ini +0 -2
  91. package/docs/ci-examples/desktop.ini +0 -2
  92. package/docs/desktop.ini +0 -2
  93. package/examples/.gsd-t/contracts/desktop.ini +0 -2
  94. package/examples/.gsd-t/desktop.ini +0 -2
  95. package/examples/.gsd-t/domains/desktop.ini +0 -2
  96. package/examples/.gsd-t/domains/example-domain/desktop.ini +0 -2
  97. package/examples/desktop.ini +0 -2
  98. package/examples/rules/desktop.ini +0 -2
  99. package/scripts/desktop.ini +0 -2
  100. 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
+ };