Thank you for your interest in contributing to claudelint! This document provides guidelines for contributing to the project.
Want to add a validation rule? See the Rule Development Guide - this is our detailed technical guide for writing rules.
Want to contribute in other ways? Keep reading this document for general contribution guidelines (git workflow, testing, code style, etc.).
This project adheres to a Code of Conduct that all contributors are expected to follow. Please read CODE_OF_CONDUCT.md before contributing.
- Node.js 20.0.0 or higher
- npm or yarn
- Git
-
Fork the repository on GitHub
-
Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/claudelint.git cd claudelint -
Install dependencies:
npm install
-
Build the project:
npm run build
-
Run tests to verify setup:
npm test -
Set up git hooks (runs automatically on install):
npm run setup:hooks
-
Create a new branch for your feature or fix:
git checkout -b feature/your-feature-name
-
Make your changes following the code style guidelines below
-
Add or update tests for your changes
-
Run the full test suite:
npm test -
Run linting and formatting:
npm run lint npm run format:check
-
Run validation on the project itself (dogfooding):
npm run validate
See the CLI Reference for complete command reference.
- Follow TypeScript best practices
- Use
unknowninstead ofanyfor unknown types - Add JSDoc comments to all public APIs
- Keep functions small and focused
- Write descriptive variable and function names
- Use proper type guards when narrowing types
IMPORTANT: Library code MUST NOT use console directly.
See the Internals Guide for full details.
Use DiagnosticCollector instead:
import { DiagnosticCollector } from '../utils/diagnostics';
// In functions
export function myFunction(
param: string,
diagnostics?: DiagnosticCollector
): Result {
if (invalid) {
diagnostics?.warn(
'Invalid param',
'MyFunction',
'MY_001'
);
}
}
// In classes
export class MyClass {
constructor(private diagnostics?: DiagnosticCollector) {}
myMethod() {
this.diagnostics?.error(
'Operation failed',
'MyClass',
'MY_002'
);
}
}Why:
- Makes library testable (no console spam during tests)
- Allows programmatic usage (consumers control output)
- Provides structured diagnostics with source tracking
- Follows industry standards (ESLint, TypeScript, Webpack)
Where console IS allowed:
- CLI layer only:
src/cli/utils/logger.ts - Output formatting:
src/utils/reporting/ - Script utilities:
scripts/util/logger.ts
Enforcement:
Library code is checked in CI and pre-commit hooks:
npm run check:logger-usage- Write unit tests for all new validators
- Add integration tests for CLI commands
- Aim for 80%+ code coverage
- Test error conditions and edge cases
- Use descriptive test names
Constants like ToolNames and ModelNames must stay synchronized with Claude Code. We verify these by querying the Claude Code CLI.
Requirements:
- Claude Code CLI installed (
brew install claude-codeor from https://code.claude.com/) ANTHROPIC_API_KEYconfigured
When to verify:
- Before releases (required)
- After Claude Code updates
- Every 90 days (recommended)
- When users report validation issues
How to verify:
# Verify all constants
npm run check:constants
# Or individually
npm run check:tool-names
npm run check:model-namesIf drift detected:
- Review the output to see missing/extra values
- Cross-check with official docs:
- Update
src/schemas/constants.tsif needed - Run tests:
npm test - Re-verify:
npm run check:constants - Re-verify:
npm run check:constants
Note: Regular contributors don't need Claude CLI installed. This is only for maintainers doing releases.
Follow conventional commit format:
type(scope): subject
body (optional)
footer (optional)
Types:
feat: New featurefix: Bug fixdocs: Documentation changestest: Test changesrefactor: Code refactoringchore: Build/tooling changesperf: Performance improvements
Examples:
feat(validators): add MCP server validatorfix(cli): handle missing config file gracefullydocs(readme): update installation instructions
claudelint/
├── src/
│ ├── validators/ # Validator implementations
│ ├── utils/ # Utility functions
│ └── cli.ts # CLI entry point
├── tests/
│ ├── validators/ # Validator tests
│ ├── utils/ # Utility tests
│ └── integration/ # Integration tests
├── docs/projects/ # Internal project tracking
├── website/ # Documentation site (claudelint.com)
├── .claude/ # Claude Code plugin files
│ ├── skills/ # Plugin skills
│ └── hooks/ # Plugin hooks
└── scripts/ # Build and automation scripts
claudelint uses a rule-based architecture (similar to ESLint). Contributors write individual validation rules, not validators.
See the comprehensive Rule Development Guide for:
- Understanding rules and architecture
- Writing custom rules (external developers)
- Contributing built-in rules (contributors)
- Rule structure and metadata
- Validation logic patterns
- Testing strategies
- Auto-fix capabilities
- Best practices
Quick checklist:
- ✓ Create rule file in
src/rules/{category}/{rule-id}.ts - ✓ Define rule metadata (id, name, description, category, severity)
- ✓ Implement
validate()function - ✓ Add rule to category index in
src/rules/{category}/index.ts - ✓ Write unit tests in
tests/rules/{category}/{rule-id}.test.ts - ✓ Run
npm run docs:generateto generate rule documentation - ✓ Test the rule with
npm test - ✓ Run validation on project:
npm run validate
Skills are interactive capabilities that allow Claude to help users validate, optimize, and fix their Claude Code projects through natural conversation.
All skills must follow Anthropic's best practices for skill development.
Required for all skills:
- Description with trigger phrases - Must include what, when, triggers, and capabilities
- Proper naming - No single-word verbs, use suffixes like
-all,-cc,-cc-md - Automated validation - Must pass
claudelint validate-skills - Manual testing - Trigger phrases tested, functionality verified
Required for complex skills:
- Examples section - Scenario-based examples (User says → Actions → Result)
- Troubleshooting section - For skills with >3 scripts or that edit files
Recommended:
- Progressive disclosure - For skills with >3,000 words, use references/ directory
Before submitting a PR:
-
Validate structure:
claudelint validate-skills .claude/skills/your-skill
-
Test trigger phrases:
- Start fresh Claude session
- Test phrases from description field
- Verify 90%+ trigger success rate
-
Test functionality:
- Valid inputs work correctly
- Invalid inputs detected properly
- Edge cases handled
-
Update documentation:
- Add to README.md skills list
- Add to .claude-plugin/README.md
- Include in PR description
When submitting a skill PR, include the following in your PR description (the PR template will guide you):
- Skill name and one-line description
- Trigger phrases (list at least 3)
- Confirmation that
claudelint validate-skillspasses - Trigger phrase test results (90%+ success rate)
Review criteria:
- Follows naming conventions (no generic names like "format", "validate")
- Description includes trigger phrases
- Progressive disclosure used (if >3,000 words)
- Examples follow scenario format
- Troubleshooting addresses skill usage issues (not issues skill fixes)
See the Contributing Guide for detailed requirements.
When a rule needs to be changed or removed, follow this deprecation policy to give users time to migrate their configurations.
Deprecate a rule when:
- The rule validates a field that no longer exists in the official spec
- The rule's behavior is being merged into another rule
- The rule's validation logic is fundamentally flawed and needs a complete rewrite
- The rule is being split into multiple focused rules
Do not deprecate for minor fixes, bug fixes, or improved error messages - these should be updated in place.
Add a deprecated field to the rule's metadata. Use boolean for simple cases, or DeprecationInfo object for full metadata:
// Simple deprecation (boolean)
export const rule: Rule = {
meta: {
id: 'old-rule-name',
name: 'Old Rule Name',
description: 'Description',
category: 'Category',
severity: 'warn',
fixable: false,
deprecated: true, // Simple boolean
since: '0.1.0',
},
validate: async (context) => {
// Rule still executes but shows deprecation warning
},
};
// Full deprecation metadata (recommended)
export const rule: Rule = {
meta: {
id: 'old-rule-name',
name: 'Old Rule Name',
description: 'Description',
category: 'Category',
severity: 'warn',
fixable: false,
deprecated: {
reason: 'This rule validates a field that was removed from the spec',
replacedBy: 'new-rule-name', // Single replacement
// OR: replacedBy: ['rule-1', 'rule-2'], // Multiple replacements
deprecatedSince: '0.3.0',
removeInVersion: '1.0.0', // When it will be removed
// OR: removeInVersion: null, // Retained indefinitely
url: 'https://claudelint.com/guide/troubleshooting',
},
since: '0.1.0',
},
validate: async (context) => {
// Rule still executes but shows deprecation warning
},
};Follow this timeline for deprecating rules:
-
Deprecate (Minor Version)
- Add
deprecatedmetadata to the rule - Rule still executes normally but shows warnings
- Document in CHANGELOG.md under "Deprecated" section
- Add migration guide (if replacedBy exists)
- Add
-
Warn (2+ Minor Versions)
- Keep the rule for at least 2 minor versions after deprecation
- Users see warnings when they use deprecated rules
claudelint check-deprecatedshows all deprecated rules in configclaudelint migratecan auto-update configs (1:1 replacements only)
-
Remove (Next Major Version)
- Remove the rule implementation in next major version (e.g., 1.0.0)
- Document in CHANGELOG.md under "Breaking Changes"
- Add to migration guide for major version
Exception: Rules deprecated before 1.0.0 may be removed in 1.0.0 if they validate non-existent fields.
deprecated: {
reason: 'Field was renamed in official spec',
replacedBy: 'new-rule-name',
}Users: Run claudelint migrate to auto-update configs.
deprecated: {
reason: 'Rule was split into multiple focused rules',
replacedBy: ['rule-1', 'rule-2', 'rule-3'],
}Users: Must manually update configs. claudelint migrate will warn about this.
deprecated: {
reason: 'Field no longer exists in official spec',
// No replacedBy field
}Users: Remove rule from config. claudelint migrate will suggest removal.
deprecated: {
reason: 'Deprecated but kept for backward compatibility',
removeInVersion: null, // Will never be removed
}Users: Can keep using but should migrate to new approach when possible.
Users can manage deprecated rules with these commands:
claudelint check-deprecated- List all deprecated rules in configclaudelint check-all --no-deprecated-warnings- Suppress deprecation warningsclaudelint check-all --error-on-deprecated- Treat deprecated rules as errors (CI mode)claudelint migrate- Auto-update config files (1:1 replacements)claudelint migrate --dry-run- Preview changes without writing
Continue testing deprecated rules until removal:
describe('old-rule-name (deprecated)', () => {
it('should still validate correctly', async () => {
// Test the rule's core functionality
});
it('should be marked as deprecated', () => {
const rule = RuleRegistry.getRule('old-rule-name');
expect(isRuleDeprecated(rule)).toBe(true);
});
it('should have replacement info', () => {
const rule = RuleRegistry.getRule('old-rule-name');
const info = getDeprecationInfo(rule);
expect(info?.replacedBy).toBeDefined();
});
});-
Push your changes to your fork:
git push origin feature/your-feature-name
-
Open a pull request on GitHub
-
Fill out the PR template (summary, type of change, test plan, checklist)
-
Ensure your PR title follows Conventional Commits format (e.g.,
feat: add new rule) — this is enforced by CI -
Wait for CI checks to pass
-
Address any review feedback
-
Once approved, a maintainer will merge your PR
Include:
- claudelint version (
claudelint --version) - Node.js version (
node --version) - Operating system
- Steps to reproduce
- Expected vs actual behavior
- Error messages or logs
Include:
- Use case description
- Proposed solution
- Alternative solutions considered
- Impact on existing functionality
If your PR changes the public API (adds/removes/modifies exports from src/index.ts or types in src/api/):
-
Update the API report:
npm run check:api-report:update
This regenerates
etc/claude-code-lint.api.md. Commit the updated report alongside your code changes. -
Update the snapshot test — run
npm test -- -u --testPathPatterns=api-surfaceif you intentionally added or removed exports. -
Add JSDoc comments to any new public exports (enforced by ESLint).
-
Update type assertions in
src/index.test-d.tsif you changed function signatures. -
Update documentation pages in
website/api/to reflect the change.
CI will fail if the API report is stale, the export snapshot doesn't match, or public exports lack JSDoc.
- Update README.md for user-facing changes
- All documentation lives at claudelint.com (source in
website/) - Add JSDoc comments for API changes
- Include code examples where helpful
For contributing code:
- Custom Rules - How to write validation rules (START HERE)
- Architecture - System architecture and design decisions
- Contributing - Contribution guidelines
For users:
- Getting Started - Installation and first steps
- Rules Overview - Understanding validation categories
- Configuration - Configuring claudelint
- Troubleshooting - Common issues and solutions
(For maintainers only)
Pre-release checklist:
-
Verify constants are current:
npm run check:constants
If drift detected, fix it before proceeding. See Verifying Constants section.
-
Run full validation:
npm run validate:all
-
Choose release type:
# Patch (bug fixes): 0.2.0 -> 0.2.1 npm run release:patch # Minor (new features): 0.2.0 -> 0.3.0 npm run release:minor # Major (breaking changes): 0.2.0 -> 1.0.0 npm run release:major # Or interactive (prompts for version): npm run release
-
What
npm run releasedoes:- Runs lint, test, and build
- Analyzes commits since last release
- Auto-generates CHANGELOG section
- Bumps version in package.json
- Syncs skill versions
- Creates git commit and tag
- Pushes to GitHub
- Creates GitHub release
npm publishing happens automatically via CI when the
v*tag is pushed, using OIDC trusted publishing with provenance attestation.
Note: The npm run release command uses release-it and handles all steps automatically. Manual version/changelog updates are not needed.
You can optionally run constants verification in CI, but be aware of trade-offs:
Trade-offs:
- Pro: Catches drift automatically on every PR
- Con: Costs tokens (~$0.01 per run, adds up)
- Con: Requires
ANTHROPIC_API_KEYin CI secrets - Con: Adds ~30 seconds to CI runs
Example GitHub Actions workflow:
# .github/workflows/verify-constants.yml (optional)
name: Verify Constants
on:
pull_request:
schedule:
# Run weekly on Mondays at 9am UTC
- cron: '0 9 * * 1'
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '20'
- run: npm ci
# Install Claude CLI (example for Linux)
- name: Install Claude CLI
run: |
curl -fsSL https://code.claude.com/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Verify Constants
run: npm run check:constants
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}Recommendation: Don't enable this initially. Manual verification before releases is sufficient for most projects.
- Read the documentation
- Search existing issues
- Ask in discussions
- Open a new issue if needed
By contributing to claudelint, you agree that your contributions will be licensed under the MIT License.