Skip to content

feat(mail): env password, list enhancements, and batch command (0.2.6) - #13

Closed
bscott wants to merge 4 commits into
masterfrom
v0.2.6
Closed

feat(mail): env password, list enhancements, and batch command (0.2.6)#13
bscott wants to merge 4 commits into
masterfrom
v0.2.6

Conversation

@bscott

@bscott bscott commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Implements three feature requests (#8, #9, #10) for automated/headless email workflows. Reimplemented from scratch (issue branches used only as reference); builds clean, go vet clean, all tests pass.

#8 — Bridge password from env var

  • GetPassword() checks PM_CLI_BRIDGE_PASSWORD before the system keyring → enables headless/CI use with no secret service.
  • Interactive use unchanged (env var only consulted when set & non-empty). Env-takes-precedence is documented.

#9mail list enhancements

  • Fixes the --unread undercount: --unread/--flagged now use server-side IMAP SEARCH, so results honor --limit instead of being thinned by client-side filtering. The two can be combined.
  • Adds from_address, to, message_id, in_reply_to to list/search JSON (all already in the ENVELOPE — zero extra fetch). Previously from dropped the address whenever a display name was present.
  • Adds --fields (project onto named JSON fields) and --compact (bare array). JSON-only; text output unchanged.
  • ListMessages now takes a ListOptions struct; all callers updated.

#10mail batch

  • New mail batch subcommand runs a JSON array of ops (label, unlabel, archive, move, flag, delete) over a single IMAP session.
  • All ops validated before connecting; names with IMAP special chars rejected — including the source mailbox (closed a gap vs. the original proposal); 10MB input cap; errors sanitized via safetext.
  • --stop-on-error halts after first failure; per-op results + totals reported.

Tests & docs

  • New tests: env-var precedence, field parsing/projection, batch validation (incl. CRLF/wildcard rejection) and decode.
  • docs/commands.md, new docs/batch-format.md, and the --help-json schema updated. Version bumped to 0.2.6.

⚠️ Sequencing note

mail batch routes through CopyMessages/DeleteMessages/MoveMessages. Its per-op success reporting is only fully accurate once #11 (silent no-op detection in STORE/COPY) lands — and #11's COPY check still needs the UIDPLUS/COPYUID verification flagged in that review. Recommend landing #11 before relying on batch's label/unlabel results.


Supersedes #12 (closed automatically when its branch was renamed to v0.2.6).

bscott added 4 commits June 1, 2026 19:12
Implements three feature requests for automated/headless email workflows.

#8 - Bridge password from environment:
- GetPassword() checks PM_CLI_BRIDGE_PASSWORD before the system keyring,
  enabling headless/CI use where no secret service is available. Interactive
  use is unchanged (env var only consulted when set and non-empty).

#9 - mail list enhancements:
- --unread/--flagged now use server-side IMAP SEARCH, so results honor
  --limit instead of being thinned by client-side filtering.
- Adds from_address, to, message_id, and in_reply_to to list/search output
  (all already in the ENVELOPE, no extra fetch).
- Adds --fields (project onto named JSON fields) and --compact (bare array).
- ListMessages now takes a ListOptions struct; all callers updated.

#10 - mail batch:
- New 'mail batch' subcommand executes a JSON array of operations (label,
  unlabel, archive, move, flag, delete) over a single IMAP session.
- Operations are fully validated before connecting; mailbox/label names with
  IMAP special characters (including the source mailbox) are rejected; input
  is capped at 10MB; errors are sanitized for terminal output.
- --stop-on-error halts after the first failure; per-op results reported.

Docs (commands.md, batch-format.md) and JSON help schema updated; version
bumped to 0.3.0.

Note: batch's per-op success reporting depends on issue #11 (silent no-op
detection in STORE/COPY) for full accuracy.
Report-level credit to @Juan-de-Costa-Rica for the detailed issue reports.
Implementation written fresh against the issues.
No v0.3.0 release/tag exists yet; latest published release is v0.2.5.
These changes ship in the 0.2.6 line, not a 0.3.0 minor release.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@Juan-de-Costa-Rica

Copy link
Copy Markdown

Two things in mail batch we ran into while running this branch in production against Proton Bridge.

1. permanent is not wired through on delete. batchOp has no permanent field, and executeBatchOp calls DeleteMessages(mailbox, op.UIDs, false). Since mail delete --permanent exists as a flag outside batch, the same delete behaves differently depending on which path issues it. Our pipeline had been sending {"op":"delete","permanent":true} for some time believing those were permanent, while the messages were going to Trash.

2. That was silent, because unknown keys are dropped. decodeBatchOps uses a plain json.Unmarshal, so anything not on the struct is discarded without error:

$ echo '[{"op":"label","uids":["uid:99999999"],"label":"UNSORTED","bogus_unknown_key":true,"permanent":true}]' | pm-cli mail batch --json
{"results":[{"op":"label","success":false,"error":"no messages matched the given ID(s) in INBOX"}],"total":1,"succeeded":0,"failed":1}

Both keys accepted, neither honored. A json.Decoder with DisallowUnknownFields() would turn that into a parse-time error before any IMAP command runs, which fits the validate-everything-up-front approach already in MailBatchCmd.Run.

3. Minor: batchResult carries op but not the uids it answers for, so a caller pairing results back to inputs has to rely on array position. Echoing the uids would make that unambiguous.

Happy to send a PR for any of these if useful.

@bscott

bscott commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

Closing in favor of #18, which carries forward this PR's unique contribution.

This PR bundled three features. Two of them landed independently in the meantime:

Those overlapped this branch across five files (cli.go, mail.go, config.go, client.go, types.go). #16's version was the stronger implementation — it extracts listSeqSet/paginateSeqNums/newMessageSummary rather than inlining the logic, and ships ~500 lines of tests including an in-memory IMAP server.

Two things this PR did better were ported into #18 rather than lost:

Two defects from this branch are also fixed in #18: the --help-json batch examples used \" inside raw string literals (emitting literal backslashes into the agent schema), and docs/commands.md listed seq as a --fields name where the shipped implementation uses seq_num.

Note #12 was an identical duplicate of this branch (same head SHA b38b298) and was already closed.

One item from this PR that did not carry over, worth a follow-up: Search() was updated here to populate from_address/to/message_id/in_reply_to. #16 only changed ListMessages, so mail list and mail search currently return different JSON shapes.

@bscott bscott closed this Aug 8, 2026
bscott added a commit that referenced this pull request Aug 8, 2026
Carries forward the unique portion of #13. The env-var and mail-list
work in that PR landed independently via #14 and #16, so only the batch
command remains; this drops the duplicated implementations rather than
conflicting with what shipped.

mail batch runs multiple label/unlabel/archive/move/flag/delete
operations over a single IMAP session, avoiding a connect and auth
round-trip per operation. All operations are validated up front, so
malformed input fails before any connection is opened or any mailbox is
mutated. Names containing IMAP special characters or CRLF are rejected,
and input is capped at 10MB.

Also fixes two defects from #13:
  - The --help-json examples used \" inside raw string literals, which
    emitted literal backslashes into the agent-facing schema.
  - docs/commands.md listed `seq` as a --fields name; the implementation
    that shipped in #16 uses `seq_num`.

Adds a CHANGELOG covering everything in 0.2.6 and bumps the version.

Closes #10.

Co-authored-by: exe.dev user <exedev@discovery-lotus.exe.xyz>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants