Open-source practice-app engine
The mill that grinds questions into knowledge.
Bring a folder of JSON — a learning pack — and quizmill turns it into a fast, installable, offline-first practice app. Better still: don't write the questions at all. Your AI agent does.
« three commands to your own practice app »
npx quizmill new my-topic
npx quizmill run my-topic
npx quizmill build my-topic
↑ This isn't a video. Get one wrong — watch it come back.
One engine, endless apps
Every pack becomes its own app.
Title, icon, theme, categories — all generated from the pack manifest at build time. Pick a pack, then actually use the app: practise, unlock a sticker, check your progress. It's all in there.
Not just flashcards
A practice app that keeps you coming back.
Stickers to chase, progress to watch climb, and explanations that actually teach — every pack ships the lot. Here's a real look.
★ Sticker cabinet
Streaks, volume, daily habit, per-category mastery.
▥ Progress
🔥 6-day streakAccuracy by category, and the trend over time.
Last 7 days — weak spots re-queue for review.
✎ Explanations that teach
Markdown, with real source links and attribution.
Endpoints are computed from the Service selector — no matching Pod labels means an empty endpoint set. DNS still resolves; it just points at nothing. Kubernetes docs: Service ↗
The authoring story
You don't write packs. Your agent does.
quizmill ships a create-learning-pack skill. Open the repo in
Claude Code (or any agent that reads project skills), say what you want
to learn, and the agent scopes it, writes the bank, and loops on the
validator until the pack is clean. Malformed content can't reach the app.
Grist for the mill
A pack is three JSON files. That's the whole format.
{
"schemaVersion": 1,
"id": "k8s-networking",
"title": "Kubernetes Networking",
"description": "Services, Ingress, DNS and NetworkPolicy, drilled.",
"homeSubtitle": "Packets find a way.",
"themeColor": "#7c3aed",
"categories": [
{ "key": "services", "label": "Services & kube-proxy", "weight": 0.4 },
{ "key": "ingress", "label": "Ingress & Gateway", "weight": 0.3 },
{ "key": "netpol", "label": "NetworkPolicy", "weight": 0.3 }
]
}
[
{
"id": "k8s-networking-services-001",
"categoryKey": "services",
"difficulty": 3,
"prompt": "A ClusterIP Service routes to zero endpoints. What's the most likely cause?",
"options": [
{ "key": "A", "text": "kube-proxy is not running on the control plane" },
{ "key": "B", "text": "The Service selector doesn't match any Pod labels" },
{ "key": "C", "text": "CoreDNS has no record for the Service" },
{ "key": "D", "text": "The Pods lack a readinessProbe" }
],
"correctKey": "B",
"explanation": "Endpoints are computed from the selector; no matching labels → empty endpoint set. DNS still resolves (C) — it just points at nothing.",
"source": "generated",
"reviewStatus": "reviewed"
}
]
[
{
"id": "checkout-outage",
"title": "The checkout outage",
"stem": "It's 09:02 on Black Friday. Checkout pods are healthy but the Service returns connection refused for ~20% of requests..."
}
]
Schema v2 adds the rest — 2–6 options
(true/false through multi-choice), images on questions
and answers (non-verbal reasoning included), an optional
concepts.json of teaching cards shown after a wrong answer,
and a pack-defined level dimension to filter practice by.
v1 packs keep working unchanged.
The Zod schema + validator CLI (npx quizmill validate) cross-checks
ids, category references, scenario references and weights — and exits
non-zero with per-question errors. That's what agents loop on.
The full breadth
Everything ships in every pack.
⟳ Mistakes that come back
Wrong answers queue up and are re-asked until you rescue them. The loop is the product.
◌ Unseen-biased practice
Weighted random selection that favours questions you haven't met, tunable per category blueprint.
🖼 Images & any question shape
Questions and answers can carry images — non-verbal reasoning, diagrams — and options run 2–6, from true/false to multi-choice.
📖 Concept cards & levels
Short teaching cards surface after a wrong answer, and pack-defined level bands (Year, grade, CEFR…) let you filter practice.
▦ Offline-first PWA
Installable to the home screen with a versioned service worker. Aeroplane-mode proof.
★ Sticker cabinet
Streaks, volume, daily habit, and per-category mastery — every pack grows its own cabinet of earned stickers.
▥ Progress charts
Accuracy by category and over time, plus a weakest-questions list that links straight back into practice.
✎ Per-question feedback
Thumbs up/down with comments, surfaced in Settings so pack authors can curate the bank.
↗ Source attribution
Questions carry a sourceRef; the answer panel links to the original author's work.
⇅ Optional cloud sync
Magic-link auth + Supabase mirroring with a retry queue. Dormant unless you configure it.
⌂ Private by default
Packs are gitignored and never enter the engine's history. Private GitHub repos install fine.
▲ Deploy anywhere
npx quizmill build emits a plain static site — Cloudflare Pages, GitHub Pages, Netlify, a USB stick.
Take it home
Install it your way.
Scaffold a pack
npx quizmill new my-topic
npx quizmill run my-topic
No clone, no install — npx fetches the engine for you. Edit the JSON (or hand it to your agent), then run.
Install a published pack
npx quizmill list
npx quizmill run quizmill/pack-claude-cert
Any GitHub repo with a valid pack works — private ones too, through your own git clone access.
Publish your own
# pack.json + questions.json in a repo
npx quizmill validate my-topic
git push # → installable by anyone
PR it into the registry and it shows up in quizmill list.