@link-assistant/hive-mind 1.23.13 → 1.24.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 CHANGED
@@ -1,5 +1,28 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.24.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c93b8cd: Add support for Claude Sonnet 4.6 and set it as the default model for `--tool claude`
8
+ - Added `claude-sonnet-4-6` as the new default model when using `sonnet` alias
9
+ - Added `sonnet-4-6` short alias for explicit Sonnet 4.6 selection
10
+ - Added backward compatibility aliases: `sonnet-4-5` and `claude-sonnet-4-5` for Sonnet 4.5
11
+ - Added 1M token context window support for Sonnet 4.6 (`sonnet[1m]`, `sonnet-4-6[1m]`)
12
+ - Maintained full backward compatibility with previous model versions
13
+
14
+ ## 1.23.14
15
+
16
+ ### Patch Changes
17
+
18
+ - 069d437: Parallelize version gathering with Promise.all for 6-30x performance improvement
19
+ - Replaced sequential `execSync` calls with parallel `execAsync` using `Promise.all`
20
+ - Reduced execution time from 30-150s to ~2-5s for version info gathering
21
+ - Added support for all `--tool` options: agent, codex, opencode, qwen-code, gemini, copilot
22
+ - Reorganized Telegram output to group tools by programming language instead of generic categories
23
+ - Consolidated hive-mind version display to show single version with restart warning when process version differs from installed
24
+ - Added `gatherTimeMs` metric to track performance
25
+
3
26
  ## 1.23.13
4
27
 
5
28
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.23.13",
3
+ "version": "1.24.0",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -6,22 +6,24 @@
6
6
  */
7
7
 
8
8
  // Claude models (Anthropic API)
9
- // Updated for Opus 4.5/4.6 support (Issue #1221, Issue #1238)
9
+ // Updated for Opus 4.5/4.6 and Sonnet 4.6 support (Issue #1221, Issue #1238, Issue #1329)
10
10
  export const claudeModels = {
11
- sonnet: 'claude-sonnet-4-5-20250929', // Sonnet 4.5
12
- opus: 'claude-opus-4-5-20251101', // Opus 4.5 (default, Issue #1238)
11
+ sonnet: 'claude-sonnet-4-6', // Sonnet 4.6 (default, Issue #1329)
12
+ opus: 'claude-opus-4-5-20251101', // Opus 4.5 (Issue #1238)
13
13
  haiku: 'claude-haiku-4-5-20251001', // Haiku 4.5
14
14
  'haiku-3-5': 'claude-3-5-haiku-20241022', // Haiku 3.5
15
15
  'haiku-3': 'claude-3-haiku-20240307', // Haiku 3
16
- // Shorter version aliases (Issue #1221 - PR comment feedback)
16
+ // Shorter version aliases (Issue #1221, Issue #1329 - PR comment feedback)
17
+ 'sonnet-4-6': 'claude-sonnet-4-6', // Sonnet 4.6 short alias (Issue #1329)
17
18
  'opus-4-6': 'claude-opus-4-6', // Opus 4.6 short alias
18
19
  'opus-4-5': 'claude-opus-4-5-20251101', // Opus 4.5 short alias
19
- 'sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5 short alias
20
+ 'sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5 short alias (backward compatibility)
20
21
  'haiku-4-5': 'claude-haiku-4-5-20251001', // Haiku 4.5 short alias
21
- // Version aliases for backward compatibility (Issue #1221)
22
+ // Version aliases for backward compatibility (Issue #1221, Issue #1329)
23
+ 'claude-sonnet-4-6': 'claude-sonnet-4-6', // Sonnet 4.6 (Issue #1329)
22
24
  'claude-opus-4-6': 'claude-opus-4-6', // Opus 4.6
23
25
  'claude-opus-4-5': 'claude-opus-4-5-20251101', // Opus 4.5
24
- 'claude-sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5
26
+ 'claude-sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5 (backward compatibility)
25
27
  'claude-haiku-4-5': 'claude-haiku-4-5-20251001', // Haiku 4.5
26
28
  };
27
29
 
@@ -14,21 +14,23 @@ import { log } from './lib.mjs';
14
14
  // These are the "known good" model names that we accept
