A stdlib-only launcher that starts Claude Code
or opencode inside an exam-prep folder, so that folder's CLAUDE.md drives the study loop:
open your reference sheets and the newest problem set, then feed you one
bite-sized exercise at a time, personalised from your actual mistakes. The
launcher encodes no study procedure: about 2000 lines that pick a folder, pick a
model and send a "let's study" trigger, so you improve the loop by editing a
workspace's CLAUDE.md, not this code. Tested on Linux with KDE.
One tool, two commands. studyclaude runs the session in Claude Code, openstudy in
opencode (main.py --engine claude|opencode). The Lern-Loop, menu and courses are the
same; only the launch and these settings differ per engine: model, effort and working
medium. The courses, the default course, the exam file and the Quickie streak are
shared. openstudy used to be its own repo (now archived); studyclaude was formerly
lernclaude (renamed 2026-10-18).
Differences when the engine is opencode: no --append-system-prompt, so the launcher
writes the prompt to $XDG_RUNTIME_DIR/study/prompt-<hash>.md and passes it, with the
course's CLAUDE.md, as instructions; the Tutor Board and Google Calendar are not
connected (Claude Code gets them as claude.ai connectors, opencode needs its own MCP
entry), so use the Xournal++ medium there; subagents run on the session's model.
The tool speaks German — prompts, workspace template, menu. The loop itself is language-agnostic: docs/workspace.md.
- Claude Code (for
studyclaude) and/or opencode (foropenstudy) on yourPATH - Python 3.9+, standard library only (
requirements.txtis empty) konsole(KDE) for the windowed launch; without it, it runs in your terminal
git clone https://github.com/AutomatedAlchemy/studyclaude.git
cd studyclaude
python3 main.py # menu; first entry is "neuen Kurs anlegen"
python3 main.py --install # desktop icons + the `studyclaude` and `openstudy` aliases
python3 main.py --engine opencode # run with opencode instead of Claude Codeneuen Kurs anlegen starts a Claude session that searches your filesystem with
you, agrees on a course folder, scaffolds and registers it, then reads your
material and fills in exam date, topic map and starting points. --install needs
cli-tools-kit; without it it
says what is missing, and the tool still runs by path.
A bare studyclaude opens a curses picker of your registered courses, exam banner
above, progress note per row. The top row is the Begleiter, the default persona.
↑/↓ move · Enter starts · Space marks courses for the
Gärtner · g Gärtner · m medium · o model · e effort · d
default · x removes the course from the menu, and on request moves its folder
into _archiv/ beside it · q quits. Untouched for ten seconds it autostarts your default.
| Launch | What it does |
|---|---|
studyclaude <workspace> |
Run that course's Lern-Loop; --menu forces the menu instead |
studyclaude --begleiter |
Begleiter (the default): one session over all courses checks the timetable for the current time and helps with what is due — a lecture running now, starting soon or just ended, a deadline, or a Häppchen in free time |
studyclaude --quickie |
One short, winnable Häppchen (about 5 min); counts a daily streak |
studyclaude --gaertner [PFAD…] |
Gärtner: a maintenance session, no studying. You tell it in chat which courses to clean up, merge or revise; the paths are its focus |
Mechanics of the three modes, the streak and the dossiers: docs/modes.md.
The Begleiter reads who you are and where your timetable lives from a tab
„Profil“ on one of your Tutor Boards (any board, found by the tab name); without it, it asks you in chat
(docs/modes.md § Begleiter).
To steer which course the Begleiter and the Quickie pick on which weekday, put a
plain-text rule into data/wochenplan.md (docs/modes.md § Wochenplan).
Other flags: --list (* marks the default), --set-default, --set-medium,
--set-model, --set-effort, --add (guided onboarding), --register,
--unregister, --print-prompt, --dry-run, --install [ws], --remove [ws].
Starting on login: no studyclaude flag. Tick Auto-Start in the
cli-tools-kit installer (pre-ticked via default_autostart); it symlinks the
icon into ~/.config/autostart and clears it on removal. What starts is the menu
with its countdown, so you can always get out.
Sessions work either in Xournal++ + Firefox (the default: exercise PDF in the
browser, calculations on a separate .xopp sheet) or on a Tutor Board (a web
whiteboard, one tab per exercise, needs an account on the author's invite-only
service). Switch with m, studyclaude --set-medium xournalpp|board or
STUDY_MEDIUM; verbal mid-session switch and per-course pin in
docs/workspace.md. How many sketches and
clips a course gets is one Eckdaten row in its CLAUDE.md, see
docs/workspace.md.
Tutor Board lives at https://beta.probable.work. An agent connects as an
MCP (Model Context Protocol) connector, https://beta.probable.work/mcp, with
OAuth 2.1 + PKCE (Proof Key for Code Exchange); live docs at
https://beta.probable.work/setup.md and https://beta.probable.work/agent.md.
studyclaude ships no MCP client and no credentials — those come from your own
Claude Code MCP config.
For exams that allow a self-written sheet, the session builds a page that shows
the sheet next to every question of the catalogue and highlights the text that
answers the selected question. See
docs/workspace.md.
Set STUDY_EXAMS (or the registry key "exams_file") to a markdown file of
exam dates and the menu lists the upcoming ones above the courses: soonest first,
days left, red inside three days, yellow inside ten. Unset, no banner. It reads
every table with both a date column (Termin, Datum, Date, When) and a
label column (Fach, Kurs, Modul, Prüfung, Klausur, Subject, Course,
Exam), so existing notes work as-is:
| Prüf-Nr | Fach | ECTS | Termin |
|---------|-----------------------|-----:|---------------------|
| 12345 | Lineare Algebra I | 2,5 | **Mi 16.09. 09:00** |
| 23456 | Thermodynamik | 5 | **Fr 25.09.** |The time is optional and a bare 12.01. means the next 12 January. Rows past,
struck through (~~…~~) or marked abgelegt / bestanden / Rücktritt /
entfällt / verschoben / TBD drop out.
Put a file luecken_eingang.md into one registered course and that course
collects maths gaps from all others: every session's system prompt then tells it
to append a line there when a review finds a pure calculation error (powers,
units, fractions, signs …) rather than a subject error. The file's header states
the line format; the course's own CLAUDE.md says how it works through the
inbox. No file, no rule.
All optional.
Every variable is read as STUDY_<NAME>; the old STUDYCLAUDE_<NAME> and
OPENSTUDY_<NAME> still work as fallbacks for one release (STUDY_* wins).
| Variable | Effect |
|---|---|
STUDY_ENGINE |
Default engine when --engine is not given (claude | opencode) |
STUDY_REGISTRY |
Path to the registry (default: ./data/registry.json) |
STUDY_DEFAULT_WORKSPACE |
Override the registered default for one launch |
STUDY_ROOT |
The study root on this host: the course list lives in <root>/.study/ and onboarding starts searching there (else host.json, else $HOME for onboarding only) |
STUDY_CONFIG_DIR |
Where host.json lives (default ~/.config/study) |
STUDY_EXAMS |
Markdown file with your exam-date table (banner off when unset; registry key exams_file does the same) |
STUDY_MEDIUM |
Override the working medium for one launch (xournalpp | board) |
Where things live. The course list is <study root>/.study/courses.json, next to your
courses (versioned, one entry per course with a stable id, paths relative to the root).
The study root is per host: STUDY_ROOT, else study --set-root DIR (stored in
~/.config/study/host.json, %APPDATA%\study\host.json on Windows). Put the root in a synced
folder and the list is the same on every machine. Everything else (default course, model,
effort, medium, exam file, Quickie streak) is in the gitignored data/registry.json; model,
effort and medium are stored per engine under engines.<engine>, and the default is a course
id.
Courses are added and removed as operations under a lock, so two menus open at once do not
undo each other. A Syncthing conflict copy of the list (courses.sync-conflict-*.json) is
shown in the menu header and by --list; it is not merged. If the list cannot be read, the
menu opens empty, course changes are refused and nothing is overwritten.
Syncing the list. The atomic write leaves a short-lived .study/courses.json.<pid>.tmp
beside the list, so the sync tool should ignore it on every host (the lock file is per host and
lives in the config dir, not beside the list). For Syncthing, add to the folder's .stignore
(per host, not synced): (?d)**/.study/*.tmp. A registry on another host finds the list through
its root_hint; without one (a fresh host) the menu header says to run study --set-root.
The first start migrates the older layouts once: a registry with top-level
model/effort/medium (studyclaude), the old openstudy registry,
~/.config/study-courses.json, and the course list in the registry. The root is guessed from
your courses (their common folder) and printed. The registry is copied to
registry.json.pre-courses first. After that, a course you remove stays removed.
Sessions launch on Opus at medium effort. Medium is deliberate: the loop is
interactive tutoring, where latency is felt more than reasoning depth helps. o
(cycle) or O (interactive menu) and e in the menu (or --set-model /
--set-effort) change model (Opus | Sonnet | Fable) and effort (low | medium |
high | xhigh | max); the pick is used as chosen and persists in the registry.
Subagents a session starts (explainer clips, the Blatt-Finder mapping) run on Sonnet, or Haiku for simple work, never on Opus.
A workspace itself is a normal folder with your material plus a CLAUDE.md.
Onboarding stamps a minimal skeleton (CLAUDE.md, todo.md, fehlermuster.md,
Personalisierte_Übungen/) and never overwrites an existing file. Layout, the
overview contract and adding a course: docs/workspace.md.
python3 -m pytest -q # offline: no network, no launchMIT — see LICENSE.