takt: reviewじゃなくてlistにしたい。listとlist-tasksというコマンドにかえてくれ。

This commit is contained in:
nrslib 2026-01-29 14:22:57 +09:00
parent 73c4a3c555
commit d1956b53ac
12 changed files with 63 additions and 63 deletions

View File

@ -24,7 +24,7 @@ TAKT (Task Agent Koordination Tool) is a multi-agent orchestration system for Cl
| `takt /run-tasks` | `/run` | Execute all pending tasks from `.takt/tasks/` once | | `takt /run-tasks` | `/run` | Execute all pending tasks from `.takt/tasks/` once |
| `takt /watch` | | Watch `.takt/tasks/` and auto-execute tasks (resident process) | | `takt /watch` | | Watch `.takt/tasks/` and auto-execute tasks (resident process) |
| `takt /add-task` | `/add` | Add a new task interactively (YAML format, multiline supported) | | `takt /add-task` | `/add` | Add a new task interactively (YAML format, multiline supported) |
| `takt /review-tasks` | `/review` | Review task branches (try merge, merge & cleanup, or delete) | | `takt /list-tasks` | `/list` | List task branches (try merge, merge & cleanup, or delete) |
| `takt /switch` | | Switch workflow interactively | | `takt /switch` | | Switch workflow interactively |
| `takt /clear` | | Clear agent conversation sessions (reset state) | | `takt /clear` | | Clear agent conversation sessions (reset state) |
| `takt /refresh-builtin` | | Update builtin resources from `resources/` to `~/.takt/` | | `takt /refresh-builtin` | | Update builtin resources from `resources/` to `~/.takt/` |
@ -37,7 +37,7 @@ TAKT (Task Agent Koordination Tool) is a multi-agent orchestration system for Cl
``` ```
CLI (cli.ts) CLI (cli.ts)
→ Slash commands (/run-tasks, /watch, /add-task, /switch, /clear, /refresh-builtin, /help, /config) → Slash commands (/run-tasks, /watch, /add-task, /list-tasks, /switch, /clear, /refresh-builtin, /help, /config)
→ or executeTask() → or executeTask()
→ WorkflowEngine (workflow/engine.ts) → WorkflowEngine (workflow/engine.ts)
→ runAgent() (agents/runner.ts) → runAgent() (agents/runner.ts)
@ -199,7 +199,7 @@ Key constraints:
- **Session isolation**: Claude Code sessions are stored per-cwd in `~/.claude/projects/{encoded-path}/`. Sessions from the main project cannot be resumed in a clone. The engine skips session resume when `cwd !== projectCwd`. - **Session isolation**: Claude Code sessions are stored per-cwd in `~/.claude/projects/{encoded-path}/`. Sessions from the main project cannot be resumed in a clone. The engine skips session resume when `cwd !== projectCwd`.
- **No node_modules**: Clones only contain tracked files. `node_modules/` is absent. - **No node_modules**: Clones only contain tracked files. `node_modules/` is absent.
- **Dual cwd**: `cwd` = clone path (where agents run), `projectCwd` = project root (where `.takt/` lives). Reports, logs, and session data always write to `projectCwd`. - **Dual cwd**: `cwd` = clone path (where agents run), `projectCwd` = project root (where `.takt/` lives). Reports, logs, and session data always write to `projectCwd`.
- **Review**: Use `takt /review-tasks` to review branches. Instruct action creates a temporary clone for the branch, executes, pushes, then removes the clone. - **List**: Use `takt /list-tasks` to list branches. Instruct action creates a temporary clone for the branch, executes, pushes, then removes the clone.
## Error Propagation ## Error Propagation

View File

