Work with markdown-based task lists in .llm/todo.md files. Use when managing tasks, working with todo lists, extracting incomplete tasks, marking tasks complete, or implementing tasks from a task list.
This skill enables working with the markdown task list stored in .llm/todo.md.
Resolve <plugin-root> before running plugin scripts:
${CLAUDE_PLUGIN_ROOT}.skills/tasks/SKILL.md file.Extract the first incomplete task with its context:
python <plugin-root>/scripts/task_get.py .llm/todo.md
Returns the first [ ] checkbox line with all indented context lines below it.
Exit codes: 0 (success), 1 (file not found or error)
Add a new task:
python <plugin-root>/scripts/task_add.py .llm/todo.md "Task description
Context line 1
Context line 2"
When adding more than one task, chain the calls into a single bash command with && rather than running a separate command per task:
python <plugin-root>/scripts/task_add.py .llm/todo.md "First task
Context line 1" && \
python <plugin-root>/scripts/task_add.py .llm/todo.md "Second task" && \
python <plugin-root>/scripts/task_add.py .llm/todo.md "Third task"
Running the whole batch as one command keeps the write window to .llm/todo.md extremely short, so concurrent sessions writing to the same file are far less likely to interleave their tasks.
Creates the .llm/ directory and todo.md file if they do not exist, and appends the new task with a [ ] checkbox. The script preserves all indentation in multi-line strings.
Exit codes: 0 (success), 1 (error)
Mark the first incomplete task with a marker character:
python <plugin-root>/scripts/task_mark.py .llm/todo.md
python <plugin-root>/scripts/task_mark.py .llm/todo.md --marker='!' --reason='precommit failed on the parser rewrite'
Changes the first [ ] to [x] by default. Pass --marker with any single non-space character (e.g. !, >, -) to use a different marker.
--reason appends the failure as an indented context line at the end of the task body, above any trailing blank line:
- [!] Require authentication on API routes.
Reuse `validateJwt` from `/workspace/project/src/auth/tokens.ts`.
Blocked 2026-08-19 session 94aec27b: validateJwt does not exist; the repo uses `verifyToken`
The reason lives in the task body so it survives archive, recovery, and reinstatement, and so task_get.py hands it to the next attempt along with the rest of the context. Quote the concrete failure — the failing command, the assertion, the missing symbol — rather than restating the task.
--reason is required with --marker='!'; blocking a task without one exits 1 and leaves the file untouched. It is optional for every other marker.
Exit codes: 0 (success), 1 (no incomplete tasks, missing reason for !, or error)
Archive a completed task list:
python <plugin-root>/scripts/task_archive.py .llm/todo.md
Moves the file to .llm/YYYY-MM-DD-todo.md where YYYY-MM-DD is today's date.
Blocked [!] tasks never reach the archive. Each one moves into a fresh .llm/todo.md with its indented context, still marked [!], so task_get.py keeps skipping it and task_unblock.py stays the deliberate way to reopen it. The script prints the archive path and how many blocked tasks it carried forward.
Exit codes: 0 (success), 1 (file not found or error)
Move blocked [!] tasks out of archived task lists and back into .llm/todo.md:
python <plugin-root>/scripts/task_unblock.py .llm --dry-run
python <plugin-root>/scripts/task_unblock.py .llm
Scans every .llm/*todo*.md, including the live .llm/todo.md, and moves each [!] task with its indented context into .llm/todo.md as an open [ ] task, stamped with an indented Recovered <yyyy-mm-dd> session <session-id> line. Recovery is a move, so the task disappears from the archive it came from and re-running the script cannot duplicate it. Emptied archive files stay on disk, and existing Blocked lines are preserved so a task's full round-trip history stays readable.
Recovery rewrites historical archive files. Always run --dry-run first and confirm the report with the user before running it for real.
Exit codes: 0 (success, including when nothing is blocked), 1 (directory not found or error)
The task list is in .llm/todo.md.
NEVER use the Read tool on .llm/todo.md. Always interact with the task list exclusively through the Python scripts.
[ ] - Not started (ready to work on)[x] - Completed[!] - Blocked after failed attemptEach task includes indented context lines with full implementation details:
Each task is extracted and executed in isolation. Every task must contain ALL context needed to implement it. Repeat shared context in every related task. Never reference other tasks.
If tasks were created from a plan file, include the plan file path in each task so the implementing agent can read the full context.
When using Claude Code's native plan mode to design an implementation before creating tasks:
~/.claude/plans/<auto-generated-name>.md (e.g., ~/.claude/plans/abstract-knitting-garden.md).llm/plans/ directory if it does not exist~/.claude/plans/<auto-generated-name>.md to .llm/plans/<yyyy-mm-dd>-<descriptive-name>.md2025-12-04-thread-safety-tests.md, 2025-12-04-plugin-hook-system.md).llm/plans/ file in each task so the implementing agent can read the full contextWhen using Codex planning, store the plan under .llm/plans/<yyyy-mm-dd>-<descriptive-name>.md before adding tasks, then include that absolute path in each task.
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer