List a chat's files, and manage its chatbots
basecamp chat uploads lists the files posted in a chat, and basecamp chat bots lists, shows, adds, renames and deletes its chatbots.
Why
[CLI] Campfire chatbots and the uploads list: the coverage report (basecamp-sdk#1055) lists six SDK operations the CLI doesn't reach. API-COVERAGE.md skipped chatbots because they "require chatbot key, not OAuth", but that's only true of posting as a chatbot. Managing them goes through OAuth, which is how the MCP server already serves all five as *_chatbot tools.
The decision this PR asks for: whether the CLI should manage chatbots at all. It reverses a recorded skip, so it's a person's call, not this job's. If the answer is no, drop chat_bots.go's bots group and the chatbots rows, and chat uploads stands alone.
What changed
| Operation | Command |
|---|---|
ListCampfireUploads | basecamp chat uploads |
ListChatbots | basecamp chat bots list |
GetChatbot | basecamp chat bots show <id> |
CreateChatbot | basecamp chat bots create <name> [--command-url URL] |
UpdateChatbot | basecamp chat bots update <id> [--name NAME] [--command-url URL] |
DeleteChatbot | basecamp chat bots delete <id> [--force] |
- All of them take the chat from
--roomor the project's one chat, and the project from--in, as the otherchatcommands do. Chatbot routes name the project, so it's resolved even when--roomis given. chat uploadsreturns the chat lines that carried a file, newest first (asked for explicitly, so--limitkeeps the latest): 100 by default, or--limit,--allor--page. Contradictory flags are refused before any request.updatereads the chatbot first and sends back whatever you didn't change, because the API takes the name on every update. Without that, a rename might clear the command URL. The SDK leaves out an empty command URL, so you can change one but not remove it, and the help says so.updatewith nothing to change is refused.deleteis permanent and account-wide. Likechat delete, it asks for confirmation, or needs--forcewhere nothing can answer.- The group's help and agent notes say that chatbots belong to the account, that managing them takes an administrator, and that
lines_urlholds the bot's key, so anyone who has it can post as the bot and it should be treated like a password. The CLI printslines_urlas the API returns it, as the MCP server does: handing it to whoever runs the bot is the point of creating one. - Chatbots render through their own presenter schema, which keeps the name,
lines_urlandcommand_urlin the styled list, the Markdown list and the detail view. Every follow-up command carries--profileand--accountwhere a fresh process wouldn't pick them up. botis an alias ofbots. Catalog entry,.surface, andAPI-COVERAGE.md: chatbot management and chat uploads are now covered rows. Posting as a chatbot stays out of scope for the old reason, and the notes now explain the difference. Summary counts go to 227 of 227.skills/basecamp/SKILL.mdisn't touched: it's 130 bytes under its 16 KB limit, and the help covers these commands.
Builds on
Main at f45d3c8e, whose pinned SDK (v0.34.0) has all six operations. No bump in this PR.
Left out
Nothing from the card. Posting as a chatbot (POST to lines_url with the key) isn't an operation on the card and stays out of scope.
Tests
- Unit (
internal/commands/chat_bots_test.go): each command's method, path and body;uploadsasks for newest first, and--pagesends the page; every command's follow-ups carry the profile; conflictinguploadsflags are refused with nothing sent;createwith and without--command-url;updatekeeps the field you leave out, in all three combinations; an empty name, an empty command URL, nothing to change and a non-numeric or zero ID are each refused before any request;deletewithout--forcein machine output sends nothing, and with it sends one DELETE; a 403 oncreatecomes back as an error with nothing printed and no retry. - Presenter (
internal/presenter/presenter_test.go): the chatbot schema keeps the name and both URLs in each view. - e2e (
e2e/chat.bats): the usage refusals, asserting the exact error and hint, and the help text naminglines_url's key and the administrator requirement. - Smoke (
e2e/smoke/smoke_campfire.bats):uploadsafter the existing upload, and a chatbot lifecycle (create, list, show, update, delete--force). The file now runs its tests in order (BATS_NO_PARALLELIZE_WITHIN_FILE), which the existing line lifecycle also relied on. Create is marked unverifiable only on aforbiddenanswer, since the smoke user may not be an administrator, and.qa-allowlistnames it and the three tests that read its bot, as it does for timesheets and lineup. Written but not run, since smoke needs a live account.chat botis marked as an alias insmoke_aliases.bats.
On thelio, bin/ci passes fmt, vet, golangci-lint (0 issues), actionlint and zizmor. Its unit tests fail only in TestHardenConfigDir (internal/cli, two subtests). That's the same failure #992 recorded, and it reproduces on a clean export of f45d3c8e on that machine; this PR doesn't touch internal/cli. The rest of make check (e2e, surface, skill drift, smoke coverage, naming, provenance, tidy) passes. Remote CI is the check for TestHardenConfigDir.
Gap card
https://app.basecamp.com/2914079/buckets/48521764/card_tables/cards/10447988117 (the MCP server already serves the chatbot operations)
Summary by cubic
Adds basecamp chat uploads to list files posted in a chat, and basecamp chat bots to list, show, create, rename, and delete chatbots. Chatbot management works through OAuth as an administrator's call, so it reverses the prior reasoning that skipped chatbots; only posting as a chatbot, which needs the chatbot key, stays out of scope. Reviewers get to decide whether chatbot management belongs in the CLI — if not, drop the bots group and keep chat uploads standalone.
Notable behaviors
chat uploadsrequests files newest first so a--limitkeeps the latest ones.updatereads the chatbot first and sends back what you didn't change, since the API takes the name on every update; a command URL can be changed but not removed.deleteis permanent and account-wide, so it asks for confirmation or needs--forcewhere nothing can answer.- Chatbots render through their own presenter schema that keeps the name and both URLs in every view.
- Help text warns that
lines_urlcarries the bot's key and should be treated like a password. - The four chatbot smoke tests are allowlisted in CI, since a non-administrator smoke token gets forbidden on create and the rest depend on it.