Skip to main content

leetvault

Mirror your LeetCode account (accepted submissions + source code + metadata) into a normalized SQLite database and a GitHub repository with an auto-generated README dashboard.

Sync is account-based, driven by your authenticated LeetCode session — not a browser extension. Your LeetCode account is the single source of truth. Free and open source; the only external services involved are LeetCode and GitHub.

Install

pip install leetvault

Requires Python 3.11+. See docs/DEVELOPER.md for an editable/dev install.

Quickstart

leetvault login                                          # paste LEETCODE_SESSION + csrftoken
leetvault config repo_url https://github.com/you/repo.git # optional: enable GitHub push
leetvault import                                          # one-time full history
leetvault sync                                             # incremental, run anytime
leetvault watch                                             # or: poll automatically

Commands

  • leetvault login [--leetcode|--github] [--force] — store your LEETCODE_SESSION + csrftoken (and optionally a GitHub PAT) in the OS keyring. Only prompts for what's actually missing or expired — see below.
  • leetvault import [--keep-all] — full history import of every accepted submission (resumable, one-time per site).
  • leetvault sync [--keep-all] — incremental sync of new accepted submissions since the last run.
  • leetvault watch — poll LeetCode and sync automatically (--interval, default 90s).
  • leetvault status — show session validity/expiry and sync state.
  • leetvault logout — remove stored credentials.
  • leetvault config — get/set persistent configuration (repo URL, DB path, dedup window, ...).
  • leetvault ai — set up optional AI-generated solution analysis (off by default).
  • leetvault analyze [problem] — regenerate an existing analysis.md with a different backend — see below.
  • leetvault ask <problem> "<question>" — ask a question about one of your solutions and keep the answer — see below.
  • leetvault bot [--install] — install a GitHub Actions workflow so you can ask from GitHub itself — see below.
  • leetvault site [--publish] — build a browsable web page for the repo and publish it on GitHub Pages — see below.
  • leetvault commands [--full] — list every command and what it does, generated from the CLI itself so it cannot fall behind.

Refreshing credentials

Your LeetCode session cookies expire roughly every 14 days; a GitHub PAT lasts until you revoke it or it hits its own expiry. They fail independently, so login checks each one live and only prompts for what actually needs replacing:

leetvault login            # checks both, prompts only for what's expired/missing
leetvault login --leetcode # only refresh LeetCode cookies, never touch the stored PAT
leetvault login --github   # only refresh the GitHub PAT, never re-ask for cookies
leetvault login --force    # re-prompt for everything, even if still valid

If both are still good, login prompts for nothing and tells you so. leetvault status shows the same live check without changing anything.

AI solution analysis (optional, off by default)

leetvault ai can generate an analysis.md next to each solution — explaining the approach, walking through your actual code, and covering complexity and edge cases. It is disabled until you turn it on, and your notes.md is never touched.

It works with whichever backend you already have; run leetvault ai and it detects them:

Backend Cost Needs Notes
Gemini Free tier Free API key aistudio.google.com/apikey. No local hardware, no subscription.
Groq Free tier Free API key console.groq.com/keys. Very fast; no local hardware. Meters 8000 tokens/min.
NVIDIA NIM Free tier Free API key build.nvidia.com. Very large open models with reasoning; quota separate from Gemini and Groq.
Ollama (local) Free, unlimited ~5 GB disk + RAM ollama pull qwen2.5-coder:7b. Fully offline, no account. Slow without a GPU.
Claude Code CLI Free An existing Claude subscription npm i -g @anthropic-ai/claude-code, then claude once to sign in.
Anthropic API Paid, per token API key pip install anthropic. Highest quality.

Gemini, Groq and NVIDIA NIM are the fallback when you have neither spare RAM nor a Claude subscription — a free API key, and inference runs in the cloud. Their quotas are separate, so a backfill stopped by one provider's daily limit can be finished on another.

For Hard problems, prefer a reasoning-capable model. Tracing code and justifying complexity is multi-step arithmetic, and a mid-size instruct model will produce a confident, wrong dry run rather than admitting it cannot follow the code.

