Table of Contents
- RFC: Hook Efficiency Improvements
- Executive Summary
- Current State
- Issues Identified
- Issue 1: SessionStart Hook Overload
- Issue 2: Duplicate Logic Across Hooks
- Issue 3: Overly Broad Matchers
- Issue 4: clarity-assist Always-On
- Issue 5: No Shared Utilities
- Proposed Changes
- Change 1: Consolidate SessionStart into Single Dispatcher
- Change 2: Add Path-Based Filtering to PostToolUse Hooks
- Change 3: Smarter git-flow Bash Hooks
- Change 4: Remove viz-platform SessionStart Hook
- Change 5: Make clarity-assist Opt-In
- Change 6: Create Shared Hook Library
- Change 7: Optimize project-hygiene Cleanup
- Implementation Order
- Phase 1: Quick Wins (Low effort, high impact)
- Phase 2: Optimizations (Medium effort)
- Phase 3: Architecture (Higher effort)
- Testing Plan
- Risks and Mitigations
- Open Questions
- References
- Changelog
RFC: Hook Efficiency Improvements
Status: Proposal
Created: 2026-01-28
Author: Claude Code Analysis
Priority: Medium
Estimated Effort: 2-3 sprints
Executive Summary
Analysis of the 16 hooks across 12 plugins revealed significant inefficiencies in trigger timing, matcher precision, and redundant operations. This RFC proposes changes to reduce session startup latency, minimize per-edit overhead, and eliminate duplicate logic.
Current State
Hook Inventory
| Plugin | Event | Matcher | Script | Lines |
|---|---|---|---|---|
| projman | SessionStart | - | startup-check.sh | 119 |
| pr-review | SessionStart | - | startup-check.sh | 31 |
| claude-config-maintainer | SessionStart | - | enforce-rules.sh | 69 |
| data-platform | SessionStart | - | startup-check.sh | 55 |
| viz-platform | SessionStart | - | inline echo | 1 |
| cmdb-assistant | SessionStart | - | startup-check.sh | 67 |
| contract-validator | SessionStart | - | auto-validate.sh | 196 |
| doc-guardian | PostToolUse | Write|Edit|MultiEdit |
notify.sh | 75 |
| code-sentinel | PreToolUse | Write|Edit|MultiEdit |
security-check.sh | 63 |
| data-platform | PostToolUse | Edit|Write |
schema-diff-check.sh | 139 |
| contract-validator | PostToolUse | Edit|Write |
breaking-change-check.sh | 175 |
| project-hygiene | PostToolUse | Write|Edit |
cleanup.sh | 29 |
| cmdb-assistant | PreToolUse | NetBox MCP tools | validate-input.sh | 80 |
| git-flow | PreToolUse | Bash |
branch-check.sh | 103 |
| git-flow | PreToolUse | Bash |
commit-msg-check.sh | 75 |
| clarity-assist | UserPromptSubmit | - | vagueness-check.sh | 217 |
Total: 16 hooks, ~1,493 lines of shell code
Performance Impact
Per session startup:
- 7 bash processes spawned
- 3 Python invocations
- 4 HTTP calls (Gitea API, NetBox API)
- Estimated 200-500ms added latency
Per file edit:
- 4 bash processes spawned (PostToolUse hooks)
- 1 bash process spawned (PreToolUse - code-sentinel)
- Multiple
findandgitcommands - Each hook independently parses JSON stdin
Issues Identified
Issue 1: SessionStart Hook Overload
Problem: 7 hooks run on every session, regardless of whether the user will use that plugin.
Affected plugins:
- projman
- pr-review
- claude-config-maintainer
- data-platform
- viz-platform
- cmdb-assistant
- contract-validator
Specific concerns:
projmanmakes HTTP calls to Gitea API to check open issuescmdb-assistantmakes HTTP calls to NetBox APIdata-platformspawns Python and attempts PostgreSQL connectioncontract-validatorcomputes MD5 hashes of all plugin filesviz-platformjust echoes "loaded" - zero value
Issue 2: Duplicate Logic Across Hooks
Problem: Multiple hooks implement the same checks.
| Check | Implemented by |
|---|---|
| MCP venv exists | projman, pr-review, data-platform |
| Git remote vs .env mismatch | projman, pr-review |
| JSON stdin parsing | 8 different hooks |
| File path extraction from JSON | 8 different hooks |
Issue 3: Overly Broad Matchers
Problem: Some hooks trigger far more often than needed.
| Hook | Matcher | Problem |
|---|---|---|
| git-flow/branch-check.sh | Bash |
Triggers on every bash command |
| git-flow/commit-msg-check.sh | Bash |
Triggers on every bash command |
| project-hygiene/cleanup.sh | Write|Edit |
Runs find on entire project every edit |
| data-platform/schema-diff-check.sh | Edit|Write |
Checks every file, then pattern-matches |
Issue 4: clarity-assist Always-On
Problem: vagueness-check.sh (217 lines) runs on every user prompt.
- Parses prompt
- Runs multiple regex matches
- Uses
bcfor floating-point calculations - Adds latency to every message
Current behavior: Opt-out (runs unless CLARITY_ASSIST_AUTO_SUGGEST=false)
Recommended: Opt-in (disabled unless explicitly enabled)
Issue 5: No Shared Utilities
Problem: Each hook implements its own:
- JSON parsing
- File path extraction
- Prefix output formatting
- Early exit conditions
Proposed Changes
Change 1: Consolidate SessionStart into Single Dispatcher
Create: scripts/hooks/session-dispatcher.sh
#!/bin/bash
# Single entry point for all SessionStart checks
# Runs common checks once, dispatches to plugins only when relevant
# Common checks (run once)
check_marketplace_health() { ... }
check_mcp_venvs() { ... }
check_git_remote() { ... }
# Plugin-specific checks (conditional)
if is_projman_project; then
source_plugin_check "projman"
fi
Benefits:
- Single process instead of 7
- Common checks run once
- Plugin checks only run if relevant
Files to modify:
- Create
scripts/hooks/session-dispatcher.sh - Create
scripts/hooks/lib/common.sh(shared functions) - Update all plugin
hooks.jsonto remove SessionStart - Add single SessionStart hook at marketplace level
Change 2: Add Path-Based Filtering to PostToolUse Hooks
Current: Hooks spawn, parse stdin, check path, usually exit.
Proposed: Check path relevance before detailed processing.
# At top of each PostToolUse hook
FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | ...)
# Immediate exit for irrelevant paths
case "$FILE_PATH" in
*/node_modules/*|*/.git/*|*/venv/*|*/__pycache__/*)
exit 0 ;;
esac
Affected hooks:
- doc-guardian/notify.sh
- data-platform/schema-diff-check.sh
- contract-validator/breaking-change-check.sh
- project-hygiene/cleanup.sh
Change 3: Smarter git-flow Bash Hooks
Current: Both hooks trigger on every Bash command.
Option A: Early command check before full parsing
# Quick check - if not git command, exit immediately
if ! echo "$INPUT" | grep -q '"command".*git'; then
exit 0
fi
Option B: Combine into single hook
# Single git-flow hook that handles both branch and commit validation
Files to modify:
plugins/git-flow/hooks/branch-check.shplugins/git-flow/hooks/commit-msg-check.sh- Optionally merge into
plugins/git-flow/hooks/git-validator.sh
Change 4: Remove viz-platform SessionStart Hook
Current:
{
"event": "SessionStart",
"type": "command",
"command": "echo 'viz-platform plugin loaded'"
}
Proposed: Delete entirely. Provides zero value.
Files to modify:
plugins/viz-platform/hooks/hooks.json- remove the hook
Change 5: Make clarity-assist Opt-In
Current: Enabled by default, check CLARITY_ASSIST_AUTO_SUGGEST=false to disable.
Proposed: Disabled by default, check CLARITY_ASSIST_AUTO_SUGGEST=true to enable.
# Change from:
AUTO_SUGGEST="${CLARITY_ASSIST_AUTO_SUGGEST:-true}"
# To:
AUTO_SUGGEST="${CLARITY_ASSIST_AUTO_SUGGEST:-false}"
Files to modify:
plugins/clarity-assist/hooks/vagueness-check.sh(line 11)plugins/clarity-assist/README.md(document opt-in)
Change 6: Create Shared Hook Library
Create: scripts/hooks/lib/common.sh
#!/bin/bash
# Shared utilities for all hooks
# Parse file_path from JSON stdin
extract_file_path() {
echo "$1" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | \
head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/'
}
# Parse command from JSON stdin
extract_command() {
echo "$1" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('command',''))" 2>/dev/null
}
# Standard prefix output
hook_output() {
local prefix="$1"
shift
echo "[$prefix] $*"
}
# Check if path is in ignorable location
is_ignored_path() {
case "$1" in
*/node_modules/*|*/.git/*|*/venv/*|*/__pycache__/*|*/.venv/*)
return 0 ;;
esac
return 1
}
Benefits:
- Consistent behavior across hooks
- Single place to fix bugs
- Reduced code duplication
Change 7: Optimize project-hygiene Cleanup
Current: Runs find across entire project on every edit.
Proposed:
- Only run cleanup after significant operations (not every edit)
- Use more targeted paths
- Add cooldown period (don't run if ran in last 5 minutes)
# Add cooldown
LAST_RUN_FILE="/tmp/project-hygiene-last-run"
if [[ -f "$LAST_RUN_FILE" ]]; then
LAST_RUN=$(cat "$LAST_RUN_FILE")
NOW=$(date +%s)
if (( NOW - LAST_RUN < 300 )); then
exit 0 # Skip if ran in last 5 minutes
fi
fi
Files to modify:
plugins/project-hygiene/hooks/cleanup.sh
Implementation Order
Phase 1: Quick Wins (Low effort, high impact)
- Remove viz-platform SessionStart hook
- Make clarity-assist opt-in
- Add early exit to git-flow hooks
Phase 2: Optimizations (Medium effort)
- Add path filtering to PostToolUse hooks
- Add cooldown to project-hygiene
- Create shared hook library
Phase 3: Architecture (Higher effort)
- Consolidate SessionStart into dispatcher
- Refactor all hooks to use shared library
Testing Plan
Before changes
- Measure session startup time (10 runs, average)
- Measure time to complete 10 file edits
- Count hook invocations via logging
After each phase
- Re-measure all metrics
- Verify no functionality lost
- Test on both dev machine and Raspberry Pi (hotport)
Expected improvements
- SessionStart: 200-500ms → 50-100ms (4-5x faster)
- Per-edit overhead: 100-200ms → 20-50ms (4x faster)
- Process spawns: 7 SessionStart + 5 per-edit → 1 SessionStart + 2-3 per-edit
Risks and Mitigations
| Risk | Mitigation |
|---|---|
| Breaking existing functionality | Comprehensive test plan, phase rollout |
| Shared library versioning | Keep library in marketplace root, plugins reference via relative path |
| User config migration (clarity-assist) | Document in CHANGELOG, mention in release notes |
Open Questions
- Should we support a
hooks.jsonpath matcher natively? (Would require Claude Code changes) - Should SessionStart hooks be lazy-loaded on first plugin command instead?
- Is the shared library approach compatible with plugins being installed independently?
References
- Analysis performed: 2026-01-28
- Total hooks analyzed: 16
- Total shell code: ~1,493 lines
- Plugins affected: 12
Changelog
| Date | Change |
|---|---|
| 2026-01-28 | Initial RFC created |