Exercise-driven learning framework — turn any course into a game of
edit → save → auto-check → advance
English | 简体中文
d2x owns the learning loop — session progress, file watching, auto-advance — and nothing else. Courses plug in underneath through a small NDJSON Provider protocol; frontends consume a one-way event stream on top. It doesn't know C++, build tools, or how to compile anything.
xlings install d2x -y # install d2x (needs xlings: https://xlings.d2learn.org)
d2x install d2mcpp # grab a course, environment auto-configured
cd d2mcpp && d2x checker # start learning — edit, save, it re-checks and advances| Command | What it does |
|---|---|
d2x checker [name] |
interactive practice loop (substring match to jump to an exercise) |
d2x status |
read-only progress overview, grouped by chapter |
d2x install <pkg> |
fetch a course from the d2x index |
d2x book [-p PORT] |
preview the course's book locally; automatically avoid occupied default ports |
d2x new <name> |
scaffold a new course from the template |
d2x config |
interactive .d2x.json configuration (language, UI, editor, LLM) |
d2x list [query] |
search available courses |
Useful flags: --lang zh|en, --ui tui|print, --emit-events (NDJSON stream for external frontends).
d2x book uses the first available localhost port in 3000–3099. To select
one explicitly, run d2x book --port 8080 (or -p 8080). An explicitly
configured port is strict: a conflict produces an error rather than changing it.
Priority: --port > D2X_BOOK_PORT > project .d2x.json > ~/.d2x.json > automatic selection. For a persistent setting, add "book_port": 8080 to
one of those config files. Values must be integers from 1 to 65535; remove the
setting to restore automatic selection. d2x config preserves this field.
Port checks cover IPv4 and IPv6 localhost addresses. Another process can still claim a port between the check and mdBook's startup; if that happens, rerun the command or choose another port. Installation and serving errors return a nonzero exit status, with mdBook's output visible.
A course is a repository with a .d2x.json declaring one command — the Provider — that
answers three verbs: describe, exercises, check <id>. Any language works; the reference
implementation and conformance suite live in protocol/ and tests/.
See the architecture reference.
| Course | About |
|---|---|
| d2mcpp | Modern C++ core language features, exercises-as-tests |
| d2ds | Hands-on data structures |
Forum · Course index · Project template · xlings
The first d2x checker can build the course Provider and download its toolchain.
provider_startup_timeout bounds the entire initial describe command (default
900 seconds, positive integers only). D2X_PROVIDER_STARTUP_TIMEOUT overrides
project config, global config and the default, in that order. Output does not
extend this deadline. After startup, provider_idle_timeout still limits silence
in exercises / check (default 120 seconds; <= 0 disables that idle limit).
Preparation is visible in both built-in UIs. With --emit-events, a
preparing event precedes session and repeats every ten seconds:
{"event":"preparing","phase":"describe","elapsed":0,"timeout":900}Consumers must tolerate preparation and diagnostic output events before
session. Initialization and transport failures emit an error with code,
phase, and message, and exit nonzero. Expected exercise failures remain
verdict events and wait for edits. Provider stdout/stderr are saved separately
in .d2x/provider.stdout.log and .d2x/provider.stderr.log for the current
checker invocation; stderr is also displayed live. Logs may contain Provider
output and should be checked before sharing. Timeout/interrupt terminates the
child process tree. Ctrl+C during a Provider call returns 130.
See the startup/transport contract for error codes, compatibility and failure-injection commands.
Since 2026.10.21.1, user cancellation returns 130 and a cancelled code without Provider-failure diagnostics or a synthetic fail verdict.