Technical reference · CLI · MCP · HTTP API
One core, three machine interfaces
Commission is a Bun binary and a SQLite file. Everything on this page is the same
core function reached over a different transport — commission on a command line, typed tools over MCP stdio, and /v1 over HTTP. There is no second implementation, so there is no second set of rules.
Every command takes --json. Every rejected
action states what is missing and the exact command that supplies it, and
exits 2 — 422 over HTTP, isError over MCP, the same body
in all three. That contract is the whole reason an agent can correct itself without
a human reading the output.
commission start checkout-1checkout-1: open → in_progressbreadcrumbs: commission note checkout-1 -m "..." commission done checkout-1cannot complete checkout-1: 2 unchecked criteria; gate 'tier1' missing evidence [pr, test] missing: criterion 0: signature verified against the endpoint secret missing: criterion 1: replayed events are rejected missing: evidence.pr missing: evidence.testhint: tick with 'commission check checkout-1 <n>'; attach with 'commission evidence checkout-1 pr=<ref> test=<ref>'; or --force to override (logged)
Captured from a real run. Every terminal block on this page is: site/scripts/capture-terminal.ts drives a throwaway project through the whole loop and writes what the binary
actually printed. Nothing here is typed by hand.
Execution model
Plan, dispatch, record, prove, close
Five properties, and one loop that any actor — a coding agent, a script, or a human at a terminal — drives with the same commands.
- 1
A plan becomes a graph
commission apply -f plan.json creates every task and every dependency in one transaction, so there is never a moment where half a plan exists.
- 2
The frontier is derived
A task is ready when its dependencies are satisfied and its children are closed. It is evaluated at query time, so it cannot be stale, and blocked is never a status anybody sets.
- 3
Three agents claim three tasks, at once
commission next --claim selects by the project’s policy and takes the lease in the same transaction. Run it three times in parallel and you get three different tasks, not three copies of one.
- 4
The gate reads criteria and evidence
Completion is a predicate, not a status field. A task’s gate names the evidence kinds it requires, and the criteria have to be ticked.
- 5
Granted, refused, or routed to a person
Where the gate is satisfied, done is granted. Where it is not, done exits 2 and the refusal names the missing evidence and the exact command that attaches it. Where the execution policy requires approval, it goes to a human — because that is the one thing a gate cannot decide.
Three lanes, one command each, and three different endings — because
done is a predicate the gate evaluates, not
a status the worker sets.
- in_progress
- claimed, lease held
- satisfied
- criterion ticked, evidence attached
- missing
- the gate is still waiting on it
- exit 2
- refused, with the fix named
- One truth, derived views
- Status lives in exactly one row. Boards, the ready frontier, standup narratives, and blocked state are queries over it. There is no second place to update, so there is nothing to keep in sync.
- Work is dispatched, not assigned
- commission next --claim picks the highest-value workable task and claims it in a single transaction. Claims are leases, so a crashed session never wedges a task, and N agents running the same command each get a different one.
- Context arrives in one read
- A claim returns a context bundle: the task body, acceptance criteria, dependency state, parent and children, linked documents, attached evidence, prior session notes, recent events, and the exact commands that finish the task from its current state.
- Completion is verified, not asserted
- A task's gate names the evidence kinds it requires — a PR, a test run, a benchmark, a rollback plan. done is refused until they exist and until every acceptance criterion is ticked, and the refusal says what is missing and the exact command that supplies it.
- Parallel by default
- The ready frontier is everything workable right now: dependencies satisfied, children closed. It is claimable by many actors at once, and tasks that declare the files they touch are batched so overlapping work is visible before it collides.
- plan
commission apply -f plan.json - dispatch
commission next --claim - record
commission note <id> -m "…" - prove
commission evidence <id> pr=214 test=<url> - close
commission done <id>
The whole loop. Every command takes --json, and exits 0 on success, 2 on a refusal.
commission apply -f plan.jsonapplied to 'checkout': 4 created, 0 updated + checkout-1 (verify) + checkout-2 (idempotent) + checkout-3 (retry) + checkout-4 (summary)A graph is created in one transaction, with dependencies, so the ready frontier is accurate the moment it exists.
A cold session becomes a productive one
One command returns everything needed to work the task, and claims it on the way past — so the agent that read the context is the one holding the lease. The bundle ends with the exact commands that finish the task from the state it is actually in.
The prepared place the work happens is part of the same object: commission workspace prepare <id> materialises a worktree, environment variables from a provider chain, deterministically
allocated ports, and setup hooks, and prints the descriptor. commission workspace release <id> tears it down through the same provider chain that built it.
commission next --claimclaimed checkout-1routing: agent:one is holding it; autonomy autonomous (default); no actors are declared, so nothing is restricted checkout-1 · Verify Stripe webhook signatures [open · p1 · gate:tier1]project: Checkout criteria: [ ] 0. signature verified against the endpoint secret [ ] 1. replayed events are rejectedblocks: checkout-2 gate 'tier1' requires: pr, test next: commission start checkout-1 commission note checkout-1 -m "<progress>"- The record One SQLite file, append-only
- Tasks, dependencies, acceptance criteria, evidence, events, notes and leases live in exactly one place. Boards, the ready frontier, the standup and blocked state are queries over it, so there is no second copy to keep in sync.
- The context bundle Composed per task, returned by one claim
- commission next --claim returns the task body, its acceptance criteria, dependency state, parent and children, linked documents, attached evidence, prior session notes, recent events, and the exact commands that finish the task from the state it is actually in.
- The workspace Materialised per task, released the same way
- commission workspace prepare <id> materialises a git worktree, environment variables resolved through a provider chain, deterministically allocated ports, and setup hooks, and prints the descriptor. commission workspace release <id> tears it down through the same chain that built it.
- The session An agent, a script, or a person at a terminal
- The session holds none of the above. What it learns — a note, a piece of evidence, a transition — is appended to the record, never written back over it, which is why the next bundle is better than this one and why a crashed session loses nothing but its lease.
Dispatch
The ready frontier, and claims that are leases
Parallelism is not a feature bolted onto a tracker here; it is the consequence of two decisions — readiness is computed, and claiming is atomic.
- Readiness is derived, never set
- A task is ready when every dependency is satisfied and every child is closed. It is evaluated at query time, so there is no blocked status to set and therefore none to forget to unset. The frontier cannot be stale, because it is not stored.
- Selection is declared policy
- Ordering comes from the project's configured policy — severity, sprint, due date, priority, rank, age — not from whoever shouted loudest. Two agents running the same command get the same ordering and different tasks.
- Claiming is one transaction
- A claim is a conditional UPDATE inside a SQLite transaction: it wins if and only if the task is unclaimed or the previous lease has expired. Selection and claim happen together, so the agent that read the context is the one holding it.
- Claims are leases, not assignments
- The default lease is 8h (agentDefaults.lease, or COMMISSION_LEASE). Re-claiming as the holder extends it, commission heartbeat <id> extends it without changing state, and an expired lease is reclaimable by anyone — so a crashed session releases work by not existing.
- Overlap is visible before it collides
- A task declares the paths it touches. commission ready groups the frontier into batches whose touch sets do not intersect, so fan-out to N agents is a read rather than a guess.
- commission ready
- the frontier, batched for safe fan-out
- commission next --claim
- select by policy and take the lease atomically
- commission claim <id>
- claim one specific task
- commission heartbeat <id>
- extend your lease without changing state
- commission release <id>
- release a claim — yours, or a stale one
commission readyready frontier (dispatch independent tasks in parallel): checkout-1 · Verify Stripe webhook signatures [open · p1 · gate:tier1] ⚠ touches overlap: checkout-3 checkout-3 · Retry queue for failed captures [open · p2 · gate:tier1] ⚠ touches overlap: checkout-1 checkout-4 · Checkout summary page [open · p2 · gate:tier1]suggested parallel batches (no touches overlap within a batch — dispatch batch 1 together, then 2): 1) checkout-1, checkout-4 2) checkout-3
Readiness and the touches overlaps are
computed at query time. Nothing here was set by anyone.
commission claim checkout-1checkout-1 is already claimed by agent:onehint: pick another with 'commission ready', or 'commission release checkout-1' if that claim is stale (lease expires 2026-07-27T21:40:25.657Z) commission next --claimclaimed checkout-3skipped: checkout-1 (claimed by agent:one)routing: agent:two is holding it; autonomy autonomous (default); no actors are declared, so nothing is restricted checkout-3 · Retry queue for failed captures [open · p2 · gate:tier1]project: Checkout criteria: [ ] 0. a failed capture retries with backoff gate 'tier1' requires: pr, test next: commission start checkout-3 commission note checkout-3 -m "<progress>"The second actor is not blocked by the first. It is told the task is taken, given the command that would release a stale claim, and moved on to the next workable thing.
This is the property prompting cannot add. A conditional UPDATE inside a transaction with an expiry is either in the data model or it is
not.
Refusals
A refusal is the next prompt, and exit 2 is the contract
A rejected action states what is missing and the exact command that fixes it. That is the whole interface an agent needs in order to correct itself without a human reading the output.
Exit codes
- 0 ok
- the command did what it said
- 1 error
- something broke — a bug, a bad file, an unreachable database
- 2 refusal
- the action is not permitted yet; the message says what is missing and how to fix it
- 3 not found
- no such task, project, or binding
2 is deliberately distinct from 1, because "you cannot do that yet" and "something broke" require
different responses from the caller.
The refusal object
- error
- refusal | not_found | error — the class of the failure
- message
- what was attempted and why it was declined, in one sentence
- missing
- the itemised list — criteria, evidence kinds, open children
- hint
- the exact command that supplies what is missing
done is a predicate
Completion is not a status write. It is evaluated at the moment of closing, and any one of these refuses the transition and leaves the task exactly where it was.
- Open children
- A parent cannot close over unfinished work. The refusal names each child.
- Unchecked acceptance criteria
- Criteria are ticked one at a time with commission check <id> <n>, and the refusal lists the ones still open by number.
- Missing gate evidence
- The task's gate profile names evidence kinds — pr, test, commit, benchmark, docs, rollback. Each missing kind is listed, and the hint is the commission evidence command that attaches it.
- Outstanding human approval
- Where a task is supervised, a recorded human approval is required and --force cannot override it.
commission note checkout-1 -m "Constant-time compare — the naive === leaks timing. Rejected the stripe-node helper: it pulls the whole SDK for one function."noted on checkout-1 commission check checkout-1 0checkout-1 criterion 0 ticked; 1 remaining commission check checkout-1 1checkout-1 criterion 1 ticked; 0 remaining commission done checkout-1cannot complete checkout-1: gate 'tier1' missing evidence [pr, test] missing: evidence.pr missing: evidence.testhint: attach with 'commission evidence checkout-1 pr=<ref> test=<ref>'; or --force to override (logged) commission evidence checkout-1 pr=214 test=https://ci.example.com/runs/8821attached pr, test to checkout-1 commission done checkout-1checkout-1: in_progress → donenext task: commission next --claimOne task, closed properly: a breadcrumb, two criteria, a refusal for the missing evidence, then done.
Overrides are expensive and legible
--force exists and refuses without -m "<why>". The reason is written to the append-only event log and surfaces in
the standup. Cancellation is held to the same rule: a reason, or the
task that supersedes it.
A system that cannot be overridden gets worked around invisibly — in a side channel, in a spreadsheet, in a status document nobody reconciles. One that makes overrides expensive and legible gets overridden rarely and traceably.
The same refusal, every transport
| surface | signal | body |
|---|---|---|
| CLI | exit 2 | message, then "missing:" lines, then "hint:" on stderr |
| CLI --json | exit 2 | the JSON object above on stderr, and nothing else |
| HTTP | 422 | the same JSON object as the response body |
| MCP | isError | the same text, so the model reads the fix and retries |
There is one core, so there is one refusal. Nothing about the wording is transport-specific.
CLI
The surface agents actually operate
Every command takes --json, every refusal exits 2 with a machine-readable body, and nothing requires a person to be looking at a screen.
Work the loop
-
commission next --claim - pick the next ready task and claim it
-
commission start <id> - begin work (auto-claims if unclaimed)
-
commission note <id> -m "…" - leave a session breadcrumb
-
commission check <id> <n> - tick acceptance criterion n
-
commission evidence <id> pr=214 - attach completion evidence
-
commission done <id> - complete — refused until the gate is satisfied
See position
-
commission ready - the parallel frontier, batched for safe fan-out
-
commission board - in-flight, ready, blocked, recent
-
commission standup - a done / doing / blocked narrative
-
commission log --last 20 - the append-only event feed
-
commission search "…" - full-text over titles, bodies, and notes
-
commission show <id> - the full context bundle for one task
Plan and operate
-
commission init <slug> - create a project and write .commission.json
-
commission apply -f plan.json - create a whole task graph atomically
-
commission define <slug> - the brief for turning an idea into a graph
-
commission prime - the agent operating manual, for your CLAUDE.md
-
commission doctor - self-diagnostics with exact remediation commands
-
commission dump > backup.json - full JSON export; restore with commission restore
Two output modes, one shape
--json switches both streams: stdout carries
the result and stderr carries refusals as JSON and nothing else, so a pipeline
sees identical bytes whatever the repository's configuration looks like.
- --json
- machine-readable output on both streams, including refusals
- --project <slug>
- override the project .commission.json resolved to
- -m "<why>"
- the reason a cancellation or an override is justified
- --force
- override a gate deliberately; refuses without -m
Exit codes and the refusal body are the same contract on every surface,
so they are documented once — refusals and exit codes. The full command list, including bindings, imports, and migrations,
is commission --help; this page is the
subset an evaluating engineer needs, not a manual.
commission search "timing"checkout-1 [done] Verify Stripe webhook signatures note: Constant-time compare — the naive === leaks [timing]. Rejected the stripe-node helper…Recall is full-text search plus graph traversal over what actors actually wrote down. There is no embedding store and no inference.
MCP
The same core, as typed tools
For contexts where a shell is not available — claude.ai, Claude Desktop, any MCP client — the same core is exposed over stdio.
claude mcp add commission -- commission mcp The MCP surface is deliberately narrower than the CLI: it covers the working loop — claim, record, prove, close — and planning, not project administration or import.
Refusals come back in Commission's standard shape — message, missing, and
the exact fixing command — marked isError, so a model can correct itself without a human in the loop.
-
commission_next - claim the next ready task and return its context bundle
-
commission_show - the full context bundle for one task
-
commission_board - in progress, ready, blocked, recently done, stale claims
-
commission_ready - the parallel frontier, with suggested no-overlap batches
-
commission_standup - the done / doing / blocked narrative
-
commission_transition - start, review, done, cancel, reopen
-
commission_claim - claim a task under a lease
-
commission_release - release a claim — yours or a stale one
-
commission_note - leave a session breadcrumb
-
commission_evidence - attach completion evidence
-
commission_check - tick or untick an acceptance criterion
-
commission_apply - create a whole task graph, with dry_run
-
commission_search - full-text: did we already decide this?
-
commission_log - the append-only event feed, cursor-based
-
commission_prime - the operating manual for agents
-
commission_define - the brief for turning an idea into a graph
-
commission_sync - observe bound externals and return the outbound plan
-
commission_bind - bind a task to a Jira issue or a pull request
# registered once with: claude mcp add commission -- commission mcp echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude","version":"1"}}}' | commission mcp 2>/dev/null | jq -c .result.serverInfo{"name":"commission","version":"0.1.0"} echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"commission_transition","arguments":{"id":"checkout-4","to":"done"}}}' | commission mcp 2>/dev/null | jq -r '.result.content[0].text'{ "error": "refusal", "message": "cannot complete checkout-4: 1 unchecked criteria; gate 'tier1' missing evidence [pr, test]", "missing": [ "criterion 0: displayed totals match the order", "evidence.pr", "evidence.test" ], "hint": "tick with 'commission check checkout-4 <n>'; attach with 'commission evidence checkout-4 pr=<ref> test=<ref>'; or --force to override (logged)"}The same refusal as the CLI's, over a different transport. Nothing about it is transport-specific, because there is only one core.
HTTP API
One core, three transports
commission serve exposes the same functions the CLI calls over /v1, and renders the human web view from the same rows.
| method | route | returns |
|---|---|---|
| GET | /v1/version | binary, schema, and config versions |
| GET | /v1/projects | every project in this database |
| GET | /v1/projects/:slug/board | the board, derived |
| GET | /v1/projects/:slug/ready | the ready frontier, with claim state |
| GET | /v1/next | preview the next task — never claims |
| POST | /v1/next | select and claim atomically |
| GET | /v1/tasks/:id | the full context bundle |
| POST | /v1/tasks | create one task |
| POST | /v1/tasks/:id/transition | move a task — refusable |
| POST | /v1/tasks/:id/claim | take a lease |
| POST | /v1/tasks/:id/release | release a lease |
| POST | /v1/tasks/:id/notes | append a note |
| POST | /v1/tasks/:id/evidence | attach evidence |
| POST | /v1/apply | create a whole graph in one transaction |
| GET | /v1/search | full-text search |
| GET | /v1/events | the event log, from a cursor |
commission serve # commission serve --port 4401 is running in another shell curl -s localhost:4401/v1/version{"commission":"0.1.0","schema":10,"config":1} curl -s -i -X POST localhost:4401/v1/tasks/checkout-4/transition -d '{"to":"done"}'HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
Date: Mon, 27 Jul 2026 13:40:26 GMT
Content-Length: 354
{"error":"refusal","message":"cannot complete checkout-4: 1 unchecked criteria; gate 'tier1' missing evidence [pr, test]","missing":["criterion 0: displayed totals match the order","evidence.pr","evidence.test"],"hint":"tick with 'commission check checkout-4 <n>'; attach with 'commission evidence checkout-4 pr=<ref> test=<ref>'; or --force to override (logged)"}
A refusal is 422 with the same body the
CLI prints: the message, the missing list, and the hint carrying the exact
fixing command.
Set COMMISSION_TOKEN and every mutation requires
a bearer token, while reads stay open — which is what lets the web view
be exposed to people while writes stay agent-only. The web view is read-only
by design: humans observe and judge, they do not drive the loop by clicking.
Derived views
Every view is a query, and the log is append-only
There is one row that says what a task's status is. Boards, the frontier, standup narratives, and blocked state are all computed from it, so there is no second place to update and nothing to keep in sync.
- commission board
- Position: in flight with who holds each lease, the ready frontier, what is blocked and by what, and what closed recently.
- commission standup
- A done / doing / blocked narrative for a human on a call. Overrides and forced transitions surface here rather than staying in the log.
- commission log --last 20
- The append-only event feed, read forward from a cursor. Every entry carries the actor that caused it.
- commission search "…"
- Full-text over titles, bodies, and notes — the "did we already decide this?" query.
- commission export / commission dump
- A git-committable markdown snapshot, and a full JSON export restorable with commission restore. The database stays the truth; both are derived.
commission boardCheckout (checkout) — 1/4 done, 3 open ready: checkout-2 · Idempotent order creation [open · p1 · gate:tier1] checkout-3 · Retry queue for failed captures [open · p2 · gate:tier1 · @agent:two] checkout-4 · Checkout summary page [open · p2 · gate:tier1] recently done: checkout-1 · Verify Stripe webhook signatures (2026-07-27 13:40)commission standupstandup — Checkout (checkout) what to say (covers since Friday 7:00 AM): Since Friday 7:00 AM we shipped Verify Stripe webhook signatures. Next up: Idempotent order creation. done (since Friday 7:00 AM): ✓ checkout-1 Verify Stripe webhook signatures (just now)A standup with nobody to attend it — derived from the same rows the agents were writing to all night.
commission log --last 102026-07-27 13:40 agent:one checkout-4 created: Checkout summary page2026-07-27 13:40 agent:one - project: {"action":"apply","created":4,"updated":0}2026-07-27 13:40 agent:one checkout-1 claim: {"via":"next"}2026-07-27 13:40 agent:two checkout-3 claim: {"via":"next"}2026-07-27 13:40 agent:one checkout-1 transition: open→in_progress2026-07-27 13:40 agent:one checkout-1 note: Constant-time compare — the naive === leaks timing. Rejected the stripe-node helper: it pulls the whole SDK for one function.2026-07-27 13:40 agent:one checkout-1 criteria: {"position":0,"text":"signature verified against the endpoint secret","done":true}2026-07-27 13:40 agent:one checkout-1 criteria: {"position":1,"text":"replayed events are rejected","done":true}2026-07-27 13:40 agent:one checkout-1 evidence: pr=214, test=https://ci.example.com/runs/88212026-07-27 13:40 agent:one checkout-1 transition: in_progress→donenext cursor: 15 (commission log --after 15)History is appended, never rewritten. A status cannot be quietly corrected after the fact, because there is no place to correct it.
Recall, across sessions
The durable parts of a session are data attached to the task, not to the transcript: notes, criteria state, evidence with references, the dependency graph, and the events in between. A different actor arriving days later reads the decision the first one made and the evidence that closed it.
The honest limit: this is not a semantic memory system. There is no embedding store and no inference. Recall is full-text search plus graph traversal over what actors actually wrote down — its advantage is that the workflow makes writing things down the path of least resistance.
commission show checkout-1checkout-1 · Verify Stripe webhook signatures [done · p1 · gate:tier1]project: Checkout criteria: [x] 0. signature verified against the endpoint secret [x] 1. replayed events are rejectedblocks: checkout-2 gate 'tier1' requires: pr✓, test✓evidence: pr=214, test=https://ci.example.com/runs/8821 notes: 2026-07-27 13:40 agent:one: Constant-time compare — the naive === leaks timing. Rejected the stripe-node helper: it pulls the whole SDK for one function. next: commission reopen checkout-1Internals
One file, five statuses, and configuration that refuses to guess
Commission is Bun + SQLite in WAL mode + Drizzle + Hono. The core is a library and the only writer; the CLI, the HTTP API, the MCP server, and the adapters are thin consumers of it.
Five statuses, moved only by a transition
Nothing can write a status except a transition, so every state change is validated, attributed, and logged. There is no settable blocked status: blocked is derived from dependencies, and a status that can be set can be wrong.
| from | may move to |
|---|---|
| open | in_progress · done · cancelled |
| in_progress | in_review · done · cancelled |
| in_review | in_progress · done · cancelled |
| done | open (reopen only — done is terminal) |
| cancelled | open |
Cancelling requires a reason or the task that supersedes it, because silent cancellation destroys the context the next session needs.
The storage engine is the boring part
One SQLite file in WAL mode. No server, no account, no daemon, and
nothing to provision — the database is created on first use at ~/.commission/commission.db, or wherever COMMISSION_DB points.
The interesting parts are the invariants: readiness is derived so it cannot be stale, claiming is atomic so parallelism is safe, done is refusable so completion means something, and events are append-only so history cannot be rewritten.
Configuration is strict and explainable
.commission.json is versioned and validated
against a strict schema: unknown keys are refused rather than ignored, because
a typo that silently does nothing is the failure mode the file exists to
prevent. A solo project's config is two keys — a version and a project slug.
Precedence — last wins
- defaults
- db
- .commission.json
- .commission.local.json
- env
- flags
commission config explain <key> prints
the value, the layer that set it, and every layer it overrode. commission config validate exits 2 on an invalid file, which is what makes it usable in CI.
commission init checkout --name "Checkout" --gates '{"tier1":["pr","test"]}'created project checkout (Checkout)wrote .commission.json — bare 'commission next' now targets 'checkout' from this repoadd tasks: commission add "..." or commission apply -f plan.json
First run. Two keys in .commission.json,
and every bare command in that repository targets the right project.
Environment
- COMMISSION_ACTOR
- who is acting — recorded on every event, claim, and note
- COMMISSION_DB
- the database file; defaults to ~/.commission/commission.db
- COMMISSION_PROJECT
- the project slug, overriding .commission.json
- COMMISSION_CONFIG
- an explicit config file, skipping discovery
- COMMISSION_LEASE
- claim lease duration, e.g. 8h
- COMMISSION_TOKEN
- bearer token required for mutations over HTTP
Commission contains no model, performs no inference, and writes no code. It is not an agent runtime and not a CI system: it never runs a test, it records that a test run happened and where to find it.
Install
From source today; four paths when RC1 is cut
Commission is a binary and a SQLite file — one file, no server, no account. There is nothing to sign up for.
No release published yet
No tag has ever been cut: the repository has carried the placeholder
version 0.1.0 since its first commit. The four
distribution paths below are built and documented but not yet publishing artifacts,
so none of those commands resolves today.
planned · relay-52 Building from source is the honest answer,
and it is the same code.
Works today
Requires Bun, and access to the repository, which is private while the
open-versus-closed-source decision is open.
planned · relay-57 bun link puts commission on your PATH; the database is created on first use at ~/.commission/commission.db, or wherever COMMISSION_DB points.
git clone https://github.com/Row-Labs/commission.git bun install bun link Then, in your project
commission init myapp --name "My App" --gates '{"tier1":["pr","test"]}' commission prime When RC1 is cut planned · relay-52
Four paths, all first-class, each producing a working commission --version and passing the same smoke test on macOS, Linux, and Windows. Copy them
now if you like; they will not resolve until the tag exists.
curl -fsSL commission.sh/install | sh brew install row-labs/tap/commission npm i -g @rowlabs/commission gh release download --repo Row-Labs/commission --pattern "commission-*"