issue参照時にもピース選択を実施 #97
This commit is contained in:
parent
919215fad3
commit
973c7df85d
@ -275,6 +275,7 @@ describe('addTask', () => {
|
|||||||
// Given: issue reference "#99"
|
// Given: issue reference "#99"
|
||||||
const issueText = 'Issue #99: Fix login timeout\n\nThe login page times out after 30 seconds.';
|
const issueText = 'Issue #99: Fix login timeout\n\nThe login page times out after 30 seconds.';
|
||||||
mockResolveIssueTask.mockReturnValue(issueText);
|
mockResolveIssueTask.mockReturnValue(issueText);
|
||||||
|
mockDeterminePiece.mockResolvedValue('default');
|
||||||
|
|
||||||
mockSummarizeTaskName.mockResolvedValue('fix-login-timeout');
|
mockSummarizeTaskName.mockResolvedValue('fix-login-timeout');
|
||||||
mockConfirm.mockResolvedValue(false);
|
mockConfirm.mockResolvedValue(false);
|
||||||
@ -288,6 +289,9 @@ describe('addTask', () => {
|
|||||||
// Then: resolveIssueTask was called
|
// Then: resolveIssueTask was called
|
||||||
expect(mockResolveIssueTask).toHaveBeenCalledWith('#99');
|
expect(mockResolveIssueTask).toHaveBeenCalledWith('#99');
|
||||||
|
|
||||||
|
// Then: determinePiece was called for piece selection
|
||||||
|
expect(mockDeterminePiece).toHaveBeenCalledWith(testDir);
|
||||||
|
|
||||||
// Then: task file created with issue text directly (no AI summarization)
|
// Then: task file created with issue text directly (no AI summarization)
|
||||||
const taskFile = path.join(testDir, '.takt', 'tasks', 'fix-login-timeout.yaml');
|
const taskFile = path.join(testDir, '.takt', 'tasks', 'fix-login-timeout.yaml');
|
||||||
expect(fs.existsSync(taskFile)).toBe(true);
|
expect(fs.existsSync(taskFile)).toBe(true);
|
||||||
@ -298,6 +302,7 @@ describe('addTask', () => {
|
|||||||
it('should proceed to worktree settings after issue fetch', async () => {
|
it('should proceed to worktree settings after issue fetch', async () => {
|
||||||
// Given: issue with worktree enabled
|
// Given: issue with worktree enabled
|
||||||
mockResolveIssueTask.mockReturnValue('Issue text');
|
mockResolveIssueTask.mockReturnValue('Issue text');
|
||||||
|
mockDeterminePiece.mockResolvedValue('default');
|
||||||
mockSummarizeTaskName.mockResolvedValue('issue-task');
|
mockSummarizeTaskName.mockResolvedValue('issue-task');
|
||||||
mockConfirm.mockResolvedValue(true);
|
mockConfirm.mockResolvedValue(true);
|
||||||
mockPromptInput
|
mockPromptInput
|
||||||
@ -331,6 +336,7 @@ describe('addTask', () => {
|
|||||||
// Given: issue reference "#99"
|
// Given: issue reference "#99"
|
||||||
const issueText = 'Issue #99: Fix login timeout';
|
const issueText = 'Issue #99: Fix login timeout';
|
||||||
mockResolveIssueTask.mockReturnValue(issueText);
|
mockResolveIssueTask.mockReturnValue(issueText);
|
||||||
|
mockDeterminePiece.mockResolvedValue('default');
|
||||||
mockSummarizeTaskName.mockResolvedValue('fix-login-timeout');
|
mockSummarizeTaskName.mockResolvedValue('fix-login-timeout');
|
||||||
mockConfirm.mockResolvedValue(false);
|
mockConfirm.mockResolvedValue(false);
|
||||||
|
|
||||||
@ -344,6 +350,42 @@ describe('addTask', () => {
|
|||||||
expect(content).toContain('issue: 99');
|
expect(content).toContain('issue: 99');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should include piece selection in task file when issue reference is used', async () => {
|
||||||
|
// Given: issue reference "#99" with non-default piece selection
|
||||||
|
const issueText = 'Issue #99: Fix login timeout';
|
||||||
|
mockResolveIssueTask.mockReturnValue(issueText);
|
||||||
|
mockDeterminePiece.mockResolvedValue('review');
|
||||||
|
mockSummarizeTaskName.mockResolvedValue('fix-login-timeout');
|
||||||
|
mockConfirm.mockResolvedValue(false);
|
||||||
|
|
||||||
|
// When
|
||||||
|
await addTask(testDir, '#99');
|
||||||
|
|
||||||
|
// Then: task file contains piece field
|
||||||
|
const taskFile = path.join(testDir, '.takt', 'tasks', 'fix-login-timeout.yaml');
|
||||||
|
expect(fs.existsSync(taskFile)).toBe(true);
|
||||||
|
const content = fs.readFileSync(taskFile, 'utf-8');
|
||||||
|
expect(content).toContain('piece: review');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cancel when piece selection returns null for issue reference', async () => {
|
||||||
|
// Given: issue fetched successfully but user cancels piece selection
|
||||||
|
const issueText = 'Issue #99: Fix login timeout';
|
||||||
|
mockResolveIssueTask.mockReturnValue(issueText);
|
||||||
|
mockDeterminePiece.mockResolvedValue(null);
|
||||||
|
|
||||||
|
// When
|
||||||
|
await addTask(testDir, '#99');
|
||||||
|
|
||||||
|
// Then: no task file created (cancelled at piece selection)
|
||||||
|
const tasksDir = path.join(testDir, '.takt', 'tasks');
|
||||||
|
const files = fs.readdirSync(tasksDir);
|
||||||
|
expect(files.length).toBe(0);
|
||||||
|
|
||||||
|
// Then: issue was fetched before cancellation
|
||||||
|
expect(mockResolveIssueTask).toHaveBeenCalledWith('#99');
|
||||||
|
});
|
||||||
|
|
||||||
describe('create_issue action', () => {
|
describe('create_issue action', () => {
|
||||||
it('should call createIssue when create_issue action is selected', async () => {
|
it('should call createIssue when create_issue action is selected', async () => {
|
||||||
// Given: interactive mode returns create_issue action
|
// Given: interactive mode returns create_issue action
|
||||||
|
|||||||
@ -106,18 +106,14 @@ export async function saveTaskFromInteractive(
|
|||||||
* add command handler
|
* add command handler
|
||||||
*
|
*
|
||||||
* Flow:
|
* Flow:
|
||||||
* 1. ピース選択
|
* A) Issue参照の場合: issue取得 → ピース選択 → ワークツリー設定 → YAML作成
|
||||||
* 2. AI対話モードでタスクを詰める
|
* B) それ以外: ピース選択 → AI対話モード → ワークツリー設定 → YAML作成
|
||||||
* 3. 会話履歴からAIがタスク要約を生成
|
|
||||||
* 4. 要約からファイル名をAIで生成
|
|
||||||
* 5. ワークツリー/ブランチ設定
|
|
||||||
* 6. YAMLファイル作成
|
|
||||||
*/
|
*/
|
||||||
export async function addTask(cwd: string, task?: string): Promise<void> {
|
export async function addTask(cwd: string, task?: string): Promise<void> {
|
||||||
const tasksDir = path.join(cwd, '.takt', 'tasks');
|
const tasksDir = path.join(cwd, '.takt', 'tasks');
|
||||||
fs.mkdirSync(tasksDir, { recursive: true });
|
fs.mkdirSync(tasksDir, { recursive: true });
|
||||||
|
|
||||||
// 1. ピース選択(Issue参照以外の場合、対話モードの前に実施)
|
// ピース選択とタスク内容の決定
|
||||||
let taskContent: string;
|
let taskContent: string;
|
||||||
let issueNumber: number | undefined;
|
let issueNumber: number | undefined;
|
||||||
let piece: string | undefined;
|
let piece: string | undefined;
|
||||||
@ -137,6 +133,14 @@ export async function addTask(cwd: string, task?: string): Promise<void> {
|
|||||||
info(`Failed to fetch issue ${task}: ${msg}`);
|
info(`Failed to fetch issue ${task}: ${msg}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ピース選択(issue取得成功後)
|
||||||
|
const pieceId = await determinePiece(cwd);
|
||||||
|
if (pieceId === null) {
|
||||||
|
info('Cancelled.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
piece = pieceId;
|
||||||
} else {
|
} else {
|
||||||
// ピース選択を先に行い、結果を対話モードに渡す
|
// ピース選択を先に行い、結果を対話モードに渡す
|
||||||
const pieceId = await determinePiece(cwd);
|
const pieceId = await determinePiece(cwd);
|
||||||
@ -165,7 +169,7 @@ export async function addTask(cwd: string, task?: string): Promise<void> {
|
|||||||
taskContent = result.task;
|
taskContent = result.task;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. ワークツリー/ブランチ設定
|
// ワークツリー/ブランチ設定
|
||||||
let worktree: boolean | string | undefined;
|
let worktree: boolean | string | undefined;
|
||||||
let branch: string | undefined;
|
let branch: string | undefined;
|
||||||
|
|
||||||
@ -180,7 +184,7 @@ export async function addTask(cwd: string, task?: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. YAMLファイル作成
|
// YAMLファイル作成
|
||||||
const filePath = await saveTaskFile(cwd, taskContent, {
|
const filePath = await saveTaskFile(cwd, taskContent, {
|
||||||
piece,
|
piece,
|
||||||
issue: issueNumber,
|
issue: issueNumber,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user