* 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>
* feat(docs): add tab support for Google Docs
Add support for Google Docs tabs (multi-tab documents):
- `docs list-tabs <docId>`: List all tabs with ID, title, and index
- `docs cat <docId> --all-tabs`: Print all tabs with headers
- `docs cat <docId> --tab <title|id>`: Print a specific tab by title or ID
Key behaviors:
- Default `docs cat` (no flags) is fully backward compatible
- Tab lookup supports both exact ID match and case-insensitive title match
- Nested/child tabs are flattened and included in all operations
- JSON output supported for all tab operations
Uses the Google Docs API `includeTabsContent` parameter to retrieve
tab-aware document content via `Document.Tabs`.
Closes#125
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(docs): add write, insert, delete, find-replace commands
Port custom editing commands from fork to upstream architecture:
- write: append or replace doc content (plain text or markdown)
- insert: insert text at specific character index
- delete: delete text range by start/end index
- find-replace: find and replace text with optional case matching
Also fix lint issues: err shadow in tab support code, goconst for
string literal "true".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(docs): harden tab/write paths and add editing regressions (#225) (thanks @alexknowshtml)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Matt's security review caught that URLs like '?foo=bar' would incorrectly
trigger QP detection because '=ba' matches as hex digits.
Changed to only match UPPERCASE hex (0-9, A-F) since:
- RFC 2045 recommends uppercase for QP encoding
- Most encoders use uppercase in practice
- This avoids false positives from lowercase URL params
When Gmail API returns already-decoded content (format=full), the
Content-Transfer-Encoding header may still say 'quoted-printable'.
Previously, we would attempt to QP-decode again, causing raw '='
characters in URLs to be replaced with U+FFFD (replacement char).
This adds looksLikeQuotedPrintable() to detect actual QP sequences
(=XX hex or =CRLF soft breaks) and skip decoding when content appears
pre-decoded.
Fixes#159
* feat(slides): add `slides add-slide` command for full-bleed image slides
Adds a new `gog slides add-slide <presentationId> <image>` command that
appends a slide with a full-bleed image and optional speaker notes using
the native Google Slides API (presentations.batchUpdate).
Workflow: create a deck with `gog slides create`, then add slides one at
a time with this command. Supports --notes for inline text and
--notes-file for multiline markdown speaker notes.
Also registers ServiceSlides in the auth layer with proper scopes and
readonly support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(slides): add list-slides and delete-slide commands
list-slides shows all slide object IDs in a presentation.
delete-slide removes a slide by its object ID.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(slides): add --before flag to add-slide for insertion ordering
Allow inserting a slide before a specific existing slide ID instead of
always appending to the end of the presentation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(slides): add read-slide, update-notes, and replace-slide commands
Adds three new commands to support in-place editing of existing slides:
- `slides read-slide` — shows slide content including speaker notes,
text elements, and image references (supports --json output)
- `slides update-notes` — updates speaker notes on an existing slide
without deleting/re-adding (--notes or --notes-file)
- `slides replace-slide` — atomically swaps the image on an existing
slide using the ReplaceImage API, optionally updating notes in the
same batch operation
These eliminate the error-prone delete+add-before workflow when editing
slides in existing decks.
Closes chrismdp/gogcli#1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(slides): rebase PR 214, clear notes semantics, and hard-fail missing placeholders (#214) (thanks @chrismdp)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat: add docs update command for editing Google Docs
* fix: handle document content range correctly for replace
* docs: add Jarbas avatar
* feat(gogcli): add markdown formatting support for Google Docs
Phase 1 & 2 complete:
- Markdown parser supporting headings, lists, code blocks, blockquotes, links
- Google Docs API integration for formatting
- --format markdown flag for docs update command
- Heading styles (H1-H6), horizontal rules, list indentation
- Code blocks with monospace font
Pending (Phase 3):
- Inline formatting (bold, italic, inline code) - index calculation issues
- Links - index calculation issues
Usage:
gog docs update <docId> --content-file ./doc.md --format markdown
* fix(gogcli): fix inline formatting indices in markdown formatter
- Simplified document generation to avoid index calculation errors
- Fixed ParseInlineFormatting to correctly track positions
- Preserves: headings, code blocks, blockquotes, lists, horizontal rules
Pending: inline formatting (bold, italic, code, links) - indices still need work
* fix(gogcli): use UTF-16 code units for Google Docs API indexing
- Fixed markdown formatter to use UTF-16 code units instead of UTF-8 bytes
- Added utf16Len() helper function for accurate character counting
- Fixed inline formatting indices (bold, italic, code, links)
- Added empty line handling (MDEmptyLine)
- Successfully tested with Docker course doc (21KB, emojis, diagrams)
This resolves index mismatch errors caused by multi-byte characters like emojis
which are 4 bytes in UTF-8 but 2 code units in UTF-16.
* feat(gogcli): add slides commands with markdown support
- Add 'gog slides update' command with markdown formatting
- Create slides_formatter.go for Google Slides API batch updates
- Create slides_markdown.go for markdown parsing (titles, bullets, code)
- Add slides.go with update/create/read operations
- Update googleauth service for Slides scope
Related: PR #219
* fix(gogcli): use shapes for slides text boxes instead of direct insertion
- Fixed slides creation to use CreateShape with TEXT_BOX instead of inserting text directly
- Direct text insertion into slides is not supported by Google Slides API
- Added title text box with bold 36pt font
- Added body text box for content (bullets, paragraphs, code)
- Supports markdown formatting (bold, bullets, code blocks)
Tested: Successfully created 20-slide presentation from Docker course outline
* feat: add markdown table support (formatted text output)
* feat: implement native Google Docs table insertion with multi-step API
* feat(slides): add --template flag for creating presentations from templates
* fix: stabilize docs/slides markdown + auth flow (#219) (thanks @goncaloalves)
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Upload a Markdown file to Google Drive with auto-conversion to native
Docs format. Images are handled in a second pass: markdown image
references are replaced with placeholders during upload, then the
created document is read back via the Docs API to locate placeholder
positions and replace them with inline images (InsertInlineImage).
Local images are uploaded to Drive temporarily with public read access
(required by the Docs API), then cleaned up after insertion. Remote
URLs (http/https) are used directly. Path traversal is prevented by
resolving symlinks and verifying images stay within the markdown
file's directory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.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>
- Remove unused wrapEventWithDays and printCalendarEvent functions
- Extract printEventAttendees and printEventReminders helpers
to reduce printCalendarEventWithTimezone complexity from 51 to ~40
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>