/review に instruct アクションを追加
worktree レビュー時に追加指示を出せる機能を追加。 - 「Instruct」を選択 → 指示入力 → ワークフロー選択 → 実行 → 自動コミット - selectWorkflowForInstruction() でワークフロー選択 - instructWorktree() でタスク実行と自動コミットを処理
This commit is contained in:
parent
277d490eeb
commit
42ae981b65
@ -170,9 +170,10 @@ describe('buildReviewItems', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('ReviewAction type', () => {
|
describe('ReviewAction type', () => {
|
||||||
it('should include try, merge, delete (no skip)', () => {
|
it('should include instruct, try, merge, delete (no skip)', () => {
|
||||||
const actions: ReviewAction[] = ['try', 'merge', 'delete'];
|
const actions: ReviewAction[] = ['instruct', 'try', 'merge', 'delete'];
|
||||||
expect(actions).toHaveLength(3);
|
expect(actions).toHaveLength(4);
|
||||||
|
expect(actions).toContain('instruct');
|
||||||
expect(actions).toContain('try');
|
expect(actions).toContain('try');
|
||||||
expect(actions).not.toContain('skip');
|
expect(actions).not.toContain('skip');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -14,14 +14,19 @@ import {
|
|||||||
buildReviewItems,
|
buildReviewItems,
|
||||||
type WorktreeReviewItem,
|
type WorktreeReviewItem,
|
||||||
} from '../task/worktree.js';
|
} from '../task/worktree.js';
|
||||||
import { selectOption, confirm } from '../prompt/index.js';
|
import { selectOption, confirm, promptInput } from '../prompt/index.js';
|
||||||
import { info, success, error as logError, warn } from '../utils/ui.js';
|
import { info, success, error as logError, warn } from '../utils/ui.js';
|
||||||
import { createLogger } from '../utils/debug.js';
|
import { createLogger } from '../utils/debug.js';
|
||||||
|
import { executeTask } from './taskExecution.js';
|
||||||
|
import { autoCommitWorktree } from '../task/autoCommit.js';
|
||||||
|
import { listWorkflows } from '../config/workflowLoader.js';
|
||||||
|
import { getCurrentWorkflow } from '../config/paths.js';
|
||||||
|
import { DEFAULT_WORKFLOW_NAME } from '../constants.js';
|
||||||
|
|
||||||
const log = createLogger('review-tasks');
|
const log = createLogger('review-tasks');
|
||||||
|
|
||||||
/** Actions available for a reviewed worktree */
|
/** Actions available for a reviewed worktree */
|
||||||
export type ReviewAction = 'try' | 'merge' | 'delete';
|
export type ReviewAction = 'try' | 'merge' | 'delete' | 'instruct';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a branch has already been merged into HEAD.
|
* Check if a branch has already been merged into HEAD.
|
||||||
@ -66,6 +71,7 @@ async function showDiffAndPromptAction(
|
|||||||
const action = await selectOption<ReviewAction>(
|
const action = await selectOption<ReviewAction>(
|
||||||
`Action for ${item.info.branch}:`,
|
`Action for ${item.info.branch}:`,
|
||||||
[
|
[
|
||||||
|
{ label: 'Instruct', value: 'instruct', description: 'Give additional instructions to modify this worktree' },
|
||||||
{ label: 'Try merge', value: 'try', description: 'Merge without cleanup (keep worktree & branch)' },
|
{ label: 'Try merge', value: 'try', description: 'Merge without cleanup (keep worktree & branch)' },
|
||||||
{ label: 'Merge & cleanup', value: 'merge', description: 'Merge (if needed) and remove worktree & branch' },
|
{ label: 'Merge & cleanup', value: 'merge', description: 'Merge (if needed) and remove worktree & branch' },
|
||||||
{ label: 'Delete', value: 'delete', description: 'Discard changes, remove worktree and branch' },
|
{ label: 'Delete', value: 'delete', description: 'Discard changes, remove worktree and branch' },
|
||||||
@ -176,6 +182,80 @@ export function deleteWorktreeBranch(projectDir: string, item: WorktreeReviewIte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the workflow to use for instruction.
|
||||||
|
* If multiple workflows available, prompt user to select.
|
||||||
|
*/
|
||||||
|
async function selectWorkflowForInstruction(projectDir: string): Promise<string | null> {
|
||||||
|
const availableWorkflows = listWorkflows();
|
||||||
|
const currentWorkflow = getCurrentWorkflow(projectDir);
|
||||||
|
|
||||||
|
if (availableWorkflows.length === 0) {
|
||||||
|
return DEFAULT_WORKFLOW_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableWorkflows.length === 1 && availableWorkflows[0]) {
|
||||||
|
return availableWorkflows[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple workflows: let user select
|
||||||
|
const options = availableWorkflows.map((name) => ({
|
||||||
|
label: name === currentWorkflow ? `${name} (current)` : name,
|
||||||
|
value: name,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await selectOption('Select workflow:', options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instruct worktree: give additional instructions to modify the worktree.
|
||||||
|
* Executes a task on the worktree and auto-commits if successful.
|
||||||
|
*/
|
||||||
|
export async function instructWorktree(
|
||||||
|
projectDir: string,
|
||||||
|
item: WorktreeReviewItem,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const { branch } = item.info;
|
||||||
|
const worktreePath = item.info.path;
|
||||||
|
|
||||||
|
// 1. Prompt for instruction
|
||||||
|
const instruction = await promptInput('Enter instruction');
|
||||||
|
if (!instruction) {
|
||||||
|
info('Cancelled');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Select workflow
|
||||||
|
const selectedWorkflow = await selectWorkflowForInstruction(projectDir);
|
||||||
|
if (!selectedWorkflow) {
|
||||||
|
info('Cancelled');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info('Instructing worktree', { branch, worktreePath, workflow: selectedWorkflow });
|
||||||
|
info(`Running instruction on ${branch}...`);
|
||||||
|
|
||||||
|
// 3. Execute task on worktree
|
||||||
|
const taskSuccess = await executeTask(instruction, worktreePath, selectedWorkflow, projectDir);
|
||||||
|
|
||||||
|
// 4. Auto-commit if successful
|
||||||
|
if (taskSuccess) {
|
||||||
|
const commitResult = autoCommitWorktree(worktreePath, item.taskSlug);
|
||||||
|
if (commitResult.success && commitResult.commitHash) {
|
||||||
|
info(`Auto-committed: ${commitResult.commitHash}`);
|
||||||
|
} else if (!commitResult.success) {
|
||||||
|
warn(`Auto-commit skipped: ${commitResult.message}`);
|
||||||
|
}
|
||||||
|
success(`Instruction completed on ${branch}`);
|
||||||
|
log.info('Instruction completed', { branch });
|
||||||
|
} else {
|
||||||
|
logError(`Instruction failed on ${branch}`);
|
||||||
|
log.error('Instruction failed', { branch });
|
||||||
|
}
|
||||||
|
|
||||||
|
return taskSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry point: review worktree tasks interactively.
|
* Main entry point: review worktree tasks interactively.
|
||||||
*/
|
*/
|
||||||
@ -219,6 +299,9 @@ export async function reviewTasks(cwd: string): Promise<void> {
|
|||||||
if (action === null) continue;
|
if (action === null) continue;
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
case 'instruct':
|
||||||
|
await instructWorktree(cwd, item);
|
||||||
|
break;
|
||||||
case 'try':
|
case 'try':
|
||||||
tryMergeWorktreeBranch(cwd, item);
|
tryMergeWorktreeBranch(cwd, item);
|
||||||
break;
|
break;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user