---
name: glab-mr
description: >
  Create a GitLab merge request from the current branch: run the quality gate, resolve the target
  branch, draft a Conventional-Commits title and French description from the template, inherit the
  ticket's labels, link the source issue, set the assignee, then open it. Use when the user says
  /glab-mr, /mr, 'crée une MR', 'ouvre une merge request', 'fais une MR', 'soumets pour review',
  'ouvre la MR', 'PR cette branche', 'crée la merge request', 'create a MR', 'open a merge request',
  'submit this for review', 'raise a PR', or wants to turn the current branch into a merge request.
  Distinct from /glab-mr-find (which locates an existing MR), /glab-mr-merge (which merges one), and
  /code-implement (which writes the code first, then opens the MR at the end).
argument-hint: "[optional: source issue IID or description] [--skip-quality to bypass pre-MR checks]"
allowed-tools:
  - Read
  - Glob
  - Grep
  - Bash
  - AskUserQuestion
---

Create a GitLab merge request from the current branch using the `glab` CLI.

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

## Mode detection

Detect the execution context using `detect_context`:
```bash
CTX=$(pysae-ai-tools internal detect-context)
IS_CI=$(echo "$CTX" | jq -r '.is_ci')
PROJECT_ID=$(echo "$CTX" | jq -r '.project_id // empty')
MR_IID=$(echo "$CTX" | jq -r '.mr_iid // empty')
MR_SOURCE_BRANCH=$(echo "$CTX" | jq -r '.mr_source_branch // empty')
```

In CI mode (`IS_CI` is `true`):
- **Never use `AskUserQuestion`** — all decisions must be resolved automatically
- Skip confirmation step (step 9) — create the MR directly
- If no source issue is found, continue without ticket linkage (do not prompt)
- If type cannot be detected, infer from branch prefix or default to `feat`

## Workflow

1. **Check for existing MR** — before doing anything, check if an MR already exists for the current branch:
   ```bash
   glab mr list --source-branch "$(git branch --show-current)" --output json 2>/dev/null \
     | jq -r '.[] | "\(.state)\t\(.web_url)\t\(.title)"'
   ```
   - If an open MR already exists, display its URL and title and **stop** — do not create a duplicate. Ask the user if they want to update the existing MR instead.
   - If only closed/merged MRs exist, proceed normally (creating a new one is fine).
2. **Quality gate** — unless `$ARGUMENTS` contains `--skip-quality`, run tests and linters to verify the branch is clean before creating the MR:
   1. Read `CLAUDE.md` (if it exists) to find project-specific test and lint commands
   2. Run the test suite:
      - Use the test command from CLAUDE.md if available
      - Python: `pytest` / Node: `yarn test` / `npm test`
   3. Run linters:
      - Use the lint command from CLAUDE.md if available
      - Python: `ruff check` / Node: `yarn lint` / `npm run lint`
   4. Run pre-commit hooks (if configured): `pre-commit run --all-files`
   - **If ALL checks pass** → continue to step 3
   - **If ANY check fails** → display the failures clearly, then suggest:
     > ❌ Tests ou linters en échec. Lance `/code-quality` pour corriger automatiquement, ou corrige manuellement avant de relancer `/glab-mr`.
     Then **stop** — do NOT create the MR
   - If `--skip-quality` is present, skip this step entirely and warn:
     > ⚠️ Quality gate skippé — les tests/linters n'ont pas été vérifiés.
3. **Verify ticket linkage** — find the source issue IID using these signals (in order):
   - `$ARGUMENTS` (user passed an IID directly)
   - Branch name pattern: `feat/XXX-*`, `fix/XXX-*`, `ai-XXX`
   - Commit messages: `#XXX`, `Closes #XXX`, `Fixes #XXX`
   - If no issue found:
     - **Local mode**: ask the user:
       > Aucun ticket lié détecté. Indique le numéro de l'issue GitLab (`#XXX`) ou tape "skip" pour continuer sans ticket.
     - **CI mode**: continue without ticket linkage (log a warning)
   - If the user provides an IID, use it. If the user says "skip" (local mode), continue without a ticket but warn:
     > ⚠️ MR créée sans ticket. Pense à la rattacher manuellement.
4. **Check changelog** — look for an existing entry in `changelogs/` directory. If none exists, invoke the `/code-changelog` skill, which decides whether an entry is actually needed: it creates one for net-new work, or — when this MR only corrects/adjusts a change whose entry is still pending (not yet released) — either reuses the pending entry (no new file) or updates it. Proceed once `/code-changelog` returns.
5. **Determine target branch** — resolve `MR_TARGET_BRANCH` using the first matching rule:
   1. If `$ARGUMENTS` contains `--target-branch <branch>`, use that value.
   2. If the user references a parent MR (by IID or URL), fetch it and:
      - If the MR is **open** (not merged): use its **source branch** as `MR_TARGET_BRANCH` (stacking MRs).
      - If the MR is **merged**: use its **target branch** as `MR_TARGET_BRANCH` (the changes are already in that branch).
   3. Otherwise, detect the repository's default branch:
      ```bash
      git remote show origin | sed -n 's/.*HEAD branch: //p'
      ```
   Store the result as `MR_TARGET_BRANCH`. Use it for all subsequent diff commands and for the `--target-branch` flag when creating the MR.
