---
name: stats-weekly-summary
description: >
  Generate a formatted weekly recap of engineering activity across Pysae repos:
  merged MRs, closed issues, production deploys, and epic progress with attached tickets.
  Output is Slack mrkdwn ready to paste.
  Use when the user says /stats-weekly-summary, /weekly-recap, 'recap de la semaine', 'weekly summary',
  'résumé hebdo', 'quoi de neuf cette semaine', 'what shipped this week',
  'recap pour le standup', 'bilan de la semaine', or wants a summary of recent
  engineering activity. Also trigger when the user asks for a team status update,
  sprint review prep, or weekly report.
argument-hint: "[--all | --summary (default)] [number of days (default 7)] [specific repo] [date range] [--channel #channel-name]"
allowed-tools:
  - Bash
  - Read
  - Glob
  - Grep
  - Write
---

Generate a weekly recap of engineering activity across the Pysae GitLab group, formatted for Slack.

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

## Parse arguments

If `$ARGUMENTS` contains:
- `--all` → verbose mode (read `references/verbose-template.md` for the full template)
- `--summary` or nothing → summary mode (default): narrative recap by epic, PM-style
- A number → use as the lookback period in days (default: 7)
- A repo name → restrict to that repo only
- A date range (`YYYY-MM-DD..YYYY-MM-DD`) → use those exact bounds
- `--channel #name` → target Slack channel for posting

Compute `SINCE` date (ISO 8601: `YYYY-MM-DDT00:00:00Z`).

Resolve Slack channel (priority): `--channel` arg → `$WEEKLY_RECAP_SLACK_CHANNEL` env var → ask user (local) or `#tech-product` (CI).

## Collect data

Run the collection command. It fetches merged MRs, closed issues, prod deploys, and epics with activity in a single pass:

```bash
pysae-ai-tools glab weekly-data --since YYYY-MM-DD [--repo REPO_NAME]
```

The command outputs a JSON object with keys: `merged_mrs`, `closed_issues`, `deploys`, `epics`. Each epic includes `closed_issues` (array), `total`, `closed_total`, and `description`.

If the command fails or a specific query times out, fall back to running individual `glab api` calls manually. The module source (`pysae_ai_tools/glab/weekly_data.py`) shows the exact queries.

**Edge cases:**
- **0 MRs returned** — likely a date-window issue, not a quiet week. Check that `$SINCE` is correct before assuming empty data.
- **0 epics with activity** — skip the epic sections entirely. Output only "Hors épics" with a summary of MRs/issues grouped by repo.
- **`--repo` filter + epics** — epics span multiple repos. When a repo filter is active, still show epics that have at least one closed issue in the filtered repo, but only list issues from that repo under the epic.
- **Script partial failure** — if deploys fail but MRs/issues succeed, continue with partial data and add a warning line: _"⚠️ Les données de déploiement n'ont pas pu être récupérées."_

## Enrich and deduplicate

- Cross-reference closed issues with merged MRs (via branch name pattern `*/<IID>-*` or MR closing references)
- Tag issues closed by a `~code-agent` MR (AI-assisted) → :robot_face: badge
- Group MRs and issues by project path, sort chronologically (most recent first)
- For each epic, determine deployment status: all issues closed AND deploys exist → "développement terminé et déployé"

For each epic, fetch issue descriptions and recent comments to enrich the narrative:
```bash
glab api "projects/${PROJECT_ID}/issues/${ISSUE_IID}" | jq '{title, description, labels, weight}'
glab api "projects/${PROJECT_ID}/issues/${ISSUE_IID}/notes?sort=desc&per_page=10" | \
  jq --arg since "$SINCE" '[.[] | select(.created_at >= $since and .system == false)]'
```

Also check for PM comments on the epic itself — they often contain the exact narrative tone to reflect:
```bash
glab api "groups/${GROUP_ID}/epics/${EPIC_IID}/notes?sort=desc&per_page=10" | \
  jq --arg since "$SINCE" '[.[] | select(.created_at >= $since and .system == false)]'
```

Use PM comments as the primary source when available. Complement with issue data when absent.

## Compute epic progress

```
raw_current  = closed_total / total * 100
raw_previous = (closed_total - closed_this_week) / total * 100
current  = round(raw_current / 5) * 5     # 52% → 50%, 67% → 65%
previous = round(raw_previous / 5) * 5
```

Show as delta: "passé de {previous} à {current}%".

## Format: summary mode (default)

Narrative recap organized by epic. Written for #tech-product (audience: CEO, PM, CS, devs).

**Template:**
```
Bonjour à tous ! L'actualité côté produit-tech :

*{epic_title_simplified}* ({progress}%)
{narrative: what was accomplished, current status, next steps. Mention if deployed.}

{repeat for each epic with activity}

*Hors épics*
{Significant MRs/issues not attached to any epic, summarized in 1-2 lines. Skip trivial fixes.}

---
_{TOTAL_MR_COUNT} MRs · {TOTAL_ISSUE_COUNT} tickets · {TOTAL_DEPLOY_COUNT} déploiements · {AI_COUNT} IA :robot_face:_
```

