Terminal-based flashcard reviewer for local Anki collections. Read and review your cards without leaving the terminal.
- Deck browser with collapsible hierarchy and due counts (new/learn/review)
- Card review with front/back flow and Anki's official scheduler
- HTML rendering to terminal: bold, italic, colors, lists, tables,
<hr> - Inline images via Kitty graphics protocol (Kitty, WezTerm, Ghostty)
- Three backends: the bundled sidecar, AnkiConnect, or the
anki-tui-bridgeadd-on (Anki's own queue and answer times while Anki Desktop stays open) - Audio playback for
[sound:]references (MP3, OGG) with auto-play - Write-back through Anki's backend — reviews sync with Anki desktop/mobile
- Dry-run mode for safe browsing without modifying the collection
- Sub-deck gathering — studying a parent deck includes all children
Requires Rust 1.85+ to build and Python 3.10-3.13 at runtime for the Python sidecar.
uv is only used for sidecar development tests; the released TUI does not require it.
git clone <repo-url>
cd anki-tui
cargo build --releaseBinary at target/release/anki-tui.
On first run, the sidecar installs Anki's official Python package (anki==25.9.4) from PyPI
into a managed virtual environment — a one-time download. For offline installs, provide a
local wheelhouse instead; if one is found, it takes precedence and no network is used:
scripts/build-sidecar-wheelhouse.shThe script chooses Python 3.13, 3.12, 3.11, or 3.10 in the same order as the TUI. To force a
specific supported interpreter, run PYTHON=/path/to/python3.13 scripts/build-sidecar-wheelhouse.sh.
By default the app looks for wheels in $XDG_DATA_HOME/anki-tui/wheels, then
~/.local/share/anki-tui/wheels, then an install-relative share/anki-tui/wheels, and finally
sidecar/wheels in the source tree. Set ANKI_TUI_WHEELHOUSE_DIR to override this.
The first run creates a managed virtual environment under $XDG_DATA_HOME/anki-tui/sidecar or,
if XDG_DATA_HOME is unset, ~/.local/share/anki-tui/sidecar. Set
ANKI_TUI_SIDECAR_HOME to override this.
# Auto-detect Anki collection
anki-tui
# Use a specific collection (e.g. a copy for testing)
anki-tui --collection path/to/collection.anki2
# Override media directory (if collection is a copy)
anki-tui --collection copy.anki2 --media-dir ~/Library/Application\ Support/Anki2/User\ 1/collection.media
# Browse without writing to the database
anki-tui --dry-run
# Resume the last review session (or set ANKI_TUI_RESUME=1)
anki-tui --resume
# Backends: sidecar (default) | anki-connect | anki-tui-bridge (see below)
anki-tui --backend anki-connect
anki-tui --backend anki-tui-bridge # our add-on, most capable
anki-tui --backend anki-connect --endpoint localhost:9000Anki holds an exclusive lock on the collection while it runs, so anki-tui cannot open the file itself. Three backends cover the possible situations, from least to most capable:
--backend |
Needs | Card order | Answer time | Notes |
|---|---|---|---|---|
sidecar (default) |
Python 3.10–3.13 | Anki's scheduler | recorded | Opens the collection directly; Anki must be closed |
anki-connect |
AnkiConnect | approximated from searches | not recorded | Anki stays open; the widest compatibility |
anki-tui-bridge |
the anki-tui-bridge add-on |
Anki's scheduler | recorded | Anki stays open; the most faithful |
anki-tui # sidecar: opens the collection itself
anki-tui --backend anki-connect # 127.0.0.1:8765 by default
anki-tui --backend anki-tui-bridge # 127.0.0.1:8766 by default
anki-tui --backend anki-connect --endpoint localhost:9000Anki stays the only writer of the collection in the two add-on modes, so anki-tui and
other AnkiConnect clients (scripts, agents) can run at the same time. --anki-connect
still works as a shorthand for --backend anki-connect (as does ANKI_TUI_ANKI_CONNECT=1),
and an AnkiConnect API key is read from ANKI_CONNECT_API_KEY. The bridge's optional token
comes from ANKI_TUI_BRIDGE_TOKEN.
AnkiConnect exposes searches, rendering, media and answerCards, but not the scheduler's
queue and not the answer time. The consequences, visible in the review header as
anki-connect:
- card order is approximated (learning, then review, then new; id order inside a bucket),
so cards answered
Again/Harddo not return during the session and buried siblings can still surface; - each review is logged with ~0 ms of answer time, because AnkiConnect starts the card timer immediately before answering. Tools that require a non-zero time (Anki Leaderboard, study-time statistics) then ignore those reviews.
The anki-tui-bridge add-on runs inside Anki, so it serves the scheduler's own queue, the
rendered card with its audio, the answer previews, and answers with the time the card
was on screen (measured by the TUI and capped by the deck preset, exactly like Anki's own
reviewer). Its review header label is anki-tui-bridge.
When a richer tier is running but not selected, anki-tui prints a one-line hint on
startup. The --anki-queue-bridge flag and ANKI_QUEUE_BRIDGE* variables from earlier
versions are gone: the bridge is a backend of its own now.
Undo/redo, bury and suspend are Anki's own reviewer actions, bound to Anki's default keys
(see below). The sidecar and anki-tui-bridge support all of them; AnkiConnect has no
bury action and no redo at all, so those keys report that the tier cannot do it. Undo
through AnkiConnect goes via Anki's GUI (guiUndo). The bridge needs add-on version
0.3.0 or newer for these actions; older add-ons are detected and reported.
With --dry-run no backend writes anything: the sidecar and the bridge walk their queues
without scheduling, the counts in the header tick down locally, bury/suspend only skip the
current card for the session, and undo/redo report that there is nothing to revert.
With --resume, the current deck is remembered in ~/.local/share/anki-tui/session.json
(respects XDG_DATA_HOME) and the next --resume run jumps straight back into reviewing it,
skipping the deck list. The session is cleared once the deck is finished. Sessions are only
recorded on runs started with --resume.
Press ? (or Shift+/) on any screen for an on-screen keymap, overlaid on the right; ?
or Esc closes it. While it is open, other keys are ignored. Hints are kept minimal
otherwise: the review footer only shows Space Show Answer, and the deck list has no footer.
| Key | Action |
|---|---|
j / k / arrows |
Navigate |
Tab / l / right |
Expand/collapse deck |
h / left |
Collapse deck |
Enter |
Study selected deck |
? |
Show keymap |
q |
Quit |
| Key | Action |
|---|---|
Space / Enter |
Show answer |
1 |
Again |
2 |
Hard |
3 / Space |
Good |
4 |
Easy |
j / k |
Scroll card content |
r |
Replay audio |
u / Ctrl+Z (Cmd+Z) |
Undo |
Shift+U / Ctrl+Shift+Z (Cmd+Shift+Z) |
Redo |
- |
Bury card |
= |
Bury note (all its cards) |
@ |
Suspend card |
! |
Suspend note (all its cards) |
? |
Show keymap |
q |
Back to deck list |
Undo/redo and bury/suspend work in both phases, like Anki's reviewer. Their result is shown
briefly in the header (Cards buried: 1, Undid Review, ...), and the actions are undoable
with u afterwards. Redo is Shift+U (Anki itself only has the Ctrl+Shift+Z menu
accelerator); r stays replay-audio.
- Starts a managed Python sidecar environment from bundled wheels
- The sidecar opens the collection with Anki's official Python package
- Deck counts, rendered card HTML, interval labels, and answers come from Anki's scheduler
- Converts HTML to styled terminal text via
scraper+ratatui - Live answers are written through Anki's backend;
--dry-runskips writes in-memory
- Requires Python 3.10-3.13; the first run downloads the official
ankiPython package from PyPI unless a local wheelhouse is provided.--anki-connectneeds no Python at all - Do not keep Anki Desktop open on the same collection while using the
sidecarbackend; use one of the add-on backends instead --backend anki-connectrecords reviews without an answer time and approximates card order; use--backend anki-tui-bridgefor exact behaviour--backend anki-connectcannot bury cards or redo; suspend and undo work (undo through Anki's GUI), and the other keys report the limitation--backend anki-tui-bridgeneeds add-on version 0.3.0 or newer for undo/redo/bury/suspend; older add-ons show an update hint- Add-on backends never drive Anki's reviewer window, so an open reviewer can show a stale card while the TUI answers
- With
--backend anki-tui-bridge, starting a deck selects that deck in Anki (the bridge never switches it back, because a switch after a review would shadow that review on Anki's undo stack and clear redo; Anki's own reviewer leaves the studied deck selected too) - Media references that point outside the collection's media directory (absolute paths elsewhere, or
..) are shown as placeholders but never decoded or played --dry-runis a no-write preview; it advances through queued cards for the session but does not simulate all future scheduler transitions, and undo/redo are unavailable- When running with
--anki-connect, no answer time is transmitted, which might cause troubles with Anki's and other tools (e.g. Anki Leaderboard) review counts
ratatui, crossterm, scraper, rodio, image, base64, thiserror, serde, serde_json, dirs, plus the sidecar wheels generated from sidecar/pyproject.toml / anki==25.9.4.
Useful sidecar overrides:
ANKI_TUI_SIDECAR_CMD: run a fully custom sidecar commandANKI_TUI_PYTHON: choose the Python executable used to create the managed venvANKI_TUI_SIDECAR_HOME: choose where the managed venv and sidecar script are storedANKI_TUI_WHEELHOUSE_DIR: choose where offline wheels are read from
Almost entirely with AI (Claude Code) — partly out of curiosity to see how far a "purely vibe-coded" project can go, partly to quickly validate a random idea I had while working on my dissertation. I'm surprisingly satisfied with the result, and hope this can be useful to someone out there as well.
0 comments
log in to comment.