fix: --auto-pr/--draft をパイプラインモード専用に制限
インタラクティブモードでの PR 自動作成プロンプト(resolveAutoPr / resolveDraftPr)を削除し、 --auto-pr / --draft はパイプラインモードでのみ有効になるよう制限する。 SelectAndExecuteOptions から repo / branch / issues フィールドも削除。
This commit is contained in:
parent
fa222915ea
commit
d3ac5cc17c
@ -14,7 +14,7 @@
|
|||||||
| `-w, --piece <name or path>` | Piece 名または piece YAML ファイルのパス |
|
| `-w, --piece <name or path>` | Piece 名または piece YAML ファイルのパス |
|
||||||
| `-b, --branch <name>` | ブランチ名を指定(省略時は自動生成) |
|
| `-b, --branch <name>` | ブランチ名を指定(省略時は自動生成) |
|
||||||
| `--pr <number>` | PR 番号を指定してレビューコメントを取得し修正を実行 |
|
| `--pr <number>` | PR 番号を指定してレビューコメントを取得し修正を実行 |
|
||||||
| `--auto-pr` | PR を作成(インタラクティブ: 確認スキップ、pipeline: PR 有効化) |
|
| `--auto-pr` | PR を作成(pipeline モードのみ) |
|
||||||
| `--draft` | PR をドラフトとして作成(`--auto-pr` または `auto_pr` 設定が必要) |
|
| `--draft` | PR をドラフトとして作成(`--auto-pr` または `auto_pr` 設定が必要) |
|
||||||
| `--skip-git` | ブランチ作成、コミット、プッシュをスキップ(pipeline モード、piece のみ実行) |
|
| `--skip-git` | ブランチ作成、コミット、プッシュをスキップ(pipeline モード、piece のみ実行) |
|
||||||
| `--repo <owner/repo>` | リポジトリを指定(PR 作成用) |
|
| `--repo <owner/repo>` | リポジトリを指定(PR 作成用) |
|
||||||
@ -101,9 +101,6 @@ takt --task "Fix bug"
|
|||||||
|
|
||||||
# piece を指定
|
# piece を指定
|
||||||
takt --task "Add authentication" --piece dual
|
takt --task "Add authentication" --piece dual
|
||||||
|
|
||||||
# PR を自動作成
|
|
||||||
takt --task "Fix bug" --auto-pr
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**注意:** 引数として文字列を渡す場合(例: `takt "Add login feature"`)は、初期メッセージとしてインタラクティブモードに入ります。
|
**注意:** 引数として文字列を渡す場合(例: `takt "Add login feature"`)は、初期メッセージとしてインタラクティブモードに入ります。
|
||||||
@ -119,9 +116,6 @@ takt --issue 6
|
|||||||
|
|
||||||
# Issue + piece 指定
|
# Issue + piece 指定
|
||||||
takt #6 --piece dual
|
takt #6 --piece dual
|
||||||
|
|
||||||
# Issue + PR 自動作成
|
|
||||||
takt #6 --auto-pr
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**要件:** [GitHub CLI](https://cli.github.com/)(`gh`)がインストールされ、認証済みである必要があります。
|
**要件:** [GitHub CLI](https://cli.github.com/)(`gh`)がインストールされ、認証済みである必要があります。
|
||||||
|
|||||||
@ -14,7 +14,7 @@ This document provides a complete reference for all TAKT CLI commands and option
|
|||||||
| `-w, --piece <name or path>` | Piece name or path to piece YAML file |
|
| `-w, --piece <name or path>` | Piece name or path to piece YAML file |
|
||||||
| `-b, --branch <name>` | Specify branch name (auto-generated if omitted) |
|
| `-b, --branch <name>` | Specify branch name (auto-generated if omitted) |
|
||||||
| `--pr <number>` | PR number to fetch review comments and fix |
|
| `--pr <number>` | PR number to fetch review comments and fix |
|
||||||
| `--auto-pr` | Create PR (interactive: skip confirmation, pipeline: enable PR) |
|
| `--auto-pr` | Create PR after execution (pipeline mode only) |
|
||||||
| `--draft` | Create PR as draft (requires `--auto-pr` or `auto_pr` config) |
|
| `--draft` | Create PR as draft (requires `--auto-pr` or `auto_pr` config) |
|
||||||
| `--skip-git` | Skip branch creation, commit, and push (pipeline mode, piece-only) |
|
| `--skip-git` | Skip branch creation, commit, and push (pipeline mode, piece-only) |
|
||||||
| `--repo <owner/repo>` | Specify repository (for PR creation) |
|
| `--repo <owner/repo>` | Specify repository (for PR creation) |
|
||||||
@ -101,9 +101,6 @@ takt --task "Fix bug"
|
|||||||
|
|
||||||
# Specify piece
|
# Specify piece
|
||||||
takt --task "Add authentication" --piece dual
|
takt --task "Add authentication" --piece dual
|
||||||
|
|
||||||
# Auto-create PR
|
|
||||||
takt --task "Fix bug" --auto-pr
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** Passing a string as an argument (e.g., `takt "Add login feature"`) enters interactive mode with it as the initial message.
|
**Note:** Passing a string as an argument (e.g., `takt "Add login feature"`) enters interactive mode with it as the initial message.
|
||||||
@ -119,9 +116,6 @@ takt --issue 6
|
|||||||
|
|
||||||
# Issue + piece specification
|
# Issue + piece specification
|
||||||
takt #6 --piece dual
|
takt #6 --piece dual
|
||||||
|
|
||||||
# Issue + auto-create PR
|
|
||||||
takt #6 --auto-pr
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Requirements:** [GitHub CLI](https://cli.github.com/) (`gh`) must be installed and authenticated.
|
**Requirements:** [GitHub CLI](https://cli.github.com/) (`gh`) must be installed and authenticated.
|
||||||
|
|||||||
@ -179,6 +179,36 @@ describe('Issue resolution in routing', () => {
|
|||||||
mockExit.mockRestore();
|
mockExit.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should show error and exit when only --auto-pr is used outside pipeline mode', async () => {
|
||||||
|
mockOpts.autoPr = true;
|
||||||
|
|
||||||
|
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
|
||||||
|
throw new Error('process.exit called');
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(executeDefaultAction()).rejects.toThrow('process.exit called');
|
||||||
|
|
||||||
|
expect(mockError).toHaveBeenCalledWith('--auto-pr/--draft are supported only in --pipeline mode');
|
||||||
|
expect(mockExit).toHaveBeenCalledWith(1);
|
||||||
|
|
||||||
|
mockExit.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show error and exit when only --draft is used outside pipeline mode', async () => {
|
||||||
|
mockOpts.draft = true;
|
||||||
|
|
||||||
|
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
|
||||||
|
throw new Error('process.exit called');
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(executeDefaultAction()).rejects.toThrow('process.exit called');
|
||||||
|
|
||||||
|
expect(mockError).toHaveBeenCalledWith('--auto-pr/--draft are supported only in --pipeline mode');
|
||||||
|
expect(mockExit).toHaveBeenCalledWith(1);
|
||||||
|
|
||||||
|
mockExit.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
it('should show migration error and exit when deprecated --create-worktree is used', async () => {
|
it('should show migration error and exit when deprecated --create-worktree is used', async () => {
|
||||||
mockOpts.createWorktree = 'yes';
|
mockOpts.createWorktree = 'yes';
|
||||||
|
|
||||||
@ -221,13 +251,11 @@ describe('Issue resolution in routing', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Then: selectAndExecuteTask should receive issues in options
|
// Then: selectAndExecuteTask should be called (issues are used only for initialInput, not selectOptions)
|
||||||
expect(mockSelectAndExecuteTask).toHaveBeenCalledWith(
|
expect(mockSelectAndExecuteTask).toHaveBeenCalledWith(
|
||||||
'/test/cwd',
|
'/test/cwd',
|
||||||
'summarized task',
|
'summarized task',
|
||||||
expect.objectContaining({
|
expect.any(Object),
|
||||||
issues: [issue131],
|
|
||||||
}),
|
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -274,13 +302,11 @@ describe('Issue resolution in routing', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Then: selectAndExecuteTask should receive issues
|
// Then: selectAndExecuteTask should be called
|
||||||
expect(mockSelectAndExecuteTask).toHaveBeenCalledWith(
|
expect(mockSelectAndExecuteTask).toHaveBeenCalledWith(
|
||||||
'/test/cwd',
|
'/test/cwd',
|
||||||
'summarized task',
|
'summarized task',
|
||||||
expect.objectContaining({
|
expect.any(Object),
|
||||||
issues: [issue131],
|
|
||||||
}),
|
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -302,9 +328,8 @@ describe('Issue resolution in routing', () => {
|
|||||||
// Then: no issue fetching should occur
|
// Then: no issue fetching should occur
|
||||||
expect(mockFetchIssue).not.toHaveBeenCalled();
|
expect(mockFetchIssue).not.toHaveBeenCalled();
|
||||||
|
|
||||||
// Then: selectAndExecuteTask should be called without issues
|
// Then: selectAndExecuteTask should be called
|
||||||
const callArgs = mockSelectAndExecuteTask.mock.calls[0];
|
expect(mockSelectAndExecuteTask).toHaveBeenCalledTimes(1);
|
||||||
expect(callArgs?.[2]?.issues).toBeUndefined();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should enter interactive mode with no input when no args provided', async () => {
|
it('should enter interactive mode with no input when no args provided', async () => {
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* Tests for PR resolution in routing module.
|
* Tests for PR resolution in routing module.
|
||||||
*
|
*
|
||||||
* Verifies that --pr option fetches review comments,
|
* Verifies that --pr option fetches review comments
|
||||||
* passes formatted task to interactive mode, and sets
|
* and passes formatted task to interactive mode.
|
||||||
* the branch for worktree checkout.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
@ -175,7 +174,7 @@ describe('PR resolution in routing', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set branch in selectOptions from PR headRefName', async () => {
|
it('should execute task after resolving PR review comments', async () => {
|
||||||
// Given
|
// Given
|
||||||
mockOpts.pr = 456;
|
mockOpts.pr = 456;
|
||||||
const prReview = createMockPrReview({ headRefName: 'feat/my-pr-branch' });
|
const prReview = createMockPrReview({ headRefName: 'feat/my-pr-branch' });
|
||||||
@ -185,13 +184,11 @@ describe('PR resolution in routing', () => {
|
|||||||
// When
|
// When
|
||||||
await executeDefaultAction();
|
await executeDefaultAction();
|
||||||
|
|
||||||
// Then
|
// Then: selectAndExecuteTask is called (branch is no longer passed via selectOptions)
|
||||||
expect(mockSelectAndExecuteTask).toHaveBeenCalledWith(
|
expect(mockSelectAndExecuteTask).toHaveBeenCalledWith(
|
||||||
'/test/cwd',
|
'/test/cwd',
|
||||||
'summarized task',
|
'summarized task',
|
||||||
expect.objectContaining({
|
expect.any(Object),
|
||||||
branch: 'feat/my-pr-branch',
|
|
||||||
}),
|
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -33,14 +33,6 @@ vi.mock('../infra/github/index.js', () => ({
|
|||||||
buildPrBody: (...args: unknown[]) => mockBuildPrBody(...args),
|
buildPrBody: (...args: unknown[]) => mockBuildPrBody(...args),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../infra/config/index.js', () => ({
|
|
||||||
resolvePieceConfigValue: vi.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('../shared/prompt/index.js', () => ({
|
|
||||||
confirm: vi.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('../shared/ui/index.js', () => ({
|
vi.mock('../shared/ui/index.js', () => ({
|
||||||
info: vi.fn(),
|
info: vi.fn(),
|
||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
@ -56,12 +48,7 @@ vi.mock('../shared/utils/index.js', async (importOriginal) => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { postExecutionFlow, resolveDraftPr } from '../features/tasks/execute/postExecution.js';
|
import { postExecutionFlow } 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',
|
||||||
@ -237,36 +224,3 @@ describe('postExecutionFlow', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
@ -37,8 +37,6 @@ vi.mock('../infra/task/index.js', () => ({
|
|||||||
createSharedClone: vi.fn(),
|
createSharedClone: vi.fn(),
|
||||||
autoCommitAndPush: vi.fn(),
|
autoCommitAndPush: vi.fn(),
|
||||||
summarizeTaskName: vi.fn(),
|
summarizeTaskName: vi.fn(),
|
||||||
getCurrentBranch: vi.fn(() => 'main'),
|
|
||||||
detectDefaultBranch: vi.fn(() => 'main'),
|
|
||||||
resolveBaseBranch: vi.fn(() => ({ branch: 'main' })),
|
resolveBaseBranch: vi.fn(() => ({ branch: 'main' })),
|
||||||
TaskRunner: vi.fn(() => ({
|
TaskRunner: vi.fn(() => ({
|
||||||
addTask: (...args: unknown[]) => mockAddTask(...args),
|
addTask: (...args: unknown[]) => mockAddTask(...args),
|
||||||
@ -104,6 +102,9 @@ describe('selectAndExecuteTask (execute path)', () => {
|
|||||||
|
|
||||||
expect(mockAutoCommitAndPush).not.toHaveBeenCalled();
|
expect(mockAutoCommitAndPush).not.toHaveBeenCalled();
|
||||||
expect(mockAddTask).toHaveBeenCalledWith('test task', { piece: 'default' });
|
expect(mockAddTask).toHaveBeenCalledWith('test task', { piece: 'default' });
|
||||||
|
expect(mockExecuteTask).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ cwd: '/project', projectCwd: '/project' }),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call selectPiece when no override is provided', async () => {
|
it('should call selectPiece when no override is provided', async () => {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ 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 { Issue, PrReviewData } from '../../infra/git/index.js';
|
import type { PrReviewData } from '../../infra/git/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 {
|
||||||
@ -35,13 +35,13 @@ import { loadTaskHistory } from './taskHistory.js';
|
|||||||
* - --issue N option (numeric issue number)
|
* - --issue N option (numeric issue number)
|
||||||
* - Positional argument containing issue references (#N or "#1 #2")
|
* - Positional argument containing issue references (#N or "#1 #2")
|
||||||
*
|
*
|
||||||
* Returns resolved issues and the formatted task text for interactive mode.
|
* Returns the formatted task text for interactive mode.
|
||||||
* Throws on gh CLI unavailability or fetch failure.
|
* Throws on gh CLI unavailability or fetch failure.
|
||||||
*/
|
*/
|
||||||
async function resolveIssueInput(
|
async function resolveIssueInput(
|
||||||
issueOption: number | undefined,
|
issueOption: number | undefined,
|
||||||
task: string | undefined,
|
task: string | undefined,
|
||||||
): Promise<{ issues: Issue[]; initialInput: string } | null> {
|
): Promise<{ initialInput: string } | null> {
|
||||||
if (issueOption) {
|
if (issueOption) {
|
||||||
const ghStatus = getGitProvider().checkCliStatus();
|
const ghStatus = getGitProvider().checkCliStatus();
|
||||||
if (!ghStatus.available) {
|
if (!ghStatus.available) {
|
||||||
@ -52,7 +52,7 @@ async function resolveIssueInput(
|
|||||||
(fetchedIssue) => `GitHub Issue fetched: #${fetchedIssue.number} ${fetchedIssue.title}`,
|
(fetchedIssue) => `GitHub Issue fetched: #${fetchedIssue.number} ${fetchedIssue.title}`,
|
||||||
async () => getGitProvider().fetchIssue(issueOption),
|
async () => getGitProvider().fetchIssue(issueOption),
|
||||||
);
|
);
|
||||||
return { issues: [issue], initialInput: formatIssueAsTask(issue) };
|
return { initialInput: formatIssueAsTask(issue) };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task && isDirectTask(task)) {
|
if (task && isDirectTask(task)) {
|
||||||
@ -70,7 +70,7 @@ async function resolveIssueInput(
|
|||||||
(fetchedIssues) => `GitHub Issues fetched: ${fetchedIssues.map((issue) => `#${issue.number}`).join(', ')}`,
|
(fetchedIssues) => `GitHub Issues fetched: ${fetchedIssues.map((issue) => `#${issue.number}`).join(', ')}`,
|
||||||
async () => issueNumbers.map((n) => getGitProvider().fetchIssue(n)),
|
async () => issueNumbers.map((n) => getGitProvider().fetchIssue(n)),
|
||||||
);
|
);
|
||||||
return { issues, initialInput: issues.map(formatIssueAsTask).join('\n\n---\n\n') };
|
return { initialInput: issues.map(formatIssueAsTask).join('\n\n---\n\n') };
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -80,12 +80,12 @@ async function resolveIssueInput(
|
|||||||
* Resolve PR review comments from `--pr` option.
|
* Resolve PR review comments from `--pr` option.
|
||||||
*
|
*
|
||||||
* Fetches review comments and metadata, formats as task text.
|
* Fetches review comments and metadata, formats as task text.
|
||||||
* Returns the PR branch name for checkout and the formatted task.
|
* Returns the formatted task text for interactive mode.
|
||||||
* Throws on gh CLI unavailability or fetch failure.
|
* Throws on gh CLI unavailability or fetch failure.
|
||||||
*/
|
*/
|
||||||
async function resolvePrInput(
|
async function resolvePrInput(
|
||||||
prNumber: number,
|
prNumber: number,
|
||||||
): Promise<{ initialInput: string; prBranch: string }> {
|
): Promise<{ initialInput: 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);
|
||||||
@ -101,10 +101,7 @@ async function resolvePrInput(
|
|||||||
throw new Error(`PR #${prNumber} has no review comments`);
|
throw new Error(`PR #${prNumber} has no review comments`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return { initialInput: formatPrReviewAsTask(prReview) };
|
||||||
initialInput: formatPrReviewAsTask(prReview),
|
|
||||||
prBranch: prReview.headRefName,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,7 +143,6 @@ export async function executeDefaultAction(task?: string): Promise<void> {
|
|||||||
? true
|
? true
|
||||||
: (resolveConfigValue(resolvedCwd, 'draftPr') ?? false);
|
: (resolveConfigValue(resolvedCwd, 'draftPr') ?? false);
|
||||||
const selectOptions: SelectAndExecuteOptions = {
|
const selectOptions: SelectAndExecuteOptions = {
|
||||||
repo: opts.repo as string | undefined,
|
|
||||||
piece: opts.piece as string | undefined,
|
piece: opts.piece as string | undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -190,7 +186,6 @@ export async function executeDefaultAction(task?: string): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
const prResult = await resolvePrInput(prNumber);
|
const prResult = await resolvePrInput(prNumber);
|
||||||
initialInput = prResult.initialInput;
|
initialInput = prResult.initialInput;
|
||||||
selectOptions.branch = prResult.prBranch;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(getErrorMessage(e));
|
logError(getErrorMessage(e));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@ -200,7 +195,6 @@ export async function executeDefaultAction(task?: string): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
const issueResult = await resolveIssueInput(issueNumber, task);
|
const issueResult = await resolveIssueInput(issueNumber, task);
|
||||||
if (issueResult) {
|
if (issueResult) {
|
||||||
selectOptions.issues = issueResult.issues;
|
|
||||||
initialInput = issueResult.initialInput;
|
initialInput = issueResult.initialInput;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* Shared post-execution logic: auto-commit, push, and PR creation.
|
* Shared post-execution logic: auto-commit, push, and PR creation.
|
||||||
*
|
*
|
||||||
* Used by both selectAndExecuteTask (interactive mode) and
|
* Used by taskExecution (takt run / watch path) and
|
||||||
* instructBranch (instruct mode from takt list).
|
* instructBranch (takt list).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { resolvePieceConfigValue } from '../../../infra/config/index.js';
|
|
||||||
import { confirm } from '../../../shared/prompt/index.js';
|
|
||||||
import { autoCommitAndPush, pushBranch } from '../../../infra/task/index.js';
|
import { autoCommitAndPush, pushBranch } from '../../../infra/task/index.js';
|
||||||
import { info, error, success } from '../../../shared/ui/index.js';
|
import { info, error, success } from '../../../shared/ui/index.js';
|
||||||
import { createLogger } from '../../../shared/utils/index.js';
|
import { createLogger } from '../../../shared/utils/index.js';
|
||||||
@ -16,39 +14,6 @@ import type { Issue } from '../../../infra/git/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.
|
|
||||||
*/
|
|
||||||
export async function resolveAutoPr(optionAutoPr: boolean | undefined, cwd: string): Promise<boolean> {
|
|
||||||
return resolvePrBooleanOption(optionAutoPr, cwd, 'autoPr', 'Create pull request?');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve draft-PR setting with priority: CLI option > config > prompt.
|
|
||||||
* Only called when shouldCreatePr is 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 {
|
||||||
execCwd: string;
|
execCwd: string;
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* Task execution orchestration.
|
* Task execution orchestration.
|
||||||
*
|
*
|
||||||
* Coordinates piece selection and task execution,
|
* Coordinates piece selection and in-place task execution.
|
||||||
* auto-commit, and PR creation. Extracted from cli.ts to avoid
|
* Extracted from cli.ts to avoid mixing CLI parsing with business logic.
|
||||||
* mixing CLI parsing with business logic.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -126,11 +125,10 @@ export async function selectAndExecuteTask(
|
|||||||
|
|
||||||
const completedAt = new Date().toISOString();
|
const completedAt = new Date().toISOString();
|
||||||
|
|
||||||
const effectiveSuccess = taskSuccess;
|
|
||||||
if (taskRecord) {
|
if (taskRecord) {
|
||||||
const taskResult = buildBooleanTaskResult({
|
const taskResult = buildBooleanTaskResult({
|
||||||
task: taskRecord,
|
task: taskRecord,
|
||||||
taskSuccess: effectiveSuccess,
|
taskSuccess,
|
||||||
successResponse: 'Task completed successfully',
|
successResponse: 'Task completed successfully',
|
||||||
failureResponse: 'Task failed',
|
failureResponse: 'Task failed',
|
||||||
startedAt,
|
startedAt,
|
||||||
@ -139,7 +137,7 @@ export async function selectAndExecuteTask(
|
|||||||
persistTaskResult(taskRunner, taskResult);
|
persistTaskResult(taskRunner, taskResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!effectiveSuccess) {
|
if (!taskSuccess) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import type { PersonaProviderEntry } from '../../../core/models/persisted-global
|
|||||||
import type { ProviderPermissionProfiles } from '../../../core/models/provider-profiles.js';
|
import type { ProviderPermissionProfiles } from '../../../core/models/provider-profiles.js';
|
||||||
import type { MovementProviderOptions } from '../../../core/models/piece-types.js';
|
import type { MovementProviderOptions } from '../../../core/models/piece-types.js';
|
||||||
import type { ProviderType } from '../../../infra/providers/index.js';
|
import type { ProviderType } from '../../../infra/providers/index.js';
|
||||||
import type { Issue } from '../../../infra/git/index.js';
|
|
||||||
import type { ProviderOptionsSource } from '../../../core/piece/types.js';
|
import type { ProviderOptionsSource } from '../../../core/piece/types.js';
|
||||||
|
|
||||||
/** Result of piece execution */
|
/** Result of piece execution */
|
||||||
@ -136,16 +135,11 @@ export interface WorktreeConfirmationResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SelectAndExecuteOptions {
|
export interface SelectAndExecuteOptions {
|
||||||
repo?: string;
|
|
||||||
piece?: string;
|
piece?: string;
|
||||||
/** Override branch name (e.g., PR head branch for --pr) */
|
|
||||||
branch?: string;
|
|
||||||
/** Enable interactive user input during step transitions */
|
/** Enable interactive user input during step transitions */
|
||||||
interactiveUserInput?: boolean;
|
interactiveUserInput?: boolean;
|
||||||
/** Interactive mode result metadata for NDJSON logging */
|
/** Interactive mode result metadata for NDJSON logging */
|
||||||
interactiveMetadata?: InteractiveMetadata;
|
interactiveMetadata?: InteractiveMetadata;
|
||||||
/** GitHub Issues to associate with the PR (adds "Closes #N" for each issue) */
|
|
||||||
issues?: Issue[];
|
|
||||||
/** Skip adding task to tasks.yaml */
|
/** Skip adding task to tasks.yaml */
|
||||||
skipTaskList?: boolean;
|
skipTaskList?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,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 { postExecutionFlow, type PostExecutionOptions } from './execute/postExecution.js';
|
||||||
export { addTask, saveTaskFile, saveTaskFromInteractive, createIssueFromTask, createIssueAndSaveTask, promptLabelSelection } from './add/index.js';
|
export { addTask, saveTaskFile, saveTaskFromInteractive, createIssueFromTask, createIssueAndSaveTask, promptLabelSelection } from './add/index.js';
|
||||||
export { watchTasks } from './watch/index.js';
|
export { watchTasks } from './watch/index.js';
|
||||||
export {
|
export {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user