| name | task-list |
| description | TaskCreate / TaskUpdate / render the current TaskList in response to `/task-list` (with or without flags). The skill body executes immediately on the syntax — no permission-seeking, no echoing the list back as plain text. |
| argument-hint | [<item>...] [--update [<#> :: <new>]] [--display] |
| user-invocable | true |
/task-list
When the user invokes /task-list, act on the syntax immediately. The
directive is unambiguous; do not ask "would you like me to proceed?" and do not
echo the input back as plain text before acting.
Modes
1. Bare invocation: /task-list <items>
Call TaskCreate once per item in the provided list.
- Items may arrive as a numbered list, bulleted list, or newline-separated
lines.
- Use imperative-form subjects (e.g.
"Read SKILL.md", not "Reading SKILL.md"
or "Should I read SKILL.md?").
- Each item → one TaskCreate call. No batching into a single task.
2. --update (no argument): reconcile
Derive the "latest set of tasks for this session" from your own conversation
context, then reconcile against the current TaskList:
- TaskCreate for tasks that have emerged in conversation but aren't tracked
yet.
- TaskUpdate(status=deleted) for tasks that are obsolete (scope dropped,
superseded, no longer relevant).
- Leave already-completed tasks alone. They are history, not reconcilable
state.
Do NOT ask the user "what's the latest set?" — the directive is to derive it
from session context.
3. --update <#> :: <new subject/description>: in-place rewrite
Call TaskUpdate(taskId=<#>, subject=<new>) (or description=) on the same
task ID. The original ID is preserved.
- The
:: separator splits the task ID from the new content.
- Do NOT delete + re-create — that is a different operation. TaskUpdate
preserves the original ID; use it.
- Default to
subject=. Use description= (stripping the prefix) only when the
new content carries an explicit desc: prefix.
- If
<#> does not match any task in the current TaskList, surface an inline
error and do NOT call TaskUpdate.
4. --display: render the full TaskList
Render every task in your response — id, status, subject (and owner if set).
Do NOT mutate any task. This mode is read-only.
- Call TaskList to retrieve the full state.
- If the default rendering truncates (e.g.
... +N pending), enumerate the rest
by calling TaskGet on each remaining task id.
- Render the full list inline as a table or numbered list — never echo the
truncated form.
Fallback: Task tools absent from the tool surface
An interactive session may have TaskCreate, TaskUpdate, TaskList, and
TaskGet gated off the tool surface entirely (a GrowthBook experiment flag;
legacy TodoWrite can be absent too, and headless -p sessions are
unaffected). When none of the four tools are callable, this skill still applies
— do not report "the tool isn't available" and drop tracking.
- State once, then proceed. In your first response after detecting the gap,
say once that the Task tools are absent from this session's tool surface and
that you are tracking the queue as a Markdown table instead. Do not repeat the
caveat on every turn, and do not ask permission to proceed.
- This is a fallback, not a preference. If the Task tools ARE present on the
tool surface, use them per the Modes above — the Markdown table is only for
when they are gone.
Markdown-table fallback format
Render the queue as a Markdown table — not free prose, not a bulleted checklist
— with exactly these columns:
| # | Status | Task |
|---|
| 1 | pending | Read SKILL.md |
| 2 | in_progress | Run the test suite |
| 3 | completed | Bump version in pyproject.toml |
# is a stable per-session ordinal — reuse it across edits to the same row;
do not renumber on every update.
Status is one of pending, in_progress, completed, or deleted.
- Re-render the full table (not a diff) each time the queue changes, the same
way
--display renders the full TaskList.
Examples
Bare invocation
User: /task-list
- Read SKILL.md
- Run the test suite
- Bump version in pyproject.toml
→ Call TaskCreate(subject="Read SKILL.md", ...),
TaskCreate(subject="Run the test suite", ...),
TaskCreate(subject="Bump version in pyproject.toml", ...).
--update reconcile
User: /task-list --update (after 10 turns where item 4 was split, a new
task emerged, and item 5 was dropped)
→ TaskCreate(...) for the emerged task. →
TaskUpdate(taskId=5, status=deleted) for the dropped task. → For #4: if it is
being rewritten in place, TaskUpdate(taskId=4, ...). If it is being split
into multiple tasks, TaskCreate the new tasks and
TaskUpdate(taskId=4, status=deleted) the original — splitting is a distinct
operation from rewriting, and only splitting may delete.
--update <#> :: <new>
User:
/task-list --update 3 :: Bump version in pyproject.toml AND .claude-plugin/plugin.json (lockstep)
→
TaskUpdate(taskId=3, subject="Bump version in pyproject.toml AND .claude-plugin/plugin.json (lockstep)").
--display
User: /task-list --display (20 tasks tracked; default render truncates
after 5)
→ TaskList() → see truncated 5 visible + "... +15 pending". → TaskGet(...) ×
15 for the remaining IDs. → Render all 20 inline in the response.
Anti-patterns
These are the captured baseline rationalizations — do NOT do them.
- ❌ "Would you like me to proceed?" The syntax is unambiguous. Act.
- ❌ "The skill is not available, here is your list as plain text." The
skill IS this body; if you're reading this, you can act.
- ❌ "I've been tracking these in a markdown checklist already." This
applies only when the Task tools are available on the tool surface — prose
checklists are not a substitute for TaskList state; TaskCreate is the tracking
mechanism. When the Task tools are absent, this anti-pattern does not apply:
the Markdown-table fallback above is the correct mechanism, not an improvised
substitute.
- ❌ "I'll surface the proposed changes for confirmation." For
--update,
derive and execute. The user will see the result and can correct it via
another /task-list --update <#> :: <fix> if wrong.
- ❌ For a single-task
--update <#> rewrite: delete + recreate. TaskUpdate
preserves the original ID; use its subject= / description= parameters.
(Splitting one task into several is a different operation — see Mode 2.)
- ❌ For
--display: render only the visible 5. Default truncation is the
problem this mode exists to solve.
When to use
- A multi-step task surfaced in conversation that needs durable tracking.
- Mid-session scope drift that has left the TaskList stale.
- Operator wants to see the full queue (default render truncates after ~5).
When NOT to use
- One- or two-step trivial work — just do it.
- The current task list is already accurate and up-to-date.
- Deleting a single known-obsolete task — use
TaskUpdate(status=deleted)
directly.