{"id":18855389,"url":"https://github.com/cak/elf","last_synced_at":"2026-01-27T15:33:03.961Z","repository":{"id":323348850,"uuid":"1065342975","full_name":"cak/elf","owner":"cak","description":"A modern Advent of Code helper that fetches inputs, submits answers, and tracks your progress.","archived":false,"fork":false,"pushed_at":"2025-12-01T13:51:58.000Z","size":291,"stargazers_count":32,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-03T22:38:52.579Z","etag":null,"topics":["advent-of-code","advent-of-code-cli","aoc","cli","httpx","leaderboard","pydantic","typer"],"latest_commit_sha":null,"homepage":"","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/cak.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":"2025-09-27T14:31:47.000Z","updated_at":"2025-12-03T18:10:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cak/elf","commit_stats":null,"previous_names":["snally-dev/elf","cak/elf"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/cak/elf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cak%2Felf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cak%2Felf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cak%2Felf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cak%2Felf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cak","download_url":"https://codeload.github.com/cak/elf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cak%2Felf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28815407,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-27T12:25:15.069Z","status":"ssl_error","status_checked_at":"2026-01-27T12:25:05.297Z","response_time":168,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["advent-of-code","advent-of-code-cli","aoc","cli","httpx","leaderboard","pydantic","typer"],"created_at":"2024-11-08T03:53:49.109Z","updated_at":"2026-01-27T15:33:03.955Z","avatar_url":"https://github.com/cak.png","language":"Python","readme":"# elf: Advent of Code helper for Python\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://snally.com/assets/elf-logo.png\" width=\"200\" alt=\"elf logo\"\u003e\n\u003c/p\u003e\n\nA fast, modern Advent of Code CLI with caching, guardrails, leaderboards, and a lightweight Python API.\n\nWorks on macOS, Linux, and Windows. Most networked commands require an AoC session cookie (`AOC_SESSION`).\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/pypi/v/elf.svg\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/License-MIT-success.svg\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Advent%20of%20Code-Ready-00cc66\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/North%20Pole%20API-Compliant-blue\"\u003e\n\u003c/p\u003e\n\n## Why I Built This\n\nAdvent of Code has become one of my favorite Christmas traditions. I am never the fastest solver, and some puzzles definitely keep me humble, but the challenges always spark new ideas and mark the start of the holiday season for me.\n\nThank you to **Eric Wastl**, the creator of [Advent of Code](https://adventofcode.com/). His work brings an incredible community together every December and inspires people around the world to learn, explore new ideas, and enjoy the fun of programming puzzles.\n\nAfter refining a small helper tool I have used personally for the past few years, I decided to turn it into a package others can benefit from as well. I originally built an early version for my own workflows in [my personal AoC repo](https://github.com/cak/advent-of-code/tree/dc9c02a5a77a36b725a8e01cff18a6de46e0db0d?tab=readme-ov-file#%EF%B8%8F-automating-tasks-with-the-elf-cli).\n\nIf Advent of Code is part of your December ritual too, I hope this little elf makes the journey smoother, more fun, and a bit more magical.\n\n## Highlights\n\n- Fetch inputs instantly with **automatic caching**\n- Submit answers safely with **smart guardrails** (duplicate, low, high, locked, cooldown)\n- Explore private leaderboards as **tables**, **JSON**, or **Pydantic models**\n- View your **status calendar** (table, JSON, or model)\n- Inspect your **guess history** with timestamps and per-part details\n- Open any AoC page (puzzle, input, or website) straight from the CLI\n- Use elf as a **CLI tool** or a full **Python API**\n\n## Installation\n\n### Using uv (recommended)\n\n#### Install as a tool\n\n```sh\nuv tool install elf\n```\n\n#### Inside a project\n\n```sh\nuv add elf\n```\n\n### Using pip\n\n```sh\npip install elf\n```\n\n### Requirements\n\n- Python 3.11 or newer\n- An Advent of Code account\n- `AOC_SESSION` cookie set in your environment for most commands\n\n## Configure your AoC Session\n\nMost features in elf require your Advent of Code session cookie so the CLI can access your personal puzzle inputs and progress.\n\nTo get it:\n\n1. Log in to https://adventofcode.com using GitHub, Google, or Reddit.\n2. Open your browser’s developer tools.\n   - Chrome: View → Developer → Developer Tools\n   - Firefox: Tools → Browser Tools → Web Developer Tools\n3. Go to the **Application** (Chrome) or **Storage** (Firefox) tab.\n4. Look for **Cookies** for the domain `adventofcode.com`.\n5. Find the cookie named **`session`**.\n6. Copy the value (a long hex string).\n7. Set it as an environment variable:\n\n```sh\nexport AOC_SESSION=\"your-session-token\"\n```\n\nOn Windows you can persist the cookie with PowerShell:\n\n```powershell\n$env:AOC_SESSION = \"your-session-token\"\nsetx AOC_SESSION \"your-session-token\"\n```\n\nOr via CMD:\n\n```cmd\nsetx AOC_SESSION \"your-session-token\"\n```\n\nMost commands require this. You can also pass it via `--session` in the CLI or `session=` in the API.\n\n### Your User-Agent (Recommended)\n\nElf follows the guidance from Eric Wastl (creator of Advent of Code) to help keep traffic friendly to the site.\n\nIf you run tools that make automated requests, you should provide contact information in your User-Agent header so Eric can reach you if something goes wrong with your traffic. This is optional but strongly recommended.\n\nSet your email address in the environment variable `AOC_USER_AGENT`:\n\n```sh\nexport AOC_USER_AGENT=\"you@example.com\"\nWindows PowerShell:\n$env:AOC_USER_AGENT = \"you@example.com\"\nsetx AOC_USER_AGENT \"you@example.com\"\n```\n\nIf this variable is not set or does not look like an email address, elf falls back to a safe default and prints a warning.\nYour User-Agent header will look like:\nelf/\u003cversion\u003e (you@example.com)\nThis helps Eric identify the person sending the traffic while still including the library name.\n\nIf the warning fires, elf instead sends `elf/\u003cversion\u003e (+https://github.com/cak/elf)` so AoC still sees a recognizable identifier even without your email.\n\n## Quick Start\n\n```sh\nexport AOC_SESSION=\"your-session-token\"\n\nelf input        # fetch today's input\nelf answer 2025 1 1 123 # submit answer for part 1\nelf status       # view progress\nelf leaderboard 2025 123456 --view-key ABCDEF\n```\n\n### Saving puzzle input to files\n\nIf you prefer to keep your own local copies of puzzle inputs, elf works just like a regular Unix command. You can redirect the output to save any day's input:\n\n```sh\nelf input \u003e input.txt              # save today's input\nelf input 2025 2 \u003e day02.txt       # save a specific day's input\n```\n\n## CLI Documentation\n\n### `elf --help`\n\n```sh\nUsage: elf [OPTIONS] COMMAND [ARGS]...\n\nAdvent of Code CLI\n\nOptions:\n  --version, -V\n  --debug\n  --install-completion\n  --show-completion\n  --help\n\nCommands:\n  input        Fetch the input for a given year/day\n  answer       Submit an answer\n  leaderboard  Fetch/display a private leaderboard\n  guesses      Show cached guesses\n  status       Show yearly star status\n  open         Open puzzle/input/website\n  cache        Show cache information\n```\n\nEnable detailed tracebacks with `--debug` or `ELF_DEBUG=1` when troubleshooting.\n\n---\n\n## Commands\n\n### `elf input`\n\nFetch puzzle input with caching. Requires a session cookie.\n\n```sh\nUsage: elf input [YEAR] [DAY]\n\nOptions:\n  --session TEXT   AOC session cookie (env: AOC_SESSION)\n```\n\nDefaults:\n\n- `year`: current year\n- `day`: current day in December, otherwise 1 (Dec 1)\n- Caches to `~/.cache/elf/\u003cyear\u003e/\u003cday\u003e/input.txt` (or platform equivalent)\n\n### `elf answer`\n\nSubmit an answer with safety guardrails. Requires a session cookie. Guardrails use your local guess cache to short-circuit duplicate answers and infer too-high/too-low for integer guesses.\n\n```sh\nUsage: elf answer YEAR DAY PART ANSWER\n\nOptions:\n  --session TEXT  AOC session cookie\n```\n\nBehaviors:\n\n- Year and day are required to avoid accidental submissions.\n- Detects locked puzzles (future days or future years) and shows unlock timestamp.\n- Identifies too high / too low / duplicate guesses from local cache\n- Caches cooldown responses locally for ~60 seconds to avoid hammering the site\n- Writes to guess cache automatically (per part)\n\nExit codes:\n\n- `0` when the answer is correct or already completed on AoC\n- `2` when AoC rate-limits you (cooldown/WAIT)\n- `1` for incorrect/too-high/too-low/unknown guesses\n\nDuplicate guesses reuse cached submissions, so the exit code follows the cached result: e.g., a cached correct/completed guess still exits `0`, while cached incorrect/high/low responses return `1`.\n\nExample errors:\n\n```sh\n❄️ Puzzle YYYY‑MM‑DD not unlocked yet\n1234 is not correct.\nYou submitted an answer recently. Please wait...\n12345 is correct. Star awarded.\n```\n\n### `elf guesses`\n\nDisplay local guess history (per part). Requires a cached `guesses.csv` from previous submissions. Displays all guesses for both parts automatically.\n\n```sh\nUsage: elf guesses [YEAR] [DAY]\n```\n\n`elf guesses` loads its data solely from the local `guesses.csv`; it never reaches out to AoC and does not require `AOC_SESSION`, so it works offline as long as the cache already exists.\n\nExample table:\n\n```sh\nTime (UTC)      Guess  Status\n2025‑12‑05 ...  959    too_low\n2025‑12‑05 ...  6951   correct\n```\n\n### `elf leaderboard`\n\nFetch private leaderboards. Provide a view key for read-only access or a session cookie for authenticated access. A view key is the read-only share token you can generate on your AoC leaderboard page.\n\n```sh\nUsage: elf leaderboard YEAR BOARD_ID\n\nOptions:\n  --view-key TEXT\n  --session TEXT\n  --format table|json|model\n```\n\nSupports:\n\n- **table:** pretty Rich table\n- **json:** raw JSON\n- **model:** structured Pydantic model\n\n### `elf status`\n\nView your Advent of Code star calendar. Requires a session cookie.\n\n```sh\nUsage: elf status [YEAR]\n\nOptions:\n  --session TEXT\n  --format table|json|model\n```\n\nDefaults:\n\n- `year`: current year if omitted\n\nPrints stars for each day.\n\n### `elf open`\n\nOpens puzzle pages in your browser.\n\n```sh\nUsage: elf open [YEAR] [DAY]\n\nOptions:\n  --kind puzzle|input|website\n```\n\n### `elf cache`\n\nDisplay cache directory information.\n\n```sh\nUsage: elf cache\n```\n\nShows:\n\n- Cache root directory (platform-aware)\n- Number of cached files\n- Reminder to delete the directory manually to clear cache\n\n---\n\n## Caching Behavior\n\n- Default cache dir: macOS/Linux `~/.cache/elf`, Windows `%LOCALAPPDATA%\\elf`\n- Override location with `ELF_CACHE_DIR` (respects `XDG_CACHE_HOME` on Linux)\n- Inputs stored under: `\u003ccache\u003e/\u003cyear\u003e/\u003cday\u003e/input.txt`\n- Guess history stored as `\u003ccache\u003e/\u003cyear\u003e/\u003cday\u003e/guesses.csv`\n- Duplicate/high/low guesses are short‑circuited locally when possible\n- Cooldown responses are cached locally for ~60 seconds to avoid hammering AoC\n- Guardrails rely on your local guess history only (new machines/cleared cache = fresh)\n- Delete the cache directory to clear everything\n\n### Concurrency note\n\nThe library reuses a process-global `httpx.Client` to keep CLI calls fast. For heavy multi-threaded usage, create separate `elf.aoc_client.AOCClient` instances per thread to avoid sharing the global session.\n\n---\n\n## Library Usage\n\n```python\nfrom elf import (\n    get_puzzle_input,\n    submit_puzzle_answer,\n    get_private_leaderboard,\n    get_user_status,\n    OutputFormat,\n)\n\n# AOC_SESSION is used automatically if not passed explicitly\ninput_text = get_puzzle_input(2023, 5)\n\nresult = submit_puzzle_answer(2023, 5, 1, \"12345\")\nprint(result.is_correct, result.message)\n\nleaderboard = get_private_leaderboard(\n    2023, session=None, board_id=123456, view_key=None, fmt=OutputFormat.MODEL\n)\n\nstatus = get_user_status(2023, fmt=OutputFormat.TABLE)\nprint(status)\n```\n\n### Puzzle Helpers\n\n`elf.helpers` includes some small utilities you can use in your own AoC solutions:\n\n```python\nfrom elf.helpers import parse_input, read_test_input, timer\n```\n\n### Leaderboard Example\n\n```sh\n❯ elf leaderboard 2025 3913340\n              Advent of Code 2025 – Private Leaderboard\n┏━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓\n┃ Rank ┃ Name           ┃ Stars ┃ Local Score ┃ Last Star (UTC)     ┃\n┡━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩\n│    1 │ User A         │    45 │         900 │ 2025-12-26 00:53:17 │\n│    2 │ User B         │    45 │         855 │ 2025-12-26 00:53:56 │\n│    3 │ User C         │    36 │         622 │ 2025-12-26 03:29:21 │\n└──────┴────────────────┴───────┴─────────────┴─────────────────────┘\n```\n\nJSON format:\n\n```sh\nelf leaderboard 2025 3982840 --format json\n```\n\nModel format (Pydantic):\n\n```python\nlb = get_private_leaderboard(2025, board_id=3982840, fmt=OutputFormat.MODEL)\nmembers = sorted(lb.members.values(), key=lambda m: (-m.local_score, -m.stars))\nprint(members[0].name, members[0].stars)\n```\n\n### Status Example\n\n```sh\n❯ elf status 2023\nAdvent of Code\n  2023 – cak\n(AoC++) [34⭐]\n┏━━━━━┳━━━━━━━┓\n┃ Day ┃ Stars ┃\n┡━━━━━╇━━━━━━━┩\n│   1 │  ★★   │\n│   2 │  ★★   │\n│   3 │  ★★   │\n│   4 │  ★☆   │\n│   5 │  ★☆   │\n│   6 │  ★★   │\n│   7 │  ★★   │\n│   8 │  ★☆   │\n│   9 │  ☆☆   │\n│  10 │  ☆☆   │\n│  .. │  ..   │\n│  25 │  ☆☆   │\n└─────┴───────┘\n```\n\nJSON format:\n\n```sh\nelf status 2023 --format json\n```\n\nModel format:\n\n```python\nstatus = get_user_status(2023, fmt=OutputFormat.MODEL)\nprint(status.days[0].day, status.days[0].stars)\n```\n\n---\n\n## Development / Testing\n\nRun the test suite via:\n\n```sh\nuv run pytest\n```\n\nAlternatively, you can still run:\n\n```sh\npython -m pytest\n```\n\nRunning `python -m pytest` directly requires `pytest` to be installed in your environment. Install it via `pip install pytest\u003e=9.0.1` (or use `uv run pytest` / `pip install .[dev]` if you maintain the dev extras) before running the command outside of `uv`.\n\n## Special Thanks\n\nThis project exists thanks to the generosity and creativity of others.\n\nThanks to **Solos** for donating the `elf` PyPI name.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcak%2Felf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcak%2Felf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcak%2Felf/lists"}