wiggum-cli 0.1.0

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 (236) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +341 -0
  3. package/bin/ralph.js +8 -0
  4. package/dist/ai/enhancer.d.ts +100 -0
  5. package/dist/ai/enhancer.d.ts.map +1 -0
  6. package/dist/ai/enhancer.js +233 -0
  7. package/dist/ai/enhancer.js.map +1 -0
  8. package/dist/ai/index.d.ts +8 -0
  9. package/dist/ai/index.d.ts.map +1 -0
  10. package/dist/ai/index.js +11 -0
  11. package/dist/ai/index.js.map +1 -0
  12. package/dist/ai/prompts.d.ts +26 -0
  13. package/dist/ai/prompts.d.ts.map +1 -0
  14. package/dist/ai/prompts.js +201 -0
  15. package/dist/ai/prompts.js.map +1 -0
  16. package/dist/ai/providers.d.ts +35 -0
  17. package/dist/ai/providers.d.ts.map +1 -0
  18. package/dist/ai/providers.js +104 -0
  19. package/dist/ai/providers.js.map +1 -0
  20. package/dist/cli.d.ts +6 -0
  21. package/dist/cli.d.ts.map +1 -0
  22. package/dist/cli.js +196 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/commands/init.d.ts +16 -0
  25. package/dist/commands/init.d.ts.map +1 -0
  26. package/dist/commands/init.js +124 -0
  27. package/dist/commands/init.js.map +1 -0
  28. package/dist/commands/monitor.d.ts +17 -0
  29. package/dist/commands/monitor.d.ts.map +1 -0
  30. package/dist/commands/monitor.js +342 -0
  31. package/dist/commands/monitor.js.map +1 -0
  32. package/dist/commands/new.d.ts +19 -0
  33. package/dist/commands/new.d.ts.map +1 -0
  34. package/dist/commands/new.js +272 -0
  35. package/dist/commands/new.js.map +1 -0
  36. package/dist/commands/run.d.ts +16 -0
  37. package/dist/commands/run.d.ts.map +1 -0
  38. package/dist/commands/run.js +175 -0
  39. package/dist/commands/run.js.map +1 -0
  40. package/dist/generator/config.d.ts +59 -0
  41. package/dist/generator/config.d.ts.map +1 -0
  42. package/dist/generator/config.js +68 -0
  43. package/dist/generator/config.js.map +1 -0
  44. package/dist/generator/index.d.ts +64 -0
  45. package/dist/generator/index.d.ts.map +1 -0
  46. package/dist/generator/index.js +147 -0
  47. package/dist/generator/index.js.map +1 -0
  48. package/dist/generator/templates.d.ts +70 -0
  49. package/dist/generator/templates.d.ts.map +1 -0
  50. package/dist/generator/templates.js +296 -0
  51. package/dist/generator/templates.js.map +1 -0
  52. package/dist/generator/writer.d.ts +93 -0
  53. package/dist/generator/writer.d.ts.map +1 -0
  54. package/dist/generator/writer.js +213 -0
  55. package/dist/generator/writer.js.map +1 -0
  56. package/dist/index.d.ts +12 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +17 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/scanner/detectors/core/framework.d.ts +11 -0
  61. package/dist/scanner/detectors/core/framework.d.ts.map +1 -0
  62. package/dist/scanner/detectors/core/framework.js +275 -0
  63. package/dist/scanner/detectors/core/framework.js.map +1 -0
  64. package/dist/scanner/detectors/core/packageManager.d.ts +11 -0
  65. package/dist/scanner/detectors/core/packageManager.d.ts.map +1 -0
  66. package/dist/scanner/detectors/core/packageManager.js +74 -0
  67. package/dist/scanner/detectors/core/packageManager.js.map +1 -0
  68. package/dist/scanner/detectors/core/styling.d.ts +12 -0
  69. package/dist/scanner/detectors/core/styling.d.ts.map +1 -0
  70. package/dist/scanner/detectors/core/styling.js +230 -0
  71. package/dist/scanner/detectors/core/styling.js.map +1 -0
  72. package/dist/scanner/detectors/core/testing.d.ts +12 -0
  73. package/dist/scanner/detectors/core/testing.d.ts.map +1 -0
  74. package/dist/scanner/detectors/core/testing.js +190 -0
  75. package/dist/scanner/detectors/core/testing.js.map +1 -0
  76. package/dist/scanner/detectors/data/api.d.ts +12 -0
  77. package/dist/scanner/detectors/data/api.d.ts.map +1 -0
  78. package/dist/scanner/detectors/data/api.js +261 -0
  79. package/dist/scanner/detectors/data/api.js.map +1 -0
  80. package/dist/scanner/detectors/data/database.d.ts +12 -0
  81. package/dist/scanner/detectors/data/database.d.ts.map +1 -0
  82. package/dist/scanner/detectors/data/database.js +213 -0
  83. package/dist/scanner/detectors/data/database.js.map +1 -0
  84. package/dist/scanner/detectors/data/orm.d.ts +12 -0
  85. package/dist/scanner/detectors/data/orm.d.ts.map +1 -0
  86. package/dist/scanner/detectors/data/orm.js +160 -0
  87. package/dist/scanner/detectors/data/orm.js.map +1 -0
  88. package/dist/scanner/detectors/frontend/formHandling.d.ts +12 -0
  89. package/dist/scanner/detectors/frontend/formHandling.d.ts.map +1 -0
  90. package/dist/scanner/detectors/frontend/formHandling.js +211 -0
  91. package/dist/scanner/detectors/frontend/formHandling.js.map +1 -0
  92. package/dist/scanner/detectors/frontend/stateManagement.d.ts +12 -0
  93. package/dist/scanner/detectors/frontend/stateManagement.d.ts.map +1 -0
  94. package/dist/scanner/detectors/frontend/stateManagement.js +221 -0
  95. package/dist/scanner/detectors/frontend/stateManagement.js.map +1 -0
  96. package/dist/scanner/detectors/frontend/uiComponents.d.ts +12 -0
  97. package/dist/scanner/detectors/frontend/uiComponents.d.ts.map +1 -0
  98. package/dist/scanner/detectors/frontend/uiComponents.js +285 -0
  99. package/dist/scanner/detectors/frontend/uiComponents.js.map +1 -0
  100. package/dist/scanner/detectors/infra/deployment.d.ts +12 -0
  101. package/dist/scanner/detectors/infra/deployment.d.ts.map +1 -0
  102. package/dist/scanner/detectors/infra/deployment.js +301 -0
  103. package/dist/scanner/detectors/infra/deployment.js.map +1 -0
  104. package/dist/scanner/detectors/infra/monorepo.d.ts +12 -0
  105. package/dist/scanner/detectors/infra/monorepo.d.ts.map +1 -0
  106. package/dist/scanner/detectors/infra/monorepo.js +219 -0
  107. package/dist/scanner/detectors/infra/monorepo.js.map +1 -0
  108. package/dist/scanner/detectors/mcp/mcpProject.d.ts +12 -0
  109. package/dist/scanner/detectors/mcp/mcpProject.d.ts.map +1 -0
  110. package/dist/scanner/detectors/mcp/mcpProject.js +154 -0
  111. package/dist/scanner/detectors/mcp/mcpProject.js.map +1 -0
  112. package/dist/scanner/detectors/mcp/mcpServers.d.ts +17 -0
  113. package/dist/scanner/detectors/mcp/mcpServers.d.ts.map +1 -0
  114. package/dist/scanner/detectors/mcp/mcpServers.js +193 -0
  115. package/dist/scanner/detectors/mcp/mcpServers.js.map +1 -0
  116. package/dist/scanner/detectors/services/analytics.d.ts +12 -0
  117. package/dist/scanner/detectors/services/analytics.d.ts.map +1 -0
  118. package/dist/scanner/detectors/services/analytics.js +236 -0
  119. package/dist/scanner/detectors/services/analytics.js.map +1 -0
  120. package/dist/scanner/detectors/services/auth.d.ts +12 -0
  121. package/dist/scanner/detectors/services/auth.d.ts.map +1 -0
  122. package/dist/scanner/detectors/services/auth.js +217 -0
  123. package/dist/scanner/detectors/services/auth.js.map +1 -0
  124. package/dist/scanner/detectors/services/email.d.ts +12 -0
  125. package/dist/scanner/detectors/services/email.d.ts.map +1 -0
  126. package/dist/scanner/detectors/services/email.js +211 -0
  127. package/dist/scanner/detectors/services/email.js.map +1 -0
  128. package/dist/scanner/detectors/services/payments.d.ts +12 -0
  129. package/dist/scanner/detectors/services/payments.d.ts.map +1 -0
  130. package/dist/scanner/detectors/services/payments.js +185 -0
  131. package/dist/scanner/detectors/services/payments.js.map +1 -0
  132. package/dist/scanner/detectors/utils.d.ts +160 -0
  133. package/dist/scanner/detectors/utils.d.ts.map +1 -0
  134. package/dist/scanner/detectors/utils.js +222 -0
  135. package/dist/scanner/detectors/utils.js.map +1 -0
  136. package/dist/scanner/index.d.ts +42 -0
  137. package/dist/scanner/index.d.ts.map +1 -0
  138. package/dist/scanner/index.js +282 -0
  139. package/dist/scanner/index.js.map +1 -0
  140. package/dist/scanner/registry.d.ts +43 -0
  141. package/dist/scanner/registry.d.ts.map +1 -0
  142. package/dist/scanner/registry.js +243 -0
  143. package/dist/scanner/registry.js.map +1 -0
  144. package/dist/scanner/types.d.ts +112 -0
  145. package/dist/scanner/types.d.ts.map +1 -0
  146. package/dist/scanner/types.js +6 -0
  147. package/dist/scanner/types.js.map +1 -0
  148. package/dist/templates/config/ralph.config.js.tmpl +38 -0
  149. package/dist/templates/guides/AGENTS.md.tmpl +100 -0
  150. package/dist/templates/guides/FRONTEND.md.tmpl +523 -0
  151. package/dist/templates/guides/PERFORMANCE.md.tmpl +264 -0
  152. package/dist/templates/guides/SECURITY.md.tmpl +100 -0
  153. package/dist/templates/prompts/PROMPT.md.tmpl +77 -0
  154. package/dist/templates/prompts/PROMPT_e2e.md.tmpl +234 -0
  155. package/dist/templates/prompts/PROMPT_feature.md.tmpl +83 -0
  156. package/dist/templates/prompts/PROMPT_review.md.tmpl +167 -0
  157. package/dist/templates/prompts/PROMPT_verify.md.tmpl +72 -0
  158. package/dist/templates/root/.gitignore.tmpl +5 -0
  159. package/dist/templates/root/LEARNINGS.md.tmpl +24 -0
  160. package/dist/templates/root/README.md.tmpl +61 -0
  161. package/dist/templates/scripts/feature-loop.sh.tmpl +267 -0
  162. package/dist/templates/scripts/loop.sh.tmpl +59 -0
  163. package/dist/templates/scripts/ralph-monitor.sh.tmpl +244 -0
  164. package/dist/templates/specs/README.md.tmpl +57 -0
  165. package/dist/templates/specs/_example.md.tmpl +71 -0
  166. package/dist/utils/config.d.ts +95 -0
  167. package/dist/utils/config.d.ts.map +1 -0
  168. package/dist/utils/config.js +148 -0
  169. package/dist/utils/config.js.map +1 -0
  170. package/dist/utils/header.d.ts +5 -0
  171. package/dist/utils/header.d.ts.map +1 -0
  172. package/dist/utils/header.js +15 -0
  173. package/dist/utils/header.js.map +1 -0
  174. package/dist/utils/logger.d.ts +11 -0
  175. package/dist/utils/logger.d.ts.map +1 -0
  176. package/dist/utils/logger.js +24 -0
  177. package/dist/utils/logger.js.map +1 -0
  178. package/package.json +44 -0
  179. package/src/ai/enhancer.ts +350 -0
  180. package/src/ai/index.ts +38 -0
  181. package/src/ai/prompts.ts +217 -0
  182. package/src/ai/providers.ts +136 -0
  183. package/src/cli.ts +255 -0
  184. package/src/commands/init.ts +149 -0
  185. package/src/commands/monitor.ts +412 -0
  186. package/src/commands/new.ts +312 -0
  187. package/src/commands/run.ts +214 -0
  188. package/src/generator/config.ts +116 -0
  189. package/src/generator/index.ts +227 -0
  190. package/src/generator/templates.ts +412 -0
  191. package/src/generator/writer.ts +293 -0
  192. package/src/index.ts +41 -0
  193. package/src/scanner/detectors/core/framework.ts +332 -0
  194. package/src/scanner/detectors/core/packageManager.ts +91 -0
  195. package/src/scanner/detectors/core/styling.ts +261 -0
  196. package/src/scanner/detectors/core/testing.ts +221 -0
  197. package/src/scanner/detectors/data/api.ts +303 -0
  198. package/src/scanner/detectors/data/database.ts +245 -0
  199. package/src/scanner/detectors/data/orm.ts +180 -0
  200. package/src/scanner/detectors/frontend/formHandling.ts +244 -0
  201. package/src/scanner/detectors/frontend/stateManagement.ts +261 -0
  202. package/src/scanner/detectors/frontend/uiComponents.ts +328 -0
  203. package/src/scanner/detectors/infra/deployment.ts +343 -0
  204. package/src/scanner/detectors/infra/monorepo.ts +251 -0
  205. package/src/scanner/detectors/mcp/mcpProject.ts +176 -0
  206. package/src/scanner/detectors/mcp/mcpServers.ts +237 -0
  207. package/src/scanner/detectors/services/analytics.ts +273 -0
  208. package/src/scanner/detectors/services/auth.ts +254 -0
  209. package/src/scanner/detectors/services/email.ts +244 -0
  210. package/src/scanner/detectors/services/payments.ts +213 -0
  211. package/src/scanner/detectors/utils.ts +251 -0
  212. package/src/scanner/index.ts +354 -0
  213. package/src/scanner/registry.ts +301 -0
  214. package/src/scanner/types.ts +152 -0
  215. package/src/templates/config/ralph.config.js.tmpl +38 -0
  216. package/src/templates/guides/AGENTS.md.tmpl +100 -0
  217. package/src/templates/guides/FRONTEND.md.tmpl +523 -0
  218. package/src/templates/guides/PERFORMANCE.md.tmpl +264 -0
  219. package/src/templates/guides/SECURITY.md.tmpl +100 -0
  220. package/src/templates/prompts/PROMPT.md.tmpl +77 -0
  221. package/src/templates/prompts/PROMPT_e2e.md.tmpl +234 -0
  222. package/src/templates/prompts/PROMPT_feature.md.tmpl +83 -0
  223. package/src/templates/prompts/PROMPT_review.md.tmpl +167 -0
  224. package/src/templates/prompts/PROMPT_verify.md.tmpl +72 -0
  225. package/src/templates/root/.gitignore.tmpl +5 -0
  226. package/src/templates/root/LEARNINGS.md.tmpl +24 -0
  227. package/src/templates/root/README.md.tmpl +61 -0
  228. package/src/templates/scripts/feature-loop.sh.tmpl +267 -0
  229. package/src/templates/scripts/loop.sh.tmpl +59 -0
  230. package/src/templates/scripts/ralph-monitor.sh.tmpl +244 -0
  231. package/src/templates/specs/README.md.tmpl +57 -0
  232. package/src/templates/specs/_example.md.tmpl +71 -0
  233. package/src/utils/config.ts +221 -0
  234. package/src/utils/header.ts +15 -0
  235. package/src/utils/logger.ts +28 -0
  236. package/tsconfig.json +19 -0
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Analytics Detector
3
+ * Detects: PostHog, Mixpanel, Vercel Analytics, Google Analytics
4
+ */
5
+
6
+ import { existsSync, readFileSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import type { Detector, DetectionResult } from '../../types.js';
9
+
10
+ /**
11
+ * Read and parse package.json from a directory
12
+ */
13
+ function readPackageJson(projectRoot: string): Record<string, unknown> | null {
14
+ const packageJsonPath = join(projectRoot, 'package.json');
15
+ if (!existsSync(packageJsonPath)) {
16
+ return null;
17
+ }
18
+ try {
19
+ const content = readFileSync(packageJsonPath, 'utf-8');
20
+ return JSON.parse(content);
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Get all dependencies from package.json (deps + devDeps)
28
+ */
29
+ function getDependencies(pkg: Record<string, unknown>): Record<string, string> {
30
+ const deps = (pkg.dependencies as Record<string, string>) || {};
31
+ const devDeps = (pkg.devDependencies as Record<string, string>) || {};
32
+ return { ...deps, ...devDeps };
33
+ }
34
+
35
+ /**
36
+ * Find all matching dependencies by pattern
37
+ */
38
+ function findMatchingDeps(deps: Record<string, string>, pattern: string): string[] {
39
+ return Object.keys(deps).filter(name => name.startsWith(pattern));
40
+ }
41
+
42
+ /**
43
+ * Detect PostHog
44
+ */
45
+ function detectPostHog(deps: Record<string, string>): DetectionResult | null {
46
+ const evidence: string[] = [];
47
+ let confidence = 0;
48
+ let variant: string | undefined;
49
+
50
+ // Check for posthog-js (browser)
51
+ if (deps['posthog-js']) {
52
+ evidence.push(`posthog-js@${deps['posthog-js']} in dependencies`);
53
+ confidence += 80;
54
+ variant = 'browser';
55
+ }
56
+
57
+ // Check for posthog-node (server)
58
+ if (deps['posthog-node']) {
59
+ evidence.push(`posthog-node@${deps['posthog-node']} in dependencies`);
60
+ confidence += 80;
61
+ variant = variant ? 'full-stack' : 'server';
62
+ }
63
+
64
+ // Check for Next.js specific package
65
+ if (deps['posthog-react']) {
66
+ evidence.push('posthog-react found');
67
+ confidence += 10;
68
+ }
69
+
70
+ if (confidence === 0) return null;
71
+
72
+ return {
73
+ name: 'PostHog',
74
+ version: deps['posthog-js'] || deps['posthog-node'],
75
+ variant,
76
+ confidence: Math.min(confidence, 100),
77
+ evidence,
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Detect Mixpanel
83
+ */
84
+ function detectMixpanel(deps: Record<string, string>): DetectionResult | null {
85
+ const evidence: string[] = [];
86
+ let confidence = 0;
87
+
88
+ // Check for mixpanel packages
89
+ const mixpanelPackages = findMatchingDeps(deps, 'mixpanel');
90
+ if (mixpanelPackages.length > 0) {
91
+ evidence.push(`Mixpanel packages found: ${mixpanelPackages.join(', ')}`);
92
+ confidence += 80;
93
+ }
94
+
95
+ // Check for @mixpanel/* packages
96
+ const mixpanelScopedPackages = findMatchingDeps(deps, '@mixpanel/');
97
+ if (mixpanelScopedPackages.length > 0) {
98
+ evidence.push(`@mixpanel packages found: ${mixpanelScopedPackages.join(', ')}`);
99
+ confidence += 80;
100
+ }
101
+
102
+ if (confidence === 0) return null;
103
+
104
+ return {
105
+ name: 'Mixpanel',
106
+ version: deps.mixpanel || deps['mixpanel-browser'],
107
+ confidence: Math.min(confidence, 100),
108
+ evidence,
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Detect Vercel Analytics
114
+ */
115
+ function detectVercelAnalytics(deps: Record<string, string>): DetectionResult | null {
116
+ const evidence: string[] = [];
117
+ let confidence = 0;
118
+
119
+ if (deps['@vercel/analytics']) {
120
+ evidence.push(`@vercel/analytics@${deps['@vercel/analytics']} in dependencies`);
121
+ confidence += 80;
122
+ }
123
+
124
+ // Check for @vercel/speed-insights (often used together)
125
+ if (deps['@vercel/speed-insights']) {
126
+ evidence.push('@vercel/speed-insights found');
127
+ confidence += 10;
128
+ }
129
+
130
+ if (confidence === 0) return null;
131
+
132
+ return {
133
+ name: 'Vercel Analytics',
134
+ version: deps['@vercel/analytics'],
135
+ confidence: Math.min(confidence, 100),
136
+ evidence,
137
+ };
138
+ }
139
+
140
+ /**
141
+ * Detect Google Analytics
142
+ */
143
+ function detectGoogleAnalytics(deps: Record<string, string>): DetectionResult | null {
144
+ const evidence: string[] = [];
145
+ let confidence = 0;
146
+
147
+ // Check for various GA packages
148
+ if (deps['@google-analytics/data']) {
149
+ evidence.push('@google-analytics/data found');
150
+ confidence += 70;
151
+ }
152
+
153
+ if (deps['react-ga4'] || deps['react-ga']) {
154
+ evidence.push('React GA package found');
155
+ confidence += 70;
156
+ }
157
+
158
+ if (deps['ga-4-react']) {
159
+ evidence.push('ga-4-react found');
160
+ confidence += 70;
161
+ }
162
+
163
+ // Check for gtag
164
+ if (deps.gtag) {
165
+ evidence.push('gtag found');
166
+ confidence += 60;
167
+ }
168
+
169
+ if (confidence === 0) return null;
170
+
171
+ return {
172
+ name: 'Google Analytics',
173
+ version: deps['react-ga4'] || deps['@google-analytics/data'],
174
+ confidence: Math.min(confidence, 100),
175
+ evidence,
176
+ };
177
+ }
178
+
179
+ /**
180
+ * Detect Amplitude
181
+ */
182
+ function detectAmplitude(deps: Record<string, string>): DetectionResult | null {
183
+ const evidence: string[] = [];
184
+ let confidence = 0;
185
+
186
+ // Check for @amplitude/* packages
187
+ const amplitudePackages = findMatchingDeps(deps, '@amplitude/');
188
+ if (amplitudePackages.length > 0) {
189
+ evidence.push(`Amplitude packages found: ${amplitudePackages.join(', ')}`);
190
+ confidence += 80;
191
+ }
192
+
193
+ // Check for amplitude-js
194
+ if (deps['amplitude-js']) {
195
+ evidence.push(`amplitude-js@${deps['amplitude-js']} in dependencies`);
196
+ confidence += 80;
197
+ }
198
+
199
+ if (confidence === 0) return null;
200
+
201
+ return {
202
+ name: 'Amplitude',
203
+ version: deps['@amplitude/analytics-browser'] || deps['amplitude-js'],
204
+ confidence: Math.min(confidence, 100),
205
+ evidence,
206
+ };
207
+ }
208
+
209
+ /**
210
+ * Detect Plausible
211
+ */
212
+ function detectPlausible(deps: Record<string, string>): DetectionResult | null {
213
+ const evidence: string[] = [];
214
+ let confidence = 0;
215
+
216
+ if (deps['plausible-tracker']) {
217
+ evidence.push(`plausible-tracker@${deps['plausible-tracker']} in dependencies`);
218
+ confidence += 80;
219
+ }
220
+
221
+ if (deps['next-plausible']) {
222
+ evidence.push('next-plausible found');
223
+ confidence += 80;
224
+ }
225
+
226
+ if (confidence === 0) return null;
227
+
228
+ return {
229
+ name: 'Plausible',
230
+ version: deps['plausible-tracker'] || deps['next-plausible'],
231
+ confidence: Math.min(confidence, 100),
232
+ evidence,
233
+ };
234
+ }
235
+
236
+ /**
237
+ * Analytics detector
238
+ * Returns all detected analytics solutions (projects often use multiple)
239
+ */
240
+ export const analyticsDetector: Detector = {
241
+ category: 'analytics',
242
+ name: 'Analytics Detector',
243
+
244
+ async detect(projectRoot: string): Promise<DetectionResult[] | null> {
245
+ const pkg = readPackageJson(projectRoot);
246
+ if (!pkg) {
247
+ return null;
248
+ }
249
+
250
+ const deps = getDependencies(pkg);
251
+ const results: DetectionResult[] = [];
252
+
253
+ const detectors = [
254
+ () => detectPostHog(deps),
255
+ () => detectMixpanel(deps),
256
+ () => detectVercelAnalytics(deps),
257
+ () => detectGoogleAnalytics(deps),
258
+ () => detectAmplitude(deps),
259
+ () => detectPlausible(deps),
260
+ ];
261
+
262
+ for (const detector of detectors) {
263
+ const result = detector();
264
+ if (result && result.confidence >= 40) {
265
+ results.push(result);
266
+ }
267
+ }
268
+
269
+ return results.length > 0 ? results : null;
270
+ },
271
+ };
272
+
273
+ export default analyticsDetector;
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Auth Provider Detector
3
+ * Detects: NextAuth, Clerk, Auth0, Supabase Auth
4
+ */
5
+
6
+ import type { Detector, DetectionResult } from '../../types.js';
7
+ import {
8
+ readPackageJson,
9
+ getDependencies,
10
+ findMatchingDeps,
11
+ type DependencyMap,
12
+ } from '../utils.js';
13
+
14
+ /**
15
+ * Detect NextAuth.js / Auth.js
16
+ */
17
+ function detectNextAuth(deps: DependencyMap): DetectionResult | null {
18
+ const evidence: string[] = [];
19
+ let confidence = 0;
20
+ let variant: string | undefined;
21
+
22
+ // Check for next-auth (v4 and below)
23
+ if (deps['next-auth']) {
24
+ evidence.push(`next-auth@${deps['next-auth']} in dependencies`);
25
+ confidence += 80;
26
+ variant = 'v4';
27
+ }
28
+
29
+ // Check for @auth/* packages (Auth.js v5+)
30
+ const authPackages = findMatchingDeps(deps, '@auth/');
31
+ if (authPackages.length > 0) {
32
+ evidence.push(`Auth.js packages found: ${authPackages.join(', ')}`);
33
+ confidence += 80;
34
+ variant = 'v5';
35
+ }
36
+
37
+ if (confidence === 0) return null;
38
+
39
+ return {
40
+ name: 'NextAuth.js',
41
+ version: deps['next-auth'] || deps['@auth/core'],
42
+ variant,
43
+ confidence: Math.min(confidence, 100),
44
+ evidence,
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Detect Clerk
50
+ */
51
+ function detectClerk(deps: DependencyMap): DetectionResult | null {
52
+ const evidence: string[] = [];
53
+ let confidence = 0;
54
+
55
+ // Check for @clerk/* packages
56
+ const clerkPackages = findMatchingDeps(deps, '@clerk/');
57
+ if (clerkPackages.length > 0) {
58
+ const mainVersion = deps['@clerk/nextjs'] || deps['@clerk/clerk-react'] || deps['@clerk/express'];
59
+ evidence.push(`Clerk packages found: ${clerkPackages.join(', ')}`);
60
+ confidence += 80;
61
+
62
+ // Detect framework variant
63
+ let variant: string | undefined;
64
+ if (deps['@clerk/nextjs']) {
65
+ variant = 'nextjs';
66
+ } else if (deps['@clerk/clerk-react']) {
67
+ variant = 'react';
68
+ } else if (deps['@clerk/express']) {
69
+ variant = 'express';
70
+ }
71
+
72
+ return {
73
+ name: 'Clerk',
74
+ version: mainVersion,
75
+ variant,
76
+ confidence: Math.min(confidence, 100),
77
+ evidence,
78
+ };
79
+ }
80
+
81
+ return null;
82
+ }
83
+
84
+ /**
85
+ * Detect Auth0
86
+ */
87
+ function detectAuth0(deps: DependencyMap): DetectionResult | null {
88
+ const evidence: string[] = [];
89
+ let confidence = 0;
90
+ let variant: string | undefined;
91
+
92
+ // Check for @auth0/* packages
93
+ const auth0Packages = findMatchingDeps(deps, '@auth0/');
94
+ if (auth0Packages.length > 0) {
95
+ evidence.push(`Auth0 packages found: ${auth0Packages.join(', ')}`);
96
+ confidence += 70;
97
+
98
+ if (deps['@auth0/nextjs-auth0']) {
99
+ variant = 'nextjs';
100
+ confidence += 10;
101
+ } else if (deps['@auth0/auth0-react']) {
102
+ variant = 'react';
103
+ confidence += 10;
104
+ }
105
+ }
106
+
107
+ // Check for auth0 package
108
+ if (deps.auth0) {
109
+ evidence.push(`auth0@${deps.auth0} in dependencies`);
110
+ confidence += 60;
111
+ variant = 'node';
112
+ }
113
+
114
+ if (confidence === 0) return null;
115
+
116
+ return {
117
+ name: 'Auth0',
118
+ version: deps['@auth0/nextjs-auth0'] || deps['@auth0/auth0-react'] || deps.auth0,
119
+ variant,
120
+ confidence: Math.min(confidence, 100),
121
+ evidence,
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Detect Supabase Auth
127
+ */
128
+ function detectSupabaseAuth(deps: DependencyMap): DetectionResult | null {
129
+ const evidence: string[] = [];
130
+ let confidence = 0;
131
+
132
+ // Check for @supabase/auth-helpers-* packages
133
+ const authHelperPackages = findMatchingDeps(deps, '@supabase/auth-helpers-');
134
+ if (authHelperPackages.length > 0) {
135
+ evidence.push(`Supabase auth helpers found: ${authHelperPackages.join(', ')}`);
136
+ confidence += 70;
137
+ }
138
+
139
+ // Check for @supabase/ssr (newer auth approach)
140
+ if (deps['@supabase/ssr']) {
141
+ evidence.push(`@supabase/ssr@${deps['@supabase/ssr']} in dependencies`);
142
+ confidence += 70;
143
+ }
144
+
145
+ // Check for @supabase/auth-ui-react
146
+ if (deps['@supabase/auth-ui-react']) {
147
+ evidence.push('@supabase/auth-ui-react found');
148
+ confidence += 20;
149
+ }
150
+
151
+ if (confidence === 0) return null;
152
+
153
+ return {
154
+ name: 'Supabase Auth',
155
+ version: deps['@supabase/ssr'] || authHelperPackages[0] ? deps[authHelperPackages[0]] : undefined,
156
+ confidence: Math.min(confidence, 100),
157
+ evidence,
158
+ };
159
+ }
160
+
161
+ /**
162
+ * Detect Lucia Auth
163
+ */
164
+ function detectLucia(deps: DependencyMap): DetectionResult | null {
165
+ const evidence: string[] = [];
166
+ let confidence = 0;
167
+
168
+ if (deps.lucia) {
169
+ evidence.push(`lucia@${deps.lucia} in dependencies`);
170
+ confidence += 80;
171
+ }
172
+
173
+ // Check for @lucia-auth/* adapters
174
+ const luciaPackages = findMatchingDeps(deps, '@lucia-auth/');
175
+ if (luciaPackages.length > 0) {
176
+ evidence.push(`Lucia adapters found: ${luciaPackages.join(', ')}`);
177
+ confidence += 10;
178
+ }
179
+
180
+ if (confidence === 0) return null;
181
+
182
+ return {
183
+ name: 'Lucia',
184
+ version: deps.lucia,
185
+ confidence: Math.min(confidence, 100),
186
+ evidence,
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Detect Better Auth
192
+ */
193
+ function detectBetterAuth(deps: DependencyMap): DetectionResult | null {
194
+ const evidence: string[] = [];
195
+ let confidence = 0;
196
+
197
+ if (deps['better-auth']) {
198
+ evidence.push(`better-auth@${deps['better-auth']} in dependencies`);
199
+ confidence += 80;
200
+ }
201
+
202
+ if (confidence === 0) return null;
203
+
204
+ return {
205
+ name: 'Better Auth',
206
+ version: deps['better-auth'],
207
+ confidence: Math.min(confidence, 100),
208
+ evidence,
209
+ };
210
+ }
211
+
212
+ /**
213
+ * Auth provider detector
214
+ * Returns the primary auth solution detected
215
+ */
216
+ export const authDetector: Detector = {
217
+ category: 'auth',
218
+ name: 'Auth Provider Detector',
219
+
220
+ async detect(projectRoot: string): Promise<DetectionResult | null> {
221
+ const pkg = readPackageJson(projectRoot);
222
+ if (!pkg) {
223
+ return null;
224
+ }
225
+
226
+ const deps = getDependencies(pkg);
227
+
228
+ // Priority order based on popularity/comprehensiveness
229
+ const detectors = [
230
+ () => detectClerk(deps),
231
+ () => detectNextAuth(deps),
232
+ () => detectAuth0(deps),
233
+ () => detectLucia(deps),
234
+ () => detectBetterAuth(deps),
235
+ () => detectSupabaseAuth(deps),
236
+ ];
237
+
238
+ // Find the highest confidence result
239
+ let bestResult: DetectionResult | null = null;
240
+ let bestConfidence = 0;
241
+
242
+ for (const detector of detectors) {
243
+ const result = detector();
244
+ if (result && result.confidence > bestConfidence) {
245
+ bestResult = result;
246
+ bestConfidence = result.confidence;
247
+ }
248
+ }
249
+
250
+ return bestResult && bestResult.confidence >= 40 ? bestResult : null;
251
+ },
252
+ };
253
+
254
+ export default authDetector;