ポストエクスキューションの共通化とinstructモードの改善
- commit+push+PR作成ロジックをpostExecutionFlowに抽出し、interactive/run/watchの3ルートで共通化 - instructモードはexecuteでcommit+pushのみ(既存PRにpushで反映されるためPR作成不要) - instructのsave_taskで元ブランチ名・worktree・auto_pr:falseを固定保存(プロンプト不要) - instructの会話ループにpieceContextを渡し、/goのサマリー品質を改善 - resolveTaskExecutionのautoPrをboolean必須に変更(undefinedフォールバック廃止) - cloneデフォルトパスを../から../takt-worktree/に変更
This commit is contained in:
parent
8af8ff0943
commit
9cc6ac2ca7
@ -120,6 +120,7 @@ describe('resolveTaskExecution', () => {
|
|||||||
execCwd: '/project',
|
execCwd: '/project',
|
||||||
execPiece: 'default',
|
execPiece: 'default',
|
||||||
isWorktree: false,
|
isWorktree: false,
|
||||||
|
autoPr: false,
|
||||||
});
|
});
|
||||||
expect(mockSummarizeTaskName).not.toHaveBeenCalled();
|
expect(mockSummarizeTaskName).not.toHaveBeenCalled();
|
||||||
expect(mockCreateSharedClone).not.toHaveBeenCalled();
|
expect(mockCreateSharedClone).not.toHaveBeenCalled();
|
||||||
@ -177,6 +178,7 @@ describe('resolveTaskExecution', () => {
|
|||||||
execCwd: '/project/../20260128T0504-add-auth',
|
execCwd: '/project/../20260128T0504-add-auth',
|
||||||
execPiece: 'default',
|
execPiece: 'default',
|
||||||
isWorktree: true,
|
isWorktree: true,
|
||||||
|
autoPr: false,
|
||||||
branch: 'takt/20260128T0504-add-auth',
|
branch: 'takt/20260128T0504-add-auth',
|
||||||
baseBranch: 'main',
|
baseBranch: 'main',
|
||||||
});
|
});
|
||||||
@ -372,7 +374,7 @@ describe('resolveTaskExecution', () => {
|
|||||||
expect(result.autoPr).toBe(true);
|
expect(result.autoPr).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined autoPr when neither task nor config specifies', async () => {
|
it('should return false autoPr when neither task nor config specifies', async () => {
|
||||||
// Given: Neither task nor config has autoPr
|
// Given: Neither task nor config has autoPr
|
||||||
mockLoadGlobalConfig.mockReturnValue({
|
mockLoadGlobalConfig.mockReturnValue({
|
||||||
language: 'en',
|
language: 'en',
|
||||||
@ -393,7 +395,7 @@ describe('resolveTaskExecution', () => {
|
|||||||
const result = await resolveTaskExecution(task, '/project', 'default');
|
const result = await resolveTaskExecution(task, '/project', 'default');
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(result.autoPr).toBeUndefined();
|
expect(result.autoPr).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should prioritize task YAML auto_pr over global config', async () => {
|
it('should prioritize task YAML auto_pr over global config', async () => {
|
||||||
|
|||||||
81
src/features/tasks/execute/postExecution.ts
Normal file
81
src/features/tasks/execute/postExecution.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* Shared post-execution logic: auto-commit, push, and PR creation.
|
||||||
|
*
|
||||||
|
* Used by both selectAndExecuteTask (interactive mode) and
|
||||||
|
* instructBranch (instruct mode from takt list).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { loadGlobalConfig } from '../../../infra/config/index.js';
|
||||||
|
import { confirm } from '../../../shared/prompt/index.js';
|
||||||
|
import { autoCommitAndPush } from '../../../infra/task/index.js';
|
||||||
|
import { info, error, success } from '../../../shared/ui/index.js';
|
||||||
|
import { createLogger } from '../../../shared/utils/index.js';
|
||||||
|
import { createPullRequest, buildPrBody, pushBranch } from '../../../infra/github/index.js';
|
||||||
|
import type { GitHubIssue } from '../../../infra/github/index.js';
|
||||||
|
|
||||||
|
const log = createLogger('postExecution');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve auto-PR setting with priority: CLI option > config > prompt.
|
||||||
|
*/
|
||||||
|
export async function resolveAutoPr(optionAutoPr: boolean | undefined): Promise<boolean> {
|
||||||
|
if (typeof optionAutoPr === 'boolean') {
|
||||||
|
return optionAutoPr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const globalConfig = loadGlobalConfig();
|
||||||
|
if (typeof globalConfig.autoPr === 'boolean') {
|
||||||
|
return globalConfig.autoPr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return confirm('Create pull request?', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostExecutionOptions {
|
||||||
|
execCwd: string;
|
||||||
|
projectCwd: string;
|
||||||
|
task: string;
|
||||||
|
branch?: string;
|
||||||
|
baseBranch?: string;
|
||||||
|
shouldCreatePr: boolean;
|
||||||
|
pieceIdentifier?: string;
|
||||||
|
issues?: GitHubIssue[];
|
||||||
|
repo?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-commit, push, and optionally create a PR after successful task execution.
|
||||||
|
*/
|
||||||
|
export async function postExecutionFlow(options: PostExecutionOptions): Promise<void> {
|
||||||
|
const { execCwd, projectCwd, task, branch, baseBranch, shouldCreatePr, pieceIdentifier, issues, repo } = options;
|
||||||
|
|
||||||
|
const commitResult = autoCommitAndPush(execCwd, task, projectCwd);
|
||||||
|
if (commitResult.success && commitResult.commitHash) {
|
||||||
|
success(`Auto-committed & pushed: ${commitResult.commitHash}`);
|
||||||
|
} else if (!commitResult.success) {
|
||||||
|
error(`Auto-commit failed: ${commitResult.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commitResult.success && commitResult.commitHash && branch && shouldCreatePr) {
|
||||||
|
info('Creating pull request...');
|
||||||
|
try {
|
||||||
|
pushBranch(projectCwd, branch);
|
||||||
|
} catch (pushError) {
|
||||||
|
log.info('Branch push from project cwd failed (may already exist)', { error: pushError });
|
||||||
|
}
|
||||||
|
const report = pieceIdentifier ? `Piece \`${pieceIdentifier}\` completed successfully.` : 'Task completed successfully.';
|
||||||
|
const prBody = buildPrBody(issues, report);
|
||||||
|
const prResult = createPullRequest(projectCwd, {
|
||||||
|
branch,
|
||||||
|
title: task.length > 100 ? `${task.slice(0, 97)}...` : task,
|
||||||
|
body: prBody,
|
||||||
|
base: baseBranch,
|
||||||
|
repo,
|
||||||
|
});
|
||||||
|
if (prResult.success) {
|
||||||
|
success(`PR created: ${prResult.url}`);
|
||||||
|
} else {
|
||||||
|
error(`PR creation failed: ${prResult.error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,7 +19,7 @@ export interface ResolvedTaskExecution {
|
|||||||
baseBranch?: string;
|
baseBranch?: string;
|
||||||
startMovement?: string;
|
startMovement?: string;
|
||||||
retryNote?: string;
|
retryNote?: string;
|
||||||
autoPr?: boolean;
|
autoPr: boolean;
|
||||||
issueNumber?: number;
|
issueNumber?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ export async function resolveTaskExecution(
|
|||||||
|
|
||||||
const data = task.data;
|
const data = task.data;
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return { execCwd: defaultCwd, execPiece: defaultPiece, isWorktree: false };
|
return { execCwd: defaultCwd, execPiece: defaultPiece, isWorktree: false, autoPr: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
let execCwd = defaultCwd;
|
let execCwd = defaultCwd;
|
||||||
@ -115,7 +115,6 @@ export async function resolveTaskExecution(
|
|||||||
execCwd = result.path;
|
execCwd = result.path;
|
||||||
branch = result.branch;
|
branch = result.branch;
|
||||||
isWorktree = true;
|
isWorktree = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task.taskDir && reportDirName) {
|
if (task.taskDir && reportDirName) {
|
||||||
@ -126,25 +125,25 @@ export async function resolveTaskExecution(
|
|||||||
const startMovement = data.start_movement;
|
const startMovement = data.start_movement;
|
||||||
const retryNote = data.retry_note;
|
const retryNote = data.retry_note;
|
||||||
|
|
||||||
let autoPr: boolean | undefined;
|
let autoPr: boolean;
|
||||||
if (data.auto_pr !== undefined) {
|
if (data.auto_pr !== undefined) {
|
||||||
autoPr = data.auto_pr;
|
autoPr = data.auto_pr;
|
||||||
} else {
|
} else {
|
||||||
const globalConfig = loadGlobalConfig();
|
const globalConfig = loadGlobalConfig();
|
||||||
autoPr = globalConfig.autoPr;
|
autoPr = globalConfig.autoPr ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
execCwd,
|
execCwd,
|
||||||
execPiece,
|
execPiece,
|
||||||
isWorktree,
|
isWorktree,
|
||||||
|
autoPr,
|
||||||
...(taskPrompt ? { taskPrompt } : {}),
|
...(taskPrompt ? { taskPrompt } : {}),
|
||||||
...(reportDirName ? { reportDirName } : {}),
|
...(reportDirName ? { reportDirName } : {}),
|
||||||
...(branch ? { branch } : {}),
|
...(branch ? { branch } : {}),
|
||||||
...(baseBranch ? { baseBranch } : {}),
|
...(baseBranch ? { baseBranch } : {}),
|
||||||
...(startMovement ? { startMovement } : {}),
|
...(startMovement ? { startMovement } : {}),
|
||||||
...(retryNote ? { retryNote } : {}),
|
...(retryNote ? { retryNote } : {}),
|
||||||
...(autoPr !== undefined ? { autoPr } : {}),
|
|
||||||
...(data.issue !== undefined ? { issueNumber: data.issue } : {}),
|
...(data.issue !== undefined ? { issueNumber: data.issue } : {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,15 +9,14 @@
|
|||||||
import {
|
import {
|
||||||
listPieces,
|
listPieces,
|
||||||
isPiecePath,
|
isPiecePath,
|
||||||
loadGlobalConfig,
|
|
||||||
} from '../../../infra/config/index.js';
|
} from '../../../infra/config/index.js';
|
||||||
import { confirm } from '../../../shared/prompt/index.js';
|
import { confirm } from '../../../shared/prompt/index.js';
|
||||||
import { createSharedClone, autoCommitAndPush, summarizeTaskName, getCurrentBranch } from '../../../infra/task/index.js';
|
import { createSharedClone, summarizeTaskName, getCurrentBranch } from '../../../infra/task/index.js';
|
||||||
import { DEFAULT_PIECE_NAME } from '../../../shared/constants.js';
|
import { DEFAULT_PIECE_NAME } from '../../../shared/constants.js';
|
||||||
import { info, error, success, withProgress } from '../../../shared/ui/index.js';
|
import { info, error, withProgress } from '../../../shared/ui/index.js';
|
||||||
import { createLogger } from '../../../shared/utils/index.js';
|
import { createLogger } from '../../../shared/utils/index.js';
|
||||||
import { createPullRequest, buildPrBody, pushBranch } from '../../../infra/github/index.js';
|
|
||||||
import { executeTask } from './taskExecution.js';
|
import { executeTask } from './taskExecution.js';
|
||||||
|
import { resolveAutoPr, postExecutionFlow } from './postExecution.js';
|
||||||
import type { TaskExecutionOptions, WorktreeConfirmationResult, SelectAndExecuteOptions } from './types.js';
|
import type { TaskExecutionOptions, WorktreeConfirmationResult, SelectAndExecuteOptions } from './types.js';
|
||||||
import { selectPiece } from '../../pieceSelection/index.js';
|
import { selectPiece } from '../../pieceSelection/index.js';
|
||||||
|
|
||||||
@ -75,26 +74,6 @@ export async function confirmAndCreateWorktree(
|
|||||||
return { execCwd: result.path, isWorktree: true, branch: result.branch, baseBranch };
|
return { execCwd: result.path, isWorktree: true, branch: result.branch, baseBranch };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve auto-PR setting with priority: CLI option > config > prompt.
|
|
||||||
* Only applicable when worktree is enabled.
|
|
||||||
*/
|
|
||||||
async function resolveAutoPr(optionAutoPr: boolean | undefined): Promise<boolean> {
|
|
||||||
// CLI option takes precedence
|
|
||||||
if (typeof optionAutoPr === 'boolean') {
|
|
||||||
return optionAutoPr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check global config
|
|
||||||
const globalConfig = loadGlobalConfig();
|
|
||||||
if (typeof globalConfig.autoPr === 'boolean') {
|
|
||||||
return globalConfig.autoPr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to interactive prompt
|
|
||||||
return confirm('Create pull request?', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a task with piece selection, optional worktree, and auto-commit.
|
* Execute a task with piece selection, optional worktree, and auto-commit.
|
||||||
* Shared by direct task execution and interactive mode.
|
* Shared by direct task execution and interactive mode.
|
||||||
@ -136,36 +115,17 @@ export async function selectAndExecuteTask(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (taskSuccess && isWorktree) {
|
if (taskSuccess && isWorktree) {
|
||||||
const commitResult = autoCommitAndPush(execCwd, task, cwd);
|
await postExecutionFlow({
|
||||||
if (commitResult.success && commitResult.commitHash) {
|
execCwd,
|
||||||
success(`Auto-committed & pushed: ${commitResult.commitHash}`);
|
projectCwd: cwd,
|
||||||
} else if (!commitResult.success) {
|
task,
|
||||||
error(`Auto-commit failed: ${commitResult.message}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commitResult.success && commitResult.commitHash && branch && shouldCreatePr) {
|
|
||||||
info('Creating pull request...');
|
|
||||||
// Push branch from project cwd to origin (clone's origin is removed after shared clone)
|
|
||||||
try {
|
|
||||||
pushBranch(cwd, branch);
|
|
||||||
} catch (pushError) {
|
|
||||||
// Branch may already be pushed by autoCommitAndPush, continue to PR creation
|
|
||||||
log.info('Branch push from project cwd failed (may already exist)', { error: pushError });
|
|
||||||
}
|
|
||||||
const prBody = buildPrBody(options?.issues, `Piece \`${pieceIdentifier}\` completed successfully.`);
|
|
||||||
const prResult = createPullRequest(cwd, {
|
|
||||||
branch,
|
branch,
|
||||||
title: task.length > 100 ? `${task.slice(0, 97)}...` : task,
|
baseBranch,
|
||||||
body: prBody,
|
shouldCreatePr,
|
||||||
base: baseBranch,
|
pieceIdentifier,
|
||||||
|
issues: options?.issues,
|
||||||
repo: options?.repo,
|
repo: options?.repo,
|
||||||
});
|
});
|
||||||
if (prResult.success) {
|
|
||||||
success(`PR created: ${prResult.url}`);
|
|
||||||
} else {
|
|
||||||
error(`PR creation failed: ${prResult.error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!taskSuccess) {
|
if (!taskSuccess) {
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { loadPieceByIdentifier, isPiecePath, loadGlobalConfig } from '../../../infra/config/index.js';
|
import { loadPieceByIdentifier, isPiecePath, loadGlobalConfig } from '../../../infra/config/index.js';
|
||||||
import { TaskRunner, type TaskInfo, autoCommitAndPush } from '../../../infra/task/index.js';
|
import { TaskRunner, type TaskInfo } from '../../../infra/task/index.js';
|
||||||
import {
|
import {
|
||||||
header,
|
header,
|
||||||
info,
|
info,
|
||||||
@ -17,9 +17,10 @@ import { getLabel } from '../../../shared/i18n/index.js';
|
|||||||
import { executePiece } from './pieceExecution.js';
|
import { executePiece } from './pieceExecution.js';
|
||||||
import { DEFAULT_PIECE_NAME } from '../../../shared/constants.js';
|
import { DEFAULT_PIECE_NAME } from '../../../shared/constants.js';
|
||||||
import type { TaskExecutionOptions, ExecuteTaskOptions, PieceExecutionResult } from './types.js';
|
import type { TaskExecutionOptions, ExecuteTaskOptions, PieceExecutionResult } from './types.js';
|
||||||
import { createPullRequest, buildPrBody, pushBranch, fetchIssue, checkGhCli } from '../../../infra/github/index.js';
|
import { fetchIssue, checkGhCli } from '../../../infra/github/index.js';
|
||||||
import { runWithWorkerPool } from './parallelExecution.js';
|
import { runWithWorkerPool } from './parallelExecution.js';
|
||||||
import { resolveTaskExecution } from './resolveTask.js';
|
import { resolveTaskExecution } from './resolveTask.js';
|
||||||
|
import { postExecutionFlow } from './postExecution.js';
|
||||||
|
|
||||||
export type { TaskExecutionOptions, ExecuteTaskOptions };
|
export type { TaskExecutionOptions, ExecuteTaskOptions };
|
||||||
|
|
||||||
@ -167,37 +168,17 @@ export async function executeAndCompleteTask(
|
|||||||
const completedAt = new Date().toISOString();
|
const completedAt = new Date().toISOString();
|
||||||
|
|
||||||
if (taskSuccess && isWorktree) {
|
if (taskSuccess && isWorktree) {
|
||||||
const commitResult = autoCommitAndPush(execCwd, task.name, cwd);
|
|
||||||
if (commitResult.success && commitResult.commitHash) {
|
|
||||||
info(`Auto-committed & pushed: ${commitResult.commitHash}`);
|
|
||||||
} else if (!commitResult.success) {
|
|
||||||
error(`Auto-commit failed: ${commitResult.message}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create PR if autoPr is enabled and commit succeeded
|
|
||||||
if (commitResult.success && commitResult.commitHash && branch && autoPr) {
|
|
||||||
info('Creating pull request...');
|
|
||||||
// Push branch from project cwd to origin
|
|
||||||
try {
|
|
||||||
pushBranch(cwd, branch);
|
|
||||||
} catch (pushError) {
|
|
||||||
// Branch may already be pushed, continue to PR creation
|
|
||||||
log.info('Branch push from project cwd failed (may already exist)', { error: pushError });
|
|
||||||
}
|
|
||||||
const issues = resolveTaskIssue(issueNumber);
|
const issues = resolveTaskIssue(issueNumber);
|
||||||
const prBody = buildPrBody(issues, `Piece \`${execPiece}\` completed successfully.`);
|
await postExecutionFlow({
|
||||||
const prResult = createPullRequest(cwd, {
|
execCwd,
|
||||||
|
projectCwd: cwd,
|
||||||
|
task: task.name,
|
||||||
branch,
|
branch,
|
||||||
title: task.name.length > 100 ? `${task.name.slice(0, 97)}...` : task.name,
|
baseBranch,
|
||||||
body: prBody,
|
shouldCreatePr: autoPr,
|
||||||
base: baseBranch,
|
pieceIdentifier: execPiece,
|
||||||
|
issues,
|
||||||
});
|
});
|
||||||
if (prResult.success) {
|
|
||||||
success(`PR created: ${prResult.url}`);
|
|
||||||
} else {
|
|
||||||
error(`PR creation failed: ${prResult.error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskResult = {
|
const taskResult = {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ export {
|
|||||||
type SelectAndExecuteOptions,
|
type SelectAndExecuteOptions,
|
||||||
type WorktreeConfirmationResult,
|
type WorktreeConfirmationResult,
|
||||||
} from './execute/selectAndExecute.js';
|
} from './execute/selectAndExecute.js';
|
||||||
|
export { resolveAutoPr, postExecutionFlow, type PostExecutionOptions } from './execute/postExecution.js';
|
||||||
export { addTask, saveTaskFile, saveTaskFromInteractive, createIssueFromTask, createIssueAndSaveTask } from './add/index.js';
|
export { addTask, saveTaskFile, saveTaskFromInteractive, createIssueFromTask, createIssueAndSaveTask } from './add/index.js';
|
||||||
export { watchTasks } from './watch/index.js';
|
export { watchTasks } from './watch/index.js';
|
||||||
export {
|
export {
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import {
|
|||||||
resolveLanguage,
|
resolveLanguage,
|
||||||
buildSummaryActionOptions,
|
buildSummaryActionOptions,
|
||||||
selectSummaryAction,
|
selectSummaryAction,
|
||||||
|
type PieceContext,
|
||||||
} from '../../interactive/interactive.js';
|
} from '../../interactive/interactive.js';
|
||||||
import { loadTemplate } from '../../../shared/prompts/index.js';
|
import { loadTemplate } from '../../../shared/prompts/index.js';
|
||||||
import { getLabelObject } from '../../../shared/i18n/index.js';
|
import { getLabelObject } from '../../../shared/i18n/index.js';
|
||||||
@ -66,6 +67,7 @@ export async function runInstructMode(
|
|||||||
cwd: string,
|
cwd: string,
|
||||||
branchContext: string,
|
branchContext: string,
|
||||||
branchName: string,
|
branchName: string,
|
||||||
|
pieceContext?: PieceContext,
|
||||||
): Promise<InstructModeResult> {
|
): Promise<InstructModeResult> {
|
||||||
const globalConfig = loadGlobalConfig();
|
const globalConfig = loadGlobalConfig();
|
||||||
const lang = resolveLanguage(globalConfig.language);
|
const lang = resolveLanguage(globalConfig.language);
|
||||||
@ -113,7 +115,7 @@ export async function runInstructMode(
|
|||||||
selectAction: createSelectInstructAction(ui),
|
selectAction: createSelectInstructAction(ui),
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await runConversationLoop(cwd, ctx, strategy, undefined, undefined);
|
const result = await runConversationLoop(cwd, ctx, strategy, pieceContext, undefined);
|
||||||
|
|
||||||
if (result.action === 'cancel') {
|
if (result.action === 'cancel') {
|
||||||
return { action: 'cancel', task: '' };
|
return { action: 'cancel', task: '' };
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
autoCommitAndPush,
|
autoCommitAndPush,
|
||||||
type BranchListItem,
|
type BranchListItem,
|
||||||
} from '../../../infra/task/index.js';
|
} from '../../../infra/task/index.js';
|
||||||
|
import { loadGlobalConfig, getPieceDescription } from '../../../infra/config/index.js';
|
||||||
import { selectOption } from '../../../shared/prompt/index.js';
|
import { selectOption } from '../../../shared/prompt/index.js';
|
||||||
import { info, success, error as logError, warn, header, blankLine } from '../../../shared/ui/index.js';
|
import { info, success, error as logError, warn, header, blankLine } from '../../../shared/ui/index.js';
|
||||||
import { createLogger, getErrorMessage } from '../../../shared/utils/index.js';
|
import { createLogger, getErrorMessage } from '../../../shared/utils/index.js';
|
||||||
@ -29,6 +30,7 @@ import { runInstructMode } from './instructMode.js';
|
|||||||
import { saveTaskFile } from '../add/index.js';
|
import { saveTaskFile } from '../add/index.js';
|
||||||
import { selectPiece } from '../../pieceSelection/index.js';
|
import { selectPiece } from '../../pieceSelection/index.js';
|
||||||
import { dispatchConversationAction } from '../../interactive/actionDispatcher.js';
|
import { dispatchConversationAction } from '../../interactive/actionDispatcher.js';
|
||||||
|
import type { PieceContext } from '../../interactive/interactive.js';
|
||||||
|
|
||||||
const log = createLogger('list-tasks');
|
const log = createLogger('list-tasks');
|
||||||
|
|
||||||
@ -306,7 +308,7 @@ function getBranchContext(projectDir: string, branch: string): string {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Instruct branch: create a temp clone, give additional instructions via
|
* Instruct branch: create a temp clone, give additional instructions via
|
||||||
* interactive conversation, then auto-commit+push or save as task file.
|
* interactive conversation, then auto-commit+push+PR or save as task file.
|
||||||
*/
|
*/
|
||||||
export async function instructBranch(
|
export async function instructBranch(
|
||||||
projectDir: string,
|
projectDir: string,
|
||||||
@ -315,45 +317,43 @@ export async function instructBranch(
|
|||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const { branch } = item.info;
|
const { branch } = item.info;
|
||||||
|
|
||||||
const branchContext = getBranchContext(projectDir, branch);
|
const selectedPiece = await selectPiece(projectDir);
|
||||||
const result = await runInstructMode(projectDir, branchContext, branch);
|
|
||||||
let selectedPiece: string | null = null;
|
|
||||||
|
|
||||||
const ensurePieceSelected = async (): Promise<string | null> => {
|
|
||||||
if (selectedPiece) {
|
|
||||||
return selectedPiece;
|
|
||||||
}
|
|
||||||
selectedPiece = await selectPiece(projectDir);
|
|
||||||
if (!selectedPiece) {
|
if (!selectedPiece) {
|
||||||
info('Cancelled');
|
info('Cancelled');
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
return selectedPiece;
|
|
||||||
|
const globalConfig = loadGlobalConfig();
|
||||||
|
const pieceDesc = getPieceDescription(selectedPiece, projectDir, globalConfig.interactivePreviewMovements);
|
||||||
|
const pieceContext: PieceContext = {
|
||||||
|
name: pieceDesc.name,
|
||||||
|
description: pieceDesc.description,
|
||||||
|
pieceStructure: pieceDesc.pieceStructure,
|
||||||
|
movementPreviews: pieceDesc.movementPreviews,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const branchContext = getBranchContext(projectDir, branch);
|
||||||
|
const result = await runInstructMode(projectDir, branchContext, branch, pieceContext);
|
||||||
|
|
||||||
return dispatchConversationAction(result, {
|
return dispatchConversationAction(result, {
|
||||||
cancel: () => {
|
cancel: () => {
|
||||||
info('Cancelled');
|
info('Cancelled');
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
save_task: async ({ task }) => {
|
save_task: async ({ task }) => {
|
||||||
const piece = await ensurePieceSelected();
|
const created = await saveTaskFile(projectDir, task, {
|
||||||
if (!piece) {
|
piece: selectedPiece,
|
||||||
return false;
|
worktree: true,
|
||||||
}
|
branch,
|
||||||
const created = await saveTaskFile(projectDir, task, { piece });
|
autoPr: false,
|
||||||
|
});
|
||||||
success(`Task saved: ${created.taskName}`);
|
success(`Task saved: ${created.taskName}`);
|
||||||
info(` File: ${created.tasksFile}`);
|
info(` Branch: ${branch}`);
|
||||||
log.info('Task saved from instruct mode', { branch, piece });
|
log.info('Task saved from instruct mode', { branch, piece: selectedPiece });
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
execute: async ({ task }) => {
|
execute: async ({ task }) => {
|
||||||
const piece = await ensurePieceSelected();
|
log.info('Instructing branch via temp clone', { branch, piece: selectedPiece });
|
||||||
if (!piece) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info('Instructing branch via temp clone', { branch, piece });
|
|
||||||
info(`Running instruction on ${branch}...`);
|
info(`Running instruction on ${branch}...`);
|
||||||
|
|
||||||
const clone = createTempCloneForBranch(projectDir, branch);
|
const clone = createTempCloneForBranch(projectDir, branch);
|
||||||
@ -366,17 +366,17 @@ export async function instructBranch(
|
|||||||
const taskSuccess = await executeTask({
|
const taskSuccess = await executeTask({
|
||||||
task: fullInstruction,
|
task: fullInstruction,
|
||||||
cwd: clone.path,
|
cwd: clone.path,
|
||||||
pieceIdentifier: piece,
|
pieceIdentifier: selectedPiece,
|
||||||
projectCwd: projectDir,
|
projectCwd: projectDir,
|
||||||
agentOverrides: options,
|
agentOverrides: options,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (taskSuccess) {
|
if (taskSuccess) {
|
||||||
const commitResult = autoCommitAndPush(clone.path, item.taskSlug, projectDir);
|
const commitResult = autoCommitAndPush(clone.path, task, projectDir);
|
||||||
if (commitResult.success && commitResult.commitHash) {
|
if (commitResult.success && commitResult.commitHash) {
|
||||||
info(`Auto-committed & pushed: ${commitResult.commitHash}`);
|
success(`Auto-committed & pushed: ${commitResult.commitHash}`);
|
||||||
} else if (!commitResult.success) {
|
} else if (!commitResult.success) {
|
||||||
warn(`Auto-commit skipped: ${commitResult.message}`);
|
logError(`Auto-commit failed: ${commitResult.message}`);
|
||||||
}
|
}
|
||||||
success(`Instruction completed on ${branch}`);
|
success(`Instruction completed on ${branch}`);
|
||||||
log.info('Instruction completed', { branch });
|
log.info('Instruction completed', { branch });
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export class CloneManager {
|
|||||||
? globalConfig.worktreeDir
|
? globalConfig.worktreeDir
|
||||||
: path.resolve(projectDir, globalConfig.worktreeDir);
|
: path.resolve(projectDir, globalConfig.worktreeDir);
|
||||||
}
|
}
|
||||||
return path.join(projectDir, '..');
|
return path.join(projectDir, '..', 'takt-worktree');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resolve the clone path based on options and global config */
|
/** Resolve the clone path based on options and global config */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user