start-command 0.7.1 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +11 -17
- package/REQUIREMENTS.md +0 -1
- package/docs/case-studies/issue-22/analysis.md +215 -0
- package/docs/case-studies/issue-25/README.md +225 -0
- package/docs/case-studies/issue-25/issue-data.json +21 -0
- package/experiments/test-screen-tee-debug.js +237 -0
- package/experiments/test-screen-tee-fallback.js +230 -0
- package/package.json +2 -2
- package/src/bin/cli.js +73 -18
- package/src/lib/isolation.js +18 -3
- package/test/isolation.test.js +50 -0
- package/test/version.test.js +46 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# start-command
|
|
2
2
|
|
|
3
|
+
## 0.7.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d058c43: fix: Screen isolation output not captured for quoted commands
|
|
8
|
+
|
|
9
|
+
This fixes issue #25 where commands with quoted strings (e.g., echo "hello") would not show their output when using screen isolation. The fix uses spawnSync with array arguments instead of execSync with a constructed string to avoid shell quoting issues.
|
|
10
|
+
|
|
11
|
+
## 0.7.2
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- fa0fb23: docs: Update documentation to Bun-first approach
|
|
16
|
+
- Remove npm installation option from README.md
|
|
17
|
+
- Update examples to use bun commands instead of npm
|
|
18
|
+
- Change package.json engines from node to bun
|
|
19
|
+
- Update REQUIREMENTS.md to remove Node.js alternative
|
|
20
|
+
|
|
3
21
|
## 0.7.1
|
|
4
22
|
|
|
5
23
|
### Patch 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
|
@@ -210,7 +210,6 @@ Repository not detected - automatic issue creation skipped
|
|
|
210
210
|
### Required
|
|
211
211
|
|
|
212
212
|
- **Bun >= 1.0.0** (primary runtime)
|
|
213
|
-
- Alternatively: Node.js >= 20.0.0 (for compatibility)
|
|
214
213
|
- `child_process` (built-in)
|
|
215
214
|
- `os` (built-in)
|
|
216
215
|
- `fs` (built-in)
|
|
@@ -330,3 +330,218 @@ if ((args.length === 1 && hasVersionFlag) || isOnlyVersionWithSeparator) {
|
|
|
330
330
|
- ✅ CI tests pass
|
|
331
331
|
- ✅ REQUIREMENTS.md updated
|
|
332
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,225 @@
|
|
|
1
|
+
# Case Study: Issue #25 - Screen Isolation Output Missing
|
|
2
|
+
|
|
3
|
+
## Issue Summary
|
|
4
|
+
|
|
5
|
+
**Issue URL:** https://github.com/link-foundation/start/issues/25
|
|
6
|
+
**Date Reported:** 2025-12-23
|
|
7
|
+
**Reporter:** @konard
|
|
8
|
+
**Status:** Resolved
|
|
9
|
+
|
|
10
|
+
### Problem Statement
|
|
11
|
+
|
|
12
|
+
When running commands with screen isolation in attached mode (without `-d`/`--detached`), the command output is not displayed. Specifically:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
$ --isolated screen --verbose -- echo "hello"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Shows `[screen is terminating]` but no "hello" output, even though the command executes successfully with exit code 0.
|
|
19
|
+
|
|
20
|
+
### Environment
|
|
21
|
+
|
|
22
|
+
- **Platform:** macOS 15.7.2
|
|
23
|
+
- **Package:** start-command@0.7.2
|
|
24
|
+
- **Screen version:** macOS bundled 4.00.03 (FAU) 23-Oct-06
|
|
25
|
+
- **Bun Version:** 1.2.20
|
|
26
|
+
- **Architecture:** arm64
|
|
27
|
+
|
|
28
|
+
## Timeline of Events
|
|
29
|
+
|
|
30
|
+
1. User installs start-command: `bun install -g start-command`
|
|
31
|
+
2. Direct command execution works: `$ echo "hello"` shows "hello"
|
|
32
|
+
3. Docker isolation works: `$ --isolated docker --image alpine -- echo "hello"` shows "hello"
|
|
33
|
+
4. **Screen isolation fails**: `$ --isolated screen -- echo "hello"` shows only `[screen is terminating]`
|
|
34
|
+
|
|
35
|
+
## Observed Behavior
|
|
36
|
+
|
|
37
|
+
### Expected
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
$ --isolated screen --verbose -- echo "hello"
|
|
41
|
+
[2025-12-23 20:56:28.265] Starting: echo hello
|
|
42
|
+
|
|
43
|
+
[Isolation] Environment: screen, Mode: attached
|
|
44
|
+
|
|
45
|
+
hello
|
|
46
|
+
|
|
47
|
+
Screen session "screen-1766523388276-4oecji" exited with code 0
|
|
48
|
+
|
|
49
|
+
[2025-12-23 20:56:28.362] Finished
|
|
50
|
+
Exit code: 0
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Actual (Before Fix)
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
$ --isolated screen --verbose -- echo "hello"
|
|
57
|
+
[2025-12-23 20:56:28.265] Starting: echo hello
|
|
58
|
+
|
|
59
|
+
[Isolation] Environment: screen, Mode: attached
|
|
60
|
+
|
|
61
|
+
[screen is terminating]
|
|
62
|
+
|
|
63
|
+
Screen session "screen-1766523388276-4oecji" exited with code 0
|
|
64
|
+
|
|
65
|
+
[2025-12-23 20:56:28.362] Finished
|
|
66
|
+
Exit code: 0
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Notice:** No "hello" output in the screen isolation case, though exit code is 0.
|
|
70
|
+
|
|
71
|
+
## Root Cause Analysis
|
|
72
|
+
|
|
73
|
+
### PRIMARY ROOT CAUSE: Shell Quoting Issues with execSync
|
|
74
|
+
|
|
75
|
+
The issue was in the `runScreenWithLogCapture` function in `src/lib/isolation.js`.
|
|
76
|
+
|
|
77
|
+
**The Problematic Code:**
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
execSync(`screen ${screenArgs.map((a) => `"${a}"`).join(' ')}`, {
|
|
81
|
+
stdio: 'inherit',
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This code constructs a shell command string by wrapping each argument in double quotes. However, when the command being executed already contains double quotes (like `echo "hello"`), the nested quoting breaks the shell parsing.
|
|
86
|
+
|
|
87
|
+
**Example of Broken Command:**
|
|
88
|
+
|
|
89
|
+
For the command `echo "hello"`:
|
|
90
|
+
|
|
91
|
+
1. `effectiveCommand` becomes: `(echo "hello") 2>&1 | tee "/tmp/...log"`
|
|
92
|
+
2. `screenArgs` is: `['-dmS', 'session-name', '/bin/sh', '-c', '(echo "hello") 2>&1 | tee "/tmp/...log"']`
|
|
93
|
+
3. After wrapping with `"${a}"`:
|
|
94
|
+
```
|
|
95
|
+
screen "-dmS" "session-name" "/bin/sh" "-c" "(echo "hello") 2>&1 | tee "/tmp/...log""
|
|
96
|
+
```
|
|
97
|
+
4. **Problem**: The nested double quotes cause shell parsing errors - the shell sees `hello` as a separate token!
|
|
98
|
+
|
|
99
|
+
**Why Simple Commands Worked:**
|
|
100
|
+
|
|
101
|
+
Commands without quotes (like `echo hello` without the quotes) worked because there was no quoting conflict.
|
|
102
|
+
|
|
103
|
+
### Experimental Evidence
|
|
104
|
+
|
|
105
|
+
We created `experiments/test-screen-tee-debug.js` to test different approaches:
|
|
106
|
+
|
|
107
|
+
| Test | Command | Result |
|
|
108
|
+
| ------ | ----------------------------------------------- | -------------------------------- |
|
|
109
|
+
| Test 3 | `echo "hello"` (simple) | SUCCESS |
|
|
110
|
+
| Test 4 | `echo "hello from attached mode"` (with spaces) | **FAILED** - No log file created |
|
|
111
|
+
| Test 5 | Same with escaped quotes | SUCCESS |
|
|
112
|
+
| Test 6 | Using `spawnSync` with array | **SUCCESS** |
|
|
113
|
+
|
|
114
|
+
The experiments clearly showed that:
|
|
115
|
+
|
|
116
|
+
1. Commands with spaces in quoted strings fail with `execSync` + string construction
|
|
117
|
+
2. Using `spawnSync` with an array of arguments works correctly
|
|
118
|
+
|
|
119
|
+
### Why spawnSync Works
|
|
120
|
+
|
|
121
|
+
Node.js/Bun's `spawnSync` with array arguments:
|
|
122
|
+
|
|
123
|
+
- Passes arguments directly to the process without shell interpretation
|
|
124
|
+
- Each array element becomes a separate argv entry
|
|
125
|
+
- No shell quoting issues - the quotes in the command are preserved as-is
|
|
126
|
+
|
|
127
|
+
## The Solution
|
|
128
|
+
|
|
129
|
+
### Code Changes in `src/lib/isolation.js`
|
|
130
|
+
|
|
131
|
+
1. **Added `spawnSync` import:**
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
const { execSync, spawn, spawnSync } = require('child_process');
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
2. **Replaced `execSync` with `spawnSync` in two locations:**
|
|
138
|
+
|
|
139
|
+
**Location 1: `runScreenWithLogCapture` function (attached mode with log capture)**
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
// Before (broken):
|
|
143
|
+
execSync(`screen ${screenArgs.map((a) => `"${a}"`).join(' ')}`, {
|
|
144
|
+
stdio: 'inherit',
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// After (fixed):
|
|
148
|
+
const result = spawnSync('screen', screenArgs, {
|
|
149
|
+
stdio: 'inherit',
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (result.error) {
|
|
153
|
+
throw result.error;
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Location 2: `runInScreen` function (detached mode)**
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
// Same pattern - replaced execSync with spawnSync
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Testing Strategy
|
|
164
|
+
|
|
165
|
+
### New Regression Tests Added
|
|
166
|
+
|
|
167
|
+
Two new tests were added to `test/isolation.test.js`:
|
|
168
|
+
|
|
169
|
+
1. **Test: should capture output from commands with quoted strings (issue #25)**
|
|
170
|
+
- Tests: `echo "hello"`
|
|
171
|
+
- Verifies the exact scenario from issue #25
|
|
172
|
+
|
|
173
|
+
2. **Test: should capture output from commands with complex quoted strings**
|
|
174
|
+
- Tests: `echo "hello from attached mode"`
|
|
175
|
+
- Verifies commands with spaces inside quotes work
|
|
176
|
+
|
|
177
|
+
### Test Results
|
|
178
|
+
|
|
179
|
+
All 25 isolation tests pass:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
bun test test/isolation.test.js
|
|
183
|
+
|
|
184
|
+
Captured quoted output: "hello"
|
|
185
|
+
Captured complex quote output: "hello from attached mode"
|
|
186
|
+
|
|
187
|
+
25 pass
|
|
188
|
+
0 fail
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Key Learnings
|
|
192
|
+
|
|
193
|
+
1. **String construction for shell commands is fragile**: When building shell command strings, nested quoting can cause silent failures.
|
|
194
|
+
|
|
195
|
+
2. **Prefer array-based process spawning**: `spawnSync`/`spawn` with arrays are more robust than `execSync` with constructed strings.
|
|
196
|
+
|
|
197
|
+
3. **Test with varied input**: Simple commands may work while complex ones fail - test with real-world examples including quotes and spaces.
|
|
198
|
+
|
|
199
|
+
4. **Debug systematically**: Creating experiments (`test-screen-tee-debug.js`) helped isolate the exact failure mode.
|
|
200
|
+
|
|
201
|
+
## Connection to Previous Issues
|
|
202
|
+
|
|
203
|
+
This issue is related to Issue #15 (Screen Isolation Not Working As Expected) which addressed a different root cause:
|
|
204
|
+
|
|
205
|
+
- Issue #15: macOS Screen version incompatibility (lacking `-Logfile` option)
|
|
206
|
+
- Issue #25: Shell quoting issues in the tee fallback approach (used for older screen versions)
|
|
207
|
+
|
|
208
|
+
Both issues together ensure screen isolation works on:
|
|
209
|
+
|
|
210
|
+
- Modern screen (>= 4.5.1) with native `-Logfile` support
|
|
211
|
+
- Older screen (< 4.5.1, like macOS bundled 4.0.3) with tee fallback
|
|
212
|
+
|
|
213
|
+
## Files Modified
|
|
214
|
+
|
|
215
|
+
1. `src/lib/isolation.js` - Core fix: use `spawnSync` instead of `execSync`
|
|
216
|
+
2. `test/isolation.test.js` - Added 2 regression tests for issue #25
|
|
217
|
+
3. `experiments/test-screen-tee-fallback.js` - Experiment script (new)
|
|
218
|
+
4. `experiments/test-screen-tee-debug.js` - Debug experiment script (new)
|
|
219
|
+
|
|
220
|
+
## References
|
|
221
|
+
|
|
222
|
+
- [Node.js child_process.spawnSync](https://nodejs.org/api/child_process.html#child_processspawnsynccommand-args-options)
|
|
223
|
+
- [GNU Screen Manual](https://www.gnu.org/software/screen/manual/screen.html)
|
|
224
|
+
- [Issue #15 Case Study](../issue-15/README.md)
|
|
225
|
+
- [Issue #22 Case Study](../issue-22/analysis.md)
|
|
@@ -0,0 +1,21 @@
|
|
|
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.2 with binaries:\n - $\n\n1 package installed [2.34s]\nkonard@MacBook-Pro-Konstantin ~ % $ --version \nstart-command version: 0.7.2\n\nOS: darwin\nOS Version: 15.7.2\nBun Version: 1.2.20\nArchitecture: arm64\n\nIsolation tools:\n screen: Screen version 4.00.03 (FAU) 23-Oct-06\n tmux: not installed\n docker: Docker version 28.5.1, build e180ab8\nkonard@MacBook-Pro-Konstantin ~ % $ --version -- \nstart-command version: 0.7.2\n\nOS: darwin\nOS Version: 15.7.2\nBun Version: 1.2.20\nArchitecture: arm64\n\nIsolation tools:\n screen: Screen version 4.00.03 (FAU) 23-Oct-06\n tmux: not installed\n docker: Docker version 28.5.1, build e180ab8\nkonard@MacBook-Pro-Konstantin ~ % $ --isolated screen --verbose -- echo \"hello\"\n[2025-12-23 20:56:28.265] Starting: echo hello\n\n[Isolation] Environment: screen, Mode: attached\n\n[screen is terminating]\n\nScreen session \"screen-1766523388276-4oecji\" exited with code 0\n\n[2025-12-23 20:56:28.362] Finished\nExit code: 0\nLog saved: /var/folders/cl/831lqjgd58v5mb_m74cfdfcw0000gn/T/start-command-screen-1766523388265-j5puqf.log\nkonard@MacBook-Pro-Konstantin ~ % $ echo \"hello\" \n[2025-12-23 20:56:37.680] Starting: echo hello\n\nhello\n\n[2025-12-23 20:56:37.688] Finished\nExit code: 0\nLog saved: /var/folders/cl/831lqjgd58v5mb_m74cfdfcw0000gn/T/start-command-1766523397680-fb62fx.log\nkonard@MacBook-Pro-Konstantin ~ % $ --isolated docker --image alpine -- echo \"hello\"\n[2025-12-23 20:56:45.619] Starting: echo hello\n\n[Isolation] Environment: docker, Mode: attached\n[Isolation] Image: alpine\n\nhello\n\nDocker container \"docker-1766523405627-qgv7h1\" exited with code 0\n\n[2025-12-23 20:56:47.091] Finished\nExit code: 0\nLog saved: /var/folders/cl/831lqjgd58v5mb_m74cfdfcw0000gn/T/start-command-docker-1766523405619-8izdfr.log\nkonard@MacBook-Pro-Konstantin ~ % \n```\n\nMake sure we have test on macOS that does specifically reproduces this exact error. After that it must be fixed. And that test will guarantee we will never get regression again.\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
|
+
"createdAt": "2025-12-23T20:57:23Z",
|
|
10
|
+
"labels": [
|
|
11
|
+
{
|
|
12
|
+
"id": "LA_kwDOP85RQM8AAAACMMqWww",
|
|
13
|
+
"name": "bug",
|
|
14
|
+
"description": "Something isn't working",
|
|
15
|
+
"color": "d73a4a"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"number": 25,
|
|
19
|
+
"state": "OPEN",
|
|
20
|
+
"title": "We don't get `Hello` output from `$ --isolated screen --verbose -- echo \"hello\"` command"
|
|
21
|
+
}
|