@vibecheckai/cli 3.5.0 → 3.5.2

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 (224) hide show
  1. package/bin/registry.js +214 -237
  2. package/bin/runners/cli-utils.js +33 -2
  3. package/bin/runners/context/analyzer.js +52 -1
  4. package/bin/runners/context/generators/cursor.js +2 -49
  5. package/bin/runners/context/git-context.js +3 -1
  6. package/bin/runners/context/team-conventions.js +33 -7
  7. package/bin/runners/lib/analysis-core.js +25 -5
  8. package/bin/runners/lib/analyzers.js +431 -481
  9. package/bin/runners/lib/default-config.js +127 -0
  10. package/bin/runners/lib/doctor/modules/security.js +3 -1
  11. package/bin/runners/lib/engine/ast-cache.js +210 -0
  12. package/bin/runners/lib/engine/auth-extractor.js +211 -0
  13. package/bin/runners/lib/engine/billing-extractor.js +112 -0
  14. package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
  15. package/bin/runners/lib/engine/env-extractor.js +207 -0
  16. package/bin/runners/lib/engine/express-extractor.js +208 -0
  17. package/bin/runners/lib/engine/extractors.js +849 -0
  18. package/bin/runners/lib/engine/index.js +207 -0
  19. package/bin/runners/lib/engine/repo-index.js +514 -0
  20. package/bin/runners/lib/engine/types.js +124 -0
  21. package/bin/runners/lib/engines/accessibility-engine.js +18 -218
  22. package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
  23. package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
  24. package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
  25. package/bin/runners/lib/engines/mock-data-engine.js +10 -53
  26. package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
  27. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
  28. package/bin/runners/lib/engines/type-aware-engine.js +39 -263
  29. package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
  30. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  31. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  32. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  33. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  34. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  35. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  36. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  37. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
  38. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  39. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  40. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  41. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  42. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  43. package/bin/runners/lib/entitlements-v2.js +73 -97
  44. package/bin/runners/lib/error-handler.js +44 -3
  45. package/bin/runners/lib/error-messages.js +289 -0
  46. package/bin/runners/lib/evidence-pack.js +7 -1
  47. package/bin/runners/lib/finding-id.js +69 -0
  48. package/bin/runners/lib/finding-sorter.js +89 -0
  49. package/bin/runners/lib/html-proof-report.js +700 -350
  50. package/bin/runners/lib/missions/plan.js +6 -46
  51. package/bin/runners/lib/missions/templates.js +0 -232
  52. package/bin/runners/lib/next-action.js +560 -0
  53. package/bin/runners/lib/prerequisites.js +149 -0
  54. package/bin/runners/lib/route-detection.js +137 -68
  55. package/bin/runners/lib/scan-output.js +91 -76
  56. package/bin/runners/lib/scan-runner.js +135 -0
  57. package/bin/runners/lib/schemas/ajv-validator.js +464 -0
  58. package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
  59. package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
  60. package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
  61. package/bin/runners/lib/schemas/run-request.schema.json +108 -0
  62. package/bin/runners/lib/schemas/validator.js +27 -0
  63. package/bin/runners/lib/schemas/verdict.schema.json +140 -0
  64. package/bin/runners/lib/ship-output-enterprise.js +23 -23
  65. package/bin/runners/lib/ship-output.js +75 -31
  66. package/bin/runners/lib/terminal-ui.js +6 -113
  67. package/bin/runners/lib/truth.js +351 -10
  68. package/bin/runners/lib/unified-cli-output.js +430 -603
  69. package/bin/runners/lib/unified-output.js +13 -9
  70. package/bin/runners/runAIAgent.js +10 -5
  71. package/bin/runners/runAgent.js +0 -3
  72. package/bin/runners/runAllowlist.js +389 -0
  73. package/bin/runners/runApprove.js +0 -33
  74. package/bin/runners/runAuth.js +73 -45
  75. package/bin/runners/runCheckpoint.js +51 -11
  76. package/bin/runners/runClassify.js +85 -21
  77. package/bin/runners/runContext.js +0 -3
  78. package/bin/runners/runDoctor.js +41 -28
  79. package/bin/runners/runEvidencePack.js +362 -0
  80. package/bin/runners/runFirewall.js +0 -3
  81. package/bin/runners/runFirewallHook.js +0 -3
  82. package/bin/runners/runFix.js +66 -76
  83. package/bin/runners/runGuard.js +18 -411
  84. package/bin/runners/runInit.js +113 -30
  85. package/bin/runners/runLabs.js +424 -0
  86. package/bin/runners/runMcp.js +19 -25
  87. package/bin/runners/runPolish.js +64 -240
  88. package/bin/runners/runPromptFirewall.js +12 -5
  89. package/bin/runners/runProve.js +57 -22
  90. package/bin/runners/runQuickstart.js +531 -0
  91. package/bin/runners/runReality.js +59 -68
  92. package/bin/runners/runReport.js +38 -33
  93. package/bin/runners/runRuntime.js +8 -5
  94. package/bin/runners/runScan.js +1413 -190
  95. package/bin/runners/runShip.js +113 -719
  96. package/bin/runners/runTruth.js +0 -3
  97. package/bin/runners/runValidate.js +13 -9
  98. package/bin/runners/runWatch.js +23 -14
  99. package/bin/scan.js +6 -1
  100. package/bin/vibecheck.js +204 -185
  101. package/mcp-server/deprecation-middleware.js +282 -0
  102. package/mcp-server/handlers/index.ts +15 -0
  103. package/mcp-server/handlers/tool-handler.ts +554 -0
  104. package/mcp-server/index-v1.js +698 -0
  105. package/mcp-server/index.js +210 -238
  106. package/mcp-server/lib/cache-wrapper.cjs +383 -0
  107. package/mcp-server/lib/error-envelope.js +138 -0
  108. package/mcp-server/lib/executor.ts +499 -0
  109. package/mcp-server/lib/index.ts +19 -0
  110. package/mcp-server/lib/rate-limiter.js +166 -0
  111. package/mcp-server/lib/sandbox.test.ts +519 -0
  112. package/mcp-server/lib/sandbox.ts +395 -0
  113. package/mcp-server/lib/types.ts +267 -0
  114. package/mcp-server/package.json +12 -3
  115. package/mcp-server/registry/tool-registry.js +794 -0
  116. package/mcp-server/registry/tools.json +605 -0
  117. package/mcp-server/registry.test.ts +334 -0
  118. package/mcp-server/tests/tier-gating.test.js +297 -0
  119. package/mcp-server/tier-auth.js +378 -45
  120. package/mcp-server/tools-v3.js +353 -442
  121. package/mcp-server/tsconfig.json +37 -0
  122. package/mcp-server/vibecheck-2.0-tools.js +14 -1
  123. package/package.json +1 -1
  124. package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
  125. package/bin/runners/lib/audit-logger.js +0 -532
  126. package/bin/runners/lib/authority/authorities/architecture.js +0 -364
  127. package/bin/runners/lib/authority/authorities/compliance.js +0 -341
  128. package/bin/runners/lib/authority/authorities/human.js +0 -343
  129. package/bin/runners/lib/authority/authorities/quality.js +0 -420
  130. package/bin/runners/lib/authority/authorities/security.js +0 -228
  131. package/bin/runners/lib/authority/index.js +0 -293
  132. package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
  133. package/bin/runners/lib/cli-charts.js +0 -368
  134. package/bin/runners/lib/cli-config-display.js +0 -405
  135. package/bin/runners/lib/cli-demo.js +0 -275
  136. package/bin/runners/lib/cli-errors.js +0 -438
  137. package/bin/runners/lib/cli-help-formatter.js +0 -439
  138. package/bin/runners/lib/cli-interactive-menu.js +0 -509
  139. package/bin/runners/lib/cli-prompts.js +0 -441
  140. package/bin/runners/lib/cli-scan-cards.js +0 -362
  141. package/bin/runners/lib/compliance-reporter.js +0 -710
  142. package/bin/runners/lib/conductor/index.js +0 -671
  143. package/bin/runners/lib/easy/README.md +0 -123
  144. package/bin/runners/lib/easy/index.js +0 -140
  145. package/bin/runners/lib/easy/interactive-wizard.js +0 -788
  146. package/bin/runners/lib/easy/one-click-firewall.js +0 -564
  147. package/bin/runners/lib/easy/zero-config-reality.js +0 -714
  148. package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
  149. package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
  150. package/bin/runners/lib/engines/confidence-scoring.js +0 -276
  151. package/bin/runners/lib/engines/context-detection.js +0 -264
  152. package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
  153. package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
  154. package/bin/runners/lib/engines/env-variables-engine.js +0 -458
  155. package/bin/runners/lib/engines/error-handling-engine.js +0 -437
  156. package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
  157. package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
  158. package/bin/runners/lib/engines/framework-detection.js +0 -508
  159. package/bin/runners/lib/engines/import-order-engine.js +0 -429
  160. package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
  161. package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
  162. package/bin/runners/lib/engines/orchestrator.js +0 -334
  163. package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
  164. package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
  165. package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
  166. package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
  167. package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
  168. package/bin/runners/lib/enhanced-features/index.js +0 -305
  169. package/bin/runners/lib/enhanced-output.js +0 -631
  170. package/bin/runners/lib/enterprise.js +0 -300
  171. package/bin/runners/lib/firewall/command-validator.js +0 -351
  172. package/bin/runners/lib/firewall/config.js +0 -341
  173. package/bin/runners/lib/firewall/content-validator.js +0 -519
  174. package/bin/runners/lib/firewall/index.js +0 -101
  175. package/bin/runners/lib/firewall/path-validator.js +0 -256
  176. package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
  177. package/bin/runners/lib/mcp-utils.js +0 -425
  178. package/bin/runners/lib/output/index.js +0 -1022
  179. package/bin/runners/lib/policy-engine.js +0 -652
  180. package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
  181. package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
  182. package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
  183. package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
  184. package/bin/runners/lib/polish/autofix/index.js +0 -200
  185. package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
  186. package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
  187. package/bin/runners/lib/polish/backend-checks.js +0 -148
  188. package/bin/runners/lib/polish/documentation-checks.js +0 -111
  189. package/bin/runners/lib/polish/frontend-checks.js +0 -168
  190. package/bin/runners/lib/polish/index.js +0 -71
  191. package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
  192. package/bin/runners/lib/polish/library-detection.js +0 -175
  193. package/bin/runners/lib/polish/performance-checks.js +0 -100
  194. package/bin/runners/lib/polish/security-checks.js +0 -148
  195. package/bin/runners/lib/polish/utils.js +0 -203
  196. package/bin/runners/lib/prompt-builder.js +0 -540
  197. package/bin/runners/lib/proof-certificate.js +0 -634
  198. package/bin/runners/lib/reality/accessibility-audit.js +0 -946
  199. package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
  200. package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
  201. package/bin/runners/lib/reality/performance-tracker.js +0 -1077
  202. package/bin/runners/lib/reality/scenario-generator.js +0 -1404
  203. package/bin/runners/lib/reality/visual-regression.js +0 -852
  204. package/bin/runners/lib/reality-profiler.js +0 -717
  205. package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
  206. package/bin/runners/lib/review/ai-code-review.js +0 -832
  207. package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
  208. package/bin/runners/lib/sbom-generator.js +0 -641
  209. package/bin/runners/lib/scan-output-enhanced.js +0 -512
  210. package/bin/runners/lib/security/owasp-scanner.js +0 -939
  211. package/bin/runners/lib/validators/contract-validator.js +0 -283
  212. package/bin/runners/lib/validators/dead-export-detector.js +0 -279
  213. package/bin/runners/lib/validators/dep-audit.js +0 -245
  214. package/bin/runners/lib/validators/env-validator.js +0 -319
  215. package/bin/runners/lib/validators/index.js +0 -120
  216. package/bin/runners/lib/validators/license-checker.js +0 -252
  217. package/bin/runners/lib/validators/route-validator.js +0 -290
  218. package/bin/runners/runAuthority.js +0 -528
  219. package/bin/runners/runConductor.js +0 -772
  220. package/bin/runners/runContainer.js +0 -366
  221. package/bin/runners/runEasy.js +0 -410
  222. package/bin/runners/runIaC.js +0 -372
  223. package/bin/runners/runVibe.js +0 -791
  224. package/mcp-server/tools.js +0 -495