leetvault ai                      # detect backends and choose one
leetvault ai --set-key gemini     # store a free key (or groq / nvidia / anthropic)
leetvault ai --show               # print current settings
leetvault ai --disable            # turn it back off
leetvault config ai_model openai/gpt-oss-120b   # override the model

Generation is best-effort: a failing or slow model never breaks a sync. sync only fills gaps — a problem that already has an analysis.md is skipped, which is what makes a backfill resumable across a free tier's daily limit. To replace one you are not happy with, use leetvault analyze.

Each file's footer records the model that wrote it, so a repo built across several providers stays traceable.

How an analysis is assembled

leetvault asks for the whole analysis in one call and keeps that when it comes back complete. If the reply is truncated or missing sections, it re-requests them in halves, then pairs, then one section at a time, pausing between calls so a per-minute token budget is not spent in one shot. A partial analysis is never written — a file that exists is a problem that never gets looked at again.

Redoing an analysis

leetvault analyze --list                  # which model wrote each analysis
leetvault analyze two-sum -p nvidia       # redo one problem
leetvault analyze --from groq -p nvidia   # redo everything Groq wrote
leetvault analyze --all -p gemini         # redo the lot, one model throughout

Name a problem by slug, number, or part of its title. You are asked before anything is overwritten (-y skips that), and if the new provider fails, the existing analysis is kept rather than lost.

--keep-all

By default, import/sync keep only the newest accepted submission per problem within a rolling 24-hour window (dedup_window_seconds in leetvault config, default 86400) — solving the same problem twice in one sitting doesn't clutter history with near-duplicate attempts. --keep-all disables that and stores every accepted submission individually:

leetvault sync --keep-all      # one-off: keep everything from this run onward
leetvault import --keep-all    # same, for the initial full-history import

To make this the permanent default instead of retyping the flag every time:

leetvault config dedup_window_seconds 0

--keep-all only changes how future submissions are processed — it can't retroactively recover a submission an earlier (non---keep-all) run already deduped, since sync only walks forward from the last submission it saw. See docs/TROUBLESHOOTING.md if you hit that.

What gets stored

A normalized SQLite database (problems, submissions, source code, topics, sync state) plus a disk layout per problem:

Problems/<slug>/
  latest.<ext>          the most recent accepted submission
  history/submission_<id>.<ext>   every kept accepted submission
  question.md            the problem statement, examples, constraints + collapsed hints
  analysis.md            optional AI explanation of your solution (off by default)
  run.py                  runs your solution against the problem's example inputs
  metadata.json          difficulty, topics, runtime/memory percentiles, ...
  notes.md                yours - never overwritten once created
leetvault_runner.py       shared runner that each run.py delegates to
.devcontainer/            so the repo opens ready-to-run in GitHub Codespaces
README.md                 auto-generated dashboard: progress, streaks, full solutions table,
                          and clickable topic tags that jump to a per-topic problem list

Running solutions in the browser

Open your solutions repo on GitHub and choose Code ▸ Codespaces ▸ Create codespace - you get full VS Code with a terminal, and the devcontainer means Python is already set up:

python Problems/two-sum/run.py

It prints your solution's output for each of LeetCode's example inputs. It intentionally does not report pass/fail: the API exposes example inputs but not their expected outputs, so any verdict would be guesswork - check the Output: lines in that problem's question.md. Problems needing non-JSON inputs (linked lists, trees) or with no single entry point (design problems) say so rather than running incorrectly.

question.md is fetched once per problem and never re-fetched, so it costs nothing on subsequent syncs. Disable it entirely with leetvault config write_question_md false. Problem statements remain the property of LeetCode; each file notes this.

Deduplicated by default within a 24h window — see --keep-all above to change that.

Asking questions

analysis.md answers the questions leetvault thought to ask. For everything else:

leetvault ask two-sum "why a hash map and not sorting first?"
leetvault ask 3348 "is this actually greedy, or dynamic programming?" --push

