claude-autopm 1.27.0 → 1.28.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/README.md CHANGED
@@ -44,6 +44,46 @@ PRD → Epic Decomposition → Parallel Development → Testing → Production
44
44
 
45
45
  ## ✨ Key Features
46
46
 
47
+ ### 🆕 **NEW in v1.28.0: Templates & Scaffolding!**
48
+
49
+ **PRD Templates (Quick Start)**
50
+ - 📋 **5 Built-in Templates** - api-feature, ui-feature, bug-fix, data-migration, documentation
51
+ - 🚀 **70% Faster** - Create PRDs in 9 minutes instead of 30
52
+ - 🎯 **Context7-Verified** - All templates use 2025 best practices
53
+ - ✨ **Smart Variables** - Auto-generate IDs, timestamps, authors
54
+ - 🎨 **Custom Templates** - Create your own team-specific templates
55
+
56
+ **Template Commands**
57
+ ```bash
58
+ # Create PRD from template
59
+ autopm prd:new --template api-feature "Payment API"
60
+
61
+ # Interactive template selection
62
+ autopm prd:new "my-feature"
63
+
64
+ # List available templates
65
+ autopm template:list
66
+
67
+ # Create custom template
68
+ autopm template:new prd my-custom-template
69
+ ```
70
+
71
+ **260+ Tests Passing** - Production-ready with comprehensive test coverage!
72
+
73
+ ---
74
+
75
+ ### 🎉 **v1.27.0: Phase 2 Complete!**
76
+
77
+ **GitHub Sync (Bidirectional)**
78
+ - 📤 **Upload to GitHub Issues** - Sync PRDs/Epics/Tasks with smart conflict detection
79
+ - 📥 **Download from GitHub** - Pull Issues back to local files with reverse mapping
80
+ - 🔄 **Bidirectional Mapping** - Maintain consistency with `sync-map.json`
81
+
82
+ **Task Management**
83
+ - ✅ **Complete Task Lifecycle** - List, show, update tasks within epics
84
+ - 🔗 **Dependency Tracking** - Validate task dependencies automatically
85
+ - 📊 **Progress Auto-update** - Epic progress updates on task completion
86
+
47
87
  ### 🤖 **39 Specialized AI Agents**
48
88
 
49
89
  Organized into dynamic teams that load based on your work context:
@@ -1,16 +1,282 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * PRD New - Launch brainstorming for new product requirement
4
+ *
5
+ * Supports:
6
+ * - Template-based creation (--template flag)
7
+ * - Interactive template selection
8
+ * - Traditional brainstorming mode (backwards compatible)
4
9
  */
5
10
 
6
11
  const fs = require('fs');
7
12
  const path = require('path');
8
13
  const readline = require('readline');
9
14
 
15
+ // Dynamically resolve template engine path
16
+ // This works both in installed projects and during testing
17
+ let TemplateEngine;
18
+ try {
19
+ // Try relative path from .claude/scripts/pm/
20
+ TemplateEngine = require(path.join(__dirname, '..', '..', '..', '..', 'lib', 'template-engine'));
21
+ } catch (err) {
22
+ try {
23
+ // Try from project root
24
+ TemplateEngine = require(path.join(process.cwd(), 'lib', 'template-engine'));
25
+ } catch (err2) {
26
+ // Fallback to relative
27
+ TemplateEngine = require('../../../../lib/template-engine');
28
+ }
29
+ }
30
+
10
31
  class PrdCreator {
11
32
  constructor() {
12
33
  this.prdsDir = path.join('.claude', 'prds');
13
34
  this.templatesDir = path.join(__dirname, '..', '..', 'templates');
35
+ this.templateEngine = new TemplateEngine();
36
+ }
37
+
38
+ /**
39
+ * Create PRD from template
40
+ */
41
+ async createPrdFromTemplate(prdName, templateName) {
42
+ console.log(`\n🚀 Creating PRD from Template: ${templateName}`);
43
+ console.log(`${'═'.repeat(50)}\n`);
44
+
45
+ // Ensure PRDs directory exists
46
+ if (!fs.existsSync(this.prdsDir)) {
47
+ fs.mkdirSync(this.prdsDir, { recursive: true });
48
+ }
49
+
50
+ // Check if PRD already exists
51
+ const prdFile = path.join(this.prdsDir, `${prdName}.md`);
52
+ if (fs.existsSync(prdFile)) {
53
+ console.error(`❌ PRD already exists: ${prdName}`);
54
+ console.log(`💡 Edit file: .claude/prds/${prdName}.md`);
55
+ return false;
56
+ }
57
+
58
+ // Find template
59
+ const templatePath = this.templateEngine.findTemplate('prds', templateName);
60
+ if (!templatePath) {
61
+ console.error(`❌ Template not found: ${templateName}`);
62
+ console.log(`💡 List available templates: autopm template:list`);
63
+ return false;
64
+ }
65
+
66
+ // Read template to find required variables
67
+ const templateContent = fs.readFileSync(templatePath, 'utf8');
68
+ const requiredVars = this.extractTemplateVariables(templateContent);
69
+
70
+ // Prompt for variables
71
+ const rl = readline.createInterface({
72
+ input: process.stdin,
73
+ output: process.stdout
74
+ });
75
+
76
+ const prompt = (question) => new Promise((resolve) => {
77
+ rl.question(question, resolve);
78
+ });
79
+
80
+ try {
81
+ console.log(`📋 Template: ${templateName}`);
82
+ console.log(`Fill in the following details:\n`);
83
+
84
+ const variables = {
85
+ title: prdName.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '),
86
+ type: 'prd'
87
+ };
88
+
89
+ // Prompt for common variables
90
+ const title = await prompt(`Title [${variables.title}]: `);
91
+ if (title) variables.title = title;
92
+
93
+ const priority = await prompt('Priority (P0/P1/P2/P3) [P2]: ');
94
+ variables.priority = priority || 'P2';
95
+
96
+ const timeline = await prompt('Timeline [TBD]: ');
97
+ variables.timeline = timeline || 'TBD';
98
+
99
+ // Prompt for template-specific variables
100
+ const templateSpecific = this.getTemplateSpecificPrompts(templateName);
101
+ for (const varName of templateSpecific) {
102
+ if (!variables[varName]) {
103
+ const value = await prompt(`${varName.replace(/_/g, ' ')}: `);
104
+ variables[varName] = value || '';
105
+ }
106
+ }
107
+
108
+ // Render template
109
+ const rendered = this.templateEngine.renderFile(templatePath, variables);
110
+
111
+ // Write PRD file
112
+ fs.writeFileSync(prdFile, rendered);
113
+
114
+ console.log('\n✅ PRD created successfully!');
115
+ console.log(`📄 File: ${prdFile}`);
116
+
117
+ // Show next steps
118
+ this.showNextSteps(prdName);
119
+
120
+ return true;
121
+ } finally {
122
+ rl.close();
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Extract variables from template
128
+ */
129
+ extractTemplateVariables(template) {
130
+ const varRegex = /\{\{(\w+)\}\}/g;
131
+ const vars = new Set();
132
+ let match;
133
+
134
+ while ((match = varRegex.exec(template)) !== null) {
135
+ vars.add(match[1]);
136
+ }
137
+
138
+ // Remove auto-generated variables
139
+ vars.delete('id');
140
+ vars.delete('timestamp');
141
+ vars.delete('date');
142
+ vars.delete('author');
143
+
144
+ return Array.from(vars);
145
+ }
146
+
147
+ /**
148
+ * Get template-specific prompts
149
+ */
150
+ getTemplateSpecificPrompts(templateName) {
151
+ const prompts = {
152
+ 'api-feature': [
153
+ 'api_purpose',
154
+ 'problem',
155
+ 'business_value',
156
+ 'http_method',
157
+ 'api_endpoint',
158
+ 'auth_method',
159
+ 'rate_limit',
160
+ 'user_role',
161
+ 'api_action',
162
+ 'user_benefit'
163
+ ],
164
+ 'ui-feature': [
165
+ 'component_type',
166
+ 'feature_purpose',
167
+ 'problem',
168
+ 'user_need',
169
+ 'user_goal',
170
+ 'user_role',
171
+ 'user_action',
172
+ 'user_benefit'
173
+ ],
174
+ 'bug-fix': [
175
+ 'bug_summary',
176
+ 'severity',
177
+ 'user_impact',
178
+ 'step_1',
179
+ 'step_2',
180
+ 'step_3',
181
+ 'expected_behavior',
182
+ 'actual_behavior',
183
+ 'root_cause',
184
+ 'solution_approach'
185
+ ],
186
+ 'data-migration': [
187
+ 'migration_purpose',
188
+ 'current_state',
189
+ 'desired_state',
190
+ 'affected_tables',
191
+ 'data_volume',
192
+ 'migration_strategy'
193
+ ],
194
+ 'documentation': [
195
+ 'doc_type',
196
+ 'target_audience',
197
+ 'documentation_scope',
198
+ 'current_gaps'
199
+ ]
200
+ };
201
+
202
+ return prompts[templateName] || [];
203
+ }
204
+
205
+ /**
206
+ * Show interactive template selection
207
+ */
208
+ async selectTemplate() {
209
+ const templates = this.templateEngine.listTemplates('prds');
210
+
211
+ if (templates.length === 0) {
212
+ console.log('No templates available');
213
+ return null;
214
+ }
215
+
216
+ console.log('\n📋 Available Templates:');
217
+
218
+ const builtIn = templates.filter(t => !t.custom);
219
+ const custom = templates.filter(t => t.custom);
220
+
221
+ let index = 1;
222
+ const options = [];
223
+
224
+ builtIn.forEach(t => {
225
+ const description = this.getTemplateDescription(t.name);
226
+ console.log(`${index}. ${t.name.padEnd(20)} - ${description}`);
227
+ options.push(t.name);
228
+ index++;
229
+ });
230
+
231
+ if (custom.length > 0) {
232
+ console.log('\nCustom Templates:');
233
+ custom.forEach(t => {
234
+ console.log(`${index}. ${t.name.padEnd(20)} - [Custom]`);
235
+ options.push(t.name);
236
+ index++;
237
+ });
238
+ }
239
+
240
+ console.log(`${index}. none${' '.repeat(20)} - Create empty PRD\n`);
241
+ options.push('none');
242
+
243
+ const rl = readline.createInterface({
244
+ input: process.stdin,
245
+ output: process.stdout
246
+ });
247
+
248
+ const prompt = (question) => new Promise((resolve) => {
249
+ rl.question(question, resolve);
250
+ });
251
+
252
+ try {
253
+ const selection = await prompt(`Select template (1-${options.length}): `);
254
+ const selectionNum = parseInt(selection, 10);
255
+
256
+ if (selectionNum < 1 || selectionNum > options.length) {
257
+ console.error('Invalid selection');
258
+ return null;
259
+ }
260
+
261
+ return options[selectionNum - 1];
262
+ } finally {
263
+ rl.close();
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Get template description
269
+ */
270
+ getTemplateDescription(templateName) {
271
+ const descriptions = {
272
+ 'api-feature': 'REST/GraphQL API development',
273
+ 'ui-feature': 'Frontend component/page',
274
+ 'bug-fix': 'Bug resolution workflow',
275
+ 'data-migration': 'Database schema changes',
276
+ 'documentation': 'Documentation updates'
277
+ };
278
+
279
+ return descriptions[templateName] || 'Template';
14
280
  }
15
281
 
16
282
  async createPrd(prdName) {
@@ -319,7 +585,19 @@ ${data.technical || 'Technical requirements to be specified...'}
319
585
  }
320
586
 
321
587
  async run(args) {
322
- let prdName = args[0];
588
+ // Parse arguments
589
+ let prdName = null;
590
+ let templateName = null;
591
+
592
+ // Check for --template or -t flag
593
+ for (let i = 0; i < args.length; i++) {
594
+ if (args[i] === '--template' || args[i] === '-t') {
595
+ templateName = args[i + 1];
596
+ i++; // Skip next arg
597
+ } else if (!prdName) {
598
+ prdName = args[i];
599
+ }
600
+ }
323
601
 
324
602
  if (!prdName) {
325
603
  // Interactive mode
@@ -345,12 +623,24 @@ ${data.technical || 'Technical requirements to be specified...'}
345
623
  console.error('❌ Error: PRD name required');
346
624
  process.exit(1);
347
625
  }
626
+
627
+ // Ask for template if not provided
628
+ if (!templateName) {
629
+ templateName = await this.selectTemplate();
630
+ }
348
631
  }
349
632
 
350
633
  // Sanitize PRD name
351
634
  prdName = prdName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
352
635
 
353
- const success = await this.createPrd(prdName);
636
+ // Create PRD from template or traditional mode
637
+ let success;
638
+ if (templateName && templateName !== 'none') {
639
+ success = await this.createPrdFromTemplate(prdName, templateName);
640
+ } else {
641
+ success = await this.createPrd(prdName);
642
+ }
643
+
354
644
  process.exit(success ? 0 : 1);
355
645
  }
356
646
  }
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Template List - Show available templates
4
+ *
5
+ * Usage:
6
+ * autopm template:list
7
+ * autopm template:list prd
8
+ * autopm template:list epic
9
+ * autopm template:list task
10
+ */
11
+
12
+ const path = require('path');
13
+
14
+ // Dynamically resolve template engine path
15
+ let TemplateEngine;
16
+ try {
17
+ TemplateEngine = require(path.join(__dirname, '..', '..', '..', '..', 'lib', 'template-engine'));
18
+ } catch (err) {
19
+ try {
20
+ TemplateEngine = require(path.join(process.cwd(), 'lib', 'template-engine'));
21
+ } catch (err2) {
22
+ TemplateEngine = require('../../../lib/template-engine');
23
+ }
24
+ }
25
+
26
+ class TemplateLister {
27
+ constructor() {
28
+ this.templateEngine = new TemplateEngine();
29
+ this.descriptions = {
30
+ // PRD templates
31
+ 'api-feature': 'REST/GraphQL API development',
32
+ 'ui-feature': 'Frontend component/page',
33
+ 'bug-fix': 'Bug resolution workflow',
34
+ 'data-migration': 'Database schema changes',
35
+ 'documentation': 'Documentation updates',
36
+
37
+ // Epic templates (future)
38
+ 'sprint': 'Sprint planning',
39
+ 'release': 'Release epic',
40
+
41
+ // Task templates (future)
42
+ 'development': 'Development task',
43
+ 'testing': 'Testing task'
44
+ };
45
+ }
46
+
47
+ list(type = null) {
48
+ const types = type ? [type] : ['prds', 'epics', 'tasks'];
49
+
50
+ console.log('\n📋 Available Templates\n');
51
+ console.log('═'.repeat(60));
52
+
53
+ for (const templateType of types) {
54
+ const templates = this.templateEngine.listTemplates(templateType);
55
+
56
+ if (templates.length === 0) {
57
+ continue;
58
+ }
59
+
60
+ const typeName = templateType.charAt(0).toUpperCase() + templateType.slice(1);
61
+ console.log(`\n${typeName} Templates:`);
62
+
63
+ const builtIn = templates.filter(t => !t.custom);
64
+ const custom = templates.filter(t => t.custom);
65
+
66
+ if (builtIn.length > 0) {
67
+ console.log('\nBuilt-in:');
68
+ builtIn.forEach(t => {
69
+ const desc = this.descriptions[t.name] || 'Template';
70
+ console.log(` • ${t.name.padEnd(20)} - ${desc}`);
71
+ });
72
+ }
73
+
74
+ if (custom.length > 0) {
75
+ console.log('\nCustom:');
76
+ custom.forEach(t => {
77
+ console.log(` • ${t.name.padEnd(20)} - [Custom Template]`);
78
+ });
79
+ }
80
+ }
81
+
82
+ console.log('\n' + '═'.repeat(60));
83
+ console.log('\nUsage:');
84
+ console.log(' autopm prd:new --template <name> "<title>"');
85
+ console.log(' autopm prd:new -t api-feature "User Authentication API"');
86
+ console.log('\nCreate custom template:');
87
+ console.log(' autopm template:new prd my-custom-template\n');
88
+ }
89
+
90
+ run(args) {
91
+ const type = args[0]; // Optional: 'prd', 'epic', 'task'
92
+ const validTypes = ['prd', 'prds', 'epic', 'epics', 'task', 'tasks'];
93
+
94
+ if (type && !validTypes.includes(type)) {
95
+ console.error(`❌ Invalid type: ${type}`);
96
+ console.log('Valid types: prd, epic, task');
97
+ process.exit(1);
98
+ }
99
+
100
+ // Normalize type
101
+ let normalizedType = null;
102
+ if (type) {
103
+ if (type === 'prd') normalizedType = 'prds';
104
+ else if (type === 'epic') normalizedType = 'epics';
105
+ else if (type === 'task') normalizedType = 'tasks';
106
+ else normalizedType = type;
107
+ }
108
+
109
+ this.list(normalizedType);
110
+ }
111
+ }
112
+
113
+ // Main execution
114
+ if (require.main === module) {
115
+ const lister = new TemplateLister();
116
+ lister.run(process.argv.slice(2));
117
+ }
118
+
119
+ module.exports = TemplateLister;