claude-autopm 2.1.1 → 2.2.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.
@@ -17,6 +17,7 @@ const fs = require('fs-extra');
17
17
  const ora = require('ora');
18
18
  const chalk = require('chalk');
19
19
  const path = require('path');
20
+ const { spawn } = require('child_process');
20
21
 
21
22
  /**
22
23
  * Get PRD file path
@@ -46,6 +47,310 @@ async function readPrdFile(name) {
46
47
  return await fs.readFile(prdPath, 'utf8');
47
48
  }
48
49
 
50
+ /**
51
+ * List all PRDs
52
+ */
53
+ async function prdList(argv) {
54
+ const spinner = ora('Loading PRDs...').start();
55
+
56
+ try {
57
+ const prdsDir = path.join(process.cwd(), '.claude', 'prds');
58
+
59
+ // Check if directory exists
60
+ const dirExists = await fs.pathExists(prdsDir);
61
+ if (!dirExists) {
62
+ spinner.info(chalk.yellow('No PRDs directory found'));
63
+ console.log(chalk.yellow('\nCreate your first PRD with: autopm prd new <name>'));
64
+ return;
65
+ }
66
+
67
+ // Read all PRD files
68
+ const files = await fs.readdir(prdsDir);
69
+ const prdFiles = files.filter(f => f.endsWith('.md'));
70
+
71
+ if (prdFiles.length === 0) {
72
+ spinner.info(chalk.yellow('No PRDs found'));
73
+ console.log(chalk.yellow('\nCreate your first PRD with: autopm prd new <name>'));
74
+ return;
75
+ }
76
+
77
+ spinner.succeed(chalk.green(`Found ${prdFiles.length} PRD(s)`));
78
+
79
+ // Read and parse each PRD
80
+ const prds = [];
81
+ for (const file of prdFiles) {
82
+ const filePath = path.join(prdsDir, file);
83
+ const content = await fs.readFile(filePath, 'utf8');
84
+
85
+ // Extract frontmatter
86
+ const titleMatch = content.match(/^title:\s*(.+)$/m);
87
+ const statusMatch = content.match(/^status:\s*(\w+)$/m);
88
+ const priorityMatch = content.match(/^priority:\s*(P\d|Critical|High|Medium|Low)$/m);
89
+ const createdMatch = content.match(/^created:\s*(.+)$/m);
90
+
91
+ prds.push({
92
+ name: file.replace('.md', ''),
93
+ title: titleMatch ? titleMatch[1] : file.replace('.md', ''),
94
+ status: statusMatch ? statusMatch[1] : 'unknown',
95
+ priority: priorityMatch ? priorityMatch[1] : 'P2',
96
+ created: createdMatch ? createdMatch[1] : 'unknown'
97
+ });
98
+ }
99
+
100
+ // Sort by priority (P0 > P1 > P2 > P3)
101
+ prds.sort((a, b) => {
102
+ const priorities = { 'P0': 0, 'Critical': 0, 'P1': 1, 'High': 1, 'P2': 2, 'Medium': 2, 'P3': 3, 'Low': 3 };
103
+ return (priorities[a.priority] || 2) - (priorities[b.priority] || 2);
104
+ });
105
+
106
+ // Display PRDs
107
+ console.log(chalk.green('\nšŸ“‹ PRDs:\n'));
108
+
109
+ prds.forEach((prd, index) => {
110
+ const priorityColor = prd.priority.startsWith('P0') || prd.priority === 'Critical' ? chalk.red :
111
+ prd.priority.startsWith('P1') || prd.priority === 'High' ? chalk.yellow :
112
+ chalk.blue;
113
+
114
+ const statusColor = prd.status === 'completed' ? chalk.green :
115
+ prd.status === 'in-progress' ? chalk.yellow :
116
+ prd.status === 'draft' ? chalk.gray :
117
+ chalk.white;
118
+
119
+ console.log(`${index + 1}. ${chalk.bold(prd.name)}`);
120
+ console.log(` ${priorityColor(prd.priority.padEnd(10))} ${statusColor(prd.status.padEnd(12))} ${chalk.gray(prd.created)}`);
121
+ if (prd.title !== prd.name) {
122
+ console.log(` ${chalk.dim(prd.title)}`);
123
+ }
124
+ console.log('');
125
+ });
126
+
127
+ console.log(chalk.dim(`\nTotal: ${prds.length} PRD(s)`));
128
+ console.log(chalk.dim('Use: autopm prd show <name> to view details\n'));
129
+
130
+ } catch (error) {
131
+ spinner.fail(chalk.red('Failed to list PRDs'));
132
+ console.error(chalk.red(`\nError: ${error.message}`));
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Show PRD content
138
+ * @param {Object} argv - Command arguments
139
+ */
140
+ async function prdShow(argv) {
141
+ const spinner = ora(`Loading PRD: ${argv.name}`).start();
142
+
143
+ try {
144
+ const content = await readPrdFile(argv.name);
145
+ spinner.succeed(chalk.green('PRD loaded'));
146
+
147
+ console.log('\n' + chalk.gray('─'.repeat(80)) + '\n');
148
+ console.log(content);
149
+ console.log('\n' + chalk.gray('─'.repeat(80)) + '\n');
150
+
151
+ const prdPath = getPrdPath(argv.name);
152
+ console.log(chalk.dim(`File: ${prdPath}\n`));
153
+
154
+ } catch (error) {
155
+ spinner.fail(chalk.red('Failed to show PRD'));
156
+
157
+ if (error.message.includes('not found')) {
158
+ console.error(chalk.red(`\nError: ${error.message}`));
159
+ console.error(chalk.yellow('Use: autopm prd list to see available PRDs'));
160
+ } else {
161
+ console.error(chalk.red(`\nError: ${error.message}`));
162
+ }
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Edit PRD in editor
168
+ * @param {Object} argv - Command arguments
169
+ */
170
+ async function prdEdit(argv) {
171
+ const spinner = ora(`Opening PRD: ${argv.name}`).start();
172
+
173
+ try {
174
+ const prdPath = getPrdPath(argv.name);
175
+
176
+ // Check if file exists
177
+ const exists = await fs.pathExists(prdPath);
178
+ if (!exists) {
179
+ spinner.fail(chalk.red('PRD not found'));
180
+ console.error(chalk.red(`\nError: PRD file not found: ${prdPath}`));
181
+ console.error(chalk.yellow('Use: autopm prd list to see available PRDs'));
182
+ return;
183
+ }
184
+
185
+ spinner.succeed(chalk.green('Opening editor...'));
186
+
187
+ // Determine editor
188
+ const editor = process.env.EDITOR || process.env.VISUAL || 'nano';
189
+
190
+ // Spawn editor
191
+ const { spawn } = require('child_process');
192
+ const child = spawn(editor, [prdPath], {
193
+ stdio: 'inherit',
194
+ cwd: process.cwd()
195
+ });
196
+
197
+ // Wait for editor to close
198
+ await new Promise((resolve, reject) => {
199
+ child.on('close', (code) => {
200
+ if (code === 0) {
201
+ console.log(chalk.green('\nāœ“ PRD saved'));
202
+ resolve();
203
+ } else {
204
+ reject(new Error(`Editor exited with code ${code}`));
205
+ }
206
+ });
207
+ child.on('error', reject);
208
+ });
209
+
210
+ } catch (error) {
211
+ spinner.fail(chalk.red('Failed to edit PRD'));
212
+ console.error(chalk.red(`\nError: ${error.message}`));
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Show PRD status
218
+ * @param {Object} argv - Command arguments
219
+ */
220
+ async function prdStatus(argv) {
221
+ const spinner = ora(`Analyzing PRD: ${argv.name}`).start();
222
+
223
+ try {
224
+ const content = await readPrdFile(argv.name);
225
+
226
+ // Extract metadata
227
+ const titleMatch = content.match(/^title:\s*(.+)$/m);
228
+ const statusMatch = content.match(/^status:\s*(\w+)$/m);
229
+ const priorityMatch = content.match(/^priority:\s*(P\d|Critical|High|Medium|Low)$/m);
230
+ const createdMatch = content.match(/^created:\s*(.+)$/m);
231
+ const authorMatch = content.match(/^author:\s*(.+)$/m);
232
+ const timelineMatch = content.match(/^timeline:\s*(.+)$/m);
233
+
234
+ // Count sections
235
+ const sections = {
236
+ 'Problem Statement': content.includes('## Problem Statement'),
237
+ 'User Stories': content.includes('## User Stories'),
238
+ 'Technical Requirements': content.includes('## Technical Requirements'),
239
+ 'Success Metrics': content.includes('## Success Metrics'),
240
+ 'Implementation Plan': content.includes('## Implementation Plan'),
241
+ 'Risks': content.includes('## Risks')
242
+ };
243
+
244
+ const completedSections = Object.values(sections).filter(Boolean).length;
245
+ const totalSections = Object.keys(sections).length;
246
+ const completeness = Math.round((completedSections / totalSections) * 100);
247
+
248
+ spinner.succeed(chalk.green('Status analyzed'));
249
+
250
+ // Display status
251
+ console.log('\n' + chalk.bold('šŸ“Š PRD Status Report') + '\n');
252
+ console.log(chalk.gray('─'.repeat(50)) + '\n');
253
+
254
+ console.log(chalk.bold('Metadata:'));
255
+ console.log(` Title: ${titleMatch ? titleMatch[1] : 'N/A'}`);
256
+ console.log(` Status: ${statusMatch ? chalk.yellow(statusMatch[1]) : 'N/A'}`);
257
+ console.log(` Priority: ${priorityMatch ? chalk.red(priorityMatch[1]) : 'N/A'}`);
258
+ console.log(` Created: ${createdMatch ? createdMatch[1] : 'N/A'}`);
259
+ console.log(` Author: ${authorMatch ? authorMatch[1] : 'N/A'}`);
260
+ console.log(` Timeline: ${timelineMatch ? timelineMatch[1] : 'N/A'}`);
261
+
262
+ console.log('\n' + chalk.bold('Completeness:') + ` ${completeness}%`);
263
+
264
+ const progressBar = 'ā–ˆ'.repeat(Math.floor(completeness / 5)) +
265
+ 'ā–‘'.repeat(20 - Math.floor(completeness / 5));
266
+ console.log(` [${completeness >= 80 ? chalk.green(progressBar) :
267
+ completeness >= 50 ? chalk.yellow(progressBar) :
268
+ chalk.red(progressBar)}]`);
269
+
270
+ console.log('\n' + chalk.bold('Sections:'));
271
+ Object.entries(sections).forEach(([name, exists]) => {
272
+ const icon = exists ? chalk.green('āœ“') : chalk.red('āœ—');
273
+ console.log(` ${icon} ${name}`);
274
+ });
275
+
276
+ // Statistics
277
+ const lines = content.split('\n').length;
278
+ const words = content.split(/\s+/).length;
279
+ const chars = content.length;
280
+
281
+ console.log('\n' + chalk.bold('Statistics:'));
282
+ console.log(` Lines: ${lines}`);
283
+ console.log(` Words: ${words}`);
284
+ console.log(` Chars: ${chars}`);
285
+
286
+ console.log('\n' + chalk.gray('─'.repeat(50)) + '\n');
287
+
288
+ const prdPath = getPrdPath(argv.name);
289
+ console.log(chalk.dim(`File: ${prdPath}\n`));
290
+
291
+ } catch (error) {
292
+ spinner.fail(chalk.red('Failed to analyze status'));
293
+
294
+ if (error.message.includes('not found')) {
295
+ console.error(chalk.red(`\nError: ${error.message}`));
296
+ } else {
297
+ console.error(chalk.red(`\nError: ${error.message}`));
298
+ }
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Create new PRD
304
+ * @param {Object} argv - Command arguments
305
+ */
306
+ async function prdNew(argv) {
307
+ const spinner = ora(`Creating PRD: ${argv.name}`).start();
308
+
309
+ try {
310
+ // Build script path
311
+ const scriptPath = path.join(process.cwd(), '.claude', 'scripts', 'pm', 'prd-new.js');
312
+
313
+ // Check if script exists
314
+ const scriptExists = await fs.pathExists(scriptPath);
315
+ if (!scriptExists) {
316
+ spinner.fail(chalk.red('PRD creation script not found'));
317
+ console.error(chalk.red('\nError: .claude/scripts/pm/prd-new.js not found'));
318
+ console.error(chalk.yellow('Run: autopm install'));
319
+ return;
320
+ }
321
+
322
+ // Build arguments
323
+ const args = [scriptPath, argv.name];
324
+ if (argv.template) {
325
+ args.push('--template', argv.template);
326
+ }
327
+
328
+ spinner.stop();
329
+
330
+ // Spawn interactive process
331
+ const child = spawn('node', args, {
332
+ stdio: 'inherit',
333
+ cwd: process.cwd()
334
+ });
335
+
336
+ // Wait for completion
337
+ await new Promise((resolve, reject) => {
338
+ child.on('close', (code) => {
339
+ if (code === 0) {
340
+ resolve();
341
+ } else {
342
+ reject(new Error(`PRD creation failed with code ${code}`));
343
+ }
344
+ });
345
+ child.on('error', reject);
346
+ });
347
+
348
+ } catch (error) {
349
+ spinner.fail(chalk.red('Failed to create PRD'));
350
+ console.error(chalk.red(`\nError: ${error.message}`));
351
+ }
352
+ }
353
+
49
354
  /**
50
355
  * Parse PRD with AI
51
356
  * @param {Object} argv - Command arguments
@@ -222,7 +527,7 @@ async function prdValidate(argv) {
222
527
  */
223
528
  async function handler(argv) {
224
529
  // Validate action
225
- const validActions = ['parse', 'extract-epics', 'summarize', 'validate'];
530
+ const validActions = ['list', 'new', 'show', 'edit', 'status', 'parse', 'extract-epics', 'summarize', 'validate'];
226
531
 
227
532
  if (!validActions.includes(argv.action)) {
228
533
  console.error(chalk.red(`\nError: Unknown action: ${argv.action}`));
@@ -233,6 +538,21 @@ async function handler(argv) {
233
538
  // Route to appropriate handler
234
539
  try {
235
540
  switch (argv.action) {
541
+ case 'list':
542
+ await prdList(argv);
543
+ break;
544
+ case 'new':
545
+ await prdNew(argv);
546
+ break;
547
+ case 'show':
548
+ await prdShow(argv);
549
+ break;
550
+ case 'edit':
551
+ await prdEdit(argv);
552
+ break;
553
+ case 'status':
554
+ await prdStatus(argv);
555
+ break;
236
556
  case 'parse':
237
557
  await prdParse(argv);
238
558
  break;
@@ -259,6 +579,69 @@ async function handler(argv) {
259
579
  */
260
580
  function builder(yargs) {
261
581
  return yargs
582
+ .command(
583
+ 'list',
584
+ 'List all PRDs',
585
+ (yargs) => {
586
+ return yargs
587
+ .example('autopm prd list', 'Show all PRDs');
588
+ }
589
+ )
590
+ .command(
591
+ 'new <name>',
592
+ 'Create new PRD interactively',
593
+ (yargs) => {
594
+ return yargs
595
+ .positional('name', {
596
+ describe: 'PRD name (use-kebab-case)',
597
+ type: 'string'
598
+ })
599
+ .option('template', {
600
+ describe: 'Template to use (api-feature, ui-feature, bug-fix, data-migration, documentation)',
601
+ type: 'string',
602
+ alias: 't'
603
+ })
604
+ .example('autopm prd new my-feature', 'Create PRD with wizard')
605
+ .example('autopm prd new payment-api --template api-feature', 'Create PRD from template');
606
+ }
607
+ )
608
+ .command(
609
+ 'show <name>',
610
+ 'Display PRD content',
611
+ (yargs) => {
612
+ return yargs
613
+ .positional('name', {
614
+ describe: 'PRD name (without .md extension)',
615
+ type: 'string'
616
+ })
617
+ .example('autopm prd show my-feature', 'Display PRD content');
618
+ }
619
+ )
620
+ .command(
621
+ 'edit <name>',
622
+ 'Edit PRD in your editor',
623
+ (yargs) => {
624
+ return yargs
625
+ .positional('name', {
626
+ describe: 'PRD name (without .md extension)',
627
+ type: 'string'
628
+ })
629
+ .example('autopm prd edit my-feature', 'Open PRD in editor')
630
+ .example('EDITOR=code autopm prd edit my-feature', 'Open PRD in VS Code');
631
+ }
632
+ )
633
+ .command(
634
+ 'status <name>',
635
+ 'Show PRD status and completeness',
636
+ (yargs) => {
637
+ return yargs
638
+ .positional('name', {
639
+ describe: 'PRD name (without .md extension)',
640
+ type: 'string'
641
+ })
642
+ .example('autopm prd status my-feature', 'Show PRD status report');
643
+ }
644
+ )
262
645
  .command(
263
646
  'parse <name>',
264
647
  'Parse PRD with AI analysis',
@@ -337,6 +720,11 @@ module.exports = {
337
720
  builder,
338
721
  handler,
339
722
  handlers: {
723
+ list: prdList,
724
+ new: prdNew,
725
+ show: prdShow,
726
+ edit: prdEdit,
727
+ status: prdStatus,
340
728
  parse: prdParse,
341
729
  extractEpics: prdExtractEpics,
342
730
  summarize: prdSummarize,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-autopm",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "Autonomous Project Management Framework for Claude Code - Advanced AI-powered development automation",
5
5
  "main": "bin/autopm.js",
6
6
  "bin": {