---
name: code-review
description: >
  Review the current code changes for bugs, security, performance, and quality, then post the
  review as a comment on the GitLab MR (skip posting only with --local). Read-only: it reports
  issues, it does not fix them. Use when the user says /code-review, /review, 'review my code',
  'review my changes', 'check my changes', 'review before push', 'relis mon code',
  'revois mes changements', 'fais une review', 'review ma MR', 'review cette MR',
  'review avant de push', 'jette un œil à mon code', 'is my code ok', 'any bugs in my changes',
  or wants a pre-push code review. Distinct from /code-review-fix, which loops review-and-fix until
  zero issues remain, and from /code-review-pre-release, which audits everything since the last
  release tag — /code-review only reviews and reports the current diff.
argument-hint: "[optional: MR !IID or URL, issue #IID or URL, or specific files/focus area] [--local to skip GitLab posting]"
allowed-tools:
  - Read
  - Glob
  - Grep
  - Bash
  - Skill
hooks:
  - type: PreToolUse
    matcher: Edit
    hook: |
      echo "BLOCKED: code-review is read-only. Use /code-review-fix to apply fixes."
      exit 2
  - type: PreToolUse
    matcher: Write
    hook: |
      echo "BLOCKED: code-review is read-only. Use /code-review-fix to apply fixes."
      exit 2
---

Review the code changes on the current branch compared to the target branch.

## Required tools

- `glab`
- `jq`

Run `pysae-ai-tools tools require <tools>` before starting; if it exits non-zero, abort and report the install command from stderr to the user.

## Project configuration

