mailmate 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +13 -5
- data/lib/mailmate/cli/search.rb +58 -11
- data/lib/mailmate/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 52941d76f7875e2944527026ed3d8584eb15e718dded7c7db5b75cad880be7d2
|
|
4
|
+
data.tar.gz: bb5f9463d23b07f6b6755c1c1c54326a060b9bd5680554399ff97f23137f7b6c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ac99eb6fee19f727a953228a599f5ec29d741339776388547ff514403a838c990635c586881dfee5750c3a0c6a72f6c66bdcc513f3dba4ed85eb0e980b588357
|
|
7
|
+
data.tar.gz: 6a293bf66d563b80d9f9a0e2f0808fa32db6ba4bd40bea65fbb364a73e0fbef3d0aa715b284ad07f464a7cbb695825da4d2f614903699f1d3e527101e7135edc
|
data/README.md
CHANGED
|
@@ -108,12 +108,8 @@ Pre-1.0 (0.x). Breaking changes allowed without version bumps. See [`docs/roadma
|
|
|
108
108
|
|
|
109
109
|
## Install
|
|
110
110
|
|
|
111
|
-
For development (no `gem install` needed):
|
|
112
|
-
|
|
113
111
|
```bash
|
|
114
|
-
|
|
115
|
-
echo 'export PATH="$HOME/code/claude/mailmate/exe:$PATH"' >> ~/.zshrc
|
|
116
|
-
source ~/.zshrc
|
|
112
|
+
gem install mailmate
|
|
117
113
|
```
|
|
118
114
|
|
|
119
115
|
Then bootstrap your config:
|
|
@@ -124,6 +120,18 @@ mmdiscover
|
|
|
124
120
|
|
|
125
121
|
`mmdiscover` reads MailMate's `Sources.plist` and `Identities.plist`, shows you the accounts and addresses it found, and offers to write `~/.config/mailmate/config.yml` from them. It also writes `~/.config/mailmate/bundle_loader.rb` for MailMate bundles.
|
|
126
122
|
|
|
123
|
+
### From source (development)
|
|
124
|
+
|
|
125
|
+
If you're hacking on the gem itself, skip `gem install` and put the repo's `exe/` on your `PATH`:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
git clone <this repo> ~/code/claude/mailmate
|
|
129
|
+
echo 'export PATH="$HOME/code/claude/mailmate/exe:$PATH"' >> ~/.zshrc
|
|
130
|
+
source ~/.zshrc
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Then `mmdiscover` as above.
|
|
134
|
+
|
|
127
135
|
## Commands
|
|
128
136
|
|
|
129
137
|
| Command | What it does |
|
data/lib/mailmate/cli/search.rb
CHANGED
|
@@ -25,7 +25,8 @@ module Mailmate
|
|
|
25
25
|
HEADER_FIELDS = %i[from recipients cc subject address_any any].freeze
|
|
26
26
|
|
|
27
27
|
VALID_FIELDS = %w[id path mailbox from to cc bcc reply-to subject date time
|
|
28
|
-
message-id
|
|
28
|
+
message-id references in-reply-to
|
|
29
|
+
direction party flags read archive tags keywords].freeze
|
|
29
30
|
|
|
30
31
|
HEADER_LABELS = {
|
|
31
32
|
"direction" => "dir",
|
|
@@ -39,8 +40,11 @@ module Mailmate
|
|
|
39
40
|
"read" => :index,
|
|
40
41
|
"archive" => :index,
|
|
41
42
|
"flags" => :index,
|
|
43
|
+
"tags" => :index,
|
|
44
|
+
"keywords" => :index,
|
|
42
45
|
"from" => :header, "to" => :header, "cc" => :header, "bcc" => :header,
|
|
43
46
|
"reply-to" => :header, "subject" => :header, "message-id" => :header,
|
|
47
|
+
"references" => :header, "in-reply-to" => :header,
|
|
44
48
|
"direction" => :header, "party" => :header,
|
|
45
49
|
}.freeze
|
|
46
50
|
|
|
@@ -57,8 +61,10 @@ module Mailmate
|
|
|
57
61
|
parser.parse!(argv)
|
|
58
62
|
|
|
59
63
|
search_string = argv[0] || DEFAULT_SEARCH
|
|
60
|
-
fields_arg = (opts[:fields] || argv[1] || DEFAULT_FIELDS).to_s
|
|
61
|
-
|
|
64
|
+
fields_arg = (opts[:fields] || argv[1] || DEFAULT_FIELDS).to_s.strip
|
|
65
|
+
# `+...` means "defaults plus these"; bare list replaces defaults.
|
|
66
|
+
fields_arg = "#{DEFAULT_FIELDS} #{fields_arg[1..]}" if fields_arg.start_with?("+")
|
|
67
|
+
extra_fields = fields_arg.split(/\s+/).reject(&:empty?)
|
|
62
68
|
fields = (["id"] + extra_fields).uniq
|
|
63
69
|
|
|
64
70
|
imap_root = Mailmate.config.imap_root
|
|
@@ -105,7 +111,7 @@ module Mailmate
|
|
|
105
111
|
:index
|
|
106
112
|
elsif specs.any? { |field, _, _| (field == :body || field == :message_or_body) && !opts[:headers_only] }
|
|
107
113
|
:full
|
|
108
|
-
elsif specs.all? { |field, _, _|
|
|
114
|
+
elsif specs.all? { |field, _, _| %i[date tag keyword].include?(field) }
|
|
109
115
|
:index
|
|
110
116
|
else
|
|
111
117
|
:header
|
|
@@ -156,8 +162,9 @@ module Mailmate
|
|
|
156
162
|
o.separator ""
|
|
157
163
|
o.separator "POSITIONAL ARGS"
|
|
158
164
|
o.separator " search-string Quicksearch expression. Default: 'd 1d'. Pass '' to disable."
|
|
159
|
-
o.separator " fields Space-separated columns to
|
|
165
|
+
o.separator " fields Space-separated columns to show (id is always first)."
|
|
160
166
|
o.separator " Default: 'flags date time direction party subject'."
|
|
167
|
+
o.separator " Prefix with '+' to add to the defaults: '+tags' = defaults + tags."
|
|
161
168
|
o.separator ""
|
|
162
169
|
o.separator "OPTIONS"
|
|
163
170
|
o.on("--mailbox X", "Mailbox to search (default: all)") { |v| opts[:mailbox] = v }
|
|
@@ -188,6 +195,29 @@ module Mailmate
|
|
|
188
195
|
o.separator " mmsearch 'f medium d 7d' from Medium in last 7 days"
|
|
189
196
|
o.separator " mmsearch 's \"rent due\" !draft' subject has rent due, no 'draft'"
|
|
190
197
|
o.separator " mmsearch 'd 2026-05' received in May 2026"
|
|
198
|
+
o.separator ""
|
|
199
|
+
o.separator "FIELDS (for the fields argument / --fields)"
|
|
200
|
+
o.separator " id eml-id (always included as first column)"
|
|
201
|
+
o.separator " path full path to the .eml file"
|
|
202
|
+
o.separator " mailbox account/mailbox path (no /Messages/<id>.eml suffix)"
|
|
203
|
+
o.separator " from From header"
|
|
204
|
+
o.separator " to To header"
|
|
205
|
+
o.separator " cc Cc header"
|
|
206
|
+
o.separator " bcc Bcc header"
|
|
207
|
+
o.separator " reply-to Reply-To header"
|
|
208
|
+
o.separator " subject Subject header"
|
|
209
|
+
o.separator " message-id RFC Message-ID header"
|
|
210
|
+
o.separator " references RFC References header (space-joined when multiple)"
|
|
211
|
+
o.separator " in-reply-to RFC In-Reply-To header"
|
|
212
|
+
o.separator " date received date, YYYY-MM-DD (local time)"
|
|
213
|
+
o.separator " time received time, HH:MM (local time)"
|
|
214
|
+
o.separator " direction '→' outbound, '←' inbound (column header: 'dir')"
|
|
215
|
+
o.separator " party counterparty (recipients if outbound, sender if inbound)"
|
|
216
|
+
o.separator " flags archive + read combined, e.g. 'AR', 'PU'"
|
|
217
|
+
o.separator " read 'R' read or 'U' unread (column header: 'r')"
|
|
218
|
+
o.separator " archive 'A' archived or 'P' present elsewhere (column header: 'a')"
|
|
219
|
+
o.separator " tags user tags (IMAP keywords), comma-joined; system flags (\\… , $…) excluded"
|
|
220
|
+
o.separator " keywords raw IMAP keyword list (incl. \\Seen, \\Draft, \\Flagged, \$Forwarded, user tags)"
|
|
191
221
|
end
|
|
192
222
|
end
|
|
193
223
|
|
|
@@ -359,11 +389,19 @@ module Mailmate
|
|
|
359
389
|
when :address_any
|
|
360
390
|
[mail[:from], mail[:to], mail[:cc], mail[:reply_to], mail[:sender]]
|
|
361
391
|
.compact.map { |h| h.value.to_s }.join(" ").downcase
|
|
362
|
-
when :tag, :keyword
|
|
363
|
-
[mail["x-keywords"]&.value, mail["keywords"]&.value].compact.join(" ").downcase
|
|
364
392
|
end
|
|
365
393
|
end
|
|
366
394
|
|
|
395
|
+
# MailMate stores user tags as IMAP keywords in the `#flags` index — not
|
|
396
|
+
# as `X-Keywords`/`Keywords` headers in the .eml — so tag matching has to
|
|
397
|
+
# go through the index, not the parsed mail. Strips `\…` (RFC) and `$…`
|
|
398
|
+
# (Thunderbird/Apple) system flags so substring matches only hit user tags.
|
|
399
|
+
def tag_value(eml_id)
|
|
400
|
+
return "" unless eml_id
|
|
401
|
+
flags = (Mailmate::IndexReader.for("#flags").flags_for(eml_id.to_i) rescue [])
|
|
402
|
+
flags.reject { |f| f.start_with?("\\", "$") }.join(" ").downcase
|
|
403
|
+
end
|
|
404
|
+
|
|
367
405
|
def text_body(mail)
|
|
368
406
|
(mail.text_part&.decoded || mail.body.decoded).to_s.force_encoding("UTF-8").scrub.downcase
|
|
369
407
|
rescue StandardError
|
|
@@ -374,8 +412,10 @@ module Mailmate
|
|
|
374
412
|
specs.all? do |field, term, negate|
|
|
375
413
|
hit =
|
|
376
414
|
case field
|
|
377
|
-
when :from, :recipients, :cc, :subject, :address_any
|
|
415
|
+
when :from, :recipients, :cc, :subject, :address_any
|
|
378
416
|
field_value(mail, field).include?(term)
|
|
417
|
+
when :tag, :keyword
|
|
418
|
+
tag_value(eml_id).include?(term)
|
|
379
419
|
when :body
|
|
380
420
|
headers_only ? false : text_body(mail).include?(term)
|
|
381
421
|
when :message_or_body
|
|
@@ -485,14 +525,21 @@ module Mailmate
|
|
|
485
525
|
archive = path.include?("/Archive.mailbox/") ? "A" : "P"
|
|
486
526
|
seen = (Mailmate::IndexReader.for("#flags").flags_for(eml_id.to_i) rescue []).include?("\\Seen")
|
|
487
527
|
"#{archive}#{seen ? 'R' : 'U'}"
|
|
528
|
+
when "tags"
|
|
529
|
+
flags = (Mailmate::IndexReader.for("#flags").flags_for(eml_id.to_i) rescue [])
|
|
530
|
+
flags.reject { |f| f.start_with?("\\", "$") }.join(",")
|
|
531
|
+
when "keywords"
|
|
532
|
+
(Mailmate::IndexReader.for("#flags").flags_for(eml_id.to_i) rescue []).join(",")
|
|
488
533
|
when "from" then mail ? Array(mail.from).join("; ") : nil
|
|
489
534
|
when "to" then mail ? Array(mail.to).join("; ") : nil
|
|
490
535
|
when "cc" then mail ? Array(mail.cc).join("; ") : nil
|
|
491
536
|
when "bcc" then mail ? Array(mail.bcc).join("; ") : nil
|
|
492
537
|
when "reply-to" then mail ? Array(mail.reply_to).join("; ") : nil
|
|
493
|
-
when "subject"
|
|
494
|
-
when "message-id"
|
|
495
|
-
when "
|
|
538
|
+
when "subject" then mail&.subject.to_s
|
|
539
|
+
when "message-id" then mail&.message_id.to_s
|
|
540
|
+
when "references" then mail ? Array(mail.references).join(" ") : nil
|
|
541
|
+
when "in-reply-to" then mail ? Array(mail.in_reply_to).join(" ") : nil
|
|
542
|
+
when "direction" then mail ? (outbound?(path, mail) ? "→" : "←") : nil
|
|
496
543
|
when "party" then mail ? party_for(mail, outbound?(path, mail)) : nil
|
|
497
544
|
end.to_s
|
|
498
545
|
end
|
data/lib/mailmate/version.rb
CHANGED