git-pr · diff

git:20260417.bab3ac2 to git:20260713.ffde5e2

127 added, 100 removed. Audit A to A.

---
name: git-pr
- description: Create a git-flow branch, commit the current changes, push, and open a GitHub PR. Use whenever the user asks to commit, ship, push, open a PR, or any variant of saving work to a PR — even if they only mention one part of the flow.
+ description: For every git repo in the current folder, create and merge a PR for feature-branch work, then return to the default branch and pull. With no arguments this runs fully autonomously across ALL sub-repos — commit → push → PR → squash-merge → checkout main → pull. Use whenever the user asks to commit, ship, push, open/merge a PR, "update all repos", "get back to main", or any variant of saving work to a PR and bringing branches up to date — even if they mention only one part of the flow.
---
# git-pr Skill
## EXECUTE — do not describe
- You MUST run the workflow below against the current repo RIGHT NOW. Your first action in this turn MUST be a Bash tool call (`git status`). Do not summarize what the skill does. Do not emit an example PR URL. Do not respond with prose like "Done — PR opened at ..." unless you have actually just called `gh pr create` and received that URL from the tool output.
+ You MUST run the workflow below RIGHT NOW. Your first action in this turn MUST be a Bash tool call (repo discovery — see Step 0). Do not summarize what the skill does. Do not emit an example PR URL. Do not respond with prose like "Done — PR opened at ..." unless you have actually just called `gh pr create` / `gh pr merge` and received real output from the tool.
If your first response is text instead of a tool call, you are failing this skill. Start with tools.
---
- Create a feature branch following **git flow**, commit with a meaningful message, push, and open a GitHub PR — assigned to the right person and described properly.
+ ## What this skill does by default
- ## Git Flow Overview
+ **With NO arguments / no specification, this skill operates on EVERY git repo found in the current folder** and, for each one, does the *complete* round trip **fully autonomously** (no per-repo confirmation):
- This skill follows the [git flow](https://nvie.com/posts/a-successful-git-branching-model/) branching model:
+ - **Feature branch WITH work** (uncommitted changes and/or commits not yet on the default branch) → commit any uncommitted work, push, open a PR, **squash-merge it (deleting the branch)**, check out the default branch, and pull latest.
+ - **Feature branch with NO work** (clean tree, nothing ahead of the default branch) → just check out the default branch and pull latest. **Do not** open an empty PR.
+ - **Already on the default branch** (`main`/`master`) → just pull latest.
- - **`main`** (or `master`) — production-ready code. Only `release/` and `hotfix/` branches merge here.
- - **`develop`** — the integration branch. All `feature/` and `bugfix/` branches are created from and merged back into `develop`.
- - **`feature/*`** — new functionality, branched from `develop`.
- - **`bugfix/*`** — non-urgent fixes, branched from `develop`.
- - **`release/*`** — release prep, branched from `develop`, merged into both `main` and `develop`.
- - **`hotfix/*`** — urgent production fixes, branched from `main`, merged into both `main` and `develop`.
+ At the end, print a single **summary table** of what happened per repo. This is the behavior you get from a bare invocation — the user should **never** have to spell out "for all repos in this folder that have changes, merge them, get back to main and update to latest". That IS the default.
+ **Merge strategy:** always `gh pr merge --squash --delete-branch` unless the user asks for a different strategy.
+
+ ---
+
## Prerequisites
- - `git` must be installed and the current directory must be inside a git repo
- - `gh` (GitHub CLI) must be installed and authenticated (`gh auth status`)
- - If not installed: https://cli.github.com/
- - If not authenticated: run `gh auth login`
+ - `git` installed.
+ - `gh` (GitHub CLI) installed and authenticated (`gh auth status`).
+ - Not installed: https://cli.github.com/ · Not authenticated: `gh auth login`
---
## Workflow
- ### Step 1: Understand the changes and detect the git flow base branch
+ ### Step 0: Discover the working set of repos
- Run `git diff` (and `git diff --staged` if anything is staged) plus `git status` to understand what has changed. Do NOT ask the user to explain their changes — figure it out from the diff yourself.
+ The "current folder" is the working directory. It may itself be a repo, OR it may be a **parent folder containing several repos** as immediate subdirectories (the common case — e.g. `~/Projects/Github/onion-ai-eu/` holding `onion-demo-01`, `onion-prod-01`, …).
+ Find every repo to operate on:
+
```bash
- git status
- git diff
- git diff --staged
+ # Immediate-subdirectory repos
+ for d in */; do [ -d "$d/.git" ] && echo "REPO: ${d%/}"; done
+ # ...and the current folder itself, if it is a repo
+ [ -d ".git" ] && echo "REPO: ."
```
- If there are **no changes** at all, tell the user and stop.
+ - If subdirectory repos are found, operate on **each** of them.
+ - If none are found but the current folder is itself a repo, operate on just that one.
+ - If neither, tell the user there are no git repos here and stop.
- **Detect the base branch:**
+ If the user named a specific folder (e.g. "for all repos in `~/Projects/Github/onion-ai-eu`"), run discovery inside that folder instead of the CWD.
- Check which branches exist in the repo to determine the git flow setup:
+ ### Step 1: Classify each repo
+ Do this per repo. Use `git -C <repo>` so you never have to `cd` (which can trigger permission prompts). First sync remote refs so "ahead" counts are accurate:
+
```bash
- git branch -a | grep -E '(develop|main|master)'
- ```
+ git -C <repo> fetch --prune origin
- - If `develop` exists, use it as the default base branch (for `feature/` and `bugfix/` branches).
- - If the user explicitly says this is a **hotfix** (urgent production fix), branch from `main`/`master` instead.
- - If `develop` does not exist, fall back to `main`/`master` and let the user know the repo doesn't appear to follow git flow fully.
+ # Default branch (the branch to return to and merge into)
+ BASE=$(git -C <repo> symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's@^origin/@@')
+ # Fallback if origin/HEAD isn't set:
+ # BASE=main if origin/main exists, else master, else develop
- ### Step 2: Generate branch name and commit message
+ CUR=$(git -C <repo> branch --show-current) # current branch
+ DIRTY=$(git -C <repo> status --porcelain) # non-empty => uncommitted changes
+ AHEAD=$(git -C <repo> rev-list --count "origin/$BASE..HEAD" 2>/dev/null) # commits to ship
+ ```
- From the diff, synthesize:
+ Decide the case:
- **Branch name (git flow conventions):**
- - `feature/<short-kebab-slug>` — new functionality (e.g. `feature/add-login-button`)
- - `bugfix/<short-kebab-slug>` — non-urgent bug fixes targeting `develop`
- - `hotfix/<short-kebab-slug>` — urgent production fixes (branched from `main`/`master`)
- - `release/<version>` — release preparation (e.g. `release/1.2.0`)
- - `chore/<short-kebab-slug>` — non-functional changes (deps, config, docs)
- - Max ~5 words, lowercase, hyphens only
- - Default to `feature/` when in doubt
+ | Condition | Case |
+ |---|---|
+ | `CUR` == `BASE` | **C — on default branch** → pull only |
+ | `CUR` != `BASE`, and (`DIRTY` non-empty **or** `AHEAD` > 0) | **A — feature branch with work** → full PR + merge + return |
+ | `CUR` != `BASE`, clean, `AHEAD` == 0 | **B — feature branch, no work** → return to default + pull |
- **Commit message:**
- - First line: imperative mood, ≤72 chars (e.g. `Add login button to navbar`)
- - Optionally followed by a blank line and a short body (2–4 lines) if the change is complex
- - Be specific — never use vague messages like "update files" or "fix stuff"
+ > `develop`-based git flow: if the repo has a `develop` branch and the user is following git flow, `feature/*` and `bugfix/*` target `develop` and `hotfix/*` target `main`. Detect `develop` and use it as the merge base for those branches, then return to `develop`. Most of the user's repos are `main`-based — default to the repo's actual default branch and don't assume `develop` exists.
- **Show both to the user and ask for confirmation before proceeding.** Keep it brief — just show the proposed branch name and commit message and ask "Look good? I'll proceed unless you want changes."
+ ### Step 2 — Case C: on the default branch
- ### Step 3: Create the branch from the correct base
+ ```bash
+ git -C <repo> pull --ff-only origin "$BASE"
+ ```
+ Record "updated `<base>`" and move on.
- First, ensure you're branching from the right base per git flow:
+ ### Step 3 — Case B: feature branch, nothing to ship
```bash
- # For feature/ and bugfix/ branches — branch from develop
- git checkout develop
- git pull origin develop
- git checkout -b <branch-name>
-
- # For hotfix/ branches — branch from main/master
- git checkout main
- git pull origin main
- git checkout -b <branch-name>
+ git -C <repo> checkout "$BASE"
+ git -C <repo> pull --ff-only origin "$BASE"
```
-
- If the branch already exists, append a short suffix like `-2`.
+ Optionally delete the now-stale local feature branch if it's fully merged (`git -C <repo> branch -d <feature>`; skip if it errors). Record "no changes → returned to `<base>` & pulled".
- ### Step 4: Stage and commit
+ ### Step 4 — Case A: feature branch with work → full flow
- Stage everything that's unstaged (unless the user has explicitly staged a subset — in that case respect their staging):
+ **4a. Commit any uncommitted work.** Synthesize a specific commit message from the diff (imperative, ≤72 chars; never "update files"). Inspect with `git -C <repo> diff` / `git -C <repo> status` first.
```bash
- git add -A # or git add <specific files> if partial staging is intentional
- git commit -m "<commit message>"
+ git -C <repo> add -A
+ git -C <repo> commit -m "<specific message>" # skip if tree was already clean
```
- For multi-line commit messages:
- ```bash
- git commit -m "<subject>" -m "<body>"
- ```
+ If the current branch name is generic (e.g. `feature` or a bare name) it's fine to keep it; do **not** rename an existing branch that already has commits/PR.
- ### Step 5: Push the branch
+ **4b. Push.**
```bash
- git push -u origin <branch-name>
+ git -C <repo> push -u origin "$CUR"
```
- ### Step 6: Create the PR with `gh` (targeting the correct base)
-
- Determine the correct PR base branch per git flow:
- - `feature/*`, `bugfix/*` → base is `develop`
- - `hotfix/*` → base is `main`/`master`
- - `release/*` → base is `main`/`master`
+ **4c. Open the PR** (base = default branch, or `develop`/`main` per the git-flow note). If a PR already exists for this branch, skip creation and reuse it.
```bash
- gh pr create \
- --base <base-branch> \
- --title "<PR title — same as commit subject>" \
- --body "<PR description>" \
+ git -C <repo> ... # (use gh from within the repo)
+ gh -R <owner/repo> pr create \
+ --base "$BASE" \
+ --head "$CUR" \
+ --title "<PR title = commit subject>" \
+ --body "<see template>" \
--assignee "@me"
```
-
- **PR description template** (fill in from the diff):
+ Use `gh pr create` from inside the repo dir if `-R` is awkward — either works. Get `<owner/repo>` from `gh repo view --json nameWithOwner -q .nameWithOwner`.
+ PR body template (fill from the diff; drop a section if not inferable):
```
## What
- <1–2 sentences describing what this PR does>
+ <1–2 sentences>
## Why
- <1–2 sentences on motivation / context, if inferable from the code>
+ <1–2 sentences, only if motivation is clear from the code>
## Changes
- - <bullet: key file or component changed and what was done>
- - <bullet: ...>
+ - <key change>
+ - <key change>
```
- Keep the description concise. If motivation isn't clear from the code, omit the "Why" section rather than guessing.
+ **4d. Squash-merge and delete the branch.**
- **Do not** add reviewers, labels, or milestones unless the user requests them.
+ ```bash
+ gh pr merge "$CUR" --squash --delete-branch
+ ```
+ If the merge is blocked by required status checks, enable auto-merge with `--auto` (it will merge once checks pass) and note that in the summary. If the user has admin rights and wants to bypass protections, `--admin` forces it — only use `--admin` if the user has asked to bypass checks.
+ **4e. Return to the default branch and update.**
+
+ ```bash
+ git -C <repo> checkout "$BASE"
+ git -C <repo> pull --ff-only origin "$BASE"
+ git -C <repo> branch -D "$CUR" 2>/dev/null # local cleanup (remote branch already deleted by --delete-branch)
+ ```
+ Record the real PR URL + "merged → returned to `<base>` & pulled".
+
+ ### Step 5: Print the summary
+
+ After all repos are processed, output one table:
+
+ ```
+ | Repo | Branch | Action | PR |
+ |-----------------|-----------------|------------------------------------------|----|
+ | onion-demo-01 | feature/x | committed, merged, back on main & pulled | <url> |
+ | onion-prod-01 | feature/y | no changes → back on main & pulled | — |
+ | onion-tech-... | main | pulled latest | — |
+ ```
+
+ Only report real PR URLs returned by `gh`. Never a placeholder like `.../pull/42`.
+
---
+ ## Autonomy
+
+ The default run is **fully autonomous** — do not ask for per-repo confirmation of branch names, commit messages, or merges. Just do the flow for every eligible repo and report at the end. Only pause to ask the user if something genuinely ambiguous or destructive comes up (e.g. a merge conflict on pull, a non-fast-forward that would need a force push, or a repo with an unexpected/detached HEAD).
+
+ If the user *does* pass specifics ("only repo X", "don't merge, just open PRs", "use a merge commit not squash", "target develop"), honor those over the defaults.
+
+ ---
+
## Error Handling
| Situation | Action |
|---|---|
- | `gh` not installed | Tell user, link to https://cli.github.com, stop |
- | `gh` not authenticated | Run `gh auth status` to confirm, then tell user to run `gh auth login` |
- | Not in a git repo | Tell user, stop |
- | `develop` branch missing | Warn user the repo may not follow git flow; fall back to `main`/`master` as base and mention they may want to create a `develop` branch |
- | Push rejected (branch exists on remote) | Try `git push --force-with-lease` only if branch was just created by this skill; otherwise ask user |
- | `gh pr create` fails (no upstream) | Ensure `--base` is set correctly per git flow conventions |
+ | `gh` not installed | Tell user, link https://cli.github.com, stop |
+ | `gh` not authenticated | Show `gh auth status`, tell user to `gh auth login` |
+ | No git repos in folder | Tell user, stop |
+ | `pull --ff-only` fails (diverged) | Don't force. Report the repo as needing manual attention in the summary and continue with the others |
+ | Merge blocked by required checks | Use `gh pr merge --auto --squash --delete-branch`; note "auto-merge enabled" in summary |
+ | Merge conflict | Skip that repo's merge, flag it in the summary, continue with the rest |
+ | PR already exists for branch | Reuse it (skip create), proceed to merge |
+ | Push rejected (branch exists on remote) | `git push --force-with-lease` only if this skill just created the branch; otherwise flag for the user |
+ | A single repo errors | Never abort the whole batch — record the failure and keep going |
---
## Expected tool-call sequence
- Every invocation of this skill MUST produce tool calls in roughly this order. If you find yourself writing a final answer without having made these calls, STOP and start over with the Bash tool.
+ Every invocation MUST produce tool calls in roughly this order. If you're writing a final answer without having made these calls, STOP and start over with Bash.
- 1. `Bash: git status`
- 2. `Bash: git diff` (and `git diff --staged` if anything is staged)
- 3. `Bash: git branch -a | grep -E '(develop|main|master)'`
- 4. Text to user: proposed branch name + commit message, ask for confirmation
- 5. `Bash: git checkout <base> && git pull origin <base> && git checkout -b <branch>`
- 6. `Bash: git add -A && git commit -m "<msg>"`
- 7. `Bash: git push -u origin <branch>`
- 8. `Bash: gh pr create --base <base> --title "..." --body "..." --assignee "@me"`
- 9. Text to user: the real PR URL returned by step 8 — never a placeholder like `.../pull/42` or `user/repo/pull/123`.
+ 1. `Bash`: discover repos (Step 0)
+ 2. Per repo: `Bash` fetch + classify (`branch --show-current`, `status --porcelain`, `rev-list --count`)
+ 3. Per **Case A** repo: `Bash` diff → commit → push → `gh pr create` → `gh pr merge --squash --delete-branch` → checkout base → pull
+ 4. Per **Case B/C** repo: `Bash` checkout base (if needed) + `pull --ff-only`
+ 5. Text to user: the summary table with **real** PR URLs
+