---
name: slack-ask-review
description: >
  Ask for a code review on Slack by posting a contextual message with the MR link.
  Use when the user says /slack-ask-review, 'demande une review', 'ask for review on slack',
  or wants to notify the team that a MR is ready for review.
argument-hint: "[optional: additional context or specific reviewer mention]"
allowed-tools:
  - Read
  - Glob
  - Grep
  - Bash
---

Post a review request message on the appropriate Slack channel for the current merge request.

All Slack API calls go through the bundled `pysae-ai-tools slack …` subcommands — no MCP. The commands pick `SLACK_USER_TOKEN` locally (OAuth browser flow) and `SLACK_BOT_TOKEN` in CI automatically.

## Required tools

- `glab`

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.

## Prerequisite — resolve the Slack token

Every `pysae-ai-tools slack …` call below reads `SLACK_USER_TOKEN` locally or `SLACK_BOT_TOKEN` in CI. Export the right one up-front so the commands never fail for missing auth:

```bash
if [ -n "${CI:-}" ]; then
  # CI: non-interactive bot token, fetched from ai-tools/dev/secrets.
  eval "$(pysae-ai-tools env resolve --set SLACK_BOT_TOKEN)"
else
  # Local: user token. First run triggers a Slack OAuth browser flow
  # (falls back to SLACK_BOT_TOKEN if the user token isn't available).
  eval "$(pysae-ai-tools env resolve --set SLACK_USER_TOKEN)" \
    || eval "$(pysae-ai-tools env resolve --set SLACK_BOT_TOKEN)"
fi
```

If neither resolves, stop and tell the user to run `/env-resolve SLACK_USER_TOKEN` (local) or configure the secret in CI.

## Process

1. **Resolve context** — detect MR and project in one shot:
   ```bash
   CTX=$(pysae-ai-tools internal detect-context)
   ```
   Extract: `MR_IID`, `MR_TITLE`, `MR_DESCRIPTION`, `MR_AUTHOR`, `PROJECT_URL`, `MR_SOURCE_BRANCH`, `MR_TARGET_BRANCH`, `TECH_SLACK_CHANNEL`, `TECH_SLACK_CHANNEL_ID`.
   If `MR_IID` is empty, inform the user that no MR exists and stop.

