Adds `gog slides create-from-template` command to create presentations
from templates with automatic placeholder text replacement.
Features:
- {{key}} placeholder format (auto-wrapped)
- Multiple replacement sources: --replace flags and --replacements JSON file
- Type conversion for JSON (numbers, booleans → strings)
- --exact flag for arbitrary string replacement
- Replacement statistics in output
- JSON/text output modes
Implementation:
- Copy template via Drive API
- Batch text replacement via Slides API ReplaceAllText
- Comprehensive error handling and validation
- 7 test cases covering all features and edge cases
Usage: gog slides create-from-template <templateId> <title> \
--replace "key=value" \
--replacements data.json
Co-authored-by: Cursor <cursoragent@cursor.com>
Google added native text/markdown export support for Google Docs in
July 2024. This wires up the existing Drive export pipeline to support
--format md, leveraging Google's server-side conversion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: brew official formula
* docs(install): note official Homebrew formula (#361) (thanks @zeldrisho)
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(gmail-watch): delay history fetch in watch serve
* fix: land gmail watch fetch delay and changelog (#397) (thanks @salmonumbrella)
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Add paragraph-number addressing (5d, 3s/.*/text/, $a/text/) and
`docs structure` / `docs cat -N` commands for paragraph-level
document manipulation.
New commands:
- `docs structure` — numbered paragraph list with types (text + JSON)
- `docs cat -N` — cat with [N] paragraph prefixes
Address syntax for `docs sed`:
- Nd (delete paragraph N), N,Md (range delete), $d (last)
- Ns/pat/repl/ (substitute within paragraph N)
- Na/text/ (append after N), Ni/text/ (insert before N)
- --tab flag for multi-tab document support
Testing:
- 24 new unit tests covering parseAddress (13 cases),
parseFullExpr_Addressed (10 cases), resolveAddress (6 cases),
and buildParagraphMap (8 cases). All pass.
- Manual testing against live Google Docs verified: structure,
cat -N, addressed substitute, delete, append, insert, dollar
addressing, and range delete.
- Bug found and fixed during manual testing: addressed a/text/
and i/text/ were parsed by parseAICommand (expects a/pat/text/)
which put text in the pattern field instead of replacement,
producing empty paragraphs. Added parseAddressedAICommand for
the single-field addressed form.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(gmail): decode ISO-2022-JP bodies
* fix(gmail): include cc/bcc in get output
* feat(calendar): allow selecting calendars in events
* test(gmail): add edge case tests for ISO-2022-JP decoding
Add tests for edge cases in ISO-2022-JP body decoding:
- Mixed ASCII and Japanese text (e.g., "Hello こんにちは World")
- Empty content with ISO-2022-JP charset header
- Malformed ISO-2022-JP sequences (graceful degradation)
- Truncated escape sequences
These tests verify the graceful fallback behavior in decodeBodyCharset
which returns original data if decoding fails.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(calendar): validate unknown calendar names in resolveCalendarIDs
When a calendar name doesn't match any known calendar (not in bySummary
or byID maps), return an error listing the unrecognized names instead
of treating them as raw calendar IDs which causes cryptic Google API
errors.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(calendar): validate unknown and ambiguous calendar name resolutions
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(cli): improve agent ergonomics
* fix(cli): address code review findings
- Fix nil pointer dereference in confirmDestructive when flags is nil
- Deduplicate dry-run logic by delegating to dryRunExit
- Remove deprecated net.Error.Temporary() call (dead since Go 1.18)
- Add unit tests for resolveTasklistID and resolveCalendarID
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve PR #201 conflicts and follow-ups (#201) (thanks @salmonumbrella)
* fix: resolve rebase fallout for PR #201 landing (#201) (thanks @salmonumbrella)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Add Drive upload conversion with auto detection (--convert) and explicit targets (--convert-to), preserve explicit --name values, and improve conversion behavior tests/docs for PR #240.
Co-authored-by: Daniel Weber <daniel@suno.com>
* fix(gmail): fallback to send-as list for display name
* refactor(gmail): remove dead code in primarySendAsDisplayNameFromList
The condition `primary == nil && sa.IsPrimary` inside the email-matching
block can never be true because `primary` is already unconditionally set
to `sa` when `sa.IsPrimary` is true earlier in the same loop iteration.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(gmail): add --from display name fallback to list test
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(auth): persist manual oauth state
* feat(cli): add remote manual auth flow
* fix(auth): enforce remote manual auth state
* fix(auth): satisfy lint for manual auth flow
* fix(auth): harden remote manual auth state cache
* chore: update changelog for remote manual auth (#187) (thanks @salmonumbrella)
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(drive): add --domain flag to share command
Allow sharing files/folders with an entire Google Workspace domain
(e.g. `gog drive share <id> --domain=example.com --role=writer`).
This creates a "domain" type permission in the Drive API, enabling
the "Anyone in <org> with the link" sharing mode.
Also updates `permissions` output to display domain names.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(drive): add --to target for share (#192) (thanks @Danielkweber)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(gmail): add timezone support for date output
Add --timezone and --local flags to gmail search and watch serve commands
to control how dates are displayed. By default uses local timezone.
Users can specify any IANA timezone (e.g., America/New_York, UTC).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(gmail): fix execute test for timezone changes
Update TestExecute_GmailSearch_JSON to use explicit --timezone UTC flag
and expect the correctly converted time (22:04 UTC from 15:04 -0700).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(gmail): add -z short flag for --timezone
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(gmail): add unit tests for resolveOutputLocation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs(gmail): clarify timezone flag help text
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(gmail): add timezone conversion tests for formatGmailDateInLocation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(config): add default_timezone setting
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(gmail): support GOG_TIMEZONE env var and config for timezone
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(config): add gog config command for timezone and settings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(calendar): respect GOG_TIMEZONE and config for timezone
Update calendar time command to follow the same timezone priority as gmail:
1. --timezone flag (explicit)
2. GOG_TIMEZONE env var
3. Config file default_timezone
4. Fall back to Google Calendar's timezone (calendar-specific fallback)
Add getConfiguredTimezone helper that returns nil when no explicit timezone
is configured, allowing calendar commands to use their own fallback behavior.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(gmail): add tests for GOG_TIMEZONE env var and getConfiguredTimezone
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(config): warn on invalid timezone instead of error
When default_timezone in config is invalid, print a warning to stderr
and fall back to local timezone instead of returning a hard error.
Invalid flag/env var still returns errors (user mistake in current
session), but invalid config should not break the CLI (stale config).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor(gmail): remove unused formatGmailDate, add nolint directive
- Remove unused formatGmailDate wrapper function (all callers use
formatGmailDateInLocation directly with explicit location)
- Add nolint:nilnil directive to getConfiguredTimezone with explanation
that nil return signals caller to use its own fallback
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(timezone): honor --timezone local
* refactor(cli): unify config/timezone helpers
* test(cmd): isolate config env in timezone tests
* refactor(config): centralize config key metadata
* fix(config): drop unused key spec lookup
* fix(config): standardize config key errors
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
- Add comprehensive unit tests for classroom helper functions
(date/time parsing, error wrapping, profile helpers)
- Add profileEmail() helper for nil-safe email extraction
- Fix nil pointer guards for Profile access (12 locations in
courses, rosters, and guardians)
- Add --topic filter for coursework and materials list commands
- Update docs/spec.md with new filter options
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>