@link-assistant/hive-mind 0.46.1 → 0.47.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 (63) hide show
  1. package/CHANGELOG.md +10 -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
@@ -3,24 +3,7 @@
3
3
  * Handles automatic creation of draft pull requests with initial commits
4
4
  */
5
5
 
6
- export async function handleAutoPrCreation({
7
- argv,
8
- tempDir,
9
- branchName,
10
- issueNumber,
11
- owner,
12
- repo,
13
- defaultBranch,
14
- forkedRepo,
15
- isContinueMode,
16
- prNumber,
17
- log,
18
- formatAligned,
19
- $,
20
- reportError,
21
- path,
22
- fs
23
- }) {
6
+ export async function handleAutoPrCreation({ argv, tempDir, branchName, issueNumber, owner, repo, defaultBranch, forkedRepo, isContinueMode, prNumber, log, formatAligned, $, reportError, path, fs }) {
24
7
  // Skip auto-PR creation if:
25
8
  // 1. Auto-PR creation is disabled AND we're not in continue mode with no PR
26
9
  // 2. Continue mode is active AND we already have a PR
@@ -80,9 +63,13 @@ export async function handleAutoPrCreation({
80
63
  const timestamp = new Date().toISOString();
81
64
  const taskInfo = `Issue to solve: ${issueUrl}
82
65
  Your prepared branch: ${branchName}
83
- Your prepared working directory: ${tempDir}${argv.fork && forkedRepo ? `
66
+ Your prepared working directory: ${tempDir}${
67
+ argv.fork && forkedRepo
68
+ ? `
84
69
  Your forked repository: ${forkedRepo}
85
- Original repository (upstream): ${owner}/${repo}` : ''}
70
+ Original repository (upstream): ${owner}/${repo}`
71
+ : ''
72
+ }
86
73
 
87
74
  Proceed.`;
88
75
 
@@ -158,7 +145,9 @@ Proceed.`;
158
145
 
159
146
  if (gitkeepAddResult.code !== 0) {
160
147
  await log('❌ Failed to add .gitkeep', { level: 'error' });
161
- await log(` Error: ${gitkeepAddResult.stderr ? gitkeepAddResult.stderr.toString() : 'Unknown error'}`, { level: 'error' });
148
+ await log(` Error: ${gitkeepAddResult.stderr ? gitkeepAddResult.stderr.toString() : 'Unknown error'}`, {
149
+ level: 'error',
150
+ });
162
151
  throw new Error('Failed to add .gitkeep');
163
152
  }
164
153
 
@@ -168,7 +157,9 @@ Proceed.`;
168
157
 
169
158
  if (!gitStatus || gitStatus.length === 0) {
170
159
  await log('');
171
- await log(formatAligned('❌', 'GIT ADD FAILED:', 'Neither CLAUDE.md nor .gitkeep could be staged'), { level: 'error' });
160
+ await log(formatAligned('❌', 'GIT ADD FAILED:', 'Neither CLAUDE.md nor .gitkeep could be staged'), {
161
+ level: 'error',
162
+ });
172
163
  await log('');
173
164
  await log(' 🔍 What happened:');
174
165
  await log(' Both CLAUDE.md and .gitkeep failed to stage.');
@@ -211,14 +202,15 @@ Proceed.`;
211
202
  }
212
203
 
213
204
  await log(formatAligned('📝', 'Creating commit:', `With ${commitFileName} file`));
214
- const commitMessage = commitFileName === 'CLAUDE.md'
215
- ? `Initial commit with task details
205
+ const commitMessage =
206
+ commitFileName === 'CLAUDE.md'
207
+ ? `Initial commit with task details
216
208
 
217
209
  Adding CLAUDE.md with task information for AI processing.
218
210
  This file will be removed when the task is complete.
219
211
 
220
212
  Issue: ${issueUrl}`
221
- : `Initial commit with task details
213
+ : `Initial commit with task details
222
214
 
223
215
  Adding .gitkeep for PR creation (CLAUDE.md is in .gitignore).
224
216
  This file will be removed when the task is complete.
@@ -332,7 +324,9 @@ Issue: ${issueUrl}`;
332
324
 
333
325
  // Check for archived repository error
334
326
  if (errorOutput.includes('archived') && errorOutput.includes('read-only')) {
335
- await log(`\n${formatAligned('❌', 'REPOSITORY ARCHIVED:', 'Cannot push to archived repository')}`, { level: 'error' });
327
+ await log(`\n${formatAligned('❌', 'REPOSITORY ARCHIVED:', 'Cannot push to archived repository')}`, {
328
+ level: 'error',
329
+ });
336
330
  await log('');
337
331
  await log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
338
332
  await log('');
@@ -396,7 +390,7 @@ Issue: ${issueUrl}`;
396
390
  context: 'fork_check',
397
391
  owner,
398
392
  repo,
399
- operation: 'check_user_fork'
393
+ operation: 'check_user_fork',
400
394
  });
401
395
  // Ignore error - fork check is optional
402
396
  }
@@ -408,7 +402,7 @@ Issue: ${issueUrl}`;
408
402
  await log(` 🔒 You don't have write access to ${owner}/${repo}`);
409
403
  await log('');
410
404
  await log(' This typically happens when:');
411
- await log(' • You\'re not a collaborator on the repository');
405
+ await log(" • You're not a collaborator on the repository");
412
406
  await log(' • The repository belongs to another user/organization');
413
407
  await log('');
414
408
  await log(' 📋 HOW TO FIX THIS:');
@@ -532,7 +526,9 @@ Issue: ${issueUrl}`;
532
526
  } else {
533
527
  headRef = branchName;
534
528
  }
535
- compareResult = await $({ silent: true })`gh api repos/${owner}/${repo}/compare/${targetBranchForCompare}...${headRef} --jq '.ahead_by' 2>&1`;
529
+ compareResult = await $({
530
+ silent: true,
531
+ })`gh api repos/${owner}/${repo}/compare/${targetBranchForCompare}...${headRef} --jq '.ahead_by' 2>&1`;
536
532
 
537
533
  if (compareResult.code === 0) {
538
534
  const aheadBy = parseInt(compareResult.stdout.toString().trim(), 10);
@@ -569,7 +565,9 @@ Issue: ${issueUrl}`;
569
565
  await log('');
570
566
  await log(formatAligned('🔍', 'Investigating:', 'Checking fork relationship...'));
571
567
 
572
- const forkInfoResult = await $({ silent: true })`gh api repos/${forkedRepo} --jq '{fork: .fork, parent: .parent.full_name, source: .source.full_name}' 2>&1`;
568
+ const forkInfoResult = await $({
569
+ silent: true,
570
+ })`gh api repos/${forkedRepo} --jq '{fork: .fork, parent: .parent.full_name, source: .source.full_name}' 2>&1`;
573
571
 
574
572
  let isFork = false;
575
573
  let parentRepo = null;
@@ -597,7 +595,7 @@ Issue: ${issueUrl}`;
597
595
  await log('');
598
596
  await log(' 💡 Why this happens:');
599
597
  await log(' This repository was likely created by cloning and pushing (git clone + git push)');
600
- await log(' instead of using GitHub\'s Fork button or API.');
598
+ await log(" instead of using GitHub's Fork button or API.");
601
599
  await log('');
602
600
  await log(' When a repository is created this way:');
603
601
  await log(' • GitHub does not track it as a fork');
@@ -627,7 +625,9 @@ Issue: ${issueUrl}`;
627
625
  } else if (parentRepo !== `${owner}/${repo}` && sourceRepo !== `${owner}/${repo}`) {
628
626
  // Repository IS a fork, but of a different repository
629
627
  await log('');
630
- await log(formatAligned('❌', 'WRONG FORK PARENT:', 'Fork is from different repository'), { level: 'error' });
628
+ await log(formatAligned('❌', 'WRONG FORK PARENT:', 'Fork is from different repository'), {
629
+ level: 'error',
630
+ });
631
631
  await log('');
632
632
  await log(' 🔍 What happened:');
633
633
  await log(` The repository ${forkedRepo} IS a GitHub fork,`);
@@ -666,7 +666,7 @@ Issue: ${issueUrl}`;
666
666
  await log('');
667
667
  await log(' 🔍 What happened:');
668
668
  await log(` The repository ${forkedRepo} is a valid fork of ${owner}/${repo},`);
669
- await log(' but GitHub\'s compare API still returned an error.');
669
+ await log(" but GitHub's compare API still returned an error.");
670
670
  await log('');
671
671
  await log(' 📦 Fork verification:');
672
672
  await log(' • Your fork: ' + forkedRepo);
@@ -692,15 +692,17 @@ Issue: ${issueUrl}`;
692
692
  } else {
693
693
  // Original timeout error for other cases
694
694
  await log('');
695
- await log(formatAligned('❌', 'GITHUB SYNC TIMEOUT:', 'Compare API not ready after retries'), { level: 'error' });
695
+ await log(formatAligned('❌', 'GITHUB SYNC TIMEOUT:', 'Compare API not ready after retries'), {
696
+ level: 'error',
697
+ });
696
698
  await log('');
697
699
  await log(' 🔍 What happened:');
698
700
  await log(` After ${maxCompareAttempts} attempts, GitHub's compare API still shows no commits`);
699
701
  await log(` between ${targetBranchForCompare} and ${branchName}.`);
700
702
  await log('');
701
703
  await log(' 💡 This usually means:');
702
- await log(' • GitHub\'s backend systems haven\'t finished indexing the push');
703
- await log(' • There\'s a temporary issue with GitHub\'s API');
704
+ await log(" • GitHub's backend systems haven't finished indexing the push");
705
+ await log(" • There's a temporary issue with GitHub's API");
704
706
  await log(' • The commits may not have been pushed correctly');
705
707
  await log('');
706
708
  await log(' 🔧 How to fix:');
@@ -714,7 +716,7 @@ Issue: ${issueUrl}`;
714
716
  }
715
717
  await log(' 2. Check if the branch exists on GitHub:');
716
718
  // Show the correct repository where the branch was pushed
717
- const branchRepo = (argv.fork && forkedRepo) ? forkedRepo : `${owner}/${repo}`;
719
+ const branchRepo = argv.fork && forkedRepo ? forkedRepo : `${owner}/${repo}`;
718
720
  await log(` https://github.com/${branchRepo}/tree/${branchName}`);
719
721
  await log(' 3. Check the commit is on GitHub:');
720
722
  // Use the correct head reference for the compare API check
@@ -732,13 +734,17 @@ Issue: ${issueUrl}`;
732
734
 
733
735
  // Verify the push actually worked by checking GitHub API
734
736
  // When using fork mode, check the fork repository; otherwise check the original repository
735
- const repoToCheck = (argv.fork && forkedRepo) ? forkedRepo : `${owner}/${repo}`;
736
- const branchCheckResult = await $({ silent: true })`gh api repos/${repoToCheck}/branches/${branchName} --jq .name 2>&1`;
737
+ const repoToCheck = argv.fork && forkedRepo ? forkedRepo : `${owner}/${repo}`;
738
+ const branchCheckResult = await $({
739
+ silent: true,
740
+ })`gh api repos/${repoToCheck}/branches/${branchName} --jq .name 2>&1`;
737
741
  if (branchCheckResult.code === 0 && branchCheckResult.stdout.toString().trim() === branchName) {
738
742
  await log(` Branch verified on GitHub: ${branchName}`);
739
743
 
740
744
  // Get the commit SHA from GitHub
741
- const shaCheckResult = await $({ silent: true })`gh api repos/${repoToCheck}/branches/${branchName} --jq .commit.sha 2>&1`;
745
+ const shaCheckResult = await $({
746
+ silent: true,
747
+ })`gh api repos/${repoToCheck}/branches/${branchName} --jq .commit.sha 2>&1`;
742
748
  if (shaCheckResult.code === 0) {
743
749
  const remoteSha = shaCheckResult.stdout.toString().trim();
744
750
  await log(` Remote commit SHA: ${remoteSha.substring(0, 7)}...`);
@@ -751,7 +757,9 @@ Issue: ${issueUrl}`;
751
757
  await log(` Branch check result: ${branchCheckResult.stdout || branchCheckResult.stderr || 'empty'}`);
752
758
 
753
759
  // Show all branches on GitHub
754
- const allBranchesResult = await $({ silent: true })`gh api repos/${repoToCheck}/branches --jq '.[].name' 2>&1`;
760
+ const allBranchesResult = await $({
761
+ silent: true,
762
+ })`gh api repos/${repoToCheck}/branches --jq '.[].name' 2>&1`;
755
763
  if (allBranchesResult.code === 0) {
756
764
  await log(` All GitHub branches: ${allBranchesResult.stdout.toString().split('\n').slice(0, 5).join(', ')}...`);
757
765
  }
@@ -780,7 +788,9 @@ Issue: ${issueUrl}`;
780
788
 
781
789
  // Get issue title for PR title
782
790
  await log(formatAligned('📋', 'Getting issue:', 'Title from GitHub...'), { verbose: true });
783
- const issueTitleResult = await $({ silent: true })`gh api repos/${owner}/${repo}/issues/${issueNumber} --jq .title 2>&1`;
791
+ const issueTitleResult = await $({
792
+ silent: true,
793
+ })`gh api repos/${owner}/${repo}/issues/${issueNumber} --jq .title 2>&1`;
784
794
  let issueTitle = `Fix issue #${issueNumber}`;
785
795
  if (issueTitleResult.code === 0) {
786
796
  issueTitle = issueTitleResult.stdout.toString().trim();
@@ -807,8 +817,10 @@ Issue: ${issueUrl}`;
807
817
  const { promisify } = await import('util');
808
818
  const execAsync = promisify(exec);
809
819
  // This will throw if user doesn't have access, but won't print anything
810
- await execAsync(`gh api repos/${owner}/${repo}/collaborators/${currentUser} 2>/dev/null`,
811
- { encoding: 'utf8', env: process.env });
820
+ await execAsync(`gh api repos/${owner}/${repo}/collaborators/${currentUser} 2>/dev/null`, {
821
+ encoding: 'utf8',
822
+ env: process.env,
823
+ });
812
824
  canAssign = true;
813
825
  await log(' User has collaborator access', { verbose: true });
814
826
  } catch (e) {
@@ -817,7 +829,7 @@ Issue: ${issueUrl}`;
817
829
  owner,
818
830
  repo,
819
831
  currentUser,
820
- operation: 'check_collaborator_access'
832
+ operation: 'check_collaborator_access',
821
833
  });
822
834
  // User doesn't have permission, but that's okay - we just won't assign
823
835
  canAssign = false;
@@ -840,12 +852,17 @@ Issue: ${issueUrl}`;
840
852
  // Fetch latest state of target branch to ensure accurate comparison
841
853
  const targetBranch = argv.baseBranch || defaultBranch;
842
854
  await log(formatAligned('🔄', 'Fetching:', `Latest ${targetBranch} branch...`));
843
- const fetchBaseResult = await $({ cwd: tempDir, silent: true })`git fetch origin ${targetBranch}:refs/remotes/origin/${targetBranch} 2>&1`;
855
+ const fetchBaseResult = await $({
856
+ cwd: tempDir,
857
+ silent: true,
858
+ })`git fetch origin ${targetBranch}:refs/remotes/origin/${targetBranch} 2>&1`;
844
859
 
845
860
  if (fetchBaseResult.code !== 0) {
846
861
  await log(`⚠️ Warning: Could not fetch latest ${targetBranch}`, { level: 'warning' });
847
862
  if (argv.verbose) {
848
- await log(` Fetch output: ${fetchBaseResult.stdout || fetchBaseResult.stderr || 'none'}`, { verbose: true });
863
+ await log(` Fetch output: ${fetchBaseResult.stdout || fetchBaseResult.stderr || 'none'}`, {
864
+ verbose: true,
865
+ });
849
866
  }
850
867
  } else {
851
868
  await log(formatAligned('✅', 'Base updated:', `Fetched latest ${targetBranch}`));
@@ -853,7 +870,10 @@ Issue: ${issueUrl}`;
853
870
 
854
871
  // Verify there are commits between base and head before attempting PR creation
855
872
  await log(formatAligned('🔍', 'Checking:', 'Commits between branches...'));
856
- const commitCheckResult = await $({ cwd: tempDir, silent: true })`git rev-list --count origin/${targetBranch}..HEAD 2>&1`;
873
+ const commitCheckResult = await $({
874
+ cwd: tempDir,
875
+ silent: true,
876
+ })`git rev-list --count origin/${targetBranch}..HEAD 2>&1`;
857
877
 
858
878
  if (commitCheckResult.code === 0) {
859
879
  const commitCount = parseInt(commitCheckResult.stdout.toString().trim(), 10);
@@ -863,7 +883,10 @@ Issue: ${issueUrl}`;
863
883
 
864
884
  if (commitCount === 0) {
865
885
  // Check if the branch was already merged
866
- const mergedCheckResult = await $({ cwd: tempDir, silent: true })`git branch -r --merged origin/${targetBranch} | grep -q "origin/${branchName}" 2>&1`;
886
+ const mergedCheckResult = await $({
887
+ cwd: tempDir,
888
+ silent: true,
889
+ })`git branch -r --merged origin/${targetBranch} | grep -q "origin/${branchName}" 2>&1`;
867
890
  const wasAlreadyMerged = mergedCheckResult.code === 0;
868
891
 
869
892
  // No commits to create PR - branch is up to date with base or behind it
@@ -930,7 +953,9 @@ Issue: ${issueUrl}`;
930
953
  } else {
931
954
  await log('⚠️ Warning: Could not verify commit count', { level: 'warning' });
932
955
  if (argv.verbose) {
933
- await log(` Check output: ${commitCheckResult.stdout || commitCheckResult.stderr || 'none'}`, { verbose: true });
956
+ await log(` Check output: ${commitCheckResult.stdout || commitCheckResult.stderr || 'none'}`, {
957
+ verbose: true,
958
+ });
934
959
  }
935
960
  }
936
961
 
@@ -968,8 +993,11 @@ _Details will be added as the solution draft is developed..._
968
993
  if (currentUser) {
969
994
  await log(` Assignee: ${currentUser}`, { verbose: true });
970
995
  }
971
- await log(` PR Body:
972
- ${prBody}`, { verbose: true });
996
+ await log(
997
+ ` PR Body:
998
+ ${prBody}`,
999
+ { verbose: true }
1000
+ );
973
1001
  }
