@itz4blitz/agentful 0.2.0 → 0.2.1
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/product/README.md +32 -0
- package/bin/cli.js +10 -82
- package/lib/agent-generator.js +123 -30
- package/lib/domain-detector.js +4 -4
- package/lib/template-engine.js +87 -0
- package/package.json +9 -3
- package/templates/agents/domain-agent.template.md +208 -0
- package/templates/agents/tech-agent.template.md +124 -0
|
@@ -301,6 +301,38 @@ For hierarchical structure examples, check:
|
|
|
301
301
|
|
|
302
302
|
**No**: The system auto-detects ONE format. Choose the format that best fits your project size and complexity.
|
|
303
303
|
|
|
304
|
+
## Product Analysis File
|
|
305
|
+
|
|
306
|
+
After running `/agentful-product`, a `product-analysis.json` file is generated:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
.claude/product/
|
|
310
|
+
├── index.md # Your product spec (template)
|
|
311
|
+
└── product-analysis.json # Generated analysis (created by /agentful-product)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Key points:**
|
|
315
|
+
- **Not in templates**: This file doesn't exist in the agentful package itself
|
|
316
|
+
- **Generated on demand**: Created when you run `/agentful-product` in your project
|
|
317
|
+
- **Purpose**: Analyzes your product spec for completeness, clarity, feasibility, testability, and consistency
|
|
318
|
+
- **Scores readiness**: 0-100% score with blocking issues and recommendations
|
|
319
|
+
- **Version controlled**: Commit to git to track spec quality improvements over time
|
|
320
|
+
|
|
321
|
+
**Example content:**
|
|
322
|
+
```json
|
|
323
|
+
{
|
|
324
|
+
"version": "1.0",
|
|
325
|
+
"timestamp": "2026-01-20T00:00:00Z",
|
|
326
|
+
"readiness_score": 75,
|
|
327
|
+
"dimensions": {
|
|
328
|
+
"completeness": { "score": 85 },
|
|
329
|
+
"clarity": { "score": 90 }
|
|
330
|
+
},
|
|
331
|
+
"blocking_issues": [],
|
|
332
|
+
"can_start_development": true
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
304
336
|
## Summary
|
|
305
337
|
|
|
306
338
|
| Format | Best For | Tracking | File Structure |
|
package/bin/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ import { fileURLToPath } from 'url';
|
|
|
11
11
|
import { analyzeProject, exportToArchitectureJson } from '../lib/project-analyzer.js';
|
|
12
12
|
import AgentGenerator from '../lib/agent-generator.js';
|
|
13
13
|
import DomainStructureGenerator from '../lib/domain-structure-generator.js';
|
|
14
|
+
import { detectTechStack } from '../lib/tech-stack-detector.js';
|
|
14
15
|
|
|
15
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
17
|
const __dirname = path.dirname(__filename);
|
|
@@ -582,87 +583,13 @@ function showStatus() {
|
|
|
582
583
|
console.log('');
|
|
583
584
|
}
|
|
584
585
|
|
|
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)) {
|
|
597
|
-
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');
|
|
617
|
-
}
|
|
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
586
|
function generateAgentPrompt(stack) {
|
|
661
587
|
let prompt = `# Tech Stack Analysis\n\n`;
|
|
662
588
|
prompt += `**Language**: ${stack.language || 'Unknown'}\n`;
|
|
663
|
-
|
|
589
|
+
const primaryFramework = stack.frameworks && stack.frameworks.length > 0 ? stack.frameworks[0] : null;
|
|
590
|
+
prompt += `**Framework**: ${primaryFramework || 'None'}\n\n`;
|
|
664
591
|
|
|
665
|
-
if (stack.dependencies.length > 0) {
|
|
592
|
+
if (stack.dependencies && stack.dependencies.length > 0) {
|
|
666
593
|
prompt += `**Key Dependencies**:\n`;
|
|
667
594
|
stack.dependencies.slice(0, 10).forEach(dep => {
|
|
668
595
|
prompt += `- ${dep}\n`;
|
|
@@ -724,11 +651,11 @@ async function generateAgents() {
|
|
|
724
651
|
process.exit(1);
|
|
725
652
|
}
|
|
726
653
|
|
|
727
|
-
// Detect tech stack
|
|
654
|
+
// Detect tech stack using library function
|
|
728
655
|
log(colors.dim, 'Analyzing tech stack...');
|
|
729
|
-
const stack = detectTechStack();
|
|
656
|
+
const stack = await detectTechStack(process.cwd());
|
|
730
657
|
|
|
731
|
-
if (!stack.language) {
|
|
658
|
+
if (!stack.language || stack.language === 'unknown') {
|
|
732
659
|
log(colors.yellow, '⚠️ Could not detect language/framework');
|
|
733
660
|
log(colors.dim, 'Supported: Node.js, Python, Go, Rust, C#, Java');
|
|
734
661
|
console.log('');
|
|
@@ -739,7 +666,8 @@ async function generateAgents() {
|
|
|
739
666
|
return;
|
|
740
667
|
}
|
|
741
668
|
|
|
742
|
-
|
|
669
|
+
const primaryFramework = stack.frameworks.length > 0 ? stack.frameworks[0] : null;
|
|
670
|
+
log(colors.green, `✓ Detected: ${stack.language} ${primaryFramework ? `(${primaryFramework})` : ''}`);
|
|
743
671
|
log(colors.dim, ` Dependencies: ${stack.dependencies.length} packages`);
|
|
744
672
|
|
|
745
673
|
// Update architecture.json
|
|
@@ -762,7 +690,7 @@ async function generateAgents() {
|
|
|
762
690
|
|
|
763
691
|
log(colors.bright, 'Detected Stack:');
|
|
764
692
|
if (stack.language) log(colors.cyan, ` Language: ${stack.language}`);
|
|
765
|
-
if (
|
|
693
|
+
if (primaryFramework) log(colors.cyan, ` Framework: ${primaryFramework}`);
|
|
766
694
|
if (stack.dependencies.length > 0) {
|
|
767
695
|
log(colors.cyan, ` Dependencies: ${stack.dependencies.length} packages`);
|
|
768
696
|
}
|
package/lib/agent-generator.js
CHANGED
|
@@ -13,12 +13,124 @@ import TemplateEngine from './template-engine.js';
|
|
|
13
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
14
|
const __dirname = path.dirname(__filename);
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Default core agent types registry
|
|
18
|
+
* Users can extend this by passing custom agent types to the constructor
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_CORE_AGENT_TYPES = ['backend', 'frontend', 'tester', 'reviewer', 'fixer'];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Default agent patterns configuration
|
|
24
|
+
* Defines how each agent type should extract patterns from the codebase
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_AGENT_PATTERNS = {
|
|
27
|
+
backend: {
|
|
28
|
+
directories: ['src/repositories', 'src/services', 'src/controllers', 'src/routes', 'api'],
|
|
29
|
+
keywords: ['repository', 'service', 'controller', 'route', 'handler'],
|
|
30
|
+
},
|
|
31
|
+
frontend: {
|
|
32
|
+
directories: ['src/components', 'src/pages', 'src/app', 'components', 'pages'],
|
|
33
|
+
keywords: ['component', 'hook', 'page', 'view'],
|
|
34
|
+
},
|
|
35
|
+
tester: {
|
|
36
|
+
directories: ['tests', 'test', '__tests__', '__tests__', 'spec'],
|
|
37
|
+
keywords: ['describe', 'test', 'it', 'expect', 'mock'],
|
|
38
|
+
},
|
|
39
|
+
reviewer: {
|
|
40
|
+
directories: ['src'],
|
|
41
|
+
keywords: ['export', 'function', 'class', 'interface'],
|
|
42
|
+
},
|
|
43
|
+
fixer: {
|
|
44
|
+
directories: ['src'],
|
|
45
|
+
keywords: ['error', 'bug', 'fix', 'throw'],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
16
49
|
class AgentGenerator {
|
|
17
|
-
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} projectPath - Path to the project
|
|
52
|
+
* @param {object} analysis - Project analysis results
|
|
53
|
+
* @param {object} options - Configuration options
|
|
54
|
+
* @param {string[]} options.customAgentTypes - Additional core agent types to generate
|
|
55
|
+
* @param {object} options.customAgentPatterns - Pattern configurations for custom agent types
|
|
56
|
+
*/
|
|
57
|
+
constructor(projectPath, analysis, options = {}) {
|
|
18
58
|
this.projectPath = projectPath;
|
|
19
59
|
this.analysis = analysis;
|
|
20
60
|
this.templatesDir = path.join(__dirname, '../templates/agents');
|
|
21
61
|
this.agentsDir = path.join(projectPath, '.claude/agents/auto-generated');
|
|
62
|
+
|
|
63
|
+
// Extensible agent type registry
|
|
64
|
+
this.coreAgentTypes = [...DEFAULT_CORE_AGENT_TYPES];
|
|
65
|
+
this.agentPatterns = { ...DEFAULT_AGENT_PATTERNS };
|
|
66
|
+
|
|
67
|
+
// Apply custom agent types if provided
|
|
68
|
+
if (options.customAgentTypes && Array.isArray(options.customAgentTypes)) {
|
|
69
|
+
this.coreAgentTypes.push(...options.customAgentTypes);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Apply custom agent patterns if provided
|
|
73
|
+
if (options.customAgentPatterns && typeof options.customAgentPatterns === 'object') {
|
|
74
|
+
Object.assign(this.agentPatterns, options.customAgentPatterns);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Register a custom agent type at runtime
|
|
80
|
+
* @param {string} type - Agent type name
|
|
81
|
+
* @param {object} pattern - Pattern configuration for the agent type
|
|
82
|
+
* @param {string[]} pattern.directories - Directories to scan for this agent type
|
|
83
|
+
* @param {string[]} pattern.keywords - Keywords to look for in code
|
|
84
|
+
*/
|
|
85
|
+
registerAgentType(type, pattern) {
|
|
86
|
+
if (!this.coreAgentTypes.includes(type)) {
|
|
87
|
+
this.coreAgentTypes.push(type);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (pattern) {
|
|
91
|
+
this.agentPatterns[type] = pattern;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get all registered core agent types
|
|
97
|
+
* @returns {string[]} List of core agent types
|
|
98
|
+
*/
|
|
99
|
+
getCoreAgentTypes() {
|
|
100
|
+
return [...this.coreAgentTypes];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get pattern configuration for a specific agent type
|
|
105
|
+
* @param {string} type - Agent type name
|
|
106
|
+
* @returns {object|null} Pattern configuration or null if not found
|
|
107
|
+
*/
|
|
108
|
+
getAgentPattern(type) {
|
|
109
|
+
return this.agentPatterns[type] || null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Load custom agent types from a configuration file
|
|
114
|
+
* @param {string} configPath - Path to the configuration file (JSON)
|
|
115
|
+
* @returns {Promise<void>}
|
|
116
|
+
*/
|
|
117
|
+
async loadCustomAgentTypesFromConfig(configPath) {
|
|
118
|
+
try {
|
|
119
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
120
|
+
const config = JSON.parse(content);
|
|
121
|
+
|
|
122
|
+
if (config.customAgentTypes && Array.isArray(config.customAgentTypes)) {
|
|
123
|
+
for (const agentConfig of config.customAgentTypes) {
|
|
124
|
+
if (agentConfig.type && agentConfig.pattern) {
|
|
125
|
+
this.registerAgentType(agentConfig.type, agentConfig.pattern);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log(`✅ Loaded custom agent types from ${configPath}`);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.warn(`⚠️ Could not load custom agent types from ${configPath}: ${error.message}`);
|
|
133
|
+
}
|
|
22
134
|
}
|
|
23
135
|
|
|
24
136
|
/**
|
|
@@ -59,11 +171,9 @@ class AgentGenerator {
|
|
|
59
171
|
* Generate core agents (always needed)
|
|
60
172
|
*/
|
|
61
173
|
async generateCoreAgents() {
|
|
62
|
-
const coreAgentTypes = ['backend', 'frontend', 'tester', 'reviewer', 'fixer'];
|
|
63
|
-
|
|
64
174
|
const agents = [];
|
|
65
175
|
|
|
66
|
-
for (const type of coreAgentTypes) {
|
|
176
|
+
for (const type of this.coreAgentTypes) {
|
|
67
177
|
const agentPath = path.join(this.agentsDir, `${type}.md`);
|
|
68
178
|
const template = await this.loadTemplate(`${type}-agent.template.md`);
|
|
69
179
|
|
|
@@ -254,32 +364,12 @@ class AgentGenerator {
|
|
|
254
364
|
samples: [],
|
|
255
365
|
};
|
|
256
366
|
|
|
257
|
-
//
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
frontend: {
|
|
264
|
-
directories: ['src/components', 'src/pages', 'src/app', 'components', 'pages'],
|
|
265
|
-
keywords: ['component', 'hook', 'page', 'view'],
|
|
266
|
-
},
|
|
267
|
-
tester: {
|
|
268
|
-
directories: ['tests', 'test', '__tests__', '__tests__', 'spec'],
|
|
269
|
-
keywords: ['describe', 'test', 'it', 'expect', 'mock'],
|
|
270
|
-
},
|
|
271
|
-
reviewer: {
|
|
272
|
-
directories: ['src'],
|
|
273
|
-
keywords: ['export', 'function', 'class', 'interface'],
|
|
274
|
-
},
|
|
275
|
-
fixer: {
|
|
276
|
-
directories: ['src'],
|
|
277
|
-
keywords: ['error', 'bug', 'fix', 'throw'],
|
|
278
|
-
},
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
const config = agentPatterns[agentType];
|
|
282
|
-
if (!config) return patterns;
|
|
367
|
+
// Get pattern configuration from registry
|
|
368
|
+
const config = this.agentPatterns[agentType];
|
|
369
|
+
if (!config) {
|
|
370
|
+
console.warn(`⚠️ No pattern configuration found for agent type: ${agentType}`);
|
|
371
|
+
return patterns;
|
|
372
|
+
}
|
|
283
373
|
|
|
284
374
|
// Scan directories for patterns
|
|
285
375
|
for (const dir of config.directories) {
|
|
@@ -683,3 +773,6 @@ class AgentGenerator {
|
|
|
683
773
|
}
|
|
684
774
|
|
|
685
775
|
export default AgentGenerator;
|
|
776
|
+
|
|
777
|
+
// Export default configurations for use by consumers
|
|
778
|
+
export { DEFAULT_CORE_AGENT_TYPES, DEFAULT_AGENT_PATTERNS };
|
package/lib/domain-detector.js
CHANGED
|
@@ -198,9 +198,9 @@ async function analyzeDirectoryStructure(projectRoot) {
|
|
|
198
198
|
if (dirName.includes(keyword.toLowerCase())) {
|
|
199
199
|
// Exact match gets higher score
|
|
200
200
|
if (dirName === keyword.toLowerCase()) {
|
|
201
|
-
signals[domain] = Math.min(signals[domain] || 0
|
|
201
|
+
signals[domain] = Math.min((signals[domain] || 0) + 0.8, 1.0);
|
|
202
202
|
} else {
|
|
203
|
-
signals[domain] = Math.min(signals[domain] || 0
|
|
203
|
+
signals[domain] = Math.min((signals[domain] || 0) + 0.5, 1.0);
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
}
|
|
@@ -217,7 +217,7 @@ async function analyzeDirectoryStructure(projectRoot) {
|
|
|
217
217
|
for (const [domain, pattern] of Object.entries(DOMAIN_PATTERNS)) {
|
|
218
218
|
for (const keyword of pattern.keywords) {
|
|
219
219
|
if (subDirName.includes(keyword.toLowerCase())) {
|
|
220
|
-
signals[domain] = Math.min(signals[domain] || 0
|
|
220
|
+
signals[domain] = Math.min((signals[domain] || 0) + 0.3, 1.0);
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
}
|
|
@@ -298,7 +298,7 @@ function analyzeFileForDomains(filePath, signals) {
|
|
|
298
298
|
for (const keyword of pattern.keywords) {
|
|
299
299
|
// Check filename
|
|
300
300
|
if (fileName.includes(keyword.toLowerCase())) {
|
|
301
|
-
signals[domain] = Math.min(signals[domain] || 0
|
|
301
|
+
signals[domain] = Math.min((signals[domain] || 0) + 0.4, 1.0);
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
// Check content with regex for word boundaries
|
package/lib/template-engine.js
CHANGED
|
@@ -13,8 +13,13 @@ class TemplateEngine {
|
|
|
13
13
|
let content = template;
|
|
14
14
|
|
|
15
15
|
// Handle arrays and objects with special formatting FIRST
|
|
16
|
+
// This allows formatComplexTypes to format arrays before conditionals/loops process them
|
|
16
17
|
content = this.formatComplexTypes(content, data);
|
|
17
18
|
|
|
19
|
+
// Handle conditionals and loops (after complex types are formatted)
|
|
20
|
+
content = this.processConditionals(content, data);
|
|
21
|
+
content = this.processLoops(content, data);
|
|
22
|
+
|
|
18
23
|
// Replace simple variables: {{variable}}
|
|
19
24
|
content = this.replaceVariables(content, data);
|
|
20
25
|
|
|
@@ -26,6 +31,88 @@ class TemplateEngine {
|
|
|
26
31
|
return content;
|
|
27
32
|
}
|
|
28
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Process {{#if variable}}...{{else}}...{{/if}} conditionals
|
|
36
|
+
*/
|
|
37
|
+
static processConditionals(content, data) {
|
|
38
|
+
// Match {{#if variable}}...{{else}}...{{/if}} or {{#if variable}}...{{/if}}
|
|
39
|
+
const ifRegex = /\{\{#if\s+(\w+(?:\.\w+)*)\s*\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if\}\}/g;
|
|
40
|
+
|
|
41
|
+
return content.replace(ifRegex, (match, variable, trueBlock, falseBlock) => {
|
|
42
|
+
const value = this.getNestedValue(data, variable);
|
|
43
|
+
const isTruthy = this.isTruthy(value);
|
|
44
|
+
|
|
45
|
+
if (isTruthy) {
|
|
46
|
+
return trueBlock || '';
|
|
47
|
+
} else {
|
|
48
|
+
return falseBlock || '';
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Process {{#each array}}...{{/each}} loops
|
|
55
|
+
*/
|
|
56
|
+
static processLoops(content, data) {
|
|
57
|
+
// Match {{#each variable}}...{{/each}}
|
|
58
|
+
const eachRegex = /\{\{#each\s+(\w+(?:\.\w+)*)\s*\}\}([\s\S]*?)\{\{\/each\}\}/g;
|
|
59
|
+
|
|
60
|
+
return content.replace(eachRegex, (match, variable, block) => {
|
|
61
|
+
const array = this.getNestedValue(data, variable);
|
|
62
|
+
|
|
63
|
+
if (!Array.isArray(array) || array.length === 0) {
|
|
64
|
+
return '';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return array.map(item => {
|
|
68
|
+
let itemContent = block;
|
|
69
|
+
|
|
70
|
+
// Handle {{this}} for primitive values
|
|
71
|
+
if (typeof item !== 'object') {
|
|
72
|
+
itemContent = itemContent.replace(/\{\{this\}\}/g, String(item));
|
|
73
|
+
} else {
|
|
74
|
+
// Handle {{this.property}} for objects
|
|
75
|
+
itemContent = itemContent.replace(/\{\{this\.(\w+)\}\}/g, (m, prop) => {
|
|
76
|
+
const value = item[prop];
|
|
77
|
+
return value !== undefined ? String(value) : '';
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Handle {{this}} for objects
|
|
81
|
+
// Smart default: if object has 'code' or 'name' property, use that; otherwise stringify
|
|
82
|
+
itemContent = itemContent.replace(/\{\{this\}\}/g, () => {
|
|
83
|
+
if (item.code !== undefined) return String(item.code);
|
|
84
|
+
if (item.name !== undefined) return String(item.name);
|
|
85
|
+
return JSON.stringify(item);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return itemContent;
|
|
90
|
+
}).join('');
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get nested value from object using dot notation
|
|
96
|
+
*/
|
|
97
|
+
static getNestedValue(obj, path) {
|
|
98
|
+
return path.split('.').reduce((current, prop) => {
|
|
99
|
+
return current?.[prop];
|
|
100
|
+
}, obj);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Check if value is truthy (for conditionals)
|
|
105
|
+
*/
|
|
106
|
+
static isTruthy(value) {
|
|
107
|
+
if (value === null || value === undefined) return false;
|
|
108
|
+
if (typeof value === 'boolean') return value;
|
|
109
|
+
if (typeof value === 'number') return value !== 0;
|
|
110
|
+
if (typeof value === 'string') return value.length > 0;
|
|
111
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
112
|
+
if (typeof value === 'object') return Object.keys(value).length > 0;
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
|
|
29
116
|
/**
|
|
30
117
|
* Replace simple variables
|
|
31
118
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itz4blitz/agentful",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Human-in-the-loop development kit for Claude Code with smart product analysis and natural conversation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,17 +10,23 @@
|
|
|
10
10
|
"init": "node bin/cli.js init",
|
|
11
11
|
"release": "semantic-release",
|
|
12
12
|
"release:dry-run": "semantic-release --dry-run",
|
|
13
|
-
"version": "node -e \"const pkg=require('./package.json'); const v=require('./version.json'); v.version=pkg.version; require('fs').writeFileSync('version.json', JSON.stringify(v, null, 2));\"",
|
|
14
13
|
"docs:dev": "vocs dev",
|
|
15
14
|
"docs:build": "vocs build",
|
|
16
15
|
"docs:preview": "vocs preview"
|
|
17
16
|
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"import": "./lib/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
18
22
|
"files": [
|
|
19
23
|
"bin/",
|
|
20
24
|
"lib/",
|
|
21
25
|
"template/",
|
|
26
|
+
"templates/",
|
|
22
27
|
".claude/",
|
|
23
|
-
"version.json"
|
|
28
|
+
"version.json",
|
|
29
|
+
"LICENSE"
|
|
24
30
|
],
|
|
25
31
|
"keywords": [
|
|
26
32
|
"claude-code",
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: {{domain}}-agent
|
|
3
|
+
description: Specialized agent for {{domain}} domain with context-aware knowledge of codebase patterns, conventions, and existing implementations.
|
|
4
|
+
model: sonnet
|
|
5
|
+
tools: Read, Write, Edit, Glob, Grep, Bash
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{domain}} Agent
|
|
9
|
+
|
|
10
|
+
You are the **{{domain}}** domain specialist. You have deep knowledge of this project's {{domain}} implementation, patterns, and conventions.
|
|
11
|
+
|
|
12
|
+
## Domain Context
|
|
13
|
+
|
|
14
|
+
**Confidence**: {{confidence}}%
|
|
15
|
+
**Language**: {{language}}
|
|
16
|
+
**Detected Features**: {{features}}
|
|
17
|
+
|
|
18
|
+
## Your Scope
|
|
19
|
+
|
|
20
|
+
You work exclusively on **{{domain}}**-related functionality:
|
|
21
|
+
{{#if features}}
|
|
22
|
+
{{#each features}}
|
|
23
|
+
- **{{this.name}}** - {{this.description}}
|
|
24
|
+
{{/each}}
|
|
25
|
+
{{else}}
|
|
26
|
+
- All {{domain}} domain features and business logic
|
|
27
|
+
- {{domain}}-specific APIs and endpoints
|
|
28
|
+
- {{domain}} data models and schemas
|
|
29
|
+
- {{domain}} services and repositories
|
|
30
|
+
{{/if}}
|
|
31
|
+
|
|
32
|
+
## Codebase Knowledge
|
|
33
|
+
|
|
34
|
+
This project uses:
|
|
35
|
+
- **Language**: {{language}}
|
|
36
|
+
- **Framework**: {{framework}}
|
|
37
|
+
- **Confidence**: {{confidence}}%
|
|
38
|
+
|
|
39
|
+
### Project Conventions
|
|
40
|
+
|
|
41
|
+
{{conventions}}
|
|
42
|
+
|
|
43
|
+
### Code Samples from This Project
|
|
44
|
+
|
|
45
|
+
{{code_samples}}
|
|
46
|
+
|
|
47
|
+
### Detected Patterns
|
|
48
|
+
|
|
49
|
+
{{patterns}}
|
|
50
|
+
|
|
51
|
+
## Implementation Guidelines
|
|
52
|
+
|
|
53
|
+
### 1. Follow Existing Patterns
|
|
54
|
+
|
|
55
|
+
Before implementing anything, study the existing code samples above. Match:
|
|
56
|
+
- Naming conventions (camelCase, PascalCase, etc.)
|
|
57
|
+
- File structure and organization
|
|
58
|
+
- Import/export patterns
|
|
59
|
+
- Error handling style
|
|
60
|
+
- Code formatting and spacing
|
|
61
|
+
|
|
62
|
+
### 2. Stay Within Domain
|
|
63
|
+
|
|
64
|
+
**DO**:
|
|
65
|
+
- Implement {{domain}} features
|
|
66
|
+
- Modify {{domain}} services, repositories, controllers
|
|
67
|
+
- Update {{domain}} data models
|
|
68
|
+
- Add {{domain}} API endpoints
|
|
69
|
+
- Fix bugs in {{domain}} code
|
|
70
|
+
|
|
71
|
+
**DON'T**:
|
|
72
|
+
- Modify other domains (delegate to those agents)
|
|
73
|
+
- Change frontend UI components (delegate to @frontend)
|
|
74
|
+
- Modify infrastructure (delegate to @backend)
|
|
75
|
+
- Break existing {{domain}} contracts
|
|
76
|
+
|
|
77
|
+
### 3. Maintain Consistency
|
|
78
|
+
|
|
79
|
+
Always use the project's existing patterns:
|
|
80
|
+
{{#if patterns}}
|
|
81
|
+
{{#each patterns}}
|
|
82
|
+
- {{this}}
|
|
83
|
+
{{/each}}
|
|
84
|
+
{{/if}}
|
|
85
|
+
|
|
86
|
+
## Common Tasks
|
|
87
|
+
|
|
88
|
+
### Adding New {{domain}} Feature
|
|
89
|
+
|
|
90
|
+
1. **Check existing code first** - Use the samples above
|
|
91
|
+
2. **Follow the architecture**:
|
|
92
|
+
```
|
|
93
|
+
src/
|
|
94
|
+
├── domains/{{domain}}/
|
|
95
|
+
│ ├── repositories/ # Data access
|
|
96
|
+
│ ├── services/ # Business logic
|
|
97
|
+
│ ├── controllers/ # HTTP handlers
|
|
98
|
+
│ ├── models/ # Data models
|
|
99
|
+
│ └── types/ # TypeScript types
|
|
100
|
+
```
|
|
101
|
+
3. **Use existing patterns** from code samples
|
|
102
|
+
4. **Test thoroughly** - Delegate to @tester
|
|
103
|
+
|
|
104
|
+
### Modifying Existing {{domain}} Code
|
|
105
|
+
|
|
106
|
+
1. Read the existing implementation
|
|
107
|
+
2. Understand the current patterns
|
|
108
|
+
3. Make minimal changes
|
|
109
|
+
4. Ensure backward compatibility
|
|
110
|
+
5. Test thoroughly
|
|
111
|
+
|
|
112
|
+
### API Endpoints
|
|
113
|
+
|
|
114
|
+
{{#if endpoints}}
|
|
115
|
+
Existing {{domain}} endpoints:
|
|
116
|
+
{{#each endpoints}}
|
|
117
|
+
- `{{this}}`
|
|
118
|
+
{{/each}}
|
|
119
|
+
{{/if}}
|
|
120
|
+
|
|
121
|
+
When adding new endpoints:
|
|
122
|
+
1. Follow existing endpoint patterns
|
|
123
|
+
2. Use proper HTTP methods
|
|
124
|
+
3. Implement validation
|
|
125
|
+
4. Add error handling
|
|
126
|
+
5. Document with JSDoc
|
|
127
|
+
|
|
128
|
+
### Data Models
|
|
129
|
+
|
|
130
|
+
{{#if models}}
|
|
131
|
+
Existing {{domain}} models:
|
|
132
|
+
{{#each models}}
|
|
133
|
+
- `{{this}}`
|
|
134
|
+
{{/each}}
|
|
135
|
+
{{/if}}
|
|
136
|
+
|
|
137
|
+
When adding/modifying models:
|
|
138
|
+
1. Check existing model patterns
|
|
139
|
+
2. Use proper types
|
|
140
|
+
3. Add validation rules
|
|
141
|
+
4. Document fields
|
|
142
|
+
5. Consider migrations
|
|
143
|
+
|
|
144
|
+
## Rules
|
|
145
|
+
|
|
146
|
+
1. **ALWAYS** read existing code before implementing
|
|
147
|
+
2. **ALWAYS** follow project conventions (see samples above)
|
|
148
|
+
3. **ALWAYS** stay within your domain scope
|
|
149
|
+
4. **NEVER** break existing patterns
|
|
150
|
+
5. **NEVER** modify other domains without permission
|
|
151
|
+
6. **ALWAYS** test your changes
|
|
152
|
+
7. **ALWAYS** use TypeScript strict mode (if applicable)
|
|
153
|
+
8. **NEVER** skip error handling
|
|
154
|
+
|
|
155
|
+
## Integration with Other Agents
|
|
156
|
+
|
|
157
|
+
- **@backend** - For infrastructure-level changes
|
|
158
|
+
- **@frontend** - For UI components consuming your APIs
|
|
159
|
+
- **@tester** - For testing your implementations
|
|
160
|
+
- **@reviewer** - For code review
|
|
161
|
+
- **@fixer** - For bug fixes
|
|
162
|
+
|
|
163
|
+
## After Implementation
|
|
164
|
+
|
|
165
|
+
Always report:
|
|
166
|
+
- Files created/modified
|
|
167
|
+
- What was implemented
|
|
168
|
+
- Breaking changes (if any)
|
|
169
|
+
- What needs testing (delegate to @tester)
|
|
170
|
+
- API endpoints added/modified
|
|
171
|
+
|
|
172
|
+
## Examples
|
|
173
|
+
|
|
174
|
+
### Example: Following Project Patterns
|
|
175
|
+
|
|
176
|
+
Based on the code samples above, when implementing a new {{domain}} feature:
|
|
177
|
+
|
|
178
|
+
```{{language}}
|
|
179
|
+
// Match the project's existing style
|
|
180
|
+
export class {{domain}}Service {
|
|
181
|
+
constructor(private repo: {{domain}}Repository) {}
|
|
182
|
+
|
|
183
|
+
async performAction(input: InputType): Promise<ResultType> {
|
|
184
|
+
// Follow the error handling pattern used in the project
|
|
185
|
+
try {
|
|
186
|
+
const existing = await this.repo.findById(input.id);
|
|
187
|
+
if (!existing) {
|
|
188
|
+
throw new NotFoundError('Resource not found');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Apply business logic
|
|
192
|
+
const result = await this.repo.update(input.id, input);
|
|
193
|
+
return result;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
// Use the project's error handling pattern
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Remember**: The code samples above are your guide. Match the style exactly.
|
|
203
|
+
|
|
204
|
+
## Domain-Specific Notes
|
|
205
|
+
|
|
206
|
+
{{domain_notes}}
|
|
207
|
+
|
|
208
|
+
Auto-generated based on project analysis. Last updated: {{generated_at}}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: {{tech}}-agent
|
|
3
|
+
description: {{techType}} specialist with deep knowledge of {{tech}} patterns, best practices, and project-specific conventions.
|
|
4
|
+
model: sonnet
|
|
5
|
+
tools: Read, Write, Edit, Glob, Grep, Bash
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{tech}} Agent
|
|
9
|
+
|
|
10
|
+
You are the **{{tech}}** specialist. You have expert knowledge of {{tech}} and understand how it's used in this project.
|
|
11
|
+
|
|
12
|
+
## Technology Context
|
|
13
|
+
|
|
14
|
+
**Technology**: {{tech}}
|
|
15
|
+
**Type**: {{techType}}
|
|
16
|
+
**Language**: {{language}}
|
|
17
|
+
|
|
18
|
+
## Project Implementation
|
|
19
|
+
|
|
20
|
+
This project uses {{tech}} with these patterns and conventions:
|
|
21
|
+
|
|
22
|
+
### Detected Patterns
|
|
23
|
+
|
|
24
|
+
{{patterns}}
|
|
25
|
+
|
|
26
|
+
### Project Conventions
|
|
27
|
+
|
|
28
|
+
{{conventions}}
|
|
29
|
+
|
|
30
|
+
### Code Samples from This Project
|
|
31
|
+
|
|
32
|
+
{{samples}}
|
|
33
|
+
|
|
34
|
+
## Your Expertise
|
|
35
|
+
|
|
36
|
+
You are an expert in {{tech}} usage within this specific codebase.
|
|
37
|
+
|
|
38
|
+
### Common {{tech}} Tasks
|
|
39
|
+
|
|
40
|
+
- Following project-specific {{tech}} patterns
|
|
41
|
+
- Implementing {{tech}} features using existing conventions
|
|
42
|
+
- Optimizing {{tech}} performance
|
|
43
|
+
- Debugging {{tech}} issues
|
|
44
|
+
- Maintaining consistency with existing code
|
|
45
|
+
|
|
46
|
+
## Implementation Guidelines
|
|
47
|
+
|
|
48
|
+
### 1. Follow Project Conventions
|
|
49
|
+
|
|
50
|
+
Before making changes, study the code samples above. Match:
|
|
51
|
+
- The project's specific usage of {{tech}}
|
|
52
|
+
- Configuration patterns
|
|
53
|
+
- File organization
|
|
54
|
+
- Naming conventions
|
|
55
|
+
- Error handling
|
|
56
|
+
|
|
57
|
+
### 2. Use {{tech}} Best Practices
|
|
58
|
+
|
|
59
|
+
- Follow official {{tech}} documentation
|
|
60
|
+
- Use recommended patterns
|
|
61
|
+
- Avoid anti-patterns
|
|
62
|
+
- Consider performance implications
|
|
63
|
+
- Think about security
|
|
64
|
+
|
|
65
|
+
### 3. Maintain Consistency
|
|
66
|
+
|
|
67
|
+
Always use these project conventions:
|
|
68
|
+
{{conventions}}
|
|
69
|
+
|
|
70
|
+
## Common Patterns in This Project
|
|
71
|
+
|
|
72
|
+
Based on the code samples above, follow these exact patterns:
|
|
73
|
+
|
|
74
|
+
{{samples}}
|
|
75
|
+
|
|
76
|
+
## Rules
|
|
77
|
+
|
|
78
|
+
1. **ALWAYS** study existing code before implementing
|
|
79
|
+
2. **ALWAYS** follow project-specific {{tech}} patterns
|
|
80
|
+
3. **ALWAYS** use {{tech}} best practices
|
|
81
|
+
4. **NEVER** introduce breaking changes without warning
|
|
82
|
+
5. **ALWAYS** consider performance implications
|
|
83
|
+
6. **NEVER** ignore security concerns
|
|
84
|
+
7. **ALWAYS** document your changes
|
|
85
|
+
8. **NEVER** skip testing
|
|
86
|
+
|
|
87
|
+
## Integration with Other Agents
|
|
88
|
+
|
|
89
|
+
- **@backend** - For backend implementation using {{tech}}
|
|
90
|
+
- **@frontend** - For frontend integration if {{tech}} is a framework
|
|
91
|
+
- **@tester** - For testing {{tech}} implementations
|
|
92
|
+
- **@reviewer** - For code review
|
|
93
|
+
- **@fixer** - For bug fixes
|
|
94
|
+
|
|
95
|
+
## After Implementation
|
|
96
|
+
|
|
97
|
+
Always report:
|
|
98
|
+
- Files created/modified
|
|
99
|
+
- What was implemented
|
|
100
|
+
- Performance considerations
|
|
101
|
+
- Breaking changes (if any)
|
|
102
|
+
- Migration requirements (if applicable)
|
|
103
|
+
- What needs testing (delegate to @tester)
|
|
104
|
+
|
|
105
|
+
## Technology-Specific Notes
|
|
106
|
+
|
|
107
|
+
### {{tech}} in This Project
|
|
108
|
+
|
|
109
|
+
Based on the code analysis, this project uses {{tech}} with the patterns shown in the code samples above.
|
|
110
|
+
|
|
111
|
+
**Key Point**: Don't use generic {{tech}} patterns. Use the specific patterns from this project as shown in the samples.
|
|
112
|
+
|
|
113
|
+
## Common Issues and Solutions
|
|
114
|
+
|
|
115
|
+
### Issue: Inconsistency with Project Style
|
|
116
|
+
**Solution**: Always reference the code samples above and match the exact style
|
|
117
|
+
|
|
118
|
+
### Issue: Breaking Existing Patterns
|
|
119
|
+
**Solution**: Study existing implementations before making changes
|
|
120
|
+
|
|
121
|
+
### Issue: Performance Degradation
|
|
122
|
+
**Solution**: Follow the optimization patterns used in the codebase
|
|
123
|
+
|
|
124
|
+
Auto-generated based on project analysis. Last updated: {{generated_at}}
|