writing-anki-cards
Generates high-quality Anki flashcards from source material and adds them directly to Anki via AnkiConnect. Produces atomic cloze and Q/A cards, skips duplicates safely, and returns a succinct run report. Use when the user asks to create Anki cards, flashcards, or spaced-repetition prompts from notes, articles, docs, or text.
Writing Anki Cards (Add-Only)
This skill is add-only: generate cards and insert notes into Anki. Do not run review/update/delete workflows.
SKILL_DIR="$(dirname "$SKILL_PATH")"
PIPELINE="$SKILL_DIR/scripts/anki_add_pipeline.py"
REFS="$SKILL_DIR/references"
Quick start
- Ensure Anki is running with AnkiConnect.
- Save source to
/tmp/source.txtand run the source gate. - Read the card formulation principles.
- For each chunk: generate → evaluate → fix.
- Merge, preflight, add notes, return report.
Prerequisites
- Anki + AnkiConnect (2055492159)
python3,jqpyyaml:python3 -m venv .venv && source .venv/bin/activate && pip install pyyamlpiCLI (for the per-chunk evaluator)
Canonical workflow
1. Save source material to /tmp/source.txt.
2. Run source gate — always, regardless of source size. Produces a chunk plan and source metrics YAML before any cards are written. See Source gate below.
3. Read card formulation principles using the read tool — mandatory before writing any cards:
- Card formulation principles
- Advanced techniques
read "$REFS/01-card-formulation-principles.md"read "$REFS/02-advanced-techniques.md"
4. For each chunk — generate → evaluate → fix:
a. Generate cards from that chunk's source sections → write /tmp/anki-chunk-N.yaml
b. Run the per-chunk evaluator → read free-form output.
See Per-chunk evaluator below.
c. Rewrite /tmp/anki-chunk-N.yaml completely based on evaluation output.
Do not make surgical edits — the evaluator may identify structural issues
(missing coverage, ratio drift, systematic patterns) that require adding or
removing cards, not just editing existing ones.
Process each chunk to completion before starting the next. Do not stop mid-source unless the user explicitly approves an early exit.
5. Merge all reviewed chunks:
cat /tmp/anki-chunk-*.yaml > /tmp/anki-notes.yaml
6. Run preflight:
python3 "$PIPELINE" preflight --deck "<deck>"
7. Run add pipeline:
python3 "$PIPELINE" add-notes \
--deck "<deck>" \
--notes-file "/tmp/anki-notes.yaml" \
--source-identity "<source-url-or-path-or-title>" \
--source-text-file "/tmp/source.txt"
8. Return succinct report from script output.
Source gate (step 2)
Always run — for sources ≤2,500 words, produce a single-chunk plan.
- Count total words in
/tmp/source.txt. - List section headings with approximate word counts.
- Group sections into ~2,500-word semantic chunks. If total ≤2,500 words, one chunk covers the full source.
- Compute per-chunk card targets (5–10 cards per 1,000 words).
- Write
/tmp/anki-source-metrics-<hash>.yaml:
source_hash: "6f0fa16a8c25"
total_words: 8303
sections:
- heading: "§1. Introduction"
words: 225
- heading: "§2. Manifesto"
words: 300
- Write
/tmp/anki-chunk-plan-<hash>.yaml:
source_hash: "6f0fa16a8c25"
total_words: 8303
card_target_min: 41
card_target_max: 83
chunks:
- id: 1
sections: "§1–§5"
approx_words: 2100
card_min: 10
card_max: 21
status: pending
The hash is the first 12 characters of sha256(normalized_source_identity + "\n" + normalized_source_text) — the same value the pipeline script computes internally.
Per-chunk evaluator (step 4b)
After writing /tmp/anki-chunk-N.yaml, build and run the evaluator.
Template: Evaluator prompt
PROMPT=$(python3 -c "
import sys
t = open(sys.argv[1]).read()
c = open(sys.argv[2]).read()
print(t.replace('{{CHUNK_YAML}}', c))
" "$REFS/03-evaluator-prompt.md" "/tmp/anki-chunk-N.yaml")
pi --mode json --no-session --no-skills --no-extensions \
--no-tools --no-context-files \
--model opencode-go/glm-5.1 "$PROMPT" 2>/dev/null \
| jq -rj '..|.delta? // empty'
The evaluator returns three sections: a card-by-card verdict, a chunk-level synthesis (coverage gaps, ratio, systematic patterns, interference), and a concrete action list. Read all three before rewriting the chunk. Evaluate the feedback critically — the evaluator can be wrong, over-strict, or miss domain context. Accept findings that improve clarity and retrieval; push back on those that would make cards worse.
Notes YAML contract
Notes files are YAML lists. Always double-quote all field values — this single rule
prevents all YAML parse errors (colons in values, boolean-like words, and {{...}} syntax
are all safe inside double quotes).
# Section comments are native in YAML — use them freely
# === Chunk 1: §1–§5 Foundations (2,100 words) | target: 10–21 cards ===
- modelName: Cloze
fields:
Text: "Warmth is judged before {{c1::competence}}."
Back Extra: ""
tags: [warmth-competence]
- modelName: Basic
fields:
Front: "Why lead with warmth?"
Back: "Competence-first signaling reads as cold before trust is established."
tags: [warmth-competence]
Rules:
modelNamemust beClozeorBasic.Clozefields:TextandBack Extra.Basicfields:FrontandBack.tags: optional flow sequence[tag1, tag2].
Source-independent wording (required)
Cards must be comprehensible in isolation. Do not reference the source artifact.
❌ "this guide", "the guide", "this article", "the author says", "in the text/document", or any proper name unique to a worked example in the source (system names, fictional entities, organisation names used only as examples).
✅ Rewrite to domain wording: "In threat modeling…", "For REST APIs…", "When designing…"
What the script guarantees
- AnkiConnect/version preflight
- Model/field preflight (
Basic:Front,Back;Cloze:Text,Back Extra) - Deck preflight + auto-create if missing
- Deterministic source hash (
sha256(normalized_identity + "\n" + normalized_text)) - Stable source tag + batch tag; tag sanitization
- YAML checklist at
/tmp/anki-add-run-<hash>-<deck>.yaml - Resume semantics (
pending|in_progress|done|failed) - Duplicate preflight (
canAddNotesWithErrorDetail) - Batched insertion (default execution chunk size: 25)
- Newline normalization (
\n→<br>in all fields) - Soft warnings:
Front/Text> 220 chars;Back/Back Extra> 600 chars
Density and card-type calibration (default targets)
- Cards per 1,000 words: 5–10
- Cloze:Basic ratio: 2:1 to 3:1
Calibration checks:
- Below density → under-extraction; above → over-splitting or low-value cards.
- Outside ratio range → rebalance unless source structure strongly justifies it.
- Atomicity and single-answer retrieval take priority over hitting numeric targets.
Resume commands
python3 "$PIPELINE" resume-status --checklist "/tmp/anki-add-run-<hash>-<deck>.yaml"
Re-run the same add-notes command to resume unfinished execution chunks.
Duplicate policy
Skip non-addable notes and continue. Do not fail the run for duplicates.
Fallback (manual API reference only)
If script use is impossible: version, modelNames, modelFieldNames, deckNames,
createDeck, canAddNotesWithErrorDetail, addNotes.
Final response format
- Deck:
<deck> - Total words (accurate):
<n> - Section word-count min/max:
<min>/<max> - Source metrics file:
/tmp/anki-source-metrics-<source-hash>.yaml - Chunk plan file:
/tmp/anki-chunk-plan-<source-hash>.yaml - Chunks planned/completed:
<n>/<n> - Document card target min/max:
<min>/<max> - Generated:
<n> - Attempted:
<n> - Added:
<n> - Skipped (duplicates/non-addable):
<n> - Failed:
<n> - Warnings:
<n> - Source tag:
<source::...> - Batch tag:
<batch::...> - Checklist file:
/tmp/anki-add-run-<hash>-<deck>.yaml