@pactto/cli
v1.7.0
Published
Pactto CLI - external programmatic surface for LLMs and scripts
Readme
Pactto CLI
External CLI to read meeting context from Pactto rooms — transcription, PDFs, images, canvas shapes, participants — without opening the web app.
Made to be used by LLMs in the terminal (Claude Code, ChatGPT in a shell) or by humans piping JSON through jq.
Prerequisites
- Node.js 20 or newer
Check with
node --version. If older, install from nodejs.org or via your package manager. - An API key from the Pactto web app
Go to Settings → API Keys → Create. Copy the token shown — it appears only once. Tokens start with
pact_dev_(dev environment) orpact_prod_(production).
Install
The CLI is on the npm public registry. Install globally:
npm install -g @pactto/cliVerify:
pactto --version # should print 0.4.0 (or newer)
pactto --help # shows all commandsIf pactto: command not found: your npm global bin is not in $PATH. Run npm bin -g to see where npm installs global binaries, and add that to $PATH in your ~/.zshrc or ~/.bashrc. With nvm this is automatic.
No-install try-out: npx -y @pactto/cli <command> runs a one-shot without installing globally — handy for exploring.
Pre-release tarballs (internal / fallback)
If you received a pactto-cli-X.Y.Z.tgz file directly (for an unreleased canary, or when the public registry is unavailable), cd into the directory holding it and install with a relative path:
cd ~/Downloads # or wherever the .tgz is
npm install -g ./pactto-cli-0.4.0.tgzOr use the absolute path (starts with /, no leading ./):
npm install -g /Users/yourname/Downloads/pactto-cli-0.4.0.tgzCommon pitfall:
npm install -g ./Users/yourname/Downloads/...(with./before an absolute path) is treated as relative, and npm looks inside your current working directory — producingENOENT: no such file or directory. Use one of the two patterns above.
First-time setup
pactto auth login
# Paste the token from the web app when promptedThe CLI auto-detects which environment (dev/prod) to talk to based on the token prefix and saves the config in ~/.pactto/config.json (chmod 600 — only you can read).
Set a default room so you don't need --room on every command:
pactto rooms # list rooms you have access to
pactto use room_<roomPk> # pick one
pactto context # confirmCommon commands
# Read meeting context
pactto transcription # last session
pactto transcription --scope full # entire history
pactto participants # who is in the room + online status
pactto canvas list # all shapes in the room
pactto canvas list --type pacttoNote # filter
pactto canvas show <shapeId> [<id>...] # full details
pactto pdf <shapeId> # text content of a PDF shape
pactto pdf <shapeId> --pages 1-5 # only some pages
pactto pdf-visual <shapeId> # describe figures / scanned PDFs
pactto image <shapeId> # describe an image shape
pactto doc <docId> # read a Pactto Doc by UUID
pactto room-instructions get # the room's durable instructions
# Get help on any command
pactto <cmd> --helpAll output is JSON in stdout. Use jq to filter. Errors are JSON in stderr with non-zero exit code.
Add --pretty for human-readable indented output.
Tip: using with Claude Code (or any external LLM)
Generate a primer doc and feed it to your LLM so it knows the surface:
pactto instructions > ~/myproject/.claude/PACTTO.md
# (or just `pactto instructions | pbcopy` and paste into your prompt)Then Claude Code (running in ~/myproject) automatically knows it can do pactto rooms, pactto transcription, etc.
Update to a new version
npm install -g @pactto/cli@latest
pactto --version # confirmYour config in ~/.pactto/config.json is preserved across upgrades.
To see all published versions:
npm view @pactto/cli versionsLogout / uninstall
pactto auth logout # clears local config (token + default room)
npm uninstall -g @pactto/cli # removes the binaryTroubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| getaddrinfo ENOTFOUND ... | CLI is pointing at a wrong endpoint | PACTTO_API_ENDPOINT=<url> pactto rooms to override, or get a fresh token |
| {"error":"invalid_api_key"} | Token expired/revoked, or wrong env (dev vs prod) | Generate a new token in the web app |
| {"error":"not_logged_in"} | Config missing or pactto auth logout was run | pactto auth login again |
| {"error":"forbidden_room"} | Your API key was scoped to specific rooms; you tried another | Create a key with broader allowedRooms in the web app |
| {"error":"room_required"} | No --room and no default set | pactto use <roomPk> or pass --room <roomPk> per command |
| pactto: command not found | npm global bin not in $PATH | See Install section |
| npm error ENOENT: no such file or directory ... .tgz on install | Used ./ before an absolute path during install (./Users/...) — npm treats it as relative | cd into the directory holding the .tgz and use ./pactto-cli-X.Y.Z.tgz, or use the full absolute path (starts with /) without leading ./ |
Status of commands in 0.4.0
All Phase 1 read commands, Phase 2 write commands, Phase 3 Pactto-specific shape commands, and Phase 4 board commands have been smoke-tested end-to-end against the dev backend (CLI version 0.4.0+). Tracking matters because the Pactto backend has a few subtle invariants (data model, claim formats, tldraw v4 schemas) that unit tests stub away.
| Command | Status | Notes |
|---|---|---|
| pactto auth login | confirmed | Validates token by calling /whoami |
| pactto auth whoami | confirmed | Returns userPk + scope + allowedRooms |
| pactto auth logout | works locally | Just clears ~/.pactto/config.json — no backend call |
| pactto rooms | confirmed | Delegates to /v1/roomActions/rooms via Lambda invoke |
| pactto use <roomPk> | works locally | Writes to config — no backend call |
| pactto context | works locally | Reads config |
| pactto instructions | works locally | Prints bundled primer Markdown |
| pactto transcription | confirmed | Resolves transcription_chat_room_id via Lambda invoke, then reads S3. Use --scope full for all sessions; default last_session returns only the active session window |
| pactto participants | confirmed | Reuses the same handler that the Bedrock agent uses |
| pactto canvas list | confirmed | Same handler as agent |
| pactto canvas show <id> | confirmed | Same handler as agent |
| pactto pdf <shapeId> | confirmed | Reads PDF from S3 + extracts text |
| pactto pdf-visual <shapeId> | confirmed | Calls Gemini for visual description |
| pactto image <shapeId> | confirmed | Calls Gemini |
| pactto doc <docId> | confirmed | Calls docs.pactto.com |
| pactto room-instructions get | confirmed | Reads from S3 |
| pactto canvas create-note <text> | confirmed | Phase 2. Sticky note (yellow default; --color) |
| pactto canvas create-text <text> | confirmed | Phase 2. Plain text shape |
| pactto canvas create-arrow --from --to | confirmed | Phase 2. Arrow + 2 bindings linking two existing shapes |
| pactto canvas update-shape <id> | confirmed | Phase 2. Patch x/y/w/h/rotation/color/parent |
| pactto canvas update-text <id> <text> | confirmed | Phase 2. Replace text on note/text shape (other types rejected) |
| pactto canvas delete <id>... | confirmed | Phase 2. Cascades bindings |
| pactto canvas align <id>... --mode | confirmed | Phase 2. top/middle/bottom/left/center/right |
| pactto canvas distribute <id>... --axis | confirmed | Phase 2. horizontal/vertical |
| pactto canvas link --url | confirmed | Phase 3. URL as pacttoLink shape + Link/Asset/RoomAsset records |
| pactto canvas image --file | confirmed | Phase 3. Local image upload → pacttoImage. Dims via image-size |
| pactto canvas video --file | confirmed | Phase 3. Local video upload → pacttoVideo. Dims via ffprobe (or --w --h override) |
| pactto canvas pdf --file | confirmed | Phase 3. Local PDF upload → pacttoPdf. totalPages + first-page dims via pdf-lib |
| pactto canvas comment --content | confirmed | Phase 3. pacttoComment pin with optional anchor + cascade (commentPerson/commentFlag/attachment). Author resolved via Person+RoomPerson DDB |
| pactto canvas annotation --comment-shape-id --strokes | confirmed | Phase 3. Freehand drawing tied to a pacttoComment (optional annotationBinding to an asset) |
| pactto canvas board | confirmed | Phase 4. Empty board (tldraw frame shape). Flags: --at --w --h --name --color |
| pactto canvas group-into-board | confirmed | Phase 4. Wrap ≥2 existing shapes in a new board. Backend validates atomically (details.missing[] / details.locked[]) |
| pactto room-instructions set | deliberately omitted | Durable memory changes only via web app |
| pactto ask / pactto session * | deliberately omitted | The CLI does not invoke the Pactto agent — your external LLM is the brain |
| pactto media generate / edit | deliberately omitted | AI image generation only via web app; Phase 3 canvas image is for uploading existing files, not generation |
| pactto canvas board --items ... / create-frame | deliberately omitted | Single-shot "board with items" was cut in 2026-05-26 scope review. Workflow: create children individually via create-note etc., then group-into-board |
"Implemented, not smoke-tested" means: the code path is in place, unit tests pass, code is deployed to dev, but nobody has run the command against real data yet. They should work — if you try one and something breaks, please report so the maintainer can investigate (include the full error JSON from stderr).
Limitations (0.4.0)
- Canvas writes require an API key with scope=write. A read-only key receives 403 on any write. Web UI creates write-scoped keys explicitly.
- No
room-instructions set. Durable memory updates happen only via the web app (Cognito-authenticated). - No AI image/media generation. Phase 3 added file uploads (
canvas image/video/pdftake local file paths), NOT generation from text prompts. AI-generated media is web-app-only (Pactto'smedia_optool). - No single-shot "board with items".
pactto canvas boardcreates an empty board. To populate, create the children individually (canvas create-note, etc.) and callpactto canvas group-into-boardwith their IDs. - No editing of Pactto-specific shape props. You can move/resize/recolor/delete any shape, but you cannot edit the
contentof apacttoComment, theurlof apacttoLink, etc. via CLI. Use the web app for content edits.
Environment overrides
| Env var | Effect |
|---|---|
| PACTTO_API_KEY | Overrides the token from config (useful to run as another user temporarily) |
| PACTTO_ROOM | Overrides the default room |
| PACTTO_API_ENDPOINT | Overrides the auto-detected backend URL (testing, staging, or fallback) |
For maintainers — releasing a new version
This section is for whoever ships new versions of @pactto/cli to the public npm registry.
Standard release flow (publish via GitHub Actions)
- Bump the version in
cli/package.json(semver — major for breaking, minor for new commands, patch for bug fix). Mirror the bump incli/package-lock.json. - Update the bundled primer (
src/assets/PACTTO_CLI_PRIMER.md) if behavior changed. - Run tests locally:
cd cli npm install # if deps changed npm test # all tests must pass cd .. - Open a PR with the version bump and merge it to
main. - From
main, create and push the tag matching the new version:git checkout main && git pull git tag cli-vX.Y.Z git push origin cli-vX.Y.Z - The
.github/workflows/cli-publish-v2.ymlworkflow runs on tag push: it validates that the tag matchescli/package.jsonversion, runs tests, and publishes vianpm publish --access public. Total runtime ~15 seconds. (Filename is-v2.ymlafter the original workflow ID got stuck on GitHub's side during the 0.3.1 publish; recreating under a new path restored a fresh workflow registration. See roadmap Gate 3 post-delivery notes.) - Verify:
npm view @pactto/cli version # should match the tag
The published tarball includes only what's listed in files of cli/package.json (bin/, src/, README.md, LICENSE). Tests, lockfiles, and config are excluded by default.
Token rotation (every 90 days)
The npm publish token consumed by GitHub Actions (secrets.PACTTO_CLI_PUBLISH_CI) has a 90-day expiry by design. Rotate before it lapses to avoid release downtime.
- The npm org owner (
pacttoinc— currently Demian) creates a new Granular Access Token athttps://www.npmjs.com/settings/~/tokenswith:- Permission: Read and write, scope All packages (narrower scopes have failed at publish time — see
docs/features/pactto-cli/roadmap-npm-publish.mdGate 6) - Bypass 2FA for publishes enabled (CI cannot prompt for OTP)
- Expiry: 90 days
- Permission: Read and write, scope All packages (narrower scopes have failed at publish time — see
- Copy the token immediately — npm shows it only once.
- Update the secret in GitHub:
gh secret set PACTTO_CLI_PUBLISH_CI --repo PrepForImpact/pfi-pactto # paste the token when prompted - (Recommended) Revoke the previous token in npm once the new one is confirmed working.
- Validate with a patch release or a canary publish (see roadmap Gate 6 for canary playbook).
If the token expires before rotation, the workflow will fail with npm error code E401 / EOTP. Same fix: rotate.
Manual npm pack fallback
For pre-public-release distribution (canary tarballs shared via Slack) or when GitHub Actions is unavailable:
cd cli
npm test # all tests must pass
npm pack # produces pactto-cli-<version>.tgz in cwdInspect before shipping:
npm pack --dry-run # prints file list, no .tgz produced
tar tf pactto-cli-0.3.0.tgz # list contentsThis path does not register the version on the public npm registry — once GA is back, do a proper release with a new patch number to publish for real.
