Files
semantica/tests/test_plugin_manifest.py
T
7487 f44020c742 fix(plugins): declare agents as an array of file paths in plugin.json
Claude Code's plugin schema rejects "agents": "./agents" (a bare
directory string) with:

    Validation errors: agents: Invalid input

so the bundled plugin has never been installable. Unlike "skills",
which accepts a directory string, "agents" must be an array of .md
file paths.

Replaced the string with the explicit list of the three agent files.
Verified with `claude plugin validate plugins` (2.1.231): fails on the
old manifest with the error above, passes after this change.

Added tests/test_plugin_manifest.py to guard the manifest shape: agents
is a non-empty array of existing .md paths that stays in sync with
plugins/agents/, and the skills directory exists.

Fixes #1350
2026-09-01 17:02:52 +08:00

51 lines
1.9 KiB
Python

"""
Test for the Claude Code plugin manifest (Issue #1350).
Claude Code's plugin schema requires "agents" to be an array of .md file
paths (a bare directory string is rejected with "agents: Invalid input"),
while "skills" may be a directory string. This guards the manifest shape
so the bundled plugin stays installable.
"""
import json
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
MANIFEST = REPO_ROOT / "plugins" / ".claude-plugin" / "plugin.json"
class TestPluginManifest(unittest.TestCase):
"""Validate plugins/.claude-plugin/plugin.json against Claude Code's schema shape."""
@classmethod
def setUpClass(cls):
cls.manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
cls.plugin_root = MANIFEST.parent.parent
def test_agents_is_list_of_md_file_paths(self):
agents = self.manifest["agents"]
self.assertIsInstance(
agents, list,
'Claude Code rejects "agents" unless it is an array of .md file paths',
)
self.assertTrue(agents, "agents list should not be empty")
for entry in agents:
self.assertIsInstance(entry, str)
self.assertTrue(entry.endswith(".md"), f"{entry} is not a .md file path")
path = self.plugin_root / entry
self.assertTrue(path.is_file(), f"{entry} does not exist under plugins/")
def test_agents_list_covers_all_agent_files(self):
declared = {Path(entry).name for entry in self.manifest["agents"]}
on_disk = {p.name for p in (self.plugin_root / "agents").glob("*.md")}
self.assertEqual(declared, on_disk)
def test_skills_directory_exists(self):
skills = self.manifest["skills"]
self.assertIsInstance(skills, str)
self.assertTrue((self.plugin_root / skills).is_dir())
if __name__ == "__main__":
unittest.main()