145 lines
4.4 KiB
Python
Executable File
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()
|