claude-autopm 3.0.0 → 3.0.2

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.
@@ -0,0 +1,345 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Epic Oneshot - One-command workflow: Parse PRD → Decompose → Sync
4
+ *
5
+ * Usage: node epic-oneshot.js <feature-name> [options]
6
+ *
7
+ * This script combines three operations:
8
+ * 1. Parse PRD into epic structure
9
+ * 2. Decompose epic into implementation tasks
10
+ * 3. Sync to GitHub/Azure DevOps
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const { execSync } = require('child_process');
16
+
17
+ class EpicOneshot {
18
+ constructor() {
19
+ this.scriptsDir = __dirname;
20
+ this.claudeDir = path.join(process.cwd(), '.claude');
21
+ this.prdsDir = path.join(this.claudeDir, 'prds');
22
+ this.epicsDir = path.join(this.claudeDir, 'epics');
23
+ }
24
+
25
+ log(message, emoji = '📋') {
26
+ console.log(`${emoji} ${message}`);
27
+ }
28
+
29
+ error(message) {
30
+ console.error(`❌ ${message}`);
31
+ }
32
+
33
+ success(message) {
34
+ console.log(`✅ ${message}`);
35
+ }
36
+
37
+ async step1ParsePRD(featureName) {
38
+ this.log('Step 1/3: Parsing PRD into epic...', '🔄');
39
+ console.log(`${'═'.repeat(50)}\n`);
40
+
41
+ const prdParseScript = path.join(this.scriptsDir, 'prd-parse.js');
42
+
43
+ if (!fs.existsSync(prdParseScript)) {
44
+ this.error('PRD parse script not found');
45
+ return false;
46
+ }
47
+
48
+ try {
49
+ execSync(`node "${prdParseScript}" ${featureName}`, {
50
+ stdio: 'inherit',
51
+ cwd: process.cwd()
52
+ });
53
+
54
+ // Verify epic was created
55
+ const epicFile = path.join(this.epicsDir, featureName, 'epic.md');
56
+ if (fs.existsSync(epicFile)) {
57
+ this.success('Epic created successfully');
58
+ return true;
59
+ } else {
60
+ this.error('Epic file not created');
61
+ return false;
62
+ }
63
+ } catch (error) {
64
+ this.error(`Failed to parse PRD: ${error.message}`);
65
+ return false;
66
+ }
67
+ }
68
+
69
+ async step2DecomposeTasks(featureName) {
70
+ this.log('Step 2/3: Decomposing epic into tasks...', '🔨');
71
+ console.log(`${'═'.repeat(50)}\n`);
72
+
73
+ const epicFile = path.join(this.epicsDir, featureName, 'epic.md');
74
+
75
+ if (!fs.existsSync(epicFile)) {
76
+ this.error('Epic file not found');
77
+ return false;
78
+ }
79
+
80
+ try {
81
+ // Read epic file
82
+ const epicContent = fs.readFileSync(epicFile, 'utf8');
83
+
84
+ // Check if tasks already exist
85
+ if (epicContent.includes('## Tasks') || epicContent.includes('## Implementation Tasks')) {
86
+ this.log('Tasks already exist in epic', 'ℹ️');
87
+ return true;
88
+ }
89
+
90
+ // Generate tasks section
91
+ const tasksSection = this.generateTasksFromEpic(epicContent, featureName);
92
+
93
+ // Append tasks to epic
94
+ const updatedContent = epicContent + '\n\n' + tasksSection;
95
+ fs.writeFileSync(epicFile, updatedContent, 'utf8');
96
+
97
+ this.success('Tasks decomposed successfully');
98
+ return true;
99
+ } catch (error) {
100
+ this.error(`Failed to decompose tasks: ${error.message}`);
101
+ return false;
102
+ }
103
+ }
104
+
105
+ generateTasksFromEpic(epicContent, featureName) {
106
+ // Extract features and requirements from epic
107
+ const features = this.extractListItems(epicContent, 'Features');
108
+ const requirements = this.extractListItems(epicContent, 'Requirements');
109
+
110
+ let tasks = [];
111
+ let taskId = 1;
112
+
113
+ // Setup task
114
+ tasks.push({
115
+ id: taskId++,
116
+ title: 'Project setup and configuration',
117
+ description: 'Initialize project structure, dependencies, and development environment',
118
+ effort: '2h',
119
+ type: 'setup'
120
+ });
121
+
122
+ // Feature implementation tasks
123
+ features.forEach(feature => {
124
+ tasks.push({
125
+ id: taskId++,
126
+ title: `Implement: ${feature}`,
127
+ description: `Implementation for ${feature}`,
128
+ effort: '1d',
129
+ type: 'feature'
130
+ });
131
+ });
132
+
133
+ // Requirements implementation tasks
134
+ requirements.forEach(req => {
135
+ tasks.push({
136
+ id: taskId++,
137
+ title: `Requirement: ${req}`,
138
+ description: `Implementation for ${req}`,
139
+ effort: '1d',
140
+ type: 'requirement'
141
+ });
142
+ });
143
+
144
+ // Integration tasks
145
+ tasks.push({
146
+ id: taskId++,
147
+ title: 'Integration and API connections',
148
+ description: 'Connect components and integrate APIs',
149
+ effort: '1d',
150
+ type: 'integration'
151
+ });
152
+
153
+ // Testing task
154
+ tasks.push({
155
+ id: taskId++,
156
+ title: 'Testing and quality assurance',
157
+ description: 'Write tests, perform QA, and fix bugs',
158
+ effort: '1d',
159
+ type: 'testing'
160
+ });
161
+
162
+ // Deployment task
163
+ tasks.push({
164
+ id: taskId++,
165
+ title: 'Deployment and documentation',
166
+ description: 'Deploy to production and update documentation',
167
+ effort: '4h',
168
+ type: 'deployment'
169
+ });
170
+
171
+ // Format tasks as markdown
172
+ let tasksMarkdown = '## Implementation Tasks\n\n';
173
+ tasksMarkdown += '### Task Breakdown\n\n';
174
+
175
+ tasks.forEach(task => {
176
+ tasksMarkdown += `#### TASK-${String(task.id).padStart(3, '0')}: ${task.title}\n\n`;
177
+ tasksMarkdown += `**Type:** ${task.type}\n`;
178
+ tasksMarkdown += `**Effort:** ${task.effort}\n`;
179
+ tasksMarkdown += `**Description:** ${task.description}\n\n`;
180
+ });
181
+
182
+ return tasksMarkdown;
183
+ }
184
+
185
+ extractListItems(content, sectionName) {
186
+ const items = [];
187
+ const regex = new RegExp(`## ${sectionName}[\\s\\S]*?(?=##|$)`, 'i');
188
+ const match = content.match(regex);
189
+
190
+ if (match) {
191
+ const section = match[0];
192
+ const lines = section.split('\n');
193
+
194
+ lines.forEach(line => {
195
+ const trimmed = line.trim();
196
+ if (trimmed.match(/^[-*•]\s/) || trimmed.match(/^\d+\.\s/)) {
197
+ // Remove bullet points or numbers
198
+ let item = trimmed.replace(/^[-*•]\s+/, '').trim();
199
+ item = item.replace(/^\d+\.\s+/, '').trim();
200
+ if (item) items.push(item);
201
+ }
202
+ });
203
+ }
204
+
205
+ return items;
206
+ }
207
+
208
+ async step3SyncToProvider(featureName) {
209
+ this.log('Step 3/3: Syncing to GitHub/Azure DevOps...', '🔄');
210
+ console.log(`${'═'.repeat(50)}\n`);
211
+
212
+ // Check if sync script exists
213
+ const syncScript = path.join(this.scriptsDir, 'sync.js');
214
+ const epicSyncScript = path.join(this.scriptsDir, 'epic-sync.sh');
215
+
216
+ if (fs.existsSync(epicSyncScript)) {
217
+ try {
218
+ execSync(`bash "${epicSyncScript}" ${featureName}`, {
219
+ stdio: 'inherit',
220
+ cwd: process.cwd()
221
+ });
222
+ this.success('Epic synced to provider');
223
+ return true;
224
+ } catch (error) {
225
+ this.error(`Failed to sync: ${error.message}`);
226
+ return false;
227
+ }
228
+ } else if (fs.existsSync(syncScript)) {
229
+ try {
230
+ execSync(`node "${syncScript}" epic ${featureName}`, {
231
+ stdio: 'inherit',
232
+ cwd: process.cwd()
233
+ });
234
+ this.success('Epic synced to provider');
235
+ return true;
236
+ } catch (error) {
237
+ this.error(`Failed to sync: ${error.message}`);
238
+ return false;
239
+ }
240
+ } else {
241
+ this.log('Sync script not found - skipping provider sync', '⚠️');
242
+ this.log('Epic is ready locally. Use /pm:epic-sync to sync manually', 'ℹ️');
243
+ return true; // Don't fail if sync not available
244
+ }
245
+ }
246
+
247
+ async run(featureName, options = {}) {
248
+ console.log('\n╔════════════════════════════════════════════════╗');
249
+ console.log('║ Epic Oneshot Workflow ║');
250
+ console.log('╚════════════════════════════════════════════════╝\n');
251
+
252
+ this.log(`Feature: ${featureName}`, '🎯');
253
+ console.log('\n');
254
+
255
+ // Validate PRD exists
256
+ const prdFile = path.join(this.prdsDir, `${featureName}.md`);
257
+ if (!fs.existsSync(prdFile)) {
258
+ this.error(`PRD not found: ${featureName}.md`);
259
+ this.log('Create it first with: /pm:prd-new ' + featureName, '💡');
260
+ process.exit(1);
261
+ }
262
+
263
+ // Step 1: Parse PRD
264
+ const parseSuccess = await this.step1ParsePRD(featureName);
265
+ if (!parseSuccess) {
266
+ this.error('Failed at step 1 (Parse PRD)');
267
+ process.exit(1);
268
+ }
269
+ console.log('\n');
270
+
271
+ // Step 2: Decompose tasks
272
+ const decomposeSuccess = await this.step2DecomposeTasks(featureName);
273
+ if (!decomposeSuccess) {
274
+ this.error('Failed at step 2 (Decompose tasks)');
275
+ process.exit(1);
276
+ }
277
+ console.log('\n');
278
+
279
+ // Step 3: Sync to provider (optional)
280
+ if (!options.noSync) {
281
+ await this.step3SyncToProvider(featureName);
282
+ } else {
283
+ this.log('Skipping sync (--no-sync flag)', 'ℹ️');
284
+ }
285
+
286
+ console.log('\n╔════════════════════════════════════════════════╗');
287
+ console.log('║ Epic Oneshot Complete! ✨ ║');
288
+ console.log('╚════════════════════════════════════════════════╝\n');
289
+
290
+ this.log('Next steps:', '📋');
291
+ console.log(` • View epic: /pm:epic-show ${featureName}`);
292
+ console.log(` • Get next task: /pm:next`);
293
+ console.log(` • View status: /pm:status`);
294
+ console.log('');
295
+ }
296
+ }
297
+
298
+ // CLI execution
299
+ if (require.main === module) {
300
+ const args = process.argv.slice(2);
301
+
302
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
303
+ console.log(`
304
+ ╔════════════════════════════════════════════════╗
305
+ ║ Epic Oneshot - Quick Start ║
306
+ ╚════════════════════════════════════════════════╝
307
+
308
+ Usage: node epic-oneshot.js <feature-name> [options]
309
+
310
+ Options:
311
+ --no-sync Skip syncing to GitHub/Azure DevOps
312
+ --help Show this help message
313
+
314
+ Example:
315
+ node epic-oneshot.js user-authentication
316
+ node epic-oneshot.js my-feature --no-sync
317
+
318
+ What it does:
319
+ 1. 🔄 Parses PRD into epic structure
320
+ 2. 🔨 Decomposes epic into implementation tasks
321
+ 3. 🔄 Syncs to GitHub/Azure DevOps
322
+
323
+ Prerequisites:
324
+ • PRD must exist in .claude/prds/<feature-name>.md
325
+ • Create with: /pm:prd-new <feature-name>
326
+ `);
327
+ process.exit(0);
328
+ }
329
+
330
+ const featureName = args[0];
331
+ const options = {
332
+ noSync: args.includes('--no-sync')
333
+ };
334
+
335
+ const oneshot = new EpicOneshot();
336
+ oneshot.run(featureName, options).catch(error => {
337
+ console.error('❌ Fatal error:', error.message);
338
+ if (process.env.DEBUG) {
339
+ console.error(error.stack);
340
+ }
341
+ process.exit(1);
342
+ });
343
+ }
344
+
345
+ module.exports = EpicOneshot;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudeautopm/plugin-testing",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Testing frameworks, E2E testing, and quality assurance specialists",
5
5
  "keywords": [
6
6
  "claudeautopm",
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Add missing wrapper scripts to plugin.json
4
+ *
5
+ * This script finds all .sh wrapper scripts and ensures they have
6
+ * corresponding entries in plugin.json as standalone scripts.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ const pluginJsonPath = path.join(__dirname, '..', 'packages', 'plugin-pm', 'plugin.json');
13
+ const scriptsDir = path.join(__dirname, '..', 'packages', 'plugin-pm', 'scripts', 'pm');
14
+
15
+ // Read plugin.json
16
+ const pluginJson = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf8'));
17
+
18
+ // Get all .sh files
19
+ const shellScripts = fs.readdirSync(scriptsDir)
20
+ .filter(file => file.endsWith('.sh'))
21
+ .map(file => path.basename(file, '.sh'));
22
+
23
+ console.log(`Found ${shellScripts.length} shell scripts:`, shellScripts);
24
+
25
+ // Find which ones are missing as standalone entries
26
+ const existingScripts = new Set(
27
+ pluginJson.scripts
28
+ .filter(s => s.file && s.file.includes('.sh'))
29
+ .map(s => path.basename(s.file, '.sh'))
30
+ );
31
+
32
+ console.log(`\nExisting standalone entries:`, Array.from(existingScripts));
33
+
34
+ const missingScripts = shellScripts.filter(name => !existingScripts.has(name));
35
+
36
+ console.log(`\nMissing standalone entries:`, missingScripts);
37
+
38
+ if (missingScripts.length === 0) {
39
+ console.log('\n✅ All wrapper scripts already have standalone entries!');
40
+ process.exit(0);
41
+ }
42
+
43
+ // Add missing scripts
44
+ const newEntries = missingScripts.map(name => ({
45
+ name: name,
46
+ file: `scripts/pm/${name}.sh`,
47
+ description: `${name.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')} wrapper script`,
48
+ type: 'workflow',
49
+ executable: true,
50
+ category: 'pm-workflow',
51
+ tags: ['pm', name.includes('epic') ? 'epic' : 'workflow', 'automation']
52
+ }));
53
+
54
+ console.log(`\n📝 Adding ${newEntries.length} new entries...`);
55
+
56
+ // Insert new entries after existing scripts
57
+ pluginJson.scripts.push(...newEntries);
58
+
59
+ // Write back
60
+ fs.writeFileSync(pluginJsonPath, JSON.stringify(pluginJson, null, 2) + '\n', 'utf8');
61
+
62
+ console.log(`\n✅ Updated plugin.json with ${newEntries.length} new wrapper script entries`);
63
+ console.log(`\nAdded scripts:`);
64
+ newEntries.forEach(entry => {
65
+ console.log(` - ${entry.name} (${entry.file})`);
66
+ });