---
name: unblock
user-invocable: false
description: Plan and track a project as a dependency graph in .github/unblock.json. Use when starting a project or feature, when deciding what to work on next, when finishing a task, or when the user asks about progress, the plan, the roadmap, what's blocked, or "what should I do now".
---

# Unblock: a dependency-graph plan that lives in the repo

The plan for this repository is a JSON file, `.github/unblock.json`: tasks, what each one waits on, and their status. You keep it current with a small CLI as you work. The user only looks at it, as a graph: never ask them to update the plan, file tasks or change statuses. That's your job, and it should cost them nothing.

Source: https://unblock.esh.sh/skill.md. To keep this skill, save this file as `.claude/skills/unblock/SKILL.md` in the project, or `~/.claude/skills/unblock/SKILL.md` for every project.

## Setup

**The CLI, once per machine.** It is a single Python 3.8+ file with no dependencies:

```bash
curl -fsSL https://unblock.esh.sh/install.sh | sh -s -- --no-modify-path
```

On Windows, in PowerShell: `irm https://unblock.esh.sh/install.ps1 | iex` (it adds its folder to the user's PATH itself). Both check the download against the release's sha256.

Then check `command -v unblock`. If it finds nothing, `~/.local/bin` isn't on the PATH: ask the user to add it (`echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc`, or their shell's profile), because Claude Code's hooks, the status line and the side pane run `unblock` by name. Until then, run it as `python3 ~/.local/bin/unblock`.

**A board, once per repository.** Check for `.github/unblock.json` first (`unblock where` says which plan unblock would use, and why). A folder can also keep several plans as `<name>.unblock.json`: `unblock list` shows them, `unblock use <name>` picks one for the folder, `unblock new <name>` starts another, and `-f <file>` names any file, in a git repository or not. If unblock says a folder keeps several plans, ask the user which one this work belongs to rather than guessing. If there isn't a board:

1. If the repository already keeps a plan in another tool (`.taskmaster/`, `backlog/tasks/`, `specs/*/tasks.md`, `.kiro/specs/`), run `unblock import` and continue from step 3. If the user tracks the work in GitHub issues, `unblock import github --repo owner/repo` does the same from the issues. Otherwise `unblock init --draft` starts the board from the checklists in the README and TODO files and the TODO comments in the code (an empty board if there are none).
2. Draft the plan (or finish the draft) from what the user asked for, the README and the code: 5 to 30 tasks. A draft has no links yet, so add them. Group related tasks under a parent, and link each task to what really has to be finished first. Use the CLI (`unblock add "Title" --parent <group> --depends-on a,b`) or edit the JSON directly.
3. Mark work that already exists as `done`.
4. Run `unblock init --agents` so every agent that opens the repo follows this loop (it writes a short block into AGENTS.md and CLAUDE.md), then `unblock init --git` so branches merge the board task by task.
5. Run `unblock validate`, show the user `unblock` (the summary), and commit the files.

## The loop

```bash
unblock prime                # at the start of a session: progress, in flight, what to pick up
unblock now                  # the one task to do now, and why
unblock next --json          # ready tasks, most unblocking first
unblock claim <id>           # before starting: marks it doing, and other agents skip it (on a branch named for it, claude/<id>, the id can go)
# ...do the work, test it...
unblock set <id> done --note "what you decided"   # prints what just became ready
```

- Before starting work, run `unblock next` and pick from the top unless the user asked for something specific.
- To hand a task to another agent or session, `unblock prompt <id>` prints the instructions to give it (claim, branch, pull request with `done: <id>`).
- Point the user at the board at three moments: starting a task, finishing one (`unblock point <id> --note "done; now ready: …"`), and being stuck on them. Not after every edit.
- Waiting on the user (a question, an approval)? `unblock heartbeat --state needs-you` makes your task stand out on their board. In Claude Code, the hooks from `unblock init --agents` (or the Unblock plugin) do this for you.
- Mark a task `done` only when it is finished and verified. In the commit or pull request that finishes it, add a line `done: <id>`: the GitHub Action (`unblock init --ci`) marks it done when the change reaches the default branch, in case you didn't.
- When you discover new work, add it as a task with the right links rather than doing it silently: `unblock add "Title" --parent <group> --depends-on <id>`.
- When something blocks you, add the missing prerequisite (`unblock add ...`, then `unblock link <task> <prereq>`) instead of guessing.
- Commit the board together with the code change it describes, so the history shows progress. `unblock diff` shows what your uncommitted change does to the plan; `unblock diff main...HEAD` shows the whole branch.
- When several agents or branches edit the plan, run `unblock init --git` once per clone: git then merges the board task by task.
- `unblock init --ci` adds a GitHub Action that comments that diff on every pull request. Suggest it when the user works with pull requests.
- Ask before deleting tasks you didn't create, or before rewriting the plan.