The question and answer append to that problem's qa.md, so the thread accumulates and earlier exchanges are carried into later ones as context. A problem can be named by slug, number, or part of its title. --no-save prints the answer without keeping it.

Context comes from the repository — question.md, your solution, and any existing analysis.md — not from the database. That is what lets the same command run in CI.

Asking from GitHub

leetvault bot --install

If the GitHub CLI is installed and signed in, that one command does everything: writes the workflow and issue template, uploads every stored API key as an encrypted repository secret, sets the provider (and model, if you have pinned one) as repository variables, grants the workflow permission to commit, and pushes. Then open an issue titled

[two-sum]: why a hash map?

and the bot replies as a comment and commits the exchange to Problems/two-sum/qa.md. Replying in the thread asks a follow-up: the comment is the question, and the existing qa.md is fed back in as context, so "why?" or "show me that trace" makes sense on its own. It works from a phone, since it is just GitHub.

gh is used rather than the REST API for two reasons: uploading a secret means encrypting it with the repository's public key, which would otherwise mean a new dependency, and gh's token carries the workflow scope that a fine-grained PAT usually lacks — without it GitHub refuses any push that touches .github/workflows/. If gh is missing or a step is not permitted, the files are still written and the manual steps printed; --manual skips the GitHub calls entirely.

To change which model the bot uses without reinstalling:

gh variable set LEETVAULT_AI_PROVIDER --repo <owner>/<repo> --body groq
gh variable set LEETVAULT_AI_MODEL    --repo <owner>/<repo> --body openai/gpt-oss-120b

LEETVAULT_AI_MODEL is optional — leave it unset and each provider uses its own default. Providers retire models without much notice, and a retired model is a 404 rather than a warning - if a backend suddenly stops answering, check its model list before anything else.

Two things worth knowing:

  • Your API key lives in GitHub's encrypted secrets, never in the repo. leetvault bot prints the exact settings page and secret name for the backend you have configured.
  • Only issues you open are answered. On a public repo anyone can file an issue, and without that check every stranger would be spending your quota. The workflow compares the issue author against the repository owner and does nothing otherwise.

GitHub renders Markdown but does not run it, so a chat box inside Problems/<slug>/ is not possible. Issues are the closest real equivalent — and they give you threading, search, notifications and history for free.

Discovering commands

leetvault commands          # every command and what it does
leetvault commands --full   # plus every argument and option

Generated from the CLI itself, so it cannot fall behind the commands that actually exist.

The web page

README.md is a table. leetvault site builds something you can actually read in:

leetvault site              # write the page into the repo
leetvault site --publish    # …and push it and turn on GitHub Pages

It publishes at https://<you>.github.io/<repo>/ and gives you:

  • an index you can search and filter by topic, difficulty or language, and sort by any column;
  • a problem view with the question, your code and the analysis in three panes you can drag to any width — the sizes are remembered;
  • every earlier submission behind one button in the code pane, latest shown first;
  • the problem index as a slide-over drawer, so the three panes keep the full window;
  • My Course — your own notes, organised however you like;
  • syntax-highlighted code, with the colour scheme chosen in Settings;
  • five pane arrangements - three columns, two-left, two-right, one-top, three rows - each remembering its own sizes;
  • collapsible hints, tables, images and everything else rendered exactly as GitHub renders the same file - the page links to GitHub on every problem, so the two showing different documents would read as one of them being broken;
  • a light/dark switch that follows your system until you choose otherwise, and a sidebar you can fold away (or press \) when you want the whole window.

