Automated error detection, fix tracking, and prevention for Claude Code. Learns from mistakes and teaches future sessions the fixes.
ERROR OCCURS FIX APPLIED FUTURE SESSIONS
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Capture │ │ Capture │ │ Prevent │
│ Error │────────────────▶│ Fix │─────────────────▶ │ + Teach │
│ │ same session │ │ curator pairs │ │
└──────────┘ same tool type └──────────┘ error → fix └──────────┘
│ │ │
▼ ▼ ▼
errors.jsonl errors.jsonl learned.json
{cmd, error} {fix, linked_to} {block, learned_fix}
Result: Claude makes mistake once → learns the fix → never makes that mistake again.
The plugin analyzes error messages, not just command patterns, to create precise blocking rules:
| Error Type | What Gets Blocked | Example |
|---|---|---|
Bad flag --xyz |
Only that specific flag | ls --invalid blocked, ls -la works |
| Command not found | Only that command | choco blocked, other commands work |
| Path not found | Nothing (environmental) | Won't learn from missing files |
| Permission denied | Nothing (environmental) | Won't learn from access errors |
This prevents false positives like blocking all ls commands just because one bad flag failed.
Beyond reactive blocking, the plugin now injects the most-relevant learned rules into Claude's context at the moment you submit a prompt. Each prompt triggers a TF-IDF ranking over your learned patterns; the top 10 (configurable) get rendered as a short natural-language bullet list under a 400-token cap, so Claude avoids the mistake in the first place.
Key knobs in config.json:
| Key | Default | Purpose |
|---|---|---|
injection_enabled |
true |
Turn off for reactive-only |
injection_top_k |
10 |
Max patterns per prompt |
injection_token_cap |
400 |
Hard ceiling on injected content |
injection_scope |
"top_learned" |
top_learned, all_active, learned_only, high_confidence |
decay_max_age_days |
30 |
Stale patterns drop from the pool |
vote_down_threshold |
3 |
Down-votes before auto-disable |
llm_curate_enabled |
false |
Opt in (requires ANTHROPIC_API_KEY) |
Disagree with a block? /error-learning vote <pattern_id> down.
Three down-votes disables it permanently.
From any Claude Code session:
/plugin marketplace add jonathondouglasyager-debug/claude-error-learning
/plugin install error-learning@yager-plugins
That's it. Hooks register automatically — no need to edit settings.json. Start a new Claude session for the hooks to take effect.
To enable your OS-specific pattern pack after installing:
# macOS/Linux
python3 ~/.claude/plugins/cache/**/error-learning/**/hooks/error-curator.py --enable linux
# Windows (PowerShell)
python "$env:USERPROFILE\.claude\plugins\cache\*\error-learning\*\hooks\error-curator.py" --enable windows(You can also just run the slash command /error-learning packs enable linux once the plugin is installed.)
If you'd rather install from a clone — e.g. to hack on the plugin locally — follow these steps instead.
Manual install steps
# macOS/Linux
git clone https://github.com/jonathondouglasyager-debug/claude-error-learning.git ~/claude-error-learning
# Windows
git clone https://github.com/jonathondouglasyager-debug/claude-error-learning.git %USERPROFILE%\claude-error-learningAdd the following to your ~/.claude/settings.json (macOS/Linux) or %USERPROFILE%\.claude\settings.json (Windows):
macOS/Linux:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 \"$HOME/claude-error-learning/hooks/command-validator.py\""
}
]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 \"$HOME/claude-error-learning/hooks/fix-tracker.py\""
}
]
}
],
"PostToolUseFailure": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 \"$HOME/claude-error-learning/hooks/error-logger.py\""
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "python3 \"$HOME/claude-error-learning/hooks/error-curator.py\" --auto"
}
]
}
]
}
}Windows:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python \"%USERPROFILE%\\claude-error-learning\\hooks\\command-validator.py\""
}
]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python \"%USERPROFILE%\\claude-error-learning\\hooks\\fix-tracker.py\""
}
]
}
],
"PostToolUseFailure": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python \"%USERPROFILE%\\claude-error-learning\\hooks\\error-logger.py\""
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "python \"%USERPROFILE%\\claude-error-learning\\hooks\\error-curator.py\" --auto"
}
]
}
]
}
}cd ~/claude-error-learning # or %USERPROFILE%\claude-error-learning on Windows
# macOS/Linux users:
python3 hooks/error-curator.py --enable linux
# Windows users:
python hooks/error-curator.py --enable windowsStart a new Claude session for hooks to take effect.
After installation, the plugin works automatically:
- Errors are captured when any tool fails
- Fixes are tracked when a successful command follows a failed one
- Patterns are learned at session end (if seen 2+ times)
- Commands are blocked in future sessions with the learned fix shown
Use the slash command to manage:
/error-learning # Show status and stats
/error-learning review # Review pending patterns
/error-learning packs # List available pattern packs
Patterns are organized into packs that can be enabled/disabled:
| Pack | Description | Default |
|---|---|---|
common |
Universal patterns (use Read not cat, etc.) | Enabled |
windows |
Windows/PowerShell patterns | Disabled |
linux |
Linux/bash patterns | Disabled |
learned |
Auto-learned from your errors | Enabled |
custom |
Your manual additions | Enabled |
Interactive (no coding):
- Double-click
manage-packs.pyor use Desktop shortcut - Type a number to toggle packs on/off
CLI:
python hooks/error-curator.py --packs # List all packs
python hooks/error-curator.py --enable windows # Enable a pack
python hooks/error-curator.py --disable linux # Disable a packIf a command gets incorrectly blocked, add it to the allowlist to bypass all learned patterns:
# List current allowlist
python hooks/error-curator.py --allowlist
# Allow by prefix (matches "ls -la", "ls foo", etc.)
python hooks/error-curator.py --allow "ls "
# Allow exact command only
python hooks/error-curator.py --allow-exact "git status"
# Allow by regex pattern
python hooks/error-curator.py --allow-regex "^npm (run|test|install)"
# Remove from allowlist
python hooks/error-curator.py --unallow "ls "The allowlist is checked before blocking patterns, so it acts as an override.
Slash command:
/error-learning packs # List all packs
/error-learning packs enable windows # Enable Windows patterns
/error-learning packs disable linux # Disable Linux patterns
Or edit config.json directly:
{
"enabled_packs": ["common", "windows", "learned", "custom"],
"auto_curate": true,
"curate_threshold": 2,
"show_confidence": true
}| Hook | File | Purpose |
|---|---|---|
| PostToolUseFailure | error-logger.py |
Captures errors with awaiting_fix flag |
| PostToolUse (Bash) | fix-tracker.py |
Links successful commands to prior errors |
| PreToolUse (Bash) | command-validator.py |
Blocks known-bad commands, shows fix |
| SessionEnd | error-curator.py |
Pairs errors with fixes, updates patterns |
When an error occurs:
error-logger.pylogs it withawaiting_fix: true- When a successful command follows (same session, same tool)
fix-tracker.pylogs it as a fix linked to the error- At session end,
error-curator.pypairs them and extracts patterns
When a blocked command is detected:
BLOCKED: '&&' doesn't work here.
LEARNED FIX (87% confidence): Use "cmd1; cmd2" instead
The confidence score is based on how often the fix worked.
error-learning/
├── .claude-plugin/
│ ├── plugin.json # Plugin manifest
│ └── marketplace.json # Self-hosted marketplace catalog
├── config.json # User settings
├── manage-packs.py # Interactive pack manager (double-click!)
├── hooks/
│ ├── error-logger.py # PostToolUseFailure
│ ├── fix-tracker.py # PostToolUse (Bash)
│ ├── command-validator.py # PreToolUse (Bash)
│ └── error-curator.py # SessionEnd + CLI commands
├── patterns/
│ ├── active.json # Merged patterns (auto-generated)
│ ├── allowlist.json # Commands that bypass blocking
│ └── packs/
│ ├── common.json # Universal patterns
│ ├── windows.json # Windows/PowerShell
│ ├── linux.json # Linux/bash
│ ├── learned.json # Auto-learned
│ └── custom.json # User additions
├── lib/
│ ├── __init__.py
│ ├── sanitizer.py # Error text sanitization
│ ├── tokenizer.py # TF-IDF tokenizer
│ ├── ranker.py # TF-IDF ranking
│ ├── renderer.py # Natural-language rendering + token cap
│ ├── decay.py # Pattern decay logic
│ ├── vote.py # Vote tracking
│ ├── curation.py # Opt-in LLM curation
│ └── config.py # Central config loader
├── tests/ # unittest suite (stdlib only)
├── Makefile # make test / make test-verbose
├── commands/
│ └── error-learning.md # Slash command
└── data/ # gitignored
├── errors.jsonl # Error + fix log
└── curator.log # Curation activity
python hooks/error-curator.py --reviewShows all error signatures, their occurrence count, and sample commands.
python hooks/error-curator.py --add-allAdds all detected patterns to learned.json.
python hooks/error-curator.py --add bash_and_chainingpython hooks/error-curator.py --mergeRegenerates active.json from enabled packs.
Use the slash command:
/error-learning add "rm command should use Remove-Item"
Or edit patterns/packs/custom.json:
{
"id": "my_custom_pattern",
"name": "My Custom Pattern",
"category": "custom",
"tool": "Bash",
"match": {
"type": "contains",
"pattern": "bad command"
},
"message": "BLOCKED: Explanation",
"learned_fix": "Use this instead",
"confidence": 100,
"source": "manual"
}Match types: contains, regex, exact
| Scenario | Tokens |
|---|---|
| Without prevention | ~210-610 per error |
| With prevention | ~60 per error |
| Savings | 70-90% |
- Fork this repo
- Add patterns to the appropriate pack
- Submit a PR
Pattern contributions welcome for:
- Platform-specific gotchas
- Common tool misuse patterns
- Language-specific errors
MIT