pr-prism 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,8 +16,6 @@ jobs:
16
16
 
17
17
  - name: Setup pnpm
18
18
  uses: pnpm/action-setup@v4
19
- with:
20
- version: 9.15.0
21
19
 
22
20
  - name: Setup Node.js
23
21
  uses: actions/setup-node@v4
@@ -0,0 +1,114 @@
1
+ name: Release & Publish
2
+
3
+ # Auto-releases on every push to main.
4
+ #
5
+ # Version bump is determined by conventional commit prefix:
6
+ # feat!: / BREAKING CHANGE → major
7
+ # feat: → minor
8
+ # everything else → patch
9
+ #
10
+ # If package.json already has an un-tagged version (manual bump),
11
+ # that version is used as-is — no auto-bump.
12
+ #
13
+ # Skip release: add [skip release] to the commit message.
14
+
15
+ on:
16
+ push:
17
+ branches: [main]
18
+
19
+ concurrency:
20
+ group: release
21
+ cancel-in-progress: false
22
+
23
+ permissions:
24
+ id-token: write
25
+ contents: write
26
+
27
+ jobs:
28
+ release:
29
+ if: >
30
+ !startsWith(github.event.head_commit.message, 'chore(release):') &&
31
+ !contains(github.event.head_commit.message, '[skip release]')
32
+ runs-on: ubuntu-latest
33
+
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ with:
37
+ fetch-depth: 0
38
+
39
+ - uses: pnpm/action-setup@v4
40
+
41
+ - uses: actions/setup-node@v4
42
+ with:
43
+ node-version: 22
44
+ registry-url: https://registry.npmjs.org
45
+ cache: pnpm
46
+
47
+ - name: Upgrade npm for Trusted Publishing
48
+ run: npm install -g npm@latest
49
+
50
+ - name: Install dependencies
51
+ run: pnpm install --frozen-lockfile
52
+
53
+ - name: Configure Git
54
+ run: |
55
+ git config user.name "github-actions[bot]"
56
+ git config user.email "github-actions[bot]@users.noreply.github.com"
57
+
58
+ - name: Determine version bump type
59
+ id: bump
60
+ run: |
61
+ MSG=$(git log -1 --pretty=format:'%s')
62
+
63
+ if echo "$MSG" | grep -qE '^[a-z]+(\(.*\))?!:' || echo "$MSG" | grep -q 'BREAKING CHANGE'; then
64
+ echo "type=major" >> "$GITHUB_OUTPUT"
65
+ elif echo "$MSG" | grep -qE '^feat(\(.*\))?:'; then
66
+ echo "type=minor" >> "$GITHUB_OUTPUT"
67
+ else
68
+ echo "type=patch" >> "$GITHUB_OUTPUT"
69
+ fi
70
+
71
+ echo "Commit: $MSG"
72
+ echo "Bump type: $(grep type= "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)"
73
+
74
+ - name: Resolve version
75
+ id: version
76
+ run: |
77
+ CURRENT=$(node -p "require('./package.json').version")
78
+ TAG="v${CURRENT}"
79
+
80
+ if git rev-parse "${TAG}" >/dev/null 2>&1; then
81
+ echo "v${CURRENT} already tagged — auto-bumping ${{ steps.bump.outputs.type }}"
82
+ npm version ${{ steps.bump.outputs.type }} --no-git-tag-version
83
+ else
84
+ echo "v${CURRENT} is a new version — using as-is"
85
+ fi
86
+
87
+ VERSION=$(node -p "require('./package.json').version")
88
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
89
+ echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
90
+ echo "Releasing v${VERSION}"
91
+
92
+ - name: Type check
93
+ run: pnpm run typecheck
94
+
95
+ - name: Commit version bump and tag
96
+ run: |
97
+ if ! git diff --quiet package.json 2>/dev/null; then
98
+ git add package.json pnpm-lock.yaml 2>/dev/null || git add package.json
99
+ git commit -m "chore(release): v${{ steps.version.outputs.version }}"
100
+ fi
101
+
102
+ git tag -a "v${{ steps.version.outputs.version }}" \
103
+ -m "Release v${{ steps.version.outputs.version }}"
104
+ git push origin main --follow-tags
105
+
106
+ - name: Publish to npm
107
+ run: pnpm publish --no-git-checks --access public --provenance
108
+
109
+ - name: Create GitHub Release
110
+ uses: softprops/action-gh-release@v2
111
+ with:
112
+ tag_name: v${{ steps.version.outputs.version }}
113
+ name: v${{ steps.version.outputs.version }}
114
+ generate_release_notes: true
@@ -0,0 +1,123 @@
1
+ # Automated Release Workflow
2
+ # ─────────────────────────
3
+ # Bumps version, creates Git tag, and publishes a GitHub Release
4
+ # when a PR is merged into `main`.
5
+ #
6
+ # Version bump is determined by PR labels:
7
+ # - `major` → breaking changes (1.0.0 → 2.0.0)
8
+ # - `minor` → new features (1.0.0 → 1.1.0)
9
+ # - `patch` → bug fixes (1.0.0 → 1.0.1) [default]
10
+ # - `no-release` → skip entirely
11
+ #
12
+ # Template Note:
13
+ # - Ensure labels `major`, `minor`, `patch`, `no-release` exist in your repo
14
+ # - For npm publish: uncomment the publish step and add NPM_TOKEN secret
15
+ # - Works with dev → main merge flow out of the box
16
+ # - GITHUB_TOKEN won't trigger downstream workflows; use a PAT if needed
17
+
18
+ name: Release
19
+
20
+ on:
21
+ pull_request:
22
+ types: [closed]
23
+ branches: [main]
24
+
25
+ jobs:
26
+ release:
27
+ if: >
28
+ github.event.pull_request.merged == true &&
29
+ !contains(github.event.pull_request.labels.*.name, 'no-release')
30
+ runs-on: ubuntu-latest
31
+ permissions:
32
+ contents: write
33
+
34
+ steps:
35
+ - name: Checkout
36
+ uses: actions/checkout@v6
37
+ with:
38
+ ref: main
39
+ fetch-depth: 0
40
+ token: ${{ secrets.GITHUB_TOKEN }}
41
+
42
+ - name: Setup Node.js
43
+ uses: actions/setup-node@v6
44
+ with:
45
+ node-version: 22
46
+
47
+ - name: Configure Git
48
+ run: |
49
+ git config user.name "github-actions[bot]"
50
+ git config user.email "github-actions[bot]@users.noreply.github.com"
51
+
52
+ - name: Determine version bump
53
+ id: bump
54
+ env:
55
+ LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }}
56
+ run: |
57
+ if echo "$LABELS" | grep -q '"major"'; then
58
+ echo "type=major" >> "$GITHUB_OUTPUT"
59
+ elif echo "$LABELS" | grep -q '"minor"'; then
60
+ echo "type=minor" >> "$GITHUB_OUTPUT"
61
+ else
62
+ echo "type=patch" >> "$GITHUB_OUTPUT"
63
+ fi
64
+
65
+ - name: Bump version in package.json
66
+ id: version
67
+ run: |
68
+ if ! node -e "const p = require('./package.json'); if (!p.version) process.exit(1);" 2>/dev/null; then
69
+ echo "No version found in package.json — initializing to 0.1.0"
70
+ npm pkg set version=0.1.0
71
+ fi
72
+ NEW_VERSION=$(npm version ${{ steps.bump.outputs.type }} --no-git-tag-version)
73
+ echo "version=$NEW_VERSION" >> "$GITHUB_OUTPUT"
74
+ echo "Bumped to $NEW_VERSION (${{ steps.bump.outputs.type }})"
75
+
76
+ - name: Build release notes from commits
77
+ id: notes
78
+ env:
79
+ PR_TITLE: ${{ github.event.pull_request.title }}
80
+ PR_NUMBER: ${{ github.event.pull_request.number }}
81
+ PR_URL: ${{ github.event.pull_request.html_url }}
82
+ BUMP_TYPE: ${{ steps.bump.outputs.type }}
83
+ run: |
84
+ PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
85
+ if [ -n "$PREV_TAG" ]; then
86
+ COMMITS=$(git log "$PREV_TAG"..HEAD --pretty=format:"- %s (%h)" --no-merges)
87
+ else
88
+ COMMITS=$(git log --pretty=format:"- %s (%h)" --no-merges -20)
89
+ fi
90
+
91
+ {
92
+ echo "body<<RELEASE_EOF"
93
+ echo "## What Changed"
94
+ echo ""
95
+ echo "**PR:** [#${PR_NUMBER} — ${PR_TITLE}](${PR_URL})"
96
+ echo "**Bump:** \`${BUMP_TYPE}\`"
97
+ echo ""
98
+ echo "### Commits"
99
+ echo ""
100
+ echo "$COMMITS"
101
+ echo ""
102
+ echo "RELEASE_EOF"
103
+ } >> "$GITHUB_OUTPUT"
104
+
105
+ - name: Commit version bump and create tag
106
+ run: |
107
+ git add package.json package-lock.json 2>/dev/null || git add package.json
108
+ git commit -m "chore(release): ${{ steps.version.outputs.version }}"
109
+ git tag -a "${{ steps.version.outputs.version }}" \
110
+ -m "Release ${{ steps.version.outputs.version }} — ${{ github.event.pull_request.title }}"
111
+ git push origin main --follow-tags
112
+
113
+ - name: Create GitHub Release
114
+ env:
115
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
116
+ TAG: ${{ steps.version.outputs.version }}
117
+ run: |
118
+ gh release create "$TAG" \
119
+ --title "$TAG — ${{ github.event.pull_request.title }}" \
120
+ --notes "${{ steps.notes.outputs.body }}" \
121
+ --latest
122
+
123
+ # npm publish is handled by publish.yml (triggered by the tag created above)
@@ -18,8 +18,6 @@ jobs:
18
18
 