Nothing is duplicated. The page reads Problems/<slug>/question.md, latest.*, analysis.md and history/* at the paths sync already writes, so a file you edit on GitHub changes what the page shows immediately. Only the catalogue is generated, and leetvault sync refreshes it, so new problems appear without re-running site.

My Course is the Course/ folder: Markdown files, nested in whatever folders you like, and the hierarchy on the page is the folder layout — reorganising is a file move. Because GitHub Pages is static and cannot write anything back, the Edit and Add buttons open GitHub's own editor, which keeps every note in git history and means no access token is ever embedded in a public page.

The page is plain HTML, CSS and JavaScript with no build step and no CDN - including its Markdown renderer and syntax highlighter - so a repo that still exists in five years still renders. Asset URLs carry a content hash, so an update is never served from a stale cache.

Rendered the way GitHub renders it

Every problem page links to the same file on GitHub, so the two showing different documents would read as one of them being broken. The page's renderer is therefore written against GitHub's own output as its specification, not against a Markdown standard in the abstract.

That covers what these files actually contain: images; tables written as HTML, which is how LeetCode writes several of them; tables that run past their last real row; nested and loose lists; hints as collapsible sections; hard line breaks; bare URLs; code spans that cross a line ending; indented code; fenced code with a language, which is highlighted; setext headings; table alignment and escaped pipes; entities; backslash escapes; strikethrough; and the overlapping emphasis runs LeetCode writes, such as *the **lexicographically smallest* *subsequence** of*, which need CommonMark's delimiter-run algorithm to close in the right order.

It matches GitHub on all 446 question.md, analysis.md and notes.md files of a 149-problem repository. Because that check needs the network, GitHub's own rendering of 35 constructs drawn from those files is recorded in tests/fixtures/github_markdown.json, and a test holds the page to it offline - see docs/DEVELOPER.md to regenerate it and docs/ARCHITECTURE.md for the reasoning.

Two consequences worth knowing. <u> is not underlined, because GitHub's sanitizer drops that tag - a subsequence LeetCode underlines renders as plain text on github.com, and the page matches GitHub rather than LeetCode. And no attribute survives on any tag except an href on <a> and a src on <img>, and only one that is http, https, mailto, a fragment or relative: analysis.md is model-generated and notes.md is free-form, so both are untrusted input, and a tag rebuilt from its name alone leaves no room for an onerror=.

Command reference

Every command with every argument and option. Generated from the CLI itself, and a test fails if this section falls behind it.

leetvault ai

Set up optional AI-generated solution analysis (off by default).

Flag / argument Kind Meaning
--disable option Turn AI analysis off.
--set-key option Store an API key for gemini, groq, nvidia, or anthropic.
--show option Print the current AI settings.

leetvault analyze [problem]

Regenerate an existing analysis.md with a different AI backend.

Flag / argument Kind Meaning
problem argument Problem slug, number, or part of its title. Omit with --all or --from.
--provider, -p option Backend to use instead of the configured one.
--model option Model override for this run.
--all option Re-analyse every problem.
--from option Re-analyse only what a given provider generated, e.g. groq.
--list option Show which model wrote each analysis and exit.
--yes, -y option Skip the overwrite confirmation.

leetvault ask <problem> <question>

Ask a question about one of your solutions.

Flag / argument Kind Meaning
problem argument Problem slug, number, or part of its title.
question argument What you want to know about it.
--provider, -p option Backend to use instead of the configured one.
--model option Model override for this run.
--repo option Repository to read from and write to (default: configured).
--save option Append to the problem's qa.md.
--push option Commit and push the qa.md.

leetvault bot

Set up a GitHub bot that answers questions asked as issues.

Flag / argument Kind Meaning
--install option Write the GitHub Actions workflow into your repo.
--repo option Repository to install into.
--show option Print the setup steps and exit.
--manual option Only write the files; do not touch GitHub settings.

leetvault commands

List every leetvault command and what it does.

Flag / argument Kind Meaning
--full option Include every argument and option for each command.

leetvault config [key] [value]

Get or set persistent config (repo URL, DB path, dedup window, etc.).

Flag / argument Kind Meaning
key argument Config key to get/set, e.g. repo.url.
value argument Value to set; omit to read.

leetvault import

Full history import of all accepted submissions (resumable).

Flag / argument Kind Meaning
--site option LeetCode site: com or cn.
--keep-all option Disable same-day dedup; keep every accepted submission.

leetvault login

Store LEETCODE_SESSION + csrftoken (and optionally a GitHub PAT) in the OS keyring.

Flag / argument Kind Meaning
--leetcode option Only refresh the LeetCode session; leave the GitHub PAT alone.
--github option Only refresh the GitHub PAT; leave the LeetCode session alone.
--force option Re-prompt even for credentials that are still valid.

leetvault logout

Remove stored credentials from the OS keyring.

leetvault site

Build a browsable web page for your solutions repo.

Flag / argument Kind Meaning
--publish option Commit, push, and enable GitHub Pages for the repo.
--repo option Repository to write the site into.

leetvault status

Show session validity/expiry, sync state, and repo config.

leetvault sync

Incremental sync: pick up new accepted submissions since the last sync.

Flag / argument Kind Meaning
--site option LeetCode site: com or cn.
--keep-all option Disable same-day dedup; keep every accepted submission.

leetvault watch

Poll for new accepted submissions and sync+push automatically.

Flag / argument Kind Meaning
--interval option Polling interval in seconds (60-120).
--site option LeetCode site: com or cn.

Global flags, usable with any command: --install-completion, --show-completion, --help.

leetvault commands --full prints this same listing in the terminal.

Honest limits

  • watch is polling (default 90s, configurable), not a real-time push — LeetCode has no public webhook/streaming API.
  • LeetCode may Cloudflare-challenge automated HTTP clients; leetvault fails gracefully rather than faking success.
  • Storing session cookies for automated access may be against LeetCode's Terms of Service. Use at your own risk, against your own account only.
  • LeetCode has no official API — every endpoint leetvault uses is reverse-engineered and could change without notice.
  • AI analysis is only as good as the model behind it. leetvault checks that a response is complete and rejects truncated or stub replies, but it cannot verify that a dry run or a complexity claim is correct — read generated analysis as a strong draft, not an authority, and prefer a reasoning-capable model for Hard problems.

Docs

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

leetvault-0.19.2.tar.gz (191.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

leetvault-0.19.2-py3-none-any.whl (115.8 kB view details)

Uploaded Python 3

File details

Details for the file leetvault-0.19.2.tar.gz.

File metadata

  • Download URL: leetvault-0.19.2.tar.gz
  • Upload date:
  • Size: 191.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for leetvault-0.19.2.tar.gz
Algorithm Hash digest
SHA256 4cf6d216e2406379649c6c90cfed6dcc6c78653311dba8ca8f81beff1a7567fc
MD5 41e003653f8c67ba655275ce16338c99
BLAKE2b-256 ef2af2e6bc61b2a7a46661c3a484fca92460851aa06bc4d0cfea6327a74c16d7

See more details on using hashes here.

Provenance

The following attestation bundles were made for leetvault-0.19.2.tar.gz:

Publisher: publish.yml on priyadip/LeetVault

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file leetvault-0.19.2-py3-none-any.whl.

File metadata

  • Download URL: leetvault-0.19.2-py3-none-any.whl
  • Upload date:
  • Size: 115.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for leetvault-0.19.2-py3-none-any.whl
Algorithm Hash digest
SHA256 5de1f79b3f60d8d84b7bd6d7b0579de9f136b58c172ab7aa5f2b9f69535ca239
MD5 c0d1d9e6bf9f3eddaba6cf778c4d7158
BLAKE2b-256 2efc9abd2a92a7361c85b3e0ae67bf5f1d67c9e4d8e59eef0bc220ab697a2546

See more details on using hashes here.

Provenance

The following attestation bundles were made for leetvault-0.19.2-py3-none-any.whl:

Publisher: publish.yml on priyadip/LeetVault

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.19.2 This release

2 files

0.19.1

2 files

0.19.0

2 files

0.18.0

2 files

0.17.1

2 files

0.17.0

2 files

0.16.4

2 files

0.16.3

2 files

0.16.2

2 files

0.16.1

2 files

0.16.0

2 files

0.15.3

2 files

0.15.2

2 files

0.15.1

2 files

0.15.0

2 files

0.14.1

2 files

0.14.0

2 files

0.13.0

2 files

0.12.2

2 files

0.12.1

2 files

0.12.0

2 files

0.11.1

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.2

2 files

0.8.1

2 files

0.8.0

2 files

0.7.1

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page