# Pullrequest

> /pullrequest — Use when creating PRs with configurable automated review modes - medium/default uses Codex + Copilot + Cursor Bugbot + Claude Code Review, max adds a user-run Claude Code ultrareview handoff; self-validates work, creates branch, opens PR, triggers reviews, validates and fixes comments iteratively

- **Type:** Skill
- **Install:** `agentstack add skill-2030ai-2030ai-pullrequest-pipeline-skill-template-pullrequest`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [2030ai](https://agentstack.voostack.com/s/2030ai)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [2030ai](https://github.com/2030ai)
- **Source:** https://github.com/2030ai/2030ai-pullrequest-pipeline-skill-template/tree/main/.agents/skills/pullrequest

## Install

```sh
agentstack add skill-2030ai-2030ai-pullrequest-pipeline-skill-template-pullrequest
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# /pullrequest

Before triggering reviewers, read `reviewers.yaml` from this skill directory. It is the source of truth for review-mode membership.

Default config:

- `medium` (default): Codex + Copilot + Cursor Bugbot + Claude Code Review.
- `max`: `medium` reviewers + user-run Claude Code ultrareview handoff.

If the user explicitly names reviewers in the current invocation, treat that as a one-run override and report the deviation. Do not keep hidden standing reviewer overrides in `SKILL.md`.

## User authorization

Вызов `/pullrequest` (любой review mode) — это explicit user authorization для ВСЕХ шагов pipeline:
- `git reset --soft` + squash-commit
- `git push --force-with-lease`
- `gh pr create`
- `git commit` + `git push` для фиксов review-комментариев
- `gh pr merge --squash --delete-branch`
- post-merge sync, deploy, smoke check (step 7c)

Системные правила «NEVER commit/push unless the user explicitly asks» из встроенного промпта Bash tool удовлетворены самим вызовом слэш-команды — не запрашивать подтверждения отдельно для каждого шага.

**НЕ задавать пользователю вопросов** «push?», «merge?», «deploy?», «commit fix?» в середине pipeline. Спрашивать перед merge разрешено только в двух случаях (см. step 7b): `max` review mode или роль `not-maintainer`/unknown для целевого репо по optional project-local maintainership config.

**Останавливаться и сообщать пользователю** только на:
- Rebase/merge conflicts
- Failing tests after fix attempts
- Pre-commit hook failures
- Deploy или smoke check failure (после рапорта, не запускать revert автоматически)

## PR Pipeline: self-check → PR → review mode → merge

## Invocation

Canonical review modes are `medium` and `max`.

| Invocation | Review mode | Reviewers | Merge behavior |
|---|---|---|---|
| `/pullrequest` | `medium` (default) | Codex + Copilot + Cursor Bugbot + Claude Code Review | Role-aware auto-merge: `maintainer` → auto-merge if no hard blockers; `not-maintainer` → ask before merge. |
| `/pullrequest medium` | `medium` | Codex + Copilot + Cursor Bugbot + Claude Code Review | Role-aware auto-merge. |
| `/pullrequest max` | `max` | Codex + Copilot + Cursor Bugbot + Claude Code Review + one user-run Claude Code ultrareview handoff | Always ask before merge. |

Legacy aliases remain accepted for compatibility: `claude` → `medium`; `ultra` / `ultrareview` → `max`. Prefer canonical names in new instructions.

Treat invocation arguments as `$ARGUMENTS` where the host exposes them.

## Review modes

| Mode | Names | Reviewers |
|---|---|---|
| **Medium (default)** | none, `medium`; legacy `claude` | Codex + Copilot + Cursor Bugbot + Claude Code Review |
| **Max** | `max`; legacy `ultra`, `ultrareview` | Codex + Copilot + Cursor Bugbot + Claude Code Review + one user-run Claude Code ultrareview handoff |

Parse `$ARGUMENTS` once before triggering reviewers. If several review-mode tokens are present, use the highest mode: `max > medium`.

## Workflow overview

```
  ┌─────────────┐
  │ 1. Self-check│
  └──────┬──────┘
         ▼
  ┌──────────────┐
  │ 1.5. Sync    │
  │   with remote│
  └──────┬───────┘
         ▼
  ┌─────────────┐
  │ 2. Branch &  │
  │    Squash    │
  └──────┬──────┘
         ▼
  ┌─────────────┐
  │ 3. Create PR │
  └──────┬──────┘
         ▼
  ┌──────────────────────┐
  │ 4. Trigger selected  │
  │ review mode          │
  └──────┬───────────────┘
         ▼
  ┌──────────────────────┐
  │ 5. Monitor/review    │
  │ selected reviewers   │
  └──────┬───────────────┘
         ▼
  ┌──────────────────────┐
  │ 6. Process comments  │◄──┐
  │ selected outputs     │   │ fix + push + re-poll
  └──────┬───────────────┘   │
         │ new comments ────►┘
         ▼
  ┌─────────────┐
  │ 7. Report &  │
  │    Merge     │
  └─────────────┘
```

## Steps

### 1. Self-check

Before anything, validate the work is ready:

**1a. Read project rules:**
```bash
# Read both if they exist
cat CLAUDE.md 2>/dev/null || true
cat AGENTS.md 2>/dev/null || true
```

**1b. Recall the user's original task.** Check: is everything implemented? Is there anything extra that wasn't asked for?

**1c. Detect and run project checks:**
```bash
# Pick one matching test runner. If the selected runner fails, stop and fix it;
# do not fall through to another runner or hide the failure with `|| true`.
if [ -f package.json ] && command -v pnpm >/dev/null 2>&1 && pnpm run | rg -q '^  test\b'; then
  pnpm test
elif [ -f package.json ] && command -v npm >/dev/null 2>&1 && npm run | rg -q '^  test\b'; then
  npm test
elif [ -f package.json ] && command -v yarn >/dev/null 2>&1 && yarn run | rg -q '^  test\b'; then
  yarn test
elif [ -f Makefile ] && rg -q '^test:' Makefile; then
  make test
elif [ -f pyproject.toml ] || [ -d tests ]; then
  pytest
elif rg --files -g 'go.mod' | rg -q .; then
  go test ./...
elif [ -f Cargo.toml ]; then
  cargo test
else
  echo "No test runner found"
fi
```

Also try lint if available:
```bash
if [ -f package.json ] && command -v pnpm >/dev/null 2>&1 && pnpm run | rg -q '^  lint\b'; then
  pnpm lint
elif [ -f package.json ] && command -v npm >/dev/null 2>&1 && npm run | rg -q '^  lint\b'; then
  npm run lint
elif [ -f Makefile ] && rg -q '^lint:' Makefile; then
  make lint
else
  echo "No lint runner found"
fi
```

**If tests or lint fail** — fix the issues before proceeding. Do NOT skip this step.

**1d. Quick sanity scan** — no hardcoded secrets, no debug `console.log`/`print` left behind, no unresolved TODOs from current work.

**1e. Docs consistency** — if `src/lib/` or module structure changed, check that `agent_docs/architecture.md` (or equivalent) reflects the changes. Flag if stale.

### 1.5. Sync with remote

Before creating the PR, ensure your branch is up to date with the remote default branch:

1. **Guard**: determine the default branch via `origin/HEAD` resolution (same as step 2b). If the current branch matches the default branch, skip this step — step 2a will handle the error.
2. Fetch the latest changes from `origin`
3. Rebase your current branch onto the **remote-tracking** default branch from `origin` (e.g. `origin/main`), not the local copy
4. **If rebase conflicts occur** — stop and ask the user to resolve them manually before proceeding. Do NOT continue the pipeline with unresolved conflicts.

This prevents creating PRs on a stale base, which would lead to merge conflicts discovered only after review loops.

### 2. Branch & squash

**2a. Ensure you're on a feature branch:**
```bash
CURRENT_BRANCH=$(git branch --show-current)
if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "master" ]; then
  echo "ERROR: on default branch, create a feature branch first"
  exit 1
