@vibecheckai/cli 3.5.0 → 3.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/bin/registry.js +214 -237
  2. package/bin/runners/cli-utils.js +33 -2
  3. package/bin/runners/context/analyzer.js +52 -1
  4. package/bin/runners/context/generators/cursor.js +2 -49
  5. package/bin/runners/context/git-context.js +3 -1
  6. package/bin/runners/context/team-conventions.js +33 -7
  7. package/bin/runners/lib/analysis-core.js +25 -5
  8. package/bin/runners/lib/analyzers.js +431 -481
  9. package/bin/runners/lib/default-config.js +127 -0
  10. package/bin/runners/lib/doctor/modules/security.js +3 -1
  11. package/bin/runners/lib/engine/ast-cache.js +210 -0
  12. package/bin/runners/lib/engine/auth-extractor.js +211 -0
  13. package/bin/runners/lib/engine/billing-extractor.js +112 -0
  14. package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
  15. package/bin/runners/lib/engine/env-extractor.js +207 -0
  16. package/bin/runners/lib/engine/express-extractor.js +208 -0
  17. package/bin/runners/lib/engine/extractors.js +849 -0
  18. package/bin/runners/lib/engine/index.js +207 -0
  19. package/bin/runners/lib/engine/repo-index.js +514 -0
  20. package/bin/runners/lib/engine/types.js +124 -0
  21. package/bin/runners/lib/engines/accessibility-engine.js +18 -218
  22. package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
  23. package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
  24. package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
  25. package/bin/runners/lib/engines/mock-data-engine.js +10 -53
  26. package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
  27. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
  28. package/bin/runners/lib/engines/type-aware-engine.js +39 -263
  29. package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
  30. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  31. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  32. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  33. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  34. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  35. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  36. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  37. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
  38. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  39. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  40. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  41. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  42. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  43. package/bin/runners/lib/entitlements-v2.js +73 -97
  44. package/bin/runners/lib/error-handler.js +44 -3
  45. package/bin/runners/lib/error-messages.js +289 -0
  46. package/bin/runners/lib/evidence-pack.js +7 -1
  47. package/bin/runners/lib/finding-id.js +69 -0
  48. package/bin/runners/lib/finding-sorter.js +89 -0
  49. package/bin/runners/lib/html-proof-report.js +700 -350
  50. package/bin/runners/lib/missions/plan.js +6 -46
  51. package/bin/runners/lib/missions/templates.js +0 -232
  52. package/bin/runners/lib/next-action.js +560 -0
  53. package/bin/runners/lib/prerequisites.js +149 -0
  54. package/bin/runners/lib/route-detection.js +137 -68
  55. package/bin/runners/lib/scan-output.js +91 -76
  56. package/bin/runners/lib/scan-runner.js +135 -0
  57. package/bin/runners/lib/schemas/ajv-validator.js +464 -0
  58. package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
  59. package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
  60. package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
  61. package/bin/runners/lib/schemas/run-request.schema.json +108 -0
  62. package/bin/runners/lib/schemas/validator.js +27 -0
  63. package/bin/runners/lib/schemas/verdict.schema.json +140 -0
  64. package/bin/runners/lib/ship-output-enterprise.js +23 -23
  65. package/bin/runners/lib/ship-output.js +75 -31
  66. package/bin/runners/lib/terminal-ui.js +6 -113
  67. package/bin/runners/lib/truth.js +351 -10
  68. package/bin/runners/lib/unified-cli-output.js +430 -603
  69. package/bin/runners/lib/unified-output.js +13 -9
  70. package/bin/runners/runAIAgent.js +10 -5
  71. package/bin/runners/runAgent.js +0 -3
  72. package/bin/runners/runAllowlist.js +389 -0
  73. package/bin/runners/runApprove.js +0 -33
  74. package/bin/runners/runAuth.js +73 -45
  75. package/bin/runners/runCheckpoint.js +51 -11
  76. package/bin/runners/runClassify.js +85 -21
  77. package/bin/runners/runContext.js +0 -3
  78. package/bin/runners/runDoctor.js +41 -28
  79. package/bin/runners/runEvidencePack.js +362 -0
  80. package/bin/runners/runFirewall.js +0 -3
  81. package/bin/runners/runFirewallHook.js +0 -3
  82. package/bin/runners/runFix.js +66 -76
  83. package/bin/runners/runGuard.js +18 -411
  84. package/bin/runners/runInit.js +113 -30
  85. package/bin/runners/runLabs.js +424 -0
  86. package/bin/runners/runMcp.js +19 -25
  87. package/bin/runners/runPolish.js +64 -240
  88. package/bin/runners/runPromptFirewall.js +12 -5
  89. package/bin/runners/runProve.js +57 -22
  90. package/bin/runners/runQuickstart.js +531 -0
  91. package/bin/runners/runReality.js +59 -68
  92. package/bin/runners/runReport.js +38 -33
  93. package/bin/runners/runRuntime.js +8 -5
  94. package/bin/runners/runScan.js +1413 -190
  95. package/bin/runners/runShip.js +113 -719
  96. package/bin/runners/runTruth.js +0 -3
  97. package/bin/runners/runValidate.js +13 -9
  98. package/bin/runners/runWatch.js +23 -14
  99. package/bin/scan.js +6 -1
  100. package/bin/vibecheck.js +204 -185
  101. package/mcp-server/deprecation-middleware.js +282 -0
  102. package/mcp-server/handlers/index.ts +15 -0
  103. package/mcp-server/handlers/tool-handler.ts +554 -0
  104. package/mcp-server/index-v1.js +698 -0
  105. package/mcp-server/index.js +210 -238
  106. package/mcp-server/lib/cache-wrapper.cjs +383 -0
  107. package/mcp-server/lib/error-envelope.js +138 -0
  108. package/mcp-server/lib/executor.ts +499 -0
  109. package/mcp-server/lib/index.ts +19 -0
  110. package/mcp-server/lib/rate-limiter.js +166 -0
  111. package/mcp-server/lib/sandbox.test.ts +519 -0
  112. package/mcp-server/lib/sandbox.ts +395 -0
  113. package/mcp-server/lib/types.ts +267 -0
  114. package/mcp-server/package.json +12 -3
  115. package/mcp-server/registry/tool-registry.js +794 -0
  116. package/mcp-server/registry/tools.json +605 -0
  117. package/mcp-server/registry.test.ts +334 -0
  118. package/mcp-server/tests/tier-gating.test.js +297 -0
  119. package/mcp-server/tier-auth.js +378 -45
  120. package/mcp-server/tools-v3.js +353 -442
  121. package/mcp-server/tsconfig.json +37 -0
  122. package/mcp-server/vibecheck-2.0-tools.js +14 -1
  123. package/package.json +1 -1
  124. package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
  125. package/bin/runners/lib/audit-logger.js +0 -532
  126. package/bin/runners/lib/authority/authorities/architecture.js +0 -364
  127. package/bin/runners/lib/authority/authorities/compliance.js +0 -341
  128. package/bin/runners/lib/authority/authorities/human.js +0 -343
  129. package/bin/runners/lib/authority/authorities/quality.js +0 -420
  130. package/bin/runners/lib/authority/authorities/security.js +0 -228
  131. package/bin/runners/lib/authority/index.js +0 -293
  132. package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
  133. package/bin/runners/lib/cli-charts.js +0 -368
  134. package/bin/runners/lib/cli-config-display.js +0 -405
  135. package/bin/runners/lib/cli-demo.js +0 -275
  136. package/bin/runners/lib/cli-errors.js +0 -438
  137. package/bin/runners/lib/cli-help-formatter.js +0 -439
  138. package/bin/runners/lib/cli-interactive-menu.js +0 -509
  139. package/bin/runners/lib/cli-prompts.js +0 -441
  140. package/bin/runners/lib/cli-scan-cards.js +0 -362
  141. package/bin/runners/lib/compliance-reporter.js +0 -710
  142. package/bin/runners/lib/conductor/index.js +0 -671
  143. package/bin/runners/lib/easy/README.md +0 -123
  144. package/bin/runners/lib/easy/index.js +0 -140
  145. package/bin/runners/lib/easy/interactive-wizard.js +0 -788
  146. package/bin/runners/lib/easy/one-click-firewall.js +0 -564
  147. package/bin/runners/lib/easy/zero-config-reality.js +0 -714
  148. package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
  149. package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
  150. package/bin/runners/lib/engines/confidence-scoring.js +0 -276
  151. package/bin/runners/lib/engines/context-detection.js +0 -264
  152. package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
  153. package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
  154. package/bin/runners/lib/engines/env-variables-engine.js +0 -458
  155. package/bin/runners/lib/engines/error-handling-engine.js +0 -437
  156. package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
  157. package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
  158. package/bin/runners/lib/engines/framework-detection.js +0 -508
  159. package/bin/runners/lib/engines/import-order-engine.js +0 -429
  160. package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
  161. package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
  162. package/bin/runners/lib/engines/orchestrator.js +0 -334
  163. package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
  164. package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
  165. package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
  166. package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
  167. package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
  168. package/bin/runners/lib/enhanced-features/index.js +0 -305
  169. package/bin/runners/lib/enhanced-output.js +0 -631
  170. package/bin/runners/lib/enterprise.js +0 -300
  171. package/bin/runners/lib/firewall/command-validator.js +0 -351
  172. package/bin/runners/lib/firewall/config.js +0 -341
  173. package/bin/runners/lib/firewall/content-validator.js +0 -519
  174. package/bin/runners/lib/firewall/index.js +0 -101
  175. package/bin/runners/lib/firewall/path-validator.js +0 -256
  176. package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
  177. package/bin/runners/lib/mcp-utils.js +0 -425
  178. package/bin/runners/lib/output/index.js +0 -1022
  179. package/bin/runners/lib/policy-engine.js +0 -652
  180. package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
  181. package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
  182. package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
  183. package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
  184. package/bin/runners/lib/polish/autofix/index.js +0 -200
  185. package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
  186. package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
  187. package/bin/runners/lib/polish/backend-checks.js +0 -148
  188. package/bin/runners/lib/polish/documentation-checks.js +0 -111
  189. package/bin/runners/lib/polish/frontend-checks.js +0 -168
  190. package/bin/runners/lib/polish/index.js +0 -71
  191. package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
  192. package/bin/runners/lib/polish/library-detection.js +0 -175
  193. package/bin/runners/lib/polish/performance-checks.js +0 -100
  194. package/bin/runners/lib/polish/security-checks.js +0 -148
  195. package/bin/runners/lib/polish/utils.js +0 -203
  196. package/bin/runners/lib/prompt-builder.js +0 -540
  197. package/bin/runners/lib/proof-certificate.js +0 -634
  198. package/bin/runners/lib/reality/accessibility-audit.js +0 -946
  199. package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
  200. package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
  201. package/bin/runners/lib/reality/performance-tracker.js +0 -1077
  202. package/bin/runners/lib/reality/scenario-generator.js +0 -1404
  203. package/bin/runners/lib/reality/visual-regression.js +0 -852
  204. package/bin/runners/lib/reality-profiler.js +0 -717
  205. package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
  206. package/bin/runners/lib/review/ai-code-review.js +0 -832
  207. package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
  208. package/bin/runners/lib/sbom-generator.js +0 -641
  209. package/bin/runners/lib/scan-output-enhanced.js +0 -512
  210. package/bin/runners/lib/security/owasp-scanner.js +0 -939
  211. package/bin/runners/lib/validators/contract-validator.js +0 -283
  212. package/bin/runners/lib/validators/dead-export-detector.js +0 -279
  213. package/bin/runners/lib/validators/dep-audit.js +0 -245
  214. package/bin/runners/lib/validators/env-validator.js +0 -319
  215. package/bin/runners/lib/validators/index.js +0 -120
  216. package/bin/runners/lib/validators/license-checker.js +0 -252
  217. package/bin/runners/lib/validators/route-validator.js +0 -290
  218. package/bin/runners/runAuthority.js +0 -528
  219. package/bin/runners/runConductor.js +0 -772
  220. package/bin/runners/runContainer.js +0 -366
  221. package/bin/runners/runEasy.js +0 -410
  222. package/bin/runners/runIaC.js +0 -372
  223. package/bin/runners/runVibe.js +0 -791
  224. package/mcp-server/tools.js +0 -495
