---
name: code-quality
description: >
  Run the full test suite, linters, and pre-commit hooks on the current branch, then fix any
  failures with atomic commits. Use when the user says /code-quality, 'run tests and linters',
  'check quality', 'lance les tests et les linters', 'vérifie la qualité', 'fais passer les tests',
  'corrige le lint', 'lance le pre-commit', 'run the test suite', 'fix the failing tests',
  'make the linters pass', 'run lint and tests', 'vérifie que tout passe', or when delegated
  from another skill before MR creation or after review fixes. Distinct from /ci-debug, which
  diagnoses a failing GitLab CI job — this runs the checks locally and fixes them.
argument-hint: "[optional: commit prefix for fixes, e.g. 'fix' or 'feat'] [--exclude-test=unit|it|e2e|all] [--exclude-lint]"
allowed-tools:
  - Read
  - Edit
  - Write
  - Glob
  - Grep
  - Bash
---

Run a full code quality validation pass on the current branch: tests, linters, and pre-commit hooks. Fix any failures with atomic commits.

## Step 1 : Ensure correct working branch

If this skill was invoked **directly by the user** (not delegated from `/code-implement`, `/code-autopilot`, or `/code-review-fix`):

Invoke `/git-worktree` (without arguments) to ensure we are on the correct branch. The worktree skill handles all cases automatically: detecting MR source branches when on main, checking for IDE, asking the user if ambiguous.

If delegated from another skill, skip this step.

## Step 2 : Determine commit prefix

Determine `COMMIT_PREFIX` following the rules in [../references/commit-rules.md](../references/commit-rules.md).

## Step 3 : Run checks

### Parse exclusion flags from `$ARGUMENTS`

Two independent opt-out flags can be passed in `$ARGUMENTS` to narrow the run. They compose freely (use both, either, or neither).

**`--exclude-test=<category>[,<category>…]`** — drop a category from the test step. Accepted values: `unit`, `it`, `e2e`, `all`. Multiple categories can be combined with commas (e.g. `--exclude-test=unit,e2e` runs only `it`). `all` is a shortcut for "skip the test step entirely".

Default mapping when `CLAUDE.md` doesn't specify a per-category command:

- **unit** — non-marked tests (Python: typical pytest default). Excluding means *requiring* at least one of the other markers: `pytest -m "it or e2e"`. Node: drop `yarn test:unit` from the run.
- **it** — Python: tests marked `@pytest.mark.it`. Excluding: `pytest -m "not it"`. Node: drop `yarn test:it`.
- **e2e** — Python: tests marked `@pytest.mark.e2e`. Excluding: `pytest -m "not e2e"`. Node: drop `yarn test:e2e`.

When multiple categories are excluded, AND-combine the pytest markers (e.g. `--exclude-test=it,e2e` → `pytest -m "not it and not e2e"`).

If the project doesn't define one of the excluded categories (e.g. `--exclude-test=e2e` on a repo with no e2e marker / suite), just proceed with the regular full run — there's nothing to exclude.

**`--exclude-lint`** (boolean, no value) — skip both the linters sub-step and the pre-commit hooks sub-step below. Use when iterating fast on a behavioural fix and the lint pass is noise; the next /code-quality call without the flag must pass before MR.

When neither flag is present, run everything: full test suite, linters, pre-commit.

### Discover the project's actual tools

Never assume a specific linter, formatter, or test runner — each project pins its own. Resolve the real commands from the project itself, in this order of authority:

1. **CLAUDE.md** (if it exists) — explicit test and lint commands take precedence over everything else.
2. **The tool manifests** — read whatever the project declares:
   - Python: `pyproject.toml` (`[tool.*]` sections, `[project.optional-dependencies]`, dependency groups), `setup.cfg`, `tox.ini`, `noxfile.py`, `Makefile`.
   - Node: `package.json` `scripts` (`lint`, `format`, `test`, `test:*`) and `devDependencies`.
   - Any language: a `Makefile` / task runner target (`make lint`, `make test`, `just check`).
3. **`.pre-commit-config.yaml`** — the declared hooks ARE the project's linting/formatting gate. Prefer running them (see below) over guessing individual tool invocations.

Only invoke a tool the project actually declares. Do not run a formatter or linter the repo doesn't configure (e.g. do not run `ruff format --check` on a repo formatted by `black`, or `ruff` on a repo with no ruff config) — it produces noise unrelated to the current change.

### Run tests

- If `--exclude-test=all` (or the flag list collapses to all categories the project supports), skip this step entirely.
- If one or more categories were excluded, run the scoped command resolved in the « Parse exclusion flags » sub-step above (e.g. `pytest -m "not it and not e2e"`).
- Otherwise, run the **full test suite** (ALL tests, not only those related to current changes):
  - Use the test command from CLAUDE.md if available
  - Python: `pytest` / Node: `yarn test` / `npm test`
  - **Do NOT filter or select tests based on changed files** — always run the entire suite
  - Exception: if the user explicitly asks to run only impacted/related tests (e.g. "only impacted tests", "tests liés aux changements"), then identify affected test files from `git diff` and run only those

### Run linters

Skip this step entirely when `--exclude-lint` is set.

Run only the linters/formatters the project declares, as resolved in « Discover the project's actual tools » above:

- If CLAUDE.md gives a lint command, use it verbatim.
- Otherwise run the tools the manifests actually configure — e.g. the linter and formatter pinned in `pyproject.toml` `[tool.*]`, or the `lint` / `format` scripts in `package.json`.
- If the project's linting is driven entirely by pre-commit, don't reproduce each hook by hand — let the pre-commit sub-step below be the gate.

Run a formatter in check mode (not write mode) here; fixes happen in Step 4.

### Run pre-commit hooks

Skip this step entirely when `--exclude-lint` is set (pre-commit largely overlaps with the linter pass and shares the same opt-out).

- `pre-commit run --all-files` (if configured)

## Step 4 : Fix failures

If any check fails:

1. Fix the problem in the code
2. Commit the fix:
   ```bash
   git add <specific_files>
   git commit -m "$(cat <<'EOF'
   ${COMMIT_PREFIX}: <short description of the fix>

   Co-Authored-By: Claude <noreply@anthropic.com>
   EOF
   )"
   ```
3. Re-run the failing check to confirm it passes
4. Repeat until all checks are green

**Safety limit**: max 3 fix attempts per check. If still failing after 3 attempts, stop and report the issue to the user.

## Step 5 : Report

Once all checks pass, report the status:
- Tests: PASS (with count if available) — or `SKIPPED (--exclude-test=all)` / `PASS (excluded: it, e2e)` etc. when the run was scoped
- Linters: PASS — or `SKIPPED (--exclude-lint)`
- Pre-commit: PASS (or skipped if not configured / `--exclude-lint`)

$ARGUMENTS
