diff --git a/src/__tests__/retrySlashCommand.test.ts b/src/__tests__/retrySlashCommand.test.ts index 3a2f4cd..5387196 100644 --- a/src/__tests__/retrySlashCommand.test.ts +++ b/src/__tests__/retrySlashCommand.test.ts @@ -2,7 +2,7 @@ * Tests for /retry slash command in the conversation loop. * * Verifies: - * - /retry with previousOrderContent returns execute action with order content + * - /retry with previousOrderContent uses post-summary action selection * - /retry without previousOrderContent shows error and continues loop * - /retry in retry mode with order.md context in system prompt */ @@ -18,6 +18,7 @@ import { createMockProvider, type MockProviderCapture, } from './helpers/stdinSimulator.js'; +import { selectOption } from '../shared/prompt/index.js'; // --- Mocks (infrastructure only) --- @@ -147,7 +148,8 @@ describe('/retry slash command', () => { rmSync(tmpDir, { recursive: true, force: true }); }); - it('should execute with previous order content when /retry is used', async () => { + it('should route previous order content through action selection when /retry is used', async () => { + vi.mocked(selectOption).mockResolvedValueOnce('save_task'); const orderContent = '# Task Order\n\nImplement feature X with tests.'; setupRawStdin(toRawInputs(['/retry'])); setupProvider([]); @@ -155,7 +157,7 @@ describe('/retry slash command', () => { const retryContext = buildRetryContext({ previousOrderContent: orderContent }); const result = await runRetryMode(tmpDir, retryContext, orderContent); - expect(result.action).toBe('execute'); + expect(result.action).toBe('save_task'); expect(result.task).toBe(orderContent); }); @@ -170,6 +172,18 @@ describe('/retry slash command', () => { expect(result.action).toBe('cancel'); }); + it('should continue when /retry is selected with continue action', async () => { + vi.mocked(selectOption).mockResolvedValueOnce('continue'); + setupRawStdin(toRawInputs(['/retry', '/cancel'])); + setupProvider([]); + + const orderContent = '# Task Order\n\nImplement feature X with tests.'; + const retryContext = buildRetryContext({ previousOrderContent: orderContent }); + const result = await runRetryMode(tmpDir, retryContext, orderContent); + + expect(result.action).toBe('cancel'); + }); + it('should inject order.md content into retry system prompt', async () => { const orderContent = '# Build login page\n\nWith OAuth2 support.'; setupRawStdin(toRawInputs(['check the order', '/cancel'])); diff --git a/src/features/interactive/conversationLoop.ts b/src/features/interactive/conversationLoop.ts index e113278..058d253 100644 --- a/src/features/interactive/conversationLoop.ts +++ b/src/features/interactive/conversationLoop.ts @@ -144,6 +144,18 @@ export async function runConversationLoop( } } + async function handleSummaryAction(task: string): Promise { + const selectedAction = strategy.selectAction + ? await strategy.selectAction(task, ctx.lang) + : await selectPostSummaryAction(task, ui.proposed, ui); + if (selectedAction === 'continue' || selectedAction === null) { + info(ui.continuePrompt); + return null; + } + log.info('Conversation action selected', { action: selectedAction, messageCount: history.length }); + return { action: selectedAction, task }; + } + while (true) { const input = await readMultilineInput(chalk.green('> ')); @@ -203,8 +215,12 @@ export async function runConversationLoop( info(ui.retryNoOrder); continue; } - log.info('Retry command — resubmitting previous order.md'); - return { action: 'execute', task: strategy.previousOrderContent }; + log.info('Retry command — using previous order.md'); + const selectedAction = await handleSummaryAction(strategy.previousOrderContent); + if (selectedAction === null) { + continue; + } + return selectedAction; } case SlashCommand.Go: { @@ -234,15 +250,11 @@ export async function runConversationLoop( return { action: 'cancel', task: '' }; } const task = summaryResult.content.trim(); - const selectedAction = strategy.selectAction - ? await strategy.selectAction(task, ctx.lang) - : await selectPostSummaryAction(task, ui.proposed, ui); - if (selectedAction === 'continue' || selectedAction === null) { - info(ui.continuePrompt); + const selectedAction = await handleSummaryAction(task); + if (selectedAction === null) { continue; } - log.info('Conversation action selected', { action: selectedAction, messageCount: history.length }); - return { action: selectedAction, task }; + return selectedAction; } case SlashCommand.Replay: {