LLM-driven pedagogical framework for technical self-study. Derived from Karpathy's CLAUDE.md structure (short, imperative, tradeoff-stated), inverted for learning rather than coding assistance.
Two components:
- CLAUDE.md is always-on teaching heuristics (ask before telling, withhold by default, learner produces all artifacts)
- perturbation skill is an on-demand adversarial probing protocol (diagnose → predict → perturb → verify transfer)
Clone once, load into any Claude Code session. The CLAUDE.md and skill are picked up automatically.
git clone https://github.com/<you>/llm-tutor.git ~/llm-tutorThen start Claude Code in your project with:
CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 claude --add-dir ~/llm-tutorTo avoid typing the env var every time, add to your shell profile:
# ~/.bashrc or ~/.zshrc
export CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1Then it's just:
claude --add-dir ~/llm-tutorWhat this does (per Claude Code docs):
- Skills from
--add-dirdirectories are loaded automatically. The perturbation skill becomes available as/perturbation - CLAUDE.md from
--add-dirrequires the env var to load, without it only the skill is picked up not the teaching heuristics
Copy the CLAUDE.md content into your global file and install the skill to your personal skills directory.
# Append tutor heuristics to global CLAUDE.md
cat ~/llm-tutor/CLAUDE.md >> ~/.claude/CLAUDE.md
# Copy the skill
cp -r ~/llm-tutor/.claude/skills/perturbation ~/.claude/skills/Tradeoff: The tutor heuristics are now active in every Claude Code session, including ones where you want a normal coding assistant. Add a mode switch to the CLAUDE.md if you go this route. The anti-patterns section already has "if shipping, switch to assistant mode."
Copy both into your project's .claude/ directory:
cp ~/llm-tutor/CLAUDE.md ./CLAUDE.md
cp -r ~/llm-tutor/.claude/skills ./.claude/skillsTradeoff: Tutor rules only active in that project. Must repeat for each learning repo.
If this repo is on GitHub, others can install the perturbation skill:
npx skills add <you>/llm-tutor -g -a claude-codeNote: This installs the skill only, not the CLAUDE.md. The user still needs to manually add the CLAUDE.md content to their global or project config. This is a limitation of the skills ecosystem — skills are on-demand; CLAUDE.md is always-on. They're different mechanisms.
With Option A or B active, Claude will follow the tutor heuristics on every turn. When you complete an exercise, Claude should invoke /perturbation automatically (the CLAUDE.md §2 instructs it to).
You can also invoke the protocol manually:
/perturbation
The CLAUDE.md is domain-agnostic. To specialise for a domain (embedded systems, web dev, ML, etc.), add a project-level CLAUDE.md in your learning repo with domain-specific rules. Claude Code merges CLAUDE.md files from all levels - global rules are inherited, project rules add specifics.
Example project-level CLAUDE.md for embedded systems:
# Domain: Bare-metal embedded (STM32G4, ARM Cortex-M4)
- Point to RM0440 sections rather than summarising register layouts.
- The skill of navigating a 2000-page reference manual is itself a
core competency — do not shortcut it.
- Connect concepts to interview framing when relevant: pointer fluency,
memory layout, synchronisation primitives, driver architecture.
- Every concept should terminate in something observable on hardware.llm-tutor/
├── CLAUDE.md # Always-on tutor heuristics
├── .claude/
│ └── skills/
│ └── perturbation/
│ └── SKILL.md # On-demand probing protocol
└── README.md
The separation is intentional. CLAUDE.md is a constitution: short, stateless, shapes every turn. The skill is a protocol: stateful, multi-step, loaded only when the perturbation condition fires. Mixing them in one file causes instruction decay (IFScale research shows adherence degrades linearly past ~150 instructions for Claude Sonnet) and makes the protocol behave like a heuristic, which the LLM will pattern-match rather than follow step-by-step.