@lovelybunch/cli 1.0.75-alpha.9 → 1.0.76-alpha.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/dist/cli.js +5 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/events.js +3 -3
- package/dist/commands/events.js.map +1 -1
- package/dist/commands/implement.d.ts.map +1 -1
- package/dist/commands/implement.js +31 -24
- package/dist/commands/implement.js.map +1 -1
- package/dist/commands/init.js +2 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +24 -23
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/mail.d.ts +3 -0
- package/dist/commands/mail.d.ts.map +1 -0
- package/dist/commands/mail.js +274 -0
- package/dist/commands/mail.js.map +1 -0
- package/dist/commands/notify.d.ts +3 -0
- package/dist/commands/notify.d.ts.map +1 -0
- package/dist/commands/notify.js +36 -0
- package/dist/commands/notify.js.map +1 -0
- package/dist/commands/propose.js +18 -18
- package/dist/commands/propose.js.map +1 -1
- package/dist/commands/schema.js +2 -2
- package/dist/commands/schema.js.map +1 -1
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +47 -74
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/task.d.ts.map +1 -1
- package/dist/commands/task.js +207 -80
- package/dist/commands/task.js.map +1 -1
- package/package.json +4 -4
package/dist/commands/task.js
CHANGED
|
@@ -5,11 +5,11 @@ import inquirer from 'inquirer';
|
|
|
5
5
|
import { promises as fs } from 'fs';
|
|
6
6
|
import { table } from 'table';
|
|
7
7
|
import os from 'os';
|
|
8
|
-
import {
|
|
8
|
+
import { listTasks, getTask, createTask, updateTask, deleteTask, addPlanStep, updatePlanStep, addComment, } from '@lovelybunch/core';
|
|
9
9
|
// ============================================================================
|
|
10
10
|
// Constants & Helpers
|
|
11
11
|
// ============================================================================
|
|
12
|
-
const VALID_STATUSES = ['draft', 'backlog', 'ready', 'queued', 'active', 'blocked', 'review', 'done', 'canceled', 'duplicate'];
|
|
12
|
+
const VALID_STATUSES = ['draft', 'backlog', 'ready', 'queued', 'active', 'blocked', 'review', 'revision', 'done', 'canceled', 'duplicate'];
|
|
13
13
|
const VALID_PRIORITIES = ['low', 'medium', 'high', 'critical'];
|
|
14
14
|
function parseTags(tagsString) {
|
|
15
15
|
if (!tagsString)
|
|
@@ -38,6 +38,15 @@ function validateStatus(status) {
|
|
|
38
38
|
}
|
|
39
39
|
return normalized;
|
|
40
40
|
}
|
|
41
|
+
function validateReadiness(value) {
|
|
42
|
+
if (value == null)
|
|
43
|
+
return undefined;
|
|
44
|
+
const n = parseInt(value, 10);
|
|
45
|
+
if (Number.isNaN(n) || n < 0 || n > 100) {
|
|
46
|
+
throw new Error(`Invalid readiness "${value}". Must be an integer between 0 and 100.`);
|
|
47
|
+
}
|
|
48
|
+
return n;
|
|
49
|
+
}
|
|
41
50
|
function interpretEscapeSequences(str) {
|
|
42
51
|
return str
|
|
43
52
|
.replace(/\\n/g, '\n')
|
|
@@ -54,6 +63,7 @@ function getStatusColor(status) {
|
|
|
54
63
|
active: chalk.yellow,
|
|
55
64
|
blocked: chalk.red,
|
|
56
65
|
review: chalk.cyan,
|
|
66
|
+
revision: chalk.magenta,
|
|
57
67
|
done: chalk.green,
|
|
58
68
|
canceled: chalk.gray,
|
|
59
69
|
duplicate: chalk.gray,
|
|
@@ -72,9 +82,10 @@ function getPriorityColor(priority) {
|
|
|
72
82
|
function getStepStatusIcon(status) {
|
|
73
83
|
const icons = {
|
|
74
84
|
pending: chalk.gray('○'),
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
active: chalk.yellow('◐'),
|
|
86
|
+
done: chalk.green('●'),
|
|
77
87
|
failed: chalk.red('✗'),
|
|
88
|
+
skipped: chalk.dim('⊘'),
|
|
78
89
|
};
|
|
79
90
|
return icons[status] || chalk.gray('○');
|
|
80
91
|
}
|
|
@@ -82,8 +93,7 @@ function getStepStatusIcon(status) {
|
|
|
82
93
|
// Parent Command
|
|
83
94
|
// ============================================================================
|
|
84
95
|
export const taskCommand = new Command('task')
|
|
85
|
-
.description('Manage tasks
|
|
86
|
-
.alias('proposal')
|
|
96
|
+
.description('Manage tasks')
|
|
87
97
|
.addHelpText('after', `
|
|
88
98
|
Examples:
|
|
89
99
|
${chalk.gray('# List all tasks')}
|
|
@@ -119,7 +129,7 @@ Examples:
|
|
|
119
129
|
taskCommand
|
|
120
130
|
.command('list')
|
|
121
131
|
.description('List all tasks')
|
|
122
|
-
.option('-s, --status <status>', 'Filter by status (draft|backlog|ready|queued|active|blocked|review|done|canceled|duplicate)')
|
|
132
|
+
.option('-s, --status <status>', 'Filter by status (draft|backlog|ready|queued|active|blocked|review|revision|done|canceled|duplicate)')
|
|
123
133
|
.option('-a, --author <author>', 'Filter by author name or email')
|
|
124
134
|
.option('-p, --priority <priority>', 'Filter by priority (low|medium|high|critical)')
|
|
125
135
|
.option('-q, --search <query>', 'Search tasks by content (fuzzy search)')
|
|
@@ -145,9 +155,10 @@ taskCommand
|
|
|
145
155
|
if (options.search) {
|
|
146
156
|
filters.search = options.search;
|
|
147
157
|
}
|
|
148
|
-
const proposals = await listProposals(filters);
|
|
149
158
|
const limit = parseInt(options.limit, 10) || 20;
|
|
150
|
-
|
|
159
|
+
filters.limit = limit;
|
|
160
|
+
const tasks = await listTasks(filters);
|
|
161
|
+
const limited = tasks.slice(0, limit);
|
|
151
162
|
spinner.stop();
|
|
152
163
|
if (options.json) {
|
|
153
164
|
console.log(JSON.stringify(limited, null, 2));
|
|
@@ -167,26 +178,32 @@ taskCommand
|
|
|
167
178
|
const tableData = [
|
|
168
179
|
[
|
|
169
180
|
chalk.bold('ID'),
|
|
170
|
-
chalk.bold('
|
|
181
|
+
chalk.bold('Title'),
|
|
171
182
|
chalk.bold('Author'),
|
|
172
183
|
chalk.bold('Status'),
|
|
173
184
|
chalk.bold('Priority'),
|
|
185
|
+
chalk.bold('Steps'),
|
|
174
186
|
chalk.bold('Created'),
|
|
175
187
|
],
|
|
176
188
|
];
|
|
177
|
-
for (const
|
|
178
|
-
const titleText =
|
|
189
|
+
for (const task of limited) {
|
|
190
|
+
const titleText = task.title || task.intent || '';
|
|
179
191
|
const displayTitle = titleText.length > 35
|
|
180
192
|
? titleText.substring(0, 32) + '...'
|
|
181
193
|
: titleText;
|
|
182
|
-
const priority =
|
|
194
|
+
const priority = task.metadata?.priority || 'medium';
|
|
195
|
+
const steps = task.planSteps || [];
|
|
196
|
+
const stepsDisplay = steps.length > 0
|
|
197
|
+
? chalk.dim(`${steps.filter(s => s.status === 'done').length}/${steps.length}`)
|
|
198
|
+
: chalk.dim('-');
|
|
183
199
|
tableData.push([
|
|
184
|
-
chalk.cyan(
|
|
200
|
+
chalk.cyan(task.id),
|
|
185
201
|
displayTitle,
|
|
186
|
-
|
|
187
|
-
getStatusColor(
|
|
202
|
+
task.author.name,
|
|
203
|
+
getStatusColor(task.status)(task.status),
|
|
188
204
|
getPriorityColor(priority)(priority),
|
|
189
|
-
|
|
205
|
+
stepsDisplay,
|
|
206
|
+
chalk.gray(new Date(task.metadata?.createdAt || '').toLocaleDateString()),
|
|
190
207
|
]);
|
|
191
208
|
}
|
|
192
209
|
// Display table
|
|
@@ -211,7 +228,7 @@ taskCommand
|
|
|
211
228
|
};
|
|
212
229
|
console.log('\n' + chalk.bold.underline('Tasks'));
|
|
213
230
|
console.log(table(tableData, tableConfig));
|
|
214
|
-
console.log(chalk.gray(`Showing ${limited.length} of ${
|
|
231
|
+
console.log(chalk.gray(`Showing ${limited.length} of ${tasks.length} tasks`));
|
|
215
232
|
}
|
|
216
233
|
catch (error) {
|
|
217
234
|
spinner.fail('Failed to list tasks');
|
|
@@ -231,19 +248,19 @@ taskCommand
|
|
|
231
248
|
.action(async (id, options) => {
|
|
232
249
|
const spinner = ora('Loading task...').start();
|
|
233
250
|
try {
|
|
234
|
-
const
|
|
251
|
+
const task = await getTask(id);
|
|
235
252
|
spinner.stop();
|
|
236
|
-
if (!
|
|
253
|
+
if (!task) {
|
|
237
254
|
console.error(chalk.red(`Task '${id}' not found.`));
|
|
238
255
|
console.log(chalk.gray('Use "nut task list" to see all tasks.'));
|
|
239
256
|
process.exit(1);
|
|
240
257
|
}
|
|
241
258
|
if (options.json) {
|
|
242
|
-
console.log(JSON.stringify(
|
|
259
|
+
console.log(JSON.stringify(task, null, 2));
|
|
243
260
|
return;
|
|
244
261
|
}
|
|
245
|
-
if (options.raw &&
|
|
246
|
-
console.log(
|
|
262
|
+
if (options.raw && task.content) {
|
|
263
|
+
console.log(task.content);
|
|
247
264
|
return;
|
|
248
265
|
}
|
|
249
266
|
// Display formatted output
|
|
@@ -251,55 +268,75 @@ taskCommand
|
|
|
251
268
|
console.log();
|
|
252
269
|
// Basic Information
|
|
253
270
|
console.log(chalk.cyan('Basic Information:'));
|
|
254
|
-
console.log(' ID: ' + chalk.white(
|
|
255
|
-
console.log(' Title: ' + chalk.white(
|
|
256
|
-
console.log(' Status: ' + getStatusColor(
|
|
257
|
-
if (
|
|
258
|
-
console.log(' Priority: ' + getPriorityColor(
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
271
|
+
console.log(' ID: ' + chalk.white(task.id));
|
|
272
|
+
console.log(' Title: ' + chalk.white(task.title || task.intent));
|
|
273
|
+
console.log(' Status: ' + getStatusColor(task.status)(task.status.toUpperCase()));
|
|
274
|
+
if (task.metadata?.priority) {
|
|
275
|
+
console.log(' Priority: ' + getPriorityColor(task.metadata.priority)(task.metadata.priority.toUpperCase()));
|
|
276
|
+
}
|
|
277
|
+
if (task.metadata?.readiness != null) {
|
|
278
|
+
console.log(' Readiness: ' + chalk.white(`${task.metadata.readiness}%`));
|
|
279
|
+
}
|
|
280
|
+
console.log(' Created: ' + chalk.white(new Date(task.metadata?.createdAt || '').toLocaleString()));
|
|
281
|
+
console.log(' Updated: ' + chalk.white(new Date(task.metadata?.updatedAt || '').toLocaleString()));
|
|
262
282
|
// Author Information
|
|
263
283
|
console.log('\n' + chalk.cyan('Author:'));
|
|
264
|
-
console.log(' Name: ' + chalk.white(
|
|
265
|
-
console.log(' Type: ' + chalk.white(
|
|
266
|
-
if (
|
|
267
|
-
console.log(' Email: ' + chalk.white(
|
|
284
|
+
console.log(' Name: ' + chalk.white(task.author.name));
|
|
285
|
+
console.log(' Type: ' + chalk.white(task.author.type));
|
|
286
|
+
if (task.author.email) {
|
|
287
|
+
console.log(' Email: ' + chalk.white(task.author.email));
|
|
268
288
|
}
|
|
269
289
|
// Product Spec Reference
|
|
270
|
-
if (
|
|
290
|
+
if (task.productSpecRef) {
|
|
271
291
|
console.log('\n' + chalk.cyan('Product Specification:'));
|
|
272
|
-
console.log(' Reference: ' + chalk.white(
|
|
292
|
+
console.log(' Reference: ' + chalk.white(task.productSpecRef));
|
|
273
293
|
}
|
|
274
294
|
// Content
|
|
275
|
-
if (
|
|
295
|
+
if (task.content) {
|
|
276
296
|
console.log('\n' + chalk.cyan('Content:'));
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
297
|
+
task.content.split('\n').forEach(line => console.log(' ' + chalk.gray(line)));
|
|
298
|
+
}
|
|
299
|
+
// Comments
|
|
300
|
+
if (task.comments && task.comments.length > 0) {
|
|
301
|
+
console.log('\n' + chalk.cyan('Comments:'));
|
|
302
|
+
task.comments.forEach((comment) => {
|
|
303
|
+
const date = new Date(comment.createdAt).toLocaleString();
|
|
304
|
+
console.log(' ' + chalk.white(comment.author) + ' ' + chalk.dim(`(${date})`));
|
|
305
|
+
console.log(' ' + chalk.gray(comment.content));
|
|
306
|
+
console.log();
|
|
307
|
+
});
|
|
282
308
|
}
|
|
283
309
|
// Plan Steps
|
|
284
|
-
if (
|
|
285
|
-
|
|
286
|
-
|
|
310
|
+
if (task.planSteps && task.planSteps.length > 0) {
|
|
311
|
+
const doneCount = task.planSteps.filter(s => s.status === 'done').length;
|
|
312
|
+
const total = task.planSteps.length;
|
|
313
|
+
console.log('\n' + chalk.cyan('Plan Steps:') + ' ' + chalk.dim(`(${doneCount}/${total} done)`));
|
|
314
|
+
task.planSteps.forEach((step, index) => {
|
|
287
315
|
const statusIcon = getStepStatusIcon(step.status);
|
|
288
|
-
console.log(` ${index + 1}. ${statusIcon} ${step.description}`);
|
|
316
|
+
console.log(` ${index + 1}. ${statusIcon} ${chalk.dim(`[${step.id}]`)} ${step.description}`);
|
|
289
317
|
if (step.command) {
|
|
290
318
|
console.log(' Command: ' + chalk.gray(step.command));
|
|
291
319
|
}
|
|
292
320
|
});
|
|
293
321
|
}
|
|
322
|
+
// Reviewers
|
|
323
|
+
if (task.metadata?.reviewers && task.metadata.reviewers.length > 0) {
|
|
324
|
+
console.log('\n' + chalk.cyan('Reviewers:'));
|
|
325
|
+
console.log(' ' + task.metadata.reviewers.map(r => chalk.white(r)).join(', '));
|
|
326
|
+
}
|
|
294
327
|
// Tags
|
|
295
|
-
if (
|
|
328
|
+
if (task.metadata?.tags && task.metadata.tags.length > 0) {
|
|
296
329
|
console.log('\n' + chalk.cyan('Tags:'));
|
|
297
|
-
console.log(' ' +
|
|
330
|
+
console.log(' ' + task.metadata.tags.map(tag => chalk.magenta(`#${tag}`)).join(' '));
|
|
298
331
|
}
|
|
299
332
|
// Actions
|
|
300
333
|
console.log('\n' + chalk.cyan('Actions:'));
|
|
301
|
-
console.log(' • Update
|
|
302
|
-
console.log('
|
|
334
|
+
console.log(' • Update status: ' + chalk.yellow(`nut task update ${task.id} --status <status>`));
|
|
335
|
+
console.log(' ' + chalk.gray(`statuses: ${VALID_STATUSES.join(', ')}`));
|
|
336
|
+
console.log(' • Add step: ' + chalk.yellow(`nut task step ${task.id} "Description"`));
|
|
337
|
+
console.log(' • Update step: ' + chalk.yellow(`nut task step-update ${task.id} <stepId> --status <status>`));
|
|
338
|
+
console.log(' ' + chalk.gray('statuses: pending, active, done, failed, skipped'));
|
|
339
|
+
console.log(' • Add comment: ' + chalk.yellow(`nut task comment ${task.id} "Comment"`));
|
|
303
340
|
}
|
|
304
341
|
catch (error) {
|
|
305
342
|
spinner.fail('Failed to load task');
|
|
@@ -321,7 +358,8 @@ taskCommand
|
|
|
321
358
|
.option('-f, --file <path>', 'Read content from a file')
|
|
322
359
|
.option('-t, --tags <tags>', 'Comma-separated tags')
|
|
323
360
|
.option('-p, --priority <level>', 'Priority (low|medium|high|critical)')
|
|
324
|
-
.option('-
|
|
361
|
+
.option('-r, --readiness <score>', 'Readiness score (0-100)')
|
|
362
|
+
.option('-s, --status <status>', 'Initial status (draft|backlog|ready|queued|active|blocked|review|revision|done|canceled|duplicate, default: draft)')
|
|
325
363
|
.option('--spec <ref>', 'Product specification reference')
|
|
326
364
|
.option('--json', 'Output as JSON')
|
|
327
365
|
.option('-y, --yes', 'Skip confirmation prompts')
|
|
@@ -369,7 +407,7 @@ taskCommand
|
|
|
369
407
|
content = interpretEscapeSequences(options.content);
|
|
370
408
|
}
|
|
371
409
|
const spinner = ora('Creating task...').start();
|
|
372
|
-
const
|
|
410
|
+
const task = await createTask({
|
|
373
411
|
intent: title,
|
|
374
412
|
content: content || title, // Use title as content if no content provided
|
|
375
413
|
author: {
|
|
@@ -382,28 +420,32 @@ taskCommand
|
|
|
382
420
|
metadata: {
|
|
383
421
|
tags: parseTags(options.tags),
|
|
384
422
|
priority: validatePriority(options.priority),
|
|
423
|
+
readiness: validateReadiness(options.readiness),
|
|
385
424
|
},
|
|
386
425
|
productSpecRef: options.spec,
|
|
387
426
|
});
|
|
388
|
-
spinner.succeed(`Task created: ${chalk.cyan(
|
|
427
|
+
spinner.succeed(`Task created: ${chalk.cyan(task.id)}`);
|
|
389
428
|
if (options.json) {
|
|
390
|
-
console.log(JSON.stringify(
|
|
429
|
+
console.log(JSON.stringify(task, null, 2));
|
|
391
430
|
return;
|
|
392
431
|
}
|
|
393
432
|
console.log('\n' + chalk.bold('Task Details:'));
|
|
394
|
-
console.log(' ID: ' + chalk.cyan(
|
|
395
|
-
console.log(' Title: ' + chalk.white(
|
|
396
|
-
console.log(' Author: ' + chalk.white(`${
|
|
397
|
-
console.log(' Status: ' + getStatusColor(
|
|
398
|
-
if (
|
|
399
|
-
console.log(' Priority: ' + getPriorityColor(
|
|
433
|
+
console.log(' ID: ' + chalk.cyan(task.id));
|
|
434
|
+
console.log(' Title: ' + chalk.white(task.title || task.intent));
|
|
435
|
+
console.log(' Author: ' + chalk.white(`${task.author.name} (${task.author.type})`));
|
|
436
|
+
console.log(' Status: ' + getStatusColor(task.status)(task.status));
|
|
437
|
+
if (task.metadata?.priority) {
|
|
438
|
+
console.log(' Priority: ' + getPriorityColor(task.metadata.priority)(task.metadata.priority));
|
|
439
|
+
}
|
|
440
|
+
if (task.metadata?.readiness != null) {
|
|
441
|
+
console.log(' Readiness: ' + chalk.white(`${task.metadata.readiness}%`));
|
|
400
442
|
}
|
|
401
|
-
if (
|
|
402
|
-
console.log(' Tags: ' + chalk.gray(
|
|
443
|
+
if (task.metadata?.tags && task.metadata.tags.length > 0) {
|
|
444
|
+
console.log(' Tags: ' + chalk.gray(task.metadata.tags.join(', ')));
|
|
403
445
|
}
|
|
404
446
|
console.log('\n' + chalk.cyan('Next steps:'));
|
|
405
|
-
console.log(' • View task: ' + chalk.yellow(`nut task get ${
|
|
406
|
-
console.log(' • Update task: ' + chalk.yellow(`nut task update ${
|
|
447
|
+
console.log(' • View task: ' + chalk.yellow(`nut task get ${task.id}`));
|
|
448
|
+
console.log(' • Update task: ' + chalk.yellow(`nut task update ${task.id} --status ready`));
|
|
407
449
|
console.log(' • List tasks: ' + chalk.yellow('nut task list'));
|
|
408
450
|
}
|
|
409
451
|
catch (error) {
|
|
@@ -421,9 +463,11 @@ taskCommand
|
|
|
421
463
|
.option('-t, -i, --title, --intent <text>', 'Update title (aliases: -i, --intent)')
|
|
422
464
|
.option('-c, --content <text>', 'Update content')
|
|
423
465
|
.option('-f, --file <path>', 'Read new content from a file')
|
|
424
|
-
.option('-s, --status <status>', 'Update status (draft|backlog|ready|queued|active|blocked|review|done|canceled|duplicate)')
|
|
466
|
+
.option('-s, --status <status>', 'Update status (draft|backlog|ready|queued|active|blocked|review|revision|done|canceled|duplicate)')
|
|
425
467
|
.option('-p, --priority <level>', 'Update priority (low|medium|high|critical)')
|
|
468
|
+
.option('-r, --readiness <score>', 'Update readiness score (0-100)')
|
|
426
469
|
.option('--tags <tags>', 'Update tags (comma-separated)')
|
|
470
|
+
.option('--reviewers <reviewers>', 'Update reviewers (comma-separated names or emails)')
|
|
427
471
|
.option('--spec <ref>', 'Update product specification reference')
|
|
428
472
|
.option('--json', 'Output as JSON')
|
|
429
473
|
.action(async (id, options) => {
|
|
@@ -431,10 +475,10 @@ taskCommand
|
|
|
431
475
|
// Check if any update option is provided
|
|
432
476
|
const titleValue = options.title || options.intent;
|
|
433
477
|
const hasUpdates = titleValue || options.content || options.file ||
|
|
434
|
-
options.status || options.priority || options.tags || options.spec;
|
|
478
|
+
options.status || options.priority || options.readiness || options.tags || options.reviewers || options.spec;
|
|
435
479
|
if (!hasUpdates) {
|
|
436
480
|
console.error(chalk.red('No updates specified.'));
|
|
437
|
-
console.log(chalk.gray('Use --title, --content, --status, --priority, --tags, or --spec to update.'));
|
|
481
|
+
console.log(chalk.gray('Use --title, --content, --status, --priority, --readiness, --tags, --reviewers, or --spec to update.'));
|
|
438
482
|
process.exit(1);
|
|
439
483
|
}
|
|
440
484
|
// Read content from file if specified
|
|
@@ -465,14 +509,18 @@ taskCommand
|
|
|
465
509
|
if (options.spec !== undefined)
|
|
466
510
|
updates.productSpecRef = options.spec;
|
|
467
511
|
// Handle metadata updates
|
|
468
|
-
if (options.priority || options.tags) {
|
|
512
|
+
if (options.priority || options.readiness || options.tags || options.reviewers) {
|
|
469
513
|
updates.metadata = {};
|
|
470
514
|
if (options.priority)
|
|
471
515
|
updates.metadata.priority = validatePriority(options.priority);
|
|
516
|
+
if (options.readiness)
|
|
517
|
+
updates.metadata.readiness = validateReadiness(options.readiness);
|
|
472
518
|
if (options.tags)
|
|
473
519
|
updates.metadata.tags = parseTags(options.tags);
|
|
520
|
+
if (options.reviewers)
|
|
521
|
+
updates.metadata.reviewers = options.reviewers.split(',').map((r) => r.trim()).filter((r) => r.length > 0);
|
|
474
522
|
}
|
|
475
|
-
const updated = await
|
|
523
|
+
const updated = await updateTask(id, updates);
|
|
476
524
|
spinner.succeed(`Task updated: ${chalk.cyan(id)}`);
|
|
477
525
|
if (options.json) {
|
|
478
526
|
console.log(JSON.stringify(updated, null, 2));
|
|
@@ -485,6 +533,9 @@ taskCommand
|
|
|
485
533
|
if (updated.metadata?.priority) {
|
|
486
534
|
console.log(' Priority: ' + getPriorityColor(updated.metadata.priority)(updated.metadata.priority));
|
|
487
535
|
}
|
|
536
|
+
if (updated.metadata?.readiness != null) {
|
|
537
|
+
console.log(' Readiness: ' + chalk.white(`${updated.metadata.readiness}%`));
|
|
538
|
+
}
|
|
488
539
|
}
|
|
489
540
|
catch (error) {
|
|
490
541
|
console.error(chalk.red('Failed to update task:'), error.message);
|
|
@@ -503,18 +554,18 @@ taskCommand
|
|
|
503
554
|
try {
|
|
504
555
|
// Verify task exists first
|
|
505
556
|
const spinner = ora('Loading task...').start();
|
|
506
|
-
const
|
|
557
|
+
const task = await getTask(id);
|
|
507
558
|
spinner.stop();
|
|
508
|
-
if (!
|
|
559
|
+
if (!task) {
|
|
509
560
|
console.error(chalk.red(`Task '${id}' not found.`));
|
|
510
561
|
process.exit(1);
|
|
511
562
|
}
|
|
512
563
|
// Confirm deletion
|
|
513
564
|
if (!options.yes) {
|
|
514
565
|
console.log('\n' + chalk.bold('Task to delete:'));
|
|
515
|
-
console.log(' ID: ' + chalk.cyan(
|
|
516
|
-
console.log(' Title: ' + chalk.white(
|
|
517
|
-
console.log(' Status: ' + getStatusColor(
|
|
566
|
+
console.log(' ID: ' + chalk.cyan(task.id));
|
|
567
|
+
console.log(' Title: ' + chalk.white(task.title || task.intent));
|
|
568
|
+
console.log(' Status: ' + getStatusColor(task.status)(task.status));
|
|
518
569
|
console.log();
|
|
519
570
|
const { confirm } = await inquirer.prompt([
|
|
520
571
|
{
|
|
@@ -530,7 +581,7 @@ taskCommand
|
|
|
530
581
|
}
|
|
531
582
|
}
|
|
532
583
|
const deleteSpinner = ora('Deleting task...').start();
|
|
533
|
-
const deleted = await
|
|
584
|
+
const deleted = await deleteTask(id);
|
|
534
585
|
if (deleted) {
|
|
535
586
|
deleteSpinner.succeed(`Task deleted: ${chalk.cyan(id)}`);
|
|
536
587
|
}
|
|
@@ -552,14 +603,14 @@ taskCommand
|
|
|
552
603
|
.description('Add a plan step to a task')
|
|
553
604
|
.argument('<id>', 'Task ID to add step to')
|
|
554
605
|
.argument('<description>', 'Description of the step')
|
|
555
|
-
.option('-s, --status <status>', 'Initial status (pending/
|
|
606
|
+
.option('-s, --status <status>', 'Initial status (pending/active/done/failed/skipped)', 'pending')
|
|
556
607
|
.option('-c, --command <cmd>', 'Command to execute for this step')
|
|
557
608
|
.option('-e, --expected <outcome>', 'Expected outcome of this step')
|
|
558
609
|
.option('--json', 'Output as JSON')
|
|
559
610
|
.action(async (id, description, options) => {
|
|
560
611
|
try {
|
|
561
612
|
const spinner = ora('Adding plan step...').start();
|
|
562
|
-
const validStepStatuses = ['pending', '
|
|
613
|
+
const validStepStatuses = ['pending', 'active', 'done', 'failed', 'skipped'];
|
|
563
614
|
const status = options.status?.toLowerCase();
|
|
564
615
|
if (status && !validStepStatuses.includes(status)) {
|
|
565
616
|
spinner.fail(`Invalid step status: ${options.status}`);
|
|
@@ -588,8 +639,9 @@ taskCommand
|
|
|
588
639
|
console.log(' Expected: ' + chalk.gray(step.expectedOutcome));
|
|
589
640
|
}
|
|
590
641
|
console.log('\n' + chalk.cyan('Next steps:'));
|
|
591
|
-
console.log(' • View task:
|
|
592
|
-
console.log(' • Add more:
|
|
642
|
+
console.log(' • View task: ' + chalk.yellow(`nut task get ${id}`));
|
|
643
|
+
console.log(' • Add more: ' + chalk.yellow(`nut task step ${id} "Next step"`));
|
|
644
|
+
console.log(' • Update status: ' + chalk.yellow(`nut task step-update ${id} ${step.id} --status <status>`));
|
|
593
645
|
}
|
|
594
646
|
catch (error) {
|
|
595
647
|
console.error(chalk.red('Failed to add plan step:'), error.message);
|
|
@@ -597,6 +649,81 @@ taskCommand
|
|
|
597
649
|
}
|
|
598
650
|
});
|
|
599
651
|
// ============================================================================
|
|
652
|
+
// task step-update (update existing plan step)
|
|
653
|
+
// ============================================================================
|
|
654
|
+
taskCommand
|
|
655
|
+
.command('step-update')
|
|
656
|
+
.description('Update an existing plan step on a task')
|
|
657
|
+
.argument('<id>', 'Task ID containing the step')
|
|
658
|
+
.argument('<stepId>', 'Step ID to update (e.g. step-01)')
|
|
659
|
+
.option('-s, --status <status>', 'New status (pending/active/done/failed/skipped)')
|
|
660
|
+
.option('-d, --description <desc>', 'Update description')
|
|
661
|
+
.option('-c, --command <cmd>', 'Update command')
|
|
662
|
+
.option('-e, --expected <outcome>', 'Update expected outcome')
|
|
663
|
+
.option('--output <output>', 'Set output')
|
|
664
|
+
.option('--error <error>', 'Set error message')
|
|
665
|
+
.option('--json', 'Output as JSON')
|
|
666
|
+
.action(async (id, stepId, options) => {
|
|
667
|
+
try {
|
|
668
|
+
const spinner = ora('Updating plan step...').start();
|
|
669
|
+
const validStepStatuses = ['pending', 'active', 'done', 'failed', 'skipped'];
|
|
670
|
+
if (options.status) {
|
|
671
|
+
const status = options.status.toLowerCase();
|
|
672
|
+
if (!validStepStatuses.includes(status)) {
|
|
673
|
+
spinner.fail(`Invalid step status: ${options.status}`);
|
|
674
|
+
console.log(chalk.gray(`Valid statuses: ${validStepStatuses.join(', ')}`));
|
|
675
|
+
process.exit(1);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
const updates = {};
|
|
679
|
+
if (options.status)
|
|
680
|
+
updates.status = options.status.toLowerCase();
|
|
681
|
+
if (options.description)
|
|
682
|
+
updates.description = options.description;
|
|
683
|
+
if (options.command)
|
|
684
|
+
updates.command = options.command;
|
|
685
|
+
if (options.expected)
|
|
686
|
+
updates.expectedOutcome = options.expected;
|
|
687
|
+
if (options.output)
|
|
688
|
+
updates.output = options.output;
|
|
689
|
+
if (options.error)
|
|
690
|
+
updates.error = options.error;
|
|
691
|
+
if (Object.keys(updates).length === 0) {
|
|
692
|
+
spinner.fail('No updates provided');
|
|
693
|
+
console.log(chalk.gray('Use --status, --description, --command, --expected, --output, or --error'));
|
|
694
|
+
process.exit(1);
|
|
695
|
+
}
|
|
696
|
+
const step = await updatePlanStep(id, stepId, updates);
|
|
697
|
+
spinner.succeed(`Plan step updated: ${chalk.cyan(step.id)}`);
|
|
698
|
+
if (options.json) {
|
|
699
|
+
console.log(JSON.stringify(step, null, 2));
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
console.log('\n' + chalk.bold('Step Details:'));
|
|
703
|
+
console.log(' ID: ' + chalk.cyan(step.id));
|
|
704
|
+
console.log(' Description: ' + chalk.white(step.description));
|
|
705
|
+
console.log(' Status: ' + getStepStatusIcon(step.status) + ' ' + step.status);
|
|
706
|
+
if (step.command) {
|
|
707
|
+
console.log(' Command: ' + chalk.gray(step.command));
|
|
708
|
+
}
|
|
709
|
+
if (step.expectedOutcome) {
|
|
710
|
+
console.log(' Expected: ' + chalk.gray(step.expectedOutcome));
|
|
711
|
+
}
|
|
712
|
+
if (step.output) {
|
|
713
|
+
console.log(' Output: ' + chalk.gray(step.output));
|
|
714
|
+
}
|
|
715
|
+
if (step.error) {
|
|
716
|
+
console.log(' Error: ' + chalk.red(step.error));
|
|
717
|
+
}
|
|
718
|
+
console.log('\n' + chalk.cyan('Next steps:'));
|
|
719
|
+
console.log(' • View task: ' + chalk.yellow(`nut task get ${id}`));
|
|
720
|
+
}
|
|
721
|
+
catch (error) {
|
|
722
|
+
console.error(chalk.red('Failed to update plan step:'), error.message);
|
|
723
|
+
process.exit(1);
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
// ============================================================================
|
|
600
727
|
// task comment (add comment)
|
|
601
728
|
// ============================================================================
|
|
602
729
|
taskCommand
|