@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.
Files changed (68) hide show
  1. package/package.json +9 -1
  2. package/bin/cli-hygiene.js +0 -241
  3. package/bin/guardrail.js +0 -834
  4. package/bin/runners/cli-utils.js +0 -1070
  5. package/bin/runners/context/ai-task-decomposer.js +0 -337
  6. package/bin/runners/context/analyzer.js +0 -462
  7. package/bin/runners/context/api-contracts.js +0 -427
  8. package/bin/runners/context/context-diff.js +0 -342
  9. package/bin/runners/context/context-pruner.js +0 -291
  10. package/bin/runners/context/dependency-graph.js +0 -414
  11. package/bin/runners/context/generators/claude.js +0 -107
  12. package/bin/runners/context/generators/codex.js +0 -108
  13. package/bin/runners/context/generators/copilot.js +0 -119
  14. package/bin/runners/context/generators/cursor.js +0 -514
  15. package/bin/runners/context/generators/mcp.js +0 -151
  16. package/bin/runners/context/generators/windsurf.js +0 -180
  17. package/bin/runners/context/git-context.js +0 -302
  18. package/bin/runners/context/index.js +0 -1042
  19. package/bin/runners/context/insights.js +0 -173
  20. package/bin/runners/context/mcp-server/generate-rules.js +0 -337
  21. package/bin/runners/context/mcp-server/index.js +0 -1176
  22. package/bin/runners/context/mcp-server/package.json +0 -24
  23. package/bin/runners/context/memory.js +0 -200
  24. package/bin/runners/context/monorepo.js +0 -215
  25. package/bin/runners/context/multi-repo-federation.js +0 -404
  26. package/bin/runners/context/patterns.js +0 -253
  27. package/bin/runners/context/proof-context.js +0 -972
  28. package/bin/runners/context/security-scanner.js +0 -303
  29. package/bin/runners/context/semantic-search.js +0 -350
  30. package/bin/runners/context/shared.js +0 -264
  31. package/bin/runners/context/team-conventions.js +0 -310
  32. package/bin/runners/lib/ai-bridge.js +0 -416
  33. package/bin/runners/lib/analysis-core.js +0 -271
  34. package/bin/runners/lib/analyzers.js +0 -541
  35. package/bin/runners/lib/audit-bridge.js +0 -391
  36. package/bin/runners/lib/auth-truth.js +0 -193
  37. package/bin/runners/lib/auth.js +0 -215
  38. package/bin/runners/lib/backup.js +0 -62
  39. package/bin/runners/lib/billing.js +0 -107
  40. package/bin/runners/lib/claims.js +0 -118
  41. package/bin/runners/lib/cli-ui.js +0 -540
  42. package/bin/runners/lib/compliance-bridge-new.js +0 -0
  43. package/bin/runners/lib/compliance-bridge.js +0 -165
  44. package/bin/runners/lib/contracts/auth-contract.js +0 -194
  45. package/bin/runners/lib/contracts/env-contract.js +0 -178
  46. package/bin/runners/lib/contracts/external-contract.js +0 -198
  47. package/bin/runners/lib/contracts/guard.js +0 -168
  48. package/bin/runners/lib/contracts/index.js +0 -89
  49. package/bin/runners/lib/contracts/plan-validator.js +0 -311
  50. package/bin/runners/lib/contracts/route-contract.js +0 -192
  51. package/bin/runners/lib/detect.js +0 -89
  52. package/bin/runners/lib/doctor/autofix.js +0 -254
  53. package/bin/runners/lib/doctor/index.js +0 -37
  54. package/bin/runners/lib/doctor/modules/dependencies.js +0 -325
  55. package/bin/runners/lib/doctor/modules/index.js +0 -46
  56. package/bin/runners/lib/doctor/modules/network.js +0 -250
  57. package/bin/runners/lib/doctor/modules/project.js +0 -312
  58. package/bin/runners/lib/doctor/modules/runtime.js +0 -224
  59. package/bin/runners/lib/doctor/modules/security.js +0 -348
  60. package/bin/runners/lib/doctor/modules/system.js +0 -213
  61. package/bin/runners/lib/doctor/modules/vibecheck.js +0 -394
  62. package/bin/runners/lib/doctor/reporter.js +0 -262
  63. package/bin/runners/lib/doctor/service.js +0 -262
  64. package/bin/runners/lib/doctor/types.js +0 -113
  65. package/bin/runners/lib/doctor/ui.js +0 -263
  66. package/bin/runners/lib/doctor-enhanced.js +0 -233
  67. package/bin/runners/lib/doctor-v2.js +0 -608
  68. package/bin/runners/lib/enforcement.js +0 -72
