Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Installation

Pick the method that matches your platform or workflow.

Install Script (Linux & macOS)

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/shaankhosla/repeater/releases/latest/download/repeater-installer.sh | sh

Homebrew (macOS)

brew install shaankhosla/tap/repeater

Windows (PowerShell)

irm https://github.com/shaankhosla/repeater/releases/latest/download/repeater-installer.ps1 | iex

npm

npm install @shaankhosla/repeater

Optional: add a rpt shortcut

Use repeater in docs and scripts so examples stay canonical. If you prefer a shorter command locally, add rpt with one of these snippets.

macOS / Linux

Bash (~/.bashrc or ~/.bash_profile)

alias rpt='repeater'

Reload:

source ~/.bashrc  # or: source ~/.bash_profile

Zsh (~/.zshrc)

alias rpt='repeater'

Reload:

source ~/.zshrc

Fish (~/.config/fish/config.fish)

alias rpt repeater

Reload:

source ~/.config/fish/config.fish
ln -s "$(command -v repeater)" /usr/local/bin/rpt

On Apple Silicon you may prefer /opt/homebrew/bin; add sudo if permissions require it.

Windows

PowerShell (profile)

notepad $PROFILE

Add this line, save, and restart PowerShell:

Set-Alias rpt repeater

Command Prompt (per session)

doskey rpt=repeater $*

Optional: rpt.cmd shim (permanent)

Create rpt.cmd on your PATH containing:

@echo off
repeater %*

