Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions s05_todo_write/README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,21 @@ The dispatch mechanism is unchanged; the new tool is still routed through `TOOL_

## How It Works

**The todo_write tool**, accepts a list with statuses, persists to `.tasks/current_todos.json` (teaching version writes to disk for observability), and displays progress in the terminal:
**The todo_write tool** accepts a list with statuses, keeps it in the current process memory, and displays progress in the terminal:

```python
CURRENT_TODOS: list[dict] = []

def run_todo_write(todos: list) -> str:
tasks_file = TASKS_DIR / "current_todos.json"
tasks_file.write_text(json.dumps(todos, indent=2, ensure_ascii=False))
global CURRENT_TODOS
CURRENT_TODOS = todos

lines = ["\n## Current Tasks"]
for t in todos:
for t in CURRENT_TODOS:
icon = {"pending": " ", "in_progress": "▸", "completed": "✓"}[t["status"]]
lines.append(f" [{icon}] {t['content']}")
print("\n".join(lines))
return f"Updated {len(todos)} tasks"
return f"Updated {len(CURRENT_TODOS)} tasks"
```

The tool definition joins the other 5 in the dispatch map:
Expand Down Expand Up @@ -135,7 +137,7 @@ The Agent can plan now. But if a task is too large, say "refactor the entire aut

CC has two task systems coexisting (`tasks.ts:133-139`):

- **TodoWrite (V1)**: A simple list tool, data maintained in memory AppState (`TodoWriteTool.ts:65-103`). The teaching version writes to `.tasks/current_todos.json` for observability; the real V1 does not write to disk.
- **TodoWrite (V1)**: A simple list tool, data maintained in memory AppState (`TodoWriteTool.ts:65-103`). The teaching version also keeps it in process memory and clears it on exit.
- **Task System (V2 = s12)**: File-persisted, dependency graph, concurrency locks, ownership.

The switch is controlled by `isTodoV2Enabled()`. In the current source: V2 is enabled by default in interactive sessions, V1 in non-interactive (SDK) sessions; setting `CLAUDE_CODE_ENABLE_TASKS` forces V2 regardless. Note the source comment "Force-enable tasks in non-interactive mode" describes the env var path's purpose, not the default branch's return semantics.
Expand Down
14 changes: 8 additions & 6 deletions s05_todo_write/README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,21 @@ Agent は作業を開始する。3 つのファイルをリネーム、テスト

## 仕組み

**todo_write ツール**、ステータス付きのリストを受け取り、`.tasks/current_todos.json` に永続化(教育版は観察用にディスクに書き込む)、端末に進捗を表示する:
**todo_write ツール**、ステータス付きのリストを受け取り、現在のプロセスメモリに保持し、端末に進捗を表示する:

```python
CURRENT_TODOS: list[dict] = []

def run_todo_write(todos: list) -> str:
tasks_file = TASKS_DIR / "current_todos.json"
tasks_file.write_text(json.dumps(todos, indent=2, ensure_ascii=False))
global CURRENT_TODOS
CURRENT_TODOS = todos

lines = ["\n## Current Tasks"]
for t in todos:
for t in CURRENT_TODOS:
icon = {"pending": " ", "in_progress": "▸", "completed": "✓"}[t["status"]]
lines.append(f" [{icon}] {t['content']}")
print("\n".join(lines))
return f"Updated {len(todos)} tasks"
return f"Updated {len(CURRENT_TODOS)} tasks"
```

ツール定義は他の 5 つと一緒にディスパッチマップに追加される:
Expand Down Expand Up @@ -135,7 +137,7 @@ Agent は計画できるようになった。しかしタスクが大きすぎ

CC には二つのタスクシステムが共存している(`tasks.ts:133-139`):

- **TodoWrite(V1)**:シンプルなリストツール、データはメモリ AppState で管理(`TodoWriteTool.ts:65-103`)。教育版は観察用に `.tasks/current_todos.json` に書き込むが、実際の V1 はディスクに書き込まない
- **TodoWrite(V1)**:シンプルなリストツール、データはメモリ AppState で管理(`TodoWriteTool.ts:65-103`)。教育版もプロセスメモリに保持し、終了時に消える
- **Task System(V2 = s12)**:ファイル永続化、依存グラフ、並行ロック、ownership

切り替えは `isTodoV2Enabled()` で制御される。現在のソースコードの実装:対話型セッションでは V2 がデフォルトで有効、非対話型セッション(SDK)では V1 がデフォルトで有効。`CLAUDE_CODE_ENABLE_TASKS` 環境変数を設定するとセッション種別に関わらず V2 が強制有効になる。ソースコメント「Force-enable tasks in non-interactive mode」は環境変数パスの用途を説明しており、デフォルト分岐の戻り値のセマンティクスとは異なるため注意。
Expand Down
16 changes: 9 additions & 7 deletions s05_todo_write/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,21 @@ dispatch 机制不变,新工具仍然走 `TOOL_HANDLERS[block.name]` 分发。

## 工作原理

**todo_write 工具**,接收一个带状态的列表,持久化到 `.tasks/current_todos.json`(教学版写盘以便观察),同时在终端显示进度:
**todo_write 工具**,接收一个带状态的列表,保存在当前进程内存中,同时在终端显示进度:

```python
CURRENT_TODOS: list[dict] = []

