@link-assistant/hive-mind 0.51.6 → 0.51.7

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 0.51.7
4
+
5
+ ### Patch Changes
6
+
7
+ - b7c7a2c: feat: add GitHub API rate limits to /limits command
8
+
9
+ Adds GitHub API core rate limit information to the Telegram bot's /limits command output, allowing users to monitor GitHub API usage alongside Claude usage limits and disk space. This helps plan issue execution when GitHub API limits are approaching.
10
+
3
11
  ## 0.51.6
4
12
 
5
13
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "0.51.6",
3
+ "version": "0.51.7",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -147,6 +147,83 @@ function formatBytes(bytes) {
147
147
  return `${value.toFixed(decimals)} ${sizes[i]}`;
148
148
  }
149
149
 
150
+ /**
151
+ * Get GitHub API rate limits by calling gh api rate_limit
152
+ * Returns rate limit info for core, search, graphql, and other resources
153
+ *
154
+ * @param {boolean} verbose - Whether to log verbose output
155
+ * @returns {Object} Object with success boolean, and either rate limit data or error message
156
+ */
157
+ export async function getGitHubRateLimits(verbose = false) {
158
+ try {
159
+ const { stdout } = await execAsync('gh api rate_limit 2>/dev/null');
160
+ const data = JSON.parse(stdout);
161
+
162
+ if (verbose) {
163
+ console.log('[VERBOSE] /limits GitHub rate limit response:', JSON.stringify(data, null, 2));
164
+ }
165
+
166
+ // Extract the core rate limit (most important for general API usage)
167
+ const core = data.resources?.core;
168
+ if (!core) {
169
+ return {
170
+ success: false,
171
+ error: 'Could not parse GitHub rate limit response',
172
+ };
173
+ }
174
+
175
+ // Calculate remaining percentage
176
+ const usedPercentage = core.limit > 0 ? Math.round((core.used / core.limit) * 100) : 0;
177
+ const remainingPercentage = 100 - usedPercentage;
178
+
179
+ // Format reset time from Unix timestamp
180
+ const resetDate = new Date(core.reset * 1000);
181
+ const resetTimeFormatted = formatResetTime(resetDate.toISOString());
182
+
183
+ // Calculate relative time until reset
184
+ const now = new Date();
185
+ const diffMs = resetDate - now;
186
+ let relativeReset = null;
187
+ if (diffMs > 0) {
188
+ const totalMinutes = Math.floor(diffMs / (1000 * 60));
189
+ const hours = Math.floor(totalMinutes / 60);
190
+ const minutes = totalMinutes % 60;
191
+ if (hours > 0) {
192
+ relativeReset = `${hours}h ${minutes}m`;
193
+ } else {
194
+ relativeReset = `${minutes}m`;
195
+ }
196
+ }
197
+
198
+ if (verbose) {
199
+ console.log(`[VERBOSE] /limits GitHub API: ${core.remaining}/${core.limit} remaining (${remainingPercentage}% available)`);
200
+ }
201
+
202
+ return {
203
+ success: true,
204
+ githubRateLimit: {
205
+ limit: core.limit,
206
+ used: core.used,
207
+ remaining: core.remaining,
208
+ usedPercentage,
209
+ remainingPercentage,
210
+ resetTimestamp: core.reset,
211
+ resetTime: resetTimeFormatted,
212
+ relativeReset,
213
+ resetsAt: resetDate.toISOString(),
214
+ },
215
+ };
216
+ } catch (error) {
217
+ if (verbose) {
218
+ console.error('[VERBOSE] /limits GitHub rate limit error:', error);
219
+ }
220
+ return {
221
+ success: false,
222
+ error: `Failed to get GitHub rate limits: ${error.message}`,
223
+ };
224
+ }
225
+ }
226
+
150
227
  /**
151
228
  * Get disk space information for the current filesystem
152
229
  * Returns total, used, available space and usage percentage
@@ -385,9 +462,10 @@ export function calculateTimePassedPercentage(resetsAt, periodHours) {
385
462
  * Format Claude usage data into a Telegram-friendly message
386
463
  * @param {Object} usage - The usage object from getClaudeUsageLimits
387
464
  * @param {Object} diskSpace - Optional disk space info from getDiskSpaceInfo
465
+ * @param {Object} githubRateLimit - Optional GitHub rate limit info from getGitHubRateLimits
388
466
  * @returns {string} Formatted message
389
467
  */
