Hooks
Bobbin’s hook system integrates with Claude Code to automatically inject relevant code context into every conversation. When you type a prompt, bobbin searches your index for code related to what you’re asking about and injects it before Claude sees the message. The result: Claude understands your codebase without you having to copy-paste code.
How hooks work
Bobbin installs two Claude Code hooks:
- UserPromptSubmit — fires on every prompt you send. Bobbin runs a semantic search against your query, and if the results are relevant enough, injects them as context that Claude can see.
- SessionStart (compact) — fires after Claude Code compacts its context window. Bobbin re-injects key context so Claude doesn’t lose codebase awareness after compaction.
Both hooks are non-blocking and add minimal latency to your interaction.
Setting up hooks
Prerequisites
You need a bobbin index first:
bobbin init
bobbin index
Install hooks
bobbin hook install
This adds hook entries to your project’s .claude/settings.json. From now on, every prompt in this project directory triggers automatic context injection.
For global installation (all projects):
bobbin hook install --global
Global hooks write to ~/.claude/settings.json and fire in every Claude Code session, regardless of project.
Verify installation
bobbin hook status
Output shows whether hooks are installed and the current configuration:
Claude Code hooks: installed
Git hook: not installed
Configuration:
threshold: 0.5
budget: 150 lines
content_mode: preview
min_prompt_length: 10
gate_threshold: 0.75
dedup: enabled
Tuning injection behavior
The defaults work for most projects, but you can tune how aggressively bobbin injects context.
Threshold
The threshold controls the minimum relevance score for a search result to be included in the injected context:
bobbin hook install --threshold 0.3 # More results, lower relevance bar
bobbin hook install --threshold 0.7 # Fewer results, higher quality
Lower values mean more code gets injected (broader context). Higher values mean only highly relevant code is injected (tighter focus).
You can also set this in .bobbin/config.toml:
[hooks]
threshold = 0.5
Budget
The budget limits the total lines of injected context:
bobbin hook install --budget 200 # More context
bobbin hook install --budget 50 # Minimal context
A larger budget gives Claude more code to work with. A smaller budget keeps injections concise and avoids overwhelming the context window.
[hooks]
budget = 150
Gate threshold
The gate threshold is a separate check that decides whether injection happens at all. Before injecting, bobbin checks the top semantic search result’s raw similarity score. If it falls below the gate threshold, the entire injection is skipped — your prompt isn’t relevant to the indexed code.
[hooks]
gate_threshold = 0.75 # Default: skip injection if top result < 0.75 similarity
Lower values make injection more eager (fires on loosely related prompts). Higher values make it more conservative (only fires when the prompt clearly relates to indexed code).
Minimum prompt length
Very short prompts (“yes”, “ok”, “continue”) don’t benefit from code injection. The min_prompt_length setting skips injection for prompts below a character threshold:
[hooks]
min_prompt_length = 10 # Default
Content mode
Controls how much code is included per result:
[hooks]
content_mode = "preview" # 3-line excerpts (default)
# content_mode = "full" # Full source code
# content_mode = "none" # Paths and metadata only
preview is the default and works well for most cases — it gives Claude enough to understand each result without consuming too much of the context window.
full is useful when you want Claude to have complete function implementations in context, not just previews.
Deduplication
By default, bobbin tracks what it injected in the previous prompt and skips re-injection if the results haven’t changed. This avoids flooding Claude’s context with the same code on follow-up messages about the same topic.
[hooks]
dedup_enabled = true # Default
Set to false if you want every prompt to get fresh injection regardless.
Progressive reducing
Over a long conversation, bobbin avoids re-injecting code you’ve already seen. The reducing system keeps a per-session ledger of every chunk injected and filters duplicates on subsequent turns.
[hooks]
reducing_enabled = true # Default
How it works:
- Each injection records which chunks were sent, stored in
.bobbin/session/<session_id>/ledger.jsonl - On the next prompt, bobbin filters out chunks already in the ledger
- Only new or changed chunks are injected
- The ledger resets on context compaction (SessionStart hook)
This means early prompts get full context, and follow-up prompts get only new relevant code — keeping the context window efficient.
When reducing fires, you’ll see output like:
bobbin: injecting 3 new chunks (7 previously injected, turn 4)
Set reducing_enabled = false to inject all matching chunks every time (useful for debugging).
Keyword-triggered repo scoping
In multi-repo setups, certain queries clearly target specific repos. Configure keyword rules to auto-scope search:
[[hooks.keyword_repos]]
keywords = ["ansible", "playbook", "deploy"]
repos = ["goldblum", "homelab-mcp"]
[[hooks.keyword_repos]]
keywords = ["bobbin", "search", "embedding"]
repos = ["bobbin"]
[[hooks.keyword_repos]]
keywords = ["beads", "bd ", "issue"]
repos = ["beads"]
When any keyword matches (case-insensitive substring), search is scoped to only those repos. If no keywords match, all repos are searched.
Multiple rules can match simultaneously — their repo lists are merged and deduplicated.
Repo affinity boost
Files from the agent’s current working directory repo get a score multiplier:
[hooks]
repo_affinity_boost = 2.0 # Default: 2x score for local repo
This is a soft bias — cross-repo results still appear, but local files rank higher. Set to 1.0 to disable.
Feedback prompts
Bobbin periodically prompts agents to rate injected context quality:
[hooks]
feedback_prompt_interval = 5 # Every 5 injections (0 = disabled)
Every N injections, bobbin shows unrated injection IDs and prompts:
bobbin: 3 unrated injections this session. Rate with:
bobbin feedback submit --injection <id> --rating <useful|noise|harmful>
Ratings feed into the Feedback System — over time, frequently useful code gets boosted and noisy code gets demoted.
Format modes
Control the output format of injected context:
[hooks]
format_mode = "standard" # Default
| Mode | Description |
|---|---|
standard | File paths, line ranges, and code content |
minimal | Compact output, fewer decorators |
verbose | Extended metadata (scores, tags, chunk types) |
xml | XML-tagged output for structured parsing |
The show_docs setting controls whether documentation files appear in injection output:
[hooks]
show_docs = true # Default. Set false to inject code-only
When false, doc files are still used for bridge discovery but not shown to the agent.
Full configuration reference
All settings in .bobbin/config.toml under [hooks]:
| Setting | Default | Description |
|---|---|---|
threshold | 0.5 | Minimum relevance score to include a result |
budget | 300 | Maximum lines of injected context |
content_mode | "full" | Display mode: full, preview, or none |
min_prompt_length | 20 | Skip injection for prompts shorter than this (chars) |
gate_threshold | 0.45 | Minimum top-result similarity to inject at all |
dedup_enabled | true | Skip injection when results match previous turn |
reducing_enabled | true | Progressive reducing (delta injection across session) |
show_docs | true | Include documentation files in output |
format_mode | "standard" | Output format: standard, minimal, verbose, xml |
skip_prefixes | [] | Prompt prefixes that skip injection entirely |
keyword_repos | [] | Keyword-triggered repo scoping rules |
repo_affinity_boost | 2.0 | Score multiplier for current repo files |
feedback_prompt_interval | 5 | Prompt for feedback every N injections (0 = disabled) |
Git hooks
Separately from Claude Code hooks, bobbin can install a git post-commit hook to keep the index fresh:
bobbin hook install-git-hook
This re-indexes files changed in each commit. See Watch & Automation for details on keeping your index current.
Hot topics
After using hooks for a while, bobbin accumulates data on which code areas are most frequently injected. The hot-topics subcommand generates a summary:
bobbin hook hot-topics
This writes hot-topics.md with analytics about your most-referenced code. Use it to identify which parts of your codebase come up most often in AI conversations — these are the areas worth keeping well-documented and well-structured.
Use --force to regenerate even if the injection count hasn’t reached the automatic threshold:
bobbin hook hot-topics --force
Reactions (PostToolUse hooks)
Reactions are rule-based context injections that fire after specific tool calls. Unlike the prompt-level hook, reactions target tool usage patterns — when an agent edits a config file, runs a deploy command, or touches a specific codebase area, bobbin can inject relevant guidance and related files.
Reactions live in .bobbin/reactions.toml:
[[reactions]]
name = "service-restart-runbook"
tool = "Bash"
[reactions.match]
command = "systemctl (restart|stop|start)"
guidance = "Check the runbook for {args.command} before restarting services in production."
search_query = "service restart runbook {file_stem}"
max_context_lines = 30
[[reactions]]
name = "ansible-role-edit"
tool = "Edit"
[reactions.match]
file_path = ".*/ansible/roles/.*"
guidance = "This is an Ansible role. Test with: ansible-playbook --check -l test"
use_coupling = true
coupling_threshold = 0.15
[[reactions]]
name = "credential-guard"
tool = "Edit"
[reactions.match]
file_path = ".*\\.(env|credentials|secret).*"
guidance = "WARNING: This file may contain credentials. Never commit secrets to git."
Reaction fields
| Field | Type | Description |
|---|---|---|
name | string | Rule name (used for dedup and metrics) |
tool | string | Tool name glob (e.g., "Edit", "Bash", "mcp__homelab__*") |
match | table | Parameter match conditions — keys are param names, values are regex |
guidance | string | Text shown to the agent. Supports {args.X} and {file_stem} templating |
search_query | string | Search query template for injecting related files |
search_group | string | Index group to search |
search_tags | list | Tag filters for scoped search |
max_context_lines | int | Max lines to inject (default: 50) |
use_coupling | bool | Use git coupling instead of search |
coupling_threshold | float | Min coupling score (default: 0.1) |
roles | list | Role patterns this reaction applies to (glob, e.g., "aegis/crew/*") |
Role-scoped reactions
Restrict reactions to specific agent roles:
[[reactions]]
name = "deploy-check"
tool = "Bash"
[reactions.match]
command = "deploy|kubectl"
roles = ["*/crew/*"] # Only crew members, not polecats
guidance = "Deploy detected. Verify staging tests passed first."
Noise filtering
In multi-agent environments, many prompts are operational (patrol nudges, reactor alerts, handoff messages) rather than development queries. Bobbin automatically detects these and skips injection to avoid wasting tokens on irrelevant context.
Automated message detection recognizes patterns like:
- Patrol nudges (
"Auto-patrol: ...","PATROL LOOP ...") - Reactor alerts (
"[reactor] ...") - Handoff/startup protocols (
"HANDOFF COMPLETE","STARTUP PROTOCOL") - Agent role announcements (
"aegis Crew ian, checking in.") - System reminder blocks (
"<system-reminder>...")
This is hardcoded in the hook logic and not configurable — it targets patterns that are definitively operational.
For user-configurable noise filtering, use skip_prefixes:
[hooks]
skip_prefixes = [
"git ", "bd ", "gt ",
"cargo build", "go test",
"y", "n", "ok", "done",
]
See Hooks Configuration for full skip_prefixes documentation.
Directory navigation injection
When an agent encounters a file-not-found error or tries to read a directory, bobbin can inject the directory tree to help navigation:
- EISDIR (directory read): Shows the directory tree (up to 20 lines)
- File not found: Shows the parent directory tree to help locate the right file
This fires via the try_directory_navigation() function in the hook and requires no configuration.
Removing hooks
Remove Claude Code hooks:
bobbin hook uninstall
# Or for global hooks:
bobbin hook uninstall --global
Remove the git post-commit hook:
bobbin hook uninstall-git-hook
Practical workflows
Daily development setup
Install hooks once per project, then forget about them:
cd ~/projects/my-app
bobbin init && bobbin index
bobbin hook install
bobbin hook install-git-hook # Keep index fresh on commits
Now every Claude Code conversation in this directory automatically gets relevant code context.
Combining hooks with watch mode
For real-time index updates (not just on commits):
bobbin hook install # Context injection
bobbin watch & # Real-time index updates
This ensures the injected context reflects your latest uncommitted changes, not just what was last committed.
Debugging injection
If Claude doesn’t seem to be getting the right context, check what bobbin would inject for a given prompt:
echo '{"query": "how does auth work"}' | bobbin hook inject-context --no-dedup
Or check the hook status to verify configuration:
bobbin hook status --json
Next steps
- Watch & Automation — keep your index current
- Searching — understand the search engine behind injection
hookCLI reference — full subcommand reference- Configuration Reference — all
config.tomlsettings