fix(install): empty the skills directories instead of replacing them

install.sh removed ~/.claude/skills and ~/.agents/skills and created them anew. The Agent Orchestrator bind-mounts ~/.claude/skills, and a new inode left that mount showing the deleted directory, so the hub's radio send failed with radio.py not found after every make install. The installer now keeps each directory and deletes its contents before copying.

🎯 Fix: 93 %
💥 Break: 3 %
💸 Cost: 100 %
⏱️ Time: 100 %
🛡️ Security: 100 %
🏆 Score: 90
🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 %

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-11 08:20:16 +00:00
1 parent 57ab29eb0d
commit c803f4ee28
2 files changed
+43 -1

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
"""Pin scripts/install.sh: the installed skill directories keep their inode, so a bind mount of them stays current."""
from __future__ import annotations
import os
import shutil
import subprocess
import tempfile
import unittest
from pathlib import Path
INSTALL = Path(__file__).resolve().parents[1] / "scripts" / "install.sh"
NPX = "#!/bin/sh\nmkdir -p .agents/skills/locked\necho locked > .agents/skills/locked/SKILL.md\n"
class TestInstall(unittest.TestCase):
def test_reinstall_empties_the_skill_directories_in_place(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
repo, target, stubs = root / "repo", root / "target", root / "bin"
(repo / "scripts").mkdir(parents=True)
shutil.copy(INSTALL, repo / "scripts" / "install.sh")
(repo / "scripts" / "enable-plugins.js").write_text("", encoding="utf-8")
(repo / "skills" / "own").mkdir(parents=True)
(repo / "skills" / "own" / "SKILL.md").write_text("own", encoding="utf-8")
stubs.mkdir()
(stubs / "npx").write_text(NPX, encoding="utf-8")
(stubs / "npx").chmod(0o755)
installed = [target / ".agents" / "skills", target / ".claude" / "skills"]
for folder in installed:
(folder / "stale").mkdir(parents=True)
before = [folder.stat().st_ino for folder in installed]
env = {**os.environ, "PATH": f"{stubs}:/usr/bin:/bin", "TARGET": str(target)}
done = subprocess.run(["bash", str(repo / "scripts" / "install.sh")], env=env, capture_output=True, text=True, check=False)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertEqual([folder.stat().st_ino for folder in installed], before)
for folder in installed:
self.assertEqual(sorted(path.name for path in folder.iterdir()), ["locked", "own"])
if __name__ == "__main__":
unittest.main()