Add export-attachment and export-attachments CLI commands, consolidate shared export logic#56
Merged
Add export-attachment and export-attachments CLI commands, consolidate shared export logic#56
Conversation
Add CLI command to export attachment binaries by content hash. Users can get the hash from 'show-message --json' and extract attachments without parsing raw MIME/EML files. Supports multiple output modes: - Binary file output (-o file.pdf) - Binary stdout (-o - or default) - Base64 stdout (--base64) - JSON with embedded base64 (--json) Resolves the need for programmatic attachment access without external MIME parsing dependencies.
- Use export.ValidateContentHash() instead of inline hex validation - Stream binary output with io.Copy instead of loading entire file into memory via os.ReadFile, supporting large attachments - Stream base64 output with base64.NewEncoder wrapping stdout - Add flag mutual exclusivity validation (--json/--base64 vs --output) - Extract functions for testability: exportAttachmentAsJSON, exportAttachmentAsBase64, exportAttachmentBinary, openAttachmentFile, readAttachmentFile - Write output files with 0600 permissions (was 0644) - Add unit tests covering binary/JSON/base64 output, missing files, flag validation, and hash validation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add export-attachments CLI command that exports all attachments from a message as individual files. Core logic (AttachmentsToDir, CreateExclusiveFile, StoragePath) lives in internal/export so it can be reused by TUI and MCP code paths. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove duplicated sanitizeFilename, createExclusive, and pathConflict from the MCP handler. Use export.SanitizeFilename, export.CreateExclusiveFile, export.ValidateContentHash, and export.StoragePath instead. Also use StoragePath in addAttachmentToZip for consistency. This eliminates three code paths (TUI, CLI, MCP) each implementing their own filename sanitization and exclusive file creation logic. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ests - StoragePath now returns (string, error) with internal hash validation, preventing panics from short/empty hashes (#4466-2, #4467-1) - Fix misleading comment: only JSON reads full file into memory; base64 and binary stream (#4465-1) - Fix base64 test to use io.ReadAll instead of fixed 4096-byte buffer (#4465-2) - Add 5 integration tests for export-attachments CLI: full flow, Gmail ID fallback, message not found, output dir validation, not-a-directory (#4466-3) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…consistency - Reject --json + --base64 when both set (was silently preferring JSON) - Clean up partial output file on write/close error in exportAttachmentBinary - Standardize MCP export file permissions from 0644 to 0600 for consistency - Add test case for --json + --base64 mutual exclusivity Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
export-attachmentcommand to export single attachment binaries by content hash (supersedes feat: add export-attachment command #4)export-attachmentscommand to export all attachments from a message as individual filesinternal/exportso TUI, CLI, and MCP share one code pathShared export package (
internal/export)AttachmentsToDir()— export attachments as individual files to a directory (streaming I/O, deduped filenames,O_EXCLfile creation)CreateExclusiveFile()— atomic file creation with_1,_2suffix on conflictStoragePath()— content-addressed path construction with hash validationValidateContentHash(),SanitizeFilename()— already existed, now used by all code pathsMCP consolidation
sanitizeFilename,createExclusive,pathConflictfrom MCP handler (~50 lines)export.SanitizeFilename,export.CreateExclusiveFile,export.StoragePathCloses #3
Test plan
make test && make lintpassinternal/export: 8TestAttachmentsToDirsubtests,TestCreateExclusiveFile(4 subtests),TestAttachmentsToDir_FilePermissions,TestAttachmentsToDir_DiskConflictcmd/.../export_attachment_test.go: binary, JSON, base64 output modes; missing file; flag exclusivity; hash validationcmd/.../export_attachments_test.go: full flow with real DB, Gmail ID fallback, message not found, output dir validation, not-a-directoryTestSanitizeFilenameupdated to use shared functionmsgvault export-attachments <id>with real message🤖 Generated with Claude Code