proton-mail-bridge-client 2.15.2 → 2.16.1

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.
Files changed (85) hide show
  1. package/README.md +39 -7
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +49 -2
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +56 -12
  7. package/dist/index.js.map +1 -1
  8. package/dist/scripts/install-claude-desktop.d.ts.map +1 -1
  9. package/dist/scripts/install-claude-desktop.js +13 -13
  10. package/dist/scripts/install-claude-desktop.js.map +1 -1
  11. package/dist/services/account-manager.d.ts +4 -0
  12. package/dist/services/account-manager.d.ts.map +1 -1
  13. package/dist/services/account-manager.js +6 -0
  14. package/dist/services/account-manager.js.map +1 -1
  15. package/dist/services/autoresponder-service.d.ts +1 -1
  16. package/dist/services/autoresponder-service.d.ts.map +1 -1
  17. package/dist/services/autoresponder-service.js +4 -0
  18. package/dist/services/autoresponder-service.js.map +1 -1
  19. package/dist/services/brief-service.d.ts +42 -0
  20. package/dist/services/brief-service.d.ts.map +1 -0
  21. package/dist/services/brief-service.js +47 -0
  22. package/dist/services/brief-service.js.map +1 -0
  23. package/dist/services/commitment-service.d.ts +39 -0
  24. package/dist/services/commitment-service.d.ts.map +1 -0
  25. package/dist/services/commitment-service.js +83 -0
  26. package/dist/services/commitment-service.js.map +1 -0
  27. package/dist/services/triage-plan-service.d.ts +56 -0
  28. package/dist/services/triage-plan-service.d.ts.map +1 -0
  29. package/dist/services/triage-plan-service.js +83 -0
  30. package/dist/services/triage-plan-service.js.map +1 -0
  31. package/dist/tools/args.d.ts +2 -0
  32. package/dist/tools/args.d.ts.map +1 -1
  33. package/dist/tools/args.js +16 -0
  34. package/dist/tools/args.js.map +1 -1
  35. package/dist/tools/brief.d.ts +4 -0
  36. package/dist/tools/brief.d.ts.map +1 -0
  37. package/dist/tools/brief.js +73 -0
  38. package/dist/tools/brief.js.map +1 -0
  39. package/dist/tools/commitments.d.ts +16 -0
  40. package/dist/tools/commitments.d.ts.map +1 -0
  41. package/dist/tools/commitments.js +145 -0
  42. package/dist/tools/commitments.js.map +1 -0
  43. package/dist/tools/context.d.ts +2 -0
  44. package/dist/tools/context.d.ts.map +1 -1
  45. package/dist/tools/context.js +3 -0
  46. package/dist/tools/context.js.map +1 -1
  47. package/dist/tools/definitions.d.ts +271 -0
  48. package/dist/tools/definitions.d.ts.map +1 -1
  49. package/dist/tools/definitions.js +123 -0
  50. package/dist/tools/definitions.js.map +1 -1
  51. package/dist/tools/drafts.js +14 -14
  52. package/dist/tools/drafts.js.map +1 -1
  53. package/dist/tools/reading.d.ts.map +1 -1
  54. package/dist/tools/reading.js +77 -23
  55. package/dist/tools/reading.js.map +1 -1
  56. package/dist/tools/rules-runner.d.ts.map +1 -1
  57. package/dist/tools/rules-runner.js +4 -1
  58. package/dist/tools/rules-runner.js.map +1 -1
  59. package/dist/tools/sending.js +11 -11
  60. package/dist/tools/sending.js.map +1 -1
  61. package/dist/tools/style.d.ts +4 -0
  62. package/dist/tools/style.d.ts.map +1 -0
  63. package/dist/tools/style.js +94 -0
  64. package/dist/tools/style.js.map +1 -0
  65. package/dist/tools/triage.d.ts +4 -0
  66. package/dist/tools/triage.d.ts.map +1 -0
  67. package/dist/tools/triage.js +197 -0
  68. package/dist/tools/triage.js.map +1 -0
  69. package/dist/utils/commitment-phrases.d.ts +10 -0
  70. package/dist/utils/commitment-phrases.d.ts.map +1 -0
  71. package/dist/utils/commitment-phrases.js +118 -0
  72. package/dist/utils/commitment-phrases.js.map +1 -0
  73. package/dist/utils/mail-security.d.ts +34 -0
  74. package/dist/utils/mail-security.d.ts.map +1 -0
  75. package/dist/utils/mail-security.js +173 -0
  76. package/dist/utils/mail-security.js.map +1 -0
  77. package/dist/utils/schedule-templates.d.ts +2 -0
  78. package/dist/utils/schedule-templates.d.ts.map +1 -1
  79. package/dist/utils/schedule-templates.js +20 -17
  80. package/dist/utils/schedule-templates.js.map +1 -1
  81. package/dist/utils/style-profile.d.ts +29 -0
  82. package/dist/utils/style-profile.d.ts.map +1 -0
  83. package/dist/utils/style-profile.js +125 -0
  84. package/dist/utils/style-profile.js.map +1 -0
  85. package/package.json +1 -1
