🔧 feat(skills): install to agents home

This commit is contained in:
csh 2026-02-05 15:26:48 +08:00
parent 8b75747587
commit 872c1afc24
5 changed files with 41 additions and 16 deletions

View File

@ -4,8 +4,8 @@
并给出与本 Playbook`docs/` + `rulesets/`)配套的技能编写建议与内置技能清单。
> 提示Codex skills 是“按用户安装”的(默认在
> `~/.codex/skills`)。本仓库将 skills 以可分发的形式放在
> `codex/skills/`,并提供脚本一键安装到你的 `CODEX_HOME`。
> `~/.agents/skills`)。本仓库将 skills 以可分发的形式放在
> `codex/skills/`,并提供脚本一键安装到你的 `~/.agents`。
---
@ -38,14 +38,14 @@ codex/skills/
最终安装到本机后,对应路径为:
```txt
$CODEX_HOME/skills/<skill-name>/SKILL.md
~/.agents/skills/<skill-name>/SKILL.md
```
---
## 3. 安装到本机(推荐)
使用统一入口 `playbook.py` 安装 skills会把 `codex/skills/*` 复制到 `$CODEX_HOME/skills/`
使用统一入口 `playbook.py` 安装 skills会把 `codex/skills/*` 复制到 `~/.agents/skills/`
```toml
# playbook.toml
@ -54,7 +54,7 @@ project_root = "."
[install_skills]
mode = "all" # list|all
codex_home = "~/.codex"
agents_home = "~/.agents"
```
```bash
@ -74,10 +74,10 @@ skills = ["style-cleanup", "commit-message"]
```toml
[install_skills]
mode = "all"
codex_home = "./.codex"
agents_home = "./.agents"
```
> 注意Codex 只会从 `CODEX_HOME` 加载 skills使用本地安装时启动 Codex 需设置同样的 `CODEX_HOME`
> 注意Codex 默认从 `~/.agents/skills` 加载 skills使用本地安装时需要确保 Codex 能发现该路径
如果你的项目通过 `git subtree` vendoring 本 Playbook推荐前缀
`docs/standards/playbook`),则在目标项目里执行:

View File

@ -9,7 +9,7 @@ description: Use when creating new skills, editing existing skills, or verifying
**Writing skills IS Test-Driven Development applied to process documentation.**
**Personal skills live in agent-specific directories (`~/.claude/skills` for Claude Code, `~/.codex/skills` for Codex)**
**Personal skills live in agent-specific directories (`~/.claude/skills` for Claude Code, `~/.agents/skills` for Codex)**
You write test cases (pressure scenarios with subagents), watch them fail (baseline behavior), write the skill (documentation), watch tests pass (agents comply), and refactor (close loopholes).

View File

@ -14,6 +14,7 @@
[sync_rules]
# 同步 AGENT_RULES.md配置节存在即启用
# force = false # 可选:覆盖已有文件
# no_backup = false # 可选:跳过备份
[sync_memory_bank]
# 同步 memory-bank/(配置节存在即启用)
@ -32,11 +33,12 @@
[sync_standards]
# langs = ["tsl", "cpp"] # 必填:要同步的语言
# gitattr_mode = "append" # append(补全缺失)|overwrite(覆盖)|block(插入块)|skip(跳过)
# no_backup = false # 可选:跳过备份(.agents/.gitattributes
[install_skills]
# mode = "list" # list|all
# skills = ["brainstorming"] # mode=list 时必填
# codex_home = "~/.codex" # 可选:默认 ~/.codex
# agents_home = "~/.agents" # 可选:默认 ~/.agents
[format_md]
# tool = "prettier" # 仅支持 prettier

View File

@ -1056,16 +1056,19 @@ def normalize_globs(raw: object) -> list[str]:
def install_skills_action(config: dict, context: dict) -> int:
mode = str(config.get("mode", "list")).lower()
codex_home = Path(config.get("codex_home", "~/.codex")).expanduser()
if not codex_home.is_absolute():
codex_home = (context["project_root"] / codex_home).resolve()
if "codex_home" in config:
print("ERROR: codex_home is no longer supported; use agents_home", file=sys.stderr)
return 2
agents_home = Path(config.get("agents_home", "~/.agents")).expanduser()
if not agents_home.is_absolute():
agents_home = (context["project_root"] / agents_home).resolve()
skills_src_root = PLAYBOOK_ROOT / "codex/skills"
if not skills_src_root.is_dir():
print(f"ERROR: skills source not found: {skills_src_root}", file=sys.stderr)
return 2
skills_dst_root = codex_home / "skills"
skills_dst_root = agents_home / "skills"
ensure_dir(skills_dst_root)
if mode == "all":

View File

@ -126,6 +126,27 @@ langs = ["tsl"]
self.assertEqual(block[bullet_idx - 1], "")
def test_install_skills(self):
with tempfile.TemporaryDirectory() as tmp_dir:
target = Path(tmp_dir) / "agents"
config_body = f"""
[playbook]
project_root = "{tmp_dir}"
[install_skills]
agents_home = "{target}"
mode = "list"
skills = ["brainstorming"]
"""
config_path = Path(tmp_dir) / "playbook.toml"
config_path.write_text(config_body, encoding="utf-8")
result = run_cli("-config", str(config_path))
skill_file = target / "skills/brainstorming/SKILL.md"
self.assertEqual(result.returncode, 0)
self.assertTrue(skill_file.is_file())
def test_install_skills_rejects_codex_home(self):
with tempfile.TemporaryDirectory() as tmp_dir:
target = Path(tmp_dir) / "codex"
config_body = f"""
@ -142,9 +163,8 @@ skills = ["brainstorming"]
result = run_cli("-config", str(config_path))
skill_file = target / "skills/brainstorming/SKILL.md"
self.assertEqual(result.returncode, 0)
self.assertTrue(skill_file.is_file())
self.assertNotEqual(result.returncode, 0)
self.assertIn("codex_home", result.stdout + result.stderr)
if __name__ == "__main__":
unittest.main()