@vreko/cli 3.0.1

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 (98) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +45 -0
  3. package/dist/CeremonyView-LQS7FTMK.js +134 -0
  4. package/dist/CeremonyView-LQS7FTMK.js.map +1 -0
  5. package/dist/InitApp-7K5DTYSW.js +1479 -0
  6. package/dist/InitApp-7K5DTYSW.js.map +1 -0
  7. package/dist/SkippedTestDetector-PJSKSOZR.js +7 -0
  8. package/dist/SkippedTestDetector-PJSKSOZR.js.map +1 -0
  9. package/dist/TuiApp-FX23XQBK.js +8 -0
  10. package/dist/TuiApp-FX23XQBK.js.map +1 -0
  11. package/dist/analysis-ABEO6RTN.js +8 -0
  12. package/dist/analysis-ABEO6RTN.js.map +1 -0
  13. package/dist/auth-XNBEBNPY.js +7669 -0
  14. package/dist/auth-XNBEBNPY.js.map +1 -0
  15. package/dist/ceremony-M7CXVBVA.js +45 -0
  16. package/dist/ceremony-M7CXVBVA.js.map +1 -0
  17. package/dist/chunk-A3QSZJPD.js +3147 -0
  18. package/dist/chunk-A3QSZJPD.js.map +1 -0
  19. package/dist/chunk-ASGZ5B6C.js +3969 -0
  20. package/dist/chunk-ASGZ5B6C.js.map +1 -0
  21. package/dist/chunk-DMXC2JTC.js +58 -0
  22. package/dist/chunk-DMXC2JTC.js.map +1 -0
  23. package/dist/chunk-EEBSK2IH.js +161 -0
  24. package/dist/chunk-EEBSK2IH.js.map +1 -0
  25. package/dist/chunk-EWOJGXRX.js +22 -0
  26. package/dist/chunk-EWOJGXRX.js.map +1 -0
  27. package/dist/chunk-F7GEJLP7.js +2389 -0
  28. package/dist/chunk-F7GEJLP7.js.map +1 -0
  29. package/dist/chunk-GOYL3F4T.js +605 -0
  30. package/dist/chunk-GOYL3F4T.js.map +1 -0
  31. package/dist/chunk-GRMRYWYS.js +17 -0
  32. package/dist/chunk-GRMRYWYS.js.map +1 -0
  33. package/dist/chunk-GSUGROXB.js +1951 -0
  34. package/dist/chunk-GSUGROXB.js.map +1 -0
  35. package/dist/chunk-H7773ONB.js +50 -0
  36. package/dist/chunk-H7773ONB.js.map +1 -0
  37. package/dist/chunk-HFQHU5LC.js +445 -0
  38. package/dist/chunk-HFQHU5LC.js.map +1 -0
  39. package/dist/chunk-IVHUBLJD.js +318 -0
  40. package/dist/chunk-IVHUBLJD.js.map +1 -0
  41. package/dist/chunk-KJWKY4L4.js +14 -0
  42. package/dist/chunk-KJWKY4L4.js.map +1 -0
  43. package/dist/chunk-MJVY2XUN.js +1793 -0
  44. package/dist/chunk-MJVY2XUN.js.map +1 -0
  45. package/dist/chunk-QWZVCJII.js +1797 -0
  46. package/dist/chunk-QWZVCJII.js.map +1 -0
  47. package/dist/chunk-VTSNRV3V.js +3237 -0
  48. package/dist/chunk-VTSNRV3V.js.map +1 -0
  49. package/dist/chunk-W5B4GTXR.js +1466 -0
  50. package/dist/chunk-W5B4GTXR.js.map +1 -0
  51. package/dist/chunk-WZEZLVOW.js +4995 -0
  52. package/dist/chunk-WZEZLVOW.js.map +1 -0
  53. package/dist/chunk-YPTTIXKC.js +199 -0
  54. package/dist/chunk-YPTTIXKC.js.map +1 -0
  55. package/dist/chunk-Z55UGM6X.js +6360 -0
  56. package/dist/chunk-Z55UGM6X.js.map +1 -0
  57. package/dist/chunk-ZIIRQODJ.js +110 -0
  58. package/dist/chunk-ZIIRQODJ.js.map +1 -0
  59. package/dist/chunk-ZSUQ4FMB.js +77 -0
  60. package/dist/chunk-ZSUQ4FMB.js.map +1 -0
  61. package/dist/client-JMTSZS3V.js +10 -0
  62. package/dist/client-JMTSZS3V.js.map +1 -0
  63. package/dist/deprecated-snap.js +19 -0
  64. package/dist/deprecated-snap.js.map +1 -0
  65. package/dist/dist-2KWBZFLA.js +14 -0
  66. package/dist/dist-2KWBZFLA.js.map +1 -0
  67. package/dist/dist-5ZYKNNU3.js +7 -0
  68. package/dist/dist-5ZYKNNU3.js.map +1 -0
  69. package/dist/dist-CP3RFHPI.js +11 -0
  70. package/dist/dist-CP3RFHPI.js.map +1 -0
  71. package/dist/gecko-53ITAGG6.js +56 -0
  72. package/dist/gecko-53ITAGG6.js.map +1 -0
  73. package/dist/guards-QAFC64NO.js +7 -0
  74. package/dist/guards-QAFC64NO.js.map +1 -0
  75. package/dist/index.js +57785 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/init-command-246JIVXM.js +7 -0
  78. package/dist/init-command-246JIVXM.js.map +1 -0
  79. package/dist/init-core-KAI7LCXZ.js +12 -0
  80. package/dist/init-core-KAI7LCXZ.js.map +1 -0
  81. package/dist/init-scan-RZNYDTUV.js +1919 -0
  82. package/dist/init-scan-RZNYDTUV.js.map +1 -0
  83. package/dist/local-service-adapter-6KNN6WQL.js +8 -0
  84. package/dist/local-service-adapter-6KNN6WQL.js.map +1 -0
  85. package/dist/secure-credentials-JXWAQLS2.js +306 -0
  86. package/dist/secure-credentials-JXWAQLS2.js.map +1 -0
  87. package/dist/tui-TPJPUS2R.js +111 -0
  88. package/dist/tui-TPJPUS2R.js.map +1 -0
  89. package/dist/vreko-dir-O3RLG7PI.js +8 -0
  90. package/dist/vreko-dir-O3RLG7PI.js.map +1 -0
  91. package/package.json +132 -0
  92. package/scripts/check-banned-words.ts +152 -0
  93. package/scripts/hooks/posttooluse-file-notify.sh +108 -0
  94. package/scripts/hooks/pretooluse-fragile-guard.sh +82 -0
  95. package/scripts/post-install-notice.js +24 -0
  96. package/scripts/postinstall.mjs +84 -0
  97. package/scripts/preuninstall.mjs +34 -0
  98. package/scripts/verify-jsx-transform.mjs +55 -0
