Self-administered comprehension quiz for AI-assisted code
Project description
Examen
It's like an exam. For your own code. You will get a D.
I built this because I kept failing my own code reviews. I'd merge something Claude wrote, walk into standup the next day, get asked "why did you handle the edge case that way?" and freeze. Examen runs a 5-question quiz on your own repo. Each question shows you the code, the answer options, and a 1–5 confidence chip on one screen. You submit both together, so your gut goes on the record at the same time as your answer.
The results screen plots a heatmap: knowledge × calibration. Top-right is good. Top-left is dangerous — confident and wrong.
pip install examen
examen serve quiz.json
Local-only. No telemetry. No SaaS. Quiz artifacts generated by the
examen Claude skill, or hand-rolled (see the
included fixture).
Table of contents
- Try it in 60 seconds (no Claude Code needed)
- Full setup with Claude Code
- How it works
- CLI reference
- File locations
- Known limitations (v0.2.0)
- Development
- Troubleshooting
- Design docs
- License
Try it in 60 seconds (no Claude Code needed)
The repo ships a hand-crafted test fixture. You can take a real 5-question
quiz against it without involving Claude or code-review-graph at all — good
for seeing the UX before committing to the full setup.
git clone <this-repo> examen && cd examen
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .
examen serve tests/fixtures/example_quiz_small.json
Your browser opens at http://127.0.0.1:8780/start. Click Start quiz, work
through the 5 questions (each one takes your answer and a 1–5 confidence
rating together), and end at a results screen with a topic heatmap and a
calibration score.
The fixture covers fictional auth/billing/api modules — the questions are toys, but the flow is exactly what real generated quizzes look like.
Full setup with Claude Code
The end-to-end use case is: generate a quiz from the diff I just merged, take it, see what I don't actually understand.
Prerequisites
- Python 3.11 or newer
- Claude Code installed and working
code-review-graphfor indexing the target repo's call graph
One-time install
# 1. Install Examen from source (not on PyPI yet).
git clone <this-repo> examen && cd examen
python3.11 -m venv .venv && source .venv/bin/activate
pip install -e .
# 2. Drop the Claude Code skill into ~/.claude/skills/examen.
examen skill install
# 3. Install code-review-graph (see its repo for current install command).
pip install code-review-graph
Per-repo setup
In the repo you want to be quizzed on:
cd /path/to/your-project
code-review-graph build # indexes the repo; one-time per repo
This creates .code-review-graph/ (gitignored by Examen's gitignore template,
but if you're adding to an existing project add it yourself).
Generate and take a quiz
Inside that project, open Claude Code and type one of:
/examen:generate-diff # quiz on git diff vs main
/examen:generate-path src/auth # quiz on a directory
/examen:generate-repo # quiz on the whole repo
The skill writes a quiz artifact to .examen/quizzes/quiz-<uuid>.json in
the repo root and prints:
Ready. Run: examen serve .examen/quizzes/quiz-<uuid>.json
Run that command (from any shell, doesn't need to be Claude Code), and the
browser opens at http://127.0.0.1:8780/start.
How it works
Two halves, one JSON contract between them.
┌─────────────────────────────────────────────────────────────────┐
│ Generator (Claude Code skill: examen) │
│ - Reads code through code-review-graph MCP tools │
│ - Claude authors questions grounded in graph facts │
│ - Emits quiz-<uuid>.json (the artifact) │
└──────────────────────────┬──────────────────────────────────────┘
│ JSON file on disk
▼
┌─────────────────────────────────────────────────────────────────┐
│ Runner (Python, FastAPI + htmx + SQLite) │
│ - `examen serve <quiz.json>` opens browser at localhost │
│ - Confidence committed alongside the answer (server-locked) │
│ - Brier-style scoring: confident-and-wrong → 0.0, uncertain- │
│ and-wrong → 1.0 (perfectly calibrated honesty) │
│ - Two-channel heatmap: correctness × miscalibration │
│ - Longitudinal history in ~/.examen/history.db │
│ │
│ HARD RULE: runner never calls an LLM, never makes network │
│ requests beyond serving on 127.0.0.1. │
└─────────────────────────────────────────────────────────────────┘
The artifact format is the contract. Anyone could write a different generator (different model, manual authoring, custom static analysis) and the runner doesn't change.
Why confidence-alongside-answer matters
A multiple-choice question with 4 options gives you a 25% chance of guessing right. Without measuring confidence, "I got it right" doesn't distinguish between knowing and guessing. With a confidence chip submitted alongside each answer:
| Confidence | Correctness | Brier score | Interpretation |
|---|---|---|---|
| 5 (sure) | 1.0 | 1.00 | You knew it. |
| 5 (sure) | 0.0 | 0.00 | You were confidently wrong — the dangerous case. |
| 1 (no idea) | 0.0 | 1.00 | You said you didn't know, and you didn't. Calibrated. |
| 1 (no idea) | 1.0 | 0.44 | Got lucky — partial credit only. |
| 3 (medium) | 1.0 | 0.75 | Right with reasonable uncertainty. |
The "confidently wrong" cell is what the tool is hunting. The heatmap surfaces miscalibration separately from score so a topic where you scored 0.6 but were wildly overconfident gets flagged with a ⚠.
CLI reference
examen serve <artifact> [--port N] [--host ADDR] [--history-db PATH] [--no-browser]
Open the quiz UI in a browser for a given artifact. Default port 8780;
picks the next free port if taken. --no-browser is useful for SSH/headless.
--host defaults to 127.0.0.1 (local-only); pass 0.0.0.0 to expose on all
interfaces, e.g. inside a container.
examen validate <artifact>
Validate an artifact against the schema without serving it — schema
violations, unsupported schema_version, and topic cross-reference errors.
Exits non-zero on any problem. Useful for catching bad generator output early.
examen history [--repo PATH]
List past sessions across all repos (5 most recent per repo), or filtered to
one repo's full history with --repo /path/to/repo.
examen trend --repo PATH [--topic LABEL]
ASCII bar chart of overall score over time for one repo; pass --topic to
plot just that topic's trend ("am I getting better at auth?").
examen revise --repo PATH [--limit N] [--questions N]
Surface a repo's weakest topics (worst score first, with calibration flags) and print a ready-to-paste Claude Code command that regenerates a quiz focused on those topics' files. Closes the loop: quiz → see weak spots → re-quiz them. The runner never calls an LLM; generation stays in the skill.
examen prune --older-than 30d|6w|6mo|1y [--yes]
Delete completed sessions older than the given duration. Cascades through
answers and topic-snapshots. Confirms unless --yes.
examen skill install [--dest PATH]
Copy the Claude Code skill bundle to ~/.claude/skills/examen (or --dest).
Asks before overwriting an existing install.
examen generate
Stub. Quiz generation lives in the Claude Code skill, not the CLI. Prints a pointer and exits with code 2.
examen --version
examen --help
File locations
| Path | What |
|---|---|
~/.examen/history.db |
Longitudinal SQLite store: every session, answer, topic snapshot. ~5 KB per session. |
<repo>/.examen/quizzes/quiz-<uuid>.json |
Quiz artifacts the skill writes. Gitignored. |
<repo>/.code-review-graph/ |
code-review-graph index. Gitignored (it's regeneratable). |
~/.claude/skills/examen/ |
Where examen skill install drops the skill bundle. |
Nothing leaves your machine. The runner only binds 127.0.0.1.
Known limitations (v0.2.0)
Be honest with yourself before relying on this:
- No staleness detection. The runner serves the artifact as-is and never
checks whether your code has changed since the quiz was generated. (The
v0.1.0 codegraph-backed drift check was removed in v0.2.0 — it flagged
every blast-radius question as drifted; see
docs/codegraph-schema-notes.md.) Regenerate the quiz after meaningful changes. predict_outputquestion type is not implemented. The schema rejects it until a safe execution sandbox exists.- Single-user, single-machine. No multi-user, no team mode, no shared-history. The history db is local.
- No git rename detection. If you rename
auth/toauthentication/, every topic in that area gets a new ID and trend continuity breaks. - Cheat-resistant only by social contract. Copying a question into another LLM defeats the tool. It's a self-honesty instrument, not an adversarial assessment.
- Session state is in-memory. A runner restart mid-session loses the in-flight question (DB has the answers committed so far, but the adaptive state machine resets). Resume support is partial.
Development
git clone <this-repo> examen && cd examen
python3.11 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
# Run the test suite (146 tests)
pytest
# Type checking (ty — checks src + tests)
ty check
# Lint
ruff check .
# Sync skill schema copy + example fixture (after editing source schema)
make sync-schema
make sync-example
Project layout:
src/examen/
├── adaptive/ # scoring + state machine
├── artifact/ # JSON schema, loader, typed artifact structures
├── store/ # SQLite store (migrations, repos, sessions, topics)
├── static/ # vendored htmx + CSS
├── templates/ # Jinja2 templates for the 4 screens
├── cli.py # Click CLI
├── codeview.py # code-context loading + syntax highlighting
├── sessions.py # in-memory live-session registry
├── viewmodel.py # results/heatmap view models
└── serve.py # FastAPI app
skill/examen/ # Claude Code skill bundle (markdown prompts)
tests/ # unit, integration, smoke tests
docs/ # design spec, implementation plan, codegraph notes
Conventions: every Python file starts with a # path/to/file.py comment;
ty type checking; Google Python Style; no Claude attribution in commits.
Troubleshooting
"Module 'examen.cli' has no attribute 'main'" after pip install -e .
Re-run pip install -e ".[dev]" after switching branches — entry points are
re-registered at install time.
examen --version says command not found
The console script is registered via pyproject.toml's [project.scripts].
Make sure the venv is activated, or call directly: .venv/bin/examen --version.
/examen:generate-* doesn't appear in Claude Code
Verify the skill is installed: ls ~/.claude/skills/examen/SKILL.md. If
missing, run examen skill install. Restart Claude Code to pick up new
skills.
The skill complains that code-review-graph isn't built
Run code-review-graph build from your repo root. Indexing 1000+ files
takes a couple of minutes the first time.
Quiz artifact fails to load: "Artifact fails schema validation" The skill version that wrote the artifact may be out of sync with the runner version. Regenerate the quiz with the current skill, or downgrade the runner to match.
"Port 8780 taken"
Examen picks the next free port and prints the new URL on stderr. Or pass
--port N explicitly.
The browser doesn't open automatically
Use --no-browser and copy the URL yourself. Examen prints it before
launching uvicorn so you can see it regardless.
Design docs
The full design is in docs/superpowers/specs/2026-05-17-examen-design.md
(~800 lines). The phased implementation plan is in
docs/superpowers/plans/2026-05-17-examen-implementation.md (~5000 lines).
Historical codegraph integration notes (integration removed in v0.2.0):
docs/codegraph-schema-notes.md.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file examen-0.3.0.tar.gz.
File metadata
- Download URL: examen-0.3.0.tar.gz
- Upload date:
- Size: 251.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
87947863f88e45ec42ef8d7c312a94a705f68bb2ac5223c06e9797b9567386df
|
|
| MD5 |
065c199a0e38e020f636844b4880cb64
|
|
| BLAKE2b-256 |
6495b39f2a3e2a199c54d95cb82d262ce9d34ea1613286f8e5d6e1eccca60ffd
|
Provenance
The following attestation bundles were made for examen-0.3.0.tar.gz:
Publisher:
publish.yml on abhiksark/examen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
examen-0.3.0.tar.gz -
Subject digest:
87947863f88e45ec42ef8d7c312a94a705f68bb2ac5223c06e9797b9567386df - Sigstore transparency entry: 2039147027
- Sigstore integration time:
-
Permalink:
abhiksark/examen@f6c10ce1a4719a1244ee8f1d25241d7c346bd4d4 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/abhiksark
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f6c10ce1a4719a1244ee8f1d25241d7c346bd4d4 -
Trigger Event:
release
-
Statement type:
File details
Details for the file examen-0.3.0-py3-none-any.whl.
File metadata
- Download URL: examen-0.3.0-py3-none-any.whl
- Upload date:
- Size: 86.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5b106bbfebdd7ea1c16dd9e3b248f3a67a5a2fb407db1b970bcf37271ca877f
|
|
| MD5 |
07a1a31eab5a1bda7c2183cb7f60317e
|
|
| BLAKE2b-256 |
f310de67f55725ac21385b7a03d22d158ac0d5cc41b07c0f770087f32a95e865
|
Provenance
The following attestation bundles were made for examen-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on abhiksark/examen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
examen-0.3.0-py3-none-any.whl -
Subject digest:
f5b106bbfebdd7ea1c16dd9e3b248f3a67a5a2fb407db1b970bcf37271ca877f - Sigstore transparency entry: 2039147192
- Sigstore integration time:
-
Permalink:
abhiksark/examen@f6c10ce1a4719a1244ee8f1d25241d7c346bd4d4 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/abhiksark
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f6c10ce1a4719a1244ee8f1d25241d7c346bd4d4 -
Trigger Event:
release
-
Statement type: