takt: reviewじゃなくてlistにしたい。listとlist-tasksというコマンドにかえてくれ。
This commit is contained in:
parent
73c4a3c555
commit
d1956b53ac
@ -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 /watch` | | Watch `.takt/tasks/` and auto-execute tasks (resident process) |
|
||||
| `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 /clear` | | Clear agent conversation sessions (reset state) |
|
||||
| `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)
|
||||
→ 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()
|
||||
→ WorkflowEngine (workflow/engine.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`.
|
||||
- **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`.
|
||||
- **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
|
||||
|
||||
|
||||
12
README.md
12
README.md
@ -35,8 +35,8 @@ takt /run-tasks
|
||||
# Watch for tasks and auto-execute
|
||||
takt /watch
|
||||
|
||||
# Review task branches (merge or delete)
|
||||
takt /review-tasks
|
||||
# List task branches (merge or delete)
|
||||
takt /list-tasks
|
||||
|
||||
# Switch workflow
|
||||
takt /switch
|
||||
@ -51,7 +51,7 @@ takt /switch
|
||||
| `takt /run-tasks` | `/run` | Run all pending tasks from `.takt/tasks/` |
|
||||
| `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 /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 /clear` | | Clear agent conversation sessions |
|
||||
| `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.
|
||||
|
||||
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`
|
||||
|
||||
@ -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
|
||||
- Long-running development sessions where tasks are queued over time
|
||||
|
||||
#### Reviewing Task Branches with `/review-tasks`
|
||||
#### Listing Task Branches with `/list-tasks`
|
||||
|
||||
```bash
|
||||
takt /review-tasks
|
||||
takt /list-tasks
|
||||
```
|
||||
|
||||
Lists all `takt/`-prefixed branches with file change counts. For each branch you can:
|
||||
|
||||
@ -116,7 +116,7 @@ YAMLタスクファイルで`worktree`を指定すると、各タスクを`git c
|
||||
|
||||
> **Note**: YAMLフィールド名は後方互換のため`worktree`のままです。内部的には`git worktree`ではなく`git clone --shared`を使用しています。git worktreeの`.git`ファイルには`gitdir:`でメインリポジトリへのパスが記載されており、Claude Codeがそれを辿ってメインリポジトリをプロジェクトルートと認識してしまうためです。共有クローンは独立した`.git`ディレクトリを持つため、この問題が発生しません。
|
||||
|
||||
クローンは使い捨てです。タスク完了後に自動的にコミット+プッシュし、クローンを削除します。ブランチが唯一の永続的な成果物です。`takt /review-tasks`でブランチのレビュー・マージ・削除ができます。
|
||||
クローンは使い捨てです。タスク完了後に自動的にコミット+プッシュし、クローンを削除します。ブランチが唯一の永続的な成果物です。`takt /list-tasks`でブランチの一覧表示・マージ・削除ができます。
|
||||
|
||||
#### `/run-tasks` でタスクを実行
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@ vi.mock('../commands/index.js', () => ({
|
||||
addTask: vi.fn(),
|
||||
refreshBuiltin: vi.fn(),
|
||||
watchTasks: vi.fn(),
|
||||
reviewTasks: vi.fn(),
|
||||
listTasks: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../config/workflowLoader.js', () => ({
|
||||
|
||||
@ -12,7 +12,7 @@ vi.mock('node:child_process', () => ({
|
||||
import { execFileSync } from 'node:child_process';
|
||||
const mockExecFileSync = vi.mocked(execFileSync);
|
||||
|
||||
import { getOriginalInstruction } from '../task/branchReview.js';
|
||||
import { getOriginalInstruction } from '../task/branchList.js';
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
/**
|
||||
* Tests for review-tasks command
|
||||
* Tests for list-tasks command
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import {
|
||||
parseTaktBranches,
|
||||
extractTaskSlug,
|
||||
buildReviewItems,
|
||||
buildListItems,
|
||||
type BranchInfo,
|
||||
} from '../task/branchReview.js';
|
||||
import { isBranchMerged, showFullDiff, type ReviewAction } from '../commands/reviewTasks.js';
|
||||
} from '../task/branchList.js';
|
||||
import { isBranchMerged, showFullDiff, type ListAction } from '../commands/listTasks.js';
|
||||
|
||||
describe('parseTaktBranches', () => {
|
||||
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', () => {
|
||||
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[0]!.taskSlug).toBe('fix-auth');
|
||||
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[0]!.taskSlug).toBe('fix-auth');
|
||||
expect(items[1]!.taskSlug).toBe('add-search');
|
||||
});
|
||||
|
||||
it('should handle empty branch list', () => {
|
||||
const items = buildReviewItems('/project', [], 'main');
|
||||
const items = buildListItems('/project', [], 'main');
|
||||
expect(items).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ReviewAction type', () => {
|
||||
describe('ListAction type', () => {
|
||||
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).toContain('diff');
|
||||
expect(actions).toContain('instruct');
|
||||
10
src/cli.ts
10
src/cli.ts
@ -32,7 +32,7 @@ import {
|
||||
addTask,
|
||||
refreshBuiltin,
|
||||
watchTasks,
|
||||
reviewTasks,
|
||||
listTasks,
|
||||
} from './commands/index.js';
|
||||
import { listWorkflows } from './config/workflowLoader.js';
|
||||
import { selectOptionWithDefault, confirm } from './prompt/index.js';
|
||||
@ -167,14 +167,14 @@ program
|
||||
await watchTasks(cwd);
|
||||
return;
|
||||
|
||||
case 'review-tasks':
|
||||
case 'review':
|
||||
await reviewTasks(cwd);
|
||||
case 'list-tasks':
|
||||
case 'list':
|
||||
await listTasks(cwd);
|
||||
return;
|
||||
|
||||
default:
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ Usage:
|
||||
takt /run-tasks (/run) Run all pending tasks from .takt/tasks/
|
||||
takt /watch Watch for tasks and auto-execute (stays resident)
|
||||
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 /clear Clear agent conversation sessions (reset to initial state)
|
||||
takt /refresh-builtin Overwrite builtin agents/workflows with latest version
|
||||
@ -30,7 +30,7 @@ Examples:
|
||||
takt /clear # Clear sessions, start fresh
|
||||
takt /watch # Watch & auto-execute tasks
|
||||
takt /refresh-builtin # Update builtin resources
|
||||
takt /review-tasks # Review & merge task branches
|
||||
takt /list-tasks # List & merge task branches
|
||||
takt /switch
|
||||
takt /run-tasks
|
||||
|
||||
|
||||
@ -11,4 +11,4 @@ export { showHelp } from './help.js';
|
||||
export { withAgentSession } from './session.js';
|
||||
export { switchWorkflow } from './workflow.js';
|
||||
export { switchConfig, getCurrentPermissionMode, setPermissionMode, type PermissionMode } from './config.js';
|
||||
export { reviewTasks } from './reviewTasks.js';
|
||||
export { listTasks } from './listTasks.js';
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Review tasks command
|
||||
* List tasks command
|
||||
*
|
||||
* Interactive UI for reviewing branch-based task results:
|
||||
* try merge, merge & cleanup, or delete actions.
|
||||
@ -17,9 +17,9 @@ import {
|
||||
import {
|
||||
detectDefaultBranch,
|
||||
listTaktBranches,
|
||||
buildReviewItems,
|
||||
type BranchReviewItem,
|
||||
} from '../task/branchReview.js';
|
||||
buildListItems,
|
||||
type BranchListItem,
|
||||
} from '../task/branchList.js';
|
||||
import { autoCommitAndPush } from '../task/autoCommit.js';
|
||||
import { selectOption, confirm, promptInput } from '../prompt/index.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 { DEFAULT_WORKFLOW_NAME } from '../constants.js';
|
||||
|
||||
const log = createLogger('review-tasks');
|
||||
const log = createLogger('list-tasks');
|
||||
|
||||
/** Actions available for a reviewed branch */
|
||||
export type ReviewAction = 'diff' | 'instruct' | 'try' | 'merge' | 'delete';
|
||||
/** Actions available for a listed branch */
|
||||
export type ListAction = 'diff' | 'instruct' | 'try' | 'merge' | 'delete';
|
||||
|
||||
/**
|
||||
* Check if a branch has already been merged into HEAD.
|
||||
@ -78,8 +78,8 @@ export function showFullDiff(
|
||||
async function showDiffAndPromptAction(
|
||||
cwd: string,
|
||||
defaultBranch: string,
|
||||
item: BranchReviewItem,
|
||||
): Promise<ReviewAction | null> {
|
||||
item: BranchListItem,
|
||||
): Promise<ListAction | null> {
|
||||
console.log();
|
||||
console.log(chalk.bold.cyan(`=== ${item.info.branch} ===`));
|
||||
if (item.originalInstruction) {
|
||||
@ -99,7 +99,7 @@ async function showDiffAndPromptAction(
|
||||
}
|
||||
|
||||
// Prompt action
|
||||
const action = await selectOption<ReviewAction>(
|
||||
const action = await selectOption<ListAction>(
|
||||
`Action for ${item.info.branch}:`,
|
||||
[
|
||||
{ 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.
|
||||
* 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;
|
||||
|
||||
try {
|
||||
@ -145,7 +145,7 @@ export function tryMergeBranch(projectDir: string, item: BranchReviewItem): bool
|
||||
* Otherwise merge first, then delete the branch.
|
||||
* 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 alreadyMerged = isBranchMerged(projectDir, branch);
|
||||
|
||||
@ -192,7 +192,7 @@ export function mergeBranch(projectDir: string, item: BranchReviewItem): boolean
|
||||
* Delete a branch (discard changes).
|
||||
* 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;
|
||||
|
||||
try {
|
||||
@ -291,7 +291,7 @@ function getBranchContext(projectDir: string, branch: string): string {
|
||||
*/
|
||||
export async function instructBranch(
|
||||
projectDir: string,
|
||||
item: BranchReviewItem,
|
||||
item: BranchListItem,
|
||||
): Promise<boolean> {
|
||||
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> {
|
||||
log.info('Starting review-tasks');
|
||||
export async function listTasks(cwd: string): Promise<void> {
|
||||
log.info('Starting list-tasks');
|
||||
|
||||
const defaultBranch = detectDefaultBranch(cwd);
|
||||
let branches = listTaktBranches(cwd);
|
||||
|
||||
if (branches.length === 0) {
|
||||
info('No tasks to review.');
|
||||
info('No tasks to list.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Interactive loop
|
||||
while (branches.length > 0) {
|
||||
const items = buildReviewItems(cwd, branches, defaultBranch);
|
||||
const items = buildListItems(cwd, branches, defaultBranch);
|
||||
|
||||
// Build selection options
|
||||
const options = items.map((item, idx) => {
|
||||
@ -380,7 +380,7 @@ export async function reviewTasks(cwd: string): Promise<void> {
|
||||
});
|
||||
|
||||
const selected = await selectOption<string>(
|
||||
'Review Tasks (Branches)',
|
||||
'List Tasks (Branches)',
|
||||
options,
|
||||
);
|
||||
|
||||
@ -393,7 +393,7 @@ export async function reviewTasks(cwd: string): Promise<void> {
|
||||
if (!item) continue;
|
||||
|
||||
// Action loop: re-show menu after viewing diff
|
||||
let action: ReviewAction | null;
|
||||
let action: ListAction | null;
|
||||
do {
|
||||
action = await showDiffAndPromptAction(cwd, defaultBranch, item);
|
||||
|
||||
@ -430,5 +430,5 @@ export async function reviewTasks(cwd: string): Promise<void> {
|
||||
branches = listTaktBranches(cwd);
|
||||
}
|
||||
|
||||
info('All tasks reviewed.');
|
||||
info('All tasks listed.');
|
||||
}
|
||||
@ -1,15 +1,15 @@
|
||||
/**
|
||||
* Branch review helpers
|
||||
* Branch list helpers
|
||||
*
|
||||
* Functions for listing, parsing, and enriching takt-managed branches
|
||||
* 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 { createLogger } from '../utils/debug.js';
|
||||
|
||||
const log = createLogger('branchReview');
|
||||
const log = createLogger('branchList');
|
||||
|
||||
/** Branch info from `git branch --list` */
|
||||
export interface BranchInfo {
|
||||
@ -17,8 +17,8 @@ export interface BranchInfo {
|
||||
commit: string;
|
||||
}
|
||||
|
||||
/** Branch with review metadata */
|
||||
export interface BranchReviewItem {
|
||||
/** Branch with list metadata */
|
||||
export interface BranchListItem {
|
||||
info: BranchInfo;
|
||||
filesChanged: number;
|
||||
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,
|
||||
branches: BranchInfo[],
|
||||
defaultBranch: string,
|
||||
): BranchReviewItem[] {
|
||||
): BranchListItem[] {
|
||||
return branches.map(br => ({
|
||||
info: br,
|
||||
filesChanged: getFilesChanged(projectDir, defaultBranch, br.branch),
|
||||
@ -29,9 +29,9 @@ export {
|
||||
getFilesChanged,
|
||||
extractTaskSlug,
|
||||
getOriginalInstruction,
|
||||
buildReviewItems,
|
||||
buildListItems,
|
||||
type BranchInfo,
|
||||
type BranchReviewItem,
|
||||
} from './branchReview.js';
|
||||
type BranchListItem,
|
||||
} from './branchList.js';
|
||||
export { autoCommitAndPush, type AutoCommitResult } from './autoCommit.js';
|
||||
export { TaskWatcher, type TaskWatcherOptions } from './watcher.js';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user