390
- export function formatUsageMessage(usage, diskSpace = null) {
468
+ export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = null) {
391
469
  // Use code block for monospace font to align progress bars properly
392
470
  let message = '```\n';
393
471
 
@@ -397,10 +475,25 @@ export function formatUsageMessage(usage, diskSpace = null) {
397
475
  // Disk space section (if provided)
398
476
  if (diskSpace) {
399
477
  message += 'Disk space\n';
400
- // Show free percentage with progress bar (inverted - showing free space)
401
- const freeBar = getProgressBar(diskSpace.freePercentage);
402
- message += `${freeBar} ${diskSpace.freePercentage}% free\n`;
403
- message += `${diskSpace.availableFormatted} free of ${diskSpace.totalFormatted}\n\n`;
478
+ // Show used percentage with progress bar
479
+ const usedBar = getProgressBar(diskSpace.usedPercentage);
480
+ message += `${usedBar} ${diskSpace.usedPercentage}% used\n`;
481
+ message += `${diskSpace.usedFormatted} used of ${diskSpace.totalFormatted}\n\n`;
482
+ }
483
+
484
+ // GitHub API rate limits section (if provided)
485
+ if (githubRateLimit) {
486
+ message += 'GitHub API\n';
487
+ // Show used percentage with progress bar
488
+ const usedBar = getProgressBar(githubRateLimit.usedPercentage);
489
+ message += `${usedBar} ${githubRateLimit.usedPercentage}% used\n`;
490
+ message += `${githubRateLimit.used}/${githubRateLimit.limit} requests used\n`;
491
+ if (githubRateLimit.relativeReset) {
492
+ message += `Resets in ${githubRateLimit.relativeReset} (${githubRateLimit.resetTime})\n`;
493
+ } else if (githubRateLimit.resetTime) {
494
+ message += `Resets ${githubRateLimit.resetTime}\n`;
495
+ }
496
+ message += '\n';
404
497
  }
405
498
 
406
499
  // Current session (five_hour)
@@ -493,6 +586,7 @@ export function formatUsageMessage(usage, diskSpace = null) {
493
586
  export default {
494
587
  getClaudeUsageLimits,
495
588
  getDiskSpaceInfo,
589
+ getGitHubRateLimits,
496
590
  getProgressBar,
497
591
  calculateTimePassedPercentage,
498
592
  formatUsageMessage,
@@ -45,7 +45,7 @@ const { parseGitHubUrl } = await import('./github.lib.mjs');
45
45
  const { validateModelName } = await import('./model-validation.lib.mjs');
46
46
 
47
47
  // Import Claude limits library for /limits command
48
- const { getClaudeUsageLimits, getDiskSpaceInfo, formatUsageMessage } = await import('./claude-limits.lib.mjs');
48
+ const { getClaudeUsageLimits, getDiskSpaceInfo, getGitHubRateLimits, formatUsageMessage } = await import('./claude-limits.lib.mjs');
49
49
 
50
50
  // Import version info library for /version command
51
51
  const { getVersionInfo, formatVersionMessage } = await import('./version-info.lib.mjs');
@@ -848,8 +848,8 @@ bot.command('limits', async ctx => {
848
848
  reply_to_message_id: ctx.message.message_id,
849
849
  });
850
850
 
851
- // Get the usage limits and disk space info in parallel
852
- const [result, diskSpaceResult] = await Promise.all([getClaudeUsageLimits(VERBOSE), getDiskSpaceInfo(VERBOSE)]);
851
+ // Get the usage limits, disk space info, and GitHub rate limits in parallel
852
+ const [result, diskSpaceResult, githubLimitsResult] = await Promise.all([getClaudeUsageLimits(VERBOSE), getDiskSpaceInfo(VERBOSE), getGitHubRateLimits(VERBOSE)]);
853
853
 
854
854
  if (!result.success) {
855
855
  // Edit the fetching message to show the error
@@ -859,10 +859,8 @@ bot.command('limits', async ctx => {
859
859
  return;
860
860
  }
861
861
 
862
- // Format and edit the fetching message with the results
863
- // Pass disk space info if available (non-critical if it fails)
864
- const diskSpace = diskSpaceResult.success ? diskSpaceResult.diskSpace : null;
865
- const message = '📊 *Usage Limits*\n\n' + formatUsageMessage(result.usage, diskSpace);
862
+ // Format and edit the fetching message with the results (pass disk space and GitHub limits if available)
863
+ const message = '📊 *Usage Limits*\n\n' + formatUsageMessage(result.usage, diskSpaceResult.success ? diskSpaceResult.diskSpace : null, githubLimitsResult.success ? githubLimitsResult.githubRateLimit : null);
866
864
  await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, message, {
867
865
  parse_mode: 'Markdown',
868
866
  });