ghreview 1.0.1 → 2.0.1

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
@@ -31,13 +31,14 @@ jobs:
31
31
  contents: write
32
32
  issues: write
33
33
  pull-requests: write
34
+ id-token: write
34
35
  steps:
35
36
  - name: Checkout
36
- uses: actions/checkout@v4.2.2
37
+ uses: actions/checkout@v6.0.1
37
38
  with:
38
39
  fetch-depth: 0
39
40
  - name: Setup Node.js
40
- uses: actions/setup-node@v4.4.0
41
+ uses: actions/setup-node@v6.1.0
41
42
  with:
42
43
  node-version: lts/*
43
44
  - name: Install dependencies
@@ -46,6 +47,6 @@ jobs:
46
47
  - name: Release
47
48
  env:
48
49
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
49
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
50
+ NPM_CONFIG_PROVENANCE: true
50
51
  run: npx semantic-release
51
52
 
package/CHANGELOG.md CHANGED
@@ -1,3 +1,33 @@
1
+ ## [2.0.1](https://github.com/rvagg/ghreview/compare/v2.0.0...v2.0.1) (2026-01-24)
2
+
3
+ ### Trivial Changes
4
+
5
+ * **deps-dev:** bump nock from 14.0.7 to 14.0.10 ([#5](https://github.com/rvagg/ghreview/issues/5)) ([b979aba](https://github.com/rvagg/ghreview/commit/b979abaad70a259a0d01eb6d8b89eeb0a7707dc6))
6
+
7
+ ## [2.0.0](https://github.com/rvagg/ghreview/compare/v1.0.1...v2.0.0) (2026-01-24)
8
+
9
+ ### ⚠ BREAKING CHANGES
10
+
11
+ * Config location changed from ~/.ghreview/config.json
12
+ to XDG paths (~/.config/ghreview/config.json on Linux). Move your
13
+ existing config to the new location.
14
+
15
+ ### Features
16
+
17
+ * add interactive auth and filter resolved comments ([29a8196](https://github.com/rvagg/ghreview/commit/29a81961b5c9ee636552d048f96cc9d59c7ace81))
18
+
19
+ ### Trivial Changes
20
+
21
+ * **ci:** oidc publishing ([4cef06b](https://github.com/rvagg/ghreview/commit/4cef06b9f64ec067b0363fc230babca74e224395))
22
+ * **deps-dev:** bump @semantic-release/github from 11.0.6 to 12.0.0 ([cef9450](https://github.com/rvagg/ghreview/commit/cef94502b1d00ced4c653b67b0af7b6ff350948b))
23
+ * **deps-dev:** bump @semantic-release/npm from 12.0.2 to 13.1.1 ([c63e419](https://github.com/rvagg/ghreview/commit/c63e419188b913ed866ddf48b7bbbe4c2d2b8f00))
24
+ * **deps-dev:** bump nock from 14.0.7 to 14.0.10 ([a7c98df](https://github.com/rvagg/ghreview/commit/a7c98dfe7840fbd26a5c9c81ed21acd5bfcf7240))
25
+ * **deps:** bump actions/checkout from 4.2.2 to 6.0.1 ([170b48c](https://github.com/rvagg/ghreview/commit/170b48c47bfd6a601037d362b9c3b4e084a83d05))
26
+ * **deps:** bump actions/setup-node from 4.4.0 to 6.1.0 ([61ce2d9](https://github.com/rvagg/ghreview/commit/61ce2d9f083b83562d139de8bcfba985c9017a4a))
27
+ * **deps:** bump chalk from 5.4.1 to 5.6.2 ([948fa9d](https://github.com/rvagg/ghreview/commit/948fa9d78c11b527c4483276e7426b6207084751))
28
+ * **deps:** bump ora from 8.2.0 to 9.0.0 ([8dff5ca](https://github.com/rvagg/ghreview/commit/8dff5ca3d61eb056739422b647d8e7c260967924))
29
+ * fix dependabot config ([4d2b670](https://github.com/rvagg/ghreview/commit/4d2b670cda6b6c975d760ec822f0ce2ca1b337cd))
30
+
1
31
  ## [1.0.1](https://github.com/rvagg/ghreview/compare/v1.0.0...v1.0.1) (2025-07-28)
2
32
 
3
33
  ### Trivial Changes
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.1",
3
+ "version": "2.0.1",
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
  },