feat: --raw + --params for programmatic API-shape output (#188)#189
Conversation
Adds a programmatic mode for scripting Google Workspace via gws:
* --raw emits the unmodified Google API response JSON (no field renaming,
no base64 decoding, no header collapsing). Default ergonomic
output is unchanged when the flag is not set.
* --params <json> passes a JSON object whose keys map directly to the
underlying API request parameters. Keys override the
equivalent CLI flags (params win) — documented in
help/README.
Under --all, raw mode concatenates the top-level list field across pages
(messages / spaces / members) and drops nextPageToken from the final
output.
Coverage in this PR (per issue #188):
gmail list (users.messages.list — switches to messages.list
under --raw to match the API shape)
gmail thread <id> (users.threads.get — payload tree, headers
as {name,value} arrays, base64 body.data,
internalDate, labelIds, snippet)
chat spaces list (spaces.list)
chat members list (spaces.members.list)
chat messages list (spaces.messages.list)
people get (people.get)
New noun-verb command paths (chat spaces|messages|members list,
gws people get) live alongside the existing ergonomic commands; the
existing chat list / chat messages <id> / chat members <id> / contacts get
also gain --raw + --params for an easy upgrade path. No existing flags
or output shapes change.
Tests cover --raw output keys/structure for all six commands by
marshaling the SDK response structs and asserting against Google's
documented shape, plus --params parsing edge cases. All existing tests
continue to pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Summary: This PR adds programmatic What looks good: the raw paths generally preserve SDK JSON shapes, Issues found:
I could not run tests in this sandbox because the filesystem is read-only and Go could not create a build cache, even with |
Lint flagged it as unused (CI's golangci-lint fail). The runners pull the chat service through the existing factory in chat.go before branching into the raw paths, so the helper was dead code. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SummaryThis PR adds programmatic What Looks Good
Issues FoundCritical: Warning: raw command behavior is not covered by httptest runner tests. Warning: skill reference docs were not updated. Warning: Gmail I could not run tests in this environment because the filesystem is read-only, including |
Codex review findings:
1. Critical: --all in raw chat runners was silently capped by --max
default (25 for messages, 100 for members). When --all is set we now
ignore --max (0 = unlimited). Same guard added to chat spaces raw
for symmetry.
2. Warning: printRaw bypassed the global --quiet contract. It now
short-circuits when quiet is set, matching GetPrinter's NullPrinter.
3. Warning: --params maxResults was not mapped directly to the Gmail
API parameter under --all (always sent the API's 500 max). It now
maps directly: --params maxResults is the per-page parameter; --max
stays the total-results cap. --all drops --max for symmetry with
the chat fix.
4. Warning: tests didn't exercise the Cobra runners. Added
cmd/raw_runners_test.go covering httptest-driven runs:
* gmail list raw: --all aggregates, drops nextPageToken,
--params overrides flag-derived query/page size
* gmail list raw: --quiet suppresses output
* gmail thread raw: --params format passes through
* chat spaces list raw: --all aggregates, --params overrides
* chat messages list raw: --all ignores 25-default cap
(regression test for the critical bug above)
* chat members list raw: --all ignores 100-default cap
* people get raw: resourceName/personFields passthrough
5. Warning: skill references didn't mention the new commands/flags.
Updated skills/gmail/references/commands.md, skills/chat/...,
skills/contacts/... with the new flags, the spaces|messages|
members list aliases, and the new `gws people get`.
6. README command tables now list --raw/--params alongside the
relevant rows and a new "People" section.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Summary: This PR adds What Looks Good: The PR mostly follows existing Cobra registration patterns, keeps raw runners separate from ergonomic output, adds httptest coverage for core raw runner behavior, and updates the visible command docs for the new surfaces. Issues Found: Warning: Raw single-page output is not actually verbatim when Warning: Verification: I attempted |
1. Raw mode is verbatim by default. The CLI's --max flag would
previously slice a verbatim API response (e.g. --params
maxResults=50 with default --max=10 trimmed to 10). Each raw
runner now only honors --max when the caller explicitly set it
(cmd.Flags().Changed("max")); the default is no cap. --all still
disables --max for "fetch every page" semantics.
Affected: runGmailListRaw, runChatListRaw, runChatMessagesRaw,
runChatMembersRaw (and their httptest cases updated).
2. chat messages --after / --before were silently dropped under
--raw because the raw dispatch happened before they were folded
into the filter expression. Moved the filter combination above
the raw dispatch so both code paths see the same query.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Summary: The PR adds What Looks Good: The new raw runners generally follow existing Cobra registration and Issues Found:
Verification: I could not run |
1. runPeopleGet now has a real httptest runner test. Refactored
runPeopleGet into a thin wrapper around runPeopleGetWithSvc
(takes *people.Service) so we can drive it against an httptest
backend without OAuth. New tests cover:
- --params resourceName/personFields override positional args
and CLI flags
- --person-fields flag is the fallback when --params has no
personFields
- missing resourceName produces an explicit error
The previous "test" only exercised svc.People.Get directly, not
the runner — codex was right to call that out.
2. parseParams now rejects trailing junk after the JSON object.
Previously '{"pageSize":50} garbage' silently parsed the first
object; now it errors with "unexpected trailing data".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SummaryThe PR adds What Looks Good
Issues FoundWarning:
|
1. --all on existing chat messages/members commands was exposed but
ignored in ergonomic mode. The new noun-verb list commands honor
--all under --raw; the ergonomic commands intentionally don't.
Updated help text so the flag's scope is unambiguous:
chat messages --all → "Raw mode only: ..."
chat members --all → "Raw mode only: ..."
chat list --all → notes that --max=0 already returns all
in ergonomic mode
2. contacts get docs mentioned `--params resourceName` but the
command still had ExactArgs(1), so the positional was mandatory
regardless. Relaxed to `[resource-name]` / MaximumNArgs(1) so
the documented `--params resourceName` actually replaces the
positional. Updated the existing TestContactsCommands assertion
and the contacts skill reference to match.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Summary: This PR adds What looks good: The raw runners use SDK response structs, cover pagination aggregation with Issues found:
Verification: I could not run |
1. gmail thread relaxed to [thread-id]: the docs/runner advertised --params id as an override, but ExactArgs(1) required the positional regardless. Now MaximumNArgs(1); runGmailThread passes through to runGmailThreadRaw under --raw, and the raw runner errors with an explicit message if neither positional nor --params id is set. Matches the contacts get / people get pattern from round 4. Tests + skill ref updated. 2. Version bumped to 1.39.0 per CLAUDE.md "Implementation Patterns" convention: feature PRs adding new commands bump VERSION. Also updated the "Current Version" line in CLAUDE.md with the v1.39 release summary describing --raw/--params and the new noun-verb paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Summary: This PR adds What Looks Good
Issues Found
Verification
|
1. Chat raw runners clamp page size to remaining --max budget. The previous code requested the default pageSize (100), then sliced the response. That left nextPageToken pointing past item 100 even though we returned items 1-N, so a client continuing pagination would skip the items we sliced off. Now: when --max is set and !fetchAll, pageSize is clamped to (remaining-budget). Verbatim nextPageToken now stays aligned with the returned slice. New TestChatListRaw_ClampsPageSizeToMax test covers the bug. 2. `people` is now a recognized auth service alias for `contacts`. The new `gws people get` command needs the same People API scopes as `gws contacts`. Users can now run `gws auth login --services people` and scoped-auth warnings can refer to either name interchangeably. ServiceForScope iterates a deterministic order so `contacts` remains canonical when a scope maps to both aliases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Summary: This PR adds What Looks Good: The new raw paths generally preserve SDK/API JSON shape, add httptest-based runner coverage, and update README plus Issues Found:
I could not run the Go tests because the sandbox is read-only and Go could not create a build cache under |
1. Gmail raw clamps perPage to remaining --max budget unconditionally.
Previously the clamp only applied when --params maxResults was
absent, so e.g. --max 5 --params '{"maxResults":100}' would
request 100 from the server, slice to 5, and emit a
nextPageToken positioned after item 100 — a client continuing
pagination skipped messages 6-100. --max is now always the
authoritative output cap; nextPageToken stays aligned.
2. Existing chat messages / chat members commands relaxed from
ExactArgs(1) to MaximumNArgs(1). They already had --raw + --params
wired, but Cobra rejected the invocation before the runner could
read --params parent. Now passing `--params '{"parent":"spaces/.."}'`
works on the existing commands too, matching the contacts get /
gmail thread / people get pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Summary: This PR adds What Looks Good: The new Chat noun-verb command paths are registered and covered in Issues Found:
I could not run |
1. parseParams trailing-junk detection: switched from dec.More()
to an explicit second-Decode-must-return-io.EOF check. The
existing test passed locally — dec.More() does work for the
top-level streaming case — but the new idiom doesn't rely on
subtle decoder semantics and is what every reader expects.
Codex marked this critical even though the test passed; the
new code is unambiguously correct.
2. Raw runner errors now go through GetPrinter().PrintError so
scripts get the project's structured error shape
({"error": "..."}) instead of Cobra-style stderr. Applied to
runGmailListRaw, runGmailThreadRaw, runChatListRaw,
runChatMessagesRaw, runChatMembersRaw, runPeopleGet, and
runPeopleGetWithSvc. Updated the corresponding runner test
that previously asserted on the returned error directly.
3. README command tables now show optional positional args for
commands that accept the id via --params:
gws gmail thread [id]
gws chat messages [space]
gws chat members [space]
gws contacts get [resource-name]
…matching the skill references and the actual Cobra Use lines.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Summary What Looks Good
Issues Found
Verification |
1. Validate resource ids before auth/factory for optional-positional commands. Previously `gws gmail thread` (no id, no --params id) hit OAuth/config errors before reporting the missing id. Now each affected runner parses --params and resolves the id from positional or params, validates non-empty, and only then creates the client. Affected runners: runGmailThread, runContactsGet, runPeopleGet, runChatMessages, runChatMembers. 2. README pagination doc now lists the correct API field for chat members: the response field is `memberships`, not `members` (the code/tests already used the right name; only the doc was off). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Summary: The PR adds What Looks Good: Command wiring follows existing Cobra patterns, raw paths have focused httptest coverage, README and skill command references were updated, and raw pagination behavior is covered for Issues Found:
No critical issues found. I did not run the test suite in this read-only review environment. |
Factory scope check now treats `people` and `contacts` as alias services. Previously `gws auth login --services people` granted the right OAuth scopes (via the auth.ServiceScopes alias added in round 6), but `factory.People()` still warned "contacts requires additional permissions" because the check looked for an exact name match in grantedServices. Introduced a small serviceAliases map + canonicalService() helper so any future aliases resolve through the same path. Test added. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Summary: What Looks Good: Issues Found: Verification: |
Closes #188.
Summary
Adds two new flags for scripting / automation consumers:
--rawemits the unmodified Google API response JSON (no field renaming, no base64 decoding, no header collapsing).--params <json>passes a JSON object whose keys map directly to the underlying API request parameters. Precedence:--paramswins over the equivalent CLI flags.Under
--all, raw mode concatenates the top-level list field across pages (messages/spaces/memberships) and dropsnextPageTokenfrom the final output.Default ergonomic output is unchanged when
--rawis not set. All existing tests continue to pass.Coverage in this PR
gmail listusers.messages.list(switches under--raw)gmail thread <id>users.threads.getchat spaces list(new path)spaces.listchat members list(new path)spaces.members.listchat messages list(new path)spaces.messages.listpeople get(new top-level command)people.getExisting leaf commands (
chat list,chat messages <id>,chat members <id>,contacts get) also accept--raw+--paramsfor an easy upgrade path. Out of scope per the issue: extending to other commands.Hard requirement → file map
--rawflag —cmd/raw.go(addRawParamsFlags,isRaw,printRaw,writeRaw). Skips the transform layer; SDK structs marshal with Google's JSON tags directly.--params <json>—cmd/raw.go(parseParams,paramString,paramInt64,paramBool,paramStringSlice). Precedence (params win) is documented in the flag help and inREADME.md.--allunder--rawpreserves API shape — concatenates the list field and dropsnextPageToken. SeerunGmailListRawincmd/gmail.goand the three raw runners incmd/chat_raw.go.gmail listandgmail threadincmd/gmail.go;chat spaces|messages|members listincmd/chat_raw.go(plus existing leaves wired incmd/chat.go);people getincmd/people.go.cmd/raw_test.gosnapshot-tests--rawoutput keys/structure for all six commands by marshaling the SDK response structs and asserting against Google's documented shape (headersas[{name,value}], base64body.datapreserved inparts[*],internalDate/labelIds/snippetpresent, camelCase top-level keys, etc.) and covers--paramsparsing (invalid JSON, mixed types, slice forms). New command surfaces are also asserted incmd/commands_test.go.--rawand--paramsare additive. Precedence rule:--paramskeys win over CLI flags.Verification
Sandbox in my environment blocks live Workspace API calls, so I could not paste live output. The expected shape is asserted by the shape tests in
cmd/raw_test.go. Please run the canonical set against your account and paste truncated output:Test plan
go test ./...— all packages pass.go vet ./...clean,gofmt -l cmd/empty../bin/gws people get --help/chat spaces list --help/chat messages list --helprender the new flags & examples.🤖 Generated with Claude Code