takt のプロンプトをファイルに

This commit is contained in:
nrslib 2026-02-03 17:04:09 +09:00
parent 1b168e6b20
commit ad28dc7438
17 changed files with 1100 additions and 531 deletions

View File

@ -8,10 +8,18 @@ TAKT is built with TAKT (dogfooding).
## Requirements
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) or Codex must be installed and configured
You need one of the following:
- **Anthropic API Key** or **OpenAI API Key**
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) or [Codex](https://github.com/openai/codex) installed and configured
Additional requirements:
- [GitHub CLI](https://cli.github.com/) (`gh`) — required only for `takt #N` (GitHub Issue execution)
TAKT supports both Claude Code and Codex as providers; you can choose the provider during setup.
**About pricing**: When using API keys, TAKT calls Claude API (Anthropic) or OpenAI API directly. The pricing is the same as using Claude Code or Codex. Be mindful of costs, especially when running tasks automatically in CI/CD environments, as API usage can accumulate quickly.
TAKT supports both Claude (Anthropic) and Codex (OpenAI) as providers.
## Installation
@ -375,6 +383,12 @@ default_workflow: default
log_level: info
provider: claude # Default provider: claude or codex
model: sonnet # Default model (optional)
# API Key configuration (optional)
# Can be overridden by TAKT_ANTHROPIC_API_KEY / TAKT_OPENAI_API_KEY env vars
anthropic_api_key: sk-ant-... # For Claude (Anthropic)
# openai_api_key: sk-... # For Codex (OpenAI)
trusted_directories:
- /path/to/trusted/dir
@ -389,6 +403,22 @@ trusted_directories:
# Closes #{issue}
```
**API Key configuration:**
1. **Using environment variables** (recommended):
```bash
export TAKT_ANTHROPIC_API_KEY=sk-ant-... # For Claude
# or
export TAKT_OPENAI_API_KEY=sk-... # For Codex
```
2. **Using config file**:
Add `anthropic_api_key` or `openai_api_key` to `~/.takt/config.yaml` as shown above
Priority: Environment variables > `config.yaml` settings
**Note**: When API keys are configured, Claude Code or Codex installation is not required. TAKT will call Anthropic API or OpenAI API directly.
**Pipeline template variables:**
| Variable | Available in | Description |
@ -692,7 +722,15 @@ npm install -g takt
takt --pipeline --task "fix bug" --auto-pr --repo owner/repo
```
Set `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` environment variables for authentication.
Set `TAKT_ANTHROPIC_API_KEY` or `TAKT_OPENAI_API_KEY` environment variables for authentication (TAKT-specific prefixed variables).
```bash
# For Claude (Anthropic)
export TAKT_ANTHROPIC_API_KEY=sk-ant-...
# For Codex (OpenAI)
export TAKT_OPENAI_API_KEY=sk-...
```
## Docker Support

View File

@ -6,10 +6,18 @@ TAKTはTAKT自身で開発されていますドッグフーディング
## 必要条件
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) または Codex がインストール・設定済みであること
- [GitHub CLI](https://cli.github.com/) (`gh`) — `takt "#N"`GitHub Issue実行を使う場合のみ必要
次のいずれかが必要です。
TAKTはClaude CodeとCodexの両方をプロバイダーとしてサポートしています。セットアップ時にプロバイダーを選択できます。
- **Anthropic API Key** または **OpenAI API Key**
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) または [Codex](https://github.com/openai/codex) がインストール・設定済みであること
追加で必要なもの:
- [GitHub CLI](https://cli.github.com/) (`gh`) — `takt #N`GitHub Issue実行を使う場合のみ必要
**料金について**: TAKT は API Key を使用する場合、Claude APIAnthropicまたは OpenAI API を直接呼び出します。これは Claude Code や Codex を使った場合と同じ料金体系になります。特に CI/CD で自動実行する場合、API 使用量が増えるため、コストに注意してください。
TAKTは ClaudeAnthropicと CodexOpenAIの両方をプロバイダーとしてサポートしています。
## インストール
@ -23,9 +31,6 @@ npm install -g takt
# 対話モードでAIとタスク要件を詰めてから実行
takt
# 最初のメッセージを指定して会話を開始することも可能
takt こんにちは
# GitHub Issueをタスクとして実行どちらも同じ
takt #6
takt --issue 6
@ -34,81 +39,139 @@ takt --issue 6
takt --pipeline --task "バグを修正して" --auto-pr
```
### タスク実行の流れ
## 使い方
`takt #6` (GitHub Issue参照) を実行すると、以下の対話フローが表示されます:
### 対話モード
**1. ワークフロー選択**
```
Select workflow:
(↑↓ to move, Enter to select)
default (current) (default)
expert
expert-cqrs
magi
research
simple
Cancel
```
**2. 隔離クローン作成**(オプション)
```
? Create worktree? (Y/n)
```
`y` を選ぶと `git clone --shared` で隔離環境を作成し、作業ディレクトリをクリーンに保てます。
**3. 実行** — 選択したワークフローが複数のエージェントを連携させてタスクを完了します。
**4. PR作成**worktree実行後
```
? Create pull request? (y/N)
```
`--auto-pr` を指定している場合は確認なしで自動作成されます。
### おすすめワークフロー
| ワークフロー | おすすめ用途 |
|------------|------------|
| `default` | 本格的な開発タスク。TAKT自身の開発で使用。アーキテクトセキュリティの並列レビュー付き多段階レビュー。 |
| `minimal` | 簡単な修正やシンプルなタスク。基本的なレビュー付きの最小限のワークフロー。 |
| `review-fix-minimal` | レビュー&修正ワークフロー。レビューフィードバックに基づく反復的な改善に特化。 |
| `research` | 調査・リサーチ。質問せずに自律的にリサーチを実行。 |
## コマンド一覧
### 対話モード(デフォルト)
日常の開発で使う基本モード。ワークフロー選択、worktree作成、PR作成を対話的に確認します。
AI との会話でタスク内容を詰めてから実行するモード。タスクの要件が曖昧な場合や、AI と相談しながら内容を整理したい場合に便利です。
```bash
# 対話モードでAIとタスク要件を詰めてから実行
# 対話モードを開始(引数なし)
takt
# 最初のメッセージを指定して会話を開始することも可能
takt こんにちは
# 最初のメッセージを指定(短い単語のみ)
takt hello
```
# GitHub Issueをタスクとして実行どちらも同じ
**注意:** スペースを含む文字列や Issue 参照(`#6`)、`--task` / `--issue` オプションを指定すると、対話モードをスキップして直接タスク実行されます。
**フロー:**
1. ワークフロー選択
2. AI との会話でタスク内容を整理
3. `/go` でタスク指示を確定
4. 実行worktree 作成、ワークフロー実行、PR 作成)
#### 実行例
```
$ takt
Select workflow:
🎼 default (current)
📁 Development/
📁 Research/
Cancel
対話モード - タスク内容を入力してください。コマンド: /go実行, /cancel終了
> ユーザー認証機能を追加したい
[AI が要件を確認・整理]
> /go
提案されたタスク指示:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ユーザー認証機能を実装する。
要件:
- メールアドレスとパスワードによるログイン機能
- JWT トークンを使った認証
- パスワードのハッシュ化bcrypt
- ログイン・ログアウト API エンドポイント
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
このタスク指示で進めますか? (Y/n) y
? Create worktree? (Y/n) y
[ワークフロー実行開始...]
```
### 直接タスク実行
タスク内容が明確な場合は、対話モードをスキップして直接実行できます。
```bash
# タスク内容を直接指定(スペースを含む文字列)
takt "ログイン機能を追加する"
# --task オプションでタスク内容を指定
takt --task "バグを修正"
# ワークフロー指定
takt "認証機能を追加" --workflow expert
# PR 自動作成
takt "バグを修正" --auto-pr
```
### GitHub Issue タスク
GitHub Issue を直接タスクとして実行できます。Issue のタイトル、本文、ラベル、コメントが自動的にタスク内容として取り込まれます。
```bash
# Issue番号を指定して実行
takt #6
takt --issue 6
# PR自動作成確認プロンプトをスキップ
takt #6 --auto-pr
# Issue + ワークフロー指定
takt #6 --workflow expert
# --taskオプションでタスク内容を指定GitHub Issueの代わり
takt --task "ログイン機能を追加"
# Issue + PR自動作成
takt #6 --auto-pr
```
`--auto-pr` を指定しない場合、worktreeでの実行成功後に「PR作成する」と確認されます。
**必要条件:** [GitHub CLI](https://cli.github.com/) (`gh`) がインストールされ、認証済みであること
### パイプラインモード(`--pipeline`
### タスク管理add / run / watch / list
`--pipeline` を指定すると非対話のパイプラインモードに入ります。ブランチ作成 → ワークフロー実行 → commit & push を自動で行います。スクリプトやCI連携に適しています。
タスクファイル(`.takt/tasks/`)を使ったバッチ処理。複数のタスクを積んでおいて、後でまとめて実行する使い方に便利です。
#### タスクを追加(`takt add`
```bash
# AI会話でタスクの要件を詰めてからタスクを追加
takt add
# GitHub IssueからタスクAddIssue番号がブランチ名に反映される
takt add #28
```
#### タスクを実行(`takt run`
```bash
# .takt/tasks/ の保留中タスクをすべて実行
takt run
```
#### タスクを監視(`takt watch`
```bash
# .takt/tasks/ を監視してタスクを自動実行(常駐プロセス)
takt watch
```
#### タスクブランチを一覧表示(`takt list`
```bash
# タスクブランチ一覧(マージ・削除)
takt list
```
### パイプラインモードCI/自動化向け)
`--pipeline` を指定すると非対話のパイプラインモードに入ります。ブランチ作成 → ワークフロー実行 → commit & push を自動で行います。CI/CD での自動化に適しています。
```bash
# タスクをパイプライン実行
@ -128,25 +191,41 @@ takt --pipeline --task "バグを修正" --auto-pr --repo owner/repo
# ワークフロー実行のみブランチ作成・commit・pushをスキップ
takt --pipeline --task "バグを修正" --skip-git
# 最小限の出力モードCI向け
takt --pipeline --task "バグを修正" --quiet
```
パイプラインモードでは `--auto-pr` を指定しない限りPRは作成されません。
パイプラインモードでは `--auto-pr` を指定しない限り PR は作成されません。
### サブコマンド
**GitHub との統合:** GitHub Actions で TAKT を使う場合は、[takt-action](https://github.com/nrslib/takt-action) を参照してください。PR レビューやタスク実行を自動化できます。詳細は [CI/CD 連携](#cicd連携) セクションを参照してください。
| コマンド | 説明 |
|---------|------|
| `takt run` | `.takt/tasks/` の保留中タスクをすべて実行 |
| `takt watch` | `.takt/tasks/` を監視してタスクを自動実行(常駐プロセス) |
| `takt add` | AI会話で新しいタスクを追加 |
| `takt list` | タスクブランチ一覧(マージ・削除) |
| `takt switch` | ワークフローを対話的に切り替え |
| `takt clear` | エージェントの会話セッションをクリア |
| `takt eject` | ビルトインのワークフロー/エージェントを`~/.takt/`にコピーしてカスタマイズ |
| `takt config` | パーミッションモードを設定 |
| `takt --help` | ヘルプを表示 |
### その他のコマンド
### オプション
```bash
# ワークフローを対話的に切り替え
takt switch
# ビルトインのワークフロー/エージェントを~/.takt/にコピーしてカスタマイズ
takt eject
# エージェントの会話セッションをクリア
takt clear
# パーミッションモードを設定
takt config
```
### おすすめワークフロー
| ワークフロー | おすすめ用途 |
|------------|------------|
| `default` | 本格的な開発タスク。TAKT自身の開発で使用。アーキテクトセキュリティの並列レビュー付き多段階レビュー。 |
| `minimal` | 簡単な修正やシンプルなタスク。基本的なレビュー付きの最小限のワークフロー。 |
| `review-fix-minimal` | レビュー&修正ワークフロー。レビューフィードバックに基づく反復的な改善に特化。 |
| `research` | 調査・リサーチ。質問せずに自律的にリサーチを実行。 |
### 主要なオプション
| オプション | 説明 |
|-----------|------|
@ -162,7 +241,6 @@ takt --pipeline --task "バグを修正" --skip-git
| `-q, --quiet` | 最小限の出力モード: AIの出力を抑制CI向け |
| `--provider <name>` | エージェントプロバイダーを上書きclaude\|codex\|mock |
| `--model <name>` | エージェントモデルを上書き |
| `--config <path>` | グローバル設定ファイルのパス(デフォルト: `~/.takt/config.yaml` |
## ワークフロー
@ -281,13 +359,13 @@ TAKTには複数のビルトインワークフローが同梱されています:
| ワークフロー | 説明 |
|------------|------|
| `default` | フル開発ワークフロー: 計画 → アーキテクチャ設計 → 実装 → AI レビュー → 並列レビュー(アーキテクト+セキュリティ)→ スーパーバイザー承認。各レビュー段階に修正ループあり。 |
| `minimal` | クイックワークフロー: 計画 → 実装 → レビュー → スーパーバイザー。高速イテレーション向けの最小ステップ。 |
| `default` | フル開発ワークフロー: 計画 → 実装 → AIレビュー → 並列レビュー(アーキテクト+セキュリティ)→ スーパーバイザー承認。各レビュー段階に修正ループあり。 |
| `review-fix-minimal` | レビュー重視ワークフロー: レビュー → 修正 → スーパーバイザー。レビューフィードバックに基づく反復改善向け。 |
| `research` | リサーチワークフロー: プランナー → ディガー → スーパーバイザー。質問せずに自律的にリサーチを実行。 |
| `expert` | ドメインエキスパートによる逐次レビュー: アーキテクチャ、フロントエンド、セキュリティ、QAレビューと修正ループ。 |
| `expert-cqrs` | ドメインエキスパートによる逐次レビュー: CQRS+ES、フロントエンド、セキュリティ、QAレビューと修正ループ。 |
| `magi` | エヴァンゲリオンにインスパイアされた審議システム。3つのAIペルソナMELCHIOR、BALTHASAR、CASPERが分析し投票。 |
| `expert` | フルスタック開発ワークフロー: アーキテクチャ、フロントエンド、セキュリティ、QA レビューと修正ループ。 |
| `expert-cqrs` | フルスタック開発ワークフローCQRS+ES特化: CQRS+ES、フロントエンド、セキュリティ、QA レビューと修正ループ。 |
| `magi` | エヴァンゲリオンにインスパイアされた審議システム。3つの AI ペルソナMELCHIOR、BALTHASAR、CASPERが分析し投票。 |
| `review-only` | 変更を加えない読み取り専用のコードレビューワークフロー。 |
`takt switch` でワークフローを切り替えられます。
@ -305,18 +383,7 @@ TAKTには複数のビルトインワークフローが同梱されています:
## カスタムエージェント
`.takt/agents.yaml`でカスタムエージェントを定義:
```yaml
agents:
- name: my-reviewer
prompt_file: .takt/prompts/reviewer.md
allowed_tools: [Read, Glob, Grep]
provider: claude # オプション: claude または codex
model: opus # Claude: opus/sonnet/haiku、Codex: gpt-5.2-codex 等
```
またはMarkdownファイルでエージェントプロンプトを作成:
Markdown ファイルでエージェントプロンプトを作成:
```markdown
# ~/.takt/agents/my-agents/reviewer.md
@ -344,20 +411,23 @@ Claude Code はエイリアス(`opus`、`sonnet`、`haiku`、`opusplan`、`def
## プロジェクト構造
```
~/.takt/
~/.takt/ # グローバル設定ディレクトリ
├── config.yaml # グローバル設定(プロバイダー、モデル、ワークフロー等)
├── workflows/ # ユーザーワークフロー定義(ビルトインを上書き)
└── agents/ # ユーザーエージェントプロンプトファイル
│ └── custom.yaml
└── agents/ # ユーザーエージェントプロンプトファイル(.md
└── my-agent.md
.takt/ # プロジェクトレベルの設定
├── agents.yaml # カスタムエージェント定義
├── config.yaml # プロジェクト設定(現在のワークフロー等)
├── tasks/ # 保留中のタスクファイル(.yaml, .md
├── completed/ # 完了したタスクとレポート
├── reports/ # 実行レポート(自動生成)
└── logs/ # NDJSON形式のセッションログ
│ └── {timestamp}-{slug}/
└── logs/ # NDJSON 形式のセッションログ
├── latest.json # 現在/最新セッションへのポインタ
├── previous.json # 前回セッションへのポインタ
└── {sessionId}.jsonl # ワークフロー実行ごとのNDJSONセッションログ
└── {sessionId}.jsonl # ワークフロー実行ごとの NDJSON セッションログ
```
ビルトインリソースはnpmパッケージ`dist/resources/`)に埋め込まれています。`~/.takt/` のユーザーファイルが優先されます。
@ -373,6 +443,12 @@ default_workflow: default
log_level: info
provider: claude # デフォルトプロバイダー: claude または codex
model: sonnet # デフォルトモデル(オプション)
# API Key 設定(オプション)
# 環境変数 TAKT_ANTHROPIC_API_KEY / TAKT_OPENAI_API_KEY で上書き可能
anthropic_api_key: sk-ant-... # Claude (Anthropic) を使う場合
# openai_api_key: sk-... # Codex (OpenAI) を使う場合
trusted_directories:
- /path/to/trusted/dir
@ -387,6 +463,24 @@ trusted_directories:
# Closes #{issue}
```
**API Key の設定方法:**
1. **環境変数で設定**:
```bash
export TAKT_ANTHROPIC_API_KEY=sk-ant-... # Claude の場合
# または
export TAKT_OPENAI_API_KEY=sk-... # Codex の場合
```
2. **設定ファイルで設定**:
上記の `~/.takt/config.yaml``anthropic_api_key` または `openai_api_key` を記述
優先順位: 環境変数 > `config.yaml` の設定
**注意事項:**
- API Key を設定した場合、Claude Code や Codex のインストールは不要です。TAKT が直接 Anthropic API または OpenAI API を呼び出します。
- **セキュリティ**: `config.yaml` に API Key を記述した場合、このファイルを Git にコミットしないよう注意してください。環境変数での設定を使うか、`.gitignore``~/.takt/config.yaml` を追加することを検討してください。
**パイプラインテンプレート変数:**
| 変数 | 使用可能箇所 | 説明 |
@ -402,35 +496,11 @@ trusted_directories:
3. グローバル設定の `model`
4. プロバイダーデフォルトClaude: sonnet、Codex: codex
## 実践的な使い方ガイド
## 詳細ガイド
### 対話ワークフロー
### タスクファイルの形式
`takt` (対話モード)または `takt #6` GitHub Issueを実行すると、以下の流れで案内されます:
1. **ワークフロー選択** - 利用可能なワークフローから選択矢印キーで移動、ESCでキャンセル
2. **隔離クローン作成**(オプション) - `git clone --shared` による隔離環境でタスクを実行
3. **PR作成**worktree実行後 - タスクブランチからPRを作成
`--auto-pr` を指定している場合、PR作成の確認はスキップされ自動で作成されます。
### タスク管理
TAKTは`.takt/tasks/`内のタスクファイルによるバッチ処理をサポートしています。`.yaml`/`.yml``.md`の両方のファイル形式に対応しています。
#### `takt add` でタスクを追加
```bash
# AI会話でタスクの要件を詰めてからタスクを追加
takt add
# GitHub IssueからタスクAddIssue番号がブランチ名に反映される
takt add #28
```
`takt add` はAI会話を開始し、タスクの要件を詰めます。`/go` で確定すると、AIが会話を要約してYAMLタスクファイルを作成します。worktree/branch/workflowの設定も対話的に行えます。`takt add #N` を使用すると、Issue番号が自動的にブランチ名に反映されます例: `takt/issue-28-...`)。
#### タスクファイルの形式
TAKT は `.takt/tasks/` 内のタスクファイルによるバッチ処理をサポートしています。`.yaml`/`.yml``.md` の両方のファイル形式に対応しています。
**YAML形式**推奨、worktree/branch/workflowオプション対応:
@ -466,40 +536,7 @@ YAMLタスクファイルで`worktree`を指定すると、各タスクを`git c
> **Note**: YAMLフィールド名は後方互換のため`worktree`のままです。内部的には`git worktree`ではなく`git clone --shared`を使用しています。git worktreeの`.git`ファイルには`gitdir:`でメインリポジトリへのパスが記載されており、Claude Codeがそれを辿ってメインリポジトリをプロジェクトルートと認識してしまうためです。共有クローンは独立した`.git`ディレクトリを持つため、この問題が発生しません。
クローンは使い捨てです。タスク完了後に自動的にコミット+プッシュし、クローンを削除します。ブランチが唯一の永続的な成果物です。`takt list`でブランチの一覧表示・マージ・削除ができます。
#### `/run-tasks` でタスクを実行
```bash
takt run
```
- タスクはアルファベット順に実行されます(`001-``002-`のようなプレフィックスで順序を制御)
- 完了したタスクは実行レポートとともに`.takt/completed/`に移動されます
- 実行中に追加された新しいタスクも動的に取得されます
#### `/watch` でタスクを監視
```bash
takt watch
```
ウォッチモードは`.takt/tasks/`をポーリングし、新しいタスクファイルが現れると自動実行します。`Ctrl+C`で停止する常駐プロセスです。以下のような場合に便利です:
- タスクファイルを生成するCI/CDパイプライン
- 外部プロセスがタスクを追加する自動化ワークフロー
- タスクを順次キューイングする長時間の開発セッション
#### `/list-tasks` でタスクブランチを一覧表示
```bash
takt list
```
`takt/`プレフィックスのブランチをファイル変更数とともに一覧表示します。各ブランチに対して以下の操作が可能です:
- **Try merge** - mainにスカッシュマージ変更をステージングのみ、コミットなし
- **Instruct** - 一時クローン経由で追加指示を与える
- **Merge & cleanup** - マージしてブランチを削除
- **Delete** - マージせずにブランチを削除
クローンは使い捨てです。タスク完了後に自動的にコミット+プッシュし、クローンを削除します。ブランチが唯一の永続的な成果物です。`takt list` でブランチの一覧表示・マージ・削除ができます。
### セッションログ
@ -515,7 +552,7 @@ TAKTはセッションログをNDJSON`.jsonl`)形式で`.takt/logs/`に書
### カスタムワークフローの追加
`~/.takt/workflows/`にYAMLファイルを追加するか、`/eject`でビルトインをカスタマイズします:
`~/.takt/workflows/` に YAML ファイルを追加するか、`takt eject` でビルトインをカスタマイズします:
```bash
# defaultワークフローを~/.takt/workflows/にコピーして編集
@ -689,28 +726,16 @@ npm install -g takt
takt --pipeline --task "バグ修正" --auto-pr --repo owner/repo
```
認証には `ANTHROPIC_API_KEY` または `OPENAI_API_KEY` 環境変数を設定してください。
## Docker サポート
他の環境でのテスト用にDocker環境が提供されています:
認証には `TAKT_ANTHROPIC_API_KEY` または `TAKT_OPENAI_API_KEY` 環境変数を設定してくださいTAKT 独自のプレフィックス付き)。
```bash
# Dockerイメージをビルド
docker compose build
# Claude (Anthropic) を使う場合
export TAKT_ANTHROPIC_API_KEY=sk-ant-...
# コンテナでテストを実行
docker compose run --rm test
# コンテナでlintを実行
docker compose run --rm lint
# ビルドのみ(テストをスキップ)
docker compose run --rm build
# Codex (OpenAI) を使う場合
export TAKT_OPENAI_API_KEY=sk-...
```
これにより、クリーンなNode.js 20環境でプロジェクトが正しく動作することが保証されます。
## ドキュメント
- [Workflow Guide](./workflows.md) - ワークフローの作成とカスタマイズ

View File

@ -10,7 +10,7 @@
"takt-cli": "./dist/app/cli/index.js"
},
"scripts": {
"build": "tsc",
"build": "tsc && mkdir -p dist/shared/prompts && cp src/shared/prompts/prompts_en.yaml src/shared/prompts/prompts_ja.yaml dist/shared/prompts/",
"watch": "tsc --watch",
"test": "vitest run",
"test:watch": "vitest",

View File

@ -0,0 +1,190 @@
/**
* Tests for prompt loader utility (src/shared/prompts/index.ts)
*/
import { describe, it, expect, beforeEach } from 'vitest';
import { getPrompt, getPromptObject, _resetCache } from '../shared/prompts/index.js';
beforeEach(() => {
_resetCache();
});
describe('getPrompt', () => {
it('returns a language-independent prompt by key (defaults to en)', () => {
const result = getPrompt('summarize.slugGenerator');
expect(result).toContain('You are a slug generator');
});
it('returns an English prompt when lang is "en"', () => {
const result = getPrompt('interactive.systemPrompt', 'en');
expect(result).toContain('You are a task planning assistant');
});
it('returns a Japanese prompt when lang is "ja"', () => {
const result = getPrompt('interactive.systemPrompt', 'ja');
expect(result).toContain('あなたはTAKT');
});
it('throws for a non-existent key', () => {
expect(() => getPrompt('nonexistent.key')).toThrow('Prompt key not found: nonexistent.key');
});
it('throws for a non-existent key with language', () => {
expect(() => getPrompt('nonexistent.key', 'en')).toThrow('Prompt key not found: nonexistent.key (lang: en)');
});
it('returns prompt from en file when lang is explicitly "en"', () => {
const result = getPrompt('summarize.slugGenerator', 'en');
expect(result).toContain('You are a slug generator');
});
describe('template variable substitution', () => {
it('replaces {variableName} placeholders with provided values', () => {
const result = getPrompt('claude.agentDefault', undefined, { agentName: 'test-agent' });
expect(result).toContain('You are the test-agent agent');
expect(result).toContain('Follow the standard test-agent workflow');
});
it('leaves unmatched placeholders as-is', () => {
const result = getPrompt('claude.agentDefault', undefined, {});
expect(result).toContain('{agentName}');
});
it('replaces multiple different variables', () => {
const result = getPrompt('workflow.iterationLimit.maxReached', undefined, {
currentIteration: '5',
maxIterations: '10',
});
expect(result).toContain('(5/10)');
});
});
});
describe('getPromptObject', () => {
it('returns an object for a given key and language', () => {
const result = getPromptObject<{ heading: string }>('instruction.metadata', 'en');
expect(result.heading).toBe('## Execution Context');
});
it('returns a Japanese object when lang is "ja"', () => {
const result = getPromptObject<{ heading: string }>('instruction.metadata', 'ja');
expect(result.heading).toBe('## 実行コンテキスト');
});
it('returns interactive UI text object', () => {
const result = getPromptObject<{ intro: string }>('interactive.ui', 'en');
expect(result.intro).toContain('Interactive mode');
});
it('throws for a non-existent key', () => {
expect(() => getPromptObject('nonexistent.key')).toThrow('Prompt key not found: nonexistent.key');
});
});
describe('caching', () => {
it('returns the same data on repeated calls', () => {
const first = getPrompt('summarize.slugGenerator');
const second = getPrompt('summarize.slugGenerator');
expect(first).toBe(second);
});
it('reloads after cache reset', () => {
const first = getPrompt('summarize.slugGenerator');
_resetCache();
const second = getPrompt('summarize.slugGenerator');
expect(first).toBe(second);
});
});
describe('YAML content integrity', () => {
it('contains all expected top-level keys in en', () => {
expect(() => getPrompt('interactive.systemPrompt', 'en')).not.toThrow();
expect(() => getPrompt('interactive.summaryPrompt', 'en')).not.toThrow();
expect(() => getPrompt('interactive.workflowInfo', 'en')).not.toThrow();
expect(() => getPrompt('interactive.conversationLabel', 'en')).not.toThrow();
expect(() => getPrompt('interactive.noTranscript', 'en')).not.toThrow();
expect(() => getPromptObject('interactive.ui', 'en')).not.toThrow();
expect(() => getPrompt('summarize.slugGenerator')).not.toThrow();
expect(() => getPrompt('summarize.conversationSummarizer')).not.toThrow();
expect(() => getPrompt('claude.agentDefault')).not.toThrow();
expect(() => getPrompt('claude.judgePrompt')).not.toThrow();
expect(() => getPromptObject('instruction.metadata', 'en')).not.toThrow();
expect(() => getPromptObject('instruction.sections', 'en')).not.toThrow();
expect(() => getPromptObject('instruction.reportOutput', 'en')).not.toThrow();
expect(() => getPromptObject('instruction.reportPhase', 'en')).not.toThrow();
expect(() => getPromptObject('instruction.reportSections', 'en')).not.toThrow();
expect(() => getPrompt('instruction.statusJudgment.header', 'en')).not.toThrow();
expect(() => getPromptObject('instruction.statusRules', 'en')).not.toThrow();
expect(() => getPrompt('workflow.iterationLimit.maxReached')).not.toThrow();
});
it('contains all expected top-level keys in ja', () => {
expect(() => getPrompt('interactive.systemPrompt', 'ja')).not.toThrow();
expect(() => getPrompt('interactive.summaryPrompt', 'ja')).not.toThrow();
expect(() => getPrompt('interactive.workflowInfo', 'ja')).not.toThrow();
expect(() => getPrompt('interactive.conversationLabel', 'ja')).not.toThrow();
expect(() => getPrompt('interactive.noTranscript', 'ja')).not.toThrow();
expect(() => getPromptObject('interactive.ui', 'ja')).not.toThrow();
expect(() => getPrompt('summarize.slugGenerator', 'ja')).not.toThrow();
expect(() => getPrompt('summarize.conversationSummarizer', 'ja')).not.toThrow();
expect(() => getPrompt('claude.agentDefault', 'ja')).not.toThrow();
expect(() => getPrompt('claude.judgePrompt', 'ja')).not.toThrow();
expect(() => getPromptObject('instruction.metadata', 'ja')).not.toThrow();
expect(() => getPromptObject('instruction.sections', 'ja')).not.toThrow();
expect(() => getPromptObject('instruction.reportOutput', 'ja')).not.toThrow();
expect(() => getPromptObject('instruction.reportPhase', 'ja')).not.toThrow();
expect(() => getPromptObject('instruction.reportSections', 'ja')).not.toThrow();
expect(() => getPrompt('instruction.statusJudgment.header', 'ja')).not.toThrow();
expect(() => getPromptObject('instruction.statusRules', 'ja')).not.toThrow();
expect(() => getPrompt('workflow.iterationLimit.maxReached', 'ja')).not.toThrow();
});
it('instruction.metadata has all required fields', () => {
const en = getPromptObject<Record<string, string>>('instruction.metadata', 'en');
expect(en).toHaveProperty('heading');
expect(en).toHaveProperty('workingDirectory');
expect(en).toHaveProperty('rulesHeading');
expect(en).toHaveProperty('noCommit');
expect(en).toHaveProperty('noCd');
expect(en).toHaveProperty('editEnabled');
expect(en).toHaveProperty('editDisabled');
expect(en).toHaveProperty('note');
});
it('instruction.sections has all required fields', () => {
const en = getPromptObject<Record<string, string>>('instruction.sections', 'en');
expect(en).toHaveProperty('workflowContext');
expect(en).toHaveProperty('iteration');
expect(en).toHaveProperty('step');
expect(en).toHaveProperty('userRequest');
expect(en).toHaveProperty('instructions');
});
it('instruction.statusRules has appendixInstruction with {tag} placeholder', () => {
const en = getPromptObject<{ appendixInstruction: string }>('instruction.statusRules', 'en');
expect(en.appendixInstruction).toContain('{tag}');
});
it('en and ja files have the same key structure', () => {
// Verify a sampling of keys exist in both languages
const stringKeys = [
'interactive.systemPrompt',
'summarize.slugGenerator',
'claude.agentDefault',
'workflow.iterationLimit.maxReached',
];
for (const key of stringKeys) {
expect(() => getPrompt(key, 'en')).not.toThrow();
expect(() => getPrompt(key, 'ja')).not.toThrow();
}
const objectKeys = [
'interactive.ui',
'instruction.metadata',
'instruction.sections',
];
for (const key of objectKeys) {
expect(() => getPromptObject(key, 'en')).not.toThrow();
expect(() => getPromptObject(key, 'ja')).not.toThrow();
}
});
});

View File

@ -14,6 +14,7 @@ import type { InstructionContext } from './instruction-context.js';
import { buildExecutionMetadata, renderExecutionMetadata } from './instruction-context.js';
import { generateStatusRulesFromRules } from './status-rules.js';
import { escapeTemplateChars, replaceTemplatePlaceholders } from './escape.js';
import { getPromptObject } from '../../../shared/prompts/index.js';
/**
* Check if a report config is the object form (ReportObjectConfig).
@ -22,57 +23,31 @@ export function isReportObjectConfig(report: string | ReportConfig[] | ReportObj
return typeof report === 'object' && !Array.isArray(report) && 'name' in report;
}
/** Localized strings for auto-injected sections */
const SECTION_STRINGS = {
en: {
workflowContext: '## Workflow Context',
iteration: 'Iteration',
iterationWorkflowWide: '(workflow-wide)',
stepIteration: 'Step Iteration',
stepIterationTimes: '(times this step has run)',
step: 'Step',
reportDirectory: 'Report Directory',
reportFile: 'Report File',
reportFiles: 'Report Files',
phaseNote: '**Note:** This is Phase 1 (main work). After you complete your work, Phase 2 will automatically generate the report based on your findings.',
userRequest: '## User Request',
previousResponse: '## Previous Response',
additionalUserInputs: '## Additional User Inputs',
instructions: '## Instructions',
},
ja: {
workflowContext: '## Workflow Context',
iteration: 'Iteration',
iterationWorkflowWide: '(ワークフロー全体)',
stepIteration: 'Step Iteration',
stepIterationTimes: '(このステップの実行回数)',
step: 'Step',
reportDirectory: 'Report Directory',
reportFile: 'Report File',
reportFiles: 'Report Files',
phaseNote: '**注意:** これはPhase 1本来の作業です。作業完了後、Phase 2で自動的にレポートを生成します。',
userRequest: '## User Request',
previousResponse: '## Previous Response',
additionalUserInputs: '## Additional User Inputs',
instructions: '## Instructions',
},
} as const;
/** Shape of localized section strings */
interface SectionStrings {
workflowContext: string;
iteration: string;
iterationWorkflowWide: string;
stepIteration: string;
stepIterationTimes: string;
step: string;
reportDirectory: string;
reportFile: string;
reportFiles: string;
phaseNote: string;
userRequest: string;
previousResponse: string;
additionalUserInputs: string;
instructions: string;
}
/** Localized strings for auto-generated report output instructions */
const REPORT_OUTPUT_STRINGS = {
en: {
singleHeading: '**Report output:** Output to the `Report File` specified above.',
multiHeading: '**Report output:** Output to the `Report Files` specified above.',
createRule: '- If file does not exist: Create new file',
appendRule: '- If file exists: Append with `## Iteration {step_iteration}` section',
},
ja: {
singleHeading: '**レポート出力:** `Report File` に出力してください。',
multiHeading: '**レポート出力:** Report Files に出力してください。',
createRule: '- ファイルが存在しない場合: 新規作成',
appendRule: '- ファイルが存在する場合: `## Iteration {step_iteration}` セクションを追記',
},
} as const;
/** Shape of localized report output strings */
interface ReportOutputStrings {
singleHeading: string;
multiHeading: string;
createRule: string;
appendRule: string;
}
/**
* Builds Phase 1 instructions for agent execution.
@ -93,7 +68,7 @@ export class InstructionBuilder {
*/
build(): string {
const language = this.context.language ?? 'en';
const s = SECTION_STRINGS[language];
const s = getPromptObject<SectionStrings>('instruction.sections', language);
const sections: string[] = [];
// 1. Execution context metadata (working directory + rules + edit permission)
@ -151,7 +126,7 @@ export class InstructionBuilder {
}
private renderWorkflowContext(language: Language): string {
const s = SECTION_STRINGS[language];
const s = getPromptObject<SectionStrings>('instruction.sections', language);
const lines: string[] = [
s.workflowContext,
`- ${s.iteration}: ${this.context.iteration}/${this.context.maxIterations}${s.iterationWorkflowWide}`,
@ -180,7 +155,7 @@ export function renderReportContext(
reportDir: string,
language: Language,
): string {
const s = SECTION_STRINGS[language];
const s = getPromptObject<SectionStrings>('instruction.sections', language);
const lines: string[] = [
`- ${s.reportDirectory}: ${reportDir}/`,
];
@ -210,7 +185,7 @@ export function renderReportOutputInstruction(
): string | undefined {
if (!step.report || !context.reportDir) return undefined;
const s = REPORT_OUTPUT_STRINGS[language];
const s = getPromptObject<ReportOutputStrings>('instruction.reportOutput', language);
const isMulti = Array.isArray(step.report);
const heading = isMulti ? s.multiHeading : s.singleHeading;
const appendRule = s.appendRule.replace('{step_iteration}', String(context.stepIteration));

View File

@ -12,35 +12,26 @@
import type { WorkflowStep, Language } from '../../models/types.js';
import type { InstructionContext } from './instruction-context.js';
import { METADATA_STRINGS } from './instruction-context.js';
import { getMetadataStrings } from './instruction-context.js';
import { replaceTemplatePlaceholders } from './escape.js';
import { isReportObjectConfig, renderReportContext, renderReportOutputInstruction } from './InstructionBuilder.js';
import { getPromptObject } from '../../../shared/prompts/index.js';
/** Localized strings for report phase execution rules */
const REPORT_PHASE_STRINGS = {
en: {
noSourceEdit: '**Do NOT modify project source files.** Only output report files.',
reportDirOnly: '**Use only the Report Directory files shown above.** Do not search or open reports outside that directory.',
instructionBody: 'Output the results of your previous work as a report.',
reportJsonFormat: 'JSON format is optional. If you use JSON, map report file names to content (file name key only).',
reportPlainAllowed: 'You may output plain text. If there are multiple report files, the same content will be written to each file.',
reportOnlyOutput: 'Output only the report content (no status tags, no commentary).',
},
ja: {
noSourceEdit: '**プロジェクトのソースファイルを変更しないでください。** レポートファイルのみ出力してください。',
reportDirOnly: '**上記のReport Directory内のファイルのみ使用してください。** 他のレポートディレクトリは検索/参照しないでください。',
instructionBody: '前のステップの作業結果をレポートとして出力してください。',
reportJsonFormat: 'JSON形式は任意です。JSONを使う場合は「レポートファイル名→内容」のオブジェクトにしてくださいキーはファイル名のみ。',
reportPlainAllowed: '本文のみの出力も可です。複数ファイルの場合は同じ内容が各ファイルに書き込まれます。',
reportOnlyOutput: 'レポート本文のみを出力してください(ステータスタグやコメントは禁止)。',
},
} as const;
/** Shape of localized report phase strings */
interface ReportPhaseStrings {
noSourceEdit: string;
reportDirOnly: string;
instructionBody: string;
reportJsonFormat: string;
reportPlainAllowed: string;
reportOnlyOutput: string;
}
/** Localized section strings (shared subset) */
const SECTION_STRINGS = {
en: { workflowContext: '## Workflow Context', instructions: '## Instructions' },
ja: { workflowContext: '## Workflow Context', instructions: '## Instructions' },
} as const;
/** Shape of localized report section strings */
interface ReportSectionStrings {
workflowContext: string;
instructions: string;
}
/**
* Context for building report phase instruction.
@ -71,9 +62,9 @@ export class ReportInstructionBuilder {
}
const language = this.context.language ?? 'en';
const s = SECTION_STRINGS[language];
const r = REPORT_PHASE_STRINGS[language];
const m = METADATA_STRINGS[language];
const s = getPromptObject<ReportSectionStrings>('instruction.reportSections', language);
const r = getPromptObject<ReportPhaseStrings>('instruction.reportPhase', language);
const m = getMetadataStrings(language);
const sections: string[] = [];
// 1. Execution Context

View File

@ -11,16 +11,7 @@
import type { WorkflowStep, Language } from '../../models/types.js';
import { generateStatusRulesFromRules } from './status-rules.js';
/** Localized strings for status judgment phase */
const STATUS_JUDGMENT_STRINGS = {
en: {
header: 'Review your work results and determine the status. Do NOT perform any additional work.',
},
ja: {
header: '作業結果を振り返り、ステータスを判定してください。追加の作業は行わないでください。',
},
} as const;
import { getPrompt } from '../../../shared/prompts/index.js';
/**
* Context for building status judgment instruction.
@ -47,11 +38,10 @@ export class StatusJudgmentBuilder {
}
const language = this.context.language ?? 'en';
const s = STATUS_JUDGMENT_STRINGS[language];
const sections: string[] = [];
// Header
sections.push(s.header);
sections.push(getPrompt('instruction.statusJudgment.header', language));
// Status rules (criteria table + output format)
const generatedPrompt = generateStatusRulesFromRules(

View File

@ -6,6 +6,7 @@
*/
import type { AgentResponse, Language } from '../../models/types.js';
import { getPromptObject } from '../../../shared/prompts/index.js';
/**
* Context for building instruction from template.
@ -58,29 +59,22 @@ export function buildExecutionMetadata(context: InstructionContext, edit?: boole
};
}
/** Localized strings for execution metadata rendering */
export const METADATA_STRINGS = {
en: {
heading: '## Execution Context',
workingDirectory: 'Working Directory',
rulesHeading: '## Execution Rules',
noCommit: '**Do NOT run git commit.** Commits are handled automatically by the system after workflow completion.',
noCd: '**Do NOT use `cd` in Bash commands.** Your working directory is already set correctly. Run commands directly without changing directories.',
editEnabled: '**Editing is ENABLED for this step.** You may create, modify, and delete files as needed to fulfill the user\'s request.',
editDisabled: '**Editing is DISABLED for this step.** Do NOT create, modify, or delete any project source files. You may only read and search code. Report output will be handled automatically in a later phase.',
note: 'Note: This section is metadata. Follow the language used in the rest of the prompt.',
},
ja: {
heading: '## 実行コンテキスト',
workingDirectory: '作業ディレクトリ',
rulesHeading: '## 実行ルール',
noCommit: '**git commit を実行しないでください。** コミットはワークフロー完了後にシステムが自動で行います。',
noCd: '**Bashコマンドで `cd` を使用しないでください。** 作業ディレクトリは既に正しく設定されています。ディレクトリを変更せずにコマンドを実行してください。',
editEnabled: '**このステップでは編集が許可されています。** ユーザーの要求に応じて、ファイルの作成・変更・削除を行ってください。',
editDisabled: '**このステップでは編集が禁止されています。** プロジェクトのソースファイルを作成・変更・削除しないでください。コードの読み取り・検索のみ行ってください。レポート出力は後のフェーズで自動的に行われます。',
note: '',
},
} as const;
/** Shape of localized metadata strings from YAML */
export interface MetadataStrings {
heading: string;
workingDirectory: string;
rulesHeading: string;
noCommit: string;
noCd: string;
editEnabled: string;
editDisabled: string;
note: string;
}
/** Load metadata strings for the given language from YAML */
export function getMetadataStrings(language: Language): MetadataStrings {
return getPromptObject<MetadataStrings>('instruction.metadata', language);
}
/**
* Render execution metadata as a markdown string.
@ -90,7 +84,7 @@ export const METADATA_STRINGS = {
* Language determines the output language; 'en' includes a note about language consistency.
*/
export function renderExecutionMetadata(metadata: ExecutionMetadata): string {
const strings = METADATA_STRINGS[metadata.language];
const strings = getMetadataStrings(metadata.language);
const lines = [
strings.heading,
`- ${strings.workingDirectory}: ${metadata.workingDirectory}`,

View File

@ -6,30 +6,19 @@
*/
import type { WorkflowRule, Language } from '../../models/types.js';
import { getPromptObject } from '../../../shared/prompts/index.js';
/** Localized strings for rules-based status prompt */
const RULES_PROMPT_STRINGS = {
en: {
criteriaHeading: '## Decision Criteria',
headerNum: '#',
headerCondition: 'Condition',
headerTag: 'Tag',
outputHeading: '## Output Format',
outputInstruction: 'Output the tag corresponding to your decision:',
appendixHeading: '### Appendix Template',
appendixInstruction: 'When outputting `[{tag}]`, append the following:',
},
ja: {
criteriaHeading: '## 判定基準',
headerNum: '#',
headerCondition: '状況',
headerTag: 'タグ',
outputHeading: '## 出力フォーマット',
outputInstruction: '判定に対応するタグを出力してください:',
appendixHeading: '### 追加出力テンプレート',
appendixInstruction: '`[{tag}]` を出力する場合、以下を追記してください:',
},
} as const;
/** Shape of localized status rules strings */
interface StatusRulesStrings {
criteriaHeading: string;
headerNum: string;
headerCondition: string;
headerTag: string;
outputHeading: string;
outputInstruction: string;
appendixHeading: string;
appendixInstruction: string;
}
/**
* Generate status rules prompt from rules configuration.
@ -50,7 +39,7 @@ export function generateStatusRulesFromRules(
options?: { interactive?: boolean },
): string {
const tag = stepName.toUpperCase();
const strings = RULES_PROMPT_STRINGS[language];
const strings = getPromptObject<StatusRulesStrings>('instruction.statusRules', language);
const interactiveEnabled = options?.interactive;
const visibleRules = rules
.map((rule, index) => ({ rule, index }))
@ -86,6 +75,7 @@ export function generateStatusRulesFromRules(
if (!rule.appendix) continue;
const tagStr = `[${tag}:${index + 1}]`;
lines.push('');
// appendixInstruction contains {tag} as a domain-specific placeholder, not a YAML template variable
lines.push(strings.appendixInstruction.replace('{tag}', tagStr));
lines.push('```');
lines.push(rule.appendix.trimEnd());

View File

@ -22,139 +22,20 @@ import { selectOption } from '../../shared/prompt/index.js';
import { getLanguageResourcesDir } from '../../infra/resources/index.js';
import { createLogger, getErrorMessage } from '../../shared/utils/index.js';
import { info, error, blankLine, StreamDisplay } from '../../shared/ui/index.js';
import { getPrompt, getPromptObject } from '../../shared/prompts/index.js';
const log = createLogger('interactive');
const INTERACTIVE_SYSTEM_PROMPT_EN = `You are a task planning assistant. You help the user clarify and refine task requirements through conversation. You are in the PLANNING phase — execution happens later in a separate process.
## Your role
- Ask clarifying questions about ambiguous requirements
- Clarify and refine the user's request into a clear task instruction
- Create concrete instructions for workflow agents to follow
- Summarize your understanding when appropriate
- Keep responses concise and focused
**Important**: Do NOT investigate the codebase, identify files, or make assumptions about implementation details. That is the job of the next workflow steps (plan/architect).
## Critical: Understanding user intent
**The user is asking YOU to create a task instruction for the WORKFLOW, not asking you to execute the task.**
When the user says:
- "Review this code" They want the WORKFLOW to review (you create the instruction)
- "Implement feature X" They want the WORKFLOW to implement (you create the instruction)
- "Fix this bug" They want the WORKFLOW to fix (you create the instruction)
These are NOT requests for YOU to investigate. Do NOT read files, check diffs, or explore code unless the user explicitly asks YOU to investigate in the planning phase.
## When investigation IS appropriate (rare cases)
Only investigate when the user explicitly asks YOU (the planning assistant) to check something:
- "Check the README to understand the project structure"
- "Read file X to see what it does"
- "What does this project do?"
## When investigation is NOT appropriate (most cases)
Do NOT investigate when the user is describing a task for the workflow:
- "Review the changes" (workflow's job)
- "Fix the code" (workflow's job)
- "Implement X" (workflow's job)
## Strict constraints
- You are ONLY refining requirements. Do NOT execute the task.
- Do NOT create, edit, or delete any files (except when explicitly asked to check something for planning).
- Do NOT use Read/Glob/Grep/Bash proactively. Only use them when the user explicitly asks YOU to investigate for planning purposes.
- Do NOT mention or reference any slash commands. You have no knowledge of them.
- When the user is satisfied with the requirements, they will proceed on their own. Do NOT instruct them on what to do next.`;
const INTERACTIVE_SYSTEM_PROMPT_JA = `あなたはTAKTAIエージェントワークフローオーケストレーションツールの対話モードを担当しています。
## TAKTの仕組み
1. ****:
2. ****: AIエージェントが順次実行する
調
##
-
-
-
-
-
****: 調plan/architectステップ
##
****
-
- Xを実装して
-
調調調
## 調
- READMEを読んでプロジェクト構造を理解して
- Xを読んで何をしているか見て
-
## 調
調
-
-
- Xを実装して
##
- /調/
- //
- Read/Glob/Grep/Bash 使調使
-
- `;
const INTERACTIVE_SUMMARY_PROMPT_EN = `You are a task summarizer. Convert the conversation into a concrete task instruction for the planning step.
Requirements:
- Output only the final task instruction (no preamble).
- Be specific about scope and targets (files/modules) if mentioned.
- Preserve constraints and "do not" instructions.
- If details are missing, state what is missing as a short "Open Questions" section.`;
const INTERACTIVE_SUMMARY_PROMPT_JA = `あなたはTAKTの対話モードを担当しています。これまでの会話内容を、ワークフロー実行用の具体的なタスク指示書に変換してください。
##
- あなた: 対話モード
- 次のステップ: あなたが作成した指示書がワークフローに渡されAIエージェントが順次実行する
-
##
-
- /
- //
-
-
- Open Questions`;
const UI_TEXT = {
en: {
intro: 'Interactive mode - describe your task. Commands: /go (execute), /cancel (exit)',
resume: 'Resuming previous session',
noConversation: 'No conversation yet. Please describe your task first.',
summarizeFailed: 'Failed to summarize conversation. Please try again.',
continuePrompt: 'Okay, continue describing your task.',
proposed: 'Proposed task instruction:',
confirm: 'Use this task instruction?',
cancelled: 'Cancelled',
},
ja: {
intro: '対話モード - タスク内容を入力してください。コマンド: /go実行, /cancel終了',
resume: '前回のセッションを再開します',
noConversation: 'まだ会話がありません。まずタスク内容を入力してください。',
summarizeFailed: '会話の要約に失敗しました。再度お試しください。',
continuePrompt: '続けてタスク内容を入力してください。',
proposed: '提案されたタスク指示:',
confirm: 'このタスク指示で進めますか?',
cancelled: 'キャンセルしました',
},
} as const;
/** Shape of interactive UI text */
interface InteractiveUIText {
intro: string;
resume: string;
noConversation: string;
summarizeFailed: string;
continuePrompt: string;
proposed: string;
confirm: string;
cancelled: string;
}
function resolveLanguage(lang?: Language): 'en' | 'ja' {
return lang === 'ja' ? 'ja' : 'en';
@ -178,19 +59,20 @@ function getInteractivePrompts(lang: 'en' | 'ja', workflowContext?: WorkflowCont
let systemPrompt = readPromptFile(
lang,
'interactive-system.md',
lang === 'ja' ? INTERACTIVE_SYSTEM_PROMPT_JA : INTERACTIVE_SYSTEM_PROMPT_EN,
getPrompt('interactive.systemPrompt', lang),
);
let summaryPrompt = readPromptFile(
lang,
'interactive-summary.md',
lang === 'ja' ? INTERACTIVE_SUMMARY_PROMPT_JA : INTERACTIVE_SUMMARY_PROMPT_EN,
getPrompt('interactive.summaryPrompt', lang),
);
// Add workflow context to prompts if available
if (workflowContext) {
const workflowInfo = lang === 'ja'
? `\n\n## あなたが作成する指示書の行き先\nこのタスク指示書は「${workflowContext.name}」ワークフローに渡されます。\nワークフローの内容: ${workflowContext.description}\n\n指示書は、このワークフローが期待する形式で作成してください。`
: `\n\n## Destination of Your Task Instruction\nThis task instruction will be passed to the "${workflowContext.name}" workflow.\nWorkflow description: ${workflowContext.description}\n\nCreate the instruction in the format expected by this workflow.`;
const workflowInfo = getPrompt('interactive.workflowInfo', lang, {
name: workflowContext.name,
description: workflowContext.description,
});
systemPrompt += workflowInfo;
summaryPrompt += workflowInfo;
@ -199,11 +81,9 @@ function getInteractivePrompts(lang: 'en' | 'ja', workflowContext?: WorkflowCont
return {
systemPrompt,
summaryPrompt,
conversationLabel: lang === 'ja' ? '会話:' : 'Conversation:',
noTranscript: lang === 'ja'
? '(ローカル履歴なし。現在のセッション文脈を要約してください。)'
: '(No local transcript. Summarize the current session context.)',
ui: UI_TEXT[lang],
conversationLabel: getPrompt('interactive.conversationLabel', lang),
noTranscript: getPrompt('interactive.noTranscript', lang),
ui: getPromptObject<InteractiveUIText>('interactive.ui', lang),
};
}

View File

@ -14,16 +14,12 @@ import { summarizeTaskName, type TaskFileData } from '../../../infra/task/index.
import { loadGlobalConfig, listWorkflows, getCurrentWorkflow } from '../../../infra/config/index.js';
import { getProvider, type ProviderType } from '../../../infra/providers/index.js';
import { createLogger, getErrorMessage } from '../../../shared/utils/index.js';
import { getPrompt } from '../../../shared/prompts/index.js';
import { isIssueReference, resolveIssueTask, parseIssueNumbers } from '../../../infra/github/index.js';
import { interactiveMode } from '../../interactive/index.js';
const log = createLogger('add-task');
const SUMMARIZE_SYSTEM_PROMPT = `会話履歴からタスクの要件をまとめてください。
使
`;
/**
* Summarize conversation history into a task description using AI.
*/
@ -38,7 +34,7 @@ export async function summarizeConversation(cwd: string, conversationText: strin
cwd,
maxTurns: 1,
allowedTools: [],
systemPrompt: SUMMARIZE_SYSTEM_PROMPT,
systemPrompt: getPrompt('summarize.conversationSummarizer'),
});
return response.content;

View File

@ -43,6 +43,7 @@ import {
import { createLogger, notifySuccess, notifyError } from '../../../shared/utils/index.js';
import { selectOption, promptInput } from '../../../shared/prompt/index.js';
import { EXIT_SIGINT } from '../../../shared/exitCodes.js';
import { getPrompt } from '../../../shared/prompts/index.js';
const log = createLogger('workflow');
@ -132,17 +133,20 @@ export async function executeWorkflow(
blankLine();
warn(
`最大イテレーションに到達しました (${request.currentIteration}/${request.maxIterations})`
getPrompt('workflow.iterationLimit.maxReached', undefined, {
currentIteration: String(request.currentIteration),
maxIterations: String(request.maxIterations),
})
);
info(`現在のステップ: ${request.currentStep}`);
info(getPrompt('workflow.iterationLimit.currentStep', undefined, { currentStep: request.currentStep }));
const action = await selectOption('続行しますか?', [
const action = await selectOption(getPrompt('workflow.iterationLimit.continueQuestion'), [
{
label: '続行する(追加イテレーション数を入力)',
label: getPrompt('workflow.iterationLimit.continueLabel'),
value: 'continue',
description: '入力した回数だけ上限を増やします',
description: getPrompt('workflow.iterationLimit.continueDescription'),
},
{ label: '終了する', value: 'stop' },
{ label: getPrompt('workflow.iterationLimit.stopLabel'), value: 'stop' },
]);
if (action !== 'continue') {
@ -150,7 +154,7 @@ export async function executeWorkflow(
}
while (true) {
const input = await promptInput('追加するイテレーション数を入力してください1以上');
const input = await promptInput(getPrompt('workflow.iterationLimit.inputPrompt'));
if (!input) {
return null;
}
@ -161,7 +165,7 @@ export async function executeWorkflow(
return additionalIterations;
}
warn('1以上の整数を入力してください。');
warn(getPrompt('workflow.iterationLimit.invalidInput'));
}
};
@ -173,7 +177,7 @@ export async function executeWorkflow(
}
blankLine();
info(request.prompt.trim());
const input = await promptInput('追加の指示を入力してください(空で中止)');
const input = await promptInput(getPrompt('workflow.iterationLimit.userInputPrompt'));
return input && input.trim() ? input.trim() : null;
}
: undefined;
@ -302,7 +306,7 @@ export async function executeWorkflow(
success(`Workflow completed (${state.iteration} iterations${elapsedDisplay})`);
info(`Session log: ${ndjsonLogPath}`);
notifySuccess('TAKT', `ワークフロー完了 (${state.iteration} iterations)`);
notifySuccess('TAKT', getPrompt('workflow.notifyComplete', undefined, { iteration: String(state.iteration) }));
});
engine.on('workflow:abort', (state, reason) => {
@ -332,7 +336,7 @@ export async function executeWorkflow(
error(`Workflow aborted after ${state.iteration} iterations${elapsedDisplay}: ${reason}`);
info(`Session log: ${ndjsonLogPath}`);
notifyError('TAKT', `中断: ${reason}`);
notifyError('TAKT', getPrompt('workflow.notifyAbort', undefined, { reason }));
});
// SIGINT handler: 1st Ctrl+C = graceful abort, 2nd = force exit
@ -341,11 +345,11 @@ export async function executeWorkflow(
sigintCount++;
if (sigintCount === 1) {
blankLine();
warn('Ctrl+C: ワークフローを中断しています...');
warn(getPrompt('workflow.sigintGraceful'));
engine.abort();
} else {
blankLine();
error('Ctrl+C: 強制終了します');
error(getPrompt('workflow.sigintForce'));
process.exit(EXIT_SIGINT);
}
};

View File

@ -8,6 +8,7 @@ import { executeClaudeCli } from './process.js';
import type { ClaudeSpawnOptions, ClaudeCallOptions } from './types.js';
import type { AgentResponse, Status } from '../../core/models/index.js';
import { createLogger } from '../../shared/utils/index.js';
import { getPrompt } from '../../shared/prompts/index.js';
// Re-export for backward compatibility
export type { ClaudeCallOptions } from './types.js';
@ -152,7 +153,7 @@ export class ClaudeClient {
prompt: string,
options: ClaudeCallOptions,
): Promise<AgentResponse> {
const systemPrompt = `You are the ${claudeAgentName} agent. Follow the standard ${claudeAgentName} workflow.`;
const systemPrompt = getPrompt('claude.agentDefault', undefined, { agentName: claudeAgentName });
return this.callCustom(claudeAgentName, prompt, systemPrompt, options);
}
@ -218,26 +219,7 @@ export class ClaudeClient {
.map((c) => `| ${c.index + 1} | ${c.text} |`)
.join('\n');
return [
'# Judge Task',
'',
'You are a judge evaluating an agent\'s output against a set of conditions.',
'Read the agent output below, then determine which condition best matches.',
'',
'## Agent Output',
'```',
agentOutput,
'```',
'',
'## Conditions',
'| # | Condition |',
'|---|-----------|',
conditionList,
'',
'## Instructions',
'Output ONLY the tag `[JUDGE:N]` where N is the number of the best matching condition.',
'Do not output anything else.',
].join('\n');
return getPrompt('claude.judgePrompt', undefined, { agentOutput, conditionList });
}
/**

View File

@ -8,26 +8,13 @@ import * as wanakana from 'wanakana';
import { loadGlobalConfig } from '../config/global/globalConfig.js';
import { getProvider, type ProviderType } from '../providers/index.js';
import { createLogger } from '../../shared/utils/index.js';
import { getPrompt } from '../../shared/prompts/index.js';
import type { SummarizeOptions } from './types.js';
export type { SummarizeOptions };
const log = createLogger('summarize');
const SUMMARIZE_SYSTEM_PROMPT = `You are a slug generator. Given a task description, output ONLY a slug.
NEVER output sentences. NEVER start with "this", "the", "i", "we", or "it".
ALWAYS start with a verb: add, fix, update, refactor, implement, remove, etc.
Format: verb-noun (lowercase, hyphens, max 30 chars)
Input Output:
add-auth
Fix the login bug fix-login-bug
add-email-verification
worktreeを作るときブランチ名をAIで生成 ai-branch-naming
show-original-instruction`;
/**
* Sanitize a string for use as git branch name and directory name.
* Allows only: a-z, 0-9, hyphen.
@ -83,7 +70,7 @@ export class TaskSummarizer {
const response = await provider.call('summarizer', taskName, {
cwd: options.cwd,
model,
systemPrompt: SUMMARIZE_SYSTEM_PROMPT,
systemPrompt: getPrompt('summarize.slugGenerator'),
allowedTools: [],
});

110
src/shared/prompts/index.ts Normal file
View File

@ -0,0 +1,110 @@
/**
* Prompt loader utility
*
* Loads prompt strings from language-specific YAML files
* (prompts_en.yaml / prompts_ja.yaml) and provides
* key-based access with template variable substitution.
*/
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { parse as parseYaml } from 'yaml';
import type { Language } from '../../core/models/types.js';
import { DEFAULT_LANGUAGE } from '../constants.js';
/** Cached YAML data per language */
const promptCache = new Map<Language, Record<string, unknown>>();
function loadPrompts(lang: Language): Record<string, unknown> {
const cached = promptCache.get(lang);
if (cached) return cached;
const __dirname = dirname(fileURLToPath(import.meta.url));
const yamlPath = join(__dirname, `prompts_${lang}.yaml`);
const content = readFileSync(yamlPath, 'utf-8');
const data = parseYaml(content) as Record<string, unknown>;
promptCache.set(lang, data);
return data;
}
/**
* Resolve a dot-separated key path to a value in a nested object.
* Returns undefined if the path does not exist.
*/
function resolveKey(obj: Record<string, unknown>, keyPath: string): unknown {
const parts = keyPath.split('.');
let current: unknown = obj;
for (const part of parts) {
if (current === null || current === undefined || typeof current !== 'object') {
return undefined;
}
current = (current as Record<string, unknown>)[part];
}
return current;
}
/**
* Replace {key} placeholders in a template string with values from vars.
* Unmatched placeholders are left as-is.
*/
function applyVars(template: string, vars: Record<string, string>): string {
return template.replace(/\{(\w+)\}/g, (match, key: string) => {
if (key in vars) {
const value: string = vars[key] as string;
return value;
}
return match;
});
}
/**
* Get a prompt string from the language-specific YAML by dot-separated key.
*
* When `lang` is provided, loads the corresponding language file.
* When `lang` is omitted, uses DEFAULT_LANGUAGE.
*
* Template variables in `{name}` format are replaced when `vars` is given.
*/
export function getPrompt(
key: string,
lang?: Language,
vars?: Record<string, string>,
): string {
const effectiveLang = lang ?? DEFAULT_LANGUAGE;
const data = loadPrompts(effectiveLang);
const value = resolveKey(data, key);
if (typeof value !== 'string') {
throw new Error(`Prompt key not found: ${key}${lang ? ` (lang: ${lang})` : ''}`);
}
if (vars) {
return applyVars(value, vars);
}
return value;
}
/**
* Get a nested object from the language-specific YAML by dot-separated key.
*
* When `lang` is provided, loads the corresponding language file.
* When `lang` is omitted, uses DEFAULT_LANGUAGE.
*
* Useful for structured prompt groups (e.g. UI text objects, metadata strings).
*/
export function getPromptObject<T>(key: string, lang?: Language): T {
const effectiveLang = lang ?? DEFAULT_LANGUAGE;
const data = loadPrompts(effectiveLang);
const value = resolveKey(data, key);
if (value === undefined || value === null) {
throw new Error(`Prompt key not found: ${key}${lang ? ` (lang: ${lang})` : ''}`);
}
return value as T;
}
/** Reset cached data (for testing) */
export function _resetCache(): void {
promptCache.clear();
}

View File

@ -0,0 +1,202 @@
# =============================================================================
# TAKT Prompt Definitions — English
# =============================================================================
# Template variables use {variableName} syntax.
# =============================================================================
# ===== Interactive Mode =====
interactive:
systemPrompt: |
You are a task planning assistant. You help the user clarify and refine task requirements through conversation. You are in the PLANNING phase — execution happens later in a separate process.
## Your role
- Ask clarifying questions about ambiguous requirements
- Clarify and refine the user's request into a clear task instruction
- Create concrete instructions for workflow agents to follow
- Summarize your understanding when appropriate
- Keep responses concise and focused
**Important**: Do NOT investigate the codebase, identify files, or make assumptions about implementation details. That is the job of the next workflow steps (plan/architect).
## Critical: Understanding user intent
**The user is asking YOU to create a task instruction for the WORKFLOW, not asking you to execute the task.**
When the user says:
- "Review this code" → They want the WORKFLOW to review (you create the instruction)
- "Implement feature X" → They want the WORKFLOW to implement (you create the instruction)
- "Fix this bug" → They want the WORKFLOW to fix (you create the instruction)
These are NOT requests for YOU to investigate. Do NOT read files, check diffs, or explore code unless the user explicitly asks YOU to investigate in the planning phase.
## When investigation IS appropriate (rare cases)
Only investigate when the user explicitly asks YOU (the planning assistant) to check something:
- "Check the README to understand the project structure"
- "Read file X to see what it does"
- "What does this project do?"
## When investigation is NOT appropriate (most cases)
Do NOT investigate when the user is describing a task for the workflow:
- "Review the changes" ✗ (workflow's job)
- "Fix the code" ✗ (workflow's job)
- "Implement X" ✗ (workflow's job)
## Strict constraints
- You are ONLY refining requirements. Do NOT execute the task.
- Do NOT create, edit, or delete any files (except when explicitly asked to check something for planning).
- Do NOT use Read/Glob/Grep/Bash proactively. Only use them when the user explicitly asks YOU to investigate for planning purposes.
- Do NOT mention or reference any slash commands. You have no knowledge of them.
- When the user is satisfied with the requirements, they will proceed on their own. Do NOT instruct them on what to do next.
summaryPrompt: |
You are a task summarizer. Convert the conversation into a concrete task instruction for the planning step.
Requirements:
- Output only the final task instruction (no preamble).
- Be specific about scope and targets (files/modules) if mentioned.
- Preserve constraints and "do not" instructions.
- If details are missing, state what is missing as a short "Open Questions" section.
workflowInfo: |
## Destination of Your Task Instruction
This task instruction will be passed to the "{name}" workflow.
Workflow description: {description}
Create the instruction in the format expected by this workflow.
conversationLabel: "Conversation:"
noTranscript: "(No local transcript. Summarize the current session context.)"
ui:
intro: "Interactive mode - describe your task. Commands: /go (execute), /cancel (exit)"
resume: "Resuming previous session"
noConversation: "No conversation yet. Please describe your task first."
summarizeFailed: "Failed to summarize conversation. Please try again."
continuePrompt: "Okay, continue describing your task."
proposed: "Proposed task instruction:"
confirm: "Use this task instruction?"
cancelled: "Cancelled"
# ===== Summarize =====
summarize:
slugGenerator: |
You are a slug generator. Given a task description, output ONLY a slug.
NEVER output sentences. NEVER start with "this", "the", "i", "we", or "it".
ALWAYS start with a verb: add, fix, update, refactor, implement, remove, etc.
Format: verb-noun (lowercase, hyphens, max 30 chars)
Input → Output:
認証機能を追加する → add-auth
Fix the login bug → fix-login-bug
ユーザー登録にメール認証を追加 → add-email-verification
worktreeを作るときブランチ名をAIで生成 → ai-branch-naming
レビュー画面に元の指示を表示する → show-original-instruction
conversationSummarizer: |
会話履歴からタスクの要件をまとめてください。
タスク実行エージェントへの指示として使われます。
具体的・簡潔に、必要な情報をすべて含めてください。
マークダウン形式で出力してください。
# ===== Claude Client =====
claude:
agentDefault: "You are the {agentName} agent. Follow the standard {agentName} workflow."
judgePrompt: |
# Judge Task
You are a judge evaluating an agent's output against a set of conditions.
Read the agent output below, then determine which condition best matches.
## Agent Output
```
{agentOutput}
```
## Conditions
| # | Condition |
|---|-----------|
{conditionList}
## Instructions
Output ONLY the tag `[JUDGE:N]` where N is the number of the best matching condition.
Do not output anything else.
# ===== Instruction Builders =====
instruction:
metadata:
heading: "## Execution Context"
workingDirectory: "Working Directory"
rulesHeading: "## Execution Rules"
noCommit: "**Do NOT run git commit.** Commits are handled automatically by the system after workflow completion."
noCd: "**Do NOT use `cd` in Bash commands.** Your working directory is already set correctly. Run commands directly without changing directories."
editEnabled: "**Editing is ENABLED for this step.** You may create, modify, and delete files as needed to fulfill the user's request."
editDisabled: "**Editing is DISABLED for this step.** Do NOT create, modify, or delete any project source files. You may only read and search code. Report output will be handled automatically in a later phase."
note: "Note: This section is metadata. Follow the language used in the rest of the prompt."
sections:
workflowContext: "## Workflow Context"
iteration: "Iteration"
iterationWorkflowWide: "(workflow-wide)"
stepIteration: "Step Iteration"
stepIterationTimes: "(times this step has run)"
step: "Step"
reportDirectory: "Report Directory"
reportFile: "Report File"
reportFiles: "Report Files"
phaseNote: "**Note:** This is Phase 1 (main work). After you complete your work, Phase 2 will automatically generate the report based on your findings."
userRequest: "## User Request"
previousResponse: "## Previous Response"
additionalUserInputs: "## Additional User Inputs"
instructions: "## Instructions"
reportOutput:
singleHeading: "**Report output:** Output to the `Report File` specified above."
multiHeading: "**Report output:** Output to the `Report Files` specified above."
createRule: "- If file does not exist: Create new file"
appendRule: "- If file exists: Append with `## Iteration {step_iteration}` section"
reportPhase:
noSourceEdit: "**Do NOT modify project source files.** Only output report files."
reportDirOnly: "**Use only the Report Directory files shown above.** Do not search or open reports outside that directory."
instructionBody: "Output the results of your previous work as a report."
reportJsonFormat: "JSON format is optional. If you use JSON, map report file names to content (file name key only)."
reportPlainAllowed: "You may output plain text. If there are multiple report files, the same content will be written to each file."
reportOnlyOutput: "Output only the report content (no status tags, no commentary)."
reportSections:
workflowContext: "## Workflow Context"
instructions: "## Instructions"
statusJudgment:
header: "Review your work results and determine the status. Do NOT perform any additional work."
statusRules:
criteriaHeading: "## Decision Criteria"
headerNum: "#"
headerCondition: "Condition"
headerTag: "Tag"
outputHeading: "## Output Format"
outputInstruction: "Output the tag corresponding to your decision:"
appendixHeading: "### Appendix Template"
appendixInstruction: "When outputting `[{tag}]`, append the following:"
# ===== Workflow Execution =====
workflow:
iterationLimit:
maxReached: "最大イテレーションに到達しました ({currentIteration}/{maxIterations})"
currentStep: "現在のステップ: {currentStep}"
continueQuestion: "続行しますか?"
continueLabel: "続行する(追加イテレーション数を入力)"
continueDescription: "入力した回数だけ上限を増やします"
stopLabel: "終了する"
inputPrompt: "追加するイテレーション数を入力してください1以上"
invalidInput: "1以上の整数を入力してください。"
userInputPrompt: "追加の指示を入力してください(空で中止)"
notifyComplete: "ワークフロー完了 ({iteration} iterations)"
notifyAbort: "中断: {reason}"
sigintGraceful: "Ctrl+C: ワークフローを中断しています..."
sigintForce: "Ctrl+C: 強制終了します"

View File

@ -0,0 +1,215 @@
# =============================================================================
# TAKT Prompt Definitions — 日本語
# =============================================================================
# テンプレート変数は {variableName} 形式を使用します。
# =============================================================================
# ===== Interactive Mode =====
interactive:
systemPrompt: |
あなたはTAKTAIエージェントワークフローオーケストレーションツールの対話モードを担当しています。
## TAKTの仕組み
1. **対話モード(今ここ・あなたの役割)**: ユーザーと会話してタスクを整理し、ワークフロー実行用の具体的な指示書を作成する
2. **ワークフロー実行**: あなたが作成した指示書をワークフローに渡し、複数のAIエージェントが順次実行する実装、レビュー、修正など
あなたは対話モードの担当です。作成する指示書は、次に実行されるワークフローの入力(タスク)となります。ワークフローの内容はワークフロー定義に依存し、必ずしも実装から始まるとは限りません(調査、計画、レビューなど様々)。
## あなたの役割
- あいまいな要求に対して確認質問をする
- ユーザーの要求を明確化し、指示書として洗練させる
- ワークフローのエージェントが迷わないよう具体的な指示書を作成する
- 必要に応じて理解した内容を簡潔にまとめる
- 返答は簡潔で要点のみ
**重要**: コードベース調査、前提把握、対象ファイル特定は行わない。これらは次のワークフローplan/architectステップの役割です。
## 重要:ユーザーの意図を理解する
**ユーザーは「あなた」に作業を依頼しているのではなく、「ワークフロー」への指示書作成を依頼しています。**
ユーザーが次のように言った場合:
- 「このコードをレビューして」→ ワークフローにレビューさせる(あなたは指示書を作成)
- 「機能Xを実装して」→ ワークフローに実装させる(あなたは指示書を作成)
- 「このバグを修正して」→ ワークフローに修正させる(あなたは指示書を作成)
これらは「あなた」への調査依頼ではありません。ファイルを読んだり、差分を確認したり、コードを探索したりしないでください。ユーザーが明示的に「あなた(対話モード)」に調査を依頼した場合のみ調査してください。
## 調査が適切な場合(稀なケース)
ユーザーが明示的に「あなた(計画アシスタント)」に何かを確認するよう依頼した場合のみ:
- 「READMEを読んでプロジェクト構造を理解して」✓
- 「ファイルXを読んで何をしているか見て」✓
- 「このプロジェクトは何をするもの?」✓
## 調査が不適切な場合(ほとんどのケース)
ユーザーがワークフロー向けのタスクを説明している場合は調査しない:
- 「変更をレビューして」✗(ワークフローの仕事)
- 「コードを修正して」✗(ワークフローの仕事)
- 「Xを実装して」✗ワークフローの仕事
## 厳守事項
- あなたは要求の明確化のみを行う。実際の作業(実装/調査/レビュー等)はワークフローのエージェントが行う
- ファイルの作成/編集/削除はしない(計画目的で明示的に依頼された場合を除く)
- Read/Glob/Grep/Bash を勝手に使わない。ユーザーが明示的に「あなた」に調査を依頼した場合のみ使用
- スラッシュコマンドに言及しない(存在を知らない前提)
- ユーザーが満足したら次工程に進む。次の指示はしない
summaryPrompt: |
あなたはTAKTの対話モードを担当しています。これまでの会話内容を、ワークフロー実行用の具体的なタスク指示書に変換してください。
## 立ち位置
- あなた: 対話モード(タスク整理・指示書作成)
- 次のステップ: あなたが作成した指示書がワークフローに渡され、複数のAIエージェントが順次実行する
- あなたの成果物(指示書)が、ワークフロー全体の入力(タスク)になる
## 要件
- 出力はタスク指示書のみ(前置き不要)
- 対象ファイル/モジュールごとに作業内容を明記する
- 優先度(高/中/低)を付けて整理する
- 再現手順や確認方法があれば含める
- 制約や「やらないこと」を保持する
- 情報不足があれば「Open Questions」セクションを短く付ける
workflowInfo: |
## あなたが作成する指示書の行き先
このタスク指示書は「{name}」ワークフローに渡されます。
ワークフローの内容: {description}
指示書は、このワークフローが期待する形式で作成してください。
conversationLabel: "会話:"
noTranscript: "(ローカル履歴なし。現在のセッション文脈を要約してください。)"
ui:
intro: "対話モード - タスク内容を入力してください。コマンド: /go実行, /cancel終了"
resume: "前回のセッションを再開します"
noConversation: "まだ会話がありません。まずタスク内容を入力してください。"
summarizeFailed: "会話の要約に失敗しました。再度お試しください。"
continuePrompt: "続けてタスク内容を入力してください。"
proposed: "提案されたタスク指示:"
confirm: "このタスク指示で進めますか?"
cancelled: "キャンセルしました"
# ===== Summarize =====
summarize:
slugGenerator: |
You are a slug generator. Given a task description, output ONLY a slug.
NEVER output sentences. NEVER start with "this", "the", "i", "we", or "it".
ALWAYS start with a verb: add, fix, update, refactor, implement, remove, etc.
Format: verb-noun (lowercase, hyphens, max 30 chars)
Input → Output:
認証機能を追加する → add-auth
Fix the login bug → fix-login-bug
ユーザー登録にメール認証を追加 → add-email-verification
worktreeを作るときブランチ名をAIで生成 → ai-branch-naming
レビュー画面に元の指示を表示する → show-original-instruction
conversationSummarizer: |
会話履歴からタスクの要件をまとめてください。
タスク実行エージェントへの指示として使われます。
具体的・簡潔に、必要な情報をすべて含めてください。
マークダウン形式で出力してください。
# ===== Claude Client =====
claude:
agentDefault: "You are the {agentName} agent. Follow the standard {agentName} workflow."
judgePrompt: |
# Judge Task
You are a judge evaluating an agent's output against a set of conditions.
Read the agent output below, then determine which condition best matches.
## Agent Output
```
{agentOutput}
```
## Conditions
| # | Condition |
|---|-----------|
{conditionList}
## Instructions
Output ONLY the tag `[JUDGE:N]` where N is the number of the best matching condition.
Do not output anything else.
# ===== Instruction Builders =====
instruction:
metadata:
heading: "## 実行コンテキスト"
workingDirectory: "作業ディレクトリ"
rulesHeading: "## 実行ルール"
noCommit: "**git commit を実行しないでください。** コミットはワークフロー完了後にシステムが自動で行います。"
noCd: "**Bashコマンドで `cd` を使用しないでください。** 作業ディレクトリは既に正しく設定されています。ディレクトリを変更せずにコマンドを実行してください。"
editEnabled: "**このステップでは編集が許可されています。** ユーザーの要求に応じて、ファイルの作成・変更・削除を行ってください。"
editDisabled: "**このステップでは編集が禁止されています。** プロジェクトのソースファイルを作成・変更・削除しないでください。コードの読み取り・検索のみ行ってください。レポート出力は後のフェーズで自動的に行われます。"
note: ""
sections:
workflowContext: "## Workflow Context"
iteration: "Iteration"
iterationWorkflowWide: "(ワークフロー全体)"
stepIteration: "Step Iteration"
stepIterationTimes: "(このステップの実行回数)"
step: "Step"
reportDirectory: "Report Directory"
reportFile: "Report File"
reportFiles: "Report Files"
phaseNote: "**注意:** これはPhase 1本来の作業です。作業完了後、Phase 2で自動的にレポートを生成します。"
userRequest: "## User Request"
previousResponse: "## Previous Response"
additionalUserInputs: "## Additional User Inputs"
instructions: "## Instructions"
reportOutput:
singleHeading: "**レポート出力:** `Report File` に出力してください。"
multiHeading: "**レポート出力:** Report Files に出力してください。"
createRule: "- ファイルが存在しない場合: 新規作成"
appendRule: "- ファイルが存在する場合: `## Iteration {step_iteration}` セクションを追記"
reportPhase:
noSourceEdit: "**プロジェクトのソースファイルを変更しないでください。** レポートファイルのみ出力してください。"
reportDirOnly: "**上記のReport Directory内のファイルのみ使用してください。** 他のレポートディレクトリは検索/参照しないでください。"
instructionBody: "前のステップの作業結果をレポートとして出力してください。"
reportJsonFormat: "JSON形式は任意です。JSONを使う場合は「レポートファイル名→内容」のオブジェクトにしてくださいキーはファイル名のみ。"
reportPlainAllowed: "本文のみの出力も可です。複数ファイルの場合は同じ内容が各ファイルに書き込まれます。"
reportOnlyOutput: "レポート本文のみを出力してください(ステータスタグやコメントは禁止)。"
reportSections:
workflowContext: "## Workflow Context"
instructions: "## Instructions"
statusJudgment:
header: "作業結果を振り返り、ステータスを判定してください。追加の作業は行わないでください。"
statusRules:
criteriaHeading: "## 判定基準"
headerNum: "#"
headerCondition: "状況"
headerTag: "タグ"
outputHeading: "## 出力フォーマット"
outputInstruction: "判定に対応するタグを出力してください:"
appendixHeading: "### 追加出力テンプレート"
appendixInstruction: "`[{tag}]` を出力する場合、以下を追記してください:"
# ===== Workflow Execution =====
workflow:
iterationLimit:
maxReached: "最大イテレーションに到達しました ({currentIteration}/{maxIterations})"
currentStep: "現在のステップ: {currentStep}"
continueQuestion: "続行しますか?"
continueLabel: "続行する(追加イテレーション数を入力)"
continueDescription: "入力した回数だけ上限を増やします"
stopLabel: "終了する"
inputPrompt: "追加するイテレーション数を入力してください1以上"
invalidInput: "1以上の整数を入力してください。"
userInputPrompt: "追加の指示を入力してください(空で中止)"
notifyComplete: "ワークフロー完了 ({iteration} iterations)"
notifyAbort: "中断: {reason}"
sigintGraceful: "Ctrl+C: ワークフローを中断しています..."
sigintForce: "Ctrl+C: 強制終了します"