Skip to content

da2gl/language-learning-mate

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Language Learning Mate (LLM)

Turns raw language-lesson notes into a cross-linked Obsidian wiki and Anki cards from a single prompt in Claude Code. The cards come with pronunciation audio, pictures, example sentences, and cloze deletions.

License: Apache 2.0 Built with Claude Code

Drop a lesson note into wiki/raw/ and tell Claude Code to process it:

/process-raw-notes wiki/raw/2026-07-05-lesson.md

The pipeline extracts the words, phrases and sentences worth learning, writes a wiki page for each, generates the media, and files the Anki cards. It ships tuned for learning Spanish from English; adapting it to another pair is a documented checklist.

The architecture is inspired by Andrej Karpathy's LLM Wiki note: an LLM-maintained knowledge base over immutable raw sources. This project applies that idea to language learning.

Meet Language Learning Mate

Why

The typical language-learning loop looks like this: take a lesson, leave raw notes (vocab, phrases, grammar), intend to turn them into Anki cards and maintain a personal dictionary, never actually do it. The problem is not the learning. It's the busywork: picking an image per word, recording pronunciation, writing example sentences, building cloze deletions, filing everything consistently, keeping cross-references between lessons. Humans abandon the loop; LLMs don't.

This project takes over that part. The user adds a new raw note and asks to process it. The orchestrator parses it, updates the wiki, produces media, writes cards, and tracks what was done in a log. The user reviews the result in Obsidian (for the wiki) and Anki (for the cards). The cards they see tomorrow in Anki are the ones they added to wiki/raw/ yesterday, without the user writing either wiki pages or cards by hand.

Prerequisites

  • Claude Code with workflow support (tested on 2.1.195) — the ingest runs inside it as a background workflow.
  • Python 3 — standard library only, nothing to install.
  • Anki with the AnkiConnect add-on.
  • An ElevenLabs API key for pronunciation audio (paid; the free tier covers a few small lessons).
  • A Stability AI API key for card images (paid).
  • Obsidian — optional, for browsing the wiki and its graph; any markdown viewer works.

Quickstart

  1. Fork or clone the repo.

  2. Copy .env.example to .env and fill in the ElevenLabs and Stability keys.

  3. Set up Anki — the deck, the two note types, AnkiConnect — step by step in docs/anki-setup.md. Keep Anki running during ingests. Phone sync is docs/ankiweb-sync-setup.md.

  4. Optional: open wiki/ as an Obsidian vault; docs/obsidian-front-matter-title-setup.md makes the graph show words instead of numeric IDs.

  5. Open the repo in Claude Code.

  6. Drop a lesson note into wiki/raw/ — one item per line, term - translation. TEMPLATE.md is the recommended shape; deviations are fine, the extractor judges by meaning.

  7. Ask for the ingest:

    /process-raw-notes wiki/raw/<your-note>.md
    

    Watch live progress with /workflows. The finished run reports what was created, what was skipped as a duplicate, and what was parked in wiki/unresolved.md with a reason.

  8. Review the new pages in Obsidian and the new cards in Anki.

How it works

One run takes a raw note end to end: it extracts candidate items, normalizes each to canonical dictionary form, deduplicates against the existing wiki and inside the file, reserves stable IDs, builds every entity (audio and image over the ElevenLabs and Stability REST APIs, then the wiki page, then the Anki notes), gates each on a completeness check, and updates the index and the log. Claude Code executes this as a deterministic background workflow (.claude/workflows/ingest.js); LLM agents are called only for language judgement, and every side effect goes through small Python modules. The runtime map lives in .claude/CLAUDE.md.

The system keeps three layers:

wiki/raw/   immutable source notes from lessons and homework
wiki/       LLM-maintained markdown knowledge base
Anki        derived flashcards, synced from the wiki over AnkiConnect

wiki/raw/ holds the user's curated lesson notes, homework conversations, teacher-chat exports. Markdown or plain text, occasionally images or PDFs. Agents read this layer but never modify it. If the user wants to edit a raw note, they edit it themselves; a future re-ingest would re-process it. The folder sits inside wiki/ only so that Obsidian resolves the sources: wikilinks on entity pages to the actual lesson notes; conceptually it remains its own layer, owned by the user alone.

wiki/ is a directory of markdown files, one per entity, created and updated by the LLM as lessons are ingested. The wiki is the compiled artifact: accumulating, interlinked, and the master for all media. Opened in Obsidian, the graph view shows the shape of the user's growing vocabulary.

Anki is the repetition layer. Cards are generated from the wiki through AnkiConnect. Two note types live in one deck: L1→L2 (a native-language prompt with a picture → the target word with audio) and Cloze (a target-language sentence with the studied word blanked out). Card fields, tags, and media all come straight from the wiki. The wiki is the source of truth; Anki is derived.

