Files

89 lines
3.0 KiB
Python

"""
Version Manager — archives and rolls back teacher skill versions.
Adapted from colleague-skill's version_manager.py.
"""
import json
import os
import shutil
from datetime import datetime
MAX_VERSIONS = 10
def list_versions(teacher_dir: str) -> list:
"""List all archived versions of a teacher skill."""
versions_dir = os.path.join(teacher_dir, "versions")
if not os.path.exists(versions_dir):
return []
versions = []
for entry in sorted(os.listdir(versions_dir)):
entry_path = os.path.join(versions_dir, entry)
if os.path.isdir(entry_path) and entry.startswith("v"):
files = os.listdir(entry_path)
mtime = os.path.getmtime(entry_path)
versions.append({
"version": entry[1:],
"archived_at": datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M"),
"files": files,
})
return versions
def rollback(teacher_dir: str, target_version: str) -> bool:
"""Roll back a teacher skill to a previous version. Backs up current state first."""
target_dir = os.path.join(teacher_dir, "versions", f"v{target_version}")
if not os.path.isdir(target_dir):
return False
meta_path = os.path.join(teacher_dir, "meta.json")
current_version = "unknown"
if os.path.exists(meta_path):
with open(meta_path, "r", encoding="utf-8") as f:
meta = json.load(f)
current_version = meta.get("version", "unknown")
backup_dir = os.path.join(teacher_dir, "versions", f"v{current_version}_before_rollback")
os.makedirs(backup_dir, exist_ok=True)
for fname in ["SKILL.md", "teaching.md", "voice.md", "meta.json"]:
src = os.path.join(teacher_dir, fname)
if os.path.exists(src):
shutil.copy2(src, backup_dir)
for fname in ["SKILL.md", "teaching.md", "voice.md", "meta.json"]:
src = os.path.join(target_dir, fname)
if os.path.exists(src):
shutil.copy2(src, os.path.join(teacher_dir, fname))
if os.path.exists(meta_path):
with open(meta_path, "r", encoding="utf-8") as f:
meta = json.load(f)
meta["rollback_from"] = current_version
meta["updated_at"] = datetime.now().strftime("%Y-%m-%d")
with open(meta_path, "w", encoding="utf-8") as f:
json.dump(meta, f, ensure_ascii=False, indent=2)
return True
def cleanup_old_versions(teacher_dir: str) -> int:
"""Remove old versions beyond MAX_VERSIONS limit."""
versions_dir = os.path.join(teacher_dir, "versions")
if not os.path.exists(versions_dir):
return 0
entries = []
for entry in os.listdir(versions_dir):
entry_path = os.path.join(versions_dir, entry)
if os.path.isdir(entry_path):
entries.append((entry_path, os.path.getmtime(entry_path)))
entries.sort(key=lambda x: x[1], reverse=True)
removed = 0
for path, _ in entries[MAX_VERSIONS:]:
shutil.rmtree(path)
removed += 1
return removed