@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
@@ -1,327 +1,327 @@
1
- 'use strict';
2
-
3
- /**
4
- * Language-specific parsers for extracting code entities.
5
- * Zero external dependencies — regex-based only.
6
- */
7
-
8
- // --- JS/TS Parser ---
9
-
10
- const JS_FUNC_PATTERNS = [
11
- // function declarations: function name(, async function name(
12
- /^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/gm,
13
- // arrow/const: const name = (, const name = function(, const name = async (
14
- /^\s*(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:function\s*)?\(/gm,
15
- // arrow with =>: const name = (...) =>
16
- /^\s*(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/gm,
17
- ];
18
-
19
- const JS_CLASS_PATTERN = /^\s*(?:export\s+)?(?:default\s+)?class\s+(\w+)/gm;
20
-
21
- const JS_METHOD_PATTERN = /^\s+(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/gm;
22
-
23
- const JS_IMPORT_PATTERNS = [
24
- // import { x, y } from 'module'
25
- /^\s*import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/gm,
26
- // import x from 'module'
27
- /^\s*import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/gm,
28
- // import * as x from 'module'
29
- /^\s*import\s+\*\s+as\s+(\w+)\s+from\s+['"]([^'"]+)['"]/gm,
30
- // const x = require('module')
31
- /^\s*(?:const|let|var)\s+(?:\{([^}]+)\}|(\w+))\s*=\s*require\(\s*['"]([^'"]+)['"]\s*\)/gm,
32
- ];
33
-
34
- const JS_EXPORT_PATTERNS = [
35
- // module.exports = { ... } or module.exports = name
36
- /^\s*module\.exports\s*=\s*/gm,
37
- // export default
38
- /^\s*export\s+default\s+/gm,
39
- // export { x, y }
40
- /^\s*export\s+\{([^}]+)\}/gm,
41
- ];
42
-
43
- function parseJavaScript(content, filePath) {
44
- const entities = [];
45
- const imports = [];
46
- const calls = [];
47
- const lines = content.split('\n');
48
- const exportedNames = new Set();
49
- let currentClass = null;
50
-
51
- // Collect exported names
52
- for (const line of lines) {
53
- if (/^\s*export\s+/.test(line)) {
54
- const m = line.match(/(?:function|class|const|let|var)\s+(\w+)/);
55
- if (m) exportedNames.add(m[1]);
56
- }
57
- const exportMatch = line.match(/^\s*export\s+\{([^}]+)\}/);
58
- if (exportMatch) {
59
- exportMatch[1].split(',').forEach(n => {
60
- const name = n.trim().split(/\s+as\s+/)[0].trim();
61
- if (name) exportedNames.add(name);
62
- });
63
- }
64
- if (/module\.exports/.test(line)) {
65
- const m = line.match(/module\.exports\s*=\s*\{\s*([^}]+)\}/);
66
- if (m) {
67
- m[1].split(',').forEach(n => {
68
- const name = n.trim().split(':')[0].trim();
69
- if (name) exportedNames.add(name);
70
- });
71
- }
72
- }
73
- }
74
-
75
- // Extract entities line by line
76
- for (let i = 0; i < lines.length; i++) {
77
- const line = lines[i];
78
- const lineNum = i + 1;
79
-
80
- // Class declarations
81
- const classMatch = line.match(
82
- /^\s*(?:export\s+)?(?:default\s+)?class\s+(\w+)/
83
- );
84
- if (classMatch) {
85
- currentClass = classMatch[1];
86
- entities.push({
87
- id: `${filePath}:${lineNum}:${classMatch[1]}`,
88
- name: classMatch[1],
89
- type: 'class',
90
- file: filePath,
91
- line: lineNum,
92
- domain: null,
93
- exported: exportedNames.has(classMatch[1]) ||
94
- /export/.test(line)
95
- });
96
- continue;
97
- }
98
-
99
- // Method declarations (inside class)
100
- if (currentClass) {
101
- const methodMatch = line.match(
102
- /^\s+(?:static\s+)?(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/
103
- );
104
- if (methodMatch && methodMatch[1] !== 'constructor') {
105
- entities.push({
106
- id: `${filePath}:${lineNum}:${methodMatch[1]}`,
107
- name: methodMatch[1],
108
- type: 'method',
109
- file: filePath,
110
- line: lineNum,
111
- domain: null,
112
- exported: exportedNames.has(currentClass)
113
- });
114
- continue;
115
- }
116
- // Detect end of class
117
- if (/^}/.test(line)) currentClass = null;
118
- }
119
-
120
- // Function declarations
121
- const funcMatch = line.match(
122
- /^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/
123
- );
124
- if (funcMatch) {
125
- entities.push({
126
- id: `${filePath}:${lineNum}:${funcMatch[1]}`,
127
- name: funcMatch[1],
128
- type: 'function',
129
- file: filePath,
130
- line: lineNum,
131
- domain: null,
132
- exported: exportedNames.has(funcMatch[1]) ||
133
- /export/.test(line)
134
- });
135
- continue;
136
- }
137
-
138
- // Arrow/const functions
139
- const arrowMatch = line.match(
140
- /^\s*(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:function\s*)?\(/
141
- ) || line.match(
142
- /^\s*(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/
143
- );
144
- if (arrowMatch) {
145
- entities.push({
146
- id: `${filePath}:${lineNum}:${arrowMatch[1]}`,
147
- name: arrowMatch[1],
148
- type: 'function',
149
- file: filePath,
150
- line: lineNum,
151
- domain: null,
152
- exported: exportedNames.has(arrowMatch[1]) ||
153
- /export/.test(line)
154
- });
155
- continue;
156
- }
157
-
158
- // Import statements
159
- const esImportNamed = line.match(
160
- /^\s*import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/
161
- );
162
- if (esImportNamed) {
163
- const names = esImportNamed[1].split(',').map(n =>
164
- n.trim().split(/\s+as\s+/)[0].trim()
165
- ).filter(Boolean);
166
- imports.push({
167
- source: filePath,
168
- target: esImportNamed[2],
169
- names,
170
- line: lineNum
171
- });
172
- continue;
173
- }
174
-
175
- const esImportDefault = line.match(
176
- /^\s*import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/
177
- );
178
- if (esImportDefault) {
179
- imports.push({
180
- source: filePath,
181
- target: esImportDefault[2],
182
- names: [esImportDefault[1]],
183
- line: lineNum
184
- });
185
- continue;
186
- }
187
-
188
- const esImportStar = line.match(
189
- /^\s*import\s+\*\s+as\s+(\w+)\s+from\s+['"]([^'"]+)['"]/
190
- );
191
- if (esImportStar) {
192
- imports.push({
193
- source: filePath,
194
- target: esImportStar[2],
195
- names: [esImportStar[1]],
196
- line: lineNum
197
- });
198
- continue;
199
- }
200
-
201
- const requireMatch = line.match(
202
- /^\s*(?:const|let|var)\s+(?:\{([^}]+)\}|(\w+))\s*=\s*require\(\s*['"]([^'"]+)['"]\s*\)/
203
- );
204
- if (requireMatch) {
205
- const names = requireMatch[1]
206
- ? requireMatch[1].split(',').map(n =>
207
- n.trim().split(':')[0].trim()
208
- ).filter(Boolean)
209
- : [requireMatch[2]];
210
- imports.push({
211
- source: filePath,
212
- target: requireMatch[3],
213
- names,
214
- line: lineNum
215
- });
216
- }
217
- }
218
-
219
- // Extract calls (best-effort: known entity names followed by '(')
220
- const entityNames = new Set(entities.map(e => e.name));
221
- for (let i = 0; i < lines.length; i++) {
222
- const line = lines[i];
223
- // Skip declarations, imports, comments
224
- if (/^\s*(\/\/|\/\*|import |const |let |var |function |class |export )/.test(line)) continue;
225
- for (const name of entityNames) {
226
- const re = new RegExp(`\\b${name}\\s*\\(`, 'g');
227
- if (re.test(line)) {
228
- calls.push({ caller: `${filePath}:${i + 1}:_caller`, callee: name, line: i + 1 });
229
- }
230
- }
231
- }
232
-
233
- return { entities, imports, calls };
234
- }
235
-
236
- // --- Python Parser ---
237
-
238
- function parsePython(content, filePath) {
239
- const entities = [];
240
- const imports = [];
241
- const calls = [];
242
- const lines = content.split('\n');
243
- let currentClass = null;
244
-
245
- for (let i = 0; i < lines.length; i++) {
246
- const line = lines[i];
247
- const lineNum = i + 1;
248
-
249
- // Class declaration
250
- const classMatch = line.match(/^class\s+(\w+)\s*[:(]/);
251
- if (classMatch) {
252
- currentClass = classMatch[1];
253
- entities.push({
254
- id: `${filePath}:${lineNum}:${classMatch[1]}`,
255
- name: classMatch[1],
256
- type: 'class',
257
- file: filePath,
258
- line: lineNum,
259
- domain: null,
260
- exported: true
261
- });
262
- continue;
263
- }
264
-
265
- // Function/method declaration
266
- const defMatch = line.match(/^(\s*)def\s+(\w+)\s*\(/);
267
- if (defMatch) {
268
- const indent = defMatch[1].length;
269
- const isMethod = indent > 0 && currentClass;
270
- if (!defMatch[2].startsWith('_') || defMatch[2] === '__init__') {
271
- entities.push({
272
- id: `${filePath}:${lineNum}:${defMatch[2]}`,
273
- name: defMatch[2],
274
- type: isMethod ? 'method' : 'function',
275
- file: filePath,
276
- line: lineNum,
277
- domain: null,
278
- exported: !defMatch[2].startsWith('_')
279
- });
280
- }
281
- continue;
282
- }
283
-
284
- // Reset class context on unindented non-empty line
285
- if (currentClass && /^\S/.test(line) && line.trim()) {
286
- currentClass = null;
287
- }
288
-
289
- // import x, import x as y
290
- const importMatch = line.match(/^import\s+([\w.]+)/);
291
- if (importMatch) {
292
- imports.push({
293
- source: filePath,
294
- target: importMatch[1],
295
- names: [importMatch[1].split('.').pop()],
296
- line: lineNum
297
- });
298
- continue;
299
- }
300
-
301
- // from x import y, z
302
- const fromMatch = line.match(/^from\s+([\w.]+)\s+import\s+(.+)/);
303
- if (fromMatch) {
304
- const names = fromMatch[2].split(',').map(n =>
305
- n.trim().split(/\s+as\s+/)[0].trim()
306
- ).filter(Boolean);
307
- imports.push({
308
- source: filePath,
309
- target: fromMatch[1],
310
- names,
311
- line: lineNum
312
- });
313
- }
314
- }
315
-
316
- return { entities, imports, calls };
317
- }
318
-
319
- function getParser(ext) {
320
- if (['.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx'].includes(ext)) {
321
- return parseJavaScript;
322
- }
323
- if (ext === '.py') return parsePython;
324
- return null;
325
- }
326
-
327
- module.exports = { parseJavaScript, parsePython, getParser };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Language-specific parsers for extracting code entities.
5
+ * Zero external dependencies — regex-based only.
6
+ */
7
+
8
+ // --- JS/TS Parser ---
9
+
10
+ const JS_FUNC_PATTERNS = [
11
+ // function declarations: function name(, async function name(
12
+ /^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/gm,
13
+ // arrow/const: const name = (, const name = function(, const name = async (
14
+ /^\s*(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:function\s*)?\(/gm,
15
+ // arrow with =>: const name = (...) =>
16
+ /^\s*(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/gm,
17
+ ];
18
+
19
+ const JS_CLASS_PATTERN = /^\s*(?:export\s+)?(?:default\s+)?class\s+(\w+)/gm;
20
+
21
+ const JS_METHOD_PATTERN = /^\s+(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/gm;
22
+
23
+ const JS_IMPORT_PATTERNS = [
24
+ // import { x, y } from 'module'
25
+ /^\s*import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/gm,
26
+ // import x from 'module'
27
+ /^\s*import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/gm,
28
+ // import * as x from 'module'
29
+ /^\s*import\s+\*\s+as\s+(\w+)\s+from\s+['"]([^'"]+)['"]/gm,
30
+ // const x = require('module')
31
+ /^\s*(?:const|let|var)\s+(?:\{([^}]+)\}|(\w+))\s*=\s*require\(\s*['"]([^'"]+)['"]\s*\)/gm,
32
+ ];
33
+
34
+ const JS_EXPORT_PATTERNS = [
35
+ // module.exports = { ... } or module.exports = name
36
+ /^\s*module\.exports\s*=\s*/gm,
37
+ // export default
38
+ /^\s*export\s+default\s+/gm,
39
+ // export { x, y }
40
+ /^\s*export\s+\{([^}]+)\}/gm,
41
+ ];
42
+
43
+ function parseJavaScript(content, filePath) {
44
+ const entities = [];
45
+ const imports = [];
46
+ const calls = [];
47
+ const lines = content.split('\n');
48
+ const exportedNames = new Set();
49
+ let currentClass = null;
50
+
51
+ // Collect exported names
52
+ for (const line of lines) {
53
+ if (/^\s*export\s+/.test(line)) {
54
+ const m = line.match(/(?:function|class|const|let|var)\s+(\w+)/);
55
+ if (m) exportedNames.add(m[1]);
56
+ }
57
+ const exportMatch = line.match(/^\s*export\s+\{([^}]+)\}/);
58
+ if (exportMatch) {
59
+ exportMatch[1].split(',').forEach(n => {
60
+ const name = n.trim().split(/\s+as\s+/)[0].trim();
61
+ if (name) exportedNames.add(name);
62
+ });
63
+ }
64
+ if (/module\.exports/.test(line)) {
65
+ const m = line.match(/module\.exports\s*=\s*\{\s*([^}]+)\}/);
66
+ if (m) {
67
+ m[1].split(',').forEach(n => {
68
+ const name = n.trim().split(':')[0].trim();
69
+ if (name) exportedNames.add(name);
70
+ });
71
+ }
72
+ }
73
+ }
74
+
75
+ // Extract entities line by line
76
+ for (let i = 0; i < lines.length; i++) {
77
+ const line = lines[i];
78
+ const lineNum = i + 1;
79
+
80
+ // Class declarations
81
+ const classMatch = line.match(
82
+ /^\s*(?:export\s+)?(?:default\s+)?class\s+(\w+)/
83
+ );
84
+ if (classMatch) {
85
+ currentClass = classMatch[1];
86
+ entities.push({
87
+ id: `${filePath}:${lineNum}:${classMatch[1]}`,
88
+ name: classMatch[1],
89
+ type: 'class',
90
+ file: filePath,
91
+ line: lineNum,
92
+ domain: null,
93
+ exported: exportedNames.has(classMatch[1]) ||
94
+ /export/.test(line)
95
+ });
96
+ continue;
97
+ }
98
+
99
+ // Method declarations (inside class)
100
+ if (currentClass) {
101
+ const methodMatch = line.match(
102
+ /^\s+(?:static\s+)?(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/
103
+ );
104
+ if (methodMatch && methodMatch[1] !== 'constructor') {
105
+ entities.push({
106
+ id: `${filePath}:${lineNum}:${methodMatch[1]}`,
107
+ name: methodMatch[1],
108
+ type: 'method',
109
+ file: filePath,
110
+ line: lineNum,
111
+ domain: null,
112
+ exported: exportedNames.has(currentClass)
113
+ });
114
+ continue;
115
+ }
116
+ // Detect end of class
117
+ if (/^}/.test(line)) currentClass = null;
118
+ }
119
+
120
+ // Function declarations
121
+ const funcMatch = line.match(
122
+ /^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/
123
+ );
124
+ if (funcMatch) {
125
+ entities.push({
126
+ id: `${filePath}:${lineNum}:${funcMatch[1]}`,
127
+ name: funcMatch[1],
128
+ type: 'function',
129
+ file: filePath,
130
+ line: lineNum,
131
+ domain: null,
132
+ exported: exportedNames.has(funcMatch[1]) ||
133
+ /export/.test(line)
134
+ });
135
+ continue;
136
+ }
137
+
138
+ // Arrow/const functions
139
+ const arrowMatch = line.match(
140
+ /^\s*(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:function\s*)?\(/
141
+ ) || line.match(
142
+ /^\s*(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/
143
+ );
144
+ if (arrowMatch) {
145
+ entities.push({
146
+ id: `${filePath}:${lineNum}:${arrowMatch[1]}`,
147
+ name: arrowMatch[1],
148
+ type: 'function',
149
+ file: filePath,
150
+ line: lineNum,
151
+ domain: null,
152
+ exported: exportedNames.has(arrowMatch[1]) ||
153
+ /export/.test(line)
154
+ });
155
+ continue;
156
+ }
157
+
158
+ // Import statements
159
+ const esImportNamed = line.match(
160
+ /^\s*import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/
161
+ );
162
+ if (esImportNamed) {
163
+ const names = esImportNamed[1].split(',').map(n =>
164
+ n.trim().split(/\s+as\s+/)[0].trim()
165
+ ).filter(Boolean);
166
+ imports.push({
167
+ source: filePath,
168
+ target: esImportNamed[2],
169
+ names,
170
+ line: lineNum
171
+ });
172
+ continue;
173
+ }
174
+
175
+ const esImportDefault = line.match(
176
+ /^\s*import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/
177
+ );
178
+ if (esImportDefault) {
179
+ imports.push({
180
+ source: filePath,
181
+ target: esImportDefault[2],
182
+ names: [esImportDefault[1]],
183
+ line: lineNum
184
+ });
185
+ continue;
186
+ }
187
+
188
+ const esImportStar = line.match(
189
+ /^\s*import\s+\*\s+as\s+(\w+)\s+from\s+['"]([^'"]+)['"]/
190
+ );
191
+ if (esImportStar) {
192
+ imports.push({
193
+ source: filePath,
194
+ target: esImportStar[2],
195
+ names: [esImportStar[1]],
196
+ line: lineNum
197
+ });
198
+ continue;
199
+ }
200
+
201
+ const requireMatch = line.match(
202
+ /^\s*(?:const|let|var)\s+(?:\{([^}]+)\}|(\w+))\s*=\s*require\(\s*['"]([^'"]+)['"]\s*\)/
203
+ );
204
+ if (requireMatch) {
205
+ const names = requireMatch[1]
206
+ ? requireMatch[1].split(',').map(n =>
207
+ n.trim().split(':')[0].trim()
208
+ ).filter(Boolean)
209
+ : [requireMatch[2]];
210
+ imports.push({
211
+ source: filePath,
212
+ target: requireMatch[3],
213
+ names,
214
+ line: lineNum
215
+ });
216
+ }
217
+ }
218
+
219
+ // Extract calls (best-effort: known entity names followed by '(')
220
+ const entityNames = new Set(entities.map(e => e.name));
221
+ for (let i = 0; i < lines.length; i++) {
222
+ const line = lines[i];
223
+ // Skip declarations, imports, comments
224
+ if (/^\s*(\/\/|\/\*|import |const |let |var |function |class |export )/.test(line)) continue;
225
+ for (const name of entityNames) {
226
+ const re = new RegExp(`\\b${name}\\s*\\(`, 'g');
227
+ if (re.test(line)) {
228
+ calls.push({ caller: `${filePath}:${i + 1}:_caller`, callee: name, line: i + 1 });
229
+ }
230
+ }
231
+ }
232
+
233
+ return { entities, imports, calls };
234
+ }
235
+
236
+ // --- Python Parser ---
237
+
238
+ function parsePython(content, filePath) {
239
+ const entities = [];
240
+ const imports = [];
241
+ const calls = [];
242
+ const lines = content.split('\n');
243
+ let currentClass = null;
244
+
245
+ for (let i = 0; i < lines.length; i++) {
246
+ const line = lines[i];
247
+ const lineNum = i + 1;
248
+
249
+ // Class declaration
250
+ const classMatch = line.match(/^class\s+(\w+)\s*[:(]/);
251
+ if (classMatch) {
252
+ currentClass = classMatch[1];
253
+ entities.push({
254
+ id: `${filePath}:${lineNum}:${classMatch[1]}`,
255
+ name: classMatch[1],
256
+ type: 'class',
257
+ file: filePath,
258
+ line: lineNum,
259
+ domain: null,
260
+ exported: true
261
+ });
262
+ continue;
263
+ }
264
+
265
+ // Function/method declaration
266
+ const defMatch = line.match(/^(\s*)def\s+(\w+)\s*\(/);
267
+ if (defMatch) {
268
+ const indent = defMatch[1].length;
269
+ const isMethod = indent > 0 && currentClass;
270
+ if (!defMatch[2].startsWith('_') || defMatch[2] === '__init__') {
271
+ entities.push({
272
+ id: `${filePath}:${lineNum}:${defMatch[2]}`,
273
+ name: defMatch[2],
274
+ type: isMethod ? 'method' : 'function',
275
+ file: filePath,
276
+ line: lineNum,
277
+ domain: null,
278
+ exported: !defMatch[2].startsWith('_')
279
+ });
280
+ }
281
+ continue;
282
+ }
283
+
284
+ // Reset class context on unindented non-empty line
285
+ if (currentClass && /^\S/.test(line) && line.trim()) {
286
+ currentClass = null;
287
+ }
288
+
289
+ // import x, import x as y
290
+ const importMatch = line.match(/^import\s+([\w.]+)/);
291
+ if (importMatch) {
292
+ imports.push({
293
+ source: filePath,
294
+ target: importMatch[1],
295
+ names: [importMatch[1].split('.').pop()],
296
+ line: lineNum
297
+ });
298
+ continue;
299
+ }
300
+
301
+ // from x import y, z
302
+ const fromMatch = line.match(/^from\s+([\w.]+)\s+import\s+(.+)/);
303
+ if (fromMatch) {
304
+ const names = fromMatch[2].split(',').map(n =>
305
+ n.trim().split(/\s+as\s+/)[0].trim()
306
+ ).filter(Boolean);
307
+ imports.push({
308
+ source: filePath,
309
+ target: fromMatch[1],
310
+ names,
311
+ line: lineNum
312
+ });
313
+ }
314
+ }
315
+
316
+ return { entities, imports, calls };
317
+ }
318
+
319
+ function getParser(ext) {
320
+ if (['.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx'].includes(ext)) {
321
+ return parseJavaScript;
322
+ }
323
+ if (ext === '.py') return parsePython;
324
+ return null;
325
+ }
326
+
327
+ module.exports = { parseJavaScript, parsePython, getParser };