claude-autopm 1.31.0 → 2.1.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/autopm/.claude/mcp/test-server.md +10 -0
- package/bin/autopm-poc.js +176 -44
- package/bin/autopm.js +6 -0
- package/lib/ai-providers/AbstractAIProvider.js +524 -0
- package/lib/ai-providers/ClaudeProvider.js +359 -48
- package/lib/ai-providers/TemplateProvider.js +432 -0
- package/lib/cli/commands/agent.js +206 -0
- package/lib/cli/commands/config.js +488 -0
- package/lib/cli/commands/prd.js +345 -0
- package/lib/cli/commands/task.js +206 -0
- package/lib/config/ConfigManager.js +531 -0
- package/lib/errors/AIProviderError.js +164 -0
- package/lib/services/AgentService.js +557 -0
- package/lib/services/EpicService.js +609 -0
- package/lib/services/PRDService.js +928 -103
- package/lib/services/TaskService.js +760 -0
- package/lib/services/interfaces.js +753 -0
- package/lib/utils/CircuitBreaker.js +165 -0
- package/lib/utils/Encryption.js +201 -0
- package/lib/utils/RateLimiter.js +241 -0
- package/lib/utils/ServiceFactory.js +165 -0
- package/package.json +6 -5
- package/scripts/config/get.js +108 -0
- package/scripts/config/init.js +100 -0
- package/scripts/config/list-providers.js +93 -0
- package/scripts/config/set-api-key.js +107 -0
- package/scripts/config/set-provider.js +201 -0
- package/scripts/config/set.js +139 -0
- package/scripts/config/show.js +181 -0
- package/autopm/.claude/.env +0 -158
- package/autopm/.claude/settings.local.json +0 -9
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI PRD Commands
|
|
3
|
+
*
|
|
4
|
+
* Provides PRD (Product Requirements Document) management commands.
|
|
5
|
+
* Implements subcommands for parse, extract-epics, summarize, and validate operations.
|
|
6
|
+
*
|
|
7
|
+
* @module cli/commands/prd
|
|
8
|
+
* @requires ../../services/PRDService
|
|
9
|
+
* @requires fs-extra
|
|
10
|
+
* @requires ora
|
|
11
|
+
* @requires chalk
|
|
12
|
+
* @requires path
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const PRDService = require('../../services/PRDService');
|
|
16
|
+
const fs = require('fs-extra');
|
|
17
|
+
const ora = require('ora');
|
|
18
|
+
const chalk = require('chalk');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get PRD file path
|
|
23
|
+
* @param {string} name - PRD name
|
|
24
|
+
* @returns {string} Full path to PRD file
|
|
25
|
+
*/
|
|
26
|
+
function getPrdPath(name) {
|
|
27
|
+
return path.join(process.cwd(), '.claude', 'prds', `${name}.md`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Read PRD file
|
|
32
|
+
* @param {string} name - PRD name
|
|
33
|
+
* @returns {Promise<string>} PRD content
|
|
34
|
+
* @throws {Error} If file doesn't exist or can't be read
|
|
35
|
+
*/
|
|
36
|
+
async function readPrdFile(name) {
|
|
37
|
+
const prdPath = getPrdPath(name);
|
|
38
|
+
|
|
39
|
+
// Check if file exists
|
|
40
|
+
const exists = await fs.pathExists(prdPath);
|
|
41
|
+
if (!exists) {
|
|
42
|
+
throw new Error(`PRD file not found: ${prdPath}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Read file content
|
|
46
|
+
return await fs.readFile(prdPath, 'utf8');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Parse PRD with AI
|
|
51
|
+
* @param {Object} argv - Command arguments
|
|
52
|
+
*/
|
|
53
|
+
async function prdParse(argv) {
|
|
54
|
+
const spinner = ora(`Parsing PRD: ${argv.name}`).start();
|
|
55
|
+
const prdService = new PRDService();
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const content = await readPrdFile(argv.name);
|
|
59
|
+
|
|
60
|
+
if (argv.stream) {
|
|
61
|
+
// Streaming mode
|
|
62
|
+
spinner.text = 'Streaming PRD analysis...';
|
|
63
|
+
|
|
64
|
+
for await (const chunk of prdService.parseStream(content)) {
|
|
65
|
+
process.stdout.write(chunk);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
spinner.succeed(chalk.green('PRD parsed successfully'));
|
|
69
|
+
} else {
|
|
70
|
+
// Non-streaming mode
|
|
71
|
+
const result = await prdService.parse(content);
|
|
72
|
+
|
|
73
|
+
spinner.succeed(chalk.green('PRD parsed successfully'));
|
|
74
|
+
|
|
75
|
+
if (result.epics && result.epics.length > 0) {
|
|
76
|
+
console.log(chalk.green(`\nFound ${result.epics.length} epic(s):`));
|
|
77
|
+
result.epics.forEach(epic => {
|
|
78
|
+
console.log(` - ${epic.id}: ${epic.title}`);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch (error) {
|
|
83
|
+
spinner.fail(chalk.red('Failed to parse PRD'));
|
|
84
|
+
|
|
85
|
+
if (error.message.includes('not found')) {
|
|
86
|
+
console.error(chalk.red(`\nError: ${error.message}`));
|
|
87
|
+
} else if (error.message.includes('Failed to read')) {
|
|
88
|
+
console.error(chalk.red(`\nError: ${error.message}`));
|
|
89
|
+
} else {
|
|
90
|
+
console.error(chalk.red(`\nError: Failed to parse PRD: ${error.message}`));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Extract epics from PRD
|
|
97
|
+
* @param {Object} argv - Command arguments
|
|
98
|
+
*/
|
|
99
|
+
async function prdExtractEpics(argv) {
|
|
100
|
+
const spinner = ora(`Extracting epics from: ${argv.name}`).start();
|
|
101
|
+
const prdService = new PRDService();
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const content = await readPrdFile(argv.name);
|
|
105
|
+
|
|
106
|
+
if (argv.stream) {
|
|
107
|
+
// Streaming mode
|
|
108
|
+
spinner.text = 'Streaming epic extraction...';
|
|
109
|
+
|
|
110
|
+
for await (const chunk of prdService.extractEpicsStream(content)) {
|
|
111
|
+
process.stdout.write(chunk);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
spinner.succeed(chalk.green('Epics extracted successfully'));
|
|
115
|
+
} else {
|
|
116
|
+
// Non-streaming mode
|
|
117
|
+
const epics = await prdService.extractEpics(content);
|
|
118
|
+
|
|
119
|
+
spinner.succeed(chalk.green(`Found ${epics.length} epics`));
|
|
120
|
+
|
|
121
|
+
console.log(chalk.green(`\nExtracted ${epics.length} epic(s):`));
|
|
122
|
+
epics.forEach((epic, index) => {
|
|
123
|
+
console.log(`\n${index + 1}. ${epic.title || epic.id}`);
|
|
124
|
+
if (epic.description) {
|
|
125
|
+
console.log(` ${epic.description}`);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
spinner.fail(chalk.red('Failed to extract epics'));
|
|
131
|
+
|
|
132
|
+
if (error.message.includes('not found')) {
|
|
133
|
+
console.error(chalk.red(`\nError: ${error.message}`));
|
|
134
|
+
} else if (error.message.includes('Failed to read')) {
|
|
135
|
+
console.error(chalk.red(`\nError: ${error.message}`));
|
|
136
|
+
} else {
|
|
137
|
+
console.error(chalk.red(`\nError: Failed to extract epics: ${error.message}`));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Generate PRD summary
|
|
144
|
+
* @param {Object} argv - Command arguments
|
|
145
|
+
*/
|
|
146
|
+
async function prdSummarize(argv) {
|
|
147
|
+
const spinner = ora(`Generating summary for: ${argv.name}`).start();
|
|
148
|
+
const prdService = new PRDService();
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const content = await readPrdFile(argv.name);
|
|
152
|
+
|
|
153
|
+
if (argv.stream) {
|
|
154
|
+
// Streaming mode
|
|
155
|
+
spinner.text = 'Streaming summary generation...';
|
|
156
|
+
|
|
157
|
+
for await (const chunk of prdService.summarizeStream(content)) {
|
|
158
|
+
process.stdout.write(chunk);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
spinner.succeed(chalk.green('Summary generated successfully'));
|
|
162
|
+
} else {
|
|
163
|
+
// Non-streaming mode
|
|
164
|
+
const summary = await prdService.summarize(content);
|
|
165
|
+
|
|
166
|
+
spinner.succeed(chalk.green('Summary generated successfully'));
|
|
167
|
+
|
|
168
|
+
console.log(chalk.green('\nPRD Summary:'));
|
|
169
|
+
console.log(summary);
|
|
170
|
+
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
spinner.fail(chalk.red('Failed to generate summary'));
|
|
173
|
+
|
|
174
|
+
if (error.message.includes('not found')) {
|
|
175
|
+
console.error(chalk.red(`\nError: ${error.message}`));
|
|
176
|
+
} else if (error.message.includes('Failed to read')) {
|
|
177
|
+
console.error(chalk.red(`\nError: ${error.message}`));
|
|
178
|
+
} else {
|
|
179
|
+
console.error(chalk.red(`\nError: Failed to generate summary: ${error.message}`));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Validate PRD structure
|
|
186
|
+
* @param {Object} argv - Command arguments
|
|
187
|
+
*/
|
|
188
|
+
async function prdValidate(argv) {
|
|
189
|
+
const spinner = ora(`Validating PRD: ${argv.name}`).start();
|
|
190
|
+
const prdService = new PRDService();
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const content = await readPrdFile(argv.name);
|
|
194
|
+
const result = await prdService.validate(content);
|
|
195
|
+
|
|
196
|
+
if (result.valid) {
|
|
197
|
+
spinner.succeed(chalk.green('PRD is valid'));
|
|
198
|
+
console.log(chalk.green('\nValidation passed - PRD structure is correct'));
|
|
199
|
+
} else {
|
|
200
|
+
spinner.fail(chalk.red(`PRD validation failed - ${result.issues.length} issues found`));
|
|
201
|
+
console.error(chalk.red(`\nValidation failed - ${result.issues.length} issue(s):`));
|
|
202
|
+
result.issues.forEach((issue, index) => {
|
|
203
|
+
console.error(chalk.red(` ${index + 1}. ${issue}`));
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
spinner.fail(chalk.red('Failed to validate PRD'));
|
|
208
|
+
|
|
209
|
+
if (error.message.includes('not found')) {
|
|
210
|
+
console.error(chalk.red(`\nError: ${error.message}`));
|
|
211
|
+
} else if (error.message.includes('Failed to read')) {
|
|
212
|
+
console.error(chalk.red(`\nError: ${error.message}`));
|
|
213
|
+
} else {
|
|
214
|
+
console.error(chalk.red(`\nError: Failed to validate PRD: ${error.message}`));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Main command handler
|
|
221
|
+
* @param {Object} argv - Command arguments
|
|
222
|
+
*/
|
|
223
|
+
async function handler(argv) {
|
|
224
|
+
// Validate action
|
|
225
|
+
const validActions = ['parse', 'extract-epics', 'summarize', 'validate'];
|
|
226
|
+
|
|
227
|
+
if (!validActions.includes(argv.action)) {
|
|
228
|
+
console.error(chalk.red(`\nError: Unknown action: ${argv.action}`));
|
|
229
|
+
console.error(chalk.yellow(`Valid actions: ${validActions.join(', ')}`));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Route to appropriate handler
|
|
234
|
+
try {
|
|
235
|
+
switch (argv.action) {
|
|
236
|
+
case 'parse':
|
|
237
|
+
await prdParse(argv);
|
|
238
|
+
break;
|
|
239
|
+
case 'extract-epics':
|
|
240
|
+
await prdExtractEpics(argv);
|
|
241
|
+
break;
|
|
242
|
+
case 'summarize':
|
|
243
|
+
await prdSummarize(argv);
|
|
244
|
+
break;
|
|
245
|
+
case 'validate':
|
|
246
|
+
await prdValidate(argv);
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
// Global error handler for unexpected errors
|
|
251
|
+
console.error(chalk.red(`\nUnexpected error: ${error.message}`));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Command builder - registers all subcommands
|
|
257
|
+
* @param {Object} yargs - Yargs instance
|
|
258
|
+
* @returns {Object} Configured yargs instance
|
|
259
|
+
*/
|
|
260
|
+
function builder(yargs) {
|
|
261
|
+
return yargs
|
|
262
|
+
.command(
|
|
263
|
+
'parse <name>',
|
|
264
|
+
'Parse PRD with AI analysis',
|
|
265
|
+
(yargs) => {
|
|
266
|
+
return yargs
|
|
267
|
+
.positional('name', {
|
|
268
|
+
describe: 'PRD name (without .md extension)',
|
|
269
|
+
type: 'string'
|
|
270
|
+
})
|
|
271
|
+
.option('stream', {
|
|
272
|
+
describe: 'Use streaming mode',
|
|
273
|
+
type: 'boolean',
|
|
274
|
+
default: false
|
|
275
|
+
})
|
|
276
|
+
.option('ai', {
|
|
277
|
+
describe: 'Use AI for parsing',
|
|
278
|
+
type: 'boolean',
|
|
279
|
+
default: true
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
.command(
|
|
284
|
+
'extract-epics <name>',
|
|
285
|
+
'Extract epics from PRD',
|
|
286
|
+
(yargs) => {
|
|
287
|
+
return yargs
|
|
288
|
+
.positional('name', {
|
|
289
|
+
describe: 'PRD name (without .md extension)',
|
|
290
|
+
type: 'string'
|
|
291
|
+
})
|
|
292
|
+
.option('stream', {
|
|
293
|
+
describe: 'Use streaming mode',
|
|
294
|
+
type: 'boolean',
|
|
295
|
+
default: false
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
)
|
|
299
|
+
.command(
|
|
300
|
+
'summarize <name>',
|
|
301
|
+
'Generate PRD summary',
|
|
302
|
+
(yargs) => {
|
|
303
|
+
return yargs
|
|
304
|
+
.positional('name', {
|
|
305
|
+
describe: 'PRD name (without .md extension)',
|
|
306
|
+
type: 'string'
|
|
307
|
+
})
|
|
308
|
+
.option('stream', {
|
|
309
|
+
describe: 'Use streaming mode',
|
|
310
|
+
type: 'boolean',
|
|
311
|
+
default: false
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
)
|
|
315
|
+
.command(
|
|
316
|
+
'validate <name>',
|
|
317
|
+
'Validate PRD structure',
|
|
318
|
+
(yargs) => {
|
|
319
|
+
return yargs
|
|
320
|
+
.positional('name', {
|
|
321
|
+
describe: 'PRD name (without .md extension)',
|
|
322
|
+
type: 'string'
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
)
|
|
326
|
+
.demandCommand(1, 'You must specify a PRD action')
|
|
327
|
+
.strictCommands()
|
|
328
|
+
.help();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Command export
|
|
333
|
+
*/
|
|
334
|
+
module.exports = {
|
|
335
|
+
command: 'prd <action> [name]',
|
|
336
|
+
describe: 'Manage PRD (Product Requirements Documents)',
|
|
337
|
+
builder,
|
|
338
|
+
handler,
|
|
339
|
+
handlers: {
|
|
340
|
+
parse: prdParse,
|
|
341
|
+
extractEpics: prdExtractEpics,
|
|
342
|
+
summarize: prdSummarize,
|
|
343
|
+
validate: prdValidate
|
|
344
|
+
}
|
|
345
|
+
};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Task Commands
|
|
3
|
+
*
|
|
4
|
+
* Provides task management commands for epic-based workflows.
|
|
5
|
+
* Implements subcommands for list and prioritize operations.
|
|
6
|
+
*
|
|
7
|
+
* @module cli/commands/task
|
|
8
|
+
* @requires ../../services/TaskService
|
|
9
|
+
* @requires fs-extra
|
|
10
|
+
* @requires ora
|
|
11
|
+
* @requires chalk
|
|
12
|
+
* @requires path
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const TaskService = require('../../services/TaskService');
|
|
16
|
+
const PRDService = require('../../services/PRDService');
|
|
17
|
+
const fs = require('fs-extra');
|
|
18
|
+
const ora = require('ora');
|
|
19
|
+
const chalk = require('chalk');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get epic file path
|
|
24
|
+
* @param {string} name - Epic name
|
|
25
|
+
* @returns {string} Full path to epic file
|
|
26
|
+
*/
|
|
27
|
+
function getEpicPath(name) {
|
|
28
|
+
return path.join(process.cwd(), '.claude', 'epics', `${name}.md`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Read epic file
|
|
33
|
+
* @param {string} name - Epic name
|
|
34
|
+
* @returns {Promise<string>} Epic content
|
|
35
|
+
* @throws {Error} If file doesn't exist or can't be read
|
|
36
|
+
*/
|
|
37
|
+
async function readEpicFile(name) {
|
|
38
|
+
const epicPath = getEpicPath(name);
|
|
39
|
+
|
|
40
|
+
// Check if file exists
|
|
41
|
+
const exists = await fs.pathExists(epicPath);
|
|
42
|
+
if (!exists) {
|
|
43
|
+
throw new Error(`Epic file not found: ${epicPath}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Read file content
|
|
47
|
+
return await fs.readFile(epicPath, 'utf8');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* List all tasks in epic
|
|
52
|
+
* @param {Object} argv - Command arguments
|
|
53
|
+
*/
|
|
54
|
+
async function taskList(argv) {
|
|
55
|
+
const spinner = ora(`Loading tasks from epic: ${argv.epic}`).start();
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const content = await readEpicFile(argv.epic);
|
|
59
|
+
|
|
60
|
+
// Initialize services
|
|
61
|
+
const prdService = new PRDService();
|
|
62
|
+
const taskService = new TaskService({ prdService });
|
|
63
|
+
|
|
64
|
+
// Get tasks from epic content
|
|
65
|
+
const tasks = taskService.getTasks(content);
|
|
66
|
+
|
|
67
|
+
spinner.succeed(chalk.green(`Found ${tasks.length} tasks`));
|
|
68
|
+
|
|
69
|
+
// Display task list
|
|
70
|
+
if (tasks.length > 0) {
|
|
71
|
+
console.log(chalk.green(`\n${tasks.length} tasks in epic '${argv.epic}':`));
|
|
72
|
+
tasks.forEach((task, index) => {
|
|
73
|
+
const statusIcon = task.status === 'completed' ? '✓' :
|
|
74
|
+
task.status === 'in-progress' ? '→' :
|
|
75
|
+
task.status === 'blocked' ? '✗' : '○';
|
|
76
|
+
console.log(` ${statusIcon} ${task.id || `#${index + 1}`}: ${task.title} [${task.status}]`);
|
|
77
|
+
});
|
|
78
|
+
} else {
|
|
79
|
+
console.log(chalk.yellow('\nNo tasks found in this epic.'));
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
spinner.fail(chalk.red('Failed to list tasks'));
|
|
83
|
+
|
|
84
|
+
if (error.message.includes('not found')) {
|
|
85
|
+
console.error(chalk.red(`\nError: ${error.message}`));
|
|
86
|
+
} else {
|
|
87
|
+
console.error(chalk.red(`\nError: Failed to list tasks: ${error.message}`));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Prioritize tasks in epic
|
|
94
|
+
* @param {Object} argv - Command arguments
|
|
95
|
+
*/
|
|
96
|
+
async function taskPrioritize(argv) {
|
|
97
|
+
const spinner = ora(`Prioritizing tasks in epic: ${argv.epic}`).start();
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const content = await readEpicFile(argv.epic);
|
|
101
|
+
|
|
102
|
+
// Initialize services
|
|
103
|
+
const prdService = new PRDService();
|
|
104
|
+
const taskService = new TaskService({ prdService });
|
|
105
|
+
|
|
106
|
+
// Prioritize tasks
|
|
107
|
+
const prioritizedTasks = await taskService.prioritize(content);
|
|
108
|
+
|
|
109
|
+
spinner.succeed(chalk.green('Tasks prioritized successfully'));
|
|
110
|
+
|
|
111
|
+
// Display prioritized tasks
|
|
112
|
+
if (prioritizedTasks.length > 0) {
|
|
113
|
+
console.log(chalk.green(`\nPrioritized ${prioritizedTasks.length} tasks:`));
|
|
114
|
+
prioritizedTasks.forEach((task, index) => {
|
|
115
|
+
const priorityLabel = task.priority || 'P2';
|
|
116
|
+
console.log(` ${index + 1}. [${priorityLabel}] ${task.id}: ${task.title || 'Untitled'}`);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
spinner.fail(chalk.red('Failed to prioritize tasks'));
|
|
121
|
+
|
|
122
|
+
if (error.message.includes('not found')) {
|
|
123
|
+
console.error(chalk.red(`\nError: ${error.message}`));
|
|
124
|
+
} else {
|
|
125
|
+
console.error(chalk.red(`\nError: Failed to prioritize tasks: ${error.message}`));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Main command handler
|
|
132
|
+
* @param {Object} argv - Command arguments
|
|
133
|
+
*/
|
|
134
|
+
async function handler(argv) {
|
|
135
|
+
// Validate action
|
|
136
|
+
const validActions = ['list', 'prioritize'];
|
|
137
|
+
|
|
138
|
+
if (!validActions.includes(argv.action)) {
|
|
139
|
+
console.error(chalk.red(`\nError: Unknown action: ${argv.action}`));
|
|
140
|
+
console.error(chalk.yellow(`Valid actions: ${validActions.join(', ')}`));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Route to appropriate handler
|
|
145
|
+
try {
|
|
146
|
+
switch (argv.action) {
|
|
147
|
+
case 'list':
|
|
148
|
+
await taskList(argv);
|
|
149
|
+
break;
|
|
150
|
+
case 'prioritize':
|
|
151
|
+
await taskPrioritize(argv);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
// Global error handler for unexpected errors
|
|
156
|
+
console.error(chalk.red(`\nUnexpected error: ${error.message}`));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Command builder - registers all subcommands
|
|
162
|
+
* @param {Object} yargs - Yargs instance
|
|
163
|
+
* @returns {Object} Configured yargs instance
|
|
164
|
+
*/
|
|
165
|
+
function builder(yargs) {
|
|
166
|
+
return yargs
|
|
167
|
+
.command(
|
|
168
|
+
'list <epic>',
|
|
169
|
+
'List all tasks in epic',
|
|
170
|
+
(yargs) => {
|
|
171
|
+
return yargs
|
|
172
|
+
.positional('epic', {
|
|
173
|
+
describe: 'Epic name (without .md extension)',
|
|
174
|
+
type: 'string'
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
.command(
|
|
179
|
+
'prioritize <epic>',
|
|
180
|
+
'Prioritize tasks in epic',
|
|
181
|
+
(yargs) => {
|
|
182
|
+
return yargs
|
|
183
|
+
.positional('epic', {
|
|
184
|
+
describe: 'Epic name (without .md extension)',
|
|
185
|
+
type: 'string'
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
.demandCommand(1, 'You must specify a task action')
|
|
190
|
+
.strictCommands()
|
|
191
|
+
.help();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Command export
|
|
196
|
+
*/
|
|
197
|
+
module.exports = {
|
|
198
|
+
command: 'task <action>',
|
|
199
|
+
describe: 'Manage tasks in epics',
|
|
200
|
+
builder,
|
|
201
|
+
handler,
|
|
202
|
+
handlers: {
|
|
203
|
+
list: taskList,
|
|
204
|
+
prioritize: taskPrioritize
|
|
205
|
+
}
|
|
206
|
+
};
|