@vibecheckai/cli 3.2.2 → 3.2.4

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 (170) hide show
  1. package/bin/.generated +25 -25
  2. package/bin/dev/run-v2-torture.js +30 -30
  3. package/bin/runners/ENHANCEMENT_GUIDE.md +121 -121
  4. package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
  5. package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
  6. package/bin/runners/lib/agent-firewall/claims/extractor.js +117 -28
  7. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +23 -14
  8. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +72 -1
  9. package/bin/runners/lib/agent-firewall/interceptor/base.js +2 -2
  10. package/bin/runners/lib/agent-firewall/policy/default-policy.json +6 -0
  11. package/bin/runners/lib/agent-firewall/policy/engine.js +34 -3
  12. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +29 -4
  13. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +12 -0
  14. package/bin/runners/lib/agent-firewall/truthpack/loader.js +21 -0
  15. package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
  16. package/bin/runners/lib/analyzers.js +606 -325
  17. package/bin/runners/lib/auth-truth.js +193 -193
  18. package/bin/runners/lib/backup.js +62 -62
  19. package/bin/runners/lib/billing.js +107 -107
  20. package/bin/runners/lib/claims.js +118 -118
  21. package/bin/runners/lib/cli-ui.js +540 -540
  22. package/bin/runners/lib/contracts/auth-contract.js +202 -202
  23. package/bin/runners/lib/contracts/env-contract.js +181 -181
  24. package/bin/runners/lib/contracts/external-contract.js +206 -206
  25. package/bin/runners/lib/contracts/guard.js +168 -168
  26. package/bin/runners/lib/contracts/index.js +89 -89
  27. package/bin/runners/lib/contracts/plan-validator.js +311 -311
  28. package/bin/runners/lib/contracts/route-contract.js +199 -199
  29. package/bin/runners/lib/contracts.js +804 -804
  30. package/bin/runners/lib/detect.js +89 -89
  31. package/bin/runners/lib/doctor/autofix.js +254 -254
  32. package/bin/runners/lib/doctor/index.js +37 -37
  33. package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
  34. package/bin/runners/lib/doctor/modules/index.js +46 -46
  35. package/bin/runners/lib/doctor/modules/network.js +250 -250
  36. package/bin/runners/lib/doctor/modules/project.js +312 -312
  37. package/bin/runners/lib/doctor/modules/runtime.js +224 -224
  38. package/bin/runners/lib/doctor/modules/security.js +348 -348
  39. package/bin/runners/lib/doctor/modules/system.js +213 -213
  40. package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
  41. package/bin/runners/lib/doctor/reporter.js +262 -262
  42. package/bin/runners/lib/doctor/service.js +262 -262
  43. package/bin/runners/lib/doctor/types.js +113 -113
  44. package/bin/runners/lib/doctor/ui.js +263 -263
  45. package/bin/runners/lib/doctor-v2.js +608 -608
  46. package/bin/runners/lib/drift.js +425 -425
  47. package/bin/runners/lib/enforcement.js +72 -72
  48. package/bin/runners/lib/engines/accessibility-engine.js +190 -0
  49. package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
  50. package/bin/runners/lib/engines/ast-cache.js +99 -0
  51. package/bin/runners/lib/engines/code-quality-engine.js +255 -0
  52. package/bin/runners/lib/engines/console-logs-engine.js +115 -0
  53. package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
  54. package/bin/runners/lib/engines/dead-code-engine.js +198 -0
  55. package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
  56. package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
  57. package/bin/runners/lib/engines/file-filter.js +131 -0
  58. package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
  59. package/bin/runners/lib/engines/mock-data-engine.js +272 -0
  60. package/bin/runners/lib/engines/parallel-processor.js +71 -0
  61. package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
  62. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
  63. package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
  64. package/bin/runners/lib/engines/type-aware-engine.js +152 -0
  65. package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
  66. package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
  67. package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
  68. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  69. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  70. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  71. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  72. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  73. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  74. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  75. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
  76. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  77. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  78. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  79. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  80. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  81. package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
  82. package/bin/runners/lib/enterprise-detect.js +603 -603
  83. package/bin/runners/lib/enterprise-init.js +942 -942
  84. package/bin/runners/lib/env-resolver.js +417 -417
  85. package/bin/runners/lib/env-template.js +66 -66
  86. package/bin/runners/lib/env.js +189 -189
  87. package/bin/runners/lib/extractors/client-calls.js +990 -990
  88. package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
  89. package/bin/runners/lib/extractors/fastify-routes.js +426 -426
  90. package/bin/runners/lib/extractors/index.js +363 -363
  91. package/bin/runners/lib/extractors/next-routes.js +524 -524
  92. package/bin/runners/lib/extractors/proof-graph.js +431 -431
  93. package/bin/runners/lib/extractors/route-matcher.js +451 -451
  94. package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
  95. package/bin/runners/lib/extractors/ui-bindings.js +547 -547
  96. package/bin/runners/lib/findings-schema.js +281 -281
  97. package/bin/runners/lib/firewall-prompt.js +50 -50
  98. package/bin/runners/lib/global-flags.js +213 -213
  99. package/bin/runners/lib/graph/graph-builder.js +265 -265
  100. package/bin/runners/lib/graph/html-renderer.js +413 -413
  101. package/bin/runners/lib/graph/index.js +32 -32
  102. package/bin/runners/lib/graph/runtime-collector.js +215 -215
  103. package/bin/runners/lib/graph/static-extractor.js +518 -518
  104. package/bin/runners/lib/html-report.js +650 -650
  105. package/bin/runners/lib/interactive-menu.js +1496 -1496
  106. package/bin/runners/lib/llm.js +75 -75
  107. package/bin/runners/lib/meter.js +61 -61
  108. package/bin/runners/lib/missions/evidence.js +126 -126
  109. package/bin/runners/lib/patch.js +40 -40
  110. package/bin/runners/lib/permissions/auth-model.js +213 -213
  111. package/bin/runners/lib/permissions/idor-prover.js +205 -205
  112. package/bin/runners/lib/permissions/index.js +45 -45
  113. package/bin/runners/lib/permissions/matrix-builder.js +198 -198
  114. package/bin/runners/lib/pkgjson.js +28 -28
  115. package/bin/runners/lib/policy.js +295 -295
  116. package/bin/runners/lib/preflight.js +142 -142
  117. package/bin/runners/lib/reality/correlation-detectors.js +359 -359
  118. package/bin/runners/lib/reality/index.js +318 -318
  119. package/bin/runners/lib/reality/request-hashing.js +416 -416
  120. package/bin/runners/lib/reality/request-mapper.js +453 -453
  121. package/bin/runners/lib/reality/safety-rails.js +463 -463
  122. package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
  123. package/bin/runners/lib/reality/toast-detector.js +393 -393
  124. package/bin/runners/lib/reality-findings.js +84 -84
  125. package/bin/runners/lib/receipts.js +179 -179
  126. package/bin/runners/lib/redact.js +29 -29
  127. package/bin/runners/lib/replay/capsule-manager.js +154 -154
  128. package/bin/runners/lib/replay/index.js +263 -263
  129. package/bin/runners/lib/replay/player.js +348 -348
  130. package/bin/runners/lib/replay/recorder.js +331 -331
  131. package/bin/runners/lib/report-output.js +187 -187
  132. package/bin/runners/lib/report.js +135 -135
  133. package/bin/runners/lib/route-detection.js +1140 -1140
  134. package/bin/runners/lib/sandbox/index.js +59 -59
  135. package/bin/runners/lib/sandbox/proof-chain.js +399 -399
  136. package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
  137. package/bin/runners/lib/sandbox/worktree.js +174 -174
  138. package/bin/runners/lib/scan-output.js +525 -190
  139. package/bin/runners/lib/schema-validator.js +350 -350
  140. package/bin/runners/lib/schemas/contracts.schema.json +160 -160
  141. package/bin/runners/lib/schemas/finding.schema.json +100 -100
  142. package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
  143. package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
  144. package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
  145. package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
  146. package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
  147. package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
  148. package/bin/runners/lib/schemas/validator.js +438 -438
  149. package/bin/runners/lib/score-history.js +282 -282
  150. package/bin/runners/lib/share-pack.js +239 -239
  151. package/bin/runners/lib/snippets.js +67 -67
  152. package/bin/runners/lib/status-output.js +253 -253
  153. package/bin/runners/lib/terminal-ui.js +351 -271
  154. package/bin/runners/lib/upsell.js +510 -510
  155. package/bin/runners/lib/usage.js +153 -153
  156. package/bin/runners/lib/validate-patch.js +156 -156
  157. package/bin/runners/lib/verdict-engine.js +628 -628
  158. package/bin/runners/reality/engine.js +917 -917
  159. package/bin/runners/reality/flows.js +122 -122
  160. package/bin/runners/reality/report.js +378 -378
  161. package/bin/runners/reality/session.js +193 -193
  162. package/bin/runners/runGuard.js +168 -168
  163. package/bin/runners/runProof.zip +0 -0
  164. package/bin/runners/runProve.js +8 -0
  165. package/bin/runners/runReality.js +14 -0
  166. package/bin/runners/runScan.js +17 -1
  167. package/bin/runners/runTruth.js +15 -3
  168. package/mcp-server/tier-auth.js +4 -4
  169. package/mcp-server/tools/index.js +72 -72
  170. package/package.json +1 -1
