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.
- package/LICENSE +21 -0
- package/README.md +341 -0
- package/bin/ralph.js +8 -0
- package/dist/ai/enhancer.d.ts +100 -0
- package/dist/ai/enhancer.d.ts.map +1 -0
- package/dist/ai/enhancer.js +233 -0
- package/dist/ai/enhancer.js.map +1 -0
- package/dist/ai/index.d.ts +8 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +11 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/prompts.d.ts +26 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +201 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/ai/providers.d.ts +35 -0
- package/dist/ai/providers.d.ts.map +1 -0
- package/dist/ai/providers.js +104 -0
- package/dist/ai/providers.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +196 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/init.d.ts +16 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +124 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/monitor.d.ts +17 -0
- package/dist/commands/monitor.d.ts.map +1 -0
- package/dist/commands/monitor.js +342 -0
- package/dist/commands/monitor.js.map +1 -0
- package/dist/commands/new.d.ts +19 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +272 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/run.d.ts +16 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +175 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/generator/config.d.ts +59 -0
- package/dist/generator/config.d.ts.map +1 -0
- package/dist/generator/config.js +68 -0
- package/dist/generator/config.js.map +1 -0
- package/dist/generator/index.d.ts +64 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +147 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/templates.d.ts +70 -0
- package/dist/generator/templates.d.ts.map +1 -0
- package/dist/generator/templates.js +296 -0
- package/dist/generator/templates.js.map +1 -0
- package/dist/generator/writer.d.ts +93 -0
- package/dist/generator/writer.d.ts.map +1 -0
- package/dist/generator/writer.js +213 -0
- package/dist/generator/writer.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/scanner/detectors/core/framework.d.ts +11 -0
- package/dist/scanner/detectors/core/framework.d.ts.map +1 -0
- package/dist/scanner/detectors/core/framework.js +275 -0
- package/dist/scanner/detectors/core/framework.js.map +1 -0
- package/dist/scanner/detectors/core/packageManager.d.ts +11 -0
- package/dist/scanner/detectors/core/packageManager.d.ts.map +1 -0
- package/dist/scanner/detectors/core/packageManager.js +74 -0
- package/dist/scanner/detectors/core/packageManager.js.map +1 -0
- package/dist/scanner/detectors/core/styling.d.ts +12 -0
- package/dist/scanner/detectors/core/styling.d.ts.map +1 -0
- package/dist/scanner/detectors/core/styling.js +230 -0
- package/dist/scanner/detectors/core/styling.js.map +1 -0
- package/dist/scanner/detectors/core/testing.d.ts +12 -0
- package/dist/scanner/detectors/core/testing.d.ts.map +1 -0
- package/dist/scanner/detectors/core/testing.js +190 -0
- package/dist/scanner/detectors/core/testing.js.map +1 -0
- package/dist/scanner/detectors/data/api.d.ts +12 -0
- package/dist/scanner/detectors/data/api.d.ts.map +1 -0
- package/dist/scanner/detectors/data/api.js +261 -0
- package/dist/scanner/detectors/data/api.js.map +1 -0
- package/dist/scanner/detectors/data/database.d.ts +12 -0
- package/dist/scanner/detectors/data/database.d.ts.map +1 -0
- package/dist/scanner/detectors/data/database.js +213 -0
- package/dist/scanner/detectors/data/database.js.map +1 -0
- package/dist/scanner/detectors/data/orm.d.ts +12 -0
- package/dist/scanner/detectors/data/orm.d.ts.map +1 -0
- package/dist/scanner/detectors/data/orm.js +160 -0
- package/dist/scanner/detectors/data/orm.js.map +1 -0
- package/dist/scanner/detectors/frontend/formHandling.d.ts +12 -0
- package/dist/scanner/detectors/frontend/formHandling.d.ts.map +1 -0
- package/dist/scanner/detectors/frontend/formHandling.js +211 -0
- package/dist/scanner/detectors/frontend/formHandling.js.map +1 -0
- package/dist/scanner/detectors/frontend/stateManagement.d.ts +12 -0
- package/dist/scanner/detectors/frontend/stateManagement.d.ts.map +1 -0
- package/dist/scanner/detectors/frontend/stateManagement.js +221 -0
- package/dist/scanner/detectors/frontend/stateManagement.js.map +1 -0
- package/dist/scanner/detectors/frontend/uiComponents.d.ts +12 -0
- package/dist/scanner/detectors/frontend/uiComponents.d.ts.map +1 -0
- package/dist/scanner/detectors/frontend/uiComponents.js +285 -0
- package/dist/scanner/detectors/frontend/uiComponents.js.map +1 -0
- package/dist/scanner/detectors/infra/deployment.d.ts +12 -0
- package/dist/scanner/detectors/infra/deployment.d.ts.map +1 -0
- package/dist/scanner/detectors/infra/deployment.js +301 -0
- package/dist/scanner/detectors/infra/deployment.js.map +1 -0
- package/dist/scanner/detectors/infra/monorepo.d.ts +12 -0
- package/dist/scanner/detectors/infra/monorepo.d.ts.map +1 -0
- package/dist/scanner/detectors/infra/monorepo.js +219 -0
- package/dist/scanner/detectors/infra/monorepo.js.map +1 -0
- package/dist/scanner/detectors/mcp/mcpProject.d.ts +12 -0
- package/dist/scanner/detectors/mcp/mcpProject.d.ts.map +1 -0
- package/dist/scanner/detectors/mcp/mcpProject.js +154 -0
- package/dist/scanner/detectors/mcp/mcpProject.js.map +1 -0
- package/dist/scanner/detectors/mcp/mcpServers.d.ts +17 -0
- package/dist/scanner/detectors/mcp/mcpServers.d.ts.map +1 -0
- package/dist/scanner/detectors/mcp/mcpServers.js +193 -0
- package/dist/scanner/detectors/mcp/mcpServers.js.map +1 -0
- package/dist/scanner/detectors/services/analytics.d.ts +12 -0
- package/dist/scanner/detectors/services/analytics.d.ts.map +1 -0
- package/dist/scanner/detectors/services/analytics.js +236 -0
- package/dist/scanner/detectors/services/analytics.js.map +1 -0
- package/dist/scanner/detectors/services/auth.d.ts +12 -0
- package/dist/scanner/detectors/services/auth.d.ts.map +1 -0
- package/dist/scanner/detectors/services/auth.js +217 -0
- package/dist/scanner/detectors/services/auth.js.map +1 -0
- package/dist/scanner/detectors/services/email.d.ts +12 -0
- package/dist/scanner/detectors/services/email.d.ts.map +1 -0
- package/dist/scanner/detectors/services/email.js +211 -0
- package/dist/scanner/detectors/services/email.js.map +1 -0
- package/dist/scanner/detectors/services/payments.d.ts +12 -0
- package/dist/scanner/detectors/services/payments.d.ts.map +1 -0
- package/dist/scanner/detectors/services/payments.js +185 -0
- package/dist/scanner/detectors/services/payments.js.map +1 -0
- package/dist/scanner/detectors/utils.d.ts +160 -0
- package/dist/scanner/detectors/utils.d.ts.map +1 -0
- package/dist/scanner/detectors/utils.js +222 -0
- package/dist/scanner/detectors/utils.js.map +1 -0
- package/dist/scanner/index.d.ts +42 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +282 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/registry.d.ts +43 -0
- package/dist/scanner/registry.d.ts.map +1 -0
- package/dist/scanner/registry.js +243 -0
- package/dist/scanner/registry.js.map +1 -0
- package/dist/scanner/types.d.ts +112 -0
- package/dist/scanner/types.d.ts.map +1 -0
- package/dist/scanner/types.js +6 -0
- package/dist/scanner/types.js.map +1 -0
- package/dist/templates/config/ralph.config.js.tmpl +38 -0
- package/dist/templates/guides/AGENTS.md.tmpl +100 -0
- package/dist/templates/guides/FRONTEND.md.tmpl +523 -0
- package/dist/templates/guides/PERFORMANCE.md.tmpl +264 -0
- package/dist/templates/guides/SECURITY.md.tmpl +100 -0
- package/dist/templates/prompts/PROMPT.md.tmpl +77 -0
- package/dist/templates/prompts/PROMPT_e2e.md.tmpl +234 -0
- package/dist/templates/prompts/PROMPT_feature.md.tmpl +83 -0
- package/dist/templates/prompts/PROMPT_review.md.tmpl +167 -0
- package/dist/templates/prompts/PROMPT_verify.md.tmpl +72 -0
- package/dist/templates/root/.gitignore.tmpl +5 -0
- package/dist/templates/root/LEARNINGS.md.tmpl +24 -0
- package/dist/templates/root/README.md.tmpl +61 -0
- package/dist/templates/scripts/feature-loop.sh.tmpl +267 -0
- package/dist/templates/scripts/loop.sh.tmpl +59 -0
- package/dist/templates/scripts/ralph-monitor.sh.tmpl +244 -0
- package/dist/templates/specs/README.md.tmpl +57 -0
- package/dist/templates/specs/_example.md.tmpl +71 -0
- package/dist/utils/config.d.ts +95 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +148 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/header.d.ts +5 -0
- package/dist/utils/header.d.ts.map +1 -0
- package/dist/utils/header.js +15 -0
- package/dist/utils/header.js.map +1 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +24 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +44 -0
- package/src/ai/enhancer.ts +350 -0
- package/src/ai/index.ts +38 -0
- package/src/ai/prompts.ts +217 -0
- package/src/ai/providers.ts +136 -0
- package/src/cli.ts +255 -0
- package/src/commands/init.ts +149 -0
- package/src/commands/monitor.ts +412 -0
- package/src/commands/new.ts +312 -0
- package/src/commands/run.ts +214 -0
- package/src/generator/config.ts +116 -0
- package/src/generator/index.ts +227 -0
- package/src/generator/templates.ts +412 -0
- package/src/generator/writer.ts +293 -0
- package/src/index.ts +41 -0
- package/src/scanner/detectors/core/framework.ts +332 -0
- package/src/scanner/detectors/core/packageManager.ts +91 -0
- package/src/scanner/detectors/core/styling.ts +261 -0
- package/src/scanner/detectors/core/testing.ts +221 -0
- package/src/scanner/detectors/data/api.ts +303 -0
- package/src/scanner/detectors/data/database.ts +245 -0
- package/src/scanner/detectors/data/orm.ts +180 -0
- package/src/scanner/detectors/frontend/formHandling.ts +244 -0
- package/src/scanner/detectors/frontend/stateManagement.ts +261 -0
- package/src/scanner/detectors/frontend/uiComponents.ts +328 -0
- package/src/scanner/detectors/infra/deployment.ts +343 -0
- package/src/scanner/detectors/infra/monorepo.ts +251 -0
- package/src/scanner/detectors/mcp/mcpProject.ts +176 -0
- package/src/scanner/detectors/mcp/mcpServers.ts +237 -0
- package/src/scanner/detectors/services/analytics.ts +273 -0
- package/src/scanner/detectors/services/auth.ts +254 -0
- package/src/scanner/detectors/services/email.ts +244 -0
- package/src/scanner/detectors/services/payments.ts +213 -0
- package/src/scanner/detectors/utils.ts +251 -0
- package/src/scanner/index.ts +354 -0
- package/src/scanner/registry.ts +301 -0
- package/src/scanner/types.ts +152 -0
- package/src/templates/config/ralph.config.js.tmpl +38 -0
- package/src/templates/guides/AGENTS.md.tmpl +100 -0
- package/src/templates/guides/FRONTEND.md.tmpl +523 -0
- package/src/templates/guides/PERFORMANCE.md.tmpl +264 -0
- package/src/templates/guides/SECURITY.md.tmpl +100 -0
- package/src/templates/prompts/PROMPT.md.tmpl +77 -0
- package/src/templates/prompts/PROMPT_e2e.md.tmpl +234 -0
- package/src/templates/prompts/PROMPT_feature.md.tmpl +83 -0
- package/src/templates/prompts/PROMPT_review.md.tmpl +167 -0
- package/src/templates/prompts/PROMPT_verify.md.tmpl +72 -0
- package/src/templates/root/.gitignore.tmpl +5 -0
- package/src/templates/root/LEARNINGS.md.tmpl +24 -0
- package/src/templates/root/README.md.tmpl +61 -0
- package/src/templates/scripts/feature-loop.sh.tmpl +267 -0
- package/src/templates/scripts/loop.sh.tmpl +59 -0
- package/src/templates/scripts/ralph-monitor.sh.tmpl +244 -0
- package/src/templates/specs/README.md.tmpl +57 -0
- package/src/templates/specs/_example.md.tmpl +71 -0
- package/src/utils/config.ts +221 -0
- package/src/utils/header.ts +15 -0
- package/src/utils/logger.ts +28 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ORM Detector
|
|
3
|
+
* Detects ORMs: Prisma, Drizzle
|
|
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 a config file exists (supports multiple extensions)
|
|
37
|
+
*/
|
|
38
|
+
function findConfigFile(projectRoot: string, baseName: string, extensions: string[]): string | null {
|
|
39
|
+
for (const ext of extensions) {
|
|
40
|
+
const filePath = join(projectRoot, `${baseName}${ext}`);
|
|
41
|
+
if (existsSync(filePath)) {
|
|
42
|
+
return `${baseName}${ext}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Detect Prisma
|
|
50
|
+
*/
|
|
51
|
+
function detectPrisma(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
|
|
52
|
+
const evidence: string[] = [];
|
|
53
|
+
let confidence = 0;
|
|
54
|
+
|
|
55
|
+
// Check for @prisma/client
|
|
56
|
+
if (deps['@prisma/client']) {
|
|
57
|
+
evidence.push(`@prisma/client@${deps['@prisma/client']} in dependencies`);
|
|
58
|
+
confidence += 50;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check for prisma CLI
|
|
62
|
+
if (deps.prisma) {
|
|
63
|
+
evidence.push(`prisma@${deps.prisma} in devDependencies`);
|
|
64
|
+
confidence += 30;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check for prisma schema file
|
|
68
|
+
const schemaPath = join(projectRoot, 'prisma', 'schema.prisma');
|
|
69
|
+
if (existsSync(schemaPath)) {
|
|
70
|
+
evidence.push('prisma/schema.prisma found');
|
|
71
|
+
confidence += 30;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Also check for schema in root (less common but valid)
|
|
75
|
+
const rootSchema = join(projectRoot, 'schema.prisma');
|
|
76
|
+
if (existsSync(rootSchema)) {
|
|
77
|
+
evidence.push('schema.prisma found in root');
|
|
78
|
+
confidence += 20;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (confidence === 0) return null;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
name: 'Prisma',
|
|
85
|
+
version: deps['@prisma/client'] || deps.prisma,
|
|
86
|
+
confidence: Math.min(confidence, 100),
|
|
87
|
+
evidence,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Detect Drizzle ORM
|
|
93
|
+
*/
|
|
94
|
+
function detectDrizzle(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
|
|
95
|
+
const evidence: string[] = [];
|
|
96
|
+
let confidence = 0;
|
|
97
|
+
|
|
98
|
+
// Check for drizzle-orm
|
|
99
|
+
if (deps['drizzle-orm']) {
|
|
100
|
+
evidence.push(`drizzle-orm@${deps['drizzle-orm']} in dependencies`);
|
|
101
|
+
confidence += 60;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check for drizzle-kit (migration tool)
|
|
105
|
+
if (deps['drizzle-kit']) {
|
|
106
|
+
evidence.push(`drizzle-kit@${deps['drizzle-kit']} in dependencies`);
|
|
107
|
+
confidence += 20;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check for drizzle config file
|
|
111
|
+
const configExtensions = ['.config.ts', '.config.js', '.config.mjs'];
|
|
112
|
+
const configFile = findConfigFile(projectRoot, 'drizzle', configExtensions);
|
|
113
|
+
if (configFile) {
|
|
114
|
+
evidence.push(`${configFile} found`);
|
|
115
|
+
confidence += 20;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Detect database adapter
|
|
119
|
+
let variant: string | undefined;
|
|
120
|
+
if (deps['@planetscale/database'] || deps['drizzle-orm/planetscale-serverless']) {
|
|
121
|
+
variant = 'planetscale';
|
|
122
|
+
} else if (deps['@neondatabase/serverless']) {
|
|
123
|
+
variant = 'neon';
|
|
124
|
+
} else if (deps['@libsql/client'] || deps['better-sqlite3']) {
|
|
125
|
+
variant = 'sqlite';
|
|
126
|
+
} else if (deps.pg || deps.postgres) {
|
|
127
|
+
variant = 'postgres';
|
|
128
|
+
} else if (deps.mysql2) {
|
|
129
|
+
variant = 'mysql';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (confidence === 0) return null;
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
name: 'Drizzle',
|
|
136
|
+
version: deps['drizzle-orm'],
|
|
137
|
+
variant,
|
|
138
|
+
confidence: Math.min(confidence, 100),
|
|
139
|
+
evidence,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* ORM detector
|
|
145
|
+
* Returns the primary ORM detected
|
|
146
|
+
*/
|
|
147
|
+
export const ormDetector: Detector = {
|
|
148
|
+
category: 'orm',
|
|
149
|
+
name: 'ORM Detector',
|
|
150
|
+
|
|
151
|
+
async detect(projectRoot: string): Promise<DetectionResult | null> {
|
|
152
|
+
const pkg = readPackageJson(projectRoot);
|
|
153
|
+
if (!pkg) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const deps = getDependencies(pkg);
|
|
158
|
+
|
|
159
|
+
// Check both ORMs and return the one with higher confidence
|
|
160
|
+
const prisma = detectPrisma(projectRoot, deps);
|
|
161
|
+
const drizzle = detectDrizzle(projectRoot, deps);
|
|
162
|
+
|
|
163
|
+
// Return the one with higher confidence, or first one if equal
|
|
164
|
+
if (prisma && drizzle) {
|
|
165
|
+
return prisma.confidence >= drizzle.confidence ? prisma : drizzle;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (prisma && prisma.confidence >= 40) {
|
|
169
|
+
return prisma;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (drizzle && drizzle.confidence >= 40) {
|
|
173
|
+
return drizzle;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return null;
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export default ormDetector;
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form Handling Detector
|
|
3
|
+
* Detects: React Hook Form, Formik, Zod, Yup
|
|
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
|
+
* Detect React Hook Form
|
|
37
|
+
*/
|
|
38
|
+
function detectReactHookForm(deps: Record<string, string>): DetectionResult | null {
|
|
39
|
+
const evidence: string[] = [];
|
|
40
|
+
let confidence = 0;
|
|
41
|
+
|
|
42
|
+
if (deps['react-hook-form']) {
|
|
43
|
+
evidence.push(`react-hook-form@${deps['react-hook-form']} in dependencies`);
|
|
44
|
+
confidence += 80;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check for @hookform/resolvers (validation integrations)
|
|
48
|
+
if (deps['@hookform/resolvers']) {
|
|
49
|
+
evidence.push('@hookform/resolvers found');
|
|
50
|
+
confidence += 10;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check for @hookform/devtools
|
|
54
|
+
if (deps['@hookform/devtools']) {
|
|
55
|
+
evidence.push('@hookform/devtools found');
|
|
56
|
+
confidence += 5;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (confidence === 0) return null;
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
name: 'React Hook Form',
|
|
63
|
+
version: deps['react-hook-form'],
|
|
64
|
+
confidence: Math.min(confidence, 100),
|
|
65
|
+
evidence,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Detect Formik
|
|
71
|
+
*/
|
|
72
|
+
function detectFormik(deps: Record<string, string>): DetectionResult | null {
|
|
73
|
+
const evidence: string[] = [];
|
|
74
|
+
let confidence = 0;
|
|
75
|
+
|
|
76
|
+
if (deps.formik) {
|
|
77
|
+
evidence.push(`formik@${deps.formik} in dependencies`);
|
|
78
|
+
confidence += 80;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (confidence === 0) return null;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
name: 'Formik',
|
|
85
|
+
version: deps.formik,
|
|
86
|
+
confidence: Math.min(confidence, 100),
|
|
87
|
+
evidence,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Detect Zod
|
|
93
|
+
*/
|
|
94
|
+
function detectZod(deps: Record<string, string>): DetectionResult | null {
|
|
95
|
+
const evidence: string[] = [];
|
|
96
|
+
let confidence = 0;
|
|
97
|
+
|
|
98
|
+
if (deps.zod) {
|
|
99
|
+
evidence.push(`zod@${deps.zod} in dependencies`);
|
|
100
|
+
confidence += 80;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check for zod-to-json-schema
|
|
104
|
+
if (deps['zod-to-json-schema']) {
|
|
105
|
+
evidence.push('zod-to-json-schema found');
|
|
106
|
+
confidence += 10;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (confidence === 0) return null;
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
name: 'Zod',
|
|
113
|
+
version: deps.zod,
|
|
114
|
+
variant: 'validation',
|
|
115
|
+
confidence: Math.min(confidence, 100),
|
|
116
|
+
evidence,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Detect Yup
|
|
122
|
+
*/
|
|
123
|
+
function detectYup(deps: Record<string, string>): DetectionResult | null {
|
|
124
|
+
const evidence: string[] = [];
|
|
125
|
+
let confidence = 0;
|
|
126
|
+
|
|
127
|
+
if (deps.yup) {
|
|
128
|
+
evidence.push(`yup@${deps.yup} in dependencies`);
|
|
129
|
+
confidence += 80;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (confidence === 0) return null;
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
name: 'Yup',
|
|
136
|
+
version: deps.yup,
|
|
137
|
+
variant: 'validation',
|
|
138
|
+
confidence: Math.min(confidence, 100),
|
|
139
|
+
evidence,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Detect Valibot (lightweight alternative to Zod)
|
|
145
|
+
*/
|
|
146
|
+
function detectValibot(deps: Record<string, string>): DetectionResult | null {
|
|
147
|
+
const evidence: string[] = [];
|
|
148
|
+
let confidence = 0;
|
|
149
|
+
|
|
150
|
+
if (deps.valibot) {
|
|
151
|
+
evidence.push(`valibot@${deps.valibot} in dependencies`);
|
|
152
|
+
confidence += 80;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (confidence === 0) return null;
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
name: 'Valibot',
|
|
159
|
+
version: deps.valibot,
|
|
160
|
+
variant: 'validation',
|
|
161
|
+
confidence: Math.min(confidence, 100),
|
|
162
|
+
evidence,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Detect TanStack Form
|
|
168
|
+
*/
|
|
169
|
+
function detectTanStackForm(deps: Record<string, string>): DetectionResult | null {
|
|
170
|
+
const evidence: string[] = [];
|
|
171
|
+
let confidence = 0;
|
|
172
|
+
|
|
173
|
+
if (deps['@tanstack/react-form']) {
|
|
174
|
+
evidence.push(`@tanstack/react-form@${deps['@tanstack/react-form']} in dependencies`);
|
|
175
|
+
confidence += 80;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (deps['@tanstack/vue-form']) {
|
|
179
|
+
evidence.push(`@tanstack/vue-form@${deps['@tanstack/vue-form']} in dependencies`);
|
|
180
|
+
confidence += 80;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (confidence === 0) return null;
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
name: 'TanStack Form',
|
|
187
|
+
version: deps['@tanstack/react-form'] || deps['@tanstack/vue-form'],
|
|
188
|
+
confidence: Math.min(confidence, 100),
|
|
189
|
+
evidence,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Form handling detector
|
|
195
|
+
* Returns all detected form/validation libraries (projects often use multiple)
|
|
196
|
+
*/
|
|
197
|
+
export const formHandlingDetector: Detector = {
|
|
198
|
+
category: 'formHandling',
|
|
199
|
+
name: 'Form Handling Detector',
|
|
200
|
+
|
|
201
|
+
async detect(projectRoot: string): Promise<DetectionResult[] | null> {
|
|
202
|
+
const pkg = readPackageJson(projectRoot);
|
|
203
|
+
if (!pkg) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const deps = getDependencies(pkg);
|
|
208
|
+
const results: DetectionResult[] = [];
|
|
209
|
+
|
|
210
|
+
// Check form libraries
|
|
211
|
+
const formDetectors = [
|
|
212
|
+
() => detectReactHookForm(deps),
|
|
213
|
+
() => detectFormik(deps),
|
|
214
|
+
() => detectTanStackForm(deps),
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
// Check validation libraries
|
|
218
|
+
const validationDetectors = [
|
|
219
|
+
() => detectZod(deps),
|
|
220
|
+
() => detectYup(deps),
|
|
221
|
+
() => detectValibot(deps),
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
// Add form libraries
|
|
225
|
+
for (const detector of formDetectors) {
|
|
226
|
+
const result = detector();
|
|
227
|
+
if (result && result.confidence >= 40) {
|
|
228
|
+
results.push(result);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Add validation libraries
|
|
233
|
+
for (const detector of validationDetectors) {
|
|
234
|
+
const result = detector();
|
|
235
|
+
if (result && result.confidence >= 40) {
|
|
236
|
+
results.push(result);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return results.length > 0 ? results : null;
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
export default formHandlingDetector;
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Management Detector
|
|
3
|
+
* Detects: Redux, Zustand, Jotai, Pinia, Recoil, MobX
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Detector, DetectionResult } from '../../types.js';
|
|
7
|
+
import {
|
|
8
|
+
readPackageJson,
|
|
9
|
+
getDependencies,
|
|
10
|
+
type DependencyMap,
|
|
11
|
+
} from '../utils.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Detect Redux / Redux Toolkit
|
|
15
|
+
*/
|
|
16
|
+
function detectRedux(deps: DependencyMap): DetectionResult | null {
|
|
17
|
+
const evidence: string[] = [];
|
|
18
|
+
let confidence = 0;
|
|
19
|
+
let variant: string | undefined;
|
|
20
|
+
|
|
21
|
+
// Check for Redux Toolkit (modern approach)
|
|
22
|
+
if (deps['@reduxjs/toolkit']) {
|
|
23
|
+
evidence.push(`@reduxjs/toolkit@${deps['@reduxjs/toolkit']} in dependencies`);
|
|
24
|
+
confidence += 70;
|
|
25
|
+
variant = 'toolkit';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check for classic redux
|
|
29
|
+
if (deps.redux) {
|
|
30
|
+
evidence.push(`redux@${deps.redux} in dependencies`);
|
|
31
|
+
confidence += 50;
|
|
32
|
+
if (!variant) variant = 'classic';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Check for react-redux bindings
|
|
36
|
+
if (deps['react-redux']) {
|
|
37
|
+
evidence.push(`react-redux@${deps['react-redux']} in dependencies`);
|
|
38
|
+
confidence += 20;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check for redux-saga or redux-thunk
|
|
42
|
+
if (deps['redux-saga']) {
|
|
43
|
+
evidence.push('redux-saga detected');
|
|
44
|
+
confidence += 10;
|
|
45
|
+
}
|
|
46
|
+
if (deps['redux-thunk']) {
|
|
47
|
+
evidence.push('redux-thunk detected');
|
|
48
|
+
confidence += 10;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (confidence === 0) return null;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
name: 'Redux',
|
|
55
|
+
version: deps['@reduxjs/toolkit'] || deps.redux,
|
|
56
|
+
variant,
|
|
57
|
+
confidence: Math.min(confidence, 100),
|
|
58
|
+
evidence,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Detect Zustand
|
|
64
|
+
*/
|
|
65
|
+
function detectZustand(deps: DependencyMap): DetectionResult | null {
|
|
66
|
+
const evidence: string[] = [];
|
|
67
|
+
let confidence = 0;
|
|
68
|
+
|
|
69
|
+
if (deps.zustand) {
|
|
70
|
+
evidence.push(`zustand@${deps.zustand} in dependencies`);
|
|
71
|
+
confidence += 90;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (confidence === 0) return null;
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
name: 'Zustand',
|
|
78
|
+
version: deps.zustand,
|
|
79
|
+
confidence: Math.min(confidence, 100),
|
|
80
|
+
evidence,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Detect Jotai
|
|
86
|
+
*/
|
|
87
|
+
function detectJotai(deps: DependencyMap): DetectionResult | null {
|
|
88
|
+
const evidence: string[] = [];
|
|
89
|
+
let confidence = 0;
|
|
90
|
+
|
|
91
|
+
if (deps.jotai) {
|
|
92
|
+
evidence.push(`jotai@${deps.jotai} in dependencies`);
|
|
93
|
+
confidence += 90;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Check for jotai utils
|
|
97
|
+
if (deps['jotai-devtools']) {
|
|
98
|
+
evidence.push('jotai-devtools detected');
|
|
99
|
+
confidence += 10;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (confidence === 0) return null;
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
name: 'Jotai',
|
|
106
|
+
version: deps.jotai,
|
|
107
|
+
confidence: Math.min(confidence, 100),
|
|
108
|
+
evidence,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Detect Pinia (Vue state management)
|
|
114
|
+
*/
|
|
115
|
+
function detectPinia(deps: DependencyMap): DetectionResult | null {
|
|
116
|
+
const evidence: string[] = [];
|
|
117
|
+
let confidence = 0;
|
|
118
|
+
|
|
119
|
+
if (deps.pinia) {
|
|
120
|
+
evidence.push(`pinia@${deps.pinia} in dependencies`);
|
|
121
|
+
confidence += 90;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check for pinia plugins
|
|
125
|
+
if (deps['pinia-plugin-persistedstate']) {
|
|
126
|
+
evidence.push('pinia-plugin-persistedstate detected');
|
|
127
|
+
confidence += 10;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (confidence === 0) return null;
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
name: 'Pinia',
|
|
134
|
+
version: deps.pinia,
|
|
135
|
+
confidence: Math.min(confidence, 100),
|
|
136
|
+
evidence,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Detect Recoil
|
|
142
|
+
*/
|
|
143
|
+
function detectRecoil(deps: DependencyMap): DetectionResult | null {
|
|
144
|
+
const evidence: string[] = [];
|
|
145
|
+
let confidence = 0;
|
|
146
|
+
|
|
147
|
+
if (deps.recoil) {
|
|
148
|
+
evidence.push(`recoil@${deps.recoil} in dependencies`);
|
|
149
|
+
confidence += 90;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (confidence === 0) return null;
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
name: 'Recoil',
|
|
156
|
+
version: deps.recoil,
|
|
157
|
+
confidence: Math.min(confidence, 100),
|
|
158
|
+
evidence,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Detect MobX
|
|
164
|
+
*/
|
|
165
|
+
function detectMobX(deps: DependencyMap): DetectionResult | null {
|
|
166
|
+
const evidence: string[] = [];
|
|
167
|
+
let confidence = 0;
|
|
168
|
+
|
|
169
|
+
if (deps.mobx) {
|
|
170
|
+
evidence.push(`mobx@${deps.mobx} in dependencies`);
|
|
171
|
+
confidence += 70;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Check for mobx-react bindings
|
|
175
|
+
if (deps['mobx-react'] || deps['mobx-react-lite']) {
|
|
176
|
+
evidence.push('mobx-react bindings detected');
|
|
177
|
+
confidence += 20;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check for mobx-state-tree
|
|
181
|
+
if (deps['mobx-state-tree']) {
|
|
182
|
+
evidence.push('mobx-state-tree detected');
|
|
183
|
+
confidence += 10;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (confidence === 0) return null;
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
name: 'MobX',
|
|
190
|
+
version: deps.mobx,
|
|
191
|
+
confidence: Math.min(confidence, 100),
|
|
192
|
+
evidence,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Detect Valtio
|
|
198
|
+
*/
|
|
199
|
+
function detectValtio(deps: DependencyMap): DetectionResult | null {
|
|
200
|
+
const evidence: string[] = [];
|
|
201
|
+
let confidence = 0;
|
|
202
|
+
|
|
203
|
+
if (deps.valtio) {
|
|
204
|
+
evidence.push(`valtio@${deps.valtio} in dependencies`);
|
|
205
|
+
confidence += 90;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (confidence === 0) return null;
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
name: 'Valtio',
|
|
212
|
+
version: deps.valtio,
|
|
213
|
+
confidence: Math.min(confidence, 100),
|
|
214
|
+
evidence,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* State management detector
|
|
220
|
+
* Returns the primary state management solution detected
|
|
221
|
+
*/
|
|
222
|
+
export const stateManagementDetector: Detector = {
|
|
223
|
+
category: 'stateManagement',
|
|
224
|
+
name: 'State Management Detector',
|
|
225
|
+
|
|
226
|
+
async detect(projectRoot: string): Promise<DetectionResult | null> {
|
|
227
|
+
const pkg = readPackageJson(projectRoot);
|
|
228
|
+
if (!pkg) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const deps = getDependencies(pkg);
|
|
233
|
+
|
|
234
|
+
// Modern/lightweight solutions preferred in detection order
|
|
235
|
+
const detectors = [
|
|
236
|
+
() => detectZustand(deps),
|
|
237
|
+
() => detectJotai(deps),
|
|
238
|
+
() => detectValtio(deps),
|
|
239
|
+
() => detectPinia(deps),
|
|
240
|
+
() => detectRecoil(deps),
|
|
241
|
+
() => detectRedux(deps),
|
|
242
|
+
() => detectMobX(deps),
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
// Find the highest confidence result
|
|
246
|
+
let bestResult: DetectionResult | null = null;
|
|
247
|
+
let bestConfidence = 0;
|
|
248
|
+
|
|
249
|
+
for (const detector of detectors) {
|
|
250
|
+
const result = detector();
|
|
251
|
+
if (result && result.confidence > bestConfidence) {
|
|
252
|
+
bestResult = result;
|
|
253
|
+
bestConfidence = result.confidence;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return bestResult && bestResult.confidence >= 40 ? bestResult : null;
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export default stateManagementDetector;
|