Context Manifest
Generated by context-gathering agent on 2026-02-13
How This Currently Works: Plugin Validation Architecture
The plugin-validator.py (3045 lines) implements a protocol-based validation framework with 9 existing validators that check skills, agents, commands, and plugin structure. When a file is validated:
- File Type Detection (lines 147-165):
FileType.detect_file_type()classifies files into 5 types (SKILL, AGENT, COMMAND, PLUGIN, UNKNOWN) based on filename and path patterns - Validator Selection (lines 2896-2913): Hard-coded dispatch selects validators per file type - skills get 7 validators, agents get 4, commands get 4, plugins get 1
- Single-File Read (optimization needed): Currently multiple validators may re-read same file - architecture spec requires reading once and passing content to all validators (lines 814-836 of architecture spec)
- Validation Execution: Each validator implements the Validator protocol (lines 249-286) with
validate(path: Path) -> ValidationResult,can_fix() -> bool,fix(path: Path) -> list[str] - Result Aggregation (lines 2865-2951): Collects ValidationResult objects from all validators
- Report Generation (lines 2599-2858): Three reporter implementations (Console, CI, Summary) format and display results
Data Models (lines 168-242):
ValidationIssue(frozen dataclass): field, severity, message, code, line, suggestion, docs_urlValidationResult(frozen dataclass): passed, errors, warnings, infoComplexityMetrics(frozen dataclass): total_tokens, frontmatter_tokens, body_tokens, encoding
Token-Based Complexity (lines 1939-2122): ComplexityValidator uses tiktoken library (cl100k_base encoding) to count tokens instead of lines. Thresholds: 4000 tokens warning (SK006), 10000 tokens error (SK007). Architecture at lines 44-45 defines these constants.
Pydantic Schema Validation (lines 1030-1236): Three frontmatter models:
SkillFrontmatter(10 fields, all optional, normalizes CSV fields)CommandFrontmatter(7 fields, description required, normalizes CSV)AgentFrontmatter(9+ fields, name+description required, uses camelCase per official schema)
Validation Flow per Validator:
- FrontmatterValidator (lines 1243-1600): Extracts YAML frontmatter, validates with Pydantic model, converts Pydantic errors to ValidationIssues, auto-fixes YAML arrays→CSV, multiline→single-line, removes name field from plugin skills (Claude Code bug workaround lines 1541-1560)
- NameFormatValidator (lines 1603-1787): Checks name pattern
^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$, no auto-fix - DescriptionValidator (lines 1790-1936): Checks min 20 chars (SK004), requires trigger phrases (SK005) from REQUIRED_TRIGGER_PHRASES constant (lines 56-64), no auto-fix
- ComplexityValidator (lines 1939-2122): Uses tiktoken to count tokens, splits frontmatter vs body, no auto-fix
- InternalLinkValidator (lines 448-631): Regex extracts markdown links, checks file existence, ignores code blocks (lines 468-490), no auto-fix
- ProgressiveDisclosureValidator (lines 334-441): Checks for references/, examples/, scripts/ directories, INFO severity only, no auto-fix
- NamespaceReferenceValidator (lines 638-1023): Parses namespace-qualified references (Skill(), Task(), @agent, /command), resolves from settings.json, checks target file exists, no auto-fix
- PluginStructureValidator (lines 2125-2414): Validates plugin.json schema, runs
claude plugin validateCLI if available (lines 2421-2500), no auto-fix
Known UX Bugs:
- Lines 2681-2709: ConsoleReporter counts ValidationResult objects not unique files (shows "7 files" for 1 file with 7 validators)
- Lines 904-911: Dead code in NamespaceReferenceValidator after early return (need coverage verification before removal)
- Line 1802: DescriptionValidator fires SK005 on commands (commands don't need trigger phrases since explicitly invoked)
Error Code Registry (lines 71-109): 23 existing codes across 6 categories:
- FM001-FM010: Frontmatter errors (10 codes)
- SK001-SK007: Skill errors (7 codes)
- LK001-LK002: Link errors (2 codes)
- PD001-PD003: Progressive disclosure (3 codes)
- PL001-PL005: Plugin errors (5 codes)
- NR001-NR002: Namespace reference errors (2 codes)
For New Feature Implementation: Hook/MCP/LSP Configuration Validation
Architecture Requirements (architect-plugin-linter.md):
Need to add 4 new FileType variants (lines 85-130 of architecture):
HOOK_CONFIG- Detectshooks.jsonexact filenameMCP_CONFIG- Detects.mcp.jsonexact filenameLSP_CONFIG- Detects.lsp.jsonexact filenameHOOK_SCRIPT- Detectshooks/*.{js,py,sh}directory pattern
Detection Priority (architecture lines 105-130):
- Exact filename matches (hooks.json, .mcp.json, .lsp.json)
- Special filenames (SKILL.md, plugin.json)
- Directory-based (agents/, commands/, hooks/)
- UNKNOWN fallback
New Pydantic Models Required (architecture lines 135-312):
HookConfig Schema (architecture lines 135-194, official source: https://docs.anthropic.com/en/docs/claude-code/hooks.md):
- 15 valid event types: SessionStart, UserPromptSubmit, PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, Notification, SubagentStart, SubagentStop, Stop, TeammateIdle, TaskCompleted, PreCompact, SessionEnd, ToolUseFailure
- 3 hook types: COMMAND (shell execution), PROMPT (LLM injection), AGENT (sub-agent invocation)
- Discriminated union on type field - command/prompt/agent fields required based on type
- Matcher field contains regex pattern (must validate compilation without execution)
- Timeout field must be positive integer if present
MCPConfig Schema (architecture lines 196-222, official source: https://modelcontextprotocol.io/docs/server):
- Required: command (non-empty string)
- Optional: args (list of strings NOT single string), env (dict[str, str]), cwd (directory path)
- Validation: command existence check with shutil.which() as WARNING only (may not exist at validation time)
LSPConfig Schema (architecture lines 224-265, official source: https://docs.anthropic.com/en/docs/claude-code/plugins.md#lsp-servers):
- Required: command (non-empty string), extensionToLanguage (non-empty dict)
- Extension keys must start with dot (.py not py or *.py)
- Language values must be lowercase identifiers
- Transport: stdio or socket only
- Timeouts must be positive integers
- maxRestarts must be positive
AgentFrontmatter Enum Enhancement (architecture lines 267-312):
Existing AgentFrontmatter model (lines 1167-1235) needs 3 new enum validations:
modelfield: Add AgentModel enum (sonnet, opus, haiku, inherit)permissionModefield: Add AgentPermissionMode enum (6 values including default, acceptEdits, dontAsk, etc.)memoryfield: Add AgentMemory enum (user, project, local)
New Validators Required (architecture lines 360-580):
HookConfigValidator (architecture lines 360-420):
- No auto-fix (JSON too complex)
- Validates 15 event types with fuzzy matching suggestions for unknown types
- Validates hook type discriminators
- Compiles regex patterns without execution (security)
- Error codes: HK001-HK010
MCPConfigValidator (architecture lines 422-473):
- No auto-fix
- Command existence check via shutil.which() produces WARNING not error
- Args must be list[str] not single string
- Env must be dict[str, str]
- Error codes: MC001-MC010
LSPConfigValidator (architecture lines 475-528):
- No auto-fix
- Strict extension format validation (.py not py)
- Lowercase language identifier enforcement
- Transport enum validation
- Error codes: LS001-LS010
AgentEnumValidator (architecture lines 530-580):
- No auto-fix (would guess user intent)
- Validates enum fields against official schema
- Provides suggestions for invalid enum values
- Only runs on FileType.AGENT files
- Error codes: AG001-AG010
DescriptionValidator Modification (architecture lines 582-629):
Current signature: validate(self, path: Path, content: str) -> ValidationResult
New signature: validate(self, path: Path, content: str, file_type: FileType) -> ValidationResult
Behavior change:
- Skip SK005 (trigger phrase check) when
file_type == FileType.COMMAND - Continue SK005 for SKILL and AGENT types
- Preserve SK004 (length check) for all types
Validator Registration (architecture lines 637-697):
Need to create VALIDATORS_BY_TYPE dispatch table:
VALIDATORS_BY_TYPE: dict[FileType, list[type[Validator]]] = {
FileType.SKILL: [FrontmatterValidator, NameFormatValidator, DescriptionValidator, ComplexityValidator, InternalLinkValidator, ProgressiveDisclosureValidator, NamespaceReferenceValidator],
FileType.AGENT: [FrontmatterValidator, NameFormatValidator, DescriptionValidator, AgentEnumValidator, NamespaceReferenceValidator],
FileType.COMMAND: [FrontmatterValidator, DescriptionValidator, NamespaceReferenceValidator], # SK005 skipped
FileType.HOOK_CONFIG: [HookConfigValidator],
FileType.MCP_CONFIG: [MCPConfigValidator],
FileType.LSP_CONFIG: [LSPConfigValidator],
FileType.PLUGIN: [PluginStructureValidator],
}
Error Code Registry Expansion (architecture lines 700-763):
Need 40 new error code constants (lines 71-109 pattern):
- HK001-HK010: Hook configuration errors (10 codes)
- MC001-MC010: MCP configuration errors (10 codes)
- LS001-LS010: LSP configuration errors (10 codes)
- AG001-AG010: Agent enum errors (10 codes)
Report Generation Fix (architecture lines 836-893):
Change from counting validators to counting files:
- Current:
len(results)counts ValidationResult objects - Required: Count unique file paths
len(set(path for path, _ in results)) - Message change: "X validators passed" → "X files validated"
- Group issues by file path, then by error code within file
Dead Code Removal (architecture lines 1149-1165):
Lines 904-911 in NamespaceReferenceValidator contain unreachable nested skill reference resolution after early return line 903. Verify with coverage before removal.
Testing Strategy (architecture lines 900-1027):
Minimum coverage: 80% overall, 90% for new validators Test files needed:
- test_hook_config_validator.py - parametrize 15 event types, test all HK error codes
- test_mcp_config_validator.py - mock shutil.which(), test warning vs error severity
- test_lsp_config_validator.py - parametrize extension formats, test language identifier validation
- test_agent_enum_validator.py - parametrize valid enum combinations, test suggestion quality
- test_file_type_detection.py - test all 9 FileType values, edge cases (nested paths)
- test_report_generation.py - verify file counts not validator counts
- test_description_validator.py - add file_type parameter tests
Performance Requirements (architecture lines 1060-1097):
- Single file: <1 second
- Plugin with 10 components: <5 seconds
- Full repository scan: <30 seconds
- Optimization: Read each file once, pass content to all validators (no redundant I/O)
Pre-Commit Integration (architecture lines 1129-1143):
Current .pre-commit-config.yaml pattern (line 1136):
files: '^plugins/.*(SKILL\.md|agents/.*\.md|commands/.*\.md|plugin\.json)$'
Required enhancement:
files: '^plugins/.*(SKILL\.md|agents/.*\.md|commands/.*\.md|plugin\.json|hooks\.json|\.mcp\.json|\.lsp\.json)$'
Technical Reference Details
Component Interfaces & Signatures
FileType Enum Extension (plugin-validator.py lines 138-166, architecture lines 85-130):
class FileType(StrEnum):
SKILL = "skill"
AGENT = "agent"
COMMAND = "command"
PLUGIN = "plugin"
HOOK_CONFIG = "hook_config" # NEW
MCP_CONFIG = "mcp_config" # NEW
LSP_CONFIG = "lsp_config" # NEW
HOOK_SCRIPT = "hook_script" # NEW
UNKNOWN = "unknown"
@staticmethod
def detect_file_type(path: Path) -> FileType:
"""Detect file type from path structure and filename.
Detection priority order:
1. Exact filename matches (hooks.json, .mcp.json, .lsp.json)
2. Special filenames (SKILL.md, plugin.json)
3. Directory-based (agents/, commands/, hooks/)
4. UNKNOWN fallback
"""
Validator Protocol (plugin-validator.py lines 249-286, unchanged):
class Validator(Protocol):
def validate(self, path: Path, content: str) -> ValidationResult: ...
def can_fix(self, issue: ValidationIssue) -> bool: ...
def fix(self, path: Path, content: str) -> str: ...
DescriptionValidator Signature Change (architecture lines 596-629):
class DescriptionValidator:
def validate(
self,
path: Path,
content: str,
file_type: FileType # NEW PARAMETER
) -> ValidationResult:
"""Validate description field with file-type awareness.
Checks:
- Minimum length (SK004) - ALL file types
- Trigger phrases (SK005) - ONLY skills and agents, skip commands
Args:
path: Path to file
content: File content
file_type: FileType enum (determines SK005 behavior)
Returns:
ValidationResult with SK errors (SK005 skipped for commands)
"""
Data Structures
HookConfig Pydantic Models (architecture lines 135-194):
from pydantic import BaseModel, Field
class HookType(StrEnum):
COMMAND = "command"
PROMPT = "prompt"
AGENT = "agent"
class HookEventType(StrEnum):
SESSION_START = "SessionStart"
USER_PROMPT_SUBMIT = "UserPromptSubmit"
PRE_TOOL_USE = "PreToolUse"
PERMISSION_REQUEST = "PermissionRequest"
POST_TOOL_USE = "PostToolUse"
POST_TOOL_USE_FAILURE = "PostToolUseFailure"
NOTIFICATION = "Notification"
SUBAGENT_START = "SubagentStart"
SUBAGENT_STOP = "SubagentStop"
STOP = "Stop"
TEAMMATE_IDLE = "TeammateIdle"
TASK_COMPLETED = "TaskCompleted"
PRE_COMPACT = "PreCompact"
SESSION_END = "SessionEnd"
class HookDefinition(BaseModel):
type: HookType
command: str | None = None # Required if type=command
prompt: str | None = None # Required if type=prompt
agent: str | None = None # Required if type=agent
timeout: int | None = None
model: str | None = None
class EventMatcher(BaseModel):
matcher: str | None = None # Optional regex pattern
hooks: list[HookDefinition]
class HookConfig(BaseModel):
hooks: dict[str, list[EventMatcher]] # Event name → matchers
MCPConfig Pydantic Models (architecture lines 196-222):
class MCPServer(BaseModel):
command: str
args: list[str] = Field(default_factory=list)
env: dict[str, str] = Field(default_factory=dict)
cwd: str | None = None
class MCPConfig(BaseModel):
mcpServers: dict[str, MCPServer]
LSPConfig Pydantic Models (architecture lines 224-265):
class LSPTransport(StrEnum):
STDIO = "stdio"
SOCKET = "socket"
class LSPServer(BaseModel):
command: str
extensionToLanguage: dict[str, str]
args: list[str] = Field(default_factory=list)
transport: LSPTransport = LSPTransport.STDIO
env: dict[str, str] = Field(default_factory=dict)
initializationOptions: dict[str, Any] = Field(default_factory=dict)
settings: dict[str, Any] = Field(default_factory=dict)
workspaceFolder: str | None = None
startupTimeout: int | None = None
shutdownTimeout: int | None = None
restartOnCrash: bool = False
maxRestarts: int | None = None
class LSPConfig(BaseModel):
lspServers: dict[str, LSPServer]
AgentFrontmatter Enum Models (architecture lines 267-312):
class AgentModel(StrEnum):
SONNET = "sonnet"
OPUS = "opus"
HAIKU = "haiku"
INHERIT = "inherit"
class AgentPermissionMode(StrEnum):
DEFAULT = "default"
ACCEPT_EDITS = "acceptEdits"
DELEGATE = "delegate"
DONT_ASK = "dontAsk"
BYPASS_PERMISSIONS = "bypassPermissions"
PLAN = "plan"
class AgentMemory(StrEnum):
USER = "user"
PROJECT = "project"
LOCAL = "local"
class AgentFrontmatter(BaseModel):
name: str # Required
description: str # Required
model: AgentModel | None = None # Enhanced with enum
permissionMode: AgentPermissionMode | None = None # Enhanced with enum
memory: AgentMemory | None = None # NEW with enum
# ... other existing fields (existing validators unchanged)
Configuration Requirements
Official Schema Sources (architecture lines 1487-1493):
- Hooks: https://docs.anthropic.com/en/docs/claude-code/hooks.md
- Agents: https://docs.anthropic.com/en/docs/claude-code/sub-agents.md
- MCP: https://modelcontextprotocol.io/docs/server
- LSP: https://docs.anthropic.com/en/docs/claude-code/plugins.md#lsp-servers
- Plugins: https://docs.anthropic.com/en/docs/claude-code/plugins.md
Token Counting (plugin-validator.py lines 44-45):
- Library: tiktoken 0.8.0+
- Encoding: cl100k_base
- Warning threshold: 4000 tokens
- Error threshold: 10000 tokens
Dependencies (plugin-validator.py lines 1-11):
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "typer>=0.21.0",
# "rich>=13.0.0",
# "tiktoken>=0.8.0",
# "pyyaml>=6.0",
# "pydantic>=2.0.0",
# ]
# ///
File Locations
Implementation Target: plugins/plugin-creator/scripts/plugin-validator.py
Key Line Regions:
- Lines 71-109: Error code constants (add 40 new constants)
- Lines 138-166: FileType enum (add 4 new variants)
- Lines 147-165: detect_file_type() method (enhance detection logic)
- Lines 1030-1236: Pydantic models (add HookConfig, MCPConfig, LSPConfig, agent enums)
- Lines 1790-1936: DescriptionValidator (add file_type parameter)
- Lines 2896-2913: Validator registration (create VALIDATORS_BY_TYPE dispatch)
- Lines 2681-2709: ConsoleReporter.summarize() (fix file counting)
- Lines 904-911: Dead code removal (verify with coverage first)
Test Files to Create:
plugins/plugin-creator/tests/test_hook_config_validator.pyplugins/plugin-creator/tests/test_mcp_config_validator.pyplugins/plugin-creator/tests/test_lsp_config_validator.pyplugins/plugin-creator/tests/test_agent_enum_validator.pyplugins/plugin-creator/tests/test_file_type_detection.pyplugins/plugin-creator/tests/test_report_generation.py
Test Files to Modify:
plugins/plugin-creator/tests/test_description_validator.py(add file_type parameter tests)
Documentation to Update:
plugins/plugin-creator/scripts/ERROR_CODES.md(add 40 new error code entries)
Pre-Commit Config:
.pre-commit-config.yaml(update file pattern around line 1136)
References
Primary Sources (Read Before Implementation):
- Task decomposition:
./plan/tasks-1-plugin-linter.md- Complete task breakdown with dependencies - Architecture specification:
./plan/architect-plugin-linter.md- WHAT to build (interfaces, contracts, schemas) - Feature context:
./plan/feature-context-plugin-linter.md- WHY this feature exists, gaps identified - Codebase analysis:
./plan/codebase/plugin-validator-architecture.md- HOW current system works - Main implementation:
./plugins/plugin-creator/scripts/plugin-validator.py- Complete 3045-line validator - Existing tests:
./plugins/plugin-creator/tests/- Test patterns to follow (11 test files) - Pre-commit config:
./.pre-commit-config.yaml- Hook integration patterns
Official Documentation (Cite in Model Docstrings):
- Hooks schema: https://docs.anthropic.com/en/docs/claude-code/hooks.md
- Agent schema: https://docs.anthropic.com/en/docs/claude-code/sub-agents.md
- MCP schema: https://modelcontextprotocol.io/docs/server
- LSP schema: https://docs.anthropic.com/en/docs/claude-code/plugins.md#lsp-servers
Testing Standards (architecture lines 1030-1051):
- Fixture type hints required:
Generator[YieldType, None, None] - Test function signatures: All parameters typed,
-> Nonereturn type - Mocking: pytest-mock only, NEVER unittest.mock
- AAA pattern: Arrange-Act-Assert with comments
- Property-based: Use hypothesis for validation functions, minimum 500 examples
Verification Checklist Before Starting:
- Read complete architecture specification (architect-plugin-linter.md)
- Read complete task decomposition (tasks-1-plugin-linter.md)
- Read current plugin-validator.py implementation (all 3045 lines)
- Read all official schema documentation URLs above
- Understand FileType detection priority order
- Understand Validator protocol contract
- Understand validation pipeline flow (detect → select → validate → aggregate → report)
- Understand token-based complexity measurement (tiktoken usage)
- Understand Pydantic field validator patterns from existing models
- Understand existing test file structure (conftest.py fixtures, test class patterns)