# flashcards-srs

Spaced-repetition scheduling engine for flashcards, study decks and memory apps: a pure, deterministic SM-2 (SuperMemo 2) implementation plus Leitner boxes and deck analytics. schedule takes one...


Spaced-repetition scheduling engine for flashcards, study decks and memory apps — a pure, deterministic
**SM-2 (SuperMemo 2)** implementation plus the classic **Leitner** box system and deck analytics.
Packaged as a Leumas "intelligent microservice" adapter pack.

Every tool has a deterministic algorithmic core that works fully **offline** with no model. A couple of
tools (`schedule`, `difficulty`) can *optionally* enrich their study tip with an LLM when one is
reachable (via `../_shared/llm.js`), and silently fall back to a templated tip otherwise. Results carry a
`mode: 'heuristic' | 'llm'` tag when the LLM path is enabled.

## Tools

| Tool | Input | Returns |
|---|---|---|
| `schedule` | `{ card, grade, date?, options? }` | One SM-2 step: next `intervalDays`, updated `ease`, `repetitions`, `lapses`, `phase`, `due` date. Grade `<3` lapses the card (reset to 1 day). |
| `review` | `{ cards, date?, options? }` | Batch SM-2 — advances an array of `{ ...cardState, grade }` cards; returns per-card next state + deck accuracy/avg interval. |
| `leitner` | `{ card?, box?, correct, date?, options? }` | Moves a card up one Leitner box on correct recall, resets to box 1 on incorrect; returns new box, interval and due date. |
| `dueToday` | `{ cards, date?, options? }` | Buckets a deck into `overdue`, `dueNow`, `upcoming` (within `horizonDays`) and new cards; returns the flat `due` id list. |
| `deckStats` | `{ cards, date?, options? }` | Deck aggregate: state distribution (new/learning/review/mature), avg ease, maturity %, difficulty mix, and a day-by-day due forecast. |
| `difficulty` | `{ card, options? }` | Classifies a card `easy`/`medium`/`hard`/`leech` with a 0-100 difficulty index and a remediation tip. |
| `retentionEstimate` | `{ card, date?, options? }` | Forgetting-curve `R(t)=exp(-t/S)` recall probability now + the optimal next review for a `targetRetention`. |

## Card shape

All tools accept a defensive card object (all keys optional, sensible defaults applied):

```json
{ "id": "capital-of-france", "ease": 2.5, "interval": 6, "repetitions": 2, "lapses": 0,
  "box": 3, "due": "2026-07-20", "lastReviewed": "2026-07-14" }
```

## Usage

```js
import srs from './index.js';

// SM-2: a card recalled perfectly (grade 5) after 2 prior reps
srs.adapters.schedule({ card: { ease: 2.5, interval: 6, repetitions: 2 }, grade: 5 });
// -> { mode:'heuristic', ease:2.6, intervalDays:16, repetitions:3, phase:'review', due:'2026-07-30', ... }

// Leitner: a wrong answer drops the card to box 1
srs.adapters.leitner({ card: { id: 'x', box: 4 }, correct: false });
// -> { fromBox:4, box:1, intervalDays:1, due:'2026-07-15', note:'Incorrect — reset to box 1...' }

// What's due
srs.adapters.dueToday({ cards: deck, date: '2026-07-14' });
```

## SM-2 algorithm (implemented exactly)

- `grade q ∈ 0..5`. `q < 3` ⇒ lapse: repetitions → 0, interval → 1 day, lapse counter +1.
- `EF' = EF + (0.1 - (5-q)·(0.08 + (5-q)·0.02))`, floored at **1.3**.
- Interval: rep 1 → 1 day, rep 2 → 6 days, rep n>2 → `round(prevInterval × EF')`.

## DRY boundary

New capability — a memory/learning scheduler. It deliberately does **not** overlap:
- `datetime` / `ical` — generic date math and calendar-file generation (this owns study scheduling logic).
- `numbers` / `statistics` — generic numeric/series math (this owns the SM-2, Leitner and forgetting-curve models).
- `cron` — interval-string parsing (`5m`); unrelated to review scheduling.

Self-contained: Node built-ins only, zero npm deps. The only cross-pack import is `../_shared/llm.js`
for the optional study-tip enrichment.


---
Source: shared/engines/adapters/domain/flashcards-srs/README.md
Canonical: https://docs.leumas.tech/p/adapters/domain/flashcards-srs
