@link-assistant/hive-mind 0.46.1 → 0.47.1

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 (63) hide show
  1. package/CHANGELOG.md +20 -15
  2. package/README.md +42 -8
  3. package/package.json +16 -3
  4. package/src/agent.lib.mjs +49 -70
  5. package/src/agent.prompts.lib.mjs +6 -20
  6. package/src/buildUserMention.lib.mjs +4 -17
  7. package/src/claude-limits.lib.mjs +15 -15
  8. package/src/claude.lib.mjs +617 -626
  9. package/src/claude.prompts.lib.mjs +7 -22
  10. package/src/codex.lib.mjs +39 -71
  11. package/src/codex.prompts.lib.mjs +6 -20
  12. package/src/config.lib.mjs +3 -16
  13. package/src/contributing-guidelines.lib.mjs +5 -18
  14. package/src/exit-handler.lib.mjs +4 -4
  15. package/src/git.lib.mjs +7 -7
  16. package/src/github-issue-creator.lib.mjs +17 -17
  17. package/src/github-linking.lib.mjs +8 -33
  18. package/src/github.batch.lib.mjs +20 -16
  19. package/src/github.graphql.lib.mjs +18 -18
  20. package/src/github.lib.mjs +89 -91
  21. package/src/hive.config.lib.mjs +50 -50
  22. package/src/hive.mjs +1293 -1296
  23. package/src/instrument.mjs +7 -11
  24. package/src/interactive-mode.lib.mjs +112 -138
  25. package/src/lenv-reader.lib.mjs +1 -6
  26. package/src/lib.mjs +36 -45
  27. package/src/lino.lib.mjs +2 -2
  28. package/src/local-ci-checks.lib.mjs +15 -14
  29. package/src/memory-check.mjs +52 -60
  30. package/src/model-mapping.lib.mjs +25 -32
  31. package/src/model-validation.lib.mjs +31 -31
  32. package/src/opencode.lib.mjs +37 -62
  33. package/src/opencode.prompts.lib.mjs +7 -21
  34. package/src/protect-branch.mjs +14 -15
  35. package/src/review.mjs +28 -27
  36. package/src/reviewers-hive.mjs +64 -69
  37. package/src/sentry.lib.mjs +13 -10
  38. package/src/solve.auto-continue.lib.mjs +48 -38
  39. package/src/solve.auto-pr.lib.mjs +111 -69
  40. package/src/solve.branch-errors.lib.mjs +17 -46
  41. package/src/solve.branch.lib.mjs +16 -23
  42. package/src/solve.config.lib.mjs +263 -261
  43. package/src/solve.error-handlers.lib.mjs +21 -79
  44. package/src/solve.execution.lib.mjs +10 -18
  45. package/src/solve.feedback.lib.mjs +25 -46
  46. package/src/solve.mjs +59 -60
  47. package/src/solve.preparation.lib.mjs +10 -36
  48. package/src/solve.repo-setup.lib.mjs +4 -19
  49. package/src/solve.repository.lib.mjs +37 -37
  50. package/src/solve.results.lib.mjs +32 -46
  51. package/src/solve.session.lib.mjs +7 -22
  52. package/src/solve.validation.lib.mjs +19 -17
  53. package/src/solve.watch.lib.mjs +20 -33
  54. package/src/start-screen.mjs +24 -24
  55. package/src/task.mjs +38 -44
  56. package/src/telegram-bot.mjs +125 -121
  57. package/src/telegram-top-command.lib.mjs +32 -48
  58. package/src/usage-limit.lib.mjs +9 -13
  59. package/src/version-info.lib.mjs +1 -1
  60. package/src/version.lib.mjs +1 -1
  61. package/src/youtrack/solve.youtrack.lib.mjs +3 -8
  62. package/src/youtrack/youtrack-sync.mjs +8 -14
  63. package/src/youtrack/youtrack.lib.mjs +26 -28
@@ -19,15 +19,15 @@ import { timeouts } from './config.lib.mjs';
19
19
  import { detectUsageLimit, formatUsageLimitMessage } from './usage-limit.lib.mjs';
20
20
 
21
21
  // Model mapping to translate aliases to full model IDs for OpenCode
