# Analyze Prs

> Analyze all open PRs targeting a base branch — determine merge order, identify file overlaps, and tag each PR as simple or needs_check for complexity. Use at the start of a PR consolidation workflow.

- **Type:** Skill
- **Install:** `agentstack add skill-talont-org-autoskillit-analyze-prs`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [TalonT-Org](https://agentstack.voostack.com/s/talont-org)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [TalonT-Org](https://github.com/TalonT-Org)
- **Source:** https://github.com/TalonT-Org/AutoSkillit/tree/main/src/autoskillit/skills_extended/analyze-prs

## Install

```sh
agentstack add skill-talont-org-autoskillit-analyze-prs
```

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

## About

# PR Analysis Skill

Analyze all open PRs targeting a base branch, determine a safe merge order, assess
complexity, and produce machine-readable output for the `merge-prs` recipe.

## When to Use

- At the start of a `merge-prs` run
- User wants to understand which PRs can be merged safely and in what order
- User says "analyze PRs", "order PRs", or "assess PR complexity"

## Critical Constraints

**NEVER:**
- Merge, close, or modify any PR
- Modify any source code files
- Create files outside `{{AUTOSKILLIT_TEMP}}/merge-prs/` directory
- Run subagents in the background (`run_in_background: true` is prohibited)

**ALWAYS:**
- Use subagents to fetch PR data in parallel
- Use `model: "sonnet"` when spawning all subagents via the Task tool
- Abort clearly if `gh` CLI is not authenticated
- Include every open PR targeting base_branch in the output — no PR is silently dropped (blocked PRs appear in ci_blocked_prs / review_blocked_prs arrays, not in the ordered prs list)
- **Default to parallel batch processing**: When multiple PRs are present, ALWAYS process
  all analysis tasks (diff fetching, overlap computation, complexity tagging) using parallel
  subagent batches. Processing PRs one-at-a-time without an explicit user instruction to do
  so is the wrong default. Up to 8 PRs should be processed in a single parallel batch;
  launch additional batches for larger sets.

## Arguments

`{base_branch} [merge_queue_data_path=]`

- `base_branch` — the base branch to list PRs against (e.g., `main`)
- `merge_queue_data_path` (optional) — absolute path to a JSON file containing pre-fetched merge queue data (produced by `fetch_merge_queue_data` run_python step). When provided and present, read from file instead of calling GitHub GraphQL API inline.

## Workflow

### Step 0: Authenticate and List PRs

Run:
```bash
gh pr list --base {base_branch} --state open --json number,title,headRefName,author,body,additions,deletions,changedFiles --limit 100
```

If zero PRs are returned: write a summary to terminal and exit cleanly with an empty
`pr_order_{ts}.json` (zero PRs, no integration branch needed).

If `gh` returns an auth error: abort with a clear message.

### Step 0.5: Detect GitHub Merge Queue

Before fetching diffs, load pre-fetched merge queue data to determine whether a merge queue
is active on `{base_branch}` with `MERGEABLE` entries.

1. Read pre-fetched merge queue data from disk when available:
   ```bash
   if [ -n "${merge_queue_data_path:-}" ]; then
       if [ -f "$merge_queue_data_path" ]; then
           QUEUE_ENTRIES="$(cat "$merge_queue_data_path")"
       else
           echo "WARNING: merge_queue_data_path='$merge_queue_data_path' provided but file not found — possible misconfiguration. Falling back to no-queue mode."
           QUEUE_ENTRIES="[]"
       fi
   else
       QUEUE_ENTRIES="[]"  # no path provided (standalone invocation) → QUEUE_MODE=false
   fi
   ```
   `QUEUE_ENTRIES` is a JSON array of `{position, state, pr_number, pr_title}` objects,
   pre-fetched by the `fetch_merge_queue_data` run_python step in the recipe. When
   `merge_queue_data_path` is absent (standalone invocation), `QUEUE_ENTRIES` defaults to
   `[]`, which sets `QUEUE_MODE = false`.

2. Determine mode:
   - If the resulting entry list contains at least one entry with `state == "MERGEABLE"`:
     set **`QUEUE_MODE = true`** and store the full sorted entry list as `QUEUE_ENTRIES`.
   - Otherwise (empty list or no MERGEABLE entries):
     set **`QUEUE_MODE = false`** and proceed with the existing analysis path.

   Log which mode was selected and the entry count to the terminal so pipeline runs are
   observable.

**State variables set by this step (referenced in Steps 1–4):**

| Variable | Type | Description |
|----------|------|-------------|
| `QUEUE_MODE` | boolean | `true` when the merge queue has ≥1 MERGEABLE entry; `false` otherwise |
| `QUEUE_ENTRIES` | list[dict] | Sorted queue entries `{position, state, pr_number, pr_title}` when `QUEUE_MODE = true`; empty list when `false` |

### Step 1: Fetch PR Data

- **If `QUEUE_MODE = false`**: **ALWAYS launch subagents in parallel** — never process PRs
  sequentially. Launch one Explore subagent per PR (up to 8 simultaneously; batch in groups
  of 8 if more):

  Each subagent fetches:
  - `gh pr diff {number}` — full unified diff
  - `gh pr view {number} --json files` — structured file list; extract path strings via
    `gh pr view {number} --json files -q '[.files[].path]'`
  - `gh pr view {number} --json body -q .body` — PR body to extract `## Requirements` section if present

  Each subagent returns:
  - `pr_number`: int
  - `title`: str
  - `branch`: str (headRefName)
  - `files_changed`: list of file paths (strings extracted from `.files[].path`)
  - `additions`: int
  - `deletions`: int
  - `test_files_changed`: list of test file paths (files matching `test_*.py`, `*_test.py`, `*.test.*`, `tests/**`)
  - `requirements_section`: str — the `## Requirements` section extracted from the PR body, or `""` if not present

- **If `QUEUE_MODE = true`**: for each PR number in `QUEUE_ENTRIES`, fetch only the
  metadata needed for the manifest (no diffs, no body extraction):
  ```
  gh pr view {number} --json headRefName,files,additions,deletions,changedFiles
  ```
  Extract file paths from the `files` array: `gh pr view {number} --json files -q '[.files[].path]'`.
  Collect `files_changed` (list of file path strings), `test_files_changed` (subset matching
  `test_*.py`, `*_test.py`, `*.test.*`, `tests/**`), `additions`, `deletions`, and
  `branch` (`headRefName`). Run these fetches in parallel (up to 8 simultaneously).
  The PR list for subsequent steps is exactly `QUEUE_ENTRIES` — do **not** use the
  `gh pr list` output.

### Step 1.5: Filter PRs by CI and Review Status

- **If `QUEUE_MODE = false`**: After fetching all PR diffs, filter the candidate list before
  building the overlap matrix. PRs that fail either gate are reported in the manifest but
  excluded from merge ordering.

  Use a single GraphQL alias query to fetch CI status and reviews for all PRs at once
  (1 API call regardless of PR count, instead of 2N sequential REST calls). Chunk into
  batches of 20 PRs to stay within GraphQL complexity limits.

  ```bash
  ELIGIBLE_PRS=()
  CI_BLOCKED_PRS=()      # [{number, title, reason}]
  REVIEW_BLOCKED_PRS=()  # [{number, title, reason}]

  # Build GraphQL alias query for all PRs in batches of 20
  ALL_PR_NUMS=($(echo "$ALL_PRS" | jq -r '.[].number'))
  BATCH_SIZE=20
  for batch_start in $(seq 0 $BATCH_SIZE $((${#ALL_PR_NUMS[@]} - 1))); do
    BATCH_NUMS=("${ALL_PR_NUMS[@]:$batch_start:$BATCH_SIZE}")

    QUERY="query { "
    for i in $(seq 0 $((${#BATCH_NUMS[@]} - 1))); do
      NUM="${BATCH_NUMS[$i]}"
      QUERY="${QUERY} pr${i}: repository(owner: \"${OWNER}\", name: \"${REPO}\") {
        pullRequest(number: ${NUM}) {
          number
          reviews(last: 20) { nodes { state } }
          commits(last: 1) { nodes { commit { statusCheckRollup {
            state
            contexts(last: 100) { nodes { ... on CheckRun { name status conclusion } } }
          } } } }
        }
      }"
    done
    QUERY="${QUERY} }"
    BATCH_RESULT=$(gh api graphql -f query="${QUERY}")

    # Parse per-PR results from BATCH_RESULT
    for i in $(seq 0 $((${#BATCH_NUMS[@]} - 1))); do
      NUM="${BATCH_NUMS[$i]}"
      PR_TITLE=$(echo "$ALL_PRS" | jq -r --argjson n "$NUM" '.[] | select(.number == $n) | .title')
      PR_DATA=$(echo "$BATCH_RESULT" | jq --arg key "pr${i}" '.data[$key].pullRequest')

      # --- CI Gate ---
      FAILING=$(echo "$PR_DATA" | jq '
        [(.commits.nodes[0].commit.statusCheckRollup.contexts.nodes // [])[] |
         select(.conclusion != null and
                .conclusion != "success" and
                .conclusion != "skipped" and
                .conclusion != "neutral")] | length')
      IN_PROGRESS=$(echo "$PR_DATA" | jq '
        [(.commits.nodes[0].commit.statusCheckRollup.contexts.nodes // [])[] |
         select(.conclusion == null)] | length')

      if [ "${FAILING:-0}" -gt 0 ] || [ "${IN_PROGRESS:-0}" -gt 0 ]; then
        REASON="CI failing: ${FAILING} failed, ${IN_PROGRESS} in-progress"
        CI_BLOCKED_PRS+=("{\"number\":${NUM},\"title\":\"${PR_TITLE}\",\"reason\":\"${REASON}\"}")
        continue
      fi

      # --- Review Gate ---
      CHANGES_REQUESTED=$(echo "$PR_DATA" | jq '
        [.reviews.nodes |
         group_by(.author.login)[] |
         last |
         select(.state == "CHANGES_REQUESTED")] | length')

      if [ "${CHANGES_REQUESTED:-0}" -gt 0 ]; then
        REASON="${CHANGES_REQUESTED} unresolved CHANGES_REQUESTED review(s)"
        REVIEW_BLOCKED_PRS+=("{\"number\":${NUM},\"title\":\"${PR_TITLE}\",\"reason\":\"${REASON}\"}")
        continue
      fi

      mapfile -t _pr_entry  200) → place after small PRs that touch the same files, unless they have no overlap

  Produce a final ordered list. Document the rationale for each ordering decision.

- **If `QUEUE_MODE = true`**: the merge order is `QUEUE_ENTRIES` sorted by `position`
  ascending (ascending is already guaranteed by `parse_merge_queue_response`).

### Step 4: Tag Complexity

- **If `QUEUE_MODE = false`**: For each PR in the ordered list, assign a complexity tag:

  **`simple`** — all of the following are true:
  - No shared files with any PR ahead of it in the merge order
  - Total additions  **IMPORTANT:** Emit the structured output tokens as **literal plain text with no
> markdown formatting on the token names**. Do not wrap token names in `**bold**`,
> `*italic*`, or any other markdown. The adjudicator performs a regex match on the
> exact token name — decorators cause match failure.

```
pr_order_file = {absolute_path_to_pr_order_json}
analysis_file = {absolute_path_to_pr_analysis_plan_md}
batch_branch = {batch_branch_name}
pr_count = {eligible_pr_count}
simple_count = {simple_pr_count}
needs_check_count = {needs_check_pr_count}
ci_blocked_count = {ci_blocked_pr_count}
review_blocked_count = {review_blocked_pr_count}
queue_mode = {queue_mode}   # true when merge queue has ≥1 MERGEABLE entry; false otherwise
```

## Related Skills

- **`/autoskillit:merge-pr`** — Merges individual PRs from this skill's ordered list
- **`/autoskillit:make-plan`** — Called for complex PRs that need conflict resolution plans
- **`/autoskillit:audit-impl`** — Receives `{{AUTOSKILLIT_TEMP}}/merge-prs/` as plans_input

## Source & license

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

- **Author:** [TalonT-Org](https://github.com/TalonT-Org)
- **Source:** [TalonT-Org/AutoSkillit](https://github.com/TalonT-Org/AutoSkillit)
- **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:** no
- **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-talont-org-autoskillit-analyze-prs
- Seller: https://agentstack.voostack.com/s/talont-org
- 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%.
