@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
package/lib/core/analyzer.js
DELETED
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { EventEmitter } from 'events';
|
|
4
|
-
import {
|
|
5
|
-
detectLanguages,
|
|
6
|
-
getPrimaryLanguage
|
|
7
|
-
} from './detectors/language.js';
|
|
8
|
-
import { detectFrameworks } from './detectors/framework.js';
|
|
9
|
-
import { detectPatterns } from './detectors/patterns.js';
|
|
10
|
-
import { detectConventions } from './detectors/conventions.js';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Codebase Analyzer
|
|
14
|
-
*
|
|
15
|
-
* Analyzes project codebase to detect:
|
|
16
|
-
* - Programming languages
|
|
17
|
-
* - Frameworks and libraries
|
|
18
|
-
* - Architecture patterns
|
|
19
|
-
* - Code conventions
|
|
20
|
-
* - Project structure
|
|
21
|
-
*
|
|
22
|
-
* Outputs structured analysis to .agentful/architecture.json
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Default ignore patterns for file scanning
|
|
27
|
-
*/
|
|
28
|
-
const DEFAULT_IGNORE_PATTERNS = [
|
|
29
|
-
'node_modules',
|
|
30
|
-
'.git',
|
|
31
|
-
'.agentful',
|
|
32
|
-
'dist',
|
|
33
|
-
'build',
|
|
34
|
-
'coverage',
|
|
35
|
-
'.next',
|
|
36
|
-
'.nuxt',
|
|
37
|
-
'.vscode',
|
|
38
|
-
'.idea',
|
|
39
|
-
'*.log',
|
|
40
|
-
'*.lock',
|
|
41
|
-
'package-lock.json',
|
|
42
|
-
'yarn.lock',
|
|
43
|
-
'pnpm-lock.yaml'
|
|
44
|
-
];
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Codebase Analyzer Class
|
|
48
|
-
*/
|
|
49
|
-
export class CodebaseAnalyzer extends EventEmitter {
|
|
50
|
-
constructor(options = {}) {
|
|
51
|
-
super();
|
|
52
|
-
|
|
53
|
-
this.options = {
|
|
54
|
-
projectRoot: options.projectRoot || process.cwd(),
|
|
55
|
-
outputPath: options.outputPath || '.agentful/architecture.json',
|
|
56
|
-
ignorePatterns: options.ignorePatterns || DEFAULT_IGNORE_PATTERNS,
|
|
57
|
-
maxFiles: options.maxFiles || 5000,
|
|
58
|
-
timeout: options.timeout || 30000,
|
|
59
|
-
...options
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
this.startTime = null;
|
|
63
|
-
this.analysis = null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Run full codebase analysis
|
|
68
|
-
*
|
|
69
|
-
* @returns {Promise<Object>} Analysis results
|
|
70
|
-
*/
|
|
71
|
-
async analyze() {
|
|
72
|
-
this.startTime = Date.now();
|
|
73
|
-
this.emit('start', { projectRoot: this.options.projectRoot });
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
// Step 1: Scan project files
|
|
77
|
-
this.emit('progress', { stage: 'scanning', progress: 0 });
|
|
78
|
-
const files = await this.scanFiles();
|
|
79
|
-
this.emit('progress', { stage: 'scanning', progress: 100, fileCount: files.length });
|
|
80
|
-
|
|
81
|
-
// Step 2: Detect languages
|
|
82
|
-
this.emit('progress', { stage: 'languages', progress: 0 });
|
|
83
|
-
const languages = await detectLanguages(files, this.options.projectRoot);
|
|
84
|
-
this.emit('progress', { stage: 'languages', progress: 100, count: languages.length });
|
|
85
|
-
|
|
86
|
-
// Step 3: Detect frameworks
|
|
87
|
-
this.emit('progress', { stage: 'frameworks', progress: 0 });
|
|
88
|
-
const frameworks = await detectFrameworks(files, this.options.projectRoot);
|
|
89
|
-
this.emit('progress', { stage: 'frameworks', progress: 100, count: frameworks.length });
|
|
90
|
-
|
|
91
|
-
// Step 4: Detect patterns
|
|
92
|
-
this.emit('progress', { stage: 'patterns', progress: 0 });
|
|
93
|
-
const patterns = await detectPatterns(files, this.options.projectRoot);
|
|
94
|
-
this.emit('progress', { stage: 'patterns', progress: 100 });
|
|
95
|
-
|
|
96
|
-
// Step 5: Detect conventions
|
|
97
|
-
this.emit('progress', { stage: 'conventions', progress: 0 });
|
|
98
|
-
const conventions = await detectConventions(files, this.options.projectRoot);
|
|
99
|
-
this.emit('progress', { stage: 'conventions', progress: 100 });
|
|
100
|
-
|
|
101
|
-
// Build analysis result
|
|
102
|
-
this.analysis = this.buildAnalysisResult(languages, frameworks, patterns, conventions, files);
|
|
103
|
-
|
|
104
|
-
// Step 6: Write to file
|
|
105
|
-
this.emit('progress', { stage: 'writing', progress: 0 });
|
|
106
|
-
await this.writeAnalysis(this.analysis);
|
|
107
|
-
this.emit('progress', { stage: 'writing', progress: 100 });
|
|
108
|
-
|
|
109
|
-
const duration = Date.now() - this.startTime;
|
|
110
|
-
this.emit('complete', { duration, analysis: this.analysis });
|
|
111
|
-
|
|
112
|
-
return this.analysis;
|
|
113
|
-
} catch (error) {
|
|
114
|
-
this.emit('error', error);
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Scan project files
|
|
121
|
-
*
|
|
122
|
-
* @returns {Promise<string[]>} Array of relative file paths
|
|
123
|
-
*/
|
|
124
|
-
async scanFiles() {
|
|
125
|
-
const files = [];
|
|
126
|
-
const ignorePatterns = this.options.ignorePatterns;
|
|
127
|
-
|
|
128
|
-
const shouldIgnore = (filePath) => {
|
|
129
|
-
return ignorePatterns.some(pattern => {
|
|
130
|
-
if (pattern.includes('*')) {
|
|
131
|
-
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
|
|
132
|
-
return regex.test(filePath);
|
|
133
|
-
}
|
|
134
|
-
return filePath.includes(pattern);
|
|
135
|
-
});
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const scanDir = async (dir, basePath = '') => {
|
|
139
|
-
if (files.length >= this.options.maxFiles) return;
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
143
|
-
|
|
144
|
-
// Separate directories and files
|
|
145
|
-
const dirs = [];
|
|
146
|
-
const fileEntries = [];
|
|
147
|
-
|
|
148
|
-
for (const entry of entries) {
|
|
149
|
-
const relativePath = path.join(basePath, entry.name);
|
|
150
|
-
if (shouldIgnore(relativePath)) continue;
|
|
151
|
-
|
|
152
|
-
const fullPath = path.join(dir, entry.name);
|
|
153
|
-
|
|
154
|
-
if (entry.isDirectory()) {
|
|
155
|
-
dirs.push({ fullPath, relativePath });
|
|
156
|
-
} else if (entry.isFile()) {
|
|
157
|
-
fileEntries.push(relativePath);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Add files
|
|
162
|
-
files.push(...fileEntries);
|
|
163
|
-
|
|
164
|
-
if (files.length >= this.options.maxFiles) {
|
|
165
|
-
this.emit('warning', {
|
|
166
|
-
message: `Max file limit reached (${this.options.maxFiles})`,
|
|
167
|
-
partial: true
|
|
168
|
-
});
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Scan subdirectories in parallel
|
|
173
|
-
if (dirs.length > 0) {
|
|
174
|
-
await Promise.all(
|
|
175
|
-
dirs.map(({ fullPath, relativePath }) =>
|
|
176
|
-
scanDir(fullPath, relativePath)
|
|
177
|
-
)
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
} catch (error) {
|
|
181
|
-
// Skip directories we can't read
|
|
182
|
-
this.emit('warning', {
|
|
183
|
-
message: `Cannot read directory: ${dir}`,
|
|
184
|
-
error: error.message
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
await scanDir(this.options.projectRoot);
|
|
190
|
-
|
|
191
|
-
return files;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Build structured analysis result
|
|
196
|
-
*
|
|
197
|
-
* @param {Object[]} languages - Detected languages
|
|
198
|
-
* @param {Object[]} frameworks - Detected frameworks
|
|
199
|
-
* @param {Object} patterns - Detected patterns
|
|
200
|
-
* @param {Object} conventions - Detected conventions
|
|
201
|
-
* @param {string[]} files - Scanned files
|
|
202
|
-
* @returns {Object} Structured analysis result
|
|
203
|
-
*/
|
|
204
|
-
buildAnalysisResult(languages, frameworks, patterns, conventions, files) {
|
|
205
|
-
const primaryLanguage = getPrimaryLanguage(languages);
|
|
206
|
-
const isNewProject = files.length < 10;
|
|
207
|
-
|
|
208
|
-
// Calculate overall confidence score
|
|
209
|
-
const confidence = this.calculateConfidence(languages, frameworks, patterns, isNewProject);
|
|
210
|
-
|
|
211
|
-
return {
|
|
212
|
-
version: '1.0.0',
|
|
213
|
-
analyzedAt: new Date().toISOString(),
|
|
214
|
-
duration: Date.now() - this.startTime,
|
|
215
|
-
projectRoot: this.options.projectRoot,
|
|
216
|
-
fileCount: files.length,
|
|
217
|
-
isNewProject,
|
|
218
|
-
confidence,
|
|
219
|
-
|
|
220
|
-
languages: languages.map(lang => ({
|
|
221
|
-
name: lang.name,
|
|
222
|
-
confidence: lang.confidence,
|
|
223
|
-
files: lang.files,
|
|
224
|
-
percentage: lang.percentage,
|
|
225
|
-
extensions: lang.extensions
|
|
226
|
-
})),
|
|
227
|
-
|
|
228
|
-
primaryLanguage: primaryLanguage ? primaryLanguage.name : null,
|
|
229
|
-
|
|
230
|
-
frameworks: frameworks.map(fw => ({
|
|
231
|
-
name: fw.name,
|
|
232
|
-
version: fw.version || 'unknown',
|
|
233
|
-
type: fw.type,
|
|
234
|
-
category: fw.category,
|
|
235
|
-
confidence: fw.confidence,
|
|
236
|
-
source: fw.source
|
|
237
|
-
})),
|
|
238
|
-
|
|
239
|
-
patterns: {
|
|
240
|
-
components: patterns.components || { detected: false },
|
|
241
|
-
api: patterns.api || { detected: false },
|
|
242
|
-
database: patterns.database || { detected: false },
|
|
243
|
-
tests: patterns.tests || { detected: false },
|
|
244
|
-
auth: patterns.auth || { detected: false }
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
conventions: {
|
|
248
|
-
naming: conventions.naming,
|
|
249
|
-
namingConfidence: conventions.namingConfidence,
|
|
250
|
-
fileStructure: conventions.fileStructure,
|
|
251
|
-
structureConfidence: conventions.structureConfidence,
|
|
252
|
-
codeStyle: conventions.codeStyle,
|
|
253
|
-
importStyle: conventions.importStyle,
|
|
254
|
-
linting: conventions.linting
|
|
255
|
-
},
|
|
256
|
-
|
|
257
|
-
recommendations: this.generateRecommendations(
|
|
258
|
-
languages,
|
|
259
|
-
frameworks,
|
|
260
|
-
patterns,
|
|
261
|
-
isNewProject,
|
|
262
|
-
confidence
|
|
263
|
-
)
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Calculate overall confidence score
|
|
269
|
-
*
|
|
270
|
-
* @param {Object[]} languages - Detected languages
|
|
271
|
-
* @param {Object[]} frameworks - Detected frameworks
|
|
272
|
-
* @param {Object} patterns - Detected patterns
|
|
273
|
-
* @param {boolean} isNewProject - Whether this is a new project
|
|
274
|
-
* @returns {number} Confidence score 0-100
|
|
275
|
-
*/
|
|
276
|
-
calculateConfidence(languages, frameworks, patterns, isNewProject) {
|
|
277
|
-
if (isNewProject) {
|
|
278
|
-
// New projects have low confidence by design
|
|
279
|
-
return 30;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
let score = 0;
|
|
283
|
-
|
|
284
|
-
// Language detection confidence (40 points max)
|
|
285
|
-
if (languages.length > 0) {
|
|
286
|
-
const avgLangConfidence = languages.reduce((sum, l) => sum + l.confidence, 0) / languages.length;
|
|
287
|
-
score += (avgLangConfidence / 100) * 40;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Framework detection confidence (30 points max)
|
|
291
|
-
if (frameworks.length > 0) {
|
|
292
|
-
const avgFwConfidence = frameworks.reduce((sum, f) => sum + f.confidence, 0) / frameworks.length;
|
|
293
|
-
score += (avgFwConfidence / 100) * 30;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Pattern detection confidence (30 points max)
|
|
297
|
-
const detectedPatterns = Object.values(patterns).filter(p => p.detected).length;
|
|
298
|
-
const totalPatterns = Object.keys(patterns).length;
|
|
299
|
-
score += (detectedPatterns / totalPatterns) * 30;
|
|
300
|
-
|
|
301
|
-
return Math.round(score);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Generate recommendations based on analysis
|
|
306
|
-
*
|
|
307
|
-
* @param {Object[]} languages - Detected languages
|
|
308
|
-
* @param {Object[]} frameworks - Detected frameworks
|
|
309
|
-
* @param {Object} patterns - Detected patterns
|
|
310
|
-
* @param {boolean} isNewProject - Whether this is a new project
|
|
311
|
-
* @param {number} confidence - Overall confidence score
|
|
312
|
-
* @returns {Object[]} Array of recommendations
|
|
313
|
-
*/
|
|
314
|
-
generateRecommendations(languages, frameworks, patterns, isNewProject, confidence) {
|
|
315
|
-
const recommendations = [];
|
|
316
|
-
|
|
317
|
-
// Low confidence warning
|
|
318
|
-
if (confidence < 50) {
|
|
319
|
-
recommendations.push({
|
|
320
|
-
type: 'warning',
|
|
321
|
-
priority: 'high',
|
|
322
|
-
message: 'Low detection confidence. Consider manually specifying tech stack in product spec.',
|
|
323
|
-
action: 'Add explicit tech stack section to .claude/product/index.md'
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// New project recommendations
|
|
328
|
-
if (isNewProject) {
|
|
329
|
-
recommendations.push({
|
|
330
|
-
type: 'info',
|
|
331
|
-
priority: 'medium',
|
|
332
|
-
message: 'New project detected. Analyzer will use default configurations.',
|
|
333
|
-
action: 'Continue with /agentful-start to generate specialized agents'
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// No tests detected
|
|
338
|
-
if (!patterns.tests || !patterns.tests.detected) {
|
|
339
|
-
recommendations.push({
|
|
340
|
-
type: 'suggestion',
|
|
341
|
-
priority: 'medium',
|
|
342
|
-
message: 'No test files detected. Consider setting up testing framework.',
|
|
343
|
-
action: 'Add vitest, jest, or other testing framework'
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// No linting detected
|
|
348
|
-
if (!patterns.tests?.detected) {
|
|
349
|
-
recommendations.push({
|
|
350
|
-
type: 'suggestion',
|
|
351
|
-
priority: 'low',
|
|
352
|
-
message: 'No linting configuration detected.',
|
|
353
|
-
action: 'Consider adding ESLint and Prettier for code quality'
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Multiple languages with similar confidence
|
|
358
|
-
if (languages.length > 1) {
|
|
359
|
-
const topTwo = languages.slice(0, 2);
|
|
360
|
-
const diff = topTwo[0].confidence - topTwo[1].confidence;
|
|
361
|
-
if (diff < 10) {
|
|
362
|
-
recommendations.push({
|
|
363
|
-
type: 'info',
|
|
364
|
-
priority: 'low',
|
|
365
|
-
message: `Multiple primary languages detected: ${topTwo.map(l => l.name).join(', ')}`,
|
|
366
|
-
action: 'Agents will be generated for both languages'
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
return recommendations;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Write analysis to file
|
|
376
|
-
*
|
|
377
|
-
* @param {Object} analysis - Analysis results
|
|
378
|
-
* @returns {Promise<void>}
|
|
379
|
-
*/
|
|
380
|
-
async writeAnalysis(analysis) {
|
|
381
|
-
const outputPath = path.join(this.options.projectRoot, this.options.outputPath);
|
|
382
|
-
const outputDir = path.dirname(outputPath);
|
|
383
|
-
|
|
384
|
-
// Ensure output directory exists
|
|
385
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
386
|
-
|
|
387
|
-
// Write analysis with pretty formatting
|
|
388
|
-
await fs.writeFile(outputPath, JSON.stringify(analysis, null, 2), 'utf-8');
|
|
389
|
-
|
|
390
|
-
this.emit('written', { path: outputPath });
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Read existing analysis from file
|
|
395
|
-
*
|
|
396
|
-
* @returns {Promise<Object|null>} Existing analysis or null if not found
|
|
397
|
-
*/
|
|
398
|
-
async readExistingAnalysis() {
|
|
399
|
-
const outputPath = path.join(this.options.projectRoot, this.options.outputPath);
|
|
400
|
-
|
|
401
|
-
try {
|
|
402
|
-
const content = await fs.readFile(outputPath, 'utf-8');
|
|
403
|
-
return JSON.parse(content);
|
|
404
|
-
} catch (error) {
|
|
405
|
-
return null;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Check if analysis is stale (older than 24 hours or file count changed significantly)
|
|
411
|
-
*
|
|
412
|
-
* @param {Object} existingAnalysis - Existing analysis to check
|
|
413
|
-
* @returns {Promise<boolean>} True if stale, false if fresh
|
|
414
|
-
*/
|
|
415
|
-
async isAnalysisStale(existingAnalysis) {
|
|
416
|
-
if (!existingAnalysis) return true;
|
|
417
|
-
|
|
418
|
-
// Check age (24 hours)
|
|
419
|
-
const age = Date.now() - new Date(existingAnalysis.analyzedAt).getTime();
|
|
420
|
-
const maxAge = 24 * 60 * 60 * 1000; // 24 hours
|
|
421
|
-
if (age > maxAge) return true;
|
|
422
|
-
|
|
423
|
-
// Check if file count changed significantly (>10%)
|
|
424
|
-
const currentFiles = await this.scanFiles();
|
|
425
|
-
const fileCountDiff = Math.abs(currentFiles.length - existingAnalysis.fileCount);
|
|
426
|
-
const fileCountChangePercent = (fileCountDiff / existingAnalysis.fileCount) * 100;
|
|
427
|
-
if (fileCountChangePercent > 10) return true;
|
|
428
|
-
|
|
429
|
-
return false;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* Analyze with caching - only re-analyze if stale
|
|
434
|
-
*
|
|
435
|
-
* @param {boolean} force - Force re-analysis even if fresh
|
|
436
|
-
* @returns {Promise<Object>} Analysis results
|
|
437
|
-
*/
|
|
438
|
-
async analyzeWithCache(force = false) {
|
|
439
|
-
if (!force) {
|
|
440
|
-
const existing = await this.readExistingAnalysis();
|
|
441
|
-
if (existing) {
|
|
442
|
-
const isStale = await this.isAnalysisStale(existing);
|
|
443
|
-
if (!isStale) {
|
|
444
|
-
this.emit('cached', { analysis: existing });
|
|
445
|
-
return existing;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
return this.analyze();
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Create analyzer instance
|
|
456
|
-
*
|
|
457
|
-
* @param {Object} options - Analyzer options
|
|
458
|
-
* @returns {CodebaseAnalyzer} Analyzer instance
|
|
459
|
-
*/
|
|
460
|
-
export function createAnalyzer(options = {}) {
|
|
461
|
-
return new CodebaseAnalyzer(options);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* Quick analyze function - analyze and return results
|
|
466
|
-
*
|
|
467
|
-
* @param {Object} options - Analyzer options
|
|
468
|
-
* @returns {Promise<Object>} Analysis results
|
|
469
|
-
*/
|
|
470
|
-
export async function analyzeCodebase(options = {}) {
|
|
471
|
-
const analyzer = new CodebaseAnalyzer(options);
|
|
472
|
-
return analyzer.analyze();
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* Analyze with progress logging
|
|
477
|
-
*
|
|
478
|
-
* @param {Object} options - Analyzer options
|
|
479
|
-
* @param {Function} onProgress - Progress callback
|
|
480
|
-
* @returns {Promise<Object>} Analysis results
|
|
481
|
-
*/
|
|
482
|
-
export async function analyzeWithProgress(options = {}, onProgress = null) {
|
|
483
|
-
const analyzer = new CodebaseAnalyzer(options);
|
|
484
|
-
|
|
485
|
-
if (onProgress) {
|
|
486
|
-
analyzer.on('progress', onProgress);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
return analyzer.analyze();
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
export default {
|
|
493
|
-
CodebaseAnalyzer,
|
|
494
|
-
createAnalyzer,
|
|
495
|
-
analyzeCodebase,
|
|
496
|
-
analyzeWithProgress
|
|
497
|
-
};
|
package/lib/core/cli.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Codebase Analyzer CLI
|
|
5
|
-
*
|
|
6
|
-
* Simple CLI for testing the analyzer
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* node lib/core/cli.js [options]
|
|
10
|
-
*
|
|
11
|
-
* Options:
|
|
12
|
-
* --project <path> Project root path (default: current directory)
|
|
13
|
-
* --output <path> Output file path (default: .agentful/architecture.json)
|
|
14
|
-
* --force Force re-analysis even if cache is fresh
|
|
15
|
-
* --verbose Show detailed progress
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { CodebaseAnalyzer } from './analyzer.js';
|
|
19
|
-
|
|
20
|
-
const args = process.argv.slice(2);
|
|
21
|
-
|
|
22
|
-
// Parse CLI arguments
|
|
23
|
-
const options = {
|
|
24
|
-
projectRoot: process.cwd(),
|
|
25
|
-
outputPath: '.agentful/architecture.json',
|
|
26
|
-
force: false,
|
|
27
|
-
verbose: false
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
for (let i = 0; i < args.length; i++) {
|
|
31
|
-
const arg = args[i];
|
|
32
|
-
|
|
33
|
-
if (arg === '--project' && args[i + 1]) {
|
|
34
|
-
options.projectRoot = args[++i];
|
|
35
|
-
} else if (arg === '--output' && args[i + 1]) {
|
|
36
|
-
options.outputPath = args[++i];
|
|
37
|
-
} else if (arg === '--force') {
|
|
38
|
-
options.force = true;
|
|
39
|
-
} else if (arg === '--verbose') {
|
|
40
|
-
options.verbose = true;
|
|
41
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
42
|
-
console.log(`
|
|
43
|
-
Codebase Analyzer CLI
|
|
44
|
-
|
|
45
|
-
Usage:
|
|
46
|
-
node lib/core/cli.js [options]
|
|
47
|
-
|
|
48
|
-
Options:
|
|
49
|
-
--project <path> Project root path (default: current directory)
|
|
50
|
-
--output <path> Output file path (default: .agentful/architecture.json)
|
|
51
|
-
--force Force re-analysis even if cache is fresh
|
|
52
|
-
--verbose Show detailed progress
|
|
53
|
-
--help, -h Show this help message
|
|
54
|
-
|
|
55
|
-
Examples:
|
|
56
|
-
# Analyze current directory
|
|
57
|
-
node lib/core/cli.js
|
|
58
|
-
|
|
59
|
-
# Analyze specific project
|
|
60
|
-
node lib/core/cli.js --project /path/to/project
|
|
61
|
-
|
|
62
|
-
# Force re-analysis
|
|
63
|
-
node lib/core/cli.js --force --verbose
|
|
64
|
-
`);
|
|
65
|
-
process.exit(0);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Create analyzer
|
|
70
|
-
const analyzer = new CodebaseAnalyzer(options);
|
|
71
|
-
|
|
72
|
-
// Setup event listeners
|
|
73
|
-
analyzer.on('start', ({ projectRoot }) => {
|
|
74
|
-
console.log(`\nš Analyzing codebase: ${projectRoot}\n`);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
analyzer.on('progress', ({ stage, progress, fileCount, count }) => {
|
|
78
|
-
if (options.verbose) {
|
|
79
|
-
let message = ` ${stage}: ${progress}%`;
|
|
80
|
-
if (fileCount) message += ` (${fileCount} files)`;
|
|
81
|
-
if (count) message += ` (${count} detected)`;
|
|
82
|
-
console.log(message);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
analyzer.on('warning', ({ message }) => {
|
|
87
|
-
if (options.verbose) {
|
|
88
|
-
console.warn(` ā ļø ${message}`);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
analyzer.on('written', ({ path }) => {
|
|
93
|
-
console.log(`\nā
Analysis written to: ${path}\n`);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
analyzer.on('complete', ({ duration, analysis }) => {
|
|
97
|
-
console.log(`ā±ļø Completed in ${duration}ms\n`);
|
|
98
|
-
|
|
99
|
-
// Print summary
|
|
100
|
-
console.log('š Summary:');
|
|
101
|
-
console.log(` Files analyzed: ${analysis.fileCount}`);
|
|
102
|
-
console.log(` Confidence: ${analysis.confidence}%`);
|
|
103
|
-
console.log(` Primary language: ${analysis.primaryLanguage || 'unknown'}`);
|
|
104
|
-
console.log(` Languages: ${analysis.languages.map(l => l.name).join(', ')}`);
|
|
105
|
-
console.log(` Frameworks: ${analysis.frameworks.map(f => f.name).join(', ') || 'none'}`);
|
|
106
|
-
|
|
107
|
-
if (analysis.recommendations.length > 0) {
|
|
108
|
-
console.log(`\nš” Recommendations:`);
|
|
109
|
-
analysis.recommendations.forEach((rec, i) => {
|
|
110
|
-
console.log(` ${i + 1}. [${rec.priority}] ${rec.message}`);
|
|
111
|
-
console.log(` ā ${rec.action}`);
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
console.log('');
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
analyzer.on('error', (error) => {
|
|
119
|
-
console.error(`\nā Analysis failed: ${error.message}\n`);
|
|
120
|
-
if (options.verbose) {
|
|
121
|
-
console.error(error.stack);
|
|
122
|
-
}
|
|
123
|
-
process.exit(1);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// Run analysis
|
|
127
|
-
(async () => {
|
|
128
|
-
try {
|
|
129
|
-
if (options.force) {
|
|
130
|
-
await analyzer.analyze();
|
|
131
|
-
} else {
|
|
132
|
-
await analyzer.analyzeWithCache();
|
|
133
|
-
}
|
|
134
|
-
} catch (error) {
|
|
135
|
-
console.error(`\nā Unexpected error: ${error.message}\n`);
|
|
136
|
-
if (options.verbose) {
|
|
137
|
-
console.error(error.stack);
|
|
138
|
-
}
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
})();
|