@link-assistant/hive-mind 1.23.6 → 1.23.8

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,23 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.23.8
4
+
5
+ ### Patch Changes
6
+
7
+ - Fix spelling: rename --auto-restart-until-mergable to --auto-restart-until-mergeable throughout the codebase. This includes CLI options, function names, variable names, documentation, and code comments to use the correct English spelling.
8
+
9
+ Increase limit reset buffer from 5 to 10 minutes and add random jitter (0-5 min) to avoid thundering herd problem when multiple instances wait for the same limit reset. Format reset time in PR comments with relative time and UTC timezone for better user understanding.
10
+
11
+ ## 1.23.7
12
+
13
+ ### Patch Changes
14
+
15
+ - d951635: Fix --auto-restart-until-mergeable false positive on empty CI checks
16
+
17
+ The `--auto-restart-until-mergeable` mode was incorrectly posting "Ready to merge" when CI checks hadn't started yet. This was caused by JavaScript's vacuous truth: `[].every(fn)` returns `true`, so an empty checks array would pass all validation.
18
+
19
+ Fix: Return `pending` status when no CI checks exist yet, instead of `success`.
20
+
3
21
  ## 1.23.6
4
22
 
5
23
  ### Patch Changes
@@ -127,7 +145,7 @@
127
145
  ### Patch Changes
128
146
 
129
147
  - fdd8eaa: Fix auto-merge failure in fork mode with permission pre-check (Issue #1226)
130
- - Add fork-mode guard in `startAutoRestartUntilMergable()` to detect when `--auto-merge` cannot work
148
+ - Add fork-mode guard in `startAutoRestartUntilMergeable()` to detect when `--auto-merge` cannot work
131
149
  - Add `checkMergePermissions()` function to verify write/push/admin/maintain access before merge attempts
132
150
  - Add permission pre-check in `attemptAutoMerge()` to fail fast when user lacks write access
133
151
  - Post "Ready to merge" comment to PR when auto-merge cannot be performed due to permissions
@@ -375,7 +393,7 @@
375
393
 
376
394
  - 5723a93: fix: prevent early exit when --auto-merge flag is used
377
395
 
378
- The `verifyResults()` function was calling `safeExit(0)` before the auto-merge logic could run. This caused the `--auto-merge` flag to be silently ignored. Now the exit condition properly checks for `argv.autoMerge` and `argv.autoRestartUntilMergable` flags.
396
+ The `verifyResults()` function was calling `safeExit(0)` before the auto-merge logic could run. This caused the `--auto-merge` flag to be silently ignored. Now the exit condition properly checks for `argv.autoMerge|autoRestartUntilMergeable` and `argv.autoMerge|autoRestartUntilMergeable` flags.
379
397
 
380
398
  ## 1.15.1
381
399
 
@@ -458,11 +476,11 @@
458
476
 
459
477
  ### Minor Changes
460
478
 
461
- - 03adcb6: Add --auto-merge and --auto-restart-until-mergable options for autonomous PR management
479
+ - 03adcb6: Add --auto-merge and --auto-restart-until-mergeable options for autonomous PR management
462
480
 
463
481
  New CLI options:
464
- - `--auto-merge`: Automatically merge the pull request when CI passes and PR is mergeable. Implies --auto-restart-until-mergable.
465
- - `--auto-restart-until-mergable`: Auto-restart the AI agent until PR becomes mergeable (no iteration limit). Restarts on new comments from non-bot users, CI failures, merge conflicts, or uncommitted changes. Does NOT auto-merge.
482
+ - `--auto-merge`: Automatically merge the pull request when CI passes and PR is mergeable. Implies --auto-restart-until-mergeable.
483
+ - `--auto-restart-until-mergeable`: Auto-restart the AI agent until PR becomes mergeable (no iteration limit). Restarts on new comments from non-bot users, CI failures, merge conflicts, or uncommitted changes. Does NOT auto-merge.
466
484
 
467
485
  Features:
468
486
  - Non-bot comment detection with configurable bot patterns
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.23.6",
3
+ "version": "1.23.8",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -61,10 +61,19 @@ export const autoContinue = {
61
61
 
62
62
  // Auto-resume on limit reset configurations
63
63
  // See: https://github.com/link-assistant/hive-mind/issues/1152
64
+ // See: https://github.com/link-assistant/hive-mind/issues/1236
64
65
  export const limitReset = {
65
66
  // Buffer time to wait after limit reset (in milliseconds)
66
- // Default: 5 minutes - accounts for server time differences
67
- bufferMs: parseIntWithDefault('HIVE_MIND_LIMIT_RESET_BUFFER_MS', 5 * 60 * 1000),
67
+ // Default: 10 minutes - accounts for server time differences and API propagation delays
68
+ // Increased from 5 to 10 minutes to reduce risk of hitting limits again immediately
69
+ // See: https://github.com/link-assistant/hive-mind/issues/1236
70
+ bufferMs: parseIntWithDefault('HIVE_MIND_LIMIT_RESET_BUFFER_MS', 10 * 60 * 1000),
71
+ // Random jitter added to buffer to avoid thundering herd problem (in milliseconds)
72
+ // When multiple instances wait for the same limit reset, jitter distributes their
73
+ // resume times to reduce simultaneous API load
74
+ // Default: 5 minutes (0 to 5 minutes random) - total wait after reset: 10-15 minutes
75
+ // See: https://github.com/link-assistant/hive-mind/issues/1236
76
+ jitterMs: parseIntWithDefault('HIVE_MIND_LIMIT_RESET_JITTER_MS', 5 * 60 * 1000),
68
77
  };
69
78
 
70
79
  // GitHub API limits
@@ -340,6 +340,22 @@ export async function checkPRCIStatus(owner, repo, prNumber, verbose = false) {
340
340
  })),
