Six bundled default skills with skills install target (1.40.0)
- BUNDLED_SKILLS embeds deep-research, seriousdevelopment, git, plan, requirements-clarification, taste (21 files), adapted for tai: load_skill, ask_user, fork/poll workers, web_fetch (never curl), single-line descriptions, no em dashes; seriousdevelopment verbatim - new skills install target (12th): install writes missing only, upgrade refreshes pristine and loudly keeps user-modified files, reinstall rebuilds tracked files keeping extras, uninstall removes only unmodified content; sidecar records upstream provenance - bare --install includes skills; run_install refreshes live skills - README: Skills section bundled paragraph, twelve targets
This commit is contained in:
+149
-1
@@ -1956,7 +1956,7 @@ class InstallOpsTests(unittest.TestCase):
|
||||
report = tai.install_report("status", list(tai.INSTALL_TARGETS), self.config.home, self.store)
|
||||
for label in (
|
||||
"binary:", "bash-hook:", "zsh-hook:", "fish-hook:", "plexer-hook:", "zsh-plexer-hook:",
|
||||
"fish-plexer-hook:", "venv:", "scheduler-service:", "telegram-service:", "container:", "vault:",
|
||||
"fish-plexer-hook:", "venv:", "scheduler-service:", "telegram-service:", "container:", "skills:", "vault:",
|
||||
):
|
||||
self.assertIn(label, report)
|
||||
self.assertIn("not installed", report)
|
||||
@@ -2182,6 +2182,154 @@ class InstallOpsTests(unittest.TestCase):
|
||||
self.assertIn("use /install", printed)
|
||||
|
||||
|
||||
class BundledSkillsTests(unittest.TestCase):
|
||||
NAMES = ("deep-research", "seriousdevelopment", "git", "plan", "requirements-clarification", "taste")
|
||||
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.home = os.path.join(self.tmp.name, "home")
|
||||
os.makedirs(self.home)
|
||||
self.env = mock.patch.dict(os.environ, {"HOME": self.home, "TAI_HOME": os.path.join(self.home, ".tai")})
|
||||
self.env.start()
|
||||
|
||||
class FakeArgs:
|
||||
profile = "t"
|
||||
yes = True
|
||||
|
||||
self.config = tai.Config(FakeArgs())
|
||||
self.store = tai.Store(self.config, tai.Seal(self.config.home, "skills-test-1"))
|
||||
self.app = FakeSecretApp(self.store)
|
||||
self.app.config = self.config
|
||||
self.app.skills = {}
|
||||
self.tools = tai.Tools(self.app)
|
||||
|
||||
def tearDown(self):
|
||||
self.store.close()
|
||||
self.env.stop()
|
||||
self.tmp.cleanup()
|
||||
os.environ.pop("TAI_HOME", None)
|
||||
|
||||
def skill_path(self, name, rel="SKILL.md"):
|
||||
return os.path.join(self.config.home, "skills", name, rel)
|
||||
|
||||
def test_embedded_skills_parse(self):
|
||||
self.assertEqual(sorted(tai.BUNDLED_SKILLS), sorted(self.NAMES))
|
||||
for name in self.NAMES:
|
||||
files = tai.BUNDLED_SKILLS[name]
|
||||
self.assertIn("SKILL.md", files)
|
||||
skill = tai.parse_skill_front(files["SKILL.md"].split("\n---", 1)[0].splitlines()[1:])
|
||||
self.assertEqual(skill.get("name"), name)
|
||||
self.assertTrue(len(skill.get("description", "")) > 100, name)
|
||||
self.assertTrue(len(files["SKILL.md"]) > 1500, name)
|
||||
|
||||
def test_embedded_content_guards(self):
|
||||
for name in self.NAMES:
|
||||
self.assertIn(name, tai.BUNDLED_SKILL_UPSTREAM)
|
||||
self.assertTrue(tai.BUNDLED_SKILL_UPSTREAM[name], name)
|
||||
for rel, text in tai.BUNDLED_SKILLS[name].items():
|
||||
label = "%s/%s" % (name, rel)
|
||||
self.assertNotIn("\u2014", text, label)
|
||||
self.assertNotIn('"""', text, label)
|
||||
self.assertNotIn("read_skill", text, label)
|
||||
self.assertNotIn("request_user_input", text, label)
|
||||
self.assertNotIn("Claude Code", text, label)
|
||||
self.assertNotIn("curl -", text, label)
|
||||
self.assertNotIn("curl commands", text, label)
|
||||
self.assertNotIn("`/plan`", text, label)
|
||||
self.assertNotIn("/skill plan", text, label)
|
||||
|
||||
def test_install_discovers_all(self):
|
||||
report = tai.op_skills("install", self.config.home)
|
||||
self.assertIn("installed 6", report)
|
||||
for name in self.NAMES:
|
||||
self.assertIn(name, report)
|
||||
self.assertTrue(os.path.isfile(self.skill_path(name)), name)
|
||||
skills = tai.discover_skills(self.config.home, self.tmp.name)
|
||||
self.assertEqual(sorted(skills), sorted(self.NAMES))
|
||||
meta = tai.load_skill_install_meta(os.path.join(self.config.home, "skills"), "git")
|
||||
self.assertEqual(meta["mode"], "bundled")
|
||||
self.assertEqual(meta["scope"], "home")
|
||||
self.assertEqual(meta["source"], "tai-bundled")
|
||||
self.assertEqual(len(meta["files"]), len(tai.BUNDLED_SKILLS["git"]))
|
||||
self.assertEqual(tai.skill_provenance_note(skills["git"]), "[installed bundled, home scope, from tai-bundled]")
|
||||
extras = tai.skill_extra_files(os.path.join(self.config.home, "skills", "git"))
|
||||
self.assertEqual(extras, [os.path.join(self.config.home, "skills", "git", "scripts", "workspace-recovery.sh")])
|
||||
|
||||
def test_install_idempotent_and_never_overwrites(self):
|
||||
tai.op_skills("install", self.config.home)
|
||||
path = self.skill_path("taste")
|
||||
with open(path, "w", encoding="utf-8") as handle:
|
||||
handle.write("user rewrite")
|
||||
report = tai.op_skills("install", self.config.home)
|
||||
self.assertNotIn("installed 6", report)
|
||||
with open(path, encoding="utf-8") as handle:
|
||||
self.assertEqual(handle.read(), "user rewrite")
|
||||
|
||||
def test_upgrade_keeps_modified_loudly(self):
|
||||
tai.op_skills("install", self.config.home)
|
||||
path = self.skill_path("taste")
|
||||
with open(path, "w", encoding="utf-8") as handle:
|
||||
handle.write("user rewrite")
|
||||
report = tai.op_skills("upgrade", self.config.home)
|
||||
self.assertIn("already current 5", report)
|
||||
self.assertIn("kept modified 1 (taste (SKILL.md))", report)
|
||||
with open(path, encoding="utf-8") as handle:
|
||||
self.assertEqual(handle.read(), "user rewrite")
|
||||
self.assertIn("modified: taste", "\n".join(tai.status_lines(self.config.home, self.store)))
|
||||
|
||||
def test_reinstall_refreshes_but_keeps_extras(self):
|
||||
tai.op_skills("install", self.config.home)
|
||||
path = self.skill_path("taste")
|
||||
with open(path, "w", encoding="utf-8") as handle:
|
||||
handle.write("user rewrite")
|
||||
extra = os.path.join(self.config.home, "skills", "taste", "mynotes.md")
|
||||
with open(extra, "w", encoding="utf-8") as handle:
|
||||
handle.write("mine")
|
||||
report = tai.op_skills("reinstall", self.config.home)
|
||||
self.assertIn("reinstalled 1 (taste)", report)
|
||||
self.assertTrue(tai.bundled_skill_pristine(self.config.home, "taste"))
|
||||
self.assertTrue(os.path.isfile(extra))
|
||||
|
||||
def test_uninstall_keeps_user_content(self):
|
||||
tai.op_skills("install", self.config.home)
|
||||
path = self.skill_path("git")
|
||||
with open(path, "w", encoding="utf-8") as handle:
|
||||
handle.write("user rewrite")
|
||||
extra = os.path.join(self.config.home, "skills", "git", "mynotes.md")
|
||||
with open(extra, "w", encoding="utf-8") as handle:
|
||||
handle.write("mine")
|
||||
report = tai.op_skills("uninstall", self.config.home)
|
||||
self.assertIn("removed 5", report)
|
||||
self.assertIn("kept modified 1 (git (SKILL.md, mynotes.md))", report)
|
||||
self.assertTrue(os.path.isfile(path))
|
||||
self.assertTrue(os.path.isfile(extra))
|
||||
self.assertTrue(os.path.isfile(tai.skill_install_meta_path(os.path.join(self.config.home, "skills"), "git")))
|
||||
self.assertFalse(os.path.isdir(os.path.join(self.config.home, "skills", "taste")))
|
||||
report = tai.op_skills("uninstall", self.config.home)
|
||||
self.assertIn("kept modified 1", report)
|
||||
os.remove(path)
|
||||
os.remove(extra)
|
||||
self.assertIn("removed 1 (git)", tai.op_skills("uninstall", self.config.home))
|
||||
self.assertIn("not present", tai.op_skills("uninstall", self.config.home))
|
||||
|
||||
def test_status_counts(self):
|
||||
report = "\n".join(tai.status_lines(self.config.home, self.store))
|
||||
self.assertIn("skills: 0/6 bundled present, missing: deep-research, git, plan", report)
|
||||
tai.op_skills("install", self.config.home)
|
||||
report = "\n".join(tai.status_lines(self.config.home, self.store))
|
||||
self.assertIn("skills: 6/6 bundled present", report)
|
||||
self.assertNotIn("missing:", report.split("skills:")[1].split("\n")[0])
|
||||
|
||||
def test_run_install_refreshes_live_skills(self):
|
||||
self.assertEqual(self.app.skills, {})
|
||||
report = self.tools.dispatch("install", json.dumps({"action": "install", "targets": ["skills"]}))
|
||||
self.assertIn("installed 6", report)
|
||||
self.assertEqual(sorted(self.app.skills), sorted(self.NAMES))
|
||||
report = self.tools.dispatch("install", json.dumps({"action": "uninstall", "targets": ["skills"]}))
|
||||
self.assertIn("removed 6", report)
|
||||
self.assertEqual(self.app.skills, {})
|
||||
|
||||
|
||||
class BoxCapsTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
tai._BOX_CAPS_STAMP["done"] = False
|
||||
|
||||
Reference in New Issue
Block a user