Skip to the content.

Case Calendar is a Python CLI. You’ll need:

← Back to docs

Install Case Calendar

git clone https://github.com/seanthegeek/case-calendar
cd case-calendar
uv sync

That’s the whole installation. uv sync reads pyproject.toml and installs everything into a project-local virtual environment. Prefix every command with uv run (e.g. uv run case-calendar sync) and uv handles the activation for you.

Configure secrets

Case Calendar reads secrets from a .env file in the project root:

cp .env.example .env

Open .env and fill in:

COURTLISTENER_TOKEN=your_token_here
# Provide Gemini + Anthropic for the recommended split (Gemini extraction,
# Anthropic summaries); one key alone also works for both tracks.
GEMINI_API_KEY=...
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...             # optional third provider
CASE_CALENDAR_WEBHOOK_SECRET=...  # only needed for `case-calendar serve`

You can set keys for one provider or for all three. The tool auto-detects which provider to use from whichever *_API_KEY is set, and the priority is per track: the extraction track prefers gemini > anthropic > openai, and the case-summary track prefers anthropic > gemini > openai. So a fresh operator who provisions all three keys without setting any LLM_* variable lands on the project’s recommended split — Gemini for extraction, Anthropic for summaries (see Architecture → Why the default is a split). If you provide only one provider’s key, both tracks use that provider.

To force one provider for BOTH tracks, set LLM_PROVIDER=anthropic (or gemini / openai) — it’s the global default that overrides the per-track auto-detect on both sides.

To pin each track independently — keeping the default split, or building a different one — use the per-track override env vars:

Either or both can be set with or without LLM_PROVIDER — when an override is set, that track uses it; otherwise the track falls back to LLM_PROVIDER or, last, the per-track key auto-detect.

⚠️ The .env file should never be committed to source control — the repository’s .gitignore already lists it.

CourtListener stores RECAP PDFs unedited — it does not re-OCR documents contributed to RECAP. The plain_text CourtListener returns is whatever the uploader’s PDF carried natively, which on a non-trivial fraction of court PDFs is either:

Installing the local OCR fallback lets Case Calendar re-process those PDFs itself, so the AI summary pipeline sees usable text instead of an explicit “insufficient documents” refusal:

# Debian / Ubuntu
sudo apt install poppler-utils tesseract-ocr

# RHEL / CentOS / Rocky (tesseract lives in EPEL)
sudo dnf install epel-release
sudo dnf install poppler-utils tesseract

# macOS
brew install poppler tesseract

Without these, the tool still works — it just skips un-OCR’d or garbled PDFs and retries on each sync (no cache poisoning). But on those dockets the AI summary will fall back to:

Documents available for this docket are insufficient to generate a reliable summary.

Install them. The extra disk footprint is small and the OCR runs only when the primary text extraction has already failed.

Optional: calendar push backends

By default Case Calendar writes an ICS file you can subscribe to. If you’d rather have events show up directly in Google Calendar or Microsoft 365 / Outlook, see the calendar backends page for the one-time OAuth setup. Both are opt-in and require no per-command flag once authorized.

Verify it works

Add at least one case to config.yaml (see configuration) and run:

uv run case-calendar sync

A successful first sync prints one line per case, e.g.:

[us-v-wang] dockets_skipped=0 entries_seen=42 processed=11 actions=8
[cybercrime] wrote 14 events -> out/cybercrime.ics

The ICS file is now ready to subscribe to from any calendar app. See calendars for what to do with it.

Next steps