Smart Merge Hook
Name: smart-merge Version: 1.0.0 Category: Documentation Status: Active Trigger: Pre-commit, File Watch
Overview
Event-driven hook that detects similar files and potential duplicates during development workflows. Can be triggered on pre-commit or as a file watcher to prevent duplicate documentation from being created.
Hook Configuration
Pre-commit Hook
Add to .claude/hooks.json:
{
"hooks": [
{
"name": "smart-merge-check",
"type": "pre-commit",
"description": "Detect similar files before commit",
"command": "python3 scripts/smart-merge.py find . --pattern '*.md' --threshold 0.85 --json",
"on_match": "warn",
"exit_on_match": false,
"message_template": "Found {count} similar file pairs. Consider merging:\n{details}"
}
]
}
File Watch Hook
For real-time detection during development:
{
"hooks": [
{
"name": "smart-merge-watch",
"type": "file-watch",
"description": "Detect when similar file is being created",
"watch_patterns": ["**/*.md", "**/*.yaml", "**/*.json"],
"events": ["create", "rename"],
"command": "python3 scripts/smart-merge.py analyze {new_file} {existing_match}",
"threshold": 0.7,
"action": "prompt"
}
]
}
Trigger Events
| Event | Description | Action |
|---|---|---|
pre-commit | Before git commit | Scan staged files for duplicates |
file-create | New file created | Check against existing files |
file-rename | File renamed | Check for naming conflicts |
manual | User invokes hook | Full directory scan |
Hook Script
Create scripts/hooks/smart-merge-hook.py:
#!/usr/bin/env python3
"""
Smart Merge Hook - Detect similar files during git operations.
Usage:
python3 scripts/hooks/smart-merge-hook.py pre-commit
python3 scripts/hooks/smart-merge-hook.py watch FILE
python3 scripts/hooks/smart-merge-hook.py scan [DIRECTORY]
"""
import json
import subprocess
import sys
from pathlib import Path
def get_staged_files():
"""Get list of staged markdown files."""
result = subprocess.run(
['git', 'diff', '--cached', '--name-only', '--diff-filter=ACM'],
capture_output=True, text=True
)
files = [f for f in result.stdout.strip().split('\n') if f.endswith('.md')]
return files
def check_similarity(file_a: str, file_b: str, threshold: float = 0.85) -> dict:
"""Check similarity between two files."""
result = subprocess.run(
['python3', 'scripts/smart-merge.py', 'analyze', file_a, file_b, '--json'],
capture_output=True, text=True
)
if result.returncode == 0:
return json.loads(result.stdout)
return None
def find_similar_in_repo(pattern: str = '*.md', threshold: float = 0.85) -> list:
"""Find similar files in repository."""
result = subprocess.run(
['python3', 'scripts/smart-merge.py', 'find', '.',
'--pattern', pattern, '--threshold', str(threshold), '--json'],
capture_output=True, text=True
)
if result.returncode == 0:
return json.loads(result.stdout)
return []
def pre_commit_hook():
"""Pre-commit hook to detect similar files."""
staged = get_staged_files()
if not staged:
return 0
similar = find_similar_in_repo(threshold=0.85)
# Check if any staged files are in similar pairs
warnings = []
for pair in similar:
if pair['file_a'] in staged or pair['file_b'] in staged:
warnings.append(pair)
if warnings:
print("\n⚠️ SMART MERGE WARNING: Similar files detected\n")
for w in warnings:
print(f" {w['similarity']:.1f}% similar:")
print(f" {w['file_a']}")
print(f" {w['file_b']}")
print()
print("Consider merging these files with:")
print(" /smart-merge --analyze FILE_A FILE_B")
print()
# Return warning but don't block commit
return 0
return 0
def watch_file(new_file: str):
"""Check if newly created file is similar to existing files."""
# Find existing files with same name pattern
new_path = Path(new_file)
existing = list(Path('.').rglob(f'*{new_path.stem}*{new_path.suffix}'))
existing = [f for f in existing if str(f) != new_file]
for existing_file in existing:
result = check_similarity(new_file, str(existing_file))
if result and result.get('similarity_score', 0) > 70:
print(f"\n⚠️ Similar file detected:")
print(f" New file: {new_file}")
print(f" Existing: {existing_file}")
print(f" Similarity: {result['similarity_score']:.1f}%")
print(f" Recommendation: {result.get('recommendation', 'Review needed')}")
print()
def main():
if len(sys.argv) < 2:
print("Usage: smart-merge-hook.py {pre-commit|watch FILE|scan [DIR]}")
sys.exit(1)
command = sys.argv[1]
if command == 'pre-commit':
sys.exit(pre_commit_hook())
elif command == 'watch' and len(sys.argv) > 2:
watch_file(sys.argv[2])
elif command == 'scan':
directory = sys.argv[2] if len(sys.argv) > 2 else '.'
similar = find_similar_in_repo()
if similar:
print(f"Found {len(similar)} similar file pairs")
for pair in similar:
print(f" {pair['similarity']:.1f}%: {pair['file_a']} <-> {pair['file_b']}")
else:
print("No similar files found")
else:
print(f"Unknown command: {command}")
sys.exit(1)
if __name__ == '__main__':
main()
Git Hook Installation
Method 1: Symlink
# Create hooks directory if needed
mkdir -p .git/hooks
# Create pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
python3 scripts/hooks/smart-merge-hook.py pre-commit
EOF
chmod +x .git/hooks/pre-commit
Method 2: Husky (Node.js projects)
// package.json
{
"husky": {
"hooks": {
"pre-commit": "python3 scripts/hooks/smart-merge-hook.py pre-commit"
}
}
}
Method 3: Pre-commit Framework
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: smart-merge-check
name: Smart Merge Check
entry: python3 scripts/hooks/smart-merge-hook.py pre-commit
language: system
pass_filenames: false
types: [markdown]
Configuration Options
| Option | Default | Description |
|---|---|---|
threshold | 0.85 | Similarity threshold (0-1) to trigger warning |
patterns | *.md | File patterns to check |
exit_on_match | false | Whether to block commit on match |
scan_depth | unlimited | Directory depth to scan |
ignore_paths | [] | Paths to exclude from scanning |
Hook Behavior
Pre-commit Flow
git commit
│
▼
┌─────────────────────────────┐
│ smart-merge-hook pre-commit │
│ - Get staged .md files │
│ - Scan repo for similar │
│ - Check if staged in pairs │
└─────────────────────────────┘
│
├─► No matches → Commit proceeds
│
└─► Matches found → Warning displayed
│
└─► Commit proceeds (non-blocking)
Watch Flow
File created/renamed
│
▼
┌─────────────────────────────┐
│ smart-merge-hook watch FILE │
│ - Find files with same name │
│ - Check similarity scores │
│ - Display recommendations │
└─────────────────────────────┘
│
└─► User decides to merge or keep separate
Integration with Claude Code
The hook can be triggered automatically via Claude Code hooks system:
// .claude/settings.local.json
{
"hooks": {
"pre_commit": {
"enabled": true,
"commands": [
"python3 scripts/hooks/smart-merge-hook.py pre-commit"
]
}
}
}
Related Components
- Script:
scripts/smart-merge.py - Agent:
agents/document-merger.md - Skill:
skills/smart-merge/SKILL.md - Command:
commands/smart-merge.md
Author: CODITECT Core Team Created: 2025-12-11