Kibitz User Guide

This guide covers everything you can click in the app and every feature that is currently CLI-only. It is also available inside the app: press Help & tour at the bottom of the navigation rail (Esc closes it).


The window at a glance

The rail, item by item

In the data tables used across the app (Database, Opening tree, Position search, Profile, Prep), some column headers sort: clicking one cycles ascending → descending → original order.


Home

The startup screen: your day at a glance, built only from real data — absent data produces absent panels, never invented widgets. Needs an open database ("Open a database to see your day." otherwise).

When nothing at all is due — no cards, no new games, no findings, no commitment — Home degrades to a short honest list ("Nothing due today. / No new games this week. / Build a profile to surface findings.") instead of padding the screen.


The Game view

Header bar

These three buttons are enabled only for games loaded from the database.

Board column

Keyboard map (Game view)

Global to the Game view, but ignored while you are typing in a text field, while a modifier key is held, and while a dialog (help, tour, promotion picker) is open. The status strip's right cell repeats the active screen's working shortcuts.

Explain panel

The Explain rail item (COACH) toggles the panel on or off. While it is on, the free static screen runs automatically at every ply — no keypress, and still no engine: the tactical screen is static analysis.

The panel is bounded: it takes a fixed share of the right pane so the moves list below always keeps room. Two controls manage the prose:

Panel anatomy:

The development coach (openings)

In the opening the coach speaks from the classical principles, voiced as dreams rather than rules: which minor pieces are still asleep at home, whether the king has castled yet, an early queen sortie, a piece that keeps moving while its colleagues wait, and the unclaimed center pawns. Because "this knight already moved twice" needs the game's history, this layer works from the move list — it is automatic in the Game view (the panel receives the moves so far) and in Annotate narrations; explaining a bare pasted FEN shows only what a lone position can prove. CONSIDER chips follow suit: early developing moves, castling, and center pushes now appear as candidates, with knights nudged ahead of bishops while the structure is still unresolved.

The coach is also book-aware: while the position is still inside the bundled openings book it will not lecture principles — you get a single quiet line ("Still in the book — theory has walked this road.") and the development voice starts at the first move that leaves the book. In Annotate, the book line appears once per game, and the development story re-tells itself only when it actually changes (a sortie or wandering piece appearing, the phase turning). Everything here is static — no engine is spawned for any of it.

Evidence overlay language

One shared vocabulary, identical in both themes and both board treatments:

Evidence renders at 44 % intensity by default and at 100 % for the hovered sentence.

Moves panel

A move-pair grid: number column, White's and Black's moves per row, NAG glyphs on the moves, and the per-move stored evals (muted = legacy import; hover any eval for its engine). Comments render as serif rows under their move; variations render as tagged rows — FRESH (named after one of the game's fresh engine identities), LEGACY (imported engine lines / pre-2020 years), or plain human lines.

Previewing variations: click a variation row's move text to load that line onto the board. The board jumps to the variation's first move (the position where it branches off the mainline), and an accent PREVIEWING VARIATION pill appears under the board with the line's name, its own ◀ / ▶ controls and a ← Back to game button. ← / → step within the variation while the preview is active; Esc, the pill's back button, or any main-game navigation (clicking a mainline move, the main Prev/Next controls, ↑/↓/Home/End) exits and the board returns exactly where you were. While previewing, the eval bar and Explain panel pause — they describe the *main* game, so instead of pretending, they sit out until you're back. (Live analysis, if on, does follow the previewed positions.)

Editing (database games only):

Below the Moves panel, the repertoire footer — "Mainline → repertoire" (or "Line (first N plies) → repertoire" after stepping forward) with as White / as Black — sends the current line to the Openings SRS trainer. Re-adding a line never duplicates cards.

Promotion picker

Wherever a board accepts moves — Game-view variations, Openings SRS, Tactics, Endgames — dragging a pawn to the last rank opens a picker: choose Q / R / B / N by click or keys 1–4; Esc cancels. Underpromotions are fully supported, including tactics puzzles whose solution underpromotes.

When does the engine run?

Stockfish is off by default and never runs behind your back — but an explicit click *is* an explicit engine request:

  1. Stepping through games, the Explain panel and Annotate's static comments never involve the engine.
  2. Re-analyze and Annotate (game view) run their jobs immediately on click — enqueue *and* start the worker — with an inline progress row in the game view. So does confirming a database-wide batch (Database header / Settings), and Run pending jobs in the Jobs view (or the CLI run-jobs) resumes anything left pending. The status strip's engine dot and batch progress show it happening.
  3. Live analysis (game view, next to Flip): an explicit toggle that runs infinite analysis on the position you're looking at. The strip below the move controls streams the evaluation (White's point of view), the search depth, and the engine's best line in SAN with move numbers ("14.Qg3 dxe5 15.fxe5 Nh5 …") — the first 8 half-moves inline, the full line and node counts in the tooltip. It follows you as you step through the game and stops the moment you switch it off, load another game, or leave the game view. It always starts off — the setting is deliberately not remembered between sessions.

