feat: --pr インタラクティブモードで create_issue 除外・save_task 時の PR ブランチ自動設定
- --pr 指定時のインタラクティブモードで create_issue を選択肢から除外 - execute アクション時に PR ブランチを fetch + checkout してから実行 - save_task アクション時に worktree/branch/autoPr を自動設定しプロンプトをスキップ - saveTaskFromInteractive に presetSettings オプションを追加 - interactiveMode に InteractiveModeOptions(excludeActions)を追加 - checkoutBranch() を git.ts に追加し steps.ts の重複コードを DRY 化
This commit is contained in:
parent
e5f296a3e0
commit
c843858f2e
@ -231,6 +231,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
'## GitHub Issue #131: Issue #131',
|
'## GitHub Issue #131: Issue #131',
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Then: selectAndExecuteTask should be called (issues are used only for initialInput, not selectOptions)
|
// Then: selectAndExecuteTask should be called (issues are used only for initialInput, not selectOptions)
|
||||||
@ -282,6 +284,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
'## GitHub Issue #131: Issue #131',
|
'## GitHub Issue #131: Issue #131',
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Then: selectAndExecuteTask should be called
|
// Then: selectAndExecuteTask should be called
|
||||||
@ -305,6 +309,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
'refactor the code',
|
'refactor the code',
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Then: no issue fetching should occur
|
// Then: no issue fetching should occur
|
||||||
@ -324,6 +330,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Then: no issue fetching should occur
|
// Then: no issue fetching should occur
|
||||||
@ -399,6 +407,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -433,6 +443,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -450,6 +462,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
'fix issue',
|
'fix issue',
|
||||||
expect.objectContaining({ taskHistory: [] }),
|
expect.objectContaining({ taskHistory: [] }),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -463,6 +477,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
'verify history',
|
'verify history',
|
||||||
expect.objectContaining({ taskHistory: [] }),
|
expect.objectContaining({ taskHistory: [] }),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -533,6 +549,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
'saved-session-123',
|
'saved-session-123',
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -556,6 +574,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -572,6 +592,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -586,6 +608,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|||||||
|
|
||||||
vi.mock('../shared/ui/index.js', () => ({
|
vi.mock('../shared/ui/index.js', () => ({
|
||||||
info: vi.fn(),
|
info: vi.fn(),
|
||||||
|
success: vi.fn(),
|
||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
withProgress: vi.fn(async (_start, _done, operation) => operation()),
|
withProgress: vi.fn(async (_start, _done, operation) => operation()),
|
||||||
}));
|
}));
|
||||||
@ -76,11 +77,13 @@ vi.mock('../features/interactive/index.js', () => ({
|
|||||||
|
|
||||||
const mockListAllTaskItems = vi.fn();
|
const mockListAllTaskItems = vi.fn();
|
||||||
const mockIsStaleRunningTask = vi.fn();
|
const mockIsStaleRunningTask = vi.fn();
|
||||||
|
const mockCheckoutBranch = vi.fn();
|
||||||
vi.mock('../infra/task/index.js', () => ({
|
vi.mock('../infra/task/index.js', () => ({
|
||||||
TaskRunner: vi.fn(() => ({
|
TaskRunner: vi.fn(() => ({
|
||||||
listAllTaskItems: mockListAllTaskItems,
|
listAllTaskItems: mockListAllTaskItems,
|
||||||
})),
|
})),
|
||||||
isStaleRunningTask: (...args: unknown[]) => mockIsStaleRunningTask(...args),
|
isStaleRunningTask: (...args: unknown[]) => mockIsStaleRunningTask(...args),
|
||||||
|
checkoutBranch: (...args: unknown[]) => mockCheckoutBranch(...args),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../infra/config/index.js', () => ({
|
vi.mock('../infra/config/index.js', () => ({
|
||||||
@ -171,6 +174,8 @@ describe('PR resolution in routing', () => {
|
|||||||
expect.stringContaining('## PR #456 Review Comments:'),
|
expect.stringContaining('## PR #456 Review Comments:'),
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{ excludeActions: ['create_issue'] },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -184,7 +189,7 @@ describe('PR resolution in routing', () => {
|
|||||||
// When
|
// When
|
||||||
await executeDefaultAction();
|
await executeDefaultAction();
|
||||||
|
|
||||||
// Then: selectAndExecuteTask is called (branch is no longer passed via selectOptions)
|
// Then: selectAndExecuteTask is called
|
||||||
expect(mockSelectAndExecuteTask).toHaveBeenCalledWith(
|
expect(mockSelectAndExecuteTask).toHaveBeenCalledWith(
|
||||||
'/test/cwd',
|
'/test/cwd',
|
||||||
'summarized task',
|
'summarized task',
|
||||||
@ -193,6 +198,20 @@ describe('PR resolution in routing', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should checkout PR branch before executing task', async () => {
|
||||||
|
// Given
|
||||||
|
mockOpts.pr = 456;
|
||||||
|
const prReview = createMockPrReview({ headRefName: 'feat/my-pr-branch' });
|
||||||
|
mockCheckCliStatus.mockReturnValue({ available: true });
|
||||||
|
mockFetchPrReviewComments.mockReturnValue(prReview);
|
||||||
|
|
||||||
|
// When
|
||||||
|
await executeDefaultAction();
|
||||||
|
|
||||||
|
// Then: checkoutBranch is called with the PR's head branch
|
||||||
|
expect(mockCheckoutBranch).toHaveBeenCalledWith('/test/cwd', 'feat/my-pr-branch');
|
||||||
|
});
|
||||||
|
|
||||||
it('should exit with error when gh CLI is unavailable', async () => {
|
it('should exit with error when gh CLI is unavailable', async () => {
|
||||||
// Given
|
// Given
|
||||||
mockOpts.pr = 456;
|
mockOpts.pr = 456;
|
||||||
@ -229,6 +248,8 @@ describe('PR resolution in routing', () => {
|
|||||||
expect.stringContaining('## PR #456 Review Comments:'),
|
expect.stringContaining('## PR #456 Review Comments:'),
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{ excludeActions: ['create_issue'] },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -5,12 +5,13 @@
|
|||||||
* pipeline mode, or interactive mode.
|
* pipeline mode, or interactive mode.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { info, error as logError, withProgress } from '../../shared/ui/index.js';
|
import { info, success, error as logError, withProgress } from '../../shared/ui/index.js';
|
||||||
import { getErrorMessage } from '../../shared/utils/index.js';
|
import { getErrorMessage } from '../../shared/utils/index.js';
|
||||||
import { getLabel } from '../../shared/i18n/index.js';
|
import { getLabel } from '../../shared/i18n/index.js';
|
||||||
import { formatIssueAsTask, parseIssueNumbers, formatPrReviewAsTask } from '../../infra/github/index.js';
|
import { formatIssueAsTask, parseIssueNumbers, formatPrReviewAsTask } from '../../infra/github/index.js';
|
||||||
import { getGitProvider } from '../../infra/git/index.js';
|
import { getGitProvider } from '../../infra/git/index.js';
|
||||||
import type { PrReviewData } from '../../infra/git/index.js';
|
import type { PrReviewData } from '../../infra/git/index.js';
|
||||||
|
import { checkoutBranch } from '../../infra/task/index.js';
|
||||||
import { selectAndExecuteTask, determinePiece, saveTaskFromInteractive, createIssueAndSaveTask, promptLabelSelection, type SelectAndExecuteOptions } from '../../features/tasks/index.js';
|
import { selectAndExecuteTask, determinePiece, saveTaskFromInteractive, createIssueAndSaveTask, promptLabelSelection, type SelectAndExecuteOptions } from '../../features/tasks/index.js';
|
||||||
import { executePipeline } from '../../features/pipeline/index.js';
|
import { executePipeline } from '../../features/pipeline/index.js';
|
||||||
import {
|
import {
|
||||||
@ -85,7 +86,7 @@ async function resolveIssueInput(
|
|||||||
*/
|
*/
|
||||||
async function resolvePrInput(
|
async function resolvePrInput(
|
||||||
prNumber: number,
|
prNumber: number,
|
||||||
): Promise<{ initialInput: string }> {
|
): Promise<{ initialInput: string; prBranch: string }> {
|
||||||
const ghStatus = getGitProvider().checkCliStatus();
|
const ghStatus = getGitProvider().checkCliStatus();
|
||||||
if (!ghStatus.available) {
|
if (!ghStatus.available) {
|
||||||
throw new Error(ghStatus.error);
|
throw new Error(ghStatus.error);
|
||||||
@ -97,7 +98,7 @@ async function resolvePrInput(
|
|||||||
async () => getGitProvider().fetchPrReviewComments(prNumber),
|
async () => getGitProvider().fetchPrReviewComments(prNumber),
|
||||||
);
|
);
|
||||||
|
|
||||||
return { initialInput: formatPrReviewAsTask(prReview) };
|
return { initialInput: formatPrReviewAsTask(prReview), prBranch: prReview.headRefName };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,11 +170,13 @@ export async function executeDefaultAction(task?: string): Promise<void> {
|
|||||||
|
|
||||||
// Resolve PR review comments (--pr N) before interactive mode
|
// Resolve PR review comments (--pr N) before interactive mode
|
||||||
let initialInput: string | undefined = task;
|
let initialInput: string | undefined = task;
|
||||||
|
let prBranch: string | undefined;
|
||||||
|
|
||||||
if (prNumber) {
|
if (prNumber) {
|
||||||
try {
|
try {
|
||||||
const prResult = await resolvePrInput(prNumber);
|
const prResult = await resolvePrInput(prNumber);
|
||||||
initialInput = prResult.initialInput;
|
initialInput = prResult.initialInput;
|
||||||
|
prBranch = prResult.prBranch;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(getErrorMessage(e));
|
logError(getErrorMessage(e));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@ -234,7 +237,8 @@ export async function executeDefaultAction(task?: string): Promise<void> {
|
|||||||
info(getLabel('interactive.continueNoSession', lang));
|
info(getLabel('interactive.continueNoSession', lang));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = await interactiveMode(resolvedCwd, initialInput, pieceContext, selectedSessionId);
|
const interactiveOpts = prBranch ? { excludeActions: ['create_issue'] as const } : undefined;
|
||||||
|
result = await interactiveMode(resolvedCwd, initialInput, pieceContext, selectedSessionId, undefined, interactiveOpts);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,6 +263,11 @@ export async function executeDefaultAction(task?: string): Promise<void> {
|
|||||||
|
|
||||||
await dispatchConversationAction(result, {
|
await dispatchConversationAction(result, {
|
||||||
execute: async ({ task: confirmedTask }) => {
|
execute: async ({ task: confirmedTask }) => {
|
||||||
|
if (prBranch) {
|
||||||
|
info(`Fetching and checking out PR branch: ${prBranch}`);
|
||||||
|
checkoutBranch(resolvedCwd, prBranch);
|
||||||
|
success(`Checked out PR branch: ${prBranch}`);
|
||||||
|
}
|
||||||
selectOptions.interactiveUserInput = true;
|
selectOptions.interactiveUserInput = true;
|
||||||
selectOptions.piece = pieceId;
|
selectOptions.piece = pieceId;
|
||||||
selectOptions.interactiveMetadata = { confirmed: true, task: confirmedTask };
|
selectOptions.interactiveMetadata = { confirmed: true, task: confirmedTask };
|
||||||
@ -273,7 +282,10 @@ export async function executeDefaultAction(task?: string): Promise<void> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
save_task: async ({ task: confirmedTask }) => {
|
save_task: async ({ task: confirmedTask }) => {
|
||||||
await saveTaskFromInteractive(resolvedCwd, confirmedTask, pieceId);
|
const presetSettings = prBranch
|
||||||
|
? { worktree: true as const, branch: prBranch, autoPr: false }
|
||||||
|
: undefined;
|
||||||
|
await saveTaskFromInteractive(resolvedCwd, confirmedTask, pieceId, { presetSettings });
|
||||||
},
|
},
|
||||||
cancel: () => undefined,
|
cancel: () => undefined,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -14,6 +14,7 @@ export {
|
|||||||
type TaskHistorySummaryItem,
|
type TaskHistorySummaryItem,
|
||||||
type InteractiveModeResult,
|
type InteractiveModeResult,
|
||||||
type InteractiveModeAction,
|
type InteractiveModeAction,
|
||||||
|
type InteractiveModeOptions,
|
||||||
} from './interactive.js';
|
} from './interactive.js';
|
||||||
|
|
||||||
export { selectInteractiveMode } from './modeSelection.js';
|
export { selectInteractiveMode } from './modeSelection.js';
|
||||||
|
|||||||
@ -25,6 +25,10 @@ import {
|
|||||||
type PieceContext,
|
type PieceContext,
|
||||||
formatMovementPreviews,
|
formatMovementPreviews,
|
||||||
type InteractiveModeAction,
|
type InteractiveModeAction,
|
||||||
|
type SummaryActionValue,
|
||||||
|
type PostSummaryAction,
|
||||||
|
buildSummaryActionOptions,
|
||||||
|
selectSummaryAction,
|
||||||
} from './interactive-summary.js';
|
} from './interactive-summary.js';
|
||||||
import { type RunSessionContext, formatRunSessionForPrompt } from './runSessionReader.js';
|
import { type RunSessionContext, formatRunSessionForPrompt } from './runSessionReader.js';
|
||||||
|
|
||||||
@ -113,12 +117,18 @@ export {
|
|||||||
* /cancel → exits without executing
|
* /cancel → exits without executing
|
||||||
* Ctrl+D → exits without executing
|
* Ctrl+D → exits without executing
|
||||||
*/
|
*/
|
||||||
|
export interface InteractiveModeOptions {
|
||||||
|
/** Actions to exclude from the post-summary action selector. */
|
||||||
|
excludeActions?: readonly SummaryActionValue[];
|
||||||
|
}
|
||||||
|
|
||||||
export async function interactiveMode(
|
export async function interactiveMode(
|
||||||
cwd: string,
|
cwd: string,
|
||||||
initialInput?: string,
|
initialInput?: string,
|
||||||
pieceContext?: PieceContext,
|
pieceContext?: PieceContext,
|
||||||
sessionId?: string,
|
sessionId?: string,
|
||||||
runSessionContext?: RunSessionContext,
|
runSessionContext?: RunSessionContext,
|
||||||
|
options?: InteractiveModeOptions,
|
||||||
): Promise<InteractiveModeResult> {
|
): Promise<InteractiveModeResult> {
|
||||||
const baseCtx = initializeSession(cwd, 'interactive');
|
const baseCtx = initializeSession(cwd, 'interactive');
|
||||||
const ctx = sessionId ? { ...baseCtx, sessionId } : baseCtx;
|
const ctx = sessionId ? { ...baseCtx, sessionId } : baseCtx;
|
||||||
@ -155,11 +165,32 @@ export async function interactiveMode(
|
|||||||
return `## Policy\n${policyIntro}\n\n${policyContent}\n\n---\n\n${userMessage}\n\n---\n**Policy Reminder:** ${reminderLabel}`;
|
return `## Policy\n${policyIntro}\n\n${policyContent}\n\n---\n\n${userMessage}\n\n---\n**Policy Reminder:** ${reminderLabel}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const excludeActions = options?.excludeActions;
|
||||||
|
const selectAction = excludeActions?.length
|
||||||
|
? (task: string): Promise<PostSummaryAction | null> =>
|
||||||
|
selectSummaryAction(
|
||||||
|
task,
|
||||||
|
ui.proposed,
|
||||||
|
ui.actionPrompt,
|
||||||
|
buildSummaryActionOptions(
|
||||||
|
{
|
||||||
|
execute: ui.actions.execute,
|
||||||
|
createIssue: ui.actions.createIssue,
|
||||||
|
saveTask: ui.actions.saveTask,
|
||||||
|
continue: ui.actions.continue,
|
||||||
|
},
|
||||||
|
['create_issue'],
|
||||||
|
excludeActions,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return runConversationLoop(cwd, ctx, {
|
return runConversationLoop(cwd, ctx, {
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
allowedTools: DEFAULT_INTERACTIVE_TOOLS,
|
allowedTools: DEFAULT_INTERACTIVE_TOOLS,
|
||||||
transformPrompt: injectPolicy,
|
transformPrompt: injectPolicy,
|
||||||
introMessage: ui.intro,
|
introMessage: ui.intro,
|
||||||
|
selectAction,
|
||||||
}, pieceContext, initialInput);
|
}, pieceContext, initialInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { formatIssueAsTask, buildPrBody, formatPrReviewAsTask } from '../../infra/github/index.js';
|
import { formatIssueAsTask, buildPrBody, formatPrReviewAsTask } from '../../infra/github/index.js';
|
||||||
import { getGitProvider, type Issue } from '../../infra/git/index.js';
|
import { getGitProvider, type Issue } from '../../infra/git/index.js';
|
||||||
import { stageAndCommit, resolveBaseBranch, pushBranch } from '../../infra/task/index.js';
|
import { stageAndCommit, resolveBaseBranch, pushBranch, checkoutBranch } from '../../infra/task/index.js';
|
||||||
import { executeTask, confirmAndCreateWorktree, type TaskExecutionOptions, type PipelineExecutionOptions } from '../tasks/index.js';
|
import { executeTask, confirmAndCreateWorktree, type TaskExecutionOptions, type PipelineExecutionOptions } from '../tasks/index.js';
|
||||||
import { info, error, success } from '../../shared/ui/index.js';
|
import { info, error, success } from '../../shared/ui/index.js';
|
||||||
import { getErrorMessage } from '../../shared/utils/index.js';
|
import { getErrorMessage } from '../../shared/utils/index.js';
|
||||||
@ -150,8 +150,7 @@ export async function resolveExecutionContext(
|
|||||||
}
|
}
|
||||||
if (prBranch) {
|
if (prBranch) {
|
||||||
info(`Fetching and checking out PR branch: ${prBranch}`);
|
info(`Fetching and checking out PR branch: ${prBranch}`);
|
||||||
execFileSync('git', ['fetch', 'origin', prBranch], { cwd, stdio: 'pipe' });
|
checkoutBranch(cwd, prBranch);
|
||||||
execFileSync('git', ['checkout', prBranch], { cwd, stdio: 'pipe' });
|
|
||||||
success(`Checked out PR branch: ${prBranch}`);
|
success(`Checked out PR branch: ${prBranch}`);
|
||||||
return { execCwd: cwd, branch: prBranch, baseBranch: resolveBaseBranch(cwd).branch, isWorktree: false };
|
return { execCwd: cwd, branch: prBranch, baseBranch: resolveBaseBranch(cwd).branch, isWorktree: false };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -142,12 +142,13 @@ async function promptWorktreeSettings(): Promise<WorktreeSettings> {
|
|||||||
/**
|
/**
|
||||||
* Save a task from interactive mode result.
|
* Save a task from interactive mode result.
|
||||||
* Prompts for worktree/branch/auto_pr settings before saving.
|
* Prompts for worktree/branch/auto_pr settings before saving.
|
||||||
|
* If presetSettings is provided, skips the prompt and uses those settings directly.
|
||||||
*/
|
*/
|
||||||
export async function saveTaskFromInteractive(
|
export async function saveTaskFromInteractive(
|
||||||
cwd: string,
|
cwd: string,
|
||||||
task: string,
|
task: string,
|
||||||
piece?: string,
|
piece?: string,
|
||||||
options?: { issue?: number; confirmAtEndMessage?: string },
|
options?: { issue?: number; confirmAtEndMessage?: string; presetSettings?: WorktreeSettings },
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (options?.confirmAtEndMessage) {
|
if (options?.confirmAtEndMessage) {
|
||||||
const approved = await confirm(options.confirmAtEndMessage, true);
|
const approved = await confirm(options.confirmAtEndMessage, true);
|
||||||
@ -155,7 +156,7 @@ export async function saveTaskFromInteractive(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const settings = await promptWorktreeSettings();
|
const settings = options?.presetSettings ?? await promptWorktreeSettings();
|
||||||
const created = await saveTaskFile(cwd, task, { piece, issue: options?.issue, ...settings });
|
const created = await saveTaskFile(cwd, task, { piece, issue: options?.issue, ...settings });
|
||||||
displayTaskCreationResult(created, settings, piece);
|
displayTaskCreationResult(created, settings, piece);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,15 @@ export function stageAndCommit(cwd: string, message: string): string | undefined
|
|||||||
}).trim();
|
}).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches and checks out a branch from origin. Throws on failure.
|
||||||
|
*/
|
||||||
|
export function checkoutBranch(cwd: string, branch: string): void {
|
||||||
|
log.info('Checking out branch from origin', { branch });
|
||||||
|
execFileSync('git', ['fetch', 'origin', branch], { cwd, stdio: 'pipe' });
|
||||||
|
execFileSync('git', ['checkout', branch], { cwd, stdio: 'pipe' });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throws on failure.
|
* Throws on failure.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -55,7 +55,7 @@ export {
|
|||||||
getOriginalInstruction,
|
getOriginalInstruction,
|
||||||
buildListItems,
|
buildListItems,
|
||||||
} from './branchList.js';
|
} from './branchList.js';
|
||||||
export { stageAndCommit, getCurrentBranch, pushBranch } from './git.js';
|
export { stageAndCommit, getCurrentBranch, pushBranch, checkoutBranch } from './git.js';
|
||||||
export { autoCommitAndPush, type AutoCommitResult } from './autoCommit.js';
|
export { autoCommitAndPush, type AutoCommitResult } from './autoCommit.js';
|
||||||
export { summarizeTaskName } from './summarize.js';
|
export { summarizeTaskName } from './summarize.js';
|
||||||
export { TaskWatcher, type TaskWatcherOptions } from './watcher.js';
|
export { TaskWatcher, type TaskWatcherOptions } from './watcher.js';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user