# 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.

## Configuration Model: 4-Tier System

Lintro uses a clear 4-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                     |

### Key Principles

1. **Native configs are respected by default** - Tools use their own `.prettierrc`,
   `pyproject.toml [tool.ruff]`, etc.
2. **`enforce` settings override via CLI flags** - Line length and target Python are
   injected as CLI arguments to ensure consistency
3. **`defaults` provide fallbacks** - Only used when a tool has no native config file
4. **Simple and transparent** - Users know exactly which config is used

## Lintro Configuration

### Configuration File: `.lintro-config.yaml`

Create a `.lintro-config.yaml` in your project root:

```yaml
# 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

# 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 and config source
tools:
  ruff:
    enabled: true
  prettier:
    enabled: true
    config_source: '.prettierrc' # Optional: explicit native config path
```

### Configuration Report Command

Use `lintro config` to view the current configuration status for all tools:

```bash
# 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

### Command-Line Options

#### Global Options

```bash
# 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
```

#### Tool-Specific Options

```bash
# 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

```bash
# Override default settings
export LINTRO_DEFAULT_TIMEOUT=60
export LINTRO_VERBOSE=1

# Default exclude patterns
export LINTRO_EXCLUDE="*.pyc,venv,node_modules"

# Default output format
export LINTRO_DEFAULT_FORMAT="grid"
```

## 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.

```yaml
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 `defaults` tier 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.

```yaml
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:**

1. Lintro checks if the tool has a native config file (e.g., `.prettierrc`)
2. If NO native config exists, Lintro generates a temp file from `defaults`
3. If native config EXISTS, `defaults` are ignored (native config is used)

### Legacy `pyproject.toml` Support

You can still use `pyproject.toml` under `[tool.lintro]`, but `.lintro-config.yaml` is
preferred. The `global:` section is deprecated in favor of `enforce:`.

```toml
# DEPRECATED - use .lintro-config.yaml instead
[tool.lintro]
line_length = 88
target_python = "py313"
```

### 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.

