From c2caca0066057f1efcef243338b153a8b47c46fb Mon Sep 17 00:00:00 2001 From: Rob Zolkos Date: Thu, 2 Jul 2026 16:39:51 -0400 Subject: [PATCH 1/4] Scope templates --status flag to the list subcommand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The --status filter was registered as a persistent flag on the templates command group, so it leaked onto every subcommand (construct, show, create, etc.) where it has no meaning and is silently ignored. Move it to a local flag on `templates list`, the only command that uses it. This is cosmetic — the stray flag was never sent in any request body and did not cause the `templates construct` 400 reported in #454 (that is an SDK bug tracked in basecamp/basecamp-sdk#354). Refs #454 --- .surface | 7 ------- internal/commands/templates.go | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/.surface b/.surface index 03010afed..5f7c4ceeb 100644 --- a/.surface +++ b/.surface @@ -11606,7 +11606,6 @@ FLAG basecamp templates --profile type=string FLAG basecamp templates --project type=string FLAG basecamp templates --quiet type=bool FLAG basecamp templates --stats type=bool -FLAG basecamp templates --status type=string FLAG basecamp templates --styled type=bool FLAG basecamp templates --todolist type=string FLAG basecamp templates --verbose type=count @@ -11631,7 +11630,6 @@ 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 --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 @@ -11653,7 +11651,6 @@ 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 @@ -11678,7 +11675,6 @@ 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 @@ -11700,7 +11696,6 @@ 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 @@ -11744,7 +11739,6 @@ 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 @@ -11769,7 +11763,6 @@ 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 diff --git a/internal/commands/templates.go b/internal/commands/templates.go index 8709956be..5d844ba10 100644 --- a/internal/commands/templates.go +++ b/internal/commands/templates.go @@ -15,8 +15,6 @@ import ( // NewTemplatesCmd creates the templates command for managing project templates. func NewTemplatesCmd() *cobra.Command { - var status string - cmd := &cobra.Command{ Use: "templates", Short: "Manage project templates", @@ -27,10 +25,8 @@ tools, and content.`, Annotations: map[string]string{"agent_notes": "Construction from template is asynchronous — poll construction until status=completed to get the new project ID"}, } - cmd.PersistentFlags().StringVar(&status, "status", "active", "Filter: active, archived, trashed") - cmd.AddCommand( - newTemplatesListCmd(&status), + newTemplatesListCmd(), newTemplatesShowCmd(), newTemplatesCreateCmd(), newTemplatesUpdateCmd(), @@ -42,15 +38,21 @@ tools, and content.`, return cmd } -func newTemplatesListCmd(status *string) *cobra.Command { - return &cobra.Command{ +func newTemplatesListCmd() *cobra.Command { + var status string + + cmd := &cobra.Command{ Use: "list", Short: "List templates", Long: "List all project templates.", RunE: func(cmd *cobra.Command, args []string) error { - return runTemplatesList(cmd, *status) + return runTemplatesList(cmd, status) }, } + + cmd.Flags().StringVar(&status, "status", "active", "Filter: active, archived, trashed") + + return cmd } func runTemplatesList(cmd *cobra.Command, status string) error { From c88697f13eb5dddf73641364aefd9c590e8fa3d7 Mon Sep 17 00:00:00 2001 From: Rob Zolkos Date: Thu, 2 Jul 2026 16:44:47 -0400 Subject: [PATCH 2/4] Acknowledge --status surface removals and fix e2e test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI surface diff flags the 7 removed `templates … --status` entries as breaking removals — acknowledge them in .surface-breaking. Also retarget the e2e flag-parsing test from `templates --status` to `templates list --status`, matching where the flag now lives. --- .surface-breaking | 7 +++++++ e2e/templates.bats | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.surface-breaking b/.surface-breaking index 3df26d102..1d1638fd8 100644 --- a/.surface-breaking +++ b/.surface-breaking @@ -1054,3 +1054,10 @@ SUB basecamp uploads vault list SUB basecamp uploads vaults SUB basecamp uploads vaults create SUB basecamp uploads vaults list +FLAG basecamp templates --status type=string +FLAG basecamp templates construct --status type=string +FLAG basecamp templates construction --status type=string +FLAG basecamp templates create --status type=string +FLAG basecamp templates delete --status type=string +FLAG basecamp templates show --status type=string +FLAG basecamp templates update --status type=string diff --git a/e2e/templates.bats b/e2e/templates.bats index 544410727..36ce150f7 100644 --- a/e2e/templates.bats +++ b/e2e/templates.bats @@ -133,11 +133,11 @@ load test_helper # Flag parsing -@test "templates --status without value shows error" { +@test "templates list --status without value shows error" { create_credentials create_global_config '{"account_id": 99999}' - run basecamp templates --status + run basecamp templates list --status assert_failure assert_output_contains "--status requires a value" } From 34359a187e8a6885de91ab10d0cb65b45ff69484 Mon Sep 17 00:00:00 2001 From: Rob Zolkos Date: Thu, 2 Jul 2026 16:56:07 -0400 Subject: [PATCH 3/4] Validate templates list --status value Per PR review: --status was accepted as any string and, for non-active values, interpolated directly into the request URL. Reject anything outside active/archived/trashed (or empty) with a usage error before the value reaches the account layer or the URL. Add an e2e test for the invalid case. --- e2e/templates.bats | 9 +++++++++ internal/commands/templates.go | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/e2e/templates.bats b/e2e/templates.bats index 36ce150f7..fc57f40fe 100644 --- a/e2e/templates.bats +++ b/e2e/templates.bats @@ -142,6 +142,15 @@ load test_helper assert_output_contains "--status requires a value" } +@test "templates list --status with invalid value shows error" { + create_credentials + create_global_config '{"account_id": 99999}' + + run basecamp templates list --status bogus + assert_failure + assert_output_contains "unknown --status value" +} + # Help diff --git a/internal/commands/templates.go b/internal/commands/templates.go index 5d844ba10..654ba376b 100644 --- a/internal/commands/templates.go +++ b/internal/commands/templates.go @@ -56,6 +56,15 @@ func newTemplatesListCmd() *cobra.Command { } func runTemplatesList(cmd *cobra.Command, status string) error { + // Validate before the value reaches the request URL — only the lifecycle + // filters the API understands are allowed. + switch status { + case "", "active", "archived", "trashed": + default: + return output.ErrUsage( + fmt.Sprintf("unknown --status value %q (expected active, archived, or trashed)", status)) + } + app := appctx.FromContext(cmd.Context()) if err := ensureAccount(cmd, app); err != nil { From 8140b4843aacdc4be12749b75a66896b1183b3a4 Mon Sep 17 00:00:00 2001 From: Rob Zolkos Date: Thu, 2 Jul 2026 17:01:43 -0400 Subject: [PATCH 4/4] Group templates --status entries into .surface-breaking FLAG section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per PR review: the acknowledged removals were appended after the SUB block. Move them into the FLAG section in correct lexical position so the file stays grouped and deterministic. Content is unchanged — only reordered. --- .surface-breaking | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.surface-breaking b/.surface-breaking index 1d1638fd8..342ab672b 100644 --- a/.surface-breaking +++ b/.surface-breaking @@ -669,6 +669,13 @@ FLAG basecamp reopen --styled type=bool 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 --status type=string +FLAG basecamp templates construction --status type=string +FLAG basecamp templates create --status type=string +FLAG basecamp templates delete --status type=string +FLAG basecamp templates show --status type=string +FLAG basecamp templates update --status type=string FLAG basecamp tlgroup create --name type=string FLAG basecamp tlgroup rename --name type=string FLAG basecamp tlgroup update --name type=string @@ -1054,10 +1061,3 @@ SUB basecamp uploads vault list SUB basecamp uploads vaults SUB basecamp uploads vaults create SUB basecamp uploads vaults list -FLAG basecamp templates --status type=string -FLAG basecamp templates construct --status type=string -FLAG basecamp templates construction --status type=string -FLAG basecamp templates create --status type=string -FLAG basecamp templates delete --status type=string -FLAG basecamp templates show --status type=string -FLAG basecamp templates update --status type=string