@felixgeelhaar/cclint
Version:
Catch CLAUDE.md drift before Claude misbehaves. Lints CLAUDE.md, skills, subagents, and hooks for Claude Code projects.
731 lines (537 loc) โข 23.7 kB
Markdown
# CC Linter ๐
[](https://github.com/felixgeelhaar/cclint/actions)
[](https://badge.fury.io/js/@felixgeelhaar%2Fcclint)
[](https://typescriptlang.org)
[](https://opensource.org/licenses/MIT)
**Catch CLAUDE.md drift before Claude misbehaves.** A fast linter for the configuration files Claude Code actually reads โ `CLAUDE.md`, skills, subagents, and hooks โ so silent context bugs stop costing you sessions.
## What it catches
- **Stale or invalid model IDs** in subagents (e.g. `claude-3-5-sonnet` flagged as deprecated; `claude-opus-4-7` recognized)
- **Unresolved `@path` imports** and circular import chains
- **Dangerous bash** in code blocks (`rm -rf /`, `curl | bash`, fork bombs)
- **Duplicate content** across parent / sibling CLAUDE.md files in monorepos
- **Skill / subagent / hook** structural errors before Claude Code loads them
- **Vague instructions** ("follow best practices") that degrade model adherence
## Why use it
- **Built for Claude Code** โ knows the spec, not just markdown syntax
- **Fast** โ TypeScript + Vitest, lints in milliseconds
- **Extensible** โ plugin API for custom rules
- **CI-ready** โ GitHub Action, JSON output, exit codes
- **Auto-fix** โ `--fix` and interactive `-i` modes for common issues
## ๐ฆ Installation
### Global Installation
```bash
npm install -g @felixgeelhaar/cclint
```
### Local Installation
```bash
npm install --save-dev @felixgeelhaar/cclint
```
### Using npx (No Installation Required)
```bash
npx @felixgeelhaar/cclint lint your-claude.md
```
## ๐ Quick Start
### Basic Usage
```bash
# Lint a CLAUDE.md file
cclint lint CLAUDE.md
# Lint with JSON output
cclint lint CLAUDE.md --format json
# Lint with SARIF output (for GitHub Code Scanning)
cclint lint CLAUDE.md --format sarif > cclint.sarif
# Set custom file size limit
cclint lint CLAUDE.md --max-size 5000
```
### Example Output
```
๐ Linting results for CLAUDE.md:
โ error: Missing required section: "Development Commands" at 1:1 [structure]
โ ๏ธ warning: File size (12,543 characters) exceeds maximum allowed size (10,000 characters) at 1:1 [file-size]
โ ๏ธ warning: Missing required content: TypeScript usage (expected: "TypeScript") at 1:1 [content]
Summary: 1 errors, 2 warnings
```
## ๐ Built-in Rules
### Import Syntax Rule (`import-syntax`) ๐
Validates Anthropic's `@path/to/file` import syntax for CLAUDE.md files.
- **Checks**:
- Import syntax outside code blocks/spans
- Path format validation (relative, absolute, `~/`)
- Duplicate import detection
- Max depth violations (5 hops)
- **Severity**: Mixed (errors for syntax, warnings for patterns)
- **Enabled**: By default
### Content Organization Rule (`content-organization`) ๐
Validates content quality and structure following Anthropic best practices.
- **Checks**:
- Heading hierarchy (h1 โ h2 โ h3, no skipping)
- Bullet point usage for organization
- Vague language detection ("properly" โ specific instructions)
- Emphasis markers (IMPORTANT, YOU MUST)
- Specificity (measurements, tool names)
- **Severity**: Info (suggestions for improvement)
- **Purpose**: Ensures clear, actionable instructions
### File Location Rule (`file-location`) ๐
Validates file placement and naming conventions.
- **Checks**:
- CLAUDE.local.md deprecation warnings
- File naming (CLAUDE.md required)
- Location recommendations (user vs project)
- Git awareness (.gitignore suggestions)
- **Severity**: Mixed (errors for naming, warnings/info for recommendations)
- **Enabled**: By default
### Import Resolution Rule (`import-resolution`) โญ v0.6.0
Validates that imports resolve to existing files and detects circular dependencies.
- **Checks**:
- File existence validation for all @path imports
- Circular dependency detection (A โ B โ A)
- Recursive depth limit enforcement (5 hops max)
- Path resolution (relative, absolute, home directory)
- **Severity**: Error for missing files and cycles
- **Enabled**: By default
### Content Appropriateness Rule (`content-appropriateness`) โญ v0.6.0
Ensures content is specific, actionable, and belongs in CLAUDE.md.
- **Checks**:
- Generic instructions detection ("follow best practices")
- File size recommendations (~5KB limit)
- Content placement (README vs CLAUDE.md)
- Section size optimization
- Actionable vs passive language
- **Severity**: Warning for size, Info for suggestions
- **Enabled**: By default
### Monorepo Hierarchy Rule (`monorepo-hierarchy`) โญ v0.6.0
Validates CLAUDE.md file relationships in monorepos.
- **Checks**:
- Parent/child CLAUDE.md conflict detection
- Duplicate content across hierarchy
- Organization recommendations for multi-package repos
- Import-based consolidation suggestions
- **Severity**: Warning for conflicts, Info for guidance
- **Enabled**: By default
### Command Safety Rule (`command-safety`) โญ v0.6.0
Validates bash command safety in code blocks.
- **Checks**:
- Dangerous commands (`rm -rf /`, `curl | bash`, fork bombs)
- Error handling (`set -e`, `|| exit 1`)
- Variable quoting in destructive operations
- Unsafe `sudo` usage warnings
- **Severity**: Error for dangerous commands, Warning for safety issues
- **Enabled**: By default
### Skill Structure Rule (`skill-structure`) ๐ v0.11.0
Validates Claude Code skill files (`.claude/skills/*.md`).
- **Checks**:
- Frontmatter presence and validity
- Name format (kebab-case required)
- Description length (10-200 characters)
- Content structure after frontmatter
- **Severity**: Error for missing required fields, Warning for style issues
- **Enabled**: By default
### Subagent Structure Rule (`subagent-structure`) ๐ v0.11.0
Validates Claude Code subagent files (`.claude/agents/*.md`).
- **Checks**:
- Frontmatter presence with name and description
- Valid tool names (Read, Edit, Bash, Glob, etc.)
- Valid model identifiers (claude-3-5-sonnet, opus, haiku, etc.)
- Prompt content presence and minimum length
- **Severity**: Error for missing required fields, Warning for invalid tools/models
- **Enabled**: By default
### Hook Configuration Rule (`hook-configuration`) ๐ v0.11.0
Validates Claude Code hook configuration (`.claude/settings.json`).
- **Checks**:
- Valid JSON syntax
- Hook structure (matcher and command fields)
- Dangerous command detection (`rm -rf`, `curl | sh`, fork bombs)
- Command safety (warnings for `&&` without `set -e`)
- **Severity**: Error for JSON/structure issues, Warning for dangerous commands
- **Enabled**: By default
### Karpathy Recommendations (`karpathy`) ๐
Opinionated CLAUDE.md style advisories inspired by Andrej Karpathy's commentary on writing for LLMs and "context engineering" โ you program the model in English, so the context window should be minimal, high signal-to-noise, literal, and example-driven. Heuristics, not an official standard.
- **Checks**:
- Hedging language (`try to`, `where appropriate`) that makes instructions non-literal
- Filler / politeness (`please`, `thank you`, `you are a helpful assistant`) that spends context without signal
- Guideline sections that list many rules but show no concrete example (show, don't tell)
- Overly long prose paragraphs (prefer tight, skimmable lines or bullets)
- **Scope**: `CLAUDE.md` files only; code fences are ignored
- **Severity**: Info (recommendations, never fails CI)
- **Enabled**: By default
### Secret Detection Rule (`secret-detection`) ๐
Flags likely credentials pasted into `CLAUDE.md` โ one of the most damaging authoring mistakes, since context files are versioned, shared, and fed to models.
- **Checks**:
- Provider key shapes: OpenAI (`sk-โฆ`, `sk-proj-โฆ`), Anthropic (`sk-ant-โฆ`), GitHub (`ghp_`/`gho_`/`ghs_`/`ghu_`/`github_pat_โฆ`), AWS access keys (`AKIAโฆ`), Google (`AIzaโฆ`), Slack (`xoxb-โฆ`)
- PEM private-key blocks (`-----BEGIN โฆ PRIVATE KEY-----`)
- High-entropy `KEY=`/`TOKEN=`/`SECRET=`/`PASSWORD=` assignments
- **Scope**: Markdown files only; both prose and fenced code blocks are scanned
- **Severity**: Error
- **Masking**: Messages show only the first four characters of a secret (e.g. `sk-Aโฆ`) and never echo the full value
- **False positives**: Obvious placeholders (`sk-xxxx`, `your-api-key-here`, `<โฆ>`, `example`, all-same-char) are ignored
- **Enabled**: By default
### Plugin Manifest Rule (`plugin-manifest`) ๐
Validates Claude Code plugin manifests โ the plugin descriptor (`.claude-plugin/plugin.json`) and the marketplace listing (`marketplace.json`). A malformed manifest silently breaks plugin discovery and installation.
- **Checks**:
- Valid JSON that parses to an object
- Required `name` field (non-empty string)
- `version`, when present, is valid SemVer (e.g. `1.2.3`)
- Resource references (`commands`, `agents`, `skills`, `hooks`) are path strings or arrays of path strings; absolute paths and backslashes are flagged
- Marketplace `plugins` is an array whose entries carry a `name` (and a `source`)
- **Scope**: `plugin.json` and `marketplace.json` files only
- **Severity**: Error (structural), Warning (path portability, missing `source`)
- **Enabled**: By default
### MCP Config Rule (`mcp-config`) ๐
Validates Model Context Protocol server configuration in `.mcp.json`.
- **Checks**:
- Valid JSON containing an `mcpServers` object
- Each server is **either** stdio (`command`, optional `args`/`env`) **or** remote (`url` + `type` of `sse`/`http`) โ never both or neither
- `${VAR}` environment-variable placeholders are well-formed
- No duplicate server names
- `args` is an array of strings and `env` is an object of string values
- **Scope**: `.mcp.json` files only
- **Severity**: Error (structural), Warning (missing/ambiguous fields)
- **Enabled**: By default
### Output Style Rule (`output-style`) ๐
Validates Claude Code output-style definitions in `.claude/output-styles/*.md`.
- **Checks**:
- Frontmatter is present with required `name` and `description` fields
- Warns on unknown frontmatter keys (only `name` and `description` are recognized)
- **Scope**: Markdown files under an `output-styles/` directory
- **Severity**: Error (missing required fields), Warning (unknown keys)
- **Enabled**: By default
### File Size Rule (`file-size`)
Validates that CLAUDE.md files don't exceed size limits for optimal performance.
- **Default**: 10,000 characters
- **Severity**: Warning
- **Configurable**: `--max-size <number>`
### Structure Rule (`structure`)
Ensures required sections are present in CLAUDE.md files.
- **Required Sections**:
- "Project Overview"
- "Development Commands"
- "Architecture"
- **Severity**: Error
- **Purpose**: Maintains consistent documentation structure
### Content Rule (`content`) โ ๏ธ Deprecated
> **Note**: This rule is deprecated in v0.5.0. Use `content-organization` instead.
Technology-specific content validation (deprecated in favor of content-organization).
- **Status**: Maintained for backward compatibility
- **Migration**: Switch to `content-organization` rule
### Format Rule (`format`)
Validates Markdown syntax and formatting best practices.
- **Checks**:
- Header spacing (`# Header` not `#Header`)
- Trailing whitespace
- Consecutive empty lines (max 2)
- Code block formatting
- File ending with newline
- **Severity**: Mixed (errors for syntax, warnings for style)
### Code Blocks Rule (`code-blocks`)
Validates fenced code blocks inside CLAUDE.md.
- **Checks**:
- Language tag present (` ```bash ` not bare ` ``` `)
- Matched fence delimiters
- Indentation consistency inside the block
- **Severity**: Warning
- **Fixable**: Yes โ `--fix` can add missing language tags
- **Why**: Untyped code blocks degrade Claude's ability to parse intent and surface bash code blocks for the `command-safety` rule.
## ๐ค MCP Server
Run cclint inside any MCP-compatible client (Claude Desktop, Claude Code, Cursor) โ no global install required. Add to your MCP config:
```json
{
"mcpServers": {
"cclint": {
"command": "npx",
"args": ["@felixgeelhaar/cclint", "mcp"]
}
}
}
```
Tools exposed:
- `lint_file` โ lint a file on disk
- `lint_string` โ lint inline content (e.g. before saving an edit)
- `list_rules` โ list every cclint rule
- `explain_rule` โ get rationale + examples for a rule
Or run as a standalone bin: `npx cclint-mcp`.
## ๐๏ธ LSP Server
Get real-time cclint diagnostics in your editor while you edit CLAUDE.md, skills, subagents, and Claude Code config files โ no save-and-run round trip. cclint ships a Language Server Protocol server that works with any LSP-compatible editor (VS Code, Neovim, Emacs, Sublime, โฆ).
Run it over stdio:
```bash
cclint-lsp --stdio
```
What it provides:
- **Live diagnostics** โ the same rules as the CLI run on the buffer's live text on open, change, and save, published as editor squiggles. File-kind gating is honored, so a `settings.json` gets hook rules while a `CLAUDE.md` gets structure rules.
- **Quick fixes** โ violations that carry a structured fix are offered as `quickfix` code actions that apply the exact edit.
- **Config aware** โ `.cclintrc.json` and presets are discovered upward from the edited document, so per-workspace config is respected.
Example Neovim (`nvim-lspconfig`) setup:
```lua
require('lspconfig.configs').cclint = {
default_config = {
cmd = { 'cclint-lsp', '--stdio' },
filetypes = { 'markdown', 'json' },
root_dir = require('lspconfig.util').root_pattern('.cclintrc.json', '.git'),
},
}
require('lspconfig').cclint.setup({})
```
> A dedicated VS Code extension client is not yet published; any editor with a generic LSP client can launch `cclint-lsp --stdio` today.
## ๐ก `cclint why` โ AI fix suggestions
Get plain-language explanations and AI-generated fix suggestions for any violation:
```bash
cclint why CLAUDE.md # all violations
cclint why CLAUDE.md --rule command-safety # filter by rule
cclint why CLAUDE.md --line 41 # filter by line
cclint why CLAUDE.md --ai # AI-generated fix (needs ANTHROPIC_API_KEY)
```
Without `--ai`, prints the rule rationale and good example. With `--ai`, sends the offending line + rule context to Claude Haiku 4.5 and prints a focused 3โ6 line fix suggestion.
## โ๏ธ Configuration
### Command Line Options
```bash
cclint lint [options] <file>
Options:
-f, --format <format> Output format (text, json, sarif) (default: "text")
--max-size <size> Maximum file size in characters (default: "10000")
-c, --config <path> Path to configuration file
--fix Automatically fix problems where possible
-h, --help Display help for command
cclint install [options]
Options:
--hooks Install pre-commit git hooks (default: true)
--pre-push Install pre-push quality check hooks (default: true)
-h, --help Display help for command
```
### Exit Codes
- `0`: No errors (warnings allowed)
- `1`: Errors found or execution failed
## ๐๏ธ Architecture
CC Linter follows a **hexagonal architecture** with clean separation of concerns:
```
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ CLI Adapter โ โ VS Code Extensionโ
โ โ โ (Future) โ
โโโโโโโโโโโฌโโโโโโโโ โโโโโโโโโโโฌโโโโโโโโ
โ โ
โโโโโโโโโโโโฌโโโโโโโโโโโโ
โ
โโโโโโโโโโโโผโโโโโโโโโโโโ
โ Core Engine โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ Rules Engine โ โ
โ โ - FileSizeRule โ โ
โ โ - StructureRule โ โ
โ โ - ContentRule โ โ
โ โ - FormatRule โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโ
```
### Domain Model
- **ContextFile**: Represents a CLAUDE.md file with parsing capabilities
- **Rule**: Interface for validation logic
- **Violation**: Represents a rule violation with location and severity
- **LintingResult**: Aggregates all violations for a file
## ๐ ๏ธ Development
### Prerequisites
- Node.js 18+
- npm or yarn
### Setup
```bash
# Clone the repository
git clone https://github.com/felixgeelhaar/cclint.git
cd cclint
# Install dependencies
npm install
# Run tests
npm test
# Build the project
npm run build
# Run the linter on itself
npm run dev -- lint CLAUDE.md
# Or after global install
cclint lint CLAUDE.md
```
### Scripts
```bash
npm test # Run test suite with Vitest
npm run test:watch # Run tests in watch mode
npm run test:coverage # Generate coverage report
npm run typecheck # Type check with TypeScript
npm run lint # Lint source code
npm run build # Build for production
npm run dev # Run development version
```
### Testing Philosophy
CC Linter follows **Test-Driven Development (TDD)**:
- โ
**371 tests** with comprehensive coverage
- ๐ **Vitest** for ultra-fast test execution
- ๐ฏ **Unit tests** for domain logic
- ๐ **Integration tests** for CLI functionality
- ๐ **Coverage reporting** for quality assurance
## ๐ค Contributing
We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) for details on:
- Code of conduct
- Development process
- Pull request requirements
- Testing guidelines
### Quick Contribution Steps
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes with tests
4. Run the test suite (`npm test`)
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to your branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request
## ๐ License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## ๐ Support
- ๐ [Documentation](https://github.com/felixgeelhaar/cclint#readme)
- ๐ [Report Issues](https://github.com/felixgeelhaar/cclint/issues)
- ๐ฌ [Discussions](https://github.com/felixgeelhaar/cclint/discussions)
- ๐ง [Email Support](mailto:felix@felixgeelhaar.de)
## ๐ Why CC Linter?
### For Developers
- **Consistency**: Maintain standardized CLAUDE.md files across projects
- **Quality**: Catch common issues before they impact AI interactions
- **Speed**: Fast feedback loop with instant validation
- **Integration**: Works with CI/CD pipelines and development workflows
### For Teams
- **Standards**: Enforce documentation standards across repositories
- **Onboarding**: Help new developers understand project structure
- **Maintenance**: Keep context files up-to-date and effective
- **Automation**: Integrate with existing development processes
## โ๏ธ Advanced Features
### Configuration Files
Create a `.cclintrc.json` file to customize rules for your project:
```json
{
"rules": {
"file-size": {
"enabled": true,
"severity": "warning",
"options": {
"maxSize": 15000
}
},
"structure": {
"enabled": true,
"options": {
"requiredSections": ["Overview", "Commands", "Architecture"]
}
}
},
"ignore": ["*.backup.md"]
}
```
Prefer a shared baseline? Extend a built-in preset instead of hand-writing rules:
```json
{
"extends": "@cclint/recommended"
}
```
- `@cclint/recommended` โ the sensible defaults (core rules as warnings).
- `@cclint/strict` โ every rule enabled, every violation an error (great for CI).
`extends` also accepts an array (applied left-to-right), and your own `rules`
always override the preset.
๐ [Full Configuration Guide](docs/configuration.md)
### Auto-fix
Automatically fix common formatting issues:
```bash
cclint lint CLAUDE.md --fix
```
### Git Hooks
Install pre-commit hooks to lint files automatically:
```bash
cclint install --hooks
```
Install pre-push hooks for comprehensive quality checks:
```bash
cclint install --pre-push
```
Install both hooks:
```bash
cclint install --hooks --pre-push
```
The pre-push hook runs:
- TypeScript type checking
- ESLint linting
- Prettier formatting check
- Full test suite
### GitHub Action
Add automated linting to your CI/CD pipeline:
```yaml
- name: Lint CLAUDE.md
uses: felixgeelhaar/cclint@v0.16.0
with:
files: 'CLAUDE.md'
format: 'text'
```
#### GitHub Code Scanning (SARIF)
Emit SARIF and upload it so violations appear as inline PR annotations and in
the repository's Code Scanning dashboard:
```yaml
- name: Lint CLAUDE.md (SARIF)
run: npx @felixgeelhaar/cclint lint CLAUDE.md --format sarif > cclint.sarif
continue-on-error: true # keep the run alive so the SARIF still uploads
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: cclint.sarif
```
๐ [GitHub Action Guide](docs/github-action.md)
### Custom Rules API
Create your own validation rules with the powerful Custom Rules API:
```javascript
import { CustomRule } from '@felixgeelhaar/cclint';
class MyCustomRule extends CustomRule {
constructor() {
super('my-rule', 'Description of my custom rule');
}
validateInternal(file) {
const violations = [];
// Your validation logic here
return violations;
}
generateFixes(violations, content) {
// Your auto-fix logic here
return [];
}
}
// Plugin export
export default {
name: 'my-plugin',
version: '1.0.0',
rules: [new MyCustomRule()],
};
```
**Configuration (.cclintrc.json):**
```json
{
"plugins": [
{
"name": "./my-plugin.js",
"enabled": true
}
],
"rules": {
"my-rule": {
"enabled": true,
"severity": "warning"
}
}
}
```
**Features:**
- ๐ **Plugin System**: Load custom rules dynamically
- ๐ฏ **TypeScript Support**: Full type safety and IntelliSense
- ๐ง **Auto-fix Integration**: Custom rules support automatic fixes
- โ๏ธ **Configurable**: Enable/disable and configure custom rules
- ๐ **Multiple Severities**: Error, warning, or info levels
๐ [View Example Custom Rules](examples/custom-rules/)
## ๐ฎ Roadmap
- [ ] **VS Code Extension** - Real-time linting in your editor
- [x] **Custom Rules API** - Plugin system for custom validation logic โ
- [x] **Enhanced Auto-fix** - More intelligent fixes and suggestions โ
- [x] **Configuration Files** - `.cclintrc.json` for project-specific rules โ
- [x] **Auto-fix Suggestions** - Automatic fixes for common issues โ
- [x] **Pre-push Quality Hooks** - Comprehensive quality checks before push โ
- [x] **Git Hooks Integration** - Pre-commit validation โ
- [x] **GitHub Action** - Easy CI/CD integration โ
---
<div align="center">
**Made with โค๏ธ by Felix Geelhaar for the Claude AI developer community**
[โญ Star us on GitHub](https://github.com/felixgeelhaar/cclint) โข [๐ฆ View on npm](https://www.npmjs.com/package/@felixgeelhaar/cclint) โข [๐ Report Bug](https://github.com/felixgeelhaar/cclint/issues)
</div>