15
15
  export const CLAUDE_MODELS = {
16
16
  // Short aliases (single word)
17
- sonnet: 'claude-sonnet-4-5-20250929',
18
- opus: 'claude-opus-4-5-20251101', // Changed to Opus 4.5 (Issue #1238)
17
+ sonnet: 'claude-sonnet-4-6', // Sonnet 4.6 (default, Issue #1329)
18
+ opus: 'claude-opus-4-5-20251101', // Opus 4.5 (Issue #1238)
19
19
  haiku: 'claude-haiku-4-5-20251001',
20
20
  'haiku-3-5': 'claude-3-5-haiku-20241022',
21
21
  'haiku-3': 'claude-3-haiku-20240307',
22
- // Shorter version aliases (Issue #1221 - PR comment feedback)
22
+ // Shorter version aliases (Issue #1221, Issue #1329 - PR comment feedback)
23
+ 'sonnet-4-6': 'claude-sonnet-4-6', // Sonnet 4.6 short alias (Issue #1329)
23
24
  'opus-4-6': 'claude-opus-4-6', // Opus 4.6 short alias
24
25
  'opus-4-5': 'claude-opus-4-5-20251101', // Opus 4.5 short alias
25
- 'sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5 short alias
26
+ 'sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5 short alias (backward compatibility)
26
27
  'haiku-4-5': 'claude-haiku-4-5-20251001', // Haiku 4.5 short alias
28
+ // Sonnet version aliases (Issue #1329)
29
+ 'claude-sonnet-4-6': 'claude-sonnet-4-6', // Sonnet 4.6
30
+ 'claude-sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5 (backward compatibility)
27
31
  // Opus version aliases (Issue #1221)
28
32
  'claude-opus-4-6': 'claude-opus-4-6', // Opus 4.6
29
33
  'claude-opus-4-5': 'claude-opus-4-5-20251101', // Opus 4.5
30
- // Sonnet version aliases
31
- 'claude-sonnet-4-5': 'claude-sonnet-4-5-20250929',
32
34
  // Haiku version aliases
33
35
  'claude-haiku-4-5': 'claude-haiku-4-5-20251001',
34
36
  // Full model IDs (also valid inputs)
@@ -39,14 +41,16 @@ export const CLAUDE_MODELS = {
39
41
  'claude-3-haiku-20240307': 'claude-3-haiku-20240307',
40
42
  };
41
43
 
42
- // Models that support 1M token context window via [1m] suffix (Issue #1221, Issue #1238)
44
+ // Models that support 1M token context window via [1m] suffix (Issue #1221, Issue #1238, Issue #1329)
43
45
  // See: https://code.claude.com/docs/en/model-config
44
46
  export const MODELS_SUPPORTING_1M_CONTEXT = [
45
47
  'claude-opus-4-6',
46
48
  'claude-opus-4-5-20251101',
49
+ 'claude-sonnet-4-6', // Sonnet 4.6 (Issue #1329)
47
50
  'claude-sonnet-4-5-20250929',
48
51
  'claude-sonnet-4-5',
49
- 'sonnet',
52
+ 'sonnet', // Now maps to Sonnet 4.6 (Issue #1329)
53
+ 'sonnet-4-6', // Short alias (Issue #1329)
50
54
  'opus',
51
55
  'opus-4-6', // Short alias (Issue #1221 - PR comment feedback)
52
56
  'opus-4-5', // Short alias (Issue #1238)
@@ -2,42 +2,152 @@
2
2
 
3
3
  // Version information library for hive-mind project
4
4
  // Provides comprehensive version information for bot, commands, and runtime
5
+ //
6
+ // Performance optimization (issue #1320):
7
+ // Uses Promise.all for parallel command execution instead of sequential execSync.
8
+ // This reduces version gathering time from ~30-150s to ~5s (limited by slowest command).
5
9
 
6
10
  import { getVersion } from './version.lib.mjs';
7
- import { execSync } from 'child_process';
11
+ import { exec } from 'child_process';
12
+ import { promisify } from 'util';
13
+
14
+ const execAsync = promisify(exec);
8
15
 
9
16
  /**
10
- * Execute a command and return its output, or null if it fails
17
+ * Execute a command asynchronously and return its output, or null if it fails
11
18
  * @param {string} command - Command to execute
12
- * @param {boolean} verbose - Enable verbose logging
13
- * @returns {string|null} Command output or null
19
+ * @param {number} timeout - Timeout in milliseconds (default: 5000ms)
20
+ * @returns {Promise<string|null>} Command output or null
14
21
  */
15
- function execCommand(command, verbose = false) {
22
+ async function execCommandAsync(command, timeout = 5000) {
16
23
  try {
17
- const result = execSync(command, { encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'ignore'] });
18
- const trimmed = result.trim();
24
+ const { stdout } = await execAsync(command, { timeout, maxBuffer: 1024 * 1024 });
25
+ const trimmed = stdout.trim();
19
26
  // Return null if the output looks like an error message
20
27
  if (trimmed.includes('not found') || trimmed.includes('command not found') || trimmed === '') {
21
28
  return null;
22
29
  }
23
30
  return trimmed;
24
- } catch (error) {
25
- if (verbose) {
26
- console.log(`[VERBOSE] Command failed: ${command}`, error.message);
27
- }
31
+ } catch {
28
32
  return null;
29
33
  }
30
34
  }
31
35
 
36
+ /**
37
+ * Command definitions for version checking
38
+ * Each entry has: key, command, and optional fallbacks
39
+ * @type {Array<{key: string, command: string, fallbacks?: string[]}>}
40
+ */
41
+ const VERSION_COMMANDS = [
42
+ // AI Agents and Tools (--tool options)
43
+ { key: 'claudeCode', command: 'claude --version 2>&1' },
44
+ { key: 'agent', command: 'agent --version 2>&1' },
45
+ { key: 'codex', command: 'codex --version 2>&1' },
46
+ { key: 'opencode', command: 'opencode --version 2>&1' },
47
+ { key: 'qwenCode', command: 'qwen-code --version 2>&1' },
48
+ { key: 'gemini', command: 'gemini --version 2>&1' },
49
+ { key: 'copilot', command: 'copilot --version 2>&1' },
50
+
51
+ // Browser Automation
52
+ { key: 'playwright', command: 'playwright --version 2>&1' },
53
+ { key: 'playwrightMcp', command: "npm list -g @playwright/mcp --depth=0 2>&1 | grep @playwright/mcp | awk '{print $2}'" },
54
+
55
+ // JavaScript/Node.js ecosystem
56
+ { key: 'bun', command: 'bun --version 2>&1' },
57
+ { key: 'deno', command: 'deno --version 2>&1 | head -n1' },
58
+ { key: 'npm', command: 'npm --version 2>&1' },
59
+ { key: 'nvm', command: 'nvm --version 2>&1' },
60
+
61
+ // Python ecosystem
62
+ { key: 'python', command: 'python --version 2>&1' },
63
+ { key: 'pyenv', command: 'pyenv --version 2>&1' },
64
+
65
+ // Rust ecosystem
66
+ { key: 'rust', command: 'rustc --version 2>&1' },
67
+ { key: 'cargo', command: 'cargo --version 2>&1' },
68
+
69
+ // Java ecosystem
70
+ { key: 'java', command: 'java -version 2>&1 | head -n1' },
71
+ { key: 'sdkman', command: "sdk version 2>&1 | grep -oE '[0-9]+\\.[0-9]+\\.[0-9]+'" },
72
+
73
+ // Go
74
+ { key: 'go', command: 'go version 2>&1' },
75
+
76
+ // PHP
77
+ { key: 'php', command: 'php --version 2>&1 | head -n1' },
78
+
79
+ // .NET
80
+ { key: 'dotnet', command: 'dotnet --version 2>&1' },
81
+
82
+ // Perl ecosystem
83
+ { key: 'perl', command: "perl -v 2>&1 | grep -oE 'v[0-9]+\\.[0-9]+\\.[0-9]+'" },
84
+ { key: 'perlbrew', command: 'perlbrew --version 2>&1' },
85
+
86
+ // OCaml/Rocq ecosystem
87
+ { key: 'ocaml', command: 'ocaml --version 2>&1' },
88
+ { key: 'opam', command: 'opam --version 2>&1' },
89
+ // Rocq has fallback commands (rocq -> rocqc -> coqc)
90
+ { key: 'rocq', command: 'rocq -v 2>&1 | head -n1', fallbacks: ['rocqc --version 2>&1 | head -n1', 'coqc --version 2>&1 | head -n1'] },
91
+
92
+ // Lean ecosystem
93
+ { key: 'lean', command: 'lean --version 2>&1' },
94
+ { key: 'elan', command: 'elan --version 2>&1' },
95
+ { key: 'lake', command: 'lake --version 2>&1' },
96
+
97
+ // C/C++ Development Tools
98
+ { key: 'gcc', command: 'gcc --version 2>&1 | head -n1' },
99
+ { key: 'gpp', command: 'g++ --version 2>&1 | head -n1' },
100
+ { key: 'clang', command: 'clang --version 2>&1 | head -n1' },
101
+ { key: 'llvm', command: 'llvm-config --version 2>&1' },
102
+ { key: 'lld', command: 'lld --version 2>&1 | head -n1' },
103
+ { key: 'make', command: 'make --version 2>&1 | head -n1' },
104
+ { key: 'cmake', command: 'cmake --version 2>&1 | head -n1' },
105
+
106
+ // Development Tools
107
+ { key: 'git', command: 'git --version 2>&1' },
108
+ { key: 'gh', command: 'gh --version 2>&1 | head -n1' },
109
+ { key: 'brew', command: 'brew --version 2>&1 | head -n1' },
110
+ ];
111
+
112
+ /**
113
+ * Execute a version command with optional fallbacks
114
+ * @param {{key: string, command: string, fallbacks?: string[]}} cmdDef - Command definition
115
+ * @param {boolean} verbose - Enable verbose logging
116
+ * @returns {Promise<{key: string, value: string|null}>}
117
+ */
118
+ async function executeVersionCommand(cmdDef, verbose) {
119
+ let result = await execCommandAsync(cmdDef.command);
120
+
121
+ // Try fallbacks if primary command failed
122
+ if (!result && cmdDef.fallbacks) {
123
+ for (const fallback of cmdDef.fallbacks) {
124
+ result = await execCommandAsync(fallback);
125
+ if (result) break;
126
+ }
127
+ }
128
+
129
+ if (verbose && result) {
130
+ console.log(`[VERBOSE] ${cmdDef.key}: ${result}`);
131
+ } else if (verbose && !result) {
132
+ console.log(`[VERBOSE] ${cmdDef.key}: not found`);
133
+ }
134
+
135
+ return { key: cmdDef.key, value: result };
136
+ }
137
+
32
138
  /**
33
139
  * Get comprehensive version information for all components
140
+ * Uses Promise.all for parallel execution (issue #1320)
34
141
  * @param {boolean} verbose - Enable verbose logging
142
+ * @param {string} [processVersion] - Optional: version from the running process (for restart warning)
35
143
  * @returns {Promise<Object>} Version information object
36
144
  */
37
- export async function getVersionInfo(verbose = false) {
145
+ export async function getVersionInfo(verbose = false, processVersion = null) {
146
+ const startTime = Date.now();
147
+
38
148
  try {
39
149
  if (verbose) {
40
- console.log('[VERBOSE] Gathering version information...');
150
+ console.log('[VERBOSE] Gathering version information (parallel execution)...');
41
151
  }
42
152
 
43
153
  // Get hive-mind package version
@@ -46,297 +156,120 @@ export async function getVersionInfo(verbose = false) {
46
156
  console.log(`[VERBOSE] Package version: ${packageVersion}`);
47
157
  }
48
158
 
49
- // === Agents ===
159
+ // Execute all version commands in parallel
160
+ const results = await Promise.all(VERSION_COMMANDS.map(cmd => executeVersionCommand(cmd, verbose)));
50
161
 
51
- // Claude Code
52
- const claudeVersion = execCommand('claude --version 2>&1', verbose);
53
- if (verbose && claudeVersion) {
54
- console.log(`[VERBOSE] Claude Code version: ${claudeVersion}`);
162
+ // Convert results array to object
163
+ const versions = {};
164
+ for (const { key, value } of results) {
165
+ versions[key] = value;
55
166
  }
56
167
 
57
- // Playwright
58
- const playwrightVersion = execCommand('playwright --version 2>&1', verbose);
59
- if (verbose && playwrightVersion) {
60
- console.log(`[VERBOSE] Playwright version: ${playwrightVersion}`);
61
- }
62
-
63
- // Playwright MCP (check if installed via npm)
64
- const playwrightMcpVersion = execCommand("npm list -g @playwright/mcp --depth=0 2>&1 | grep @playwright/mcp | awk '{print $2}'", verbose);
65
- if (verbose && playwrightMcpVersion) {
66
- console.log(`[VERBOSE] Playwright MCP version: ${playwrightMcpVersion}`);
67
- }
68
-
69
- // === Language Runtimes ===
70
-
71
- // Node.js (from process, always available)
72
- const nodeVersion = process.version;
168
+ // Add Node.js version (always available from process)
169
+ versions.node = process.version;
73
170
  if (verbose) {
74
- console.log(`[VERBOSE] Node.js version: ${nodeVersion}`);
75
- }
76
-
77
- // Python
78
- const pythonVersion = execCommand('python --version 2>&1', verbose);
79
- if (verbose && pythonVersion) {
80
- console.log(`[VERBOSE] Python version: ${pythonVersion}`);
81
- }
82
-
83
- // Pyenv
84
- const pyenvVersion = execCommand('pyenv --version 2>&1', verbose);
85
- if (verbose && pyenvVersion) {
86
- console.log(`[VERBOSE] Pyenv version: ${pyenvVersion}`);
87
- }
88
-
89
- // Rust
90
- const rustVersion = execCommand('rustc --version 2>&1', verbose);
91
- if (verbose && rustVersion) {
92
- console.log(`[VERBOSE] Rust version: ${rustVersion}`);
93
- }
94
-
95
- // Cargo
96
- const cargoVersion = execCommand('cargo --version 2>&1', verbose);
97
- if (verbose && cargoVersion) {
98
- console.log(`[VERBOSE] Cargo version: ${cargoVersion}`);
99
- }
100
-
101
- // PHP
102
- const phpVersion = execCommand('php --version 2>&1 | head -n1', verbose);
103
- if (verbose && phpVersion) {
104
- console.log(`[VERBOSE] PHP version: ${phpVersion}`);
105
- }
106
-
107
- // Bun
108
- const bunVersion = execCommand('bun --version 2>&1', verbose);
109
- if (verbose && bunVersion) {
110
- console.log(`[VERBOSE] Bun version: ${bunVersion}`);
111
- }
112
-
113
- // .NET
114
- const dotnetVersion = execCommand('dotnet --version 2>&1', verbose);
115
- if (verbose && dotnetVersion) {
116
- console.log(`[VERBOSE] .NET version: ${dotnetVersion}`);
117
- }
118
-
119
- // Deno
120
- const denoVersion = execCommand('deno --version 2>&1 | head -n1', verbose);
121
- if (verbose && denoVersion) {
122
- console.log(`[VERBOSE] Deno version: ${denoVersion}`);
123
- }
124
-
125
- // Go (Golang)
126
- const goVersion = execCommand('go version 2>&1', verbose);
127
- if (verbose && goVersion) {
128
- console.log(`[VERBOSE] Go version: ${goVersion}`);
129
- }
130
-
131
- // Java
132
- const javaVersion = execCommand('java -version 2>&1 | head -n1', verbose);
133
- if (verbose && javaVersion) {
134
- console.log(`[VERBOSE] Java version: ${javaVersion}`);
135
- }
136
-
137
- // Lean (theorem prover)
138
- const leanVersion = execCommand('lean --version 2>&1', verbose);
139
- if (verbose && leanVersion) {
140
- console.log(`[VERBOSE] Lean version: ${leanVersion}`);
141
- }
142
-
143
- // Perl
144
- const perlVersion = execCommand("perl -v 2>&1 | grep -oE 'v[0-9]+\\.[0-9]+\\.[0-9]+'", verbose);
145
- if (verbose && perlVersion) {
146
- console.log(`[VERBOSE] Perl version: ${perlVersion}`);
147
- }
148
-
149
- // OCaml (via opam)
150
- const ocamlVersion = execCommand('ocaml --version 2>&1', verbose);
151
- if (verbose && ocamlVersion) {
152
- console.log(`[VERBOSE] OCaml version: ${ocamlVersion}`);
153
- }
154
-
155
- // Rocq/Coq (theorem prover)
156
- // Try rocq first (Rocq 9.0+), then fall back to coqc (legacy)
157
- let rocqVersion = execCommand('rocq -v 2>&1 | head -n1', verbose);
158
- if (!rocqVersion) {
159
- rocqVersion = execCommand('rocqc --version 2>&1 | head -n1', verbose);
160
- }
161
- if (!rocqVersion) {
162
- rocqVersion = execCommand('coqc --version 2>&1 | head -n1', verbose);
163
- }
164
- if (verbose && rocqVersion) {
165
- console.log(`[VERBOSE] Rocq/Coq version: ${rocqVersion}`);
166
- }
167
-
168
- // === Development Tools ===
169
-
170
- // Git
171
- const gitVersion = execCommand('git --version 2>&1', verbose);
172
- if (verbose && gitVersion) {
173
- console.log(`[VERBOSE] Git version: ${gitVersion}`);
174
- }
175
-
176
- // GitHub CLI
177
- const ghVersion = execCommand('gh --version 2>&1 | head -n1', verbose);
178
- if (verbose && ghVersion) {
179
- console.log(`[VERBOSE] GitHub CLI version: ${ghVersion}`);
180
- }
181
-
182
- // NVM
183
- const nvmVersion = execCommand('nvm --version 2>&1', verbose);
184
- if (verbose && nvmVersion) {
185
- console.log(`[VERBOSE] NVM version: ${nvmVersion}`);
186
- }
187
-
188
- // Homebrew
189
- const brewVersion = execCommand('brew --version 2>&1 | head -n1', verbose);
190
- if (verbose && brewVersion) {
191
- console.log(`[VERBOSE] Homebrew version: ${brewVersion}`);
192
- }
193
-
194
- // NPM
195
- const npmVersion = execCommand('npm --version 2>&1', verbose);
196
- if (verbose && npmVersion) {
197
- console.log(`[VERBOSE] NPM version: ${npmVersion}`);
198
- }
199
-
200
- // SDKMAN (Java version manager)
201
- const sdkmanVersion = execCommand("sdk version 2>&1 | grep -oE '[0-9]+\\.[0-9]+\\.[0-9]+'", verbose);
202
- if (verbose && sdkmanVersion) {
203
- console.log(`[VERBOSE] SDKMAN version: ${sdkmanVersion}`);
204
- }
205
-
206
- // Elan (Lean version manager)
207
- const elanVersion = execCommand('elan --version 2>&1', verbose);
208
- if (verbose && elanVersion) {
209
- console.log(`[VERBOSE] Elan version: ${elanVersion}`);
210
- }
211
-
212
- // Lake (Lean package manager)
213
- const lakeVersion = execCommand('lake --version 2>&1', verbose);
214
- if (verbose && lakeVersion) {
215
- console.log(`[VERBOSE] Lake version: ${lakeVersion}`);
216
- }
217
-
218
- // Perlbrew (Perl version manager)
219
- const perlbrewVersion = execCommand('perlbrew --version 2>&1', verbose);
220
- if (verbose && perlbrewVersion) {
221
- console.log(`[VERBOSE] Perlbrew version: ${perlbrewVersion}`);
222
- }
223
-
224
- // Opam (OCaml package manager)
225
- const opamVersion = execCommand('opam --version 2>&1', verbose);
226
- if (verbose && opamVersion) {
227
- console.log(`[VERBOSE] Opam version: ${opamVersion}`);
228
- }
229
-
230
- // === C/C++ Development Tools ===
231
-
232
- // Make
233
- const makeVersion = execCommand('make --version 2>&1 | head -n1', verbose);
234
- if (verbose && makeVersion) {
235
- console.log(`[VERBOSE] Make version: ${makeVersion}`);
236
- }
237
-
238
- // CMake
239
- const cmakeVersion = execCommand('cmake --version 2>&1 | head -n1', verbose);
240
- if (verbose && cmakeVersion) {
241
- console.log(`[VERBOSE] CMake version: ${cmakeVersion}`);
242
- }
243
-
244
- // GCC
245
- const gccVersion = execCommand('gcc --version 2>&1 | head -n1', verbose);
246
- if (verbose && gccVersion) {
247
- console.log(`[VERBOSE] GCC version: ${gccVersion}`);
248
- }
249
-
250
- // G++
251
- const gppVersion = execCommand('g++ --version 2>&1 | head -n1', verbose);
252
- if (verbose && gppVersion) {
253
- console.log(`[VERBOSE] G++ version: ${gppVersion}`);
254
- }
255
-
256
- // Clang
257
- const clangVersion = execCommand('clang --version 2>&1 | head -n1', verbose);
258
- if (verbose && clangVersion) {
259
- console.log(`[VERBOSE] Clang version: ${clangVersion}`);
260
- }
261
-
262
- // LLVM
263
- const llvmVersion = execCommand('llvm-config --version 2>&1', verbose);
264
- if (verbose && llvmVersion) {
265
- console.log(`[VERBOSE] LLVM version: ${llvmVersion}`);
266
- }
267
-
268
- // LLD (LLVM linker)
269
- const lldVersion = execCommand('lld --version 2>&1 | head -n1', verbose);
270
- if (verbose && lldVersion) {
271
- console.log(`[VERBOSE] LLD version: ${lldVersion}`);
171
+ console.log(`[VERBOSE] Node.js version: ${versions.node}`);
272
172
  }
273
173
 
274
- // === Platform Information ===
174
+ // Platform information
275
175
  const platform = process.platform;
276
176
  const arch = process.arch;
177
+ versions.platform = `${platform} (${arch})`;
277
178
  if (verbose) {
278
- console.log(`[VERBOSE] Platform: ${platform} (${arch})`);
179
+ console.log(`[VERBOSE] Platform: ${versions.platform}`);
279
180
  }
280
181
 
182
+ // Check if process version differs from installed version (restart warning)
183
+ const needsRestart = processVersion && processVersion !== packageVersion;
184
+
281
185
  // Build version info object
282
186
  const versionInfo = {
283
187
  success: true,
284
188
  versions: {
285
- // Bot components
286
- bot: packageVersion,
287
- solve: packageVersion,
288
- hive: packageVersion,
289
-
290
- // Agents
291
- claudeCode: claudeVersion,
292
- playwright: playwrightVersion,
293
- playwrightMcp: playwrightMcpVersion,
294
-
295
- // Language runtimes
296
- node: nodeVersion,
297
- python: pythonVersion,
298
- rust: rustVersion,
299
- php: phpVersion,
300
- bun: bunVersion,
301
- dotnet: dotnetVersion,
302
- deno: denoVersion,
303
- go: goVersion,
304
- java: javaVersion,
305
- lean: leanVersion,
306
- perl: perlVersion,
307
- ocaml: ocamlVersion,
308
- rocq: rocqVersion,
309
-
310
- // Development tools
311
- git: gitVersion,
312
- gh: ghVersion,
313
- npm: npmVersion,
314
- nvm: nvmVersion,
315
- pyenv: pyenvVersion,
316
- cargo: cargoVersion,
317
- brew: brewVersion,
318
- sdkman: sdkmanVersion,
319
- elan: elanVersion,
320
- lake: lakeVersion,
321
- perlbrew: perlbrewVersion,
322
- opam: opamVersion,
323
-
324
- // C/C++ Development Tools
325
- make: makeVersion,
326
- cmake: cmakeVersion,
327
- gcc: gccVersion,
328
- gpp: gppVersion,
329
- clang: clangVersion,
330
- llvm: llvmVersion,
331
- lld: lldVersion,
189
+ // Hive-mind package (single entry, not duplicated)
190
+ hiveMind: packageVersion,
191
+ processVersion: processVersion || packageVersion,
192
+ needsRestart,
193
+
194
+ // AI Agents (--tool options)
195
+ claudeCode: versions.claudeCode,
196
+ agent: versions.agent,
197
+ codex: versions.codex,
198
+ opencode: versions.opencode,
199
+ qwenCode: versions.qwenCode,
200
+ gemini: versions.gemini,
201
+ copilot: versions.copilot,
202
+
203
+ // Browser Automation
204
+ playwright: versions.playwright,
205
+ playwrightMcp: versions.playwrightMcp,
206
+
207
+ // JavaScript/Node.js
208
+ node: versions.node,
209
+ bun: versions.bun,
210
+ deno: versions.deno,
211
+ npm: versions.npm,
212
+ nvm: versions.nvm,
213
+
214
+ // Python
215
+ python: versions.python,
216
+ pyenv: versions.pyenv,
217
+
218
+ // Rust
219
+ rust: versions.rust,
220
+ cargo: versions.cargo,
221
+
222
+ // Java
223
+ java: versions.java,
224
+ sdkman: versions.sdkman,
225
+
226
+ // Go
227
+ go: versions.go,
228
+
229
+ // PHP
230
+ php: versions.php,
231
+
232
+ // .NET
233
+ dotnet: versions.dotnet,
234
+
235
+ // Perl
236
+ perl: versions.perl,
237
+ perlbrew: versions.perlbrew,
238
+
239
+ // OCaml/Rocq
240
+ ocaml: versions.ocaml,
241
+ opam: versions.opam,
242
+ rocq: versions.rocq,
243
+
244
+ // Lean
245
+ lean: versions.lean,
246
+ elan: versions.elan,
247
+ lake: versions.lake,
248
+
249
+ // C/C++
250
+ gcc: versions.gcc,
251
+ gpp: versions.gpp,
252
+ clang: versions.clang,
253
+ llvm: versions.llvm,
254
+ lld: versions.lld,
255
+ make: versions.make,
256
+ cmake: versions.cmake,
257
+
258
+ // Development Tools
259
+ git: versions.git,
260
+ gh: versions.gh,
261
+ brew: versions.brew,
332
262
 
333
263
  // Platform
334
- platform: `${platform} (${arch})`,
264
+ platform: versions.platform,
335
265
  },
266
+ // Performance metrics
267
+ gatherTimeMs: Date.now() - startTime,
336
268
  };
337
269
 
338
270
  if (verbose) {
339
- console.log('[VERBOSE] Version info gathered successfully:', JSON.stringify(versionInfo, null, 2));
271
+ console.log(`[VERBOSE] Version info gathered in ${versionInfo.gatherTimeMs}ms`);
272
+ console.log('[VERBOSE] Version info:', JSON.stringify(versionInfo, null, 2));
340
273
  }
341
274
 
342
275
  return versionInfo;
@@ -348,134 +281,183 @@ export async function getVersionInfo(verbose = false) {
348
281
  return {
349
282
  success: false,
350
283
  error: error.message || 'Failed to gather version information',
284
+ gatherTimeMs: Date.now() - startTime,
351
285
  };
352
286
  }
353
287
  }
354
288
 
289
+ /**
290
+ * Helper to add version line if version exists
291
+ * @param {string[]} lines - Array to push to
292
+ * @param {string} label - Display label
293
+ * @param {string|null} version - Version string or null
294
+ */
295
+ function addVersionLine(lines, label, version) {
296
+ if (version) {
297
+ lines.push(`• ${label}: \`${version}\``);
298
+ }
299
+ }
300
+
355
301
  /**
356
302
  * Format version information as a Telegram message
303
+ * Groups tools by programming language for better readability (issue #1320)
357
304
  * @param {Object} versions - Version information object
358
305
  * @returns {string} Formatted message
359
306
  */
360
307
  export function formatVersionMessage(versions) {
361
308
  const lines = [];
362
309
 
363
- // === Bot Components ===
364
- lines.push('*🤖 Bot Components*');
365
- if (versions.bot) {
366
- lines.push(`• Bot: \`${versions.bot}\``);
367
- }
368
- if (versions.solve) {
369
- lines.push(`• solve: \`${versions.solve}\``);
370
- }
371
- if (versions.hive) {
372
- lines.push(`• hive: \`${versions.hive}\``);
310
+ // === Hive-Mind Package (single entry with restart warning) ===
311
+ lines.push('*🤖 Hive-Mind*');
312
+ if (versions.hiveMind) {
313
+ lines.push(`• Version: \`${versions.hiveMind}\``);
314
+ if (versions.needsRestart) {
315
+ lines.push(`⚠️ _Process running: \`${versions.processVersion}\` (restart needed)_`);
316
+ }
373
317
  }
374
318
 
375
- // === Agents ===
319
+ // === AI Agents (--tool options) ===
376
320
  const agentLines = [];
377
- if (versions.claudeCode) {
378
- agentLines.push(`• Claude Code: \`${versions.claudeCode}\``);
379
- }
380
- if (versions.playwright) {
381
- agentLines.push(`• Playwright: \`${versions.playwright}\``);
382
- }
383
- if (versions.playwrightMcp) {
384
- agentLines.push(`• Playwright MCP: \`${versions.playwrightMcp}\``);
385
- }
321
+ addVersionLine(agentLines, 'Claude Code', versions.claudeCode);
322
+ addVersionLine(agentLines, 'Agent CLI', versions.agent);
323
+ addVersionLine(agentLines, 'OpenAI Codex', versions.codex);
324
+ addVersionLine(agentLines, 'OpenCode', versions.opencode);
325
+ addVersionLine(agentLines, 'Qwen Code', versions.qwenCode);
326
+ addVersionLine(agentLines, 'Gemini CLI', versions.gemini);
327
+ addVersionLine(agentLines, 'GitHub Copilot', versions.copilot);
386
328
 
387
329
  if (agentLines.length > 0) {
388
330
  lines.push('');
389
- lines.push('*🎭 Agents*');
331
+ lines.push('*🎭 AI Agents*');
390
332
  lines.push(...agentLines);
391
333
  }
392
334
 
393
- // === Language Runtimes ===
394
- const runtimeLines = [];
395
- if (versions.node) {
396
- runtimeLines.push(`• Node.js: \`${versions.node}\``);
397
- }
398
- if (versions.python) {
399
- runtimeLines.push(`• Python: \`${versions.python}\``);
400
- }
401
- if (versions.rust) {
402
- runtimeLines.push(`• Rust: \`${versions.rust}\``);
403
- }
404
- if (versions.php) {
405
- runtimeLines.push(`• PHP: \`${versions.php}\``);
335
+ // === JavaScript/Node.js ===
336
+ const jsLines = [];
337
+ addVersionLine(jsLines, 'Node.js', versions.node);
338
+ addVersionLine(jsLines, 'Bun', versions.bun);
339
+ addVersionLine(jsLines, 'Deno', versions.deno);
340
+ addVersionLine(jsLines, 'NPM', versions.npm);
341
+ addVersionLine(jsLines, 'NVM', versions.nvm);
342
+
343
+ if (jsLines.length > 0) {
344
+ lines.push('');
345
+ lines.push('*📦 JavaScript/Node.js*');
346
+ lines.push(...jsLines);
406
347
  }
407
- if (versions.bun) {
408
- runtimeLines.push(`• Bun: \`${versions.bun}\``);
348
+
349
+ // === Python ===
350
+ const pythonLines = [];
351
+ addVersionLine(pythonLines, 'Python', versions.python);
352
+ addVersionLine(pythonLines, 'Pyenv', versions.pyenv);
353
+
354
+ if (pythonLines.length > 0) {
355
+ lines.push('');
356
+ lines.push('*🐍 Python*');
357
+ lines.push(...pythonLines);
409
358
  }
410
- if (versions.dotnet) {
411
- runtimeLines.push(`• .NET: \`${versions.dotnet}\``);
359
+
360
+ // === Rust ===
361
+ const rustLines = [];
362
+ addVersionLine(rustLines, 'Rustc', versions.rust);
363
+ addVersionLine(rustLines, 'Cargo', versions.cargo);
364
+
365
+ if (rustLines.length > 0) {
366
+ lines.push('');
367
+ lines.push('*🦀 Rust*');
368
+ lines.push(...rustLines);
412
369
  }
413
- if (versions.deno) {
414
- runtimeLines.push(`• Deno: \`${versions.deno}\``);
370
+
371
+ // === Java ===
372
+ const javaLines = [];
373
+ addVersionLine(javaLines, 'Java', versions.java);
374
+ addVersionLine(javaLines, 'SDKMAN', versions.sdkman);
375
+
376
+ if (javaLines.length > 0) {
377
+ lines.push('');
378
+ lines.push('*☕ Java*');
379
+ lines.push(...javaLines);
415
380
  }
381
+
382
+ // === Go ===
416
383
  if (versions.go) {
417
- runtimeLines.push(`• Go: \`${versions.go}\``);
384
+ lines.push('');
385
+ lines.push('*🔷 Go*');
386
+ addVersionLine(lines, 'Go', versions.go);
418
387
  }
419
- if (versions.java) {
420
- runtimeLines.push(`• Java: \`${versions.java}\``);
388
+
389
+ // === PHP ===
390
+ if (versions.php) {
391
+ lines.push('');
392
+ lines.push('*🐘 PHP*');
393
+ addVersionLine(lines, 'PHP', versions.php);
421
394
  }
422
- if (versions.lean) {
423
- runtimeLines.push(`• Lean: \`${versions.lean}\``);
395
+
396
+ // === .NET ===
397
+ if (versions.dotnet) {
398
+ lines.push('');
399
+ lines.push('*📦 .NET*');
400
+ addVersionLine(lines, '.NET SDK', versions.dotnet);
424
401
  }
425
- if (versions.perl) {
426
- runtimeLines.push(`• Perl: \`${versions.perl}\``);
402
+
403
+ // === Perl ===
404
+ const perlLines = [];
405
+ addVersionLine(perlLines, 'Perl', versions.perl);
406
+ addVersionLine(perlLines, 'Perlbrew', versions.perlbrew);
407
+
408
+ if (perlLines.length > 0) {
409
+ lines.push('');
410
+ lines.push('*💎 Perl*');
411
+ lines.push(...perlLines);
427
412
  }
428
- if (versions.ocaml) {
429
- runtimeLines.push(`• OCaml: \`${versions.ocaml}\``);
413
+
414
+ // === OCaml/Rocq ===
415
+ const ocamlLines = [];
416
+ addVersionLine(ocamlLines, 'OCaml', versions.ocaml);
417
+ addVersionLine(ocamlLines, 'Opam', versions.opam);
418
+ addVersionLine(ocamlLines, 'Rocq/Coq', versions.rocq);
419
+
420
+ if (ocamlLines.length > 0) {
421
+ lines.push('');
422
+ lines.push('*🐫 OCaml/Rocq*');
423
+ lines.push(...ocamlLines);
430
424
  }
431
- if (versions.rocq) {
432
- runtimeLines.push(`• Rocq/Coq: \`${versions.rocq}\``);
425
+
426
+ // === Lean ===
427
+ const leanLines = [];
428
+ addVersionLine(leanLines, 'Lean', versions.lean);
429
+ addVersionLine(leanLines, 'Elan', versions.elan);
430
+ addVersionLine(leanLines, 'Lake', versions.lake);
431
+
432
+ if (leanLines.length > 0) {
433
+ lines.push('');
434
+ lines.push('*📐 Lean*');
435
+ lines.push(...leanLines);
433
436
  }
434
437
 
435
- if (runtimeLines.length > 0) {
438
+ // === C/C++ ===
439
+ const cppLines = [];
440
+ addVersionLine(cppLines, 'GCC', versions.gcc);
441
+ addVersionLine(cppLines, 'G++', versions.gpp);
442
+ addVersionLine(cppLines, 'Clang', versions.clang);
443
+ addVersionLine(cppLines, 'LLVM', versions.llvm);
444
+ addVersionLine(cppLines, 'LLD', versions.lld);
445
+ addVersionLine(cppLines, 'Make', versions.make);
446
+ addVersionLine(cppLines, 'CMake', versions.cmake);
447
+
448
+ if (cppLines.length > 0) {
436
449
  lines.push('');
437
- lines.push('*⚙️ Language Runtimes*');
438
- lines.push(...runtimeLines);
450
+ lines.push('*🔨 C/C++*');
451
+ lines.push(...cppLines);
439
452
  }
440
453
 
441
454
  // === Development Tools ===
442
455
  const toolLines = [];
443
- if (versions.git) {
444
- toolLines.push(`• Git: \`${versions.git}\``);
445
- }
446
- if (versions.gh) {
447
- toolLines.push(`• GitHub CLI: \`${versions.gh}\``);
448
- }
449
- if (versions.npm) {
450
- toolLines.push(`• NPM: \`${versions.npm}\``);
451
- }
452
- if (versions.nvm) {
453
- toolLines.push(`• NVM: \`${versions.nvm}\``);
454
- }
455
- if (versions.pyenv) {
456
- toolLines.push(`• Pyenv: \`${versions.pyenv}\``);
457
- }
458
- if (versions.cargo) {
459
- toolLines.push(`• Cargo: \`${versions.cargo}\``);
460
- }
461
- if (versions.brew) {
462
- toolLines.push(`• Homebrew: \`${versions.brew}\``);
463
- }
464
- if (versions.sdkman) {
465
- toolLines.push(`• SDKMAN: \`${versions.sdkman}\``);
466
- }
467
- if (versions.elan) {
468
- toolLines.push(`• Elan: \`${versions.elan}\``);
469
- }
470
- if (versions.lake) {
471
- toolLines.push(`• Lake: \`${versions.lake}\``);
472
- }
473
- if (versions.perlbrew) {
474
- toolLines.push(`• Perlbrew: \`${versions.perlbrew}\``);
475
- }
476
- if (versions.opam) {
477
- toolLines.push(`• Opam: \`${versions.opam}\``);
478
- }
456
+ addVersionLine(toolLines, 'Git', versions.git);
457
+ addVersionLine(toolLines, 'GitHub CLI', versions.gh);
458
+ addVersionLine(toolLines, 'Playwright', versions.playwright);
459
+ addVersionLine(toolLines, 'Playwright MCP', versions.playwrightMcp);
460
+ addVersionLine(toolLines, 'Homebrew', versions.brew);
479
461
 
480
462
  if (toolLines.length > 0) {
481
463
  lines.push('');
@@ -483,36 +465,6 @@ export function formatVersionMessage(versions) {
483
465
  lines.push(...toolLines);
484
466
  }
485
467
 
486
- // === C/C++ Development Tools ===
487
- const cppToolLines = [];
488
- if (versions.make) {
489
- cppToolLines.push(`• Make: \`${versions.make}\``);
490
- }
491
- if (versions.cmake) {
492
- cppToolLines.push(`• CMake: \`${versions.cmake}\``);
493
- }
494
- if (versions.gcc) {
495
- cppToolLines.push(`• GCC: \`${versions.gcc}\``);
496
- }
497
- if (versions.gpp) {
498
- cppToolLines.push(`• G++: \`${versions.gpp}\``);
499
- }
500
- if (versions.clang) {
501
- cppToolLines.push(`• Clang: \`${versions.clang}\``);
502
- }
503
- if (versions.llvm) {
504
- cppToolLines.push(`• LLVM: \`${versions.llvm}\``);
505
- }
506
- if (versions.lld) {
507
- cppToolLines.push(`• LLD: \`${versions.lld}\``);
508
- }
509
-
510
- if (cppToolLines.length > 0) {
511
- lines.push('');
512
- lines.push('*🔧 C/C++ Development Tools*');
513
- lines.push(...cppToolLines);
514
- }
515
-
516
468
  // === Platform ===
517
469
  if (versions.platform) {
518
470
  lines.push('');