@link-assistant/hive-mind 1.23.7 → 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 +15 -7
- package/package.json +1 -1
- package/src/config.lib.mjs +11 -2
- package/src/github.lib.mjs +15 -16
- package/src/option-suggestions.lib.mjs +1 -1
- package/src/solve.auto-continue.lib.mjs +14 -5
- package/src/solve.auto-merge.lib.mjs +24 -24
- package/src/solve.config.lib.mjs +2 -2
- package/src/solve.mjs +10 -7
- package/src/solve.restart-shared.lib.mjs +3 -3
- package/src/solve.results.lib.mjs +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
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
|
+
|
|
3
11
|
## 1.23.7
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
6
14
|
|
|
7
|
-
- d951635: Fix --auto-restart-until-
|
|
15
|
+
- d951635: Fix --auto-restart-until-mergeable false positive on empty CI checks
|
|
8
16
|
|
|
9
|
-
The `--auto-restart-until-
|
|
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.
|
|
10
18
|
|
|
11
19
|
Fix: Return `pending` status when no CI checks exist yet, instead of `success`.
|
|
12
20
|
|
|
@@ -137,7 +145,7 @@
|
|
|
137
145
|
### Patch Changes
|
|
138
146
|
|
|
139
147
|
- fdd8eaa: Fix auto-merge failure in fork mode with permission pre-check (Issue #1226)
|
|
140
|
-
- Add fork-mode guard in `
|
|
148
|
+
- Add fork-mode guard in `startAutoRestartUntilMergeable()` to detect when `--auto-merge` cannot work
|
|
141
149
|
- Add `checkMergePermissions()` function to verify write/push/admin/maintain access before merge attempts
|
|
142
150
|
- Add permission pre-check in `attemptAutoMerge()` to fail fast when user lacks write access
|
|
143
151
|
- Post "Ready to merge" comment to PR when auto-merge cannot be performed due to permissions
|
|
@@ -385,7 +393,7 @@
|
|
|
385
393
|
|
|
386
394
|
- 5723a93: fix: prevent early exit when --auto-merge flag is used
|
|
387
395
|
|
|
388
|
-
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.
|
|
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.
|
|
389
397
|
|
|
390
398
|
## 1.15.1
|
|
391
399
|
|
|
@@ -468,11 +476,11 @@
|
|
|
468
476
|
|
|
469
477
|
### Minor Changes
|
|
470
478
|
|
|
471
|
-
- 03adcb6: Add --auto-merge and --auto-restart-until-
|
|
479
|
+
- 03adcb6: Add --auto-merge and --auto-restart-until-mergeable options for autonomous PR management
|
|
472
480
|
|
|
473
481
|
New CLI options:
|
|
474
|
-
- `--auto-merge`: Automatically merge the pull request when CI passes and PR is mergeable. Implies --auto-restart-until-
|
|
475
|
-
- `--auto-restart-until-
|
|
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.
|
|
476
484
|
|
|
477
485
|
Features:
|
|
478
486
|
- Non-bot comment detection with configurable bot patterns
|
package/package.json
CHANGED
package/src/config.lib.mjs
CHANGED
|
@@ -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:
|
|
67
|
-
|
|
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
|
package/src/github.lib.mjs
CHANGED
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// GitHub-related utility functions
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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:
|
|
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
|
-
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
179
|
+
* This implements --auto-restart-until-mergeable functionality
|
|
180
180
|
*/
|
|
181
|
-
export const
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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: '
|
|
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-
|
|
571
|
+
* Start auto-restart-until-mergeable mode
|
|
572
572
|
*/
|
|
573
|
-
export const
|
|
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
|
|
578
|
+
const isAutoRestartUntilMergeable = argv.autoRestartUntilMergeable || false;
|
|
579
579
|
|
|
580
|
-
if (!isAutoMerge && !
|
|
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-
|
|
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-
|
|
635
|
+
// If --auto-merge implies --auto-restart-until-mergeable
|
|
636
636
|
if (isAutoMerge) {
|
|
637
|
-
argv.
|
|
637
|
+
argv.autoRestartUntilMergeable = true;
|
|
638
638
|
}
|
|
639
639
|
|
|
640
640
|
// Start the watch loop
|
|
641
|
-
return await
|
|
641
|
+
return await watchUntilMergeable(params);
|
|
642
642
|
};
|
|
643
643
|
|
|
644
644
|
export default {
|
|
645
|
-
|
|
645
|
+
watchUntilMergeable,
|
|
646
646
|
attemptAutoMerge,
|
|
647
|
-
|
|
647
|
+
startAutoRestartUntilMergeable,
|
|
648
648
|
checkForNonBotComments,
|
|
649
649
|
};
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -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-
|
|
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-
|
|
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 {
|
|
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
|
-
|
|
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-
|
|
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-
|
|
1409
|
-
if (argv.autoMerge || argv.
|
|
1410
|
-
const autoMergeResult = await
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
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-
|
|
768
|
-
const shouldWaitForAutoMergeComment = argv.autoMerge || argv.
|
|
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-
|
|
789
|
-
const shouldWaitForAutoMergeNoAction = argv.autoMerge || argv.
|
|
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-
|
|
808
|
-
const shouldWaitForAutoMergeError = argv.autoMerge || argv.
|
|
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
|
}
|