**Writing rules:**

The audience is non-technical. Write for people who care about user impact, not implementation details.

- Strip `[2026-S1]` prefix from epic titles — use the human-readable part only
- Progress as percentage with delta, rounded to nearest 5: "passé de 15 à 50%"
- 100% + deployed → "Le développement est terminé et déployé."
- Progress increased → "Nous avons avancé sur ce sujet, passant de X à Y%."
- Work started this week (prev = 0%) → "Nous avons commencé le travail sur ce chantier."
- **Zero jargon** — never mention: Redis, MongoDB, cache, index, WebSocket, ddtrace, Python, CI, Helm, k6, ArgoCD, endpoint, API (sauf "l'API"), refactoring, component, model, repository, branch, MR, commit, deploy (dire "mis en production" ou "livré")
- **User/business impact** — not the technical solution. "Les temps de chargement sont réduits" not "ajout de cache sur DeviceRepository"
- Each epic: 2-3 sentences max
- French, tutoiement collectif ("nous"), tone factuel mais engageant
- Footer stats line uses short technical labels — that's OK, it's metadata

**Example output:**
```
Bonjour à tous ! L'actualité côté produit-tech :

*Réduire la charge sur nos bases de données* (50%)
Nous avons bien avancé cette semaine, passant de 15 à 50%. Les temps de réponse de la plateforme sont en nette amélioration grâce à l'optimisation des accès aux données. Plusieurs mises en production ont été faites pour accompagner ces changements.

*Mettre en place l'architecture événementielle* (65%)
Correction d'un problème de fiabilité sur le traitement des événements. Le chantier progresse de 65 à 65%, pas de mouvement significatif cette semaine.

*Hors épics*
Améliorations de la stabilité générale de la plateforme. Côté Op, nouveau système de notifications et intégration de l'envoi de SMS via la page d'alertes.

---
_41 MRs · 38 tickets · 8 déploiements · 0 IA :robot_face:_
```

## Format: verbose mode (`--all`)

Read `references/verbose-template.md` for the full template and formatting rules.

## Output

### Mode interactif (local)

1. Print the formatted recap to stdout
2. Save to `weekly-recap-{START_DATE}.md`
3. Save a copy to `${CLAUDE_PLUGIN_DATA}/recaps/weekly-recap-{START_DATE}.md` for history
4. If `--channel` was provided, propose posting there. Otherwise ask: _"Prêt à poster sur Slack ? Si oui, sur quel channel ?"_
5. If confirmed, post via `slack_send_message`

### Mode CI (headless)

When `$CI` is set:
1. Save to `/tmp/weekly-recap-{START_DATE}.md`
2. Post to the resolved channel (see "Parse arguments" for priority order) — no confirmation
3. Always use `--summary` mode

### Previous recap comparison

Before generating, check `${CLAUDE_PLUGIN_DATA}/recaps/` for the most recent previous recap. If found, read it to:
- Compute week-over-week deltas (MR count, issue count, deploy count)
- Note epics that made progress vs stalled vs completed since last week
- Mention trends in the footer: "En hausse par rapport à la semaine dernière (32 MRs → 41 MRs)"

## Rules

- **No empty sections** — omit any section with 0 items
- **Slack mrkdwn** — `*bold*`, `<url|text>` links, `:emoji:`, `•` bullets
- **AI badge** — :robot_face: on any MR or issue with the `code-agent` label
- **Weight** — show points only when issues have weight, skip `0pt`
- **Respect rate limits** — the collect script handles this with `sleep 0.2`
- **Timeout gracefully** — if any API call fails, log warning and continue with partial data
- **French** — all section headers and summary 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é
- **Summary default** — `--summary` is default, `--all` must be explicitly requested

## Gotchas

- **GitLab API rate limits** — the collect script paginates automatically (up to 500 results per query). If the week has >500 MRs, raise `max_pages` in the script. The group ID is resolved from: `--group-id` arg → `$PYSAE_GROUP_ID` env var → fallback `918966`.
- **Prod environment naming** — Pysae uses `<app>-prod` (e.g. `api-prod`), NOT `production`. The script filters with `grep -E '\-prod$'`. If a new app uses a different naming convention, it won't be detected.
- **Epic progress rounding** — rounding to nearest 5% can hide small movements. If 1 issue out of 50 is closed, that's 2% → rounds to 0%. The delta will show "0 → 0%" even though work was done. Mention the closed issue explicitly in this case.
- **MR merged_at vs updated_after** — the API filters by `updated_after` not `merged_after`. An old MR updated (commented on) this week will appear in results even if it was merged months ago. The script's `jq` filter on `merged_at >= $SINCE` handles this, but if the script is bypassed, remember to filter.
- **Slack mrkdwn ≠ markdown** — `*bold*` not `**bold**`, no headers (`#`), no code blocks with language hints. Links are `<url|text>` not `[text](url)`.
- **`glab` auth scope** — the bot token needs `read_api` on the group. If deploys return empty, check that the token has access to the project's environments.

$ARGUMENTS
