feat(gmail): add forward command#482
Conversation
Add `gog gmail forward` (alias `fwd`) to forward a Gmail message to new recipients with proper threading, original attachments, and forwarded message headers. The command fetches the original message in full format, constructs a new RFC 2822 MIME message with the forwarded content (plain text + HTML), downloads and re-attaches all original attachments, and sets threadId / In-Reply-To / References headers for proper Gmail thread grouping. Flags: --to, --cc, --bcc, --note, --note-file, --from, --skip-attachments Supports: --dry-run, --json, --plain output modes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 772193f818
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| origAtts := collectAttachments(origMsg.Payload) | ||
| for _, att := range origAtts { | ||
| data, dlErr := fetchAttachmentBytes(ctx, svc, messageID, att.AttachmentID) |
There was a problem hiding this comment.
Include inline-data attachments when forwarding
Forwarding currently builds attachment payloads only from collectAttachments(origMsg.Payload) and then refetches bytes by AttachmentID. That helper only surfaces parts with a non-empty attachmentId, so MIME parts where Gmail already provides bytes in part.body.data are skipped entirely. In those messages, gog gmail forward silently drops some original files even when --skip-attachments is not set.
Useful? React with 👍 / 👎.
| attachments = append(attachments, mailAttachment{ | ||
| Filename: att.Filename, | ||
| MIMEType: att.MimeType, | ||
| Data: data, |
There was a problem hiding this comment.
Preserve inline MIME metadata for HTML forwards
The forward path reconstructs attachments using only filename, MIME type, and raw data, which discards inline metadata (for example Content-ID/inline disposition). Because the original HTML body is forwarded verbatim, any message that uses cid: references will render broken images after forwarding: the parts are reattached as generic attachments instead of inline MIME parts.
Useful? React with 👍 / 👎.
Summary
gog gmail forward <messageId> --to <recipients>(aliasfwd) to forward a Gmail message to new recipientsthreadId,In-Reply-To, andReferencesheaders so the forward stays in the sender's Gmail thread--note/--note-filefor introductory text above the forwarded content--skip-attachmentsflag to omit original attachments--fromfor send-as alias support--dry-run,--json,--plainoutput mode supportFwd: Fwd:→Fwd:)Motivation
The Gmail API has no native forward endpoint — forwarding is done by constructing a new message via
messages.send. gog currently supports replies (--reply-to-message-id,--quote) but not forwards, which is a common workflow gap. This command fills that gap using the samebuildRFC822()andreplyInfoinfrastructure that replies already use.Test plan
TestExecute_GmailForward_Basic— verifies subject, forward separator, original headers in body, note text, original body content, threadId, In-Reply-To headerTestExecute_GmailForward_WithAttachments— verifies attachments are fetched and included in MIMETestExecute_GmailForward_SkipAttachments— verifies--skip-attachmentsprevents attachment fetchTestBuildForwardSubject— subject prefix handling (new, existing Fwd:, double Fwd:, Fw:, FWD:, empty, Re:)TestStripForwardPrefix— prefix stripping edge casesTestFormatForwardedMessage— plain text body construction with all fieldsTestFormatForwardedMessage_NoNote— no leading blank lines when note is emptyTestFormatForwardedMessageHTML— HTML body construction🤖 Generated with Claude Code