takt: add-draft-pr-option (#323)
This commit is contained in:
parent
5960a0d212
commit
4f8255d509
@ -42,6 +42,15 @@ describe('config env overrides', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('TAKT_DRAFT_PR が draft_pr に反映される', () => {
|
||||||
|
process.env.TAKT_DRAFT_PR = 'true';
|
||||||
|
|
||||||
|
const raw: Record<string, unknown> = {};
|
||||||
|
applyGlobalConfigEnvOverrides(raw);
|
||||||
|
|
||||||
|
expect(raw.draft_pr).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('should apply project env overrides from generated env names', () => {
|
it('should apply project env overrides from generated env names', () => {
|
||||||
process.env.TAKT_VERBOSE = 'true';
|
process.env.TAKT_VERBOSE = 'true';
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ vi.mock('../shared/utils/index.js', async (importOriginal) => ({
|
|||||||
getErrorMessage: (e: unknown) => String(e),
|
getErrorMessage: (e: unknown) => String(e),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { buildPrBody, findExistingPr } from '../infra/github/pr.js';
|
import { buildPrBody, findExistingPr, createPullRequest } from '../infra/github/pr.js';
|
||||||
import type { GitHubIssue } from '../infra/github/types.js';
|
import type { GitHubIssue } from '../infra/github/types.js';
|
||||||
|
|
||||||
describe('findExistingPr', () => {
|
describe('findExistingPr', () => {
|
||||||
@ -59,6 +59,53 @@ describe('findExistingPr', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('createPullRequest', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('draft: true の場合、args に --draft が含まれる', () => {
|
||||||
|
mockExecFileSync.mockReturnValue('https://github.com/org/repo/pull/1\n');
|
||||||
|
|
||||||
|
createPullRequest('/project', {
|
||||||
|
branch: 'feat/my-branch',
|
||||||
|
title: 'My PR',
|
||||||
|
body: 'PR body',
|
||||||
|
draft: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const call = mockExecFileSync.mock.calls[0];
|
||||||
|
expect(call[1]).toContain('--draft');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('draft: false の場合、args に --draft が含まれない', () => {
|
||||||
|
mockExecFileSync.mockReturnValue('https://github.com/org/repo/pull/2\n');
|
||||||
|
|
||||||
|
createPullRequest('/project', {
|
||||||
|
branch: 'feat/my-branch',
|
||||||
|
title: 'My PR',
|
||||||
|
body: 'PR body',
|
||||||
|
draft: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const call = mockExecFileSync.mock.calls[0];
|
||||||
|
expect(call[1]).not.toContain('--draft');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('draft が未指定の場合、args に --draft が含まれない', () => {
|
||||||
|
mockExecFileSync.mockReturnValue('https://github.com/org/repo/pull/3\n');
|
||||||
|
|
||||||
|
createPullRequest('/project', {
|
||||||
|
branch: 'feat/my-branch',
|
||||||
|
title: 'My PR',
|
||||||
|
body: 'PR body',
|
||||||
|
});
|
||||||
|
|
||||||
|
const call = mockExecFileSync.mock.calls[0];
|
||||||
|
expect(call[1]).not.toContain('--draft');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('buildPrBody', () => {
|
describe('buildPrBody', () => {
|
||||||
it('should build body with single issue and report', () => {
|
it('should build body with single issue and report', () => {
|
||||||
const issue: GitHubIssue = {
|
const issue: GitHubIssue = {
|
||||||
|
|||||||
@ -218,6 +218,46 @@ describe('executePipeline', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('draftPr: true の場合、createPullRequest に draft: true が渡される', async () => {
|
||||||
|
mockExecuteTask.mockResolvedValueOnce(true);
|
||||||
|
mockCreatePullRequest.mockReturnValueOnce({ success: true, url: 'https://github.com/test/pr/1' });
|
||||||
|
|
||||||
|
const exitCode = await executePipeline({
|
||||||
|
task: 'Fix the bug',
|
||||||
|
piece: 'default',
|
||||||
|
branch: 'fix/my-branch',
|
||||||
|
autoPr: true,
|
||||||
|
draftPr: true,
|
||||||
|
cwd: '/tmp/test',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
expect(mockCreatePullRequest).toHaveBeenCalledWith(
|
||||||
|
'/tmp/test',
|
||||||
|
expect.objectContaining({ draft: true }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('draftPr: false の場合、createPullRequest に draft: false が渡される', async () => {
|
||||||
|
mockExecuteTask.mockResolvedValueOnce(true);
|
||||||
|
mockCreatePullRequest.mockReturnValueOnce({ success: true, url: 'https://github.com/test/pr/1' });
|
||||||
|
|
||||||
|
const exitCode = await executePipeline({
|
||||||
|
task: 'Fix the bug',
|
||||||
|
piece: 'default',
|
||||||
|
branch: 'fix/my-branch',
|
||||||
|
autoPr: true,
|
||||||
|
draftPr: false,
|
||||||
|
cwd: '/tmp/test',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
expect(mockCreatePullRequest).toHaveBeenCalledWith(
|
||||||
|
'/tmp/test',
|
||||||
|
expect.objectContaining({ draft: false }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should pass baseBranch as base to createPullRequest', async () => {
|
it('should pass baseBranch as base to createPullRequest', async () => {
|
||||||
// Given: getCurrentBranch returns 'develop' before branch creation
|
// Given: getCurrentBranch returns 'develop' before branch creation
|
||||||
mockExecFileSync.mockImplementation((_cmd: string, args: string[]) => {
|
mockExecFileSync.mockImplementation((_cmd: string, args: string[]) => {
|
||||||
|
|||||||
@ -51,7 +51,12 @@ vi.mock('../shared/utils/index.js', async (importOriginal) => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { postExecutionFlow } from '../features/tasks/execute/postExecution.js';
|
import { postExecutionFlow, resolveDraftPr } from '../features/tasks/execute/postExecution.js';
|
||||||
|
import { resolvePieceConfigValue } from '../infra/config/index.js';
|
||||||
|
import { confirm } from '../shared/prompt/index.js';
|
||||||
|
|
||||||
|
const mockResolvePieceConfigValue = vi.mocked(resolvePieceConfigValue);
|
||||||
|
const mockConfirm = vi.mocked(confirm);
|
||||||
|
|
||||||
const baseOptions = {
|
const baseOptions = {
|
||||||
execCwd: '/clone',
|
execCwd: '/clone',
|
||||||
@ -60,6 +65,7 @@ const baseOptions = {
|
|||||||
branch: 'task/fix-the-bug',
|
branch: 'task/fix-the-bug',
|
||||||
baseBranch: 'main',
|
baseBranch: 'main',
|
||||||
shouldCreatePr: true,
|
shouldCreatePr: true,
|
||||||
|
draftPr: false,
|
||||||
pieceIdentifier: 'default',
|
pieceIdentifier: 'default',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -113,4 +119,60 @@ describe('postExecutionFlow', () => {
|
|||||||
expect(mockFindExistingPr).not.toHaveBeenCalled();
|
expect(mockFindExistingPr).not.toHaveBeenCalled();
|
||||||
expect(mockCreatePullRequest).not.toHaveBeenCalled();
|
expect(mockCreatePullRequest).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('draftPr: true の場合、createPullRequest に draft: true が渡される', async () => {
|
||||||
|
mockFindExistingPr.mockReturnValue(undefined);
|
||||||
|
|
||||||
|
await postExecutionFlow({ ...baseOptions, draftPr: true });
|
||||||
|
|
||||||
|
expect(mockCreatePullRequest).toHaveBeenCalledWith(
|
||||||
|
'/project',
|
||||||
|
expect.objectContaining({ draft: true }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('draftPr: false の場合、createPullRequest に draft: false が渡される', async () => {
|
||||||
|
mockFindExistingPr.mockReturnValue(undefined);
|
||||||
|
|
||||||
|
await postExecutionFlow({ ...baseOptions, draftPr: false });
|
||||||
|
|
||||||
|
expect(mockCreatePullRequest).toHaveBeenCalledWith(
|
||||||
|
'/project',
|
||||||
|
expect.objectContaining({ draft: false }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('resolveDraftPr', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CLI オプション true が渡された場合は true を返す', async () => {
|
||||||
|
const result = await resolveDraftPr(true, '/project');
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CLI オプション false が渡された場合は false を返す', async () => {
|
||||||
|
const result = await resolveDraftPr(false, '/project');
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CLI オプションが未指定で config が true の場合は true を返す', async () => {
|
||||||
|
mockResolvePieceConfigValue.mockReturnValue(true);
|
||||||
|
|
||||||
|
const result = await resolveDraftPr(undefined, '/project');
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CLI オプション・config ともに未指定の場合はプロンプトを表示する', async () => {
|
||||||
|
mockResolvePieceConfigValue.mockReturnValue(undefined);
|
||||||
|
mockConfirm.mockResolvedValue(false);
|
||||||
|
|
||||||
|
const result = await resolveDraftPr(undefined, '/project');
|
||||||
|
|
||||||
|
expect(mockConfirm).toHaveBeenCalledWith('Create as draft?', true);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -48,6 +48,7 @@ describe('resolveTaskExecution', () => {
|
|||||||
execPiece: 'default',
|
execPiece: 'default',
|
||||||
isWorktree: false,
|
isWorktree: false,
|
||||||
autoPr: false,
|
autoPr: false,
|
||||||
|
draftPr: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -76,6 +77,7 @@ describe('resolveTaskExecution', () => {
|
|||||||
execPiece: 'default',
|
execPiece: 'default',
|
||||||
isWorktree: false,
|
isWorktree: false,
|
||||||
autoPr: true,
|
autoPr: true,
|
||||||
|
draftPr: false,
|
||||||
reportDirName: 'issue-task-123',
|
reportDirName: 'issue-task-123',
|
||||||
issueNumber: 12345,
|
issueNumber: 12345,
|
||||||
taskPrompt: expect.stringContaining('Primary spec: `.takt/runs/issue-task-123/context/task/order.md`'),
|
taskPrompt: expect.stringContaining('Primary spec: `.takt/runs/issue-task-123/context/task/order.md`'),
|
||||||
@ -83,4 +85,20 @@ describe('resolveTaskExecution', () => {
|
|||||||
expect(fs.existsSync(expectedReportOrderPath)).toBe(true);
|
expect(fs.existsSync(expectedReportOrderPath)).toBe(true);
|
||||||
expect(fs.readFileSync(expectedReportOrderPath, 'utf-8')).toBe('# task instruction');
|
expect(fs.readFileSync(expectedReportOrderPath, 'utf-8')).toBe('# task instruction');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('draft_pr: true が draftPr: true として解決される', async () => {
|
||||||
|
const root = createTempProjectDir();
|
||||||
|
const task = createTask({
|
||||||
|
data: {
|
||||||
|
task: 'Run draft task',
|
||||||
|
auto_pr: true,
|
||||||
|
draft_pr: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await resolveTaskExecution(task, root, 'default');
|
||||||
|
|
||||||
|
expect(result.draftPr).toBe(true);
|
||||||
|
expect(result.autoPr).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -103,6 +103,17 @@ describe('saveTaskFile', () => {
|
|||||||
expect(task.task_dir).toBeTypeOf('string');
|
expect(task.task_dir).toBeTypeOf('string');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('draftPr: true が draft_pr: true として保存される', async () => {
|
||||||
|
await saveTaskFile(testDir, 'Draft task', {
|
||||||
|
autoPr: true,
|
||||||
|
draftPr: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const task = loadTasks(testDir).tasks[0]!;
|
||||||
|
expect(task.auto_pr).toBe(true);
|
||||||
|
expect(task.draft_pr).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('should generate unique names on duplicates', async () => {
|
it('should generate unique names on duplicates', async () => {
|
||||||
const first = await saveTaskFile(testDir, 'Same title');
|
const first = await saveTaskFile(testDir, 'Same title');
|
||||||
const second = await saveTaskFile(testDir, 'Same title');
|
const second = await saveTaskFile(testDir, 'Same title');
|
||||||
@ -122,7 +133,8 @@ describe('saveTaskFromInteractive', () => {
|
|||||||
it('should always save task with worktree settings', async () => {
|
it('should always save task with worktree settings', async () => {
|
||||||
mockPromptInput.mockResolvedValueOnce('');
|
mockPromptInput.mockResolvedValueOnce('');
|
||||||
mockPromptInput.mockResolvedValueOnce('');
|
mockPromptInput.mockResolvedValueOnce('');
|
||||||
mockConfirm.mockResolvedValueOnce(true);
|
mockConfirm.mockResolvedValueOnce(true); // auto-create PR?
|
||||||
|
mockConfirm.mockResolvedValueOnce(true); // create as draft?
|
||||||
|
|
||||||
await saveTaskFromInteractive(testDir, 'Task content');
|
await saveTaskFromInteractive(testDir, 'Task content');
|
||||||
|
|
||||||
@ -130,6 +142,7 @@ describe('saveTaskFromInteractive', () => {
|
|||||||
const task = loadTasks(testDir).tasks[0]!;
|
const task = loadTasks(testDir).tasks[0]!;
|
||||||
expect(task.worktree).toBe(true);
|
expect(task.worktree).toBe(true);
|
||||||
expect(task.auto_pr).toBe(true);
|
expect(task.auto_pr).toBe(true);
|
||||||
|
expect(task.draft_pr).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should keep worktree enabled even when auto-pr is declined', async () => {
|
it('should keep worktree enabled even when auto-pr is declined', async () => {
|
||||||
|
|||||||
@ -127,6 +127,50 @@ describe('resolveAutoPr default in selectAndExecuteTask', () => {
|
|||||||
expect(autoPrCall![1]).toBe(true);
|
expect(autoPrCall![1]).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shouldCreatePr=true の場合、"Create as draft?" プロンプトが表示される', async () => {
|
||||||
|
// confirm はすべての呼び出しに対して true を返す(autoPr=true → draftPr prompt)
|
||||||
|
mockConfirm.mockResolvedValue(true);
|
||||||
|
mockSummarizeTaskName.mockResolvedValue('test-task');
|
||||||
|
mockCreateSharedClone.mockReturnValue({
|
||||||
|
path: '/project/../clone',
|
||||||
|
branch: 'takt/test-task',
|
||||||
|
});
|
||||||
|
mockAutoCommitAndPush.mockReturnValue({
|
||||||
|
success: false,
|
||||||
|
message: 'no changes',
|
||||||
|
});
|
||||||
|
|
||||||
|
await selectAndExecuteTask('/project', 'test task', {
|
||||||
|
piece: 'default',
|
||||||
|
createWorktree: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const draftPrCall = mockConfirm.mock.calls.find((call) => call[0] === 'Create as draft?');
|
||||||
|
expect(draftPrCall).toBeDefined();
|
||||||
|
expect(draftPrCall![1]).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shouldCreatePr=false の場合、"Create as draft?" プロンプトは表示されない', async () => {
|
||||||
|
mockConfirm.mockResolvedValue(false); // autoPr=false → draft prompt skipped
|
||||||
|
mockSummarizeTaskName.mockResolvedValue('test-task');
|
||||||
|
mockCreateSharedClone.mockReturnValue({
|
||||||
|
path: '/project/../clone',
|
||||||
|
branch: 'takt/test-task',
|
||||||
|
});
|
||||||
|
mockAutoCommitAndPush.mockReturnValue({
|
||||||
|
success: false,
|
||||||
|
message: 'no changes',
|
||||||
|
});
|
||||||
|
|
||||||
|
await selectAndExecuteTask('/project', 'test task', {
|
||||||
|
piece: 'default',
|
||||||
|
createWorktree: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const draftPrCall = mockConfirm.mock.calls.find((call) => call[0] === 'Create as draft?');
|
||||||
|
expect(draftPrCall).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
it('should call selectPiece when no override is provided', async () => {
|
it('should call selectPiece when no override is provided', async () => {
|
||||||
mockSelectPiece.mockResolvedValue('selected-piece');
|
mockSelectPiece.mockResolvedValue('selected-piece');
|
||||||
|
|
||||||
@ -175,6 +219,7 @@ describe('resolveAutoPr default in selectAndExecuteTask', () => {
|
|||||||
branch: 'takt/test-task',
|
branch: 'takt/test-task',
|
||||||
worktree_path: '/project/../clone',
|
worktree_path: '/project/../clone',
|
||||||
auto_pr: true,
|
auto_pr: true,
|
||||||
|
draft_pr: true,
|
||||||
}));
|
}));
|
||||||
expect(mockCompleteTask).toHaveBeenCalledTimes(1);
|
expect(mockCompleteTask).toHaveBeenCalledTimes(1);
|
||||||
expect(mockFailTask).not.toHaveBeenCalled();
|
expect(mockFailTask).not.toHaveBeenCalled();
|
||||||
|
|||||||
@ -44,6 +44,7 @@ program
|
|||||||
.option('-w, --piece <name>', 'Piece name or path to piece file')
|
.option('-w, --piece <name>', 'Piece name or path to piece file')
|
||||||
.option('-b, --branch <name>', 'Branch name (auto-generated if omitted)')
|
.option('-b, --branch <name>', 'Branch name (auto-generated if omitted)')
|
||||||
.option('--auto-pr', 'Create PR after successful execution')
|
.option('--auto-pr', 'Create PR after successful execution')
|
||||||
|
.option('--draft', 'Create PR as draft (requires --auto-pr or auto_pr config)')
|
||||||
.option('--repo <owner/repo>', 'Repository (defaults to current)')
|
.option('--repo <owner/repo>', 'Repository (defaults to current)')
|
||||||
.option('--provider <name>', 'Override agent provider (claude|codex|opencode|mock)')
|
.option('--provider <name>', 'Override agent provider (claude|codex|opencode|mock)')
|
||||||
.option('--model <name>', 'Override agent model')
|
.option('--model <name>', 'Override agent model')
|
||||||
|
|||||||
@ -86,8 +86,12 @@ export async function executeDefaultAction(task?: string): Promise<void> {
|
|||||||
const resolvedPipelineAutoPr = opts.autoPr === true
|
const resolvedPipelineAutoPr = opts.autoPr === true
|
||||||
? true
|
? true
|
||||||
: (resolveConfigValue(resolvedCwd, 'autoPr') ?? false);
|
: (resolveConfigValue(resolvedCwd, 'autoPr') ?? false);
|
||||||
|
const resolvedPipelineDraftPr = opts.draft === true
|
||||||
|
? true
|
||||||
|
: (resolveConfigValue(resolvedCwd, 'draftPr') ?? false);
|
||||||
const selectOptions: SelectAndExecuteOptions = {
|
const selectOptions: SelectAndExecuteOptions = {
|
||||||
autoPr: opts.autoPr === true ? true : undefined,
|
autoPr: opts.autoPr === true ? true : undefined,
|
||||||
|
draftPr: opts.draft === true ? true : undefined,
|
||||||
repo: opts.repo as string | undefined,
|
repo: opts.repo as string | undefined,
|
||||||
piece: opts.piece as string | undefined,
|
piece: opts.piece as string | undefined,
|
||||||
createWorktree: createWorktreeOverride,
|
createWorktree: createWorktreeOverride,
|
||||||
@ -101,6 +105,7 @@ export async function executeDefaultAction(task?: string): Promise<void> {
|
|||||||
piece: resolvedPipelinePiece,
|
piece: resolvedPipelinePiece,
|
||||||
branch: opts.branch as string | undefined,
|
branch: opts.branch as string | undefined,
|
||||||
autoPr: resolvedPipelineAutoPr,
|
autoPr: resolvedPipelineAutoPr,
|
||||||
|
draftPr: resolvedPipelineDraftPr,
|
||||||
repo: opts.repo as string | undefined,
|
repo: opts.repo as string | undefined,
|
||||||
skipGit: opts.skipGit === true,
|
skipGit: opts.skipGit === true,
|
||||||
cwd: resolvedCwd,
|
cwd: resolvedCwd,
|
||||||
|
|||||||
@ -72,6 +72,8 @@ export interface GlobalConfig {
|
|||||||
worktreeDir?: string;
|
worktreeDir?: string;
|
||||||
/** Auto-create PR after worktree execution (default: prompt in interactive mode) */
|
/** Auto-create PR after worktree execution (default: prompt in interactive mode) */
|
||||||
autoPr?: boolean;
|
autoPr?: boolean;
|
||||||
|
/** Create PR as draft (default: prompt in interactive mode when autoPr is true) */
|
||||||
|
draftPr?: boolean;
|
||||||
/** List of builtin piece/agent names to exclude from fallback loading */
|
/** List of builtin piece/agent names to exclude from fallback loading */
|
||||||
disabledBuiltins?: string[];
|
disabledBuiltins?: string[];
|
||||||
/** Enable builtin pieces from builtins/{lang}/pieces */
|
/** Enable builtin pieces from builtins/{lang}/pieces */
|
||||||
|
|||||||
@ -421,6 +421,8 @@ export const GlobalConfigSchema = z.object({
|
|||||||
worktree_dir: z.string().optional(),
|
worktree_dir: z.string().optional(),
|
||||||
/** Auto-create PR after worktree execution (default: prompt in interactive mode) */
|
/** Auto-create PR after worktree execution (default: prompt in interactive mode) */
|
||||||
auto_pr: z.boolean().optional(),
|
auto_pr: z.boolean().optional(),
|
||||||
|
/** Create PR as draft (default: prompt in interactive mode when auto_pr is true) */
|
||||||
|
draft_pr: z.boolean().optional(),
|
||||||
/** List of builtin piece/agent names to exclude from fallback loading */
|
/** List of builtin piece/agent names to exclude from fallback loading */
|
||||||
disabled_builtins: z.array(z.string()).optional().default([]),
|
disabled_builtins: z.array(z.string()).optional().default([]),
|
||||||
/** Enable builtin pieces from builtins/{lang}/pieces */
|
/** Enable builtin pieces from builtins/{lang}/pieces */
|
||||||
|
|||||||
@ -105,7 +105,7 @@ function buildPipelinePrBody(
|
|||||||
* Returns a process exit code (0 on success, 2-5 on specific failures).
|
* Returns a process exit code (0 on success, 2-5 on specific failures).
|
||||||
*/
|
*/
|
||||||
export async function executePipeline(options: PipelineExecutionOptions): Promise<number> {
|
export async function executePipeline(options: PipelineExecutionOptions): Promise<number> {
|
||||||
const { cwd, piece, autoPr, skipGit } = options;
|
const { cwd, piece, autoPr, draftPr, skipGit } = options;
|
||||||
const globalConfig = resolveConfigValues(cwd, ['pipeline']);
|
const globalConfig = resolveConfigValues(cwd, ['pipeline']);
|
||||||
const pipelineConfig = globalConfig.pipeline;
|
const pipelineConfig = globalConfig.pipeline;
|
||||||
let issue: GitHubIssue | undefined;
|
let issue: GitHubIssue | undefined;
|
||||||
@ -210,6 +210,7 @@ export async function executePipeline(options: PipelineExecutionOptions): Promis
|
|||||||
body: prBody,
|
body: prBody,
|
||||||
base: baseBranch,
|
base: baseBranch,
|
||||||
repo: options.repo,
|
repo: options.repo,
|
||||||
|
draft: draftPr,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (prResult.success) {
|
if (prResult.success) {
|
||||||
|
|||||||
@ -37,7 +37,7 @@ function resolveUniqueTaskSlug(cwd: string, baseSlug: string): string {
|
|||||||
export async function saveTaskFile(
|
export async function saveTaskFile(
|
||||||
cwd: string,
|
cwd: string,
|
||||||
taskContent: string,
|
taskContent: string,
|
||||||
options?: { piece?: string; issue?: number; worktree?: boolean | string; branch?: string; autoPr?: boolean },
|
options?: { piece?: string; issue?: number; worktree?: boolean | string; branch?: string; autoPr?: boolean; draftPr?: boolean },
|
||||||
): Promise<{ taskName: string; tasksFile: string }> {
|
): Promise<{ taskName: string; tasksFile: string }> {
|
||||||
const runner = new TaskRunner(cwd);
|
const runner = new TaskRunner(cwd);
|
||||||
const slug = await summarizeTaskName(taskContent, { cwd });
|
const slug = await summarizeTaskName(taskContent, { cwd });
|
||||||
@ -54,6 +54,7 @@ export async function saveTaskFile(
|
|||||||
...(options?.piece && { piece: options.piece }),
|
...(options?.piece && { piece: options.piece }),
|
||||||
...(options?.issue !== undefined && { issue: options.issue }),
|
...(options?.issue !== undefined && { issue: options.issue }),
|
||||||
...(options?.autoPr !== undefined && { auto_pr: options.autoPr }),
|
...(options?.autoPr !== undefined && { auto_pr: options.autoPr }),
|
||||||
|
...(options?.draftPr !== undefined && { draft_pr: options.draftPr }),
|
||||||
};
|
};
|
||||||
const created = runner.addTask(taskContent, {
|
const created = runner.addTask(taskContent, {
|
||||||
...config,
|
...config,
|
||||||
@ -95,6 +96,7 @@ interface WorktreeSettings {
|
|||||||
worktree?: boolean | string;
|
worktree?: boolean | string;
|
||||||
branch?: string;
|
branch?: string;
|
||||||
autoPr?: boolean;
|
autoPr?: boolean;
|
||||||
|
draftPr?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayTaskCreationResult(
|
function displayTaskCreationResult(
|
||||||
@ -113,6 +115,9 @@ function displayTaskCreationResult(
|
|||||||
if (settings.autoPr) {
|
if (settings.autoPr) {
|
||||||
info(` Auto-PR: yes`);
|
info(` Auto-PR: yes`);
|
||||||
}
|
}
|
||||||
|
if (settings.draftPr) {
|
||||||
|
info(` Draft PR: yes`);
|
||||||
|
}
|
||||||
if (piece) info(` Piece: ${piece}`);
|
if (piece) info(` Piece: ${piece}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,8 +142,9 @@ async function promptWorktreeSettings(): Promise<WorktreeSettings> {
|
|||||||
const branch = customBranch || undefined;
|
const branch = customBranch || undefined;
|
||||||
|
|
||||||
const autoPr = await confirm('Auto-create PR?', true);
|
const autoPr = await confirm('Auto-create PR?', true);
|
||||||
|
const draftPr = autoPr ? await confirm('Create as draft?', true) : false;
|
||||||
|
|
||||||
return { worktree, branch, autoPr };
|
return { worktree, branch, autoPr, draftPr };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -15,19 +15,38 @@ import type { GitHubIssue } from '../../../infra/github/index.js';
|
|||||||
|
|
||||||
const log = createLogger('postExecution');
|
const log = createLogger('postExecution');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a boolean PR option with priority: CLI option > config > prompt.
|
||||||
|
*/
|
||||||
|
async function resolvePrBooleanOption(
|
||||||
|
option: boolean | undefined,
|
||||||
|
cwd: string,
|
||||||
|
configKey: 'autoPr' | 'draftPr',
|
||||||
|
promptMessage: string,
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (typeof option === 'boolean') {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
const configValue = resolvePieceConfigValue(cwd, configKey);
|
||||||
|
if (typeof configValue === 'boolean') {
|
||||||
|
return configValue;
|
||||||
|
}
|
||||||
|
return confirm(promptMessage, true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve auto-PR setting with priority: CLI option > config > prompt.
|
* Resolve auto-PR setting with priority: CLI option > config > prompt.
|
||||||
*/
|
*/
|
||||||
export async function resolveAutoPr(optionAutoPr: boolean | undefined, cwd: string): Promise<boolean> {
|
export async function resolveAutoPr(optionAutoPr: boolean | undefined, cwd: string): Promise<boolean> {
|
||||||
if (typeof optionAutoPr === 'boolean') {
|
return resolvePrBooleanOption(optionAutoPr, cwd, 'autoPr', 'Create pull request?');
|
||||||
return optionAutoPr;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const autoPr = resolvePieceConfigValue(cwd, 'autoPr');
|
/**
|
||||||
if (typeof autoPr === 'boolean') {
|
* Resolve draft-PR setting with priority: CLI option > config > prompt.
|
||||||
return autoPr;
|
* Only called when shouldCreatePr is true.
|
||||||
}
|
*/
|
||||||
return confirm('Create pull request?', true);
|
export async function resolveDraftPr(optionDraftPr: boolean | undefined, cwd: string): Promise<boolean> {
|
||||||
|
return resolvePrBooleanOption(optionDraftPr, cwd, 'draftPr', 'Create as draft?');
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PostExecutionOptions {
|
export interface PostExecutionOptions {
|
||||||
@ -37,6 +56,7 @@ export interface PostExecutionOptions {
|
|||||||
branch?: string;
|
branch?: string;
|
||||||
baseBranch?: string;
|
baseBranch?: string;
|
||||||
shouldCreatePr: boolean;
|
shouldCreatePr: boolean;
|
||||||
|
draftPr: boolean;
|
||||||
pieceIdentifier?: string;
|
pieceIdentifier?: string;
|
||||||
issues?: GitHubIssue[];
|
issues?: GitHubIssue[];
|
||||||
repo?: string;
|
repo?: string;
|
||||||
@ -50,7 +70,7 @@ export interface PostExecutionResult {
|
|||||||
* Auto-commit, push, and optionally create a PR after successful task execution.
|
* Auto-commit, push, and optionally create a PR after successful task execution.
|
||||||
*/
|
*/
|
||||||
export async function postExecutionFlow(options: PostExecutionOptions): Promise<PostExecutionResult> {
|
export async function postExecutionFlow(options: PostExecutionOptions): Promise<PostExecutionResult> {
|
||||||
const { execCwd, projectCwd, task, branch, baseBranch, shouldCreatePr, pieceIdentifier, issues, repo } = options;
|
const { execCwd, projectCwd, task, branch, baseBranch, shouldCreatePr, draftPr, pieceIdentifier, issues, repo } = options;
|
||||||
|
|
||||||
const commitResult = autoCommitAndPush(execCwd, task, projectCwd);
|
const commitResult = autoCommitAndPush(execCwd, task, projectCwd);
|
||||||
if (commitResult.success && commitResult.commitHash) {
|
if (commitResult.success && commitResult.commitHash) {
|
||||||
@ -86,6 +106,7 @@ export async function postExecutionFlow(options: PostExecutionOptions): Promise<
|
|||||||
body: prBody,
|
body: prBody,
|
||||||
base: baseBranch,
|
base: baseBranch,
|
||||||
repo,
|
repo,
|
||||||
|
draft: draftPr,
|
||||||
});
|
});
|
||||||
if (prResult.success) {
|
if (prResult.success) {
|
||||||
success(`PR created: ${prResult.url}`);
|
success(`PR created: ${prResult.url}`);
|
||||||
|
|||||||
@ -25,6 +25,7 @@ export interface ResolvedTaskExecution {
|
|||||||
startMovement?: string;
|
startMovement?: string;
|
||||||
retryNote?: string;
|
retryNote?: string;
|
||||||
autoPr: boolean;
|
autoPr: boolean;
|
||||||
|
draftPr: boolean;
|
||||||
issueNumber?: number;
|
issueNumber?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +104,7 @@ export async function resolveTaskExecution(
|
|||||||
|
|
||||||
const data = task.data;
|
const data = task.data;
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return { execCwd: defaultCwd, execPiece: defaultPiece, isWorktree: false, autoPr: false };
|
return { execCwd: defaultCwd, execPiece: defaultPiece, isWorktree: false, autoPr: false, draftPr: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
let execCwd = defaultCwd;
|
let execCwd = defaultCwd;
|
||||||
@ -165,18 +166,15 @@ 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;
|
const autoPr = data.auto_pr ?? resolvePieceConfigValue(defaultCwd, 'autoPr') ?? false;
|
||||||
if (data.auto_pr !== undefined) {
|
const draftPr = data.draft_pr ?? resolvePieceConfigValue(defaultCwd, 'draftPr') ?? false;
|
||||||
autoPr = data.auto_pr;
|
|
||||||
} else {
|
|
||||||
autoPr = resolvePieceConfigValue(defaultCwd, 'autoPr') ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
execCwd,
|
execCwd,
|
||||||
execPiece,
|
execPiece,
|
||||||
isWorktree,
|
isWorktree,
|
||||||
autoPr,
|
autoPr,
|
||||||
|
draftPr,
|
||||||
...(taskPrompt ? { taskPrompt } : {}),
|
...(taskPrompt ? { taskPrompt } : {}),
|
||||||
...(reportDirName ? { reportDirName } : {}),
|
...(reportDirName ? { reportDirName } : {}),
|
||||||
...(branch ? { branch } : {}),
|
...(branch ? { branch } : {}),
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import { DEFAULT_PIECE_NAME } from '../../../shared/constants.js';
|
|||||||
import { info, error, 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 { executeTask } from './taskExecution.js';
|
import { executeTask } from './taskExecution.js';
|
||||||
import { resolveAutoPr, postExecutionFlow } from './postExecution.js';
|
import { resolveAutoPr, resolveDraftPr, 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';
|
||||||
import { buildBooleanTaskResult, persistTaskError, persistTaskResult } from './taskResultHandler.js';
|
import { buildBooleanTaskResult, persistTaskError, persistTaskResult } from './taskResultHandler.js';
|
||||||
@ -100,11 +100,15 @@ export async function selectAndExecuteTask(
|
|||||||
|
|
||||||
// Ask for PR creation BEFORE execution (only if worktree is enabled)
|
// Ask for PR creation BEFORE execution (only if worktree is enabled)
|
||||||
let shouldCreatePr = false;
|
let shouldCreatePr = false;
|
||||||
|
let shouldDraftPr = false;
|
||||||
if (isWorktree) {
|
if (isWorktree) {
|
||||||
shouldCreatePr = await resolveAutoPr(options?.autoPr, cwd);
|
shouldCreatePr = await resolveAutoPr(options?.autoPr, cwd);
|
||||||
|
if (shouldCreatePr) {
|
||||||
|
shouldDraftPr = await resolveDraftPr(options?.draftPr, cwd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info('Starting task execution', { piece: pieceIdentifier, worktree: isWorktree, autoPr: shouldCreatePr });
|
log.info('Starting task execution', { piece: pieceIdentifier, worktree: isWorktree, autoPr: shouldCreatePr, draftPr: shouldDraftPr });
|
||||||
const taskRunner = new TaskRunner(cwd);
|
const taskRunner = new TaskRunner(cwd);
|
||||||
const taskRecord = taskRunner.addTask(task, {
|
const taskRecord = taskRunner.addTask(task, {
|
||||||
piece: pieceIdentifier,
|
piece: pieceIdentifier,
|
||||||
@ -112,6 +116,7 @@ export async function selectAndExecuteTask(
|
|||||||
...(branch ? { branch } : {}),
|
...(branch ? { branch } : {}),
|
||||||
...(isWorktree ? { worktree_path: execCwd } : {}),
|
...(isWorktree ? { worktree_path: execCwd } : {}),
|
||||||
auto_pr: shouldCreatePr,
|
auto_pr: shouldCreatePr,
|
||||||
|
draft_pr: shouldDraftPr,
|
||||||
...(taskSlug ? { slug: taskSlug } : {}),
|
...(taskSlug ? { slug: taskSlug } : {}),
|
||||||
});
|
});
|
||||||
const startedAt = new Date().toISOString();
|
const startedAt = new Date().toISOString();
|
||||||
@ -157,6 +162,7 @@ export async function selectAndExecuteTask(
|
|||||||
branch,
|
branch,
|
||||||
baseBranch,
|
baseBranch,
|
||||||
shouldCreatePr,
|
shouldCreatePr,
|
||||||
|
draftPr: shouldDraftPr,
|
||||||
pieceIdentifier,
|
pieceIdentifier,
|
||||||
issues: options?.issues,
|
issues: options?.issues,
|
||||||
repo: options?.repo,
|
repo: options?.repo,
|
||||||
|
|||||||
@ -144,6 +144,7 @@ export async function executeAndCompleteTask(
|
|||||||
startMovement,
|
startMovement,
|
||||||
retryNote,
|
retryNote,
|
||||||
autoPr,
|
autoPr,
|
||||||
|
draftPr,
|
||||||
issueNumber,
|
issueNumber,
|
||||||
} = await resolveTaskExecution(task, cwd, pieceName, taskAbortSignal);
|
} = await resolveTaskExecution(task, cwd, pieceName, taskAbortSignal);
|
||||||
|
|
||||||
@ -176,6 +177,7 @@ export async function executeAndCompleteTask(
|
|||||||
branch,
|
branch,
|
||||||
baseBranch,
|
baseBranch,
|
||||||
shouldCreatePr: autoPr,
|
shouldCreatePr: autoPr,
|
||||||
|
draftPr,
|
||||||
pieceIdentifier: execPiece,
|
pieceIdentifier: execPiece,
|
||||||
issues,
|
issues,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -107,6 +107,8 @@ export interface PipelineExecutionOptions {
|
|||||||
branch?: string;
|
branch?: string;
|
||||||
/** Whether to create a PR after successful execution */
|
/** Whether to create a PR after successful execution */
|
||||||
autoPr: boolean;
|
autoPr: boolean;
|
||||||
|
/** Whether to create PR as draft */
|
||||||
|
draftPr?: boolean;
|
||||||
/** Repository in owner/repo format */
|
/** Repository in owner/repo format */
|
||||||
repo?: string;
|
repo?: string;
|
||||||
/** Skip branch creation, commit, and push (piece-only execution) */
|
/** Skip branch creation, commit, and push (piece-only execution) */
|
||||||
@ -127,6 +129,7 @@ export interface WorktreeConfirmationResult {
|
|||||||
|
|
||||||
export interface SelectAndExecuteOptions {
|
export interface SelectAndExecuteOptions {
|
||||||
autoPr?: boolean;
|
autoPr?: boolean;
|
||||||
|
draftPr?: boolean;
|
||||||
repo?: string;
|
repo?: string;
|
||||||
piece?: string;
|
piece?: string;
|
||||||
createWorktree?: boolean | undefined;
|
createWorktree?: boolean | undefined;
|
||||||
|
|||||||
1
src/infra/config/env/config-env-overrides.ts
vendored
1
src/infra/config/env/config-env-overrides.ts
vendored
@ -84,6 +84,7 @@ const GLOBAL_ENV_SPECS: readonly EnvSpec[] = [
|
|||||||
{ path: 'observability.provider_events', type: 'boolean' },
|
{ path: 'observability.provider_events', type: 'boolean' },
|
||||||
{ path: 'worktree_dir', type: 'string' },
|
{ path: 'worktree_dir', type: 'string' },
|
||||||
{ path: 'auto_pr', type: 'boolean' },
|
{ path: 'auto_pr', type: 'boolean' },
|
||||||
|
{ path: 'draft_pr', type: 'boolean' },
|
||||||
{ path: 'disabled_builtins', type: 'json' },
|
{ path: 'disabled_builtins', type: 'json' },
|
||||||
{ path: 'enable_builtin_pieces', type: 'boolean' },
|
{ path: 'enable_builtin_pieces', type: 'boolean' },
|
||||||
{ path: 'anthropic_api_key', type: 'string' },
|
{ path: 'anthropic_api_key', type: 'string' },
|
||||||
|
|||||||
@ -171,6 +171,7 @@ export class GlobalConfigManager {
|
|||||||
} : undefined,
|
} : undefined,
|
||||||
worktreeDir: parsed.worktree_dir,
|
worktreeDir: parsed.worktree_dir,
|
||||||
autoPr: parsed.auto_pr,
|
autoPr: parsed.auto_pr,
|
||||||
|
draftPr: parsed.draft_pr,
|
||||||
disabledBuiltins: parsed.disabled_builtins,
|
disabledBuiltins: parsed.disabled_builtins,
|
||||||
enableBuiltinPieces: parsed.enable_builtin_pieces,
|
enableBuiltinPieces: parsed.enable_builtin_pieces,
|
||||||
anthropicApiKey: parsed.anthropic_api_key,
|
anthropicApiKey: parsed.anthropic_api_key,
|
||||||
@ -242,6 +243,9 @@ export class GlobalConfigManager {
|
|||||||
if (config.autoPr !== undefined) {
|
if (config.autoPr !== undefined) {
|
||||||
raw.auto_pr = config.autoPr;
|
raw.auto_pr = config.autoPr;
|
||||||
}
|
}
|
||||||
|
if (config.draftPr !== undefined) {
|
||||||
|
raw.draft_pr = config.draftPr;
|
||||||
|
}
|
||||||
if (config.disabledBuiltins && config.disabledBuiltins.length > 0) {
|
if (config.disabledBuiltins && config.disabledBuiltins.length > 0) {
|
||||||
raw.disabled_builtins = config.disabledBuiltins;
|
raw.disabled_builtins = config.disabledBuiltins;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export function loadConfig(projectDir: string): LoadedConfig {
|
|||||||
piece: project.piece ?? 'default',
|
piece: project.piece ?? 'default',
|
||||||
provider,
|
provider,
|
||||||
autoPr: project.auto_pr ?? global.autoPr,
|
autoPr: project.auto_pr ?? global.autoPr,
|
||||||
|
draftPr: project.draft_pr ?? global.draftPr,
|
||||||
model: resolveModel(global, provider),
|
model: resolveModel(global, provider),
|
||||||
verbose: resolveVerbose(project.verbose, global.verbose),
|
verbose: resolveVerbose(project.verbose, global.verbose),
|
||||||
providerOptions: mergeProviderOptions(global.providerOptions, project.providerOptions),
|
providerOptions: mergeProviderOptions(global.providerOptions, project.providerOptions),
|
||||||
|
|||||||
@ -13,6 +13,8 @@ export interface ProjectLocalConfig {
|
|||||||
provider?: 'claude' | 'codex' | 'opencode' | 'mock';
|
provider?: 'claude' | 'codex' | 'opencode' | 'mock';
|
||||||
/** Auto-create PR after worktree execution */
|
/** Auto-create PR after worktree execution */
|
||||||
auto_pr?: boolean;
|
auto_pr?: boolean;
|
||||||
|
/** Create PR as draft */
|
||||||
|
draft_pr?: boolean;
|
||||||
/** Verbose output mode */
|
/** Verbose output mode */
|
||||||
verbose?: boolean;
|
verbose?: boolean;
|
||||||
/** Provider-specific options (overrides global, overridden by piece/movement) */
|
/** Provider-specific options (overrides global, overridden by piece/movement) */
|
||||||
|
|||||||
@ -97,7 +97,11 @@ export function createPullRequest(cwd: string, options: CreatePrOptions): Create
|
|||||||
args.push('--repo', options.repo);
|
args.push('--repo', options.repo);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info('Creating PR', { branch: options.branch, title: options.title });
|
if (options.draft) {
|
||||||
|
args.push('--draft');
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info('Creating PR', { branch: options.branch, title: options.title, draft: options.draft });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const output = execFileSync('gh', args, {
|
const output = execFileSync('gh', args, {
|
||||||
|
|||||||
@ -26,6 +26,8 @@ export interface CreatePrOptions {
|
|||||||
base?: string;
|
base?: string;
|
||||||
/** Repository in owner/repo format (optional, uses current repo if omitted) */
|
/** Repository in owner/repo format (optional, uses current repo if omitted) */
|
||||||
repo?: string;
|
repo?: string;
|
||||||
|
/** Create PR as draft */
|
||||||
|
draft?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreatePrResult {
|
export interface CreatePrResult {
|
||||||
|
|||||||
@ -55,6 +55,7 @@ export function toTaskData(projectDir: string, task: TaskRecord): TaskFileData {
|
|||||||
start_movement: task.start_movement,
|
start_movement: task.start_movement,
|
||||||
retry_note: task.retry_note,
|
retry_note: task.retry_note,
|
||||||
auto_pr: task.auto_pr,
|
auto_pr: task.auto_pr,
|
||||||
|
draft_pr: task.draft_pr,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +79,7 @@ export function toTaskInfo(projectDir: string, tasksFile: string, task: TaskReco
|
|||||||
start_movement: task.start_movement,
|
start_movement: task.start_movement,
|
||||||
retry_note: task.retry_note,
|
retry_note: task.retry_note,
|
||||||
auto_pr: task.auto_pr,
|
auto_pr: task.auto_pr,
|
||||||
|
draft_pr: task.draft_pr,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ export const TaskExecutionConfigSchema = z.object({
|
|||||||
start_movement: z.string().optional(),
|
start_movement: z.string().optional(),
|
||||||
retry_note: z.string().optional(),
|
retry_note: z.string().optional(),
|
||||||
auto_pr: z.boolean().optional(),
|
auto_pr: z.boolean().optional(),
|
||||||
|
draft_pr: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user