Add --due to cards create

Merged
RO
2d ago
+125-18

Fixes #876.

What

basecamp cards create takes -d, --due, with the same help text and parsing as cards update --due:

basecamp cards create "Launch review" --due "next friday" --in myproject
basecamp cards create "Daily card" -d 2026-07-04 --in myproject

The date goes into the create request (CreateCardRequest.DueOn), so the card is made with its date in one POST.

Parsing lives in one helper, parseDueDate in internal/commands/helpers.go: dateparse.Parse, then a YYYY-MM-DD check, and on failure the existing usage error Invalid due date: "<value>". cards create, cards update and todos update all call it. The last two had identical inline copies of this check, so their behavior is unchanged.

On cards create the date is checked with the other flag-only checks, before a - body drains stdin and before any request. A whitespace-only --due " " is rejected rather than creating a card with no date.

Why

Today a dated card takes two calls (cards create, then cards update <id> --due). If the second fails, the card is left without a date, which is what the scheduled job in #876 runs into. The API already accepts due_on on card create.

Testing

  • internal/commands/cards_test.go: the single create POST carries due_on for --due 2026-07-04, -d 2026-07-04 and --due tomorrow, and has no due_on key without the flag. not-a-date and " " return a usage error with zero HTTP requests. Each test fails if the feature is removed, if the raw value is passed through unchecked, or if the check moves after the network calls.
  • stdin_integration_test.go / stdin_ordering_test.go: a bad --due is rejected before stdin is read; parseDueDate is registered as a pre-read check.
  • Ran the built binary against local bc3 (master c329da98a81) and read each card back from the API: --due 2026-07-04 stored due_on: "2026-07-04", -d tomorrow stored tomorrow's date, and --due not-a-date / --due " " exited 1 with a usage error and created no card.
  • bin/ci passes (includes make check).

Summary by cubic

Adds -d, --due to basecamp cards create so a card is created with its date in one POST instead of two calls. Previously a dated card required cards create followed by cards update <id> --due; a failure in the second call left the card without a date, which is what #876 runs into.

The due date is parsed exactly like cards update --due: natural language or YYYY-MM-DD, with anything that doesn't resolve rejected as a usage error before any request is sent or stdin is read. The check moves into a shared parseDueDate helper used by cards create, cards update, and todos update; the latter two had identical inline copies, so their behavior is unchanged.