From e9fd04df4dd75b1384cdfceb55fefe95fa835808 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Wed, 9 Sep 2026 21:07:16 -0700 Subject: [PATCH 1/4] Add --start-date to templates construct The API has accepted project[start_date] on template construction since 2022, so a scripted construction could anchor the template's relative dates to a chosen week only by re-dating every to-do and schedule entry afterwards. --start-date takes YYYY-MM-DD or the natural forms the other date flags accept, is validated before any request, and rides under the project envelope through the SDK's CreateProjectOptions. Template weeks start on Sunday, so the server anchors to the Sunday on or before the date; the help, agent notes and skill say so. Pins the SDK to the head of basecamp/basecamp-sdk#856 through make bump-sdk, with the vendored MCP model re-synced from that checkout and the Nix vendorHash recomputed and build-verified. --- .surface | 1 + e2e/templates.bats | 9 +++ go.mod | 2 +- go.sum | 4 +- internal/commands/templates.go | 22 +++++- internal/commands/templates_test.go | 85 ++++++++++++++++++++++++ internal/mcpserver/model/PROVENANCE.json | 4 +- internal/mcpserver/model/openapi.json | 4 ++ internal/version/sdk-provenance.json | 6 +- nix/package.nix | 2 +- skills/basecamp/SKILL.md | 6 +- 11 files changed, 132 insertions(+), 13 deletions(-) diff --git a/.surface b/.surface index e444710f..c7ff82be 100644 --- a/.surface +++ b/.surface @@ -13442,6 +13442,7 @@ 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 --styled type=bool FLAG basecamp templates construct --todolist type=string diff --git a/e2e/templates.bats b/e2e/templates.bats index 92ad572e..435618c6 100644 --- a/e2e/templates.bats +++ b/e2e/templates.bats @@ -109,6 +109,15 @@ load test_helper assert_output_contains "name required" } +@test "templates construct with malformed start date shows error" { + create_credentials + create_global_config '{"account_id": 99999}' + + run basecamp templates construct 123 --name "Project" --start-date someday + assert_failure + assert_output_contains "Invalid start date" +} + # Construction status errors diff --git a/go.mod b/go.mod index e0947fda..952baa74 100644 --- a/go.mod +++ b/go.mod @@ -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.17.0 + github.com/basecamp/basecamp-sdk/go v0.17.1-0.20260910040302-11cc4b54ecfe 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 diff --git a/go.sum b/go.sum index f200b7c8..91b8d804 100644 --- a/go.sum +++ b/go.sum @@ -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.17.0 h1:u2kIDtr7bmHXLSkF61HXkLDymQdwoI+pzbT0F6OwHYM= -github.com/basecamp/basecamp-sdk/go v0.17.0/go.mod h1:Cs9DV8iRJaVT4+IQXGZTeyG2nQAV2kQda7GHuBOeonY= +github.com/basecamp/basecamp-sdk/go v0.17.1-0.20260910040302-11cc4b54ecfe h1:oMOjC86dOmjeJitWcaO7ELmAmG/LnACewwDoaGeoATo= +github.com/basecamp/basecamp-sdk/go v0.17.1-0.20260910040302-11cc4b54ecfe/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= diff --git a/internal/commands/templates.go b/internal/commands/templates.go index 45644253..0e230066 100644 --- a/internal/commands/templates.go +++ b/internal/commands/templates.go @@ -12,6 +12,7 @@ import ( "github.com/basecamp/basecamp-sdk/go/pkg/basecamp" "github.com/basecamp/basecamp-cli/internal/appctx" + "github.com/basecamp/basecamp-cli/internal/dateparse" "github.com/basecamp/basecamp-cli/internal/output" ) @@ -24,7 +25,7 @@ func NewTemplatesCmd() *cobra.Command { Project templates create projects with predefined structure, tools, and content. Library templates copy a reusable to-do list into an existing project.`, - Annotations: map[string]string{"agent_notes": "Project construction and to-do list template copies are asynchronous. Poll construction or copy-status until status=completed. Copy grants referenced people project access only with --confirm-adding-people."}, + Annotations: map[string]string{"agent_notes": "Project construction and to-do list template copies are asynchronous. Poll construction or copy-status until status=completed. construct --start-date anchors the template's relative dates to the Sunday of that week; without it they anchor to the week of construction. Copy grants referenced people project access only with --confirm-adding-people."}, } cmd.AddCommand( @@ -629,6 +630,7 @@ func newTemplatesDeleteCmd() *cobra.Command { func newTemplatesConstructCmd() *cobra.Command { var projectName string var projectDesc string + var startDate string cmd := &cobra.Command{ Use: "construct ", @@ -636,7 +638,11 @@ func newTemplatesConstructCmd() *cobra.Command { Long: `Create a new project from a template. This is an asynchronous operation. The command returns a construction ID -which can be polled via 'templates construction' until the status is "completed".`, +which can be polled via 'templates construction' until the status is "completed". + +A template's dates are relative to the start of its first week, and template +weeks start on a Sunday. --start-date anchors them to the Sunday on or before +the given date; without it they anchor to the week the project is constructed.`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if projectName == "" { @@ -648,6 +654,14 @@ which can be polled via 'templates construction' until the status is "completed" return output.ErrUsage("Invalid template ID") } + var parsedStart string + if strings.TrimSpace(startDate) != "" { + parsedStart = dateparse.Parse(startDate) + if _, err := time.Parse("2006-01-02", parsedStart); err != nil { + return output.ErrUsage(fmt.Sprintf("Invalid start date: %q", startDate)) + } + } + // Syntactic checks first, then "-", then account and network. projectDesc, err := resolveContentValue(cmd, projectDesc, -1, "--description") if err != nil { @@ -660,7 +674,8 @@ which can be polled via 'templates construction' until the status is "completed" return err } - construction, err := app.Account().Templates().CreateProject(cmd.Context(), templateID, projectName, projectDesc) + construction, err := app.Account().Templates().CreateProject(cmd.Context(), templateID, projectName, projectDesc, + &basecamp.CreateProjectOptions{StartDate: parsedStart}) if err != nil { return convertSDKError(err) } @@ -681,6 +696,7 @@ which can be polled via 'templates construction' until the status is "completed" cmd.Flags().StringVar(&projectName, "name", "", "Project name (required)") cmd.Flags().StringVar(&projectDesc, "description", "", "Project description; use - to read from stdin") cmd.Flags().StringVar(&projectDesc, "desc", "", "Project description (alias)") + cmd.Flags().StringVar(&startDate, "start-date", "", "Project start date (YYYY-MM-DD or natural, e.g. \"next monday\"); template dates anchor to the Sunday of that week") _ = cmd.MarkFlagRequired("name") allowDash(cmd, "flag:description", "flag:desc") diff --git a/internal/commands/templates_test.go b/internal/commands/templates_test.go index cc0b5123..e0fe2297 100644 --- a/internal/commands/templates_test.go +++ b/internal/commands/templates_test.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -228,6 +229,90 @@ func TestTemplatesCopyExplainsPeopleConfirmation(t *testing.T) { assert.True(t, errors.As(err, &confirmationErr), "typed SDK error should remain available") } +func TestTemplatesConstructSendsStartDateUnderProjectEnvelope(t *testing.T) { + app, transport := setupRecordingTestApp(t, + stubRoute{ + method: http.MethodPost, + path: "/99999/templates/2085958507/project_constructions.json", + status: http.StatusCreated, + body: `{"id":598194962,"status":"pending","url":"https://example.test/constructions/598194962.json"}`, + }, + ) + buf := captureTemplateOutput(app) + + err := executeRecordingCommand(NewTemplatesCmd(), app, + "construct", "2085958507", "--name", "Marketing Campaign", + "--description", "For Client: Xyz Corp Conference", "--start-date", "2026-09-01") + require.NoError(t, err) + + requests := transport.recorded() + require.Len(t, requests, 1) + assert.Equal(t, http.MethodPost, requests[0].Method) + assert.JSONEq(t, + `{"project":{"name":"Marketing Campaign","description":"For Client: Xyz Corp Conference","start_date":"2026-09-01"}}`, + requests[0].Body, + ) + + envelope := decodeTemplateEnvelope(t, buf) + assert.Equal(t, "Started project construction #598194962 (pending)", envelope.Summary) + require.Len(t, envelope.Breadcrumbs, 1) + assert.Equal(t, "basecamp templates construction 2085958507 598194962", envelope.Breadcrumbs[0].Cmd) +} + +func TestTemplatesConstructOmitsStartDateWhenUnset(t *testing.T) { + app, transport := setupRecordingTestApp(t, + stubRoute{ + method: http.MethodPost, + path: "/99999/templates/2085958507/project_constructions.json", + status: http.StatusCreated, + body: `{"id":598194962,"status":"pending","url":"https://example.test/constructions/598194962.json"}`, + }, + ) + captureTemplateOutput(app) + + err := executeRecordingCommand(NewTemplatesCmd(), app, "construct", "2085958507", "--name", "Marketing Campaign") + require.NoError(t, err) + + requests := transport.recorded() + require.Len(t, requests, 1) + assert.JSONEq(t, `{"project":{"name":"Marketing Campaign"}}`, requests[0].Body) +} + +func TestTemplatesConstructParsesNaturalStartDate(t *testing.T) { + app, transport := setupRecordingTestApp(t, + stubRoute{ + method: http.MethodPost, + path: "/99999/templates/2085958507/project_constructions.json", + status: http.StatusCreated, + body: `{"id":598194962,"status":"pending","url":"https://example.test/constructions/598194962.json"}`, + }, + ) + captureTemplateOutput(app) + + err := executeRecordingCommand(NewTemplatesCmd(), app, "construct", "2085958507", "--name", "Marketing Campaign", "--start-date", "tomorrow") + require.NoError(t, err) + + requests := transport.recorded() + require.Len(t, requests, 1) + var body struct { + Project struct { + StartDate string `json:"start_date"` + } `json:"project"` + } + require.NoError(t, json.Unmarshal([]byte(requests[0].Body), &body)) + assert.Equal(t, time.Now().AddDate(0, 0, 1).Format("2006-01-02"), body.Project.StartDate) +} + +func TestTemplatesConstructRejectsMalformedStartDateBeforeAnyRequest(t *testing.T) { + app, transport := setupRecordingTestApp(t) + captureTemplateOutput(app) + + err := executeRecordingCommand(NewTemplatesCmd(), app, "construct", "2085958507", "--name", "Marketing Campaign", "--start-date", "someday") + outErr := requireUsageErr(t, err) + assert.Contains(t, outErr.Message, "Invalid start date") + assert.Empty(t, transport.recorded()) +} + func TestTemplatesCopyStatusStates(t *testing.T) { completed := `{ "id":5,"status":"completed","source_recording_id":3,"destination_parent_id":9,"url":"https://example.test/copies/5.json", diff --git a/internal/mcpserver/model/PROVENANCE.json b/internal/mcpserver/model/PROVENANCE.json index 30764f81..af49a06a 100644 --- a/internal/mcpserver/model/PROVENANCE.json +++ b/internal/mcpserver/model/PROVENANCE.json @@ -1,7 +1,7 @@ { "source": "github.com/basecamp/basecamp-sdk", - "commit": "6e73a06f4262062b2c973b2d0787ab0029b08d0d", - "ref": "go/v0.17.0", + "commit": "11cc4b54ecfee747cb13e7e3035d91ec862d4824", + "ref": "go/v0.17.1-0.20260910040302-11cc4b54ecfe", "files": ["behavior-model.json", "openapi.json"], "synced_by": "scripts/sync-mcp-model.sh", "patches": "tags assigned to operations the export leaves untagged (PATCHED_TAGS); binary-upload operations dropped (EXCLUDED_OPERATIONS) — see the sync script" diff --git a/internal/mcpserver/model/openapi.json b/internal/mcpserver/model/openapi.json index 6714b588..cf838833 100644 --- a/internal/mcpserver/model/openapi.json +++ b/internal/mcpserver/model/openapi.json @@ -33601,6 +33601,10 @@ }, "description": { "type": "string" + }, + "start_date": { + "type": "string", + "description": "Date the new project starts on. Template dates are relative to the start\nof the template's first week, and template weeks start on a Sunday, so\nthe project's dates are anchored to the Sunday on or before this date.\nOmitted, they are anchored to the week in which the construction is\nprocessed." } }, "required": [ diff --git a/internal/version/sdk-provenance.json b/internal/version/sdk-provenance.json index f5287d19..e2675fa8 100644 --- a/internal/version/sdk-provenance.json +++ b/internal/version/sdk-provenance.json @@ -1,9 +1,9 @@ { "sdk": { "module": "github.com/basecamp/basecamp-sdk/go", - "version": "v0.17.0", - "revision": "6e73a06f4262", - "updated_at": "2026-09-09T22:00:24Z" + "version": "v0.17.1-0.20260910040302-11cc4b54ecfe", + "revision": "11cc4b54ecfe", + "updated_at": "2026-09-10T04:03:02Z" }, "api": { "repo": "basecamp/bc3", diff --git a/nix/package.nix b/nix/package.nix index de8833f1..e7b352ba 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -8,7 +8,7 @@ buildGoModule.override { go = go_1_26; } (finalAttrs: { src = lib.cleanSource ./..; # To update: set to lib.fakeHash, run `nix build`, use the hash from the error. - vendorHash = "sha256-GCw7WkPmXDWDAhJrVqbXUfwLa9YzpWdTcSimwovjpcQ="; + vendorHash = "sha256-NqML00Kc1RnvF2BehjOQi0ZXY3NFe4Z3MUS71m7QPtg="; subPackages = [ "cmd/basecamp" ]; diff --git a/skills/basecamp/SKILL.md b/skills/basecamp/SKILL.md index 29886ae9..94a5fb71 100644 --- a/skills/basecamp/SKILL.md +++ b/skills/basecamp/SKILL.md @@ -975,6 +975,7 @@ basecamp templates create "Template Name" # Create empty project templat basecamp templates update --name "New Name" basecamp templates delete # Trash project template basecamp templates construct --name "New Project" # Create project (async) +basecamp templates construct --name "New Project" --start-date 2026-09-01 # Anchor template dates to that week basecamp templates construction # Check project status basecamp templates library --json # List active to-do list templates @@ -983,7 +984,10 @@ basecamp templates copy-status # Check copy status ``` **Asynchronous results:** `construct` returns a construction ID; poll `construction` -until `status="completed"` to get the project. `copy` returns a copy ID; poll +until `status="completed"` to get the project. A template's dates are relative to its +first week, and template weeks start on Sunday: `--start-date` (YYYY-MM-DD or natural, +e.g. `next monday`) anchors them to the Sunday on or before that date; without it they +anchor to the week of construction. `copy` returns a copy ID; poll `copy-status` through `pending` and `processing` until it is `completed` or `failed`. A copy can report the people who need access to the destination project. Show those From 6dbffb16f4de0f2aabc8c2ead166db4e415bcde2 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Wed, 9 Sep 2026 21:10:23 -0700 Subject: [PATCH 2/4] Pin the SDK to the current head of basecamp-sdk#856 --- go.mod | 2 +- go.sum | 4 ++-- internal/mcpserver/model/PROVENANCE.json | 4 ++-- internal/version/sdk-provenance.json | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 952baa74..27f495cd 100644 --- a/go.mod +++ b/go.mod @@ -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.17.1-0.20260910040302-11cc4b54ecfe + github.com/basecamp/basecamp-sdk/go v0.17.1-0.20260910040941-9610e51f759d 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 diff --git a/go.sum b/go.sum index 91b8d804..770b672f 100644 --- a/go.sum +++ b/go.sum @@ -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.17.1-0.20260910040302-11cc4b54ecfe h1:oMOjC86dOmjeJitWcaO7ELmAmG/LnACewwDoaGeoATo= -github.com/basecamp/basecamp-sdk/go v0.17.1-0.20260910040302-11cc4b54ecfe/go.mod h1:Cs9DV8iRJaVT4+IQXGZTeyG2nQAV2kQda7GHuBOeonY= +github.com/basecamp/basecamp-sdk/go v0.17.1-0.20260910040941-9610e51f759d h1:PUtCjOcMs3uinaUxQIDFmc1K3FceiS9kQRedlPdEKr4= +github.com/basecamp/basecamp-sdk/go v0.17.1-0.20260910040941-9610e51f759d/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= diff --git a/internal/mcpserver/model/PROVENANCE.json b/internal/mcpserver/model/PROVENANCE.json index af49a06a..0f1fb64f 100644 --- a/internal/mcpserver/model/PROVENANCE.json +++ b/internal/mcpserver/model/PROVENANCE.json @@ -1,7 +1,7 @@ { "source": "github.com/basecamp/basecamp-sdk", - "commit": "11cc4b54ecfee747cb13e7e3035d91ec862d4824", - "ref": "go/v0.17.1-0.20260910040302-11cc4b54ecfe", + "commit": "9610e51f759dfdae75e2a3a621b77b7056d5a072", + "ref": "go/v0.17.1-0.20260910040941-9610e51f759d", "files": ["behavior-model.json", "openapi.json"], "synced_by": "scripts/sync-mcp-model.sh", "patches": "tags assigned to operations the export leaves untagged (PATCHED_TAGS); binary-upload operations dropped (EXCLUDED_OPERATIONS) — see the sync script" diff --git a/internal/version/sdk-provenance.json b/internal/version/sdk-provenance.json index e2675fa8..8d9066c3 100644 --- a/internal/version/sdk-provenance.json +++ b/internal/version/sdk-provenance.json @@ -1,9 +1,9 @@ { "sdk": { "module": "github.com/basecamp/basecamp-sdk/go", - "version": "v0.17.1-0.20260910040302-11cc4b54ecfe", - "revision": "11cc4b54ecfe", - "updated_at": "2026-09-10T04:03:02Z" + "version": "v0.17.1-0.20260910040941-9610e51f759d", + "revision": "9610e51f759d", + "updated_at": "2026-09-10T04:09:41Z" }, "api": { "repo": "basecamp/bc3", From cea7e09253ed143a316f255fb7b591d6bbd26bd4 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Wed, 9 Sep 2026 21:14:01 -0700 Subject: [PATCH 3/4] Reject an explicitly blank --start-date and pin the natural-date test to its own clock --- internal/commands/templates.go | 2 +- internal/commands/templates_test.go | 15 ++++++++++++++- nix/package.nix | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/internal/commands/templates.go b/internal/commands/templates.go index 0e230066..a8482b3d 100644 --- a/internal/commands/templates.go +++ b/internal/commands/templates.go @@ -655,7 +655,7 @@ the given date; without it they anchor to the week the project is constructed.`, } var parsedStart string - if strings.TrimSpace(startDate) != "" { + if cmd.Flags().Changed("start-date") { parsedStart = dateparse.Parse(startDate) if _, err := time.Parse("2006-01-02", parsedStart); err != nil { return output.ErrUsage(fmt.Sprintf("Invalid start date: %q", startDate)) diff --git a/internal/commands/templates_test.go b/internal/commands/templates_test.go index e0fe2297..397e11e5 100644 --- a/internal/commands/templates_test.go +++ b/internal/commands/templates_test.go @@ -289,7 +289,9 @@ func TestTemplatesConstructParsesNaturalStartDate(t *testing.T) { ) captureTemplateOutput(app) + before := time.Now() err := executeRecordingCommand(NewTemplatesCmd(), app, "construct", "2085958507", "--name", "Marketing Campaign", "--start-date", "tomorrow") + after := time.Now() require.NoError(t, err) requests := transport.recorded() @@ -300,7 +302,18 @@ func TestTemplatesConstructParsesNaturalStartDate(t *testing.T) { } `json:"project"` } require.NoError(t, json.Unmarshal([]byte(requests[0].Body), &body)) - assert.Equal(t, time.Now().AddDate(0, 0, 1).Format("2006-01-02"), body.Project.StartDate) + tomorrow := func(now time.Time) string { return now.AddDate(0, 0, 1).Format("2006-01-02") } + assert.Contains(t, []string{tomorrow(before), tomorrow(after)}, body.Project.StartDate) +} + +func TestTemplatesConstructRejectsBlankStartDate(t *testing.T) { + app, transport := setupRecordingTestApp(t) + captureTemplateOutput(app) + + err := executeRecordingCommand(NewTemplatesCmd(), app, "construct", "2085958507", "--name", "Marketing Campaign", "--start-date", "") + outErr := requireUsageErr(t, err) + assert.Contains(t, outErr.Message, "Invalid start date") + assert.Empty(t, transport.recorded()) } func TestTemplatesConstructRejectsMalformedStartDateBeforeAnyRequest(t *testing.T) { diff --git a/nix/package.nix b/nix/package.nix index e7b352ba..d8dd97e7 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -8,7 +8,7 @@ buildGoModule.override { go = go_1_26; } (finalAttrs: { src = lib.cleanSource ./..; # To update: set to lib.fakeHash, run `nix build`, use the hash from the error. - vendorHash = "sha256-NqML00Kc1RnvF2BehjOQi0ZXY3NFe4Z3MUS71m7QPtg="; + vendorHash = "sha256-nWEquNPUTr46bza902GOP41Y3RLjDG+HcNGdskQYx4A="; subPackages = [ "cmd/basecamp" ]; From 902b2c413f2973f148ef05a7628cc432babb9cb0 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Wed, 9 Sep 2026 22:45:55 -0700 Subject: [PATCH 4/4] Pin basecamp-sdk to v0.18.0 --- go.mod | 2 +- go.sum | 4 ++-- internal/mcpserver/model/PROVENANCE.json | 4 ++-- internal/version/sdk-provenance.json | 6 +++--- nix/package.nix | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 27f495cd..93c284fe 100644 --- a/go.mod +++ b/go.mod @@ -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.17.1-0.20260910040941-9610e51f759d + github.com/basecamp/basecamp-sdk/go v0.18.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 diff --git a/go.sum b/go.sum index 770b672f..965fdfd7 100644 --- a/go.sum +++ b/go.sum @@ -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.17.1-0.20260910040941-9610e51f759d h1:PUtCjOcMs3uinaUxQIDFmc1K3FceiS9kQRedlPdEKr4= -github.com/basecamp/basecamp-sdk/go v0.17.1-0.20260910040941-9610e51f759d/go.mod h1:Cs9DV8iRJaVT4+IQXGZTeyG2nQAV2kQda7GHuBOeonY= +github.com/basecamp/basecamp-sdk/go v0.18.0 h1:A6Mu1ugnvvExNdLN4w9th6Tz2fktSdfbqbbDhxYgPPE= +github.com/basecamp/basecamp-sdk/go v0.18.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= diff --git a/internal/mcpserver/model/PROVENANCE.json b/internal/mcpserver/model/PROVENANCE.json index 0f1fb64f..c223d264 100644 --- a/internal/mcpserver/model/PROVENANCE.json +++ b/internal/mcpserver/model/PROVENANCE.json @@ -1,7 +1,7 @@ { "source": "github.com/basecamp/basecamp-sdk", - "commit": "9610e51f759dfdae75e2a3a621b77b7056d5a072", - "ref": "go/v0.17.1-0.20260910040941-9610e51f759d", + "commit": "400c76ca433d5ae08e1e7d6a561ab113949c70d8", + "ref": "go/v0.18.0", "files": ["behavior-model.json", "openapi.json"], "synced_by": "scripts/sync-mcp-model.sh", "patches": "tags assigned to operations the export leaves untagged (PATCHED_TAGS); binary-upload operations dropped (EXCLUDED_OPERATIONS) — see the sync script" diff --git a/internal/version/sdk-provenance.json b/internal/version/sdk-provenance.json index 8d9066c3..86b479d4 100644 --- a/internal/version/sdk-provenance.json +++ b/internal/version/sdk-provenance.json @@ -1,9 +1,9 @@ { "sdk": { "module": "github.com/basecamp/basecamp-sdk/go", - "version": "v0.17.1-0.20260910040941-9610e51f759d", - "revision": "9610e51f759d", - "updated_at": "2026-09-10T04:09:41Z" + "version": "v0.18.0", + "revision": "400c76ca433d", + "updated_at": "2026-09-10T05:31:19Z" }, "api": { "repo": "basecamp/bc3", diff --git a/nix/package.nix b/nix/package.nix index d8dd97e7..26a0a2f4 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -8,7 +8,7 @@ buildGoModule.override { go = go_1_26; } (finalAttrs: { src = lib.cleanSource ./..; # To update: set to lib.fakeHash, run `nix build`, use the hash from the error. - vendorHash = "sha256-nWEquNPUTr46bza902GOP41Y3RLjDG+HcNGdskQYx4A="; + vendorHash = "sha256-MdVsRrJ3SEEtFFU6X2gP5s414kEhulSkxLqoD9GxiIg="; subPackages = [ "cmd/basecamp" ];