@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.
- package/bin/registry.js +214 -237
- package/bin/runners/cli-utils.js +33 -2
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/cursor.js +2 -49
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/team-conventions.js +33 -7
- package/bin/runners/lib/analysis-core.js +25 -5
- package/bin/runners/lib/analyzers.js +431 -481
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/doctor/modules/security.js +3 -1
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +18 -218
- package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
- package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
- package/bin/runners/lib/engines/mock-data-engine.js +10 -53
- package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
- package/bin/runners/lib/engines/type-aware-engine.js +39 -263
- package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/entitlements-v2.js +73 -97
- package/bin/runners/lib/error-handler.js +44 -3
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/html-proof-report.js +700 -350
- package/bin/runners/lib/missions/plan.js +6 -46
- package/bin/runners/lib/missions/templates.js +0 -232
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/route-detection.js +137 -68
- package/bin/runners/lib/scan-output.js +91 -76
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/validator.js +27 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/ship-output-enterprise.js +23 -23
- package/bin/runners/lib/ship-output.js +75 -31
- package/bin/runners/lib/terminal-ui.js +6 -113
- package/bin/runners/lib/truth.js +351 -10
- package/bin/runners/lib/unified-cli-output.js +430 -603
- package/bin/runners/lib/unified-output.js +13 -9
- package/bin/runners/runAIAgent.js +10 -5
- package/bin/runners/runAgent.js +0 -3
- package/bin/runners/runAllowlist.js +389 -0
- package/bin/runners/runApprove.js +0 -33
- package/bin/runners/runAuth.js +73 -45
- package/bin/runners/runCheckpoint.js +51 -11
- package/bin/runners/runClassify.js +85 -21
- package/bin/runners/runContext.js +0 -3
- package/bin/runners/runDoctor.js +41 -28
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.js +0 -3
- package/bin/runners/runFirewallHook.js +0 -3
- package/bin/runners/runFix.js +66 -76
- package/bin/runners/runGuard.js +18 -411
- package/bin/runners/runInit.js +113 -30
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runMcp.js +19 -25
- package/bin/runners/runPolish.js +64 -240
- package/bin/runners/runPromptFirewall.js +12 -5
- package/bin/runners/runProve.js +57 -22
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +59 -68
- package/bin/runners/runReport.js +38 -33
- package/bin/runners/runRuntime.js +8 -5
- package/bin/runners/runScan.js +1413 -190
- package/bin/runners/runShip.js +113 -719
- package/bin/runners/runTruth.js +0 -3
- package/bin/runners/runValidate.js +13 -9
- package/bin/runners/runWatch.js +23 -14
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +204 -185
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +554 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +210 -238
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +499 -0
- package/mcp-server/lib/index.ts +19 -0
- package/mcp-server/lib/rate-limiter.js +166 -0
- package/mcp-server/lib/sandbox.test.ts +519 -0
- package/mcp-server/lib/sandbox.ts +395 -0
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/package.json +12 -3
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +605 -0
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +378 -45
- package/mcp-server/tools-v3.js +353 -442
- package/mcp-server/tsconfig.json +37 -0
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/package.json +1 -1
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
- package/bin/runners/lib/audit-logger.js +0 -532
- package/bin/runners/lib/authority/authorities/architecture.js +0 -364
- package/bin/runners/lib/authority/authorities/compliance.js +0 -341
- package/bin/runners/lib/authority/authorities/human.js +0 -343
- package/bin/runners/lib/authority/authorities/quality.js +0 -420
- package/bin/runners/lib/authority/authorities/security.js +0 -228
- package/bin/runners/lib/authority/index.js +0 -293
- package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
- package/bin/runners/lib/cli-charts.js +0 -368
- package/bin/runners/lib/cli-config-display.js +0 -405
- package/bin/runners/lib/cli-demo.js +0 -275
- package/bin/runners/lib/cli-errors.js +0 -438
- package/bin/runners/lib/cli-help-formatter.js +0 -439
- package/bin/runners/lib/cli-interactive-menu.js +0 -509
- package/bin/runners/lib/cli-prompts.js +0 -441
- package/bin/runners/lib/cli-scan-cards.js +0 -362
- package/bin/runners/lib/compliance-reporter.js +0 -710
- package/bin/runners/lib/conductor/index.js +0 -671
- package/bin/runners/lib/easy/README.md +0 -123
- package/bin/runners/lib/easy/index.js +0 -140
- package/bin/runners/lib/easy/interactive-wizard.js +0 -788
- package/bin/runners/lib/easy/one-click-firewall.js +0 -564
- package/bin/runners/lib/easy/zero-config-reality.js +0 -714
- package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
- package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
- package/bin/runners/lib/engines/confidence-scoring.js +0 -276
- package/bin/runners/lib/engines/context-detection.js +0 -264
- package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
- package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
- package/bin/runners/lib/engines/env-variables-engine.js +0 -458
- package/bin/runners/lib/engines/error-handling-engine.js +0 -437
- package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
- package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
- package/bin/runners/lib/engines/framework-detection.js +0 -508
- package/bin/runners/lib/engines/import-order-engine.js +0 -429
- package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
- package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
- package/bin/runners/lib/engines/orchestrator.js +0 -334
- package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
- package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
- package/bin/runners/lib/enhanced-features/index.js +0 -305
- package/bin/runners/lib/enhanced-output.js +0 -631
- package/bin/runners/lib/enterprise.js +0 -300
- package/bin/runners/lib/firewall/command-validator.js +0 -351
- package/bin/runners/lib/firewall/config.js +0 -341
- package/bin/runners/lib/firewall/content-validator.js +0 -519
- package/bin/runners/lib/firewall/index.js +0 -101
- package/bin/runners/lib/firewall/path-validator.js +0 -256
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
- package/bin/runners/lib/mcp-utils.js +0 -425
- package/bin/runners/lib/output/index.js +0 -1022
- package/bin/runners/lib/policy-engine.js +0 -652
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
- package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
- package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
- package/bin/runners/lib/polish/autofix/index.js +0 -200
- package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
- package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
- package/bin/runners/lib/polish/backend-checks.js +0 -148
- package/bin/runners/lib/polish/documentation-checks.js +0 -111
- package/bin/runners/lib/polish/frontend-checks.js +0 -168
- package/bin/runners/lib/polish/index.js +0 -71
- package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
- package/bin/runners/lib/polish/library-detection.js +0 -175
- package/bin/runners/lib/polish/performance-checks.js +0 -100
- package/bin/runners/lib/polish/security-checks.js +0 -148
- package/bin/runners/lib/polish/utils.js +0 -203
- package/bin/runners/lib/prompt-builder.js +0 -540
- package/bin/runners/lib/proof-certificate.js +0 -634
- package/bin/runners/lib/reality/accessibility-audit.js +0 -946
- package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
- package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
- package/bin/runners/lib/reality/performance-tracker.js +0 -1077
- package/bin/runners/lib/reality/scenario-generator.js +0 -1404
- package/bin/runners/lib/reality/visual-regression.js +0 -852
- package/bin/runners/lib/reality-profiler.js +0 -717
- package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
- package/bin/runners/lib/review/ai-code-review.js +0 -832
- package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
- package/bin/runners/lib/sbom-generator.js +0 -641
- package/bin/runners/lib/scan-output-enhanced.js +0 -512
- package/bin/runners/lib/security/owasp-scanner.js +0 -939
- package/bin/runners/lib/validators/contract-validator.js +0 -283
- package/bin/runners/lib/validators/dead-export-detector.js +0 -279
- package/bin/runners/lib/validators/dep-audit.js +0 -245
- package/bin/runners/lib/validators/env-validator.js +0 -319
- package/bin/runners/lib/validators/index.js +0 -120
- package/bin/runners/lib/validators/license-checker.js +0 -252
- package/bin/runners/lib/validators/route-validator.js +0 -290
- package/bin/runners/runAuthority.js +0 -528
- package/bin/runners/runConductor.js +0 -772
- package/bin/runners/runContainer.js +0 -366
- package/bin/runners/runEasy.js +0 -410
- package/bin/runners/runIaC.js +0 -372
- package/bin/runners/runVibe.js +0 -791
- package/mcp-server/tools.js +0 -495
package/bin/runners/runShip.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* vibecheck ship -
|
|
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
|
|
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
|
-
//
|
|
31
|
-
const {
|
|
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
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
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(
|
|
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
|
|
897
|
+
const truthpack = await buildTruthpackSmart({ repoRoot: projectPath, fastifyEntry });
|
|
1299
898
|
writeTruthpack(projectPath, truthpack);
|
|
1300
899
|
results.truthpack = truthpack;
|
|
1301
900
|
|
|
1302
|
-
// Run
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1349
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
1377
|
-
|
|
1378
|
-
|
|
961
|
+
// Apply strict mode
|
|
962
|
+
if (opts.strict && warnings.length > 0) {
|
|
963
|
+
results.canShip = false;
|
|
964
|
+
}
|
|
1379
965
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
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
|
-
|
|
973
|
+
results.score = Math.max(0, results.score - 15);
|
|
1395
974
|
} else if (finding.severity === 'WARN') {
|
|
1396
|
-
|
|
1397
|
-
} else {
|
|
1398
|
-
categoryDeductions[category] += 1;
|
|
975
|
+
results.score = Math.max(0, results.score - 5);
|
|
1399
976
|
}
|
|
1400
977
|
}
|
|
1401
978
|
|
|
1402
|
-
|
|
1403
|
-
const
|
|
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
|
-
//
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
...
|
|
1841
|
-
...
|
|
1842
|
-
|
|
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
|
|