Install

What Learner needs, the ways in, what lands where, and which platforms can run it.

Requirements

Claude Code plugin

claude plugin marketplace add Tykok/learning-with-claude
claude plugin install learner

Installs and enables the skill and its hooks natively — no ~/.claude file copying, no learner-install step. Claude Code manages updates itself (/plugin update learner); run learner update and it will tell you the same thing rather than trying to curl a second, traditional install on top. Already installed via curl, clone, Homebrew, or apt? Run uninstall.sh (or learner-uninstall) first — running both wires every hook twice.

apt (Debian/Ubuntu)

# one time
curl -fsSL https://tykok.github.io/learning-with-claude/apt/learner.gpg \
  | sudo gpg --dearmor -o /usr/share/keyrings/learner.gpg
echo "deb [signed-by=/usr/share/keyrings/learner.gpg] https://tykok.github.io/learning-with-claude/apt stable main" \
  | sudo tee /etc/apt/sources.list.d/learner.list

# from then on
sudo apt update && sudo apt install learner
learner-install --level S --synthesis normal --blanks 2

One curl remains — fetching the repository's trust anchor once, the same pattern Docker's and HashiCorp's own apt repos use. There is no keyless way to establish that first trust, but after this one-time step, sudo apt update && sudo apt upgrade learner is the whole update story — no more curl involved, ever.

Prefer not to add a repository? Grab the .deb directly from Releases instead:

curl -LO https://github.com/Tykok/learning-with-claude/releases/download/v0.2.0/learner_0.2.0_all.deb
sudo apt install ./learner_0.2.0_all.deb
learner-install --level S --synthesis normal --blanks 2

v0.2.0 is this release. Check Releases for the current version if you're reading this after a newer one has shipped.

Homebrew

brew tap Tykok/learning-with-claude https://github.com/Tykok/learning-with-claude
brew install learner
learner-install --level S --synthesis normal --blanks 2

A personal tap, not homebrew-core.

Both apt and Homebrew only stage the files and drop learner-install / learner-uninstall on PATH — neither touches ~/.claude on its own. Run learner-install afterward, same flags as install.sh below. Uninstalling reverses the same way: sudo apt remove learner / brew uninstall learner only remove those two wrapper binaries — the payload under ~/.claude still needs learner-uninstall (same as uninstall.sh) to actually come out.

Clone and run

A first-class path, not a fallback: it is the one that lets you read the installer before you run it.

git clone https://github.com/Tykok/learning-with-claude
cd learning-with-claude
./install.sh                       # prompts for level / synthesis / blanks
./install.sh --dry-run             # print what would happen, write nothing
./install.sh --yes                 # never prompt; defaults for anything unset
./install.sh --level S --synthesis normal --blanks 2    # non-interactive, one shot

The installer is idempotent: re-running re-copies the hooks and the skill and re-merges the hook wiring without duplicating entries, and it never overwrites a config you already have. It writes nothing into any repository. Every path it touches is under $CLAUDE_CONFIG_DIR (default ~/.claude), and every wired hook command carries the literal ${CLAUDE_CONFIG_DIR:-$HOME/.claude} rather than an expanded path, so moving your config directory later needs no reinstall.

Hook wiring is read once, when a Claude Code session starts. Installing (or re-installing) while a session is already open changes nothing in it — quit and start a new session for the hooks to take effect.

Alternative: the curl one-liner

curl -fsSL https://raw.githubusercontent.com/Tykok/learning-with-claude/main/bootstrap.sh | sh

It asks for your level, how often a synthesis question should replace a granular one, and how many holes a fill exercise leaves, then writes everything under your Claude Code config directory — this is what it fetches and runs under the hood. To skip the prompts, pass the flags install.sh takes — bootstrap.sh forwards them through untouched and owns no defaults of its own:

curl -fsSL https://raw.githubusercontent.com/Tykok/learning-with-claude/main/bootstrap.sh \
  | sh -s -- --level S --synthesis normal --blanks 2

To install a specific revision rather than whatever main says today, name the ref twice — once in the URL your shell reads, once in LEARNER_REF for the payload that URL then fetches. Anything git resolves works: a release tag, a branch name, or a commit SHA.

REF=v0.1.0   # a tag, a branch name, or a commit SHA
curl -fsSL "https://raw.githubusercontent.com/Tykok/learning-with-claude/$REF/bootstrap.sh" \
  | LEARNER_REF="$REF" sh

LEARNER_REF pins the payload, not bootstrap.sh itself — your shell has already read that from the URL by the time the variable is visible. Pinning only one of the two still runs whatever main says, which is why the ref appears in both places.

What gets installed, and where

Everything lives under $CLAUDE_CONFIG_DIR, written $CFG below.

PathRole
$CFG/skills/learner/SKILL.mdThe learner skill: dispatch, levels, config
$CFG/skills/learner/references/The protocols: quiz.md, hook-quiz.md, improve.md, export.md, update.md, data.md
$CFG/skills/learner/VERSIONThe installed version string; re-copied unconditionally on every install/re-install, so it always matches what's on disk
$CFG/skills/learner/INSTALL_ORIGINHow this install got here — curl, brew or apt; re-stamped on every install/re-install
$CFG/hooks/learner-config.shShared config resolution — sourced-only, never run on its own
$CFG/hooks/learner-onboard.shSessionStart — reports a broken install and nothing else
$CFG/hooks/learner-update-check.shSessionStart (second entry) — notifies once a day when a newer version is out; no jq dependency
$CFG/hooks/learner-record-edit.shPostToolUse on Write/Edit — records the files edited this session
$CFG/hooks/learner-quiz.shStop — the quiz trigger, and the LEARNER-TODO guardrail
$CFG/hooks/learner-cleanup.shSessionEnd — deletes this session's scratch files
$CFG/settings.jsonHook wiring, merged in; a .bak of the pre-Learner file is kept beside it
$CFG/learner.jsonYour global config
$CFG/learner/Empty directory, created by the installer: the skill writes memory.md, recap.md and export.json into it at runtime, and the update-check hook writes its throttle stamp (.last-update-check) there too
$CFG/learner/memory.mdOpen weak spots — the only file read to choose a question. Created by the skill on first use, not by the installer
$CFG/learner/recap.mdReadable dashboard: to improve, mastered, session history. Also created on first use
$CFG/learner/export.jsonWhich Notion database learner export pushes to, and when it last ran. Created on the first export, not by the installer

Six hook files ship and five are wired, into SessionStart (twice — learner-onboard.sh and learner-update-check.sh both fire there), PostToolUse, Stop and SessionEnd. learner-config.sh is the sixth: it is sourced-only, and Claude Code never invokes it directly. Four of the other five source it — every hook but learner-cleanup.sh, which needs none of it.

Platforms

PlatformSupportedNotes
macOSyesneeds jq
Linuxyesneeds jq
Windows via WSLyesit is a Linux environment
Windows via Git Bashyesprovides the POSIX sh the hooks need
Windows, nativenothe hooks are POSIX sh scripts; with no POSIX shell, Claude Code cannot run them

Native Windows is a genuine gap rather than an untested platform. Every hook is a POSIX sh script and each is wired as sh "…", so without a POSIX shell on the machine there is nothing for Claude Code to execute. WSL and Git Bash both supply one, and on either of them Learner behaves exactly as it does on Linux.

Next: Usage — the three shapes a question takes.