Install
openclaw skills install @robbeverhelst/do-cliInstall and drive `do`, the agent-drivable CLI for the do personal todo / life-tracking app. `do` is a thin client over the app's public REST API (`/api/v1`) with per-tenant API-key auth — capture tasks, see what's actionable today, complete/snooze tasks, log habits, and read entities (tasks, habits, notes, contacts, recipes, …). There are no destructive verbs, so it's safe to hand to an autonomous agent. Use when the user wants to manage their todos / life-tracking from a terminal or agent, mentions the `do` CLI, or asks to capture or review tasks and habits.
openclaw skills install @robbeverhelst/do-clido is a thin client over the do app's public REST API (/api/v1). It
inherits the same per-tenant auth, validation, and row-level-security wall as the
web app — there is no direct DB access. The v1 surface is the daily loop:
capture, today, complete/snooze a task, log a habit, and read entities. There are
no destructive verbs (no create/update/delete), so it is safe to hand to an
autonomous agent.
The live, authoritative surface is always
do help --json. This skill is the orientation; when in doubt, introspect the binary — the manifest is generated from the same registry the commands dispatch from, so they can't drift.
bunx @robbeverhelst/do <command>.npm i -g @robbeverhelst/do (or bun add -g @robbeverhelst/do), then run do <command>.do-<os>-<arch> asset from a GitHub Release
(https://github.com/robbeverhelst/do/releases) and put it on your PATH.Requires Bun ≥ 1.3 for the bunx/binary paths (bun --version; install with
curl -fsSL https://bun.sh/install | bash).
Every call needs a per-tenant API key, sent as Authorization: Bearer <key>.
Keys are minted by the app (the JWT-guarded POST /api-keys endpoint, e.g. from
the web app's settings); the CLI only consumes a key — it never creates one. A
key only reaches routes whose scope it holds, so grant the scopes for the verbs
you need (tasks:read/tasks:write, habits:write, …).
Config resolves in precedence order (first wins):
--api-key / --url flagsDO_API_KEY / DO_API_URL environment variables ← the agent / CI path~/.config/do/config.json ← written by do login / do config set, for humansThe hosted instance is https://do.robbe.work/api — so you only need a key:
# Agent / CI: just export the env vars.
export DO_API_KEY=do_xxx
export DO_API_URL=https://do.robbe.work/api # hosted instance (see note below)
# Human: verify a key and save it (file is written 0600).
do login --api-key do_xxx --url https://do.robbe.work/api
do whoami # prove the key resolves to a tenant + scopes
do config show # resolved config, key redacted, with sources
The URL ends in /api because the API sits behind the web app's same-origin
/api proxy (the CLI appends /api/v1 itself). For a self-hosted instance, use
https://<your-host>/api; for local dev, http://localhost:3001. Only the
API key is per-user — ask the user for a scoped key.
--json forces JSON; output is automatically JSON when stdout is piped
(non-TTY), so an agent always gets parseable output. A TTY gets pretty,
colored text (honoring NO_COLOR, --quiet, --verbose).
Failures print a JSON error envelope to stdout in JSON mode
({ "error": { "code", "message" } }) and a non-zero exit code:
| exit | meaning |
|---|---|
| 0 | success |
| 1 | runtime / network / unexpected HTTP |
| 2 | usage (unknown command, missing argument) |
| 3 | config / auth (no key, 401, 403) |
| 4 | not found (404) |
| 5 | validation (400) |
do capture quick-adds a task to the inbox from free text with two inline tokens:
#tag — adds a tag (repeatable): #home #urgent!priority — !high !medium !low !none (aliases !h !m !l !n); last one winsBoth are stripped from the title; standalone only (so C# or 10! in prose is
left alone):
do capture "Email the landlord about the leak #home !high"
# → task "Email the landlord about the leak", tag home, priority high
# Capture an idea into the inbox
do capture "Draft Q3 plan #work !medium"
# See what's actionable today (overdue, scheduled, due, in-progress)
do today
# Complete a task (idempotent — a no-op success if already done)
do task done 01J9Z2K7Q8ABCDEF0123456789
# Snooze a task forward (default +1 day, or an explicit target)
do task snooze 01J9Z2K7Q8ABCDEF0123456789 --days 3
do task snooze 01J9Z2K7Q8ABCDEF0123456789 --until 2026-07-01
# Log a habit check-in (value defaults to 1; --at for a past time)
do habit log 01J9Z2K7Q8ABCDEF0123456789
do habit log 01J9Z2K7Q8ABCDEF0123456789 --value 30 --at 2026-06-18T21:00:00Z
# Read entities: `do <entity> list` / `do <entity> get <id>`
do tasks list --filter unsorted # the GTD inbox
do habits list
do notes get 01J9Z2K7Q8ABCDEF0123456789
# Pipe JSON into jq (auto-JSON because stdout is piped)
do today | jq '.items[] | select(.bucket == "overdue") .title'
Readable entities: tasks, habits, occurrences, shopping-lists,
inventory-items, recipes, meal-plans, notes, contacts, webhooks.
Run do help for the human overview, or do help --json for the complete
machine-readable manifest — every command, argument, flag, required scope, return
type, the read entities, auth resolution, and exit codes. Introspect that instead
of trusting this skill when the surface may have grown:
do help --json | jq '.commands[] | {command, scopes, returns}'
do help --json | jq '.entities[].name'
Source + full docs: https://github.com/robbeverhelst/do