claudelint
A configurable, rule-based linter for Claude Code plugins and plugin marketplaces.
Features
✨ Context-Aware - Automatically detects single plugin vs marketplace repositories
🎯 Rule-Based - Enable/disable individual rules with configurable severity levels
🔌 Extensible - Load custom rules from Python files
📋 Comprehensive - Validates plugin structure, metadata, command format, and more
🐳 Containerized - Run via Docker for consistent, isolated linting
⚡ Fast - Efficient validation with clear, actionable output
Installation
Via uvx (easiest - no install required)
# From git (works before PyPI release)
uvx --from 'git+https://github.com/stbenjam/claudelint' claudelint
# Once published to PyPI, simply:
uvx claudelint
# With specific path
uvx --from 'git+https://github.com/stbenjam/claudelint' claudelint /path/to/plugin
Via pip (recommended for regular use)
pip install claudelint
From source
git clone https://github.com/stbenjam/claudelint.git
cd claudelint
pip install -e .
Using Docker
docker pull ghcr.io/stbenjam/claudelint:latest
# Run on current directory
docker run -v $(pwd):/workspace ghcr.io/stbenjam/claudelint
Quick Start
# Lint current directory
claudelint
# Lint specific directory
claudelint /path/to/plugin
# Verbose output
claudelint -v
# Strict mode (warnings as errors)
claudelint --strict
# Generate default config
claudelint --init
# List all available rules
claudelint --list-rules
Repository Types
claudelint automatically detects your repository structure:
Single Plugin
my-plugin/
├── .claude-plugin/
│ └── plugin.json
├── commands/
│ └── my-command.md
└── README.md
Marketplace (Multiple Plugins)
claudelint supports multiple marketplace structures per the Claude Code specification:
Traditional Structure (plugins/ directory)
marketplace/
├── .claude-plugin/
│ └── marketplace.json
└── plugins/
├── plugin-one/
│ ├── .claude-plugin/
│ └── commands/
└── plugin-two/
├── .claude-plugin/
└── commands/
Flat Structure (root-level plugin)
marketplace/
├── .claude-plugin/
│ └── marketplace.json # source: "./"
├── commands/ # Plugin components at root
│ └── my-command.md
└── skills/
└── my-skill/
Custom Paths
marketplace/
├── .claude-plugin/
│ └── marketplace.json # source: "./custom/my-plugin"
└── custom/
└── my-plugin/
├── commands/
└── skills/
Mixed Structures
Plugins from plugins/, custom paths, and remote sources can coexist in one marketplace. Only local sources are validated.
Marketplace Features
Flexible Plugin Sources
claudelint understands all plugin source types and validates any sources that resolve to local paths:
- Relative paths:
"source": "./"(flat structure),"source": "./custom/path" - GitHub repositories:
"source": {"source": "github", "repo": "owner/repo"} - Git URLs:
"source": {"source": "url", "url": "https://..."}
Remote sources (GitHub, git URLs) are logged and skipped during local validation. They are valid per spec but cannot be checked until the plugin is fetched locally.
Strict Mode
The strict field in marketplace entries controls validation behavior:
{
"name": "my-plugin",
"source": "./",
"strict": false, // plugin.json becomes optional
"description": "Plugin description can be in marketplace.json"
}
When strict: false:
plugin.jsonis optional- Marketplace entry serves as the complete plugin manifest
- Plugin metadata is validated from marketplace.json
- Skills, commands, and other components work normally
When strict: true (default):
plugin.jsonis required- Marketplace entry supplements plugin.json metadata
Configuration
Create .claudelint.yaml in your repository root:
# Enable/disable rules
rules:
plugin-json-required:
enabled: true
severity: error
plugin-naming:
enabled: true
severity: warning
command-sections:
enabled: true
severity: warning
# 'auto' enables only for marketplace repos
marketplace-registration:
enabled: auto
severity: error
# Load custom rules
custom-rules:
- ./my-custom-rules.py
# Exclude patterns
exclude:
- "**/node_modules/**"
- "**/.git/**"
# Treat warnings as errors
strict: false
Generating Default Config
claudelint --init
This creates .claudelint.yaml with all builtin rules enabled.
Builtin Rules
Plugin Structure
| Rule ID | Description | Default Severity | Notes |
|---|---|---|---|
plugin-json-required |
Plugin must have .claude-plugin/plugin.json |
error | Skipped when strict: false in marketplace |
plugin-json-valid |
Plugin.json must be valid with required fields | error | |
plugin-naming |
Plugin names should use kebab-case | warning | |
commands-dir-required |
Plugin should have a commands directory | warning (disabled by default) | |
commands-exist |
Plugin should have at least one command file | info (disabled by default) | |
plugin-readme |
Plugin should have a README.md file | warning |
Command Format
| Rule ID | Description | Default Severity |
|---|---|---|
command-naming |
Command files should use kebab-case | warning |
command-frontmatter |
Command files must have valid frontmatter | error |
command-sections |
Commands should have Name, Synopsis, Description, Implementation sections | warning |
command-name-format |
Command Name section should be plugin:command format |
warning |
Marketplace
| Rule ID | Description | Default Severity | Notes |
|---|---|---|---|
marketplace-json-valid |
Marketplace.json must be valid JSON | error (auto) | |
marketplace-registration |
Plugins must be registered in marketplace.json | error (auto) | Supports flat structures, custom paths, and remote sources |
Skills
| Rule ID | Description | Default Severity |
|---|---|---|
skill-frontmatter |
SKILL.md files should have frontmatter | warning |
Agents
| Rule ID | Description | Default Severity |
|---|---|---|
agent-frontmatter |
Agent files must have valid frontmatter with description and capabilities | error |
Hooks
| Rule ID | Description | Default Severity |
|---|---|---|
hooks-json-valid |
hooks.json must be valid JSON with proper hook configuration structure | error |
MCP (Model Context Protocol)
| Rule ID | Description | Default Severity | Notes |
|---|---|---|---|
mcp-valid-json |
MCP configuration must be valid JSON with proper mcpServers structure | error | Validates both .mcp.json and mcpServers in plugin.json |
mcp-prohibited |
Plugins should not enable MCP servers | error (disabled by default) | Security/policy rule - enable to prohibit MCP usage |
Custom Rules
Create custom validation rules by extending the Rule base class:
# my_custom_rules.py
from pathlib import Path
from typing import List
from claudelint import Rule, RuleViolation, Severity, RepositoryContext
class NoTodoCommentsRule(Rule):
@property
def rule_id(self) -> str:
return "no-todo-comments"
@property
def description(self) -> str:
return "Command files should not contain TODO comments"
def default_severity(self) -> Severity:
return Severity.WARNING
def check(self, context: RepositoryContext) -> List[RuleViolation]:
violations = []
for plugin_path in context.plugins:
commands_dir = plugin_path / "commands"
if not commands_dir.exists():
continue
for cmd_file in commands_dir.glob("*.md"):
with open(cmd_file, 'r') as f:
content = f.read()
if 'TODO' in content:
violations.append(
self.violation(
"Found TODO comment in command file",
file_path=cmd_file
)
)
return violations
Then reference it in .claudelint.yaml:
custom-rules:
- ./my_custom_rules.py
rules:
no-todo-comments:
enabled: true
severity: warning
CI/CD Integration
GitHub Actions
name: Lint Claude Plugins
on: [pull_request, push]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install claudelint
run: pip install claudelint
- name: Run linter
run: claudelint --strict
GitLab CI
lint-plugins:
image: python:3.11
script:
- pip install claudelint
- claudelint --strict
Docker
docker run -v $(pwd):/workspace -w /workspace ghcr.io/stbenjam/claudelint --strict
Exit Codes
0- Success (no errors, or warnings only in non-strict mode)1- Failure (errors found, or warnings in strict mode)
Examples
Example Output
Linting Claude plugins in: /path/to/marketplace
Errors:
✗ ERROR [plugins/git/.claude-plugin/plugin.json]: Missing plugin.json
✗ ERROR [.claude-plugin/marketplace.json]: Plugin 'new-plugin' not registered
Warnings:
⚠ WARNING [plugins/utils]: Missing README.md (recommended)
⚠ WARNING [plugins/jira/commands/solve.md]: Missing recommended section '## Implementation'
Summary:
Errors: 2
Warnings: 2
Disabling Specific Rules
rules:
plugin-readme:
enabled: false # Don't require README files
command-sections:
enabled: false # Don't check for specific sections
Changing Severity
rules:
plugin-naming:
severity: error # Make naming violations errors instead of warnings
command-name-format:
severity: info # Downgrade to info level
Development
Running Tests
pytest tests/
Building Docker Image
docker build -t claudelint .
Project Structure
claudelint/
├── src/
│ ├── rule.py # Base Rule class
│ ├── context.py # Repository detection
│ ├── config.py # Configuration management
│ └── linter.py # Main linter orchestration
├── rules/
│ └── builtin/ # Builtin validation rules
├── tests/ # Test suite
├── examples/ # Example configs and custom rules
├── claudelint # CLI entry point
├── Dockerfile # Container image
└── pyproject.toml # Package metadata
Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
License
Apache 2.0 - See LICENSE for details.
See Also
- Claude Code Documentation
- Claude Code Plugins Reference
- AI Helpers Marketplace - Example repository using claudelint
Support
Metadata
Release files for claudelint 0.3.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| claudelint-0.3.6.tar.gz | 38.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| claudelint-0.3.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 69.2 kB
Release files / claudelint-0.3.6.tar.gz
| Download URL | claudelint-0.3.6.tar.gz |
|---|---|
| Size | 38.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
42d4c3364cc54f65d454834f3a0b8e68d2e1de8e1e0052f20ebf5bd8ebdca328
|
|
BLAKE2b-256 checksum How to use checksums |
2ce53efdb4c94579bf0aa4ef5fc39c879af342db15029e82988b2c98db2f2b0d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 25, 2026.
Transparency logRelease files / claudelint-0.3.6-py3-none-any.whl
| Download URL | claudelint-0.3.6-py3-none-any.whl |
|---|---|
| Size | 31.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bfae5513876d9866c2ceab4613c3a53abcc01973fcc464145e9cbce12791f0d0
|
|
BLAKE2b-256 checksum How to use checksums |
60b9de653cac6b35533aab23b3f9723f79198e8ad9cae89e3b562fbe3ba41b37
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 25, 2026.
Transparency log