2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
3> Use this file to discover all available pages before exploring further.3> Use this file to discover all available pages before exploring further.
4 4
5# Todo リスト5# Todo を追跡する
6 6
7> Claude Agent SDK を使用して todo を追跡・表示し、タスク管理を整理します7> Agent SDK セッションで todo を追跡し、構造化されたツール呼び出しから Claude の進捗をアプリケーションでレンダリングします
8 8
9Todo 追跡は、タスクを管理し、ユーザーに進捗を表示するための構造化された方法を提供します。Claude Agent SDK には、複雑なワークフローを整理し、ユーザーにタスク進捗を知らせるのに役立つ組み込み todo 機能が含まれています。9[モデル利用可能性](#model-availability)に記載されているモデルでは、Claude は書かれた todo リストなしで複数ステップの作業を追跡し、Claude Code はデフォルトでセッションから[タスク追跡ツール](/docs/ja/tools-reference#task-tool-availability)を除外します。これらのモデルで Claude が複数ステップのタスクを処理するために、このページの内容は必要ありません。
10
11タスク追跡ツールを持つセッションでは、Claude は書かれた todo リストを保持し、作業を進めるにつれて各アイテムのステータスを更新します。メッセージストリーム内で各変更が構造化されたツール呼び出しとして表示されます。アプリケーションがこれらのツール呼び出しを読み取る場合にのみセッションをオプトインしてください。タスク活動をログするか、独自の進捗表示をレンダリングするかどうかに関わらず。
12
13<h2 id="model-availability">
14 モデル利用可能性
15</h2>
10 16
11<Note>17<Note>
12 TypeScript Agent SDK 0.3.142 および Claude Code v2.1.142 以降、セッションは `TodoWrite` の代わりに構造化された Task ツール `TaskCreate`、`TaskUpdate`、`TaskGet`、および `TaskList` を使用します。Python SDK は Python パッケージバージョンではなく、起動する Claude Code CLI からこの変更を取得します。スイッチは、その CLI(pip パッケージ内にバンドルされているコピー、または `cli_path` で指定するコピー)が v2.1.142 以降の場合に適用されます。監視コードの変更方法については、[Task ツールへの移行](#migrate-to-task-tools)を参照してください。このページの例では、まだ移行していないセッションの `TodoWrite` を引き続き表示するために `CLAUDE_CODE_ENABLE_TASKS=0` を設定しています。18 TypeScript Agent SDK 0.3.233 以降、または Python Agent SDK 0.2.139 以降では、以下の制限が適用されます。
19
20 The following tools aren't available on Opus 4.8, Sonnet 5, Fable 5, Mythos 5, or later versions of those families unless you opt in:
21
22 * `TodoWrite`
23 * `TaskCreate`
24 * `TaskGet`
25 * `TaskUpdate`
26 * `TaskList`
27
28 On other models, Claude Code provides the Task tools by default and `TodoWrite` only when you set `CLAUDE_CODE_ENABLE_TASKS=0`.
13</Note>29</Note>
14 30
15<h3 id="todo-lifecycle">31記載されているモデルでは、セッションをオプトインしない限り、メッセージストリーム内でこれらのツールの `tool_use` ブロックは表示されません。Agent SDK は、バンドルされている Claude Code バイナリを通じてこれらのデフォルトを適用します。`pathToClaudeCodeExecutable`(TypeScript)または `cli_path`(Python)を独自の Claude Code インストールに指定する場合、そのインストールが提供するツールが、独自のデフォルトの下で取得されます。実行中のセッションで正確なセットを確認するには、[利用可能なツールを確認](/docs/ja/tools-reference#check-which-tools-are-available)してください。セッションをオプトインするには、以下のいずれかを実行してください:
32
33* [`allowedTools`](/docs/ja/agent-sdk/permissions#allow-and-deny-rules)(TypeScript)または `allowed_tools`(Python)オプションでツールの 1 つに名前を付ける
34* `tools` オプションにツールをリストします。これはセッションの組み込みツールを、それが名前を付けるものに制限します。使用する他の組み込みツールと一緒に必要なツールを含めます
35* このページの例のように、`env` オプションで `CLAUDE_CODE_ENABLE_TODO_TOOLS=1` を設定します。TypeScript では、`env` はサブプロセス環境を置き換えるため、継承された変数を保持するために `...process.env` を展開します。Python では、`env` は継承された環境の上にマージされます
36
37<h2 id="todo-lifecycle">
16 Todo ライフサイクル38 Todo ライフサイクル
17</h3>39</h2>
18 40
19Todo は予測可能なライフサイクルに従います:41Claude は各 todo を予測可能なライフサイクルを通じて移動させます:
20 42
211. **作成** - タスクが識別されたときに `pending` として作成される431. **作成**:Claude がタスクを識別したときに `pending` として todo を追加します
222. **アクティベート** - 作業が開始されたときに `in_progress` に変更される442. **アクティベート**:Claude が作業を開始したときに todo を `in_progress` に設定します
233. **完了** - タスクが正常に完了したときに完了する453. **完了**:Claude がタスクが正常に完了したときにそれを完了としてマークします
244. **削除** - グループ内のすべてのタスクが完了したときに削除される464. **削除**:Claude が `TaskUpdate` 呼び出しで `status: "deleted"` を設定することで、不要になった todo を削除します
25 47
26<h3 id="when-todos-are-used">48<h2 id="when-claude-creates-todos">
27 Todo が使用される場合49 Claude が todo を作成するとき
28</h3>50</h2>
29 51
30SDK は以下の場合に自動的に todo を作成します:52[タスク追跡ツールを持つセッション](#model-availability)では、Claude は以下のような複数ステップの作業のほとんどに対して todo を作成します:
31 53
32* **複雑なマルチステップタスク** - 3 つ以上の異なるアクションが必要な場合54* **複雑な複数ステップのタスク** - 3 つ以上の異なるアクションが必要な場合
33* **ユーザー提供のタスクリスト** - 複数のアイテムが言及されている場合55* **ユーザー提供のタスクリスト** - 複数のアイテムが言及されている場合
34* **非自明な操作** - 進捗追跡の恩恵を受ける場合56* **より長い操作** - 進捗追跡の恩恵を受ける場合
35* **明示的なリクエスト** - ユーザーが todo 整理を要求した場合57* **明示的なリクエスト** - ユーザーが todo 整理を要求する場合
58
59Claude は非常に短いまたは単一ステップのリクエストに対しては todo をスキップする場合があります。
36 60
37<h2 id="examples">61<h2 id="examples">
38 例62 例
39</h2>63</h2>
40 64
41これらの例を実行する前に、[クイックスタート](/docs/ja/agent-sdk/quickstart)に従って Claude Agent SDK をインストールしてください。65これらの例を実行する前に、[クイックスタート](/docs/ja/agent-sdk/quickstart)に従って Claude Agent SDK をインストールしてください。このページのすべての例は同じ権限設定と終了動作を共有します:
42
43各例はエージェントが完了して最終結果メッセージを生成するまで実行されます。セッションがターン制限に最初に達した場合、その結果メッセージは `error_max_turns` サブタイプを持ちます。終了を検出するために `subtype` を確認してください。
44 66
45これらの例は単一ショットの `query()` 呼び出しを使用します。`error_max_turns` 結果を生成した後、`query()` は `Reached maximum number of turns` を含むエラーを発生させます。各例はそれが発生したときにクリーンに終了するために、ループを try ブロックでラップします。67* **権限モード**:例のプロンプトは Claude にプロジェクトで実際の作業を行うよう要求するため、各例は `permissionMode: "acceptEdits"`(TypeScript)または `permission_mode="acceptEdits"`(Python)を設定して、作業が生成するファイル編集を自動承認します。[権限モード](/docs/ja/agent-sdk/permissions#permission-modes)で代替案を参照してください。
68* **ターン制限**:各例はエージェントが完了して最終結果メッセージを生成するまで実行されます。セッションがターン制限に最初に達した場合、その結果メッセージは `error_max_turns` サブタイプを持ちます。終了を検出するために `subtype` を確認してください。
69* **エラー処理**:これらの例は単一ショットの `query()` 呼び出しを使用します。`error_max_turns` 結果を生成した後、`query()` は `Reached maximum number of turns` を含むエラーを発生させます。各例はそれが発生したときにクリーンに終了するために、ループを try ブロックでラップします。結果サブタイプについては、[結果を処理する](/docs/ja/agent-sdk/agent-loop#handle-the-result)を参照してください。
46 70
47結果サブタイプについては、[結果を処理する](/docs/ja/agent-sdk/agent-loop#handle-the-result)を参照してください。71<Note>
72 タスクシステムメッセージ、[`SDKTaskNotificationMessage`](/docs/ja/agent-sdk/typescript#sdktasknotificationmessage)(TypeScript)または [`TaskNotificationMessage`](/docs/ja/agent-sdk/python#tasknotificationmessage)(Python)を含むものは、バックグラウンドコマンドやサブエージェントなどのバックグラウンドタスクを報告します。メッセージストリーム内では、todo アクティビティがアシスタントメッセージ内の `tool_use` ブロックとして表示されます。
73</Note>
48 74
49<h3 id="monitoring-todo-changes">75<h3 id="monitor-todo-changes">
50 Todo 変更の監視76 Todo 変更を監視する
51</h3>77</h3>
52 78
79次の例はアシスタントストリームで `TaskCreate` と `TaskUpdate` の `tool_use` ブロックを監視し、各新しいタスクの件名を含む `+` 行と各ステータス変更のタスク ID と新しいステータスを含む更新行を出力します。タスク活動のログが必要で、レンダリングされた表示ではない場合、この形状を使用してください。`+` 行は割り当てられた ID を含まないため、このログは更新を作成に一致させることはできません。その相関を保つには、[リアルタイムで進捗を表示する](#display-progress-in-real-time)のように ID をキャプチャしてください。
80
81ストリーミングされた `tool_use` 入力は、モデルが発行した生の形状です。Claude Code は実行前にいくつかの近いが正確でないキー名を修復し、`id` または `task_id` を `taskId` にマッピングし、`active_form` を `activeForm` にマッピングしますが、その修復はストリームに反映されません。以下の両方の例のように、常に正規名が存在すると仮定するのではなく、`TaskUpdate` 入力フィールドを防御的に読み取ります。
82
53<CodeGroup>83<CodeGroup>
54 ```typescript TypeScript theme={null}84 ```typescript TypeScript theme={null}
55 import { query } from "@anthropic-ai/claude-agent-sdk";85 import { query } from "@anthropic-ai/claude-agent-sdk";
56 86
57 try {87 try {
58 for await (const message of query({88 for await (const message of query({
59 prompt: "Optimize my React app performance and track progress with todos",89 prompt: "Create a static website with a home page, an about page, and a shared stylesheet, and track progress with todos",
60 // Re-enable TodoWrite, which this example monitors. Without it, the SDK uses90 // Keeps the Task tools on models where Claude Code otherwise doesn't provide them.
61 // Task tools instead and these tool_use blocks never appear.91 options: { maxTurns: 15, permissionMode: "acceptEdits", env: { ...process.env, CLAUDE_CODE_ENABLE_TODO_TOOLS: "1" } },
62 options: { maxTurns: 15, env: { ...process.env, CLAUDE_CODE_ENABLE_TASKS: "0" } }
63 })) {92 })) {
64 // Todo updates are reflected in the message stream93 if (message.type !== "assistant") continue;
65 if (message.type === "assistant") {
66 for (const block of message.message.content) {94 for (const block of message.message.content) {
67 if (block.type === "tool_use" && block.name === "TodoWrite") {95 if (block.type !== "tool_use") continue;
68 const todos = block.input.todos;96 if (block.name === "TaskCreate") {
69 97 const input = block.input as { subject: string };
70 console.log("Todo Status Update:");98 console.log(`+ ${input.subject}`);
71 todos.forEach((todo, index) => {99 } else if (block.name === "TaskUpdate") {
72 const status =100 const input = block.input as {
73 todo.status === "completed" ? "✅" : todo.status === "in_progress" ? "🔧" : "❌";101 taskId?: string;
74 console.log(`${index + 1}. ${status} ${todo.content}`);102 id?: string;
75 });103 task_id?: string;
76 }104 status?: string;
105 };
106 const taskId = input.taskId ?? input.id ?? input.task_id;
107 if (taskId && input.status) console.log(` ${taskId} -> ${input.status}`);
77 }108 }
78 }109 }
79 }110 }
80 } catch (error) {111 } catch (error) {
81 // A single-shot query() throws after yielding an error result,112 // A single-shot query() throws after yielding an error result.
82 // such as when the maxTurns limit is hit.
83 console.log(`Session ended with an error: ${error}`);113 console.log(`Session ended with an error: ${error}`);
84 }114 }
85 ```115 ```
93 async def main():122 async def main():
94 try:123 try:
95 async for message in query(124 async for message in query(
96 prompt="Optimize my React app performance and track progress with todos",125 prompt="Create a static website with a home page, an about page, and a shared stylesheet, and track progress with todos",
97 # Re-enable TodoWrite, which this example monitors. Without it, the SDK uses126 # Keeps the Task tools on models where Claude Code otherwise doesn't provide them.
98 # Task tools instead and these tool_use blocks never appear.127 options=ClaudeAgentOptions(max_turns=15, permission_mode="acceptEdits", env={"CLAUDE_CODE_ENABLE_TODO_TOOLS": "1"}),
99 options=ClaudeAgentOptions(max_turns=15, env={"CLAUDE_CODE_ENABLE_TASKS": "0"}),
100 ):128 ):
101 # Todo updates are reflected in the message stream129 if not isinstance(message, AssistantMessage):
102 if isinstance(message, AssistantMessage):130 continue
103 for block in message.content:131 for block in message.content:
104 if isinstance(block, ToolUseBlock) and block.name == "TodoWrite":132 if not isinstance(block, ToolUseBlock):
105 todos = block.input["todos"]133 continue
106 134 if block.name == "TaskCreate":
107 print("Todo Status Update:")135 print(f"+ {block.input.get('subject', '')}")
108 for i, todo in enumerate(todos):136 elif block.name == "TaskUpdate" and block.input.get("status"):
109 status = (137 task_id = (
110 "✅"138 block.input.get("taskId")
111 if todo["status"] == "completed"139 or block.input.get("id")
112 else "🔧"140 or block.input.get("task_id")
113 if todo["status"] == "in_progress"
114 else "❌"
115 )141 )
116 print(f"{i + 1}. {status} {todo['content']}")142 if task_id:
143 print(f" {task_id} -> {block.input['status']}")
117 except Exception as error:144 except Exception as error:
118 # A single-shot query() raises after yielding an error result,145 # A single-shot query() raises after yielding an error result.
119 # such as when the max_turns limit is hit.
120 print(f"Session ended with an error: {error}")146 print(f"Session ended with an error: {error}")
121 147
122 148
124 ```150 ```
125</CodeGroup>151</CodeGroup>
126 152
127<h3 id="real-time-progress-display">153<h3 id="display-progress-in-real-time">
128 リアルタイム進捗表示154 リアルタイムで進捗を表示する
129</h3>155</h3>
130 156
157次の例はアシスタントストリームで `TaskCreate` と `TaskUpdate` の `tool_use` ブロックを監視し、`TaskTracker` クラスでタスク ID でキー付けされたタスクのマップを保持し、変更のたびに進捗サマリーを再レンダリングします。サマリーは完了したタスクと進行中のタスクをカウントし、各アクティブなアイテムの `activeForm` ラベルを `subject` の代わりに表示します。アプリケーションが各イベントをログするのではなく進捗表示を保持する場合、この形状を使用してください。
158
159割り当てられたタスク ID は `TaskCreate` 入力にはありません。Claude Code は各ツールの構造化された出力を、`tool_result` ブロックを含むユーザーメッセージで、`tool_use_result` フィールドで配信します。`TaskCreate` の場合、そのオブジェクトは TypeScript の [ツール出力タイプ](/docs/ja/agent-sdk/typescript#tool-output-types)の下で `TaskCreateOutput` として文書化され、Python ではフィールドは同じ形状の平文辞書です。トラッカーは `tool_use_id` で各 `tool_result` ブロックをその `tool_use` 呼び出しとペアリングし、ペアリングされたメッセージの `tool_use_result` から `task.id` を読み取ります。Claude は `TaskList` でリストを読み戻すことができ、`TaskGet` で 1 つのタスクの完全な詳細を読み取ることができます。
160
131<CodeGroup>161<CodeGroup>
132 ```typescript TypeScript theme={null}162 ```typescript TypeScript theme={null}
133 import { query } from "@anthropic-ai/claude-agent-sdk";163 import { query } from "@anthropic-ai/claude-agent-sdk";
134 164
135 class TodoTracker {165 type Task = { subject: string; activeForm?: string; status: string };
136 private todos: any[] = [];166
167 class TaskTracker {
168 private tasks = new Map<string, Task>();
169 private pendingCreates = new Map<string, { subject: string; activeForm?: string }>();
137 170
138 displayProgress() {171 displayProgress() {
139 if (this.todos.length === 0) return;172 if (this.tasks.size === 0) {
173 console.log("\nProgress: no open tasks\n");
174 return;
175 }
140 176
141 const completed = this.todos.filter((t) => t.status === "completed").length;177 const items = [...this.tasks.values()];
142 const inProgress = this.todos.filter((t) => t.status === "in_progress").length;178 const completed = items.filter((t) => t.status === "completed").length;
143 const total = this.todos.length;179 const inProgress = items.filter((t) => t.status === "in_progress").length;
144 180
145 console.log(`\nProgress: ${completed}/${total} completed`);181 console.log(`\nProgress: ${completed}/${this.tasks.size} completed`);
146 console.log(`Currently working on: ${inProgress} task(s)\n`);182 console.log(`Currently working on: ${inProgress} task(s)\n`);
147 183
148 this.todos.forEach((todo, index) => {184 for (const [id, task] of this.tasks) {
149 const icon =185 const icon =
150 todo.status === "completed" ? "✅" : todo.status === "in_progress" ? "🔧" : "❌";186 task.status === "completed" ? "✅" : task.status === "in_progress" ? "🔧" : "❌";
151 const text = todo.status === "in_progress" ? todo.activeForm : todo.content;187 const text = task.status === "in_progress" && task.activeForm ? task.activeForm : task.subject;
152 console.log(`${index + 1}. ${icon} ${text}`);188 console.log(`${id}. ${icon} ${text}`);
189 }
190 }
191
192 handleToolUse(block: { id: string; name: string; input: unknown }) {
193 if (block.name === "TaskCreate") {
194 const input = block.input as { subject: string; activeForm?: string; active_form?: string };
195 this.pendingCreates.set(block.id, {
196 subject: input.subject,
197 activeForm: input.activeForm ?? input.active_form,
153 });198 });
199 } else if (block.name === "TaskUpdate") {
200 const input = block.input as {
201 taskId?: string;
202 id?: string;
203 task_id?: string;
204 status?: string;
205 activeForm?: string;
206 active_form?: string;
207 };
208 const taskId = input.taskId ?? input.id ?? input.task_id;
209 if (!taskId) return;
210 if (input.status === "deleted") {
211 this.tasks.delete(taskId);
212 this.displayProgress();
213 return;
214 }
215 const task = this.tasks.get(taskId);
216 if (!task) return;
217 if (input.status) task.status = input.status;
218 const active = input.activeForm ?? input.active_form;
219 if (active) task.activeForm = active;
220 this.displayProgress();
221 }
222 }
223
224 handleToolResult(block: { tool_use_id: string; is_error?: boolean }, result: unknown) {
225 const create = this.pendingCreates.get(block.tool_use_id);
226 if (!create) return;
227 this.pendingCreates.delete(block.tool_use_id);
228 if (block.is_error) return;
229 // The result's user message carries the tool's structured output as
230 // tool_use_result; for TaskCreate that's TaskCreateOutput,
231 // { task: { id, subject } }.
232 const out = result as { task?: { id: string } };
233 if (!out?.task?.id) return;
234 this.tasks.set(out.task.id, { ...create, status: "pending" });
235 this.displayProgress();
154 }236 }
155 237
156 async trackQuery(prompt: string) {238 async trackQuery(prompt: string) {
157 try {239 try {
158 for await (const message of query({240 for await (const message of query({
159 prompt,241 prompt,
160 // Re-enable TodoWrite, which this tracker watches for.242 options: { maxTurns: 20, permissionMode: "acceptEdits", env: { ...process.env, CLAUDE_CODE_ENABLE_TODO_TOOLS: "1" } },
161 options: { maxTurns: 20, env: { ...process.env, CLAUDE_CODE_ENABLE_TASKS: "0" } }
162 })) {243 })) {
163 if (message.type === "assistant") {244 if (message.type === "assistant") {
164 for (const block of message.message.content) {245 for (const block of message.message.content) {
165 if (block.type === "tool_use" && block.name === "TodoWrite") {246 if (block.type === "tool_use") this.handleToolUse(block);
166 this.todos = block.input.todos;
167 this.displayProgress();
168 }247 }
169 }248 }
249 if (message.type === "user" && Array.isArray(message.message.content)) {
250 for (const block of message.message.content) {
251 if (block.type === "tool_result") this.handleToolResult(block, message.tool_use_result);
252 }
170 }253 }
171 }254 }
172 } catch (error) {255 } catch (error) {
178 }261 }
179 262
180 // Usage263 // Usage
181 const tracker = new TodoTracker();264 const tracker = new TaskTracker();
182 await tracker.trackQuery("Build a complete authentication system with todos");265 await tracker.trackQuery("Build a complete authentication system with todos");
183 ```266 ```
184 267
185 ```python Python theme={null}268 ```python Python theme={null}
186 import asyncio269 import asyncio
187 270
188 from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock271 from claude_agent_sdk import (
189 from typing import List, Dict272 query,
273 ClaudeAgentOptions,
274 AssistantMessage,
275 UserMessage,
276 ToolUseBlock,
277 ToolResultBlock,
278 )
190 279
191 280
192 class TodoTracker:281 class TaskTracker:
193 def __init__(self):282 def __init__(self):
194 self.todos: List[Dict] = []283 self.tasks: dict[str, dict] = {}
284 self.pending_creates: dict[str, dict] = {}
195 285
196 def display_progress(self):286 def display_progress(self):
197 if not self.todos:287 if not self.tasks:
288 print("\nProgress: no open tasks\n")
198 return289 return
199 290
200 completed = len([t for t in self.todos if t["status"] == "completed"])291 completed = len([t for t in self.tasks.values() if t["status"] == "completed"])
201 in_progress = len([t for t in self.todos if t["status"] == "in_progress"])292 in_progress = len([t for t in self.tasks.values() if t["status"] == "in_progress"])
202 total = len(self.todos)
203 293
204 print(f"\nProgress: {completed}/{total} completed")294 print(f"\nProgress: {completed}/{len(self.tasks)} completed")
205 print(f"Currently working on: {in_progress} task(s)\n")295 print(f"Currently working on: {in_progress} task(s)\n")
206 296
207 for i, todo in enumerate(self.todos):297 for task_id, task in self.tasks.items():
208 icon = (298 icon = (
209 "✅"299 "✅"
210 if todo["status"] == "completed"300 if task["status"] == "completed"
211 else "🔧"301 else "🔧"
212 if todo["status"] == "in_progress"302 if task["status"] == "in_progress"
213 else "❌"303 else "❌"
214 )304 )
215 text = (305 text = (
216 todo["activeForm"]306 task["activeForm"]
217 if todo["status"] == "in_progress"307 if task["status"] == "in_progress" and task.get("activeForm")
218 else todo["content"]308 else task["subject"]
219 )309 )
220 print(f"{i + 1}. {icon} {text}")310 print(f"{task_id}. {icon} {text}")
311
312 def handle_tool_use(self, block: ToolUseBlock):
313 if block.name == "TaskCreate":
314 self.pending_creates[block.id] = {
315 "subject": block.input.get("subject", ""),
316 "activeForm": block.input.get("activeForm") or block.input.get("active_form"),
317 }
318 elif block.name == "TaskUpdate":
319 task_id = (
320 block.input.get("taskId")
321 or block.input.get("id")
322 or block.input.get("task_id")
323 )
324 if not task_id:
325 return
326 if block.input.get("status") == "deleted":
327 self.tasks.pop(task_id, None)
328 self.display_progress()
329 return
330 task = self.tasks.get(task_id)
331 if not task:
332 return
333 if block.input.get("status"):
334 task["status"] = block.input["status"]
335 active = block.input.get("activeForm") or block.input.get("active_form")
336 if active:
337 task["activeForm"] = active
338 self.display_progress()
339
340 def handle_tool_result(self, block: ToolResultBlock, tool_use_result):
341 create = self.pending_creates.pop(block.tool_use_id, None)
342 if create is None or block.is_error:
343 return
344 # The result's user message carries the tool's structured output as
345 # tool_use_result; for TaskCreate that's {"task": {"id": ..., "subject": ...}}.
346 task = (tool_use_result or {}).get("task") or {}
347 if not task.get("id"):
348 return
349 self.tasks[task["id"]] = {**create, "status": "pending"}
350 self.display_progress()
221 351
222 async def track_query(self, prompt: str):352 async def track_query(self, prompt: str):
223 try:353 try:
224 async for message in query(354 async for message in query(
225 prompt=prompt,355 prompt=prompt,
226 # Re-enable TodoWrite, which this tracker watches for.356 options=ClaudeAgentOptions(
227 options=ClaudeAgentOptions(max_turns=20, env={"CLAUDE_CODE_ENABLE_TASKS": "0"}),357 max_turns=20,
358 permission_mode="acceptEdits",
359 env={"CLAUDE_CODE_ENABLE_TODO_TOOLS": "1"},
360 ),
228 ):361 ):
229 if isinstance(message, AssistantMessage):362 if isinstance(message, AssistantMessage):
230 for block in message.content:363 for block in message.content:
231 if isinstance(block, ToolUseBlock) and block.name == "TodoWrite":364 if isinstance(block, ToolUseBlock):
232 self.todos = block.input["todos"]365 self.handle_tool_use(block)
233 self.display_progress()366 if isinstance(message, UserMessage) and isinstance(message.content, list):
367 for block in message.content:
368 if isinstance(block, ToolResultBlock):
369 self.handle_tool_result(block, message.tool_use_result)
234 except Exception as error:370 except Exception as error:
235 # A single-shot query() raises after yielding an error result,371 # A single-shot query() raises after yielding an error result,
236 # such as when the max_turns limit is hit.372 # such as when the max_turns limit is hit.
239 375
240 # Usage376 # Usage
241 async def main():377 async def main():
242 tracker = TodoTracker()378 tracker = TaskTracker()
243 await tracker.track_query("Build a complete authentication system with todos")379 await tracker.track_query("Build a complete authentication system with todos")
244 380
245 381
247 ```383 ```
248</CodeGroup>384</CodeGroup>
249 385
250<h2 id="migrate-to-task-tools">
251 Task ツールへの移行
252</h2>
253
254Task ツールは、単一の `TodoWrite` 呼び出しを、各新規アイテムの `TaskCreate` と各ステータス変更の `TaskUpdate` に分割し、`TaskList` と `TaskGet` はモデルが現在のリストを読み戻すために利用可能です。監視コードは引き続きアシスタントストリーム内の `tool_use` ブロックを検査しますが、すべての呼び出しでリスト全体を置き換える代わりに、タスク ID でキー付けされたマップを保持します。Task ツールは TypeScript Agent SDK 0.3.142 および Claude Code v2.1.142 以降のデフォルトであるため、`options.env` の変更は不要です。
255
256| `TodoWrite` を使用 | Task ツールを使用 |
257| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
258| 1 つのツール呼び出しで完全な `todos` 配列を書き直す | `TaskCreate` は 1 つのアイテムを追加し、`TaskUpdate` は `taskId` で 1 つのアイテムをパッチする |
259| `block.name === "TodoWrite"` に一致 | `block.name === "TaskCreate"` または `"TaskUpdate"` に一致 |
260| アイテム形状:`{ content, status, activeForm }` | `TaskCreate` 入力:`{ subject, description, activeForm?, metadata? }`。`TaskUpdate` 入力:`{ taskId, status?, subject?, description?, activeForm?, addBlocks?, addBlockedBy?, owner?, metadata? }`。`status` は `"pending"`、`"in_progress"`、または `"completed"`;削除するには `status: "deleted"` を設定 |
261| `block.input.todos` を直接レンダリング | 呼び出し全体でアイテムを蓄積するか、ストリームから `TaskList` ツール結果のスナップショットを読み取る |
262
263割り当てられたタスク ID は `TaskCreate` 入力にはありません。マッチング `tool_result` で `{ task: { id, subject } }` として返されるため、マップをキー付けするために結果ブロックからそれをキャプチャします。次の例は、[Todo 変更の監視](#monitoring-todo-changes)ループへの最小限の変更を示しています。ストリーム内の `TaskList` ツール結果を監視するか、`TaskCreate` 結果と `TaskUpdate` 入力をマップに蓄積することで、完全なリストをレンダリングできます。
264
265ストリーミングされた `tool_use` 入力は、モデルが発行した生の形状です。Claude Code は実行前にいくつかの近いが正確でないキー名を修復し、`id` または `task_id` を `taskId` にマッピングし、`active_form` を `activeForm` にマッピングしますが、その修復はストリームに反映されません。以下のサンプルのように、常に正規名が存在すると仮定するのではなく、`TaskUpdate` 入力フィールドを防御的に読み取ります。
266
267<CodeGroup>
268 ```typescript TypeScript theme={null}
269 import { query } from "@anthropic-ai/claude-agent-sdk";
270
271 try {
272 for await (const message of query({
273 prompt: "Optimize my React app performance and track progress with todos",
274 options: { maxTurns: 15 },
275 })) {
276 if (message.type !== "assistant") continue;
277 for (const block of message.message.content) {
278 if (block.type !== "tool_use") continue;
279 if (block.name === "TaskCreate") {
280 const input = block.input as { subject: string };
281 console.log(`+ ${input.subject}`);
282 } else if (block.name === "TaskUpdate") {
283 const input = block.input as {
284 taskId?: string;
285 id?: string;
286 task_id?: string;
287 status?: string;
288 };
289 const taskId = input.taskId ?? input.id ?? input.task_id;
290 if (taskId && input.status) console.log(` ${taskId} -> ${input.status}`);
291 }
292 }
293 }
294 } catch (error) {
295 // A single-shot query() throws after yielding an error result.
296 console.log(`Session ended with an error: ${error}`);
297 }
298 ```
299
300 ```python Python theme={null}
301 import asyncio
302
303 from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
304
305 async def main():
306 try:
307 async for message in query(
308 prompt="Optimize my React app performance and track progress with todos",
309 options=ClaudeAgentOptions(max_turns=15),
310 ):
311 if not isinstance(message, AssistantMessage):
312 continue
313 for block in message.content:
314 if not isinstance(block, ToolUseBlock):
315 continue
316 if block.name == "TaskCreate":
317 print(f"+ {block.input['subject']}")
318 elif block.name == "TaskUpdate" and block.input.get("status"):
319 task_id = (
320 block.input.get("taskId")
321 or block.input.get("id")
322 or block.input.get("task_id")
323 )
324 if task_id:
325 print(f" {task_id} -> {block.input['status']}")
326 except Exception as error:
327 # A single-shot query() raises after yielding an error result.
328 print(f"Session ended with an error: {error}")
329
330
331 asyncio.run(main())
332 ```
333</CodeGroup>
334
335<h2 id="related-documentation">386<h2 id="related-documentation">
336 関連ドキュメント387 関連ドキュメント
337</h2>388</h2>
338 389
339* [TypeScript SDK リファレンス](/docs/ja/agent-sdk/typescript)390* [Agent SDK リファレンス - TypeScript](/docs/ja/agent-sdk/typescript):TypeScript SDK のオプション、タイプ、およびツールスキーマ。Task ツール入力および出力タイプを含みます
340* [Python SDK リファレンス](/docs/ja/agent-sdk/python)391* [Agent SDK リファレンス - Python](/docs/ja/agent-sdk/python):Python SDK のオプション、タイプ、およびツールドキュメント
341* [ストリーミング vs シングルモード](/docs/ja/agent-sdk/streaming-vs-single-mode)392* [ストリーミング入力](/docs/ja/agent-sdk/streaming-vs-single-mode):2 つの入力モード、およびこれらの例が使用する単一ショット呼び出しの代わりにストリーミング入力を使用する場合
342* [カスタムツール](/docs/ja/agent-sdk/custom-tools)393* [Claude にカスタムツールを提供する](/docs/ja/agent-sdk/custom-tools):SDK のインプロセス MCP サーバーで独自のツールを定義します