mail-notify 1.0.0__tar.gz

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 (87) hide show
  1. mail_notify-1.0.0/CHANGELOG.md +148 -0
  2. mail_notify-1.0.0/CODE_OF_CONDUCT.md +9 -0
  3. mail_notify-1.0.0/CONTRIBUTING.md +33 -0
  4. mail_notify-1.0.0/LICENSE +21 -0
  5. mail_notify-1.0.0/MANIFEST.in +4 -0
  6. mail_notify-1.0.0/PKG-INFO +388 -0
  7. mail_notify-1.0.0/README.md +358 -0
  8. mail_notify-1.0.0/SECURITY.md +46 -0
  9. mail_notify-1.0.0/docs/HOW-IT-WORKS.md +102 -0
  10. mail_notify-1.0.0/docs/RESOURCES.md +199 -0
  11. mail_notify-1.0.0/docs/STABILITY.md +33 -0
  12. mail_notify-1.0.0/docs/dev/ANALISI-APP-CLAUDE.md +125 -0
  13. mail_notify-1.0.0/docs/dev/PIANO-v0.3.md +34 -0
  14. mail_notify-1.0.0/pyproject.toml +47 -0
  15. mail_notify-1.0.0/setup.cfg +4 -0
  16. mail_notify-1.0.0/src/ccmail/__init__.py +1 -0
  17. mail_notify-1.0.0/src/ccmail/__main__.py +13 -0
  18. mail_notify-1.0.0/src/ccmail/ack.py +178 -0
  19. mail_notify-1.0.0/src/ccmail/activity.py +136 -0
  20. mail_notify-1.0.0/src/ccmail/auth.py +67 -0
  21. mail_notify-1.0.0/src/ccmail/autostart.py +142 -0
  22. mail_notify-1.0.0/src/ccmail/chats.py +238 -0
  23. mail_notify-1.0.0/src/ccmail/cli.py +536 -0
  24. mail_notify-1.0.0/src/ccmail/config.py +161 -0
  25. mail_notify-1.0.0/src/ccmail/credstore.py +274 -0
  26. mail_notify-1.0.0/src/ccmail/daemon.py +107 -0
  27. mail_notify-1.0.0/src/ccmail/db.py +245 -0
  28. mail_notify-1.0.0/src/ccmail/diagnostics.py +49 -0
  29. mail_notify-1.0.0/src/ccmail/heartbeat.py +105 -0
  30. mail_notify-1.0.0/src/ccmail/hookclient.py +112 -0
  31. mail_notify-1.0.0/src/ccmail/htmlmail.py +341 -0
  32. mail_notify-1.0.0/src/ccmail/i18n.py +80 -0
  33. mail_notify-1.0.0/src/ccmail/identity.py +35 -0
  34. mail_notify-1.0.0/src/ccmail/inbound.py +453 -0
  35. mail_notify-1.0.0/src/ccmail/installer.py +171 -0
  36. mail_notify-1.0.0/src/ccmail/launcher.py +73 -0
  37. mail_notify-1.0.0/src/ccmail/mailer.py +69 -0
  38. mail_notify-1.0.0/src/ccmail/paths.py +111 -0
  39. mail_notify-1.0.0/src/ccmail/policy.py +179 -0
  40. mail_notify-1.0.0/src/ccmail/projroot.py +84 -0
  41. mail_notify-1.0.0/src/ccmail/providers.py +52 -0
  42. mail_notify-1.0.0/src/ccmail/redact.py +20 -0
  43. mail_notify-1.0.0/src/ccmail/render.py +233 -0
  44. mail_notify-1.0.0/src/ccmail/server.py +450 -0
  45. mail_notify-1.0.0/src/ccmail/skill/SKILL.md +66 -0
  46. mail_notify-1.0.0/src/ccmail/skillinstall.py +58 -0
  47. mail_notify-1.0.0/src/ccmail/suggest.py +181 -0
  48. mail_notify-1.0.0/src/ccmail/summarize.py +181 -0
  49. mail_notify-1.0.0/src/ccmail/tracer.py +82 -0
  50. mail_notify-1.0.0/src/ccmail/wakeclient.py +63 -0
  51. mail_notify-1.0.0/src/ccmail/web/app.css +581 -0
  52. mail_notify-1.0.0/src/ccmail/web/app.js +1909 -0
  53. mail_notify-1.0.0/src/ccmail/web/favicon.svg +6 -0
  54. mail_notify-1.0.0/src/ccmail/web/index.html +471 -0
  55. mail_notify-1.0.0/src/ccmail/web/lang-en-app.js +436 -0
  56. mail_notify-1.0.0/src/ccmail/web/lang-en.js +194 -0
  57. mail_notify-1.0.0/src/ccmail/web/lang.js +127 -0
  58. mail_notify-1.0.0/src/ccmail/worker.py +82 -0
  59. mail_notify-1.0.0/src/mail_notify.egg-info/PKG-INFO +388 -0
  60. mail_notify-1.0.0/src/mail_notify.egg-info/SOURCES.txt +85 -0
  61. mail_notify-1.0.0/src/mail_notify.egg-info/dependency_links.txt +1 -0
  62. mail_notify-1.0.0/src/mail_notify.egg-info/entry_points.txt +3 -0
  63. mail_notify-1.0.0/src/mail_notify.egg-info/top_level.txt +1 -0
  64. mail_notify-1.0.0/tests/helpers.py +50 -0
  65. mail_notify-1.0.0/tests/test_ack.py +180 -0
  66. mail_notify-1.0.0/tests/test_api_limits.py +72 -0
  67. mail_notify-1.0.0/tests/test_chats.py +211 -0
  68. mail_notify-1.0.0/tests/test_cli_api.py +63 -0
  69. mail_notify-1.0.0/tests/test_discovery.py +130 -0
  70. mail_notify-1.0.0/tests/test_heartbeat.py +250 -0
  71. mail_notify-1.0.0/tests/test_htmlmail.py +259 -0
  72. mail_notify-1.0.0/tests/test_inbound.py +326 -0
  73. mail_notify-1.0.0/tests/test_inturn.py +99 -0
  74. mail_notify-1.0.0/tests/test_launcher.py +61 -0
  75. mail_notify-1.0.0/tests/test_mail_content.py +189 -0
  76. mail_notify-1.0.0/tests/test_matrix.py +147 -0
  77. mail_notify-1.0.0/tests/test_misc.py +155 -0
  78. mail_notify-1.0.0/tests/test_policy.py +171 -0
  79. mail_notify-1.0.0/tests/test_portable.py +254 -0
  80. mail_notify-1.0.0/tests/test_rejection.py +112 -0
  81. mail_notify-1.0.0/tests/test_reply_turn.py +74 -0
  82. mail_notify-1.0.0/tests/test_server.py +187 -0
  83. mail_notify-1.0.0/tests/test_setup.py +73 -0
  84. mail_notify-1.0.0/tests/test_suggest.py +54 -0
  85. mail_notify-1.0.0/tests/test_summarize.py +106 -0
  86. mail_notify-1.0.0/tests/test_webi18n.py +137 -0
  87. mail_notify-1.0.0/tests/test_wrapped_quote.py +21 -0
