@itz4blitz/agentful 1.2.0 → 1.3.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/README.md +28 -1
- package/bin/cli.js +11 -1055
- package/bin/hooks/block-file-creation.js +271 -0
- package/bin/hooks/product-spec-watcher.js +151 -0
- package/lib/index.js +0 -11
- package/lib/init.js +2 -21
- package/lib/parallel-execution.js +235 -0
- package/lib/presets.js +26 -4
- package/package.json +4 -7
- package/template/.claude/agents/architect.md +2 -2
- package/template/.claude/agents/backend.md +17 -30
- package/template/.claude/agents/frontend.md +17 -39
- package/template/.claude/agents/orchestrator.md +63 -4
- package/template/.claude/agents/product-analyzer.md +1 -1
- package/template/.claude/agents/tester.md +16 -29
- package/template/.claude/commands/agentful-generate.md +221 -14
- package/template/.claude/commands/agentful-init.md +621 -0
- package/template/.claude/commands/agentful-product.md +1 -1
- package/template/.claude/commands/agentful-start.md +99 -1
- package/template/.claude/product/EXAMPLES.md +2 -2
- package/template/.claude/product/index.md +1 -1
- package/template/.claude/settings.json +22 -0
- package/template/.claude/skills/research/SKILL.md +432 -0
- package/template/CLAUDE.md +5 -6
- package/template/bin/hooks/architect-drift-detector.js +242 -0
- package/template/bin/hooks/product-spec-watcher.js +151 -0
- package/version.json +1 -1
- package/bin/hooks/post-agent.js +0 -101
- package/bin/hooks/post-feature.js +0 -227
- package/bin/hooks/pre-agent.js +0 -118
- package/bin/hooks/pre-feature.js +0 -138
- package/lib/VALIDATION_README.md +0 -455
- package/lib/ci/claude-action-integration.js +0 -641
- package/lib/ci/index.js +0 -10
- package/lib/core/analyzer.js +0 -497
- package/lib/core/cli.js +0 -141
- package/lib/core/detectors/conventions.js +0 -342
- package/lib/core/detectors/framework.js +0 -276
- package/lib/core/detectors/index.js +0 -15
- package/lib/core/detectors/language.js +0 -199
- package/lib/core/detectors/patterns.js +0 -356
- package/lib/core/generator.js +0 -626
- package/lib/core/index.js +0 -9
- package/lib/core/output-parser.js +0 -458
- package/lib/core/storage.js +0 -515
- package/lib/core/templates.js +0 -556
- package/lib/pipeline/cli.js +0 -423
- package/lib/pipeline/engine.js +0 -928
- package/lib/pipeline/executor.js +0 -440
- package/lib/pipeline/index.js +0 -33
- package/lib/pipeline/integrations.js +0 -559
- package/lib/pipeline/schemas.js +0 -288
- package/lib/remote/client.js +0 -361
- package/lib/server/auth.js +0 -270
- package/lib/server/client-example.js +0 -190
- package/lib/server/executor.js +0 -477
- package/lib/server/index.js +0 -494
- package/lib/update-helpers.js +0 -505
- package/lib/validation.js +0 -460
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Language Detector
|
|
6
|
-
*
|
|
7
|
-
* Detects programming languages used in the project based on:
|
|
8
|
-
* - File extensions
|
|
9
|
-
* - File content analysis
|
|
10
|
-
* - Configuration files
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Language detection rules
|
|
15
|
-
* Maps file extensions to languages
|
|
16
|
-
*/
|
|
17
|
-
const LANGUAGE_EXTENSIONS = {
|
|
18
|
-
'.js': 'JavaScript',
|
|
19
|
-
'.mjs': 'JavaScript',
|
|
20
|
-
'.cjs': 'JavaScript',
|
|
21
|
-
'.jsx': 'JavaScript',
|
|
22
|
-
'.ts': 'TypeScript',
|
|
23
|
-
'.tsx': 'TypeScript',
|
|
24
|
-
'.py': 'Python',
|
|
25
|
-
'.java': 'Java',
|
|
26
|
-
'.go': 'Go',
|
|
27
|
-
'.rs': 'Rust',
|
|
28
|
-
'.rb': 'Ruby',
|
|
29
|
-
'.php': 'PHP',
|
|
30
|
-
'.cs': 'C#',
|
|
31
|
-
'.cpp': 'C++',
|
|
32
|
-
'.c': 'C',
|
|
33
|
-
'.h': 'C',
|
|
34
|
-
'.hpp': 'C++',
|
|
35
|
-
'.swift': 'Swift',
|
|
36
|
-
'.kt': 'Kotlin',
|
|
37
|
-
'.scala': 'Scala',
|
|
38
|
-
'.r': 'R',
|
|
39
|
-
'.jl': 'Julia',
|
|
40
|
-
'.lua': 'Lua',
|
|
41
|
-
'.sh': 'Shell',
|
|
42
|
-
'.bash': 'Shell',
|
|
43
|
-
'.zsh': 'Shell'
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Language detection from config files
|
|
48
|
-
*/
|
|
49
|
-
const CONFIG_FILES = {
|
|
50
|
-
'tsconfig.json': 'TypeScript',
|
|
51
|
-
'jsconfig.json': 'JavaScript',
|
|
52
|
-
'package.json': 'JavaScript',
|
|
53
|
-
'requirements.txt': 'Python',
|
|
54
|
-
'setup.py': 'Python',
|
|
55
|
-
'Pipfile': 'Python',
|
|
56
|
-
'pyproject.toml': 'Python',
|
|
57
|
-
'go.mod': 'Go',
|
|
58
|
-
'Cargo.toml': 'Rust',
|
|
59
|
-
'pom.xml': 'Java',
|
|
60
|
-
'build.gradle': 'Java',
|
|
61
|
-
'Gemfile': 'Ruby',
|
|
62
|
-
'composer.json': 'PHP'
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Detect languages from file list
|
|
67
|
-
*
|
|
68
|
-
* @param {string[]} files - Array of file paths relative to project root
|
|
69
|
-
* @param {string} projectRoot - Absolute path to project root
|
|
70
|
-
* @returns {Promise<Object[]>} Array of detected languages with confidence scores
|
|
71
|
-
*/
|
|
72
|
-
export async function detectLanguages(files, projectRoot) {
|
|
73
|
-
const languageCounts = {};
|
|
74
|
-
const configEvidence = {};
|
|
75
|
-
|
|
76
|
-
// Count files by extension
|
|
77
|
-
for (const file of files) {
|
|
78
|
-
const ext = path.extname(file).toLowerCase();
|
|
79
|
-
const language = LANGUAGE_EXTENSIONS[ext];
|
|
80
|
-
|
|
81
|
-
if (language) {
|
|
82
|
-
if (!languageCounts[language]) {
|
|
83
|
-
languageCounts[language] = { files: 0, extensions: new Set() };
|
|
84
|
-
}
|
|
85
|
-
languageCounts[language].files++;
|
|
86
|
-
languageCounts[language].extensions.add(ext);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Check for config files
|
|
90
|
-
const basename = path.basename(file);
|
|
91
|
-
const configLang = CONFIG_FILES[basename];
|
|
92
|
-
if (configLang) {
|
|
93
|
-
configEvidence[configLang] = (configEvidence[configLang] || 0) + 1;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Calculate confidence scores
|
|
98
|
-
const totalFiles = files.length;
|
|
99
|
-
const languages = [];
|
|
100
|
-
|
|
101
|
-
for (const [language, data] of Object.entries(languageCounts)) {
|
|
102
|
-
const filePercentage = (data.files / totalFiles) * 100;
|
|
103
|
-
const configBonus = configEvidence[language] ? 10 : 0;
|
|
104
|
-
const extensionDiversity = data.extensions.size > 1 ? 5 : 0;
|
|
105
|
-
|
|
106
|
-
let confidence = Math.min(95, Math.round(filePercentage * 0.8 + configBonus + extensionDiversity));
|
|
107
|
-
|
|
108
|
-
// TypeScript bonus: if tsconfig.json exists
|
|
109
|
-
if (language === 'TypeScript' && configEvidence['TypeScript']) {
|
|
110
|
-
confidence = Math.min(95, confidence + 10);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
languages.push({
|
|
114
|
-
name: language,
|
|
115
|
-
confidence,
|
|
116
|
-
files: data.files,
|
|
117
|
-
percentage: Math.round(filePercentage * 10) / 10,
|
|
118
|
-
extensions: Array.from(data.extensions)
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Sort by file count descending
|
|
123
|
-
languages.sort((a, b) => b.files - a.files);
|
|
124
|
-
|
|
125
|
-
return languages;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Detect primary language (highest confidence)
|
|
130
|
-
*
|
|
131
|
-
* @param {Object[]} languages - Array of detected languages
|
|
132
|
-
* @returns {Object|null} Primary language or null if none detected
|
|
133
|
-
*/
|
|
134
|
-
export function getPrimaryLanguage(languages) {
|
|
135
|
-
if (languages.length === 0) return null;
|
|
136
|
-
|
|
137
|
-
// Return language with highest confidence
|
|
138
|
-
return languages.reduce((prev, current) =>
|
|
139
|
-
current.confidence > prev.confidence ? current : prev
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Analyze language-specific patterns in content
|
|
145
|
-
*
|
|
146
|
-
* @param {string} content - File content to analyze
|
|
147
|
-
* @param {string} language - Language to check for
|
|
148
|
-
* @returns {Object} Pattern detection result
|
|
149
|
-
*/
|
|
150
|
-
export function analyzeLanguagePatterns(content, language) {
|
|
151
|
-
const patterns = {
|
|
152
|
-
TypeScript: {
|
|
153
|
-
features: [
|
|
154
|
-
{ name: 'interfaces', regex: /interface\s+\w+/g },
|
|
155
|
-
{ name: 'types', regex: /type\s+\w+\s*=/g },
|
|
156
|
-
{ name: 'generics', regex: /<[A-Z]\w*>/g },
|
|
157
|
-
{ name: 'decorators', regex: /@\w+/g }
|
|
158
|
-
]
|
|
159
|
-
},
|
|
160
|
-
JavaScript: {
|
|
161
|
-
features: [
|
|
162
|
-
{ name: 'async/await', regex: /async\s+function|await\s+/g },
|
|
163
|
-
{ name: 'arrow functions', regex: /=>\s*{|=>\s*\w+/g },
|
|
164
|
-
{ name: 'destructuring', regex: /const\s*{[^}]+}\s*=/g },
|
|
165
|
-
{ name: 'template literals', regex: /`[^`]*\${[^}]*}[^`]*`/g }
|
|
166
|
-
]
|
|
167
|
-
},
|
|
168
|
-
Python: {
|
|
169
|
-
features: [
|
|
170
|
-
{ name: 'classes', regex: /class\s+\w+:/g },
|
|
171
|
-
{ name: 'decorators', regex: /@\w+/g },
|
|
172
|
-
{ name: 'list comprehensions', regex: /\[[^\]]+for\s+\w+\s+in\s+[^\]]+\]/g },
|
|
173
|
-
{ name: 'type hints', regex: /:\s*\w+\s*(?:=|\))/g }
|
|
174
|
-
]
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
const langPatterns = patterns[language];
|
|
179
|
-
if (!langPatterns) {
|
|
180
|
-
return { detected: false };
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const detected = {};
|
|
184
|
-
for (const { name, regex } of langPatterns.features) {
|
|
185
|
-
const matches = content.match(regex);
|
|
186
|
-
detected[name] = matches ? matches.length : 0;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
detected: true,
|
|
191
|
-
features: detected
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export default {
|
|
196
|
-
detectLanguages,
|
|
197
|
-
getPrimaryLanguage,
|
|
198
|
-
analyzeLanguagePatterns
|
|
199
|
-
};
|
|
@@ -1,356 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs/promises';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Pattern Detector
|
|
6
|
-
*
|
|
7
|
-
* Detects code patterns and architectural styles:
|
|
8
|
-
* - Component patterns (React, Vue, etc.)
|
|
9
|
-
* - API patterns (REST, GraphQL, RPC)
|
|
10
|
-
* - Database patterns (SQL, NoSQL, ORM)
|
|
11
|
-
* - Test patterns (unit, integration, e2e)
|
|
12
|
-
* - Authentication patterns (JWT, OAuth, sessions)
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Detect component patterns
|
|
17
|
-
*
|
|
18
|
-
* @param {string[]} files - Array of file paths
|
|
19
|
-
* @param {string} projectRoot - Absolute path to project root
|
|
20
|
-
* @returns {Promise<Object>} Component pattern detection result
|
|
21
|
-
*/
|
|
22
|
-
export async function detectComponentPatterns(files, projectRoot) {
|
|
23
|
-
const componentFiles = files.filter(file =>
|
|
24
|
-
/\.(jsx|tsx|vue|svelte)$/.test(file) ||
|
|
25
|
-
file.includes('/components/') ||
|
|
26
|
-
file.includes('/ui/')
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
if (componentFiles.length === 0) {
|
|
30
|
-
return { detected: false };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const examples = componentFiles.slice(0, 5);
|
|
34
|
-
const patterns = {
|
|
35
|
-
functional: 0,
|
|
36
|
-
class: 0,
|
|
37
|
-
hooks: 0
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Sample a few files to detect patterns
|
|
41
|
-
for (const file of examples.slice(0, 3)) {
|
|
42
|
-
try {
|
|
43
|
-
const filePath = path.join(projectRoot, file);
|
|
44
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
45
|
-
|
|
46
|
-
if (/export\s+default\s+function|const\s+\w+\s*=\s*\(\)/.test(content)) {
|
|
47
|
-
patterns.functional++;
|
|
48
|
-
}
|
|
49
|
-
if (/class\s+\w+\s+extends/.test(content)) {
|
|
50
|
-
patterns.class++;
|
|
51
|
-
}
|
|
52
|
-
if (/use[A-Z]\w+/.test(content)) {
|
|
53
|
-
patterns.hooks++;
|
|
54
|
-
}
|
|
55
|
-
} catch (error) {
|
|
56
|
-
// Skip files that can't be read
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
detected: true,
|
|
62
|
-
count: componentFiles.length,
|
|
63
|
-
examples: examples.map(f => path.basename(f)),
|
|
64
|
-
style: patterns.functional > patterns.class ? 'functional' : 'class',
|
|
65
|
-
usesHooks: patterns.hooks > 0
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Detect API patterns
|
|
71
|
-
*
|
|
72
|
-
* @param {string[]} files - Array of file paths
|
|
73
|
-
* @param {string} projectRoot - Absolute path to project root
|
|
74
|
-
* @returns {Promise<Object>} API pattern detection result
|
|
75
|
-
*/
|
|
76
|
-
export async function detectAPIPatterns(files, projectRoot) {
|
|
77
|
-
const apiFiles = files.filter(file =>
|
|
78
|
-
file.includes('/api/') ||
|
|
79
|
-
file.includes('/routes/') ||
|
|
80
|
-
file.includes('/controllers/') ||
|
|
81
|
-
file.includes('/endpoints/')
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
if (apiFiles.length === 0) {
|
|
85
|
-
return { detected: false };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const patterns = {
|
|
89
|
-
rest: 0,
|
|
90
|
-
graphql: 0,
|
|
91
|
-
rpc: 0
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// Check for GraphQL files
|
|
95
|
-
const hasGraphQL = files.some(file =>
|
|
96
|
-
file.endsWith('.graphql') ||
|
|
97
|
-
file.endsWith('.gql') ||
|
|
98
|
-
file.includes('schema.') ||
|
|
99
|
-
file.includes('resolvers')
|
|
100
|
-
);
|
|
101
|
-
if (hasGraphQL) patterns.graphql++;
|
|
102
|
-
|
|
103
|
-
// Check for RPC/tRPC
|
|
104
|
-
const hasRPC = files.some(file =>
|
|
105
|
-
file.includes('trpc') ||
|
|
106
|
-
file.includes('.proto') ||
|
|
107
|
-
file.includes('grpc')
|
|
108
|
-
);
|
|
109
|
-
if (hasRPC) patterns.rpc++;
|
|
110
|
-
|
|
111
|
-
// Default to REST if API files exist
|
|
112
|
-
if (apiFiles.length > 0) patterns.rest++;
|
|
113
|
-
|
|
114
|
-
// Determine pattern directories
|
|
115
|
-
const apiDirs = new Set();
|
|
116
|
-
for (const file of apiFiles) {
|
|
117
|
-
const dir = path.dirname(file);
|
|
118
|
-
const parts = dir.split('/');
|
|
119
|
-
if (parts.includes('api')) {
|
|
120
|
-
apiDirs.add(parts.slice(0, parts.indexOf('api') + 1).join('/'));
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
detected: true,
|
|
126
|
-
pattern: Array.from(apiDirs)[0] || 'api/',
|
|
127
|
-
count: apiFiles.length,
|
|
128
|
-
type: patterns.graphql > 0 ? 'GraphQL' : patterns.rpc > 0 ? 'RPC' : 'REST',
|
|
129
|
-
examples: apiFiles.slice(0, 3).map(f => path.basename(f))
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Detect database patterns
|
|
135
|
-
*
|
|
136
|
-
* @param {string[]} files - Array of file paths
|
|
137
|
-
* @param {string} projectRoot - Absolute path to project root
|
|
138
|
-
* @returns {Promise<Object>} Database pattern detection result
|
|
139
|
-
*/
|
|
140
|
-
export async function detectDatabasePatterns(files, projectRoot) {
|
|
141
|
-
const dbFiles = files.filter(file =>
|
|
142
|
-
file.includes('/models/') ||
|
|
143
|
-
file.includes('/entities/') ||
|
|
144
|
-
file.includes('/schema') ||
|
|
145
|
-
file.includes('/migrations/') ||
|
|
146
|
-
file.includes('prisma/') ||
|
|
147
|
-
file.includes('database/')
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const patterns = {
|
|
151
|
-
orm: null,
|
|
152
|
-
migrations: false,
|
|
153
|
-
type: 'unknown'
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
// Check for ORM
|
|
157
|
-
if (files.some(file => file.includes('prisma/schema.prisma'))) {
|
|
158
|
-
patterns.orm = 'Prisma';
|
|
159
|
-
patterns.type = 'SQL';
|
|
160
|
-
} else if (files.some(file => file.includes('drizzle.config'))) {
|
|
161
|
-
patterns.orm = 'Drizzle';
|
|
162
|
-
patterns.type = 'SQL';
|
|
163
|
-
} else if (files.some(file => file.includes('typeorm') || file.endsWith('.entity.ts'))) {
|
|
164
|
-
patterns.orm = 'TypeORM';
|
|
165
|
-
patterns.type = 'SQL';
|
|
166
|
-
} else if (files.some(file => file.includes('sequelize'))) {
|
|
167
|
-
patterns.orm = 'Sequelize';
|
|
168
|
-
patterns.type = 'SQL';
|
|
169
|
-
} else if (files.some(file => file.includes('mongoose'))) {
|
|
170
|
-
patterns.orm = 'Mongoose';
|
|
171
|
-
patterns.type = 'NoSQL';
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Check for migrations
|
|
175
|
-
patterns.migrations = files.some(file =>
|
|
176
|
-
file.includes('/migrations/') ||
|
|
177
|
-
file.includes('migrate')
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
if (dbFiles.length === 0 && !patterns.orm) {
|
|
181
|
-
return { detected: false };
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
detected: true,
|
|
186
|
-
orm: patterns.orm,
|
|
187
|
-
type: patterns.type,
|
|
188
|
-
migrations: patterns.migrations,
|
|
189
|
-
files: dbFiles.length,
|
|
190
|
-
examples: dbFiles.slice(0, 3).map(f => path.basename(f))
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Detect test patterns
|
|
196
|
-
*
|
|
197
|
-
* @param {string[]} files - Array of file paths
|
|
198
|
-
* @param {string} projectRoot - Absolute path to project root
|
|
199
|
-
* @returns {Promise<Object>} Test pattern detection result
|
|
200
|
-
*/
|
|
201
|
-
export async function detectTestPatterns(files, projectRoot) {
|
|
202
|
-
const testFiles = files.filter(file =>
|
|
203
|
-
/\.(test|spec)\.(js|ts|jsx|tsx)$/.test(file) ||
|
|
204
|
-
file.includes('/tests/') ||
|
|
205
|
-
file.includes('/__tests__/') ||
|
|
206
|
-
file.includes('/test/')
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
if (testFiles.length === 0) {
|
|
210
|
-
return { detected: false };
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const patterns = {
|
|
214
|
-
unit: 0,
|
|
215
|
-
integration: 0,
|
|
216
|
-
e2e: 0
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
// Categorize tests by directory
|
|
220
|
-
for (const file of testFiles) {
|
|
221
|
-
if (file.includes('/unit/') || file.includes('.unit.')) {
|
|
222
|
-
patterns.unit++;
|
|
223
|
-
} else if (file.includes('/integration/') || file.includes('.integration.')) {
|
|
224
|
-
patterns.integration++;
|
|
225
|
-
} else if (file.includes('/e2e/') || file.includes('.e2e.') || file.includes('cypress/') || file.includes('playwright/')) {
|
|
226
|
-
patterns.e2e++;
|
|
227
|
-
} else {
|
|
228
|
-
// Default to unit tests
|
|
229
|
-
patterns.unit++;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Detect test framework from file content
|
|
234
|
-
let framework = 'unknown';
|
|
235
|
-
try {
|
|
236
|
-
const sampleFile = path.join(projectRoot, testFiles[0]);
|
|
237
|
-
const content = await fs.readFile(sampleFile, 'utf-8');
|
|
238
|
-
|
|
239
|
-
if (content.includes('vitest') || content.includes('describe.only')) {
|
|
240
|
-
framework = 'vitest';
|
|
241
|
-
} else if (content.includes('jest') || content.includes('@jest')) {
|
|
242
|
-
framework = 'jest';
|
|
243
|
-
} else if (content.includes('mocha') || content.includes('chai')) {
|
|
244
|
-
framework = 'mocha';
|
|
245
|
-
} else if (content.includes('cypress')) {
|
|
246
|
-
framework = 'cypress';
|
|
247
|
-
} else if (content.includes('playwright')) {
|
|
248
|
-
framework = 'playwright';
|
|
249
|
-
} else if (/describe\(|it\(|test\(/.test(content)) {
|
|
250
|
-
framework = 'jest'; // Default assumption
|
|
251
|
-
}
|
|
252
|
-
} catch (error) {
|
|
253
|
-
// Can't read file, framework remains unknown
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return {
|
|
257
|
-
detected: true,
|
|
258
|
-
framework,
|
|
259
|
-
count: testFiles.length,
|
|
260
|
-
types: {
|
|
261
|
-
unit: patterns.unit,
|
|
262
|
-
integration: patterns.integration,
|
|
263
|
-
e2e: patterns.e2e
|
|
264
|
-
},
|
|
265
|
-
coverage: files.some(file => file.includes('coverage/')) || files.some(file => file.includes('.coverage'))
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Detect authentication patterns
|
|
271
|
-
*
|
|
272
|
-
* @param {string[]} files - Array of file paths
|
|
273
|
-
* @param {string} projectRoot - Absolute path to project root
|
|
274
|
-
* @returns {Promise<Object>} Auth pattern detection result
|
|
275
|
-
*/
|
|
276
|
-
export async function detectAuthPatterns(files, projectRoot) {
|
|
277
|
-
const authFiles = files.filter(file =>
|
|
278
|
-
file.includes('/auth/') ||
|
|
279
|
-
file.includes('authentication') ||
|
|
280
|
-
file.includes('login') ||
|
|
281
|
-
file.includes('session') ||
|
|
282
|
-
file.includes('jwt') ||
|
|
283
|
-
file.includes('oauth')
|
|
284
|
-
);
|
|
285
|
-
|
|
286
|
-
if (authFiles.length === 0) {
|
|
287
|
-
return { detected: false };
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const patterns = {
|
|
291
|
-
jwt: false,
|
|
292
|
-
oauth: false,
|
|
293
|
-
sessions: false,
|
|
294
|
-
nextAuth: false
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
// Check for specific auth patterns
|
|
298
|
-
patterns.jwt = authFiles.some(file =>
|
|
299
|
-
file.includes('jwt') || file.includes('token')
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
patterns.oauth = authFiles.some(file =>
|
|
303
|
-
file.includes('oauth') || file.includes('passport')
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
patterns.sessions = authFiles.some(file =>
|
|
307
|
-
file.includes('session')
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
patterns.nextAuth = files.some(file =>
|
|
311
|
-
file.includes('next-auth') || file.includes('[...nextauth]')
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
return {
|
|
315
|
-
detected: true,
|
|
316
|
-
methods: Object.entries(patterns)
|
|
317
|
-
.filter(([_, detected]) => detected)
|
|
318
|
-
.map(([method]) => method),
|
|
319
|
-
files: authFiles.length,
|
|
320
|
-
examples: authFiles.slice(0, 3).map(f => path.basename(f))
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Detect all patterns in project
|
|
326
|
-
*
|
|
327
|
-
* @param {string[]} files - Array of file paths
|
|
328
|
-
* @param {string} projectRoot - Absolute path to project root
|
|
329
|
-
* @returns {Promise<Object>} All detected patterns
|
|
330
|
-
*/
|
|
331
|
-
export async function detectPatterns(files, projectRoot) {
|
|
332
|
-
const [components, api, database, tests, auth] = await Promise.all([
|
|
333
|
-
detectComponentPatterns(files, projectRoot),
|
|
334
|
-
detectAPIPatterns(files, projectRoot),
|
|
335
|
-
detectDatabasePatterns(files, projectRoot),
|
|
336
|
-
detectTestPatterns(files, projectRoot),
|
|
337
|
-
detectAuthPatterns(files, projectRoot)
|
|
338
|
-
]);
|
|
339
|
-
|
|
340
|
-
return {
|
|
341
|
-
components,
|
|
342
|
-
api,
|
|
343
|
-
database,
|
|
344
|
-
tests,
|
|
345
|
-
auth
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
export default {
|
|
350
|
-
detectPatterns,
|
|
351
|
-
detectComponentPatterns,
|
|
352
|
-
detectAPIPatterns,
|
|
353
|
-
detectDatabasePatterns,
|
|
354
|
-
detectTestPatterns,
|
|
355
|
-
detectAuthPatterns
|
|
356
|
-
};
|