Compare commits
28 Commits
8f450c0e7b
...
v5.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 34227126c2 | |||
| ef94602eba | |||
| 155e7be399 | |||
| 1fb9e6cece | |||
| f2cf082ba8 | |||
| d580464f4a | |||
| fe6b354ee2 | |||
| ec965dc8ee | |||
| cb07a382ea | |||
| 48ce693bb5 | |||
| bc0282b5f8 | |||
| db67d3cc76 | |||
| a933edeef1 | |||
| 1df9573f7a | |||
| 35d5f14003 | |||
| 5321b2929e | |||
| 05aa50d409 | |||
| 54e8e694b1 | |||
| 8ec7cbb1e9 | |||
| 3519a96d06 | |||
| f809c672b5 | |||
| 5d205c9c13 | |||
| efb83e0f28 | |||
| 7bedfa2c65 | |||
| 42ab4f13cf | |||
| 77dc122079 | |||
| ce774bcc6f | |||
| b3abe863af |
@@ -6,7 +6,7 @@
|
|||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"description": "Project management plugins with Gitea and NetBox integrations",
|
"description": "Project management plugins with Gitea and NetBox integrations",
|
||||||
"version": "4.1.0"
|
"version": "5.0.0"
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
@@ -181,6 +181,22 @@
|
|||||||
"category": "visualization",
|
"category": "visualization",
|
||||||
"tags": ["dash", "plotly", "mantine", "charts", "dashboards", "theming", "dmc"],
|
"tags": ["dash", "plotly", "mantine", "charts", "dashboards", "theming", "dmc"],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "contract-validator",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Cross-plugin compatibility validation and Claude.md agent verification",
|
||||||
|
"source": "./plugins/contract-validator",
|
||||||
|
"author": {
|
||||||
|
"name": "Leo Miranda",
|
||||||
|
"email": "leobmiranda@gmail.com"
|
||||||
|
},
|
||||||
|
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/contract-validator/README.md",
|
||||||
|
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
|
||||||
|
"mcpServers": ["./.mcp.json"],
|
||||||
|
"category": "development",
|
||||||
|
"tags": ["validation", "contracts", "compatibility", "agents", "interfaces", "cross-plugin"],
|
||||||
|
"license": "MIT"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ All notable changes to the Leo Claude Marketplace will be documented in this fil
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [5.0.0] - 2026-01-26
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ See `docs/DEBUGGING-CHECKLIST.md` for details on cache timing.
|
|||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
**Repository:** leo-claude-mktplace
|
**Repository:** leo-claude-mktplace
|
||||||
**Version:** 4.1.0
|
**Version:** 5.0.0
|
||||||
**Status:** Production Ready
|
**Status:** Production Ready
|
||||||
|
|
||||||
A plugin marketplace for Claude Code containing:
|
A plugin marketplace for Claude Code containing:
|
||||||
@@ -67,6 +67,7 @@ A plugin marketplace for Claude Code containing:
|
|||||||
| `cmdb-assistant` | NetBox CMDB integration for infrastructure management | 1.0.0 |
|
| `cmdb-assistant` | NetBox CMDB integration for infrastructure management | 1.0.0 |
|
||||||
| `data-platform` | pandas, PostgreSQL, and dbt integration for data engineering | 1.0.0 |
|
| `data-platform` | pandas, PostgreSQL, and dbt integration for data engineering | 1.0.0 |
|
||||||
| `viz-platform` | DMC validation, Plotly charts, and theming for dashboards | 1.0.0 |
|
| `viz-platform` | DMC validation, Plotly charts, and theming for dashboards | 1.0.0 |
|
||||||
|
| `contract-validator` | Cross-plugin compatibility validation and agent verification | 1.0.0 |
|
||||||
| `project-hygiene` | Post-task cleanup automation via hooks | 0.1.0 |
|
| `project-hygiene` | Post-task cleanup automation via hooks | 0.1.0 |
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
@@ -93,6 +94,7 @@ A plugin marketplace for Claude Code containing:
|
|||||||
| **Config** | `/config-analyze`, `/config-optimize` |
|
| **Config** | `/config-analyze`, `/config-optimize` |
|
||||||
| **Data** | `/ingest`, `/profile`, `/schema`, `/explain`, `/lineage`, `/run` |
|
| **Data** | `/ingest`, `/profile`, `/schema`, `/explain`, `/lineage`, `/run` |
|
||||||
| **Visualization** | `/component`, `/chart`, `/dashboard`, `/theme`, `/theme-new`, `/theme-css` |
|
| **Visualization** | `/component`, `/chart`, `/dashboard`, `/theme`, `/theme-new`, `/theme-css` |
|
||||||
|
| **Validation** | `/validate-contracts`, `/check-agent`, `/list-interfaces` |
|
||||||
| **Debug** | `/debug-report`, `/debug-review` |
|
| **Debug** | `/debug-report`, `/debug-review` |
|
||||||
|
|
||||||
## Repository Structure
|
## Repository Structure
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Leo Claude Marketplace - v4.1.0
|
# Leo Claude Marketplace - v5.0.0
|
||||||
|
|
||||||
A collection of Claude Code plugins for project management, infrastructure automation, and development workflows.
|
A collection of Claude Code plugins for project management, infrastructure automation, and development workflows.
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**This file defines ALL valid paths in this repository. No exceptions. No inference. No assumptions.**
|
**This file defines ALL valid paths in this repository. No exceptions. No inference. No assumptions.**
|
||||||
|
|
||||||
Last Updated: 2026-01-26 (v4.1.0)
|
Last Updated: 2026-01-26 (v5.0.0)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -49,6 +49,15 @@ leo-claude-mktplace/
|
|||||||
│ │ │ └── dbt_tools.py
|
│ │ │ └── dbt_tools.py
|
||||||
│ │ ├── requirements.txt
|
│ │ ├── requirements.txt
|
||||||
│ │ └── .venv/
|
│ │ └── .venv/
|
||||||
|
│ ├── contract-validator/ # Contract validation MCP (NEW v5.0.0)
|
||||||
|
│ │ ├── mcp_server/
|
||||||
|
│ │ │ ├── server.py
|
||||||
|
│ │ │ ├── parse_tools.py
|
||||||
|
│ │ │ ├── validation_tools.py
|
||||||
|
│ │ │ └── report_tools.py
|
||||||
|
│ │ ├── tests/
|
||||||
|
│ │ ├── requirements.txt
|
||||||
|
│ │ └── .venv/
|
||||||
│ └── viz-platform/ # Visualization MCP (NEW v4.1.0)
|
│ └── viz-platform/ # Visualization MCP (NEW v4.1.0)
|
||||||
│ ├── mcp_server/
|
│ ├── mcp_server/
|
||||||
│ │ ├── server.py
|
│ │ ├── server.py
|
||||||
@@ -135,6 +144,14 @@ leo-claude-mktplace/
|
|||||||
│ │ ├── agents/
|
│ │ ├── agents/
|
||||||
│ │ ├── hooks/
|
│ │ ├── hooks/
|
||||||
│ │ └── claude-md-integration.md
|
│ │ └── claude-md-integration.md
|
||||||
|
│ ├── contract-validator/ # NEW in v5.0.0
|
||||||
|
│ │ ├── .claude-plugin/
|
||||||
|
│ │ ├── .mcp.json
|
||||||
|
│ │ ├── mcp-servers/
|
||||||
|
│ │ │ └── contract-validator -> ../../../mcp-servers/contract-validator # SYMLINK
|
||||||
|
│ │ ├── commands/
|
||||||
|
│ │ ├── agents/
|
||||||
|
│ │ └── claude-md-integration.md
|
||||||
│ └── viz-platform/ # NEW in v4.1.0
|
│ └── viz-platform/ # NEW in v4.1.0
|
||||||
│ ├── .claude-plugin/
|
│ ├── .claude-plugin/
|
||||||
│ ├── .mcp.json
|
│ ├── .mcp.json
|
||||||
@@ -269,6 +286,7 @@ plugins/cmdb-assistant/mcp-servers/netbox -> ../../../mcp-servers/netbox
|
|||||||
plugins/pr-review/mcp-servers/gitea -> ../../../mcp-servers/gitea
|
plugins/pr-review/mcp-servers/gitea -> ../../../mcp-servers/gitea
|
||||||
plugins/data-platform/mcp-servers/data-platform -> ../../../mcp-servers/data-platform
|
plugins/data-platform/mcp-servers/data-platform -> ../../../mcp-servers/data-platform
|
||||||
plugins/viz-platform/mcp-servers/viz-platform -> ../../../mcp-servers/viz-platform
|
plugins/viz-platform/mcp-servers/viz-platform -> ../../../mcp-servers/viz-platform
|
||||||
|
plugins/contract-validator/mcp-servers/contract-validator -> ../../../mcp-servers/contract-validator
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -277,6 +295,7 @@ plugins/viz-platform/mcp-servers/viz-platform -> ../../../mcp-servers/viz-platfo
|
|||||||
|
|
||||||
| Date | Change | By |
|
| Date | Change | By |
|
||||||
|------|--------|-----|
|
|------|--------|-----|
|
||||||
|
| 2026-01-26 | v5.0.0: Added contract-validator plugin and MCP server | Claude Code |
|
||||||
| 2026-01-26 | v4.1.0: Added viz-platform plugin and MCP server | Claude Code |
|
| 2026-01-26 | v4.1.0: Added viz-platform plugin and MCP server | Claude Code |
|
||||||
| 2026-01-25 | v4.0.0: Added data-platform plugin and MCP server | Claude Code |
|
| 2026-01-25 | v4.0.0: Added data-platform plugin and MCP server | Claude Code |
|
||||||
| 2026-01-20 | v3.0.0: MCP servers moved to root with symlinks | Claude Code |
|
| 2026-01-20 | v3.0.0: MCP servers moved to root with symlinks | Claude Code |
|
||||||
|
|||||||
1
mcp-servers/contract-validator/tests/__init__.py
Normal file
1
mcp-servers/contract-validator/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Tests for contract-validator MCP server
|
||||||
193
mcp-servers/contract-validator/tests/test_parse_tools.py
Normal file
193
mcp-servers/contract-validator/tests/test_parse_tools.py
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for parse tools.
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def parse_tools():
|
||||||
|
"""Create ParseTools instance"""
|
||||||
|
from mcp_server.parse_tools import ParseTools
|
||||||
|
return ParseTools()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sample_readme(tmp_path):
|
||||||
|
"""Create a sample README.md for testing"""
|
||||||
|
readme = tmp_path / "README.md"
|
||||||
|
readme.write_text("""# Test Plugin
|
||||||
|
|
||||||
|
A test plugin for validation.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Feature One**: Does something
|
||||||
|
- **Feature Two**: Does something else
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/test-cmd` | Test command |
|
||||||
|
| `/another-cmd` | Another test command |
|
||||||
|
|
||||||
|
## Agents
|
||||||
|
|
||||||
|
| Agent | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `test-agent` | A test agent |
|
||||||
|
|
||||||
|
## Tools Summary
|
||||||
|
|
||||||
|
### Category A (3 tools)
|
||||||
|
`tool_a`, `tool_b`, `tool_c`
|
||||||
|
|
||||||
|
### Category B (2 tools)
|
||||||
|
`tool_d`, `tool_e`
|
||||||
|
""")
|
||||||
|
return str(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sample_claude_md(tmp_path):
|
||||||
|
"""Create a sample CLAUDE.md for testing"""
|
||||||
|
claude_md = tmp_path / "CLAUDE.md"
|
||||||
|
claude_md.write_text("""# CLAUDE.md
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
### Four-Agent Model (test)
|
||||||
|
|
||||||
|
| Agent | Personality | Responsibilities |
|
||||||
|
|-------|-------------|------------------|
|
||||||
|
| **Planner** | Thoughtful | Planning via `create_issue`, `search_lessons` |
|
||||||
|
| **Executor** | Focused | Implementation via `write`, `edit` |
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Planner creates issues
|
||||||
|
2. Executor implements code
|
||||||
|
""")
|
||||||
|
return str(claude_md)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parse_plugin_interface_basic(parse_tools, sample_readme):
|
||||||
|
"""Test basic plugin interface parsing"""
|
||||||
|
result = await parse_tools.parse_plugin_interface(sample_readme)
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
# Plugin name extraction strips "Plugin" suffix
|
||||||
|
assert result["plugin_name"] == "Test"
|
||||||
|
assert "A test plugin" in result["description"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parse_plugin_interface_commands(parse_tools, sample_readme):
|
||||||
|
"""Test command extraction from README"""
|
||||||
|
result = await parse_tools.parse_plugin_interface(sample_readme)
|
||||||
|
|
||||||
|
commands = result["commands"]
|
||||||
|
assert len(commands) == 2
|
||||||
|
assert commands[0]["name"] == "/test-cmd"
|
||||||
|
assert commands[1]["name"] == "/another-cmd"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parse_plugin_interface_agents(parse_tools, sample_readme):
|
||||||
|
"""Test agent extraction from README"""
|
||||||
|
result = await parse_tools.parse_plugin_interface(sample_readme)
|
||||||
|
|
||||||
|
agents = result["agents"]
|
||||||
|
assert len(agents) == 1
|
||||||
|
assert agents[0]["name"] == "test-agent"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parse_plugin_interface_tools(parse_tools, sample_readme):
|
||||||
|
"""Test tool extraction from README"""
|
||||||
|
result = await parse_tools.parse_plugin_interface(sample_readme)
|
||||||
|
|
||||||
|
tools = result["tools"]
|
||||||
|
tool_names = [t["name"] for t in tools]
|
||||||
|
assert "tool_a" in tool_names
|
||||||
|
assert "tool_b" in tool_names
|
||||||
|
assert "tool_e" in tool_names
|
||||||
|
assert len(tools) >= 5
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parse_plugin_interface_categories(parse_tools, sample_readme):
|
||||||
|
"""Test tool category extraction"""
|
||||||
|
result = await parse_tools.parse_plugin_interface(sample_readme)
|
||||||
|
|
||||||
|
categories = result["tool_categories"]
|
||||||
|
assert "Category A" in categories
|
||||||
|
assert "Category B" in categories
|
||||||
|
assert "tool_a" in categories["Category A"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parse_plugin_interface_features(parse_tools, sample_readme):
|
||||||
|
"""Test feature extraction"""
|
||||||
|
result = await parse_tools.parse_plugin_interface(sample_readme)
|
||||||
|
|
||||||
|
features = result["features"]
|
||||||
|
assert "Feature One" in features
|
||||||
|
assert "Feature Two" in features
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parse_plugin_interface_not_found(parse_tools, tmp_path):
|
||||||
|
"""Test error when README not found"""
|
||||||
|
result = await parse_tools.parse_plugin_interface(str(tmp_path / "nonexistent"))
|
||||||
|
|
||||||
|
assert "error" in result
|
||||||
|
assert "not found" in result["error"].lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parse_claude_md_agents(parse_tools, sample_claude_md):
|
||||||
|
"""Test agent extraction from CLAUDE.md"""
|
||||||
|
result = await parse_tools.parse_claude_md_agents(sample_claude_md)
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
assert result["agent_count"] == 2
|
||||||
|
|
||||||
|
agents = result["agents"]
|
||||||
|
agent_names = [a["name"] for a in agents]
|
||||||
|
assert "Planner" in agent_names
|
||||||
|
assert "Executor" in agent_names
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parse_claude_md_tool_refs(parse_tools, sample_claude_md):
|
||||||
|
"""Test tool reference extraction from agents"""
|
||||||
|
result = await parse_tools.parse_claude_md_agents(sample_claude_md)
|
||||||
|
|
||||||
|
agents = {a["name"]: a for a in result["agents"]}
|
||||||
|
planner = agents["Planner"]
|
||||||
|
|
||||||
|
assert "create_issue" in planner["tool_refs"]
|
||||||
|
assert "search_lessons" in planner["tool_refs"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parse_claude_md_not_found(parse_tools, tmp_path):
|
||||||
|
"""Test error when CLAUDE.md not found"""
|
||||||
|
result = await parse_tools.parse_claude_md_agents(str(tmp_path / "CLAUDE.md"))
|
||||||
|
|
||||||
|
assert "error" in result
|
||||||
|
assert "not found" in result["error"].lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parse_plugin_with_direct_file(parse_tools, sample_readme):
|
||||||
|
"""Test parsing with direct file path instead of directory"""
|
||||||
|
readme_path = Path(sample_readme) / "README.md"
|
||||||
|
result = await parse_tools.parse_plugin_interface(str(readme_path))
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
# Plugin name extraction strips "Plugin" suffix
|
||||||
|
assert result["plugin_name"] == "Test"
|
||||||
261
mcp-servers/contract-validator/tests/test_report_tools.py
Normal file
261
mcp-servers/contract-validator/tests/test_report_tools.py
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for report tools.
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def report_tools():
|
||||||
|
"""Create ReportTools instance"""
|
||||||
|
from mcp_server.report_tools import ReportTools
|
||||||
|
return ReportTools()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sample_marketplace(tmp_path):
|
||||||
|
"""Create a sample marketplace structure"""
|
||||||
|
import json
|
||||||
|
|
||||||
|
plugins_dir = tmp_path / "plugins"
|
||||||
|
plugins_dir.mkdir()
|
||||||
|
|
||||||
|
# Plugin 1
|
||||||
|
plugin1 = plugins_dir / "plugin-one"
|
||||||
|
plugin1.mkdir()
|
||||||
|
plugin1_meta = plugin1 / ".claude-plugin"
|
||||||
|
plugin1_meta.mkdir()
|
||||||
|
(plugin1_meta / "plugin.json").write_text(json.dumps({"name": "plugin-one"}))
|
||||||
|
(plugin1 / "README.md").write_text("""# plugin-one
|
||||||
|
|
||||||
|
First test plugin.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/cmd-one` | Command one |
|
||||||
|
|
||||||
|
## Tools Summary
|
||||||
|
|
||||||
|
### Tools (2 tools)
|
||||||
|
`tool_a`, `tool_b`
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Plugin 2
|
||||||
|
plugin2 = plugins_dir / "plugin-two"
|
||||||
|
plugin2.mkdir()
|
||||||
|
plugin2_meta = plugin2 / ".claude-plugin"
|
||||||
|
plugin2_meta.mkdir()
|
||||||
|
(plugin2_meta / "plugin.json").write_text(json.dumps({"name": "plugin-two"}))
|
||||||
|
(plugin2 / "README.md").write_text("""# plugin-two
|
||||||
|
|
||||||
|
Second test plugin.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/cmd-two` | Command two |
|
||||||
|
|
||||||
|
## Tools Summary
|
||||||
|
|
||||||
|
### Tools (2 tools)
|
||||||
|
`tool_c`, `tool_d`
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Plugin 3 (with conflict)
|
||||||
|
plugin3 = plugins_dir / "plugin-three"
|
||||||
|
plugin3.mkdir()
|
||||||
|
plugin3_meta = plugin3 / ".claude-plugin"
|
||||||
|
plugin3_meta.mkdir()
|
||||||
|
(plugin3_meta / "plugin.json").write_text(json.dumps({"name": "plugin-three"}))
|
||||||
|
(plugin3 / "README.md").write_text("""# plugin-three
|
||||||
|
|
||||||
|
Third test plugin with conflict.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/cmd-one` | Conflicting command |
|
||||||
|
|
||||||
|
## Tools Summary
|
||||||
|
|
||||||
|
### Tools (1 tool)
|
||||||
|
`tool_e`
|
||||||
|
""")
|
||||||
|
|
||||||
|
return str(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def marketplace_no_plugins(tmp_path):
|
||||||
|
"""Create marketplace with no plugins"""
|
||||||
|
plugins_dir = tmp_path / "plugins"
|
||||||
|
plugins_dir.mkdir()
|
||||||
|
return str(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def marketplace_no_dir(tmp_path):
|
||||||
|
"""Create path without plugins directory"""
|
||||||
|
return str(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_generate_report_json_format(report_tools, sample_marketplace):
|
||||||
|
"""Test JSON format report generation"""
|
||||||
|
result = await report_tools.generate_compatibility_report(
|
||||||
|
sample_marketplace, "json"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
assert "generated_at" in result
|
||||||
|
assert "summary" in result
|
||||||
|
assert "plugins" in result
|
||||||
|
assert result["summary"]["total_plugins"] == 3
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_generate_report_markdown_format(report_tools, sample_marketplace):
|
||||||
|
"""Test markdown format report generation"""
|
||||||
|
result = await report_tools.generate_compatibility_report(
|
||||||
|
sample_marketplace, "markdown"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
assert "report" in result
|
||||||
|
assert "# Contract Validation Report" in result["report"]
|
||||||
|
assert "## Summary" in result["report"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_generate_report_finds_conflicts(report_tools, sample_marketplace):
|
||||||
|
"""Test that report finds command conflicts"""
|
||||||
|
result = await report_tools.generate_compatibility_report(
|
||||||
|
sample_marketplace, "json"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
assert result["summary"]["errors"] > 0
|
||||||
|
assert result["summary"]["total_issues"] > 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_generate_report_counts_correctly(report_tools, sample_marketplace):
|
||||||
|
"""Test summary counts are correct"""
|
||||||
|
result = await report_tools.generate_compatibility_report(
|
||||||
|
sample_marketplace, "json"
|
||||||
|
)
|
||||||
|
|
||||||
|
summary = result["summary"]
|
||||||
|
assert summary["total_plugins"] == 3
|
||||||
|
assert summary["total_commands"] == 3 # 3 commands total
|
||||||
|
assert summary["total_tools"] == 5 # a, b, c, d, e
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_generate_report_no_plugins(report_tools, marketplace_no_plugins):
|
||||||
|
"""Test error when no plugins found"""
|
||||||
|
result = await report_tools.generate_compatibility_report(
|
||||||
|
marketplace_no_plugins, "json"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "error" in result
|
||||||
|
assert "no plugins" in result["error"].lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_generate_report_no_plugins_dir(report_tools, marketplace_no_dir):
|
||||||
|
"""Test error when plugins directory doesn't exist"""
|
||||||
|
result = await report_tools.generate_compatibility_report(
|
||||||
|
marketplace_no_dir, "json"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "error" in result
|
||||||
|
assert "not found" in result["error"].lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_list_issues_all(report_tools, sample_marketplace):
|
||||||
|
"""Test listing all issues"""
|
||||||
|
result = await report_tools.list_issues(sample_marketplace, "all", "all")
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
assert "issues" in result
|
||||||
|
assert result["total_issues"] > 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_list_issues_filter_by_severity(report_tools, sample_marketplace):
|
||||||
|
"""Test filtering issues by severity"""
|
||||||
|
all_result = await report_tools.list_issues(sample_marketplace, "all", "all")
|
||||||
|
error_result = await report_tools.list_issues(sample_marketplace, "error", "all")
|
||||||
|
|
||||||
|
# Error count should be less than or equal to all
|
||||||
|
assert error_result["total_issues"] <= all_result["total_issues"]
|
||||||
|
|
||||||
|
# All issues should have error severity
|
||||||
|
for issue in error_result["issues"]:
|
||||||
|
sev = issue.get("severity", "")
|
||||||
|
if hasattr(sev, 'value'):
|
||||||
|
sev = sev.value
|
||||||
|
assert "error" in str(sev).lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_list_issues_filter_by_type(report_tools, sample_marketplace):
|
||||||
|
"""Test filtering issues by type"""
|
||||||
|
result = await report_tools.list_issues(
|
||||||
|
sample_marketplace, "all", "interface_mismatch"
|
||||||
|
)
|
||||||
|
|
||||||
|
# All issues should have matching type
|
||||||
|
for issue in result["issues"]:
|
||||||
|
itype = issue.get("issue_type", "")
|
||||||
|
if hasattr(itype, 'value'):
|
||||||
|
itype = itype.value
|
||||||
|
assert "interface_mismatch" in str(itype).lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_list_issues_combined_filters(report_tools, sample_marketplace):
|
||||||
|
"""Test combined severity and type filters"""
|
||||||
|
result = await report_tools.list_issues(
|
||||||
|
sample_marketplace, "error", "interface_mismatch"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
# Should have command conflict errors
|
||||||
|
assert result["total_issues"] > 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_report_markdown_has_all_sections(report_tools, sample_marketplace):
|
||||||
|
"""Test markdown report contains all expected sections"""
|
||||||
|
result = await report_tools.generate_compatibility_report(
|
||||||
|
sample_marketplace, "markdown"
|
||||||
|
)
|
||||||
|
|
||||||
|
report = result["report"]
|
||||||
|
assert "## Summary" in report
|
||||||
|
assert "## Plugins" in report
|
||||||
|
# Compatibility section only if there are checks
|
||||||
|
assert "Plugin One" in report or "plugin-one" in report.lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_report_includes_suggestions(report_tools, sample_marketplace):
|
||||||
|
"""Test that issues include suggestions"""
|
||||||
|
result = await report_tools.generate_compatibility_report(
|
||||||
|
sample_marketplace, "json"
|
||||||
|
)
|
||||||
|
|
||||||
|
issues = result.get("all_issues", [])
|
||||||
|
# Find an issue with a suggestion
|
||||||
|
issues_with_suggestions = [
|
||||||
|
i for i in issues
|
||||||
|
if i.get("suggestion")
|
||||||
|
]
|
||||||
|
assert len(issues_with_suggestions) > 0
|
||||||
256
mcp-servers/contract-validator/tests/test_validation_tools.py
Normal file
256
mcp-servers/contract-validator/tests/test_validation_tools.py
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for validation tools.
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def validation_tools():
|
||||||
|
"""Create ValidationTools instance"""
|
||||||
|
from mcp_server.validation_tools import ValidationTools
|
||||||
|
return ValidationTools()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def plugin_a(tmp_path):
|
||||||
|
"""Create first test plugin"""
|
||||||
|
plugin_dir = tmp_path / "plugin-a"
|
||||||
|
plugin_dir.mkdir()
|
||||||
|
(plugin_dir / ".claude-plugin").mkdir()
|
||||||
|
|
||||||
|
readme = plugin_dir / "README.md"
|
||||||
|
readme.write_text("""# Plugin A
|
||||||
|
|
||||||
|
Test plugin A.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/setup-a` | Setup A |
|
||||||
|
| `/shared-cmd` | Shared command |
|
||||||
|
|
||||||
|
## Tools Summary
|
||||||
|
|
||||||
|
### Core (2 tools)
|
||||||
|
`tool_one`, `tool_two`
|
||||||
|
""")
|
||||||
|
return str(plugin_dir)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def plugin_b(tmp_path):
|
||||||
|
"""Create second test plugin"""
|
||||||
|
plugin_dir = tmp_path / "plugin-b"
|
||||||
|
plugin_dir.mkdir()
|
||||||
|
(plugin_dir / ".claude-plugin").mkdir()
|
||||||
|
|
||||||
|
readme = plugin_dir / "README.md"
|
||||||
|
readme.write_text("""# Plugin B
|
||||||
|
|
||||||
|
Test plugin B.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/setup-b` | Setup B |
|
||||||
|
| `/shared-cmd` | Shared command (conflict!) |
|
||||||
|
|
||||||
|
## Tools Summary
|
||||||
|
|
||||||
|
### Core (2 tools)
|
||||||
|
`tool_two`, `tool_three`
|
||||||
|
""")
|
||||||
|
return str(plugin_dir)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def plugin_no_conflict(tmp_path):
|
||||||
|
"""Create plugin with no conflicts"""
|
||||||
|
plugin_dir = tmp_path / "plugin-c"
|
||||||
|
plugin_dir.mkdir()
|
||||||
|
(plugin_dir / ".claude-plugin").mkdir()
|
||||||
|
|
||||||
|
readme = plugin_dir / "README.md"
|
||||||
|
readme.write_text("""# Plugin C
|
||||||
|
|
||||||
|
Test plugin C.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/unique-cmd` | Unique command |
|
||||||
|
|
||||||
|
## Tools Summary
|
||||||
|
|
||||||
|
### Core (1 tool)
|
||||||
|
`unique_tool`
|
||||||
|
""")
|
||||||
|
return str(plugin_dir)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def claude_md_with_agents(tmp_path):
|
||||||
|
"""Create CLAUDE.md with agent definitions"""
|
||||||
|
claude_md = tmp_path / "CLAUDE.md"
|
||||||
|
claude_md.write_text("""# CLAUDE.md
|
||||||
|
|
||||||
|
### Four-Agent Model
|
||||||
|
|
||||||
|
| Agent | Personality | Responsibilities |
|
||||||
|
|-------|-------------|------------------|
|
||||||
|
| **TestAgent** | Careful | Uses `tool_one`, `tool_two`, `missing_tool` |
|
||||||
|
| **ValidAgent** | Thorough | Uses `tool_one` only |
|
||||||
|
| **EmptyAgent** | Unknown | General tasks |
|
||||||
|
""")
|
||||||
|
return str(claude_md)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_compatibility_command_conflict(validation_tools, plugin_a, plugin_b):
|
||||||
|
"""Test detection of command name conflicts"""
|
||||||
|
result = await validation_tools.validate_compatibility(plugin_a, plugin_b)
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
assert result["compatible"] is False
|
||||||
|
|
||||||
|
# Find the command conflict issue
|
||||||
|
error_issues = [i for i in result["issues"] if i["severity"].value == "error"]
|
||||||
|
assert len(error_issues) > 0
|
||||||
|
assert any("/shared-cmd" in str(i["message"]) for i in error_issues)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_compatibility_tool_overlap(validation_tools, plugin_a, plugin_b):
|
||||||
|
"""Test detection of tool name overlaps"""
|
||||||
|
result = await validation_tools.validate_compatibility(plugin_a, plugin_b)
|
||||||
|
|
||||||
|
assert "tool_two" in result["shared_tools"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_compatibility_unique_tools(validation_tools, plugin_a, plugin_b):
|
||||||
|
"""Test identification of unique tools per plugin"""
|
||||||
|
result = await validation_tools.validate_compatibility(plugin_a, plugin_b)
|
||||||
|
|
||||||
|
assert "tool_one" in result["a_only_tools"]
|
||||||
|
assert "tool_three" in result["b_only_tools"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_compatibility_no_conflict(validation_tools, plugin_a, plugin_no_conflict):
|
||||||
|
"""Test compatible plugins"""
|
||||||
|
result = await validation_tools.validate_compatibility(plugin_a, plugin_no_conflict)
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
assert result["compatible"] is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_compatibility_missing_plugin(validation_tools, plugin_a, tmp_path):
|
||||||
|
"""Test error when plugin not found"""
|
||||||
|
result = await validation_tools.validate_compatibility(
|
||||||
|
plugin_a,
|
||||||
|
str(tmp_path / "nonexistent")
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "error" in result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_agent_refs_with_missing_tools(validation_tools, claude_md_with_agents, plugin_a):
|
||||||
|
"""Test detection of missing tool references"""
|
||||||
|
result = await validation_tools.validate_agent_refs(
|
||||||
|
"TestAgent",
|
||||||
|
claude_md_with_agents,
|
||||||
|
[plugin_a]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
assert result["valid"] is False
|
||||||
|
assert "missing_tool" in result["tool_refs_missing"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_agent_refs_valid_agent(validation_tools, claude_md_with_agents, plugin_a):
|
||||||
|
"""Test valid agent with all tools found"""
|
||||||
|
result = await validation_tools.validate_agent_refs(
|
||||||
|
"ValidAgent",
|
||||||
|
claude_md_with_agents,
|
||||||
|
[plugin_a]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
assert result["valid"] is True
|
||||||
|
assert "tool_one" in result["tool_refs_found"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_agent_refs_empty_agent(validation_tools, claude_md_with_agents, plugin_a):
|
||||||
|
"""Test agent with no tool references"""
|
||||||
|
result = await validation_tools.validate_agent_refs(
|
||||||
|
"EmptyAgent",
|
||||||
|
claude_md_with_agents,
|
||||||
|
[plugin_a]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
# Should have info issue about undocumented references
|
||||||
|
info_issues = [i for i in result["issues"] if i["severity"].value == "info"]
|
||||||
|
assert len(info_issues) > 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_agent_refs_agent_not_found(validation_tools, claude_md_with_agents, plugin_a):
|
||||||
|
"""Test error when agent not found"""
|
||||||
|
result = await validation_tools.validate_agent_refs(
|
||||||
|
"NonexistentAgent",
|
||||||
|
claude_md_with_agents,
|
||||||
|
[plugin_a]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "error" in result
|
||||||
|
assert "not found" in result["error"].lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_data_flow_valid(validation_tools, tmp_path):
|
||||||
|
"""Test data flow validation with valid flow"""
|
||||||
|
claude_md = tmp_path / "CLAUDE.md"
|
||||||
|
claude_md.write_text("""# CLAUDE.md
|
||||||
|
|
||||||
|
### Four-Agent Model
|
||||||
|
|
||||||
|
| Agent | Personality | Responsibilities |
|
||||||
|
|-------|-------------|------------------|
|
||||||
|
| **DataAgent** | Analytical | Load with `read_csv`, analyze with `describe`, export with `to_csv` |
|
||||||
|
""")
|
||||||
|
|
||||||
|
result = await validation_tools.validate_data_flow("DataAgent", str(claude_md))
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_data_flow_missing_producer(validation_tools, tmp_path):
|
||||||
|
"""Test data flow with consumer but no producer"""
|
||||||
|
claude_md = tmp_path / "CLAUDE.md"
|
||||||
|
claude_md.write_text("""# CLAUDE.md
|
||||||
|
|
||||||
|
### Four-Agent Model
|
||||||
|
|
||||||
|
| Agent | Personality | Responsibilities |
|
||||||
|
|-------|-------------|------------------|
|
||||||
|
| **BadAgent** | Careless | Just runs `describe`, `head`, `tail` without loading |
|
||||||
|
""")
|
||||||
|
|
||||||
|
result = await validation_tools.validate_data_flow("BadAgent", str(claude_md))
|
||||||
|
|
||||||
|
assert "error" not in result
|
||||||
|
# Should have warning about missing producer
|
||||||
|
warning_issues = [i for i in result["issues"] if i["severity"].value == "warning"]
|
||||||
|
assert len(warning_issues) > 0
|
||||||
156
plugins/contract-validator/README.md
Normal file
156
plugins/contract-validator/README.md
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
# contract-validator Plugin
|
||||||
|
|
||||||
|
Cross-plugin compatibility validation and CLAUDE.md agent verification for Claude Code plugin marketplaces.
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
|
||||||
|
As plugin marketplaces grow, several compatibility issues emerge:
|
||||||
|
|
||||||
|
- **Command conflicts**: Multiple plugins defining the same slash command (e.g., `/initial-setup`)
|
||||||
|
- **Tool name overlaps**: Different plugins using identical tool names with incompatible interfaces
|
||||||
|
- **Undocumented dependencies**: Agents referencing tools that don't exist
|
||||||
|
- **Broken data flows**: Agent sequences that expect outputs not produced by prior steps
|
||||||
|
|
||||||
|
Contract-validator solves these by parsing plugin interfaces and validating compatibility before runtime.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Interface Parsing**: Extract commands, agents, and tools from plugin README.md files
|
||||||
|
- **Agent Extraction**: Parse CLAUDE.md Four-Agent Model tables and Agents sections
|
||||||
|
- **Compatibility Checks**: Pairwise validation between all plugins in a marketplace
|
||||||
|
- **Data Flow Validation**: Verify agent tool sequences have valid data producers/consumers
|
||||||
|
- **Comprehensive Reports**: Markdown or JSON reports with actionable suggestions
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This plugin is part of the leo-claude-mktplace. Install via:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From marketplace
|
||||||
|
claude plugins install leo-claude-mktplace/contract-validator
|
||||||
|
|
||||||
|
# Setup MCP server venv
|
||||||
|
cd ~/.claude/plugins/marketplaces/leo-claude-mktplace/mcp-servers/contract-validator
|
||||||
|
python -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/validate-contracts` | Full marketplace compatibility validation |
|
||||||
|
| `/check-agent` | Validate single agent definition |
|
||||||
|
| `/list-interfaces` | Show all plugin interfaces |
|
||||||
|
|
||||||
|
## Agents
|
||||||
|
|
||||||
|
| Agent | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `full-validation` | Complete cross-plugin compatibility validation |
|
||||||
|
| `agent-check` | Single agent definition verification |
|
||||||
|
|
||||||
|
## Tools Summary
|
||||||
|
|
||||||
|
### Parse Tools (2)
|
||||||
|
- `parse_plugin_interface` - Extract interface from plugin README.md
|
||||||
|
- `parse_claude_md_agents` - Extract agents from CLAUDE.md
|
||||||
|
|
||||||
|
### Validation Tools (3)
|
||||||
|
- `validate_compatibility` - Check two plugins for conflicts
|
||||||
|
- `validate_agent_refs` - Verify agent tool references exist
|
||||||
|
- `validate_data_flow` - Check data flow through agent sequences
|
||||||
|
|
||||||
|
### Report Tools (2)
|
||||||
|
- `generate_compatibility_report` - Full marketplace validation report
|
||||||
|
- `list_issues` - Filter issues by severity or type
|
||||||
|
|
||||||
|
## Example Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
/validate-contracts ~/claude-plugins-work
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
# Contract Validation Report
|
||||||
|
#
|
||||||
|
# | Metric | Count |
|
||||||
|
# |------------|-------|
|
||||||
|
# | Plugins | 12 |
|
||||||
|
# | Commands | 39 |
|
||||||
|
# | Tools | 32 |
|
||||||
|
# | **Issues** | **7** |
|
||||||
|
# | - Errors | 3 |
|
||||||
|
# | - Warnings | 0 |
|
||||||
|
# | - Info | 4 |
|
||||||
|
#
|
||||||
|
# ## Issues Found
|
||||||
|
# [ERROR] Command conflict: projman and data-platform both define /initial-setup
|
||||||
|
# [ERROR] Command conflict: projman and pr-review both define /initial-setup
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
/check-agent Planner ./CLAUDE.md
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
# Agent: Planner
|
||||||
|
# Status: VALID
|
||||||
|
#
|
||||||
|
# Tool References Found (3):
|
||||||
|
# - create_issue ✓
|
||||||
|
# - search_lessons ✓
|
||||||
|
# - get_execution_order ✓
|
||||||
|
#
|
||||||
|
# Data Flow: No issues detected
|
||||||
|
```
|
||||||
|
|
||||||
|
## Issue Types
|
||||||
|
|
||||||
|
| Type | Severity | Description |
|
||||||
|
|------|----------|-------------|
|
||||||
|
| `interface_mismatch` | ERROR | Command name conflict between plugins |
|
||||||
|
| `missing_tool` | ERROR | Agent references non-existent tool |
|
||||||
|
| `interface_mismatch` | WARNING | Tool name overlap (different plugins) |
|
||||||
|
| `optional_dependency` | WARNING | Agent uses tool from non-required plugin |
|
||||||
|
| `undeclared_output` | INFO | Agent has no documented tool references |
|
||||||
|
|
||||||
|
## Parsed Interface Structure
|
||||||
|
|
||||||
|
When parsing a plugin README.md, the following structure is extracted:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"plugin_name": "data-platform",
|
||||||
|
"description": "Data engineering tools...",
|
||||||
|
"commands": [
|
||||||
|
{"name": "/ingest", "description": "Load data..."}
|
||||||
|
],
|
||||||
|
"agents": [
|
||||||
|
{"name": "data-analysis", "description": "..."}
|
||||||
|
],
|
||||||
|
"tools": [
|
||||||
|
{"name": "read_csv", "category": "pandas"}
|
||||||
|
],
|
||||||
|
"tool_categories": {
|
||||||
|
"pandas": ["read_csv", "to_csv", ...],
|
||||||
|
"PostgreSQL": ["pg_query", ...]
|
||||||
|
},
|
||||||
|
"features": ["pandas Operations", "PostgreSQL/PostGIS", ...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### For Plugin Authors
|
||||||
|
|
||||||
|
1. **Use unique command names**: Prefix with plugin name if generic (e.g., `/data-setup` vs `/initial-setup`)
|
||||||
|
2. **Document all tools**: Include tool names in README.md with backticks
|
||||||
|
3. **Specify tool categories**: Use `### Category (N tools)` headers
|
||||||
|
4. **Declare agent tools**: List tools used by agents in their definitions
|
||||||
|
|
||||||
|
### For Marketplace Maintainers
|
||||||
|
|
||||||
|
1. **Run validation before merging**: Use `/validate-contracts` in CI/CD
|
||||||
|
2. **Review warnings**: Tool overlaps may indicate design issues
|
||||||
|
3. **Track issues over time**: Use JSON format for programmatic tracking
|
||||||
90
plugins/contract-validator/agents/agent-check.md
Normal file
90
plugins/contract-validator/agents/agent-check.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# Agent Check Agent
|
||||||
|
|
||||||
|
You are an agent definition validator. Your role is to verify that a specific agent's tool references and data flow are valid.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
- Parse agent definitions from CLAUDE.md
|
||||||
|
- Validate tool references against available plugins
|
||||||
|
- Verify data flow patterns through agent sequences
|
||||||
|
- Provide detailed validation feedback
|
||||||
|
|
||||||
|
## Available Tools
|
||||||
|
|
||||||
|
### Parsing
|
||||||
|
- `parse_claude_md_agents` - Extract all agents from CLAUDE.md
|
||||||
|
- `parse_plugin_interface` - Extract interface from plugin (for available tools)
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
- `validate_agent_refs` - Check agent tool references exist
|
||||||
|
- `validate_data_flow` - Verify data flow through agent sequence
|
||||||
|
|
||||||
|
### Reporting
|
||||||
|
- `list_issues` - Filter issues for this agent
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Locate the agent**:
|
||||||
|
- Use `parse_claude_md_agents` on specified CLAUDE.md
|
||||||
|
- Find agent by name (case-insensitive match)
|
||||||
|
- If not found, list available agents
|
||||||
|
|
||||||
|
2. **Gather available tools**:
|
||||||
|
- Scan plugins directory for available plugins
|
||||||
|
- For each plugin, use `parse_plugin_interface`
|
||||||
|
- Build set of all available tool names
|
||||||
|
|
||||||
|
3. **Validate tool references**:
|
||||||
|
- Use `validate_agent_refs` with agent name and plugin paths
|
||||||
|
- Report found tools (valid references)
|
||||||
|
- Report missing tools (errors)
|
||||||
|
- Suggest corrections for typos
|
||||||
|
|
||||||
|
4. **Validate data flow**:
|
||||||
|
- Use `validate_data_flow` to check sequence
|
||||||
|
- Verify data producers precede consumers
|
||||||
|
- Check for orphaned data references
|
||||||
|
- Identify potential flow issues
|
||||||
|
|
||||||
|
5. **Report findings**:
|
||||||
|
- Agent name and source file
|
||||||
|
- Responsibilities extracted
|
||||||
|
- Tool references: found vs missing
|
||||||
|
- Data flow validation results
|
||||||
|
- Suggestions for improvement
|
||||||
|
|
||||||
|
## Validation Rules
|
||||||
|
|
||||||
|
### Tool Reference Rules
|
||||||
|
- All referenced tools must exist in available plugins
|
||||||
|
- Tool names are case-sensitive
|
||||||
|
- Partial matches suggest typos
|
||||||
|
|
||||||
|
### Data Flow Rules
|
||||||
|
- Data producers (read_csv, pg_query, etc.) should precede consumers
|
||||||
|
- Data consumers (describe, head, to_csv, etc.) need valid data_ref
|
||||||
|
- Workflow steps should have logical sequence
|
||||||
|
|
||||||
|
## Issue Severities
|
||||||
|
|
||||||
|
- **ERROR**: Tool reference not found - agent will fail
|
||||||
|
- **WARNING**: Data flow issue - agent may produce unexpected results
|
||||||
|
- **INFO**: Undocumented reference - consider adding documentation
|
||||||
|
|
||||||
|
## Example Interaction
|
||||||
|
|
||||||
|
**User**: /check-agent Orchestrator
|
||||||
|
|
||||||
|
**Agent**:
|
||||||
|
1. Parses CLAUDE.md, finds Orchestrator agent
|
||||||
|
2. Extracts responsibilities: "Sprint execution, parallel batching, Git operations"
|
||||||
|
3. Finds tool refs: create_issue, update_issue, search_lessons
|
||||||
|
4. Validates against plugins: all tools found in projman/gitea
|
||||||
|
5. Validates data flow: no data producers/consumers used
|
||||||
|
6. Reports: "Agent Orchestrator: VALID - all 3 tool references found"
|
||||||
|
|
||||||
|
**User**: /check-agent InvalidAgent
|
||||||
|
|
||||||
|
**Agent**:
|
||||||
|
1. Parses CLAUDE.md, agent not found
|
||||||
|
2. Reports: "Agent 'InvalidAgent' not found. Available agents: Planner, Orchestrator, Executor, Code Reviewer"
|
||||||
87
plugins/contract-validator/agents/full-validation.md
Normal file
87
plugins/contract-validator/agents/full-validation.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# Full Validation Agent
|
||||||
|
|
||||||
|
You are a contract validation specialist. Your role is to perform comprehensive cross-plugin compatibility validation for the entire marketplace.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
- Parse plugin interfaces from README.md files
|
||||||
|
- Parse agent definitions from CLAUDE.md files
|
||||||
|
- Validate cross-plugin compatibility
|
||||||
|
- Identify interface mismatches and conflicts
|
||||||
|
- Generate detailed validation reports
|
||||||
|
|
||||||
|
## Available Tools
|
||||||
|
|
||||||
|
### Parsing
|
||||||
|
- `parse_plugin_interface` - Extract interface from plugin README.md
|
||||||
|
- `parse_claude_md_agents` - Extract agents from CLAUDE.md
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
- `validate_compatibility` - Check two plugins for conflicts
|
||||||
|
- `validate_agent_refs` - Verify agent tool references exist
|
||||||
|
- `validate_data_flow` - Check data flow through agent sequences
|
||||||
|
|
||||||
|
### Reporting
|
||||||
|
- `generate_compatibility_report` - Full marketplace report
|
||||||
|
- `list_issues` - Filter issues by severity/type
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Discover plugins**:
|
||||||
|
- Locate marketplace plugins directory
|
||||||
|
- Identify plugins by `.claude-plugin/` marker
|
||||||
|
- Build list of all plugins to validate
|
||||||
|
|
||||||
|
2. **Parse all interfaces**:
|
||||||
|
- For each plugin, use `parse_plugin_interface`
|
||||||
|
- Extract commands, agents, tools from README.md
|
||||||
|
- Track tool categories and features
|
||||||
|
|
||||||
|
3. **Run pairwise compatibility checks**:
|
||||||
|
- For each pair of plugins, use `validate_compatibility`
|
||||||
|
- Check for command name conflicts (ERROR)
|
||||||
|
- Check for tool name overlaps (WARNING)
|
||||||
|
- Identify interface mismatches
|
||||||
|
|
||||||
|
4. **Validate CLAUDE.md agents** (if present):
|
||||||
|
- Use `parse_claude_md_agents` on project CLAUDE.md
|
||||||
|
- For each agent, use `validate_agent_refs`
|
||||||
|
- Use `validate_data_flow` to check sequences
|
||||||
|
|
||||||
|
5. **Generate comprehensive report**:
|
||||||
|
- Use `generate_compatibility_report`
|
||||||
|
- Format: markdown for human review, JSON for programmatic use
|
||||||
|
- Include summary statistics and detailed findings
|
||||||
|
|
||||||
|
## Report Structure
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
- Total plugins scanned
|
||||||
|
- Total commands, agents, tools found
|
||||||
|
- Issue counts by severity (error/warning/info)
|
||||||
|
|
||||||
|
### Compatibility Matrix
|
||||||
|
- Plugin pairs with conflicts
|
||||||
|
- Shared tools between plugins
|
||||||
|
- Unique tools per plugin
|
||||||
|
|
||||||
|
### Issues List
|
||||||
|
- ERROR: Command name conflicts (must fix)
|
||||||
|
- WARNING: Tool name overlaps (review needed)
|
||||||
|
- INFO: Undocumented references (improve docs)
|
||||||
|
|
||||||
|
### Recommendations
|
||||||
|
- Actionable suggestions per issue
|
||||||
|
- Priority order for fixes
|
||||||
|
|
||||||
|
## Example Interaction
|
||||||
|
|
||||||
|
**User**: /validate-contracts ~/claude-plugins-work
|
||||||
|
|
||||||
|
**Agent**:
|
||||||
|
1. Discovers 12 plugins in marketplace
|
||||||
|
2. Parses all README.md files
|
||||||
|
3. Runs 66 pairwise compatibility checks
|
||||||
|
4. Finds 3 errors, 4 warnings
|
||||||
|
5. Reports: "Command conflict: projman and data-platform both define /initial-setup"
|
||||||
|
6. Suggests: "Rename one command to avoid ambiguity"
|
||||||
152
plugins/contract-validator/claude-md-integration.md
Normal file
152
plugins/contract-validator/claude-md-integration.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# contract-validator Plugin - CLAUDE.md Integration
|
||||||
|
|
||||||
|
Add this section to your marketplace or project's CLAUDE.md to enable contract validation features.
|
||||||
|
|
||||||
|
## Suggested CLAUDE.md Section
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Contract Validation
|
||||||
|
|
||||||
|
This marketplace uses the contract-validator plugin for cross-plugin compatibility checks.
|
||||||
|
|
||||||
|
### Available Commands
|
||||||
|
|
||||||
|
| Command | Purpose |
|
||||||
|
|---------|---------|
|
||||||
|
| `/validate-contracts` | Full marketplace compatibility validation |
|
||||||
|
| `/check-agent` | Validate single agent definition |
|
||||||
|
| `/list-interfaces` | Show all plugin interfaces |
|
||||||
|
|
||||||
|
### Validation Workflow
|
||||||
|
|
||||||
|
Run before merging plugin changes:
|
||||||
|
|
||||||
|
1. `/validate-contracts` - Check for conflicts
|
||||||
|
2. Review errors (must fix) and warnings (should review)
|
||||||
|
3. Fix issues before merging
|
||||||
|
|
||||||
|
### Interface Documentation Standards
|
||||||
|
|
||||||
|
For plugins to be validated correctly, document interfaces in README.md:
|
||||||
|
|
||||||
|
**Commands Section:**
|
||||||
|
```markdown
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/my-command` | What it does |
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tools Section:**
|
||||||
|
```markdown
|
||||||
|
## Tools Summary
|
||||||
|
|
||||||
|
### Category (N tools)
|
||||||
|
`tool_a`, `tool_b`, `tool_c`
|
||||||
|
```
|
||||||
|
|
||||||
|
**Agents Section:**
|
||||||
|
```markdown
|
||||||
|
## Agents
|
||||||
|
|
||||||
|
| Agent | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `my-agent` | What it does |
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
## Declaring Agent Tool References
|
||||||
|
|
||||||
|
For agent validation to work, document tool usage in CLAUDE.md:
|
||||||
|
|
||||||
|
### Option 1: Four-Agent Model Table
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Four-Agent Model
|
||||||
|
|
||||||
|
| Agent | Personality | Responsibilities |
|
||||||
|
|-------|-------------|------------------|
|
||||||
|
| **Planner** | Methodical | Planning via `create_issue`, `search_lessons` |
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Agent Sections
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Planner Agent
|
||||||
|
|
||||||
|
Uses these tools:
|
||||||
|
- `create_issue` - Create planning issues
|
||||||
|
- `search_lessons` - Find relevant lessons
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices for Plugin Authors
|
||||||
|
|
||||||
|
### Unique Command Names
|
||||||
|
|
||||||
|
Avoid generic names that may conflict:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# BAD - Will conflict with other plugins
|
||||||
|
| `/setup` | Setup wizard |
|
||||||
|
|
||||||
|
# GOOD - Plugin-specific prefix
|
||||||
|
| `/data-setup` | Data platform setup wizard |
|
||||||
|
```
|
||||||
|
|
||||||
|
### Document All Tools
|
||||||
|
|
||||||
|
Ensure every MCP tool is listed in README.md:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Tools Summary
|
||||||
|
|
||||||
|
### pandas (14 tools)
|
||||||
|
`read_csv`, `read_parquet`, `read_json`, `to_csv`, `to_parquet`,
|
||||||
|
`describe`, `head`, `tail`, `filter`, `select`, `groupby`, `join`,
|
||||||
|
`list_data`, `drop_data`
|
||||||
|
```
|
||||||
|
|
||||||
|
### Specify Dependencies
|
||||||
|
|
||||||
|
If agents depend on tools from other plugins, document it:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
This agent uses tools from:
|
||||||
|
- `projman` - Issue management (`create_issue`, `update_issue`)
|
||||||
|
- `data-platform` - Data loading (`read_csv`, `describe`)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Typical Workflows
|
||||||
|
|
||||||
|
### Pre-Merge Validation
|
||||||
|
|
||||||
|
```
|
||||||
|
# Before merging new plugin
|
||||||
|
/validate-contracts
|
||||||
|
|
||||||
|
# Check specific agent after changes
|
||||||
|
/check-agent Orchestrator
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plugin Development
|
||||||
|
|
||||||
|
```
|
||||||
|
# See what interfaces exist
|
||||||
|
/list-interfaces
|
||||||
|
|
||||||
|
# After adding new command, verify no conflicts
|
||||||
|
/validate-contracts
|
||||||
|
```
|
||||||
|
|
||||||
|
### CI/CD Integration
|
||||||
|
|
||||||
|
Add to your pipeline:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Validate Plugin Contracts
|
||||||
|
run: |
|
||||||
|
claude --skill contract-validator:validate-contracts --args "${{ github.workspace }}"
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user