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
156 changes: 156 additions & 0 deletions .surface
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ ARG basecamp tlgroups update 00 <id>
ARG basecamp tlgroups update 01 <name>
ARG basecamp todolist archive 00 <id|url>
ARG basecamp todolist create 00 <name>
ARG basecamp todolist move 00 <id|url>...
ARG basecamp todolist position 00 <id|url>...
ARG basecamp todolist reorder 00 <id|url>...
ARG basecamp todolist restore 00 <id|url>
ARG basecamp todolist show 00 <id|url>
ARG basecamp todolist trash 00 <id|url>
Expand All @@ -330,6 +333,9 @@ ARG basecamp todolistgroups update 00 <id>
ARG basecamp todolistgroups update 01 <name>
ARG basecamp todolists archive 00 <id|url>
ARG basecamp todolists create 00 <name>
ARG basecamp todolists move 00 <id|url>...
ARG basecamp todolists position 00 <id|url>...
ARG basecamp todolists reorder 00 <id|url>...
ARG basecamp todolists restore 00 <id|url>
ARG basecamp todolists show 00 <id|url>
ARG basecamp todolists trash 00 <id|url>
Expand Down Expand Up @@ -896,6 +902,9 @@ CMD basecamp todolist
CMD basecamp todolist archive
CMD basecamp todolist create
CMD basecamp todolist list
CMD basecamp todolist move
CMD basecamp todolist position
CMD basecamp todolist reorder
CMD basecamp todolist restore
CMD basecamp todolist show
CMD basecamp todolist trash
Expand All @@ -920,6 +929,9 @@ CMD basecamp todolists
CMD basecamp todolists archive
CMD basecamp todolists create
CMD basecamp todolists list
CMD basecamp todolists move
CMD basecamp todolists position
CMD basecamp todolists reorder
CMD basecamp todolists restore
CMD basecamp todolists show
CMD basecamp todolists trash
Expand Down Expand Up @@ -12482,6 +12494,75 @@ FLAG basecamp todolist list --styled type=bool
FLAG basecamp todolist list --todolist type=string
FLAG basecamp todolist list --todoset type=string
FLAG basecamp todolist list --verbose type=count
FLAG basecamp todolist move --account type=string
FLAG basecamp todolist move --agent type=bool
FLAG basecamp todolist move --cache-dir type=string
FLAG basecamp todolist move --count type=bool
FLAG basecamp todolist move --help type=bool
FLAG basecamp todolist move --hints type=bool
FLAG basecamp todolist move --ids-only type=bool
FLAG basecamp todolist move --in type=string
FLAG basecamp todolist move --jq type=string
FLAG basecamp todolist move --json type=bool
FLAG basecamp todolist move --markdown type=bool
FLAG basecamp todolist move --md type=bool
FLAG basecamp todolist move --no-hints type=bool
FLAG basecamp todolist move --no-stats type=bool
FLAG basecamp todolist move --position type=int
FLAG basecamp todolist move --profile type=string
FLAG basecamp todolist move --project type=string
FLAG basecamp todolist move --quiet type=bool
FLAG basecamp todolist move --stats type=bool
FLAG basecamp todolist move --styled type=bool
FLAG basecamp todolist move --to type=int
FLAG basecamp todolist move --todolist type=string
FLAG basecamp todolist move --verbose type=count
FLAG basecamp todolist position --account type=string
FLAG basecamp todolist position --agent type=bool
FLAG basecamp todolist position --cache-dir type=string
FLAG basecamp todolist position --count type=bool
FLAG basecamp todolist position --help type=bool
FLAG basecamp todolist position --hints type=bool
FLAG basecamp todolist position --ids-only type=bool
FLAG basecamp todolist position --in type=string
FLAG basecamp todolist position --jq type=string
FLAG basecamp todolist position --json type=bool
FLAG basecamp todolist position --markdown type=bool
FLAG basecamp todolist position --md type=bool
FLAG basecamp todolist position --no-hints type=bool
FLAG basecamp todolist position --no-stats type=bool
FLAG basecamp todolist position --position type=int
FLAG basecamp todolist position --profile type=string
FLAG basecamp todolist position --project type=string
FLAG basecamp todolist position --quiet type=bool
FLAG basecamp todolist position --stats type=bool
FLAG basecamp todolist position --styled type=bool
FLAG basecamp todolist position --to type=int
FLAG basecamp todolist position --todolist type=string
FLAG basecamp todolist position --verbose type=count
FLAG basecamp todolist reorder --account type=string
FLAG basecamp todolist reorder --agent type=bool
FLAG basecamp todolist reorder --cache-dir type=string
FLAG basecamp todolist reorder --count type=bool
FLAG basecamp todolist reorder --help type=bool
FLAG basecamp todolist reorder --hints type=bool
FLAG basecamp todolist reorder --ids-only type=bool
FLAG basecamp todolist reorder --in type=string
FLAG basecamp todolist reorder --jq type=string
FLAG basecamp todolist reorder --json type=bool
FLAG basecamp todolist reorder --markdown type=bool
FLAG basecamp todolist reorder --md type=bool
FLAG basecamp todolist reorder --no-hints type=bool
FLAG basecamp todolist reorder --no-stats type=bool
FLAG basecamp todolist reorder --position type=int
FLAG basecamp todolist reorder --profile type=string
FLAG basecamp todolist reorder --project type=string
FLAG basecamp todolist reorder --quiet type=bool
FLAG basecamp todolist reorder --stats type=bool
FLAG basecamp todolist reorder --styled type=bool
FLAG basecamp todolist reorder --to type=int
FLAG basecamp todolist reorder --todolist type=string
FLAG basecamp todolist reorder --verbose type=count
FLAG basecamp todolist restore --account type=string
FLAG basecamp todolist restore --agent type=bool
FLAG basecamp todolist restore --cache-dir type=string
Expand Down Expand Up @@ -13025,6 +13106,75 @@ FLAG basecamp todolists list --styled type=bool
FLAG basecamp todolists list --todolist type=string
FLAG basecamp todolists list --todoset type=string
FLAG basecamp todolists list --verbose type=count
FLAG basecamp todolists move --account type=string
FLAG basecamp todolists move --agent type=bool
FLAG basecamp todolists move --cache-dir type=string
FLAG basecamp todolists move --count type=bool
FLAG basecamp todolists move --help type=bool
FLAG basecamp todolists move --hints type=bool
FLAG basecamp todolists move --ids-only type=bool
FLAG basecamp todolists move --in type=string
FLAG basecamp todolists move --jq type=string
FLAG basecamp todolists move --json type=bool
FLAG basecamp todolists move --markdown type=bool
FLAG basecamp todolists move --md type=bool
FLAG basecamp todolists move --no-hints type=bool
FLAG basecamp todolists move --no-stats type=bool
FLAG basecamp todolists move --position type=int
FLAG basecamp todolists move --profile type=string
FLAG basecamp todolists move --project type=string
FLAG basecamp todolists move --quiet type=bool
FLAG basecamp todolists move --stats type=bool
FLAG basecamp todolists move --styled type=bool
FLAG basecamp todolists move --to type=int
FLAG basecamp todolists move --todolist type=string
FLAG basecamp todolists move --verbose type=count
FLAG basecamp todolists position --account type=string
FLAG basecamp todolists position --agent type=bool
FLAG basecamp todolists position --cache-dir type=string
FLAG basecamp todolists position --count type=bool
FLAG basecamp todolists position --help type=bool
FLAG basecamp todolists position --hints type=bool
FLAG basecamp todolists position --ids-only type=bool
FLAG basecamp todolists position --in type=string
FLAG basecamp todolists position --jq type=string
FLAG basecamp todolists position --json type=bool
FLAG basecamp todolists position --markdown type=bool
FLAG basecamp todolists position --md type=bool
FLAG basecamp todolists position --no-hints type=bool
FLAG basecamp todolists position --no-stats type=bool
FLAG basecamp todolists position --position type=int
FLAG basecamp todolists position --profile type=string
FLAG basecamp todolists position --project type=string
FLAG basecamp todolists position --quiet type=bool
FLAG basecamp todolists position --stats type=bool
FLAG basecamp todolists position --styled type=bool
FLAG basecamp todolists position --to type=int
FLAG basecamp todolists position --todolist type=string
FLAG basecamp todolists position --verbose type=count
FLAG basecamp todolists reorder --account type=string
FLAG basecamp todolists reorder --agent type=bool
FLAG basecamp todolists reorder --cache-dir type=string
FLAG basecamp todolists reorder --count type=bool
FLAG basecamp todolists reorder --help type=bool
FLAG basecamp todolists reorder --hints type=bool
FLAG basecamp todolists reorder --ids-only type=bool
FLAG basecamp todolists reorder --in type=string
FLAG basecamp todolists reorder --jq type=string
FLAG basecamp todolists reorder --json type=bool
FLAG basecamp todolists reorder --markdown type=bool
FLAG basecamp todolists reorder --md type=bool
FLAG basecamp todolists reorder --no-hints type=bool
FLAG basecamp todolists reorder --no-stats type=bool
FLAG basecamp todolists reorder --position type=int
FLAG basecamp todolists reorder --profile type=string
FLAG basecamp todolists reorder --project type=string
FLAG basecamp todolists reorder --quiet type=bool
FLAG basecamp todolists reorder --stats type=bool
FLAG basecamp todolists reorder --styled type=bool
FLAG basecamp todolists reorder --to type=int
FLAG basecamp todolists reorder --todolist type=string
FLAG basecamp todolists reorder --verbose type=count
FLAG basecamp todolists restore --account type=string
FLAG basecamp todolists restore --agent type=bool
FLAG basecamp todolists restore --cache-dir type=string
Expand Down Expand Up @@ -16545,6 +16695,9 @@ SUB basecamp todolist
SUB basecamp todolist archive
SUB basecamp todolist create
SUB basecamp todolist list
SUB basecamp todolist move
SUB basecamp todolist position
SUB basecamp todolist reorder
SUB basecamp todolist restore
SUB basecamp todolist show
SUB basecamp todolist trash
Expand All @@ -16569,6 +16722,9 @@ SUB basecamp todolists
SUB basecamp todolists archive
SUB basecamp todolists create
SUB basecamp todolists list
SUB basecamp todolists move
SUB basecamp todolists position
SUB basecamp todolists reorder
SUB basecamp todolists restore
SUB basecamp todolists show
SUB basecamp todolists trash
Expand Down
10 changes: 5 additions & 5 deletions API-COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ Coverage of Basecamp 3 API endpoints. Source: [bc3-api/sections](https://github.

| Status | Sections | Endpoints |
|--------|----------|-----------|
| ✅ Implemented | 43 | 162 |
| ✅ Implemented | 43 | 163 |
| ⏭️ Out of scope | 4 | 12 |
| **Total (docs)** | **47** | **174** |
| **Total (docs)** | **47** | **175** |

**100% coverage of in-scope API** (162/162 endpoints)
**100% coverage of in-scope API** (163/163 endpoints)

Out-of-scope sections are excluded from parity totals and scripts: chatbots (different auth), legacy Clientside (deprecated)

> 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 (47 sections) is authoritative for the `Since` column. Reconciling endpoint counts is pre-existing maintenance, tracked separately.

**SDK version:** v0.8.0 — adds `Campfires.UpdateLine` (`PUT /chats/{c}/lines/{l}`, basecamp/basecamp-sdk#295), consumed by `chat update`; also carries the BC5 `Tools.Create` create-by-type reshape. API date advanced to 2026-07-22.
**SDK version:** v0.8.1-0.20260724184307-e2c1abea4aea — adds `Todolists.Reposition` (`PUT /todosets/todolists/{id}/position.json`, basecamp/basecamp-sdk#398), consumed by `todolists position`; also carries the create-time `visible_to_clients` support behind `todolists create --visible-to-clients`, `Campfires.UpdateLine` (`PUT /chats/{c}/lines/{l}`, basecamp/basecamp-sdk#295), consumed by `chat update`, and the BC5 `Tools.Create` create-by-type reshape. API date 2026-07-24.

## Coverage by Section

Expand All @@ -27,7 +27,7 @@ The **Since** column tags each row with the Basecamp version that introduced its
| **Core** |
| projects | 9 | `projects` | ✅ | BC4 | - | list, show, create, update, delete |
| todos | 11 | `todos`, `todo`, `done`, `reopen` | ✅ | BC4 | - | list, show, create, update, complete, uncomplete, position (BC5: `steps` shown on `todos show`; edit via `cards step`) |
| todolists | 8 | `todolists` | ✅ | BC4 | - | list, show, create, update |
| todolists | 9 | `todolists` | ✅ | BC4 | - | list, show, create, update, position |
| todosets | 3 | `todosets` | ✅ | BC4 | - | Container for todolists, accessed via project dock (BC5: `todos_count`, `completed_loose_todos_count`, `todos_url`, `app_todos_url`) |
| todolist_groups | 8 | `todolistgroups` | ✅ | BC4 | - | list, show, create, update, position |
| dock_tools | 7 | `tools` | ✅ | BC4 | - | Dock tool management: show, update, trash, enable, disable, reposition. `create` is BC5-only (create-by-type: `POST /buckets/{id}/dock/tools.json`), replacing the removed clone call |
Expand Down
38 changes: 38 additions & 0 deletions e2e/errors.bats
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,44 @@ load test_helper
assert_output_contains "required"
}

@test "todolists position without id shows error" {
create_credentials
create_global_config '{"account_id": 99999, "project_id": 123}'

run basecamp todolists position --to 1
assert_failure
assert_json_value '.error' '<id|url>... required'
assert_json_value '.code' 'usage'
}

@test "todolists position without --to shows error" {
create_credentials
create_global_config '{"account_id": 99999, "project_id": 123}'

run basecamp todolists position 123
assert_failure
assert_output_contains "required"
}

@test "todolists position multiple ids with --to 2 shows error" {
create_credentials
create_global_config '{"account_id": 99999, "project_id": 123}'

run basecamp todolists position 123 456 --to 2
assert_failure
assert_output_contains "position 1"
}

@test "todolists position with only a comma shows error" {
create_credentials
create_global_config '{"account_id": 99999, "project_id": 123}'

run basecamp todolists position ","
assert_failure
assert_json_value '.error' '<id|url>... required'
assert_json_value '.code' 'usage'
}

@test "comments without subcommand shows help" {
create_credentials
create_global_config '{"account_id": 99999, "project_id": 123}'
Expand Down
10 changes: 10 additions & 0 deletions e2e/smoke/smoke_aliases.bats
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,16 @@ load smoke_helper
mark_out_of_scope "Alias for todos position — tested via canonical form"
}

# --- todolists ---

@test "todolists move is out of scope" {
mark_out_of_scope "Alias for todolists position — tested via canonical form"
}

@test "todolists reorder is out of scope" {
mark_out_of_scope "Alias for todolists position — tested via canonical form"
}

# --- todolistgroups ---

@test "todolistgroups move is out of scope" {
Expand Down
12 changes: 12 additions & 0 deletions e2e/smoke/smoke_todos_write.bats
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,18 @@ setup_file() {
echo "$tl_id" > "$BATS_FILE_TMPDIR/todolist_target_id"
}

@test "todolists position repositions a todolist" {
local id_file="$BATS_FILE_TMPDIR/todolist_target_id"
[[ -f "$id_file" ]] || mark_unverifiable "No todolist created in prior test"
local tl_id
tl_id=$(<"$id_file")

# Runs before archive/trash so the target list is still active.
run_smoke basecamp todolists position "$tl_id" --to 1 -p "$QA_PROJECT" --json
assert_success
assert_json_value '.ok' 'true'
}

@test "todolists archive archives a todolist" {
local id_file="$BATS_FILE_TMPDIR/todolist_target_id"
[[ -f "$id_file" ]] || mark_unverifiable "No todolist created in prior test"
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func CommandCategories() []CommandCategory {
Commands: []CommandInfo{
{Name: "projects", Category: "core", Description: "Manage projects", Actions: []string{"list", "show", "create", "update", "delete"}},
{Name: "todos", Category: "core", Description: "Manage to-dos", Actions: []string{"list", "show", "create", "update", "complete", "uncomplete", "position", "trash", "archive", "restore"}},
{Name: "todolists", Category: "core", Description: "Manage to-do lists", Actions: []string{"list", "show", "create", "update", "trash", "archive", "restore"}},
{Name: "todolists", Category: "core", Description: "Manage to-do lists", Actions: []string{"list", "show", "create", "update", "position", "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: "gauges", Category: "core", Description: "Manage gauges", Actions: []string{"list", "needles", "needle", "create", "update", "delete", "enable", "disable"}},
Expand Down
Loading
Loading