@vibecheckai/cli 3.5.1 → 3.5.3

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 (272) hide show
  1. package/bin/registry.js +406 -154
  2. package/bin/runners/context/analyzer.js +52 -1
  3. package/bin/runners/context/generators/mcp.js +15 -13
  4. package/bin/runners/context/git-context.js +3 -1
  5. package/bin/runners/context/proof-context.js +248 -1
  6. package/bin/runners/context/team-conventions.js +33 -7
  7. package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
  8. package/bin/runners/lib/agent-firewall/change-packet/builder.js +488 -0
  9. package/bin/runners/lib/agent-firewall/change-packet/schema.json +228 -0
  10. package/bin/runners/lib/agent-firewall/change-packet/store.js +200 -0
  11. package/bin/runners/lib/agent-firewall/claims/claim-types.js +21 -0
  12. package/bin/runners/lib/agent-firewall/claims/extractor.js +303 -0
  13. package/bin/runners/lib/agent-firewall/claims/patterns.js +24 -0
  14. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  15. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  16. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  17. package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +88 -0
  18. package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +75 -0
  19. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +127 -0
  20. package/bin/runners/lib/agent-firewall/evidence/resolver.js +102 -0
  21. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +213 -0
  22. package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +145 -0
  23. package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +19 -0
  24. package/bin/runners/lib/agent-firewall/fs-hook/installer.js +87 -0
  25. package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +184 -0
  26. package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +163 -0
  27. package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +107 -0
  28. package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +68 -0
  29. package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +66 -0
  30. package/bin/runners/lib/agent-firewall/interceptor/base.js +304 -0
  31. package/bin/runners/lib/agent-firewall/interceptor/cursor.js +35 -0
  32. package/bin/runners/lib/agent-firewall/interceptor/vscode.js +35 -0
  33. package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +34 -0
  34. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  35. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  36. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  37. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  38. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  39. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  40. package/bin/runners/lib/agent-firewall/policy/default-policy.json +90 -0
  41. package/bin/runners/lib/agent-firewall/policy/engine.js +103 -0
  42. package/bin/runners/lib/agent-firewall/policy/loader.js +451 -0
  43. package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +50 -0
  44. package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +50 -0
  45. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +86 -0
  46. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +162 -0
  47. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +189 -0
  48. package/bin/runners/lib/agent-firewall/policy/rules/scope.js +93 -0
  49. package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +57 -0
  50. package/bin/runners/lib/agent-firewall/policy/schema.json +183 -0
  51. package/bin/runners/lib/agent-firewall/policy/verdict.js +54 -0
  52. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  53. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  54. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  55. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  56. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  57. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  58. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  59. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  60. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  61. package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
  62. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  63. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  64. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  65. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  66. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  67. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  68. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  69. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  70. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  71. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  72. package/bin/runners/lib/agent-firewall/truthpack/index.js +67 -0
  73. package/bin/runners/lib/agent-firewall/truthpack/loader.js +137 -0
  74. package/bin/runners/lib/agent-firewall/unblock/planner.js +337 -0
  75. package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
  76. package/bin/runners/lib/analysis-core.js +220 -182
  77. package/bin/runners/lib/analyzers.js +2145 -224
  78. package/bin/runners/lib/api-client.js +269 -0
  79. package/bin/runners/lib/authority-badge.js +425 -0
  80. package/bin/runners/lib/cli-output.js +242 -210
  81. package/bin/runners/lib/default-config.js +127 -0
  82. package/bin/runners/lib/detectors-v2.js +547 -785
  83. package/bin/runners/lib/doctor/modules/security.js +3 -1
  84. package/bin/runners/lib/engine/ast-cache.js +210 -0
  85. package/bin/runners/lib/engine/auth-extractor.js +211 -0
  86. package/bin/runners/lib/engine/billing-extractor.js +112 -0
  87. package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
  88. package/bin/runners/lib/engine/env-extractor.js +207 -0
  89. package/bin/runners/lib/engine/express-extractor.js +208 -0
  90. package/bin/runners/lib/engine/extractors.js +849 -0
  91. package/bin/runners/lib/engine/index.js +207 -0
  92. package/bin/runners/lib/engine/repo-index.js +514 -0
  93. package/bin/runners/lib/engine/types.js +124 -0
  94. package/bin/runners/lib/engines/accessibility-engine.js +190 -0
  95. package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
  96. package/bin/runners/lib/engines/ast-cache.js +99 -0
  97. package/bin/runners/lib/engines/code-quality-engine.js +255 -0
  98. package/bin/runners/lib/engines/console-logs-engine.js +115 -0
  99. package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
  100. package/bin/runners/lib/engines/dead-code-engine.js +198 -0
  101. package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
  102. package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
  103. package/bin/runners/lib/engines/file-filter.js +131 -0
  104. package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
  105. package/bin/runners/lib/engines/mock-data-engine.js +272 -0
  106. package/bin/runners/lib/engines/parallel-processor.js +71 -0
  107. package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
  108. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
  109. package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
  110. package/bin/runners/lib/engines/type-aware-engine.js +152 -0
  111. package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
  112. package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
  113. package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
  114. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  115. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  116. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  117. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  118. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  119. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  120. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  121. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
  122. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  123. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  124. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  125. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  126. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  127. package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
  128. package/bin/runners/lib/entitlements-v2.js +152 -446
  129. package/bin/runners/lib/error-handler.js +60 -12
  130. package/bin/runners/lib/error-messages.js +289 -0
  131. package/bin/runners/lib/evidence-pack.js +7 -1
  132. package/bin/runners/lib/exit-codes.js +275 -0
  133. package/bin/runners/lib/finding-id.js +69 -0
  134. package/bin/runners/lib/finding-sorter.js +89 -0
  135. package/bin/runners/lib/fingerprint.js +377 -0
  136. package/bin/runners/lib/global-flags.js +37 -0
  137. package/bin/runners/lib/help-formatter.js +413 -0
  138. package/bin/runners/lib/logger.js +38 -0
  139. package/bin/runners/lib/next-action.js +560 -0
  140. package/bin/runners/lib/prerequisites.js +149 -0
  141. package/bin/runners/lib/route-detection.js +137 -68
  142. package/bin/runners/lib/route-truth.js +1167 -322
  143. package/bin/runners/lib/scan-output.js +504 -463
  144. package/bin/runners/lib/scan-runner.js +135 -0
  145. package/bin/runners/lib/schemas/ajv-validator.js +464 -0
  146. package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
  147. package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
  148. package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
  149. package/bin/runners/lib/schemas/run-request.schema.json +108 -0
  150. package/bin/runners/lib/schemas/validator.js +27 -0
  151. package/bin/runners/lib/schemas/verdict.schema.json +140 -0
  152. package/bin/runners/lib/ship-output-enterprise.js +239 -0
  153. package/bin/runners/lib/ship-output.js +328 -31
  154. package/bin/runners/lib/terminal-ui.js +234 -731
  155. package/bin/runners/lib/truth.js +1332 -308
  156. package/bin/runners/lib/unified-cli-output.js +604 -0
  157. package/bin/runners/lib/unified-output.js +163 -155
  158. package/bin/runners/lib/upsell.js +104 -204
  159. package/bin/runners/runAgent.d.ts +5 -0
  160. package/bin/runners/runAgent.js +161 -0
  161. package/bin/runners/runAllowlist.js +166 -101
  162. package/bin/runners/runApprove.js +1200 -0
  163. package/bin/runners/runAuth.js +373 -95
  164. package/bin/runners/runCheckpoint.js +59 -21
  165. package/bin/runners/runClassify.js +926 -0
  166. package/bin/runners/runContext.d.ts +4 -0
  167. package/bin/runners/runContext.js +136 -24
  168. package/bin/runners/runDoctor.js +115 -67
  169. package/bin/runners/runEvidencePack.js +239 -96
  170. package/bin/runners/runFirewall.d.ts +5 -0
  171. package/bin/runners/runFirewall.js +134 -0
  172. package/bin/runners/runFirewallHook.d.ts +5 -0
  173. package/bin/runners/runFirewallHook.js +56 -0
  174. package/bin/runners/runFix.js +6 -5
  175. package/bin/runners/runGuard.js +212 -118
  176. package/bin/runners/runInit.js +66 -21
  177. package/bin/runners/runLabs.js +204 -121
  178. package/bin/runners/runMcp.js +131 -60
  179. package/bin/runners/runPolish.d.ts +4 -0
  180. package/bin/runners/runPolish.js +43 -20
  181. package/bin/runners/runProof.zip +0 -0
  182. package/bin/runners/runProve.js +15 -5
  183. package/bin/runners/runQuickstart.js +531 -0
  184. package/bin/runners/runReality.js +14 -0
  185. package/bin/runners/runReport.js +36 -4
  186. package/bin/runners/runScan.js +689 -91
  187. package/bin/runners/runShip.js +96 -40
  188. package/bin/runners/runTruth.d.ts +5 -0
  189. package/bin/runners/runTruth.js +101 -0
  190. package/bin/runners/runValidate.js +21 -4
  191. package/bin/runners/runWatch.js +118 -54
  192. package/bin/scan.js +6 -1
  193. package/bin/vibecheck.js +297 -52
  194. package/mcp-server/HARDENING_SUMMARY.md +299 -0
  195. package/mcp-server/agent-firewall-interceptor.js +500 -0
  196. package/mcp-server/authority-tools.js +569 -0
  197. package/mcp-server/conductor/conflict-resolver.js +588 -0
  198. package/mcp-server/conductor/execution-planner.js +544 -0
  199. package/mcp-server/conductor/index.js +377 -0
  200. package/mcp-server/conductor/lock-manager.js +615 -0
  201. package/mcp-server/conductor/request-queue.js +550 -0
  202. package/mcp-server/conductor/session-manager.js +500 -0
  203. package/mcp-server/conductor/tools.js +510 -0
  204. package/mcp-server/deprecation-middleware.js +282 -0
  205. package/mcp-server/handlers/index.ts +15 -0
  206. package/mcp-server/handlers/tool-handler.ts +474 -591
  207. package/mcp-server/index.js +1748 -1099
  208. package/mcp-server/lib/api-client.cjs +13 -0
  209. package/mcp-server/lib/cache-wrapper.cjs +383 -0
  210. package/mcp-server/lib/error-envelope.js +138 -0
  211. package/mcp-server/lib/executor.ts +428 -721
  212. package/mcp-server/lib/index.ts +19 -0
  213. package/mcp-server/lib/logger.cjs +30 -0
  214. package/mcp-server/lib/rate-limiter.js +166 -0
  215. package/mcp-server/lib/sandbox.test.ts +519 -0
  216. package/mcp-server/lib/sandbox.ts +342 -284
  217. package/mcp-server/lib/types.ts +267 -0
  218. package/mcp-server/logger.js +173 -0
  219. package/mcp-server/package.json +11 -27
  220. package/mcp-server/premium-tools.js +2 -2
  221. package/mcp-server/registry/tool-registry.js +794 -0
  222. package/mcp-server/registry/tools.json +507 -378
  223. package/mcp-server/registry.test.ts +334 -0
  224. package/mcp-server/tests/tier-gating.test.js +297 -0
  225. package/mcp-server/tier-auth.js +492 -347
  226. package/mcp-server/tools-v3.js +950 -0
  227. package/mcp-server/truth-context.js +131 -90
  228. package/mcp-server/truth-firewall-tools.js +1612 -1001
  229. package/mcp-server/tsconfig.json +8 -5
  230. package/mcp-server/vibecheck-2.0-tools.js +14 -1
  231. package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
  232. package/mcp-server/vibecheck-tools.js +2 -2
  233. package/package.json +4 -3
  234. package/bin/runners/runInstall.js +0 -281
  235. package/mcp-server/ARCHITECTURE.md +0 -339
  236. package/mcp-server/__tests__/cache.test.ts +0 -313
  237. package/mcp-server/__tests__/executor.test.ts +0 -239
  238. package/mcp-server/__tests__/fixtures/exclusion-test/.cache/webpack/cache.pack +0 -1
  239. package/mcp-server/__tests__/fixtures/exclusion-test/.next/server/chunk.js +0 -3
  240. package/mcp-server/__tests__/fixtures/exclusion-test/.turbo/cache.json +0 -3
  241. package/mcp-server/__tests__/fixtures/exclusion-test/.venv/lib/env.py +0 -3
  242. package/mcp-server/__tests__/fixtures/exclusion-test/dist/bundle.js +0 -3
  243. package/mcp-server/__tests__/fixtures/exclusion-test/package.json +0 -5
  244. package/mcp-server/__tests__/fixtures/exclusion-test/src/app.ts +0 -5
  245. package/mcp-server/__tests__/fixtures/exclusion-test/venv/lib/config.py +0 -4
  246. package/mcp-server/__tests__/ids.test.ts +0 -345
  247. package/mcp-server/__tests__/integration/tools.test.ts +0 -410
  248. package/mcp-server/__tests__/registry.test.ts +0 -365
  249. package/mcp-server/__tests__/sandbox.test.ts +0 -323
  250. package/mcp-server/__tests__/schemas.test.ts +0 -372
  251. package/mcp-server/benchmarks/run-benchmarks.ts +0 -304
  252. package/mcp-server/examples/doctor.request.json +0 -14
  253. package/mcp-server/examples/doctor.response.json +0 -53
  254. package/mcp-server/examples/error.response.json +0 -15
  255. package/mcp-server/examples/scan.request.json +0 -14
  256. package/mcp-server/examples/scan.response.json +0 -108
  257. package/mcp-server/index-v3.ts +0 -293
  258. package/mcp-server/index.old.js +0 -4137
  259. package/mcp-server/lib/cache.ts +0 -341
  260. package/mcp-server/lib/errors.ts +0 -346
  261. package/mcp-server/lib/ids.ts +0 -238
  262. package/mcp-server/lib/logger.ts +0 -368
  263. package/mcp-server/lib/metrics.ts +0 -365
  264. package/mcp-server/lib/validator.ts +0 -229
  265. package/mcp-server/package-lock.json +0 -165
  266. package/mcp-server/schemas/error-envelope.schema.json +0 -125
  267. package/mcp-server/schemas/finding.schema.json +0 -167
  268. package/mcp-server/schemas/report-artifact.schema.json +0 -88
  269. package/mcp-server/schemas/run-request.schema.json +0 -75
  270. package/mcp-server/schemas/verdict.schema.json +0 -168
  271. package/mcp-server/tier-auth.d.ts +0 -71
  272. package/mcp-server/vitest.config.ts +0 -16
