Skip to content

Add hey event commands (list, create, edit, delete)#79

Open
guilhermeyo wants to merge 28 commits intobasecamp:mainfrom
guilhermeyo:add-event-commands
Open

Add hey event commands (list, create, edit, delete)#79
guilhermeyo wants to merge 28 commits intobasecamp:mainfrom
guilhermeyo:add-event-commands

Conversation

@guilhermeyo
Copy link
Copy Markdown

@guilhermeyo guilhermeyo commented Apr 15, 2026

Refs #48.

Adds hey event list|create|edit|delete on top of the CalendarEventsService added to the SDK in basecamp/hey-sdk#30 — thanks @monorkin for the SDK piece, this plugs into it with no further SDK changes.

Commands

hey event list [--calendar <id-or-name>] [--limit N] [--all] [--ids-only]
hey event create --title T --date YYYY-MM-DD (--all-day | --start HH:MM --end HH:MM) \
  [--calendar <id-or-name>] [--timezone TZ] [--reminder DUR ...]
hey event edit <id> [--title T] [--date D] [--start T] [--end T] [--all-day] \
  [--timezone TZ] [--reminder DUR ...]
hey event delete <id>

--calendar accepts a numeric ID or the name of an owned calendar (case-insensitive). --reminder accepts 30m, 1h, 2d, 1w and is repeatable.

Scope intentionally matches CreateCalendarEventParams / UpdateCalendarEventParams exactly — title, date, start/end or all-day, timezone, reminders. Location, description, attendees, recurrence, and countdown aren't wired up here; they'd need corresponding SDK additions first.

Default calendar UX

When --calendar is omitted the CLI uses the existing findPersonalCalendarID helper (same one hey recordings uses). On some accounts the personal: true calendar returns 404 on event creation — when that happens we catch the 404 and print the owned calendar list with a --calendar <id-or-name> hint instead of a bare HTTP error. Same list is shown if findPersonalCalendarID can't find a personal calendar at all.

Test plan

  • Unit tests (httptest) — list (default / limit / ids-only / by-calendar-name), create (timed / all-day / title-required), edit (happy / invalid-id / only-changed-fields), delete, calendar name resolution (match / ambiguous / not-found), default-calendar fallback error
  • Manual E2E against a real HEY account — full create → list → edit → delete round-trip plus both error paths (unknown calendar name, default-calendar 404 fallback)

Summary by cubic

Adds CLI support to list, create, edit, and delete calendar events with hey event. Improves timezone detection and input validation, with clearer errors when choosing calendars.

  • New Features

    • Commands: hey event list|create|edit|delete
    • List: --calendar <id-or-name>, --limit, --all, --ids-only (uses a safe date window when targeting a specific calendar)
    • Create: --title, --date, --all-day or --start/--end; optional --timezone (defaults to local for timed events; resolves via $TZ and /etc/localtime, requires --timezone if undeterminable; rejects "Local"); repeatable --reminder (m|h|d|w, rejects overflow); --timezone and --start/--end cannot be combined with --all-day; trims whitespace
    • Edit: update any subset of --title, --date, --start, --end, --all-day, --timezone, --reminder; requires at least one flag; rejects empty --title; --timezone and --start/--end cannot be combined with --all-day; trims whitespace
    • Delete: hey event delete <id> (rejects non‑positive IDs)
    • Calendar resolution: --calendar accepts an ID or case-insensitive owned name; rejects empty/whitespace and non-positive IDs; helpful errors for unknown or ambiguous names with a hint to run hey calendars
    • Default calendar: uses your personal calendar when --calendar is omitted; if missing or event creation returns 404, shows owned calendars and suggests --calendar
    • Testing: fixes a data race when running with -race
  • Dependencies

    • Pin github.com/basecamp/hey-sdk/go to v0.3.1-0.20260407122900-212ceb7d1fe6 to consume CalendarEvents until the next SDK tag is released.

Written for commit 82cfd59. Summary will update on new commits.

The /calendars/:id/recordings endpoint rejects requests without
starts_on/ends_on with 400. listPersonalRecordings already handles
this via personalRecordingsLookback/Lookahead constants; apply the
same window to the explicit --calendar path.
--calendar now accepts either a numeric ID or a case-insensitive owned
calendar name. When a name can't be resolved (or is ambiguous), the CLI
surfaces a helpful error pointing to 'hey calendars'. When no --calendar
is given and the default personal calendar can't be determined, the
error now lists owned calendars so the user can pick one.
When findPersonalCalendarID picks a calendar that HEY won't accept new
events in (some accounts have an orphaned personal:true calendar), the
raw 404 is unhelpful. Fall back to listing owned calendars and suggest
--calendar explicitly — only triggered when --calendar was not set.
The Task 9 edit replaced the prior hint with --calendar info only;
keep both since agents benefit from knowing the pipe-workflow too.
Aggregated cleanup from code review:
- event.go: reuse default calendar list on 404 fallback, dedupe filter
  call, rename list-command local to match siblings, drop unreachable
  reminder nil guard