22
- export const mapModelToId = (model) => {
22
+ export const mapModelToId = model => {
23
23
  const modelMap = {
24
- 'gpt4': 'openai/gpt-4',
25
- 'gpt4o': 'openai/gpt-4o',
26
- 'claude': 'anthropic/claude-3-5-sonnet',
27
- 'sonnet': 'anthropic/claude-3-5-sonnet',
28
- 'opus': 'anthropic/claude-3-opus',
29
- 'gemini': 'google/gemini-pro',
30
- 'grok': 'opencode/grok-code',
24
+ gpt4: 'openai/gpt-4',
25
+ gpt4o: 'openai/gpt-4o',
26
+ claude: 'anthropic/claude-3-5-sonnet',
27
+ sonnet: 'anthropic/claude-3-5-sonnet',
28
+ opus: 'anthropic/claude-3-opus',
29
+ gemini: 'google/gemini-pro',
30
+ grok: 'opencode/grok-code',
31
31
  'grok-code': 'opencode/grok-code',
32
32
  'grok-code-fast-1': 'opencode/grok-code',
33
33
  };
@@ -108,28 +108,8 @@ export const handleOpenCodeRuntimeSwitch = async () => {
108
108
  };
109
109
 
110
110
  // Main function to execute OpenCode with prompts and settings
111
- export const executeOpenCode = async (params) => {
112
- const {
113
- issueUrl,
114
- issueNumber,
115
- prNumber,
116
- prUrl,
117
- branchName,
118
- tempDir,
119
- isContinueMode,
120
- mergeStateStatus,
121
- forkedRepo,
122
- feedbackLines,
123
- forkActionsUrl,
124
- owner,
125
- repo,
126
- argv,
127
- log,
128
- formatAligned,
129
- getResourceSnapshot,
130
- opencodePath = 'opencode',
131
- $
132
- } = params;
111
+ export const executeOpenCode = async params => {
112
+ const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, formatAligned, getResourceSnapshot, opencodePath = 'opencode', $ } = params;
133
113
 
134
114
  // Import prompt building functions from opencode.prompts.lib.mjs
135
115
  const { buildUserPrompt, buildSystemPrompt } = await import('./opencode.prompts.lib.mjs');
@@ -149,7 +129,7 @@ export const executeOpenCode = async (params) => {
149
129
  forkActionsUrl,
150
130
  owner,
151
131
  repo,
152
- argv
132
+ argv,
153
133
  });
154
134
 
155
135
  // Build the system prompt
@@ -162,7 +142,7 @@ export const executeOpenCode = async (params) => {
162
142
  tempDir,
163
143
  isContinueMode,
164
144
  forkedRepo,
165
- argv
145
+ argv,
166
146
  });
167
147
 
168
148
  // Log prompt details in verbose mode
@@ -199,25 +179,12 @@ export const executeOpenCode = async (params) => {
199
179
  forkedRepo,
200
180
  feedbackLines,
201
181
  opencodePath,
202
- $
182
+ $,
203
183
  });
204
184
  };
205
185
 
206
- export const executeOpenCodeCommand = async (params) => {
207
- const {
208
- tempDir,
209
- branchName,
210
- prompt,
211
- systemPrompt,
212
- argv,
213
- log,
214
- formatAligned,
215
- getResourceSnapshot,
216
- forkedRepo,
217
- feedbackLines,
218
- opencodePath,
219
- $
220
- } = params;
186
+ export const executeOpenCodeCommand = async params => {
187
+ const { tempDir, branchName, prompt, systemPrompt, argv, log, formatAligned, getResourceSnapshot, forkedRepo, feedbackLines, opencodePath, $ } = params;
221
188
 
222
189
  // Retry configuration
223
190
  const maxRetries = 3;
@@ -286,12 +253,12 @@ export const executeOpenCodeCommand = async (params) => {
286
253
  if (argv.resume) {
287
254
  execCommand = $({
288
255
  cwd: tempDir,
289
- mirror: false
256
+ mirror: false,
290
257
  })`cat ${promptFile} | ${opencodePath} run --format json --resume ${argv.resume} --model ${mappedModel}`;
291
258
  } else {
292
259
  execCommand = $({
293
260
  cwd: tempDir,
294
- mirror: false
261
+ mirror: false,
295
262
  })`cat ${promptFile} | ${opencodePath} run --format json --model ${mappedModel}`;
296
263
  }
297
264
 
@@ -340,7 +307,7 @@ export const executeOpenCodeCommand = async (params) => {
340
307
  tool: 'OpenCode',
341
308
  resetTime: limitInfo.resetTime,
342
309
  sessionId,
343
- resumeCommand: sessionId ? `${process.argv[0]} ${process.argv[1]} ${argv.url} --resume ${sessionId}` : null
310
+ resumeCommand: sessionId ? `${process.argv[0]} ${process.argv[1]} ${argv.url} --resume ${sessionId}` : null,
344
311
  });
345
312
 
346
313
  for (const line of messageLines) {
@@ -359,7 +326,7 @@ export const executeOpenCodeCommand = async (params) => {
359
326
  success: false,
360
327
  sessionId,
361
328
  limitReached,
362
- limitResetTime
329
+ limitResetTime,
363
330
  };
364
331
  }
365
332
 
@@ -369,14 +336,14 @@ export const executeOpenCodeCommand = async (params) => {
369
336
  success: true,
370
337
  sessionId,
371
338
  limitReached,
372
- limitResetTime
339
+ limitResetTime,
373
340
  };
374
341
  } catch (error) {
375
342
  reportError(error, {
376
343
  context: 'execute_opencode',
377
344
  command: params.command,
378
345
  opencodePath: params.opencodePath,
379
- operation: 'run_opencode_command'
346
+ operation: 'run_opencode_command',
380
347
  });
381
348
 
382
349
  await log(`\n\nāŒ Error executing OpenCode command: ${error.message}`, { level: 'error' });
@@ -384,7 +351,7 @@ export const executeOpenCodeCommand = async (params) => {
384
351
  success: false,
385
352
  sessionId: null,
386
353
  limitReached: false,
387
- limitResetTime: null
354
+ limitResetTime: null,
388
355
  };
389
356
  }
390
357
  };
@@ -425,13 +392,19 @@ export const checkForUncommittedChanges = async (tempDir, owner, repo, branchNam
425
392
  if (pushResult.code === 0) {
426
393
  await log('āœ… Changes pushed successfully');
427
394
  } else {
428
- await log(`āš ļø Warning: Could not push changes: ${pushResult.stderr?.toString().trim()}`, { level: 'warning' });
395
+ await log(`āš ļø Warning: Could not push changes: ${pushResult.stderr?.toString().trim()}`, {
396
+ level: 'warning',
397
+ });
429
398
  }
430
399
  } else {
431
- await log(`āš ļø Warning: Could not commit changes: ${commitResult.stderr?.toString().trim()}`, { level: 'warning' });
400
+ await log(`āš ļø Warning: Could not commit changes: ${commitResult.stderr?.toString().trim()}`, {
401
+ level: 'warning',
402
+ });
432
403
  }
433
404
  } else {
434
- await log(`āš ļø Warning: Could not stage changes: ${addResult.stderr?.toString().trim()}`, { level: 'warning' });
405
+ await log(`āš ļø Warning: Could not stage changes: ${addResult.stderr?.toString().trim()}`, {
406
+ level: 'warning',
407
+ });
435
408
  }
436
409
  return false;
437
410
  } else if (autoRestartEnabled) {
@@ -455,14 +428,16 @@ export const checkForUncommittedChanges = async (tempDir, owner, repo, branchNam
455
428
  return false;
456
429
  }
457
430
  } else {
458
- await log(`āš ļø Warning: Could not check git status: ${gitStatusResult.stderr?.toString().trim()}`, { level: 'warning' });
431
+ await log(`āš ļø Warning: Could not check git status: ${gitStatusResult.stderr?.toString().trim()}`, {
432
+ level: 'warning',
433
+ });
459
434
  return false;
460
435
  }
461
436
  } catch (gitError) {
462
437
  reportError(gitError, {
463
438
  context: 'check_uncommitted_changes_opencode',
464
439
  tempDir,
465
- operation: 'git_status_check'
440
+ operation: 'git_status_check',
466
441
  });
467
442
  await log(`āš ļø Warning: Error checking for uncommitted changes: ${gitError.message}`, { level: 'warning' });
468
443
  return false;
@@ -475,5 +450,5 @@ export default {
475
450
  handleOpenCodeRuntimeSwitch,
476
451
  executeOpenCode,
477
452
  executeOpenCodeCommand,
478
- checkForUncommittedChanges
479
- };
453
+ checkForUncommittedChanges,
454
+ };
@@ -8,22 +8,8 @@
8
8
  * @param {Object} params - Parameters for building the user prompt
9
9
  * @returns {string} The formatted user prompt
10
10
  */
11
- export const buildUserPrompt = (params) => {
12
- const {
13
- issueUrl,
14
- issueNumber,
15
- prNumber,
16
- prUrl,
17
- branchName,
18
- tempDir,
19
- isContinueMode,
20
- forkedRepo,
21
- feedbackLines,
22
- forkActionsUrl,
23
- owner,
24
- repo,
25
- argv
26
- } = params;
11
+ export const buildUserPrompt = params => {
12
+ const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, isContinueMode, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv } = params;
27
13
 
28
14
  const promptLines = [];
29
15
 
@@ -70,7 +56,7 @@ export const buildUserPrompt = (params) => {
70
56
  low: 'Think.',
71
57
  medium: 'Think hard.',
72
58
  high: 'Think harder.',
73
- max: 'Ultrathink.'
59
+ max: 'Ultrathink.',
74
60
  };
75
61
  promptLines.push(thinkMessages[argv.think]);
76
62
  }
@@ -87,7 +73,7 @@ export const buildUserPrompt = (params) => {
87
73
  * @param {Object} params - Parameters for building the prompt
88
74
  * @returns {string} The formatted system prompt
89
75
  */
90
- export const buildSystemPrompt = (params) => {
76
+ export const buildSystemPrompt = params => {
91
77
  const { owner, repo, issueNumber, prNumber, branchName, argv } = params;
92
78
 
93
79
  // Build thinking instruction based on --think level
@@ -97,7 +83,7 @@ export const buildSystemPrompt = (params) => {
97
83
  low: 'You always think on every step.',
98
84
  medium: 'You always think hard on every step.',
99
85
  high: 'You always think harder on every step.',
100
- max: 'You always ultrathink on every step.'
86
+ max: 'You always ultrathink on every step.',
101
87
  };
102
88
  thinkLine = `\n${thinkMessages[argv.think]}\n`;
103
89
  }
@@ -190,5 +176,5 @@ GitHub CLI command patterns.
190
176
  // Export all functions as default object too
191
177
  export default {
192
178
  buildUserPrompt,
193
- buildSystemPrompt
194
- };
179
+ buildSystemPrompt,
180
+ };
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Branch Protection Script
4
- *
4
+ *
5
5
  * Enables branch protection rules on the default branch of a GitHub repository
6
6
  * to require pull requests before merging.
7
- *
7
+ *
8
8
  * Usage:
9
9
  * ./protect-branch.mjs <owner>/<repo>
10
10
  * ./protect-branch.mjs <owner>/<repo> <branch-name>
11
- *
11
+ *
12
12
  * Examples:
13
13
  * ./protect-branch.mjs konard/my-repo
14
14
  * ./protect-branch.mjs konard/my-repo main
@@ -51,13 +51,13 @@ try {
51
51
  if (!branchName) {
52
52
  console.log('šŸ” Detecting default branch...');
53
53
  const defaultBranchResult = await $`gh api repos/${owner}/${repo} --jq .default_branch`;
54
-
54
+
55
55
  if (defaultBranchResult.code !== 0) {
56
56
  console.error('Error: Failed to get repository information');
57
57
  console.error(defaultBranchResult.stderr ? defaultBranchResult.stderr.toString() : 'Unknown error');
58
58
  process.exit(1);
59
59
  }
60
-
60
+
61
61
  branchName = defaultBranchResult.stdout.toString().trim();
62
62
  console.log(`āœ… Default branch: ${branchName}`);
63
63
  } else {
@@ -67,7 +67,7 @@ try {
67
67
  // Check if branch exists
68
68
  console.log('šŸ” Verifying branch exists...');
69
69
  const branchCheckResult = await $`gh api repos/${owner}/${repo}/branches/${branchName} --silent`;
70
-
70
+
71
71
  if (branchCheckResult.code !== 0) {
72
72
  console.error(`Error: Branch '${branchName}' not found in repository`);
73
73
  process.exit(1);
@@ -76,7 +76,7 @@ try {
76
76
 
77
77
  // Enable branch protection with PR requirement
78
78
  console.log(`šŸ” Enabling branch protection for '${branchName}'...`);
79
-
79
+
80
80
  // Create the protection rules JSON
81
81
  const protectionRules = {
82
82
  required_status_checks: null,
@@ -85,7 +85,7 @@ try {
85
85
  dismiss_stale_reviews: false,
86
86
  require_code_owner_reviews: false,
87
87
  required_approving_review_count: 0,
88
- require_last_push_approval: false
88
+ require_last_push_approval: false,
89
89
  },
90
90
  restrictions: null,
91
91
  allow_force_pushes: false,
@@ -93,7 +93,7 @@ try {
93
93
  block_creations: false,
94
94
  required_conversation_resolution: false,
95
95
  lock_branch: false,
96
- allow_fork_syncing: false
96
+ allow_fork_syncing: false,
97
97
  };
98
98
 
99
99
  // Apply branch protection using GitHub API
@@ -112,10 +112,10 @@ EOF`;
112
112
  console.error('Error: Failed to enable branch protection');
113
113
  console.error(protectResult.stderr ? protectResult.stderr.toString() : 'Unknown error');
114
114
  }
115
-
115
+
116
116
  // Try a simpler approach for public repos
117
117
  console.log('šŸ”„ Trying alternative method...');
118
-
118
+
119
119
  // For public repos, we can at least try to update settings
120
120
  const updateResult = await $`gh api \
121
121
  --method PATCH \
@@ -124,7 +124,7 @@ EOF`;
124
124
  --field allow_squash_merge=true \
125
125
  --field allow_rebase_merge=true \
126
126
  --field delete_branch_on_merge=false`;
127
-
127
+
128
128
  if (updateResult.code === 0) {
129
129
  console.log('āœ… Repository settings updated (PR workflow encouraged)');
130
130
  }
@@ -135,7 +135,7 @@ EOF`;
135
135
  // Verify the protection status
136
136
  console.log('\nšŸ“Š Verifying protection status...');
137
137
  const statusResult = await $`gh api repos/${owner}/${repo}/branches/${branchName}/protection --silent 2>/dev/null || echo "not-protected"`;
138
-
138
+
139
139
  if (statusResult.stdout.toString().trim() === 'not-protected') {
140
140
  console.log('āš ļø Branch protection not fully active (may require admin rights or paid plan)');
141
141
  console.log('\nšŸ’” Alternative: Configure protection manually in repository settings:');
@@ -149,11 +149,10 @@ EOF`;
149
149
  }
150
150
 
151
151
  console.log('\n✨ Done! The branch is configured to require pull requests.');
152
-
153
152
  } catch (error) {
154
153
  console.error('\nāŒ Error:', error.message);
155
154
  if (error.stderr) {
156
155
  console.error('Details:', error.stderr.toString());
157
156
  }
158
157
  process.exit(1);
159
- }
158
+ }
package/src/review.mjs CHANGED
@@ -55,45 +55,45 @@ const argv = yargs()
55
55
  .usage('Usage: $0 <pr-url> [options]')
56
56
  .positional('pr-url', {
57
57
  type: 'string',
58
- description: 'The GitHub pull request URL to review'
58
+ description: 'The GitHub pull request URL to review',
59
59
  })
60
60
  .option('resume', {
61
61
  type: 'string',
62
62
  description: 'Resume from a previous session ID (when limit was reached)',
63
- alias: 'r'
63
+ alias: 'r',
64
64
  })
65
65
  .option('dry-run', {
66
66
  type: 'boolean',
67
67
  description: 'Prepare everything but do not execute Claude',
68
- alias: 'n'
68
+ alias: 'n',
69
69
  })
70
70
  .option('model', {
71
71
  type: 'string',
72
72
  description: 'Model to use (opus, sonnet, or full model ID like claude-sonnet-4-5-20250929)',
73
73
  alias: 'm',
74
74
  default: 'opus',
75
- choices: ['opus', 'sonnet', 'claude-sonnet-4-5-20250929', 'claude-opus-4-5-20251101']
75
+ choices: ['opus', 'sonnet', 'claude-sonnet-4-5-20250929', 'claude-opus-4-5-20251101'],
76
76
  })
77
77
  .option('focus', {
78
78
  type: 'string',
79
79
  description: 'Focus areas for review (security, performance, logic, style, tests)',
80
80
  alias: 'f',
81
- default: 'all'
81
+ default: 'all',
82
82
  })
83
83
  .option('approve', {
84
84
  type: 'boolean',
85
85
  description: 'If review passes, approve the PR',
86
- default: false
86
+ default: false,
87
87
  })
88
88
  .option('verbose', {
89
89
  type: 'boolean',
90
90
  description: 'Enable verbose logging for debugging',
91
91
  alias: 'v',
92
- default: false
92
+ default: false,
93
93
  })
94
94
  .demandCommand(1, 'The GitHub pull request URL is required')
95
95
  .parserConfiguration({
96
- 'boolean-negation': true
96
+ 'boolean-negation': true,
97
97
  })
98
98
  .help('h')
99
99
  .alias('h', 'help')
@@ -120,7 +120,9 @@ await log(' (All output will be logged here)\n');
120
120
 
121
121
  // Validate GitHub PR URL format
122
122
  if (!prUrl.match(/^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+$/)) {
123
- await log('Error: Please provide a valid GitHub pull request URL (e.g., https://github.com/owner/repo/pull/123)', { level: 'error' });
123
+ await log('Error: Please provide a valid GitHub pull request URL (e.g., https://github.com/owner/repo/pull/123)', {
124
+ level: 'error',
125
+ });
124
126
  process.exit(1);
125
127
  }
126
128
 
@@ -153,7 +155,7 @@ if (isResuming) {
153
155
  } catch (err) {
154
156
  reportError(err, {
155
157
  context: 'resume_session_lookup',
156
- sessionId: argv.resume
158
+ sessionId: argv.resume,
157
159
  });
158
160
  await log(`Warning: Session log for ${argv.resume} not found, but continuing with resume attempt`);
159
161
  tempDir = path.join(os.tmpdir(), `gh-pr-reviewer-resume-${argv.resume}-${Date.now()}`);
@@ -172,15 +174,15 @@ try {
172
174
  // Get PR details first
173
175
  await log('šŸ“Š Getting pull request details...');
174
176
  const prDetailsResult = await $`gh pr view ${prUrl} --json title,body,headRefName,baseRefName,author,number,state,files`;
175
-
177
+
176
178
  if (prDetailsResult.code !== 0) {
177
179
  await log('Error: Failed to get PR details', { level: 'error' });
178
180
  await log(prDetailsResult.stderr ? prDetailsResult.stderr.toString() : 'Unknown error', { level: 'error' });
179
181
  process.exit(1);
180
182
  }
181
-
183
+
182
184
  const prDetails = JSON.parse(prDetailsResult.stdout.toString());
183
-
185
+
184
186
  await log(`\nšŸ“„ Pull Request: #${prDetails.number} - ${prDetails.title}`);
185
187
  await log(`šŸ‘¤ Author: ${prDetails.author.login}`);
186
188
  await log(`🌿 Branch: ${prDetails.headRefName} → ${prDetails.baseRefName}`);
@@ -190,7 +192,7 @@ try {
190
192
  // Clone the repository using gh tool with authentication
191
193
  await log(`\nCloning repository ${owner}/${repo} using gh tool...\n`);
192
194
  const cloneResult = await $`gh repo clone ${owner}/${repo} ${tempDir}`;
193
-
195
+
194
196
  // Verify clone was successful
195
197
  if (cloneResult.code !== 0) {
196
198
  await log('Error: Failed to clone repository', { level: 'error' });
@@ -209,25 +211,25 @@ try {
209
211
  // Fetch and checkout the PR branch
210
212
  await log(`šŸ”€ Fetching and checking out PR branch: ${prDetails.headRefName}`);
211
213
  const fetchResult = await $`cd ${tempDir} && gh pr checkout ${prNumber}`;
212
-
214
+
213
215
  if (fetchResult.code !== 0) {
214
216
  await log('Error: Failed to checkout PR branch', { level: 'error' });
215
217
  await log(fetchResult.stderr ? fetchResult.stderr.toString() : 'Unknown error', { level: 'error' });
216
218
  process.exit(1);
217
219
  }
218
-
220
+
219
221
  await log('āœ… Successfully checked out PR branch\n');
220
222
 
221
223
  // Get the diff for the PR
222
224
  await log('šŸ“ Getting PR diff...');
223
225
  const diffResult = await $`gh pr diff ${prUrl}`;
224
-
226
+
225
227
  if (diffResult.code !== 0) {
226
228
  await log('Error: Failed to get PR diff', { level: 'error' });
227
229
  await log(diffResult.stderr ? diffResult.stderr.toString() : 'Unknown error', { level: 'error' });
228
230
  process.exit(1);
229
231
  }
230
-
232
+
231
233
  const prDiff = diffResult.stdout.toString();
232
234
  await log(`āœ… Got PR diff (${prDiff.length} characters)\n`);
233
235
 
@@ -342,7 +344,7 @@ Review this pull request thoroughly.`;
342
344
  forkedRepo: null,
343
345
  feedbackLines: [],
344
346
  claudePath,
345
- $
347
+ $,
346
348
  });
347
349
 
348
350
  const { success: commandSuccess, sessionId, limitReached: limitReachedResult } = result;
@@ -370,11 +372,11 @@ Review this pull request thoroughly.`;
370
372
  } else {
371
373
  // Check if review was submitted
372
374
  await log('\nšŸ” Checking for submitted review...');
373
-
375
+
374
376
  try {
375
377
  // Get reviews for the PR
376
378
  const reviewsResult = await $`gh api repos/${owner}/${repo}/pulls/${prNumber}/reviews --jq '.[] | select(.user.login == "'$(gh api user --jq .login)'") | {state, submitted_at}'`;
377
-
379
+
378
380
  if (reviewsResult.code === 0 && reviewsResult.stdout.toString().trim()) {
379
381
  await log(`āœ… Review has been submitted to PR #${prNumber}`);
380
382
  await log(`šŸ“ View at: ${prUrl}`);
@@ -385,11 +387,11 @@ Review this pull request thoroughly.`;
385
387
  reportError(error, {
386
388
  context: 'verify_review_status',
387
389
  prNumber,
388
- level: 'warning'
390
+ level: 'warning',
389
391
  });
390
392
  await log('āš ļø Could not verify review status');
391
393
  }
392
-
394
+
393
395
  // Show command to resume session in interactive mode
394
396
  await log('\nšŸ’” To continue this session in Claude Code interactive mode:\n');
395
397
  await log(` (cd ${tempDir} && claude --resume ${sessionId})`);
@@ -402,11 +404,10 @@ Review this pull request thoroughly.`;
402
404
 
403
405
  await log('\n✨ Review process complete. Check the PR for review comments.');
404
406
  await log(`šŸ“ Pull Request: ${prUrl}`);
405
-
406
407
  } catch (error) {
407
408
  reportError(error, {
408
409
  context: 'review_execution',
409
- prUrl: prUrl
410
+ prUrl: prUrl,
410
411
  });
411
412
  await log('Error executing review:', error.message, { level: 'error' });
412
413
  process.exit(1);
@@ -421,7 +422,7 @@ Review this pull request thoroughly.`;
421
422
  reportError(cleanupError, {
422
423
  context: 'cleanup_temp_dir',
423
424
  level: 'warning',
424
- tempDir
425
+ tempDir,
425
426
  });
426
427
  await log(' āš ļø (failed)');
427
428
  }
@@ -430,4 +431,4 @@ Review this pull request thoroughly.`;
430
431
  } else if (limitReached) {
431
432
  await log(`\nšŸ“ Keeping directory for future resume: ${tempDir}`);
432
433
  }
433
- }
434
+ }