d791f5584c
- skills/manage-release -> skills/releaser(git mv,内容不变) - releaser 的 frontmatter/标题改名,description 保留旧名作为触发词 - skills/manage-release/ 保留为纯弃用占位(SKILL.md + README.md,指向 releaser), 已装软链与旧项目 .pouch.yaml 不再悬空或失效 - 同步或引用:AGENTS.md、README.md、docs/getting-started.md、orc(routing/SKILL/README)、 ack delivery、builder(SKILL/README/contract)、memory-distill、pouch onboard 与 suggest_skills - 测试:catalog 需列出旧名占位,新增弃用占位断言,orc children 纳入旧名占位
145 lines
4.0 KiB
Python
145 lines
4.0 KiB
Python
#!/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())
|