ghreview 2.0.3 → 3.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## [3.0.0](https://github.com/rvagg/ghreview/compare/v2.0.4...v3.0.0) (2026-02-07)
2
+
3
+ ### ⚠ BREAKING CHANGES
4
+
5
+ * only include tracked files in review by default
6
+
7
+ ### Features
8
+
9
+ * only include tracked files in review by default ([8e20211](https://github.com/rvagg/ghreview/commit/8e20211e48c12ab42e7e170f48478e881930f04d))
10
+ * preserve staging state across review cycle ([6fe2429](https://github.com/rvagg/ghreview/commit/6fe242947623fc4fef3f8e75b1bc7b6baaf60463))
11
+
12
+ ## [2.0.4](https://github.com/rvagg/ghreview/compare/v2.0.3...v2.0.4) (2026-02-02)
13
+
14
+ ### Trivial Changes
15
+
16
+ * **deps-dev:** bump vitest from 4.0.17 to 4.0.18 ([#27](https://github.com/rvagg/ghreview/issues/27)) ([116ab54](https://github.com/rvagg/ghreview/commit/116ab54f511e5f2089b643df9eb8b27150c9a977))
17
+
1
18
  ## [2.0.3](https://github.com/rvagg/ghreview/compare/v2.0.2...v2.0.3) (2026-01-27)
2
19
 
3
20
  ### Trivial Changes
package/bin/ghreview.js CHANGED
@@ -18,9 +18,16 @@ process.on('SIGTERM', () => {
18
18
  yargs(hideBin(process.argv))
19
19
  .scriptName('ghreview')
20
20
  .usage('$0 <command> [args]')
21
- .command('init', 'Create a PR for review', {}, async () => {
21
+ .command('init', 'Create a PR for review', (yargs) => {
22
+ return yargs.option('all', {
23
+ alias: 'a',
24
+ type: 'boolean',
25
+ default: false,
26
+ describe: 'Include untracked files (by default, only tracked and staged files are included)'
27
+ })
28
+ }, async (argv) => {
22
29
  try {
23
- await init()
30
+ await init({ all: argv.all })
24
31
  } catch (error) {
25
32
  console.error(chalk.red('Error:'), error.message)
26
33
  process.exit(1)
package/lib/git.js CHANGED
@@ -10,17 +10,25 @@ export async function getCurrentBranch () {
10
10
  }
11
11
  }
12
12
 
13
- export async function hasUnstagedChanges () {
13
+ export async function hasReviewableChanges ({ all = false } = {}) {
14
14
  const git = simpleGit()
15
15
  const status = await git.status()
16
- return status.files.length > 0
16
+ if (all) {
17
+ return status.files.length > 0
18
+ }
19
+ // Exclude untracked files — user must `git add` new files explicitly
20
+ return status.files.some(f => !(f.index === '?' && f.working_dir === '?'))
17
21
  }
18
22
 
19
- export async function createReviewCommit (message, authorConfig) {
23
+ export async function createReviewCommit (message, authorConfig, { all = false } = {}) {
20
24
  const git = simpleGit()
21
25
 
22
- // Add all changes
23
- await git.add('.')
26
+ if (all) {
27
+ await git.add('.')
28
+ } else {
29
+ // Only stage tracked file modifications; new files require explicit `git add`
30
+ await git.add(['-u'])
31
+ }
24
32
 
25
33
  // Set up environment for custom author if provided
26
34
  const env = {}
@@ -44,6 +52,17 @@ export async function pushBranch (remote, branch) {
44
52
  }
45
53
  }
46
54
 
55
+ export async function saveIndex () {
56
+ const git = simpleGit()
57
+ const treeHash = await git.raw(['write-tree'])
58
+ return treeHash.trim()
59
+ }
60
+
61
+ export async function restoreIndex (treeHash) {
62
+ const git = simpleGit()
63
+ await git.raw(['read-tree', treeHash])
64
+ }
65
+
47
66
  export async function resetLastCommit () {
48
67
  const git = simpleGit()
49
68
  await git.reset(['HEAD~1'])
package/lib/index.js CHANGED
@@ -3,7 +3,9 @@ import ora from 'ora'
3
3
  import { loadConfig } from './config.js'
4
4
  import {
5
5
  getCurrentBranch,
6
- hasUnstagedChanges,
6
+ hasReviewableChanges,
7
+ saveIndex,
8
+ restoreIndex,
7
9
  createReviewCommit,
8
10
  pushBranch,
9
11
  resetLastCommit,
@@ -16,9 +18,10 @@ import {
16
18
  formatFeedback
17
19
  } from './github.js'
18
20
 
19
- export async function init () {
21
+ export async function init ({ all = false } = {}) {
20
22
  const spinner = ora('Initializing review').start()
21
23
  let commitCreated = false
24
+ let savedTree = null
22
25
 
23
26
  try {
24
27
  // Check if we're in a git repository
@@ -39,9 +42,11 @@ export async function init () {
39
42
  const config = await loadConfig()
40
43
  const [owner, repo] = config.reviewRepo.split('/')
41
44
 
42
- // Check for unstaged changes
43
- if (!await hasUnstagedChanges()) {
44
- spinner.fail('No unstaged changes to review')
45
+ // Check for reviewable changes
46
+ if (!await hasReviewableChanges({ all })) {
47
+ spinner.fail(all
48
+ ? 'No changes to review'
49
+ : 'No tracked changes to review (use --all to include untracked files)')
45
50
  return
46
51
  }
47
52
 
@@ -63,11 +68,15 @@ export async function init () {
63
68
  const currentBranch = await getCurrentBranch()
64
69
  await pushBranch('review', `${currentBranch}:${baseBranch}`)
65
70
 
71
+ // Snapshot the current index so we can restore staging state after reset
72
+ savedTree = await saveIndex()
73
+
66
74
  // Create temporary commit with changes
67
75
  spinner.text = 'Creating review commit'
68
76
  await createReviewCommit(
69
77
  `Review checkpoint ${timestamp}`,
70
- config.author
78
+ config.author,
79
+ { all }
71
80
  )
72
81
  commitCreated = true
73
82
 
@@ -93,10 +102,13 @@ export async function init () {
93
102
  spinner.fail('Failed to create review')
94
103
  throw error
95
104
  } finally {
96
- // Always reset the commit if we created one
105
+ // Always reset the commit if we created one, then restore original staging
97
106
  if (commitCreated) {
98
107
  try {
99
108
  await resetLastCommit()
109
+ if (savedTree) {
110
+ await restoreIndex(savedTree)
111
+ }
100
112
  } catch (resetError) {
101
113
  console.error(chalk.yellow('Warning: Failed to reset temporary commit'))
102
114
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghreview",
3
- "version": "2.0.3",
3
+ "version": "3.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",
@@ -23,9 +23,11 @@ describe('ghreview', () => {
23
23
  })
24
24
 
25
25
  // Mock git operations
26
- vi.mocked(git.hasUnstagedChanges).mockResolvedValue(true)
26
+ vi.mocked(git.hasReviewableChanges).mockResolvedValue(true)
27
27
  vi.mocked(git.getCurrentBranch).mockResolvedValue('main')
28
28
  vi.mocked(git.ensureReviewRemote).mockResolvedValue('review')
29
+ vi.mocked(git.saveIndex).mockResolvedValue('abc123')
30
+ vi.mocked(git.restoreIndex).mockResolvedValue()
29
31
  vi.mocked(git.createReviewCommit).mockResolvedValue()
30
32
  vi.mocked(git.pushBranch).mockResolvedValue()
31
33
  vi.mocked(git.resetLastCommit).mockResolvedValue()
@@ -41,10 +43,12 @@ describe('ghreview', () => {
41
43
  await init()
42
44
 
43
45
  // Verify calls
44
- expect(git.hasUnstagedChanges).toHaveBeenCalled()
46
+ expect(git.hasReviewableChanges).toHaveBeenCalled()
47
+ expect(git.saveIndex).toHaveBeenCalled()
45
48
  expect(git.createReviewCommit).toHaveBeenCalled()
46
49
  expect(git.pushBranch).toHaveBeenCalledTimes(2) // base and review
47
50
  expect(git.resetLastCommit).toHaveBeenCalled()
51
+ expect(git.restoreIndex).toHaveBeenCalledWith('abc123')
48
52
  expect(github.createPullRequest).toHaveBeenCalled()
49
53
  })
50
54
 
@@ -53,7 +57,7 @@ describe('ghreview', () => {
53
57
  reviewRepo: 'owner/repo',
54
58
  githubToken: 'test-token'
55
59
  })
56
- vi.mocked(git.hasUnstagedChanges).mockResolvedValue(false)
60
+ vi.mocked(git.hasReviewableChanges).mockResolvedValue(false)
57
61
 
58
62
  await init()
59
63