From 9fc8ab73fd8816277f139cbc0883d3821d05905b Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Wed, 4 Mar 2026 16:20:37 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=AA=E3=83=88=E3=83=A9=E3=82=A4=E6=99=82?= =?UTF-8?q?=E3=81=A7=E3=82=BF=E3=82=B9=E3=82=AF=E3=81=AB=E3=81=A4=E3=82=81?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/retrySlashCommand.test.ts | 20 +++++++++++-- src/features/interactive/conversationLoop.ts | 30 ++++++++++++++------ 2 files changed, 38 insertions(+), 12 deletions(-) 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: {