claude-autopm 2.1.1 ā 2.2.1
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/lib/cli/commands/prd.js +498 -19
- package/package.json +1 -1
package/lib/cli/commands/prd.js
CHANGED
|
@@ -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,16 +47,366 @@ 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
|
+
// Debug: Show what we're looking for
|
|
314
|
+
if (process.env.DEBUG) {
|
|
315
|
+
console.log(chalk.gray(`\nDebug: Looking for script at: ${scriptPath}`));
|
|
316
|
+
console.log(chalk.gray(`Debug: Current directory: ${process.cwd()}`));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Check if script exists
|
|
320
|
+
const scriptExists = await fs.pathExists(scriptPath);
|
|
321
|
+
if (!scriptExists) {
|
|
322
|
+
spinner.fail(chalk.red('PRD creation script not found'));
|
|
323
|
+
console.error(chalk.red('\nError: .claude/scripts/pm/prd-new.js not found'));
|
|
324
|
+
console.error(chalk.red(`Expected location: ${scriptPath}`));
|
|
325
|
+
console.error(chalk.yellow('\nSolution: Run "autopm install" to install the framework'));
|
|
326
|
+
|
|
327
|
+
// Show what directories DO exist
|
|
328
|
+
const claudeDir = path.join(process.cwd(), '.claude');
|
|
329
|
+
const scriptsDir = path.join(claudeDir, 'scripts');
|
|
330
|
+
const pmDir = path.join(scriptsDir, 'pm');
|
|
331
|
+
|
|
332
|
+
console.error(chalk.gray('\nDirectory status:'));
|
|
333
|
+
console.error(chalk.gray(` .claude/ exists: ${await fs.pathExists(claudeDir)}`));
|
|
334
|
+
console.error(chalk.gray(` .claude/scripts/ exists: ${await fs.pathExists(scriptsDir)}`));
|
|
335
|
+
console.error(chalk.gray(` .claude/scripts/pm/ exists: ${await fs.pathExists(pmDir)}`));
|
|
336
|
+
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Build arguments
|
|
341
|
+
const args = [scriptPath, argv.name];
|
|
342
|
+
if (argv.template) {
|
|
343
|
+
args.push('--template', argv.template);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (process.env.DEBUG) {
|
|
347
|
+
console.log(chalk.gray(`Debug: Running: node ${args.join(' ')}`));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
spinner.stop();
|
|
351
|
+
|
|
352
|
+
console.log(chalk.cyan(`\nš Starting PRD wizard for: ${argv.name}`));
|
|
353
|
+
if (argv.template) {
|
|
354
|
+
console.log(chalk.cyan(`š Using template: ${argv.template}`));
|
|
355
|
+
}
|
|
356
|
+
console.log(chalk.gray('Press Ctrl+C to cancel\n'));
|
|
357
|
+
|
|
358
|
+
// Spawn interactive process
|
|
359
|
+
const child = spawn('node', args, {
|
|
360
|
+
stdio: 'inherit',
|
|
361
|
+
cwd: process.cwd(),
|
|
362
|
+
env: { ...process.env }
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Wait for completion
|
|
366
|
+
await new Promise((resolve, reject) => {
|
|
367
|
+
child.on('close', (code) => {
|
|
368
|
+
if (code === 0) {
|
|
369
|
+
console.log(chalk.green('\nā PRD created successfully'));
|
|
370
|
+
resolve();
|
|
371
|
+
} else if (code === null) {
|
|
372
|
+
console.error(chalk.yellow('\nā Process was terminated'));
|
|
373
|
+
reject(new Error('Process terminated'));
|
|
374
|
+
} else {
|
|
375
|
+
console.error(chalk.red(`\nā PRD creation failed with exit code ${code}`));
|
|
376
|
+
reject(new Error(`PRD creation failed with code ${code}`));
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
child.on('error', (err) => {
|
|
381
|
+
console.error(chalk.red(`\nā Failed to start process: ${err.message}`));
|
|
382
|
+
reject(err);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
} catch (error) {
|
|
387
|
+
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
388
|
+
|
|
389
|
+
if (error.message.includes('terminated')) {
|
|
390
|
+
console.error(chalk.yellow('Operation cancelled by user'));
|
|
391
|
+
} else if (error.code === 'ENOENT') {
|
|
392
|
+
console.error(chalk.red('Node.js executable not found'));
|
|
393
|
+
console.error(chalk.yellow('Ensure Node.js is installed and in PATH'));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
49
400
|
/**
|
|
50
401
|
* Parse PRD with AI
|
|
51
402
|
* @param {Object} argv - Command arguments
|
|
52
403
|
*/
|
|
53
404
|
async function prdParse(argv) {
|
|
54
405
|
const spinner = ora(`Parsing PRD: ${argv.name}`).start();
|
|
55
|
-
const prdService = new PRDService();
|
|
56
406
|
|
|
57
407
|
try {
|
|
58
408
|
const content = await readPrdFile(argv.name);
|
|
409
|
+
const prdService = new PRDService();
|
|
59
410
|
|
|
60
411
|
if (argv.stream) {
|
|
61
412
|
// Streaming mode
|
|
@@ -83,12 +434,24 @@ async function prdParse(argv) {
|
|
|
83
434
|
spinner.fail(chalk.red('Failed to parse PRD'));
|
|
84
435
|
|
|
85
436
|
if (error.message.includes('not found')) {
|
|
86
|
-
console.error(chalk.red(`\
|
|
437
|
+
console.error(chalk.red(`\nā Error: PRD file not found`));
|
|
438
|
+
console.error(chalk.red(` File: ${getPrdPath(argv.name)}`));
|
|
439
|
+
console.error(chalk.yellow('\nš” Use: autopm prd list to see available PRDs'));
|
|
440
|
+
} else if (error.message.includes('ANTHROPIC_API_KEY') || error.message.includes('API key')) {
|
|
441
|
+
console.error(chalk.red(`\nā Error: API key not configured`));
|
|
442
|
+
console.error(chalk.yellow('\nš” Set your API key in .env file:'));
|
|
443
|
+
console.error(chalk.cyan(' ANTHROPIC_API_KEY=sk-ant-api03-...'));
|
|
87
444
|
} else if (error.message.includes('Failed to read')) {
|
|
88
|
-
console.error(chalk.red(`\
|
|
445
|
+
console.error(chalk.red(`\nā Error: Cannot read PRD file`));
|
|
446
|
+
console.error(chalk.red(` ${error.message}`));
|
|
89
447
|
} else {
|
|
90
|
-
console.error(chalk.red(`\
|
|
448
|
+
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
449
|
+
if (process.env.DEBUG) {
|
|
450
|
+
console.error(chalk.gray('\nStack trace:'));
|
|
451
|
+
console.error(chalk.gray(error.stack));
|
|
452
|
+
}
|
|
91
453
|
}
|
|
454
|
+
process.exit(1);
|
|
92
455
|
}
|
|
93
456
|
}
|
|
94
457
|
|
|
@@ -98,10 +461,10 @@ async function prdParse(argv) {
|
|
|
98
461
|
*/
|
|
99
462
|
async function prdExtractEpics(argv) {
|
|
100
463
|
const spinner = ora(`Extracting epics from: ${argv.name}`).start();
|
|
101
|
-
const prdService = new PRDService();
|
|
102
464
|
|
|
103
465
|
try {
|
|
104
466
|
const content = await readPrdFile(argv.name);
|
|
467
|
+
const prdService = new PRDService();
|
|
105
468
|
|
|
106
469
|
if (argv.stream) {
|
|
107
470
|
// Streaming mode
|
|
@@ -130,12 +493,24 @@ async function prdExtractEpics(argv) {
|
|
|
130
493
|
spinner.fail(chalk.red('Failed to extract epics'));
|
|
131
494
|
|
|
132
495
|
if (error.message.includes('not found')) {
|
|
133
|
-
console.error(chalk.red(`\
|
|
496
|
+
console.error(chalk.red(`\nā Error: PRD file not found`));
|
|
497
|
+
console.error(chalk.red(` File: ${getPrdPath(argv.name)}`));
|
|
498
|
+
console.error(chalk.yellow('\nš” Use: autopm prd list to see available PRDs'));
|
|
499
|
+
} else if (error.message.includes('ANTHROPIC_API_KEY') || error.message.includes('API key')) {
|
|
500
|
+
console.error(chalk.red(`\nā Error: API key not configured`));
|
|
501
|
+
console.error(chalk.yellow('\nš” Set your API key in .env file:'));
|
|
502
|
+
console.error(chalk.cyan(' ANTHROPIC_API_KEY=sk-ant-api03-...'));
|
|
134
503
|
} else if (error.message.includes('Failed to read')) {
|
|
135
|
-
console.error(chalk.red(`\
|
|
504
|
+
console.error(chalk.red(`\nā Error: Cannot read PRD file`));
|
|
505
|
+
console.error(chalk.red(` ${error.message}`));
|
|
136
506
|
} else {
|
|
137
|
-
console.error(chalk.red(`\
|
|
507
|
+
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
508
|
+
if (process.env.DEBUG) {
|
|
509
|
+
console.error(chalk.gray('\nStack trace:'));
|
|
510
|
+
console.error(chalk.gray(error.stack));
|
|
511
|
+
}
|
|
138
512
|
}
|
|
513
|
+
process.exit(1);
|
|
139
514
|
}
|
|
140
515
|
}
|
|
141
516
|
|
|
@@ -145,10 +520,10 @@ async function prdExtractEpics(argv) {
|
|
|
145
520
|
*/
|
|
146
521
|
async function prdSummarize(argv) {
|
|
147
522
|
const spinner = ora(`Generating summary for: ${argv.name}`).start();
|
|
148
|
-
const prdService = new PRDService();
|
|
149
523
|
|
|
150
524
|
try {
|
|
151
525
|
const content = await readPrdFile(argv.name);
|
|
526
|
+
const prdService = new PRDService();
|
|
152
527
|
|
|
153
528
|
if (argv.stream) {
|
|
154
529
|
// Streaming mode
|
|
@@ -172,12 +547,24 @@ async function prdSummarize(argv) {
|
|
|
172
547
|
spinner.fail(chalk.red('Failed to generate summary'));
|
|
173
548
|
|
|
174
549
|
if (error.message.includes('not found')) {
|
|
175
|
-
console.error(chalk.red(`\
|
|
550
|
+
console.error(chalk.red(`\nā Error: PRD file not found`));
|
|
551
|
+
console.error(chalk.red(` File: ${getPrdPath(argv.name)}`));
|
|
552
|
+
console.error(chalk.yellow('\nš” Use: autopm prd list to see available PRDs'));
|
|
553
|
+
} else if (error.message.includes('ANTHROPIC_API_KEY') || error.message.includes('API key')) {
|
|
554
|
+
console.error(chalk.red(`\nā Error: API key not configured`));
|
|
555
|
+
console.error(chalk.yellow('\nš” Set your API key in .env file:'));
|
|
556
|
+
console.error(chalk.cyan(' ANTHROPIC_API_KEY=sk-ant-api03-...'));
|
|
176
557
|
} else if (error.message.includes('Failed to read')) {
|
|
177
|
-
console.error(chalk.red(`\
|
|
558
|
+
console.error(chalk.red(`\nā Error: Cannot read PRD file`));
|
|
559
|
+
console.error(chalk.red(` ${error.message}`));
|
|
178
560
|
} else {
|
|
179
|
-
console.error(chalk.red(`\
|
|
561
|
+
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
562
|
+
if (process.env.DEBUG) {
|
|
563
|
+
console.error(chalk.gray('\nStack trace:'));
|
|
564
|
+
console.error(chalk.gray(error.stack));
|
|
565
|
+
}
|
|
180
566
|
}
|
|
567
|
+
process.exit(1);
|
|
181
568
|
}
|
|
182
569
|
}
|
|
183
570
|
|
|
@@ -187,32 +574,41 @@ async function prdSummarize(argv) {
|
|
|
187
574
|
*/
|
|
188
575
|
async function prdValidate(argv) {
|
|
189
576
|
const spinner = ora(`Validating PRD: ${argv.name}`).start();
|
|
190
|
-
const prdService = new PRDService();
|
|
191
577
|
|
|
192
578
|
try {
|
|
193
579
|
const content = await readPrdFile(argv.name);
|
|
580
|
+
const prdService = new PRDService();
|
|
194
581
|
const result = await prdService.validate(content);
|
|
195
582
|
|
|
196
583
|
if (result.valid) {
|
|
197
584
|
spinner.succeed(chalk.green('PRD is valid'));
|
|
198
|
-
console.log(chalk.green('\
|
|
585
|
+
console.log(chalk.green('\nā Validation passed - PRD structure is correct'));
|
|
199
586
|
} else {
|
|
200
587
|
spinner.fail(chalk.red(`PRD validation failed - ${result.issues.length} issues found`));
|
|
201
|
-
console.error(chalk.red(`\
|
|
588
|
+
console.error(chalk.red(`\nā Validation failed - ${result.issues.length} issue(s):`));
|
|
202
589
|
result.issues.forEach((issue, index) => {
|
|
203
590
|
console.error(chalk.red(` ${index + 1}. ${issue}`));
|
|
204
591
|
});
|
|
592
|
+
process.exit(1);
|
|
205
593
|
}
|
|
206
594
|
} catch (error) {
|
|
207
595
|
spinner.fail(chalk.red('Failed to validate PRD'));
|
|
208
596
|
|
|
209
597
|
if (error.message.includes('not found')) {
|
|
210
|
-
console.error(chalk.red(`\
|
|
598
|
+
console.error(chalk.red(`\nā Error: PRD file not found`));
|
|
599
|
+
console.error(chalk.red(` File: ${getPrdPath(argv.name)}`));
|
|
600
|
+
console.error(chalk.yellow('\nš” Use: autopm prd list to see available PRDs'));
|
|
211
601
|
} else if (error.message.includes('Failed to read')) {
|
|
212
|
-
console.error(chalk.red(`\
|
|
602
|
+
console.error(chalk.red(`\nā Error: Cannot read PRD file`));
|
|
603
|
+
console.error(chalk.red(` ${error.message}`));
|
|
213
604
|
} else {
|
|
214
|
-
console.error(chalk.red(`\
|
|
605
|
+
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
606
|
+
if (process.env.DEBUG) {
|
|
607
|
+
console.error(chalk.gray('\nStack trace:'));
|
|
608
|
+
console.error(chalk.gray(error.stack));
|
|
609
|
+
}
|
|
215
610
|
}
|
|
611
|
+
process.exit(1);
|
|
216
612
|
}
|
|
217
613
|
}
|
|
218
614
|
|
|
@@ -222,7 +618,7 @@ async function prdValidate(argv) {
|
|
|
222
618
|
*/
|
|
223
619
|
async function handler(argv) {
|
|
224
620
|
// Validate action
|
|
225
|
-
const validActions = ['parse', 'extract-epics', 'summarize', 'validate'];
|
|
621
|
+
const validActions = ['list', 'new', 'show', 'edit', 'status', 'parse', 'extract-epics', 'summarize', 'validate'];
|
|
226
622
|
|
|
227
623
|
if (!validActions.includes(argv.action)) {
|
|
228
624
|
console.error(chalk.red(`\nError: Unknown action: ${argv.action}`));
|
|
@@ -233,6 +629,21 @@ async function handler(argv) {
|
|
|
233
629
|
// Route to appropriate handler
|
|
234
630
|
try {
|
|
235
631
|
switch (argv.action) {
|
|
632
|
+
case 'list':
|
|
633
|
+
await prdList(argv);
|
|
634
|
+
break;
|
|
635
|
+
case 'new':
|
|
636
|
+
await prdNew(argv);
|
|
637
|
+
break;
|
|
638
|
+
case 'show':
|
|
639
|
+
await prdShow(argv);
|
|
640
|
+
break;
|
|
641
|
+
case 'edit':
|
|
642
|
+
await prdEdit(argv);
|
|
643
|
+
break;
|
|
644
|
+
case 'status':
|
|
645
|
+
await prdStatus(argv);
|
|
646
|
+
break;
|
|
236
647
|
case 'parse':
|
|
237
648
|
await prdParse(argv);
|
|
238
649
|
break;
|
|
@@ -259,6 +670,69 @@ async function handler(argv) {
|
|
|
259
670
|
*/
|
|
260
671
|
function builder(yargs) {
|
|
261
672
|
return yargs
|
|
673
|
+
.command(
|
|
674
|
+
'list',
|
|
675
|
+
'List all PRDs',
|
|
676
|
+
(yargs) => {
|
|
677
|
+
return yargs
|
|
678
|
+
.example('autopm prd list', 'Show all PRDs');
|
|
679
|
+
}
|
|
680
|
+
)
|
|
681
|
+
.command(
|
|
682
|
+
'new <name>',
|
|
683
|
+
'Create new PRD interactively',
|
|
684
|
+
(yargs) => {
|
|
685
|
+
return yargs
|
|
686
|
+
.positional('name', {
|
|
687
|
+
describe: 'PRD name (use-kebab-case)',
|
|
688
|
+
type: 'string'
|
|
689
|
+
})
|
|
690
|
+
.option('template', {
|
|
691
|
+
describe: 'Template to use (api-feature, ui-feature, bug-fix, data-migration, documentation)',
|
|
692
|
+
type: 'string',
|
|
693
|
+
alias: 't'
|
|
694
|
+
})
|
|
695
|
+
.example('autopm prd new my-feature', 'Create PRD with wizard')
|
|
696
|
+
.example('autopm prd new payment-api --template api-feature', 'Create PRD from template');
|
|
697
|
+
}
|
|
698
|
+
)
|
|
699
|
+
.command(
|
|
700
|
+
'show <name>',
|
|
701
|
+
'Display PRD content',
|
|
702
|
+
(yargs) => {
|
|
703
|
+
return yargs
|
|
704
|
+
.positional('name', {
|
|
705
|
+
describe: 'PRD name (without .md extension)',
|
|
706
|
+
type: 'string'
|
|
707
|
+
})
|
|
708
|
+
.example('autopm prd show my-feature', 'Display PRD content');
|
|
709
|
+
}
|
|
710
|
+
)
|
|
711
|
+
.command(
|
|
712
|
+
'edit <name>',
|
|
713
|
+
'Edit PRD in your editor',
|
|
714
|
+
(yargs) => {
|
|
715
|
+
return yargs
|
|
716
|
+
.positional('name', {
|
|
717
|
+
describe: 'PRD name (without .md extension)',
|
|
718
|
+
type: 'string'
|
|
719
|
+
})
|
|
720
|
+
.example('autopm prd edit my-feature', 'Open PRD in editor')
|
|
721
|
+
.example('EDITOR=code autopm prd edit my-feature', 'Open PRD in VS Code');
|
|
722
|
+
}
|
|
723
|
+
)
|
|
724
|
+
.command(
|
|
725
|
+
'status <name>',
|
|
726
|
+
'Show PRD status and completeness',
|
|
727
|
+
(yargs) => {
|
|
728
|
+
return yargs
|
|
729
|
+
.positional('name', {
|
|
730
|
+
describe: 'PRD name (without .md extension)',
|
|
731
|
+
type: 'string'
|
|
732
|
+
})
|
|
733
|
+
.example('autopm prd status my-feature', 'Show PRD status report');
|
|
734
|
+
}
|
|
735
|
+
)
|
|
262
736
|
.command(
|
|
263
737
|
'parse <name>',
|
|
264
738
|
'Parse PRD with AI analysis',
|
|
@@ -337,6 +811,11 @@ module.exports = {
|
|
|
337
811
|
builder,
|
|
338
812
|
handler,
|
|
339
813
|
handlers: {
|
|
814
|
+
list: prdList,
|
|
815
|
+
new: prdNew,
|
|
816
|
+
show: prdShow,
|
|
817
|
+
edit: prdEdit,
|
|
818
|
+
status: prdStatus,
|
|
340
819
|
parse: prdParse,
|
|
341
820
|
extractEpics: prdExtractEpics,
|
|
342
821
|
summarize: prdSummarize,
|