@@ -1,283 +0,0 @@
1
- /**
2
- * Contract Validator - Ship-only check
3
- * Validates API contracts match implementation
4
- */
5
-
6
- "use strict";
7
-
8
- const path = require("path");
9
- const fs = require("fs");
10
- const fg = require("fast-glob");
11
-
12
- /**
13
- * Validate API contracts against implementation
14
- * Checks:
15
- * - OpenAPI/Swagger specs match actual routes
16
- * - TypeScript types match API responses
17
- * - tRPC routers match client usage
18
- */
19
- async function validateContracts(projectPath) {
20
- const findings = [];
21
- const startTime = Date.now();
22
-
23
- try {
24
- // Check for OpenAPI/Swagger specs
25
- const openApiFindings = await validateOpenApiContracts(projectPath);
26
- findings.push(...openApiFindings);
27
-
28
- // Check for tRPC contracts
29
- const trpcFindings = await validateTrpcContracts(projectPath);
30
- findings.push(...trpcFindings);
31
-
32
- // Check for GraphQL schemas
33
- const graphqlFindings = await validateGraphqlContracts(projectPath);
34
- findings.push(...graphqlFindings);
35
-
36
- } catch (error) {
37
- findings.push({
38
- id: "CONTRACT_VALIDATION_ERROR",
39
- category: "ContractValidation",
40
- severity: "INFO",
41
- title: "Contract validation incomplete",
42
- message: `Could not fully validate contracts: ${error.message}`,
43
- confidence: "low",
44
- type: "validation_error",
45
- });
46
- }
47
-
48
- return {
49
- findings,
50
- duration: Date.now() - startTime,
51
- type: "contract-validator",
52
- };
53
- }
54
-
55
- async function validateOpenApiContracts(projectPath) {
56
- const findings = [];
57
-
58
- // Find OpenAPI/Swagger spec files
59
- const specFiles = fg.sync([
60
- "**/openapi.{json,yaml,yml}",
61
- "**/swagger.{json,yaml,yml}",
62
- "**/api-spec.{json,yaml,yml}",
63
- ], {
64
- cwd: projectPath,
65
- absolute: true,
66
- ignore: ["**/node_modules/**"],
67
- });
68
-
69
- if (specFiles.length === 0) {
70
- // No OpenAPI spec - not an error, just no contract to validate
71
- return findings;
72
- }
73
-
74
- for (const specFile of specFiles) {
75
- try {
76
- const content = fs.readFileSync(specFile, "utf8");
77
- const relativePath = path.relative(projectPath, specFile).replace(/\\/g, "/");
78
-
79
- // Basic validation - check if it parses
80
- let spec;
81
- if (specFile.endsWith(".json")) {
82
- spec = JSON.parse(content);
83
- } else {
84
- // YAML parsing would require yaml package
85
- continue;
86
- }
87
-
88
- // Check for paths without implementations
89
- const paths = spec.paths || {};
90
-
91
- for (const [routePath, methods] of Object.entries(paths)) {
92
- for (const method of Object.keys(methods)) {
93
- if (["parameters", "servers", "$ref"].includes(method)) continue;
94
-
95
- // Check if this route exists in codebase
96
- const routeExists = await checkRouteExists(projectPath, routePath, method);
97
-
98
- if (!routeExists) {
99
- findings.push({
100
- id: `CONTRACT_MISSING_IMPL_${hashString(routePath + method)}`,
101
- category: "ContractValidation",
102
- severity: "WARN",
103
- title: `API spec defines route without implementation: ${method.toUpperCase()} ${routePath}`,
104
- message: `OpenAPI spec defines ${method.toUpperCase()} ${routePath} but no matching handler found`,
105
- file: relativePath,
106
- confidence: "medium",
107
- type: "missing_implementation",
108
- });
109
- }
110
- }
111
- }
112
-
113
- } catch (error) {
114
- findings.push({
115
- id: `CONTRACT_PARSE_ERROR_${hashString(specFile)}`,
116
- category: "ContractValidation",
117
- severity: "WARN",
118
- title: `Could not parse API spec: ${path.basename(specFile)}`,
119
- message: error.message,
120
- file: path.relative(projectPath, specFile).replace(/\\/g, "/"),
121
- confidence: "high",
122
- type: "parse_error",
123
- });
124
- }
125
- }
126
-
127
- return findings;
128
- }
129
-
130
- async function validateTrpcContracts(projectPath) {
131
- const findings = [];
132
-
133
- // Find tRPC router files
134
- const trpcFiles = fg.sync([
135
- "**/trpc/**/*.{ts,tsx}",
136
- "**/server/routers/**/*.{ts,tsx}",
137
- "**/*.router.{ts,tsx}",
138
- ], {
139
- cwd: projectPath,
140
- absolute: true,
141
- ignore: ["**/node_modules/**", "**/*.test.*", "**/*.spec.*"],
142
- });
143
-
144
- if (trpcFiles.length === 0) return findings;
145
-
146
- // Extract procedure names from routers
147
- const definedProcedures = new Set();
148
-
149
- for (const file of trpcFiles) {
150
- try {
151
- const content = fs.readFileSync(file, "utf8");
152
-
153
- // Find procedure definitions
154
- const procedureMatches = content.matchAll(/\.(?:query|mutation|subscription)\s*\(/g);
155
- const nameMatches = content.matchAll(/(\w+):\s*(?:router|publicProcedure|protectedProcedure)/g);
156
-
157
- for (const match of nameMatches) {
158
- definedProcedures.add(match[1]);
159
- }
160
- } catch {
161
- // Skip files that can't be read
162
- }
163
- }
164
-
165
- // Find tRPC client usage
166
- const clientFiles = fg.sync(["**/*.{ts,tsx}"], {
167
- cwd: projectPath,
168
- absolute: true,
169
- ignore: ["**/node_modules/**", "**/server/**", "**/*.test.*"],
170
- });
171
-
172
- for (const file of clientFiles) {
173
- try {
174
- const content = fs.readFileSync(file, "utf8");
175
- const relativePath = path.relative(projectPath, file).replace(/\\/g, "/");
176
-
177
- // Find trpc client calls
178
- const clientMatches = content.matchAll(/trpc\.(\w+)\.(?:useQuery|useMutation|useSubscription)/g);
179
-
180
- for (const match of clientMatches) {
181
- const procedureName = match[1];
182
- const line = content.substring(0, match.index).split("\n").length;
183
-
184
- if (!definedProcedures.has(procedureName) && procedureName !== "useContext") {
185
- findings.push({
186
- id: `TRPC_MISSING_PROCEDURE_${hashString(procedureName + file)}`,
187
- category: "ContractValidation",
188
- severity: "WARN",
189
- title: `tRPC procedure may not exist: ${procedureName}`,
190
- message: `Client calls trpc.${procedureName} but no matching router procedure found`,
191
- file: relativePath,
192
- line,
193
- confidence: "medium",
194
- type: "missing_procedure",
195
- });
196
- }
197
- }
198
- } catch {
199
- // Skip files that can't be read
200
- }
201
- }
202
-
203
- return findings;
204
- }
205
-
206
- async function validateGraphqlContracts(projectPath) {
207
- const findings = [];
208
-
209
- // Find GraphQL schema files
210
- const schemaFiles = fg.sync([
211
- "**/*.graphql",
212
- "**/*.gql",
213
- "**/schema.{ts,js}",
214
- ], {
215
- cwd: projectPath,
216
- absolute: true,
217
- ignore: ["**/node_modules/**"],
218
- });
219
-
220
- if (schemaFiles.length === 0) return findings;
221
-
222
- // Basic check: schema files should have resolvers
223
- const resolverFiles = fg.sync([
224
- "**/resolvers/**/*.{ts,js}",
225
- "**/*.resolvers.{ts,js}",
226
- ], {
227
- cwd: projectPath,
228
- absolute: true,
229
- ignore: ["**/node_modules/**"],
230
- });
231
-
232
- if (schemaFiles.length > 0 && resolverFiles.length === 0) {
233
- findings.push({
234
- id: "GRAPHQL_NO_RESOLVERS",
235
- category: "ContractValidation",
236
- severity: "WARN",
237
- title: "GraphQL schema found but no resolvers detected",
238
- message: "GraphQL schema files exist but no resolver implementations found",
239
- confidence: "medium",
240
- type: "missing_resolvers",
241
- });
242
- }
243
-
244
- return findings;
245
- }
246
-
247
- async function checkRouteExists(projectPath, routePath, method) {
248
- // Convert OpenAPI path to file path pattern
249
- const normalizedPath = routePath
250
- .replace(/\{(\w+)\}/g, "[$1]") // {id} -> [id]
251
- .replace(/^\//, "");
252
-
253
- // Check for Next.js API routes
254
- const patterns = [
255
- `**/pages/api/${normalizedPath}.{ts,js}`,
256
- `**/pages/api/${normalizedPath}/index.{ts,js}`,
257
- `**/app/api/${normalizedPath}/route.{ts,js}`,
258
- ];
259
-
260
- for (const pattern of patterns) {
261
- const files = fg.sync([pattern], {
262
- cwd: projectPath,
263
- ignore: ["**/node_modules/**"],
264
- });
265
- if (files.length > 0) return true;
266
- }
267
-
268
- return false;
269
- }
270
-
271
- function hashString(str) {
272
- let hash = 0;
273
- for (let i = 0; i < str.length; i++) {
274
- const char = str.charCodeAt(i);
275
- hash = ((hash << 5) - hash) + char;
276
- hash = hash & hash;
277
- }
278
- return Math.abs(hash).toString(36).substring(0, 8);
279
- }
280
-
281
- module.exports = {
282
- validateContracts,
283
- };
@@ -1,279 +0,0 @@
1
- /**
2
- * Dead Export Detector - Ship-only check
3
- * Detects exports that are never imported anywhere
4
- */
5
-
6
- "use strict";
7
-
8
- const path = require("path");
9
- const fs = require("fs");
10
- const fg = require("fast-glob");
11
-
12
- /**
13
- * Detect exports that are never imported
14
- * This helps identify dead code that should be removed
15
- */
16
- async function detectDeadExports(projectPath) {
17
- const findings = [];
18
- const startTime = Date.now();
19
-
20
- try {
21
- // Get all source files
22
- const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
23
- cwd: projectPath,
24
- absolute: true,
25
- ignore: [
26
- "**/node_modules/**",
27
- "**/dist/**",
28
- "**/build/**",
29
- "**/.next/**",
30
- "**/*.test.*",
31
- "**/*.spec.*",
32
- "**/coverage/**",
33
- "**/__tests__/**",
34
- "**/tests/**",
35
- "**/index.ts",
36
- "**/index.js",
37
- "**/index.tsx",
38
- "**/index.jsx",
39
- ],
40
- });
41
-
42
- // Phase 1: Collect all exports
43
- const exports = new Map(); // export name -> { file, line, type }
44
-
45
- for (const file of files) {
46
- try {
47
- const content = fs.readFileSync(file, "utf8");
48
- const relativePath = path.relative(projectPath, file).replace(/\\/g, "/");
49
-
50
- const fileExports = extractExports(content, relativePath);
51
- for (const exp of fileExports) {
52
- const key = `${relativePath}:${exp.name}`;
53
- exports.set(key, exp);
54
- }
55
- } catch {
56
- // Skip files that can't be read
57
- }
58
- }
59
-
60
- // Phase 2: Collect all imports
61
- const imports = new Set(); // imported names
62
-
63
- for (const file of files) {
64
- try {
65
- const content = fs.readFileSync(file, "utf8");
66
- const fileImports = extractImports(content);
67
- for (const imp of fileImports) {
68
- imports.add(imp);
69
- }
70
- } catch {
71
- // Skip files that can't be read
72
- }
73
- }
74
-
75
- // Phase 3: Find dead exports
76
- let deadCount = 0;
77
- const MAX_DEAD_EXPORTS = 20; // Limit to prevent noise
78
-
79
- for (const [key, exp] of exports.entries()) {
80
- // Skip entry points and common patterns
81
- if (isLikelyEntryPoint(exp.file, exp.name)) continue;
82
-
83
- // Check if this export is imported anywhere
84
- if (!imports.has(exp.name)) {
85
- // Double-check: search for the name in all files
86
- const isUsed = await isExportUsedAnywhere(exp.name, files, exp.file);
87
-
88
- if (!isUsed) {
89
- deadCount++;
90
-
91
- if (deadCount <= MAX_DEAD_EXPORTS) {
92
- findings.push({
93
- id: `DEAD_EXPORT_${hashString(key)}`,
94
- category: "DeadExport",
95
- severity: "INFO",
96
- title: `Potentially unused export: ${exp.name}`,
97
- message: `${exp.name} is exported from ${exp.file} but may not be imported anywhere`,
98
- file: exp.file,
99
- line: exp.line,
100
- confidence: "low",
101
- type: "dead_export",
102
- });
103
- }
104
- }
105
- }
106
- }
107
-
108
- // Summary if there are many dead exports
109
- if (deadCount > MAX_DEAD_EXPORTS) {
110
- findings.push({
111
- id: "DEAD_EXPORT_SUMMARY",
112
- category: "DeadExport",
113
- severity: "WARN",
114
- title: `${deadCount} potentially unused exports found`,
115
- message: `Consider removing unused exports to reduce bundle size and improve maintainability`,
116
- confidence: "low",
117
- type: "dead_export_summary",
118
- });
119
- }
120
-
121
- } catch (error) {
122
- findings.push({
123
- id: "DEAD_EXPORT_ERROR",
124
- category: "DeadExport",
125
- severity: "INFO",
126
- title: "Dead export detection incomplete",
127
- message: `Could not complete dead export detection: ${error.message}`,
128
- confidence: "low",
129
- type: "detection_error",
130
- });
131
- }
132
-
133
- return {
134
- findings,
135
- duration: Date.now() - startTime,
136
- type: "dead-export-detector",
137
- };
138
- }
139
-
140
- function extractExports(content, file) {
141
- const exports = [];
142
- const lines = content.split("\n");
143
-
144
- for (let i = 0; i < lines.length; i++) {
145
- const line = lines[i];
146
-
147
- // export const/let/var/function/class name
148
- const namedExportMatch = line.match(/export\s+(?:const|let|var|function|class|type|interface|enum)\s+([A-Za-z_$][A-Za-z0-9_$]*)/);
149
- if (namedExportMatch) {
150
- exports.push({
151
- name: namedExportMatch[1],
152
- file,
153
- line: i + 1,
154
- type: "named",
155
- });
156
- continue;
157
- }
158
-
159
- // export { name, name2 }
160
- const bracedExportMatch = line.match(/export\s*\{([^}]+)\}/);
161
- if (bracedExportMatch) {
162
- const names = bracedExportMatch[1].split(",").map(n => {
163
- const asMatch = n.trim().match(/(\w+)\s+as\s+(\w+)/);
164
- return asMatch ? asMatch[2] : n.trim().split(" ")[0];
165
- }).filter(Boolean);
166
-
167
- for (const name of names) {
168
- exports.push({
169
- name,
170
- file,
171
- line: i + 1,
172
- type: "named",
173
- });
174
- }
175
- continue;
176
- }
177
-
178
- // export default (we track this specially)
179
- if (/export\s+default/.test(line)) {
180
- exports.push({
181
- name: "default",
182
- file,
183
- line: i + 1,
184
- type: "default",
185
- });
186
- }
187
- }
188
-
189
- return exports;
190
- }
191
-
192
- function extractImports(content) {
193
- const imports = new Set();
194
-
195
- // import { name, name2 } from 'module'
196
- const bracedImportMatches = content.matchAll(/import\s*\{([^}]+)\}\s*from/g);
197
- for (const match of bracedImportMatches) {
198
- const names = match[1].split(",").map(n => {
199
- const asMatch = n.trim().match(/(\w+)\s+as\s+(\w+)/);
200
- return asMatch ? asMatch[1] : n.trim().split(" ")[0];
201
- }).filter(Boolean);
202
-
203
- for (const name of names) {
204
- imports.add(name);
205
- }
206
- }
207
-
208
- // import name from 'module' (default import)
209
- const defaultImportMatches = content.matchAll(/import\s+(\w+)\s+from/g);
210
- for (const match of defaultImportMatches) {
211
- imports.add(match[1]);
212
- imports.add("default");
213
- }
214
-
215
- // Dynamic imports
216
- const dynamicImportMatches = content.matchAll(/import\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g);
217
- for (const match of dynamicImportMatches) {
218
- // Mark the default export as potentially used
219
- imports.add("default");
220
- }
221
-
222
- return imports;
223
- }
224
-
225
- function isLikelyEntryPoint(file, name) {
226
- // Entry point files
227
- if (file.includes("page.") || file.includes("route.") || file.includes("layout.")) return true;
228
- if (file.includes("middleware.")) return true;
229
- if (file.endsWith("config.ts") || file.endsWith("config.js")) return true;
230
-
231
- // Common exports that are used externally
232
- const externalExports = ["handler", "GET", "POST", "PUT", "DELETE", "PATCH", "middleware"];
233
- if (externalExports.includes(name)) return true;
234
-
235
- // API route handlers
236
- if (file.includes("/api/")) return true;
237
-
238
- // Default exports are often consumed by frameworks
239
- if (name === "default") return true;
240
-
241
- return false;
242
- }
243
-
244
- async function isExportUsedAnywhere(name, files, sourceFile) {
245
- // Quick regex check across all files
246
- for (const file of files) {
247
- if (file.includes(sourceFile)) continue; // Skip the source file
248
-
249
- try {
250
- const content = fs.readFileSync(file, "utf8");
251
-
252
- // Check for import of this name
253
- const importPattern = new RegExp(`import[^}]*\\b${name}\\b[^}]*from`, "g");
254
- if (importPattern.test(content)) return true;
255
-
256
- // Check for dynamic import and usage
257
- if (content.includes(name) && content.includes("import(")) return true;
258
-
259
- } catch {
260
- // Skip files that can't be read
261
- }
262
- }
263
-
264
- return false;
265
- }
266
-
267
- function hashString(str) {
268
- let hash = 0;
269
- for (let i = 0; i < str.length; i++) {
270
- const char = str.charCodeAt(i);
271
- hash = ((hash << 5) - hash) + char;
272
- hash = hash & hash;
273
- }
274
- return Math.abs(hash).toString(36).substring(0, 8);
275
- }
276
-
277
- module.exports = {
278
- detectDeadExports,
279
- };