341
341
  ];
342
342
 
343
+ // Issue #1304: If no checks exist yet, treat as pending
344
+ // This handles the race condition where CI hasn't started yet after a commit is pushed.
345
+ // An empty array would otherwise pass all checks due to JavaScript's vacuous truth
346
+ // ([].every(fn) returns true for any fn).
347
+ if (allChecks.length === 0) {
348
+ if (verbose) {
349
+ console.log(`[VERBOSE] /merge: PR #${prNumber} has no CI checks yet - treating as pending`);
350
+ }
351
+ return {
352
+ status: 'pending',
353
+ checks: [],
354
+ allPassed: false,
355
+ hasPending: true,
356
+ };
357
+ }
358
+
343
359
  const hasPending = allChecks.some(c => c.status !== 'completed' || c.conclusion === null);
344
360
  const allPassed = !hasPending && allChecks.every(c => c.conclusion === 'success' || c.conclusion === 'skipped' || c.conclusion === 'neutral');
345
361
  const hasFailed = allChecks.some(c => c.conclusion === 'failure' || c.conclusion === 'cancelled' || c.conclusion === 'timed_out');
@@ -1,24 +1,15 @@
1
1
  #!/usr/bin/env node
2
- // GitHub-related utility functions
3
- // Check if use is already defined (when imported from solve.mjs)
4
- // If not, fetch it (when running standalone)
5
- if (typeof globalThis.use === 'undefined') {
6
- globalThis.use = (await eval(await (await fetch('https://unpkg.com/use-m/use.js')).text())).use;
7
- }
8
- // Use command-stream for consistent $ behavior
9
- const { $ } = await use('command-stream');
10
- // Import log and maskToken from general lib
2
+ // GitHub-related utility functions. Check if use is already defined (when imported from solve.mjs), if not, fetch it (when running standalone)
3
+ if (typeof globalThis.use === 'undefined') globalThis.use = (await eval(await (await fetch('https://unpkg.com/use-m/use.js')).text())).use;
4
+ const { $ } = await use('command-stream'); // Use command-stream for consistent $ behavior
11
5
  import { log, maskToken, cleanErrorMessage } from './lib.mjs';
12
6
  import { reportError } from './sentry.lib.mjs';
13
7
  import { githubLimits, timeouts } from './config.lib.mjs';
14
- // Import batch operations from separate module
15
8
  import { batchCheckPullRequestsForIssues as batchCheckPRs, batchCheckArchivedRepositories as batchCheckArchived } from './github.batch.lib.mjs';
16
- // Import token sanitization from dedicated module (Issue #1037 fix)
17
9
  import { isSafeToken, isHexInSafeContext, getGitHubTokensFromFiles, getGitHubTokensFromCommand, sanitizeLogContent } from './token-sanitization.lib.mjs';
