fix(install): stop listing plugin skills twice in Claude
install.sh copies every skills-lock.json skill into .claude/skills while enable-plugins.js also enables the caveman and ponytail plugins that ship the same skills, so Claude listed 13 long descriptions twice on every turn of every session. enable-plugins.js now removes from the settings' .claude/skills every locked skill whose source is an enabled plugin's repository, taken from MARKETPLACES; .agents/skills keeps the copies for Codex and Gemini, which have no Claude plugin. The install message names it. test_dream checks that plugin copies go and other skills stay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
031fd3deb4
commit
97e239a67c
2 files changed
+17
-1
No files matched your search
@@ -49,6 +49,14 @@ for (const [name, entry] of Object.entries(MARKETPLACES)) {
|
|||||||
}
|
}
|
||||||
data.enabledPlugins = Object.assign(data.enabledPlugins || {}, PLUGINS);
|
data.enabledPlugins = Object.assign(data.enabledPlugins || {}, PLUGINS);
|
||||||
|
|
||||||
|
const pluginRepos = new Set(Object.values(MARKETPLACES).map((entry) => entry.source.repo));
|
||||||
|
const lock = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "skills-lock.json"), "utf8"));
|
||||||
|
for (const [name, entry] of Object.entries(lock.skills || {})) {
|
||||||
|
if (pluginRepos.has(entry.source)) {
|
||||||
|
fs.rmSync(path.join(path.dirname(settingsPath), "skills", name), { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data.hooks = data.hooks || {};
|
data.hooks = data.hooks || {};
|
||||||
const retired = (hook) =>
|
const retired = (hook) =>
|
||||||
typeof hook.command === "string" && RETIRED.some((r) => hook.command.includes(`/skills/${r}`));
|
typeof hook.command === "string" && RETIRED.some((r) => hook.command.includes(`/skills/${r}`));
|
||||||
@@ -109,4 +117,4 @@ for (const [script, event, matcher, sub] of SCRIPT_HOOKS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2) + "\n");
|
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2) + "\n");
|
||||||
console.log(`skills: enabled caveman + ponytail plugins, the ${REMINDERS.join(" + ")} reminders, the kvb-org-organize claim gate, the resource watch and the kvb-org-perform log in ${settingsPath}`);
|
console.log(`skills: enabled caveman + ponytail plugins (their skill copies dropped from this .claude), the ${REMINDERS.join(" + ")} reminders, the kvb-org-organize claim gate, news hook and resource watch, and the kvb-org-perform log in ${settingsPath}`);
|
||||||
@@ -161,6 +161,14 @@ class TestSettingsRegistration(unittest.TestCase):
|
|||||||
self.assertEqual(gate.count("kvb-org-perform/scripts/log.py"), 1)
|
self.assertEqual(gate.count("kvb-org-perform/scripts/log.py"), 1)
|
||||||
self.assertEqual(post.count("kvb-org-perform/scripts/log.py"), 1)
|
self.assertEqual(post.count("kvb-org-perform/scripts/log.py"), 1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_skills_are_not_copied_twice(self):
|
||||||
|
skills = Path(self.tmp.name) / "skills"
|
||||||
|
for name in ("caveman", "ponytail-review", "cavecrew", "archify", "kvb-dream"):
|
||||||
|
(skills / name).mkdir(parents=True)
|
||||||
|
self._patch()
|
||||||
|
self.assertEqual(sorted(p.name for p in skills.iterdir()), ["archify", "kvb-dream"])
|
||||||
|
|
||||||
def test_existing_hooks_preserved(self):
|
def test_existing_hooks_preserved(self):
|
||||||
self.settings.write_text(
|
self.settings.write_text(
|
||||||
json.dumps({"hooks": {"UserPromptSubmit": [{"hooks": [{"type": "command", "command": "true"}]}]}}),
|
json.dumps({"hooks": {"UserPromptSubmit": [{"hooks": [{"type": "command", "command": "true"}]}]}}),
|
||||||
|
|||||||
Reference in new issue
Block a user