@itz4blitz/agentful 1.2.0 → 1.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.
Files changed (59) hide show
  1. package/README.md +28 -1
  2. package/bin/cli.js +11 -1055
  3. package/bin/hooks/block-file-creation.js +271 -0
  4. package/bin/hooks/product-spec-watcher.js +151 -0
  5. package/lib/index.js +0 -11
  6. package/lib/init.js +2 -21
  7. package/lib/parallel-execution.js +235 -0
  8. package/lib/presets.js +26 -4
  9. package/package.json +4 -7
  10. package/template/.claude/agents/architect.md +2 -2
  11. package/template/.claude/agents/backend.md +17 -30
  12. package/template/.claude/agents/frontend.md +17 -39
  13. package/template/.claude/agents/orchestrator.md +63 -4
  14. package/template/.claude/agents/product-analyzer.md +1 -1
  15. package/template/.claude/agents/tester.md +16 -29
  16. package/template/.claude/commands/agentful-generate.md +221 -14
  17. package/template/.claude/commands/agentful-init.md +621 -0
  18. package/template/.claude/commands/agentful-product.md +1 -1
  19. package/template/.claude/commands/agentful-start.md +99 -1
  20. package/template/.claude/product/EXAMPLES.md +2 -2
  21. package/template/.claude/product/index.md +1 -1
  22. package/template/.claude/settings.json +22 -0
  23. package/template/.claude/skills/research/SKILL.md +432 -0
  24. package/template/CLAUDE.md +5 -6
  25. package/template/bin/hooks/architect-drift-detector.js +242 -0
  26. package/template/bin/hooks/product-spec-watcher.js +151 -0
  27. package/version.json +1 -1
  28. package/bin/hooks/post-agent.js +0 -101
  29. package/bin/hooks/post-feature.js +0 -227
  30. package/bin/hooks/pre-agent.js +0 -118
  31. package/bin/hooks/pre-feature.js +0 -138
  32. package/lib/VALIDATION_README.md +0 -455
  33. package/lib/ci/claude-action-integration.js +0 -641
  34. package/lib/ci/index.js +0 -10
  35. package/lib/core/analyzer.js +0 -497
  36. package/lib/core/cli.js +0 -141
  37. package/lib/core/detectors/conventions.js +0 -342
  38. package/lib/core/detectors/framework.js +0 -276
  39. package/lib/core/detectors/index.js +0 -15
  40. package/lib/core/detectors/language.js +0 -199
  41. package/lib/core/detectors/patterns.js +0 -356
  42. package/lib/core/generator.js +0 -626
  43. package/lib/core/index.js +0 -9
  44. package/lib/core/output-parser.js +0 -458
  45. package/lib/core/storage.js +0 -515
  46. package/lib/core/templates.js +0 -556
  47. package/lib/pipeline/cli.js +0 -423
  48. package/lib/pipeline/engine.js +0 -928
  49. package/lib/pipeline/executor.js +0 -440
  50. package/lib/pipeline/index.js +0 -33
  51. package/lib/pipeline/integrations.js +0 -559
  52. package/lib/pipeline/schemas.js +0 -288
  53. package/lib/remote/client.js +0 -361
  54. package/lib/server/auth.js +0 -270
  55. package/lib/server/client-example.js +0 -190
  56. package/lib/server/executor.js +0 -477
  57. package/lib/server/index.js +0 -494
  58. package/lib/update-helpers.js +0 -505
  59. package/lib/validation.js +0 -460
