f3cd56b78e
Use ~/.pouch, the pouch CLI, and .pouch.yaml as the SSOT container. Keep the inner skills/ packages, and store ACK project state in .pouch/ack instead of docs/ack.
104 lines
3.1 KiB
Python
104 lines
3.1 KiB
Python
"""路径与 Agent 目标定义。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
|
||
HOME = Path.home()
|
||
PREFERRED_POUCH_HOME = HOME / ".pouch"
|
||
LEGACY_POUCH_HOME = HOME / ".skills"
|
||
PROJECT_MANIFEST = ".pouch.yaml"
|
||
LEGACY_PROJECT_MANIFEST = ".skills.yaml"
|
||
|
||
|
||
def _first_existing_dir(*candidates: Path, default: Path) -> Path:
|
||
for path in candidates:
|
||
if path.is_dir():
|
||
return path
|
||
return default
|
||
|
||
|
||
def _first_existing_file(*candidates: Path, default: Path) -> Path:
|
||
for path in candidates:
|
||
if path.is_file():
|
||
return path
|
||
return default
|
||
|
||
|
||
POUCH_HOME = _first_existing_dir(
|
||
PREFERRED_POUCH_HOME,
|
||
LEGACY_POUCH_HOME,
|
||
default=PREFERRED_POUCH_HOME,
|
||
)
|
||
SKILLS_DIR = POUCH_HOME / "skills"
|
||
TEMPLATE_DIR = SKILLS_DIR / "_template"
|
||
DRAFTS_DIR = POUCH_HOME / ".drafts"
|
||
CATALOG_FILE = POUCH_HOME / "catalog.yaml"
|
||
LEGACY_REGISTRY_FILE = POUCH_HOME / "registry.yaml"
|
||
CATALOG_CACHE_DIR = _first_existing_dir(
|
||
HOME / ".local" / "share" / "pouch" / "externals",
|
||
HOME / ".local" / "share" / "skills" / "externals",
|
||
default=HOME / ".local" / "share" / "pouch" / "externals",
|
||
)
|
||
CONFIG_FILE = _first_existing_file(
|
||
HOME / ".config" / "pouch" / "config.yaml",
|
||
HOME / ".config" / "skiff" / "config.yaml",
|
||
default=HOME / ".config" / "pouch" / "config.yaml",
|
||
)
|
||
SOURCES_DIR = _first_existing_dir(
|
||
HOME / ".local" / "share" / "pouch" / "sources",
|
||
HOME / ".local" / "share" / "skiff" / "sources",
|
||
default=HOME / ".local" / "share" / "pouch" / "sources",
|
||
)
|
||
|
||
AGENT_GLOBAL: dict[str, Path] = {
|
||
"cursor": HOME / ".cursor" / "skills",
|
||
"claude": HOME / ".claude" / "skills",
|
||
"codex": HOME / ".codex" / "skills",
|
||
# agents 标准目录:OMP 原生 canonical(agents provider),cursor/codex 项目级同路径
|
||
"agents": HOME / ".agents" / "skills",
|
||
}
|
||
|
||
AGENT_PROJECT: dict[str, str] = {
|
||
"cursor": ".agents/skills",
|
||
"claude": ".claude/skills",
|
||
"codex": ".agents/skills",
|
||
# 与 cursor/codex 共用 .agents/skills;软链幂等,同路径只写一次
|
||
"agents": ".agents/skills",
|
||
}
|
||
|
||
ALL_TARGETS = ("cursor", "claude", "codex", "agents")
|
||
|
||
|
||
def project_manifest(root: Path) -> Path:
|
||
"""项目清单路径:优先 .pouch.yaml,否则沿用 .skills.yaml。"""
|
||
preferred = root / PROJECT_MANIFEST
|
||
legacy = root / LEGACY_PROJECT_MANIFEST
|
||
if preferred.is_file():
|
||
return preferred
|
||
if legacy.is_file():
|
||
return legacy
|
||
return preferred
|
||
|
||
|
||
def resolve_targets(target: str | None) -> list[str]:
|
||
if target is None or target == "all":
|
||
return list(ALL_TARGETS)
|
||
if target not in ALL_TARGETS:
|
||
raise ValueError(f"未知 target: {target!r},可选: {', '.join(ALL_TARGETS)}, all")
|
||
return [target]
|
||
|
||
|
||
def agent_skill_dir(target: str, *, project_root: Path | None = None) -> Path:
|
||
if project_root is None:
|
||
return AGENT_GLOBAL[target]
|
||
return project_root / AGENT_PROJECT[target]
|
||
|
||
|
||
def ensure_pouch_home() -> None:
|
||
if not POUCH_HOME.is_dir():
|
||
raise SystemExit(
|
||
"~/.pouch 不存在。请将本仓库克隆到 ~/.pouch。"
|
||
"若仍是 ~/.skills,先执行: mv ~/.skills ~/.pouch"
|
||
)
|