Skip to content

Releases: WireCatLabs/gcal-cli

0.10.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 23:55
7fef040
  • gcal now runs on cli-core 0.512.0. Nothing changes for gcal's commands or its MCP server: cli-core 0.512.0 renamed
    id to operation_id in its own calendar MCP tools, which gcal does not serve. gcal's agents write through
    gcal_write, whose events create and events update already take operation_id since 0.9.0, and page
    events list through gcal_read with page.

0.9.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 22:26
d3c0224

Changed — may break callers

  • events create --id and events update --following --id are now --operation-id, the name every WireCat
    write uses for a retry after outcome_unknown (WC-CMD-10, WC-OUT-10). The value is the same: the operation_id
    the lost write's error names. --id is refused as an unknown option; change scripts and agent prompts that pass it.
  • The log of what agents did over gcal mcp names the agent that called, not the server (cli-core 0.509.0): the
    client's own name, such as claude-code, where it was always gcal-mcp. A filter on gcal-mcp no longer finds
    new rows. gcal now runs on cli-core 0.510.0 and cli-base 0.68.1.

Added

  • events list --page <n>: the next page of stored events. A list with more events than --limit (100) says
    page.next_page, and its next step runs the same query one page on; until now it said has_more with no way to
    read further.
  • search events --backend both asks the store and Google Calendar's own search together. The store's matches
    come first, in the store's order, and each one Google also found has server in its found_by; then the events
    only Google found, earliest first. One page with no cursor: it refuses --cursor and --newest, and needs the
    network. Before, both was offered and always refused.

Fixed

  • search events --cursor help no longer names next_offset, a key the answer does not have; the value comes
    from page.next_cursor.

0.8.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 21:15
4bbde0f

Fixed

  • The keyring's file fallback now comes from cli-base 0.68.0 and cli-core 0.507.0, with no workaround of gcal's
    own. cli-base fixed the bug gcal worked around: removing the keyring copy after --credential-storage file also
    cleared the file. A sign-in in the private file is read from there and its refreshes stay there; the next
    session start that reaches the keyring moves it back and removes the file copy. Answers are as in 0.7.0, and the
    keyring warning still comes once per run.

0.7.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 20:08
a9dfe69

Changed — may break callers

  • A raw api write that is easy to undo no longer asks. api calendar-events-insert, -patch, -move,
    -quickAdd, calendar-calendars-insert and the other methods that change Calendar without deleting are allow,
    as events create and events update already were. A method that deletes (calendar.events.delete,
    calendar.calendarList.delete, calendar.acl.delete) and sharing a calendar stay ask; emptying, deleting or
    giving away a calendar stays deny, and events.notify, events.delete.series and sharing.notify still hold on
    every api write. To keep the question, set permissions.api ask.

Fixed

  • session start --help keeps each option to one short line (WC-CMD-22). What --grant write and
    --grant sharing allow, and when to pick --credential-storage file, moved below the options.

0.6.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 14:26
065c339

Added

  • events list finds events from their earliest start: starts_at>=<date>. starts_at now takes either side
    and a range: starts_at>=2026-11-02, starts_at:[2026-11-02 TO 2026-11-07}, and starts_at:2026-11-02 for the
    events of one day. An event that starts exactly at the moment is kept; an all-day event is compared by its date in
    your time zone. In 0.5.0 starts_at>=… was refused. ends_at is unchanged: an earliest end only, strictly after.

Changed — may break callers

  • session start falls back to a private file when the keyring fails, and never asks. It used to
    stop with oauth.not-saved and tell you to pass --credential-storage file. Now it signs in, says once on stderr
    that it used credentials.json in the state folder, and answers credential_storage: "file". The flag still
    chooses the file outright; the keyring is still the default and the safer choice. session status and
    session end no longer answer keyring: "unreachable": the keyring's failure is the stderr warning.
  • A run with moved folders names its temporary keyring entry after its state folder, where it used to name it after
    its config folder; a temporary entry an older run left is no longer read. bin/keyring-dedupe removes it.
  • calendars busy takes the standard field query, and none of its own words. today, week, from: and to:
    are refused. The window is the answer's own start and end: from:2026-11-02 to:2026-11-06 is
    start>=2026-11-02 end<=2026-11-06, and today is start>=today end<=today. Without start the window starts
    today, without end it lasts seven days, so a bare calendars busy is still this week. calendar: and person:
    stay and may still be repeated. Over MCP the input is query, no longer words. The first screen shows
    calendars busy --duration-minutes 30.
  • gcal needs @wirecat/cli-base 0.67 and @wirecat/cli-core 0.502, which now build store status,
    search events <query> and the field queries themselves; the commands, their answers and permission keys are the
    same as in 0.5.0.

0.5.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 12:59
7307612

Added

  • calendars list takes a query: synced:, primary: and access_role: narrow the list, such as
    gcal personal calendars list synced:true.
  • commands info describes what each calendar command answers with, and --fields can pick from it.