@@ -1,556 +0,0 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
-
5
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
-
7
- /**
8
- * Template Manager
9
- *
10
- * Manages agent templates with support for:
11
- * - Base templates (orchestrator, backend, frontend, etc.)
12
- * - Framework-specific templates (Next.js, NestJS, etc.)
13
- * - Pattern templates (database, auth, etc.)
14
- * - Custom user templates
15
- * - Template inheritance
16
- *
17
- * Templates are organized in:
18
- * - templates/base/ - Core agent templates
19
- * - templates/frameworks/ - Framework-specific templates
20
- * - templates/patterns/ - Pattern-based templates
21
- * - .agentful/templates/custom/ - User custom templates
22
- */
23
- export class TemplateManager {
24
- constructor(projectPath, templateBaseDir = null) {
25
- this.projectPath = projectPath;
26
- this.templateBaseDir = templateBaseDir || path.join(__dirname, '../../templates');
27
- this.customTemplateDir = path.join(projectPath, '.agentful', 'templates', 'custom');
28
- this.cache = new Map();
29
- this.registry = new Map();
30
- }
31
-
32
- /**
33
- * Initialize template system
34
- */
35
- async initialize() {
36
- await this._ensureDirectories();
37
- await this._loadBuiltInTemplates();
38
- await this._scanTemplates();
39
- }
40
-
41
- /**
42
- * Load template by name or path
43
- *
44
- * @param {string} nameOrPath - Template name (e.g., 'base/backend') or path
45
- * @returns {Promise<string>} Template content
46
- */
47
- async loadTemplate(nameOrPath) {
48
- // Check cache first
49
- if (this.cache.has(nameOrPath)) {
50
- return this.cache.get(nameOrPath);
51
- }
52
-
53
- // Find template file
54
- const templatePath = await this._findTemplatePath(nameOrPath);
55
- if (!templatePath) {
56
- throw new Error(`Template not found: ${nameOrPath}`);
57
- }
58
-
59
- // Load template content
60
- const content = await fs.readFile(templatePath, 'utf-8');
61
-
62
- // Handle template inheritance (if template has "extends" directive)
63
- const extendedContent = await this._processInheritance(content);
64
-
65
- // Cache the template
66
- this.cache.set(nameOrPath, extendedContent);
67
-
68
- return extendedContent;
69
- }
70
-
71
- /**
72
- * List all available templates
73
- *
74
- * @returns {Promise<Array>} Template metadata
75
- */
76
- async listTemplates() {
77
- return Array.from(this.registry.values());
78
- }
79
-
80
- /**
81
- * Find templates by criteria
82
- *
83
- * @param {Object} criteria - Search criteria
84
- * @param {string} criteria.type - Template type (base, framework, pattern)
85
- * @param {string} criteria.category - Template category
86
- * @param {Array} criteria.tags - Template tags
87
- * @returns {Promise<Array>} Matching templates
88
- */
89
- async findTemplates(criteria = {}) {
90
- const matching = [];
91
-
92
- for (const [name, meta] of this.registry) {
93
- let matches = true;
94
-
95
- if (criteria.type && meta.type !== criteria.type) {
96
- matches = false;
97
- }
98
-
99
- if (criteria.category && meta.category !== criteria.category) {
100
- matches = false;
101
- }
102
-
103
- if (criteria.tags && criteria.tags.length > 0) {
104
- const hasTag = criteria.tags.some(tag => meta.tags?.includes(tag));
105
- if (!hasTag) {
106
- matches = false;
107
- }
108
- }
109
-
110
- if (matches) {
111
- matching.push({ name, ...meta });
112
- }
113
- }
114
-
115
- return matching;
116
- }
117
-
118
- /**
119
- * Create custom template
120
- *
121
- * @param {string} name - Template name
122
- * @param {string} content - Template content
123
- * @param {Object} metadata - Template metadata
124
- */
125
- async createTemplate(name, content, metadata = {}) {
126
- const templatePath = path.join(this.customTemplateDir, `${name}.md`);
127
-
128
- // Ensure custom template directory exists
129
- await fs.mkdir(this.customTemplateDir, { recursive: true });
130
-
131
- // Write template
132
- await fs.writeFile(templatePath, content, 'utf-8');
133
-
134
- // Update registry
135
- this.registry.set(name, {
136
- type: 'custom',
137
- category: metadata.category || 'custom',
138
- tags: metadata.tags || [],
139
- path: templatePath,
140
- ...metadata
141
- });
142
-
143
- // Invalidate cache
144
- this.cache.delete(name);
145
- }
146
-
147
- /**
148
- * Find template file path
149
- *
150
- * @param {string} nameOrPath - Template name or path
151
- * @returns {Promise<string|null>} Template path or null
152
- * @private
153
- */
154
- async _findTemplatePath(nameOrPath) {
155
- // If it's a relative path with slashes, search in that category
156
- if (nameOrPath.includes('/')) {
157
- const [category, name] = nameOrPath.split('/');
158
- const searchPaths = [
159
- path.join(this.templateBaseDir, category, `${name}.md`),
160
- path.join(this.customTemplateDir, nameOrPath, `${name}.md`)
161
- ];
162
-
163
- for (const searchPath of searchPaths) {
164
- try {
165
- await fs.access(searchPath);
166
- return searchPath;
167
- } catch {
168
- // Continue searching
169
- }
170
- }
171
- }
172
-
173
- // Search in all template directories
174
- const searchPaths = [
175
- path.join(this.customTemplateDir, `${nameOrPath}.md`),
176
- path.join(this.templateBaseDir, 'frameworks', `${nameOrPath}.md`),
177
- path.join(this.templateBaseDir, 'patterns', `${nameOrPath}.md`),
178
- path.join(this.templateBaseDir, 'base', `${nameOrPath}.md`)
179
- ];
180
-
181
- for (const searchPath of searchPaths) {
182
- try {
183
- await fs.access(searchPath);
184
- return searchPath;
185
- } catch {
186
- // Continue searching
187
- }
188
- }
189
-
190
- return null;
191
- }
192
-
193
- /**
194
- * Process template inheritance
195
- *
196
- * @param {string} content - Template content
197
- * @returns {Promise<string>} Extended content
198
- * @private
199
- */
200
- async _processInheritance(content) {
201
- // Check for extends directive in frontmatter
202
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
203
- if (!frontmatterMatch) {
204
- return content;
205
- }
206
-
207
- const frontmatter = frontmatterMatch[1];
208
- const extendsMatch = frontmatter.match(/extends:\s*(.+)/);
209
-
210
- if (!extendsMatch) {
211
- return content;
212
- }
213
-
214
- const parentTemplate = extendsMatch[1].trim();
215
-
216
- // Load parent template
217
- const parentContent = await this.loadTemplate(parentTemplate);
218
-
219
- // Merge templates (simple approach: parent + child sections)
220
- return this._mergeTemplates(parentContent, content);
221
- }
222
-
223
- /**
224
- * Merge parent and child templates
225
- *
226
- * @param {string} parent - Parent template content
227
- * @param {string} child - Child template content
228
- * @returns {string} Merged content
229
- * @private
230
- */
231
- _mergeTemplates(parent, child) {
232
- // Extract frontmatter from both
233
- const parentFrontmatter = parent.match(/^---\n([\s\S]*?)\n---/)?.[1] || '';
234
- const childFrontmatter = child.match(/^---\n([\s\S]*?)\n---/)?.[1] || '';
235
-
236
- // Extract body (everything after frontmatter)
237
- const parentBody = parent.replace(/^---\n[\s\S]*?\n---\n?/, '');
238
- const childBody = child.replace(/^---\n[\s\S]*?\n---\n?/, '');
239
-
240
- // Merge frontmatter (child overrides parent)
241
- const mergedFrontmatter = this._mergeFrontmatter(parentFrontmatter, childFrontmatter);
242
-
243
- // Merge body (append child sections to parent)
244
- const mergedBody = parentBody + '\n\n' + childBody;
245
-
246
- return `---\n${mergedFrontmatter}\n---\n\n${mergedBody}`;
247
- }
248
-
249
- /**
250
- * Merge frontmatter YAML
251
- *
252
- * @param {string} parent - Parent frontmatter
253
- * @param {string} child - Child frontmatter
254
- * @returns {string} Merged frontmatter
255
- * @private
256
- */
257
- _mergeFrontmatter(parent, child) {
258
- const parseYaml = (yaml) => {
259
- const obj = {};
260
- const lines = yaml.split('\n');
261
-
262
- for (const line of lines) {
263
- const match = line.match(/^(\w+):\s*(.+)$/);
264
- if (match) {
265
- const [, key, value] = match;
266
- obj[key] = value.trim();
267
- }
268
- }
269
-
270
- return obj;
271
- };
272
-
273
- const parentObj = parseYaml(parent);
274
- const childObj = parseYaml(child);
275
-
276
- // Merge objects (child overrides parent)
277
- const merged = { ...parentObj, ...childObj };
278
-
279
- // Convert back to YAML
280
- return Object.entries(merged)
281
- .map(([key, value]) => `${key}: ${value}`)
282
- .join('\n');
283
- }
284
-
285
- /**
286
- * Ensure template directories exist
287
- *
288
- * @private
289
- */
290
- async _ensureDirectories() {
291
- const dirs = [
292
- path.join(this.templateBaseDir, 'base'),
293
- path.join(this.templateBaseDir, 'frameworks'),
294
- path.join(this.templateBaseDir, 'patterns'),
295
- this.customTemplateDir
296
- ];
297
-
298
- for (const dir of dirs) {
299
- await fs.mkdir(dir, { recursive: true });
300
- }
301
- }
302
-
303
- /**
304
- * Load built-in templates
305
- *
306
- * @private
307
- */
308
- async _loadBuiltInTemplates() {
309
- // Create base templates if they don't exist
310
- const baseTemplates = this._getBuiltInTemplates();
311
-
312
- for (const [name, content] of Object.entries(baseTemplates)) {
313
- const templatePath = path.join(this.templateBaseDir, 'base', `${name}.md`);
314
-
315
- try {
316
- await fs.access(templatePath);
317
- // Template exists, don't overwrite
318
- } catch {
319
- // Template doesn't exist, create it
320
- await fs.writeFile(templatePath, content, 'utf-8');
321
- }
322
- }
323
- }
324
-
325
- /**
326
- * Scan all templates and build registry
327
- *
328
- * @private
329
- */
330
- async _scanTemplates() {
331
- const categories = [
332
- { dir: path.join(this.templateBaseDir, 'base'), type: 'base' },
333
- { dir: path.join(this.templateBaseDir, 'frameworks'), type: 'framework' },
334
- { dir: path.join(this.templateBaseDir, 'patterns'), type: 'pattern' },
335
- { dir: this.customTemplateDir, type: 'custom' }
336
- ];
337
-
338
- for (const { dir, type } of categories) {
339
- try {
340
- const files = await fs.readdir(dir);
341
-
342
- for (const file of files) {
343
- if (file.endsWith('.md')) {
344
- const name = path.basename(file, '.md');
345
- const filePath = path.join(dir, file);
346
-
347
- // Extract metadata from frontmatter
348
- const content = await fs.readFile(filePath, 'utf-8');
349
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
350
-
351
- if (frontmatterMatch) {
352
- const frontmatter = frontmatterMatch[1];
353
- const metadata = this._parseFrontmatter(frontmatter);
354
-
355
- this.registry.set(name, {
356
- type,
357
- category: metadata.category || type,
358
- tags: metadata.tags || [],
359
- description: metadata.description,
360
- path: filePath
361
- });
362
- }
363
- }
364
- }
365
- } catch (error) {
366
- // Directory doesn't exist, skip
367
- }
368
- }
369
- }
370
-
371
- /**
372
- * Parse frontmatter YAML
373
- *
374
- * @param {string} frontmatter - Frontmatter content
375
- * @returns {Object} Parsed metadata
376
- * @private
377
- */
378
- _parseFrontmatter(frontmatter) {
379
- const metadata = {};
380
- const lines = frontmatter.split('\n');
381
-
382
- for (const line of lines) {
383
- const match = line.match(/^(\w+):\s*(.+)$/);
384
- if (match) {
385
- const [, key, value] = match;
386
- metadata[key] = value.trim();
387
- }
388
- }
389
-
390
- return metadata;
391
- }
392
-
393
- /**
394
- * Get built-in template content
395
- *
396
- * @returns {Object} Template name -> content mapping
397
- * @private
398
- */
399
- _getBuiltInTemplates() {
400
- return {
401
- orchestrator: `---
402
- name: orchestrator
403
- description: Coordinates structured product development with human checkpoints
404
- model: opus
405
- tools: Read, Write, Edit, Glob, Grep, Task
406
- category: base
407
- tags: core, orchestration
408
- ---
409
-
410
- # {{projectName}} Orchestrator
411
-
412
- You coordinate autonomous product development for **{{projectName}}**.
413
-
414
- ## Tech Stack
415
-
416
- {{#if techStack}}
417
- {{#each techStack}}
418
- - **{{@key}}**: {{this}}
419
- {{/each}}
420
- {{/if}}
421
-
422
- ## Your Role
423
-
424
- You orchestrate all development activities by delegating to specialized agents.
425
-
426
- ## Workflow
427
-
428
- 1. Analyze requirements from product spec
429
- 2. Delegate architecture to @architect
430
- 3. Delegate implementation to specialist agents
431
- 4. Coordinate validation with @reviewer
432
- 5. Delegate fixes to @fixer
433
- 6. Track progress in state.json
434
- `,
435
-
436
- backend: `---
437
- name: backend
438
- description: Implements backend services, APIs, databases, and business logic
439
- model: sonnet
440
- tools: Read, Write, Edit, Glob, Grep, Bash
441
- category: base
442
- tags: backend, api, database
443
- ---
444
-
445
- # {{projectName}} Backend Agent
446
-
447
- You implement server-side code for **{{projectName}}** using {{#if framework}}{{framework}}{{else}}best practices{{/if}}.
448
-
449
- ## Tech Stack
450
-
451
- - **Language**: {{language}}
452
- {{#if framework}}- **Framework**: {{framework}}{{/if}}
453
- {{#if database}}- **Database**: {{database.type}}{{/if}}
454
- {{#if orm}}- **ORM**: {{orm}}{{/if}}
455
-
456
- ## Your Scope
457
-
458
- - API Routes & Controllers
459
- - Service Layer (business logic)
460
- - Repository Layer (data access)
461
- - Database schemas and migrations
462
- - Authentication & Authorization
463
- - Input validation
464
- - Error handling
465
- - Caching strategies
466
-
467
- ## Architecture Pattern
468
-
469
- Follow clean architecture with three layers:
470
-
471
- 1. **Controllers** - Handle HTTP requests/responses
472
- 2. **Services** - Business logic and orchestration
473
- 3. **Repositories** - Data access and external services
474
-
475
- See \`.claude/skills/backend-patterns/SKILL.md\` for detailed implementation patterns.
476
-
477
- ## Boundaries
478
-
479
- ### Handles
480
- - Server-side business logic
481
- - API endpoint implementation
482
- - Database operations
483
- - Authentication flows
484
-
485
- ### Delegates to @frontend
486
- - UI components, client-side state
487
-
488
- ### Delegates to @tester
489
- - Unit tests, integration tests
490
-
491
- ## Rules
492
-
493
- 1. **Always validate inputs** - Never trust client data
494
- 2. **Use transactions** - Multi-step DB operations must be atomic
495
- 3. **Handle errors properly** - Return meaningful error messages
496
- 4. **Follow project patterns** - Match existing code conventions
497
- `,
498
-
499
- frontend: `---
500
- name: frontend
501
- description: Implements UI components, pages, and client-side logic
502
- model: sonnet
503
- tools: Read, Write, Edit, Glob, Grep, Bash
504
- category: base
505
- tags: frontend, ui, components
506
- ---
507
-
508
- # {{projectName}} Frontend Agent
509
-
510
- You implement client-side code for **{{projectName}}** using {{#if framework}}{{framework}}{{else}}modern web standards{{/if}}.
511
-
512
- ## Tech Stack
513
-
514
- - **Language**: {{language}}
515
- {{#if framework}}- **Framework**: {{framework}}{{/if}}
516
- - **Styling**: {{#if styling}}{{styling}}{{else}}CSS{{/if}}
517
-
518
- ## Your Scope
519
-
520
- - UI Components
521
- - Pages and Routes
522
- - State Management
523
- - API Integration
524
- - Form Validation
525
- - Client-side Routing
526
- - Responsive Design
527
- - Accessibility
528
-
529
- See \`.claude/skills/frontend-patterns/SKILL.md\` for detailed implementation patterns.
530
-
531
- ## Boundaries
532
-
533
- ### Handles
534
- - Client-side UI and interactions
535
- - Component architecture
536
- - State management
537
- - Form handling
538
-
539
- ### Delegates to @backend
540
- - API endpoints, business logic
541
-
542
- ### Delegates to @tester
543
- - Component tests, E2E tests
544
-
545
- ## Rules
546
-
547
- 1. **Semantic HTML** - Use proper HTML5 elements
548
- 2. **Accessibility first** - WCAG 2.1 AA compliance
549
- 3. **Responsive design** - Mobile-first approach
550
- 4. **Performance** - Optimize bundle size and rendering
551
- `
552
- };
553
- }
554
- }
555
-
556
- export default TemplateManager;