19
19
  - name: Setup pnpm
20
20
  uses: pnpm/action-setup@v4
21
- with:
22
- version: 9.15.0
23
21
 
24
22
  - name: Setup Node.js
25
23
  uses: actions/setup-node@v4
@@ -0,0 +1,3 @@
1
+ {
2
+ "agentMentions": ["cubic-dev-ai", "coderabbitai"]
3
+ }
package/README.md CHANGED
@@ -1,114 +1,119 @@
1
1
  # pr-prism
2
2
 
3
- > Filter the noise. Focus on what matters.
3
+ <div align="center">
4
4
 
5
- **pr-prism** is a stateful GitHub PR review scraper built for AI agent workflows. It fetches review comments directly via the GitHub GraphQL API, filters out bots and noise, emits only what's actionable — once per comment, forever cached — and can resolve handled threads and tag agents for re-review when fixes are pushed.
5
+ [![npm version](https://img.shields.io/npm/v/pr-prism)](https://www.npmjs.com/package/pr-prism)
6
+ [![npm downloads](https://img.shields.io/npm/dm/pr-prism)](https://www.npmjs.com/package/pr-prism)
7
+ [![CI](https://github.com/YosefHayim/pr-prism/actions/workflows/ci.yml/badge.svg)](https://github.com/YosefHayim/pr-prism/actions/workflows/ci.yml)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
9
+ [![Contributors Welcome](https://img.shields.io/badge/contributors-welcome-brightgreen.svg)](https://github.com/YosefHayim/pr-prism/issues)
6
10
 
7
- ---
8
-
9
- ## The Problem
11
+ > Stateful GitHub PR review scraper for AI agent workflows. Filter noise, cache seen comments, deliver signal.
10
12
 
11
- Running an AI agent in a PR-fix loop has one brutal problem: **statefulness**.
13
+ Built with [grimoire-wizard](https://github.com/YosefHayim/grimoire) for interactive CLI prompts.
12
14
 
13
- Every re-run, the agent re-reads the same resolved comments, outdated threads, and bot spam — wasting tokens, inflating context, and confusing what actually needs fixing right now.
15
+ </div>
14
16
 
15
- ## The Solution
16
-
17
- pr-prism solves this with a two-command workflow:
17
+ ---
18
18
 
19
- 1. **`pr-review`** scrapes new comments, caches IDs so re-runs only show what's new
20
- 2. **`pr-resolve`** — resolves handled threads via GraphQL and tags AI agents for re-review
19
+ AI agents re-reading the same resolved comments, outdated threads, and bot spam on every loop — wasting tokens and losing context. pr-prism solves this with two commands: scrape only what's new, then resolve what's been addressed.
21
20
 
22
- ---
21
+ ## Why
23
22
 
24
- ## Features
23
+ AI coding agents loop on PR review comments — re-reading the same resolved threads, outdated feedback, and bot noise on every iteration. This wastes tokens and loses context fast.
25
24
 
26
- | Feature | What it does |
27
- |---|---|
28
- | **ID cache** | `pr-reviews/.scraped-ids.json` — re-runs emit only new comments |
29
- | **Resolved threads** | Silently skipped via GraphQL `isResolved` |
30
- | **Outdated threads** | Flagged with `⚠️ OUTDATED` so agents don't chase dead feedback |
31
- | **Bot filter** | Authors ending in `[bot]` or matching `KNOWN_BOTS` are skipped |
32
- | **Suggested changes** | ` ```suggestion ` blocks rendered as clean `diff` (REMOVE / ADD) |
33
- | **File path** | Each inline comment prefixed with `📄 File: path/to/file.ts` |
34
- | **Noise domains** | Social sharing links (Twitter, Reddit, LinkedIn, CodeAnt) stripped |
35
- | **Thread resolution** | `pr-resolve` closes threads via `resolveReviewThread` mutation |
36
- | **Agent tagging** | `--tag-agents` posts a configurable @mention comment after resolving |
25
+ pr-prism solves this with two focused commands: scrape only what's new since the last run, then auto-resolve threads that have been addressed. Your agent gets clean signal, not stale noise.
37
26
 
38
27
  ---
39
28
 
40
29
  ## Requirements
41
30
 
42
31
  - Node.js ≥ 20
43
- - [pnpm](https://pnpm.io) (or npm / yarn)
44
- - [gh CLI](https://cli.github.com) authenticated: `gh auth login`
32
+ - [gh CLI](https://cli.github.com) authenticated (`gh auth login`)
45
33
 
46
34
  ---
47
35
 
48
36
  ## Installation
49
37
 
38
+ **One-time use (no install):**
39
+ ```bash
40
+ npx pr-prism pr-review -- 42
41
+ ```
42
+
43
+ **Global install:**
44
+ ```bash
45
+ npm install -g pr-prism
46
+ ```
47
+
48
+ **Local to your project:**
50
49
  ```bash
51
- git clone https://github.com/YosefHayim/pr-prism
52
- cd pr-prism
53
- pnpm install
50
+ npm install --save-dev pr-prism
54
51
  ```
55
52
 
56
53
  ---
57
54
 
58
55
  ## Usage
59
56
 
60
- ### `pr-review` — scrape review comments
57
+ ### `pr-review` — scrape new comments
61
58
 
62
59
  ```bash
63
- pnpm run pr-review # detect repo, list open PRs, interactive select
64
- pnpm run pr-review -- 42 # process PR #42 directly
65
- pnpm run pr-review -- <url> # process by full GitHub PR URL
60
+ pr-review # list open PRs, pick interactively
61
+ pr-review 42 # process PR #42 directly
62
+ pr-review <url> # process by full GitHub PR URL
66
63
  ```
67
64
 
68
- ### `pr-resolve` resolve threads + tag agents
65
+ On first run, automatically appends output patterns to your `.gitignore`.
66
+
67
+ ### `pr-resolve` — resolve threads
69
68
 
70
69
  ```bash
71
- pnpm run pr-resolve # resolve threads from latest .threads-*.json
72
- pnpm run pr-resolve -- 42 # explicit PR number
73
- pnpm run pr-resolve -- 42 --dry-run # preview what would be resolved
74
- pnpm run pr-resolve -- 42 --tag-agents # resolve + post @mention comment
75
- pnpm run pr-resolve -- 42 --tag-agents --comment "Fixed in abc123"
76
- pnpm run pr-resolve -- 42 --unresolve # re-open threads if needed
70
+ pr-resolve 42 # resolve all threads from last scrape
71
+ pr-resolve 42 --auto # smart resolve: addressed threads only
72
+ pr-resolve 42 --auto --dry-run # preview what would be resolved
73
+ pr-resolve 42 --auto --tag-agents # resolve + tag agents for re-review
74
+ pr-resolve 42 --tag-agents --comment "msg" # custom message
75
+ pr-resolve 42 --unresolve # re-open resolved threads
77
76
  ```
78
77
 
79
- ---
78
+ **`--auto` detection signals:**
79
+ - `isOutdated` — GitHub detected the commented lines were changed
80
+ - Suggestion applied — `` ```suggestion `` block content found in current file at HEAD
80
81
 
81
- ## Output
82
-
83
- | File | Description |
84
- |---|---|
85
- | `pr-reviews/new-<timestamp>.md` | New actionable comments since last run |
86
- | `pr-reviews/.scraped-ids.json` | Persistent ID cache — **commit this file** |
87
- | `pr-reviews/.threads-<pr>.json` | Thread IDs consumed by `pr-resolve` |
82
+ Skips threads with no signal. Posts a summary comment on the PR.
88
83
 
89
84
  ---
90
85
 
91
- ## The AI Agent Loop
86
+ ## The Agent Loop
92
87
 
93
88
  ```
94
- 1. pnpm run pr-review -- <PR number>
95
- → pr-reviews/new-<timestamp>.md (new comments only)
96
- → pr-reviews/.threads-<pr>.json (thread IDs for resolve)
89
+ 1. pr-review 42
90
+ → pr-reviews/new-<timestamp>.md (new comments only)
97
91
 
98
- 2. Agent reads the markdown, implements fixes, commits, pushes
92
+ 2. Agent reads markdown, implements fixes, commits, pushes
99
93
 
100
- 3. pnpm run pr-resolve -- <PR number> --tag-agents
101
- Resolves all handled threads via GitHub GraphQL
102
- → Posts: "All feedback addressed. @cubic-dev-ai please re-review."
94
+ 3. pr-resolve 42 --auto --tag-agents
95
+ Auto-resolves addressed threads
96
+ → Posts: "Auto-resolved 6/8 threads. 2 remain."
97
+ → Tags agents for re-review
103
98
 
104
- 4. Repeat from step 1 — only new reviewer replies appear
99
+ 4. Repeat — only new replies appear
105
100
  ```
106
101
 
107
102
  ---
108
103
 
109
- ## Config (`.pr-prism.json`)
104
+ ## Output Files
105
+
106
+ | File | Description |
107
+ |---|---|
108
+ | `pr-reviews/new-<timestamp>.md` | New actionable comments since last run |
109
+ | `pr-reviews/.scraped-ids.json` | Persistent ID cache — **commit this** |
110
+ | `pr-reviews/.threads-<pr>.json` | Thread IDs for `pr-resolve` |
111
+
112
+ ---
110
113
 
111
- Create `.pr-prism.json` in your repo root to customise agent mentions without editing source:
114
+ ## Config
115
+
116
+ Copy `.pr-prism.example.json` to `.pr-prism.json` in your repo root:
112
117
 
113
118
  ```json
114
119
  {
@@ -116,28 +121,26 @@ Create `.pr-prism.json` in your repo root to customise agent mentions without ed
116
121
  }
117
122
  ```
118
123
 
119
- ---
124
+ `agentMentions` — GitHub handles (without `@`) tagged in the post-resolve comment. Defaults to `["cubic-dev-ai"]`.
120
125
 
121
- ## Extending the bot list
126
+ ---
122
127
 
123
- Edit `KNOWN_BOTS` at the top of `scripts/scrape-pr-reviews.ts`:
128
+ ## What Gets Filtered
124
129
 
125
- ```ts
126
- const KNOWN_BOTS = ["github-actions", "dependabot", "coderabbitai", "changeset-bot", "codeantai"];
127
- ```
130
+ | Signal | Behaviour |
131
+ |---|---|
132
+ | Already-seen comment ID | Silently skipped |
133
+ | Resolved thread | Skipped + cached |
134
+ | Bot author (`[bot]`, `KNOWN_BOTS`) | Skipped + cached |
135
+ | Outdated thread | Shown with `⚠️ OUTDATED` warning |
136
+ | Social sharing links | Stripped from body |
137
+ | `` ```suggestion `` blocks | Rendered as `diff` |
128
138
 
129
- ## Adding noise domains
139
+ ---
130
140
 
131
- Edit `NOISE_DOMAINS` in the same file:
141
+ ## Contributing
132
142
 
133
- ```ts
134
- const NOISE_DOMAINS = [
135
- "twitter.com/intent", "x.com/intent",
136
- "reddit.com/submit",
137
- "linkedin.com/sharing",
138
- "app.codeant.ai", "codeant.ai/feedback",
139
- ];
140
- ```
143
+ PRs and issues welcome. Fork the repo, create a feature branch, and open a pull request.
141
144
 
142
145
  ---
143
146
 
@@ -32,6 +32,9 @@ pnpm run pr-review -- https://github.com/owner/repo/pull/42
32
32
 
33
33
  # Resolve handled threads + tag agents
34
34
  pnpm run pr-resolve -- 42 # resolve all threads from last scrape
35
+ pnpm run pr-resolve -- 42 --auto # smart auto-resolve (detects addressed threads)
36
+ pnpm run pr-resolve -- 42 --auto --dry-run # preview auto-resolve classifications
37
+ pnpm run pr-resolve -- 42 --auto --tag-agents # auto-resolve + tag agents
35
38
  pnpm run pr-resolve -- 42 --dry-run # preview without mutating
36
39
  pnpm run pr-resolve -- 42 --tag-agents # resolve + post @mention comment
37
40
  pnpm run pr-resolve -- 42 --tag-agents --comment "Fixed in abc123"
@@ -69,9 +72,11 @@ pnpm run pr-resolve -- 42 --unresolve # re-open threads if needed
69
72
  1. pnpm run pr-review -- <PR number>
70
73
  2. Agent reads pr-reviews/new-<timestamp>.md
71
74
  3. Agent implements fixes, commits, pushes
72
- 4. pnpm run pr-resolve -- <PR number> --tag-agents
73
- Resolves handled threads via GraphQL
74
- Posts comment tagging AI agents for re-review
75
+ 4. pnpm run pr-resolve -- <PR number> --auto --tag-agents
76
+ Re-fetches live thread state from GitHub
77
+ Auto-resolves: isOutdated threads + matched suggestions
78
+ → Posts: "Auto-resolved 6/8 threads. 2 remain."
79
+ → Tags agents for re-review
75
80
  5. Repeat from step 1 — only new reviewer replies appear
76
81
  ```
77
82
 
@@ -0,0 +1,166 @@
1
+ # Auto-Resolve Design — GitHub Signals + Suggestion Matching
2
+
3
+ **Date**: 2026-03-14
4
+ **Status**: Approved
5
+ **Approach**: B (GitHub-native signals + suggestion block comparison)
6
+
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ `pr-resolve` resolves ALL threads from the sidecar indiscriminately. Agents must manually invoke it after pushing fixes, and every thread gets resolved regardless of whether the underlying comment was actually addressed. This creates noise — reviewers see resolved threads that weren't truly fixed, eroding trust.
12
+
13
+ ## Solution
14
+
15
+ Extend `pr-resolve` with an `--auto` flag that re-fetches live thread state from GitHub, classifies each thread by confidence signals, and resolves only threads with strong evidence of being addressed. Posts a transparent summary comment on the PR.
16
+
17
+ ## Detection Signals
18
+
19
+ ### Signal 1: `isOutdated` (GitHub-native)
20
+
21
+ GitHub automatically sets `isOutdated: true` on a `PullRequestReviewThread` when the exact lines the comment refers to are modified by a subsequent commit. This is the primary, highest-confidence signal.
22
+
23
+ Additionally, when `line: null` on a thread (or `position: null` via REST), the diff can no longer anchor the comment — the code has changed enough that the comment's location is gone.
24
+
25
+ ### Signal 2: Suggestion Matching
26
+
27
+ Review comments may contain ` ```suggestion ` blocks — explicit code change requests. After the agent pushes, fetch the current file content at HEAD and check if the suggested code is now present in the file. If all suggestion lines match, the comment was applied.
28
+
29
+ ## Classification Buckets
30
+
31
+ | Bucket | Signal | Action |
32
+ |---|---|---|
33
+ | **auto-resolve** | `isOutdated: true` OR `line: null` | Resolve + tag as "lines changed" |
34
+ | **auto-resolve** | Has suggestion block AND content matches current file | Resolve + tag as "suggestion applied" |
35
+ | **skip** | None of the above | Leave unresolved, report count |
36
+
37
+ ## Extended GraphQL Query
38
+
39
+ ```graphql
40
+ query($owner: String!, $repo: String!, $prNumber: Int!) {
41
+ repository(owner: $owner, name: $repo) {
42
+ pullRequest(number: $prNumber) {
43
+ headRefOid
44
+ reviewThreads(first: 100) {
45
+ nodes {
46
+ id isResolved isOutdated
47
+ path line originalLine
48
+ comments(first: 20) {
49
+ nodes {
50
+ databaseId
51
+ author { login }
52
+ body
53
+ path
54
+ outdated
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ Key additions vs current query: `headRefOid`, `path`/`line`/`originalLine` on thread, `outdated` on comment.
65
+
66
+ ## Suggestion Matching Algorithm
67
+
68
+ 1. Extract suggestion content from comment body using existing regex: `` /```suggestion\n([\s\S]*?)```/g ``
69
+ 2. Fetch file at HEAD: `gh api repos/{owner}/{repo}/contents/{path}?ref={headSha}` — base64 decode response
70
+ 3. Normalize both suggestion lines and file lines (trim whitespace, collapse spaces)
71
+ 4. Check if suggestion lines appear as a contiguous block in the file
72
+ 5. If ALL suggestion blocks in a comment match → thread is addressed
73
+
74
+ ### Edge cases
75
+
76
+ - **Multiple suggestion blocks in one comment**: All must match for auto-resolve
77
+ - **Whitespace differences**: Normalize before comparing (trim, collapse)
78
+ - **File deleted**: Treat as addressed (the code was removed)
79
+ - **Partial application**: NOT handled — if agent applied 3 of 5 suggested lines, thread is not auto-resolved (conservative)
80
+ - **Reformatted suggestions**: NOT handled — exact content match only (conservative)
81
+
82
+ ## Command Interface
83
+
84
+ ```bash
85
+ # Existing (unchanged) — resolve ALL threads from sidecar
86
+ pnpm run pr-resolve -- 42
87
+
88
+ # NEW — smart auto-resolve
89
+ pnpm run pr-resolve -- 42 --auto
90
+
91
+ # Combine with existing flags
92
+ pnpm run pr-resolve -- 42 --auto --tag-agents
93
+ pnpm run pr-resolve -- 42 --auto --dry-run
94
+ pnpm run pr-resolve -- 42 --auto --tag-agents --comment "Fixes pushed"
95
+ ```
96
+
97
+ When `--auto` is used:
98
+ - Sidecar file is **not required** — threads fetched directly from GitHub
99
+ - If sidecar exists, used as hint but GitHub's live state takes precedence
100
+ - `--dry-run` shows classification without mutating
101
+
102
+ ## Summary Comment Format
103
+
104
+ Posted on the PR after auto-resolution:
105
+
106
+ ```
107
+ :robot: **pr-prism auto-resolve** — 6 of 8 threads resolved
108
+
109
+ **Auto-resolved (lines changed):** 4 threads
110
+ **Auto-resolved (suggestion applied):** 2 threads
111
+ **Remaining (needs manual review):** 2 threads
112
+ ```
113
+
114
+ When combined with `--tag-agents`, the agent mentions are appended to this comment instead of the existing default message.
115
+
116
+ ## Data Flow
117
+
118
+ ```
119
+ Agent pushes fixes
120
+ |
121
+ v
122
+ pr-resolve -- 42 --auto --tag-agents
123
+ |
124
+ +---> Fetch live threads from GitHub GraphQL
125
+ | (isOutdated, line, path, comments.body, headRefOid)
126
+ |
127
+ +---> For each unresolved thread:
128
+ | +-- isOutdated=true OR line=null?
129
+ | | -> BUCKET: auto-resolve (lines changed)
130
+ | +-- Has suggestion block?
131
+ | | -> Fetch file at HEAD, normalize, compare
132
+ | | -> match? -> BUCKET: auto-resolve (suggestion applied)
133
+ | +-- Neither?
134
+ | -> BUCKET: skip
135
+ |
136
+ +---> Execute resolveReviewThread mutations for auto-resolve bucket
137
+ |
138
+ +---> Post summary comment on PR
139
+ |
140
+ +---> Tag agents if --tag-agents
141
+ ```
142
+
143
+ ## File Changes
144
+
145
+ | File | Change |
146
+ |---|---|
147
+ | `scripts/resolve-pr-threads.ts` | Add `--auto` flag, extended GraphQL query for auto mode, `classifyThreads()` function, `extractSuggestions()` function, `fetchFileAtHead()` function, `matchSuggestion()` function, summary comment logic |
148
+
149
+ No new files. No new dependencies. All logic fits in the existing resolve script (~80-100 lines added).
150
+
151
+ ## Updated Agent Loop
152
+
153
+ ```
154
+ 1. pnpm run pr-review -- <PR number>
155
+ -> pr-reviews/new-<timestamp>.md (new comments only)
156
+
157
+ 2. Agent reads the markdown, implements fixes, commits, pushes
158
+
159
+ 3. pnpm run pr-resolve -- <PR number> --auto --tag-agents
160
+ -> Re-fetches live thread state from GitHub
161
+ -> Auto-resolves: isOutdated threads + matched suggestions
162
+ -> Posts: "Auto-resolved 6/8 threads. 2 remain."
163
+ -> Tags agents for re-review
164
+
165
+ 4. Repeat from step 1 — only new reviewer replies appear
166
+ ```