UNPKG

@morodomi/ait3

Version:

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

143 lines (130 loc) 5.86 kB
import { ValidationError, TicketNotFoundError } from '../../common/errors.js'; import { STYLES } from '../../common/styles.js'; import { IDUtils } from '../../common/utils.js'; import { generateCommitMessage, formatTicketHeader } from '../../common/flow-utils.js'; import { formatTicketDisplay } from '../../common/utils/location-utils.js'; const INVALID_TICKET_ID_MESSAGE = 'Invalid ticket ID format. Use local format (0001) or GitHub format (#70, 70)'; /** * Convert ticket title to kebab-case feature name */ function titleToFeatureName(title) { return title.toLowerCase().replace(/\s+/g, '-'); } export async function planPhase(args, services) { const { ticketId, mode = 'guided', requirements } = args; // Validate ticket ID format if (!IDUtils.isValidTicketId(ticketId)) { throw new ValidationError(INVALID_TICKET_ID_MESSAGE, 'ticketId'); } // Fetch ticket to get feature name const ticket = await services.ticketService.getTicket(ticketId); if (!ticket) { throw new TicketNotFoundError(ticketId); } // Use ticket title as feature name const featureName = titleToFeatureName(ticket.title); // Create ticket info for display const ticketInfo = `\n${STYLES.info('INFO: Ticket #' + ticketId)}: ${ticket.title}`; switch (mode) { case 'express': return expressPlan(ticketId, featureName, ticket, services, requirements, ticketInfo); case 'manual': return manualPlan(ticketId, featureName, ticket, services, ticketInfo); case 'guided': default: return guidedPlan(ticketId, featureName, ticket, services, requirements, ticketInfo); } } function expressPlan(ticketId, featureName, ticket, services, requirements, _ticketInfo) { const requirementsText = requirements?.length ? `\n${STYLES.info('INFO: Requirements')}: ${requirements.join(', ')}` : ''; const ticketLocation = formatTicketDisplay(ticket, services.ticketService); return { success: true, message: ` ${formatTicketHeader(ticketId, featureName, 'PLANNING Phase (Express)', ticket, services)}${requirementsText} ${STYLES.bold('INFO: Claude Code Quick Analysis')}: 1. Read ticket: ${STYLES.info(ticketLocation)} 2. Analyze existing patterns in codebase 3. Propose minimal viable implementation 4. Skip Gemini analysis (express mode) ${STYLES.info('TODO: Next actions for AI')}: ├─ Quick analysis: │ └─ Read ${STYLES.info(ticketLocation)} ├─ Rapid proposal: │ └─ Minimal viable approach └─ Fast commit: └─ ${STYLES.code(`git commit -m "${generateCommitMessage('planning', ticketId, featureName)}"`)} ${STYLES.muted('Express mode: ait3 flow red after quick approval')} ` }; } function manualPlan(ticketId, featureName, ticket, services, _ticketInfo) { const ticketLocation = formatTicketDisplay(ticket, services.ticketService); return { success: true, message: ` ${formatTicketHeader(ticketId, featureName, 'PLANNING Phase (Manual)', ticket, services)} ${STYLES.bold('INFO: Dialectical Process')}: ├─ ${STYLES.info('Claude proposes')} → Generate technical approach with clear rationale ├─ ${STYLES.danger('Gemini refutes')} → Challenge assumptions and identify alternatives └─ ${STYLES.success('Human decides')} → Synthesize evidence and make informed decisions ${STYLES.bold('INFO: Manual Planning Steps')}: 1. Read ticket: ${STYLES.info(ticketLocation)} 2. Research existing approaches and patterns 3. Consult: ${STYLES.info(`gemini -p "@src/ @CLAUDE.md @.tickets/doing/${ticketId}-*.md Critique approach"`)} 4. Document architectural decisions 5. Update ticket with reasoning ${STYLES.info('TODO: Next actions for AI')}: ├─ Research approaches: │ └─ Investigate existing patterns ├─ Create proposal: │ └─ Document design decisions ├─ Get dialectical critique: │ └─ ${STYLES.code('gemini -p "@src/ @CLAUDE.md Critique"')} ├─ Synthesize decision: │ └─ Weigh arguments and choose └─ Document reasoning: └─ ${STYLES.code(`git commit -m "${generateCommitMessage('planning', ticketId, featureName)}"`)} ${STYLES.muted('Manual mode: Proceed to ait3 flow red after decision')} ` }; } function guidedPlan(ticketId, featureName, ticket, services, requirements, _ticketInfo) { const requirementsSection = requirements?.length ? `\n${STYLES.info('INFO: Requirements')}: ${requirements.join(', ')}` : ''; const ticketLocation = formatTicketDisplay(ticket, services.ticketService); return { success: true, message: ` ${formatTicketHeader(ticketId, featureName, 'PLANNING Phase', ticket, services)}${requirementsSection} ${STYLES.bold('Claude Code Instructions')}: 1. Read ticket: ${STYLES.info(ticketLocation)} 2. Analyze and propose: ├─ Purpose & Goals ├─ Implementation approach ├─ Test scenarios ├─ Edge cases ├─ Technical considerations └─ Dependencies & Integration points 3. (Optional) Gemini analysis: ${STYLES.info(`$ gemini -p "@src/ @CLAUDE.md @.tickets/doing/${ticketId}-*.md Critique this approach"`)} ${STYLES.muted('Note: @src/ includes relevant source files. For full codebase use @./')} 4. Present proposal for human decision ${STYLES.info('TODO: Next actions for AI')}: ├─ Analyze ticket: │ └─ Read ${STYLES.info(ticketLocation)} ├─ Research codebase: │ └─ Understand existing patterns ├─ Propose approach: │ └─ Document technical design ├─ Validate with Gemini (optional): │ └─ ${STYLES.code('gemini -p "@src/ Critique approach"')} └─ Commit design: └─ ${STYLES.code(`git commit -m "${generateCommitMessage('planning', ticketId, featureName)}"`)} ${STYLES.muted('After approval: ait3 flow red')} ` }; }