taskwerk
Version:
A task management CLI for developers and AI agents working together
177 lines (153 loc) • 6.59 kB
JavaScript
import { Command } from 'commander';
import { TaskwerkAPI } from '../../api/taskwerk-api.js';
import { Logger } from '../../logging/logger.js';
export function taskUpdateCommand() {
const update = new Command('update');
update
.description('Update a task')
.argument('<id>', 'Task ID')
.option('-n, --name <name>', 'Update task name')
.option('-p, --priority <level>', 'Update priority')
.option('-a, --assignee <name>', 'Update assignee')
.option('-e, --estimate <hours>', 'Update time estimate')
.option('-s, --status <status>', 'Update status')
.option('--progress <percent>', 'Update progress (0-100)')
.option('--add-tags <tags...>', 'Add tags')
.option('--remove-tags <tags...>', 'Remove tags')
.option('--note <text>', 'Append a note')
.addHelpText(
'after',
`
Examples:
Update basic fields:
$ twrk updatetask 1 -n "Updated task name" # Rename task
$ twrk updatetask 1 -p critical # Change priority
$ twrk updatetask 1 -a @sarah # Reassign task
$ twrk updatetask 1 -s in-progress # Update status
$ twrk updatetask 1 -e 16 # Update estimate to 16 hours
$ twrk updatetask 1 --progress 75 # Set 75% complete
Multiple updates at once:
$ twrk updatetask 1 -p high -s in-progress -a @team
$ twrk updatetask 1 -n "Redesign homepage" -e 40 --progress 25
Managing tags:
$ twrk updatetask 1 --add-tags urgent hotfix # Add multiple tags
$ twrk updatetask 1 --remove-tags wontfix # Remove tags
$ twrk updatetask 1 --add-tags v2.0 --remove-tags v1.0
Adding notes for context:
$ twrk updatetask 1 --note "Blocked by API changes"
$ twrk updatetask 1 --note "Customer reported this affects checkout"
$ twrk updatetask 1 -s blocked --note "Waiting for design approval"
For AI/LLM workflows:
$ twrk updatetask 1 -a @ai-agent --note "Please implement the login endpoint"
$ twrk updatetask 1 --note "Requirements:
- Use JWT for auth
- Rate limit to 5 attempts/minute
- Log all failed attempts"
$ twrk updatetask 1 -a @claude --add-tags ai-ready
Complex updates:
$ twrk updatetask TASK-001 -s done --progress 100 --note "Deployed to prod"
$ twrk updatetask 5 -p low -a null --note "Deprioritized, unassigning"
Note:
- Use fuzzy matching: '1' instead of 'TASK-001'
- Set assignee to 'null' to unassign
- Notes are appended, not replaced`
)
.action(async (id, options) => {
const logger = new Logger('task-update');
try {
const api = new TaskwerkAPI();
// Check if task exists first (this will also resolve fuzzy matches)
const currentTask = api.getTask(id);
const actualTaskId = currentTask.id; // Use the actual task ID for subsequent operations
// Build updates object
const updates = {};
if (options.name) {
updates.name = options.name;
}
if (options.priority) {
updates.priority = options.priority;
}
if (options.assignee) {
updates.assignee = options.assignee;
}
if (options.status) {
updates.status = options.status;
}
if (options.estimate) {
const estimateNum = parseInt(options.estimate);
if (isNaN(estimateNum)) {
console.error('❌ Estimate must be a number');
process.exit(1);
}
updates.estimate = estimateNum;
}
if (options.progress) {
const progressNum = parseInt(options.progress);
if (isNaN(progressNum) || progressNum < 0 || progressNum > 100) {
console.error('❌ Progress must be a number between 0 and 100');
process.exit(1);
}
updates.progress = progressNum;
}
// Update task if there are field updates
if (Object.keys(updates).length > 0) {
const updatedTask = await api.updateTask(actualTaskId, updates, 'user');
console.log(`✅ Updated task ${updatedTask.id}: ${updatedTask.name}`);
// Show what changed
for (const [field, newValue] of Object.entries(updates)) {
const oldValue = currentTask[field];
if (oldValue !== newValue) {
console.log(` ${field}: ${oldValue || '(none)'} → ${newValue}`);
}
}
}
// Handle tag operations
if (options.addTags && options.addTags.length > 0) {
await api.addTaskTags(actualTaskId, options.addTags, 'user');
console.log(`🏷️ Added tags: ${options.addTags.join(', ')}`);
}
if (options.removeTags && options.removeTags.length > 0) {
await api.removeTaskTags(actualTaskId, options.removeTags, 'user');
console.log(`🗑️ Removed tags: ${options.removeTags.join(', ')}`);
}
// Add note if provided
if (options.note) {
await api.addTaskNote(actualTaskId, options.note, 'user');
console.log(`📝 Added note: ${options.note}`);
}
// Show updated task details
if (
Object.keys(updates).length > 0 ||
options.addTags ||
options.removeTags ||
options.note
) {
console.log('\nUpdated task:');
console.log(` ID: ${actualTaskId}`);
console.log(` Name: ${updates.name || currentTask.name}`);
console.log(` Status: ${updates.status || currentTask.status}`);
console.log(` Priority: ${updates.priority || currentTask.priority}`);
if (updates.assignee || currentTask.assignee) {
console.log(` Assignee: ${updates.assignee || currentTask.assignee}`);
}
if (updates.progress !== undefined || currentTask.progress > 0) {
console.log(
` Progress: ${updates.progress !== undefined ? updates.progress : currentTask.progress}%`
);
}
} else {
console.log('ℹ️ No changes were made');
}
} catch (error) {
logger.error('Failed to update task', error);
// For TaskNotFoundError, the message already contains suggestions
if (error.code === 'TASK_NOT_FOUND') {
console.error(`❌ ${error.message}`);
} else {
console.error('❌ Failed to update task:', error.message);
}
process.exit(1);
}
});
return update;
}