@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.
Files changed (59) hide show
  1. package/README.md +28 -1
  2. package/bin/cli.js +11 -1055
  3. package/bin/hooks/block-file-creation.js +271 -0
  4. package/bin/hooks/product-spec-watcher.js +151 -0
  5. package/lib/index.js +0 -11
  6. package/lib/init.js +2 -21
  7. package/lib/parallel-execution.js +235 -0
  8. package/lib/presets.js +26 -4
  9. package/package.json +4 -7
  10. package/template/.claude/agents/architect.md +2 -2
  11. package/template/.claude/agents/backend.md +17 -30
  12. package/template/.claude/agents/frontend.md +17 -39
  13. package/template/.claude/agents/orchestrator.md +63 -4
  14. package/template/.claude/agents/product-analyzer.md +1 -1
  15. package/template/.claude/agents/tester.md +16 -29
  16. package/template/.claude/commands/agentful-generate.md +221 -14
  17. package/template/.claude/commands/agentful-init.md +621 -0
  18. package/template/.claude/commands/agentful-product.md +1 -1
  19. package/template/.claude/commands/agentful-start.md +99 -1
  20. package/template/.claude/product/EXAMPLES.md +2 -2
  21. package/template/.claude/product/index.md +1 -1
  22. package/template/.claude/settings.json +22 -0
  23. package/template/.claude/skills/research/SKILL.md +432 -0
  24. package/template/CLAUDE.md +5 -6
  25. package/template/bin/hooks/architect-drift-detector.js +242 -0
  26. package/template/bin/hooks/product-spec-watcher.js +151 -0
  27. package/version.json +1 -1
  28. package/bin/hooks/post-agent.js +0 -101
  29. package/bin/hooks/post-feature.js +0 -227
  30. package/bin/hooks/pre-agent.js +0 -118
  31. package/bin/hooks/pre-feature.js +0 -138
  32. package/lib/VALIDATION_README.md +0 -455
  33. package/lib/ci/claude-action-integration.js +0 -641
  34. package/lib/ci/index.js +0 -10
  35. package/lib/core/analyzer.js +0 -497
  36. package/lib/core/cli.js +0 -141
  37. package/lib/core/detectors/conventions.js +0 -342
  38. package/lib/core/detectors/framework.js +0 -276
  39. package/lib/core/detectors/index.js +0 -15
  40. package/lib/core/detectors/language.js +0 -199
  41. package/lib/core/detectors/patterns.js +0 -356
  42. package/lib/core/generator.js +0 -626
  43. package/lib/core/index.js +0 -9
  44. package/lib/core/output-parser.js +0 -458
  45. package/lib/core/storage.js +0 -515
  46. package/lib/core/templates.js +0 -556
  47. package/lib/pipeline/cli.js +0 -423
  48. package/lib/pipeline/engine.js +0 -928
  49. package/lib/pipeline/executor.js +0 -440
  50. package/lib/pipeline/index.js +0 -33
  51. package/lib/pipeline/integrations.js +0 -559
  52. package/lib/pipeline/schemas.js +0 -288
  53. package/lib/remote/client.js +0 -361
  54. package/lib/server/auth.js +0 -270
  55. package/lib/server/client-example.js +0 -190
  56. package/lib/server/executor.js +0 -477
  57. package/lib/server/index.js +0 -494
  58. package/lib/update-helpers.js +0 -505
  59. package/lib/validation.js +0 -460
