♻️ refactor(skills): rename repo skills source dir

move the repository skills source from `codex/skills` to `skills`.

update vendor/install_skills paths, thirdparty sync pipeline, docs, and regression tests, including deployment-route e2e coverage.
This commit is contained in:
csh
2026-05-19 09:09:54 +08:00
parent f049dfbe10
commit 2c5050de09
100 changed files with 241 additions and 36 deletions
+3
View File
@@ -397,6 +397,9 @@ skills = ["brainstorming"]
manifest_src = ROOT / ".gitea" / "ci" / "thirdparty_skills.json"
manifest_dst = repo / ".gitea" / "ci" / "thirdparty_skills.json"
manifest_dst.write_text(manifest_src.read_text(encoding="utf-8"), encoding="utf-8")
sync_src = ROOT / ".gitea" / "ci" / "sync_thirdparty_skills.sh"
sync_dst = repo / ".gitea" / "ci" / "sync_thirdparty_skills.sh"
sync_dst.write_text(sync_src.read_text(encoding="utf-8"), encoding="utf-8")
sync_result = subprocess.run(
["bash", ".gitea/ci/sync_thirdparty_skills.sh"],
+185
View File
@@ -0,0 +1,185 @@
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
DEFAULT_DEPLOY_ROOT = Path("docs/standards/playbook")
CUSTOM_DEPLOY_ROOT = Path("custom/playbook")
REPO_COPY_IGNORE = shutil.ignore_patterns(
".git",
".venv",
"node_modules",
"__pycache__",
".pytest_cache",
".mypy_cache",
".ruff_cache",
)
def run_script(
script: Path, *args: str, cwd: Path | None = None
) -> subprocess.CompletedProcess[str]:
return subprocess.run(
[sys.executable, str(script), *args],
capture_output=True,
text=True,
cwd=cwd,
)
def write_config(root: Path, name: str, body: str) -> Path:
config_path = root / name
config_path.write_text(body.strip() + "\n", encoding="utf-8")
return config_path
def copy_repo(target: Path) -> Path:
shutil.copytree(ROOT, target, ignore=REPO_COPY_IGNORE)
return target
class DeploymentRoutesE2ETests(unittest.TestCase):
def assert_core_project_files(self, project_root: Path) -> None:
expected = [
"AGENTS.md",
"AGENT_RULES.md",
"AGENT_RULES.local.md",
"memory-bank/progress.md",
"docs/prompts/system/agent-behavior.md",
".agents/index.md",
".agents/tsl/index.md",
".agents/markdown/index.md",
".gitattributes",
]
for rel in expected:
with self.subTest(path=rel):
self.assertTrue((project_root / rel).exists(), f"missing: {rel}")
def assert_docs_prefix(self, project_root: Path, docs_prefix: str) -> None:
agents_index = (project_root / ".agents" / "index.md").read_text(encoding="utf-8")
self.assertIn("`.agents/tsl/index.md`", agents_index)
self.assertIn("`.agents/markdown/index.md`", agents_index)
self.assertIn(f"- {docs_prefix}", agents_index)
tsl_index = (project_root / ".agents" / "tsl" / "index.md").read_text(
encoding="utf-8"
)
self.assertIn(f"`{docs_prefix}/tsl/index.md`", tsl_index)
self.assertNotIn("`docs/tsl/index.md`", tsl_index)
def test_subtree_style_deployment_syncs_project_files(self):
with tempfile.TemporaryDirectory() as tmp_dir:
tmp_root = Path(tmp_dir)
project_root = tmp_root / "project"
playbook_root = project_root / DEFAULT_DEPLOY_ROOT
project_root.mkdir()
copy_repo(playbook_root)
config_path = write_config(
project_root,
"playbook.toml",
"""
[playbook]
project_root = "."
[sync_rules]
no_backup = true
[sync_memory_bank]
project_name = "Demo"
[sync_prompts]
no_backup = true
[sync_standards]
langs = ["tsl", "markdown"]
no_backup = true
""",
)
result = run_script(
playbook_root / "scripts" / "playbook.py",
"-config",
str(config_path),
cwd=project_root,
)
self.assertEqual(result.returncode, 0, msg=result.stdout + result.stderr)
self.assert_core_project_files(project_root)
self.assert_docs_prefix(project_root, "docs/standards/playbook/docs")
claude_md = project_root / "CLAUDE.md"
self.assertTrue(claude_md.is_file())
text = claude_md.read_text(encoding="utf-8")
self.assertIn("@AGENTS.md", text)
self.assertIn("@AGENT_RULES.md", text)
self.assertIn("<!-- playbook:claude:start -->", text)
def test_external_clone_deployment_vendors_snapshot_and_updates_claude(self):
with tempfile.TemporaryDirectory() as tmp_dir:
tmp_root = Path(tmp_dir)
external_clone = tmp_root / "playbook"
project_root = tmp_root / "project"
copy_repo(external_clone)
project_root.mkdir()
claude_md = project_root / ".claude" / "CLAUDE.md"
claude_md.parent.mkdir()
claude_md.write_text("# Existing Claude\n\nKeep this.\n", encoding="utf-8")
config_path = write_config(
project_root,
"playbook.toml",
f"""
[playbook]
project_root = "."
deploy_root = "{CUSTOM_DEPLOY_ROOT.as_posix()}"
[vendor]
langs = ["tsl", "markdown"]
[sync_rules]
no_backup = true
[sync_memory_bank]
project_name = "Demo"
[sync_prompts]
no_backup = true
[sync_standards]
langs = ["tsl", "markdown"]
no_backup = true
""",
)
result = run_script(
external_clone / "scripts" / "playbook.py",
"-config",
str(config_path),
cwd=project_root,
)
self.assertEqual(result.returncode, 0, msg=result.stdout + result.stderr)
snapshot_root = project_root / CUSTOM_DEPLOY_ROOT
self.assertTrue((snapshot_root / "SOURCE.md").is_file())
self.assertTrue((snapshot_root / "scripts" / "playbook.py").is_file())
self.assertTrue((snapshot_root / "templates" / "README.md").is_file())
self.assert_core_project_files(project_root)
self.assert_docs_prefix(project_root, "custom/playbook/docs")
text = claude_md.read_text(encoding="utf-8")
self.assertIn("Keep this.", text)
self.assertIn("@../AGENTS.md", text)
self.assertIn("@../AGENT_RULES.md", text)
self.assertEqual(text.count("<!-- playbook:claude:start -->"), 1)
self.assertFalse((project_root / "CLAUDE.md").exists())
if __name__ == "__main__":
unittest.main()
+10 -9
View File
@@ -14,9 +14,9 @@ LEGACY_WORKFLOW = ROOT / ".gitea" / "workflows" / "update-thirdparty-superpowers
UPDATE_SCRIPT = ROOT / ".gitea" / "ci" / "update_thirdparty_skills.sh"
SYNC_SCRIPT = ROOT / ".gitea" / "ci" / "sync_thirdparty_skills.sh"
SKILLS_MD = ROOT / "SKILLS.md"
SUPERPOWERS_LIST = ROOT / "codex" / "skills" / "thirdparty" / ".sources" / "superpowers.list"
UI_UX_PRO_MAX_LIST = ROOT / "codex" / "skills" / "thirdparty" / ".sources" / "ui-ux-pro-max.list"
UI_UX_PRO_MAX_DIR = ROOT / "codex" / "skills" / "thirdparty" / "ui-ux-pro-max"
SUPERPOWERS_LIST = ROOT / "skills" / "thirdparty" / ".sources" / "superpowers.list"
UI_UX_PRO_MAX_LIST = ROOT / "skills" / "thirdparty" / ".sources" / "ui-ux-pro-max.list"
UI_UX_PRO_MAX_DIR = ROOT / "skills" / "thirdparty" / "ui-ux-pro-max"
def load_manifest() -> dict:
@@ -58,13 +58,13 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
self.assertEqual(karpathy["snapshot_dir"], "andrej-karpathy-skills")
self.assertEqual(karpathy["skills_subdir"], "skills")
self.assertEqual(
karpathy["source_list"], "codex/skills/thirdparty/.sources/andrej-karpathy-skills.list"
karpathy["source_list"], "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_skill_sync_mode(self):
data = load_manifest()
ui_skill = next(item for item in data["sources"] if item["id"] == "ui-ux-pro-max")
self.assertEqual(ui_skill["sync_mode"], "render_codex_skill")
self.assertEqual(ui_skill["sync_mode"], "render_skill")
self.assertEqual(ui_skill["snapshot_dir"], "ui-ux-pro-max")
def test_superpowers_manifest_prunes_non_superpowers_paths(self):
@@ -105,7 +105,7 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
def test_skills_doc_points_to_generic_thirdparty_sources(self):
text = SKILLS_MD.read_text(encoding="utf-8")
self.assertIn("## 9. Third-party Skills", text)
self.assertIn("来源:`codex/skills/thirdparty/.sources/`(第三方来源清单目录)。", text)
self.assertIn("来源:`skills/thirdparty/.sources/`(第三方来源清单目录)。", text)
self.assertNotIn("Third-party Skills (superpowers)", text)
def test_superpowers_and_ui_ux_pro_max_source_lists_exist(self):
@@ -160,6 +160,7 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
self.assertEqual(set_remote.returncode, 0, msg=set_remote.stderr)
shutil.copy2(MANIFEST, work / ".gitea" / "ci" / "thirdparty_skills.json")
shutil.copy2(SYNC_SCRIPT, work / ".gitea" / "ci" / "sync_thirdparty_skills.sh")
sync_result = run_command("bash", ".gitea/ci/sync_thirdparty_skills.sh", cwd=work)
self.assertEqual(
@@ -169,10 +170,10 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
)
generated_list = (
work / "codex" / "skills" / "thirdparty" / ".sources" / "andrej-karpathy-skills.list"
work / "skills" / "thirdparty" / ".sources" / "andrej-karpathy-skills.list"
)
generated_skill = (
work / "codex" / "skills" / "thirdparty" / "karpathy-guidelines" / "SKILL.md"
work / "skills" / "thirdparty" / "karpathy-guidelines" / "SKILL.md"
)
self.assertTrue(generated_list.is_file())
self.assertTrue(generated_skill.is_file())
+15 -1
View File
@@ -7,7 +7,8 @@ README = ROOT / "README.md"
PLAYBOOK_EXAMPLE = ROOT / "playbook.toml.example"
SKILLS_DOC = ROOT / "SKILLS.md"
TEMPLATES_CI_README = ROOT / "templates" / "ci" / "README.md"
REMOVED_TSL_GUIDE = ROOT / "codex" / "skills" / "tsl-guide"
TEMPLATES_README = ROOT / "templates" / "README.md"
REMOVED_TSL_GUIDE = ROOT / "skills" / "tsl-guide"
class TslEntrypointsConsistencyTests(unittest.TestCase):
def test_ruleset_lists_canonical_tsl_layers(self):
@@ -44,6 +45,19 @@ class TslEntrypointsConsistencyTests(unittest.TestCase):
self.assertNotIn("vendoring", SKILLS_DOC.read_text(encoding="utf-8"))
self.assertNotIn("vendoring", TEMPLATES_CI_README.read_text(encoding="utf-8"))
def test_repo_docs_use_platform_neutral_skills_source_dir(self):
readme_text = README.read_text(encoding="utf-8")
skills_text = SKILLS_DOC.read_text(encoding="utf-8")
templates_text = TEMPLATES_README.read_text(encoding="utf-8")
self.assertIn("`skills/`", readme_text)
self.assertIn("`skills/`", skills_text)
self.assertIn("├── skills/", templates_text)
self.assertNotIn("`codex/skills/`", readme_text)
self.assertNotIn("`codex/skills/`", skills_text)
self.assertNotIn("├── codex/skills/", templates_text)
def test_repo_no_longer_ships_tsl_guide_skill(self):
self.assertFalse(REMOVED_TSL_GUIDE.exists())
self.assertNotIn("tsl-guide", README.read_text(encoding="utf-8"))
+2
View File
@@ -40,6 +40,8 @@ langs = ["tsl"]
self.assertTrue((snapshot / "templates/README.md").is_file())
self.assertTrue((snapshot / "templates/memory-bank").is_dir())
self.assertTrue((snapshot / "templates/prompts").is_dir())
self.assertTrue((snapshot / "skills").is_dir())
self.assertFalse((snapshot / "codex").exists())
if __name__ == "__main__":