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# 待辦事項清單5# 追蹤待辦事項
6 6
7> 使用 Claude Agent SDK 追蹤和顯示待辦事項,以實現有組織的任務管理7> 在 Agent SDK 工作階段中追蹤待辦事項,並從結構化工具呼叫在應用程式中呈現 Claude 的進度
8 8
9待辦事項追蹤提供了一種結構化的方式來管理任務並向用戶顯示進度。Claude Agent SDK 包含內置的待辦事項功能,可幫助組織複雜的工作流程並讓用戶了解任務進度。9在[模型可用性](#model-availability)下列出的模型上,Claude 無需書面待辦事項清單即可追蹤多步驟工作,而 Claude Code 預設會將[任務追蹤工具](/docs/zh-TW/tools-reference#task-tool-availability)排除在工作階段之外。您不需要本頁面上的任何內容,Claude 就能在這些模型上完成多步驟任務。
10
11在具有任務追蹤工具的工作階段中,Claude 保持書面待辦事項清單,在工作時更新每個項目的狀態。您在訊息流中看到每個變更都是結構化工具呼叫。僅當您的應用程式讀取這些工具呼叫時才選擇加入工作階段,無論是記錄任務活動還是呈現自己的進度顯示。
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 起,會話使用結構化的 Task 工具 `TaskCreate`、`TaskUpdate`、`TaskGet` 和 `TaskList`,而不是 `TodoWrite`。Python SDK 從它啟動的 Claude Code CLI 獲得此變更,而不是從 Python 套件版本:一旦該 CLI(pip 套件內捆綁的副本,或您使用 `cli_path` 指向的副本)為 v2.1.142 或更新版本,此切換就會適用。請參閱[遷移到 Task 工具](#migrate-to-task-tools)以了解監控代碼如何變更。此頁面上的範例設置 `CLAUDE_CODE_ENABLE_TASKS=0` 以繼續為尚未遷移的會話顯示 `TodoWrite`。18 在 TypeScript Agent SDK 0.3.233 及更新版本或 Python Agent SDK 0.2.139 及更新版本上,以下限制適用。
19
20 The following tools are available by default only on Claude 3.x models, Opus 4 through 4.7, Sonnet 4 through 4.6, and Haiku 4.5. On every other model, including model IDs Claude Code doesn't recognize, they aren't available unless you opt in:
21
22 * `TodoWrite`
23 * `TaskCreate`
24 * `TaskGet`
25 * `TaskUpdate`
26 * `TaskList`
27
28 Wherever the tools are available, Claude Code provides the four Task tools, or `TodoWrite` instead when you set `CLAUDE_CODE_ENABLE_TASKS=0`.
29
30 This default set applies in Claude Code v2.1.268 and later, which the TypeScript Agent SDK bundles from v0.3.268.
13</Note>31</Note>
14 32
15<h3 id="todo-lifecycle">33在列出的模型上,除非您選擇加入工作階段,否則您在訊息流中看不到這些工具的 `tool_use` 區塊。Agent SDK 通過它捆綁的 Claude Code 二進位檔案應用這些預設值。如果您將 `pathToClaudeCodeExecutable`(TypeScript)或 `cli_path`(Python)指向您自己的 Claude Code 安裝,您將獲得該安裝提供的任何工具,在其自己的預設值下。要查看執行中工作階段中的確切集合,請[檢查哪些工具可用](/docs/zh-TW/tools-reference#check-which-tools-are-available)。要選擇加入工作階段,請執行以下操作之一:
34
35* 在 [`allowedTools`](/docs/zh-TW/agent-sdk/permissions#allow-and-deny-rules)(TypeScript)或 `allowed_tools`(Python)選項中命名其中一個工具
36* 在 `tools` 選項中列出工具,這會將工作階段的內置工具限制為它命名的工具。將您想要的工具與您使用的其他內置工具一起包含
37* 在 `env` 選項中設定 `CLAUDE_CODE_ENABLE_TODO_TOOLS=1`,如本頁面上的範例所做的那樣。在 TypeScript 中,`env` 替換子程序環境,因此展開 `...process.env` 以保留繼承的變數。在 Python 中,`env` 合併在繼承的環境之上
38
39<h2 id="todo-lifecycle">
16 待辦事項生命週期40 待辦事項生命週期
17</h3>41</h2>
18 42
19待辦事項遵循可預測的生命週期:43Claude 將每個待辦事項移動通過可預測的生命週期:
20 44
211. **建立**為 `pending` 當任務被識別時451. **建立**:Claude 在識別任務時將待辦事項新增為 `pending`
222. **啟動**為 `in_progress` 當工作開始時462. **啟動**:Claude 在開始工作時將待辦事項設定為 `in_progress`
233. **完成**當任務成功完成時473. **完成**:Claude 在任務成功完成時將其標記為已完成
244. **移除**當群組中的所有任務都完成時484. **移除**:Claude 通過在 `TaskUpdate` 呼叫中設定 `status: "deleted"` 來刪除不再需要的待辦事項
25 49
26<h3 id="when-todos-are-used">50<h2 id="when-claude-creates-todos">
27 何時使用待辦事項51 Claude 何時建立待辦事項
28</h3>52</h2>
29 53
30SDK 會為大多數多步驟工作建立待辦事項,例如:54在[具有任務追蹤工具的工作階段](#model-availability)中,Claude 為大多數多步驟工作建立待辦事項,例如:
31 55
32* **複雜的多步驟任務**需要 3 個或更多不同的操作56* **複雜的多步驟任務**需要三個或更多不同的操作
33* **用戶提供的任務清單**當提及多個項目時57* **使用者提供的任務清單**當提及多個項目時
34* **非平凡的操作**受益於進度追蹤58* **較長的操作**受益於進度追蹤
35* **明確的請求**當用戶要求待辦事項組織時59* **明確的請求**當使用者要求待辦事項組織時
36 60
37它可能會跳過非常短或單步驟請求的待辦事項。61Claude 可能會跳過非常短或單步驟請求的待辦事項。
38 62
39<h2 id="examples">63<h2 id="examples">
40 範例64 範例
41</h2>65</h2>
42 66
43在執行這些範例之前,請按照[快速入門](/docs/zh-TW/agent-sdk/quickstart)安裝 Claude Agent SDK。67在執行這些範例之前,請按照[快速入門](/docs/zh-TW/agent-sdk/quickstart)安裝 Claude Agent SDK。本頁面上的每個範例都共享相同的權限設定和退出行為:
44
45每個範例會執行到代理程式完成並產生其最終結果訊息為止。如果工作階段先達到其輪次限制,該結果訊息會有 `error_max_turns` 子類型。檢查 `subtype` 以偵測該結束。
46 68
47這些範例使用單次 `query()` 呼叫。在產生 `error_max_turns` 結果後,`query()` 會拋出包含 `Reached maximum number of turns` 的錯誤。每個範例都將其迴圈包裝在 try 區塊中,以便在發生這種情況時乾淨地退出。69* **權限模式**:範例提示要求 Claude 在專案上執行實際工作,因此每個範例都設定 `permissionMode: "acceptEdits"`(TypeScript)或 `permission_mode="acceptEdits"`(Python)以自動批准工作產生的檔案編輯。請參閱[權限模式](/docs/zh-TW/agent-sdk/permissions#permission-modes)以了解替代方案。
70* **輪次限制**:每個範例執行到代理程式完成並產生其最終結果訊息為止。如果工作階段先達到其輪次限制,該結果訊息會有 `error_max_turns` 子類型。檢查 `subtype` 以偵測該結束。
71* **錯誤處理**:這些範例使用單次 `query()` 呼叫。在產生 `error_max_turns` 結果後,`query()` 會拋出包含 `Reached maximum number of turns` 的錯誤。每個範例都將其迴圈包裝在 try 區塊中,以便在發生這種情況時乾淨地退出。請參閱[處理結果](/docs/zh-TW/agent-sdk/agent-loop#handle-the-result)以了解結果子類型。
48 72
49請參閱[處理結果](/docs/zh-TW/agent-sdk/agent-loop#handle-the-result)以了解結果子類型。73<Note>
74 任務系統訊息,[`SDKTaskNotificationMessage`](/docs/zh-TW/agent-sdk/typescript#sdktasknotificationmessage)(TypeScript)或 [`TaskNotificationMessage`](/docs/zh-TW/agent-sdk/python#tasknotificationmessage)(Python)等,報告背景任務,例如背景命令和子代理程式。在訊息流中,您看到待辦事項活動作為助手訊息中的 `tool_use` 區塊。
75</Note>
50 76
51<h3 id="monitoring-todo-changes">77<h3 id="monitor-todo-changes">
52 監控待辦事項變更78 監控待辦事項變更
53</h3>79</h3>
54 80
81以下範例監視助手流中的 `TaskCreate` 和 `TaskUpdate` `tool_use` 區塊,並列印一個 `+` 行,其中包含每個新任務的主題,以及一個更新行,其中包含每個狀態變更的任務 ID 和新狀態。當您想要任務活動的日誌而不是呈現的顯示時,請使用此形狀。`+` 行不包含指派的 ID,因此此日誌無法將更新與其建立相匹配。要保持該相關性,請如[即時顯示進度](#display-progress-in-real-time)所做的那樣捕獲 ID。
82
83串流的 `tool_use` 輸入是模型發出的原始形狀。Claude Code 在執行前修復一些接近但不正確的鍵名,將 `id` 或 `task_id` 對應到 `taskId` 和 `active_form` 對應到 `activeForm`,但該修復不會反映在流中。防禦性地讀取 `TaskUpdate` 輸入欄位,如本頁面上的兩個範例所做的那樣,而不是假設規範名稱始終存在。
84
55<CodeGroup>85<CodeGroup>
56 ```typescript TypeScript theme={null}86 ```typescript TypeScript theme={null}
57 import { query } from "@anthropic-ai/claude-agent-sdk";87 import { query } from "@anthropic-ai/claude-agent-sdk";
58 88
59 try {89 try {
60 for await (const message of query({90 for await (const message of query({
61 prompt: "Optimize my React app performance and track progress with todos",91 prompt: "Create a static website with a home page, an about page, and a shared stylesheet, and track progress with todos",
62 // Re-enable TodoWrite, which this example monitors. Without it, the SDK uses92 // Keeps the Task tools on models where Claude Code otherwise doesn't provide them.
63 // Task tools instead and these tool_use blocks never appear.93 options: { maxTurns: 15, permissionMode: "acceptEdits", env: { ...process.env, CLAUDE_CODE_ENABLE_TODO_TOOLS: "1" } },
64 options: { maxTurns: 15, env: { ...process.env, CLAUDE_CODE_ENABLE_TASKS: "0" } }
65 })) {94 })) {
66 // Todo updates are reflected in the message stream95 if (message.type !== "assistant") continue;
67 if (message.type === "assistant") {
68 for (const block of message.message.content) {96 for (const block of message.message.content) {
69 if (block.type === "tool_use" && block.name === "TodoWrite") {97 if (block.type !== "tool_use") continue;
70 const todos = block.input.todos;98 if (block.name === "TaskCreate") {
71 99 const input = block.input as { subject: string };
72 console.log("Todo Status Update:");100 console.log(`+ ${input.subject}`);
73 todos.forEach((todo, index) => {101 } else if (block.name === "TaskUpdate") {
74 const status =102 const input = block.input as {
75 todo.status === "completed" ? "✅" : todo.status === "in_progress" ? "🔧" : "❌";103 taskId?: string;
76 console.log(`${index + 1}. ${status} ${todo.content}`);104 id?: string;
77 });105 task_id?: string;
78 }106 status?: string;
107 };
108 const taskId = input.taskId ?? input.id ?? input.task_id;
109 if (taskId && input.status) console.log(` ${taskId} -> ${input.status}`);
79 }110 }
80 }111 }
81 }112 }
82 } catch (error) {113 } catch (error) {
83 // A single-shot query() throws after yielding an error result,114 // A single-shot query() throws after yielding an error result.
84 // such as when the maxTurns limit is hit.
85 console.log(`Session ended with an error: ${error}`);115 console.log(`Session ended with an error: ${error}`);
86 }116 }
87 ```117 ```
95 async def main():124 async def main():
96 try:125 try:
97 async for message in query(126 async for message in query(
98 prompt="Optimize my React app performance and track progress with todos",127 prompt="Create a static website with a home page, an about page, and a shared stylesheet, and track progress with todos",
99 # Re-enable TodoWrite, which this example monitors. Without it, the SDK uses128 # Keeps the Task tools on models where Claude Code otherwise doesn't provide them.
100 # Task tools instead and these tool_use blocks never appear.129 options=ClaudeAgentOptions(max_turns=15, permission_mode="acceptEdits", env={"CLAUDE_CODE_ENABLE_TODO_TOOLS": "1"}),
101 options=ClaudeAgentOptions(max_turns=15, env={"CLAUDE_CODE_ENABLE_TASKS": "0"}),
102 ):130 ):
103 # Todo updates are reflected in the message stream131 if not isinstance(message, AssistantMessage):
104 if isinstance(message, AssistantMessage):132 continue
105 for block in message.content:133 for block in message.content:
106 if isinstance(block, ToolUseBlock) and block.name == "TodoWrite":134 if not isinstance(block, ToolUseBlock):
107 todos = block.input["todos"]135 continue
108 136 if block.name == "TaskCreate":
109 print("Todo Status Update:")137 print(f"+ {block.input.get('subject', '')}")
110 for i, todo in enumerate(todos):138 elif block.name == "TaskUpdate" and block.input.get("status"):
111 status = (139 task_id = (
112 "✅"140 block.input.get("taskId")
113 if todo["status"] == "completed"141 or block.input.get("id")
114 else "🔧"142 or block.input.get("task_id")
115 if todo["status"] == "in_progress"
116 else "❌"
117 )143 )
118 print(f"{i + 1}. {status} {todo['content']}")144 if task_id:
145 print(f" {task_id} -> {block.input['status']}")
119 except Exception as error:146 except Exception as error:
120 # A single-shot query() raises after yielding an error result,147 # A single-shot query() raises after yielding an error result.
121 # such as when the max_turns limit is hit.
122 print(f"Session ended with an error: {error}")148 print(f"Session ended with an error: {error}")
123 149
124 150
126 ```152 ```
127</CodeGroup>153</CodeGroup>
128 154
129<h3 id="real-time-progress-display">155<h3 id="display-progress-in-real-time">
130 實時進度顯示156 即時顯示進度
131</h3>157</h3>
132 158
159以下範例監視助手流中的 `TaskCreate` 和 `TaskUpdate` `tool_use` 區塊,並在 `TaskTracker` 類別中保持由任務 ID 鍵入的任務映射,在每次變更時重新呈現進度摘要。摘要計算已完成和進行中的任務,並顯示每個活動項目的 `activeForm` 標籤以代替其 `subject`。當您的應用程式維護進度顯示而不是記錄每個事件時,請使用此形狀。
160
161指派的任務 ID 不在 `TaskCreate` 輸入中。Claude Code 在攜帶其 `tool_result` 區塊的使用者訊息上傳遞每個工具的結構化輸出,在 `tool_use_result` 欄位中。對於 `TaskCreate`,該物件在 TypeScript 中記錄為[工具輸出類型](/docs/zh-TW/agent-sdk/typescript#tool-output-types)下的 `TaskCreateOutput`,在 Python 中該欄位是相同形狀的純字典。追蹤器通過 `tool_use_id` 將每個 `tool_result` 區塊與其 `tool_use` 呼叫配對,並從配對訊息的 `tool_use_result` 讀取 `task.id`。Claude 可以使用 `TaskList` 讀回清單,使用 `TaskGet` 讀回一個任務的完整詳細資訊。
162
133<CodeGroup>163<CodeGroup>
134 ```typescript TypeScript theme={null}164 ```typescript TypeScript theme={null}
135 import { query } from "@anthropic-ai/claude-agent-sdk";165 import { query } from "@anthropic-ai/claude-agent-sdk";
136 166
137 class TodoTracker {167 type Task = { subject: string; activeForm?: string; status: string };
138 private todos: any[] = [];168
169 class TaskTracker {
170 private tasks = new Map<string, Task>();
171 private pendingCreates = new Map<string, { subject: string; activeForm?: string }>();
139 172
140 displayProgress() {173 displayProgress() {
141 if (this.todos.length === 0) return;174 if (this.tasks.size === 0) {
175 console.log("\nProgress: no open tasks\n");
176 return;
177 }
142 178
143 const completed = this.todos.filter((t) => t.status === "completed").length;179 const items = [...this.tasks.values()];
144 const inProgress = this.todos.filter((t) => t.status === "in_progress").length;180 const completed = items.filter((t) => t.status === "completed").length;
145 const total = this.todos.length;181 const inProgress = items.filter((t) => t.status === "in_progress").length;
146 182
147 console.log(`\nProgress: ${completed}/${total} completed`);183 console.log(`\nProgress: ${completed}/${this.tasks.size} completed`);
148 console.log(`Currently working on: ${inProgress} task(s)\n`);184 console.log(`Currently working on: ${inProgress} task(s)\n`);
149 185
150 this.todos.forEach((todo, index) => {186 for (const [id, task] of this.tasks) {
151 const icon =187 const icon =
152 todo.status === "completed" ? "✅" : todo.status === "in_progress" ? "🔧" : "❌";188 task.status === "completed" ? "✅" : task.status === "in_progress" ? "🔧" : "❌";
153 const text = todo.status === "in_progress" ? todo.activeForm : todo.content;189 const text = task.status === "in_progress" && task.activeForm ? task.activeForm : task.subject;
154 console.log(`${index + 1}. ${icon} ${text}`);190 console.log(`${id}. ${icon} ${text}`);
191 }
192 }
193
194 handleToolUse(block: { id: string; name: string; input: unknown }) {
195 if (block.name === "TaskCreate") {
196 const input = block.input as { subject: string; activeForm?: string; active_form?: string };
197 this.pendingCreates.set(block.id, {
198 subject: input.subject,
199 activeForm: input.activeForm ?? input.active_form,
155 });200 });
201 } else if (block.name === "TaskUpdate") {
202 const input = block.input as {
203 taskId?: string;
204 id?: string;
205 task_id?: string;
206 status?: string;
207 activeForm?: string;
208 active_form?: string;
209 };
210 const taskId = input.taskId ?? input.id ?? input.task_id;
211 if (!taskId) return;
212 if (input.status === "deleted") {
213 this.tasks.delete(taskId);
214 this.displayProgress();
215 return;
216 }
217 const task = this.tasks.get(taskId);
218 if (!task) return;
219 if (input.status) task.status = input.status;
220 const active = input.activeForm ?? input.active_form;
221 if (active) task.activeForm = active;
222 this.displayProgress();
223 }
224 }
225
226 handleToolResult(block: { tool_use_id: string; is_error?: boolean }, result: unknown) {
227 const create = this.pendingCreates.get(block.tool_use_id);
228 if (!create) return;
229 this.pendingCreates.delete(block.tool_use_id);
230 if (block.is_error) return;
231 // The result's user message carries the tool's structured output as
232 // tool_use_result; for TaskCreate that's TaskCreateOutput,
233 // { task: { id, subject } }.
234 const out = result as { task?: { id: string } };
235 if (!out?.task?.id) return;
236 this.tasks.set(out.task.id, { ...create, status: "pending" });
237 this.displayProgress();
156 }238 }
157 239
158 async trackQuery(prompt: string) {240 async trackQuery(prompt: string) {
159 try {241 try {
160 for await (const message of query({242 for await (const message of query({
161 prompt,243 prompt,
162 // Re-enable TodoWrite, which this tracker watches for.244 options: { maxTurns: 20, permissionMode: "acceptEdits", env: { ...process.env, CLAUDE_CODE_ENABLE_TODO_TOOLS: "1" } },
163 options: { maxTurns: 20, env: { ...process.env, CLAUDE_CODE_ENABLE_TASKS: "0" } }
164 })) {245 })) {
165 if (message.type === "assistant") {246 if (message.type === "assistant") {
166 for (const block of message.message.content) {247 for (const block of message.message.content) {
167 if (block.type === "tool_use" && block.name === "TodoWrite") {248 if (block.type === "tool_use") this.handleToolUse(block);
168 this.todos = block.input.todos;
169 this.displayProgress();
170 }249 }
171 }250 }
251 if (message.type === "user" && Array.isArray(message.message.content)) {
252 for (const block of message.message.content) {
253 if (block.type === "tool_result") this.handleToolResult(block, message.tool_use_result);
254 }
172 }255 }
173 }256 }
174 } catch (error) {257 } catch (error) {
180 }263 }
181 264
182 // Usage265 // Usage
183 const tracker = new TodoTracker();266 const tracker = new TaskTracker();
184 await tracker.trackQuery("Build a complete authentication system with todos");267 await tracker.trackQuery("Build a complete authentication system with todos");
185 ```268 ```
186 269
187 ```python Python theme={null}270 ```python Python theme={null}
188 import asyncio271 import asyncio
189 272
190 from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock273 from claude_agent_sdk import (
191 from typing import List, Dict274 query,
275 ClaudeAgentOptions,
276 AssistantMessage,
277 UserMessage,
278 ToolUseBlock,
279 ToolResultBlock,
280 )
192 281
193 282
194 class TodoTracker:283 class TaskTracker:
195 def __init__(self):284 def __init__(self):
196 self.todos: List[Dict] = []285 self.tasks: dict[str, dict] = {}
286 self.pending_creates: dict[str, dict] = {}
197 287
198 def display_progress(self):288 def display_progress(self):
199 if not self.todos:289 if not self.tasks:
290 print("\nProgress: no open tasks\n")
200 return291 return
201 292
202 completed = len([t for t in self.todos if t["status"] == "completed"])293 completed = len([t for t in self.tasks.values() if t["status"] == "completed"])
203 in_progress = len([t for t in self.todos if t["status"] == "in_progress"])294 in_progress = len([t for t in self.tasks.values() if t["status"] == "in_progress"])
204 total = len(self.todos)
205 295
206 print(f"\nProgress: {completed}/{total} completed")296 print(f"\nProgress: {completed}/{len(self.tasks)} completed")
207 print(f"Currently working on: {in_progress} task(s)\n")297 print(f"Currently working on: {in_progress} task(s)\n")
208 298
209 for i, todo in enumerate(self.todos):299 for task_id, task in self.tasks.items():
210 icon = (300 icon = (
211 "✅"301 "✅"
212 if todo["status"] == "completed"302 if task["status"] == "completed"
213 else "🔧"303 else "🔧"
214 if todo["status"] == "in_progress"304 if task["status"] == "in_progress"
215 else "❌"305 else "❌"
216 )306 )
217 text = (307 text = (
218 todo["activeForm"]308 task["activeForm"]
219 if todo["status"] == "in_progress"309 if task["status"] == "in_progress" and task.get("activeForm")
220 else todo["content"]310 else task["subject"]
221 )311 )
222 print(f"{i + 1}. {icon} {text}")312 print(f"{task_id}. {icon} {text}")
313
314 def handle_tool_use(self, block: ToolUseBlock):
315 if block.name == "TaskCreate":
316 self.pending_creates[block.id] = {
317 "subject": block.input.get("subject", ""),
318 "activeForm": block.input.get("activeForm") or block.input.get("active_form"),
319 }
320 elif block.name == "TaskUpdate":
321 task_id = (
322 block.input.get("taskId")
323 or block.input.get("id")
324 or block.input.get("task_id")
325 )
326 if not task_id:
327 return
328 if block.input.get("status") == "deleted":
329 self.tasks.pop(task_id, None)
330 self.display_progress()
331 return
332 task = self.tasks.get(task_id)
333 if not task:
334 return
335 if block.input.get("status"):
336 task["status"] = block.input["status"]
337 active = block.input.get("activeForm") or block.input.get("active_form")
338 if active:
339 task["activeForm"] = active
340 self.display_progress()
341
342 def handle_tool_result(self, block: ToolResultBlock, tool_use_result):
343 create = self.pending_creates.pop(block.tool_use_id, None)
344 if create is None or block.is_error:
345 return
346 # The result's user message carries the tool's structured output as
347 # tool_use_result; for TaskCreate that's {"task": {"id": ..., "subject": ...}}.
348 task = (tool_use_result or {}).get("task") or {}
349 if not task.get("id"):
350 return
351 self.tasks[task["id"]] = {**create, "status": "pending"}
352 self.display_progress()
223 353
224 async def track_query(self, prompt: str):354 async def track_query(self, prompt: str):
225 try:355 try:
226 async for message in query(356 async for message in query(
227 prompt=prompt,357 prompt=prompt,
228 # Re-enable TodoWrite, which this tracker watches for.358 options=ClaudeAgentOptions(
229 options=ClaudeAgentOptions(max_turns=20, env={"CLAUDE_CODE_ENABLE_TASKS": "0"}),359 max_turns=20,
360 permission_mode="acceptEdits",
361 env={"CLAUDE_CODE_ENABLE_TODO_TOOLS": "1"},
362 ),
230 ):363 ):
231 if isinstance(message, AssistantMessage):364 if isinstance(message, AssistantMessage):
232 for block in message.content:365 for block in message.content:
233 if isinstance(block, ToolUseBlock) and block.name == "TodoWrite":366 if isinstance(block, ToolUseBlock):
234 self.todos = block.input["todos"]367 self.handle_tool_use(block)
235 self.display_progress()368 if isinstance(message, UserMessage) and isinstance(message.content, list):
369 for block in message.content:
370 if isinstance(block, ToolResultBlock):
371 self.handle_tool_result(block, message.tool_use_result)
236 except Exception as error:372 except Exception as error:
237 # A single-shot query() raises after yielding an error result,373 # A single-shot query() raises after yielding an error result,
238 # such as when the max_turns limit is hit.374 # such as when the max_turns limit is hit.
241 377
242 # Usage378 # Usage
243 async def main():379 async def main():
244 tracker = TodoTracker()380 tracker = TaskTracker()
245 await tracker.track_query("Build a complete authentication system with todos")381 await tracker.track_query("Build a complete authentication system with todos")
246 382
247 383
249 ```385 ```
250</CodeGroup>386</CodeGroup>
251 387
252<h2 id="migrate-to-task-tools">
253 遷移到 Task 工具
254</h2>
255
256Task 工具將單個 `TodoWrite` 呼叫分割為每個新項目的 `TaskCreate` 和每個狀態變更的 `TaskUpdate`,並提供 `TaskList` 和 `TaskGet` 供模型讀回當前清單。您的監控代碼仍然檢查助手流中的 `tool_use` 區塊,但維護一個由任務 ID 鍵入的映射,而不是在每次呼叫時替換整個清單。Task 工具是 TypeScript Agent SDK 0.3.142 和 Claude Code v2.1.142 起的預設值,因此不需要 `options.env` 變更。
257
258| 使用 `TodoWrite` | 使用 Task 工具 |
259| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
260| 一個工具呼叫重寫完整的 `todos` 陣列 | `TaskCreate` 新增一個項目,`TaskUpdate` 按 `taskId` 修補一個項目 |
261| 匹配 `block.name === "TodoWrite"` | 匹配 `block.name === "TaskCreate"` 或 `"TaskUpdate"` |
262| 項目形狀:`{ 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"` 以刪除 |
263| 直接呈現 `block.input.todos` | 跨呼叫累積項目,或從 `TaskList` 工具結果讀取快照 |
264
265指派的任務 ID 不在 `TaskCreate` 輸入中。它在匹配的 `tool_result` 中作為 `{ task: { id, subject } }` 返回,因此從結果區塊捕獲它以鍵入您的映射。以下範例顯示了對[監控待辦事項變更](#monitoring-todo-changes)迴圈的最小變更。它僅讀取 `tool_use` 輸入並跳過從 `tool_result` 區塊捕獲 ID。要呈現完整清單,請在流中監視 `TaskList` 工具結果或將 `TaskCreate` 結果和 `TaskUpdate` 輸入累積到映射中。
266
267串流的 `tool_use` 輸入是模型發出的原始形狀。Claude Code 在執行前修復一些接近但不正確的鍵名,將 `id` 或 `task_id` 映射到 `taskId` 和 `active_form` 映射到 `activeForm`,但該修復不會反映在流中。防禦性地讀取 `TaskUpdate` 輸入欄位,如下面的範例所示,而不是假設規範名稱始終存在。
268
269<CodeGroup>
270 ```typescript TypeScript theme={null}
271 import { query } from "@anthropic-ai/claude-agent-sdk";
272
273 try {
274 for await (const message of query({
275 prompt: "Optimize my React app performance and track progress with todos",
276 options: { maxTurns: 15 },
277 })) {
278 if (message.type !== "assistant") continue;
279 for (const block of message.message.content) {
280 if (block.type !== "tool_use") continue;
281 if (block.name === "TaskCreate") {
282 const input = block.input as { subject: string };
283 console.log(`+ ${input.subject}`);
284 } else if (block.name === "TaskUpdate") {
285 const input = block.input as {
286 taskId?: string;
287 id?: string;
288 task_id?: string;
289 status?: string;
290 };
291 const taskId = input.taskId ?? input.id ?? input.task_id;
292 if (taskId && input.status) console.log(` ${taskId} -> ${input.status}`);
293 }
294 }
295 }
296 } catch (error) {
297 // A single-shot query() throws after yielding an error result.
298 console.log(`Session ended with an error: ${error}`);
299 }
300 ```
301
302 ```python Python theme={null}
303 import asyncio
304
305 from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
306
307 async def main():
308 try:
309 async for message in query(
310 prompt="Optimize my React app performance and track progress with todos",
311 options=ClaudeAgentOptions(max_turns=15),
312 ):
313 if not isinstance(message, AssistantMessage):
314 continue
315 for block in message.content:
316 if not isinstance(block, ToolUseBlock):
317 continue
318 if block.name == "TaskCreate":
319 print(f"+ {block.input['subject']}")
320 elif block.name == "TaskUpdate" and block.input.get("status"):
321 task_id = (
322 block.input.get("taskId")
323 or block.input.get("id")
324 or block.input.get("task_id")
325 )
326 if task_id:
327 print(f" {task_id} -> {block.input['status']}")
328 except Exception as error:
329 # A single-shot query() raises after yielding an error result.
330 print(f"Session ended with an error: {error}")
331
332
333 asyncio.run(main())
334 ```
335</CodeGroup>
336
337<h2 id="related-documentation">388<h2 id="related-documentation">
338 相關文檔389 相關文件
339</h2>390</h2>
340 391
341* [TypeScript SDK 參考](/docs/zh-TW/agent-sdk/typescript)392* [Agent SDK 參考 - TypeScript](/docs/zh-TW/agent-sdk/typescript):TypeScript SDK 的選項、類型和工具架構,包括 Task 工具輸入和輸出類型
342* [Python SDK 參考](/docs/zh-TW/agent-sdk/python)393* [Agent SDK 參考 - Python](/docs/zh-TW/agent-sdk/python):Python SDK 的選項、類型和工具文件
343* [串流與單一模式](/docs/zh-TW/agent-sdk/streaming-vs-single-mode)394* [串流輸入](/docs/zh-TW/agent-sdk/streaming-vs-single-mode):兩種輸入模式,以及何時使用串流輸入而不是這些範例使用的單次呼叫
344* [自訂工具](/docs/zh-TW/agent-sdk/custom-tools)395* [為 Claude 提供自訂工具](/docs/zh-TW/agent-sdk/custom-tools):使用 SDK 的進程內 MCP 伺服器定義您自己的工具