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,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package Manager Detector
|
|
3
|
+
* Detects: pnpm, yarn, bun, npm
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import type { Detector, DetectionResult } from '../../types.js';
|
|
9
|
+
|
|
10
|
+
interface LockFileInfo {
|
|
11
|
+
name: string;
|
|
12
|
+
file: string;
|
|
13
|
+
confidence: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const LOCK_FILES: LockFileInfo[] = [
|
|
17
|
+
{ name: 'pnpm', file: 'pnpm-lock.yaml', confidence: 95 },
|
|
18
|
+
{ name: 'yarn', file: 'yarn.lock', confidence: 95 },
|
|
19
|
+
{ name: 'bun', file: 'bun.lockb', confidence: 95 },
|
|
20
|
+
{ name: 'npm', file: 'package-lock.json', confidence: 95 },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Package manager detector
|
|
25
|
+
*/
|
|
26
|
+
export const packageManagerDetector: Detector = {
|
|
27
|
+
category: 'packageManager',
|
|
28
|
+
name: 'Package Manager Detector',
|
|
29
|
+
|
|
30
|
+
async detect(projectRoot: string): Promise<DetectionResult | null> {
|
|
31
|
+
const evidence: string[] = [];
|
|
32
|
+
|
|
33
|
+
// Check for lock files in order of preference
|
|
34
|
+
for (const lockFile of LOCK_FILES) {
|
|
35
|
+
const lockFilePath = join(projectRoot, lockFile.file);
|
|
36
|
+
if (existsSync(lockFilePath)) {
|
|
37
|
+
evidence.push(`${lockFile.file} found`);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
name: lockFile.name,
|
|
41
|
+
confidence: lockFile.confidence,
|
|
42
|
+
evidence,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check for packageManager field in package.json
|
|
48
|
+
const packageJsonPath = join(projectRoot, 'package.json');
|
|
49
|
+
if (existsSync(packageJsonPath)) {
|
|
50
|
+
try {
|
|
51
|
+
const { readFileSync } = await import('node:fs');
|
|
52
|
+
const content = readFileSync(packageJsonPath, 'utf-8');
|
|
53
|
+
const pkg = JSON.parse(content);
|
|
54
|
+
|
|
55
|
+
if (pkg.packageManager) {
|
|
56
|
+
const match = pkg.packageManager.match(/^(pnpm|yarn|npm|bun)@/);
|
|
57
|
+
if (match) {
|
|
58
|
+
const manager = match[1];
|
|
59
|
+
const versionMatch = pkg.packageManager.match(/@([^\s]+)/);
|
|
60
|
+
evidence.push(`packageManager field: ${pkg.packageManager}`);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
name: manager,
|
|
64
|
+
version: versionMatch ? versionMatch[1] : undefined,
|
|
65
|
+
confidence: 90,
|
|
66
|
+
evidence,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
// Ignore parse errors
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check for node_modules (fallback, assume npm)
|
|
76
|
+
const nodeModulesPath = join(projectRoot, 'node_modules');
|
|
77
|
+
if (existsSync(nodeModulesPath)) {
|
|
78
|
+
evidence.push('node_modules exists, no lock file found');
|
|
79
|
+
return {
|
|
80
|
+
name: 'npm',
|
|
81
|
+
confidence: 30, // Low confidence since we're just guessing
|
|
82
|
+
evidence,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// No package manager detected
|
|
87
|
+
return null;
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export default packageManagerDetector;
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Styling Detector
|
|
3
|
+
* Detects: Tailwind, CSS Modules, styled-components, Sass/SCSS
|
|
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
|
+
* 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
|
+
* Check if CSS Module files exist in common directories
|
|
50
|
+
*/
|
|
51
|
+
function hasCssModules(projectRoot: string): boolean {
|
|
52
|
+
const dirsToCheck = ['src', 'app', 'pages', 'components', 'styles'];
|
|
53
|
+
|
|
54
|
+
for (const dir of dirsToCheck) {
|
|
55
|
+
const dirPath = join(projectRoot, dir);
|
|
56
|
+
if (existsSync(dirPath)) {
|
|
57
|
+
try {
|
|
58
|
+
const files = readdirSync(dirPath, { recursive: true });
|
|
59
|
+
for (const file of files) {
|
|
60
|
+
const fileName = typeof file === 'string' ? file : file.toString();
|
|
61
|
+
if (fileName.includes('.module.css') || fileName.includes('.module.scss')) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
// Ignore directory read errors
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Detect Tailwind CSS
|
|
76
|
+
*/
|
|
77
|
+
function detectTailwind(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
|
|
78
|
+
const evidence: string[] = [];
|
|
79
|
+
let confidence = 0;
|
|
80
|
+
|
|
81
|
+
if (deps.tailwindcss) {
|
|
82
|
+
evidence.push(`tailwindcss@${deps.tailwindcss} in dependencies`);
|
|
83
|
+
confidence += 50;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check for tailwind config
|
|
87
|
+
const configExtensions = ['.config.js', '.config.ts', '.config.mjs', '.config.cjs'];
|
|
88
|
+
const configFile = findConfigFile(projectRoot, 'tailwind', configExtensions);
|
|
89
|
+
if (configFile) {
|
|
90
|
+
evidence.push(`${configFile} found`);
|
|
91
|
+
confidence += 40;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check for postcss config (often paired with Tailwind)
|
|
95
|
+
const postcssConfig = findConfigFile(projectRoot, 'postcss.config', ['.js', '.mjs', '.cjs']);
|
|
96
|
+
if (postcssConfig && deps.tailwindcss) {
|
|
97
|
+
evidence.push(`${postcssConfig} found`);
|
|
98
|
+
confidence += 10;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (confidence === 0) return null;
|
|
102
|
+
|
|
103
|
+
// Detect Tailwind v4 (uses CSS-based config)
|
|
104
|
+
let variant: string | undefined;
|
|
105
|
+
if (deps.tailwindcss) {
|
|
106
|
+
const version = deps.tailwindcss.replace(/[\^~]/, '');
|
|
107
|
+
if (version.startsWith('4.')) {
|
|
108
|
+
variant = 'v4';
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
name: 'Tailwind CSS',
|
|
114
|
+
version: deps.tailwindcss,
|
|
115
|
+
variant,
|
|
116
|
+
confidence: Math.min(confidence, 100),
|
|
117
|
+
evidence,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Detect styled-components
|
|
123
|
+
*/
|
|
124
|
+
function detectStyledComponents(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
|
|
125
|
+
const evidence: string[] = [];
|
|
126
|
+
let confidence = 0;
|
|
127
|
+
|
|
128
|
+
if (deps['styled-components']) {
|
|
129
|
+
evidence.push(`styled-components@${deps['styled-components']} in dependencies`);
|
|
130
|
+
confidence += 80;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check for babel plugin
|
|
134
|
+
if (deps['babel-plugin-styled-components']) {
|
|
135
|
+
evidence.push('babel-plugin-styled-components in dependencies');
|
|
136
|
+
confidence += 10;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (confidence === 0) return null;
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
name: 'styled-components',
|
|
143
|
+
version: deps['styled-components'],
|
|
144
|
+
confidence: Math.min(confidence, 100),
|
|
145
|
+
evidence,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Detect Emotion
|
|
151
|
+
*/
|
|
152
|
+
function detectEmotion(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
|
|
153
|
+
const evidence: string[] = [];
|
|
154
|
+
let confidence = 0;
|
|
155
|
+
|
|
156
|
+
if (deps['@emotion/react'] || deps['@emotion/styled']) {
|
|
157
|
+
const version = deps['@emotion/react'] || deps['@emotion/styled'];
|
|
158
|
+
evidence.push(`@emotion packages@${version} in dependencies`);
|
|
159
|
+
confidence += 80;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (confidence === 0) return null;
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
name: 'Emotion',
|
|
166
|
+
version: deps['@emotion/react'] || deps['@emotion/styled'],
|
|
167
|
+
confidence: Math.min(confidence, 100),
|
|
168
|
+
evidence,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Detect Sass/SCSS
|
|
174
|
+
*/
|
|
175
|
+
function detectSass(projectRoot: string, deps: Record<string, string>): DetectionResult | null {
|
|
176
|
+
const evidence: string[] = [];
|
|
177
|
+
let confidence = 0;
|
|
178
|
+
|
|
179
|
+
if (deps.sass) {
|
|
180
|
+
evidence.push(`sass@${deps.sass} in dependencies`);
|
|
181
|
+
confidence += 70;
|
|
182
|
+
} else if (deps['node-sass']) {
|
|
183
|
+
evidence.push(`node-sass@${deps['node-sass']} in dependencies`);
|
|
184
|
+
confidence += 70;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (confidence === 0) return null;
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
name: 'Sass/SCSS',
|
|
191
|
+
version: deps.sass || deps['node-sass'],
|
|
192
|
+
confidence: Math.min(confidence, 100),
|
|
193
|
+
evidence,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Detect CSS Modules
|
|
199
|
+
*/
|
|
200
|
+
function detectCssModules(projectRoot: string): DetectionResult | null {
|
|
201
|
+
const evidence: string[] = [];
|
|
202
|
+
let confidence = 0;
|
|
203
|
+
|
|
204
|
+
if (hasCssModules(projectRoot)) {
|
|
205
|
+
evidence.push('*.module.css or *.module.scss files found');
|
|
206
|
+
confidence += 80;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (confidence === 0) return null;
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
name: 'CSS Modules',
|
|
213
|
+
confidence: Math.min(confidence, 100),
|
|
214
|
+
evidence,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Styling detector
|
|
220
|
+
* Returns the primary styling approach detected
|
|
221
|
+
*/
|
|
222
|
+
export const stylingDetector: Detector = {
|
|
223
|
+
category: 'styling',
|
|
224
|
+
name: 'Styling Detector',
|
|
225
|
+
|
|
226
|
+
async detect(projectRoot: string): Promise<DetectionResult | null> {
|
|
227
|
+
const pkg = readPackageJson(projectRoot);
|
|
228
|
+
if (!pkg) {
|
|
229
|
+
// Still check for CSS Modules even without package.json
|
|
230
|
+
return detectCssModules(projectRoot);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const deps = getDependencies(pkg);
|
|
234
|
+
|
|
235
|
+
// Priority order for styling detection
|
|
236
|
+
// Tailwind is checked first as it's often the primary styling approach
|
|
237
|
+
const detectors = [
|
|
238
|
+
() => detectTailwind(projectRoot, deps),
|
|
239
|
+
() => detectStyledComponents(projectRoot, deps),
|
|
240
|
+
() => detectEmotion(projectRoot, deps),
|
|
241
|
+
() => detectSass(projectRoot, deps),
|
|
242
|
+
() => detectCssModules(projectRoot),
|
|
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;
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export default stylingDetector;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Testing Framework Detector
|
|
3
|
+
* Detects unit testing (Jest, Vitest) and E2E testing (Playwright, Cypress)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import type { Detector, DetectionResult } from '../../types.js';
|
|
9
|
+
import {
|
|
10
|
+
readPackageJson,
|
|
11
|
+
getDependencies,
|
|
12
|
+
findConfigFile,
|
|
13
|
+
type PackageJson,
|
|
14
|
+
type DependencyMap,
|
|
15
|
+
} from '../utils.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Detect Vitest
|
|
19
|
+
*/
|
|
20
|
+
function detectVitest(projectRoot: string, deps: DependencyMap): DetectionResult | null {
|
|
21
|
+
const evidence: string[] = [];
|
|
22
|
+
let confidence = 0;
|
|
23
|
+
|
|
24
|
+
if (deps.vitest) {
|
|
25
|
+
evidence.push(`vitest@${deps.vitest} in dependencies`);
|
|
26
|
+
confidence += 60;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check for vitest config
|
|
30
|
+
const configExtensions = ['.config.ts', '.config.js', '.config.mts', '.config.mjs'];
|
|
31
|
+
const configFile = findConfigFile(projectRoot, 'vitest', configExtensions);
|
|
32
|
+
if (configFile) {
|
|
33
|
+
evidence.push(`${configFile} found`);
|
|
34
|
+
confidence += 30;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Vitest can also be configured in vite.config
|
|
38
|
+
if (!configFile && deps.vitest) {
|
|
39
|
+
const viteConfig = findConfigFile(projectRoot, 'vite.config', ['.ts', '.js', '.mjs']);
|
|
40
|
+
if (viteConfig) {
|
|
41
|
+
evidence.push('Vitest likely configured in vite.config');
|
|
42
|
+
confidence += 10;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (confidence === 0) return null;
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
name: 'Vitest',
|
|
50
|
+
version: deps.vitest,
|
|
51
|
+
confidence: Math.min(confidence, 100),
|
|
52
|
+
evidence,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Detect Jest
|
|
58
|
+
*/
|
|
59
|
+
function detectJest(projectRoot: string, deps: DependencyMap, pkg: PackageJson): DetectionResult | null {
|
|
60
|
+
const evidence: string[] = [];
|
|
61
|
+
let confidence = 0;
|
|
62
|
+
|
|
63
|
+
if (deps.jest) {
|
|
64
|
+
evidence.push(`jest@${deps.jest} in dependencies`);
|
|
65
|
+
confidence += 50;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check for jest config files
|
|
69
|
+
const configExtensions = ['.config.js', '.config.ts', '.config.mjs', '.config.cjs'];
|
|
70
|
+
const configFile = findConfigFile(projectRoot, 'jest', configExtensions);
|
|
71
|
+
if (configFile) {
|
|
72
|
+
evidence.push(`${configFile} found`);
|
|
73
|
+
confidence += 30;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check for jest field in package.json
|
|
77
|
+
if (pkg.jest) {
|
|
78
|
+
evidence.push('jest config in package.json');
|
|
79
|
+
confidence += 20;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check for related packages
|
|
83
|
+
if (deps['@types/jest']) {
|
|
84
|
+
evidence.push('@types/jest in dependencies');
|
|
85
|
+
confidence += 10;
|
|
86
|
+
}
|
|
87
|
+
if (deps['ts-jest']) {
|
|
88
|
+
evidence.push('ts-jest in dependencies');
|
|
89
|
+
confidence += 10;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (confidence === 0) return null;
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
name: 'Jest',
|
|
96
|
+
version: deps.jest,
|
|
97
|
+
confidence: Math.min(confidence, 100),
|
|
98
|
+
evidence,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Detect Playwright
|
|
104
|
+
*/
|
|
105
|
+
function detectPlaywright(projectRoot: string, deps: DependencyMap): DetectionResult | null {
|
|
106
|
+
const evidence: string[] = [];
|
|
107
|
+
let confidence = 0;
|
|
108
|
+
|
|
109
|
+
if (deps['@playwright/test']) {
|
|
110
|
+
evidence.push(`@playwright/test@${deps['@playwright/test']} in dependencies`);
|
|
111
|
+
confidence += 60;
|
|
112
|
+
} else if (deps.playwright) {
|
|
113
|
+
evidence.push(`playwright@${deps.playwright} in dependencies`);
|
|
114
|
+
confidence += 50;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check for playwright config
|
|
118
|
+
const configExtensions = ['.config.ts', '.config.js', '.config.mjs'];
|
|
119
|
+
const configFile = findConfigFile(projectRoot, 'playwright', configExtensions);
|
|
120
|
+
if (configFile) {
|
|
121
|
+
evidence.push(`${configFile} found`);
|
|
122
|
+
confidence += 30;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (confidence === 0) return null;
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
name: 'Playwright',
|
|
129
|
+
version: deps['@playwright/test'] || deps.playwright,
|
|
130
|
+
confidence: Math.min(confidence, 100),
|
|
131
|
+
evidence,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Detect Cypress
|
|
137
|
+
*/
|
|
138
|
+
function detectCypress(projectRoot: string, deps: DependencyMap): DetectionResult | null {
|
|
139
|
+
const evidence: string[] = [];
|
|
140
|
+
let confidence = 0;
|
|
141
|
+
|
|
142
|
+
if (deps.cypress) {
|
|
143
|
+
evidence.push(`cypress@${deps.cypress} in dependencies`);
|
|
144
|
+
confidence += 60;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Check for cypress config
|
|
148
|
+
const configExtensions = ['.config.ts', '.config.js', '.config.mjs', '.config.cjs'];
|
|
149
|
+
const configFile = findConfigFile(projectRoot, 'cypress', configExtensions);
|
|
150
|
+
if (configFile) {
|
|
151
|
+
evidence.push(`${configFile} found`);
|
|
152
|
+
confidence += 30;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Check for cypress folder
|
|
156
|
+
const cypressFolder = join(projectRoot, 'cypress');
|
|
157
|
+
if (existsSync(cypressFolder)) {
|
|
158
|
+
evidence.push('cypress/ folder found');
|
|
159
|
+
confidence += 10;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (confidence === 0) return null;
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
name: 'Cypress',
|
|
166
|
+
version: deps.cypress,
|
|
167
|
+
confidence: Math.min(confidence, 100),
|
|
168
|
+
evidence,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Testing framework detector
|
|
174
|
+
* Returns both unit and e2e test frameworks
|
|
175
|
+
*/
|
|
176
|
+
export const testingDetector: Detector = {
|
|
177
|
+
category: 'testing',
|
|
178
|
+
name: 'Testing Framework Detector',
|
|
179
|
+
|
|
180
|
+
async detect(projectRoot: string): Promise<DetectionResult[] | null> {
|
|
181
|
+
const pkg = readPackageJson(projectRoot);
|
|
182
|
+
if (!pkg) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const deps = getDependencies(pkg);
|
|
187
|
+
const results: DetectionResult[] = [];
|
|
188
|
+
|
|
189
|
+
// Detect unit testing frameworks
|
|
190
|
+
// Prefer Vitest if both are present (as Vitest is often added later)
|
|
191
|
+
const vitest = detectVitest(projectRoot, deps);
|
|
192
|
+
const jest = detectJest(projectRoot, deps, pkg);
|
|
193
|
+
|
|
194
|
+
if (vitest && vitest.confidence >= 40) {
|
|
195
|
+
results.push({ ...vitest, variant: 'unit' });
|
|
196
|
+
} else if (jest && jest.confidence >= 40) {
|
|
197
|
+
results.push({ ...jest, variant: 'unit' });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Detect E2E testing frameworks
|
|
201
|
+
const playwright = detectPlaywright(projectRoot, deps);
|
|
202
|
+
const cypress = detectCypress(projectRoot, deps);
|
|
203
|
+
|
|
204
|
+
// If both are present, include the one with higher confidence
|
|
205
|
+
if (playwright && cypress) {
|
|
206
|
+
if (playwright.confidence >= cypress.confidence) {
|
|
207
|
+
results.push({ ...playwright, variant: 'e2e' });
|
|
208
|
+
} else {
|
|
209
|
+
results.push({ ...cypress, variant: 'e2e' });
|
|
210
|
+
}
|
|
211
|
+
} else if (playwright && playwright.confidence >= 40) {
|
|
212
|
+
results.push({ ...playwright, variant: 'e2e' });
|
|
213
|
+
} else if (cypress && cypress.confidence >= 40) {
|
|
214
|
+
results.push({ ...cypress, variant: 'e2e' });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return results.length > 0 ? results : null;
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
export default testingDetector;
|