@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-query.js
CHANGED
|
@@ -1,452 +1,453 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const store = require('./graph-store');
|
|
3
|
-
const { indexProject } = require('./graph-indexer');
|
|
4
|
-
const { cgcProvider, cgcQuery } = require('./graph-cgc');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Graph Abstraction Layer — unified query interface.
|
|
8
|
-
* Routes queries to best available provider: CGC → native → grep.
|
|
9
|
-
* Commands call query() and never interact with providers directly.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const providers = [];
|
|
13
|
-
let sessionProvider = null; // cached per session
|
|
14
|
-
let _lastFreshnessCheck = 0; // timestamp of last staleness check
|
|
15
|
-
|
|
16
|
-
function registerProvider(provider) {
|
|
17
|
-
providers.push(provider);
|
|
18
|
-
providers.sort((a, b) => a.priority - b.priority);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function getProviders() {
|
|
22
|
-
return [...providers];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function selectProvider() {
|
|
26
|
-
if (sessionProvider) return sessionProvider;
|
|
27
|
-
for (const p of providers) {
|
|
28
|
-
if (p.available()) {
|
|
29
|
-
sessionProvider = p;
|
|
30
|
-
return p;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function resetSession() {
|
|
37
|
-
sessionProvider = null;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
case '
|
|
102
|
-
|
|
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
|
-
names[e.name].
|
|
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
|
-
graph[edge.source]
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
visited.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
callerEnt.domain
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
//
|
|
370
|
-
|
|
371
|
-
const
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
'
|
|
394
|
-
'
|
|
395
|
-
'
|
|
396
|
-
'
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if (p.
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
registerProvider(
|
|
446
|
-
registerProvider(
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
const store = require('./graph-store');
|
|
3
|
+
const { indexProject } = require('./graph-indexer');
|
|
4
|
+
const { cgcProvider, cgcQuery } = require('./graph-cgc');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Graph Abstraction Layer — unified query interface.
|
|
8
|
+
* Routes queries to best available provider: CGC → native → grep.
|
|
9
|
+
* Commands call query() and never interact with providers directly.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const providers = [];
|
|
13
|
+
let sessionProvider = null; // cached per session
|
|
14
|
+
let _lastFreshnessCheck = 0; // timestamp of last staleness check
|
|
15
|
+
|
|
16
|
+
function registerProvider(provider) {
|
|
17
|
+
providers.push(provider);
|
|
18
|
+
providers.sort((a, b) => a.priority - b.priority);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getProviders() {
|
|
22
|
+
return [...providers];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function selectProvider() {
|
|
26
|
+
if (sessionProvider) return sessionProvider;
|
|
27
|
+
for (const p of providers) {
|
|
28
|
+
if (p.available()) {
|
|
29
|
+
sessionProvider = p;
|
|
30
|
+
return p;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resetSession() {
|
|
37
|
+
sessionProvider = null;
|
|
38
|
+
_lastFreshnessCheck = 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// --- Native provider ---
|
|
42
|
+
|
|
43
|
+
function nativeAvailable(projectRoot) {
|
|
44
|
+
const meta = store.readMeta(projectRoot);
|
|
45
|
+
return meta !== null && meta.entityCount > 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function nativeQuery(type, params, projectRoot) {
|
|
49
|
+
const idx = store.readIndex(projectRoot);
|
|
50
|
+
const calls = store.readCalls(projectRoot);
|
|
51
|
+
const imps = store.readImports(projectRoot);
|
|
52
|
+
const contracts = store.readContracts(projectRoot);
|
|
53
|
+
const requirements = store.readRequirements(projectRoot);
|
|
54
|
+
const tests = store.readTests(projectRoot);
|
|
55
|
+
const surfaces = store.readSurfaces(projectRoot);
|
|
56
|
+
|
|
57
|
+
switch (type) {
|
|
58
|
+
case 'getEntity': {
|
|
59
|
+
return idx.entities.find(e =>
|
|
60
|
+
e.name === params.name &&
|
|
61
|
+
(!params.file || e.file === params.file)
|
|
62
|
+
) || null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
case 'getEntities': {
|
|
66
|
+
return idx.entities.filter(e => e.file === params.file);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
case 'getEntitiesByDomain': {
|
|
70
|
+
return idx.entities.filter(
|
|
71
|
+
e => e.domain === params.domain
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
case 'getCallers': {
|
|
76
|
+
const edges = calls.edges.filter(
|
|
77
|
+
e => e.callee === params.entity ||
|
|
78
|
+
e.callee.endsWith(':' + params.entity)
|
|
79
|
+
);
|
|
80
|
+
return edges.map(e => {
|
|
81
|
+
const caller = idx.entities.find(
|
|
82
|
+
ent => ent.id === e.caller
|
|
83
|
+
);
|
|
84
|
+
return caller || { id: e.caller, name: e.caller };
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
case 'getCallees': {
|
|
89
|
+
const edges = calls.edges.filter(
|
|
90
|
+
e => e.caller === params.entity ||
|
|
91
|
+
e.caller.endsWith(':' + params.entity)
|
|
92
|
+
);
|
|
93
|
+
return edges.map(e => {
|
|
94
|
+
const callee = idx.entities.find(
|
|
95
|
+
ent => ent.id === e.callee
|
|
96
|
+
);
|
|
97
|
+
return callee || { id: e.callee, name: e.callee };
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
case 'getTransitiveCallers':
|
|
102
|
+
case 'getTransitiveCallees': {
|
|
103
|
+
// Native: 1-level only (CGC enhances to N-level)
|
|
104
|
+
const direction = type === 'getTransitiveCallers'
|
|
105
|
+
? 'getCallers' : 'getCallees';
|
|
106
|
+
return nativeQuery(direction, params, projectRoot);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
case 'getImports': {
|
|
110
|
+
return imps.edges.filter(e => e.source === params.file);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
case 'getImporters': {
|
|
114
|
+
return imps.edges.filter(e => e.target === params.file ||
|
|
115
|
+
e.target.endsWith('/' + params.file));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
case 'getDomainOwner': {
|
|
119
|
+
const entity = typeof params.entity === 'string'
|
|
120
|
+
? idx.entities.find(e => e.id === params.entity ||
|
|
121
|
+
e.name === params.entity)
|
|
122
|
+
: params.entity;
|
|
123
|
+
return entity ? entity.domain : null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
case 'getContractFor': {
|
|
127
|
+
const m = contracts.mappings.find(
|
|
128
|
+
c => c.entity === params.entity ||
|
|
129
|
+
c.entity.endsWith(':' + params.entity)
|
|
130
|
+
);
|
|
131
|
+
return m ? m.contract : null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
case 'getRequirementFor': {
|
|
135
|
+
const m = requirements.mappings.find(
|
|
136
|
+
r => r.entity === params.entity ||
|
|
137
|
+
r.entity.endsWith(':' + params.entity)
|
|
138
|
+
);
|
|
139
|
+
return m ? m.requirement : null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
case 'getTestsFor': {
|
|
143
|
+
return tests.mappings.filter(
|
|
144
|
+
t => t.entity === params.entity ||
|
|
145
|
+
t.entity.endsWith(':' + params.entity)
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
case 'getDebtFor': {
|
|
150
|
+
return []; // debt mapping is basic
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
case 'getSurfaceConsumers': {
|
|
154
|
+
const m = surfaces.mappings.find(
|
|
155
|
+
s => s.entity === params.entity ||
|
|
156
|
+
s.entity.endsWith(':' + params.entity)
|
|
157
|
+
);
|
|
158
|
+
return m ? m.surfaces : [];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
case 'findDuplicates': {
|
|
162
|
+
// Native: name-based only (CGC adds AST comparison)
|
|
163
|
+
const names = {};
|
|
164
|
+
for (const e of idx.entities) {
|
|
165
|
+
if (!names[e.name]) names[e.name] = [];
|
|
166
|
+
names[e.name].push(e);
|
|
167
|
+
}
|
|
168
|
+
const dupes = [];
|
|
169
|
+
for (const [name, ents] of Object.entries(names)) {
|
|
170
|
+
if (ents.length > 1) {
|
|
171
|
+
for (let i = 0; i < ents.length - 1; i++) {
|
|
172
|
+
dupes.push({
|
|
173
|
+
entityA: ents[i],
|
|
174
|
+
entityB: ents[i + 1],
|
|
175
|
+
similarity: 1.0
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return dupes;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
case 'findDeadCode': {
|
|
184
|
+
// Entities that are not called and not exported
|
|
185
|
+
const calledIds = new Set(
|
|
186
|
+
calls.edges.map(e => e.callee)
|
|
187
|
+
);
|
|
188
|
+
return idx.entities.filter(e =>
|
|
189
|
+
!e.exported && !calledIds.has(e.id)
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
case 'findCircularDeps': {
|
|
194
|
+
// Native: import-level cycle detection
|
|
195
|
+
const graph = {};
|
|
196
|
+
for (const edge of imps.edges) {
|
|
197
|
+
if (!graph[edge.source]) graph[edge.source] = [];
|
|
198
|
+
graph[edge.source].push(edge.target);
|
|
199
|
+
}
|
|
200
|
+
const cycles = [];
|
|
201
|
+
const visited = new Set();
|
|
202
|
+
const stack = new Set();
|
|
203
|
+
|
|
204
|
+
function dfs(node, pathArr) {
|
|
205
|
+
if (stack.has(node)) {
|
|
206
|
+
const cycleStart = pathArr.indexOf(node);
|
|
207
|
+
if (cycleStart >= 0) {
|
|
208
|
+
cycles.push({
|
|
209
|
+
path: pathArr.slice(cycleStart),
|
|
210
|
+
entities: []
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (visited.has(node)) return;
|
|
216
|
+
visited.add(node);
|
|
217
|
+
stack.add(node);
|
|
218
|
+
pathArr.push(node);
|
|
219
|
+
for (const neighbor of (graph[node] || [])) {
|
|
220
|
+
dfs(neighbor, [...pathArr]);
|
|
221
|
+
}
|
|
222
|
+
stack.delete(node);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
for (const node of Object.keys(graph)) {
|
|
226
|
+
dfs(node, []);
|
|
227
|
+
}
|
|
228
|
+
return cycles;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
case 'getDomainBoundaryViolations': {
|
|
232
|
+
const violations = [];
|
|
233
|
+
for (const edge of calls.edges) {
|
|
234
|
+
const callerEnt = idx.entities.find(
|
|
235
|
+
e => e.id === edge.caller
|
|
236
|
+
);
|
|
237
|
+
const calleeEnt = idx.entities.find(
|
|
238
|
+
e => e.id === edge.callee
|
|
239
|
+
);
|
|
240
|
+
if (callerEnt && calleeEnt &&
|
|
241
|
+
callerEnt.domain && calleeEnt.domain &&
|
|
242
|
+
callerEnt.domain !== calleeEnt.domain) {
|
|
243
|
+
violations.push({
|
|
244
|
+
entity: calleeEnt,
|
|
245
|
+
ownerDomain: calleeEnt.domain,
|
|
246
|
+
accessedBy: callerEnt,
|
|
247
|
+
accessorDomain: callerEnt.domain
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return violations;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
case 'getProvider': {
|
|
255
|
+
const p = selectProvider();
|
|
256
|
+
return p ? p.name : 'grep';
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
case 'getIndexStatus': {
|
|
260
|
+
const meta = store.readMeta(projectRoot);
|
|
261
|
+
return {
|
|
262
|
+
provider: meta ? meta.provider : 'none',
|
|
263
|
+
indexed: meta !== null,
|
|
264
|
+
entityCount: meta ? meta.entityCount : 0,
|
|
265
|
+
lastIndexed: meta ? meta.lastIndexed : null,
|
|
266
|
+
stale: false,
|
|
267
|
+
stalePaths: []
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
case 'reindex': {
|
|
272
|
+
return indexProject(projectRoot, {
|
|
273
|
+
force: params.force || false
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
default:
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const nativeProvider = {
|
|
283
|
+
name: 'native',
|
|
284
|
+
priority: 2,
|
|
285
|
+
_projectRoot: null,
|
|
286
|
+
setProjectRoot(root) { this._projectRoot = root; },
|
|
287
|
+
available() {
|
|
288
|
+
return this._projectRoot
|
|
289
|
+
? nativeAvailable(this._projectRoot) : false;
|
|
290
|
+
},
|
|
291
|
+
query(type, params) {
|
|
292
|
+
return nativeQuery(type, params, this._projectRoot);
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
// --- Grep fallback provider ---
|
|
297
|
+
|
|
298
|
+
const fs = require('fs');
|
|
299
|
+
const path = require('path');
|
|
300
|
+
const { execFileSync } = require('child_process');
|
|
301
|
+
|
|
302
|
+
const SAFE_ENTITY_RE = /^[\w.\-/\\:]+$/;
|
|
303
|
+
|
|
304
|
+
function grepQuery(type, params, projectRoot) {
|
|
305
|
+
switch (type) {
|
|
306
|
+
case 'getCallers': {
|
|
307
|
+
const name = params.entity;
|
|
308
|
+
if (!name || !SAFE_ENTITY_RE.test(name)) return [];
|
|
309
|
+
try {
|
|
310
|
+
const out = execFileSync('grep', ['-rn', name + '(', '--include=*.js', '--include=*.ts', '--include=*.py', projectRoot], { encoding: 'utf8', timeout: 5000 });
|
|
311
|
+
return out.split('\n').filter(Boolean).map(line => {
|
|
312
|
+
const [file] = line.split(':');
|
|
313
|
+
return {
|
|
314
|
+
id: file,
|
|
315
|
+
name: 'grep_result',
|
|
316
|
+
file: path.relative(projectRoot, file)
|
|
317
|
+
};
|
|
318
|
+
});
|
|
319
|
+
} catch { return []; }
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
case 'getImporters': {
|
|
323
|
+
const name = params.file || params.entity;
|
|
324
|
+
if (!name || !SAFE_ENTITY_RE.test(name)) return [];
|
|
325
|
+
try {
|
|
326
|
+
const out = execFileSync('grep', ['-rn', '-e', 'import.*' + name, '-e', 'require.*' + name, '--include=*.js', '--include=*.ts', projectRoot], { encoding: 'utf8', timeout: 5000 });
|
|
327
|
+
return out.split('\n').filter(Boolean).map(line => ({
|
|
328
|
+
source: line.split(':')[0],
|
|
329
|
+
target: name,
|
|
330
|
+
names: [],
|
|
331
|
+
line: parseInt(line.split(':')[1]) || 0
|
|
332
|
+
}));
|
|
333
|
+
} catch { return []; }
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
case 'getProvider':
|
|
337
|
+
return 'grep';
|
|
338
|
+
|
|
339
|
+
case 'getIndexStatus':
|
|
340
|
+
return {
|
|
341
|
+
provider: 'grep',
|
|
342
|
+
indexed: false,
|
|
343
|
+
entityCount: 0,
|
|
344
|
+
lastIndexed: null,
|
|
345
|
+
stale: true,
|
|
346
|
+
stalePaths: []
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
default:
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const grepProvider = {
|
|
355
|
+
name: 'grep',
|
|
356
|
+
priority: 3,
|
|
357
|
+
_projectRoot: null,
|
|
358
|
+
setProjectRoot(root) { this._projectRoot = root; },
|
|
359
|
+
available() { return true; }, // always available
|
|
360
|
+
query(type, params) {
|
|
361
|
+
return grepQuery(type, params, this._projectRoot);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// --- CGC Sync (Phase 2: keep Neo4j in sync at command boundary) ---
|
|
366
|
+
|
|
367
|
+
function _syncCgc(projectRoot) {
|
|
368
|
+
if (!cgcProvider.available()) return;
|
|
369
|
+
// Use CLI instead of MCP tool call — add_code_to_graph MCP is broken
|
|
370
|
+
// on Windows in CGC 0.3.1 (directory param arrives as None)
|
|
371
|
+
const { execFileSync } = require('child_process');
|
|
372
|
+
const cgcEnv = { ...process.env, PYTHONIOENCODING: 'utf-8' };
|
|
373
|
+
const cgcOpts = { timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'], env: cgcEnv };
|
|
374
|
+
|
|
375
|
+
// Attempt 1: normal sync
|
|
376
|
+
try {
|
|
377
|
+
execFileSync('cgc', ['index', projectRoot], cgcOpts);
|
|
378
|
+
return;
|
|
379
|
+
} catch (err1) {
|
|
380
|
+
const msg1 = err1.stderr ? err1.stderr.toString() : err1.message;
|
|
381
|
+
// Attempt 2: force re-index to recover from corrupt state
|
|
382
|
+
try {
|
|
383
|
+
execFileSync('cgc', ['index', projectRoot, '--force'], cgcOpts);
|
|
384
|
+
process.stderr.write(
|
|
385
|
+
'[GSD-T] CGC sync recovered via force re-index for ' +
|
|
386
|
+
projectRoot + '\n'
|
|
387
|
+
);
|
|
388
|
+
return;
|
|
389
|
+
} catch (err2) {
|
|
390
|
+
const msg2 = err2.stderr ? err2.stderr.toString() : err2.message;
|
|
391
|
+
// Both attempts failed — warn the user clearly
|
|
392
|
+
process.stderr.write(
|
|
393
|
+
'[GSD-T] ⚠ CGC sync FAILED for ' + projectRoot + '\n' +
|
|
394
|
+
' Error: ' + (msg2 || msg1).split('\n')[0] + '\n' +
|
|
395
|
+
' Impact: Neo4j graph is stale — deep call chain analysis ' +
|
|
396
|
+
'may return outdated results\n' +
|
|
397
|
+
' Fix: run "cgc index ' + projectRoot + ' --force" manually\n'
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// --- Main query function ---
|
|
404
|
+
|
|
405
|
+
function query(type, params, projectRoot) {
|
|
406
|
+
// Set project root on providers
|
|
407
|
+
nativeProvider.setProjectRoot(projectRoot);
|
|
408
|
+
grepProvider.setProjectRoot(projectRoot);
|
|
409
|
+
|
|
410
|
+
// Auto-trigger reindex if stale (command-boundary freshness check)
|
|
411
|
+
if (type !== 'reindex' && type !== 'getIndexStatus' &&
|
|
412
|
+
type !== 'getProvider') {
|
|
413
|
+
const now = Date.now();
|
|
414
|
+
if (now - _lastFreshnessCheck > 500) {
|
|
415
|
+
_lastFreshnessCheck = now;
|
|
416
|
+
const meta = store.readMeta(projectRoot);
|
|
417
|
+
if (!meta) {
|
|
418
|
+
// No index exists — run initial index
|
|
419
|
+
indexProject(projectRoot);
|
|
420
|
+
_syncCgc(projectRoot);
|
|
421
|
+
} else {
|
|
422
|
+
// Check staleness — reindex if any files changed
|
|
423
|
+
const result = indexProject(projectRoot);
|
|
424
|
+
if (result.filesProcessed > 0) {
|
|
425
|
+
_syncCgc(projectRoot);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Try providers in priority order
|
|
432
|
+
for (const p of providers) {
|
|
433
|
+
if (!p.available()) continue;
|
|
434
|
+
if (p._projectRoot !== undefined) {
|
|
435
|
+
p.setProjectRoot(projectRoot);
|
|
436
|
+
}
|
|
437
|
+
const result = p.query(type, params, projectRoot);
|
|
438
|
+
if (result !== null && result !== undefined) return result;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Register default providers
|
|
445
|
+
registerProvider(cgcProvider);
|
|
446
|
+
registerProvider(nativeProvider);
|
|
447
|
+
registerProvider(grepProvider);
|
|
448
|
+
|
|
449
|
+
module.exports = {
|
|
450
|
+
query, registerProvider, getProviders,
|
|
451
|
+
selectProvider, resetSession,
|
|
452
|
+
nativeProvider, grepProvider
|
|
453
|
+
};
|