A Claude Code skill that turns an engine-annotated PGN into a deep, broadcast-style game review
SaferSkills independently audited chess-analyst (Agent Skill) and scored it 92/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 2 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 2 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Turns an engine-annotated PGN into the kind of write-up a strong coach or a top-event commentator would give: a narrative, not a wall of numbers.
The engine has already done the math. A PGN exported from Lichess (or chess.com with analysis) embeds everything we need in the move comments:
[%eval 0.18] — evaluation after the move (White's perspective; #-1 = mate)[%clk 0:14:13] — clock after the moveNxh7?? (0.27 → -3.40) Blunder. c3 was best.(...) parenthesesSo do not reach for Stockfish when the PGN is already annotated. Your job is the layer the engine can't do: a coherent human story built on those numbers, plus time and momentum reading.
*.pgn(also *.txt containing PGN — Lichess sometimes exports that way), then settle two things with the user (use AskUserQuestion):
analyze (offer "all of them" as an option). With exactly one, skip the question and just use it.
"Black"). Match it against the White/Black headers to fix the point of view: the whole report is written for that player — their mistakes, their missed wins, their lessons — with the opponent treated as the opposition. If the user doesn't care, fall back to a neutral two-sided write-up.
--focus with theplayer from intake so the coach-level metrics are computed from their side:
python3 scripts/analyze.py <game.pgn> --focus "<player|white|black>" --json /tmp/chess.jsonIt returns: headers, per-move eval/clock/time-spent/accuracy, momentum series, judgement counts, ranked critical moments, per-side time summary, and — when --focus is set — an opportunities block from the focus player's point of view:
missed_wins — positions you were clearly winning and let slip with your own move,declined_gifts — opponent blunders your reply failed to punish,best_moves — your strongest moves (to credit, not just criticize).engine_annotated is false, the PGN has no evals. Tell the user, andoffer the Stockfish fallback (see below) rather than inventing evaluations.
python-chess is available:
pip install chess # one-time, optional
python3 scripts/render_boards.py <game.pgn> --plies <list> --out diagrams/Pick plies from the top critical moments. Skip silently if the lib is absent — diagrams are a nice-to-have, not the point.
templates/report.md. Save it next to the PGNas <game>_analysis.md. Put a <!--ply:NN--> anchor before each critical-moment header (its ply from critical_moments) — harmless in the .md, and it lets the next step wire that heading to the board. Always glyph the move in each critical-moment heading (?? blunder, ? mistake, ?! inaccuracy, !? practical, ! good, !! brilliant) — the engine only flags errors, so add the glyph yourself for strong/practical moves. That glyph drives a colored at-a-glance verdict chip in the HTML, so the reader sees what kind of move it was without studying the position.
visual counterpart to the report and the main deliverable people actually open: one self-contained .html (no CDN, opens offline) with a clickable eval graph, an embedded board at every critical moment — each steps through what was played (red arrow) and toggles to the engine's best_line (green arrow), "here's how it should have gone" — each board carries file/rank coordinates and each moment a color-coded verdict chip (blunder / good / practical, from the heading glyph) — and a glued side quiz that auto-picks the 2-3 sharpest mistakes and asks "what would you play?", then reveals the move actually made plus a mini-explanation. Always produce it; only skip if the user says no.
python3 scripts/build_artifact.py <game.pgn> \
--json /tmp/chess.json --md <game>_analysis.md --out <game>_analysis.htmlAdd `--flip` when the focus player has the black pieces — it orients every board with that player's side at the bottom, the way they actually saw the game. (No flag = White at the bottom, the default.)
Pass `--lang` to match the report's language so the HTML chrome (buttons, verdict chips, quiz) speaks the same language as your prose — --lang en for an English report, ru is the default. Built-in: ru, en (see the LANGS table in build_artifact.py to add more). Write the report's section headings in that same language too.
Needs python-chess — it turns SAN into the board positions the stdlib analyzer never tracks. If it's genuinely not installed, try pip install chess first; if that fails, the script exits cleanly and the Markdown report stays the deliverable. Because each critical moment renders its own inline board, put a <!--ply:NN--> anchor before every critical-moment ### header (step 4) — that's what places a board there.
Lean on the data, never hand-wave. Concretely:
opening plan, where it tipped, the decisive sequence, the finish.
best_move/best_line was, why the played move fails, in words.
the player wanted something. Name the plan and how it backfired: "you played Nxh7 going for the classic bishop sac, but the h-file never opened and you were just down a piece." This "wanted X, got Y" framing is the single most coach-like thing in the report — do it for each real mistake, inferring the idea from the position and the line, not from numbers alone.
inhuman (a quiet computer-only resource) and the played move was reasonable, say so — separate "this lost" from "this was a forgivable practical choice." Don't hold the player to a standard no human meets.
opportunities) deserve their own beat:positions you were winning and let go (missed_wins), and opponent blunders you didn't punish (declined_gifts) — these sting more than a plain blunder count and are exactly what a coach circles.
best_moves to praise the player's genuinely strongmoves. A report that's all errors demoralizes; a coach builds on strengths too.
time_spent_s with blunders. A long thinkthat still produced a mistake, or a fast move in a critical position, is a real finding — cite the seconds. (In the sample game, both sides' single longest thinks were both blunders — that is the kind of observation to surface.)
momentum series. Repeated swings, awinning position thrown away, mutual blunders trading the advantage — describe the psychological shape of the game, but ALWAYS anchored to eval+clock numbers, never as free-floating "he got nervous."
mistakes: group them by type (tactical oversight, opening prep, time trouble, converting a winning position, prophylaxis…) using the judgements and the best_line the engine gives. For each recurring pattern, name a concrete training takeaway the player can act on next time — anchored to specific moves from this game, not generic advice.
Authoritative, specific, readable — the register of a strong-event broadcast or a serious post-game coaching session. No filler. Praise good moves where the eval supports it; be honest about mistakes without being snide.
Report language: write in the language the user is speaking to you in; default to Russian if it's ambiguous. Don't ask about it at intake — infer it. Only switch if the user explicitly requests another language.
If engine_annotated is false and the user still wants a full analysis, you may shell out to a local stockfish binary to evaluate each position. This is the exception, not the default path. Confirm with the user before installing or running anything heavy.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.