@link-assistant/hive-mind 1.26.0 → 1.26.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 +16 -0
- package/README.md +23 -120
- package/package.json +1 -1
- package/src/limits.lib.mjs +122 -115
- package/src/solve.auto-merge.lib.mjs +14 -6
- package/src/telegram-bot.mjs +6 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.26.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 72c933c: Skip empty Claude subsection headers when auth error occurs in /limits output
|
|
8
|
+
|
|
9
|
+
## 1.26.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 278415a: fix: post "Ready to merge" comment after auto-restart sequence with --auto-restart-until-mergeable (Issue #1371)
|
|
14
|
+
|
|
15
|
+
When `--auto-restart-until-mergeable` was used after a regular auto-restart sequence (triggered by uncommitted changes), the "Ready to merge" comment was silently suppressed because `checkForExistingComment` found a matching comment from a previous `solve` run.
|
|
16
|
+
|
|
17
|
+
The deduplication logic in `watchUntilMergeable` now uses an in-memory flag (`readyToMergeCommentPosted`) scoped to the current session, rather than searching all PR comment history. This correctly prevents duplicate comments within a single run while allowing new notifications when a fresh `solve` invocation starts.
|
|
18
|
+
|
|
3
19
|
## 1.26.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ For detailed features and comparisons, see [docs/FEATURES.md](./docs/FEATURES.md
|
|
|
46
46
|
|
|
47
47
|
It is UNSAFE to run this software on your developer machine.
|
|
48
48
|
|
|
49
|
-
It is recommended to use
|
|
49
|
+
It is recommended to use Docker for installation (both locally and on servers). See the [Docker installation](#using-docker) section below.
|
|
50
50
|
|
|
51
51
|
This software uses full autonomous mode of Claude Code, that means it is free to execute any commands it sees fit.
|
|
52
52
|
|
|
@@ -292,121 +292,26 @@ See [docs/HELM.md](./docs/HELM.md) for detailed Helm configuration options.
|
|
|
292
292
|
|
|
293
293
|
**Note:** The Helm chart is published to [ArtifactHub](https://artifacthub.io/packages/helm/link-assistant/hive-mind) for easy discovery.
|
|
294
294
|
|
|
295
|
-
### Installation on Ubuntu 24.04 server
|
|
295
|
+
### Installation on Ubuntu 24.04 server (Deprecated)
|
|
296
296
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
4. Login to `hive` user
|
|
308
|
-
|
|
309
|
-
```bash
|
|
310
|
-
su - hive
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
5. **IMPORTANT:** Authenticate with GitHub CLI AFTER installation is complete
|
|
314
|
-
|
|
315
|
-
```bash
|
|
316
|
-
gh-setup-git-identity
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
Note: Follow the prompts to authenticate with your GitHub account. This is required for the gh tool to work, and the system will perform all actions using this GitHub account. This step must be done AFTER the installation script completes to avoid build timeouts in Docker environments.
|
|
320
|
-
|
|
321
|
-
6. Claude Code CLI, OpenCode AI CLI, and @link-assistant/agent are preinstalled with the previous script. Now you need to make sure claude is authorized. Execute claude command, and follow all steps to authorize the local claude
|
|
322
|
-
|
|
323
|
-
```bash
|
|
324
|
-
claude
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
Note: Both opencode and agent come with free Grok Code Fast 1 model by default - so no authorization is required for these tools.
|
|
328
|
-
|
|
329
|
-
7. Launch the Hive Mind telegram bot:
|
|
330
|
-
|
|
331
|
-
**Using Links Notation (recommended):**
|
|
332
|
-
|
|
333
|
-
```
|
|
334
|
-
screen -R bot # Enter new screen for bot
|
|
335
|
-
|
|
336
|
-
hive-telegram-bot --configuration "
|
|
337
|
-
TELEGRAM_BOT_TOKEN: '849...355:AAG...rgk_YZk...aPU'
|
|
338
|
-
TELEGRAM_ALLOWED_CHATS:
|
|
339
|
-
-1002975819706
|
|
340
|
-
-1002861722681
|
|
341
|
-
TELEGRAM_HIVE_OVERRIDES:
|
|
342
|
-
--all-issues
|
|
343
|
-
--once
|
|
344
|
-
--skip-issues-with-prs
|
|
345
|
-
--attach-logs
|
|
346
|
-
--verbose
|
|
347
|
-
--no-tool-check
|
|
348
|
-
TELEGRAM_SOLVE_OVERRIDES:
|
|
349
|
-
--attach-logs
|
|
350
|
-
--verbose
|
|
351
|
-
--no-tool-check
|
|
352
|
-
TELEGRAM_BOT_VERBOSE: true
|
|
353
|
-
"
|
|
354
|
-
|
|
355
|
-
# Press CTRL + A + D for detach from screen
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
**Using individual command-line options:**
|
|
359
|
-
|
|
360
|
-
```
|
|
361
|
-
screen -R bot # Enter new screen for bot
|
|
362
|
-
|
|
363
|
-
hive-telegram-bot --token 849...355:AAG...rgk_YZk...aPU --allowed-chats "(
|
|
364
|
-
-1002975819706
|
|
365
|
-
-1002861722681
|
|
366
|
-
)" --hive-overrides "(
|
|
367
|
-
--all-issues
|
|
368
|
-
--once
|
|
369
|
-
--skip-issues-with-prs
|
|
370
|
-
--attach-logs
|
|
371
|
-
--verbose
|
|
372
|
-
--no-tool-check
|
|
373
|
-
)" --solve-overrides "(
|
|
374
|
-
--attach-logs
|
|
375
|
-
--verbose
|
|
376
|
-
--no-tool-check
|
|
377
|
-
)" --verbose
|
|
378
|
-
|
|
379
|
-
# Press CTRL + A + D for detach from screen
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
Note: You may need to register you own bot with https://t.me/BotFather to get the bot token.
|
|
383
|
-
|
|
384
|
-
#### Codex sign-in
|
|
385
|
-
|
|
386
|
-
1. Connect to your instance of VPS with Hive Mind installed, using SSH with tunnel opened
|
|
387
|
-
|
|
388
|
-
```bash
|
|
389
|
-
ssh -L 1455:localhost:1455 root@123.123.123.123
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
2. Start codex login oAuth server:
|
|
393
|
-
|
|
394
|
-
```bash
|
|
395
|
-
codex login
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
The oAuth callback server on 1455 port will be started, and the link to oAuth will be printed, copy the link.
|
|
399
|
-
|
|
400
|
-
3. Use your browser on machine where you started the tunnel from, paste there the link from `codex login` command, and go there using your browser. Once redirected to localhost:1455 you will see successful login page, and in `codex login` you will see `Successfully logged in`. After that `codex login` command will complete, and you can use `codex` command as usual to verify. It should also be working with `--tool codex` in `solve` and `hive` commands.
|
|
297
|
+
> ⚠️ **DEPRECATED:** This installation method is no longer recommended.
|
|
298
|
+
>
|
|
299
|
+
> **We now recommend using Docker for all installations**, both on developer machines and servers.
|
|
300
|
+
> Docker provides better isolation, easier management, and consistent environments.
|
|
301
|
+
>
|
|
302
|
+
> Please use the [Docker installation method](#using-docker) above.
|
|
303
|
+
> For Kubernetes deployments, see the [Helm installation](#helm-installation-kubernetes) section.
|
|
304
|
+
>
|
|
305
|
+
> The legacy bare-metal installation instructions have been moved to [docs/UBUNTU-SERVER.md](./docs/UBUNTU-SERVER.md) for reference.
|
|
401
306
|
|
|
402
307
|
### Core Operations
|
|
403
308
|
|
|
404
309
|
```bash
|
|
405
310
|
# Solve using maximum power
|
|
406
|
-
solve https://github.com/Veronika89-lang/index.html/issues/1 --auto-continue --attach-logs --verbose --model opus --
|
|
311
|
+
solve https://github.com/Veronika89-lang/index.html/issues/1 --auto-continue --attach-logs --verbose --model opus --think max
|
|
407
312
|
|
|
408
|
-
# Solve GitHub issues automatically
|
|
409
|
-
solve https://github.com/owner/repo/issues/123 --
|
|
313
|
+
# Solve GitHub issues automatically
|
|
314
|
+
solve https://github.com/owner/repo/issues/123 --model sonnet
|
|
410
315
|
|
|
411
316
|
# Solve issue with PR to custom branch (manual fork mode)
|
|
412
317
|
solve https://github.com/owner/repo/issues/123 --base-branch develop --fork
|
|
@@ -420,8 +325,8 @@ solve https://github.com/owner/repo/issues/123 --resume session-id
|
|
|
420
325
|
# Start hive orchestration (monitor and solve issues automatically)
|
|
421
326
|
hive https://github.com/owner/repo --monitor-tag "help wanted" --concurrency 3
|
|
422
327
|
|
|
423
|
-
# Monitor all issues in organization
|
|
424
|
-
hive https://github.com/microsoft --all-issues --max-issues 10
|
|
328
|
+
# Monitor all issues in organization
|
|
329
|
+
hive https://github.com/microsoft --all-issues --max-issues 10
|
|
425
330
|
|
|
426
331
|
# Run collaborative review process
|
|
427
332
|
review --repo owner/repo --pr 456
|
|
@@ -440,8 +345,6 @@ review --repo owner/repo --pr 456
|
|
|
440
345
|
| `reviewers-hive.mjs` (alpha / experimental) | Review team management | Multi-agent consensus, reviewer assignment |
|
|
441
346
|
| `telegram-bot.mjs` (stable) | Telegram bot interface | Remote command execution, group chat support, diagnostic tools |
|
|
442
347
|
|
|
443
|
-
> **Note**: For a comprehensive analysis of the "Could not process image" error in AI issue solvers, see the [Case Study: Issue #597](docs/case-studies/issue-597/README.md). The case study includes root cause analysis, timeline reconstruction, and evidence of GitHub's time-limited S3 URLs causing image processing failures. Separate tools for downloading GitHub issues and PRs with embedded images are being developed at [gh-download-issue](https://github.com/link-foundation/gh-download-issue) and [gh-download-pull-request](https://github.com/link-foundation/gh-download-pull-request).
|
|
444
|
-
|
|
445
348
|
## 🔧 solve Options
|
|
446
349
|
|
|
447
350
|
```bash
|
|
@@ -715,8 +618,8 @@ sequenceDiagram
|
|
|
715
618
|
### Automated Issue Resolution
|
|
716
619
|
|
|
717
620
|
```bash
|
|
718
|
-
#
|
|
719
|
-
solve https://github.com/owner/repo/issues/123 --
|
|
621
|
+
# Solve issue (automatically forks if no write access)
|
|
622
|
+
solve https://github.com/owner/repo/issues/123 --model opus
|
|
720
623
|
|
|
721
624
|
# Manual fork and solve issue (works for both public and private repos)
|
|
722
625
|
solve https://github.com/owner/repo/issues/123 --fork --model opus
|
|
@@ -737,17 +640,17 @@ solve https://github.com/owner/repo/issues/123 --dry-run
|
|
|
737
640
|
# Monitor single repository with specific label
|
|
738
641
|
hive https://github.com/owner/repo --monitor-tag "bug" --concurrency 4
|
|
739
642
|
|
|
740
|
-
# Monitor all issues in an organization
|
|
741
|
-
hive https://github.com/microsoft --all-issues --max-issues 20 --once
|
|
643
|
+
# Monitor all issues in an organization
|
|
644
|
+
hive https://github.com/microsoft --all-issues --max-issues 20 --once
|
|
742
645
|
|
|
743
646
|
# Monitor user repositories with high concurrency
|
|
744
|
-
hive https://github.com/username --all-issues --concurrency 8 --interval 120
|
|
647
|
+
hive https://github.com/username --all-issues --concurrency 8 --interval 120
|
|
745
648
|
|
|
746
649
|
# Skip issues that already have PRs
|
|
747
650
|
hive https://github.com/org/repo --skip-issues-with-prs --verbose
|
|
748
651
|
|
|
749
|
-
# Auto-cleanup temporary files
|
|
750
|
-
hive https://github.com/org/repo --auto-cleanup --
|
|
652
|
+
# Auto-cleanup temporary files
|
|
653
|
+
hive https://github.com/org/repo --auto-cleanup --concurrency 5
|
|
751
654
|
```
|
|
752
655
|
|
|
753
656
|
### Session Management
|
package/package.json
CHANGED
package/src/limits.lib.mjs
CHANGED
|
@@ -700,177 +700,184 @@ export function calculateTimePassedPercentage(resetsAt, periodHours) {
|
|
|
700
700
|
* @param {Object} cpuLoad - Optional CPU load info from getCpuLoadInfo
|
|
701
701
|
* @param {Object} memory - Optional memory info from getMemoryInfo
|
|
702
702
|
* @param {string|null} claudeError - Optional error message to show in Claude sections (e.g., auth expired)
|
|
703
|
-
* @
|
|
703
|
+
* @param {string[]} extraSections - Optional extra sections to append inside the code block (e.g. queue status)
|
|
704
|
+
* @returns {string} Formatted message wrapped in a single code block
|
|
704
705
|
* @see https://github.com/link-assistant/hive-mind/issues/1242
|
|
705
706
|
*/
|
|
706
|
-
export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = null, cpuLoad = null, memory = null, claudeError = null) {
|
|
707
|
-
//
|
|
708
|
-
|
|
707
|
+
export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = null, cpuLoad = null, memory = null, claudeError = null, extraSections = []) {
|
|
708
|
+
// Build sections as individual text blocks; they will all be joined and wrapped in a
|
|
709
|
+
// single code block at the end. This avoids fragile string-searching to inject content.
|
|
710
|
+
|
|
711
|
+
const sections = [];
|
|
709
712
|
|
|
710
713
|
// Show current time
|
|
711
|
-
|
|
714
|
+
sections.push(`Current time: ${formatCurrentTime()}\n`);
|
|
712
715
|
|
|
713
716
|
// CPU load section (if provided)
|
|
714
717
|
// Threshold: Blocks new commands when usage >= 65%
|
|
715
718
|
if (cpuLoad) {
|
|
716
|
-
|
|
719
|
+
let section = 'CPU\n';
|
|
717
720
|
const usedBar = getProgressBar(cpuLoad.usagePercentage, DISPLAY_THRESHOLDS.CPU);
|
|
718
721
|
// Show 'used' label when below threshold, warning emoji when at/above threshold
|
|
719
722
|
// See: https://github.com/link-assistant/hive-mind/issues/1267
|
|
720
723
|
const suffix = cpuLoad.usagePercentage >= DISPLAY_THRESHOLDS.CPU ? ' ⚠️' : ' used';
|
|
721
|
-
|
|
724
|
+
section += `${usedBar} ${cpuLoad.usagePercentage}%${suffix}\n`;
|
|
722
725
|
// Show cores used based on 5m load average (e.g., "0.04/6 CPU cores used" or "3/6 CPU cores used")
|
|
723
726
|
// Use parseFloat to strip unnecessary trailing zeros (3.00 -> 3, 0.10 -> 0.1, 0.04 -> 0.04)
|
|
724
|
-
|
|
727
|
+
section += `${parseFloat(cpuLoad.loadAvg5.toFixed(2))}/${cpuLoad.cpuCount} CPU cores\n`;
|
|
728
|
+
sections.push(section);
|
|
725
729
|
}
|
|
726
730
|
|
|
727
731
|
// Memory section (if provided)
|
|
728
732
|
// Threshold: Blocks new commands when usage >= 65%
|
|
729
733
|
if (memory) {
|
|
730
|
-
|
|
734
|
+
let section = 'RAM\n';
|
|
731
735
|
const usedBar = getProgressBar(memory.usedPercentage, DISPLAY_THRESHOLDS.RAM);
|
|
732
736
|
const suffix = memory.usedPercentage >= DISPLAY_THRESHOLDS.RAM ? ' ⚠️' : ' used';
|
|
733
|
-
|
|
734
|
-
|
|
737
|
+
section += `${usedBar} ${memory.usedPercentage}%${suffix}\n`;
|
|
738
|
+
section += `${formatBytesRange(memory.usedBytes, memory.totalBytes)}\n`;
|
|
739
|
+
sections.push(section);
|
|
735
740
|
}
|
|
736
741
|
|
|
737
742
|
// Disk space section (if provided)
|
|
738
743
|
// Threshold: One-at-a-time mode when usage >= 90%
|
|
739
744
|
if (diskSpace) {
|
|
740
|
-
|
|
745
|
+
let section = 'Disk space\n';
|
|
741
746
|
// Show used percentage with progress bar and threshold marker
|
|
742
747
|
const usedBar = getProgressBar(diskSpace.usedPercentage, DISPLAY_THRESHOLDS.DISK);
|
|
743
748
|
const suffix = diskSpace.usedPercentage >= DISPLAY_THRESHOLDS.DISK ? ' ⚠️' : ' used';
|
|
744
|
-
|
|
745
|
-
|
|
749
|
+
section += `${usedBar} ${diskSpace.usedPercentage}%${suffix}\n`;
|
|
750
|
+
section += `${formatBytesRange(diskSpace.usedBytes, diskSpace.totalBytes)}\n`;
|
|
751
|
+
sections.push(section);
|
|
746
752
|
}
|
|
747
753
|
|
|
748
754
|
// GitHub API rate limits section (if provided)
|
|
749
755
|
// Threshold: Blocks parallel claude commands when >= 75%
|
|
750
756
|
if (githubRateLimit) {
|
|
751
|
-
|
|
757
|
+
let section = 'GitHub API\n';
|
|
752
758
|
// Show used percentage with progress bar and threshold marker
|
|
753
759
|
const usedBar = getProgressBar(githubRateLimit.usedPercentage, DISPLAY_THRESHOLDS.GITHUB_API);
|
|
754
760
|
const suffix = githubRateLimit.usedPercentage >= DISPLAY_THRESHOLDS.GITHUB_API ? ' ⚠️' : ' used';
|
|
755
|
-
|
|
756
|
-
|
|
761
|
+
section += `${usedBar} ${githubRateLimit.usedPercentage}%${suffix}\n`;
|
|
762
|
+
section += `${githubRateLimit.used}/${githubRateLimit.limit} requests\n`;
|
|
757
763
|
if (githubRateLimit.relativeReset) {
|
|
758
|
-
|
|
764
|
+
section += `Resets in ${githubRateLimit.relativeReset} (${githubRateLimit.resetTime})\n`;
|
|
759
765
|
} else if (githubRateLimit.resetTime) {
|
|
760
|
-
|
|
766
|
+
section += `Resets ${githubRateLimit.resetTime}\n`;
|
|
761
767
|
}
|
|
762
|
-
|
|
768
|
+
sections.push(section);
|
|
763
769
|
}
|
|
764
770
|
|
|
765
771
|
// Claude limits section
|
|
766
|
-
// When there's an error (e.g., auth expired), show it once
|
|
767
|
-
if (claudeError) {
|
|
768
|
-
message += `Claude limits\n${claudeError}\n\n`;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
// Claude 5 hour session (five_hour)
|
|
772
|
-
// Threshold: One-at-a-time mode when usage >= 65%
|
|
773
|
-
message += 'Claude 5 hour session\n';
|
|
772
|
+
// When there's an error (e.g., auth expired), show it once and skip empty subsections
|
|
774
773
|
if (claudeError) {
|
|
775
|
-
|
|
776
|
-
} else
|
|
777
|
-
//
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
774
|
+
sections.push(`Claude limits\n${claudeError}\n`);
|
|
775
|
+
} else {
|
|
776
|
+
// Claude 5 hour session (five_hour)
|
|
777
|
+
// Threshold: One-at-a-time mode when usage >= 65%
|
|
778
|
+
let sessionSection = 'Claude 5 hour session\n';
|
|
779
|
+
if (usage && usage.currentSession.percentage !== null) {
|
|
780
|
+
// Add time passed progress bar first (no threshold marker for time)
|
|
781
|
+
const timePassed = calculateTimePassedPercentage(usage.currentSession.resetsAt, 5);
|
|
782
|
+
if (timePassed !== null) {
|
|
783
|
+
const timeBar = getProgressBar(timePassed);
|
|
784
|
+
sessionSection += `${timeBar} ${timePassed}% passed\n`;
|
|
785
|
+
}
|
|
783
786
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
787
|
+
// Add usage progress bar second with threshold marker
|
|
788
|
+
// Use Math.floor so 100% only appears when usage is exactly 100%
|
|
789
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1133
|
|
790
|
+
const pct = Math.floor(usage.currentSession.percentage);
|
|
791
|
+
const bar = getProgressBar(pct, DISPLAY_THRESHOLDS.CLAUDE_5_HOUR_SESSION);
|
|
792
|
+
const suffix = pct >= DISPLAY_THRESHOLDS.CLAUDE_5_HOUR_SESSION ? ' ⚠️' : ' used';
|
|
793
|
+
sessionSection += `${bar} ${pct}%${suffix}\n`;
|
|
794
|
+
|
|
795
|
+
if (usage.currentSession.resetTime) {
|
|
796
|
+
const relativeTime = formatRelativeTime(usage.currentSession.resetsAt);
|
|
797
|
+
if (relativeTime) {
|
|
798
|
+
sessionSection += `Resets in ${relativeTime} (${usage.currentSession.resetTime})\n`;
|
|
799
|
+
} else {
|
|
800
|
+
sessionSection += `Resets ${usage.currentSession.resetTime}\n`;
|
|
801
|
+
}
|
|
798
802
|
}
|
|
803
|
+
} else {
|
|
804
|
+
sessionSection += 'N/A\n';
|
|
799
805
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
806
|
+
sections.push(sessionSection);
|
|
807
|
+
|
|
808
|
+
// Current week (all models / seven_day)
|
|
809
|
+
// Threshold: One-at-a-time mode when usage >= 97%
|
|
810
|
+
let allModelsSection = 'Current week (all models)\n';
|
|
811
|
+
if (usage && usage.allModels.percentage !== null) {
|
|
812
|
+
// Add time passed progress bar first (no threshold marker for time)
|
|
813
|
+
const timePassed = calculateTimePassedPercentage(usage.allModels.resetsAt, 168);
|
|
814
|
+
if (timePassed !== null) {
|
|
815
|
+
const timeBar = getProgressBar(timePassed);
|
|
816
|
+
allModelsSection += `${timeBar} ${timePassed}% passed\n`;
|
|
817
|
+
}
|
|
804
818
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
819
|
+
// Add usage progress bar second with threshold marker
|
|
820
|
+
// Use Math.floor so 100% only appears when usage is exactly 100%
|
|
821
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1133
|
|
822
|
+
const pct = Math.floor(usage.allModels.percentage);
|
|
823
|
+
const bar = getProgressBar(pct, DISPLAY_THRESHOLDS.CLAUDE_WEEKLY);
|
|
824
|
+
const suffix = pct >= DISPLAY_THRESHOLDS.CLAUDE_WEEKLY ? ' ⚠️' : ' used';
|
|
825
|
+
allModelsSection += `${bar} ${pct}%${suffix}\n`;
|
|
826
|
+
|
|
827
|
+
if (usage.allModels.resetTime) {
|
|
828
|
+
const relativeTime = formatRelativeTime(usage.allModels.resetsAt);
|
|
829
|
+
if (relativeTime) {
|
|
830
|
+
allModelsSection += `Resets in ${relativeTime} (${usage.allModels.resetTime})\n`;
|
|
831
|
+
} else {
|
|
832
|
+
allModelsSection += `Resets ${usage.allModels.resetTime}\n`;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
} else {
|
|
836
|
+
allModelsSection += 'N/A\n';
|
|
816
837
|
}
|
|
838
|
+
sections.push(allModelsSection);
|
|
839
|
+
|
|
840
|
+
// Current week (Sonnet only / seven_day_sonnet)
|
|
841
|
+
// Threshold: One-at-a-time mode when usage >= 97% (same as all models)
|
|
842
|
+
let sonnetSection = 'Current week (Sonnet only)\n';
|
|
843
|
+
if (usage && usage.sonnetOnly.percentage !== null) {
|
|
844
|
+
// Add time passed progress bar first (no threshold marker for time)
|
|
845
|
+
const timePassed = calculateTimePassedPercentage(usage.sonnetOnly.resetsAt, 168);
|
|
846
|
+
if (timePassed !== null) {
|
|
847
|
+
const timeBar = getProgressBar(timePassed);
|
|
848
|
+
sonnetSection += `${timeBar} ${timePassed}% passed\n`;
|
|
849
|
+
}
|
|
817
850
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
851
|
+
// Add usage progress bar second with threshold marker
|
|
852
|
+
// Use Math.floor so 100% only appears when usage is exactly 100%
|
|
853
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1133
|
|
854
|
+
const pct = Math.floor(usage.sonnetOnly.percentage);
|
|
855
|
+
const bar = getProgressBar(pct, DISPLAY_THRESHOLDS.CLAUDE_WEEKLY);
|
|
856
|
+
const suffix = pct >= DISPLAY_THRESHOLDS.CLAUDE_WEEKLY ? ' ⚠️' : ' used';
|
|
857
|
+
sonnetSection += `${bar} ${pct}%${suffix}\n`;
|
|
858
|
+
|
|
859
|
+
if (usage.sonnetOnly.resetTime) {
|
|
860
|
+
const relativeTime = formatRelativeTime(usage.sonnetOnly.resetsAt);
|
|
861
|
+
if (relativeTime) {
|
|
862
|
+
sonnetSection += `Resets in ${relativeTime} (${usage.sonnetOnly.resetTime})\n`;
|
|
863
|
+
} else {
|
|
864
|
+
sonnetSection += `Resets ${usage.sonnetOnly.resetTime}\n`;
|
|
865
|
+
}
|
|
832
866
|
}
|
|
867
|
+
} else {
|
|
868
|
+
sonnetSection += 'N/A\n';
|
|
833
869
|
}
|
|
834
|
-
|
|
835
|
-
message += 'N/A\n';
|
|
870
|
+
sections.push(sonnetSection);
|
|
836
871
|
}
|
|
837
|
-
message += '\n';
|
|
838
872
|
|
|
839
|
-
//
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
if (claudeError) {
|
|
843
|
-
// Error already shown above; skip subsection content
|
|
844
|
-
} else if (usage && usage.sonnetOnly.percentage !== null) {
|
|
845
|
-
// Add time passed progress bar first (no threshold marker for time)
|
|
846
|
-
const timePassed = calculateTimePassedPercentage(usage.sonnetOnly.resetsAt, 168);
|
|
847
|
-
if (timePassed !== null) {
|
|
848
|
-
const timeBar = getProgressBar(timePassed);
|
|
849
|
-
message += `${timeBar} ${timePassed}% passed\n`;
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
// Add usage progress bar second with threshold marker
|
|
853
|
-
// Use Math.floor so 100% only appears when usage is exactly 100%
|
|
854
|
-
// See: https://github.com/link-assistant/hive-mind/issues/1133
|
|
855
|
-
const pct = Math.floor(usage.sonnetOnly.percentage);
|
|
856
|
-
const bar = getProgressBar(pct, DISPLAY_THRESHOLDS.CLAUDE_WEEKLY);
|
|
857
|
-
const suffix = pct >= DISPLAY_THRESHOLDS.CLAUDE_WEEKLY ? ' ⚠️' : ' used';
|
|
858
|
-
message += `${bar} ${pct}%${suffix}\n`;
|
|
859
|
-
|
|
860
|
-
if (usage.sonnetOnly.resetTime) {
|
|
861
|
-
const relativeTime = formatRelativeTime(usage.sonnetOnly.resetsAt);
|
|
862
|
-
if (relativeTime) {
|
|
863
|
-
message += `Resets in ${relativeTime} (${usage.sonnetOnly.resetTime})\n`;
|
|
864
|
-
} else {
|
|
865
|
-
message += `Resets ${usage.sonnetOnly.resetTime}\n`;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
} else {
|
|
869
|
-
message += 'N/A\n';
|
|
873
|
+
// Append any caller-provided extra sections (e.g. queue status) inside the code block
|
|
874
|
+
for (const extra of extraSections) {
|
|
875
|
+
sections.push(extra);
|
|
870
876
|
}
|
|
871
877
|
|
|
872
|
-
|
|
873
|
-
|
|
878
|
+
// Wrap all sections in a single code block for monospace font / aligned progress bars.
|
|
879
|
+
// Sections are separated by blank lines; the trailing newline on each section provides spacing.
|
|
880
|
+
return '```\n' + sections.join('\n') + '```';
|
|
874
881
|
}
|
|
875
882
|
|
|
876
883
|
// ============================================================================
|
|
@@ -345,6 +345,13 @@ export const watchUntilMergeable = async params => {
|
|
|
345
345
|
// `restartCount` counts actual AI tool executions (when we actually restart the AI)
|
|
346
346
|
let restartCount = 0;
|
|
347
347
|
|
|
348
|
+
// Issue #1371: Track whether a "Ready to merge" comment was posted in THIS session.
|
|
349
|
+
// This replaces the all-time history check (checkForExistingComment) which incorrectly
|
|
350
|
+
// suppressed new notifications when a previous solve run had already posted one.
|
|
351
|
+
// In-memory deduplication correctly handles the case where multiple check cycles in
|
|
352
|
+
// the same run detect mergeability simultaneously, without blocking fresh runs.
|
|
353
|
+
let readyToMergeCommentPosted = false;
|
|
354
|
+
|
|
348
355
|
let currentBackoffSeconds = watchInterval;
|
|
349
356
|
|
|
350
357
|
await log('');
|
|
@@ -430,18 +437,19 @@ export const watchUntilMergeable = async params => {
|
|
|
430
437
|
await log(formatAligned('', 'PR is ready to be merged manually', '', 2));
|
|
431
438
|
await log(formatAligned('', 'Exiting auto-restart-until-mergeable mode', '', 2));
|
|
432
439
|
|
|
433
|
-
// Issue #
|
|
434
|
-
//
|
|
440
|
+
// Issue #1371: Post success comment only if not already posted in this session.
|
|
441
|
+
// Use in-memory flag instead of checking all PR comment history (issue #1323),
|
|
442
|
+
// since the historical check incorrectly suppressed notifications when a
|
|
443
|
+
// previous solve run had already posted a "Ready to merge" comment.
|
|
435
444
|
try {
|
|
436
|
-
|
|
437
|
-
const hasExistingComment = await checkForExistingComment(owner, repo, prNumber, readyToMergeSignature, argv.verbose);
|
|
438
|
-
if (!hasExistingComment) {
|
|
445
|
+
if (!readyToMergeCommentPosted) {
|
|
439
446
|
// Issue #1345: Differentiate message when no CI is configured
|
|
440
447
|
const ciLine = noCiConfigured ? '- No CI/CD checks are configured for this repository' : '- All CI checks have passed';
|
|
441
448
|
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*`;
|
|
442
449
|
await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
|
|
450
|
+
readyToMergeCommentPosted = true;
|
|
443
451
|
} else {
|
|
444
|
-
await log(formatAligned('', 'Skipping duplicate "Ready to merge" comment', '', 2));
|
|
452
|
+
await log(formatAligned('', 'Skipping duplicate "Ready to merge" comment (already posted this session)', '', 2));
|
|
445
453
|
}
|
|
446
454
|
} catch {
|
|
447
455
|
// Don't fail if comment posting fails
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -785,17 +785,14 @@ bot.command('limits', async ctx => {
|
|
|
785
785
|
// while still displaying all other limits sections (disk, GitHub, CPU, memory)
|
|
786
786
|
// See: https://github.com/link-assistant/hive-mind/issues/1343
|
|
787
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);
|
|
789
788
|
const solveQueue = getSolveQueue({ verbose: VERBOSE });
|
|
790
|
-
//
|
|
791
|
-
//
|
|
792
|
-
//
|
|
789
|
+
// Fetch queue status and pass it as an extra section to formatUsageMessage so that all
|
|
790
|
+
// sections are assembled before the code block is formed — no fragile string-searching needed.
|
|
791
|
+
// Shows each queue (claude, agent) with pending/processing counts.
|
|
792
|
+
// Processing counts are actual running system processes (via pgrep).
|
|
793
793
|
// See: https://github.com/link-assistant/hive-mind/issues/1267
|
|
794
|
-
const
|
|
795
|
-
|
|
796
|
-
const queueStatus = await solveQueue.formatStatus();
|
|
797
|
-
message = message.slice(0, codeBlockEnd) + `\n${queueStatus}` + message.slice(codeBlockEnd);
|
|
798
|
-
}
|
|
794
|
+
const queueStatus = await solveQueue.formatStatus();
|
|
795
|
+
const 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, [queueStatus]);
|
|
799
796
|
await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, message, { parse_mode: 'Markdown' });
|
|
800
797
|
});
|
|
801
798
|
bot.command('version', async ctx => {
|