会話内容からタスク要件をまとめる必要がなくなったので削除

This commit is contained in:
nrslib 2026-02-03 19:18:51 +09:00
parent fabf4bcd27
commit 184a1d756a
6 changed files with 8 additions and 111 deletions

View File

@ -69,45 +69,33 @@ vi.mock('../infra/github/issue.js', () => ({
})); }));
import { interactiveMode } from '../features/interactive/index.js'; import { interactiveMode } from '../features/interactive/index.js';
import { getProvider } from '../infra/providers/index.js';
import { promptInput, confirm } from '../shared/prompt/index.js'; import { promptInput, confirm } from '../shared/prompt/index.js';
import { summarizeTaskName } from '../infra/task/summarize.js'; import { summarizeTaskName } from '../infra/task/summarize.js';
import { determineWorkflow } from '../features/tasks/execute/selectAndExecute.js'; import { determineWorkflow } from '../features/tasks/execute/selectAndExecute.js';
import { getWorkflowDescription } from '../infra/config/loaders/workflowResolver.js'; import { getWorkflowDescription } from '../infra/config/loaders/workflowResolver.js';
import { resolveIssueTask } from '../infra/github/issue.js'; import { resolveIssueTask } from '../infra/github/issue.js';
import { addTask, summarizeConversation } from '../features/tasks/index.js'; import { addTask } from '../features/tasks/index.js';
const mockResolveIssueTask = vi.mocked(resolveIssueTask); const mockResolveIssueTask = vi.mocked(resolveIssueTask);
const mockInteractiveMode = vi.mocked(interactiveMode); const mockInteractiveMode = vi.mocked(interactiveMode);
const mockGetProvider = vi.mocked(getProvider);
const mockPromptInput = vi.mocked(promptInput); const mockPromptInput = vi.mocked(promptInput);
const mockConfirm = vi.mocked(confirm); const mockConfirm = vi.mocked(confirm);
const mockSummarizeTaskName = vi.mocked(summarizeTaskName); const mockSummarizeTaskName = vi.mocked(summarizeTaskName);
const mockDetermineWorkflow = vi.mocked(determineWorkflow); const mockDetermineWorkflow = vi.mocked(determineWorkflow);
const mockGetWorkflowDescription = vi.mocked(getWorkflowDescription); const mockGetWorkflowDescription = vi.mocked(getWorkflowDescription);
/** Helper: set up mocks for the full happy path */
function setupFullFlowMocks(overrides?: { function setupFullFlowMocks(overrides?: {
conversationTask?: string; task?: string;
summaryContent?: string;
slug?: string; slug?: string;
}) { }) {
const task = overrides?.conversationTask ?? 'User: 認証機能を追加したい\n\nAssistant: 了解です。'; const task = overrides?.task ?? '# 認証機能追加\nJWT認証を実装する';
const summary = overrides?.summaryContent ?? '# 認証機能追加\nJWT認証を実装する';
const slug = overrides?.slug ?? 'add-auth'; const slug = overrides?.slug ?? 'add-auth';
mockDetermineWorkflow.mockResolvedValue('default'); mockDetermineWorkflow.mockResolvedValue('default');
mockGetWorkflowDescription.mockReturnValue({ name: 'default', description: '' }); mockGetWorkflowDescription.mockReturnValue({ name: 'default', description: '' });
mockInteractiveMode.mockResolvedValue({ confirmed: true, task }); mockInteractiveMode.mockResolvedValue({ confirmed: true, task });
const mockProviderCall = vi.fn().mockResolvedValue({ content: summary });
mockGetProvider.mockReturnValue({ call: mockProviderCall } as any);
mockSummarizeTaskName.mockResolvedValue(slug); mockSummarizeTaskName.mockResolvedValue(slug);
mockConfirm.mockResolvedValue(false); mockConfirm.mockResolvedValue(false);
return { mockProviderCall };
} }
let testDir: string; let testDir: string;
@ -135,11 +123,9 @@ describe('addTask', () => {
// When // When
await addTask(testDir); await addTask(testDir);
// Then: no task file created
const tasksDir = path.join(testDir, '.takt', 'tasks'); const tasksDir = path.join(testDir, '.takt', 'tasks');
const files = fs.existsSync(tasksDir) ? fs.readdirSync(tasksDir) : []; const files = fs.existsSync(tasksDir) ? fs.readdirSync(tasksDir) : [];
expect(files.length).toBe(0); expect(files.length).toBe(0);
expect(mockGetProvider).not.toHaveBeenCalled();
expect(mockSummarizeTaskName).not.toHaveBeenCalled(); expect(mockSummarizeTaskName).not.toHaveBeenCalled();
}); });
@ -160,38 +146,14 @@ describe('addTask', () => {
expect(content).toContain('JWT認証を実装する'); expect(content).toContain('JWT認証を実装する');
}); });
it('should summarize conversation via provider.call', async () => { it('should use first line of task for filename generation', async () => {
// Given
const { mockProviderCall } = setupFullFlowMocks({
conversationTask: 'User: バグ修正して\n\nAssistant: どのバグですか?',
});
// When
await addTask(testDir);
// Then: provider.call was called with conversation text
expect(mockProviderCall).toHaveBeenCalledWith(
'task-summarizer',
'User: バグ修正して\n\nAssistant: どのバグですか?',
expect.objectContaining({
cwd: testDir,
maxTurns: 1,
allowedTools: [],
}),
);
});
it('should use first line of summary for filename generation', async () => {
// Given: summary with multiple lines
setupFullFlowMocks({ setupFullFlowMocks({
summaryContent: 'First line summary\nSecond line details', task: 'First line summary\nSecond line details',
slug: 'first-line', slug: 'first-line',
}); });
// When
await addTask(testDir); await addTask(testDir);
// Then: summarizeTaskName receives only the first line
expect(mockSummarizeTaskName).toHaveBeenCalledWith('First line summary', { cwd: testDir }); expect(mockSummarizeTaskName).toHaveBeenCalledWith('First line summary', { cwd: testDir });
}); });
@ -354,11 +316,9 @@ describe('addTask', () => {
// When // When
await addTask(testDir, '#99'); await addTask(testDir, '#99');
// Then: no task file created, no crash
const tasksDir = path.join(testDir, '.takt', 'tasks'); const tasksDir = path.join(testDir, '.takt', 'tasks');
const files = fs.readdirSync(tasksDir); const files = fs.readdirSync(tasksDir);
expect(files.length).toBe(0); expect(files.length).toBe(0);
expect(mockGetProvider).not.toHaveBeenCalled();
}); });
it('should include issue number in task file when issue reference is used', async () => { it('should include issue number in task file when issue reference is used', async () => {
@ -378,27 +338,3 @@ describe('addTask', () => {
expect(content).toContain('issue: 99'); expect(content).toContain('issue: 99');
}); });
}); });
describe('summarizeConversation', () => {
it('should call provider with summarize system prompt', async () => {
// Given
const mockCall = vi.fn().mockResolvedValue({ content: 'Summary text' });
mockGetProvider.mockReturnValue({ call: mockCall } as any);
// When
const result = await summarizeConversation('/project', 'conversation text');
// Then
expect(result).toBe('Summary text');
expect(mockCall).toHaveBeenCalledWith(
'task-summarizer',
'conversation text',
expect.objectContaining({
cwd: '/project',
maxTurns: 1,
allowedTools: [],
systemPrompt: expect.stringContaining('会話履歴からタスクの要件をまとめてください'),
}),
);
});
});