@@ -0,0 +1,2389 @@
1
+ #!/usr/bin/env node
2
+ import { __name } from './chunk-EWOJGXRX.js';
3
+ import { parseSync } from 'oxc-parser';
4
+ import { dirname, resolve, relative, basename } from 'path';
5
+ import * as eslintParser from '@typescript-eslint/parser';
6
+ import { parse } from '@babel/parser';
7
+ import traverse from '@babel/traverse';
8
+
9
+ process.env.VREKO_CLI='true';process.env.NODE_NO_WARNINGS='1';
10
+ var TS_EXTENSIONS = /* @__PURE__ */ new Set([
11
+ ".ts",
12
+ ".tsx",
13
+ ".mts",
14
+ ".cts"
15
+ ]);
16
+ var JSX_EXTENSIONS = /* @__PURE__ */ new Set([
17
+ ".tsx",
18
+ ".jsx"
19
+ ]);
20
+ var ALL_EXTENSIONS = /* @__PURE__ */ new Set([
21
+ ".ts",
22
+ ".tsx",
23
+ ".js",
24
+ ".jsx",
25
+ ".mts",
26
+ ".cts",
27
+ ".mjs",
28
+ ".cjs"
29
+ ]);
30
+ function isSupportedFile(filePath) {
31
+ const ext = getExtension(filePath);
32
+ return ALL_EXTENSIONS.has(ext);
33
+ }
34
+ __name(isSupportedFile, "isSupportedFile");
35
+ function parseSource(content, filePath) {
36
+ const ext = getExtension(filePath);
37
+ try {
38
+ const lang = JSX_EXTENSIONS.has(ext) ? TS_EXTENSIONS.has(ext) ? "tsx" : "jsx" : TS_EXTENSIONS.has(ext) ? "ts" : "js";
39
+ const result = parseSync(filePath, content, {
40
+ sourceType: "module",
41
+ lang
42
+ });
43
+ const errors = Array.isArray(result.errors) ? result.errors.map((e) => normalizeError(e)) : [];
44
+ return {
45
+ program: result.program,
46
+ errors,
47
+ success: errors.length === 0
48
+ };
49
+ } catch (error) {
50
+ return {
51
+ program: {
52
+ type: "Program",
53
+ body: [],
54
+ sourceType: "module"
55
+ },
56
+ errors: [
57
+ {
58
+ message: error instanceof Error ? error.message : String(error),
59
+ severity: "error"
60
+ }
61
+ ],
62
+ success: false
63
+ };
64
+ }
65
+ }
66
+ __name(parseSource, "parseSource");
67
+ function walkAST(node, visitor, parent) {
68
+ if (!node || typeof node !== "object") {
69
+ return;
70
+ }
71
+ const n = node;
72
+ if (typeof n.type === "string") {
73
+ visitor(n, parent);
74
+ }
75
+ for (const key of Object.keys(n)) {
76
+ if (key === "type" || key === "start" || key === "end" || key === "loc") {
77
+ continue;
78
+ }
79
+ const value = n[key];
80
+ if (Array.isArray(value)) {
81
+ for (const item of value) {
82
+ if (item && typeof item === "object" && typeof item.type === "string") {
83
+ walkAST(item, visitor, n);
84
+ }
85
+ }
86
+ } else if (value && typeof value === "object" && typeof value.type === "string") {
87
+ walkAST(value, visitor, n);
88
+ }
89
+ }
90
+ }
91
+ __name(walkAST, "walkAST");
92
+ function countASTNodes(program) {
93
+ let count = 0;
94
+ walkAST(program, () => {
95
+ count++;
96
+ });
97
+ return count;
98
+ }
99
+ __name(countASTNodes, "countASTNodes");
100
+ function offsetToLine(source, offset) {
101
+ if (offset < 0 || offset > source.length) {
102
+ return 1;
103
+ }
104
+ let line = 1;
105
+ for (let i = 0; i < offset; i++) {
106
+ if (source[i] === "\n") {
107
+ line++;
108
+ }
109
+ }
110
+ return line;
111
+ }
112
+ __name(offsetToLine, "offsetToLine");
113
+ function getExtension(filePath) {
114
+ const lastDot = filePath.lastIndexOf(".");
115
+ return lastDot >= 0 ? filePath.substring(lastDot).toLowerCase() : "";
116
+ }
117
+ __name(getExtension, "getExtension");
118
+ function normalizeError(e) {
119
+ if (typeof e === "object" && e !== null) {
120
+ const err = e;
121
+ return {
122
+ message: String(err.message ?? err),
123
+ severity: String(err.severity ?? "error"),
124
+ labels: Array.isArray(err.labels) ? err.labels : void 0
125
+ };
126
+ }
127
+ return {
128
+ message: String(e),
129
+ severity: "error"
130
+ };
131
+ }
132
+ __name(normalizeError, "normalizeError");
133
+
134
+ // ../../packages/core/dist/analysis/ast/ComplexityAnalyzer.js
135
+ var THRESHOLDS = {
136
+ /** Cyclomatic complexity per function */
137
+ maxCyclomaticPerFunction: 15,
138
+ /** Maximum nesting depth per function */
139
+ maxNestingDepth: 5,
140
+ /** Maximum parameters per function */
141
+ maxParameters: 5,
142
+ /** Maximum functions per file */
143
+ maxFunctionsPerFile: 30,
144
+ /** File-level aggregate cyclomatic complexity */
145
+ maxCyclomaticPerFile: 50
146
+ };
147
+ var BRANCH_NODES = /* @__PURE__ */ new Set([
148
+ "IfStatement",
149
+ "ConditionalExpression",
150
+ "SwitchCase",
151
+ "ForStatement",
152
+ "ForInStatement",
153
+ "ForOfStatement",
154
+ "WhileStatement",
155
+ "DoWhileStatement",
156
+ "CatchClause"
157
+ ]);
158
+ var LOGICAL_OPERATORS = /* @__PURE__ */ new Set([
159
+ "&&",
160
+ "||",
161
+ "??"
162
+ ]);
163
+ var FUNCTION_NODES = /* @__PURE__ */ new Set([
164
+ "FunctionDeclaration",
165
+ "FunctionExpression",
166
+ "ArrowFunctionExpression",
167
+ "MethodDefinition"
168
+ ]);
169
+ var ComplexityAnalyzer = class {
170
+ static {
171
+ __name(this, "ComplexityAnalyzer");
172
+ }
173
+ id = "complexity";
174
+ name = "Complexity Analysis";
175
+ filePatterns = [
176
+ "*.ts",
177
+ "*.tsx",
178
+ "*.js",
179
+ "*.jsx"
180
+ ];
181
+ async analyze(context) {
182
+ const startTime = performance.now();
183
+ const issues = [];
184
+ let filesAnalyzed = 0;
185
+ let totalNodesVisited = 0;
186
+ const parseErrors = [];
187
+ for (const [file, content] of context.contents) {
188
+ if (!this.shouldAnalyzeFile(file)) {
189
+ continue;
190
+ }
191
+ filesAnalyzed++;
192
+ const fileComplexity = this.analyzeFile(content, file);
193
+ if (!fileComplexity) {
194
+ parseErrors.push(`${file}: Failed to parse`);
195
+ continue;
196
+ }
197
+ totalNodesVisited += fileComplexity.functions.length;
198
+ for (const fn of fileComplexity.functions) {
199
+ if (fn.cyclomatic > THRESHOLDS.maxCyclomaticPerFunction) {
200
+ issues.push({
201
+ id: `complexity/cyclomatic/${file}/${fn.line}`,
202
+ severity: fn.cyclomatic > THRESHOLDS.maxCyclomaticPerFunction * 2 ? "high" : "medium",
203
+ type: "HIGH_CYCLOMATIC_COMPLEXITY",
204
+ message: `Function "${fn.name}" has cyclomatic complexity ${fn.cyclomatic} (max: ${THRESHOLDS.maxCyclomaticPerFunction})`,
205
+ file,
206
+ line: fn.line,
207
+ fix: "Extract helper functions or simplify branching logic"
208
+ });
209
+ }
210
+ if (fn.maxNesting > THRESHOLDS.maxNestingDepth) {
211
+ issues.push({
212
+ id: `complexity/nesting/${file}/${fn.line}`,
213
+ severity: "medium",
214
+ type: "DEEP_NESTING",
215
+ message: `Function "${fn.name}" has nesting depth ${fn.maxNesting} (max: ${THRESHOLDS.maxNestingDepth})`,
216
+ file,
217
+ line: fn.line,
218
+ fix: "Use early returns or extract nested logic into helper functions"
219
+ });
220
+ }
221
+ if (fn.parameters > THRESHOLDS.maxParameters) {
222
+ issues.push({
223
+ id: `complexity/parameters/${file}/${fn.line}`,
224
+ severity: "low",
225
+ type: "TOO_MANY_PARAMETERS",
226
+ message: `Function "${fn.name}" has ${fn.parameters} parameters (max: ${THRESHOLDS.maxParameters})`,
227
+ file,
228
+ line: fn.line,
229
+ fix: "Use an options object pattern to reduce parameter count"
230
+ });
231
+ }
232
+ }
233
+ if (fileComplexity.functionCount > THRESHOLDS.maxFunctionsPerFile) {
234
+ issues.push({
235
+ id: `complexity/function-count/${file}`,
236
+ severity: "low",
237
+ type: "TOO_MANY_FUNCTIONS",
238
+ message: `${file} has ${fileComplexity.functionCount} functions (max: ${THRESHOLDS.maxFunctionsPerFile})`,
239
+ file,
240
+ fix: "Split into multiple focused modules"
241
+ });
242
+ }
243
+ if (fileComplexity.totalCyclomatic > THRESHOLDS.maxCyclomaticPerFile) {
244
+ issues.push({
245
+ id: `complexity/file-complexity/${file}`,
246
+ severity: "medium",
247
+ type: "HIGH_FILE_COMPLEXITY",
248
+ message: `${file} has total cyclomatic complexity ${fileComplexity.totalCyclomatic} (max: ${THRESHOLDS.maxCyclomaticPerFile})`,
249
+ file,
250
+ fix: "Consider splitting this file into smaller modules"
251
+ });
252
+ }
253
+ }
254
+ return {
255
+ analyzer: this.id,
256
+ success: true,
257
+ issues,
258
+ coverage: filesAnalyzed / Math.max(context.files.length, 1),
259
+ duration: performance.now() - startTime,
260
+ metadata: {
261
+ filesAnalyzed,
262
+ nodesVisited: totalNodesVisited,
263
+ patternsChecked: [
264
+ "HIGH_CYCLOMATIC_COMPLEXITY",
265
+ "DEEP_NESTING",
266
+ "TOO_MANY_PARAMETERS",
267
+ "TOO_MANY_FUNCTIONS",
268
+ "HIGH_FILE_COMPLEXITY"
269
+ ],
270
+ parseErrors
271
+ }
272
+ };
273
+ }
274
+ shouldRun(context) {
275
+ return context.files.some((f) => this.shouldAnalyzeFile(f));
276
+ }
277
+ /**
278
+ * Analyze a single file and return complexity metrics.
279
+ * Useful for external callers that just want metrics, not issues.
280
+ */
281
+ analyzeFile(content, filePath) {
282
+ if (!isSupportedFile(filePath)) {
283
+ return null;
284
+ }
285
+ const { program, success } = parseSource(content, filePath);
286
+ if (!success && program.body.length === 0) {
287
+ return null;
288
+ }
289
+ const functions = [];
290
+ let fileMaxNesting = 0;
291
+ walkAST(program, (node) => {
292
+ if (!FUNCTION_NODES.has(node.type)) {
293
+ return;
294
+ }
295
+ const fn = this.analyzeFunctionNode(node);
296
+ functions.push(fn);
297
+ if (fn.maxNesting > fileMaxNesting) {
298
+ fileMaxNesting = fn.maxNesting;
299
+ }
300
+ });
301
+ const totalCyclomatic = functions.reduce((sum, fn) => sum + fn.cyclomatic, 0);
302
+ return {
303
+ filePath,
304
+ functions,
305
+ totalCyclomatic,
306
+ maxNesting: fileMaxNesting,
307
+ functionCount: functions.length,
308
+ averageCyclomatic: functions.length > 0 ? totalCyclomatic / functions.length : 0
309
+ };
310
+ }
311
+ // -----------------------------------------------------------------------
312
+ // Per-function analysis
313
+ // -----------------------------------------------------------------------
314
+ analyzeFunctionNode(node) {
315
+ const name = this.getFunctionName(node);
316
+ const line = node.start ?? 0;
317
+ const params = this.getParameterCount(node);
318
+ let cyclomatic = 1;
319
+ let maxNesting = 0;
320
+ const body = this.getFunctionBody(node);
321
+ if (body) {
322
+ this.walkForComplexity(body, (n, depth) => {
323
+ if (BRANCH_NODES.has(n.type)) {
324
+ cyclomatic++;
325
+ }
326
+ if (n.type === "LogicalExpression") {
327
+ const operator = n.operator;
328
+ if (LOGICAL_OPERATORS.has(operator)) {
329
+ cyclomatic++;
330
+ }
331
+ }
332
+ if (depth > maxNesting) {
333
+ maxNesting = depth;
334
+ }
335
+ });
336
+ }
337
+ return {
338
+ name,
339
+ line,
340
+ cyclomatic,
341
+ maxNesting,
342
+ parameters: params
343
+ };
344
+ }
345
+ /**
346
+ * Walk AST nodes counting nesting depth for complexity metrics
347
+ */
348
+ walkForComplexity(node, callback, depth = 0) {
349
+ if (!node || typeof node !== "object") {
350
+ return;
351
+ }
352
+ const n = node;
353
+ if (typeof n.type !== "string") {
354
+ return;
355
+ }
356
+ const nestingNodes = /* @__PURE__ */ new Set([
357
+ "IfStatement",
358
+ "ForStatement",
359
+ "ForInStatement",
360
+ "ForOfStatement",
361
+ "WhileStatement",
362
+ "DoWhileStatement",
363
+ "SwitchStatement",
364
+ "TryStatement"
365
+ ]);
366
+ const newDepth = nestingNodes.has(n.type) ? depth + 1 : depth;
367
+ callback(n, newDepth);
368
+ for (const key of Object.keys(n)) {
369
+ if (key === "type" || key === "start" || key === "end" || key === "loc") {
370
+ continue;
371
+ }
372
+ const value = n[key];
373
+ if (Array.isArray(value)) {
374
+ for (const item of value) {
375
+ this.walkForComplexity(item, callback, newDepth);
376
+ }
377
+ } else if (value && typeof value === "object" && typeof value.type === "string") {
378
+ this.walkForComplexity(value, callback, newDepth);
379
+ }
380
+ }
381
+ }
382
+ // -----------------------------------------------------------------------
383
+ // Helpers
384
+ // -----------------------------------------------------------------------
385
+ getFunctionName(node) {
386
+ if (node.id && typeof node.id === "object") {
387
+ const id = node.id;
388
+ if (typeof id.name === "string") {
389
+ return id.name;
390
+ }
391
+ }
392
+ if (node.key && typeof node.key === "object") {
393
+ const key = node.key;
394
+ if (typeof key.name === "string") {
395
+ return key.name;
396
+ }
397
+ if (typeof key.value === "string") {
398
+ return key.value;
399
+ }
400
+ }
401
+ return "<anonymous>";
402
+ }
403
+ getParameterCount(node) {
404
+ if (node.type === "MethodDefinition") {
405
+ const value = node.value;
406
+ if (value && Array.isArray(value.params)) {
407
+ return value.params.length;
408
+ }
409
+ }
410
+ if (Array.isArray(node.params)) {
411
+ return node.params.length;
412
+ }
413
+ return 0;
414
+ }
415
+ getFunctionBody(node) {
416
+ if (node.type === "MethodDefinition") {
417
+ const value = node.value;
418
+ if (value?.body) {
419
+ return value.body;
420
+ }
421
+ }
422
+ if (node.body) {
423
+ return node.body;
424
+ }
425
+ return null;
426
+ }
427
+ shouldAnalyzeFile(file) {
428
+ const ext = file.split(".").pop()?.toLowerCase();
429
+ return [
430
+ "ts",
431
+ "tsx",
432
+ "js",
433
+ "jsx",
434
+ "mts",
435
+ "cts"
436
+ ].includes(ext ?? "");
437
+ }
438
+ };
439
+
440
+ // ../../packages/core/dist/analysis/ast/import-extractor.js
441
+ function extractImports(content, filePath) {
442
+ if (!isSupportedFile(filePath)) {
443
+ return {
444
+ filePath,
445
+ imports: [],
446
+ parseSuccess: false,
447
+ parseErrors: [
448
+ `Unsupported file type: ${filePath}`
449
+ ]
450
+ };
451
+ }
452
+ const { program, errors, success } = parseSource(content, filePath);
453
+ const imports = [];
454
+ walkAST(program, (node) => {
455
+ switch (node.type) {
456
+ // import ... from 'source'
457
+ case "ImportDeclaration":
458
+ handleImportDeclaration(node, imports);
459
+ break;
460
+ // export { ... } from 'source' OR export * from 'source'
461
+ case "ExportNamedDeclaration":
462
+ case "ExportAllDeclaration":
463
+ handleExportDeclaration(node, imports);
464
+ break;
465
+ // import('source') - ESTree ImportExpression
466
+ case "ImportExpression":
467
+ handleImportExpression(node, imports);
468
+ break;
469
+ // require('source') OR legacy import('source') as CallExpression
470
+ case "CallExpression":
471
+ handleCallExpression(node, imports);
472
+ break;
473
+ }
474
+ });
475
+ return {
476
+ filePath,
477
+ imports,
478
+ parseSuccess: success,
479
+ parseErrors: errors.map((e) => e.message)
480
+ };
481
+ }
482
+ __name(extractImports, "extractImports");
483
+ function extractImportSources(content, filePath) {
484
+ const result = extractImports(content, filePath);
485
+ const sources = /* @__PURE__ */ new Set();
486
+ for (const imp of result.imports) {
487
+ sources.add(imp.source);
488
+ }
489
+ return [
490
+ ...sources
491
+ ];
492
+ }
493
+ __name(extractImportSources, "extractImportSources");
494
+ function extractImportsBatch(files) {
495
+ const results = /* @__PURE__ */ new Map();
496
+ for (const [filePath, content] of files) {
497
+ results.set(filePath, extractImports(content, filePath));
498
+ }
499
+ return results;
500
+ }
501
+ __name(extractImportsBatch, "extractImportsBatch");
502
+ function handleImportDeclaration(node, imports) {
503
+ const source = getStringValue(node.source);
504
+ if (!source) {
505
+ return;
506
+ }
507
+ const specifiers = [];
508
+ if (Array.isArray(node.specifiers)) {
509
+ for (const spec of node.specifiers) {
510
+ const s = spec;
511
+ if (s.type === "ImportSpecifier") {
512
+ const imported = s.imported;
513
+ specifiers.push(getIdentifierName(imported) ?? "unknown");
514
+ } else if (s.type === "ImportDefaultSpecifier") {
515
+ specifiers.push("default");
516
+ } else if (s.type === "ImportNamespaceSpecifier") {
517
+ specifiers.push("*");
518
+ }
519
+ }
520
+ }
521
+ imports.push({
522
+ source,
523
+ kind: "static",
524
+ typeOnly: node.importKind === "type" || Boolean(node.isTypeOnly),
525
+ line: node.start != null ? node.start : void 0,
526
+ specifiers
527
+ });
528
+ }
529
+ __name(handleImportDeclaration, "handleImportDeclaration");
530
+ function handleExportDeclaration(node, imports) {
531
+ const source = getStringValue(node.source);
532
+ if (!source) {
533
+ return;
534
+ }
535
+ const specifiers = [];
536
+ if (node.type === "ExportAllDeclaration") {
537
+ specifiers.push("*");
538
+ } else if (Array.isArray(node.specifiers)) {
539
+ for (const spec of node.specifiers) {
540
+ const s = spec;
541
+ const local = s.local;
542
+ specifiers.push(getIdentifierName(local) ?? "unknown");
543
+ }
544
+ }
545
+ imports.push({
546
+ source,
547
+ kind: "re-export",
548
+ typeOnly: node.exportKind === "type" || Boolean(node.isTypeOnly),
549
+ line: node.start != null ? node.start : void 0,
550
+ specifiers
551
+ });
552
+ }
553
+ __name(handleExportDeclaration, "handleExportDeclaration");
554
+ function handleImportExpression(node, imports) {
555
+ const source = getStringValue(node.source);
556
+ if (source) {
557
+ imports.push({
558
+ source,
559
+ kind: "dynamic",
560
+ typeOnly: false,
561
+ line: node.start != null ? node.start : void 0,
562
+ specifiers: []
563
+ });
564
+ }
565
+ }
566
+ __name(handleImportExpression, "handleImportExpression");
567
+ function handleCallExpression(node, imports) {
568
+ const callee = node.callee;
569
+ if (!callee) {
570
+ return;
571
+ }
572
+ if (node.type === "CallExpression" && callee.type === "Import") {
573
+ const args = node.arguments;
574
+ if (args && args.length > 0) {
575
+ const source = getStringValue(args[0]);
576
+ if (source) {
577
+ imports.push({
578
+ source,
579
+ kind: "dynamic",
580
+ typeOnly: false,
581
+ line: node.start != null ? node.start : void 0,
582
+ specifiers: []
583
+ });
584
+ }
585
+ }
586
+ return;
587
+ }
588
+ if (callee.type === "Identifier" && callee.name === "require") {
589
+ const args = node.arguments;
590
+ if (args && args.length > 0) {
591
+ const source = getStringValue(args[0]);
592
+ if (source) {
593
+ imports.push({
594
+ source,
595
+ kind: "require",
596
+ typeOnly: false,
597
+ line: node.start != null ? node.start : void 0,
598
+ specifiers: []
599
+ });
600
+ }
601
+ }
602
+ }
603
+ }
604
+ __name(handleCallExpression, "handleCallExpression");
605
+ function getStringValue(node) {
606
+ if (!node || typeof node !== "object") {
607
+ return void 0;
608
+ }
609
+ const n = node;
610
+ if (n.type === "StringLiteral" || n.type === "Literal") {
611
+ return typeof n.value === "string" ? n.value : void 0;
612
+ }
613
+ return void 0;
614
+ }
615
+ __name(getStringValue, "getStringValue");
616
+ function getIdentifierName(node) {
617
+ if (!node || typeof node !== "object") {
618
+ return void 0;
619
+ }
620
+ const n = node;
621
+ if (n.type === "Identifier" || n.type === "IdentifierName" || n.type === "IdentifierReference") {
622
+ return typeof n.name === "string" ? n.name : void 0;
623
+ }
624
+ return void 0;
625
+ }
626
+ __name(getIdentifierName, "getIdentifierName");
627
+
628
+ // ../../packages/core/dist/analysis/ast/ImportGraphAnalyzer.js
629
+ var THRESHOLDS2 = {
630
+ /** Max files that can import a single file before it's flagged as high fan-in */
631
+ highFanIn: 15,
632
+ /** Max imports a single file can have before it's flagged as high fan-out */
633
+ highFanOut: 20,
634
+ /** Minimum cycle length to report (avoids noise from self-imports) */
635
+ minCycleLength: 2
636
+ };
637
+ var ImportGraphAnalyzer = class {
638
+ static {
639
+ __name(this, "ImportGraphAnalyzer");
640
+ }
641
+ id = "import-graph";
642
+ name = "Import Graph Analysis";
643
+ filePatterns = [
644
+ "*.ts",
645
+ "*.tsx",
646
+ "*.js",
647
+ "*.jsx"
648
+ ];
649
+ async analyze(context) {
650
+ const startTime = performance.now();
651
+ const issues = [];
652
+ let filesAnalyzed = 0;
653
+ const parseErrors = [];
654
+ const extractions = /* @__PURE__ */ new Map();
655
+ for (const [file, content] of context.contents) {
656
+ if (!this.shouldAnalyzeFile(file)) {
657
+ continue;
658
+ }
659
+ filesAnalyzed++;
660
+ const result = extractImports(content, file);
661
+ extractions.set(file, result);
662
+ if (!result.parseSuccess) {
663
+ parseErrors.push(...result.parseErrors);
664
+ }
665
+ }
666
+ const graph = this.buildGraph(extractions, context.workspaceRoot);
667
+ const cycles = this.detectCycles(graph.edges);
668
+ graph.cycles = cycles;
669
+ for (const cycle of cycles) {
670
+ issues.push({
671
+ id: `import-graph/circular/${cycle.join("->")}`,
672
+ severity: "high",
673
+ type: "CIRCULAR_DEPENDENCY",
674
+ message: `Circular dependency: ${cycle.join(" \u2192 ")}`,
675
+ file: cycle[0],
676
+ fix: "Break the cycle by extracting shared code or using dependency injection"
677
+ });
678
+ }
679
+ for (const [file, node] of graph.nodes) {
680
+ if (node.importedBy.length > THRESHOLDS2.highFanIn) {
681
+ issues.push({
682
+ id: `import-graph/high-fan-in/${file}`,
683
+ severity: "medium",
684
+ type: "HIGH_FAN_IN",
685
+ message: `${file} is imported by ${node.importedBy.length} files - changes here have high blast radius`,
686
+ file,
687
+ fix: "Consider splitting into smaller, more focused modules"
688
+ });
689
+ }
690
+ }
691
+ for (const [file, node] of graph.nodes) {
692
+ const runtimeImports = node.imports.filter((imp) => !node.typeOnlyImports.includes(imp));
693
+ if (runtimeImports.length > THRESHOLDS2.highFanOut) {
694
+ issues.push({
695
+ id: `import-graph/high-fan-out/${file}`,
696
+ severity: "low",
697
+ type: "HIGH_FAN_OUT",
698
+ message: `${file} imports ${runtimeImports.length} modules - high coupling`,
699
+ file,
700
+ fix: "Consider using a facade or consolidating related imports"
701
+ });
702
+ }
703
+ }
704
+ for (const [file, node] of graph.nodes) {
705
+ if (node.importedBy.length === 0 && !this.isEntryPoint(file)) {
706
+ issues.push({
707
+ id: `import-graph/orphan/${file}`,
708
+ severity: "info",
709
+ type: "ORPHAN_FILE",
710
+ message: `${file} is not imported by any other analyzed file`,
711
+ file,
712
+ fix: "Verify this file is needed - it may be dead code"
713
+ });
714
+ }
715
+ }
716
+ return {
717
+ analyzer: this.id,
718
+ success: true,
719
+ issues,
720
+ coverage: filesAnalyzed / Math.max(context.files.length, 1),
721
+ duration: performance.now() - startTime,
722
+ metadata: {
723
+ filesAnalyzed,
724
+ nodesVisited: graph.nodes.size,
725
+ patternsChecked: [
726
+ "CIRCULAR_DEPENDENCY",
727
+ "HIGH_FAN_IN",
728
+ "HIGH_FAN_OUT",
729
+ "ORPHAN_FILE"
730
+ ],
731
+ parseErrors
732
+ }
733
+ };
734
+ }
735
+ shouldRun(context) {
736
+ return context.files.some((f) => this.shouldAnalyzeFile(f));
737
+ }
738
+ /**
739
+ * Build the import graph and return it for external consumption.
740
+ * Useful for other tools (momentum scoring, risk propagation, etc.).
741
+ */
742
+ buildGraphFromContext(context) {
743
+ const extractions = /* @__PURE__ */ new Map();
744
+ for (const [file, content] of context.contents) {
745
+ if (this.shouldAnalyzeFile(file)) {
746
+ extractions.set(file, extractImports(content, file));
747
+ }
748
+ }
749
+ const graph = this.buildGraph(extractions, context.workspaceRoot);
750
+ graph.cycles = this.detectCycles(graph.edges);
751
+ return graph;
752
+ }
753
+ // -----------------------------------------------------------------------
754
+ // Graph construction
755
+ // -----------------------------------------------------------------------
756
+ buildGraph(extractions, workspaceRoot) {
757
+ const nodes = /* @__PURE__ */ new Map();
758
+ const edges = /* @__PURE__ */ new Map();
759
+ const reverseEdges = /* @__PURE__ */ new Map();
760
+ for (const filePath of extractions.keys()) {
761
+ const normalized = this.normalizePath(filePath);
762
+ nodes.set(normalized, {
763
+ filePath: normalized,
764
+ imports: [],
765
+ importedBy: [],
766
+ typeOnlyImports: []
767
+ });
768
+ edges.set(normalized, /* @__PURE__ */ new Set());
769
+ }
770
+ for (const [filePath, extraction] of extractions) {
771
+ const normalized = this.normalizePath(filePath);
772
+ for (const imp of extraction.imports) {
773
+ const resolved = this.resolveImport(imp.source, filePath, workspaceRoot);
774
+ if (!resolved) {
775
+ continue;
776
+ }
777
+ const resolvedNorm = this.normalizePath(resolved);
778
+ edges.get(normalized)?.add(resolvedNorm);
779
+ const node = nodes.get(normalized);
780
+ if (node && !node.imports.includes(resolvedNorm)) {
781
+ node.imports.push(resolvedNorm);
782
+ if (imp.typeOnly) {
783
+ node.typeOnlyImports.push(resolvedNorm);
784
+ }
785
+ }
786
+ if (!reverseEdges.has(resolvedNorm)) {
787
+ reverseEdges.set(resolvedNorm, /* @__PURE__ */ new Set());
788
+ }
789
+ reverseEdges.get(resolvedNorm)?.add(normalized);
790
+ if (!nodes.has(resolvedNorm)) {
791
+ nodes.set(resolvedNorm, {
792
+ filePath: resolvedNorm,
793
+ imports: [],
794
+ importedBy: [],
795
+ typeOnlyImports: []
796
+ });
797
+ }
798
+ }
799
+ }
800
+ for (const [file, importers] of reverseEdges) {
801
+ const node = nodes.get(file);
802
+ if (node) {
803
+ node.importedBy = [
804
+ ...importers
805
+ ];
806
+ }
807
+ }
808
+ return {
809
+ nodes,
810
+ edges,
811
+ reverseEdges,
812
+ cycles: []
813
+ };
814
+ }
815
+ // -----------------------------------------------------------------------
816
+ // Cycle detection (Tarjan's SCC adapted for cycles)
817
+ // -----------------------------------------------------------------------
818
+ detectCycles(edges) {
819
+ const cycles = [];
820
+ const visited = /* @__PURE__ */ new Set();
821
+ const inStack = /* @__PURE__ */ new Set();
822
+ const stack = [];
823
+ const dfs = /* @__PURE__ */ __name((node) => {
824
+ if (inStack.has(node)) {
825
+ const cycleStart = stack.indexOf(node);
826
+ if (cycleStart >= 0) {
827
+ const cycle = stack.slice(cycleStart);
828
+ if (cycle.length >= THRESHOLDS2.minCycleLength) {
829
+ cycles.push([
830
+ ...cycle,
831
+ node
832
+ ]);
833
+ }
834
+ }
835
+ return;
836
+ }
837
+ if (visited.has(node)) {
838
+ return;
839
+ }
840
+ visited.add(node);
841
+ inStack.add(node);
842
+ stack.push(node);
843
+ const neighbors = edges.get(node) ?? /* @__PURE__ */ new Set();
844
+ for (const neighbor of neighbors) {
845
+ dfs(neighbor);
846
+ }
847
+ stack.pop();
848
+ inStack.delete(node);
849
+ }, "dfs");
850
+ for (const node of edges.keys()) {
851
+ dfs(node);
852
+ }
853
+ return cycles;
854
+ }
855
+ // -----------------------------------------------------------------------
856
+ // Import resolution
857
+ // -----------------------------------------------------------------------
858
+ resolveImport(importSource, fromFile, _workspaceRoot) {
859
+ if (!importSource.startsWith(".") && !importSource.startsWith("/")) {
860
+ if (importSource.startsWith("@")) {
861
+ const parts = importSource.split("/");
862
+ if (parts.length >= 2) {
863
+ const pkg = parts[1];
864
+ return `packages/${pkg}/src/index.ts`;
865
+ }
866
+ }
867
+ return null;
868
+ }
869
+ const fromDir = dirname(fromFile);
870
+ let resolved = resolve(fromDir, importSource);
871
+ if (!resolved.match(/\.(ts|tsx|js|jsx|mts|cts|mjs|cjs)$/)) {
872
+ resolved += ".ts";
873
+ }
874
+ resolved = resolved.replace(/\.js$/, ".ts").replace(/\.jsx$/, ".tsx");
875
+ return resolved;
876
+ }
877
+ // -----------------------------------------------------------------------
878
+ // Helpers
879
+ // -----------------------------------------------------------------------
880
+ shouldAnalyzeFile(file) {
881
+ const ext = file.split(".").pop()?.toLowerCase();
882
+ return [
883
+ "ts",
884
+ "tsx",
885
+ "js",
886
+ "jsx",
887
+ "mts",
888
+ "cts"
889
+ ].includes(ext ?? "");
890
+ }
891
+ isEntryPoint(file) {
892
+ return file.includes("index.") || file.includes("main.") || file.includes("entry.") || file.includes("server.") || file.includes("app.") || file.endsWith("/page.tsx") || file.endsWith("/layout.tsx") || file.endsWith("/route.ts") || file.includes("__tests__") || file.includes(".test.") || file.includes(".spec.");
893
+ }
894
+ normalizePath(filePath) {
895
+ return filePath.replace(/\\/g, "/").replace(/^\.\//, "");
896
+ }
897
+ };
898
+ var SyntaxAnalyzer = class {
899
+ static {
900
+ __name(this, "SyntaxAnalyzer");
901
+ }
902
+ id = "syntax";
903
+ name = "Syntax Analysis";
904
+ filePatterns = [
905
+ "*.ts",
906
+ "*.tsx",
907
+ "*.js",
908
+ "*.jsx"
909
+ ];
910
+ async analyze(context) {
911
+ const startTime = performance.now();
912
+ const issues = [];
913
+ let filesAnalyzed = 0;
914
+ let nodesVisited = 0;
915
+ const parseErrors = [];
916
+ for (const [file, content] of context.contents) {
917
+ if (!this.shouldAnalyzeFile(file)) {
918
+ continue;
919
+ }
920
+ filesAnalyzed++;
921
+ try {
922
+ const ast = eslintParser.parse(content, {
923
+ sourceType: "module",
924
+ ecmaFeatures: {
925
+ jsx: file.endsWith(".tsx") || file.endsWith(".jsx")
926
+ },
927
+ ecmaVersion: "latest",
928
+ // Error recovery mode to get partial AST even with errors
929
+ errorOnUnknownASTType: false
930
+ });
931
+ nodesVisited += this.countNodes(ast);
932
+ this.checkSyntaxPatterns(content, file, issues);
933
+ } catch (error) {
934
+ const parseError = this.extractParseError(error);
935
+ parseErrors.push(`${file}: ${parseError.message}`);
936
+ issues.push({
937
+ id: `syntax/parse-error/${file}/${parseError.line}`,
938
+ severity: "critical",
939
+ type: "SYNTAX_ERROR",
940
+ message: parseError.message,
941
+ file,
942
+ line: parseError.line,
943
+ column: parseError.column,
944
+ fix: "Fix the syntax error to allow parsing"
945
+ });
946
+ }
947
+ }
948
+ return {
949
+ analyzer: this.id,
950
+ success: true,
951
+ issues,
952
+ coverage: filesAnalyzed / Math.max(context.files.length, 1),
953
+ duration: performance.now() - startTime,
954
+ metadata: {
955
+ filesAnalyzed,
956
+ nodesVisited,
957
+ parseErrors
958
+ }
959
+ };
960
+ }
961
+ shouldRun(context) {
962
+ return context.files.some((f) => this.shouldAnalyzeFile(f));
963
+ }
964
+ shouldAnalyzeFile(file) {
965
+ const ext = file.split(".").pop()?.toLowerCase();
966
+ return [
967
+ "ts",
968
+ "tsx",
969
+ "js",
970
+ "jsx"
971
+ ].includes(ext || "");
972
+ }
973
+ /**
974
+ * Extract parse error information from parser exception
975
+ */
976
+ extractParseError(error) {
977
+ if (error instanceof Error) {
978
+ const match = error.message.match(/\((\d+):(\d+)\)/);
979
+ if (match) {
980
+ return {
981
+ message: error.message,
982
+ line: Number.parseInt(match[1], 10),
983
+ column: Number.parseInt(match[2], 10)
984
+ };
985
+ }
986
+ return {
987
+ message: error.message,
988
+ line: 1,
989
+ column: 1
990
+ };
991
+ }
992
+ return {
993
+ message: String(error),
994
+ line: 1,
995
+ column: 1
996
+ };
997
+ }
998
+ /**
999
+ * Count AST nodes for coverage metrics
1000
+ */
1001
+ countNodes(node) {
1002
+ if (!node || typeof node !== "object") {
1003
+ return 0;
1004
+ }
1005
+ let count = 1;
1006
+ for (const key of Object.keys(node)) {
1007
+ const value = node[key];
1008
+ if (Array.isArray(value)) {
1009
+ for (const item of value) {
1010
+ count += this.countNodes(item);
1011
+ }
1012
+ } else if (value && typeof value === "object" && "type" in value) {
1013
+ count += this.countNodes(value);
1014
+ }
1015
+ }
1016
+ return count;
1017
+ }
1018
+ /**
1019
+ * Check for additional syntax patterns that may indicate issues
1020
+ */
1021
+ checkSyntaxPatterns(content, file, issues) {
1022
+ const lines = content.split("\n");
1023
+ for (let i = 0; i < lines.length; i++) {
1024
+ const line = lines[i];
1025
+ const lineNum = i + 1;
1026
+ if (line.includes(";;")) {
1027
+ issues.push({
1028
+ id: `syntax/double-semicolon/${file}/${lineNum}`,
1029
+ severity: "low",
1030
+ type: "SYNTAX_WARNING",
1031
+ message: "Double semicolon detected",
1032
+ file,
1033
+ line: lineNum,
1034
+ column: line.indexOf(";;") + 1,
1035
+ fix: "Remove extra semicolon",
1036
+ snippet: line.trim()
1037
+ });
1038
+ }
1039
+ if (/console\.assert\([^,]+,\s*\)/.test(line)) {
1040
+ issues.push({
1041
+ id: `syntax/empty-assert/${file}/${lineNum}`,
1042
+ severity: "medium",
1043
+ type: "SYNTAX_WARNING",
1044
+ message: "console.assert with empty message",
1045
+ file,
1046
+ line: lineNum,
1047
+ fix: "Add assertion message for debugging",
1048
+ snippet: line.trim()
1049
+ });
1050
+ }
1051
+ if (/if\s*\([^=]*=\s*[^=]/.test(line) && !/if\s*\([^=]*[=!]==/.test(line)) {
1052
+ const assignMatch = line.match(/if\s*\(\s*(\w+)\s*=\s*[^=]/);
1053
+ if (assignMatch) {
1054
+ issues.push({
1055
+ id: `syntax/assignment-in-condition/${file}/${lineNum}`,
1056
+ severity: "medium",
1057
+ type: "SYNTAX_WARNING",
1058
+ message: "Possible assignment in condition (did you mean ===?)",
1059
+ file,
1060
+ line: lineNum,
1061
+ fix: "Use === for comparison, or wrap in extra parentheses if intentional",
1062
+ snippet: line.trim()
1063
+ });
1064
+ }
1065
+ }
1066
+ }
1067
+ }
1068
+ };
1069
+ var CompletenessAnalyzer = class {
1070
+ static {
1071
+ __name(this, "CompletenessAnalyzer");
1072
+ }
1073
+ id = "completeness";
1074
+ name = "Completeness Detection";
1075
+ filePatterns = [
1076
+ "*.ts",
1077
+ "*.tsx",
1078
+ "*.js",
1079
+ "*.jsx"
1080
+ ];
1081
+ todoPatterns = [
1082
+ /\/\/\s*TODO\b/gi,
1083
+ /\/\/\s*FIXME\b/gi,
1084
+ /\/\/\s*XXX\b/gi,
1085
+ /\/\/\s*HACK\b/gi,
1086
+ /\/\*\s*TODO\b/gi,
1087
+ /\/\*\s*FIXME\b/gi
1088
+ ];
1089
+ placeholderPatterns = [
1090
+ /throw\s+new\s+Error\s*\(\s*['"`].*not\s*implemented.*['"`]\s*\)/gi,
1091
+ /throw\s+new\s+Error\s*\(\s*['"`]TODO.*['"`]\s*\)/gi,
1092
+ /NotImplementedError/gi,
1093
+ /throw\s+new\s+Error\s*\(\s*['"`]STUB['"`]\s*\)/gi
1094
+ ];
1095
+ parserOptions = {
1096
+ sourceType: "module",
1097
+ plugins: [
1098
+ "typescript",
1099
+ "jsx"
1100
+ ],
1101
+ errorRecovery: true
1102
+ };
1103
+ async analyze(context) {
1104
+ const startTime = performance.now();
1105
+ const issues = [];
1106
+ let filesAnalyzed = 0;
1107
+ let nodesVisited = 0;
1108
+ const parseErrors = [];
1109
+ for (const [file, content] of context.contents) {
1110
+ if (!this.shouldAnalyzeFile(file)) {
1111
+ continue;
1112
+ }
1113
+ filesAnalyzed++;
1114
+ this.checkTodoComments(content, file, issues);
1115
+ this.checkPlaceholderPatterns(content, file, issues);
1116
+ try {
1117
+ const ast = parse(content, {
1118
+ ...this.parserOptions,
1119
+ plugins: this.getPluginsForFile(file)
1120
+ });
1121
+ const result = this.analyzeAST(ast, content, file);
1122
+ issues.push(...result.issues);
1123
+ nodesVisited += result.nodesVisited;
1124
+ } catch (error) {
1125
+ parseErrors.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);
1126
+ }
1127
+ }
1128
+ return {
1129
+ analyzer: this.id,
1130
+ success: true,
1131
+ issues,
1132
+ coverage: filesAnalyzed / Math.max(context.files.length, 1),
1133
+ duration: performance.now() - startTime,
1134
+ metadata: {
1135
+ filesAnalyzed,
1136
+ nodesVisited,
1137
+ patternsChecked: [
1138
+ "TODO",
1139
+ "FIXME",
1140
+ "EMPTY_CATCH",
1141
+ "EMPTY_FUNCTION",
1142
+ "NOT_IMPLEMENTED",
1143
+ "PLACEHOLDER"
1144
+ ],
1145
+ parseErrors
1146
+ }
1147
+ };
1148
+ }
1149
+ shouldRun(context) {
1150
+ return context.files.some((f) => this.shouldAnalyzeFile(f));
1151
+ }
1152
+ shouldAnalyzeFile(file) {
1153
+ const ext = file.split(".").pop()?.toLowerCase();
1154
+ return [
1155
+ "ts",
1156
+ "tsx",
1157
+ "js",
1158
+ "jsx"
1159
+ ].includes(ext || "");
1160
+ }
1161
+ getPluginsForFile(file) {
1162
+ const plugins = [
1163
+ "typescript"
1164
+ ];
1165
+ if (file.endsWith(".tsx") || file.endsWith(".jsx")) {
1166
+ plugins.push("jsx");
1167
+ }
1168
+ return plugins;
1169
+ }
1170
+ /**
1171
+ * Check for TODO/FIXME comments // Issue: LIN-0000
1172
+ */
1173
+ checkTodoComments(content, file, issues) {
1174
+ const lines = content.split("\n");
1175
+ for (let i = 0; i < lines.length; i++) {
1176
+ const line = lines[i];
1177
+ const lineNum = i + 1;
1178
+ for (const pattern of this.todoPatterns) {
1179
+ pattern.lastIndex = 0;
1180
+ if (pattern.test(line)) {
1181
+ const todoContent = line.trim().slice(0, 100);
1182
+ issues.push({
1183
+ id: `completeness/todo/${file}/${lineNum}`,
1184
+ severity: "medium",
1185
+ type: "INCOMPLETE_IMPLEMENTATION",
1186
+ message: `TODO/FIXME: ${todoContent}`,
1187
+ file,
1188
+ line: lineNum,
1189
+ snippet: todoContent
1190
+ });
1191
+ break;
1192
+ }
1193
+ }
1194
+ }
1195
+ }
1196
+ /**
1197
+ * Check for placeholder/stub patterns
1198
+ */
1199
+ checkPlaceholderPatterns(content, file, issues) {
1200
+ const lines = content.split("\n");
1201
+ for (let i = 0; i < lines.length; i++) {
1202
+ const line = lines[i];
1203
+ const lineNum = i + 1;
1204
+ for (const pattern of this.placeholderPatterns) {
1205
+ pattern.lastIndex = 0;
1206
+ if (pattern.test(line)) {
1207
+ issues.push({
1208
+ id: `completeness/placeholder/${file}/${lineNum}`,
1209
+ severity: "high",
1210
+ type: "INCOMPLETE_IMPLEMENTATION",
1211
+ message: 'Placeholder implementation: "not implemented" or similar',
1212
+ file,
1213
+ line: lineNum,
1214
+ fix: "Implement the functionality or remove the placeholder",
1215
+ snippet: line.trim().slice(0, 100)
1216
+ });
1217
+ break;
1218
+ }
1219
+ }
1220
+ }
1221
+ }
1222
+ /**
1223
+ * AST-based detection of empty/incomplete code
1224
+ */
1225
+ analyzeAST(ast, _content, file) {
1226
+ const issues = [];
1227
+ let nodesVisited = 0;
1228
+ traverse(ast, {
1229
+ enter() {
1230
+ nodesVisited++;
1231
+ },
1232
+ // Empty catch blocks
1233
+ CatchClause: /* @__PURE__ */ __name((path) => {
1234
+ const body = path.node.body;
1235
+ if (body.body.length === 0) {
1236
+ issues.push({
1237
+ id: `completeness/empty-catch/${file}/${path.node.loc?.start.line}`,
1238
+ severity: "medium",
1239
+ type: "INCOMPLETE_IMPLEMENTATION",
1240
+ message: "Empty catch block - errors silently swallowed",
1241
+ file,
1242
+ line: path.node.loc?.start.line,
1243
+ fix: "Add error handling, rethrow, or log the error"
1244
+ });
1245
+ } else if (body.body.length === 1) {
1246
+ const stmt = body.body[0];
1247
+ if (stmt.type === "EmptyStatement") {
1248
+ issues.push({
1249
+ id: `completeness/empty-catch/${file}/${path.node.loc?.start.line}`,
1250
+ severity: "medium",
1251
+ type: "INCOMPLETE_IMPLEMENTATION",
1252
+ message: "Catch block contains only empty statement",
1253
+ file,
1254
+ line: path.node.loc?.start.line,
1255
+ fix: "Add proper error handling"
1256
+ });
1257
+ }
1258
+ }
1259
+ }, "CatchClause"),
1260
+ // Empty function bodies (excluding type declarations and interface methods)
1261
+ FunctionDeclaration: /* @__PURE__ */ __name((path) => {
1262
+ if (path.node.body.body.length === 0) {
1263
+ const funcName = path.node.id?.name || "anonymous";
1264
+ {
1265
+ issues.push({
1266
+ id: `completeness/empty-fn/${file}/${path.node.loc?.start.line}`,
1267
+ severity: "medium",
1268
+ type: "INCOMPLETE_IMPLEMENTATION",
1269
+ message: `Empty function body: ${funcName}()`,
1270
+ file,
1271
+ line: path.node.loc?.start.line,
1272
+ fix: "Implement the function or mark as abstract/stub if intentional"
1273
+ });
1274
+ }
1275
+ }
1276
+ }, "FunctionDeclaration"),
1277
+ // Empty method bodies
1278
+ ClassMethod: /* @__PURE__ */ __name((path) => {
1279
+ if (path.node.abstract) {
1280
+ return;
1281
+ }
1282
+ if (path.node.kind === "get" || path.node.kind === "set") {
1283
+ return;
1284
+ }
1285
+ const body = path.node.body;
1286
+ if (body && body.body.length === 0) {
1287
+ const methodName = path.node.key.type === "Identifier" ? path.node.key.name : "anonymous";
1288
+ if (methodName === "constructor") {
1289
+ return;
1290
+ }
1291
+ issues.push({
1292
+ id: `completeness/empty-method/${file}/${path.node.loc?.start.line}`,
1293
+ severity: "medium",
1294
+ type: "INCOMPLETE_IMPLEMENTATION",
1295
+ message: `Empty method body: ${methodName}()`,
1296
+ file,
1297
+ line: path.node.loc?.start.line,
1298
+ fix: "Implement the method or mark as abstract if intentional"
1299
+ });
1300
+ }
1301
+ }, "ClassMethod"),
1302
+ // Arrow functions that just throw or are empty (might be intentional)
1303
+ ArrowFunctionExpression: /* @__PURE__ */ __name((path) => {
1304
+ const body = path.node.body;
1305
+ if (body.type === "BlockStatement" && body.body.length === 0) {
1306
+ const parent = path.parent;
1307
+ if (parent.type === "VariableDeclarator") {
1308
+ const varName = parent.id.type === "Identifier" ? parent.id.name : "anonymous";
1309
+ issues.push({
1310
+ id: `completeness/empty-arrow/${file}/${path.node.loc?.start.line}`,
1311
+ severity: "low",
1312
+ type: "INCOMPLETE_IMPLEMENTATION",
1313
+ message: `Empty arrow function: ${varName}`,
1314
+ file,
1315
+ line: path.node.loc?.start.line,
1316
+ fix: "Implement the function or use () => { /* intentionally empty */ } if intentionally empty"
1317
+ });
1318
+ }
1319
+ }
1320
+ }, "ArrowFunctionExpression"),
1321
+ // Check for structured-log that might be debug code
1322
+ CallExpression: /* @__PURE__ */ __name((path) => {
1323
+ const callee = path.node.callee;
1324
+ if (callee.type === "MemberExpression" && callee.object.type === "Identifier" && callee.object.name === "console" && callee.property.type === "Identifier" && callee.property.name === "log") {
1325
+ const firstArg = path.node.arguments[0];
1326
+ if (firstArg && firstArg.type === "StringLiteral") {
1327
+ const msg = firstArg.value.toLowerCase();
1328
+ if (msg.includes("debug") || msg.includes("test") || msg.includes("todo") || msg.includes("remove")) {
1329
+ issues.push({
1330
+ id: `completeness/debug-log/${file}/${path.node.loc?.start.line}`,
1331
+ severity: "low",
1332
+ type: "DEBUG_CODE",
1333
+ message: `Debug process.stdout.write left in code: "${firstArg.value.slice(0, 50)}"`,
1334
+ file,
1335
+ line: path.node.loc?.start.line,
1336
+ fix: "Remove debug logging before commit"
1337
+ });
1338
+ }
1339
+ }
1340
+ }
1341
+ }, "CallExpression")
1342
+ });
1343
+ return {
1344
+ issues,
1345
+ nodesVisited
1346
+ };
1347
+ }
1348
+ };
1349
+ var EXPORT_PATTERNS = [
1350
+ /export\s+(const|function|class|interface|type|enum)\s+(\w+)/g,
1351
+ /export\s+default\s+(function|class)?\s*(\w+)?/g,
1352
+ /export\s+\{([^}]+)\}/g
1353
+ ];
1354
+ var PERFORMANCE_PATTERNS = [
1355
+ {
1356
+ pattern: /\.forEach\s*\(/g,
1357
+ type: "computation",
1358
+ risk: "low"
1359
+ },
1360
+ {
1361
+ pattern: /for\s*\(\s*let\s+\w+\s*=\s*0/g,
1362
+ type: "computation",
1363
+ risk: "low"
1364
+ },
1365
+ {
1366
+ pattern: /while\s*\(/g,
1367
+ type: "computation",
1368
+ risk: "medium"
1369
+ },
1370
+ {
1371
+ pattern: /async\s+function|await\s+/g,
1372
+ type: "io",
1373
+ risk: "medium"
1374
+ },
1375
+ {
1376
+ pattern: /new\s+(Map|Set|Array)\s*\(/g,
1377
+ type: "memory",
1378
+ risk: "low"
1379
+ },
1380
+ {
1381
+ pattern: /JSON\.(parse|stringify)/g,
1382
+ type: "computation",
1383
+ risk: "medium"
1384
+ },
1385
+ {
1386
+ pattern: /readFileSync|writeFileSync/g,
1387
+ type: "io",
1388
+ risk: "high"
1389
+ },
1390
+ {
1391
+ pattern: /spawn|exec\s*\(/g,
1392
+ type: "io",
1393
+ risk: "high"
1394
+ },
1395
+ {
1396
+ pattern: /import\s*\(/g,
1397
+ type: "bundle",
1398
+ risk: "low"
1399
+ },
1400
+ {
1401
+ pattern: /require\s*\(/g,
1402
+ type: "bundle",
1403
+ risk: "medium"
1404
+ }
1405
+ ];
1406
+ var TEST_FILE_PATTERNS = [
1407
+ /\.test\.[tj]sx?$/,
1408
+ /\.spec\.[tj]sx?$/,
1409
+ /__tests__\//,
1410
+ /test\//,
1411
+ /tests\//
1412
+ ];
1413
+ var ChangeImpactAnalyzer = class {
1414
+ static {
1415
+ __name(this, "ChangeImpactAnalyzer");
1416
+ }
1417
+ id = "change-impact";
1418
+ name = "Change Impact Analyzer";
1419
+ filePatterns = [
1420
+ "**/*.ts",
1421
+ "**/*.tsx",
1422
+ "**/*.js",
1423
+ "**/*.jsx"
1424
+ ];
1425
+ workspaceRoot;
1426
+ dependencyGraph = /* @__PURE__ */ new Map();
1427
+ reverseDependencyGraph = /* @__PURE__ */ new Map();
1428
+ constructor(workspaceRoot) {
1429
+ this.workspaceRoot = workspaceRoot;
1430
+ }
1431
+ /**
1432
+ * Check if this analyzer should run
1433
+ */
1434
+ shouldRun(context) {
1435
+ return context.files.some((f) => this.filePatterns.some((p) => new RegExp(p.replace(/\*/g, ".*")).test(f)));
1436
+ }
1437
+ /**
1438
+ * Run impact analysis
1439
+ */
1440
+ async analyze(context) {
1441
+ const start = Date.now();
1442
+ const issues = [];
1443
+ try {
1444
+ await this.buildDependencyGraph(context);
1445
+ for (const file of context.files) {
1446
+ const content = context.contents.get(file);
1447
+ if (!content) {
1448
+ continue;
1449
+ }
1450
+ const breakingChanges = this.detectBreakingChanges(content, file);
1451
+ for (const bc of breakingChanges) {
1452
+ issues.push({
1453
+ id: `impact/breaking/${bc.type}/${file}/${bc.symbol}`,
1454
+ severity: bc.severity,
1455
+ type: `BREAKING_${bc.type.toUpperCase()}`,
1456
+ message: bc.description,
1457
+ file,
1458
+ fix: bc.migration
1459
+ });
1460
+ }
1461
+ const perfImpacts = this.detectPerformanceImpacts(content, file);
1462
+ for (const pi of perfImpacts) {
1463
+ if (pi.risk === "high" || pi.risk === "critical") {
1464
+ issues.push({
1465
+ id: `impact/perf/${pi.type}/${file}/${pi.component}`,
1466
+ severity: pi.risk === "critical" ? "critical" : "high",
1467
+ type: `PERF_${pi.type.toUpperCase()}`,
1468
+ message: pi.description,
1469
+ file,
1470
+ fix: pi.recommendation
1471
+ });
1472
+ }
1473
+ }
1474
+ const affectedTests = this.findAffectedTests(file);
1475
+ if (affectedTests.length > 5) {
1476
+ issues.push({
1477
+ id: `impact/tests/${file}`,
1478
+ severity: "medium",
1479
+ type: "HIGH_TEST_IMPACT",
1480
+ message: `Change affects ${affectedTests.length} test files - consider running full test suite`,
1481
+ file
1482
+ });
1483
+ }
1484
+ }
1485
+ return {
1486
+ analyzer: this.id,
1487
+ success: true,
1488
+ issues,
1489
+ coverage: 1,
1490
+ duration: Date.now() - start,
1491
+ metadata: {
1492
+ filesAnalyzed: context.files.length
1493
+ }
1494
+ };
1495
+ } catch (error) {
1496
+ return {
1497
+ analyzer: this.id,
1498
+ success: false,
1499
+ issues: [
1500
+ {
1501
+ id: "impact/error",
1502
+ severity: "high",
1503
+ type: "ANALYSIS_ERROR",
1504
+ message: error instanceof Error ? error.message : String(error)
1505
+ }
1506
+ ],
1507
+ coverage: 0,
1508
+ duration: Date.now() - start
1509
+ };
1510
+ }
1511
+ }
1512
+ /**
1513
+ * Get full impact analysis (more detailed than standard analyze)
1514
+ */
1515
+ async getFullImpact(files, contents) {
1516
+ const start = Date.now();
1517
+ const context = {
1518
+ workspaceRoot: this.workspaceRoot,
1519
+ files,
1520
+ contents
1521
+ };
1522
+ await this.buildDependencyGraph(context);
1523
+ const affectedTests = [];
1524
+ const breakingChanges = [];
1525
+ const performanceImpacts = [];
1526
+ const dependentFiles = [];
1527
+ const recommendations = [];
1528
+ for (const file of files) {
1529
+ const content = contents.get(file) || "";
1530
+ const tests = this.findAffectedTests(file);
1531
+ affectedTests.push(...tests);
1532
+ const breaks = this.detectBreakingChanges(content, file);
1533
+ breakingChanges.push(...breaks);
1534
+ const perfs = this.detectPerformanceImpacts(content, file);
1535
+ performanceImpacts.push(...perfs);
1536
+ const deps = this.findDependentFiles(file);
1537
+ dependentFiles.push(...deps);
1538
+ }
1539
+ const impactScore = this.calculateImpactScore(affectedTests, breakingChanges, performanceImpacts, dependentFiles);
1540
+ if (breakingChanges.length > 0) {
1541
+ recommendations.push(`\u26A0\uFE0F ${breakingChanges.length} breaking change(s) detected - update dependent code`);
1542
+ }
1543
+ if (affectedTests.length > 10) {
1544
+ recommendations.push(`\u{1F9EA} Run full test suite - ${affectedTests.length} tests potentially affected`);
1545
+ }
1546
+ if (performanceImpacts.some((p) => p.risk === "high" || p.risk === "critical")) {
1547
+ recommendations.push("\u26A1 Performance-sensitive code modified - run benchmarks");
1548
+ }
1549
+ if (dependentFiles.length > 20) {
1550
+ recommendations.push("\u{1F517} High ripple effect - consider incremental rollout");
1551
+ }
1552
+ return {
1553
+ filesAnalyzed: files.length,
1554
+ affectedTests: this.dedupeItems(affectedTests),
1555
+ breakingChanges,
1556
+ performanceImpacts,
1557
+ dependentFiles: this.dedupeItems(dependentFiles),
1558
+ impactScore,
1559
+ recommendations,
1560
+ duration: Date.now() - start
1561
+ };
1562
+ }
1563
+ // =========================================================================
1564
+ // Private Methods
1565
+ // =========================================================================
1566
+ /**
1567
+ * Build dependency graph from file contents
1568
+ */
1569
+ async buildDependencyGraph(context) {
1570
+ this.dependencyGraph.clear();
1571
+ this.reverseDependencyGraph.clear();
1572
+ for (const file of context.files) {
1573
+ const content = context.contents.get(file);
1574
+ if (!content) {
1575
+ continue;
1576
+ }
1577
+ const imports = this.extractImports(content, file);
1578
+ this.dependencyGraph.set(file, imports);
1579
+ for (const imp of imports) {
1580
+ const existing = this.reverseDependencyGraph.get(imp) || [];
1581
+ existing.push(file);
1582
+ this.reverseDependencyGraph.set(imp, existing);
1583
+ }
1584
+ }
1585
+ }
1586
+ /**
1587
+ * Extract import statements from file content using AST analysis.
1588
+ *
1589
+ * UPGRADED (11b): Replaces regex-based import extraction with proper AST walking
1590
+ * via packages/core/src/analysis/ast/import-extractor.ts (oxc-parser based).
1591
+ * This eliminates false positives from imports in strings/comments and correctly
1592
+ * handles dynamic imports, re-exports, and type-only imports.
1593
+ */
1594
+ extractImports(content, fromFile) {
1595
+ const rawSources = extractImportSources(content, fromFile);
1596
+ const imports = [];
1597
+ for (const source of rawSources) {
1598
+ const importPath = this.resolveImportPath(source, fromFile);
1599
+ if (importPath) {
1600
+ imports.push(importPath);
1601
+ }
1602
+ }
1603
+ return imports;
1604
+ }
1605
+ /**
1606
+ * Resolve import path to absolute file path
1607
+ */
1608
+ resolveImportPath(importPath, fromFile) {
1609
+ if (!importPath.startsWith(".") && !importPath.startsWith("/")) {
1610
+ return null;
1611
+ }
1612
+ const dir = dirname(fromFile);
1613
+ const normalized = `${dir}/${importPath}`.replace(/\/\.\//g, "/");
1614
+ if (/\.[cm]?[jt]sx?$/.test(importPath)) {
1615
+ return normalized;
1616
+ }
1617
+ return `${normalized}.ts`;
1618
+ }
1619
+ /**
1620
+ * Find test files that might be affected by a change
1621
+ */
1622
+ findAffectedTests(file) {
1623
+ const tests = [];
1624
+ const relPath = relative(this.workspaceRoot, file);
1625
+ const fileName = basename(file).replace(/\.[tj]sx?$/, "");
1626
+ const directTestPatterns = [
1627
+ `${fileName}.test.ts`,
1628
+ `${fileName}.test.tsx`,
1629
+ `${fileName}.spec.ts`,
1630
+ `${fileName}.spec.tsx`,
1631
+ `__tests__/${fileName}.test.ts`,
1632
+ `__tests__/${fileName}.test.tsx`
1633
+ ];
1634
+ for (const pattern of directTestPatterns) {
1635
+ tests.push({
1636
+ path: pattern,
1637
+ reason: "Direct test file for changed source",
1638
+ level: "high"
1639
+ });
1640
+ }
1641
+ const importers = this.reverseDependencyGraph.get(file) || [];
1642
+ for (const importer of importers) {
1643
+ if (this.isTestFile(importer)) {
1644
+ tests.push({
1645
+ path: relative(this.workspaceRoot, importer),
1646
+ reason: "Test file imports changed module",
1647
+ level: "medium"
1648
+ });
1649
+ }
1650
+ }
1651
+ if (relPath.includes("/core/") || relPath.includes("/services/")) {
1652
+ tests.push({
1653
+ path: "**/*.integration.test.ts",
1654
+ reason: "Core module change may affect integration tests",
1655
+ level: "low"
1656
+ });
1657
+ }
1658
+ return tests;
1659
+ }
1660
+ /**
1661
+ * Check if a file is a test file
1662
+ */
1663
+ isTestFile(file) {
1664
+ return TEST_FILE_PATTERNS.some((p) => p.test(file));
1665
+ }
1666
+ /**
1667
+ * Detect breaking changes in content
1668
+ */
1669
+ detectBreakingChanges(content, file) {
1670
+ const breaks = [];
1671
+ for (const pattern of EXPORT_PATTERNS) {
1672
+ const regex = new RegExp(pattern.source, pattern.flags);
1673
+ let match2;
1674
+ while ((match2 = regex.exec(content)) !== null) {
1675
+ const symbolName = match2[2] || match2[1];
1676
+ if (symbolName) {
1677
+ breaks.push({
1678
+ type: "export",
1679
+ symbol: symbolName,
1680
+ file,
1681
+ description: `Exported symbol '${symbolName}' may have changed`,
1682
+ severity: "medium",
1683
+ migration: `Verify consumers of '${symbolName}' are updated`
1684
+ });
1685
+ }
1686
+ }
1687
+ }
1688
+ const interfaceRegex = /(?:export\s+)?interface\s+(\w+)\s*\{([^}]+)\}/g;
1689
+ let match;
1690
+ while ((match = interfaceRegex.exec(content)) !== null) {
1691
+ const interfaceName = match[1];
1692
+ const body = match[2];
1693
+ if (body.includes("?:") || body.includes(": ")) {
1694
+ breaks.push({
1695
+ type: "type",
1696
+ symbol: interfaceName,
1697
+ file,
1698
+ description: `Interface '${interfaceName}' definition changed`,
1699
+ severity: "medium"
1700
+ });
1701
+ }
1702
+ }
1703
+ return breaks;
1704
+ }
1705
+ /**
1706
+ * Detect performance-sensitive code changes
1707
+ */
1708
+ detectPerformanceImpacts(content, file) {
1709
+ const impacts = [];
1710
+ for (const { pattern, type, risk } of PERFORMANCE_PATTERNS) {
1711
+ const regex = new RegExp(pattern.source, pattern.flags);
1712
+ let match;
1713
+ while ((match = regex.exec(content)) !== null) {
1714
+ impacts.push({
1715
+ type,
1716
+ description: `${type} operation detected: ${match[0]}`,
1717
+ risk,
1718
+ component: basename(file),
1719
+ recommendation: this.getPerformanceRecommendation(type)
1720
+ });
1721
+ }
1722
+ }
1723
+ return impacts;
1724
+ }
1725
+ /**
1726
+ * Get recommendation for performance issue type
1727
+ */
1728
+ getPerformanceRecommendation(type) {
1729
+ switch (type) {
1730
+ case "hotpath":
1731
+ return "Consider memoization or caching for hot paths";
1732
+ case "memory":
1733
+ return "Monitor memory usage, consider object pooling";
1734
+ case "io":
1735
+ return "Use async operations, consider batching";
1736
+ case "computation":
1737
+ return "Profile for bottlenecks, consider Web Workers";
1738
+ case "bundle":
1739
+ return "Use dynamic imports for code splitting";
1740
+ default:
1741
+ return "Profile before optimizing";
1742
+ }
1743
+ }
1744
+ /**
1745
+ * Find files that depend on changed file
1746
+ */
1747
+ findDependentFiles(file) {
1748
+ const dependents = [];
1749
+ const visited = /* @__PURE__ */ new Set();
1750
+ const traverse3 = /* @__PURE__ */ __name((current, depth) => {
1751
+ if (visited.has(current) || depth > 3) {
1752
+ return;
1753
+ }
1754
+ visited.add(current);
1755
+ const importers = this.reverseDependencyGraph.get(current) || [];
1756
+ for (const importer of importers) {
1757
+ dependents.push({
1758
+ path: relative(this.workspaceRoot, importer),
1759
+ reason: depth === 0 ? "Directly imports changed file" : `Transitive dependency (depth ${depth})`,
1760
+ level: depth === 0 ? "high" : depth === 1 ? "medium" : "low"
1761
+ });
1762
+ traverse3(importer, depth + 1);
1763
+ }
1764
+ }, "traverse");
1765
+ traverse3(file, 0);
1766
+ return dependents;
1767
+ }
1768
+ /**
1769
+ * Calculate overall impact score
1770
+ */
1771
+ calculateImpactScore(tests, breaks, perfs, deps) {
1772
+ let score = 0;
1773
+ score += Math.min(tests.length * 0.05, 0.25);
1774
+ score += Math.min(breaks.length * 0.15, 0.35);
1775
+ score += Math.min(perfs.filter((p) => p.risk === "high").length * 0.1, 0.2);
1776
+ score += Math.min(deps.length * 0.02, 0.2);
1777
+ return Math.min(score, 1);
1778
+ }
1779
+ /**
1780
+ * Deduplicate impact items
1781
+ */
1782
+ dedupeItems(items) {
1783
+ const seen = /* @__PURE__ */ new Set();
1784
+ return items.filter((item) => {
1785
+ if (seen.has(item.path)) {
1786
+ return false;
1787
+ }
1788
+ seen.add(item.path);
1789
+ return true;
1790
+ });
1791
+ }
1792
+ };
1793
+ function createChangeImpactAnalyzer(workspaceRoot) {
1794
+ return new ChangeImpactAnalyzer(workspaceRoot);
1795
+ }
1796
+ __name(createChangeImpactAnalyzer, "createChangeImpactAnalyzer");
1797
+ var SecurityAnalyzer = class {
1798
+ static {
1799
+ __name(this, "SecurityAnalyzer");
1800
+ }
1801
+ id = "security";
1802
+ name = "Security Analysis";
1803
+ filePatterns = [
1804
+ "*.ts",
1805
+ "*.tsx",
1806
+ "*.js",
1807
+ "*.jsx"
1808
+ ];
1809
+ parserOptions = {
1810
+ sourceType: "module",
1811
+ plugins: [
1812
+ "typescript",
1813
+ "jsx"
1814
+ ],
1815
+ errorRecovery: true
1816
+ };
1817
+ async analyze(context) {
1818
+ const startTime = performance.now();
1819
+ const issues = [];
1820
+ let filesAnalyzed = 0;
1821
+ let nodesVisited = 0;
1822
+ const parseErrors = [];
1823
+ for (const [file, content] of context.contents) {
1824
+ if (!this.shouldAnalyzeFile(file)) {
1825
+ continue;
1826
+ }
1827
+ filesAnalyzed++;
1828
+ try {
1829
+ const ast = parse(content, {
1830
+ ...this.parserOptions,
1831
+ plugins: this.getPluginsForFile(file)
1832
+ });
1833
+ const fileIssues = this.analyzeAST(ast, content, file);
1834
+ issues.push(...fileIssues.issues);
1835
+ nodesVisited += fileIssues.nodesVisited;
1836
+ } catch (error) {
1837
+ parseErrors.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);
1838
+ issues.push({
1839
+ id: `security/parse-error/${file}`,
1840
+ severity: "info",
1841
+ type: "PARSE_ERROR",
1842
+ message: `Could not parse for security analysis: ${error instanceof Error ? error.message : String(error)}`,
1843
+ file
1844
+ });
1845
+ }
1846
+ }
1847
+ return {
1848
+ analyzer: this.id,
1849
+ success: true,
1850
+ issues,
1851
+ coverage: filesAnalyzed / Math.max(context.files.length, 1),
1852
+ duration: performance.now() - startTime,
1853
+ metadata: {
1854
+ filesAnalyzed,
1855
+ nodesVisited,
1856
+ patternsChecked: [
1857
+ "UNSAFE_EVAL",
1858
+ "PATH_TRAVERSAL",
1859
+ "MISSING_SIGNAL_HANDLER",
1860
+ "COMMAND_INJECTION",
1861
+ "SQL_INJECTION",
1862
+ "XSS_RISK",
1863
+ "HARDCODED_SECRET",
1864
+ "UNSAFE_REGEX"
1865
+ ],
1866
+ parseErrors
1867
+ }
1868
+ };
1869
+ }
1870
+ shouldRun(context) {
1871
+ return context.files.some((f) => this.shouldAnalyzeFile(f));
1872
+ }
1873
+ shouldAnalyzeFile(file) {
1874
+ const ext = file.split(".").pop()?.toLowerCase();
1875
+ return [
1876
+ "ts",
1877
+ "tsx",
1878
+ "js",
1879
+ "jsx"
1880
+ ].includes(ext || "");
1881
+ }
1882
+ getPluginsForFile(file) {
1883
+ const plugins = [
1884
+ "typescript"
1885
+ ];
1886
+ if (file.endsWith(".tsx") || file.endsWith(".jsx")) {
1887
+ plugins.push("jsx");
1888
+ }
1889
+ return plugins;
1890
+ }
1891
+ /**
1892
+ * Analyze AST for security issues
1893
+ */
1894
+ analyzeAST(ast, content, file) {
1895
+ const issues = [];
1896
+ let nodesVisited = 0;
1897
+ const fileContext = {
1898
+ isDaemon: false,
1899
+ hasSignalHandler: false};
1900
+ fileContext.isDaemon = content.includes(".listen(") || file.includes("daemon") || file.includes("server") || file.includes("worker");
1901
+ traverse(ast, {
1902
+ enter() {
1903
+ nodesVisited++;
1904
+ },
1905
+ // Detect eval()
1906
+ CallExpression: /* @__PURE__ */ __name((path) => {
1907
+ const callee = path.node.callee;
1908
+ if (callee.type === "Identifier" && callee.name === "eval") {
1909
+ issues.push({
1910
+ id: `security/eval/${file}/${path.node.loc?.start.line}`,
1911
+ severity: "critical",
1912
+ type: "UNSAFE_EVAL",
1913
+ message: "eval() allows arbitrary code execution",
1914
+ file,
1915
+ line: path.node.loc?.start.line,
1916
+ column: path.node.loc?.start.column,
1917
+ fix: "Use JSON.parse() for data or refactor logic to avoid eval"
1918
+ });
1919
+ }
1920
+ if (callee.type === "Identifier" && callee.name === "Function") {
1921
+ issues.push({
1922
+ id: `security/function-constructor/${file}/${path.node.loc?.start.line}`,
1923
+ severity: "critical",
1924
+ type: "UNSAFE_EVAL",
1925
+ message: "new Function() is equivalent to eval() and allows arbitrary code execution",
1926
+ file,
1927
+ line: path.node.loc?.start.line,
1928
+ column: path.node.loc?.start.column,
1929
+ fix: "Refactor to avoid dynamic code generation"
1930
+ });
1931
+ }
1932
+ if (callee.type === "Identifier" && (callee.name === "setTimeout" || callee.name === "setInterval")) {
1933
+ const firstArg = path.node.arguments[0];
1934
+ if (firstArg && firstArg.type === "StringLiteral") {
1935
+ issues.push({
1936
+ id: `security/string-timer/${file}/${path.node.loc?.start.line}`,
1937
+ severity: "high",
1938
+ type: "UNSAFE_EVAL",
1939
+ message: `${callee.name} with string argument executes code like eval()`,
1940
+ file,
1941
+ line: path.node.loc?.start.line,
1942
+ fix: "Pass a function instead of a string"
1943
+ });
1944
+ }
1945
+ }
1946
+ if (callee.type === "Identifier" && (callee.name === "exec" || callee.name === "execSync")) {
1947
+ const firstArg = path.node.arguments[0];
1948
+ if (firstArg && !this.isStaticString(firstArg)) {
1949
+ issues.push({
1950
+ id: `security/command-injection/${file}/${path.node.loc?.start.line}`,
1951
+ severity: "high",
1952
+ type: "COMMAND_INJECTION",
1953
+ message: "exec with dynamic command - potential command injection",
1954
+ file,
1955
+ line: path.node.loc?.start.line,
1956
+ fix: "Validate/sanitize input or use execFile with explicit arguments"
1957
+ });
1958
+ }
1959
+ }
1960
+ if (callee.type === "MemberExpression" && callee.object.type === "Identifier" && callee.object.name === "process" && callee.property.type === "Identifier" && callee.property.name === "on") {
1961
+ const firstArg = path.node.arguments[0];
1962
+ if (firstArg && firstArg.type === "StringLiteral") {
1963
+ if (firstArg.value === "SIGTERM" || firstArg.value === "SIGINT") {
1964
+ fileContext.hasSignalHandler = true;
1965
+ }
1966
+ }
1967
+ }
1968
+ }, "CallExpression"),
1969
+ // Detect fs operations with dynamic paths
1970
+ MemberExpression: /* @__PURE__ */ __name((path) => {
1971
+ const node = path.node;
1972
+ if (node.object.type === "Identifier" && (node.object.name === "fs" || node.object.name === "fsp")) {
1973
+ const parent = path.parentPath;
1974
+ if (parent.isCallExpression()) {
1975
+ const methodName = node.property.type === "Identifier" ? node.property.name : node.property.value;
1976
+ const pathMethods = [
1977
+ "readFile",
1978
+ "readFileSync",
1979
+ "writeFile",
1980
+ "writeFileSync",
1981
+ "readdir",
1982
+ "readdirSync",
1983
+ "stat",
1984
+ "statSync",
1985
+ "unlink",
1986
+ "unlinkSync",
1987
+ "mkdir",
1988
+ "mkdirSync",
1989
+ "rmdir",
1990
+ "rmdirSync",
1991
+ "access",
1992
+ "accessSync"
1993
+ ];
1994
+ if (pathMethods.includes(methodName)) {
1995
+ const firstArg = parent.node.arguments[0];
1996
+ if (firstArg && !this.isStaticPath(firstArg)) {
1997
+ issues.push({
1998
+ id: `security/path-traversal/${file}/${path.node.loc?.start.line}`,
1999
+ severity: "high",
2000
+ type: "PATH_TRAVERSAL",
2001
+ message: `fs.${methodName} with dynamic path - potential path traversal`,
2002
+ file,
2003
+ line: path.node.loc?.start.line,
2004
+ fix: "Validate paths against workspace root before use"
2005
+ });
2006
+ }
2007
+ }
2008
+ }
2009
+ }
2010
+ }, "MemberExpression"),
2011
+ // Check for dangerous regex patterns
2012
+ NewExpression: /* @__PURE__ */ __name((path) => {
2013
+ if (path.node.callee.type === "Identifier" && path.node.callee.name === "RegExp") {
2014
+ const firstArg = path.node.arguments[0];
2015
+ if (firstArg && !this.isStaticString(firstArg)) {
2016
+ issues.push({
2017
+ id: `security/unsafe-regex/${file}/${path.node.loc?.start.line}`,
2018
+ severity: "medium",
2019
+ type: "UNSAFE_REGEX",
2020
+ message: "Dynamic RegExp - potential ReDoS or injection vulnerability",
2021
+ file,
2022
+ line: path.node.loc?.start.line,
2023
+ fix: "Use static regex patterns or validate input"
2024
+ });
2025
+ }
2026
+ }
2027
+ }, "NewExpression"),
2028
+ // Check for innerHTML/dangerouslySetInnerHTML (XSS)
2029
+ JSXAttribute: /* @__PURE__ */ __name((path) => {
2030
+ const name = path.node.name;
2031
+ if (name.type === "JSXIdentifier" && name.name === "dangerouslySetInnerHTML") {
2032
+ issues.push({
2033
+ id: `security/xss-risk/${file}/${path.node.loc?.start.line}`,
2034
+ severity: "high",
2035
+ type: "XSS_RISK",
2036
+ message: "dangerouslySetInnerHTML can lead to XSS if content is not sanitized",
2037
+ file,
2038
+ line: path.node.loc?.start.line,
2039
+ fix: "Sanitize HTML content before rendering or avoid using dangerouslySetInnerHTML"
2040
+ });
2041
+ }
2042
+ }, "JSXAttribute"),
2043
+ // Check for hardcoded secrets in variable declarations
2044
+ VariableDeclarator: /* @__PURE__ */ __name((path) => {
2045
+ const id = path.node.id;
2046
+ const init = path.node.init;
2047
+ if (id.type === "Identifier" && init) {
2048
+ this.checkForHardcodedSecret(id.name, init, file, path.node.loc?.start.line, issues);
2049
+ }
2050
+ }, "VariableDeclarator"),
2051
+ // Check for hardcoded secrets in class properties
2052
+ ClassProperty: /* @__PURE__ */ __name((path) => {
2053
+ const key = path.node.key;
2054
+ const value = path.node.value;
2055
+ if (key.type === "Identifier" && value) {
2056
+ this.checkForHardcodedSecret(key.name, value, file, path.node.loc?.start.line, issues);
2057
+ }
2058
+ }, "ClassProperty"),
2059
+ // After traversal is complete, check daemon-specific patterns
2060
+ Program: {
2061
+ exit: /* @__PURE__ */ __name(() => {
2062
+ if (fileContext.isDaemon && !fileContext.hasSignalHandler) {
2063
+ issues.push({
2064
+ id: `security/signal-handler/${file}`,
2065
+ severity: "high",
2066
+ type: "MISSING_SIGNAL_HANDLER",
2067
+ message: "Daemon/server missing signal handlers (SIGTERM/SIGINT)",
2068
+ file,
2069
+ fix: "Add process.on('SIGTERM', gracefulShutdown) for clean shutdown"
2070
+ });
2071
+ }
2072
+ }, "exit")
2073
+ }
2074
+ });
2075
+ return {
2076
+ issues,
2077
+ nodesVisited
2078
+ };
2079
+ }
2080
+ /**
2081
+ * Check if expression is a static string (safe)
2082
+ */
2083
+ isStaticString(node) {
2084
+ if (node.type === "StringLiteral") {
2085
+ return true;
2086
+ }
2087
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0) {
2088
+ return true;
2089
+ }
2090
+ return false;
2091
+ }
2092
+ /**
2093
+ * Check if expression is a static path (safe)
2094
+ */
2095
+ isStaticPath(node) {
2096
+ if (node.type === "StringLiteral") {
2097
+ return true;
2098
+ }
2099
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0) {
2100
+ return true;
2101
+ }
2102
+ if (node.type === "CallExpression") {
2103
+ const callee = node.callee;
2104
+ if (callee.type === "MemberExpression" && callee.object.type === "Identifier" && callee.object.name === "path" && callee.property.type === "Identifier" && callee.property.name === "join") {
2105
+ return node.arguments.every((arg) => {
2106
+ if (arg.type === "StringLiteral") {
2107
+ return true;
2108
+ }
2109
+ if (arg.type === "Identifier" && (arg.name === "__dirname" || arg.name === "__filename")) {
2110
+ return true;
2111
+ }
2112
+ return false;
2113
+ });
2114
+ }
2115
+ }
2116
+ return false;
2117
+ }
2118
+ /**
2119
+ * Check if a value looks like a hardcoded secret
2120
+ */
2121
+ checkForHardcodedSecret(name, value, file, line, issues) {
2122
+ if (!value) {
2123
+ return;
2124
+ }
2125
+ const varName = name.toLowerCase();
2126
+ const secretIndicators = [
2127
+ "apikey",
2128
+ "api_key",
2129
+ "secret",
2130
+ "password",
2131
+ "token",
2132
+ "credential",
2133
+ "auth",
2134
+ "key"
2135
+ ];
2136
+ if (secretIndicators.some((s) => varName.includes(s))) {
2137
+ if (value.type === "StringLiteral" && value.value.length > 8) {
2138
+ const valueStr = value.value.toLowerCase();
2139
+ if (!valueStr.includes("placeholder") && !valueStr.includes("example") && !valueStr.includes("xxx") && !valueStr.includes("todo") && !valueStr.includes("your_") && !valueStr.includes("env.")) {
2140
+ issues.push({
2141
+ id: `security/hardcoded-secret/${file}/${line}`,
2142
+ severity: "critical",
2143
+ type: "HARDCODED_SECRET",
2144
+ message: `Possible hardcoded secret in "${name}"`,
2145
+ file,
2146
+ line,
2147
+ fix: "Use environment variables for secrets"
2148
+ });
2149
+ }
2150
+ }
2151
+ }
2152
+ }
2153
+ };
2154
+
2155
+ // ../../packages/core/dist/analysis/pipeline.js
2156
+ var ANALYZER_COVERAGE_MAP = {
2157
+ syntax: "astParsed",
2158
+ security: "securityChecked",
2159
+ completeness: "completenessChecked",
2160
+ "change-impact": "architectureChecked",
2161
+ "import-graph": "importGraphChecked",
2162
+ complexity: "complexityChecked"
2163
+ };
2164
+ var CONFIDENCE_WEIGHTS = {
2165
+ syntax: 0.2,
2166
+ security: 0.25,
2167
+ completeness: 0.15,
2168
+ "change-impact": 0.1,
2169
+ "import-graph": 0.15,
2170
+ complexity: 0.15
2171
+ };
2172
+ async function runAnalysisPipeline(context, config) {
2173
+ const start = Date.now();
2174
+ const parallel = config?.parallel ?? true;
2175
+ const timeout = config?.timeout ?? 3e4;
2176
+ const allAnalyzers = createAnalyzers(context.workspaceRoot);
2177
+ const selectedAnalyzers = filterAnalyzers(allAnalyzers, context, config?.analyzers);
2178
+ const results = parallel ? await runParallel(selectedAnalyzers, context, timeout) : await runSequential(selectedAnalyzers, context, timeout);
2179
+ const coverage = buildCoverageInfo(results, context);
2180
+ const confidence = calculateConfidence(results, coverage);
2181
+ const allIssues = results.flatMap((r) => r.issues);
2182
+ const issuesBySeverity = groupBySeverity(allIssues);
2183
+ return {
2184
+ results,
2185
+ totalIssues: allIssues.length,
2186
+ issuesBySeverity,
2187
+ coverage,
2188
+ confidence,
2189
+ duration: Date.now() - start
2190
+ };
2191
+ }
2192
+ __name(runAnalysisPipeline, "runAnalysisPipeline");
2193
+ function createAnalyzers(workspaceRoot) {
2194
+ return [
2195
+ new SyntaxAnalyzer(),
2196
+ new SecurityAnalyzer(),
2197
+ new CompletenessAnalyzer(),
2198
+ new ComplexityAnalyzer(),
2199
+ new ImportGraphAnalyzer(),
2200
+ new ChangeImpactAnalyzer(workspaceRoot)
2201
+ ];
2202
+ }
2203
+ __name(createAnalyzers, "createAnalyzers");
2204
+ function filterAnalyzers(analyzers, context, selectedIds) {
2205
+ let filtered = analyzers;
2206
+ if (selectedIds && selectedIds.length > 0) {
2207
+ const idSet = new Set(selectedIds);
2208
+ filtered = filtered.filter((a) => idSet.has(a.id));
2209
+ }
2210
+ return filtered.filter((a) => a.shouldRun(context));
2211
+ }
2212
+ __name(filterAnalyzers, "filterAnalyzers");
2213
+ async function runParallel(analyzers, context, timeout) {
2214
+ const promises = analyzers.map((analyzer) => runWithTimeout(analyzer, context, timeout));
2215
+ return Promise.all(promises);
2216
+ }
2217
+ __name(runParallel, "runParallel");
2218
+ async function runSequential(analyzers, context, timeout) {
2219
+ const results = [];
2220
+ for (const analyzer of analyzers) {
2221
+ const result = await runWithTimeout(analyzer, context, timeout);
2222
+ results.push(result);
2223
+ }
2224
+ return results;
2225
+ }
2226
+ __name(runSequential, "runSequential");
2227
+ async function runWithTimeout(analyzer, context, timeout) {
2228
+ const start = Date.now();
2229
+ try {
2230
+ const result = await Promise.race([
2231
+ analyzer.analyze(context),
2232
+ new Promise((_, reject) => {
2233
+ setTimeout(() => reject(new Error(`Analyzer '${analyzer.id}' timed out after ${timeout}ms`)), timeout);
2234
+ })
2235
+ ]);
2236
+ return result;
2237
+ } catch (error) {
2238
+ return {
2239
+ analyzer: analyzer.id,
2240
+ success: false,
2241
+ issues: [
2242
+ {
2243
+ id: `pipeline/${analyzer.id}/error`,
2244
+ severity: "high",
2245
+ type: "ANALYZER_ERROR",
2246
+ message: error instanceof Error ? error.message : String(error)
2247
+ }
2248
+ ],
2249
+ coverage: 0,
2250
+ duration: Date.now() - start
2251
+ };
2252
+ }
2253
+ }
2254
+ __name(runWithTimeout, "runWithTimeout");
2255
+ function buildCoverageInfo(results, context) {
2256
+ const coverage = {
2257
+ astParsed: false,
2258
+ securityChecked: false,
2259
+ completenessChecked: false,
2260
+ architectureChecked: false,
2261
+ importGraphChecked: false,
2262
+ complexityChecked: false,
2263
+ filesCoverage: 0
2264
+ };
2265
+ for (const result of results) {
2266
+ const field = ANALYZER_COVERAGE_MAP[result.analyzer];
2267
+ if (field && field !== "filesCoverage" && result.success) {
2268
+ coverage[field] = true;
2269
+ }
2270
+ }
2271
+ const totalFiles = context.files.length;
2272
+ if (totalFiles > 0) {
2273
+ const successfulResults = results.filter((r) => r.success);
2274
+ const avgCoverage = successfulResults.length > 0 ? successfulResults.reduce((sum, r) => sum + r.coverage, 0) / successfulResults.length : 0;
2275
+ coverage.filesCoverage = avgCoverage;
2276
+ }
2277
+ return coverage;
2278
+ }
2279
+ __name(buildCoverageInfo, "buildCoverageInfo");
2280
+ function calculateConfidence(results, coverage) {
2281
+ const breakdown = {};
2282
+ let weightedSum = 0;
2283
+ let totalWeight = 0;
2284
+ let maxPossible = 0;
2285
+ for (const [id, weight] of Object.entries(CONFIDENCE_WEIGHTS)) {
2286
+ const result = results.find((r) => r.analyzer === id);
2287
+ totalWeight += weight;
2288
+ if (result) {
2289
+ const analyzerConfidence = result.success ? result.coverage : 0;
2290
+ breakdown[id] = analyzerConfidence;
2291
+ weightedSum += weight * analyzerConfidence;
2292
+ maxPossible += weight;
2293
+ } else {
2294
+ breakdown[id] = 0;
2295
+ }
2296
+ }
2297
+ const confidence = totalWeight > 0 ? weightedSum / totalWeight : 0;
2298
+ const maxPossibleConfidence = totalWeight > 0 ? maxPossible / totalWeight : 0;
2299
+ const ranCount = results.filter((r) => r.success).length;
2300
+ const totalAnalyzers = Object.keys(CONFIDENCE_WEIGHTS).length;
2301
+ const explanationParts = [
2302
+ `${ranCount}/${totalAnalyzers} analyzers ran successfully`,
2303
+ `Files coverage: ${(coverage.filesCoverage * 100).toFixed(0)}%`
2304
+ ];
2305
+ const failedAnalyzers = results.filter((r) => !r.success);
2306
+ if (failedAnalyzers.length > 0) {
2307
+ explanationParts.push(`Failed: ${failedAnalyzers.map((r) => r.analyzer).join(", ")}`);
2308
+ }
2309
+ return {
2310
+ confidence: Math.round(confidence * 100) / 100,
2311
+ breakdown,
2312
+ explanation: explanationParts.join(". "),
2313
+ maxPossibleConfidence: Math.round(maxPossibleConfidence * 100) / 100
2314
+ };
2315
+ }
2316
+ __name(calculateConfidence, "calculateConfidence");
2317
+ function groupBySeverity(issues) {
2318
+ const grouped = {
2319
+ critical: [],
2320
+ high: [],
2321
+ medium: [],
2322
+ low: [],
2323
+ info: []
2324
+ };
2325
+ for (const issue of issues) {
2326
+ const severity = issue.severity || "info";
2327
+ if (severity in grouped) {
2328
+ grouped[severity].push(issue);
2329
+ } else {
2330
+ grouped.info.push(issue);
2331
+ }
2332
+ }
2333
+ return grouped;
2334
+ }
2335
+ __name(groupBySeverity, "groupBySeverity");
2336
+
2337
+ // ../../packages/core/dist/analysis/static/index.js
2338
+ async function detectOrphans(_workspacePath, _options) {
2339
+ return [];
2340
+ }
2341
+ __name(detectOrphans, "detectOrphans");
2342
+ async function checkFilesForOrphanStatus(_files, _workspaceRoot, _options) {
2343
+ return {};
2344
+ }
2345
+ __name(checkFilesForOrphanStatus, "checkFilesForOrphanStatus");
2346
+ async function filterOrphansToFiles(_candidateFiles, _workspaceRoot, _options) {
2347
+ return [];
2348
+ }
2349
+ __name(filterOrphansToFiles, "filterOrphansToFiles");
2350
+ async function runStaticAnalysis(files, _workspaceRoot, options = {}) {
2351
+ const startTime = Date.now();
2352
+ const result = {
2353
+ skippedTests: [],
2354
+ orphanedFiles: [],
2355
+ duration: 0,
2356
+ success: true,
2357
+ errors: []
2358
+ };
2359
+ if (!options.skipTestDetection) {
2360
+ try {
2361
+ const { analyzeSkippedTests: analyzeSkippedTests2 } = await import('./SkippedTestDetector-PJSKSOZR.js');
2362
+ const testResults = analyzeSkippedTests2(files);
2363
+ for (const testResult of testResults) {
2364
+ if (!testResult.parsed && testResult.error) {
2365
+ result.errors.push(`Parse error in ${testResult.file}: ${testResult.error}`);
2366
+ }
2367
+ for (const skipped of testResult.skipped) {
2368
+ result.skippedTests.push({
2369
+ file: skipped.file,
2370
+ type: skipped.type,
2371
+ name: skipped.name,
2372
+ line: skipped.line
2373
+ });
2374
+ }
2375
+ }
2376
+ } catch (error) {
2377
+ result.errors.push(`Skipped test detection failed: ${error instanceof Error ? error.message : String(error)}`);
2378
+ }
2379
+ }
2380
+ void options.skipOrphanDetection;
2381
+ result.duration = Date.now() - startTime;
2382
+ result.success = result.errors.length === 0;
2383
+ return result;
2384
+ }
2385
+ __name(runStaticAnalysis, "runStaticAnalysis");
2386
+
2387
+ export { ChangeImpactAnalyzer, CompletenessAnalyzer, ComplexityAnalyzer, ImportGraphAnalyzer, SecurityAnalyzer, SyntaxAnalyzer, checkFilesForOrphanStatus, countASTNodes, createChangeImpactAnalyzer, detectOrphans, extractImportSources, extractImports, extractImportsBatch, filterOrphansToFiles, isSupportedFile, offsetToLine, parseSource, runAnalysisPipeline, runStaticAnalysis, walkAST };
2388
+ //# sourceMappingURL=chunk-F7GEJLP7.js.map
2389
+ //# sourceMappingURL=chunk-F7GEJLP7.js.map