Skip to content

Prepare CLI for SDK sync 2026-03-23#361

Closed
robzolkos wants to merge 10 commits intomainfrom
prep/sdk-sync-2026-03-23
Closed

Prepare CLI for SDK sync 2026-03-23#361
robzolkos wants to merge 10 commits intomainfrom
prep/sdk-sync-2026-03-23

Conversation

@robzolkos
Copy link
Copy Markdown
Collaborator

@robzolkos robzolkos commented Mar 24, 2026

Summary

This draft prepares the CLI for the upcoming SDK sync branch and keeps the work split into service-focused commits so reviewers can walk the changes commit by commit.

This branch was developed and validated locally against the SDK sync branch at 97fde17 via a temporary go.work override. The final SDK bump is intentionally not included here yet.

Dependency Status

This PR is draft-only until the SDK work lands.

Blocking items before this can merge:

Because the SDK bump is intentionally deferred, reviewers should treat this as a prepared CLI implementation branch rather than a merge-ready PR.

What Changed

Compatibility updates for existing CLI behavior

  • adapted card move calls to the new SDK Cards().Move(..., opts) signature
  • replaced the old positioned-card move workaround with the SDK's native move-position option
  • added an overflow guard around the new card position conversion path
  • added forwards sorting support with --sort created|updated and --reverse

Account management

Added account-scoped management commands under accounts:

  • basecamp accounts show
  • basecamp accounts rename <name>
  • basecamp accounts logo set <path>
  • basecamp accounts logo remove

I kept these under accounts rather than introducing a new top-level account group because account is already an alias for accounts.

People profile, preferences, and out-of-office

Extended people with current-user settings and out-of-office operations:

  • basecamp people profile show
  • basecamp people profile update ...
  • basecamp people preferences show
  • basecamp people preferences update ...
  • basecamp people out-of-office show [person]
  • basecamp people out-of-office enable --from <date> --to <date> [person]
  • basecamp people out-of-office disable [person]

Gauges

Added a new top-level gauges command group covering both account-wide listing and project-scoped needle management:

  • basecamp gauges list
  • basecamp gauges needles --in <project>
  • basecamp gauges show <needle-id|url>
  • basecamp gauges create --position <0-100> --in <project> ...
  • basecamp gauges update <needle-id|url> --description <text>
  • basecamp gauges delete <needle-id|url>
  • basecamp gauges enable --in <project>
  • basecamp gauges disable --in <project>

Notifications

Added a new top-level notifications command group:

  • basecamp notifications list [--page]
  • basecamp notifications read <readable-sgid>...

One live-data issue showed up while testing this surface: notification creators can be local people with string IDs like "bulletins" or "onboarding". That turned out to be an SDK bug rather than a CLI bug, which is why the stacked SDK PR above exists.

My assignments

Extended reports with current-user assignment views:

  • basecamp reports mine
  • basecamp reports completed
  • basecamp reports due [--scope overdue|due_today|due_tomorrow|due_later_this_week|due_next_week|due_later]

I kept these under reports rather than adding another top-level command so they stay discoverable alongside the existing account-wide report commands.

Review Guide

This branch is intentionally organized as service-focused commits:

  • 176d049 Adapt card moves to SDK sync branch
  • 3389677 Add account management commands
  • 9965106 Add people profile and preferences commands
  • 16eef9d Add gauges command group
  • be5f8ae Add notifications command group
  • dbd9c60 Add my assignments reports commands
  • 489fa2d Add forwards sorting options
  • 7497f40 Fix lint regressions after main rebase
  • 10c9177 Refresh surface baseline and smoke coverage

The intent is that a reviewer can read this top-down and see the CLI surface expand one service at a time.

Validation

Local validation on this branch was done with the SDK sync worktree wired in through GOWORK=/tmp/basecamp-cli-sdk-sync.go.work:

  • bin/ci
  • make check-surface
  • targeted service tests for the new command groups

I also ran live read-only checks against account 5978679 for the new surfaces:

  • accounts
  • people profile/preferences/out-of-office
  • reports mine/completed/due
  • gauges list
  • notifications list (after pairing with the SDK follow-up fix)

