Add pings commands: list, show, start, messages, post, upload

Merged
RO
1d ago
+3342-230

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; show and chat line|update|delete take circles/{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 --all and its help say pings are not in the list and point to pings list.
  • TUI: PingRooms is built from Pings().List; rooms carry a Title (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 small e2e/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.json on the API host. Until it deploys, pings list returns an empty list in production; show and messages by id or URL already work there (read-only checks; post and start were not run against production).
  • basecamp/basecamp-sdk#1015 adds Pings(). This branch builds against it through a replace, in its own first commit, "DO NOT MERGE: build against unreleased SDK pings branch". Once the SDK releases, drop that commit and run make bump-sdk. That commit also adapts lineup.go to the SDK 0.25.0 return values of CreateMarker and UpdateMarker; the open dependabot bump to 0.25.0 (#911) needs the same change.
  • The mcpserver composite test gains a /circles route: 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, show and chat line on 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, show on app and web URLs, chat line on a message URL, upload, chat delete on a ping line, and the second user sees the lines.
  • Production, read-only: pings show and pings messages of a real ping, show on its URL; pings list is empty until #13943 deploys.
  • bin/ci: every gate passes except lint with the local golangci-lint 2.15.0, which flags internal/connector/admission/policy.go:415 (cap shadows a predeclared identifier). That file is untouched here and CI pins 2.11.1. The SDK provenance check is skipped while the replace is 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.

  • show and chat line now take ping URLs (circles/{id}, circles/{id}@{line}, and the my/sidebar link 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 --all no longer implies pings are project chats and points to pings list.
  • @mentions resolve among the ping's people only, in both pings post and chat update on a ping line URL.
  • TUI Pings view now builds rooms from the Pings API; rooms carry a Title since pings can be groups.
  • Ping lines are edited and deleted with chat update/chat delete on the message URL, so pings line/update/delete are intentionally not added.

Dependencies

  • Requires basecamp/bc3#13943, which puts the /circles endpoints on the API host; until it deploys, pings list returns empty in production, while show and messages by 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 adapts lineup.go to CreateMarker/UpdateMarker returning the saved marker.