Guide me through adding the linter $ARGUMENTS to MegaLinter. If no linter name was provided, ask me for:
- The linter tool name (CLI executable)
- The language/format it lints
- Whether it belongs to an existing descriptor or needs a new one
Step 1 — Research the Linter
Search the internet to gather all available information about the linter:
- Official website URL and GitHub repository URL
- Rules/checks listing URL
- Configuration file format and default filename
- CLI flags: lint command, fix flag, version flag, help flag, config arg
- SARIF output support (can it output SARIF?)
- Available IDE extensions (VS Code, JetBrains, Sublime, Emacs, Eclipse, Atom, Visual Studio)
- SPDX license identifier (MIT, Apache-2.0, GPL-2.0, etc.)
- Whether it's a formatter (
is_formatter: true) or a linter
- Current latest stable version for pinning
- What package manager installs it (pip, npm, apk, gem, cargo, or raw Dockerfile)
- Supported platforms (linux/amd64, linux/arm64)
- Ignore file support (e.g.,
.eslintignore)
Step 2 — Create/Update the Descriptor
Check if a descriptor exists in megalinter/descriptors/ for this language. If not, create a new <lang>.megalinter-descriptor.yml.
If the linter covers several descriptors (like eslint, prettier or biome), define it once in megalinter/descriptors/shared/<linter_name>.megalinter-linter.yml (a complete standalone linter definition) and add a thin extends: <linter_name> entry in each descriptor with only the per-descriptor overrides (test_folder, examples, file_extensions…) — see .claude/rules/descriptors.md → "Shared Linter Definitions".
Add the linter entry with as many properties as possible. Even though the JSON schema only requires linter_name, linter_url, and examples, aim for maximum completeness. Fill in ALL of these when applicable:
Identity (required):
linter_name — CLI executable name, lowercase
linter_url — tool website
examples — CLI usage (with and without config file)
Documentation (strongly recommended):
linter_text — rich markdown description: features, what it checks, when to use it
linter_repo — GitHub repository URL
linter_rules_url — URL listing all rules
linter_rules_configuration_url — how to configure
linter_rules_inline_disable_url — how to suppress rules inline
linter_rules_ignore_config_url — how to ignore files
linter_spdx_license — SPDX license ID
linter_speed — 1 (very slow) to 5 (very fast)
linter_image_url, linter_icon_png_url, linter_banner_image_url — logos/banners
CLI configuration (always fill):
cli_lint_mode — default mode: file, list_of_files, or project
supported_cli_lint_modes — every mode the linter can run in (subset of file / list_of_files / project; defaults to ["file"], must include cli_lint_mode). Success/failure tests run once per declared mode, so only list modes the tool actually supports
cli_executable — if different from linter_name
config_file_name — default config file (e.g., .pylintrc)
cli_config_arg_name — config argument (e.g., --config)
cli_lint_extra_args — default extra arguments
cli_lint_fix_arg_name — fix argument (e.g., --fix)
cli_lint_fix_remove_args — args to remove in fix mode
cli_version_arg_name — if not --version
cli_help_arg_name — if not --help
ignore_file_name, cli_lint_ignore_arg_name — ignore file support
Excluded directories forwarding — when project is a supported lint mode, MegaLinter must forward EXCLUDED_DIRECTORIES to the tool or it will scan node_modules/build caches raw. Pick exactly ONE mechanism (see .claude/rules/descriptors.md → "Project Lint Mode: Forwarding Excluded Directories" for full semantics and known traps):
- native CLI flag →
cli_lint_mode_project_exclude_arg_name (+ _arg_value {{DIR}}/{{WORKSPACE}} template, _separator if a repeated flag overrides, _seed_values if the flag replaces the tool's built-in defaults, _config_key if it replaces a list in the tool's config file)
- flag taking an ignore file →
cli_lint_mode_project_exclude_ignore_file_arg_name (+ _seed_files, _pass_existing)
- generated/merged config needed →
manage_excluded_directories_config() override in the linter class
- tool that can only read an ignore file it discovers itself inside the repository → no forwarding: MegaLinter never writes in the analyzed sources, document the limitation in
linter_text
Research the official docs first: exact flag, value syntax (path/glob/regex, anchoring), repeatability, and whether it replaces config/built-in defaults — a wrong choice silently drops exclusions or clobbers user configuration.
Poison fixture — after declaring forwarding, add a deliberately failing file in .automation/test/<test_folder>/good/.wireit/ so test_success_project_lint_mode guards the forwarding against regressions (only if all project-capable linters sharing the folder have forwarding)
Error parsing (important for accurate counts):
cli_lint_errors_count — regex_count, regex_number, regex_sum, total_lines, or sarif
cli_lint_errors_regex — regex matching error lines in output
SARIF support (if available):
can_output_sarif: true
cli_sarif_args — with {{SARIF_OUTPUT_FILE}} placeholder
Behavior flags:
is_formatter: true — if it's a formatter
activation_rules — if it depends on env vars (e.g., style preference)
active_only_if_file_found — only activate if certain config files exist
Install (required):
IDE section (always fill):
ide.vscode, ide.idea, ide.eclipse, ide.sublime, ide.emacs, ide.atom, ide.visual_studio
- Each:
[{name: "Extension Name", url: "marketplace-url"}]
Testing:
test_folder — if different from lowercase descriptor_id
test_variables — env vars for tests
Look at existing well-populated descriptors like megalinter/descriptors/python.megalinter-descriptor.yml for reference.
Step 3 — Test Fixtures
Create two test files in .automation/test/<test_folder>/:
- A "good" file that passes linting cleanly
- A "bad" file that triggers at least one lint error matching
cli_lint_errors_regex
Step 4 — Custom Class (only if needed)
Create a minimal class in megalinter/linters/ extending megalinter.Linter. Only override what the YAML descriptor can't express.
Step 5 — Build
Run make megalinter-build to auto-generate Dockerfiles, test classes, schemas.
Do NOT run make megalinter-build-with-doc — documentation is handled by auto-update workflows and generating it in PRs causes merge conflicts.
Step 6 — Validate in Docker
LINTER="<descriptor_id_lowercase>_<linter_name>"
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" $LINTER
Step 7 — Finalize
1---2name: add-linter3description: Guided workflow for adding a new linter to MegaLinter. Use when a contributor needs to add support for a new linting tool.4---56Guide me through adding the linter `$ARGUMENTS` to MegaLinter. If no linter name was provided, ask me for:71. The linter tool name (CLI executable)82. The language/format it lints93. Whether it belongs to an existing descriptor or needs a new one1011## Step 1 — Research the Linter1213**Search the internet** to gather all available information about the linter:14- Official website URL and GitHub repository URL15- Rules/checks listing URL16- Configuration file format and default filename17- CLI flags: lint command, fix flag, version flag, help flag, config arg18- SARIF output support (can it output SARIF?)19- Available IDE extensions (VS Code, JetBrains, Sublime, Emacs, Eclipse, Atom, Visual Studio)20- SPDX license identifier (MIT, Apache-2.0, GPL-2.0, etc.)21- Whether it's a formatter (`is_formatter: true`) or a linter22- Current latest stable version for pinning23- What package manager installs it (pip, npm, apk, gem, cargo, or raw Dockerfile)24- Supported platforms (linux/amd64, linux/arm64)25- Ignore file support (e.g., `.eslintignore`)2627## Step 2 — Create/Update the Descriptor2829Check if a descriptor exists in `megalinter/descriptors/` for this language. If not, create a new `<lang>.megalinter-descriptor.yml`.3031If the linter covers **several descriptors** (like eslint, prettier or biome), define it once in `megalinter/descriptors/shared/<linter_name>.megalinter-linter.yml` (a complete standalone linter definition) and add a thin `extends: <linter_name>` entry in each descriptor with only the per-descriptor overrides (`test_folder`, `examples`, `file_extensions`…) — see `.claude/rules/descriptors.md` → "Shared Linter Definitions".3233Add the linter entry with **as many properties as possible**. Even though the JSON schema only requires `linter_name`, `linter_url`, and `examples`, aim for maximum completeness. Fill in ALL of these when applicable:3435**Identity (required):**36- `linter_name` — CLI executable name, lowercase37- `linter_url` — tool website38- `examples` — CLI usage (with and without config file)3940**Documentation (strongly recommended):**41- `linter_text` — rich markdown description: features, what it checks, when to use it42- `linter_repo` — GitHub repository URL43- `linter_rules_url` — URL listing all rules44- `linter_rules_configuration_url` — how to configure45- `linter_rules_inline_disable_url` — how to suppress rules inline46- `linter_rules_ignore_config_url` — how to ignore files47- `linter_spdx_license` — SPDX license ID48- `linter_speed` — 1 (very slow) to 5 (very fast)49- `linter_image_url`, `linter_icon_png_url`, `linter_banner_image_url` — logos/banners5051**CLI configuration (always fill):**52- `cli_lint_mode` — default mode: `file`, `list_of_files`, or `project`53- `supported_cli_lint_modes` — every mode the linter can run in (subset of `file` / `list_of_files` / `project`; defaults to `["file"]`, must include `cli_lint_mode`). Success/failure tests run once per declared mode, so only list modes the tool actually supports54- `cli_executable` — if different from `linter_name`55- `config_file_name` — default config file (e.g., `.pylintrc`)56- `cli_config_arg_name` — config argument (e.g., `--config`)57- `cli_lint_extra_args` — default extra arguments58- `cli_lint_fix_arg_name` — fix argument (e.g., `--fix`)59- `cli_lint_fix_remove_args` — args to remove in fix mode60- `cli_version_arg_name` — if not `--version`61- `cli_help_arg_name` — if not `--help`62- `ignore_file_name`, `cli_lint_ignore_arg_name` — ignore file support63- **Excluded directories forwarding** — when `project` is a supported lint mode, MegaLinter must forward `EXCLUDED_DIRECTORIES` to the tool or it will scan `node_modules`/build caches raw. Pick exactly ONE mechanism (see `.claude/rules/descriptors.md` → "Project Lint Mode: Forwarding Excluded Directories" for full semantics and known traps):64 - native CLI flag → `cli_lint_mode_project_exclude_arg_name` (+ `_arg_value` `{{DIR}}`/`{{WORKSPACE}}` template, `_separator` if a repeated flag overrides, `_seed_values` if the flag replaces the tool's built-in defaults, `_config_key` if it replaces a list in the tool's config file)65 - flag taking an ignore file → `cli_lint_mode_project_exclude_ignore_file_arg_name` (+ `_seed_files`, `_pass_existing`)66 - generated/merged config needed → `manage_excluded_directories_config()` override in the linter class67 - tool that can only read an ignore file it discovers itself inside the repository → **no forwarding**: MegaLinter never writes in the analyzed sources, document the limitation in `linter_text`6869 **Research the official docs first**: exact flag, value syntax (path/glob/regex, anchoring), repeatability, and whether it replaces config/built-in defaults — a wrong choice silently drops exclusions or clobbers user configuration.70- **Poison fixture** — after declaring forwarding, add a deliberately failing file in `.automation/test/<test_folder>/good/.wireit/` so `test_success_project_lint_mode` guards the forwarding against regressions (only if all project-capable linters sharing the folder have forwarding)7172**Error parsing (important for accurate counts):**73- `cli_lint_errors_count` — `regex_count`, `regex_number`, `regex_sum`, `total_lines`, or `sarif`74- `cli_lint_errors_regex` — regex matching error lines in output7576**SARIF support (if available):**77- `can_output_sarif: true`78- `cli_sarif_args` — with `{{SARIF_OUTPUT_FILE}}` placeholder7980**Behavior flags:**81- `is_formatter: true` — if it's a formatter82- `activation_rules` — if it depends on env vars (e.g., style preference)83- `active_only_if_file_found` — only activate if certain config files exist8485**Install (required):**86- Use renovate-compatible version pinning:87 ```yaml88 install:89 dockerfile:90 - |-91 # renovate: datasource=pypi depName=tool-name92 ARG PIP_TOOL_VERSION=1.2.393 pip:94 - tool-name==${PIP_TOOL_VERSION}95 ```96- Set `supported_platforms` with `install_override` for ARM if needed9798**IDE section (always fill):**99- `ide.vscode`, `ide.idea`, `ide.eclipse`, `ide.sublime`, `ide.emacs`, `ide.atom`, `ide.visual_studio`100- Each: `[{name: "Extension Name", url: "marketplace-url"}]`101102**Testing:**103- `test_folder` — if different from lowercase descriptor_id104- `test_variables` — env vars for tests105106Look at existing well-populated descriptors like `megalinter/descriptors/python.megalinter-descriptor.yml` for reference.107108## Step 3 — Test Fixtures109110Create two test files in `.automation/test/<test_folder>/`:111- A "good" file that passes linting cleanly112- A "bad" file that triggers at least one lint error matching `cli_lint_errors_regex`113114## Step 4 — Custom Class (only if needed)115116Create a minimal class in `megalinter/linters/` extending `megalinter.Linter`. Only override what the YAML descriptor can't express.117118## Step 5 — Build119120Run `make megalinter-build` to auto-generate Dockerfiles, test classes, schemas.121122**Do NOT run `make megalinter-build-with-doc`** — documentation is handled by auto-update workflows and generating it in PRs causes merge conflicts.123124## Step 6 — Validate in Docker125126```bash127LINTER="<descriptor_id_lowercase>_<linter_name>"128docker buildx build --platform linux/amd64 --file linters/$LINTER/Dockerfile --tag $LINTER .129docker run --rm --env TEST_CASE_RUN=true --env OUTPUT_DETAIL=detailed \130 --env TEST_KEYWORDS="${LINTER}_test" --env MEGALINTER_VOLUME_ROOT="." \131 --volume "$(pwd):/tmp/lint" $LINTER132```133134## Step 7 — Finalize135136- Add one line under **New linters** in `CHANGELOG.md` (repo root, beta section):137 ```text138 - Add [linter-name](linter_url) linter for <language> — <what it detects, one sentence>139 ```140- Branch naming: `user/add-<linter-name>`141- Use `quick build` + `TEST_KEYWORDS=<linter>_test` in commit message body during dev142- Last commit before PR merge must be a full build (~45 min)