Repository navigation
feat(calendar): free/busy, repeating events, timed/all-day switch - #13
Conversation
…r Google - calendars busy: googleCalendarSource.freeBusy over freebusy.query (calendar.readonly allows it), busy times clipped to the window, an id Google will not show reported with its reason; mounted through cli-core's shared command (deps.freeBusy). - events create|update --repeat: the rule goes to `recurrence`; a repeating all-day event's start and end carry timeZone, which Google requires for recurring events. - Timed <-> all-day: the writer fetches the event, refuses if its etag is not the synced one, and replaces it with events.update (conferenceDataVersion=1, supportsAttachments) instead of a patch that would need `start.dateTime: null`. An update also checks api.calendar.events.update. - Fakes from the API reference: freeBusy answers, a full-replace PUT. Tests for each path, both switch directions, the EXDATE kept on a rule change, busy in two time zones. - Lint overrides for the words in cli-base#381 (unreleased); usage, design, skill and changelog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…412; prove the replace keeps conference data and files Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
….465.0) - Pin @wirecat/cli-core 0.465.0 and @wirecat/cli-base 0.64.0 exactly; drop the lint overrides and baselines cli-base 0.64's vocabulary makes stale. - requestMs 1.5 s on the app identity: an estimate for one events.list page or one series' events.instances until a live run measures it. - A page token Google refuses on resume (400 or 410 with a pageToken) is a reset, so the engine starts that calendar's round over once; Google states no lifetime for page tokens (events.list reference). - A partial sync answers meta.complete false and lifts the engine's next step into the envelope: `gcal <profile> sync` with the same flags as the last line in text, the command and args for an agent. - `status` (top-level) shows each calendar's progress; `sync --rescan` starts over. - bin/gcal-live-check loops sync until complete, printing progress, at most 40 runs (GCAL_LIVE_SYNC_RUNS). - Tests follow cli-core 0.462's skill envelope and fix a test type error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
19acfa0 to
2bba257
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Not merging yet. Everything in this PR passes, but CI's cross-CLI audit ( The findings are differences that already exist between the other CLIs. gcal now joins them: All ten checks the brief named pass locally on cli-core 0.465.0 and cli-base 0.64.0, as do 🤖 Generated with Claude Code |
…l the others converge (#391) ## Task Coordinator decision (option B from the cli-core fix-wave-A lane): unblock gcal-cli#13's release audit without declaring differences. Tracking: #390. ## Why cli-base 0.64.0 put gcal into the `all` parity group. `same-path-contract` names every compared CLI in its target and cannot be baselined, so gcal's audit (WireCatLabs/gcal-cli#13 (comment)) blocks on 20 differences in `doctor`, `mcp`, `search` and `skill`. These already exist among tg, max, wa, memo, drive and zoom as published, and only their releases on cli-core's shared commands can remove them. ## What - `all` loses gcal. - New group `gcal-shared`: every CLI, including gcal, over `commands`, `upgrade`, `notes`, `meetings`. That is what gcal already shares identically; `skill` stays out because drive's summaries still differ. - The other CLIs' audits do not change: a path is compared across every member of every group that covers it. - CHANGELOG Unreleased → Fixed. ## Testing - `pnpm ci:quick`, `vitest run` (901 passed), `pnpm docs:check`. - Local `cli-dev audit --for gcal` with this build, against the published tg, max, wa, memo, drive and zoom and gcal-cli main's dist: exit 0, "0 new that block" (gcal-cli#13 CI showed 20 with 0.64.0). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## Task Release `cli-dev audit`'s new parity groups ([#391](#391)) so gcal-cli's release audit stops blocking [gcal-cli#13](WireCatLabs/gcal-cli#13). ## Why `## Unreleased` holds a "Changed — may break callers" section (handbook rule changes, and `cli-dev lint` now fails on stale `serve`/`revoke` baseline entries), so the minor is raised: 0.64.0 → 0.65.0 (WC-REL-7). ## What `package.json` version and the changelog heading only. ## Testing - `pnpm docs:check`, `pnpm lint` pass. - Dry run: the built `cli-dev audit` from this branch over gcal-cli#13's `dist` and the six published CLIs gives `audit: 7 CLIs, 420 findings, 0 new that block`; `cli-dev lint` on gcal gives `0 new`, no stale entries. - The "Template" check fails on #391 too: cli-template lists `mcp serve` and `mcp revoke` in its unknown-verb baseline, which this release makes stale. That is the expected follow-up for cli-template, not a fault here. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## Task Move cli-core to `@wirecat/cli-base` 0.65.0, so gcal-cli can take it: its release audit ([gcal-cli#13](WireCatLabs/gcal-cli#13)) passes only with cli-base 0.65's parity groups ([cli-base#391](WireCatLabs/cli-base#391)). ## Why The CLIs pin cli-base exactly, and cli-core's peer range `>=0.64.0 <0.65.0` does not allow 0.65; a CLI on 0.65 would get two copies of cli-base. ## What - `@wirecat/cli-base` 0.64.0 → 0.65.0. Peer range → `>=0.65.0 <0.66.0` (WC-REL-8), as in #2115. - `lint/baseline/unknown-verb.json`: `bot mcp serve`, `mcp serve`, `mcp revoke`, `replies consents revoke` removed. cli-base 0.65 has `serve` and `revoke` in the vocabulary and `cli-dev lint` fails on stale entries. - `docs/dev/STANDARD.md` regenerated (`pnpm standard:docs`). - Changelog under "Changed — may break callers". ## Testing `pnpm ci:quick`, `pnpm docs:check`, `pnpm lint` locally; CI on this PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
cli-base 0.65 compares gcal only on the commands it already shares (cli-base#391), so the release audit no longer blocks on the 20 same-path-contract differences the other CLIs carry. cli-core 0.469 is the first that accepts cli-base 0.65. docs/commands.md regenerated: config settings and skill show follow cli-core 0.467-0.468. No lint baseline or override entry went stale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Task
The owner chose gcal's three roadmap items next, then a budgeted, resumable sync after the first live
sync(8 calendars) died at the 30 s deadline and lost the round. The shared commands are in cli-core (WireCatLabs/cli-core#2112); this is the Google adapter.What
calendars busy:googleCalendarSource.freeBusyoverfreebusy.query. It works withcalendar.readonly(Freebusy: query listscalendar.readonlyamong its scopes). Busy times are clipped to the window. An id Google will not show comes back with its reason (notFound) and is listed inunknown, never as free. Nothing is stored.--repeat: the rule goes torecurrence. A repeating all-day event's start and end carrytimeZone; Google requires it for recurring events (Events resource, start.timeZone).events.patchmergesstart, and clearingdateTimewould need anullthat cli-base's generated request check refuses. So the writer fetches the event and replaces it withevents.update(If-Match= the synced etag,conferenceDataVersion=1,supportsAttachments=true). This is the get-then-update that Events: update documents for partial changes, and it needs no cli-base release. A plain time change stays a patch.writeKeys("update")now also namesapi.calendar.events.update.syncstops before its deadline (5 min at a terminal, 45 s over MCP;--timeoutoverrides), saves after every page, and the next run goes on.requestMs: 1_500on the app identity sets the request budget,(deadline − margin) / 1.5 s; it is an estimate until a live run measures it.--max-pageslowers it,--rescanstarts over. A partial answer hasmeta.complete: falseand anextstep (gcal personal sync, same flags) as the last line in text and as command + args for an agent. A run that moved nothing gets no auto-run step, only a hint. Top-levelgcal personal statusshows per-calendar progress (WC-LIFE-14 shape).pageTokenis a reset, so the engine restarts that calendar's round once. Google states no lifetime for a page token (events.list, sync guide). The fake answers Google's documented 400 body for an unknown page token.bin/gcal-live-checkloopssyncuntilstate: complete, printing each run's progress, with a cap of 40 runs (GCAL_LIVE_SYNC_RUNS).@wirecat/cli-core0.465.0 and@wirecat/cli-base0.64.0 exactly. The cli-base#381 lint overrides and the baselines 0.64's vocabulary covers are gone. Tests follow cli-core 0.462's skill envelope.Follow-up
docs/roadmap.mdexists only on chore(release): 0.1.0 #11 (0.1.0): moving the three items to "What works today" happens when chore(release): 0.1.0 #11 is refreshed.Not tested against live Google
{ date, timeZone }on a repeating all-day event.bin/gcal-write-checkcan cover these on a test calendar.Testing
On the released cli-core 0.465.0 and cli-base 0.64.0:
ci:quick,lint,typecheck,test:coverage,generate:check,contracts:check,test:matrix(177 tested, 0 missing),docs:check,package:check,standards:check,security:no-leak, all passing.Not run:
bin/gcal-live-checkagainst real Google (needs the owner's sign-in).🤖 Generated with Claude Code