📦 deps(skills): add karpathy thirdparty sync
This commit is contained in:
parent
96b705b00b
commit
08ca87b74c
|
|
@ -22,6 +22,15 @@
|
|||
"template_root": "src/ui-ux-pro-max/templates",
|
||||
"data_dir": "src/ui-ux-pro-max/data",
|
||||
"scripts_dir": "src/ui-ux-pro-max/scripts"
|
||||
},
|
||||
{
|
||||
"id": "andrej-karpathy-skills",
|
||||
"upstream_repo": "https://github.com/forrestchang/andrej-karpathy-skills.git",
|
||||
"upstream_ref": "main",
|
||||
"snapshot_dir": "andrej-karpathy-skills",
|
||||
"sync_mode": "copy_skill_dirs",
|
||||
"source_list": "codex/skills/.sources/andrej-karpathy-skills.list",
|
||||
"skills_subdir": "skills"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,6 +158,14 @@ python <deploy_root>/scripts/playbook.py -config playbook.toml
|
|||
|
||||
- `superpowers.list`
|
||||
- `ui-ux-pro-max.list`
|
||||
- `andrej-karpathy-skills.list`
|
||||
|
||||
部署链路:
|
||||
|
||||
- `thirdparty/skill` 分支保存上游快照 `andrej-karpathy-skills/`
|
||||
- 自动同步后,`skills/karpathy-guidelines/` 会落到 `codex/skills/karpathy-guidelines/`
|
||||
- 运行 `[install_skills]` 时,再复制到 `~/.agents/skills/karpathy-guidelines/`
|
||||
- 该 skill 本身不依赖 Playbook 文档路径重写,也不需要像 `ui-ux-pro-max` 那样额外渲染
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,15 @@ def write_config(root: Path, name: str, body: str) -> Path:
|
|||
return config_path
|
||||
|
||||
|
||||
def bash_path(path: Path) -> str:
|
||||
resolved = path.resolve()
|
||||
if sys.platform != "win32":
|
||||
return resolved.as_posix()
|
||||
drive = resolved.drive.rstrip(":").lower()
|
||||
rest = resolved.as_posix()[2:]
|
||||
return f"/mnt/{drive}{rest}"
|
||||
|
||||
|
||||
class PlaybookCliTests(unittest.TestCase):
|
||||
def assert_style_cleanup_tsl_docs_prefix(
|
||||
self, root: Path, agents_home: Path, docs_prefix: str
|
||||
|
|
@ -275,6 +284,67 @@ skills = ["brainstorming"]
|
|||
self.assertEqual(result.returncode, 0)
|
||||
self.assertTrue(skill_file.is_file())
|
||||
|
||||
def test_install_generated_thirdparty_karpathy_skill_after_sync(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
tmp_root = Path(tmp_dir)
|
||||
mirror = tmp_root / "origin.git"
|
||||
repo = tmp_root / "repo"
|
||||
target = tmp_root / "agents"
|
||||
|
||||
clone_mirror = subprocess.run(
|
||||
["git", "clone", "--mirror", str(ROOT), str(mirror)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
self.assertEqual(clone_mirror.returncode, 0, msg=clone_mirror.stderr)
|
||||
|
||||
clone_repo = subprocess.run(
|
||||
["git", "clone", str(mirror), str(repo)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
self.assertEqual(clone_repo.returncode, 0, msg=clone_repo.stderr)
|
||||
|
||||
set_remote = subprocess.run(
|
||||
["git", "-C", str(repo), "remote", "set-url", "origin", bash_path(mirror)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
self.assertEqual(set_remote.returncode, 0, msg=set_remote.stderr)
|
||||
|
||||
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_result = subprocess.run(
|
||||
["bash", ".gitea/ci/sync_thirdparty_skills.sh"],
|
||||
cwd=repo,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
self.assertEqual(
|
||||
sync_result.returncode, 0, msg=sync_result.stdout + sync_result.stderr
|
||||
)
|
||||
|
||||
config_body = f"""
|
||||
[playbook]
|
||||
project_root = "{tmp_root}"
|
||||
deploy_root = "{CUSTOM_DEPLOY_ROOT}"
|
||||
|
||||
[install_skills]
|
||||
agents_home = "{target}"
|
||||
mode = "list"
|
||||
skills = ["karpathy-guidelines"]
|
||||
"""
|
||||
config_path = tmp_root / "playbook.toml"
|
||||
config_path.write_text(config_body, encoding="utf-8")
|
||||
|
||||
result = run_script(repo / "scripts" / "playbook.py", "-config", str(config_path))
|
||||
|
||||
skill_file = target / "skills" / "karpathy-guidelines" / "SKILL.md"
|
||||
self.assertEqual(result.returncode, 0, msg=result.stdout + result.stderr)
|
||||
self.assertTrue(skill_file.is_file())
|
||||
|
||||
def test_install_skills_rejects_removed_tsl_guide(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
target = Path(tmp_dir) / "agents"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
|
@ -19,12 +23,42 @@ def load_manifest() -> dict:
|
|||
return json.loads(MANIFEST.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def bash_path(path: Path) -> str:
|
||||
resolved = path.resolve()
|
||||
if os.name != "nt":
|
||||
return resolved.as_posix()
|
||||
drive = resolved.drive.rstrip(":").lower()
|
||||
rest = resolved.as_posix()[2:]
|
||||
return f"/mnt/{drive}{rest}"
|
||||
|
||||
|
||||
def run_command(*args: str, cwd: Path | None = None) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
list(args),
|
||||
cwd=cwd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
|
||||
class ThirdpartySkillsPipelineTests(unittest.TestCase):
|
||||
def test_manifest_declares_superpowers_and_ui_ux_pro_max(self):
|
||||
def test_manifest_declares_all_thirdparty_sources(self):
|
||||
data = load_manifest()
|
||||
self.assertEqual(
|
||||
[entry["id"] for entry in data["sources"]],
|
||||
["superpowers", "ui-ux-pro-max"],
|
||||
["superpowers", "ui-ux-pro-max", "andrej-karpathy-skills"],
|
||||
)
|
||||
|
||||
def test_karpathy_manifest_uses_copy_skill_dirs_sync_mode(self):
|
||||
data = load_manifest()
|
||||
karpathy = next(
|
||||
item for item in data["sources"] if item["id"] == "andrej-karpathy-skills"
|
||||
)
|
||||
self.assertEqual(karpathy["sync_mode"], "copy_skill_dirs")
|
||||
self.assertEqual(karpathy["snapshot_dir"], "andrej-karpathy-skills")
|
||||
self.assertEqual(karpathy["skills_subdir"], "skills")
|
||||
self.assertEqual(
|
||||
karpathy["source_list"], "codex/skills/.sources/andrej-karpathy-skills.list"
|
||||
)
|
||||
|
||||
def test_ui_ux_pro_max_uses_render_codex_skill_sync_mode(self):
|
||||
|
|
@ -108,6 +142,44 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
|
|||
self.assertNotIn("exclude_skill_dirs", text)
|
||||
self.assertNotIn("is_excluded_skill_dir", text)
|
||||
|
||||
def test_sync_script_generates_karpathy_outputs_in_temp_repo(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
tmp_root = Path(tmp_dir)
|
||||
mirror = tmp_root / "origin.git"
|
||||
work = tmp_root / "work"
|
||||
|
||||
clone_mirror = run_command("git", "clone", "--mirror", str(ROOT), str(mirror))
|
||||
self.assertEqual(clone_mirror.returncode, 0, msg=clone_mirror.stderr)
|
||||
|
||||
clone_work = run_command("git", "clone", str(mirror), str(work))
|
||||
self.assertEqual(clone_work.returncode, 0, msg=clone_work.stderr)
|
||||
|
||||
set_remote = run_command(
|
||||
"git", "-C", str(work), "remote", "set-url", "origin", bash_path(mirror)
|
||||
)
|
||||
self.assertEqual(set_remote.returncode, 0, msg=set_remote.stderr)
|
||||
|
||||
shutil.copy2(MANIFEST, work / ".gitea" / "ci" / "thirdparty_skills.json")
|
||||
|
||||
sync_result = run_command("bash", ".gitea/ci/sync_thirdparty_skills.sh", cwd=work)
|
||||
self.assertEqual(
|
||||
sync_result.returncode,
|
||||
0,
|
||||
msg=sync_result.stdout + sync_result.stderr,
|
||||
)
|
||||
|
||||
generated_list = (
|
||||
work / "codex" / "skills" / ".sources" / "andrej-karpathy-skills.list"
|
||||
)
|
||||
generated_skill = (
|
||||
work / "codex" / "skills" / "karpathy-guidelines" / "SKILL.md"
|
||||
)
|
||||
self.assertTrue(generated_list.is_file())
|
||||
self.assertTrue(generated_skill.is_file())
|
||||
self.assertIn(
|
||||
"karpathy-guidelines", generated_list.read_text(encoding="utf-8")
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Reference in New Issue