@link-assistant/hive-mind 1.4.0 → 1.6.0
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 +12 -0
- package/package.json +1 -1
- package/src/claude.lib.mjs +13 -9
- package/src/claude.prompts.lib.mjs +25 -0
- package/src/solve.config.lib.mjs +5 -0
- package/src/telegram-accept-invitations.lib.mjs +128 -0
- package/src/telegram-bot.mjs +15 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 56d95bd: Add `--prompt-subagents-via-agent-commander` option to guide Claude to use agent-commander CLI for subagent delegation instead of native Task tool. This allows using any supported agent type (claude, opencode, codex, agent) with a unified API and saves main agent context. The prompt guidance is only included when agent-commander (start-agent) is actually installed on the system.
|
|
8
|
+
|
|
9
|
+
## 1.5.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 2d41edb: Add /accept_invites command to Telegram bot for automatically accepting GitHub repository and organization invitations via gh CLI
|
|
14
|
+
|
|
3
15
|
## 1.4.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/package.json
CHANGED
package/src/claude.lib.mjs
CHANGED
|
@@ -25,21 +25,13 @@ const showResumeCommand = async (sessionId, tempDir, claudePath, model, log) =>
|
|
|
25
25
|
await log(` ${cmd}\n`);
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
/**
|
|
29
|
-
* Format numbers with spaces as thousands separator (no commas)
|
|
30
|
-
* Per issue #667: Use spaces for thousands, . for decimals
|
|
31
|
-
* @param {number|null|undefined} num - Number to format
|
|
32
|
-
* @returns {string} Formatted number string
|
|
33
|
-
*/
|
|
28
|
+
/** Format numbers with spaces as thousands separator (no commas) */
|
|
34
29
|
export const formatNumber = num => {
|
|
35
30
|
if (num === null || num === undefined) return 'N/A';
|
|
36
|
-
// Convert to string and split on decimal point
|
|
37
31
|
const parts = num.toString().split('.');
|
|
38
32
|
const integerPart = parts[0];
|
|
39
33
|
const decimalPart = parts[1];
|
|
40
|
-
// Add spaces every 3 digits from the right
|
|
41
34
|
const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
|
|
42
|
-
// Return with decimal part if it exists
|
|
43
35
|
return decimalPart !== undefined ? `${formattedInteger}.${decimalPart}` : formattedInteger;
|
|
44
36
|
};
|
|
45
37
|
// Available model configurations
|
|
@@ -414,6 +406,18 @@ export const checkPlaywrightMcpAvailability = async () => {
|
|
|
414
406
|
*/
|
|
415
407
|
export const executeClaude = async params => {
|
|
416
408
|
const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, setLogFile, getLogFile, formatAligned, getResourceSnapshot, claudePath, $ } = params;
|
|
409
|
+
|
|
410
|
+
// Check if agent-commander is installed when the option is enabled
|
|
411
|
+
if (argv.promptSubagentsViaAgentCommander) {
|
|
412
|
+
try {
|
|
413
|
+
await $`which start-agent`;
|
|
414
|
+
argv.agentCommanderInstalled = true;
|
|
415
|
+
} catch {
|
|
416
|
+
argv.agentCommanderInstalled = false;
|
|
417
|
+
await log('⚠️ agent-commander not installed; prompt guidance will be skipped (npm i -g @link-assistant/agent-commander)');
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
417
421
|
// Import prompt building functions from claude.prompts.lib.mjs
|
|
418
422
|
const { buildUserPrompt, buildSystemPrompt } = await import('./claude.prompts.lib.mjs');
|
|
419
423
|
// Build the user prompt
|
|
@@ -282,6 +282,31 @@ Plan sub-agent usage.
|
|
|
282
282
|
- When using the Plan sub-agent, you can add it as the first item in your todo list.
|
|
283
283
|
- When you delegate planning, use the Task tool with subagent_type="Plan" before starting implementation work.`
|
|
284
284
|
: ''
|
|
285
|
+
}${
|
|
286
|
+
argv && argv.promptSubagentsViaAgentCommander && argv.agentCommanderInstalled
|
|
287
|
+
? `
|
|
288
|
+
|
|
289
|
+
Agent Commander usage (unified subagent delegation).
|
|
290
|
+
- When you need to delegate tasks to subagents, use the agent-commander CLI tool (start-agent) instead of the native Task tool.
|
|
291
|
+
- Agent Commander provides a unified API for different agent types (claude, opencode, codex, agent) and supports various isolation modes.
|
|
292
|
+
- To delegate a task, use the Bash tool to run start-agent with appropriate parameters:
|
|
293
|
+
\`\`\`bash
|
|
294
|
+
start-agent --tool claude --working-directory "$(pwd)" --prompt "Your task description here"
|
|
295
|
+
\`\`\`
|
|
296
|
+
- Common start-agent parameters:
|
|
297
|
+
--tool <name>: Agent to use (claude, opencode, codex, agent)
|
|
298
|
+
--working-directory <path>: Execution directory (use current directory for context)
|
|
299
|
+
--prompt <text>: The task to delegate
|
|
300
|
+
--model <name>: Model to use (sonnet, opus, haiku, grok, etc.)
|
|
301
|
+
--isolation <mode>: Execution context (none, screen, docker)
|
|
302
|
+
--detached: Run in background mode
|
|
303
|
+
- Examples:
|
|
304
|
+
Explore codebase: start-agent --tool claude --working-directory "$(pwd)" --prompt "Explore the codebase structure and find how authentication is implemented"
|
|
305
|
+
Run with specific model: start-agent --tool claude --working-directory "$(pwd)" --model opus --prompt "Review this complex algorithm"
|
|
306
|
+
Use different agent: start-agent --tool opencode --working-directory "$(pwd)" --model grok --prompt "Analyze performance issues"
|
|
307
|
+
- Benefits: Saves main agent context, supports any agent type, provides unified API across different AI tools.
|
|
308
|
+
- Note: The subagent will have access to the same working directory and can read/write files as needed.`
|
|
309
|
+
: ''
|
|
285
310
|
}${ciExamples}${getArchitectureCareSubPrompt(argv)}`;
|
|
286
311
|
};
|
|
287
312
|
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -326,6 +326,11 @@ export const createYargsConfig = yargsInstance => {
|
|
|
326
326
|
description: 'Automatically repair git configuration using gh-setup-git-identity --repair when git identity is not configured. Requires gh-setup-git-identity to be installed.',
|
|
327
327
|
default: false,
|
|
328
328
|
})
|
|
329
|
+
.option('prompt-subagents-via-agent-commander', {
|
|
330
|
+
type: 'boolean',
|
|
331
|
+
description: 'Guide Claude to use agent-commander CLI (start-agent) instead of native Task tool for subagent delegation. Allows using any supported agent type (claude, opencode, codex, agent) with unified API. Only works with --tool claude and requires agent-commander to be installed.',
|
|
332
|
+
default: false,
|
|
333
|
+
})
|
|
329
334
|
.parserConfiguration({
|
|
330
335
|
'boolean-negation': true,
|
|
331
336
|
})
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram /accept_invites command implementation
|
|
3
|
+
*
|
|
4
|
+
* This module provides the /accept_invites command functionality for the Telegram bot,
|
|
5
|
+
* allowing users to accept all pending GitHub repository and organization invitations.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Accepts all pending repository invitations
|
|
9
|
+
* - Accepts all pending organization invitations
|
|
10
|
+
* - Provides detailed feedback on accepted invitations
|
|
11
|
+
* - Error handling with detailed error messages
|
|
12
|
+
*
|
|
13
|
+
* @see https://docs.github.com/en/rest/collaborators/invitations
|
|
14
|
+
* @see https://docs.github.com/en/rest/orgs/members
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { promisify } from 'util';
|
|
18
|
+
import { exec as execCallback } from 'child_process';
|
|
19
|
+
|
|
20
|
+
const exec = promisify(execCallback);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Escapes special characters in text for Telegram Markdown formatting
|
|
24
|
+
* @param {string} text - The text to escape
|
|
25
|
+
* @returns {string} The escaped text
|
|
26
|
+
*/
|
|
27
|
+
function escapeMarkdown(text) {
|
|
28
|
+
return String(text).replace(/[_*[\]()~`>#+\-=|{}.!]/g, '\\$&');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Registers the /accept_invites command handler with the bot
|
|
33
|
+
* @param {Object} bot - The Telegraf bot instance
|
|
34
|
+
* @param {Object} options - Options object
|
|
35
|
+
* @param {boolean} options.VERBOSE - Whether to enable verbose logging
|
|
36
|
+
* @param {Function} options.isOldMessage - Function to check if message is old
|
|
37
|
+
* @param {Function} options.isForwardedOrReply - Function to check if message is forwarded/reply
|
|
38
|
+
* @param {Function} options.isGroupChat - Function to check if chat is a group
|
|
39
|
+
* @param {Function} options.isChatAuthorized - Function to check if chat is authorized
|
|
40
|
+
* @param {Function} options.addBreadcrumb - Function to add breadcrumbs for monitoring
|
|
41
|
+
*/
|
|
42
|
+
export function registerAcceptInvitesCommand(bot, options) {
|
|
43
|
+
const { VERBOSE = false, isOldMessage, isForwardedOrReply, isGroupChat, isChatAuthorized, addBreadcrumb } = options;
|
|
44
|
+
|
|
45
|
+
bot.command(/^accept[_-]?invites$/i, async ctx => {
|
|
46
|
+
VERBOSE && console.log('[VERBOSE] /accept-invites command received');
|
|
47
|
+
await addBreadcrumb({
|
|
48
|
+
category: 'telegram.command',
|
|
49
|
+
message: '/accept-invites command received',
|
|
50
|
+
level: 'info',
|
|
51
|
+
data: { chatId: ctx.chat?.id, chatType: ctx.chat?.type, userId: ctx.from?.id, username: ctx.from?.username },
|
|
52
|
+
});
|
|
53
|
+
if (isOldMessage(ctx) || isForwardedOrReply(ctx)) return;
|
|
54
|
+
if (!isGroupChat(ctx))
|
|
55
|
+
return await ctx.reply('❌ The /accept_invites command only works in group chats. Please add this bot to a group and make it an admin.', {
|
|
56
|
+
reply_to_message_id: ctx.message.message_id,
|
|
57
|
+
});
|
|
58
|
+
const chatId = ctx.chat.id;
|
|
59
|
+
if (!isChatAuthorized(chatId))
|
|
60
|
+
return await ctx.reply(`❌ This chat (ID: ${chatId}) is not authorized to use this bot. Please contact the bot administrator.`, {
|
|
61
|
+
reply_to_message_id: ctx.message.message_id,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const fetchingMessage = await ctx.reply('🔄 Fetching pending GitHub invitations...', { reply_to_message_id: ctx.message.message_id });
|
|
65
|
+
const accepted = [];
|
|
66
|
+
const errors = [];
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
// Fetch repository invitations
|
|
70
|
+
const { stdout: repoInvJson } = await exec('gh api /user/repository_invitations 2>/dev/null || echo "[]"');
|
|
71
|
+
const repoInvitations = JSON.parse(repoInvJson.trim() || '[]');
|
|
72
|
+
VERBOSE && console.log(`[VERBOSE] Found ${repoInvitations.length} pending repo invitations`);
|
|
73
|
+
|
|
74
|
+
// Accept each repo invitation
|
|
75
|
+
for (const inv of repoInvitations) {
|
|
76
|
+
const repoName = inv.repository?.full_name || 'unknown';
|
|
77
|
+
try {
|
|
78
|
+
await exec(`gh api -X PATCH /user/repository_invitations/${inv.id}`);
|
|
79
|
+
accepted.push(`📦 Repository: ${repoName}`);
|
|
80
|
+
VERBOSE && console.log(`[VERBOSE] Accepted repo invitation: ${repoName}`);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
errors.push(`📦 ${repoName}: ${e.message}`);
|
|
83
|
+
VERBOSE && console.log(`[VERBOSE] Failed to accept repo invitation ${repoName}: ${e.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Fetch organization invitations
|
|
88
|
+
const { stdout: orgMemJson } = await exec('gh api /user/memberships/orgs 2>/dev/null || echo "[]"');
|
|
89
|
+
const orgMemberships = JSON.parse(orgMemJson.trim() || '[]');
|
|
90
|
+
const pendingOrgs = orgMemberships.filter(m => m.state === 'pending');
|
|
91
|
+
VERBOSE && console.log(`[VERBOSE] Found ${pendingOrgs.length} pending org invitations`);
|
|
92
|
+
|
|
93
|
+
// Accept each org invitation
|
|
94
|
+
for (const membership of pendingOrgs) {
|
|
95
|
+
const orgName = membership.organization?.login || 'unknown';
|
|
96
|
+
try {
|
|
97
|
+
await exec(`gh api -X PATCH /user/memberships/orgs/${orgName} -f state=active`);
|
|
98
|
+
accepted.push(`🏢 Organization: ${orgName}`);
|
|
99
|
+
VERBOSE && console.log(`[VERBOSE] Accepted org invitation: ${orgName}`);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
errors.push(`🏢 ${orgName}: ${e.message}`);
|
|
102
|
+
VERBOSE && console.log(`[VERBOSE] Failed to accept org invitation ${orgName}: ${e.message}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Build response message
|
|
107
|
+
let message = '✅ *GitHub Invitations Processed*\n\n';
|
|
108
|
+
if (accepted.length === 0 && errors.length === 0) {
|
|
109
|
+
message += 'No pending invitations found.';
|
|
110
|
+
} else {
|
|
111
|
+
if (accepted.length > 0) {
|
|
112
|
+
message += '*Accepted:*\n' + accepted.map(a => ` • ${escapeMarkdown(a)}`).join('\n') + '\n\n';
|
|
113
|
+
}
|
|
114
|
+
if (errors.length > 0) {
|
|
115
|
+
message += '*Errors:*\n' + errors.map(e => ` • ${escapeMarkdown(e)}`).join('\n');
|
|
116
|
+
}
|
|
117
|
+
if (accepted.length > 0 && errors.length === 0) {
|
|
118
|
+
message += `\n🎉 Successfully accepted ${accepted.length} invitation(s)!`;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, message, { parse_mode: 'Markdown' });
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error('Error in /accept-invites:', error);
|
|
125
|
+
await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, `❌ Error fetching invitations: ${escapeMarkdown(error.message)}\n\nMake sure \`gh\` CLI is installed and authenticated.`, { parse_mode: 'Markdown' });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -761,8 +761,9 @@ bot.command('help', async ctx => {
|
|
|
761
761
|
|
|
762
762
|
message += '*/limits* - Show usage limits\n';
|
|
763
763
|
message += '*/version* - Show bot and runtime versions\n';
|
|
764
|
+
message += '*/accept\\_invites* - Accept all pending GitHub invitations\n';
|
|
764
765
|
message += '*/help* - Show this help message\n\n';
|
|
765
|
-
message += '⚠️ *Note:* /solve, /hive, /limits and /
|
|
766
|
+
message += '⚠️ *Note:* /solve, /hive, /limits, /version and /accept\\_invites commands only work in group chats.\n\n';
|
|
766
767
|
message += '🔧 *Common Options:*\n';
|
|
767
768
|
message += '• `--model <model>` or `-m` - Specify AI model (sonnet, opus, haiku, haiku-3-5, haiku-3)\n';
|
|
768
769
|
message += '• `--base-branch <branch>` or `-b` - Target branch for PR (default: repo default branch)\n';
|
|
@@ -883,6 +884,19 @@ bot.command('version', async ctx => {
|
|
|
883
884
|
if (!result.success) return await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, `❌ ${escapeMarkdownV2(result.error, { preserveCodeBlocks: true })}`, { parse_mode: 'MarkdownV2' });
|
|
884
885
|
await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, '🤖 *Version Information*\n\n' + formatVersionMessage(result.versions), { parse_mode: 'Markdown' });
|
|
885
886
|
});
|
|
887
|
+
|
|
888
|
+
// Register /accept_invites command from separate module
|
|
889
|
+
// This keeps telegram-bot.mjs under the 1500 line limit
|
|
890
|
+
const { registerAcceptInvitesCommand } = await import('./telegram-accept-invitations.lib.mjs');
|
|
891
|
+
registerAcceptInvitesCommand(bot, {
|
|
892
|
+
VERBOSE,
|
|
893
|
+
isOldMessage,
|
|
894
|
+
isForwardedOrReply,
|
|
895
|
+
isGroupChat,
|
|
896
|
+
isChatAuthorized,
|
|
897
|
+
addBreadcrumb,
|
|
898
|
+
});
|
|
899
|
+
|
|
886
900
|
bot.command(/^solve$/i, async ctx => {
|
|
887
901
|
if (VERBOSE) {
|
|
888
902
|
console.log('[VERBOSE] /solve command received');
|