- event_test.go: unify four runEvent* helpers into one, drop redundant
  server factories in favor of the Custom variants, use strconv.ParseInt,
  merge capturedRequest and capturedMethodPath
Copilot AI review requested due to automatic review settings April 15, 2026 01:42
@github-actions github-actions bot added the enhancement New feature or request label Apr 15, 2026
Copy link
Copy Markdown
Contributor

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

Adds a new hey event command group to manage calendar events (list/create/edit/delete) via the HEY SDK’s CalendarEvents service, plus supporting documentation and unit tests.

Changes:

  • Introduces hey event list|create|edit|delete commands and calendar name/ID resolution.
  • Adds httptest-based unit coverage for event operations and default-calendar fallback behavior.
  • Updates user-facing docs and command surface metadata to include the new commands.

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.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
skills/hey/SKILL.md Documents the new hey event commands and examples for the skill surface.
internal/cmd/root.go Registers the new event command on the root CLI.
internal/cmd/event.go Implements hey event subcommands, reminder parsing, and calendar resolution.
internal/cmd/event_test.go Adds unit tests covering list/create/edit/delete and calendar resolution behaviors.
README.md Adds end-user documentation and examples for hey event.
API-COVERAGE.md Records SDK endpoint coverage for event mutations.
.surface Exposes the new commands/flags to the command surface list.

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

Comment thread skills/hey/SKILL.md Outdated
Comment thread README.md Outdated
Comment thread internal/cmd/event.go
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 7 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="API-COVERAGE.md">

<violation number="1" location="API-COVERAGE.md:35">
P2: API coverage docs were updated for event create/edit/delete but omitted the new `hey event list` command mapping, causing documentation drift.</violation>
</file>

<file name="internal/cmd/event_test.go">

<violation number="1" location="internal/cmd/event_test.go:146">
P2: Tests miss coverage for the default-calendar POST 404 fallback path, so regressions in that branch would not be detected.</violation>
</file>

<file name="internal/cmd/event.go">

<violation number="1" location="internal/cmd/event.go:372">
P2: `event edit` allows an empty update request when no editable flags are provided, causing avoidable API calls/errors instead of a local usage error.</violation>

<violation number="2" location="internal/cmd/event.go:538">
P2: Default timezone logic can incorrectly force timed events to UTC when Go reports the local location as "Local", causing shifted event times.</violation>
</file>

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

Comment thread API-COVERAGE.md
Comment thread internal/cmd/event_test.go
Comment thread internal/cmd/event.go
Comment thread internal/cmd/event.go Outdated
Previously --timezone was silently ignored for all-day events, which is
inconsistent with the flag help and can surprise users. Fail fast with
a usage error instead.
Calling `hey event edit <id>` with no fields previously issued an
empty PATCH request. Fail fast locally with a usage hint listing the
editable flags.
time.Local.String() returns "Local" (not an IANA name) in some Go
runtime configurations. Previously the code silently fell back to UTC,
which would shift event times by the user's offset. Return an empty
string from localTimezoneName in that case and require --timezone from
the caller.
- README / SKILL: describe the real reminder format (non-negative number
  followed by m/h/d/w) instead of implying only the four sample values
- API-COVERAGE: map 'hey event list' to the GetRecordings row alongside
  the other list-style consumers
- event_test: cover the default-calendar-returns-404 fallback path so
  regressions in that error branch surface in CI
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.


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

Comment thread internal/cmd/event.go
Comment thread internal/cmd/event.go Outdated
Comment thread internal/cmd/event.go Outdated
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.

3 issues found across 5 files (changes from recent commits).

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/cmd/event_test.go">

<violation number="1" location="internal/cmd/event_test.go:347">
P2: The no-request assertion is weak: checking only empty body can pass even when a PATCH request was made with an empty payload.</violation>
</file>

<file name="internal/cmd/event.go">

<violation number="1" location="internal/cmd/event.go:190">
P2: When `--all-day` is set, `--start` and `--end` are silently accepted and ignored. This makes it easy to think a timed event was created when it's actually all-day. Add a validation check rejecting `--start`/`--end` when `--all-day` is present, similar to the `--timezone` check added here.</violation>

