configuration
This guide covers all configuration options for Lintro and the underlying tools it integrates. Learn how to customize behavior, set tool-specific options, and optimize
Configuration Guide
This guide covers all configuration options for Lintro and the underlying tools it integrates. Learn how to customize behavior, set tool-specific options, and optimize Lintro for your project.
TL;DR: Lintro uses your existing tool configs (
.prettierrc,pyproject.toml, etc.) automatically. It only provides fallback defaults when no native config exists. Useenforce.line_lengthto ensure consistent settings across all tools via CLI injection.
Configuration Model: 5-Tier System
Lintro uses a clear 5-tier configuration model that separates concerns:
| Tier | Purpose | When Applied |
|---|---|---|
| execution | What tools run and how | Always |
| enforce | Cross-cutting settings (line_length, target_python) | Always (via CLI flags) |
| defaults | Fallback config when no native config exists | Only when no native config |
| tools | Per-tool enable/disable and config source | Always |
| ai | AI-powered summaries and fixes | When enabled + API key set |
The five tiers above form the LintroConfig model’s core configuration story
(lintro/config/lintro_config.py). Two additional optional sections — review
(diff-review checklist) and score (health-score weights, see Health Score below) —
configure specific commands rather than tool resolution.
Key Principles
- Native configs are respected by default - Tools use their own
.prettierrc,pyproject.toml [tool.ruff], etc. enforcesettings override via CLI flags - Line length and target Python are injected as CLI arguments to ensure consistencydefaultsprovide fallbacks - Only used when a tool has no native config file- Simple and transparent - Users know exactly which config is used
Tiered Configuration Flow
The configuration system works in a specific order:
-
Execution Tier - Determines which tools run and in what order
enabled_tools: Empty list means all enabled tools run. An explicit named--toolslist on the CLI bypasses this allowlist; default runs and--tools allremain filtered by it.tool_order: Controls execution order (priority, alphabetical, or custom)fail_fast: Whether to stop on first tool failureparallel: Whether to run tools in parallel (default:true)max_workers: Maximum parallel workers, 1-32 (default: CPU count)auto_install_deps: Auto-install Node.js dependencies if missing, for the tools that need a project dependency tree (tsc,vue-tsc,svelte-check,astro-check). Unset by default, in which case Lintro falls back to container auto-detection (enabled inside containers, disabled otherwise)
-
Enforce Tier - Cross-cutting settings injected as CLI flags
- These settings override native configs via CLI arguments
- Example:
line_length: 88becomes--line-length 88for ruff/black - Applied to all tools that support the setting
-
Defaults Tier - Fallback configuration when no native config exists
- Only used if tool’s native config file is not found
- Example: If
.prettierrcdoesn’t exist, usedefaults.prettier - Generated as temporary config files when needed
-
Tools Tier - Per-tool enable/disable, config source, and auto-install
enabled: Whether the tool is enabledconfig_source: Optional explicit path to native config fileauto_install: Per-tool auto-install override (true/false/omit to inherit global)
-
AI Tier - AI-powered summaries and fix suggestions (opt-in)
enabled: Whether AI features are active (default:false)provider: AI provider to use (anthropicoropenai)- Applied only when enabled and a valid API key is set
Configuration Resolution Example
For a tool like Prettier:
- Check if tool is enabled (
tools.prettier.enabled) - Check for native config (
.prettierrc,.prettierrc.json, etc.) - If native config found:
- Use native config
- Inject
enforce.line_lengthas--print-widthCLI flag - Ignore
defaults.prettier
- If no native config found:
- Generate temp config from
defaults.prettier - Inject
enforce.line_lengthas--print-widthCLI flag - Use generated config
- Generate temp config from
This ensures consistent behavior while respecting tool-specific configurations.
Lintro Configuration
Configuration File: .lintro-config.yaml
Create a .lintro-config.yaml in your project root:
# Tier 1: EXECUTION - What tools run and how
execution:
enabled_tools: [] # Empty = all enabled tools run
tool_order: priority # priority | alphabetical | [custom list]
fail_fast: false
parallel: true # Run tools in parallel (default: true)
max_workers: 10 # Max parallel workers, 1-32 (default: CPU count)
auto_install_deps: false # Auto-install Node.js deps if missing
# Tier 2: ENFORCE - Cross-cutting settings injected via CLI flags
# These OVERRIDE native configs for consistency
enforce:
line_length: 88 # Injected as --line-length, --print-width, etc.
target_python: py313 # Injected as --target-version
# Tier 3: DEFAULTS - Fallback config when NO native config exists
# Only used if tool's native config file is not found
defaults:
prettier:
semi: true
singleQuote: true
yamllint:
extends: default
# Tier 4: TOOLS - Per-tool enable/disable, config source, and auto-install
tools:
ruff:
enabled: true
prettier:
enabled: true
config_source: '.prettierrc' # Optional: explicit native config path
tsc:
auto_install: true # Override global auto_install for this tool only
Configuration Report Command
Use lintro config to view the current configuration status for all tools:
# View configuration report
lintro config
# Show detailed output including native configs
lintro config --verbose
# Output as JSON for scripting
lintro config --json
The config command shows:
- Enforce settings: Central
line_length,target_python - Tool execution order: Based on configured strategy (priority, alphabetical, or custom)
- Per-tool configuration: Whether enabled, native config found
- Defaults applied: Which tools are using fallback defaults
Health Score
lintro check computes a single, deterministic 0-100 health score that aggregates
every issue across every tool into one trackable, CI-gateable, shareable number.
lintro check # normal output + health score line at the end
lintro check --score # print ONLY the score (for scripts/badges)
lintro check --fail-under 75 # exit 1 if the score is below 75
lintro check --output-format json # score included under summary.health_score
Scoring model
Every issue is normalised to one of three severities (ERROR, WARNING, INFO) and
weighted, then mapped onto 0-100 with a smoothly saturating penalty:
weighted = error_weight * n_errors
+ warning_weight * n_warnings
+ info_weight * n_info
score = floor( 100 * scale / (scale + weighted) )
With the default weights (ERROR=10, WARNING=3, INFO=1) and scale=100, this has
the following guaranteed properties:
- Zero issues → exactly 100. A clean run is unambiguous.
- Any issue → strictly below 100 (
floorkeeps it at most 99). - Monotonic. Adding an issue, or raising its severity, never raises the score.
- Bounded to
[0, 100]and deterministic — the result depends only on the severity counts and the configured weights/scale, never on ordering or timing.
The score hits 50 when the total weighted penalty equals scale (e.g. ten ERROR
issues, or ~33 WARNING issues, with the defaults).
Score tiers
| Score | Tier |
|---|---|
| 75-100 | great |
| 50-74 | needs-work |
| 0-49 | critical |
Configuring the weights
Weights and the smoothing scale are tunable via the score section:
# .lintro-config.yaml
score:
error_weight: 10 # penalty per ERROR issue
warning_weight: 3 # penalty per WARNING issue
info_weight: 1 # penalty per INFO issue
scale: 100 # larger = the score decays more slowly
JSON output
In --output-format json the score is added additively under
summary.health_score, leaving all existing keys untouched:
{
"summary": {
"total_issues": 3,
"total_fixed": 0,
"total_remaining": 3,
"health_score": {
"score": 88,
"tier": "great",
"severity_counts": { "error": 1, "warning": 0, "info": 0 },
"weighted_penalty": 10.0
}
}
}
Tool Timeouts in the JSON Report
A tool timeout is an execution failure, never a lint finding. Every tool accounts for one the same way, so a consumer can tell an infrastructure flake apart from a genuine finding using evidence about its own run.
A timed-out tool reports:
success: false— the run failed, and the process exit code stays non-zero.timed_out: true— the machine-readable marker to classify on. No need to regex-match the human-readableoutputstring.issues_countcounting only genuine findings. No syntheticTIMEOUTpseudo-issue is invented, and the timeout never reachessummary.total_issues. Issues legitimately detected before the timeout (for example the pre-fix check of aformatrun) are still reported.
summary.timed_out_tools lists the tools that timed out, in execution order:
{
"summary": {
"total_issues": 0,
"total_fixed": 0,
"total_remaining": 0,
"timed_out_tools": ["mypy"]
},
"results": [
{
"tool": "mypy",
"success": false,
"issues_count": 0,
"timed_out": true,
"output": "mypy execution timed out (300s limit exceeded)..."
}
]
}
This makes the conservative CI classification expressible: at least one tool timed out
AND total_issues == 0. A run with a real finding still reports a non-zero
total_issues, and a non-timeout tool failure reports timed_out: false, so neither is
ever mistaken for a flake.
Artifacts Under GitHub Actions
When GITHUB_ACTIONS=true is detected, lintro auto-emits two side-channel artifacts
regardless of --output-format, so the console/grid output every existing consumer
parses is untouched and no second invocation is needed:
| Format | Path | Purpose |
|---|---|---|
| SARIF | .lintro/artifacts/sarif/results.sarif.json | GitHub Code Scanning ingestion |
| JSON | .lintro/artifacts/json/results.json | Structured CI evidence (see above) |
SARIF omits clean tools and omits failures that produced no issues, so it cannot be used
to classify a timeout without failing open. The JSON report covers every tool that ran
and carries the per-tool timed_out flag.
Any format listed in execution.artifacts is emitted in addition to these, in every
environment.
Output Presentation
Purely cosmetic console output is controlled by the output section. These settings
never affect machine-readable documents (JSON/SARIF) or on-disk artifacts.
# .lintro-config.yaml
output:
art: true # Show decorative ASCII art after a run (default: true)
The decorative ASCII art printed after check/fmt is only emitted to an interactive
terminal. It is always suppressed when:
- stdout is not a TTY (piped output, CI logs, redirected files), or
output.art: falseis set in config, or- the
--no-artflag is passed tolintro check/lintro format.
The art is never written to .lintro/run-*/report.md, console.log, or any
--output-format stream regardless of these settings.
lintro check --no-art # suppress art for this run only
lintro format --no-art
Command-Line Options
Global Options
# Output options
lintro check # Use grid formatting
lintro check --output results.txt # Save output to file
lintro check --group-by [file|code|none|auto] # Group issues
# Tool selection
lintro check --tools ruff,prettier # Run specific tools only
lintro check --all # Run all available tools
# File filtering
lintro check --exclude "*.pyc,venv" # Exclude patterns
lintro check --include-venv # Include virtual environments
lintro check path/to/files # Check specific paths
Confirmation Prompt Options
# Skip confirmation prompts (auto-accept)
lintro check --yes
lintro check -y
# Useful in scripts and CI where no interactive input is available
lintro format --yes --tools ruff,prettier
The --yes/-y flag is available for check, format, and test commands. It skips
any confirmation prompts that would otherwise pause execution.
Note: Confirmation prompts are automatically skipped in non-TTY environments (CI
pipelines, Docker containers, scripts with redirected output), so --yes is typically
only needed when running interactively and you want to avoid prompts.
Node.js Dependency Options
# Auto-install Node.js dependencies if node_modules is missing
lintro check --auto-install --tools tsc
# Useful for TypeScript projects where dependencies aren't installed
lintro check src/ --tools tsc,oxlint --auto-install
The --auto-install flag is available for both check and format commands. When
enabled, Lintro runs bun install (or npm install if bun is unavailable) before
running the tools that need a project’s dependency tree to work: tsc, vue-tsc,
svelte-check and astro-check.
It does not apply to standalone Node.js binaries such as prettier, oxlint,
oxfmt, stylelint or markdownlint-cli2. Those run fine without a local
node_modules: if the project has not installed them, resolution falls through to a
binary on PATH and then to a version-pinned bunx/npx fetch — see
Node.js Tool Resolution. When one of them cannot be resolved
at all, Lintro reports a ⏭️ SKIP row with the reason instead of a pass — installing
project dependencies would not have helped.
You can also enable this globally via configuration:
# .lintro-config.yaml
execution:
auto_install_deps: true
Per-tool auto-install lets you override the global setting for individual tools:
# .lintro-config.yaml
tools:
tsc:
auto_install: true # Always auto-install deps for tsc
prettier:
auto_install: false # Never auto-install deps for prettier
oxlint:
# Omit auto_install to inherit the global setting
Resolution order: per-tool auto_install > global auto_install_deps > false
Container Auto-Detection
Lintro automatically detects container environments (Docker, Podman, LXC, Kubernetes) and enables auto-install by default when running in a container. This means Node.js tools work out of the box in Docker without any configuration.
You can override this behavior explicitly via configuration or the --auto-install CLI
flag:
# .lintro-config.yaml — disable auto-install even inside a container
execution:
auto_install_deps: false
See the Docker Usage Guide for more details on container behavior.
Tool-Specific Options
# Tool-specific options (key=value; lists use |)
lintro check --tool-options "ruff:line_length=88,prettier:print_width=80"
# Example with lists and booleans
lintro check --tool-options "ruff:select=E|F|W,ruff:preview=True"
# Exclude patterns
lintro check --exclude "*.pyc,venv,node_modules"
Environment Variables
Lintro reads the following environment variables at runtime. Most configuration is done
through .lintro-config.yaml and CLI flags; these variables cover a few runtime
overrides.
# Override the directory where run logs/artifacts are written (default: .lintro)
export LINTRO_LOG_DIR=/tmp/lintro-runs
# Timeout (seconds) for tool version checks (default: 30)
export LINTRO_VERSION_TIMEOUT=60
# Force Docker install-context detection (set to 1)
export LINTRO_DOCKER=1
# Opt in to loading external (third-party) plugins. Disabled by default.
export LINTRO_ENABLE_EXTERNAL_PLUGINS=1
# AI config overlays (flag > env > .lintro-config.yaml > default). See
# docs/ai-features.md "Invocation overrides".
export LINTRO_AI_PROVIDER=cursor
export LINTRO_AI_MODEL=cursor-grok-4.6-high
export LINTRO_AI_TRANSPORT=cli
export LINTRO_AI_ENABLED=1
export LINTRO_AI_MAX_COST_USD=0 # 0 = uncapped; a positive number is a USD cap
| Variable | Description | Default |
|---|---|---|
LINTRO_LOG_DIR | Base directory for run logs and artifacts | .lintro |
LINTRO_VERSION_TIMEOUT | Timeout in seconds for tool version checks (must be >= 1) | 30 |
LINTRO_DOCKER | Force Docker install-context detection when set to 1 | - |
LINTRO_CONFIG | Shown in the lintro environment report; informational only | - |
LINTRO_ENABLE_EXTERNAL_PLUGINS | Opt in to loading external (third-party) plugins (1/0) | 0 |
LINTRO_AI_PROVIDER | Override ai.provider (anthropic / openai / cursor) | - |
LINTRO_AI_MODEL | Override ai.model | - |
LINTRO_AI_TRANSPORT | Override ai.transport (api / cli) | - |
LINTRO_AI_ENABLED | Override ai.enabled (1/0/true/false) | - |
LINTRO_AI_MAX_COST_USD | Override ai.max_cost_usd (positive USD cap; 0 = uncapped) | - |
Note: There is no environment variable for tool timeouts, verbosity, exclude patterns, output format, or auto-install. Use CLI flags (
--exclude,--output-format,--auto-install) or.lintro-config.yamlfor those settings. Auto-install is resolved from the--auto-installflag, thenexecution.auto_install_deps, then container auto-detection — not from an environment variable.
External Plugins (Trust Model)
Lintro can load third-party tool plugins published as Python packages that expose a
lintro.plugins entry point. Because loading a plugin imports and executes its code,
external plugins are disabled by default — a default installation never runs
third-party plugin code at startup. This is a security boundary: any package installed
in the same environment could otherwise execute arbitrary code every time Lintro runs.
See SECURITY.md for the full threat model.
Enable external plugins only after you have reviewed and trust them, using either mechanism:
# .lintro-config.yaml
plugins:
# Opt in and restrict loading to an explicit allowlist (recommended).
# Names match the entry-point name or the distribution (package) name.
trusted:
- my-org-tool
- another-plugin
# Optional: enable loading of ALL discovered plugins (no allowlist).
# Prefer 'trusted' over this blanket toggle.
enabled: false
Equivalent pyproject.toml:
[tool.lintro.plugins]
trusted = ["my-org-tool", "another-plugin"]
enabled = false
Or, for a one-off/CI run, the environment variable:
export LINTRO_ENABLE_EXTERNAL_PLUGINS=1
Resolution rules:
- Loading is enabled when
LINTRO_ENABLE_EXTERNAL_PLUGINSis truthy or thepluginsconfig opts in (atrustedallowlist is itself an opt-in, as isenabled: true). - When a
trustedallowlist is present, only plugins whose entry-point name or distribution name is listed are loaded; all others are skipped and logged, regardless of how loading was enabled. - With no allowlist configured, enabling loads all discovered
lintro.pluginsentry points — use this only in fully trusted environments.
Pre-Execution Summary
Before running tools, Lintro displays a configuration summary table showing the effective settings for the current run:
- Environment: Local, Container, or CI
- Auto-install: Whether auto-install is enabled (and the source — CLI flag, config, or container detection)
- Tools: Which tools will run
- Skipped tools: Which tools were skipped and why
This summary is shown for all output formats except JSON (--output-format json).
Skipped Tools
When tools are skipped, Lintro reports them in the summary table with a SKIP status
and a note explaining the reason. Common skip reasons:
| Reason | Description |
|---|---|
node_modules not found | Node.js deps missing and auto-install is disabled |
disabled in config | Tool disabled via tools.<name>.enabled: false |
not in enabled_tools | Tool not in execution.enabled_tools allowlist (default/--tools all only; named --tools bypasses) |
deferred to <tool> | Framework tool preferred (e.g., tsc to vue-tsc) |
| Version check messages | Tool version below minimum required |
Skipped tools do not affect exit codes — only tools that run and find issues contribute to a non-zero exit.
Disabling a tool from pyproject.toml
The tools: section of .lintro-config.yaml has two equivalent pyproject.toml
spellings — a flat per-tool table, or a nested tool/tools table:
[tool.lintro.trufflehog]
enabled = false
# Equivalent, mirroring the YAML `tools:` section
[tool.lintro.tool.trufflehog]
enabled = false
pyproject.toml is a fallback: when a .lintro-config.yaml exists, it is the only
configuration source and these tables are not consulted.
Project Setup with lintro init
Run lintro init to detect project languages, select an install profile, and generate a
.lintro-config.yaml tailored to your stack:
lintro init # auto-detect languages and write config
lintro init --minimal # fewer defaults
lintro init --profile python # use a specific profile
lintro init --force # overwrite existing config
If a config file already exists, lintro init merges new tool entries without
clobbering user-managed sections. Use --force to replace the file entirely.
After init, run lintro install --profile recommended and lintro doctor to install
and verify tools.
Doctor: Config-Aware Health Checks
lintro doctor respects execution.enabled_tools and per-tool tools.<name>.enabled
settings. By default, disabled tools are shown separately and not counted as failures:
lintro doctor # check enabled tools only
lintro doctor --all # check every manifest tool
lintro doctor --tools ruff,mypy # explicit tools override config filtering
lintro doctor --json # machine-readable output for CI
The --json output includes per-tool fields: installed, recommended, min_version,
status (OK, MISSING, OUTDATED, INCOMPATIBLE, DISABLED, UNKNOWN), install_hint, and
upgrade_hint.
Node.js Package Manager Policy {#node-package-manager-policy}
lintro install used to pick bun whenever bun happened to be on PATH, and to install
globally regardless of what the project said. In an npm-first repository that
created two authorities: your own commands and your editor used the project’s local
dependency, while lintro installed and upgraded a global one (#2005).
Which manager, in priority order:
- Explicit choice —
lintro install --node-package-manager npm. packageManagermetadata — the Corepack field inpackage.json("packageManager": "pnpm@9.1.0").- Lockfile evidence —
bun.lock/bun.lockb,pnpm-lock.yaml,yarn.lock,package-lock.json,npm-shrinkwrap.json. - Available manager — bun if installed, otherwise npm, then pnpm, then yarn. bun and npm are lintro’s own preference; pnpm and yarn are included so a machine that only has those still gets a command it can run. This is the only step where lintro’s own preference decides anything.
Availability does not veto the first three. If your project is npm-locked but only
bun is installed, lintro still tells you to run npm install -D … rather than quietly
writing a bun.lock into your repository.
Where it installs. Inside a Node project (anything with a package.json at or above
the working directory), lintro adds a dev dependency — npm install -D <pkg>@<ver>
— because the project owns its tool versions and a lockfile-pinned dependency is what
Node.js Tool Resolution will run. Global installs are
reserved for --global or for an environment with no project manifest at all (a bare CI
runner, a container image, your $HOME). The upward search for a manifest stops at the
first directory containing a .git entry, and never treats $HOME itself as a project
root unless that is where you ran the command — a stray ~/package.json must never
collect your tools.
Project pins are never replaced implicitly. If package.json declares a version
that differs from lintro’s recommendation, --upgrade reports the difference and asks
for an explicit decision instead of rewriting your manifest:
$ lintro install prettier --upgrade
Node package manager: npm (from lockfile) → project dev dependency
…
prettier Upgrade prettier explicitly: this project pins 3.1.0 in package.json but
lintro recommends 3.9.4. Run `npm install -D prettier@3.9.4` to adopt
lintro's version, or keep the project pin.
The comparison is deliberately literal, not a semver range solve: a spec that names the
recommended version exactly — 3.9.4, ^3.9.4, ~3.9.4, =3.9.4, v3.9.4 — is not a
conflict and upgrades normally. On a first install of a declared-but-missing package,
those same spellings emit the manager’s install-all command (npm install,
bun install, …) so the lockfile pin is restored, rather than a versioned add that
would rewrite the range. Anything else, including a wider range such as ^3.9.0 that a
resolver would satisfy with 3.9.4, is reported so you decide. Erring toward asking is
deliberate: the cost of a needless question is far below the cost of silently rewriting
someone’s package.json.
Planning matches execution. The version probe for npm-installed tools resolves
through the same chain a check uses, so lintro install reports on the binary
lintro check will actually run rather than on whatever a bare name finds on PATH.
| Flag | Effect |
|---|---|
--node-package-manager {bun,npm,pnpm,yarn} | Force the manager, overriding packageManager and lockfiles. |
--global | Install npm-managed tools globally instead of as project dev dependencies. |
Install Lock / Export
Use lintro install --write-lock to capture the resolved install plan:
lintro install --profile recommended --write-lock
This writes .lintro-install.lock.json containing every tool in the plan with its
version, install hint, and status (to_install, to_upgrade, ok, outdated,
manual, skipped), plus the selected profile and detected languages. Share this file
with teammates or CI to reproduce the same tool set.
Tool Configuration
Lintro respects each tool’s native configuration files, allowing you to leverage existing setups.
Enforce Settings (Cross-Cutting Concerns)
The enforce tier contains settings that MUST be consistent across tools. These are
injected directly as CLI flags to each tool, overriding their native configs.
enforce:
line_length: 88 # Injected as --line-length (ruff, black) or --print-width (prettier)
target_python: py313 # Injected as --target-version (ruff, black)
How CLI injection works:
| Tool | CLI Flag for line_length | CLI Flag for target_python |
|---|---|---|
| Ruff | --line-length 88 | --target-version py313 |
| Black | --line-length 88 | --target-version py313 |
| Prettier | --print-width 88 | N/A |
Tools without CLI support:
Some tools (Yamllint, Markdownlint) don’t have CLI flags for line length. For these:
- Use the
defaultstier to provide fallback config - Or configure their native config files manually
Defaults Tier (Fallback Config)
The defaults tier provides fallback configuration for tools that have no native config
file. This is useful for ensuring consistent settings without creating multiple config
files.
defaults:
prettier:
semi: true
singleQuote: true
tabWidth: 2
trailingComma: es5
yamllint:
extends: default
rules:
line-length:
max: 88
markdownlint:
MD013:
line_length: 88
code_blocks: false
tables: false
When defaults are applied:
- Lintro checks if the tool has a native config file (e.g.,
.prettierrc) - If NO native config exists, Lintro generates a temp file from
defaults - If native config EXISTS,
defaultsare ignored (native config is used)
Tool Ordering Configuration
Lintro supports configurable tool execution order. By default, tools run in priority order (formatters before linters), but you can change this behavior.
[tool.lintro]
# Tool order strategy: "priority" (default), "alphabetical", or "custom"
tool_order = "priority"
# For "custom" strategy, specify the order explicitly
tool_order_custom = ["prettier", "black", "ruff", "markdownlint", "yamllint"]
# Override individual tool priorities (lower = runs first)
tool_priorities = { ruff = 5, black = 10, prettier = 1 }
Tool Order Strategies:
| Strategy | Description |
|---|---|
priority | Formatters run before linters based on priority values (default) |
alphabetical | Tools run in alphabetical order by name |
custom | Tools run in order specified by tool_order_custom |
Default Tool Priorities:
| Tool | Priority | Type |
|---|---|---|
| prettier | 10 | Formatter |
| black | 15 | Formatter |
| ruff | 20 | Linter/Formatter |
| markdownlint | 30 | Linter |
| html_validate | 30 | Linter |
| yamllint | 35 | Linter |
| pydoclint | 40 | Linter |
| bandit | 45 | Security |
| hadolint | 50 | Infrastructure |
| vale | 50 | Linter (docs) |
| actionlint | 55 | Infrastructure |
| pytest | 100 | Test Runner |
Lower priority values run first. This ensures formatters run before linters, avoiding false positives from linters detecting issues that formatters would fix.
Post-checks Configuration
Black is integrated as a post-check tool by default. Post-checks run after the main tools complete and can be configured to enforce failure if issues are found. This avoids double-formatting with Ruff and keeps formatting decisions explicit.
[tool.lintro.post_checks]
enabled = true
tools = ["black"] # Black runs after core tools
enforce_failure = true # Fail the run if Black finds issues in check mode
Notes:
- With post-checks enabled for Black, Ruff’s
format/format_checkstages can be disabled or overridden via CLI when desired. - In
lintro check, Black runs with--checkand contributes to failure whenenforce_failureis true. Inlintro format, Black formats files in the post-check phase.
Black Options via --tool-options
You can override Black behavior on the CLI. Supported options include line_length,
target_version, fast, preview, and diff.
# Increase line length and target a specific Python version
lintro check --tool-options "black:line_length=100,black:target_version=py313"
# Enable fast and preview modes
lintro format --tool-options "black:fast=True,black:preview=True"
# Show diffs during formatting (in addition to applying changes)
lintro format --tool-options "black:diff=True"
These options can also be set in pyproject.toml under [tool.lintro.black]:
[tool.lintro.black]
line_length = 100
target_version = "py313"
fast = false
preview = false
diff = false
Ruff vs Black Policy (Python)
Lintro enforces Ruff-first linting and Black-first formatting when Black is configured as a post-check.
- Ruff: primary linter (keep strict rules like
COM812trailing commas andE501line length enabled for checks) - Black: primary formatter (applies formatting during post-checks; performs safe line breaking where Ruff’s auto-format may be limited)
Runtime behavior with Black as post-check:
-
lintro format
- Ruff fixes lint issues only (Ruff
format=False) unless explicitly overridden - Black performs formatting in the post-check phase
- Ruff fixes lint issues only (Ruff
-
lintro check
- Ruff runs lint checks (Ruff
format_check=False) unless explicitly overridden - Black runs
--checkas a post-check to enforce formatting
- Ruff runs lint checks (Ruff
Overrides when needed:
# Force Ruff to format during fmt
lintro format --tool-options ruff:format=True
# Force Ruff to include format check during check
lintro check --tool-options ruff:format_check=True
Rationale:
- Avoids double-formatting churn (Ruff format followed by Black format) while preserving
Ruff’s stricter lint rules (e.g.,
COM812,E501). - Black’s safe wrapping is preferred for long lines; Ruff continues to enforce lint limits during checks.
Node.js Tool Resolution {#nodejs-tool-resolution}
Every Node.js tool resolves the same way. There is one chain, implemented once in
NodeJSBuilder (lintro/tools/core/command_builders.py), and it applies to
astro check, commitlint, html-validate, markdownlint-cli2, oxfmt, oxlint,
prettier, stylelint, svelte-check, tsc and vue-tsc alike:
node_modules/.bin/<binary>, searched upward from the directory being checked until the nearestpackage.jsonor.git(whichever is hit first). A nested package with its ownpackage.jsonstops there; a decoynode_modulesabove that boundary is ignored. This is the preferred answer: it is lockfile-pinned, offline, and it is the same binary your editor and your ownnpm runscripts use.- the
PATH-resolved absolute path of<binary>— a global install (bun add -g,npm install -g) or a Homebrew formula. bunx <package>@<pinned>/npx --yes --package <package>@<pinned> <binary>— a registry fetch at the version Lintro pins in its manifest.bunxis tried first, thennpx.bunxkeeps the short form when the executable name matches the package name (bunx prettier@<pinned>); it uses--packagewhen they differ (bunx --package typescript@<pinned> tsc).npxalways uses--yes --packageso it cannot hang waiting for a TTY prompt.- bare
<binary>— last resort, fails if nothing is installed.
@latest is never resolved at runtime, for any tool. Branch 3 emits a one-time warning
because it needs network access to the npm registry and imposes the pinned package’s own
engines floor on your runtime; a failure on that branch is reported with install
guidance rather than the tool’s raw error.
Which install should I use? A project devDependency (bun add -D <pkg> /
npm install -D <pkg>) is the best answer for every Node tool — it wins the chain and
pins the version through your lockfile. A global or Homebrew install is a valid fallback
and is used whenever no project-local install is present.
Changed after v0.115.0 (#1811). The chain above used to apply only to
html-validate. Other Node tools either went straight tobunx <binary>/npx <binary>(never consultingPATH, and resolving@latest) or preferredPATHahead of any project-local install. Two consequences worth checking after upgrading:
- A project-local install now wins. If a project pins an older tool version as a devDependency while you relied on a newer global one, Lintro now runs the local pin — the same version your editor runs. Remove the devDependency, or upgrade it, if you wanted the global.
prettieris no longerPATH-only. It previously had no Node builder at all and was resolved as a bareprettiername againstPATH; a project’s lockfile-pinned prettier was never used. It now follows the same chain as everything else.Also fixed on the way:
commitlintandmarkdownlint-cli2had nonpxbranch, so on a machine with npm but no bun a devDependency was unreachable and the tool reported a skip. Both now resolve like every other Node tool.
Because the version check (verify_tool_version) resolves through this same chain, the
binary Lintro version-gates is now the binary Lintro runs, for every Node tool rather
than just for html-validate.
Python Tools
Ruff Configuration
File: pyproject.toml
[tool.ruff]
# Basic configuration
line-length = 88
target-version = "py313"
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".mypy_cache",
".ruff_cache",
".venv",
"__pypackages__",
"migrations",
]
# Rule selection
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # Pyflakes
"I", # isort
"N", # pep8-naming
"D", # pydocstyle
"UP", # pyupgrade
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"SIM", # flake8-simplify
]
ignore = [
"D100", # Missing docstring in public module
"D104", # Missing docstring in public package
]
# Per-file ignores
[tool.ruff.per-file-ignores]
"tests/**/*.py" = ["D100", "D103"]
"__init__.py" = ["F401"]
# Import sorting
[tool.ruff.isort]
known-first-party = ["lintro"]
force-single-line = true
# Docstring configuration
[tool.ruff.pydocstyle]
convention = "google"
Alternative: setup.cfg
[tool:ruff]
line-length = 88
select = E,W,F,I,N,D
exclude = .git,__pycache__,.venv
Mypy Configuration
- Default run mode:
--strictwith--ignore-missing-importsenabled to avoid third-party stub noise (applied unless you override viaset_options()or--tool-options). - Config discovery: mypy auto-discovers
pyproject.toml [tool.mypy],mypy.ini, orsetup.cfg [mypy]and the discovered config is passed via--config-file. When a native config providesexclude, Lintro does not add its default test/test_samples excludes; otherwise, it applies the defaults plus.lintro-ignore. - Recommended overrides when needed:
# Disable strict mode temporarily
lintro check --tools mypy --tool-options mypy:strict=False
# Surface missing-import errors (no ignore)
lintro check --tools mypy --tool-options mypy:ignore_missing_imports=False
# Pin target Python version
lintro check --tools mypy --tool-options mypy:python_version=3.13
Bandit Configuration
File: pyproject.toml
[tool.bandit]
exclude_dirs = ["tests", "venv", ".git"]
tests = ["B101,B102,B103"] # Specific test IDs to run
skips = ["B101"] # Test IDs to skip
confidence = "MEDIUM" # Minimum confidence level
severity = "LOW" # Minimum severity level
[tool.bandit.assert_used]
exclude = ["test_*.py"] # Files to exclude from assert_used test
File: .bandit
[bandit]
exclude = tests,venv,.git
tests = B101,B102,B103
skips = B101
confidence = MEDIUM
severity = LOW
[[tool.bandit.assert_used]]
exclude = test_*.py
Available Options:
tests: Comma-separated list of test IDs to runskips: Comma-separated list of test IDs to skipexclude: Comma-separated list of paths to excludeexclude_dirs: List of directories to exclude (pyproject.toml only)severity: Minimum severity level (LOW,MEDIUM,HIGH)confidence: Minimum confidence level (LOW,MEDIUM,HIGH)baseline: Path to baseline report for comparison
Semgrep Configuration
Semgrep is a fast, open-source static analysis tool for security scanning and code quality enforcement across 30+ languages.
File: .semgrep.yaml or .semgrep.yml
rules:
- id: custom-security-rule
pattern: eval(...)
message: 'Avoid using eval() - potential code injection'
languages: [python]
severity: ERROR
Available Options via --tool-options:
| Option | Type | Description |
|---|---|---|
config | string | Rule config: auto, p/python, p/javascript, or path |
exclude | list | Patterns to exclude from scanning |
include | list | Patterns to include in scanning |
severity | string | Minimum severity: INFO, WARNING, ERROR |
timeout_threshold | int | Per-file timeout in seconds |
jobs | int | Number of parallel jobs |
Example Usage:
# Use Python security rules
lintro check --tools semgrep --tool-options "semgrep:config=p/python"
# Filter by severity
lintro check --tools semgrep --tool-options "semgrep:severity=ERROR"
# Exclude test files
lintro check --tools semgrep --tool-options "semgrep:exclude=tests/*|vendor/*"
Gitleaks Configuration
File: .gitleaks.toml
# Custom rule example
[[rules]]
id = "custom-api-key"
description = "Custom API Key Pattern"
regex = '''custom_api_key_[a-zA-Z0-9]{32}'''
tags = ["key", "custom"]
# Allowlist to ignore false positives
[allowlist]
paths = [
'''test_samples/''',
'''\.git/''',
]
regexes = [
'''EXAMPLE''',
'''test_''',
]
Available Options:
| Option | Type | Description |
|---|---|---|
no_git | boolean | Scan without git history (files only) |
config | string | Path to custom gitleaks config file |
baseline_path | string | Path to baseline file (ignore known secrets) |
redact | boolean | Redact secrets in output (default: true) |
max_target_megabytes | integer | Skip files larger than this size in MB |
Usage Examples:
# Basic scan with default config
lintro check --tools gitleaks
# Scan with git history (not just files)
lintro check --tools gitleaks --tool-options gitleaks:no_git=False
# Use custom config file
lintro check --tools gitleaks --tool-options gitleaks:config=.gitleaks.toml
# Use baseline to ignore known secrets
lintro check --tools gitleaks --tool-options gitleaks:baseline_path=gitleaks-baseline.json
# Limit file size to scan
lintro check --tools gitleaks --tool-options gitleaks:max_target_megabytes=10
TruffleHog Configuration
TruffleHog is a secrets scanner with 800+ provider-specific detectors and optional live
credential verification. Lintro runs it in filesystem mode. Verification is disabled
by default (--no-verification) so default scans make no outbound network calls;
verification can be re-enabled per run, in which case TruffleHog may contact third-party
providers to test candidate credentials (accept that trade-off before enabling it).
TruffleHog is configured via CLI options (there is no default config file).
Install: brew install trufflehog or
GitHub Releases
Available Options:
| Option | Type | Description |
|---|---|---|
no_verification | boolean | Disable live credential verification (default: true) |
results | string | Filter result type — single value (see note below) |
config | string | Path to a custom detector configuration file |
exclude_paths | string | Path to a file of newline-separated exclude regexes |
concurrency | integer | Number of concurrent workers |
Note:
--tool-optionsuses commas to separate options, so a comma-separatedresultsvalue (e.g.verified,unverified) cannot be passed on the CLI. Use a single value such asresults=unverified, or leave it unset to report every result type.
Usage Examples:
# Basic scan (verification disabled by default)
lintro check --tools trufflehog
# Raise worker concurrency
lintro check --tools trufflehog --tool-options trufflehog:concurrency=8
# Only report unverified results (single value; commas are not CLI-safe here)
lintro check --tools trufflehog --tool-options trufflehog:results=unverified
# Explicitly enable live verification (makes network calls — off by default)
lintro check --tools trufflehog --tool-options trufflehog:no_verification=False
OSV-Scanner Configuration
OSV-Scanner is Google’s vulnerability scanner using the Open-Source Vulnerabilities (OSV) database. It scans lockfiles for known vulnerabilities across multiple ecosystems including PyPI, npm, Go, Rust, Ruby, PHP, .NET, and Java.
Install: go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latest or
download from GitHub Releases.
File: .osv-scanner.toml
# Ignore specific vulnerabilities (ignoreUntil is required for lintro classification)
[[IgnoredVulns]]
id = "GHSA-xxxx-xxxx-xxxx"
ignoreUntil = 2026-12-31
reason = "Not applicable to this project"
# Override package scanning behavior
[[PackageOverrides]]
name = "example-package"
ecosystem = "PyPI"
ignore = true
reason = "False positive"
Available Options:
| Option | Type | Description |
|---|---|---|
timeout | integer | Scan timeout in seconds (default: 120) |
check_suppressions | boolean | Run probe scan to detect stale suppressions (default: true) |
When check_suppressions is enabled, lintro runs a second osv-scanner scan without
suppressions to classify each .osv-scanner.toml entry as Active (vulnerability
still present), Stale (vulnerability resolved upstream — safe to remove), or
Expired (past the ignoreUntil date). Results appear in the summary table Notes
column and in JSON output under metadata.suppressions.
Usage Examples:
# Scan for vulnerabilities in lockfiles
lintro check --tools osv_scanner
# With custom timeout for slow networks
lintro check --tools osv_scanner --tool-options "osv_scanner:timeout=300"
# Skip suppression staleness check
lintro check --tools osv_scanner --tool-options "osv_scanner:check_suppressions=false"
# Ignore vendored or nested checkouts (equivalent to a .lintro-ignore entry)
lintro check --tools osv_scanner --exclude ".claude"
Excluding lockfiles: osv-scanner performs its own recursive lockfile discovery, so
lintro applies the resolved exclusion set — .lintro-ignore entries plus --exclude
patterns — to the lockfile paths it reports. Both mechanisms use the same gitignore
semantics as file discovery and yield identical results. Directory patterns (.claude,
vendor/) drop every finding from that tree — useful when nested checkouts multiply the
same finding — and file patterns (bun.lock, *.lock) suppress individual lockfiles.
pip-audit Configuration
pip-audit is the Python Packaging Authority (PyPA) scanner for Python dependencies with
known vulnerabilities. It queries the PyPI Advisory Database and OSV, complementing
bandit (which scans source code) by scanning the dependency surface. It audits
requirements*.txt files (via -r) and Python projects declared in pyproject.toml /
setup.py.
Install: pip install pip-audit, uv add pip-audit, or brew install pip-audit.
File: none. pip-audit has no native config file; suppressions are passed on the command line by upstream and are not exposed via lintro.
Available Options:
| Option | Type | Description |
|---|---|---|
timeout | integer | Scan timeout in seconds (default: 120) |
Notes:
- pip-audit’s JSON output carries no severity field, so lintro reports severity as
UNKNOWN. - Advisory IDs (PYSEC/GHSA/CVE) link to the corresponding osv.dev page.
- Requirements discovery is recursive: nested files such as
requirements/base.txtorservices/api/requirements.txtare picked up, while vendored/generated trees (node_modules,.venv,venv,vendor,.git,__pycache__) are skipped. To audit a file outside this scope, pass it explicitly on the command line.
Usage Examples:
# Scan requirements and project manifests for vulnerable dependencies
lintro check --tools pip_audit
# With a longer timeout for slow networks
lintro check --tools pip_audit --tool-options "pip_audit:timeout=300"
pydoclint Configuration
File: pyproject.toml
[tool.pydoclint]
style = "google"
arg-type-hints-in-docstring = false
arg-type-hints-in-signature = true
check-return-types = false
check-arg-order = true
skip-checking-short-docstrings = true
Available Options:
style:google,numpy,sphinxarg-type-hints-in-docstring: Require types in docstring (default: true)arg-type-hints-in-signature: Require type annotations (default: true)check-return-types: Validate return types match (default: true)check-arg-order: Verify argument order matches signatureskip-checking-short-docstrings: Skip single-line docstrings
Frontend Tools
Prettier Configuration
Prettier handles formatting for CSS, HTML, JSON, YAML, Markdown, and GraphQL files.
Note: JavaScript and TypeScript files are handled by oxfmt for better performance (30x faster). See Oxfmt Configuration for JS/TS formatting options.
File: .prettierrc
{
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"quoteProps": "as-needed",
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "avoid",
"printWidth": 80,
"endOfLine": "lf"
}
File: prettier.config.js
module.exports = {
tabWidth: 2,
semi: true,
singleQuote: true,
trailingComma: 'es5',
bracketSpacing: true,
arrowParens: 'avoid',
printWidth: 80,
// Override for specific file types
overrides: [
{
files: '*.json',
options: {
tabWidth: 4,
},
},
{
files: '*.md',
options: {
printWidth: 120,
proseWrap: 'always',
},
},
],
};
File: package.json
{
"prettier": {
"tabWidth": 2,
"semi": true,
"singleQuote": true
}
}
Ignore Files: .prettierignore
node_modules/
dist/
build/
coverage/
*.min.js
*.min.css
TypeScript Tools
TypeScript Compiler (tsc) Configuration
The TypeScript Compiler provides static type checking for TypeScript projects. Lintro
wraps tsc --noEmit to check types without generating output files.
Installation:
# Homebrew (macOS/Linux)
brew install typescript
# npm
npm install -D typescript
# bun
bun add -D typescript
A project devDependency is preferred: a type checker must match the project’s own
TypeScript version, because a different compiler reports different diagnostics. A global
or Homebrew tsc is used when the project has no local install. See
Node.js Tool Resolution.
File: tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"strict": true,
"skipLibCheck": true,
"noEmit": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Available Options via --tool-options:
| Option | Type | Description |
|---|---|---|
project | string | Path to tsconfig.json (—project) |
strict | boolean | Enable all strict type checking options |
skip_lib_check | boolean | Skip type checking of declaration files (default: true) |
timeout | integer | Execution timeout in seconds (default: 60) |
Usage Examples:
# Basic type check (uses tsconfig.json if present)
lintro check src/ --tools tsc
# Enable strict mode
lintro check src/ --tools tsc --tool-options "tsc:strict=True"
# Use specific config file
lintro check src/ --tools tsc --tool-options "tsc:project=tsconfig.build.json"
# Disable library check for faster execution
lintro check src/ --tools tsc --tool-options "tsc:skip_lib_check=True"
# Combine with other JS/TS tools
lintro check src/ --tools tsc,oxlint,oxfmt
Oxlint Configuration
Oxlint is a fast JavaScript/TypeScript linter (50-100x faster than ESLint) with 661+ built-in rules from ESLint, TypeScript, React, JSX-a11y, Unicorn, and more.
Native Config Detection:
Lintro detects these oxlint config files:
.oxlintrc.jsonoxlint.json
When a native config exists, Lintro uses it automatically and skips generating defaults.
Installation:
# bun (recommended)
bun add -D oxlint
# npm
npm install -D oxlint
Lintro prefers the project’s own node_modules/.bin/oxlint, then a binary on PATH,
then a version-pinned bunx/npx fetch, so a global or Homebrew install works too. See
Node.js Tool Resolution.
File: .oxlintrc.json
{
"rules": {
"no-debugger": "error",
"no-console": "warn",
"eqeqeq": "error"
},
"plugins": ["react", "unicorn"],
"ignorePatterns": ["dist/**", "node_modules/**"]
}
Available Options via --tool-options:
| Option | Type | Description |
|---|---|---|
config | string | Path to config file (—config) |
tsconfig | string | Path to tsconfig.json (—tsconfig) |
allow | list[str] | Rules to allow (turn off) |
deny | list[str] | Rules to deny (report as errors) |
warn | list[str] | Rules to warn on (report as warnings) |
quiet | boolean | Suppress warnings, only report errors (—quiet) |
timeout | integer | Execution timeout in seconds (default: 30) |
Usage Examples:
# Basic check
lintro check --tools oxlint
# Auto-fix issues
lintro format --tools oxlint
# Suppress warnings (errors only)
lintro check --tools oxlint --tool-options "oxlint:quiet=True"
# Deny specific rules (report as errors)
lintro check --tools oxlint --tool-options "oxlint:deny=no-debugger|no-console"
# Allow specific rules (ignore them)
lintro check --tools oxlint --tool-options "oxlint:allow=no-unused-vars"
# Use custom config file
lintro check --tools oxlint --tool-options "oxlint:config=.oxlintrc.custom.json"
# Specify tsconfig for TypeScript projects
lintro check --tools oxlint --tool-options "oxlint:tsconfig=tsconfig.app.json"
Stylelint Configuration
Stylelint is a mighty, configurable linter and fixer for CSS, SCSS, Sass, and Less
stylesheets, with 100+ built-in rules and --fix support.
Stylelint requires a configuration to run. When no config is resolvable, Lintro skips stylelint as a non-error (rather than surfacing stylelint’s hard
ConfigurationError).
Native Config Detection:
Stylelint resolves configuration per file (walking upward). Lintro supports:
.stylelintrc,.stylelintrc.json,.stylelintrc.yaml,.stylelintrc.yml.stylelintrc.js,.stylelintrc.cjs,.stylelintrc.mjsstylelint.config.js,stylelint.config.cjs,stylelint.config.mjs- a
stylelintkey inpackage.json
.stylelintignore is honored by the underlying tool.
Installation:
# bun (recommended, with a shareable config)
bun add -D stylelint stylelint-config-standard
# npm
npm install -D stylelint stylelint-config-standard
Lintro prefers the project’s own node_modules/.bin/stylelint, then a binary on PATH,
then a version-pinned bunx/npx fetch, so a global install works too. See
Node.js Tool Resolution.
File: .stylelintrc.json
{
"extends": ["stylelint-config-standard"],
"rules": {
"color-hex-length": "short",
"block-no-empty": true,
"declaration-block-no-duplicate-properties": true
}
}
Available Options via --tool-options:
| Option | Type | Description |
|---|---|---|
config | string | Path to a stylelint config file (--config) |
verbose_fix_output | boolean | Include raw stylelint output in fix() |
timeout | integer | Execution timeout in seconds (default: 30) |
Usage Examples:
# Basic check
lintro check styles/ --tools stylelint
# Auto-fix issues
lintro format styles/ --tools stylelint
# Use a specific config file
lintro check styles/ --tools stylelint --tool-options "stylelint:config=.stylelintrc.json"
# Increase timeout for large stylesheets
lintro check styles/ --tools stylelint --tool-options "stylelint:timeout=60"
Oxfmt Configuration
Oxfmt is a fast JavaScript/TypeScript formatter (30x faster than Prettier) that provides Prettier-compatible formatting with minimal configuration.
Native Config Detection:
Lintro detects these oxfmt config files:
.oxfmtrc.json.oxfmtrc.jsonc(supports comments)
When a native config exists, Lintro uses it automatically and skips generating defaults.
Installation:
# bun (recommended)
bun add -D oxfmt
# npm
npm install -D oxfmt
Lintro prefers the project’s own node_modules/.bin/oxfmt, then a binary on PATH,
then a version-pinned bunx/npx fetch, so a global or Homebrew install works too. See
Node.js Tool Resolution.
File: .oxfmtrc.json or .oxfmtrc.jsonc
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "es5"
}
Available Options via --tool-options:
| Option | Type | Description |
|---|---|---|
config | string | Path to config file (—config) |
ignore_path | string | Path to ignore file (—ignore-path) |
timeout | integer | Execution timeout in seconds (default: 30) |
Note: Formatting options (
printWidth,tabWidth,useTabs,semi,singleQuote) are only supported via config file (.oxfmtrc.json), not CLI flags.
Usage Examples:
# Basic check
lintro check --tools oxfmt
# Format files
lintro format --tools oxfmt
# Use custom config file
lintro format --tools oxfmt --tool-options "oxfmt:config=.oxfmtrc.custom.json"
# Use custom ignore file
lintro format --tools oxfmt --tool-options "oxfmt:ignore_path=.oxfmtignore"
# Increase timeout
lintro format --tools oxfmt --tool-options "oxfmt:timeout=60"
Web Framework Tools
Astro Check Configuration
Astro Check is Astro’s built-in type checking command that provides TypeScript
diagnostics for .astro files including frontmatter scripts, component props, and
template expressions.
Installation:
# bun (recommended)
bun add -D @astrojs/check
# npm
npm install -D @astrojs/check
astro check needs @astrojs/check, and Lintro runs Astro with CI=1 so its
interactive “install @astrojs/check?” prompt cannot complete. Add that package as a
devDependency; astro is already a production dependency in an Astro project, and
installing it with -D would move it out of dependencies. A project-local install of
@astrojs/check is strongly preferred here; see
Node.js Tool Resolution.
Native Config: astro.config.mjs, astro.config.ts, or astro.config.js
Astro check uses your project’s astro.config and tsconfig.json for configuration. No
additional configuration is needed for Lintro.
Available Options via --tool-options:
| Option | Type | Description |
|---|---|---|
root | string | Root directory for the Astro project |
timeout | integer | Execution timeout in seconds (default: 120) |
Usage Examples:
# Check Astro project
lintro check src/ --tools astro-check
# Check with specific root directory
lintro check . --tools astro-check --tool-options "astro-check:root=./packages/web"
# Auto-install dependencies before checking
lintro check src/ --tools astro-check --auto-install
Svelte Check Configuration
Svelte Check is the official type checker and linter for Svelte components. It provides
TypeScript type checking, unused CSS detection, and accessibility hints for .svelte
files.
Installation:
# bun (recommended)
bun add -D svelte-check
# npm
npm install -D svelte-check
Native Config: svelte.config.js or svelte.config.ts
Svelte Check uses your project’s svelte.config and tsconfig.json for configuration.
No additional configuration is needed for Lintro.
Available Options via --tool-options:
| Option | Type | Description |
|---|---|---|
threshold | string | Minimum severity to report: error or warning (default: warning) |
tsconfig | string | Path to tsconfig.json file |
timeout | integer | Execution timeout in seconds (default: 120) |
Usage Examples:
# Check Svelte project
lintro check src/ --tools svelte-check
# Check with warning threshold
lintro check src/ --tools svelte-check --tool-options "svelte-check:threshold=warning"
# Check with specific tsconfig
lintro check . --tools svelte-check --tool-options "svelte-check:tsconfig=./tsconfig.app.json"
# Auto-install dependencies before checking
lintro check src/ --tools svelte-check --auto-install
Vue-tsc Configuration
Vue-tsc is the TypeScript type checker for Vue Single File Components (SFCs). It extends
tsc with Vue-specific type checking capabilities for .vue files.
Installation:
# bun (recommended)
bun add -D vue-tsc
# npm
npm install -D vue-tsc
Native Config: tsconfig.json or tsconfig.app.json
Vue-tsc uses your project’s tsconfig.json for configuration. No additional
configuration is needed for Lintro.
Available Options via --tool-options:
| Option | Type | Description |
|---|---|---|
project | string | Path to tsconfig.json file |
strict | boolean | Enable strict type checking mode |
skip_lib_check | boolean | Skip checking declaration files (default: true) |
use_project_files | boolean | Use tsconfig’s include patterns (default: false) |
timeout | integer | Execution timeout in seconds (default: 120) |
Usage Examples:
# Check Vue project
lintro check src/ --tools vue-tsc
# Check with specific tsconfig
lintro check . --tools vue-tsc --tool-options "vue-tsc:project=tsconfig.app.json"
# Enable strict mode
lintro check src/ --tools vue-tsc --tool-options "vue-tsc:strict=true"
# Auto-install dependencies before checking
lintro check src/ --tools vue-tsc --auto-install
html-validate Configuration
html-validate is an offline HTML validator that checks documents for standards
compliance, best practices, and accessibility (WCAG) issues. It is check-only — the tool
ships no autofixer. By default it inspects *.html, *.htm, *.vue, and *.svelte
files.
Installation:
# bun (recommended)
bun add -D html-validate
# npm
npm install -D html-validate
A project-local devDependency is the supported configuration. It is the first and
most reliable branch of Lintro’s executable resolution, it is lockfile-pinned, and it
needs no registry access at check time. A global install (-g) does not populate
node_modules/.bin, so it lands on a later, weaker branch.
html-validate uses the shared Node.js chain — as of a release after v0.115.0 (#1811) so does every other Node.js tool; see Node.js Tool Resolution.
Executable resolution order:
node_modules/.bin/html-validate, searched upward from the directory being checked until the nearestpackage.jsonor.git.- the
PATH-resolved absolute path ofhtml-validate(e.g. a global install). bunx html-validate@<pinned>— registry fallback, only ifbunxis available.npx --yes --package html-validate@<pinned> html-validate— registry fallback, only ifnpxis available.- bare
html-validate(fails if nothing is installed).
<pinned> is the version Lintro pins in its manifest; @latest is never resolved at
runtime. Branches 3 and 4 emit a one-time warning because they require network access to
the npm registry, and a failure on either path is reported with install guidance rather
than html-validate’s raw error.
Node runtime requirement: the pinned html-validate (currently 11.6.2) declares
engines: { "node": "^22.22.0 || >= 24.8.0" }. Any consumer that installs it, or that
reaches the bunx/npx fallback, needs a Node runtime satisfying that range — Node 20,
21, and 22.0–22.21 are not supported. Size your CI matrix accordingly.
Native Config: .htmlvalidate.json, .htmlvalidate.js, .htmlvalidate.cjs, or
.htmlvalidate.mjs
html-validate reads its rule configuration from the project’s native config file when present. No additional configuration is required for Lintro.
Available Options via --tool-options:
| Option | Type | Description |
|---|---|---|
timeout | integer | Execution timeout in seconds (default: 120) |
Usage Examples:
# Check HTML files
lintro check src/ --tools html-validate
# Check the whole project
lintro check . --tools html-validate
# Auto-install dependencies before checking
lintro check src/ --tools html-validate --auto-install
SQL Tools
SQLFluff Configuration
File: .sqlfluff
[sqlfluff]
dialect = ansi
templater = jinja
exclude_rules = L016,L031
[sqlfluff:indentation]
indent_unit = space
tab_space_size = 4
[sqlfluff:layout:type:comma]
line_position = trailing
[sqlfluff:rules:capitalisation.keywords]
capitalisation_policy = upper
[sqlfluff:rules:capitalisation.identifiers]
extended_capitalisation_policy = lower
File: pyproject.toml
[tool.sqlfluff.core]
dialect = "ansi"
templater = "jinja"
exclude_rules = ["L016", "L031"]
[tool.sqlfluff.indentation]
indent_unit = "space"
tab_space_size = 4
[tool.sqlfluff.rules.capitalisation.keywords]
capitalisation_policy = "upper"
[tool.sqlfluff.rules.capitalisation.identifiers]
extended_capitalisation_policy = "lower"
Available Options:
| Option | Type | Description |
|---|---|---|
dialect | string | SQL dialect (ansi, bigquery, postgres, mysql, etc.) |
exclude_rules | list | List of rules to exclude from checking |
rules | list | List of specific rules to include |
templater | string | Templater to use (raw, jinja, python, placeholder) |
Supported Dialects:
ansi- ANSI SQL standardbigquery- Google BigQueryclickhouse- ClickHousedatabricks- Databricks SQLdb2- IBM Db2exasol- Exasolhive- Apache Hivemysql- MySQLoracle- Oracle Databasepostgres- PostgreSQLredshift- Amazon Redshiftsnowflake- Snowflakesoql- Salesforce SOQLsparksql- Apache Spark SQLsqlite- SQLiteteradata- Teradatatsql- T-SQL (Microsoft SQL Server)
Usage Examples:
# Basic SQL check
lintro check --tools sqlfluff
# Format SQL files
lintro format --tools sqlfluff
# Check with specific dialect
lintro check --tools sqlfluff --tool-options sqlfluff:dialect=postgres
# Exclude specific rules
lintro check --tools sqlfluff --tool-options sqlfluff:exclude_rules=L010,L014
# Use jinja templater
lintro check --tools sqlfluff --tool-options sqlfluff:templater=jinja
YAML Tools
Yamllint Configuration
File: .yamllint
extends: default
rules:
# Line length
line-length:
max: 120
level: warning
# Indentation
indentation:
spaces: 2
indent-sequences: true
check-multi-line-strings: false
# Comments
comments:
min-spaces-from-content: 2
# Document start
document-start:
present: false
# Truthy values
truthy:
allowed-values: ['true', 'false']
check-keys: true
File: pyproject.toml
[tool.yamllint]
extends = "default"
[tool.yamllint.rules.line-length]
max = 120
[tool.yamllint.rules.indentation]
spaces = 2
Markdown Tools
Markdownlint-cli2 Configuration {#markdownlint-cli2-configuration}
Markdownlint-cli2 supports configuration via JSON, JSONC, YAML, or TOML files. Lintro defers to markdownlint-cli2’s native configuration discovery, which searches upward from the file being checked.
File: .markdownlint.json
{
"default": true,
"MD013": {
"line_length": 120
},
"MD041": false
}
File: .markdownlint.yaml
default: true
MD013:
line_length: 120
MD041: false
File: .markdownlint-cli2.jsonc
{
"config": {
"default": true,
"MD013": { "line_length": 120 },
},
}
Available Options:
- Configuration files are discovered automatically by markdownlint-cli2
- Rules can be enabled/disabled via configuration files
- Lintro respects markdownlint-cli2’s native configuration discovery
- Future versions may expose additional options via
[tool.lintro.markdownlint-cli2]inpyproject.toml
Prose / Documentation Tools
Vale Configuration {#vale-configuration}
Vale is a syntax-aware prose linter. Lintro defers to Vale’s native
configuration discovery, which walks upward from each linted file to find a .vale.ini.
Vale requires a configuration to run. When none is resolvable, Lintro skips vale as a non-error (rather than surfacing vale’s
E100runtime error), keeping mixed-language runs clean.
Native config detection: .vale.ini, _vale.ini, vale.ini.
File: .vale.ini
MinAlertLevel = suggestion
[*.md]
BasedOnStyles = Vale
Installation:
brew install vale
# or download from https://github.com/errata-ai/vale/releases
Available --tool-options:
| Option | Type | Description |
|---|---|---|
config | string | Path to a Vale config file (maps to --config) |
min_alert_level | string | Minimum alert level: suggestion, warning, or error |
timeout | int | Per-run timeout in seconds (default 30) |
Usage:
lintro check docs/ --tools vale
lintro check docs/ --tools vale --tool-options vale:min_alert_level=warning
lintro check docs/ --tools vale --tool-options vale:config=.vale.ini
Rust Tools
Clippy Configuration
Clippy is Rust’s official linter and is configured through Cargo.toml or a separate clippy.toml file. Lintro automatically discovers and runs clippy on Rust projects by finding Cargo.toml files.
File: Cargo.toml
[package]
name = "my-rust-project"
version = "0.1.0"
[lints.clippy]
# Enable all lints
pedantic = "warn"
# Or be more restrictive
# pedantic = { level = "warn", priority = -1 }
# Disable specific lints
too_many_arguments = "allow"
type_complexity = "allow"
# Configure lint levels
needless_return = "warn"
unused_variables = "error"
File: clippy.toml (alternative)
# Clippy-specific configuration
too-many-arguments-threshold = 10
type-complexity-threshold = 100
cognitive-complexity-threshold = 15
# Disable specific lints
disallowed-names = []
Available Options:
- pedantic: Enable all lints that are typically only enabled in CI
- nursery: Enable newer, more experimental lints
- restriction: Enable very strict lints that may be overly restrictive for some projects
- cargo: Enable lints that check Cargo.toml files
Lintro usage:
# Check Rust code with Clippy
lintro check --tools clippy
# Auto-fix Clippy issues where possible
lintro format --tools clippy
# Check specific Rust directories
lintro check src/ --tools clippy
golangci-lint Configuration
golangci-lint is the de-facto Go meta-linter, running 100+ sub-linters in parallel.
Lintro targets golangci-lint v2 and automatically discovers Go modules by finding
go.mod files; non-Go projects are skipped. It requires the Go toolchain to be
installed. Linter selection and rule tuning are configured through the project’s native
config file.
Installation:
brew install golangci-lint
# or see https://golangci-lint.run/welcome/install/
File: .golangci.yml (also .golangci.yaml, .golangci.toml, .golangci.json)
version: '2'
linters:
enable:
- errcheck
- staticcheck
- ineffassign
- govet
Available Options:
| Option | Type | Description |
|---|---|---|
timeout | integer | Execution timeout in seconds (120) |
Linter enable/disable and per-linter settings live in the native .golangci.* config
file rather than as lintro --tool-options.
Lintro usage:
# Check Go code with golangci-lint
lintro check --tools golangci_lint
# Auto-fix issues where the underlying linters support it
lintro format --tools golangci_lint
# Increase the timeout for a large module
lintro check --tools golangci_lint --tool-options golangci_lint:timeout=300
Cargo-deny Configuration
Cargo-deny checks Rust dependencies for license compliance, security advisories, banned
crates, and duplicate dependencies. It uses deny.toml for configuration and runs via
cargo deny check.
Installation:
# cargo install
cargo install cargo-deny
# Or via cargo-binstall for faster installation
cargo binstall cargo-deny
File: deny.toml
[advisories]
db-path = "~/.cargo/advisory-db"
db-urls = ["https://github.com/rustsec/advisory-db"]
[licenses]
allow = ["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC"]
[bans]
multiple-versions = "warn"
wildcards = "allow"
[sources]
unknown-registry = "warn"
unknown-git = "warn"
Available Options via --tool-options:
| Option | Type | Description |
|---|---|---|
timeout | integer | Execution timeout in seconds (default: 60) |
Lintro usage:
# Check Rust project with cargo-deny
lintro check --tools cargo_deny
# Check specific Rust directory
lintro check my-crate/ --tools cargo_deny
# Set a longer timeout for large workspaces
lintro check . --tools cargo_deny --tool-options "cargo_deny:timeout=120"
Shell Tools
ShellCheck Configuration
ShellCheck is a static analysis tool for shell scripts. It identifies bugs, syntax issues, and suggests improvements for bash/sh/dash/ksh/zsh scripts. Unlike formatters, ShellCheck focuses on finding potential bugs and problematic patterns.
Installation:
# macOS
brew install shellcheck
# Debian/Ubuntu
apt-get install shellcheck
# Fedora
dnf install ShellCheck
File: .shellcheckrc
# Exclude specific codes
disable=SC2086,SC2046
# Set default shell dialect
shell=bash
# Set minimum severity level
severity=warning
Lintro options via --tool-options:
# Set minimum severity level (error, warning, info, style)
lintro check --tools shellcheck --tool-options "shellcheck:severity=warning"
# Force shell dialect (bash, sh, dash, ksh, zsh)
lintro check --tools shellcheck --tool-options "shellcheck:shell=bash"
# Exclude specific codes
lintro check --tools shellcheck --tool-options "shellcheck:exclude=SC2086|SC2046"
# Follow repo-local sourced files (resolves SC1091 for SCRIPT_DIR sourcing)
lintro check --tools shellcheck \
--tool-options "shellcheck:external_sources=True,shellcheck:source_paths=SCRIPTDIR"
Available Options:
| Option | Type | Description |
|---|---|---|
severity | str | Minimum severity: error, warning, info, style |
exclude | list[str] | List of codes to exclude (e.g., SC2086, SC2046) |
shell | str | Force shell dialect: bash, sh, dash, ksh, zsh |
external_sources | bool | Follow sourced files external to the script (-x). Default False |
source_paths | list[str] | Search paths for sourced files (--source-path=...); supports the SCRIPTDIR token. Setting this implies external_sources (-x is enabled automatically) |
Source-following (SC1091):
Scripts that source repo-local helpers with the runtime-safe pattern below emit SC1091
(“Not following …”) by default, because ShellCheck cannot statically resolve the
dynamic path:
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../lib/common.sh"
Opt in to source-following (off by default, so existing projects are unaffected) by
pointing source_paths at the directories ShellCheck should search. ShellCheck’s
literal SCRIPTDIR token resolves relative to each script’s own directory, which
matches the pattern above without hard-coding repo paths. Because ShellCheck ignores
--source-path unless -x is active, setting source_paths automatically enables
external_sources, so you do not need to set both — configuring paths is enough. (You
may still set external_sources = true on its own to follow sources found on the
default search path.)
pyproject.toml:
[tool.lintro.shellcheck]
# source_paths implies external_sources; -x is added automatically.
source_paths = ["SCRIPTDIR", "scripts/lib"]
With this enabled, the SC1091 above is resolved without per-line
# shellcheck disable=SC1091 or # shellcheck source=... suppressions.
Common ShellCheck Codes:
| Code | Description |
|---|---|
| SC2086 | Double quote to prevent globbing and word splitting |
| SC2046 | Quote this to prevent word splitting |
| SC2002 | Useless use of cat |
| SC2006 | Use $(…) notation instead of backticks |
| SC2034 | Variable appears unused |
| SC2155 | Declare and assign separately |
Inline ignoring:
#!/bin/bash
# shellcheck disable=SC2086
echo $unquoted_variable
# Or use a directive that applies to the whole file at the top:
# shellcheck disable=SC2086,SC2046
Lintro usage:
# Check shell scripts with ShellCheck
lintro check --tools shellcheck
# Check with warning level (ignores info and style)
lintro check --tools shellcheck --tool-options "shellcheck:severity=warning"
# Check specific shell directories
lintro check scripts/ --tools shellcheck
Shfmt Configuration
Shfmt is a shell script formatter that supports POSIX, Bash, mksh, and bats shells. It formats shell scripts to ensure consistent style and can detect formatting issues in diff mode.
Installation:
# macOS
brew install shfmt
# Linux (via Go)
go install mvdan.cc/sh/v3/cmd/shfmt@latest
# Via npm/bun
bun add -g shfmt
File: .editorconfig (shfmt respects EditorConfig)
[*.sh]
indent_style = tab
indent_size = 4
shell_variant = bash
binary_next_line = true
switch_case_indent = true
space_redirects = false
Lintro options via --tool-options:
# Set indentation to 4 spaces (0 for tabs)
lintro check --tools shfmt --tool-options "shfmt:indent=4"
# Enable binary operators at start of line
lintro check --tools shfmt --tool-options "shfmt:binary_next_line=True"
# Indent switch cases
lintro check --tools shfmt --tool-options "shfmt:switch_case_indent=True"
# Add space after redirect operators
lintro check --tools shfmt --tool-options "shfmt:space_redirects=True"
# Set language dialect (bash, posix, mksh, bats)
lintro check --tools shfmt --tool-options "shfmt:language_dialect=bash"
# Enable code simplification
lintro check --tools shfmt --tool-options "shfmt:simplify=True"
Available Options:
| Option | Type | Description |
|---|---|---|
indent | int | Indentation size. 0 for tabs, >0 for spaces |
binary_next_line | bool | Binary ops like && and | may start a line |
switch_case_indent | bool | Indent switch cases |
space_redirects | bool | Redirect operators followed by space |
language_dialect | str | Shell dialect: bash, posix, mksh, bats |
simplify | bool | Simplify code where possible |
Lintro usage:
# Check shell scripts with shfmt
lintro check --tools shfmt
# Auto-format shell scripts
lintro format --tools shfmt
# Check specific shell directories
lintro check scripts/ --tools shfmt
Dotenv Tools
dotenv-linter Configuration
dotenv-linter is a fast, Rust-based linter and fixer
for .env files. It detects duplicate keys, lowercase keys, incorrect delimiters,
unordered keys, and stray whitespace, and can auto-fix most of them.
Installation:
# macOS
brew install dotenv-linter
# Cargo
cargo install dotenv-linter
# Binary releases
# https://github.com/dotenv-linter/dotenv-linter/releases
Native config: dotenv-linter has no config file; behavior is controlled entirely via
CLI flags (surfaced through --tool-options).
Lintro options via --tool-options:
# Recursively scan directories for .env files
lintro check --tools dotenv_linter --tool-options "dotenv_linter:recursive=True"
# Skip specific checks (maps to --ignore-checks)
lintro check --tools dotenv_linter \
--tool-options "dotenv_linter:skip_checks=LowercaseKey|UnorderedKey"
# Exclude paths from linting
lintro check --tools dotenv_linter --tool-options "dotenv_linter:exclude=vendor"
# Validate against a schema file
lintro check --tools dotenv_linter --tool-options "dotenv_linter:schema=env.schema.json"
# Auto-fix issues in place (no .env.bak backups are created)
lintro format --tools dotenv_linter
Available Options:
| Option | Type | Description |
|---|---|---|
recursive | bool | Recursively scan directories for .env files. Default False |
exclude | list[str] | File or directory paths to exclude |
skip_checks | list[str] | Check names to bypass (maps to --ignore-checks) |
schema | str | Path to a schema file to validate .env contents |
Note: real
.envfiles are frequently.gitignored, so they may not be discovered during a normal repository scan. Point Lintro at the file explicitly, or commit a template such as.env.examplefor the linter to check.
TOML Tools
Taplo Configuration
File: taplo.toml or .taplo.toml
# Taplo configuration
[formatting]
align_entries = false
align_comments = true
array_trailing_comma = true
array_auto_expand = true
array_auto_collapse = true
compact_arrays = true
compact_inline_tables = false
column_width = 80
indent_tables = false
indent_entries = false
indent_string = " "
trailing_newline = true
reorder_keys = false
allowed_blank_lines = 2
crlf = false
[[rule]]
# Apply to all TOML files
include = ["**/*.toml"]
keys = ["Cargo.toml"]
[rule.formatting]
reorder_keys = true
Available Options:
| Option | Type | Description |
|---|---|---|
schema | string | Path or URL to JSON schema for validation |
aligned_arrays | boolean | Align array entries vertically |
aligned_entries | boolean | Align table entries (key = value) |
array_trailing_comma | boolean | Add trailing comma in multi-line arrays |
indent_string | string | Indentation string (default: 2 spaces) |
reorder_keys | boolean | Reorder keys alphabetically |
Usage Examples:
# Basic TOML check
lintro check --tools taplo
# Format TOML files
lintro format --tools taplo
# Check with aligned entries
lintro check --tools taplo --tool-options taplo:aligned_entries=true
# Format with specific indent
lintro format --tools taplo --tool-options taplo:indent_string=" "
# Use custom schema for validation
lintro check --tools taplo --tool-options taplo:schema=pyproject.schema.json
Infrastructure Tools
Hadolint Configuration
File: .hadolint.yaml
ignored:
- DL3008 # Pin versions in apt-get install
- DL3009 # Delete apt-get lists
- DL3015 # Avoid additional packages
trustedRegistries:
- docker.io
- gcr.io
allowedRegistries:
- docker.io
- gcr.io
- quay.io
Inline ignoring:
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y \
python3 \
python3-pip
Actionlint Configuration
Actionlint validates GitHub Actions workflows. Lintro discovers workflow files under
/.github/workflows/ when you run lintro check . and invokes the actionlint binary.
- Discovery: YAML files filtered to those in
/.github/workflows/ - Defaults: Lintro does not pass special flags; native actionlint defaults are used
- Local install: use
scripts/utils/install-tools.sh --localto placeactionlinton PATH - Docker/CI: the Docker image installs
actionlintduring build, so CI tests run it
# Validate workflows only
lintro check --tools actionlint
# Validate workflows along with other tools
lintro check --tools ruff,actionlint
Git Tools
Commitlint Configuration
Commitlint validates git commit messages against the
Conventional Commits specification. Unlike
file-based tools, it inspects git state: Lintro runs commitlint --last to validate the
repository’s most recent commit message.
- Requires a config; Lintro skips it as a non-error when none is present.
- Install:
bun add -D @commitlint/cli @commitlint/config-conventionalornpm install -D @commitlint/cli @commitlint/config-conventional. A project devDependency is preferred; a global or Homebrew install is used when there is no local one. See Node.js Tool Resolution. - Cannot auto-fix — amend the commit to satisfy the rules.
File: commitlint.config.js (or .commitlintrc.{js,cjs,json,yaml,yml}, or a
commitlint key in package.json)
// commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] };
Available --tool-options:
| Option | Type | Default | Description |
|---|---|---|---|
timeout | int | 30 | Max seconds to wait for commitlint |
# Validate the latest commit message
lintro check --tools commitlint
# Increase the timeout
lintro check --tools commitlint --tool-options "commitlint:timeout=60"
Project-Specific Configuration
Multi-Language Projects
For projects with multiple languages, organize configuration by component:
project/
├── .lintro.toml # Lintro-specific config
├── pyproject.toml # Python tools
├── .prettierrc # JavaScript/CSS
├── .yamllint # YAML files
├── .hadolint.yaml # Docker files
├── frontend/
│ └── .prettierrc # Frontend-specific overrides
└── backend/
└── pyproject.toml # Backend-specific overrides
Lintro Project Configuration
File: .lintro.toml (future feature)
[lintro]
default_tools = ["ruff", "pydoclint", "prettier", "yamllint"]
table_format = true
group_by = "auto"
exclude_patterns = ["migrations", "node_modules", "dist"]
[lintro.timeouts]
default = 30
pydoclint = 45
prettier = 60
[lintro.paths]
python = ["src/", "tests/"]
javascript = ["frontend/", "assets/"]
yaml = [".github/", "config/"]
docker = ["Dockerfile*", "docker/"]
[lintro.output]
format = "table"
save_to_file = true
file_prefix = "lintro-report"
Output System: Auto-Generated Reports
Lintro now generates all output formats for every run in a timestamped directory under
.lintro/ (e.g., .lintro/run-20240722-153000/).
You do not need to specify output format or file options. Each run produces:
console.log: The full console outputresults.json: Machine-readable resultsreport.md: Human-readable Markdown reportreport.html: Web-viewable HTML reportsummary.csv: Spreadsheet-friendly summary
This ensures you always have every format available for your workflow, CI, or reporting needs.
AI Configuration
Lintro includes optional AI-powered features for actionable summaries and interactive fix suggestions. See the full AI Features Guide for detailed usage.
Quick Setup
# Install AI dependencies (published package)
uv pip install 'lintro[ai]'
# Or from source checkout:
uv sync --extra ai
# Set API key for your configured provider
# Anthropic (default): ANTHROPIC_API_KEY
# OpenAI: OPENAI_API_KEY
# Custom: set ai.api_key_env in config to use any env var name
export ANTHROPIC_API_KEY=sk-ant-...
# .lintro-config.yaml
ai:
enabled: true
lint: true # AI summaries / --fix on chk/fmt
review: false # lintro review (opt-in separately)
provider: anthropic
AI CLI Flags
| Flag | Effect |
|---|---|
| (none) | AI summary only (1 API call) |
--fix | Add interactive AI fix suggestions |
Config Defaults for Flags
Set default_fix to avoid typing the flag every time:
ai:
enabled: true
lint: true
review: true
default_fix: false # only run --fix when explicitly requested
Full AI Config Reference
| Setting | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Master switch; ANDs with lint / review |
lint | bool | false | Enable AI lint summaries on chk/fmt |
review | bool | false | Enable the lintro review AI diff review |
provider | string | anthropic | AI provider (anthropic or openai) |
model | string | (default) | Model override |
api_key_env | string | (default) | Custom env var for API key |
default_fix | bool | false | Always run --fix in check |
auto_apply | bool | false | Apply fixes without confirmation |
auto_apply_safe_fixes | bool | true | Auto-apply safe-style fixes in non-interactive |
max_tokens | int | 4096 | Max tokens per request |
max_fix_attempts | int | 20 | Max issues to attempt fixing per run |
max_parallel_calls | int | 5 | Concurrent AI calls (1-20); honored with a cost cap; n−1 overshoot possible |
max_retries | int | 2 | Max retries for transient errors (0-10) |
max_cost_usd | float | null | Legacy USD cap; prefer profiles. Overlay 0 = uncapped (YAML 0 is $0) |
api_timeout | float | 60.0 | Legacy timeout (s); prefer transports.*.timeout |
transports | object | empty profiles | Per-transport profiles (api / cli) — see AI review transports |
validate_after_group | bool | false | Validate immediately after each accepted group |
show_cost_estimate | bool | true | Show token/cost info in output |
context_lines | int | 15 | Lines of context sent for fix generation (1-100) |
fix_search_radius | int | 5 | Line search radius for fix application (1-50) |
checkpoint_retention | int | 10 | Git checkpoint refs kept (>=0; 0 = current only) |
checkpoint_fmt | bool | false | Git checkpoint before lintro format mutations |
retry_base_delay | float | 1.0 | Initial retry delay in seconds (min 0.1) |
retry_max_delay | float | 30.0 | Maximum retry delay in seconds (min 1.0) |
retry_backoff_factor | float | 2.0 | Retry delay multiplier (min 1.0) |
transcript_logging | bool | false | Opt-in NDJSON logging of AI provider traffic |
transcript_retention | int | 10 | Max transcript files kept under .lintro-cache |
Idiom Review Tool (idiom-review)
The idiom-review tool uses AI to find issues that syntax-matching linters cannot: code
that is syntactically correct but non-idiomatic or redundantly duplicated across files.
Unlike the AI summary and --fix flows, it is a first-class ToolDefinition plugin. It
is classified advisory, so it runs under lintro review — never under
lintro check or lintro format, whose findings must stay deterministic (#1308).
Install:
uv pip install 'lintro[ai]'
export ANTHROPIC_API_KEY=sk-ant-... # or OPENAI_API_KEY for OpenAI
The tool is disabled by default and is a no-op until explicitly opted in. When no AI
provider is available (missing SDK, key, or credits), it degrades gracefully to a
skipped result rather than failing the run. Findings are cached by content hash under
.lintro-cache/idiom, so unchanged files cost nothing on repeat runs.
Options:
| Option | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Opt-in gate — must be true to run |
mode | string | per-file | per-file · duplication · both |
min_confidence | string | medium | Drop findings below this level (low/medium/high) |
max_files | int | 25 | Cap on files reviewed per run (cost bound) |
language | string | python | Language to review; set explicitly for other languages |
Modes:
per-file— flags idiomatic misses per file (e.g. verbose loops instead ofany()/all()comprehensions).duplication— flags the same utility logic reimplemented across files, invisible to per-file linters, with a suggested extraction point.both— runs both modes in one pass.
Usage example:
# .lintro-config.yaml
ai:
enabled: true
provider: anthropic
transport: api
tools:
idiom-review:
options:
enabled: true # opt-in gate (default: false)
mode: per-file # per-file | duplication | both
min_confidence: medium
max_files: 25 # cap files reviewed per run (cost bound)
Or enable ad hoc from the CLI without modifying config:
lintro review --advisory-only --tool-options idiom-review:enabled=true
Advanced Configuration
Tool Conflicts and Priorities
Some tools may conflict with each other. Lintro handles this by:
- Priority system - Higher priority tools run first
- Conflict detection - Warns about conflicting tools
- Auto-resolution - Chooses the best tool for each task
# Check for conflicts
lintro list-tools --show-conflicts
# Force conflicting tools to run
lintro check --tools ruff,black --ignore-conflicts
Performance Optimization
Large Codebases
# Use specific tools for faster checks
lintro check --tools ruff
# Process directories separately
lintro check src/ --tools ruff,pydoclint
lintro check tests/ --tools ruff
# Exclude heavy directories
lintro check --exclude "venv,node_modules,migrations"
CI/CD Optimization
# Fast checks for PR validation
lintro check --tools ruff
# Full analysis for main branch
lintro check --all --output full-report.txt
Custom Output Formats
JSON Output (planned)
lintro check --output-format json --output results.json
{
"summary": {
"total_issues": 15,
"tools_run": ["ruff", "pydoclint"],
"files_checked": 42
},
"issues": [
{
"file": "src/main.py",
"line": 12,
"column": 5,
"tool": "ruff",
"code": "F401",
"message": "'os' imported but unused",
"severity": "error"
}
]
}
Markdown Output (planned)
lintro check --output-format markdown --output QUALITY_REPORT.md
Integration Patterns
Pre-commit Hooks
File: .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: lintro-check
name: Lintro Quality Check
entry: lintro check --output-format grid
language: system
pass_filenames: false
stages: [commit]
- id: lintro-fix
name: Lintro Auto-fix
entry: lintro format --output-format grid
language: system
pass_filenames: false
stages: [commit]
Makefile Integration
.PHONY: lint fix check quality install-tools
# Quality checks
lint:
lintro check
fix:
lintro format
check: lint
@echo "Quality check completed"
# Comprehensive quality report
quality:
lintro check --all --output quality-report.txt
@echo "Full quality report saved to quality-report.txt"
# Tool installation
install-tools:
pip install ruff pydoclint
npm install -D prettier
IDE Integration
VS Code Settings
File: .vscode/settings.json
{
"python.linting.enabled": false,
"python.formatting.provider": "none",
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.organizeImports": false
},
"files.associations": {
".lintro.toml": "toml"
}
}
File: .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "Lintro Check",
"type": "shell",
"command": "lintro",
"args": ["check", "--output-format grid"],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "new"
},
"problemMatcher": []
},
{
"label": "Lintro Fix",
"type": "shell",
"command": "lintro",
"args": ["format", "--output-format grid"],
"group": "build"
}
]
}
Troubleshooting Configuration
Common Issues
1. Tool not respecting configuration:
# Check if config file is found
lintro check --tools ruff --verbose
# Verify config file syntax
ruff check --show-settings
2. Conflicting configurations:
# Check for multiple config files
find . -name "*.toml" -o -name ".ruff*" -o -name "setup.cfg"
# Use specific config
ruff check --config custom-ruff.toml
3. Performance issues:
# Profile tool execution
time lintro check --tools ruff --output-format grid
# Use more specific file patterns
lintro check "src/**/*.py" --tools ruff --output-format grid
Debug Configuration
# Enable verbose output
lintro check --verbose --output-format grid
# Check tool availability
lintro list-tools
# Test individual tools
ruff check src/
pydoclint src/main.py
prettier --check package.json
This comprehensive configuration guide should help you customize Lintro to fit your project’s specific needs and integrate seamlessly into your development workflow!