@@ -0,0 +1,474 @@
1
+ /**
2
+ * AI-Powered False Positive Analyzer
3
+ *
4
+ * Uses AI to analyze code context and determine if a violation is a false positive.
5
+ * Helps reduce false positives by understanding code intent and patterns.
6
+ */
7
+
8
+ "use strict";
9
+
10
+ const fs = require("fs");
11
+ const path = require("path");
12
+
13
+ // Cache for AI responses to avoid repeated calls
14
+ const aiCache = new Map();
15
+ const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
16
+
17
+ /**
18
+ * Analyze if a violation is likely a false positive using AI
19
+ * @param {object} params
20
+ * @param {object} params.violation - Violation object
21
+ * @param {object} params.claim - Original claim
22
+ * @param {string} params.filePath - File path where violation occurred
23
+ * @param {string} params.projectRoot - Project root directory
24
+ * @param {object} params.policy - Policy configuration
25
+ * @returns {Promise<object>} Analysis result with isFalsePositive boolean and confidence
26
+ */
27
+ async function analyzeFalsePositive({ violation, claim, filePath, projectRoot, policy }) {
28
+ const ruleConfig = policy.rules?.ai_false_positive_detection;
29
+
30
+ // Check if AI analysis is enabled
31
+ if (!ruleConfig || !ruleConfig.enabled) {
32
+ return { isFalsePositive: false, confidence: 0, reason: "AI analysis disabled" };
33
+ }
34
+
35
+ // Check cache first
36
+ const cacheKey = `${violation.rule}|${claim.value}|${filePath}`;
37
+ const cached = aiCache.get(cacheKey);
38
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
39
+ return cached.result;
40
+ }
41
+
42
+ try {
43
+ // Read the file to get context
44
+ const fullPath = path.join(projectRoot, filePath);
45
+ if (!fs.existsSync(fullPath)) {
46
+ return { isFalsePositive: false, confidence: 0, reason: "File not found" };
47
+ }
48
+
49
+ const fileContent = fs.readFileSync(fullPath, "utf8");
50
+ const lines = fileContent.split("\n");
51
+
52
+ // Extract context around the violation (10 lines before and after)
53
+ const pointer = claim.pointer || violation.claim?.pointer;
54
+ let startLine = 1;
55
+ let endLine = lines.length;
56
+
57
+ if (pointer) {
58
+ const match = pointer.match(/:(\d+)-(\d+)/);
59
+ if (match) {
60
+ const lineNum = parseInt(match[1], 10);
61
+ startLine = Math.max(1, lineNum - 10);
62
+ endLine = Math.min(lines.length, lineNum + 10);
63
+ }
64
+ }
65
+
66
+ const context = lines.slice(startLine - 1, endLine).join("\n");
67
+ const lineNumbers = Array.from({ length: endLine - startLine + 1 }, (_, i) => startLine + i);
68
+ const numberedContext = lines.slice(startLine - 1, endLine)
69
+ .map((line, i) => `${lineNumbers[i]}: ${line}`)
70
+ .join("\n");
71
+
72
+ // Build AI prompt
73
+ const prompt = buildAnalysisPrompt({
74
+ violation,
75
+ claim,
76
+ filePath,
77
+ context: numberedContext,
78
+ ruleType: violation.rule
79
+ });
80
+
81
+ // Try LLM first if enabled, otherwise use heuristics
82
+ let analysis = null;
83
+ if (ruleConfig.useLLM) {
84
+ analysis = await analyzeWithLLM(prompt, ruleConfig);
85
+ }
86
+
87
+ // Fall back to heuristics if LLM not available or failed
88
+ if (!analysis) {
89
+ analysis = await analyzeWithHeuristics({
90
+ violation,
91
+ claim,
92
+ filePath,
93
+ context,
94
+ numberedContext
95
+ });
96
+ }
97
+
98
+ // Cache result
99
+ aiCache.set(cacheKey, {
100
+ timestamp: Date.now(),
101
+ result: analysis
102
+ });
103
+
104
+ return analysis;
105
+ } catch (error) {
106
+ // If AI analysis fails, default to not a false positive
107
+ return {
108
+ isFalsePositive: false,
109
+ confidence: 0,
110
+ reason: `AI analysis failed: ${error.message}`
111
+ };
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Build prompt for AI analysis
117
+ */
118
+ function buildAnalysisPrompt({ violation, claim, filePath, context, ruleType }) {
119
+ return `Analyze this code violation to determine if it's a false positive.
120
+
121
+ Rule: ${ruleType}
122
+ Claim Type: ${claim.type}
123
+ Claim Value: ${claim.value}
124
+ File: ${filePath}
125
+
126
+ Code Context:
127
+ \`\`\`
128
+ ${context}
129
+ \`\`\`
130
+
131
+ Question: Is this a false positive? Consider:
132
+ 1. Is this an import path being mistaken for a route? (e.g., "from './api/content'")
133
+ 2. Is this an external API call that shouldn't be validated? (e.g., "/content/blog" going to backend)
134
+ 3. Is this test/fixture code that should be ignored?
135
+ 4. Is this a legitimate pattern that the rule doesn't understand?
136
+
137
+ Respond with JSON:
138
+ {
139
+ "isFalsePositive": boolean,
140
+ "confidence": 0.0-1.0,
141
+ "reason": "explanation",
142
+ "suggestedFix": "what to do if false positive"
143
+ }`;
144
+ }
145
+
146
+ /**
147
+ * Analyze using heuristics (can be replaced with actual LLM call)
148
+ * This provides immediate value while LLM integration can be added later
149
+ */
150
+ async function analyzeWithHeuristics({ violation, claim, filePath, context, numberedContext }) {
151
+ const claimValue = String(claim.value || "").trim();
152
+ const filePathLower = filePath.toLowerCase();
153
+ const contextLower = context.toLowerCase();
154
+
155
+ // Heuristic 1: Import paths
156
+ if (claimValue.includes("./") || claimValue.includes("../")) {
157
+ // Check if it's in an import/require statement
158
+ const importPatterns = [
159
+ /from\s+['"]\./,
160
+ /require\(['"]\./,
161
+ /import\s+.*from\s+['"]\./
162
+ ];
163
+
164
+ for (const pattern of importPatterns) {
165
+ if (pattern.test(context)) {
166
+ return {
167
+ isFalsePositive: true,
168
+ confidence: 0.95,
169
+ reason: "This appears to be an import path, not a route",
170
+ suggestedFix: "Update route detection to exclude import statements"
171
+ };
172
+ }
173
+ }
174
+ }
175
+
176
+ // Heuristic 2: External API calls (not starting with /api/)
177
+ if (violation.rule === "ghost_route" && claimValue.startsWith("/")) {
178
+ if (!claimValue.startsWith("/api/")) {
179
+ return {
180
+ isFalsePositive: true,
181
+ confidence: 0.9,
182
+ reason: "This is an external API call to the backend, not a Next.js route",
183
+ suggestedFix: "Only validate Next.js API routes (starting with /api/)"
184
+ };
185
+ }
186
+ }
187
+
188
+ // Heuristic 3: Test files
189
+ if (filePathLower.includes("test") ||
190
+ filePathLower.includes("spec") ||
191
+ filePathLower.includes("fixture") ||
192
+ filePathLower.includes("mock")) {
193
+ return {
194
+ isFalsePositive: true,
195
+ confidence: 0.85,
196
+ reason: "This is in a test/fixture file",
197
+ suggestedFix: "Add test files to .vibecheckignore"
198
+ };
199
+ }
200
+
201
+ // Heuristic 4: Comments or strings
202
+ if (claimValue.includes("//") ||
203
+ claimValue.includes("/*") ||
204
+ contextLower.includes(`"${claimValue}"`) ||
205
+ contextLower.includes(`'${claimValue}'`)) {
206
+ // Check if it's in a comment
207
+ const lines = numberedContext.split("\n");
208
+ for (const line of lines) {
209
+ if (line.includes(claimValue)) {
210
+ const beforeValue = line.substring(0, line.indexOf(claimValue));
211
+ if (beforeValue.includes("//") || beforeValue.includes("/*")) {
212
+ return {
213
+ isFalsePositive: true,
214
+ confidence: 0.8,
215
+ reason: "This appears to be in a comment",
216
+ suggestedFix: "Skip route detection in comments"
217
+ };
218
+ }
219
+ }
220
+ }
221
+ }
222
+
223
+ // Heuristic 5: Environment variable names (for ghost_env)
224
+ if (violation.rule === "ghost_env") {
225
+ // Check if it's a standard/system env var that doesn't need declaration
226
+ const systemEnvVars = [
227
+ "NODE_ENV", "PATH", "HOME", "USER", "SHELL", "TMPDIR",
228
+ "CI", "GITHUB_ACTIONS", "GITLAB_CI", "CIRCLECI", "BUILDKITE",
229
+ "COLORTERM", "TERM", "LANG", "LC_ALL"
230
+ ];
231
+
232
+ if (systemEnvVars.includes(claimValue)) {
233
+ return {
234
+ isFalsePositive: true,
235
+ confidence: 0.9,
236
+ reason: "This is a standard system environment variable",
237
+ suggestedFix: "Add system env vars to allowlist"
238
+ };
239
+ }
240
+ }
241
+
242
+ // Heuristic 6: Success messages in UI components (for fake_success_ui)
243
+ if (violation.rule === "fake_success_ui") {
244
+ // Check if it's just displaying a version number or static text
245
+ if (claimValue.match(/v?\d+\.\d+\.\d+/) || // Version numbers
246
+ claimValue.length < 10) { // Very short messages
247
+ return {
248
+ isFalsePositive: true,
249
+ confidence: 0.7,
250
+ reason: "This appears to be a version number or static text, not a success message",
251
+ suggestedFix: "Improve success message detection to exclude version numbers"
252
+ };
253
+ }
254
+ }
255
+
256
+ // Default: not a false positive
257
+ return {
258
+ isFalsePositive: false,
259
+ confidence: 0.5,
260
+ reason: "No clear indicators of false positive",
261
+ suggestedFix: null
262
+ };
263
+ }
264
+
265
+ /**
266
+ * Call actual LLM for analysis (when AI is available)
267
+ * Uses OpenAI or Anthropic API directly
268
+ */
269
+ async function analyzeWithLLM(prompt, config = {}) {
270
+ const { useLLM = false, provider = "openai" } = config;
271
+
272
+ if (!useLLM) {
273
+ return null;
274
+ }
275
+
276
+ const hasOpenAI = !!process.env.OPENAI_API_KEY;
277
+ const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
278
+
279
+ if (!hasOpenAI && !hasAnthropic) {
280
+ return null;
281
+ }
282
+
283
+ try {
284
+ // Try OpenAI first (default)
285
+ if ((provider === "openai" || !hasAnthropic) && hasOpenAI) {
286
+ return await callOpenAI(prompt);
287
+ }
288
+
289
+ // Try Anthropic
290
+ if ((provider === "anthropic" || !hasOpenAI) && hasAnthropic) {
291
+ return await callAnthropic(prompt);
292
+ }
293
+ } catch (error) {
294
+ // If LLM call fails, fall back to heuristics
295
+ console.warn(`[AI] LLM analysis failed: ${error.message}`);
296
+ return null;
297
+ }
298
+
299
+ return null;
300
+ }
301
+
302
+ /**
303
+ * Call OpenAI API
304
+ */
305
+ async function callOpenAI(prompt) {
306
+ const https = require("https");
307
+ const http = require("http");
308
+
309
+ const apiKey = process.env.OPENAI_API_KEY;
310
+ if (!apiKey) return null;
311
+
312
+ const requestBody = JSON.stringify({
313
+ model: "gpt-4o-mini", // Fast and cheap model
314
+ messages: [
315
+ {
316
+ role: "system",
317
+ content: "You are a code analysis assistant. Analyze if a code violation is a false positive. Respond with JSON only."
318
+ },
319
+ {
320
+ role: "user",
321
+ content: prompt
322
+ }
323
+ ],
324
+ response_format: { type: "json_object" },
325
+ temperature: 0.3,
326
+ max_tokens: 500
327
+ });
328
+
329
+ return new Promise((resolve, reject) => {
330
+ const options = {
331
+ hostname: "api.openai.com",
332
+ path: "/v1/chat/completions",
333
+ method: "POST",
334
+ headers: {
335
+ "Content-Type": "application/json",
336
+ "Authorization": `Bearer ${apiKey}`,
337
+ "Content-Length": Buffer.byteLength(requestBody)
338
+ },
339
+ timeout: 5000
340
+ };
341
+
342
+ const req = https.request(options, (res) => {
343
+ let data = "";
344
+
345
+ res.on("data", (chunk) => {
346
+ data += chunk;
347
+ });
348
+
349
+ res.on("end", () => {
350
+ try {
351
+ const response = JSON.parse(data);
352
+ if (response.error) {
353
+ reject(new Error(response.error.message));
354
+ return;
355
+ }
356
+
357
+ const content = response.choices?.[0]?.message?.content;
358
+ if (!content) {
359
+ resolve(null);
360
+ return;
361
+ }
362
+
363
+ const result = JSON.parse(content);
364
+ resolve(result);
365
+ } catch (error) {
366
+ reject(error);
367
+ }
368
+ });
369
+ });
370
+
371
+ req.on("error", reject);
372
+ req.on("timeout", () => {
373
+ req.destroy();
374
+ reject(new Error("Request timeout"));
375
+ });
376
+
377
+ req.write(requestBody);
378
+ req.end();
379
+ });
380
+ }
381
+
382
+ /**
383
+ * Call Anthropic API
384
+ */
385
+ async function callAnthropic(prompt) {
386
+ const https = require("https");
387
+
388
+ const apiKey = process.env.ANTHROPIC_API_KEY;
389
+ if (!apiKey) return null;
390
+
391
+ const requestBody = JSON.stringify({
392
+ model: "claude-3-haiku-20240307", // Fast and cheap model
393
+ max_tokens: 500,
394
+ messages: [
395
+ {
396
+ role: "user",
397
+ content: prompt
398
+ }
399
+ ],
400
+ system: "You are a code analysis assistant. Analyze if a code violation is a false positive. Respond with JSON only."
401
+ });
402
+
403
+ return new Promise((resolve, reject) => {
404
+ const options = {
405
+ hostname: "api.anthropic.com",
406
+ path: "/v1/messages",
407
+ method: "POST",
408
+ headers: {
409
+ "Content-Type": "application/json",
410
+ "x-api-key": apiKey,
411
+ "anthropic-version": "2023-06-01",
412
+ "Content-Length": Buffer.byteLength(requestBody)
413
+ },
414
+ timeout: 5000
415
+ };
416
+
417
+ const req = https.request(options, (res) => {
418
+ let data = "";
419
+
420
+ res.on("data", (chunk) => {
421
+ data += chunk;
422
+ });
423
+
424
+ res.on("end", () => {
425
+ try {
426
+ const response = JSON.parse(data);
427
+ if (response.error) {
428
+ reject(new Error(response.error.message));
429
+ return;
430
+ }
431
+
432
+ const content = response.content?.[0]?.text;
433
+ if (!content) {
434
+ resolve(null);
435
+ return;
436
+ }
437
+
438
+ // Extract JSON from response (might be wrapped in markdown)
439
+ const jsonMatch = content.match(/\{[\s\S]*\}/);
440
+ if (!jsonMatch) {
441
+ resolve(null);
442
+ return;
443
+ }
444
+
445
+ const result = JSON.parse(jsonMatch[0]);
446
+ resolve(result);
447
+ } catch (error) {
448
+ reject(error);
449
+ }
450
+ });
451
+ });
452
+
453
+ req.on("error", reject);
454
+ req.on("timeout", () => {
455
+ req.destroy();
456
+ reject(new Error("Request timeout"));
457
+ });
458
+
459
+ req.write(requestBody);
460
+ req.end();
461
+ });
462
+ }
463
+
464
+ /**
465
+ * Clear AI cache (useful for testing)
466
+ */
467
+ function clearCache() {
468
+ aiCache.clear();
469
+ }
470
+
471
+ module.exports = {
472
+ analyzeFalsePositive,
473
+ clearCache
474
+ };