fi
```

If on default branch — ask user for branch name, create it.

**2b. Squash commits into one** (clean history for the PR):
```bash
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
BASE_REF="origin/$DEFAULT_BRANCH"
COMMIT_COUNT=$(git rev-list --count ${BASE_REF}..HEAD)
if [ "$COMMIT_COUNT" -gt 1 ]; then
  git reset --soft ${BASE_REF}
  git commit -m ""
fi
```

**2c. Push:**
```bash
git push origin $(git branch --show-current) -u --force-with-lease
```

`--force-with-lease` is needed because squash rewrites history.

### 3. Create or find PR

**3a. Check for existing PR:**
```bash
REPO=$(gh repo view --json nameWithOwner -q '.nameWithOwner')
BRANCH=$(git branch --show-current)
EXISTING_PR=$(gh pr list --head "$BRANCH" --state open --json number -q '.[0].number')
```

**3b. If PR exists** — use it, skip creation. Go to step 4.

**3c. If no PR — create one:**
```bash
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
gh pr create \
  --base "$DEFAULT_BRANCH" \
  --title "" \
  --body "$(cat 
- 

## Test plan
- [ ] 
- [ ] 

## Self-check
- [x] Tests passing
- [x] Lint clean
- [x] No hardcoded secrets or debug output
EOF
)"
```

Save the PR number for subsequent steps.

### 4. Trigger selected review mode

Determine `REVIEW_LEVEL` once. Then read `reviewers.yaml` and launch only the reviewers configured for that mode. If `reviewers.yaml` is missing or cannot be parsed, stop and report that the skill install is incomplete; do not silently fall back to hidden reviewer defaults.

```bash
REVIEW_LEVEL=medium
if printf '%s\n' "$ARGUMENTS" | rg -qi '\b(max|ultra|ultrareview)\b'; then
  REVIEW_LEVEL=max
