{"id":"0a9c936f-9cc7-48d0-99c3-16c814ef4295","slug":"arnochenfx-agent-kanban","kind":"skill","name":"agent-kanban","summary":"agent-kanban is a project-scoped task board that lets multiple AI agent sessions share one source of truth, and pick up each other's work when they crash.","owner":"ArnoChenFx","repoUrl":"https://github.com/ArnoChenFx/agent-kanban","license":"MIT","licenseTier":"permissive","trust":"community","stars":0,"match":null,"fewStars":true,"format":"skill","createdAt":"2026-09-26T08:00:31.000Z","addedAt":"2026-10-21T13:01:07.591Z","logoUrl":"https://avatars.githubusercontent.com/u/24894259?u=131fa66d05531fb74426f02cb96f6956de4a3294&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["productivity"],"tags":["agent","agentic-workflow","kanban-board"],"runtime":"node","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T11:51:35.169Z","sample":false,"url":"https://conduits.dev/tools/arnochenfx-agent-kanban","language":"TypeScript","transports":["stdio"],"install":{"npm":"agent-kanban"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# agent-kanban\n\n[English](README.md) · [中文](README-zh.md) · [For developers](Develop.md) · [开发者文档](Develop-zh.md)\n\nA project-scoped task board that lets multiple AI agent sessions share one source of truth — and pick up each other's work when they crash.\n\n---\n![Screenshot](docs/images/screenshot.png)\n![Task](docs/images/task.png)\n\n## The problem\n\nYou are running several coding agents against one repository. Maybe you run a main session plus a few subagents. Maybe an agent dies halfway through a task: a network blip, an OOM kill, a closed laptop, a `SIGKILL` from a supervisor.\n\nWhat usually survives is work in a terminal scrollback that nobody can read later:\n\n- Agent A was halfway through rewriting the auth layer. That context lived only in Agent A's prompt window.\n- Agent B sees a task titled \"fix auth\" and starts from zero, or worse, starts *conflicting* with A.\n- Nobody knows which of the three open branches is stale.\n- The task list lives in a TODO file, the reasoning in the agent's output, the status in your head.\n\nEvery restart is archaeology.\n\n## What agent-kanban does\n\nIt gives each agent a **lease on a task**, an **append-only event log**, and a **handoff protocol** — so a new session can start with the previous session's actual context instead of a task title.\n\n```\n┌──────────┐   claim    ┌───────────┐   write     ┌──────────┐\n│ session  │ ─────────► │   task    │ ──────────► │ progress │\n│    A     │            │ T-0007    │            │ 60%, ... │\n└────┬─────┘            └─────┬─────┘            └────┬─────┘\n     │                        │                       │\n     │  lease expires, or agent crashes              │\n     ▼                        ▼                       ▼\n┌────────────────────────────────────────────────────────────┐\n│  event log  (append-only, the single source of truth)      │\n└────────────────────────────────────────────────────────────┘\n     │                        │\n     │  handoff               │  rebuild\n     ▼                        ▼\n┌──────────┐            ┌──────────┐\n│ handoff  │ ─────────► │ session  │  agent-kanban resume T-0007\n│ note     │            │    B     │  → gets the handoff + timeline\n└──────────┘            └──────────┘\n```\n\nFour ideas carry the whole design:\n\n**1. Task ownership is a lease, with an expiry.**\n`agent-kanban task claim T-0007` gives the current session a time-boxed lease (15 min by default). Progress and checklist items are preserved. When the lease runs out, whether the agent crashed, was killed, or just walked away, the server reclaims the task automatically. The next `context` call tells the new session the card is up for grabs. Nobody has to run a cleanup script.\n\n**2. The event log is the source of truth; the board is a projection.**\nEvery state change is an event: `task_created`, `task_progress`, `handoff_created`, `plan_superseded`. The `tasks` and `plans` tables are derived. That means `agent-kanban rebuild` can recompute the entire board from the log and diff it field by field against what's stored:\n\n```console\n$ agent-kanban rebuild\nagent-kanban  replayed 412 events (23ms)\n  Recomputed: tasks 18 · deps 6 · plans 4 · handoffs 5\n\n✗ found 4 drifts (the stored projections ≠ the result recomputed from the events)\n  tasks.T-0012 field status\n    stored:     todo\n    recomputed: doing\n  ... and 3 more\n  Fix: agent-kanban rebuild --write --force\n  (--write overwrites tasks/plans/handoffs/task_deps with the event stream)\n\n$ agent-kanban rebuild --write --force\nagent-kanban  replayed 412 events (19ms)\n  Recomputed: tasks 18 · deps 6 · plans 4 · handoffs 5\n\n✓ the projections match the event stream exactly\n  the projections have been overwritten with the recomputed result\n```\n\nIf the log and the projection ever disagree, you find out then, rather than six weeks later.\n\n**3. Handoffs are written by the agent, not generated by the system.**\nWhen a session stops, it writes a handoff: what's done, what's next, what's blocked, what open questions remain. `agent-kanban resume` shows that first. Crash-generated handoffs exist as a fallback, and they explicitly say \"the previous holder is unreachable\", so you never mistake a dead session's guesses for a live one's instructions.\n\n**4. Plans are versioned rather than overwritten.**\n`agent-kanban plan save` always creates a new version and marks the old one `superseded`. `agent-kanban plan history` shows the chain; `agent-kanban plan at --ts` shows what was in effect at any moment. When a task is done and someone asks \"why did we do it this way?\", the answer is still there.\n\n## Quick start\n\n### Option A — one-line install (no runtime required)\n\n```bash\n# macOS / Linux\ncurl -fsSL https://raw.githubusercontent.com/ArnoChenFx/agent-kanban/main/install/install.sh | bash\n```\n\n```powershell\n# Windows (PowerShell)\nirm https://raw.githubusercontent.com/ArnoChenFx/agent-kanban/main/install/install.ps1 | iex\n```\n\nThe script detects your platform, downloads the matching binary, installs it to a\nper-user directory (`~/.local/bin`, or `%LOCALAPPDATA%\\Programs\\agent-kanban` on\nWindows) and adds it to your PATH. No administrator rights, no `sudo`. Re-run the\nsame command to upgrade.\n\n```bash\nagent-kanban init                      # create a board in the current directory\nagent-kanban install-protocol          # teach your agents to use the board\nagent-kanban serve                     # → http://127.0.0.1:7788/\n```\n\nPin a version with `AGENT_KANBAN_VERSION=0.1.5`. There is no Linux arm64 binary;\nthe script tells you so and points you at Docker, which is multi-architecture.\n\n**Prefer to download by hand?** The binary is self-contained (the Bun runtime, the\ndatabase schema and the whole web UI are embedded). Get it from\n[Releases](https://github.com/ArnoChenFx/agent-kanban/releases):\n\n```bash\nchmod +x agent-kanban-linux-x64        # macOS: agent-kanban-darwin-arm64 · Windows: agent-kanban-windows-x64.exe\n./agent-kanban-linux-x64 init\n./agent-kanban-linux-x64 serve\n```\n\nTo upgrade later, run `agent-kanban update` (or `--check` first to peek). It fetches the latest release, verifies it against the published `.sha256`, replaces the binary in place, and keeps the previous one as `<name>.old`. When running from source or in Docker, it prints the matching upgrade path instead.\n\n### Option B — from source\n\nRequires [Bun](https://bun.sh) ≥ 1.4.2.\n\n```bash\nbun install\nbun run build:binary            # build frontend, embed assets, compile\n./dist/agent-kanban init\n./dist/agent-kanban serve\n```\n\n### Option C — Docker\n\n```bash\ncp .env.example .env\n# Set KANBAN_ADMIN_TOKEN — generate one: openssl rand -hex 16\ndocker compose up -d\n```\n\nThe image does **not** ship a compiled binary. It runs the TypeScript sources directly on `oven/bun`, so there is no build step in the image and only one copy of the Bun runtime.\n\nCompose pulls the published image from **GitHub Container Registry** — releases push to GHCR only, never to Docker Hub. The image reference is pinned in `docker-compose.yml`; edit that one line to use a different tag or namespace. `KANBAN_BIND` and `KANBAN_PORT` control how far the server is reachable and which host port it binds.\n\n## Daily use\n\n```bash\n# 1. Start a session (registers who you are)\nagent-kanban session start --agent pi-main --harness pi\n\n# 2. Read the situation — this is the first thing you should do\nagent-kanban context\n#    → your handoffs, what you're working on, what you could claim\n\n# 3. Claim a task (acquires the lease)\nagent-kanban task claim T-0007\n\n# 4. Work, and keep the board honest as you go\nagent-kanban task progress T-0007 --pct 60 --note \"Storage layer rewritten\"\nagent-kanban task check T-0007 --item \"Add migration test\"\n\n# 5. Stuck? Say so, and the lease is released\nagent-kanban task block T-0007 --reason \"Waiting on API key from ops\"\n\n# 6. Stepping away? Leave a real handoff\nagent-kanban handoff --task T-0007 \\\n  --summary \"Auth rewrite done, token refresh left\" \\\n  --next \"Start from refreshToken(); see plan v2\" \\\n  --blockers \"Needs staging API key\" \\\n  --open \"Rotate refresh tokens on every use?\"\n\n# 7. Done\nagent-kanban task done T-0007 --note \"Tests green\"\nagent-kanban session end\n```\n\n### Coming back after a crash\n\n```bash\nagent-kanban context           # what's mine, what's free, who crashed\nagent-kanban resume T-0007     # take over: handoff + full timeline injected\n```\n\nYou get the previous session's notes, the ordered list of every change made to the task, and the current plan version — not a blank card titled \"fix auth\".\n\n### Several agents in one directory\n\nEach agent gets its own identity automatically, so two agents in the same checkout stay separate\nwithout exporting anything:\n\n```bash\n# terminal 1                      # terminal 2\nagent-kanban session start --agent pi-main --harness pi\nagent-kanban context              # agent-b: same thing, separate session\n```\n\nThe identity comes from a per-session variable the agent harness already exports, and lands in\n`.kanban/sessions/<key>`, one file per agent. The old single `.kanban/session` file is used only by\nprocesses that have no such variable, so a keyless shell never borrows someone else's identity.\n\n| Agent tool | Variable it exports |\n|---|---|\n| pi | `PI_SESSION_ID` |\n| oh-my-pi (`omp`) | `PI_SESSION_FILE` (a path — only the file name is used) |\n| Claude Code | `CLAUDE_CODE_SESSION_ID` |\n| Grok | `GROK_SESSION_ID` |\n| Codex | `CODEX_SESSION_ID` (the root session, not the thread) |\n| DeepSeek Harness | `DSH_SESSION_ID` (only present for agent-initiated shell calls) |\n| Qoder | **none — no automatic session identity** |\n| anything else | any `<TOOL>_SESSION_ID` in the environment is picked up automatically |\n\nIf your agent tool exports nothing usable (cursor-agent doesn't, as of this writing), or you want to\nbe explicit, set the key yourself:\n\n```bash\nexport KANBAN_SESSION_KEY=my-agent   # any stable unique value\nagent-kanban session start --agent codex-main --harness codex\n```\n\n`session start` prints the identity it picked, so you can confirm two agents really did land on\ndifferent keys:\n\n```\n  session_id : s-ayj4se\n  agent      : pi-main (pi)\n  identity   : pi-01a0debe-ca55-752e-895f-c20eb141ac19\n```\n\n## The web board\n\n`agent-kanban serve` hosts a real UI on port 7788. It serves the board, the admin page, and the\nsame API the CLI talks to — one data path, no second source of truth.\n\n```bash\nagent-kanban serve                                  # → http://127.0.0.1:7788/\nagent-kanban serve --port 9000 --host 0.0.0.0       # --host 0.0.0.0 needs the TLS note below\nagent-kanban serve --reap-interval 30               # how often the server reaps dead sessions\n```\n\n### Getting in — every `/api/*` needs a token\n\nThe browser has no ambient authority, so the first screen asks for a token. There are exactly\nthree ways to get one:\n\n| Where the token comes from | How |\n|---|---|\n| **Admin token, auto-generated** | The first `serve` on a machine prints it once and writes it to `.kanban/config.toml` under `[server] admin_token`. If it came from `KANBAN_ADMIN_TOKEN` it is never printed — read the file. |\n| **Project token, issued** | `agent-kanban admin token create --project my-app --name \"CI runner\"` — prints the plaintext exactly once. |\n| **A shareable link** | `http://host:7788/?key=k_…&project=my-app` — read from the URL, then stripped from it. |\n\nPaste the token into the login card. The **project** field is optional: leave it blank and the board\npicks the first project the token can reach. A token may be granted several projects, which is what\nthe switcher in the header is for.\n\n| Token type | Reaches |\n|---|---|\n| Project token | only the projects it was explicitly granted |\n| Admin token | every project, plus project and token management |\n\n### What the board does\n\n- **7 swimlanes** — Backlog, Todo, Doing, Blocked, In Review, Done, Cancelled\n- **Drag and drop** with state-machine validation; an illegal move is rejected with the reason\n- **Task drawer** — overview (description, checklist item by item with who ticked it, dependencies/parent), timeline, handoffs, plan version history\n- **Live updates** via SSE, with cursor resume after a reconnect\n- **Multi-project switcher**, light/dark mode\n- **Chinese / English UI** — one click in the header, on both the board and the admin page (`/admin`), sharing a single preference; first visit follows the browser language\n- **Shareable links** — `?key=…&project=…`; parameters are stripped from the URL after reading\n\nThe web UI and the CLI operate on the same data through the same code path. Anything you can do in\nthe browser, an agent can do from its shell.\n\n### The admin page at `/admin`\n\nCreate projects, issue tokens, change which projects a token may reach, revoke tokens. Revocation\ntakes effect on the next request — there is no session to expire.\n\n`/admin` requires an **admin** token. A project token gets `403` with the reason spelled out, which\nis deliberate: *forbidden* means \"valid token, wrong role, go get another one\", while *invalid* means\n\"go get a fresh token\" — different problems, different fixes.\n\nIf the frontend was never built, `serve` falls back to a built-in placeholder page that lists the\nAPI surface instead of pretending to be a board: `GET /api/health` (no auth), `POST /api/op`\n(needs `X-Kanban-Key` or `Authorization: Bearer`), `POST /api/stream-ticket` + `GET /api/stream`\n(SSE), and `/admin`.\n\n### Before you expose it\n\n`serve` does not terminate TLS and binds `127.0.0.1` by default. Put it behind a TLS terminator\nbefore binding `0.0.0.0` — **without TLS, tokens travel in plaintext.** See [Security](#security).\n\nIn Docker the board is on the same port as the API; `KANBAN_PORT` sets the host port and\n`KANBAN_BIND` the host-side bind address (the container itself always listens on `0.0.0.0`).\n\nThe browser keeps your token in `localStorage` so a refresh does not log you out. The price is that\nXSS can read it, which is why this project forbids `innerHTML` outright.\n\n## Sharing a board across machines\n\nOne `agent-kanban serve` process can host many projects. Each project is fully isolated: its own tasks, its own tokens, its own ID space (`T-0001` starts at 1 in every project).\n\n```bash\n# On the server machine\nagent-kanban serve                                  # prints the admin token once\nagent-kanban admin project add my-app\nagent-kanban admin token create --project my-app --name \"CI runner\"\n# → k_a1b2c3...\n\n# On a client machine: write <project>/.kanban/config.toml\n```\n\n```toml\nmode = \"remote\"\n\n[server]\nurl   = \"https://kanban.example.com\"\ntoken = \"k_a1b2c3...\"\n\n[project]\nkey = \"my-app\"\n```\n\n```bash\nagent-kanban config show     # confirm what resolved and where it came from\nagent-kanban task list       # no flags needed from here on\n```\n\nOr set it up in one command:\n\n```bash\nagent-kanban config init --server https://kanban.example.com --project my-app --key k_a1b2c3\n```\n\n## Configuration\n\nEverything lives in `<project>/.kanban/config.toml`.\n\n| Setting | CLI flag | Environment variable | Default |\n|---|---|---|---|\n| Server URL | `--server` | `KANBAN_SERVER` | derived from the project directory |\n| Project key | `--project` | `KANBAN_PROJECT` | derived from the project directory |\n| Access token | `--key` | `KANBAN_KEY` | — |\n| Mode | — | `KANBAN_MODE` | `remote` if a server is set, else `local` |\n| Database path | `--db` | `KANBAN_DB` | `<project>/.kanban/kanban.db` |\n| Session ID | `--session` | `KANBAN_SESSION` | `.kanban/sessions/<identity-key>`, else `.kanban/session` |\n| Identity key | — | `KANBAN_SESSION_KEY` | harness session variable (`PI_SESSION_ID`, `CLAUDE_CODE_SESSION_ID`, …) |\n\nTwo flags affect every command, so they work before or after the subcommand: `--json` for machine-readable output, and `--no-color` to disable ANSI colors. In CI, set `NO_COLOR=1` instead.\n\nServer-side: `KANBAN_HOST`, `KANBAN_PORT`, `KANBAN_WEB_DIR`, `KANBAN_ADMIN_TOKEN`. In Docker, `KANBAN_BIND` controls the *host-side* bind address (`127.0.0.1` or `0.0.0.0`). Note that the container itself always listens on `0.0.0.0`, so this variable is what actually decides how far the server is reachable.\n\n**Precedence:** CLI flags > environment variables > `config.toml` > derived defaults.\n\n> An empty environment variable counts as *unset*, not as an empty value. Otherwise `export KANBAN_SERVER=` would silently shadow a perfectly good `config.toml` and quietly create a new empty local database.\n\n## Security\n\n**`serve` does not terminate TLS, and binds to `127.0.0.1` by default.**\n\nThat is deliberate. Most deployments of a tool like this already sit behind an existing HTTPS terminator: a corporate gateway, nginx, a Kubernetes Ingress, or a network reachable only over VPN. Shipping a container that renews certificates introduces a failure mode (expired cert means total outage) that most users do not need.\n\nIf you expose it, put it behind a TLS terminator. **Without TLS, tokens travel in plaintext.**\n\n| Token type | Access |\n|---|---|\n| Project token | Only the projects it is explicitly granted |\n| Admin token | Every project, plus project and token management |\n\n`KANBAN_ADMIN_TOKEN` must be `k_` followed by 32 lowercase hex characters. A malformed value fails at startup rather than at first use, because a misconfigured credential that only breaks later is the worst kind.\n\nWhen it comes from the environment, the token is neither written to `config.toml` nor printed to the log.\n\n**No endpoint accepts a token in the URL.** Headers (`X-Kanban-Key` or `Authorization: Bearer`)\nare the only way in; browsers that cannot set headers on `EventSource` exchange a 60-second\nsingle-use ticket at `POST /api/stream-ticket` first. A token in a query string ends up in\nproxy access logs, container logs, `Referer`, and browser history, so `?key=` is rejected\neverywhere — including on the SSE path. The one legitimate use of `?key=` is a **shareable link**\nto the board itself (`http://host:7788/?key=…&project=…`), which the frontend reads once and\nstrips from the address bar before making any API call.\n\n## Command reference\n\n| Command | Purpose |\n|---|---|\n| `agent-kanban init` | Create the local board (`.kanban/`) |\n| `agent-kanban install-protocol` | Write the agent collaboration protocol into the project's `AGENTS.md` |\n| `agent-kanban mcp` | Run the MCP server (stdio) so an agent can call the board as tools |\n| `agent-kanban session start \\| end \\| list \\| heartbeat` | Session lifecycle |\n| `agent-kanban task add \\| list \\| show \\| claim \\| progress \\| block \\| done \\| …` | Task operations |\n| `agent-kanban board` | Swimlane view in the terminal |\n| `agent-kanban context` | Read the situation: board + handoffs + suggested actions |\n| `agent-kanban resume <task>` | Take over a task; injects handoff and timeline |\n| `agent-kanban handoff` | Write a handoff (summary / next / blockers / open) |\n| `agent-kanban plan save \\| show \\| list \\| history \\| at \\| attach` | Versioned plans |\n| `agent-kanban rebuild [--write]` | Replay the event log and verify the projection |\n| `agent-kanban doctor [--deep]` | Consistency self-check and repair |\n| `agent-kanban export [--out <dir>]` | Export the event journal (one file per day) |\n| `agent-kanban import <dir> [--dry-run]` | Rebuild the database from a journal (cross-machine migration) |\n| `agent-kanban snapshot` | Write a human-readable board snapshot |\n| `agent-kanban compact [--keep-days 30]` | Trim old events (snapshots first) |\n| `agent-kanban config show \\| init \\| set \\| use \\| path` | Configuration |\n| `agent-kanban project list` | Query projects (local database) |\n| `agent-kanban admin project … \\| token …` | Project and token management |\n| `agent-kanban update [--check]` | Self-update the binary from GitHub Releases (checksum-verified) |\n| `agent-kanban serve` | Run the HTTP + SSE server |\n\nRun `agent-kanban <command> --help` for the full option list.\n\n### Exit codes\n\nThese are a published contract. Agents branch on them, so the meanings never change.\n\n| Code | Name | Meaning | What to do |\n|---|---|---|---|\n| `0` | OK | Success | — |\n| `1` | USAGE | Bad arguments | Read `details.usage` and fix the command |\n| `2` | STATE | Task missing, illegal transition, missing required `--reason` | Do not retry as-is |\n| `3` | CONFLICT | Task is held by another session with a live lease | Work on something else, or wait. **Do not** reach for `--force` |\n| `4` | BUSY | Database is locked | Back off and retry, at most 3 times |\n| `5` | NOT_INIT | No `.kanban/` directory, or schema needs migrating | Run `agent-kanban init` |\n| `6` | INTERNAL | Bug | Report it, do not retry |\n| `7` | AUTH | Missing key, wrong key, or project not found | Fix your credentials |\n\nAgents should branch on the code (or the `error.name` string in `--json` output), never on message text.\n\n## Letting an agent drive the board\n\n### Option 1 — the protocol file (works with any agent)\n\n```bash\nagent-kanban install-protocol      # writes a managed block into <project>/AGENTS.md\nagent-kanban install-protocol --check   # CI guard: exits 2 if missing or outdated\n```\n\nThe block sits between `<!-- agent-kanban:begin -->` and `<!-- agent-kanban:end -->` and is the only\nthing the command touches, so you can keep your own conventions in the same file. `agent-kanban\ndoctor` reports when the installed block is behind the CLI version.\n\nAny agent that reads `AGENTS.md` (or `CLAUDE.md`, or whatever your harness looks for) then\nknows to run `agent-kanban session start` and `agent-kanban context` before touching code. Use\n`--file` to write somewhere else, for example `--file .cursor/rules/kanban.mdc`.\n\n### Option 2 — MCP tools\n\n```bash\npi mcp add agent-kanban -- cmd agent-kanban mcp\nclaude mcp add agent-kanban -- cmd agent-kanban mcp\n```\n\nAlready in this repo: `.mcp.json` at the project root registers the local build, so any\nharness that reads standard MCP files picks it up with no extra setup. Start the\nharness from the project root — the `command` path is relative to its working\ndirectory, and the board itself is found by walking up from there.\n\n```json\n{\n  \"mcpServers\": {\n    \"kanban\": {\n      \"command\": \"kanban\",\n      \"args\": [\"mcp\"]\n    }\n  }\n}\n```\n\n29 tools, all thin wrappers over the same core the CLI uses:\n\n| Group | Tools |\n|---|---|\n| Session | `kanban_session_start` / `kanban_bootstrap` / `kanban_session_end` |\n| Tasks | `kanban_task_list` / `get` / `create` / `claim` / `progress` / `note` / `block` / `unblock` / `complete` / `review` / `release` / `cancel` / `reopen` / `remove` |\n| Dependencies | `kanban_task_dep_add` / `dep_remove` / `dep_list` |\n| Recovery | `kanban_resume` / `kanban_handoff` |\n| Plans | `kanban_plan_save` / `show` / `list` / `history` / `diff` |\n| Board | `kanban_board` / `kanban_doctor` |\n\nPer-tool parameters are in [docs/plan/002-接口契约.md §3.2](docs/plan/002-接口契约.md), which also\nlists the operations that are deliberately **not** exposed to agents (project and token\nmanagement, among others) and why.\n\nThe recovery flow an agent is expected to run:\n\n```\nkanban_session_start(agent_name=\"pi-fix\")   → s-4k9d2m\nkanban_bootstrap(session_id=\"s-4k9d2m\")     → handoffs, your cards, unattended cards\nkanban_resume(session_id=\"s-4k9d2m\", task_id=\"T-0007\")\nkanban_plan_show(task_id=\"T-0007\")\nkanban_task_progress(..., pct=80)\nkanban_handoff(..., summary=\"...\", next_step=\"...\")\nkanban_session_end(session_id=\"s-4k9d2m\")\n```\n\nEvery tool returns `{ ok, data, next_actions }`; failures return `{ ok: false, error }` with\nthe same code and `name` the CLI would exit with, so an agent can branch on them the same way.\n\n### Option 3 — the agent skill\n\nA skill is the third option, and it is the one that teaches an agent the *workflow* rather than\nthe interface. The protocol file says \"run `agent-kanban context` first\"; the skill says what to do when\n`task claim` returns exit code 3, that `task progress` doubles as lease renewal, and that\n`doing → done` needs `--force` so you go through `review` instead. It ships the parts an agent\nkeeps getting wrong as reference files: the full command reference, the MCP tool mapping, the\nlocal-versus-remote differences, and a troubleshooting table keyed by exit code.\n\n```bash\nnpx skills add ArnoChenFx/agent-kanban --skill agent-kanban            # install\n```\n\nSkills live in this repo under `skills/`, so the install works on any project, not just this one.\nInstall it into a project that already has a board, then pair it with whichever of the two options\nabove your agent uses. The three are complementary: the skill teaches, the protocol file enforces,\nthe MCP tools give it hands.\n\n## When to reach for this\n\nGood fit:\n\n- More than one agent session working the same repo\n- Tasks long enough to span a context window\n- Work that gets interrupted (CI, laptop closed, agent restarted)\n- A team that wants to *see* what the agents are doing without tailing logs\n\nPoor fit:\n\n- A single agent on a short task. The overhead is not worth it\n- Work that must not be shared with a server. Use local mode, no server needed\n- Anything needing a real multi-user permissions model. Tokens are project-scoped, not user-scoped\n\n## FAQ\n\n**The database is not in git. Is that safe?**\n\nYes, and losing it is recoverable. `.kanban/kanban.db` is a SQLite file: a build\nartifact, awkward to merge. It is not in `.gitignore` by accident.\n\nThat file holds **only token hashes**, never a token itself — the `id` column is an\nindependent random reference (`t_…`), and authentication compares `sha256(token)`\nagainst `key_hash`. An older version stored the token in `id` itself, which meant any\ncopy of the database — a backup, an `export`, a screenshot — handed out every live\ncredential.\n\nUpgrading from that version needs **no action for those tokens** (the hash was always\ncomputed correctly, so they keep working), but no migration rewrites the old rows: they\nstill display masked, and the admin page cannot grant or revoke them. Manage those from\nthe CLI (`agent-kanban admin token revoke <token>` — for an old row its `id` *is* the\ntoken), or issue a new one and revoke the old. Details in [Develop.md](Develop.md).\n\nWhat *is* in git is `.kanban/journal/` — the append-only event log, one JSON line per\nchange, grouped into daily files. Since events are the source of truth and the board is\njust a projection, replaying that log on a fresh machine reproduces everything: tasks,\nchecklists, plans, handoffs, dependencies, the lot. `agent-kanban rebuild --write` does exactly\nthat, which is the same mechanism that lets it prove its own consistency.\n\n```bash\n# on the old machine\nagent-kanban export --out .kanban/journal\n\n# on the new one\nagent-kanban init\nagent-kanban import .kanban/journal\nagent-kanban rebuild --write --force\n```\n\nProject keys are derived from directory names, so they differ between machines.\n`import` rewrites events onto the current project and tells you it did; pass\n`--keep-project` to preserve the original keys instead.\n\n**Can I just copy the `.db` file to another machine?**\n\nUsually yes, as long as the path does not change. But it is a live database, so copy it\nwhile the server is stopped, and it will not merge with anything already on the target.\nPrefer `export` / `import`: it is mergeable, diffable, and safe to run while the server is\nup.\n\n**What if the event log has a gap?**\n\n`agent-kanban doctor` reports the inconsistency, and `agent-kanban rebuild` shows exactly which fields\ndisagree. It will not overwrite anything unless you pass `--write --force`, so a gap in the\nhistory can never silently wipe your board.\n\n**Two agents grabbed the same task. What happens?**\n\nOne wins. The loser gets exit code `3` together with the current holder's name, progress,\nand last action, and should pick something else. `--force` exists for the case where you\nhave decided a human confirmed the override; it leaves a `task_reclaimed` event behind.\n\n**An agent died mid-task. How do I pick it up?**\n\n`agent-kanban context` lists cards whose holder has gone stale, then `agent-kanban resume T-0007`\ntakes it over. Progress and checklist survive, and the previous holder's handoff is\ninjected. If they left a handoff, you can take over even before the lease expires —\nwriting a handoff means yielding.\n\n**How do I move everything to a shared server?**\n\nRun `agent-kanban serve` on the box, create a project, issue a token, and point clients at it\nwith `.kanban/config.toml`. Each project is isolated, and each token only reaches the\nprojects it was granted.\n\n**The board has been running for months and the database grew large.**\n\n`agent-kanban compact --keep-days 30`. It writes a snapshot first, then drops old events —\nkeeping everything from the last N days, anything belonging to a live task, and a floor of\n1000 rows regardless of age. The trimmed history survives only in that snapshot.\n\n**Is it safe to expose to a network?**\n\n`serve` binds `127.0.0.1` and does not terminate TLS by default. Put it behind a TLS\nterminator before exposing it: without TLS, tokens go over the wire in plaintext. See\n[Security](#security).\n\n## Documentation\n\n- [Develop.md](Develop.md) — architecture, data model, build, release, verification\n\n## License\n\nMIT\n"}