+ Add as variation (on the strip, database games): inserts the first 10 half-moves of the current engine line as a variation of the mainline move at your current position, tagged with a comment like ENGINE d24 +0.53 — so it renders as a FRESH engine variation in the Moves panel. The insert is a normal annotation edit: it is local until you Save (and Revert discards it). The button is disabled at the end of the mainline (there is no move to vary), for pasted PGNs (no database identity to save to), and while previewing a variation.


STUDY views

Database

The database screen: filter bar, the games table, and the two database-wide batch actions.

The Database screen, filtered to one event: the filter bar, the games table with result, ECO and provenance columns, and the batch actions
The Database screen, filtered to one event: the filter bar, the games table with result, ECO and provenance columns, and the batch actions

Crosstables

Every named event has a crosstable, opened from a Database row's Event cell or from the Game view header's event line (events named "?" get no affordance — there is nothing to cross-tabulate).

Opening tree

Transposition-aware: counts merge every move order that reaches the position, and the header shows the measured query time in ms.

The Opening tree at the start position: every first move with its game count, win/draw/loss bar, average Elo and performance
The Opening tree at the start position: every first move with its game count, win/draw/loss bar, average Elo and performance

Which games reached a position you set up yourself.

For an arbitrary FEN from a script, the CLI find-fen does the same query.


COACH views

Explain

A toggle for the Game view's Explain panel (documented above), not a page. The badge shows whether it is on.

Profile

Engine-derived findings about one player — and every number on the screen is a control: clicking a motif cell, a structure bar, a phase tile or a rate tile re-targets the evidence aside on the right.

Opponent prep

A four-step workflow in a header stepper — ① Opponent → ② Fingerprint → ③ Weak lines → ④ Master games — with free backward navigation (click any reached chip; each chip shows its chosen value) beside a persistent aside.

  1. Opponent — Search local lists up to eight matching names from your own database with their game counts. Fetch from Lichess / chess.com are visible but honestly disabled in this stepper — pull the opponent's games in first via the Account syncs screen (or the CLI lichess-sync / chesscom-sync). Picking a name advances. (Home's "Prep an opponent" lands here pre-searched.)
  2. Fingerprint — their repertoire by ECO family for the chosen colour (as White / as Black): share of games, score bar against the 50 % tick (weak families — under 50 % on 3+ games — marked), and the book-exit column ("leaves book at Nf6 (ply 5, 12×)"). AVG ELO is an honest "—" (not recorded per family yet). Click a row to rank the weak lines.
  3. Weak lines — ranked — cards ordered by prep value: rank, the line name (opening name, ECO, or "Out of book"), "by ply N · plays …", the score chip ("38% in 21"), and a serif paragraph explaining *why* this spot is weak, citing real counts (plus a note when it is also a book-exit point). Click one to see its master games.
  4. Master games — games reaching the exact position, with the stated ranking rule: both players rated 2200 or above, strongest pairings first. Click a row to open it in the Game view at the prep ply.