18
- // Re-export token sanitization functions for backward compatibility
19
- export { isSafeToken, isHexInSafeContext, getGitHubTokensFromFiles, getGitHubTokensFromCommand, sanitizeLogContent };
20
- // Import log upload function from separate module
10
+ export { isSafeToken, isHexInSafeContext, getGitHubTokensFromFiles, getGitHubTokensFromCommand, sanitizeLogContent }; // Re-export for backward compatibility
21
11
  import { uploadLogWithGhUploadLog } from './log-upload.lib.mjs';
12
+ import { formatResetTimeWithRelative } from './usage-limit.lib.mjs'; // See: https://github.com/link-assistant/hive-mind/issues/1236
22
13
 
23
14
  /**
24
15
  * Build cost estimation string for log comments
@@ -488,7 +479,11 @@ The automated solution draft was interrupted because the ${toolName} usage limit
488
479
  - **Limit Type**: Usage limit exceeded`;
489
480
 
490
481
  if (limitResetTime) {
491
- logComment += `\n- **Reset Time**: ${limitResetTime}`;
482
+ // Format reset time with relative time and UTC for better user understanding
483
+ // Shows "in 14m (Feb 6, 3:00 PM UTC)" instead of just "4:00 PM"
484
+ // See: https://github.com/link-assistant/hive-mind/issues/1236
485
+ const formattedResetTime = formatResetTimeWithRelative(limitResetTime, global.limitTimezone || null) || limitResetTime;
486
+ logComment += `\n- **Reset Time**: ${formattedResetTime}`;
492
487
  }
493
488
 
494
489
  if (sessionId) {
@@ -681,7 +676,11 @@ The automated solution draft was interrupted because the ${toolName} usage limit
681
676
  - **Limit Type**: Usage limit exceeded`;
682
677
 
683
678
  if (limitResetTime) {
684
- logUploadComment += `\n- **Reset Time**: ${limitResetTime}`;
679
+ // Format reset time with relative time and UTC for better user understanding
680
+ // Shows "in 14m (Feb 6, 3:00 PM UTC)" instead of just "4:00 PM"
681
+ // See: https://github.com/link-assistant/hive-mind/issues/1236
682
+ const formattedUploadResetTime = formatResetTimeWithRelative(limitResetTime, global.limitTimezone || null) || limitResetTime;
683
+ logUploadComment += `\n- **Reset Time**: ${formattedUploadResetTime}`;
685
684
  }
686
685
 
687
686
  if (sessionId) {
@@ -205,7 +205,7 @@ const KNOWN_OPTION_NAMES = [
205
205
  'auto-continue-only-on-new-comments',
206
206
  'auto-restart-on-limit-reset',
207
207
  'auto-restart-on-non-updated-pull-request-description',
208
- 'auto-restart-until-mergable',
208
+ 'auto-restart-until-mergeable',
209
209
  'auto-merge',
210
210
  'auto-gitkeep-file',
211
211
  'playwright-mcp-auto-cleanup',
@@ -81,18 +81,27 @@ export const autoContinueWhenLimitResets = async (issueUrl, sessionId, argv, sho
81
81
  const baseWaitMs = calculateWaitTime(resetTime);
82
82
 
83
83
  // Add buffer time after limit reset to account for server time differences
84
- // Default: 5 minutes (configurable via HIVE_MIND_LIMIT_RESET_BUFFER_MS)
84
+ // Default: 10 minutes (configurable via HIVE_MIND_LIMIT_RESET_BUFFER_MS)
85
85
  // See: https://github.com/link-assistant/hive-mind/issues/1152
86
+ // See: https://github.com/link-assistant/hive-mind/issues/1236
86
87
  const bufferMs = limitReset.bufferMs;
87
- const waitMs = baseWaitMs + bufferMs;
88
+
89
+ // Add random jitter to avoid thundering herd problem when multiple instances
90
+ // wait for the same limit reset time and all resume simultaneously
91
+ // Default: random 0-5 minutes (configurable via HIVE_MIND_LIMIT_RESET_JITTER_MS)
92
+ // See: https://github.com/link-assistant/hive-mind/issues/1236
93
+ const jitterMs = Math.floor(Math.random() * limitReset.jitterMs);
94
+ const totalBufferMs = bufferMs + jitterMs;
95
+ const waitMs = baseWaitMs + totalBufferMs;
88
96
  const bufferMinutes = Math.round(bufferMs / 60000);
97
+ const jitterSeconds = Math.round(jitterMs / 1000);
89
98
 
90
99
  // Format reset time with relative time and UTC for better user understanding
91
100
  // See: https://github.com/link-assistant/hive-mind/issues/1152
92
101
  const formattedResetTime = formatResetTimeWithRelative(resetTime, timezone);
93
102
 
94
- await log(`\n⏰ Waiting until ${formattedResetTime} + ${bufferMinutes} min buffer for limit to reset...`);
95
- await log(` Wait time: ${formatWaitTime(waitMs)} (includes ${bufferMinutes} min buffer for server time differences)`);
103
+ await log(`\n⏰ Waiting until ${formattedResetTime} + ${bufferMinutes} min buffer + ${jitterSeconds}s jitter for limit to reset...`);
104
+ await log(` Wait time: ${formatWaitTime(waitMs)} (includes ${bufferMinutes} min buffer + ${jitterSeconds}s random jitter)`);
96
105
  await log(` Current time: ${new Date().toLocaleTimeString()}`);
97
106
 
98
107
  // Show countdown every 30 minutes for long waits, every minute for short waits
@@ -111,7 +120,7 @@ export const autoContinueWhenLimitResets = async (issueUrl, sessionId, argv, sho
111
120
  clearInterval(countdownTimer);
112
121
 
113
122
  const actionType = isRestart ? 'Restarting' : 'Resuming';
114
- await log(`\n✅ Limit reset time reached (+ ${bufferMinutes} min buffer)! ${actionType} session...`);
123
+ await log(`\n✅ Limit reset time reached (+ ${bufferMinutes} min buffer + ${jitterSeconds}s jitter)! ${actionType} session...`);
115
124
  await log(` Current time: ${new Date().toLocaleTimeString()}`);
116
125
 
117
126
  // Recursively call the solve script
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Auto-merge and auto-restart-until-mergable module for solve.mjs
4
+ * Auto-merge and auto-restart-until-mergeable module for solve.mjs
5
5
  * Handles automatic merging of PRs and continuous restart until PR becomes mergeable
6
6
  *
7
7
  * Uses shared utilities from solve.restart-shared.lib.mjs for common functions.
@@ -25,7 +25,7 @@ const { log, cleanErrorMessage, formatAligned, getLogFile } = lib;
25
25
 
26
26
  // Note: We don't use detectAndCountFeedback from solve.feedback.lib.mjs
27
27
  // because we have our own non-bot comment detection logic that's more
28
- // appropriate for auto-restart-until-mergable mode
28
+ // appropriate for auto-restart-until-mergeable mode
29
29
 
30
30
  // Import Sentry integration
31
31
  const sentryLib = await import('./sentry.lib.mjs');
@@ -176,9 +176,9 @@ const getMergeBlockers = async (owner, repo, prNumber, verbose = false) => {
176
176
 
177
177
  /**
178
178
  * Main function: Watch and restart until PR becomes mergeable
179
- * This implements --auto-restart-until-mergable functionality
179
+ * This implements --auto-restart-until-mergeable functionality
180
180
  */