Changed — may break callers

  • gcal search <words> is now gcal search events <query>, and gcal status is now gcal store status, the
    shape the other WireCat tools use (max: search messages <query>, store status). The old paths are gone, with no
    alias. Over MCP the command is search events, and its input is query (was words); a store search's next-page
    step names query too. A partial sync's hint points at store status. The permission keys move with them:
    permissions.store.status and permissions.search.events; permissions.search still covers the search. A config
    that still sets permissions.status is refused with config.invalid, which names
    gcal <profile> config unset permissions.status.
  • events list takes only the standard field query, and none of its own words. today, week, from:, to:,
    changed: and overlapping are refused. Write the window as fields: from:2026-11-02 to:2026-11-06 is
    ends_at>=2026-11-02 starts_at<=2026-11-06, and changed:2026-10-25 is changed_at>=2026-10-25; calendar: and
    person: stay. Over MCP the input is query ("ends_at>=2026-11-02"), no longer words. What callers lose:
    • today and week: write the dates. The query language has today but no word for a day ahead, so the end of a
      window is an ISO date.
    • overlapping and the overlaps field: no standard term asks for events that clash, so the list no longer finds
      them. Compare starts_at and ends_at of the listed events instead.
    • starts_at>=… is refused and names the ends_at>=… term to write instead: the store finds events by the moments
      they span, so it can bound only the latest start and the earliest end. An event that ends exactly at the
      ends_at moment is left out.
    • Without a time term the list still starts at the beginning of today.
  • Deleting a whole Google calendar and giving it to another account are deny by default, as emptying one is.
    api.calendar.calendars.delete and api.calendar.calendars.transferOwnership were ask, which over MCP runs with no
    question. Set either key to ask or allow to let it run.
  • session status and session end answer where no keyring can be reached and no sign-in file is kept. They
    failed with oauth.not-saved. Now session status says state: missing with keyring: unreachable, and
    session end says ended: false with keyring: unreachable: this run can use no sign-in, so there is nothing to
    show or end. A sign-in kept with --credential-storage file is read as before. session start and the commands that
    call Google still fail with oauth.not-saved, which troubleshooting explains. A script
    that took the failure to mean "keyring out of reach" reads keyring in the answer instead.
  • people list takes one event as event:<id>. gcal personal people list 42 is refused as a word with no
    field; write people list event:42. Over MCP the input is query ("event:42"), no longer event.
  • search --backend server answers as the shared search does. Each event's found_by is ["server"] (was
    ["google"]). The refusals have new reasons: no synced calendar is search.no-calendars (was
    calendars.none-synced), --offline is search.offline (was offline.refused), --exact, --cursor or
    --newest is search.server-options, and --backend both is search.one-backend (was search.backend-both).
  • The calendar commands' help no longer lists --json and --jsonl under each command, and their MCP tools no
    longer take json or jsonl inputs. The global --json and --jsonl work as before; an agent that sent
    jsonl: true was refused.
  • config show --bot and config set|unset --personal|--bot are hidden. gcal has no bots, so they had nothing
    behind them. They are still accepted, so a script that passes one keeps working.

Fixed

  • Calls an agent sends to gcal mcp serve at the same time each get their own answer. Two calendar reads sent
    together could answer one of them empty ({"item":{"value":null}}) or fail it with command.crashed, because the
    calls shared which command was running and the store it had opened. Each call now keeps its own
    (WC-ARCH-22).
  • An all-day event with no time zone of its own or of its calendar starts at midnight in your computer's time zone.
    gcal read it as midnight UTC: on a computer in Madrid it started an hour late, and in New York on the evening before.
    Such an event's timezone now names the zone it was read in, where it was null. An event or calendar that names
    its zone is read in that zone, as before. An event already stored keeps its old start until Google sends it again,
    after it changes
    (WC-TIME-3).

0.4.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 03:40
24924eb

Fixed

  • gcal keeps the shared local store at the same version as the other WireCat tools (store version 40). The store
    gains the trigram index that notes and documents search uses; gcal has no notes of its own, but an older gcal no
    longer leaves a store written by a newer tool without the index entries it should write.

0.3.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 02:22
3eba1e1

Added

  • gcal models list and gcal models download <model> fetch the model that search by meaning uses. The search's
    hint named gcal models download e5-small, which did not exist. The model goes into the folder every WireCat tool
    shares, so one already downloaded by another WireCat tool is used as it is.
  • events move <event> --to-calendar <id> moves an event, with its id, to another calendar of the same account.
    It refuses a calendar you cannot write to, the same calendar, an event Google does not move (focus time, out of
    office, working location, birthdays) and an occurrence without --series. Over MCP it is the events move command.
  • events update --following and events delete --following change or delete "this and all following"
    occurrences.
    An update ends the series just before the occurrence and starts a new series there with the change,
    keeping the attendees; a delete only ends the series. A series that ends after a number of times needs --repeat
    for the new part.
  • events list changed:<date> lists what changed since a date, from the store: each event says change (new,
    moved or cancelled) and changed_at, and a moved one where it was.
  • events list overlapping lists the meetings that clash, each with overlaps, the other events. Only events that
    take your time count: not cancelled, not all-day, not declined or marked free.

