待辦事項清單
使用 Claude Agent SDK 追蹤和顯示待辦事項,以實現有組織的任務管理
待辦事項追蹤提供了一種結構化的方式來管理任務並向用戶顯示進度。Claude Agent SDK 包含內置的待辦事項功能,可幫助組織複雜的工作流程並讓用戶了解任務進度。
自 TypeScript Agent SDK 0.3.142 和 Claude Code v2.1.142 起,會話使用結構化的 Task 工具 TaskCreate、TaskUpdate、TaskGet 和 TaskList,而不是 TodoWrite。請參閱遷移到 Task 工具以了解監控代碼如何變更。此頁面上的範例設置 CLAUDE_CODE_ENABLE_TASKS=0 以繼續為尚未遷移的會話顯示 TodoWrite。
待辦事項生命週期
待辦事項遵循可預測的生命週期:
- 建立為
pending當任務被識別時 - 啟動為
in_progress當工作開始時 - 完成當任務成功完成時
- 移除當群組中的所有任務都完成時
何時使用待辦事項
SDK 會自動為以下情況建立待辦事項:
- 複雜的多步驟任務需要 3 個或更多不同的操作
- 用戶提供的任務清單當提及多個項目時
- 非平凡的操作受益於進度追蹤
- 明確的請求當用戶要求待辦事項組織時
範例
監控待辦事項變更
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Optimize my React app performance and track progress with todos",
// Re-enable TodoWrite, which this example monitors. Without it, the SDK uses
// Task tools instead and these tool_use blocks never appear.
options: { maxTurns: 15, env: { ...process.env, CLAUDE_CODE_ENABLE_TASKS: "0" } }
})) {
// Todo updates are reflected in the message stream
if (message.type === "assistant") {
for (const block of message.message.content) {
if (block.type === "tool_use" && block.name === "TodoWrite") {
const todos = block.input.todos;
console.log("Todo Status Update:");
todos.forEach((todo, index) => {
const status =
todo.status === "completed" ? "✅" : todo.status === "in_progress" ? "🔧" : "❌";
console.log(`${index + 1}. ${status} ${todo.content}`);
});
}
}
}
}
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
async for message in query(
prompt="Optimize my React app performance and track progress with todos",
# Re-enable TodoWrite, which this example monitors. Without it, the SDK uses
# Task tools instead and these tool_use blocks never appear.
options=ClaudeAgentOptions(max_turns=15, env={"CLAUDE_CODE_ENABLE_TASKS": "0"}),
):
# Todo updates are reflected in the message stream
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock) and block.name == "TodoWrite":
todos = block.input["todos"]
print("Todo Status Update:")
for i, todo in enumerate(todos):
status = (
"✅"
if todo["status"] == "completed"
else "🔧"
if todo["status"] == "in_progress"
else "❌"
)
print(f"{i + 1}. {status} {todo['content']}")
實時進度顯示
import { query } from "@anthropic-ai/claude-agent-sdk";
class TodoTracker {
private todos: any[] = [];
displayProgress() {
if (this.todos.length === 0) return;
const completed = this.todos.filter((t) => t.status === "completed").length;
const inProgress = this.todos.filter((t) => t.status === "in_progress").length;
const total = this.todos.length;
console.log(`\nProgress: ${completed}/${total} completed`);
console.log(`Currently working on: ${inProgress} task(s)\n`);
this.todos.forEach((todo, index) => {
const icon =
todo.status === "completed" ? "✅" : todo.status === "in_progress" ? "🔧" : "❌";
const text = todo.status === "in_progress" ? todo.activeForm : todo.content;
console.log(`${index + 1}. ${icon} ${text}`);
});
}
async trackQuery(prompt: string) {
for await (const message of query({
prompt,
// Re-enable TodoWrite, which this tracker watches for.
options: { maxTurns: 20, env: { ...process.env, CLAUDE_CODE_ENABLE_TASKS: "0" } }
})) {
if (message.type === "assistant") {
for (const block of message.message.content) {
if (block.type === "tool_use" && block.name === "TodoWrite") {
this.todos = block.input.todos;
this.displayProgress();
}
}
}
}
}
}
// Usage
const tracker = new TodoTracker();
await tracker.trackQuery("Build a complete authentication system with todos");
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
from typing import List, Dict
class TodoTracker:
def __init__(self):
self.todos: List[Dict] = []
def display_progress(self):
if not self.todos:
return
completed = len([t for t in self.todos if t["status"] == "completed"])
in_progress = len([t for t in self.todos if t["status"] == "in_progress"])
total = len(self.todos)
print(f"\nProgress: {completed}/{total} completed")
print(f"Currently working on: {in_progress} task(s)\n")
for i, todo in enumerate(self.todos):
icon = (
"✅"
if todo["status"] == "completed"
else "🔧"
if todo["status"] == "in_progress"
else "❌"
)
text = (
todo["activeForm"]
if todo["status"] == "in_progress"
else todo["content"]
)
print(f"{i + 1}. {icon} {text}")
async def track_query(self, prompt: str):
async for message in query(
prompt=prompt,
# Re-enable TodoWrite, which this tracker watches for.
options=ClaudeAgentOptions(max_turns=20, env={"CLAUDE_CODE_ENABLE_TASKS": "0"}),
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock) and block.name == "TodoWrite":
self.todos = block.input["todos"]
self.display_progress()
# Usage
tracker = TodoTracker()
await tracker.track_query("Build a complete authentication system with todos")
遷移到 Task 工具
Task 工具將單個 TodoWrite 呼叫分割為每個新項目的 TaskCreate 和每個狀態變更的 TaskUpdate,並提供 TaskList 和 TaskGet 供模型讀回當前清單。您的監控代碼仍然檢查助手流中的 tool_use 區塊,但維護一個由任務 ID 鍵入的映射,而不是在每次呼叫時替換整個清單。{/* min-version: 2.1.142 */}Task 工具是 TypeScript Agent SDK 0.3.142 和 Claude Code v2.1.142 起的預設值,因此不需要 options.env 變更。
使用 TodoWrite |
使用 Task 工具 |
|---|---|
一個工具呼叫重寫完整的 todos 陣列 |
TaskCreate 新增一個項目,TaskUpdate 按 taskId 修補一個項目 |
匹配 block.name === "TodoWrite" |
匹配 block.name === "TaskCreate" 或 "TaskUpdate" |
項目形狀:{ 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" 以刪除 |
直接呈現 block.input.todos |
跨呼叫累積項目,或從 TaskList 工具結果讀取快照 |
指派的任務 ID 不在 TaskCreate 輸入中。它在匹配的 tool_result 中作為 { task: { id, subject } } 返回,因此從結果區塊捕獲它以鍵入您的映射。以下範例顯示了對監控待辦事項變更迴圈的最小變更。要呈現完整清單,請在流中監視 TaskList 工具結果或將 TaskCreate 結果和 TaskUpdate 輸入累積到映射中。
串流的 tool_use 輸入是模型發出的原始形狀。Claude Code 在執行前修復一些接近但不正確的鍵名,將 id 或 task_id 映射到 taskId 和 active_form 映射到 activeForm,但該修復不會反映在流中。防禦性地讀取 TaskUpdate 輸入欄位,如下面的範例所示,而不是假設規範名稱始終存在。
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Optimize my React app performance",
})) {
if (message.type !== "assistant") continue;
for (const block of message.message.content) {
if (block.type !== "tool_use") continue;
if (block.name === "TaskCreate") {
const input = block.input as { subject: string };
console.log(`+ ${input.subject}`);
} else if (block.name === "TaskUpdate") {
const input = block.input as {
taskId?: string;
id?: string;
task_id?: string;
status?: string;
};
const taskId = input.taskId ?? input.id ?? input.task_id;
if (taskId && input.status) console.log(` ${taskId} -> ${input.status}`);
}
}
}
from claude_agent_sdk import query, AssistantMessage, ToolUseBlock
async for message in query(
prompt="Optimize my React app performance",
):
if not isinstance(message, AssistantMessage):
continue
for block in message.content:
if not isinstance(block, ToolUseBlock):
continue
if block.name == "TaskCreate":
print(f"+ {block.input['subject']}")
elif block.name == "TaskUpdate" and block.input.get("status"):
task_id = (
block.input.get("taskId")
or block.input.get("id")
or block.input.get("task_id")
)
if task_id:
print(f" {task_id} -> {block.input['status']}")