@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,681 @@
1
+ import { Schema } from '@effect/schema'
2
+
3
+ // Authentication schemas
4
+ export const GerritCredentials: Schema.Schema<{
5
+ readonly host: string
6
+ readonly username: string
7
+ readonly password: string
8
+ }> = Schema.Struct({
9
+ host: Schema.String.pipe(
10
+ Schema.pattern(/^https?:\/\/.+$/),
11
+ Schema.annotations({ description: 'Gerrit server URL' }),
12
+ ),
13
+ username: Schema.String.pipe(
14
+ Schema.minLength(1),
15
+ Schema.annotations({ description: 'Gerrit username' }),
16
+ ),
17
+ password: Schema.String.pipe(
18
+ Schema.minLength(1),
19
+ Schema.annotations({ description: 'HTTP password or API token' }),
20
+ ),
21
+ })
22
+ export type GerritCredentials = Schema.Schema.Type<typeof GerritCredentials>
23
+
24
+ // Forward declare RevisionInfo type for use in ChangeInfo
25
+ export interface RevisionInfoType {
26
+ readonly kind?: string
27
+ readonly _number: number
28
+ readonly created: string
29
+ readonly uploader: {
30
+ readonly _account_id: number
31
+ readonly name?: string
32
+ readonly email?: string
33
+ }
34
+ readonly ref: string
35
+ readonly fetch?: Record<string, unknown>
36
+ readonly commit?: {
37
+ readonly commit: string
38
+ readonly parents: ReadonlyArray<{
39
+ readonly commit: string
40
+ readonly subject: string
41
+ }>
42
+ readonly author: {
43
+ readonly name: string
44
+ readonly email: string
45
+ readonly date: string
46
+ }
47
+ readonly committer: {
48
+ readonly name: string
49
+ readonly email: string
50
+ readonly date: string
51
+ }
52
+ readonly subject: string
53
+ readonly message: string
54
+ }
55
+ readonly files?: Record<
56
+ string,
57
+ {
58
+ readonly status?: 'A' | 'D' | 'R' | 'C' | 'M'
59
+ readonly lines_inserted?: number
60
+ readonly lines_deleted?: number
61
+ readonly size?: number
62
+ readonly size_delta?: number
63
+ readonly old_path?: string
64
+ }
65
+ >
66
+ }
67
+
68
+ // Change schemas
69
+ export const ChangeInfo: Schema.Schema<{
70
+ readonly id: string
71
+ readonly project: string
72
+ readonly branch: string
73
+ readonly change_id: string
74
+ readonly subject: string
75
+ readonly status: 'NEW' | 'MERGED' | 'ABANDONED' | 'DRAFT'
76
+ readonly created?: string
77
+ readonly updated?: string
78
+ readonly insertions?: number
79
+ readonly deletions?: number
80
+ readonly _number: number
81
+ readonly owner?: {
82
+ readonly _account_id: number
83
+ readonly name?: string
84
+ readonly email?: string
85
+ readonly username?: string
86
+ }
87
+ readonly labels?: Record<
88
+ string,
89
+ {
90
+ readonly approved?: {
91
+ readonly _account_id: number
92
+ readonly name?: string
93
+ readonly email?: string
94
+ readonly username?: string
95
+ }
96
+ readonly rejected?: {
97
+ readonly _account_id: number
98
+ readonly name?: string
99
+ readonly email?: string
100
+ readonly username?: string
101
+ }
102
+ readonly recommended?: {
103
+ readonly _account_id: number
104
+ readonly name?: string
105
+ readonly email?: string
106
+ readonly username?: string
107
+ }
108
+ readonly disliked?: {
109
+ readonly _account_id: number
110
+ readonly name?: string
111
+ readonly email?: string
112
+ readonly username?: string
113
+ }
114
+ readonly value?: number
115
+ }
116
+ >
117
+ readonly submittable?: boolean
118
+ readonly work_in_progress?: boolean
119
+ readonly current_revision?: string
120
+ readonly revisions?: Record<string, RevisionInfoType>
121
+ }> = Schema.Struct({
122
+ id: Schema.String,
123
+ project: Schema.String,
124
+ branch: Schema.String,
125
+ change_id: Schema.String,
126
+ subject: Schema.String,
127
+ status: Schema.Literal('NEW', 'MERGED', 'ABANDONED', 'DRAFT'),
128
+ created: Schema.optional(Schema.String),
129
+ updated: Schema.optional(Schema.String),
130
+ insertions: Schema.optional(Schema.Number),
131
+ deletions: Schema.optional(Schema.Number),
132
+ _number: Schema.Number,
133
+ owner: Schema.optional(
134
+ Schema.Struct({
135
+ _account_id: Schema.Number,
136
+ name: Schema.optional(Schema.String),
137
+ email: Schema.optional(Schema.String),
138
+ username: Schema.optional(Schema.String),
139
+ }),
140
+ ),
141
+ labels: Schema.optional(
142
+ Schema.Record({
143
+ key: Schema.String,
144
+ value: Schema.Struct({
145
+ approved: Schema.optional(
146
+ Schema.Struct({
147
+ _account_id: Schema.Number,
148
+ name: Schema.optional(Schema.String),
149
+ email: Schema.optional(Schema.String),
150
+ username: Schema.optional(Schema.String),
151
+ }),
152
+ ),
153
+ rejected: Schema.optional(
154
+ Schema.Struct({
155
+ _account_id: Schema.Number,
156
+ name: Schema.optional(Schema.String),
157
+ email: Schema.optional(Schema.String),
158
+ username: Schema.optional(Schema.String),
159
+ }),
160
+ ),
161
+ recommended: Schema.optional(
162
+ Schema.Struct({
163
+ _account_id: Schema.Number,
164
+ name: Schema.optional(Schema.String),
165
+ email: Schema.optional(Schema.String),
166
+ username: Schema.optional(Schema.String),
167
+ }),
168
+ ),
169
+ disliked: Schema.optional(
170
+ Schema.Struct({
171
+ _account_id: Schema.Number,
172
+ name: Schema.optional(Schema.String),
173
+ email: Schema.optional(Schema.String),
174
+ username: Schema.optional(Schema.String),
175
+ }),
176
+ ),
177
+ value: Schema.optional(Schema.Number),
178
+ }),
179
+ }),
180
+ ),
181
+ submittable: Schema.optional(Schema.Boolean),
182
+ work_in_progress: Schema.optional(Schema.Boolean),
183
+ current_revision: Schema.optional(Schema.String),
184
+ revisions: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
185
+ })
186
+ export type ChangeInfo = Schema.Schema.Type<typeof ChangeInfo>
187
+
188
+ // Comment schemas
189
+ export const CommentInput: Schema.Schema<{
190
+ readonly message: string
191
+ readonly unresolved?: boolean
192
+ }> = Schema.Struct({
193
+ message: Schema.String.pipe(
194
+ Schema.minLength(1),
195
+ Schema.annotations({ description: 'Comment message' }),
196
+ ),
197
+ unresolved: Schema.optional(Schema.Boolean),
198
+ })
199
+ export type CommentInput = Schema.Schema.Type<typeof CommentInput>
200
+
201
+ // Comment info returned from API
202
+ export const CommentInfo: Schema.Schema<{
203
+ readonly id: string
204
+ readonly path?: string
205
+ readonly line?: number
206
+ readonly range?: {
207
+ readonly start_line: number
208
+ readonly end_line: number
209
+ readonly start_character?: number
210
+ readonly end_character?: number
211
+ }
212
+ readonly message: string
213
+ readonly author?: {
214
+ readonly name?: string
215
+ readonly email?: string
216
+ readonly _account_id?: number
217
+ }
218
+ readonly updated?: string
219
+ readonly unresolved?: boolean
220
+ readonly in_reply_to?: string
221
+ }> = Schema.Struct({
222
+ id: Schema.String,
223
+ path: Schema.optional(Schema.String),
224
+ line: Schema.optional(Schema.Number),
225
+ range: Schema.optional(
226
+ Schema.Struct({
227
+ start_line: Schema.Number,
228
+ end_line: Schema.Number,
229
+ start_character: Schema.optional(Schema.Number),
230
+ end_character: Schema.optional(Schema.Number),
231
+ }),
232
+ ),
233
+ message: Schema.String,
234
+ author: Schema.optional(
235
+ Schema.Struct({
236
+ name: Schema.optional(Schema.String),
237
+ email: Schema.optional(Schema.String),
238
+ _account_id: Schema.optional(Schema.Number),
239
+ }),
240
+ ),
241
+ updated: Schema.optional(Schema.String),
242
+ unresolved: Schema.optional(Schema.Boolean),
243
+ in_reply_to: Schema.optional(Schema.String),
244
+ })
245
+ export type CommentInfo = Schema.Schema.Type<typeof CommentInfo>
246
+
247
+ // Message info for review messages
248
+ export const MessageInfo: Schema.Schema<{
249
+ readonly id: string
250
+ readonly message: string
251
+ readonly author?: {
252
+ readonly _account_id: number
253
+ readonly name?: string
254
+ readonly email?: string
255
+ }
256
+ readonly date: string
257
+ readonly _revision_number?: number
258
+ readonly tag?: string
259
+ }> = Schema.Struct({
260
+ id: Schema.String,
261
+ message: Schema.String,
262
+ author: Schema.optional(
263
+ Schema.Struct({
264
+ _account_id: Schema.Number,
265
+ name: Schema.optional(Schema.String),
266
+ email: Schema.optional(Schema.String),
267
+ }),
268
+ ),
269
+ date: Schema.String,
270
+ _revision_number: Schema.optional(Schema.Number),
271
+ tag: Schema.optional(Schema.String),
272
+ })
273
+ export type MessageInfo = Schema.Schema.Type<typeof MessageInfo>
274
+
275
+ export const ReviewInput: Schema.Schema<{
276
+ readonly message?: string
277
+ readonly labels?: Record<string, number>
278
+ readonly comments?: Record<
279
+ string,
280
+ ReadonlyArray<{
281
+ readonly line?: number
282
+ readonly range?: {
283
+ readonly start_line: number
284
+ readonly end_line: number
285
+ readonly start_character?: number
286
+ readonly end_character?: number
287
+ }
288
+ readonly message: string
289
+ readonly side?: 'PARENT' | 'REVISION'
290
+ readonly unresolved?: boolean
291
+ }>
292
+ >
293
+ }> = Schema.Struct({
294
+ message: Schema.optional(Schema.String),
295
+ labels: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Number })),
296
+ comments: Schema.optional(
297
+ Schema.Record({
298
+ key: Schema.String,
299
+ value: Schema.Array(
300
+ Schema.Struct({
301
+ line: Schema.optional(Schema.Number),
302
+ range: Schema.optional(
303
+ Schema.Struct({
304
+ start_line: Schema.Number,
305
+ end_line: Schema.Number,
306
+ start_character: Schema.optional(Schema.Number),
307
+ end_character: Schema.optional(Schema.Number),
308
+ }),
309
+ ),
310
+ message: Schema.String,
311
+ side: Schema.optional(Schema.Literal('PARENT', 'REVISION')),
312
+ unresolved: Schema.optional(Schema.Boolean),
313
+ }),
314
+ ),
315
+ }),
316
+ ),
317
+ })
318
+ export type ReviewInput = Schema.Schema.Type<typeof ReviewInput>
319
+
320
+ // Project schema
321
+ export const ProjectInfo: Schema.Schema<{
322
+ readonly id: string
323
+ readonly name: string
324
+ readonly parent?: string
325
+ readonly state?: 'ACTIVE' | 'READ_ONLY' | 'HIDDEN'
326
+ }> = Schema.Struct({
327
+ id: Schema.String,
328
+ name: Schema.String,
329
+ parent: Schema.optional(Schema.String),
330
+ state: Schema.optional(Schema.Literal('ACTIVE', 'READ_ONLY', 'HIDDEN')),
331
+ })
332
+ export type ProjectInfo = Schema.Schema.Type<typeof ProjectInfo>
333
+
334
+ // File and diff schemas
335
+ export const FileInfo: Schema.Schema<{
336
+ readonly status?: 'A' | 'D' | 'R' | 'C' | 'M'
337
+ readonly lines_inserted?: number
338
+ readonly lines_deleted?: number
339
+ readonly size?: number
340
+ readonly size_delta?: number
341
+ readonly old_path?: string
342
+ }> = Schema.Struct({
343
+ status: Schema.optional(Schema.Literal('A', 'D', 'R', 'C', 'M')), // Added, Deleted, Renamed, Copied, Modified
344
+ lines_inserted: Schema.optional(Schema.Number),
345
+ lines_deleted: Schema.optional(Schema.Number),
346
+ size_delta: Schema.optional(Schema.Number),
347
+ size: Schema.optional(Schema.Number),
348
+ old_path: Schema.optional(Schema.String),
349
+ })
350
+ export type FileInfo = Schema.Schema.Type<typeof FileInfo>
351
+
352
+ export const FileDiffContent: Schema.Schema<{
353
+ readonly a?: string
354
+ readonly b?: string
355
+ readonly content: ReadonlyArray<{
356
+ readonly a?: ReadonlyArray<string>
357
+ readonly b?: ReadonlyArray<string>
358
+ readonly ab?: ReadonlyArray<string>
359
+ readonly edit_list?: ReadonlyArray<{
360
+ readonly op: 'i' | 'd' | 'r'
361
+ readonly a: ReadonlyArray<string>
362
+ readonly b: ReadonlyArray<string>
363
+ }>
364
+ readonly due_to_rebase?: boolean
365
+ readonly skip?: number
366
+ }>
367
+ readonly change_type?: 'ADDED' | 'MODIFIED' | 'DELETED' | 'RENAMED' | 'COPIED'
368
+ readonly diff_header?: ReadonlyArray<string>
369
+ }> = Schema.Struct({
370
+ a: Schema.optional(Schema.String), // Old file content path
371
+ b: Schema.optional(Schema.String), // New file content path
372
+ content: Schema.Array(
373
+ Schema.Struct({
374
+ a: Schema.optional(Schema.Array(Schema.String)), // Lines from old file
375
+ b: Schema.optional(Schema.Array(Schema.String)), // Lines from new file
376
+ ab: Schema.optional(Schema.Array(Schema.String)), // Common lines
377
+ edit_list: Schema.optional(
378
+ Schema.Array(
379
+ Schema.Struct({
380
+ op: Schema.Literal('i', 'd', 'r'), // insert, delete, replace
381
+ a: Schema.Array(Schema.String),
382
+ b: Schema.Array(Schema.String),
383
+ }),
384
+ ),
385
+ ),
386
+ due_to_rebase: Schema.optional(Schema.Boolean),
387
+ skip: Schema.optional(Schema.Number),
388
+ }),
389
+ ),
390
+ change_type: Schema.optional(Schema.Literal('ADDED', 'MODIFIED', 'DELETED', 'RENAMED', 'COPIED')),
391
+ diff_header: Schema.optional(Schema.Array(Schema.String)),
392
+ })
393
+ export type FileDiffContent = Schema.Schema.Type<typeof FileDiffContent>
394
+
395
+ export const RevisionInfo: Schema.Schema<{
396
+ readonly kind?: string
397
+ readonly _number: number
398
+ readonly created: string
399
+ readonly uploader: {
400
+ readonly _account_id: number
401
+ readonly name?: string
402
+ readonly email?: string
403
+ }
404
+ readonly ref: string
405
+ readonly fetch?: Record<string, unknown>
406
+ readonly commit?: {
407
+ readonly commit: string
408
+ readonly parents: ReadonlyArray<{
409
+ readonly commit: string
410
+ readonly subject: string
411
+ }>
412
+ readonly author: {
413
+ readonly name: string
414
+ readonly email: string
415
+ readonly date: string
416
+ }
417
+ readonly committer: {
418
+ readonly name: string
419
+ readonly email: string
420
+ readonly date: string
421
+ }
422
+ readonly subject: string
423
+ readonly message: string
424
+ }
425
+ readonly files?: Record<
426
+ string,
427
+ {
428
+ readonly status?: 'A' | 'D' | 'R' | 'C' | 'M'
429
+ readonly lines_inserted?: number
430
+ readonly lines_deleted?: number
431
+ readonly size?: number
432
+ readonly size_delta?: number
433
+ readonly old_path?: string
434
+ }
435
+ >
436
+ }> = Schema.Struct({
437
+ kind: Schema.optional(Schema.String),
438
+ _number: Schema.Number,
439
+ created: Schema.String,
440
+ uploader: Schema.Struct({
441
+ _account_id: Schema.Number,
442
+ name: Schema.optional(Schema.String),
443
+ email: Schema.optional(Schema.String),
444
+ }),
445
+ ref: Schema.String,
446
+ fetch: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
447
+ commit: Schema.optional(
448
+ Schema.Struct({
449
+ commit: Schema.String,
450
+ parents: Schema.Array(
451
+ Schema.Struct({
452
+ commit: Schema.String,
453
+ subject: Schema.String,
454
+ }),
455
+ ),
456
+ author: Schema.Struct({
457
+ name: Schema.String,
458
+ email: Schema.String,
459
+ date: Schema.String,
460
+ }),
461
+ committer: Schema.Struct({
462
+ name: Schema.String,
463
+ email: Schema.String,
464
+ date: Schema.String,
465
+ }),
466
+ subject: Schema.String,
467
+ message: Schema.String,
468
+ }),
469
+ ),
470
+ files: Schema.optional(Schema.Record({ key: Schema.String, value: FileInfo })),
471
+ })
472
+ export type RevisionInfo = Schema.Schema.Type<typeof RevisionInfo>
473
+
474
+ // Diff output format options
475
+ export const DiffFormat: Schema.Schema<'unified' | 'json' | 'files'> = Schema.Literal(
476
+ 'unified',
477
+ 'json',
478
+ 'files',
479
+ )
480
+ export type DiffFormat = Schema.Schema.Type<typeof DiffFormat>
481
+
482
+ export const DiffOptions: Schema.Schema<{
483
+ readonly format?: 'unified' | 'json' | 'files'
484
+ readonly patchset?: number
485
+ readonly file?: string
486
+ readonly filesOnly?: boolean
487
+ readonly fullFiles?: boolean
488
+ readonly base?: number
489
+ readonly target?: number
490
+ }> = Schema.Struct({
491
+ format: Schema.optional(DiffFormat),
492
+ patchset: Schema.optional(Schema.Number),
493
+ file: Schema.optional(Schema.String),
494
+ filesOnly: Schema.optional(Schema.Boolean),
495
+ fullFiles: Schema.optional(Schema.Boolean),
496
+ base: Schema.optional(Schema.Number),
497
+ target: Schema.optional(Schema.Number),
498
+ })
499
+ export type DiffOptions = Schema.Schema.Type<typeof DiffOptions>
500
+
501
+ // Command options schemas
502
+ export const DiffCommandOptions: Schema.Schema<{
503
+ readonly xml?: boolean
504
+ readonly file?: string
505
+ readonly filesOnly?: boolean
506
+ readonly format?: 'unified' | 'json' | 'files'
507
+ }> = Schema.Struct({
508
+ xml: Schema.optional(Schema.Boolean),
509
+ file: Schema.optional(
510
+ Schema.String.pipe(
511
+ Schema.minLength(1),
512
+ Schema.annotations({ description: 'File path for diff (relative to repo root)' }),
513
+ ),
514
+ ),
515
+ filesOnly: Schema.optional(Schema.Boolean),
516
+ format: Schema.optional(DiffFormat),
517
+ })
518
+ export type DiffCommandOptions = Schema.Schema.Type<typeof DiffCommandOptions>
519
+
520
+ // Reviewer schemas
521
+ export const ReviewerInput: Schema.Schema<{
522
+ readonly reviewer: string
523
+ readonly state?: 'REVIEWER' | 'CC' | 'REMOVED'
524
+ readonly confirmed?: boolean
525
+ readonly notify?: 'NONE' | 'OWNER' | 'OWNER_REVIEWERS' | 'ALL'
526
+ }> = Schema.Struct({
527
+ reviewer: Schema.String,
528
+ state: Schema.optional(Schema.Literal('REVIEWER', 'CC', 'REMOVED')),
529
+ confirmed: Schema.optional(Schema.Boolean),
530
+ notify: Schema.optional(Schema.Literal('NONE', 'OWNER', 'OWNER_REVIEWERS', 'ALL')),
531
+ })
532
+ export type ReviewerInput = Schema.Schema.Type<typeof ReviewerInput>
533
+
534
+ const ReviewerAccountInfo = Schema.Struct({
535
+ _account_id: Schema.Number,
536
+ name: Schema.optional(Schema.String),
537
+ email: Schema.optional(Schema.String),
538
+ })
539
+
540
+ export const ReviewerResult: Schema.Schema<{
541
+ readonly input: string
542
+ readonly reviewers?: ReadonlyArray<{
543
+ readonly _account_id: number
544
+ readonly name?: string
545
+ readonly email?: string
546
+ }>
547
+ readonly ccs?: ReadonlyArray<{
548
+ readonly _account_id: number
549
+ readonly name?: string
550
+ readonly email?: string
551
+ }>
552
+ readonly error?: string
553
+ readonly confirm?: boolean
554
+ }> = Schema.Struct({
555
+ input: Schema.String,
556
+ reviewers: Schema.optional(Schema.Array(ReviewerAccountInfo)),
557
+ ccs: Schema.optional(Schema.Array(ReviewerAccountInfo)),
558
+ error: Schema.optional(Schema.String),
559
+ confirm: Schema.optional(Schema.Boolean),
560
+ })
561
+ export type ReviewerResult = Schema.Schema.Type<typeof ReviewerResult>
562
+
563
+ // Rebase schemas
564
+ export const RebaseInput: Schema.Schema<{
565
+ readonly base?: string
566
+ }> = Schema.Struct({
567
+ base: Schema.optional(Schema.String),
568
+ })
569
+ export type RebaseInput = Schema.Schema.Type<typeof RebaseInput>
570
+
571
+ // Submit schemas
572
+ export const SubmitInfo: Schema.Schema<{
573
+ readonly status: 'MERGED' | 'SUBMITTED'
574
+ readonly change_id?: string
575
+ }> = Schema.Struct({
576
+ status: Schema.Literal('MERGED', 'SUBMITTED'),
577
+ change_id: Schema.optional(Schema.String),
578
+ })
579
+ export type SubmitInfo = Schema.Schema.Type<typeof SubmitInfo>
580
+
581
+ // API Response schemas
582
+ export const GerritError: Schema.Schema<{
583
+ readonly message: string
584
+ readonly status?: number
585
+ }> = Schema.Struct({
586
+ message: Schema.String,
587
+ status: Schema.optional(Schema.Number),
588
+ })
589
+ export type GerritError = Schema.Schema.Type<typeof GerritError>
590
+
591
+ // Account/User schema (reusable for groups and reviewers)
592
+ export const AccountInfo: Schema.Schema<{
593
+ readonly _account_id: number
594
+ readonly name?: string
595
+ readonly email?: string
596
+ readonly username?: string
597
+ }> = Schema.Struct({
598
+ _account_id: Schema.Number,
599
+ name: Schema.optional(Schema.String),
600
+ email: Schema.optional(Schema.String),
601
+ username: Schema.optional(Schema.String),
602
+ })
603
+ export type AccountInfo = Schema.Schema.Type<typeof AccountInfo>
604
+
605
+ // Groups schemas
606
+ export const GroupInfo: Schema.Schema<{
607
+ readonly id: string
608
+ readonly name?: string
609
+ readonly url?: string
610
+ readonly options?: {
611
+ readonly visible_to_all?: boolean
612
+ }
613
+ readonly description?: string
614
+ readonly group_id?: number
615
+ readonly owner?: string
616
+ readonly owner_id?: string
617
+ readonly created_on?: string
618
+ }> = Schema.Struct({
619
+ id: Schema.String,
620
+ name: Schema.optional(Schema.String),
621
+ url: Schema.optional(Schema.String),
622
+ options: Schema.optional(
623
+ Schema.Struct({
624
+ visible_to_all: Schema.optional(Schema.Boolean),
625
+ }),
626
+ ),
627
+ description: Schema.optional(Schema.String),
628
+ group_id: Schema.optional(Schema.Number),
629
+ owner: Schema.optional(Schema.String),
630
+ owner_id: Schema.optional(Schema.String),
631
+ created_on: Schema.optional(Schema.String),
632
+ })
633
+ export type GroupInfo = Schema.Schema.Type<typeof GroupInfo>
634
+
635
+ export const GroupDetailInfo: Schema.Schema<{
636
+ readonly id: string
637
+ readonly name?: string
638
+ readonly url?: string
639
+ readonly options?: {
640
+ readonly visible_to_all?: boolean
641
+ }
642
+ readonly description?: string
643
+ readonly group_id?: number
644
+ readonly owner?: string
645
+ readonly owner_id?: string
646
+ readonly created_on?: string
647
+ readonly members?: ReadonlyArray<{
648
+ readonly _account_id: number
649
+ readonly name?: string
650
+ readonly email?: string
651
+ readonly username?: string
652
+ }>
653
+ readonly includes?: ReadonlyArray<{
654
+ readonly id: string
655
+ readonly name?: string
656
+ }>
657
+ }> = Schema.Struct({
658
+ id: Schema.String,
659
+ name: Schema.optional(Schema.String),
660
+ url: Schema.optional(Schema.String),
661
+ options: Schema.optional(
662
+ Schema.Struct({
663
+ visible_to_all: Schema.optional(Schema.Boolean),
664
+ }),
665
+ ),
666
+ description: Schema.optional(Schema.String),
667
+ group_id: Schema.optional(Schema.Number),
668
+ owner: Schema.optional(Schema.String),
669
+ owner_id: Schema.optional(Schema.String),
670
+ created_on: Schema.optional(Schema.String),
671
+ members: Schema.optional(Schema.Array(AccountInfo)),
672
+ includes: Schema.optional(
673
+ Schema.Array(
674
+ Schema.Struct({
675
+ id: Schema.String,
676
+ name: Schema.optional(Schema.String),
677
+ }),
678
+ ),
679
+ ),
680
+ })
681
+ export type GroupDetailInfo = Schema.Schema.Type<typeof GroupDetailInfo>