plugins/taskwarrior/skills/taskwarrior/SKILL.md
Use when querying or modifying tasks via the taskwarrior CLI - dense recipes for listing, single-field lookups, multi-task batched lookups, full-text search, and the soft description-length convention. Activates on `task list`, `task add`, `task info`, `task done`, "what's on the backlog", "find a task", or any taskwarrior interaction.
npx skillsauth add technicalpickles/pickled-claude-plugins taskwarriorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use these recipes whenever you invoke task on the user's behalf. They cut output size 4-10× compared to default formats.
Companion config: This skill assumes ~/.taskrc has the named dense report defined and verbose= configured to suppress override-echo noise. If task (with no args) doesn't run a dense report, see the design doc at docs/superpowers/specs/2026-05-08-taskwarrior-token-density-design.md.
Default. Use the named report:
task <filter> dense
Examples:
task project:pickled-claude-plugins dense
task +followup dense
task dense # bare = all pending, urgency-sorted
Custom shape, when you need different columns or truncation:
task <filter> export | jq -r '.[] | "\(.uuid[0:8]) [\(.project // "-")] \(.tags // [] | join(",")) \(.due // "-") \(.description[0:80])"'
This produces ~120 chars/task. The dense report produces ~150 chars/task with column alignment. The jq form is for when you need a specific shape (e.g., longer description, different fields).
Never: raw task list with no filter. The 320+ pending tasks at default formatting is the largest single cost in your taskwarrior I/O.
When you need ONE field of a known task, use _get with a DOM reference (<uuid>.<field>):
task _get <uuid>.description
task _get <uuid>.project
task _get <uuid>.tags
task _get <uuid>.due
task _get <uuid>.urgency
You can also batch fields in one call:
task _get <uuid>.project <uuid>.tags <uuid>.due
_get returns the raw field value(s), no metadata, no formatting. Roughly 10× denser than task <uuid> info. Note the syntax: _get is the command and the DOM reference is its argument. NOT task <uuid> _get description (that returns empty).
Never: task <uuid> info when you only need one field. info dumps full metadata (entry/modified/uuid/status/etc.): useful when triaging, wasteful for field lookup.
When inspecting several tasks at once, pass UUIDs (or short UUIDs) as space-separated positional args to export:
task UUID1 UUID2 UUID3 export | jq -r '.[] | "\(.uuid[0:8]) [\(.project // "-")] \(.description[0:120])"'
Or for richer output:
task UUID1 UUID2 UUID3 export | jq -r '.[] | "uuid: \(.uuid[0:8]) tags: \(.tags // [] | join(",")) due: \(.due // "-") desc: \(.description[0:80])"'
Short (8-char) UUIDs work fine: task c9dca83f 53c8850a b940d369 export is equivalent. Comma-separated forms (uuid:A,B,C) do NOT work in taskwarrior 3.4.2; they return empty.
Never: task A info && echo --- && task B info && echo --- && task C info && .... This pattern has been observed at 25,000+ chars per call (vs ~600 chars for the batched export equivalent).
task list | grep is unreliable because descriptions wrap across lines. Use export + jq:
# Search descriptions (null-safe)
task export | jq -r '.[] | select((.description // "") | test("PATTERN"; "i")) | "\(.uuid[0:8]) \(.description[0:120])"'
# Search annotations
task export | jq -r '.[] | select(.annotations[]?.description | test("PATTERN"; "i")) | "\(.uuid[0:8]) \(.description[0:120])"'
# Search both
task export | jq -r '.[] | select((.description // "") + " " + ((.annotations // []) | map(.description) | join(" ")) | test("PATTERN"; "i")) | "\(.uuid[0:8]) \(.description[0:120])"'
The "i" flag makes the test case-insensitive. Drop it for case-sensitive matches.
When creating tasks (task add ...), aim for descriptions ≤ 100 chars. Long context goes in annotations:
# Good: short title, then annotate context.
task add project:foo +bug "OAuth token refresh fails with 401 after 24h"
# Output: "Created task 197." Use that int ID for follow-up annotate in the same shell session,
# or for scripting: NEW_UUID=$(task entry.after:now-1m +bug export | jq -r '.[0].uuid[0:8]')
task 197 annotate "Repro: lifecycle.test.ts:124. Happens only with refresh-token rotation enabled. Suspect cache key collision between user_id and session_id; see retro 2026-04-19."
# Bad: paragraph as description.
task add project:foo +bug "OAuth token refresh fails with 401 after 24h. Repro: lifecycle.test.ts:124. Happens only with refresh-token rotation enabled. Suspect cache key collision between user_id and session_id; see retro 2026-04-19. Affects all users on the canary deployment, blocks GA. Fix: split cache namespace by token_type."
Soft target. Existing bloated descriptions decay naturally as tasks are completed or edited; no retroactive cleanup is required.
task <filter> count returns a single integer, ~3 chars.task summary is a project-by-project rollup, very dense.task projects lists all project names with counts.task tags lists all tags with counts.| Need | Run |
|---|---|
| "What's pending in project X?" | task project:X dense |
| "How many open in project X?" | task project:X count |
| "What's on the backlog overall?" | task summary (then drill into a project with dense) |
| "What does task <uuid> look like?" | task <uuid> info (full metadata is fine for one task) |
| "What's the description of <uuid>?" | task _get <uuid>.description |
| "What about these 5 tasks?" | task A B C D E export \| jq -r '...' (space-separated UUIDs) |
| "Find tasks mentioning 'oauth'" | task export \| jq -r '.[] \| select(.description \| test("oauth"; "i")) \| ...' |
| "Add a task" | task add project:X +tag "short title ≤100c" then optionally task <uuid> annotate "context" |
| "Mark done" | task <uuid> done |
tools
--- name: writing-for-scannability description: Use when structuring prose so readers can skim it - drafting or restructuring READMEs, docs, PR or issue bodies, design docs, RFCs, or any long-form text where a wall of prose hides the structure. Also use when explicitly asked to make something scannable or skimmable, convert prose to a list, surface a buried list, fix a wall of text, or decide whether bullets or prose fit. Strong signal: text with parallel sentence shapes, contrast markers ("that
development
Ignore actually-lsp nudges for an ecosystem in this project. Use when the user wants to silence, dismiss, or ignore the LSP setup nudges for a specific ecosystem (Rust, TypeScript, Ruby), or invokes `/actually-lsp-ignore` directly. Writes `dismissed=true` to `.claude/actually-lsp.json`. Persistent across sessions for this project only.
tools
Diagnose and fix LSP setup for the current project's detected ecosystems (Rust, TypeScript, Ruby). Use when the SessionStart hook nudged about a missing LSP plugin, when the env isn't ready (no `bundle install`, no `cargo build`, missing server binary), when LSP calls are failing, or when the user invokes `/actually-lsp-doctor` directly. Walks the per-ecosystem state machine, reports what's missing, then runs the fix.
tools
--- name: investigating-runs description: Use whenever the user mentions a GitHub Actions / GHA run, even casually — invoke this skill before reaching for raw `gh` commands, because the bundled `gha-snapshot` helper distills `gh run view --log-failed` (a firehose) into a readable block with per-job status, failed-step log tails, and annotations. Specific triggers (any one is enough): a `github.com/.../actions/runs/...` URL; the phrase "GitHub Actions" or "GHA"; the `gh run` CLI; a failing workfl