@itz4blitz/agentful 0.1.0 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/architect.md +283 -11
- package/.claude/agents/backend.md +282 -218
- package/.claude/agents/frontend.md +242 -319
- package/.claude/agents/orchestrator.md +27 -27
- package/.claude/agents/reviewer.md +1 -1
- package/.claude/agents/tester.md +375 -284
- package/.claude/commands/agentful-decide.md +104 -29
- package/.claude/commands/agentful-start.md +18 -16
- package/.claude/commands/agentful-status.md +28 -22
- package/.claude/commands/agentful-validate.md +42 -20
- package/.claude/commands/agentful.md +329 -0
- package/.claude/product/README.md +1 -1
- package/.claude/product/index.md +1 -1
- package/.claude/settings.json +4 -3
- package/.claude/skills/conversation/SKILL.md +1130 -0
- package/LICENSE +1 -1
- package/README.md +557 -222
- package/bin/cli.js +319 -36
- package/lib/agent-generator.js +685 -0
- package/lib/domain-detector.js +468 -0
- package/lib/domain-structure-generator.js +770 -0
- package/lib/index.js +40 -0
- package/lib/project-analyzer.js +701 -0
- package/lib/tech-stack-detector.js +1091 -0
- package/lib/template-engine.js +153 -0
- package/package.json +14 -5
- package/template/CLAUDE.md +62 -21
- package/template/PRODUCT.md +89 -1
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain Structure Generator
|
|
3
|
+
*
|
|
4
|
+
* Creates hierarchical domain structures for product organization
|
|
5
|
+
* with comprehensive specifications, features, and technical details.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
|
|
14
|
+
class DomainStructureGenerator {
|
|
15
|
+
constructor(projectPath, analysis) {
|
|
16
|
+
this.projectPath = projectPath;
|
|
17
|
+
this.analysis = analysis;
|
|
18
|
+
this.productDir = path.join(projectPath, '.claude/product');
|
|
19
|
+
this.domainsDir = path.join(this.productDir, 'domains');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Main entry point - generates domain structure
|
|
24
|
+
*/
|
|
25
|
+
async generateDomainStructure() {
|
|
26
|
+
console.log('📁 Generating domain structure...');
|
|
27
|
+
|
|
28
|
+
// Ensure directories exist
|
|
29
|
+
await fs.mkdir(this.domainsDir, { recursive: true });
|
|
30
|
+
|
|
31
|
+
// Get domains from analysis
|
|
32
|
+
const domains = this.analysis.domains || [];
|
|
33
|
+
|
|
34
|
+
// Handle new projects (no domains detected)
|
|
35
|
+
if (domains.length === 0) {
|
|
36
|
+
await this.generateEmptyProjectStructure();
|
|
37
|
+
} else {
|
|
38
|
+
// Generate structure for each detected domain
|
|
39
|
+
for (const domain of domains) {
|
|
40
|
+
await this.generateDomainStructureForDomain(domain);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Generate/update product index
|
|
45
|
+
await this.generateProductIndex(domains);
|
|
46
|
+
|
|
47
|
+
// Generate completion schema
|
|
48
|
+
await this.generateCompletionSchema(domains);
|
|
49
|
+
|
|
50
|
+
console.log(`✅ Generated structure for ${domains.length} domains`);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
domains: domains.length,
|
|
54
|
+
features: await this.countFeatures(domains),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Generate structure for empty project
|
|
60
|
+
*/
|
|
61
|
+
async generateEmptyProjectStructure() {
|
|
62
|
+
const indexContent = `# Product Structure
|
|
63
|
+
|
|
64
|
+
This is a new project. Domains will be automatically detected as you build features.
|
|
65
|
+
|
|
66
|
+
## Getting Started
|
|
67
|
+
|
|
68
|
+
1. Create your first feature (e.g., authentication, user management)
|
|
69
|
+
2. The domain structure will be automatically updated
|
|
70
|
+
3. Each domain will contain:
|
|
71
|
+
- Feature specifications
|
|
72
|
+
- Acceptance criteria
|
|
73
|
+
- Technical implementation details
|
|
74
|
+
- API documentation
|
|
75
|
+
- Data models
|
|
76
|
+
|
|
77
|
+
## Initial Suggested Domains
|
|
78
|
+
|
|
79
|
+
Based on your tech stack, consider starting with:
|
|
80
|
+
|
|
81
|
+
- **Authentication** - User registration, login, sessions
|
|
82
|
+
- **Core Features** - Main business logic
|
|
83
|
+
- **Data Models** - Database schemas
|
|
84
|
+
|
|
85
|
+
As you implement features, run \`agentful init\` again to update this structure.
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
await fs.writeFile(
|
|
89
|
+
path.join(this.productDir, 'index.md'),
|
|
90
|
+
indexContent
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Generate completion.json for empty project
|
|
94
|
+
const completionContent = {
|
|
95
|
+
version: '1.0.0',
|
|
96
|
+
domains: [],
|
|
97
|
+
generatedAt: new Date().toISOString(),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
await fs.writeFile(
|
|
101
|
+
path.join(this.productDir, 'completion.json'),
|
|
102
|
+
JSON.stringify(completionContent, null, 2)
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Generate structure for a specific domain
|
|
108
|
+
*/
|
|
109
|
+
async generateDomainStructureForDomain(domain) {
|
|
110
|
+
const domainDir = path.join(this.domainsDir, domain.name);
|
|
111
|
+
await fs.mkdir(domainDir, { recursive: true });
|
|
112
|
+
|
|
113
|
+
// Generate domain index
|
|
114
|
+
await this.generateDomainIndex(domain);
|
|
115
|
+
|
|
116
|
+
// Generate features
|
|
117
|
+
if (domain.features && domain.features.length > 0) {
|
|
118
|
+
const featuresDir = path.join(domainDir, 'features');
|
|
119
|
+
await fs.mkdir(featuresDir, { recursive: true });
|
|
120
|
+
|
|
121
|
+
for (const feature of domain.features) {
|
|
122
|
+
await this.generateFeatureSpec(domain.name, feature);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Generate technical details
|
|
127
|
+
await this.generateTechnicalSpec(domain);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Generate domain index file
|
|
132
|
+
*/
|
|
133
|
+
async generateDomainIndex(domain) {
|
|
134
|
+
const domainDir = path.join(this.domainsDir, domain.name);
|
|
135
|
+
|
|
136
|
+
// Extract domain-specific code samples
|
|
137
|
+
const samples = await this.extractDomainSamples(domain.name);
|
|
138
|
+
|
|
139
|
+
// Detect dependencies
|
|
140
|
+
const dependencies = await this.detectDomainDependencies(domain.name);
|
|
141
|
+
|
|
142
|
+
const content = `# ${this.formatDomainName(domain.name)} Domain
|
|
143
|
+
|
|
144
|
+
${this.generateDomainOverview(domain)}
|
|
145
|
+
|
|
146
|
+
## Confidence
|
|
147
|
+
|
|
148
|
+
${this.formatConfidence(domain.confidence)}
|
|
149
|
+
|
|
150
|
+
## Detected Features
|
|
151
|
+
|
|
152
|
+
${this.formatFeatures(domain.features || [])}
|
|
153
|
+
|
|
154
|
+
## Technical Details
|
|
155
|
+
|
|
156
|
+
### Technologies Used
|
|
157
|
+
|
|
158
|
+
${this.formatTechnologies(domain.technologies || [])}
|
|
159
|
+
|
|
160
|
+
### Code Samples
|
|
161
|
+
|
|
162
|
+
${this.formatCodeSamples(samples)}
|
|
163
|
+
|
|
164
|
+
### API Endpoints
|
|
165
|
+
|
|
166
|
+
${this.formatEndpoints(samples.endpoints || [])}
|
|
167
|
+
|
|
168
|
+
### Data Models
|
|
169
|
+
|
|
170
|
+
${this.formatModels(samples.models || [])}
|
|
171
|
+
|
|
172
|
+
## Dependencies
|
|
173
|
+
|
|
174
|
+
${this.formatDependencies(dependencies)}
|
|
175
|
+
|
|
176
|
+
## Integration Points
|
|
177
|
+
|
|
178
|
+
${this.formatIntegrationPoints()}
|
|
179
|
+
|
|
180
|
+
## Implementation Notes
|
|
181
|
+
|
|
182
|
+
${this.generateImplementationNotes(domain)}
|
|
183
|
+
`;
|
|
184
|
+
|
|
185
|
+
await fs.writeFile(
|
|
186
|
+
path.join(domainDir, 'index.md'),
|
|
187
|
+
content
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Generate feature specification
|
|
193
|
+
*/
|
|
194
|
+
async generateFeatureSpec(domainName, feature) {
|
|
195
|
+
const featuresDir = path.join(this.domainsDir, domainName, 'features');
|
|
196
|
+
const featureFile = path.join(featuresDir, `${feature.name}.md`);
|
|
197
|
+
|
|
198
|
+
const content = `# ${this.formatFeatureName(feature.name)}
|
|
199
|
+
|
|
200
|
+
${this.generateFeatureOverview(feature)}
|
|
201
|
+
|
|
202
|
+
## Acceptance Criteria
|
|
203
|
+
|
|
204
|
+
${this.formatAcceptanceCriteria(feature.acceptanceCriteria || [])}
|
|
205
|
+
|
|
206
|
+
## Implementation Status
|
|
207
|
+
|
|
208
|
+
${this.formatImplementationStatus(feature)}
|
|
209
|
+
|
|
210
|
+
## Technical Details
|
|
211
|
+
|
|
212
|
+
### API Endpoints
|
|
213
|
+
|
|
214
|
+
${this.formatFeatureEndpoints(feature)}
|
|
215
|
+
|
|
216
|
+
### Data Models
|
|
217
|
+
|
|
218
|
+
${this.formatFeatureModels(feature)}
|
|
219
|
+
|
|
220
|
+
### Business Logic
|
|
221
|
+
|
|
222
|
+
${this.formatBusinessLogic(feature)}
|
|
223
|
+
|
|
224
|
+
## Related Files
|
|
225
|
+
|
|
226
|
+
${this.formatRelatedFiles(feature)}
|
|
227
|
+
|
|
228
|
+
## Dependencies
|
|
229
|
+
|
|
230
|
+
${this.formatFeatureDependencies(feature)}
|
|
231
|
+
`;
|
|
232
|
+
|
|
233
|
+
await fs.writeFile(featureFile, content);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Generate technical specification for domain
|
|
238
|
+
*/
|
|
239
|
+
async generateTechnicalSpec(domain) {
|
|
240
|
+
const domainDir = path.join(this.domainsDir, domain.name);
|
|
241
|
+
const samples = await this.extractDomainSamples(domain.name);
|
|
242
|
+
|
|
243
|
+
const content = `# Technical Specification: ${this.formatDomainName(domain.name)}
|
|
244
|
+
|
|
245
|
+
## Architecture
|
|
246
|
+
|
|
247
|
+
${this.generateArchitectureDiagram()}
|
|
248
|
+
|
|
249
|
+
## API Specification
|
|
250
|
+
|
|
251
|
+
### REST Endpoints
|
|
252
|
+
|
|
253
|
+
${this.formatAPIEndpoints(samples.endpoints || [])}
|
|
254
|
+
|
|
255
|
+
### Request/Response Schemas
|
|
256
|
+
|
|
257
|
+
${this.formatSchemas()}
|
|
258
|
+
|
|
259
|
+
## Data Models
|
|
260
|
+
|
|
261
|
+
### Database Schema
|
|
262
|
+
|
|
263
|
+
${this.formatDatabaseSchema(samples.models || [])}
|
|
264
|
+
|
|
265
|
+
### Entity Relationships
|
|
266
|
+
|
|
267
|
+
${this.formatEntityRelationships()}
|
|
268
|
+
|
|
269
|
+
## Business Logic
|
|
270
|
+
|
|
271
|
+
### Services
|
|
272
|
+
|
|
273
|
+
${this.formatServices(samples.services || [])}
|
|
274
|
+
|
|
275
|
+
### Workflows
|
|
276
|
+
|
|
277
|
+
${this.formatWorkflows()}
|
|
278
|
+
|
|
279
|
+
## Security
|
|
280
|
+
|
|
281
|
+
${this.formatSecuritySpec()}
|
|
282
|
+
|
|
283
|
+
## Performance Considerations
|
|
284
|
+
|
|
285
|
+
${this.formatPerformanceSpec()}
|
|
286
|
+
|
|
287
|
+
## Testing Strategy
|
|
288
|
+
|
|
289
|
+
${this.formatTestingSpec()}
|
|
290
|
+
`;
|
|
291
|
+
|
|
292
|
+
await fs.writeFile(
|
|
293
|
+
path.join(domainDir, 'technical.md'),
|
|
294
|
+
content
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Generate product index
|
|
300
|
+
*/
|
|
301
|
+
async generateProductIndex(domains) {
|
|
302
|
+
const content = `# Product Structure
|
|
303
|
+
|
|
304
|
+
Auto-generated domain structure for your project.
|
|
305
|
+
|
|
306
|
+
## Overview
|
|
307
|
+
|
|
308
|
+
This project has been analyzed and organized into ${domains.length} domain(s).
|
|
309
|
+
|
|
310
|
+
**Last Updated**: ${new Date().toISOString()}
|
|
311
|
+
|
|
312
|
+
## Domains
|
|
313
|
+
|
|
314
|
+
${this.formatDomainList(domains)}
|
|
315
|
+
|
|
316
|
+
## Domain Hierarchy
|
|
317
|
+
|
|
318
|
+
${this.generateDomainHierarchy(domains)}
|
|
319
|
+
|
|
320
|
+
## Quick Navigation
|
|
321
|
+
|
|
322
|
+
${this.generateQuickNavigation(domains)}
|
|
323
|
+
|
|
324
|
+
## Statistics
|
|
325
|
+
|
|
326
|
+
- **Total Domains**: ${domains.length}
|
|
327
|
+
- **Total Features**: ${this.countTotalFeatures(domains)}
|
|
328
|
+
- **Detected Technologies**: ${this.formatDetectedTech()}
|
|
329
|
+
|
|
330
|
+
## Usage
|
|
331
|
+
|
|
332
|
+
Use this structure to:
|
|
333
|
+
- Navigate between product domains
|
|
334
|
+
- Understand feature relationships
|
|
335
|
+
- Find API documentation
|
|
336
|
+
- Review data models
|
|
337
|
+
- Track implementation progress
|
|
338
|
+
`;
|
|
339
|
+
|
|
340
|
+
await fs.writeFile(
|
|
341
|
+
path.join(this.productDir, 'index.md'),
|
|
342
|
+
content
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Generate completion schema
|
|
348
|
+
*/
|
|
349
|
+
async generateCompletionSchema(domains) {
|
|
350
|
+
const completion = {
|
|
351
|
+
version: '1.0.0',
|
|
352
|
+
generatedAt: new Date().toISOString(),
|
|
353
|
+
project: {
|
|
354
|
+
path: this.projectPath,
|
|
355
|
+
name: path.basename(this.projectPath),
|
|
356
|
+
},
|
|
357
|
+
techStack: this.analysis.techStack || {},
|
|
358
|
+
domains: [],
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// Build domain structure
|
|
362
|
+
for (const domain of domains) {
|
|
363
|
+
const domainData = {
|
|
364
|
+
name: domain.name,
|
|
365
|
+
confidence: domain.confidence,
|
|
366
|
+
features: [],
|
|
367
|
+
technologies: domain.technologies || [],
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
if (domain.features) {
|
|
371
|
+
for (const feature of domain.features) {
|
|
372
|
+
domainData.features.push({
|
|
373
|
+
name: feature.name,
|
|
374
|
+
status: feature.status || 'detected',
|
|
375
|
+
acceptanceCriteria: feature.acceptanceCriteria || [],
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
completion.domains.push(domainData);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
await fs.writeFile(
|
|
384
|
+
path.join(this.productDir, 'completion.json'),
|
|
385
|
+
JSON.stringify(completion, null, 2)
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Extract domain-specific code samples
|
|
391
|
+
*/
|
|
392
|
+
async extractDomainSamples(domainName) {
|
|
393
|
+
const samples = {
|
|
394
|
+
code: [],
|
|
395
|
+
endpoints: [],
|
|
396
|
+
models: [],
|
|
397
|
+
schemas: [],
|
|
398
|
+
services: [],
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
// Domain-specific file patterns
|
|
402
|
+
const patterns = {
|
|
403
|
+
'auth-agent': ['auth', 'user', 'login', 'register', 'session'],
|
|
404
|
+
'billing-agent': ['billing', 'payment', 'subscription', 'invoice'],
|
|
405
|
+
'content-agent': ['content', 'post', 'article', 'blog'],
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const keywords = patterns[domainName] || [domainName];
|
|
409
|
+
|
|
410
|
+
// Find related files
|
|
411
|
+
const files = await this.findDomainFiles(keywords, 5);
|
|
412
|
+
|
|
413
|
+
for (const file of files) {
|
|
414
|
+
try {
|
|
415
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
416
|
+
const relativePath = path.relative(this.projectPath, file);
|
|
417
|
+
|
|
418
|
+
// Extract code samples
|
|
419
|
+
if (content.length > 0 && content.length < 1500) {
|
|
420
|
+
samples.code.push({
|
|
421
|
+
path: relativePath,
|
|
422
|
+
content: content,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Extract endpoints
|
|
427
|
+
if (content.includes('router.') || content.includes('app.') || content.includes('@Get')) {
|
|
428
|
+
samples.endpoints.push(...this.extractEndpoints(content, relativePath));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Extract models
|
|
432
|
+
if (content.includes('model') || content.includes('schema') || content.includes('interface')) {
|
|
433
|
+
samples.models.push(...this.extractModels(content, relativePath));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Extract services
|
|
437
|
+
if (content.includes('service') || content.includes('Service')) {
|
|
438
|
+
samples.services.push({
|
|
439
|
+
path: relativePath,
|
|
440
|
+
content: content.substring(0, 500),
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
} catch (error) {
|
|
444
|
+
// Skip files that can't be read
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return samples;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Detect domain dependencies
|
|
453
|
+
*/
|
|
454
|
+
async detectDomainDependencies(domainName) {
|
|
455
|
+
const dependencies = [];
|
|
456
|
+
|
|
457
|
+
// Analyze imports and requires in domain files
|
|
458
|
+
const files = await this.findDomainFiles([domainName], 5);
|
|
459
|
+
|
|
460
|
+
for (const file of files) {
|
|
461
|
+
try {
|
|
462
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
463
|
+
const lines = content.split('\n');
|
|
464
|
+
|
|
465
|
+
for (const line of lines) {
|
|
466
|
+
// Detect imports from other domains
|
|
467
|
+
if (line.includes('import') || line.includes('require')) {
|
|
468
|
+
const domains = ['auth', 'user', 'billing', 'content', 'notification'];
|
|
469
|
+
for (const domain of domains) {
|
|
470
|
+
if (line.toLowerCase().includes(domain) && domain !== domainName) {
|
|
471
|
+
dependencies.push({
|
|
472
|
+
from: domainName,
|
|
473
|
+
to: domain,
|
|
474
|
+
type: line.includes('import') ? 'import' : 'require',
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
} catch (error) {
|
|
481
|
+
// Skip files that can't be read
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return [...new Set(dependencies.map(d => JSON.stringify(d)))].map(s => JSON.parse(s));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Find domain files
|
|
490
|
+
*/
|
|
491
|
+
async findDomainFiles(keywords, maxFiles = 5) {
|
|
492
|
+
const files = [];
|
|
493
|
+
|
|
494
|
+
const scanDir = async (dirPath) => {
|
|
495
|
+
try {
|
|
496
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
497
|
+
|
|
498
|
+
for (const entry of entries) {
|
|
499
|
+
if (files.length >= maxFiles) return;
|
|
500
|
+
|
|
501
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
502
|
+
|
|
503
|
+
if (entry.isDirectory()) {
|
|
504
|
+
// Skip common directories
|
|
505
|
+
if (!['node_modules', '.git', 'dist', 'build'].includes(entry.name)) {
|
|
506
|
+
await scanDir(fullPath);
|
|
507
|
+
}
|
|
508
|
+
} else if (entry.isFile() && this.isSourceFile(entry.name)) {
|
|
509
|
+
// Check if file matches keywords
|
|
510
|
+
for (const keyword of keywords) {
|
|
511
|
+
if (entry.name.toLowerCase().includes(keyword.toLowerCase())) {
|
|
512
|
+
files.push(fullPath);
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
} catch (error) {
|
|
519
|
+
// Can't read directory
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
await scanDir(this.projectPath);
|
|
524
|
+
return files;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Check if file is a source file
|
|
529
|
+
*/
|
|
530
|
+
isSourceFile(filename) {
|
|
531
|
+
const extensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.go', '.rs'];
|
|
532
|
+
return extensions.some(ext => filename.endsWith(ext));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Extract endpoints from code
|
|
537
|
+
*/
|
|
538
|
+
extractEndpoints(content, filePath) {
|
|
539
|
+
const endpoints = [];
|
|
540
|
+
const lines = content.split('\n');
|
|
541
|
+
|
|
542
|
+
for (const line of lines) {
|
|
543
|
+
if (line.includes('router.') || line.includes('app.') || line.includes('@Get') || line.includes('@Post')) {
|
|
544
|
+
endpoints.push({
|
|
545
|
+
file: filePath,
|
|
546
|
+
code: line.trim(),
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return endpoints;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Extract models from code
|
|
556
|
+
*/
|
|
557
|
+
extractModels(content, filePath) {
|
|
558
|
+
const models = [];
|
|
559
|
+
const lines = content.split('\n');
|
|
560
|
+
|
|
561
|
+
for (const line of lines) {
|
|
562
|
+
if (line.includes('model ') || line.includes('schema ') || line.includes('interface ') || line.includes('type ')) {
|
|
563
|
+
models.push({
|
|
564
|
+
file: filePath,
|
|
565
|
+
code: line.trim(),
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return models;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Count features
|
|
575
|
+
*/
|
|
576
|
+
async countFeatures(domains) {
|
|
577
|
+
let count = 0;
|
|
578
|
+
for (const domain of domains) {
|
|
579
|
+
if (domain.features) {
|
|
580
|
+
count += domain.features.length;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return count;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Format helpers
|
|
588
|
+
*/
|
|
589
|
+
|
|
590
|
+
formatDomainName(name) {
|
|
591
|
+
return name.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
formatFeatureName(name) {
|
|
595
|
+
return name.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
formatConfidence(confidence) {
|
|
599
|
+
const percentage = Math.round((confidence || 0) * 100);
|
|
600
|
+
return `${percentage}% - Based on code analysis`;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
formatFeatures(features) {
|
|
604
|
+
if (!features || features.length === 0) {
|
|
605
|
+
return 'No specific features detected yet.';
|
|
606
|
+
}
|
|
607
|
+
return features.map(f => `- **${this.formatFeatureName(f.name)}**: ${f.description || 'No description'}`).join('\n');
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
formatTechnologies(tech) {
|
|
611
|
+
if (!tech || tech.length === 0) return 'Not specified';
|
|
612
|
+
return tech.map(t => `- \`${t}\``).join('\n');
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
formatCodeSamples(samples) {
|
|
616
|
+
if (!samples.code || samples.code.length === 0) return 'No samples available';
|
|
617
|
+
return samples.code.slice(0, 3).map(s =>
|
|
618
|
+
`#### ${s.path}\n\`\`\`\n${s.content.substring(0, 500)}...\n\`\`\``
|
|
619
|
+
).join('\n\n');
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
formatEndpoints(endpoints) {
|
|
623
|
+
if (!endpoints || endpoints.length === 0) return 'No endpoints detected';
|
|
624
|
+
return endpoints.map(e => `- ${e.file}: \`${e.code}\``).join('\n');
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
formatModels(models) {
|
|
628
|
+
if (!models || models.length === 0) return 'No models detected';
|
|
629
|
+
return models.map(m => `- ${m.file}: \`${m.code}\``).join('\n');
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
formatDependencies(dependencies) {
|
|
633
|
+
if (!dependencies || dependencies.length === 0) return 'No dependencies detected';
|
|
634
|
+
return dependencies.map(d => `- Depends on **${d.to}** (${d.type})`).join('\n');
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
formatIntegrationPoints() {
|
|
638
|
+
return 'Integration points will be detected as you build more features.';
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
formatDetectedTech() {
|
|
642
|
+
const tech = this.analysis.techStack || {};
|
|
643
|
+
return Object.entries(tech).map(([key, value]) =>
|
|
644
|
+
`- ${key}: ${value || 'Not detected'}`
|
|
645
|
+
).join('\n');
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
formatDomainList(domains) {
|
|
649
|
+
if (domains.length === 0) return 'No domains detected yet.';
|
|
650
|
+
return domains.map(d =>
|
|
651
|
+
`### [${this.formatDomainName(d.name)}](./domains/${d.name}/index.md)\n\n${d.description || 'No description'}`
|
|
652
|
+
).join('\n\n');
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
generateDomainOverview(domain) {
|
|
656
|
+
return `The **${this.formatDomainName(domain.name)}** domain handles ${this.formatDomainName(domain.name)}-related functionality.
|
|
657
|
+
|
|
658
|
+
${domain.description || 'Automatically detected from codebase analysis.'}`;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
generateImplementationNotes(domain) {
|
|
662
|
+
return `This domain was automatically detected with **${Math.round((domain.confidence || 0) * 100)}%** confidence based on:
|
|
663
|
+
|
|
664
|
+
- File names and directory structure
|
|
665
|
+
- Code patterns and imports
|
|
666
|
+
- API endpoints
|
|
667
|
+
- Data models
|
|
668
|
+
|
|
669
|
+
As you implement more features in this domain, this documentation will be updated automatically.`;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
countTotalFeatures(domains) {
|
|
673
|
+
return domains.reduce((count, domain) => count + (domain.features?.length || 0), 0);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
generateDomainHierarchy(domains) {
|
|
677
|
+
// Simple hierarchy for now - can be enhanced
|
|
678
|
+
if (domains.length === 0) return 'No domains detected yet.';
|
|
679
|
+
|
|
680
|
+
return domains.map(d => `- ${d.name}${d.features ? ` (${d.features.length} features)` : ''}`).join('\n');
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
generateQuickNavigation(domains) {
|
|
684
|
+
return domains.map(d => `- [${this.formatDomainName(d.name)}](domains/${d.name}/index.md)`).join('\n');
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
formatAcceptanceCriteria(criteria) {
|
|
688
|
+
if (!criteria || criteria.length === 0) return 'No acceptance criteria defined yet.';
|
|
689
|
+
return criteria.map(c => `- [ ] ${c}`).join('\n');
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
formatImplementationStatus(feature) {
|
|
693
|
+
return feature.status || 'Detected - not yet implemented';
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
formatFeatureEndpoints(feature) {
|
|
697
|
+
return feature.endpoints ? feature.endpoints.map(e => `- \`${e.method} ${e.path}\``).join('\n') : 'No endpoints defined';
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
formatFeatureModels(feature) {
|
|
701
|
+
return feature.models ? feature.models.map(m => `- \`${m}\``).join('\n') : 'No models defined';
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
formatBusinessLogic(feature) {
|
|
705
|
+
return feature.businessLogic || 'Business logic will be documented during implementation.';
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
formatRelatedFiles(feature) {
|
|
709
|
+
return feature.files ? feature.files.map(f => `- \`${f}\``).join('\n') : 'No files detected';
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
formatFeatureDependencies(feature) {
|
|
713
|
+
return feature.dependencies ? feature.dependencies.map(d => `- ${d}`).join('\n') : 'No dependencies';
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
generateFeatureOverview(feature) {
|
|
717
|
+
return feature.description || `Feature for ${this.formatFeatureName(feature.name)}`;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
formatAPIEndpoints(endpoints) {
|
|
721
|
+
if (!endpoints || endpoints.length === 0) return 'No endpoints documented yet.';
|
|
722
|
+
return endpoints.map(e =>
|
|
723
|
+
`#### ${e.file}\n\`\`\`\n${e.code}\n\`\`\``
|
|
724
|
+
).join('\n\n');
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
formatSchemas() {
|
|
728
|
+
return 'Request/response schemas will be documented during implementation.';
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
formatDatabaseSchema(models) {
|
|
732
|
+
if (!models || models.length === 0) return 'No models documented yet.';
|
|
733
|
+
return models.map(m =>
|
|
734
|
+
`#### ${m.file}\n\`\`\`\n${m.code}\n\`\`\``
|
|
735
|
+
).join('\n\n');
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
formatEntityRelationships() {
|
|
739
|
+
return 'Entity relationships will be documented as more features are implemented.';
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
formatServices(services) {
|
|
743
|
+
if (!services || services.length === 0) return 'No services documented yet.';
|
|
744
|
+
return services.map(s =>
|
|
745
|
+
`#### ${s.path}\n\`\`\`\n${s.content}\n\`\`\``
|
|
746
|
+
).join('\n\n');
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
formatWorkflows() {
|
|
750
|
+
return 'Workflows will be documented during implementation.';
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
formatSecuritySpec() {
|
|
754
|
+
return 'Security considerations will be documented during implementation.';
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
formatPerformanceSpec() {
|
|
758
|
+
return 'Performance considerations will be documented during implementation.';
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
formatTestingSpec() {
|
|
762
|
+
return 'Testing strategy will be documented during implementation.';
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
generateArchitectureDiagram() {
|
|
766
|
+
return 'Architecture diagram will be generated during implementation.';
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
export default DomainStructureGenerator;
|