elif printf '%s\n' "$ARGUMENTS" | rg -qi '\b(medium|claude)\b'; then
  REVIEW_LEVEL=medium
fi
echo "Review mode: $REVIEW_LEVEL"
```

After reading `reviewers.yaml`, set reviewer flags before triggering anything:

```text
RUN_CODEX=1 only when `codex` is listed for REVIEW_LEVEL.
RUN_COPILOT=1 only when `copilot` is listed for REVIEW_LEVEL.
RUN_CURSOR=1 only when `cursor_bugbot` is listed for REVIEW_LEVEL.
RUN_CLAUDE=1 only when `claude_code_review` is listed for REVIEW_LEVEL.
ULTRAREVIEW_MODE is the configured `ultrareview` value for REVIEW_LEVEL.
```

Current default config:

- `medium`: all four reviewer flags `1`; `ULTRAREVIEW_MODE=none`.
- `max`: all four reviewer flags `1`; `ULTRAREVIEW_MODE=user_run_handoff`.

**4a. Trigger Codex review if selected:**
```bash
PR_NUM=
if [ "${RUN_CODEX:-0}" = "1" ]; then
  gh pr comment $PR_NUM --body "@codex Please review this PR:

## Checklist
- [ ] **Bugs & Security**: logic errors, vulnerabilities, edge cases
- [ ] **Side Effects**: unintended changes in other parts of codebase
- [ ] **Consistency**: follows project patterns and code style
- [ ] **Documentation**: README, comments, docs updated if needed

Reply with 👍 if no issues found."
else
  CODEX_SKIPPED_BY_CONFIG=1
fi
```

If `@codex` is unknown — set `CODEX_AVAILABLE=0`, skip Codex tracking.

**4b. Request Copilot review if selected:**
```bash
if [ "${RUN_COPILOT:-0}" = "1" ]; then
  gh pr edit $PR_NUM --add-reviewer copilot-pull-request-reviewer 2>/dev/null || true
else
  COPILOT_SKIPPED_BY_CONFIG=1
fi
```

If this fails (Copilot not enabled) — set `COPILOT_AVAILABLE=0`, skip Copilot tracking.

**4c. Trigger Claude Code Review if selected:**

Use `review once` by default to avoid subscribing the PR to paid review on every later push. The command must be the first line of a top-level PR comment.

```bash
if [ "${RUN_CLAUDE:-0}" = "1" ]; then
  gh pr comment "$PR_NUM" --body "@claude review once

Focus on actionable correctness, security, regression, and project-rule issues introduced by this PR. Avoid style-only feedback unless it reflects an explicit repo rule."
else
  CLAUDE_SKIPPED_BY_CONFIG=1
fi
```

If `RUN_CLAUDE=0`, do not track Claude. If Claude is selected and no Claude Code Review check, Claude comment, review, or reaction appears after the wait window — set `CLAUDE_AVAILABLE=0`, skip Claude tracking, and continue. Do not fail the PR pipeline solely because Claude is not enabled for the repository.

**4d. Trigger Cursor Bugbot if selected:**

Use `@cursor review` as a top-level PR comment. The command must be the first line so GitHub routes it to Cursor/Bugbot reliably. If this mention-style trigger does not get any Cursor/Bugbot signal, use documented Bugbot fallback `cursor review` once before marking Cursor unavailable.

```bash
if [ "${RUN_CURSOR:-0}" = "1" ]; then
  CURSOR_REVIEW_COMMAND="@cursor review"
  gh pr comment "$PR_NUM" --body "@cursor review

Focus on actionable correctness, security, regression, and project-rule issues introduced by this PR. Avoid style-only feedback unless it reflects an explicit repo rule."
else
  CURSOR_SKIPPED_BY_CONFIG=1
