feat: reproducible agent skill collection with install/update tooling
- skills-lock.json pins every skill (extracted from infinito-nexus-core) - make install (global to $HOME) and make project TARGET=<repo> copy the rendered skills into .agents/skills + .claude/skills - make update refreshes the lock via npx skills; make test validates the lock shape and shellchecks the scripts - CI: test workflow on push/PR, daily update workflow opening a PR - MIRRORS for code.infinito.nexus and git.veen.world
This commit is contained in:
55
tests/test_lock.py
Normal file
55
tests/test_lock.py
Normal file
@@ -0,0 +1,55 @@
|
||||
"""Validate skills-lock.json: shape, required fields, and hash format."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
LOCK_FILE = REPO_ROOT / "skills-lock.json"
|
||||
|
||||
HASH_RE = re.compile(r"^[0-9a-f]{64}$")
|
||||
REQUIRED_FIELDS = ("source", "sourceType", "skillPath", "computedHash")
|
||||
|
||||
|
||||
class TestSkillsLock(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.lock = json.loads(LOCK_FILE.read_text(encoding="utf-8"))
|
||||
|
||||
def test_version_is_supported(self):
|
||||
self.assertEqual(self.lock.get("version"), 1)
|
||||
|
||||
def test_skills_present(self):
|
||||
self.assertTrue(self.lock.get("skills"), "lock holds no skills")
|
||||
|
||||
def test_every_skill_is_complete(self):
|
||||
for name, entry in self.lock["skills"].items():
|
||||
with self.subTest(skill=name):
|
||||
for field in REQUIRED_FIELDS:
|
||||
self.assertTrue(
|
||||
str(entry.get(field, "")).strip(),
|
||||
f"{name}: missing or empty field '{field}'",
|
||||
)
|
||||
self.assertRegex(
|
||||
entry["computedHash"],
|
||||
HASH_RE,
|
||||
f"{name}: computedHash is not a sha256 hex digest",
|
||||
)
|
||||
self.assertTrue(
|
||||
entry["skillPath"].endswith("SKILL.md"),
|
||||
f"{name}: skillPath must point to a SKILL.md",
|
||||
)
|
||||
|
||||
def test_skills_sorted_ascending(self):
|
||||
names = list(self.lock["skills"].keys())
|
||||
self.assertEqual(
|
||||
names,
|
||||
sorted(names),
|
||||
f"skills must be sorted ascending; expected {sorted(names)}",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user