Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0b2fa0e
fix: OpenCode SDKサーバー起動タイムアウトを30秒に延長
nrslib Feb 13, 2026
e078c49
Merge branch 'develop' of https://github.com/nrslib/takt into develop
nrslib Feb 13, 2026
8731d64
Merge branch 'develop' of https://github.com/nrslib/takt into develop
nrslib Feb 13, 2026
c85f23c
claude code がsandboxで実行されるため、テストが実行できない問題を対処できるオプションを追加
nrslib Feb 13, 2026
3ff6f99
task-1770947391780 (#270)
nrslib Feb 13, 2026
02272e5
github-issue-255-ui (#266)
nrslib Feb 13, 2026
4e58c86
github-issue-256-takt-list-instruct (#267)
nrslib Feb 13, 2026
e7c5031
ignore OPENCODE_CONFIG_CONTENT
nrslib Feb 13, 2026
fcabcd9
Merge branch 'develop' of https://github.com/nrslib/takt into develop
nrslib Feb 13, 2026
f5d1c6f
ignore OPENCODE_CONFIG_CONTENT
nrslib Feb 13, 2026
54e9f80
opencodeがパラレル実行時にセッションIDを引き継げないことがある
nrslib Feb 13, 2026
6fe8fec
interactive の選択肢が非同期実行時に出てしまうバグのfix
nrslib Feb 13, 2026
8af8ff0
plan/ai-review/superviseのインストラクションにスコープ縮小防止策を追加
nrslib Feb 13, 2026
9cc6ac2
ポストエクスキューションの共通化とinstructモードの改善
nrslib Feb 13, 2026
eb593e3
OpenCode: サーバーシングルトン化で並列実行時の競合を解消
nrslib Feb 14, 2026
e52e1da
takt-list (#271)
nrslib Feb 14, 2026
c7a679d
test: enforce GitHub connectivity in e2e and stabilize SIGINT assertion
nrslib Feb 14, 2026
18bad35
Release v0.14.0
nrslib Feb 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ coverage/

task_planning/

OPENCODE_CONFIG_CONTENT

# Local editor/agent settings
.claude/
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,37 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [0.14.0] - 2026-02-14

### Added

- **`takt list` インストラクトモード (#267)**: 既存ブランチに対して追加指示を行えるインストラクトモードを追加 — 会話ループで要件を詳細化してからピース実行が可能に
- **`takt list` 完了タスクアクション (#271)**: 完了タスクに対する diff 表示・ブランチ操作(マージ、削除)を追加
- **Claude サンドボックス設定**: `provider_options.claude.sandbox` でサンドボックスの除外コマンド(`excluded_commands`)やサンドボックス無効化(`allow_unsandboxed_commands`)を設定可能に
- **`provider_options` のグローバル/プロジェクト設定**: `provider_options` を `~/.takt/config.yaml`(グローバル)および `.takt/config.yaml`(プロジェクト)で設定可能に — ピースレベル設定の最低優先フォールバックとして機能

### Changed

- **provider/model の解決ロジックを AgentRunner に集約**: provider 解決でプロジェクト設定をカスタムエージェント設定より優先するよう修正。ステップレベルの `stepModel` / `stepProvider` による上書きを追加
- **ポストエクスキューションの共通化**: インタラクティブモードとインストラクトモードで post-execution フロー(auto-commit, push, PR 作成)を `postExecution.ts` に共通化
- **スコープ縮小防止策をインストラクションに追加**: plan, ai-review, supervise のインストラクションに要件の取りこぼし検出を追加 — plan では要件ごとの「変更要/不要」判定と根拠提示を必須化、supervise では計画レポートの鵜呑み禁止

### Fixed

- インタラクティブモードの選択肢が非同期実行時に表示されてしまうバグを修正 (#266)
- OpenCode のパラレル実行時にセッション ID を引き継げない問題を修正 — サーバーをシングルトン化し並列実行時の競合を解消
- OpenCode SDK サーバー起動タイムアウトを 30 秒から 60 秒に延長

### Internal

- タスク管理の大規模リファクタリング: `TaskRunner` の責務を `TaskLifecycleService`、`TaskDeletionService`、`TaskQueryService` に分離
- `taskActions.ts` を機能別に分割: `taskBranchLifecycleActions.ts`、`taskDiffActions.ts`、`taskInstructionActions.ts`、`taskDeleteActions.ts`
- `postExecution.ts`、`taskResultHandler.ts`、`instructMode.ts`、`taskActionTarget.ts` を新規追加
- ピース選択ロジックを `pieceSelection/index.ts` に集約(`selectAndExecute.ts` から抽出)
- テスト追加: instructMode, listNonInteractive-completedActions, listTasksInteractiveStatusActions, option-resolution-order, taskInstructionActions, selectAndExecute-autoPr 等を新規・拡充
- E2E テストに Claude Code サンドボックス対応オプション(`dangerouslyDisableSandbox`)を追加
- `OPENCODE_CONFIG_CONTENT` を `.gitignore` に追加

## [0.13.0] - 2026-02-13

### Added
Expand Down
1 change: 1 addition & 0 deletions builtins/en/instructions/ai-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Review the code for AI-specific issues:
- Plausible but incorrect patterns
- Compatibility with the existing codebase
- Scope creep detection
- Scope shrinkage detection (missing task requirements)

## Judgment Procedure

Expand Down
1 change: 1 addition & 0 deletions builtins/en/instructions/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ For small tasks, skip the design sections in the report.

**Actions:**
1. Understand the task requirements
- **For each requirement, determine "change needed / not needed". If "not needed", cite the relevant code (file:line) as evidence. Claiming "already correct" without evidence is prohibited**
2. Investigate code to resolve unknowns
3. Identify the impact area
4. Determine file structure and design patterns (if needed)
Expand Down
3 changes: 2 additions & 1 deletion builtins/en/instructions/supervise.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ Run tests, verify the build, and perform final approval.
**Overall piece verification:**
1. Whether the plan and implementation results are consistent
2. Whether findings from each review movement have been addressed
3. Whether the original task objective has been achieved
3. Whether each task spec requirement has been achieved
- Do not rely on the plan report's judgment; independently verify each requirement against actual code (file:line)

**Report verification:** Read all reports in the Report Directory and
check for any unaddressed improvement suggestions.
Expand Down
1 change: 1 addition & 0 deletions builtins/ja/instructions/ai-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ AI特有の問題についてコードをレビューしてください:
- もっともらしいが間違っているパターン
- 既存コードベースとの適合性
- スコープクリープの検出
- スコープ縮小の検出(タスク要件の取りこぼし)

## 判定手順

Expand Down
1 change: 1 addition & 0 deletions builtins/ja/instructions/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- **指示書に明記されていない別ファイルを「参照資料の代わり」として使うことは禁止**
2. タスクの要件を理解する
- 参照資料の内容と現在の実装を突き合わせて差分を特定する
- **要件ごとに「変更要/不要」を判定する。「不要」の場合は現行コードの該当箇所(ファイル:行)を根拠として示すこと。根拠なしの「既に正しい」は禁止**
3. コードを調査して不明点を解決する
4. 影響範囲を特定する
5. ファイル構成・設計パターンを決定する(必要な場合)
Expand Down
3 changes: 2 additions & 1 deletion builtins/ja/instructions/supervise.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
**ピース全体の確認:**
1. 計画と実装結果が一致しているか
2. 各レビュームーブメントの指摘が対応されているか
3. 元のタスク目的が達成されているか
3. タスク指示書の各要件が達成されているか
- 計画レポートの判断を鵜呑みにせず、要件ごとに実コード(ファイル:行)で独立照合する

**レポートの確認:** Report Directory内の全レポートを読み、
未対応の改善提案がないか確認してください。
Expand Down
168 changes: 168 additions & 0 deletions docs/provider-sandbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# Provider Sandbox Configuration

TAKT supports configuring sandbox settings for AI agent providers. This document covers how sandbox isolation works across providers, how to configure it, and the security trade-offs.

## Overview

| Provider | Sandbox Mechanism | Build Tool Issues | TAKT Configuration |
|----------|------------------|-------------------|-------------------|
| **Claude Code** | macOS Seatbelt / Linux bubblewrap | Gradle/JVM blocked in `edit` mode | `provider_options.claude.sandbox` |
| **Codex CLI** | macOS Seatbelt / Linux Landlock+seccomp | npm/maven/pytest failures (widespread) | `provider_options.codex.network_access` |
| **OpenCode CLI** | None (no native sandbox) | No constraints (no security either) | N/A |

## Claude Code Sandbox

### The Problem

When a movement uses `permission_mode: edit` (mapped to Claude SDK's `acceptEdits`), Bash commands run inside a macOS Seatbelt sandbox. This sandbox blocks:

- Writes outside the working directory (e.g., `~/.gradle`)
- Certain system calls required by JVM initialization
- Network access (by default)

As a result, build tools like Gradle, Maven, or any JVM-based tool fail with `Operation not permitted`.

### Solution: `provider_options.claude.sandbox`

TAKT exposes Claude SDK's `SandboxSettings` through `provider_options.claude.sandbox` at four configuration levels.

#### Option A: `allow_unsandboxed_commands` (Recommended)

Allow all Bash commands to run outside the sandbox while keeping file edit permissions controlled:

```yaml
provider_options:
claude:
sandbox:
allow_unsandboxed_commands: true
```

#### Option B: `excluded_commands`

Exclude only specific commands from the sandbox:

```yaml
provider_options:
claude:
sandbox:
excluded_commands:
- ./gradlew
- npm
- npx
```

### Configuration Levels

Settings are merged with the following priority (highest wins):

```
Movement > Piece > Project Local > Global
```

#### Global (`~/.takt/config.yaml`)

Applies to all projects and all pieces:

```yaml
# ~/.takt/config.yaml
provider_options:
claude:
sandbox:
allow_unsandboxed_commands: true
```

#### Project Local (`.takt/config.yaml`)

Applies to this project only:

```yaml
# .takt/config.yaml
provider_options:
claude:
sandbox:
excluded_commands:
- ./gradlew
```

#### Piece (`piece_config` section)

Applies to all movements in this piece:

```yaml
# pieces/my-piece.yaml
piece_config:
provider_options:
claude:
sandbox:
allow_unsandboxed_commands: true
```

#### Movement (per step)

Applies to a specific movement only:

```yaml
movements:
- name: implement
permission_mode: edit
provider_options:
claude:
sandbox:
allow_unsandboxed_commands: true
- name: review
permission_mode: readonly
# No sandbox config needed — readonly doesn't sandbox Bash
```

### Security Risk Comparison

| Configuration | File Edits | Network | Bash Commands | CWD-external Writes | Risk Level |
|--------------|-----------|---------|---------------|---------------------|------------|
| `permission_mode: edit` (default) | Permitted | Blocked | Sandboxed | Blocked | Low |
| `excluded_commands: [./gradlew]` | Permitted | Blocked | Only `./gradlew` unsandboxed | Only via `./gradlew` | Low |
| `allow_unsandboxed_commands: true` | Permitted | Allowed | Unsandboxed | Allowed via Bash | **Medium** |
| `permission_mode: full` | All permitted | Allowed | Unsandboxed | All permitted | **High** |

**Key difference between `allow_unsandboxed_commands` and `permission_mode: full`:**
- `allow_unsandboxed_commands`: File edits still require Claude Code's permission check (`acceptEdits` mode). Only Bash is unsandboxed.
- `permission_mode: full`: All permission checks are bypassed (`bypassPermissions` mode). No guardrails at all.

### Practical Risk Assessment

The "Medium" risk of `allow_unsandboxed_commands` is manageable in practice because:

- TAKT runs locally on the developer's machine (not a public-facing service)
- Input comes from task instructions written by the developer
- Agent behavior is reviewed by the supervisor movement
- File edit operations still go through Claude Code's permission system

## Codex CLI Sandbox

Codex CLI uses macOS Seatbelt (same as Claude Code) but has **more severe compatibility issues** with build tools. Community reports show npm, Maven, pytest, and other tools frequently failing with `Operation not permitted` — even when the same commands work in Claude Code.

Codex sandbox is configured via `~/.codex/config.toml` (not through TAKT):

```toml
# ~/.codex/config.toml
sandbox_mode = "workspace-write"

[sandbox_workspace_write]
network_access = true
writable_roots = ["/Users/YOU/.gradle"]
```

TAKT provides `provider_options.codex.network_access` to control network access via the Codex SDK:

```yaml
provider_options:
codex:
network_access: true
```

For other sandbox settings (writable_roots, sandbox_mode), configure directly in `~/.codex/config.toml`.

## OpenCode CLI Sandbox

OpenCode CLI does not have a native sandbox mechanism. All commands run without filesystem or network restrictions. For isolation, the community recommends Docker containers (e.g., [opencode-sandbox](https://github.com/fabianlema/opencode-sandbox)).

No TAKT-side sandbox configuration is needed or available for OpenCode.
8 changes: 6 additions & 2 deletions e2e/specs/run-sigint-graceful.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ describe('E2E: Run tasks graceful shutdown on SIGINT (parallel)', () => {
env: {
...isolatedEnv.env,
TAKT_MOCK_SCENARIO: scenarioPath,
TAKT_E2E_SELF_SIGINT_TWICE: '1',
},
stdio: ['ignore', 'pipe', 'pipe'],
});
Expand All @@ -242,9 +241,14 @@ describe('E2E: Run tasks graceful shutdown on SIGINT (parallel)', () => {
);
expect(workersFilled, `stdout:\n${stdout}\n\nstderr:\n${stderr}`).toBe(true);

// Simulate user pressing Ctrl+C twice.
child.kill('SIGINT');
await new Promise((resolvePromise) => setTimeout(resolvePromise, 25));
child.kill('SIGINT');

const exit = await waitForClose(child, 60_000);
expect(
exit.signal === 'SIGINT' || exit.code === 130,
exit.signal === 'SIGINT' || exit.code === 130 || exit.code === 0,
`unexpected exit: code=${exit.code}, signal=${exit.signal}`,
).toBe(true);

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "takt",
"version": "0.13.0",
"version": "0.14.0",
"description": "TAKT: TAKT Agent Koordination Topology - AI Agent Piece Orchestration",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -14,7 +14,7 @@
"watch": "tsc --watch",
"test": "vitest run",
"test:watch": "vitest",
"test:e2e": "npm run test:e2e:mock; code=$?; if [ \"$code\" -eq 0 ]; then msg='test:e2e passed'; else msg=\"test:e2e failed (exit=$code)\"; fi; if command -v osascript >/dev/null 2>&1; then osascript -e \"display notification \\\"$msg\\\" with title \\\"takt\\\" subtitle \\\"E2E\\\"\" >/dev/null 2>&1 || true; fi; echo \"[takt] $msg\"; exit $code",
"test:e2e": "tmp=\"$(mktemp -t takt-e2e.XXXXXX)\"; npm run test:e2e:mock >\"$tmp\" 2>&1; code=$?; cat \"$tmp\"; if grep -q \"error connecting to api.github.com\" \"$tmp\"; then echo \"[takt] GitHub connectivity error detected in E2E output\"; code=1; fi; rm -f \"$tmp\"; if [ \"$code\" -eq 0 ]; then msg='test:e2e passed'; else msg=\"test:e2e failed (exit=$code)\"; fi; if command -v osascript >/dev/null 2>&1; then osascript -e \"display notification \\\"$msg\\\" with title \\\"takt\\\" subtitle \\\"E2E\\\"\" >/dev/null 2>&1 || true; fi; echo \"[takt] $msg\"; exit $code",
"test:e2e:mock": "TAKT_E2E_PROVIDER=mock vitest run --config vitest.config.e2e.mock.ts --reporter=verbose",
"test:e2e:all": "npm run test:e2e:mock && npm run test:e2e:provider",
"test:e2e:provider": "npm run test:e2e:provider:claude && npm run test:e2e:provider:codex",
Expand Down
39 changes: 39 additions & 0 deletions src/__tests__/actionDispatcher.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { describe, it, expect, vi } from 'vitest';

import { dispatchConversationAction } from '../features/interactive/actionDispatcher.js';

describe('dispatchConversationAction', () => {
it('should dispatch to matching handler with full result payload', async () => {
const execute = vi.fn().mockResolvedValue('executed');
const saveTask = vi.fn().mockResolvedValue('saved');
const cancel = vi.fn().mockResolvedValue('cancelled');

const result = await dispatchConversationAction(
{ action: 'save_task', task: 'refine branch docs' },
{
execute,
save_task: saveTask,
cancel,
},
);

expect(result).toBe('saved');
expect(saveTask).toHaveBeenCalledWith({ action: 'save_task', task: 'refine branch docs' });
expect(execute).not.toHaveBeenCalled();
expect(cancel).not.toHaveBeenCalled();
});

it('should support synchronous handlers', async () => {
const result = await dispatchConversationAction(
{ action: 'cancel', task: '' },
{
execute: () => true,
save_task: () => true,
cancel: () => false,
},
);

expect(result).toBe(false);
});
});

Loading