statusline-vocab
Skill19 starsChanged 3 months agoMITView on GitHub
Manage the statusline-vocab feature — a `Stop` hook picks one English word worth learning from each conversation and renders `{emoji} {word} /IPA/ pos. {translation}` on the Claude Code statusline (translation language configurable; Chinese by default). Trigger on "/statusline-vocab" or when the user asks to install / configure / check / uninstall / switch language / debug the vocab statusline. Do NOT auto-trigger — explicit invocation only.
In this repository18 other files it gives its agents.
- base-breakout-scanSkill
- commit-contextSkill
- conviction-funnelSkill
- distill-memorySkill
- distill-moduleSkill
- fable-mindSkill
- hackernewsSkill
- map-moduleSkill
- mcd-orderSkill
- mean-reversion-scanSkill
- momentum-scanSkill
- premarket-briefSkill
- regime-scanSkill
- review-iterateSkill
- snapback-scanSkill
- sparse-checkoutSkill
- wallstreetbetsSkill
- yfinanceSkill
The file
We split this skill into the rules it gives an agent
7 rules. About 0 are common, 3 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 13
Put a "word of the conversation" segment on the Claude Code statusline so the user passively learns English vocabulary from their own work.
- Distinctive: line 49
Before doing anything destructive, verify the prerequisites.
- Very specific: line 119
After inserting, run Step 4 to confirm.
- Distinctive: line 188
If the user asks for a tunable that requires more than a one-line edit (e.g.
- Very specific: line 208
No word ever appears: Check ~/.claude/vocab/extract.log for errors.
- Very specific: line 220
Hook fires twice on every turn: Look for duplicate entries in ~/.claude/settings.json under hooks.Stop — possible if the file was …
- Very specific: line 234
If the user wants a richer experience later (Anki export, daily review prompt, frequency-based spaced repetition over history.jsonl), build …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
statusline-vocab
Put a "word of the conversation" segment on the Claude Code statusline so the user passively learns English vocabulary from their own work. Three moving parts:
~/.claude/hooks/vocab/extract.sh—Stophook. Each time the assistant finishes a turn, this hook reads the last ~30 transcript entries, callsclaude -p --model claude-haiku-4-5to pick one English word worth learning (lemma + US IPA + POS + translation + emoji; target language configurable), and writes JSON to~/.claude/vocab/current.json. A 3-minute cooldown prevents the word from churning every single turn within one conversation.~/.claude/vocab/current.json+~/.claude/vocab/history.jsonl— current word plus an append-only wordbook history of everything that's been picked.~/.claude/statusline-command.sh— readscurrent.jsonand appends a vocab segment to the statusline output.
Rendered example (with default Chinese; swap to any language via --lang):
➜ skills git:(develop) · Opus 4.7 ctx:12% · 📝 transcript /ˈtrænskrɪpt/ n. 誊本,逐字记录When to use this skill
The user has typed /statusline-vocab or otherwise referenced this feature. Likely intents:
- Install on a fresh machine — set everything up (optionally with
--lang) - Status / diagnose — "why isn't a word showing up?", "what's installed?", "what language am I on?"
- Switch language — "translate to Spanish", "use Japanese definitions" →
install.py config --lang … - Reconfigure other knobs — cooldown, model, selection criteria (edit
extract.shdirectly) - Uninstall — remove the hook and tell the user how to clean the statusline
Pick the right subcommand below based on which of these the user wants. If unclear, run
status first so the conversation has a shared baseline.
Workflow
Step 1 — Detect environment
Before doing anything destructive, verify the prerequisites. The installer script does this too, but it's worth knowing what it checks so you can give a useful error if something is missing:
claudeCLI onPATH(the hook shells out toclaude -p)jqonPATH(both the hook and the statusline parse JSON with it)~/.claude/exists (true on any machine where Claude Code has been launched)
Step 2 — Run the installer
python <skill-dir>/scripts/install.py installThe installer is idempotent — running it twice does nothing the second time. It:
- Creates
~/.claude/hooks/vocab/and~/.claude/vocab/if missing. - Copies the canonical
extract.shfrom the skill into~/.claude/hooks/vocab/extract.sh(overwrites the file each run — this is how the user picks up skill updates). - Registers the hook under
hooks.Stopin~/.claude/settings.json. Skips if the exact command path is already present. Makes a.bak-vocabbackup before writing. - Handles the statusline based on its current state — see Step 3.
Step 3 — Statusline integration
The installer does not auto-edit a custom statusline. It branches into three cases and prints which one it took:
Case A — no statusline exists (~/.claude/statusline-command.sh missing or empty)
→ installer writes the bundled default-statusline.sh verbatim. The default is intentionally
plain (dir · model · ctx · vocab); users who want a richer look can edit freely after.
Case B — already integrated (statusline contains the # vocab:start marker)
→ installer skips, prints "already integrated".
Case C — existing custom statusline without markers
→ installer prints the snippet below and refuses to auto-modify. Your job in the
conversation: read the user's ~/.claude/statusline-command.sh, understand how it builds
its output line, and insert the snippet so the vocab segment lands at the end of the line
with a · separator. Always show the diff before writing so the user can sanity-check.
Insertion snippet (always surround with the markers so a future uninstall can find it):
# vocab:start — statusline-vocab skill
vocab_file="$HOME/.claude/vocab/current.json"
vocab_part=""
if [ -f "$vocab_file" ]; then
v_word=$(jq -r '.word // empty' "$vocab_file" 2>/dev/null)
if [ -n "$v_word" ] && [ "$v_word" != "null" ]; then
v_emoji=$(jq -r '.emoji // "📖"' "$vocab_file" 2>/dev/null)
v_ipa=$(jq -r '.ipa // ""' "$vocab_file" 2>/dev/null)
v_pos=$(jq -r '.pos // ""' "$vocab_file" 2>/dev/null)
v_meaning=$(jq -r '.meaning // ""' "$vocab_file" 2>/dev/null)
vocab_part=" · ${v_emoji} \033[1;94m${v_word}\033[0m \033[2m${v_ipa}\033[0m \033[0;33m${v_pos}\033[0m ${v_meaning}"
fi
fi
# vocab:endHow to wire ${vocab_part} into the final output depends on how the statusline is structured:
- Statusline builds a single
linevariable and prints once at the end (most common pattern, matches the bundled default): append${vocab_part}to that variable just before the finalprintf/echo. - Statusline calls
printfmultiple times inline: refactor lightly so the lastprintfgets${vocab_part}concatenated onto its argument. Don't add a separateprintfcall after, because Claude Code's statusline keeps trailing newlines literal. - Statusline outputs multiple lines on purpose (e.g. uses
printf '%b\n'twice): pick the line you want vocab on, and append there.
After inserting, run Step 4 to confirm.
Step 4 — Verify
python <skill-dir>/scripts/install.py statusReports: hook script present? hook registered? statusline integrated (markers found)? what word is current? how many history entries?
To preview rendering without waiting for a real Stop, pipe a stub event:
echo '{"workspace":{"current_dir":"'"$PWD"'"},"cwd":"'"$PWD"'","model":{"display_name":"Test"},"context_window":{"used_percentage":0}}' \
| bash ~/.claude/statusline-command.shThe vocab segment only appears once a word has been extracted — that happens after the next real
Stop hook fires in a Claude Code session. To force an extract right now, point the hook script
at an existing transcript:
latest=$(ls -t ~/.claude/projects/*/*.jsonl | head -1)
printf '{"transcript_path":"%s","hook_event_name":"Stop"}' "$latest" \
| ~/.claude/hooks/vocab/extract.sh
cat ~/.claude/vocab/current.jsonConfiguration
Translation language
The meaning field is translated into a target language — Chinese by default, but switchable. The
language is stored in ~/.claude/vocab/config so it survives across upgrades and doesn't require
editing extract.sh. Two ways to set it:
# At install time:
python <skill-dir>/scripts/install.py install --lang Spanish
# Anytime after, without reinstalling:
python <skill-dir>/scripts/install.py config --lang Japanese
# Inspect current:
python <skill-dir>/scripts/install.py configThe value is passed verbatim to the prompt ("…tutor for a {LANG}-speaking learner. … concise
translation in {LANG}, under ~15 characters or 3-4 words…"), so any language the model knows
works: Chinese, Japanese, Korean, Spanish, French, German, Portuguese, Italian,
Russian, Vietnamese, Thai, …
The change takes effect on the next Stop hook fire. The currently-displayed word
(current.json) is not retroactively re-translated — wait for the cooldown to expire or
rm ~/.claude/vocab/current.json to force a fresh extract.
Other knobs
For knobs that don't change often, edit ~/.claude/hooks/vocab/extract.sh directly. They live
at the top under # --- Knobs ---:
| Knob | Default | Effect |
COOLDOWN | 180 (seconds) | Skip re-extraction if current.json is fresher than this. Higher = more stable word; lower = updates more often. Raising to 600 gives roughly one word per "real" conversation. |
MODEL | claude-haiku-4-5 | Swap for claude-sonnet-4-6 if you want richer choices. Cost goes up; latency too (still async, so doesn't block). |
DEFAULT_LANG | Chinese | Only used when no config file exists. The config file always wins. |
| The prompt body | see file | Edit the skip-list ("the", "use", "make"…), the difficulty bias, or the JSON schema. After editing, the next Stop picks up the new prompt — no restart needed. |
If the user asks for a tunable that requires more than a one-line edit (e.g. "only extract on weekdays", "rotate through three difficulty levels"), edit the script in place.
Uninstall
python <skill-dir>/scripts/install.py uninstallThis:
- Removes the hook entry from
~/.claude/settings.json(backup at.json.bak-vocab-uninstall). - Deletes
~/.claude/hooks/vocab/extract.sh. - Leaves
~/.claude/vocab/intact so the user'shistory.jsonlsurvives. - Leaves the statusline alone, but prints the marker lines so the user (or Claude) can strip
them. If the statusline was the bundled default and the user wants a clean slate, they can
just
rm ~/.claude/statusline-command.sh.
Troubleshooting
- No word ever appears. Check
~/.claude/vocab/extract.logfor errors. The most common cause isclaudenot found from the hook's non-interactive shell — Claude Code hooks inherit a minimal PATH. Fix by symlinkingclaudeinto/usr/local/bin/or by editing the hook to use the absolute path. Verify manually with the "force an extract" snippet above. - Same word keeps showing. Cooldown is doing its job; either wait it out or
rm ~/.claude/vocab/current.jsonto force a re-extract on the nextStop. - Statusline renders broken / garbled.
jqis probably missing. The snippet swallows individualjqerrors but ends up with an empty vocab segment, which usually looks fine. If color codes are leaking as raw\033[...text, the user's terminal doesn't renderprintf '%b'— switch the insertion toecho -eor drop the colors. - Model picks bad words ("the", "use", "make"). Strengthen the skip-list in the prompt inside
extract.sh. Haiku follows the skip-list reliably. - Hook fires twice on every turn. Look for duplicate entries in
~/.claude/settings.jsonunderhooks.Stop— possible if the file was hand-edited between installer runs. Either re-run the installer (it dedupes) or remove the duplicate manually.
Mental model
Three boundaries, each swappable without touching the others:
- Trigger — when to think about updating the word. Currently
Stop(every assistant turn, rate-limited byCOOLDOWN). Could be moved toSessionEndfor "one word per session" semantics. - Storage —
current.json(the visible word) plushistory.jsonl(the running wordbook). - Render — how the statusline displays the word. The snippet only reads JSON; you can rewrite the format without ever touching the hook.
If the user wants a richer experience later (Anki export, daily review prompt, frequency-based
spaced repetition over history.jsonl), build it as a fourth piece that consumes history.jsonl —
don't entangle it with the trigger or render layers.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

