takt: github-issue-328-tasuku-ritora (#340)
This commit is contained in:
parent
9e68f086d4
commit
b309233aeb
@ -70,7 +70,11 @@ vi.mock('../features/interactive/index.js', () => ({
|
|||||||
loadRunSessionContext: vi.fn(),
|
loadRunSessionContext: vi.fn(),
|
||||||
getRunPaths: vi.fn(() => ({ logsDir: '/tmp/logs', reportsDir: '/tmp/reports' })),
|
getRunPaths: vi.fn(() => ({ logsDir: '/tmp/logs', reportsDir: '/tmp/reports' })),
|
||||||
formatRunSessionForPrompt: vi.fn(() => ({
|
formatRunSessionForPrompt: vi.fn(() => ({
|
||||||
runTask: '', runPiece: '', runStatus: '', runMovementLogs: '', runReports: '',
|
runTask: '',
|
||||||
|
runPiece: 'default',
|
||||||
|
runStatus: '',
|
||||||
|
runMovementLogs: '',
|
||||||
|
runReports: '',
|
||||||
})),
|
})),
|
||||||
runRetryMode: (...args: unknown[]) => mockRunRetryMode(...args),
|
runRetryMode: (...args: unknown[]) => mockRunRetryMode(...args),
|
||||||
findPreviousOrderContent: vi.fn(() => null),
|
findPreviousOrderContent: vi.fn(() => null),
|
||||||
@ -91,6 +95,17 @@ vi.mock('../features/tasks/execute/taskExecution.js', () => ({
|
|||||||
executeAndCompleteTask: (...args: unknown[]) => mockExecuteAndCompleteTask(...args),
|
executeAndCompleteTask: (...args: unknown[]) => mockExecuteAndCompleteTask(...args),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
vi.mock('../shared/i18n/index.js', () => ({
|
||||||
|
getLabel: vi.fn((key: string) => {
|
||||||
|
const labels: Record<string, string> = {
|
||||||
|
'retry.workflowPrompt': 'Select workflow:',
|
||||||
|
'retry.usePreviousWorkflow': 'Use previous',
|
||||||
|
'retry.changeWorkflow': 'Change workflow',
|
||||||
|
};
|
||||||
|
return labels[key] ?? key;
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
import { retryFailedTask } from '../features/tasks/list/taskRetryActions.js';
|
import { retryFailedTask } from '../features/tasks/list/taskRetryActions.js';
|
||||||
import type { TaskListItem } from '../infra/task/types.js';
|
import type { TaskListItem } from '../infra/task/types.js';
|
||||||
import type { PieceConfig } from '../core/models/index.js';
|
import type { PieceConfig } from '../core/models/index.js';
|
||||||
@ -262,4 +277,54 @@ describe('retryFailedTask', () => {
|
|||||||
|
|
||||||
expect(mockRequeueTask).toHaveBeenCalledWith('my-task', ['failed'], undefined, '既存ノート\n\n追加指示A');
|
expect(mockRequeueTask).toHaveBeenCalledWith('my-task', ['failed'], undefined, '既存ノート\n\n追加指示A');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when previous workflow exists', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockFindRunForTask.mockReturnValue('run-123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show workflow selection prompt when runInfo.piece exists', async () => {
|
||||||
|
const task = makeFailedTask();
|
||||||
|
|
||||||
|
await retryFailedTask(task, '/project');
|
||||||
|
|
||||||
|
expect(mockSelectOptionWithDefault).toHaveBeenCalledWith(
|
||||||
|
'Select workflow:',
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ value: 'use_previous' }),
|
||||||
|
expect.objectContaining({ value: 'change' }),
|
||||||
|
]),
|
||||||
|
'use_previous',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use previous workflow when use_previous is selected', async () => {
|
||||||
|
const task = makeFailedTask();
|
||||||
|
mockSelectOptionWithDefault.mockResolvedValue('use_previous');
|
||||||
|
|
||||||
|
await retryFailedTask(task, '/project');
|
||||||
|
|
||||||
|
expect(mockSelectPiece).not.toHaveBeenCalled();
|
||||||
|
expect(mockLoadPieceByIdentifier).toHaveBeenCalledWith('default', '/project');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call selectPiece when change is selected', async () => {
|
||||||
|
const task = makeFailedTask();
|
||||||
|
mockSelectOptionWithDefault.mockResolvedValue('change');
|
||||||
|
|
||||||
|
await retryFailedTask(task, '/project');
|
||||||
|
|
||||||
|
expect(mockSelectPiece).toHaveBeenCalledWith('/project');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when workflow selection is cancelled', async () => {
|
||||||
|
const task = makeFailedTask();
|
||||||
|
mockSelectOptionWithDefault.mockResolvedValue(null);
|
||||||
|
|
||||||
|
const result = await retryFailedTask(task, '/project');
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
expect(mockLoadPieceByIdentifier).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { TaskRunner } from '../../../infra/task/index.js';
|
|||||||
import { loadPieceByIdentifier, resolvePieceConfigValue, getPieceDescription } from '../../../infra/config/index.js';
|
import { loadPieceByIdentifier, resolvePieceConfigValue, getPieceDescription } from '../../../infra/config/index.js';
|
||||||
import { selectPiece } from '../../pieceSelection/index.js';
|
import { selectPiece } from '../../pieceSelection/index.js';
|
||||||
import { selectOptionWithDefault } from '../../../shared/prompt/index.js';
|
import { selectOptionWithDefault } from '../../../shared/prompt/index.js';
|
||||||
|
import { getLabel } from '../../../shared/i18n/index.js';
|
||||||
import { info, header, blankLine, status } from '../../../shared/ui/index.js';
|
import { info, header, blankLine, status } from '../../../shared/ui/index.js';
|
||||||
import { createLogger } from '../../../shared/utils/index.js';
|
import { createLogger } from '../../../shared/utils/index.js';
|
||||||
import type { PieceConfig } from '../../../core/models/index.js';
|
import type { PieceConfig } from '../../../core/models/index.js';
|
||||||
@ -128,12 +129,46 @@ export async function retryFailedTask(
|
|||||||
|
|
||||||
displayFailureInfo(task);
|
displayFailureInfo(task);
|
||||||
|
|
||||||
const selectedPiece = await selectPiece(projectDir);
|
const matchedSlug = findRunForTask(worktreePath, task.content);
|
||||||
if (!selectedPiece) {
|
const runInfo = matchedSlug ? buildRetryRunInfo(worktreePath, matchedSlug) : null;
|
||||||
|
|
||||||
|
let selectedPiece: string;
|
||||||
|
if (runInfo?.piece) {
|
||||||
|
const usePreviousLabel = getLabel('retry.usePreviousWorkflow');
|
||||||
|
const changeWorkflowLabel = getLabel('retry.changeWorkflow');
|
||||||
|
const choice = await selectOptionWithDefault(
|
||||||
|
getLabel('retry.workflowPrompt'),
|
||||||
|
[
|
||||||
|
{ label: `${runInfo.piece} - ${usePreviousLabel}`, value: 'use_previous' },
|
||||||
|
{ label: changeWorkflowLabel, value: 'change' },
|
||||||
|
],
|
||||||
|
'use_previous',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (choice === null) {
|
||||||
info('Cancelled');
|
info('Cancelled');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (choice === 'use_previous') {
|
||||||
|
selectedPiece = runInfo.piece;
|
||||||
|
} else {
|
||||||
|
const selected = await selectPiece(projectDir);
|
||||||
|
if (!selected) {
|
||||||
|
info('Cancelled');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
selectedPiece = selected;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const selected = await selectPiece(projectDir);
|
||||||
|
if (!selected) {
|
||||||
|
info('Cancelled');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
selectedPiece = selected;
|
||||||
|
}
|
||||||
|
|
||||||
const previewCount = resolvePieceConfigValue(projectDir, 'interactivePreviewMovements');
|
const previewCount = resolvePieceConfigValue(projectDir, 'interactivePreviewMovements');
|
||||||
const pieceConfig = loadPieceByIdentifier(selectedPiece, projectDir);
|
const pieceConfig = loadPieceByIdentifier(selectedPiece, projectDir);
|
||||||
|
|
||||||
@ -155,8 +190,6 @@ export async function retryFailedTask(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Runs data lives in the worktree (written during previous execution)
|
// Runs data lives in the worktree (written during previous execution)
|
||||||
const matchedSlug = findRunForTask(worktreePath, task.content);
|
|
||||||
const runInfo = matchedSlug ? buildRetryRunInfo(worktreePath, matchedSlug) : null;
|
|
||||||
const previousOrderContent = findPreviousOrderContent(worktreePath, matchedSlug);
|
const previousOrderContent = findPreviousOrderContent(worktreePath, matchedSlug);
|
||||||
|
|
||||||
blankLine();
|
blankLine();
|
||||||
|
|||||||
@ -96,6 +96,9 @@ instruct:
|
|||||||
retry:
|
retry:
|
||||||
ui:
|
ui:
|
||||||
intro: "Retry mode - describe additional instructions. Commands: /go (create instruction & run), /retry (rerun previous order), /cancel (exit)"
|
intro: "Retry mode - describe additional instructions. Commands: /go (create instruction & run), /retry (rerun previous order), /cancel (exit)"
|
||||||
|
workflowPrompt: "Select workflow:"
|
||||||
|
usePreviousWorkflow: "Use previous"
|
||||||
|
changeWorkflow: "Change workflow"
|
||||||
|
|
||||||
run:
|
run:
|
||||||
notifyComplete: "Run complete ({total} tasks)"
|
notifyComplete: "Run complete ({total} tasks)"
|
||||||
|
|||||||
@ -96,6 +96,9 @@ instruct:
|
|||||||
retry:
|
retry:
|
||||||
ui:
|
ui:
|
||||||
intro: "リトライモード - 追加指示を入力してください。コマンド: /go(指示書作成・実行), /retry(前回の指示書で再実行), /cancel(終了)"
|
intro: "リトライモード - 追加指示を入力してください。コマンド: /go(指示書作成・実行), /retry(前回の指示書で再実行), /cancel(終了)"
|
||||||
|
workflowPrompt: "ワークフローを選択:"
|
||||||
|
usePreviousWorkflow: "前回のまま使用"
|
||||||
|
changeWorkflow: "ワークフローを変更"
|
||||||
|
|
||||||
run:
|
run:
|
||||||
notifyComplete: "run完了 ({total} tasks)"
|
notifyComplete: "run完了 ({total} tasks)"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user