The aside persists across steps: a board showing the position under discussion (with a caption naming the line, ply and score), a PROFILE FINDING about this opponent (from *their* profile only — an honest absence line otherwise), Open his profile (Profile with the opponent subject), and Study in game view. Starting a prep is recorded, which keeps Home's "no prep started for X yet" truthful.


TRAIN views

Openings SRS (Repertoire Trainer)

Openings SRS before a session: repertoires with their due counts, and the session panel showing due, done, lapses and new
Openings SRS before a session: repertoires with their due counts, and the session panel showing due, done, lapses and new

Spaced-repetition review of your opening repertoires, scheduled with FSRS-4.5. Each card is a position where it is your colour's turn plus the repertoire move you must know there. Layout: repertoire column | board | session aside.

Building a repertoire:

cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  import-repertoire study.pgn white --name "main"

--name defaults to main; re-importing is idempotent (positions that already have a card are left untouched — first move in wins).

Reviewing:

Opening triage

After your games sync in, the triage tells you exactly where your opening play needs work — measured against your own repertoire cards, both colors. Layout: ranked lists | board + detail aside.

Type your name as it appears in your games (suggestions come from the local database; all your name forms and declared aliases count as you — see Profile's identity panel) and Run triage. The walk is static database work: the engine stays off.

Each recent game is classified at the FIRST moment it left your book:

When your play IS your repertoire — the reality check. If your cards say one thing but your own games overwhelmingly play another (the played move appears in at least 10 games AND at least 3× as often as you actually followed the card), the triage stops scolding and says so: "Your cards say 1... e5 — but you've played 1... c5 in 119 of 120 games. That looks like your real repertoire." The panel lists the lines you actually play from there (game counts, your score, opening names) with two honest choices: Adopt what you play rewrites the conflicting card to your real move (its training restarts fresh — the old memory was memory of the old move), adds cards for the rest of each line, and re-runs the triage; Keep training the cards dismisses the panel for this session and lists the deviation normally. Nothing is ever adopted automatically.

I know my answer — play it on the board. Every gap row (whole-opening holes and in-book gaps alike) and every reality-check panel also takes your own move directly: select the row and the position board accepts a move for your color (promotions included). Nothing is written until you confirm — "Set 1... Nf6 as your repertoire answer to 1. d4? [Set as my answer] [Cancel]". Confirming adds the line (the path to the position plus your move) as SRS cards and re-runs the triage; cancelling snaps the board back.

Rows are ranked by how many games hit the same point (the N× badge — transpositions collapse onto one row), then by earliest ply. The summary line reports the real shapes it found, e.g. "199 games scanned · your play disagrees with your cards at 1 position · 2 whole-opening holes · 3 in-book gaps". Selecting a row shows the position on the board, the line that reached it, the ECO name when it is a book position, and every source game as a link.

Extending the book (gaps and frontiers): Extend with engine (4 lines) asks Stockfish for a deep MultiPV analysis (4 candidate lines, depth 30) of the position. The click is the explicit engine request — the job is queued through the shared job queue and the worker starts immediately; the aside shows honest progress ("Queued — N jobs ahead of it", "Engine analysing…", or a retryable failure). Requests are idempotent: asking again for the same position reuses the existing job or result.

The finished result is stored durably (it survives restarts; rows with one show "engine lines ready"). Each candidate line renders with its white-POV eval and an Adopt button: adopting adds the line to that color's repertoire from this exact position — your moves in it become SRS cards (confirmed with the real cards-added count) and show up in Openings SRS on their normal schedule. A gap you adopt a line for stops being a gap on the next triage run; its frontier moves outward instead.

No repertoire yet? The triage suggests one from your games. You don't have to have named a repertoire before the screen is useful. The color tab you land on is one that actually has cards (with no cards anywhere, the color you have more games as) — never a dead tab. When the selected color has no cards, its games are honestly reported as skipped ("White games are skipped until a White repertoire exists — adopt one below.") and the triage reads your recent games as that color instead: it builds the tree of your own in-book moves (the bundled CC0 openings dataset decides what's "in book") and lists every line at least 3 of your games support — branching on opponent replies too — up to 12 lines, each with its game count, your score in those games, and the opening name. Adopt a single line or Adopt all N lines: your moves become SRS cards (real cards-added totals are shown) and the triage re-runs automatically, so you land on actual triage points for the repertoire you really play. If no games are found for your name at all, the message names the identity forms it searched — declare your online handle on the Profile screen's INCLUDES strip if it's missing. You can still build the book by hand instead: add lines from the Game view ("→ repertoire") or import a PGN study.