Changed — may break callers

  • Creating, changing, moving and answering events go ahead without a question by default. events.create,
    events.update, events.respond and the new events.move are allow (were ask); a script no longer needs --yes
    for them. events.delete stays ask, --following included. To be asked again, set a key to ask, such as
    gcal personal config set permissions.events.create ask. The api.calendar.events.* keys stop the event writes
    once you set them; their built-in ask holds for the api commands only.
  • A series update whose new rule ends it sooner also needs events.delete.series, which is deny by default. A
    new COUNT, an earlier UNTIL, or a bound on a series that had none removed the later occurrences under
    events.update alone.
  • A sync where a calendar fails prints nothing on stdout and fails with sync.partial. Its message names each
    failed calendar and its error, details.calendars holds every calendar's result (error on the failed ones), and
    the error says a retry is safe. The calendars that synced are saved. A sync stopped by its time or request budget
    still answers state: "partial" and succeeds.
  • search answers a list: the events under items, with page in cursor mode, as every other list does. It
    answered one item holding hits, has_more and untrusted (WC-OUT-4). A script reads items in place of
    item.hits, and page.next_cursor is the value for --cursor. Each event names its own untrusted fields, and why
    meaning was not searched is under missing. query and by are gone; each event still says found_by.
  • links review answers its reviews under items, without accounts: "all". It answered one item holding the
    list. Link choices are still kept per store, so every account's reviews are listed.
  • search --backend server refuses --newest. Google returns its matches in no stated order and gcal reads only
    the first page of each calendar, so the newest event could be missing from a "newest first" answer. Its matches
    are listed earliest first, and each event's found_by is ["google"], as a store search writes it.

Fixed

  • gcal --help no longer offers a --read-only option, which does not exist. GCAL_READ_ONLY=1 is how a run
    refuses every change.
  • events delete of an event already deleted at Google succeeds and removes the stored copy. Google answers such a
    delete with 410, and 404 once it no longer shows the event; gcal failed on both and kept the event in the store
    until the next sync.
  • The daily full sync no longer marks events outside its window as deleted. Older and later single events, moved
    occurrences and ended series were marked deleted and their events cancelled. A store this damaged heals on its next
    sync.
  • A repeating event moved to another time no longer keeps its occurrences at the old time after an incremental
    sync.
  • A sync --rescan stopped by its budget no longer leaves the calendars it did not reach on incremental rounds.
  • A create Google made but the store failed to save names the event's id, so a repeat with --id finds it.
  • sync writes to the store in far fewer transactions, and the daily full round asks only changed series for their
    occurrences.
    events list by time and people list read the store faster.
  • A refusal from Google for a missing or forbidden item names a reason: google.not-found or google.denied,
    so a script can branch on it instead of the message.
  • A sign-in cut short by a timeout or Ctrl-C still closes the store it opened.
  • gcal api call --help no longer shows Default: [object Object] for --params. Left out, it still sends no
    parameters.
  • gcal starts in about half the time (gcal --help: 0.40 s before, 0.19 s now, on a laptop with Node 24). The
    package ships one bundled program instead of hundreds of modules. A sync also maps each event faster.

Security

  • search --backend server marks the right fields as written by others. Its untrusted list named title and
    location, while the answer held them under hits, so an agent that follows the list did not treat them as data.
    Each event now names title and location itself.
  • gcal api refuses an empty, . or .. id in a request's address (api.bad-path-id). Such an id made the
    address point at another Google endpoint, calendars/primary/events/.. at the calendar itself; nothing is sent now.
  • session end in a run with moved folders keeps the owner's sign-in in the system keyring, and says so. A run
    with GCAL_CONFIG_DIR, GCAL_STATE_DIR or a moved HOME uses the keyring entry every run of gcal shares; ending
    the session there would have signed the owner out everywhere. It now answers kept: "keyring" and says the run is
    still signed in; ended is true only when the run had a sign-in of its own to remove. A run in the normal folders
    ends the sign-in as before.
  • A sign-in in a run with moved folders no longer replaces the owner's sign-in in the system keyring. It goes to
    the run's own entry, gcal-cli:<config folder>, which the run reads first and its session end removes; the run
    otherwise reads the owner's entry and never changes it. So a test sign-in with another Google account can no longer
    switch the owner's profile.

0.2.0

Choose a tag to compare

@github-actions github-actions released this 30 Oct 11:57
cf1180e

Added

  • The gcal-cli skill is published on ClawHub as wirecat-gcal after each release, and tells OpenClaw that it
    needs gcal and how to install it
    (metadata.openclaw: requires.bins and the npm package @wirecat/gcal-cli).
    With gcal missing from PATH, OpenClaw marks the skill not ready and offers to install the package. Claude Code and
    Codex ignore the block.

0.1.1

Choose a tag to compare

@github-actions github-actions released this 30 Oct 07:53
08cfde2

Fixed

  • The agent guide's header (skills/gcal-cli/SKILL.md) is valid YAML again. Its description held
    Calendar: sync, which a YAML parser reads as a nested mapping, so strict skill loaders and the WireCat site's
    skills index refused the file. The text is unchanged apart from a dash in place of that colon.