Skip to content
Back to skills

Pr Triage

ASecurity

PR triage: audit open PRs, deep review selected ones, draft and post review comments. Args: "all" to review all, PR numbers to focus (e.g. "42 57"), "en"/"fr" for language, no arg = audit only in French.

  • 94 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added May 26, 2026
developmentrustgobashgitapibackendsecurityperformance

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add FlorianBruniaux/ccboard --skill pr-triage --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Pr Triage?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Pr Triage
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/florianbruniaux-pr-triage/badge)](https://www.skillsdirectory.com/skills/florianbruniaux-pr-triage)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: pr-triage
description: >
  PR triage: audit open PRs, deep review selected ones, draft and post review comments.
  Args: "all" to review all, PR numbers to focus (e.g. "42 57"), "en"/"fr" for language, no arg = audit only in French.
allowed-tools: Bash
effort: medium
tags: [github, prs, triage, review, automation]
---

# PR Triage

## Quand utiliser

| Skill | Usage | Output |
|-------|-------|--------|
| `/pr-triage` | Trier, reviewer, commenter les PRs | Tableau d'action + reviews + commentaires postes |

**Declencheurs** :
- Manuellement : `/pr-triage` ou `/pr-triage all` ou `/pr-triage 42 57`
- Proactivement : quand >5 PRs ouvertes sans review, ou PR stale >14j detectee

---

## Langue

- Verifier l'argument passe au skill
- Si `en` ou `english` -> tableaux et resume en anglais
- Si `fr`, `french`, ou pas d'argument -> francais (defaut)
- Note : les commentaires GitHub (Phase 3) restent TOUJOURS en anglais (audience internationale)

---

Workflow en 3 phases : audit automatique -> deep review opt-in -> commentaires avec validation obligatoire.

## Preconditions

```bash
git rev-parse --is-inside-work-tree
gh auth status
```

Si l'un echoue, stop et expliquer ce qui manque.

---

## Phase 1 -- Audit (toujours executee)

### Data Gathering (commandes en parallele)

```bash
# Identite du repo
gh repo view --json nameWithOwner -q .nameWithOwner

# PRs ouvertes avec metadonnees completes
gh pr list --state open --limit 50 \
  --json number,title,author,createdAt,updatedAt,additions,deletions,changedFiles,isDraft,mergeable,reviewDecision,statusCheckRollup,body

# Collaborateurs (pour distinguer "nos PRs" des externes)
gh api "repos/{owner}/{repo}/collaborators" --jq '.[].login'
```

**Fallback collaborateurs** : si `gh api .../collaborators` echoue (403/404) :
```bash
gh pr list --state merged --limit 10 --json author --jq '.[].author.login' | sort -u
```
Si toujours ambigu, demander a l'utilisateur via `AskUserQuestion`.

Pour chaque PR, recuperer reviews existantes ET fichiers modifies :

```bash
gh api "repos/{owner}/{repo}/pulls/{num}/reviews" \
  --jq '[.[] | .user.login + ":" + .state] | join(", ")'

# Fichiers modifies (necessaire pour overlap detection)
gh pr view {num} --json files --jq '[.files[].path] | join(",")'
```

**Note rate-limiting** : la recuperation des fichiers est N appels API (1 par PR). Pour repos avec 20+ PRs, prioriser les PRs candidates a l'overlap (meme domaine fonctionnel, meme auteur).

**Note** : `author` est un objet `{login: "..."}` -- toujours extraire `.author.login`.

### Analyse

**Classification taille** :
| Label | Additions |
|-------|-----------|
| XS | < 50 |
| S | 50-200 |
| M | 200-500 |
| L | 500-1000 |
| XL | > 1000 |

Format taille : `+{additions}/-{deletions}, {files} files ({label})`

**Detections** :
- **Overlaps** : comparer les listes de fichiers entre PRs -- si >50% de fichiers en commun -> cross-reference
- **Clusters** : auteur avec 3+ PRs ouvertes -> suggerer ordre de review (plus petite en premier)
- **Staleness** : aucune activite depuis >14j -> flag "stale"
- **CI status** : via `statusCheckRollup` -> `clean` / `unstable` / `dirty`
- **Reviews** : approved / changes_requested / aucune

**Liens PR <-> Issues** :
- Scanner le `body` de chaque PR pour `fixes #N`, `closes #N`, `resolves #N` (case-insensitive)
- Si trouve, afficher dans le tableau : `Fixes #42` dans la colonne Action/Status

**Categorisation** :

_Nos PRs_ : auteur dans la liste des collaborateurs

_Externes -- Pretes_ : additions <= 1000 ET files <= 10 ET `mergeable` != `CONFLICTING` ET CI clean/unstable

_Externes -- Problematiques_ : un des criteres suivants :
- additions > 1000 OU files > 10
- OU `mergeable` == `CONFLICTING` (conflit de merge)
- OU CI dirty (statusCheckRollup contient des echecs)
- OU overlap avec une autre PR ouverte (>50% fichiers communs)

### Output -- Tableau de triage

```
## PRs ouvertes ({count})

### Nos PRs
| PR | Titre | Taille | CI | Status |
| -- | ----- | ------ | -- | ------ |

### Externes -- Pretes pour review
| PR | Auteur | Titre | Taille | CI | Reviews | Action |
| -- | ------ | ----- | ------ | -- | ------- | ------ |

### Externes -- Problematiques
| PR | Auteur | Titre | Taille | Probleme | Action recommandee |
| -- | ------ | ----- | ------ | -------- | ------------------ |

### Resume
- Quick wins : {PRs XS/S pretes a merger}
- Risques : {overlaps, tailles XL, CI dirty}
- Clusters : {auteurs avec 3+ PRs}
- Stale : {PRs sans activite >14j}
- Overlaps : {PRs qui touchent les memes fichiers}
```

0 PRs -> afficher `Aucune PR ouverte.` et terminer.

### Copie automatique

Apres affichage du tableau de triage, copier dans le presse-papier :
```bash
pbcopy <<'EOF'
{tableau de triage complet}
EOF
```
Confirmer : `Tableau copie dans le presse-papier.` (FR) / `Triage table copied to clipboard.` (EN)

---

## Phase 2 -- Deep Review (opt-in)

### Selection des PRs

**Si argument passe** :
- `"all"` -> toutes les PRs externes
- Numeros (`"42 57"`) -> uniquement ces PRs
- Pas d'argument -> proposer via `AskUserQuestion`

**Si pas d'argument**, afficher :

```
question: "Quelles PRs voulez-vous reviewer en profondeur ?"
header: "Deep Review"
multiSelect: true
options:
  - label: "Toutes les externes"
    description: "Review {N} PRs externes avec rust-ccboard en parallele"
  - label: "Problematiques uniquement"
    description: "Focus sur les {M} PRs a risque (CI dirty, trop large, overlaps)"
  - label: "Pretes uniquement"
    description: "Review {K} PRs pretes a merger"
  - label: "Passer"
    description: "Terminer ici -- juste l'audit"
```

**Note sur les drafts** :
- Les PRs en draft sont EXCLUES des options "Toutes les externes" et "Pretes uniquement"
- Les PRs en draft sont INCLUSES dans "Problematiques uniquement"
- Pour reviewer un draft : taper son numero explicitement (ex: `42`)

Si "Passer" -> fin du workflow.

### Execution des Reviews

Pour chaque PR selectionnee, lancer un agent `rust-ccboard` via **Task tool en parallele** :

```
subagent_type: rust-ccboard
model: sonnet
prompt: |
  Review PR #{num}: "{title}" by @{author}

  **Metadata**: +{additions}/-{deletions}, {changedFiles} files ({size_label})
  **CI**: {ci_status} | **Reviews**: {existing_reviews} | **Draft**: {isDraft}

  **PR Body**:
  {body}

  **Diff**:
  {gh pr diff {num} output}

  Apply your security-guardian and backend-architect skills for this review.
  Additionally, apply the ccboard Rust review checklist:

  CRITICAL (block merge if violated):
  - anyhow::Result + .context("msg") on every ? -- no bare ?, no .unwrap() in production code
  - parking_lot::RwLock (not std::sync::RwLock) for shared state
  - No blocking in async context (use tokio::time::sleep, tokio::task::spawn_blocking for I/O)
  - Graceful degradation: parsers must return Option<T> and populate LoadReport, never panic on malformed input
  - cargo fmt --all + cargo clippy --all-targets must pass (zero warnings)
  - Tests in #[cfg(test)] mod tests with real fixtures from tests/fixtures/ (not synthetic data)

  IMPORTANT (should fix before merge):
  - thiserror in ccboard-core (library crate), anyhow in ccboard-tui/ccboard-web/ccboard (binaries)
  - No full JSONL file loading at startup -- metadata-only via BufReader line-by-line
  - Arc<DataStore> for shared ownership (NOT DashMap -- removed in v0.4.0, 50x memory reduction)
  - parking_lot::RwLock for low-contention shared state (stats, config) -- not std::sync::RwLock
  - Startup performance target maintained: <2s for 1000+ sessions
  - Read-only constraint: no writes to ~/.claude/* (monitoring only)

  SUGGESTIONS (nice to have):
  - Arc<T> for shared ownership instead of cloning large structs
  - JoinSet for bounded parallel scanning
  - tokio::sync::broadcast for EventBus updates

  Return structured review:
  ### Critical Issues (block merge)
  ### Important Issues (should fix)
  ### Suggestions (nice to have)
  ### What's Good

  Be specific: cite file:line, explain the problem, suggest the fix.
```

Recuperer le diff via :
```bash
gh pr diff {num}
gh pr view {num} --json body,title,author -q '{body: .body, title: .title, author: .author.login}'
```

Agreger tous les rapports. Afficher un resume apres toutes les reviews.

---

## Phase 3 -- Commentaires (validation obligatoire)

### Generation des drafts

Pour chaque PR reviewee, generer un commentaire GitHub en utilisant le template `templates/review-comment.md`.

**Regles** :
- Langue : **anglais** (audience internationale)
- Ton : professionnel, constructif, factuel
- Toujours inclure au moins 1 point positif
- Citer les lignes de code quand pertinent (format `file.rs:42`)

### Affichage et validation

**Afficher TOUS les commentaires draftes** au format :

```
---
### Draft -- PR #{num}: {title}

{commentaire complet}

---
```

Puis demander validation via `AskUserQuestion` :

```
question: "Ces commentaires sont prets. Lesquels voulez-vous poster ?"
header: "Poster"
multiSelect: true
options:
  - label: "Tous ({N} commentaires)"
    description: "Poster sur toutes les PRs reviewees"
  - label: "PR #{x} -- {title_truncated}"
    description: "Poster uniquement sur cette PR"
  - label: "Aucun"
    description: "Annuler -- ne rien poster"
```

(Generer une option par PR + "Tous" + "Aucun")

### Posting

Pour chaque commentaire valide :

```bash
gh pr comment {num} --body-file - <<'REVIEW_EOF'
{commentaire}
REVIEW_EOF
```

Confirmer chaque post : `Commentaire poste sur PR #{num}: {title}`

Si "Aucun" -> `Aucun commentaire poste. Workflow termine.`

---

## Gestion des cas limites

| Situation | Comportement |
|-----------|--------------|
| 0 PRs ouvertes | `Aucune PR ouverte.` + terminer |
| PR en draft | Indiquer dans tableau, skip pour review sauf si selectionnee explicitement |
| CI inconnu | Afficher `?` dans colonne CI |
| Review agent timeout | Afficher erreur partielle, continuer avec les autres |
| `gh pr diff` vide | Skip cette PR, notifier l'utilisateur |
| PR tres large (>5000 additions) | Avertir : "Review partielle, diff tronque" |
| Collaborateurs API 403/404 | Fallback sur auteurs des 10 derniers PRs merges |

---

## Notes

- Toujours deriver owner/repo via `gh repo view`, jamais hardcoder
- Utiliser `gh` CLI (pas `curl` GitHub API) sauf pour la liste des collaborateurs
- `statusCheckRollup` peut etre null -> traiter comme `?`
- `mergeable` peut etre `MERGEABLE`, `CONFLICTING`, ou `UNKNOWN` -> traiter `UNKNOWN` comme `?`
- Ne jamais poster sans validation explicite de l'utilisateur dans le chat
- Les commentaires draftes doivent etre visibles AVANT tout `gh pr comment`

Files in this skill

  • SKILL.md10.4 KB
  • templates/review-comment.md3.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.