@ -35,8 +35,8 @@ takt /run-tasks
# Watch for tasks and auto-execute # Watch for tasks and auto-execute
takt /watch takt /watch
# Review task branches (merge or delete) # List task branches (merge or delete)
takt /review-tasks takt /list-tasks
# Switch workflow # Switch workflow
takt /switch takt /switch
@ -51,7 +51,7 @@ takt /switch
| `takt /run-tasks` | `/run` | Run all pending tasks from `.takt/tasks/` | | `takt /run-tasks` | `/run` | Run all pending tasks from `.takt/tasks/` |
| `takt /watch` | | Watch `.takt/tasks/` and auto-execute tasks (stays resident) | | `takt /watch` | | Watch `.takt/tasks/` and auto-execute tasks (stays resident) |
| `takt /add-task` | `/add` | Add a new task interactively (YAML format, multiline supported) | | `takt /add-task` | `/add` | Add a new task interactively (YAML format, multiline supported) |
| `takt /review-tasks` | `/review` | Review task branches (try merge, merge & cleanup, or delete) | | `takt /list-tasks` | `/list` | List task branches (try merge, merge & cleanup, or delete) |
| `takt /switch` | | Switch workflow interactively | | `takt /switch` | | Switch workflow interactively |
| `takt /clear` | | Clear agent conversation sessions | | `takt /clear` | | Clear agent conversation sessions |
| `takt /refresh-builtin` | | Update builtin agents/workflows to latest version | | `takt /refresh-builtin` | | Update builtin agents/workflows to latest version |
@ -353,7 +353,7 @@ YAML task files can specify `worktree` to run each task in an isolated `git clon
> **Note**: The YAML field is named `worktree` for backward compatibility. Internally, `git clone --shared` is used instead of `git worktree` because git worktrees have a `.git` file with `gitdir:` that points back to the main repository, causing Claude Code to recognize the main repo as the project root. Shared clones have an independent `.git` directory that avoids this issue. > **Note**: The YAML field is named `worktree` for backward compatibility. Internally, `git clone --shared` is used instead of `git worktree` because git worktrees have a `.git` file with `gitdir:` that points back to the main repository, causing Claude Code to recognize the main repo as the project root. Shared clones have an independent `.git` directory that avoids this issue.
Clones are ephemeral. When a task completes successfully, TAKT automatically commits all changes and pushes the branch to the main repository, then deletes the clone. Use `takt /review-tasks` to review, try-merge, or delete task branches. Clones are ephemeral. When a task completes successfully, TAKT automatically commits all changes and pushes the branch to the main repository, then deletes the clone. Use `takt /list-tasks` to list, try-merge, or delete task branches.
#### Running Tasks with `/run-tasks` #### Running Tasks with `/run-tasks`
@ -376,10 +376,10 @@ Watch mode polls `.takt/tasks/` for new task files and auto-executes them as the
- Automated workflows where tasks are added by external processes - Automated workflows where tasks are added by external processes
- Long-running development sessions where tasks are queued over time - Long-running development sessions where tasks are queued over time
#### Reviewing Task Branches with `/review-tasks` #### Listing Task Branches with `/list-tasks`
```bash ```bash
takt /review-tasks takt /list-tasks
``` ```
Lists all `takt/`-prefixed branches with file change counts. For each branch you can: Lists all `takt/`-prefixed branches with file change counts. For each branch you can:

View File

@ -116,7 +116,7 @@ YAMLタスクファイルで`worktree`を指定すると、各タスクを`git c
> **Note**: YAMLフィールド名は後方互換のため`worktree`のままです。内部的には`git worktree`ではなく`git clone --shared`を使用しています。git worktreeの`.git`ファイルには`gitdir:`でメインリポジトリへのパスが記載されており、Claude Codeがそれを辿ってメインリポジトリをプロジェクトルートと認識してしまうためです。共有クローンは独立した`.git`ディレクトリを持つため、この問題が発生しません。 > **Note**: YAMLフィールド名は後方互換のため`worktree`のままです。内部的には`git worktree`ではなく`git clone --shared`を使用しています。git worktreeの`.git`ファイルには`gitdir:`でメインリポジトリへのパスが記載されており、Claude Codeがそれを辿ってメインリポジトリをプロジェクトルートと認識してしまうためです。共有クローンは独立した`.git`ディレクトリを持つため、この問題が発生しません。
クローンは使い捨てです。タスク完了後に自動的にコミット+プッシュし、クローンを削除します。ブランチが唯一の永続的な成果物です。`takt /review-tasks`でブランチのレビュー・マージ・削除ができます。 クローンは使い捨てです。タスク完了後に自動的にコミット+プッシュし、クローンを削除します。ブランチが唯一の永続的な成果物です。`takt /list-tasks`でブランチの一覧表示・マージ・削除ができます。
#### `/run-tasks` でタスクを実行 #### `/run-tasks` でタスクを実行

