diff --git a/s05_todo_write/README.en.md b/s05_todo_write/README.en.md index 9ae608996..88c526cc7 100644 --- a/s05_todo_write/README.en.md +++ b/s05_todo_write/README.en.md @@ -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: @@ -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. diff --git a/s05_todo_write/README.ja.md b/s05_todo_write/README.ja.md index db383de82..3830c02d2 100644 --- a/s05_todo_write/README.ja.md +++ b/s05_todo_write/README.ja.md @@ -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 つと一緒にディスパッチマップに追加される: @@ -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」は環境変数パスの用途を説明しており、デフォルト分岐の戻り値のセマンティクスとは異なるため注意。 diff --git a/s05_todo_write/README.md b/s05_todo_write/README.md index 146a4d682..3a4de4e10 100644 --- a/s05_todo_write/README.md +++ b/s05_todo_write/README.md @@ -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: @@ -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 路径的用途,和默认分支的返回值语义不同,阅读时需区分。 @@ -153,4 +155,4 @@ Task System 相比 TodoWrite 的核心增量: - + diff --git a/s05_todo_write/code.py b/s05_todo_write/code.py index db4597453..6793129a0 100644 --- a/s05_todo_write/code.py +++ b/s05_todo_write/code.py @@ -12,7 +12,7 @@ todo_write ← NEW +------------------+ | - .tasks/current_todos.json + in-memory current_todos | if rounds_since_todo >= 3: inject @@ -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: @@ -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 = ( @@ -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.", diff --git a/s05_todo_write/images/todo-overview.en.svg b/s05_todo_write/images/todo-overview.en.svg index afec010dc..b4655e1a9 100644 --- a/s05_todo_write/images/todo-overview.en.svg +++ b/s05_todo_write/images/todo-overview.en.svg @@ -73,7 +73,7 @@ todo_write s05 New - → .tasks/current_todos.json + → in-memory TODO list diff --git a/s05_todo_write/images/todo-overview.ja.svg b/s05_todo_write/images/todo-overview.ja.svg index 8b3a50648..ce0f6977b 100644 --- a/s05_todo_write/images/todo-overview.ja.svg +++ b/s05_todo_write/images/todo-overview.ja.svg @@ -73,7 +73,7 @@ todo_write s05 新規 - → .tasks/current_todos.json + → メモリ内 TODO リスト diff --git a/s05_todo_write/images/todo-overview.svg b/s05_todo_write/images/todo-overview.svg index 53d05fe5d..25e12fecd 100644 --- a/s05_todo_write/images/todo-overview.svg +++ b/s05_todo_write/images/todo-overview.svg @@ -73,7 +73,7 @@ todo_write s05 新增 - → .tasks/current_todos.json + → 进程内 TODO 列表 diff --git a/s06_subagent/code.py b/s06_subagent/code.py index e61c325b8..7c9749f81 100644 --- a/s06_subagent/code.py +++ b/s06_subagent/code.py @@ -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: @@ -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}. " @@ -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.""" diff --git a/s07_skill_loading/code.py b/s07_skill_loading/code.py index 66159a3ac..fd3a5d916 100644 --- a/s07_skill_loading/code.py +++ b/s07_skill_loading/code.py @@ -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: @@ -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]: @@ -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): diff --git a/s08_context_compact/code.py b/s08_context_compact/code.py index 281018231..a11be2202 100644 --- a/s08_context_compact/code.py +++ b/s08_context_compact/code.py @@ -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]: @@ -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) diff --git a/s12_task_system/README.en.md b/s12_task_system/README.en.md index a4737cc2c..756aedb42 100644 --- a/s12_task_system/README.en.md +++ b/s12_task_system/README.en.md @@ -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. --- @@ -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 | --- diff --git a/s12_task_system/README.ja.md b/s12_task_system/README.ja.md index 11d04975b..ebc8c7e06 100644 --- a/s12_task_system/README.ja.md +++ b/s12_task_system/README.ja.md @@ -16,7 +16,7 @@ Agent がプロジェクトを受けた:データベース構築、API 実装 屋根を先に建てて基礎を後から打つことはできない。タスクには順序がある。タスクの依存関係は有向非巡回グラフ(DAG)を形成すべき;教学版は `blockedBy` チェックのみをデモし、循環検出は実装していない。 -s05 の TodoWrite はリスト。依存関係も永続化もなく、会話が終わればリストも消える。必要なのは**タスクシステム**:各タスクは JSON ファイル、タスク間に `blockedBy` 依存関係、ディスク上でセッションをまたいで永続化。 +s05 の TodoWrite は現在のタスクの実行チェックリストで、セッションメモリに保持される。ここで必要なのは**タスクシステム**:各タスクは JSON ファイル、タスク間に `blockedBy` 依存関係、ディスク上でセッションをまたいで永続化。 --- @@ -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 自身の手順 | 認識・追跡・アンロックできるタスク | --- diff --git a/s12_task_system/README.md b/s12_task_system/README.md index 8867cbb12..03a925281 100644 --- a/s12_task_system/README.md +++ b/s12_task_system/README.md @@ -16,7 +16,7 @@ Agent 接到一个项目:搭数据库、写 API、加测试。它用 s05 的 T 盖房子不能先盖屋顶再打地基。任务之间有先后。任务依赖应该形成有向无环图(DAG);教学版只演示 `blockedBy` 检查,没有实现环检测。 -s05 的 TodoWrite 是一个列表。没有依赖关系、没有持久化、对话结束列表就没了。你需要的是**任务系统**:每个任务是一个 JSON 文件,任务之间有 `blockedBy` 依赖,跨会话持久化在磁盘上。 +s05 的 TodoWrite 是当前任务的执行清单,保存在会话内存中。这里需要的是**任务系统**:每个任务是一个 JSON 文件,任务之间有 `blockedBy` 依赖,跨会话持久化在磁盘上。 --- @@ -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 自己的步骤 | 可被认领、追踪、解锁的任务 | --- @@ -277,4 +279,4 @@ CC 的任务系统有四个工具(不是教学版的一个通用 Task 工具 - + diff --git a/s20_comprehensive/README.en.md b/s20_comprehensive/README.en.md index 115a0c1fa..07bdcadb5 100644 --- a/s20_comprehensive/README.en.md +++ b/s20_comprehensive/README.en.md @@ -118,7 +118,7 @@ That means permission, logging, and audit logic all attach to the same hook poin S20 keeps two planning layers: -- `todo_write`: lightweight plan for the current session, written to `.tasks/current_todos.json` +- `todo_write`: lightweight plan for the current session, kept in memory - task graph: cross-session, dependency-aware, claimable task files under `.tasks/task_*.json` The first keeps a single agent from drifting. The second supports team coordination. diff --git a/s20_comprehensive/README.ja.md b/s20_comprehensive/README.ja.md index a755a8ea1..157c6c2ec 100644 --- a/s20_comprehensive/README.ja.md +++ b/s20_comprehensive/README.ja.md @@ -118,7 +118,7 @@ if blocked: S20 には 2 層の plan がある: -- `todo_write`: current session 用の軽量 plan。`.tasks/current_todos.json` に保存。 +- `todo_write`: current session 用の軽量 plan。メモリに保持。 - task graph: cross-session、dependency-aware、claimable な task file。`.tasks/task_*.json` に保存。 前者は単独 agent の drift を防ぐ。後者は team coordination の土台になる。 diff --git a/s20_comprehensive/README.md b/s20_comprehensive/README.md index aa3a139bf..f9f49d715 100644 --- a/s20_comprehensive/README.md +++ b/s20_comprehensive/README.md @@ -118,7 +118,7 @@ if blocked: S20 同时保留两层计划: -- `todo_write`:当前会话内的轻量计划,写入 `.tasks/current_todos.json` +- `todo_write`:当前会话内的轻量计划,保存在内存中 - task graph:跨会话、可依赖、可认领的任务文件,写入 `.tasks/task_*.json` 前者帮助单个 Agent 不漂移;后者支撑团队协作。 diff --git a/s20_comprehensive/code.py b/s20_comprehensive/code.py index f5706a36e..ced6860ad 100644 --- a/s20_comprehensive/code.py +++ b/s20_comprehensive/code.py @@ -73,6 +73,7 @@ def terminal_print(text: str): # worktrees, and teammates on top of this same file-backed state. TASKS_DIR = WORKDIR / ".tasks" TASKS_DIR.mkdir(exist_ok=True) +CURRENT_TODOS: list[dict] = [] @dataclass @@ -457,15 +458,15 @@ def call_tool_handler(handler, args: dict, name: str) -> str: def run_todo_write(todos: list) -> str: + global CURRENT_TODOS for i, todo in enumerate(todos): if "content" not in todo or "status" not in todo: return f"Error: todos[{i}] missing 'content' or 'status'" if todo["status"] not in ("pending", "in_progress", "completed"): return f"Error: todos[{i}] has invalid status '{todo['status']}'" - path = TASKS_DIR / "current_todos.json" - path.write_text(json.dumps(todos, indent=2, ensure_ascii=False)) - print(f" \033[33m[todo] updated {len(todos)} item(s)\033[0m") - return f"Updated {len(todos)} todos" + CURRENT_TODOS = todos + print(f" \033[33m[todo] updated {len(CURRENT_TODOS)} item(s)\033[0m") + return f"Updated {len(CURRENT_TODOS)} todos" # ── MessageBus ──