# Working with agents (/docs/guides/agent-workflow)



The CLI is usable by a person, but it exists because agents need somewhere to put state
that outlives a context window. This page is what you would tell an agent — and, via the
bundled skill, what it gets told automatically.

## The skill [#the-skill]

`tasks init` installs an [agent skill](https://agentskills.io) into
`.claude/skills/tasks/`. It is what makes an agent reach for this CLI on its own rather
than inventing a `TODO.md`: it documents when persistent tasks beat a session checklist,
the claim-before-working loop, the invariants, and the JSON contract.

```sh
tasks skill install [--force]
```

Add it to a project that predates it, or refresh it after upgrading the CLI, with the
command above. An existing install is **left alone** unless `--force`, so local edits
survive an accidental reinstall.

Commit `.claude/skills/tasks/` so every agent working in the repo gets it.

<Callout title="Skip it if you want">
  `tasks init --no-skill` scaffolds `.tasks/` without touching `.claude/`.
</Callout>

## The loop [#the-loop]

```sh
tasks ready --json                  # unblocked and available right now
tasks claim <id>                    # take it — records who and when
# ...do the actual work...
tasks status <id> DONE              # complete it
```

Two things about this that are easy to get wrong:

**Claim before working, not after.** The claim is what stops a second agent starting the
same task, and it is only useful if it happens first. Claiming on the way out records
history but prevents nothing.

**Update status as you go, not in a batch at the end.** A session that dies mid-task
leaves a claimed task, which is recoverable — you can see who had it and when. A session
that dies leaving everything `PENDING` loses the work record entirely.

## Before anything else [#before-anything-else]

```sh
tasks list --json
```

Exit `3` with `No .tasks/ directory found` means the project is not set up. &#x2A;*Ask before
running `tasks init`** — creating it is a project-level decision, not a side effect of a
question about work.

If `tasks` is not on `PATH`, use `npx @speekl/tasks` in place of `tasks` everywhere.

## Rules that will bite [#rules-that-will-bite]

<Accordions>
  <Accordion title="Never hand-edit frontmatter">
    Every field has a command: `status`, `claim`, `release`, `dep add` / `dep rm`, `move`.
    Editing the YAML directly bypasses the transition and dependency checks that make the
    tree trustworthy.

    The body below the frontmatter is yours — edit it freely with normal file tools. That is
    in fact the expected way to write a task: `tasks new` scaffolds the headings and returns
    a `path`; you open that path and fill it in.
  </Accordion>

  <Accordion title="Never `mv` a task file">
    The directory determines the phase. Use `tasks move <id> <phase>`, which relocates the
    file, rewrites the frontmatter, and cleans up an emptied phase directory.
  </Accordion>

  <Accordion title="No status skipping">
    By default a task steps forward or back exactly one status, so `PENDING → DONE` is
    rejected with exit `4`. Claim it first. The error's `details.allowed` tells you what is
    legal from where you are.
  </Accordion>

  <Accordion title="`tasks status <id> IN_PROGRESS` is refused">
    Exit `2`, redirecting to `tasks claim`. This exists so claim metadata can never be
    bypassed — anything that reaches the claimed status has a recorded owner and timestamp.
  </Accordion>

  <Accordion title="A CONFLICT on claim means someone else has it">
    Pick a different task from `tasks ready`. Do **not** reach for `--force`.

    `--force` on claim overrides *dependencies*, not other people's claims. Only use it when
    the user has explicitly said the dependency does not apply.
  </Accordion>

  <Accordion title="The last status is not terminal">
    To reopen a completed task, `tasks claim <id>` steps it back from `DONE`. Agents mark
    things done in error; this is the supported recovery, not a hack.
  </Accordion>

  <Accordion title="Status names are project-configurable">
    `PENDING` / `IN_PROGRESS` / `DONE` are defaults, not guarantees. A project may use
    `[TODO, DOING, REVIEW, SHIPPED]`. Read the real list from `.tasks/config.yml`, or from an
    error's `details.statuses`, rather than assuming.
  </Accordion>
</Accordions>

## Creating tasks well [#creating-tasks-well]

```sh
tasks new <phase> <name...> [--id <id>]
tasks dep add <id> <ref>...
```

The phase directory is created on first use. The id defaults to a kebab-case slug of the
name and must be unique across the whole tree, including `.archive/`.

**Write the body after creating.** `tasks new` scaffolds `## Description`,
`## Acceptance criteria`, and `## Notes`; fill them in with a normal file edit at the
`path` returned in the JSON. A task whose body is empty is not a task, it is a reminder.
Put enough in it that a different agent arriving cold in a later session can do the work
without asking.

**Prefer several small dependent tasks over one large one.** `depends_on` is what makes
`tasks ready` meaningful; a single task with a ten-item checklist in its body tells the
next agent nothing about what is unblocked.

## Parsing output [#parsing-output]

Use `--json` whenever you parse. The plain tables are for humans and their layout is not
stable. See the [JSON contract](/docs/reference/json-contract) for shapes and codes.

```sh
id=$(tasks ready --json | jq -r '.tasks[0].id // empty')
[ -n "$id" ] && tasks claim "$id"
```

Without `-p` / `-a`, `list` and `ready` scope to `active_phase` from `.tasks/config.yml`
when it is set. &#x2A;*If a task you expect is missing, retry with `--all`** before concluding
it does not exist.

## When something looks wrong [#when-something-looks-wrong]

Run `tasks validate --json` before hand-repairing anything. It reports id/filename
mismatches, phase drift, unknown statuses, broken or cyclic dependencies, and a stale
`_index.md`. `--fix` repairs the mechanically safe subset and leaves the rest for a
human.

Do not fix drift by editing YAML. See [troubleshooting](/docs/guides/troubleshooting).
