@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
@@ -1,11 +1,6 @@
1
1
  /**
2
- * vibecheck ship - Comprehensive Production Readiness Analysis [PRO]
3
- *
4
- * The comprehensive analysis command running:
5
- * - ALL 17+ scan engines (vs 5 in quick scan)
6
- * - Ship-only validators (route integrity, contracts, deps, licenses, env, dead exports)
7
- * - Production Readiness Score calculation
8
- * - SHIP/WARN/BLOCK verdict with proof certificate
2
+ * vibecheck ship - The Vibe Coder's Best Friend
3
+ * Zero config. Plain English. One command to ship with confidence.
9
4
  *
10
5
  * ═══════════════════════════════════════════════════════════════════════════════
11
6
  * ENTERPRISE EDITION - World-Class Terminal Experience
@@ -16,7 +11,7 @@ const path = require("path");
16
11
  const fs = require("fs");
17
12
  const { withErrorHandling } = require("./lib/error-handler");
18
13
  const { ensureOutputDir, detectProjectFeatures } = require("./utils");
19
- const { enforceLimit, enforceFeature, trackUsage, getCurrentTier } = require("./lib/entitlements-v2");
14
+ const { enforceLimit, enforceFeature, trackUsage, getCurrentTier } = require("./lib/entitlements");
20
15
  const { emitShipCheck } = require("./lib/audit-bridge");
21
16
  const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
22
17
  const {
@@ -27,62 +22,8 @@ const {
27
22
  } = require("./lib/cli-output");
28
23
  const { EXIT, verdictToExitCode, exitCodeToVerdict } = require("./lib/exit-codes");
29
24
 
30
- // NEW: Import orchestrator and validators
31
- const { runShipEngines, ALL_ENGINES } = require("./lib/engines/orchestrator");
32
- const { runShipValidators } = require("./lib/validators");
33
-
34
- // Route Truth v1 - Fake endpoint detection
35
- const { buildTruthpack, writeTruthpack, detectFastifyEntry } = require("./lib/truth");
36
-
37
- // Helper to normalize severity for ship verdict
38
- function normalizeSeverityForShip(sev) {
39
- if (!sev) return 'WARN';
40
- const s = String(sev).toLowerCase();
41
- if (s === 'block' || s === 'critical' || s === 'high') return 'BLOCK';
42
- if (s === 'warn' || s === 'warning' || s === 'medium') return 'WARN';
43
- return 'INFO';
44
- }
45
-
46
- // Helper to categorize findings for score breakdown
47
- function categorizeFindings(finding) {
48
- const category = (finding.category || '').toLowerCase();
49
- const engine = (finding.engine || '').toLowerCase();
50
- const validator = (finding.validator || '').toLowerCase();
51
-
52
- // Security
53
- if (category.includes('secret') || category.includes('security') ||
54
- category.includes('auth') || category.includes('billing') ||
55
- engine.includes('secret') || engine.includes('security')) {
56
- return 'security';
57
- }
58
-
59
- // Hallucinations
60
- if (category.includes('hallucination') || category.includes('fake') ||
61
- category.includes('mock') || engine.includes('hallucination')) {
62
- return 'hallucination';
63
- }
64
-
65
- // Routes
66
- if (category.includes('route') || category.includes('missing') ||
67
- validator.includes('route')) {
68
- return 'routes';
69
- }
70
-
71
- // Contracts
72
- if (category.includes('contract') || category.includes('drift') ||
73
- validator.includes('contract')) {
74
- return 'contracts';
75
- }
76
-
77
- // Dependencies
78
- if (category.includes('dependency') || category.includes('license') ||
79
- validator.includes('dep') || validator.includes('license')) {
80
- return 'dependencies';
81
- }
82
-
83
- // Default to quality
84
- return 'quality';
85
- }
25
+ // Route Truth - Fake endpoint detection (V2 engine-aware)
26
+ const { buildTruthpackSmart, writeTruthpack, detectFastifyEntry } = require("./lib/truth");
86
27
  const {
87
28
  findMissingRoutes,
88
29
  findEnvGaps,
@@ -90,220 +31,18 @@ const {
90
31
  findGhostAuth,
91
32
  findStripeWebhookViolations,
92
33
  findPaidSurfaceNotEnforced,
93
- findOwnerModeBypass
34
+ findOwnerModeBypass,
35
+ // NEW: AI Hallucination Detectors
36
+ findOptimisticNoRollback,
37
+ findSilentCatch,
38
+ findMethodMismatch,
39
+ findDeadUI,
94
40
  } = require("./lib/analyzers");
95
41
  const { findingsFromReality } = require("./lib/reality-findings");
96
42
  const { findContractDrift, loadContracts, hasContracts, getDriftSummary } = require("./lib/drift");
97
43
  const upsell = require("./lib/upsell");
98
44
  const entitlements = require("./lib/entitlements-v2");
99
45
 
100
- // V7: World-class proof certificate and risk radar
101
- let proofCertificate;
102
- try {
103
- proofCertificate = require("./lib/proof-certificate");
104
- } catch (e) {
105
- proofCertificate = null;
106
- }
107
-
108
- // Import vibecheck engines for enhanced analysis
109
- let vibeEngines;
110
- try {
111
- vibeEngines = require("./lib/engines/vibecheck-engines");
112
- } catch (e) {
113
- vibeEngines = null;
114
- }
115
-
116
- // ═══════════════════════════════════════════════════════════════════════════════
117
- // ENHANCED ANALYSIS WITH VIBECHECK ENGINES
118
- // ═══════════════════════════════════════════════════════════════════════════════
119
-
120
- const CODE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
121
- const IGNORE_PATTERNS = ['node_modules', '.git', 'dist', 'build', '.next', '.nuxt', 'coverage', '__pycache__', '.vibecheck'];
122
-
123
- function* walkFilesForShip(dir, depth = 0, maxDepth = 8) {
124
- if (depth > maxDepth) return;
125
-
126
- let entries;
127
- try {
128
- entries = fs.readdirSync(dir, { withFileTypes: true });
129
- } catch {
130
- return;
131
- }
132
-
133
- for (const entry of entries) {
134
- const fullPath = path.join(dir, entry.name);
135
-
136
- if (entry.isDirectory()) {
137
- if (IGNORE_PATTERNS.some(p => entry.name.includes(p))) continue;
138
- yield* walkFilesForShip(fullPath, depth + 1, maxDepth);
139
- } else if (entry.isFile()) {
140
- const ext = path.extname(entry.name);
141
- if (CODE_EXTENSIONS.includes(ext)) {
142
- yield fullPath;
143
- }
144
- }
145
- }
146
- }
147
-
148
- async function runVibeEngineAnalysis(projectPath, opts = {}) {
149
- if (!vibeEngines) return [];
150
-
151
- const findings = [];
152
- const files = Array.from(walkFilesForShip(projectPath));
153
-
154
- // Limit files to analyze in ship mode (performance)
155
- const filesToAnalyze = files.slice(0, 500);
156
-
157
- for (const filePath of filesToAnalyze) {
158
- try {
159
- const content = fs.readFileSync(filePath, 'utf-8');
160
-
161
- // Skip test files unless explicitly included
162
- if (!opts.includeTests && (
163
- filePath.includes('.test.') ||
164
- filePath.includes('.spec.') ||
165
- filePath.includes('__tests__')
166
- )) {
167
- continue;
168
- }
169
-
170
- // Run async patterns analysis - critical for production
171
- if (vibeEngines.analyzeAsyncPatterns) {
172
- try {
173
- const asyncFindings = vibeEngines.analyzeAsyncPatterns(content, filePath);
174
- findings.push(...asyncFindings.filter(f =>
175
- f.severity === 'BLOCK' || f.severity === 'WARN'
176
- ).map(f => ({
177
- ...f,
178
- category: f.category || 'AsyncPatterns',
179
- // Map to ship finding format
180
- title: f.title,
181
- why: f.message,
182
- severity: f.severity,
183
- evidence: [{ file: f.file, lines: String(f.line) }],
184
- fixHints: [f.fixHint].filter(Boolean),
185
- })));
186
- } catch {}
187
- }
188
-
189
- // Run env variable analysis - security critical
190
- if (vibeEngines.analyzeEnvVariables) {
191
- try {
192
- const envFindings = vibeEngines.analyzeEnvVariables(content, filePath);
193
- findings.push(...envFindings.filter(f =>
194
- f.severity === 'BLOCK' || f.severity === 'WARN'
195
- ).map(f => ({
196
- ...f,
197
- category: f.category || 'EnvVariable',
198
- title: f.title,
199
- why: f.message,
200
- severity: f.severity,
201
- evidence: [{ file: f.file, lines: String(f.line) }],
202
- fixHints: [f.fixHint].filter(Boolean),
203
- })));
204
- } catch {}
205
- }
206
-
207
- // Run AI hallucination detection - core vibecheck functionality
208
- if (vibeEngines.analyzeAIHallucinations) {
209
- try {
210
- const aiFindings = vibeEngines.analyzeAIHallucinations(content, filePath, {
211
- includeTests: opts.includeTests || false,
212
- });
213
- findings.push(...aiFindings.filter(f =>
214
- f.severity === 'BLOCK' || f.severity === 'WARN'
215
- ).map(f => ({
216
- ...f,
217
- category: f.category || 'AIHallucination',
218
- title: f.title || f.type,
219
- why: f.message,
220
- severity: f.severity,
221
- evidence: [{ file: f.file, lines: String(f.line) }],
222
- fixHints: [f.fixHint].filter(Boolean),
223
- })));
224
- } catch {}
225
- }
226
-
227
- // Run React patterns analysis (critical for React apps)
228
- if (vibeEngines.analyzeReactPatterns) {
229
- try {
230
- const reactFindings = vibeEngines.analyzeReactPatterns(content, filePath);
231
- findings.push(...reactFindings.filter(f =>
232
- f.severity === 'BLOCK' || f.severity === 'WARN'
233
- ).map(f => ({
234
- ...f,
235
- category: f.category || 'ReactPatterns',
236
- title: f.title,
237
- why: f.message,
238
- severity: f.severity,
239
- evidence: [{ file: f.file, lines: String(f.line) }],
240
- fixHints: [f.fixHint].filter(Boolean),
241
- })));
242
- } catch {}
243
- }
244
-
245
- // Run database patterns analysis (critical for production)
246
- if (vibeEngines.analyzeDatabasePatterns) {
247
- try {
248
- const dbFindings = vibeEngines.analyzeDatabasePatterns(content, filePath);
249
- findings.push(...dbFindings.filter(f =>
250
- f.severity === 'BLOCK' || f.severity === 'WARN'
251
- ).map(f => ({
252
- ...f,
253
- category: f.category || 'DatabasePatterns',
254
- title: f.title,
255
- why: f.message,
256
- severity: f.severity,
257
- evidence: [{ file: f.file, lines: String(f.line) }],
258
- fixHints: [f.fixHint].filter(Boolean),
259
- })));
260
- } catch {}
261
- }
262
-
263
- // Run error handling analysis
264
- if (vibeEngines.analyzeErrorHandling) {
265
- try {
266
- const errorFindings = vibeEngines.analyzeErrorHandling(content, filePath);
267
- findings.push(...errorFindings.filter(f =>
268
- f.severity === 'BLOCK' || f.severity === 'WARN'
269
- ).map(f => ({
270
- ...f,
271
- category: f.category || 'ErrorHandling',
272
- title: f.title,
273
- why: f.message,
274
- severity: f.severity,
275
- evidence: [{ file: f.file, lines: String(f.line) }],
276
- fixHints: [f.fixHint].filter(Boolean),
277
- })));
278
- } catch {}
279
- }
280
-
281
- } catch (e) {
282
- // Skip files that can't be read
283
- }
284
- }
285
-
286
- // Run env setup analysis at project level
287
- if (vibeEngines.analyzeEnvSetup) {
288
- try {
289
- const setupFindings = vibeEngines.analyzeEnvSetup(projectPath);
290
- findings.push(...setupFindings.filter(f =>
291
- f.severity === 'BLOCK' || f.severity === 'WARN'
292
- ).map(f => ({
293
- ...f,
294
- category: f.category || 'EnvSetup',
295
- title: f.title,
296
- why: f.message,
297
- severity: f.severity,
298
- evidence: [{ file: f.file, lines: String(f.line) }],
299
- fixHints: [f.fixHint].filter(Boolean),
300
- })));
301
- } catch {}
302
- }
303
-
304
- return findings;
305
- }
306
-
307
46
  // ═══════════════════════════════════════════════════════════════════════════════
308
47
  // ENHANCED TERMINAL UI & OUTPUT MODULES
309
48
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -329,9 +68,6 @@ const {
329
68
  shipIcons,
330
69
  } = require("./lib/ship-output");
331
70
 
332
- // Unified Output System
333
- const { output } = require("./lib/output/index.js");
334
-
335
71
  const {
336
72
  formatShipOutput,
337
73
  } = require("./lib/ship-output-enterprise");
@@ -628,8 +364,6 @@ function getCategoryIcon(category) {
628
364
  'MissingRoute': ICONS.route,
629
365
  'EnvContract': ICONS.env,
630
366
  'EnvGap': ICONS.env,
631
- 'EnvVariable': ICONS.env,
632
- 'EnvSetup': ICONS.env,
633
367
  'FakeSuccess': ICONS.ghost,
634
368
  'GhostAuth': ICONS.auth,
635
369
  'StripeWebhook': ICONS.money,
@@ -640,33 +374,6 @@ function getCategoryIcon(category) {
640
374
  'Security': ICONS.shield,
641
375
  'Auth': ICONS.lock,
642
376
  'Fake Code': ICONS.ghost,
643
- // New engine categories
644
- 'AsyncPatterns': ICONS.lightning,
645
- 'AIHallucination': ICONS.ghost,
646
- 'NamingConventions': '📝',
647
- 'floating_promise': ICONS.lightning,
648
- 'empty_async_catch': ICONS.bug,
649
- 'exposed_secret': ICONS.key,
650
- 'insecure_default': ICONS.warning,
651
- // React patterns
652
- 'ReactPatterns': '⚛️',
653
- 'missing_key': '⚛️',
654
- 'conditional_hook': '⚛️',
655
- 'direct_state_mutation': '⚛️',
656
- 'stale_closure': '⚛️',
657
- // Database patterns
658
- 'DatabasePatterns': '🗃️',
659
- 'n_plus_1_query': '🗃️',
660
- 'query_in_loop': '🗃️',
661
- 'unbounded_query': '🗃️',
662
- 'missing_transaction': '🗃️',
663
- 'raw_query_interpolation': ICONS.key,
664
- // Error handling
665
- 'ErrorHandling': ICONS.bug,
666
- 'empty_catch': ICONS.bug,
667
- 'generic_error_message': ICONS.bug,
668
- // Import order
669
- 'ImportOrder': '📦',
670
377
  };
671
378
  return icons[category] || ICONS.bug;
672
379
  }
@@ -931,11 +638,6 @@ function printHelp(showBanner = true) {
931
638
  ${colors.accent}--verbose, -v${ansi.reset} Show detailed progress
932
639
  ${colors.accent}--help, -h${ansi.reset} Show this help
933
640
 
934
- ${ansi.bold}${colors.accent}Deploy Gate (CI/CD Integration):${ansi.reset}
935
- ${colors.accent}--gate, -g${ansi.reset} Enable deploy gate mode ${ansi.dim}(blocks deploys on failures)${ansi.reset}
936
- ${colors.accent}--fail-on${ansi.reset} What triggers failure: fake-features, warnings, any, blockers
937
- ${ansi.dim}(default: fake-features)${ansi.reset}
938
-
939
641
  ${ansi.bold}Exit Codes:${ansi.reset}
940
642
  ${colors.success}0${ansi.reset} SHIP — Ready to ship
941
643
  ${colors.warning}1${ansi.reset} WARN — Warnings found, review recommended
@@ -953,20 +655,6 @@ function printHelp(showBanner = true) {
953
655
 
954
656
  ${ansi.dim}# Strict CI mode (warnings = failure)${ansi.reset}
955
657
  vibecheck ship --strict --ci
956
-
957
- ${ansi.bold}${colors.accent}Deploy Gate Examples:${ansi.reset}
958
-
959
- ${ansi.dim}# Block deploy if fake features detected (default)${ansi.reset}
960
- vibecheck ship --gate
961
-
962
- ${ansi.dim}# Block deploy on any warning${ansi.reset}
963
- vibecheck ship --gate --fail-on=warnings
964
-
965
- ${ansi.dim}# Block deploy only on critical blockers${ansi.reset}
966
- vibecheck ship --gate --fail-on=blockers
967
-
968
- ${ansi.dim}# Use in GitHub Actions / Vercel / Netlify${ansi.reset}
969
- vibecheck ship --gate && npm run build
970
658
  `);
971
659
  }
972
660
 
@@ -1048,40 +736,13 @@ function getClaimType(category) {
1048
736
  const map = {
1049
737
  'MissingRoute': 'route_exists',
1050
738
  'EnvContract': 'env_declared',
1051
- 'EnvVariable': 'env_validated',
1052
- 'EnvSetup': 'env_secured',
1053
739
  'FakeSuccess': 'success_verified',
1054
740
  'GhostAuth': 'auth_protected',
1055
741
  'StripeWebhook': 'billing_enforced',
1056
742
  'PaidSurface': 'billing_enforced',
1057
743
  'OwnerModeBypass': 'billing_enforced',
1058
744
  'DeadUI': 'ui_wired',
1059
- 'ContractDrift': 'contract_satisfied',
1060
- // New engine claim types
1061
- 'AsyncPatterns': 'promises_handled',
1062
- 'AIHallucination': 'implementation_real',
1063
- 'NamingConventions': 'code_quality',
1064
- 'floating_promise': 'promises_handled',
1065
- 'empty_async_catch': 'errors_handled',
1066
- 'exposed_secret': 'secrets_secured',
1067
- 'insecure_default': 'config_secured',
1068
- // React patterns
1069
- 'ReactPatterns': 'react_patterns_valid',
1070
- 'missing_key': 'list_keys_present',
1071
- 'conditional_hook': 'hooks_order_valid',
1072
- 'direct_state_mutation': 'state_immutable',
1073
- 'stale_closure': 'deps_correct',
1074
- // Database patterns
1075
- 'DatabasePatterns': 'db_patterns_optimal',
1076
- 'n_plus_1_query': 'queries_optimized',
1077
- 'query_in_loop': 'queries_batched',
1078
- 'unbounded_query': 'queries_limited',
1079
- 'missing_transaction': 'transactions_used',
1080
- 'raw_query_interpolation': 'queries_parameterized',
1081
- // Error handling
1082
- 'ErrorHandling': 'errors_handled',
1083
- 'empty_catch': 'errors_logged',
1084
- 'generic_error_message': 'errors_descriptive',
745
+ 'ContractDrift': 'contract_satisfied'
1085
746
  };
1086
747
  return map[category] || 'ui_wired';
1087
748
  }
@@ -1090,40 +751,13 @@ function getGapType(category) {
1090
751
  const map = {
1091
752
  'MissingRoute': 'missing_handler',
1092
753
  'EnvContract': 'missing_verification',
1093
- 'EnvVariable': 'missing_validation',
1094
- 'EnvSetup': 'missing_security',
1095
754
  'FakeSuccess': 'missing_verification',
1096
755
  'GhostAuth': 'missing_gate',
1097
756
  'StripeWebhook': 'missing_verification',
1098
757
  'PaidSurface': 'missing_gate',
1099
758
  'OwnerModeBypass': 'missing_gate',
1100
759
  'DeadUI': 'missing_handler',
1101
- 'ContractDrift': 'contract_drift',
1102
- // New engine gap types
1103
- 'AsyncPatterns': 'unhandled_async',
1104
- 'AIHallucination': 'stub_implementation',
1105
- 'NamingConventions': 'naming_issue',
1106
- 'floating_promise': 'unhandled_promise',
1107
- 'empty_async_catch': 'swallowed_error',
1108
- 'exposed_secret': 'exposed_credential',
1109
- 'insecure_default': 'insecure_config',
1110
- // React patterns
1111
- 'ReactPatterns': 'react_antipattern',
1112
- 'missing_key': 'missing_list_key',
1113
- 'conditional_hook': 'invalid_hook_call',
1114
- 'direct_state_mutation': 'state_mutation',
1115
- 'stale_closure': 'stale_deps',
1116
- // Database patterns
1117
- 'DatabasePatterns': 'db_antipattern',
1118
- 'n_plus_1_query': 'n_plus_1',
1119
- 'query_in_loop': 'loop_query',
1120
- 'unbounded_query': 'no_limit',
1121
- 'missing_transaction': 'no_transaction',
1122
- 'raw_query_interpolation': 'sql_injection_risk',
1123
- // Error handling
1124
- 'ErrorHandling': 'poor_error_handling',
1125
- 'empty_catch': 'swallowed_error',
1126
- 'generic_error_message': 'generic_error',
760
+ 'ContractDrift': 'contract_drift'
1127
761
  };
1128
762
  return map[category] || 'untested_path';
1129
763
  }
@@ -1150,10 +784,6 @@ function parseArgs(args) {
1150
784
  help: globalFlags.help || false,
1151
785
  noBanner: globalFlags.noBanner || false,
1152
786
  quiet: globalFlags.quiet || false,
1153
- // Deploy Gate mode - for CI/CD integration (Vercel, Netlify, GitHub Actions)
1154
- gate: false,
1155
- // Deploy Gate options
1156
- failOn: "fake-features", // fake-features, warnings, any
1157
787
  };
1158
788
 
1159
789
  // Parse command-specific args
@@ -1169,21 +799,6 @@ function parseArgs(args) {
1169
799
  }
1170
800
  else if (a.startsWith("--path=")) opts.path = a.split("=")[1];
1171
801
  else if (a === "--path" || a === "-p") opts.path = args[++i];
1172
- // Deploy Gate flags for CI/CD integration
1173
- else if (a === "--gate" || a === "-g") {
1174
- opts.gate = true;
1175
- opts.ci = true; // Gate mode implies CI mode
1176
- opts.json = true; // Gate mode outputs JSON for parsing
1177
- }
1178
- else if (a === "--fail-on") {
1179
- const next = cleanArgs[++i];
1180
- if (["fake-features", "warnings", "any", "blockers"].includes(next)) {
1181
- opts.failOn = next;
1182
- }
1183
- }
1184
- else if (a.startsWith("--fail-on=")) {
1185
- opts.failOn = a.split("=")[1];
1186
- }
1187
802
  }
1188
803
 
1189
804
  return opts;
@@ -1201,13 +816,6 @@ async function runShip(args, context = {}) {
1201
816
  const opts = parseArgs(args);
1202
817
  const executionStart = Date.now();
1203
818
 
1204
- // Configure unified output mode
1205
- output.setMode({
1206
- json: opts.json,
1207
- quiet: opts.quiet,
1208
- ci: opts.ci
1209
- });
1210
-
1211
819
  // Show help if requested
1212
820
  if (opts.help) {
1213
821
  printHelp(shouldShowBanner(opts));
@@ -1255,59 +863,72 @@ async function runShip(args, context = {}) {
1255
863
  proofGraph: null,
1256
864
  };
1257
865
 
1258
- try {
1259
- // Initialize spinner
1260
- let spinner;
1261
- if (!opts.json && !opts.ci) {
1262
- spinner = new Spinner({ color: colors.accent });
1263
- }
866
+ // Initialize spinner outside try block so it's in scope for catch
867
+ let spinner;
868
+ if (!opts.json && !opts.ci) {
869
+ spinner = new Spinner({ color: colors.accent });
870
+ }
1264
871
 
1265
- // ═══════════════════════════════════════════════════════════════════════════
1266
- // PHASE 1: Run ALL 17+ Scan Engines (comprehensive analysis)
1267
- // ═══════════════════════════════════════════════════════════════════════════
1268
- if (spinner) spinner.start(`Running comprehensive analysis (${ALL_ENGINES.length} engines)...`);
1269
-
1270
- const engineResult = await runShipEngines(projectPath, {
1271
- maxFiles: 2000,
1272
- onProgress: opts.verbose ? (phase, pct) => {
1273
- if (spinner) spinner.text = `${phase}: ${pct}%`;
1274
- } : undefined,
1275
- });
1276
-
1277
- if (spinner) spinner.succeed(`Engines complete (${engineResult.findings.length} findings from ${ALL_ENGINES.length} engines)`);
872
+ try {
1278
873
 
1279
- // ═══════════════════════════════════════════════════════════════════════════
1280
- // PHASE 2: Run Ship-Only Validators
1281
- // ═══════════════════════════════════════════════════════════════════════════
1282
- if (spinner) spinner.start('Running ship-only validators...');
874
+ // Phase 1: Production Integrity Check
875
+ if (spinner) spinner.start('Checking production integrity...');
1283
876
 
1284
- const validatorResult = await runShipValidators(projectPath, {
1285
- onProgress: opts.verbose ? (name, status) => {
1286
- if (spinner) spinner.text = `${name}: ${status}`;
1287
- } : undefined,
1288
- });
877
+ try {
878
+ const { auditProductionIntegrity } = require(
879
+ path.join(__dirname, "../../scripts/audit-production-integrity.js"),
880
+ );
881
+ const { results: integrityResults, integrity } = await auditProductionIntegrity(projectPath);
882
+ results.score = integrity.score;
883
+ results.grade = integrity.grade;
884
+ results.canShip = integrity.canShip;
885
+ results.deductions = integrity.deductions;
886
+ results.integrity = integrityResults;
887
+ } catch (err) {
888
+ if (opts.verbose) console.warn(` ${ansi.dim}Integrity check skipped: ${err.message}${ansi.reset}`);
889
+ }
1289
890
 
1290
- if (spinner) spinner.succeed(`Validators complete (${validatorResult.findings.length} findings from ${Object.keys(validatorResult.stats.findingsPerValidator).length} validators)`);
891
+ if (spinner) spinner.succeed('Production integrity checked');
1291
892
 
1292
- // ═══════════════════════════════════════════════════════════════════════════
1293
- // PHASE 3: Legacy analyzers (route truth, billing, etc.)
1294
- // ═══════════════════════════════════════════════════════════════════════════
893
+ // Phase 2: Route Truth Analysis
1295
894
  if (spinner) spinner.start('Building route truth map...');
1296
895
 
1297
896
  const fastifyEntry = detectFastifyEntry(projectPath);
1298
- const truthpack = await buildTruthpack({ repoRoot: projectPath, fastifyEntry });
897
+ const truthpack = await buildTruthpackSmart({ repoRoot: projectPath, fastifyEntry });
1299
898
  writeTruthpack(projectPath, truthpack);
1300
899
  results.truthpack = truthpack;
1301
900
 
1302
- // Run legacy analyzers that aren't in the orchestrator
1303
- const legacyFindings = [
1304
- ...findMissingRoutes(truthpack),
1305
- ...findEnvGaps(truthpack),
1306
- ...findFakeSuccess(projectPath),
1307
- ...findGhostAuth(truthpack, projectPath),
1308
- ...findStripeWebhookViolations(truthpack),
1309
- ...findPaidSurfaceNotEnforced(truthpack),
1310
- ...findOwnerModeBypass(projectPath),
901
+ // Run all analyzers in parallel for performance (independent analyzers)
902
+ // Group 1: File-based analyzers (can run fully in parallel)
903
+ const fileAnalyzerPromises = [
904
+ Promise.resolve(findFakeSuccess(projectPath)),
905
+ Promise.resolve(findOptimisticNoRollback(projectPath)),
906
+ Promise.resolve(findSilentCatch(projectPath)),
907
+ Promise.resolve(findDeadUI(projectPath)),
908
+ Promise.resolve(findOwnerModeBypass(projectPath)),
909
+ ];
910
+
911
+ // Group 2: Truthpack-based analyzers (depend on truthpack but not each other)
912
+ const truthpackAnalyzerPromises = [
913
+ Promise.resolve(findMissingRoutes(truthpack)),
914
+ Promise.resolve(findMethodMismatch(truthpack)),
915
+ Promise.resolve(findEnvGaps(truthpack)),
916
+ Promise.resolve(findGhostAuth(truthpack, projectPath)),
917
+ Promise.resolve(findStripeWebhookViolations(truthpack)),
918
+ Promise.resolve(findPaidSurfaceNotEnforced(truthpack)),
919
+ ];
920
+
921
+ // Run both groups in parallel
922
+ const [fileResults, truthpackResults] = await Promise.all([
923
+ Promise.all(fileAnalyzerPromises),
924
+ Promise.all(truthpackAnalyzerPromises),
925
+ ]);
926
+
927
+ // Flatten results
928
+ const allFindings = [
929
+ ...fileResults.flat(),
930
+ ...truthpackResults.flat(),
931
+ // Runtime findings (if requested)
1311
932
  ...(opts.withRuntime ? findingsFromReality(projectPath) : [])
1312
933
  ];
1313
934
 
@@ -1315,38 +936,12 @@ async function runShip(args, context = {}) {
1315
936
  if (hasContracts(projectPath)) {
1316
937
  const contracts = loadContracts(projectPath);
1317
938
  const driftFindings = findContractDrift(contracts, truthpack);
1318
- legacyFindings.push(...driftFindings);
939
+ allFindings.push(...driftFindings);
1319
940
  }
1320
941
 
1321
- if (spinner) spinner.succeed(`Route truth mapped (${truthpack.routes?.server?.length || 0} routes)`);
1322
-
1323
- // ═══════════════════════════════════════════════════════════════════════════
1324
- // Combine all findings
1325
- // ═══════════════════════════════════════════════════════════════════════════
1326
- const allFindings = [
1327
- // Engine findings (17+ engines)
1328
- ...engineResult.findings.map(f => ({
1329
- ...f,
1330
- source: 'engine',
1331
- severity: normalizeSeverityForShip(f.severity),
1332
- })),
1333
- // Validator findings (6 ship-only validators)
1334
- ...validatorResult.findings.map(f => ({
1335
- ...f,
1336
- source: 'validator',
1337
- severity: normalizeSeverityForShip(f.severity),
1338
- })),
1339
- // Legacy analyzer findings
1340
- ...legacyFindings.map(f => ({
1341
- ...f,
1342
- source: 'legacy',
1343
- severity: normalizeSeverityForShip(f.severity),
1344
- })),
1345
- ];
1346
-
1347
942
  results.findings = allFindings;
1348
- results.engineStats = engineResult.stats;
1349
- results.validatorStats = validatorResult.stats;
943
+
944
+ if (spinner) spinner.succeed(`Route truth mapped (${truthpack.routes?.server?.length || 0} routes)`);
1350
945
 
1351
946
  // Phase 3: Build Proof Graph
1352
947
  if (spinner) spinner.start('Building proof graph...');
@@ -1356,94 +951,33 @@ async function runShip(args, context = {}) {
1356
951
 
1357
952
  if (spinner) spinner.succeed(`Proof graph built (${proofGraph.summary.totalClaims} claims)`);
1358
953
 
1359
- // ═══════════════════════════════════════════════════════════════════════════
1360
- // Calculate Production Readiness Score and Verdict
1361
- // ═══════════════════════════════════════════════════════════════════════════
1362
- const blockers = allFindings.filter(f => f.severity === 'BLOCK');
1363
- const warnings = allFindings.filter(f => f.severity === 'WARN');
1364
- const infos = allFindings.filter(f => f.severity === 'INFO');
1365
-
1366
- // Check for hallucinations (critical for vibecheck)
1367
- const hallucinations = allFindings.filter(f =>
1368
- f.engine === 'ai-hallucination-engine' ||
1369
- f.category?.includes('AIHallucination') ||
1370
- f.category?.includes('Hallucination')
1371
- );
954
+ // Calculate final verdict
955
+ const blockers = allFindings.filter(f => f.severity === 'BLOCK' || f.severity === 'critical');
956
+ const warnings = allFindings.filter(f => f.severity === 'WARN' || f.severity === 'warning');
1372
957
 
1373
958
  results.blockers = blockers;
1374
959
  results.warnings = warnings;
1375
960
 
1376
- // Calculate Production Readiness Score (0-100)
1377
- // Start at 100 and deduct based on findings
1378
- let score = 100;
961
+ // Apply strict mode
962
+ if (opts.strict && warnings.length > 0) {
963
+ results.canShip = false;
964
+ }
1379
965
 
1380
- // Category-based deductions
1381
- const categoryDeductions = {
1382
- security: 0,
1383
- quality: 0,
1384
- hallucination: 0,
1385
- routes: 0,
1386
- contracts: 0,
1387
- dependencies: 0,
1388
- };
966
+ if (blockers.length > 0) {
967
+ results.canShip = false;
968
+ }
1389
969
 
970
+ // Deduct score for findings
1390
971
  for (const finding of allFindings) {
1391
- const category = categorizeFindings(finding);
1392
-
1393
972
  if (finding.severity === 'BLOCK') {
1394
- categoryDeductions[category] += 15;
973
+ results.score = Math.max(0, results.score - 15);
1395
974
  } else if (finding.severity === 'WARN') {
1396
- categoryDeductions[category] += 5;
1397
- } else {
1398
- categoryDeductions[category] += 1;
975
+ results.score = Math.max(0, results.score - 5);
1399
976
  }
1400
977
  }
1401
978
 
1402
- // Apply weighted deductions (max 100 points deducted)
1403
- const totalDeduction = Math.min(100,
1404
- categoryDeductions.security * 1.5 + // Security issues weighted higher
1405
- categoryDeductions.hallucination * 2.0 + // AI hallucinations weighted highest
1406
- categoryDeductions.quality * 0.8 +
1407
- categoryDeductions.routes * 1.0 +
1408
- categoryDeductions.contracts * 1.0 +
1409
- categoryDeductions.dependencies * 0.5
1410
- );
1411
-
1412
- score = Math.max(0, Math.round(100 - totalDeduction));
1413
- results.score = score;
1414
-
1415
- // Determine grade
1416
- results.grade = score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : score >= 50 ? 'D' : 'F';
1417
-
1418
- // Determine verdict using the specified rules
1419
- let verdict;
1420
- const hasCritical = blockers.length > 0;
1421
- const hasHallucinations = hallucinations.filter(f => f.severity !== 'INFO').length > 0;
1422
-
1423
- if (hasCritical || score < 50) {
1424
- verdict = 'BLOCK';
1425
- results.canShip = false;
1426
- } else if (hasHallucinations || score < 75 || (opts.strict && warnings.length > 0)) {
1427
- verdict = 'WARN';
1428
- results.canShip = false;
1429
- } else {
1430
- verdict = 'SHIP';
1431
- results.canShip = true;
1432
- }
1433
-
1434
- // Build breakdown by category
1435
- const breakdown = {
1436
- security: allFindings.filter(f => categorizeFindings(f) === 'security'),
1437
- quality: allFindings.filter(f => categorizeFindings(f) === 'quality'),
1438
- hallucination: hallucinations,
1439
- routes: allFindings.filter(f => categorizeFindings(f) === 'routes'),
1440
- contracts: allFindings.filter(f => categorizeFindings(f) === 'contracts'),
1441
- dependencies: allFindings.filter(f => categorizeFindings(f) === 'dependencies'),
1442
- };
1443
-
1444
- results.breakdown = breakdown;
1445
-
1446
- const duration = Date.now() - executionStart;
979
+ const verdict = results.canShip ? 'SHIP' : blockers.length > 0 ? 'BLOCK' : 'WARN';
980
+ const duration = Date.now() - startTime;
1447
981
 
1448
982
  // ═══════════════════════════════════════════════════════════════════════════
1449
983
  // OUTPUT
@@ -1548,77 +1082,9 @@ async function runShip(args, context = {}) {
1548
1082
  tier: currentTier,
1549
1083
  }));
1550
1084
 
1551
- // V7: World-class Risk Radar and Pre-flight Checklist
1552
- if (proofCertificate && !opts.quiet && !opts.json && !opts.ci) {
1553
- // Risk Radar visualization
1554
- const riskRadar = proofCertificate.calculateRiskRadar(allFindings);
1555
- console.log();
1556
- console.log(proofCertificate.renderRiskRadar(riskRadar));
1557
-
1558
- // Pre-flight Checklist
1559
- const preflight = proofCertificate.runPreflightChecklist(allFindings, proofGraph);
1560
- console.log();
1561
- console.log(proofCertificate.renderPreflightChecklist(preflight));
1562
-
1563
- // Generate and save Proof Certificate
1564
- const certificate = proofCertificate.generateProofCertificate({
1565
- projectPath,
1566
- projectName: path.basename(projectPath),
1567
- verdict,
1568
- score: results.score,
1569
- findings: allFindings,
1570
- truthpack,
1571
- proofGraph,
1572
- duration: Date.now() - executionStart,
1573
- tier: currentTier,
1574
- version: require('../../package.json').version || '1.0.0',
1575
- });
1576
-
1577
- // Save certificate
1578
- fs.mkdirSync(outputDir, { recursive: true });
1579
- fs.writeFileSync(
1580
- path.join(outputDir, 'proof-certificate.json'),
1581
- JSON.stringify(certificate.certificate, null, 2)
1582
- );
1583
-
1584
- // Show certificate ID
1585
- console.log();
1586
- console.log(` ${ansi.dim}╭${'─'.repeat(58)}╮${ansi.reset}`);
1587
- console.log(` ${ansi.dim}│${ansi.reset} ${colors.accent}📜 PROOF CERTIFICATE${ansi.reset} ${ansi.dim}│${ansi.reset}`);
1588
- console.log(` ${ansi.dim}├${'─'.repeat(58)}┤${ansi.reset}`);
1589
- console.log(` ${ansi.dim}│${ansi.reset} ID: ${ansi.cyan}${certificate.certificate.certificateId}${ansi.reset} ${ansi.dim}│${ansi.reset}`);
1590
- console.log(` ${ansi.dim}│${ansi.reset} Short Code: ${ansi.bold}${certificate.shortCode}${ansi.reset} ${ansi.dim}│${ansi.reset}`);
1591
- console.log(` ${ansi.dim}│${ansi.reset} Confidence: ${ansi.bold}${certificate.certificate.verdict.confidence}%${ansi.reset} ${ansi.dim}│${ansi.reset}`);
1592
- console.log(` ${ansi.dim}│${ansi.reset} Expires: ${certificate.certificate.expires.slice(0, 10)} ${ansi.dim}│${ansi.reset}`);
1593
- console.log(` ${ansi.dim}├${'─'.repeat(58)}┤${ansi.reset}`);
1594
- console.log(` ${ansi.dim}│${ansi.reset} ${ansi.dim}Verify at:${ansi.reset} ${ansi.cyan}${certificate.certificate.verificationUrl.slice(0, 45)}${ansi.reset} ${ansi.dim}│${ansi.reset}`);
1595
- console.log(` ${ansi.dim}╰${'─'.repeat(58)}╯${ansi.reset}`);
1596
- }
1597
-
1598
- // Pro upsell for free users
1599
- if (currentTier === 'free' && !opts.quiet) {
1600
- console.log();
1601
- console.log(` ${ansi.gray}${BOX.dTopLeft}${BOX.dHorizontal.repeat(66)}${BOX.dTopRight}${ansi.reset}`);
1602
- console.log(` ${ansi.gray}${BOX.dVertical}${ansi.reset}${' '.repeat(66)}${ansi.gray}${BOX.dVertical}${ansi.reset}`);
1603
-
1604
- if (verdict === 'SHIP') {
1605
- console.log(` ${ansi.gray}${BOX.dVertical}${ansi.reset} ${ansi.magenta}★ PRO${ansi.reset} Generate a ${ansi.bold}status badge${ansi.reset} to show off your clean code! ${ansi.gray}${BOX.dVertical}${ansi.reset}`);
1606
- console.log(` ${ansi.gray}${BOX.dVertical}${ansi.reset} Run: ${ansi.cyan}vibecheck ship --badge${ansi.reset} ${ansi.gray}${BOX.dVertical}${ansi.reset}`);
1607
- } else if (allFindings.length > 0) {
1608
- console.log(` ${ansi.gray}${BOX.dVertical}${ansi.reset} ${ansi.magenta}★ PRO${ansi.reset} Auto-fix all ${ansi.bold}${allFindings.length} issues${ansi.reset} instantly with AI ${ansi.gray}${BOX.dVertical}${ansi.reset}`);
1609
- console.log(` ${ansi.gray}${BOX.dVertical}${ansi.reset} Run: ${ansi.cyan}vibecheck fix --apply${ansi.reset} ${ansi.gray}${BOX.dVertical}${ansi.reset}`);
1610
- }
1611
-
1612
- console.log(` ${ansi.gray}${BOX.dVertical}${ansi.reset}${' '.repeat(66)}${ansi.gray}${BOX.dVertical}${ansi.reset}`);
1613
- console.log(` ${ansi.gray}${BOX.dVertical}${ansi.reset} Upgrade: ${ansi.cyan}https://vibecheckai.dev/pricing${ansi.reset} ${ansi.gray}${BOX.dVertical}${ansi.reset}`);
1614
- console.log(` ${ansi.gray}${BOX.dVertical}${ansi.reset}${' '.repeat(66)}${ansi.gray}${BOX.dVertical}${ansi.reset}`);
1615
- console.log(` ${ansi.gray}${BOX.dBottomLeft}${BOX.dHorizontal.repeat(66)}${BOX.dBottomRight}${ansi.reset}`);
1616
- console.log();
1617
- }
1618
-
1619
- // Badge file generation (PRO only)
1085
+ // Badge file generation (STARTER+ only)
1620
1086
  if (opts.badge) {
1621
- const isVerified = opts.withRuntime && currentTier === 'pro';
1087
+ const isVerified = opts.withRuntime && (currentTier === 'pro' || currentTier === 'compliance');
1622
1088
  const { data: badgeData } = renderBadgeOutput(projectPath, verdict, results.score, {
1623
1089
  tier: currentTier,
1624
1090
  isVerified
@@ -1642,7 +1108,7 @@ async function runShip(args, context = {}) {
1642
1108
  // Earned upsell: Badge withheld when verdict != SHIP
1643
1109
  if (!results.canShip) {
1644
1110
  const currentTier = context?.authInfo?.access?.tier || "free";
1645
- if (currentTier === "pro") {
1111
+ if (entitlements.tierMeetsMinimum(currentTier, "starter")) {
1646
1112
  // User has badge access but verdict prevents it
1647
1113
  console.log(upsell.formatEarnedUpsell({
1648
1114
  cmd: "ship",
@@ -1670,92 +1136,7 @@ async function runShip(args, context = {}) {
1670
1136
  } catch {}
1671
1137
 
1672
1138
  // Exit code: 0=SHIP, 1=WARN, 2=BLOCK
1673
- let exitCode = getExitCode(verdict);
1674
-
1675
- // Deploy Gate Mode: Stricter exit code logic for CI/CD integration
1676
- if (opts.gate) {
1677
- const hasFakeFeatures = allFindings.some(f =>
1678
- f.category?.toLowerCase().includes('fake') ||
1679
- f.category?.toLowerCase().includes('mock') ||
1680
- f.type?.toLowerCase().includes('fake') ||
1681
- f.title?.toLowerCase().includes('fake') ||
1682
- f.title?.toLowerCase().includes('mock data')
1683
- );
1684
-
1685
- const hasBlockers = allFindings.some(f =>
1686
- f.severity === 'BLOCK' || f.severity === 'critical' || f.severity === 'high'
1687
- );
1688
-
1689
- const hasWarnings = allFindings.some(f =>
1690
- f.severity === 'WARN' || f.severity === 'warning' || f.severity === 'medium'
1691
- );
1692
-
1693
- // Determine gate failure based on --fail-on option
1694
- let gateBlocked = false;
1695
- let gateReason = '';
1696
-
1697
- switch (opts.failOn) {
1698
- case 'fake-features':
1699
- gateBlocked = hasFakeFeatures;
1700
- gateReason = 'Fake features detected';
1701
- break;
1702
- case 'warnings':
1703
- gateBlocked = hasWarnings || hasBlockers || hasFakeFeatures;
1704
- gateReason = hasBlockers ? 'Blockers found' : hasFakeFeatures ? 'Fake features detected' : 'Warnings found';
1705
- break;
1706
- case 'any':
1707
- gateBlocked = allFindings.length > 0;
1708
- gateReason = 'Issues detected';
1709
- break;
1710
- case 'blockers':
1711
- default:
1712
- gateBlocked = hasBlockers || hasFakeFeatures;
1713
- gateReason = hasFakeFeatures ? 'Fake features detected' : 'Blockers found';
1714
- break;
1715
- }
1716
-
1717
- if (gateBlocked) {
1718
- exitCode = EXIT.BLOCKING;
1719
-
1720
- // Output gate-specific JSON for CI/CD parsing
1721
- if (opts.json) {
1722
- console.log(JSON.stringify({
1723
- gate: {
1724
- blocked: true,
1725
- reason: gateReason,
1726
- failOn: opts.failOn,
1727
- hasFakeFeatures,
1728
- hasBlockers,
1729
- hasWarnings,
1730
- issueCount: allFindings.length,
1731
- },
1732
- verdict,
1733
- score: results.score,
1734
- canShip: false,
1735
- exitCode,
1736
- findings: allFindings.slice(0, 20), // Top 20 findings
1737
- timestamp: new Date().toISOString(),
1738
- }, null, 2));
1739
- }
1740
- } else if (opts.json) {
1741
- console.log(JSON.stringify({
1742
- gate: {
1743
- blocked: false,
1744
- reason: 'All checks passed',
1745
- failOn: opts.failOn,
1746
- hasFakeFeatures: false,
1747
- hasBlockers: false,
1748
- hasWarnings,
1749
- issueCount: allFindings.length,
1750
- },
1751
- verdict,
1752
- score: results.score,
1753
- canShip: true,
1754
- exitCode,
1755
- timestamp: new Date().toISOString(),
1756
- }, null, 2));
1757
- }
1758
- }
1139
+ const exitCode = getExitCode(verdict);
1759
1140
 
1760
1141
  // Save final results
1761
1142
  saveArtifact(runId, "summary", {
@@ -1763,8 +1144,6 @@ async function runShip(args, context = {}) {
1763
1144
  score: results.score,
1764
1145
  canShip: results.canShip,
1765
1146
  exitCode,
1766
- gateMode: opts.gate,
1767
- failOn: opts.failOn,
1768
1147
  timestamp: new Date().toISOString()
1769
1148
  });
1770
1149
 
@@ -1833,17 +1212,32 @@ async function shipCore({ repoRoot, fastifyEntry, jsonOut, noWrite } = {}) {
1833
1212
  const root = repoRoot || process.cwd();
1834
1213
  const fastEntry = fastifyEntry || detectFastifyEntry(root);
1835
1214
 
1836
- const truthpack = await buildTruthpack({ repoRoot: root, fastifyEntry: fastEntry });
1215
+ const truthpack = await buildTruthpackSmart({ repoRoot: root, fastifyEntry: fastEntry });
1837
1216
  if (!noWrite) writeTruthpack(root, truthpack);
1838
1217
 
1218
+ // Run analyzers in parallel for performance
1219
+ const [fileResults, truthpackResults] = await Promise.all([
1220
+ Promise.all([
1221
+ Promise.resolve(findFakeSuccess(root)),
1222
+ Promise.resolve(findOptimisticNoRollback(root)),
1223
+ Promise.resolve(findSilentCatch(root)),
1224
+ Promise.resolve(findDeadUI(root)),
1225
+ Promise.resolve(findOwnerModeBypass(root)),
1226
+ ]),
1227
+ Promise.all([
1228
+ Promise.resolve(findMissingRoutes(truthpack)),
1229
+ Promise.resolve(findMethodMismatch(truthpack)),
1230
+ Promise.resolve(findEnvGaps(truthpack)),
1231
+ Promise.resolve(findGhostAuth(truthpack, root)),
1232
+ Promise.resolve(findStripeWebhookViolations(truthpack)),
1233
+ Promise.resolve(findPaidSurfaceNotEnforced(truthpack)),
1234
+ ]),
1235
+ ]);
1236
+
1839
1237
  const allFindings = [
1840
- ...findMissingRoutes(truthpack),
1841
- ...findEnvGaps(truthpack),
1842
- ...findFakeSuccess(root),
1843
- ...findGhostAuth(truthpack, root),
1844
- ...findStripeWebhookViolations(truthpack),
1845
- ...findPaidSurfaceNotEnforced(truthpack),
1846
- ...findOwnerModeBypass(root),
1238
+ ...fileResults.flat(),
1239
+ ...truthpackResults.flat(),
1240
+ // Runtime findings
1847
1241
  ...findingsFromReality(root)
1848
1242
  ];
1849
1243