mxctl 0.3.0__tar.gz → 0.4.1__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.
- {mxctl-0.3.0 → mxctl-0.4.1}/.github/workflows/ci.yml +4 -4
- {mxctl-0.3.0 → mxctl-0.4.1}/.github/workflows/release.yml +8 -1
- {mxctl-0.3.0 → mxctl-0.4.1}/ARCHITECTURE.md +3 -3
- {mxctl-0.3.0 → mxctl-0.4.1}/CHANGELOG.md +33 -1
- {mxctl-0.3.0 → mxctl-0.4.1}/PKG-INFO +67 -40
- {mxctl-0.3.0 → mxctl-0.4.1}/README.md +66 -39
- mxctl-0.4.1/demo/preview.png +0 -0
- mxctl-0.4.1/demo/splash.png +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/pyproject.toml +1 -1
- mxctl-0.4.1/src/mxctl/api.py +188 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/accounts.py +96 -55
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/actions.py +202 -130
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/ai.py +111 -42
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/analytics.py +121 -73
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/attachments.py +69 -41
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/batch.py +208 -103
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/compose.py +57 -47
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/composite.py +155 -70
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/inbox_tools.py +134 -93
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/manage.py +75 -48
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/messages.py +130 -57
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/setup.py +175 -23
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/system.py +78 -33
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/templates.py +39 -29
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/todoist_integration.py +46 -19
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/undo.py +90 -74
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/config.py +15 -6
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/applescript.py +3 -5
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/applescript_templates.py +4 -15
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/mail_helpers.py +1 -1
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/conftest.py +1 -4
- mxctl-0.4.1/tests/test_100_coverage.py +1666 -0
- mxctl-0.4.1/tests/test_ai_classification.py +751 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_applescript.py +3 -3
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_commands.py +537 -168
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_compose_batch_errors.py +40 -61
- mxctl-0.4.1/tests/test_config.py +469 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_count.py +1 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_error_paths.py +69 -32
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_mail_helpers.py +1 -3
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_manage.py +13 -4
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_new_coverage.py +411 -115
- mxctl-0.4.1/tests/test_setup.py +1626 -0
- mxctl-0.4.1/tests/test_stats_undo.py +1002 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_templates.py +6 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_wave1_coverage.py +214 -65
- mxctl-0.3.0/tests/test_ai_classification.py +0 -413
- mxctl-0.3.0/tests/test_config.py +0 -93
- mxctl-0.3.0/tests/test_setup.py +0 -275
- mxctl-0.3.0/tests/test_stats_undo.py +0 -461
- {mxctl-0.3.0 → mxctl-0.4.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/.github/dependabot.yml +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/.gitignore +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/.pre-commit-config.yaml +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/CODE_OF_CONDUCT.md +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/CONTRIBUTING.md +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/LICENSE +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/SECURITY.md +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/demo/ai-demo.gif +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/demo/batch-delete-demo.gif +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/demo/demo.gif +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/demo/demo.tape +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/demo/init-demo.gif +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/demo/unsubscribe-demo.gif +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/__init__.py +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/__main__.py +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/__init__.py +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/__init__.py +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/main.py +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/__init__.py +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/dates.py +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/formatting.py +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/__init__.py +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_dates.py +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_formatting.py +0 -0
- {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_resolve_context.py +0 -0
|
@@ -10,8 +10,8 @@ jobs:
|
|
|
10
10
|
lint:
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
steps:
|
|
13
|
-
- uses: actions/checkout@
|
|
14
|
-
- uses: actions/setup-python@
|
|
13
|
+
- uses: actions/checkout@v6
|
|
14
|
+
- uses: actions/setup-python@v6
|
|
15
15
|
with:
|
|
16
16
|
python-version: "3.12"
|
|
17
17
|
- run: pip install ruff
|
|
@@ -23,8 +23,8 @@ jobs:
|
|
|
23
23
|
matrix:
|
|
24
24
|
python-version: ["3.10", "3.11", "3.12"]
|
|
25
25
|
steps:
|
|
26
|
-
- uses: actions/checkout@
|
|
27
|
-
- uses: actions/setup-python@
|
|
26
|
+
- uses: actions/checkout@v6
|
|
27
|
+
- uses: actions/setup-python@v6
|
|
28
28
|
with:
|
|
29
29
|
python-version: ${{ matrix.python-version }}
|
|
30
30
|
- run: pip install -e ".[dev]"
|
|
@@ -7,12 +7,13 @@ on:
|
|
|
7
7
|
|
|
8
8
|
permissions:
|
|
9
9
|
contents: write
|
|
10
|
+
id-token: write
|
|
10
11
|
|
|
11
12
|
jobs:
|
|
12
13
|
release:
|
|
13
14
|
runs-on: ubuntu-latest
|
|
14
15
|
steps:
|
|
15
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@v6
|
|
16
17
|
|
|
17
18
|
- name: Extract version from tag
|
|
18
19
|
id: version
|
|
@@ -34,3 +35,9 @@ jobs:
|
|
|
34
35
|
body_path: /tmp/release-notes.md
|
|
35
36
|
draft: false
|
|
36
37
|
prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') }}
|
|
38
|
+
|
|
39
|
+
- name: Build package
|
|
40
|
+
run: pip install build && python -m build
|
|
41
|
+
|
|
42
|
+
- name: Publish to PyPI
|
|
43
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -33,7 +33,7 @@ src/mxctl/
|
|
|
33
33
|
├── manage.py # create-mailbox, delete-mailbox, empty-trash
|
|
34
34
|
├── batch.py # batch-read, batch-flag, batch-move, batch-delete
|
|
35
35
|
├── analytics.py # stats, top-senders, digest, show-flagged
|
|
36
|
-
├── setup.py # init
|
|
36
|
+
├── setup.py # init, ai-setup (setup wizards)
|
|
37
37
|
├── system.py # check, headers, rules, junk, not-junk
|
|
38
38
|
├── composite.py # export, thread, reply, forward
|
|
39
39
|
├── ai.py # summary, triage, context, find-related
|
|
@@ -85,6 +85,6 @@ Batch commands (`batch-read`, `batch-move`, `batch-delete`, `batch-flag`) log th
|
|
|
85
85
|
|
|
86
86
|
## Testing
|
|
87
87
|
|
|
88
|
-
Tests live in `tests/` and use `unittest.mock` to mock AppleScript calls. No actual Mail.app interaction happens during testing. Run with `pytest
|
|
88
|
+
Tests live in `tests/` and use `unittest.mock` to mock AppleScript calls. No actual Mail.app interaction happens during testing. Run with `pytest --cov` for coverage.
|
|
89
89
|
|
|
90
|
-
The suite has
|
|
90
|
+
The suite has 678 tests (100% coverage) across 19 test files covering command parsing, AppleScript output parsing, error paths, date handling, formatting, config resolution, batch operations, undo logging, templates, AI classification logic, unsubscribe HTTP paths, Todoist integration, inbox tools, bulk export, and the public API module. Six unreachable defensive guards are marked with `# pragma: no cover`.
|
|
@@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.4.1] - 2026-02-27
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Public Python API** — new `mxctl.api` module with 56 importable functions for programmatic access to Apple Mail. All command internals refactored to separate data retrieval from CLI presentation. External projects can now `from mxctl.api import get_messages, read_message` without any CLI or argparse dependency.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- README cleanup — removed personal email from examples, trimmed redundant sections, tightened AI workflow documentation (488 → 434 lines)
|
|
18
|
+
|
|
19
|
+
## [0.4.0] - 2026-02-25
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- `ai-setup` command — interactive wizard to configure Claude Code, Cursor, or Windsurf to use mxctl; detects existing config files, previews the snippet before writing, supports `--json`
|
|
24
|
+
- `ai-setup --print` flag — dump the raw snippet to stdout for piping into Ollama Modelfiles, Aider prompts, system prompt files, or clipboard
|
|
25
|
+
- "Pointing Your AI Assistant to mxctl" section in README — `mxctl ai-setup` walkthrough plus manual snippet for Claude Code, Cursor, Windsurf, and local AI tools
|
|
26
|
+
|
|
9
27
|
## [0.3.0] - 2026-02-24
|
|
10
28
|
|
|
11
29
|
### Changed
|
|
@@ -16,7 +34,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
16
34
|
- **Version bump** — 0.2.0 → 0.3.0 (breaking: new binary name and command structure)
|
|
17
35
|
- **Package name** — Python package renamed from `my_cli` to `mxctl`
|
|
18
36
|
- **GitHub repo** — `Jscoats/my-apple-mail-cli` → `Jscoats/mxctl`
|
|
19
|
-
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- **Published to PyPI** — `pip install mxctl` now works
|
|
41
|
+
- **100% test coverage** — 655 tests across 20 test files (up from 422), measured with pytest-cov
|
|
42
|
+
- **5 demo GIFs** — main commands, init wizard, AI triage, batch delete, newsletter unsubscribe (all scripted with fictional data)
|
|
43
|
+
- **Feature comparison table** — "Why Not X?" section with mxctl vs mutt vs Gmail API vs raw AppleScript
|
|
44
|
+
- **Automated release workflow** — `release.yml` creates GitHub Release with changelog on tag push
|
|
45
|
+
- **Pre-commit hooks** — ruff check + ruff format run on every commit
|
|
46
|
+
- **Dependabot** — weekly updates for pip and GitHub Actions dependencies
|
|
47
|
+
- **pytest-cov** — coverage reporting in CI and locally
|
|
48
|
+
- **Ruff config** — `[tool.ruff]` in pyproject.toml with 7 rule sets (E, F, W, I, UP, B, SIM)
|
|
49
|
+
- **PyPI classifiers** — 12 classifiers and 7 keywords for discoverability
|
|
50
|
+
- **Centered README header** — styled `<h1>` with 5 badges (PyPI, CI, Python, coverage, license)
|
|
51
|
+
- **README table of contents** — 14 sections linked
|
|
20
52
|
|
|
21
53
|
## [0.2.0] - 2026-02-22
|
|
22
54
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mxctl
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Apple Mail from your terminal
|
|
5
5
|
Project-URL: Homepage, https://github.com/Jscoats/mxctl
|
|
6
6
|
Project-URL: Repository, https://github.com/Jscoats/mxctl
|
|
@@ -30,16 +30,25 @@ Requires-Dist: pytest-cov; extra == 'dev'
|
|
|
30
30
|
Requires-Dist: ruff; extra == 'dev'
|
|
31
31
|
Description-Content-Type: text/markdown
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
<p align="center">
|
|
34
|
+
<img src="demo/splash.png" alt="mxctl — Apple Mail from your terminal" width="700">
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
<h1 align="center">
|
|
38
|
+
<code>mxctl</code>
|
|
39
|
+
</h1>
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
<p align="center">
|
|
42
|
+
<a href="https://pypi.org/project/mxctl/"><img src="https://img.shields.io/pypi/v/mxctl.svg" alt="PyPI"></a>
|
|
43
|
+
<a href="https://github.com/Jscoats/mxctl/actions/workflows/ci.yml"><img src="https://github.com/Jscoats/mxctl/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
44
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="Python 3.10+"></a>
|
|
45
|
+
<a href="https://github.com/Jscoats/mxctl"><img src="https://img.shields.io/badge/coverage-100%25-brightgreen.svg" alt="Coverage: 100%"></a>
|
|
46
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License: MIT"></a>
|
|
47
|
+
</p>
|
|
39
48
|
|
|
40
49
|
> Apple Mail from your terminal.
|
|
41
50
|
|
|
42
|
-
**
|
|
51
|
+
**50+ commands.** Triage with AI, batch-process newsletters, turn emails into Todoist tasks — all from the terminal. Every command supports `--json` for scripting and AI workflows. Zero external dependencies.
|
|
43
52
|
|
|
44
53
|
<p align="center">
|
|
45
54
|
<img src="demo/demo.gif" alt="mxctl demo — inbox, triage, summary, and batch operations" width="700">
|
|
@@ -62,7 +71,7 @@ Description-Content-Type: text/markdown
|
|
|
62
71
|
|
|
63
72
|
## Key Features
|
|
64
73
|
|
|
65
|
-
- **
|
|
74
|
+
- **50+ Commands** - Everything from basic operations to advanced batch processing
|
|
66
75
|
- **Any Account, One Interface** - iCloud, Gmail, Outlook, Exchange, IMAP -- whatever Mail.app has, this works with
|
|
67
76
|
- **Gmail Mailbox Translation** - Automatically maps standard names (`Trash`, `Spam`, `Sent`) to Gmail's `[Gmail]/...` paths
|
|
68
77
|
- **Built for AI Workflows** - Every command supports `--json` output designed for AI assistants to read and act on
|
|
@@ -75,10 +84,10 @@ Description-Content-Type: text/markdown
|
|
|
75
84
|
|
|
76
85
|
```bash
|
|
77
86
|
# Requires Python 3.10+ and macOS
|
|
78
|
-
pip install
|
|
87
|
+
pip install mxctl
|
|
79
88
|
|
|
80
89
|
# Or with uv (faster)
|
|
81
|
-
uv tool install
|
|
90
|
+
uv tool install mxctl
|
|
82
91
|
```
|
|
83
92
|
|
|
84
93
|
Then run the setup wizard — it detects your Mail.app accounts, configures Gmail mailbox translation, and optionally connects Todoist:
|
|
@@ -126,7 +135,7 @@ Inbox Overview
|
|
|
126
135
|
------------------------------------------
|
|
127
136
|
iCloud 3 unread (47 total)
|
|
128
137
|
Work Email 12 unread (203 total)
|
|
129
|
-
|
|
138
|
+
Personal Gmail 0 unread (18 total)
|
|
130
139
|
------------------------------------------
|
|
131
140
|
Total 15 unread
|
|
132
141
|
```
|
|
@@ -169,6 +178,7 @@ Triage -- 15 Unread Messages
|
|
|
169
178
|
|
|
170
179
|
### Setup
|
|
171
180
|
- `init` - First-time setup wizard (auto-detects Mail accounts, configures default account and optional Todoist token)
|
|
181
|
+
- `ai-setup` - Configure your AI assistant to use mxctl (Claude Code, Cursor, Windsurf, Ollama, and others)
|
|
172
182
|
|
|
173
183
|
### Account & Mailbox Management
|
|
174
184
|
- `inbox` - Overview of all accounts with unread counts
|
|
@@ -304,13 +314,6 @@ mxctl move 3 --to Archive # Move message [3]
|
|
|
304
314
|
|
|
305
315
|
Aliases update each time you run a listing command (`list`, `inbox`, `search`, `triage`, `summary`, etc.). Full message IDs still work if you prefer them. JSON output includes both `id` (real) and `alias` (short number).
|
|
306
316
|
|
|
307
|
-
### JSON Output for Automation
|
|
308
|
-
```bash
|
|
309
|
-
# Every command supports --json
|
|
310
|
-
mxctl inbox --json | jq '.accounts[0].unread_count'
|
|
311
|
-
mxctl search "invoice" --json | jq '.[].subject'
|
|
312
|
-
```
|
|
313
|
-
|
|
314
317
|
### Export Messages
|
|
315
318
|
```bash
|
|
316
319
|
# Export a single message
|
|
@@ -323,8 +326,6 @@ mxctl export "Work" --to ~/Documents/mail/ -a "Work Email"
|
|
|
323
326
|
mxctl export "INBOX" --to ~/Documents/mail/ -a "iCloud" --after 2026-01-01
|
|
324
327
|
```
|
|
325
328
|
|
|
326
|
-
Note: The destination flag is `--to` (not `--dest`).
|
|
327
|
-
|
|
328
329
|
### Email Templates
|
|
329
330
|
```bash
|
|
330
331
|
# Create a template
|
|
@@ -340,13 +341,40 @@ mxctl draft --to client@company.com --template "meeting-followup"
|
|
|
340
341
|
|
|
341
342
|
Every command supports `--json`, making your inbox data available to any AI assistant. Commands like `summary`, `triage`, and `context` are specifically designed to give AI a structured understanding of your inbox in seconds.
|
|
342
343
|
|
|
343
|
-
###
|
|
344
|
+
### Pointing Your AI Assistant to mxctl
|
|
345
|
+
|
|
346
|
+
For an AI assistant to use mxctl effectively, it needs to know the tool is available. The fastest way is the built-in setup command:
|
|
347
|
+
|
|
344
348
|
```bash
|
|
345
|
-
|
|
346
|
-
"Run mxctl triage and tell me what's urgent"
|
|
347
|
-
"Summarize my unread mail and create Todoist tasks for anything that needs action"
|
|
349
|
+
mxctl ai-setup
|
|
348
350
|
```
|
|
349
351
|
|
|
352
|
+
It walks you through selecting your AI assistant (Claude Code, Cursor, or Windsurf), previews the snippet it will write, and asks for confirmation before touching any file. Run it once; skip it any time you don't need it.
|
|
353
|
+
|
|
354
|
+
#### Manual setup
|
|
355
|
+
|
|
356
|
+
Prefer to configure it yourself? Run `mxctl ai-setup --print` to get the raw snippet, then paste it into your assistant's context file (`~/.claude/CLAUDE.md`, `.cursorrules`, `.windsurfrules`, etc.).
|
|
357
|
+
|
|
358
|
+
#### Local AI (Ollama, LM Studio, Aider, etc.)
|
|
359
|
+
|
|
360
|
+
Use `--print` to get the raw snippet for piping:
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
mxctl ai-setup --print | pbcopy # clipboard
|
|
364
|
+
mxctl ai-setup --print >> ~/Modelfile # Ollama
|
|
365
|
+
mxctl ai-setup --print > .aider.prompt.md # Aider
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
For a one-off session, `mxctl --help` gives any AI the full command reference.
|
|
369
|
+
|
|
370
|
+
### With Claude Code
|
|
371
|
+
|
|
372
|
+
Just ask in natural language:
|
|
373
|
+
|
|
374
|
+
> *"Run mxctl triage and tell me what's urgent"*
|
|
375
|
+
>
|
|
376
|
+
> *"Summarize my unread mail and create Todoist tasks for anything that needs action"*
|
|
377
|
+
|
|
350
378
|
### With any AI tool
|
|
351
379
|
```bash
|
|
352
380
|
# Pipe structured data to any LLM CLI
|
|
@@ -361,12 +389,11 @@ mxctl triage --json | llm "Draft responses for the urgent items"
|
|
|
361
389
|
# Unread count for your status bar
|
|
362
390
|
mxctl count
|
|
363
391
|
|
|
364
|
-
#
|
|
365
|
-
mxctl inbox --json | jq '.
|
|
392
|
+
# Every command supports --json
|
|
393
|
+
mxctl inbox --json | jq '.[].unread'
|
|
394
|
+
mxctl search "invoice" --json | jq '.[].subject'
|
|
366
395
|
```
|
|
367
396
|
|
|
368
|
-
The CLI is the bridge between Mail.app and whatever tools you use -- AI, scripts, or both.
|
|
369
|
-
|
|
370
397
|
## AI Demos
|
|
371
398
|
|
|
372
399
|
These demos show how an AI assistant (like Claude Code) uses mxctl to manage your inbox conversationally. You say what you want in plain English, and the AI picks the right commands, checks before acting, and reports back.
|
|
@@ -399,7 +426,7 @@ The AI analyzes which newsletters you actually read vs. ignore, then unsubscribe
|
|
|
399
426
|
|
|
400
427
|
Built with modern Python patterns:
|
|
401
428
|
- **Zero runtime dependencies** (stdlib only)
|
|
402
|
-
- **Comprehensive test suite** (
|
|
429
|
+
- **Comprehensive test suite** (678 tests)
|
|
403
430
|
- **Modular command structure** (16 focused modules)
|
|
404
431
|
- **AppleScript bridge** for Mail.app communication
|
|
405
432
|
- **Three-tier account resolution** (explicit flag -> config default -> last-used)
|
|
@@ -408,14 +435,18 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed architecture documentation.
|
|
|
408
435
|
|
|
409
436
|
## Why Not X?
|
|
410
437
|
|
|
411
|
-
**
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
**
|
|
415
|
-
|
|
438
|
+
| | **mxctl** | **mutt/neomutt** | **Gmail/Outlook API** | **Raw AppleScript** |
|
|
439
|
+
|---|---|---|---|---|
|
|
440
|
+
| **Approach** | Extends Mail.app | Replaces Mail.app | Per-provider SDK | DIY scripting |
|
|
441
|
+
| **Multi-account** | Any account in Mail.app | Config per account | Separate auth per provider | Manual per account |
|
|
442
|
+
| **macOS integration** | Notifications, Rules, Continuity | None (terminal-only) | None | Partial |
|
|
443
|
+
| **Structured output** | `--json` on every command | Text only | JSON (provider-specific) | Raw text |
|
|
444
|
+
| **AI-ready** | Built for it (triage, summary) | No | Build it yourself | No |
|
|
445
|
+
| **Batch + undo** | Built-in with rollback | No undo | Build it yourself | No |
|
|
446
|
+
| **Setup** | `pip install mxctl && mxctl init` | Extensive config | OAuth flows + API keys | Write your own scripts |
|
|
447
|
+
| **Dependencies** | Zero (stdlib only) | Varies | SDK + auth libraries | None |
|
|
416
448
|
|
|
417
|
-
**
|
|
418
|
-
You could wire this up yourself -- but this gives you 49 structured commands with `--json` output, batch operations with undo, template support, Todoist integration, and an AI-ready interface, all without writing a single line of AppleScript. The hard parts (field parsing, timeout handling, account resolution, error recovery) are already done.
|
|
449
|
+
**In short:** mutt replaces Mail.app (you lose macOS integration). Provider APIs lock you into one service. Raw AppleScript works but you're building everything from scratch. mxctl gives you 50+ structured commands on top of the Mail.app you already use.
|
|
419
450
|
|
|
420
451
|
## Contributing
|
|
421
452
|
|
|
@@ -425,10 +456,6 @@ Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
|
425
456
|
|
|
426
457
|
MIT License - see [LICENSE](LICENSE) file for details.
|
|
427
458
|
|
|
428
|
-
## Acknowledgments
|
|
429
|
-
|
|
430
|
-
Built to automate email workflows without leaving the terminal.
|
|
431
|
-
|
|
432
459
|
## Contact
|
|
433
460
|
|
|
434
461
|
- **GitHub:** [@Jscoats](https://github.com/Jscoats)
|
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="demo/splash.png" alt="mxctl — Apple Mail from your terminal" width="700">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">
|
|
6
|
+
<code>mxctl</code>
|
|
7
|
+
</h1>
|
|
2
8
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://pypi.org/project/mxctl/"><img src="https://img.shields.io/pypi/v/mxctl.svg" alt="PyPI"></a>
|
|
11
|
+
<a href="https://github.com/Jscoats/mxctl/actions/workflows/ci.yml"><img src="https://github.com/Jscoats/mxctl/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
12
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="Python 3.10+"></a>
|
|
13
|
+
<a href="https://github.com/Jscoats/mxctl"><img src="https://img.shields.io/badge/coverage-100%25-brightgreen.svg" alt="Coverage: 100%"></a>
|
|
14
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License: MIT"></a>
|
|
15
|
+
</p>
|
|
7
16
|
|
|
8
17
|
> Apple Mail from your terminal.
|
|
9
18
|
|
|
10
|
-
**
|
|
19
|
+
**50+ commands.** Triage with AI, batch-process newsletters, turn emails into Todoist tasks — all from the terminal. Every command supports `--json` for scripting and AI workflows. Zero external dependencies.
|
|
11
20
|
|
|
12
21
|
<p align="center">
|
|
13
22
|
<img src="demo/demo.gif" alt="mxctl demo — inbox, triage, summary, and batch operations" width="700">
|
|
@@ -30,7 +39,7 @@
|
|
|
30
39
|
|
|
31
40
|
## Key Features
|
|
32
41
|
|
|
33
|
-
- **
|
|
42
|
+
- **50+ Commands** - Everything from basic operations to advanced batch processing
|
|
34
43
|
- **Any Account, One Interface** - iCloud, Gmail, Outlook, Exchange, IMAP -- whatever Mail.app has, this works with
|
|
35
44
|
- **Gmail Mailbox Translation** - Automatically maps standard names (`Trash`, `Spam`, `Sent`) to Gmail's `[Gmail]/...` paths
|
|
36
45
|
- **Built for AI Workflows** - Every command supports `--json` output designed for AI assistants to read and act on
|
|
@@ -43,10 +52,10 @@
|
|
|
43
52
|
|
|
44
53
|
```bash
|
|
45
54
|
# Requires Python 3.10+ and macOS
|
|
46
|
-
pip install
|
|
55
|
+
pip install mxctl
|
|
47
56
|
|
|
48
57
|
# Or with uv (faster)
|
|
49
|
-
uv tool install
|
|
58
|
+
uv tool install mxctl
|
|
50
59
|
```
|
|
51
60
|
|
|
52
61
|
Then run the setup wizard — it detects your Mail.app accounts, configures Gmail mailbox translation, and optionally connects Todoist:
|
|
@@ -94,7 +103,7 @@ Inbox Overview
|
|
|
94
103
|
------------------------------------------
|
|
95
104
|
iCloud 3 unread (47 total)
|
|
96
105
|
Work Email 12 unread (203 total)
|
|
97
|
-
|
|
106
|
+
Personal Gmail 0 unread (18 total)
|
|
98
107
|
------------------------------------------
|
|
99
108
|
Total 15 unread
|
|
100
109
|
```
|
|
@@ -137,6 +146,7 @@ Triage -- 15 Unread Messages
|
|
|
137
146
|
|
|
138
147
|
### Setup
|
|
139
148
|
- `init` - First-time setup wizard (auto-detects Mail accounts, configures default account and optional Todoist token)
|
|
149
|
+
- `ai-setup` - Configure your AI assistant to use mxctl (Claude Code, Cursor, Windsurf, Ollama, and others)
|
|
140
150
|
|
|
141
151
|
### Account & Mailbox Management
|
|
142
152
|
- `inbox` - Overview of all accounts with unread counts
|
|
@@ -272,13 +282,6 @@ mxctl move 3 --to Archive # Move message [3]
|
|
|
272
282
|
|
|
273
283
|
Aliases update each time you run a listing command (`list`, `inbox`, `search`, `triage`, `summary`, etc.). Full message IDs still work if you prefer them. JSON output includes both `id` (real) and `alias` (short number).
|
|
274
284
|
|
|
275
|
-
### JSON Output for Automation
|
|
276
|
-
```bash
|
|
277
|
-
# Every command supports --json
|
|
278
|
-
mxctl inbox --json | jq '.accounts[0].unread_count'
|
|
279
|
-
mxctl search "invoice" --json | jq '.[].subject'
|
|
280
|
-
```
|
|
281
|
-
|
|
282
285
|
### Export Messages
|
|
283
286
|
```bash
|
|
284
287
|
# Export a single message
|
|
@@ -291,8 +294,6 @@ mxctl export "Work" --to ~/Documents/mail/ -a "Work Email"
|
|
|
291
294
|
mxctl export "INBOX" --to ~/Documents/mail/ -a "iCloud" --after 2026-01-01
|
|
292
295
|
```
|
|
293
296
|
|
|
294
|
-
Note: The destination flag is `--to` (not `--dest`).
|
|
295
|
-
|
|
296
297
|
### Email Templates
|
|
297
298
|
```bash
|
|
298
299
|
# Create a template
|
|
@@ -308,13 +309,40 @@ mxctl draft --to client@company.com --template "meeting-followup"
|
|
|
308
309
|
|
|
309
310
|
Every command supports `--json`, making your inbox data available to any AI assistant. Commands like `summary`, `triage`, and `context` are specifically designed to give AI a structured understanding of your inbox in seconds.
|
|
310
311
|
|
|
311
|
-
###
|
|
312
|
+
### Pointing Your AI Assistant to mxctl
|
|
313
|
+
|
|
314
|
+
For an AI assistant to use mxctl effectively, it needs to know the tool is available. The fastest way is the built-in setup command:
|
|
315
|
+
|
|
312
316
|
```bash
|
|
313
|
-
|
|
314
|
-
"Run mxctl triage and tell me what's urgent"
|
|
315
|
-
"Summarize my unread mail and create Todoist tasks for anything that needs action"
|
|
317
|
+
mxctl ai-setup
|
|
316
318
|
```
|
|
317
319
|
|
|
320
|
+
It walks you through selecting your AI assistant (Claude Code, Cursor, or Windsurf), previews the snippet it will write, and asks for confirmation before touching any file. Run it once; skip it any time you don't need it.
|
|
321
|
+
|
|
322
|
+
#### Manual setup
|
|
323
|
+
|
|
324
|
+
Prefer to configure it yourself? Run `mxctl ai-setup --print` to get the raw snippet, then paste it into your assistant's context file (`~/.claude/CLAUDE.md`, `.cursorrules`, `.windsurfrules`, etc.).
|
|
325
|
+
|
|
326
|
+
#### Local AI (Ollama, LM Studio, Aider, etc.)
|
|
327
|
+
|
|
328
|
+
Use `--print` to get the raw snippet for piping:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
mxctl ai-setup --print | pbcopy # clipboard
|
|
332
|
+
mxctl ai-setup --print >> ~/Modelfile # Ollama
|
|
333
|
+
mxctl ai-setup --print > .aider.prompt.md # Aider
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
For a one-off session, `mxctl --help` gives any AI the full command reference.
|
|
337
|
+
|
|
338
|
+
### With Claude Code
|
|
339
|
+
|
|
340
|
+
Just ask in natural language:
|
|
341
|
+
|
|
342
|
+
> *"Run mxctl triage and tell me what's urgent"*
|
|
343
|
+
>
|
|
344
|
+
> *"Summarize my unread mail and create Todoist tasks for anything that needs action"*
|
|
345
|
+
|
|
318
346
|
### With any AI tool
|
|
319
347
|
```bash
|
|
320
348
|
# Pipe structured data to any LLM CLI
|
|
@@ -329,12 +357,11 @@ mxctl triage --json | llm "Draft responses for the urgent items"
|
|
|
329
357
|
# Unread count for your status bar
|
|
330
358
|
mxctl count
|
|
331
359
|
|
|
332
|
-
#
|
|
333
|
-
mxctl inbox --json | jq '.
|
|
360
|
+
# Every command supports --json
|
|
361
|
+
mxctl inbox --json | jq '.[].unread'
|
|
362
|
+
mxctl search "invoice" --json | jq '.[].subject'
|
|
334
363
|
```
|
|
335
364
|
|
|
336
|
-
The CLI is the bridge between Mail.app and whatever tools you use -- AI, scripts, or both.
|
|
337
|
-
|
|
338
365
|
## AI Demos
|
|
339
366
|
|
|
340
367
|
These demos show how an AI assistant (like Claude Code) uses mxctl to manage your inbox conversationally. You say what you want in plain English, and the AI picks the right commands, checks before acting, and reports back.
|
|
@@ -367,7 +394,7 @@ The AI analyzes which newsletters you actually read vs. ignore, then unsubscribe
|
|
|
367
394
|
|
|
368
395
|
Built with modern Python patterns:
|
|
369
396
|
- **Zero runtime dependencies** (stdlib only)
|
|
370
|
-
- **Comprehensive test suite** (
|
|
397
|
+
- **Comprehensive test suite** (678 tests)
|
|
371
398
|
- **Modular command structure** (16 focused modules)
|
|
372
399
|
- **AppleScript bridge** for Mail.app communication
|
|
373
400
|
- **Three-tier account resolution** (explicit flag -> config default -> last-used)
|
|
@@ -376,14 +403,18 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed architecture documentation.
|
|
|
376
403
|
|
|
377
404
|
## Why Not X?
|
|
378
405
|
|
|
379
|
-
**
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
**
|
|
383
|
-
|
|
406
|
+
| | **mxctl** | **mutt/neomutt** | **Gmail/Outlook API** | **Raw AppleScript** |
|
|
407
|
+
|---|---|---|---|---|
|
|
408
|
+
| **Approach** | Extends Mail.app | Replaces Mail.app | Per-provider SDK | DIY scripting |
|
|
409
|
+
| **Multi-account** | Any account in Mail.app | Config per account | Separate auth per provider | Manual per account |
|
|
410
|
+
| **macOS integration** | Notifications, Rules, Continuity | None (terminal-only) | None | Partial |
|
|
411
|
+
| **Structured output** | `--json` on every command | Text only | JSON (provider-specific) | Raw text |
|
|
412
|
+
| **AI-ready** | Built for it (triage, summary) | No | Build it yourself | No |
|
|
413
|
+
| **Batch + undo** | Built-in with rollback | No undo | Build it yourself | No |
|
|
414
|
+
| **Setup** | `pip install mxctl && mxctl init` | Extensive config | OAuth flows + API keys | Write your own scripts |
|
|
415
|
+
| **Dependencies** | Zero (stdlib only) | Varies | SDK + auth libraries | None |
|
|
384
416
|
|
|
385
|
-
**
|
|
386
|
-
You could wire this up yourself -- but this gives you 49 structured commands with `--json` output, batch operations with undo, template support, Todoist integration, and an AI-ready interface, all without writing a single line of AppleScript. The hard parts (field parsing, timeout handling, account resolution, error recovery) are already done.
|
|
417
|
+
**In short:** mutt replaces Mail.app (you lose macOS integration). Provider APIs lock you into one service. Raw AppleScript works but you're building everything from scratch. mxctl gives you 50+ structured commands on top of the Mail.app you already use.
|
|
387
418
|
|
|
388
419
|
## Contributing
|
|
389
420
|
|
|
@@ -393,10 +424,6 @@ Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
|
393
424
|
|
|
394
425
|
MIT License - see [LICENSE](LICENSE) file for details.
|
|
395
426
|
|
|
396
|
-
## Acknowledgments
|
|
397
|
-
|
|
398
|
-
Built to automate email workflows without leaving the terminal.
|
|
399
|
-
|
|
400
427
|
## Contact
|
|
401
428
|
|
|
402
429
|
- **GitHub:** [@Jscoats](https://github.com/Jscoats)
|
|
Binary file
|
|
Binary file
|