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
79 changes: 79 additions & 0 deletions .surface
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ ARG basecamp gauges update 00 <id>
ARG basecamp help 00 [command]
ARG basecamp hillcharts track 00 <todolist-ids>
ARG basecamp hillcharts untrack 00 <todolist-ids>
ARG basecamp hillcharts updates show 00 <id|url>
ARG basecamp lineup create 00 <name>
ARG basecamp lineup create 01 <date>
ARG basecamp lineup delete 00 <id|url>
Expand Down Expand Up @@ -991,6 +992,9 @@ CMD basecamp hillcharts
CMD basecamp hillcharts show
CMD basecamp hillcharts track
CMD basecamp hillcharts untrack
CMD basecamp hillcharts updates
CMD basecamp hillcharts updates list
CMD basecamp hillcharts updates show
CMD basecamp inbox
CMD basecamp lineup
CMD basecamp lineup create
Expand Down Expand Up @@ -11612,6 +11616,78 @@ FLAG basecamp hillcharts untrack --styled type=bool
FLAG basecamp hillcharts untrack --todolist type=string
FLAG basecamp hillcharts untrack --todoset type=string
FLAG basecamp hillcharts untrack --verbose type=count
FLAG basecamp hillcharts updates --account type=string
FLAG basecamp hillcharts updates --agent type=bool
FLAG basecamp hillcharts updates --all type=bool
FLAG basecamp hillcharts updates --cache-dir type=string
FLAG basecamp hillcharts updates --count type=bool
FLAG basecamp hillcharts updates --help type=bool
FLAG basecamp hillcharts updates --hints type=bool
FLAG basecamp hillcharts updates --ids-only type=bool
FLAG basecamp hillcharts updates --in type=string
FLAG basecamp hillcharts updates --jq type=string
FLAG basecamp hillcharts updates --json type=bool
FLAG basecamp hillcharts updates --limit type=int
FLAG basecamp hillcharts updates --markdown type=bool
FLAG basecamp hillcharts updates --md type=bool
FLAG basecamp hillcharts updates --no-hints type=bool
FLAG basecamp hillcharts updates --no-stats type=bool
FLAG basecamp hillcharts updates --page type=int
FLAG basecamp hillcharts updates --profile type=string
FLAG basecamp hillcharts updates --project type=string
FLAG basecamp hillcharts updates --quiet type=bool
FLAG basecamp hillcharts updates --stats type=bool
FLAG basecamp hillcharts updates --styled type=bool
FLAG basecamp hillcharts updates --todolist type=string
FLAG basecamp hillcharts updates --todoset type=string
FLAG basecamp hillcharts updates --verbose type=count
FLAG basecamp hillcharts updates list --account type=string
FLAG basecamp hillcharts updates list --agent type=bool
FLAG basecamp hillcharts updates list --all type=bool
FLAG basecamp hillcharts updates list --cache-dir type=string
FLAG basecamp hillcharts updates list --count type=bool
FLAG basecamp hillcharts updates list --help type=bool
FLAG basecamp hillcharts updates list --hints type=bool
FLAG basecamp hillcharts updates list --ids-only type=bool
FLAG basecamp hillcharts updates list --in type=string
FLAG basecamp hillcharts updates list --jq type=string
FLAG basecamp hillcharts updates list --json type=bool
FLAG basecamp hillcharts updates list --limit type=int
FLAG basecamp hillcharts updates list --markdown type=bool
FLAG basecamp hillcharts updates list --md type=bool
FLAG basecamp hillcharts updates list --no-hints type=bool
FLAG basecamp hillcharts updates list --no-stats type=bool
FLAG basecamp hillcharts updates list --page type=int
FLAG basecamp hillcharts updates list --profile type=string
FLAG basecamp hillcharts updates list --project type=string
FLAG basecamp hillcharts updates list --quiet type=bool
FLAG basecamp hillcharts updates list --stats type=bool
FLAG basecamp hillcharts updates list --styled type=bool
FLAG basecamp hillcharts updates list --todolist type=string
FLAG basecamp hillcharts updates list --todoset type=string
FLAG basecamp hillcharts updates list --verbose type=count
FLAG basecamp hillcharts updates show --account type=string
FLAG basecamp hillcharts updates show --agent type=bool
FLAG basecamp hillcharts updates show --cache-dir type=string
FLAG basecamp hillcharts updates show --count type=bool
FLAG basecamp hillcharts updates show --help type=bool
FLAG basecamp hillcharts updates show --hints type=bool
FLAG basecamp hillcharts updates show --ids-only type=bool
FLAG basecamp hillcharts updates show --in type=string
FLAG basecamp hillcharts updates show --jq type=string
FLAG basecamp hillcharts updates show --json type=bool
FLAG basecamp hillcharts updates show --markdown type=bool
FLAG basecamp hillcharts updates show --md type=bool
FLAG basecamp hillcharts updates show --no-hints type=bool
FLAG basecamp hillcharts updates show --no-stats type=bool
FLAG basecamp hillcharts updates show --profile type=string
FLAG basecamp hillcharts updates show --project type=string
FLAG basecamp hillcharts updates show --quiet type=bool
FLAG basecamp hillcharts updates show --stats type=bool
FLAG basecamp hillcharts updates show --styled type=bool
FLAG basecamp hillcharts updates show --todolist type=string
FLAG basecamp hillcharts updates show --todoset type=string
FLAG basecamp hillcharts updates show --verbose type=count
FLAG basecamp inbox --account type=string
FLAG basecamp inbox --agent type=bool
FLAG basecamp inbox --all type=bool
Expand Down Expand Up @@ -21188,6 +21264,9 @@ SUB basecamp hillcharts
SUB basecamp hillcharts show
SUB basecamp hillcharts track
SUB basecamp hillcharts untrack
SUB basecamp hillcharts updates
SUB basecamp hillcharts updates list
SUB basecamp hillcharts updates show
SUB basecamp inbox
SUB basecamp lineup
SUB basecamp lineup create
Expand Down
14 changes: 10 additions & 4 deletions API-COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ Coverage of Basecamp 3 API endpoints. Source: [bc3-api/sections](https://github.

| Status | Sections | Endpoints |
|--------|----------|-----------|
| ✅ Implemented | 57 | 218 |
| ✅ Implemented | 58 | 220 |
| ⚠️ Blocked | 0 | 0 |
| ⏭️ Out of scope | 4 | 12 |
| **Total tracked** | **61** | **230** |

**218 of 218 tracked in-scope endpoints.** Uploading and removing a project's
| **Total tracked** | **62** | **232** |

**220 of 220 tracked in-scope endpoints.** Reading a hill chart's progress
updates (`GET /todosets/:id/hills/versions.json` and
`GET /todosets/:id/hills/versions/:id.json`, modelled by
[basecamp/basecamp-sdk#1061](https://github.com/basecamp/basecamp-sdk/pull/1061) and
shipped in SDK v0.33.0) lands as `hillcharts updates` and `hillcharts updates show`.
Uploading and removing a project's
logo (`PUT`/`DELETE /projects/:id/logo.json`, added by
[bc3#14174](https://github.com/basecamp/bc3/pull/14174) and shipped in
[SDK v0.30.0](https://github.com/basecamp/basecamp-sdk/releases/tag/v0.30.0))
Expand Down Expand Up @@ -226,6 +231,7 @@ cannot faithfully cover at least one endpoint for a reason outside the CLI. A
| dock_tools | 7 | `tools` | ✅ | BC4 | - | Dock tool management: show, update, trash, enable, disable, reposition (also `tools move --position`; `tools move --to-project` moves a tool to another project through recordings (moves, copies)). `create` is BC5-only (create-by-type: `POST /buckets/{id}/dock/tools.json`), replacing the removed clone call; create-time `visible_to_clients` behind `tools create --visible-to-clients` (chat/kanban only) |
| **Hill Charts** |
| hill_charts | 2 | `hillcharts` | ✅ | BC4 | - | show, track/untrack todolists |
| hill_charts (progress updates) | 2 | `hillcharts updates` | ✅ | BC5 | - | list (`GET /todosets/:id/hills/versions.json`, paginated, newest first; bare `hillcharts updates` or `updates list`), show (`GET /todosets/:id/hills/versions/:id.json`; an id with `--in`/`--todoset`, or the update's web or API URL, which names its todoset). Each update carries its note (`description`, rich text) and every tracked todolist's position (`dots`; `id` is null for a todolist since deleted or moved). Todoset resolved as `hillcharts show` does |
| gauges | 7 | `gauges` | ✅ | BC4 | - | list, needles, needle, create, update, delete, enable/disable |
| **Communication** |
| messages | 10 | `messages`, `message` | ✅ | BC4 | - | list, show, create, update, publish, pin, unpin. Create supports `--subscribe`/`--no-subscribe` and `--draft`. Publish promotes drafts to active. `move`/`copy` file a message onto a message board in another project (see recordings (moves, copies)) |
Expand Down
65 changes: 65 additions & 0 deletions e2e/hillcharts.bats
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,71 @@ load test_helper
}


# Progress updates

@test "hillcharts updates without project shows error" {
create_credentials
create_global_config '{"account_id": 99999}'

run basecamp hillcharts updates
assert_failure
assert_output_contains "project"
}

@test "hillcharts updates with --todoset skips project requirement" {
create_credentials
create_global_config '{"account_id": 99999}'

run basecamp hillcharts updates --todoset 12345
assert_failure
assert_output_not_contains "project"
}

@test "hillcharts updates rejects --all with --limit" {
create_credentials
create_global_config '{"account_id": 99999}'

run basecamp hillcharts updates --todoset 12345 --all --limit 5
assert_failure
assert_output_contains "--all and --limit are mutually exclusive"
}

@test "hillcharts updates with an ID points at show" {
create_credentials
create_global_config '{"account_id": 99999}'

run basecamp hillcharts updates 501 --todoset 12345
assert_failure
assert_output_contains "hillcharts updates show"
}

@test "hillcharts updates show without args shows error" {
create_credentials
create_global_config '{"account_id": 99999}'

run basecamp hillcharts updates show
assert_failure
}

@test "hillcharts updates show refuses an untrusted URL" {
create_credentials
create_global_config '{"account_id": 99999}'

run basecamp hillcharts updates show "https://3.basecamp.example.com/99999/buckets/1/todosets/2/hills/versions#__recording_3"
assert_failure
assert_output_contains "untrusted host"
}

@test "hillcharts updates show refuses a URL that is not a progress update" {
create_credentials
create_global_config '{"account_id": 99999}'

run basecamp hillcharts updates show "https://3.basecamp.com/99999/buckets/1/todolists/2"
assert_failure
assert_output_contains "does not name a hill chart progress update"
}


# Help

@test "hillcharts --help shows help" {
Expand Down
19 changes: 19 additions & 0 deletions e2e/smoke/smoke_hillcharts.bats
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,22 @@ setup_file() {
# Untrack may fail with not-found for a fake ID; just verify it reaches the API
mark_unverifiable "Requires valid todolist ID"
}

@test "hillcharts updates list returns progress updates" {
run_smoke basecamp hillcharts updates list -p "$QA_PROJECT" --json
# A hill chart with no tracked todolists may be refused (403)
[[ "$status" -eq 4 ]] && mark_unverifiable "Hill chart not enabled on project"
assert_success
assert_json_value '.ok' 'true'
}

@test "hillcharts updates show returns a progress update" {
run_smoke basecamp hillcharts updates list -p "$QA_PROJECT" --limit 1 --json
[[ "$status" -eq 0 ]] || mark_unverifiable "Cannot list progress updates"
local id
id=$(echo "$output" | jq -r '.data[0].id // empty')
[[ -n "$id" ]] || mark_unverifiable "No progress updates on the project's hill chart"
run_smoke basecamp hillcharts updates show "$id" -p "$QA_PROJECT" --json
assert_success
assert_json_value '.data.id' "$id"
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
charm.land/bubbles/v2 v2.2.1
charm.land/bubbletea/v2 v2.1.0
charm.land/lipgloss/v2 v2.0.6
github.com/basecamp/basecamp-sdk/go v0.32.0
github.com/basecamp/basecamp-sdk/go v0.33.0
github.com/basecamp/cli v0.2.2-0.20261023171439-c9bce8206df9
github.com/basecamp/mcp v0.0.0-20260828100356-2d6f44b51e9d
github.com/basecamp/surfguard/go v0.1.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuP
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/basecamp/actioncable-go v1.2.0 h1:t0MxlfMTcmaGXZ5ztgKRs7ZiY1MwXtBRwSSSAh1ofVg=
github.com/basecamp/actioncable-go v1.2.0/go.mod h1:ezaV5z1GXQAsqyejqTs6wCFl2D8Wj+COLQkHc/kwoRs=
github.com/basecamp/basecamp-sdk/go v0.32.0 h1:HWRxJGhlaxzh7f/2+cxax664dN5BgjfM+Our3rEWuZU=
github.com/basecamp/basecamp-sdk/go v0.32.0/go.mod h1:mUm/MQGMyg1E1cddyxdG7tQHiq0NsYee2zUeAUT5VjU=
github.com/basecamp/basecamp-sdk/go v0.33.0 h1:ZyDMeLiHy3oParlc7Z6CRp0V/yj3G8VYFk3NGOLr9uU=
github.com/basecamp/basecamp-sdk/go v0.33.0/go.mod h1:mUm/MQGMyg1E1cddyxdG7tQHiq0NsYee2zUeAUT5VjU=
github.com/basecamp/cli v0.2.2-0.20261023171439-c9bce8206df9 h1:3NfGs+zx23Tp6cf+97+UewrJHlzZMLzmvEXOmOJyQAk=
github.com/basecamp/cli v0.2.2-0.20261023171439-c9bce8206df9/go.mod h1:iTBTaWvsPEFIcZfkxQHEfISyJ6sZ7036K6bNx0RY3EE=
github.com/basecamp/mcp v0.0.0-20260828100356-2d6f44b51e9d h1:zEQVGq1x1nhKMZ2TudFAcSJ32CHT8richI1vQakIKz4=
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func CommandCategories() []CommandCategory {
{Name: "subtasks", Category: "core", Description: "Manage subtasks on to-dos and cards", Actions: []string{"list", "show", "create", "update", "complete", "uncomplete", "move", "delete"}},
{Name: "todolists", Category: "core", Description: "Manage to-do lists", Actions: []string{"list", "show", "create", "update", "position", "copy", "trash", "archive", "restore"}},
{Name: "todosets", Category: "core", Description: "Manage to-do set containers", Actions: []string{"list", "show"}},
{Name: "hillcharts", Category: "core", Description: "Manage hill charts", Actions: []string{"show", "track", "untrack"}},
{Name: "hillcharts", Category: "core", Description: "Manage hill charts", Actions: []string{"show", "track", "untrack", "updates"}},
{Name: "gauges", Category: "core", Description: "Manage gauges", Actions: []string{"list", "needles", "needle", "create", "update", "delete", "enable", "disable"}},
{Name: "todolistgroups", Category: "core", Description: "Manage to-do list groups", Actions: []string{"list", "show", "create", "update", "position", "copy"}},
{Name: "messages", Category: "core", Description: "Manage messages", Actions: []string{"list", "show", "create", "update", "publish", "pin", "unpin", "move", "copy", "trash", "archive", "restore"}},
Expand Down
Loading
Loading