You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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).
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.
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, a 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.
chat list --all no longer implies pings are project chats and points to pings list.
@mentions in a ping post resolve among the ping's people only.
TUI Pings view now builds rooms from the Pings API; rooms carry a Title since pings can be groups.
pings line/update/delete are intentionally not added — edit and delete ping lines with chat update/chat delete on the message URL, which now work.
Skill, README, API coverage, .surface, and smoke tests updated.
Dependencies
Requires basecamp/bc3#13943 (GET /circles.json); until it deploys, pings list returns empty in production, while show and messages by id or URL already work.
Temporary replace onto basecamp-sdk circles-service (9f265cd), which sits
on SDK 0.25.0. Also adapts lineup.go to the 0.25.0 Create/UpdateMarker
return values. Drop this commit and run 'make bump-sdk' once the SDK
releases the Pings service.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A Ping's web URL is circles/{ping} or circles/{ping}@{line}. The router
sees the @ form as a type named after the whole segment, so urlarg now
rewrites it the way it does chats/{room}@{line}: a Ping is its own
bucket, so the form parses as a chat line in that bucket, with the Ping
in the new PingID field. The Campfire is not in the URL; it comes from
the Ping.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The Pings view built its rooms from the account Campfire listing, which
never returns a ping's Campfire, so it was always empty. Build them from
Pings().List instead, with each ping's Campfire from its room URL; the
ping is its own bucket, so it is the room's project. Pings can be groups,
so the room carries a Title rather than a person's name, and the wording
no longer calls them 1:1 threads.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
basecamp pings list, show, start (alias with), messages, post and upload
cover a Ping: a private conversation, direct or group, held in a Circle
bucket with one Campfire. A <ping> is an id, a ping URL, or a person; a
bare number is always a ping id. start opens or reuses (the API returns
the existing ping), reporting people Basecamp left out and the 403 and
422 refusals. messages, post and upload run through chat's own code on
the ping's Campfire: chat's post, messages and upload now take a resolved
Campfire, the commands that continue from it, and a mention resolver, so
@mentions in a ping resolve among its people.
show and chat line take ping URLs (circles/{id}, circles/{id}@{line}); a
bucket with no dock that is a Ping now resolves to its Campfire, so
chat messages --in <ping> works; chat list --all says pings are not in
it; and the skill, README, API coverage, surface and smoke coverage
follow.
The mcpserver composite test gains the /circles probe the SDK now makes
for a bucket without a dock.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds private Pings conversations to the CLI and connects the TUI Pings view to the dedicated API.
Changes:
Adds six pings commands with person resolution and ping-scoped mentions.
Supports ping URLs through existing show/chat workflows and updates TUI discovery.
Adds documentation, command registration, and test coverage.
[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Basecamp also links a ping as {account}/my/sidebar/circles/{id}, with
circles/{id}@{line} under the same prefix. Drop the my/sidebar prefix
before routing so pings, show and chat parse it like the plain form.
A numeric target of 0 falls through to person resolution with an empty name. The name resolver matches an empty string against everyone, so an account with exactly one pingable person resolves that person. Consequently, pings post 0 "message" can start their ping and send the message to an unintended recipient. Distinguish numeric targets using the empty person field so zero remains an ID, rather than becoming a person lookup. Add a regression test with one pingable person that verifies no ping or message is created.
Preserve recording fragments in ping URL dispatch
internal/commands/show.go:70
This early return bypasses the recording-fragment handling immediately below it. For example, show https://app.basecamp.com/123/circles/456#__recording_111 shows ping 456 instead of recording 111, contrary to the command's documented fragment behavior. Dispatch directly to showPingURL only when no recording fragment is present, and add a regression test for this URL form.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Makes Pings usable from the CLI. Until now a ping could only be found through search,
chat showandshow <ping url>failed on one,chat linerefused/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
/circlesand the bucket type isCircle; that shows up only in the API docs and the agent notes.Commands
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,showandmessagesread the direct ping frompings listand never start one;postanduploadstart it if missing.pings startrelies on the API reusing: one direct ping per person, one group per set of people unless--new-group. Sopings start Jasonis 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. 403Pings have been disabledand the agent 422 get plain messages.messages,postanduploadrun through chat's own code on the ping's Campfire.post,messagesanduploadinchat.gonow take a resolved Campfire, the commands that continue from it (so breadcrumbs saypings ...), 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.chat messages --in <ping>works.chat list --alland its help say pings are not in the list and point topings list.PingRoomsis built fromPings().List; rooms carry aTitle(pings can be groups) and the wording no longer says 1:1..surface,API-COVERAGE.md, README, smoke coverage and a smalle2e/pings.bats.Not done
pings line,pings updateandpings deleteare not added. Messages in a ping are edited and deleted withchat updateandchat deleteon the message URL, which now work for ping URLs, so a second spelling would only duplicate chat's confirmation and stdin handling.Dependencies
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).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 (deps: bump github.com/basecamp/basecamp-sdk/go from 0.24.0 to 0.25.0 in the go-dependencies group #911) needs the same change./circlesroute: with the SDK change, chat-line discovery probes a bucket as a ping before falling back to the account listing.Verification
@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.--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.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
pingscommand group —list,show,start,messages,post, andupload— for private conversations outside projects, so pings are reachable from the CLI instead of only through search. A<ping>is a ping id, a ping URL, or a person's name;startreuses the API's existing direct or group ping (unless--new-group), andpost/uploadstart one if missing. This also fixesshowandchat linefailing 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.chat list --allno longer implies pings are project chats and points topings list.Titlesince pings can be groups.pings line/update/deleteare intentionally not added — edit and delete ping lines withchat update/chat deleteon the message URL, which now work..surface, and smoke tests updated.Dependencies
GET /circles.json); until it deploys,pings listreturns empty in production, whileshowandmessagesby id or URL already work.replacein a "DO NOT MERGE" commit; drop the commit and runmake bump-sdkonce the SDK releases.Written for commit 22891ab. Summary will update on new commits.