37 Commits

Author SHA1 Message Date
Justin Poehnelt 503315bc42 docs: update installation instructions to prioritize github releases (#656)
* docs: update installation instructions to prioritize github releases

* ci: add explicit verification instructions to github release notes

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-31 12:11:46 -06:00
Dicky Arinal 58fd38e6ab docs: fix Environment Variables table formatting in README (#490)
* docs: fix Environment Variables table formatting in README

Remove blank line that was breaking the markdown table into two separate tables.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore: trigger CLA recheck

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-17 11:51:16 -06:00
Justin Poehnelt 47afe5fdb3 feat(timezone): use Google account timezone for day-boundary calculations (#480)
Replace machine-local chrono::Local and UTC epoch math with the
authenticated user's Google account timezone (Calendar Settings API).

- Add chrono-tz dependency for IANA timezone parsing
- New src/timezone.rs: resolve timezone with priority:
  --timezone flag > 24h cache > Calendar API > local fallback
- calendar.rs: add --timezone/--tz flag to +agenda
- workflows.rs: fix +standup-report, +weekly-digest, +meeting-prep
- auth_commands.rs: invalidate timezone cache on logout
- Update README.md and AGENTS.md with timezone docs

Supersedes #369 and #462.

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-13 16:41:16 -06:00
Justin Poehnelt 6f3e0906d3 feat: add opt-in structured HTTP request logging via tracing (#478)
Add PII-free structured logging controlled by two environment variables:
- GOOGLE_WORKSPACE_CLI_LOG: stderr log filter (e.g., gws=debug)
- GOOGLE_WORKSPACE_CLI_LOG_FILE: directory for JSON log files (daily rotation)

Logging is silent by default (zero overhead). Instrumented sites:
- executor.rs: API request/response (method, status, latency)
- client.rs: 429 retry events
- discovery.rs: cache hit vs network fetch

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-13 15:27:38 -06:00
Anshul Garg 8448c1aac1 docs: add Homebrew as an installation option in README (#467)
Closes #399.

Add `brew install googleworkspace-cli` to the installation section.
The formula is already available in homebrew-core:
https://github.com/Homebrew/homebrew-core/blob/main/Formula/g/googleworkspace-cli.rb
2026-03-13 13:42:58 -06:00
Abhi Ram Reddy Salammagari 2df32eed1b docs: document helper commands and the + prefix convention (#427)
* docs: document helper commands and the + prefix convention

Add a "Helper Commands" section to the Advanced Usage chapter of the
README explaining:

- What the `+` prefix means and why it exists (visually distinct from
  Discovery-generated method names, no collision risk)
- How to discover helpers via `gws <service> --help`
- A full reference table of all 23 helper commands across 11 services
- Usage examples for the most common helpers (gmail, sheets, calendar,
  drive, workflow)

Fixes discoverability gap: users had no way to learn about helper
commands without reading the source code.

* fix(docs): correct +append and +upload examples per Gemini review

- gws sheets +append: flag is --spreadsheet (not --spreadsheet-id) and
  +append has no --range argument
- gws drive +upload: file path is a positional argument, not --file flag

* docs: clarify script +push is destructive (replaces, not adds)

The +push helper replaces all files in an Apps Script project.
Update description to reflect this so users understand the action
is destructive before running it.

Addresses code-review feedback.

* chore(changeset): correct helper command count to 24 across 10 services

Addresses code-review feedback noting the count was off.
2026-03-12 10:06:13 -06:00
Abhi Ram Reddy Salammagari 247e27a876 feat(error): add structured exit codes for scriptable error handling (#428)
* feat(error): add structured exit codes for scriptable error handling

Replace the hardcoded `std::process::exit(1)` with a type-specific exit
code derived from the GwsError variant:

  0 — success
  1 — API error      (GwsError::Api)
  2 — auth error     (GwsError::Auth)
  3 — validation     (GwsError::Validation)
  4 — discovery      (GwsError::Discovery)
  5 — internal       (GwsError::Other)

This allows shell scripts to branch on failure type without parsing the
JSON error output:

  gws drive files list ...
  case $? in
    1) echo "API error — check your params" ;;
    2) echo "Auth error — run: gws auth login" ;;
    3) echo "Bad arguments" ;;
  esac

Changes:
- Add GwsError::exit_code() mapping variants to codes
- Update main() to call std::process::exit(err.exit_code())
- Document exit codes in gws --help (print_usage)
- Document exit codes in README under new Exit Codes section
- Add 6 unit tests including a regression guard asserting all codes are distinct

* refactor(error): replace magic exit-code numbers with named constants

Add EXIT_CODE_API/AUTH/VALIDATION/DISCOVERY/OTHER associated constants
on GwsError so callers and tests reference symbolic names rather than
bare integers. Update exit_code() match arms and all tests accordingly.
The distinctness test now validates the constants array directly.

Addresses code-review feedback requesting named constants.

* refactor(error): centralize exit code help text via EXIT_CODE_DOCUMENTATION

Add a module-level EXIT_CODE_DOCUMENTATION constant — a static slice of
(code, description) pairs built from the EXIT_CODE_* constants. Replace
the hardcoded println! block in print_usage() with a loop over this slice
so the help output is always in sync with the defined constants and cannot
drift out of date.

Addresses code-review feedback requesting a single source of truth.
2026-03-12 10:02:47 -06:00
Justin Poehnelt 8d89325a8b feat(credential_store): add GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND env var (#359)
* feat(credential_store): add GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND env var

Add gogcli-style backend selection for encryption key storage:
- keyring (default): OS keyring with file fallback
- file: .encryption_key file only (Docker/CI/headless)

Never delete .encryption_key — it always serves as a durable fallback
for environments where the keyring is ephemeral. When generating new
keys with backend=keyring, save to both keyring and file.

Extracts KeyringProvider trait + resolve_key() for testability.
25 tests covering both backends and all edge cases.

Fixes #344

* chore: regenerate skills [skip ci]

* fix(credential_store): use O_EXCL for race-safe key generation

Use create_new(true) (O_EXCL on Unix, CREATE_NEW on Windows) when
generating a new encryption key file. If another process wins the
race, read their key instead. Platform-independent.

* fix(credential_store): sync winner's key into keyring after file race

When two processes race to create the encryption key file, the loser
now syncs the winner's key back into the keyring. Without this, the
keyring and file could permanently diverge.

* test(credential_store): add 9 tests covering file exclusion, env parsing, and race paths

- save_key_file_exclusive: creates new file, rejects existing
- save_key_file: overwrites existing
- ensure_key_dir: creates nested dirs
- KeyringBackend: file/FILE/invalid parsing
- Race loser: syncs winner key to keyring
- Race loser: corrupt file gets overwritten

* feat(credential_store): security and robustness hardening

1. Warn on unrecognized KEYRING_BACKEND values instead of silent default
2. fsync after key file writes for crash durability
3. Zeroize decoded key material from heap after copy
4. Warn if key file has overly permissive Unix permissions (mode & 077)
5. Log which keyring backend was selected to stderr
6. Expose keyring_backend in 'gws auth status' JSON output

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-10 11:17:35 -06:00
Justin Poehnelt f0a38ac871 docs: remove obsolete notice
Removed note about temporarily disabling non-collaborator pull requests.
2026-03-09 12:44:06 -06:00
Shane Huntley 4d41e52198 fix(auth): prioritize local project configuration over global ADC for quota attribution (#295)
* fix(auth): prioritize local project configuration over global ADC for quota attribution

* chore: add changeset for project ID priority fix
2026-03-08 18:20:56 -06:00
Steve Bazyl dd3fc9074d fix!: Remove MCP server mode (#275)
* BREAKING CHANGE: Remove MCP server mode

* Add changeset file
2026-03-06 11:33:23 -07:00
Justin Poehnelt d6372105eb feat!: remove multi-account, DWD, and impersonation support (#253)
* feat!: remove multi-account, DWD, and impersonation support

BREAKING CHANGE: Remove domain-wide delegation, multi-account support,
and impersonation from the CLI authentication flow.

Removed:
- `gws auth list` and `gws auth default` commands
- `--account` flag from `gws auth login` and `gws auth logout`
- `GOOGLE_WORKSPACE_CLI_ACCOUNT` env var
- `GOOGLE_WORKSPACE_CLI_IMPERSONATED_USER` env var
- Per-account credential storage (accounts.json registry)
- Service account impersonation (subject/DWD)

Preserved:
- `GOOGLE_WORKSPACE_CLI_TOKEN` (raw access token)
- `GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE` (SA key path)
- `GOOGLE_WORKSPACE_CLI_CLIENT_ID` / `CLIENT_SECRET` (OAuth config)
- `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` (config dir override)

* chore: update changeset description

* docs: remove multi-account and DWD references from docs

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-05 22:43:43 -05:00
Steve Bazyl e1505afe12 chore: Remove dwd support (#250)
* chore: Remove dwd support

* Add changeset file

* chore: regenerate skills [skip ci]

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-05 19:26:27 -08:00
Justin Poehnelt a38f313d3c docs: add note 2026-03-05 19:15:15 -08:00
Justin Poehnelt 322529d8a9 fix: document all environment variables and enable CONFIG_DIR override (#222)
* docs: document all environment variables and enable CONFIG_DIR override (#171)

* docs: clarify env vars are trusted inputs in AGENTS.md

* chore: add Gemini Code Assist style guide

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-05 16:09:01 -08:00
Justin Poehnelt 0a16d0bca3 fix: allow services flag for login and improve default list for consumer accounts (#177)
* feat: add -s/--services flag to auth login for scope picker filtering

* fix: restrict recommended scope template to minimal consumer scopes

* refactor: address PR review comments — dedup parsing, extract helpers, use is_none_or
2026-03-05 08:14:54 -08:00
Mattia Righetti fa04502d14 chore(docs): easier cmd to install from source (#176)
* chore(docs): easier cmd to install from source

* Apply suggestion from @gemini-code-assist[bot]

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-05 08:10:25 -08:00
Justin Poehnelt 2aa6084e59 docs: improve readme especially auth which is complex no matter what (#136)
* docs: improve readme especially auth which is complex no matter what

* docs: add changeset for README overhaul

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-05 00:47:55 -08:00
Justin Poehnelt d1825f9385 feat: multi-account support (#85)
* feat: multi-account support with --account flag, per-account credential storage

- Add --account global flag and GOOGLE_WORKSPACE_CLI_ACCOUNT env var
- Per-account encrypted credential files (credentials.<b64-email>.enc)
- Per-account token cache (token_cache.<b64-email>.json)
- accounts.json registry with default account tracking
- New auth subcommands: list, default, per-account logout
- login_hint in OAuth URL for account pre-selection
- Email validation via Google userinfo after OAuth flow
- 12 new unit tests (380 total)

BREAKING CHANGE: Existing users must run 'gws auth login' again.
Credential storage changed from single credentials.enc to per-account files.

* refactor: Improve error handling for file system operations, rename `GWS_ACCOUNT` to `GOOGLE_WORKSPACE_CLI_ACCOUNT`, and refine service account token cache path generation.

* fix: clean up per-account token caches on logout

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-04 23:19:14 -07:00
Justin Poehnelt 805e037c74 docs: Remove logo image from README for reasons
Removed logo image from README and retained title and description.
2026-03-04 23:16:49 -07:00
Justin Poehnelt 1991d536b4 docs: add note about not official product more prominently (#115) 2026-03-04 22:45:22 -07:00
Steve Bazyl 37ab483840 feat: Add nix flake (#94) 2026-03-04 22:21:36 -07:00
Vedant Thapa a18d17374b fix: add github.com instead of just github in readme (#79)
* fix: add github.com instead of just github in readme

* Update README.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-04 18:52:32 -07:00
Justin Poehnelt 670267f6ec feat: add gws mcp server (#58)
* feat: add gws mcp server

Adds a new `gws mcp` subcommand that starts a Model Context Protocol
(MCP) server over stdio, exposing Google Workspace APIs as structured
tools to any MCP-compatible client.

- New `src/mcp_server.rs`: JSON-RPC stdio transport, handles
  `initialize`, `tools/list`, and `tools/call`
- Tool discovery dynamically builds schemas from Google Discovery Docs
- Filtering via `-s <services>` flag (e.g. `-s drive,gmail` or `-s all`)
- `-w/--workflows` and `-e/--helpers` flags for optional extras
- stderr startup warning when no services are configured
- Refactored `executor::execute_method` to support output capture
  (returns `Option<Value>` instead of printing to stdout) so the MCP
  transport is not corrupted
- Updated README.md with MCP Server section and usage examples

* fix: address PR review comments

- Add stderr warning when discovery doc fails to load (mcp_server.rs)
- Remove redundant 'all' string check in service validation (mcp_server.rs)
- Validate upload path to prevent arbitrary file reads - security fix (mcp_server.rs)
- Remove redundant inner capture_output check in handle_binary_response (executor.rs)
- Add changeset for minor version bump

* fix: resolve CI lint, fmt, and test failures

- cargo fmt: format all changed files
- clippy: add #[allow(clippy::too_many_arguments)] on private handle_json_response
- clippy: collapse else { if } to else if in executor.rs
- clippy: replace svc_name.clone() with std::slice::from_ref in mcp_server.rs
- clippy: replace index-based loop with iterator in walk path resolution
- test: add ::<()> turbofish annotation to handle_error_response test calls
  to fix E0282 type inference errors
2026-03-04 11:58:28 -07:00
Justin Poehnelt 3de97622a3 docs: fix gws setup → gws auth setup (fixes #56, #57) (#60)
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-04 11:57:16 -07:00
jpoehnelt-bot de2787e90f feat(error): detect accessNotConfigured and guide users to enable APIs (#33)
* feat(error): detect accessNotConfigured and guide users to enable APIs

When the Google API returns a 403 with reason accessNotConfigured,
gws now:
- Extracts the GCP Console enable URL from the error message.
- Adds an optional enable_url field to the JSON error output.
- Prints an actionable hint with the enable URL to stderr.

Also adds extract_enable_url() helper with tests, and a Troubleshooting
section to README.

Fixes #31

* fix(error): trim trailing punctuation from accessNotConfigured enable URL
2026-03-04 00:30:51 -07:00
jpoehnelt-bot f281797d07 docs(auth): add manual OAuth client and browser consent guidance (#29)
* docs(auth): add manual OAuth client/browser guidance

* docs(auth): use canonical Cloud Console URLs for OAuth consent/credentials

Update the OAuth setup section to use the canonical, stable Cloud Console
URLs as suggested in Gemini review (PR #29):
- /auth/overview → /apis/credentials/consent (matches the actual page title)
- /auth/clients  → /apis/credentials (more direct, avoids redirect)
2026-03-04 00:26:34 -07:00
Justin Poehnelt bdb458bd1c docs: update link 2026-03-03 18:39:29 -07:00
Justin Poehnelt e71ce292b2 fix: Gemini extension installation issue and update docs (#16)
* Fix Gemini extension installation issue and update docs

- Removed redundant authentication settings from `gemini-extension.json`.
- Added Gemini extension installation section to `README.md`.
- Added Gemini agent rules of engagement to `CONTEXT.md`.
- Added a changeset file for the patch.

Co-authored-by: jpoehnelt <3392975+jpoehnelt@users.noreply.github.com>

* docs: simplify gemini part

Removed installation section and optimization tips for gws in the README.

* fix: remove template sanitization settings from config

Removed settings related to template sanitization.

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: jpoehnelt <3392975+jpoehnelt@users.noreply.github.com>
2026-03-03 15:03:32 -08:00
Justin Poehnelt b0d0b95d07 feat: skills expansion (#18)
* wip

* feat: replace admin recipes with 50 consumer-focused recipes

- Remove all admin/security/IT recipes (offboard-user, audit-user-login, etc.)
- Remove enterprise-only recipes (initiate-litigation-hold)
- Replace dangerous recipes (setup-email-forwarding -> create-gmail-filter)
- Remove recipes overlapping with gws-workflow-* helpers
- Remove thin 2-step recipes better served as helpers
- Add 50 curated consumer recipes for Gmail, Drive, Docs, Calendar, Sheets
- Update README: link to docs/skills.md, update skill count to 100+
- Fix clippy needless_borrow warnings in generate_skills.rs
- Fix lefthook.yml: run fmt/clippy sequentially (parallel causes races)

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-03 15:02:34 -08:00
Justin Poehnelt d29f41ecf7 fix(docs): improve README typography and spacing (#14)
* fix(docs): improve README typography and spacing

- Remove center alignment for tagline and badges to match left-aligned body
- Add `<br>` after badges for visual separation before install block
- Increase whitespace above install block to emphasize it
- Add an empty line above `> [!IMPORTANT]` block to decouple it from previous paragraph

Co-authored-by: jpoehnelt <3392975+jpoehnelt@users.noreply.github.com>

* Update README.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* docs: improve README typography and layout

- Remove center alignment from tagline and badges
- Add space below badges
- Adjust spacing around install code block (remove borders, add empty line above)
- Add empty line above IMPORTANT callout

Co-authored-by: jpoehnelt <3392975+jpoehnelt@users.noreply.github.com>

* docs: refine copy after install code block

- Change "When Google adds an API endpoint" to "When Google Workspace adds an API endpoint or method" to be more accurate.

Co-authored-by: jpoehnelt <3392975+jpoehnelt@users.noreply.github.com>

* docs: remove horizontal borders from README

Per user request, removed all remaining Markdown horizontal rules (`---`) throughout `README.md` to create a cleaner, borderless design. All previous typography and spacing improvements remain intact.

Co-authored-by: jpoehnelt <3392975+jpoehnelt@users.noreply.github.com>

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: jpoehnelt <3392975+jpoehnelt@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-03 12:53:14 -08:00
Justin Poehnelt d990dcc119 docs: make README banner full-width and add changeset (#13)
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-03 12:55:03 -07:00
Justin Poehnelt a73ff26522 docs: improve readme 2026-03-03 11:59:56 -07:00
Justin Poehnelt 30d929b218 docs: fix gif, readme, etc (#5) 2026-03-03 09:11:29 -08:00
Justin Poehnelt 30484cec4d docs: Add development warning and update demo GIF link in README. 2026-03-02 17:32:15 -07:00
Justin Poehnelt f75bf6dcf7 feat: implement cli (#1) 2026-03-02 17:26:21 -07:00
Justin Poehnelt f6d850ceeb chore: init repo 2026-03-02 12:57:02 -07:00