94 lines
3.4 KiB
Python
Executable File
94 lines
3.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Validate plugin paths alignment between marketplace.json and plugin structure.
|
|
|
|
Checks:
|
|
- .claude-plugin/plugin.json exists
|
|
- marketplace.json (if used at project root) paths match actual plugin directories
|
|
- Plugin name in plugin.json matches directory name
|
|
- Required plugin fields are present
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def validate_plugin_paths():
|
|
"""Check plugin paths in marketplace and plugin structure."""
|
|
errors = []
|
|
plugin_root = Path(os.environ.get("CLAUDE_PLUGIN_ROOT", "."))
|
|
|
|
# Check .claude-plugin/plugin.json exists
|
|
plugin_json = plugin_root / ".claude-plugin" / "plugin.json"
|
|
if not plugin_json.exists():
|
|
errors.append(".claude-plugin directory or plugin.json not found")
|
|
print("❌ Plugin Path Validation Failed:")
|
|
for error in errors:
|
|
print(f" • {error}")
|
|
return 1
|
|
|
|
try:
|
|
with open(plugin_json) as f:
|
|
plugin_config = json.load(f)
|
|
|
|
# Check required fields (only 'name' is required per Claude Code docs)
|
|
if "name" not in plugin_config:
|
|
errors.append(".claude-plugin/plugin.json: Missing 'name' field")
|
|
|
|
# Verify plugin name matches directory (if not at root)
|
|
plugin_name = plugin_config.get("name")
|
|
if plugin_name and plugin_root.name != ".":
|
|
dir_name = plugin_root.name
|
|
if plugin_name != dir_name:
|
|
errors.append(
|
|
f"Plugin name '{plugin_name}' in plugin.json does not match "
|
|
f"directory name '{dir_name}'"
|
|
)
|
|
|
|
except json.JSONDecodeError as e:
|
|
errors.append(f".claude-plugin/plugin.json: Invalid JSON - {e}")
|
|
|
|
# Check marketplace.json at project root if this is a plugin directory
|
|
marketplace_root = plugin_root.parent.parent / ".claude-plugin" / "marketplace.json"
|
|
if marketplace_root.exists():
|
|
try:
|
|
with open(marketplace_root) as f:
|
|
marketplace = json.load(f)
|
|
|
|
if "plugins" in marketplace:
|
|
plugin_name = plugin_config.get("name")
|
|
for plugin_entry in marketplace["plugins"]:
|
|
if plugin_entry.get("name") == plugin_name:
|
|
# Check path matches
|
|
path = plugin_entry.get("path")
|
|
if path:
|
|
# Resolve relative path
|
|
expected_path = plugin_root / "plugin.json"
|
|
if "${CLAUDE_PLUGIN_ROOT}" in path:
|
|
expected = path.replace("${CLAUDE_PLUGIN_ROOT}", str(plugin_root))
|
|
else:
|
|
expected = str(expected_path)
|
|
|
|
actual = plugin_root / ".claude-plugin" / "plugin.json"
|
|
if not actual.exists():
|
|
errors.append(
|
|
f"marketplace.json references path that doesn't exist: {path}"
|
|
)
|
|
|
|
except json.JSONDecodeError as e:
|
|
errors.append(f"marketplace.json: Invalid JSON - {e}")
|
|
|
|
if errors:
|
|
print("❌ Plugin Path Validation Failed:")
|
|
for error in errors:
|
|
print(f" • {error}")
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(validate_plugin_paths())
|