fix: --auto-pr/--draft をパイプラインモード専用に制限

インタラクティブモードでの PR 自動作成プロンプト(resolveAutoPr / resolveDraftPr)を削除し、
--auto-pr / --draft はパイプラインモードでのみ有効になるよう制限する。
SelectAndExecuteOptions から repo / branch / issues フィールドも削除。
This commit is contained in:
nrslib 2026-03-02 17:37:46 +09:00
parent fa222915ea
commit d3ac5cc17c
11 changed files with 62 additions and 146 deletions

View File

@ -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`)がインストールされ、認証済みである必要があります。

View File

@ -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.

View File

@ -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 () => {

View File

@ -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,
); );
}); });

View File

@ -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);
});
});

View File

@ -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 () => {

View File

@ -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) {

View File

@ -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;

View File

@ -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);
} }
} }

View File

@ -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;
} }

View File

@ -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 {