mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
5365f971ef
Signed-off-by: phernandez <paul@basicmachines.co>
79 lines
3.0 KiB
Python
79 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Validate the Basic Memory Claude Code plugin layout."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from validate_skills import parse_frontmatter
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def read_json(path: Path) -> dict:
|
|
return json.loads(path.read_text())
|
|
|
|
|
|
def validate_claude_plugin(plugin_dir: Path) -> None:
|
|
plugin_dir = plugin_dir.resolve()
|
|
plugin_json = plugin_dir / ".claude-plugin/plugin.json"
|
|
local_marketplace_json = plugin_dir / ".claude-plugin/marketplace.json"
|
|
root_marketplace_json = ROOT / ".claude-plugin/marketplace.json"
|
|
|
|
for path in [plugin_json, local_marketplace_json, root_marketplace_json]:
|
|
if not path.exists():
|
|
raise SystemExit(f"Missing Claude plugin manifest: {path}")
|
|
|
|
plugin = read_json(plugin_json)
|
|
if plugin.get("name") != "basic-memory":
|
|
raise SystemExit(f"{plugin_json}: expected name=basic-memory")
|
|
if not str(plugin.get("repository", "")).endswith(
|
|
"/basic-memory/tree/main/plugins/claude-code"
|
|
):
|
|
raise SystemExit(f"{plugin_json}: repository must point at the monorepo subdirectory")
|
|
|
|
root_marketplace = read_json(root_marketplace_json)
|
|
root_plugin = root_marketplace["plugins"][0]
|
|
expected_source = "./plugins/claude-code"
|
|
if root_plugin.get("name") != "basic-memory" or root_plugin.get("source") != expected_source:
|
|
raise SystemExit(f"{root_marketplace_json}: expected basic-memory source {expected_source}")
|
|
|
|
local_marketplace = read_json(local_marketplace_json)
|
|
local_plugin = local_marketplace["plugins"][0]
|
|
if local_plugin.get("name") != "basic-memory" or local_plugin.get("source") != "./":
|
|
raise SystemExit(f"{local_marketplace_json}: expected plugin-local source ./")
|
|
|
|
hooks = plugin_dir / "hooks/hooks.json"
|
|
hooks_json = read_json(hooks)
|
|
if "PreToolUse" not in hooks_json.get("hooks", {}):
|
|
raise SystemExit(f"{hooks}: missing PreToolUse hook")
|
|
|
|
agent = plugin_dir / "agents/basic-memory-manager.md"
|
|
agent_frontmatter = parse_frontmatter(agent)
|
|
if agent_frontmatter.get("name") != "basic-memory-manager":
|
|
raise SystemExit(f"{agent}: missing basic-memory-manager frontmatter")
|
|
|
|
skill_dirs = sorted(path for path in (plugin_dir / "skills").iterdir() if path.is_dir())
|
|
if not skill_dirs:
|
|
raise SystemExit(f"{plugin_dir / 'skills'}: no bundled Claude Code skills")
|
|
for skill_dir in skill_dirs:
|
|
frontmatter = parse_frontmatter(skill_dir / "SKILL.md")
|
|
if frontmatter.get("name") != skill_dir.name:
|
|
raise SystemExit(f"{skill_dir}: skill name must match directory")
|
|
|
|
print(f"validated Claude Code plugin in {plugin_dir}")
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("plugin_dir", nargs="?", default="plugins/claude-code")
|
|
args = parser.parse_args()
|
|
validate_claude_plugin(Path.cwd() / args.plugin_dir)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|