Add "RFC-Hook-Efficiency-Improvements"
371
RFC-Hook-Efficiency-Improvements.-.md
Normal file
371
RFC-Hook-Efficiency-Improvements.-.md
Normal file
@@ -0,0 +1,371 @@
|
||||
# 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 `find` and `git` commands
|
||||
- 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:**
|
||||
- `projman` makes HTTP calls to Gitea API to check open issues
|
||||
- `cmdb-assistant` makes HTTP calls to NetBox API
|
||||
- `data-platform` spawns Python and attempts PostgreSQL connection
|
||||
- `contract-validator` computes MD5 hashes of all plugin files
|
||||
- `viz-platform` just 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 `bc` for 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`
|
||||
|
||||
```bash
|
||||
#!/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.json` to 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.
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```bash
|
||||
# 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
|
||||
```bash
|
||||
# Single git-flow hook that handles both branch and commit validation
|
||||
```
|
||||
|
||||
**Files to modify:**
|
||||
- `plugins/git-flow/hooks/branch-check.sh`
|
||||
- `plugins/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:**
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
|
||||
```bash
|
||||
# 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`
|
||||
|
||||
```bash
|
||||
#!/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:**
|
||||
1. Only run cleanup after significant operations (not every edit)
|
||||
2. Use more targeted paths
|
||||
3. Add cooldown period (don't run if ran in last 5 minutes)
|
||||
|
||||
```bash
|
||||
# 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)
|
||||
1. Remove viz-platform SessionStart hook
|
||||
2. Make clarity-assist opt-in
|
||||
3. Add early exit to git-flow hooks
|
||||
|
||||
### Phase 2: Optimizations (Medium effort)
|
||||
4. Add path filtering to PostToolUse hooks
|
||||
5. Add cooldown to project-hygiene
|
||||
6. Create shared hook library
|
||||
|
||||
### Phase 3: Architecture (Higher effort)
|
||||
7. Consolidate SessionStart into dispatcher
|
||||
8. 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
|
||||
|
||||
1. Should we support a `hooks.json` path matcher natively? (Would require Claude Code changes)
|
||||
2. Should SessionStart hooks be lazy-loaded on first plugin command instead?
|
||||
3. 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 |
|
||||
Reference in New Issue
Block a user