package/README.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  ---
26
26
 
27
- Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mail inbox: read, search, send, draft, triage threads, manage folders, save attachments, and more — 108 MCP tools in total. Most of the same capabilities are also available as a full CLI for scripting, cron, and piped automation — no Claude required.
27
+ Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mail inbox: read, search, send, draft, triage threads, manage folders, save attachments, and more — 116 MCP tools in total. Most of the same capabilities are also available as a full CLI for scripting, cron, and piped automation — no Claude required.
28
28
 
29
29
  > **Using Proton Drive too?** See [**proton-drive-mcp**](https://github.com/googlarz/proton-drive-mcp), the companion MCP server and CLI for Proton Drive (upload, download, share and manage your encrypted files from Claude). Run both side by side to handle mail and files in one conversation, for example save an attachment from here, then upload it to Drive. More in [Related projects](#related-projects).
30
30
 
@@ -45,7 +45,7 @@ Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mai
45
45
  - "Which of my emails have attachments from this month?"
46
46
  - "Show me my drafts and tighten the oldest one."
47
47
  5. **Not sure about handing over control?** Start with `PROTONMAIL_MODE=drafts`: Claude can read, search and prepare drafts, but nothing is sent or deleted until you change the mode. The server also tells the client how to behave (ids, accounts, never obeying instructions found inside a message).
48
- 6. **Optional:** `PROTONMAIL_READ_ONLY=true` to browse without being able to change anything, `PROTONMAIL_TOOL_TIER=core` for a smaller tool set (25 instead of 108), and more accounts under [Multi-account support](#multi-account-support-v210). More recipes below in [Try it](#try-it-example-claude-prompts).
48
+ 6. **Optional:** `PROTONMAIL_READ_ONLY=true` to browse without being able to change anything, `PROTONMAIL_TOOL_TIER=core` for a smaller tool set (25 instead of 116), and more accounts under [Multi-account support](#multi-account-support-v210). More recipes below in [Try it](#try-it-example-claude-prompts).
49
49
 
50
50
  ---
51
51
 
@@ -57,7 +57,7 @@ Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mai
57
57
  - **Fast local search** — full-text search across your inbox from a local index that keeps its read state in step with the server
58
58
  - **Rules that suggest themselves** — a scan proposes filters and cleanup from your own mail, applies them to existing mail after a preview, and can forward by condition; see [Mailbox rules](#mailbox-rules-suggestions-filters-cleanup-forwarding)
59
59
  - **Automatic reply, per address, with rules** — off by default, with a preview of what it would do, draft-for-approval mode and working-hours; see [Autoresponder](#autoresponder-automatic-reply-per-address) (Proton's own auto-reply may be all you need)
60
- - **Full CLI** — a dedicated command for every one of the 108 tools (plus a generic `tool <name>` passthrough), scriptable and pipeable; `doctor` checks every account and `report` prints a diagnostic summary for a bug report
60
+ - **Full CLI** — a dedicated command for every one of the 116 tools (plus a generic `tool <name>` passthrough), scriptable and pipeable; `doctor` checks every account and `report` prints a diagnostic summary for a bug report
61
61
  - **Runs where you do** — Claude Desktop bundle, npm, Homebrew, or a [Docker image](#running-in-docker-headless-machines) for a headless machine
62
62
  - **Privacy-native** — no third-party email service involved; your mail stays on your machine
63
63
 
@@ -298,7 +298,7 @@ Reload the Cline extension after saving. Proton Mail tools will appear in Cline'
298
298
  For a server with no desktop, next to a headless Bridge. The image installs the published npm package and is published for each release to `ghcr.io/googlarz/proton-mail-bridge-client` (`:<version>` and `:latest`, linux/amd64 and linux/arm64). To build it yourself from this repo:
299
299
 
300
300
  ```bash
301
- docker build -f docker/Dockerfile --build-arg VERSION=2.15.2 -t proton-mail-bridge-client .
301
+ docker build -f docker/Dockerfile --build-arg VERSION=2.16.1 -t proton-mail-bridge-client .
302
302
  docker run -i --rm --network host \
303
303
  -e PROTONMAIL_USERNAME=you@proton.me -e PROTONMAIL_PASSWORD_FILE=/run/secrets/bridge \
304
304
  -v /path/to/bridge-password:/run/secrets/bridge:ro -v bridge-client-data:/data \
@@ -334,6 +334,38 @@ get_autoresponder account=work@example.com # state, counts
334
334
 
335
335
  `outgoingNote` is appended, after the signature, to every mail sent from that address while the rule is active (a send with `appendSignature: false` opts out). On the command line: `set-autoresponder`, `autoresponder` and `preview-autoresponder`. The rule, the log and the memory of who was answered live in `autoresponder.json` in the account's data directory; every account (every address Bridge exposes) has its own.
336
336
 
337
+ ## Mail firewall (prompt-injection guard)
338
+
339
+ Mail is untrusted text that a model reads. `get_email_by_id` and `get_emails_by_ids` add a `risk` note (`caution` or `high`, with the reasons) when a message looks written to steer an AI or to deceive you: instructions addressed to an assistant (English, Polish, German), text hidden by markup or zero-width characters, a link that reads one address and goes to another, a Reply-To on another domain, a sender name that shows a different address, a failed SPF/DKIM/DMARC check. Normal mail carries no note. The instruction patterns are also checked wherever else text reaches the model: attachment text, thread reads, lists with previews and reading a message as an MCP resource.
340
+
341
+ After a **high** message has been read, the tools that reach outside (`send_email`, replies, `forward_email`, `send_draft`, `schedule_draft`, `delete_email`, `delete_thread`, `bulk_delete`, `empty_folder`, `unsubscribe_sender`, `respond_to_invite`, and switching on `set_rule` or `set_autoresponder`) ask for `confirmed: true` and quote the reason, even when `PROTONMAIL_CONFIRM_DESTRUCTIVE` is off. Previews (`dryRun`) and switching things off pass. Rules and the autoresponder never act by themselves on a high-risk message. This narrows what a persuaded model can do; it cannot read intent (see the [threat model](docs/threat-model.md)).
342
+
343
+ ## Brief (what changed since you last looked)
344
+
345
+ `get_brief` (CLI: `brief`) reports from the local index how much mail came since the last brief, split into people and automatic mail, what waits for your reply, replies you are waiting for that are overdue, and suspicious mail (see the mail firewall above). `markSeen` moves the "since" mark, so a daily run reports only the day. To get it without opening anything, run it from your system's scheduler:
346
+
347
+ ```bash
348
+ proton-mail-bridge-client brief --sync --mark-seen --notify
349
+ ```
350
+
351
+ `--notify` shows the few lines as a system notification (macOS and Linux with `notify-send`). `schedule --job brief` prints the launchd, systemd, cron or Windows task definition that runs it every day (it installs nothing).
352
+
353
+ ## Tidy in one approval (`plan_inbox`)
354
+
355
+ Ask Claude to tidy the inbox and it prepares a **plan**: a numbered list of changes that can all be taken back (archive, move, mark read, label), from its own proposals and from a scan for automatic mail nobody reads (unread for 3 days, or read and over 14 days old). Nothing changes until you approve. `approve_plan` applies everything, only some numbers, or everything except some; each change goes through the same runtime policy as the single tools. `undo_plan` puts the applied changes back (mail returns to its folder, read marks and labels are reverted). Senders of scanned items you left out are not proposed again. A plan never contains delete or trash.
356
+
357
+ ## Commitments ("I'll send it by Friday")
358
+
359
+ A local ledger of what you promised and what was promised to you. `find_commitments` reads recent sent mail (and, if you ask, mail from people) and returns sentences that look like promises, in English, Polish and German, with the date each names ("by Friday", "do piątku", "bis Freitag", `05.11.`). They are only candidates: Claude confirms with you, then `set_commitment` records the real ones. `list_commitments` shows each open entry as overdue, due today, open or probably done (the person who owed it wrote again in the thread). Overdue and due-today entries appear in the brief. Closing an entry is `set_commitment` with `id` and `status`. Nothing in the mailbox changes.
360
+
361
+ ## Style profile (drafts that sound like you)
362
+
363
+ `get_style_profile` works out how you write from your own sent mail, on your machine: language, how you open and close ("Dzień dobry {name},", "Cheers"), tone, typical length. It returns a few plain hints for drafting, overall and for a given recipient or company ("to pat@client.example you write in Polish, formally"), and the server tells Claude to call it before drafting. The profile is cached for a week in `style-profile.json` and holds patterns and counts only: no sentences, no names.
364
+
365
+ ## Search by meaning (several wordings)
366
+
367
+ `search_indexed_emails` takes `queries`: several wordings of one question, up to 8. Claude supplies the variants (synonyms, other languages: `["deposit", "kaucja", "Kaution", "rental agreement"]`), the server searches each in the local index and fuses the results, so a message found by several wordings ranks first and every hit says which wordings found it (`matchedBy`). There is no model and no download: the meaning comes from the model you already use. What it cannot find is mail that uses none of the words tried.
368
+
337
369
  ## Mailbox rules (suggestions, filters, cleanup, forwarding)
338
370
 
339
371
  Proton's own filters act on **new** mail, the free plan has one custom filter, and auto-delete of Trash and Spam is a paid feature with a fixed 30 days (see Proton's pages on [filters](https://proton.me/support/email-inbox-filters) and [auto-delete](https://proton.me/support/auto-delete-unwanted-messages)). These rules work alongside them: on mail that is already there, with a preview, and with a scan that suggests rules instead of waiting for you to write them. They run on this server, so they only act while it runs.
@@ -497,7 +529,7 @@ All commands support `--json` for machine-readable output, and any MCP tool is d
497
529
 
498
530
  `<command> --help` prints that command's usage without running it. Flags accept `--flag value` or `--flag=value` (a value starting with `--` needs the `=` form, or must come after a bare `--`); unknown flags are an error. Exit codes: `0` success, `1` failure (including a failed `doctor` or `connection-status` check, or a failed item in a `batch`/`bulk-*` run), `2` usage error.
499
531
 
500
- **Full command reference: [docs/cli.md](docs/cli.md)** (a named command for every one of the 108 tools, across read, triage, compose, mailbox actions, folders, drafts, templates, analytics, and diagnostics).
532
+ **Full command reference: [docs/cli.md](docs/cli.md)** (a named command for every one of the 116 tools, across read, triage, compose, mailbox actions, folders, drafts, templates, analytics, and diagnostics).
501
533
 
502
534
  ---
503
535
 
@@ -519,7 +551,7 @@ A mode is a floor: the individual flags below can make things stricter (`PROTONM
519
551
  All flags work in both the MCP server and CLI:
520
552
 
521
553
  ```bash
522
- PROTONMAIL_TOOL_TIER=core # expose 25 core tools instead of all 108 — saves context window
554
+ PROTONMAIL_TOOL_TIER=core # expose 25 core tools instead of all 116 — saves context window
523
555
  PROTONMAIL_READ_ONLY=true # disable all write operations
524
556
  PROTONMAIL_ALLOW_SEND=false # disable SMTP sends only (other writes still work)
525
557
  PROTONMAIL_CONFIRM_DESTRUCTIVE=true # require confirmed:true on send, reply, forward, delete
@@ -742,7 +774,7 @@ Measured in October 2026 (v2.7.0) on a copy of the same ~57,000-message index. "
742
774
  - Defaults are bounded: `search_emails`/`search_indexed_emails` return 50 (pass `limit` for more), `list_drafts` 20, `list_scheduled_sends` 50 newest first. Each reports `hasMore`; page with `offset`.
743
775
  - Draft and queued-message bodies are shortened to a preview in list and update responses (`bodyTruncated`, `bodyLength`); use `get_draft` for one full draft. Attachment content is never inlined in a listing.
744
776
  - Large attachments: set `PROTONMAIL_ALLOW_FILE_DOWNLOAD_DIR` and pass `saveTo`, rather than raising `PROTONMAIL_MAX_INLINE_KB`.
745
- - **`PROTONMAIL_TOOL_TIER=core`** exposes 25 tools instead of 108: about 7k tokens of tool definitions instead of about 21k (roughly 67% less; the earlier measurement with a reference tokenizer, 7k against 20k for 96 tools, scaled by the 3.7% the four newest tool definitions add), paid on every conversation that loads the server.
777
+ - **`PROTONMAIL_TOOL_TIER=core`** exposes 25 tools instead of 116: about 7k tokens of tool definitions instead of about 21k (roughly 67% less; the earlier measurement with a reference tokenizer, 7k against 20k for 96 tools, scaled by the 3.7% the four newest tool definitions add), paid on every conversation that loads the server.
746
778
  - Repeating an `update_draft` that changes nothing on a draft whose remote copy is already in sync does no store write and no IMAP upload. (A draft whose last sync failed, or that was edited with `syncToRemote:false`, still syncs.)
747
779
 
748
780
  **Working with drafts**
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAaA,OAAO,KAAK,EAAgB,gBAAgB,EAAqB,MAAM,kBAAkB,CAAC;AAqB1F,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAiBD,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAM3C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAuD1D;AA0ID,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAOD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAqErD,CAAC;AAoBF,eAAO,MAAM,iBAAiB,YAAI,KAAK,EAAE,MAAM,EAAE,MAAM,CAAU,CAAC;AAGlE,wBAAgB,kBAAkB,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAQlG;AAID,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA+DtD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAcnE;AAyKD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAY5F;AAiND,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAoB5G;AAgGD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGnD;AAGD,wBAAgB,aAAa,CAAC,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,EAAE,IAAI,SAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS7G;AA01BD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,KAAK,EAAE,OAAO,GACb;IAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAYzE;AA2SD,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IAIb,SAAS,CAAC,EAAE,MAAM,CAAC;IAMnB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,eAAO,MAAM,kBAAkB,EAAE,eAAe,EA2E/C,CAAC;AA2EF,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAoP1C;AA4ED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAaA,OAAO,KAAK,EAAgB,gBAAgB,EAAqB,MAAM,kBAAkB,CAAC;AAqB1F,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAiBD,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAM3C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAuD1D;AA2ID,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAOD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAsErD,CAAC;AAoBF,eAAO,MAAM,iBAAiB,YAAI,KAAK,EAAE,MAAM,EAAE,MAAM,CAAU,CAAC;AAGlE,wBAAgB,kBAAkB,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAQlG;AAID,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA+DtD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAcnE;AAyKD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAY5F;AAiND,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAoB5G;AAgGD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGnD;AAGD,wBAAgB,aAAa,CAAC,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,EAAE,IAAI,SAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS7G;AA+2BD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,KAAK,EAAE,OAAO,GACb;IAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAYzE;AA2SD,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IAIb,SAAS,CAAC,EAAE,MAAM,CAAC;IAMnB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,eAAO,MAAM,kBAAkB,EAAE,eAAe,EAmF/C,CAAC;AA+EF,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAuP1C;AA+ED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD"}
package/dist/cli.js CHANGED
@@ -129,6 +129,7 @@ function printHelp() {
129
129
  " empty-folder <folder> Permanently empty a folder (--confirmed to execute)",
130
130
  " labels List normalized labels from the local index",
131
131
  " threads [query] List normalized threads from the local index",
132
+ " brief What changed since the last brief (--since --limit --sync --mark-seen --notify)",
132
133
  " digest Show inbox digest and top actionable threads",
133
134
  " followups Show follow-up candidates from the local index",
134
135
  " emails List emails from a folder (--folder --limit --offset)",
@@ -233,7 +234,7 @@ const DRAFT_COPY_FLAGS = ["cc", "bcc", "notes"];
233
234
  const BULK_FILTER_FLAGS = ["from", "subject", "since", "before"];
234
235
  export const COMMAND_SPECS = {
235
236
  completion: { usage: "completion <zsh|bash|fish>", description: "Print a shell completion script for the commands and their flags", flags: [] },
236
- schedule: { usage: "schedule [--at HH:MM] [--system launchd|systemd|cron|schtasks] [--output <file>] [--password-file <file>]", description: "Print the scheduler definition that runs `digest` every day, with the commands to switch it on (installs nothing)", flags: ["at", "system", "output", "password-file"] },
237
+ schedule: { usage: "schedule [--job digest|brief] [--at HH:MM] [--system launchd|systemd|cron|schtasks] [--output <file>] [--password-file <file>]", description: "Print the scheduler definition that runs `digest` (or `brief`, as a notification) every day, with the commands to switch it on (installs nothing)", flags: ["job", "at", "system", "output", "password-file"] },
237
238
  help: { usage: "help [command]", description: "Show the command list, or the help for one command", flags: [] },
238
239
  version: { usage: "version [--check]", description: "Print the version and exit; --check also asks the npm registry whether a newer release exists", flags: ["check"] },
239
240
  "setup-claude-desktop": { usage: "setup-claude-desktop", description: "Run the Claude Desktop setup wizard (works from any install)", flags: [] },
@@ -252,6 +253,7 @@ export const COMMAND_SPECS = {
252
253
  "empty-folder": { usage: "empty-folder <folder> [--confirmed]", description: "Permanently empty a folder (--confirmed to execute)", flags: ["folder", "confirmed"] },
253
254
  labels: { usage: "labels [--limit <n>]", description: "List normalized labels from the local index", flags: ["limit"] },
254
255
  threads: { usage: "threads [query] [--label <name>] [--limit <n>] [--sync]", description: "List normalized threads from the local index", flags: ["sync", "folder", "limit", "label"] },
256
+ brief: { usage: "brief [--account <a>] [--since <time>] [--limit <n>] [--sync] [--mark-seen] [--notify]", description: "What changed since the last brief: new mail, what waits for your reply, overdue replies, suspicious mail (--notify shows it as a system notification, --mark-seen moves the since mark)", flags: ["account", "since", "limit", "sync", "mark-seen", "notify"] },
255
257
  digest: { usage: "digest [--limit <n>] [--age-hours <n>] [--sync]", description: "Show inbox digest and top actionable threads", flags: ["sync", "limit", "age-hours"] },
256
258
  followups: { usage: "followups [--pending you|them|any] [--limit <n>] [--age-hours <n>] [--sync]", description: "Show follow-up candidates from the local index", flags: ["sync", "pending", "limit", "age-hours"] },
257
259
  emails: { usage: "emails [--folder <name>] [--limit <n>] [--offset <n>]", description: "List emails from a folder", flags: ["folder", "limit", "offset"] },
@@ -1631,6 +1633,31 @@ async function runContacts(parsed) {
1631
1633
  printToolCallResult(result, wantJson);
1632
1634
  });
1633
1635
  }
1636
+ async function runBrief(parsed) {
1637
+ const wantJson = isTruthyFlag(parsed.flags.json);
1638
+ const args = { limit: getNumberFlag(parsed.flags, "limit", 5) };
1639
+ const account = getStringFlag(parsed.flags, "account");
1640
+ const since = getStringFlag(parsed.flags, "since");
1641
+ if (account)
1642
+ args.account = account;
1643
+ if (since)
1644
+ args.since = since;
1645
+ if (isTruthyFlag(parsed.flags.sync))
1646
+ args.syncBefore = true;
1647
+ if (isTruthyFlag(parsed.flags["mark-seen"]))
1648
+ args.markSeen = true;
1649
+ await withMcpClient(async (client) => {
1650
+ const result = await client.callTool({ name: "get_brief", arguments: args });
1651
+ printToolCallResult(result, wantJson);
1652
+ // The same few lines as a system notification, so a scheduled run reaches you without opening anything.
1653
+ if (isTruthyFlag(parsed.flags.notify) && !result.isError) {
1654
+ const text = result.content.find((part) => part.type === "text")?.text;
1655
+ const lines = text ? JSON.parse(text).text : undefined;
1656
+ if (lines?.length)
1657
+ await sendSystemNotification("Proton Mail brief", lines.join("\n"));
1658
+ }
1659
+ });
1660
+ }
1634
1661
  async function runVolumeTrends(parsed) {
1635
1662
  const wantJson = isTruthyFlag(parsed.flags.json);
1636
1663
  await withMcpClient(async (client) => {
@@ -2005,6 +2032,14 @@ export const TOOL_ONLY_COMMANDS = [
2005
2032
  { command: "set-rule", tool: "set_rule", positionals: [], help: "Create, change or accept a proposal as a rule (--proposalId, --id, --name, --enabled, --when, --actions, --confirmed)" },
2006
2033
  { command: "delete-rule", tool: "delete_rule", positionals: ["id"], help: "Delete a rule, or dismiss a proposal" },
2007
2034
  { command: "apply-rules", tool: "apply_rules", positionals: [], help: "Run the rules on mail already in the index; a preview unless --dryRun false (--ruleId, --limit, --confirmed)" },
2035
+ { command: "plan-inbox", tool: "plan_inbox", positionals: [], help: "Prepare a batch of mailbox changes to approve at once (--args '{\"scan\":true,\"limit\":30}')" },
2036
+ { command: "approve-plan", tool: "approve_plan", positionals: ["planId"], help: "Apply a plan from plan-inbox (--args '{\"exclude\":[3]}')" },
2037
+ { command: "undo-plan", tool: "undo_plan", positionals: ["planId"], help: "Put back what an approved plan changed" },
2038
+ { command: "find-commitments", tool: "find_commitments", positionals: [], help: "Find sentences that look like promises in recent mail (--args '{\"days\":7,\"direction\":\"both\"}')" },
2039
+ { command: "set-commitment", tool: "set_commitment", positionals: [], help: "Record a commitment, or close one (--args '{\"text\":\"...\",\"direction\":\"mine\",\"due\":\"2026-11-05\"}')" },
2040
+ { command: "commitments", tool: "list_commitments", positionals: [], help: "The commitment ledger: overdue, due today, open, probably done (--args '{\"status\":\"all\"}')" },
2041
+ { command: "style-profile", tool: "get_style_profile", positionals: [], help: "How you write, from your sent mail: language, greeting, closing, tone (--args '{\"recipient\":\"pat@client.example\",\"refresh\":true}')" },
2042
+ { command: "get-brief", tool: "get_brief", positionals: [], help: "(tool form; see also `brief`)" },
2008
2043
  { command: "autoresponder", tool: "get_autoresponder", positionals: [], help: "Show the automatic reply of an address (--account)" },
2009
2044
  { command: "set-autoresponder", tool: "set_autoresponder", positionals: [], help: "Set up or switch off the automatic reply of an address (--enabled, --body, --subject, --startsAt, --endsAt, --outgoingNote, --mode, --timezone, --rules, --confirmed)" },
2010
2045
  { command: "list-templates", tool: "list_templates", positionals: [], help: "List saved email templates" },
@@ -2076,8 +2111,13 @@ async function runToolOnlyCommand(entry, parsed) {
2076
2111
  if (requiredFileField && args[requiredFileField] === undefined) {
2077
2112
  throw new Error(`${entry.command} requires --file <path>, or ${requiredFileField} via --args.`);
2078
2113
  }
2114
+ // Reading sent mail for the style profile is slow on a large mailbox and a command line has nothing to run it in the
2115
+ // background, so it waits for the whole build.
2116
+ const slow = entry.tool === "get_style_profile";
2117
+ if (slow && args.waitSeconds === undefined)
2118
+ args.waitSeconds = 280;
2079
2119
  await withMcpClient(async (client) => {
2080
- const result = await client.callTool({ name: entry.tool, arguments: args });
2120
+ const result = await client.callTool({ name: entry.tool, arguments: args }, undefined, slow ? { timeout: 290_000 } : undefined);
2081
2121
  printToolCallResult(result, wantJson);
2082
2122
  });
2083
2123
  }
@@ -2195,6 +2235,9 @@ export async function main() {
2195
2235
  case "threads":
2196
2236
  await runThreads(parsed);
2197
2237
  return;
2238
+ case "brief":
2239
+ await runBrief(parsed);
2240
+ return;
2198
2241
  case "digest":
2199
2242
  await runDigest(parsed);
2200
2243
  return;
@@ -2387,10 +2430,14 @@ function scheduleText(parsed) {
2387
2430
  throw new CliUsageError(`--system must be one of: ${SCHEDULER_SYSTEMS.join(", ")} (got "${requested}").`);
2388
2431
  }
2389
2432
  const system = requested ?? defaultSchedulerSystem(process.platform);
2433
+ const job = getStringFlag(parsed.flags, "job") ?? "digest";
2434
+ if (job !== "digest" && job !== "brief")
2435
+ throw new CliUsageError(`--job must be digest or brief (got "${job}").`);
2390
2436
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
2391
2437
  const output = getStringFlag(parsed.flags, "output") ?? `${home}/proton-mail-brief.txt`;
2392
2438
  const passwordFile = getStringFlag(parsed.flags, "password-file") ?? `${home}/.proton-mail-bridge-password`;
2393
2439
  const result = buildSchedule({
2440
+ job,
2394
2441
  system,
2395
2442
  hour,
2396
2443
  minute,