def run_todo_write(todos: list) -> str:
tasks_file = TASKS_DIR / "current_todos.json"
tasks_file.write_text(json.dumps(todos, indent=2, ensure_ascii=False))
global CURRENT_TODOS
CURRENT_TODOS = todos

lines = ["\n## Current Tasks"]
for t in todos:
for t in CURRENT_TODOS:
icon = {"pending": " ", "in_progress": "▸", "completed": "✓"}[t["status"]]
lines.append(f" [{icon}] {t['content']}")
print("\n".join(lines))
return f"Updated {len(todos)} tasks"
return f"Updated {len(CURRENT_TODOS)} tasks"
```

工具定义和其他 5 个工具一起加入 dispatch map:
Expand Down Expand Up @@ -135,7 +137,7 @@ s06 Subagent → 把大任务拆成子任务,每个子任务派一个独立的

CC 中有两套任务系统并存(`tasks.ts:133-139`):

- **TodoWrite(V1)**:一个简单的列表工具,数据在内存 AppState 中维护(`TodoWriteTool.ts:65-103`)。教学版写盘到 `.tasks/current_todos.json` 是为了可观察性,真实 V1 不写盘
- **TodoWrite(V1)**:一个简单的列表工具,数据在内存 AppState 中维护(`TodoWriteTool.ts:65-103`)。教学版也保存在进程内存里,退出后清空
- **Task System(V2 = s12)**:文件持久化、依赖图、并发锁、ownership

切换由 `isTodoV2Enabled()` 控制。当前源码的实现逻辑:交互式会话中 V2 默认启用,非交互式会话(SDK)中 V1 默认启用;设置 `CLAUDE_CODE_ENABLE_TASKS` 环境变量可强制启用 V2。注意源码注释 "Force-enable tasks in non-interactive mode" 描述的是 env var 路径的用途,和默认分支的返回值语义不同,阅读时需区分。
Expand All @@ -153,4 +155,4 @@ Task System 相比 TodoWrite 的核心增量:

</details>

<!-- translation-sync: zh@v1, en@v0, ja@v0 -->
<!-- translation-sync: zh@v1, en@v1, ja@v1 -->
14 changes: 7 additions & 7 deletions s05_todo_write/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
todo_write ← NEW
+------------------+
|
.tasks/current_todos.json
in-memory current_todos
|
if rounds_since_todo >= 3:
inject <reminder>
Expand All @@ -28,7 +28,7 @@
Needs: pip install anthropic python-dotenv + ANTHROPIC_API_KEY in .env
"""

import os, subprocess, json
import os, subprocess
from pathlib import Path

try:
Expand All @@ -45,9 +45,9 @@
os.environ.pop("ANTHROPIC_AUTH_TOKEN", None)

WORKDIR = Path.cwd()
TASKS_DIR = WORKDIR / ".tasks"; TASKS_DIR.mkdir(exist_ok=True)
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
MODEL = os.environ["MODEL_ID"]
CURRENT_TODOS: list[dict] = []

# s05 change: SYSTEM prompt adds planning guidance
SYSTEM = (
Expand Down Expand Up @@ -122,20 +122,20 @@ def run_glob(pattern: str) -> str:
# ═══════════════════════════════════════════════════════════

def run_todo_write(todos: list) -> str:
global CURRENT_TODOS
# validate required fields
for i, t in enumerate(todos):
if "content" not in t or "status" not in t:
return f"Error: todos[{i}] missing 'content' or 'status'"
if t["status"] not in ("pending", "in_progress", "completed"):
return f"Error: todos[{i}] has invalid status '{t['status']}'"
tasks_file = TASKS_DIR / "current_todos.json"
tasks_file.write_text(json.dumps(todos, indent=2, ensure_ascii=False))
CURRENT_TODOS = todos
lines = ["\n\033[33m## Current Tasks\033[0m"]
for t in todos:
for t in CURRENT_TODOS:
icon = {"pending": " ", "in_progress": "\033[36m▸\033[0m", "completed": "\033[32m✓\033[0m"}[t["status"]]
lines.append(f" [{icon}] {t['content']}")
print("\n".join(lines))
return f"Updated {len(todos)} tasks"
return f"Updated {len(CURRENT_TODOS)} tasks"

TOOLS = [
{"name": "bash", "description": "Run a shell command.",
Expand Down
2 changes: 1 addition & 1 deletion s05_todo_write/images/todo-overview.en.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion s05_todo_write/images/todo-overview.ja.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion s05_todo_write/images/todo-overview.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions s06_subagent/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
Needs: pip install anthropic python-dotenv + ANTHROPIC_API_KEY in .env
"""

import os, subprocess, json
import os, subprocess
from pathlib import Path

try:
Expand All @@ -45,9 +45,9 @@
os.environ.pop("ANTHROPIC_AUTH_TOKEN", None)

WORKDIR = Path.cwd()
TASKS_DIR = WORKDIR / ".tasks"; TASKS_DIR.mkdir(exist_ok=True)
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
MODEL = os.environ["MODEL_ID"]
CURRENT_TODOS: list[dict] = []

SYSTEM = (
f"You are a coding agent at {WORKDIR}. "
Expand Down Expand Up @@ -122,19 +122,19 @@ def run_glob(pattern: str) -> str:
return f"Error: {e}"

def run_todo_write(todos: list) -> str:
global CURRENT_TODOS
for i, t in enumerate(todos):
if "content" not in t or "status" not in t:
return f"Error: todos[{i}] missing 'content' or 'status'"
if t["status"] not in ("pending", "in_progress", "completed"):
return f"Error: todos[{i}] has invalid status '{t['status']}'"
tasks_file = TASKS_DIR / "current_todos.json"
tasks_file.write_text(json.dumps(todos, indent=2, ensure_ascii=False))
CURRENT_TODOS = todos
lines = ["\n\033[33m## Current Tasks\033[0m"]
for t in todos:
for t in CURRENT_TODOS:
icon = {"pending": " ", "in_progress": "\033[36m▸\033[0m", "completed": "\033[32m✓\033[0m"}[t["status"]]
lines.append(f" [{icon}] {t['content']}")
print("\n".join(lines))
return f"Updated {len(todos)} tasks"
return f"Updated {len(CURRENT_TODOS)} tasks"

def extract_text(content) -> str:
"""Extract text from message content blocks."""
Expand Down
12 changes: 6 additions & 6 deletions s07_skill_loading/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
Needs: pip install anthropic python-dotenv + ANTHROPIC_API_KEY in .env
"""

import os, subprocess, json
import os, subprocess
from pathlib import Path

try:
Expand All @@ -44,9 +44,9 @@

WORKDIR = Path.cwd()
SKILLS_DIR = WORKDIR / "skills"
TASKS_DIR = WORKDIR / ".tasks"; TASKS_DIR.mkdir(exist_ok=True)
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
MODEL = os.environ["MODEL_ID"]
CURRENT_TODOS: list[dict] = []

# s07: Skill catalog scan (used by build_system below)
def _parse_frontmatter(text: str) -> tuple[dict, str]:
Expand Down Expand Up @@ -169,19 +169,19 @@ def run_glob(pattern: str) -> str:
return f"Error: {e}"

def run_todo_write(todos: list) -> str:
global CURRENT_TODOS
for i, t in enumerate(todos):
if "content" not in t or "status" not in t:
return f"Error: todos[{i}] missing 'content' or 'status'"
if t["status"] not in ("pending", "in_progress", "completed"):
return f"Error: todos[{i}] has invalid status '{t['status']}'"
tasks_file = TASKS_DIR / "current_todos.json"
tasks_file.write_text(json.dumps(todos, indent=2, ensure_ascii=False))
CURRENT_TODOS = todos
lines = ["\n\033[33m## Current Tasks\033[0m"]
for t in todos:
for t in CURRENT_TODOS:
icon = {"pending": " ", "in_progress": "\033[36m▸\033[0m", "completed": "\033[32m✓\033[0m"}[t["status"]]
lines.append(f" [{icon}] {t['content']}")
print("\n".join(lines))
return f"Updated {len(todos)} tasks"
return f"Updated {len(CURRENT_TODOS)} tasks"

def extract_text(content) -> str:
if not isinstance(content, list):
Expand Down
10 changes: 5 additions & 5 deletions s08_context_compact/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@
SKILLS_DIR = WORKDIR / "skills"
TRANSCRIPT_DIR = WORKDIR / ".transcripts"
TOOL_RESULTS_DIR = WORKDIR / ".task_outputs" / "tool-results"
TASKS_DIR = WORKDIR / ".tasks"; TASKS_DIR.mkdir(exist_ok=True)
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
MODEL = os.environ["MODEL_ID"]
CURRENT_TODOS: list[dict] = []

# s07: Skill catalog scan (inherited from s07)
def _parse_frontmatter(text: str) -> tuple[dict, str]:
Expand Down Expand Up @@ -166,19 +166,19 @@ def run_glob(pattern: str) -> str:
except Exception as e: return f"Error: {e}"

def run_todo_write(todos: list) -> str:
global CURRENT_TODOS
for i, t in enumerate(todos):
if "content" not in t or "status" not in t:
return f"Error: todos[{i}] missing 'content' or 'status'"
if t["status"] not in ("pending", "in_progress", "completed"):
return f"Error: todos[{i}] has invalid status '{t['status']}'"
tasks_file = TASKS_DIR / "current_todos.json"
tasks_file.write_text(json.dumps(todos, indent=2, ensure_ascii=False))
CURRENT_TODOS = todos
lines = ["\n\033[33m## Current Tasks\033[0m"]
for t in todos:
for t in CURRENT_TODOS:
icon = {"pending": " ", "in_progress": "\033[36m▸\033[0m", "completed": "\033[32m✓\033[0m"}[t["status"]]
lines.append(f" [{icon}] {t['content']}")
print("\n".join(lines))
return f"Updated {len(todos)} tasks"
return f"Updated {len(CURRENT_TODOS)} tasks"

def extract_text(content) -> str:
if not isinstance(content, list): return str(content)
Expand Down
14 changes: 8 additions & 6 deletions s12_task_system/README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The agent receives a project: set up a database, write APIs, add tests. It uses

You can't build the roof before laying the foundation. Tasks have ordering. Task dependencies should form a Directed Acyclic Graph (DAG); the teaching version only demonstrates `blockedBy` checking, without cycle detection.

s05's TodoWrite is a list. No dependencies, no persistence — when the conversation ends, the list is gone. What you need is a **task system**: each task is a JSON file, tasks have `blockedBy` dependencies, and they persist across sessions on disk.
s05's TodoWrite is an execution checklist for the current task, kept in session memory. What you need here is a **task system**: each task is a JSON file, tasks have `blockedBy` dependencies, and they persist across sessions on disk.

---

Expand All @@ -30,11 +30,13 @@ TodoWrite vs Task System:

| | TodoWrite (s05) | Task System (s12) |
|---|---|---|
| Storage | In-memory list | `.tasks/` JSON files |
| Dependencies | None | `blockedBy` dependency graph |
| Persistence | Lost when conversation ends | Cross-session |
| Multi-agent | None | `owner` field |
| Status | checked / unchecked | pending → in_progress → completed |
| Role | Execution checklist for the current task | Recoverable task system |
| Storage | In-process / session state | `.tasks/{id}.json` |
| Dependencies | None | `blockedBy` / `blocks` graph |
| Lifecycle | Current session / current task | Cross-session |
| Coordination | No task claiming | `owner` / claim |
| Status | pending / in_progress / completed | pending / in_progress / completed |
| Granularity | The agent's own steps | Tasks that can be claimed, tracked, and unblocked |

---

Expand Down
14 changes: 8 additions & 6 deletions s12_task_system/README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Agent がプロジェクトを受けた:データベース構築、API 実装

屋根を先に建てて基礎を後から打つことはできない。タスクには順序がある。タスクの依存関係は有向非巡回グラフ(DAG)を形成すべき;教学版は `blockedBy` チェックのみをデモし、循環検出は実装していない。

s05 の TodoWrite はリスト。依存関係も永続化もなく、会話が終わればリストも消える。必要なのは**タスクシステム**:各タスクは JSON ファイル、タスク間に `blockedBy` 依存関係、ディスク上でセッションをまたいで永続化。
s05 の TodoWrite は現在のタスクの実行チェックリストで、セッションメモリに保持される。ここで必要なのは**タスクシステム**:各タスクは JSON ファイル、タスク間に `blockedBy` 依存関係、ディスク上でセッションをまたいで永続化。

---

Expand All @@ -30,11 +30,13 @@ TodoWrite vs Task System:

| | TodoWrite (s05) | Task System (s12) |
|---|---|---|
| ストレージ | メモリ内リスト | `.tasks/` JSON ファイル |
| 依存関係 | なし | `blockedBy` 依存グラフ |
| 永続性 | 会話終了で消失 | セッション横断 |
| マルチ Agent | なし | `owner` フィールド |
| ステータス | checked / unchecked | pending → in_progress → completed |
| 位置づけ | 現在のタスクの実行チェックリスト | 復旧可能なタスクシステム |
| ストレージ | プロセス内 / セッション状態 | `.tasks/{id}.json` |
| 依存関係 | なし | `blockedBy` / `blocks` グラフ |
| ライフサイクル | 現在のセッション / 現在のタスク | セッション横断 |
| 分担 | タスク認識を扱わない | `owner` / claim |
| ステータス | pending / in_progress / completed | pending / in_progress / completed |
| 粒度 | Agent 自身の手順 | 認識・追跡・アンロックできるタスク |

---

Expand Down
Loading