Skip to main content

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

EventDescriptionAction
pre-commitBefore git commitScan staged files for duplicates
file-createNew file createdCheck against existing files
file-renameFile renamedCheck for naming conflicts
manualUser invokes hookFull 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

# 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

OptionDefaultDescription
threshold0.85Similarity threshold (0-1) to trigger warning
patterns*.mdFile patterns to check
exit_on_matchfalseWhether to block commit on match
scan_depthunlimitedDirectory 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"
]
}
}
}
  • 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