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,328 @@
1
+ /**
2
+ * UI Components Detector
3
+ * Detects: shadcn, Radix, MUI, Chakra, Ant Design, Headless UI
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 shadcn/ui
44
+ */
45
+ function detectShadcn(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
46
+ const evidence: string[] = [];
47
+ let confidence = 0;
48
+
49
+ // Check for components.json (shadcn config file)
50
+ const componentsJsonPath = join(projectRoot, 'components.json');
51
+ if (existsSync(componentsJsonPath)) {
52
+ evidence.push('components.json found');
53
+ confidence += 60;
54
+
55
+ // Try to read and validate it's a shadcn config
56
+ try {
57
+ const content = readFileSync(componentsJsonPath, 'utf-8');
58
+ const config = JSON.parse(content);
59
+ if (config.style || config.rsc || config.tsx || config.components) {
60
+ evidence.push('Valid shadcn/ui configuration detected');
61
+ confidence += 20;
62
+ }
63
+ } catch {
64
+ // File exists but couldn't be parsed
65
+ }
66
+ }
67
+
68
+ // Check for @radix-ui/* packages (shadcn uses Radix primitives)
69
+ const radixPackages = findMatchingDeps(deps, '@radix-ui/');
70
+ if (radixPackages.length >= 3) {
71
+ evidence.push(`Multiple @radix-ui packages found (${radixPackages.length})`);
72
+ confidence += 20;
73
+ }
74
+
75
+ // Check for class-variance-authority (commonly used with shadcn)
76
+ if (deps['class-variance-authority']) {
77
+ evidence.push('class-variance-authority detected (common with shadcn)');
78
+ confidence += 10;
79
+ }
80
+
81
+ // Check for clsx or tailwind-merge (common utilities with shadcn)
82
+ if (deps.clsx || deps['tailwind-merge']) {
83
+ evidence.push('clsx/tailwind-merge utilities detected');
84
+ confidence += 5;
85
+ }
86
+
87
+ if (confidence === 0) return null;
88
+
89
+ return {
90
+ name: 'shadcn/ui',
91
+ confidence: Math.min(confidence, 100),
92
+ evidence,
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Detect Radix UI (standalone, not as part of shadcn)
98
+ */
99
+ function detectRadix(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
100
+ const evidence: string[] = [];
101
+ let confidence = 0;
102
+
103
+ // Check for @radix-ui/* packages
104
+ const radixPackages = findMatchingDeps(deps, '@radix-ui/');
105
+ if (radixPackages.length > 0) {
106
+ evidence.push(`@radix-ui packages found: ${radixPackages.slice(0, 5).join(', ')}${radixPackages.length > 5 ? '...' : ''}`);
107
+ confidence += 50 + Math.min(radixPackages.length * 5, 30);
108
+ }
109
+
110
+ // Check for @radix-ui/themes (full theme package)
111
+ if (deps['@radix-ui/themes']) {
112
+ evidence.push('@radix-ui/themes found');
113
+ confidence += 20;
114
+ }
115
+
116
+ if (confidence === 0) return null;
117
+
118
+ return {
119
+ name: 'Radix UI',
120
+ confidence: Math.min(confidence, 100),
121
+ evidence,
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Detect Material UI (MUI)
127
+ */
128
+ function detectMUI(deps: Record<string, string>): DetectionResult | null {
129
+ const evidence: string[] = [];
130
+ let confidence = 0;
131
+
132
+ // Check for @mui/material
133
+ if (deps['@mui/material']) {
134
+ evidence.push(`@mui/material@${deps['@mui/material']} in dependencies`);
135
+ confidence += 70;
136
+ }
137
+
138
+ // Check for @mui/icons-material
139
+ if (deps['@mui/icons-material']) {
140
+ evidence.push('@mui/icons-material found');
141
+ confidence += 10;
142
+ }
143
+
144
+ // Check for @mui/x-* (data grid, date pickers, etc.)
145
+ const muiXPackages = findMatchingDeps(deps, '@mui/x-');
146
+ if (muiXPackages.length > 0) {
147
+ evidence.push(`MUI X packages found: ${muiXPackages.join(', ')}`);
148
+ confidence += 10;
149
+ }
150
+
151
+ // Check for @emotion (MUI's default styling)
152
+ if (deps['@emotion/react'] || deps['@emotion/styled']) {
153
+ evidence.push('Emotion styling detected (MUI default)');
154
+ confidence += 10;
155
+ }
156
+
157
+ if (confidence === 0) return null;
158
+
159
+ return {
160
+ name: 'MUI',
161
+ version: deps['@mui/material'],
162
+ confidence: Math.min(confidence, 100),
163
+ evidence,
164
+ };
165
+ }
166
+
167
+ /**
168
+ * Detect Chakra UI
169
+ */
170
+ function detectChakra(deps: Record<string, string>): DetectionResult | null {
171
+ const evidence: string[] = [];
172
+ let confidence = 0;
173
+
174
+ // Check for @chakra-ui/react
175
+ if (deps['@chakra-ui/react']) {
176
+ evidence.push(`@chakra-ui/react@${deps['@chakra-ui/react']} in dependencies`);
177
+ confidence += 80;
178
+ }
179
+
180
+ // Check for @chakra-ui/* packages
181
+ const chakraPackages = findMatchingDeps(deps, '@chakra-ui/');
182
+ if (chakraPackages.length > 1) {
183
+ evidence.push(`Multiple Chakra packages found (${chakraPackages.length})`);
184
+ confidence += 10;
185
+ }
186
+
187
+ if (confidence === 0) return null;
188
+
189
+ return {
190
+ name: 'Chakra UI',
191
+ version: deps['@chakra-ui/react'],
192
+ confidence: Math.min(confidence, 100),
193
+ evidence,
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Detect Ant Design
199
+ */
200
+ function detectAntDesign(deps: Record<string, string>): DetectionResult | null {
201
+ const evidence: string[] = [];
202
+ let confidence = 0;
203
+
204
+ if (deps.antd) {
205
+ evidence.push(`antd@${deps.antd} in dependencies`);
206
+ confidence += 80;
207
+ }
208
+
209
+ // Check for @ant-design/* packages
210
+ const antPackages = findMatchingDeps(deps, '@ant-design/');
211
+ if (antPackages.length > 0) {
212
+ evidence.push(`Ant Design packages found: ${antPackages.join(', ')}`);
213
+ confidence += 10;
214
+ }
215
+
216
+ if (confidence === 0) return null;
217
+
218
+ return {
219
+ name: 'Ant Design',
220
+ version: deps.antd,
221
+ confidence: Math.min(confidence, 100),
222
+ evidence,
223
+ };
224
+ }
225
+
226
+ /**
227
+ * Detect Headless UI
228
+ */
229
+ function detectHeadlessUI(deps: Record<string, string>): DetectionResult | null {
230
+ const evidence: string[] = [];
231
+ let confidence = 0;
232
+
233
+ // Check for @headlessui/react
234
+ if (deps['@headlessui/react']) {
235
+ evidence.push(`@headlessui/react@${deps['@headlessui/react']} in dependencies`);
236
+ confidence += 80;
237
+ }
238
+
239
+ // Check for @headlessui/vue
240
+ if (deps['@headlessui/vue']) {
241
+ evidence.push(`@headlessui/vue@${deps['@headlessui/vue']} in dependencies`);
242
+ confidence += 80;
243
+ }
244
+
245
+ if (confidence === 0) return null;
246
+
247
+ return {
248
+ name: 'Headless UI',
249
+ version: deps['@headlessui/react'] || deps['@headlessui/vue'],
250
+ confidence: Math.min(confidence, 100),
251
+ evidence,
252
+ };
253
+ }
254
+
255
+ /**
256
+ * Detect daisyUI
257
+ */
258
+ function detectDaisyUI(deps: Record<string, string>): DetectionResult | null {
259
+ const evidence: string[] = [];
260
+ let confidence = 0;
261
+
262
+ if (deps.daisyui) {
263
+ evidence.push(`daisyui@${deps.daisyui} in dependencies`);
264
+ confidence += 80;
265
+ }
266
+
267
+ if (confidence === 0) return null;
268
+
269
+ return {
270
+ name: 'daisyUI',
271
+ version: deps.daisyui,
272
+ confidence: Math.min(confidence, 100),
273
+ evidence,
274
+ };
275
+ }
276
+
277
+ /**
278
+ * UI Components detector
279
+ * Returns all detected UI libraries (projects often use multiple)
280
+ */
281
+ export const uiComponentsDetector: Detector = {
282
+ category: 'uiComponents',
283
+ name: 'UI Components Detector',
284
+
285
+ async detect(projectRoot: string): Promise<DetectionResult[] | null> {
286
+ const pkg = readPackageJson(projectRoot);
287
+ if (!pkg) {
288
+ return null;
289
+ }
290
+
291
+ const deps = getDependencies(pkg);
292
+ const results: DetectionResult[] = [];
293
+
294
+ // Check for shadcn first (it uses Radix under the hood)
295
+ const shadcn = detectShadcn(projectRoot, deps);
296
+ if (shadcn && shadcn.confidence >= 40) {
297
+ results.push(shadcn);
298
+ }
299
+
300
+ // Only add Radix as separate if not using shadcn
301
+ if (!shadcn || shadcn.confidence < 40) {
302
+ const radix = detectRadix(projectRoot, deps);
303
+ if (radix && radix.confidence >= 40) {
304
+ results.push(radix);
305
+ }
306
+ }
307
+
308
+ // Check other UI libraries
309
+ const detectors = [
310
+ () => detectMUI(deps),
311
+ () => detectChakra(deps),
312
+ () => detectAntDesign(deps),
313
+ () => detectHeadlessUI(deps),
314
+ () => detectDaisyUI(deps),
315
+ ];
316
+
317
+ for (const detector of detectors) {
318
+ const result = detector();
319
+ if (result && result.confidence >= 40) {
320
+ results.push(result);
321
+ }
322
+ }
323
+
324
+ return results.length > 0 ? results : null;
325
+ },
326
+ };
327
+
328
+ export default uiComponentsDetector;
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Deployment Target Detector
3
+ * Detects: Vercel, Netlify, Railway, Docker
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 Vercel deployment
44
+ */
45
+ function detectVercel(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
46
+ const evidence: string[] = [];
47
+ let confidence = 0;
48
+
49
+ // Check for vercel.json
50
+ const vercelJson = join(projectRoot, 'vercel.json');
51
+ if (existsSync(vercelJson)) {
52
+ evidence.push('vercel.json found');
53
+ confidence += 50;
54
+ }
55
+
56
+ // Check for .vercel directory
57
+ const vercelDir = join(projectRoot, '.vercel');
58
+ if (existsSync(vercelDir)) {
59
+ evidence.push('.vercel/ directory found');
60
+ confidence += 30;
61
+ }
62
+
63
+ // Check for @vercel/* packages
64
+ const vercelPackages = findMatchingDeps(deps, '@vercel/');
65
+ if (vercelPackages.length > 0) {
66
+ evidence.push(`Vercel packages found: ${vercelPackages.join(', ')}`);
67
+ confidence += 30;
68
+ }
69
+
70
+ // Check for vercel CLI
71
+ if (deps.vercel) {
72
+ evidence.push('vercel CLI in devDependencies');
73
+ confidence += 20;
74
+ }
75
+
76
+ if (confidence === 0) return null;
77
+
78
+ return {
79
+ name: 'Vercel',
80
+ confidence: Math.min(confidence, 100),
81
+ evidence,
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Detect Netlify deployment
87
+ */
88
+ function detectNetlify(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
89
+ const evidence: string[] = [];
90
+ let confidence = 0;
91
+
92
+ // Check for netlify.toml
93
+ const netlifyToml = join(projectRoot, 'netlify.toml');
94
+ if (existsSync(netlifyToml)) {
95
+ evidence.push('netlify.toml found');
96
+ confidence += 60;
97
+ }
98
+
99
+ // Check for .netlify directory
100
+ const netlifyDir = join(projectRoot, '.netlify');
101
+ if (existsSync(netlifyDir)) {
102
+ evidence.push('.netlify/ directory found');
103
+ confidence += 30;
104
+ }
105
+
106
+ // Check for netlify-cli
107
+ if (deps['netlify-cli']) {
108
+ evidence.push('netlify-cli in devDependencies');
109
+ confidence += 20;
110
+ }
111
+
112
+ // Check for @netlify/* packages
113
+ const netlifyPackages = findMatchingDeps(deps, '@netlify/');
114
+ if (netlifyPackages.length > 0) {
115
+ evidence.push(`Netlify packages found: ${netlifyPackages.join(', ')}`);
116
+ confidence += 20;
117
+ }
118
+
119
+ if (confidence === 0) return null;
120
+
121
+ return {
122
+ name: 'Netlify',
123
+ confidence: Math.min(confidence, 100),
124
+ evidence,
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Detect Railway deployment
130
+ */
131
+ function detectRailway(projectRoot: string): DetectionResult | null {
132
+ const evidence: string[] = [];
133
+ let confidence = 0;
134
+
135
+ // Check for railway.json
136
+ const railwayJson = join(projectRoot, 'railway.json');
137
+ if (existsSync(railwayJson)) {
138
+ evidence.push('railway.json found');
139
+ confidence += 70;
140
+ }
141
+
142
+ // Check for railway.toml
143
+ const railwayToml = join(projectRoot, 'railway.toml');
144
+ if (existsSync(railwayToml)) {
145
+ evidence.push('railway.toml found');
146
+ confidence += 70;
147
+ }
148
+
149
+ if (confidence === 0) return null;
150
+
151
+ return {
152
+ name: 'Railway',
153
+ confidence: Math.min(confidence, 100),
154
+ evidence,
155
+ };
156
+ }
157
+
158
+ /**
159
+ * Detect Docker deployment
160
+ */
161
+ function detectDocker(projectRoot: string): DetectionResult | null {
162
+ const evidence: string[] = [];
163
+ let confidence = 0;
164
+ let variant: string | undefined;
165
+
166
+ // Check for Dockerfile
167
+ const dockerfile = join(projectRoot, 'Dockerfile');
168
+ if (existsSync(dockerfile)) {
169
+ evidence.push('Dockerfile found');
170
+ confidence += 50;
171
+ }
172
+
173
+ // Check for docker-compose files
174
+ const composeFiles = [
175
+ 'docker-compose.yml',
176
+ 'docker-compose.yaml',
177
+ 'compose.yml',
178
+ 'compose.yaml',
179
+ ];
180
+
181
+ for (const file of composeFiles) {
182
+ if (existsSync(join(projectRoot, file))) {
183
+ evidence.push(`${file} found`);
184
+ confidence += 40;
185
+ variant = 'compose';
186
+ break;
187
+ }
188
+ }
189
+
190
+ // Check for .dockerignore
191
+ const dockerignore = join(projectRoot, '.dockerignore');
192
+ if (existsSync(dockerignore)) {
193
+ evidence.push('.dockerignore found');
194
+ confidence += 10;
195
+ }
196
+
197
+ if (confidence === 0) return null;
198
+
199
+ return {
200
+ name: 'Docker',
201
+ variant,
202
+ confidence: Math.min(confidence, 100),
203
+ evidence,
204
+ };
205
+ }
206
+
207
+ /**
208
+ * Detect Fly.io deployment
209
+ */
210
+ function detectFly(projectRoot: string): DetectionResult | null {
211
+ const evidence: string[] = [];
212
+ let confidence = 0;
213
+
214
+ // Check for fly.toml
215
+ const flyToml = join(projectRoot, 'fly.toml');
216
+ if (existsSync(flyToml)) {
217
+ evidence.push('fly.toml found');
218
+ confidence += 80;
219
+ }
220
+
221
+ if (confidence === 0) return null;
222
+
223
+ return {
224
+ name: 'Fly.io',
225
+ confidence: Math.min(confidence, 100),
226
+ evidence,
227
+ };
228
+ }
229
+
230
+ /**
231
+ * Detect Render deployment
232
+ */
233
+ function detectRender(projectRoot: string): DetectionResult | null {
234
+ const evidence: string[] = [];
235
+ let confidence = 0;
236
+
237
+ // Check for render.yaml
238
+ const renderYaml = join(projectRoot, 'render.yaml');
239
+ if (existsSync(renderYaml)) {
240
+ evidence.push('render.yaml found');
241
+ confidence += 80;
242
+ }
243
+
244
+ if (confidence === 0) return null;
245
+
246
+ return {
247
+ name: 'Render',
248
+ confidence: Math.min(confidence, 100),
249
+ evidence,
250
+ };
251
+ }
252
+
253
+ /**
254
+ * Detect AWS deployment
255
+ */
256
+ function detectAWS(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
257
+ const evidence: string[] = [];
258
+ let confidence = 0;
259
+ let variant: string | undefined;
260
+
261
+ // Check for AWS SAM template
262
+ const samTemplate = join(projectRoot, 'template.yaml');
263
+ if (existsSync(samTemplate)) {
264
+ try {
265
+ const content = readFileSync(samTemplate, 'utf-8');
266
+ if (content.includes('AWS::Serverless') || content.includes('AWSTemplateFormatVersion')) {
267
+ evidence.push('AWS SAM template.yaml found');
268
+ confidence += 70;
269
+ variant = 'sam';
270
+ }
271
+ } catch {
272
+ // Ignore read errors
273
+ }
274
+ }
275
+
276
+ // Check for serverless.yml (Serverless Framework)
277
+ const serverlessYml = join(projectRoot, 'serverless.yml');
278
+ const serverlessYaml = join(projectRoot, 'serverless.yaml');
279
+ if (existsSync(serverlessYml) || existsSync(serverlessYaml)) {
280
+ evidence.push('serverless.yml found');
281
+ confidence += 60;
282
+ variant = 'serverless-framework';
283
+ }
284
+
285
+ // Check for AWS CDK
286
+ if (deps['aws-cdk-lib'] || deps['@aws-cdk/core']) {
287
+ evidence.push('AWS CDK detected');
288
+ confidence += 60;
289
+ variant = 'cdk';
290
+ }
291
+
292
+ // Check for SST
293
+ if (deps.sst) {
294
+ evidence.push(`sst@${deps.sst} in dependencies`);
295
+ confidence += 70;
296
+ variant = 'sst';
297
+ }
298
+
299
+ if (confidence === 0) return null;
300
+
301
+ return {
302
+ name: 'AWS',
303
+ variant,
304
+ confidence: Math.min(confidence, 100),
305
+ evidence,
306
+ };
307
+ }
308
+
309
+ /**
310
+ * Deployment target detector
311
+ * Returns all detected deployment targets (projects can deploy to multiple)
312
+ */
313
+ export const deploymentDetector: Detector = {
314
+ category: 'deployment',
315
+ name: 'Deployment Target Detector',
316
+
317
+ async detect(projectRoot: string): Promise<DetectionResult[] | null> {
318
+ const pkg = readPackageJson(projectRoot);
319
+ const deps = pkg ? getDependencies(pkg) : {};
320
+ const results: DetectionResult[] = [];
321
+
322
+ const detectors = [
323
+ () => detectVercel(projectRoot, deps),
324
+ () => detectNetlify(projectRoot, deps),
325
+ () => detectRailway(projectRoot),
326
+ () => detectDocker(projectRoot),
327
+ () => detectFly(projectRoot),
328
+ () => detectRender(projectRoot),
329
+ () => detectAWS(projectRoot, deps),
330
+ ];
331
+
332
+ for (const detector of detectors) {
333
+ const result = detector();
334
+ if (result && result.confidence >= 40) {
335
+ results.push(result);
336
+ }
337
+ }
338
+
339
+ return results.length > 0 ? results : null;
340
+ },
341
+ };
342
+
343
+ export default deploymentDetector;