start-command 0.7.0 → 0.7.2
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 +22 -0
- package/README.md +11 -17
- package/REQUIREMENTS.md +2 -2
- package/docs/case-studies/issue-22/analysis.md +547 -0
- package/docs/case-studies/issue-22/issue-data.json +12 -0
- package/experiments/test-screen-attached.js +2 -2
- package/experiments/test-screen-logfile.js +1 -1
- package/experiments/test-screen-modes.js +1 -1
- package/package.json +6 -6
- package/src/bin/cli.js +119 -26
- package/test/cli.test.js +7 -4
- package/test/version.test.js +310 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# start-command
|
|
2
2
|
|
|
3
|
+
## 0.7.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fa0fb23: docs: Update documentation to Bun-first approach
|
|
8
|
+
- Remove npm installation option from README.md
|
|
9
|
+
- Update examples to use bun commands instead of npm
|
|
10
|
+
- Change package.json engines from node to bun
|
|
11
|
+
- Update REQUIREMENTS.md to remove Node.js alternative
|
|
12
|
+
|
|
13
|
+
## 0.7.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- d5a7c66: Fix all --version detection issues
|
|
18
|
+
- Fix screen version detection by capturing stderr
|
|
19
|
+
- Show Bun version instead of Node.js version when running with Bun
|
|
20
|
+
- Show macOS ProductVersion instead of kernel version
|
|
21
|
+
- Fix argument parsing to handle `$ --version --` same as `$ --version`
|
|
22
|
+
- Update all scripts and examples to use Bun instead of Node.js
|
|
23
|
+
- Add comprehensive tests for --version flag
|
|
24
|
+
|
|
3
25
|
## 0.7.0
|
|
4
26
|
|
|
5
27
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -4,18 +4,12 @@ Gamification of coding - execute any command with automatic logging and ability
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Install using [Bun](https://bun.sh):
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
bun install -g start-command
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Or using npm:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install -g start-command
|
|
17
|
-
```
|
|
18
|
-
|
|
19
13
|
## Usage
|
|
20
14
|
|
|
21
15
|
The `$` command acts as a wrapper for any shell command:
|
|
@@ -123,16 +117,16 @@ Run commands in isolated environments using terminal multiplexers or containers:
|
|
|
123
117
|
|
|
124
118
|
```bash
|
|
125
119
|
# Run in tmux (attached by default)
|
|
126
|
-
$ --isolated tmux --
|
|
120
|
+
$ --isolated tmux -- bun start
|
|
127
121
|
|
|
128
122
|
# Run in screen detached
|
|
129
|
-
$ --isolated screen --detached --
|
|
123
|
+
$ --isolated screen --detached -- bun start
|
|
130
124
|
|
|
131
125
|
# Run in docker container
|
|
132
|
-
$ --isolated docker --image
|
|
126
|
+
$ --isolated docker --image oven/bun:latest -- bun install
|
|
133
127
|
|
|
134
128
|
# Short form with custom session name
|
|
135
|
-
$ -i tmux -s my-session -d
|
|
129
|
+
$ -i tmux -s my-session -d bun start
|
|
136
130
|
```
|
|
137
131
|
|
|
138
132
|
#### Supported Backends
|
|
@@ -169,7 +163,7 @@ The tool works in any environment:
|
|
|
169
163
|
|
|
170
164
|
### Required
|
|
171
165
|
|
|
172
|
-
-
|
|
166
|
+
- [Bun](https://bun.sh) >= 1.0.0
|
|
173
167
|
|
|
174
168
|
### Optional (for full auto-reporting)
|
|
175
169
|
|
|
@@ -183,7 +177,7 @@ To set up auto-reporting:
|
|
|
183
177
|
gh auth login
|
|
184
178
|
|
|
185
179
|
# Install log uploader
|
|
186
|
-
|
|
180
|
+
bun install -g gh-upload-log
|
|
187
181
|
```
|
|
188
182
|
|
|
189
183
|
## How It Works
|
|
@@ -214,10 +208,10 @@ Example:
|
|
|
214
208
|
|
|
215
209
|
```bash
|
|
216
210
|
# Run without auto-issue creation
|
|
217
|
-
START_DISABLE_AUTO_ISSUE=1 $
|
|
211
|
+
START_DISABLE_AUTO_ISSUE=1 $ bun test
|
|
218
212
|
|
|
219
213
|
# Use custom log directory
|
|
220
|
-
START_LOG_DIR=./logs $
|
|
214
|
+
START_LOG_DIR=./logs $ bun test
|
|
221
215
|
|
|
222
216
|
# Disable substitutions (use raw command)
|
|
223
217
|
START_DISABLE_SUBSTITUTIONS=1 $ install lodash npm package
|
|
@@ -237,10 +231,10 @@ Log files are saved as `start-command-{timestamp}-{random}.log` and contain:
|
|
|
237
231
|
```
|
|
238
232
|
=== Start Command Log ===
|
|
239
233
|
Timestamp: 2024-01-15 10:30:45.123
|
|
240
|
-
Command:
|
|
234
|
+
Command: bun test
|
|
241
235
|
Shell: /bin/bash
|
|
242
236
|
Platform: linux
|
|
243
|
-
|
|
237
|
+
Bun Version: 1.2.0
|
|
244
238
|
Working Directory: /home/user/project
|
|
245
239
|
==================================================
|
|
246
240
|
|
package/REQUIREMENTS.md
CHANGED
|
@@ -76,7 +76,7 @@ The `$` command is a CLI tool that wraps any shell command and provides automati
|
|
|
76
76
|
- Command that was executed
|
|
77
77
|
- Exit code
|
|
78
78
|
- Link to uploaded log
|
|
79
|
-
- System information (OS, Node version)
|
|
79
|
+
- System information (OS, Bun/Node.js version)
|
|
80
80
|
- Timestamp
|
|
81
81
|
- Print the created issue URL to console
|
|
82
82
|
|
|
@@ -209,7 +209,7 @@ Repository not detected - automatic issue creation skipped
|
|
|
209
209
|
|
|
210
210
|
### Required
|
|
211
211
|
|
|
212
|
-
-
|
|
212
|
+
- **Bun >= 1.0.0** (primary runtime)
|
|
213
213
|
- `child_process` (built-in)
|
|
214
214
|
- `os` (built-in)
|
|
215
215
|
- `fs` (built-in)
|
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
# Case Study: Issue #22 - Version Detection Issues
|
|
2
|
+
|
|
3
|
+
## Issue Overview
|
|
4
|
+
|
|
5
|
+
**Issue ID:** #22
|
|
6
|
+
**Title:** --version issues
|
|
7
|
+
**Reported By:** konard
|
|
8
|
+
**Created:** 2025-12-23T17:55:53Z
|
|
9
|
+
**Status:** Open
|
|
10
|
+
|
|
11
|
+
## Problem Summary
|
|
12
|
+
|
|
13
|
+
The `$ --version` command exhibited multiple issues when run on macOS:
|
|
14
|
+
|
|
15
|
+
1. **Screen version not detected** - Despite screen being installed, it showed "not installed"
|
|
16
|
+
2. **Wrong runtime displayed** - Showed Node.js version instead of Bun version
|
|
17
|
+
3. **Incorrect OS version format** - Showed OS Release (kernel version) instead of macOS version
|
|
18
|
+
4. **Argument parsing issue** - `$ --version --` resulted in "No command provided" error
|
|
19
|
+
|
|
20
|
+
## Timeline of Events
|
|
21
|
+
|
|
22
|
+
### User Environment
|
|
23
|
+
|
|
24
|
+
- **System:** macOS 15.7.2 (ProductVersion)
|
|
25
|
+
- **Kernel:** 24.6.0 (OS Release)
|
|
26
|
+
- **Bun Version:** 1.2.20
|
|
27
|
+
- **Node.js Emulation:** v24.3.0 (provided by Bun)
|
|
28
|
+
- **Architecture:** arm64
|
|
29
|
+
- **Package:** start-command@0.7.0
|
|
30
|
+
|
|
31
|
+
### Observed Behavior
|
|
32
|
+
|
|
33
|
+
#### Command 1: `$ --version --`
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
Error: No command provided
|
|
37
|
+
Usage: $ [options] [--] <command> [args...]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Expected:** Should display version information (same as `$ --version`)
|
|
41
|
+
**Actual:** Treated `--` as command separator and found no command after it
|
|
42
|
+
|
|
43
|
+
#### Command 2: `$ --version`
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
start-command version: 0.7.0
|
|
47
|
+
|
|
48
|
+
OS: darwin
|
|
49
|
+
OS Release: 24.6.0
|
|
50
|
+
Node Version: v24.3.0
|
|
51
|
+
Architecture: arm64
|
|
52
|
+
|
|
53
|
+
Isolation tools:
|
|
54
|
+
screen: not installed
|
|
55
|
+
tmux: not installed
|
|
56
|
+
docker: Docker version 28.5.1, build e180ab8
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Issues Identified:**
|
|
60
|
+
|
|
61
|
+
1. Screen shown as "not installed" despite being present
|
|
62
|
+
2. "Node Version: v24.3.0" when using Bun
|
|
63
|
+
3. "OS Release: 24.6.0" (kernel) instead of "15.7.2" (macOS version)
|
|
64
|
+
|
|
65
|
+
#### Verification Commands
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
screen -v
|
|
69
|
+
# Output: Screen version 4.00.03 (FAU) 23-Oct-06
|
|
70
|
+
|
|
71
|
+
sw_vers
|
|
72
|
+
# Output:
|
|
73
|
+
# ProductName: macOS
|
|
74
|
+
# ProductVersion: 15.7.2
|
|
75
|
+
# BuildVersion: 24G325
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Root Cause Analysis
|
|
79
|
+
|
|
80
|
+
### Issue 1: Screen Version Not Detected
|
|
81
|
+
|
|
82
|
+
**Location:** `src/bin/cli.js:84`
|
|
83
|
+
|
|
84
|
+
**Root Cause:**
|
|
85
|
+
|
|
86
|
+
- Screen outputs version to stderr, not stdout
|
|
87
|
+
- Current implementation uses `execSync()` with default stdio configuration
|
|
88
|
+
- On some systems/versions, screen sends output to stderr
|
|
89
|
+
|
|
90
|
+
**Code Analysis:**
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
const screenVersion = getToolVersion('screen', '--version');
|
|
94
|
+
|
|
95
|
+
function getToolVersion(toolName, versionFlag) {
|
|
96
|
+
try {
|
|
97
|
+
const result = execSync(`${toolName} ${versionFlag}`, {
|
|
98
|
+
encoding: 'utf8',
|
|
99
|
+
stdio: ['pipe', 'pipe', 'pipe'], // stderr is piped but not captured
|
|
100
|
+
timeout: 5000,
|
|
101
|
+
}).trim();
|
|
102
|
+
// ...
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The `execSync()` call captures stdout but the function doesn't merge stderr into the result. On macOS, screen may output to stderr.
|
|
106
|
+
|
|
107
|
+
### Issue 2: Node.js Version Instead of Bun Version
|
|
108
|
+
|
|
109
|
+
**Location:** `src/bin/cli.js:76`
|
|
110
|
+
|
|
111
|
+
**Root Cause:**
|
|
112
|
+
|
|
113
|
+
- Code uses `process.version` which returns Node.js compatibility version in Bun
|
|
114
|
+
- Bun emulates Node.js v24.3.0 for compatibility
|
|
115
|
+
- Should use `process.versions.bun` or `Bun.version` instead
|
|
116
|
+
|
|
117
|
+
**Code Analysis:**
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
console.log(`Node Version: ${process.version}`);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Research Findings:**
|
|
124
|
+
|
|
125
|
+
- `process.version` in Bun → Returns emulated Node.js version (v24.3.0)
|
|
126
|
+
- `process.versions.bun` → Returns actual Bun version (1.2.20)
|
|
127
|
+
- `Bun.version` → Bun-specific API for version
|
|
128
|
+
|
|
129
|
+
**Sources:**
|
|
130
|
+
|
|
131
|
+
- [Bun — Detect the Version at Runtime](https://futurestud.io/tutorials/bun-detect-the-version-at-runtime)
|
|
132
|
+
- [Get the current Bun version - Bun](https://bun.com/docs/guides/util/version)
|
|
133
|
+
|
|
134
|
+
### Issue 3: Incorrect macOS Version Format
|
|
135
|
+
|
|
136
|
+
**Location:** `src/bin/cli.js:75`
|
|
137
|
+
|
|
138
|
+
**Root Cause:**
|
|
139
|
+
|
|
140
|
+
- Code uses `os.release()` which returns kernel version (24.6.0)
|
|
141
|
+
- Should use `sw_vers -productVersion` for user-facing macOS version (15.7.2)
|
|
142
|
+
|
|
143
|
+
**Code Analysis:**
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
console.log(`OS Release: ${os.release()}`);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Kernel vs Product Version:**
|
|
150
|
+
|
|
151
|
+
- `os.release()` → 24.6.0 (Darwin kernel version)
|
|
152
|
+
- `sw_vers -productVersion` → 15.7.2 (macOS product version)
|
|
153
|
+
|
|
154
|
+
**Research Findings:**
|
|
155
|
+
The macOS `sw_vers` command provides user-facing version information:
|
|
156
|
+
|
|
157
|
+
- `sw_vers -productVersion` returns the macOS version (e.g., "15.7.2", "26.0")
|
|
158
|
+
- This is what users recognize as their macOS version
|
|
159
|
+
|
|
160
|
+
**Sources:**
|
|
161
|
+
|
|
162
|
+
- [sw_vers Man Page - macOS - SS64.com](https://ss64.com/mac/sw_vers.html)
|
|
163
|
+
- [Check macOS Latest Version · For Tahoe · 2025](https://mac.install.guide/macos/check-version)
|
|
164
|
+
|
|
165
|
+
### Issue 4: Argument Parsing for `--version --`
|
|
166
|
+
|
|
167
|
+
**Location:** `src/bin/cli.js:52-55`
|
|
168
|
+
|
|
169
|
+
**Root Cause:**
|
|
170
|
+
|
|
171
|
+
- Version check happens before argument parsing
|
|
172
|
+
- Only checks for exact match: `args.length === 1 && (args[0] === '--version' || args[0] === '-v')`
|
|
173
|
+
- When `--version --` is passed, args = `['--version', '--']`, length is 2, fails the check
|
|
174
|
+
- Falls through to argument parser which treats `--` as separator
|
|
175
|
+
|
|
176
|
+
**Code Analysis:**
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
if (args.length === 1 && (args[0] === '--version' || args[0] === '-v')) {
|
|
180
|
+
printVersion();
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Expected Behavior:**
|
|
186
|
+
|
|
187
|
+
- `$ --version` → Show version ✓
|
|
188
|
+
- `$ --version --` → Show version (should ignore trailing `--`)
|
|
189
|
+
- `$ --` → "No command provided" error ✓
|
|
190
|
+
- `$` → Show usage ✓
|
|
191
|
+
|
|
192
|
+
## Proposed Solutions
|
|
193
|
+
|
|
194
|
+
### Solution 1: Fix Screen Detection
|
|
195
|
+
|
|
196
|
+
Capture both stdout and stderr when detecting tool versions:
|
|
197
|
+
|
|
198
|
+
```javascript
|
|
199
|
+
function getToolVersion(toolName, versionFlag) {
|
|
200
|
+
try {
|
|
201
|
+
const result = execSync(`${toolName} ${versionFlag} 2>&1`, {
|
|
202
|
+
encoding: 'utf8',
|
|
203
|
+
timeout: 5000,
|
|
204
|
+
}).trim();
|
|
205
|
+
const firstLine = result.split('\n')[0];
|
|
206
|
+
return firstLine;
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Solution 2: Use Bun Version
|
|
214
|
+
|
|
215
|
+
Detect runtime and show appropriate version:
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
// Detect if running in Bun
|
|
219
|
+
const runtime = typeof Bun !== 'undefined' ? 'Bun' : 'Node.js';
|
|
220
|
+
const runtimeVersion =
|
|
221
|
+
typeof Bun !== 'undefined' ? Bun.version : process.version;
|
|
222
|
+
|
|
223
|
+
console.log(`${runtime} Version: ${runtimeVersion}`);
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Solution 3: Fix macOS Version Detection
|
|
227
|
+
|
|
228
|
+
Use `sw_vers -productVersion` on macOS:
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
function getOSVersion() {
|
|
232
|
+
if (process.platform === 'darwin') {
|
|
233
|
+
try {
|
|
234
|
+
return execSync('sw_vers -productVersion', { encoding: 'utf8' }).trim();
|
|
235
|
+
} catch {
|
|
236
|
+
return os.release();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return os.release();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.log(`OS: ${process.platform}`);
|
|
243
|
+
console.log(`OS Version: ${getOSVersion()}`);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Solution 4: Fix Argument Parsing
|
|
247
|
+
|
|
248
|
+
Check for `--version` flag regardless of other arguments:
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
// Check if --version is present (ignore trailing --)
|
|
252
|
+
const hasVersionFlag = args.includes('--version') || args.includes('-v');
|
|
253
|
+
const isOnlyVersionWithSeparator =
|
|
254
|
+
args.length === 2 &&
|
|
255
|
+
(args[0] === '--version' || args[0] === '-v') &&
|
|
256
|
+
args[1] === '--';
|
|
257
|
+
|
|
258
|
+
if ((args.length === 1 && hasVersionFlag) || isOnlyVersionWithSeparator) {
|
|
259
|
+
printVersion();
|
|
260
|
+
process.exit(0);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Implementation Plan
|
|
265
|
+
|
|
266
|
+
1. **Fix argument parsing** - Handle `--version --` case
|
|
267
|
+
2. **Fix screen detection** - Capture stderr in version detection
|
|
268
|
+
3. **Replace Node with Bun** - Detect runtime and show correct version
|
|
269
|
+
4. **Fix OS version** - Use `sw_vers` on macOS
|
|
270
|
+
5. **Update REQUIREMENTS.md** - Remove npm references, emphasize Bun-first
|
|
271
|
+
6. **Add comprehensive tests** - Cover all version scenarios
|
|
272
|
+
7. **Ensure CI runs tests** - Validate quality
|
|
273
|
+
|
|
274
|
+
## Testing Strategy
|
|
275
|
+
|
|
276
|
+
### Test Cases Required
|
|
277
|
+
|
|
278
|
+
1. **Argument Parsing Tests:**
|
|
279
|
+
- `$ --version` → Shows version
|
|
280
|
+
- `$ -v` → Shows version
|
|
281
|
+
- `$ --version --` → Shows version
|
|
282
|
+
- `$ --` → Error: No command provided
|
|
283
|
+
- `$` → Shows usage
|
|
284
|
+
|
|
285
|
+
2. **Screen Detection Tests:**
|
|
286
|
+
- When screen installed → Shows version
|
|
287
|
+
- When screen not installed → Shows "not installed"
|
|
288
|
+
|
|
289
|
+
3. **Runtime Detection Tests:**
|
|
290
|
+
- Running with Bun → Shows "Bun Version: X.X.X"
|
|
291
|
+
- Running with Node → Shows "Node.js Version: vX.X.X"
|
|
292
|
+
|
|
293
|
+
4. **OS Version Tests:**
|
|
294
|
+
- On macOS → Shows ProductVersion (15.7.2 format)
|
|
295
|
+
- On Linux → Shows kernel version
|
|
296
|
+
- On Windows → Shows kernel version
|
|
297
|
+
|
|
298
|
+
## Documentation Updates
|
|
299
|
+
|
|
300
|
+
### REQUIREMENTS.md Changes Needed
|
|
301
|
+
|
|
302
|
+
1. Replace all "npm install" references with "bun install"
|
|
303
|
+
2. Update "Node.js >= 14.0.0" to "Bun >= 1.0.0"
|
|
304
|
+
3. Update system information to show "Bun Version" instead of "Node Version"
|
|
305
|
+
4. Emphasize Bun-first approach
|
|
306
|
+
|
|
307
|
+
## Additional Notes
|
|
308
|
+
|
|
309
|
+
- The project uses `#!/usr/bin/env bun` shebang correctly
|
|
310
|
+
- Package scripts still use `node --test` which should be changed to `bun test`
|
|
311
|
+
- All references to npm in documentation should be updated to bun
|
|
312
|
+
- Consider removing npm-specific features if they don't work with bun
|
|
313
|
+
|
|
314
|
+
## Files to Modify
|
|
315
|
+
|
|
316
|
+
1. `src/bin/cli.js` - Fix all version detection issues
|
|
317
|
+
2. `src/lib/args-parser.js` - No changes needed (issue is in cli.js)
|
|
318
|
+
3. `REQUIREMENTS.md` - Update to Bun-first approach
|
|
319
|
+
4. `package.json` - Update test script to use bun
|
|
320
|
+
5. `test/cli.test.js` - Add version detection tests
|
|
321
|
+
6. New: `test/version.test.js` - Comprehensive version tests
|
|
322
|
+
|
|
323
|
+
## Success Criteria
|
|
324
|
+
|
|
325
|
+
- ✅ `$ --version --` works same as `$ --version`
|
|
326
|
+
- ✅ Screen version detected correctly when installed
|
|
327
|
+
- ✅ Shows "Bun Version" instead of "Node Version"
|
|
328
|
+
- ✅ macOS shows ProductVersion not kernel version
|
|
329
|
+
- ✅ All tests pass locally
|
|
330
|
+
- ✅ CI tests pass
|
|
331
|
+
- ✅ REQUIREMENTS.md updated
|
|
332
|
+
- ✅ No npm references in documentation
|
|
333
|
+
|
|
334
|
+
## Implementation Status
|
|
335
|
+
|
|
336
|
+
**Status:** 🔄 In Progress (Second Iteration)
|
|
337
|
+
|
|
338
|
+
### First Iteration (PR #23 - Merged to Main)
|
|
339
|
+
|
|
340
|
+
The initial fixes were merged to `main` branch via PR #23, addressing:
|
|
341
|
+
|
|
342
|
+
- Version flag handling with trailing `--`
|
|
343
|
+
- Runtime detection (Bun vs Node.js)
|
|
344
|
+
- macOS version detection using `sw_vers`
|
|
345
|
+
- Basic screen detection using `2>&1`
|
|
346
|
+
|
|
347
|
+
### Second Iteration (This PR) - The Screen Detection Bug
|
|
348
|
+
|
|
349
|
+
After v0.7.1 was released, users on macOS still reported:
|
|
350
|
+
|
|
351
|
+
```
|
|
352
|
+
screen: not installed
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Even though screen was installed:
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
$ screen -v
|
|
359
|
+
Screen version 4.00.03 (FAU) 23-Oct-06
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### Deep Root Cause Analysis
|
|
363
|
+
|
|
364
|
+
**The Problem:** The macOS bundled version of GNU Screen (4.00.03) returns a **non-zero exit code** when running `screen --version` or `screen -v`, even though it successfully outputs the version information.
|
|
365
|
+
|
|
366
|
+
**Discovery Process:**
|
|
367
|
+
|
|
368
|
+
1. Searched for known issues with GNU Screen version flag
|
|
369
|
+
2. Found reference to [GNU Screen v.4.8.0 release notes](https://savannah.gnu.org/forum/forum.php?forum_id=9665)
|
|
370
|
+
3. The release notes mention: "Make screen exit code be 0 when checking --version"
|
|
371
|
+
|
|
372
|
+
**Root Cause Confirmed:**
|
|
373
|
+
This was a **known bug in GNU Screen prior to version 4.8.0** where the `--version` flag would exit with a non-zero status code.
|
|
374
|
+
|
|
375
|
+
- macOS bundled version: 4.00.03 (affected by bug)
|
|
376
|
+
- Bug fix version: 4.8.0+
|
|
377
|
+
- Linux typically has newer versions via package managers
|
|
378
|
+
|
|
379
|
+
**Why Previous Fix Didn't Work:**
|
|
380
|
+
|
|
381
|
+
The previous fix used `execSync()` with `2>&1` to capture stderr:
|
|
382
|
+
|
|
383
|
+
```javascript
|
|
384
|
+
const result = execSync(`${toolName} ${versionFlag} 2>&1`, {
|
|
385
|
+
encoding: 'utf8',
|
|
386
|
+
timeout: 5000,
|
|
387
|
+
}).trim();
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Problem:** `execSync()` throws an exception when the command returns non-zero exit code, so even though the output was captured correctly, the `catch` block returned `null`.
|
|
391
|
+
|
|
392
|
+
#### The Solution
|
|
393
|
+
|
|
394
|
+
Use `spawnSync()` instead of `execSync()` to capture output **regardless of exit code**:
|
|
395
|
+
|
|
396
|
+
```javascript
|
|
397
|
+
function getToolVersion(toolName, versionFlag, verbose = false) {
|
|
398
|
+
const isWindows = process.platform === 'win32';
|
|
399
|
+
const whichCmd = isWindows ? 'where' : 'which';
|
|
400
|
+
|
|
401
|
+
// First, check if the tool exists in PATH
|
|
402
|
+
try {
|
|
403
|
+
execSync(`${whichCmd} ${toolName}`, {
|
|
404
|
+
encoding: 'utf8',
|
|
405
|
+
timeout: 5000,
|
|
406
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
407
|
+
});
|
|
408
|
+
} catch {
|
|
409
|
+
// Tool not found in PATH
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Tool exists, try to get version using spawnSync
|
|
414
|
+
// This captures output regardless of exit code
|
|
415
|
+
const result = spawnSync(toolName, [versionFlag], {
|
|
416
|
+
encoding: 'utf8',
|
|
417
|
+
timeout: 5000,
|
|
418
|
+
shell: false,
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// Combine stdout and stderr
|
|
422
|
+
const output = ((result.stdout || '') + (result.stderr || '')).trim();
|
|
423
|
+
|
|
424
|
+
if (!output) {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return output.split('\n')[0];
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Additional Improvements
|
|
433
|
+
|
|
434
|
+
1. **`--verbose` flag support** - Users can now debug version detection:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
$ --version --verbose
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
This shows detailed debugging information about tool detection.
|
|
441
|
+
|
|
442
|
+
2. **`-v` flag for screen** - Changed from `--version` to `-v` which is more universally supported.
|
|
443
|
+
|
|
444
|
+
3. **Comprehensive test coverage** - Added 14 tests for version detection scenarios.
|
|
445
|
+
|
|
446
|
+
### Changes Made (Second Iteration)
|
|
447
|
+
|
|
448
|
+
#### src/bin/cli.js
|
|
449
|
+
|
|
450
|
+
1. **Added `spawnSync` import:**
|
|
451
|
+
|
|
452
|
+
```javascript
|
|
453
|
+
const { spawn, execSync, spawnSync } = require('child_process');
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
2. **Enhanced version flag handling with verbose support:**
|
|
457
|
+
|
|
458
|
+
```javascript
|
|
459
|
+
const hasVerboseWithVersion =
|
|
460
|
+
hasVersionFlag &&
|
|
461
|
+
args.some((arg) => arg === '--verbose' || arg === '--debug');
|
|
462
|
+
|
|
463
|
+
if (hasVersionFlag && isVersionOnly) {
|
|
464
|
+
printVersion(hasVerboseWithVersion || config.verbose);
|
|
465
|
+
process.exit(0);
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
3. **Fixed getToolVersion to use spawnSync:**
|
|
470
|
+
- First checks if tool exists using `which`/`where`
|
|
471
|
+
- Uses `spawnSync` to capture output regardless of exit code
|
|
472
|
+
- Combines stdout and stderr
|
|
473
|
+
- Supports verbose mode for debugging
|
|
474
|
+
|
|
475
|
+
4. **Updated printVersion to accept verbose parameter:**
|
|
476
|
+
- Shows `[verbose]` messages when debugging
|
|
477
|
+
- Logs tool detection details
|
|
478
|
+
|
|
479
|
+
#### test/version.test.js
|
|
480
|
+
|
|
481
|
+
Added 3 new tests for verbose mode:
|
|
482
|
+
|
|
483
|
+
- `--version --verbose`
|
|
484
|
+
- `--version --debug`
|
|
485
|
+
- `START_VERBOSE=1` environment variable
|
|
486
|
+
|
|
487
|
+
### Test Results
|
|
488
|
+
|
|
489
|
+
All 84 tests passing across 4 test files:
|
|
490
|
+
|
|
491
|
+
- `test/version.test.js`: 14 tests
|
|
492
|
+
- `test/cli.test.js`: Passing
|
|
493
|
+
- `test/args-parser.test.js`: Passing
|
|
494
|
+
- `test/isolation.test.js`: Passing
|
|
495
|
+
- `test/substitution.test.js`: 22 tests
|
|
496
|
+
|
|
497
|
+
### Verified Behavior
|
|
498
|
+
|
|
499
|
+
```bash
|
|
500
|
+
$ --version
|
|
501
|
+
start-command version: 0.7.1
|
|
502
|
+
|
|
503
|
+
OS: linux
|
|
504
|
+
OS Version: 6.8.0-90-generic
|
|
505
|
+
Bun Version: 1.3.3
|
|
506
|
+
Architecture: x64
|
|
507
|
+
|
|
508
|
+
Isolation tools:
|
|
509
|
+
screen: Screen version 4.09.01 (GNU) 20-Aug-23
|
|
510
|
+
tmux: tmux 3.4
|
|
511
|
+
docker: not installed
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
```bash
|
|
515
|
+
$ --version --verbose
|
|
516
|
+
start-command version: 0.7.1
|
|
517
|
+
|
|
518
|
+
OS: linux
|
|
519
|
+
OS Version: 6.8.0-90-generic
|
|
520
|
+
Bun Version: 1.3.3
|
|
521
|
+
Architecture: x64
|
|
522
|
+
|
|
523
|
+
Isolation tools:
|
|
524
|
+
[verbose] Checking isolation tools...
|
|
525
|
+
[verbose] screen -v: exit=0, output="Screen version 4.09.01 (GNU) 20-Aug-23"
|
|
526
|
+
screen: Screen version 4.09.01 (GNU) 20-Aug-23
|
|
527
|
+
[verbose] tmux -V: exit=0, output="tmux 3.4"
|
|
528
|
+
tmux: tmux 3.4
|
|
529
|
+
[verbose] docker: not found in PATH
|
|
530
|
+
docker: not installed
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
## Key Learnings
|
|
534
|
+
|
|
535
|
+
1. **Exit codes matter**: Some tools return non-zero exit codes even for successful operations. Always consider using `spawnSync` when you need to capture output regardless of exit status.
|
|
536
|
+
|
|
537
|
+
2. **macOS bundled tools are often outdated**: The macOS bundled version of screen (4.00.03 from 2006) has known bugs fixed in newer versions.
|
|
538
|
+
|
|
539
|
+
3. **Testing on multiple platforms is crucial**: The bug only manifested on macOS with the bundled screen, not on Linux with modern screen versions.
|
|
540
|
+
|
|
541
|
+
4. **Verbose mode is invaluable for debugging**: Adding `--verbose` support allows users to self-diagnose issues.
|
|
542
|
+
|
|
543
|
+
## References
|
|
544
|
+
|
|
545
|
+
- [GNU Screen v.4.8.0 Release Notes](https://savannah.gnu.org/forum/forum.php?forum_id=9665) - Documents the exit code fix
|
|
546
|
+
- [screen Man Page - macOS](https://ss64.com/mac/screen.html) - macOS screen documentation
|
|
547
|
+
- [Node.js spawnSync](https://nodejs.org/api/child_process.html#child_processspawnsynccommand-args-options) - Alternative to execSync that doesn't throw on non-zero exit
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": {
|
|
3
|
+
"id": "MDQ6VXNlcjE0MzE5MDQ=",
|
|
4
|
+
"is_bot": false,
|
|
5
|
+
"login": "konard",
|
|
6
|
+
"name": "Konstantin Diachenko"
|
|
7
|
+
},
|
|
8
|
+
"body": "```\nkonard@MacBook-Pro-Konstantin ~ % bun install -g start-command \nbun add v1.2.20 (6ad208bc)\n\ninstalled start-command@0.7.0 with binaries:\n - $\n\n1 package installed [2.40s]\nkonard@MacBook-Pro-Konstantin ~ % $ --version -- \nError: No command provided\nUsage: $ [options] [--] <command> [args...]\n $ <command> [args...]\n\nOptions:\n --isolated, -i <environment> Run in isolated environment (screen, tmux, docker)\n --attached, -a Run in attached mode (foreground)\n --detached, -d Run in detached mode (background)\n --session, -s <name> Session name for isolation\n --image <image> Docker image (required for docker isolation)\n --version, -v Show version information\n\nExamples:\n $ echo \"Hello World\"\n $ npm test\n $ --isolated tmux -- npm start\n $ -i screen -d npm start\n $ --isolated docker --image node:20 -- npm install\n\nFeatures:\n - Logs all output to temporary directory\n - Displays timestamps and exit codes\n - Auto-reports failures for NPM packages (when gh is available)\n - Natural language command aliases (via substitutions.lino)\n - Process isolation via screen, tmux, or docker\n\nAlias examples:\n $ install lodash npm package -> npm install lodash\n $ install 4.17.21 version of lodash npm package -> npm install lodash@4.17.21\n $ clone https://github.com/user/repo repository -> git clone https://github.com/user/repo\nkonard@MacBook-Pro-Konstantin ~ % $ --version \nstart-command version: 0.7.0\n\nOS: darwin\nOS Release: 24.6.0\nNode Version: v24.3.0\nArchitecture: arm64\n\nIsolation tools:\n screen: not installed\n tmux: not installed\n docker: Docker version 28.5.1, build e180ab8\nkonard@MacBook-Pro-Konstantin ~ % screen -v\nScreen version 4.00.03 (FAU) 23-Oct-06\nkonard@MacBook-Pro-Konstantin ~ % docker -v\nDocker version 28.5.1, build e180ab8\nkonard@MacBook-Pro-Konstantin ~ % sw_vers\nProductName:\t\tmacOS\nProductVersion:\t\t15.7.2\nBuildVersion:\t\t24G325\nkonard@MacBook-Pro-Konstantin ~ % \n```\n\nThe problems are:\n\n- `screen` tool version was not detected.\n- We use Bun, not Node.js (double check that, and also remove suggestions to install it using npm in the docs. Our tool should be Bun first and Bun only. For all scripts and shebangs we should use bun, not node. So in `--version` there should be bun version, not `node.js` version.\n- OS version detection was wrong.\n- `$ --version --` should work the same as `$ --version`, only `$ --` or `$` should result in `No command provided`.\n\nMake sure we have tests for all these (and these tests should be executed in CI, so we guarantee the quality). Also [REQUIREMENTS.md](https://github.com/link-foundation/start/blob/main/REQUIREMENTS.md) should be updated as needed.\n\nPlease download all logs and data related about the issue to this repository, make sure we compile that data to `./docs/case-studies/issue-{id}` folder, and use it to do deep case study analysis (also make sure to search online for additional facts and data), in which we will reconstruct timeline/sequence of events, find root causes of the problem, and propose possible solutions.",
|
|
9
|
+
"comments": [],
|
|
10
|
+
"createdAt": "2025-12-23T17:55:53Z",
|
|
11
|
+
"title": "--version issues"
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "start-command",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Gamification of coding, execute any command with ability to auto-report issues on GitHub",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"$": "./src/bin/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "
|
|
10
|
+
"test": "bun test test/",
|
|
11
11
|
"lint": "eslint .",
|
|
12
12
|
"lint:fix": "eslint . --fix",
|
|
13
13
|
"format": "prettier --write .",
|
|
14
14
|
"format:check": "prettier --check .",
|
|
15
|
-
"check:file-size": "
|
|
16
|
-
"check": "
|
|
15
|
+
"check:file-size": "bun scripts/check-file-size.mjs",
|
|
16
|
+
"check": "bun run lint && bun run format:check && bun run check:file-size",
|
|
17
17
|
"prepare": "husky || true",
|
|
18
18
|
"changeset": "changeset",
|
|
19
|
-
"changeset:version": "
|
|
19
|
+
"changeset:version": "bun scripts/changeset-version.mjs",
|
|
20
20
|
"changeset:publish": "changeset publish",
|
|
21
21
|
"changeset:status": "changeset status --since=origin/main"
|
|
22
22
|
},
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"author": "",
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"engines": {
|
|
33
|
-
"
|
|
33
|
+
"bun": ">=1.0.0"
|
|
34
34
|
},
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
package/src/bin/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
const { spawn, execSync } = require('child_process');
|
|
3
|
+
const { spawn, execSync, spawnSync } = require('child_process');
|
|
4
4
|
const process = require('process');
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const fs = require('fs');
|
|
@@ -49,8 +49,28 @@ const config = {
|
|
|
49
49
|
const args = process.argv.slice(2);
|
|
50
50
|
|
|
51
51
|
// Handle --version flag
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
// Support: $ --version, $ -v, $ --version --
|
|
53
|
+
// The trailing -- should be ignored for version check
|
|
54
|
+
// Also support --verbose flag for debugging: $ --version --verbose
|
|
55
|
+
const hasVersionFlag =
|
|
56
|
+
args.length >= 1 && (args[0] === '--version' || args[0] === '-v');
|
|
57
|
+
|
|
58
|
+
// Check for --verbose flag in version context
|
|
59
|
+
const hasVerboseWithVersion =
|
|
60
|
+
hasVersionFlag &&
|
|
61
|
+
args.some((arg) => arg === '--verbose' || arg === '--debug');
|
|
62
|
+
|
|
63
|
+
// Determine if this is a version-only call
|
|
64
|
+
// Allow: --version, -v, --version --, --version --verbose, etc.
|
|
65
|
+
const versionRelatedArgs = ['--version', '-v', '--', '--verbose', '--debug'];
|
|
66
|
+
const isVersionOnly =
|
|
67
|
+
hasVersionFlag &&
|
|
68
|
+
args.every(
|
|
69
|
+
(arg) => versionRelatedArgs.includes(arg) || arg === args[0] // Allow the version flag itself
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (hasVersionFlag && isVersionOnly) {
|
|
73
|
+
printVersion(hasVerboseWithVersion || config.verbose);
|
|
54
74
|
process.exit(0);
|
|
55
75
|
}
|
|
56
76
|
|
|
@@ -61,8 +81,9 @@ if (args.length === 0) {
|
|
|
61
81
|
|
|
62
82
|
/**
|
|
63
83
|
* Print version information
|
|
84
|
+
* @param {boolean} verbose - Whether to show verbose debugging info
|
|
64
85
|
*/
|
|
65
|
-
function printVersion() {
|
|
86
|
+
function printVersion(verbose = false) {
|
|
66
87
|
// Get package version
|
|
67
88
|
const packageJson = require('../../package.json');
|
|
68
89
|
const startCommandVersion = packageJson.version;
|
|
@@ -70,18 +91,50 @@ function printVersion() {
|
|
|
70
91
|
console.log(`start-command version: ${startCommandVersion}`);
|
|
71
92
|
console.log('');
|
|
72
93
|
|
|
94
|
+
// Get runtime information (Bun or Node.js)
|
|
95
|
+
const runtime = typeof Bun !== 'undefined' ? 'Bun' : 'Node.js';
|
|
96
|
+
const runtimeVersion =
|
|
97
|
+
typeof Bun !== 'undefined' ? Bun.version : process.version;
|
|
98
|
+
|
|
73
99
|
// Get OS information
|
|
74
100
|
console.log(`OS: ${process.platform}`);
|
|
75
|
-
|
|
76
|
-
|
|
101
|
+
|
|
102
|
+
// Get OS version (use sw_vers on macOS for user-friendly version)
|
|
103
|
+
let osVersion = os.release();
|
|
104
|
+
if (process.platform === 'darwin') {
|
|
105
|
+
try {
|
|
106
|
+
osVersion = execSync('sw_vers -productVersion', {
|
|
107
|
+
encoding: 'utf8',
|
|
108
|
+
timeout: 5000,
|
|
109
|
+
}).trim();
|
|
110
|
+
if (verbose) {
|
|
111
|
+
console.log(`[verbose] macOS version from sw_vers: ${osVersion}`);
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
// Fallback to kernel version if sw_vers fails
|
|
115
|
+
osVersion = os.release();
|
|
116
|
+
if (verbose) {
|
|
117
|
+
console.log(
|
|
118
|
+
`[verbose] sw_vers failed, using kernel version: ${osVersion}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
console.log(`OS Version: ${osVersion}`);
|
|
125
|
+
console.log(`${runtime} Version: ${runtimeVersion}`);
|
|
77
126
|
console.log(`Architecture: ${process.arch}`);
|
|
78
127
|
console.log('');
|
|
79
128
|
|
|
80
129
|
// Check for installed isolation tools
|
|
81
130
|
console.log('Isolation tools:');
|
|
82
131
|
|
|
83
|
-
|
|
84
|
-
|
|
132
|
+
if (verbose) {
|
|
133
|
+
console.log('[verbose] Checking isolation tools...');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check screen (use -v flag for compatibility with older versions)
|
|
137
|
+
const screenVersion = getToolVersion('screen', '-v', verbose);
|
|
85
138
|
if (screenVersion) {
|
|
86
139
|
console.log(` screen: ${screenVersion}`);
|
|
87
140
|
} else {
|
|
@@ -89,7 +142,7 @@ function printVersion() {
|
|
|
89
142
|
}
|
|
90
143
|
|
|
91
144
|
// Check tmux
|
|
92
|
-
const tmuxVersion = getToolVersion('tmux', '-V');
|
|
145
|
+
const tmuxVersion = getToolVersion('tmux', '-V', verbose);
|
|
93
146
|
if (tmuxVersion) {
|
|
94
147
|
console.log(` tmux: ${tmuxVersion}`);
|
|
95
148
|
} else {
|
|
@@ -97,7 +150,7 @@ function printVersion() {
|
|
|
97
150
|
}
|
|
98
151
|
|
|
99
152
|
// Check docker
|
|
100
|
-
const dockerVersion = getToolVersion('docker', '--version');
|
|
153
|
+
const dockerVersion = getToolVersion('docker', '--version', verbose);
|
|
101
154
|
if (dockerVersion) {
|
|
102
155
|
console.log(` docker: ${dockerVersion}`);
|
|
103
156
|
} else {
|
|
@@ -109,23 +162,53 @@ function printVersion() {
|
|
|
109
162
|
* Get version of an installed tool
|
|
110
163
|
* @param {string} toolName - Name of the tool
|
|
111
164
|
* @param {string} versionFlag - Flag to get version (e.g., '--version', '-V')
|
|
165
|
+
* @param {boolean} verbose - Whether to log verbose information
|
|
112
166
|
* @returns {string|null} Version string or null if not installed
|
|
113
167
|
*/
|
|
114
|
-
function getToolVersion(toolName, versionFlag) {
|
|
168
|
+
function getToolVersion(toolName, versionFlag, verbose = false) {
|
|
169
|
+
const isWindows = process.platform === 'win32';
|
|
170
|
+
const whichCmd = isWindows ? 'where' : 'which';
|
|
171
|
+
|
|
172
|
+
// First, check if the tool exists in PATH
|
|
115
173
|
try {
|
|
116
|
-
|
|
174
|
+
execSync(`${whichCmd} ${toolName}`, {
|
|
117
175
|
encoding: 'utf8',
|
|
118
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
119
176
|
timeout: 5000,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// Extract version number from output
|
|
123
|
-
// Most tools output version in various formats, so we'll return the first line
|
|
124
|
-
const firstLine = result.split('\n')[0];
|
|
125
|
-
return firstLine;
|
|
177
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
178
|
+
});
|
|
126
179
|
} catch {
|
|
180
|
+
// Tool not found in PATH
|
|
181
|
+
if (verbose) {
|
|
182
|
+
console.log(`[verbose] ${toolName}: not found in PATH`);
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Tool exists, try to get version using spawnSync
|
|
188
|
+
// This captures output regardless of exit code (some tools like older screen
|
|
189
|
+
// versions return non-zero exit code even when showing version successfully)
|
|
190
|
+
const result = spawnSync(toolName, [versionFlag], {
|
|
191
|
+
encoding: 'utf8',
|
|
192
|
+
timeout: 5000,
|
|
193
|
+
shell: false,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Combine stdout and stderr (some tools output version to stderr)
|
|
197
|
+
const output = ((result.stdout || '') + (result.stderr || '')).trim();
|
|
198
|
+
|
|
199
|
+
if (verbose) {
|
|
200
|
+
console.log(
|
|
201
|
+
`[verbose] ${toolName} ${versionFlag}: exit=${result.status}, output="${output.substring(0, 100)}"`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!output) {
|
|
127
206
|
return null;
|
|
128
207
|
}
|
|
208
|
+
|
|
209
|
+
// Return the first line of output
|
|
210
|
+
const firstLine = output.split('\n')[0];
|
|
211
|
+
return firstLine || null;
|
|
129
212
|
}
|
|
130
213
|
|
|
131
214
|
/**
|
|
@@ -149,10 +232,10 @@ function printUsage() {
|
|
|
149
232
|
console.log('');
|
|
150
233
|
console.log('Examples:');
|
|
151
234
|
console.log(' $ echo "Hello World"');
|
|
152
|
-
console.log(' $
|
|
153
|
-
console.log(' $ --isolated tmux --
|
|
154
|
-
console.log(' $ -i screen -d
|
|
155
|
-
console.log(' $ --isolated docker --image
|
|
235
|
+
console.log(' $ bun test');
|
|
236
|
+
console.log(' $ --isolated tmux -- bun start');
|
|
237
|
+
console.log(' $ -i screen -d bun start');
|
|
238
|
+
console.log(' $ --isolated docker --image oven/bun:latest -- bun install');
|
|
156
239
|
console.log('');
|
|
157
240
|
console.log('Features:');
|
|
158
241
|
console.log(' - Logs all output to temporary directory');
|
|
@@ -313,6 +396,11 @@ function runDirect(cmd) {
|
|
|
313
396
|
let logContent = '';
|
|
314
397
|
const startTime = getTimestamp();
|
|
315
398
|
|
|
399
|
+
// Get runtime information
|
|
400
|
+
const runtime = typeof Bun !== 'undefined' ? 'Bun' : 'Node.js';
|
|
401
|
+
const runtimeVersion =
|
|
402
|
+
typeof Bun !== 'undefined' ? Bun.version : process.version;
|
|
403
|
+
|
|
316
404
|
// Log header
|
|
317
405
|
logContent += `=== Start Command Log ===\n`;
|
|
318
406
|
logContent += `Timestamp: ${startTime}\n`;
|
|
@@ -325,7 +413,7 @@ function runDirect(cmd) {
|
|
|
325
413
|
}
|
|
326
414
|
logContent += `Shell: ${shell}\n`;
|
|
327
415
|
logContent += `Platform: ${process.platform}\n`;
|
|
328
|
-
logContent +=
|
|
416
|
+
logContent += `${runtime} Version: ${runtimeVersion}\n`;
|
|
329
417
|
logContent += `Working Directory: ${process.cwd()}\n`;
|
|
330
418
|
logContent += `${'='.repeat(50)}\n\n`;
|
|
331
419
|
|
|
@@ -474,7 +562,7 @@ function handleFailure(cmdName, fullCommand, exitCode, logPath) {
|
|
|
474
562
|
}
|
|
475
563
|
} else {
|
|
476
564
|
console.log('gh-upload-log not installed - log upload skipped');
|
|
477
|
-
console.log('Install with:
|
|
565
|
+
console.log('Install with: bun install -g gh-upload-log');
|
|
478
566
|
}
|
|
479
567
|
|
|
480
568
|
// Check if we can create issues in this repository
|
|
@@ -745,6 +833,11 @@ function createIssue(repoInfo, fullCommand, exitCode, logUrl) {
|
|
|
745
833
|
try {
|
|
746
834
|
const title = `Command failed with exit code ${exitCode}: ${fullCommand.substring(0, 50)}${fullCommand.length > 50 ? '...' : ''}`;
|
|
747
835
|
|
|
836
|
+
// Get runtime information
|
|
837
|
+
const runtime = typeof Bun !== 'undefined' ? 'Bun' : 'Node.js';
|
|
838
|
+
const runtimeVersion =
|
|
839
|
+
typeof Bun !== 'undefined' ? Bun.version : process.version;
|
|
840
|
+
|
|
748
841
|
let body = `## Command Execution Failure Report\n\n`;
|
|
749
842
|
body += `**Command:** \`${fullCommand}\`\n\n`;
|
|
750
843
|
body += `**Exit Code:** ${exitCode}\n\n`;
|
|
@@ -752,7 +845,7 @@ function createIssue(repoInfo, fullCommand, exitCode, logUrl) {
|
|
|
752
845
|
body += `### System Information\n\n`;
|
|
753
846
|
body += `- **Platform:** ${process.platform}\n`;
|
|
754
847
|
body += `- **OS Release:** ${os.release()}\n`;
|
|
755
|
-
body += `-
|
|
848
|
+
body += `- **${runtime} Version:** ${runtimeVersion}\n`;
|
|
756
849
|
body += `- **Architecture:** ${process.arch}\n\n`;
|
|
757
850
|
|
|
758
851
|
if (logUrl) {
|
package/test/cli.test.js
CHANGED
|
@@ -38,12 +38,15 @@ describe('CLI version flag', () => {
|
|
|
38
38
|
);
|
|
39
39
|
assert.ok(result.stdout.includes('OS:'), 'Should display OS');
|
|
40
40
|
assert.ok(
|
|
41
|
-
result.stdout.includes('OS
|
|
42
|
-
'Should display OS
|
|
41
|
+
result.stdout.includes('OS Version:'),
|
|
42
|
+
'Should display OS Version'
|
|
43
43
|
);
|
|
44
|
+
// Check for either Bun or Node.js version depending on runtime
|
|
45
|
+
const hasBunVersion = result.stdout.includes('Bun Version:');
|
|
46
|
+
const hasNodeVersion = result.stdout.includes('Node.js Version:');
|
|
44
47
|
assert.ok(
|
|
45
|
-
|
|
46
|
-
'Should display Node Version'
|
|
48
|
+
hasBunVersion || hasNodeVersion,
|
|
49
|
+
'Should display Bun Version or Node.js Version'
|
|
47
50
|
);
|
|
48
51
|
assert.ok(
|
|
49
52
|
result.stdout.includes('Architecture:'),
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Tests for --version flag behavior
|
|
4
|
+
* Tests for issue #22: --version issues
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { describe, it } = require('node:test');
|
|
8
|
+
const assert = require('node:assert');
|
|
9
|
+
const { execSync, spawnSync } = require('child_process');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
// Path to the CLI
|
|
14
|
+
const cliPath = path.resolve(__dirname, '../src/bin/cli.js');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Helper to run the CLI command
|
|
18
|
+
*/
|
|
19
|
+
function runCli(args) {
|
|
20
|
+
try {
|
|
21
|
+
// Use '--' separator to ensure args are passed to the script, not consumed by bun
|
|
22
|
+
// This is important for testing edge cases like passing '--' as an argument
|
|
23
|
+
const result = spawnSync('bun', [cliPath, '--', ...args], {
|
|
24
|
+
encoding: 'utf8',
|
|
25
|
+
timeout: 5000,
|
|
26
|
+
env: {
|
|
27
|
+
...process.env,
|
|
28
|
+
START_DISABLE_AUTO_ISSUE: '1',
|
|
29
|
+
START_DISABLE_LOG_UPLOAD: '1',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
stdout: result.stdout || '',
|
|
34
|
+
stderr: result.stderr || '',
|
|
35
|
+
exitCode: result.status,
|
|
36
|
+
error: result.error,
|
|
37
|
+
};
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return {
|
|
40
|
+
stdout: '',
|
|
41
|
+
stderr: error.message,
|
|
42
|
+
exitCode: 1,
|
|
43
|
+
error,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe('Version Flag Tests', () => {
|
|
49
|
+
describe('Basic version flag', () => {
|
|
50
|
+
it('should show version with --version', () => {
|
|
51
|
+
const result = runCli(['--version']);
|
|
52
|
+
assert.strictEqual(result.exitCode, 0, 'Exit code should be 0');
|
|
53
|
+
assert.match(
|
|
54
|
+
result.stdout,
|
|
55
|
+
/start-command version:/,
|
|
56
|
+
'Should show start-command version'
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should show version with -v', () => {
|
|
61
|
+
const result = runCli(['-v']);
|
|
62
|
+
assert.strictEqual(result.exitCode, 0, 'Exit code should be 0');
|
|
63
|
+
assert.match(
|
|
64
|
+
result.stdout,
|
|
65
|
+
/start-command version:/,
|
|
66
|
+
'Should show start-command version'
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should show version with --version -- (trailing separator)', () => {
|
|
71
|
+
const result = runCli(['--version', '--']);
|
|
72
|
+
assert.strictEqual(result.exitCode, 0, 'Exit code should be 0');
|
|
73
|
+
assert.match(
|
|
74
|
+
result.stdout,
|
|
75
|
+
/start-command version:/,
|
|
76
|
+
'Should show start-command version with trailing --'
|
|
77
|
+
);
|
|
78
|
+
assert.doesNotMatch(
|
|
79
|
+
result.stderr,
|
|
80
|
+
/No command provided/,
|
|
81
|
+
'Should not show "No command provided" error'
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('Runtime detection', () => {
|
|
87
|
+
it('should show Bun version when running with Bun', () => {
|
|
88
|
+
const result = runCli(['--version']);
|
|
89
|
+
assert.strictEqual(result.exitCode, 0, 'Exit code should be 0');
|
|
90
|
+
|
|
91
|
+
// When running with Bun, should show "Bun Version"
|
|
92
|
+
if (typeof Bun !== 'undefined') {
|
|
93
|
+
assert.match(
|
|
94
|
+
result.stdout,
|
|
95
|
+
/Bun Version:/,
|
|
96
|
+
'Should show "Bun Version:" when running with Bun'
|
|
97
|
+
);
|
|
98
|
+
assert.doesNotMatch(
|
|
99
|
+
result.stdout,
|
|
100
|
+
/Node\.js Version:/,
|
|
101
|
+
'Should not show "Node.js Version:" when running with Bun'
|
|
102
|
+
);
|
|
103
|
+
} else {
|
|
104
|
+
// When running with Node.js, should show "Node.js Version"
|
|
105
|
+
assert.match(
|
|
106
|
+
result.stdout,
|
|
107
|
+
/Node\.js Version:/,
|
|
108
|
+
'Should show "Node.js Version:" when running with Node.js'
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('OS version detection', () => {
|
|
115
|
+
it('should show OS information', () => {
|
|
116
|
+
const result = runCli(['--version']);
|
|
117
|
+
assert.strictEqual(result.exitCode, 0, 'Exit code should be 0');
|
|
118
|
+
assert.match(result.stdout, /OS:/, 'Should show OS');
|
|
119
|
+
assert.match(result.stdout, /OS Version:/, 'Should show OS Version');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should show macOS ProductVersion on darwin', () => {
|
|
123
|
+
const result = runCli(['--version']);
|
|
124
|
+
assert.strictEqual(result.exitCode, 0, 'Exit code should be 0');
|
|
125
|
+
|
|
126
|
+
if (process.platform === 'darwin') {
|
|
127
|
+
// Get the actual macOS version using sw_vers
|
|
128
|
+
const macOSVersion = execSync('sw_vers -productVersion', {
|
|
129
|
+
encoding: 'utf8',
|
|
130
|
+
}).trim();
|
|
131
|
+
|
|
132
|
+
// Version output should contain the ProductVersion, not the kernel version
|
|
133
|
+
assert.match(
|
|
134
|
+
result.stdout,
|
|
135
|
+
new RegExp(`OS Version: ${macOSVersion.replace('.', '\\.')}`),
|
|
136
|
+
`Should show macOS ProductVersion (${macOSVersion}), not kernel version`
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// Should NOT show kernel version (which starts with 2x on modern macOS)
|
|
140
|
+
const kernelVersion = os.release();
|
|
141
|
+
if (kernelVersion.startsWith('2')) {
|
|
142
|
+
assert.doesNotMatch(
|
|
143
|
+
result.stdout,
|
|
144
|
+
new RegExp(`OS Version: ${kernelVersion.replace('.', '\\.')}`),
|
|
145
|
+
`Should not show kernel version (${kernelVersion})`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('Tool version detection', () => {
|
|
153
|
+
it('should detect screen version if installed', () => {
|
|
154
|
+
const result = runCli(['--version']);
|
|
155
|
+
assert.strictEqual(result.exitCode, 0, 'Exit code should be 0');
|
|
156
|
+
|
|
157
|
+
// Check if screen is actually installed
|
|
158
|
+
try {
|
|
159
|
+
const screenVersion = execSync('screen --version 2>&1', {
|
|
160
|
+
encoding: 'utf8',
|
|
161
|
+
timeout: 5000,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (screenVersion) {
|
|
165
|
+
// If screen is installed, it should not show "not installed"
|
|
166
|
+
assert.doesNotMatch(
|
|
167
|
+
result.stdout,
|
|
168
|
+
/screen: not installed/,
|
|
169
|
+
'Should not show "screen: not installed" when screen is available'
|
|
170
|
+
);
|
|
171
|
+
assert.match(
|
|
172
|
+
result.stdout,
|
|
173
|
+
/screen:/,
|
|
174
|
+
'Should show screen version info'
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
// Screen is not installed, should show "not installed"
|
|
179
|
+
assert.match(
|
|
180
|
+
result.stdout,
|
|
181
|
+
/screen: not installed/,
|
|
182
|
+
'Should show "screen: not installed" when screen is unavailable'
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should detect tmux version if installed', () => {
|
|
188
|
+
const result = runCli(['--version']);
|
|
189
|
+
assert.strictEqual(result.exitCode, 0, 'Exit code should be 0');
|
|
190
|
+
|
|
191
|
+
// Check if tmux is actually installed
|
|
192
|
+
try {
|
|
193
|
+
const tmuxVersion = execSync('tmux -V 2>&1', {
|
|
194
|
+
encoding: 'utf8',
|
|
195
|
+
timeout: 5000,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (tmuxVersion) {
|
|
199
|
+
// If tmux is installed, it should not show "not installed"
|
|
200
|
+
assert.doesNotMatch(
|
|
201
|
+
result.stdout,
|
|
202
|
+
/tmux: not installed/,
|
|
203
|
+
'Should not show "tmux: not installed" when tmux is available'
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
} catch {
|
|
207
|
+
// tmux is not installed, should show "not installed"
|
|
208
|
+
assert.match(
|
|
209
|
+
result.stdout,
|
|
210
|
+
/tmux: not installed/,
|
|
211
|
+
'Should show "tmux: not installed" when tmux is unavailable'
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should detect docker version if installed', () => {
|
|
217
|
+
const result = runCli(['--version']);
|
|
218
|
+
assert.strictEqual(result.exitCode, 0, 'Exit code should be 0');
|
|
219
|
+
|
|
220
|
+
// Check if docker is actually installed
|
|
221
|
+
try {
|
|
222
|
+
const dockerVersion = execSync('docker --version 2>&1', {
|
|
223
|
+
encoding: 'utf8',
|
|
224
|
+
timeout: 5000,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (dockerVersion) {
|
|
228
|
+
// If docker is installed, it should not show "not installed"
|
|
229
|
+
assert.doesNotMatch(
|
|
230
|
+
result.stdout,
|
|
231
|
+
/docker: not installed/,
|
|
232
|
+
'Should not show "docker: not installed" when docker is available'
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
} catch {
|
|
236
|
+
// docker is not installed, should show "not installed"
|
|
237
|
+
assert.match(
|
|
238
|
+
result.stdout,
|
|
239
|
+
/docker: not installed/,
|
|
240
|
+
'Should show "docker: not installed" when docker is unavailable'
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe('Verbose mode', () => {
|
|
247
|
+
it('should show verbose output with --version --verbose', () => {
|
|
248
|
+
const result = runCli(['--version', '--verbose']);
|
|
249
|
+
assert.strictEqual(result.exitCode, 0, 'Exit code should be 0');
|
|
250
|
+
assert.match(
|
|
251
|
+
result.stdout,
|
|
252
|
+
/start-command version:/,
|
|
253
|
+
'Should show start-command version'
|
|
254
|
+
);
|
|
255
|
+
assert.match(
|
|
256
|
+
result.stdout,
|
|
257
|
+
/\[verbose\]/,
|
|
258
|
+
'Should show verbose output markers'
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should show verbose output with --version --debug', () => {
|
|
263
|
+
const result = runCli(['--version', '--debug']);
|
|
264
|
+
assert.strictEqual(result.exitCode, 0, 'Exit code should be 0');
|
|
265
|
+
assert.match(
|
|
266
|
+
result.stdout,
|
|
267
|
+
/\[verbose\]/,
|
|
268
|
+
'Should show verbose output with --debug flag'
|
|
269
|
+
);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should show verbose output with START_VERBOSE=1', () => {
|
|
273
|
+
const result = spawnSync('bun', [cliPath, '--version'], {
|
|
274
|
+
encoding: 'utf8',
|
|
275
|
+
timeout: 5000,
|
|
276
|
+
env: {
|
|
277
|
+
...process.env,
|
|
278
|
+
START_VERBOSE: '1',
|
|
279
|
+
START_DISABLE_AUTO_ISSUE: '1',
|
|
280
|
+
START_DISABLE_LOG_UPLOAD: '1',
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
assert.strictEqual(result.status, 0, 'Exit code should be 0');
|
|
284
|
+
assert.match(
|
|
285
|
+
result.stdout,
|
|
286
|
+
/\[verbose\]/,
|
|
287
|
+
'Should show verbose output with START_VERBOSE=1'
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe('Error cases', () => {
|
|
293
|
+
it('should error with "No command provided" for $ --', () => {
|
|
294
|
+
const result = runCli(['--']);
|
|
295
|
+
assert.strictEqual(result.exitCode, 1, 'Exit code should be 1');
|
|
296
|
+
const output = result.stdout + result.stderr;
|
|
297
|
+
assert.match(
|
|
298
|
+
output,
|
|
299
|
+
/No command provided/,
|
|
300
|
+
'Should show "No command provided" error for --'
|
|
301
|
+
);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should error with "No command provided" for no args', () => {
|
|
305
|
+
const result = runCli([]);
|
|
306
|
+
assert.strictEqual(result.exitCode, 0, 'Should show usage and exit 0');
|
|
307
|
+
assert.match(result.stdout, /Usage:/, 'Should show usage message');
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
});
|