@aaronshaf/ger 1.2.10 → 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 -180
  178. package/src/ger.ts +0 -22
  179. package/src/types.d.ts +0 -35
  180. package/src/utils.ts +0 -130
package/EXAMPLES.md ADDED
@@ -0,0 +1,457 @@
1
+ # Programmatic Usage Examples
2
+
3
+ This package can be used both as a CLI tool and as a library. Below are examples of using `@aaronshaf/ger` programmatically with Effect-TS.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @aaronshaf/ger
9
+ # or
10
+ npm install @aaronshaf/ger
11
+ ```
12
+
13
+ ## Basic Setup
14
+
15
+ All services in this package are built with Effect-TS, providing type-safe, composable operations.
16
+
17
+ ### Import the services
18
+
19
+ ```typescript
20
+ import { Effect, pipe } from 'effect'
21
+ import {
22
+ GerritApiService,
23
+ GerritApiServiceLive,
24
+ ConfigServiceLive,
25
+ type ChangeInfo,
26
+ } from '@aaronshaf/ger'
27
+ ```
28
+
29
+ ## Configuration
30
+
31
+ ### Using Environment Variables
32
+
33
+ Set these environment variables before running your program:
34
+
35
+ ```bash
36
+ export GERRIT_HOST="https://gerrit.example.com"
37
+ export GERRIT_USERNAME="your-username"
38
+ export GERRIT_PASSWORD="your-http-password"
39
+ ```
40
+
41
+ ### Using File-Based Config
42
+
43
+ Or run the CLI once to set up configuration:
44
+
45
+ ```bash
46
+ ger setup
47
+ ```
48
+
49
+ This stores credentials in `~/.ger/config.json`.
50
+
51
+ ## Examples
52
+
53
+ ### 1. Get Change Information
54
+
55
+ ```typescript
56
+ import { Effect, pipe } from 'effect'
57
+ import {
58
+ GerritApiService,
59
+ GerritApiServiceLive,
60
+ ConfigServiceLive,
61
+ } from '@aaronshaf/ger'
62
+
63
+ const getChangeDetails = (changeId: string) =>
64
+ Effect.gen(function* () {
65
+ const api = yield* GerritApiService
66
+ const change = yield* api.getChange(changeId)
67
+
68
+ console.log(`Change: ${change.subject}`)
69
+ console.log(`Status: ${change.status}`)
70
+ console.log(`Owner: ${change.owner?.name || 'Unknown'}`)
71
+
72
+ return change
73
+ })
74
+
75
+ // Run the program
76
+ const program = pipe(
77
+ getChangeDetails('12345'),
78
+ Effect.provide(GerritApiServiceLive),
79
+ Effect.provide(ConfigServiceLive)
80
+ )
81
+
82
+ Effect.runPromise(program)
83
+ .then(() => console.log('Done!'))
84
+ .catch(console.error)
85
+ ```
86
+
87
+ ### 2. List Open Changes
88
+
89
+ ```typescript
90
+ import { Effect, pipe } from 'effect'
91
+ import {
92
+ GerritApiService,
93
+ GerritApiServiceLive,
94
+ ConfigServiceLive,
95
+ } from '@aaronshaf/ger'
96
+
97
+ const listMyChanges = Effect.gen(function* () {
98
+ const api = yield* GerritApiService
99
+
100
+ // Query for your open changes
101
+ const changes = yield* api.listChanges('is:open owner:self')
102
+
103
+ console.log(`You have ${changes.length} open changes:`)
104
+ for (const change of changes) {
105
+ console.log(` - #${change._number}: ${change.subject}`)
106
+ }
107
+
108
+ return changes
109
+ })
110
+
111
+ const program = pipe(
112
+ listMyChanges,
113
+ Effect.provide(GerritApiServiceLive),
114
+ Effect.provide(ConfigServiceLive)
115
+ )
116
+
117
+ Effect.runPromise(program).catch(console.error)
118
+ ```
119
+
120
+ ### 2b. Search Changes with Query Syntax
121
+
122
+ ```typescript
123
+ import { Effect, pipe } from 'effect'
124
+ import {
125
+ GerritApiService,
126
+ GerritApiServiceLive,
127
+ ConfigServiceLive,
128
+ } from '@aaronshaf/ger'
129
+
130
+ const searchChanges = (query: string, limit = 25) =>
131
+ Effect.gen(function* () {
132
+ const api = yield* GerritApiService
133
+
134
+ // Use Gerrit query syntax to search changes
135
+ const fullQuery = query.includes('limit:') ? query : `${query} limit:${limit}`
136
+ const changes = yield* api.listChanges(fullQuery)
137
+
138
+ // Group by project for organized output
139
+ const byProject = new Map<string, typeof changes>()
140
+ for (const change of changes) {
141
+ const existing = byProject.get(change.project) ?? []
142
+ existing.push(change)
143
+ byProject.set(change.project, existing)
144
+ }
145
+
146
+ console.log(`Found ${changes.length} changes:`)
147
+ for (const [project, projectChanges] of byProject) {
148
+ console.log(`\n${project}:`)
149
+ for (const change of projectChanges) {
150
+ console.log(` #${change._number} - ${change.subject} (${change.status})`)
151
+ }
152
+ }
153
+
154
+ return changes
155
+ })
156
+
157
+ // Example queries
158
+ const program = pipe(
159
+ // Search for merged changes in the last week
160
+ searchChanges('status:merged age:7d', 10),
161
+ Effect.provide(GerritApiServiceLive),
162
+ Effect.provide(ConfigServiceLive)
163
+ )
164
+
165
+ Effect.runPromise(program).catch(console.error)
166
+ ```
167
+
168
+ ### 3. Post a Comment
169
+
170
+ ```typescript
171
+ import { Effect, pipe } from 'effect'
172
+ import {
173
+ GerritApiService,
174
+ GerritApiServiceLive,
175
+ ConfigServiceLive,
176
+ type ReviewInput,
177
+ } from '@aaronshaf/ger'
178
+
179
+ const postComment = (changeId: string) =>
180
+ Effect.gen(function* () {
181
+ const api = yield* GerritApiService
182
+
183
+ const review: ReviewInput = {
184
+ message: 'Looks good to me!',
185
+ labels: {
186
+ 'Code-Review': 1,
187
+ },
188
+ }
189
+
190
+ yield* api.postReview(changeId, review)
191
+ console.log('Comment posted successfully!')
192
+ })
193
+
194
+ const program = pipe(
195
+ postComment('12345'),
196
+ Effect.provide(GerritApiServiceLive),
197
+ Effect.provide(ConfigServiceLive)
198
+ )
199
+
200
+ Effect.runPromise(program).catch(console.error)
201
+ ```
202
+
203
+ ### 4. Post Inline Comments
204
+
205
+ ```typescript
206
+ import { Effect, pipe } from 'effect'
207
+ import {
208
+ GerritApiService,
209
+ GerritApiServiceLive,
210
+ ConfigServiceLive,
211
+ type ReviewInput,
212
+ } from '@aaronshaf/ger'
213
+
214
+ const postInlineComments = (changeId: string) =>
215
+ Effect.gen(function* () {
216
+ const api = yield* GerritApiService
217
+
218
+ const review: ReviewInput = {
219
+ message: 'Review complete',
220
+ comments: {
221
+ 'src/api.ts': [
222
+ {
223
+ line: 42,
224
+ message: 'Consider using const here for immutability',
225
+ unresolved: false,
226
+ },
227
+ {
228
+ line: 55,
229
+ message: 'This could cause a security issue',
230
+ unresolved: true,
231
+ },
232
+ ],
233
+ 'src/utils.ts': [
234
+ {
235
+ line: 10,
236
+ message: 'Nice refactor!',
237
+ },
238
+ ],
239
+ },
240
+ }
241
+
242
+ yield* api.postReview(changeId, review)
243
+ console.log('Inline comments posted!')
244
+ })
245
+
246
+ const program = pipe(
247
+ postInlineComments('12345'),
248
+ Effect.provide(GerritApiServiceLive),
249
+ Effect.provide(ConfigServiceLive)
250
+ )
251
+
252
+ Effect.runPromise(program).catch(console.error)
253
+ ```
254
+
255
+ ### 5. Get Diff for a Change
256
+
257
+ ```typescript
258
+ import { Effect, pipe } from 'effect'
259
+ import {
260
+ GerritApiService,
261
+ GerritApiServiceLive,
262
+ ConfigServiceLive,
263
+ type DiffOptions,
264
+ } from '@aaronshaf/ger'
265
+
266
+ const getDiff = (changeId: string) =>
267
+ Effect.gen(function* () {
268
+ const api = yield* GerritApiService
269
+
270
+ // Get unified diff format (default)
271
+ const diff = yield* api.getDiff(changeId, { format: 'unified' })
272
+ console.log('Diff:', diff)
273
+
274
+ // Or get list of changed files
275
+ const files = yield* api.getDiff(changeId, { format: 'files' })
276
+ console.log('Changed files:', files)
277
+
278
+ return diff
279
+ })
280
+
281
+ const program = pipe(
282
+ getDiff('12345'),
283
+ Effect.provide(GerritApiServiceLive),
284
+ Effect.provide(ConfigServiceLive)
285
+ )
286
+
287
+ Effect.runPromise(program).catch(console.error)
288
+ ```
289
+
290
+ ### 6. Test Connection
291
+
292
+ ```typescript
293
+ import { Effect, pipe } from 'effect'
294
+ import {
295
+ GerritApiService,
296
+ GerritApiServiceLive,
297
+ ConfigServiceLive,
298
+ } from '@aaronshaf/ger'
299
+
300
+ const testConnection = Effect.gen(function* () {
301
+ const api = yield* GerritApiService
302
+ const isConnected = yield* api.testConnection
303
+
304
+ if (isConnected) {
305
+ console.log('✓ Connected to Gerrit!')
306
+ } else {
307
+ console.log('✗ Connection failed')
308
+ }
309
+
310
+ return isConnected
311
+ })
312
+
313
+ const program = pipe(
314
+ testConnection,
315
+ Effect.provide(GerritApiServiceLive),
316
+ Effect.provide(ConfigServiceLive)
317
+ )
318
+
319
+ Effect.runPromise(program).catch(console.error)
320
+ ```
321
+
322
+ ### 7. Error Handling with Effect
323
+
324
+ ```typescript
325
+ import { Effect, pipe, Console } from 'effect'
326
+ import {
327
+ GerritApiService,
328
+ GerritApiServiceLive,
329
+ ConfigServiceLive,
330
+ ApiError,
331
+ ConfigError,
332
+ } from '@aaronshaf/ger'
333
+
334
+ const safeGetChange = (changeId: string) =>
335
+ Effect.gen(function* () {
336
+ const api = yield* GerritApiService
337
+ const change = yield* api.getChange(changeId)
338
+ return change
339
+ }).pipe(
340
+ Effect.catchTag('ApiError', (error) =>
341
+ Console.error(`API Error: ${error.message}`).pipe(
342
+ Effect.map(() => null)
343
+ )
344
+ ),
345
+ Effect.catchTag('ConfigError', (error) =>
346
+ Console.error(`Config Error: ${error.message}`).pipe(
347
+ Effect.map(() => null)
348
+ )
349
+ )
350
+ )
351
+
352
+ const program = pipe(
353
+ safeGetChange('invalid-change'),
354
+ Effect.provide(GerritApiServiceLive),
355
+ Effect.provide(ConfigServiceLive)
356
+ )
357
+
358
+ Effect.runPromise(program)
359
+ ```
360
+
361
+ ### 8. Using Utilities
362
+
363
+ ```typescript
364
+ import {
365
+ normalizeChangeIdentifier,
366
+ extractChangeIdFromCommitMessage,
367
+ extractChangeNumber,
368
+ normalizeGerritHost,
369
+ } from '@aaronshaf/ger'
370
+
371
+ // Normalize change identifiers
372
+ const normalized = normalizeChangeIdentifier('12345')
373
+ // or
374
+ const normalizedId = normalizeChangeIdentifier('If5a3ae8cb5a107e187447802358417f311d0c4b1')
375
+
376
+ // Extract change ID from commit message
377
+ const commitMsg = `feat: add feature
378
+
379
+ Change-Id: If5a3ae8cb5a107e187447802358417f311d0c4b1`
380
+
381
+ const changeId = extractChangeIdFromCommitMessage(commitMsg)
382
+ console.log(changeId) // "If5a3ae8cb5a107e187447802358417f311d0c4b1"
383
+
384
+ // Extract change number from Gerrit URL
385
+ const url = 'https://gerrit.example.com/c/project/+/12345'
386
+ const changeNumber = extractChangeNumber(url)
387
+ console.log(changeNumber) // "12345"
388
+
389
+ // Normalize Gerrit host
390
+ const host = normalizeGerritHost('gerrit.example.com')
391
+ console.log(host) // "https://gerrit.example.com"
392
+ ```
393
+
394
+ ### 9. Working with Schemas
395
+
396
+ ```typescript
397
+ import { Schema } from '@effect/schema'
398
+ import { Effect } from 'effect'
399
+ import { ChangeInfo, ReviewInput } from '@aaronshaf/ger'
400
+
401
+ // Validate and decode API responses
402
+ const validateChange = (data: unknown) =>
403
+ Schema.decodeUnknown(ChangeInfo)(data)
404
+
405
+ // Validate review input before sending
406
+ const validateReview = (review: unknown) =>
407
+ Schema.decodeUnknown(ReviewInput)(review)
408
+
409
+ // Use in an Effect program
410
+ const safeReview = Effect.gen(function* () {
411
+ const review = {
412
+ message: 'LGTM',
413
+ labels: { 'Code-Review': 2 },
414
+ }
415
+
416
+ const validated = yield* validateReview(review)
417
+ console.log('Review is valid:', validated)
418
+
419
+ return validated
420
+ })
421
+ ```
422
+
423
+ ## Direct Module Access
424
+
425
+ You can also import directly from specific modules:
426
+
427
+ ```typescript
428
+ // Import from specific services
429
+ import { GerritApiService, GerritApiServiceLive } from '@aaronshaf/ger/api'
430
+ import { ConfigService, ConfigServiceLive } from '@aaronshaf/ger/services/config'
431
+
432
+ // Import from specific schemas
433
+ import { ChangeInfo, ReviewInput } from '@aaronshaf/ger/schemas/gerrit'
434
+
435
+ // Import utilities
436
+ import { normalizeChangeIdentifier, extractChangeNumber } from '@aaronshaf/ger/utils'
437
+ ```
438
+
439
+ ## TypeScript Configuration
440
+
441
+ Make sure your `tsconfig.json` includes:
442
+
443
+ ```json
444
+ {
445
+ "compilerOptions": {
446
+ "moduleResolution": "bundler",
447
+ "allowImportingTsExtensions": true,
448
+ "strict": true
449
+ }
450
+ }
451
+ ```
452
+
453
+ ## More Information
454
+
455
+ - See the [main README](./README.md) for CLI usage
456
+ - Check out the [Effect documentation](https://effect.website/) to learn more about Effect-TS
457
+ - View the type definitions in your IDE for detailed API documentation