TradingAgents/.claude/hooks/auto_sync_dev.py

145 lines
4.4 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Auto-sync hook for plugin development.
Automatically syncs local plugin changes to installed location before commits.
This prevents the "two-location hell" where developers edit one location but
Claude Code reads from another.
Exit codes:
0: Allow commit, no message (sync successful or not needed)
1: Allow commit, show warning (sync recommended)
2: Block commit, show error (sync failed, must fix)
"""
import json
import subprocess
import sys
from pathlib import Path
def is_plugin_development_mode():
"""Check if we're developing the autonomous-dev plugin itself."""
# Check if we're in the plugins/autonomous-dev directory structure
cwd = Path.cwd()
# Look for plugin.json in .claude-plugin/ subdirectory
plugin_json = cwd / "plugins" / "autonomous-dev" / ".claude-plugin" / "plugin.json"
return plugin_json.exists()
def is_plugin_installed():
"""Check if the plugin is installed in Claude Code."""
home = Path.home()
installed_plugins_file = home / ".claude" / "plugins" / "installed_plugins.json"
if not installed_plugins_file.exists():
return False
try:
with open(installed_plugins_file) as f:
config = json.load(f)
# Look for autonomous-dev plugin
for plugin_key in config.get("plugins", {}).keys():
if plugin_key.startswith("autonomous-dev@"):
return True
except (json.JSONDecodeError, PermissionError, FileNotFoundError):
return False
return False
def get_modified_plugin_files():
"""Get list of modified files in plugins/autonomous-dev/."""
try:
result = subprocess.run(
["git", "diff", "--cached", "--name-only", "--", "plugins/autonomous-dev/"],
capture_output=True,
text=True,
check=True
)
files = [f for f in result.stdout.strip().split('\n') if f]
# Filter to files that matter (not tests, not docs/dev)
relevant_files = []
for f in files:
if any(x in f for x in ["agents/", "commands/", "hooks/", "lib/"]):
relevant_files.append(f)
return relevant_files
except subprocess.CalledProcessError:
return []
def auto_sync():
"""Automatically sync changes to installed plugin."""
sync_script = Path("plugins/autonomous-dev/hooks/sync_to_installed.py")
if not sync_script.exists():
return False, "Sync script not found"
try:
result = subprocess.run(
["python3", str(sync_script)],
capture_output=True,
text=True,
check=True,
timeout=10
)
return True, result.stdout
except subprocess.CalledProcessError as e:
return False, f"Sync failed: {e.stderr}"
except subprocess.TimeoutExpired:
return False, "Sync timed out"
except Exception as e:
return False, f"Sync error: {str(e)}"
def main():
"""Main hook logic."""
# Only run for plugin development
if not is_plugin_development_mode():
sys.exit(0) # Not plugin dev, allow commit
# Check if plugin is installed
if not is_plugin_installed():
# Plugin not installed, no need to sync
sys.exit(0)
# Check if we're modifying plugin files
modified_files = get_modified_plugin_files()
if not modified_files:
# No plugin files modified, allow commit
sys.exit(0)
# Relevant plugin files modified and plugin installed - auto-sync
print("🔄 Auto-syncing plugin changes to installed location...", file=sys.stderr)
print(f" Modified files: {len(modified_files)}", file=sys.stderr)
print("", file=sys.stderr)
success, message = auto_sync()
if success:
print("✅ Plugin changes synced to installed location", file=sys.stderr)
print("⚠️ RESTART REQUIRED: Quit and restart Claude Code to see changes", file=sys.stderr)
print("", file=sys.stderr)
sys.exit(0) # Allow commit
else:
print("❌ Auto-sync failed!", file=sys.stderr)
print(file=sys.stderr)
print(message, file=sys.stderr)
print(file=sys.stderr)
print("Options:", file=sys.stderr)
print(" 1. Run manually: python plugins/autonomous-dev/hooks/sync_to_installed.py", file=sys.stderr)
print(" 2. Skip sync: git commit --no-verify", file=sys.stderr)
sys.exit(2) # Block commit
if __name__ == "__main__":
main()