# Howl An M365 email management daemon that uses the Microsoft Graph API and Claude AI to automatically classify and route incoming email based on your customer, vendor, and whitelist database. ## How it works ``` Inbox (M365) │ ▼ Graph API polls for unread mail │ ▼ PostgreSQL lookup — is the sender a customer, vendor, or trusted contact? │ ▼ Claude (LLM) classifies the email using sender context + body content │ ▼ Business rules applied (whitelist always wins, low-confidence → hold for review) │ ▼ Action taken: move to folder / flag / escalate with notification │ ▼ email_log table records every decision for audit and retry ``` ## Features - Supports both **shared mailboxes** (app-only, no user login) and **personal mailboxes** (delegated device-code auth) - **No emails are ever deleted** — only moved, flagged, or escalated - **Dry-run mode** — analyze and log without touching any email - **Idempotent** — safe to restart; already-processed messages are skipped - **Automatic retry** — failed messages are retried on the next poll cycle - **Full audit log** — every classification, confidence score, and action is stored in PostgreSQL ## Folder routing | Sender type | LLM classification | Action | |---|---|---| | Customer | Inquiry / correspondence | Move to `Customers` folder | | Vendor | Invoice / notification | Move to `Vendors` folder | | Whitelist | Trusted contact | Mark read, stay in Inbox | | Any | Urgent / escalation | Move to `Escalate` + webhook notification | | Any | Uncertain (low confidence) | Move to `Needs Review` | | Unknown | Spam / newsletter | Move to `Junk Email` | ## Quick start ```bash # 1. Create and activate a virtual environment python3 -m venv .venv && source .venv/bin/activate # 2. Install dependencies pip install -e ".[dev]" # 3. Configure environment cp .env.example .env # Edit .env with your Azure, Anthropic, and PostgreSQL credentials # 4. Run database migrations alembic upgrade head # 5. Test with dry-run (no email mutations) howl dry-run --limit 10 # 6. Start the daemon howl run ``` ## CLI commands | Command | Description | |---|---| | `howl run` | Start the daemon (blocking, polls on schedule) | | `howl dry-run [--limit N]` | Classify up to N emails, log results, take no action | | `howl status [--limit N]` | Display recent email_log entries in a table | ## Project structure ``` howl/ ├── howl/ │ ├── config.py # All configuration (pydantic-settings + .env) │ ├── main.py # CLI entry point (Typer) │ ├── logging_config.py # structlog setup (JSON or text) │ ├── db/ │ │ ├── engine.py # Async SQLAlchemy engine │ │ ├── models.py # ORM models │ │ └── queries.py # lookup_sender(), email log helpers │ ├── graph/ │ │ ├── auth.py # MSAL token provider (client-credentials or delegated) │ │ └── client.py # Graph API: list, read, move, flag messages │ ├── llm/ │ │ ├── client.py # Anthropic SDK wrapper with retry │ │ ├── prompts.py # Prompt builder (system prompt + per-email user turn) │ │ └── schemas.py # EmailClassification model + tool definition │ ├── pipeline/ │ │ ├── processor.py # EmailProcessor — orchestrates the full pipeline │ │ ├── classifier.py # Business rule overrides on LLM output │ │ └── actions.py # ActionExecutor — Graph mutations + webhook │ └── daemon/ │ └── scheduler.py # APScheduler poll loop + graceful shutdown ├── migrations/ # Alembic database migrations ├── tests/ # Unit and integration tests ├── .env.example # Configuration template ├── SETUP.md # Detailed setup and configuration guide └── NEXT_STEPS.md # Roadmap and future improvements ``` ## Running tests ```bash pytest tests/ -q # All tests (no external services needed) pytest tests/ -q -m "not integration" # Unit tests only (default) pytest tests/ -q -m integration # Requires real M365 + Anthropic credentials ``` ## Requirements - Python 3.11+ - PostgreSQL 14+ - Microsoft Azure app registration with `Mail.Read` + `Mail.ReadWrite` permissions - Anthropic API key