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,303 @@
1
+ /**
2
+ * API Pattern Detector
3
+ * Detects: tRPC, GraphQL, TanStack Query, REST patterns
4
+ */
5
+
6
+ import { existsSync, readFileSync, readdirSync } 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) || name === pattern);
40
+ }
41
+
42
+ /**
43
+ * Detect tRPC
44
+ */
45
+ function detectTRPC(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
46
+ const evidence: string[] = [];
47
+ let confidence = 0;
48
+
49
+ // Check for @trpc/* packages
50
+ const trpcPackages = findMatchingDeps(deps, '@trpc/');
51
+ if (trpcPackages.length > 0) {
52
+ const mainVersion = deps['@trpc/server'] || deps['@trpc/client'];
53
+ evidence.push(`tRPC packages found: ${trpcPackages.join(', ')}`);
54
+ confidence += 70;
55
+
56
+ // Check for specific packages
57
+ if (deps['@trpc/server'] && deps['@trpc/client']) {
58
+ confidence += 20;
59
+ }
60
+ if (deps['@trpc/react-query'] || deps['@trpc/next']) {
61
+ evidence.push('tRPC React/Next integration detected');
62
+ confidence += 10;
63
+ }
64
+
65
+ return {
66
+ name: 'tRPC',
67
+ version: mainVersion,
68
+ confidence: Math.min(confidence, 100),
69
+ evidence,
70
+ };
71
+ }
72
+
73
+ return null;
74
+ }
75
+
76
+ /**
77
+ * Detect GraphQL
78
+ */
79
+ function detectGraphQL(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
80
+ const evidence: string[] = [];
81
+ let confidence = 0;
82
+
83
+ // Check for graphql package
84
+ if (deps.graphql) {
85
+ evidence.push(`graphql@${deps.graphql} in dependencies`);
86
+ confidence += 40;
87
+ }
88
+
89
+ // Check for Apollo packages
90
+ const apolloPackages = findMatchingDeps(deps, '@apollo/');
91
+ if (apolloPackages.length > 0) {
92
+ evidence.push(`Apollo packages found: ${apolloPackages.join(', ')}`);
93
+ confidence += 40;
94
+
95
+ return {
96
+ name: 'GraphQL',
97
+ version: deps.graphql,
98
+ variant: 'apollo',
99
+ confidence: Math.min(confidence, 100),
100
+ evidence,
101
+ };
102
+ }
103
+
104
+ // Check for urql
105
+ if (deps.urql || deps['@urql/core']) {
106
+ evidence.push('urql client detected');
107
+ confidence += 40;
108
+
109
+ return {
110
+ name: 'GraphQL',
111
+ version: deps.graphql,
112
+ variant: 'urql',
113
+ confidence: Math.min(confidence, 100),
114
+ evidence,
115
+ };
116
+ }
117
+
118
+ // Check for relay
119
+ if (deps['react-relay'] || deps['relay-runtime']) {
120
+ evidence.push('Relay detected');
121
+ confidence += 40;
122
+
123
+ return {
124
+ name: 'GraphQL',
125
+ version: deps.graphql,
126
+ variant: 'relay',
127
+ confidence: Math.min(confidence, 100),
128
+ evidence,
129
+ };
130
+ }
131
+
132
+ // Check for schema files
133
+ const schemaFiles = ['schema.graphql', 'schema.gql'];
134
+ for (const file of schemaFiles) {
135
+ if (existsSync(join(projectRoot, file))) {
136
+ evidence.push(`${file} found`);
137
+ confidence += 20;
138
+ }
139
+ }
140
+
141
+ if (confidence === 0) return null;
142
+
143
+ return {
144
+ name: 'GraphQL',
145
+ version: deps.graphql,
146
+ confidence: Math.min(confidence, 100),
147
+ evidence,
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Detect TanStack Query (React Query, Vue Query)
153
+ */
154
+ function detectTanStackQuery(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
155
+ const evidence: string[] = [];
156
+ let confidence = 0;
157
+ let variant: string | undefined;
158
+
159
+ // Check for @tanstack/react-query
160
+ if (deps['@tanstack/react-query']) {
161
+ evidence.push(`@tanstack/react-query@${deps['@tanstack/react-query']} in dependencies`);
162
+ confidence += 80;
163
+ variant = 'react';
164
+ }
165
+
166
+ // Check for @tanstack/vue-query
167
+ if (deps['@tanstack/vue-query']) {
168
+ evidence.push(`@tanstack/vue-query@${deps['@tanstack/vue-query']} in dependencies`);
169
+ confidence += 80;
170
+ variant = 'vue';
171
+ }
172
+
173
+ // Check for @tanstack/svelte-query
174
+ if (deps['@tanstack/svelte-query']) {
175
+ evidence.push(`@tanstack/svelte-query@${deps['@tanstack/svelte-query']} in dependencies`);
176
+ confidence += 80;
177
+ variant = 'svelte';
178
+ }
179
+
180
+ // Check for old react-query package (v3)
181
+ if (deps['react-query']) {
182
+ evidence.push(`react-query@${deps['react-query']} in dependencies (legacy)`);
183
+ confidence += 70;
184
+ variant = 'react-legacy';
185
+ }
186
+
187
+ // Check for devtools
188
+ if (deps['@tanstack/react-query-devtools'] || deps['@tanstack/vue-query-devtools']) {
189
+ evidence.push('TanStack Query devtools detected');
190
+ confidence += 10;
191
+ }
192
+
193
+ if (confidence === 0) return null;
194
+
195
+ const version = deps['@tanstack/react-query'] || deps['@tanstack/vue-query'] ||
196
+ deps['@tanstack/svelte-query'] || deps['react-query'];
197
+
198
+ return {
199
+ name: 'TanStack Query',
200
+ version,
201
+ variant,
202
+ confidence: Math.min(confidence, 100),
203
+ evidence,
204
+ };
205
+ }
206
+
207
+ /**
208
+ * Detect REST patterns (axios, fetch wrappers)
209
+ */
210
+ function detectREST(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
211
+ const evidence: string[] = [];
212
+ let confidence = 0;
213
+ let variant: string | undefined;
214
+
215
+ // Check for axios
216
+ if (deps.axios) {
217
+ evidence.push(`axios@${deps.axios} in dependencies`);
218
+ confidence += 60;
219
+ variant = 'axios';
220
+ }
221
+
222
+ // Check for ky (modern fetch wrapper)
223
+ if (deps.ky) {
224
+ evidence.push(`ky@${deps.ky} in dependencies`);
225
+ confidence += 60;
226
+ variant = 'ky';
227
+ }
228
+
229
+ // Check for got (Node.js HTTP client)
230
+ if (deps.got) {
231
+ evidence.push(`got@${deps.got} in dependencies`);
232
+ confidence += 50;
233
+ variant = 'got';
234
+ }
235
+
236
+ // Check for node-fetch
237
+ if (deps['node-fetch']) {
238
+ evidence.push(`node-fetch@${deps['node-fetch']} in dependencies`);
239
+ confidence += 40;
240
+ variant = 'node-fetch';
241
+ }
242
+
243
+ // Check for SWR (while not strictly REST, often used with REST)
244
+ if (deps.swr) {
245
+ evidence.push(`swr@${deps.swr} in dependencies`);
246
+ confidence += 50;
247
+ variant = 'swr';
248
+ }
249
+
250
+ if (confidence === 0) return null;
251
+
252
+ return {
253
+ name: 'REST',
254
+ version: deps.axios || deps.ky || deps.got || deps['node-fetch'] || deps.swr,
255
+ variant,
256
+ confidence: Math.min(confidence, 100),
257
+ evidence,
258
+ };
259
+ }
260
+
261
+ /**
262
+ * API pattern detector
263
+ * Returns all detected API patterns
264
+ */
265
+ export const apiDetector: Detector = {
266
+ category: 'api',
267
+ name: 'API Pattern Detector',
268
+
269
+ async detect(projectRoot: string): Promise<DetectionResult[] | null> {
270
+ const pkg = readPackageJson(projectRoot);
271
+ if (!pkg) {
272
+ return null;
273
+ }
274
+
275
+ const deps = getDependencies(pkg);
276
+ const results: DetectionResult[] = [];
277
+
278
+ // Detect all API patterns (project can use multiple)
279
+ const trpc = detectTRPC(projectRoot, deps);
280
+ if (trpc && trpc.confidence >= 40) {
281
+ results.push(trpc);
282
+ }
283
+
284
+ const graphql = detectGraphQL(projectRoot, deps);
285
+ if (graphql && graphql.confidence >= 40) {
286
+ results.push(graphql);
287
+ }
288
+
289
+ const tanstack = detectTanStackQuery(projectRoot, deps);
290
+ if (tanstack && tanstack.confidence >= 40) {
291
+ results.push(tanstack);
292
+ }
293
+
294
+ const rest = detectREST(projectRoot, deps);
295
+ if (rest && rest.confidence >= 40) {
296
+ results.push(rest);
297
+ }
298
+
299
+ return results.length > 0 ? results : null;
300
+ },
301
+ };
302
+
303
+ export default apiDetector;
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Database Detector
3
+ * Detects databases: Supabase, Firebase, MongoDB, PostgreSQL
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
+ * Check if any dependency matches a pattern
37
+ */
38
+ function hasDependencyPattern(deps: Record<string, string>, pattern: string): string | undefined {
39
+ for (const [name, version] of Object.entries(deps)) {
40
+ if (name.startsWith(pattern) || name === pattern) {
41
+ return version;
42
+ }
43
+ }
44
+ return undefined;
45
+ }
46
+
47
+ /**
48
+ * Find all matching dependencies by pattern
49
+ */
50
+ function findMatchingDeps(deps: Record<string, string>, pattern: string): string[] {
51
+ return Object.keys(deps).filter(name => name.startsWith(pattern) || name === pattern);
52
+ }
53
+
54
+ /**
55
+ * Detect Supabase
56
+ */
57
+ function detectSupabase(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
58
+ const evidence: string[] = [];
59
+ let confidence = 0;
60
+
61
+ // Check for @supabase/* packages
62
+ if (deps['@supabase/supabase-js']) {
63
+ evidence.push(`@supabase/supabase-js@${deps['@supabase/supabase-js']} in dependencies`);
64
+ confidence += 70;
65
+ }
66
+
67
+ const supabasePackages = findMatchingDeps(deps, '@supabase/');
68
+ if (supabasePackages.length > 1) {
69
+ evidence.push(`Multiple @supabase packages found: ${supabasePackages.join(', ')}`);
70
+ confidence += 10;
71
+ }
72
+
73
+ // Check for supabase directory
74
+ const supabaseDir = join(projectRoot, 'supabase');
75
+ if (existsSync(supabaseDir)) {
76
+ evidence.push('supabase/ directory found');
77
+ confidence += 20;
78
+ }
79
+
80
+ // Check for supabase config
81
+ const configPath = join(supabaseDir, 'config.toml');
82
+ if (existsSync(configPath)) {
83
+ evidence.push('supabase/config.toml found');
84
+ confidence += 10;
85
+ }
86
+
87
+ if (confidence === 0) return null;
88
+
89
+ return {
90
+ name: 'Supabase',
91
+ version: deps['@supabase/supabase-js'],
92
+ confidence: Math.min(confidence, 100),
93
+ evidence,
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Detect Firebase
99
+ */
100
+ function detectFirebase(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
101
+ const evidence: string[] = [];
102
+ let confidence = 0;
103
+
104
+ // Check for firebase package
105
+ if (deps.firebase) {
106
+ evidence.push(`firebase@${deps.firebase} in dependencies`);
107
+ confidence += 70;
108
+ }
109
+
110
+ // Check for @firebase/* packages
111
+ const firebasePackages = findMatchingDeps(deps, '@firebase/');
112
+ if (firebasePackages.length > 0) {
113
+ evidence.push(`Firebase packages found: ${firebasePackages.slice(0, 3).join(', ')}${firebasePackages.length > 3 ? '...' : ''}`);
114
+ confidence += 20;
115
+ }
116
+
117
+ // Check for firebase admin
118
+ if (deps['firebase-admin']) {
119
+ evidence.push(`firebase-admin@${deps['firebase-admin']} in dependencies`);
120
+ confidence += 20;
121
+ }
122
+
123
+ // Check for firebase.json config
124
+ const firebaseConfig = join(projectRoot, 'firebase.json');
125
+ if (existsSync(firebaseConfig)) {
126
+ evidence.push('firebase.json found');
127
+ confidence += 10;
128
+ }
129
+
130
+ if (confidence === 0) return null;
131
+
132
+ return {
133
+ name: 'Firebase',
134
+ version: deps.firebase || deps['firebase-admin'],
135
+ confidence: Math.min(confidence, 100),
136
+ evidence,
137
+ };
138
+ }
139
+
140
+ /**
141
+ * Detect MongoDB (via mongoose or mongodb driver)
142
+ */
143
+ function detectMongoDB(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
144
+ const evidence: string[] = [];
145
+ let confidence = 0;
146
+
147
+ // Check for mongoose (ODM)
148
+ if (deps.mongoose) {
149
+ evidence.push(`mongoose@${deps.mongoose} in dependencies`);
150
+ confidence += 80;
151
+ return {
152
+ name: 'MongoDB',
153
+ version: deps.mongoose,
154
+ variant: 'mongoose',
155
+ confidence: Math.min(confidence, 100),
156
+ evidence,
157
+ };
158
+ }
159
+
160
+ // Check for native mongodb driver
161
+ if (deps.mongodb) {
162
+ evidence.push(`mongodb@${deps.mongodb} in dependencies`);
163
+ confidence += 80;
164
+ return {
165
+ name: 'MongoDB',
166
+ version: deps.mongodb,
167
+ variant: 'native-driver',
168
+ confidence: Math.min(confidence, 100),
169
+ evidence,
170
+ };
171
+ }
172
+
173
+ return null;
174
+ }
175
+
176
+ /**
177
+ * Detect PostgreSQL
178
+ */
179
+ function detectPostgreSQL(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
180
+ const evidence: string[] = [];
181
+ let confidence = 0;
182
+
183
+ // Check for pg (node-postgres)
184
+ if (deps.pg) {
185
+ evidence.push(`pg@${deps.pg} in dependencies`);
186
+ confidence += 70;
187
+ }
188
+
189
+ // Check for postgres (modern driver)
190
+ if (deps.postgres) {
191
+ evidence.push(`postgres@${deps.postgres} in dependencies`);
192
+ confidence += 70;
193
+ }
194
+
195
+ // Note: Supabase and Prisma also use Postgres, but they're separate categories
196
+ // This detector is for direct Postgres usage
197
+
198
+ if (confidence === 0) return null;
199
+
200
+ return {
201
+ name: 'PostgreSQL',
202
+ version: deps.pg || deps.postgres,
203
+ variant: deps.pg ? 'node-postgres' : 'postgres.js',
204
+ confidence: Math.min(confidence, 100),
205
+ evidence,
206
+ };
207
+ }
208
+
209
+ /**
210
+ * Database detector
211
+ * Returns the primary database detected
212
+ */
213
+ export const databaseDetector: Detector = {
214
+ category: 'database',
215
+ name: 'Database Detector',
216
+
217
+ async detect(projectRoot: string): Promise<DetectionResult | null> {
218
+ const pkg = readPackageJson(projectRoot);
219
+ if (!pkg) {
220
+ return null;
221
+ }
222
+
223
+ const deps = getDependencies(pkg);
224
+
225
+ // Priority: Supabase > Firebase > MongoDB > PostgreSQL
226
+ // (BaaS solutions take precedence as they encompass more)
227
+ const detectors = [
228
+ () => detectSupabase(projectRoot, deps),
229
+ () => detectFirebase(projectRoot, deps),
230
+ () => detectMongoDB(projectRoot, deps),
231
+ () => detectPostgreSQL(projectRoot, deps),
232
+ ];
233
+
234
+ for (const detector of detectors) {
235
+ const result = detector();
236
+ if (result && result.confidence >= 40) {
237
+ return result;
238
+ }
239
+ }
240
+
241
+ return null;
242
+ },
243
+ };
244
+
245
+ export default databaseDetector;