From personal
Use when the user invokes /anki-connect, asks to create or look up Anki flashcards, copy cards between decks, or notices something worth remembering long-term (a concept, gotcha, or vocab from a lesson).
How this skill is triggered — by the user, by Claude, or both
Slash command
/personal:anki-connectThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Look up and create Anki notes via the [Anki-Connect](https://git.sr.ht/~foosoft/anki-connect) add-on (HTTP API on `http://127.0.0.1:8765`, API version 6, Anki 2.1.x).
Look up and create Anki notes via the Anki-Connect add-on (HTTP API on http://127.0.0.1:8765, API version 6, Anki 2.1.x).
Two batch-friendly scripts under ./scripts/:
| Script | Purpose |
|---|---|
find_cards.py | Look up many words at once — word-boundary match (default) or --exact, deck-aware, target-deck classification |
add_card.py | Always batch — JSONL on stdin, supports clone_from to copy cards between decks |
update_card.py | Partial field updates — JSONL {"id": <nid>, "fields": {...}}, leaves other fields/tags alone |
anki_connect.py | Underlying HTTP client. Import for ad-hoc Python (anki_request("<action>", **params)). Full API: https://git.sr.ht/~foosoft/anki-connect |
# Word-boundary match in the Word field (default). `Mittag` finds `der Mittag`,
# `am Mittag`, but NOT `Mittagspause`. Unicode-aware (umlauts, Cyrillic).
python ./scripts/find_cards.py Mittag Sommer Klavier auf neben
# Whole-field equality when you need strict matching
python ./scripts/find_cards.py Mittag Sommer --exact
# Multi-field search (any-match across all listed fields)
python ./scripts/find_cards.py лето стол -F Translation -F Word
# Lesson dedupe: classify each word as in-target / elsewhere / missing
python ./scripts/find_cards.py auf neben Mittag Sommer Klavier \
--target-deck "German::0. My German Vocab"
# Pipe a file in (auto-reads stdin when no positional words)
cat lesson_words.txt | python ./scripts/find_cards.py
# Full details for a single word
python ./scripts/find_cards.py Klavier --detail
Summary legend: = in --target-deck, + found elsewhere, ✗ missing.
Always reads JSONL on stdin (one note per line). Per-line schema:
{"fields": {"<FieldName>": "<value>", ...}, "tags": [...], "deck": "...", "model": "...", "clone_from": <nid>}
CLI provides defaults; per-line keys override or extend. Either fields or clone_from is required per line.
# Plain new notes
echo '{"fields":{"Word":"der Tisch","Translation":"стол"}}
{"fields":{"Word":"die Wohnung","Translation":"квартира"}}' | \
python ./scripts/add_card.py -d "German::0. My German Vocab" -m "My German" \
-t lesson-2026-04-29
# Clone existing notes into another deck (Sound, Image and tags inherited from source)
echo '{"clone_from": 1442860633303}
{"clone_from": 1442860633583}' | \
python ./scripts/add_card.py -d "German::0. My German Vocab" \
-t lesson-2026-04-29 -t copied-from-db1 \
--allow-duplicate
# Dry-run first to preview
... | python ./scripts/add_card.py -d ... -m ... -t ... --dry-run
--allow-duplicate is required when cloning. Anki's first-field duplicate check is global; the duplicate is intentional (you want the same content in another deck).
Patch only specific fields on existing notes. Other fields and all tags are preserved.
# Bulk-fix translations on a few cards
cat <<'EOF' | python ./scripts/update_card.py
{"id": 1442860633303, "fields": {"Translation": "полдень (~12:00)"}}
{"id": 1442860633583, "fields": {"Translation": "лето"}}
EOF
# Set Sound on a single note
echo '{"id": 1777466685362, "fields": {"Sound": "[sound:foo.mp3]"}}' | \
python ./scripts/update_card.py
find_cards.py <words> --target-deck "<lesson deck>". Three buckets fall out:
= already in target deck → no action+ exists elsewhere (e.g. German::Deutsch DB1) → clone into the lesson deck✗ missing → create newclone_from lines for the + bucket and fields lines for the ✗ bucket.add_card.py with -t lesson-<date> and --allow-duplicate.Ctrl+Shift+T to fill the Sound field.Same script — JSONL with one line:
echo '{"fields":{"Word":"der Stab","Translation":"палка"}}' | \
python ./scripts/add_card.py -d "German::0. My German Vocab" -m "My German" -t lesson-2026-04-29
python -c "from anki_connect import anki_request; print(anki_request('deckNames'))"
python -c "from anki_connect import anki_request; print(anki_request('modelFieldNames', modelName='My German'))"
The user has 226 leeches. Quality over quantity.
lesson-YYYY-MM-DD.der Sommer, die Wohnung) — gender is part of the word.find_cards.py --target-deck <deck>.add_card.py (with --dry-run first if the batch is large).The connection requires Anki desktop running with the Anki-Connect add-on installed (AnkiWeb code 2055492159). Verify reachability — http://127.0.0.1:8765 should display Anki-Connect.
On macOS, disable App Nap so Anki keeps responding when in the background:
defaults write net.ankiweb.dtop NSAppSleepDisabled -bool true
defaults write net.ichi2.anki NSAppSleepDisabled -bool true
defaults write org.qt-project.Qt.QtWebEngineCore NSAppSleepDisabled -bool true
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub k4black/dotfiles --plugin personal