Hover a line to walk it on the board. Every prospective line — inferred lines, reality-panel lines, whole-opening-hole suggestions, and engine candidates (here and in the Opening lab) — scrubs on hover: sweep the mouse across the moves and the position board plays them out (a small caption says where you are, e.g. "after 3. Nf3"); leaving the line snaps the board back.

Opening lab

"I've struggled with my Nimzo for years without improving — which book moves should I actually pick and train?" The Opening lab answers that from your own games, with a reframe: it finds where the games actually *die*. Maybe the opening isn't the problem at all. Layout: picker + verdict + branch table | board + recommendation aside.

Step 1 — pick a cohort. Type your name (all your name forms and declared aliases count as you) and List my openings: every opening family you actually play, per color, with real game counts and ECO ranges ("Nimzo-Indian Defense · as Black · E20–E59 · 412 games"). Pick one; your last pick is remembered.

Step 2 — the verdict. For each cohort game the Lab computes, from a static walk (engine off): the ply it left opening theory (the bundled CC0 dataset), the stored eval at that exit, your first significant error (the first of your moves that drops the eval by ≥ 1.2 pawns from your point of view — stored evals only, fresh preferred over legacy imports), and the game's middlegame structure tags (the same classification the Profile uses). The one-paragraph verdict states where the damage happens, honestly:

Step 3 — branches. Every in-book position where it was your move, ranked by damage = frequency × score deficit (games × points lost vs a 50% baseline). Each row shows your actual move distribution with per-move score, stored-eval average, book status, and the opponent replies you really faced. Selecting a branch shows the position and its source games (deep-linked to the exact ply).

Step 4 — the recommendation. Extend with engine (4 lines) runs the same deep MultiPV book-extension job the triage uses (queued + worker started on click; idempotent). Each candidate line then shows its factors separately — never a single opaque score:

Adopt puts the line into that color's repertoire from the branch position — your moves become SRS cards on their normal schedule.

Step 5 — structure homework. Your first-error positions in the verdict's killer structures, each deep-linking the game at the error ply with the eval swing ("+0.1 → −1.6"). Reviewing those moments — not more move memorization — is the point when the verdict says middlegame.

Tactics

A tactics puzzle with the WHY THIS PUZZLE panel naming the motif, where it came from in your profile, and the puzzle's rating
A tactics puzzle with the WHY THIS PUZZLE panel naming the motif, where it came from in your profile, and the puzzle's rating

Puzzle drills over the Lichess puzzle database. Layout: mode column | board | WHY THIS PUZZLE reasoning aside. No engine is involved — solving is checked against the stored solution line. The header shows puzzle count, your tactics rating and attempt count.

The five modes (mode column; badges are real numbers or absent):

Solving — Next puzzle (⏎): the opponent's setup move plays after a beat, the clock starts (the clock renders only in the timed modes: speed and Woodpecker), and you play every solution move on the board, oriented to your side (opponent replies play automatically).

