tinycld CLI reference
The complete surface of the tinycld binary. For a task-oriented
introduction — downloading it, logging in, and typical usage — see
Command line tool.
This page documents the user-facing tinycld binary. It is unrelated to
tinycld-pkg, the per-member developer tool that runs biome, tsc, and
vitest, and to @tinycld/bootstrap, the scaffolder.
Global flags
Available on every command:
| Flag | Default | Meaning |
|---|---|---|
--output table|json|csv | table | Output format |
--json | — | Shorthand for --output json |
--context <name> | current context | Run against a different saved server |
--quiet | — | Suppress informational messages |
--no-color | — | Disable colored output |
--yes | — | Answer yes to prompts and skip interactive input |
-h, --help | — | Help for the command |
-v, --version | — | Version (root command only) |
Without a TTY the tool disables color and never blocks on input, so no extra flags are needed to make it CI-safe.
Table output is column-aligned with a header row; CSV writes a header row then records; JSON is indented. JSON is a separate payload from the table, not a serialization of it — some commands deliberately expose more (or differently-named) fields than they print. Verify a command’s actual JSON before depending on a key.
Exit codes
0 on success, 1 on any error. There is no differentiated exit-code
scheme — do not branch on specific non-zero values.
Errors are written to stderr as Error: <message>. Server-side failures
render as server error (HTTP <status>): <server message>.
Core commands
Present in every build regardless of which packages the server has.
| Command | Arguments | Notes |
|---|---|---|
auth login | <host> | Device-grant login; saves a context and credentials |
auth status | Current context, origin, user, and granted scopes | |
auth logout | Revokes the grant server-side, then clears the credential | |
context list | Saved contexts; the current one is marked | |
context use | <name> | Switch the current context |
context add | <name> <origin> | Save a context without logging in |
context remove | <name> | Remove a context and its stored credentials |
search | <query> | Federated search across installed packages |
version | Version, Go version, OS, and architecture | |
completion | bash|zsh|fish|powershell | Shell completion script |
help | [command] | Help for any command |
search accepts --pkg <slug> (repeatable), --not <terms>, and
--limit <n>. It understands the same query grammar as the in-app palette:
pkg: prefixes scope the search and a leading hyphen excludes a term. Quote
the query so the shell passes it as one argument; a query beginning with a
hyphen needs a -- separator first.
Counts and partial-result warnings go to stderr, keeping stdout a clean
document under --json.
A bare host in auth login or context add is normalized to https://,
except localhost, 127.0.0.1, and ::1, which get http://.
Drive
| Command | Arguments | Flags |
|---|---|---|
drive ls | [path] | -l/--long, -a/--all |
drive tree | [path] | --depth <n> (3), -a/--all |
drive search | <query> | --limit, --offset |
drive cat | <path> | |
drive get | <path> [dest] | Folders download as a zip; asks before overwriting a local file (--yes skips) |
drive put | <local> [dest] | -p/--parents, -r/--recursive |
drive mkdir | <path> | -p/--parents |
drive mv | <src> <dst> | |
drive cp | <src> <dst> | |
drive rm | <path> | --permanent (default is to trash) |
drive trash | Lists trashed items | |
drive restore | <path|id:…> | |
drive share | <path> | --user (repeatable), --role viewer|editor, --message |
drive link create | <path> | --role viewer|commentor|editor, --expires <RFC3339> |
drive link list | <path> | |
drive link revoke | <link-id> | |
drive versions | <path> | --restore <n>, --snapshot, --label |
drive export | <path> [dest] | --to pdf|svg (pdf); asks before overwriting (--yes skips) |
drive usage | Storage usage |
Every path argument also accepts id:<record-id> to bypass path resolution.
| Command | Arguments | Flags |
|---|---|---|
mail search | [query] | --mailbox, --limit, --offset, --from, --to, --subject, --has-words, --date-after, --date-before, --folder, --has-attachment, --not |
mail list | --folder (inbox), --mailbox, --limit (25), --page (1) | |
mail read | <message|thread> | --html, --raw, --no-mark |
mail attachments | <message> | |
mail download | <message> | --attachment N|all, --out <dir> |
mail send | --to, --cc, --bcc (repeatable), --subject, --body, --body-file, --attach, --mailbox, --from | |
mail reply | <message|thread> | --all, --body, --body-file, --attach, --from |
mail draft | Same flags as send, plus --message-id to update an existing draft | |
mail draft send | <message|thread> | |
mail labels | Labels available to you | |
mail label add | <thread> <label> | |
mail label remove | <thread> <label> | |
mail mailboxes | Addresses you can read and send as | |
mail status | Per-mailbox unread and folder counts | |
mail archive | <thread…> | |
mail trash | <thread…> | |
mail spam | <thread…> | |
mail star | <thread…> | |
mail unstar | <thread…> | |
mail move | <folder> <thread…> | Folder is one of inbox, sent, drafts, trash, spam, archive |
mail mark | read|unread <thread…> |
--body-file - reads the message body from stdin. Folder, label, star, and
read-state changes write the caller’s own per-user thread state, so they
never affect other members of a shared mailbox.
Boards
Present when the Boards package is installed. The
column group also answers to columns, and card to cards. Every
view command aliases show, every remove aliases rm and delete, and
list aliases ls.
| Command | Arguments | Flags |
|---|---|---|
boards list | -a/--all (include archived) | |
boards view | <board> | -a/--all, --sprint <n|active|next|id|backlog> |
boards archive | <board> | --unset to restore |
boards remove | <board> | Deletes the board and everything on it |
boards export | <board> | --format csv|json (csv), --out <file>; asks before overwriting (--yes skips) |
boards import | <file.json> | --name, --hooks (write history and send notifications for imported cards). Accepts a Trello export or a board export |
boards column show | -b/--board (required) | |
boards column add | <name> | -b/--board (required), --index |
boards column rename | <list> <name> | -b/--board (required) |
boards column move | <list> <index> | -b/--board (required); zero-based, counted after the move |
boards column category | <list> backlog|todo|in_progress|done|canceled | -b/--board (required) |
boards column done | <list> | -b/--board (required), --unset. Shorthand for category <list> done |
boards column wip | <list> <limit> | -b/--board (required); 0–999, 0 clears. A warning, never enforced |
boards column remove | <list> | -b/--board (required) — deletes its cards |
boards card view | <id> | Checklist, comments, links, and reactions |
boards card add | <title> | -b/--board and -l/--list (both required), --description, --due, --start, --index, --reporter, --priority urgent|high|medium|low|none, --estimate <points>, --parent <card>, --sprint <n|active|next|id> |
boards card edit | <id> | --title, --description, --due, --clear-due, --start, --clear-start, --reporter, --clear-reporter, --priority, --estimate (0 clears), --parent, --clear-parent, --sprint, --clear-sprint |
boards card move | <id> | -l/--list, --index; -b/--board for a cross-board move, with --family move|unlink for its sub-tasks and --epic move|unlink for its epic |
boards card copy | <id> | --title; copies the checklist, assignees, and labels, not attachments |
boards card archive | <id> | --unset to restore |
boards card remove | <id> | Permanent |
boards card link | <id> <other> | Exactly one of --blocks, --related, --duplicates |
boards card unlink | <id> <other> | Removes every link between the two |
boards card link-pr | <key> <url> | Link a GitHub pull request to a card |
boards card unlink-pr | <key> <url> | |
boards card react | <comment-id> <emoji> | Emoji or a shorthand: thumbs_up, heart, rocket, eyes, … |
boards card unreact | <comment-id> <emoji> | |
boards sprint list | <board> | |
boards sprint view | <sprint> | -b/--board |
boards sprint create | -b/--board (required), --name, --goal, --start, --end | |
boards sprint edit | <sprint> | -b/--board, --name, --goal, --start, --clear-start, --end, --clear-end |
boards sprint start | <sprint> | -b/--board, --name, --goal, --start (today), --end (the board’s sprint length) |
boards sprint complete | <sprint> | -b/--board, --unfinished next|new|backlog (required when cards remain), --next <sprint> |
boards sprint delete | <sprint> | -b/--board; its cards return to the backlog. Aliases rm, remove |
boards github list | <board> | Repositories attached to the board. Attaching one is done in the app |
A <board> argument resolves by id, key, or name; a <sprint> by its number
within the board, active, next, or id; a card <id> by id or key
(OTTER-123). Dates are YYYY-MM-DD, or "YYYY-MM-DD HH:MM" for a due
time. Mutating commands print nothing in table format — use --json or
--output csv to get the written row back. Board membership and share links
are read-only to the CLI — manage them in the app.
Contacts
Group alias: contact.
| Command | Arguments | Flags |
|---|---|---|
contacts list | --favorites, --trashed, --limit | |
contacts search | <query> | --limit |
contacts show | <id> | Alias: view |
contacts add | --first, --last, --email, --phone, --company, --title, --notes, --favorite. Aliases: new, create | |
contacts edit | <id> | Same field flags, plus --restore. Aliases: update, set |
contacts rm | <id> | --permanent. Aliases: delete, remove |
contacts export | --out <file>; writes vCard to stdout by default. Asks before overwriting (--yes skips) | |
contacts import | <file.vcf> | Upserts on the vCard UID |
rm is a soft delete. list --trashed finds trashed contacts and
edit --restore brings one back; --permanent is the only hard delete.
Calendar
Group alias: cal.
| Command | Arguments | Flags |
|---|---|---|
calendar agenda | --days (7), --calendar. Alias: next | |
calendar list | Aliases: ls, calendars. Includes a ROLE column, also present in --json as role (with kind) | |
calendar events | --from, --to, --calendar | |
calendar show | <id> | Alias: view |
calendar add | --calendar, --title, --start, --end, --all-day, --location, --description, --guest (repeatable), --recurrence daily|weekly|monthly|yearly, --reminder <minutes>, --busy busy|free. Aliases: new, create | |
calendar rm | <id> | Aliases: delete, remove |
calendar rsvp | <id> yes|no|maybe | Refused if you are not on the guest list |
calendar export | --calendar, --out <file>; asks before overwriting (--yes skips) | |
calendar import | <file.ics> | --calendar |
Reading a calendar requires membership in any role; writing requires owner or
editor. calendar list’s ROLE column is the only advance indication of which
you have.
Text and Calc
Each contributes one command. Both alias comments to comment.
| Command | Arguments | Flags |
|---|---|---|
text comments | <path> | --add, --reply-to, --quote, --resolve, --reopen, --all |
calc comments | <path> | --add, --reply-to, --cell, --sheet, --resolve, --reopen, --all |
--all includes resolved threads. --cell uses one-based A1 notation, and a new spreadsheet comment also needs --sheet <id> (the sheet’s id, e.g. sheet1) — a reply inherits its thread’s anchor and refuses both flags.
Documents and workbooks are Drive items, so tinycld drive handles creating,
listing, downloading, and deleting them; their bodies are collaborative edit
operations with no safe shell representation, which is why only comments are
exposed here.
Authentication and scopes
Login uses the OAuth 2.1 Device Authorization Grant (RFC 8628) against
the first-party public client tinycld-cli. The CLI requests a device code,
prints the user code, opens the browser for approval, and polls for the
token.
Access tokens refresh automatically when a request returns 401. A revoked or
expired refresh token surfaces as
authentication expired — run tinycld auth login. Grants are database rows
checked per request, so revoking one from Settings → Personal → Connected
apps takes effect on the terminal’s next call.
Scopes requested at login. Each package declares its own from its Go server
(oauth.RegisterPackage in server/oauth_scopes.go), naming the collections
and routes the scope governs and the label the consent screen shows:
| Scope | Grants |
|---|---|
profile | Identity for auth status |
mail:read, mail:send | Read mail; send and reply |
drive:read, drive:write | Read files; upload, move, share |
contacts:read, contacts:write | Read contacts; add and edit |
calendar:read, calendar:write | Read calendars; add and edit events |
boards:read, boards:write | Read boards; edit lists and cards |
text:read, text:write | Read document comments; post them |
calc:read, calc:write | Read spreadsheet comments; post them |
Only the scopes for installed packages appear in a given build.
How a package contributes commands
A package adds a command group by declaring a cli block in its manifest:
cli: {
package: 'cli', // directory, relative to the member root
module: 'tinycld.org/packages/mail/cli', // Go module path
},
and exposing a single entry point from that Go module:
func Register(root *cobra.Command, c *client.Client)
There is deliberately no command list in the manifest — Cobra owns the
command tree and generates --help from it. There is no scope list either:
the package’s Go server registers its OAuth scopes with core
(oauth.RegisterPackage), and the CLI’s device-flow login requests whatever
the installed packages registered.
The generator (pnpm run packages:generate) emits cli/cli_extensions.go
with the imports and Register calls, a cli/go.work covering each member’s
cli/ directory, and cli/search_slugs.go listing searchable packages. All
three are generated and gitignored — see
Generated files.
A package whose cli.package directory is absent is skipped silently. The
CLI module must not import tinycld.org/core; the few shared structs it
needs are mirrored locally, with a comment naming the source.
Because the command tree is compiled, a server without a package has no trace of its commands — there is no runtime gating to configure.
Build and distribution
Built with Go and Cobra. To build locally, run the generator first:
cd tinycld && pnpm run packages:generate
cd cli && go build .
A local build reports its version as dev; releases stamp it via
-ldflags "-X main.version=<v>".
Servers cross-compile the binary as a step of the package-build pipeline,
with CGO_ENABLED=0 and -trimpath -ldflags "-s -w", for five targets:
darwin/arm64, darwin/amd64, linux/amd64, linux/arm64, and
windows/amd64. Artifacts land in <appDir>/cli-dist/. The step is
best-effort: a target that fails to compile is logged and skipped, and never
fails the package install.
There is no package registry, Homebrew tap, or auto-updater. The server
distributes its own binary over two public endpoints — GET /api/cli/downloads
lists the available platforms, and GET /api/cli/download/{platform} streams
one. Both are public because a browser download link cannot carry an
authorization header; {platform} is resolved by lookup against a fixed
target list, so it cannot be used to traverse the filesystem.