v1.1.1 · Apache-2.0 · Go · local-only

The Proton Mail CLI your agent can actually drive

higgs is an agent-first CLI for Proton Mail. A schema manifest instead of --help prose, NDJSON on stdout, typed error envelopes, and a stable exit-code enum — designed to be driven by a language model, not parsed by regex.

$ brew install higgscli/higgs/higgs
Also via go install or release tarballs · runs entirely on localhost
higgs — agent session
# 1. The agent discovers the tool — no prompt-engineered syntax $ higgs schema classify { "name": "classify", "stdout": "ndjson", "exit_codes": [0,2,3,4,5,6,7,9] } # 2. Then drives it — one JSON object per line, terminator at the end $ higgs classify --dry-run --limit 3 INBOX {"uid":1842,"subject":"Your order has shipped","suggested_labels":["Orders"],"confidence":0.94} {"uid":1843,"subject":"Stripe receipt","suggested_labels":["Finance"],"confidence":0.91} {"uid":1844,"subject":"PR review requested","suggested_labels":["Work"],"confidence":0.97} {"type":"summary","classified":3,"errors":0,"skipped":0} # 3. And composes it — NDJSON out pipes straight into --uid - $ higgs search INBOX --before 2025-07-01 | higgs archive INBOX --uid -
Why higgs exists

Wiring a normal CLI into an agent loop is painful

Stdout mixes prose and data, errors are English sentences, exit codes are 0-or-1, and the only tool spec is --help. higgs inverts every one of those assumptions: the primary caller is a model.

The usual workaround

Parse --help with the model and hope it guesses flags correctly, every session, forever.
Regex the stdout because prose and data share a stream, and the format shifts between versions.
Branch on English — "authentication failed" vs "auth error" vs "login incorrect" — to decide whether to retry.
Guess when a stream ends. No terminator, so agents wait on timeouts and heuristics.

The higgs way

Load higgs schema once. A JSON manifest of every subcommand: flags, args, stdout format, exit codes. The schema is the prompt.
Read NDJSON until {"type":"summary"}. One object per line, a guaranteed terminator. No heuristics.
Branch on .error.kind. Typed envelopes with kind, code, reason, message, hint. The envelope is the branching logic.
Retry by exit code. Codes map 1:1 to error kinds: retry on 5 imap, prompt the user on 2 auth, surface on 4 config.
The agent contract

Four primitives. Zero ambiguity.

Everything an agent needs to discover, drive, recover, and branch — built into the binary, not into your system prompt.

01 Discover

$ higgs schema classify
{
  "name": "classify",
  "summary": "Classify messages with Ollama…",
  "args": [{"name":"mailbox","default":"INBOX"}],
  "flags": [
    {"name":"dry-run","type":"bool"},
    {"name":"apply","type":"bool"},
    {"name":"limit","type":"int","default":100}
  ],
  "stdout": "ndjson",
  "exit_codes": [0,2,3,4,5,6,7,9]
}

02 Recover

$ higgs classify --apply INBOX
{
  "error": {
    "kind": "auth",
    "code": 401,
    "reason": "authFailed",
    "message": "IMAP authentication failed",
    "hint": "Check PM_IMAP_PASSWORD matches Bridge"
  }
}
# exit 2 → agent prompts the user, not the void

03 Stream

# stdout: data. stderr: sanitized human progress —
# ANSI escapes, bidi controls, zero-width chars stripped,
# safe to feed back into a model's context.
{"uid":1842,"suggested_labels":["Orders"]}
{"uid":1843,"suggested_labels":["Finance"]}
{"type":"summary","classified":2,"errors":0}

04 Branch

ExitKindAgent strategy
0successParse stdout, continue
2authPrompt user for credentials
3validationFix flags or args, retry
4configSurface missing config
5imapRetry with backoff
6classifyCheck Ollama, retry
7stateInspect the state DB
9internalEscalate to user
Unix, but typed