Honour `.pysae-ai-tools.yaml` (this repo's CLAUDE.md is NOT in your context at runtime). Check each flag with `pysae-ai-tools project flag <path>` (exit 0 = on, 1 = off — no `jq`):

- `ai_notes.enabled` — `pysae-ai-tools project flag ai_notes.enabled` ; when off, skip the ai-notes coherence check.

## Step 1 : Resolve context

If `$ARGUMENTS` contains `--local`, set `LOCAL_ONLY=true` and strip it before further parsing. Local-only mode skips all GitLab interactions (no MR note, no reviewer, no approval, no label update).

1. **Run detect_context** with any refs from `$ARGUMENTS` (handles `!IID`, `#IID`, URLs automatically):
   ```bash
   CTX=$(pysae-ai-tools internal detect-context $ARGUMENTS_REFS)
   ```
   Extract: `IS_CI`, `MR_IID`, `PROJECT_ID`, `PROJECT_URL`, `MR_TARGET_BRANCH`, `MR_SOURCE_BRANCH`, `ISSUE_IID`.

2. **Checkout source branch** if an explicit MR/issue was provided and we're not already on it.

## Step 2 : Analyze the diff

1. **Set workflow label** — unless `LOCAL_ONLY`, if `ISSUE_IID` is set:
   ```bash
   pysae-ai-tools glab workflow-transition ${ISSUE_IID} "workflow::Under review"
   ```
   Skip silently if it fails. Do NOT change the label again after the review.

2. **Sync branch** — `git pull --ff-only` (skip if explicit MR/issue was provided).

3. **Get the diff** — `git diff origin/${MR_TARGET_BRANCH}...HEAD` — use the target branch from `detect_context`, never guess.

4. **Read modified files** for full context (imports, types, surrounding logic). Stay scoped to diff lines only.

5. **Diff convention**: lines with `-` prefix are **already deleted** — never suggest fixing them (only flag if deletion causes a regression). Lines with `+` are new code to review.

6. **Large diffs** (>500 lines): split the review by file to avoid quality degradation.

7. **Apply the review criteria and production impact analysis** below.

If a `CLAUDE.md` file exists at the repo root, apply those conventions too.

## Review criteria

Apply the shared criteria from [../references/review-criteria.md](../references/review-criteria.md) (`Critical` / `Major` / `Minor`).

### Synchronisation doc & tests (vérifier, ne pas faire confiance)

L'étape de code ne mérite pas la confiance sur la doc/tests : on re-vérifie.

- **Structurel (déterministe)** : lancer `pysae-ai-tools docs check`. Tout `DRIFT` (skill absent du README, lien mort) = finding **Major** à corriger avant `APPROVED`.
- **Sémantique (jugement)** : si le diff change un comportement public (API, CLI, skill, setup), vérifier que la doc (README/docs/specs/SKILL.md) et les tests **reflètent** ce nouveau comportement. Un changement public dont la doc ou les tests n'ont pas bougé = finding (Major si la doc devient fausse, Minor si juste incomplète).

### Cohérence des ai-notes (vérifier, ne pas faire confiance)

Si `ai_notes.enabled` est actif (voir *Project configuration* ; sauter cette vérification quand il est désactivé), la branche porte une note d'implémentation dans `docs/ai-notes/`. Elle a été écrite au moment du code initial : les commits de review-fix ou tout changement ultérieur ont pu la rendre périmée. On la re-vérifie contre le code réellement présent dans le diff.

- **Localiser la note** : `docs/ai-notes/*-${ISSUE_IID}-*.md` (fallback : tout `docs/ai-notes/*.md` présent dans le diff, hors `README.md`). Absente alors que le diff porte un changement de comportement non trivial → finding **Minor** (note attendue mais manquante).
- **Cohérence** : les sections *Design decisions*, *Deviations* et *Tradeoffs* décrivent-elles toujours ce que le code fait ? Une décision documentée que le code contredit désormais, ou un choix structurant du diff absent de la note = finding **Major** (note devenue fausse) ou **Minor** (note incomplète).
- **Open questions** : une question 🔴 encore ouverte doit être traitée avant le merge → finding **Major**. Une 🟡 dont l'hypothèse ne tient plus au vu du code = finding **Major**. Les 🟢 sont informatives.
- **Références ticket** : les liens vers l'issue/la MR dans la note doivent rester résolvables.

`code-review` est read-only : les incohérences sont rapportées comme findings, jamais corrigées ici (`/code-review-fix` s'en charge ensuite).

## Step 3 : Format and print the review

Follow [../references/review-issue-format.md](../references/review-issue-format.md) for severity labels, numbering (`\#<N>`, sorted Critical > Major > Minor), file permalinks, and the per-issue block structure.

`/code-review`-specific additions on top of the shared format:

- **`COMMIT_SHA`** = `$(git rev-parse HEAD)` (the current branch HEAD).
- **Empty review** → print exactly `**"No issues found."**`.
- **Final verdict** (mandatory last line): **APPROVED** / **MODIFICATIONS REQUIRED** / **BLOCKED**.
- The numbering allows `/code-review-fix` to reference specific issues — e.g. `/code-review-fix exclu la #3` skips issue \#3, `/code-review-fix #4 uniquement` only fixes \#4.

In local mode, offer to explain findings in more detail if the user asks.

## Step 4 : Post the review on the GitLab MR

**Skip entirely if `LOCAL_ONLY=true`.**

1. **Find existing note**: `pysae-ai-tools glab review-note find ${MR_IID}` → JSON or `NONE`
2. **Compose body** — if previous note exists, apply update rules below
3. **Upsert**: `echo "${BODY}" | pysae-ai-tools glab review-note upsert ${MR_IID}`
4. **Add as reviewer** (skip in CI): `pysae-ai-tools glab review-note add-reviewer ${MR_IID}`
5. **Approve if APPROVED** — **local mode only**: delegate to `/glab-mr-approve`. **Never approve in CI.**

### Note update rules

Previous issues must NEVER disappear from the note:
- **Deduplicate**: issue in both old and new → keep only in new
- **Resolved**: issue in old but not in new → move to collapsed `<details>` with fixing commit SHA from `git log`
- **Already-resolved**: carry forward from previous `<details>` as-is
- **Permalinks**: new issues use current SHA, carried-over issues keep their original SHA

```
<new review content>

<details><summary>Anciennes reviews (problèmes corrigés depuis)</summary>

### <ORIGINAL_SEVERITY> — <original title>
**Fichier**: <original text, unchanged>
**Problème**: <original text, unchanged>
**Correctif**: <original text, unchanged>
**Corrigé**: [<short_sha>](${PROJECT_URL}/-/commit/<full_sha>)

</details>

---
- *Review \#<N> — <DD/MM/YYYY HH:MM> — @<AUTHOR>*
- *Review \#<N-1> — <previous datetime> — @<previous author>*
- *...*
<!-- claude-review-meta:pass=<N> -->
```

- `<N>`: pass number (1 for first, increment on update). Extract from `<!-- claude-review-meta:pass=<N> -->`
- `<AUTHOR>`: if `$CI_JOB_URL` is set, display `[CI](${CI_JOB_URL})`; otherwise use the current GitLab username (`glab api user | jq -r '.username'`) prefixed with `@`.
- Escape `#` as `\#` to prevent GitLab auto-linking
- Date in Europe/Paris: `TZ=Europe/Paris date +"%d/%m/%Y %H:%M"`
- **History**: keep all previous `- *Review \#...*` lines from the existing note, most recent first. The new line is prepended above the old ones.
- Omit `<details>` if first review or previous had zero issues

$ARGUMENTS
