Sitelet https://github.com/basecamp/basecamp-cli/pull/705/files
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ go = "1.26"
goreleaser = "2.15.4"
# Keep in sync with .github/workflows/test.yml rhysd/actionlint version pin.
actionlint = "1.7.12"
# Runs the e2e suite. Keep in sync with the bats-core tag installed in
# .github/workflows/test.yml and .github/workflows/release.yml.
bats = "1.11.0"
# Not invoked directly; actionlint shells out to it to lint workflow `run:`
# scripts (CI uses the runner's preinstalled shellcheck).
shellcheck = "0.11.0"
Expand Down
1,120 changes: 868 additions & 252 deletions .surface

Large diffs are not rendered by default.

252 changes: 252 additions & 0 deletions .surface-breaking
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ ARG basecamp message 00 <title>
ARG basecamp message 01 [body]
ARG basecamp react 00 <content>
ARG basecamp reopen 00 <id|url>...
ARG basecamp templates construct 00 <template_id>
ARG basecamp templates construction 00 <template_id>
ARG basecamp templates construction 01 <construction_id>
ARG basecamp templates copy 00 <template_id>
ARG basecamp templates copy-status 00 <copy_id>
ARG basecamp templates create 00 <name>
ARG basecamp templates delete 00 <id>
ARG basecamp templates show 00 <id>
ARG basecamp templates update 00 <id>
ARG basecamp todo 00 <content>
ARG basecamp unassign 00 <id>
ARG basecamp unassign 00 <todo_id>
Expand Down Expand Up @@ -74,6 +83,16 @@ CMD basecamp mcp
CMD basecamp message
CMD basecamp react
CMD basecamp reopen
CMD basecamp templates construct
CMD basecamp templates construction
CMD basecamp templates copy
CMD basecamp templates copy-status
CMD basecamp templates create
CMD basecamp templates delete
CMD basecamp templates library
CMD basecamp templates list
CMD basecamp templates show
CMD basecamp templates update
CMD basecamp todo
CMD basecamp upload archive
CMD basecamp upload doc
Expand Down Expand Up @@ -749,12 +768,235 @@ FLAG basecamp reopen --todolist type=string
FLAG basecamp reopen --verbose type=count
FLAG basecamp reopen --version type=bool
FLAG basecamp templates --status type=string
FLAG basecamp templates construct --account type=string
FLAG basecamp templates construct --agent type=bool
FLAG basecamp templates construct --cache-dir type=string
FLAG basecamp templates construct --count type=bool
FLAG basecamp templates construct --desc type=string
FLAG basecamp templates construct --description type=string
FLAG basecamp templates construct --help type=bool
FLAG basecamp templates construct --hints type=bool
FLAG basecamp templates construct --ids-only type=bool
FLAG basecamp templates construct --in type=string
FLAG basecamp templates construct --jq type=string
FLAG basecamp templates construct --json type=bool
FLAG basecamp templates construct --markdown type=bool
FLAG basecamp templates construct --md type=bool
FLAG basecamp templates construct --name type=string
FLAG basecamp templates construct --no-hints type=bool
FLAG basecamp templates construct --no-stats type=bool
FLAG basecamp templates construct --profile type=string
FLAG basecamp templates construct --project type=string
FLAG basecamp templates construct --quiet type=bool
FLAG basecamp templates construct --start-date type=string
FLAG basecamp templates construct --stats type=bool
FLAG basecamp templates construct --status type=string
FLAG basecamp templates construct --styled type=bool
FLAG basecamp templates construct --todolist type=string
FLAG basecamp templates construct --verbose type=count
FLAG basecamp templates construction --account type=string
FLAG basecamp templates construction --agent type=bool
FLAG basecamp templates construction --cache-dir type=string
FLAG basecamp templates construction --count type=bool
FLAG basecamp templates construction --help type=bool
FLAG basecamp templates construction --hints type=bool
FLAG basecamp templates construction --ids-only type=bool
FLAG basecamp templates construction --in type=string
FLAG basecamp templates construction --jq type=string
FLAG basecamp templates construction --json type=bool
FLAG basecamp templates construction --markdown type=bool
FLAG basecamp templates construction --md type=bool
FLAG basecamp templates construction --no-hints type=bool
FLAG basecamp templates construction --no-stats type=bool
FLAG basecamp templates construction --profile type=string
FLAG basecamp templates construction --project type=string
FLAG basecamp templates construction --quiet type=bool
FLAG basecamp templates construction --stats type=bool
FLAG basecamp templates construction --status type=string
FLAG basecamp templates construction --styled type=bool
FLAG basecamp templates construction --todolist type=string
FLAG basecamp templates construction --verbose type=count
FLAG basecamp templates copy --account type=string
FLAG basecamp templates copy --agent type=bool
FLAG basecamp templates copy --cache-dir type=string
FLAG basecamp templates copy --confirm-adding-people type=bool
FLAG basecamp templates copy --count type=bool
FLAG basecamp templates copy --help type=bool
FLAG basecamp templates copy --hints type=bool
FLAG basecamp templates copy --ids-only type=bool
FLAG basecamp templates copy --in type=string
FLAG basecamp templates copy --jq type=string
FLAG basecamp templates copy --json type=bool
FLAG basecamp templates copy --markdown type=bool
FLAG basecamp templates copy --md type=bool
FLAG basecamp templates copy --no-hints type=bool
FLAG basecamp templates copy --no-stats type=bool
FLAG basecamp templates copy --profile type=string
FLAG basecamp templates copy --project type=string
FLAG basecamp templates copy --quiet type=bool
FLAG basecamp templates copy --stats type=bool
FLAG basecamp templates copy --styled type=bool
FLAG basecamp templates copy --todolist type=string
FLAG basecamp templates copy --todoset type=string
FLAG basecamp templates copy --verbose type=count
FLAG basecamp templates copy-status --account type=string
FLAG basecamp templates copy-status --agent type=bool
FLAG basecamp templates copy-status --cache-dir type=string
FLAG basecamp templates copy-status --count type=bool
FLAG basecamp templates copy-status --help type=bool
FLAG basecamp templates copy-status --hints type=bool
FLAG basecamp templates copy-status --ids-only type=bool
FLAG basecamp templates copy-status --in type=string
FLAG basecamp templates copy-status --jq type=string
FLAG basecamp templates copy-status --json type=bool
FLAG basecamp templates copy-status --markdown type=bool
FLAG basecamp templates copy-status --md type=bool
FLAG basecamp templates copy-status --no-hints type=bool
FLAG basecamp templates copy-status --no-stats type=bool
FLAG basecamp templates copy-status --profile type=string
FLAG basecamp templates copy-status --project type=string
FLAG basecamp templates copy-status --quiet type=bool
FLAG basecamp templates copy-status --stats type=bool
FLAG basecamp templates copy-status --styled type=bool
FLAG basecamp templates copy-status --todolist type=string
FLAG basecamp templates copy-status --verbose type=count
FLAG basecamp templates create --account type=string
FLAG basecamp templates create --agent type=bool
FLAG basecamp templates create --cache-dir type=string
FLAG basecamp templates create --count type=bool
FLAG basecamp templates create --desc type=string
FLAG basecamp templates create --description type=string
FLAG basecamp templates create --help type=bool
FLAG basecamp templates create --hints type=bool
FLAG basecamp templates create --ids-only type=bool
FLAG basecamp templates create --in type=string
FLAG basecamp templates create --jq type=string
FLAG basecamp templates create --json type=bool
FLAG basecamp templates create --markdown type=bool
FLAG basecamp templates create --md type=bool
FLAG basecamp templates create --name type=string
FLAG basecamp templates create --no-hints type=bool
FLAG basecamp templates create --no-stats type=bool
FLAG basecamp templates create --profile type=string
FLAG basecamp templates create --project type=string
FLAG basecamp templates create --quiet type=bool
FLAG basecamp templates create --stats type=bool
FLAG basecamp templates create --status type=string
FLAG basecamp templates create --styled type=bool
FLAG basecamp templates create --todolist type=string
FLAG basecamp templates create --verbose type=count
FLAG basecamp templates delete --account type=string
FLAG basecamp templates delete --agent type=bool
FLAG basecamp templates delete --cache-dir type=string
FLAG basecamp templates delete --count type=bool
FLAG basecamp templates delete --help type=bool
FLAG basecamp templates delete --hints type=bool
FLAG basecamp templates delete --ids-only type=bool
FLAG basecamp templates delete --in type=string
FLAG basecamp templates delete --jq type=string
FLAG basecamp templates delete --json type=bool
FLAG basecamp templates delete --markdown type=bool
FLAG basecamp templates delete --md type=bool
FLAG basecamp templates delete --no-hints type=bool
FLAG basecamp templates delete --no-stats type=bool
FLAG basecamp templates delete --profile type=string
FLAG basecamp templates delete --project type=string
FLAG basecamp templates delete --quiet type=bool
FLAG basecamp templates delete --stats type=bool
FLAG basecamp templates delete --status type=string
FLAG basecamp templates delete --styled type=bool
FLAG basecamp templates delete --todolist type=string
FLAG basecamp templates delete --verbose type=count
FLAG basecamp templates library --account type=string
FLAG basecamp templates library --agent type=bool
FLAG basecamp templates library --cache-dir type=string
FLAG basecamp templates library --count type=bool
FLAG basecamp templates library --help type=bool
FLAG basecamp templates library --hints type=bool
FLAG basecamp templates library --ids-only type=bool
FLAG basecamp templates library --in type=string
FLAG basecamp templates library --jq type=string
FLAG basecamp templates library --json type=bool
FLAG basecamp templates library --markdown type=bool
FLAG basecamp templates library --md type=bool
FLAG basecamp templates library --no-hints type=bool
FLAG basecamp templates library --no-stats type=bool
FLAG basecamp templates library --profile type=string
FLAG basecamp templates library --project type=string
FLAG basecamp templates library --quiet type=bool
FLAG basecamp templates library --stats type=bool
FLAG basecamp templates library --styled type=bool
FLAG basecamp templates library --todolist type=string
FLAG basecamp templates library --verbose type=count
FLAG basecamp templates list --account type=string
FLAG basecamp templates list --agent type=bool
FLAG basecamp templates list --cache-dir type=string
FLAG basecamp templates list --count type=bool
FLAG basecamp templates list --help type=bool
FLAG basecamp templates list --hints type=bool
FLAG basecamp templates list --ids-only type=bool
FLAG basecamp templates list --in type=string
FLAG basecamp templates list --jq type=string
FLAG basecamp templates list --json type=bool
FLAG basecamp templates list --markdown type=bool
FLAG basecamp templates list --md type=bool
FLAG basecamp templates list --no-hints type=bool
FLAG basecamp templates list --no-stats type=bool
FLAG basecamp templates list --profile type=string
FLAG basecamp templates list --project type=string
FLAG basecamp templates list --quiet type=bool
FLAG basecamp templates list --stats type=bool
FLAG basecamp templates list --status type=string
FLAG basecamp templates list --styled type=bool
FLAG basecamp templates list --todolist type=string
FLAG basecamp templates list --verbose type=count
FLAG basecamp templates show --account type=string
FLAG basecamp templates show --agent type=bool
FLAG basecamp templates show --cache-dir type=string
FLAG basecamp templates show --count type=bool
FLAG basecamp templates show --help type=bool
FLAG basecamp templates show --hints type=bool
FLAG basecamp templates show --ids-only type=bool
FLAG basecamp templates show --in type=string
FLAG basecamp templates show --jq type=string
FLAG basecamp templates show --json type=bool
FLAG basecamp templates show --markdown type=bool
FLAG basecamp templates show --md type=bool
FLAG basecamp templates show --no-hints type=bool
FLAG basecamp templates show --no-stats type=bool
FLAG basecamp templates show --profile type=string
FLAG basecamp templates show --project type=string
FLAG basecamp templates show --quiet type=bool
FLAG basecamp templates show --stats type=bool
FLAG basecamp templates show --status type=string
FLAG basecamp templates show --styled type=bool
FLAG basecamp templates show --todolist type=string
FLAG basecamp templates show --verbose type=count
FLAG basecamp templates update --account type=string
FLAG basecamp templates update --agent type=bool
FLAG basecamp templates update --cache-dir type=string
FLAG basecamp templates update --count type=bool
FLAG basecamp templates update --desc type=string
FLAG basecamp templates update --description type=string
FLAG basecamp templates update --help type=bool
FLAG basecamp templates update --hints type=bool
FLAG basecamp templates update --ids-only type=bool
FLAG basecamp templates update --in type=string
FLAG basecamp templates update --jq type=string
FLAG basecamp templates update --json type=bool
FLAG basecamp templates update --markdown type=bool
FLAG basecamp templates update --md type=bool
FLAG basecamp templates update --name type=string
FLAG basecamp templates update --no-hints type=bool
FLAG basecamp templates update --no-stats type=bool
FLAG basecamp templates update --profile type=string
FLAG basecamp templates update --project type=string
FLAG basecamp templates update --quiet type=bool
FLAG basecamp templates update --stats type=bool
FLAG basecamp templates update --status type=string
FLAG basecamp templates update --styled type=bool
FLAG basecamp templates update --todolist type=string
FLAG basecamp templates update --verbose type=count
FLAG basecamp timeline --interval type=int
FLAG basecamp timeline --watch type=bool
FLAG basecamp tlgroup create --name type=string
Expand Down Expand Up @@ -1085,6 +1327,16 @@ SUB basecamp mcp
SUB basecamp message
SUB basecamp react
SUB basecamp reopen
SUB basecamp templates construct
SUB basecamp templates construction
SUB basecamp templates copy
SUB basecamp templates copy-status
SUB basecamp templates create
SUB basecamp templates delete
SUB basecamp templates library
SUB basecamp templates list
SUB basecamp templates show
SUB basecamp templates update
SUB basecamp todo
SUB basecamp upload archive
SUB basecamp upload doc
Expand Down
6 changes: 3 additions & 3 deletions API-COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ Coverage of Basecamp 3 API endpoints. Source: [bc3-api/sections](https://github.
basecamp/bc3#13098 added — `PUT /projects/:id/people/client_users.json` and
`POST`/`DELETE /projects/:id/client_enablement.json` — land as `people clients`.
SDK v0.16.0 adds the three to-do
list template-library operations, available through `templates library`,
`templates copy`, and `templates copy-status`. The previous last gap — `GET
list template-library operations, available through `templates todolists list`,
`templates todolists duplicate`, and `templates todolists duplication`. The previous last gap — `GET
Comment thread
robzolkos marked this conversation as resolved.
/uploads/:id/versions.json` — closed with the v0.14.0 SDK bump. The command
(`files versions`) was written earlier but held: the SDK's
`UploadsService.ListVersions` decoded the response as `[]Upload` when the API
Expand Down Expand Up @@ -240,7 +240,7 @@ cannot faithfully cover at least one endpoint for a reason outside the CLI. A
| **Webhooks** |
| webhooks | 7 | `webhooks` | ✅ | BC4 | - | list, show, create, update, delete |
| **Templates** |
| templates | 10 | `templates` | ✅ | BC4 | - | list, show, create, update, delete, construct, construction, library, copy, copy-status |
| templates | 15 | `templates projects`, `templates todolists`, `templates card-tables`, `todolists templatify`, `card-tables templatify` | ✅ | BC4 | - | projects: list, show, create, update, delete, construct, construction; todolists and card-tables: list, create, duplicate, duplication, archive, trash, restore; templatify, templatification. The flat `templates list/library/copy/…` spellings remain as hidden, deprecated aliases |
| **Time Tracking** |
| timesheets | 6 | `timesheet` | ✅ | BC4 | - | list, show, create, update, delete |
| **Subscriptions** |
Expand Down
15 changes: 15 additions & 0 deletions STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ the canonical `<group> <action>` form (`cards create`, `todos create`,
`todos complete`, etc.). Introducing a new top-level verb that shadows an
existing group noun is not allowed.

Where a group is split by the kind of thing it holds, every verb lives under the
kind it acts on. `templates` has `projects`, `todolists`, and `card-tables`. Use
`templates projects delete`, never `templates delete` — a flat verb cannot say
which kind it acts on, and the three kinds hit different endpoints.

When a regrouping retires spellings that have shipped, hide and deprecate them
rather than removing them: scripts and older skill copies still call them. The
flat pre-grouping spellings (`templates list`, `templates show`, `templates
create`, `templates update`, `templates delete`, `templates construct`,
`templates construction`, `templates library`, `templates copy`,
`templates copy-status`) still run their grouped command, hidden from help and
completion, and print one deprecation line to stderr naming the replacement.
They are out of `.surface` because hidden commands are, so their entries in
`.surface-breaking` stay. Never document or suggest a deprecated spelling.
Comment on lines +55 to +56

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: because hidden commands are stops mid-clause — hidden commands are excluded from what? Finish the thought so the rule reads as one idea: "They are not in .surface because hidden commands are omitted from it, so their entries in .surface-breaking stay."

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At STYLE.md, line 55:

<comment>`because hidden commands are` stops mid-clause — hidden commands are excluded from what? Finish the thought so the rule reads as one idea: "They are not in `.surface` because hidden commands are omitted from it, so their entries in `.surface-breaking` stay."</comment>

<file context>
@@ -40,6 +40,21 @@ the canonical `<group> <action>` form (`cards create`, `todos create`,
+`templates construction`, `templates library`, `templates copy`,
+`templates copy-status`) still run their grouped command, hidden from help and
+completion, and print one deprecation line to stderr naming the replacement.
+They are out of `.surface` because hidden commands are, so their entries in
+`.surface-breaking` stay. Never document or suggest a deprecated spelling.
+
</file context>
Suggested change
They are out of `.surface` because hidden commands are, so their entries in
`.surface-breaking` stay. Never document or suggest a deprecated spelling.
They are not in `.surface` because hidden commands are omitted from it, so their entries in
`.surface-breaking` stay.


Shortcut commands without a sibling plural group — `search`, `url`,
`recordings`, `timesheet`, `assignments`, `notifications`, `setup`, `completion`
— may have both `RunE` and subcommands.
Expand Down
21 changes: 14 additions & 7 deletions e2e/smoke/smoke_account.bats
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ setup_file() {
assert_json_not_null '.data[0].id'
}

@test "templates list returns templates" {
run_smoke basecamp templates list --json
@test "templates projects list returns templates" {
run_smoke basecamp templates projects list --json
assert_success
assert_json_value '.ok' 'true'
}

@test "templates library returns to-do list templates" {
run_smoke basecamp templates library --json
@test "templates todolists list returns to-do list templates" {
run_smoke basecamp templates todolists list --json
[[ "$status" -ne 0 ]] && mark_unverifiable "Template library not available in this account"
assert_success
assert_json_value '.ok' 'true'
Expand All @@ -35,14 +35,14 @@ setup_file() {
assert_json_not_null '.data.id'
}

@test "templates show returns template detail" {
@test "templates projects show returns template detail" {
local out
out=$(basecamp templates list --json 2>/dev/null) || mark_unverifiable "Cannot list templates"
out=$(basecamp templates projects list --json 2>/dev/null) || mark_unverifiable "Cannot list templates"
local tmpl_id
tmpl_id=$(echo "$out" | jq -r '.data[0].id // empty')
[[ -n "$tmpl_id" ]] || mark_unverifiable "No templates found"

run_smoke basecamp templates show "$tmpl_id" --json
run_smoke basecamp templates projects show "$tmpl_id" --json
assert_success
assert_json_value '.ok' 'true'
assert_json_not_null '.data.id'
Expand Down Expand Up @@ -102,3 +102,10 @@ setup_file() {
@test "account is out of scope" {
mark_out_of_scope "Alias for accounts — tested via canonical form"
}

@test "templates card-tables list returns card table templates" {
run_smoke basecamp templates card-tables list --json
[[ "$status" -ne 0 ]] && mark_unverifiable "Card table template library not available in this account"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This skips the smoke test for every failure, including CLI regressions and server errors, rather than only an account where card-table templates are unavailable. Skip only for the known unavailable response and let other failures reach assert_success.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At e2e/smoke/smoke_account.bats, line 108:

<comment>This skips the smoke test for every failure, including CLI regressions and server errors, rather than only an account where card-table templates are unavailable. Skip only for the known unavailable response and let other failures reach `assert_success`.</comment>

<file context>
@@ -102,3 +102,10 @@ setup_file() {
+
+@test "templates card-tables list returns card table templates" {
+  run_smoke basecamp templates card-tables list --json
+  [[ "$status" -ne 0 ]] && mark_unverifiable "Card table template library not available in this account"
+  assert_success
+  assert_json_value '.ok' 'true'
</file context>

assert_success
assert_json_value '.ok' 'true'
}
Loading
Loading