Files
skill-seekers-reference/src/skill_seekers/cli/sync_cli.py
yusyus 0265de5816 style: Format all Python files with ruff
- Formatted 103 files to comply with ruff format requirements
- No code logic changes, only formatting/whitespace
- Fixes CI formatting check failures
2026-02-08 14:42:27 +03:00

208 lines
6.5 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Documentation sync CLI.
Monitor documentation for changes and automatically update skills.
"""
import sys
import argparse
import signal
from pathlib import Path
from ..sync import SyncMonitor
def handle_signal(_signum, _frame):
"""Handle interrupt signals."""
print("\n🛑 Stopping sync monitor...")
sys.exit(0)
def start_command(args):
"""Start monitoring."""
monitor = SyncMonitor(
config_path=args.config, check_interval=args.interval, auto_update=args.auto_update
)
# Register signal handlers
signal.signal(signal.SIGINT, handle_signal)
signal.signal(signal.SIGTERM, handle_signal)
try:
monitor.start()
print(f"\n📊 Monitoring {args.config}")
print(f" Check interval: {args.interval}s ({args.interval // 60}m)")
print(f" Auto-update: {'✅ enabled' if args.auto_update else '❌ disabled'}")
print("\nPress Ctrl+C to stop\n")
# Keep running
while True:
import time
time.sleep(1)
except KeyboardInterrupt:
print("\n🛑 Stopping...")
monitor.stop()
def check_command(args):
"""Check for changes once."""
monitor = SyncMonitor(
config_path=args.config,
check_interval=3600, # Not used for single check
)
print(f"🔍 Checking {args.config} for changes...")
report = monitor.check_now(generate_diffs=args.diff)
print(f"\n📊 Results:")
print(f" Total pages: {report.total_pages}")
print(f" Added: {len(report.added)}")
print(f" Modified: {len(report.modified)}")
print(f" Deleted: {len(report.deleted)}")
print(f" Unchanged: {report.unchanged}")
if report.has_changes:
print(f"\n✨ Detected {report.change_count} changes!")
if args.verbose:
if report.added:
print("\n✅ Added pages:")
for change in report.added:
print(f"{change.url}")
if report.modified:
print("\n✏️ Modified pages:")
for change in report.modified:
print(f"{change.url}")
if change.diff and args.diff:
print(f" Diff preview (first 5 lines):")
for line in change.diff.split("\n")[:5]:
print(f" {line}")
if report.deleted:
print("\n❌ Deleted pages:")
for change in report.deleted:
print(f"{change.url}")
else:
print("\n✅ No changes detected")
def stats_command(args):
"""Show monitoring statistics."""
monitor = SyncMonitor(config_path=args.config, check_interval=3600)
stats = monitor.stats()
print(f"\n📊 Statistics for {stats['skill_name']}:")
print(f" Status: {stats['status']}")
print(f" Last check: {stats['last_check'] or 'Never'}")
print(f" Last change: {stats['last_change'] or 'Never'}")
print(f" Total checks: {stats['total_checks']}")
print(f" Total changes: {stats['total_changes']}")
print(f" Tracked pages: {stats['tracked_pages']}")
print(f" Running: {'✅ Yes' if stats['running'] else '❌ No'}")
def reset_command(args):
"""Reset monitoring state."""
state_file = Path(f"{args.skill_name}_sync.json")
if state_file.exists():
if args.force or input(f"⚠️ Reset state for {args.skill_name}? [y/N]: ").lower() == "y":
state_file.unlink()
print(f"✅ State reset for {args.skill_name}")
else:
print("❌ Reset cancelled")
else:
print(f" No state file found for {args.skill_name}")
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Monitor documentation for changes and update skills",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Start monitoring (checks every hour)
skill-seekers-sync start --config configs/react.json
# Start with custom interval (10 minutes)
skill-seekers-sync start --config configs/react.json --interval 600
# Start with auto-update
skill-seekers-sync start --config configs/react.json --auto-update
# Check once (no continuous monitoring)
skill-seekers-sync check --config configs/react.json
# Check with diffs
skill-seekers-sync check --config configs/react.json --diff -v
# Show statistics
skill-seekers-sync stats --config configs/react.json
# Reset state
skill-seekers-sync reset --skill-name react
""",
)
subparsers = parser.add_subparsers(dest="command", help="Command to execute")
# Start command
start_parser = subparsers.add_parser("start", help="Start continuous monitoring")
start_parser.add_argument("--config", required=True, help="Path to skill config file")
start_parser.add_argument(
"--interval",
"-i",
type=int,
default=3600,
help="Check interval in seconds (default: 3600 = 1 hour)",
)
start_parser.add_argument(
"--auto-update", action="store_true", help="Automatically rebuild skill on changes"
)
# Check command
check_parser = subparsers.add_parser("check", help="Check for changes once")
check_parser.add_argument("--config", required=True, help="Path to skill config file")
check_parser.add_argument("--diff", "-d", action="store_true", help="Generate content diffs")
check_parser.add_argument("--verbose", "-v", action="store_true", help="Show detailed output")
# Stats command
stats_parser = subparsers.add_parser("stats", help="Show monitoring statistics")
stats_parser.add_argument("--config", required=True, help="Path to skill config file")
# Reset command
reset_parser = subparsers.add_parser("reset", help="Reset monitoring state")
reset_parser.add_argument("--skill-name", required=True, help="Skill name")
reset_parser.add_argument("--force", "-f", action="store_true", help="Skip confirmation")
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
try:
if args.command == "start":
start_command(args)
elif args.command == "check":
check_command(args)
elif args.command == "stats":
stats_command(args)
elif args.command == "reset":
reset_command(args)
except Exception as e:
print(f"\n❌ Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()