@plotio/cli
v0.8.0
Published
Read Plot pages, follow a meeting as it happens, and draft or send messages, from any agent with a shell.
Readme
@plotio/cli
plot: read Plot pages, follow a meeting while it happens, and write back, from
any agent with a shell (Claude Code, Codex CLI, Amp, OpenCode) or a script.
Plot records conversations — meetings, calls, voice notes — as shared pages: a transcript of who said what, plus topics Plot's own model keeps up to date as people talk.
Install
npx @plotio/cli help agents # no install
npm install -g @plotio/cli # or install the `plot` binaryNode 22 or later.
An agent should run plot help agents first. It is the whole guide, and it
is the one that matches the version installed. plot help <command> lists a
command's options; everything below is the map, not a second copy of it.
Getting access
Two kinds of credential, both plot_agent_sk_… tokens that Plot stores only as
a hash. Each is kept once redeemed, so later shells need nothing.
- A page. Someone picks "Copy agent instructions" on a page, which gives a
one-time
temp_code_…(ten minutes, single use).plot open temp_code_… --agent "Claude Code"redeems it for a token that reads that one page (24 hours from the last read, a week at most). - A workspace.
plot login --agent "Claude Code"signs in with OAuth in a browser, against the authorization server the API itself names — so--apiat a local or preview deployment signs in on that deployment's own web app, never production's. - A workspace, with no browser anywhere. Someone signed in to Plot creates
a token in Settings > Agents and hands it over. Set
PLOT_TOKEN(andPLOT_API_URLunless it is production) and every command works with no login and nothing stored, which is what a sandbox or a CI job wants.
Whoever grants the access also chooses whether the agent may post. Allowed
drafts only, plot message still works and --send is refused
(draft_only); plot whoami says so under Writes.
plot login ends three ways, and what it can see decides which:
- This machine's browser. It opens, you approve, and it hands the terminal the credential itself. Nothing to paste.
- A browser somewhere else (SSH, another computer). Approve at the URL the
terminal prints, then paste the code that page shows. Ten minutes.
--no-browserprints that URL and listens for nothing. - An agent or a sandbox (no TTY). Nothing waits:
plot loginprints the URL and exits 0. Hand the URL to your person; they approve and send back the code the page showed.plot login --code <code>finishes the login, within ten minutes, and prints the workspace.
Commands
| Command | Does |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| plot open <temp_code_…> [--agent <name>] | Redeem a handoff code and store the page token |
| plot login [--agent] [--no-browser] | Connect a workspace: sign in and approve in the browser |
| plot login --code <code> | Finish a login started without a terminal, with the code the browser showed |
| plot logout [<page>] [--all] | Revoke a credential on the server, then forget it |
| plot whoami [<page>] [--json] | What the credential acts for, its scope and expiry |
| plot pages [--limit <n>] [--next <c>] [--json] | Pages the workspace grant can read, newest first |
| plot create <title> [-t task] [--parent <p>] [--assign <id>] | Make a page, or a task; --parent puts it inside a page |
| plot read <page> [--json] | The page as Markdown (topics included), or as JSON; stores the updates cursor |
| plot rename <page> <title> [--json] | Give the page a new title |
| plot watch <page> [--level …] [options] | Wait for the next update, print it, store the cursor |
| plot topics <page> | Topics as JSON, with the quotes behind each point |
| plot files <page> [--get <id>] [--out <p>] | Files on the page; --get writes one to disk |
| plot task <page> [--complete\|--reopen] [--assign <id>]… | Whether a task is completed, and who has it; with an option, set them |
| plot search <query> [--limit] [--next] [--json] | Search every page the workspace grant can read |
| plot message <page> [--body <t>] [--branch] [--note] [--id] | Draft a message into the person's composer (body from --body, else stdin) |
| plot message <page> --send … | Post it now instead, shown as your agent, for the person |
| plot message <page> --show\|--discard [<id>] | Read back or withdraw a draft (the last one made here by default) |
| plot message <page> --replace … | Let the draft overwrite the one already waiting in that composer |
| plot message <page> --attach <path> … | Put a file from this machine on the message; repeat for more |
| plot mcp-config [--host <h>] [--token] [--write] [<page>] | The MCP server block for a host: the <api>/mcp URL, signed in with OAuth |
| plot help agents | The guide for an AI agent |
<page> is a page id or a page URL (https://www.plot.io/p/<id>).
plot create makes it as the person whose credential it is, with them as its owner. A
task is a kind of page rather than a thing of its own, so -t task is the same command:
it is born not completed, and plot task <page> --complete ticks it off. With
--parent it goes inside that page and inherits who can read it, which is how anybody
else sees it; with no parent it is a top-level page in the workspace and private —
one member, you — until the person shares it from the link printed. Sharing it, and
changing who is on a page, are theirs to do and not something this CLI can.
The main conversation and each reply have their own composer, and a composer holds one
draft at a time, so a second draft where one is already waiting is refused
(draft_exists) rather than quietly replacing the first: several things to report are
one message, never one draft each. --replace is for when writing over the waiting
draft is what you mean; the refusal is not a reason to post with --send instead.
Every command takes --api <url>, --token <token>, --json and -h/--help
(except mcp-config, where --token is a switch taking no value).
watch is one update per run: it blocks until something qualifies at its
--level — a file someone attached counts — prints only what is new since the
last watch or read of that page, stores the cursor and exits, so the next
run continues where it left off. Keep --timeout under your shell tool's own.
See plot help watch.
mcp-config
By default the block is the URL and nothing else, which is the standard way to reach a remote MCP server: the host gets a 401 pointing at Plot's OAuth metadata, registers itself, and has you sign in the first time it connects.
{
"mcpServers": {
"plot": { "type": "http", "url": "https://agents.plot.io/mcp" }
}
}Hosts: claude-code (default, .mcp.json), cursor, vscode, zed and
claude-desktop. --write merges the block into that host's file, keeping
every other server. For Claude Code,
claude mcp add --transport http --scope project plot <api>/mcp is equivalent.
--token adds a bearer header instead, for where OAuth cannot be used — a page
grant, which has no OAuth flow, or a sandbox or CI job with no browser. The
header names PLOT_TOKEN rather than holding the token, since these files are
usually committed. See plot help mcp-config.
Environment
| Variable | Meaning |
| ----------------- | ---------------------------------------------------------------------------- |
| PLOT_API_URL | API base. Default https://agents.plot.io; https://agents-staging.plot.io |
| | for staging; a deployment's .convex.site host for local dev and previews. |
| | --api wins over it. |
| PLOT_TOKEN | Use this token instead of the store (a sandbox or CI job). |
| PLOT_AGENT_NAME | Default --agent. Inside Claude Code the default is "Claude Code". |
| XDG_CONFIG_HOME | Where the credential store lives (below). |
Token resolution for a page command: --token, then PLOT_TOKEN, then the
page's stored grant, then the stored workspace grant. A stored token the server
calls expired or revoked is removed and the next one tried.
Where credentials live
$XDG_CONFIG_HOME/plot/credentials.json, by default
~/.config/plot/credentials.json, mode 0600 in a 0700 directory. It is
keyed by API base, so production's tokens and a preview's never collide — and
so changing --api or PLOT_API_URL changes which credential is in play. It
also holds each page's last watch cursor, which is how a new shell resumes,
and the id of the last message plot message made there.
plot logout --all revokes and removes everything for the current API base.
A plot login with no TTY leaves a pending-login.json beside it, same mode,
holding what --code needs to finish the login. It is deleted on success and
is useless after ten minutes.
