Get Started
lessons-learned automatically captures Claude's mistakes from session logs and injects relevant warnings before the same mistake repeats. No manual curation needed.
Prerequisites
- Node.js ≥ 22.5
- Claude Code (any recent version)
- A Unix-like shell (macOS, Linux, WSL)
Step 1 — Clone the repo
git clone https://github.com/joeblackwaslike/lessons-learned.git ~/lessons-learned
cd ~/lessons-learned
npm ci
npm ci installs dependencies and registers pre-commit hooks.
Step 2 — Wire the hooks
Open ~/.claude/settings.json in your editor and add the hooks section. If the file doesn't exist yet, create it.
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume|clear|compact",
"hooks": [
{
"type": "command",
"command": "node \"/Users/you/lessons-learned/hooks/session-start-reset.mjs\""
},
{
"type": "command",
"command": "node \"/Users/you/lessons-learned/hooks/session-start-lesson-protocol.mjs\""
}
]
},
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "node \"/Users/you/lessons-learned/hooks/session-start-scan.mjs\"",
"timeout": 5
}
]
}
],
"PreToolUse": [
{
"matcher": "Read|Edit|Write|Bash|Glob",
"hooks": [
{
"type": "command",
"command": "node \"/Users/you/lessons-learned/hooks/pretooluse-lesson-inject.mjs\"",
"timeout": 5
}
]
}
],
"SubagentStart": [
{
"matcher": ".+",
"hooks": [
{
"type": "command",
"command": "node \"/Users/you/lessons-learned/hooks/subagent-start-lesson-protocol.mjs\"",
"timeout": 5
}
]
}
]
}
}
Replace /Users/you/lessons-learned with your actual clone path — e.g. /home/alice/lessons-learned.
Tilde expansion (~/lessons-learned) is not supported in settings.json hook commands.
Step 3 — Verify injection is working
Start a new Claude Code session (restart required for hooks to load), then ask Claude to run any command that matches one of the seed lessons:
Ask Claude: "run pytest tests/"
If injection is working, Claude will receive a context block before running the command. You can also verify by checking that the hook script runs without errors:
echo '{"tool_name":"Bash","tool_input":{"command":"pytest tests/"},"session_id":"test"}' | \
node ~/lessons-learned/hooks/pretooluse-lesson-inject.mjs
Expected output (trimmed):
{ "hookSpecificOutput": { "additionalContext": "## REQUIRED: pytest flags..." } }
An empty {} means no lesson matched — which is correct for commands that don't match any trigger.
Step 4 — Add your first lesson
From within Claude Code, run:
/lessons:add
Claude will ask you five questions — what went wrong, how to fix it, what command triggers it, a summary, and optional tags/priority. The lesson is validated, written to data/lessons.db, and the manifest is rebuilt automatically.
Step 5 — Build the manifest
/lessons:add (and /lessons:review, /lessons:manage, and the other slash commands) rebuild the manifest for you automatically after every change — you shouldn't need to do this manually in normal use.
The manual build step only matters if you edit data/lessons.db directly (e.g. scripting a batch import) or are working on the plugin itself:
node scripts/lessons.mjs build
The manifest is what the injection hook reads at runtime, so if you ever bypass the slash commands and the CLI to change lessons, rebuild it before the change takes effect.
What's next
- How it works — understand the capture → inject loop end-to-end
- Working with lessons — anatomy, triggers, and lesson types
- Emitting lessons — the
#lessontag format - Scanning & discovery — automatic candidate discovery from session logs
- Slash commands — conversational interface to manage lessons
- Configuration — tune injection budget and thresholds
- Interactive visualization — explore the system architecture