6. **Detect context** — read git log, diff against `MR_TARGET_BRANCH`, and identify the type of change
7. **Detect issue type and inherit labels** — if a source issue is provided or referenced in commits, fetch its labels. Use them to determine the type (`bug`, `feature`, `technical`) **and** to build the MR's label set: the MR gets the **same labels as the ticket, minus the board/workflow column label** (board columns belong to the issue, not the MR). Strip `workflow::Refinement`, `workflow::Ready`, and any `workflow::*` label; keep everything else (domain, `type::*`, `priority::*`, `review::release`, `version::*`, …):
   ```bash
   ISSUE_LABELS=$(glab api "projects/${PROJECT_ID}/issues/${IID}" | jq -r '.labels[]')
   MR_LABELS=$(printf '%s\n' "$ISSUE_LABELS" \
     | grep -vE '^(Refinement|Ready|workflow::)' \
     | paste -sd, -)
   ```
   Pass `MR_LABELS` to `glab mr create` via `--label` (see [Output format](#output-format)). When there is no source issue, skip this — no inherited labels.
8. **Build description** — use the matching MR template loaded from GitLab (see [MR templates](#mr-templates-dynamic) below). **Link the source issue with `Closes #<IID>`** when one exists. Auto-close is disabled on all Pysae projects (`autoclose_referenced_issues=false`), so the merge does **not** actually close the issue — `Closes` just records the precise MR↔issue link (it populates GitLab's native `closed_by`, which the board tooling relies on); the board workflow owns the status (merge → `workflow::To deploy`, closed later only when shipped)
9. **Ask for confirmation** (local mode only) — present title, description summary, labels, assignee, and target branch (`MR_TARGET_BRANCH`); ask user to confirm or adjust. In CI mode, skip this step and create the MR directly
10. **Create MR via glab** — create the MR directly (see [Output format](#output-format) below)
11. **Link to source issue** — the `Closes #<IID>` in the MR description populates GitLab's `closed_by` (the precise MR↔issue link); with `autoclose_referenced_issues=false` the merge does not close the issue. **Do NOT transition the workflow label** — the issue stays in `workflow::In progress` until `/code-review` is launched, which is the only skill that flips it to `workflow::Under review`.

## Type detection

Determine the type from the source issue labels, commit prefixes, or branch name — see [../references/issue-type-mapping.md](../references/issue-type-mapping.md) for the full mapping table. Use `BRANCH_PREFIX` as `MR_PREFIX`. If the type cannot be determined, ask the user.

## Critical rules

- **Changelog entry — required by default, with one exception**: a changelog entry must exist in `changelogs/` and be properly formatted (single line: `* <type>: <short description> (#<IID>)`) before creating the MR. If missing or malformed, invoke the `/code-changelog` skill to create or fix it first. **Exception**: when the work only corrects or adjusts a change whose changelog entry is *still pending* (a `changelogs/*.md` file not yet shipped in a release), no new entry is needed — either open the MR **without** a changelog (the pending entry already describes the final behaviour) or **update that pending entry** if the behaviour it describes changed. See `/code-changelog` → "Decide first — a new entry is not always needed".
- **Auto-assign**: resolve the current username via `glab api user | jq -r '.username'` and use `--assignee <username>`. Do NOT use `@me` — glab does not resolve it. If the user specifies a different assignee, use their username instead
- **Title = a valid Conventional Commits header** — `<type>[(<scope>)]: <subject>`, English, imperative mood, optional scope (`fix: prevent null pointer in trip sync`, `feat(ndp): add trip export endpoint`, `perf(api): optimize query`). This is **mandatory, not stylistic**: MRs are squash-merged and GitLab uses the **MR title as the squash commit message**, which is validated by **commitlint** on the target branch — a non-conforming title blocks the merge. The `type` must be in the commitlint type-enum (`build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `style`, `test`, `tech`, `release` — never `bump`); `MR_PREFIX` already resolves to one. See [../references/commit-rules.md § MR title](../references/commit-rules.md).
- **Description body in French**
- **Langue française** : respecter les [règles de rédaction en français](../references/french-writing-rules.md) pour tout texte généré
- **Emojis**: preserve exactly as they appear in the loaded template — do not add or remove any
- **No nested backticks** — they break GitLab markdown
- **Link the issue with `Closes #<IID>`** (one closing keyword per issue — for several, repeat `Closes #A, Closes #B`, never `Closes #A #B`). Auto-close is disabled project-wide (`autoclose_referenced_issues=false`), so this does not close the issue on merge — it only records the precise native link (`closed_by`) the board tooling uses; the board owns the lifecycle
- **Escape `#N` patterns** — in descriptions, numbered items like `#1`, `#2`, `#3` create unwanted links to GitLab issues. Escape them as `\#1`, `\#2`, `\#3`. Only use bare `#N` when intentionally referencing a real issue (e.g. `Closes #42`)
- **Inherit the ticket's labels** (minus the board/workflow column): the MR carries the same labels as its source issue with `workflow::Refinement`, `workflow::Ready`, and `workflow::*` stripped (see Step 7). This keeps domain/type/priority/`review::release`/`version::*` aligned between issue and MR
- Always include the `/label ~augmented` quick action in the description
- List ALL modified files in the modifications table
- Be honest about test status — never claim PASS if tests were not run
- **Never create draft MRs** — do not use `--draft`, and never prefix the title with `Draft:` or `WIP:`. All MRs must be created as ready for review
- **Never use `--fill` with `--title`/`--description`** — they are mutually exclusive in `glab`. Since we always provide title and description, never use `--fill`
- **Always use `--yes --no-editor < /dev/null`** — `--yes` skips submission confirmation, `--no-editor` prevents editor launch, and `< /dev/null` ensures no hidden prompt can block. Without all three, `glab mr create` can hang indefinitely
- If `glab` is not available, fall back to the GitLab API directly (see [Timeout & fallback](#timeout--fallback) below)

## MR templates (dynamic)

The team's GitLab MR templates are loaded below. **Use the template matching the detected type (`bug`, `feature`, or `technical`) as the skeleton for the description.**

**CRITICAL — preserve template formatting exactly as-is:** keep all emojis, section headers, bullet markers, and structure from the template verbatim. Do NOT strip emojis, rename headers, reorder sections, or reformat. Only fill in the placeholder content within each section. The template is the source of truth for formatting.

!`for tpl in bug feature technical; do echo "=== Template: $tpl ==="; glab api "projects/pysae%2Fissue-templates/repository/files/.gitlab%2Fmerge_request_templates%2F${tpl}.md/raw?ref=main" 2>/dev/null; echo -e "\n"; done 2>/dev/null || echo "Templates unavailable"`

## Self-check before output

- [ ] Quality gate passed (tests + linters + pre-commit) or explicitly skipped with `--skip-quality`
- [ ] Source issue linked with `Closes #<IID>` (populates `closed_by`) — or user explicitly skipped
- [ ] Type detected and correct template variant used
- [ ] All modified files listed
- [ ] Title is English, imperative, not prefixed with `Draft:` or `WIP:`
- [ ] Description body is French
- [ ] `/label ~augmented` included
- [ ] Ticket labels inherited (minus `workflow::Refinement` / `workflow::Ready` / `workflow::*`) when a source issue exists
- [ ] Assignee set

## Output format

**Always write the description to a temp file first**, then pass it to `glab`. This avoids shell expansion issues and hangs with long descriptions or special characters. Use `mktemp` to generate a unique filename — never a fixed path like `/tmp/mr_desc.md`, which would collide if several skill runs execute in parallel on the same machine:

```bash
MR_DESC=$(mktemp -t mr-desc-XXXXXX.md)
trap 'rm -f "$MR_DESC"' EXIT

cat > "$MR_DESC" <<'EOF'
[filled-in MR template for the detected type]
EOF

# Resolve current GitLab username
GL_USER=$(glab api user | jq -r '.username')

pysae-ai-tools glab retry mr create \
  --source-branch "$(git branch --show-current)" \
  --title "${MR_PREFIX}: <short description>" \
  --description "$(cat "$MR_DESC")" \
  --target-branch "${MR_TARGET_BRANCH}" \
  --assignee "${GL_USER}" \
  --label "${MR_LABELS}" \
  --remove-source-branch \
  --no-editor \
  --yes
```

The `Closes #<IID>` link lives in the **description body** (Step 8). Do **not** also pass `--related-issue` — it would inject a second, duplicate `Closes` line. (Auto-close is disabled project-wide, so `Closes` records the link without closing on merge.) Omit `--label "${MR_LABELS}"` when `MR_LABELS` is empty (no source issue, or the issue had only board labels).

## Worktree branch naming

If working inside a worktree, the local branch may be named `worktree-*`. Keep it as-is locally, but push under the conventional name on the remote:
```bash
CURRENT=$(git branch --show-current)
if [[ "$CURRENT" == worktree-* ]]; then
  git push origin "HEAD:${BRANCH}"
  git branch --set-upstream-to="origin/${BRANCH}"
fi
```
Use `${BRANCH}` (the remote name, not `worktree-*`) as the `--source-branch` when creating the MR.

## Timeout & retry

The `pysae-ai-tools glab retry` wrapper handles timeout, retry (3 attempts), process cleanup, and API fallback automatically. Always use it for `glab mr create` and `glab issue create`.

$ARGUMENTS
