hkb-cli
v0.3.0
Published
hkb — a workload scheduler for coding agents: file a Job, and one agent runs one brief to completion in its own worktree. A SQLite board per machine, the Claude Agent SDK as the runtime, GitHub as the forge.
Downloads
791
Maintainers
Readme
hkb — a workload scheduler for coding agents
File a Job, and one agent runs one brief to completion in a git worktree of its own, then opens a draft pull request for a human to review. The board is a SQLite file on your machine. The runtime is the Claude Agent SDK. GitHub is the forge, not the board.
⚠️ Experimental — expect breaking changes
hkb is under active development and is not stable. It is
0.xand it means it: the schema, the CLI's verbs and flags, and the protocol between the controller and a worker all still change without notice.It is also built with itself — hkb's own work is filed as Jobs on an hkb board and written by workers hkb schedules — so the parts under construction move fast and land in large pieces.
Practically: pin an exact version if you depend on one, and read the release notes before upgrading. Issues and questions are welcome; treat anything here as subject to change until this notice is gone.
hkb — the workload scheduler
The first and only workload kind is a Job: one agent, one brief, run to completion. A Job runs in a git worktree of its own, then commits, pushes and opens a draft pull request — a human reviews and merges. The kanban DAG, cards that depend on cards, is a second kind that does not exist yet.
The board is ~/.hkb/board.db — SQLite behind Prisma, one board per machine with a Board row per
repository, the way one cluster holds a namespace per project. It is created and migrated the first time
anything touches it. Commands take the board from the repository you are standing in; --board <slug> names
one instead, and HKB_DATABASE_URL points at a different board file entirely.
Before you start
- Node >= 22.18.0. Measured, not guessed: 22.18.0 is the first release that strips TypeScript types without a flag, and a shebang cannot pass one.
- The GitHub CLI, with
gh auth loginalready done — a worker opens its own pull request with it, and hkb reads pull requests back through it. - A Claude Code login, which is what the Agent SDK runs a worker on.
npm i -g hkb-cli # or: npx hkb-cli --helpIn a checkout it is node bin/hkb.ts — Node runs the TypeScript directly, so there is no build step.
Quickstart
cd ~/code/my-project # the repository you are in decides which board you mean
hkb new "Add a --dry-run flag" \
--brief "Add --dry-run to the export command, with a test. Keep it small."
#> #1 Add a --dry-run flag [pending] on my-project
hkb run # reconcile once, in the foreground, watching it work
hkb show 1 # phase, spec, and every attempt: outcome, cost, session, PR URL--brief-file <path> and --brief - (stdin) take a brief too long to type, and --json works on every verb.
The verbs
| | |
|---|---|
| hkb new <name> | file a Job |
| hkb ls | what is on the board (--all for every board on the machine) |
| hkb show <id> | one screen: spec, phase, every attempt |
| hkb run [<id>] | reconcile once, in the foreground |
| hkb retry <id> | re-queue a Job that stopped, resuming its session |
| hkb done <id> "<why>" · hkb cancel <id> "<why>" | the two ends only a human can call |
| hkb rm <id> | delete a Job and its attempts |
| hkb stop · hkb start | the board's kill switch, and clearing it |
| hkb up · hkb down | the same reconcile pass on a timer, detached |
| hkb log [<id>] | what happened, in order |
| hkb boards | every board on this machine, and what each one may spend |
| hkb version | what this build is |
hkb run is the foreground tool — one reconcile, in this process, streaming what the worker does — and it is
the one to reach for when something is wrong, because everything it does is visible. hkb up is the same
pass on a timer in a detached process, serving every board on the machine; it exists for the work only a clock
can notice, not to make hkb run obsolete. --interval <s> changes the period, --status shows what is up
and what each board may still spend, and hkb down stops it cleanly, leaving no lease held.
To keep it alive across reboots, put hkb up --foreground under a supervisor:
docs/wiki/howto/running-the-daemon.md.
Retrying, and the one retry that is not automatic
hkb retry <id> puts a Job that stopped back on the board, resuming its session. A Job that spent its
whole --max-budget is not retried automatically — the retry would get the same cap and stop in the same
place, at the same price — so this is where you raise it: hkb retry 6 --max-budget 4, and the raise is on the
event log.
hkb done and hkb cancel end a Job the machinery cannot end itself: one whose pull request was
reviewed and merged while it sat pending on a spent budget, or one nobody wants any more. Two verbs because
they are two different statements about the work; both take a reason, both record the person who made the call
as an Event, and both refuse a Job a worker currently holds (stop the daemon, or wait). Neither is succeeded,
which means the session completed, and neither is hkb rm, which deletes the record that any of it happened.
Boards, ceilings and defaults
hkb boards lists every board on this machine; hkb boards add <slug> --repo <path> points one at a
repository.
hkb boards set <slug> carries the board's ceilings — --max-concurrent (0 drains it), --daily-budget
— and its spec defaults: --model, --effort, --max-turns, --max-budget, --max-retries. A board
that runs cheap, high-volume work says so once instead of on every hkb new.
Resolution is three-deep: the Job's own value wins, the board's default fills what the Job left unset, the
built-in is the last resort. none clears a default rather than setting it to the word, and hkb show names
which of the three answered each field. A default is not a ceiling: a Job may freely override --model,
and may not exceed --daily-budget.
The ceilings are checked before a claim and never during a run — a ceiling that could stop a running worker would strand its worktree, while one that declines to start another is only a decision.
What a Job produces
A pull request, by default. --export <path> on hkb new (repeatable) declares a file or directory the Job
must write as well: the board copies it out of the worktree into the repository before the checkout is torn
down, and a declared path the run did not produce fails the attempt — see
ADR-008. An undeclared file left in the checkout is litter,
and is deleted with it.
--no-isolate runs the Job in the current checkout rather than a worktree of its own, for work that has no
business on a branch.
Isolation, and the file the tests need
A worktree is a fresh checkout of a commit, so two things are true of it and both matter: uncommitted work in
your tree is invisible inside it, and gitignored files do not come across. A repository whose tests need a
gitignored .env therefore passes for you and fails in a worker. Declare what to carry across in
.worktreeinclude — docs/wiki/features/worktree-includes.md.
Worktrees are expensive (a worker installs the target repository's dependency tree to run its tests), so they are reclaimed by a sweep on the daemon's tick rather than at the end of a run: "safe to delete" is a state a worktree enters later, when its pull request lands.
How it maps
If you know Kubernetes, the shape is deliberate:
| hkb | Kubernetes |
|---|---|
| Job | Job |
| Attempt | Pod |
| Lease | Lease |
| Board | Namespace |
| Controller row | leader election |
| hkb up | a resync loop, not a watch |
The controller is level-triggered: it reads observed state, compares it to desired state and takes one step. It is safe to run repeatedly, to interrupt, and to run while another host runs it. Nothing depends on having seen an event. hkb fuses the controller-manager and the kubelet — it executes the work inline rather than scheduling it onto a node.
Local state
~/.hkb/board.db— the board. Back it up by copying the file.~/.hkb/hkb-<board>.log— what a detachedhkb upwrote.<repo>/.hkb/worktrees/— per-attempt checkouts. Gitignored; contents are branches, not files to track.
Docs
- docs/wiki/ — the code-derived wiki. Start at the index.
- ADR-007 — why hkb is a workload scheduler, and what it stopped being.
- ADR-008 — a Job declares its outputs.
- docs/rebuild-plan.md — the plan of record, and what is next.
- docs/releasing.md — publishing is a tag.
License
MIT
