checklists-cli
v0.2.0
Published
Author, run and approve checklists.io runbooks from the terminal.
Maintainers
Readme
checklists-cli
Approve checklists.io human gates from the terminal.
When an agent runs a checklist and hits a step marked gate: human, the run stops and waits for
you. This is how you decide without leaving the terminal the agent is running in.
npm install -g checklists-cli
checklists auth login # opens your browser, same sign-in as the website
checklists pending # what is waiting on you
checklists show <run-id> # the agent's case for the gate
checklists approve <run-id> --step cut-releaseIt also authors and runs them, so you never have to leave the terminal:
checklists runbooks pull <id> -o release.yaml # the definition, as YAML
$EDITOR release.yaml
checklists runbooks push <id> -f release.yaml # shows a diff, asks, applies
checklists runs start <id>Ids can be shortened anywhere a full uuid is accepted — the first eight characters are enough, and an ambiguous prefix is refused with the candidates listed rather than guessed at.
Why a separate command instead of just telling the agent "yes"
If you tell the agent yes and the agent records the approval, the approval is the agent's API
call. Nothing downstream can distinguish that from an agent that decided to approve itself — which
is the exact thing a gate exists to prevent. So the agent has no approve tool: request_approval
and wait_for_approval are all it gets, and the decision is made with your credentials, by you.
There is a softer reason too, and in practice it matters more. A prompt that appears mid-conversation with a Yes button gets clicked reflexively. Typing a command names the step you are approving and puts the agent's reasoning on screen first. That friction is the feature.
Commands
Deciding
| | |
| :--- | :--- |
| checklists pending | Every step awaiting your approval, across all runs. |
| checklists show <run-id> | The run, its steps, and the full context for anything pending. |
| checklists approve <run-id> --step <name> | Approve. --yes skips the confirmation prompt. |
| checklists reject <run-id> --step <name> --reason <why> | Reject — fails the whole run. |
Runbooks
| | |
| :--- | :--- |
| checklists runbooks | List them. |
| checklists runbooks show <id> | Steps, gates, dependencies, run count. |
| checklists runbooks pull <id> [-o file] | Write the definition as YAML. Stdout by default, so pull > file.yaml works. |
| checklists runbooks push <id> -f <file> | Diff the file against the live runbook, ask, apply, bump the version. |
| checklists runbooks create -f <file> | Create from a definition. |
| checklists runbooks rm <id> | Move to the recycle bin. |
| checklists runbooks restore <id> | Bring it back. |
Runs
| | |
| :--- | :--- |
| checklists runs | In progress. --all or --status <s> for the rest. |
| checklists runs start <runbook-id> | Start a run. |
| checklists runs complete <run-id> --step <name> | Complete a step yourself. --evidence <json> when the step requires proof. |
| checklists runs abort <run-id> | Stop a run. Not a failure, and not resumable. |
Agent tokens
| | |
| :--- | :--- |
| checklists tokens | List active tokens. --all includes revoked. |
| checklists tokens mint <name> | Mint a ck_ token. Printed once. |
| checklists tokens revoke <id> | Revoke immediately. |
Account
| | |
| :--- | :--- |
| checklists auth login | Browser sign-in (Google). --no-browser for an emailed code, --provider <name> for another provider. |
| checklists auth logout | Remove stored credentials. |
| checklists auth whoami | Print the signed-in email. |
Editing a runbook
pull writes the same YAML that create reads, so editing is a round trip through your editor and
your version control:
name: Release
steps:
- id: tests-green
check: CI passes on main
evidence: required
- id: cut-release
check: Tag and publish
gate: human
gate_surfaces: ["web"]
depends_on: [tests-green]push compares parsed values rather than text, so reformatting the file is never mistaken for a
change. It prints what it will do and waits:
~ tests-green check "CI passes on main" -> "CI passes on main AND the release branch"
+ security-scan (new step)
- staging-smoke (removed)
Apply? [y/N]Editing never disturbs a run already under way. A run snapshots its steps when it starts, and every rule the server enforces — gates, required evidence, dependencies, approval surfaces — is read from that snapshot. Change a runbook mid-run and the run keeps executing the version it began with; the change applies to the next one.
--step is required and can be the step's title (or a unique fragment of it) or its node id.
Approving whatever happens to be pending is how you approve something you did not read.
checklist is installed as an alias for checklists, because the singular is easy to type by
reflex. They are the same command.
What is guaranteed, and what is not
Guaranteed: approval authority is your signed-in identity. The database derives the approver from the authenticated session, not from what the caller claims, so an agent token cannot approve anything — it is not a user credential and cannot reach the approval RPC at all.
Not guaranteed: that the approval came from a human sitting at the keyboard. An agent with shell access to your machine can run this command, just as it could drive a browser you are already signed into. The surface is not a security boundary and never was.
So the CLI records the surface instead of pretending: every approval writes approved_via = "cli"
and this machine's hostname to the step, and the run page shows it. "Approved via cli · my-laptop"
is weaker evidence than an approval from a device the agent cannot reach, and it should look weaker.
For gates where that matters, restrict the step:
{ "gate": "human", "gate_surfaces": ["web"], "check": "production migration applied" }Absent or empty means any surface. The check runs in the database, not the client.
Sign-in
checklists auth login opens your browser, you authenticate where your session and password
manager already are, and the credential returns over http://localhost:8976/callback. PKCE, so the
returned code is useless to anyone who did not start the flow. No password is typed into or stored
by this CLI.
The session lives in ~/.config/checklists/config.json, mode 0600, and refreshes itself.
On a machine with no browser (SSH, a container), checklists auth login --no-browser sends a
one-time code by email instead.
Self-hosting checklists.io
Point at your own project:
export CHECKLISTS_SUPABASE_URL=https://<ref>.supabase.co
export CHECKLISTS_SUPABASE_KEY=sb_publishable_...Your project needs two things configured:
http://localhost:8976/callbackon the redirect allow-list (Authentication → URL Configuration). Without it Supabase silently redirects to your site URL instead of the CLI, so sign-in appears to work in the browser while the terminal waits forever.{{ .Token }}in the Magic Link email template, if you want--no-browserto work. The default template sends only a link, which cannot be pasted into a terminal.
Requirements
Node 20+.