WHY THIS PUZZLE aside — a Coach | Neutral voice toggle (the same voice state as Explain), a voice-aware headline, and the body quoting the selector's per-pick reasoning verbatim in weakness mode ("picked because your games allow many … tactics (N allowed, M missed in your profile) — this puzzle's themes […] train that motif"). The facts block stays non-spoiling: MOTIF shows your profiled motif in weakness mode but otherwise says "revealed when solved" until you finish; SOURCE credits your profile or the Lichess puzzle id (CC0); RATING is the puzzle's. The footer tracks the session (solved/attempts) or the Woodpecker queue.

Importing puzzles — when no puzzles are imported yet, the board column offers the import: CSV path (default testdata/corpus/lichess_db_puzzle.csv) + Import CSV (puzzles below popularity 50 are skipped). Download lichess_db_puzzle.csv from database.lichess.org (CC0). The CLI import adds knobs:

cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  import-puzzles lichess_db_puzzle.csv --min-popularity 50 --max-rows 100000

The rating is Elo-style against fixed puzzle ratings (K = 40 for your first 30 rated attempts, then K = 20); only rated, motif and weakness attempts move it.

Endgames

The Endgames curriculum mid-tier: mastered drills ticked, one solved drill showing its clean streak toward mastery, and a drill in play graded against the tablebase
The Endgames curriculum mid-tier: mastered drills ticked, one solved drill showing its clean streak toward mastery, and a drill in play graded against the tablebase

A rating-tiered curriculum of classic theoretical positions, played out against the toughest defence available and graded against tablebase truth, never an engine score. Layout: curriculum column | board | feedback aside.

Tablebases: the Syzygy directory resolves from the KIBITZ_SYZYGY environment variable, else by walking up to testdata/syzygy. The repo script

scripts/fetch_syzygy_test_files.sh

downloads the complete 3-man set (under 100 KB) — enough for the test suite, but most drills have more pieces and then fall back to the heuristic defender; point KIBITZ_SYZYGY at a 3-4-5-man set for tablebase-verified play across the whole curriculum.


Play online

Play rated or casual games on lichess without leaving Kibitz (rail: TRAIN → Play online), over the official lichess Board API. The loop is: seek → play on the standard board → the finished game imports automatically with full provenance and shows up on Home under "New since", feeding the same profile and tactics machinery as any other personal/online game.

Connecting your account

  1. On lichess: Preferences → API access tokens → New personal access token, with the board:play scope (that one scope is enough).
  2. In Kibitz: Settings → LICHESS PLAY, paste the token, Save token.

The token is a secret and is treated like one: it is validated against your lichess account, then stored in its own file in the app config directory with owner-only (0600) permissions — never in the database, never logged, and never shown again in full (Settings displays only "configured · ends in …XXXX" and the username it signed in as). Clear token deletes the file and stops all play streams.

Time controls — rapid, classical, correspondence only

Lichess restricts third-party clients to rapid, classical and correspondence — no bullet or blitz. The seek card offers exactly that: realtime presets (10+0 up to 30+20, each labeled with its honest speed class) and correspondence with 1–14 days per move. A realtime seek keeps a connection open until an opponent is found (Cancel withdraws it); a correspondence seek is parked on lichess until someone joins.

Playing

The game runs on the standard Kibitz board — your color at the bottom, promotion picker included. Clocks tick locally from the last server state and resync on every move. Buttons: Abort (before move two), Offer draw / Accept draw / Decline draw, and Resign (with a confirm step). Closing the app mid-game is safe — especially for correspondence: on relaunch the Ongoing games list (from your lichess account) lets you rejoin any game in progress, with the full move list restored from the stream.

Fair play — assistance is off. Period.

While a lichess game is in progress no engine, no coach explanation, no live analysis and no suggestions are reachable from the play screen — the screen simply has none of those surfaces, and a visible notice says so. This is the lichess Terms of Service, and it is enforced structurally in Kibitz the same way the engine-off principle is (a regression test fails the build if an analysis surface is ever wired in). When the game finishes it is imported without any engine jobs; open it from Home or the Database afterwards and Annotate / Re-analyze it explicitly, like any other game.

Import details

