diff --git a/OPENCODE_CONFIG_CONTENT b/OPENCODE_CONFIG_CONTENT new file mode 100644 index 0000000..6b5dea8 --- /dev/null +++ b/OPENCODE_CONFIG_CONTENT @@ -0,0 +1,2 @@ +{ + "$schema": "https://opencode.ai/config.json","model":"zai-coding-plan/glm-5","small_model":"zai-coding-plan/glm-5","permission":"deny"} \ No newline at end of file diff --git a/src/__tests__/saveTaskFile.test.ts b/src/__tests__/saveTaskFile.test.ts index f4edfb1..54d0e91 100644 --- a/src/__tests__/saveTaskFile.test.ts +++ b/src/__tests__/saveTaskFile.test.ts @@ -139,14 +139,43 @@ describe('saveTaskFromInteractive', () => { }); it('should record issue number in tasks.yaml when issue option is provided', async () => { - // Given: user declines worktree mockConfirm.mockResolvedValueOnce(false); - // When await saveTaskFromInteractive(testDir, 'Fix login bug', 'default', { issue: 42 }); - // Then const task = loadTasks(testDir).tasks[0]!; expect(task.issue).toBe(42); }); + + describe('with confirmAtEndMessage', () => { + it('should not save task when user declines confirmAtEndMessage', async () => { + mockConfirm.mockResolvedValueOnce(false); + + await saveTaskFromInteractive(testDir, 'Task content', 'default', { + issue: 42, + confirmAtEndMessage: 'Add this issue to tasks?', + }); + + expect(fs.existsSync(path.join(testDir, '.takt', 'tasks.yaml'))).toBe(false); + }); + + it('should prompt worktree settings after confirming confirmAtEndMessage', async () => { + mockConfirm.mockResolvedValueOnce(true); + mockPromptInput.mockResolvedValueOnce(''); + mockPromptInput.mockResolvedValueOnce(''); + mockConfirm.mockResolvedValueOnce(true); + mockConfirm.mockResolvedValueOnce(false); + + await saveTaskFromInteractive(testDir, 'Task content', 'default', { + issue: 42, + confirmAtEndMessage: 'Add this issue to tasks?', + }); + + expect(mockConfirm).toHaveBeenNthCalledWith(1, 'Add this issue to tasks?', true); + expect(mockConfirm).toHaveBeenNthCalledWith(2, 'Create worktree?', true); + const task = loadTasks(testDir).tasks[0]!; + expect(task.issue).toBe(42); + expect(task.worktree).toBe(true); + }); + }); }); diff --git a/src/features/tasks/add/index.ts b/src/features/tasks/add/index.ts index cd48f40..3fe042f 100644 --- a/src/features/tasks/add/index.ts +++ b/src/features/tasks/add/index.ts @@ -151,13 +151,13 @@ export async function saveTaskFromInteractive( piece?: string, options?: { issue?: number; confirmAtEndMessage?: string }, ): Promise { - const settings = await promptWorktreeSettings(); if (options?.confirmAtEndMessage) { const approved = await confirm(options.confirmAtEndMessage, true); if (!approved) { return; } } + const settings = await promptWorktreeSettings(); const created = await saveTaskFile(cwd, task, { piece, issue: options?.issue, ...settings }); displayTaskCreationResult(created, settings, piece); } diff --git a/src/infra/opencode/client.ts b/src/infra/opencode/client.ts index 841221d..1f33038 100644 --- a/src/infra/opencode/client.ts +++ b/src/infra/opencode/client.ts @@ -36,6 +36,7 @@ const OPENCODE_STREAM_ABORTED_MESSAGE = 'OpenCode execution aborted'; const OPENCODE_RETRY_MAX_ATTEMPTS = 3; const OPENCODE_RETRY_BASE_DELAY_MS = 250; const OPENCODE_INTERACTION_TIMEOUT_MS = 5000; +const OPENCODE_SERVER_START_TIMEOUT_MS = 30000; const OPENCODE_RETRYABLE_ERROR_PATTERNS = [ 'stream disconnected before completion', 'transport error', @@ -324,6 +325,7 @@ export class OpenCodeClient { port, signal: streamAbortController.signal, config, + timeout: OPENCODE_SERVER_START_TIMEOUT_MS, }); opencodeApiClient = client; serverClose = server.close;