Entity types

  • Lemma. A single word in canonical dictionary form. Gets a wiki page with grammatical metadata, one pronunciation audio, optionally one image (only if the concept is visualizable), one example sentence, and one cloze. Produces both an L1→L2 card and a Cloze card. Polysemy is modeled as separate wiki pages, one per sense, each with its own ID.

  • Phrase. A fixed multi-word expression: a collocation, an idiom, a greeting. Gets a wiki page, an audio of the whole phrase, optionally an image, and one example sentence. Produces only the L1→L2 card, no cloze, because the phrase itself is the unit being practiced.

  • Sentence. A syntactically complete utterance the user wants to memorize as a whole. Gets a wiki page and one audio of the full sentence. Produces only the L1→L2 card; no image, no cloze.

  • Rule. A grammar pattern, a pronunciation rule, a number template. Gets a wiki page in rules/<kind>/ and nothing else: no media, no Anki cards. Lemma and phrase pages link to rules via related: references.

Special files

A handful of special files inside wiki/ carry the navigational and bookkeeping load.

index.md is the content-oriented catalog: every entity page listed by category with a link and a one-line summary. It is the entry point for querying the wiki. To answer a question, the LLM reads index.md first and drills into the pages it points to. This lightweight navigation works well at moderate scale (hundreds of pages) and avoids the need for embedding-based RAG infrastructure. The ingest workflow updates index.md at the end of every run.

log.md is the chronological, append-only record of what happened and when. Each entry starts with a consistent prefix, ## [YYYY-MM-DD] <op> | <human-anchor>, which keeps the log parseable with plain Unix tools (grep '^## \[' wiki/log.md | tail -5 returns the last five operations). The log gives a timeline of the wiki's evolution and lets the orchestrator detect whether a given raw file has already been ingested.

unresolved.md is the append-only parking lot for raw items the normalizer could not confidently handle: suspected typos, ambiguous words with no translation, lines that violate the one-unit-per-line convention. Each entry records the offending item and a human-readable reason. Nothing is ever silently dropped: an item that cannot become a wiki page lands here instead. Because wiki/raw/ is immutable, the user does not edit source notes to fix these; instead they copy the corrected items into a new raw file, run the ingest on it, and then clear the entries they resolved from unresolved.md. It is the one file inside wiki/ the user edits by hand.

.counter holds the next free entity ID as a plain integer on its first line, followed by a trailing empty line (kept so git diffs stay clean). The ingest's reserve_ids.py reads and parses only the integer on the first line, writes the incremented value back to that same line, and never touches the trailing empty line. The stored value is the bare number; the 5-digit ID is formed at generation time by left-padding it with zeros (4200042).

System layout

.
├── TEMPLATE.md                    recommended structure for raw notes
├── .env.example                   API-key template (copy to .env, which stays untracked)
├── docs/                          setup guides: Anki, AnkiWeb sync, Obsidian, language-pair adaptation
├── .claude/
│   ├── CLAUDE.md                  orchestrator cheatsheet (runtime map)
│   ├── agents/                    LLM agent definitions (language judgement)
│   ├── skills/                    process-raw-notes entry-point skill
│   ├── workflows/                 ingest.js — deterministic ingest orchestrator
│   └── scripts/                   Python modules: media, wiki pages, Anki, verify
└── wiki/                          LLM-maintained knowledge base (Obsidian vault)
    ├── raw/                       immutable source notes (added by the user)
    ├── index.md                   catalog of all pages
    ├── log.md                     chronological ingest log
    ├── unresolved.md              parked items pending a user fix-file
    ├── .counter                   next free entity ID
    ├── lemmas/                    lemma pages (one word, canonical form)
    ├── phrases/                   phrase pages (fixed multi-word units)
    ├── sentences/                 sentence pages (full utterances)
    ├── rules/                     rule pages
    └── assets/{images,audio}/     master copies of media, synced to Anki

Adapting to your language pair

Nothing in the pipeline is tied to Spanish or English, but the pair is baked into the Anki setup, the voice, and the agent prompts. docs/adapting-to-your-languages.md lists every place to change and ends with a prompt that lets Claude do the whole edit for you.

Roadmap

Next up: a grammarian for rule pages, an enricher for thin entries, a mechanic that repairs broken cards. The full list, in rough priority order, is in ROADMAP.md.

Support

Questions and ideas go to Discussions, bugs to issues — both welcome. If the project saves you the busywork, you can buy me a coffee.

About

Turn your lesson notes into a living wiki and ready-to-study flashcards. The busywork is automated, so you can focus on the language itself.

Resources

License

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Python 67.7%
  • JavaScript 32.3%