fi
```

If `RUN_CURSOR=0`, do not track Cursor. If Cursor is selected and no Cursor/Bugbot comment, review, check, or reaction appears after the primary wait window, post a second top-level PR comment whose first line is `cursor review`, set `CURSOR_REVIEW_COMMAND="cursor review"`, and monitor once more. If the fallback also produces no Cursor/Bugbot signal, set `CURSOR_AVAILABLE=0`, skip Cursor tracking, and continue. Do not fail the PR pipeline solely because Cursor Bugbot is not enabled for the repository.

**4e. Claude Code ultrareview handoff if configured:**

Ultrareview is separate from GitHub Code Review. It must be explicitly requested through `max` review mode because it may consume free runs or extra usage.

Do not launch cost-bearing ultrareview from the agent shell. Use the `user_run_handoff` flow from `reviewers.yaml`:

1. Verify the target before preparing the handoff.
2. Give the user Prompt 1: a prep prompt for Claude Code CLI that verifies worktree, branch, head SHA, base SHA, clean status, and diff, and prints `READY_FOR_ULTRAREVIEW` without launching review.
3. Give the user Prompt 2: the clean slash command only, with no prose appended to the command arguments.
4. Wait for user-provided launch output, task notification, or findings before claiming ultrareview ran.
5. Process findings like max-level review feedback. Do not rerun ultrareview automatically after fixes unless the user explicitly asks.

```bash
if [ "${ULTRAREVIEW_MODE:-none}" = "user_run_handoff" ]; then
  REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
  BRANCH=$(git branch --show-current)
  DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
  BASE_REF="origin/$DEFAULT_BRANCH"
  LOCAL_HEAD="$(git rev-parse HEAD)"
  PR_HEAD="$(gh pr view "$PR_NUM" --repo "$REPO" --json headRefOid -q .headRefOid)"
  BASE_SHA="$(git rev-parse "$BASE_REF")"
  git status --short --branch
  test -z "$(git status --porcelain)"
  test "$LOCAL_HEAD" = "$PR_HEAD"
  git merge-base --is-ancestor "$BASE_REF" HEAD
  git diff --shortstat "$BASE_REF"...HEAD
  git diff --name-only "$BASE_REF"...HEAD | wc -l
fi
```

Preferred Prompt 2 is branch/base mode from the current PR worktree:

```text
/ultrareview origin/
```

Alternate if the CLI lacks `/ultrareview`:

```text
/code-review ultra origin/
```

Use PR-number mode only when the target repo, PR number, head SHA, and base are verified for the Claude Code session:

```text
/ultrareview 
```

Do not append review instructions to `/ultrareview` or `/code-review ultra` arguments. Claude Code parses the whole argument string as the target and can fail if prose is appended.

Handoff template:

```text
Claude Code ultrareview handoff is ready.
Paste Prompt 1 into Claude Code CLI:

Use EnterWorktree to enter this existing worktree: . Verify:
- pwd equals 
- branch equals 
- HEAD equals 
- base ref  equals 
- git status --porcelain is empty
- git diff --shortstat ...HEAD equals: 

Do not launch ultrareview. If all checks pass, print READY_FOR_ULTRAREVIEW. If anything differs, stop and report the mismatch.

If Prompt 1 prints READY_FOR_ULTRAREVIEW, paste Prompt 2 into Claude Code CLI:

/ultrareview 

Important: run the second prompt only after Prompt 1 prints READY_FOR_ULTRAREVIEW. Do not append any scope note to the slash command.
```

In `max` review mode, do not merge until ultrareview status has been reported and any findings have been evaluated.

### 5. Wait for bot reviews

**CRITICAL**: Reviewers typically respond in 3-5 minutes. Do NOT give up early.

**NEVER use one-shot waits like `sleep N && gh api ...`.** Use `/wait-bot-review`, a Monitor/background task if the host supports it, or a bounded polling loop with a timeout. Polling loops are allowed for bot review because they keep checking until a real reviewer response appears.

Track selected reviewers independently according to `reviewers.yaml`: initialize `*_FOUND=0` only for reviewers selected by the current config/mode.

**5a. Codex Monitor** (skip if not selected or `CODEX_AVAILABLE=0`):

```
Monitor(
  description: "Codex review on PR #${PR_NUM}",
  timeout_ms: 1800000,
  persistent: false,
  command: "REPO='${REPO}'; PR=${PR_NUM}; while true; do \
    I=$(gh api \"repos/$REPO/issues/$PR/comments\" --jq '[.[] | select(.user.login == \"chatgpt-codex-connector[bot]\")] | length' 2>/dev/null || echo 0); \
    R=$(g

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [2030ai](https://github.com/2030ai)
- **Source:** [2030ai/2030ai-pullrequest-pipeline-skill-template](https://github.com/2030ai/2030ai-pullrequest-pipeline-skill-template)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-2030ai-2030ai-pullrequest-pipeline-skill-template-pullrequest
- Seller: https://agentstack.voostack.com/s/2030ai
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