@@ -27,12 +27,7 @@ const ansi = {
27
27
  dim: `${ESC}[2m`,
28
28
  italic: `${ESC}[3m`,
29
29
  underline: `${ESC}[4m`,
30
- blink: `${ESC}[5m`,
31
- inverse: `${ESC}[7m`,
32
- hidden: `${ESC}[8m`,
33
- strike: `${ESC}[9m`,
34
30
  // Colors
35
- black: `${ESC}[30m`,
36
31
  red: `${ESC}[31m`,
37
32
  green: `${ESC}[32m`,
38
33
  yellow: `${ESC}[33m`,
@@ -40,42 +35,22 @@ const ansi = {
40
35
  magenta: `${ESC}[35m`,
41
36
  cyan: `${ESC}[36m`,
42
37
  white: `${ESC}[37m`,
43
- // Bright colors
44
38
  gray: `${ESC}[90m`,
45
- brightRed: `${ESC}[91m`,
46
- brightGreen: `${ESC}[92m`,
47
- brightYellow: `${ESC}[93m`,
48
- brightBlue: `${ESC}[94m`,
49
- brightMagenta: `${ESC}[95m`,
50
- brightCyan: `${ESC}[96m`,
51
- brightWhite: `${ESC}[97m`,
52
39
  // RGB colors (truecolor support)
53
40
  rgb: (r, g, b) => SUPPORTS_TRUECOLOR ? `${ESC}[38;2;${r};${g};${b}m` : '',
54
41
  bgRgb: (r, g, b) => SUPPORTS_TRUECOLOR ? `${ESC}[48;2;${r};${g};${b}m` : '',
55
42
  // Backgrounds
56
- bgBlack: `${ESC}[40m`,
57
43
  bgRed: `${ESC}[41m`,
58
44
  bgGreen: `${ESC}[42m`,
59
45
  bgYellow: `${ESC}[43m`,
60
46
  bgBlue: `${ESC}[44m`,
61
47
  bgMagenta: `${ESC}[45m`,
62
48
  bgCyan: `${ESC}[46m`,
63
- bgWhite: `${ESC}[47m`,
64
- bgBrightBlack: `${ESC}[100m`,
65
- // Cursor control
49
+ // Cursor
66
50
  hideCursor: `${ESC}[?25l`,
67
51
  showCursor: `${ESC}[?25h`,
68
52
  clearLine: `${ESC}[2K`,
69
- clearScreen: `${ESC}[2J`,
70
- clearToEnd: `${ESC}[0J`,
71
- saveCursor: `${ESC}[s`,
72
- restoreCursor: `${ESC}[u`,
73
53
  cursorUp: (n = 1) => `${ESC}[${n}A`,
74
- cursorDown: (n = 1) => `${ESC}[${n}B`,
75
- cursorRight: (n = 1) => `${ESC}[${n}C`,
76
- cursorLeft: (n = 1) => `${ESC}[${n}D`,
77
- cursorTo: (row, col) => `${ESC}[${row};${col}H`,
78
- cursorHome: `${ESC}[H`,
79
54
  };
80
55
 
81
56
  // Semantic Palette
@@ -94,50 +69,17 @@ const style = {
94
69
  };
95
70
 
96
71
  const icons = {
97
- // Status
98
72
  success: '✓',
99
- check: '✓',
100
73
  error: '✗',
101
- cross: '✗',
102
74
  warning: '⚠',
103
75
  info: 'ℹ',
104
- // Navigation
105
76
  bullet: '•',
106
77
  pointer: '❯',
107
78
  arrowRight: '→',
108
- arrow: '→',
109
- arrowLeft: '←',
110
- arrowUp: '↑',
111
- arrowDown: '↓',
112
- // Shapes
113
79
  line: '─',
114
80
  radioOn: '◉',
115
81
  radioOff: '○',
116
- star: '★',
117
- sparkle: '✨',
118
- // Emoji icons
119
82
  lock: '🔒',
120
- ship: '🚀',
121
- rocket: '🚀',
122
- eye: '👁️',
123
- brain: '🧠',
124
- target: '🎯',
125
- chart: '📊',
126
- wrench: '🔧',
127
- gear: '⚙️',
128
- shield: '🛡️',
129
- lightning: '⚡',
130
- fire: '🔥',
131
- folder: '📁',
132
- file: '📄',
133
- search: '🔍',
134
- book: '📖',
135
- server: '🖥️',
136
- // Severity
137
- critical: '🔴',
138
- high: '🟠',
139
- medium: '🟡',
140
- low: '🔵',
141
83
  };
142
84
 
143
85
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -208,6 +150,11 @@ class Spinner {
208
150
  return this;
209
151
  }
210
152
 
153
+ update(text) {
154
+ this.text = text;
155
+ return this;
156
+ }
157
+
211
158
  stop(symbol = icons.success, color = ansi.green, finalMsg) {
212
159
  if (this.timer) clearInterval(this.timer);
213
160
  process.stdout.write(`\r${ansi.clearLine}`);
@@ -341,27 +288,6 @@ const colors = {
341
288
  accent: ansi.cyan,
342
289
  muted: ansi.gray,
343
290
  highlight: ansi.white,
344
- // Extended palette for specific use cases
345
- shipGreen: ansi.rgb(0, 255, 150),
346
- warnAmber: ansi.rgb(255, 200, 0),
347
- blockRed: ansi.rgb(255, 80, 80),
348
- watching: ansi.rgb(100, 200, 255),
349
- gradient1: ansi.rgb(150, 100, 255),
350
- gradient2: ansi.rgb(130, 80, 255),
351
- gradient3: ansi.rgb(110, 60, 255),
352
- };
353
-
354
- // ═══════════════════════════════════════════════════════════════════════════════
355
- // SHORTHAND 'c' ALIAS (for legacy runner compatibility)
356
- // ═══════════════════════════════════════════════════════════════════════════════
357
-
358
- /**
359
- * Legacy shorthand - prefer using `ansi` directly in new code
360
- */
361
- const c = {
362
- ...ansi,
363
- // Clear shorthand
364
- clear: `${ESC}[2J${ESC}[H`,
365
291
  };
366
292
 
367
293
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -404,60 +330,27 @@ class PhaseProgress {
404
330
  stop() { return this; }
405
331
  }
406
332
 
407
- // ═══════════════════════════════════════════════════════════════════════════════
408
- // TOP-LEVEL RGB FUNCTIONS (for migration ease)
409
- // ═══════════════════════════════════════════════════════════════════════════════
410
-
411
- const rgb = ansi.rgb;
412
- const bgRgb = ansi.bgRgb;
413
-
414
333
  // ═══════════════════════════════════════════════════════════════════════════════
415
334
  // EXPORTS
416
335
  // ═══════════════════════════════════════════════════════════════════════════════
417
336
 
418
337
  module.exports = {
419
- // Core constants
420
338
  WIDTH,
421
- ESC,
422
- SUPPORTS_TRUECOLOR,
423
-
424
- // ANSI codes (main export)
425
339
  ansi,
426
-
427
- // Legacy shorthand alias
428
- c,
429
-
430
- // Semantic colors
431
340
  colors,
432
341
  style,
433
-
434
- // Icons
435
342
  icons,
436
-
437
- // Box drawing
438
343
  BOX,
439
-
440
- // RGB functions (top-level)
441
- rgb,
442
- bgRgb,
443
-
444
- // Text utilities
445
344
  padCenter,
446
345
  padRight,
447
346
  truncate,
448
-
449
- // Components
450
347
  Spinner,
451
348
  PhaseProgress,
452
-
453
- // Render functions
454
349
  renderProgressBar,
455
350
  renderScreenHeader,
456
351
  renderScreenFooter,
457
352
  renderVerdictTable,
458
353
  renderSection,
459
354
  renderBanner,
460
-
461
- // Utilities
462
355
  formatDuration,
463
356
  };
@@ -500,14 +500,131 @@ function resolveFastifyRoutes(repoRoot, entryAbs, stats) {
500
500
  // best-effort: fastify instance identifiers
501
501
  const fastifyNames = new Set(["fastify"]);
502
502
 
503
+ // Static string + local-plugin harvesting (cheap, big impact on false positives)
504
+ const _rawConstInits = new Map(); // name -> init node (evaluated lazily)
505
+ const _constStrings = new Map(); // name -> resolved string
506
+ const _localPlugins = new Map(); // name -> Function node
507
+
508
+ function evalStaticString(node, depth = 0) {
509
+ if (!node || depth > 6) return null;
510
+
511
+ if (t.isStringLiteral(node)) return node.value;
512
+
513
+ if (t.isTemplateLiteral(node) && node.expressions.length === 0) {
514
+ return node.quasis.map((q) => q.value.cooked || "").join("");
515
+ }
516
+
517
+ if (t.isIdentifier(node)) {
518
+ const name = node.name;
519
+ if (_constStrings.has(name)) return _constStrings.get(name);
520
+ const init = _rawConstInits.get(name);
521
+ if (!init) return null;
522
+ const v = evalStaticString(init, depth + 1);
523
+ if (typeof v === "string") {
524
+ // cap to avoid pathological blowups
525
+ if (v.length <= 4096) _constStrings.set(name, v);
526
+ return v;
527
+ }
528
+ return null;
529
+ }
530
+
531
+ if (t.isBinaryExpression(node, { operator: "+" })) {
532
+ const l = evalStaticString(node.left, depth + 1);
533
+ const r = evalStaticString(node.right, depth + 1);
534
+ if (typeof l !== "string" || typeof r !== "string") return null;
535
+ const out = l + r;
536
+ return out.length <= 4096 ? out : null;
537
+ }
538
+
539
+ return null;
540
+ }
541
+
542
+ function extractPrefixFromOptsV3(node) {
543
+ if (!t.isObjectExpression(node)) return null;
544
+ for (const p of node.properties) {
545
+ if (!t.isObjectProperty(p)) continue;
546
+ const key =
547
+ t.isIdentifier(p.key) ? p.key.name :
548
+ t.isStringLiteral(p.key) ? p.key.value :
549
+ null;
550
+ if (key !== "prefix") continue;
551
+ return evalStaticString(p.value);
552
+ }
553
+ return null;
554
+ }
555
+
556
+ function extractRouteObjectV3(objExpr) {
557
+ let url = null;
558
+ let methods = [];
559
+ let hasHandler = false;
560
+ const hooks = [];
561
+
562
+ for (const p of objExpr.properties) {
563
+ if (!t.isObjectProperty(p)) continue;
564
+
565
+ const key =
566
+ t.isIdentifier(p.key) ? p.key.name :
567
+ t.isStringLiteral(p.key) ? p.key.value :
568
+ null;
569
+ if (!key) continue;
570
+
571
+ if (key === "url") {
572
+ const u = evalStaticString(p.value);
573
+ if (typeof u === "string") url = u;
574
+ }
575
+
576
+ if (key === "method") {
577
+ if (t.isStringLiteral(p.value)) methods = [p.value.value];
578
+ if (t.isArrayExpression(p.value)) {
579
+ methods = p.value.elements.filter((e) => t.isStringLiteral(e)).map((e) => e.value);
580
+ }
581
+ }
582
+
583
+ if (key === "handler") hasHandler = true;
584
+ if (["preHandler", "onRequest", "preValidation", "preSerialization"].includes(key)) hooks.push(key);
585
+ }
586
+
587
+ return { url, methods, hasHandler, hooks };
588
+ }
589
+
590
+ function unwrapPluginArg(node) {
591
+ // fastify-plugin wrappers are common: fastify.register(fp(plugin))
592
+ // We unwrap 1 layer, best-effort.
593
+ if (!node) return node;
594
+ if (!t.isCallExpression(node)) return node;
595
+
596
+ const calleeName =
597
+ t.isIdentifier(node.callee) ? node.callee.name :
598
+ t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.property) ? node.callee.property.name :
599
+ null;
600
+
601
+ if (!calleeName) return node;
602
+ if (!/^(fp|fastifyPlugin|plugin|fastifyPluginify)$/i.test(calleeName)) return node;
603
+
604
+ const a0 = node.arguments && node.arguments[0];
605
+ return a0 || node;
606
+ }
607
+
503
608
  try {
504
609
  traverse(ast, {
610
+ FunctionDeclaration(p) {
611
+ if (t.isIdentifier(p.node.id)) {
612
+ _localPlugins.set(p.node.id.name, p.node);
613
+ }
614
+ },
505
615
  VariableDeclarator(p) {
506
616
  if (!t.isIdentifier(p.node.id)) return;
507
617
  const id = p.node.id.name;
508
618
  const init = p.node.init;
509
619
  if (!init) return;
510
620
 
621
+ _rawConstInits.set(id, init);
622
+
623
+ // local plugin: const routes = async (fastify) => { ... }
624
+ if (t.isFunctionExpression(init) || t.isArrowFunctionExpression(init)) {
625
+ _localPlugins.set(id, init);
626
+ }
627
+
511
628
  // const app = Fastify()
512
629
  if (t.isCallExpression(init) && t.isIdentifier(init.callee)) {
513
630
  const cal = init.callee.name;
@@ -570,7 +687,7 @@ function resolveFastifyRoutes(repoRoot, entryAbs, stats) {
570
687
 
571
688
  // fastify.get('/x', ...)
572
689
  if (isFastifyMethod(prop)) {
573
- const routeStr = extractStringLiteral(p.node.arguments[0]);
690
+ const routeStr = evalStaticString(p.node.arguments[0]);
574
691
  if (!routeStr) return;
575
692
 
576
693
  const fullPath = joinPaths(prefix, routeStr);
@@ -599,7 +716,7 @@ function resolveFastifyRoutes(repoRoot, entryAbs, stats) {
599
716
  const arg0 = p.node.arguments[0];
600
717
  if (!t.isObjectExpression(arg0)) return;
601
718
 
602
- const r = extractRouteObject(arg0);
719
+ const r = extractRouteObjectV3(arg0);
603
720
  if (!r.url) return;
604
721
 
605
722
  const fullPath = joinPaths(prefix, r.url);
@@ -628,21 +745,31 @@ function resolveFastifyRoutes(repoRoot, entryAbs, stats) {
628
745
 
629
746
  // fastify.register(plugin, { prefix })
630
747
  if (prop === "register") {
631
- const pluginArgRaw = p.node.arguments[0];
748
+ const pluginArgRaw = unwrapPluginArg(p.node.arguments[0]);
632
749
  const optsArg = p.node.arguments[1];
633
750
 
634
- const childPrefixRaw = extractPrefixFromOpts(optsArg);
751
+ const childPrefixRaw = extractPrefixFromOptsV3(optsArg);
635
752
  const childPrefix = childPrefixRaw ? joinPaths(prefix, childPrefixRaw) : prefix;
636
753
 
637
- // inline plugin
638
- if (t.isFunctionExpression(pluginArgRaw) || t.isArrowFunctionExpression(pluginArgRaw)) {
639
- const param0 = pluginArgRaw.params[0];
754
+ // inline plugin OR local plugin identifier (common in real Fastify codebases)
755
+ let pluginFn = null;
756
+ const localIdentName = t.isIdentifier(pluginArgRaw) ? pluginArgRaw.name : null;
757
+ if (t.isFunctionExpression(pluginArgRaw) || t.isArrowFunctionExpression(pluginArgRaw)) pluginFn = pluginArgRaw;
758
+ if (!pluginFn && localIdentName) pluginFn = _localPlugins.get(localIdentName) || null;
759
+
760
+ if (pluginFn) {
761
+ const param0 = pluginFn.params && pluginFn.params[0];
640
762
  const innerName = t.isIdentifier(param0) ? param0.name : "fastify";
763
+ const bodyNode = pluginFn.body;
764
+ if (!t.isBlockStatement(bodyNode)) {
765
+ // We only handle block bodies. Expression-bodied arrows are rare for route plugins.
766
+ if (localIdentName) return;
767
+ }
641
768
 
642
769
  // traverse just the plugin body (best effort)
643
770
  try {
644
771
  traverse(
645
- pluginArgRaw.body,
772
+ bodyNode,
646
773
  {
647
774
  CallExpression(pp) {
648
775
  const c = pp.node.callee;
@@ -653,7 +780,7 @@ function resolveFastifyRoutes(repoRoot, entryAbs, stats) {
653
780
  const pr = c.property.name;
654
781
 
655
782
  if (isFastifyMethod(pr)) {
656
- const rs = extractStringLiteral(pp.node.arguments[0]);
783
+ const rs = evalStaticString(pp.node.arguments[0]);
657
784
  if (!rs) return;
658
785
 
659
786
  const fullPath = joinPaths(childPrefix, rs);
@@ -680,7 +807,7 @@ function resolveFastifyRoutes(repoRoot, entryAbs, stats) {
680
807
  const a0 = pp.node.arguments[0];
681
808
  if (!t.isObjectExpression(a0)) return;
682
809
 
683
- const r = extractRouteObject(a0);
810
+ const r = extractRouteObjectV3(a0);
684
811
  if (!r.url) return;
685
812
 
686
813
  const fullPath = joinPaths(childPrefix, r.url);
@@ -713,6 +840,8 @@ function resolveFastifyRoutes(repoRoot, entryAbs, stats) {
713
840
  // Inner traverse can fail; skip this plugin body
714
841
  }
715
842
 
843
+ // If this was a local plugin identifier, we've already extracted its routes.
844
+ if (localIdentName) return;
716
845
  return;
717
846
  }
718
847
 
@@ -1321,10 +1450,222 @@ function loadTruthpack(repoRoot) {
1321
1450
  }
1322
1451
  }
1323
1452
 
1453
+ // ---------- RepoIndex-powered build (vNext) ----------
1454
+
1455
+ /**
1456
+ * Build truthpack using RepoIndex for single-pass file indexing
1457
+ * This is the optimized path that shares file reads across all extractors.
1458
+ *
1459
+ * Enable with: VIBECHECK_ENGINE_V2=1
1460
+ *
1461
+ * @param {Object} options
1462
+ * @param {string} options.repoRoot
1463
+ * @param {string} [options.fastifyEntry]
1464
+ * @param {boolean} [options.verbose]
1465
+ * @returns {Promise<Object>}
1466
+ */
1467
+ async function buildTruthpackV2({ repoRoot, fastifyEntry, verbose }) {
1468
+ const {
1469
+ createIndex,
1470
+ globalASTCache,
1471
+ logIndexSummary,
1472
+ extractNextAppRoutes,
1473
+ extractNextPagesRoutes,
1474
+ extractClientRefs,
1475
+ extractFastifyRoutes,
1476
+ detectFastifyEntries: detectFastifyEntriesV2,
1477
+ buildEnvTruthV2,
1478
+ buildBillingTruthV2,
1479
+ buildAuthTruthV2,
1480
+ buildEnforcementTruthV2,
1481
+ extractExpressRoutes,
1482
+ } = require("./engine");
1483
+
1484
+ const startTime = Date.now();
1485
+
1486
+ // Phase 0: Build RepoIndex (single glob pass)
1487
+ const index = await createIndex(repoRoot);
1488
+
1489
+ if (verbose || process.env.VIBECHECK_VERBOSE) {
1490
+ logIndexSummary(index);
1491
+ console.log(` AST Cache: ${globalASTCache.getSummary().hitRate} hit rate`);
1492
+ }
1493
+
1494
+ const stats = {
1495
+ parseErrors: 0,
1496
+ fastifyEntries: 0,
1497
+ fastifyRoutes: 0,
1498
+ nextAppRoutes: 0,
1499
+ nextPagesRoutes: 0,
1500
+ clientRefs: 0,
1501
+ serverRoutes: 0,
1502
+ gaps: 0,
1503
+ indexTimeMs: index.stats.indexTimeMs,
1504
+ };
1505
+
1506
+ // Use signals from index instead of re-detecting
1507
+ const detectedFrameworks = Array.from(index.signals.detectedFrameworks);
1508
+
1509
+ // Workspaces
1510
+ const workspaces = detectWorkspaces(repoRoot);
1511
+
1512
+ // Phase 1: Extract routes using optimized extractors (use RepoIndex + AST cache)
1513
+
1514
+ // Next.js routes - use optimized extractors
1515
+ const nextApp = extractNextAppRoutes(index, stats);
1516
+ const nextPages = extractNextPagesRoutes(index, stats);
1517
+
1518
+ stats.nextAppRoutes = nextApp.length;
1519
+ stats.nextPagesRoutes = nextPages.length;
1520
+
1521
+ // Fastify routes - use optimized extractor
1522
+ let fastify = { routes: [], gaps: [] };
1523
+
1524
+ if (index.hasFramework("fastify") || fastifyEntry) {
1525
+ if (fastifyEntry) {
1526
+ const entryAbs = path.isAbsolute(fastifyEntry) ? fastifyEntry : path.join(repoRoot, fastifyEntry);
1527
+ if (exists(entryAbs)) {
1528
+ const resolved = extractFastifyRoutes(index, entryAbs, stats);
1529
+ fastify.routes.push(...resolved.routes);
1530
+ fastify.gaps.push(...resolved.gaps);
1531
+ stats.fastifyEntries = 1;
1532
+ }
1533
+ } else {
1534
+ const entries = detectFastifyEntriesV2(index);
1535
+ stats.fastifyEntries = entries.length;
1536
+
1537
+ for (const entryAbs of entries) {
1538
+ const resolved = extractFastifyRoutes(index, entryAbs, stats);
1539
+ fastify.routes.push(...resolved.routes);
1540
+ fastify.gaps.push(...resolved.gaps);
1541
+ }
1542
+ }
1543
+ }
1544
+
1545
+ stats.fastifyRoutes = fastify.routes.length;
1546
+
1547
+ // Express routes - use optimized extractor
1548
+ const expressRoutes = extractExpressRoutes(index, stats);
1549
+
1550
+ // Multi-framework routes (Flask, Django, etc. - still uses old resolvers for non-JS frameworks)
1551
+ // Express is handled above via optimized extractor
1552
+ const multiFramework = await resolveAllRoutes(repoRoot, {
1553
+ mode: process.env.VIBECHECK_ROUTE_SCAN_MODE || "smart",
1554
+ verbose: verbose || !!process.env.VIBECHECK_VERBOSE_ROUTES,
1555
+ skipExpress: true, // Skip Express since we handle it above with optimized extractor
1556
+ });
1557
+
1558
+ // Client refs - use optimized extractor
1559
+ const clientRefsOptimized = extractClientRefs(index, stats);
1560
+ const allClientRefs = [...clientRefsOptimized, ...(multiFramework.clientRefs || [])];
1561
+
1562
+ stats.clientRefs = allClientRefs.length;
1563
+
1564
+ // Merge server routes (including optimized Express routes)
1565
+ const serverRoutesRaw = [...nextApp, ...nextPages, ...(fastify.routes || []), ...expressRoutes, ...(multiFramework.routes || [])];
1566
+
1567
+ const bestByKey = new Map();
1568
+ for (const r of serverRoutesRaw) {
1569
+ const key = `${canonicalizeMethod(r.method)}:${canonicalizePath(r.path)}`;
1570
+ const prev = bestByKey.get(key);
1571
+ if (!prev) {
1572
+ bestByKey.set(key, { ...r, method: canonicalizeMethod(r.method), path: canonicalizePath(r.path) });
1573
+ continue;
1574
+ }
1575
+ const prevScore = scoreConfidence(prev.confidence) + (prev.method === "*" ? 0 : 1);
1576
+ const curScore = scoreConfidence(r.confidence) + (r.method === "*" ? 0 : 1);
1577
+ if (curScore > prevScore) {
1578
+ bestByKey.set(key, { ...r, method: canonicalizeMethod(r.method), path: canonicalizePath(r.path) });
1579
+ }
1580
+ }
1581
+
1582
+ const server = Array.from(bestByKey.values());
1583
+ stats.serverRoutes = server.length;
1584
+
1585
+ // Merge gaps
1586
+ const allGaps = [...(fastify.gaps || []), ...(multiFramework.gaps || [])];
1587
+ stats.gaps = allGaps.length;
1588
+
1589
+ // Phase 2: Build other truths (env, auth, billing, enforcement)
1590
+ // All use optimized extractors with RepoIndex
1591
+ const env = buildEnvTruthV2(index, stats);
1592
+ const auth = buildAuthTruthV2(index, server);
1593
+ const billing = buildBillingTruthV2(index, stats);
1594
+ const enforcement = buildEnforcementTruthV2(index, server);
1595
+
1596
+ // Determine frameworks
1597
+ const frameworks = new Set(detectedFrameworks);
1598
+ server.forEach((r) => r.framework && frameworks.add(r.framework));
1599
+ if (nextApp.length || nextPages.length) frameworks.add("next");
1600
+ if (fastify.routes.length) frameworks.add("fastify");
1601
+
1602
+ stats.totalTimeMs = Date.now() - startTime;
1603
+
1604
+ const truthpack = {
1605
+ meta: {
1606
+ version: "2.2.0", // Bump version for v2 engine
1607
+ generatedAt: new Date().toISOString(),
1608
+ repoRoot,
1609
+ commit: { sha: process.env.VIBECHECK_COMMIT_SHA || "unknown" },
1610
+ stats,
1611
+ engine: "v2", // Mark as v2 engine
1612
+ },
1613
+ project: {
1614
+ frameworks: Array.from(frameworks),
1615
+ workspaces,
1616
+ entrypoints: {
1617
+ fastify: fastifyEntry ? [fastifyEntry] : [],
1618
+ },
1619
+ },
1620
+ routes: { server, clientRefs: allClientRefs, gaps: allGaps },
1621
+ env,
1622
+ auth,
1623
+ billing,
1624
+ enforcement,
1625
+ };
1626
+
1627
+ const hash = sha256(JSON.stringify(truthpack));
1628
+ truthpack.index = {
1629
+ hashes: { truthpackHash: hash },
1630
+ evidenceRefs: [],
1631
+ repoIndex: {
1632
+ totalFiles: index.stats.totalFiles,
1633
+ totalSize: index.stats.totalSize,
1634
+ indexTimeMs: index.stats.indexTimeMs,
1635
+ },
1636
+ };
1637
+
1638
+ // Clear caches to free memory
1639
+ index.clearContentCache();
1640
+ clearCache();
1641
+
1642
+ return truthpack;
1643
+ }
1644
+
1645
+ /**
1646
+ * Smart buildTruthpack - uses v2 engine by default for better performance.
1647
+ * Set VIBECHECK_ENGINE_V1=1 to fall back to v1 for backward compatibility.
1648
+ *
1649
+ * V2 Engine Benefits:
1650
+ * - Single-pass file indexing (RepoIndex)
1651
+ * - Shared AST cache (globalASTCache)
1652
+ * - Token prefiltering for faster file selection
1653
+ * - ~30% faster on typical projects
1654
+ */
1655
+ async function buildTruthpackSmart(options) {
1656
+ if (process.env.VIBECHECK_ENGINE_V1 === "1") {
1657
+ return buildTruthpack(options);
1658
+ }
1659
+ // V2 is now the default - uses RepoIndex + AST cache
1660
+ return buildTruthpackV2(options);
1661
+ }
1662
+
1324
1663
  module.exports = {
1325
1664
  canonicalizeMethod,
1326
1665
  canonicalizePath,
1327
1666
  buildTruthpack,
1667
+ buildTruthpackV2,
1668
+ buildTruthpackSmart,
1328
1669
  writeTruthpack,
1329
1670
  loadTruthpack,
1330
1671
  clearCache, // Clear file cache to free memory (important for long-running processes)