@@ -0,0 +1,148 @@
1
+ # Changelog
2
+
3
+ All notable changes to ccmail. Versions follow [Semantic Versioning](https://semver.org/).
4
+
5
+ ## 1.0.0 - 2026-10-13
6
+
7
+ First stable release. What is stable is listed in [docs/STABILITY.md](docs/STABILITY.md).
8
+
9
+ New: `mail-notify setup [--autostart]` installs the hooks, the skill and the short command in one step (`init`
10
+ stays separate: it asks for the password).
11
+
12
+ Fixed before release: `status` and `stop` no longer treat an answer from a foreign daemon (HTTP 401/403, for
13
+ example another data folder's) as "our daemon is running"; the source distribution now ships the full test suite
14
+ (`MANIFEST.in`).
15
+
16
+ The distribution (PyPI/pipx) name is `mail-notify`, matching the command and the repository; the Python module and
17
+ the daemon keep the name `ccmail`, and `ccmail` still works as a command.
18
+
19
+ ### Known limitations
20
+ - macOS, Linux and mail providers other than Gmail (Outlook, iCloud and Yahoo presets) are covered by the
21
+ automated tests only; real-system testing is planned for a later release.
22
+ - Hints in the terminal and in the web app now name the short `mail-notify` command (`python -m ccmail` still works).
23
+
24
+ ### Changed (summary)
25
+ - The summary in `summary` mode now covers the whole answer in at most 10 short lines: a lead, one line per
26
+ section ("Heading: what it opens with") sampled evenly across the message, and the closing question. The default
27
+ `summary_chars` is 300. The scrollable box of the HTML mail holds the complete answer (in `full` mode, the part
28
+ that is not already shown above it).
29
+ - `install-skill` and `install-hooks` also create the short `mail-notify` command, so there is no third step.
30
+ - Summary lines are short (about 130 characters; one bullet per section; a table is named by its first column),
31
+ the default `summary_chars` is 300 (the outcome fits in 300 characters), and the box no longer repeats the sentences the answer starts with.
32
+ - The scrollable box formats the Markdown of the answer: fenced code as a code block, inline code, bold, headings and
33
+ bullets, *italic*; bold may wrap inline code, and `full` mode formats the outcome too (everything escaped; the
34
+ plain-text part is unchanged).
35
+
36
+ ### Added (command name)
37
+ - `mail-notify` is a command of its own (console script next to `ccmail`), and `install-cli` / `uninstall-cli` write
38
+ or remove a small launcher in a folder already on `PATH`, for setups where the Scripts folder is not (Microsoft
39
+ Store Python). The launcher only ever overwrites or deletes a file it wrote itself.
40
+
41
+ ### Added (language)
42
+ - The web app is available in English: the `language` setting now covers the page, the mails and the command line.
43
+ `auto` follows Claude Code's own `language` setting (Italian/English), then the system language. Catalogs are
44
+ static files (`web/lang-en.js`, `web/lang-en-app.js`), applied without network access; a test checks that every
45
+ catalog entry still exists in the page.
46
+
47
+ ### Fixed
48
+ - Several commands arriving in one pass (for example ON then OFF) are applied oldest first, so the project ends
49
+ in the state asked for last; before, the newest was applied first and the older one won.
50
+
51
+ ### Added
52
+ - A verified reply that cannot be delivered (no chat tied to the mail, unknown or expired reference, delivery to
53
+ Claude switched off) is answered with a mail in the usual layout whose OUTCOME says "reply discarded" and why.
54
+ Capped at 5 an hour; ccmail's own mails are never answered, so no loop.
55
+ - `html_mail` setting (off by default): mails carry an HTML alternative next to the unchanged plain text, with
56
+ quick-reply buttons (OFF / ON / PAUSE 60, and "Write to Claude") that are pre-filled `mailto:` replies, and the
57
+ whole message in a box with its own scroll bar. The box follows `content_mode` (nothing extra in `title`/`minimal`).
58
+ One-click actions without pressing Send are not possible with a loopback-only daemon (Gmail actions need
59
+ registration with Google and a public HTTPS endpoint).
60
+
61
+ ## 0.6.0
62
+
63
+ ### Added
64
+ - **Cross-platform daemon**: data folders for Windows, macOS and Linux; credentials in Windows Credential Manager,
65
+ macOS Keychain or Linux Secret Service (password through stdin only), with an announced 0600-file fallback;
66
+ autostart through Task Scheduler, LaunchAgent or `systemd --user` (XDG autostart as last resort).
67
+ - Provider presets (Gmail, Outlook/Microsoft 365, iCloud, Yahoo, custom) in `ccmail init` and the web app.
68
+ - `language` setting (`auto`/`it`/`en`): mails, suggested commands, messages delivered to Claude and the command line.
69
+ `PAUSE` is accepted next to `PAUSA`.
70
+ - `device_name` setting: a `Device:` line in mails when ccmail runs on several computers.
71
+ - `ccmail doctor [--network]`: installation check that prints no secrets (credentials, hooks, interpreter, skill,
72
+ autostart, daemon, mail servers).
73
+ - `ccmail install-skill` / `uninstall-skill`: the `mail-notify` skill ships inside the package.
74
+ - `CLAUDE_CONFIG_DIR` is honoured for settings, skills, transcripts and the account name.
75
+ - The daemon picks a free port when the configured one is taken by another program.
76
+ - `docs/RESOURCES.md`: measured token, CPU, memory, disk, network and latency costs.
77
+ - Combination tests (`tests/test_matrix.py`): every mail kind x content mode x language x reply options, and policy vs. dry run.
78
+ - Community files, CI matrix (Windows/macOS/Linux x Python 3.10-3.13), release workflow with PyPI trusted publishing.
79
+
80
+ ### Fixed
81
+ - The answer mail of an e-mail reply was still skipped as a "stop hook loop": the reply reaches the chat through a
82
+ Stop hook, so its turn ends with `stop_hook_active` set. The guard no longer applies to that one-shot turn.
83
+ - The IMAP scan fetched one header per message each pass; it now asks for all of them in a single command.
84
+ - The answer to a reply sent by e-mail was never mailed back when the turn was short or processes were still running
85
+ (the user, being away, never saw it). A turn started by an e-mail reply is now always mailed at its end, with the
86
+ subject "risposta alla tua email"; the per-project switch, snooze and hourly cap still apply.
87
+
88
+ ### Changed
89
+ - Detached daemon start on POSIX uses a new session; `pythonw.exe` only on Windows.
90
+ - The design notes moved from the README to `docs/HOW-IT-WORKS.md`; working notes to `docs/dev/`.
91
+
92
+ ## 0.5.0
93
+
94
+ ### Added
95
+ - Reply-by-email control channel (`inbound_enabled`): `OFF` / `ON` / `PAUSA [min]` in the first line of a reply.
96
+ - Optional delivery of free-text replies into the Claude chat (`inbound_to_claude`, off by default).
97
+ - Mails are split into sections (outcome, background processes, suggested commands, management) and carry
98
+ `Chat: <name>`; account name comes from the Claude account.
99
+ - Usage-limit reached is its own event; summaries never cut a sentence in the middle.
100
+ - `ccmail diagnose` shows the reply channel state.
101
+
102
+ ### Fixed
103
+ - Replies sent to oneself were rejected as unauthenticated: Gmail's Sent label is now used.
104
+ - The test mail was not recorded, so the app kept saying "SMTP not yet verified".
105
+
106
+ ## 0.3.0
107
+
108
+ ### Added
109
+ - Chat names: the daemon reads the Claude desktop app's per-chat metadata (title, project folder, archived flag, last activity, and the Claude Code session id). Mails show `Chat: <name>`; unknown chats show `Sessione: <8 characters>`.
110
+ - Title fallback: when app metadata is missing, the title is the latest `custom-title` record at the end of the session transcript (read from the tail of the file only, cached).
111
+ - Discovery of app metadata in both the Microsoft Store package location (`%LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude`) and `%APPDATA%\Claude`, merged by session id. Override with `CCMAIL_CLAUDE_SESSIONS` (paths separated by `;`).
112
+ - Stable project resolution: chat folder, then a chat whose folder matches the transcript folder, then `CLAUDE_PROJECT_DIR`, then the shortest working folder seen for that transcript folder. Sub-folders are never projects; sub-agent transcripts are excluded.
113
+ - Startup repair: realigns stored sessions to their projects and removes projects with no sessions or chats; per-project switches are kept.
114
+ - Activity API: `GET /v1/projects?chats=N` (default 5 newest non-archived chats per project, `0` = all; archived chats listed separately) and `GET /v1/diagnostics`.
115
+ - `ccmail diagnose`: read-only report of what ccmail sees in the desktop app.
116
+ - Per-project on/off switch (`PUT /v1/projects/enabled`). When off, no mail and no wake-ups for that project. `ccmail status --cwd <folder>` reports it.
117
+ - Honest session states: working, waiting (only if recent), waiting for background processes, periodic check, quiet (idle over 6 h), ended, archived, stale, uncertain.
118
+ - `PostToolBatch` hook (async) for liveness.
119
+
120
+ ### Changed
121
+ - Projects are derived from the chat's folder instead of the current working folder.
122
+
123
+ ### Upgrade notes
124
+ - Run `ccmail install-hooks` again and restart Claude Code (the new `PostToolBatch` hook).
125
+
126
+ ### Known limits
127
+ - The desktop app's metadata format is internal and undocumented. Every read soft-fails: if the format changes, ccmail falls back to hook data.
128
+ - A chat could in theory change its Claude Code session id after resume or compaction. Not observed so far.
129
+
130
+ ## 0.2.0
131
+
132
+ ### Added
133
+ - Background-task awareness: while tasks run in the background (`Stop.background_tasks`), intermediate `Stop` events send no mail.
134
+ - Periodic wake-up: every `heartbeat_minutes`, and only while tasks are still running, the daemon wakes Claude through an `asyncRewake` Stop hook (`ccmail wake`). Claude checks the tasks and its short reply is mailed as a status update. A `skill` mode is available where Claude schedules its own wake-ups.
135
+ - "Finished" mail: one mail when the last background task disappears.
136
+ - Watchdog rule: an optional silent-session watchdog (`watchdog_minutes`), which must be off or at least twice `heartbeat_minutes`.
137
+ - Summary mode (`content_mode = summary`, default): a short extractive summary of the turn, capped by `summary_chars`.
138
+ - Minimal mode (`content_mode = minimal`): no model-derived text at all.
139
+ - Account name (`account_name`), shown in every mail as `Account:`; empty = Windows user name.
140
+
141
+ ## 0.1.0
142
+
143
+ ### Added
144
+ - Local daemon for Windows that emails about Claude Code events through Gmail SMTP, with a local configuration web app on `127.0.0.1`.
145
+ - Hooks (`Stop`, `Notification`, `StopFailure`, `SessionStart`, `SessionEnd`, `UserPromptSubmit`, `PostToolUse` for `Bash`) forwarded to the daemon; all decisions are made in the daemon.
146
+ - Anti-spam gates: no confirmation mails, minimum turn length for `Stop`, mute after toggle, per-session cooldown, hourly cap, selectable `Notification` types, title-only mode.
147
+ - Credentials in Windows Credential Manager, with a DPAPI-encrypted file fallback.
148
+ - Commands: `init`, `on`, `off`, `status`, `test`, `open`, `stop`, `daemon`, `install-hooks`, `uninstall-hooks`, `migrate`.
@@ -0,0 +1,9 @@
1
+ # Code of Conduct
2
+
3
+ Be respectful and constructive. Harassment, personal attacks and discrimination are not tolerated in issues, pull
4
+ requests or discussions. Assume good faith, be specific, and keep feedback about the work.
5
+
6
+ Maintainers may edit or remove contributions that break these rules and may restrict participation. Report problems
7
+ privately through the repository's security advisory form or by contacting the maintainer on GitHub.
8
+
9
+ This is a short statement in the spirit of the Contributor Covenant 2.1.
@@ -0,0 +1,33 @@
1
+ # Contributing
2
+
3
+ Thanks for helping. ccmail is deliberately small: standard library only, no runtime dependencies. Please keep it so.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/lobascioemanuele-droid/mail-notify && cd mail-notify
9
+ python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
10
+ pip install -e .
11
+ PYTHONPATH=src:tests python -W ignore -m unittest discover -s tests # Windows: set PYTHONPATH=src;tests
12
+ ```
13
+
14
+ `tests/test_matrix.py` runs every mail kind x content mode x language x reply option and compares the
15
+ policy with the web app's dry run: extend its tables when you add a kind, a mode or a setting.
16
+
17
+ Tests isolate the data folder (`CCMAIL_HOME`), the desktop-app metadata (`CCMAIL_CLAUDE_SESSIONS`) and the account
18
+ name, and pin `CCMAIL_LANG=it`. They never touch your real credentials or `~/.claude`.
19
+
20
+ ## Rules
21
+
22
+ - **Never** handle a real password in code, tests, logs or issues. Use fake values.
23
+ - No personal paths, names or addresses in code, tests or docs.
24
+ - Texts a user can see exist in Italian and English: write them as an `(it, en)` pair with `i18n.pick`.
25
+ - Anything that can send mail, or can reach Claude, needs a test for the case where it must *not* happen.
26
+ - Platform code goes through the small seams that already exist (`paths`, `credstore`, `autostart`) so it can be tested
27
+ on any OS.
28
+ - Update `CHANGELOG.md`. Add docs when behaviour changes, and `docs/RESOURCES.md` when a change alters what ccmail
29
+ sends to the model, polls, or runs.
30
+
31
+ ## Security issues
32
+
33
+ Report them privately: see [SECURITY.md](SECURITY.md).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mail-notify contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include LICENSE README.md CHANGELOG.md SECURITY.md CONTRIBUTING.md CODE_OF_CONDUCT.md
2
+ recursive-include tests *.py
3
+ recursive-include docs *.md
4
+ global-exclude __pycache__ *.py[cod]
@@ -0,0 +1,388 @@
1
+ Metadata-Version: 2.4
2
+ Name: mail-notify
3
+ Version: 1.0.0
4
+ Summary: Local daemon that e-mails you about Claude Code events, with a local settings web app.
5
+ Author: mail-notify contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/lobascioemanuele-droid/mail-notify
8
+ Project-URL: Source, https://github.com/lobascioemanuele-droid/mail-notify
9
+ Project-URL: Issues, https://github.com/lobascioemanuele-droid/mail-notify/issues
10
+ Project-URL: Changelog, https://github.com/lobascioemanuele-droid/mail-notify/blob/main/CHANGELOG.md
11
+ Keywords: claude,claude-code,notifications,email,hooks,smtp
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Environment :: Web Environment
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Communications :: Email
25
+ Classifier: Topic :: Software Development
26
+ Requires-Python: >=3.10
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Dynamic: license-file
30
+
31
+ # mail-notify
32
+
33
+ **E-mail notifications for [Claude Code](https://code.claude.com), with a local settings web app.**
34
+ A small daemon on your own computer mails you when Claude finishes a long task, needs a permission, hits an error or a
35
+ usage limit, or when your background processes finish. You can answer from your phone by replying to the mail.
36
+
37
+ *Naming: the package and the command are `mail-notify`; the daemon and the Python module inside are called `ccmail`
38
+ (`ccmail` also works as a command).*
39
+
40
+ [![CI](https://github.com/lobascioemanuele-droid/mail-notify/actions/workflows/ci.yml/badge.svg)](https://github.com/lobascioemanuele-droid/mail-notify/actions/workflows/ci.yml)
41
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
42
+ ![Dependencies](https://img.shields.io/badge/dependencies-none-brightgreen)
43
+ ![License](https://img.shields.io/badge/license-MIT-green)
44
+
45
+ - **Few mails, on purpose.** Every mail passes several gates (turn length, cooldown, hourly cap, event types). Turning
46
+ it on or off sends nothing.
47
+ - **Private by design.** Standard library only, no telemetry. The only thing that leaves your machine is the mail
48
+ itself, sent through your own mail account. The password lives in your system's credential store.
49
+ - **Reply to act.** Reply `OFF`, `ON` or `PAUSE 60` to a notification or, if you enable it, write a sentence that is
50
+ delivered to the Claude chat that sent the mail.
51
+ - **Understands your chats.** Mails name the project, the chat and your Claude account, and carry a short summary of
52
+ what Claude said (about 300 characters, at most 10 lines; the complete answer follows in a scrollable box).
53
+ - **Works while you are away.** While background tasks run, Claude is woken at a period you choose, only when tasks
54
+ exist, to report their status.
55
+
56
+ > **Tested today: Windows with Gmail.** macOS, Linux and mail providers other than Gmail (the Outlook, iCloud and
57
+ > Yahoo presets) are implemented and covered by the automated tests in CI, but are **not yet tested on a real
58
+ > system: that is planned for a later release.** Please report what you find.
59
+
60
+ ## Install step by step
61
+
62
+ You need your mail account's app password and about fifteen minutes. Run every command in **your own terminal**.
63
+ ccmail never asks Claude for your password.
64
+
65
+ ### Requirements
66
+
67
+ - Python 3.10 or newer.
68
+ - [Claude Code](https://code.claude.com), as the CLI or the desktop app.
69
+ - A mail account that allows SMTP with an **app password**. Presets exist for Gmail, Outlook, iCloud and Yahoo, and
70
+ you can enter any SMTP server.
71
+ - Optional, for replies by mail: IMAP access to the same mailbox.
72
+
73
+ ### Step 1: install the package
74
+
75
+ Install with [pipx](https://pipx.pypa.io), which keeps the program in its own environment. Once mail-notify is on PyPI:
76
+
77
+ ```bash
78
+ pipx install mail-notify
79
+ ```
80
+
81
+ To install the current version from the repository instead:
82
+
83
+ ```bash
84
+ pipx install git+https://github.com/lobascioemanuele-droid/mail-notify
85
+ ```
86
+
87
+ With Microsoft Store Python the `mail-notify` command can be "not found", because its Scripts folder is not on
88
+ `PATH`. In that case use `python -m ccmail` instead of `mail-notify` in every step below, or run
89
+ `python -m ccmail install-cli` once: it creates a short `mail-notify` command in a folder that is already on `PATH`.
90
+
91
+ ### Step 2: set up the sender account
92
+
93
+ ```bash
94
+ mail-notify init
95
+ ```
96
+
97
+ The prompts ask for the sender address, the provider (the right app-password link is shown for it), the app password,
98
+ the recipient (it defaults to the sender) and an optional device name. The app password is typed in a **hidden**
99
+ prompt and stored only in the system credential store (Windows Credential Manager, macOS Keychain or Linux Secret
100
+ Service). Never paste it into a chat, a file or an issue. This step sends no mail.
101
+
102
+ Where to create an app password:
103
+
104
+ - Gmail: https://myaccount.google.com/apppasswords (needs 2-step verification)
105
+ - Outlook: https://account.live.com/proofs/AppPassword
106
+ - iCloud: https://account.apple.com/account/manage
107
+ - Yahoo: https://login.yahoo.com/account/security
108
+
109
+ ### Step 3: install the hooks, the skill and the short command
110
+
111
+ ```bash
112
+ mail-notify setup --autostart
113
+ ```
114
+
115
+ One command does all of this, and sends nothing:
116
+
117
+ - adds ccmail's hooks to Claude Code's settings (a backup of the file is made first);
118
+ - copies the `/mail-notify` skill to `~/.claude/skills/mail-notify/`;
119
+ - creates the short `mail-notify` command if it is missing;
120
+ - with `--autostart`, starts the background daemon at logon.
121
+
122
+ It never asks for the password (that is `init`, step 2) and can be run again at any time. The three parts are also
123
+ available one by one: `install-hooks`, `install-skill` and `install-cli`.
124
+
125
+ ### Step 4: restart Claude Code
126
+
127
+ Close and reopen Claude Code: hooks and skills are read at startup. After that `/mail-notify` appears in the list of
128
+ skills.
129
+
130
+ ### Step 5: turn it on and test it
131
+
132
+ ```bash
133
+ mail-notify on
134
+ ```
135
+
136
+ Notifications are **off** after installation, so nothing is sent until you run this. `on` sends no mail.
137
+
138
+ ```bash
139
+ mail-notify test
140
+ ```
141
+
142
+ This sends **one** test mail. It also works while notifications are off.
143
+
144
+ ```bash
145
+ mail-notify open
146
+ ```
147
+
148
+ This opens the settings web app in your browser. It listens on `127.0.0.1` only and uses a one-time sign-in link.
149
+ Optionally, in its **Rules** tab, turn on:
150
+
151
+ - **Control by replying to the e-mail** (reply `OFF` / `ON` / `PAUSE`; needs IMAP, see above).
152
+ - **Replies reach Claude** (free-text replies are delivered to the chat that sent the mail).
153
+ - **E-mail with buttons** (an HTML version with tappable quick-reply cards).
154
+
155
+ ### Step 6: use it
156
+
157
+ In Claude Code, the `/mail-notify` skill accepts `on`, `off`, `status`, `test` and `open`:
158
+
159
+ ```bash
160
+ /mail-notify on
161
+ ```
162
+
163
+ ```bash
164
+ /mail-notify status
165
+ ```
166
+
167
+ Each project also has its own switch in the web app's **Activity** tab.
168
+
169
+ When reply control is on, each mail tells you how to reply. Write one of these on the **first line** of your reply:
170
+
171
+ - `OFF`: turns notifications off for that project.
172
+ - `ON`: turns them back on for that project.
173
+ - `PAUSE 60` (or `SLEEP`, or `PAUSE` alone for 60 minutes): silence on all projects for that many minutes.
174
+
175
+ ### Updating
176
+
177
+ Update the package (use the command that matches how you installed it):
178
+
179
+ ```bash
180
+ pipx upgrade mail-notify
181
+ ```
182
+
183
+ ```bash
184
+ pipx install --force git+https://github.com/lobascioemanuele-droid/mail-notify
185
+ ```
186
+
187
+ Then refresh the hooks and the skill, and restart Claude Code:
188
+
189
+ ```bash
190
+ mail-notify setup
191
+ ```
192
+
193
+ ### Uninstalling
194
+
195
+ Stop the daemon, then remove the pieces in this order:
196
+
197
+ ```bash
198
+ mail-notify stop
199
+ ```
200
+
201
+ ```bash
202
+ mail-notify uninstall-hooks --autostart
203
+ ```
204
+
205
+ ```bash
206
+ mail-notify uninstall-skill
207
+ ```
208
+
209
+ ```bash
210
+ mail-notify uninstall-cli
211
+ ```
212
+
213
+ ```bash
214
+ pipx uninstall mail-notify
215
+ ```
216
+
217
+ Uninstalling does not delete the data folder or the stored password. Delete the data folder yourself (on Windows
218
+ `%APPDATA%\ccmail`), remove the ccmail entry from your credential store (on Windows it is `ccmail:smtp` in Credential
219
+ Manager), and revoke the app password at your mail provider.
220
+
221
+ ## What a mail looks like
222
+
223
+ - **Header:** `Project:`, `Account:` and `Chat:` (the chat's name in the desktop app, or `Session:` with a short id when
224
+ the name is unknown). `Device:` appears when you set a device name. Turn duration and type follow for a finished turn.
225
+ - **OUTCOME** (`ESITO` in Italian): a short summary of the whole answer, about 300 characters (the `summary_chars`
226
+ setting) and at most 10 lines: a lead, one line per section, and the closing question when the answer ends with one.
227
+ With HTML mail on, the complete answer is in a scrollable box below it.
228
+ - **Background processes:** how many are still running, and their ids, when there are any.
229
+ - **Suggested commands:** a numbered list tailored to the message, to paste into the chat. They are tappable boxes
230
+ when "Replies reach Claude" is on.
231
+ - **Quick-reply cards** (HTML mail with reply control on): `OFF`, `ON` and `SLEEP` (a 1-hour pause on all projects),
232
+ other pauses (15 minutes, 3 hours, 8 hours) and, if "Replies reach Claude" is on, "Write to Claude". Each card opens
233
+ a pre-filled reply; you still press Send.
234
+ - **Reply instructions** in plain text, when reply control is on.
235
+
236
+ ## What you get
237
+
238
+ | Mail | When |
239
+ |---|---|
240
+ | Task finished | A turn longer than 2 minutes (setting) ended |
241
+ | Needs your input | A permission prompt or a question is waiting |
242
+ | Error / usage limit | An API error, or the account limit was reached (with the reset time) |
243
+ | Background processes | Periodic status while they run; one mail when the last one ends |
244
+ | Chat stalled | Optional watchdog: no events for N minutes with tasks declared |
245
+
246
+ ### HTML mail (optional)
247
+
248
+ Turn on **E-mail with buttons** in the web app's Rules tab and every mail also carries an HTML version next to the
249
+ unchanged plain text: a coloured header by event type, the data in a grid, quick-reply cards, the suggested commands
250
+ as tappable boxes (when "Replies reach Claude" is on), and the whole message in a box with its own scroll bar (at most
251
+ 340 px tall). The same layout is used for the confirmation, "command executed" and "reply discarded" mails.
252
+
253
+ - The cards are pre-filled replies (`mailto:`): you still press Send. One-click actions inside Gmail need registration
254
+ with Google and a public HTTPS endpoint, which a daemon listening on loopback cannot offer.
255
+ - A mail app that does not show HTML shows the plain text, without cards or the scroll box.
256
+ - Nothing extra leaves your machine: the box follows the content mode (`title` and `minimal` add nothing), and text
257
+ goes through the same secret redaction.
258
+ - A verified reply that cannot be delivered (no chat tied to the mail, unknown or expired reference, delivery to
259
+ Claude switched off) is answered with a mail whose outcome says "reply discarded" and why. At most 5 an hour.
260
+
261
+ ### Settings web app
262
+
263
+ `mail-notify open` shows: an **Overview** with health checks and the main on/off switch; **Rules**
264
+ (what to report, how often, how much text, reply control); **Activity** with per-project switches and the latest chats;
265
+ and **Connection** with provider presets, the device name and a test button. It listens on `127.0.0.1` only, with a
266
+ one-time sign-in link, CSRF protection and a strict content security policy.
267
+
268
+ The page, the mails and the command line follow the `language` setting: `auto` (the default), `it` or `en`. `auto`
269
+ uses the language set in Claude Code's own `language` setting when it names Italian or English, and otherwise the
270
+ system language. The page is translated from static catalogs in the package. Nothing is sent to a translation
271
+ service, and data from the daemon (project names, chat titles, mail subjects) is never translated.
272
+
273
+ ### Reply by mail
274
+
275
+ Off by default (web app → Rules → *Control by replying to the e-mail*). When on, ccmail reads your mailbox over IMAP
276
+ and accepts only replies that come from you, are verified by the provider, quote a valid reference from a recent
277
+ ccmail mail, and start with `OFF`, `ON` or `PAUSE [minutes]` (the Italian words `SPEGNI`, `ACCENDI`, `PAUSA`, and a few
278
+ others are accepted too). With **Replies reach Claude** on, any other reply text is delivered to the chat the mail came
279
+ from. The answer to it is mailed back to you when that turn ends, even if it was short, with the subject
280
+ "answer to your e-mail" (Italian: "risposta alla tua email").
281
+
282
+ See [SECURITY.md](SECURITY.md) for the exact rules and [docs/HOW-IT-WORKS.md](docs/HOW-IT-WORKS.md) for the delivery
283
+ paths.
284
+
285
+ ### The `/mail-notify` skill
286
+
287
+ `mail-notify install-skill` copies a small skill to `~/.claude/skills/mail-notify/`. It lets you type
288
+ `/mail-notify on`, `off`, `status`, `test` or `open` in Claude Code, and tells Claude how to behave during long
289
+ processes (a concise status while they run, a short final summary). The skill is instructed never to read credential
290
+ files or edit Claude's settings.
291
+
292
+ ## Several computers
293
+
294
+ Every computer has its own daemon, data folder and credentials, and its own copy of the settings. Give each one a name
295
+ (`mail-notify init`, or web app → Connection → *Name of this device*) and it appears as `Device:` in the mails. A reply
296
+ is applied by the daemon that sent the mail it refers to; daemons ignore references they did not issue.
297
+
298
+ ## Platforms
299
+
300
+ | | Windows | macOS | Linux |
301
+ |---|---|---|---|
302
+ | Data folder | `%APPDATA%\ccmail` | `~/Library/Application Support/ccmail` | `~/.config/ccmail` |
303
+ | Password store | Credential Manager (fallback: DPAPI file) | Keychain (fallback: 0600 file) | Secret Service via `secret-tool` (fallback: 0600 file) |
304
+ | Start at logon | Task Scheduler | LaunchAgent | `systemd --user` (fallback: XDG autostart) |
305
+ | Status | tested | to be tested (later release) | to be tested (later release) |
306
+
307
+ The fallback files are announced loudly by `init`, `status`, `doctor` and the web app. On Linux install
308
+ `libsecret-tools` to use the Secret Service. Claude Code's folder honours `CLAUDE_CONFIG_DIR`.
309
+
310
+ ## Privacy and security
311
+
312
+ - ccmail reads only what it needs: what Claude Code passes to its hooks; the desktop app's chat titles and folders; the
313
+ latest chat title recorded at the end of a session transcript, when the app's metadata is missing; the account's
314
+ display name from `~/.claude.json` (nothing else in that file is used); and the `language` key of Claude Code's
315
+ `settings.json`. Installing the hooks edits that file, after making a backup.
316
+ - Message text is redacted for common secrets before it is put in a mail. `title` and `minimal` content modes send no
317
+ model-written text at all.
318
+ - The password is never written to logs, the database, the repository or the web app; the API never returns it.
319
+ - The daemon binds to `127.0.0.1`, authenticates hooks with a file-based token and checks `Host` and `Origin`.
320
+
321
+ Details and the threat model: [SECURITY.md](SECURITY.md). Report vulnerabilities privately (see that file).
322
+
323
+ ## Troubleshooting
324
+
325
+ | Symptom | Try |
326
+ |---|---|
327
+ | No mail at all | `mail-notify status`: notifications start OFF, so run `mail-notify on`. Then `mail-notify doctor --network` and `mail-notify test`; check spam and the app password |
328
+ | Hooks do nothing | `mail-notify install-hooks`, then restart Claude Code; `mail-notify doctor` flags a moved Python |
329
+ | No buttons or cards in a mail | Cards need HTML mail and reply control on; plain-text mail apps show no buttons |
330
+ | Chat names missing | `mail-notify diagnose` shows where the desktop app's metadata is looked for |
331
+ | Port busy | The daemon picks a free port by itself; hooks and CLI read it from `daemon.json` |
332
+ | Reply by mail is slow | Most of the delay is the provider delivering mail to yourself (seconds to about 25 s) |
333
+ | Linux: password in a file | Install `libsecret-tools` and run `mail-notify init` again |
334
+
335
+ ## Commands
336
+
337
+ `init` · `setup [--autostart]` · `on` · `off` · `status [--cwd DIR]` · `test` · `open` · `stop` · `doctor [--network]` · `install-hooks
338
+ [--autostart]` · `uninstall-hooks [--autostart]` · `install-skill` · `uninstall-skill` · `diagnose` · `trace on|off|show`
339
+ · `migrate` · `daemon [--foreground]` · `install-cli` · `uninstall-cli`.
340
+
341
+ The command is installed as both `mail-notify` and `ccmail`. `install-hooks` and `install-skill` also create the short
342
+ `mail-notify` command. If neither name is found, run `python -m ccmail install-cli` once (see Step 1).
343
+
344
+ ## Resource use
345
+
346
+ No model calls at all in normal operation. The daemon uses about 35 MB and about 0.05–0.25 % of one core, and each hook
347
+ call adds about 0.12 s. Tokens are spent only on the skill text, on periodic wake-ups while background tasks run and on
348
+ delivered replies. The HTML mail costs no tokens (about 0.1–0.6 ms of CPU and 6–9 KB more per mail). Measurements,
349
+ formulas and limits: [docs/RESOURCES.md](docs/RESOURCES.md).
350
+
351
+ ## How it works
352
+
353
+ Hooks run a tiny client (`python -m ccmail hook`) that forwards the event to the local daemon and always exits 0. The
354
+ daemon applies the rules, writes to SQLite, and an outbox worker sends mail with retries. More in
355
+ [docs/HOW-IT-WORKS.md](docs/HOW-IT-WORKS.md).
356
+
357
+ ## Development
358
+
359
+ ```bash
360
+ git clone https://github.com/lobascioemanuele-droid/mail-notify && cd mail-notify
361
+ ```
362
+
363
+ ```bash
364
+ python -m venv .venv
365
+ ```
366
+
367
+ ```bash
368
+ . .venv/bin/activate
369
+ ```
370
+
371
+ On Windows, activate with `.venv\Scripts\activate` instead.
372
+
373
+ ```bash
374
+ pip install -e .
375
+ ```
376
+
377
+ ```bash
378
+ python -W ignore -m unittest discover -s tests
379
+ ```
380
+
381
+ Set `PYTHONPATH=src:tests` first if the package is not installed.
382
+
383
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Changes are listed in [CHANGELOG.md](CHANGELOG.md); what 1.x promises to keep
384
+ stable is in [docs/STABILITY.md](docs/STABILITY.md).
385
+
386
+ ## License
387
+
388
+ [MIT](LICENSE).