Rename the portable conversation-shortcut skill from agent-shortcuts to shortcuts and re-prefix every token to soc* (sobu, soco, socobu, socon, sona, sote) so each token mirrors its command and never places two consonants next to each other. make install and make project now also register the caveman and ponytail marketplaces and enable both plugins in the target's .claude/settings.json so their SessionStart hooks activate every session; the merge is non-destructive, idempotent, and leaves an unparseable settings.json untouched. - skills/shortcuts/SKILL.md: new name and soc* token table - tests/test_shortcuts.py: assert present, sorted, soc-prefixed, no cluster - remove skills/agent-shortcuts and tests/test_agent_shortcuts.py - scripts/enable-plugins.js: node JSON merge helper - scripts/install.sh: run the helper after copying skills - README.md: document the auto-enable behavior Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
60 lines
1.8 KiB
Python
60 lines
1.8 KiB
Python
"""Validate the shortcuts skill table: rows present, sorted, ``soc``-prefixed,
|
|
no two adjacent consonants."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
SKILL = REPO_ROOT / "skills" / "shortcuts" / "SKILL.md"
|
|
|
|
ROW_RE = re.compile(r"^\|\s*`(?P<shortcut>[^`]+)`\s*\|")
|
|
VOWELS = frozenset("aeiou")
|
|
|
|
|
|
def _has_consonant_cluster(name: str) -> bool:
|
|
prev_consonant = False
|
|
for char in name:
|
|
if not char.isalpha():
|
|
prev_consonant = False
|
|
continue
|
|
is_consonant = char.lower() not in VOWELS
|
|
if is_consonant and prev_consonant:
|
|
return True
|
|
prev_consonant = is_consonant
|
|
return False
|
|
|
|
|
|
class TestShortcuts(unittest.TestCase):
|
|
def setUp(self):
|
|
self.assertTrue(SKILL.is_file(), f"missing {SKILL}")
|
|
self.shortcuts = [
|
|
match.group("shortcut")
|
|
for line in SKILL.read_text(encoding="utf-8").splitlines()
|
|
if (match := ROW_RE.match(line))
|
|
]
|
|
|
|
def test_shortcuts_present(self):
|
|
self.assertTrue(self.shortcuts, "no shortcut rows found in shortcuts skill")
|
|
|
|
def test_shortcuts_sorted_ascending(self):
|
|
self.assertEqual(
|
|
self.shortcuts,
|
|
sorted(self.shortcuts),
|
|
f"shortcuts table not sorted; expected {sorted(self.shortcuts)}",
|
|
)
|
|
|
|
def test_shortcuts_start_with_so(self):
|
|
bad = [s for s in self.shortcuts if not s.startswith("so")]
|
|
self.assertFalse(bad, f"shortcuts not starting with 'so': {bad}")
|
|
|
|
def test_no_consecutive_consonants(self):
|
|
bad = [s for s in self.shortcuts if _has_consonant_cluster(s)]
|
|
self.assertFalse(bad, f"shortcuts with two adjacent consonants: {bad}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|