View File

@ -105,7 +105,6 @@ describe('YAML content integrity', () => {
expect(() => getPrompt('interactive.noTranscript', 'en')).not.toThrow(); expect(() => getPrompt('interactive.noTranscript', 'en')).not.toThrow();
expect(() => getPromptObject('interactive.ui', 'en')).not.toThrow(); expect(() => getPromptObject('interactive.ui', 'en')).not.toThrow();
expect(() => getPrompt('summarize.slugGenerator')).not.toThrow(); expect(() => getPrompt('summarize.slugGenerator')).not.toThrow();
expect(() => getPrompt('summarize.conversationSummarizer')).not.toThrow();
expect(() => getPrompt('claude.agentDefault')).not.toThrow(); expect(() => getPrompt('claude.agentDefault')).not.toThrow();
expect(() => getPrompt('claude.judgePrompt')).not.toThrow(); expect(() => getPrompt('claude.judgePrompt')).not.toThrow();
expect(() => getPromptObject('instruction.metadata', 'en')).not.toThrow(); expect(() => getPromptObject('instruction.metadata', 'en')).not.toThrow();
@ -126,7 +125,6 @@ describe('YAML content integrity', () => {
expect(() => getPrompt('interactive.noTranscript', 'ja')).not.toThrow(); expect(() => getPrompt('interactive.noTranscript', 'ja')).not.toThrow();
expect(() => getPromptObject('interactive.ui', 'ja')).not.toThrow(); expect(() => getPromptObject('interactive.ui', 'ja')).not.toThrow();
expect(() => getPrompt('summarize.slugGenerator', 'ja')).not.toThrow(); expect(() => getPrompt('summarize.slugGenerator', 'ja')).not.toThrow();
expect(() => getPrompt('summarize.conversationSummarizer', 'ja')).not.toThrow();
expect(() => getPrompt('claude.agentDefault', 'ja')).not.toThrow(); expect(() => getPrompt('claude.agentDefault', 'ja')).not.toThrow();
expect(() => getPrompt('claude.judgePrompt', 'ja')).not.toThrow(); expect(() => getPrompt('claude.judgePrompt', 'ja')).not.toThrow();
expect(() => getPromptObject('instruction.metadata', 'ja')).not.toThrow(); expect(() => getPromptObject('instruction.metadata', 'ja')).not.toThrow();

View File

@ -13,37 +13,12 @@ import { success, info } from '../../../shared/ui/index.js';
import { summarizeTaskName, type TaskFileData } from '../../../infra/task/index.js'; import { summarizeTaskName, type TaskFileData } from '../../../infra/task/index.js';
import { loadGlobalConfig, getWorkflowDescription } from '../../../infra/config/index.js'; import { loadGlobalConfig, getWorkflowDescription } from '../../../infra/config/index.js';
import { determineWorkflow } from '../execute/selectAndExecute.js'; import { determineWorkflow } from '../execute/selectAndExecute.js';
import { getProvider, type ProviderType } from '../../../infra/providers/index.js';
import { createLogger, getErrorMessage } from '../../../shared/utils/index.js'; import { createLogger, getErrorMessage } from '../../../shared/utils/index.js';
import { getPrompt } from '../../../shared/prompts/index.js';
import { isIssueReference, resolveIssueTask, parseIssueNumbers } from '../../../infra/github/index.js'; import { isIssueReference, resolveIssueTask, parseIssueNumbers } from '../../../infra/github/index.js';
import { interactiveMode } from '../../interactive/index.js'; import { interactiveMode } from '../../interactive/index.js';
const log = createLogger('add-task'); const log = createLogger('add-task');
/**
* Summarize conversation history into a task description using AI.
*/
export async function summarizeConversation(cwd: string, conversationText: string): Promise<string> {
const globalConfig = loadGlobalConfig();
const providerType = (globalConfig.provider as ProviderType) ?? 'claude';
const provider = getProvider(providerType);
info('Summarizing task from conversation...');
const response = await provider.call('task-summarizer', conversationText, {
cwd,
maxTurns: 1,
allowedTools: [],
systemPrompt: getPrompt('summarize.conversationSummarizer'),
});
return response.content;
}
/**
* Generate a unique task filename with AI-summarized slug
*/
async function generateFilename(tasksDir: string, taskContent: string, cwd: string): Promise<string> { async function generateFilename(tasksDir: string, taskContent: string, cwd: string): Promise<string> {
info('Generating task filename...'); info('Generating task filename...');
const slug = await summarizeTaskName(taskContent, { cwd }); const slug = await summarizeTaskName(taskContent, { cwd });
@ -112,8 +87,8 @@ export async function addTask(cwd: string, task?: string): Promise<void> {
return; return;
} }
// 会話履歴からタスク要約を生成 // interactiveMode already returns a summarized task from conversation
taskContent = await summarizeConversation(cwd, result.task); taskContent = result.task;
} }
// 3. 要約からファイル名生成 // 3. 要約からファイル名生成

View File

@ -14,7 +14,7 @@ export {
type SelectAndExecuteOptions, type SelectAndExecuteOptions,
type WorktreeConfirmationResult, type WorktreeConfirmationResult,
} from './execute/selectAndExecute.js'; } from './execute/selectAndExecute.js';
export { addTask, summarizeConversation } from './add/index.js'; export { addTask } from './add/index.js';
export { watchTasks } from './watch/index.js'; export { watchTasks } from './watch/index.js';
export { export {
listTasks, listTasks,

View File

@ -96,12 +96,6 @@ summarize:
worktreeを作るときブランチ名をAIで生成 → ai-branch-naming worktreeを作るときブランチ名をAIで生成 → ai-branch-naming
レビュー画面に元の指示を表示する → show-original-instruction レビュー画面に元の指示を表示する → show-original-instruction
conversationSummarizer: |
会話履歴からタスクの要件をまとめてください。
タスク実行エージェントへの指示として使われます。
具体的・簡潔に、必要な情報をすべて含めてください。
マークダウン形式で出力してください。
# ===== Claude Client ===== # ===== Claude Client =====
claude: claude:
agentDefault: "You are the {agentName} agent. Follow the standard {agentName} workflow." agentDefault: "You are the {agentName} agent. Follow the standard {agentName} workflow."

View File

@ -109,12 +109,6 @@ summarize:
worktreeを作るときブランチ名をAIで生成 → ai-branch-naming worktreeを作るときブランチ名をAIで生成 → ai-branch-naming
レビュー画面に元の指示を表示する → show-original-instruction レビュー画面に元の指示を表示する → show-original-instruction
conversationSummarizer: |
会話履歴からタスクの要件をまとめてください。
タスク実行エージェントへの指示として使われます。
具体的・簡潔に、必要な情報をすべて含めてください。
マークダウン形式で出力してください。
# ===== Claude Client ===== # ===== Claude Client =====
claude: claude:
agentDefault: "You are the {agentName} agent. Follow the standard {agentName} workflow." agentDefault: "You are the {agentName} agent. Follow the standard {agentName} workflow."