yakanban
Let your AI agents work the board your team already uses. No migration, no second UI, nothing for your product owner to learn.
Your coding agent just finished a task. Nobody else knows: no ticket moved, no claim taken, and the agent in your other terminal is about to start the same bug.
Give them the board your team already has. The one below is this project's own, captured when this page was built: yakanban is built with yakanban, and its board is public.
brew install aramponi/tap/yakanbanscoop bucket add aramponi https://github.com/aramponi/scoop-bucket
scoop install yakanbango install github.com/aramponi/yakanban/cmd/yakanban@latestVERSION=$(curl -fsSL https://api.github.com/repos/aramponi/yakanban/releases/latest | grep -m1 '"tag_name"' | cut -d'"' -f4)
OS=$(uname -s | tr '[:upper:]' '[:lower:]'); ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -fsSL "https://github.com/aramponi/yakanban/releases/download/$VERSION/yakanban_${VERSION#v}_${OS}_${ARCH}.tar.gz" | tar xz yakanbanThe clip above is a recorded session on a demo repository. Every other terminal block on this page is captured output, from the board this project runs on.
$ yakanban board --refresh
yakanban (github)
https://github.com/users/aramponi/projects/2
COLUMN TASKS WIP BLOCKED CLAIMED
Backlog 0 — — —
Todo 1 — — —
In Progress 1 — 1 —
Review 0 — — —
Done 25 — — —
total 27 blocked 1 overdue 0
priorities medium 20 high 7
What changes for your team: nothing
Most projects that need a board have one. So setup adopts rather than provisions: point yakanban at the project the team is already using and it takes the columns as it finds them, adding only the fields it needs to store a priority, a claim and a dependency. Nothing is renamed, nothing is deleted, and the product owner's view does not change under them.
Old issues are left alone too. An issue that was never put on the board is still readable, and the first write is what puts it there, so a repository with years of history joins one ticket at a time, as work actually reaches it, instead of arriving as a migration nobody asked for.
The greenfield case is the same command with nothing to adopt: it creates the project, links it to the repository and gives it the default columns.
Two kinds of user, one board
A developer and an agent work in a terminal. A product owner works in a browser, on the project board they were already using. Neither of them is looking at a copy, and nobody had to install anything to see the other's work.
yakanban is a client, not a store. Every write goes straight to the tracker; reads pass through a one-minute cache so an agent can poll a board without spending someone's API quota. There is no state on disk to reconcile, and nothing to lose when a laptop dies.
That has a price, and it is worth saying out loud: without a network you have no board. Offline was traded for never being wrong about who owns what.
The claim is the whole trick
Two agents pointed at the same backlog will pick the same ticket. A claim is a soft lock, an agent name and an expiry written into two ordinary project fields, so the second agent is refused and goes to find other work.
Ordinary fields, because a human has to be able to see them. Open the project in the browser and the claim is a column like any other: who holds what, and until when. Nothing about the coordination is invisible to the person who did not install anything.
Expiring, because an agent that dies mid-task must not take a ticket with it. The lock lets go by itself after an hour, and a long task renews it by writing a progress note.
pick claims the next available ticket and reads the claim back before returning it. That read is the reason several agents on one board is safe: list-then-claim checks nothing, because the answer is stale by the time it arrives.
$ yakanban list --unclaimed --not-blocked --status todo
ID STATUS PRIORITY TITLE TAGS ASSIGNEES FLAGS
34 Todo medium {{.Repo}} in branch and worktree templates is the checkout'… bug
The instructions ship with the binary
The hard part of handing a board to an agent is not the API, it is telling it when to claim, when to park a task and when to stop and ask. Two skill files carry that, and they are compiled into the binary, so a downloaded release installs them with no checkout. An agent that reads them drives a ticket the same way as every other agent on the repository, which is the difference between a board and a log of what one assistant felt like doing.
skill install detects Claude Code, Codex, Cursor, Gemini CLI, Antigravity, Hermes, Pi and OpenClaw, shows you what it found and lets you change its mind before it writes anything. A file you have edited since is never overwritten without --force: each one carries a hash of the text yakanban wrote, so "stale" and "you changed this" are different answers.
One shape for every command
Fifteen commands, and every one of them takes the same global flags. There is no command that prints JSON and another that does not.
| Command | What it does |
|---|---|
init | Provision the board and write the descriptor |
list | List and filter tasks |
show ID | Full detail of one task |
create TITLE | Open an issue and put it on the board |
edit ID | Change fields, claim, block, append to the body |
move ID STATUS | Change column (--next / --prev also work) |
pick --claim AGENT | Claim the next available task, atomically |
branch ID | Create a branch and attach it to the ticket |
handoff ID --claim AGENT | Park a task with a note for whoever picks it up |
delete ID --yes | Close the issue and archive its board item |
board | Column counts, WIP pressure, blocked and overdue |
sync | Drop the read cache and refetch |
config | Show the resolved configuration |
agent-name | Generate a claim identifier |
skill install | Install the bundled agent skills |
Output you can pipe, exit codes you can branch on
--compact prints one tab-separated line per record, which is what you want when the reader pays by the token. --json prints the whole record when it does not. Both are on every command, alongside --table, --no-color, --refresh and --no-cache.
The exit codes are the part a script actually reads. Code 5 in particular is not an error: it means another agent got there first, and the correct response is to pick something else.
$ yakanban list --compact -n 8
1 Done high Add `yakanban skill install` to install the bundled agent skills #enhancement
2 Done medium Link a branch to a task with GitHub linked branches #enhancement
3 Done medium Support branching models (trunk-based, git flow) for branch and worktree naming #enhancement deps:2
4 Done medium Let `skill install` propose the detected agents instead of deciding, and support hermes and pi #enhancement deps:1
5 Done high Exercise the release build in CI before the first tag depends on it #enhancement
6 Done medium Publish a Homebrew tap so yakanban installs with brew #enhancement deps:5
7 Done high Decide how GitLab maps onto the yakanban model, tier by tier #enhancement
8 Done medium Resolve provider capabilities at runtime instead of compile time #enhancement
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | unexpected error |
| 2 | bad input or missing configuration |
| 3 | task not found |
| 4 | authentication or scope problem |
| 5 | the task is claimed by another agent |
| 6 | the provider cannot express what you asked for |
It borrows the login you already have
yakanban stores no credentials. It reads $YAKANBAN_GITHUB_TOKEN, $GH_TOKEN or $GITHUB_TOKEN, and when none of them is set it asks the GitHub CLI for the token you are already using: gh auth token. So gh is a prerequisite for the ordinary path, and the error says so rather than failing somewhere deeper.
That is the whole authentication story, and it is deliberately not ours. There is no login command, no keychain entry and no token file to leak, because the credential belongs to a tool that already manages it, and in CI, where gh is not the natural answer, one environment variable takes over.
One thing will catch you once. Projects v2 sits behind its own OAuth scope, so an account that authenticated with gh before ever touching projects needs it added:
gh auth refresh -s project
Both cases, no credential at all or a credential without the scope, exit 4, and in both the message is the instruction:
$ yakanban board --refresh
yakanban: authentication failed: `gh auth token` failed: no oauth token found for github.com (run `gh auth login`)
$ echo $?
4
What we didn't build
No second web UI
The tracker has one, and the people who do not use a terminal are already logged into it. A web UI of our own would be a second place for the board to be wrong.
No local mirror of the board
Tempting, and it would have bought offline reads. It would also have bought a sync direction, a conflict policy and a class of bug where the agent and the human disagree about reality. The cache holds reads for a minute and forgets them; that is the whole of it.
No release automation, and no git flow back-merge
branch creates branches and links them to tickets. It stops there. Cutting releases is release engineering, and a back-merge that a tool silently skips is how a hotfix gets lost. yakanban tells you the back-merge is yours and leaves it undone rather than half done.
No merge request management
Pull requests belong to the forge, which already reviews them, checks them and merges them. Wrapping that in another CLI would add a layer whose only job is to be out of date.
delete closes; it does not destroy
yakanban delete closes the issue and archives the board item. The history stays. An agent with a shell and a bad afternoon should not be able to permanently remove anybody's work.
No published gh extension
The same binary answers to gh yakanban ... when it is installed under the name gh-yakanban, and make gh-extension builds it. It is not shipped in releases: gh extension install resolves a *repository* whose name begins with gh-, and this one does not, so a published binary could never be installed the documented way. It would have been dead weight in every release.
No provider abstraction written before the second provider
The domain knows nothing about GitHub, and a backend is one package plus one line in a registry. That is as far as the abstraction goes today, because the shape of the second one is not knowable from the first.
For agents
The site serves an llms.txt at its root: the command reference, the exit codes, the two skill files and the backend mapping, as links an agent can follow instead of a page it has to scrape.
It is generated from the same sources as this page. A hand-written contract is the first file to go stale, and a stale contract is worse than none.