hey
A command line tool for HEY that lets you read and send email, manage contacts, labels, calendars, todos, habits, time tracking and journal entries from the terminal or from your coding agent.
Lets you manage Basecamp projects, todos, cards, messages, files and schedules from your terminal or from Claude Code, so you stop switching to the web app for routine updates.
Claude Code
$ npx -y skills add basecamp/basecamp-cli --agent claude-code
Codex
$ npx skills add basecamp/basecamp-cli -a codex
Cursor
$ npx skills add basecamp/basecamp-cli -a cursor
Gemini CLI
$ npx skills add basecamp/basecamp-cli -a gemini-cli
GitHub Copilot
$ npx skills add basecamp/basecamp-cli -a github-copilot
OpenCode
$ npx skills add basecamp/basecamp-cli -a opencode
Windsurf
$ npx skills add basecamp/basecamp-cli -a windsurf
Basecamp CLIbasecamp is the official command-line interface for Basecamp. Manage projects, todos, messages, and more from your terminal or through AI agents.
macOS / Linux / WSL2
curl -fsSL https://basecamp.com/install-cli | bash
Windows (PowerShell)
irm https://raw.githubusercontent.com/basecamp/basecamp-cli/main/scripts/install.ps1 | iex
On Windows 11 with Smart App Control, see Troubleshooting if the install is blocked.
On an interactive terminal, the installer opens Basecamp setup: approve OAuth in your browser and the CLI uses the account granted by OAuth, otherwise preserves an existing account or selects the first available. It saves that account globally, clears the global project default, and connects every detected coding agent. Directory-specific and environment project settings continue to apply. Use basecamp setup --customize to choose those settings instead.
Other installation methods
Brew / macOS
brew install --cask basecamp/tap/basecamp-cli
Arch Linux / Omarchy (AUR):
yay -S basecamp-cli
Linux (deb/rpm/apk):
# Download from https://github.com/basecamp/basecamp-cli/releases/latest
sudo apt install ./basecamp-cli_*_linux_amd64.deb # Debian/Ubuntu
sudo dnf install ./basecamp-cli_*_linux_amd64.rpm # Fedora/RHEL
sudo apk add --allow-untrusted ./basecamp-cli_*_linux_amd64.apk # Alpine
Arm64: substitute arm64 for amd64 in the filename. Verify the SHA-256 checksum from checksums.txt before installing unsigned Alpine packages.
Scoop (Windows):
scoop bucket add basecamp https://github.com/basecamp/homebrew-tap
scoop install basecamp-cli
Shell script (macOS / Linux / WSL2 / Git Bash):
curl -fsSL https://raw.githubusercontent.com/basecamp/basecamp-cli/main/scripts/install.sh | bash
Nix:
nix profile install github:basecamp/basecamp-cli
Go install:
go install github.com/basecamp/basecamp-cli/cmd/basecamp@latest
mise:
mise use --global github:basecamp/basecamp-cli@latest
GitHub Release: download from Releases.
basecamp upgrade
What happens depends on how the CLI was installed:
~/bin or ~/.local/bin): upgrades in place. The CLI downloads the release for your platform, verifies its Sigstore signature (the keyless checksums.txt.bundle published by the release pipeline, identity-pinned to the release workflow and tag) and SHA-256 checksum, swaps the executable transactionally, and confirms the installed binary reports the new version. On failure the previous binary is restored; in the worst case — restoration itself fails mid-swap — the error names the preserved backup file next to the binary so you can put it back by hand.brew upgrade --cask / scoop update, then verifies the manager-installed binary actually reports the new version.go install builds: never touched. basecamp upgrade exits nonzero with upgrade guidance for that install method (the exact command where it can be known, e.g. mise or go install; otherwise which package manager to use).basecamp upgrade exits 0 only when there is no update, or the update was applied and confirmed. Every other outcome is a structured failure ("ok": false in JSON) with one of these codes:
| Code | Meaning |
|---|---|
upgrade_required | An update exists but the CLI won't apply it for this install method — the hint carries the right next step |
upgrade_incomplete | The package manager exited 0 but the binary still reports the old version |
upgrade_unverified | The upgrade may have worked, but the installed version could not be confirmed |
upgrade_failed | The update check, download, signature/checksum verification, or executable swap failed — the previous binary remains installed (or the error names the preserved backup if restoration also failed) |
The install scripts verify release signatures when cosign is available: cosign v3 verifies the published bundle format as-is, v2.6+ is driven with --new-bundle-format=true, and older versions skip signature verification with a warning (SHA-256 checksums are always verified).
The first interactive basecamp run applies the recommended setup automatically after browser approval:
Run the same setup directly with basecamp setup. To choose the account, default project, config scope, and agent integrations, run:
basecamp setup --customize
basecamp projects list # List projects
basecamp todos list --in 12345 # Todos in a project
basecamp todos create "Fix bug" --in 12345 # Create todo
basecamp todos complete 67890 # Complete todo
basecamp cards done 67890 --in 12345 # Complete card (move to Done)
basecamp search "authentication" # Search across projects
basecamp files list --in 12345 # List docs & files
basecamp cards list --in 12345 # List cards (Kanban)
basecamp chat post "Hello" --in 12345 # Post to chat
basecamp pings post Jane "Lunch?" # Ping someone directly
basecamp comments create 67890 "@Jane.Smith, done!" # Comment with @mention
basecamp projects # Styled output in terminal, JSON when piped
basecamp projects --json # JSON with envelope and breadcrumbs
basecamp projects --quiet # Raw JSON data only
Every command supports --json for structured output:
{
"ok": true,
"data": [...],
"summary": "5 projects",
"breadcrumbs": [{"action": "show", "cmd": "basecamp projects show <id>"}]
}
Breadcrumbs suggest next commands, making it easy for humans and agents to navigate.
Errors use the same envelope with ok: false, a stable code, and retryable,
which says whether a retry can change the outcome:
{
"ok": false,
"error": "Gateway error (503)",
"code": "api_error",
"retryable": true,
"hint": "..."
}
Key order within the envelope is not part of the contract — the interactive (TTY) path re-encodes through a map and alphabetizes keys — so match on key names, never on position.
retryable is present on every error envelope — true when the CLI classified
the failure transient (network, timeout, rate limit, circuit open, and most
5xx/gateway responses — not all: 507 and some 500s are verdicts), false for a
verdict (usage, not found, auth, forbidden, validation, account limit) and for
any error nothing classified — and never on a success envelope. Key on it rather
than on the code or message when deciding whether to retry; false means no
known reason a retry would help, not a guarantee the failure is permanent.
OAuth 2.1 with automatic token refresh. First login opens your browser. When the server advertises the OAuth device flow, login uses it automatically: you approve a short code in the browser instead of a redirect. Login falls back to Launchpad's authorization-code flow only when no modern OAuth issuer is advertised for the server; once a modern issuer is selected, login failures surface loudly rather than silently falling back.
basecamp auth login # Authenticate with Basecamp (full access)
basecamp auth login --scope read # Read-only access (ignored by Launchpad)
basecamp auth login --scope full # Full read+write access (default; ignored by Launchpad)
basecamp auth token # Print token for scripts
--expect-identity <id> makes any login assert who it authenticated as: the
new credential is checked before it is stored, and on a mismatch nothing is
written (a profile's previous credential is untouched) and the command exits
non-zero. basecamp profile create <name> --expect-identity <id> does the same
for a profile that does not exist yet. --login-hint <email> names the account to sign in as on the
device-flow approval page (sent as login_hint; it steers the sign-in page and
never authenticates on its own; ignored by Launchpad).
A personal access token can be imported instead of running OAuth — the shape for bots, CI, and any machine that should never sign in interactively. The token is read from stdin (never an argument), verified against the server — who it authenticates as, and that it can reach the profile's account — and only then stored under a named profile, with whatever expiry the server reports for it:
op read "op://Vault/Item/credential" | basecamp auth login --with-token -P bot --account 999
op read "op://Vault/Item/credential" | basecamp auth login --with-token -P bot --account 999 --expect-identity 12345 --json
--account is required when the profile does not exist yet. --json returns
an envelope with the profile, account, identity and person, oauth_type,
scope, and expires_at (the expiry the server reports for the token, or
null when it reports none). A token has no refresh token, so near a reported
expiry the CLI refuses it and asks for a fresh import.
A Basecamp agent is a principal with no person behind it, so it has no browser
to sign in with and is granted no refresh token. It authenticates with the
OAuth client_credentials grant instead: its client id and secret mint a
short-lived self-token, and the CLI mints another whenever that one nears
expiry. The client secret is read from stdin (never an argument) and stored
with the token it mints — in the OS keyring where one is available — because
the client, not a refresh token, is what survives an expiry:
op read "op://Vault/Item/credential" | basecamp auth login --with-client-credentials --client-id <id> -P agent --account 999
The login mints once, which is what proves the client id and secret: a refused
mint stores nothing. --account is required when the profile does not exist
yet. basecamp auth logout forgets the credential; there is no useful
revocation, since the same client would mint another. To end its access,
disconnect its connection in Basecamp (on My > Agent for your own agent, in
Adminland for a shared one), which kills that connection's secret and every
token minted from it. Where My > Agent lists several connections, disconnect
the one this profile used: Disconnect all ends every one of them.
You don't paste an agent's secret anywhere: you approve the connection in your
browser and the CLI keeps the credential. It needs CLI 0.13.0 or later; check
with basecamp --version and run basecamp upgrade first if it's older.
In a terminal, one command does it all:
basecamp connect setup
It connects this computer to your agent (one approval in the browser), records who may give it work (for your personal agent, you) and which projects it
A command line tool for HEY that lets you read and send email, manage contacts, labels, calendars, todos, habits, time tracking and journal entries from the terminal or from your coding agent.
Gives you a ready Linux desktop setup with Hyprland theming and config, plus help customizing its files, diagnosing program crashes from core dumps, and building new desktop apps for it.
A skill that shapes Claude Code's output for readers with ADHD: it leads with the next action, numbers multi-step work, restates state across turns, drops tangents, gives specific time estimates, and makes progress visible. Invoke it with /i-h.
Gives your AI agent working knowledge of Obsidian, so it can read and write notes, build Bases database views and JSON Canvas mind maps, and turn web pages into clean Markdown for your vault.
Gives product managers step-by-step help with discovery, prioritization, strategy, launch and growth work, so they get structured outputs instead of loose text. Also includes skills for reviewing AI-built code before shipping.
Formerly: Colleague Skill / colleague-skill.
Install it with the command on its page. It includes basecamp-connect, basecamp-doctor, basecamp. Its skills do not fire on their own yet. Free and open source.
Is this plugin yours?
Claim it with GitHubSubmit a pluginPromote it