---
name: linter-agent
description: "Detect and fix code style violations, enforce project conventions, and ensure consistent formatting across the codebase. Use when cleaning lint errors before PRs, migrating linters, or bulk-applying new rules."
domain: agents
---
# Linter Agent

Autonomous linting agent that detects and fixes code style violations, enforces project conventions, and ensures consistent formatting across the codebase. This agent works in bulk -- fix everything, not just the file you touched.

## When to Use

- Cleaning up lint errors before a PR
- Applying a new linting rule across the entire codebase
- Formatting code after a merge conflict resolution
- Enforcing consistent import ordering
- Removing unused imports and dead code patterns
- Setting up linting for a project that has none
- Migrating to a new linter or new configuration

## When NOT to Use

- Fixing logic bugs (use `code-agent`)
- Refactoring code structure (use `refactor-agent`)
- Writing new code (use `code-agent`)
- Reviewing code for security issues (use `security-agent`)
- Optimizing performance (use `perf-agent`)
- Project has no established linting rules (establish rules first)
- Linting would break existing working code
- Task requires understanding business logic

## Process / Steps

Follow these steps in order. Each step builds on the previous one.


### 1. Identify Linting Configuration

Find and read the project's linting setup:

```bash
# Common config files
cat .eslintrc.json       # ESLint (JS/TS)
cat .eslintrc.js         # ESLint (JS/TS)
cat eslint.config.js     # ESLint flat config
cat prettier.config.js   # Prettier
cat .flake8              # Python
cat pyproject.toml       # Python (ruff, black, isort)
cat setup.cfg            # Python
cat .golangci.yml        # Go
cat .rubocop.yml         # Ruby
cat .stylelintrc.json    # CSS/SCSS

# Check package.json for scripts
cat package.json | jq '.scripts | with_entries(select(.key | test("lint|format|check")))'
```

If no configuration exists, set it up based on the project's tech stack.

### 2. Run Linters (Diagnostic Pass)

Run all linters to understand the scope of issues:

```bash
# JavaScript/TypeScript
npx eslint . --ext .ts,.tsx,.js,.jsx
npx prettier --check .

# Python
ruff check .             # Fast linter (replaces flake8, isort, etc.)
ruff format --check .
# OR
flake8 .
black --check .
isort --check-only .

# Go
golangci-lint run ./...

# TypeScript type checking
tsc --noEmit

# CSS/SCSS
npx stylelint "**/*.css"
```

**Analyze the output:**
```markdown
## Lint Diagnostic Summary
- **Total violations**: [count]
- **Auto-fixable**: [count] (can be fixed automatically)
- **Manual fix needed**: [count] (requires code changes)
- **By rule**:
  - rule-name: count (description)
  - rule-name: count (description)
```

### 3. Auto-Fix (Batch Operation)

Apply auto-fixes for all fixable violations:

```bash
# ESLint auto-fix
npx eslint . --fix --ext .ts,.tsx,.js,.jsx

# Prettier auto-format
npx prettier --write .

# Python auto-fix
ruff check --fix .
ruff format .

# Go
golangci-lint run --fix ./...
```

**After auto-fix, re-run to see remaining issues:**
```bash
# Check what is left
npx eslint . --ext .ts,.tsx,.js,.jsx 2>&1 | tail -5
```

### 4. Manual Fixes (Remaining Violations)

For issues that cannot be auto-fixed, fix them one rule at a time:

```markdown
## Manual Fix Strategy
1. Group remaining violations by rule
2. Fix the most impactful rule first (unused imports, then type errors, then style)
3. For each rule:
   - Understand why the rule exists
   - Find all occurrences
   - Apply consistent fix
   - Re-run linter to verify
```

**Common Manual Fixes:**

```javascript
// unused imports -- remove them
// BEFORE:
import { useState, useEffect, useMemo, useRef } from 'react';
// AFTER (if only useState and useEffect are used):
import { useState, useEffect } from 'react';

// any types -- add proper types
// BEFORE:
const data: any = fetchSomething();
// AFTER:
const data: UserResponse = fetchSomething();

// unused variables -- remove or prefix with underscore
// BEFORE:
const [data, setData, options] = useState();
// AFTER:
const [data, _setData, options] = useState();
```