Every command composes over pipes

Any command that accepts --uid also accepts --uid -: it reads the UID set from stdin — plain UIDs or NDJSON, from which each row's uid field is taken. One command's output is the next one's input, and verify audits the result.

# Archive everything older than July
$ higgs search INBOX --before 2025-07-01 | higgs archive INBOX --uid -

# Move every non-mailing-list message the classifier saw to Personal
$ higgs state query INBOX --is-mailing-list false | higgs move INBOX Folders/Personal --uid -

# Then prove the move happened — audit the mailbox against an expected UID set
$ higgs search INBOX --before 2025-07-01 | higgs verify INBOX --uid - --expect absent
{"type":"summary","checked":312,"violations":0}
The toolkit

30+ commands, one contract

The first workload was a local inbox classifier. The contract turned out to be the product — and now the whole mailbox is scriptable by an agent.

Read & search

  • searchtyped criteria → NDJSON
  • fetch-and-parsefull messages as JSON
  • thread / threadsconversation groups
  • attachmentsextract attachment bytes
  • scan-folderslist mailboxes
  • watchstream change events

Understand (local AI)

  • classifylabel with Ollama
  • askagentic Q&A over the mailbox
  • summarizeNDJSON summaries
  • digeststructured recent-mail digest
  • extractpull data with a JSON schema

Act

  • move / archive / trashwith verification
  • mark-read / flagflip flags in bulk
  • apply-labelswrite classifier results
  • unsubscribehonor List-Unsubscribe
  • draft / sendcompose via IMAP/SMTP

State & safety

  • state queryquery past classifications
  • verifyaudit against expected UIDs
  • backfillrebuild state from logs
  • cleanup-labelsconsolidate label sets
  • authcredentials → OS keyring

Portability

  • exportmailbox → mbox / JSONL
  • importmbox / JSONL → mailbox
  • schemathe manifest itself
  • completionshell autocompletion

Guarantees

  • stdoutalways structured JSON
  • stderrsanitized, model-safe
  • exit codesstable 0–9 enum
  • statecheckpointed SQLite, resumable
Private by architecture

Everything runs on localhost

Mail flows through Proton Mail Bridge on 127.0.0.1. Classification runs through a local Ollama model. Nothing leaves your machine.

no cloud

No API keys, no inference bills

The default model is Gemma 4 via Ollama — native function calling, 128K context, fits on a laptop. Swap in any local model you like.

keyring

Secrets never touch the context

Credentials live in the OS keyring — macOS Keychain, Windows Credential Manager, libsecret — with an AES-256-GCM file fallback. Nothing sensitive flows through an agent's context window.

no telemetry

Zero phoning home

No analytics, no crash reporting, no update pings. Releases are cosign-signed with an SBOM included. Apache-2.0, fully open source.

Get started

Install in 60 seconds

Prerequisites for the AI commands: a running Proton Mail Bridge and Ollama. Everything else just needs Bridge.

Homebrew (macOS & Linux)

brew tap higgscli/higgs
brew install higgs

Go install

go install github.com/higgscli/higgs/cmd/higgs@latest

From source

git clone https://github.com/higgscli/higgs.git
cd higgs && make build
# Point higgs at Bridge and Ollama
$ export PM_IMAP_USERNAME="alice@proton.me"
$ export PM_IMAP_PASSWORD="bridge-generated-password"
$ export PM_IMAP_HOST="127.0.0.1"  PM_IMAP_PORT="1143"  PM_OLLAMA_MODEL="gemma4"

# Dry-run against your inbox — preview, apply only when it looks right
$ higgs classify --dry-run --limit 20 INBOX
$ higgs classify --apply --workers 4 INBOX
Download v1.1.1 Read the docs

Give your agent a CLI it can actually drive

Schema-discoverable. NDJSON-native. Locally private. Pipe-composable.

Install higgs Star on GitHub