Finished games import through the same machinery as account syncs: source kind "online", name lichess play: <username> <gameId>, the exact export URL as origin, and a license note. Duplicates are detected, so a game that later arrives again via a full Lichess account sync is linked, not duplicated.


DATA IN / OUT views

Import PGN / SCID

Note: pasting a PGN loads it for *viewing* — storing games in the database is a CLI import.

TWIC ingest

The full Week in Chess catalog — every issue from the earliest one the TWIC zip archive serves (issue 920, ≈ June 2012) through the latest known issue — with per-issue status. The rail badge shows the newest imported week ("wk NNNN").

TWIC data is for personal use only and is never bundled or redistributed; downloads go to your own database with full provenance. CLI equivalent: twic-sync (see "Downloading games" below).

Account syncs

Per-service cards for online accounts; the rail badge counts configured accounts. Usernames persist in the open database, and each service's last sync result (games imported / duplicates / failures, with its timestamp — or the error, verbatim) is stored and shown.

All syncs run one at a time on the background worker with a descriptive User-Agent; HTTP 429 rate limits are respected automatically (a sync can pause for a minute or more — the card says so rather than showing a fake percentage). CLI equivalents: lichess-sync / chesscom-sync / fics-sync.

Jobs

The engine job queue — the engine only ever runs through it:

Everything the engine does goes through this queue — annotate confirmations, re-analyze passes, database-wide batches. Nothing runs until you (or a batch confirmation) start the worker.


Status strip

Always visible along the bottom:


Settings

A single column of grouped cards; each row is label + explanation, the current value, and an action.


A URL hash applies once at startup, after the database opens:

#game=123&ply=24&theme=light&treatment=instrument&voice=neutral
#screen=profile&player=Carlsen, Magnus&claim=motif:WeakKing:allowed
#screen=prep&opponent=R. Halvorsen
#db=/path/to/other.sqlite&screen=home

Any subset works. Handy for demos, screenshots and shared findings.


End-to-end workflows

Start your day from Home

  1. Launch — the last database opens and Home greets you (with your commitment, if one is set in Settings → Schedule).
  2. Resume review continues the game you left; DUE TODAY → Review openings clears the SRS queue; Solve tactics drills weaknesses.
  3. A finding row under YOUR CHESS opens Profile with that claim's evidence preselected; NEW SINCE … rows open this week's imports.

Import games and browse them

  1. CLI: import-pgn / import-si4 (or a sync command) into a .sqlite file — see below.
  2. STUDY → Database → enter the path → Open (subsequent launches auto-open it).
  3. Filter, click a game, step through it in the Game view.

