@link-assistant/hive-mind 1.24.2 → 1.24.3
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 +9 -0
- package/README.md +14 -2
- package/package.json +1 -1
- package/src/solve.auto-merge.lib.mjs +72 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.24.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 297e07c: Fix incorrect iteration counter and duplicate comments in auto-restart mode
|
|
8
|
+
- Fixed iteration counter to show actual AI restart count instead of check cycle number
|
|
9
|
+
- Added deduplication check to prevent duplicate "Ready to merge" status comments
|
|
10
|
+
- Added case study documentation for issue #1323
|
|
11
|
+
|
|
3
12
|
## 1.24.2
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -811,9 +811,21 @@ procinfo 62220
|
|
|
811
811
|
|
|
812
812
|
## Maintenance
|
|
813
813
|
|
|
814
|
-
###
|
|
814
|
+
### Enter latest screen
|
|
815
|
+
|
|
816
|
+
```bash
|
|
817
|
+
s=$(screen -ls | awk '/Detached/ {print $1; exit}'); echo "Entering $s"; screen -r "$s"; echo "Left $s";
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### Enter oldest screen
|
|
815
821
|
|
|
822
|
+
```bash
|
|
823
|
+
s=$(screen -ls | awk '/Detached/ {last=$1} END{print last}'); echo "Entering $s"; screen -r "$s"; echo "Left $s";
|
|
816
824
|
```
|
|
825
|
+
|
|
826
|
+
### Reboot server.
|
|
827
|
+
|
|
828
|
+
```bash
|
|
817
829
|
sudo reboot
|
|
818
830
|
```
|
|
819
831
|
|
|
@@ -821,7 +833,7 @@ That will remove all dangling unused proccesses and screens, which will in turn
|
|
|
821
833
|
|
|
822
834
|
### Cleanup disk space.
|
|
823
835
|
|
|
824
|
-
```
|
|
836
|
+
```bash
|
|
825
837
|
df -h
|
|
826
838
|
|
|
827
839
|
rm -rf /tmp
|
package/package.json
CHANGED
|
@@ -43,6 +43,37 @@ const { sanitizeLogContent, attachLogToGitHub } = githubLib;
|
|
|
43
43
|
const restartShared = await import('./solve.restart-shared.lib.mjs');
|
|
44
44
|
const { checkPRMerged, checkPRClosed, checkForUncommittedChanges, getUncommittedChangesDetails, executeToolIteration, buildAutoRestartInstructions, isApiError } = restartShared;
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Issue #1323: Check if a comment with specific content already exists on the PR
|
|
48
|
+
* This prevents duplicate status comments when multiple processes or restarts occur
|
|
49
|
+
* @param {string} owner - Repository owner
|
|
50
|
+
* @param {string} repo - Repository name
|
|
51
|
+
* @param {number} prNumber - Pull request number
|
|
52
|
+
* @param {string} commentSignature - Unique signature to search for in comment body (e.g., "✅ Ready to merge")
|
|
53
|
+
* @param {boolean} verbose - Enable verbose logging
|
|
54
|
+
* @returns {Promise<boolean>} - True if a matching comment already exists
|
|
55
|
+
*/
|
|
56
|
+
const checkForExistingComment = async (owner, repo, prNumber, commentSignature, verbose = false) => {
|
|
57
|
+
try {
|
|
58
|
+
// Fetch recent PR comments (last 20 to avoid fetching entire history)
|
|
59
|
+
const result = await $`gh api repos/${owner}/${repo}/issues/${prNumber}/comments --jq '.[].body' 2>/dev/null`;
|
|
60
|
+
if (result.code === 0 && result.stdout) {
|
|
61
|
+
const bodies = result.stdout.toString();
|
|
62
|
+
const hasMatch = bodies.includes(commentSignature);
|
|
63
|
+
if (verbose && hasMatch) {
|
|
64
|
+
console.log(`[VERBOSE] Found existing comment with signature: "${commentSignature}"`);
|
|
65
|
+
}
|
|
66
|
+
return hasMatch;
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
// If check fails, allow posting to avoid silent failures
|
|
70
|
+
if (verbose) {
|
|
71
|
+
console.log(`[VERBOSE] Failed to check for existing comment: ${error.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
};
|
|
76
|
+
|
|
46
77
|
/**
|
|
47
78
|
* Check for new comments from non-bot users since last commit
|
|
48
79
|
* @returns {Promise<{hasNewComments: boolean, comments: Array}>}
|
|
@@ -258,6 +289,11 @@ export const watchUntilMergeable = async params => {
|
|
|
258
289
|
let latestSessionId = null;
|
|
259
290
|
let latestAnthropicCost = null;
|
|
260
291
|
|
|
292
|
+
// Issue #1323: Track actual restart count separately from check cycle iteration
|
|
293
|
+
// `iteration` counts check cycles (how many times we check for blockers)
|
|
294
|
+
// `restartCount` counts actual AI tool executions (when we actually restart the AI)
|
|
295
|
+
let restartCount = 0;
|
|
296
|
+
|
|
261
297
|
// Track consecutive API errors for retry limit
|
|
262
298
|
const MAX_API_ERROR_RETRIES = 3;
|
|
263
299
|
let consecutiveApiErrors = 0;
|
|
@@ -344,10 +380,17 @@ export const watchUntilMergeable = async params => {
|
|
|
344
380
|
await log(formatAligned('', 'PR is ready to be merged manually', '', 2));
|
|
345
381
|
await log(formatAligned('', 'Exiting auto-restart-until-mergeable mode', '', 2));
|
|
346
382
|
|
|
347
|
-
// Post success comment
|
|
383
|
+
// Issue #1323: Post success comment only if one doesn't already exist
|
|
384
|
+
// This prevents duplicate comments when multiple processes reach this point
|
|
348
385
|
try {
|
|
349
|
-
const
|
|
350
|
-
|
|
386
|
+
const readyToMergeSignature = '## ✅ Ready to merge';
|
|
387
|
+
const hasExistingComment = await checkForExistingComment(owner, repo, prNumber, readyToMergeSignature, argv.verbose);
|
|
388
|
+
if (!hasExistingComment) {
|
|
389
|
+
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*`;
|
|
390
|
+
await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
|
|
391
|
+
} else {
|
|
392
|
+
await log(formatAligned('', 'Skipping duplicate "Ready to merge" comment', '', 2));
|
|
393
|
+
}
|
|
351
394
|
} catch {
|
|
352
395
|
// Don't fail if comment posting fails
|
|
353
396
|
}
|
|
@@ -518,10 +561,14 @@ Once the billing issue is resolved, you can re-run the CI checks or push a new c
|
|
|
518
561
|
}
|
|
519
562
|
|
|
520
563
|
if (shouldRestart) {
|
|
564
|
+
// Issue #1323: Increment restart count (actual AI executions, not check cycles)
|
|
565
|
+
restartCount++;
|
|
566
|
+
|
|
521
567
|
// Add standard instructions for auto-restart-until-mergeable mode using shared utility
|
|
522
568
|
feedbackLines.push(...buildAutoRestartInstructions());
|
|
523
569
|
|
|
524
570
|
await log(formatAligned('🔄', 'RESTART TRIGGERED:', restartReason));
|
|
571
|
+
await log(formatAligned('', 'Restart iteration:', `${restartCount}`, 2));
|
|
525
572
|
await log('');
|
|
526
573
|
|
|
527
574
|
// Post a comment to PR about the restart
|
|
@@ -601,7 +648,8 @@ Once the billing issue is resolved, you can re-run the CI checks or push a new c
|
|
|
601
648
|
try {
|
|
602
649
|
const logFile = getLogFile();
|
|
603
650
|
if (logFile) {
|
|
604
|
-
|
|
651
|
+
// Issue #1323: Use restartCount (actual AI executions) instead of iteration (check cycles)
|
|
652
|
+
const customTitle = `🔄 Auto-restart-until-mergeable Log (iteration ${restartCount})`;
|
|
605
653
|
await attachLogToGitHub({
|
|
606
654
|
logFile,
|
|
607
655
|
targetType: 'pr',
|
|
@@ -779,11 +827,17 @@ export const startAutoRestartUntilMergeable = async params => {
|
|
|
779
827
|
await log(formatAligned('', 'Action:', 'PR is ready for manual merge by a repository maintainer', 2));
|
|
780
828
|
await log('');
|
|
781
829
|
|
|
782
|
-
// Post a comment to the PR notifying the maintainer
|
|
830
|
+
// Issue #1323: Post a comment to the PR notifying the maintainer (with deduplication)
|
|
783
831
|
try {
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
|
|
832
|
+
const readyToMergeSignature = '## ✅ Ready to merge';
|
|
833
|
+
const hasExistingComment = await checkForExistingComment(owner, repo, prNumber, readyToMergeSignature, argv.verbose);
|
|
834
|
+
if (!hasExistingComment) {
|
|
835
|
+
const commentBody = `## ✅ Ready to merge\n\nThis pull request is ready to be merged. Auto-merge was requested (\`--auto-merge\`) but cannot be performed because this PR was created from a fork (no write access to the target repository).\n\nPlease merge manually.\n\n---\n*hive-mind with --auto-merge flag (fork mode)*`;
|
|
836
|
+
await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
|
|
837
|
+
await log(formatAligned('', '💬 Posted merge readiness notification to PR', '', 2));
|
|
838
|
+
} else {
|
|
839
|
+
await log(formatAligned('', 'Skipping duplicate "Ready to merge" comment', '', 2));
|
|
840
|
+
}
|
|
787
841
|
} catch {
|
|
788
842
|
// Don't fail if comment posting fails
|
|
789
843
|
}
|
|
@@ -802,11 +856,17 @@ export const startAutoRestartUntilMergeable = async params => {
|
|
|
802
856
|
await log(formatAligned('', 'Action:', 'PR is ready for manual merge by a repository maintainer', 2));
|
|
803
857
|
await log('');
|
|
804
858
|
|
|
805
|
-
// Post a comment to the PR notifying the maintainer
|
|
859
|
+
// Issue #1323: Post a comment to the PR notifying the maintainer (with deduplication)
|
|
806
860
|
try {
|
|
807
|
-
const
|
|
808
|
-
|
|
809
|
-
|
|
861
|
+
const readyToMergeSignature = '## ✅ Ready to merge';
|
|
862
|
+
const hasExistingComment = await checkForExistingComment(owner, repo, prNumber, readyToMergeSignature, argv.verbose);
|
|
863
|
+
if (!hasExistingComment) {
|
|
864
|
+
const commentBody = `## ✅ Ready to merge\n\nThis pull request is ready to be merged. Auto-merge was requested (\`--auto-merge\`) but cannot be performed because the authenticated user lacks write access to \`${owner}/${repo}\` (current permission: \`${permission || 'unknown'}\`).\n\nPlease merge manually.\n\n---\n*hive-mind with --auto-merge flag*`;
|
|
865
|
+
await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
|
|
866
|
+
await log(formatAligned('', '💬 Posted merge readiness notification to PR', '', 2));
|
|
867
|
+
} else {
|
|
868
|
+
await log(formatAligned('', 'Skipping duplicate "Ready to merge" comment', '', 2));
|
|
869
|
+
}
|
|
810
870
|
} catch {
|
|
811
871
|
// Don't fail if comment posting fails
|
|
812
872
|
}
|