@vibecheckai/cli 3.0.2 → 3.0.3
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/package.json +9 -1
- package/bin/cli-hygiene.js +0 -241
- package/bin/guardrail.js +0 -834
- package/bin/runners/cli-utils.js +0 -1070
- package/bin/runners/context/ai-task-decomposer.js +0 -337
- package/bin/runners/context/analyzer.js +0 -462
- package/bin/runners/context/api-contracts.js +0 -427
- package/bin/runners/context/context-diff.js +0 -342
- package/bin/runners/context/context-pruner.js +0 -291
- package/bin/runners/context/dependency-graph.js +0 -414
- package/bin/runners/context/generators/claude.js +0 -107
- package/bin/runners/context/generators/codex.js +0 -108
- package/bin/runners/context/generators/copilot.js +0 -119
- package/bin/runners/context/generators/cursor.js +0 -514
- package/bin/runners/context/generators/mcp.js +0 -151
- package/bin/runners/context/generators/windsurf.js +0 -180
- package/bin/runners/context/git-context.js +0 -302
- package/bin/runners/context/index.js +0 -1042
- package/bin/runners/context/insights.js +0 -173
- package/bin/runners/context/mcp-server/generate-rules.js +0 -337
- package/bin/runners/context/mcp-server/index.js +0 -1176
- package/bin/runners/context/mcp-server/package.json +0 -24
- package/bin/runners/context/memory.js +0 -200
- package/bin/runners/context/monorepo.js +0 -215
- package/bin/runners/context/multi-repo-federation.js +0 -404
- package/bin/runners/context/patterns.js +0 -253
- package/bin/runners/context/proof-context.js +0 -972
- package/bin/runners/context/security-scanner.js +0 -303
- package/bin/runners/context/semantic-search.js +0 -350
- package/bin/runners/context/shared.js +0 -264
- package/bin/runners/context/team-conventions.js +0 -310
- package/bin/runners/lib/ai-bridge.js +0 -416
- package/bin/runners/lib/analysis-core.js +0 -271
- package/bin/runners/lib/analyzers.js +0 -541
- package/bin/runners/lib/audit-bridge.js +0 -391
- package/bin/runners/lib/auth-truth.js +0 -193
- package/bin/runners/lib/auth.js +0 -215
- package/bin/runners/lib/backup.js +0 -62
- package/bin/runners/lib/billing.js +0 -107
- package/bin/runners/lib/claims.js +0 -118
- package/bin/runners/lib/cli-ui.js +0 -540
- package/bin/runners/lib/compliance-bridge-new.js +0 -0
- package/bin/runners/lib/compliance-bridge.js +0 -165
- package/bin/runners/lib/contracts/auth-contract.js +0 -194
- package/bin/runners/lib/contracts/env-contract.js +0 -178
- package/bin/runners/lib/contracts/external-contract.js +0 -198
- package/bin/runners/lib/contracts/guard.js +0 -168
- package/bin/runners/lib/contracts/index.js +0 -89
- package/bin/runners/lib/contracts/plan-validator.js +0 -311
- package/bin/runners/lib/contracts/route-contract.js +0 -192
- package/bin/runners/lib/detect.js +0 -89
- package/bin/runners/lib/doctor/autofix.js +0 -254
- package/bin/runners/lib/doctor/index.js +0 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +0 -325
- package/bin/runners/lib/doctor/modules/index.js +0 -46
- package/bin/runners/lib/doctor/modules/network.js +0 -250
- package/bin/runners/lib/doctor/modules/project.js +0 -312
- package/bin/runners/lib/doctor/modules/runtime.js +0 -224
- package/bin/runners/lib/doctor/modules/security.js +0 -348
- package/bin/runners/lib/doctor/modules/system.js +0 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +0 -394
- package/bin/runners/lib/doctor/reporter.js +0 -262
- package/bin/runners/lib/doctor/service.js +0 -262
- package/bin/runners/lib/doctor/types.js +0 -113
- package/bin/runners/lib/doctor/ui.js +0 -263
- package/bin/runners/lib/doctor-enhanced.js +0 -233
- package/bin/runners/lib/doctor-v2.js +0 -608
- package/bin/runners/lib/enforcement.js +0 -72
|
@@ -1,462 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project Analyzer Module
|
|
3
|
-
* Analyzes project structure, framework, and conventions
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require("fs");
|
|
7
|
-
const path = require("path");
|
|
8
|
-
const { detectPatterns } = require("./patterns");
|
|
9
|
-
const { detectMonorepo } = require("./monorepo");
|
|
10
|
-
|
|
11
|
-
const c = {
|
|
12
|
-
dim: "\x1b[2m",
|
|
13
|
-
reset: "\x1b[0m",
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Find files recursively in a directory
|
|
18
|
-
*/
|
|
19
|
-
function findFilesRecursive(dir, extensions, maxDepth = 5, currentDepth = 0) {
|
|
20
|
-
if (currentDepth >= maxDepth || !fs.existsSync(dir)) return [];
|
|
21
|
-
|
|
22
|
-
const files = [];
|
|
23
|
-
try {
|
|
24
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
25
|
-
for (const entry of entries) {
|
|
26
|
-
const fullPath = path.join(dir, entry.name);
|
|
27
|
-
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
28
|
-
files.push(...findFilesRecursive(fullPath, extensions, maxDepth, currentDepth + 1));
|
|
29
|
-
} else if (entry.isFile() && extensions.some(ext => entry.name.endsWith(ext))) {
|
|
30
|
-
files.push(fullPath);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
} catch {}
|
|
34
|
-
return files;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Detect environment variables used in the codebase
|
|
39
|
-
*/
|
|
40
|
-
function detectEnvVars(projectPath) {
|
|
41
|
-
const envVars = new Set();
|
|
42
|
-
const envFiles = [];
|
|
43
|
-
|
|
44
|
-
const envFileNames = [".env", ".env.local", ".env.example", ".env.development", ".env.production"];
|
|
45
|
-
for (const envFile of envFileNames) {
|
|
46
|
-
const envPath = path.join(projectPath, envFile);
|
|
47
|
-
if (fs.existsSync(envPath)) {
|
|
48
|
-
envFiles.push(envFile);
|
|
49
|
-
try {
|
|
50
|
-
const content = fs.readFileSync(envPath, "utf-8");
|
|
51
|
-
const matches = content.match(/^([A-Z][A-Z0-9_]+)=/gm) || [];
|
|
52
|
-
matches.forEach(m => envVars.add(m.replace("=", "")));
|
|
53
|
-
} catch {}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const srcFiles = findFilesRecursive(projectPath, [".ts", ".tsx", ".js", ".jsx"], 4);
|
|
58
|
-
for (const file of srcFiles.slice(0, 50)) {
|
|
59
|
-
try {
|
|
60
|
-
const content = fs.readFileSync(file, "utf-8");
|
|
61
|
-
const matches = content.match(/process\.env\.([A-Z][A-Z0-9_]+)/g) || [];
|
|
62
|
-
matches.forEach(m => envVars.add(m.replace("process.env.", "")));
|
|
63
|
-
const viteMatches = content.match(/import\.meta\.env\.([A-Z][A-Z0-9_]+)/g) || [];
|
|
64
|
-
viteMatches.forEach(m => envVars.add(m.replace("import.meta.env.", "")));
|
|
65
|
-
} catch {}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
files: envFiles,
|
|
70
|
-
variables: Array.from(envVars).sort(),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Extract key types and interfaces from the codebase
|
|
76
|
-
*/
|
|
77
|
-
function extractTypes(projectPath) {
|
|
78
|
-
const types = {
|
|
79
|
-
interfaces: [],
|
|
80
|
-
types: [],
|
|
81
|
-
enums: [],
|
|
82
|
-
examples: {},
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const srcFiles = findFilesRecursive(projectPath, [".ts", ".tsx"], 5);
|
|
86
|
-
|
|
87
|
-
for (const file of srcFiles.slice(0, 80)) {
|
|
88
|
-
try {
|
|
89
|
-
const content = fs.readFileSync(file, "utf-8");
|
|
90
|
-
const relativePath = path.relative(projectPath, file);
|
|
91
|
-
|
|
92
|
-
const interfaceMatches = content.match(/export\s+interface\s+(\w+)/g) || [];
|
|
93
|
-
interfaceMatches.forEach(m => {
|
|
94
|
-
const name = m.match(/interface\s+(\w+)/)?.[1];
|
|
95
|
-
if (name && !types.interfaces.includes(name)) {
|
|
96
|
-
types.interfaces.push(name);
|
|
97
|
-
if (!types.examples.interface) {
|
|
98
|
-
const fullMatch = content.match(new RegExp(`export\\s+interface\\s+${name}\\s*\\{[^}]+\\}`, 's'));
|
|
99
|
-
if (fullMatch && fullMatch[0].length < 300) {
|
|
100
|
-
types.examples.interface = { name, code: fullMatch[0], file: relativePath };
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const typeMatches = content.match(/export\s+type\s+(\w+)\s*=/g) || [];
|
|
107
|
-
typeMatches.forEach(m => {
|
|
108
|
-
const name = m.match(/type\s+(\w+)/)?.[1];
|
|
109
|
-
if (name && !types.types.includes(name)) {
|
|
110
|
-
types.types.push(name);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const enumMatches = content.match(/export\s+enum\s+(\w+)/g) || [];
|
|
115
|
-
enumMatches.forEach(m => {
|
|
116
|
-
const name = m.match(/enum\s+(\w+)/)?.[1];
|
|
117
|
-
if (name && !types.enums.includes(name)) {
|
|
118
|
-
types.enums.push(name);
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
} catch {}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return types;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Analyze import graph
|
|
129
|
-
*/
|
|
130
|
-
function analyzeImports(projectPath) {
|
|
131
|
-
const imports = {
|
|
132
|
-
externalPackages: new Set(),
|
|
133
|
-
internalAliases: [],
|
|
134
|
-
importPatterns: [],
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const srcFiles = findFilesRecursive(projectPath, [".ts", ".tsx", ".js", ".jsx"], 4);
|
|
138
|
-
|
|
139
|
-
for (const file of srcFiles.slice(0, 60)) {
|
|
140
|
-
try {
|
|
141
|
-
const content = fs.readFileSync(file, "utf-8");
|
|
142
|
-
const importMatches = content.match(/import\s+.*?\s+from\s+['"]([^'"]+)['"]/g) || [];
|
|
143
|
-
|
|
144
|
-
importMatches.forEach(m => {
|
|
145
|
-
const source = m.match(/from\s+['"]([^'"]+)['"]/)?.[1];
|
|
146
|
-
if (source) {
|
|
147
|
-
if (source.startsWith("@/") || source.startsWith("~/")) {
|
|
148
|
-
if (!imports.internalAliases.includes(source.split("/")[0])) {
|
|
149
|
-
imports.internalAliases.push(source.split("/")[0]);
|
|
150
|
-
}
|
|
151
|
-
} else if (!source.startsWith(".") && !source.startsWith("/")) {
|
|
152
|
-
imports.externalPackages.add(source.split("/")[0]);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
} catch {}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (imports.internalAliases.includes("@/")) {
|
|
160
|
-
imports.importPatterns.push("Uses @/ path alias for src imports");
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
externalPackages: Array.from(imports.externalPackages).slice(0, 30),
|
|
165
|
-
internalAliases: imports.internalAliases,
|
|
166
|
-
importPatterns: imports.importPatterns,
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Calculate file statistics
|
|
172
|
-
*/
|
|
173
|
-
function calculateStats(projectPath) {
|
|
174
|
-
const stats = {
|
|
175
|
-
totalFiles: 0,
|
|
176
|
-
totalLines: 0,
|
|
177
|
-
byExtension: {},
|
|
178
|
-
largestFiles: [],
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
const allFiles = findFilesRecursive(projectPath, [".ts", ".tsx", ".js", ".jsx", ".css", ".scss", ".json"], 6);
|
|
182
|
-
|
|
183
|
-
for (const file of allFiles) {
|
|
184
|
-
try {
|
|
185
|
-
const content = fs.readFileSync(file, "utf-8");
|
|
186
|
-
const lines = content.split("\n").length;
|
|
187
|
-
const ext = path.extname(file);
|
|
188
|
-
const relativePath = path.relative(projectPath, file);
|
|
189
|
-
|
|
190
|
-
stats.totalFiles++;
|
|
191
|
-
stats.totalLines += lines;
|
|
192
|
-
stats.byExtension[ext] = (stats.byExtension[ext] || 0) + 1;
|
|
193
|
-
stats.largestFiles.push({ path: relativePath, lines });
|
|
194
|
-
} catch {}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
stats.largestFiles.sort((a, b) => b.lines - a.lines);
|
|
198
|
-
stats.largestFiles = stats.largestFiles.slice(0, 5);
|
|
199
|
-
|
|
200
|
-
return stats;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Get npm scripts from package.json
|
|
205
|
-
*/
|
|
206
|
-
function getNpmScripts(projectPath) {
|
|
207
|
-
const pkgPath = path.join(projectPath, "package.json");
|
|
208
|
-
if (!fs.existsSync(pkgPath)) return [];
|
|
209
|
-
|
|
210
|
-
try {
|
|
211
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
212
|
-
return Object.entries(pkg.scripts || {}).map(([name, cmd]) => ({
|
|
213
|
-
name,
|
|
214
|
-
command: cmd.length > 50 ? cmd.slice(0, 50) + "..." : cmd,
|
|
215
|
-
}));
|
|
216
|
-
} catch {
|
|
217
|
-
return [];
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Main project analysis function
|
|
223
|
-
*/
|
|
224
|
-
function analyzeProject(projectPath) {
|
|
225
|
-
const analysis = {
|
|
226
|
-
name: path.basename(projectPath),
|
|
227
|
-
framework: null,
|
|
228
|
-
language: null,
|
|
229
|
-
architecture: null,
|
|
230
|
-
hasTypescript: false,
|
|
231
|
-
hasPrisma: false,
|
|
232
|
-
hasNextjs: false,
|
|
233
|
-
hasReact: false,
|
|
234
|
-
hasExpress: false,
|
|
235
|
-
hasTailwind: false,
|
|
236
|
-
directories: [],
|
|
237
|
-
entryPoints: [],
|
|
238
|
-
apiRoutes: [],
|
|
239
|
-
components: [],
|
|
240
|
-
models: [],
|
|
241
|
-
patterns: {},
|
|
242
|
-
conventions: {
|
|
243
|
-
naming: {},
|
|
244
|
-
imports: [],
|
|
245
|
-
patterns: [],
|
|
246
|
-
},
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
// Check package.json
|
|
250
|
-
const pkgPath = path.join(projectPath, "package.json");
|
|
251
|
-
if (fs.existsSync(pkgPath)) {
|
|
252
|
-
try {
|
|
253
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
254
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
255
|
-
|
|
256
|
-
// Full-stack frameworks (check first)
|
|
257
|
-
if (deps.next) {
|
|
258
|
-
analysis.framework = "Next.js";
|
|
259
|
-
analysis.hasNextjs = true;
|
|
260
|
-
} else if (deps.nuxt || deps["@nuxt/core"]) {
|
|
261
|
-
analysis.framework = "Nuxt";
|
|
262
|
-
} else if (deps["@remix-run/node"] || deps["@remix-run/react"]) {
|
|
263
|
-
analysis.framework = "Remix";
|
|
264
|
-
}
|
|
265
|
-
// Frontend frameworks
|
|
266
|
-
else if (deps.react || deps["react-dom"]) {
|
|
267
|
-
// Check for Vite
|
|
268
|
-
if (deps.vite || deps["@vitejs/plugin-react"] ||
|
|
269
|
-
fs.existsSync(path.join(projectPath, "vite.config.ts")) ||
|
|
270
|
-
fs.existsSync(path.join(projectPath, "vite.config.js"))) {
|
|
271
|
-
analysis.framework = "Vite + React";
|
|
272
|
-
} else {
|
|
273
|
-
analysis.framework = "React";
|
|
274
|
-
}
|
|
275
|
-
analysis.hasReact = true;
|
|
276
|
-
} else if (deps.vue) {
|
|
277
|
-
// Check for Vite
|
|
278
|
-
if (deps.vite || deps["@vitejs/plugin-vue"] ||
|
|
279
|
-
fs.existsSync(path.join(projectPath, "vite.config.ts")) ||
|
|
280
|
-
fs.existsSync(path.join(projectPath, "vite.config.js"))) {
|
|
281
|
-
analysis.framework = "Vite + Vue";
|
|
282
|
-
} else {
|
|
283
|
-
analysis.framework = "Vue";
|
|
284
|
-
}
|
|
285
|
-
} else if (deps.svelte || deps["svelte-kit"]) {
|
|
286
|
-
analysis.framework = "Svelte";
|
|
287
|
-
} else if (deps["@angular/core"]) {
|
|
288
|
-
analysis.framework = "Angular";
|
|
289
|
-
}
|
|
290
|
-
// Backend frameworks
|
|
291
|
-
else if (deps.express) {
|
|
292
|
-
analysis.framework = "Express";
|
|
293
|
-
analysis.hasExpress = true;
|
|
294
|
-
} else if (deps.fastify) {
|
|
295
|
-
analysis.framework = "Fastify";
|
|
296
|
-
} else if (deps["@nestjs/core"]) {
|
|
297
|
-
analysis.framework = "NestJS";
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
analysis.hasTypescript = !!deps.typescript;
|
|
301
|
-
analysis.hasPrisma = !!deps.prisma || !!deps["@prisma/client"];
|
|
302
|
-
analysis.hasTailwind = !!deps.tailwindcss;
|
|
303
|
-
analysis.language = analysis.hasTypescript ? "TypeScript" : "JavaScript";
|
|
304
|
-
} catch {}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Detect architecture
|
|
308
|
-
if (fs.existsSync(path.join(projectPath, "src/app"))) {
|
|
309
|
-
analysis.architecture = "Next.js App Router";
|
|
310
|
-
} else if (fs.existsSync(path.join(projectPath, "src/pages"))) {
|
|
311
|
-
analysis.architecture = "Next.js Pages Router";
|
|
312
|
-
} else if (fs.existsSync(path.join(projectPath, "app"))) {
|
|
313
|
-
analysis.architecture = "Next.js App Router (root)";
|
|
314
|
-
} else if (fs.existsSync(path.join(projectPath, "src"))) {
|
|
315
|
-
analysis.architecture = "Standard src/ layout";
|
|
316
|
-
} else {
|
|
317
|
-
analysis.architecture = "Flat structure";
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Find key directories
|
|
321
|
-
const dirsToCheck = ["src", "app", "pages", "components", "lib", "utils", "services", "api", "server", "prisma"];
|
|
322
|
-
for (const dir of dirsToCheck) {
|
|
323
|
-
if (fs.existsSync(path.join(projectPath, dir))) {
|
|
324
|
-
analysis.directories.push(dir);
|
|
325
|
-
}
|
|
326
|
-
if (fs.existsSync(path.join(projectPath, "src", dir))) {
|
|
327
|
-
analysis.directories.push(`src/${dir}`);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Find API routes (including monorepo subdirectories)
|
|
332
|
-
const apiPaths = [
|
|
333
|
-
path.join(projectPath, "src/app/api"),
|
|
334
|
-
path.join(projectPath, "app/api"),
|
|
335
|
-
path.join(projectPath, "pages/api"),
|
|
336
|
-
path.join(projectPath, "server"),
|
|
337
|
-
// Monorepo common paths
|
|
338
|
-
path.join(projectPath, "services", "scanner-dash", "src", "app", "api"),
|
|
339
|
-
path.join(projectPath, "services", "scanner-dash", "src", "pages", "api"),
|
|
340
|
-
path.join(projectPath, "dashboard", "src", "app", "api"),
|
|
341
|
-
path.join(projectPath, "dashboard", "src", "pages", "api"),
|
|
342
|
-
path.join(projectPath, "apps", "web-ui", "src", "app", "api"),
|
|
343
|
-
path.join(projectPath, "apps", "web", "src", "app", "api"),
|
|
344
|
-
];
|
|
345
|
-
|
|
346
|
-
// Also search recursively for API directories
|
|
347
|
-
function findApiDirs(dir, depth = 0) {
|
|
348
|
-
const found = [];
|
|
349
|
-
if (depth > 3) return found; // Limit recursion depth
|
|
350
|
-
|
|
351
|
-
try {
|
|
352
|
-
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
353
|
-
for (const item of items) {
|
|
354
|
-
if (item.isDirectory() && !["node_modules", ".next", "dist", ".git", ".turbo"].includes(item.name)) {
|
|
355
|
-
const itemPath = path.join(dir, item.name);
|
|
356
|
-
|
|
357
|
-
// Check if this is an API directory
|
|
358
|
-
if (item.name === "api" || itemPath.includes("/api/") || itemPath.includes("\\api\\")) {
|
|
359
|
-
found.push(itemPath);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Also check for server/routes directories
|
|
363
|
-
if (item.name === "server" || item.name === "routes") {
|
|
364
|
-
found.push(itemPath);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Recursively search
|
|
368
|
-
found.push(...findApiDirs(itemPath, depth + 1));
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
} catch (err) {
|
|
372
|
-
// Skip directories we can't read
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return found;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Combine hardcoded paths with recursive search
|
|
379
|
-
const allApiPaths = [...apiPaths, ...findApiDirs(projectPath)];
|
|
380
|
-
|
|
381
|
-
for (const apiPath of allApiPaths) {
|
|
382
|
-
if (fs.existsSync(apiPath)) {
|
|
383
|
-
const routes = findFilesRecursive(apiPath, [".ts", ".js", ".tsx", ".jsx"]).map(f =>
|
|
384
|
-
f.replace(apiPath, "").replace(/\\/g, "/").replace(/\/route\.(ts|js|tsx|jsx)$/, "").replace(/\.(ts|js|tsx|jsx)$/, "")
|
|
385
|
-
);
|
|
386
|
-
analysis.apiRoutes.push(...routes);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Remove duplicates
|
|
391
|
-
analysis.apiRoutes = [...new Set(analysis.apiRoutes)];
|
|
392
|
-
|
|
393
|
-
// Find components
|
|
394
|
-
const componentPaths = [
|
|
395
|
-
path.join(projectPath, "src/components"),
|
|
396
|
-
path.join(projectPath, "components"),
|
|
397
|
-
path.join(projectPath, "src/app/components"),
|
|
398
|
-
];
|
|
399
|
-
|
|
400
|
-
for (const compPath of componentPaths) {
|
|
401
|
-
if (fs.existsSync(compPath)) {
|
|
402
|
-
analysis.components = findFilesRecursive(compPath, [".tsx", ".jsx"])
|
|
403
|
-
.map(f => path.basename(f, path.extname(f)))
|
|
404
|
-
.filter(name => /^[A-Z]/.test(name));
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// Detect naming conventions
|
|
410
|
-
if (analysis.components.length > 0) {
|
|
411
|
-
const hasPascalCase = analysis.components.some(c => /^[A-Z][a-z]/.test(c));
|
|
412
|
-
const hasKebabCase = analysis.components.some(c => c.includes("-"));
|
|
413
|
-
analysis.conventions.naming.components = hasPascalCase ? "PascalCase" : hasKebabCase ? "kebab-case" : "mixed";
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Find models/schemas
|
|
417
|
-
const prismaSchema = path.join(projectPath, "prisma/schema.prisma");
|
|
418
|
-
if (fs.existsSync(prismaSchema)) {
|
|
419
|
-
const schema = fs.readFileSync(prismaSchema, "utf-8");
|
|
420
|
-
const modelMatches = schema.match(/model\s+(\w+)\s*{/g) || [];
|
|
421
|
-
analysis.models = modelMatches.map(m => m.replace(/model\s+/, "").replace(/\s*{/, ""));
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Deep pattern detection
|
|
425
|
-
console.log(`${c.dim} Detecting patterns...${c.reset}`);
|
|
426
|
-
analysis.patterns = detectPatterns(projectPath);
|
|
427
|
-
|
|
428
|
-
// Environment variables
|
|
429
|
-
console.log(`${c.dim} Scanning env vars...${c.reset}`);
|
|
430
|
-
analysis.envVars = detectEnvVars(projectPath);
|
|
431
|
-
|
|
432
|
-
// Types and interfaces
|
|
433
|
-
console.log(`${c.dim} Extracting types...${c.reset}`);
|
|
434
|
-
analysis.types = extractTypes(projectPath);
|
|
435
|
-
|
|
436
|
-
// Import analysis
|
|
437
|
-
console.log(`${c.dim} Analyzing imports...${c.reset}`);
|
|
438
|
-
analysis.imports = analyzeImports(projectPath);
|
|
439
|
-
|
|
440
|
-
// File statistics
|
|
441
|
-
console.log(`${c.dim} Calculating stats...${c.reset}`);
|
|
442
|
-
analysis.stats = calculateStats(projectPath);
|
|
443
|
-
|
|
444
|
-
// NPM scripts
|
|
445
|
-
analysis.scripts = getNpmScripts(projectPath);
|
|
446
|
-
|
|
447
|
-
// Monorepo detection
|
|
448
|
-
console.log(`${c.dim} Detecting monorepo...${c.reset}`);
|
|
449
|
-
analysis.monorepo = detectMonorepo(projectPath);
|
|
450
|
-
|
|
451
|
-
return analysis;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
module.exports = {
|
|
455
|
-
analyzeProject,
|
|
456
|
-
findFilesRecursive,
|
|
457
|
-
detectEnvVars,
|
|
458
|
-
extractTypes,
|
|
459
|
-
analyzeImports,
|
|
460
|
-
calculateStats,
|
|
461
|
-
getNpmScripts,
|
|
462
|
-
};
|