@link-assistant/hive-mind 1.23.12 → 1.23.14
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 +21 -0
- package/README.md +16 -4
- package/package.json +1 -1
- package/src/telegram-bot.mjs +10 -7
- package/src/version-info.lib.mjs +355 -403
- package/src/version.lib.mjs +16 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.23.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 069d437: Parallelize version gathering with Promise.all for 6-30x performance improvement
|
|
8
|
+
- Replaced sequential `execSync` calls with parallel `execAsync` using `Promise.all`
|
|
9
|
+
- Reduced execution time from 30-150s to ~2-5s for version info gathering
|
|
10
|
+
- Added support for all `--tool` options: agent, codex, opencode, qwen-code, gemini, copilot
|
|
11
|
+
- Reorganized Telegram output to group tools by programming language instead of generic categories
|
|
12
|
+
- Consolidated hive-mind version display to show single version with restart warning when process version differs from installed
|
|
13
|
+
- Added `gatherTimeMs` metric to track performance
|
|
14
|
+
|
|
15
|
+
## 1.23.13
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- af1f456: fix: suppress dotenvx MISSING_ENV_FILE warnings in hive-telegram-bot --version
|
|
20
|
+
- Add early --version handling before loading dotenvx to avoid warnings
|
|
21
|
+
- Add ignore: ['MISSING_ENV_FILE'] option to make .env file optional
|
|
22
|
+
- Add tests for version output in tests/test-telegram-bot-version.mjs
|
|
23
|
+
|
|
3
24
|
## 1.23.12
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -149,11 +149,14 @@ Run the Hive Mind using Docker for safer local installation - no manual setup re
|
|
|
149
149
|
# Pull the latest image from Docker Hub
|
|
150
150
|
docker pull konard/hive-mind:latest
|
|
151
151
|
|
|
152
|
-
#
|
|
153
|
-
docker run -
|
|
152
|
+
# Start hive-mind container
|
|
153
|
+
docker run -dit --name hive-mind konard/hive-mind:latest
|
|
154
154
|
|
|
155
|
-
#
|
|
156
|
-
|
|
155
|
+
# Verify container started
|
|
156
|
+
docker ps -a
|
|
157
|
+
|
|
158
|
+
# Enter additional terminal process to do installation
|
|
159
|
+
docker exec -it hive-mind /bin/bash
|
|
157
160
|
|
|
158
161
|
# Inside the container, authenticate with GitHub
|
|
159
162
|
gh-setup-git-identity
|
|
@@ -163,6 +166,15 @@ claude
|
|
|
163
166
|
|
|
164
167
|
# Now you can use hive and solve commands
|
|
165
168
|
solve https://github.com/owner/repo/issues/123
|
|
169
|
+
|
|
170
|
+
# Or you can run telegram bot:
|
|
171
|
+
|
|
172
|
+
# Run an to main process
|
|
173
|
+
docker attach hive-mind
|
|
174
|
+
|
|
175
|
+
# Run bot here
|
|
176
|
+
|
|
177
|
+
# Press Ctrl + P, Ctrl + Q to detach without destroying the container (no stopping of main bash process)
|
|
166
178
|
```
|
|
167
179
|
|
|
168
180
|
**Benefits of Docker:**
|
package/package.json
CHANGED
package/src/telegram-bot.mjs
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// Early exit for --version (issue #1318: avoid dotenvx MISSING_ENV_FILE warnings)
|
|
3
|
+
if (process.argv.includes('--version')) {
|
|
4
|
+
const v = await import('./version.lib.mjs').then(m => m.getVersion()).catch(() => 'unknown');
|
|
5
|
+
console.log(v);
|
|
6
|
+
process.exit(v === 'unknown' ? 1 : 0);
|
|
7
|
+
}
|
|
2
8
|
|
|
3
9
|
import { spawn } from 'child_process';
|
|
4
10
|
import { promisify } from 'util';
|
|
@@ -21,7 +27,9 @@ const dotenvx = dotenvxModule.default || dotenvxModule;
|
|
|
21
27
|
const getenv = await use('getenv');
|
|
22
28
|
|
|
23
29
|
// Load .env configuration as base
|
|
24
|
-
|
|
30
|
+
// quiet: true suppresses info messages, ignore: ['MISSING_ENV_FILE'] suppresses error when .env doesn't exist
|
|
31
|
+
// This makes .env file optional (issue #1318)
|
|
32
|
+
dotenvx.config({ quiet: true, ignore: ['MISSING_ENV_FILE'] });
|
|
25
33
|
|
|
26
34
|
// Load .lenv configuration (if exists)
|
|
27
35
|
// .lenv overrides .env
|
|
@@ -30,20 +38,15 @@ loadLenvConfig({ override: true, quiet: true });
|
|
|
30
38
|
const yargsModule = await use('yargs@17.7.2');
|
|
31
39
|
const yargs = yargsModule.default || yargsModule;
|
|
32
40
|
const { hideBin } = await use('yargs@17.7.2/helpers');
|
|
33
|
-
|
|
34
|
-
// Import solve and hive yargs configurations for validation
|
|
41
|
+
// Import yargs configurations, GitHub utilities, and telegram helpers
|
|
35
42
|
const { createYargsConfig: createSolveYargsConfig, detectMalformedFlags } = await import('./solve.config.lib.mjs');
|
|
36
43
|
const { createYargsConfig: createHiveYargsConfig } = await import('./hive.config.lib.mjs');
|
|
37
|
-
// Import GitHub URL parser for extracting URLs from messages
|
|
38
44
|
const { parseGitHubUrl } = await import('./github.lib.mjs');
|
|
39
|
-
// Import model validation for early validation with helpful error messages
|
|
40
45
|
const { validateModelName } = await import('./model-validation.lib.mjs');
|
|
41
|
-
// Import libraries for /limits, /version, and markdown escaping
|
|
42
46
|
const { formatUsageMessage, getAllCachedLimits } = await import('./limits.lib.mjs');
|
|
43
47
|
const { getVersionInfo, formatVersionMessage } = await import('./version-info.lib.mjs');
|
|
44
48
|
const { escapeMarkdown, escapeMarkdownV2, cleanNonPrintableChars, makeSpecialCharsVisible } = await import('./telegram-markdown.lib.mjs');
|
|
45
49
|
const { getSolveQueue, createQueueExecuteCallback } = await import('./telegram-solve-queue.lib.mjs');
|
|
46
|
-
// Import extracted message filter functions for testability (issue #1207)
|
|
47
50
|
const { isOldMessage: _isOldMessage, isGroupChat: _isGroupChat, isChatAuthorized: _isChatAuthorized, isForwardedOrReply: _isForwardedOrReply, extractCommandFromText } = await import('./telegram-message-filters.lib.mjs');
|
|
48
51
|
// Import bot launcher with exponential backoff retry (issue #1240)
|
|
49
52
|
const { launchBotWithRetry } = await import('./telegram-bot-launcher.lib.mjs');
|
package/src/version-info.lib.mjs
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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
|
|
22
|
+
async function execCommandAsync(command, timeout = 5000) {
|
|
16
23
|
try {
|
|
17
|
-
const
|
|
18
|
-
const trimmed =
|
|
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
|
|
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
|
-
//
|
|
159
|
+
// Execute all version commands in parallel
|
|
160
|
+
const results = await Promise.all(VERSION_COMMANDS.map(cmd => executeVersionCommand(cmd, verbose)));
|
|
50
161
|
|
|
51
|
-
//
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
//
|
|
58
|
-
|
|
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: ${
|
|
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
|
-
//
|
|
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}
|
|
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
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
// Agents
|
|
291
|
-
claudeCode:
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
//
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
cargo:
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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:
|
|
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(
|
|
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
|
-
// ===
|
|
364
|
-
lines.push('*🤖
|
|
365
|
-
if (versions.
|
|
366
|
-
lines.push(`•
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
-
// ===
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
-
|
|
408
|
-
|
|
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
|
-
|
|
411
|
-
|
|
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
|
-
|
|
414
|
-
|
|
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
|
-
|
|
384
|
+
lines.push('');
|
|
385
|
+
lines.push('*🔷 Go*');
|
|
386
|
+
addVersionLine(lines, 'Go', versions.go);
|
|
418
387
|
}
|
|
419
|
-
|
|
420
|
-
|
|
388
|
+
|
|
389
|
+
// === PHP ===
|
|
390
|
+
if (versions.php) {
|
|
391
|
+
lines.push('');
|
|
392
|
+
lines.push('*🐘 PHP*');
|
|
393
|
+
addVersionLine(lines, 'PHP', versions.php);
|
|
421
394
|
}
|
|
422
|
-
|
|
423
|
-
|
|
395
|
+
|
|
396
|
+
// === .NET ===
|
|
397
|
+
if (versions.dotnet) {
|
|
398
|
+
lines.push('');
|
|
399
|
+
lines.push('*📦 .NET*');
|
|
400
|
+
addVersionLine(lines, '.NET SDK', versions.dotnet);
|
|
424
401
|
}
|
|
425
|
-
|
|
426
|
-
|
|
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
|
-
|
|
429
|
-
|
|
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
|
-
|
|
432
|
-
|
|
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
|
-
|
|
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('
|
|
438
|
-
lines.push(...
|
|
450
|
+
lines.push('*🔨 C/C++*');
|
|
451
|
+
lines.push(...cppLines);
|
|
439
452
|
}
|
|
440
453
|
|
|
441
454
|
// === Development Tools ===
|
|
442
455
|
const toolLines = [];
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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('');
|
package/src/version.lib.mjs
CHANGED
|
@@ -5,6 +5,11 @@ import { dirname, join } from 'path';
|
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { getGitVersion } from './git.lib.mjs';
|
|
7
7
|
|
|
8
|
+
// Cache for version (immutable after first read)
|
|
9
|
+
// This ensures the version remains consistent even if package.json changes during runtime
|
|
10
|
+
// See issue #1318: version should be cached in RAM at startup
|
|
11
|
+
let cachedVersion = null;
|
|
12
|
+
|
|
8
13
|
async function isRunningAsScript() {
|
|
9
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
15
|
const __dirname = dirname(__filename);
|
|
@@ -18,6 +23,11 @@ async function isRunningAsScript() {
|
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
export async function getVersion() {
|
|
26
|
+
// Return cached version if already computed (immutable after first read)
|
|
27
|
+
if (cachedVersion !== null) {
|
|
28
|
+
return cachedVersion;
|
|
29
|
+
}
|
|
30
|
+
|
|
21
31
|
const __filename = fileURLToPath(import.meta.url);
|
|
22
32
|
const __dirname = dirname(__filename);
|
|
23
33
|
const packagePath = join(__dirname, '..', 'package.json');
|
|
@@ -28,13 +38,15 @@ export async function getVersion() {
|
|
|
28
38
|
const currentVersion = packageJson.version;
|
|
29
39
|
|
|
30
40
|
if (await isRunningAsScript()) {
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
cachedVersion = await getGitVersion(undefined, currentVersion);
|
|
42
|
+
} else {
|
|
43
|
+
cachedVersion = currentVersion;
|
|
33
44
|
}
|
|
34
45
|
|
|
35
|
-
return
|
|
46
|
+
return cachedVersion;
|
|
36
47
|
} catch {
|
|
37
|
-
|
|
48
|
+
cachedVersion = 'unknown';
|
|
49
|
+
return cachedVersion;
|
|
38
50
|
}
|
|
39
51
|
}
|
|
40
52
|
|