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