2. **Build context summary** — gather two distinct artefacts:
   - **Why summary** (1-2 lines): the *purpose* of the MR — the user value, the problem solved, the bug fixed. Not what changed (that's the bullets), but why a reviewer should care. Pull from `MR_DESCRIPTION` (look for `## Description`, `## Objectif`, `## Contexte` sections, or the first non-checklist paragraph), the linked issue title, or the MR title's intent. Avoid restating the title verbatim.
   - **What changed** (3-5 bullets): summarise the diff. Use `git diff origin/${MR_TARGET_BRANCH}...HEAD --stat` for an overview, then pick the salient changes from the actual files.
   - **Review environment URL**: check if a review environment is deployed for this MR:
     ```bash
     glab api "projects/:id/merge_requests/${MR_IID}/deployments" 2>/dev/null | jq -r '.[].environment.external_url // empty' | head -1
     ```
     If a review URL is found, save it for inclusion in the message.
   - **MR author Slack ID**: resolve `MR_AUTHOR` (GitLab username) into a Slack user ID so the message tags the person responsible for the MR (the merge owner — the one a reviewer might need to ping back). Build a short list of email candidates from `MR_AUTHOR` (e.g. `<author>@pysae.com`, then `<author>` without the `.ext` suffix) and try each via `slack find-user`:
     ```bash
     AUTHOR_ID=""
     for q in "${MR_AUTHOR}@pysae.com" "${MR_AUTHOR%.ext}@pysae.com" "${MR_AUTHOR}"; do
       AUTHOR_ID=$(pysae-ai-tools slack find-user "$q" 2>/dev/null | jq -r 'select(.found) | .id')
       [ -n "$AUTHOR_ID" ] && break
     done
     ```
     If still empty, fall back to plaintext `(par @<MR_AUTHOR>)` so the message stays informative even without a working tag.

3. **Determine the Slack channel** — use `TECH_SLACK_CHANNEL_ID` from detect_context (resolved in step 1). This is the per-repo tech channel (e.g. `#tech-api`, `#tech-frontend`), with `#tech-global` as fallback.

4. **Check for existing review request** — search the channel history for a message that already links to this MR:
   ```bash
   RESULT=$(pysae-ai-tools slack ask-review \
     --channel "${TECH_SLACK_CHANNEL_ID}" \
     --project-url "${PROJECT_URL}" \
     --mr-iid "${MR_IID}")
   ```
   The script paginates through the last 30 days of messages and searches for the exact MR URL (`project_url/-/merge_requests/iid`), avoiding cross-project false positives.

   Parse the JSON output:
   ```bash
   FOUND=$(echo "$RESULT" | jq -r '.found')
   THREAD_TS=$(echo "$RESULT" | jq -r '.thread_ts // empty')
   ORIGINAL_TEXT=$(echo "$RESULT" | jq -r '.text // empty')
   ```

   - **If `FOUND` is `true`** → thread-reply branch (step 4a).
   - **If `FOUND` is `false`** → compose-and-post branch (steps 5-7).

### 4a. Found: reply in the existing thread

Check whether there are new commits since the original message was posted (compare the message timestamp with `git log --since`):

- **New changes** → post a thread reply summarizing what changed (see [Slack mrkdwn formatting](#slack-mrkdwn-formatting) below — never use Markdown link syntax):
  ```bash
  MSG=$(cat <<EOF
  *MR mise à jour*

  <1-2 lines summarizing what changed>

  Commits :
  - <<project_url>/-/commit/<full_sha>|<short_sha>> <commit message>
  - <<project_url>/-/commit/<full_sha>|<short_sha>> <commit message>
  EOF
  )

  pysae-ai-tools slack post-message \
    --channel "${TECH_SLACK_CHANNEL_ID}" \
    --thread-ts "${THREAD_TS}" \
    --text "$MSG"
  ```

- **No new changes** → post a short bump reply:
  ```bash
  pysae-ai-tools slack post-message \
    --channel "${TECH_SLACK_CHANNEL_ID}" \
    --thread-ts "${THREAD_TS}" \
    --text "*Rappel* : cette MR est toujours en attente de review :pray:"
  ```

If the original message contains a user mention (`<@USER_ID>`), include `cc <@USER_ID>` at the end of the thread reply to re-notify the reviewer. Parse the mention from `$ORIGINAL_TEXT` with a simple grep (`<@U[A-Z0-9]+>`).

### 5. Ping a reviewer (optional)

If the user specifies someone in `$ARGUMENTS`, resolve their Slack user ID via the script:
```bash
USER_JSON=$(pysae-ai-tools slack find-user "<name or email>")
USER_ID=$(echo "$USER_JSON" | jq -r 'select(.found) | .id')
```
If `USER_ID` is non-empty, add `cc <@${USER_ID}>` to the message composed in step 6. If no match, mention the query back to the user so they can refine it.

### 6. Compose the message

Build the message body in a shell variable. The structure is fixed: title line with the MR author tagged, MR URL, a one-paragraph "why" preamble that gives the reviewer context before they click, then the bullet list of what changed.

```bash
# Resolved in step 2: AUTHOR_TAG is either "<@U…>" if found, or "@<MR_AUTHOR>" as fallback.
AUTHOR_TAG="${AUTHOR_ID:+<@${AUTHOR_ID}>}"
AUTHOR_TAG="${AUTHOR_TAG:-@${MR_AUTHOR}}"

MSG=$(cat <<EOF
*Review demandée* par ${AUTHOR_TAG} : <MR title>
<MR web URL>

> <1-2 lines explaining the WHY: user value, problem solved, business context. Not what changed.>

<3-5 bullet points summarizing what changed>

Branche : \`<source_branch>\` → \`<target_branch>\`
EOF
)
```

If a review environment URL was found in step 2, append:
```
:globe_with_meridians: Review env : <review_url>
```

If a *reviewer* (different from the author) was resolved in step 5, append `cc <@${USER_ID}>`. If `$ARGUMENTS` contains additional context, fold it into the bullet list.

The `> …` quote block on the "why" line is intentional: Slack renders it as an indented sidebar, which keeps the preamble visually distinct from the bullets and the reviewer can scan title → why → diff in one pass.

### 7. Post the message

```bash
pysae-ai-tools slack post-message \
  --channel "${TECH_SLACK_CHANNEL_ID}" \
  --text "$MSG"
```

Parse the JSON output to confirm the post succeeded (`ok: true`). On failure, surface the `error` field to the user.

## Slack mrkdwn formatting

All message text posted to Slack **must** be in Slack mrkdwn syntax — never standard Markdown. Slack does not understand `[label](url)`, `**bold**`, or backslash-escaped `\#` / `\!` references; these all render as literal text. Convert before posting:

| Don't (Markdown) | Do (Slack mrkdwn) |
| --- | --- |
| `[label](https://...)` | `<https://...\|label>` |
| Bare URL `https://...` | `<https://...>` (or `<https://...\|label>`) |
| `**bold**` | `*bold*` |
| `*italic*` or `_italic_` | `_italic_` |
| `# Heading`, `## Heading` | `*Heading*` on its own line (no real heading syntax in Slack) |
| `\#2563`, `\!2434` | `#2563`, `!2434` — *or*, when the project URL is known, link them: `<https://gitlab.com/pysae/api/-/issues/2563\|#2563>`, `<https://gitlab.com/pysae/api/-/merge_requests/2434\|!2434>` |
| `@firstname.lastname` | resolve to a real Slack user ID via `pysae-ai-tools slack find-user "<name>"` and use `<@U…>` (see [step 5](#5-ping-a-reviewer-optional)) — otherwise drop the `@` |
| Code fences with language ``` ```python ``` | plain ``` ``` ``` (Slack ignores the language tag) |
| Tables | bullet lists or pre-formatted text inside ``` ``` ``` |

Inline `` `code` ``, bullet lists with `-`, and blockquotes with `>` work the same in both. Never use `*` for bullets — Slack would render them as bold.

When a section of the message comes from MR descriptions, commit messages, or `git log` output, **rewrite it in mrkdwn**, do not paste raw Markdown.

## Rules

- **French only** for the message content
- **Langue française** : respecter les [règles de rédaction en français](../references/french-writing-rules.md) pour tout texte généré
- **Slack mrkdwn only** — apply the conversion table above to every line before posting (links, bold, GitLab refs, mentions)
- Keep the message short and scannable — no walls of text
- Do not include the full diff or file contents in the message
- If `glab` is not available or fails, inform the user and stop
- **Never use MCP Slack tools** — everything goes through the `slack` subcommands so the same flow works locally (user token) and in CI (bot token)

$ARGUMENTS
