List a chat's files, and manage its chatbots

JE
4d ago
+1728-11

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

OperationCommand
ListCampfireUploadsbasecamp chat uploads
ListChatbotsbasecamp chat bots list
GetChatbotbasecamp chat bots show <id>
CreateChatbotbasecamp chat bots create <name> [--command-url URL]
UpdateChatbotbasecamp chat bots update <id> [--name NAME] [--command-url URL]
DeleteChatbotbasecamp chat bots delete <id> [--force]
  • All of them take the chat from --room or the project's one chat, and the project from --in, as the other chat commands do. Chatbot routes name the project, so it's resolved even when --room is given.
  • chat uploads returns the chat lines that carried a file, newest first (asked for explicitly, so --limit keeps the latest): 100 by default, or --limit, --all or --page. Contradictory flags are refused before any request.
  • update reads 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. update with nothing to change is refused.
  • delete is permanent and account-wide. Like chat delete, it asks for confirmation, or needs --force where 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_url holds the bot's key, so anyone who has it can post as the bot and it should be treated like a password. The CLI prints lines_url as 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_url and command_url in the styled list, the Markdown list and the detail view. Every follow-up command carries --profile and --account where a fresh process wouldn't pick them up.
  • bot is an alias of bots. Catalog entry, .surface, and API-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.md isn'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; uploads asks for newest first, and --page sends the page; every command's follow-ups carry the profile; conflicting uploads flags are refused with nothing sent; create with and without --command-url; update keeps 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; delete without --force in machine output sends nothing, and with it sends one DELETE; a 403 on create comes 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 naming lines_url's key and the administrator requirement.
  • Smoke (e2e/smoke/smoke_campfire.bats): uploads after 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 a forbidden answer, since the smoke user may not be an administrator, and .qa-allowlist names 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 bot is marked as an alias in smoke_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 uploads requests files newest first so a --limit keeps the latest ones.
  • update reads 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.
  • delete is permanent and account-wide, so it asks for confirmation or needs --force where 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_url carries 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.