974
1002
 
975
1003
  // Use async exec for gh pr create to avoid command-stream output issues
@@ -1024,7 +1052,9 @@ ${prBody}`, { verbose: true });
1024
1052
  // Assignee validation failed - retry without assignee
1025
1053
  assigneeFailed = true;
1026
1054
  await log('');
1027
- await log(formatAligned('⚠️', 'Warning:', `User assignment failed for '${currentUser}'`), { level: 'warning' });
1055
+ await log(formatAligned('⚠️', 'Warning:', `User assignment failed for '${currentUser}'`), {
1056
+ level: 'warning',
1057
+ });
1028
1058
  await log(' Retrying PR creation without assignee...');
1029
1059
 
1030
1060
  // Rebuild command without --assignee flag
@@ -1049,18 +1079,18 @@ ${prBody}`, { verbose: true });
1049
1079
  }
1050
1080
 
1051
1081
  // Clean up temp files
1052
- await fs.unlink(prBodyFile).catch((unlinkError) => {
1082
+ await fs.unlink(prBodyFile).catch(unlinkError => {
1053
1083
  reportError(unlinkError, {
1054
1084
  context: 'pr_body_file_cleanup',
1055
1085
  prBodyFile,
1056
- operation: 'delete_temp_file'
1086
+ operation: 'delete_temp_file',
1057
1087
  });
1058
1088
  });
1059
- await fs.unlink(prTitleFile).catch((unlinkError) => {
1089
+ await fs.unlink(prTitleFile).catch(unlinkError => {
1060
1090
  reportError(unlinkError, {
1061
1091
  context: 'pr_title_file_cleanup',
1062
1092
  prTitleFile,
1063
- operation: 'delete_temp_file'
1093
+ operation: 'delete_temp_file',
1064
1094
  });
1065
1095
  });
1066
1096
 
@@ -1089,7 +1119,9 @@ ${prBody}`, { verbose: true });
1089
1119
  // CRITICAL: Verify the PR was actually created by querying GitHub API
1090
1120
  // This is essential because gh pr create can return a URL but PR creation might have failed
1091
1121
  await log(formatAligned('🔍', 'Verifying:', 'PR creation...'), { verbose: true });
1092
- const verifyResult = await $({ silent: true })`gh pr view ${localPrNumber} --repo ${owner}/${repo} --json number,url,state 2>&1`;
1122
+ const verifyResult = await $({
1123
+ silent: true,
1124
+ })`gh pr view ${localPrNumber} --repo ${owner}/${repo} --json number,url,state 2>&1`;
1093
1125
 
1094
1126
  if (verifyResult.code === 0) {
1095
1127
  try {
@@ -1197,18 +1229,28 @@ ${prBody}`, { verbose: true });
1197
1229
  const linkCheckResult = await $`gh api graphql -f query='query { repository(owner: "${owner}", name: "${repo}") { pullRequest(number: ${localPrNumber}) { closingIssuesReferences(first: 10) { nodes { number } } } } }' --jq '.data.repository.pullRequest.closingIssuesReferences.nodes[].number'`;
1198
1230
 
1199
1231
  if (linkCheckResult.code === 0) {
1200
- const linkedIssues = linkCheckResult.stdout.toString().trim().split('\n').filter(n => n);
1232
+ const linkedIssues = linkCheckResult.stdout
1233
+ .toString()
1234
+ .trim()
1235
+ .split('\n')
1236
+ .filter(n => n);
1201
1237
  if (linkedIssues.includes(issueNumber)) {
1202
1238
  await log(formatAligned('✅', 'Link verified:', `Issue #${issueNumber} → PR #${localPrNumber}`));
1203
1239
  } else {
1204
1240
  // This is a problem - the link wasn't created
1205
1241
  await log('');
1206
- await log(formatAligned('⚠️', 'ISSUE LINK MISSING:', 'PR not linked to issue'), { level: 'warning' });
1242
+ await log(formatAligned('⚠️', 'ISSUE LINK MISSING:', 'PR not linked to issue'), {
1243
+ level: 'warning',
1244
+ });
1207
1245
  await log('');
1208
1246
 
1209
1247
  if (argv.fork) {
1210
- await log(' The PR was created from a fork but wasn\'t linked to the issue.', { level: 'warning' });
1211
- await log(` Expected: "Fixes ${owner}/${repo}#${issueNumber}" in PR body`, { level: 'warning' });
1248
+ await log(" The PR was created from a fork but wasn't linked to the issue.", {
1249
+ level: 'warning',
1250
+ });
1251
+ await log(` Expected: "Fixes ${owner}/${repo}#${issueNumber}" in PR body`, {
1252
+ level: 'warning',
1253
+ });
1212
1254
  await log('');
1213
1255
  await log(' To fix manually:', { level: 'warning' });
1214
1256
  await log(` 1. Edit the PR description at: ${prUrl}`, { level: 'warning' });
@@ -1235,7 +1277,7 @@ ${prBody}`, { verbose: true });
1235
1277
  context: 'pr_issue_link_verification',
1236
1278
  prUrl,
1237
1279
  issueNumber,
1238
- operation: 'verify_issue_link'
1280
+ operation: 'verify_issue_link',
1239
1281
  });
1240
1282
  const expectedRef = argv.fork ? `${owner}/${repo}#${issueNumber}` : `#${issueNumber}`;
1241
1283
  await log(`⚠️ Could not verify issue linking: ${linkError.message}`, { level: 'warning' });
@@ -1256,7 +1298,7 @@ ${prBody}`, { verbose: true });
1256
1298
  context: 'pr_creation',
1257
1299
  issueNumber,
1258
1300
  branchName,
1259
- operation: 'create_pull_request'
1301
+ operation: 'create_pull_request',
1260
1302
  });
1261
1303
  const errorMsg = prCreateError.message || '';
1262
1304
 
@@ -1273,7 +1315,7 @@ ${prBody}`, { verbose: true });
1273
1315
  // Check for specific error types
1274
1316
  // Note: Assignee errors are now handled by automatic retry in the try block above
1275
1317
  // This catch block only handles other types of PR creation failures
1276
- if (errorMsg.includes('No commits between') || errorMsg.includes('Head sha can\'t be blank')) {
1318
+ if (errorMsg.includes('No commits between') || errorMsg.includes("Head sha can't be blank")) {
1277
1319
  // Empty PR error
1278
1320
  await log('');
1279
1321
  await log(formatAligned('❌', 'PR CREATION FAILED', ''), { level: 'error' });
@@ -1287,8 +1329,8 @@ ${prBody}`, { verbose: true });
1287
1329
  }
1288
1330
  await log('');
1289
1331
  await log(' 💡 Possible causes:');
1290
- await log(' • The branch wasn\'t pushed properly');
1291
- await log(' • The commit wasn\'t created');
1332
+ await log(" • The branch wasn't pushed properly");
1333
+ await log(" • The commit wasn't created");
1292
1334
  await log(' • GitHub sync issue');
1293
1335
  await log('');
1294
1336
  await log(' 🔧 How to fix:');
@@ -1333,7 +1375,7 @@ ${prBody}`, { verbose: true });
1333
1375
  reportError(prError, {
1334
1376
  context: 'auto_pr_creation',
1335
1377
  issueNumber,
1336
- operation: 'handle_auto_pr'
1378
+ operation: 'handle_auto_pr',
1337
1379
  });
1338
1380
 
1339
1381
  // CRITICAL: PR creation failure should stop the entire process
@@ -1371,4 +1413,4 @@ ${prBody}`, { verbose: true });
1371
1413
  }
1372
1414
 
1373
1415
  return { prUrl, prNumber: localPrNumber, claudeCommitHash };
1374
- }
1416
+ }
@@ -9,19 +9,7 @@
9
9
  // Import Sentry integration
10
10
  import { reportError } from './sentry.lib.mjs';
11
11
 
12
- export async function handleBranchCheckoutError({
13
- branchName,
14
- prNumber,
15
- errorOutput,
16
- issueUrl,
17
- owner,
18
- repo,
19
- tempDir,
20
- argv,
21
- formatAligned,
22
- log,
23
- $
24
- }) {
12
+ export async function handleBranchCheckoutError({ branchName, prNumber, errorOutput, issueUrl, owner, repo, tempDir, argv, formatAligned, log, $ }) {
25
13
  // Check if this is a PR from a fork
26
14
  let isForkPR = false;
27
15
  let forkOwner = null;
@@ -60,7 +48,7 @@ export async function handleBranchCheckoutError({
60
48
  forkOwner,
61
49
  forkRepoFullName,
62
50
  branchName,
63
- operation: 'verify_fork_branch'
51
+ operation: 'verify_fork_branch',
64
52
  });
65
53
  // Branch doesn't exist in fork or can't access it
66
54
  }
@@ -71,7 +59,7 @@ export async function handleBranchCheckoutError({
71
59
  context: 'handle_branch_checkout_error',
72
60
  prNumber,
73
61
  branchName,
74
- operation: 'analyze_branch_error'
62
+ operation: 'analyze_branch_error',
75
63
  });
76
64
  // Ignore error, proceed with default message
77
65
  }
@@ -82,7 +70,7 @@ export async function handleBranchCheckoutError({
82
70
  if (userResult.code === 0) {
83
71
  const currentUser = userResult.stdout.toString().trim();
84
72
  // Determine fork name based on --prefix-fork-name-with-owner-name option
85
- const userForkRepoName = (argv && argv.prefixForkNameWithOwnerName) ? `${owner}-${repo}` : repo;
73
+ const userForkRepoName = argv && argv.prefixForkNameWithOwnerName ? `${owner}-${repo}` : repo;
86
74
  const userForkRepo = `${currentUser}/${userForkRepoName}`;
87
75
  const forkCheckResult = await $`gh repo view ${userForkRepo} --json parent 2>/dev/null`;
88
76
  if (forkCheckResult.code === 0) {
@@ -108,7 +96,7 @@ export async function handleBranchCheckoutError({
108
96
  userForkOwner: currentUser,
109
97
  userForkRepoName,
110
98
  branchName,
111
- operation: 'check_branch_in_user_fork'
99
+ operation: 'check_branch_in_user_fork',
112
100
  });
113
101
  // Branch doesn't exist in user's fork
114
102
  }
@@ -121,7 +109,7 @@ export async function handleBranchCheckoutError({
121
109
  context: 'handle_branch_checkout_error',
122
110
  prNumber,
123
111
  branchName,
124
- operation: 'analyze_branch_error'
112
+ operation: 'analyze_branch_error',
125
113
  });
126
114
  // Ignore error, proceed with default message
127
115
  }
@@ -159,7 +147,7 @@ export async function handleBranchCheckoutError({
159
147
  if (branchExistsInFork) {
160
148
  await log(` The PR branch '${branchName}' exists in the fork repository:`);
161
149
  await log(` https://github.com/${forkOwner}/${displayForkRepo}`);
162
- await log(' but you\'re trying to access it from the main repository:');
150
+ await log(" but you're trying to access it from the main repository:");
163
151
  await log(` https://github.com/${owner}/${repo}`);
164
152
  await log(' This branch does NOT exist in the main repository.');
165
153
  } else {
@@ -223,22 +211,14 @@ export async function handleBranchCheckoutError({
223
211
  await log(` 2. Check remote branches: cd ${tempDir} && git branch -r`);
224
212
  await log(` 3. Try fetching manually: cd ${tempDir} && git fetch origin`);
225
213
  await log('');
226
- await log(' If you don\'t have write access to this repository,');
214
+ await log(" If you don't have write access to this repository,");
227
215
  await log(' consider using the --fork option:');
228
216
  const altFullUrl = prNumber ? `https://github.com/${owner}/${repo}/pull/${prNumber}` : issueUrl;
229
217
  await log(` ./solve.mjs "${altFullUrl}" --fork`);
230
218
  }
231
219
  }
232
220
 
233
- export async function handleBranchCreationError({
234
- branchName,
235
- errorOutput,
236
- tempDir,
237
- owner,
238
- repo,
239
- formatAligned,
240
- log
241
- }) {
221
+ export async function handleBranchCreationError({ branchName, errorOutput, tempDir, owner, repo, formatAligned, log }) {
242
222
  await log(`${formatAligned('❌', 'BRANCH CREATION FAILED', '')}`, { level: 'error' });
243
223
  await log('');
244
224
  await log(' 🔍 What happened:');
@@ -263,26 +243,17 @@ export async function handleBranchCreationError({
263
243
  await log(` 3. View existing branches: cd ${tempDir} && git branch -a`);
264
244
  }
265
245
 
266
- export async function handleBranchVerificationError({
267
- isContinueMode,
268
- branchName,
269
- actualBranch,
270
- prNumber,
271
- owner,
272
- repo,
273
- tempDir,
274
- formatAligned,
275
- log,
276
- $
277
- }) {
246
+ export async function handleBranchVerificationError({ isContinueMode, branchName, actualBranch, prNumber, owner, repo, tempDir, formatAligned, log, $ }) {
278
247
  await log('');
279
- await log(`${formatAligned('❌', isContinueMode ? 'BRANCH CHECKOUT FAILED' : 'BRANCH CREATION FAILED', '')}`, { level: 'error' });
248
+ await log(`${formatAligned('❌', isContinueMode ? 'BRANCH CHECKOUT FAILED' : 'BRANCH CREATION FAILED', '')}`, {
249
+ level: 'error',
250
+ });
280
251
  await log('');
281
252
  await log(' 🔍 What happened:');
282
253
  if (isContinueMode) {
283
- await log(' Git checkout command didn\'t switch to the PR branch.');
254
+ await log(" Git checkout command didn't switch to the PR branch.");
284
255
  } else {
285
- await log(' Git checkout -b command didn\'t create or switch to the branch.');
256
+ await log(" Git checkout -b command didn't create or switch to the branch.");
286
257
  }
287
258
  if (owner && repo) {
288
259
  await log(` Repository: https://github.com/${owner}/${repo}`);
@@ -308,7 +279,7 @@ export async function handleBranchVerificationError({
308
279
 
309
280
  if (isContinueMode) {
310
281
  await log(' 💡 This might mean:');
311
- await log(' • PR branch doesn\'t exist on remote');
282
+ await log(" • PR branch doesn't exist on remote");
312
283
  await log(' • Branch name mismatch');
313
284
  await log(' • Network/permission issues');
314
285
  await log('');
@@ -338,4 +309,4 @@ export async function handleBranchVerificationError({
338
309
  await log('');
339
310
  await log(` 📂 Working directory: ${tempDir}`);
340
311
  await log('');
341
- }
312
+ }