{"id":35166139,"url":"https://github.com/steipete/gogcli","last_synced_at":"2026-04-21T00:03:40.367Z","repository":{"id":328347051,"uuid":"1115161703","full_name":"steipete/gogcli","owner":"steipete","description":"Google Suite CLI: Gmail, GCal, GDrive, GContacts.","archived":false,"fork":false,"pushed_at":"2026-01-17T03:50:47.000Z","size":1057,"stargazers_count":468,"open_issues_count":7,"forks_count":49,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-01-17T04:48:11.358Z","etag":null,"topics":["gcal","gcontacts","gdrive","gmail","google"],"latest_commit_sha":null,"homepage":"https://gogcli.sh","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/steipete.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-12-12T12:36:45.000Z","updated_at":"2026-01-17T03:50:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/steipete/gogcli","commit_stats":null,"previous_names":["steipete/gogcli"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/steipete/gogcli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steipete%2Fgogcli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steipete%2Fgogcli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steipete%2Fgogcli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steipete%2Fgogcli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/steipete","download_url":"https://codeload.github.com/steipete/gogcli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steipete%2Fgogcli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28561633,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T03:31:16.861Z","status":"ssl_error","status_checked_at":"2026-01-19T03:31:15.069Z","response_time":67,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gcal","gcontacts","gdrive","gmail","google"],"created_at":"2025-12-28T19:34:08.769Z","updated_at":"2026-04-21T00:03:40.353Z","avatar_url":"https://github.com/steipete.png","language":"Go","readme":"# 🧭 gogcli — Google in your terminal.\n\n![GitHub Repo Banner](https://ghrb.waren.build/banner?header=gogcli%F0%9F%A7%AD\u0026subheader=Google+in+your+terminal\u0026bg=f3f4f6\u0026color=1f2937\u0026support=true)\n\u003c!-- Created with GitHub Repo Banner by Waren Gonzaga: https://ghrb.waren.build --\u003e\n\nFast, script-friendly CLI for Gmail, Calendar, Chat, Classroom, Drive, Docs, Slides, Sheets, Forms, Apps Script, Contacts, Tasks, People, Admin, Groups (Workspace), and Keep (Workspace-only). JSON-first output, multiple accounts, and flexible auth built in.\n\n## Features\n\n- **Gmail** - search threads/messages, send mail, view attachments, manage labels/drafts/filters/delegation/vacation settings, auto-reply once to matching mail, modify single messages, export filters, inspect history, and run Pub/Sub watch webhooks\n- **Email tracking** - track opens for `gog gmail send --track` with a small Cloudflare Worker backend\n- **Calendar** - list/create/update/delete events, manage invitations, aliases, subscriptions, team calendars, free/busy/conflicts, propose new times, focus/OOO/working-location events, recurrence, and reminders\n- **Classroom** - manage courses, roster, coursework/materials, submissions, announcements, topics, invitations, guardians, profiles\n- **Chat** - list/find/create spaces, list messages/threads, send messages and DMs, and manage emoji reactions (Workspace-only)\n- **Drive** - list/search/upload/download files, replace uploads in-place, convert uploads (including Markdown to Google Doc), manage permissions/comments, organize folders, and list shared drives\n- **Contacts** - search/create/update contacts, including addresses, relations, org/title metadata, custom fields, Workspace directory, and other contacts\n- **Tasks** - manage tasklists and tasks: get/create/add/update/done/undo/delete/clear, plus repeat schedule materialization with RRULE aliases\n- **Sheets** - read/write/update spreadsheets, insert rows/cols, manage tabs and named ranges, format/merge/freeze/resize cells, read/write notes, inspect formats, find/replace text, list links, and create/export sheets\n- **Forms** - create/update forms, manage questions, inspect responses, and manage watches\n- **Apps Script** - create/get/bind projects, inspect content, and run functions\n- **Docs/Slides** - create/copy/export docs/slides, edit Docs by tab, import Markdown, do richer find-replace, export Docs as Markdown/HTML, and generate Slides from Markdown or templates\n- **People** - profile lookup and directory search helpers\n- **Keep (Workspace only)** - list/get/search/create/delete notes and download attachments (service account + domain-wide delegation)\n- **Admin (Workspace only)** - Workspace Admin users/groups commands for common directory operations\n- **Groups** - list groups you belong to, view group members (Google Workspace)\n- **Local time** - quick local/UTC time display for scripts and agents\n- **Multiple accounts** - manage multiple Google accounts simultaneously, with account aliases and per-client OAuth buckets\n- **Command allowlist** - restrict top-level commands for sandboxed/agent runs\n- **Secure credential storage** using OS keyring or encrypted on-disk keyring (configurable)\n- **Auto-refreshing tokens** - authenticate once, use indefinitely\n- **Flexible auth** - OAuth refresh tokens, ADC, direct access tokens, service accounts, manual/remote flows, `--extra-scopes`, and proxy-safe callbacks\n- **Least-privilege auth** - `--readonly`, `--drive-scope`, and `--gmail-scope` to request fewer scopes\n- **Workspace service accounts** - domain-wide delegation auth (preferred when configured)\n- **Parseable output** - JSON mode for scripting and automation (Calendar adds day-of-week fields)\n\n## Installation\n\n### Homebrew\n\n```bash\nbrew install gogcli\n```\n### Arch User Repository\n\n```bash\nyay -S gogcli\n```\n\n### Build from Source\n\n```bash\ngit clone https://github.com/steipete/gogcli.git\ncd gogcli\nmake\n```\n\nRun:\n\n```bash\n./bin/gog --help\n```\n\nHelp:\n\n- `gog --help` shows top-level command groups.\n- Drill down with `gog \u003cgroup\u003e --help` (and deeper subcommands).\n- For the full expanded command list: `GOG_HELP=full gog --help`.\n- Make shortcut: `make gog -- --help` (or `make gog -- gmail --help`).\n- `make gog-help` shows CLI help (note: `make gog --help` is Make’s own help; use `--`).\n- Version: `gog --version` or `gog version`.\n\n## Quick Start\n\n### 1. Get OAuth2 Credentials\n\nBefore adding an account, create OAuth2 credentials from Google Cloud Console:\n\n1. Open the Google Cloud Console credentials page: https://console.cloud.google.com/apis/credentials\n1. Create a project: https://console.cloud.google.com/projectcreate\n2. Enable the APIs you need:\n   - Admin SDK API: https://console.cloud.google.com/apis/api/admin.googleapis.com\n   - Apps Script API: https://console.cloud.google.com/apis/api/script.googleapis.com\n   - Cloud Identity API (Groups): https://console.cloud.google.com/apis/api/cloudidentity.googleapis.com\n   - Gmail API: https://console.cloud.google.com/apis/api/gmail.googleapis.com\n   - Google Calendar API: https://console.cloud.google.com/apis/api/calendar-json.googleapis.com\n   - Google Chat API: https://console.cloud.google.com/apis/api/chat.googleapis.com\n   - Google Docs API: https://console.cloud.google.com/apis/api/docs.googleapis.com\n   - Google Drive API: https://console.cloud.google.com/apis/api/drive.googleapis.com\n   - Google Classroom API: https://console.cloud.google.com/apis/api/classroom.googleapis.com\n   - Google Keep API: https://console.cloud.google.com/apis/api/keep.googleapis.com\n   - People API (Contacts): https://console.cloud.google.com/apis/api/people.googleapis.com\n   - Google Tasks API: https://console.cloud.google.com/apis/api/tasks.googleapis.com\n   - Google Sheets API: https://console.cloud.google.com/apis/api/sheets.googleapis.com\n   - Google Forms API: https://console.cloud.google.com/apis/api/forms.googleapis.com\n   - Google Slides API: https://console.cloud.google.com/apis/api/slides.googleapis.com\n3. Configure OAuth consent screen: https://console.cloud.google.com/auth/branding\n4. If your app is in \"Testing\", add test users: https://console.cloud.google.com/auth/audience\n5. Create OAuth client:\n   - Go to https://console.cloud.google.com/auth/clients\n   - Click \"Create Client\"\n   - Application type: \"Desktop app\"\n   - Download the JSON file (usually named `client_secret_....apps.googleusercontent.com.json`)\n\n### 2. Store Credentials\n\n```bash\ngog auth credentials ~/Downloads/client_secret_....json\n```\n\nFor multiple OAuth clients/projects:\n\n```bash\ngog --client work auth credentials ~/Downloads/work-client.json\ngog auth credentials list\n```\n\n### 3. Authorize Your Account\n\n```bash\ngog auth add you@gmail.com\n```\n\nThis will open a browser window for OAuth authorization. The refresh token is stored securely in your system keychain.\n\nHeadless / remote server flows (no browser on the server):\n\nManual interactive flow (recommended):\n\n```bash\ngog auth add you@gmail.com --services user --manual\n```\n\n- The CLI prints an auth URL. Open it in a local browser.\n- After approval, copy the full loopback redirect URL from the browser address bar.\n- Paste that URL back into the terminal when prompted.\n\nSplit remote flow (`--remote`, useful for two-step/scripted handoff):\n\n```bash\n# Step 1: print auth URL (open it locally in a browser)\ngog auth add you@gmail.com --services user --remote --step 1\n\n# Step 2: paste the full redirect URL from your browser address bar\ngog auth add you@gmail.com --services user --remote --step 2 --auth-url 'http://127.0.0.1:\u003cport\u003e/oauth2/callback?code=...\u0026state=...'\n```\n\n- The `state` is cached on disk for a short time (about 10 minutes). If it expires, rerun step 1.\n- Remote step 2 requires a redirect URL that includes `state` (state check mandatory).\n\nBrowser OAuth behind proxies / remote tunnels:\n\n```bash\ngog auth add you@gmail.com --listen-addr 0.0.0.0:8080 --redirect-host gog.example.com\ngog auth manage --listen-addr 0.0.0.0:8080 --redirect-host gog.example.com\n```\n\n- `--listen-addr` changes where the local callback server binds.\n- `--redirect-host` builds `https://\u003chost\u003e/oauth2/callback` for the OAuth redirect URI.\n- The redirect URI must also be registered in your OAuth client settings.\n\nDirect access token flow (headless/CI, no stored refresh token):\n\n```bash\ngog --access-token \"$(gcloud auth print-access-token)\" gmail labels list\n```\n\n- Also available as `GOG_ACCESS_TOKEN`\n- Bypasses stored refresh tokens and keyring lookup\n- Token expires in about 1 hour; no auto-refresh\n\n### 4. Test Authentication\n\n```bash\nexport GOG_ACCOUNT=you@gmail.com\ngog gmail labels list\n```\n\n## Authentication \u0026 Secrets\n\n### Accounts and tokens\n\n`gog` stores your OAuth refresh tokens in a “keyring” backend. Default is `auto` (best available backend for your OS/environment).\n\nBefore you can run `gog auth add`, you must store OAuth client credentials once via `gog auth credentials \u003ccredentials.json\u003e` (download a Desktop app OAuth client JSON from the Cloud Console). For multiple clients, use `gog --client \u003cname\u003e auth credentials ...`; tokens are isolated per client.\n\nList accounts:\n\n```bash\ngog auth list\n```\n\nVerify tokens are usable (helps spot revoked/expired tokens):\n\n```bash\ngog auth list --check\n```\n\nAccounts can be authorized either via OAuth refresh tokens or Workspace service accounts (domain-wide delegation). If a service account key is configured for an account, it takes precedence over OAuth refresh tokens (see `gog auth list`).\n\nShow current auth state/services for the active account:\n\n```bash\ngog auth status\n```\n\n### Multiple OAuth clients\n\nUse `--client` (or `GOG_CLIENT`) to select a named OAuth client:\n\n```bash\ngog --client work auth credentials ~/Downloads/work.json\ngog --client work auth add you@company.com\n```\n\nOptional domain mapping for auto-selection:\n\n```bash\ngog --client work auth credentials ~/Downloads/work.json --domain example.com\n```\n\nHow it works:\n\n- Default client is `default` (stored in `credentials.json`).\n- Named clients are stored as `credentials-\u003cclient\u003e.json`.\n- Tokens are isolated per client (`token:\u003cclient\u003e:\u003cemail\u003e`); defaults are per client too.\n\nClient selection order (when `--client` is not set):\n\n1) `--client` / `GOG_CLIENT`\n2) `account_clients` config (email -\u003e client)\n3) `client_domains` config (domain -\u003e client)\n4) Credentials file named after the email domain (`credentials-example.com.json`)\n5) `default`\n\nConfig example (JSON5):\n\n```json5\n{\n  account_clients: { \"you@company.com\": \"work\" },\n  client_domains: { \"example.com\": \"work\" },\n}\n```\n\nList stored credentials:\n\n```bash\ngog auth credentials list\n```\n\nSee `docs/auth-clients.md` for the full client selection and mapping rules.\n\n### Keyring backend: Keychain vs encrypted file\n\nBackends:\n\n- `auto` (default): picks the best backend for the platform.\n- `keychain`: macOS Keychain (recommended on macOS; avoids password management).\n- `file`: encrypted on-disk keyring (requires a password).\n\nSet backend via command (writes `keyring_backend` into `config.json`):\n\n```bash\ngog auth keyring file\ngog auth keyring keychain\ngog auth keyring auto\n```\n\nShow current backend + source (env/config/default) and config path:\n\n```bash\ngog auth keyring\n```\n\nNon-interactive runs (CI/ssh): file backend requires `GOG_KEYRING_PASSWORD`.\n\n```bash\nexport GOG_KEYRING_PASSWORD='...'\ngog --no-input auth status\n```\n\nForce backend via env (overrides config):\n\n```bash\nexport GOG_KEYRING_BACKEND=file\n```\n\nPrecedence: `GOG_KEYRING_BACKEND` env var overrides `config.json`.\n\n## Configuration\n\n### Account Selection\n\nSpecify the account using either a flag or environment variable:\n\n```bash\n# Via flag\ngog gmail search 'newer_than:7d' --account you@gmail.com\n\n# Via alias\ngog auth alias set work work@company.com\ngog gmail search 'newer_than:7d' --account work\n\n# Via environment\nexport GOG_ACCOUNT=you@gmail.com\ngog gmail search 'newer_than:7d'\n\n# Auto-select (default account or the single stored token)\ngog gmail labels list --account auto\n```\n\nList configured accounts:\n\n```bash\ngog auth list\n```\n\n### Output\n\n- Default: human-friendly tables on stdout.\n- `--plain`: stable TSV on stdout (tabs preserved; best for piping to tools that expect `\\t`).\n- `--json`: JSON on stdout (best for scripting).\n- Human-facing hints/progress go to stderr.\n- Colors are enabled only in rich TTY output and are disabled automatically for `--json` and `--plain`.\n\n### Service Scopes\n\nBy default, `gog auth add` requests access to the **user** services (see `gog auth services` for the current list and scopes).\n\nTo request fewer scopes:\n\n```bash\ngog auth add you@gmail.com --services drive,calendar\n```\n\nTo request read-only scopes (write operations will fail with 403 insufficient scopes):\n\n```bash\ngog auth add you@gmail.com --services drive,calendar --readonly\n```\n\nTo control Drive’s scope (default: `full`):\n\n```bash\ngog auth add you@gmail.com --services drive --drive-scope full\ngog auth add you@gmail.com --services drive --drive-scope readonly\ngog auth add you@gmail.com --services drive --drive-scope file\n```\n\nTo control Gmail’s scope (default: `full`):\n\n```bash\ngog auth add you@gmail.com --services gmail --gmail-scope full\ngog auth add you@gmail.com --services gmail --gmail-scope readonly\n# Example: readonly on both Gmail and Drive\ngog auth add you@gmail.com --services gmail,drive --gmail-scope readonly --drive-scope readonly\n# Example: append one custom scope beyond the built-in Gmail scope set\ngog auth add you@gmail.com --services gmail --extra-scopes https://www.googleapis.com/auth/gmail.labels\n```\n\nNotes:\n\n- `--drive-scope readonly` is enough for listing/downloading/exporting via Drive (write operations will 403).\n- `--drive-scope file` is write-capable (limited to files created/opened by this app) and can’t be combined with `--readonly`.\n- `--gmail-scope readonly` requests `gmail.readonly` (no modify/settings write scopes).\n- `--extra-scopes` appends additional OAuth scope URIs after the built-in service scope set; remote step-1 guidance replays it so step 2 requests the same token.\n- For `--readonly`, `--drive-scope readonly|file`, or `--gmail-scope readonly`, auth disables Google `include_granted_scopes` to prevent old broader grants from silently accumulating.\n\nIf you need to add services later and Google doesn't return a refresh token, re-run with `--force-consent`:\n\n```bash\ngog auth add you@gmail.com --services user --force-consent\n# Or add just Sheets\ngog auth add you@gmail.com --services sheets --force-consent\n```\n\n`--services all` is accepted as an alias for `user` for backwards compatibility.\n\nDocs commands are implemented via the Drive API, and `docs` requests both Drive and Docs API scopes.\n\nService scope matrix (auto-generated; run `go run scripts/gen-auth-services-md.go`):\n\n\u003c!-- auth-services:start --\u003e\n| Service | User | APIs | Scopes | Notes |\n| --- | --- | --- | --- | --- |\n| gmail | yes | Gmail API | `https://www.googleapis.com/auth/gmail.modify`\u003cbr\u003e`https://www.googleapis.com/auth/gmail.settings.basic`\u003cbr\u003e`https://www.googleapis.com/auth/gmail.settings.sharing` |  |\n| calendar | yes | Calendar API | `https://www.googleapis.com/auth/calendar` |  |\n| chat | yes | Chat API | `https://www.googleapis.com/auth/chat.spaces`\u003cbr\u003e`https://www.googleapis.com/auth/chat.messages`\u003cbr\u003e`https://www.googleapis.com/auth/chat.memberships`\u003cbr\u003e`https://www.googleapis.com/auth/chat.users.readstate.readonly` |  |\n| classroom | yes | Classroom API | `https://www.googleapis.com/auth/classroom.courses`\u003cbr\u003e`https://www.googleapis.com/auth/classroom.rosters`\u003cbr\u003e`https://www.googleapis.com/auth/classroom.coursework.students`\u003cbr\u003e`https://www.googleapis.com/auth/classroom.coursework.me`\u003cbr\u003e`https://www.googleapis.com/auth/classroom.courseworkmaterials`\u003cbr\u003e`https://www.googleapis.com/auth/classroom.announcements`\u003cbr\u003e`https://www.googleapis.com/auth/classroom.topics`\u003cbr\u003e`https://www.googleapis.com/auth/classroom.guardianlinks.students`\u003cbr\u003e`https://www.googleapis.com/auth/classroom.profile.emails`\u003cbr\u003e`https://www.googleapis.com/auth/classroom.profile.photos` |  |\n| drive | yes | Drive API | `https://www.googleapis.com/auth/drive` |  |\n| docs | yes | Docs API, Drive API | `https://www.googleapis.com/auth/drive`\u003cbr\u003e`https://www.googleapis.com/auth/documents` | Export/copy/create via Drive |\n| slides | yes | Slides API, Drive API | `https://www.googleapis.com/auth/drive`\u003cbr\u003e`https://www.googleapis.com/auth/presentations` | Create/edit presentations |\n| contacts | yes | People API | `https://www.googleapis.com/auth/contacts`\u003cbr\u003e`https://www.googleapis.com/auth/contacts.other.readonly`\u003cbr\u003e`https://www.googleapis.com/auth/directory.readonly` | Contacts + other contacts + directory |\n| tasks | yes | Tasks API | `https://www.googleapis.com/auth/tasks` |  |\n| sheets | yes | Sheets API, Drive API | `https://www.googleapis.com/auth/drive`\u003cbr\u003e`https://www.googleapis.com/auth/spreadsheets` | Export via Drive |\n| people | yes | People API | `profile` | OIDC profile scope |\n| forms | yes | Forms API | `https://www.googleapis.com/auth/forms.body`\u003cbr\u003e`https://www.googleapis.com/auth/forms.responses.readonly` |  |\n| appscript | yes | Apps Script API | `https://www.googleapis.com/auth/script.projects`\u003cbr\u003e`https://www.googleapis.com/auth/script.deployments`\u003cbr\u003e`https://www.googleapis.com/auth/script.processes` |  |\n| ads | yes | Google Ads API | `https://www.googleapis.com/auth/adwords` | OAuth scope only |\n| groups | no | Cloud Identity API | `https://www.googleapis.com/auth/cloud-identity.groups.readonly` | Workspace only |\n| keep | no | Keep API | `https://www.googleapis.com/auth/keep` | Workspace only; service account (domain-wide delegation) |\n| admin | no | Admin SDK Directory API | `https://www.googleapis.com/auth/admin.directory.user`\u003cbr\u003e`https://www.googleapis.com/auth/admin.directory.group`\u003cbr\u003e`https://www.googleapis.com/auth/admin.directory.group.member` | Workspace only; service account with domain-wide delegation required |\n\u003c!-- auth-services:end --\u003e\n\n### Service Accounts (Workspace only)\n\nA service account is a non-human Google identity that belongs to a Google Cloud project. In Google Workspace, a service account can impersonate a user via **domain-wide delegation** (admin-controlled) and access APIs like Gmail/Calendar/Drive as that user.\n\nIn `gog`, service accounts are an **optional auth method** that can be configured per account email. If a service account key is configured for an account, it takes precedence over OAuth refresh tokens (see `gog auth list`).\n\n#### 1) Create a Service Account (Google Cloud)\n\n1. Create (or pick) a Google Cloud project.\n2. Enable the APIs you’ll use (e.g. Gmail, Calendar, Drive, Sheets, Docs, People, Tasks, Cloud Identity).\n3. Go to **IAM \u0026 Admin → Service Accounts** and create a service account.\n4. In the service account details, enable **Domain-wide delegation**.\n5. Create a key (**Keys → Add key → Create new key → JSON**) and download the JSON key file.\n\n#### 2) Allowlist scopes (Google Workspace Admin Console)\n\nDomain-wide delegation is enforced by Workspace admin settings.\n\n1. Open **Admin console → Security → API controls → Domain-wide delegation**.\n2. Add a new API client:\n   - Client ID: use the service account’s “Client ID” from Google Cloud.\n   - OAuth scopes: comma-separated list of scopes you want to allow (copy from `gog auth services` and/or your `gog auth add --services ...` usage).\n\nIf a scope is missing from the allowlist, service-account token minting can fail (or API calls will 403 with insufficient permissions).\n\n#### 3) Configure `gog` to use the service account\n\nStore the key for the user you want to impersonate:\n\n```bash\ngog auth service-account set you@yourdomain.com --key ~/Downloads/service-account.json\n```\n\nVerify `gog` is preferring the service account for that account:\n\n```bash\ngog --account you@yourdomain.com auth status\ngog auth list\n```\n\n### Google Keep (Workspace only)\n\nKeep requires Workspace + domain-wide delegation. You can configure it via the generic service-account command above (recommended), or the legacy Keep helper:\n\n```bash\ngog auth service-account set you@yourdomain.com --key ~/Downloads/service-account.json\ngog keep list --account you@yourdomain.com\ngog keep get \u003cnoteId\u003e --account you@yourdomain.com\ngog keep create --title \"Todo\" --item \"Milk\" --item \"Eggs\" --account you@yourdomain.com\ngog keep delete \u003cnoteId\u003e --account you@yourdomain.com --force\n```\n\n### Environment Variables\n\n- `GOG_ACCOUNT` - Default account email or alias to use (avoids repeating `--account`; otherwise uses keyring default or a single stored token)\n- `GOG_ACCESS_TOKEN` - Use a provided access token directly (headless/CI; no auto-refresh)\n- `GOG_CLIENT` - OAuth client name (selects stored credentials + token bucket)\n- `GOG_JSON` - Default JSON output\n- `GOG_PLAIN` - Default plain output\n- `GOG_COLOR` - Color mode: `auto` (default), `always`, or `never`\n- `GOG_TIMEZONE` - Default output timezone for Calendar/Gmail (IANA name, `UTC`, or `local`)\n- `GOG_ENABLE_COMMANDS` - Comma-separated allowlist of commands; dot paths allowed (e.g., `calendar,tasks,gmail.search`)\n- `GOG_DISABLE_COMMANDS` - Comma-separated denylist of commands; dot paths allowed (e.g., `gmail.send,gmail.drafts.send`)\n- `GOG_GMAIL_NO_SEND` - Block Gmail send operations\n- `GOG_KEYRING_SERVICE_NAME` - Override the keyring namespace/service name (default: `gogcli`)\n\n### Config File (JSON5)\n\nFind the actual config path in `gog --help` or `gog auth keyring`.\n\nTypical paths:\n\n- macOS: `~/Library/Application Support/gogcli/config.json`\n- Linux: `~/.config/gogcli/config.json` (or `$XDG_CONFIG_HOME/gogcli/config.json`)\n- Windows: `%AppData%\\\\gogcli\\\\config.json`\n\nExample (JSON5 supports comments and trailing commas):\n\n```json5\n{\n  // Avoid macOS Keychain prompts\n  keyring_backend: \"file\",\n  // Default output timezone for Calendar/Gmail (IANA, UTC, or local)\n  default_timezone: \"UTC\",\n  // Optional account aliases\n  account_aliases: {\n    work: \"work@company.com\",\n    personal: \"me@gmail.com\",\n  },\n  // Optional per-account OAuth client selection\n  account_clients: {\n    \"work@company.com\": \"work\",\n  },\n  // Optional domain -\u003e client mapping\n  client_domains: {\n    \"example.com\": \"work\",\n  },\n  // Optional safety guard: block Gmail send operations\n  gmail_no_send: true,\n  no_send_accounts: {\n    \"agent@example.com\": true,\n  },\n}\n```\n\n### Config Commands\n\n```bash\ngog config path\ngog config list\ngog config keys\ngog config get timezone\ngog config set timezone UTC\ngog config unset timezone\n```\n\n### Account Aliases\n\n```bash\ngog auth alias set work work@company.com\ngog auth alias list\ngog auth alias unset work\n```\n\nAliases work anywhere you pass `--account` or `GOG_ACCOUNT` (reserved: `auto`, `default`).\n\n### Command Guards (Sandboxing)\n\n```bash\n# Only allow calendar + tasks commands for an agent\ngog --enable-commands calendar,tasks calendar events --today\n\n# Allow one Gmail read path, but block Gmail writes\ngog --enable-commands gmail.search --disable-commands gmail.send gmail search from:me\n\n# Same via env\nexport GOG_ENABLE_COMMANDS=calendar,tasks\nexport GOG_DISABLE_COMMANDS=gmail.send,gmail.drafts.send\ngog tasks list \u003ctasklistId\u003e\n\n# Extra Gmail send guard\ngog --gmail-no-send gmail send --to someone@example.com --subject Test --body Test\ngog config no-send set agent@example.com\n```\n \n## Security\n\n### Credential Storage\n\nOAuth credentials are stored securely in your system's keychain:\n- **macOS**: Keychain Access\n- **Linux**: Secret Service (GNOME Keyring, KWallet)\n- **Windows**: Credential Manager\n\nThe CLI uses [github.com/99designs/keyring](https://github.com/99designs/keyring) for secure storage.\n\nIf no OS keychain backend is available (e.g., Linux/WSL/container), keyring can fall back to an encrypted on-disk store and may prompt for a password; for non-interactive runs set `GOG_KEYRING_PASSWORD`.\n\n### Keychain Prompts (macOS)\n\nmacOS Keychain may prompt more than you’d expect when the “app identity” keeps changing (different binary path, `go run` temp builds, rebuilding to new `./bin/gog`, multiple copies). Keychain treats those as different apps, so it asks again.\n\nOptions:\n\n- **Default (recommended):** keep using Keychain (secure) and run a stable `gog` binary path to reduce repeat prompts.\n- **Force Keychain:** `GOG_KEYRING_BACKEND=keychain` (disables any file-backend fallback).\n- **Avoid Keychain prompts entirely:** `GOG_KEYRING_BACKEND=file` (stores encrypted entries on disk under your config dir).\n  - To avoid password prompts too (CI/non-interactive): set `GOG_KEYRING_PASSWORD=...` (tradeoff: secret in env).\n- **Use a separate keyring namespace:** `GOG_KEYRING_SERVICE_NAME=custom-gog` (default: `gogcli`).\n\n### Best Practices\n\n- **Never commit OAuth client credentials** to version control\n- Store client credentials outside your project directory\n- Use different OAuth clients for development and production\n- Re-authorize with `--force-consent` if you suspect token compromise\n- Remove unused accounts with `gog auth remove \u003cemail\u003e`\n\n### OAuth Client IDs in Open Source\n\nSome open source Google CLIs ship a pre-configured OAuth client ID/secret copied from other desktop apps to avoid OAuth consent verification, testing-user limits, or quota issues. This makes the consent screen/security emails show the other app’s name and can stop working at any time.\n\n`gogcli` does not do this. Supported auth:\n\n- Your own OAuth Desktop client JSON via `gog auth credentials ...` + `gog auth add ...`\n- Google Workspace service accounts with domain-wide delegation (Workspace only)\n\n## Commands\n\nFlag aliases:\n- `--out` also accepts `--output`.\n- `--out-dir` also accepts `--output-dir` (Gmail thread attachment downloads).\n\n### Authentication\n\n```bash\ngog auth credentials \u003cpath\u003e           # Store OAuth client credentials\ngog auth credentials list             # List stored OAuth client credentials\ngog auth credentials remove work      # Remove one OAuth client plus its tokens/domain mappings\ngog auth credentials remove all       # Remove all stored OAuth clients plus their tokens/domain mappings\ngog --client work auth credentials \u003cpath\u003e  # Store named OAuth client credentials\ngog auth add \u003cemail\u003e                  # Authorize and store refresh token\ngog auth add \u003cemail\u003e --services gmail --gmail-scope readonly  # Gmail read-only token\ngog auth add \u003cemail\u003e --listen-addr 0.0.0.0:8080 --redirect-host gog.example.com\ngog auth service-account set \u003cemail\u003e --key \u003cpath\u003e  # Configure service account impersonation (Workspace only)\ngog auth service-account status \u003cemail\u003e            # Show service account status\ngog auth service-account unset \u003cemail\u003e             # Remove service account\ngog auth keep \u003cemail\u003e --key \u003cpath\u003e                 # Legacy alias (Keep)\ngog auth keyring [backend]            # Show/set keyring backend (auto|keychain|file)\ngog auth status                       # Show current auth state/services\ngog auth services                     # List available services and OAuth scopes\ngog auth list                         # List stored accounts\ngog auth list --check                 # Validate stored refresh tokens\ngog auth remove \u003cemail\u003e               # Remove a stored refresh token\ngog auth manage                       # Open accounts manager in browser\ngog auth manage --listen-addr 0.0.0.0:8080 --redirect-host gog.example.com\ngog auth tokens                       # Manage stored refresh tokens\n```\n\n### Keep (Workspace only)\n\n```bash\ngog keep list --account you@yourdomain.com\ngog keep get \u003cnoteId\u003e --account you@yourdomain.com\ngog keep search \u003cquery\u003e --account you@yourdomain.com\ngog keep create --title \"Todo\" --item \"Milk\" --item \"Eggs\" --account you@yourdomain.com\ngog keep create --title \"Note\" --text \"Remember this\" --account you@yourdomain.com\ngog keep delete \u003cnoteId\u003e --account you@yourdomain.com --force\ngog keep attachment \u003cattachmentName\u003e --account you@yourdomain.com --out ./attachment.bin\n```\n\n### Gmail\n\n```bash\n# Search and read\ngog gmail search 'newer_than:7d' --max 10\ngog gmail thread get \u003cthreadId\u003e\ngog gmail thread get \u003cthreadId\u003e --download              # Download attachments to current dir\ngog gmail thread get \u003cthreadId\u003e --download --out-dir ./attachments\ngog gmail get \u003cmessageId\u003e\ngog gmail get \u003cmessageId\u003e --format metadata\ngog gmail attachment \u003cmessageId\u003e \u003cattachmentId\u003e\ngog gmail attachment \u003cmessageId\u003e \u003cattachmentId\u003e --out ./attachment.bin\ngog gmail url \u003cthreadId\u003e              # Print Gmail web URL\ngog gmail thread modify \u003cthreadId\u003e --add STARRED --remove INBOX\n\n# Send and compose\ngog gmail send --to a@b.com --subject \"Hi\" --body \"Plain fallback\"\ngog gmail send --to a@b.com --subject \"Hi\" --body-file ./message.txt\ngog gmail send --to a@b.com --subject \"Hi\" --body-file -   # Read body from stdin\ngog gmail send --to a@b.com --subject \"Hi\" --body \"Plain fallback\" --body-html \"\u003cp\u003eHello\u003c/p\u003e\"\ngog gmail forward \u003cmessageId\u003e --to a@b.com --note \"FYI\"\ngog gmail forward \u003cmessageId\u003e --to a@b.com --skip-attachments\n# Reply + include quoted original message (auto-generates HTML quote unless you pass --body-html)\ngog gmail send --reply-to-message-id \u003cmessageId\u003e --quote --to a@b.com --subject \"Re: Hi\" --body \"My reply\"\n# Draft reply + quote (create requires explicit reply target)\ngog gmail drafts create --reply-to-message-id \u003cmessageId\u003e --quote --subject \"Re: Hi\" --body \"My reply\"\n# Draft reply + quote (update accepts explicit target; else falls back to latest non-draft, non-self message in thread)\ngog gmail drafts update \u003cdraftId\u003e --reply-to-message-id \u003cmessageId\u003e --quote --subject \"Re: Hi\" --body \"My reply\"\ngog gmail drafts update \u003cdraftId\u003e --quote --subject \"Re: Hi\" --body \"My reply\"\ngog gmail drafts list\ngog gmail drafts create --subject \"Draft\" --body \"Body\"\ngog gmail drafts create --to a@b.com --subject \"Draft\" --body \"Body\"\ngog gmail drafts update \u003cdraftId\u003e --subject \"Draft\" --body \"Body\"\ngog gmail drafts update \u003cdraftId\u003e --to a@b.com --subject \"Draft\" --body \"Body\"\ngog gmail drafts send \u003cdraftId\u003e\ngog gmail autoreply 'from:alerts@example.com newer_than:7d' --body-file ./reply.txt --label AutoReplied --dry-run\n\n# Labels\ngog gmail labels list\ngog gmail labels get INBOX --json  # Includes message counts\ngog gmail labels create \"My Label\"\ngog gmail labels rename \"Old Label\" \"New Label\"\ngog gmail labels style \"My Label\" --text-color \"#ffffff\" --background-color \"#4285f4\"\ngog gmail labels modify \u003cthreadId\u003e --add STARRED --remove INBOX\ngog gmail labels delete \u003clabelIdOrName\u003e  # Deletes user label (guards system labels; confirm)\n\n# Batch operations\ngog gmail batch delete \u003cmessageId\u003e \u003cmessageId\u003e\ngog gmail batch modify \u003cmessageId\u003e \u003cmessageId\u003e --add STARRED --remove INBOX\n\n# Filters\ngog gmail filters list\ngog gmail filters create --from 'noreply@example.com' --add-label 'Notifications'\ngog gmail filters delete \u003cfilterId\u003e\ngog gmail filters export --out ./filters.json\n\n# Settings\ngog gmail autoforward get\ngog gmail autoforward enable --email forward@example.com\ngog gmail autoforward disable\ngog gmail forwarding list\ngog gmail forwarding add --email forward@example.com\ngog gmail sendas list\ngog gmail sendas create --email alias@example.com\ngog gmail vacation get\ngog gmail vacation enable --subject \"Out of office\" --message \"...\"\ngog gmail vacation disable\n\n# Delegation (G Suite/Workspace)\ngog gmail delegates list\ngog gmail delegates add --email delegate@example.com\ngog gmail delegates remove --email delegate@example.com\n\n# Watch (Pub/Sub push)\ngog gmail watch start --topic projects/\u003cp\u003e/topics/\u003ct\u003e --label INBOX\ngog gmail watch serve --bind 127.0.0.1 --token \u003cshared\u003e --hook-url http://127.0.0.1:18789/hooks/agent\ngog gmail watch serve --bind 0.0.0.0 --verify-oidc --oidc-email \u003csvc@...\u003e --hook-url \u003curl\u003e\ngog gmail watch serve --bind 127.0.0.1 --token \u003cshared\u003e --fetch-delay 5 --hook-url http://127.0.0.1:18789/hooks/agent\ngog gmail watch serve --bind 127.0.0.1 --token \u003cshared\u003e --exclude-labels SPAM,TRASH --hook-url http://127.0.0.1:18789/hooks/agent\ngog gmail history --since \u003chistoryId\u003e\n```\n\nGmail watch (Pub/Sub push):\n- Create Pub/Sub topic + push subscription (OIDC preferred; shared token ok for dev).\n- Full flow + payload details: `docs/watch.md`.\n- `watch serve --fetch-delay` defaults to `3s` and helps avoid Gmail History indexing races after push delivery.\n- `watch serve --exclude-labels` defaults to `SPAM,TRASH`; IDs are case-sensitive.\n\n### Email Tracking\n\nTrack when recipients open your emails:\n\n```bash\n# Set up local tracking config (per-account; generates keys; follow printed deploy steps)\ngog gmail track setup --worker-url https://gog-email-tracker.\u003cacct\u003e.workers.dev\n\n# Send with tracking\ngog gmail send --to recipient@example.com --subject \"Hello\" --body-html \"\u003cp\u003eHi!\u003c/p\u003e\" --track\n\n# Check opens\ngog gmail track opens \u003ctracking_id\u003e\ngog gmail track opens --to recipient@example.com\n\n# View status\ngog gmail track status\n```\n\nDocs: `docs/email-tracking.md` (setup/deploy) + `docs/email-tracking-worker.md` (internals).\n\n**Notes:** `--track` requires exactly 1 recipient (no cc/bcc) and an HTML body (`--body-html` or `--quote`). Use `--track-split` to send per-recipient messages with individual tracking ids. The tracking worker stores IP/user-agent + coarse geo by default.\n\n### Calendar\n\n```bash\n# Calendars\ngog calendar calendars\ngog calendar create-calendar \"Team Calendar\" --timezone Europe/London\ngog calendar acl \u003ccalendarId\u003e         # List access control rules\ngog calendar colors                   # List available event/calendar colors\ngog calendar time --timezone America/New_York\ngog calendar users                    # List workspace users (use email as calendar ID)\n\n# Events (with timezone-aware time flags)\ngog calendar events \u003ccalendarId\u003e --today                    # Today's events\ngog calendar events \u003ccalendarId\u003e --tomorrow                 # Tomorrow's events\ngog calendar events \u003ccalendarId\u003e --week                     # This week (Mon-Sun by default; use --week-start)\ngog calendar events \u003ccalendarId\u003e --days 3                   # Next 3 days\ngog calendar events \u003ccalendarId\u003e --from today --to friday   # Relative dates\ngog calendar events \u003ccalendarId\u003e --from today --to friday --weekday   # Include weekday columns\ngog calendar events \u003ccalendarId\u003e --from 2025-01-01T00:00:00Z --to 2025-01-08T00:00:00Z\ngog calendar events --all             # Fetch events from all calendars\ngog calendar events --calendars 1,3   # Fetch events from calendar indices (see gog calendar calendars)\ngog calendar events --cal Work --cal Personal  # Fetch events from calendars by name/ID\ngog calendar event \u003ccalendarId\u003e \u003ceventId\u003e\ngog calendar get \u003ccalendarId\u003e \u003ceventId\u003e                     # Alias for event\ngog calendar search \"meeting\" --today\ngog calendar search \"meeting\" --tomorrow\ngog calendar search \"meeting\" --days 365\ngog calendar search \"meeting\" --from 2025-01-01T00:00:00Z --to 2025-01-31T00:00:00Z --max 50\n\n# Search defaults to 30 days ago through 90 days ahead unless you set --from/--to/--today/--week/--days.\n# Tip: set GOG_CALENDAR_WEEKDAY=1 to default --weekday for calendar events output.\n\n# JSON event output includes timezone and localized times (useful for agents).\ngog calendar get \u003ccalendarId\u003e \u003ceventId\u003e --json\n# {\n#   \"event\": {\n#     \"id\": \"...\",\n#     \"summary\": \"...\",\n#     \"startDayOfWeek\": \"Friday\",\n#     \"endDayOfWeek\": \"Friday\",\n#     \"timezone\": \"America/Los_Angeles\",\n#     \"eventTimezone\": \"America/New_York\",\n#     \"startLocal\": \"2026-01-23T20:45:00-08:00\",\n#     \"endLocal\": \"2026-01-23T22:45:00-08:00\",\n#     \"start\": { \"dateTime\": \"2026-01-23T23:45:00-05:00\" },\n#     \"end\": { \"dateTime\": \"2026-01-24T01:45:00-05:00\" }\n#   }\n# }\n\n# Team calendars (requires Cloud Identity API for Google Workspace)\ngog calendar team \u003cgroup-email\u003e --today           # Show team's events for today\ngog calendar team \u003cgroup-email\u003e --week            # Show team's events for the week (use --week-start)\ngog calendar team \u003cgroup-email\u003e --freebusy        # Show only busy/free blocks (faster)\ngog calendar team \u003cgroup-email\u003e --query \"standup\" # Filter by event title\n\n# Create and update\ngog calendar create \u003ccalendarId\u003e \\\n  --summary \"Meeting\" \\\n  --from 2025-01-15T10:00:00Z \\\n  --to 2025-01-15T11:00:00Z\n\ngog calendar create \u003ccalendarId\u003e \\\n  --summary \"Team Sync\" \\\n  --from 2025-01-15T14:00:00Z \\\n  --to 2025-01-15T15:00:00Z \\\n  --attendees \"alice@example.com,bob@example.com\" \\\n  --location \"Zoom\"\n\ngog calendar update \u003ccalendarId\u003e \u003ceventId\u003e \\\n  --summary \"Updated Meeting\" \\\n  --from 2025-01-15T11:00:00Z \\\n  --to 2025-01-15T12:00:00Z\n\n# Send notifications when creating/updating\ngog calendar create \u003ccalendarId\u003e \\\n  --summary \"Team Sync\" \\\n  --from 2025-01-15T14:00:00Z \\\n  --to 2025-01-15T15:00:00Z \\\n  --send-updates all\n\ngog calendar update \u003ccalendarId\u003e \u003ceventId\u003e \\\n  --send-updates externalOnly\n\n# Default: no attendee notifications unless you pass --send-updates.\ngog calendar delete \u003ccalendarId\u003e \u003ceventId\u003e \\\n  --send-updates all --force\n\n# Recurrence + reminders\ngog calendar create \u003ccalendarId\u003e \\\n  --summary \"Payment\" \\\n  --from 2025-02-11T09:00:00-03:00 \\\n  --to 2025-02-11T09:15:00-03:00 \\\n  --rrule \"RRULE:FREQ=MONTHLY;BYMONTHDAY=11\" \\\n  --reminder \"email:3d\" \\\n  --reminder \"popup:30m\"\n\n# Special event types via --event-type (focus-time/out-of-office/working-location)\ngog calendar create primary \\\n  --event-type focus-time \\\n  --from 2025-01-15T13:00:00Z \\\n  --to 2025-01-15T14:00:00Z\n\ngog calendar create primary \\\n  --event-type out-of-office \\\n  --from 2025-01-20 \\\n  --to 2025-01-21 \\\n  --all-day\n\ngog calendar create primary \\\n  --event-type working-location \\\n  --working-location-type office \\\n  --working-office-label \"HQ\" \\\n  --from 2025-01-22 \\\n  --to 2025-01-23\n\n# Dedicated shortcuts (same event types, more opinionated defaults)\ngog calendar focus-time --from 2025-01-15T13:00:00Z --to 2025-01-15T14:00:00Z\ngog calendar out-of-office --from 2025-01-20 --to 2025-01-21 --all-day\ngog calendar working-location --type office --office-label \"HQ\" --from 2025-01-22 --to 2025-01-23\n# Add attendees without replacing existing attendees/RSVP state\ngog calendar update \u003ccalendarId\u003e \u003ceventId\u003e \\\n  --add-attendee \"alice@example.com,bob@example.com\"\n\ngog calendar delete \u003ccalendarId\u003e \u003ceventId\u003e\n\n# Invitations\ngog calendar respond \u003ccalendarId\u003e \u003ceventId\u003e --status accepted\ngog calendar respond \u003ccalendarId\u003e \u003ceventId\u003e --status declined\ngog calendar respond \u003ccalendarId\u003e \u003ceventId\u003e --status tentative\ngog calendar respond \u003ccalendarId\u003e \u003ceventId\u003e --status declined --send-updates externalOnly\n\n# Propose a new time (browser-only flow; API limitation)\ngog calendar propose-time \u003ccalendarId\u003e \u003ceventId\u003e\ngog calendar propose-time \u003ccalendarId\u003e \u003ceventId\u003e --open\ngog calendar propose-time \u003ccalendarId\u003e \u003ceventId\u003e --decline --comment \"Can we do 5pm?\"\n\n# Availability\ngog calendar freebusy --calendars \"primary,work@example.com\" \\\n  --from 2025-01-15T00:00:00Z \\\n  --to 2025-01-16T00:00:00Z\ngog calendar freebusy --cal Work --from 2025-01-15T00:00:00Z --to 2025-01-16T00:00:00Z\n\ngog calendar conflicts --calendars \"primary,work@example.com\" \\\n  --today                             # Today's conflicts\ngog calendar conflicts --all --today # Check conflicts across all calendars\n```\n\n### Time\n\n```bash\ngog time now\ngog time now --timezone UTC\n```\n\n### Drive\n\nWhen you turn a Markdown file into a Google Doc, use **`--convert`** (extension-based) or **`--convert-to doc`**. Leading YAML frontmatter between **`---`** lines is **removed before upload** unless you pass **`--keep-frontmatter`**. That step only looks for opening and closing delimiter lines—it is **not** a full YAML parse, so odd edge cases may need **`--keep-frontmatter`** or editing the file first.\n\n```bash\n# List and search\ngog drive ls --max 20\ngog drive ls --parent \u003cfolderId\u003e --max 20\ngog drive ls --all --max 20               # List across all accessible files (cannot combine with --parent)\ngog drive ls --no-all-drives            # Only list from \"My Drive\"\ngog drive search \"invoice\" --max 20\ngog drive search \"invoice\" --no-all-drives\ngog drive search \"mimeType = 'application/pdf'\" --raw-query\ngog drive get \u003cfileId\u003e                # Get file metadata\ngog drive url \u003cfileId\u003e                # Print Drive web URL\ngog drive copy \u003cfileId\u003e \"Copy Name\"\n\n# Upload and download\ngog drive upload ./path/to/file --parent \u003cfolderId\u003e\ngog drive upload ./path/to/file --replace \u003cfileId\u003e  # Replace file content in-place (preserves shared link)\ngog drive upload ./report.docx --convert\ngog drive upload ./chart.png --convert-to sheet\ngog drive upload ./report.docx --convert --name report.docx\ngog drive upload ./notes.md --convert                              # Markdown → Google Doc (or use --convert-to doc)\ngog drive download \u003cfileId\u003e --out ./downloaded.bin\ngog drive download \u003cfileId\u003e --format pdf --out ./exported.pdf     # Google Workspace files only\ngog drive download \u003cfileId\u003e --format docx --out ./doc.docx\ngog drive download \u003cfileId\u003e --format md --out ./note.md            # Google Doc → Markdown\ngog drive download \u003cfileId\u003e --format pptx --out ./slides.pptx\n\n# Organize\ngog drive mkdir \"New Folder\"\ngog drive mkdir \"New Folder\" --parent \u003cparentFolderId\u003e\ngog drive rename \u003cfileId\u003e \"New Name\"\ngog drive move \u003cfileId\u003e --parent \u003cdestinationFolderId\u003e\ngog drive delete \u003cfileId\u003e             # Move to trash\ngog drive delete \u003cfileId\u003e --permanent # Permanently delete\n\n# Permissions\ngog drive permissions \u003cfileId\u003e\ngog drive share \u003cfileId\u003e --to user --email user@example.com --role reader\ngog drive share \u003cfileId\u003e --to user --email user@example.com --role writer\ngog drive share \u003cfileId\u003e --to user --email reviewer@example.com --role commenter\ngog drive share \u003cfileId\u003e --to domain --domain example.com --role reader\ngog drive unshare \u003cfileId\u003e --permission-id \u003cpermissionId\u003e\n\n# Shared drives (Team Drives)\ngog drive drives --max 100\n```\n\n### Docs / Slides / Sheets\n\n```bash\n# Docs\ngog docs info \u003cdocId\u003e\ngog docs cat \u003cdocId\u003e --max-bytes 10000\ngog docs create \"My Doc\"\ngog docs create \"My Doc\" --file ./doc.md            # Import markdown\ngog docs create \"My Doc\" --pageless\ngog docs copy \u003cdocId\u003e \"My Doc Copy\"\ngog docs export \u003cdocId\u003e --format pdf --out ./doc.pdf\ngog docs list-tabs \u003cdocId\u003e\ngog docs cat \u003cdocId\u003e --tab \"Notes\"\ngog docs cat \u003cdocId\u003e --all-tabs\ngog docs update \u003cdocId\u003e --text \"Append this later\"\ngog docs update \u003cdocId\u003e --text \"Only in this tab\" --tab-id t.notes\ngog docs update \u003cdocId\u003e --file ./insert.txt --index 25 --pageless\ngog docs write \u003cdocId\u003e --text \"Fresh content\"\ngog docs write \u003cdocId\u003e --text \"Rewrite one tab\" --tab-id t.notes\ngog docs write \u003cdocId\u003e --file ./body.txt --append --pageless\ngog docs write \u003cdocId\u003e --file ./body.md --replace --markdown\ngog docs find-replace \u003cdocId\u003e \"old\" \"new\"\ngog docs find-replace \u003cdocId\u003e \"old\" \"new\" --tab-id t.notes\n\n# Slides\ngog slides info \u003cpresentationId\u003e\ngog slides create \"My Deck\"\ngog slides create-from-markdown \"My Deck\" --content-file ./slides.md\ngog slides create-from-template \u003ctemplateId\u003e \"My Deck\" --replace \"name=John\" --replace \"date=2026-02-15\"\ngog slides copy \u003cpresentationId\u003e \"My Deck Copy\"\ngog slides export \u003cpresentationId\u003e --format pdf --out ./deck.pdf\ngog slides list-slides \u003cpresentationId\u003e\ngog slides add-slide \u003cpresentationId\u003e ./slide.png --notes \"Speaker notes\"\ngog slides update-notes \u003cpresentationId\u003e \u003cslideId\u003e --notes \"Updated notes\"\ngog slides replace-slide \u003cpresentationId\u003e \u003cslideId\u003e ./new-slide.png --notes \"New notes\"\n\n# Sheets\ngog sheets copy \u003cspreadsheetId\u003e \"My Sheet Copy\"\ngog sheets export \u003cspreadsheetId\u003e --format pdf --out ./sheet.pdf\ngog sheets format \u003cspreadsheetId\u003e 'Sheet1!A1:B2' --format-json '{\"textFormat\":{\"bold\":true}}' --format-fields 'userEnteredFormat.textFormat.bold'\ngog sheets format \u003cspreadsheetId\u003e 'Sheet1!A1:B2' --format-json '{\"borders\":{\"top\":{\"style\":\"SOLID\"}}}' --format-fields 'userEnteredFormat.borders.top.style'\ngog sheets merge \u003cspreadsheetId\u003e 'Sheet1!A1:B2'\ngog sheets number-format \u003cspreadsheetId\u003e 'Sheet1!C:C' --type CURRENCY --pattern '$#,##0.00'\ngog sheets freeze \u003cspreadsheetId\u003e --rows 1 --cols 1\ngog sheets resize-columns \u003cspreadsheetId\u003e 'Sheet1!A:C' --auto\ngog sheets read-format \u003cspreadsheetId\u003e 'Sheet1!A1:B2'\ngog sheets insert \u003cspreadsheetId\u003e \"Sheet1\" rows 2 --count 3\ngog sheets notes \u003cspreadsheetId\u003e 'Sheet1!A1:B10'\ngog sheets find-replace \u003cspreadsheetId\u003e \"old\" \"new\"\ngog sheets find-replace \u003cspreadsheetId\u003e \"old\" \"new\" --sheet Sheet1 --match-entire\ngog sheets links \u003cspreadsheetId\u003e 'Sheet1!A1:B10'\ngog sheets add-tab \u003cspreadsheetId\u003e \u003ctabName\u003e --index 0\ngog sheets rename-tab \u003cspreadsheetId\u003e \u003coldName\u003e \u003cnewName\u003e\ngog sheets delete-tab \u003cspreadsheetId\u003e \u003ctabName\u003e --force\n```\n\n### Contacts\n\n```bash\n# Personal contacts\ngog contacts list --max 50\ngog contacts search \"Ada\" --max 50\ngog contacts get people/\u003cresourceName\u003e\ngog contacts get user@example.com     # Get by email\n\n# Other contacts (people you've interacted with)\ngog contacts other list --max 50\ngog contacts other search \"John\" --max 50\n\n# Create and update\ngog contacts create \\\n  --given \"John\" \\\n  --family \"Doe\" \\\n  --email \"john@example.com\" \\\n  --phone \"+1234567890\" \\\n  --address \"12 St James's Square, London\" \\\n  --gender \"male\" \\\n  --relation \"spouse=Jane Doe\"\n\ngog contacts update people/\u003cresourceName\u003e \\\n  --given \"Jane\" \\\n  --email \"jane@example.com\" \\\n  --address \"1 Infinite Loop, Cupertino\" \\\n  --birthday \"1990-05-12\" \\\n  --gender \"female\" \\\n  --notes \"Met at WWDC\" \\\n  --relation \"friend=Bob\"\n\n# Update via JSON (see docs/contacts-json-update.md)\ngog contacts get people/\u003cresourceName\u003e --json | \\\n  jq '(.contact.urls //= []) | (.contact.urls += [{\"value\":\"obsidian://open?vault=notes\u0026file=People/John%20Doe\",\"type\":\"profile\"}])' | \\\n  gog contacts update people/\u003cresourceName\u003e --from-file -\n\ngog contacts delete people/\u003cresourceName\u003e\n\n# Workspace directory (requires Google Workspace)\ngog contacts directory list --max 50\ngog contacts directory search \"Jane\" --max 50\n```\n\n### Tasks\n\n```bash\n# Task lists\ngog tasks lists --max 50\ngog tasks lists create \u003ctitle\u003e\n\n# Tasks in a list\ngog tasks list \u003ctasklistId\u003e --max 50\ngog tasks get \u003ctasklistId\u003e \u003ctaskId\u003e\ngog tasks add \u003ctasklistId\u003e --title \"Task title\"\ngog tasks add \u003ctasklistId\u003e --title \"Weekly sync\" --due 2025-02-01 --repeat weekly --repeat-count 4\ngog tasks add \u003ctasklistId\u003e --title \"Daily standup\" --due 2025-02-01 --repeat daily --repeat-until 2025-02-05\ngog tasks add \u003ctasklistId\u003e --title \"Bi-weekly review\" --due 2025-02-01 --recur-rrule \"FREQ=WEEKLY;INTERVAL=2\" --repeat-count 3\ngog tasks update \u003ctasklistId\u003e \u003ctaskId\u003e --title \"New title\"\ngog tasks done \u003ctasklistId\u003e \u003ctaskId\u003e\ngog tasks undo \u003ctasklistId\u003e \u003ctaskId\u003e\ngog tasks delete \u003ctasklistId\u003e \u003ctaskId\u003e\ngog tasks clear \u003ctasklistId\u003e\n\n# Note: Google Tasks treats due dates as date-only; time components may be ignored.\n# Note: Public Google Tasks API does not expose true recurring-task metadata; `--repeat*`/`--recur*` materialize concrete tasks.\n# See docs/dates.md for all supported date/time input formats across commands.\n```\n\n### Sheets\n\n```bash\n# Read\ngog sheets metadata \u003cspreadsheetId\u003e\ngog sheets get \u003cspreadsheetId\u003e 'Sheet1!A1:B10'\ngog sheets get \u003cspreadsheetId\u003e MyNamedRange\n\n# Export (via Drive)\ngog sheets export \u003cspreadsheetId\u003e --format pdf --out ./sheet.pdf\ngog sheets export \u003cspreadsheetId\u003e --format xlsx --out ./sheet.xlsx\n\n# Write\ngog sheets update \u003cspreadsheetId\u003e 'A1' 'val1|val2,val3|val4'\ngog sheets update \u003cspreadsheetId\u003e 'A1' --values-json '[[\"a\",\"b\"],[\"c\",\"d\"]]'\ngog sheets update \u003cspreadsheetId\u003e 'Sheet1!A1:C1' 'new|row|data' --copy-validation-from 'Sheet1!A2:C2'\ngog sheets update \u003cspreadsheetId\u003e MyNamedRange 'new|row|data'\ngog sheets update \u003cspreadsheetId\u003e 'Sheet1!A1:C1' 'new|row|data' --copy-validation-from MyValidationNamedRange\ngog sheets append \u003cspreadsheetId\u003e 'Sheet1!A:C' 'new|row|data'\ngog sheets append \u003cspreadsheetId\u003e 'Sheet1!A:C' 'new|row|data' --copy-validation-from 'Sheet1!A2:C2'\ngog sheets find-replace \u003cspreadsheetId\u003e \"old\" \"new\"\ngog sheets find-replace \u003cspreadsheetId\u003e \"old\" \"new\" --sheet Sheet1 --regex\ngog sheets update-note \u003cspreadsheetId\u003e 'Sheet1!A1' --note ''\ngog sheets append \u003cspreadsheetId\u003e MyNamedRange 'new|row|data'\ngog sheets clear \u003cspreadsheetId\u003e 'Sheet1!A1:B10'\ngog sheets clear \u003cspreadsheetId\u003e MyNamedRange\n\n# Format\ngog sheets format \u003cspreadsheetId\u003e 'Sheet1!A1:B2' --format-json '{\"textFormat\":{\"bold\":true}}' --format-fields 'userEnteredFormat.textFormat.bold'\ngog sheets format \u003cspreadsheetId\u003e MyNamedRange --format-json '{\"textFormat\":{\"bold\":true}}' --format-fields 'userEnteredFormat.textFormat.bold'\ngog sheets format \u003cspreadsheetId\u003e 'Sheet1!A1:B2' --format-json '{\"borders\":{\"top\":{\"style\":\"SOLID\"}}}' --format-fields 'userEnteredFormat.borders.top.style'\ngog sheets merge \u003cspreadsheetId\u003e 'Sheet1!A1:B2'\ngog sheets unmerge \u003cspreadsheetId\u003e 'Sheet1!A1:B2'\ngog sheets number-format \u003cspreadsheetId\u003e 'Sheet1!C:C' --type CURRENCY --pattern '$#,##0.00'\ngog sheets freeze \u003cspreadsheetId\u003e --rows 1 --cols 1\ngog sheets resize-columns \u003cspreadsheetId\u003e 'Sheet1!A:C' --auto\ngog sheets resize-rows \u003cspreadsheetId\u003e 'Sheet1!1:10' --height 36\ngog sheets read-format \u003cspreadsheetId\u003e 'Sheet1!A1:B2'\ngog sheets read-format \u003cspreadsheetId\u003e 'Sheet1!A1:B2' --effective\n\n# Named ranges\ngog sheets named-ranges \u003cspreadsheetId\u003e\ngog sheets named-ranges get \u003cspreadsheetId\u003e MyNamedRange\ngog sheets named-ranges add \u003cspreadsheetId\u003e MyNamedRange 'Sheet1!A1:B2'\ngog sheets named-ranges add \u003cspreadsheetId\u003e MyCols 'Sheet1!A:C'\ngog sheets named-ranges update \u003cspreadsheetId\u003e MyNamedRange --name MyNamedRange2\ngog sheets named-ranges delete \u003cspreadsheetId\u003e MyNamedRange2\n\n# Charts\ngog sheets chart list \u003cspreadsheetId\u003e\ngog sheets chart get \u003cspreadsheetId\u003e \u003cchartId\u003e --json \u003e chart.json\ngog sheets chart create \u003cspreadsheetId\u003e --spec-json @chart.json\ngog sheets chart create \u003cspreadsheetId\u003e --spec-json '{\"title\":\"Revenue\",\"basicChart\":{\"chartType\":\"COLUMN\"}}' --sheet Sheet1 --anchor E10\ngog sheets chart update \u003cspreadsheetId\u003e \u003cchartId\u003e --spec-json '{\"title\":\"New Title\",\"basicChart\":{\"chartType\":\"PIE\"}}'\ngog sheets chart delete \u003cspreadsheetId\u003e \u003cchartId\u003e\n\n# Insert rows/cols\ngog sheets insert \u003cspreadsheetId\u003e \"Sheet1\" rows 2 --count 3\ngog sheets insert \u003cspreadsheetId\u003e \"Sheet1\" cols 3 --after\n\n# Notes\ngog sheets notes \u003cspreadsheetId\u003e 'Sheet1!A1:B10'\ngog sheets links \u003cspreadsheetId\u003e 'Sheet1!A1:B10'   # Includes rich-text links\n\n# Create\ngog sheets create \"My New Spreadsheet\" --sheets \"Sheet1,Sheet2\"\n\n# Tab management\ngog sheets add-tab \u003cspreadsheetId\u003e \u003ctabName\u003e --index 0\ngog sheets rename-tab \u003cspreadsheetId\u003e \u003coldName\u003e \u003cnewName\u003e\ngog sheets delete-tab \u003cspreadsheetId\u003e \u003ctabName\u003e          # use --force to skip confirmation\n```\n\n### Forms\n\n```bash\n# Forms\ngog forms get \u003cformId\u003e\ngog forms create --title \"Weekly Check-in\" --description \"Friday async update\"\ngog forms update \u003cformId\u003e --title \"Weekly Sync\" --quiz true\ngog forms add-question \u003cformId\u003e --title \"What shipped?\" --type paragraph --required\ngog forms move-question \u003cformId\u003e 3 1\ngog forms delete-question \u003cformId\u003e 2 --force\n\n# Responses\ngog forms responses list \u003cformId\u003e --max 20\ngog forms responses get \u003cformId\u003e \u003cresponseId\u003e\n\n# Watches\ngog forms watch create \u003cformId\u003e --topic projects/\u003cproject\u003e/topics/\u003ctopic\u003e\ngog forms watch list \u003cformId\u003e\ngog forms watch renew \u003cformId\u003e \u003cwatchId\u003e\ngog forms watch delete \u003cformId\u003e \u003cwatchId\u003e\n```\n\n### Apps Script\n\n```bash\n# Projects\ngog appscript get \u003cscriptId\u003e\ngog appscript content \u003cscriptId\u003e\ngog appscript create --title \"Automation Helpers\"\ngog appscript create --title \"Bound Script\" --parent-id \u003cdriveFileId\u003e\n\n# Execute functions\ngog appscript run \u003cscriptId\u003e myFunction --params '[\"arg1\", 123, true]'\ngog appscript run \u003cscriptId\u003e myFunction --dev-mode\n```\n\n### People\n\n```bash\n# Profile\ngog people me\ngog people get people/\u003cuserId\u003e\n\n# Search the Workspace directory\ngog people search \"Ada Lovelace\" --max 5\n\n# Relations (defaults to people/me)\ngog people relations\ngog people relations people/\u003cuserId\u003e --type manager\n```\n\n### Chat\n\n```bash\n# Spaces\ngog chat spaces list\ngog chat spaces find \"Engineering\"\ngog chat spaces find \"Engineering\" --exact\ngog chat spaces create \"Engineering\" --member alice@company.com --member bob@company.com\n\n# Messages\ngog chat messages list spaces/\u003cspaceId\u003e --max 5\ngog chat messages list spaces/\u003cspaceId\u003e --thread \u003cthreadId\u003e\ngog chat messages list spaces/\u003cspaceId\u003e --unread\ngog chat messages send spaces/\u003cspaceId\u003e --text \"Build complete!\" --thread spaces/\u003cspaceId\u003e/threads/\u003cthreadId\u003e\ngog chat messages reactions list spaces/\u003cspaceId\u003e/messages/\u003cmessageId\u003e\ngog chat messages react spaces/\u003cspaceId\u003e/messages/\u003cmessageId\u003e \"👍\"  # shorthand for reactions create\ngog chat messages reactions delete spaces/\u003cspaceId\u003e/messages/\u003cmessageId\u003e/reactions/\u003creactionId\u003e\n\n# Threads\ngog chat threads list spaces/\u003cspaceId\u003e\n\n# Direct messages\ngog chat dm space user@company.com\ngog chat dm send user@company.com --text \"ping\"\n```\n\nNote: Chat commands require a Google Workspace account (consumer @gmail.com accounts are not supported).\n\n### Admin\n\n```bash\n# Requires a Workspace service account with domain-wide delegation.\ngog admin users list --domain example.com\ngog admin users get user@example.com\ngog admin users create user@example.com --given Ada --family Lovelace --password 'TempPass123!'\ngog admin users suspend user@example.com --force\n\ngog admin groups list --domain example.com\ngog admin groups members list engineering@example.com\ngog admin groups members add engineering@example.com user@example.com --role MEMBER\ngog admin groups members remove engineering@example.com user@example.com --force\n```\n\n### Groups (Google Workspace)\n\n```bash\n# List groups you belong to\ngog groups list\n\n# List members of a group\ngog groups members engineering@company.com\n```\n\nNote: Groups commands require the Cloud Identity API and the `cloud-identity.groups.readonly` scope. If you get a permissions error, re-authenticate:\n\n```bash\ngog auth add your@email.com --services groups --force-consent\n```\n\n### Classroom (Google Workspace for Education)\n\n```bash\n# Courses\ngog classroom courses list\ngog classroom courses list --role teacher\ngog classroom courses get \u003ccourseId\u003e\ngog classroom courses create --name \"Math 101\"\ngog classroom courses update \u003ccourseId\u003e --name \"Math 102\"\ngog classroom courses archive \u003ccourseId\u003e\ngog classroom courses unarchive \u003ccourseId\u003e\ngog classroom courses url \u003ccourseId\u003e\n\n# Roster\ngog classroom roster \u003ccourseId\u003e\ngog classroom roster \u003ccourseId\u003e --students\ngog classroom students add \u003ccourseId\u003e \u003cuserId\u003e\ngog classroom teachers add \u003ccourseId\u003e \u003cuserId\u003e\n\n# Coursework\ngog classroom coursework list \u003ccourseId\u003e\ngog classroom coursework get \u003ccourseId\u003e \u003ccourseworkId\u003e\ngog classroom coursework create \u003ccourseId\u003e --title \"Homework 1\" --type ASSIGNMENT --state PUBLISHED\ngog classroom coursework update \u003ccourseId\u003e \u003ccourseworkId\u003e --title \"Updated\"\ngog classroom coursework assignees \u003ccourseId\u003e \u003ccourseworkId\u003e --mode INDIVIDUAL_STUDENTS --add-student \u003cstudentId\u003e\n\n# Materials\ngog classroom materials list \u003ccourseId\u003e\ngog classroom materials create \u003ccourseId\u003e --title \"Syllabus\" --state PUBLISHED\n\n# Submissions\ngog classroom submissions list \u003ccourseId\u003e \u003ccourseworkId\u003e\ngog classroom submissions get \u003ccourseId\u003e \u003ccourseworkId\u003e \u003csubmissionId\u003e\ngog classroom submissions grade \u003ccourseId\u003e \u003ccourseworkId\u003e \u003csubmissionId\u003e --grade 85\ngog classroom submissions return \u003ccourseId\u003e \u003ccourseworkId\u003e \u003csubmissionId\u003e\ngog classroom submissions turn-in \u003ccourseId\u003e \u003ccourseworkId\u003e \u003csubmissionId\u003e\ngog classroom submissions reclaim \u003ccourseId\u003e \u003ccourseworkId\u003e \u003csubmissionId\u003e\n\n# Announcements\ngog classroom announcements list \u003ccourseId\u003e\ngog classroom announcements create \u003ccourseId\u003e --text \"Welcome!\"\ngog classroom announcements update \u003ccourseId\u003e \u003cannouncementId\u003e --text \"Updated\"\ngog classroom announcements assignees \u003ccourseId\u003e \u003cannouncementId\u003e --mode INDIVIDUAL_STUDENTS --add-student \u003cstudentId\u003e\n\n# Topics\ngog classroom topics list \u003ccourseId\u003e\ngog classroom topics create \u003ccourseId\u003e --name \"Unit 1\"\ngog classroom topics update \u003ccourseId\u003e \u003ctopicId\u003e --name \"Unit 2\"\n\n# Invitations\ngog classroom invitations list\ngog classroom invitations create \u003ccourseId\u003e \u003cuserId\u003e --role student\ngog classroom invitations accept \u003cinvitationId\u003e\n\n# Guardians\ngog classroom guardians list \u003cstudentId\u003e\ngog classroom guardians get \u003cstudentId\u003e \u003cguardianId\u003e\ngog classroom guardians delete \u003cstudentId\u003e \u003cguardianId\u003e\n\n# Guardian invitations\ngog classroom guardian-invitations list \u003cstudentId\u003e\ngog classroom guardian-invitations create \u003cstudentId\u003e --email parent@example.com\n\n# Profiles\ngog classroom profile get\ngog classroom profile get \u003cuserId\u003e\n```\n\nNote: Classroom commands require a Google Workspace for Education account. Personal Google accounts have limited Classroom functionality.\n\n### Docs\n\n```bash\n# Export (via Drive)\ngog docs export \u003cdocId\u003e --format pdf --out ./doc.pdf\ngog docs export \u003cdocId\u003e --format docx --out ./doc.docx\ngog docs export \u003cdocId\u003e --format txt --out ./doc.txt\ngog docs export \u003cdocId\u003e --format md --out ./doc.md\ngog docs export \u003cdocId\u003e --format html --out ./doc.html\n\n# Sed-style regex editing with Markdown formatting (sedmat)\ngog docs sed \u003cdocId\u003e 's/pattern/replacement/g'\n\n# Formatting in replacements\ngog docs sed \u003cdocId\u003e 's/hello/**hello**/'          # bold\ngog docs sed \u003cdocId\u003e 's/hello/*hello*/'             # italic\ngog docs sed \u003cdocId\u003e 's/hello/~~hello~~/'           # strikethrough\ngog docs sed \u003cdocId\u003e 's/hello/`hello`/'             # monospace\ngog docs sed \u003cdocId\u003e 's/hello/__hello__/'           # underline\ngog docs sed \u003cdocId\u003e 's/Google/[Google](https://google.com)/'  # link\n\n# Images\ngog docs sed \u003cdocId\u003e 's/{{LOGO}}/![](https://example.com/logo.png)/'\ngog docs sed \u003cdocId\u003e 's/{{HERO}}/![](https://example.com/hero.jpg){width=600}/'\n\n# Tables — create, populate, modify\ngog docs sed \u003cdocId\u003e 's/{{TABLE}}/|3x4|/'            # create 3-row, 4-col table\ngog docs sed \u003cdocId\u003e 's/|1|[A1]/**Name**/'           # set cell A1 (bold)\ngog docs sed \u003cdocId\u003e 's/|1|[1,*]/**\u0026**/'             # bold entire row 1\ngog docs sed \u003cdocId\u003e 's/|1|[row:+2]//'               # insert row before row 2\ngog docs sed \u003cdocId\u003e 's/|1|[col:$+]//'               # append column at end\n```\n\n\u003e See [docs/sedmat.md](docs/sedmat.md) for the full sedmat syntax reference.\n\n### Slides\n\n```bash\n# Export (via Drive)\ngog slides export \u003cpresentationId\u003e --format pptx --out ./deck.pptx\ngog slides export \u003cpresentationId\u003e --format pdf --out ./deck.pdf\n\n# Create from template with text replacements\ngog slides create-from-template \u003ctemplateId\u003e \"Q1 Report\" \\\n  --replace \"quarter=Q1 2026\" \\\n  --replace \"revenue=$1.2M\" \\\n  --replace \"growth=15%\"\n\n# Use JSON file for many replacements\ncat \u003e replacements.json \u003c\u003cEOF\n{\n  \"name\": \"John Doe\",\n  \"title\": \"Sales Manager\",\n  \"date\": \"2026-02-15\",\n  \"sales\": \"125\",\n  \"target\": \"100\"\n}\nEOF\n\ngog slides create-from-template \u003ctemplateId\u003e \"Monthly Report\" \\\n  --replacements replacements.json\n\n# Read slide content (text, notes, images)\ngog slides read-slide \u003cpresentationId\u003e \u003cslideId\u003e\n\n# Include grouped elements, word art, and tables\ngog slides read-slide \u003cpresentationId\u003e \u003cslideId\u003e --recursive --json\n\n# Get a rendered slide thumbnail URL\ngog slides thumbnail \u003cpresentationId\u003e \u003cslideId\u003e\n\n# Download a rendered slide thumbnail\ngog slides thumbnail \u003cpresentationId\u003e \u003cslideId\u003e --output ./slide.png\n\n# Control thumbnail size and format\ngog slides thumbnail \u003cpresentationId\u003e \u003cslideId\u003e --size medium --format jpeg --output ./slide.jpg\n```\n\n## Output Formats\n\n### Text\n\nHuman-readable output with colors (default):\n\n```bash\n$ gog gmail search 'newer_than:7d' --max 3\nTHREAD_ID           SUBJECT                           FROM                  DATE\n18f1a2b3c4d5e6f7    Meeting notes                     alice@example.com     2025-01-10\n17e1d2c3b4a5f6e7    Invoice #12345                    billing@vendor.com    2025-01-09\n16d1c2b3a4e5f6d7    Project update                    bob@example.com       2025-01-08\n```\n\nMessage-level search (one row per email; add `--include-body` to fetch/decode bodies, or `--full` for untruncated text bodies):\n\n```bash\n$ gog gmail messages search 'newer_than:7d' --max 3\nID                  THREAD             SUBJECT                           FROM                  DATE\n18f1a2b3c4d5e6f7    9e8d7c6b5a4f3e2d    Meeting notes                     alice@example.com     2025-01-10\n17e1d2c3b4a5f6e7    9e8d7c6b5a4f3e2d    Invoice #12345                    billing@vendor.com    2025-01-09\n16d1c2b3a4e5f6d7    7f6e5d4c3b2a1908    Project update                    bob@example.com       2025-01-08\n```\n\n### JSON\n\nMachine-readable output for scripting and automation:\n\n```bash\n$ gog gmail search 'newer_than:7d' --max 3 --json\n{\n  \"threads\": [\n    {\n      \"id\": \"18f1a2b3c4d5e6f7\",\n      \"snippet\": \"Meeting notes from today...\",\n      \"messages\": [...]\n    },\n    ...\n  ]\n}\n```\n\n```bash\n$ gog gmail messages search 'newer_than:7d' --max 3 --json\n{\n  \"messages\": [\n    {\n      \"id\": \"18f1a2b3c4d5e6f7\",\n      \"threadId\": \"9e8d7c6b5a4f3e2d\",\n      \"subject\": \"Meeting notes\",\n      \"from\": \"alice@example.com\",\n      \"date\": \"2025-01-10\"\n    },\n    ...\n  ]\n}\n```\n\n```bash\n$ gog gmail messages search 'newer_than:7d' --max 1 --full --json\n{\n  \"messages\": [\n    {\n      \"id\": \"18f1a2b3c4d5e6f7\",\n      \"threadId\": \"9e8d7c6b5a4f3e2d\",\n      \"subject\": \"Meeting notes\",\n      \"from\": \"alice@example.com\",\n      \"date\": \"2025-01-10\",\n      \"body\": \"Hi team — meeting notes...\"\n    }\n  ]\n}\n```\n\nData goes to stdout, errors and progress to stderr for clean piping:\n\n```bash\ngog --json drive ls --max 5 | jq '.files[] | select(.mimeType==\"application/pdf\")'\n```\n\nUseful pattern:\n\n- `gog --json ... | jq .`\n\nCalendar JSON convenience fields:\n\n- `startDayOfWeek` / `endDayOfWeek` on event payloads (derived from start/end).\n\n## Examples\n\n### Search recent emails and download attachments\n\n```bash\n# Search for emails from the last week\ngog gmail search 'newer_than:7d has:attachment' --max 10\n\n# Get thread details and download attachments\ngog gmail thread get \u003cthreadId\u003e --download\n```\n\n### Modify labels on a thread\n\n```bash\n# Archive and star a thread\ngog gmail thread modify \u003cthreadId\u003e --remove INBOX --add STARRED\n```\n\n### Create a calendar event with attendees\n\n```bash\n# Find a free time slot\ngog calendar freebusy --calendars \"primary\" \\\n  --from 2025-01-15T00:00:00Z \\\n  --to 2025-01-16T00:00:00Z\n\n# Create the meeting\ngog calendar create primary \\\n  --summary \"Team Standup\" \\\n  --from 2025-01-15T10:00:00Z \\\n  --to 2025-01-15T10:30:00Z \\\n  --attendees \"alice@example.com,bob@example.com\"\n```\n\n### Find and download files from Drive\n\n```bash\n# Search for PDFs\ngog drive search \"invoice filetype:pdf\" --max 20 --json | \\\n  jq -r '.files[] | .id' | \\\n  while read fileId; do\n    gog drive download \"$fileId\"\n  done\n```\n\n### Manage multiple accounts\n\n```bash\n# Check personal Gmail\ngog gmail search 'is:unread' --account personal@gmail.com\n\n# Check work Gmail\ngog gmail search 'is:unread' --account work@company.com\n\n# Or set default\nexport GOG_ACCOUNT=work@company.com\ngog gmail search 'is:unread'\n```\n\n### Update a Google Sheet from a CSV\n\n```bash\n# Convert CSV to pipe-delimited format and update sheet\ncat data.csv | tr ',' '|' | \\\n  gog sheets update \u003cspreadsheetId\u003e 'Sheet1!A1'\n```\n\n### Export Sheets / Docs / Slides\n\n```bash\n# Sheets\ngog sheets export \u003cspreadsheetId\u003e --format pdf\n\n# Docs\ngog docs export \u003cdocId\u003e --format docx\n\n# Slides\ngog slides export \u003cpresentationId\u003e --format pptx\n```\n\n### Batch process Gmail threads\n\n```bash\n# Mark all emails from a sender as read\ngog --json gmail search 'from:noreply@example.com' --max 200 | \\\n  jq -r '.threads[].id' | \\\n  xargs -n 50 gog gmail labels modify --remove UNREAD\n\n# Archive old emails\ngog --json gmail search 'older_than:1y' --max 200 | \\\n  jq -r '.threads[].id' | \\\n  xargs -n 50 gog gmail labels modify --remove INBOX\n\n# Label important emails\ngog --json gmail search 'from:boss@example.com' --max 200 | \\\n  jq -r '.threads[].id' | \\\n  xargs -n 50 gog gmail labels modify --add IMPORTANT\n```\n\n## Advanced Features\n\n### Verbose Mode\n\nEnable verbose logging for troubleshooting:\n\n```bash\ngog --verbose gmail search 'newer_than:7d'\n# Shows API requests and responses\n```\n\n## Global Flags\n\nAll commands support these flags:\n\n- `--account \u003cemail|alias|auto\u003e` - Account to use (overrides GOG_ACCOUNT)\n- `--enable-commands \u003ccsv\u003e` - Allowlist commands; dot paths allowed (e.g., `calendar,tasks,gmail.search`)\n- `--disable-commands \u003ccsv\u003e` - Denylist commands; dot paths allowed (e.g., `gmail.send,gmail.drafts.send`)\n- `--gmail-no-send` - Block Gmail send operations\n- `--json` - Output JSON to stdout (best for scripting)\n- `--plain` - Output stable, parseable text to stdout (TSV; no colors)\n- `--color \u003cmode\u003e` - Color mode: `auto`, `always`, or `never` (default: auto)\n- `--force` - Skip confirmations for destructive commands\n- `--no-input` - Never prompt; fail instead (useful for CI)\n- `--verbose` - Enable verbose logging\n- `--help` - Show help for any command\n\n## Shell Completions\n\nGenerate shell completions for your preferred shell:\n\n### Bash\n\n```bash\n# macOS (with Homebrew)\ngog completion bash \u003e $(brew --prefix)/etc/bash_completion.d/gog\n\n# Linux\ngog completion bash \u003e /etc/bash_completion.d/gog\n\n# Or load directly in your current session\nsource \u003c(gog completion bash)\n```\n\n### Zsh\n\n```zsh\n# Generate completion file\ngog completion zsh \u003e \"${fpath[1]}/_gog\"\n\n# Or add to .zshrc for automatic loading\necho 'eval \"$(gog completion zsh)\"' \u003e\u003e ~/.zshrc\n\n# Enable completions if not already enabled\necho \"autoload -U compinit; compinit\" \u003e\u003e ~/.zshrc\n```\n\n### Fish\n\n```fish\ngog completion fish \u003e ~/.config/fish/completions/gog.fish\n```\n\n### PowerShell\n\n```powershell\n# Load for current session\ngog completion powershell | Out-String | Invoke-Expression\n\n# Or add to profile for all sessions\ngog completion powershell \u003e\u003e $PROFILE\n```\n\nAfter installing completions, start a new shell session for changes to take effect.\n\n## Development\n\nAfter cloning, install tools:\n\n```bash\nmake tools\n```\n\nPinned tools (installed into `.tools/`):\n\n- Format: `make fmt` (goimports + gofumpt)\n- Lint: `make lint` (golangci-lint)\n- Test: `make test`\n\nCI runs format checks, tests, and lint on push/PR.\n\nRegenerate the expanded command reference from the live schema when CLI surface changes:\n\n```bash\nmake docs-commands\n```\n\n### Integration Tests (Live Google APIs)\n\nOpt-in tests that hit real Google APIs using your stored `gog` credentials/tokens.\n\n```bash\n# Optional: override which account to use\nexport GOG_IT_ACCOUNT=you@gmail.com\nexport GOG_CLIENT=work\ngo test -tags=integration ./...\n```\n\nTip: if you want to avoid macOS Keychain prompts during these runs, set `GOG_KEYRING_BACKEND=file` and `GOG_KEYRING_PASSWORD=...` (uses encrypted on-disk keyring).\n\n### Live Test Script (CLI)\n\nFast end-to-end smoke checks against live APIs:\n\n```bash\nscripts/live-test.sh --fast\nscripts/live-test.sh --account you@gmail.com --skip groups,keep,calendar-enterprise\nscripts/live-test.sh --client work --account you@company.com\n```\n\nScript toggles:\n\n- `--auth all,groups` to re-auth before running\n- `--client \u003cname\u003e` to select OAuth client credentials\n- `--strict` to fail on optional features (groups/keep/enterprise)\n- `--allow-nontest` to override the test-account guardrail\n\nGo test wrapper (opt-in):\n\n```bash\nGOG_LIVE=1 go test -tags=integration ./internal/integration -run Live\n```\n\nOptional env:\n- `GOG_LIVE_FAST=1`\n- `GOG_LIVE_SKIP=groups,keep`\n- `GOG_LIVE_AUTH=all,groups`\n- `GOG_LIVE_ALLOW_NONTEST=1`\n- `GOG_LIVE_EMAIL_TEST=steipete+gogtest@gmail.com`\n- `GOG_LIVE_GROUP_EMAIL=group@domain`\n- `GOG_LIVE_CLASSROOM_COURSE=\u003ccourseId\u003e`\n- `GOG_LIVE_CLASSROOM_CREATE=1`\n- `GOG_LIVE_CLASSROOM_ALLOW_STATE=1`\n- `GOG_LIVE_TRACK=1`\n- `GOG_LIVE_GMAIL_BATCH_DELETE=1`\n- `GOG_LIVE_GMAIL_FILTERS=1`\n- `GOG_LIVE_GMAIL_WATCH_TOPIC=projects/.../topics/...`\n- `GOG_LIVE_CALENDAR_RESPOND=1`\n- `GOG_LIVE_CALENDAR_RECURRENCE=1`\n- `GOG_KEEP_SERVICE_ACCOUNT=/path/to/service-account.json`\n- `GOG_KEEP_IMPERSONATE=user@workspace-domain`\n\n### Make Shortcut\n\nBuild and run:\n\n```bash\nmake gog auth add you@gmail.com\n```\n\nFor clean stdout when scripting:\n\n- Use `--` when the first arg is a flag: `make gog -- --json gmail search \"from:me\" | jq .`\n\n## License\n\nMIT\n\n## Links\n\n- [GitHub Repository](https://github.com/steipete/gogcli)\n- [Gmail API Documentation](https://developers.google.com/gmail/api)\n- [Google Calendar API Documentation](https://developers.google.com/calendar)\n- [Google Drive API Documentation](https://developers.google.com/drive)\n- [Google People API Documentation](https://developers.google.com/people)\n- [Google Tasks API Documentation](https://developers.google.com/tasks)\n- [Google Sheets API Documentation](https://developers.google.com/sheets)\n- [Cloud Identity API Documentation](https://cloud.google.com/identity/docs/reference/rest)\n\n## Credits\n\nThis project is inspired by Mario Zechner's original CLIs:\n\n- [gmcli](https://github.com/badlogic/gmcli)\n- [gccli](https://github.com/badlogic/gccli)\n- [gdcli](https://github.com/badlogic/gdcli)\n","funding_links":[],"categories":["Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteipete%2Fgogcli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsteipete%2Fgogcli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteipete%2Fgogcli/lists"}