Add pings commands: list, show, start, messages, post, upload
What this does
Makes Pings usable from the CLI. Until now a ping could only be found through search, chat show and show <ping url> failed on one, chat line refused /circles/{id}@{line} URLs, and the TUI Pings view was always empty (it filtered the account Campfire listing, which never returns a ping's Campfire).
"Ping" is the name everywhere a person reads it. The wire path is /circles and the bucket type is Circle; that shows up only in the API docs and the agent notes.
Commands
basecamp pings list
basecamp pings show <ping>
basecamp pings start <person>... [--new-group] (alias: with)
basecamp pings messages <ping> [-n N]
basecamp pings post <ping> [message] [--attach FILE] [--content-type T]
basecamp pings upload <ping> <file>
A <ping> is a ping id, a ping URL (https://app.basecamp.com/{account}/circles/{id}, 3.basecamp.com and the @{line} form too), or a person (name, email or person id), meaning your direct ping with them. A bare number is always a ping id. For a person, show and messages read the direct ping from pings list and never start one; post and upload start it if missing.
pings start relies on the API reusing: one direct ping per person, one group per set of people unless --new-group. So pings start Jason is also "open my ping with Jason". People are resolved among the people you can ping (people pingable), and people Basecamp drops from the ping are reported. 403 Pings have been disabled and the agent 422 get plain messages.
messages, post and upload run through chat's own code on the ping's Campfire. post, messages and upload in chat.go now take a resolved Campfire, the commands that continue from it (so breadcrumbs say pings ...), and a mention resolver. @mentions in a ping post resolve among the ping's people only.
Also:
show <ping url>shows the ping;showandchat line|update|deletetakecircles/{id}@{line}message URLs.- A bucket with no dock that is a ping resolves to its Campfire, so
chat messages --in <ping>works. chat list --alland its help say pings are not in the list and point topings list.- TUI:
PingRoomsis built fromPings().List; rooms carry aTitle(pings can be groups) and the wording no longer says 1:1. - Skill: new Pings section and triggers. The skill had no ping workflow before.
.surface,API-COVERAGE.md, README, smoke coverage and a smalle2e/pings.bats.
Not done
pings line, pings update and pings delete are not added. Messages in a ping are edited and deleted with chat update and chat delete on the message URL, which now work for ping URLs, so a second spelling would only duplicate chat's confirmation and stdin handling.
Dependencies
- basecamp/bc3#13943 puts
GET /circles.jsonon the API host. Until it deploys,pings listreturns an empty list in production;showandmessagesby id or URL already work there (read-only checks;postandstartwere not run against production). - basecamp/basecamp-sdk#1015 adds
Pings(). This branch builds against it through areplace, in its own first commit, "DO NOT MERGE: build against unreleased SDK pings branch". Once the SDK releases, drop that commit and runmake bump-sdk. That commit also adaptslineup.goto the SDK 0.25.0 return values ofCreateMarkerandUpdateMarker; the open dependabot bump to 0.25.0 (#911) needs the same change. - The mcpserver composite test gains a
/circlesroute: with the SDK change, chat-line discovery probes a bucket as a ping before falling back to the account listing.
Verification
- Unit tests with httptest fakes: URL parsing (both hosts, local dev,
@line), people resolution, direct reuse, group and--new-group, dropped people, 403 and 422, mention resolution among the ping's people, read-by-person never creating,showandchat lineon ping URLs, the chat dock fallback, and the TUI room fetch. - End to end against bc3 dev with the built binary, as two users: list, start (direct twice returns the same id; group;
--new-group), post by id, URL and person, messages,showon app and web URLs,chat lineon a message URL, upload,chat deleteon a ping line, and the second user sees the lines. - Production, read-only:
pings showandpings messagesof a real ping,showon its URL;pings listis empty until #13943 deploys. bin/ci: every gate passes exceptlintwith the local golangci-lint 2.15.0, which flagsinternal/connector/admission/policy.go:415(capshadows a predeclared identifier). That file is untouched here and CI pins 2.11.1. The SDK provenance check is skipped while thereplaceis active.
🤖 Generated with Claude Code
Summary by cubic
Adds a pings command group — list, show, start, messages, post, and upload — for private conversations outside projects, so pings are reachable from the CLI instead of only through search. A <ping> is a ping id, ping URL, or a person's name; start reuses the API's existing direct or group ping (unless --new-group), and post/upload start one if missing. This also fixes show and chat line failing on ping URLs, and the TUI Pings view being always empty because it filtered the account Campfire listing, which never returns a ping's Campfire.
showandchat linenow take ping URLs (circles/{id},circles/{id}@{line}, and themy/sidebarlink form);chat messages --in <ping>works through a dock fallback for ping buckets.- On ping line URLs the chat commands resolve the ping to its Campfire first, so the URL's room wins over a stale
--room. chat list --allno longer implies pings are project chats and points topings list.- @mentions resolve among the ping's people only, in both
pings postandchat updateon a ping line URL. - TUI Pings view now builds rooms from the Pings API; rooms carry a
Titlesince pings can be groups. - Ping lines are edited and deleted with
chat update/chat deleteon the message URL, sopings line/update/deleteare intentionally not added.
Dependencies
- Requires basecamp/bc3#13943, which puts the
/circlesendpoints on the API host; until it deploys,pings listreturns empty in production, whileshowandmessagesby id or URL already work. - Builds against the released basecamp/basecamp-sdk v0.26.0, which adds the Pings service, resyncs the MCP model (boosts gain
update_boost), and adaptslineup.gotoCreateMarker/UpdateMarkerreturning the saved marker.