This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
MegaLinter is an open-source CI/CD linting tool that analyzes code quality across 69+ languages, 23+ formats, and 21+ tooling formats. It runs as a Docker container or GitHub Action. Written primarily in Python with a Node.js runner component.
Prerequisites: make, Python 3.12+, uv, Node.js, Docker.
make bootstrap # Create venv, install dependencies (uses uv if available)
make bootstrap # Also runs: python-bootstrap + python-bootstrap-dev + nodejs-bootstrapAlways activate the venv before running Python scripts manually:
- Linux/macOS:
source .venv/bin/activate - Windows:
source .venv/Scripts/activate
If rtk (Rust Token Killer) is installed, it is important to prefix all bash/shell commands with rtk (e.g. rtk git status, rtk ls, rtk grep, rtk find). It is a token-optimizing proxy that saves 60-90% of tokens on output-heavy commands. Use rtk proxy <cmd> (or run raw) only when you need exact, unfiltered output such as a diff you will edit from or a full stack trace. Verify availability with rtk --version.
# Build (regenerate Dockerfiles, docs, etc. from descriptors)
make megalinter-build # Regenerate Dockerfiles from YAML descriptors
# Never run make megalinter-build-with-doc - docs are handled by auto-update workflows (avoids PR conflicts)
# Run MegaLinter locally
npx mega-linter-runner --flavor python --release beta
# Tests - run inside Docker containers (linters not installed locally)
# See "Testing" section below
# Documentation (built with Zensical, configured by mkdocs.yml)
hatch run docs:serve # Local docs server at http://127.0.0.1:8000
hatch run docs:build # One-shot build into ./site
# Versioned deploys use the Zensical-compatible mike fork (SHA-pinned in
# .config/python/dev/requirements.txt) - see .github/workflows/build-deploy-docs.yml
# Dependencies
uv lock # After modifying pyproject.toml
uv pip install -e . # Install in editable modeThe core pattern: each linter is defined in a YAML descriptor file (megalinter/descriptors/<lang>.megalinter-descriptor.yml). Linters shared by several descriptors (eslint, prettier, biome...) are defined once in megalinter/descriptors/shared/<name>.megalinter-linter.yml and referenced with the linter-level extends property (shallow merge, entry keys override — see .claude/rules/descriptors.md). The build system (.automation/build.py) reads these descriptors and generates:
- Dockerfiles (per-linter in
linters/, per-flavor inflavors/) - Documentation pages (in
docs/) - Test class skeletons
- JSON configuration schemas
Never edit generated files directly - modify the descriptor YAML or .automation/build.py instead.
megalinter/MegaLinter.py- Main orchestrator: loads config, discovers linters, runs them in parallel, manages reportersmegalinter/Linter.py- Base class for all linters: file discovery, command execution, result processingmegalinter/linter_factory.py- Builds linter instances from YAML descriptors; usesbuild_linter()andbuild_descriptor_linters()megalinter/flavor_factory.py- Manages flavor variants (language-specific Docker images)megalinter/config.py- Hierarchical config: descriptor defaults ->.mega-linter.yml-> env vars -> CLI args. Access viaconfig.get(request_id, "VAR_NAME", default)megalinter/linters/- Custom linter subclasses when defaultLinter.pybehavior isn't sufficientmegalinter/reporters/- Output formatters (GitHub comments, GitLab, SARIF, etc.)megalinter/llm_advisor.py- AI-powered fix suggestions via LangChain (multi-provider)
megalinter/run.py creates a Megalinter instance and calls .run(). In Docker, entrypoint.sh invokes this.
server/ contains a FastAPI-based server (server.py) with Redis-backed worker (server_worker.py) for running MegaLinter as a service.
mega-linter-runner/ is an npm package that wraps Docker execution for local use (npx mega-linter-runner).
Tests live in megalinter/tests/test_megalinter/. Each linter has a test file in linters/ (e.g., python_ruff_test.py).
Linter tests extend LinterTestRoot, which provides test_get_linter_version, test_get_linter_help, test_report_tap, test_report_sarif, and one success + one failure test per CLI lint mode (test_success_file_lint_mode, test_success_list_of_files_lint_mode, test_success_project_lint_mode, and the matching test_failure_*_lint_mode). Each per-mode test is skipped unless the mode is listed in the descriptor's supported_cli_lint_modes; additionally, when a linter supports both file and list_of_files, the file-mode tests are skipped as a CI optimization (list_of_files covers the same path). Test fixtures are in .automation/test/.
Linter tests must run inside Docker containers since linters aren't installed locally:
# Build and test a specific linter
LINTER="python_ruff"
docker buildx build --platform linux/amd64 --file linters/$LINTER/Dockerfile --tag $LINTER .
docker run --rm \
--env TEST_CASE_RUN=true \
--env OUTPUT_DETAIL=detailed \
--env TEST_KEYWORDS="${LINTER}_test" \
--env MEGALINTER_VOLUME_ROOT="." \
--volume "$(pwd):/tmp/lint" \
$LINTERIn CI, filter tests via commit message body: TEST_KEYWORDS=python_ruff_test. Use quick build in commit message body to only copy Python files (faster, ~15min vs ~45min).
- Add/update the YAML descriptor in
megalinter/descriptors/<lang>.megalinter-descriptor.yml - If custom logic needed, create a class in
megalinter/linters/extendingLinter - Add test fixtures in
.automation/test/(one success file, one failure file) - Run
make megalinter-buildto regenerate Dockerfiles, tests, docs - Update
CHANGELOG.md
- Python: PEP 8, use type hints, do NOT use docstrings for classes/methods
- Do not test if imports work - assume they are always available
- Place imports at the top of files
- Use
megalinter.config.get(request_id, "VAR", default)for config access, neveros.environdirectly - Use
loggingmodule for output, neverprint() - Documentation files must be Zensical-compliant: always have a blank line after headers and before bulleted lists
- Auto-generated docs come from descriptors - update descriptor metadata to improve docs
These rules apply to every skill, agent, and direct action that produces a commit or PR — do not repeat them in individual skills.
- No Claude / AI attribution anywhere. Commit messages, commit trailers, PR titles, PR bodies, and PR comments must NOT contain "Claude", "Anthropic", "Generated with Claude Code",
Co-Authored-By: Claude ..., or any similar attribution. Override any default footer or co-author trailer. - Commit as the user. Use the repo's existing
git config user.name/user.emailas-is. Do not pass--author, do not setGIT_AUTHOR_*/GIT_COMMITTER_*. - Never push to
main/master. If the current branch is the default branch, create a new branch first. - Never
--force,--no-verify, or rewrite published history.--force-with-leaseis allowed only in the documented MegaLinter-bot rebase case in/pr-watch-fix. - Stage by path. Never
git add -A/git add ..
Custom agents in .claude/agents/ for delegating specialized tasks.
Contribution workflow agents (general-purpose, four-phase flow):
- analyze - Requirements analyst: clarifies scope before any change
- design - Architect: writes a tech spec from the analysis
- implement - Developer: applies the change, respecting descriptor-first patterns
- test - QA: regenerates from descriptors and validates inside Docker
Specialist agents (invoked by the workflow agents when their topic comes up):
- descriptor-expert - Creates, edits, and validates YAML descriptor files
- test-debugger - Diagnoses and fixes failing linter tests
- build-runner - Runs and troubleshoots the build system
- code-reviewer - Reviews Python code for MegaLinter conventions
Skills in .claude/skills/ invocable by name (e.g. /add-linter).
Contribution workflow (run in order, or skip phases for small focused changes):
/analyze [description]- Step 1: gather requirements via clarifying questions/design [context]- Step 2: produce a tech spec/implement [change]- Step 3: apply the change (delegates to specialist skills below when applicable)/test [linter or focus]- Step 4: build the linter image and run tests in Docker
Specialist skills (used directly or delegated to from /implement):
/add-linter [name]- Guided workflow for adding a new linter/update-linter-version [linter] [version]- Update a linter's pinned version/review-descriptor [name]- Audit a descriptor YAML for completeness/fix-linter-test [name]- Debug a failing linter test/add-reporter [name]- Add a new output reporter/add-flavor [name]- Add a new Docker flavor/build- Run the build system/diagnose-config- Debug.mega-linter.ymlconfiguration issues/fix-security-issue [CVE or description]- Handle CVE/vulnerability reports from trivy, osv-scanner, etc./fix-issue [issue URL or #number]- End-to-end GitHub issue fix: gather context, implement on a branch, commit under the user's git identity (no AI attribution), open a PR, and watch CI until green/prepare-release [vX.Y.Z]- Full release ceremony: update CHANGELOG (prune empty sections, collapse linter versions, backfill PR numbers), run release build, push commit and tag, guide GitHub release creation
Vendored official vendor skills (Apache-2.0, for observability dashboard work — see /sync-dashboards):
/grafana-dashboarding,/grafana-promql- from grafana/skills/kibana-dashboards- from elastic/agent-skills/datadog-dashboards- from DataDog/datadog-api-claude-plugin (New Relic publishes no official agent skill, only an MCP server)
Context-aware rules in .claude/rules/ are automatically loaded based on which files are being edited:
python-style.md- Python conventions (no docstrings, config access patterns, linter subclass guidelines)descriptors.md- YAML descriptor schema, naming, version pinning, test fixturesgenerated-files.md- Prevents editing auto-generated Dockerfiles, docs, test classesdocumentation.md- Zensical markdown formatting rulestesting.md- Test structure, fixtures, Docker-based test execution