@morodomi/ait3
Version:
AIT³ Development Platform - AI + Ticket + Test + Tool driven development methodology
194 lines (192 loc) • 6.54 kB
JavaScript
import { Command, Option } from 'commander';
import { createTicket } from './create.js';
import { listTickets } from './list.js';
import { showTicket } from './show.js';
import { startTicket } from './start.js';
import { completeTicket } from './complete.js';
import { undoTicket } from './undo.js';
import { deleteTicket } from './delete.js';
import { ServiceFactory } from '../../services/ServiceFactory.js';
import { createMissingIdErrorHandler } from './error-handler.js';
import { handleCommandError, COMMON_TIPS } from '../../common/command-error-handler.js';
// Service container will be created per command to respect config changes
export const ticketCommand = new Command('ticket')
.description('Ticket management commands')
.addHelpText('after', `
Examples:
$ ait3 ticket create "Fix authentication bug"
$ ait3 ticket create "Add dark mode" --priority high --assignee "john@example.com"
$ ait3 ticket create "Feature request" --labels "feature,backend,urgent"
Future commands:
$ ait3 ticket list
$ ait3 ticket show 001
$ ait3 ticket start 001
$ ait3 ticket complete 001
`);
// ticket create subcommand
ticketCommand
.command('create <title>')
.description('Create a new ticket')
.addOption(new Option('-p, --priority <priority>', 'Set priority level').choices(['low', 'medium', 'high', 'critical']).default('medium'))
.option('-a, --assignee <assignee>', 'Assign ticket to user')
.option('-l, --labels <labels>', 'Comma-separated labels (e.g., "feature,backend,urgent")')
.action(async (title, options) => {
try {
// Create services for this command execution
const services = await ServiceFactory.createServices();
// Parse labels
const labels = options.labels?.split(',').map((l) => l.trim()).filter(Boolean) || [];
const result = await createTicket({
title,
priority: options.priority,
assignee: options.assignee,
labels
}, services);
console.log(result.message);
process.exit(result.success ? 0 : 1);
}
catch (error) {
handleCommandError(error, {
action: 'create ticket',
tips: {
priority: COMMON_TIPS.priority,
title: COMMON_TIPS.title
}
});
}
});
// ticket list subcommand
ticketCommand
.command('list')
.description('List tickets')
.addOption(new Option('-s, --status <status>', 'Filter by status').choices(['todo', 'doing', 'done']))
.addOption(new Option('-p, --priority <priority>', 'Filter by priority').choices(['low', 'medium', 'high', 'critical']))
.action(async (options) => {
try {
const services = await ServiceFactory.createServices();
const result = await listTickets(options, services);
console.log(result.message);
process.exit(result.success ? 0 : 1);
}
catch (error) {
handleCommandError(error, {
action: 'list tickets',
tips: {
status: COMMON_TIPS.status,
priority: COMMON_TIPS.priority
}
});
}
});
// ticket show subcommand
ticketCommand
.command('show <id>')
.description('Show ticket details')
.showHelpAfterError()
.exitOverride(createMissingIdErrorHandler('show'))
.action(async (id) => {
try {
const services = await ServiceFactory.createServices();
const result = await showTicket({ id }, services);
console.log(result.message);
process.exit(result.success ? 0 : 1);
}
catch (error) {
handleCommandError(error, {
action: 'show ticket',
tips: {
ticketId: COMMON_TIPS.ticketId
}
});
}
});
// ticket start subcommand
ticketCommand
.command('start <id>')
.description('Start working on a ticket')
.showHelpAfterError()
.exitOverride(createMissingIdErrorHandler('start'))
.action(async (id) => {
try {
const services = await ServiceFactory.createServices();
const result = await startTicket({ id }, services);
console.log(result.message);
process.exit(result.success ? 0 : 1);
}
catch (error) {
handleCommandError(error, {
action: 'start ticket',
tips: {
ticketId: COMMON_TIPS.ticketId
}
});
}
});
// ticket complete subcommand
ticketCommand
.command('complete <id>')
.description('Complete a ticket')
.showHelpAfterError()
.exitOverride(createMissingIdErrorHandler('complete'))
.action(async (id) => {
try {
const services = await ServiceFactory.createServices();
const result = await completeTicket({ id }, services);
console.log(result.message);
process.exit(result.success ? 0 : 1);
}
catch (error) {
handleCommandError(error, {
action: 'complete ticket',
tips: {
ticketId: COMMON_TIPS.ticketId
}
});
}
});
// ticket undo subcommand
ticketCommand
.command('undo <id>')
.description('Undo ticket to previous state')
.option('--dry-run', 'Preview changes without executing')
.showHelpAfterError()
.exitOverride(createMissingIdErrorHandler('undo'))
.action(async (id, options) => {
try {
const services = await ServiceFactory.createServices();
const result = await undoTicket({ id, dryRun: options.dryRun }, services);
console.log(result.message);
process.exit(result.success ? 0 : 1);
}
catch (error) {
handleCommandError(error, {
action: 'undo ticket',
tips: {
ticketId: COMMON_TIPS.ticketId
}
});
}
});
// ticket delete subcommand
ticketCommand
.command('delete <id>')
.description('Delete a ticket')
.option('--dry-run', 'Preview deletion without executing')
.showHelpAfterError()
.exitOverride(createMissingIdErrorHandler('delete'))
.action(async (id, options) => {
try {
const services = await ServiceFactory.createServices();
const result = await deleteTicket({ id, dryRun: options.dryRun }, services);
console.log(result.message);
process.exit(result.success ? 0 : 1);
}
catch (error) {
handleCommandError(error, {
action: 'delete ticket',
tips: {
ticketId: COMMON_TIPS.ticketId
}
});
}
});