♻️ refactor(skills): update playbook.py and tests for thirdparty/ layout
- install_skills_action: collect skills from both codex/skills/ (own) and codex/skills/thirdparty/ (thirdparty); mode=all installs both, mode=list searches both; log [thirdparty] tag for third-party installs - Update test_thirdparty_skills_pipeline: fix source_list path assertion - Update SKILLS.md: document thirdparty/ subdirectory structure Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b3df41205d
commit
f94dba0348
|
|
@ -28,11 +28,15 @@ skills = true
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
codex/skills/
|
codex/skills/
|
||||||
<skill-name>/
|
<skill-name>/ # 本仓库自维护
|
||||||
SKILL.md
|
SKILL.md
|
||||||
references/ # 可选:拆分参考文档
|
references/ # 可选:拆分参考文档
|
||||||
templates/ # 可选:模板
|
templates/ # 可选:模板
|
||||||
scripts/ # 可选:脚本/命令封装
|
scripts/ # 可选:脚本/命令封装
|
||||||
|
thirdparty/ # 第三方同步(不可手动修改)
|
||||||
|
<skill-name>/
|
||||||
|
SKILL.md
|
||||||
|
.sources/ # 第三方来源清单
|
||||||
```
|
```
|
||||||
|
|
||||||
最终安装到本机后,对应路径为:
|
最终安装到本机后,对应路径为:
|
||||||
|
|
@ -167,7 +171,7 @@ python <deploy_root>/scripts/playbook.py -config playbook.toml
|
||||||
|
|
||||||
## 9. Third-party Skills
|
## 9. Third-party Skills
|
||||||
|
|
||||||
来源:`codex/skills/.sources/`(第三方来源清单目录)。
|
来源:`codex/skills/thirdparty/.sources/`(第三方来源清单目录)。
|
||||||
|
|
||||||
- `superpowers.list`
|
- `superpowers.list`
|
||||||
- `ui-ux-pro-max.list`
|
- `ui-ux-pro-max.list`
|
||||||
|
|
|
||||||
|
|
@ -1141,6 +1141,7 @@ def install_skills_action(config: dict, context: dict) -> int:
|
||||||
agents_home = (context["project_root"] / agents_home).resolve()
|
agents_home = (context["project_root"] / agents_home).resolve()
|
||||||
|
|
||||||
skills_src_root = PLAYBOOK_ROOT / "codex/skills"
|
skills_src_root = PLAYBOOK_ROOT / "codex/skills"
|
||||||
|
skills_thirdparty_root = skills_src_root / "thirdparty"
|
||||||
if not skills_src_root.is_dir():
|
if not skills_src_root.is_dir():
|
||||||
print(f"ERROR: skills source not found: {skills_src_root}", file=sys.stderr)
|
print(f"ERROR: skills source not found: {skills_src_root}", file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
|
|
@ -1149,28 +1150,40 @@ def install_skills_action(config: dict, context: dict) -> int:
|
||||||
ensure_dir(skills_dst_root)
|
ensure_dir(skills_dst_root)
|
||||||
|
|
||||||
if mode == "all":
|
if mode == "all":
|
||||||
skills = [
|
own_skills = [
|
||||||
path.name
|
(path.name, skills_src_root, "own")
|
||||||
for path in skills_src_root.iterdir()
|
for path in skills_src_root.iterdir()
|
||||||
if path.is_dir() and not path.name.startswith(".")
|
if path.is_dir() and not path.name.startswith(".") and path.name != "thirdparty"
|
||||||
]
|
]
|
||||||
|
third_skills = [
|
||||||
|
(path.name, skills_thirdparty_root, "thirdparty")
|
||||||
|
for path in skills_thirdparty_root.iterdir()
|
||||||
|
if path.is_dir() and not path.name.startswith(".")
|
||||||
|
] if skills_thirdparty_root.is_dir() else []
|
||||||
|
skill_entries = own_skills + third_skills
|
||||||
elif mode == "list":
|
elif mode == "list":
|
||||||
try:
|
try:
|
||||||
skills = normalize_names(config.get("skills"), "skills")
|
names = normalize_names(config.get("skills"), "skills")
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
print(f"ERROR: {exc}", file=sys.stderr)
|
print(f"ERROR: {exc}", file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
|
skill_entries = []
|
||||||
|
for name in names:
|
||||||
|
if (skills_src_root / name).is_dir():
|
||||||
|
skill_entries.append((name, skills_src_root, "own"))
|
||||||
|
elif skills_thirdparty_root.is_dir() and (skills_thirdparty_root / name).is_dir():
|
||||||
|
skill_entries.append((name, skills_thirdparty_root, "thirdparty"))
|
||||||
|
else:
|
||||||
|
print(f"ERROR: skill not found: {name}", file=sys.stderr)
|
||||||
|
return 2
|
||||||
else:
|
else:
|
||||||
print("ERROR: mode must be list or all", file=sys.stderr)
|
print("ERROR: mode must be list or all", file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
no_backup = bool(config.get("no_backup", False))
|
no_backup = bool(config.get("no_backup", False))
|
||||||
for name in skills:
|
for name, src_root, origin in skill_entries:
|
||||||
src = skills_src_root / name
|
src = src_root / name
|
||||||
if not src.is_dir():
|
|
||||||
print(f"ERROR: skill not found: {name}", file=sys.stderr)
|
|
||||||
return 2
|
|
||||||
dst = skills_dst_root / name
|
dst = skills_dst_root / name
|
||||||
if dst.exists():
|
if dst.exists():
|
||||||
if no_backup:
|
if no_backup:
|
||||||
|
|
@ -1181,7 +1194,8 @@ def install_skills_action(config: dict, context: dict) -> int:
|
||||||
log(f"Backed up existing skill: {name} -> {backup.name}")
|
log(f"Backed up existing skill: {name} -> {backup.name}")
|
||||||
copytree(src, dst)
|
copytree(src, dst)
|
||||||
rewrite_skill_docs_links(dst, resolve_docs_prefix(context))
|
rewrite_skill_docs_links(dst, resolve_docs_prefix(context))
|
||||||
log(f"Installed: {name}")
|
tag = " [thirdparty]" if origin == "thirdparty" else ""
|
||||||
|
log(f"Installed: {name}{tag}")
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
|
||||||
self.assertEqual(karpathy["snapshot_dir"], "andrej-karpathy-skills")
|
self.assertEqual(karpathy["snapshot_dir"], "andrej-karpathy-skills")
|
||||||
self.assertEqual(karpathy["skills_subdir"], "skills")
|
self.assertEqual(karpathy["skills_subdir"], "skills")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
karpathy["source_list"], "codex/skills/.sources/andrej-karpathy-skills.list"
|
karpathy["source_list"], "codex/skills/thirdparty/.sources/andrej-karpathy-skills.list"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_ui_ux_pro_max_uses_render_codex_skill_sync_mode(self):
|
def test_ui_ux_pro_max_uses_render_codex_skill_sync_mode(self):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue