Skip to content
Merged
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
73 changes: 73 additions & 0 deletions .surface
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ ARG basecamp subscriptions unsubscribe 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>
Expand Down Expand Up @@ -991,8 +993,11 @@ CMD basecamp subscriptions unsubscribe
CMD basecamp templates
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
Expand Down Expand Up @@ -13296,6 +13301,50 @@ FLAG basecamp templates construction --stats type=bool
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
Expand Down Expand Up @@ -13341,6 +13390,27 @@ FLAG basecamp templates delete --stats type=bool
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
Expand Down Expand Up @@ -18426,8 +18496,11 @@ SUB basecamp subscriptions unsubscribe
SUB basecamp templates
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
Expand Down
29 changes: 15 additions & 14 deletions API-COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ Coverage of Basecamp 3 API endpoints. Source: [bc3-api/sections](https://github.

| Status | Sections | Endpoints |
|--------|----------|-----------|
| ✅ Implemented | 50 | 186 |
| ✅ Implemented | 50 | 189 |
| ⚠️ Blocked | 0 | 0 |
| ⏭️ Out of scope | 4 | 12 |
| **Total tracked** | **54** | **198** |
| **Total tracked** | **54** | **201** |

**186 of 186 tracked in-scope endpoints.** The last gap — `GET
**189 of 189 tracked in-scope endpoints.** 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
/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 @@ -43,16 +45,15 @@ Out-of-scope sections are excluded from parity totals and scripts: chatbots (dif

> Note: the per-row `Endpoints` column in the Coverage by Section table sums higher than the Summary totals above. The discrepancy predates the BC5 baseline; the row count (48 sections) is authoritative for the `Since` column. Reconciling endpoint counts is pre-existing maintenance, tracked separately.

**SDK version:** v0.15.1 (adds Bubble Up write support; pinned to the
pre-release bubble-up SDK head until the v0.15.1 tag —
`internal/version/sdk-provenance.json` is authoritative). The command surface below largely dates to the v0.12.0 bump,
which added 20 exported Go methods over 13 new backend operations; the extra
seven wrapped endpoints that already existed but were reachable only through
the raw generated client, which the andon-cord rule forbids the CLI from
calling. v0.13.0–v0.15.0 corrected shapes and routes (pointerized optional
fields, page-selection semantics, field-keyed 422 payloads) rather than opening
new sections; their additions here are `files replace` and the un-held
`files versions`, both from v0.14.0's upload work (basecamp/basecamp-sdk#683).
**SDK version:** v0.16.0 (adds the to-do list template library and asynchronous
copy operations; `internal/version/sdk-provenance.json` is authoritative). The
command surface below largely dates to the v0.12.0 bump, which added 20 exported
Go methods over 13 new backend operations; the extra seven wrapped endpoints
that already existed but were reachable only through the raw generated client,
which the andon-cord rule forbids the CLI from calling. v0.13.0–v0.15.0
corrected shapes and routes (pointerized optional fields, page-selection
semantics, field-keyed 422 payloads) and added `files replace`, `files versions`,
and Bubble Up writes. v0.16.0 adds the three template-library operations.

Those methods land as four new command groups (`bookmarks`, `drafts`, `notes`,
`calendars`) and three extensions (`assignments` gains the Up Next verbs,
Expand Down Expand Up @@ -228,7 +229,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 | 7 | `templates` | ✅ | BC4 | - | list, show, create, update, delete, construct, construction |
| templates | 10 | `templates` | ✅ | BC4 | - | list, show, create, update, delete, construct, construction, library, copy, copy-status |
| **Time Tracking** |
| timesheets | 6 | `timesheet` | ✅ | BC4 | - | list, show, create, update, delete |
| **Subscriptions** |
Expand Down
7 changes: 7 additions & 0 deletions e2e/smoke/smoke_account.bats
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ setup_file() {
assert_json_value '.ok' 'true'
}

@test "templates library returns to-do list templates" {
run_smoke basecamp templates library --json
[[ "$status" -ne 0 ]] && mark_unverifiable "Template library not available in this account"
assert_success
assert_json_value '.ok' 'true'
}

@test "people show returns person detail" {
run_smoke basecamp people show me --json
assert_success
Expand Down
8 changes: 8 additions & 0 deletions e2e/smoke/smoke_bookmarks.bats
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,11 @@ setup_file() {
@test "bookmarks remove is out of scope" {
mark_out_of_scope "Mutating - exercised by the live add/check/remove round-trip"
}

@test "bubble-up add is out of scope" {
mark_out_of_scope "Mutates the current user's personal readings"
}

@test "bubble-up remove is out of scope" {
mark_out_of_scope "Mutates the current user's personal readings"
}
8 changes: 8 additions & 0 deletions e2e/smoke/smoke_lifecycle.bats
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ load smoke_helper
mark_out_of_scope "Depends on templates construct (OOS)"
}

@test "templates copy is out of scope" {
mark_out_of_scope "Copies a to-do list into a project and may grant project access"
}

@test "templates copy-status is out of scope" {
mark_out_of_scope "Depends on templates copy (OOS)"
}

@test "templates create is out of scope" {
mark_out_of_scope "Account-wide template mutation"
}
Expand Down
29 changes: 29 additions & 0 deletions e2e/templates.bats
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,33 @@ load test_helper
}


# Template library copy errors

@test "templates copy without template id shows error" {
create_credentials
create_global_config '{"account_id": 99999}'

run basecamp templates copy
assert_failure
assert_output_contains "ID required"
}

@test "templates copy-status without copy id shows error" {
create_credentials
create_global_config '{"account_id": 99999}'

run basecamp templates copy-status
assert_failure
assert_output_contains "ID required"
}

@test "templates copy --confirm-adding-people is accepted" {
run basecamp templates copy --help
assert_success
assert_output_contains "--confirm-adding-people"
}


# Flag parsing

@test "templates list --status without value shows error" {
Expand Down Expand Up @@ -163,6 +190,8 @@ load test_helper
assert_output_contains "basecamp templates"
assert_output_contains "construct"
assert_output_contains "construction"
assert_output_contains "library"
assert_output_contains "copy-status"
}


Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
charm.land/bubbles/v2 v2.2.1
charm.land/bubbletea/v2 v2.0.9
charm.land/lipgloss/v2 v2.0.6
github.com/basecamp/basecamp-sdk/go v0.15.1-0.20260902205921-f439f493caf2
github.com/basecamp/basecamp-sdk/go v0.16.0
github.com/basecamp/cli v0.2.2-0.20260828230226-767413fc712d
github.com/basecamp/mcp v0.0.0-20260828100356-2d6f44b51e9d
github.com/basecamp/surfguard/go v0.1.0
Expand Down Expand Up @@ -134,7 +134,7 @@ require (
go.opentelemetry.io/otel/metric v1.46.0 // indirect
go.opentelemetry.io/otel/trace v1.46.0 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/crypto v0.55.0 // indirect
golang.org/x/crypto v0.56.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/term v0.45.0 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/basecamp/basecamp-sdk/go v0.15.1-0.20260902205921-f439f493caf2 h1:bIaTwvIoWTNirPigLsXsC912abkIYDTFHuOrkSyB96U=
github.com/basecamp/basecamp-sdk/go v0.15.1-0.20260902205921-f439f493caf2/go.mod h1:Cs9DV8iRJaVT4+IQXGZTeyG2nQAV2kQda7GHuBOeonY=
github.com/basecamp/basecamp-sdk/go v0.16.0 h1:6MfdBf+tgDtfzSHLDhJO9yqsCExNvtWUlK6gzTKzMvM=
github.com/basecamp/basecamp-sdk/go v0.16.0/go.mod h1:Cs9DV8iRJaVT4+IQXGZTeyG2nQAV2kQda7GHuBOeonY=
github.com/basecamp/cli v0.2.2-0.20260828230226-767413fc712d h1:jAzDrCCzDpIwhbFT1xVVs0z2xpXoDEkomHfKB2bUUp8=
github.com/basecamp/cli v0.2.2-0.20260828230226-767413fc712d/go.mod h1:iTBTaWvsPEFIcZfkxQHEfISyJ6sZ7036K6bNx0RY3EE=
github.com/basecamp/mcp v0.0.0-20260828100356-2d6f44b51e9d h1:zEQVGq1x1nhKMZ2TudFAcSJ32CHT8richI1vQakIKz4=
Expand Down Expand Up @@ -474,8 +474,8 @@ go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func CommandCategories() []CommandCategory {
Name: "Organization",
Commands: []CommandInfo{
{Name: "people", Category: "organization", Description: "Manage people and access", Actions: []string{"list", "show", "update", "out-of-office", "pingable", "add", "remove"}},
{Name: "templates", Category: "organization", Description: "Manage project templates", Actions: []string{"list", "show", "create", "update", "delete", "construct"}},
{Name: "templates", Category: "organization", Description: "Manage project and to-do list templates", Actions: []string{"list", "show", "create", "update", "delete", "construct", "construction", "library", "copy", "copy-status"}},
{Name: "webhooks", Category: "organization", Description: "Manage webhooks", Actions: []string{"list", "show", "create", "update", "delete"}},
{Name: "lineup", Category: "organization", Description: "Manage lineup markers", Actions: []string{"list", "create", "update", "delete"}},
},
Expand Down
Loading
Loading