@link-assistant/hive-mind 1.50.0 → 1.50.2
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 +21 -0
- package/package.json +1 -1
- package/src/agent.lib.mjs +2 -2
- package/src/claude.lib.mjs +2 -2
- package/src/codex.lib.mjs +2 -2
- package/src/lib.mjs +6 -1
- package/src/opencode.lib.mjs +2 -2
- package/src/solve.auto-ensure.lib.mjs +8 -0
- package/src/solve.auto-merge.lib.mjs +68 -4
- package/src/solve.mjs +4 -4
- package/src/solve.repository.lib.mjs +3 -3
- package/src/solve.results.lib.mjs +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.50.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f09dead: fix: always post GitHub comment when usage limit is reached in auto-restart mode (#1570)
|
|
8
|
+
- Fix silent waiting behavior in watchUntilMergeable() when usage limit is reached
|
|
9
|
+
- Previously the system would silently wait 40+ minutes without any user notification
|
|
10
|
+
- Now posts a GitHub comment to the PR using attachLogToGitHub() with usage limit details
|
|
11
|
+
- Comment includes reset time, session ID, and indicates auto-restart will resume automatically
|
|
12
|
+
- Log output now also shows the calculated resume time in UTC
|
|
13
|
+
|
|
14
|
+
## 1.50.1
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 494989e: Add paths filter to CI/CD workflow trigger to skip unnecessary runs for non-code file changes (#1582)
|
|
19
|
+
- c4fadea: fix: prevent push failures in auto-restart and cleanup by syncing with remote (#1572)
|
|
20
|
+
- Add `git pull` before restart sessions and cleanup push to prevent stale local state
|
|
21
|
+
- Add `2>&1` to all `git push` commands so stderr is captured for proper error handling
|
|
22
|
+
- Fix multi-line log message formatting to include timestamps on each line
|
|
23
|
+
|
|
3
24
|
## 1.50.0
|
|
4
25
|
|
|
5
26
|
### Minor Changes
|
package/package.json
CHANGED
package/src/agent.lib.mjs
CHANGED
|
@@ -1082,12 +1082,12 @@ export const checkForUncommittedChanges = async (tempDir, owner, repo, branchNam
|
|
|
1082
1082
|
if (commitResult.code === 0) {
|
|
1083
1083
|
await log('✅ Changes committed successfully');
|
|
1084
1084
|
|
|
1085
|
-
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName}`;
|
|
1085
|
+
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName} 2>&1`;
|
|
1086
1086
|
|
|
1087
1087
|
if (pushResult.code === 0) {
|
|
1088
1088
|
await log('✅ Changes pushed successfully');
|
|
1089
1089
|
} else {
|
|
1090
|
-
await log(`⚠️ Warning: Could not push changes: ${pushResult.stderr?.toString().trim()}`, {
|
|
1090
|
+
await log(`⚠️ Warning: Could not push changes: ${pushResult.stderr?.toString().trim() || pushResult.stdout?.toString().trim()}`, {
|
|
1091
1091
|
level: 'warning',
|
|
1092
1092
|
});
|
|
1093
1093
|
}
|
package/src/claude.lib.mjs
CHANGED
|
@@ -1449,11 +1449,11 @@ export const checkForUncommittedChanges = async (tempDir, owner, repo, branchNam
|
|
|
1449
1449
|
if (commitResult.code === 0) {
|
|
1450
1450
|
await log('✅ Changes committed successfully');
|
|
1451
1451
|
await log('📤 Pushing changes to remote...');
|
|
1452
|
-
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName}`;
|
|
1452
|
+
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName} 2>&1`;
|
|
1453
1453
|
if (pushResult.code === 0) {
|
|
1454
1454
|
await log('✅ Changes pushed successfully');
|
|
1455
1455
|
} else {
|
|
1456
|
-
await log(`⚠️ Warning: Could not push changes: ${pushResult.stderr?.toString().trim()}`, {
|
|
1456
|
+
await log(`⚠️ Warning: Could not push changes: ${pushResult.stderr?.toString().trim() || pushResult.stdout?.toString().trim()}`, {
|
|
1457
1457
|
level: 'warning',
|
|
1458
1458
|
});
|
|
1459
1459
|
}
|
package/src/codex.lib.mjs
CHANGED
|
@@ -492,12 +492,12 @@ export const checkForUncommittedChanges = async (tempDir, owner, repo, branchNam
|
|
|
492
492
|
if (commitResult.code === 0) {
|
|
493
493
|
await log('✅ Changes committed successfully');
|
|
494
494
|
|
|
495
|
-
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName}`;
|
|
495
|
+
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName} 2>&1`;
|
|
496
496
|
|
|
497
497
|
if (pushResult.code === 0) {
|
|
498
498
|
await log('✅ Changes pushed successfully');
|
|
499
499
|
} else {
|
|
500
|
-
await log(`⚠️ Warning: Could not push changes: ${pushResult.stderr?.toString().trim()}`, {
|
|
500
|
+
await log(`⚠️ Warning: Could not push changes: ${pushResult.stderr?.toString().trim() || pushResult.stdout?.toString().trim()}`, {
|
|
501
501
|
level: 'warning',
|
|
502
502
|
});
|
|
503
503
|
}
|
package/src/lib.mjs
CHANGED
|
@@ -83,8 +83,13 @@ export const log = async (message, options = {}) => {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// Write to file if log file is set
|
|
86
|
+
// Issue #1572: Handle multi-line messages by timestamping each line,
|
|
87
|
+
// so continuation lines don't appear without timestamps in the log file
|
|
86
88
|
if (logFile) {
|
|
87
|
-
const
|
|
89
|
+
const timestamp = new Date().toISOString();
|
|
90
|
+
const prefix = `[${timestamp}] [${level.toUpperCase()}]`;
|
|
91
|
+
const lines = String(message).split('\n');
|
|
92
|
+
const logMessage = lines.map(line => `${prefix} ${line}`).join('\n');
|
|
88
93
|
await fs.appendFile(logFile, logMessage + '\n').catch(error => {
|
|
89
94
|
// Silent fail for file append errors to avoid infinite loop
|
|
90
95
|
// but report to Sentry in verbose mode
|
package/src/opencode.lib.mjs
CHANGED
|
@@ -546,12 +546,12 @@ export const checkForUncommittedChanges = async (tempDir, owner, repo, branchNam
|
|
|
546
546
|
if (commitResult.code === 0) {
|
|
547
547
|
await log('✅ Changes committed successfully');
|
|
548
548
|
|
|
549
|
-
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName}`;
|
|
549
|
+
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName} 2>&1`;
|
|
550
550
|
|
|
551
551
|
if (pushResult.code === 0) {
|
|
552
552
|
await log('✅ Changes pushed successfully');
|
|
553
553
|
} else {
|
|
554
|
-
await log(`⚠️ Warning: Could not push changes: ${pushResult.stderr?.toString().trim()}`, {
|
|
554
|
+
await log(`⚠️ Warning: Could not push changes: ${pushResult.stderr?.toString().trim() || pushResult.stdout?.toString().trim()}`, {
|
|
555
555
|
level: 'warning',
|
|
556
556
|
});
|
|
557
557
|
}
|
|
@@ -76,6 +76,14 @@ export const runAutoEnsureRequirements = async ({ issueUrl, owner, repo, issueNu
|
|
|
76
76
|
for (let ensureIteration = 1; ensureIteration <= finalizeCount; ensureIteration++) {
|
|
77
77
|
await log(`🔄 FINALIZE iteration ${ensureIteration}/${finalizeCount}: Restarting to verify requirements...`);
|
|
78
78
|
|
|
79
|
+
// Issue #1572: Sync local branch with remote before each finalize iteration
|
|
80
|
+
const pullResult = await $({ cwd: tempDir })`git pull origin ${branchName} 2>&1`;
|
|
81
|
+
if (pullResult.code === 0) {
|
|
82
|
+
await log(` Synced local branch ${branchName} from remote`, { verbose: true });
|
|
83
|
+
} else {
|
|
84
|
+
throw new Error(`git pull failed (code ${pullResult.code}): ${pullResult.stdout || pullResult.stderr || 'no output'}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
79
87
|
const ensureFeedbackLines = ['', '='.repeat(60), '🔍 FINALIZE REQUIREMENTS CHECK:', '='.repeat(60), '', 'We need to ensure all changes are correct, consistent, validated, tested, logged and fully meet all discussed requirements (check issue description and all comments in issue and in pull request). Ensure all CI/CD checks pass.', ''];
|
|
80
88
|
|
|
81
89
|
const ensureResult = await executeToolIteration({
|
|
@@ -976,6 +976,16 @@ Once the billing issue is resolved, you can re-run the CI checks or push a new c
|
|
|
976
976
|
const prStateResult = await $`gh api repos/${owner}/${repo}/pulls/${prNumber} --jq '.mergeStateStatus'`;
|
|
977
977
|
const mergeStateStatus = prStateResult.code === 0 ? prStateResult.stdout.toString().trim() : null;
|
|
978
978
|
|
|
979
|
+
// Issue #1572: Sync local branch with remote before restarting to avoid push failures.
|
|
980
|
+
// Without this, the restarted session works on stale local state and can't push.
|
|
981
|
+
const effectiveBranch = prBranch || branchName;
|
|
982
|
+
const pullResult = await $({ cwd: tempDir })`git pull origin ${effectiveBranch} 2>&1`;
|
|
983
|
+
if (pullResult.code === 0) {
|
|
984
|
+
await log(formatAligned('🔄', 'Synced:', `Local branch ${effectiveBranch} updated from remote`));
|
|
985
|
+
} else {
|
|
986
|
+
throw new Error(`git pull failed (code ${pullResult.code}): ${pullResult.stdout || pullResult.stderr || 'no output'}`);
|
|
987
|
+
}
|
|
988
|
+
|
|
979
989
|
// Execute the AI tool using shared utility
|
|
980
990
|
await log(formatAligned('🔄', 'Restarting:', `Running ${argv.tool.toUpperCase()} to address issues...`));
|
|
981
991
|
|
|
@@ -994,9 +1004,10 @@ Once the billing issue is resolved, you can re-run the CI checks or push a new c
|
|
|
994
1004
|
|
|
995
1005
|
if (!toolResult.success) {
|
|
996
1006
|
// Issue #1356: Check for usage limit errors FIRST (most specific)
|
|
997
|
-
// When usage limit is reached,
|
|
1007
|
+
// When usage limit is reached, wait for limitResetTime + buffer + jitter,
|
|
998
1008
|
// then resume the session using --resume <sessionId> with a "Continue" prompt.
|
|
999
|
-
//
|
|
1009
|
+
// Issue #1570: Always post a GitHub comment to notify the user about the delay
|
|
1010
|
+
// and when exactly execution will be resumed, so the user doesn't think the process is stuck.
|
|
1000
1011
|
if (isUsageLimitReached(toolResult)) {
|
|
1001
1012
|
const resumeSessionId = toolResult.sessionId;
|
|
1002
1013
|
const resetTime = toolResult.limitResetTime;
|
|
@@ -1008,17 +1019,70 @@ Once the billing issue is resolved, you can re-run the CI checks or push a new c
|
|
|
1008
1019
|
const jitterSeconds = Math.round(jitterMs / 1000);
|
|
1009
1020
|
const waitMinutes = Math.round(waitMs / 60000);
|
|
1010
1021
|
|
|
1022
|
+
// Issue #1570: Calculate the actual resume time for user display
|
|
1023
|
+
const resumeDate = new Date(Date.now() + waitMs);
|
|
1024
|
+
const resumeTimeUTC = resumeDate
|
|
1025
|
+
.toISOString()
|
|
1026
|
+
.replace('T', ' ')
|
|
1027
|
+
.replace(/\.\d+Z$/, ' UTC');
|
|
1028
|
+
|
|
1011
1029
|
await log('');
|
|
1012
1030
|
await log(formatAligned('⏳', 'USAGE LIMIT REACHED', ''));
|
|
1013
1031
|
await log(formatAligned('', 'Reset time:', resetTime || 'Unknown', 2));
|
|
1014
1032
|
await log(formatAligned('', 'Waiting:', `${waitMinutes} min (reset + ${bufferMinutes} min buffer + ${jitterSeconds}s jitter)`, 2));
|
|
1015
|
-
await log(formatAligned('', '
|
|
1033
|
+
await log(formatAligned('', 'Resume at:', resumeTimeUTC, 2));
|
|
1034
|
+
await log(formatAligned('', 'Action:', 'Posting GitHub comment and waiting for limit reset', 2));
|
|
1016
1035
|
if (resumeSessionId) {
|
|
1017
1036
|
await log(formatAligned('', 'Session ID:', resumeSessionId, 2));
|
|
1018
1037
|
}
|
|
1019
1038
|
await log('');
|
|
1020
1039
|
|
|
1021
|
-
//
|
|
1040
|
+
// Issue #1570: Post a GitHub comment to notify the user about the usage limit delay.
|
|
1041
|
+
// This follows the same pattern as solve.watch.lib.mjs to ensure consistent user experience.
|
|
1042
|
+
const shouldAttachLogs = argv.attachLogs || argv['attach-logs'];
|
|
1043
|
+
if (prNumber && shouldAttachLogs) {
|
|
1044
|
+
try {
|
|
1045
|
+
const logFile = getLogFile();
|
|
1046
|
+
if (logFile) {
|
|
1047
|
+
await attachLogToGitHub({
|
|
1048
|
+
logFile,
|
|
1049
|
+
targetType: 'pr',
|
|
1050
|
+
targetNumber: prNumber,
|
|
1051
|
+
owner,
|
|
1052
|
+
repo,
|
|
1053
|
+
$,
|
|
1054
|
+
log,
|
|
1055
|
+
sanitizeLogContent,
|
|
1056
|
+
verbose: argv.verbose,
|
|
1057
|
+
sessionId: resumeSessionId || latestSessionId,
|
|
1058
|
+
tempDir,
|
|
1059
|
+
anthropicTotalCostUSD: toolResult.anthropicTotalCostUSD || latestAnthropicCost,
|
|
1060
|
+
isUsageLimit: true,
|
|
1061
|
+
limitResetTime: resetTime,
|
|
1062
|
+
toolName: `Anthropic ${(argv.tool || 'claude').charAt(0).toUpperCase() + (argv.tool || 'claude').slice(1)} Code`,
|
|
1063
|
+
isAutoResumeEnabled: true,
|
|
1064
|
+
autoResumeMode: 'restart',
|
|
1065
|
+
requestedModel: argv.model,
|
|
1066
|
+
tool: argv.tool || 'claude',
|
|
1067
|
+
publicPricingEstimate: toolResult.publicPricingEstimate,
|
|
1068
|
+
pricingInfo: toolResult.pricingInfo,
|
|
1069
|
+
resultModelUsage: toolResult.resultModelUsage || null,
|
|
1070
|
+
});
|
|
1071
|
+
await log(formatAligned('', '✅ Usage limit comment posted to PR', '', 2));
|
|
1072
|
+
}
|
|
1073
|
+
} catch (commentError) {
|
|
1074
|
+
reportError(commentError, {
|
|
1075
|
+
context: 'attach_usage_limit_comment_auto_restart',
|
|
1076
|
+
prNumber,
|
|
1077
|
+
owner,
|
|
1078
|
+
repo,
|
|
1079
|
+
operation: 'usage_limit_comment',
|
|
1080
|
+
});
|
|
1081
|
+
await log(formatAligned('', `⚠️ Usage limit comment upload error: ${cleanErrorMessage(commentError)}`, '', 2));
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// Wait until the limit resets
|
|
1022
1086
|
await new Promise(resolve => setTimeout(resolve, waitMs));
|
|
1023
1087
|
|
|
1024
1088
|
await log(formatAligned('✅', 'Usage limit wait complete', 'Resuming session...'));
|
package/src/solve.mjs
CHANGED
|
@@ -573,12 +573,12 @@ try {
|
|
|
573
573
|
const mergeResult = await $({ cwd: tempDir })`git merge ${defaultBranch} --no-edit`;
|
|
574
574
|
if (mergeResult.code === 0) {
|
|
575
575
|
await log(`${formatAligned('✅', 'Merge successful:', 'Pushing merged branch...')}`);
|
|
576
|
-
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName}`;
|
|
576
|
+
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName} 2>&1`;
|
|
577
577
|
if (pushResult.code === 0) {
|
|
578
578
|
await log(`${formatAligned('✅', 'Push successful:', 'Branch updated with latest changes')}`);
|
|
579
579
|
} else {
|
|
580
580
|
await log(`${formatAligned('⚠️', 'Push failed:', 'Merge completed but push failed')}`, { level: 'warning' });
|
|
581
|
-
await log(` Error: ${pushResult.stderr?.toString() || 'Unknown error'}`, { level: 'warning' });
|
|
581
|
+
await log(` Error: ${pushResult.stderr?.toString() || pushResult.stdout?.toString() || 'Unknown error'}`, { level: 'warning' });
|
|
582
582
|
}
|
|
583
583
|
} else {
|
|
584
584
|
// Merge failed - likely due to conflicts
|
|
@@ -1343,13 +1343,13 @@ try {
|
|
|
1343
1343
|
await log('');
|
|
1344
1344
|
|
|
1345
1345
|
try {
|
|
1346
|
-
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName}`;
|
|
1346
|
+
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName} 2>&1`;
|
|
1347
1347
|
if (pushResult.code === 0) {
|
|
1348
1348
|
await log('✅ Changes pushed successfully to remote branch');
|
|
1349
1349
|
await log(` Branch: ${branchName}`);
|
|
1350
1350
|
await log('');
|
|
1351
1351
|
} else {
|
|
1352
|
-
const errorMsg = pushResult.stderr?.toString() || 'Unknown error';
|
|
1352
|
+
const errorMsg = pushResult.stderr?.toString() || pushResult.stdout?.toString() || 'Unknown error';
|
|
1353
1353
|
await log('⚠️ Push failed:', { level: 'error' });
|
|
1354
1354
|
await log(` ${errorMsg.trim()}`, { level: 'error' });
|
|
1355
1355
|
await log(' Please push manually:', { level: 'error' });
|
|
@@ -1113,12 +1113,12 @@ export const setupUpstreamAndSync = async (tempDir, forkedRepo, upstreamRemote,
|
|
|
1113
1113
|
|
|
1114
1114
|
// Step 3: Push the updated default branch to fork to keep it in sync
|
|
1115
1115
|
await log(`${formatAligned('🔄', 'Pushing to fork:', `${upstreamDefaultBranch} branch`)}`);
|
|
1116
|
-
const pushResult = await $({ cwd: tempDir })`git push origin ${upstreamDefaultBranch}`;
|
|
1116
|
+
const pushResult = await $({ cwd: tempDir })`git push origin ${upstreamDefaultBranch} 2>&1`;
|
|
1117
1117
|
if (pushResult.code === 0) {
|
|
1118
1118
|
await log(`${formatAligned('✅', 'Fork updated:', 'Default branch pushed to fork')}`);
|
|
1119
1119
|
} else {
|
|
1120
1120
|
// Check if it's a non-fast-forward error (fork has diverged from upstream)
|
|
1121
|
-
const errorMsg = pushResult.stderr ? pushResult.stderr.toString().trim() : '';
|
|
1121
|
+
const errorMsg = (pushResult.stderr ? pushResult.stderr.toString().trim() : '') || (pushResult.stdout ? pushResult.stdout.toString().trim() : '');
|
|
1122
1122
|
const isNonFastForward = errorMsg.includes('non-fast-forward') || errorMsg.includes('rejected') || errorMsg.includes('tip of your current branch is behind');
|
|
1123
1123
|
|
|
1124
1124
|
if (isNonFastForward) {
|
|
@@ -1147,7 +1147,7 @@ export const setupUpstreamAndSync = async (tempDir, forkedRepo, upstreamRemote,
|
|
|
1147
1147
|
await log(`${formatAligned('🔄', 'Force pushing:', 'Syncing fork with upstream (--force-with-lease)')}`);
|
|
1148
1148
|
const forcePushResult = await $({
|
|
1149
1149
|
cwd: tempDir,
|
|
1150
|
-
})`git push --force-with-lease origin ${upstreamDefaultBranch}`;
|
|
1150
|
+
})`git push --force-with-lease origin ${upstreamDefaultBranch} 2>&1`;
|
|
1151
1151
|
|
|
1152
1152
|
if (forcePushResult.code === 0) {
|
|
1153
1153
|
await log(`${formatAligned('✅', 'Fork synced:', 'Successfully force-pushed to align with upstream')}`);
|
|
@@ -269,6 +269,15 @@ export const cleanupClaudeFile = async (tempDir, branchName, claudeCommitHash =
|
|
|
269
269
|
await log(formatAligned('🔄', 'Cleanup:', `Reverting ${fileName} commit`));
|
|
270
270
|
await log(` Using saved commit hash: ${claudeCommitHash.substring(0, 7)}...`, { verbose: true });
|
|
271
271
|
|
|
272
|
+
// Issue #1572: Sync local branch with remote before cleanup to prevent push failures.
|
|
273
|
+
// After auto-restart sessions, the local branch may be behind the remote.
|
|
274
|
+
const pullResult = await $({ cwd: tempDir })`git pull origin ${branchName} 2>&1`;
|
|
275
|
+
if (pullResult.code === 0) {
|
|
276
|
+
await log(` Synced local branch before cleanup`, { verbose: true });
|
|
277
|
+
} else {
|
|
278
|
+
throw new Error(`git pull failed (code ${pullResult.code}): ${pullResult.stdout || pullResult.stderr || 'no output'}`);
|
|
279
|
+
}
|
|
280
|
+
|
|
272
281
|
const commitToRevert = claudeCommitHash;
|
|
273
282
|
|
|
274
283
|
// APPROACH 3: Check for modifications before reverting (proactive detection)
|