Intentionally Deferred

These are intentionally not in this draft yet because they should reflect the final merged SDK ref rather than the local prep setup:

  • make bump-sdk
  • internal/version/sdk-provenance.json
  • API-COVERAGE.md

Summary by cubic

Prepares the CLI for the upcoming basecamp-sdk sync by adding new surfaces (accounts, people, gauges, notifications, my assignments) and adapting card and todo moves to the new SDK APIs. The SDK bump is intentionally deferred; this is the CLI implementation only.

  • New Features

    • Accounts: show, rename, logo set/remove.
    • People: profile show/update; preferences show/update; out-of-office show/enable/disable.
    • Gauges: list; project needles; show/create/update/delete; enable/disable.
    • Notifications: list; read by readable SGID.
    • Reports: mine, completed, due [overdue|due_today|due_tomorrow|due_later_this_week|due_next_week|due_later].
    • Forwards: add --sort created|updated and --reverse.
    • Cards: switch to Cards().Move(..., opts) with position support and overflow guard; update TUI move call.
    • Todos: switch to Todos().Reposition(..., opts).
  • Dependencies

    • Blocked on the SDK sync merge and the notifications fix in basecamp-sdk.
    • After SDK lands: run make bump-sdk, then update internal/version/sdk-provenance.json and API-COVERAGE.md.

Written for commit 5810125. Summary will update on new commits.

Copilot AI review requested due to automatic review settings March 24, 2026 16:06
@github-actions github-actions bot added commands CLI command implementations tui Terminal UI tests Tests (unit and e2e) breaking Breaking change labels Mar 24, 2026
@github-actions
Copy link
Copy Markdown

⚠️ Potential breaking changes detected:

  • Change in SDK wire contract for moving cards; '/card_tables/{id}/moves.json' was replaced with '/card_tables/cards/{id}/moves.json', which could break scripts relying on the specific URL structure.
  • Removed explicit source_id and target_id in card move payloads; replaced with column_id, which could break integrations parsing or relying on the old payload format.
  • Changed '--scope' values for due reports filtering to include a stricter list (overdue, due_today, due_tomorrow, due_later_this_week, due_next_week, or due_later), potentially invalidating existing CLI scripts using unsupported values.
  • Renamed '--card-table' behavior in card movement logic, which impacts actions relying on inferred table resolution from specific commands or flags.

Review carefully before merging. Consider a major version bump.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR prepares the Basecamp CLI for the upcoming SDK sync by updating existing command integrations to match new SDK signatures and by adding several new service-focused command groups (accounts, people settings, gauges, notifications, reports “mine” views), along with accompanying unit and smoke coverage.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

Changes:

  • Updated card move wiring to the SDK’s new Move(..., opts) signature and position option handling.
  • Added new CLI surfaces: accounts management, people profile/preferences/out-of-office, gauges, notifications, and personal assignment reports under reports.
  • Added/updated unit tests and introduced an SDK-sync-focused smoke suite for the new surfaces.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