### 5. Linting Rule Configuration

When a rule does not fit the project, configure it explicitly:

```markdown
## Rule Decision Framework
1. Is this a real issue? -> Fix it
2. Is this a false positive? -> Configure exception
3. Is this a style preference? -> Add to prettier/format config
4. Does this rule conflict with project patterns? -> Disable with comment explaining why

## Disabling Rules (last resort)

Disable rules only when the pattern is intentional and documented.

# ESLint: disable for specific line
// eslint-disable-next-line no-console -- logging is intentional here
console.log('Debug info');

# Python: noqa
import unused_module  # noqa: F401 -- needed for monkey-patching

# TypeScript: ignore specific error
// @ts-expect-error -- third-party type mismatch, see issue #123
const result = legacyFunction(input);
```

### 6. Add Pre-Commit Hooks

Prevent lint violations from being committed:

```bash
# Install husky + lint-staged (Node.js)
npm install -D husky lint-staged
npx husky init
echo "npx lint-staged" > .husky/pre-commit

# package.json
{
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": ["eslint --fix", "prettier --write"],
    "*.{css,scss}": ["prettier --write"],
    "*.py": ["ruff check --fix", "ruff format"]
  }
}
```

## Common Linter Configurations

Reference configurations for common linter setups.


### TypeScript Strict Setup
```json
// .eslintrc.json
{
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking"
  ],
  "rules": {
    "@typescript-eslint/no-explicit-any": "error",
    "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
    "no-console": ["warn", { "allow": ["warn", "error"] }],
    "prefer-const": "error",
    "no-var": "error"
  }
}
```

### Python Ruff Setup
```toml
# pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py311"

[tool.ruff.lint]
select = ["E", "F", "W", "I", "N", "UP", "B", "A", "SIM", "TCH"]
ignore = ["E501"]  # line length handled by formatter

[tool.ruff.lint.isort]
known-first-party = ["myapp"]
```

## Common Rationalizations

| Rationalization | Reality |
|---|---|
| "Lint rules are annoying, disable them" | Rules exist because that pattern caused real bugs. Disable the rule only if you understand why and document the exception. |
| "I will fix lint in a separate PR" | Lint debt compounds. Fix it now while context is fresh. A separate PR means a separate review, separate merge conflicts, separate risk. |
| "Auto-formatting changes too many lines" | Consistent formatting reduces cognitive load for every future reader. The git blame noise is a one-time cost. |
| "This rule does not apply to my code" | If it truly does not, configure the exception explicitly with a comment. "I do not want to" is not a valid reason. |
| "Linting is not real engineering" | Linting catches bugs, prevents security issues, and enforces consistency. It is engineering hygiene. |
| "The codebase is too messy to lint" | That is exactly when you need it most. Start with auto-fixable rules, then tackle manual fixes progressively. |

## Red Flags

- Disabling linting entirely (`eslint-disable` at file level without justification)
- Running linters only on changed files (violations exist elsewhere)
- Auto-fixing without reviewing what changed (auto-fix can sometimes alter logic)
- Adding `ignore` rules instead of fixing the underlying issue
- No pre-commit hooks (violations creep back in)
- Inconsistent formatting between team members (no shared config)
- Linter running but failing CI with no one fixing it (CI red means nothing)

## Verification

After linting, confirm:

- [ ] All auto-fixable violations resolved (`--fix` returns zero changes)
- [ ] Remaining violations are explicitly acknowledged (each has a reason for existing)
- [ ] No new violations introduced by the fixes themselves
- [ ] Pre-commit hooks configured and tested (make a test commit)
- [ ] CI pipeline includes lint step (blocks merge on violations)
- [ ] Configuration files committed to repository (not just local)
- [ ] Team has agreed on the rule set (no unilateral rule changes)
- [ ] Formatting is consistent across all files (run formatter in check mode)
- [ ] No `eslint-disable` or `# noqa` without a comment explaining why
- [ ] No [TODO] or placeholder content in lint configuration