@@ -1,342 +0,0 @@
1
- import path from 'path';
2
- import fs from 'fs/promises';
3
-
4
- /**
5
- * Convention Detector
6
- *
7
- * Extracts coding conventions and style guidelines:
8
- * - Naming conventions (camelCase, PascalCase, snake_case, kebab-case)
9
- * - File structure organization (feature-based, layer-based, domain-driven)
10
- * - Code style (indentation, quotes, semicolons)
11
- * - Import style (named, default, relative vs absolute)
12
- */
13
-
14
- /**
15
- * Detect naming conventions from files
16
- *
17
- * @param {string[]} files - Array of file paths
18
- * @returns {Object} Naming convention analysis
19
- */
20
- export function detectNamingConventions(files) {
21
- const patterns = {
22
- camelCase: 0,
23
- PascalCase: 0,
24
- 'snake_case': 0,
25
- 'kebab-case': 0
26
- };
27
-
28
- for (const file of files) {
29
- const basename = path.basename(file, path.extname(file));
30
-
31
- if (/^[a-z][a-zA-Z0-9]*$/.test(basename)) {
32
- patterns.camelCase++;
33
- } else if (/^[A-Z][a-zA-Z0-9]*$/.test(basename)) {
34
- patterns.PascalCase++;
35
- } else if (/^[a-z][a-z0-9_]*$/.test(basename)) {
36
- patterns['snake_case']++;
37
- } else if (/^[a-z][a-z0-9-]*$/.test(basename)) {
38
- patterns['kebab-case']++;
39
- }
40
- }
41
-
42
- // Determine dominant pattern
43
- const sortedPatterns = Object.entries(patterns)
44
- .sort(([, a], [, b]) => b - a);
45
-
46
- const totalFiles = files.length;
47
- const [dominant, count] = sortedPatterns[0];
48
- const confidence = totalFiles > 0 ? Math.round((count / totalFiles) * 100) : 0;
49
-
50
- return {
51
- dominant,
52
- confidence,
53
- distribution: patterns
54
- };
55
- }
56
-
57
- /**
58
- * Detect file structure organization
59
- *
60
- * @param {string[]} files - Array of file paths
61
- * @returns {Object} File structure analysis
62
- */
63
- export function detectFileStructure(files) {
64
- const indicators = {
65
- 'feature-based': 0,
66
- 'layer-based': 0,
67
- 'domain-driven': 0,
68
- 'atomic': 0
69
- };
70
-
71
- // Feature-based indicators
72
- const hasFeatureDirs = files.some(file =>
73
- file.includes('/features/') || file.match(/\/[a-z]+-feature\//)
74
- );
75
- if (hasFeatureDirs) indicators['feature-based'] += 3;
76
-
77
- // Layer-based indicators (controllers, models, views pattern)
78
- const layers = ['controllers', 'models', 'views', 'services', 'repositories'];
79
- const layerCount = layers.filter(layer =>
80
- files.some(file => file.includes(`/${layer}/`))
81
- ).length;
82
- indicators['layer-based'] = layerCount;
83
-
84
- // Domain-driven indicators
85
- const hasDomains = files.some(file =>
86
- file.includes('/domains/') || file.includes('/domain/')
87
- );
88
- const hasAggregates = files.some(file =>
89
- file.includes('/aggregates/') || file.includes('/entities/')
90
- );
91
- if (hasDomains || hasAggregates) {
92
- indicators['domain-driven'] += 2;
93
- }
94
-
95
- // Atomic design indicators
96
- const hasAtoms = files.some(file =>
97
- file.includes('/atoms/') || file.includes('/molecules/') || file.includes('/organisms/')
98
- );
99
- if (hasAtoms) indicators['atomic'] += 3;
100
-
101
- // Determine dominant structure
102
- const sortedIndicators = Object.entries(indicators)
103
- .sort(([, a], [, b]) => b - a);
104
-
105
- const [structure, score] = sortedIndicators[0];
106
-
107
- return {
108
- structure: score > 0 ? structure : 'mixed',
109
- confidence: Math.min(90, score * 20),
110
- indicators
111
- };
112
- }
113
-
114
- /**
115
- * Detect code style conventions from file content
116
- *
117
- * @param {string} content - File content to analyze
118
- * @returns {Object} Code style analysis
119
- */
120
- export function detectCodeStyle(content) {
121
- const style = {
122
- indentation: 'unknown',
123
- quotes: 'unknown',
124
- semicolons: 'unknown',
125
- trailingCommas: 'unknown'
126
- };
127
-
128
- // Detect indentation
129
- const lines = content.split('\n');
130
- let twoSpaces = 0;
131
- let fourSpaces = 0;
132
- let tabs = 0;
133
-
134
- for (const line of lines) {
135
- const match = line.match(/^(\s+)/);
136
- if (match) {
137
- const indent = match[1];
138
- if (indent === ' ') twoSpaces++;
139
- else if (indent === ' ') fourSpaces++;
140
- else if (indent.includes('\t')) tabs++;
141
- }
142
- }
143
-
144
- if (twoSpaces > fourSpaces && twoSpaces > tabs) {
145
- style.indentation = '2 spaces';
146
- } else if (fourSpaces > twoSpaces && fourSpaces > tabs) {
147
- style.indentation = '4 spaces';
148
- } else if (tabs > twoSpaces && tabs > fourSpaces) {
149
- style.indentation = 'tabs';
150
- }
151
-
152
- // Detect quotes
153
- const singleQuotes = (content.match(/'\w+'/g) || []).length;
154
- const doubleQuotes = (content.match(/"\w+"/g) || []).length;
155
- const backticks = (content.match(/`[^`]+`/g) || []).length;
156
-
157
- if (singleQuotes > doubleQuotes && singleQuotes > backticks) {
158
- style.quotes = 'single';
159
- } else if (doubleQuotes > singleQuotes && doubleQuotes > backticks) {
160
- style.quotes = 'double';
161
- } else if (backticks > 0) {
162
- style.quotes = 'template literals preferred';
163
- }
164
-
165
- // Detect semicolons
166
- const withSemicolon = (content.match(/;$/gm) || []).length;
167
- const withoutSemicolon = (content.match(/[^;{}\s]$/gm) || []).length;
168
-
169
- style.semicolons = withSemicolon > withoutSemicolon ? 'required' : 'omitted';
170
-
171
- // Detect trailing commas
172
- const trailingCommas = (content.match(/,\s*[\]}]/g) || []).length;
173
- style.trailingCommas = trailingCommas > 5 ? 'preferred' : 'avoided';
174
-
175
- return style;
176
- }
177
-
178
- /**
179
- * Detect import style conventions
180
- *
181
- * @param {string} content - File content to analyze
182
- * @returns {Object} Import style analysis
183
- */
184
- export function detectImportStyle(content) {
185
- const style = {
186
- namedImports: 0,
187
- defaultImports: 0,
188
- namespaceImports: 0,
189
- relativeImports: 0,
190
- absoluteImports: 0,
191
- aliasedImports: 0
192
- };
193
-
194
- const importRegex = /import\s+(?:{[^}]+}|[\w]+|\*\s+as\s+[\w]+)\s+from\s+['"]([^'"]+)['"]/g;
195
- let match;
196
-
197
- while ((match = importRegex.exec(content)) !== null) {
198
- const importStatement = match[0];
199
- const importPath = match[1];
200
-
201
- // Count import types
202
- if (importStatement.includes('{')) {
203
- style.namedImports++;
204
- } else if (importStatement.includes('* as')) {
205
- style.namespaceImports++;
206
- } else {
207
- style.defaultImports++;
208
- }
209
-
210
- // Count path types
211
- if (importPath.startsWith('.')) {
212
- style.relativeImports++;
213
- } else if (importPath.startsWith('@/') || importPath.startsWith('~/')) {
214
- style.aliasedImports++;
215
- } else {
216
- style.absoluteImports++;
217
- }
218
- }
219
-
220
- // Determine preferences
221
- const total = style.namedImports + style.defaultImports + style.namespaceImports;
222
- const preference = total > 0 ? {
223
- importType: style.namedImports > style.defaultImports ? 'named' : 'default',
224
- pathStyle: style.aliasedImports > style.relativeImports ? 'aliased' :
225
- style.relativeImports > 0 ? 'relative' : 'absolute'
226
- } : null;
227
-
228
- return {
229
- ...style,
230
- preference
231
- };
232
- }
233
-
234
- /**
235
- * Detect ESLint/Prettier configuration
236
- *
237
- * @param {string[]} files - Array of file paths
238
- * @param {string} projectRoot - Absolute path to project root
239
- * @returns {Promise<Object>} Linting configuration
240
- */
241
- export async function detectLintingConfig(files, projectRoot) {
242
- const config = {
243
- eslint: false,
244
- prettier: false,
245
- styleGuide: null
246
- };
247
-
248
- // Check for config files
249
- const hasEslint = files.some(file =>
250
- file.endsWith('.eslintrc') ||
251
- file.endsWith('.eslintrc.js') ||
252
- file.endsWith('.eslintrc.json') ||
253
- file.endsWith('eslint.config.js')
254
- );
255
-
256
- const hasPrettier = files.some(file =>
257
- file.endsWith('.prettierrc') ||
258
- file.endsWith('.prettierrc.js') ||
259
- file.endsWith('.prettierrc.json') ||
260
- file.endsWith('prettier.config.js')
261
- );
262
-
263
- config.eslint = hasEslint;
264
- config.prettier = hasPrettier;
265
-
266
- // Try to detect style guide from package.json
267
- try {
268
- const packageJsonPath = path.join(projectRoot, 'package.json');
269
- const content = await fs.readFile(packageJsonPath, 'utf-8');
270
- const packageJson = JSON.parse(content);
271
-
272
- const allDeps = {
273
- ...packageJson.dependencies,
274
- ...packageJson.devDependencies
275
- };
276
-
277
- if (allDeps['eslint-config-airbnb']) {
278
- config.styleGuide = 'airbnb';
279
- } else if (allDeps['eslint-config-standard']) {
280
- config.styleGuide = 'standard';
281
- } else if (allDeps['eslint-config-google']) {
282
- config.styleGuide = 'google';
283
- } else if (allDeps['@typescript-eslint/eslint-plugin']) {
284
- config.styleGuide = 'typescript';
285
- }
286
- } catch (error) {
287
- // Can't read package.json, skip style guide detection
288
- }
289
-
290
- return config;
291
- }
292
-
293
- /**
294
- * Detect all conventions in project
295
- *
296
- * @param {string[]} files - Array of file paths
297
- * @param {string} projectRoot - Absolute path to project root
298
- * @returns {Promise<Object>} All detected conventions
299
- */
300
- export async function detectConventions(files, projectRoot) {
301
- const naming = detectNamingConventions(files);
302
- const fileStructure = detectFileStructure(files);
303
- const linting = await detectLintingConfig(files, projectRoot);
304
-
305
- // Sample a few files for code style analysis
306
- let codeStyle = null;
307
- let importStyle = null;
308
-
309
- const sampleFiles = files
310
- .filter(file => /\.(js|ts|jsx|tsx)$/.test(file))
311
- .slice(0, 3);
312
-
313
- if (sampleFiles.length > 0) {
314
- try {
315
- const samplePath = path.join(projectRoot, sampleFiles[0]);
316
- const content = await fs.readFile(samplePath, 'utf-8');
317
- codeStyle = detectCodeStyle(content);
318
- importStyle = detectImportStyle(content);
319
- } catch (error) {
320
- // Can't read sample file, skip style detection
321
- }
322
- }
323
-
324
- return {
325
- naming: naming.dominant,
326
- namingConfidence: naming.confidence,
327
- fileStructure: fileStructure.structure,
328
- structureConfidence: fileStructure.confidence,
329
- codeStyle,
330
- importStyle,
331
- linting
332
- };
333
- }
334
-
335
- export default {
336
- detectConventions,
337
- detectNamingConventions,
338
- detectFileStructure,
339
- detectCodeStyle,
340
- detectImportStyle,
341
- detectLintingConfig
342
- };
@@ -1,276 +0,0 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
-
4
- /**
5
- * Framework Detector
6
- *
7
- * Detects frameworks and libraries used in the project based on:
8
- * - package.json dependencies
9
- * - Import statements
10
- * - Configuration files
11
- * - File structure patterns
12
- */
13
-
14
- /**
15
- * Framework detection rules
16
- * Maps package names to frameworks
17
- */
18
- const FRAMEWORK_PACKAGES = {
19
- // JavaScript/TypeScript frameworks
20
- 'next': { name: 'Next.js', type: 'framework', category: 'web' },
21
- 'react': { name: 'React', type: 'library', category: 'ui' },
22
- 'vue': { name: 'Vue', type: 'framework', category: 'web' },
23
- 'nuxt': { name: 'Nuxt', type: 'framework', category: 'web' },
24
- 'angular': { name: 'Angular', type: 'framework', category: 'web' },
25
- 'svelte': { name: 'Svelte', type: 'framework', category: 'ui' },
26
- 'express': { name: 'Express', type: 'framework', category: 'backend' },
27
- 'fastify': { name: 'Fastify', type: 'framework', category: 'backend' },
28
- 'nestjs': { name: 'NestJS', type: 'framework', category: 'backend' },
29
- 'koa': { name: 'Koa', type: 'framework', category: 'backend' },
30
- 'hapi': { name: 'Hapi', type: 'framework', category: 'backend' },
31
-
32
- // Testing frameworks
33
- 'vitest': { name: 'Vitest', type: 'framework', category: 'testing' },
34
- 'jest': { name: 'Jest', type: 'framework', category: 'testing' },
35
- 'mocha': { name: 'Mocha', type: 'framework', category: 'testing' },
36
- 'jasmine': { name: 'Jasmine', type: 'framework', category: 'testing' },
37
- 'cypress': { name: 'Cypress', type: 'framework', category: 'testing' },
38
- 'playwright': { name: 'Playwright', type: 'framework', category: 'testing' },
39
-
40
- // Build tools
41
- 'vite': { name: 'Vite', type: 'tool', category: 'build' },
42
- 'webpack': { name: 'Webpack', type: 'tool', category: 'build' },
43
- 'rollup': { name: 'Rollup', type: 'tool', category: 'build' },
44
- 'esbuild': { name: 'esbuild', type: 'tool', category: 'build' },
45
- 'parcel': { name: 'Parcel', type: 'tool', category: 'build' },
46
-
47
- // Database/ORM
48
- 'prisma': { name: 'Prisma', type: 'library', category: 'database' },
49
- 'typeorm': { name: 'TypeORM', type: 'library', category: 'database' },
50
- 'sequelize': { name: 'Sequelize', type: 'library', category: 'database' },
51
- 'mongoose': { name: 'Mongoose', type: 'library', category: 'database' },
52
- 'drizzle-orm': { name: 'Drizzle', type: 'library', category: 'database' },
53
-
54
- // Styling
55
- 'tailwindcss': { name: 'Tailwind CSS', type: 'library', category: 'styling' },
56
- 'styled-components': { name: 'Styled Components', type: 'library', category: 'styling' },
57
- '@emotion/react': { name: 'Emotion', type: 'library', category: 'styling' },
58
- 'sass': { name: 'Sass', type: 'tool', category: 'styling' }
59
- };
60
-
61
- /**
62
- * Configuration files that indicate specific frameworks
63
- */
64
- const CONFIG_FILES = {
65
- 'next.config.js': 'Next.js',
66
- 'next.config.mjs': 'Next.js',
67
- 'next.config.ts': 'Next.js',
68
- 'nuxt.config.js': 'Nuxt',
69
- 'nuxt.config.ts': 'Nuxt',
70
- 'vue.config.js': 'Vue',
71
- 'angular.json': 'Angular',
72
- 'svelte.config.js': 'Svelte',
73
- 'vite.config.js': 'Vite',
74
- 'vite.config.ts': 'Vite',
75
- 'vitest.config.js': 'Vitest',
76
- 'vitest.config.ts': 'Vitest',
77
- 'jest.config.js': 'Jest',
78
- 'jest.config.ts': 'Jest',
79
- 'playwright.config.js': 'Playwright',
80
- 'playwright.config.ts': 'Playwright',
81
- 'tailwind.config.js': 'Tailwind CSS',
82
- 'tailwind.config.ts': 'Tailwind CSS'
83
- };
84
-
85
- /**
86
- * File structure patterns that indicate frameworks
87
- */
88
- const STRUCTURE_PATTERNS = {
89
- 'Next.js': ['pages/', 'app/', 'public/'],
90
- 'Nuxt': ['pages/', 'layouts/', 'nuxt.config'],
91
- 'Angular': ['src/app/', 'angular.json'],
92
- 'Vue': ['src/components/', 'src/views/'],
93
- 'Express': ['routes/', 'middleware/'],
94
- 'NestJS': ['src/modules/', 'src/controllers/']
95
- };
96
-
97
- /**
98
- * Detect frameworks from package.json
99
- *
100
- * @param {string} projectRoot - Absolute path to project root
101
- * @returns {Promise<Object[]>} Array of detected frameworks
102
- */
103
- export async function detectFromPackageJson(projectRoot) {
104
- const packageJsonPath = path.join(projectRoot, 'package.json');
105
- const frameworks = [];
106
-
107
- try {
108
- const content = await fs.readFile(packageJsonPath, 'utf-8');
109
- const packageJson = JSON.parse(content);
110
-
111
- const allDeps = {
112
- ...packageJson.dependencies,
113
- ...packageJson.devDependencies
114
- };
115
-
116
- for (const [pkg, version] of Object.entries(allDeps)) {
117
- const framework = FRAMEWORK_PACKAGES[pkg];
118
- if (framework) {
119
- frameworks.push({
120
- name: framework.name,
121
- version: version.replace(/[\^~]/g, ''),
122
- type: framework.type,
123
- category: framework.category,
124
- confidence: 95,
125
- source: 'package.json'
126
- });
127
- }
128
- }
129
- } catch (error) {
130
- // package.json not found or invalid - not an error, just no data
131
- }
132
-
133
- return frameworks;
134
- }
135
-
136
- /**
137
- * Detect frameworks from config files
138
- *
139
- * @param {string[]} files - Array of file paths
140
- * @returns {Object[]} Array of detected frameworks
141
- */
142
- export function detectFromConfigFiles(files) {
143
- const frameworks = [];
144
- const seen = new Set();
145
-
146
- for (const file of files) {
147
- const basename = path.basename(file);
148
- const frameworkName = CONFIG_FILES[basename];
149
-
150
- if (frameworkName && !seen.has(frameworkName)) {
151
- seen.add(frameworkName);
152
- frameworks.push({
153
- name: frameworkName,
154
- confidence: 90,
155
- source: 'config file',
156
- configFile: basename
157
- });
158
- }
159
- }
160
-
161
- return frameworks;
162
- }
163
-
164
- /**
165
- * Detect frameworks from file structure
166
- *
167
- * @param {string[]} files - Array of file paths
168
- * @returns {Object[]} Array of detected frameworks
169
- */
170
- export function detectFromStructure(files) {
171
- const frameworks = [];
172
-
173
- for (const [frameworkName, patterns] of Object.entries(STRUCTURE_PATTERNS)) {
174
- let matchCount = 0;
175
-
176
- for (const pattern of patterns) {
177
- const hasPattern = files.some(file =>
178
- file.startsWith(pattern) || file.includes(`/${pattern}`)
179
- );
180
- if (hasPattern) matchCount++;
181
- }
182
-
183
- if (matchCount > 0) {
184
- const confidence = Math.min(85, 50 + (matchCount / patterns.length) * 35);
185
- frameworks.push({
186
- name: frameworkName,
187
- confidence: Math.round(confidence),
188
- source: 'file structure',
189
- patternsMatched: matchCount,
190
- patternsTotal: patterns.length
191
- });
192
- }
193
- }
194
-
195
- return frameworks;
196
- }
197
-
198
- /**
199
- * Detect frameworks from import statements
200
- *
201
- * @param {string} content - File content to analyze
202
- * @returns {string[]} Array of imported framework names
203
- */
204
- export function detectFromImports(content) {
205
- const imports = new Set();
206
- const importRegex = /(?:import|require)\s*\(?['"]([^'"]+)['"]\)?/g;
207
-
208
- let match;
209
- while ((match = importRegex.exec(content)) !== null) {
210
- const importPath = match[1];
211
- const packageName = importPath.startsWith('@')
212
- ? importPath.split('/').slice(0, 2).join('/')
213
- : importPath.split('/')[0];
214
-
215
- if (FRAMEWORK_PACKAGES[packageName]) {
216
- imports.add(FRAMEWORK_PACKAGES[packageName].name);
217
- }
218
- }
219
-
220
- return Array.from(imports);
221
- }
222
-
223
- /**
224
- * Merge and deduplicate framework detections
225
- *
226
- * @param {Object[][]} detectionResults - Multiple arrays of detected frameworks
227
- * @returns {Object[]} Merged and deduplicated frameworks with highest confidence
228
- */
229
- export function mergeFrameworks(...detectionResults) {
230
- const frameworkMap = new Map();
231
-
232
- for (const results of detectionResults) {
233
- for (const framework of results) {
234
- const existing = frameworkMap.get(framework.name);
235
-
236
- if (!existing || framework.confidence > existing.confidence) {
237
- frameworkMap.set(framework.name, framework);
238
- } else if (existing) {
239
- // Merge sources
240
- existing.sources = existing.sources || [existing.source];
241
- if (!existing.sources.includes(framework.source)) {
242
- existing.sources.push(framework.source);
243
- }
244
- // Boost confidence if detected from multiple sources
245
- existing.confidence = Math.min(98, existing.confidence + 5);
246
- }
247
- }
248
- }
249
-
250
- return Array.from(frameworkMap.values())
251
- .sort((a, b) => b.confidence - a.confidence);
252
- }
253
-
254
- /**
255
- * Detect all frameworks in project
256
- *
257
- * @param {string[]} files - Array of file paths
258
- * @param {string} projectRoot - Absolute path to project root
259
- * @returns {Promise<Object[]>} Array of detected frameworks
260
- */
261
- export async function detectFrameworks(files, projectRoot) {
262
- const fromPackage = await detectFromPackageJson(projectRoot);
263
- const fromConfig = detectFromConfigFiles(files);
264
- const fromStructure = detectFromStructure(files);
265
-
266
- return mergeFrameworks(fromPackage, fromConfig, fromStructure);
267
- }
268
-
269
- export default {
270
- detectFrameworks,
271
- detectFromPackageJson,
272
- detectFromConfigFiles,
273
- detectFromStructure,
274
- detectFromImports,
275
- mergeFrameworks
276
- };
@@ -1,15 +0,0 @@
1
- /**
2
- * Detector Module Exports
3
- *
4
- * Centralized exports for all detector modules
5
- */
6
-
7
- export * from './language.js';
8
- export * from './framework.js';
9
- export * from './patterns.js';
10
- export * from './conventions.js';
11
-
12
- export { default as languageDetector } from './language.js';
13
- export { default as frameworkDetector } from './framework.js';
14
- export { default as patternDetector } from './patterns.js';
15
- export { default as conventionDetector } from './conventions.js';