This skill manages a local TODO.md task list with rich context tracking. Use when the user says "/todo", "add a todo", "mark todo as done", "list todos", "work on todo", "show todos", "add a note to todo", "log on todo", or asks to track, capture, or manage tasks in a TODO list.
Manage a structured, Markdown-based task list using a split-file architecture:
TODO.md — A lightweight index containing a numbered table of todos and a counterTODO/<NNN>-<slug>.md — One file per todo, in a TODO/ subdirectory, with full context, metadata, and guidanceTODO/DONE/ — Completed todo files are moved hereThis split keeps todo details out of Claude's context window when they aren't needed. Most mechanical operations (table manipulation, counter, file moves) are handled by the helper script todo.mjs.
Helper script: todo.mjs lives in the same directory as this skill file. Before the first script call in any operation, locate it once with Glob pattern **/.claude/skills/todo/todo.mjs and remember the resolved path as $TODO. Then run all commands as node $TODO <command> [args...] via Bash from the project root. On error, the script prints to stderr and exits non-zero.
Current todo count: !
grep -c "^| [0-9]" TODO.md 2>/dev/null || echo "0 (no TODO.md)"Determine the operation from the user's arguments or phrasing:
| User says | Operation |
|---|---|
/todo add ... or "add a todo for ..." | add |
/todo list or /todo (no args) or "list todos" or "show todos" | list |
/todo note <title or number> <text> or "add a note to todo X" | note |
/todo work <title or number> or "work on X" | work |
/todo log <title or number> <text> or "log on todo X" | log |
/todo done <title or number> or "mark X as done" | done |
/todo reopen <title or number> or "reopen todo X" | reopen |
/todo remove <title or number> or "delete todo X" | remove |
/todo test or "test the todo skill" | test |
Run node $TODO init then node $TODO next-id <title words>. Parse output to get the 3-digit number and slug (e.g. 003 fix-login-bug).
If the user provided a priority (e.g. "high priority", "P1", "low", "critical", "nice-to-have"), normalize to one of: 🔴 High / 🟡 Medium / 🟢 Low. Map common variants: P1/critical/urgent → High, P2/normal → Medium, P3/nice-to-have/low → Low. If no priority given, use empty string.
Before writing anything, compose the full detail file content while you have conversation context. The detail file must be self-contained and actionable for a future Claude session with zero prior context.
The detail file (TODO/<NNN>-<slug>.md) must include these sections:
## #<NNN> <Title> — one-sentence description of what needs to be done and why.
### Metadata — table with Created, Folder ($PWD), Project (basename or from package.json/pyproject.toml), Priority, Status (Open), Location (file:line if applicable) fields.
### Context — the full background. Mine the conversation thoroughly:
### How to work on this — step-by-step guidance:
Do not summarize or abbreviate. Zero information loss between conversation and detail file.
mkdir -p TODO via Bashnode $TODO add-row <NNN> <slug> "<title>" "<priority>" "<datetime>" (get datetime from node $TODO meta)Write to create TODO/<NNN>-<slug>.md with the composed detail contentSee examples.md for the complete detail file template.
Run node $TODO list and display the output to the user. The script groups by status (Active, Open, Done) and formats a table. If no TODO.md exists, it reports that.
Run node $TODO rows to get table rows. Find the matching todo by number or fuzzy title match (see Matching rules).
If no todo specified: filter rows for non-done items. If exactly one exists, use it. If multiple, list them and ask the user.
Get datetime from node $TODO meta.
node $TODO done-row <NNN> "<datetime>" — returns the slugnode $TODO move-done <slug>Use Edit on TODO/DONE/<NNN>-<slug>.md to:
Status to Done| Completed | <datetime> | to the Metadata table#### Conclusion under ### Work Log (create ### Work Log first if needed)Compose the Conclusion by mining the full conversation context:
Do not summarize briefly. Write enough that someone reading only the Conclusion can understand what happened.
Conclusion format:
#### Conclusion
**Completed:** <datetime>
<Full resolution narrative drawn from conversation context.>
***User note:*** <verbatim user-provided text, if any — omit if none>
Run node $TODO rows. Fuzzy match to find the todo. Use node $TODO find <NNN> to get the file path.
Read the detail file and display the full content to the user.
Run node $TODO update-status <NNN> Active "<datetime>".
Use Edit on the detail file to:
Status from Open to Active### Work Log (if not present) with a #### Context and Plan subsection:### Work Log
#### Context and Plan
**Active since:** <datetime>
<Plan of attack drawn from conversation context and "How to work on this" section.>
If ### Work Log already exists, append a new #### Context and Plan entry.
Say: "I'm ready to work on #<NNN> <title>." and outline next steps from the "How to work on this" section. Proceed to implement.
Rules: WORK does not mark done or move the file.
Run node $TODO rows. Fuzzy match. Extract the file path from the matching row's link.
Get datetime and username from node $TODO meta.
Use Edit to locate or create ### Notes (after ### How to work on this), then append:
#### <Short summary, 3-8 words>
**Added:** <datetime>
(@<username>) <user's text verbatim>
<Generated context paragraph — see below>
Add a generated context paragraph (without (@username)) if the conversation contains relevant specifics: error output, file paths, line numbers, code snippets, test results, connections to other work. Capture specifics, not summaries. Omit if no relevant context.
Run node $TODO update-status <NNN> <current-status> "<datetime>".
Rules: Always prefix user text with (@username). Notes are append-only.
Like NOTE, but the target section depends on the todo's state:
### Notes### Work LogRun node $TODO rows. Fuzzy match. Extract status and file path from the row.
Same format as NOTE. Use Edit to append to the target section. When targeting ### Work Log, append after existing subsections.
Run node $TODO update-status <NNN> <current-status> "<datetime>".
Rules: Same as NOTE — (@username) prefix, append-only.
Run node $TODO find <NNN>. Verify status is Done ✓. If not done, inform user and stop.
Get datetime from node $TODO meta.
node $TODO reopen-row <NNN> "<datetime>" — returns the slugnode $TODO move-open <slug>Use Edit on TODO/<NNN>-<slug>.md to:
Status from Done to Open| Completed | <datetime> | rowRules: Do NOT remove Work Log, Conclusion, or any history. The todo returns to Open, not Active.
node $TODO rows. Fuzzy match to find the todo.node $TODO remove-row <NNN> — outputs the file pathrm <path>Note: Do NOT renumber remaining todos or update the counter. Numbers are permanent.
Read TEST.md and follow the playbook instructions. This exercises all operations with 2 realistic todos through every state transition.
When a user references a todo, they may use:
1, 001, #1, #001 — match against the No columncsv parser, login bug — fuzzy match against the Title columnMatching rules:
#, convert to integer, use node $TODO find <number>node $TODO rows and do a case-insensitive substring match on the title textTODO.md contains ONLY: headings, the table, the --- separator, <!-- next: N --> counter, and <!-- skill: SHA --> version commentTODO/<NNN>-<slug>.md — open/active todo detail filesTODO/DONE/<NNN>-<slug>.md — completed todo detail filesOpen → Active (work) → Done (done) → Open (reopen). In TODO.md done status is Done ✓.For complete format templates, see examples.md.
TODO.md tracks the skill version via <!-- skill: SHA -->. On any operation, if the SHA doesn't match the current skill version, read MIGRATIONS.md for migration steps. Apply them in order, then update the SHA.
If no <!-- skill: SHA --> comment exists, just add it. No migration needed.
Control Philips Hue lights and scenes via the OpenHue CLI.