@aaronshaf/ger 1.2.11 → 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.
Files changed (180) hide show
  1. package/.ast-grep/rules/no-as-casting.yml +13 -0
  2. package/.claude-plugin/plugin.json +22 -0
  3. package/.github/workflows/ci-simple.yml +53 -0
  4. package/.github/workflows/ci.yml +171 -0
  5. package/.github/workflows/claude-code-review.yml +83 -0
  6. package/.github/workflows/claude.yml +50 -0
  7. package/.github/workflows/dependency-update.yml +84 -0
  8. package/.github/workflows/release.yml +166 -0
  9. package/.github/workflows/security-scan.yml +113 -0
  10. package/.github/workflows/security.yml +96 -0
  11. package/.husky/pre-commit +16 -0
  12. package/.husky/pre-push +25 -0
  13. package/.lintstagedrc.json +6 -0
  14. package/.tool-versions +1 -0
  15. package/CLAUDE.md +105 -0
  16. package/DEVELOPMENT.md +361 -0
  17. package/EXAMPLES.md +457 -0
  18. package/README.md +831 -16
  19. package/bin/ger +3 -18
  20. package/biome.json +36 -0
  21. package/bun.lock +678 -0
  22. package/bunfig.toml +8 -0
  23. package/docs/adr/0001-use-effect-for-side-effects.md +65 -0
  24. package/docs/adr/0002-use-bun-runtime.md +64 -0
  25. package/docs/adr/0003-store-credentials-in-home-directory.md +75 -0
  26. package/docs/adr/0004-use-commander-for-cli.md +76 -0
  27. package/docs/adr/0005-use-effect-schema-for-validation.md +93 -0
  28. package/docs/adr/0006-use-msw-for-api-mocking.md +89 -0
  29. package/docs/adr/0007-git-hooks-for-quality.md +94 -0
  30. package/docs/adr/0008-no-as-typecasting.md +83 -0
  31. package/docs/adr/0009-file-size-limits.md +82 -0
  32. package/docs/adr/0010-llm-friendly-xml-output.md +93 -0
  33. package/docs/adr/0011-ai-tool-strategy-pattern.md +102 -0
  34. package/docs/adr/0012-build-status-message-parsing.md +94 -0
  35. package/docs/adr/0013-git-subprocess-integration.md +98 -0
  36. package/docs/adr/0014-group-management-support.md +95 -0
  37. package/docs/adr/0015-batch-comment-processing.md +111 -0
  38. package/docs/adr/0016-flexible-change-identifiers.md +94 -0
  39. package/docs/adr/0017-git-worktree-support.md +102 -0
  40. package/docs/adr/0018-auto-install-commit-hook.md +103 -0
  41. package/docs/adr/0019-sdk-package-exports.md +95 -0
  42. package/docs/adr/0020-code-coverage-enforcement.md +105 -0
  43. package/docs/adr/0021-typescript-isolated-declarations.md +83 -0
  44. package/docs/adr/0022-biome-oxlint-tooling.md +124 -0
  45. package/docs/adr/README.md +30 -0
  46. package/docs/prd/README.md +12 -0
  47. package/docs/prd/architecture.md +325 -0
  48. package/docs/prd/commands.md +425 -0
  49. package/docs/prd/data-model.md +349 -0
  50. package/docs/prd/overview.md +124 -0
  51. package/index.ts +219 -0
  52. package/oxlint.json +24 -0
  53. package/package.json +82 -15
  54. package/scripts/check-coverage.ts +69 -0
  55. package/scripts/check-file-size.ts +38 -0
  56. package/scripts/fix-test-mocks.ts +55 -0
  57. package/skills/gerrit-workflow/SKILL.md +247 -0
  58. package/skills/gerrit-workflow/examples.md +572 -0
  59. package/skills/gerrit-workflow/reference.md +728 -0
  60. package/src/api/gerrit.ts +696 -0
  61. package/src/cli/commands/abandon.ts +65 -0
  62. package/src/cli/commands/add-reviewer.ts +156 -0
  63. package/src/cli/commands/build-status.ts +282 -0
  64. package/src/cli/commands/checkout.ts +422 -0
  65. package/src/cli/commands/comment.ts +460 -0
  66. package/src/cli/commands/comments.ts +85 -0
  67. package/src/cli/commands/diff.ts +71 -0
  68. package/src/cli/commands/extract-url.ts +266 -0
  69. package/src/cli/commands/groups-members.ts +104 -0
  70. package/src/cli/commands/groups-show.ts +169 -0
  71. package/src/cli/commands/groups.ts +137 -0
  72. package/src/cli/commands/incoming.ts +226 -0
  73. package/src/cli/commands/init.ts +164 -0
  74. package/src/cli/commands/mine.ts +115 -0
  75. package/src/cli/commands/open.ts +57 -0
  76. package/src/cli/commands/projects.ts +68 -0
  77. package/src/cli/commands/push.ts +430 -0
  78. package/src/cli/commands/rebase.ts +52 -0
  79. package/src/cli/commands/remove-reviewer.ts +123 -0
  80. package/src/cli/commands/restore.ts +50 -0
  81. package/src/cli/commands/review.ts +486 -0
  82. package/src/cli/commands/search.ts +162 -0
  83. package/src/cli/commands/setup.ts +286 -0
  84. package/src/cli/commands/show.ts +491 -0
  85. package/src/cli/commands/status.ts +35 -0
  86. package/src/cli/commands/submit.ts +108 -0
  87. package/src/cli/commands/vote.ts +119 -0
  88. package/src/cli/commands/workspace.ts +200 -0
  89. package/src/cli/index.ts +53 -0
  90. package/src/cli/register-commands.ts +659 -0
  91. package/src/cli/register-group-commands.ts +88 -0
  92. package/src/cli/register-reviewer-commands.ts +97 -0
  93. package/src/prompts/default-review.md +86 -0
  94. package/src/prompts/system-inline-review.md +135 -0
  95. package/src/prompts/system-overall-review.md +206 -0
  96. package/src/schemas/config.test.ts +245 -0
  97. package/src/schemas/config.ts +84 -0
  98. package/src/schemas/gerrit.ts +681 -0
  99. package/src/services/commit-hook.ts +314 -0
  100. package/src/services/config.test.ts +150 -0
  101. package/src/services/config.ts +250 -0
  102. package/src/services/git-worktree.ts +342 -0
  103. package/src/services/review-strategy.ts +292 -0
  104. package/src/test-utils/mock-generator.ts +138 -0
  105. package/src/utils/change-id.test.ts +98 -0
  106. package/src/utils/change-id.ts +63 -0
  107. package/src/utils/comment-formatters.ts +153 -0
  108. package/src/utils/diff-context.ts +103 -0
  109. package/src/utils/diff-formatters.ts +141 -0
  110. package/src/utils/formatters.ts +85 -0
  111. package/src/utils/git-commit.test.ts +277 -0
  112. package/src/utils/git-commit.ts +122 -0
  113. package/src/utils/index.ts +55 -0
  114. package/src/utils/message-filters.ts +26 -0
  115. package/src/utils/review-formatters.ts +89 -0
  116. package/src/utils/review-prompt-builder.ts +110 -0
  117. package/src/utils/shell-safety.ts +117 -0
  118. package/src/utils/status-indicators.ts +100 -0
  119. package/src/utils/url-parser.test.ts +271 -0
  120. package/src/utils/url-parser.ts +118 -0
  121. package/tests/abandon.test.ts +230 -0
  122. package/tests/add-reviewer.test.ts +579 -0
  123. package/tests/build-status-watch.test.ts +344 -0
  124. package/tests/build-status.test.ts +789 -0
  125. package/tests/change-id-formats.test.ts +268 -0
  126. package/tests/checkout/integration.test.ts +653 -0
  127. package/tests/checkout/parse-input.test.ts +55 -0
  128. package/tests/checkout/validation.test.ts +178 -0
  129. package/tests/comment-batch-advanced.test.ts +431 -0
  130. package/tests/comment-gerrit-api-compliance.test.ts +414 -0
  131. package/tests/comment.test.ts +708 -0
  132. package/tests/comments.test.ts +323 -0
  133. package/tests/config-service-simple.test.ts +100 -0
  134. package/tests/diff.test.ts +419 -0
  135. package/tests/extract-url.test.ts +517 -0
  136. package/tests/groups-members.test.ts +256 -0
  137. package/tests/groups-show.test.ts +323 -0
  138. package/tests/groups.test.ts +334 -0
  139. package/tests/helpers/build-status-test-setup.ts +83 -0
  140. package/tests/helpers/config-mock.ts +27 -0
  141. package/tests/incoming.test.ts +357 -0
  142. package/tests/init.test.ts +70 -0
  143. package/tests/integration/commit-hook.test.ts +246 -0
  144. package/tests/interactive-incoming.test.ts +173 -0
  145. package/tests/mine.test.ts +285 -0
  146. package/tests/mocks/msw-handlers.ts +80 -0
  147. package/tests/open.test.ts +233 -0
  148. package/tests/projects.test.ts +259 -0
  149. package/tests/rebase.test.ts +271 -0
  150. package/tests/remove-reviewer.test.ts +357 -0
  151. package/tests/restore.test.ts +237 -0
  152. package/tests/review.test.ts +135 -0
  153. package/tests/search.test.ts +712 -0
  154. package/tests/setup.test.ts +63 -0
  155. package/tests/show-auto-detect.test.ts +324 -0
  156. package/tests/show.test.ts +813 -0
  157. package/tests/status.test.ts +145 -0
  158. package/tests/submit.test.ts +316 -0
  159. package/tests/unit/commands/push.test.ts +194 -0
  160. package/tests/unit/git-branch-detection.test.ts +82 -0
  161. package/tests/unit/git-worktree.test.ts +55 -0
  162. package/tests/unit/patterns/push-patterns.test.ts +148 -0
  163. package/tests/unit/schemas/gerrit.test.ts +85 -0
  164. package/tests/unit/services/commit-hook.test.ts +132 -0
  165. package/tests/unit/services/review-strategy.test.ts +349 -0
  166. package/tests/unit/test-utils/mock-generator.test.ts +154 -0
  167. package/tests/unit/utils/comment-formatters.test.ts +415 -0
  168. package/tests/unit/utils/diff-context.test.ts +171 -0
  169. package/tests/unit/utils/diff-formatters.test.ts +165 -0
  170. package/tests/unit/utils/formatters.test.ts +411 -0
  171. package/tests/unit/utils/message-filters.test.ts +227 -0
  172. package/tests/unit/utils/shell-safety.test.ts +230 -0
  173. package/tests/unit/utils/status-indicators.test.ts +137 -0
  174. package/tests/vote.test.ts +317 -0
  175. package/tests/workspace.test.ts +295 -0
  176. package/tsconfig.json +36 -5
  177. package/src/commands/branch.ts +0 -196
  178. package/src/ger.ts +0 -22
  179. package/src/types.d.ts +0 -35
  180. package/src/utils.ts +0 -130
@@ -0,0 +1,349 @@
1
+ # Data Model
2
+
3
+ Schemas and data structures used throughout the application.
4
+
5
+ ## Gerrit API Types
6
+
7
+ ### ChangeInfo
8
+
9
+ Primary change representation from Gerrit API.
10
+
11
+ ```typescript
12
+ const ChangeInfo = Schema.Struct({
13
+ id: Schema.String, // "project~branch~Change-Id"
14
+ _number: Schema.Number, // Numeric change ID
15
+ project: Schema.String, // Project name
16
+ branch: Schema.String, // Target branch
17
+ topic: Schema.optional(Schema.String),
18
+ subject: Schema.String, // First line of commit message
19
+ status: Schema.Literal('NEW', 'MERGED', 'ABANDONED', 'DRAFT'),
20
+ created: Schema.String, // ISO timestamp
21
+ updated: Schema.String, // ISO timestamp
22
+ submitted: Schema.optional(Schema.String),
23
+ submitter: Schema.optional(AccountInfo),
24
+ owner: AccountInfo,
25
+ current_revision: Schema.optional(Schema.String),
26
+ revisions: Schema.optional(Schema.Record(Schema.String, RevisionInfo)),
27
+ labels: Schema.optional(Schema.Record(Schema.String, LabelInfo)),
28
+ reviewers: Schema.optional(ReviewerMap),
29
+ messages: Schema.optional(Schema.Array(ChangeMessage)),
30
+ mergeable: Schema.optional(Schema.Boolean),
31
+ insertions: Schema.optional(Schema.Number),
32
+ deletions: Schema.optional(Schema.Number),
33
+ })
34
+ ```
35
+
36
+ ### AccountInfo
37
+
38
+ User account information.
39
+
40
+ ```typescript
41
+ const AccountInfo = Schema.Struct({
42
+ _account_id: Schema.Number,
43
+ name: Schema.optional(Schema.String),
44
+ email: Schema.optional(Schema.String),
45
+ username: Schema.optional(Schema.String),
46
+ display_name: Schema.optional(Schema.String),
47
+ })
48
+ ```
49
+
50
+ ### RevisionInfo
51
+
52
+ Patchset revision details.
53
+
54
+ ```typescript
55
+ const RevisionInfo = Schema.Struct({
56
+ _number: Schema.Number, // Patchset number
57
+ kind: Schema.Literal(
58
+ 'REWORK', 'TRIVIAL_REBASE', 'MERGE_FIRST_PARENT_UPDATE',
59
+ 'NO_CODE_CHANGE', 'NO_CHANGE'
60
+ ),
61
+ created: Schema.String,
62
+ uploader: AccountInfo,
63
+ ref: Schema.String, // Git ref (refs/changes/xx/xxxxx/x)
64
+ fetch: Schema.optional(Schema.Record(Schema.String, FetchInfo)),
65
+ commit: Schema.optional(CommitInfo),
66
+ files: Schema.optional(Schema.Record(Schema.String, FileInfo)),
67
+ })
68
+ ```
69
+
70
+ ### CommentInfo
71
+
72
+ Comment on a change.
73
+
74
+ ```typescript
75
+ const CommentInfo = Schema.Struct({
76
+ id: Schema.String,
77
+ path: Schema.optional(Schema.String),
78
+ line: Schema.optional(Schema.Number),
79
+ range: Schema.optional(CommentRange),
80
+ message: Schema.String,
81
+ author: AccountInfo,
82
+ updated: Schema.String,
83
+ side: Schema.optional(Schema.Literal('PARENT', 'REVISION')),
84
+ unresolved: Schema.optional(Schema.Boolean),
85
+ in_reply_to: Schema.optional(Schema.String),
86
+ })
87
+ ```
88
+
89
+ ### CommentRange
90
+
91
+ Line range for inline comments.
92
+
93
+ ```typescript
94
+ const CommentRange = Schema.Struct({
95
+ start_line: Schema.Number,
96
+ start_character: Schema.Number,
97
+ end_line: Schema.Number,
98
+ end_character: Schema.Number,
99
+ })
100
+ ```
101
+
102
+ ### ChangeMessage
103
+
104
+ Message/event on a change.
105
+
106
+ ```typescript
107
+ const ChangeMessage = Schema.Struct({
108
+ id: Schema.String,
109
+ author: Schema.optional(AccountInfo),
110
+ date: Schema.String,
111
+ message: Schema.String,
112
+ tag: Schema.optional(Schema.String),
113
+ _revision_number: Schema.optional(Schema.Number),
114
+ })
115
+ ```
116
+
117
+ ### LabelInfo
118
+
119
+ Voting label information.
120
+
121
+ ```typescript
122
+ const LabelInfo = Schema.Struct({
123
+ approved: Schema.optional(AccountInfo),
124
+ rejected: Schema.optional(AccountInfo),
125
+ recommended: Schema.optional(AccountInfo),
126
+ disliked: Schema.optional(AccountInfo),
127
+ value: Schema.optional(Schema.Number),
128
+ default_value: Schema.optional(Schema.Number),
129
+ all: Schema.optional(Schema.Array(ApprovalInfo)),
130
+ values: Schema.optional(Schema.Record(Schema.String, Schema.String)),
131
+ })
132
+ ```
133
+
134
+ ### GroupInfo
135
+
136
+ Gerrit group information.
137
+
138
+ ```typescript
139
+ const GroupInfo = Schema.Struct({
140
+ id: Schema.String,
141
+ name: Schema.String,
142
+ url: Schema.optional(Schema.String),
143
+ options: Schema.optional(GroupOptions),
144
+ description: Schema.optional(Schema.String),
145
+ group_id: Schema.optional(Schema.Number),
146
+ owner: Schema.optional(Schema.String),
147
+ owner_id: Schema.optional(Schema.String),
148
+ created_on: Schema.optional(Schema.String),
149
+ members: Schema.optional(Schema.Array(AccountInfo)),
150
+ includes: Schema.optional(Schema.Array(GroupInfo)),
151
+ })
152
+ ```
153
+
154
+ ## Configuration Schema
155
+
156
+ ### AppConfig
157
+
158
+ Application configuration stored in `~/.ger/config.json`.
159
+
160
+ ```typescript
161
+ const AppConfig = Schema.Struct({
162
+ host: Schema.String, // Gerrit server URL
163
+ username: Schema.String, // Gerrit username
164
+ password: Schema.String, // HTTP password/token
165
+ aiTool: Schema.optional(Schema.String), // Preferred AI tool
166
+ aiAutoDetect: Schema.optional(Schema.Boolean), // Auto-detect AI tool
167
+ })
168
+ ```
169
+
170
+ **File permissions:** 0600 (owner read/write only)
171
+
172
+ ## API Input Types
173
+
174
+ ### ReviewInput
175
+
176
+ Input for posting reviews/comments.
177
+
178
+ ```typescript
179
+ const ReviewInput = Schema.Struct({
180
+ message: Schema.optional(Schema.String), // Overall comment
181
+ labels: Schema.optional(Schema.Record(Schema.String, Schema.Number)),
182
+ comments: Schema.optional(Schema.Record(
183
+ Schema.String, // file path
184
+ Schema.Array(CommentInput)
185
+ )),
186
+ tag: Schema.optional(Schema.String),
187
+ notify: Schema.optional(Schema.Literal('NONE', 'OWNER', 'OWNER_REVIEWERS', 'ALL')),
188
+ })
189
+ ```
190
+
191
+ ### CommentInput
192
+
193
+ Input for a single inline comment.
194
+
195
+ ```typescript
196
+ const CommentInput = Schema.Struct({
197
+ line: Schema.optional(Schema.Number),
198
+ range: Schema.optional(CommentRange),
199
+ message: Schema.String,
200
+ side: Schema.optional(Schema.Literal('PARENT', 'REVISION')),
201
+ unresolved: Schema.optional(Schema.Boolean),
202
+ in_reply_to: Schema.optional(Schema.String),
203
+ })
204
+ ```
205
+
206
+ ### ReviewerInput
207
+
208
+ Input for adding reviewers.
209
+
210
+ ```typescript
211
+ const ReviewerInput = Schema.Struct({
212
+ reviewer: Schema.String, // Username, email, or group
213
+ state: Schema.optional(Schema.Literal('REVIEWER', 'CC')),
214
+ notify: Schema.optional(Schema.Literal('NONE', 'OWNER', 'OWNER_REVIEWERS', 'ALL')),
215
+ })
216
+ ```
217
+
218
+ ## Internal Types
219
+
220
+ ### BuildState
221
+
222
+ CI build status.
223
+
224
+ ```typescript
225
+ type BuildState = 'pending' | 'running' | 'success' | 'failure' | 'not_found'
226
+ ```
227
+
228
+ ### InlineComment
229
+
230
+ CLI input format for batch comments.
231
+
232
+ ```typescript
233
+ const InlineComment = Schema.Struct({
234
+ file: Schema.String,
235
+ line: Schema.optional(Schema.Number),
236
+ range: Schema.optional(CommentRange),
237
+ message: Schema.String,
238
+ side: Schema.optional(Schema.Literal('PARENT', 'REVISION')),
239
+ unresolved: Schema.optional(Schema.Boolean),
240
+ })
241
+ ```
242
+
243
+ ### ChangeIdentifier
244
+
245
+ Normalized change identifier.
246
+
247
+ ```typescript
248
+ // Accepts:
249
+ // - Numeric: "12345"
250
+ // - Change-ID: "If5a3ae8cb5a107e187447802358417f311d0c4b1"
251
+ // - Full triplet: "project~branch~Change-Id"
252
+ // - URL: "https://gerrit.example.com/c/project/+/12345"
253
+
254
+ const isChangeNumber = (id: string): boolean => /^\d+$/.test(id)
255
+ const isChangeId = (id: string): boolean => /^I[0-9a-f]{40}$/i.test(id)
256
+ ```
257
+
258
+ ## Error Types
259
+
260
+ ### ApiError
261
+
262
+ API call failures.
263
+
264
+ ```typescript
265
+ class ApiError extends Schema.TaggedError<ApiError>()('ApiError', {
266
+ message: Schema.String,
267
+ statusCode: Schema.Number,
268
+ url: Schema.String,
269
+ }) {}
270
+ ```
271
+
272
+ ### ConfigError
273
+
274
+ Configuration issues.
275
+
276
+ ```typescript
277
+ class ConfigError extends Schema.TaggedError<ConfigError>()('ConfigError', {
278
+ message: Schema.String,
279
+ }) {}
280
+ ```
281
+
282
+ ### GitError
283
+
284
+ Git operation failures.
285
+
286
+ ```typescript
287
+ class GitError extends Schema.TaggedError<GitError>()('GitError', {
288
+ message: Schema.String,
289
+ exitCode: Schema.Number,
290
+ }) {}
291
+ ```
292
+
293
+ ### NoChangeIdError
294
+
295
+ Missing Change-ID in commit.
296
+
297
+ ```typescript
298
+ class NoChangeIdError extends Schema.TaggedError<NoChangeIdError>()('NoChangeIdError', {
299
+ message: Schema.String,
300
+ }) {}
301
+ ```
302
+
303
+ ## Output Formats
304
+
305
+ ### Text (Default)
306
+
307
+ Human-readable colored output:
308
+ ```
309
+ Change 12345: Fix login bug
310
+ Project: canvas-lms
311
+ Branch: main
312
+ Status: NEW
313
+ Owner: alice@example.com
314
+
315
+ Files:
316
+ M src/login.ts
317
+ A src/auth.ts
318
+ ```
319
+
320
+ ### XML (--xml flag)
321
+
322
+ LLM-friendly structured output:
323
+ ```xml
324
+ <change>
325
+ <number>12345</number>
326
+ <subject><![CDATA[Fix login bug]]></subject>
327
+ <project>canvas-lms</project>
328
+ <branch>main</branch>
329
+ <status>NEW</status>
330
+ <owner>alice@example.com</owner>
331
+ <files>
332
+ <file action="M">src/login.ts</file>
333
+ <file action="A">src/auth.ts</file>
334
+ </files>
335
+ </change>
336
+ ```
337
+
338
+ ### JSON
339
+
340
+ Programmatic consumption:
341
+ ```json
342
+ {
343
+ "_number": 12345,
344
+ "subject": "Fix login bug",
345
+ "project": "canvas-lms",
346
+ "branch": "main",
347
+ "status": "NEW"
348
+ }
349
+ ```
@@ -0,0 +1,124 @@
1
+ # ger - Gerrit CLI Tool
2
+
3
+ ## Overview
4
+
5
+ `ger` is a modern, LLM-friendly CLI tool and SDK for Gerrit Code Review. It provides comprehensive Gerrit workflow automation with AI integration, published as both a CLI tool and npm package.
6
+
7
+ ## Goals
8
+
9
+ 1. **Fast Gerrit operations** - Quick access to changes, diffs, comments without browser
10
+ 2. **AI integration** - LLM-friendly output and automated code review
11
+ 3. **Developer ergonomics** - Auto-detection, smart defaults, minimal configuration
12
+ 4. **Programmatic access** - SDK for building custom tools and automation
13
+ 5. **Type safety** - Full TypeScript with Effect for reliable operations
14
+
15
+ ## Non-Goals
16
+
17
+ - Replace Gerrit web UI entirely (complex dashboards, admin)
18
+ - Support Gerrit versions older than 3.0
19
+ - Implement bidirectional sync (changes are created via git push)
20
+ - Provide real-time notifications (use Gerrit's native features)
21
+
22
+ ## Design Decisions
23
+
24
+ | Decision | Choice | Rationale |
25
+ |----------|--------|-----------|
26
+ | Language | TypeScript/Bun | Fast runtime, native TS, matches ji/cn projects |
27
+ | Error handling | Effect library | Type-safe errors, composable operations |
28
+ | CLI framework | Commander.js | Stable, simple, well-documented |
29
+ | Output formats | Text, XML, JSON | Human + machine readable |
30
+ | Credentials | `~/.ger/config.json` | Secure, standard pattern |
31
+ | Git integration | Subprocess | No deps, full features, worktree support |
32
+ | Validation | Effect Schema | Single source of truth, type inference |
33
+ | Testing | MSW + Bun test | Fast, realistic API mocking |
34
+ | AI tools | Strategy pattern | Vendor-agnostic, auto-detection |
35
+
36
+ ## User Personas
37
+
38
+ ### Individual Developer
39
+ - Works on personal changes
40
+ - Needs quick status checks
41
+ - Wants CLI convenience over browser
42
+
43
+ ### Code Reviewer
44
+ - Reviews multiple changes daily
45
+ - Needs efficient navigation
46
+ - Benefits from AI-assisted review
47
+
48
+ ### Automation Engineer
49
+ - Builds CI/CD pipelines
50
+ - Needs programmatic access
51
+ - Requires JSON/XML output
52
+
53
+ ### Team Lead
54
+ - Manages reviewers and groups
55
+ - Tracks team's open changes
56
+ - Assigns reviewers efficiently
57
+
58
+ ## Commands Overview
59
+
60
+ | Category | Commands |
61
+ |----------|----------|
62
+ | **View** | `show`, `diff`, `comments`, `search` |
63
+ | **Review** | `comment`, `vote`, `review`, `add-reviewer` |
64
+ | **Manage** | `mine`, `incoming`, `abandon`, `restore` |
65
+ | **Operations** | `checkout`, `push`, `rebase`, `submit` |
66
+ | **Groups** | `groups`, `groups-show`, `groups-members` |
67
+ | **Utilities** | `status`, `setup`, `open`, `extract-url`, `build-status` |
68
+
69
+ ## User Flows
70
+
71
+ ### First-time Setup
72
+
73
+ ```
74
+ $ ger setup
75
+ ? Gerrit URL: https://gerrit.example.com
76
+ ? Username: john.doe
77
+ ? HTTP Password: ****
78
+ ✓ Configuration saved to ~/.ger/config.json
79
+ ✓ Connection verified
80
+ ```
81
+
82
+ ### Daily Code Review
83
+
84
+ ```
85
+ $ ger incoming
86
+ PROJECT: canvas-lms
87
+ 12345 Fix login bug alice CR: +1
88
+ 12346 Add dark mode bob CR: 0
89
+
90
+ $ ger show 12345
91
+ Change 12345: Fix login bug
92
+ Author: alice@example.com
93
+ Status: NEW
94
+ ...
95
+
96
+ $ ger review 12345
97
+ ✓ AI review posted (3 inline comments, 1 overall comment)
98
+ ```
99
+
100
+ ### Submit a Change
101
+
102
+ ```
103
+ $ git commit -m "Fix typo in README"
104
+ $ ger push
105
+ ✓ Pushed to refs/for/main
106
+ ✓ Change 12347 created
107
+
108
+ $ ger add-reviewer 12347 bob carol
109
+ ✓ Added 2 reviewers
110
+ ```
111
+
112
+ ## Success Metrics
113
+
114
+ - Handle repositories with 10,000+ changes
115
+ - Complete common operations in < 2 seconds
116
+ - AI review completes in < 30 seconds
117
+ - 80%+ test coverage maintained
118
+
119
+ ## References
120
+
121
+ - [Gerrit REST API](https://gerrit-review.googlesource.com/Documentation/rest-api.html)
122
+ - [Effect library](https://effect.website/)
123
+ - [ji project](https://github.com/aaronshaf/ji) - JIRA CLI (same author)
124
+ - [cn project](https://github.com/aaronshaf/cn) - Confluence CLI (same author)
package/index.ts ADDED
@@ -0,0 +1,219 @@
1
+ /**
2
+ * @aaronshaf/ger - Gerrit CLI and SDK
3
+ *
4
+ * This package provides both a CLI tool and a programmatic API for interacting with Gerrit Code Review.
5
+ * Built with Effect-TS for type-safe, composable operations.
6
+ *
7
+ * @module
8
+ *
9
+ * @example Basic usage with Effect
10
+ * ```typescript
11
+ * import { Effect, pipe } from 'effect'
12
+ * import {
13
+ * GerritApiService,
14
+ * GerritApiServiceLive,
15
+ * ConfigServiceLive,
16
+ * } from '@aaronshaf/ger'
17
+ *
18
+ * const program = Effect.gen(function* () {
19
+ * const api = yield* GerritApiService
20
+ * const change = yield* api.getChange('12345')
21
+ * console.log(change.subject)
22
+ * })
23
+ *
24
+ * const runnable = pipe(
25
+ * program,
26
+ * Effect.provide(GerritApiServiceLive),
27
+ * Effect.provide(ConfigServiceLive)
28
+ * )
29
+ *
30
+ * Effect.runPromise(runnable)
31
+ * ```
32
+ */
33
+
34
+ // ============================================================================
35
+ // Core API Service
36
+ // ============================================================================
37
+
38
+ export {
39
+ // Service tag and implementation
40
+ GerritApiService,
41
+ GerritApiServiceLive,
42
+ // Types
43
+ type GerritApiServiceImpl,
44
+ // Errors
45
+ ApiError,
46
+ type ApiErrorFields,
47
+ } from './src/api/gerrit'
48
+
49
+ // ============================================================================
50
+ // Configuration Service
51
+ // ============================================================================
52
+
53
+ export {
54
+ // Service tag and implementation
55
+ ConfigService,
56
+ ConfigServiceLive,
57
+ // Types
58
+ type ConfigServiceImpl,
59
+ // Errors
60
+ ConfigError,
61
+ type ConfigErrorFields,
62
+ } from './src/services/config'
63
+
64
+ // ============================================================================
65
+ // Review Strategy Service
66
+ // ============================================================================
67
+
68
+ export {
69
+ // Strategy types
70
+ type ReviewStrategy,
71
+ // Built-in strategies
72
+ claudeCliStrategy,
73
+ geminiCliStrategy,
74
+ openCodeCliStrategy,
75
+ // Service
76
+ ReviewStrategyService,
77
+ ReviewStrategyServiceLive,
78
+ type ReviewStrategyServiceImpl,
79
+ // Errors
80
+ ReviewStrategyError,
81
+ type ReviewStrategyErrorFields,
82
+ } from './src/services/review-strategy'
83
+
84
+ // ============================================================================
85
+ // Git Worktree Service
86
+ // ============================================================================
87
+
88
+ export {
89
+ // Service tag and implementation
90
+ GitWorktreeService,
91
+ GitWorktreeServiceLive,
92
+ type GitWorktreeServiceImpl,
93
+ // Types
94
+ type WorktreeInfo,
95
+ // Errors
96
+ WorktreeCreationError,
97
+ type WorktreeCreationErrorFields,
98
+ PatchsetFetchError,
99
+ type PatchsetFetchErrorFields,
100
+ DirtyRepoError,
101
+ type DirtyRepoErrorFields,
102
+ NotGitRepoError,
103
+ type NotGitRepoErrorFields,
104
+ type GitWorktreeError,
105
+ } from './src/services/git-worktree'
106
+
107
+ // ============================================================================
108
+ // Schemas and Types
109
+ // ============================================================================
110
+
111
+ export {
112
+ // Authentication
113
+ GerritCredentials,
114
+ type GerritCredentials as GerritCredentialsType,
115
+ // Changes
116
+ ChangeInfo,
117
+ type ChangeInfo as ChangeInfoType,
118
+ // Comments
119
+ CommentInput,
120
+ type CommentInput as CommentInputType,
121
+ CommentInfo,
122
+ type CommentInfo as CommentInfoType,
123
+ // Messages
124
+ MessageInfo,
125
+ type MessageInfo as MessageInfoType,
126
+ // Reviews
127
+ ReviewInput,
128
+ type ReviewInput as ReviewInputType,
129
+ // Files and Diffs
130
+ FileInfo,
131
+ type FileInfo as FileInfoType,
132
+ FileDiffContent,
133
+ type FileDiffContent as FileDiffContentType,
134
+ RevisionInfo,
135
+ type RevisionInfo as RevisionInfoType,
136
+ // Diff Options
137
+ DiffFormat,
138
+ type DiffFormat as DiffFormatType,
139
+ DiffOptions,
140
+ type DiffOptions as DiffOptionsType,
141
+ DiffCommandOptions,
142
+ type DiffCommandOptions as DiffCommandOptionsType,
143
+ // Errors
144
+ GerritError,
145
+ type GerritError as GerritErrorType,
146
+ } from './src/schemas/gerrit'
147
+
148
+ export {
149
+ // Config schemas
150
+ AppConfig,
151
+ type AppConfig as AppConfigType,
152
+ AiConfig,
153
+ type AiConfig as AiConfigType,
154
+ // Utilities
155
+ aiConfigFromFlat,
156
+ migrateFromNestedConfig,
157
+ } from './src/schemas/config'
158
+
159
+ // ============================================================================
160
+ // Utilities
161
+ // ============================================================================
162
+
163
+ export {
164
+ // Change ID handling
165
+ normalizeChangeIdentifier,
166
+ isChangeId,
167
+ isChangeNumber,
168
+ isValidChangeIdentifier,
169
+ getIdentifierType,
170
+ } from './src/utils/change-id'
171
+
172
+ export {
173
+ // Git commit utilities
174
+ extractChangeIdFromCommitMessage,
175
+ getLastCommitMessage,
176
+ getChangeIdFromHead,
177
+ GitError,
178
+ NoChangeIdError,
179
+ } from './src/utils/git-commit'
180
+
181
+ export {
182
+ // URL parsing
183
+ extractChangeNumber,
184
+ normalizeGerritHost,
185
+ isValidChangeId,
186
+ } from './src/utils/url-parser'
187
+
188
+ export {
189
+ // Message filtering
190
+ filterMeaningfulMessages,
191
+ sortMessagesByDate,
192
+ } from './src/utils/message-filters'
193
+
194
+ export {
195
+ // Shell safety
196
+ sanitizeCDATA,
197
+ } from './src/utils/shell-safety'
198
+
199
+ export {
200
+ // Formatters
201
+ formatDate,
202
+ getStatusIndicator,
203
+ colors,
204
+ } from './src/utils/formatters'
205
+
206
+ export {
207
+ // Comment formatters
208
+ formatCommentsPretty,
209
+ formatCommentsXml,
210
+ type CommentWithContext,
211
+ } from './src/utils/comment-formatters'
212
+
213
+ export {
214
+ // Diff formatters
215
+ formatDiffPretty,
216
+ formatDiffSummary,
217
+ formatFilesList,
218
+ extractDiffStats,
219
+ } from './src/utils/diff-formatters'
package/oxlint.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "rules": {
3
+ "no-implicit-any": "error",
4
+ "no-explicit-any": "error",
5
+ "no-unused-vars": "error",
6
+ "no-console": "warn",
7
+ "no-debugger": "error",
8
+ "prefer-const": "error",
9
+ "no-var": "error",
10
+ "eqeqeq": "error",
11
+ "no-eval": "error"
12
+ },
13
+ "ignorePatterns": [
14
+ "node_modules",
15
+ "dist",
16
+ "tmp",
17
+ "**/*.js",
18
+ "**/*.jsx",
19
+ "**/*.tsx",
20
+ "coverage",
21
+ "*.config.js",
22
+ "*.config.ts"
23
+ ]
24
+ }