181
- export const watchUntilMergable = async params => {
181
+ export const watchUntilMergeable = async params => {
182
182
  const { issueUrl, owner, repo, issueNumber, prNumber, prBranch, branchName, tempDir, argv } = params;
183
183
 
184
184
  const watchInterval = argv.watchInterval || 60; // seconds
@@ -194,9 +194,9 @@ export const watchUntilMergable = async params => {
194
194
  let currentBackoffSeconds = watchInterval;
195
195
 
196
196
  await log('');
197
- await log(formatAligned('🔄', 'AUTO-RESTART-UNTIL-MERGABLE MODE ACTIVE', ''));
197
+ await log(formatAligned('🔄', 'AUTO-RESTART-UNTIL-MERGEABLE MODE ACTIVE', ''));
198
198
  await log(formatAligned('', 'Monitoring PR:', `#${prNumber}`, 2));
199
- await log(formatAligned('', 'Mode:', isAutoMerge ? 'Auto-merge (will merge when ready)' : 'Auto-restart-until-mergable (will NOT auto-merge)', 2));
199
+ await log(formatAligned('', 'Mode:', isAutoMerge ? 'Auto-merge (will merge when ready)' : 'Auto-restart-until-mergeable (will NOT auto-merge)', 2));
200
200
  await log(formatAligned('', 'Checking interval:', `${watchInterval} seconds`, 2));
201
201
  await log(formatAligned('', 'Stop conditions:', 'PR merged, PR closed, or becomes mergeable', 2));
202
202
  await log(formatAligned('', 'Restart triggers:', 'New non-bot comments, CI failures, merge conflicts', 2));
@@ -215,7 +215,7 @@ export const watchUntilMergable = async params => {
215
215
  const isMerged = await checkPRMerged(owner, repo, prNumber);
216
216
  if (isMerged) {
217
217
  await log('');
218
- await log(formatAligned('🎉', 'PR MERGED!', 'Stopping auto-restart-until-mergable mode'));
218
+ await log(formatAligned('🎉', 'PR MERGED!', 'Stopping auto-restart-until-mergeable mode'));
219
219
  await log(formatAligned('', 'Pull request:', `#${prNumber} has been merged`, 2));
220
220
  await log('');
221
221
  return { success: true, reason: 'merged', latestSessionId, latestAnthropicCost };
@@ -225,7 +225,7 @@ export const watchUntilMergable = async params => {
225
225
  const isClosed = await checkPRClosed(owner, repo, prNumber);
226
226
  if (isClosed) {
227
227
  await log('');
228
- await log(formatAligned('🚫', 'PR CLOSED!', 'Stopping auto-restart-until-mergable mode'));
228
+ await log(formatAligned('🚫', 'PR CLOSED!', 'Stopping auto-restart-until-mergeable mode'));
229
229
  await log(formatAligned('', 'Pull request:', `#${prNumber} has been closed without merging`, 2));
230
230
  await log('');
231
231
  return { success: false, reason: 'closed', latestSessionId, latestAnthropicCost };
@@ -272,11 +272,11 @@ export const watchUntilMergable = async params => {
272
272
  } else {
273
273
  // Just report that PR is mergeable and exit
274
274
  await log(formatAligned('', 'PR is ready to be merged manually', '', 2));
275
- await log(formatAligned('', 'Exiting auto-restart-until-mergable mode', '', 2));
275
+ await log(formatAligned('', 'Exiting auto-restart-until-mergeable mode', '', 2));
276
276
 
277
277
  // Post success comment
278
278
  try {
279
- const commentBody = `## ✅ Ready to merge\n\nThis pull request is now ready to be merged:\n- All CI checks have passed\n- No merge conflicts\n- No pending changes\n\n---\n*Monitored by hive-mind with --auto-restart-until-mergable flag*`;
279
+ const commentBody = `## ✅ Ready to merge\n\nThis pull request is now ready to be merged:\n- All CI checks have passed\n- No merge conflicts\n- No pending changes\n\n---\n*Monitored by hive-mind with --auto-restart-until-mergeable flag*`;
280
280
  await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
281
281
  } catch {
282
282
  // Don't fail if comment posting fails
@@ -345,7 +345,7 @@ export const watchUntilMergable = async params => {
345
345
  }
346
346
 
347
347
  if (shouldRestart) {
348
- // Add standard instructions for auto-restart-until-mergable mode using shared utility
348
+ // Add standard instructions for auto-restart-until-mergeable mode using shared utility
349
349
  feedbackLines.push(...buildAutoRestartInstructions());
350
350
 
351
351
  await log(formatAligned('🔄', 'RESTART TRIGGERED:', restartReason));
@@ -353,7 +353,7 @@ export const watchUntilMergable = async params => {
353
353
 
354
354
  // Post a comment to PR about the restart
355
355
  try {
356
- const commentBody = `## 🔄 Auto-restart triggered\n\n**Reason:** ${restartReason}\n\nStarting new session to address the issues.\n\n---\n*Auto-restart-until-mergable mode is active. Will continue until PR becomes mergeable.*`;
356
+ const commentBody = `## 🔄 Auto-restart triggered\n\n**Reason:** ${restartReason}\n\nStarting new session to address the issues.\n\n---\n*Auto-restart-until-mergeable mode is active. Will continue until PR becomes mergeable.*`;
357
357
  await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
358
358
  await log(formatAligned('', '💬 Posted auto-restart notification to PR', '', 2));
359
359
  } catch (commentError) {
@@ -428,7 +428,7 @@ export const watchUntilMergable = async params => {
428
428
  try {
429
429
  const logFile = getLogFile();
430
430
  if (logFile) {
431
- const customTitle = `🔄 Auto-restart-until-mergable Log (iteration ${iteration})`;
431
+ const customTitle = `🔄 Auto-restart-until-mergeable Log (iteration ${iteration})`;
432
432
  await attachLogToGitHub({
433
433
  logFile,
434
434
  targetType: 'pr',
@@ -478,7 +478,7 @@ export const watchUntilMergable = async params => {
478
478
  lastCheckTime = currentTime;
479
479
  } catch (error) {
480
480
  reportError(error, {
481
- context: 'watch_until_mergable',
481
+ context: 'watch_until_mergeable',
482
482
  prNumber,
483
483
  owner,
484
484
  repo,
@@ -568,22 +568,22 @@ export const attemptAutoMerge = async params => {
568
568
  };
569
569
 
570
570
  /**
571
- * Start auto-restart-until-mergable mode
571
+ * Start auto-restart-until-mergeable mode
572
572
  */
573
- export const startAutoRestartUntilMergable = async params => {
573
+ export const startAutoRestartUntilMergeable = async params => {
574
574
  const { argv, owner, repo, prNumber } = params;
575
575
 
576
576
  // Determine the mode
577
577
  const isAutoMerge = argv.autoMerge || false;
578
- const isAutoRestartUntilMergable = argv.autoRestartUntilMergable || false;
578
+ const isAutoRestartUntilMergeable = argv.autoRestartUntilMergeable || false;
579
579
 
580
- if (!isAutoMerge && !isAutoRestartUntilMergable) {
580
+ if (!isAutoMerge && !isAutoRestartUntilMergeable) {
581
581
  return null; // Neither mode enabled
582
582
  }
583
583
 
584
584
  if (!prNumber) {
585
585
  await log('');
586
- await log(formatAligned('⚠️', 'Auto-restart-until-mergable:', 'Requires a pull request'));
586
+ await log(formatAligned('⚠️', 'Auto-restart-until-mergeable:', 'Requires a pull request'));
587
587
  await log(formatAligned('', 'Note:', 'This mode only works with existing PRs', 2));
588
588
  return null;
589
589
  }
@@ -632,18 +632,18 @@ export const startAutoRestartUntilMergable = async params => {
632
632
  }
633
633
  }
634
634
 
635
- // If --auto-merge implies --auto-restart-until-mergable
635
+ // If --auto-merge implies --auto-restart-until-mergeable
636
636
  if (isAutoMerge) {
637
- argv.autoRestartUntilMergable = true;
637
+ argv.autoRestartUntilMergeable = true;
638
638
  }
639
639
 
640
640
  // Start the watch loop
641
- return await watchUntilMergable(params);
641
+ return await watchUntilMergeable(params);
642
642
  };
643
643
 
644
644
  export default {
645
- watchUntilMergable,
645
+ watchUntilMergeable,
646
646
  attemptAutoMerge,
647
- startAutoRestartUntilMergable,
647
+ startAutoRestartUntilMergeable,
648
648
  checkForNonBotComments,
649
649
  };
@@ -173,10 +173,10 @@ export const SOLVE_OPTION_DEFINITIONS = {
173
173
  },
174
174
  'auto-merge': {
175
175
  type: 'boolean',
176
- description: 'Automatically merge the pull request when the working session is finished and all CI/CD statuses pass and PR is mergeable. Implies --auto-restart-until-mergable.',
176
+ description: 'Automatically merge the pull request when the working session is finished and all CI/CD statuses pass and PR is mergeable. Implies --auto-restart-until-mergeable.',
177
177
  default: false,
178
178
  },
179
- 'auto-restart-until-mergable': {
179
+ 'auto-restart-until-mergeable': {
180
180
  type: 'boolean',
181
181
  description: 'Auto-restart until PR becomes mergeable (no iteration limit). Restarts on new comments from non-bot users, CI failures, merge conflicts, or other issues. Does NOT auto-merge.',
182
182
  default: false,
package/src/solve.mjs CHANGED
@@ -74,7 +74,7 @@ const { createUncaughtExceptionHandler, createUnhandledRejectionHandler, handleM
74
74
  const watchLib = await import('./solve.watch.lib.mjs');
75
75
  const { startWatchMode } = watchLib;
76
76
  const autoMergeLib = await import('./solve.auto-merge.lib.mjs');
77
- const { startAutoRestartUntilMergable } = autoMergeLib;
77
+ const { startAutoRestartUntilMergeable } = autoMergeLib;
78
78
  const exitHandler = await import('./exit-handler.lib.mjs');
79
79
  const { initializeExitHandler, installGlobalExitHandlers, safeExit } = exitHandler;
80
80
  const getResourceSnapshot = memoryCheck.getResourceSnapshot;
@@ -1080,7 +1080,10 @@ try {
1080
1080
  // See: https://github.com/link-assistant/hive-mind/issues/1152
1081
1081
  const continueModeName = limitContinueMode === 'restart' ? 'auto-restart' : 'auto-resume';
1082
1082
  const continueDescription = limitContinueMode === 'restart' ? 'The session will automatically restart (fresh start) when the limit resets.' : 'The session will automatically resume (with context preserved) when the limit resets.';
1083
- const waitingComment = `⏳ **Usage Limit Reached - Waiting to ${limitContinueMode === 'restart' ? 'Restart' : 'Continue'}**\n\nThe AI tool has reached its usage limit. ${continueModeName} is enabled.\n\n**Reset time:** ${global.limitResetTime}\n**Wait time:** ${formatWaitTime(waitMs)} (days:hours:minutes:seconds)\n\n${continueDescription}\n\nSession ID: \`${sessionId}\``;
1083
+ // Format reset time with relative time and UTC for better user understanding
1084
+ // See: https://github.com/link-assistant/hive-mind/issues/1236
1085
+ const waitingResetTimeFormatted = formatResetTimeWithRelative(global.limitResetTime, global.limitTimezone || null) || global.limitResetTime;
1086
+ const waitingComment = `⏳ **Usage Limit Reached - Waiting to ${limitContinueMode === 'restart' ? 'Restart' : 'Continue'}**\n\nThe AI tool has reached its usage limit. ${continueModeName} is enabled.\n\n**Reset time:** ${waitingResetTimeFormatted}\n**Wait time:** ${formatWaitTime(waitMs)} (days:hours:minutes:seconds)\n\n${continueDescription}\n\nSession ID: \`${sessionId}\``;
1084
1087
 
1085
1088
  const commentResult = await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${waitingComment}`;
1086
1089
  if (commentResult.code === 0) {
@@ -1403,11 +1406,11 @@ try {
1403
1406
  }
1404
1407
  }
1405
1408
 
1406
- // Start auto-restart-until-mergable mode if enabled
1409
+ // Start auto-restart-until-mergeable mode if enabled
1407
1410
  // This runs after the normal watch mode completes (if any)
1408
- // --auto-merge implies --auto-restart-until-mergable
1409
- if (argv.autoMerge || argv.autoRestartUntilMergable) {
1410
- const autoMergeResult = await startAutoRestartUntilMergable({
1411
+ // --auto-merge implies --auto-restart-until-mergeable
1412
+ if (argv.autoMerge || argv.autoRestartUntilMergeable) {
1413
+ const autoMergeResult = await startAutoRestartUntilMergeable({
1411
1414
  issueUrl,
1412
1415
  owner,
1413
1416
  repo,
@@ -1425,7 +1428,7 @@ try {
1425
1428
  anthropicTotalCostUSD = autoMergeResult.latestAnthropicCost;
1426
1429
  if (argv.verbose) {
1427
1430
  await log('');
1428
- await log('📊 Updated session data from auto-restart-until-mergable mode:', { verbose: true });
1431
+ await log('📊 Updated session data from auto-restart-until-mergeable mode:', { verbose: true });
1429
1432
  await log(` Session ID: ${sessionId}`, { verbose: true });
1430
1433
  if (anthropicTotalCostUSD !== null && anthropicTotalCostUSD !== undefined) {
1431
1434
  await log(` Anthropic cost: $${anthropicTotalCostUSD.toFixed(6)}`, { verbose: true });
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Shared utilities for watch mode and auto-restart-until-mergable mode
4
+ * Shared utilities for watch mode and auto-restart-until-mergeable mode
5
5
  *
6
6
  * This module contains common functions used by both:
7
7
  * - solve.watch.lib.mjs (--watch mode and temporary auto-restart)
8
- * - solve.auto-merge.lib.mjs (--auto-merge and --auto-restart-until-mergable)
8
+ * - solve.auto-merge.lib.mjs (--auto-merge and --auto-restart-until-mergeable)
9
9
  *
10
10
  * Functions extracted to reduce duplication and ensure consistent behavior.
11
11
  *
@@ -167,7 +167,7 @@ export const getUncommittedChangesDetails = async tempDir => {
167
167
 
168
168
  /**
169
169
  * Execute the AI tool (Claude, OpenCode, Codex, Agent) for a restart iteration
170
- * This is the shared tool execution logic used by both watch mode and auto-restart-until-mergable mode
170
+ * This is the shared tool execution logic used by both watch mode and auto-restart-until-mergeable mode
171
171
  * @param {Object} params - Execution parameters
172
172
  * @returns {Promise<Object>} - Tool execution result
173
173
  */
@@ -688,12 +688,12 @@ Fixes ${issueRef}
688
688
  await log('\n✨ Please review the pull request for the proposed solution draft.');
689
689
  // Don't exit if watch mode is enabled OR if auto-restart is needed for uncommitted changes
690
690
  // Also don't exit if auto-restart-on-non-updated-pull-request-description detected placeholders
691
- // Issue #1219: Also don't exit if auto-merge or auto-restart-until-mergable is enabled
691
+ // Issue #1219: Also don't exit if auto-merge or auto-restart-until-mergeable is enabled
692
692
  const shouldAutoRestartForPlaceholder = argv.autoRestartOnNonUpdatedPullRequestDescription && (prTitleHasPlaceholder || prBodyHasPlaceholder);
693
693
  if (shouldAutoRestartForPlaceholder) {
694
694
  await log('\n🔄 Placeholder detected in PR title/description - auto-restart will be triggered');
695
695
  }
696
- const shouldWaitForAutoMerge = argv.autoMerge || argv.autoRestartUntilMergable;
696
+ const shouldWaitForAutoMerge = argv.autoMerge || argv.autoRestartUntilMergeable;
697
697
  if (shouldWaitForAutoMerge) {
698
698
  await log('\n🔄 Auto-merge mode enabled - will attempt to merge after verification');
699
699
  }
@@ -764,8 +764,8 @@ Fixes ${issueRef}
764
764
  }
765
765
  await log('\n✨ A clarifying comment has been added to the issue.');
766
766
  // Don't exit if watch mode is enabled OR if auto-restart is needed for uncommitted changes
767
- // Issue #1219: Also don't exit if auto-merge or auto-restart-until-mergable is enabled
768
- const shouldWaitForAutoMergeComment = argv.autoMerge || argv.autoRestartUntilMergable;
767
+ // Issue #1219: Also don't exit if auto-merge or auto-restart-until-mergeable is enabled
768
+ const shouldWaitForAutoMergeComment = argv.autoMerge || argv.autoRestartUntilMergeable;
769
769
  if (!argv.watch && !shouldRestart && !shouldWaitForAutoMergeComment) {
770
770
  await safeExit(0, 'Process completed successfully');
771
771
  }
@@ -785,8 +785,8 @@ Fixes ${issueRef}
785
785
  const reviewLogPath = path.resolve(getLogFile());
786
786
  await log(` ${reviewLogPath}`);
787
787
  // Don't exit if watch mode is enabled - it needs to continue monitoring
788
- // Issue #1219: Also don't exit if auto-merge or auto-restart-until-mergable is enabled
789
- const shouldWaitForAutoMergeNoAction = argv.autoMerge || argv.autoRestartUntilMergable;
788
+ // Issue #1219: Also don't exit if auto-merge or auto-restart-until-mergeable is enabled
789
+ const shouldWaitForAutoMergeNoAction = argv.autoMerge || argv.autoRestartUntilMergeable;
790
790
  if (!argv.watch && !shouldWaitForAutoMergeNoAction) {
791
791
  await safeExit(0, 'Process completed successfully');
792
792
  }
@@ -804,8 +804,8 @@ Fixes ${issueRef}
804
804
  const checkLogPath = path.resolve(getLogFile());
805
805
  await log(` ${checkLogPath}`);
806
806
  // Don't exit if watch mode is enabled - it needs to continue monitoring
807
- // Issue #1219: Also don't exit if auto-merge or auto-restart-until-mergable is enabled
808
- const shouldWaitForAutoMergeError = argv.autoMerge || argv.autoRestartUntilMergable;
807
+ // Issue #1219: Also don't exit if auto-merge or auto-restart-until-mergeable is enabled
808
+ const shouldWaitForAutoMergeError = argv.autoMerge || argv.autoRestartUntilMergeable;
809
809
  if (!argv.watch && !shouldWaitForAutoMergeError) {
810
810
  await safeExit(0, 'Process completed successfully');
811
811
  }