@bobfrankston/gcal
v0.1.81
Published
Google Calendar CLI tool with ICS import support
Maintainers
Readme
@bobfrankston/gcal
Google Calendar and Google Tasks CLI tools. Two binaries (gcal, gtask) shipped together — they share OAuth credentials, scopes, and token files, so you authenticate once.
Installation
npm install -g @bobfrankston/gcalProvides:
gcal— Google Calendargtask— Google Tasks
Both share OAuth client credentials with gcards (%APPDATA%\gcards\credentials.json).
gcal — Google Calendar
gcal <file.ics> # Import ICS file (file association)
gcal <command> [options]
gcal help <command> # Detailed help for one commandCommands
| Command | Description |
|---------|-------------|
| list [n] | List upcoming n events (default: 10) |
| show <id> | Show full details for an event (-json for raw JSON) |
| add <title> <when> [duration] | Add event (explicit) |
| add <title> <date> [days] -allday | Add all-day event spanning [days] (default 1) |
| add "<free text>" | Add event (AI-parsed) |
| add -clip | Add event from clipboard text — or image, if there is no text (AI-parsed) |
| add | Add event interactively |
| del \| delete <id> [id2...] | Delete event(s) by ID prefix |
| remind <id> <dur> [dur2...] | Add reminder(s) |
| resched <id> <when> [duration] | Reschedule (preserves duration) |
| snooze <id> [when] | Snooze (default +1d) |
| import <file.ics> | Import events from ICS |
| calendars | List available calendars |
| assoc | Set up .ics file association (Windows) |
| help [command] | Show help |
Options
| Flag | Description |
|------|-------------|
| -u, -user <email> | Set / use default Google account |
| -c, -cal, -calendar <name> | Calendar to use (default: primary). Case-insensitive partial match on calendar name or ID, e.g. -cal family; errors if ambiguous |
| -n <count> | Number of events to list |
| -v, -verbose | Show event IDs and links |
| -b, -birthday | Birthdays: include them in list/del; on add, print a Google Contacts link for the person (see below) |
| -clip | Read from clipboard — text, or an image if there is no text (for add) |
| -r, -reminder <dur> | Add popup reminder (e.g. 30m, 1h); repeatable |
| -since <date> | Start listing from <date> |
| -till <date> | End listing at <date> |
| -all | Delete all instances of recurring event |
| -json | Output raw JSON (for show) |
Examples
gcal meeting.ics
gcal list
gcal list -since "10 days ago"
gcal list -since "april 1" -till "may 1"
gcal add "Dentist" "Friday 3pm" "1h"
gcal add "Lunch" "1/14/2026 12:00" "1h"
gcal add "Dentist appointment Friday 3pm for 1 hour"
gcal add -clip
gcal add "Dentist" "Friday 3pm" -r 30m
gcal add "Ann Smith" "mar 5 1950" -birthday
gcal add "Ann Smith's birthday is March 5"
gcal show abc12345
gcal show abc12345 -json
gcal remind abc12345 30m
gcal resched abc12345 "next friday 3pm"
gcal snooze abc12345 +1w
gcal -u [email protected]Clipboard images
gcal add -clip prefers clipboard text. When the clipboard has no text, it
reads an image instead — a screenshot of an invitation, email, flyer, or an
image file copied in Explorer — and extracts the event(s) from it. Large images
are downscaled (long edge 1568px, JPEG if still oversized) before being sent.
# Win+Shift+S to snip an event flyer, then:
gcal add -clipWindows works out of the box. macOS needs pngpaste
(brew install pngpaste); Linux needs wl-paste (Wayland) or xclip (X11).
Conflict / proximity warnings
When adding or rescheduling a timed event, gcal prints a warning for any existing event that overlaps the new slot or falls within 1 hour before or after it. An overlapping event that is itself marked free is listed with "(free)".
On add, a real conflict — the new event is busy and it overlaps an existing busy event — asks "Create anyway? [y/N]". The default is No and the 60-second auto-yes does not apply. A -free event never asks. On update and resched the warnings are informational only.
Busy vs free
New events show as busy unless you pass -free, or (in AI mode) the text says so — "free", "not busy", "tentative", "optional", "FYI", "hold", and the like set the event to free. -free/-busy on the command line always win over the text.
Birthdays
A birthday belongs on the person, not on the calendar: Google Calendar shows contact birthdays in its Birthdays layer by itself (when "Birthdays from contacts" is on in Calendar's settings). So gcal add "<name>" "<date>" -birthday writes nothing. It prints the date to enter and a clickable Google Contacts search link for the name, e.g. https://contacts.google.com/search/Ann%20Smith; you pick the person there and add the birthday. -open opens the link. The first argument is the person's name; "Ann Smith's birthday" is accepted and the suffix dropped. A 4-digit year in the date ("mar 5 1950") is shown as the birth year. In AI mode the text decides: "Ann Smith's birthday is March 5" or "Joe Jones was born 1950-03-05" is a birthday (a birthday party at a given time stays an ordinary timed event); -birthday forces it. -r, -rrule, -busy, -cal, a duration/day count, -loc and -note are rejected with -birthday.
list hides birthdays unless you pass -b/-birthday. Shown birthdays are tagged [birthday, from contact] (the normal case) or [birthday] for standalone birthday events made elsewhere. del needs -b too, as a guard, and refuses a contact-derived birthday: it prints the contact's link (-open opens it) so you remove the birthday there, and the calendar entry goes with it.
Before 2026-09-22 -birthday created a standalone Calendar birthday event (eventType: birthday) instead; those were unlinked from the person and are no longer made.
Reschedule / snooze notes
resched and snooze find events up to 30 days in the past by default (so stale reminders remain findable). Widen with -since <date>. For timed events, if <when> lacks a time-of-day (e.g. tomorrow), the original time is preserved. All-day events stay all-day. Relative offsets +1d / +1w / +1h / +1m advance from the event's current start.
Windows file association
gcal assoc # Sets up .ics → gcalOn first run, gcal offers to set this up. Run gcal assoc anytime to (re)configure.
gtask — Google Tasks
gtask <command> [options]
gtask help <command>Commands
| Command | Description |
|---------|-------------|
| add <title> [when] | Add a task (optional due date — date-only) |
| list | List open tasks |
| lists | List all tasklists |
| done <id> | Mark task completed |
| undone <id> | Reopen a completed task |
| del <id> | Delete a task |
| edit <id> [-t title] [-when date] [-n notes] | Update fields |
| clear | Remove all completed tasks from list |
| move <id> -l <list> | Move task to another tasklist |
| help [command] | Show help |
Options
| Flag | Description |
|------|-------------|
| -u, -user <email> | Google account |
| -l, -list <name\|id> | Tasklist (default: primary) |
| -n, -notes <text> | Notes for add / edit |
| -t, -title <text> | New title for edit |
| -when <date> | New due date for edit |
| -a, -all | Include completed tasks in list |
Examples
gtask add "Write report"
gtask add "Write report" friday
gtask add "Pay bills" "april 30" -n "rent + utilities"
gtask add "Call plumber" tomorrow -l Errands
gtask list
gtask list -l Errands
gtask list -a
gtask done abc12345
gtask edit abc12345 -when "next monday"
gtask move abc12345 -l Personal
gtask clear -l ErrandsNotes on Google Tasks
- Due dates are date-only. The Tasks API stores RFC3339 timestamps, but the Google UI ignores time-of-day.
gtaskwrites midnight UTC. - No reminders. Tasks have no notification mechanism in the API. Reminders only appear if you create the task through the Calendar UI's task-with-time feature.
- No recurrence. Recurring tasks aren't exposed via the API.
- Hierarchy is flat. One level of subtasks via
move?parent=. Not currently surfaced bygtask.
Shared OAuth
Both tools request the combined scope set calendar + tasks (read or write depending on operation). The first time you run either tool after upgrading, Google prompts once for the new combined consent; afterward both share token.json / token-write.json.
License
MIT
