Files
.pouch/skiff/symlinks.py
T

77 lines
2.2 KiB
Python

"""软链创建、移除与健康检查。"""
from __future__ import annotations
import os
import shutil
from dataclasses import dataclass
from pathlib import Path
@dataclass
class LinkStatus:
link: Path
expected: Path
ok: bool
issue: str | None = None
def create_link(link: Path, target: Path) -> None:
target = target.resolve()
if not target.exists():
raise FileNotFoundError(f"目标不存在: {target}")
link.parent.mkdir(parents=True, exist_ok=True)
if link.is_symlink():
current = link.resolve()
if current == target:
return
link.unlink()
elif link.exists():
raise FileExistsError(f"已存在非软链路径: {link}")
link.symlink_to(target, target_is_directory=target.is_dir())
def remove_link(link: Path) -> bool:
if link.is_symlink():
link.unlink()
return True
if link.exists():
raise FileExistsError(f"不是软链,未删除: {link}")
return False
def check_link(link: Path, expected: Path) -> LinkStatus:
expected = expected.resolve()
if not link.exists() and not link.is_symlink():
return LinkStatus(link, expected, False, "缺失")
if not link.is_symlink():
return LinkStatus(link, expected, False, "非软链(可能被 Agent 替换为普通目录)")
actual = link.resolve()
if actual != expected:
return LinkStatus(link, expected, False, f"指向错误: {actual}")
if not actual.exists():
return LinkStatus(link, expected, False, "目标不存在")
return LinkStatus(link, expected, True)
def copy_template(src: Path, dst: Path) -> None:
if dst.exists():
raise FileExistsError(f"已存在: {dst}")
shutil.copytree(src, dst)
def find_repo_root(start: Path | None = None) -> Path | None:
start = (start or Path.cwd()).resolve()
for directory in [start, *start.parents]:
if (directory / ".skills.yaml").is_file():
return directory
if (directory / "skills").is_dir() and (
(directory / "catalog.yaml").is_file()
or (directory / "registry.yaml").is_file()
):
return directory
return None