feat(skills)!: rename the skills category to skill

kvb-skills-dream, -tune, -auto, -essence and -transcend become kvb-skill-*, and tests/test_naming.py now requires the category skill. enable-plugins.js registers the reminder hooks under the new names and repoints hook commands of the previous kvb-skills-* paths as well as the older autotune/dream paths; test_dream covers both generations. References in kvb-check-confidence, kvb-org-perform, kvb-org-rasci, kvb-transcend, the tests and the README organigram and skill map follow. The header comments of both reminder hooks and enable-plugins.js are dropped.

BREAKING CHANGE: /kvb-skills-dream, /kvb-skills-tune, /kvb-skills-auto, /kvb-skills-essence and /kvb-skills-transcend are now /kvb-skill-*.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-03 13:57:14 +01:00
1 parent 8be453b361
commit 359ea0cba0
17 files changed
+85 -95

No files matched your search

+9 -9
View File
@@ -1,4 +1,4 @@
"""Validate the kvb-skills-tune reminder hook and its settings registration."""
"""Validate the kvb-skill-tune reminder hook and its settings registration."""
from __future__ import annotations
@@ -10,8 +10,8 @@ import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
SKILL = REPO_ROOT / "skills" / "kvb-skills-tune" / "SKILL.md"
HOOK = REPO_ROOT / "skills" / "kvb-skills-tune" / "hooks" / "reminder.sh"
SKILL = REPO_ROOT / "skills" / "kvb-skill-tune" / "SKILL.md"
HOOK = REPO_ROOT / "skills" / "kvb-skill-tune" / "hooks" / "reminder.sh"
PATCHER = REPO_ROOT / "scripts" / "enable-plugins.js"
@@ -33,7 +33,7 @@ def _run_hook(stamp: Path, interval: str = "3600") -> subprocess.CompletedProces
class TestAutotuneSkill(unittest.TestCase):
def test_skill_is_trigger_only(self):
text = SKILL.read_text(encoding="utf-8")
self.assertIn("name: kvb-skills-tune", text)
self.assertIn("name: kvb-skill-tune", text)
self.assertIn("multiSelect: false", text)
@@ -51,7 +51,7 @@ class TestSkillPersistence(unittest.TestCase):
self.assertIn(target, text)
def test_autoskill_routes_to_the_same_persistence(self):
text = (REPO_ROOT / "skills" / "kvb-skills-auto" / "SKILL.md").read_text(
text = (REPO_ROOT / "skills" / "kvb-skill-auto" / "SKILL.md").read_text(
encoding="utf-8"
)
for target in ("Persistence", "~/.claude/skills/", "~/.agents/skills/"):
@@ -66,7 +66,7 @@ class TestAutotuneHook(unittest.TestCase):
def test_first_run_hints_and_stamps(self):
result = _run_hook(self.stamp)
self.assertIn("/kvb-skills-tune", result.stdout)
self.assertIn("/kvb-skill-tune", result.stdout)
self.assertTrue(self.stamp.is_file())
def test_second_run_is_silent_within_interval(self):
@@ -75,12 +75,12 @@ class TestAutotuneHook(unittest.TestCase):
def test_hint_returns_after_the_interval(self):
_run_hook(self.stamp)
self.assertIn("/kvb-skills-tune", _run_hook(self.stamp, interval="0").stdout)
self.assertIn("/kvb-skill-tune", _run_hook(self.stamp, interval="0").stdout)
def test_corrupt_stamp_does_not_crash(self):
self.stamp.parent.mkdir(parents=True, exist_ok=True)
self.stamp.write_text("not-a-timestamp\n", encoding="utf-8")
self.assertIn("/kvb-skills-tune", _run_hook(self.stamp).stdout)
self.assertIn("/kvb-skill-tune", _run_hook(self.stamp).stdout)
@unittest.skipUnless(shutil.which("node"), "node not installed")
@@ -100,7 +100,7 @@ class TestSettingsRegistration(unittest.TestCase):
self._patch()
data = self._patch()
entries = json.dumps(data["hooks"]["UserPromptSubmit"])
self.assertEqual(entries.count("kvb-skills-tune/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skill-tune/hooks/reminder.sh"), 1)
def test_existing_hooks_preserved(self):
self.settings.write_text(
+14 -14
View File
@@ -10,8 +10,8 @@ import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
SKILL = REPO_ROOT / "skills" / "kvb-skills-dream" / "SKILL.md"
HOOK = REPO_ROOT / "skills" / "kvb-skills-dream" / "hooks" / "reminder.sh"
SKILL = REPO_ROOT / "skills" / "kvb-skill-dream" / "SKILL.md"
HOOK = REPO_ROOT / "skills" / "kvb-skill-dream" / "hooks" / "reminder.sh"
PATCHER = REPO_ROOT / "scripts" / "enable-plugins.js"
@@ -38,7 +38,7 @@ def _run_hook(
class TestDreamSkill(unittest.TestCase):
def test_skill_is_trigger_only(self):
text = SKILL.read_text(encoding="utf-8")
self.assertIn("name: kvb-skills-dream", text)
self.assertIn("name: kvb-skill-dream", text)
self.assertIn("multiSelect: false", text)
@@ -51,7 +51,7 @@ class TestDreamHook(unittest.TestCase):
def test_first_run_hints_and_stamps(self):
result = _run_hook(self.cwd, self.stamp)
self.assertIn("/kvb-skills-dream", result.stdout)
self.assertIn("/kvb-skill-dream", result.stdout)
self.assertTrue(self.stamp.is_file())
def test_second_run_is_silent_within_interval(self):
@@ -60,12 +60,12 @@ class TestDreamHook(unittest.TestCase):
def test_hint_returns_after_the_interval(self):
_run_hook(self.cwd, self.stamp)
self.assertIn("/kvb-skills-dream", _run_hook(self.cwd, self.stamp, interval="0").stdout)
self.assertIn("/kvb-skill-dream", _run_hook(self.cwd, self.stamp, interval="0").stdout)
def test_corrupt_stamp_does_not_crash(self):
self.stamp.parent.mkdir(parents=True, exist_ok=True)
self.stamp.write_text("not-a-timestamp\n", encoding="utf-8")
self.assertIn("/kvb-skills-dream", _run_hook(self.cwd, self.stamp).stdout)
self.assertIn("/kvb-skill-dream", _run_hook(self.cwd, self.stamp).stdout)
def test_outside_a_repository_it_stays_silent(self):
self.assertEqual(_run_hook(self.cwd).stdout, "")
@@ -78,7 +78,7 @@ class TestDreamHook(unittest.TestCase):
subprocess.run(
["git", "init", "-q", str(repo)], check=True, capture_output=True
)
self.assertIn("/kvb-skills-dream", _run_hook(repo).stdout)
self.assertIn("/kvb-skill-dream", _run_hook(repo).stdout)
self.assertTrue((repo / ".git" / ".dream-stamp").is_file())
self.assertEqual(_run_hook(repo).stdout, "")
@@ -103,7 +103,7 @@ class TestDreamHook(unittest.TestCase):
subprocess.run(
["git", *args], check=True, capture_output=True, env=env, cwd=str(repo)
)
self.assertIn("/kvb-skills-dream", _run_hook(repo).stdout)
self.assertIn("/kvb-skill-dream", _run_hook(repo).stdout)
self.assertEqual(_run_hook(tree).stdout, "")
@@ -124,8 +124,8 @@ class TestSettingsRegistration(unittest.TestCase):
self._patch()
data = self._patch()
entries = json.dumps(data["hooks"]["UserPromptSubmit"])
self.assertEqual(entries.count("kvb-skills-dream/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skills-tune/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skill-dream/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skill-tune/hooks/reminder.sh"), 1)
gate = json.dumps(data["hooks"]["PreToolUse"])
self.assertEqual(gate.count("kvb-org-organize/scripts/council.py"), 1)
self.assertEqual(gate.count("kvb-org-organize/scripts/watch.py"), 1)
@@ -147,7 +147,7 @@ class TestSettingsRegistration(unittest.TestCase):
"hooks": {
"UserPromptSubmit": [
hook(f'bash "{base}/autotune/hooks/reminder.sh"'),
hook(f'bash "{base}/dream/hooks/reminder.sh"'),
hook(f'bash "{base}/kvb-skills-dream/hooks/reminder.sh"'),
hook(f'python3 "{base}/democracy/scripts/watch.py" watch'),
hook("echo keep"),
],
@@ -167,12 +167,12 @@ class TestSettingsRegistration(unittest.TestCase):
)
data = self._patch()
text = json.dumps(data["hooks"])
for old in ("/autotune/", "/dream/", "/democracy/", "/subagent-model/"):
for old in ("/autotune/", "/kvb-skills-dream/", "/democracy/", "/subagent-model/"):
self.assertNotIn(f"skills{old}", text)
self.assertIn("echo keep", text)
entries = json.dumps(data["hooks"]["UserPromptSubmit"])
self.assertEqual(entries.count("kvb-skills-dream/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skills-tune/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skill-dream/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skill-tune/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-org-organize/scripts/watch.py"), 1)
gate = json.dumps(data["hooks"]["PreToolUse"])
self.assertEqual(gate.count("kvb-org-organize/scripts/council.py"), 1)
+1 -1
View File
@@ -9,7 +9,7 @@ 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", "skills")
CATEGORIES = ("think", "create", "code", "ci", "git", "check", "org", "skill")
UNCATEGORIZED = frozenset({"kvb-cleanup", "kvb-help", "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]+)*$")
+5 -5
View File
@@ -1,4 +1,4 @@
"""The self-improving skills must share the objective held by kvb-skills-tune."""
"""The self-improving skills must share the objective held by kvb-skill-tune."""
from __future__ import annotations
@@ -7,12 +7,12 @@ from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
SKILLS = REPO_ROOT / "skills"
ROUTERS = ("kvb-skills-auto", "kvb-check-confidence")
ROUTERS = ("kvb-skill-auto", "kvb-check-confidence")
class TestOptimizeObjective(unittest.TestCase):
def test_autotune_names_the_three_axes(self):
text = (SKILLS / "kvb-skills-tune" / "SKILL.md").read_text(encoding="utf-8")
text = (SKILLS / "kvb-skill-tune" / "SKILL.md").read_text(encoding="utf-8")
self.assertIn("## Objective", text)
for axis in ("**Tokens**", "**Time**", "**Quality**"):
self.assertIn(axis, text)
@@ -21,9 +21,9 @@ class TestOptimizeObjective(unittest.TestCase):
for skill in ROUTERS:
text = (SKILLS / skill / "SKILL.md").read_text(encoding="utf-8")
self.assertIn(
"`kvb-skills-tune` skill's **Objective**",
"`kvb-skill-tune` skill's **Objective**",
text,
f"{skill} does not route to the kvb-skills-tune objective",
f"{skill} does not route to the kvb-skill-tune objective",
)