{
  "markdown": "# claude-todo-mirror\n\nA Claude Code plugin that mirrors `TodoWrite` state to per-session markdown files\nwith hierarchical checkboxes — keep an always-visible task view open in VS Code,\nObsidian, or any markdown viewer instead of scrolling back through chat history.\n\n## The problem\n\nClaude Code's inline `TodoWrite` updates scroll out of view as the conversation\ngrows. When you want to know what's left, you scroll back through messages.\nThe built-in **Tasks** panel helps, but it's tied to one window and doesn't\nexpose a hierarchy or an external file you can pin in another editor.\n\n## What this plugin does\n\nOn every `TodoWrite` call, a hook writes a markdown checklist for **the current\nsession** to `<project>/.claude/todos/session-<id>.md` and refreshes\n`_index.md` with all sessions in this project. Open either file in VS Code,\nObsidian, or `tail -f` it from a terminal — it auto-updates.\n\n### Sample output\n\n```\n# Session `abc12345-...`\n\n**Project**: `/Users/me/code/my-app`\n**Updated**: 2026-05-07 17:40:21 KST\n**Progress**: 2/7 (29%)\n**Now**: ▶ Verifying render_todos.py\n\n---\n\n- [x] Plugin scaffolding\n- [x] Hook script\n- [▶] Verifying render_todos.py\n  - [ ] edge case: indented children\n  - [ ] edge case: empty todos\n- [ ] README + LICENSE\n- [ ] GitHub push\n```\n\n### Hierarchy convention\n\n`TodoWrite` items are flat by spec, so this plugin uses leading whitespace in\nthe `content` field as the hierarchy signal. Two spaces (or one tab) = one\nindent level:\n\n```python\nTodoWrite([\n    {\"content\": \"Parent task\",      \"status\": \"in_progress\", \"activeForm\": \"Working\"},\n    {\"content\": \"  Child task A\",   \"status\": \"pending\",     \"activeForm\": \"...\"},\n    {\"content\": \"  Child task B\",   \"status\": \"pending\",     \"activeForm\": \"...\"},\n    {\"content\": \"Sibling task\",     \"status\": \"pending\",     \"activeForm\": \"...\"},\n])\n```\n\nTell Claude in your project's `CLAUDE.md` (or per-prompt) to follow that\nconvention when it writes nested todos.\n\n### Status mapping\n\n| TodoWrite status | Rendered |\n| --- | --- |\n| `pending` | `[ ]` |\n| `in_progress` | `[▶]` |\n| `completed` | `[x]` |\n\nThe first `in_progress` item is also pulled into a `Now: ▶ ...` header line so\nyou can see the active task at a glance.\n\n### Per-session isolation\n\nEach Claude Code session gets its own `session-<id>.md`. Run multiple sessions\nin parallel — the plugin keeps them separate. `_index.md` summarizes all of\nthem in one table sorted by last activity:\n\n```\n| Session | Progress | Now | File |\n| --- | --- | --- | --- |\n| `abc12345` | 2/7 (29%) | Verifying render_todos.py | [session-abc...md](./...) |\n| `def67890` | 4/4 (100%) | -                         | [session-def...md](./...) |\n```\n\n## Install\n\nThis repo is a **single-plugin marketplace** — `marketplace.json` is committed\nat `.claude-plugin/marketplace.json` so you can install it via the standard\nplugin commands.\n\n> **Heads up**: `/plugin ...` commands only work in the Claude Code **CLI**\n> (terminal), not in the Desktop app. Once installed, slash commands like\n> `/todos-watch` work in both Desktop and CLI.\n\nOpen a Claude Code CLI session and enter these two commands **separately**\n(do not paste them on the same line — Claude Code parses the second one as\npart of the first command's URL):\n\n```\n/plugin marketplace add bighaeil/claude-todo-mirror\n```\n\nThen, on its own:\n\n```\n/plugin install claude-todo-mirror@claude-todo-mirror\n```\n\nWhen the install dialog asks for scope, **\"Install for you (user scope)\"** is\nthe right choice for personal use — the plugin becomes available across every\nproject and any Claude Code surface (CLI, Desktop).\n\nFor local development without going through the marketplace:\n\n```\nclaude --plugin-dir /path/to/claude-todo-mirror\n```\n\nAfter install, every `TodoWrite` call in any project will create\n`.claude/todos/` under that project's root.\n\n### Manual hook registration (without `/plugin install`)\n\nIf you can't or don't want to use the plugin marketplace, drop this into your\nproject's `.claude/settings.local.json` (or user `~/.claude/settings.json`):\n\n```json\n{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"TodoWrite\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"python3 /absolute/path/to/claude-todo-mirror/scripts/render_todos.py\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n### Recommended workflow\n\n1. Open `<project>/.claude/todos/_index.md` in VS Code (or Obsidian) and pin\n   the tab.\n2. Or open the active session file `session-<your-session>.md` directly.\n3. Work as usual — the file refreshes on every `TodoWrite` call.\n\nFor multi-session overview, the `_index.md` is the single source of truth.\n\n## Live terminal monitor (`/todos-watch`)\n\n> **Prerequisite** — this command depends on the macOS `watch` CLI. Without\n> it, the new Terminal window opens but immediately exits with\n> `command not found: watch`.\n>\n> ```bash\n> brew install watch\n> ```\n>\n> The core hook (`TodoWrite` → markdown mirroring) only needs Python 3 and\n> works on any OS. `watch` is a dependency **for the terminal live view\n> only** — if you open the markdown files directly in VS Code or Obsidian,\n> you don't need it.\n\nDon't want to leave an editor pinned? Run the bundled slash command from any\nClaude Code session:\n\n```\n/todos-watch\n```\n\nThis opens a new **macOS Terminal** window running `watch -d` against this\nproject's `.claude/todos/`. It refreshes every 10 seconds and highlights any\nline that changes — every `TodoWrite` call shows up live without you scrolling\nback through chat or copy-pasting paths.\n\n### Environment compatibility\n\n| Environment | `/todos-watch` | Fallback |\n| --- | --- | --- |\n| macOS + `watch` installed | Works | — |\n| macOS + `watch` missing | Fails | `brew install watch` |\n| Linux | Unsupported (osascript-based) | Run `bash <plugin-dir>/scripts/watch-todos.sh \"$PWD\"` directly |\n| Windows | Unsupported | Same as Linux, via WSL or git-bash |\n\nThe `<plugin-dir>` path under `~/.claude/plugins/cache/claude-todo-mirror/` is\nthe script's location once installed via the marketplace.\n\n## Pause and resume mirroring (`/todos-pause`, `/todos-resume`)\n\nWant to keep the plugin installed but temporarily stop markdown mirroring\n(e.g. during a quick scratchpad session you don't want to record)? Two\nslash commands toggle a per-project flag:\n\n```\n/todos-pause     # creates .claude/todos/.paused → mirroring suspended\n/todos-resume    # removes the flag → mirroring active again\n```\n\nWhen `.paused` exists, every `TodoWrite` call still triggers the hook, but\n`render_todos.py` short-circuits on the flag and writes nothing — no\n`session-*.md` updates, and any open `/todos-watch` monitor stays frozen.\n\nNotes:\n\n- The flag is **per-project** (one flag per `${CLAUDE_PROJECT_DIR}/.claude/todos/`).\n  Pausing one project does not affect mirroring in another.\n- `TodoWrite` token usage is **not affected** — Claude still calls the tool\n  based on its own judgment. Only the file mirroring is suppressed.\n- The `.paused` file itself is empty; you can also create or remove it\n  manually with `touch` / `rm` if you prefer.\n\n## Requirements\n\n- Claude Code (any version that supports the plugin system + `PostToolUse` hooks)\n- Python 3 (any 3.8+ available on `python3` in `PATH`)\n- macOS + `watch` (only for `/todos-watch`; the core hook works on any platform)\n\nNo other dependencies — the hook is a single self-contained Python file.\n\n## How it works\n\n```\nhooks/hooks.json\n  └─ PostToolUse(matcher=TodoWrite)\n       └─ scripts/render_todos.py    (reads stdin JSON, writes markdown)\n            └─ short-circuits early if .claude/todos/.paused exists\n\ncommands/todos-watch.md\n  └─ /todos-watch\n       └─ scripts/launch-watch.sh    (osascript → new Terminal window)\n            └─ scripts/watch-todos.sh   (watch -d on the mirror files)\n\ncommands/todos-pause.md\n  └─ /todos-pause\n       └─ scripts/pause.sh           (touch .claude/todos/.paused)\n\ncommands/todos-resume.md\n  └─ /todos-resume\n       └─ scripts/resume.sh          (rm -f .claude/todos/.paused)\n```\n\nThe hook runs synchronously after every `TodoWrite`, parses the\n`tool_input.todos` array, and rewrites the session file + index. Failures are\nlogged to `stderr` and never block the tool — at worst your file goes stale.\n\n## Configuration\n\nNone. The plugin uses three well-known paths under `${CLAUDE_PROJECT_DIR}/.claude/todos/`:\n\n- `session-<session_id>.md` — per-session checklist (auto-generated by the hook)\n- `_index.md` — summary table of all sessions in the project (auto-regenerated)\n- `.paused` — empty toggle flag; when present, the hook writes nothing.\n  Created by `/todos-pause`, removed by `/todos-resume`. You can also create\n  or remove it manually with `touch` / `rm`.\n\nAdd `.claude/todos/` to your project's `.gitignore` if you don't want\nsession files committed.\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n\n---\n\n## 한국어 요약\n\nClaude Code의 `TodoWrite` 결과는 인라인 마크다운으로만 출력되고 대화가 길어지면\n스크롤 위로 사라집니다. 진행 상황을 보려면 매번 위로 거슬러 올라가야 하죠.\n\n이 플러그인은 `TodoWrite` 호출이 일어날 때마다 **현재 세션의 todo를\n계층 체크박스가 있는 markdown 파일로 자동 저장**합니다.\n\n```\n<프로젝트>/.claude/todos/\n├── session-<세션ID>.md    # 채널별 체크리스트\n└── _index.md              # 모든 채널 요약 표\n```\n\nVS Code · Obsidian · 마크다운 뷰어 등에 파일을 한 번 열어 두기만 하면, 매번\n자동으로 갱신되는 살아있는 todo 뷰가 됩니다. 채팅 채널이 여러 개여도 세션\nID로 분리되어 헷갈리지 않습니다.\n\n### 들여쓰기 규칙\n\n```python\nTodoWrite([\n    {\"content\": \"상위 작업\"},\n    {\"content\": \"  하위 작업 1\"},   # 2 space → 1 단계 들여쓰기\n    {\"content\": \"  하위 작업 2\"},\n])\n```\n\n`CLAUDE.md`에 이 규칙을 알려주면 Claude가 자동으로 계층 todo를 만들어 줍니다.\n\n### 설치\n\n> **주의**: `/plugin ...` 명령은 **Claude Code 터미널 CLI에서만** 동작합니다\n> (Desktop 앱에서는 안 됨). 설치 후 `/todos-watch` 같은 슬래시 명령은 Desktop과\n> CLI 양쪽에서 모두 사용 가능합니다.\n\nCLI 세션에서 두 명령을 **각각 따로** 입력하세요 — 한 줄에 같이 붙이면 Claude\nCode가 두 번째 명령을 첫 명령의 URL 일부로 해석해서 실패합니다.\n\n```\n/plugin marketplace add bighaeil/claude-todo-mirror\n```\n\n그 다음 별도로:\n\n```\n/plugin install claude-todo-mirror@claude-todo-mirror\n```\n\n설치 스코프 선택 다이얼로그가 뜨면 **\"Install for you (user scope)\"** 권장\n— 모든 프로젝트에서 사용 가능하고 Desktop에서도 슬래시 명령이 잡힙니다.\n\n또는 hook 직접 등록 (위 영어 섹션 \"Manual hook registration\" 참조).\n\n### 터미널 실시간 모니터링 — `/todos-watch`\n\n`/todos-watch`로 새 macOS Terminal 창에서 todo 변경을 실시간 확인할 수 있습니다.\n사전에 `brew install watch` 필요 (macOS 기본 미설치).\n\n상세 사전 요구사항·OS 호환성·Linux/Windows 대안은 위 영문 섹션\n[Live terminal monitor (`/todos-watch`)](#live-terminal-monitor-todos-watch)\n를 참조하세요.\n\n### 미러링 일시정지·재개 — `/todos-pause`, `/todos-resume`\n\n플러그인은 그대로 두고 markdown 미러링만 잠시 끄고 싶을 때 사용합니다.\n\n- `/todos-pause` → 현재 프로젝트의 `.claude/todos/.paused` flag 생성. 이후\n  `TodoWrite` 호출이 일어나도 markdown 파일은 갱신되지 않습니다 (열어둔\n  `/todos-watch` 모니터도 정지된 상태로 보임).\n- `/todos-resume` → flag 제거. 다음 `TodoWrite` 호출부터 다시 갱신됩니다.\n\nflag는 **프로젝트별**이라 다른 프로젝트의 미러링에는 영향 없습니다. 또한\n`TodoWrite`의 토큰 사용 자체는 그대로 — Claude가 도구를 호출하는 것은 막지\n않고 markdown 저장만 중단합니다.\n\n상세는 위 영문 섹션 [Pause and resume mirroring](#pause-and-resume-mirroring-todos-pause-todos-resume) 참조.\n",
  "bytes": 10827,
  "sha": "03d541d9bd21dcd141dba78eb81820e0a35b868e4bc4808241a4fb040ceb007e",
  "repo_slug": "bighaeil/claude-todo-mirror",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_bighaeil_claude_todo_mirror_claude_todo__329d7751/readme"
}