ghreview 1.0.0 → 2.0.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.
@@ -3,14 +3,18 @@ updates:
3
3
  - package-ecosystem: "github-actions"
4
4
  directory: "/"
5
5
  schedule:
6
- interval: "daily"
6
+ interval: "weekly"
7
7
  commit-message:
8
8
  prefix: "chore"
9
9
  include: "scope"
10
+ cooldown:
11
+ default-days: 5
10
12
  - package-ecosystem: "npm"
11
13
  directory: "/"
12
14
  schedule:
13
- interval: "daily"
15
+ interval: "weekly"
14
16
  commit-message:
15
17
  prefix: "chore"
16
18
  include: "scope"
19
+ cooldown:
20
+ default-days: 5
@@ -5,14 +5,14 @@ jobs:
5
5
  strategy:
6
6
  fail-fast: false
7
7
  matrix:
8
- node: [22.x, lts/*, current]
8
+ node: [lts/*, current]
9
9
  os: [macos-latest, ubuntu-latest, windows-latest]
10
10
  runs-on: ${{ matrix.os }}
11
11
  steps:
12
12
  - name: Checkout Repository
13
- uses: actions/checkout@v4.2.2
13
+ uses: actions/checkout@v6.0.1
14
14
  - name: Use Node.js ${{ matrix.node }}
15
- uses: actions/setup-node@v4.4.0
15
+ uses: actions/setup-node@v6.1.0
16
16
  with:
17
17
  node-version: ${{ matrix.node }}
18
18
  - name: Install Dependencies
@@ -27,24 +27,26 @@ jobs:
27
27
  needs: test
28
28
  runs-on: ubuntu-latest
29
29
  if: github.event_name == 'push' && github.ref == 'refs/heads/master'
30
+ permissions:
31
+ contents: write
32
+ issues: write
33
+ pull-requests: write
34
+ id-token: write
30
35
  steps:
31
36
  - name: Checkout
32
- uses: actions/checkout@v4.2.2
37
+ uses: actions/checkout@v6.0.1
33
38
  with:
34
39
  fetch-depth: 0
35
40
  - name: Setup Node.js
36
- uses: actions/setup-node@v4.4.0
41
+ uses: actions/setup-node@v6.1.0
37
42
  with:
38
43
  node-version: lts/*
39
44
  - name: Install dependencies
40
45
  run: |
41
46
  npm install --no-progress --no-save
42
- - name: Build
43
- run: |
44
- npm run build
45
47
  - name: Release
46
48
  env:
47
49
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
50
+ NPM_CONFIG_PROVENANCE: true
49
51
  run: npx semantic-release
50
52
 
package/CHANGELOG.md ADDED
@@ -0,0 +1,41 @@
1
+ ## [2.0.0](https://github.com/rvagg/ghreview/compare/v1.0.1...v2.0.0) (2026-01-24)
2
+
3
+ ### ⚠ BREAKING CHANGES
4
+
5
+ * Config location changed from ~/.ghreview/config.json
6
+ to XDG paths (~/.config/ghreview/config.json on Linux). Move your
7
+ existing config to the new location.
8
+
9
+ ### Features
10
+
11
+ * add interactive auth and filter resolved comments ([29a8196](https://github.com/rvagg/ghreview/commit/29a81961b5c9ee636552d048f96cc9d59c7ace81))
12
+
13
+ ### Trivial Changes
14
+
15
+ * **ci:** oidc publishing ([4cef06b](https://github.com/rvagg/ghreview/commit/4cef06b9f64ec067b0363fc230babca74e224395))
16
+ * **deps-dev:** bump @semantic-release/github from 11.0.6 to 12.0.0 ([cef9450](https://github.com/rvagg/ghreview/commit/cef94502b1d00ced4c653b67b0af7b6ff350948b))
17
+ * **deps-dev:** bump @semantic-release/npm from 12.0.2 to 13.1.1 ([c63e419](https://github.com/rvagg/ghreview/commit/c63e419188b913ed866ddf48b7bbbe4c2d2b8f00))
18
+ * **deps-dev:** bump nock from 14.0.7 to 14.0.10 ([a7c98df](https://github.com/rvagg/ghreview/commit/a7c98dfe7840fbd26a5c9c81ed21acd5bfcf7240))
19
+ * **deps:** bump actions/checkout from 4.2.2 to 6.0.1 ([170b48c](https://github.com/rvagg/ghreview/commit/170b48c47bfd6a601037d362b9c3b4e084a83d05))
20
+ * **deps:** bump actions/setup-node from 4.4.0 to 6.1.0 ([61ce2d9](https://github.com/rvagg/ghreview/commit/61ce2d9f083b83562d139de8bcfba985c9017a4a))
21
+ * **deps:** bump chalk from 5.4.1 to 5.6.2 ([948fa9d](https://github.com/rvagg/ghreview/commit/948fa9d78c11b527c4483276e7426b6207084751))
22
+ * **deps:** bump ora from 8.2.0 to 9.0.0 ([8dff5ca](https://github.com/rvagg/ghreview/commit/8dff5ca3d61eb056739422b647d8e7c260967924))
23
+ * fix dependabot config ([4d2b670](https://github.com/rvagg/ghreview/commit/4d2b670cda6b6c975d760ec822f0ce2ca1b337cd))
24
+
25
+ ## [1.0.1](https://github.com/rvagg/ghreview/compare/v1.0.0...v1.0.1) (2025-07-28)
26
+
27
+ ### Trivial Changes
28
+
29
+ * bump ([ed0f6d9](https://github.com/rvagg/ghreview/commit/ed0f6d9b16c52566b1238f4b5ccdc362f5660d82))
30
+
31
+ ## 1.0.0 (2025-07-28)
32
+
33
+ ### Bug Fixes
34
+
35
+ * publish ([b405eb4](https://github.com/rvagg/ghreview/commit/b405eb4cff0d13e856da0aeee895d7601d9f3763))
36
+ * update publish perms ([62009ef](https://github.com/rvagg/ghreview/commit/62009ef718d06664efb3ac5a001e5b84fe5364a0))
37
+
38
+ ### Trivial Changes
39
+
40
+ * **docs:** expand workflow description ([5dd5671](https://github.com/rvagg/ghreview/commit/5dd5671f455dfa83bda88a1f8e0fab2fa9d836af))
41
+ * initial release ([2a1891d](https://github.com/rvagg/ghreview/commit/2a1891d9c7661f3bb761a8d2dfa36da1287fcea3))
package/README.md CHANGED
@@ -1,10 +1,28 @@
1
1
  # ghreview
2
2
 
3
+ [![NPM](https://nodei.co/npm/ghreview.svg?style=flat&data=n,v)](https://nodei.co/npm/ghreview/)
4
+
3
5
  GitHub PR-based code review workflow for AI-assisted development.
4
6
 
5
7
  ## Overview
6
8
 
7
- `ghreview` enables you to use GitHub's pull request review interface to review uncommitted code changes, particularly useful when working with AI coding assistants. It pushes your current state and uncommitted changes as a PR, lets you review and comment, then collects that feedback in a format suitable for AI consumption.
9
+ `ghreview` bridges the gap between AI-assisted coding and GitHub's superior code review interface. It allows you to review AI-generated code changes using GitHub's familiar PR review tools while keeping your local repository pristine.
10
+
11
+ ### Why?
12
+
13
+ - **Clean local state**: Your working directory remains exactly as it was - no commits, no branch switches, no git operations visible in your project
14
+ - **Familiar workflow**: Use GitHub's excellent PR review interface with inline comments, suggestions, and discussions
15
+ - **Private reviews**: Use a dedicated private repository for reviews, keeping your AI interactions separate from your main project
16
+ - **Zero footprint**: Creates temporary branches and commits in a separate review repository, immediately cleaning up after pushing
17
+ - **AI-friendly output**: Collects all feedback in a structured format that's easy to paste back to your AI assistant
18
+
19
+ ### How?
20
+
21
+ 1. You work with your AI assistant and have uncommitted changes in your project
22
+ 2. `ghreview init` pushes your current state to a dedicated review repository without affecting your local git state
23
+ 3. You review the changes on GitHub using all the familiar tools
24
+ 4. `ghreview collect` gathers your feedback in a format optimized for AI consumption
25
+ 5. Your local repository remains untouched throughout - as if git was never involved
8
26
 
9
27
  ## Quick Start
10
28
 
@@ -182,4 +200,4 @@ npm test
182
200
 
183
201
  ## License
184
202
 
185
- Apache-2.0
203
+ Apache-2.0
package/lib/config.js CHANGED
@@ -1,42 +1,89 @@
1
1
  import fs from 'fs/promises'
2
2
  import path from 'path'
3
3
  import os from 'os'
4
+ import ghauth from 'ghauth'
5
+ import { read } from 'read'
4
6
 
5
- const CONFIG_DIR = path.join(os.homedir(), '.ghreview')
6
- const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json')
7
+ function getConfigPath () {
8
+ const home = os.homedir()
9
+ switch (os.platform()) {
10
+ case 'darwin':
11
+ return path.join(home, 'Library', 'Application Support', 'ghreview', 'config.json')
12
+ case 'win32':
13
+ return path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'ghreview', 'config.json')
14
+ default:
15
+ return path.join(process.env.XDG_CONFIG_HOME || path.join(home, '.config'), 'ghreview', 'config.json')
16
+ }
17
+ }
7
18
 
8
- export async function loadConfig () {
19
+ const CONFIG_FILE = getConfigPath()
20
+ const CONFIG_DIR = path.dirname(CONFIG_FILE)
21
+
22
+ async function readConfig () {
9
23
  try {
10
24
  const configData = await fs.readFile(CONFIG_FILE, 'utf8')
11
- const config = JSON.parse(configData)
12
-
13
- // Validate required fields
14
- if (!config.reviewRepo) {
15
- throw new Error('Missing required config field: reviewRepo')
25
+ return JSON.parse(configData)
26
+ } catch (error) {
27
+ if (error.code === 'ENOENT') {
28
+ return {}
16
29
  }
30
+ throw error
31
+ }
32
+ }
17
33
 
18
- if (!config.githubToken) {
19
- throw new Error('Missing required config field: githubToken\n\nTo create a GitHub token:\n1. Go to https://github.com/settings/tokens/new\n2. Give it a name (e.g., "ghreview")\n3. Select scopes: "repo" (Full control of private repositories)\n4. Click "Generate token"\n5. Copy the token and add it to your config file')
20
- }
34
+ async function writeConfig (config) {
35
+ await fs.mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 })
36
+ await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 })
37
+ }
21
38
 
22
- // Validate repo format
23
- if (!config.reviewRepo.includes('/')) {
24
- throw new Error('Invalid reviewRepo format. Expected: owner/repo')
25
- }
39
+ export async function loadConfig () {
40
+ const config = await readConfig()
41
+ let configChanged = false
26
42
 
27
- return config
28
- } catch (error) {
29
- if (error.code === 'ENOENT') {
30
- throw new Error(`Config file not found at ${CONFIG_FILE}\nCreate it with: {"reviewRepo": "owner/repo", "githubToken": "your_github_token"}`)
43
+ // Check if we need to acquire a token
44
+ if (!config.githubToken) {
45
+ console.log('GitHub authentication required.\n')
46
+ const authData = await ghauth({
47
+ configName: 'ghreview',
48
+ scopes: ['repo'],
49
+ noSave: true,
50
+ noDeviceFlow: true
51
+ })
52
+ config.githubToken = authData.token
53
+ config.user = authData.user
54
+ configChanged = true
55
+ }
56
+
57
+ // Check if we need a reviewRepo
58
+ if (!config.reviewRepo) {
59
+ const defaultRepo = config.user ? `${config.user}/reviews` : null
60
+ const prompt = defaultRepo
61
+ ? `Review repository (owner/repo) [${defaultRepo}]: `
62
+ : 'Review repository (owner/repo): '
63
+
64
+ const input = await read({ prompt })
65
+ config.reviewRepo = input || defaultRepo
66
+
67
+ if (!config.reviewRepo) {
68
+ throw new Error('Review repository is required')
31
69
  }
32
- throw error
70
+ configChanged = true
33
71
  }
72
+
73
+ // Validate repo format
74
+ if (!config.reviewRepo.includes('/')) {
75
+ throw new Error('Invalid reviewRepo format. Expected: owner/repo')
76
+ }
77
+
78
+ // Save config if anything changed
79
+ if (configChanged) {
80
+ await writeConfig(config)
81
+ console.log(`\nConfig saved to ${CONFIG_FILE}\n`)
82
+ }
83
+
84
+ return config
34
85
  }
35
86
 
36
87
  export async function ensureConfigDir () {
37
- try {
38
- await fs.mkdir(CONFIG_DIR, { recursive: true })
39
- } catch (error) {
40
- // Directory might already exist, that's fine
41
- }
88
+ await fs.mkdir(CONFIG_DIR, { recursive: true })
42
89
  }
package/lib/github.js CHANGED
@@ -60,12 +60,73 @@ All comments will be collected with line numbers and context for AI consumption.
60
60
  export async function fetchReviewComments (owner, repo, prNumber, token) {
61
61
  const octokit = createOctokit(token)
62
62
 
63
- // Fetch inline comments
64
- const { data: comments } = await octokit.rest.pulls.listReviewComments({
65
- owner,
66
- repo,
67
- pull_number: prNumber
68
- })
63
+ // Use GraphQL to fetch review threads with resolution status
64
+ const query = `
65
+ query($owner: String!, $repo: String!, $prNumber: Int!, $cursor: String) {
66
+ repository(owner: $owner, name: $repo) {
67
+ pullRequest(number: $prNumber) {
68
+ reviewThreads(first: 100, after: $cursor) {
69
+ pageInfo {
70
+ hasNextPage
71
+ endCursor
72
+ }
73
+ nodes {
74
+ isResolved
75
+ isOutdated
76
+ path
77
+ line
78
+ startLine
79
+ comments(first: 100) {
80
+ nodes {
81
+ id
82
+ body
83
+ path
84
+ line: originalLine
85
+ startLine: originalStartLine
86
+ position: originalPosition
87
+ user: author {
88
+ login
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ `
98
+
99
+ // Fetch all review threads with pagination
100
+ const allThreads = []
101
+ let cursor = null
102
+ let hasNextPage = true
103
+
104
+ while (hasNextPage) {
105
+ const result = await octokit.graphql(query, {
106
+ owner,
107
+ repo,
108
+ prNumber,
109
+ cursor
110
+ })
111
+
112
+ const threads = result.repository.pullRequest.reviewThreads
113
+ allThreads.push(...threads.nodes)
114
+ hasNextPage = threads.pageInfo.hasNextPage
115
+ cursor = threads.pageInfo.endCursor
116
+ }
117
+
118
+ // Filter to only unresolved threads and flatten comments
119
+ const unresolvedComments = allThreads
120
+ .filter(thread => !thread.isResolved)
121
+ .flatMap(thread => thread.comments.nodes.map(comment => ({
122
+ ...comment,
123
+ path: thread.path,
124
+ line: comment.line || thread.line,
125
+ start_line: comment.startLine || thread.startLine,
126
+ original_line: comment.line || thread.line,
127
+ original_start_line: comment.startLine || thread.startLine,
128
+ position: comment.position
129
+ })))
69
130
 
70
131
  // Fetch PR reviews (general comments)
71
132
  const { data: reviews } = await octokit.rest.pulls.listReviews({
@@ -82,7 +143,7 @@ export async function fetchReviewComments (owner, repo, prNumber, token) {
82
143
  })
83
144
 
84
145
  return {
85
- inline: comments,
146
+ inline: unresolvedComments,
86
147
  reviews: reviews.filter(r => r.body), // Only reviews with comments
87
148
  discussion: issueComments
88
149
  }
@@ -93,10 +154,11 @@ export function formatFeedback (comments, filterBots = true) {
93
154
 
94
155
  // Filter out bot comments if requested
95
156
  const isBot = (comment) => {
157
+ const login = comment.user?.login || comment.author?.login
96
158
  return filterBots && (
97
159
  comment.user?.type === 'Bot' ||
98
- comment.user?.login?.includes('[bot]') ||
99
- comment.user?.login?.includes('-bot')
160
+ login?.includes('[bot]') ||
161
+ login?.includes('-bot')
100
162
  )
101
163
  }
102
164
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghreview",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "GitHub PR-based code review workflow for AI-assisted development",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -25,8 +25,9 @@
25
25
  "license": "Apache-2.0",
26
26
  "dependencies": {
27
27
  "@octokit/rest": "^22.0.0",
28
- "chalk": "^5.4.1",
29
- "ora": "^8.2.0",
28
+ "chalk": "^5.6.2",
29
+ "ghauth": "^7.0.0",
30
+ "ora": "^9.0.0",
30
31
  "simple-git": "^3.28.0",
31
32
  "yargs": "^18.0.0"
32
33
  },
@@ -34,11 +35,11 @@
34
35
  "@semantic-release/changelog": "^6.0.3",
35
36
  "@semantic-release/commit-analyzer": "^13.0.1",
36
37
  "@semantic-release/git": "^10.0.1",
37
- "@semantic-release/github": "^11.0.3",
38
- "@semantic-release/npm": "^12.0.2",
38
+ "@semantic-release/github": "^12.0.0",
39
+ "@semantic-release/npm": "^13.1.1",
39
40
  "@semantic-release/release-notes-generator": "^14.0.3",
40
41
  "conventional-changelog-conventionalcommits": "^9.0.0",
41
- "nock": "^14.0.7",
42
+ "nock": "^14.0.10",
42
43
  "standard": "^17.1.2",
43
44
  "vitest": "^3.2.4"
44
45
  },
@@ -1,10 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(npm run lint)",
5
- "Bash(npm test)",
6
- "Bash(npm run lint:*)"
7
- ],
8
- "deny": []
9
- }
10
- }