@link-assistant/hive-mind 1.73.6 → 1.73.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 +18 -0
- package/package.json +1 -1
- package/src/locales/en.lino +1 -1
- package/src/locales/hi.lino +1 -1
- package/src/locales/ru.lino +1 -1
- package/src/locales/zh.lino +1 -1
- package/src/queue-config.lib.mjs +7 -0
- package/src/telegram-bot.mjs +2 -1
- package/src/telegram-solve-queue-command.lib.mjs +4 -3
- package/src/telegram-solve-queue.helpers.lib.mjs +58 -0
- package/src/telegram-solve-queue.lib.mjs +25 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.73.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6188172: feat(telegram): list executed issues/PRs as clickable links in /solve_queue, add /queue alias (#1837)
|
|
8
|
+
|
|
9
|
+
The `/solve_queue` detailed status previously showed only per-tool counts and a
|
|
10
|
+
final `Completed: N, Failed: M` line, so a stuck or running task could not be
|
|
11
|
+
opened from the message. It now lists each processing (`▶️`), pending (`•`),
|
|
12
|
+
recently completed (`✅`), and failed (`❌`, with the error reason) item as a
|
|
13
|
+
clickable `[owner/repo#number](url)` link, capped per section
|
|
14
|
+
(`HIVE_MIND_MAX_DISPLAY_ITEMS_PER_QUEUE`, default 5) with a localized
|
|
15
|
+
`... and N more` line to stay under Telegram's 4096-character limit.
|
|
16
|
+
|
|
17
|
+
Also adds `/queue` as a shorter alias for `/solve_queue` (both the entity-based
|
|
18
|
+
command regex and the text-based fallback handler), and documents the work in
|
|
19
|
+
`docs/case-studies/issue-1837`.
|
|
20
|
+
|
|
3
21
|
## 1.73.6
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/package.json
CHANGED
package/src/locales/en.lino
CHANGED
|
@@ -515,7 +515,7 @@ en
|
|
|
515
515
|
detail "Tool aliases imply `--tool <tool>`: `/codex <github-url>` equals `/solve <github-url> --tool codex`"
|
|
516
516
|
reply "Or reply to a message with a GitHub link: `/solve`"
|
|
517
517
|
disabled "*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*, */gemini*, */qwen*) - ❌ Disabled"
|
|
518
|
-
queue "`/solve_queue` - Show solve queue status"
|
|
518
|
+
queue "`/solve_queue` (alias: `/queue`) - Show solve queue status"
|
|
519
519
|
locked
|
|
520
520
|
options "🔒 Locked options: `{{options}}`"
|
|
521
521
|
task
|
package/src/locales/hi.lino
CHANGED
|
@@ -515,7 +515,7 @@ hi
|
|
|
515
515
|
detail "Tool aliases `--tool <tool>` लगाते हैं: `/codex <github-url>` का अर्थ `/solve <github-url> --tool codex` है"
|
|
516
516
|
reply "या GitHub लिंक वाले संदेश का उत्तर दें: `/solve`"
|
|
517
517
|
disabled "*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*, */gemini*, */qwen*) - ❌ अक्षम"
|
|
518
|
-
queue "`/solve_queue` - solve queue status दिखाएँ"
|
|
518
|
+
queue "`/solve_queue` (उपनाम: `/queue`) - solve queue status दिखाएँ"
|
|
519
519
|
locked
|
|
520
520
|
options "🔒 लॉक किए गए विकल्प: `{{options}}`"
|
|
521
521
|
task
|
package/src/locales/ru.lino
CHANGED
|
@@ -515,7 +515,7 @@ ru
|
|
|
515
515
|
detail "Алиасы инструментов добавляют `--tool <tool>`: `/codex <github-url>` равно `/solve <github-url> --tool codex`"
|
|
516
516
|
reply "Или ответьте на сообщение со ссылкой GitHub: `/solve`"
|
|
517
517
|
disabled "*/solve* (алиасы: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*, */gemini*, */qwen*) - ❌ Отключено"
|
|
518
|
-
queue "`/solve_queue` - Показать состояние очереди solve"
|
|
518
|
+
queue "`/solve_queue` (псевдоним: `/queue`) - Показать состояние очереди solve"
|
|
519
519
|
locked
|
|
520
520
|
options "🔒 Заблокированные опции: `{{options}}`"
|
|
521
521
|
task
|
package/src/locales/zh.lino
CHANGED
|
@@ -515,7 +515,7 @@ zh
|
|
|
515
515
|
detail "工具别名会添加 `--tool <tool>`:`/codex <github-url>` 等同于 `/solve <github-url> --tool codex`"
|
|
516
516
|
reply "也可以回复包含 GitHub 链接的消息:`/solve`"
|
|
517
517
|
disabled "*/solve*(别名:*/do*、*/continue*、*/claude*、*/codex*、*/opencode*、*/agent*、*/gemini*、*/qwen*)- ❌ 已禁用"
|
|
518
|
-
queue "`/solve_queue
|
|
518
|
+
queue "`/solve_queue`(别名:`/queue`)- 显示 solve 队列状态"
|
|
519
519
|
locked
|
|
520
520
|
options "🔒 锁定选项:`{{options}}`"
|
|
521
521
|
task
|
package/src/queue-config.lib.mjs
CHANGED
|
@@ -280,6 +280,13 @@ export const QUEUE_CONFIG = {
|
|
|
280
280
|
|
|
281
281
|
// Process detection
|
|
282
282
|
CLAUDE_PROCESS_NAMES: ['claude'], // Process names to detect
|
|
283
|
+
|
|
284
|
+
// Display
|
|
285
|
+
// Maximum number of items shown per section (pending/processing/completed/failed)
|
|
286
|
+
// in the /solve_queue (/queue) detailed status before collapsing into a
|
|
287
|
+
// "... and N more" line. Keeps the Telegram message under the 4096-char cap.
|
|
288
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1837
|
|
289
|
+
MAX_DISPLAY_ITEMS_PER_QUEUE: parseIntWithDefault('HIVE_MIND_MAX_DISPLAY_ITEMS_PER_QUEUE', 5),
|
|
283
290
|
};
|
|
284
291
|
|
|
285
292
|
/**
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -1169,7 +1169,8 @@ bot.on('message', async (ctx, next) => {
|
|
|
1169
1169
|
// /subscribe + /unsubscribe (#1688) are intentionally not in the text fallback — Telegraf's bot.command() is sufficient.
|
|
1170
1170
|
const solveHandlers = Object.fromEntries(SOLVE_COMMAND_NAMES.map(command => [command, handleSolveCommand]));
|
|
1171
1171
|
const taskHandlers = Object.fromEntries(TASK_COMMAND_NAMES.map(command => [command, handleTaskCommand]));
|
|
1172
|
-
|
|
1172
|
+
// /queue is the short alias for /solve_queue (issue #1837)
|
|
1173
|
+
const handlers = { ...solveHandlers, ...taskHandlers, hive: handleHiveCommand, solve_queue: handleSolveQueueCommand, solvequeue: handleSolveQueueCommand, queue: handleSolveQueueCommand };
|
|
1173
1174
|
|
|
1174
1175
|
const handler = handlers[extracted.command];
|
|
1175
1176
|
if (!handler) return next();
|
|
@@ -97,11 +97,12 @@ export function registerSolveQueueCommand(bot, options) {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
// Match /solve_queue, /solve-queue, or /
|
|
100
|
+
// Match /solve_queue, /solve-queue, /solvequeue, or the short /queue alias (case-insensitive)
|
|
101
101
|
// Note: Telegram Bot API only supports underscores in command names, not hyphens.
|
|
102
|
-
// The entity-based matching handles /solve_queue and /
|
|
102
|
+
// The entity-based matching handles /solve_queue, /solvequeue, and /queue.
|
|
103
103
|
// /solve-queue is handled by the text-based fallback in telegram-bot.mjs (issue #1232).
|
|
104
|
-
|
|
104
|
+
// The /queue alias was added in issue #1837 to make checking the queue faster to type.
|
|
105
|
+
bot.command(/^(?:solve[_-]?queue|queue)$/i, handleSolveQueueCommand);
|
|
105
106
|
|
|
106
107
|
return { handleSolveQueueCommand };
|
|
107
108
|
}
|
|
@@ -6,6 +6,64 @@ import { lt } from './limits-i18n.lib.mjs';
|
|
|
6
6
|
|
|
7
7
|
const execAsync = promisify(exec);
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Build a clickable, human-readable link to a queued issue/PR for the
|
|
11
|
+
* /solve_queue (/queue) detailed status (issue #1837).
|
|
12
|
+
*
|
|
13
|
+
* For GitHub issue/PR URLs we render a compact `[owner/repo#number](url)`
|
|
14
|
+
* Markdown link so the list is scannable and clickable. When the label would
|
|
15
|
+
* contain Markdown-special characters (e.g. `_` or `*` in an owner/repo name)
|
|
16
|
+
* that could break Telegram's legacy Markdown parser, we fall back to the bare
|
|
17
|
+
* URL — which Telegram still auto-links and renders as clickable.
|
|
18
|
+
*
|
|
19
|
+
* Non-GitHub or unparseable URLs also fall back to the bare URL.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} url - The issue/PR URL.
|
|
22
|
+
* @returns {string} A Markdown link or bare URL safe for `parse_mode: 'Markdown'`.
|
|
23
|
+
*/
|
|
24
|
+
export function formatQueueItemLink(url) {
|
|
25
|
+
if (!url || typeof url !== 'string') return String(url ?? '');
|
|
26
|
+
const match = url.match(/github\.com\/([^/\s]+)\/([^/\s]+)\/(?:issues|pull)\/(\d+)/i);
|
|
27
|
+
if (!match) return url;
|
|
28
|
+
const [, owner, repo, number] = match;
|
|
29
|
+
const label = `${owner}/${repo}#${number}`;
|
|
30
|
+
// Only build a Markdown link when the label has no Markdown-special chars
|
|
31
|
+
// that would break the legacy parser inside link text. Otherwise the bare
|
|
32
|
+
// URL is still clickable in Telegram.
|
|
33
|
+
if (/^[A-Za-z0-9/#.-]+$/.test(label)) {
|
|
34
|
+
return `[${label}](${url})`;
|
|
35
|
+
}
|
|
36
|
+
return url;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Render a history section (Completed / Failed) for the detailed queue status
|
|
41
|
+
* as a clickable list, most-recent-first, capped at `max` items with a
|
|
42
|
+
* "... and N more" line (issue #1837).
|
|
43
|
+
*
|
|
44
|
+
* @param {object} opts
|
|
45
|
+
* @param {Array} opts.items - History items (each with `url`, optional `error`).
|
|
46
|
+
* @param {string} opts.emoji - Leading emoji for each row (e.g. '✅' or '❌').
|
|
47
|
+
* @param {string} opts.label - Localized section heading.
|
|
48
|
+
* @param {number} opts.max - Maximum items to list before collapsing.
|
|
49
|
+
* @param {string|null} opts.locale - Locale for the "and N more" label.
|
|
50
|
+
* @param {boolean} [opts.withError] - Append `— error` when the item failed.
|
|
51
|
+
* @returns {string} The formatted section (empty string when no items).
|
|
52
|
+
*/
|
|
53
|
+
export function formatQueueHistorySection({ items, emoji, label, max, locale, withError = false }) {
|
|
54
|
+
if (!items || items.length === 0) return '';
|
|
55
|
+
let section = `*${label}* (${items.length}):\n`;
|
|
56
|
+
for (const item of [...items].reverse().slice(0, max)) {
|
|
57
|
+
section += ` ${emoji} ${formatQueueItemLink(item.url)}`;
|
|
58
|
+
if (withError && item.error) section += ` — ${item.error}`;
|
|
59
|
+
section += '\n';
|
|
60
|
+
}
|
|
61
|
+
if (items.length > max) {
|
|
62
|
+
section += ` ... ${lt('queue_and_more', { count: items.length - max }, { locale })}\n`;
|
|
63
|
+
}
|
|
64
|
+
return `${section}\n`;
|
|
65
|
+
}
|
|
66
|
+
|
|
9
67
|
/**
|
|
10
68
|
* Count running processes by name.
|
|
11
69
|
* @param {string} processName - Process name to search for (e.g., 'claude', 'agent', 'codex', 'gemini')
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { getCachedClaudeLimits, getCachedCodexLimits, getCachedGitHubLimits, getCachedMemoryInfo, getCachedCpuInfo, getCachedDiskInfo, getLimitCache } from './limits.lib.mjs';
|
|
19
19
|
export { formatDuration, getRunningAgentProcesses, getRunningClaudeProcesses, getRunningCodexProcesses, getRunningGeminiProcesses, getRunningProcesses, getRunningQwenProcesses } from './telegram-solve-queue.helpers.lib.mjs';
|
|
20
|
-
import { formatDuration, formatWaitingReason, getRunningAgentProcesses, getRunningClaudeProcesses, getRunningCodexProcesses, getRunningGeminiProcesses, getRunningProcesses, getRunningQwenProcesses } from './telegram-solve-queue.helpers.lib.mjs';
|
|
20
|
+
import { formatDuration, formatQueueHistorySection, formatQueueItemLink, formatWaitingReason, getRunningAgentProcesses, getRunningClaudeProcesses, getRunningCodexProcesses, getRunningGeminiProcesses, getRunningProcesses, getRunningQwenProcesses } from './telegram-solve-queue.helpers.lib.mjs';
|
|
21
21
|
export { QUEUE_CONFIG, THRESHOLD_STRATEGIES } from './queue-config.lib.mjs';
|
|
22
22
|
import { QUEUE_CONFIG } from './queue-config.lib.mjs';
|
|
23
23
|
import { formatExecutingWorkSessionMessage, formatStartingWorkSessionMessage } from './work-session-formatting.lib.mjs';
|
|
@@ -1320,28 +1320,17 @@ export class SolveQueue {
|
|
|
1320
1320
|
}
|
|
1321
1321
|
|
|
1322
1322
|
/**
|
|
1323
|
-
* Format detailed queue status for Telegram message
|
|
1324
|
-
* Groups output by tool queue
|
|
1323
|
+
* Format detailed queue status for Telegram message.
|
|
1324
|
+
* Groups output by tool queue (clickable links per item), then lists the
|
|
1325
|
+
* Completed and Failed history as clickable links, capped per section.
|
|
1325
1326
|
*
|
|
1326
1327
|
* Processing count = max(actual AI CLI processes via pgrep, tracked
|
|
1327
1328
|
* `$ --status` executing screen-isolated sessions), not queue state.
|
|
1328
1329
|
*
|
|
1329
|
-
* Output format:
|
|
1330
|
-
* ```
|
|
1331
|
-
* 📋 Solve Queue Status
|
|
1332
|
-
*
|
|
1333
|
-
* claude (pending: 6, processing: 0)
|
|
1334
|
-
* • url1 (waiting, 5h 43m 23s)
|
|
1335
|
-
* └ RAM usage is 70% (threshold: 65%)
|
|
1336
|
-
* • url2 (queued, 2m 15s)
|
|
1337
|
-
*
|
|
1338
|
-
* agent (pending: 2, processing: 0)
|
|
1339
|
-
* • url3 (waiting, 1h 2m 5s)
|
|
1340
|
-
* ```
|
|
1341
|
-
*
|
|
1342
1330
|
* @returns {Promise<string>}
|
|
1343
1331
|
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
1344
1332
|
* @see https://github.com/link-assistant/hive-mind/issues/1267
|
|
1333
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1837
|
|
1345
1334
|
*/
|
|
1346
1335
|
async formatDetailedStatus(options = {}) {
|
|
1347
1336
|
const locale = getLocale(options);
|
|
@@ -1359,22 +1348,37 @@ export class SolveQueue {
|
|
|
1359
1348
|
const processing = externalProcessing.byTool[tool] || 0;
|
|
1360
1349
|
message += `*${tool}* (${lt('queue_pending', {}, { locale })}: ${pending}, ${lt('queue_processing', {}, { locale })}: ${processing})\n`;
|
|
1361
1350
|
|
|
1362
|
-
// Show
|
|
1363
|
-
|
|
1351
|
+
// Show the items this queue is actively processing for this tool, with a
|
|
1352
|
+
// clickable link to each issue/PR (issue #1837). These come from the
|
|
1353
|
+
// queue's own tracking, so they may differ from the pgrep-based count above.
|
|
1354
|
+
const processingItems = Array.from(this.processing.values()).filter(item => item.tool === tool);
|
|
1355
|
+
for (const item of processingItems.slice(0, QUEUE_CONFIG.MAX_DISPLAY_ITEMS_PER_QUEUE)) {
|
|
1356
|
+
const waitTime = formatDuration(item.getWaitTime(), { locale });
|
|
1357
|
+
message += ` ▶️ ${formatQueueItemLink(item.url)} (${queueStatusLabel(item.status, locale)}, ${waitTime})\n`;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// Show first queued items for this tool with clickable links
|
|
1361
|
+
const displayItems = toolQueue.slice(0, QUEUE_CONFIG.MAX_DISPLAY_ITEMS_PER_QUEUE);
|
|
1364
1362
|
for (const item of displayItems) {
|
|
1365
1363
|
const waitTime = formatDuration(item.getWaitTime(), { locale });
|
|
1366
|
-
message += ` • ${item.url} (${queueStatusLabel(item.status, locale)}, ${waitTime})\n`;
|
|
1364
|
+
message += ` • ${formatQueueItemLink(item.url)} (${queueStatusLabel(item.status, locale)}, ${waitTime})\n`;
|
|
1367
1365
|
if (item.waitingReason) {
|
|
1368
1366
|
message += ` └ ${item.waitingReason}\n`;
|
|
1369
1367
|
}
|
|
1370
1368
|
}
|
|
1371
|
-
if (toolQueue.length >
|
|
1372
|
-
message += ` ... ${lt('queue_and_more', { count: toolQueue.length -
|
|
1369
|
+
if (toolQueue.length > QUEUE_CONFIG.MAX_DISPLAY_ITEMS_PER_QUEUE) {
|
|
1370
|
+
message += ` ... ${lt('queue_and_more', { count: toolQueue.length - QUEUE_CONFIG.MAX_DISPLAY_ITEMS_PER_QUEUE }, { locale })}\n`;
|
|
1373
1371
|
}
|
|
1374
1372
|
|
|
1375
1373
|
message += '\n';
|
|
1376
1374
|
}
|
|
1377
1375
|
|
|
1376
|
+
// Completed / Failed lists - clickable links to the executed issues/PRs,
|
|
1377
|
+
// most-recent-first so the newest results are easy to find (issue #1837).
|
|
1378
|
+
const max = QUEUE_CONFIG.MAX_DISPLAY_ITEMS_PER_QUEUE;
|
|
1379
|
+
message += formatQueueHistorySection({ items: this.completed, emoji: '✅', label: lt('queue_completed', {}, { locale }), max, locale });
|
|
1380
|
+
message += formatQueueHistorySection({ items: this.failed, emoji: '❌', label: lt('queue_failed', {}, { locale }), max, locale, withError: true });
|
|
1381
|
+
|
|
1378
1382
|
// Summary stats
|
|
1379
1383
|
message += `${lt('queue_completed', {}, { locale })}: ${stats.completed}, ${lt('queue_failed', {}, { locale })}: ${stats.failed}\n`;
|
|
1380
1384
|
|