internal/tui/workspace/data/mutations_card.go Updates TUI card move mutation to new SDK move signature.
internal/commands/schedule.go Minor adjustment to schedule update flow around attachment embedding.
internal/commands/reports.go Adds reports mine/completed/due commands + scope validation.
internal/commands/reports_test.go Adds tests for the new “my assignments” report commands.
internal/commands/people.go Adds people profile, preferences, and out-of-office subcommands.
internal/commands/people_test.go Extends people mock server + adds tests for new people subcommands.
internal/commands/notifications.go Introduces notifications list and notifications read.
internal/commands/notifications_test.go Adds tests for notifications list/read request behavior.
internal/commands/gauges.go Introduces gauges command group (list, needles, CRUD, enable/disable).
internal/commands/gauges_test.go Adds tests validating gauges request routing and payloads.
internal/commands/forwards.go Adds forward listing sort controls (--sort, --reverse).
internal/commands/forwards_test.go Adds tests for forwards sorting validation and query formation.
internal/commands/commands.go Registers new commands in command-category metadata.
internal/commands/commands_test.go Ensures root command includes new command groups under test harness.
internal/commands/cards.go Updates card move implementation for SDK signature + position options.
internal/commands/cards_test.go Updates move-position tests to match new endpoint/payload contract.
internal/commands/accounts.go Adds accounts show/rename/logo subcommands + logo validation.
internal/commands/accounts_test.go Adds tests for new accounts subcommands (including multipart upload).
internal/cli/root.go Registers new top-level commands in CLI root.
e2e/smoke/smoke_sdk_sync.bats Adds smoke coverage for new SDK-sync surfaces (with scoped skips).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if app == nil {
return fmt.Errorf("app not initialized")
}
if err := app.RequireAccount(); err != nil {
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In accounts show/rename/logo subcommands you call app.RequireAccount() directly, which skips the interactive account resolution behavior provided by ensureAccount (and doesn’t sync app.Names with the resolved account). This makes these commands fail when no account is configured, while most other commands will prompt/resolve. Consider switching these to ensureAccount(cmd, app) for consistent CLI behavior.

Suggested change
if err := app.RequireAccount(); err != nil {
if err := ensureAccount(cmd, app); err != nil {

Copilot uses AI. Check for mistakes.
Comment on lines 770 to 775
if positionSet && position > 0 && cardTableIDVal == "" {
cardTableIDVal, err = getCardTableID(cmd, app, resolvedProjectID, *cardTable)
if err != nil {
return err
}
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cards move still resolves a card table ID when --position is set and the destination column is already numeric (via --to <column-id>). With the new SDK move endpoint, the card table ID is no longer required for positioned moves, so this extra lookup can introduce a regression on projects with multiple card tables (ambiguous prompt/error) for an otherwise-valid command. Consider only resolving the card table when it’s actually needed (e.g., when --to is a column name) and keep the ID lookup strictly for breadcrumbs/UI.

Copilot uses AI. Check for mistakes.
Comment on lines +145 to +150
if reverse {
opts.Direction = "desc"
} else {
opts.Direction = "asc"
}
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--reverse currently only affects the request when --sort is also provided (because opts.Direction is set inside the if sortField != "" block). If a user passes --reverse alone, it silently has no effect. Consider either rejecting --reverse without --sort, or applying Direction to the default sort order as well.

Suggested change
if reverse {
opts.Direction = "desc"
} else {
opts.Direction = "asc"
}
}
if !reverse {
// Preserve existing behavior: explicit sort without --reverse is ascending.
opts.Direction = "asc"
}
}
if reverse {
// Apply reverse to whichever sort order (explicit or default) is in effect.
opts.Direction = "desc"
}

Copilot uses AI. Check for mistakes.
Comment on lines +343 to +350
if !enabled {
summaryVerb = "Disabled"
}

return &cobra.Command{
Use: use,
Short: strings.ToLower(summaryVerb) + " a project gauge",
Long: summaryVerb + " the gauge for a project.",
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enable/disable toggle command help text is grammatically off: Short becomes "enabled a project gauge" / "disabled a project gauge" and Long is past tense. Cobra command descriptions are typically imperative ("Enable a project gauge" / "Disable a project gauge"). Consider adjusting the verb forms so the help output reads correctly.

Suggested change
if !enabled {
summaryVerb = "Disabled"
}
return &cobra.Command{
Use: use,
Short: strings.ToLower(summaryVerb) + " a project gauge",
Long: summaryVerb + " the gauge for a project.",
descriptionVerb := "Enable"
if !enabled {
summaryVerb = "Disabled"
descriptionVerb = "Disable"
}
return &cobra.Command{
Use: use,
Short: descriptionVerb + " a project gauge",
Long: descriptionVerb + " the gauge for a project.",

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 21 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="internal/commands/gauges.go">

<violation number="1" location="internal/commands/gauges.go:257">
P3: Use a flag-specific usage error for missing `--description` instead of `missingArg`, which is intended for argument-missing paths.

(Based on your team's feedback about using ErrUsage for missing required flags.) [FEEDBACK_USED]</violation>
</file>

<file name="internal/commands/forwards.go">

<violation number="1" location="internal/commands/forwards.go:145">
P2: `--reverse` direction for forwards sorting is inverted versus existing CLI sort behavior for created/updated fields.</violation>
</file>

<file name="internal/commands/people.go">

<violation number="1" location="internal/commands/people.go:691">
P2: Treat whitespace-only `--from`/`--to` values as missing so required-flag validation works correctly.</violation>
</file>

<file name="internal/commands/accounts.go">

<violation number="1" location="internal/commands/accounts.go:363">
P2: AVIF/HEIC are listed as supported logo formats, but current MIME detection won’t reliably classify them, so valid files can be rejected.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

} else {
opts.Sort = "updated_at"
}
if reverse {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: --reverse direction for forwards sorting is inverted versus existing CLI sort behavior for created/updated fields.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/commands/forwards.go, line 145:

<comment>`--reverse` direction for forwards sorting is inverted versus existing CLI sort behavior for created/updated fields.</comment>

<file context>
@@ -127,6 +136,18 @@ func runForwardsList(cmd *cobra.Command, project, inboxID string, limit, page in
+		} else {
+			opts.Sort = "updated_at"
+		}
+		if reverse {
+			opts.Direction = "desc"
+		} else {
</file context>
Fix with Cubic

Long: "Enable out-of-office status for a person. Defaults to the current user.",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if startDate == "" || endDate == "" {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Treat whitespace-only --from/--to values as missing so required-flag validation works correctly.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/commands/people.go, line 691:

<comment>Treat whitespace-only `--from`/`--to` values as missing so required-flag validation works correctly.</comment>

<file context>
@@ -364,6 +368,407 @@ func runPeopleShow(cmd *cobra.Command, args []string) error {
+		Long:  "Enable out-of-office status for a person. Defaults to the current user.",
+		Args:  cobra.MaximumNArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			if startDate == "" || endDate == "" {
+				return output.ErrUsage("--from and --to are required")
+			}
</file context>
Suggested change
if startDate == "" || endDate == "" {
if strings.TrimSpace(startDate) == "" || strings.TrimSpace(endDate) == "" {
Fix with Cubic

}
contentType := richtext.DetectMIME(path)
switch contentType {
case "image/png", "image/jpeg", "image/gif", "image/webp", "image/avif", "image/heic":
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: AVIF/HEIC are listed as supported logo formats, but current MIME detection won’t reliably classify them, so valid files can be rejected.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/commands/accounts.go, line 363:

<comment>AVIF/HEIC are listed as supported logo formats, but current MIME detection won’t reliably classify them, so valid files can be rejected.</comment>

<file context>
@@ -161,3 +205,164 @@ func newAccountsUseCmd() *cobra.Command {
+	}
+	contentType := richtext.DetectMIME(path)
+	switch contentType {
+	case "image/png", "image/jpeg", "image/gif", "image/webp", "image/avif", "image/heic":
+		return nil
+	default:
</file context>
Fix with Cubic

Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if description == "" {
return missingArg(cmd, "--description")
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Use a flag-specific usage error for missing --description instead of missingArg, which is intended for argument-missing paths.

(Based on your team's feedback about using ErrUsage for missing required flags.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/commands/gauges.go, line 257:

<comment>Use a flag-specific usage error for missing `--description` instead of `missingArg`, which is intended for argument-missing paths.

(Based on your team's feedback about using ErrUsage for missing required flags.) </comment>

<file context>
@@ -0,0 +1,463 @@
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			if description == "" {
+				return missingArg(cmd, "--description")
+			}
+
</file context>
Fix with Cubic

@github-actions github-actions bot removed the breaking Breaking change label Mar 24, 2026
@robzolkos
Copy link
Copy Markdown
Collaborator Author

Closing due to newer #373

@robzolkos robzolkos closed this Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands CLI command implementations tests Tests (unit and e2e) tui Terminal UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants