@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/bin/cli.js CHANGED
@@ -1,25 +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 { 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 in current directory`);
61
- console.log(` ${colors.green}init --bare${colors.reset} Skip creating templates (just .claude/)`);
62
- console.log(` ${colors.green}init --no-smart${colors.reset} Skip smart analysis (basic init only)`);
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}agentful automatically detects the best structure:${colors.reset}`);
73
- console.log(` ${colors.green} Small projects:${colors.reset} Creates PRODUCT.md (simple, flat structure)`);
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('FOR EXTENDED DEVELOPMENT SESSIONS:');
81
- 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`);
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 initagentful(options = {}) {
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 (fs.existsSync(claudeDir)) {
140
- log(colors.yellow, '⚠️ .claude/ directory already exists!');
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
- log(colors.dim, 'Removing existing .claude/...');
158
- fs.rmSync(claudeDir, { recursive: true, force: true });
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
- // Determine if project should use hierarchical structure
251
- const shouldUseHierarchical = analysis && (
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
- // Update .gitignore
341
- checkGitignore();
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
- const readline = await import('readline');
393
- const rl = readline.createInterface({
394
- input: process.stdin,
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
- log(colors.dim, '🤖 Generating agents and domain structure...');
413
- await generateAgentsAndDomains(targetDir, analysis, {
414
- agents: autoGenerateAgents,
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
- // Done!
149
+ // Update .gitignore
150
+ checkGitignore();
151
+
152
+ // Show next steps
420
153
  console.log('');
421
- log(colors.green, ' agentful initialized successfully!');
154
+ log(colors.bright, 'Next Steps:');
422
155
  console.log('');
423
-
424
- // Determine which structure was created
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
- console.log(` 1. ${colors.cyan}Edit your product specification${colors.reset}`);
433
-
434
- if (usingHierarchical) {
435
- console.log(` ${colors.green}✓ Created .claude/product/index.md${colors.reset} (hierarchical structure)`);
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 agentfulDir = path.join(process.cwd(), '.agentful');
169
+ const targetDir = process.cwd();
170
+ const agentfulDir = path.join(targetDir, '.agentful');
505
171
 
506
172
  if (!fs.existsSync(agentfulDir)) {
507
- log(colors.red, 'agentful not initialized in this directory!');
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
- // Show current work
535
- if (state.current_task) {
536
- log(colors.blue, `🔧 Working on: ${colors.reset}${state.current_task}`);
537
- log(colors.dim, ` Phase: ${state.current_phase}`);
538
- log(colors.dim, ` Iterations: ${state.iterations}`);
539
- } else {
540
- log(colors.dim, '💤 Idle - no active task');
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
- console.log('');
544
-
545
- // Show completion if available
207
+ // Display completion
546
208
  if (completion) {
547
- const percentage = completion.overall || 0;
548
- const filled = Math.round(percentage / 5);
549
- const bar = '█'.repeat(filled) + '░'.repeat(20 - filled);
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
- // Show next action
579
- log(colors.bright, 'Next Actions:');
580
- log(colors.cyan, ' • /agentful-start - Continue development');
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
- architecture.detected_stack = stack;
683
- architecture.timestamp = new Date().toISOString();
684
-
685
- fs.writeFileSync(archPath, JSON.stringify(architecture, null, 2));
686
-
687
- console.log('');
688
- log(colors.bright, 'Tech Stack Analysis Complete!');
689
- console.log('');
690
-
691
- log(colors.bright, 'Detected Stack:');
692
- if (stack.language) log(colors.cyan, ` Language: ${stack.language}`);
693
- if (primaryFramework) log(colors.cyan, ` Framework: ${primaryFramework}`);
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
- log(colors.bright, 'Next Steps:');
700
- console.log('');
701
- log(colors.cyan, ' 1. Review .agentful/architecture.json');
702
- log(colors.cyan, ' 2. Run: claude');
703
- log(colors.cyan, ' 3. Type: /agentful-start');
704
- log(colors.cyan, ' (Architect agent will generate specialized agents automatically)');
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
- // Parse init options
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 'generate':
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 || command.startsWith('-')) {
273
+ if (!command) {
750
274
  showHelp();
751
275
  } else {
752
276
  log(colors.red, `Unknown command: ${command}`);
753
- log(colors.dim, 'Run: agentful --help');
277
+ console.log('');
278
+ log(colors.dim, 'Run: agentful help');
754
279
  process.exit(1);
755
280
  }
756
281
  }