@link-assistant/hive-mind 1.25.1 → 1.25.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 +24 -0
- package/README.md +56 -1
- package/package.json +1 -1
- package/src/limits.lib.mjs +18 -5
- package/src/solve.auto-merge.lib.mjs +34 -10
- package/src/telegram-bot.mjs +5 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.25.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 0ed3ccb: fix: prevent --auto-restart-until-mergeable infinite loop when no CI/CD is configured (Issue #1345)
|
|
8
|
+
|
|
9
|
+
Previously, when a repository had no GitHub Actions workflows configured, `--auto-restart-until-mergeable` would loop indefinitely because `getDetailedCIStatus()` returned `{ status: 'no_checks' }` and the code always treated this as a transient race condition (checks haven't started yet).
|
|
10
|
+
|
|
11
|
+
Now the fix correctly handles the `no_checks` case by also checking `checkPRMergeable()`. If GitHub reports the PR as `MERGEABLE` (`mergeStateStatus: CLEAN`), the repository has no required CI checks and the process exits immediately with an appropriate message ("No CI/CD checks are configured for this repository — PR is mergeable"). If the PR is not yet mergeable, the existing wait behavior is preserved.
|
|
12
|
+
|
|
13
|
+
Full case study analysis including timeline reconstruction from logs in `docs/case-studies/issue-1345/`.
|
|
14
|
+
|
|
15
|
+
## 1.25.2
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 0453550: feat: show all limits even when Claude authentication is expired (Issue #1343)
|
|
20
|
+
|
|
21
|
+
Previously, when Claude authentication expired, the `/limits` command would fail completely and show no information at all.
|
|
22
|
+
|
|
23
|
+
Now the command gracefully handles Claude auth failures:
|
|
24
|
+
- The error message (e.g., "Claude authentication expired. Please use /solve or /hive commands to trigger re-authentication of Claude.") is shown inline in the Claude limits sections
|
|
25
|
+
- All other limits sections (CPU, RAM, Disk space, GitHub API) continue to display normally
|
|
26
|
+
|
|
3
27
|
## 1.25.1
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -164,12 +164,29 @@ gh-setup-git-identity
|
|
|
164
164
|
# Authenticate with Claude
|
|
165
165
|
claude
|
|
166
166
|
|
|
167
|
+
# Optionally set configuration like this:
|
|
168
|
+
# Use /config command and set:
|
|
169
|
+
# Reduce motion true # Will save your ssh trafic, and make Claude Code more responsive (less latency)
|
|
170
|
+
# Thinking mode false # Anthropic models perform better and cheaper without thinking
|
|
171
|
+
# Model haiku # chepear for connection testing manually
|
|
172
|
+
# Claude in Chrome enabled by default false # No need for Chrome support on server
|
|
173
|
+
|
|
174
|
+
# Optionally test Claude connection
|
|
175
|
+
claude -p hi --model haiku
|
|
176
|
+
|
|
177
|
+
# You might need to update hive-mind and agent to latest versions:
|
|
178
|
+
bun install -g @link-assistant/hive-mind
|
|
179
|
+
bun install -g @link-assistant/agent
|
|
180
|
+
|
|
167
181
|
# Now you can use hive and solve commands
|
|
168
182
|
solve https://github.com/owner/repo/issues/123
|
|
169
183
|
|
|
170
184
|
# Or you can run telegram bot:
|
|
171
185
|
|
|
172
|
-
#
|
|
186
|
+
# Exit from additional bash session
|
|
187
|
+
exit
|
|
188
|
+
|
|
189
|
+
# Attach to main bash process
|
|
173
190
|
docker attach hive-mind
|
|
174
191
|
|
|
175
192
|
# Run bot here
|
|
@@ -187,6 +204,44 @@ docker attach hive-mind
|
|
|
187
204
|
|
|
188
205
|
See [docs/DOCKER.md](./docs/DOCKER.md) for advanced Docker usage.
|
|
189
206
|
|
|
207
|
+
#### Stoping and removing docker container
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
# Attach to main docker process to stop the container
|
|
211
|
+
docker attach hive-mind
|
|
212
|
+
|
|
213
|
+
^C # stop the telegram bot
|
|
214
|
+
|
|
215
|
+
exit # exit/stop the container
|
|
216
|
+
|
|
217
|
+
docker ps -a # show list of docker containers
|
|
218
|
+
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
|
219
|
+
# fd0fd4470ec3 konard/hive-mind:latest "/bin/bash" 5 days ago Exited (130) 16 seconds ago hive-mind
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
df -h # check disk space
|
|
223
|
+
# Filesystem Size Used Avail Use% Mounted on
|
|
224
|
+
# tmpfs 1.2G 1.1M 1.2G 1% /run
|
|
225
|
+
# /dev/sda1 96G 87G 9.8G 90% /
|
|
226
|
+
# tmpfs 5.9G 0 5.9G 0% /dev/shm
|
|
227
|
+
# tmpfs 5.0M 0 5.0M 0% /run/lock
|
|
228
|
+
# /dev/sda16 881M 117M 703M 15% /boot
|
|
229
|
+
# /dev/sda15 105M 6.2M 99M 6% /boot/efi
|
|
230
|
+
# tmpfs 1.2G 12K 1.2G 1% /run/user/0
|
|
231
|
+
|
|
232
|
+
docker rm hive-mind # remove docker container frees space used by the container, does not delete image
|
|
233
|
+
|
|
234
|
+
df -h # check disk space (to confirm space is freed)
|
|
235
|
+
# Filesystem Size Used Avail Use% Mounted on
|
|
236
|
+
# tmpfs 1.2G 1.1M 1.2G 1% /run
|
|
237
|
+
# /dev/sda1 96G 26G 71G 27% /
|
|
238
|
+
# tmpfs 5.9G 0 5.9G 0% /dev/shm
|
|
239
|
+
# tmpfs 5.0M 0 5.0M 0% /run/lock
|
|
240
|
+
# /dev/sda16 881M 117M 703M 15% /boot
|
|
241
|
+
# /dev/sda15 105M 6.2M 99M 6% /boot/efi
|
|
242
|
+
# tmpfs 1.2G 12K 1.2G 1% /run/user/0
|
|
243
|
+
```
|
|
244
|
+
|
|
190
245
|
### Helm Installation (Kubernetes)
|
|
191
246
|
|
|
192
247
|
Deploy Hive Mind on Kubernetes using Helm:
|
package/package.json
CHANGED
package/src/limits.lib.mjs
CHANGED
|
@@ -694,15 +694,16 @@ export function calculateTimePassedPercentage(resetsAt, periodHours) {
|
|
|
694
694
|
* Format Claude usage data into a Telegram-friendly message
|
|
695
695
|
* Shows threshold markers in progress bars to indicate where queue behavior changes.
|
|
696
696
|
*
|
|
697
|
-
* @param {Object} usage - The usage object from getClaudeUsageLimits
|
|
697
|
+
* @param {Object|null} usage - The usage object from getClaudeUsageLimits, or null if unavailable
|
|
698
698
|
* @param {Object} diskSpace - Optional disk space info from getDiskSpaceInfo
|
|
699
699
|
* @param {Object} githubRateLimit - Optional GitHub rate limit info from getGitHubRateLimits
|
|
700
700
|
* @param {Object} cpuLoad - Optional CPU load info from getCpuLoadInfo
|
|
701
701
|
* @param {Object} memory - Optional memory info from getMemoryInfo
|
|
702
|
+
* @param {string|null} claudeError - Optional error message to show in Claude sections (e.g., auth expired)
|
|
702
703
|
* @returns {string} Formatted message
|
|
703
704
|
* @see https://github.com/link-assistant/hive-mind/issues/1242
|
|
704
705
|
*/
|
|
705
|
-
export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = null, cpuLoad = null, memory = null) {
|
|
706
|
+
export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = null, cpuLoad = null, memory = null, claudeError = null) {
|
|
706
707
|
// Use code block for monospace font to align progress bars properly
|
|
707
708
|
let message = '```\n';
|
|
708
709
|
|
|
@@ -761,10 +762,18 @@ export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = nu
|
|
|
761
762
|
message += '\n';
|
|
762
763
|
}
|
|
763
764
|
|
|
765
|
+
// Claude limits section
|
|
766
|
+
// When there's an error (e.g., auth expired), show it once here instead of repeating in each subsection
|
|
767
|
+
if (claudeError) {
|
|
768
|
+
message += `Claude limits\n${claudeError}\n\n`;
|
|
769
|
+
}
|
|
770
|
+
|
|
764
771
|
// Claude 5 hour session (five_hour)
|
|
765
772
|
// Threshold: One-at-a-time mode when usage >= 65%
|
|
766
773
|
message += 'Claude 5 hour session\n';
|
|
767
|
-
if (
|
|
774
|
+
if (claudeError) {
|
|
775
|
+
// Error already shown above; skip subsection content
|
|
776
|
+
} else if (usage && usage.currentSession.percentage !== null) {
|
|
768
777
|
// Add time passed progress bar first (no threshold marker for time)
|
|
769
778
|
const timePassed = calculateTimePassedPercentage(usage.currentSession.resetsAt, 5);
|
|
770
779
|
if (timePassed !== null) {
|
|
@@ -796,7 +805,9 @@ export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = nu
|
|
|
796
805
|
// Current week (all models / seven_day)
|
|
797
806
|
// Threshold: One-at-a-time mode when usage >= 97%
|
|
798
807
|
message += 'Current week (all models)\n';
|
|
799
|
-
if (
|
|
808
|
+
if (claudeError) {
|
|
809
|
+
// Error already shown above; skip subsection content
|
|
810
|
+
} else if (usage && usage.allModels.percentage !== null) {
|
|
800
811
|
// Add time passed progress bar first (no threshold marker for time)
|
|
801
812
|
const timePassed = calculateTimePassedPercentage(usage.allModels.resetsAt, 168);
|
|
802
813
|
if (timePassed !== null) {
|
|
@@ -828,7 +839,9 @@ export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = nu
|
|
|
828
839
|
// Current week (Sonnet only / seven_day_sonnet)
|
|
829
840
|
// Threshold: One-at-a-time mode when usage >= 97% (same as all models)
|
|
830
841
|
message += 'Current week (Sonnet only)\n';
|
|
831
|
-
if (
|
|
842
|
+
if (claudeError) {
|
|
843
|
+
// Error already shown above; skip subsection content
|
|
844
|
+
} else if (usage && usage.sonnetOnly.percentage !== null) {
|
|
832
845
|
// Add time passed progress bar first (no threshold marker for time)
|
|
833
846
|
const timePassed = calculateTimePassedPercentage(usage.sonnetOnly.resetsAt, 168);
|
|
834
847
|
if (timePassed !== null) {
|
|
@@ -187,12 +187,32 @@ const getMergeBlockers = async (owner, repo, prNumber, verbose = false) => {
|
|
|
187
187
|
const ciStatus = await getDetailedCIStatus(owner, repo, prNumber, verbose);
|
|
188
188
|
|
|
189
189
|
if (ciStatus.status === 'no_checks') {
|
|
190
|
-
// No CI checks exist yet -
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
190
|
+
// No CI checks exist yet - this could be:
|
|
191
|
+
// 1. A race condition after push (checks haven't started yet) - wait
|
|
192
|
+
// 2. A repository with no CI/CD configured at all - should be mergeable immediately
|
|
193
|
+
//
|
|
194
|
+
// Issue #1345: Distinguish by checking the PR's mergeability status.
|
|
195
|
+
// If GitHub says the PR is MERGEABLE (mergeStateStatus === 'CLEAN'),
|
|
196
|
+
// then no CI is required and we should not block indefinitely.
|
|
197
|
+
// Otherwise (e.g. mergeStateStatus === 'BLOCKED'), treat as pending race condition.
|
|
198
|
+
const earlyMergeStatus = await checkPRMergeable(owner, repo, prNumber, verbose);
|
|
199
|
+
if (earlyMergeStatus.mergeable) {
|
|
200
|
+
// PR is already mergeable with no CI checks - the repo has no CI/CD configured.
|
|
201
|
+
// Do NOT add a ci_pending blocker. The mergeability check below will also
|
|
202
|
+
// confirm this is mergeable, so blockers will be empty → PR IS MERGEABLE path.
|
|
203
|
+
if (verbose) {
|
|
204
|
+
console.log(`[VERBOSE] /merge: PR #${prNumber} has no CI checks and is already MERGEABLE - no CI/CD configured`);
|
|
205
|
+
}
|
|
206
|
+
// Return early with no CI blocker, mergeability already confirmed
|
|
207
|
+
return { blockers, ciStatus, noCiConfigured: true };
|
|
208
|
+
} else {
|
|
209
|
+
// PR is not yet mergeable despite no checks - treat as pending race condition
|
|
210
|
+
blockers.push({
|
|
211
|
+
type: 'ci_pending',
|
|
212
|
+
message: 'CI/CD checks have not started yet (waiting for checks to appear)',
|
|
213
|
+
details: [],
|
|
214
|
+
});
|
|
215
|
+
}
|
|
196
216
|
} else if (ciStatus.status === 'pending') {
|
|
197
217
|
// CI is still running or queued - wait for completion
|
|
198
218
|
const pendingNames = [...ciStatus.pendingChecks, ...ciStatus.queuedChecks].map(c => c.name);
|
|
@@ -272,7 +292,7 @@ const getMergeBlockers = async (owner, repo, prNumber, verbose = false) => {
|
|
|
272
292
|
});
|
|
273
293
|
}
|
|
274
294
|
|
|
275
|
-
return blockers;
|
|
295
|
+
return { blockers, ciStatus, noCiConfigured: false };
|
|
276
296
|
};
|
|
277
297
|
|
|
278
298
|
/**
|
|
@@ -341,7 +361,7 @@ export const watchUntilMergeable = async params => {
|
|
|
341
361
|
|
|
342
362
|
try {
|
|
343
363
|
// Get merge blockers
|
|
344
|
-
const blockers = await getMergeBlockers(owner, repo, prNumber, argv.verbose);
|
|
364
|
+
const { blockers, noCiConfigured } = await getMergeBlockers(owner, repo, prNumber, argv.verbose);
|
|
345
365
|
|
|
346
366
|
// Check for new comments from non-bot users
|
|
347
367
|
const { hasNewComments, comments } = await checkForNonBotComments(owner, repo, prNumber, issueNumber, lastCheckTime, argv.verbose);
|
|
@@ -364,7 +384,9 @@ export const watchUntilMergeable = async params => {
|
|
|
364
384
|
|
|
365
385
|
// Post success comment
|
|
366
386
|
try {
|
|
367
|
-
|
|
387
|
+
// Issue #1345: Differentiate message when no CI is configured
|
|
388
|
+
const ciLine = noCiConfigured ? '- No CI/CD checks are configured for this repository' : '- All CI checks have passed';
|
|
389
|
+
const commentBody = `## 🎉 Auto-merged\n\nThis pull request has been automatically merged by hive-mind.\n${ciLine}\n\n---\n*Auto-merged by hive-mind with --auto-merge flag*`;
|
|
368
390
|
await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
|
|
369
391
|
} catch {
|
|
370
392
|
// Don't fail if comment posting fails
|
|
@@ -386,7 +408,9 @@ export const watchUntilMergeable = async params => {
|
|
|
386
408
|
const readyToMergeSignature = '## ✅ Ready to merge';
|
|
387
409
|
const hasExistingComment = await checkForExistingComment(owner, repo, prNumber, readyToMergeSignature, argv.verbose);
|
|
388
410
|
if (!hasExistingComment) {
|
|
389
|
-
|
|
411
|
+
// Issue #1345: Differentiate message when no CI is configured
|
|
412
|
+
const ciLine = noCiConfigured ? '- No CI/CD checks are configured for this repository' : '- All CI checks have passed';
|
|
413
|
+
const commentBody = `## ✅ Ready to merge\n\nThis pull request is now ready to be merged:\n${ciLine}\n- No merge conflicts\n- No pending changes\n\n---\n*Monitored by hive-mind with --auto-restart-until-mergeable flag*`;
|
|
390
414
|
await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
|
|
391
415
|
} else {
|
|
392
416
|
await log(formatAligned('', 'Skipping duplicate "Ready to merge" comment', '', 2));
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -780,14 +780,12 @@ bot.command('limits', async ctx => {
|
|
|
780
780
|
// Get all limits using shared cache (3min for API, 2min for system)
|
|
781
781
|
const limits = await getAllCachedLimits(VERBOSE);
|
|
782
782
|
|
|
783
|
-
if (!limits.claude.success) {
|
|
784
|
-
const escapedError = escapeMarkdownV2(limits.claude.error, { preserveCodeBlocks: true });
|
|
785
|
-
await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, `❌ ${escapedError}`, { parse_mode: 'MarkdownV2' });
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
783
|
// Format the message with usage limits and queue status
|
|
790
|
-
|
|
784
|
+
// If Claude auth failed, pass the error to formatUsageMessage to show it in the Claude sections
|
|
785
|
+
// while still displaying all other limits sections (disk, GitHub, CPU, memory)
|
|
786
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1343
|
|
787
|
+
const claudeError = limits.claude.success ? null : limits.claude.error;
|
|
788
|
+
let message = '📊 *Usage Limits*\n\n' + formatUsageMessage(limits.claude.success ? limits.claude.usage : null, limits.disk.success ? limits.disk.diskSpace : null, limits.github.success ? limits.github.githubRateLimit : null, limits.cpu.success ? limits.cpu.cpuLoad : null, limits.memory.success ? limits.memory.memory : null, claudeError);
|
|
791
789
|
const solveQueue = getSolveQueue({ verbose: VERBOSE });
|
|
792
790
|
// Insert per-queue status into the code block
|
|
793
791
|
// Shows each queue (claude, agent) with pending/processing counts
|