@houtini/metacog
v0.4.1
Published
Real-time metacognition for Claude Code. Six senses detect stuck loops, validation bias, repeated errors, and circular actions. Learns what works across sessions.
Downloads
440
Maintainers
Readme
metacog
Memory remembers what happened. Metacog notices how you're thinking. Memory plugins serve a real purpose — they persist context, preferences, and project knowledge across sessions. Metacog does something different: it gives your Claude Code agent real-time awareness of its own cognitive state. Six senses detect context overflow, stuck loops, validation bias, repeated errors, and circular actions before they spiral. And when problems resolve, the system learns what fixed them — building rules that get stronger over time, not stale. Zero dependencies. One-command install. Open source.
Install
From npm
npx @houtini/metacog --installThis downloads the package and registers both hooks into your global Claude Code settings (~/.claude/settings.json). Metacog runs silently in the background from that point on — you'll only see output when something is abnormal.
For project-scoped install (writes to .claude/settings.json in the current directory):
npx @houtini/metacog --install --projectFrom source
git clone https://github.com/houtini-ai/metacog
cd metacog && node src/install.js --installThis points the hooks at your local clone, so changes take effect immediately.
What are Claude Code hooks?
Hooks are shell commands that Claude Code runs automatically at specific moments during a session. They're the plugin system's way of letting tools react to what the agent is doing, without the agent having to ask for it.
There are a few hook events that matter here:
| Hook event | When it fires | What it's for |
|------------|--------------|---------------|
| PostToolUse | After the agent uses any tool (Read, Write, Bash, etc.) | Monitoring, validation, side effects |
| UserPromptSubmit | When you send a message | Injecting context, session setup |
| PreToolUse | Before a tool runs | Blocking dangerous actions |
| Stop | When the agent finishes responding | Cleanup, verification |
Hooks communicate back to Claude via JSON on stdout:
{
"continue": true,
"suppressOutput": false,
"systemMessage": "This message appears in the agent's context"
}If a hook outputs nothing and exits 0, it's invisible. Zero token cost. Zero latency (well, near-zero). This is what makes hooks different from MCP servers - they can be completely silent when there's nothing to say.
Metacog uses two hooks: PostToolUse for the nervous system and UserPromptSubmit for injecting learned rules at session start.
What it does
Metacog runs as a pair of Claude Code hooks. One fires after every tool call (the nervous system), the other fires once per session (the reinforcement injector). When everything is normal, both produce zero output and cost zero tokens. When something is abnormal, a short signal appears in the agent's context. Not a command - just awareness. The agent's own reasoning decides what to do about it.
The six senses
| Sense | Signal | What it detects | |-------|--------|-----------------| | O2 | Context trend | Token velocity spikes — the agent is consuming context unsustainably | | Chronos | Temporal awareness | Time and step count since last user interaction | | Nociception | Error friction | Repeated similar errors — the agent is stuck | | Spatial | Blast radius | File dependency count after writes | | Vestibular | Action diversity | Repeated identical actions — going in circles | | Echo | Validation bias | Writing code without running tests, or validating against own output instead of the project's test suite |
The three layers
Layer 1: Proprioception (always on, near-zero cost) Calculates all five senses after every tool call. Injects a signal only when values deviate from baseline. Most turns: completely silent.
[Metacognition — review and ignore if not relevant]
Context velocity is high (3 large file reads in last 5 actions).
Does your current approach need all this context, or could an Agent subagent handle the remaining exploration?Layer 2: Nociception (triggered by Layer 1 thresholds) When error friction crosses critical thresholds, escalating interventions kick in - Socratic questioning first, then directive instructions, then flagging the user.
[NOCICEPTIVE INTERRUPT]
You have attempted 4 similar fixes with consecutive similar errors.
Before taking another action:
1. State the assumption you are currently operating on
2. Describe what read-only action would falsify that assumption
3. Execute that investigation before writing any more codeLayer 3: Motor Learning (cross-session) When a nociceptive event resolves, the system extracts what changed. The delta between failure and resolution gets persisted as a behavioural lesson and injected into future sessions.
Memory vs. metacognition
Memory plugins are valuable — they persist what the agent knows across sessions: user preferences, project context, decisions made. Claude Code's built-in memory system does this well.
But memory answers "what happened?" Metacog answers "how am I thinking right now?" It's the difference between a journal and a nervous system. One records the past. The other tells you when your hand is on the stove.
Metacog doesn't replace memory. It complements it by tracking how the agent reasons — what patterns lead to failure, what changes fix them — and building rules that get more confident over time.
The seesaw problem
Standard time-decay actively punishes success. If the agent learns "don't retry the same error three times" and stops doing it, the decay system sees the rule going stale and prunes it. The agent forgets. The behaviour regresses.
Metacog inverts this. When a known pattern doesn't fire during a session where its rule was active, that's a suppression - evidence the rule is working. Both detections and suppressions increase confidence. Only truly dormant rules decay.
How the data flows
Session start - the UserPromptSubmit hook compiles all learnings (global + project-scoped) into a digest and injects it as a system message. A marker file records which patterns were active.
During the session - the PostToolUse hook fires after every tool call. It records actions into a rolling 20-item window. Silent when normal. Signals when abnormal.
Session end - when the next session starts, the system:
- Reads the active patterns from the previous session
- Runs all pattern detectors against the session state
- Detections: the failure happened
- Suppressions: the rule was active, its preconditions were met, but the failure didn't happen (evidence the rule worked)
- Persists both to JSONL - global and project-scoped
Per-project scoping
Learnings are stored at two levels:
- Global (
~/.claude/metacog-learnings.jsonl) - patterns that apply everywhere - Project (
<project>/.claude/metacog-learnings.jsonl) - patterns specific to this codebase
Project-scoped entries take precedence where they overlap.
Configuration
Metacog works with zero configuration. To tune thresholds, create .claude/metacog.config.json in your project:
{
"proprioception": {
"o2": {
"velocity_multiplier": 3,
"baseline_window": 10
},
"chronos": {
"time_threshold_minutes": 15,
"step_threshold": 25
},
"nociception": {
"consecutive_errors": 3,
"error_similarity": 0.6,
"window_size": 5
},
"spatial": {
"blast_radius_threshold": 5,
"enabled": true
},
"vestibular": {
"action_similarity": 0.8,
"consecutive_similar": 4
},
"echo": {
"write_streak_threshold": 5,
"cooldown": 8
}
},
"nociception": {
"escalation_cooldown": 5,
"reflex_arc_threshold": 8
}
}| Setting | Default | What it does |
|---------|---------|-------------|
| o2.velocity_multiplier | 3 | Trigger when token velocity exceeds baseline by this factor |
| chronos.time_threshold_minutes | 15 | Signal after this many minutes without user interaction |
| chronos.step_threshold | 25 | Signal after this many tool calls without user interaction |
| nociception.consecutive_errors | 3 | Similar errors before signalling |
| spatial.blast_radius_threshold | 5 | File imports before signalling |
| vestibular.consecutive_similar | 4 | Identical actions before signalling |
| echo.write_streak_threshold | 5 | Consecutive writes without test run before signalling |
Pattern detectors
The cross-session learning detectors are configurable. Tune thresholds or disable individual detectors:
{
"patterns": {
"circular_search": { "enabled": true, "consecutive_runs": 2 },
"repeated_file_read": { "enabled": true, "repeat_threshold": 3 },
"error_loop": { "enabled": true, "recent_window": 10, "min_errors": 4, "max_unique_sigs": 2 },
"long_autonomous_run": { "enabled": true, "turn_threshold": 50 },
"write_heavy_session": { "enabled": true, "min_writes": 10, "read_ratio": 0.5 }
}
}Custom pattern detectors
Define your own detectors in JSON:
{
"custom_patterns_path": ".claude/my-patterns.json"
}[
{
"id": "too_many_bash_calls",
"category": "Execution Patterns",
"lesson": "Consider using dedicated tools (Read, Grep) instead of Bash for file operations.",
"relevant_tools": ["Bash"],
"condition": {
"type": "count_exceeds",
"filter": { "tool_name": "Bash" },
"threshold": 15
}
}
]Supported condition types: count_exceeds, consecutive_exceeds, ratio_exceeds.
Plugin structure
metacog/
├── .claude-plugin/
│ ├── plugin.json # Plugin identity
│ └── marketplace.json # Marketplace distribution
├── hooks/
│ └── hooks.json # Hook event configuration
├── src/
│ ├── hook.js # PostToolUse - nervous system
│ ├── digest-inject.js # UserPromptSubmit - reinforcement injector
│ ├── lib/
│ │ ├── config.js # Configuration + defaults
│ │ ├── learnings.js # Cross-session pattern detection
│ │ └── state.js # Rolling action window + token estimation
│ └── senses/
│ ├── o2.js # Context trend
│ ├── chronos.js # Temporal awareness
│ ├── nociception.js # Error friction + escalation
│ ├── spatial.js # Blast radius
│ ├── vestibular.js # Action diversity
│ └── echo.js # Validation bias
├── assets/
│ └── icon.png # Plugin icon
└── docs/ # DiagramsDesign principles
- No news is good news - signals only appear when values deviate from baseline
- Trends over absolutes - measures velocity, not absolute values
- Invite reflection, don't command - signals are observations the agent can act on or dismiss
- Graceful degradation - if the hooks fail, the agent is just normal Claude
- Reinforcement over decay - rules that work get stronger, not stale
Requirements
- Node.js 18+
- Claude Code with plugin support
Backstory
See SPEC.md for the full design specification and the research behind the reinforcement model.
Licence
Apache-2.0