Quick Start

  1. Create a deck in Markdown (cards/neuro.md).

    You can put your normal notes here, `repeater` will ignore them.
    Once a "Q:,A:,C:" block is detected, it will automatically
    turn it into a card.
    
    Q: What does a synaptic vesicle store?
    A: Neurotransmitters awaiting release.
    
    ---
    Use a separator to mark the end of a card^
    Then feel free to go back to adding regular notes.
    
    C: Speech is [produced] in [Broca's] area.
    

    Alternatively, launch the built-in editor with:

    repeater create cards/neuro.md
    
  2. Index the cards and start a drill session.

    repeater drill cards
    
    • Space/Enter: reveal the answer or cloze.
    • O: open the first media file (image/audio/video) referenced in the current card before revealing the answer.
    • F: mark as Fail, Space/Enter: mark as Pass.
    • Esc or Ctrl+C: end the session early (progress so far is saved).
  3. Check your collection status.

    repeater check cards
    

    This launches the full-screen dashboard that shows totals, due/overdue cards, and upcoming workload; press Esc or Ctrl+C when you want to exit. Use --plain if you prefer a plain-text summary for scripts.

Card Format

Store decks anywhere, for example:

flashcards/
  math.md
  science/
      physics.md
      chemistry.md

Cards live in everyday Markdown. repeater scans for tagged sections and turns them into flashcards, so you can mix active-recall prompts with your normal notes.

  • Basic cards

    Q: What is Coulomb's constant?
    A: The proportionality constant of the electric force.
    
  • Cloze cards

    C: The [order] of a group is [the cardinality of its underlying set].
    

Parsing Logic

  • Cards are detected by the presence of a Q:/A: or C: block. A horizontal rule (---) or the start of another card marks the end.
  • Each card gets a hash (think fingerprint) built from its letters, numbers, and any +/- signs. Punctuation, spacing, and capitalization are ignored, so only meaningful text changes create a new history.
  • Metadata lives in cards.db under your OS data directory (for example, ~/Library/Application Support/repeater/cards.db on macOS). Delete this file to reset history; the Markdown decks remain untouched.
  • Multi-line content is supported.

Edge case examples

  • Markers must start at column 0. Indented Q:, C:, or --- lines are ignored by the scanner, so the snippet below produces zero cards.
      Q: Skipped
      A: Because the tag is indented
    
  • Next marker auto-closes the previous card. A new Q: or C: flushes the current buffer even without ---.
    Q: First?
    A: Ends here
    Q: Second starts now
    
  • Notes need a separator. Without a flush-left ---, trailing notes remain part of the last card.
    Q: Term?
    A: Definition
    This line still belongs to the answer
    
  • Basic cards require both tags. Missing or blank Q:/A: blocks throw a parse error for that card.
    Q: What is ATP?
    ---  ← rejected; no answer was captured
    
  • Cloze blocks need real [hidden] text. Empty brackets or unmatched [/] abort parsing.
    C: Bad []    ← rejected
    C: Half [good   ← rejected
    

Media in Cards

repeater scans every rendered card for media references (images, audio, and video). When the drill UI detects at least one supported file, you can press O before revealing the answer to open the first attachment in your operating system’s default viewer/player. This keeps cards lightweight in the terminal while still letting you jump into richer references on demand.

Supported formats

The following file extensions are detected:

  • Images: jpg, jpeg, png, gif, webp, bmp
  • Audio: mp3, wav, ogg, flac, m4a
  • Video: mp4, webm, mkv, mov, avi

Other links remain untouched so regular hyperlinks still work in your Markdown outside the drill UI.

Referencing media in Markdown

Use normal Markdown syntax for images (![Alt](path/to/file.png)) or links ([label](path/to/file.mp3)). repeater reads the destination path and decides if it looks like media based on the extension.

Relative paths are resolved from the directory that contains the deck file. For example, if your deck lives at notes/physics/waves.md, then:

![Standing Wave](figures/wave.png)
[audio](../audio/tone.mp3)

will look for notes/physics/figures/wave.png and notes/audio/tone.mp3. Absolute paths work too, but keeping media alongside your decks makes them easier to sync and move.

Opening media during a drill

While drilling:

  • The footer shows “media file found” whenever the current card links to supported media.
  • Press O (uppercase or lowercase) before revealing the answer to open the first listed file.
  • The file launches via the OS default handler (open on macOS, xdg-open on Linux, start on Windows), so whatever app normally opens that file type will appear.

If a file cannot be found you’ll see File does not exist: … in the terminal. Double-check the relative path from the deck file and ensure the media is synced locally.

Multiple attachments can be detected, and the first one will open today; broader selection support is on the roadmap.

Commands

repeater drill [PATH ...]

Start a terminal drilling session for one or more files/directories (default: current directory).

  • --card-limit <N>: cap the number of cards reviewed this session.
  • --new-card-limit <N>: cap the number of unseen cards introduced.
  • --rephrase: rephrase basic questions via the LLM helper before the session starts.

Example: drill all the physics decks and a single chemistry deck, stopping after 20 cards.

repeater drill flashcards/science/physics/ flashcards/science/chemistry.md --card-limit 20

Key bindings inside the drill UI:

  • Space/Enter: reveal the answer or cloze.
  • F: mark as Fail, Space/Enter: mark as Pass.
  • O: open the first media file detected in the current card (images/audio/video). The file opens in your OS default viewer before the answer is revealed.
  • Esc / Ctrl+C: exit the session.

repeater create <path/to/deck.md>

Launch the capture editor for a specific Markdown file (it is created if missing).

  • Ctrl+B: start a basic (Q:/A:) template.
  • Ctrl+K: start a cloze (C:) template.
  • Ctrl+S: save the current card; you’ll be warned if another card already uses the same meaningful text.
  • Arrow keys/PageUp/PageDown: move the cursor; Tab, Enter, Backspace, and Delete work as expected.
  • Esc or Ctrl+C: exit the editor.

Example:

repeater create cards/neuro.md

repeater check [PATH ...]

Re-index the referenced decks and open the interactive dashboard with totals for new, due, overdue, and upcoming cards (press Esc/Ctrl+C to exit).

  • --plain: print a plain-text summary to stdout instead of launching the dashboard.

Example:

repeater check flashcards/math/

repeater import <anki.apkg> <output-dir>

Convert an Anki .apkg export into Markdown decks. Existing files in the export folder are overwritten, so rerunning is safe. FSRS history is not yet transferred.

Example:

repeater import ~/Downloads/my_collection.apkg cards/anki

repeater llm [--set|--clear|--test]

Manage the optional OpenAI helper that can auto-cloze missing brackets and rephrase questions before a drill.

  • --set <KEY>: write the key to the local keyring (com.repeater/openai:default).
  • --test: verify the configured key by calling OpenAI.
  • --clear: delete the stored key; use this when rotating credentials.

Instead of --set, you can export REPEATER_OPENAI_API_KEY for one-off runs. Skip configuring this command entirely to keep the feature disabled.

LLM Usage

LLM helper (opt-in)

Opt in

  • LLM calls are off until you provide an OpenAI API key.
  • Skip every prompt to keep running fully offline.

API keys

  • repeater llm --set <KEY> saves the key via the OS keyring (com.repeat/openai:default), so macOS Keychain/Windows Credential Manager/libsecret hold it securely.
  • REPEATER_OPENAI_API_KEY overrides the keyring for temporary runs.
  • repeater llm --test confirms the key with OpenAI, repeater llm --clear forgets it instantly.

Cloze generation

  • Run repeater drill <deck>; if any C: cards lack [], repeater sends that text to OpenAI (gpt-5-nano) and patches the file before the drill continues.
  • Leave the API key prompt blank (or skip configuring a key) to keep the feature idle.

Question rephrasing

  • Run repeater drill <deck> --rephrase to rephrase basic Q: questions before the session starts.
  • The original answers are provided as context but are not revealed in the rewritten questions.

FSRS Scheduling

repeater schedules every review with the Free Spaced Repetition Scheduler (FSRS). The upstream model treats each card’s stability (how long it can be recalled) and difficulty (how hard the content feels) as latent variables and adjusts them after every answer. This page documents how repeater applies the model, along with the opinionated choices that make it feel lightweight in a terminal workflow.

Core Parameters

  • Target recall — Intervals are solved for a 90 % recall probability (TARGET_RECALL = 0.9), which is the default in FSRS research and keeps workloads manageable.
  • Weights — The 19 FSRS-4 weights (WEIGHTS) are compiled into the binary instead of being trained per-user. Everyone starts from the same curve, so reviews are predictable even without a calibration phase.
  • State tracked per card — Each row in cards.db stores stability, difficulty, interval_raw, interval_days, due_date, and review_count. The timers you see during drills are derived from these values, while the Markdown deck stays untouched.

Simplified Feedback Model

Classic FSRS expects four answer buttons. repeater distills that into two hotkeys: Pass and Fail. Internally those map to quality scores of 3 and 1 respectively, so the formulas for initial_stability, initial_difficulty, delta_d, and calculate_stability still behave correctly. The benefit is a fast keyboard workflow; the trade-off is that you can’t express nuances like “Hard” or “Easy”, so the algorithm falls back to its conservative defaults when scheduling recoveries after a lapse.

Early Review Ramp

FSRS is designed for day-scale intervals, so the code layers a short-term trainer on top:

Review count before the answerResultMax delay
0 (brand new)Pass/Fail1 minute
1Pass10 minutes
1Fail1 minute
2Pass1 day
2Fail10 minutes

These caps override the usual interval just for the first few answers, which keeps new material in front of you until you can reliably recall it. Once the review count exceeds two, the pure FSRS interval is used.

Learn-Ahead Window & Queueing

  • The spaced repetition queue treats anything due within the next 20 minutes as “due now”. This is the LEARN_AHEAD_THRESHOLD_MINS, and it means that when you sit down for a session you see cards that are about to become due so you don’t have to reopen the app later in the day.
  • During a drill, the interval returned from FSRS is compared against the same threshold. If it’s shorter than 20 minutes (for example right after a lapse) the card is immediately re-queued in the current session instead of waiting for a later run.
  • The daily queue pulls overdue cards first, then cards due later today, and only then does it sprinkle in new cards—subject to your optional daily limits. That ordering makes sure FSRS’s promises (“you’ll keep 90 % recall”) remain accurate even if you have a backlog.

What Happens After Each Answer

  1. The elapsed time since the last review is measured to compute the recall probability FSRS expected at the moment you answered.
  2. Depending on whether you pressed Pass or Fail, the algorithm updates stability and difficulty with the upstream formulas.
  3. A new interval is solved for 90 % recall, rounded, clamped, and—if applicable—shortened by the early-review caps above.
  4. Metadata in cards.db is updated atomically so stats, the check command, and future sessions all agree on the next due date.

Further Reading

Roadmap

  • Import from Anki
  • Allow scrolling to other cards in a collection while creating a new card
  • Edit an existing card while keeping the progress intact
  • Allow for a fuzzy search of existing cards
  • Use LLMs to import from various content sources

FAQ

How is scheduling different from Anki?

repeater schedules cards with the Free Spaced Repetition Scheduler (FSRS) targeting ~90 % recall, so you get dynamically computed intervals instead of SM-2’s fixed ease multipliers. Inside the drill UI there are only two buttons—Pass (2) and Fail (1)—which the code maps to FSRS quality scores of 3 and 1, respectively, while still applying the upstream stability/difficulty math plus the short “learning” ramp for your first few reviews. The end result feels faster to grade yet still reuses FSRS’s predictions.

Where does my progress live?

Your decks stay in plain Markdown wherever you save them, but progress metadata (stability, difficulty, due dates, etc.) is tracked in cards.db under the platform’s application data directory (for example ~/Library/Application Support/repeater/cards.db on macOS). Back up or sync that file if you want to keep review history when moving machines; deleting it resets scheduling without touching the Markdown decks.

What happens if I edit or move a card?

Each card gets a hash that only looks at the actual letters, numbers, and any +/- signs. We ignore punctuation, spacing, and capitalization, so cleaning up commas or case won’t touch your streak. Rewrite the wording itself and you’ll start fresh. Moving blocks between files is safe because the text stays the same.

Can I study ahead or repeat lapses immediately?

Yes. Anything due within the next 20 minutes is considered “due now”, so repeater check will show it and drills will surface it alongside overdue cards. During a session, cards that fail or return ultra-short intervals (under that 20‑minute window) are automatically added back into the current queue so you can clear them before quitting.

Does the Anki import carry over scheduling data?

repeater import converts .apkg exports into Markdown decks today, but it does not migrate Anki’s per-card FSRS/SM-2 history yet. Imported notes will be treated as new cards and scheduled fresh once they’re indexed.

I’m a developer—what’s the quickest way to run checks locally?

Use the just precommit recipe to run cargo fmt, cargo clippy --fix, cargo machete, and the full test suite behind SQLX_OFFLINE=true. The Justfile also includes helper recipes for launching repeater create, check, drill, and import against sample data plus the release workflow, so contributors can rely on those instead of memorizing individual cargo commands.


Still stuck or ran into a bug? Please open an issue at github.com/shaankhosla/repeater/issues with logs and repro steps so we can help.