Skip to content

podarok/gcalpod

gcalpod

Google Calendar CLI in Rust — multi-profile auth, range queries, JSON/TSV/CSV/raw/conky output, ICS import, hybrid grid/agenda rendering.

rust Sponsor License: Apache 2.0

Note

Binary is gcal. Crate / repo name is gcalpod. Derived from rust-dd/google-calendar-cli (Apache 2.0). Substantial modifications listed in NOTICE.md.


Highlights

  • 🔐 Multi-profile OAuth~/.gcal/profiles/<name>/ per account; share one OAuth client across profiles via gcal init --shared.
  • 📅 Range queries with natural-language input — today, +7d, +2w, weekday names, RFC3339, YYYY-MM-DD.
  • 🧾 Six output formatstable / json / tsv / csv / raw / conky. Auto-pretty JSON on tty, compact when piped.
  • 🪟 Hybrid renderer — week grid for short ranges on wide terminals, day-grouped agenda for long ranges or narrow terms.
  • 📥 ICS import with --dry-run and --skip-duplicates.
  • gcal remind — exec a command N minutes before next event with {{summary}} / {{start}} / {{html_link}} interpolation.
  • 🧯 Failure-first errors — RTK-style one-line summary + recovery-metadata path to the full log.
  • 🧭 --verbose for newcomers / init agents.
  • 📜 Man page via gcal --gen-man.

Install

git clone git@github.com:podarok/gcalpod.git
cd gcalpod
cargo build --release && cargo install --path . --locked

The binary lands at ~/.cargo/bin/gcal.

Important

gcal requires your own Google Cloud OAuth client. There is no shared / built-in fallback — every user creates their own OAuth project (~5 minutes, free for personal use).

Quickstart

gcal init                     # interactive wizard
gcal auth status              # confirm authentication
gcal list                     # current week (table)

Or, for multi-account workflows:

gcal init --shared            # one OAuth client at ~/.gcal/secret.json
gcal auth login --profile work
gcal auth login --profile personal
gcal auth switch work
gcal list --from today --to +7d
Configuring your Google Cloud OAuth client manually
  1. Visit https://console.cloud.google.com/projectcreate and create a project.
  2. Enable Calendar API at https://console.cloud.google.com/apis/library/calendar-json.googleapis.com
  3. Set up the OAuth consent screen (External / Testing) — add your email as a test user.
  4. Create OAuth client ID (Application type: Desktop app) and download the JSON.
  5. Move it into place:
    mkdir -p ~/.gcal
    mv ~/Downloads/client_secret_*.json ~/.gcal/secret.json

Detailed walkthrough: docs/custom_auth.md.

Creating events

gcal quick parses natural-language date/time via Google's quick-add endpoint, then optionally patches in fields the NL parser drops on the floor:

gcal quick "team sync May 11 2026 3pm-4pm"

gcal quick "client visit May 11 2026 3pm-4pm" \
  --location "Kyiv, Khreschatyk 1" \
  --description "Bring access badge. Confirm 1h before."

gcal quick "design review tomorrow 2pm-3pm" --conference   # attaches Meet

Flags:

  • -l / --location <text> — set place (post-create patch).
  • -d / --description <text> — set notes (post-create patch).
  • -c / --conference — attach Google Meet (post-create patch).
  • --transparency opaque|transparent — Free/Busy flag (post-create patch). opaque (default) blocks the time; transparent marks the event Free so it does not clash with overlapping slots.
  • --calendar <id> — target a non-primary calendar.

Location, description, and transparency share a single PATCH call when multiple are supplied. For arbitrary field edits on an existing event use gcal edit <id> --field key=value — supported keys: summary, description, location, start, end, transparency.

gcal add <title> <date> accepts --transparency directly at insert time (no patch round-trip). gcal import honours RFC 5545 TRANSP:TRANSPARENT on ICS input.

Commands

CommandPurpose
gcal init [--shared]Interactive setup wizard (per-profile or shared secret).
gcal auth loginOAuth flow for a profile. Flags: --scopes, --no-browser, --reauth.
gcal auth status [--all] [--check]Per-profile state. --check pings live API.
gcal auth switch <profile>Change active profile in ~/.gcal/config.toml.
gcal auth logout [--all] [--purge]Remove cached token (and secret with --purge).
gcal auth refreshForce token refresh.
gcal list [--from --to --calendar --format --style --lineart]Range query. Hybrid grid/agenda by default.
gcal agenda / gcal search <q>Flat list / full-text search.
gcal calendars listList accessible calendars.
gcal add / gcal quick <text>Create events; --conference attaches Google Meet, --location/--description set place/notes.
gcal edit <id> --field key=valueMutate fields: summary, description, location, start, end.
gcal delete <id> [-y]Delete with confirmation gate.
gcal import <path> [--dry-run] [--skip-duplicates]Bulk-insert ICS / VCAL.
gcal remind <mins> -- <cmd>...Exec command N min before next event.
gcal config get/set/unset/list/pathManage ~/.gcal/config.toml.
gcal --gen-manPrint man page (clap_mangen) to stdout.

