@itz4blitz/agentful 0.2.1 → 0.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/.claude/agents/orchestrator.md +121 -610
- package/.claude/commands/agentful-generate.md +206 -0
- package/.claude/skills/conversation/SKILL.md +152 -975
- package/bin/cli.js +108 -583
- package/bin/hooks/health-check.sh +16 -16
- package/lib/index.js +6 -36
- package/lib/init.js +162 -0
- package/package.json +1 -2
- package/version.json +1 -1
- package/.claude/commands/agentful-agents.md +0 -668
- package/.claude/commands/agentful-skills.md +0 -635
- package/.claude/product/CHANGES.md +0 -276
- package/lib/agent-generator.js +0 -778
- package/lib/domain-detector.js +0 -468
- package/lib/domain-structure-generator.js +0 -770
- package/lib/project-analyzer.js +0 -701
- package/lib/tech-stack-detector.js +0 -1091
- package/lib/template-engine.js +0 -240
- package/templates/agents/domain-agent.template.md +0 -208
- package/templates/agents/tech-agent.template.md +0 -124
package/bin/cli.js
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* agentful CLI
|
|
5
|
-
*
|
|
4
|
+
* agentful CLI - Thin wrapper for template initialization and status
|
|
5
|
+
*
|
|
6
|
+
* Smart analysis and generation happens in Claude Code using:
|
|
7
|
+
* - /agentful-agents command
|
|
8
|
+
* - /agentful-skills command
|
|
6
9
|
*/
|
|
7
10
|
|
|
8
11
|
import fs from 'fs';
|
|
9
12
|
import path from 'path';
|
|
10
13
|
import { fileURLToPath } from 'url';
|
|
11
|
-
import {
|
|
12
|
-
import AgentGenerator from '../lib/agent-generator.js';
|
|
13
|
-
import DomainStructureGenerator from '../lib/domain-structure-generator.js';
|
|
14
|
-
import { detectTechStack } from '../lib/tech-stack-detector.js';
|
|
14
|
+
import { initProject, isInitialized } from '../lib/init.js';
|
|
15
15
|
|
|
16
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
17
|
const __dirname = path.dirname(__filename);
|
|
18
18
|
|
|
19
19
|
// Read version from centralized config
|
|
20
20
|
const VERSION = JSON.parse(fs.readFileSync(path.join(__dirname, '../version.json'), 'utf-8')).version;
|
|
21
|
-
const AGENTFUL_DIR = path.resolve(__dirname, '..');
|
|
22
|
-
const TEMPLATE_DIR = path.join(AGENTFUL_DIR, 'template');
|
|
23
21
|
|
|
24
22
|
// ANSI colors
|
|
25
23
|
const colors = {
|
|
@@ -57,28 +55,20 @@ function showHelp() {
|
|
|
57
55
|
console.log(` ${colors.bright}agentful${colors.reset} ${colors.green}<command>${colors.reset}`);
|
|
58
56
|
console.log('');
|
|
59
57
|
console.log('COMMANDS:');
|
|
60
|
-
console.log(` ${colors.green}init${colors.reset} Initialize agentful
|
|
61
|
-
console.log(` ${colors.green}
|
|
62
|
-
console.log(` ${colors.green}
|
|
63
|
-
console.log(` ${colors.green}init --deep${colors.reset} Run deep analysis (slower, more thorough)`);
|
|
64
|
-
console.log(` ${colors.green}init --generate-agents${colors.reset} Auto-generate agents`);
|
|
65
|
-
console.log(` ${colors.green}init --generate-domains${colors.reset} Auto-generate domain structure`);
|
|
66
|
-
console.log(` ${colors.green}generate${colors.reset} Generate specialized agents from tech stack`);
|
|
67
|
-
console.log(` ${colors.green}status${colors.reset} Show current development progress`);
|
|
68
|
-
console.log(` ${colors.green}--help${colors.reset} Show this help message`);
|
|
58
|
+
console.log(` ${colors.green}init${colors.reset} Initialize agentful (copy templates)`);
|
|
59
|
+
console.log(` ${colors.green}status${colors.reset} Show agentful status and generated files`);
|
|
60
|
+
console.log(` ${colors.green}help${colors.reset} Show this help message`);
|
|
69
61
|
console.log(` ${colors.green}--version${colors.reset} Show version`);
|
|
70
62
|
console.log('');
|
|
71
63
|
console.log('AFTER INIT:');
|
|
72
|
-
console.log(` 1. ${colors.bright}
|
|
73
|
-
console.log(`
|
|
74
|
-
console.log(` ${colors.cyan}• Large/complex projects:${colors.reset} Creates .claude/product/ with domains`);
|
|
75
|
-
console.log(` ${colors.dim}(Detection based on: # of domains, frameworks, monorepo status)${colors.reset}`);
|
|
76
|
-
console.log(` 2. ${colors.bright}Edit your product specification${colors.reset}`);
|
|
77
|
-
console.log(` 3. Run ${colors.bright}claude${colors.reset} to start Claude Code`);
|
|
78
|
-
console.log(` 4. Type ${colors.bright}/agentful${colors.reset} for natural conversation or ${colors.bright}/agentful-start${colors.reset} for structured development`);
|
|
64
|
+
console.log(` 1. ${colors.bright}Run claude${colors.reset} to start Claude Code`);
|
|
65
|
+
console.log(` 2. ${colors.bright}Type /agentful-generate${colors.reset} to analyze codebase & generate agents`);
|
|
79
66
|
console.log('');
|
|
80
|
-
console.log('
|
|
81
|
-
console.log(` ${colors.cyan}/
|
|
67
|
+
console.log('CLAUDE CODE COMMANDS:');
|
|
68
|
+
console.log(` ${colors.cyan}/agentful-generate${colors.reset} - Analyze codebase, generate agents & skills`);
|
|
69
|
+
console.log(` ${colors.cyan}/agentful-start${colors.reset} - Begin structured development workflow`);
|
|
70
|
+
console.log(` ${colors.cyan}/agentful-status${colors.reset} - Show progress and completion`);
|
|
71
|
+
console.log(` ${colors.cyan}/agentful${colors.reset} - Natural conversation about your product`);
|
|
82
72
|
console.log('');
|
|
83
73
|
}
|
|
84
74
|
|
|
@@ -86,22 +76,6 @@ function showVersion() {
|
|
|
86
76
|
console.log(`agentful v${VERSION}`);
|
|
87
77
|
}
|
|
88
78
|
|
|
89
|
-
function copyDir(src, dest) {
|
|
90
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
91
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
92
|
-
|
|
93
|
-
for (const entry of entries) {
|
|
94
|
-
const srcPath = path.join(src, entry.name);
|
|
95
|
-
const destPath = path.join(dest, entry.name);
|
|
96
|
-
|
|
97
|
-
if (entry.isDirectory()) {
|
|
98
|
-
copyDir(srcPath, destPath);
|
|
99
|
-
} else {
|
|
100
|
-
fs.copyFileSync(srcPath, destPath);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
79
|
function checkGitignore() {
|
|
106
80
|
const gitignorePath = path.join(process.cwd(), '.gitignore');
|
|
107
81
|
let content = '';
|
|
@@ -118,26 +92,15 @@ function checkGitignore() {
|
|
|
118
92
|
}
|
|
119
93
|
}
|
|
120
94
|
|
|
121
|
-
async function
|
|
95
|
+
async function init() {
|
|
122
96
|
showBanner();
|
|
123
97
|
|
|
124
98
|
const targetDir = process.cwd();
|
|
125
99
|
const claudeDir = path.join(targetDir, '.claude');
|
|
126
|
-
const agentfulDir = path.join(targetDir, '.agentful');
|
|
127
|
-
|
|
128
|
-
// Parse options
|
|
129
|
-
const bare = options.bare || false;
|
|
130
|
-
const smart = options.smart !== false; // Default: true
|
|
131
|
-
const deep = options.deep || false;
|
|
132
|
-
const autoGenerateAgents = options.generateAgents || false;
|
|
133
|
-
const autoGenerateDomains = options.generateDomains || false;
|
|
134
|
-
|
|
135
|
-
// Initialize analysis variable (will be populated later)
|
|
136
|
-
let analysis = null;
|
|
137
100
|
|
|
138
101
|
// Check if already initialized
|
|
139
|
-
if (
|
|
140
|
-
log(colors.yellow, '
|
|
102
|
+
if (await isInitialized(targetDir)) {
|
|
103
|
+
log(colors.yellow, 'agentful is already initialized in this directory!');
|
|
141
104
|
const readline = await import('readline');
|
|
142
105
|
const rl = readline.createInterface({
|
|
143
106
|
input: process.stdin,
|
|
@@ -154,557 +117,129 @@ async function initagentful(options = {}) {
|
|
|
154
117
|
process.exit(0);
|
|
155
118
|
}
|
|
156
119
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
// Create .claude/ directory structure
|
|
162
|
-
log(colors.dim, 'Creating .claude/ directory structure...');
|
|
163
|
-
|
|
164
|
-
const sourceClaudeDir = path.join(AGENTFUL_DIR, '.claude');
|
|
165
|
-
copyDir(sourceClaudeDir, claudeDir);
|
|
166
|
-
|
|
167
|
-
// Create .agentful/ directory
|
|
168
|
-
if (!fs.existsSync(agentfulDir)) {
|
|
169
|
-
fs.mkdirSync(agentfulDir, { recursive: true });
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Initialize state files
|
|
173
|
-
log(colors.dim, 'Initializing state files...');
|
|
174
|
-
|
|
175
|
-
const now = new Date().toISOString();
|
|
176
|
-
|
|
177
|
-
fs.writeFileSync(
|
|
178
|
-
path.join(agentfulDir, 'state.json'),
|
|
179
|
-
JSON.stringify(
|
|
180
|
-
{
|
|
181
|
-
version: '0.1.1',
|
|
182
|
-
current_task: null,
|
|
183
|
-
current_phase: 'idle',
|
|
184
|
-
iterations: 0,
|
|
185
|
-
last_updated: now,
|
|
186
|
-
blocked_on: []
|
|
187
|
-
},
|
|
188
|
-
null,
|
|
189
|
-
2
|
|
190
|
-
)
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
fs.writeFileSync(
|
|
194
|
-
path.join(agentfulDir, 'decisions.json'),
|
|
195
|
-
JSON.stringify({ pending: [], resolved: [] }, null, 2)
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
fs.writeFileSync(
|
|
199
|
-
path.join(agentfulDir, 'completion.json'),
|
|
200
|
-
JSON.stringify(
|
|
201
|
-
{
|
|
202
|
-
features: {},
|
|
203
|
-
gates: {
|
|
204
|
-
tests_passing: false,
|
|
205
|
-
no_type_errors: false,
|
|
206
|
-
no_dead_code: false,
|
|
207
|
-
coverage_80: false,
|
|
208
|
-
security_clean: false
|
|
209
|
-
},
|
|
210
|
-
overall: 0,
|
|
211
|
-
last_updated: now
|
|
212
|
-
},
|
|
213
|
-
null,
|
|
214
|
-
2
|
|
215
|
-
)
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
fs.writeFileSync(
|
|
219
|
-
path.join(agentfulDir, 'architecture.json'),
|
|
220
|
-
JSON.stringify(
|
|
221
|
-
{
|
|
222
|
-
detected_stack: {},
|
|
223
|
-
generated_agents: [],
|
|
224
|
-
decisions: [],
|
|
225
|
-
timestamp: now
|
|
226
|
-
},
|
|
227
|
-
null,
|
|
228
|
-
2
|
|
229
|
-
)
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
// Copy templates if not bare mode
|
|
233
|
-
if (!bare) {
|
|
234
|
-
log(colors.dim, 'Creating template files...');
|
|
235
|
-
|
|
236
|
-
const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
|
|
237
|
-
const productMdPath = path.join(targetDir, 'PRODUCT.md');
|
|
238
|
-
const claudeProductDir = path.join(targetDir, '.claude/product');
|
|
239
|
-
|
|
240
|
-
if (!fs.existsSync(claudeMdPath)) {
|
|
241
|
-
fs.copyFileSync(
|
|
242
|
-
path.join(TEMPLATE_DIR, 'CLAUDE.md'),
|
|
243
|
-
claudeMdPath
|
|
244
|
-
);
|
|
245
|
-
log(colors.green, ' ✓ Created CLAUDE.md');
|
|
246
|
-
} else {
|
|
247
|
-
log(colors.dim, ' ⊙ CLAUDE.md already exists, skipping');
|
|
120
|
+
// Remove existing files
|
|
121
|
+
log(colors.dim, 'Removing existing agentful files...');
|
|
122
|
+
if (fs.existsSync(claudeDir)) {
|
|
123
|
+
fs.rmSync(claudeDir, { recursive: true, force: true });
|
|
248
124
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
analysis.domains.length >= 3 || // Multiple detected domains
|
|
253
|
-
(analysis.frameworks && analysis.frameworks.length >= 2) || // Multiple frameworks
|
|
254
|
-
(analysis.packageManager === 'workspace' || analysis.packageManager === 'monorepo') // Monorepo
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
// Create appropriate product structure
|
|
258
|
-
const productExists = fs.existsSync(productMdPath) || fs.existsSync(claudeProductDir);
|
|
259
|
-
|
|
260
|
-
if (!productExists) {
|
|
261
|
-
if (shouldUseHierarchical) {
|
|
262
|
-
// Create hierarchical .claude/product/ structure
|
|
263
|
-
log(colors.dim, ' 📁 Using hierarchical product structure (detected complex project)');
|
|
264
|
-
fs.mkdirSync(claudeProductDir, { recursive: true });
|
|
265
|
-
fs.mkdirSync(path.join(claudeProductDir, 'domains'), { recursive: true });
|
|
266
|
-
|
|
267
|
-
// Create main index.md
|
|
268
|
-
const indexContent = `# Product Specification
|
|
269
|
-
|
|
270
|
-
## Overview
|
|
271
|
-
[Describe your product here]
|
|
272
|
-
|
|
273
|
-
## Tech Stack
|
|
274
|
-
${analysis && analysis.language ? `- Language: ${analysis.language}` : ''}
|
|
275
|
-
${analysis && analysis.frameworks && analysis.frameworks.length > 0 ? `- Frameworks: ${analysis.frameworks.join(', ')}` : ''}
|
|
276
|
-
${analysis && analysis.packageManager && analysis.packageManager !== 'unknown' ? `- Package Manager: ${analysis.packageManager}` : ''}
|
|
277
|
-
|
|
278
|
-
## Domains
|
|
279
|
-
${analysis && analysis.domains.length > 0 ? analysis.domains.map((d, i) => `${i + 1}. [${d}] - Define details in \`domains/${d.toLowerCase().replace(/\s+/g, '-')}/index.md\``).join('\n') : '- [Domain 1] - Define in domains/domain-name/index.md\n- [Domain 2] - Define in domains/domain-name/index.md'}
|
|
280
|
-
|
|
281
|
-
## Priority Legend
|
|
282
|
-
- **CRITICAL**: Must have for launch
|
|
283
|
-
- **HIGH**: Important for MVP
|
|
284
|
-
- **MEDIUM**: Nice to have
|
|
285
|
-
- **LOW**: Future consideration
|
|
286
|
-
`;
|
|
287
|
-
|
|
288
|
-
fs.writeFileSync(path.join(claudeProductDir, 'index.md'), indexContent);
|
|
289
|
-
|
|
290
|
-
// Create domain directories with index files for detected domains
|
|
291
|
-
if (analysis && analysis.domains.length > 0) {
|
|
292
|
-
analysis.domains.slice(0, 8).forEach(domain => {
|
|
293
|
-
const domainDir = path.join(claudeProductDir, 'domains', domain.toLowerCase().replace(/\s+/g, '-'));
|
|
294
|
-
fs.mkdirSync(domainDir, { recursive: true });
|
|
295
|
-
|
|
296
|
-
const domainIndexContent = `# ${domain} Domain
|
|
297
|
-
|
|
298
|
-
## Overview
|
|
299
|
-
[Describe the ${domain} domain's purpose and scope]
|
|
300
|
-
|
|
301
|
-
## Features
|
|
302
|
-
1. [Feature 1] (CRITICAL)
|
|
303
|
-
- [Acceptance criteria]
|
|
304
|
-
- [Dependencies]
|
|
305
|
-
|
|
306
|
-
2. [Feature 2] (HIGH)
|
|
307
|
-
- [Acceptance criteria]
|
|
308
|
-
|
|
309
|
-
## Technical Notes
|
|
310
|
-
- [Any technical considerations specific to this domain]
|
|
311
|
-
`;
|
|
312
|
-
fs.writeFileSync(path.join(domainDir, 'index.md'), domainIndexContent);
|
|
313
|
-
});
|
|
314
|
-
} else {
|
|
315
|
-
// Create example domain structure
|
|
316
|
-
const exampleDomainDir = path.join(claudeProductDir, 'domains', 'example-domain');
|
|
317
|
-
fs.mkdirSync(exampleDomainDir, { recursive: true });
|
|
318
|
-
fs.writeFileSync(
|
|
319
|
-
path.join(exampleDomainDir, 'index.md'),
|
|
320
|
-
'# Example Domain\n\n## Overview\n[Describe this domain]\n\n## Features\n1. Example Feature (HIGH)\n'
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
log(colors.green, ' ✓ Created .claude/product/ with domain structure');
|
|
325
|
-
log(colors.dim, ` → Organized by ${analysis.domains.length > 0 ? analysis.domains.length : 'example'} domain(s)`);
|
|
326
|
-
} else {
|
|
327
|
-
// Create flat PRODUCT.md structure
|
|
328
|
-
log(colors.dim, ' 📄 Using flat product structure (simple project)');
|
|
329
|
-
fs.copyFileSync(
|
|
330
|
-
path.join(TEMPLATE_DIR, 'PRODUCT.md'),
|
|
331
|
-
productMdPath
|
|
332
|
-
);
|
|
333
|
-
log(colors.green, ' ✓ Created PRODUCT.md');
|
|
334
|
-
}
|
|
335
|
-
} else {
|
|
336
|
-
log(colors.dim, ' ⊙ Product spec already exists, skipping');
|
|
125
|
+
const agentfulDir = path.join(targetDir, '.agentful');
|
|
126
|
+
if (fs.existsSync(agentfulDir)) {
|
|
127
|
+
fs.rmSync(agentfulDir, { recursive: true, force: true });
|
|
337
128
|
}
|
|
338
129
|
}
|
|
339
130
|
|
|
340
|
-
//
|
|
341
|
-
|
|
131
|
+
// Initialize using lib/init.js
|
|
132
|
+
log(colors.dim, 'Copying templates...');
|
|
133
|
+
try {
|
|
134
|
+
const result = await initProject(targetDir, { includeProduct: true });
|
|
342
135
|
|
|
343
|
-
// Perform essential project detection (unless explicitly disabled)
|
|
344
|
-
if (smart) {
|
|
345
|
-
console.log('');
|
|
346
|
-
log(colors.bright, '🔍 Detecting project essentials...');
|
|
347
136
|
console.log('');
|
|
348
|
-
|
|
349
|
-
try {
|
|
350
|
-
analysis = await analyzeProject(targetDir);
|
|
351
|
-
await exportToArchitectureJson(targetDir, analysis);
|
|
352
|
-
|
|
353
|
-
// Show only essential info
|
|
354
|
-
if (analysis.language && analysis.language !== 'unknown') {
|
|
355
|
-
log(colors.cyan, ` Language: ${analysis.language}`);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (analysis.frameworks.length > 0) {
|
|
359
|
-
log(colors.cyan, ` Framework: ${analysis.frameworks[0]}`);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (analysis.packageManager && analysis.packageManager !== 'unknown') {
|
|
363
|
-
log(colors.cyan, ` Package Mgr: ${analysis.packageManager}`);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
console.log('');
|
|
367
|
-
log(colors.dim, ` ✓ Detection complete`);
|
|
368
|
-
|
|
369
|
-
// Show critical warnings only
|
|
370
|
-
if (analysis.warnings && analysis.warnings.length > 0) {
|
|
371
|
-
const criticalWarnings = analysis.warnings.filter(w =>
|
|
372
|
-
w.includes('empty') || w.includes('not detect')
|
|
373
|
-
);
|
|
374
|
-
if (criticalWarnings.length > 0) {
|
|
375
|
-
console.log('');
|
|
376
|
-
log(colors.yellow, '⚠️ Warnings:');
|
|
377
|
-
criticalWarnings.forEach(warning => {
|
|
378
|
-
log(colors.dim, ` • ${warning}`);
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
} catch (error) {
|
|
384
|
-
log(colors.dim, ' ⊙ Detection skipped (project may be empty)');
|
|
385
|
-
analysis = null;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// Interactive prompts for generation (if not auto-generated)
|
|
390
|
-
if (analysis && analysis.domains.length > 0 && !autoGenerateDomains && !autoGenerateAgents) {
|
|
137
|
+
log(colors.green, 'Initialized agentful successfully!');
|
|
391
138
|
console.log('');
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
output: process.stdout
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
// Ask about domain structure
|
|
399
|
-
const generateStructure = await new Promise(resolve => {
|
|
400
|
-
rl.question(`✨ Generate domain structure and specialized agents? [Y/n] `, answer => {
|
|
401
|
-
resolve(answer.toLowerCase() !== 'n');
|
|
402
|
-
});
|
|
139
|
+
log(colors.dim, 'Created files:');
|
|
140
|
+
result.files.forEach(file => {
|
|
141
|
+
log(colors.green, ` ${file}`);
|
|
403
142
|
});
|
|
404
|
-
|
|
405
|
-
rl.close();
|
|
406
|
-
|
|
407
|
-
if (generateStructure) {
|
|
408
|
-
await generateAgentsAndDomains(targetDir, analysis);
|
|
409
|
-
}
|
|
410
|
-
} else if (autoGenerateDomains || autoGenerateAgents) {
|
|
411
143
|
console.log('');
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
domains: autoGenerateDomains
|
|
416
|
-
});
|
|
144
|
+
} catch (error) {
|
|
145
|
+
log(colors.red, `Failed to initialize: ${error.message}`);
|
|
146
|
+
process.exit(1);
|
|
417
147
|
}
|
|
418
148
|
|
|
419
|
-
//
|
|
149
|
+
// Update .gitignore
|
|
150
|
+
checkGitignore();
|
|
151
|
+
|
|
152
|
+
// Show next steps
|
|
420
153
|
console.log('');
|
|
421
|
-
log(colors.
|
|
154
|
+
log(colors.bright, 'Next Steps:');
|
|
422
155
|
console.log('');
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const productMdPath = path.join(targetDir, 'PRODUCT.md');
|
|
426
|
-
const claudeProductDir = path.join(targetDir, '.claude/product');
|
|
427
|
-
const usingHierarchical = fs.existsSync(claudeProductDir);
|
|
428
|
-
const usingFlat = fs.existsSync(productMdPath);
|
|
429
|
-
|
|
430
|
-
log(colors.bright, 'Next steps:');
|
|
156
|
+
log(colors.cyan, ' 1. Run: claude');
|
|
157
|
+
log(colors.cyan, ' 2. Type: /agentful-generate');
|
|
431
158
|
console.log('');
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
console.log(` ${colors.dim}→ Organized by domains (best for larger projects)${colors.reset}`);
|
|
437
|
-
if (analysis && analysis.domains.length > 0) {
|
|
438
|
-
console.log(` ${colors.dim}→ Detected ${analysis.domains.length} domain(s) with pre-configured directories${colors.reset}`);
|
|
439
|
-
}
|
|
440
|
-
} else if (usingFlat) {
|
|
441
|
-
console.log(` ${colors.green}✓ Created PRODUCT.md${colors.reset} (flat structure)`);
|
|
442
|
-
console.log(` ${colors.dim}→ Simple, single-file format (best for small projects)${colors.reset}`);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
console.log(` 2. ${colors.cyan}Run: claude${colors.reset}`);
|
|
446
|
-
console.log(` 3. ${colors.cyan}Type: /agentful${colors.reset} (natural) or ${colors.cyan}/agentful-start${colors.reset} (structured)`);
|
|
159
|
+
log(colors.dim, 'This will analyze your codebase and generate:');
|
|
160
|
+
log(colors.dim, ' - Specialized agents for your tech stack');
|
|
161
|
+
log(colors.dim, ' - Domain-specific agents (auth, billing, etc.)');
|
|
162
|
+
log(colors.dim, ' - Skills for frameworks you use');
|
|
447
163
|
console.log('');
|
|
448
|
-
|
|
449
|
-
if (usingHierarchical) {
|
|
450
|
-
log(colors.dim, '💡 Hierarchical structure benefits:');
|
|
451
|
-
log(colors.dim, ' • Organized by domain (e.g., Auth, Users, Billing)');
|
|
452
|
-
log(colors.dim, ' • Easier to manage large feature sets');
|
|
453
|
-
log(colors.dim, ' • Teams can work on different domains in parallel');
|
|
454
|
-
console.log('');
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
log(colors.dim, 'For extended development sessions with fewer interruptions:');
|
|
458
|
-
log(colors.cyan, ` /ralph-loop "/agentful-start" --max-iterations 50 --completion-promise "AGENTFUL_COMPLETE"`);
|
|
164
|
+
log(colors.dim, 'Optional: Edit CLAUDE.md and PRODUCT.md first to customize.');
|
|
459
165
|
console.log('');
|
|
460
166
|
}
|
|
461
167
|
|
|
462
|
-
/**
|
|
463
|
-
* Generate agents and domain structure
|
|
464
|
-
*/
|
|
465
|
-
async function generateAgentsAndDomains(projectPath, analysis, options = {}) {
|
|
466
|
-
const { agents = true, domains = true } = options;
|
|
467
|
-
|
|
468
|
-
try {
|
|
469
|
-
// Generate domain structure
|
|
470
|
-
if (domains) {
|
|
471
|
-
log(colors.dim, '📁 Creating domain structure...');
|
|
472
|
-
const domainGenerator = new DomainStructureGenerator(projectPath, analysis);
|
|
473
|
-
const domainResult = await domainGenerator.generateDomainStructure();
|
|
474
|
-
log(colors.green, ` ✓ Generated ${domainResult.domains} domains with ${domainResult.features} features`);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// Generate specialized agents
|
|
478
|
-
if (agents) {
|
|
479
|
-
log(colors.dim, '🤖 Generating specialized agents...');
|
|
480
|
-
const agentGenerator = new AgentGenerator(projectPath, analysis);
|
|
481
|
-
const agentResult = await agentGenerator.generateAgents();
|
|
482
|
-
|
|
483
|
-
const totalAgents = agentResult.core.length + agentResult.domains.length + agentResult.tech.length;
|
|
484
|
-
log(colors.green, ` ✓ Generated ${totalAgents} agents:`);
|
|
485
|
-
log(colors.dim, ` - ${agentResult.core.length} core agents`);
|
|
486
|
-
if (agentResult.domains.length > 0) {
|
|
487
|
-
log(colors.dim, ` - ${agentResult.domains.length} domain agents`);
|
|
488
|
-
}
|
|
489
|
-
if (agentResult.tech.length > 0) {
|
|
490
|
-
log(colors.dim, ` - ${agentResult.tech.length} tech-specific agents`);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
console.log('');
|
|
495
|
-
log(colors.green, '✨ Generation complete!');
|
|
496
|
-
log(colors.dim, ' Your agents are now contextually aware of your codebase.');
|
|
497
|
-
} catch (error) {
|
|
498
|
-
log(colors.red, `❌ Generation failed: ${error.message}`);
|
|
499
|
-
log(colors.dim, ' You can continue without it, or run: agentful generate');
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
168
|
function showStatus() {
|
|
504
|
-
const
|
|
169
|
+
const targetDir = process.cwd();
|
|
170
|
+
const agentfulDir = path.join(targetDir, '.agentful');
|
|
505
171
|
|
|
506
172
|
if (!fs.existsSync(agentfulDir)) {
|
|
507
|
-
log(colors.red, '
|
|
173
|
+
log(colors.red, 'agentful not initialized in this directory!');
|
|
508
174
|
log(colors.dim, 'Run: npx @itz4blitz/agentful init');
|
|
509
175
|
process.exit(1);
|
|
510
176
|
}
|
|
511
177
|
|
|
512
|
-
const statePath = path.join(agentfulDir, 'state.json');
|
|
513
|
-
const completionPath = path.join(agentfulDir, 'completion.json');
|
|
514
|
-
const decisionsPath = path.join(agentfulDir, 'decisions.json');
|
|
515
|
-
|
|
516
|
-
if (!fs.existsSync(statePath)) {
|
|
517
|
-
log(colors.red, '❌ State file missing!');
|
|
518
|
-
process.exit(1);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
const state = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
|
|
522
|
-
const completion = fs.existsSync(completionPath)
|
|
523
|
-
? JSON.parse(fs.readFileSync(completionPath, 'utf-8'))
|
|
524
|
-
: null;
|
|
525
|
-
const decisions = fs.existsSync(decisionsPath)
|
|
526
|
-
? JSON.parse(fs.readFileSync(decisionsPath, 'utf-8'))
|
|
527
|
-
: null;
|
|
528
|
-
|
|
529
178
|
showBanner();
|
|
530
|
-
|
|
531
|
-
log(colors.bright, 'Current Status:');
|
|
179
|
+
log(colors.bright, 'Agentful Status:');
|
|
532
180
|
console.log('');
|
|
533
181
|
|
|
534
|
-
//
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
182
|
+
// Helper to read JSON safely
|
|
183
|
+
const readJSON = (filepath) => {
|
|
184
|
+
try {
|
|
185
|
+
return fs.existsSync(filepath) ? JSON.parse(fs.readFileSync(filepath, 'utf-8')) : null;
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Read state files
|
|
192
|
+
const state = readJSON(path.join(agentfulDir, 'state.json'));
|
|
193
|
+
const completion = readJSON(path.join(agentfulDir, 'completion.json'));
|
|
194
|
+
const decisions = readJSON(path.join(agentfulDir, 'decisions.json'));
|
|
195
|
+
|
|
196
|
+
// Display state
|
|
197
|
+
if (state) {
|
|
198
|
+
log(colors.green, 'State:');
|
|
199
|
+
log(colors.dim, ` Initialized: ${state.initialized || 'N/A'}`);
|
|
200
|
+
const agentCount = state.agents?.length || 0;
|
|
201
|
+
const skillCount = state.skills?.length || 0;
|
|
202
|
+
log(colors.dim, ` Agents: ${agentCount} ${agentCount === 0 ? '(run /agentful-agents)' : ''}`);
|
|
203
|
+
log(colors.dim, ` Skills: ${skillCount} ${skillCount === 0 ? '(run /agentful-skills)' : ''}`);
|
|
204
|
+
console.log('');
|
|
541
205
|
}
|
|
542
206
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
// Show completion if available
|
|
207
|
+
// Display completion
|
|
546
208
|
if (completion) {
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
log(colors.bright, 'Progress:');
|
|
552
|
-
log(colors.cyan, ` ${bar} ${percentage}%`);
|
|
553
|
-
console.log('');
|
|
554
|
-
|
|
555
|
-
// Show quality gates
|
|
556
|
-
if (completion.gates) {
|
|
557
|
-
log(colors.bright, 'Quality Gates:');
|
|
558
|
-
Object.entries(completion.gates).forEach(([gate, passed]) => {
|
|
559
|
-
const icon = passed ? '✅' : '❌';
|
|
560
|
-
const label = gate.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
561
|
-
log(passed ? colors.green : colors.red, ` ${icon} ${label}`);
|
|
562
|
-
});
|
|
563
|
-
console.log('');
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// Show pending decisions
|
|
567
|
-
if (decisions && decisions.pending && decisions.pending.length > 0) {
|
|
568
|
-
log(colors.yellow, `⚠️ ${decisions.pending.length} pending decisions:`);
|
|
569
|
-
decisions.pending.forEach((d, i) => {
|
|
570
|
-
log(colors.dim, ` ${i + 1}. ${d.question}`);
|
|
571
|
-
});
|
|
572
|
-
console.log('');
|
|
573
|
-
log(colors.cyan, ` Run: /agentful-decide`);
|
|
209
|
+
const agentComp = Object.keys(completion.agents || {}).length;
|
|
210
|
+
const skillComp = Object.keys(completion.skills || {}).length;
|
|
211
|
+
if (agentComp > 0 || skillComp > 0) {
|
|
212
|
+
log(colors.green, `Completions: ${agentComp} agents, ${skillComp} skills`);
|
|
574
213
|
console.log('');
|
|
575
214
|
}
|
|
576
215
|
}
|
|
577
216
|
|
|
578
|
-
//
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
log(colors.cyan, ' • /agentful-decide - Answer pending decisions');
|
|
582
|
-
log(colors.cyan, ' • /agentful-validate- Run quality checks');
|
|
583
|
-
console.log('');
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
function generateAgentPrompt(stack) {
|
|
587
|
-
let prompt = `# Tech Stack Analysis\n\n`;
|
|
588
|
-
prompt += `**Language**: ${stack.language || 'Unknown'}\n`;
|
|
589
|
-
const primaryFramework = stack.frameworks && stack.frameworks.length > 0 ? stack.frameworks[0] : null;
|
|
590
|
-
prompt += `**Framework**: ${primaryFramework || 'None'}\n\n`;
|
|
591
|
-
|
|
592
|
-
if (stack.dependencies && stack.dependencies.length > 0) {
|
|
593
|
-
prompt += `**Key Dependencies**:\n`;
|
|
594
|
-
stack.dependencies.slice(0, 10).forEach(dep => {
|
|
595
|
-
prompt += `- ${dep}\n`;
|
|
596
|
-
});
|
|
597
|
-
prompt += `\n`;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
prompt += `## Instructions\n\n`;
|
|
601
|
-
prompt += `You are the Architect agent. Your task is to:\n\n`;
|
|
602
|
-
prompt += `1. **Sample 3-5 files** from the codebase to understand patterns\n`;
|
|
603
|
-
prompt += `2. **Detect conventions**: file structure, naming, imports, styling\n`;
|
|
604
|
-
prompt += `3. **Generate specialized agents** with real examples from the code\n\n`;
|
|
605
|
-
prompt += `## Output\n\n`;
|
|
606
|
-
prompt += `Create/update \`.agentful/architecture.json\` with:\n`;
|
|
607
|
-
prompt += `- Detected patterns\n`;
|
|
608
|
-
prompt += `- Generated agent list\n`;
|
|
609
|
-
prompt += `- Key conventions\n\n`;
|
|
610
|
-
prompt += `Then create project-specific agent files in \`.claude/agents/\` \n`;
|
|
611
|
-
prompt += `using the naming convention: \`[tech]-specialist.md\`\n\n`;
|
|
612
|
-
|
|
613
|
-
prompt += `## Example Agent Template\n\n`;
|
|
614
|
-
prompt += `\`\`\`markdown\n`;
|
|
615
|
-
prompt += `# [Tech] Specialist Agent\n\n`;
|
|
616
|
-
prompt += `---\n`;
|
|
617
|
-
prompt += `name: [tech]-specialist\n`;
|
|
618
|
-
prompt += `description: Expert in [Tech] development patterns\n`;
|
|
619
|
-
prompt += `model: sonnet\n\n`;
|
|
620
|
-
prompt += `## Context\n\n`;
|
|
621
|
-
prompt += `[Analyze actual code samples and list real patterns]\n\n`;
|
|
622
|
-
prompt += `## Conventions\n\n`;
|
|
623
|
-
prompt += `1. [Pattern from actual code]\n`;
|
|
624
|
-
prompt += `2. [Pattern from actual code]\n`;
|
|
625
|
-
prompt += `3. [Pattern from actual code]\n\n`;
|
|
626
|
-
prompt += `## Examples from Codebase\n\n`;
|
|
627
|
-
prompt += `\`\`\`[language]\n`;
|
|
628
|
-
prompt += `[Real example from sampled files]\n`;
|
|
629
|
-
prompt += `\`\`\`\n`;
|
|
630
|
-
prompt += `\`\`\`\n\n`;
|
|
631
|
-
|
|
632
|
-
prompt += `---\n\n`;
|
|
633
|
-
prompt += `**IMPORTANT**: \n`;
|
|
634
|
-
prompt += `- Sample REAL files, don't make up patterns\n`;
|
|
635
|
-
prompt += `- Include ACTUAL code examples from this project\n`;
|
|
636
|
-
prompt += `- Respect existing conventions, don't introduce new ones\n`;
|
|
637
|
-
prompt += `- Generate agents ONLY for technologies actually in use\n`;
|
|
638
|
-
|
|
639
|
-
return prompt;
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
async function generateAgents() {
|
|
643
|
-
showBanner();
|
|
644
|
-
|
|
645
|
-
const agentfulDir = path.join(process.cwd(), '.agentful');
|
|
646
|
-
|
|
647
|
-
// Check if agentful is initialized
|
|
648
|
-
if (!fs.existsSync(agentfulDir)) {
|
|
649
|
-
log(colors.red, '❌ agentful not initialized in this directory!');
|
|
650
|
-
log(colors.dim, 'Run: npx @itz4blitz/agentful init');
|
|
651
|
-
process.exit(1);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// Detect tech stack using library function
|
|
655
|
-
log(colors.dim, 'Analyzing tech stack...');
|
|
656
|
-
const stack = await detectTechStack(process.cwd());
|
|
657
|
-
|
|
658
|
-
if (!stack.language || stack.language === 'unknown') {
|
|
659
|
-
log(colors.yellow, '⚠️ Could not detect language/framework');
|
|
660
|
-
log(colors.dim, 'Supported: Node.js, Python, Go, Rust, C#, Java');
|
|
217
|
+
// Display decisions
|
|
218
|
+
if (decisions?.decisions?.length > 0) {
|
|
219
|
+
log(colors.yellow, `Decisions: ${decisions.decisions.length} pending`);
|
|
661
220
|
console.log('');
|
|
662
|
-
log(colors.cyan, 'Manually trigger architect analysis by running:');
|
|
663
|
-
log(colors.cyan, ' claude');
|
|
664
|
-
log(colors.cyan, ' Then invoke the architect agent');
|
|
665
|
-
console.log('');
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
const primaryFramework = stack.frameworks.length > 0 ? stack.frameworks[0] : null;
|
|
670
|
-
log(colors.green, `✓ Detected: ${stack.language} ${primaryFramework ? `(${primaryFramework})` : ''}`);
|
|
671
|
-
log(colors.dim, ` Dependencies: ${stack.dependencies.length} packages`);
|
|
672
|
-
|
|
673
|
-
// Update architecture.json
|
|
674
|
-
log(colors.dim, 'Updating architecture analysis...');
|
|
675
|
-
const archPath = path.join(agentfulDir, 'architecture.json');
|
|
676
|
-
let architecture = { detected_stack: {}, generated_agents: [], decisions: [], timestamp: new Date().toISOString() };
|
|
677
|
-
|
|
678
|
-
if (fs.existsSync(archPath)) {
|
|
679
|
-
architecture = JSON.parse(fs.readFileSync(archPath, 'utf-8'));
|
|
680
221
|
}
|
|
681
222
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (stack.dependencies.length > 0) {
|
|
695
|
-
log(colors.cyan, ` Dependencies: ${stack.dependencies.length} packages`);
|
|
223
|
+
// Check generated files
|
|
224
|
+
const claudeDir = path.join(targetDir, '.claude');
|
|
225
|
+
const agentFiles = fs.existsSync(path.join(claudeDir, 'agents'))
|
|
226
|
+
? fs.readdirSync(path.join(claudeDir, 'agents')).filter(f => f.endsWith('.md'))
|
|
227
|
+
: [];
|
|
228
|
+
const skillFiles = fs.existsSync(path.join(claudeDir, 'skills'))
|
|
229
|
+
? fs.readdirSync(path.join(claudeDir, 'skills')).filter(f => f.endsWith('.md'))
|
|
230
|
+
: [];
|
|
231
|
+
|
|
232
|
+
if (agentFiles.length > 0) {
|
|
233
|
+
log(colors.green, `Generated: ${agentFiles.length} agent(s), ${skillFiles.length} skill(s)`);
|
|
234
|
+
console.log('');
|
|
696
235
|
}
|
|
697
|
-
console.log('');
|
|
698
236
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
log(colors.cyan, '
|
|
702
|
-
log(colors.cyan, '
|
|
703
|
-
log(colors.cyan, '
|
|
704
|
-
log(colors.cyan, '
|
|
705
|
-
console.log('');
|
|
706
|
-
|
|
707
|
-
log(colors.dim, '💡 Tip: Architect generates project-specific agents on first /agentful-start run');
|
|
237
|
+
// Next actions
|
|
238
|
+
log(colors.bright, 'Claude Code Commands:');
|
|
239
|
+
log(colors.cyan, ' /agentful-agents - Generate agents');
|
|
240
|
+
log(colors.cyan, ' /agentful-skills - Generate skills');
|
|
241
|
+
log(colors.cyan, ' /agentful-start - Start workflow');
|
|
242
|
+
log(colors.cyan, ' /agentful - Product chat');
|
|
708
243
|
console.log('');
|
|
709
244
|
}
|
|
710
245
|
|
|
@@ -715,42 +250,32 @@ async function main() {
|
|
|
715
250
|
|
|
716
251
|
switch (command) {
|
|
717
252
|
case 'init':
|
|
718
|
-
|
|
719
|
-
const initOptions = {
|
|
720
|
-
bare: args.includes('--bare'),
|
|
721
|
-
smart: !args.includes('--no-smart'),
|
|
722
|
-
deep: args.includes('--deep'),
|
|
723
|
-
generateAgents: args.includes('--generate-agents'),
|
|
724
|
-
generateDomains: args.includes('--generate-domains')
|
|
725
|
-
};
|
|
726
|
-
await initagentful(initOptions);
|
|
253
|
+
await init();
|
|
727
254
|
break;
|
|
728
255
|
|
|
729
256
|
case 'status':
|
|
730
257
|
showStatus();
|
|
731
258
|
break;
|
|
732
259
|
|
|
733
|
-
case '
|
|
734
|
-
await generateAgents();
|
|
735
|
-
break;
|
|
736
|
-
|
|
260
|
+
case 'help':
|
|
737
261
|
case '--help':
|
|
738
262
|
case '-h':
|
|
739
|
-
case 'help':
|
|
740
263
|
showHelp();
|
|
741
264
|
break;
|
|
742
265
|
|
|
266
|
+
case 'version':
|
|
743
267
|
case '--version':
|
|
744
268
|
case '-v':
|
|
745
269
|
showVersion();
|
|
746
270
|
break;
|
|
747
271
|
|
|
748
272
|
default:
|
|
749
|
-
if (!command
|
|
273
|
+
if (!command) {
|
|
750
274
|
showHelp();
|
|
751
275
|
} else {
|
|
752
276
|
log(colors.red, `Unknown command: ${command}`);
|
|
753
|
-
log(
|
|
277
|
+
console.log('');
|
|
278
|
+
log(colors.dim, 'Run: agentful help');
|
|
754
279
|
process.exit(1);
|
|
755
280
|
}
|
|
756
281
|
}
|