View File

@ -65,7 +65,7 @@ vi.mock('../commands/index.js', () => ({
addTask: vi.fn(), addTask: vi.fn(),
refreshBuiltin: vi.fn(), refreshBuiltin: vi.fn(),
watchTasks: vi.fn(), watchTasks: vi.fn(),
reviewTasks: vi.fn(), listTasks: vi.fn(),
})); }));
vi.mock('../config/workflowLoader.js', () => ({ vi.mock('../config/workflowLoader.js', () => ({

View File

@ -12,7 +12,7 @@ vi.mock('node:child_process', () => ({
import { execFileSync } from 'node:child_process'; import { execFileSync } from 'node:child_process';
const mockExecFileSync = vi.mocked(execFileSync); const mockExecFileSync = vi.mocked(execFileSync);
import { getOriginalInstruction } from '../task/branchReview.js'; import { getOriginalInstruction } from '../task/branchList.js';
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();

View File

@ -1,15 +1,15 @@
/** /**
* Tests for review-tasks command * Tests for list-tasks command
*/ */
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect, vi } from 'vitest';
import { import {
parseTaktBranches, parseTaktBranches,
extractTaskSlug, extractTaskSlug,
buildReviewItems, buildListItems,
type BranchInfo, type BranchInfo,
} from '../task/branchReview.js'; } from '../task/branchList.js';
import { isBranchMerged, showFullDiff, type ReviewAction } from '../commands/reviewTasks.js'; import { isBranchMerged, showFullDiff, type ListAction } from '../commands/listTasks.js';
describe('parseTaktBranches', () => { describe('parseTaktBranches', () => {
it('should parse takt/ branches from git branch output', () => { it('should parse takt/ branches from git branch output', () => {
@ -92,7 +92,7 @@ describe('extractTaskSlug', () => {
}); });
}); });
describe('buildReviewItems', () => { describe('buildListItems', () => {
it('should build items with correct task slug and originalInstruction', () => { it('should build items with correct task slug and originalInstruction', () => {
const branches: BranchInfo[] = [ const branches: BranchInfo[] = [
{ {
@ -101,7 +101,7 @@ describe('buildReviewItems', () => {
}, },
]; ];
const items = buildReviewItems('/project', branches, 'main'); const items = buildListItems('/project', branches, 'main');
expect(items).toHaveLength(1); expect(items).toHaveLength(1);
expect(items[0]!.taskSlug).toBe('fix-auth'); expect(items[0]!.taskSlug).toBe('fix-auth');
expect(items[0]!.info).toBe(branches[0]); expect(items[0]!.info).toBe(branches[0]);
@ -123,21 +123,21 @@ describe('buildReviewItems', () => {
}, },
]; ];
const items = buildReviewItems('/project', branches, 'main'); const items = buildListItems('/project', branches, 'main');
expect(items).toHaveLength(2); expect(items).toHaveLength(2);
expect(items[0]!.taskSlug).toBe('fix-auth'); expect(items[0]!.taskSlug).toBe('fix-auth');
expect(items[1]!.taskSlug).toBe('add-search'); expect(items[1]!.taskSlug).toBe('add-search');
}); });
it('should handle empty branch list', () => { it('should handle empty branch list', () => {
const items = buildReviewItems('/project', [], 'main'); const items = buildListItems('/project', [], 'main');
expect(items).toHaveLength(0); expect(items).toHaveLength(0);
}); });
}); });
describe('ReviewAction type', () => { describe('ListAction type', () => {
it('should include diff, instruct, try, merge, delete (no skip)', () => { it('should include diff, instruct, try, merge, delete (no skip)', () => {
const actions: ReviewAction[] = ['diff', 'instruct', 'try', 'merge', 'delete']; const actions: ListAction[] = ['diff', 'instruct', 'try', 'merge', 'delete'];
expect(actions).toHaveLength(5); expect(actions).toHaveLength(5);
expect(actions).toContain('diff'); expect(actions).toContain('diff');
expect(actions).toContain('instruct'); expect(actions).toContain('instruct');

View File

@ -32,7 +32,7 @@ import {
addTask, addTask,
refreshBuiltin, refreshBuiltin,
watchTasks, watchTasks,
reviewTasks, listTasks,
} from './commands/index.js'; } from './commands/index.js';
import { listWorkflows } from './config/workflowLoader.js'; import { listWorkflows } from './config/workflowLoader.js';
import { selectOptionWithDefault, confirm } from './prompt/index.js'; import { selectOptionWithDefault, confirm } from './prompt/index.js';
@ -167,14 +167,14 @@ program
await watchTasks(cwd); await watchTasks(cwd);
return; return;
case 'review-tasks': case 'list-tasks':
case 'review': case 'list':
await reviewTasks(cwd); await listTasks(cwd);
return; return;
default: default:
error(`Unknown command: /${command}`); error(`Unknown command: /${command}`);
info('Available: /run-tasks (/run), /watch, /add-task (/add), /review-tasks (/review), /switch (/sw), /clear, /refresh-builtin, /help, /config'); info('Available: /run-tasks (/run), /watch, /add-task (/add), /list-tasks (/list), /switch (/sw), /clear, /refresh-builtin, /help, /config');
process.exit(1); process.exit(1);
} }
} }

View File

@ -17,7 +17,7 @@ Usage:
takt /run-tasks (/run) Run all pending tasks from .takt/tasks/ takt /run-tasks (/run) Run all pending tasks from .takt/tasks/
takt /watch Watch for tasks and auto-execute (stays resident) takt /watch Watch for tasks and auto-execute (stays resident)
takt /add-task (/add) Add a new task (interactive, YAML format) takt /add-task (/add) Add a new task (interactive, YAML format)
takt /review-tasks (/review) Review task branches (merge/delete) takt /list-tasks (/list) List task branches (merge/delete)
takt /switch Switch workflow interactively takt /switch Switch workflow interactively
takt /clear Clear agent conversation sessions (reset to initial state) takt /clear Clear agent conversation sessions (reset to initial state)
takt /refresh-builtin Overwrite builtin agents/workflows with latest version takt /refresh-builtin Overwrite builtin agents/workflows with latest version
@ -30,7 +30,7 @@ Examples:
takt /clear # Clear sessions, start fresh takt /clear # Clear sessions, start fresh
takt /watch # Watch & auto-execute tasks takt /watch # Watch & auto-execute tasks
takt /refresh-builtin # Update builtin resources takt /refresh-builtin # Update builtin resources
takt /review-tasks # Review & merge task branches takt /list-tasks # List & merge task branches
takt /switch takt /switch
takt /run-tasks takt /run-tasks

View File

@ -11,4 +11,4 @@ export { showHelp } from './help.js';
export { withAgentSession } from './session.js'; export { withAgentSession } from './session.js';
export { switchWorkflow } from './workflow.js'; export { switchWorkflow } from './workflow.js';
export { switchConfig, getCurrentPermissionMode, setPermissionMode, type PermissionMode } from './config.js'; export { switchConfig, getCurrentPermissionMode, setPermissionMode, type PermissionMode } from './config.js';
export { reviewTasks } from './reviewTasks.js'; export { listTasks } from './listTasks.js';

View File

@ -1,5 +1,5 @@
/** /**
* Review tasks command * List tasks command
* *
* Interactive UI for reviewing branch-based task results: * Interactive UI for reviewing branch-based task results:
* try merge, merge & cleanup, or delete actions. * try merge, merge & cleanup, or delete actions.
@ -17,9 +17,9 @@ import {
import { import {
detectDefaultBranch, detectDefaultBranch,
listTaktBranches, listTaktBranches,
buildReviewItems, buildListItems,
type BranchReviewItem, type BranchListItem,
} from '../task/branchReview.js'; } from '../task/branchList.js';
import { autoCommitAndPush } from '../task/autoCommit.js'; import { autoCommitAndPush } from '../task/autoCommit.js';
import { selectOption, confirm, promptInput } from '../prompt/index.js'; import { selectOption, confirm, promptInput } from '../prompt/index.js';
import { info, success, error as logError, warn } from '../utils/ui.js'; import { info, success, error as logError, warn } from '../utils/ui.js';
@ -29,10 +29,10 @@ import { listWorkflows } from '../config/workflowLoader.js';
import { getCurrentWorkflow } from '../config/paths.js'; import { getCurrentWorkflow } from '../config/paths.js';
import { DEFAULT_WORKFLOW_NAME } from '../constants.js'; import { DEFAULT_WORKFLOW_NAME } from '../constants.js';
const log = createLogger('review-tasks'); const log = createLogger('list-tasks');
/** Actions available for a reviewed branch */ /** Actions available for a listed branch */
export type ReviewAction = 'diff' | 'instruct' | 'try' | 'merge' | 'delete'; export type ListAction = 'diff' | 'instruct' | 'try' | 'merge' | 'delete';
/** /**
* Check if a branch has already been merged into HEAD. * Check if a branch has already been merged into HEAD.
@ -78,8 +78,8 @@ export function showFullDiff(
async function showDiffAndPromptAction( async function showDiffAndPromptAction(
cwd: string, cwd: string,
defaultBranch: string, defaultBranch: string,
item: BranchReviewItem, item: BranchListItem,
): Promise<ReviewAction | null> { ): Promise<ListAction | null> {
console.log(); console.log();
console.log(chalk.bold.cyan(`=== ${item.info.branch} ===`)); console.log(chalk.bold.cyan(`=== ${item.info.branch} ===`));
if (item.originalInstruction) { if (item.originalInstruction) {
@ -99,7 +99,7 @@ async function showDiffAndPromptAction(
} }
// Prompt action // Prompt action
const action = await selectOption<ReviewAction>( const action = await selectOption<ListAction>(
`Action for ${item.info.branch}:`, `Action for ${item.info.branch}:`,
[ [
{ label: 'View diff', value: 'diff', description: 'Show full diff in pager' }, { label: 'View diff', value: 'diff', description: 'Show full diff in pager' },
@ -117,7 +117,7 @@ async function showDiffAndPromptAction(
* Try-merge (squash): stage changes from branch without committing. * Try-merge (squash): stage changes from branch without committing.
* User can inspect staged changes and commit manually if satisfied. * User can inspect staged changes and commit manually if satisfied.
*/ */
export function tryMergeBranch(projectDir: string, item: BranchReviewItem): boolean { export function tryMergeBranch(projectDir: string, item: BranchListItem): boolean {
const { branch } = item.info; const { branch } = item.info;
try { try {
@ -145,7 +145,7 @@ export function tryMergeBranch(projectDir: string, item: BranchReviewItem): bool
* Otherwise merge first, then delete the branch. * Otherwise merge first, then delete the branch.
* No worktree removal needed clones are ephemeral. * No worktree removal needed clones are ephemeral.
*/ */
export function mergeBranch(projectDir: string, item: BranchReviewItem): boolean { export function mergeBranch(projectDir: string, item: BranchListItem): boolean {
const { branch } = item.info; const { branch } = item.info;
const alreadyMerged = isBranchMerged(projectDir, branch); const alreadyMerged = isBranchMerged(projectDir, branch);
@ -192,7 +192,7 @@ export function mergeBranch(projectDir: string, item: BranchReviewItem): boolean
* Delete a branch (discard changes). * Delete a branch (discard changes).
* No worktree removal needed clones are ephemeral. * No worktree removal needed clones are ephemeral.
*/ */
export function deleteBranch(projectDir: string, item: BranchReviewItem): boolean { export function deleteBranch(projectDir: string, item: BranchListItem): boolean {
const { branch } = item.info; const { branch } = item.info;
try { try {
@ -291,7 +291,7 @@ function getBranchContext(projectDir: string, branch: string): string {
*/ */
export async function instructBranch( export async function instructBranch(
projectDir: string, projectDir: string,
item: BranchReviewItem, item: BranchListItem,
): Promise<boolean> { ): Promise<boolean> {
const { branch } = item.info; const { branch } = item.info;
@ -349,22 +349,22 @@ export async function instructBranch(
} }
/** /**
* Main entry point: review branch-based tasks interactively. * Main entry point: list branch-based tasks interactively.
*/ */
export async function reviewTasks(cwd: string): Promise<void> { export async function listTasks(cwd: string): Promise<void> {
log.info('Starting review-tasks'); log.info('Starting list-tasks');
const defaultBranch = detectDefaultBranch(cwd); const defaultBranch = detectDefaultBranch(cwd);
let branches = listTaktBranches(cwd); let branches = listTaktBranches(cwd);
if (branches.length === 0) { if (branches.length === 0) {
info('No tasks to review.'); info('No tasks to list.');
return; return;
} }
// Interactive loop // Interactive loop
while (branches.length > 0) { while (branches.length > 0) {
const items = buildReviewItems(cwd, branches, defaultBranch); const items = buildListItems(cwd, branches, defaultBranch);
// Build selection options // Build selection options
const options = items.map((item, idx) => { const options = items.map((item, idx) => {
@ -380,7 +380,7 @@ export async function reviewTasks(cwd: string): Promise<void> {
}); });
const selected = await selectOption<string>( const selected = await selectOption<string>(
'Review Tasks (Branches)', 'List Tasks (Branches)',
options, options,
); );
@ -393,7 +393,7 @@ export async function reviewTasks(cwd: string): Promise<void> {
if (!item) continue; if (!item) continue;
// Action loop: re-show menu after viewing diff // Action loop: re-show menu after viewing diff
let action: ReviewAction | null; let action: ListAction | null;
do { do {
action = await showDiffAndPromptAction(cwd, defaultBranch, item); action = await showDiffAndPromptAction(cwd, defaultBranch, item);
@ -430,5 +430,5 @@ export async function reviewTasks(cwd: string): Promise<void> {
branches = listTaktBranches(cwd); branches = listTaktBranches(cwd);
} }
info('All tasks reviewed.'); info('All tasks listed.');
} }

View File

@ -1,15 +1,15 @@
/** /**
* Branch review helpers * Branch list helpers
* *
* Functions for listing, parsing, and enriching takt-managed branches * Functions for listing, parsing, and enriching takt-managed branches
* with metadata (diff stats, original instruction, task slug). * with metadata (diff stats, original instruction, task slug).
* Used by the /review command. * Used by the /list command.
*/ */
import { execFileSync } from 'node:child_process'; import { execFileSync } from 'node:child_process';
import { createLogger } from '../utils/debug.js'; import { createLogger } from '../utils/debug.js';
const log = createLogger('branchReview'); const log = createLogger('branchList');
/** Branch info from `git branch --list` */ /** Branch info from `git branch --list` */
export interface BranchInfo { export interface BranchInfo {
@ -17,8 +17,8 @@ export interface BranchInfo {
commit: string; commit: string;
} }
/** Branch with review metadata */ /** Branch with list metadata */
export interface BranchReviewItem { export interface BranchListItem {
info: BranchInfo; info: BranchInfo;
filesChanged: number; filesChanged: number;
taskSlug: string; taskSlug: string;
@ -159,13 +159,13 @@ export function getOriginalInstruction(
} }
/** /**
* Build review items from branch list, enriching with diff stats. * Build list items from branch list, enriching with diff stats.
*/ */
export function buildReviewItems( export function buildListItems(
projectDir: string, projectDir: string,
branches: BranchInfo[], branches: BranchInfo[],
defaultBranch: string, defaultBranch: string,
): BranchReviewItem[] { ): BranchListItem[] {
return branches.map(br => ({ return branches.map(br => ({
info: br, info: br,
filesChanged: getFilesChanged(projectDir, defaultBranch, br.branch), filesChanged: getFilesChanged(projectDir, defaultBranch, br.branch),

View File

@ -29,9 +29,9 @@ export {
getFilesChanged, getFilesChanged,
extractTaskSlug, extractTaskSlug,
getOriginalInstruction, getOriginalInstruction,
buildReviewItems, buildListItems,
type BranchInfo, type BranchInfo,
type BranchReviewItem, type BranchListItem,
} from './branchReview.js'; } from './branchList.js';
export { autoCommitAndPush, type AutoCommitResult } from './autoCommit.js'; export { autoCommitAndPush, type AutoCommitResult } from './autoCommit.js';
export { TaskWatcher, type TaskWatcherOptions } from './watcher.js'; export { TaskWatcher, type TaskWatcherOptions } from './watcher.js';