Skip to main content

Portable Google Workspace CLI for humans and agents

Project description

suitewright

A portable Google Workspace CLI and workflow toolkit for humans and agents.

suitewright is a Python command-line tool for working with Google Workspace from a local machine, automation script, or AI agent runtime. It is framework-neutral and built around a file-and-cache-first workflow that both humans and agents can inspect.

Status: early standalone project (v0.0.1-rc). Commands and conventions may change.

Why suitewright

Most Google Workspace tools are either thin API wrappers, one-off scripts, or integrations tied to a specific agent framework. suitewright is designed to be:

  • Portable - works with uv, pip, a local .venv, or plain python3.
  • Agent-friendly - useful to any agent that can run Python and read or write files.
  • Human-friendly - usable as a normal CLI for everyday Workspace tasks.
  • Cache-first - encourages safe local inspection before mutation. Currently implemented for Forms; Docs cache-first support is planned.
  • Multi-service - covers Gmail, Calendar, Drive, Docs, Sheets, Contacts, and Forms.
  • Auth-aware - treats OAuth setup and local credentials as first-class parts of the workflow.

Installation

With uv:

uv tool install suitewright

With pip:

python3 -m pip install suitewright

From a checkout (development mode):

git clone https://github.com/glenkusuma/suitewright.git
cd suitewright
uv sync
uv run suitewright --help

Agent Skill

Install the suitewright skill for your AI coding agent via skills.sh:

npx skills add glenkusuma/suitewright

Authentication

suitewright uses local OAuth credentials on the machine where it runs.

Provide your Google OAuth client secret, complete browser-based consent once, and suitewright stores the resulting token locally. Run suitewright auth check at any point to see which path resolution mode is active and where files are being read from.

suitewright auth init --client-secret /path/to/client_secret.json
suitewright auth login
suitewright auth check

For headless or agent-driven flows, auth login accepts --auth-url and --auth-code as orthogonal flags so the consent step can be split across machines.

Configuration paths

Auth file resolution follows a 4-mode precedence (highest wins):

Priority Mode Source
1 env SUITEWRIGHT_TOKEN / SUITEWRIGHT_CLIENT_SECRET env vars
2 xdg $XDG_CONFIG_HOME/suitewright/auth/
3 dev SUITEWRIGHT_AUTH_DIR (default ../suitewright-auth relative to repo root)
4 default $HOME/.config/suitewright/auth/

Run suitewright auth check --json to see which mode is active. The output includes a "mode" field with one of: "env", "xdg", "dev", "default".

Cache resolution:

  • SUITEWRIGHT_CACHE_DIR env var (if set)
  • $XDG_CACHE_HOME/suitewright/ (default ~/.cache/suitewright/)

Dev mode and SUITEWRIGHT_AUTH_DIR

In development, auth files are read from SUITEWRIGHT_AUTH_DIR. This defaults to ../suitewright-auth relative to the detected repo root.

Repo-root auth/ is banned. Auth credentials must never live inside the repository tree. The _detect_dev_root() function does not check for auth/ at the repo root, and preflight scripts hard-fail if one exists. Use SUITEWRIGHT_AUTH_DIR to point to a directory outside the repo:

export SUITEWRIGHT_AUTH_DIR=/path/to/suitewright-auth
suitewright auth check --json  # confirms mode: "dev"

Command surface

suitewright auth          init | login | check | revoke | install-deps
suitewright gmail         search | get | send | reply | labels | modify | trash
suitewright calendar      list | create | delete
suitewright drive         search | get | upload | download | create-folder | share | delete
suitewright contacts      list
suitewright sheets        get | update | append
suitewright docs          get | show-structure | create | append | replace | update
                          request-template <kind>
                          replace-all | insert-table | insert-image | style-range
                          comments list | comments get | comments reply
                          table-get | table-update-cell | table-append-row
                          plan
suitewright forms         list | get | create | update
                          fetch | show-cache | validate | cache-update
                          query locate | query after | query delete-request
                          query get-item | query neighbors | query section | query indexer

Run any subcommand with --help for full flags.

Examples

Inspect a Doc safely before editing:

suitewright docs show-structure DOC_ID --full-text

Validate a batchUpdate payload without mutating:

suitewright docs update DOC_ID --dry-run --requests-file edits.json

Cache-first Forms workflow:

suitewright forms fetch FORM_ID
suitewright forms query locate FORM_ID --title "Question 1"
suitewright forms cache-update FORM_ID /path/to/changes.json

Drive round-trip with safer-by-default delete:

suitewright drive upload report.pdf --parent FOLDER_ID
suitewright drive get FILE_ID
suitewright drive delete FILE_ID                # moves to trash
suitewright drive delete FILE_ID --permanent    # explicit irreversible delete

Cache-first workflow philosophy

suitewright is designed around a cache-first pattern: fetch remote state once, inspect and plan locally, then apply a guarded mutation and refresh.

fetch live state
       ↓
write local cache artifact
       ↓
inspect / read / diff / patch locally
       ↓
compute safe edit plan
       ↓
