@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/generator.js
DELETED
|
@@ -1,626 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import { createRequire } from 'module';
|
|
5
|
-
import Handlebars from 'handlebars';
|
|
6
|
-
import { TemplateManager } from './templates.js';
|
|
7
|
-
import { StorageManager } from './storage.js';
|
|
8
|
-
|
|
9
|
-
const require = createRequire(import.meta.url);
|
|
10
|
-
|
|
11
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Agent Generator
|
|
15
|
-
*
|
|
16
|
-
* Converts architecture analysis into specialized agents using templates.
|
|
17
|
-
* Supports Handlebars templating with custom helpers for code injection.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* const generator = new AgentGenerator('/path/to/project');
|
|
21
|
-
* await generator.initialize();
|
|
22
|
-
* const agents = await generator.generateAgents();
|
|
23
|
-
*/
|
|
24
|
-
export class AgentGenerator {
|
|
25
|
-
constructor(projectPath, options = {}) {
|
|
26
|
-
this.projectPath = projectPath;
|
|
27
|
-
this.options = {
|
|
28
|
-
templateDir: options.templateDir || path.join(__dirname, '../../templates'),
|
|
29
|
-
agentsDir: options.agentsDir || '.agentful/agents',
|
|
30
|
-
preserveCustom: options.preserveCustom !== false,
|
|
31
|
-
validateOutput: options.validateOutput !== false,
|
|
32
|
-
...options
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
this.templateManager = null;
|
|
36
|
-
this.storageManager = null;
|
|
37
|
-
this.architecture = null;
|
|
38
|
-
this.handlebars = Handlebars.create();
|
|
39
|
-
|
|
40
|
-
this._registerHelpers();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Initialize generator
|
|
45
|
-
*/
|
|
46
|
-
async initialize() {
|
|
47
|
-
// Initialize template manager
|
|
48
|
-
this.templateManager = new TemplateManager(this.projectPath, this.options.templateDir);
|
|
49
|
-
await this.templateManager.initialize();
|
|
50
|
-
|
|
51
|
-
// Initialize storage manager
|
|
52
|
-
this.storageManager = new StorageManager(this.projectPath, {
|
|
53
|
-
agentsDir: this.options.agentsDir,
|
|
54
|
-
preserveCustom: this.options.preserveCustom
|
|
55
|
-
});
|
|
56
|
-
await this.storageManager.initialize();
|
|
57
|
-
|
|
58
|
-
// Load architecture analysis
|
|
59
|
-
this.architecture = await this._loadArchitecture();
|
|
60
|
-
|
|
61
|
-
if (!this.architecture) {
|
|
62
|
-
throw new Error('Architecture analysis not found. Run /agentful-analyze first.');
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Generate agents from architecture analysis
|
|
68
|
-
*
|
|
69
|
-
* @returns {Promise<{agents: Array, duration: number, generated: number}>}
|
|
70
|
-
*/
|
|
71
|
-
async generateAgents() {
|
|
72
|
-
const startTime = Date.now();
|
|
73
|
-
const agents = [];
|
|
74
|
-
|
|
75
|
-
// Determine which agents to generate based on architecture
|
|
76
|
-
const agentSpecs = this._determineAgentsToGenerate();
|
|
77
|
-
|
|
78
|
-
// Generate all agents in parallel
|
|
79
|
-
const agentPromises = agentSpecs.map(async (spec) => {
|
|
80
|
-
try {
|
|
81
|
-
return await this._generateAgent(spec);
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error(`Failed to generate ${spec.name} agent:`, error.message);
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const results = await Promise.all(agentPromises);
|
|
89
|
-
agents.push(...results.filter(agent => agent !== null));
|
|
90
|
-
|
|
91
|
-
// Validate generated agents
|
|
92
|
-
if (this.options.validateOutput) {
|
|
93
|
-
await this._validateAgents(agents);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Save agents to storage
|
|
97
|
-
await this.storageManager.saveAgents(agents);
|
|
98
|
-
|
|
99
|
-
const duration = Date.now() - startTime;
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
agents,
|
|
103
|
-
generated: agents.length,
|
|
104
|
-
duration,
|
|
105
|
-
performance: duration < 5000 ? 'PASS' : 'SLOW'
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Generate a single agent
|
|
111
|
-
*
|
|
112
|
-
* @param {Object} spec - Agent specification
|
|
113
|
-
* @returns {Promise<Object>} Generated agent
|
|
114
|
-
* @private
|
|
115
|
-
*/
|
|
116
|
-
async _generateAgent(spec) {
|
|
117
|
-
// Load appropriate template
|
|
118
|
-
const template = await this.templateManager.loadTemplate(spec.template);
|
|
119
|
-
|
|
120
|
-
// Prepare context for template compilation
|
|
121
|
-
const context = await this._buildContext(spec);
|
|
122
|
-
|
|
123
|
-
// Compile template
|
|
124
|
-
const content = this._compileTemplate(template, context);
|
|
125
|
-
|
|
126
|
-
// Extract code examples from codebase
|
|
127
|
-
const examples = await this._extractCodeExamples(spec);
|
|
128
|
-
|
|
129
|
-
// Inject examples into content
|
|
130
|
-
const enhancedContent = this._injectExamples(content, examples);
|
|
131
|
-
|
|
132
|
-
// Create agent metadata
|
|
133
|
-
const metadata = {
|
|
134
|
-
name: spec.name,
|
|
135
|
-
version: '1.0.0',
|
|
136
|
-
template: spec.template,
|
|
137
|
-
generated: new Date().toISOString(),
|
|
138
|
-
customized: false,
|
|
139
|
-
checksum: this._calculateChecksum(enhancedContent),
|
|
140
|
-
architecture: {
|
|
141
|
-
framework: spec.framework,
|
|
142
|
-
language: spec.language,
|
|
143
|
-
patterns: spec.patterns || []
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
metadata,
|
|
149
|
-
content: enhancedContent,
|
|
150
|
-
configuration: spec.configuration || {}
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Determine which agents to generate based on architecture
|
|
156
|
-
*
|
|
157
|
-
* @returns {Array} Agent specifications
|
|
158
|
-
* @private
|
|
159
|
-
*/
|
|
160
|
-
_determineAgentsToGenerate() {
|
|
161
|
-
const specs = [];
|
|
162
|
-
const { techStack, patterns, projectType } = this.architecture;
|
|
163
|
-
|
|
164
|
-
// Always generate core agents
|
|
165
|
-
specs.push(
|
|
166
|
-
{ name: 'orchestrator', template: 'base/orchestrator' },
|
|
167
|
-
{ name: 'architect', template: 'base/architect' },
|
|
168
|
-
{ name: 'reviewer', template: 'base/reviewer' },
|
|
169
|
-
{ name: 'fixer', template: 'base/fixer' },
|
|
170
|
-
{ name: 'tester', template: 'base/tester' }
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
// Generate backend agent if server-side code detected
|
|
174
|
-
if (techStack.backend || patterns.includes('backend')) {
|
|
175
|
-
specs.push({
|
|
176
|
-
name: 'backend',
|
|
177
|
-
template: this._selectBackendTemplate(),
|
|
178
|
-
framework: techStack.backend?.framework,
|
|
179
|
-
language: techStack.backend?.language || techStack.language,
|
|
180
|
-
patterns: patterns.filter(p => p.includes('backend') || p.includes('api'))
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Generate frontend agent if client-side code detected
|
|
185
|
-
if (techStack.frontend || patterns.includes('frontend')) {
|
|
186
|
-
specs.push({
|
|
187
|
-
name: 'frontend',
|
|
188
|
-
template: this._selectFrontendTemplate(),
|
|
189
|
-
framework: techStack.frontend?.framework,
|
|
190
|
-
language: techStack.frontend?.language || techStack.language,
|
|
191
|
-
patterns: patterns.filter(p => p.includes('frontend') || p.includes('ui'))
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Generate database agent if database detected
|
|
196
|
-
if (techStack.database) {
|
|
197
|
-
specs.push({
|
|
198
|
-
name: 'database',
|
|
199
|
-
template: this._selectDatabaseTemplate(),
|
|
200
|
-
database: techStack.database
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Generate framework-specific agents
|
|
205
|
-
if (techStack.framework) {
|
|
206
|
-
const frameworkAgent = this._createFrameworkAgent(techStack.framework);
|
|
207
|
-
if (frameworkAgent) {
|
|
208
|
-
specs.push(frameworkAgent);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return specs;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Select backend template based on framework
|
|
217
|
-
*
|
|
218
|
-
* @returns {string} Template path
|
|
219
|
-
* @private
|
|
220
|
-
*/
|
|
221
|
-
_selectBackendTemplate() {
|
|
222
|
-
const framework = this.architecture.techStack.backend?.framework;
|
|
223
|
-
|
|
224
|
-
const frameworkTemplates = {
|
|
225
|
-
'express': 'frameworks/express-backend',
|
|
226
|
-
'fastify': 'frameworks/fastify-backend',
|
|
227
|
-
'nestjs': 'frameworks/nestjs-backend',
|
|
228
|
-
'koa': 'frameworks/koa-backend',
|
|
229
|
-
'django': 'frameworks/django-backend',
|
|
230
|
-
'flask': 'frameworks/flask-backend',
|
|
231
|
-
'spring': 'frameworks/spring-backend'
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
return frameworkTemplates[framework] || 'base/backend';
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Select frontend template based on framework
|
|
239
|
-
*
|
|
240
|
-
* @returns {string} Template path
|
|
241
|
-
* @private
|
|
242
|
-
*/
|
|
243
|
-
_selectFrontendTemplate() {
|
|
244
|
-
const framework = this.architecture.techStack.frontend?.framework;
|
|
245
|
-
|
|
246
|
-
const frameworkTemplates = {
|
|
247
|
-
'react': 'frameworks/react-frontend',
|
|
248
|
-
'vue': 'frameworks/vue-frontend',
|
|
249
|
-
'angular': 'frameworks/angular-frontend',
|
|
250
|
-
'svelte': 'frameworks/svelte-frontend',
|
|
251
|
-
'nextjs': 'frameworks/nextjs-frontend',
|
|
252
|
-
'nuxt': 'frameworks/nuxt-frontend'
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
return frameworkTemplates[framework] || 'base/frontend';
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Select database template based on database type
|
|
260
|
-
*
|
|
261
|
-
* @returns {string} Template path
|
|
262
|
-
* @private
|
|
263
|
-
*/
|
|
264
|
-
_selectDatabaseTemplate() {
|
|
265
|
-
const dbType = this.architecture.techStack.database?.type;
|
|
266
|
-
|
|
267
|
-
const dbTemplates = {
|
|
268
|
-
'postgresql': 'patterns/postgresql',
|
|
269
|
-
'mysql': 'patterns/mysql',
|
|
270
|
-
'mongodb': 'patterns/mongodb',
|
|
271
|
-
'sqlite': 'patterns/sqlite',
|
|
272
|
-
'redis': 'patterns/redis'
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
return dbTemplates[dbType] || 'patterns/database';
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Create framework-specific agent
|
|
280
|
-
*
|
|
281
|
-
* @param {string} framework - Framework name
|
|
282
|
-
* @returns {Object|null} Agent spec or null
|
|
283
|
-
* @private
|
|
284
|
-
*/
|
|
285
|
-
_createFrameworkAgent(framework) {
|
|
286
|
-
const frameworkAgents = {
|
|
287
|
-
'nextjs': {
|
|
288
|
-
name: 'nextjs-specialist',
|
|
289
|
-
template: 'frameworks/nextjs',
|
|
290
|
-
framework: 'nextjs'
|
|
291
|
-
},
|
|
292
|
-
'nestjs': {
|
|
293
|
-
name: 'nestjs-specialist',
|
|
294
|
-
template: 'frameworks/nestjs',
|
|
295
|
-
framework: 'nestjs'
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
return frameworkAgents[framework] || null;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Build context for template compilation
|
|
304
|
-
*
|
|
305
|
-
* @param {Object} spec - Agent specification
|
|
306
|
-
* @returns {Promise<Object>} Template context
|
|
307
|
-
* @private
|
|
308
|
-
*/
|
|
309
|
-
async _buildContext(spec) {
|
|
310
|
-
const context = {
|
|
311
|
-
projectName: this.architecture.projectName || path.basename(this.projectPath),
|
|
312
|
-
language: spec.language || this.architecture.techStack.language,
|
|
313
|
-
framework: spec.framework,
|
|
314
|
-
projectType: this.architecture.projectType,
|
|
315
|
-
techStack: this.architecture.techStack,
|
|
316
|
-
patterns: spec.patterns || [],
|
|
317
|
-
conventions: this.architecture.conventions || {},
|
|
318
|
-
timestamp: new Date().toISOString()
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
// Add framework-specific context
|
|
322
|
-
if (spec.framework) {
|
|
323
|
-
context.frameworkConfig = await this._extractFrameworkConfig(spec.framework);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Add database context
|
|
327
|
-
if (spec.database) {
|
|
328
|
-
context.database = spec.database;
|
|
329
|
-
context.orm = this.architecture.techStack.orm;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
return context;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Compile template with Handlebars
|
|
337
|
-
*
|
|
338
|
-
* @param {string} template - Template content
|
|
339
|
-
* @param {Object} context - Template context
|
|
340
|
-
* @returns {string} Compiled content
|
|
341
|
-
* @private
|
|
342
|
-
*/
|
|
343
|
-
_compileTemplate(template, context) {
|
|
344
|
-
try {
|
|
345
|
-
const compiled = this.handlebars.compile(template);
|
|
346
|
-
return compiled(context);
|
|
347
|
-
} catch (error) {
|
|
348
|
-
throw new Error(`Template compilation failed: ${error.message}`);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Extract code examples from analyzed codebase
|
|
354
|
-
*
|
|
355
|
-
* @param {Object} spec - Agent specification
|
|
356
|
-
* @returns {Promise<Object>} Code examples
|
|
357
|
-
* @private
|
|
358
|
-
*/
|
|
359
|
-
async _extractCodeExamples(spec) {
|
|
360
|
-
const examples = {
|
|
361
|
-
patterns: [],
|
|
362
|
-
conventions: [],
|
|
363
|
-
bestPractices: []
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
// Extract patterns from architecture analysis
|
|
367
|
-
if (this.architecture.codeExamples) {
|
|
368
|
-
const relevantExamples = this.architecture.codeExamples.filter(ex => {
|
|
369
|
-
return ex.category === spec.name || ex.tags?.includes(spec.name);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
examples.patterns = relevantExamples.slice(0, 3); // Limit to 3 examples
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Extract conventions
|
|
376
|
-
if (this.architecture.conventions) {
|
|
377
|
-
const conventions = this.architecture.conventions[spec.name];
|
|
378
|
-
if (conventions) {
|
|
379
|
-
examples.conventions = Array.isArray(conventions)
|
|
380
|
-
? conventions
|
|
381
|
-
: [conventions];
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return examples;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Inject code examples into agent content
|
|
390
|
-
*
|
|
391
|
-
* @param {string} content - Agent content
|
|
392
|
-
* @param {Object} examples - Code examples
|
|
393
|
-
* @returns {string} Enhanced content
|
|
394
|
-
* @private
|
|
395
|
-
*/
|
|
396
|
-
_injectExamples(content, examples) {
|
|
397
|
-
let enhanced = content;
|
|
398
|
-
|
|
399
|
-
// Add examples section if we have examples
|
|
400
|
-
if (examples.patterns.length > 0) {
|
|
401
|
-
const examplesSection = this._buildExamplesSection(examples);
|
|
402
|
-
|
|
403
|
-
// Inject before the last section (usually "Rules" or similar)
|
|
404
|
-
const lastHeadingIndex = enhanced.lastIndexOf('\n## ');
|
|
405
|
-
if (lastHeadingIndex !== -1) {
|
|
406
|
-
enhanced =
|
|
407
|
-
enhanced.slice(0, lastHeadingIndex) +
|
|
408
|
-
'\n\n' + examplesSection +
|
|
409
|
-
enhanced.slice(lastHeadingIndex);
|
|
410
|
-
} else {
|
|
411
|
-
enhanced += '\n\n' + examplesSection;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return enhanced;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Build examples section
|
|
420
|
-
*
|
|
421
|
-
* @param {Object} examples - Code examples
|
|
422
|
-
* @returns {string} Examples markdown
|
|
423
|
-
* @private
|
|
424
|
-
*/
|
|
425
|
-
_buildExamplesSection(examples) {
|
|
426
|
-
const sections = [];
|
|
427
|
-
|
|
428
|
-
sections.push('## Code Examples from This Project\n');
|
|
429
|
-
sections.push('These examples show how THIS specific project implements patterns:\n');
|
|
430
|
-
|
|
431
|
-
for (const example of examples.patterns) {
|
|
432
|
-
sections.push(`### ${example.title || 'Example'}\n`);
|
|
433
|
-
sections.push(`**File**: \`${example.file}\`\n`);
|
|
434
|
-
|
|
435
|
-
if (example.description) {
|
|
436
|
-
sections.push(`${example.description}\n`);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
sections.push('```' + (example.language || '') + '\n');
|
|
440
|
-
sections.push(example.code + '\n');
|
|
441
|
-
sections.push('```\n');
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (examples.conventions.length > 0) {
|
|
445
|
-
sections.push('### Project Conventions\n');
|
|
446
|
-
for (const convention of examples.conventions) {
|
|
447
|
-
sections.push(`- ${convention}\n`);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
return sections.join('\n');
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Validate generated agents
|
|
456
|
-
*
|
|
457
|
-
* @param {Array} agents - Generated agents
|
|
458
|
-
* @returns {Promise<void>}
|
|
459
|
-
* @private
|
|
460
|
-
*/
|
|
461
|
-
async _validateAgents(agents) {
|
|
462
|
-
const errors = [];
|
|
463
|
-
|
|
464
|
-
for (const agent of agents) {
|
|
465
|
-
// Validate frontmatter
|
|
466
|
-
const frontmatterMatch = agent.content.match(/^---\n([\s\S]*?)\n---/);
|
|
467
|
-
if (!frontmatterMatch) {
|
|
468
|
-
errors.push(`${agent.metadata.name}: Missing frontmatter`);
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// Validate required frontmatter fields
|
|
473
|
-
const frontmatter = frontmatterMatch[1];
|
|
474
|
-
const requiredFields = ['name', 'description', 'model', 'tools'];
|
|
475
|
-
|
|
476
|
-
for (const field of requiredFields) {
|
|
477
|
-
if (!frontmatter.includes(`${field}:`)) {
|
|
478
|
-
errors.push(`${agent.metadata.name}: Missing required field '${field}' in frontmatter`);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// Validate structure (should have at least one heading)
|
|
483
|
-
if (!agent.content.includes('# ')) {
|
|
484
|
-
errors.push(`${agent.metadata.name}: No main heading found`);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// Validate no broken references
|
|
488
|
-
const references = agent.content.match(/@\w+/g) || [];
|
|
489
|
-
for (const ref of references) {
|
|
490
|
-
const agentName = ref.slice(1);
|
|
491
|
-
// Check if referenced agent exists in generated set or is a known agent
|
|
492
|
-
const exists = agents.some(a => a.metadata.name === agentName) ||
|
|
493
|
-
['orchestrator', 'architect', 'backend', 'frontend', 'reviewer', 'fixer', 'tester'].includes(agentName);
|
|
494
|
-
|
|
495
|
-
if (!exists) {
|
|
496
|
-
console.warn(`${agent.metadata.name}: Reference to unknown agent: ${ref}`);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
if (errors.length > 0) {
|
|
502
|
-
throw new Error(`Agent validation failed:\n${errors.join('\n')}`);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Load architecture analysis
|
|
508
|
-
*
|
|
509
|
-
* @returns {Promise<Object|null>} Architecture data
|
|
510
|
-
* @private
|
|
511
|
-
*/
|
|
512
|
-
async _loadArchitecture() {
|
|
513
|
-
const architecturePath = path.join(this.projectPath, '.agentful', 'architecture.json');
|
|
514
|
-
|
|
515
|
-
try {
|
|
516
|
-
const content = await fs.readFile(architecturePath, 'utf-8');
|
|
517
|
-
return JSON.parse(content);
|
|
518
|
-
} catch (error) {
|
|
519
|
-
if (error.code === 'ENOENT') {
|
|
520
|
-
return null;
|
|
521
|
-
}
|
|
522
|
-
throw error;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
/**
|
|
527
|
-
* Extract framework configuration
|
|
528
|
-
*
|
|
529
|
-
* @param {string} framework - Framework name
|
|
530
|
-
* @returns {Promise<Object>} Framework config
|
|
531
|
-
* @private
|
|
532
|
-
*/
|
|
533
|
-
async _extractFrameworkConfig(framework) {
|
|
534
|
-
const config = {};
|
|
535
|
-
|
|
536
|
-
// Try to load framework config files
|
|
537
|
-
const configFiles = {
|
|
538
|
-
'nextjs': 'next.config.js',
|
|
539
|
-
'nestjs': 'nest-cli.json',
|
|
540
|
-
'vue': 'vue.config.js',
|
|
541
|
-
'react': 'react-app-env.d.ts'
|
|
542
|
-
};
|
|
543
|
-
|
|
544
|
-
const configFile = configFiles[framework];
|
|
545
|
-
if (configFile) {
|
|
546
|
-
const configPath = path.join(this.projectPath, configFile);
|
|
547
|
-
try {
|
|
548
|
-
await fs.access(configPath);
|
|
549
|
-
config.configFile = configFile;
|
|
550
|
-
config.hasConfig = true;
|
|
551
|
-
} catch {
|
|
552
|
-
config.hasConfig = false;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
return config;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* Calculate checksum for content
|
|
561
|
-
*
|
|
562
|
-
* @param {string} content - Content to hash
|
|
563
|
-
* @returns {string} Checksum
|
|
564
|
-
* @private
|
|
565
|
-
*/
|
|
566
|
-
_calculateChecksum(content) {
|
|
567
|
-
const crypto = require('crypto');
|
|
568
|
-
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
/**
|
|
572
|
-
* Register Handlebars helpers
|
|
573
|
-
*
|
|
574
|
-
* @private
|
|
575
|
-
*/
|
|
576
|
-
_registerHelpers() {
|
|
577
|
-
// Helper: Capitalize first letter
|
|
578
|
-
this.handlebars.registerHelper('capitalize', (str) => {
|
|
579
|
-
return str ? str.charAt(0).toUpperCase() + str.slice(1) : '';
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
// Helper: Uppercase
|
|
583
|
-
this.handlebars.registerHelper('uppercase', (str) => {
|
|
584
|
-
return str ? str.toUpperCase() : '';
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
// Helper: Code block
|
|
588
|
-
this.handlebars.registerHelper('codeBlock', (code, language) => {
|
|
589
|
-
return `\`\`\`${language || ''}\n${code}\n\`\`\``;
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
// Helper: List items
|
|
593
|
-
this.handlebars.registerHelper('list', (items) => {
|
|
594
|
-
if (!Array.isArray(items)) return '';
|
|
595
|
-
return items.map(item => `- ${item}`).join('\n');
|
|
596
|
-
});
|
|
597
|
-
|
|
598
|
-
// Helper: Join array
|
|
599
|
-
this.handlebars.registerHelper('join', (arr, separator) => {
|
|
600
|
-
if (!Array.isArray(arr)) return '';
|
|
601
|
-
return arr.join(separator || ', ');
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
// Helper: Conditional equality
|
|
605
|
-
this.handlebars.registerHelper('eq', (a, b) => {
|
|
606
|
-
return a === b;
|
|
607
|
-
});
|
|
608
|
-
|
|
609
|
-
// Helper: Conditional includes
|
|
610
|
-
this.handlebars.registerHelper('includes', (arr, item) => {
|
|
611
|
-
return Array.isArray(arr) && arr.includes(item);
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
// Helper: Format date
|
|
615
|
-
this.handlebars.registerHelper('formatDate', (date) => {
|
|
616
|
-
return new Date(date).toISOString().split('T')[0];
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
// Helper: JSON stringify
|
|
620
|
-
this.handlebars.registerHelper('json', (obj) => {
|
|
621
|
-
return JSON.stringify(obj, null, 2);
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
export default AgentGenerator;
|
package/lib/core/index.js
DELETED