Run a database-wide annotate (or fresh analysis)

  1. STUDY → Database → Annotate database (or Fresh analysis pass; both also live in Settings → ENGINE & ANALYSIS).
  2. Read the estimate dialog — games to cover, estimated duration, and the quoted estimate basis — then Start. The jobs enqueue *and* the worker starts.
  3. Watch the inline job row (or Home's RUNNING panel / the status strip); Pause anytime — the run resumes exactly where it left off, and already-covered games are skipped on a re-run.

Annotate a single game and confirm tactics with the engine

  1. Load a game from the Database view.
  2. Header bar → Annotate — instant static comments; the engine checks run immediately (inline ANNOTATING row) and verdicts fold back automatically when the batch finishes: confirmed tactics lead with the engine's line, refuted ones vanish, and plan plies gain a recommended move vetted by the engine. (Jobs → Run pending jobs still resumes anything paused or left over.)
  3. Edit by hand in the Moves panel (comments, NAGs, variations) and Save; Export PGN to take the annotated game elsewhere.

Drill a weakness end to end

  1. COACH → Profile → Build profile (evals from a Fresh analysis pass make the phase/conversion numbers live).
  2. Click the loudest number — say a motif's ALLOWED cell. The evidence aside shows the supporting games; open one to land on the exact ply where the weakness appeared.
  3. Press Train this weakness — Tactics opens in weakness mode, seeded with that motif (the SEEDED chip shows it).
  4. Solve — the WHY THIS PUZZLE aside quotes why each puzzle was picked, and every result feeds your tactics rating.

Prep for an opponent

  1. Home → "Prep an opponent" (or COACH → Opponent prep) → search, pick the name.
  2. Fingerprint their colour, click a weak family → ranked weak lines → master games; Study in game view opens the top game at the prep position.
  3. Open his profile for the full findings; the prep is recorded so Home stops nagging about it.

Build and train an opening repertoire

  1. Load a model game, step to where your line ends, and use "→ repertoire: as White / as Black" under the Moves panel — or bulk-import a PGN study with the CLI import-repertoire.
  2. TRAIN → Openings SRS → pick the colour → Start review.
  3. Answer on the board or by typing; grade yourself Again/Hard/Good/ Easy — the buttons show the real next intervals.
  4. Come back when the rail badge (or the status-strip nudge) shows cards due — FSRS schedules the rest.

CLI-only features

Everything below has no UI entry point (exceptions are noted inline) — it exists only in the developer CLI (kibitz-cli) or the validation harness (wsui-validate). Run them from the repository root. The general form is:

cargo run --release -p kibitz-db --bin kibitz-cli -- --db <path.sqlite> <subcommand> [args]

--db defaults to kibitz.sqlite in the current directory; the database is created and migrated automatically. (A built binary works the same: kibitz-cli --db <path.sqlite> <subcommand> [args].)

Database creation & import (CLI-only)

cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite init
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  import-pgn games.pgn --source-name "My games" --origin "local file" \
  --license "personal data" --kind personal

--kind is one of personal|twic|online|other and controls duplicate priority.

cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  import-si4 /path/to/scidbase --source-name "SCID import" --kind personal
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  import-repertoire study.pgn white --name "main"
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  import-puzzles lichess_db_puzzle.csv --min-popularity 50 --max-rows 100000

Downloading games (CLI equivalents)

These all have UI homes now — the TWIC ingest and Account syncs screens (see above) — and remain available for scripting:

cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  twic-sync --from 1580 --max-issues 5

Later runs resume where the last one stopped:

cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite twic-sync
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  lichess-sync SomeUsername
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  chesscom-sync SomeUsername
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  fics-sync SomeUsername 2025 --month 6

Queries & reports (CLI-only)

cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  fingerprint "Carlsen, Magnus"
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  players carlsen
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite \
  find-fen "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
cargo run --release -p kibitz-db --bin kibitz-cli -- \
  explain-llm "r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 3 3"

CLI equivalents of app features

These duplicate UI functionality, useful for scripting and batch work (opening-tree, explain, stats also exist and mirror the Opening tree view, the Explain panel, and the database summary):

cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite export-pgn 123
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite annotate-game 123
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite reanalyze-game 123 --nodes 200000
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite run-jobs --max-jobs 100
cargo run --release -p kibitz-db --bin kibitz-cli -- --db mygames.sqlite profile "Carlsen, Magnus" --json

run-jobs needs an engine binary (set KIBITZ_STOCKFISH if it is not on PATH) and, like the Jobs view, folds verdicts back into annotations when the jobs finish.

WSUI validation harness (CLI-only)

wsui-validate measures the tactical screen's precision/recall against Lichess puzzles (positives) and engine-quiet positions (negatives); results are recorded in docs/VALIDATION.md.

cargo run --release -p kibitz-db --bin wsui-validate -- \
  --build-quiet-from mygames.sqlite --per-class 500 > quiet_fens.txt
cargo run --release -p kibitz-db --bin wsui-validate -- \
  --puzzles lichess_db_puzzle.csv --quiet quiet_fens.txt --per-class 2000
cargo run --release -p kibitz-db --bin wsui-validate -- \
  --puzzles lichess_db_puzzle.csv --emit-fixture 500 > puzzles_sample.csv

Where things are stored