<violation number="2" location="internal/cmd/event.go:336">
P2: The edit command validates `--timezone` with `--all-day` but allows `--all-day` combined with `--start`/`--end`, which produces conflicting update parameters (all-day flag plus explicit start/end times). Add a similar rejection for `--start`/`--end` when `--all-day` is set.</violation>
</file>

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

Comment thread internal/cmd/event_test.go Outdated
Comment thread internal/cmd/event.go Outdated
Comment thread internal/cmd/event.go Outdated
Previously the flags were silently ignored when --all-day was also set,
letting a user think they scheduled a timed event when they created an
all-day one. Fail fast with a usage error, matching the treatment of
--timezone + --all-day.
time.Local.String() returns "Local" on systems where the zone was loaded
from /etc/localtime (the common case on Linux without $TZ set). Erroring
out in that case forced users to always pass --timezone. Try $TZ next,
then resolve /etc/localtime's symlink to recover the IANA name.
Previously only the request body was inspected, which would pass even
for an empty PATCH. Check the captured method/path remain empty so the
test actually verifies the early return.
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.


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

Comment thread internal/cmd/event.go Outdated
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.

2 issues found across 2 files (changes from recent commits).

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/cmd/event.go">

<violation number="1" location="internal/cmd/event.go:588">
P2: Raw `$TZ` value is used as event timezone without IANA validation, so valid POSIX TZ formats can be sent to the API and fail.</violation>
</file>

<file name="internal/cmd/event_test.go">

<violation number="1" location="internal/cmd/event_test.go:225">
P2: Using `t.Skipf` on symlink setup failure skips the entire test, unintentionally dropping non-symlink assertions and reducing cross-platform coverage.</violation>
</file>

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

Comment thread internal/cmd/event.go
Comment thread internal/cmd/event_test.go
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.


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

Comment thread internal/cmd/event.go Outdated
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 15, 2026 03:40
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.


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

Comment thread internal/cmd/event.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 15, 2026 03:52
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.


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

Comment thread internal/cmd/event.go Outdated
A review suggestion assigned []generated.CalendarEvent{} where
filterRecordingsByType returns []generated.Recording, breaking the
build. Use the correct type for the nil-to-empty slice fallback.
The CalendarEventsService landed in basecamp/hey-sdk@212ceb7 on main but
has not been tagged. Using a pseudo-version pinned at that commit so CI
can build this branch. Bump to v0.4.0 (or whatever the next SDK tag is)
once it's released.
Copilot AI review requested due to automatic review settings April 15, 2026 12:33
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.


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

Comment thread internal/cmd/event.go Outdated
Comment thread API-COVERAGE.md Outdated
Defers run LIFO, so the previous order (server defer first, time.Local
swap second) caused the time.Local restoration to run while the httptest
server goroutines were still calling time.Now() — triggering -race.
Swap the setup order so server.Close() runs before time.Local is
restored.
- resolveCalendarID now rejects --calendar=0 or --calendar=-1 with a
  clear usage error instead of falling through to a name lookup that
  produces a misleading 'no owned calendar named "0"' message and an
  unnecessary HTTP request.
- API-COVERAGE.md: drop the stray .json suffix on /calendars/{id}/
  recordings; the SDK hits the path without it.
Copilot AI review requested due to automatic review settings April 15, 2026 12:59
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.


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

Comment thread internal/cmd/event.go
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.

1 issue found across 3 files (changes from recent commits).

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/cmd/event.go">

<violation number="1" location="internal/cmd/event.go:564">
P3: Whitespace-only `--calendar` values pass through to the name-lookup branch and produce a confusing error like `no owned calendar named ""`. Add an early check for `trimmed == ""` after `TrimSpace` and return a clear usage error (e.g., `--calendar cannot be empty`).</violation>
</file>

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

Comment thread internal/cmd/event.go
…t IDs

Pre-empt three adjacent gaps surfaced by the latest reviewer pass:

- resolveCalendarID: reject empty/whitespace --calendar with a clear
  usage error instead of falling through to a name lookup that yields
  'no owned calendar named ""'.
- event edit: reject --title "" so an accidental blank doesn't silently
  wipe the event's summary on the server.
- event edit/delete: reject non-positive event IDs locally instead of
  handing them to the API, mirroring what we already do for --calendar.
@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai bot commented Apr 15, 2026

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.


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

Comment thread internal/cmd/event.go
time.LoadLocation("Local") succeeds (Go treats it as an alias for the
process-local zone), so the previous check let the literal string
"Local" through to the HEY API where it isn't a meaningful IANA name.
Reject it explicitly with a usage hint pointing at real IANA names.
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 8 out of 9 changed files in this pull request and generated no new comments.


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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants