The new category agent holds skills that act on an agent's own setup. kvb-agent-optimize sets up the general code index codebase-memory-mcp (MIT, fully local) for every repository in scope and tightens its agent instructions: agents.py reports whether AGENTS.md exists, whether CLAUDE.md and GEMINI.md point to it, dead relative links, whether it names the index, whether the binary is installed and the git state of the shared snapshot; the operator installs the index through kvb-org-operator; the agent indexes with index_repository and check_index_coverage, asks whether the snapshot is shared, sends code questions to the graph first, audits the instructions with prompt-audit and confirms every change. A domain graph the generic index cannot see goes to the line that owns the repository. kvb-optimize runs it as step 9, so kvb-dream does too. test_naming lists the category, test_dream the optimizer, and test_agent_optimize covers the checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
77 lines
2.8 KiB
Python
77 lines
2.8 KiB
Python
"""Pin every first-party skill name to kvb-<category>-<name> and the Agent Skills name rules."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
SKILLS = REPO_ROOT / "skills"
|
|
README = REPO_ROOT / "README.md"
|
|
CATEGORIES = ("think", "create", "code", "ci", "git", "check", "org", "agent", "skill", "coach")
|
|
UNCATEGORIZED = frozenset(
|
|
{
|
|
"kvb-cleanup",
|
|
"kvb-coach",
|
|
"kvb-dream",
|
|
"kvb-help",
|
|
"kvb-optimize",
|
|
"kvb-transcend",
|
|
}
|
|
)
|
|
SPEC = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
|
|
SCHEMA = re.compile(rf"^kvb-(?:{'|'.join(CATEGORIES)})-[a-z0-9]+(?:-[a-z0-9]+)*$")
|
|
NAME_LINE = re.compile(r"^name:\s*(\S+)\s*$", re.MULTILINE)
|
|
ORGANIGRAM_EDGE = re.compile(r"^\s*(\w+) --> \w+\[(kvb-[a-z0-9-]+)\]\s*$", re.MULTILINE)
|
|
|
|
|
|
class TestSkillNames(unittest.TestCase):
|
|
def setUp(self):
|
|
self.names = sorted(p.name for p in SKILLS.iterdir() if p.is_dir())
|
|
|
|
def test_name_field_matches_the_directory(self):
|
|
for name in self.names:
|
|
with self.subTest(skill=name):
|
|
text = (SKILLS / name / "SKILL.md").read_text(encoding="utf-8")
|
|
match = NAME_LINE.search(text)
|
|
self.assertIsNotNone(match, "SKILL.md has no name: line")
|
|
self.assertEqual(match.group(1), name)
|
|
|
|
def test_names_follow_the_agent_skills_rules(self):
|
|
for name in self.names:
|
|
with self.subTest(skill=name):
|
|
self.assertRegex(name, SPEC)
|
|
self.assertLessEqual(len(name), 64)
|
|
self.assertNotRegex(name, "anthropic|claude")
|
|
|
|
def test_names_use_a_listed_category(self):
|
|
for name in self.names:
|
|
if name in UNCATEGORIZED:
|
|
continue
|
|
with self.subTest(skill=name):
|
|
self.assertRegex(name, SCHEMA)
|
|
|
|
def test_every_category_and_exception_is_in_use(self):
|
|
for category in CATEGORIES:
|
|
with self.subTest(category=category):
|
|
self.assertTrue(
|
|
any(n.startswith(f"kvb-{category}-") for n in self.names),
|
|
f"category {category} has no skill",
|
|
)
|
|
self.assertLessEqual(UNCATEGORIZED, set(self.names))
|
|
|
|
def test_readme_organigram_places_every_skill_under_its_category(self):
|
|
text = README.read_text(encoding="utf-8")
|
|
section = text.split("## Organigram", 1)[1].split("\n## ", 1)[0]
|
|
edges = ORGANIGRAM_EDGE.findall(section)
|
|
self.assertEqual(sorted(name for _, name in edges), self.names)
|
|
for parent, name in edges:
|
|
with self.subTest(skill=name):
|
|
expected = "none" if name in UNCATEGORIZED else name.split("-")[1]
|
|
self.assertEqual(parent, expected)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|