Pull Request / Merge Request
This skill never commits working-tree changes — it only pushes existing commits and manages the PR/MR.
Step 1: Pre-flight
If the current directory has no .git/ directory, read $CLAUDE_PLUGIN_ROOT/skills/init/references/multi-repo-detection.md and apply it. In a detected workspace:
- Filter to child repos on a non-default branch with commits ahead of the default branch (detect the default branch per
$CLAUDE_PLUGIN_ROOT/skills/pr/references/default-branch-detection.md, run inside each repo). Warn and skip any repo whose default branch cannot be detected. - 0 candidates → "No repositories in this workspace have branches with changes ready for a PR." Stop. 1 → announce which repo was detected and run Steps 2–7 inside it. Multiple → ask (AskUserQuestion): All or one specific repo.
- All → run Steps 2–7 per repo sequentially (show a
## <repo-name>heading before Step 2; run all commands inside that repo). Any per-repo stop condition (unknown platform, CLI cancel, update-flow Cancel, …) stops only that repo — record the outcome for the Step 8 summary and continue with the next. Only the gates below halt the whole run.
Verify git state (stop gates):
- Not inside a git repository → inform the user and stop.
- Default-branch detection (reference above) fails → "Could not detect the default branch. Ensure
originis configured and has been fetched." Stop. - On the default branch → "You're on the default branch (
<branch>). Switch to a feature branch first." Stop.
Step 2: Platform Detection
Read $CLAUDE_PLUGIN_ROOT/skills/pr/references/platform-detection.md and apply the Platform Detection Algorithm. Unknown platform → inform the user and stop.
Step 3: CLI Availability
Apply the reference's CLI Verification section. If the CLI is missing, offer to install it per its CLI Installation section (ask: Install / Cancel). On Cancel or install failure → provide manual installation instructions and stop. Installed but unauthenticated → "Run gh/glab auth login to authenticate, then re-run /optimus:pr." Stop.
Step 4: Push and Existing PR/MR Check
If the branch is not on the remote (git ls-remote --heads origin <branch>): with no commits on the branch → "No commits on this branch yet. Commit your changes first." Stop. Otherwise git push -u origin <branch>.
If the branch is on the remote with unpushed commits, check divergence first: git rev-list --count HEAD..origin/<branch>
- Count
0→ fast-forward push:git push origin <branch> - Count
> 0→ the remote has commits not in local history (typical after a rebase). NEVER plain-push. Ask (AskUserQuestion "Diverged branch"): Force push → rungit push --force-with-lease origin <branch>; if the lease is rejected → "Force push rejected — the remote gained new commits since your last fetch. Review them before overwriting." Stop. Cancel → tell the user to reconcile and push manually, then re-run/optimus:pr. Stop.
Check for an existing PR/MR:
- GitHub:
gh pr view --json number,state,title,body,url,baseRefName 2>/dev/null - GitLab:
glab mr view --output json 2>/dev/null
Open PR/MR → save its baseRefName / target_branch as the target branch and go to Step 6 (Update Flow). Closed/merged or none → Step 5 (Create Flow).
Step 5: Create Flow
Default branch: on GitHub, gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name' is authoritative over Step 1's local detection (local origin/HEAD may be stale); on GitLab, use Step 1's detection.
Gather the branch's commits and diff vs origin/<default-branch>. No commits ahead → "This branch has no changes compared to <default-branch>." Stop.
Detect intent context
Classify into one of three states before generating content:
- Conversation context — this conversation implemented the branch: Edit/Write/NotebookEdit calls touched files in the diff, or the conversation discussed the problem, design decisions, or non-goals. A
## TDD Summaryblock with### Behaviors Implemented(literal, case-sensitive — emitted by/optimus:tdd) is a strong state-1 signal; apply the population rule below. - Existing PR body has
## Intent(Update Flow only — see the detection rule in the template). - None — when uncertain, prefer this state over fabricating. Ask (AskUserQuestion "Intent capture"): Add intent now → have the user reply with Problem / Scope / Non-goals / Key decisions on separate lines in a plain message (blank lines skip a sub-field); Skip → omit the section entirely, no stub.
Suppress the state-3 prompt in states 1 and 2.
Generate content
Read $CLAUDE_PLUGIN_ROOT/skills/pr/references/pr-template.md and generate a title and body. Populate ## Intent from the conversation (state 1) or the user's reply (state 3), including only sub-fields the source actually answers.
TDD Summary population rule — when the handoff signal fired, fill the body from the summary block:
- Intent → Scope: one bullet per
### Behaviors Implementedrow with Status✓ Complete, description verbatim. - Intent → Non-goals: one bullet per
Not startedrow; omit if none. - Intent → Key decisions: refactor-step reasoning captured in the conversation; omit rather than invent.
- Intent → Problem: quote/summarize the spec or JIRA task file loaded in the conversation, else the initiating brief.
- Test plan: one verification item per
✓ Completerow plus the project's test command; if a### Coveragesection withBefore:/After:/Delta:lines is present, append one lineCoverage: <Before> → <After> (<Delta>).
Preview, confirm, create
Show the generated title and body under ## PR Preview (append — <repo-name> in a multi-repo run). Ask (AskUserQuestion): Create PR → proceed; Adjust → ask what to change, apply, and preview again.
Write the body to a temp file at a relative path: TMPFILE=$(mktemp ./pr-body-XXXXXX.md) — never /tmp (on Windows, Git Bash's /tmp is unresolvable by native gh.exe/glab.exe, which would silently submit an empty body). Remove it with rm -f "$TMPFILE" after the attempt.
- GitHub:
gh pr create --title "<title>" --body-file "$TMPFILE" --base <default-branch> - GitLab:
glab mr create --title "<title>" --description "$(cat "$TMPFILE")" --target-branch <default-branch>
PRs/MRs are created ready to merge (not draft). Proceed to Step 7.
Step 6: Update Flow
Show the existing PR/MR (number, title, URL, current body), then ask (AskUserQuestion "Update PR"):
- Regenerate title and description — existing
## Intentpreserved verbatim. - Regenerate description only — keep the title; existing
## Intentpreserved verbatim. - Regenerate Intent only — replace just the
## Intentsection (or add one if missing); everything else untouched. - Cancel — report the existing URL and stop.
Regenerate Intent only: re-detect intent context, treating the existing PR Intent as replaceable (the user opted in). Use conversation context when present; otherwise prompt directly for Problem / Scope / Non-goals / Key decisions in a plain reply (not the state-3 meta-prompt). Empty reply or all sub-fields blank → keep the existing Intent, report "No replacement Intent provided — existing ## Intent kept as-is. No changes will be made." and stop (do not enter Phase 3). Otherwise replace ## Intent in place (or insert it before ## Summary), skip Phases 1–2, and jump to Phase 3.
Phase 1 — fresh content. Gather commits and diff against the PR's saved target branch (not the repo default) and generate new content from the template. This is the authoritative content; do not present yet.
Phase 2 — preservation scan. Review the existing title and body for information that cannot be derived from the code changes:
## Intent— always preserve verbatim and re-insert before## Summary. Never silently overwrite it with fresh inference. If conversation context also exists, the existing section still wins; add a note to the Phase 3 preview: "Existing## Intentsection preserved. The current conversation suggests possible updates — choose 'Regenerate Intent only' if you want to revise."- Keep other non-diff information — issue/ticket references, deployment instructions, external links, reviewer notes, follow-ups — integrated into the matching section. Still-relevant non-standard sections go after
## Test plan. When regenerating the title, carry issue references into the new title. - Never preserve diff-derivable facts (version numbers, counts, symbol/path/file names) — always re-derive them from the current diff; rebases and force-pushes can change them. Discard anything outdated, wrong, or already covered by the fresh content.
- Friction floor: when the existing body has
## Intentand the conversation has no implementation context, the update must complete without any intent-related prompting — only the Phase 3 preview runs.
Phase 3 — preview. If non-diff content was carried over, note it briefly above the preview. Ask (AskUserQuestion): Update → proceed; Adjust → apply changes and preview again.
Apply using the Step 5 temp-file pattern:
- GitHub:
gh pr edit <number> --title "<title>" --body-file "$TMPFILE"(omit--titlewhen keeping the title) - GitLab:
glab mr update <number> --title "<title>" --description "$(cat "$TMPFILE")"
Step 7: Per-Repo Report
## PR/MR [Created / Updated] [— repo-name]
- URL: [url]
- Title: [title]
- Target: [target-branch]
- Status: Ready to merge
In a multi-repo run, continue with the next repo (back to Step 2); hold all recommendations until every repo is done.
Step 8: Final Summary
After a multi-repo run, show a combined table: Repo | PR/MR | Status (Created / Updated / Skipped (reason)) | URL.
Recommend /optimus:code-review in a fresh conversation for a quality review before merging — it gathers its own context from the PR description.