@@ -48,24 +48,33 @@ function resolve(projectRoot, claim) {
48
48
  }
49
49
 
50
50
  // Check if env var exists in .env.example or schema files
51
- const envExamplePath = path.join(projectRoot, ".env.example");
52
- const envSchemaPath = findEnvSchemaFile(projectRoot);
51
+ // Check multiple possible locations for .env.example
52
+ const envExamplePaths = [
53
+ path.join(projectRoot, ".env.example"),
54
+ path.join(projectRoot, "apps", "web-ui", ".env.example"),
55
+ path.join(projectRoot, "apps", "api", ".env.example")
56
+ ];
53
57
 
54
- if (fs.existsSync(envExamplePath)) {
55
- const envExample = fs.readFileSync(envExamplePath, "utf8");
56
- if (envExample.includes(envVarName)) {
57
- return {
58
- result: "PROVEN",
59
- sources: [{
60
- type: "repo.search",
61
- pointer: ".env.example",
62
- confidence: 0.7
63
- }],
64
- reason: `Environment variable ${envVarName} found in .env.example`
65
- };
58
+ for (const envExamplePath of envExamplePaths) {
59
+ if (fs.existsSync(envExamplePath)) {
60
+ const envExample = fs.readFileSync(envExamplePath, "utf8");
61
+ if (envExample.includes(envVarName)) {
62
+ const relativePath = path.relative(projectRoot, envExamplePath).replace(/\\/g, "/");
63
+ return {
64
+ result: "PROVEN",
65
+ sources: [{
66
+ type: "repo.search",
67
+ pointer: relativePath,
68
+ confidence: 0.7
69
+ }],
70
+ reason: `Environment variable ${envVarName} found in ${relativePath}`
71
+ };
72
+ }
66
73
  }
67
74
  }
68
75
 
76
+ const envSchemaPath = findEnvSchemaFile(projectRoot);
77
+
69
78
  if (envSchemaPath && fs.existsSync(envSchemaPath)) {
70
79
  const envSchema = fs.readFileSync(envSchemaPath, "utf8");
71
80
  if (envSchema.includes(envVarName)) {
@@ -9,6 +9,7 @@
9
9
 
10
10
  const { getRoutes } = require("../truthpack");
11
11
  const { canonicalizePath } = require("../../route-truth");
12
+ const { shouldIgnore } = require("../utils/ignore-checker");
12
13
 
13
14
  /**
14
15
  * Resolve route claim evidence
@@ -17,11 +18,81 @@ const { canonicalizePath } = require("../../route-truth");
17
18
  * @returns {object} Evidence result
18
19
  */
19
20
  function resolve(projectRoot, claim) {
20
- const routes = getRoutes(projectRoot);
21
+ // Skip checking routes from ignored files (test files, fixtures, etc.)
22
+ if (claim.pointer && shouldIgnore(projectRoot, claim.pointer.split(":")[0])) {
23
+ return {
24
+ result: "PROVEN",
25
+ sources: [{
26
+ type: "ignored",
27
+ pointer: claim.pointer,
28
+ confidence: 1.0
29
+ }],
30
+ reason: `Route from ignored file (test/fixture)`
31
+ };
32
+ }
21
33
 
22
34
  // Normalize route path from claim
23
35
  const routePath = canonicalizePath(claim.value);
24
36
 
37
+ // Skip external API calls (routes that don't start with /api/ in Next.js context)
38
+ // Routes like /content/blog, /content/faq are external backend API calls, not Next.js routes
39
+ // Only validate Next.js API routes (those in apps/web-ui/src/app/api/)
40
+ if (!routePath.startsWith('/api/')) {
41
+ return {
42
+ result: "PROVEN",
43
+ sources: [{
44
+ type: "external_api",
45
+ pointer: claim.pointer,
46
+ confidence: 1.0
47
+ }],
48
+ reason: `Route ${routePath} is an external API call, not a Next.js route`
49
+ };
50
+ }
51
+
52
+ // Skip wildcard patterns from template literals (they're approximations)
53
+ // These are detected from fetch(`${url}/api/...`) and are not exact routes
54
+ if (routePath.includes('*') || routePath === '/api/*') {
55
+ return {
56
+ result: "PROVEN",
57
+ sources: [{
58
+ type: "pattern",
59
+ pointer: claim.pointer,
60
+ confidence: 0.8
61
+ }],
62
+ reason: `Route pattern ${routePath} is a template literal approximation, not an exact route`
63
+ };
64
+ }
65
+
66
+ // Skip backend API routes (routes from template literals using apiUrl)
67
+ // These are calls to the backend API server, not Next.js API routes
68
+ if (claim.isBackendApi || (claim.reason && claim.reason.includes('backend API'))) {
69
+ return {
70
+ result: "PROVEN",
71
+ sources: [{
72
+ type: "backend_api",
73
+ pointer: claim.pointer,
74
+ confidence: 0.9
75
+ }],
76
+ reason: `Route ${routePath} is a backend API call (using apiUrl variable), not a Next.js route`
77
+ };
78
+ }
79
+
80
+ // Skip routes that are clearly backend API routes (v1, v2, etc. are typically backend)
81
+ // Next.js API routes are usually simpler like /api/checkout, /api/health
82
+ if (routePath.match(/^\/api\/v\d+\//)) {
83
+ return {
84
+ result: "PROVEN",
85
+ sources: [{
86
+ type: "backend_api",
87
+ pointer: claim.pointer,
88
+ confidence: 0.85
89
+ }],
90
+ reason: `Route ${routePath} appears to be a backend API route (versioned), not a Next.js route`
91
+ };
92
+ }
93
+
94
+ const routes = getRoutes(projectRoot);
95
+
25
96
  // Check if route exists in truthpack
26
97
  if (routes && routes.length > 0) {
27
98
  // First, check for exact match
@@ -77,7 +77,7 @@ async function interceptFileWrite({
77
77
  }];
78
78
 
79
79
  // Evaluate policy
80
- const verdict = evaluatePolicy({
80
+ const verdict = await evaluatePolicy({
81
81
  policy,
82
82
  claims,
83
83
  evidence,
@@ -159,7 +159,7 @@ async function interceptMultiFileWrite({
159
159
  const evidence = resolveEvidence(projectRoot, allClaims);
160
160
 
161
161
  // Evaluate policy
162
- const verdict = evaluatePolicy({
162
+ const verdict = await evaluatePolicy({
163
163
  policy,
164
164
  claims: allClaims,
165
165
  evidence,
@@ -55,6 +55,12 @@
55
55
  "unsafe_side_effect": {
56
56
  "severity": "block",
57
57
  "enabled": true
58
+ },
59
+ "ai_false_positive_detection": {
60
+ "enabled": true,
61
+ "confidence_threshold": 0.8,
62
+ "logSkipped": true,
63
+ "useLLM": false
58
64
  }
59
65
  },
60
66
  "evidence": {
@@ -2,7 +2,7 @@
2
2
  * Policy Engine
3
3
  *
4
4
  * Main policy engine that evaluates all rules deterministically.
5
- * No LLM opinions - pure rule-based evaluation.
5
+ * Uses AI to reduce false positives when enabled.
6
6
  */
7
7
 
8
8
  "use strict";
@@ -15,6 +15,7 @@ const fakeSuccess = require("./rules/fake-success");
15
15
  const scope = require("./rules/scope");
16
16
  const unsafeSideEffect = require("./rules/unsafe-side-effect");
17
17
  const { generateVerdict } = require("./verdict");
18
+ const { analyzeFalsePositive } = require("../ai/false-positive-analyzer");
18
19
 
19
20
  /**
20
21
  * Evaluate policy against change packet
@@ -24,9 +25,9 @@ const { generateVerdict } = require("./verdict");
24
25
  * @param {array} params.evidence - Evidence resolution results
25
26
  * @param {array} params.files - Changed files
26
27
  * @param {string} params.intent - Agent intent message
27
- * @returns {object} Verdict object
28
+ * @returns {Promise<object>} Verdict object
28
29
  */
29
- function evaluatePolicy({ policy, claims, evidence, files, intent }) {
30
+ async function evaluatePolicy({ policy, claims, evidence, files, intent }) {
30
31
  const violations = [];
31
32
 
32
33
  // Evaluate all rules
@@ -44,6 +45,36 @@ function evaluatePolicy({ policy, claims, evidence, files, intent }) {
44
45
  try {
45
46
  const violation = evaluator.evaluate({ claims, evidence, policy });
46
47
  if (violation) {
48
+ // Use AI to check if this is a false positive
49
+ const claim = claims.find(c =>
50
+ c.type === violation.claim?.type &&
51
+ c.value === violation.claim?.value
52
+ );
53
+
54
+ if (claim && policy.rules?.ai_false_positive_detection?.enabled) {
55
+ try {
56
+ const aiAnalysis = await analyzeFalsePositive({
57
+ violation,
58
+ claim,
59
+ filePath: claim.file || violation.claim?.file || "unknown",
60
+ projectRoot: process.cwd(),
61
+ policy
62
+ });
63
+
64
+ // If AI determines it's a false positive with high confidence, skip it
65
+ if (aiAnalysis.isFalsePositive && aiAnalysis.confidence >= 0.8) {
66
+ // Log but don't add to violations
67
+ if (policy.rules?.ai_false_positive_detection?.logSkipped) {
68
+ console.log(`[AI] Skipping false positive: ${violation.message} (confidence: ${aiAnalysis.confidence}, reason: ${aiAnalysis.reason})`);
69
+ }
70
+ continue;
71
+ }
72
+ } catch (aiError) {
73
+ // If AI analysis fails, proceed with the violation
74
+ console.warn(`[AI] False positive analysis failed: ${aiError.message}`);
75
+ }
76
+ }
77
+
47
78
  violations.push(violation);
48
79
  }
49
80
  } catch (error) {
@@ -30,7 +30,32 @@ function evaluate({ claims, evidence, policy }) {
30
30
  return null;
31
31
  }
32
32
 
33
- // Check if there's a corresponding HTTP call or side effect
33
+ // Filter out false positives: if there's an HTTP call in the same file, it's likely a response check
34
+ const realSuccessClaims = successClaims.filter(claim => {
35
+ const claimFile = claim.pointer ? claim.pointer.split(":")[0] : "";
36
+ if (!claimFile) return true;
37
+
38
+ // Check if there's an HTTP call in the same file (within reasonable distance)
39
+ const claimLine = claim.pointer ? parseInt(claim.pointer.split(":")[1]?.split("-")[0] || "0", 10) : 0;
40
+ const hasNearbyHttpCall = claims.some(c => {
41
+ if (c.type !== CLAIM_TYPES.HTTP_CALL && c.type !== CLAIM_TYPES.ROUTE) return false;
42
+ const cFile = c.pointer ? c.pointer.split(":")[0] : "";
43
+ if (cFile !== claimFile) return false;
44
+ const cLine = c.pointer ? parseInt(c.pointer.split(":")[1]?.split("-")[0] || "0", 10) : 0;
45
+ // Within 50 lines is considered "nearby"
46
+ return Math.abs(cLine - claimLine) <= 50;
47
+ });
48
+
49
+ // If there's a nearby HTTP call, this is likely checking the response, not showing UI
50
+ // Only flag if there's NO HTTP call nearby
51
+ return !hasNearbyHttpCall;
52
+ });
53
+
54
+ if (realSuccessClaims.length === 0) {
55
+ return null; // All were false positives (likely API response checks)
56
+ }
57
+
58
+ // Check if there's a corresponding HTTP call or side effect globally
34
59
  const hasHttpCall = claims.some(c =>
35
60
  c.type === CLAIM_TYPES.HTTP_CALL || c.type === CLAIM_TYPES.ROUTE
36
61
  );
@@ -40,7 +65,7 @@ function evaluate({ claims, evidence, policy }) {
40
65
  if (!hasHttpCall && !hasSideEffect) {
41
66
  // Check if domain requires blocking
42
67
  const blockDomains = ruleConfig.block_if_domain || [];
43
- const fileDomain = successClaims[0].domain || "general";
68
+ const fileDomain = realSuccessClaims[0].domain || "general";
44
69
 
45
70
  const shouldBlock = blockDomains.includes(fileDomain);
46
71
 
@@ -48,8 +73,8 @@ function evaluate({ claims, evidence, policy }) {
48
73
  rule: "fake_success_ui",
49
74
  severity: shouldBlock ? "block" : (ruleConfig.severity || "warn"),
50
75
  message: `Fake success UI: Success message shown but no HTTP call or side effect detected`,
51
- claimId: `claim_${claims.indexOf(successClaims[0])}`,
52
- claim: successClaims[0]
76
+ claimId: `claim_${claims.indexOf(realSuccessClaims[0])}`,
77
+ claim: realSuccessClaims[0]
53
78
  };
54
79
  }
55
80
 
@@ -30,7 +30,19 @@ function evaluate({ claims, evidence, policy }) {
30
30
  if (claim.type === CLAIM_TYPES.ROUTE || claim.type === CLAIM_TYPES.HTTP_CALL) {
31
31
  const ev = evidence.find(e => e.claimId === `claim_${i}`);
32
32
 
33
+ // Skip if evidence shows it's an external API call or ignored file
34
+ if (ev && (ev.result === "PROVEN" && (ev.reason?.includes("external API") || ev.reason?.includes("ignored file")))) {
35
+ continue;
36
+ }
37
+
33
38
  if (ev && ev.result === "UNPROVEN") {
39
+ // Double-check: skip external API calls (routes not starting with /api/)
40
+ const routePath = String(claim.value || "").trim();
41
+ if (!routePath.startsWith('/api/')) {
42
+ // This is an external API call, not a Next.js route - skip
43
+ continue;
44
+ }
45
+
34
46
  return {
35
47
  rule: "ghost_route",
36
48
  severity: ruleConfig.severity || "block",
@@ -65,6 +65,27 @@ function loadTruthpackFile(projectRoot, filename) {
65
65
  const filePath = path.join(projectRoot, TRUTHPACK_DIR, filename);
66
66
 
67
67
  if (!fs.existsSync(filePath)) {
68
+ // Fallback: try loading from truthpack.json if routes.json doesn't exist
69
+ if (filename === "routes.json") {
70
+ const truthpackPath = path.join(projectRoot, ".vibecheck", "truthpack.json");
71
+ if (fs.existsSync(truthpackPath)) {
72
+ try {
73
+ const truthpack = JSON.parse(fs.readFileSync(truthpackPath, "utf8"));
74
+ // Extract server routes from truthpack.json format
75
+ if (truthpack.routes && truthpack.routes.server) {
76
+ return {
77
+ routes: truthpack.routes.server.map(r => ({
78
+ path: r.path,
79
+ method: r.method,
80
+ handler: r.handler || r.source
81
+ }))
82
+ };
83
+ }
84
+ } catch (error) {
85
+ // Fall through to return null
86
+ }
87
+ }
88
+ }
68
89
  return null;
69
90
  }
70
91
 
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Ignore File Checker
3
+ *
4
+ * Checks if a file matches patterns in .vibecheckignore
5
+ */
6
+
7
+ "use strict";
8
+
9
+ const fs = require("fs");
10
+ const path = require("path");
11
+
12
+ // Try to use minimatch if available, otherwise use simple pattern matching
13
+ let minimatch;
14
+ try {
15
+ minimatch = require("minimatch").minimatch;
16
+ } catch {
17
+ minimatch = null;
18
+ }
19
+
20
+ let ignorePatterns = null;
21
+ let ignoreFile = null;
22
+
23
+ /**
24
+ * Load ignore patterns from .vibecheckignore
25
+ * @param {string} projectRoot - Project root directory
26
+ * @returns {array} Array of ignore patterns
27
+ */
28
+ function loadIgnorePatterns(projectRoot) {
29
+ if (ignorePatterns !== null && ignoreFile === projectRoot) {
30
+ return ignorePatterns;
31
+ }
32
+
33
+ const ignorePath = path.join(projectRoot, ".vibecheckignore");
34
+ ignorePatterns = [];
35
+
36
+ if (fs.existsSync(ignorePath)) {
37
+ const content = fs.readFileSync(ignorePath, "utf8");
38
+ ignorePatterns = content
39
+ .split("\n")
40
+ .map(line => line.trim())
41
+ .filter(line => line && !line.startsWith("#"));
42
+ }
43
+
44
+ ignoreFile = projectRoot;
45
+ return ignorePatterns;
46
+ }
47
+
48
+ /**
49
+ * Check if a file should be ignored
50
+ * @param {string} projectRoot - Project root directory
51
+ * @param {string} filePath - File path (relative to project root)
52
+ * @returns {boolean} True if file should be ignored
53
+ */
54
+ function shouldIgnore(projectRoot, filePath) {
55
+ const patterns = loadIgnorePatterns(projectRoot);
56
+ const normalizedPath = filePath.replace(/\\/g, "/");
57
+
58
+ // Check against all patterns
59
+ for (const pattern of patterns) {
60
+ // Handle both glob patterns and simple path matches
61
+ if (minimatch) {
62
+ if (minimatch(normalizedPath, pattern, { matchBase: true }) ||
63
+ minimatch(normalizedPath, `**/${pattern}`, { matchBase: true })) {
64
+ return true;
65
+ }
66
+ } else {
67
+ // Fallback: simple pattern matching
68
+ const regexPattern = pattern
69
+ .replace(/\*\*/g, ".*")
70
+ .replace(/\*/g, "[^/]*")
71
+ .replace(/\//g, "\\/");
72
+ const regex = new RegExp(`^${regexPattern}$`);
73
+ if (regex.test(normalizedPath)) {
74
+ return true;
75
+ }
76
+ }
77
+
78
+ // Also check if path contains the pattern as a substring (for directory patterns)
79
+ const cleanPattern = pattern.replace(/\*\*/g, "").replace(/\*/g, "");
80
+ if (normalizedPath.includes(cleanPattern)) {
81
+ return true;
82
+ }
83
+ }
84
+
85
+ // Also check common test file patterns
86
+ const testPatterns = [
87
+ /\/__tests__\//,
88
+ /\.test\.(ts|tsx|js|jsx)$/,
89
+ /\.spec\.(ts|tsx|js|jsx)$/,
90
+ /\/tests\//,
91
+ /\/test\//,
92
+ /\/fixtures\//,
93
+ /\/examples\//,
94
+ /\/templates\//
95
+ ];
96
+
97
+ for (const pattern of testPatterns) {
98
+ if (pattern.test(normalizedPath)) {
99
+ return true;
100
+ }
101
+ }
102
+
103
+ return false;
104
+ }
105
+
106
+ /**
107
+ * Clear cached ignore patterns (for testing)
108
+ */
109
+ function clearCache() {
110
+ ignorePatterns = null;
111
+ ignoreFile = null;
112
+ }
113
+
114
+ module.exports = {
115
+ shouldIgnore,
116
+ loadIgnorePatterns,
117
+ clearCache
118
+ };