@aaronshaf/ger 0.1.3 → 0.1.5
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/bun.lock +0 -25
- package/package.json +1 -2
- package/src/cli/commands/review.ts +1 -1
- package/src/cli/index.ts +3 -6
- package/src/services/review-strategy.ts +3 -107
package/bun.lock
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
"": {
|
|
5
5
|
"name": "ger",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@anthropic-ai/claude-code": "^1.0.102",
|
|
8
7
|
"@effect/platform": "^0.90.6",
|
|
9
8
|
"@effect/platform-node": "^0.94.2",
|
|
10
9
|
"@effect/schema": "^0.75.5",
|
|
@@ -32,8 +31,6 @@
|
|
|
32
31
|
},
|
|
33
32
|
},
|
|
34
33
|
"packages": {
|
|
35
|
-
"@anthropic-ai/claude-code": ["@anthropic-ai/claude-code@1.0.102", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "bin": { "claude": "cli.js" } }, "sha512-UIC6qNgKNZi1nLTf1bQvxNfd74xIAqJjIx6vggh3bJOMtuXBiFwrfPk1Pdf9CayYgwZYXgSmxYYaASt6i6ficQ=="],
|
|
36
|
-
|
|
37
34
|
"@babel/code-frame": ["@babel/code-frame@7.0.0-beta.37", "", { "dependencies": { "chalk": "^2.0.0", "esutils": "^2.0.2", "js-tokens": "^3.0.0" } }, "sha512-LIpcKm+2otOOvOvhCbD6wkNYi8aUwHk73uWR+hxBdW2EFht5D0QX89n4me8nyeNGWr5zC3Pvmjq+9MvUof+jkg=="],
|
|
38
35
|
|
|
39
36
|
"@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="],
|
|
@@ -96,28 +93,6 @@
|
|
|
96
93
|
|
|
97
94
|
"@effect/workflow": ["@effect/workflow@0.8.3", "", { "peerDependencies": { "@effect/platform": "^0.90.0", "@effect/rpc": "^0.68.3", "effect": "^3.17.6" } }, "sha512-8X5IOemCb6I66GMd84w6NSmaQ+Ya3oXwItCUMelQAEuRtGzwqsw8PNNunQgK/poSRkmpszlsKOb6kEVNjSdFiQ=="],
|
|
98
95
|
|
|
99
|
-
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
|
|
100
|
-
|
|
101
|
-
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
|
|
102
|
-
|
|
103
|
-
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="],
|
|
104
|
-
|
|
105
|
-
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="],
|
|
106
|
-
|
|
107
|
-
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="],
|
|
108
|
-
|
|
109
|
-
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="],
|
|
110
|
-
|
|
111
|
-
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="],
|
|
112
|
-
|
|
113
|
-
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="],
|
|
114
|
-
|
|
115
|
-
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="],
|
|
116
|
-
|
|
117
|
-
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="],
|
|
118
|
-
|
|
119
|
-
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
|
|
120
|
-
|
|
121
96
|
"@inquirer/checkbox": ["@inquirer/checkbox@4.2.2", "", { "dependencies": { "@inquirer/core": "^10.2.0", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g=="],
|
|
122
97
|
|
|
123
98
|
"@inquirer/confirm": ["@inquirer/confirm@5.1.16", "", { "dependencies": { "@inquirer/core": "^10.2.0", "@inquirer/type": "^3.0.8" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag=="],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aaronshaf/ger",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"module": "index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
"typescript": "^5.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@anthropic-ai/claude-code": "^1.0.102",
|
|
33
32
|
"@effect/platform": "^0.90.6",
|
|
34
33
|
"@effect/platform-node": "^0.94.2",
|
|
35
34
|
"@effect/schema": "^0.75.5",
|
|
@@ -331,7 +331,7 @@ export const reviewCommand = (changeId: string, options: ReviewOptions = {}) =>
|
|
|
331
331
|
|
|
332
332
|
if (availableStrategies.length === 0) {
|
|
333
333
|
return yield* Effect.fail(
|
|
334
|
-
new Error('No AI tools available. Please install claude, gemini, or
|
|
334
|
+
new Error('No AI tools available. Please install claude, gemini, or opencode CLI.'),
|
|
335
335
|
)
|
|
336
336
|
}
|
|
337
337
|
|
package/src/cli/index.ts
CHANGED
|
@@ -368,15 +368,12 @@ program
|
|
|
368
368
|
.option('-y, --yes', 'Skip confirmation prompts when posting comments')
|
|
369
369
|
.option('--debug', 'Show debug output including AI responses')
|
|
370
370
|
.option('--prompt <file>', 'Path to custom review prompt file (e.g., ~/prompts/review.md)')
|
|
371
|
-
.option(
|
|
372
|
-
'--provider <provider>',
|
|
373
|
-
'Preferred AI provider (claude-sdk, claude, gemini, codex, opencode)',
|
|
374
|
-
)
|
|
371
|
+
.option('--provider <provider>', 'Preferred AI provider (claude-sdk, claude, gemini, opencode)')
|
|
375
372
|
.option('--system-prompt <prompt>', 'Custom system prompt for the AI')
|
|
376
373
|
.addHelpText(
|
|
377
374
|
'after',
|
|
378
375
|
`
|
|
379
|
-
This command uses AI (Claude SDK, claude CLI, gemini CLI,
|
|
376
|
+
This command uses AI (Claude SDK, claude CLI, gemini CLI, or opencode CLI) to review a Gerrit change.
|
|
380
377
|
It performs a two-stage review process:
|
|
381
378
|
|
|
382
379
|
1. Generates inline comments for specific code issues
|
|
@@ -387,7 +384,7 @@ Use --comment to post the review to Gerrit (with confirmation prompts).
|
|
|
387
384
|
Use --comment --yes to post without confirmation.
|
|
388
385
|
|
|
389
386
|
Requirements:
|
|
390
|
-
- One of these AI tools must be available: Claude SDK (ANTHROPIC_API_KEY), claude CLI, gemini CLI,
|
|
387
|
+
- One of these AI tools must be available: Claude SDK (ANTHROPIC_API_KEY), claude CLI, gemini CLI, or opencode CLI
|
|
391
388
|
- Gerrit credentials must be configured (run 'ger setup' first)
|
|
392
389
|
|
|
393
390
|
Examples:
|
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
import { Context, Data, Effect, Layer } from 'effect'
|
|
2
|
-
import { Console } from 'effect'
|
|
3
2
|
import { exec } from 'node:child_process'
|
|
4
3
|
import { promisify } from 'node:util'
|
|
5
4
|
|
|
6
5
|
const execAsync = promisify(exec)
|
|
7
6
|
|
|
8
|
-
// Dynamic import for Claude SDK
|
|
9
|
-
const getClaudeSDK = () =>
|
|
10
|
-
Effect.tryPromise({
|
|
11
|
-
try: () => import('@anthropic-ai/claude-code'),
|
|
12
|
-
catch: () => null,
|
|
13
|
-
}).pipe(Effect.orElseSucceed(() => null))
|
|
14
|
-
|
|
15
7
|
// Simple strategy focused only on review needs
|
|
16
8
|
export class ReviewStrategyError extends Data.TaggedError('ReviewStrategyError')<{
|
|
17
9
|
message: string
|
|
@@ -205,90 +197,6 @@ export const openCodeCliStrategy: ReviewStrategy = {
|
|
|
205
197
|
}),
|
|
206
198
|
}
|
|
207
199
|
|
|
208
|
-
export const codexCliStrategy: ReviewStrategy = {
|
|
209
|
-
name: 'Codex CLI',
|
|
210
|
-
isAvailable: () =>
|
|
211
|
-
Effect.gen(function* () {
|
|
212
|
-
const result = yield* Effect.tryPromise({
|
|
213
|
-
try: () => execAsync('which codex'),
|
|
214
|
-
catch: () => null,
|
|
215
|
-
}).pipe(Effect.orElseSucceed(() => null))
|
|
216
|
-
|
|
217
|
-
return Boolean(result && result.stdout.trim())
|
|
218
|
-
}),
|
|
219
|
-
executeReview: (prompt, options = {}) =>
|
|
220
|
-
Effect.gen(function* () {
|
|
221
|
-
const command = `codex exec "${prompt.replace(/"/g, '\\"')}"`
|
|
222
|
-
|
|
223
|
-
const result = yield* Effect.tryPromise({
|
|
224
|
-
try: () => execAsync(command, { cwd: options.cwd }),
|
|
225
|
-
catch: (error) =>
|
|
226
|
-
new ReviewStrategyError({
|
|
227
|
-
message: `Codex CLI failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
228
|
-
cause: error,
|
|
229
|
-
}),
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
return result.stdout.trim()
|
|
233
|
-
}),
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export const claudeSDKStrategy: ReviewStrategy = {
|
|
237
|
-
name: 'Claude SDK',
|
|
238
|
-
isAvailable: () =>
|
|
239
|
-
Effect.gen(function* () {
|
|
240
|
-
const sdk = yield* getClaudeSDK()
|
|
241
|
-
|
|
242
|
-
// Check if we have ANTHROPIC_API_KEY
|
|
243
|
-
const hasApiKey = Boolean(process.env.ANTHROPIC_API_KEY)
|
|
244
|
-
|
|
245
|
-
return Boolean(sdk && hasApiKey)
|
|
246
|
-
}),
|
|
247
|
-
executeReview: (prompt, options = {}) =>
|
|
248
|
-
Effect.gen(function* () {
|
|
249
|
-
const sdk = yield* getClaudeSDK()
|
|
250
|
-
|
|
251
|
-
if (!sdk) {
|
|
252
|
-
return yield* Effect.fail(
|
|
253
|
-
new ReviewStrategyError({
|
|
254
|
-
message: 'Claude SDK not available',
|
|
255
|
-
}),
|
|
256
|
-
)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const result = yield* Effect.tryPromise({
|
|
260
|
-
try: async () => {
|
|
261
|
-
const messages = []
|
|
262
|
-
|
|
263
|
-
for await (const message of sdk.query({
|
|
264
|
-
prompt,
|
|
265
|
-
options: {
|
|
266
|
-
maxTurns: 3,
|
|
267
|
-
customSystemPrompt:
|
|
268
|
-
options.systemPrompt ||
|
|
269
|
-
'You are a code review expert. Analyze code changes and provide constructive feedback.',
|
|
270
|
-
allowedTools: ['Read', 'Grep', 'Glob'],
|
|
271
|
-
cwd: options.cwd,
|
|
272
|
-
},
|
|
273
|
-
})) {
|
|
274
|
-
if (message.type === 'result' && message.subtype === 'success') {
|
|
275
|
-
return message.result
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
throw new Error('No result received from Claude SDK')
|
|
280
|
-
},
|
|
281
|
-
catch: (error) =>
|
|
282
|
-
new ReviewStrategyError({
|
|
283
|
-
message: `Claude SDK failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
284
|
-
cause: error,
|
|
285
|
-
}),
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
return result
|
|
289
|
-
}),
|
|
290
|
-
}
|
|
291
|
-
|
|
292
200
|
// Review service using strategy pattern
|
|
293
201
|
export class ReviewStrategyService extends Context.Tag('ReviewStrategyService')<
|
|
294
202
|
ReviewStrategyService,
|
|
@@ -310,13 +218,7 @@ export const ReviewStrategyServiceLive = Layer.succeed(
|
|
|
310
218
|
ReviewStrategyService.of({
|
|
311
219
|
getAvailableStrategies: () =>
|
|
312
220
|
Effect.gen(function* () {
|
|
313
|
-
const strategies = [
|
|
314
|
-
claudeSDKStrategy,
|
|
315
|
-
claudeCliStrategy,
|
|
316
|
-
geminiCliStrategy,
|
|
317
|
-
openCodeCliStrategy,
|
|
318
|
-
codexCliStrategy,
|
|
319
|
-
]
|
|
221
|
+
const strategies = [claudeCliStrategy, geminiCliStrategy, openCodeCliStrategy]
|
|
320
222
|
const available: ReviewStrategy[] = []
|
|
321
223
|
|
|
322
224
|
for (const strategy of strategies) {
|
|
@@ -331,13 +233,7 @@ export const ReviewStrategyServiceLive = Layer.succeed(
|
|
|
331
233
|
|
|
332
234
|
selectStrategy: (preferredName?: string) =>
|
|
333
235
|
Effect.gen(function* () {
|
|
334
|
-
const strategies = [
|
|
335
|
-
claudeSDKStrategy,
|
|
336
|
-
claudeCliStrategy,
|
|
337
|
-
geminiCliStrategy,
|
|
338
|
-
openCodeCliStrategy,
|
|
339
|
-
codexCliStrategy,
|
|
340
|
-
]
|
|
236
|
+
const strategies = [claudeCliStrategy, geminiCliStrategy, openCodeCliStrategy]
|
|
341
237
|
const available: ReviewStrategy[] = []
|
|
342
238
|
|
|
343
239
|
for (const strategy of strategies) {
|
|
@@ -350,7 +246,7 @@ export const ReviewStrategyServiceLive = Layer.succeed(
|
|
|
350
246
|
if (available.length === 0) {
|
|
351
247
|
return yield* Effect.fail(
|
|
352
248
|
new ReviewStrategyError({
|
|
353
|
-
message: 'No AI tools available. Please install claude, gemini, or
|
|
249
|
+
message: 'No AI tools available. Please install claude, gemini, or opencode CLI.',
|
|
354
250
|
}),
|
|
355
251
|
)
|
|
356
252
|
}
|