crates/google-workspace-cli/src/helpers/README.md
+verb) — GuidelinesThe core design of gws is schema-driven: commands are dynamically generated from Google Discovery Documents at runtime. This avoids maintaining a hardcoded, unbounded argument surface. Helpers must complement this design, not duplicate it.
A +helper command should exist only when it provides value that Discovery-based commands cannot:
| Justification | Example | Why Discovery Can't Do It |
|---|---|---|
| Multi-step orchestration | +subscribe | Creates Pub/Sub topic → subscription → Workspace Events subscription (3 APIs) |
| Format translation | +write | Transforms Markdown → Docs batchUpdate JSON |
| Multi-API composition | +triage | Lists messages then fetches N metadata payloads concurrently |
| Complex body construction | +send, +reply | Builds RFC 2822 MIME from simple flags |
| Multipart upload | +upload | Handles resumable upload protocol with progress |
| Workflow recipes | +standup-report | Chains calls across multiple services |
Litmus test: Can the user achieve the same result with gws <service> <resource> <method> --params '{...}'? If yes, don't add a helper.
If a helper wraps one API call that Discovery already exposes, reject it.
Real example: +revisions (PR #563) wrapped gws drive files-revisions list — same single API call, zero added value.
Adding flags to expose data that is already in the API response creates unbounded surface area.
Real example: --thread-id, --delivered-to, --sent-last on +triage (PR #597) — all three values are already present in the Gmail API response. Agents and users should extract them with --format or jq, not new flags.
Why this is harmful: Every API response contains dozens of fields. If we add a flag for each one, helpers become unbounded maintenance burdens — the exact problem Discovery-driven design solves.
Don't re-expose Discovery-defined parameters (e.g., pageSize, fields, orderBy) as custom helper flags. Use --params passthrough instead.
Helper flags must control orchestration logic, not API parameters or output fields.
| Flag | Helper | Why It's Good |
|---|---|---|
--spreadsheet, --range | +read | Identifies which resource to operate on |
--to, --subject, --body | +send | Inputs to MIME construction (format translation) |
--dry-run | +subscribe | Controls whether API calls are actually made |
--subscription | +subscribe | Switches between "create new" vs. "use existing" orchestration path |
--target, --project | +subscribe | Required for multi-service resource creation |
| Flag | Why It's Bad | Alternative |
|---|---|---|
--thread-id | Already in API response | jq '.threadId' |
--delivered-to | Already in response headers | `jq '.payload.headers[] |
--include-labels | Output field filtering | --format or jq |
--format/jq--paramsHelpers are implemented using the Helper trait defined in mod.rs.
inject_commands: Adds subcommands to the main service command. All helper commands are always shown regardless of authentication state.handle: Implementation of the command logic. Returns Ok(true) if the command was handled, or Ok(false) to let the default raw resource handler attempt to handle it.crate::client::build_client() for HTTPcrate::validate::validate_resource_name() for user-supplied resource IDscrate::validate::encode_path_segment() for URL path segmentscrate::output::sanitize_for_terminal() for error messages--dry-run where the helper creates or mutates resourcessrc/helpers/<service>.rsHelper traitsrc/helpers/mod.rs+ (e.g., +create)