feat: resolve movement permissions via provider profiles with required floor
This commit is contained in:
parent
e1a5d7a386
commit
f065ee510f
@ -1,91 +1,92 @@
|
|||||||
# TAKT Global Configuration
|
# TAKT global configuration sample
|
||||||
# Location: ~/.takt/config.yaml
|
# Location: ~/.takt/config.yaml
|
||||||
|
|
||||||
# ── Basic ──
|
# ---- Core ----
|
||||||
|
|
||||||
# Language (en | ja)
|
|
||||||
language: en
|
language: en
|
||||||
|
|
||||||
# Default piece when no piece is specified
|
|
||||||
default_piece: default
|
default_piece: default
|
||||||
|
|
||||||
# Log level (debug | info | warn | error)
|
|
||||||
log_level: info
|
log_level: info
|
||||||
|
|
||||||
# ── Provider & Model ──
|
# ---- Provider ----
|
||||||
|
# provider: claude | codex | opencode | mock
|
||||||
# Provider runtime (claude | codex)
|
|
||||||
provider: claude
|
provider: claude
|
||||||
|
|
||||||
# Default model (optional)
|
# Model (optional)
|
||||||
# Claude: opus, sonnet, haiku
|
# Claude examples: opus, sonnet, haiku
|
||||||
# Codex: gpt-5.2-codex, gpt-5.1-codex, etc.
|
# Codex examples: gpt-5.2-codex, gpt-5.1-codex
|
||||||
|
# OpenCode format: provider/model
|
||||||
# model: sonnet
|
# model: sonnet
|
||||||
|
|
||||||
# Per-persona provider override (optional)
|
# Per-persona provider override
|
||||||
# Override provider for specific personas. Others use the global provider.
|
|
||||||
# persona_providers:
|
# persona_providers:
|
||||||
# coder: codex
|
# coder: codex
|
||||||
|
# reviewer: claude
|
||||||
|
|
||||||
# ── API Keys ──
|
# Provider-specific movement permission policy
|
||||||
# Optional. Environment variables take priority:
|
# Priority:
|
||||||
# TAKT_ANTHROPIC_API_KEY, TAKT_OPENAI_API_KEY
|
# 1) project provider_profiles override
|
||||||
|
# 2) global provider_profiles override
|
||||||
|
# 3) project provider_profiles default
|
||||||
|
# 4) global provider_profiles default
|
||||||
|
# 5) movement.required_permission_mode (minimum floor)
|
||||||
|
# provider_profiles:
|
||||||
|
# codex:
|
||||||
|
# default_permission_mode: full
|
||||||
|
# movement_permission_overrides:
|
||||||
|
# ai_review: readonly
|
||||||
|
# claude:
|
||||||
|
# default_permission_mode: edit
|
||||||
|
|
||||||
|
# Provider-specific runtime options
|
||||||
|
# provider_options:
|
||||||
|
# codex:
|
||||||
|
# network_access: true
|
||||||
|
# claude:
|
||||||
|
# sandbox:
|
||||||
|
# allow_unsandboxed_commands: true
|
||||||
|
|
||||||
|
# ---- API Keys ----
|
||||||
|
# Environment variables take priority:
|
||||||
|
# TAKT_ANTHROPIC_API_KEY / TAKT_OPENAI_API_KEY / TAKT_OPENCODE_API_KEY
|
||||||
# anthropic_api_key: ""
|
# anthropic_api_key: ""
|
||||||
# openai_api_key: ""
|
# openai_api_key: ""
|
||||||
|
# opencode_api_key: ""
|
||||||
|
|
||||||
# ── Execution ──
|
# ---- Runtime ----
|
||||||
|
# Global runtime preparation (piece_config.runtime overrides this)
|
||||||
# Worktree (shared clone) directory (default: ../{clone-name} relative to project)
|
|
||||||
# worktree_dir: ~/takt-worktrees
|
|
||||||
|
|
||||||
# Auto-create PR after worktree execution (default: prompt in interactive mode)
|
|
||||||
# auto_pr: false
|
|
||||||
|
|
||||||
# Prevent macOS idle sleep during execution using caffeinate (default: false)
|
|
||||||
# prevent_sleep: false
|
|
||||||
|
|
||||||
# Runtime environment defaults (applies to all pieces unless piece_config.runtime overrides)
|
|
||||||
# runtime:
|
# runtime:
|
||||||
# prepare:
|
# prepare:
|
||||||
# - gradle
|
# - gradle
|
||||||
# - node
|
# - node
|
||||||
|
|
||||||
# ── Parallel Execution (takt run) ──
|
# ---- Execution ----
|
||||||
|
# worktree_dir: ~/takt-worktrees
|
||||||
|
# auto_pr: false
|
||||||
|
# prevent_sleep: false
|
||||||
|
|
||||||
# Number of tasks to run concurrently (1 = sequential, max: 10)
|
# ---- Run Loop ----
|
||||||
# concurrency: 1
|
# concurrency: 1
|
||||||
|
|
||||||
# Polling interval in ms for picking up new tasks (100-5000, default: 500)
|
|
||||||
# task_poll_interval_ms: 500
|
# task_poll_interval_ms: 500
|
||||||
|
|
||||||
# ── Interactive Mode ──
|
|
||||||
|
|
||||||
# Number of movement previews shown in interactive mode (0 to disable, max: 10)
|
|
||||||
# interactive_preview_movements: 3
|
# interactive_preview_movements: 3
|
||||||
|
|
||||||
# Branch name generation strategy (romaji: fast default | ai: slow)
|
|
||||||
# branch_name_strategy: romaji
|
# branch_name_strategy: romaji
|
||||||
|
|
||||||
# ── Output ──
|
# ---- Output ----
|
||||||
|
|
||||||
# Notification sounds (default: true)
|
|
||||||
# notification_sound: true
|
|
||||||
|
|
||||||
# Minimal output for CI - suppress AI output (default: false)
|
|
||||||
# minimal_output: false
|
# minimal_output: false
|
||||||
|
# notification_sound: true
|
||||||
|
# notification_sound_events:
|
||||||
|
# iteration_limit: true
|
||||||
|
# piece_complete: true
|
||||||
|
# piece_abort: true
|
||||||
|
# run_complete: true
|
||||||
|
# run_abort: true
|
||||||
|
# observability:
|
||||||
|
# provider_events: true
|
||||||
|
|
||||||
# ── Builtin Pieces ──
|
# ---- Builtins ----
|
||||||
|
|
||||||
# Enable builtin pieces (default: true)
|
|
||||||
# enable_builtin_pieces: true
|
# enable_builtin_pieces: true
|
||||||
|
|
||||||
# Exclude specific builtins from loading
|
|
||||||
# disabled_builtins:
|
# disabled_builtins:
|
||||||
# - magi
|
# - magi
|
||||||
|
|
||||||
# ── Pipeline Mode (--pipeline) ──
|
# ---- Pipeline ----
|
||||||
|
|
||||||
# pipeline:
|
# pipeline:
|
||||||
# default_branch_prefix: "takt/"
|
# default_branch_prefix: "takt/"
|
||||||
# commit_message_template: "feat: {title} (#{issue})"
|
# commit_message_template: "feat: {title} (#{issue})"
|
||||||
@ -94,14 +95,11 @@ provider: claude
|
|||||||
# {issue_body}
|
# {issue_body}
|
||||||
# Closes #{issue}
|
# Closes #{issue}
|
||||||
|
|
||||||
# ── Preferences ──
|
# ---- Preferences ----
|
||||||
|
|
||||||
# Custom paths for preference files
|
|
||||||
# bookmarks_file: ~/.takt/preferences/bookmarks.yaml
|
# bookmarks_file: ~/.takt/preferences/bookmarks.yaml
|
||||||
# piece_categories_file: ~/.takt/preferences/piece-categories.yaml
|
# piece_categories_file: ~/.takt/preferences/piece-categories.yaml
|
||||||
|
|
||||||
# ── Debug ──
|
# ---- Debug ----
|
||||||
|
|
||||||
# debug:
|
# debug:
|
||||||
# enabled: false
|
# enabled: false
|
||||||
# log_file: ~/.takt/logs/debug.log
|
# log_file: ~/.takt/logs/debug.log
|
||||||
|
|||||||
@ -219,7 +219,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Fix complete
|
- condition: Fix complete
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -216,7 +216,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Fix complete
|
- condition: Fix complete
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -51,7 +51,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Implementation complete
|
- condition: Implementation complete
|
||||||
next: reviewers
|
next: reviewers
|
||||||
@ -132,7 +132,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Fix complete
|
- condition: Fix complete
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -82,7 +82,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Implementation complete
|
- condition: Implementation complete
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -142,7 +142,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI issues fixed
|
- condition: AI issues fixed
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -234,7 +234,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Fix complete
|
- condition: Fix complete
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -85,7 +85,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Test implementation complete
|
- condition: Test implementation complete
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -145,7 +145,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI issues fixed
|
- condition: AI issues fixed
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -212,7 +212,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Fix complete
|
- condition: Fix complete
|
||||||
next: review_test
|
next: review_test
|
||||||
|
|||||||
@ -254,7 +254,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Fix complete
|
- condition: Fix complete
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -251,7 +251,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Fix complete
|
- condition: Fix complete
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -235,7 +235,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Fix complete
|
- condition: Fix complete
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -25,7 +25,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction: implement
|
instruction: implement
|
||||||
rules:
|
rules:
|
||||||
- condition: Implementation complete
|
- condition: Implementation complete
|
||||||
@ -106,7 +106,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI Reviewer's issues fixed
|
- condition: AI Reviewer's issues fixed
|
||||||
- condition: No fix needed (verified target files/spec)
|
- condition: No fix needed (verified target files/spec)
|
||||||
@ -126,7 +126,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Supervisor's issues fixed
|
- condition: Supervisor's issues fixed
|
||||||
- condition: Cannot proceed, insufficient info
|
- condition: Cannot proceed, insufficient info
|
||||||
@ -151,7 +151,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI Reviewer's issues fixed
|
- condition: AI Reviewer's issues fixed
|
||||||
next: reviewers
|
next: reviewers
|
||||||
@ -175,7 +175,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Supervisor's issues fixed
|
- condition: Supervisor's issues fixed
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -25,7 +25,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Task complete
|
- condition: Task complete
|
||||||
next: COMPLETE
|
next: COMPLETE
|
||||||
|
|||||||
@ -25,7 +25,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction: implement
|
instruction: implement
|
||||||
rules:
|
rules:
|
||||||
- condition: Implementation complete
|
- condition: Implementation complete
|
||||||
@ -106,7 +106,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI Reviewer's issues fixed
|
- condition: AI Reviewer's issues fixed
|
||||||
- condition: No fix needed (verified target files/spec)
|
- condition: No fix needed (verified target files/spec)
|
||||||
@ -126,7 +126,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Supervisor's issues fixed
|
- condition: Supervisor's issues fixed
|
||||||
- condition: Cannot proceed, insufficient info
|
- condition: Cannot proceed, insufficient info
|
||||||
@ -151,7 +151,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI Reviewer's issues fixed
|
- condition: AI Reviewer's issues fixed
|
||||||
next: reviewers
|
next: reviewers
|
||||||
@ -175,7 +175,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Supervisor's issues fixed
|
- condition: Supervisor's issues fixed
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -226,7 +226,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction: implement
|
instruction: implement
|
||||||
rules:
|
rules:
|
||||||
- condition: Implementation complete
|
- condition: Implementation complete
|
||||||
@ -309,7 +309,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Fix complete
|
- condition: Fix complete
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -85,7 +85,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Test implementation complete
|
- condition: Test implementation complete
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -145,7 +145,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI issues fixed
|
- condition: AI issues fixed
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -212,7 +212,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: Fix complete
|
- condition: Fix complete
|
||||||
next: review_test
|
next: review_test
|
||||||
|
|||||||
@ -1,91 +1,92 @@
|
|||||||
# TAKT グローバル設定
|
# TAKT グローバル設定サンプル
|
||||||
# 配置場所: ~/.takt/config.yaml
|
# 配置場所: ~/.takt/config.yaml
|
||||||
|
|
||||||
# ── 基本設定 ──
|
# ---- 基本 ----
|
||||||
|
|
||||||
# 言語 (en | ja)
|
|
||||||
language: ja
|
language: ja
|
||||||
|
|
||||||
# デフォルトピース(指定なし時に使用)
|
|
||||||
default_piece: default
|
default_piece: default
|
||||||
|
|
||||||
# ログレベル (debug | info | warn | error)
|
|
||||||
log_level: info
|
log_level: info
|
||||||
|
|
||||||
# ── プロバイダー & モデル ──
|
# ---- プロバイダー ----
|
||||||
|
# provider: claude | codex | opencode | mock
|
||||||
# プロバイダー (claude | codex)
|
|
||||||
provider: claude
|
provider: claude
|
||||||
|
|
||||||
# デフォルトモデル(オプション)
|
# モデル(任意)
|
||||||
# Claude: opus, sonnet, haiku
|
# Claude 例: opus, sonnet, haiku
|
||||||
# Codex: gpt-5.2-codex, gpt-5.1-codex など
|
# Codex 例: gpt-5.2-codex, gpt-5.1-codex
|
||||||
|
# OpenCode 形式: provider/model
|
||||||
# model: sonnet
|
# model: sonnet
|
||||||
|
|
||||||
# ペルソナ単位のプロバイダー上書き(オプション)
|
# ペルソナ別プロバイダー上書き
|
||||||
# 特定ペルソナだけプロバイダーを変更。未指定のペルソナはグローバル設定を使用。
|
|
||||||
# persona_providers:
|
# persona_providers:
|
||||||
# coder: codex
|
# coder: codex
|
||||||
|
# reviewer: claude
|
||||||
|
|
||||||
# ── APIキー ──
|
# プロバイダー別 movement 権限ポリシー
|
||||||
# オプション。環境変数が優先:
|
# 優先順:
|
||||||
# TAKT_ANTHROPIC_API_KEY, TAKT_OPENAI_API_KEY
|
# 1) project provider_profiles override
|
||||||
|
# 2) global provider_profiles override
|
||||||
|
# 3) project provider_profiles default
|
||||||
|
# 4) global provider_profiles default
|
||||||
|
# 5) movement.required_permission_mode(下限補正)
|
||||||
|
# provider_profiles:
|
||||||
|
# codex:
|
||||||
|
# default_permission_mode: full
|
||||||
|
# movement_permission_overrides:
|
||||||
|
# ai_review: readonly
|
||||||
|
# claude:
|
||||||
|
# default_permission_mode: edit
|
||||||
|
|
||||||
|
# プロバイダー別ランタイムオプション
|
||||||
|
# provider_options:
|
||||||
|
# codex:
|
||||||
|
# network_access: true
|
||||||
|
# claude:
|
||||||
|
# sandbox:
|
||||||
|
# allow_unsandboxed_commands: true
|
||||||
|
|
||||||
|
# ---- API キー ----
|
||||||
|
# 環境変数が優先:
|
||||||
|
# TAKT_ANTHROPIC_API_KEY / TAKT_OPENAI_API_KEY / TAKT_OPENCODE_API_KEY
|
||||||
# anthropic_api_key: ""
|
# anthropic_api_key: ""
|
||||||
# openai_api_key: ""
|
# openai_api_key: ""
|
||||||
|
# opencode_api_key: ""
|
||||||
|
|
||||||
# ── 実行設定 ──
|
# ---- ランタイム ----
|
||||||
|
# グローバルなランタイム準備(piece_config.runtime があればそちらを優先)
|
||||||
# ワークツリー(shared clone)ディレクトリ(デフォルト: プロジェクトの ../{clone-name})
|
|
||||||
# worktree_dir: ~/takt-worktrees
|
|
||||||
|
|
||||||
# ワークツリー実行後に自動PR作成(デフォルト: 対話モードで確認)
|
|
||||||
# auto_pr: false
|
|
||||||
|
|
||||||
# macOS のアイドルスリープを防止(デフォルト: false)
|
|
||||||
# prevent_sleep: false
|
|
||||||
|
|
||||||
# 実行時ランタイム環境のデフォルト(piece_config.runtime があればそちらを優先)
|
|
||||||
# runtime:
|
# runtime:
|
||||||
# prepare:
|
# prepare:
|
||||||
# - gradle
|
# - gradle
|
||||||
# - node
|
# - node
|
||||||
|
|
||||||
# ── 並列実行 (takt run) ──
|
# ---- 実行 ----
|
||||||
|
# worktree_dir: ~/takt-worktrees
|
||||||
|
# auto_pr: false
|
||||||
|
# prevent_sleep: false
|
||||||
|
|
||||||
# タスクの同時実行数(1 = 逐次実行、最大: 10)
|
# ---- Run Loop ----
|
||||||
# concurrency: 1
|
# concurrency: 1
|
||||||
|
|
||||||
# 新規タスクのポーリング間隔 ms(100-5000、デフォルト: 500)
|
|
||||||
# task_poll_interval_ms: 500
|
# task_poll_interval_ms: 500
|
||||||
|
|
||||||
# ── 対話モード ──
|
|
||||||
|
|
||||||
# ムーブメントプレビューの表示数(0 で無効、最大: 10)
|
|
||||||
# interactive_preview_movements: 3
|
# interactive_preview_movements: 3
|
||||||
|
|
||||||
# ブランチ名の生成方式(romaji: 高速デフォルト | ai: 低速)
|
|
||||||
# branch_name_strategy: romaji
|
# branch_name_strategy: romaji
|
||||||
|
|
||||||
# ── 出力 ──
|
# ---- 出力 ----
|
||||||
|
|
||||||
# 通知音(デフォルト: true)
|
|
||||||
# notification_sound: true
|
|
||||||
|
|
||||||
# CI 向け最小出力 - AI 出力を抑制(デフォルト: false)
|
|
||||||
# minimal_output: false
|
# minimal_output: false
|
||||||
|
# notification_sound: true
|
||||||
|
# notification_sound_events:
|
||||||
|
# iteration_limit: true
|
||||||
|
# piece_complete: true
|
||||||
|
# piece_abort: true
|
||||||
|
# run_complete: true
|
||||||
|
# run_abort: true
|
||||||
|
# observability:
|
||||||
|
# provider_events: true
|
||||||
|
|
||||||
# ── ビルトインピース ──
|
# ---- Builtins ----
|
||||||
|
|
||||||
# ビルトインピースの有効化(デフォルト: true)
|
|
||||||
# enable_builtin_pieces: true
|
# enable_builtin_pieces: true
|
||||||
|
|
||||||
# 特定のビルトインを除外
|
|
||||||
# disabled_builtins:
|
# disabled_builtins:
|
||||||
# - magi
|
# - magi
|
||||||
|
|
||||||
# ── パイプラインモード (--pipeline) ──
|
# ---- Pipeline ----
|
||||||
|
|
||||||
# pipeline:
|
# pipeline:
|
||||||
# default_branch_prefix: "takt/"
|
# default_branch_prefix: "takt/"
|
||||||
# commit_message_template: "feat: {title} (#{issue})"
|
# commit_message_template: "feat: {title} (#{issue})"
|
||||||
@ -94,14 +95,11 @@ provider: claude
|
|||||||
# {issue_body}
|
# {issue_body}
|
||||||
# Closes #{issue}
|
# Closes #{issue}
|
||||||
|
|
||||||
# ── プリファレンス ──
|
# ---- Preferences ----
|
||||||
|
|
||||||
# プリファレンスファイルのカスタムパス
|
|
||||||
# bookmarks_file: ~/.takt/preferences/bookmarks.yaml
|
# bookmarks_file: ~/.takt/preferences/bookmarks.yaml
|
||||||
# piece_categories_file: ~/.takt/preferences/piece-categories.yaml
|
# piece_categories_file: ~/.takt/preferences/piece-categories.yaml
|
||||||
|
|
||||||
# ── デバッグ ──
|
# ---- Debug ----
|
||||||
|
|
||||||
# debug:
|
# debug:
|
||||||
# enabled: false
|
# enabled: false
|
||||||
# log_file: ~/.takt/logs/debug.log
|
# log_file: ~/.takt/logs/debug.log
|
||||||
|
|||||||
@ -219,7 +219,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 修正が完了した
|
- condition: 修正が完了した
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -216,7 +216,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 修正が完了した
|
- condition: 修正が完了した
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -51,7 +51,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 実装完了
|
- condition: 実装完了
|
||||||
next: reviewers
|
next: reviewers
|
||||||
@ -132,7 +132,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 修正完了
|
- condition: 修正完了
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -82,7 +82,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 実装完了
|
- condition: 実装完了
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -142,7 +142,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI問題の修正完了
|
- condition: AI問題の修正完了
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -234,7 +234,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 修正完了
|
- condition: 修正完了
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -85,7 +85,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: テスト実装完了
|
- condition: テスト実装完了
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -145,7 +145,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI問題の修正完了
|
- condition: AI問題の修正完了
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -212,7 +212,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 修正完了
|
- condition: 修正完了
|
||||||
next: review_test
|
next: review_test
|
||||||
|
|||||||
@ -254,7 +254,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 修正が完了した
|
- condition: 修正が完了した
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -251,7 +251,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 修正が完了した
|
- condition: 修正が完了した
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -235,7 +235,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 修正が完了した
|
- condition: 修正が完了した
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -25,7 +25,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction: implement
|
instruction: implement
|
||||||
rules:
|
rules:
|
||||||
- condition: 実装が完了した
|
- condition: 実装が完了した
|
||||||
@ -106,7 +106,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI問題の修正完了
|
- condition: AI問題の修正完了
|
||||||
- condition: 修正不要(指摘対象ファイル/仕様の確認済み)
|
- condition: 修正不要(指摘対象ファイル/仕様の確認済み)
|
||||||
@ -126,7 +126,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 監督者の指摘に対する修正が完了した
|
- condition: 監督者の指摘に対する修正が完了した
|
||||||
- condition: 修正を進行できない
|
- condition: 修正を進行できない
|
||||||
@ -151,7 +151,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI問題の修正完了
|
- condition: AI問題の修正完了
|
||||||
next: reviewers
|
next: reviewers
|
||||||
@ -175,7 +175,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 監督者の指摘に対する修正が完了した
|
- condition: 監督者の指摘に対する修正が完了した
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -25,7 +25,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: タスク完了
|
- condition: タスク完了
|
||||||
next: COMPLETE
|
next: COMPLETE
|
||||||
|
|||||||
@ -25,7 +25,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction: implement
|
instruction: implement
|
||||||
rules:
|
rules:
|
||||||
- condition: 実装が完了した
|
- condition: 実装が完了した
|
||||||
@ -106,7 +106,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI問題の修正完了
|
- condition: AI問題の修正完了
|
||||||
- condition: 修正不要(指摘対象ファイル/仕様の確認済み)
|
- condition: 修正不要(指摘対象ファイル/仕様の確認済み)
|
||||||
@ -126,7 +126,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 監督者の指摘に対する修正が完了した
|
- condition: 監督者の指摘に対する修正が完了した
|
||||||
- condition: 修正を進行できない
|
- condition: 修正を進行できない
|
||||||
@ -151,7 +151,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI問題の修正完了
|
- condition: AI問題の修正完了
|
||||||
next: reviewers
|
next: reviewers
|
||||||
@ -175,7 +175,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 監督者の指摘に対する修正が完了した
|
- condition: 監督者の指摘に対する修正が完了した
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -226,7 +226,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction: implement
|
instruction: implement
|
||||||
rules:
|
rules:
|
||||||
- condition: 実装完了
|
- condition: 実装完了
|
||||||
@ -309,7 +309,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 修正完了
|
- condition: 修正完了
|
||||||
next: reviewers
|
next: reviewers
|
||||||
|
|||||||
@ -85,7 +85,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: テスト実装完了
|
- condition: テスト実装完了
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -145,7 +145,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: AI問題の修正完了
|
- condition: AI問題の修正完了
|
||||||
next: ai_review
|
next: ai_review
|
||||||
@ -212,7 +212,7 @@ movements:
|
|||||||
- Bash
|
- Bash
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- WebFetch
|
- WebFetch
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
rules:
|
rules:
|
||||||
- condition: 修正完了
|
- condition: 修正完了
|
||||||
next: review_test
|
next: review_test
|
||||||
|
|||||||
@ -51,7 +51,7 @@ movement 内では**キー名**で参照する(パスを直接書かない)
|
|||||||
instruction: implement # 指示テンプレートキー(instructions マップを参照、任意)
|
instruction: implement # 指示テンプレートキー(instructions マップを参照、任意)
|
||||||
knowledge: architecture # ナレッジキー(knowledge マップを参照、任意)
|
knowledge: architecture # ナレッジキー(knowledge マップを参照、任意)
|
||||||
edit: true # ファイル編集可否(必須)
|
edit: true # ファイル編集可否(必須)
|
||||||
permission_mode: edit # 権限モード: edit / readonly / full(任意)
|
required_permission_mode: edit # 必要最小権限: edit / readonly / full(任意)
|
||||||
session: refresh # セッション管理(任意)
|
session: refresh # セッション管理(任意)
|
||||||
pass_previous_response: true # 前の出力を渡すか(デフォルト: true)
|
pass_previous_response: true # 前の出力を渡すか(デフォルト: true)
|
||||||
allowed_tools: [...] # 許可ツール一覧(任意、参考情報)
|
allowed_tools: [...] # 許可ツール一覧(任意、参考情報)
|
||||||
|
|||||||
@ -36,7 +36,7 @@ movements:
|
|||||||
- name: fix
|
- name: fix
|
||||||
persona: ../agents/test-coder.md
|
persona: ../agents/test-coder.md
|
||||||
edit: true
|
edit: true
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction_template: |
|
instruction_template: |
|
||||||
Fix the issues found in review.
|
Fix the issues found in review.
|
||||||
rules:
|
rules:
|
||||||
|
|||||||
@ -16,7 +16,7 @@ movements:
|
|||||||
- name: step-a
|
- name: step-a
|
||||||
edit: true
|
edit: true
|
||||||
persona: ../agents/test-coder.md
|
persona: ../agents/test-coder.md
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction_template: |
|
instruction_template: |
|
||||||
{task}
|
{task}
|
||||||
rules:
|
rules:
|
||||||
@ -26,7 +26,7 @@ movements:
|
|||||||
- name: step-b
|
- name: step-b
|
||||||
edit: true
|
edit: true
|
||||||
persona: ../agents/test-coder.md
|
persona: ../agents/test-coder.md
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction_template: |
|
instruction_template: |
|
||||||
Continue the task.
|
Continue the task.
|
||||||
rules:
|
rules:
|
||||||
|
|||||||
@ -14,7 +14,7 @@ movements:
|
|||||||
- name: execute
|
- name: execute
|
||||||
edit: true
|
edit: true
|
||||||
persona: ../agents/test-coder.md
|
persona: ../agents/test-coder.md
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction_template: |
|
instruction_template: |
|
||||||
{task}
|
{task}
|
||||||
rules:
|
rules:
|
||||||
|
|||||||
@ -18,7 +18,7 @@ movements:
|
|||||||
- Read
|
- Read
|
||||||
- Write
|
- Write
|
||||||
- Edit
|
- Edit
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction_template: |
|
instruction_template: |
|
||||||
{task}
|
{task}
|
||||||
rules:
|
rules:
|
||||||
|
|||||||
@ -16,7 +16,7 @@ movements:
|
|||||||
- name: step-1
|
- name: step-1
|
||||||
edit: true
|
edit: true
|
||||||
persona: ../agents/test-coder.md
|
persona: ../agents/test-coder.md
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction_template: |
|
instruction_template: |
|
||||||
{task}
|
{task}
|
||||||
rules:
|
rules:
|
||||||
@ -26,7 +26,7 @@ movements:
|
|||||||
- name: step-2
|
- name: step-2
|
||||||
edit: true
|
edit: true
|
||||||
persona: ../agents/test-coder.md
|
persona: ../agents/test-coder.md
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction_template: |
|
instruction_template: |
|
||||||
Continue the task.
|
Continue the task.
|
||||||
rules:
|
rules:
|
||||||
|
|||||||
@ -16,7 +16,7 @@ movements:
|
|||||||
- name: plan
|
- name: plan
|
||||||
persona: ../agents/test-coder.md
|
persona: ../agents/test-coder.md
|
||||||
edit: true
|
edit: true
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction_template: |
|
instruction_template: |
|
||||||
Create a plan for the task.
|
Create a plan for the task.
|
||||||
rules:
|
rules:
|
||||||
@ -48,7 +48,7 @@ movements:
|
|||||||
- name: fix
|
- name: fix
|
||||||
persona: ../agents/test-coder.md
|
persona: ../agents/test-coder.md
|
||||||
edit: true
|
edit: true
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction_template: |
|
instruction_template: |
|
||||||
Fix the issues found in review.
|
Fix the issues found in review.
|
||||||
rules:
|
rules:
|
||||||
|
|||||||
@ -18,7 +18,7 @@ movements:
|
|||||||
- Read
|
- Read
|
||||||
- Write
|
- Write
|
||||||
- Edit
|
- Edit
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
output_contracts:
|
output_contracts:
|
||||||
report:
|
report:
|
||||||
- Report: report.md
|
- Report: report.md
|
||||||
|
|||||||
@ -18,7 +18,7 @@ movements:
|
|||||||
- Read
|
- Read
|
||||||
- Write
|
- Write
|
||||||
- Edit
|
- Edit
|
||||||
permission_mode: edit
|
required_permission_mode: edit
|
||||||
instruction_template: |
|
instruction_template: |
|
||||||
{task}
|
{task}
|
||||||
rules:
|
rules:
|
||||||
|
|||||||
@ -14,7 +14,7 @@ movements:
|
|||||||
- name: execute
|
- name: execute
|
||||||
edit: false
|
edit: false
|
||||||
persona: ../agents/test-coder.md
|
persona: ../agents/test-coder.md
|
||||||
permission_mode: readonly
|
required_permission_mode: readonly
|
||||||
instruction_template: |
|
instruction_template: |
|
||||||
Reply with exactly: "Task completed successfully."
|
Reply with exactly: "Task completed successfully."
|
||||||
Do not do anything else.
|
Do not do anything else.
|
||||||
|
|||||||
@ -81,7 +81,7 @@ describe('E2E: runtime.prepare with provider', () => {
|
|||||||
' allowed_tools:',
|
' allowed_tools:',
|
||||||
' - Read',
|
' - Read',
|
||||||
' - Bash',
|
' - Bash',
|
||||||
' permission_mode: edit',
|
' required_permission_mode: edit',
|
||||||
' instruction_template: |',
|
' instruction_template: |',
|
||||||
' {task}',
|
' {task}',
|
||||||
' rules:',
|
' rules:',
|
||||||
|
|||||||
75
src/__tests__/global-provider-profiles.test.ts
Normal file
75
src/__tests__/global-provider-profiles.test.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { mkdirSync, rmSync, writeFileSync, existsSync } from 'node:fs';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import { vi } from 'vitest';
|
||||||
|
|
||||||
|
const testHomeDir = join(tmpdir(), `takt-gpp-test-${Date.now()}`);
|
||||||
|
|
||||||
|
vi.mock('node:os', async () => {
|
||||||
|
const actual = await vi.importActual('node:os');
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
homedir: () => testHomeDir,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const { loadGlobalConfig, saveGlobalConfig, invalidateGlobalConfigCache } = await import('../infra/config/global/globalConfig.js');
|
||||||
|
const { getGlobalConfigPath } = await import('../infra/config/paths.js');
|
||||||
|
|
||||||
|
describe('global provider_profiles', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
invalidateGlobalConfigCache();
|
||||||
|
mkdirSync(testHomeDir, { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (existsSync(testHomeDir)) {
|
||||||
|
rmSync(testHomeDir, { recursive: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads provider_profiles from yaml', () => {
|
||||||
|
const taktDir = join(testHomeDir, '.takt');
|
||||||
|
mkdirSync(taktDir, { recursive: true });
|
||||||
|
writeFileSync(
|
||||||
|
getGlobalConfigPath(),
|
||||||
|
[
|
||||||
|
'language: en',
|
||||||
|
'provider_profiles:',
|
||||||
|
' codex:',
|
||||||
|
' default_permission_mode: full',
|
||||||
|
' movement_permission_overrides:',
|
||||||
|
' ai_fix: edit',
|
||||||
|
].join('\n'),
|
||||||
|
'utf-8',
|
||||||
|
);
|
||||||
|
|
||||||
|
const config = loadGlobalConfig();
|
||||||
|
|
||||||
|
expect(config.providerProfiles?.codex?.defaultPermissionMode).toBe('full');
|
||||||
|
expect(config.providerProfiles?.codex?.movementPermissionOverrides?.ai_fix).toBe('edit');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('saves provider_profiles to yaml', () => {
|
||||||
|
const taktDir = join(testHomeDir, '.takt');
|
||||||
|
mkdirSync(taktDir, { recursive: true });
|
||||||
|
writeFileSync(getGlobalConfigPath(), 'language: en\n', 'utf-8');
|
||||||
|
|
||||||
|
const config = loadGlobalConfig();
|
||||||
|
config.providerProfiles = {
|
||||||
|
codex: {
|
||||||
|
defaultPermissionMode: 'full',
|
||||||
|
movementPermissionOverrides: {
|
||||||
|
supervise: 'full',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
saveGlobalConfig(config);
|
||||||
|
invalidateGlobalConfigCache();
|
||||||
|
|
||||||
|
const reloaded = loadGlobalConfig();
|
||||||
|
expect(reloaded.providerProfiles?.codex?.defaultPermissionMode).toBe('full');
|
||||||
|
expect(reloaded.providerProfiles?.codex?.movementPermissionOverrides?.supervise).toBe('full');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -84,7 +84,7 @@ describe('PieceConfigRawSchema', () => {
|
|||||||
expect(result.max_movements).toBe(10);
|
expect(result.max_movements).toBe(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse movement with permission_mode', () => {
|
it('should parse movement with required_permission_mode', () => {
|
||||||
const config = {
|
const config = {
|
||||||
name: 'test-piece',
|
name: 'test-piece',
|
||||||
movements: [
|
movements: [
|
||||||
@ -92,7 +92,7 @@ describe('PieceConfigRawSchema', () => {
|
|||||||
name: 'implement',
|
name: 'implement',
|
||||||
persona: 'coder',
|
persona: 'coder',
|
||||||
allowed_tools: ['Read', 'Edit', 'Write', 'Bash'],
|
allowed_tools: ['Read', 'Edit', 'Write', 'Bash'],
|
||||||
permission_mode: 'edit',
|
required_permission_mode: 'edit',
|
||||||
instruction: '{task}',
|
instruction: '{task}',
|
||||||
rules: [
|
rules: [
|
||||||
{ condition: 'Done', next: 'COMPLETE' },
|
{ condition: 'Done', next: 'COMPLETE' },
|
||||||
@ -102,7 +102,7 @@ describe('PieceConfigRawSchema', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const result = PieceConfigRawSchema.parse(config);
|
const result = PieceConfigRawSchema.parse(config);
|
||||||
expect(result.movements![0]?.permission_mode).toBe('edit');
|
expect(result.movements![0]?.required_permission_mode).toBe('edit');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse movement with provider_options', () => {
|
it('should parse movement with provider_options', () => {
|
||||||
@ -177,7 +177,7 @@ describe('PieceConfigRawSchema', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow omitting permission_mode', () => {
|
it('should allow omitting required_permission_mode', () => {
|
||||||
const config = {
|
const config = {
|
||||||
name: 'test-piece',
|
name: 'test-piece',
|
||||||
movements: [
|
movements: [
|
||||||
@ -190,17 +190,33 @@ describe('PieceConfigRawSchema', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const result = PieceConfigRawSchema.parse(config);
|
const result = PieceConfigRawSchema.parse(config);
|
||||||
expect(result.movements![0]?.permission_mode).toBeUndefined();
|
expect(result.movements![0]?.required_permission_mode).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reject invalid permission_mode', () => {
|
it('should reject invalid required_permission_mode', () => {
|
||||||
const config = {
|
const config = {
|
||||||
name: 'test-piece',
|
name: 'test-piece',
|
||||||
movements: [
|
movements: [
|
||||||
{
|
{
|
||||||
name: 'step1',
|
name: 'step1',
|
||||||
persona: 'coder',
|
persona: 'coder',
|
||||||
permission_mode: 'superAdmin',
|
required_permission_mode: 'superAdmin',
|
||||||
|
instruction: '{task}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => PieceConfigRawSchema.parse(config)).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject legacy permission_mode', () => {
|
||||||
|
const config = {
|
||||||
|
name: 'test-piece',
|
||||||
|
movements: [
|
||||||
|
{
|
||||||
|
name: 'step1',
|
||||||
|
persona: 'coder',
|
||||||
|
permission_mode: 'edit',
|
||||||
instruction: '{task}',
|
instruction: '{task}',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -3,19 +3,26 @@ import { OptionsBuilder } from '../core/piece/engine/OptionsBuilder.js';
|
|||||||
import type { PieceMovement } from '../core/models/types.js';
|
import type { PieceMovement } from '../core/models/types.js';
|
||||||
import type { PieceEngineOptions } from '../core/piece/types.js';
|
import type { PieceEngineOptions } from '../core/piece/types.js';
|
||||||
|
|
||||||
function createMovement(): PieceMovement {
|
function createMovement(overrides: Partial<PieceMovement> = {}): PieceMovement {
|
||||||
return {
|
return {
|
||||||
name: 'reviewers',
|
name: 'reviewers',
|
||||||
personaDisplayName: 'Reviewers',
|
personaDisplayName: 'Reviewers',
|
||||||
instructionTemplate: 'review',
|
instructionTemplate: 'review',
|
||||||
passPreviousResponse: false,
|
passPreviousResponse: false,
|
||||||
permissionMode: 'full',
|
...overrides,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createBuilder(step: PieceMovement): OptionsBuilder {
|
function createBuilder(step: PieceMovement, engineOverrides: Partial<PieceEngineOptions> = {}): OptionsBuilder {
|
||||||
const engineOptions: PieceEngineOptions = {
|
const engineOptions: PieceEngineOptions = {
|
||||||
projectCwd: '/project',
|
projectCwd: '/project',
|
||||||
|
globalProvider: 'codex',
|
||||||
|
globalProviderProfiles: {
|
||||||
|
codex: {
|
||||||
|
defaultPermissionMode: 'full',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...engineOverrides,
|
||||||
};
|
};
|
||||||
|
|
||||||
return new OptionsBuilder(
|
return new OptionsBuilder(
|
||||||
@ -31,10 +38,43 @@ function createBuilder(step: PieceMovement): OptionsBuilder {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('OptionsBuilder.buildBaseOptions', () => {
|
||||||
|
it('resolves permission mode using provider profiles', () => {
|
||||||
|
const step = createMovement();
|
||||||
|
const builder = createBuilder(step);
|
||||||
|
|
||||||
|
const options = builder.buildBaseOptions(step);
|
||||||
|
|
||||||
|
expect(options.permissionMode).toBe('full');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies movement requiredPermissionMode as minimum floor', () => {
|
||||||
|
const step = createMovement({ requiredPermissionMode: 'full' });
|
||||||
|
const builder = createBuilder(step);
|
||||||
|
|
||||||
|
const options = builder.buildBaseOptions(step);
|
||||||
|
|
||||||
|
expect(options.permissionMode).toBe('full');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses default profile when provider_profiles are not provided', () => {
|
||||||
|
const step = createMovement();
|
||||||
|
const builder = createBuilder(step, {
|
||||||
|
globalProvider: undefined,
|
||||||
|
globalProviderProfiles: undefined,
|
||||||
|
projectProvider: undefined,
|
||||||
|
provider: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const options = builder.buildBaseOptions(step);
|
||||||
|
expect(options.permissionMode).toBe('edit');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('OptionsBuilder.buildResumeOptions', () => {
|
describe('OptionsBuilder.buildResumeOptions', () => {
|
||||||
it('should enforce readonly permission and empty allowedTools for report/status phases', () => {
|
it('should enforce readonly permission and empty allowedTools for report/status phases', () => {
|
||||||
// Given
|
// Given
|
||||||
const step = createMovement();
|
const step = createMovement({ requiredPermissionMode: 'full' });
|
||||||
const builder = createBuilder(step);
|
const builder = createBuilder(step);
|
||||||
|
|
||||||
// When
|
// When
|
||||||
|
|||||||
61
src/__tests__/permission-profile-resolution.test.ts
Normal file
61
src/__tests__/permission-profile-resolution.test.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { resolveMovementPermissionMode } from '../core/piece/permission-profile-resolution.js';
|
||||||
|
|
||||||
|
describe('resolveMovementPermissionMode', () => {
|
||||||
|
it('applies required_permission_mode as minimum floor', () => {
|
||||||
|
const mode = resolveMovementPermissionMode({
|
||||||
|
movementName: 'implement',
|
||||||
|
requiredPermissionMode: 'full',
|
||||||
|
provider: 'codex',
|
||||||
|
projectProviderProfiles: {
|
||||||
|
codex: {
|
||||||
|
defaultPermissionMode: 'readonly',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mode).toBe('full');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resolves by priority: project override > global override > project default > global default', () => {
|
||||||
|
const mode = resolveMovementPermissionMode({
|
||||||
|
movementName: 'supervise',
|
||||||
|
provider: 'codex',
|
||||||
|
projectProviderProfiles: {
|
||||||
|
codex: {
|
||||||
|
defaultPermissionMode: 'edit',
|
||||||
|
movementPermissionOverrides: {
|
||||||
|
supervise: 'full',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
globalProviderProfiles: {
|
||||||
|
codex: {
|
||||||
|
defaultPermissionMode: 'readonly',
|
||||||
|
movementPermissionOverrides: {
|
||||||
|
supervise: 'edit',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mode).toBe('full');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws when unresolved', () => {
|
||||||
|
expect(() => resolveMovementPermissionMode({
|
||||||
|
movementName: 'fix',
|
||||||
|
provider: 'codex',
|
||||||
|
})).toThrow(/Unable to resolve permission mode/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resolves from required_permission_mode when provider is omitted', () => {
|
||||||
|
const mode = resolveMovementPermissionMode({
|
||||||
|
movementName: 'fix',
|
||||||
|
requiredPermissionMode: 'edit',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mode).toBe('edit');
|
||||||
|
});
|
||||||
|
});
|
||||||
62
src/__tests__/project-provider-profiles.test.ts
Normal file
62
src/__tests__/project-provider-profiles.test.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { mkdirSync, rmSync, writeFileSync, existsSync } from 'node:fs';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import { randomUUID } from 'node:crypto';
|
||||||
|
|
||||||
|
import { loadProjectConfig, saveProjectConfig } from '../infra/config/project/projectConfig.js';
|
||||||
|
|
||||||
|
describe('project provider_profiles', () => {
|
||||||
|
let testDir: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
testDir = join(tmpdir(), `takt-project-profile-${randomUUID()}`);
|
||||||
|
mkdirSync(testDir, { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (existsSync(testDir)) {
|
||||||
|
rmSync(testDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads provider_profiles from project config', () => {
|
||||||
|
const taktDir = join(testDir, '.takt');
|
||||||
|
mkdirSync(taktDir, { recursive: true });
|
||||||
|
writeFileSync(
|
||||||
|
join(taktDir, 'config.yaml'),
|
||||||
|
[
|
||||||
|
'piece: default',
|
||||||
|
'provider_profiles:',
|
||||||
|
' codex:',
|
||||||
|
' default_permission_mode: full',
|
||||||
|
' movement_permission_overrides:',
|
||||||
|
' implement: full',
|
||||||
|
].join('\n'),
|
||||||
|
'utf-8',
|
||||||
|
);
|
||||||
|
|
||||||
|
const config = loadProjectConfig(testDir);
|
||||||
|
|
||||||
|
expect(config.providerProfiles?.codex?.defaultPermissionMode).toBe('full');
|
||||||
|
expect(config.providerProfiles?.codex?.movementPermissionOverrides?.implement).toBe('full');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('saves providerProfiles as provider_profiles', () => {
|
||||||
|
saveProjectConfig(testDir, {
|
||||||
|
piece: 'default',
|
||||||
|
providerProfiles: {
|
||||||
|
codex: {
|
||||||
|
defaultPermissionMode: 'full',
|
||||||
|
movementPermissionOverrides: {
|
||||||
|
fix: 'full',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const config = loadProjectConfig(testDir);
|
||||||
|
expect(config.providerProfiles?.codex?.defaultPermissionMode).toBe('full');
|
||||||
|
expect(config.providerProfiles?.codex?.movementPermissionOverrides?.fix).toBe('full');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { MovementProviderOptions, PieceRuntimeConfig } from './piece-types.js';
|
import type { MovementProviderOptions, PieceRuntimeConfig } from './piece-types.js';
|
||||||
|
import type { ProviderPermissionProfiles } from './provider-profiles.js';
|
||||||
|
|
||||||
/** Custom agent configuration */
|
/** Custom agent configuration */
|
||||||
export interface CustomAgentConfig {
|
export interface CustomAgentConfig {
|
||||||
@ -90,6 +91,8 @@ export interface GlobalConfig {
|
|||||||
personaProviders?: Record<string, 'claude' | 'codex' | 'opencode' | 'mock'>;
|
personaProviders?: Record<string, 'claude' | 'codex' | 'opencode' | 'mock'>;
|
||||||
/** Global provider-specific options (lowest priority) */
|
/** Global provider-specific options (lowest priority) */
|
||||||
providerOptions?: MovementProviderOptions;
|
providerOptions?: MovementProviderOptions;
|
||||||
|
/** Provider-specific permission profiles */
|
||||||
|
providerProfiles?: ProviderPermissionProfiles;
|
||||||
/** Global runtime environment defaults (can be overridden by piece runtime) */
|
/** Global runtime environment defaults (can be overridden by piece runtime) */
|
||||||
runtime?: PieceRuntimeConfig;
|
runtime?: PieceRuntimeConfig;
|
||||||
/** Branch name generation strategy: 'romaji' (fast, default) or 'ai' (slow) */
|
/** Branch name generation strategy: 'romaji' (fast, default) or 'ai' (slow) */
|
||||||
@ -114,4 +117,6 @@ export interface ProjectConfig {
|
|||||||
agents?: CustomAgentConfig[];
|
agents?: CustomAgentConfig[];
|
||||||
provider?: 'claude' | 'codex' | 'opencode' | 'mock';
|
provider?: 'claude' | 'codex' | 'opencode' | 'mock';
|
||||||
providerOptions?: MovementProviderOptions;
|
providerOptions?: MovementProviderOptions;
|
||||||
|
/** Provider-specific permission profiles */
|
||||||
|
providerProfiles?: ProviderPermissionProfiles;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,9 @@ export type {
|
|||||||
PipelineConfig,
|
PipelineConfig,
|
||||||
GlobalConfig,
|
GlobalConfig,
|
||||||
ProjectConfig,
|
ProjectConfig,
|
||||||
|
ProviderProfileName,
|
||||||
|
ProviderPermissionProfile,
|
||||||
|
ProviderPermissionProfiles,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
|
|
||||||
// Re-export from agent.ts
|
// Re-export from agent.ts
|
||||||
|
|||||||
@ -144,8 +144,8 @@ export interface PieceMovement {
|
|||||||
provider?: 'claude' | 'codex' | 'opencode' | 'mock';
|
provider?: 'claude' | 'codex' | 'opencode' | 'mock';
|
||||||
/** Model override for this movement */
|
/** Model override for this movement */
|
||||||
model?: string;
|
model?: string;
|
||||||
/** Permission mode for tool execution in this movement */
|
/** Required minimum permission mode for tool execution in this movement */
|
||||||
permissionMode?: PermissionMode;
|
requiredPermissionMode?: PermissionMode;
|
||||||
/** Provider-specific movement options */
|
/** Provider-specific movement options */
|
||||||
providerOptions?: MovementProviderOptions;
|
providerOptions?: MovementProviderOptions;
|
||||||
/** Whether this movement is allowed to edit project files (true=allowed, false=prohibited, undefined=no prompt) */
|
/** Whether this movement is allowed to edit project files (true=allowed, false=prohibited, undefined=no prompt) */
|
||||||
|
|||||||
19
src/core/models/provider-profiles.ts
Normal file
19
src/core/models/provider-profiles.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Provider-specific permission profile types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { PermissionMode } from './status.js';
|
||||||
|
|
||||||
|
/** Supported providers for profile-based permission resolution. */
|
||||||
|
export type ProviderProfileName = 'claude' | 'codex' | 'opencode' | 'mock';
|
||||||
|
|
||||||
|
/** Permission profile for a single provider. */
|
||||||
|
export interface ProviderPermissionProfile {
|
||||||
|
/** Default permission mode for movements that do not have an explicit override. */
|
||||||
|
defaultPermissionMode: PermissionMode;
|
||||||
|
/** Per-movement permission overrides keyed by movement name. */
|
||||||
|
movementPermissionOverrides?: Record<string, PermissionMode>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Provider -> permission profile map. */
|
||||||
|
export type ProviderPermissionProfiles = Partial<Record<ProviderProfileName, ProviderPermissionProfile>>;
|
||||||
@ -78,6 +78,23 @@ export const MovementProviderOptionsSchema = z.object({
|
|||||||
}).optional(),
|
}).optional(),
|
||||||
}).optional();
|
}).optional();
|
||||||
|
|
||||||
|
/** Provider key schema for profile maps */
|
||||||
|
export const ProviderProfileNameSchema = z.enum(['claude', 'codex', 'opencode', 'mock']);
|
||||||
|
|
||||||
|
/** Provider permission profile schema */
|
||||||
|
export const ProviderPermissionProfileSchema = z.object({
|
||||||
|
default_permission_mode: PermissionModeSchema,
|
||||||
|
movement_permission_overrides: z.record(z.string(), PermissionModeSchema).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Provider permission profiles schema */
|
||||||
|
export const ProviderPermissionProfilesSchema = z.object({
|
||||||
|
claude: ProviderPermissionProfileSchema.optional(),
|
||||||
|
codex: ProviderPermissionProfileSchema.optional(),
|
||||||
|
opencode: ProviderPermissionProfileSchema.optional(),
|
||||||
|
mock: ProviderPermissionProfileSchema.optional(),
|
||||||
|
}).optional();
|
||||||
|
|
||||||
/** Runtime prepare preset identifiers */
|
/** Runtime prepare preset identifiers */
|
||||||
export const RuntimePreparePresetSchema = z.enum(['gradle', 'node']);
|
export const RuntimePreparePresetSchema = z.enum(['gradle', 'node']);
|
||||||
/** Runtime prepare entry: preset name or script path */
|
/** Runtime prepare entry: preset name or script path */
|
||||||
@ -240,7 +257,9 @@ export const ParallelSubMovementRawSchema = z.object({
|
|||||||
mcp_servers: McpServersSchema,
|
mcp_servers: McpServersSchema,
|
||||||
provider: z.enum(['claude', 'codex', 'opencode', 'mock']).optional(),
|
provider: z.enum(['claude', 'codex', 'opencode', 'mock']).optional(),
|
||||||
model: z.string().optional(),
|
model: z.string().optional(),
|
||||||
permission_mode: PermissionModeSchema.optional(),
|
/** Removed legacy field (no backward compatibility) */
|
||||||
|
permission_mode: z.never().optional(),
|
||||||
|
required_permission_mode: PermissionModeSchema.optional(),
|
||||||
provider_options: MovementProviderOptionsSchema,
|
provider_options: MovementProviderOptionsSchema,
|
||||||
edit: z.boolean().optional(),
|
edit: z.boolean().optional(),
|
||||||
instruction: z.string().optional(),
|
instruction: z.string().optional(),
|
||||||
@ -271,8 +290,10 @@ export const PieceMovementRawSchema = z.object({
|
|||||||
mcp_servers: McpServersSchema,
|
mcp_servers: McpServersSchema,
|
||||||
provider: z.enum(['claude', 'codex', 'opencode', 'mock']).optional(),
|
provider: z.enum(['claude', 'codex', 'opencode', 'mock']).optional(),
|
||||||
model: z.string().optional(),
|
model: z.string().optional(),
|
||||||
/** Permission mode for tool execution in this movement */
|
/** Removed legacy field (no backward compatibility) */
|
||||||
permission_mode: PermissionModeSchema.optional(),
|
permission_mode: z.never().optional(),
|
||||||
|
/** Required minimum permission mode for tool execution in this movement */
|
||||||
|
required_permission_mode: PermissionModeSchema.optional(),
|
||||||
/** Provider-specific movement options */
|
/** Provider-specific movement options */
|
||||||
provider_options: MovementProviderOptionsSchema,
|
provider_options: MovementProviderOptionsSchema,
|
||||||
/** Whether this movement is allowed to edit project files */
|
/** Whether this movement is allowed to edit project files */
|
||||||
@ -439,6 +460,8 @@ export const GlobalConfigSchema = z.object({
|
|||||||
persona_providers: z.record(z.string(), z.enum(['claude', 'codex', 'opencode', 'mock'])).optional(),
|
persona_providers: z.record(z.string(), z.enum(['claude', 'codex', 'opencode', 'mock'])).optional(),
|
||||||
/** Global provider-specific options (lowest priority) */
|
/** Global provider-specific options (lowest priority) */
|
||||||
provider_options: MovementProviderOptionsSchema,
|
provider_options: MovementProviderOptionsSchema,
|
||||||
|
/** Provider-specific permission profiles */
|
||||||
|
provider_profiles: ProviderPermissionProfilesSchema,
|
||||||
/** Global runtime defaults (piece runtime overrides this) */
|
/** Global runtime defaults (piece runtime overrides this) */
|
||||||
runtime: RuntimeConfigSchema,
|
runtime: RuntimeConfigSchema,
|
||||||
/** Branch name generation strategy: 'romaji' (fast, default) or 'ai' (slow) */
|
/** Branch name generation strategy: 'romaji' (fast, default) or 'ai' (slow) */
|
||||||
@ -469,4 +492,5 @@ export const ProjectConfigSchema = z.object({
|
|||||||
agents: z.array(CustomAgentConfigSchema).optional(),
|
agents: z.array(CustomAgentConfigSchema).optional(),
|
||||||
provider: z.enum(['claude', 'codex', 'opencode', 'mock']).optional(),
|
provider: z.enum(['claude', 'codex', 'opencode', 'mock']).optional(),
|
||||||
provider_options: MovementProviderOptionsSchema,
|
provider_options: MovementProviderOptionsSchema,
|
||||||
|
provider_profiles: ProviderPermissionProfilesSchema,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -52,6 +52,14 @@ export type {
|
|||||||
PieceState,
|
PieceState,
|
||||||
} from './piece-types.js';
|
} from './piece-types.js';
|
||||||
|
|
||||||
|
|
||||||
|
// Provider permission profiles
|
||||||
|
export type {
|
||||||
|
ProviderProfileName,
|
||||||
|
ProviderPermissionProfile,
|
||||||
|
ProviderPermissionProfiles,
|
||||||
|
} from './provider-profiles.js';
|
||||||
|
|
||||||
// Configuration types (global and project)
|
// Configuration types (global and project)
|
||||||
export type {
|
export type {
|
||||||
CustomAgentConfig,
|
CustomAgentConfig,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import type { PhaseRunnerContext } from '../phase-runner.js';
|
|||||||
import type { PieceEngineOptions, PhaseName } from '../types.js';
|
import type { PieceEngineOptions, PhaseName } from '../types.js';
|
||||||
import { buildSessionKey } from '../session-key.js';
|
import { buildSessionKey } from '../session-key.js';
|
||||||
import { resolveMovementProviderModel } from '../provider-resolution.js';
|
import { resolveMovementProviderModel } from '../provider-resolution.js';
|
||||||
|
import { DEFAULT_PROVIDER_PERMISSION_PROFILES, resolveMovementPermissionMode } from '../permission-profile-resolution.js';
|
||||||
|
|
||||||
export class OptionsBuilder {
|
export class OptionsBuilder {
|
||||||
constructor(
|
constructor(
|
||||||
@ -31,6 +32,13 @@ export class OptionsBuilder {
|
|||||||
personaProviders: this.engineOptions.personaProviders,
|
personaProviders: this.engineOptions.personaProviders,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const resolvedProviderForPermissions =
|
||||||
|
this.engineOptions.provider
|
||||||
|
?? this.engineOptions.projectProvider
|
||||||
|
?? resolved.provider
|
||||||
|
?? this.engineOptions.globalProvider
|
||||||
|
?? 'claude';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cwd: this.getCwd(),
|
cwd: this.getCwd(),
|
||||||
abortSignal: this.engineOptions.abortSignal,
|
abortSignal: this.engineOptions.abortSignal,
|
||||||
@ -39,7 +47,13 @@ export class OptionsBuilder {
|
|||||||
model: this.engineOptions.model,
|
model: this.engineOptions.model,
|
||||||
stepProvider: resolved.provider,
|
stepProvider: resolved.provider,
|
||||||
stepModel: resolved.model,
|
stepModel: resolved.model,
|
||||||
permissionMode: step.permissionMode,
|
permissionMode: resolveMovementPermissionMode({
|
||||||
|
movementName: step.name,
|
||||||
|
requiredPermissionMode: step.requiredPermissionMode,
|
||||||
|
provider: resolvedProviderForPermissions,
|
||||||
|
projectProviderProfiles: this.engineOptions.projectProviderProfiles,
|
||||||
|
globalProviderProfiles: this.engineOptions.globalProviderProfiles ?? DEFAULT_PROVIDER_PERMISSION_PROFILES,
|
||||||
|
}),
|
||||||
providerOptions: step.providerOptions,
|
providerOptions: step.providerOptions,
|
||||||
language: this.getLanguage(),
|
language: this.getLanguage(),
|
||||||
onStream: this.engineOptions.onStream,
|
onStream: this.engineOptions.onStream,
|
||||||
|
|||||||
@ -59,7 +59,7 @@ function createPartMovement(step: PieceMovement, part: PartDefinition): PieceMov
|
|||||||
mcpServers: step.mcpServers,
|
mcpServers: step.mcpServers,
|
||||||
provider: step.provider,
|
provider: step.provider,
|
||||||
model: step.model,
|
model: step.model,
|
||||||
permissionMode: step.teamLeader.partPermissionMode ?? step.permissionMode,
|
requiredPermissionMode: step.teamLeader.partPermissionMode ?? step.requiredPermissionMode,
|
||||||
edit: step.teamLeader.partEdit ?? step.edit,
|
edit: step.teamLeader.partEdit ?? step.edit,
|
||||||
instructionTemplate: part.instruction,
|
instructionTemplate: part.instruction,
|
||||||
passPreviousResponse: false,
|
passPreviousResponse: false,
|
||||||
|
|||||||
88
src/core/piece/permission-profile-resolution.ts
Normal file
88
src/core/piece/permission-profile-resolution.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import type { PermissionMode } from '../models/types.js';
|
||||||
|
import type { ProviderPermissionProfiles, ProviderProfileName } from '../models/provider-profiles.js';
|
||||||
|
|
||||||
|
export interface ResolvePermissionModeInput {
|
||||||
|
movementName: string;
|
||||||
|
requiredPermissionMode?: PermissionMode;
|
||||||
|
provider?: ProviderProfileName;
|
||||||
|
projectProviderProfiles?: ProviderPermissionProfiles;
|
||||||
|
globalProviderProfiles?: ProviderPermissionProfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_PROVIDER_PERMISSION_PROFILES: ProviderPermissionProfiles = {
|
||||||
|
claude: { defaultPermissionMode: 'edit' },
|
||||||
|
codex: { defaultPermissionMode: 'edit' },
|
||||||
|
opencode: { defaultPermissionMode: 'edit' },
|
||||||
|
mock: { defaultPermissionMode: 'edit' },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve movement permission mode using provider profiles.
|
||||||
|
*
|
||||||
|
* Priority:
|
||||||
|
* 1. project provider_profiles.<provider>.movement_permission_overrides.<movement>
|
||||||
|
* 2. global provider_profiles.<provider>.movement_permission_overrides.<movement>
|
||||||
|
* 3. project provider_profiles.<provider>.default_permission_mode
|
||||||
|
* 4. global provider_profiles.<provider>.default_permission_mode
|
||||||
|
* 5. apply movement.required_permission_mode as minimum floor
|
||||||
|
*
|
||||||
|
* Throws when unresolved.
|
||||||
|
*/
|
||||||
|
export function resolveMovementPermissionMode(input: ResolvePermissionModeInput): PermissionMode {
|
||||||
|
if (!input.provider) {
|
||||||
|
if (input.requiredPermissionMode) {
|
||||||
|
return input.requiredPermissionMode;
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`Unable to resolve permission mode for movement "${input.movementName}": provider is required when movement.required_permission_mode is omitted.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectProfile = input.projectProviderProfiles?.[input.provider];
|
||||||
|
const globalProfile = input.globalProviderProfiles?.[input.provider];
|
||||||
|
|
||||||
|
const projectOverride = projectProfile?.movementPermissionOverrides?.[input.movementName];
|
||||||
|
if (projectOverride) {
|
||||||
|
return applyRequiredPermissionFloor(projectOverride, input.requiredPermissionMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
const globalOverride = globalProfile?.movementPermissionOverrides?.[input.movementName];
|
||||||
|
if (globalOverride) {
|
||||||
|
return applyRequiredPermissionFloor(globalOverride, input.requiredPermissionMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectProfile?.defaultPermissionMode) {
|
||||||
|
return applyRequiredPermissionFloor(projectProfile.defaultPermissionMode, input.requiredPermissionMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (globalProfile?.defaultPermissionMode) {
|
||||||
|
return applyRequiredPermissionFloor(globalProfile.defaultPermissionMode, input.requiredPermissionMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.requiredPermissionMode) {
|
||||||
|
return input.requiredPermissionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Unable to resolve permission mode for movement "${input.movementName}" and provider "${input.provider}": ` +
|
||||||
|
'define provider_profiles defaults/overrides or movement.required_permission_mode.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PERMISSION_MODE_RANK: Record<PermissionMode, number> = {
|
||||||
|
readonly: 0,
|
||||||
|
edit: 1,
|
||||||
|
full: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
function applyRequiredPermissionFloor(
|
||||||
|
resolvedMode: PermissionMode,
|
||||||
|
requiredMode?: PermissionMode,
|
||||||
|
): PermissionMode {
|
||||||
|
if (!requiredMode) {
|
||||||
|
return resolvedMode;
|
||||||
|
}
|
||||||
|
return PERMISSION_MODE_RANK[requiredMode] > PERMISSION_MODE_RANK[resolvedMode]
|
||||||
|
? requiredMode
|
||||||
|
: resolvedMode;
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import type { PermissionResult, PermissionUpdate } from '@anthropic-ai/claude-agent-sdk';
|
import type { PermissionResult, PermissionUpdate } from '@anthropic-ai/claude-agent-sdk';
|
||||||
import type { PieceMovement, AgentResponse, PieceState, Language, LoopMonitorConfig } from '../models/types.js';
|
import type { PieceMovement, AgentResponse, PieceState, Language, LoopMonitorConfig } from '../models/types.js';
|
||||||
|
import type { ProviderPermissionProfiles } from '../models/provider-profiles.js';
|
||||||
|
|
||||||
export type ProviderType = 'claude' | 'codex' | 'opencode' | 'mock';
|
export type ProviderType = 'claude' | 'codex' | 'opencode' | 'mock';
|
||||||
|
|
||||||
@ -177,9 +178,17 @@ export interface PieceEngineOptions {
|
|||||||
/** Language for instruction metadata. Defaults to 'en'. */
|
/** Language for instruction metadata. Defaults to 'en'. */
|
||||||
language?: Language;
|
language?: Language;
|
||||||
provider?: ProviderType;
|
provider?: ProviderType;
|
||||||
|
/** Project config provider (used for provider/profile resolution parity with AgentRunner) */
|
||||||
|
projectProvider?: ProviderType;
|
||||||
|
/** Global config provider (used for provider/profile resolution parity with AgentRunner) */
|
||||||
|
globalProvider?: ProviderType;
|
||||||
model?: string;
|
model?: string;
|
||||||
/** Per-persona provider overrides (e.g., { coder: 'codex' }) */
|
/** Per-persona provider overrides (e.g., { coder: 'codex' }) */
|
||||||
personaProviders?: Record<string, ProviderType>;
|
personaProviders?: Record<string, ProviderType>;
|
||||||
|
/** Project-level provider permission profiles */
|
||||||
|
projectProviderProfiles?: ProviderPermissionProfiles;
|
||||||
|
/** Global-level provider permission profiles */
|
||||||
|
globalProviderProfiles?: ProviderPermissionProfiles;
|
||||||
/** Enable interactive-only rules and user-input transitions */
|
/** Enable interactive-only rules and user-input transitions */
|
||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
/** Rule tag index detector (required for rules evaluation) */
|
/** Rule tag index detector (required for rules evaluation) */
|
||||||
|
|||||||
@ -439,8 +439,12 @@ export async function executePiece(
|
|||||||
projectCwd,
|
projectCwd,
|
||||||
language: options.language,
|
language: options.language,
|
||||||
provider: options.provider,
|
provider: options.provider,
|
||||||
|
projectProvider: options.projectProvider,
|
||||||
|
globalProvider: options.globalProvider,
|
||||||
model: options.model,
|
model: options.model,
|
||||||
personaProviders: options.personaProviders,
|
personaProviders: options.personaProviders,
|
||||||
|
projectProviderProfiles: options.projectProviderProfiles,
|
||||||
|
globalProviderProfiles: options.globalProviderProfiles,
|
||||||
interactive: interactiveUserInput,
|
interactive: interactiveUserInput,
|
||||||
detectRuleIndex,
|
detectRuleIndex,
|
||||||
callAiJudge,
|
callAiJudge,
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
* Task execution logic
|
* Task execution logic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { loadPieceByIdentifier, isPiecePath, loadGlobalConfig } from '../../../infra/config/index.js';
|
import { loadPieceByIdentifier, isPiecePath, loadGlobalConfig, loadProjectConfig } from '../../../infra/config/index.js';
|
||||||
import { TaskRunner, type TaskInfo } from '../../../infra/task/index.js';
|
import { TaskRunner, type TaskInfo } from '../../../infra/task/index.js';
|
||||||
import {
|
import {
|
||||||
header,
|
header,
|
||||||
@ -72,12 +72,17 @@ async function executeTaskWithResult(options: ExecuteTaskOptions): Promise<Piece
|
|||||||
});
|
});
|
||||||
|
|
||||||
const globalConfig = loadGlobalConfig();
|
const globalConfig = loadGlobalConfig();
|
||||||
|
const projectConfig = loadProjectConfig(projectCwd);
|
||||||
return await executePiece(pieceConfig, task, cwd, {
|
return await executePiece(pieceConfig, task, cwd, {
|
||||||
projectCwd,
|
projectCwd,
|
||||||
language: globalConfig.language,
|
language: globalConfig.language,
|
||||||
provider: agentOverrides?.provider,
|
provider: agentOverrides?.provider,
|
||||||
|
projectProvider: projectConfig.provider,
|
||||||
|
globalProvider: globalConfig.provider,
|
||||||
model: agentOverrides?.model,
|
model: agentOverrides?.model,
|
||||||
personaProviders: globalConfig.personaProviders,
|
personaProviders: globalConfig.personaProviders,
|
||||||
|
projectProviderProfiles: projectConfig.providerProfiles,
|
||||||
|
globalProviderProfiles: globalConfig.providerProfiles,
|
||||||
interactiveUserInput,
|
interactiveUserInput,
|
||||||
interactiveMetadata,
|
interactiveMetadata,
|
||||||
startMovement,
|
startMovement,
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Language } from '../../../core/models/index.js';
|
import type { Language } from '../../../core/models/index.js';
|
||||||
|
import type { ProviderPermissionProfiles } from '../../../core/models/provider-profiles.js';
|
||||||
import type { ProviderType } from '../../../infra/providers/index.js';
|
import type { ProviderType } from '../../../infra/providers/index.js';
|
||||||
import type { GitHubIssue } from '../../../infra/github/index.js';
|
import type { GitHubIssue } from '../../../infra/github/index.js';
|
||||||
|
|
||||||
@ -31,9 +32,17 @@ export interface PieceExecutionOptions {
|
|||||||
/** Language for instruction metadata */
|
/** Language for instruction metadata */
|
||||||
language?: Language;
|
language?: Language;
|
||||||
provider?: ProviderType;
|
provider?: ProviderType;
|
||||||
|
/** Project config provider */
|
||||||
|
projectProvider?: ProviderType;
|
||||||
|
/** Global config provider */
|
||||||
|
globalProvider?: ProviderType;
|
||||||
model?: string;
|
model?: string;
|
||||||
/** Per-persona provider overrides (e.g., { coder: 'codex' }) */
|
/** Per-persona provider overrides (e.g., { coder: 'codex' }) */
|
||||||
personaProviders?: Record<string, ProviderType>;
|
personaProviders?: Record<string, ProviderType>;
|
||||||
|
/** Project-level provider permission profiles */
|
||||||
|
projectProviderProfiles?: ProviderPermissionProfiles;
|
||||||
|
/** Global-level provider permission profiles */
|
||||||
|
globalProviderProfiles?: ProviderPermissionProfiles;
|
||||||
/** 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 */
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { readFileSync, existsSync, writeFileSync } from 'node:fs';
|
|||||||
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
||||||
import { GlobalConfigSchema } from '../../../core/models/index.js';
|
import { GlobalConfigSchema } from '../../../core/models/index.js';
|
||||||
import type { GlobalConfig, DebugConfig, Language } from '../../../core/models/index.js';
|
import type { GlobalConfig, DebugConfig, Language } from '../../../core/models/index.js';
|
||||||
|
import type { ProviderPermissionProfiles } from '../../../core/models/provider-profiles.js';
|
||||||
import { normalizeProviderOptions } from '../loaders/pieceParser.js';
|
import { normalizeProviderOptions } from '../loaders/pieceParser.js';
|
||||||
import { getGlobalConfigPath, getProjectConfigPath } from '../paths.js';
|
import { getGlobalConfigPath, getProjectConfigPath } from '../paths.js';
|
||||||
import { DEFAULT_LANGUAGE } from '../../../shared/constants.js';
|
import { DEFAULT_LANGUAGE } from '../../../shared/constants.js';
|
||||||
@ -41,6 +42,34 @@ function validateProviderModelCompatibility(provider: string | undefined, model:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeProviderProfiles(
|
||||||
|
raw: Record<string, { default_permission_mode: unknown; movement_permission_overrides?: Record<string, unknown> }> | undefined,
|
||||||
|
): ProviderPermissionProfiles | undefined {
|
||||||
|
if (!raw) return undefined;
|
||||||
|
|
||||||
|
const entries = Object.entries(raw).map(([provider, profile]) => [provider, {
|
||||||
|
defaultPermissionMode: profile.default_permission_mode,
|
||||||
|
movementPermissionOverrides: profile.movement_permission_overrides,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
return Object.fromEntries(entries) as ProviderPermissionProfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
function denormalizeProviderProfiles(
|
||||||
|
profiles: ProviderPermissionProfiles | undefined,
|
||||||
|
): Record<string, { default_permission_mode: string; movement_permission_overrides?: Record<string, string> }> | undefined {
|
||||||
|
if (!profiles) return undefined;
|
||||||
|
const entries = Object.entries(profiles);
|
||||||
|
if (entries.length === 0) return undefined;
|
||||||
|
|
||||||
|
return Object.fromEntries(entries.map(([provider, profile]) => [provider, {
|
||||||
|
default_permission_mode: profile.defaultPermissionMode,
|
||||||
|
...(profile.movementPermissionOverrides
|
||||||
|
? { movement_permission_overrides: profile.movementPermissionOverrides }
|
||||||
|
: {}),
|
||||||
|
}])) as Record<string, { default_permission_mode: string; movement_permission_overrides?: Record<string, string> }>;
|
||||||
|
}
|
||||||
|
|
||||||
/** Create default global configuration (fresh instance each call) */
|
/** Create default global configuration (fresh instance each call) */
|
||||||
function createDefaultGlobalConfig(): GlobalConfig {
|
function createDefaultGlobalConfig(): GlobalConfig {
|
||||||
return {
|
return {
|
||||||
@ -126,6 +155,7 @@ export class GlobalConfigManager {
|
|||||||
pieceCategoriesFile: parsed.piece_categories_file,
|
pieceCategoriesFile: parsed.piece_categories_file,
|
||||||
personaProviders: parsed.persona_providers,
|
personaProviders: parsed.persona_providers,
|
||||||
providerOptions: normalizeProviderOptions(parsed.provider_options),
|
providerOptions: normalizeProviderOptions(parsed.provider_options),
|
||||||
|
providerProfiles: normalizeProviderProfiles(parsed.provider_profiles as Record<string, { default_permission_mode: unknown; movement_permission_overrides?: Record<string, unknown> }> | undefined),
|
||||||
runtime: parsed.runtime?.prepare && parsed.runtime.prepare.length > 0
|
runtime: parsed.runtime?.prepare && parsed.runtime.prepare.length > 0
|
||||||
? { prepare: [...new Set(parsed.runtime.prepare)] }
|
? { prepare: [...new Set(parsed.runtime.prepare)] }
|
||||||
: undefined,
|
: undefined,
|
||||||
@ -213,6 +243,10 @@ export class GlobalConfigManager {
|
|||||||
if (config.personaProviders && Object.keys(config.personaProviders).length > 0) {
|
if (config.personaProviders && Object.keys(config.personaProviders).length > 0) {
|
||||||
raw.persona_providers = config.personaProviders;
|
raw.persona_providers = config.personaProviders;
|
||||||
}
|
}
|
||||||
|
const rawProviderProfiles = denormalizeProviderProfiles(config.providerProfiles);
|
||||||
|
if (rawProviderProfiles && Object.keys(rawProviderProfiles).length > 0) {
|
||||||
|
raw.provider_profiles = rawProviderProfiles;
|
||||||
|
}
|
||||||
if (config.runtime?.prepare && config.runtime.prepare.length > 0) {
|
if (config.runtime?.prepare && config.runtime.prepare.length > 0) {
|
||||||
raw.runtime = {
|
raw.runtime = {
|
||||||
prepare: [...new Set(config.runtime.prepare)],
|
prepare: [...new Set(config.runtime.prepare)],
|
||||||
|
|||||||
@ -310,7 +310,7 @@ function normalizeStepFromRaw(
|
|||||||
mcpServers: step.mcp_servers,
|
mcpServers: step.mcp_servers,
|
||||||
provider: step.provider,
|
provider: step.provider,
|
||||||
model: step.model,
|
model: step.model,
|
||||||
permissionMode: step.permission_mode,
|
requiredPermissionMode: step.required_permission_mode,
|
||||||
providerOptions: mergeProviderOptions(inheritedProviderOptions, normalizeProviderOptions(step.provider_options)),
|
providerOptions: mergeProviderOptions(inheritedProviderOptions, normalizeProviderOptions(step.provider_options)),
|
||||||
edit: step.edit,
|
edit: step.edit,
|
||||||
instructionTemplate: (step.instruction_template
|
instructionTemplate: (step.instruction_template
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { join, resolve } from 'node:path';
|
|||||||
import { parse, stringify } from 'yaml';
|
import { parse, stringify } from 'yaml';
|
||||||
import { copyProjectResourcesToDir } from '../../resources/index.js';
|
import { copyProjectResourcesToDir } from '../../resources/index.js';
|
||||||
import type { PermissionMode, ProjectLocalConfig } from '../types.js';
|
import type { PermissionMode, ProjectLocalConfig } from '../types.js';
|
||||||
|
import type { ProviderPermissionProfiles } from '../../../core/models/provider-profiles.js';
|
||||||
|
|
||||||
export type { PermissionMode, ProjectLocalConfig };
|
export type { PermissionMode, ProjectLocalConfig };
|
||||||
|
|
||||||
@ -34,6 +35,28 @@ function getConfigPath(projectDir: string): string {
|
|||||||
return join(getConfigDir(projectDir), 'config.yaml');
|
return join(getConfigDir(projectDir), 'config.yaml');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeProviderProfiles(raw: Record<string, { default_permission_mode: unknown; movement_permission_overrides?: Record<string, unknown> }> | undefined): ProviderPermissionProfiles | undefined {
|
||||||
|
if (!raw) return undefined;
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(raw).map(([provider, profile]) => [provider, {
|
||||||
|
defaultPermissionMode: profile.default_permission_mode,
|
||||||
|
movementPermissionOverrides: profile.movement_permission_overrides,
|
||||||
|
}]),
|
||||||
|
) as ProviderPermissionProfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
function denormalizeProviderProfiles(profiles: ProviderPermissionProfiles | undefined): Record<string, { default_permission_mode: string; movement_permission_overrides?: Record<string, string> }> | undefined {
|
||||||
|
if (!profiles) return undefined;
|
||||||
|
const entries = Object.entries(profiles);
|
||||||
|
if (entries.length === 0) return undefined;
|
||||||
|
return Object.fromEntries(entries.map(([provider, profile]) => [provider, {
|
||||||
|
default_permission_mode: profile.defaultPermissionMode,
|
||||||
|
...(profile.movementPermissionOverrides
|
||||||
|
? { movement_permission_overrides: profile.movementPermissionOverrides }
|
||||||
|
: {}),
|
||||||
|
}])) as Record<string, { default_permission_mode: string; movement_permission_overrides?: Record<string, string> }>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load project configuration from .takt/config.yaml
|
* Load project configuration from .takt/config.yaml
|
||||||
*/
|
*/
|
||||||
@ -46,8 +69,12 @@ export function loadProjectConfig(projectDir: string): ProjectLocalConfig {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const content = readFileSync(configPath, 'utf-8');
|
const content = readFileSync(configPath, 'utf-8');
|
||||||
const parsed = parse(content) as ProjectLocalConfig | null;
|
const parsed = (parse(content) as ProjectLocalConfig | null) ?? {};
|
||||||
return { ...DEFAULT_PROJECT_CONFIG, ...parsed };
|
return {
|
||||||
|
...DEFAULT_PROJECT_CONFIG,
|
||||||
|
...parsed,
|
||||||
|
providerProfiles: normalizeProviderProfiles(parsed.provider_profiles as Record<string, { default_permission_mode: unknown; movement_permission_overrides?: Record<string, unknown> }> | undefined),
|
||||||
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return { ...DEFAULT_PROJECT_CONFIG };
|
return { ...DEFAULT_PROJECT_CONFIG };
|
||||||
}
|
}
|
||||||
@ -68,7 +95,16 @@ export function saveProjectConfig(projectDir: string, config: ProjectLocalConfig
|
|||||||
// Copy project resources (only copies files that don't exist)
|
// Copy project resources (only copies files that don't exist)
|
||||||
copyProjectResourcesToDir(configDir);
|
copyProjectResourcesToDir(configDir);
|
||||||
|
|
||||||
const content = stringify(config, { indent: 2 });
|
const savePayload: ProjectLocalConfig = { ...config };
|
||||||
|
const rawProfiles = denormalizeProviderProfiles(config.providerProfiles);
|
||||||
|
if (rawProfiles && Object.keys(rawProfiles).length > 0) {
|
||||||
|
savePayload.provider_profiles = rawProfiles;
|
||||||
|
} else {
|
||||||
|
delete savePayload.provider_profiles;
|
||||||
|
}
|
||||||
|
delete savePayload.providerProfiles;
|
||||||
|
|
||||||
|
const content = stringify(savePayload, { indent: 2 });
|
||||||
writeFileSync(configPath, content, 'utf-8');
|
writeFileSync(configPath, content, 'utf-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import type { PieceCategoryConfigNode } from '../../core/models/schemas.js';
|
import type { PieceCategoryConfigNode } from '../../core/models/schemas.js';
|
||||||
import type { MovementProviderOptions } from '../../core/models/piece-types.js';
|
import type { MovementProviderOptions } from '../../core/models/piece-types.js';
|
||||||
|
import type { ProviderPermissionProfiles } from '../../core/models/provider-profiles.js';
|
||||||
|
|
||||||
/** Permission mode for the project
|
/** Permission mode for the project
|
||||||
* - default: Uses Agent SDK's acceptEdits mode (auto-accepts file edits, minimal prompts)
|
* - default: Uses Agent SDK's acceptEdits mode (auto-accepts file edits, minimal prompts)
|
||||||
@ -25,6 +26,10 @@ export interface ProjectLocalConfig {
|
|||||||
verbose?: boolean;
|
verbose?: boolean;
|
||||||
/** Provider-specific options (overrides global, overridden by piece/movement) */
|
/** Provider-specific options (overrides global, overridden by piece/movement) */
|
||||||
provider_options?: MovementProviderOptions;
|
provider_options?: MovementProviderOptions;
|
||||||
|
/** Provider-specific permission profiles (project-level override) */
|
||||||
|
provider_profiles?: ProviderPermissionProfiles;
|
||||||
|
/** Provider-specific permission profiles (camelCase alias) */
|
||||||
|
providerProfiles?: ProviderPermissionProfiles;
|
||||||
/** Piece categories (name -> piece list) */
|
/** Piece categories (name -> piece list) */
|
||||||
piece_categories?: Record<string, PieceCategoryConfigNode>;
|
piece_categories?: Record<string, PieceCategoryConfigNode>;
|
||||||
/** Show uncategorized pieces under Others category */
|
/** Show uncategorized pieces under Others category */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user