#!/usr/bin/env python3 """Suggest pouch skills from well-known files in a project root.""" from __future__ import annotations import argparse import sys from pathlib import Path INIT = { "ack": "说「/ack 初始化」或 `pouch init ack`;不要静默初始化", "builder": "说「用 builder 初始化」;不要 `pouch init builder`", "deployer": "说「用 deployer 初始化」;不要 `pouch init deployer`", } FILE_SIGNALS: dict[str, tuple[str, ...]] = { "ack": (".pouch/ack",), "builder": ( "makefile.builder", "debian", "Dockerfile", "Containerfile", ".env.builder", ), "deployer": ( ".pouch/deployer", "compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml", "argocd.yaml", "k8s", "helm", ), "releaser": ("CHANGELOG.md", "CHANGELOG", "VERSION"), "declarative-openspec-loop": ("openspec", ".openspec"), "discussion-notes": ("000inbox",), } INITED_MARKERS = { "ack": (".pouch/ack",), "builder": ("makefile.builder",), "deployer": (".pouch/deployer",), } def project_root(start: Path) -> Path: cur = start.resolve() if cur.is_file(): cur = cur.parent for candidate in (cur, *cur.parents): if (candidate / ".git").exists(): return candidate return cur def is_pouch_repo(root: Path) -> bool: return ( (root / "install.sh").is_file() and (root / "bin" / "pouch").is_file() and (root / "skills" / "pouch" / "SKILL.md").is_file() ) def existing(root: Path, rel: str) -> bool: return (root / rel).exists() def hits(root: Path, names: tuple[str, ...]) -> list[str]: return [name for name in names if existing(root, name)] def suggest(root: Path) -> dict[str, object]: suggested: list[tuple[str, list[str]]] = [] already: list[tuple[str, list[str]]] = [] init: list[tuple[str, str]] = [] for name, names in FILE_SIGNALS.items(): found = hits(root, names) if not found: continue inited = hits(root, INITED_MARKERS.get(name, ())) if inited: already.append((name, inited)) else: suggested.append((name, found)) if name in INIT and not inited: init.append((name, INIT[name])) return { "root": str(root), "this_is_pouch_repo": is_pouch_repo(root), "suggested": suggested, "already": already, "init": init, } def format_report(data: dict[str, object]) -> str: lines = [f"project: {data['root']}"] if data["this_is_pouch_repo"]: lines.append("this_is_pouch_repo: true") lines.append("不要按业务项目接入。维护 skill 走 pouch 的创建或回流模式。") return "\n".join(lines) + "\n" lines.append("this_is_pouch_repo: false") suggested = data["suggested"] already = data["already"] init = data["init"] if suggested: lines.append("suggested:") for name, why in suggested: lines.append(f" {name}\twhy: {', '.join(why)}") else: lines.append("suggested: (none)") if already: lines.append("already:") for name, why in already: lines.append(f" {name}\t{', '.join(why)}") else: lines.append("already: (none)") if init: lines.append("init:") for name, how in init: lines.append(f" {name}\t{how}") else: lines.append("init: (none)") return "\n".join(lines) + "\n" def main() -> int: parser = argparse.ArgumentParser(description="根据仓库信号建议 pouch skill") parser.add_argument( "path", nargs="?", default=".", help="项目目录(默认 cwd;向上找 .git)", ) args = parser.parse_args() root = project_root(Path(args.path)) sys.stdout.write(format_report(suggest(root))) return 0 if __name__ == "__main__": raise SystemExit(main())