Metadata-Version: 2.4
Name: leetvault
Version: 0.6.0
Summary: Mirror your LeetCode account (accepted submissions + source + metadata) into a normalized SQLite DB and a GitHub repo with an auto-generated README dashboard.
Project-URL: Homepage, https://github.com/priyadip/LeetVault
Project-URL: Repository, https://github.com/priyadip/LeetVault
Project-URL: Issues, https://github.com/priyadip/LeetVault/issues
Project-URL: Changelog, https://github.com/priyadip/LeetVault/blob/main/CHANGELOG.md
Author: leetvault
License: MIT
License-File: LICENSE
Keywords: backup,cli,github,leetcode,sqlite,sync
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Version Control :: Git
Requires-Python: >=3.11
Requires-Dist: gitpython>=3.1
Requires-Dist: httpx-retries>=0.2
Requires-Dist: httpx>=0.27
Requires-Dist: jinja2>=3.1
Requires-Dist: keyring>=25.0
Requires-Dist: pyjwt>=2.8
Requires-Dist: rich>=13.7
Requires-Dist: sqlalchemy>=2.0
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest-mock>=3.14; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# 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

```bash
pip install leetvault
```

Requires Python 3.11+. See [docs/DEVELOPER.md](docs/DEVELOPER.md) for an editable/dev install.

## Quickstart

```bash
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](#refreshing-credentials).
- `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, ...).

### 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:

```bash
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.

### `--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:

```bash
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:

```bash
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](docs/TROUBLESHOOTING.md#i-solved-a-problem-again-but-github-still-only-shows-the-old-solution)
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
  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:

```bash
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`](#--keep-all) above to change
that.

## 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.

## Docs

- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — data flow, module responsibilities, and every
  place live API behavior diverged from initial assumptions.
- [docs/DEVELOPER.md](docs/DEVELOPER.md) — dev setup, project layout, running checks.
- [docs/FAQ.md](docs/FAQ.md)
- [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)
- [CONTRIBUTING.md](CONTRIBUTING.md)
- [CHANGELOG.md](CHANGELOG.md)

## License

MIT — see [LICENSE](LICENSE).
