UNPKG

@morodomi/ait3

Version:

AIT³ Development Platform - AI + Ticket + Test + Tool driven development methodology

73 lines (72 loc) 3.02 kB
import { ValidationError, TicketNotFoundError } from '../../common/errors.js'; import { IDUtils } from '../../common/utils.js'; import { formatTicketLocation } from '../../common/utils/location-utils.js'; import chalk from 'chalk'; /** * Undo ticket to previous state * State transitions: * - doing → todo (remove started timestamp) * - done → doing (remove completed timestamp) * - todo → error (cannot undo initial state) */ export async function undoTicket(args, services) { try { // Input validation if (!args.id || args.id.trim().length === 0) { throw new ValidationError('Ticket ID is required'); } const ticketId = args.id.trim(); // Validate ticket ID format if (!IDUtils.isValidTicketId(ticketId)) { throw new ValidationError('Invalid ticket ID format. Use local format (0001) or GitHub format (#70, 70)'); } // Get current ticket to check status and show transition const ticket = await services.ticketService.getTicket(ticketId); if (!ticket) { throw new TicketNotFoundError(ticketId); } // Determine transition let transition; if (ticket.status === 'todo') { throw new ValidationError(`Cannot undo ticket #${ticketId}: already in 'todo' state`); } else if (ticket.status === 'doing') { transition = 'doing → todo'; } else if (ticket.status === 'done') { transition = 'done → doing'; } else { throw new ValidationError(`Invalid ticket status: ${ticket.status}`); } // Dry run mode - show preview without executing if (args.dryRun) { return { success: true, message: `${chalk.yellow('DRY RUN:')} Would undo ticket #${ticketId}\n` + ` Title: ${ticket.title}\n` + ` Transition: ${transition}\n` + ` ${chalk.dim('Use without --dry-run to execute')}` }; } // Execute undo operation await services.ticketService.undoTicket(ticketId); // Get updated ticket for location display const updatedTicket = await services.ticketService.getTicket(ticketId); const locationDisplay = updatedTicket ? formatTicketLocation(updatedTicket, services.ticketService) : ''; return { success: true, message: `${chalk.green('SUCCESS:')} Undid ticket #${ticketId}\n` + ` Title: ${ticket.title}\n` + ` Transition: ${transition}\n` + (locationDisplay ? ` Location: ${locationDisplay}` : '') }; } catch (error) { if (error instanceof ValidationError || error instanceof TicketNotFoundError) { throw error; } // Wrap unexpected errors throw new Error(`Failed to undo ticket: ${error instanceof Error ? error.message : String(error)}`); } }