{"id":46364595,"url":"https://github.com/joryeugene/notmuch-ai","last_synced_at":"2026-03-05T02:04:37.352Z","repository":{"id":341980504,"uuid":"1172274326","full_name":"joryeugene/notmuch-ai","owner":"joryeugene","description":"AI intelligence layer for notmuch email (aerc, neomutt, himalaya)","archived":false,"fork":false,"pushed_at":"2026-03-04T06:47:09.000Z","size":133,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-04T12:46:51.762Z","etag":null,"topics":["aerc","ai","anthropic","email","email-classification","inbox-zero","llm","neomutt","notmuch"],"latest_commit_sha":null,"homepage":"https://github.com/joryeugene/notmuch-ai","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joryeugene.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-04T05:53:30.000Z","updated_at":"2026-03-04T06:47:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/joryeugene/notmuch-ai","commit_stats":null,"previous_names":["joryeugene/notmuch-ai"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/joryeugene/notmuch-ai","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joryeugene%2Fnotmuch-ai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joryeugene%2Fnotmuch-ai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joryeugene%2Fnotmuch-ai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joryeugene%2Fnotmuch-ai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joryeugene","download_url":"https://codeload.github.com/joryeugene/notmuch-ai/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joryeugene%2Fnotmuch-ai/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30106188,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T01:39:18.192Z","status":"online","status_checked_at":"2026-03-05T02:00:06.710Z","response_time":93,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aerc","ai","anthropic","email","email-classification","inbox-zero","llm","neomutt","notmuch"],"created_at":"2026-03-05T02:04:36.619Z","updated_at":"2026-03-05T02:04:37.343Z","avatar_url":"https://github.com/joryeugene.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# notmuch-ai\n\nAI intelligence layer for notmuch email. Works with aerc, neomutt, himalaya, or any notmuch-based setup.\n\n```\nmbsync  →  notmuch new  →  notmuch-ai classify  →  aerc\n```\n\nFive tags, no server, no web UI. You provide the LLM key.\n\n| Tag | Meaning |\n|-----|---------|\n| `needs-reply` | A real person wrote this and expects a response |\n| `ai-noise` | Auto-generated or marketing, no action needed |\n| `ai-urgent` | Deadline, time-sensitive, or from a senior stakeholder |\n| `ai-fyi` | Informational, genuine value, no reply needed |\n| `ai-follow-up` | Needs future attention, cannot act now |\n\nEvery decision is logged to SQLite. `notmuch-ai why \u003cid\u003e` tells you exactly why.\n\nCost: built-in classifiers use claude-haiku (~$3/month at 100 emails/day). Drafts use claude-sonnet (only on demand).\n\n---\n\n## Quickstart\n\nRequires notmuch configured and indexed, and uv installed.\n\n**Step 1: Install**\n\n```bash\ngit clone https://github.com/joryeugene/notmuch-ai\ncd notmuch-ai\njust install\n# or: uv tool install --editable .\n```\n\n**Step 2: Set your API key**\n\n```bash\nexport ANTHROPIC_API_KEY=sk-ant-...\n# add to ~/.zshrc or ~/.bashrc to persist\n```\n\n**Step 3: Run setup**\n\n```bash\nnotmuch-ai setup\n```\n\nThis creates `~/.config/notmuch-ai/rules.yaml` and appends all five AI virtual folders to your aerc queries file.\n\n**Verify it works:**\n\n```bash\njust dry-run\n# or: notmuch-ai classify --dry-run --verbose\n```\n\n---\n\n## Automating with post-new hook\n\nAdd to `~/.mail/.notmuch/hooks/post-new` (after all your existing notmuch tag rules):\n\n```bash\n# AI classification: runs after all static rules\nif command -v notmuch-ai \u0026\u003e/dev/null; then\n  notmuch-ai classify\nfi\n```\n\n```bash\nchmod +x ~/.mail/.notmuch/hooks/post-new\n```\n\nNow every `notmuch new` (including mbsync) automatically classifies new mail.\n\n---\n\n## Daily workflow\n\n```bash\n# Inspect recent decisions\njust audit\n# or: notmuch-ai log -n 50\n\n# Explain a specific decision\njust why \u003cmessage-id\u003e\n# or: notmuch-ai why id:abc123@mail.gmail.com\n\n# Generate a reply draft\njust draft \u003cmessage-id\u003e\n# or: notmuch-ai draft id:abc123@mail.gmail.com\n\n# Test rules against a specific message (no tags applied)\nnotmuch-ai rules check id:abc123@mail.gmail.com\n```\n\n---\n\n## Triage: close the feedback loop\n\nOver time the classifier makes mistakes. `notmuch-ai triage` lets you review recent decisions one at a time and correct them:\n\n```\n$ notmuch-ai triage\n\n┌─ 1/8 ─────────────────────────────────────────────────────────┐\n│ From:    eng-announce@company.com                              │\n│ Subject: Q1 architecture review notes                          │\n│ Date:    2026-03-04 09:12                                      │\n│ Tag:     ai-noise                                              │\n│                                                                │\n│ Appears informational, no reply requested...                   │\n└────────────────────────────────────────────────────────────────┘\n▸\n```\n\nPress `c` to confirm, `r` to reclassify, `s` to skip, or `q` to quit.\n\nWhen you reclassify, you choose the correct tag from a numbered list. After the session, if you made two or more corrections, the LLM analyzes the pattern and proposes new rules:\n\n```\nFound 1 rule proposal:\n\n  company-announcements\n  ─────────────────────\n  name: company-announcements\n  condition: Is this a company-wide announcement from an internal\n    address that I should read but don't need to act on?\n  action: tag add ai-fyi\n\n  Add to rules.yaml? [y/n]\n```\n\nApproved rules are appended to `~/.config/notmuch-ai/rules.yaml` immediately.\n\n---\n\n## Custom rules\n\nEdit `~/.config/notmuch-ai/rules.yaml`:\n\n```yaml\nrules:\n  - name: \"Cold outreach\"\n    condition: \"Is this a sales or marketing email from someone I don't know personally?\"\n    action: tag add ai-cold-outreach\n    static_subject:\n      - \"(?i)quick question\"\n      - \"(?i)partnership opportunity\"\n\n  - name: \"PR review request\"\n    condition: \"Is this asking me personally to review a pull request?\"\n    action: tag add needs-reply\n```\n\nConditions are plain English evaluated by LLM. `static_subject` and `static_from` are regex fast-paths that skip the LLM entirely. Use them for high-volume, predictable patterns.\n\n---\n\n## aerc integration\n\nVirtual folders are added automatically by `notmuch-ai setup`. They appear in aerc's sidebar:\n\n```ini\nneeds-reply  = tag:needs-reply AND NOT tag:replied AND NOT tag:deleted\nai-noise     = tag:ai-noise AND NOT tag:deleted\nai-urgent    = tag:ai-urgent AND NOT tag:deleted\nai-fyi       = tag:ai-fyi AND NOT tag:deleted\nai-follow-up = tag:ai-follow-up AND NOT tag:deleted\n```\n\nTo generate drafts from aerc, add this to `~/.config/aerc/binds.conf`:\n\n```ini\n[messages]\ngD = :pipe notmuch-ai draft -\u003cEnter\u003e\n\n[view]\ngd = :pipe -m notmuch-ai draft -\u003cEnter\u003e\n```\n\nUse `gd` while reading an email, or `gD` from the message list. The draft prints to a pager. Nothing is sent.\n\n---\n\n## Alternative LLM providers\n\nDefault is Anthropic (haiku for classification, sonnet for drafts). To use a different provider, set `NOTMUCH_AI_MODEL` to any [litellm-compatible model](https://docs.litellm.ai/docs/providers):\n\n```bash\nexport NOTMUCH_AI_MODEL=gpt-4o-mini    # OpenAI\nexport NOTMUCH_AI_MODEL=ollama/llama3  # local Ollama (free)\n```\n\n---\n\n## Development\n\n```bash\njust test       # unit tests (no API key needed)\njust test-live  # live integration tests (requires ANTHROPIC_API_KEY)\njust dry-run    # classify last 50 inbox messages, print reasoning\n```\n\n---\n\n## Architecture\n\nEach component has one job and no dependencies on the others:\n\n| File | Job |\n|------|-----|\n| `notmuch.py` | subprocess wrapper: search, show, tag |\n| `llm.py` | prompt → structured response |\n| `rules.py` | email + rules → tag operations |\n| `classify.py` | orchestrate: fetch → evaluate → tag → log |\n| `triage.py` | interactive review: corrections → rule proposals |\n| `draft.py` | message-id → reply draft text |\n| `db.py` | append-only audit trail + corrections (SQLite) |\n| `cli.py` | typer CLI, no business logic |\n\nNo component knows about any other. Swap the LLM provider without touching the rules engine. Replace the audit trail without touching classify.\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoryeugene%2Fnotmuch-ai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoryeugene%2Fnotmuch-ai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoryeugene%2Fnotmuch-ai/lists"}