git-chopstick-core 0.1.7 → 0.1.8

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,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.8] — 2026-06-13
4
+
5
+ ### Added
6
+ - **Test files included in package**: Added `src/__tests__/` to the `files` array in `package.json`. Integration tests ship with the installed package so consumers can run `npm test` to verify the package works for their environment.
7
+
8
+ ---
9
+
3
10
  ## [0.1.7] — 2026-06-13
4
11
 
5
12
  ### Added
@@ -122,11 +129,7 @@
122
129
 
123
130
  ---
124
131
 
125
- [0.1.4]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.4
126
- [0.1.3]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.3
127
- [0.1.2]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.2
128
- [0.1.1]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.1
129
- [0.1.0]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.0
132
+ [0.1.8]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.8
130
133
  [0.1.7]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.7
131
134
  [0.1.6]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.6
132
135
  [0.1.5]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.5
@@ -135,4 +138,4 @@
135
138
  [0.1.2]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.2
136
139
  [0.1.1]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.1
137
140
  [0.1.0]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.0
138
- [Unreleased]: https://github.com/parkiyong/git-chopstick-core/compare/v0.1.7...HEAD
141
+ [Unreleased]: https://github.com/parkiyong/git-chopstick-core/compare/v0.1.8...HEAD
package/README.md CHANGED
@@ -26,7 +26,7 @@ npm install file:../path/to/git-chopstick-core
26
26
  ```typescript
27
27
  import { Repository, getStatus, GitError } from 'git-chopstick-core'
28
28
 
29
- const repo = new Repository('/path/to/repo', 1)
29
+ const repo = new Repository('/path/to/repo')
30
30
 
31
31
  try {
32
32
  const status = await getStatus(repo)
@@ -46,7 +46,7 @@ try {
46
46
  ```typescript
47
47
  import { Repository, getStatus } from 'git-chopstick-core'
48
48
 
49
- const repo = new Repository('/path/to/repo', 1)
49
+ const repo = new Repository('/path/to/repo')
50
50
  const status = await getStatus(repo)
51
51
 
52
52
  if (!status) {
@@ -81,7 +81,7 @@ if (status.rebaseInternalState) {
81
81
  ```typescript
82
82
  import { Repository, getStatus, createCommit } from 'git-chopstick-core'
83
83
 
84
- const repo = new Repository('/path/to/repo', 1)
84
+ const repo = new Repository('/path/to/repo')
85
85
  const status = await getStatus(repo)
86
86
 
87
87
  // Stage and commit all tracked files
@@ -98,7 +98,7 @@ import {
98
98
  deleteLocalBranch, renameBranch, getBranches
99
99
  } from 'git-chopstick-core'
100
100
 
101
- const repo = new Repository('/path/to/repo', 1)
101
+ const repo = new Repository('/path/to/repo')
102
102
 
103
103
  // Create a branch
104
104
  await createBranch(repo, 'feature/new-feature', 'main')
@@ -125,7 +125,7 @@ import {
125
125
  abortMerge, getMergeBase, isMergeHeadSet
126
126
  } from 'git-chopstick-core'
127
127
 
128
- const repo = new Repository('/path/to/repo', 1)
128
+ const repo = new Repository('/path/to/repo')
129
129
 
130
130
  const result = await merge(repo, 'feature/new-feature')
131
131
 
@@ -149,7 +149,7 @@ import {
149
149
  Repository, push, pull, getRemotes, getStatus
150
150
  } from 'git-chopstick-core'
151
151
 
152
- const repo = new Repository('/path/to/repo', 1)
152
+ const repo = new Repository('/path/to/repo')
153
153
  const [remote] = await getRemotes(repo)
154
154
  const status = await getStatus(repo)
155
155
 
@@ -167,7 +167,7 @@ import {
167
167
  Repository, merge, GitError, GitErrorCodes
168
168
  } from 'git-chopstick-core'
169
169
 
170
- const repo = new Repository('/path/to/repo', 1)
170
+ const repo = new Repository('/path/to/repo')
171
171
 
172
172
  try {
173
173
  await merge(repo, 'other-branch')
@@ -328,11 +328,14 @@ src/
328
328
  # Type-check
329
329
  npm run typecheck
330
330
 
331
+ # Run integration tests (uses vitest, creates temp git repos)
332
+ npm test
333
+
331
334
  # Build (compile TypeScript → dist/)
332
335
  npm run build
333
336
 
334
- # Clean rebuild (build already cleans automatically)
335
- npm run build
337
+ # Package contents dry-run (verify files before publish)
338
+ npm pack --dry-run
336
339
 
337
340
  # Run examples (uses tsx for source-level execution)
338
341
  npx tsx examples/get-status.ts /path/to/repo
@@ -49,7 +49,7 @@ Run all of these (use parallel agents):
49
49
  - `npm test` — all integration tests must pass
50
50
  - `npm pack --dry-run` — confirm all of:
51
51
  - `dist/` is included (expect ~351 files, ~700kB)
52
- - `CHANGELOG.md` and `docs/` appear in the file list
52
+ - `CHANGELOG.md`, `docs/`, and `src/__tests__/integration.test.ts` appear in the file list
53
53
  - `package.json`, `README.md`, `LICENSE` are present (npm includes these automatically)
54
54
  - No unexpected files are leaking through (check for test artifacts, `.ts` sources outside `dist/`)
55
55
 
@@ -77,5 +77,6 @@ npm publish
77
77
 
78
78
  ## Notes
79
79
 
80
- - `prepublishOnly` runs `npm run typecheck && npm run build` automatically, so no need to build before publish but validate first anyway.
80
+ - `prepublishOnly` runs `npm run typecheck && npm test && npm run build` automatically integration tests must pass before publish can proceed. This catches API regressions, broken imports, and test failures before they ship.
81
+ You still validate in step 5 beforehand rather than relying solely on `prepublishOnly`.
81
82
  - If this is the first release in a session, run `setup-matt-pocock-skills` first to configure issue tracker context.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-chopstick-core",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -33,7 +33,8 @@
33
33
  "files": [
34
34
  "dist",
35
35
  "CHANGELOG.md",
36
- "docs"
36
+ "docs",
37
+ "src/__tests__"
37
38
  ],
38
39
  "description": "Git backend library extracted from GitHub Desktop",
39
40
  "scripts": {
@@ -41,7 +42,7 @@
41
42
  "build": "npm run clean && tsc",
42
43
  "typecheck": "tsc --noEmit",
43
44
  "test": "vitest run",
44
- "prepublishOnly": "npm run typecheck && npm run build"
45
+ "prepublishOnly": "npm run typecheck && npm test && npm run build"
45
46
  },
46
47
  "dependencies": {
47
48
  "byline": "^5.0.0",
@@ -0,0 +1,156 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest'
2
+ import { mkdtempSync, writeFileSync } from 'fs'
3
+ import { execSync } from 'child_process'
4
+ import { tmpdir } from 'os'
5
+ import { join } from 'path'
6
+ import {
7
+ Repository, getStatus, getCommits, getBranches,
8
+ createCommit, createBranch, deleteLocalBranch, renameBranch,
9
+ getCurrentBranch, getRepositoryType,
10
+ } from '../index.js'
11
+
12
+ let repoPath: string
13
+ let repo: Repository
14
+
15
+ function git(args: string) {
16
+ execSync(`git ${args}`, { cwd: repoPath, stdio: 'pipe' })
17
+ }
18
+
19
+ function writeFile(path: string, content: string) {
20
+ writeFileSync(join(repoPath, path), content)
21
+ }
22
+
23
+ beforeAll(() => {
24
+ repoPath = mkdtempSync(join(tmpdir(), 'gcctest-'))
25
+ git('init -q')
26
+ git('config user.email test@test.test')
27
+ git('config user.name Test')
28
+
29
+ // Create an initial commit so we have history for detached HEAD tests
30
+ writeFile('README.md', '# test')
31
+ git('add README.md')
32
+ git('commit -m "initial"')
33
+
34
+ repo = new Repository(repoPath)
35
+ })
36
+
37
+ describe('getRepositoryType', () => {
38
+ it('returns regular for a valid repo', async () => {
39
+ const type = await getRepositoryType(repoPath)
40
+ expect(type.kind).toBe('regular')
41
+ if (type.kind === 'regular') {
42
+ expect(type.topLevelWorkingDirectory).toBe(repoPath)
43
+ }
44
+ })
45
+
46
+ it('returns missing for a non-existent path', async () => {
47
+ const type = await getRepositoryType('/nonexistent/path')
48
+ expect(type.kind).toBe('missing')
49
+ })
50
+ })
51
+
52
+ describe('getCurrentBranch', () => {
53
+ it('returns the current branch name', async () => {
54
+ const branch = await getCurrentBranch(repoPath)
55
+ expect(branch).toBeTruthy()
56
+ expect(typeof branch).toBe('string')
57
+ })
58
+
59
+ it('returns undefined when HEAD is detached', async () => {
60
+ git('checkout --detach')
61
+ const branch = await getCurrentBranch(repoPath)
62
+ expect(branch).toBeUndefined()
63
+ // Reset back to a branch for subsequent tests
64
+ git('checkout -')
65
+ })
66
+ })
67
+
68
+ describe('getStatus', () => {
69
+ it('returns a status with branch info', async () => {
70
+ const status = await getStatus(repo)
71
+ expect(status).toBeTruthy()
72
+ expect(status!.currentBranch).toBeTruthy()
73
+ expect(typeof status!.currentBranch).toBe('string')
74
+ })
75
+
76
+ it('detects untracked files', async () => {
77
+ writeFile('untracked.txt', 'hello')
78
+ const status = await getStatus(repo)
79
+ expect(status!.workingDirectory.files.length).toBeGreaterThanOrEqual(1)
80
+ const untracked = status!.workingDirectory.files.find(
81
+ f => f.path === 'untracked.txt'
82
+ )
83
+ expect(untracked).toBeTruthy()
84
+ })
85
+
86
+ it('returns null for a path that exists but is not a git repo', async () => {
87
+ const nonRepoPath = mkdtempSync(join(tmpdir(), 'gcctest-nonrepo-'))
88
+ const result = await getStatus(new Repository(nonRepoPath))
89
+ expect(result).toBeNull()
90
+ execSync(`rm -rf ${nonRepoPath}`)
91
+ })
92
+ })
93
+
94
+ describe('getCommits', () => {
95
+ it('returns commits in order', async () => {
96
+ // Create another commit so we have history to read
97
+ writeFile('commits-test.txt', 'data')
98
+ git('add commits-test.txt')
99
+ git('commit -m "test commit for getCommits"')
100
+
101
+ const commits = await getCommits(repo, 'HEAD', 10)
102
+ expect(commits.length).toBeGreaterThanOrEqual(1)
103
+ expect(commits[0].summary).toBe('test commit for getCommits')
104
+ expect(commits[0].sha).toBeTruthy()
105
+ expect(commits[0].author).toBeTruthy()
106
+ })
107
+ })
108
+
109
+ describe('Branch operations', () => {
110
+ it('creates, lists, renames, and deletes branches', async () => {
111
+ // Create a branch
112
+ await createBranch(repo, 'test-feature', 'HEAD')
113
+ let branches = await getBranches(repo)
114
+ const names = branches.map(b => b.name)
115
+ expect(names).toContain('test-feature')
116
+
117
+ // Rename the branch
118
+ const featureBranch = branches.find(b => b.name === 'test-feature')!
119
+ await renameBranch(repo, featureBranch, 'test-feature-renamed')
120
+ branches = await getBranches(repo)
121
+ expect(branches.map(b => b.name)).toContain('test-feature-renamed')
122
+ expect(branches.map(b => b.name)).not.toContain('test-feature')
123
+
124
+ // Delete the branch
125
+ await deleteLocalBranch(repo, 'test-feature-renamed')
126
+ branches = await getBranches(repo)
127
+ expect(branches.map(b => b.name)).not.toContain('test-feature-renamed')
128
+ })
129
+ })
130
+
131
+ describe('createCommit', () => {
132
+ it('creates a commit with files', async () => {
133
+ writeFile('commit-test.txt', 'commit data')
134
+ git('add commit-test.txt')
135
+
136
+ const status = await getStatus(repo)
137
+ const files = status!.workingDirectory.files.filter(
138
+ f => f.path === 'commit-test.txt'
139
+ )
140
+
141
+ const sha = await createCommit(repo, 'feat: add commit-test.txt', files)
142
+ expect(sha).toBeTruthy()
143
+ // createCommit returns the abbreviated SHA from git commit output (7+ chars)
144
+ expect(sha.length).toBeGreaterThanOrEqual(7)
145
+
146
+ // Verify the commit exists — the full SHA from getCommits should start
147
+ // with the abbreviated SHA returned by createCommit
148
+ const commits = await getCommits(repo, 'HEAD', 5)
149
+ expect(commits[0].sha.startsWith(sha)).toBe(true)
150
+ expect(commits[0].summary).toBe('feat: add commit-test.txt')
151
+ })
152
+ })
153
+
154
+ afterAll(() => {
155
+ execSync(`rm -rf ${repoPath}`)
156
+ })