```toml
[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           |
| yamllint     | 35       | Linter           |
| darglint     | 40       | Linter           |
| bandit       | 45       | Security         |
| hadolint     | 50       | Infrastructure   |
| 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.

```toml
[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_check` stages can be
  disabled or overridden via CLI when desired.
- In `lintro check`, Black runs with `--check` and contributes to failure when
  `enforce_failure` is true. In `lintro 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`.

```bash
# 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]`:

```toml
[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 `COM812` trailing commas and `E501` line
  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

- lintro check
  - Ruff runs lint checks (Ruff `format_check=False`) unless explicitly overridden
  - Black runs `--check` as a post-check to enforce formatting

Overrides when needed:

```bash
# 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.

### Python Tools

#### Ruff Configuration

**File:** `pyproject.toml`

```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`

```ini
[tool:ruff]
line-length = 88
select = E,W,F,I,N,D
exclude = .git,__pycache__,.venv
```

#### Mypy Configuration

- Default run mode: `--strict` with `--ignore-missing-imports` enabled to avoid
  third-party stub noise (applied unless you override via `set_options()` or
  `--tool-options`).
- Config discovery: mypy auto-discovers `pyproject.toml [tool.mypy]`, `mypy.ini`, or
  `setup.cfg [mypy]` and the discovered config is passed via `--config-file`. When a
  native config provides `exclude`, Lintro does **not** add its default
  test/test_samples excludes; otherwise, it applies the defaults plus `.lintro-ignore`.
- Recommended overrides when needed:

```bash
# 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`

```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`

```ini
[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 run
- `skips`: Comma-separated list of test IDs to skip
- `exclude`: Comma-separated list of paths to exclude
- `exclude_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

#### Darglint Configuration

**File:** `.darglint`

```ini
[darglint]
docstring_style = google
strictness = full
ignore_regex = ^_(.*)|^test_(.*)
ignore = DAR101,DAR201
enable = DAR104
```

**File:** `pyproject.toml`

```toml
[tool.darglint]
docstring_style = "google"
strictness = "full"
enable = "DAR104"
ignore = "DAR101,DAR201"
ignore_regex = "^_(.*)"
```

**File:** `setup.cfg`

```ini
[darglint]
docstring_style = google
strictness = full
```

**Available Options:**

- `docstring_style`: `google`, `sphinx`, `numpy`
- `strictness`: `short`, `long`, `full`
- `ignore`: Comma-separated error codes
- `enable`: Enable specific checks
- `ignore_regex`: Regex pattern for ignoring functions

### Frontend Tools

#### Prettier Configuration

**File:** `.prettierrc`

```json
{
  "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`

```javascript
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`

```json
{
  "prettier": {
    "tabWidth": 2,
    "semi": true,
    "singleQuote": true
  }
}
```

**Ignore Files:** `.prettierignore`

```text
node_modules/
dist/
build/
coverage/
*.min.js
*.min.css
```

#### ESLint Configuration

**File:** `.eslintrc.json`

```json
{
  "root": true,
  "env": { "browser": true, "es2022": true, "node": true },
  "parserOptions": { "ecmaVersion": "latest", "sourceType": "module" },
  "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
  "plugins": ["@typescript-eslint"],
  "rules": {
    "no-console": "warn",
    "no-unused-vars": "warn"
  }
}
```

**File:** `eslint.config.js` (flat config)

```javascript
import js from '@eslint/js';
import ts from 'typescript-eslint';

export default [
  js.configs.recommended,
  ...ts.configs.recommended,
  {
    rules: {
      'no-console': 'warn',
      'no-unused-vars': 'warn',
    },
  },
];
```

**Ignore Files:** `.eslintignore`

```text
node_modules/
dist/
build/
coverage/
```

**Lintro options:** use `--tool-options "eslint:timeout=60"` or set `exclude_patterns`
via CLI to adjust discovery; native configs remain primary.

### YAML Tools

#### Yamllint Configuration

**File:** `.yamllint`

```yaml
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`

```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`

```json
{
  "default": true,
  "MD013": {
    "line_length": 120
  },
  "MD041": false
}
```

**File:** `.markdownlint.yaml`

```yaml
default: true
MD013:
  line_length: 120
MD041: false
```

**File:** `.markdownlint-cli2.jsonc`

```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]` in
  `pyproject.toml`

### Infrastructure Tools

#### Hadolint Configuration

**File:** `.hadolint.yaml`

```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:**

```dockerfile
# 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 --local` to place `actionlint` on
  PATH
- Docker/CI: the Docker image installs `actionlint` during build, so CI tests run it

```bash
# Validate workflows only
lintro check --tools actionlint

# Validate workflows along with other tools
lintro check --tools ruff,actionlint
```

## Project-Specific Configuration

### Multi-Language Projects

For projects with multiple languages, organize configuration by component:

```text
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)

```toml
[lintro]
default_tools = ["ruff", "darglint", "prettier", "yamllint"]
table_format = true
group_by = "auto"
exclude_patterns = ["migrations", "node_modules", "dist"]

[lintro.timeouts]
default = 30
darglint = 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 output
- `results.json`: Machine-readable results
- `report.md`: Human-readable Markdown report
- `report.html`: Web-viewable HTML report
- `summary.csv`: Spreadsheet-friendly summary

This ensures you always have every format available for your workflow, CI, or reporting
needs.

## Advanced Configuration

### Tool Conflicts and Priorities

Some tools may conflict with each other. Lintro handles this by:

1. **Priority system** - Higher priority tools run first
2. **Conflict detection** - Warns about conflicting tools
3. **Auto-resolution** - Chooses the best tool for each task

```bash
# Check for conflicts
lintro list-tools --show-conflicts

# Force conflicting tools to run
lintro check --tools ruff,black --ignore-conflicts
```

### Performance Optimization

#### Large Codebases

```bash
# Use specific tools for faster checks
lintro check --tools ruff

# Process directories separately
lintro check src/ --tools ruff,darglint
lintro check tests/ --tools ruff

# Exclude heavy directories
lintro check --exclude "venv,node_modules,migrations"
```

#### CI/CD Optimization

```bash
# 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)

```bash
lintro check --output-format json --output results.json
```

```json
{
  "summary": {
    "total_issues": 15,
    "tools_run": ["ruff", "darglint"],
    "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)

```bash
lintro check --output-format markdown --output QUALITY_REPORT.md
```

## Integration Patterns

### Pre-commit Hooks

**File:** `.pre-commit-config.yaml`

```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

<!-- markdownlint-disable MD010 -->

```makefile
.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 darglint
	npm install -g prettier
```

<!-- markdownlint-enable MD010 -->

### IDE Integration

#### VS Code Settings

**File:** `.vscode/settings.json`

```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`

```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:**

```bash
# Check if config file is found
lintro check --tools ruff --verbose

# Verify config file syntax
ruff check --show-settings
```

**2. Conflicting configurations:**

```bash
# 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:**

```bash
# 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

```bash
# Enable verbose output
lintro check --verbose --output-format grid

# Check tool availability
lintro list-tools

# Test individual tools
ruff check src/
darglint 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!
