@link-assistant/hive-mind 0.51.12 → 0.51.14

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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 0.51.14
4
+
5
+ ### Patch Changes
6
+
7
+ - 4e4fe08: Improve fork divergence error message clarity
8
+ - Remove misleading "Option 3: Work without syncing fork (NOT RECOMMENDED)"
9
+ - Add new Option 1 for deleting and recreating fork (marked as SIMPLEST)
10
+ - Reorder options by simplicity: deletion → auto-resolution → manual resolution
11
+ - Move risk warnings inline with relevant options for better context
12
+ - Add comprehensive case study documentation in docs/case-studies/issue-972/
13
+
14
+ This change makes the error message more useful by removing options that were never actually viable and adding the fork deletion option as the cleanest solution for most fork divergence scenarios.
15
+
16
+ ## 0.51.13
17
+
18
+ ### Patch Changes
19
+
20
+ - 20d6f3a: Fix URL hash fragment parsing - URLs with hash fragments like #issuecomment-123 are now correctly parsed. Previously, solving a PR with a comment URL like /pull/9#issuecomment-123 would fail because the PR number was extracted as "9#issuecomment-123" instead of "9".
21
+
3
22
  ## 0.51.12
4
23
 
5
24
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "0.51.12",
3
+ "version": "0.51.14",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
package/src/solve.mjs CHANGED
@@ -52,7 +52,7 @@ const { log, setLogFile, getLogFile, getAbsoluteLogPath, cleanErrorMessage, form
52
52
  const githubLib = await import('./github.lib.mjs');
53
53
  const { sanitizeLogContent, attachLogToGitHub } = githubLib;
54
54
  const validation = await import('./solve.validation.lib.mjs');
55
- const { validateGitHubUrl, showAttachLogsWarning, initializeLogFile, validateUrlRequirement, validateContinueOnlyOnFeedback, performSystemChecks, parseUrlComponents } = validation;
55
+ const { validateGitHubUrl, showAttachLogsWarning, initializeLogFile, validateUrlRequirement, validateContinueOnlyOnFeedback, performSystemChecks } = validation;
56
56
  const autoContinue = await import('./solve.auto-continue.lib.mjs');
57
57
  const { processAutoContinueForIssue } = autoContinue;
58
58
  const repository = await import('./solve.repository.lib.mjs');
@@ -175,8 +175,11 @@ const urlValidation = validateGitHubUrl(issueUrl);
175
175
  if (!urlValidation.isValid) {
176
176
  await safeExit(1, 'Invalid GitHub URL');
177
177
  }
178
- const { isIssueUrl, isPrUrl, normalizedUrl } = urlValidation;
178
+ const { isIssueUrl, isPrUrl, normalizedUrl, owner, repo, number: urlNumber } = urlValidation;
179
179
  issueUrl = normalizedUrl || issueUrl;
180
+ // Store owner and repo globally for error handlers early
181
+ global.owner = owner;
182
+ global.repo = repo;
180
183
  // Setup unhandled error handlers to ensure log path is always shown
181
184
  const errorHandlerOptions = {
182
185
  log,
@@ -222,11 +225,9 @@ if (argv.verbose) {
222
225
  await log(` Is PR URL: ${!!isPrUrl}`, { verbose: true });
223
226
  }
224
227
  const claudePath = process.env.CLAUDE_PATH || 'claude';
225
- // Parse URL components using validation module
226
- const { owner, repo, urlNumber } = parseUrlComponents(issueUrl);
227
- // Store owner and repo globally for error handlers
228
- global.owner = owner;
229
- global.repo = repo;
228
+ // Note: owner, repo, and urlNumber are already extracted from validateGitHubUrl() above
229
+ // The parseUrlComponents() call was removed as it had a bug with hash fragments (#issuecomment-xyz)
230
+ // and the validation result already provides these values correctly parsed
230
231
 
231
232
  // Handle --auto-fork option: automatically fork public repositories without write access
232
233
  if (argv.autoFork && !argv.fork) {
@@ -946,18 +946,19 @@ export const setupUpstreamAndSync = async (tempDir, forkedRepo, upstreamRemote,
946
946
  }
947
947
  } else {
948
948
  // Flag is not enabled - provide guidance
949
- await log(' ⚠️ RISKS of force-pushing:');
950
- await log(' • Overwrites fork history - any unique commits in your fork will be LOST');
951
- await log(' • Other collaborators working on your fork may face conflicts');
952
- await log(' • Cannot be undone - use with extreme caution');
953
- await log('');
954
949
  await log(' 💡 Your options:');
955
950
  await log('');
956
- await log(' Option 1: Enable automatic force-push (DANGEROUS)');
951
+ await log(' Option 1: Delete your fork and recreate it (SIMPLEST)');
952
+ await log(` gh repo delete ${forkedRepo}`);
953
+ await log(' Then run the solve command again - the fork will be recreated automatically');
954
+ await log(' ⚠️ Only use this if your fork has no unique commits you need to preserve');
955
+ await log('');
956
+ await log(' Option 2: Enable automatic force-push (DANGEROUS)');
957
957
  await log(' Add --allow-fork-divergence-resolution-using-force-push-with-lease flag to your command');
958
958
  await log(' This will automatically sync your fork with upstream using force-with-lease');
959
+ await log(' ⚠️ Overwrites fork history - any unique commits will be LOST');
959
960
  await log('');
960
- await log(' Option 2: Manually resolve the divergence');
961
+ await log(' Option 3: Manually resolve the divergence');
961
962
  await log(' 1. Decide if you need any commits unique to your fork');
962
963
  await log(' 2. If yes, cherry-pick them after syncing');
963
964
  await log(' 3. If no, manually force-push:');
@@ -965,10 +966,6 @@ export const setupUpstreamAndSync = async (tempDir, forkedRepo, upstreamRemote,
965
966
  await log(` git reset --hard upstream/${upstreamDefaultBranch}`);
966
967
  await log(` git push --force origin ${upstreamDefaultBranch}`);
967
968
  await log('');
968
- await log(' Option 3: Work without syncing fork (NOT RECOMMENDED)');
969
- await log(' Your fork will remain out-of-sync with upstream');
970
- await log(' May cause merge conflicts in pull requests');
971
- await log('');
972
969
  await log(' 🔧 To proceed with auto-resolution, restart with:');
973
970
  await log(` solve ${argv.url || argv['issue-url'] || argv._[0] || '<issue-url>'} --allow-fork-divergence-resolution-using-force-push-with-lease`);
974
971
  await log('');
@@ -272,13 +272,23 @@ export const performSystemChecks = async (minDiskSpace = 2048, skipToolConnectio
272
272
  return true;
273
273
  };
274
274
 
275
- // Parse URL components
275
+ // Parse URL components using Node.js URL API
276
+ // Note: This function is a simpler alternative to parseGitHubUrl for cases where
277
+ // you only need owner, repo, and urlNumber without full validation.
278
+ // For full validation, use validateGitHubUrl() which internally uses parseGitHubUrl().
279
+ // Uses Node.js URL API (https://nodejs.org/api/url.html) for stable parsing.
276
280
  export const parseUrlComponents = issueUrl => {
277
- const urlParts = issueUrl.split('/');
281
+ // Use Node.js URL API for reliable parsing
282
+ // This automatically handles hash fragments, query params, and edge cases
283
+ const urlObj = new globalThis.URL(issueUrl);
284
+
285
+ // Extract path segments, filtering out empty strings from leading/trailing slashes
286
+ const pathParts = urlObj.pathname.split('/').filter(p => p);
287
+
278
288
  return {
279
- owner: urlParts[3],
280
- repo: urlParts[4],
281
- urlNumber: urlParts[6], // Could be issue or PR number
289
+ owner: pathParts[0],
290
+ repo: pathParts[1],
291
+ urlNumber: pathParts[3], // Could be issue or PR number (pathParts[2] is 'issues' or 'pull')
282
292
  };
283
293
  };
284
294