A lightweight spec for AI agent task queues — the companion to AGENTS.md.
Website · Spec · Examples · MCP Server · Linter
AGENTS.md tells agents how to work. TASKS.md tells them what to work on.
Create a TASKS.md at your repo root:
# Tasks
## P0
- [ ] Fix authentication crash on token refresh
- **ID**: auth-fix
- **Details**: JWT refresh returns 500 on expired tokens
- **Files**: `src/auth/refresh.ts`, `src/middleware/auth.ts`
- **Acceptance**: Refresh works, tests pass, regression test added
## P1
- [ ] Add rate limiting to public API endpoints
- **Blocked by**: auth-fix
- [ ] Migrate database queries to prepared statements
## P2
- [ ] Update README with new API endpointsMost tasks are just checkboxes under priority headings. Tasks with dependencies get an ID so blockers can reference them stably. All metadata is optional.
Then add this to your AGENTS.md so agents know to use it:
## Task Management
- Read TASKS.md for available work before asking the user
- Claim tasks by appending (@your-name) before starting work
- Remove completed tasks from the file (history is in git log)That's it. Your agent will read TASKS.md on session start and work through the queue.
You think faster than agents can code. Ideas come in bursts — while an agent implements one feature, you've already thought of three more. Without a queue, those ideas live in your head or scatter across chat windows. TASKS.md is your buffer: write tasks down as they come, and agents work through them at their own pace.
Planning first leads to better results. When you write a task down — even a one-liner — you're forced to think about what you actually want before the agent starts coding. That small act of planning is the difference between an agent that builds the right thing and one that guesses. TASKS.md makes planning the natural first step, not an afterthought.
Zero friction beats any tool. Opening Jira to write a task takes you out of flow — you switch context, fill in fields, pick a project, assign a sprint. With TASKS.md, you add a line to a file that's already open in your editor. The lower the friction, the more likely you are to actually write tasks down — and written tasks are the whole point.
One Markdown file that any tool can read and write:
- Zero setup — No accounts, no APIs, no tokens. Create a file and start writing.
- In your editor — Add tasks without leaving your IDE. No browser tab, no context switch.
- Version-controlled — In git, next to the code. Every change is tracked.
- Agent-native — LLMs parse Markdown natively. No API client needed to read a file.
- Vendor-neutral — Works with any agent, any IDE, any CI system, today.
- Offline — Works on a plane. No server required.
- Plan — Write tasks under P0–P3 priority headings as ideas come to you
- Delegate — Agent reads the file, claims a task with
(@agent-name), implements it - Remove — Completed tasks are deleted from the file; history lives in git log
- Repeat — You keep adding tasks while agents keep working through them
You're always adding to the queue; agents are always draining it. No ideas get lost, and agents never run out of work.
The quality of your task description directly affects the quality of the agent's output. A task is a small contract between you and the agent — the more specific you are, the better the result.
A one-liner is fine for obvious work:
- [ ] Add input validation to the /users endpointAdd metadata when the task needs context:
- [ ] Fix race condition in WebSocket reconnect
- **Details**: When the server restarts, clients reconnect but sometimes
miss messages sent during the reconnect window. Add a sequence number
to messages and request missed messages after reconnecting.
- **Files**: `src/ws/client.ts`, `src/ws/server.ts`
- **Acceptance**: No dropped messages during server restart in integration testTips for writing tasks agents can actually complete:
- One session, one task — If it takes you more than a sentence to describe, it might be two tasks
- Include file paths — Agents explore faster when they know where to look
- Define "done" — An Acceptance field turns a vague ask into a testable outcome
- Use IDs for dependencies — If task B depends on task A, give A an ID and add
**Blocked by**: task-ato B. The agent will skip B until A is gone.
Priority: ## P0 through ## P3 — a widely-used scale (PagerDuty, Google SRE). P0 is "drop everything", P3 is "nice to have".
Tasks: Markdown checkboxes (- [ ]). Each task should be completable in a single agent session.
IDs: **ID**: kebab-case — stable identifiers for tasks that other tasks depend on. Don't rename once assigned.
Blockers: **Blocked by**: auth-fix, rate-limit — references task IDs across all files. A task is unblocked when the referenced IDs no longer exist in any file.
Tags: **Tags**: backend, auth — lowercase labels for filtering and routing to specialized agents.
Metadata: Optional nested fields — ID, Tags, Details, Files, Acceptance, Blocked by. Teams can add custom fields beyond these six.
Sub-tasks: Nested checkboxes under a parent. The agent who claims the parent owns all sub-tasks. Remove the entire block when done.
Multiple files: One root TASKS.md for small repos. Subdirectory files for monorepos. Split when a file exceeds ~50 tasks.
See the full specification for all rules and edge cases.
- Web application
- CLI tool
- Monorepo
- Multi-agent workflow
- Complex tasks — multiline details, rich acceptance criteria, sub-tasks with metadata
- Python API — FastAPI with SQLAlchemy, pytest, mypy, ruff
- Rust CLI — Cargo project with clippy, assert_cmd, crates.io publishing
- Mobile app — React Native with biometrics, offline sync, Detox E2E
The most useful thing about TASKS.md is a single command: "pick the next task and do it." Install the command for your agent, then type /next-task to start an autonomous work loop.
Auto-detect your agents and install the command:
npx @tasks-md/cli installOr copy manually into your project (commit it so your team gets it too):
| Agent | Install |
|---|---|
| Claude Code | cp -r commands/claude/skills/next-task .claude/skills/ |
| Codex | cp -r commands/codex/skills/next-task .agents/skills/ |
| Cursor | cp commands/cursor/next-task.md .cursor/commands/ |
| Gemini CLI | cp commands/gemini/next-task.toml .gemini/commands/ |
| Windsurf | cp commands/windsurf/next-task.md .windsurf/workflows/ |
All paths are project-local (inside your repo). See commands/ for source files and format details.
When you type /next-task, the agent runs a 6-step loop:
- Find — Discovers all
TASKS.mdfiles from the git root down - Pick — Selects the highest-priority unblocked, unclaimed task. Prefers tasks that unblock others (impact-first). Resumes previously claimed tasks if it finds its own
(@agent-id). - Claim — Appends
(@agent-id)to the task line so other agents skip it - Work — Reads the task's metadata, checks AGENTS.md for project conventions, makes changes, runs tests
- Complete — Removes the entire task block from TASKS.md, commits, pushes
- Loop — Reads TASKS.md again, picks the next task, continues until the queue is empty
You Agent
────────────────── ──────────────────
Write tasks as ideas come → /next-task
Add more tasks → Claims P0 task, starts working
Add more tasks → Completes task, picks next one
Review agent's commits ← Commits, removes task, loops
Add more tasks → ...keeps draining the queue
You're always adding to the queue. The agent is always draining it. This is the core loop — planning is your job, execution is the agent's.
The @tasks-md/cli provides task queue management — pick tasks, lint files, sync from issue trackers, and install agent commands.
npx @tasks-md/cli pick # pick highest-priority unblocked task
npx @tasks-md/cli init # create TASKS.md in current repo
npx @tasks-md/cli install # install /next-task for detected agents
npx @tasks-md/cli stats # queue overview and throughput
npx @tasks-md/cli lint TASKS.md # validate against specThe tasks-mcp server lets MCP-compatible agents (Claude Code, Cursor, Windsurf) manage TASKS.md files programmatically — list, pick, claim, unclaim, complete, and add tasks without file parsing.
{
"mcpServers": {
"tasks": {
"command": "npx",
"args": ["tasks-mcp"]
}
}
}The @tasks-md/lint CLI validates TASKS.md files against the spec — checks structure, priority ordering, ID format, duplicate IDs, dangling blocker references, and tag casing.
npx @tasks-md/lint TASKS.md # lint one file
npx @tasks-md/lint TASKS.md examples/ # lint multiple files/directories
npx @tasks-md/lint --fix TASKS.md # auto-fix (completed tasks, tag casing)Add one line to your CI workflow to validate TASKS.md on every push:
- uses: tasksmd/tasks.md/.github/actions/lint@mainSee .github/actions/lint/ for options.
They solve a different problem. Issue trackers are for team coordination — prioritizing features, tracking sprints, assigning across people, reporting to stakeholders. TASKS.md is for agent execution — a local, fast, file-based queue that agents read and write without API calls.
Key differences:
| Issue trackers | TASKS.md | |
|---|---|---|
| Audience | Product managers, teams | Agents, solo devs |
| Granularity | Features, bugs, epics | Implementation steps |
| Access | API calls, auth tokens | Read a file |
| Speed | Browser/API round-trip | Edit a line in your editor |
| Works offline | No | Yes |
| Agent can write | Needs API client + auth | Append to a file |
| Git-native | Separate system | Same repo, same PR |
They complement each other — one Jira ticket or GitHub Issue often becomes multiple TASKS.md entries. Use the tracker for what to build; use TASKS.md for how the agent builds it.
Absolutely — that's the expected setup for teams. The issue tracker is your source of truth for product work. When you pick up an issue, break it into implementation steps in TASKS.md and let the agent execute them. The agent doesn't need access to your tracker; it just needs the file.
## P1
- [ ] Implement user profile page (PROJ-142)
- **Details**: See Jira PROJ-142 for designs. Build the profile
page with avatar, bio, and settings link.
- **Files**: `src/pages/profile.tsx`, `src/api/user.ts`
- **Acceptance**: Page renders, matches Figma, tests passTODO.md has no spec and thousands of incompatible formats in the wild. A "todo list" is a human wish list; a "task queue" is an active work queue for agents. The naming fits the emerging pattern: AGENTS.md (instructions), TASKS.md (work queue).
Migration: mv TODO.md TASKS.md, add P0–P3 headings, convert to checkboxes.
No. A solo developer with one agent benefits from persistent context across sessions. You write tasks, the agent works through them. An orchestrator helps when you have multiple agents, but it's not required. TASKS.md is intentionally simple enough that any agent can use it without special tooling.
Each agent claims a unique task (different line). Git auto-merges deletions on non-adjacent lines. Conflicts are rare and trivial.
As detailed as needed for the agent to succeed without asking you. A one-liner works for obvious changes (Add input validation to /users). For anything ambiguous, add Details, Files, and Acceptance so the agent knows what to do, where to look, and when it's done.
Yes. It works as a personal backlog for any developer. The format is just prioritized Markdown checkboxes — you don't need an agent to benefit from writing tasks down before starting work. The planning habit alone improves outcomes.
Break them into sub-tasks or split them into separate tasks with dependencies. If a task takes more than one sentence to describe, it's probably two tasks. Use Blocked by to order them:
- [ ] Set up auth database schema
- **ID**: auth-schema
- [ ] Implement JWT token refresh
- **Blocked by**: auth-schemaThe agent should tell you it's stuck and move on to the next task. The stuck task stays in the queue with its (@agent-id) claim. You can either add more detail to help the next attempt, or remove the claim so another agent (or a fresh session) can try.
Yes — that's what the claiming mechanism is for. Each agent appends (@agent-id) to the task it picks up. Other agents see the claim and skip to the next unclaimed task. In multi-agent setups, agents should commit and push claims immediately to avoid races.
No. Remove them. Git log is your history. Keeping completed tasks in the file adds noise and makes it harder for agents to scan the queue. The spec enforces this — @tasks-md/lint will flag checked-off tasks as errors.
They're companions. AGENTS.md tells agents how your project works (build commands, conventions, architecture). TASKS.md tells agents what to work on (prioritized queue). Together, an agent can start a session, read both files, and be immediately productive — no human prompting needed.
- Why Your AI Agent Needs a Backlog — the motivation behind TASKS.md
- AGENTS.md — the companion spec for agent instructions
- Proposal: TASKS.md as a companion standard — discussion on the agents.md repo
All four npm packages share a single version and are published together:
| Package | npm |
|---|---|
@tasks-md/parser |
|
@tasks-md/lint |
|
@tasks-md/cli |
|
tasks-mcp |
- Go to GitHub Releases → New
- Create a new tag with a
vprefix (e.g.v0.2.0) targetingmain - Add release notes (GitHub can auto-generate them)
- Click Publish release
Or from the CLI:
gh release create v0.2.0 --generate-notesThe publish workflow runs automatically and:
- Syncs all
package.jsonversions to match the tag - Builds and runs the full test suite
- Publishes all 4 packages to npm in dependency order
- Commits the version bump back to
main
The workflow requires an NPM_TOKEN secret:
- Create an npm Automation token at npmjs.com/settings → Access Tokens (automation tokens bypass 2FA/OTP)
- Add it as a repository secret named
NPM_TOKENat Settings → Secrets → Actions
If you need to publish without a GitHub Release (e.g. from a local machine):
npm adduser # authenticate once
scripts/sync-versions.sh 0.2.0 # bump all package versions
scripts/publish-all.sh # publish in dependency order (requires OTP)We track work in our own TASKS.md. Contributions welcome:
- Improve the specification
- Add examples for your stack
- Add or improve agent commands for your tool
- Report bugs or suggest features via GitHub Issues