### Working with the person

The plan should make you a better collaborator, not a bookkeeper. In conversation:

- **Keep their focus.** When they ask for something new in the middle of a task, add it to the plan after the current task (`unblock add "Title" --depends-on <current>`) and say so in one line ("Added *Rate limits* after this task, so we stay on auth-api"), unless they say it's urgent.
- **Check what it waits on.** Before starting a task, look at what it waits on (`unblock show <id>`). If something open is in the way, say so and offer to do that first.
- **Say what it unblocked.** When you finish a task, end with one line: what became ready and the next task, and offer to start it ("That unblocked billing, which unblocks 3 more. Want me to go on?"). The `▸ Unblock` line `set` prints is made to be passed on as it is.
- **Say why this one.** When you pick a task yourself, give the reason in a sentence: it's ready, and how much it unblocks (`unblock now` says it).

Other commands: `unblock lint` (plan mistakes worth a look), `unblock show <id>` (readiness, what to do next to unblock it, links), `unblock edit <id> --title/--notes/--estimate/--assignee/--tags/--parent`, `unblock tree`, `unblock report` (everything as JSON), `unblock link <task> <prereq>`, `unblock unlink <task> <prereq>`, `unblock rm <id>`, and `unblock next --in <group>`.

**MCP instead of the CLI:** if the Unblock MCP server is connected (`claude mcp add unblock -- python3 ~/.local/bin/unblock mcp`), use its tools (`next_tasks`, `update_task`, `add_task`, `point`, …) for the same loop. With several plans in a folder, `list_boards` shows them and every tool takes the plan's name as `board`.

## The format

```json
{
  "format": "unblock/v1",
  "title": "My project roadmap",
  "tasks": [
    { "id": "backend", "title": "Backend", "dependsOn": [] },
    { "id": "auth-api", "title": "Auth API", "status": "doing", "parent": "backend",
      "dependsOn": ["db-schema"], "estimate": 3, "notes": "- [ ] magic links\n- [ ] sessions" }
  ]
}
```

- `id`: unique and stable. Never rename ids; links and parents refer to them.
- `status`: `todo`, `doing` or `done`. Only on tasks without children. Ready and blocked are worked out, never stored.
- `parent`: the group a task sits in. Any task with children is a group.
- `dependsOn`: ids that must be done first. A group id means everything inside it. A group's own `dependsOn` applies to all of its tasks.
- `estimate` (optional, default 1) weights the percentages. `assignee`, `tags` and `notes` (Markdown; `- [ ]` checklists count as progress) are optional.
- No dependency loops, and a task can't wait on its own group. `unblock validate` checks all of this.

## Showing the user

- **While working locally:** `unblock view` opens the board in the browser. Edits there save to the file, and your changes to the file show up within a second. It keeps running, so start it in the background or suggest the user runs it.
- **After pushing:** https://unblock.esh.sh/gh/OWNER/REPO shows the board from GitHub. Private repositories ask for a read-only GitHub token the first time.
- **Quick look in the terminal:** `unblock` prints progress, what's in flight, what to start next and the critical path.
- **Point at what you mean:** `unblock point <id> [<id>...] --note "Starting this: it unblocks the launch"` rings those tasks in the open viewer and shows your note beside them. Use it when you start a task, when you add tasks, and when you explain what blocks something. With the MCP server, the `point` tool also shows the board, highlighted, right in the chat. For a pushed board, link to `https://unblock.esh.sh/gh/OWNER/REPO#point=<id>,<id>&note=<text>`.