@@ -1,404 +0,0 @@
1
- /**
2
- * Multi-Repo Context Federation Module
3
- * Unified context across related repositories
4
- */
5
-
6
- const fs = require("fs");
7
- const path = require("path");
8
- const os = require("os");
9
-
10
- const VIBECHECK_HOME = path.join(os.homedir(), ".vibecheck");
11
- const FEDERATION_FILE = path.join(VIBECHECK_HOME, "repo-federation.json");
12
-
13
- /**
14
- * Initialize federation config
15
- */
16
- function initializeFederation() {
17
- if (!fs.existsSync(VIBECHECK_HOME)) {
18
- fs.mkdirSync(VIBECHECK_HOME, { recursive: true });
19
- }
20
-
21
- if (!fs.existsSync(FEDERATION_FILE)) {
22
- fs.writeFileSync(FEDERATION_FILE, JSON.stringify({
23
- version: "1.0.0",
24
- groups: {},
25
- repositories: {},
26
- sharedArtifacts: {},
27
- }, null, 2));
28
- }
29
- }
30
-
31
- /**
32
- * Load federation config
33
- */
34
- function loadFederation() {
35
- initializeFederation();
36
- try {
37
- return JSON.parse(fs.readFileSync(FEDERATION_FILE, "utf-8"));
38
- } catch {
39
- return { groups: {}, repositories: {}, sharedArtifacts: {} };
40
- }
41
- }
42
-
43
- /**
44
- * Save federation config
45
- */
46
- function saveFederation(config) {
47
- initializeFederation();
48
- fs.writeFileSync(FEDERATION_FILE, JSON.stringify(config, null, 2));
49
- }
50
-
51
- /**
52
- * Register repository in federation
53
- */
54
- function registerRepository(repoPath, options = {}) {
55
- const config = loadFederation();
56
- const repoId = path.basename(repoPath);
57
-
58
- // Detect repo type and patterns
59
- const packageJsonPath = path.join(repoPath, "package.json");
60
- let repoInfo = {
61
- id: repoId,
62
- path: repoPath,
63
- type: "unknown",
64
- registered: new Date().toISOString(),
65
- ...options,
66
- };
67
-
68
- if (fs.existsSync(packageJsonPath)) {
69
- try {
70
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
71
- repoInfo.type = pkg.private ? "private" : "public";
72
- repoInfo.name = pkg.name;
73
- repoInfo.description = pkg.description;
74
- repoInfo.dependencies = Object.keys(pkg.dependencies || {});
75
- repoInfo.devDependencies = Object.keys(pkg.devDependencies || {});
76
-
77
- // Detect framework
78
- if (pkg.dependencies?.next) repoInfo.framework = "Next.js";
79
- else if (pkg.dependencies?.react) repoInfo.framework = "React";
80
- else if (pkg.dependencies?.express) repoInfo.framework = "Express";
81
- else if (pkg.dependencies?.vue) repoInfo.framework = "Vue";
82
- } catch {}
83
- }
84
-
85
- config.repositories[repoId] = repoInfo;
86
-
87
- // Auto-add to groups based on patterns
88
- if (repoInfo.framework) {
89
- const groupName = `${repoInfo.framework.toLowerCase()}-projects`;
90
- if (!config.groups[groupName]) {
91
- config.groups[groupName] = {
92
- name: groupName,
93
- description: `${repoInfo.framework} projects`,
94
- repositories: [],
95
- };
96
- }
97
- if (!config.groups[groupName].repositories.includes(repoId)) {
98
- config.groups[groupName].repositories.push(repoId);
99
- }
100
- }
101
-
102
- saveFederation(config);
103
- return repoInfo;
104
- }
105
-
106
- /**
107
- * Create repository group
108
- */
109
- function createGroup(name, description, repoIds = []) {
110
- const config = loadFederation();
111
-
112
- config.groups[name] = {
113
- name,
114
- description,
115
- repositories: repoIds,
116
- created: new Date().toISOString(),
117
- };
118
-
119
- saveFederation(config);
120
- }
121
-
122
- /**
123
- * Get shared artifacts across repositories
124
- */
125
- function getSharedArtifacts(groupName = null) {
126
- const config = loadFederation();
127
- const artifacts = {
128
- components: new Map(),
129
- hooks: new Map(),
130
- utilities: new Map(),
131
- types: new Map(),
132
- patterns: new Map(),
133
- };
134
-
135
- const repos = groupName
136
- ? config.groups[groupName]?.repositories || []
137
- : Object.keys(config.repositories);
138
-
139
- for (const repoId of repos) {
140
- const repo = config.repositories[repoId];
141
- if (!repo) continue;
142
-
143
- // Load context from each repo
144
- const contextFile = path.join(repo.path, ".vibecheck", "context.json");
145
- if (fs.existsSync(contextFile)) {
146
- try {
147
- const context = JSON.parse(fs.readFileSync(contextFile, "utf-8"));
148
-
149
- // Collect components
150
- if (context.structure?.components) {
151
- for (const comp of context.structure.components) {
152
- if (!artifacts.components.has(comp)) {
153
- artifacts.components.set(comp, []);
154
- }
155
- artifacts.components.get(comp).push({
156
- repo: repoId,
157
- path: comp,
158
- framework: context.project?.framework,
159
- });
160
- }
161
- }
162
-
163
- // Collect hooks
164
- if (context.patterns?.hooks) {
165
- for (const hook of context.patterns.hooks) {
166
- if (!artifacts.hooks.has(hook)) {
167
- artifacts.hooks.set(hook, []);
168
- }
169
- artifacts.hooks.get(hook).push({
170
- repo: repoId,
171
- name: hook,
172
- });
173
- }
174
- }
175
-
176
- // Collect patterns
177
- if (context.patterns?.stateManagement) {
178
- const pattern = context.patterns.stateManagement;
179
- if (!artifacts.patterns.has(pattern)) {
180
- artifacts.patterns.set(pattern, []);
181
- }
182
- artifacts.patterns.get(pattern).push(repoId);
183
- }
184
-
185
- // Collect types
186
- if (context.types?.interfaces) {
187
- for (const type of context.types.interfaces) {
188
- if (!artifacts.types.has(type)) {
189
- artifacts.types.set(type, []);
190
- }
191
- artifacts.types.get(type).push({
192
- repo: repoId,
193
- interface: type,
194
- });
195
- }
196
- }
197
- } catch {}
198
- }
199
- }
200
-
201
- // Convert Maps to arrays and filter for shared items
202
- const shared = {};
203
- for (const [key, map] of Object.entries(artifacts)) {
204
- shared[key] = Array.from(map.entries())
205
- .filter(([_, items]) => items.length > 1) // Only keep items shared across repos
206
- .map(([name, items]) => ({
207
- name,
208
- repositories: items,
209
- count: items.length,
210
- }))
211
- .sort((a, b) => b.count - a.count);
212
- }
213
-
214
- return shared;
215
- }
216
-
217
- /**
218
- * Generate federated context
219
- */
220
- function generateFederatedContext(groupName = null, options = {}) {
221
- const { maxTokens = 8000, includePatterns = true } = options;
222
- const config = loadFederation();
223
-
224
- const repos = groupName
225
- ? config.groups[groupName]?.repositories || []
226
- : Object.keys(config.repositories);
227
-
228
- const federated = {
229
- version: "1.0.0",
230
- generated: new Date().toISOString(),
231
- group: groupName,
232
- repositories: repos.map(id => ({
233
- id,
234
- ...config.repositories[id],
235
- })),
236
- sharedArtifacts: getSharedArtifacts(groupName),
237
- context: {
238
- components: [],
239
- hooks: [],
240
- patterns: [],
241
- types: [],
242
- },
243
- stats: {
244
- totalRepos: repos.length,
245
- sharedComponents: 0,
246
- sharedHooks: 0,
247
- sharedPatterns: 0,
248
- },
249
- };
250
-
251
- // Build unified context
252
- let totalTokens = 0;
253
-
254
- // Add shared components
255
- for (const comp of federated.sharedArtifacts.components.slice(0, 20)) {
256
- if (totalTokens > maxTokens * 0.4) break;
257
-
258
- federated.context.components.push({
259
- name: comp.name,
260
- usage: comp.repositories,
261
- example: comp.repositories[0],
262
- });
263
- federated.stats.sharedComponents++;
264
- }
265
-
266
- // Add shared hooks
267
- for (const hook of federated.sharedArtifacts.hooks.slice(0, 15)) {
268
- if (totalTokens > maxTokens * 0.6) break;
269
-
270
- federated.context.hooks.push({
271
- name: hook.name,
272
- repos: hook.repositories,
273
- });
274
- federated.stats.sharedHooks++;
275
- }
276
-
277
- // Add shared patterns
278
- for (const pattern of federated.sharedArtifacts.patterns) {
279
- if (totalTokens > maxTokens * 0.8) break;
280
-
281
- federated.context.patterns.push({
282
- name: pattern.name,
283
- repos: pattern.repositories,
284
- });
285
- federated.stats.sharedPatterns++;
286
- }
287
-
288
- return federated;
289
- }
290
-
291
- /**
292
- * Find related repositories
293
- */
294
- function findRelatedRepositories(repoPath, limit = 5) {
295
- const config = loadFederation();
296
- const repoId = path.basename(repoPath);
297
- const repo = config.repositories[repoId];
298
-
299
- if (!repo) return [];
300
-
301
- const related = [];
302
-
303
- // Find repos with similar dependencies
304
- for (const [id, otherRepo] of Object.entries(config.repositories)) {
305
- if (id === repoId) continue;
306
-
307
- let similarity = 0;
308
-
309
- // Same framework
310
- if (repo.framework === otherRepo.framework) {
311
- similarity += 3;
312
- }
313
-
314
- // Shared dependencies
315
- const sharedDeps = (repo.dependencies || []).filter(d =>
316
- (otherRepo.dependencies || []).includes(d)
317
- ).length;
318
- similarity += sharedDeps * 0.5;
319
-
320
- // Same type (private/public)
321
- if (repo.type === otherRepo.type) {
322
- similarity += 1;
323
- }
324
-
325
- if (similarity > 0) {
326
- related.push({
327
- id,
328
- ...otherRepo,
329
- similarity,
330
- });
331
- }
332
- }
333
-
334
- return related
335
- .sort((a, b) => b.similarity - a.similarity)
336
- .slice(0, limit);
337
- }
338
-
339
- /**
340
- * Sync federation with remote
341
- */
342
- function syncFederation(remoteUrl) {
343
- // In a real implementation, this would sync with a remote service
344
- // For now, just mark as synced
345
- const config = loadFederation();
346
- config.lastSync = new Date().toISOString();
347
- config.remoteUrl = remoteUrl;
348
- saveFederation(config);
349
- }
350
-
351
- /**
352
- * Generate federation report
353
- */
354
- function generateFederationReport(federated) {
355
- let report = `# Multi-Repository Federation Report\n\n`;
356
- report += `Generated: ${new Date(federated.generated).toLocaleString()}\n`;
357
- report += `Group: ${federated.group || "All"}\n`;
358
- report += `Repositories: ${federated.stats.totalRepos}\n\n`;
359
-
360
- // Repositories
361
- report += `## Repositories\n\n`;
362
- for (const repo of federated.repositories) {
363
- report += `- **${repo.name || repo.id}** (${repo.framework || "Unknown"})\n`;
364
- report += ` - Path: ${repo.path}\n`;
365
- report += ` - Dependencies: ${repo.dependencies?.length || 0}\n`;
366
- }
367
-
368
- // Shared artifacts
369
- report += `\n## Shared Artifacts\n\n`;
370
-
371
- if (federated.sharedArtifacts.components.length > 0) {
372
- report += `### Components (${federated.stats.sharedComponents})\n\n`;
373
- for (const comp of federated.sharedArtifacts.components.slice(0, 10)) {
374
- report += `- **${comp.name}** - Used in ${comp.count} repos\n`;
375
- }
376
- }
377
-
378
- if (federated.sharedArtifacts.hooks.length > 0) {
379
- report += `\n### Hooks (${federated.stats.sharedHooks})\n\n`;
380
- for (const hook of federated.sharedArtifacts.hooks.slice(0, 10)) {
381
- report += `- **${hook.name}** - Used in ${hook.count} repos\n`;
382
- }
383
- }
384
-
385
- if (federated.sharedArtifacts.patterns.length > 0) {
386
- report += `\n### Patterns (${federated.stats.sharedPatterns})\n\n`;
387
- for (const pattern of federated.sharedArtifacts.patterns) {
388
- report += `- **${pattern.name}** - Used in ${pattern.count} repos\n`;
389
- }
390
- }
391
-
392
- return report;
393
- }
394
-
395
- module.exports = {
396
- registerRepository,
397
- createGroup,
398
- getSharedArtifacts,
399
- generateFederatedContext,
400
- findRelatedRepositories,
401
- syncFederation,
402
- generateFederationReport,
403
- loadFederation,
404
- };
@@ -1,253 +0,0 @@
1
- /**
2
- * Pattern Detection Module
3
- * Detects code patterns, hooks, state management, styling, and anti-patterns
4
- */
5
-
6
- const fs = require("fs");
7
- const path = require("path");
8
-
9
- /**
10
- * Find files recursively (local helper)
11
- */
12
- function findFiles(dir, extensions, maxDepth = 5, currentDepth = 0) {
13
- if (currentDepth >= maxDepth || !fs.existsSync(dir)) return [];
14
-
15
- const files = [];
16
- try {
17
- const entries = fs.readdirSync(dir, { withFileTypes: true });
18
- for (const entry of entries) {
19
- const fullPath = path.join(dir, entry.name);
20
- if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
21
- files.push(...findFiles(fullPath, extensions, maxDepth, currentDepth + 1));
22
- } else if (entry.isFile() && extensions.some(ext => entry.name.endsWith(ext))) {
23
- files.push(fullPath);
24
- }
25
- }
26
- } catch {}
27
- return files;
28
- }
29
-
30
- /**
31
- * Extract a function example from code
32
- */
33
- function extractFunctionExample(content, functionName) {
34
- const regex = new RegExp(`(export\\s+)?(function|const)\\s+${functionName}[^{]*\\{`, 'g');
35
- const match = regex.exec(content);
36
- if (!match) return null;
37
-
38
- const startIndex = match.index;
39
- let braceCount = 0;
40
- let endIndex = startIndex;
41
- let started = false;
42
-
43
- for (let i = startIndex; i < content.length && i < startIndex + 500; i++) {
44
- if (content[i] === '{') {
45
- braceCount++;
46
- started = true;
47
- } else if (content[i] === '}') {
48
- braceCount--;
49
- }
50
- if (started && braceCount === 0) {
51
- endIndex = i + 1;
52
- break;
53
- }
54
- }
55
-
56
- const example = content.slice(startIndex, endIndex);
57
- return example.length < 400 ? example : example.slice(0, 400) + '\n // ...';
58
- }
59
-
60
- /**
61
- * Detect anti-patterns in code
62
- */
63
- function detectAntiPatterns(content, filePath, antiPatterns) {
64
- // Console.log in production code
65
- if (!filePath.includes('.test.') && !filePath.includes('.spec.') && !filePath.includes('__tests__')) {
66
- if (/console\.(log|error|warn)\(/.test(content)) {
67
- const existing = antiPatterns.find(a => a.type === 'console-log');
68
- if (!existing) {
69
- antiPatterns.push({
70
- type: 'console-log',
71
- severity: 'warning',
72
- message: 'Console statements found in production code',
73
- suggestion: 'Use a proper logger or remove before production',
74
- });
75
- }
76
- }
77
- }
78
-
79
- // Any type usage
80
- if (/:\s*any\b/.test(content)) {
81
- const existing = antiPatterns.find(a => a.type === 'any-type');
82
- if (!existing) {
83
- antiPatterns.push({
84
- type: 'any-type',
85
- severity: 'warning',
86
- message: 'Usage of `any` type detected',
87
- suggestion: 'Use proper TypeScript types or `unknown`',
88
- });
89
- }
90
- }
91
-
92
- // Hardcoded secrets
93
- if (/(api[_-]?key|password|secret|token)\s*[:=]\s*['"][^'"]+['"]/i.test(content)) {
94
- const existing = antiPatterns.find(a => a.type === 'hardcoded-secret');
95
- if (!existing) {
96
- antiPatterns.push({
97
- type: 'hardcoded-secret',
98
- severity: 'error',
99
- message: 'Potential hardcoded secrets detected',
100
- suggestion: 'Use environment variables for sensitive data',
101
- });
102
- }
103
- }
104
-
105
- // Mock data patterns
106
- if (/(jsonplaceholder|reqres\.in|mockapi|faker|fake[A-Z])/i.test(content)) {
107
- const existing = antiPatterns.find(a => a.type === 'mock-data');
108
- if (!existing) {
109
- antiPatterns.push({
110
- type: 'mock-data',
111
- severity: 'warning',
112
- message: 'Mock data or fake APIs detected',
113
- suggestion: 'Replace with real API endpoints before production',
114
- });
115
- }
116
- }
117
-
118
- // TODO/FIXME comments
119
- if (/\/\/\s*(TODO|FIXME|HACK|XXX):/i.test(content)) {
120
- const existing = antiPatterns.find(a => a.type === 'todo-comments');
121
- if (!existing) {
122
- antiPatterns.push({
123
- type: 'todo-comments',
124
- severity: 'info',
125
- message: 'TODO/FIXME comments found',
126
- suggestion: 'Address these items before shipping',
127
- });
128
- }
129
- }
130
- }
131
-
132
- /**
133
- * Deep pattern detection - analyzes actual code patterns
134
- */
135
- function detectPatterns(projectPath) {
136
- const patterns = {
137
- hooks: [],
138
- stateManagement: null,
139
- styling: [],
140
- testing: [],
141
- dataFetching: [],
142
- validation: null,
143
- authentication: null,
144
- codeExamples: {},
145
- antiPatterns: [],
146
- };
147
-
148
- const srcFiles = findFiles(projectPath, [".ts", ".tsx", ".js", ".jsx"], 6);
149
-
150
- for (const file of srcFiles.slice(0, 100)) {
151
- try {
152
- const content = fs.readFileSync(file, "utf-8");
153
- const relativePath = path.relative(projectPath, file);
154
-
155
- // Detect custom hooks
156
- const hookMatches = content.match(/export\s+(function|const)\s+(use[A-Z]\w+)/g);
157
- if (hookMatches) {
158
- hookMatches.forEach(match => {
159
- const hookName = match.match(/use[A-Z]\w+/)?.[0];
160
- if (hookName && !patterns.hooks.includes(hookName)) {
161
- patterns.hooks.push(hookName);
162
- if (!patterns.codeExamples.hooks) {
163
- const hookExample = extractFunctionExample(content, hookName);
164
- if (hookExample) {
165
- patterns.codeExamples.hooks = { name: hookName, code: hookExample, file: relativePath };
166
- }
167
- }
168
- }
169
- });
170
- }
171
-
172
- // Detect state management
173
- if (content.includes("zustand") || content.includes("create(")) {
174
- patterns.stateManagement = "Zustand";
175
- } else if (content.includes("@reduxjs/toolkit") || content.includes("createSlice")) {
176
- patterns.stateManagement = "Redux Toolkit";
177
- } else if (content.includes("recoil") || content.includes("atom(")) {
178
- patterns.stateManagement = "Recoil";
179
- } else if (content.includes("jotai")) {
180
- patterns.stateManagement = "Jotai";
181
- } else if (content.includes("createContext") && !patterns.stateManagement) {
182
- patterns.stateManagement = "React Context";
183
- }
184
-
185
- // Detect styling patterns
186
- if (content.includes("styled-components") || content.includes("styled.")) {
187
- if (!patterns.styling.includes("Styled Components")) patterns.styling.push("Styled Components");
188
- }
189
- if (content.includes("@emotion") || content.includes("css``")) {
190
- if (!patterns.styling.includes("Emotion")) patterns.styling.push("Emotion");
191
- }
192
- if (content.includes("className=") && content.includes("tailwind")) {
193
- if (!patterns.styling.includes("Tailwind CSS")) patterns.styling.push("Tailwind CSS");
194
- }
195
- if (content.includes(".module.css") || content.includes(".module.scss")) {
196
- if (!patterns.styling.includes("CSS Modules")) patterns.styling.push("CSS Modules");
197
- }
198
-
199
- // Detect data fetching patterns
200
- if (content.includes("@tanstack/react-query") || content.includes("useQuery")) {
201
- if (!patterns.dataFetching.includes("TanStack Query")) patterns.dataFetching.push("TanStack Query");
202
- }
203
- if (content.includes("useSWR") || content.includes("swr")) {
204
- if (!patterns.dataFetching.includes("SWR")) patterns.dataFetching.push("SWR");
205
- }
206
- if (content.includes("trpc") || content.includes("createTRPCReact")) {
207
- if (!patterns.dataFetching.includes("tRPC")) patterns.dataFetching.push("tRPC");
208
- }
209
-
210
- // Detect validation
211
- if (content.includes("zod") || content.includes("z.object")) {
212
- patterns.validation = "Zod";
213
- } else if (content.includes("yup") && !patterns.validation) {
214
- patterns.validation = "Yup";
215
- }
216
-
217
- // Detect authentication
218
- if (content.includes("next-auth") || content.includes("NextAuth")) {
219
- patterns.authentication = "NextAuth.js";
220
- } else if (content.includes("clerk") || content.includes("@clerk")) {
221
- patterns.authentication = "Clerk";
222
- } else if (content.includes("supabase") && content.includes("auth")) {
223
- patterns.authentication = "Supabase Auth";
224
- }
225
-
226
- // Detect testing patterns
227
- if (content.includes("@testing-library") || content.includes("render(")) {
228
- if (!patterns.testing.includes("React Testing Library")) patterns.testing.push("React Testing Library");
229
- }
230
- if (content.includes("vitest") || content.includes("vi.mock")) {
231
- if (!patterns.testing.includes("Vitest")) patterns.testing.push("Vitest");
232
- }
233
- if (content.includes("jest") || content.includes("describe(")) {
234
- if (!patterns.testing.includes("Jest")) patterns.testing.push("Jest");
235
- }
236
- if (content.includes("playwright") || content.includes("@playwright")) {
237
- if (!patterns.testing.includes("Playwright")) patterns.testing.push("Playwright");
238
- }
239
-
240
- // Detect anti-patterns
241
- detectAntiPatterns(content, relativePath, patterns.antiPatterns);
242
-
243
- } catch {}
244
- }
245
-
246
- return patterns;
247
- }
248
-
249
- module.exports = {
250
- detectPatterns,
251
- detectAntiPatterns,
252
- extractFunctionExample,
253
- };