apply guarded remote update
       ↓
refresh local cache

This pattern is currently implemented for Forms (forms fetch -> forms query -> forms cache-update). Docs cache-first support is planned for a future release.

Local artifacts can be opened in an editor, read by an agent, committed to a branch, diffed, validated, patched, or fed into a repeatable script.

Agent-friendly conventions

  • JSON output is the default for inspection commands so it's easy to pipe into jq or feed into another tool.
  • Mutation commands separate planning from applying. docs update --dry-run validates request shape; docs plan produces an inspectable plan artifact.
  • Auth and config paths are explicit and discoverable via auth check.
  • No hidden global state; everything resolves through paths.resolve() precedence.

Development

git clone https://github.com/glenkusuma/suitewright.git
cd suitewright
uv sync
uv run suitewright --help
uv build

Running Tests

Install dev dependencies and run the unit test suite (live tests are excluded by default):

uv sync
uv run pytest

Test layout:

tests/
├── unit/          # pure-function tests
├── integration/   # mocked-service full-path tests
├── scripts/       # helper script tests (scripts/docker.py)
└── live/          # real API tests (opt-in)

With coverage:

uv run pytest --cov=suitewright

Live tests

Live tests hit real Google APIs and require valid credentials. They are opt-in:

uv run pytest -m live --run-live

Markers available:

Marker Description
live Requires a real Google account; opt-in via --run-live
smoke Read-only sanity checks against live APIs
mutate Creates or modifies real resources (sandboxed)
destructive Sends, deletes, or otherwise acts irreversibly (sandboxed)

Linting and type checking

uv run ruff check .
uv run mypy
uv run bandit -r src/suitewright

Docker

A hardened Docker image is available for running suitewright in production or CI environments.

Hardened runtime

Run with a read-only filesystem, all capabilities dropped, and explicit tmpfs mounts:

docker run --rm \
  --read-only \
  --cap-drop=ALL \
  --security-opt=no-new-privileges \
  --tmpfs /home/suitewright/.cache/suitewright:uid=1000,gid=1000,mode=700 \
  --tmpfs /home/suitewright/runtime:uid=1000,gid=1000,mode=700 \
  --tmpfs /tmp:uid=1000,gid=1000,mode=700 \
  -v "${SUITEWRIGHT_AUTH_DIR}":/home/suitewright/.config/suitewright/auth:ro \
  suitewright:local --help

Auth mount

Auth files are mounted into the auth/ subdir only - never over the entire config directory. This limits the container's read access to credentials only:

-v /path/to/suitewright-auth:/home/suitewright/.config/suitewright/auth:ro

The source path is controlled by SUITEWRIGHT_AUTH_DIR. The mount is read-only (:ro).

docker-compose

The included docker-compose.yml configures both the runtime and test services with the same hardening. Auth is sourced from SUITEWRIGHT_AUTH_DIR:

SUITEWRIGHT_AUTH_DIR=/path/to/suitewright-auth docker compose up suitewright

Dev workflow script

A contributor-friendly wrapper script handles building the test image and running tests in Docker:

uv run python scripts/docker.py build              # build test image
uv run python scripts/docker.py test               # run unit/integration tests
uv run python scripts/docker.py test --live        # run live API tests (with preflight checks)
uv run python scripts/docker.py test -k test_auth  # forward args to pytest

License

Apache-2.0. See LICENSE and NOTICE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

suitewright-0.0.1rc1.tar.gz (32.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

suitewright-0.0.1rc1-py3-none-any.whl (43.6 kB view details)

Uploaded Python 3

File details

Details for the file suitewright-0.0.1rc1.tar.gz.

File metadata

  • Download URL: suitewright-0.0.1rc1.tar.gz
  • Upload date:
  • Size: 32.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for suitewright-0.0.1rc1.tar.gz
Algorithm Hash digest
SHA256 cc6b14b57b48525eef2cbe023afa7ad9ef13b59b2a462e4b51fd7ab5dc9d4a93
MD5 3fc1a9e4a11101344e9053ddc39ccab1
BLAKE2b-256 85104f3df66317f30c58fb4ec38027872218d46e42651f4066ae16252fb816cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for suitewright-0.0.1rc1.tar.gz:

Publisher: release.yml on glenkusuma/suitewright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file suitewright-0.0.1rc1-py3-none-any.whl.

File metadata

  • Download URL: suitewright-0.0.1rc1-py3-none-any.whl
  • Upload date:
  • Size: 43.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for suitewright-0.0.1rc1-py3-none-any.whl
Algorithm Hash digest
SHA256 97a36b4b6704ee552bba99f211e0130ad9085264e483e9179d0561d5666f6c1f
MD5 12d6a11d02094ddee2e53ac164126055
BLAKE2b-256 2651ce812e87860e74f1799dbd29cfdec1ceef278e5cd8a2e25273a1de7972e8

See more details on using hashes here.

Provenance

The following attestation bundles were made for suitewright-0.0.1rc1-py3-none-any.whl:

Publisher: release.yml on glenkusuma/suitewright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page