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.
Files changed (78) hide show
  1. {mxctl-0.3.0 → mxctl-0.4.1}/.github/workflows/ci.yml +4 -4
  2. {mxctl-0.3.0 → mxctl-0.4.1}/.github/workflows/release.yml +8 -1
  3. {mxctl-0.3.0 → mxctl-0.4.1}/ARCHITECTURE.md +3 -3
  4. {mxctl-0.3.0 → mxctl-0.4.1}/CHANGELOG.md +33 -1
  5. {mxctl-0.3.0 → mxctl-0.4.1}/PKG-INFO +67 -40
  6. {mxctl-0.3.0 → mxctl-0.4.1}/README.md +66 -39
  7. mxctl-0.4.1/demo/preview.png +0 -0
  8. mxctl-0.4.1/demo/splash.png +0 -0
  9. {mxctl-0.3.0 → mxctl-0.4.1}/pyproject.toml +1 -1
  10. mxctl-0.4.1/src/mxctl/api.py +188 -0
  11. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/accounts.py +96 -55
  12. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/actions.py +202 -130
  13. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/ai.py +111 -42
  14. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/analytics.py +121 -73
  15. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/attachments.py +69 -41
  16. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/batch.py +208 -103
  17. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/compose.py +57 -47
  18. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/composite.py +155 -70
  19. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/inbox_tools.py +134 -93
  20. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/manage.py +75 -48
  21. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/messages.py +130 -57
  22. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/setup.py +175 -23
  23. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/system.py +78 -33
  24. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/templates.py +39 -29
  25. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/todoist_integration.py +46 -19
  26. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/undo.py +90 -74
  27. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/config.py +15 -6
  28. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/applescript.py +3 -5
  29. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/applescript_templates.py +4 -15
  30. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/mail_helpers.py +1 -1
  31. {mxctl-0.3.0 → mxctl-0.4.1}/tests/conftest.py +1 -4
  32. mxctl-0.4.1/tests/test_100_coverage.py +1666 -0
  33. mxctl-0.4.1/tests/test_ai_classification.py +751 -0
  34. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_applescript.py +3 -3
  35. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_commands.py +537 -168
  36. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_compose_batch_errors.py +40 -61
  37. mxctl-0.4.1/tests/test_config.py +469 -0
  38. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_count.py +1 -0
  39. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_error_paths.py +69 -32
  40. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_mail_helpers.py +1 -3
  41. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_manage.py +13 -4
  42. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_new_coverage.py +411 -115
  43. mxctl-0.4.1/tests/test_setup.py +1626 -0
  44. mxctl-0.4.1/tests/test_stats_undo.py +1002 -0
  45. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_templates.py +6 -0
  46. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_wave1_coverage.py +214 -65
  47. mxctl-0.3.0/tests/test_ai_classification.py +0 -413
  48. mxctl-0.3.0/tests/test_config.py +0 -93
  49. mxctl-0.3.0/tests/test_setup.py +0 -275
  50. mxctl-0.3.0/tests/test_stats_undo.py +0 -461
  51. {mxctl-0.3.0 → mxctl-0.4.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  52. {mxctl-0.3.0 → mxctl-0.4.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  53. {mxctl-0.3.0 → mxctl-0.4.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  54. {mxctl-0.3.0 → mxctl-0.4.1}/.github/dependabot.yml +0 -0
  55. {mxctl-0.3.0 → mxctl-0.4.1}/.gitignore +0 -0
  56. {mxctl-0.3.0 → mxctl-0.4.1}/.pre-commit-config.yaml +0 -0
  57. {mxctl-0.3.0 → mxctl-0.4.1}/CODE_OF_CONDUCT.md +0 -0
  58. {mxctl-0.3.0 → mxctl-0.4.1}/CONTRIBUTING.md +0 -0
  59. {mxctl-0.3.0 → mxctl-0.4.1}/LICENSE +0 -0
  60. {mxctl-0.3.0 → mxctl-0.4.1}/SECURITY.md +0 -0
  61. {mxctl-0.3.0 → mxctl-0.4.1}/demo/ai-demo.gif +0 -0
  62. {mxctl-0.3.0 → mxctl-0.4.1}/demo/batch-delete-demo.gif +0 -0
  63. {mxctl-0.3.0 → mxctl-0.4.1}/demo/demo.gif +0 -0
  64. {mxctl-0.3.0 → mxctl-0.4.1}/demo/demo.tape +0 -0
  65. {mxctl-0.3.0 → mxctl-0.4.1}/demo/init-demo.gif +0 -0
  66. {mxctl-0.3.0 → mxctl-0.4.1}/demo/unsubscribe-demo.gif +0 -0
  67. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/__init__.py +0 -0
  68. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/__main__.py +0 -0
  69. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/__init__.py +0 -0
  70. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/commands/mail/__init__.py +0 -0
  71. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/main.py +0 -0
  72. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/__init__.py +0 -0
  73. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/dates.py +0 -0
  74. {mxctl-0.3.0 → mxctl-0.4.1}/src/mxctl/util/formatting.py +0 -0
  75. {mxctl-0.3.0 → mxctl-0.4.1}/tests/__init__.py +0 -0
  76. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_dates.py +0 -0
  77. {mxctl-0.3.0 → mxctl-0.4.1}/tests/test_formatting.py +0 -0
  78. {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@v4
14
- - uses: actions/setup-python@v5
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@v4
27
- - uses: actions/setup-python@v5
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@v4
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 (first-time setup wizard)
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 348 tests across 17 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, and bulk export.
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
- - Test suite expanded to 422 tests
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.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
- # mxctl
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
- [![CI](https://github.com/Jscoats/mxctl/actions/workflows/ci.yml/badge.svg)](https://github.com/Jscoats/mxctl/actions/workflows/ci.yml)
36
- [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
37
- [![Coverage: 87%](https://img.shields.io/badge/coverage-87%25-yellowgreen.svg)](https://github.com/Jscoats/mxctl)
38
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
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
- **49 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.
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
- - **49 Commands** - Everything from basic operations to advanced batch processing
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 git+https://github.com/Jscoats/mxctl
87
+ pip install mxctl
79
88
 
80
89
  # Or with uv (faster)
81
- uv tool install git+https://github.com/Jscoats/mxctl
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
- Johnny.Coats84@gmail.com 0 unread (18 total)
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
- ### With Claude Code
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
- # Just ask Claude to check your mail
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
- # Export to JSON for any workflow
365
- mxctl inbox --json | jq '.accounts[].unread_count'
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** (422 tests)
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
- **Why not mutt or neomutt?**
412
- Mutt replaces Mail.app -- you lose native macOS notifications, calendar event detection, FaceTime/iMessage continuity, and Rules. This CLI *extends* Mail.app rather than replacing it: your mail is still managed natively, but now also scriptable from the terminal.
413
-
414
- **Why not the Gmail API or Outlook API?**
415
- Those are per-provider -- separate SDKs, separate auth flows, separate data models. `mxctl` works with any account configured in Mail.app (iCloud, Gmail, Outlook, Exchange, custom IMAP) through a single unified interface. Add a new account to Mail.app and it just works.
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
- **Why not raw AppleScript or Hammerspoon?**
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
- # mxctl
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
- [![CI](https://github.com/Jscoats/mxctl/actions/workflows/ci.yml/badge.svg)](https://github.com/Jscoats/mxctl/actions/workflows/ci.yml)
4
- [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
5
- [![Coverage: 87%](https://img.shields.io/badge/coverage-87%25-yellowgreen.svg)](https://github.com/Jscoats/mxctl)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
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
- **49 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.
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
- - **49 Commands** - Everything from basic operations to advanced batch processing
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 git+https://github.com/Jscoats/mxctl
55
+ pip install mxctl
47
56
 
48
57
  # Or with uv (faster)
49
- uv tool install git+https://github.com/Jscoats/mxctl
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
- Johnny.Coats84@gmail.com 0 unread (18 total)
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
- ### With Claude Code
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
- # Just ask Claude to check your mail
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
- # Export to JSON for any workflow
333
- mxctl inbox --json | jq '.accounts[].unread_count'
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** (422 tests)
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
- **Why not mutt or neomutt?**
380
- Mutt replaces Mail.app -- you lose native macOS notifications, calendar event detection, FaceTime/iMessage continuity, and Rules. This CLI *extends* Mail.app rather than replacing it: your mail is still managed natively, but now also scriptable from the terminal.
381
-
382
- **Why not the Gmail API or Outlook API?**
383
- Those are per-provider -- separate SDKs, separate auth flows, separate data models. `mxctl` works with any account configured in Mail.app (iCloud, Gmail, Outlook, Exchange, custom IMAP) through a single unified interface. Add a new account to Mail.app and it just works.
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
- **Why not raw AppleScript or Hammerspoon?**
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mxctl"
7
- version = "0.3.0"
7
+ version = "0.4.1"
8
8
  description = "Apple Mail from your terminal"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"