Add --verbose to any command for extra context — useful for new users and AI init agents.

Output formats

gcal list                                           # table (default)
gcal list --format json | jq '.[] | select(.summary | test("standup"; "i"))'
gcal list --from today --to +30d --format tsv > planning.tsv
gcal list --format csv | column -t -s,
gcal list --format raw | jq '.[] | .recurrence'    # full upstream Event JSON
gcal list --format conky                            # ${color cyan}…${color}

Stable v1 schema for --format json|tsv|csv:

{
  "id": "...",
  "calendar_id": "primary",
  "summary": "...",
  "description": "...",
  "start": "2026-05-04T12:00:00+03:00",
  "end":   "2026-05-04T13:00:00+03:00",
  "all_day": false,
  "status": "confirmed",
  "creator": "...",
  "attendees_count": 2,
  "html_link": "...",
  "updated": "..."
}

Bumping any field name / removing one is a breaking change → major version.

Configuration

gcal resolves OAuth credentials in this order:

  1. GCAL_CLIENT_ID + GCAL_CLIENT_SECRET env vars (optional GCAL_PROJECT_ID).
  2. GCAL_SECRET_FILE=<path> env var.
  3. ~/.gcal/profiles/<active>/secret.json.
  4. ~/.gcal/secret.json (shared / legacy — one client across profiles).

Active-profile resolution: --profile flag → GCAL_PROFILE env → ~/.gcal/config.toml active_profile"default".

gcal config set tz Europe/Kyiv
gcal config set default_format json
gcal config list
gcal config path

GCAL_VERBOSE=1 (or --verbose) prints which source supplied the secret + profile in use.

Failure-first errors

$ gcal auth switch nonexistent
gcal: auth switch failed: Profile 'nonexistent' does not exist. Create
it via `gcal auth login --profile nonexistent` first.; see /Users/.../
Library/Application Support/gcal/tee/1777882749_auth_switch.log

Pass -v / --verbose to print the full error inline (no log path).

Development

cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo test --all                 # 47+ tests
cargo run --release -- --help

Tip

gcal --gen-man > /usr/local/share/man/man1/gcal.1 then man gcal.

Sponsorship

Sponsor button on the repo page activates these channels (configured in .github/FUNDING.yml):

Working method

Starting v1.0.0, all changes to main flow through open issues + pull requests (PR/Issue gate). Direct pushes to main are blocked. See CONTRIBUTING.md for the full workflow:

  1. Open an issue describing intent.
  2. Wait for maintainer ack.
  3. Fork or branch → push commits.
  4. Open a PR; CI must pass (fmt, clippy, test, smoke).
  5. Maintainer review + merge.

The full v1.0.0 work plan and decision history were transferred upstream to ITCare-Company/template_for_agents/process-knowledge-base/gcalpod-queue/ as a worked Anatomy reference.

Acknowledgements

Built on top of rust-dd/google-calendar-cli. Substantial modifications + fork-point commit hash: NOTICE.md.

License

This project is licensed under the PolyForm Noncommercial License 1.0.0 plus the gcalpod Sustainable License Addendum v1 (gSL-v1).

Quick guide:

If you are... What applies What you owe
Hobbyist / student / employee on personal time core license (Noncommercial) nothing
Solo or 2-person micro-business, < $20k revenue, ≤ $20k raised Addendum B self-assess; nothing
Contributor with merged commits Addendum E nothing; commercial use granted
Larger company sponsoring ≥ $5/mo on GH Sponsors / Patreon / BMC Addendum A maintain sponsorship
Larger company without sponsorship core license (Noncommercial only) sponsor, license, or wait 4y (Addendum C)
Distro / registry packager Addendum D preserve LICENSE files

Each tagged release auto-converts to Apache 2.0 four years after its tag date (Addendum C — anti-lock-in).

Upstream-derived portions remain available under Apache License 2.0 per Apache 2.0 §4. See NOTICE.md for full attribution.

About

Google Calendar CLI written in Rust

Resources

License

Unknown and 2 other licenses found

Licenses found

Unknown
LICENSE
Unknown
LICENSE-ADDENDUM.md
Apache-2.0
LICENSE-Apache-2.0

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages