start-command 0.7.6 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,297 @@
1
+ # Architecture
2
+
3
+ This document describes the architecture of the `$` command (start-command).
4
+
5
+ ## Overview
6
+
7
+ The start-command is a CLI tool that wraps shell commands to provide automatic logging, error reporting, natural language aliases, and process isolation capabilities.
8
+
9
+ ```
10
+ ┌─────────────────────────────────────────────────────────────────────┐
11
+ │ User Command │
12
+ │ $ [options] command [args] │
13
+ └──────────────────────────────┬──────────────────────────────────────┘
14
+
15
+
16
+ ┌─────────────────────────────────────────────────────────────────────┐
17
+ │ CLI Entry Point │
18
+ │ src/bin/cli.js │
19
+ ├─────────────────────────────────────────────────────────────────────┤
20
+ │ • Parse command line arguments │
21
+ │ • Handle --version, --help flags │
22
+ │ • Route to isolation or direct execution │
23
+ └──────────────────────────────┬──────────────────────────────────────┘
24
+
25
+ ┌────────────────┴────────────────┐
26
+ │ │
27
+ ▼ ▼
28
+ ┌─────────────────────────┐ ┌─────────────────────────────────────┐
29
+ │ Direct Execution │ │ Isolated Execution │
30
+ │ (no --isolated) │ │ (--isolated screen/tmux/docker) │
31
+ ├─────────────────────────┤ ├─────────────────────────────────────┤
32
+ │ • Spawn shell process │ │ src/lib/isolation.js │
33
+ │ • Capture stdout/stderr │ │ • runInScreen() │
34
+ │ • Log to temp file │ │ • runInTmux() │
35
+ │ • Report failures │ │ • runInDocker() │
36
+ └─────────────────────────┘ └─────────────────────────────────────┘
37
+ ```
38
+
39
+ ## Core Modules
40
+
41
+ ### 1. CLI Entry Point (`src/bin/cli.js`)
42
+
43
+ The main entry point that:
44
+
45
+ - Parses command line arguments using `args-parser.js`
46
+ - Processes command substitutions using `substitution.js`
47
+ - Routes execution to direct mode or isolation mode
48
+ - Handles logging and error reporting
49
+
50
+ ### 2. Argument Parser (`src/lib/args-parser.js`)
51
+
52
+ Parses wrapper options and extracts the command to execute:
53
+
54
+ ```javascript
55
+ {
56
+ isolated: null, // 'screen' | 'tmux' | 'docker' | null
57
+ attached: false, // Run in attached/foreground mode
58
+ detached: false, // Run in detached/background mode
59
+ session: null, // Custom session name
60
+ image: null, // Docker image name
61
+ keepAlive: false, // Keep environment alive after command exits
62
+ }
63
+ ```
64
+
65
+ ### 3. Substitution Engine (`src/lib/substitution.js`)
66
+
67
+ Provides natural language command aliases:
68
+
69
+ - Loads patterns from `substitutions.lino`
70
+ - Matches user input against patterns with variables
71
+ - Returns substituted command or original if no match
72
+
73
+ ### 4. Isolation Module (`src/lib/isolation.js`)
74
+
75
+ Handles process isolation in terminal multiplexers and containers:
76
+
77
+ ```
78
+ ┌────────────────────────────────────────────────────────────┐
79
+ │ runIsolated() │
80
+ │ (dispatcher function) │
81
+ └───────────────┬───────────────┬───────────────┬────────────┘
82
+ │ │ │
83
+ ┌───────▼───────┐ ┌─────▼─────┐ ┌───────▼───────┐
84
+ │ runInScreen │ │runInTmux │ │ runInDocker │
85
+ │ │ │ │ │ │
86
+ │ GNU Screen │ │ tmux │ │ Docker │
87
+ │ multiplexer │ │ terminal │ │ containers │
88
+ └───────────────┘ └───────────┘ └───────────────┘
89
+ ```
90
+
91
+ ## Isolation Architecture
92
+
93
+ ### Execution Modes
94
+
95
+ | Mode | Description | Default Behavior |
96
+ | ------------ | ------------------------------------------- | ------------------------------ |
97
+ | Attached | Command runs in foreground, interactive | Session exits after completion |
98
+ | Detached | Command runs in background | Session exits after completion |
99
+ | + Keep-Alive | Session stays alive after command completes | Requires `--keep-alive` flag |
100
+
101
+ ### Auto-Exit Behavior
102
+
103
+ By default, all isolation environments automatically exit after command completion:
104
+
105
+ ```
106
+ ┌───────────────────────────────────────────────────────────────┐
107
+ │ Default (keepAlive=false) │
108
+ ├───────────────────────────────────────────────────────────────┤
109
+ │ 1. Start isolation environment │
110
+ │ 2. Execute command │
111
+ │ 3. Capture output (if attached mode) │
112
+ │ 4. Environment exits automatically │
113
+ │ 5. Resources freed │
114
+ └───────────────────────────────────────────────────────────────┘
115
+
116
+ ┌───────────────────────────────────────────────────────────────┐
117
+ │ With --keep-alive │
118
+ ├───────────────────────────────────────────────────────────────┤
119
+ │ 1. Start isolation environment │
120
+ │ 2. Execute command │
121
+ │ 3. Command completes │
122
+ │ 4. Shell stays running in session │
123
+ │ 5. User can reattach and interact │
124
+ └───────────────────────────────────────────────────────────────┘
125
+ ```
126
+
127
+ ### Screen Isolation
128
+
129
+ ```
130
+ ┌─────────────────────────────────────────────────────────────────┐
131
+ │ Screen Execution Flow │
132
+ ├─────────────────────────────────────────────────────────────────┤
133
+ │ │
134
+ │ Attached Mode: │
135
+ │ ┌─────────────────────────────────────────────────────────┐ │
136
+ │ │ Uses detached mode with log capture internally │ │
137
+ │ │ • Start: screen -dmS <session> -L -Logfile <log> │ │
138
+ │ │ • Poll for session completion │ │
139
+ │ │ • Read and display captured output │ │
140
+ │ │ • Clean up log file │ │
141
+ │ └─────────────────────────────────────────────────────────┘ │
142
+ │ │
143
+ │ Detached Mode: │
144
+ │ ┌─────────────────────────────────────────────────────────┐ │
145
+ │ │ • Without keep-alive: screen -dmS <session> sh -c cmd │ │
146
+ │ │ • With keep-alive: screen -dmS <session> sh -c "cmd; sh"│ │
147
+ │ └─────────────────────────────────────────────────────────┘ │
148
+ │ │
149
+ └─────────────────────────────────────────────────────────────────┘
150
+ ```
151
+
152
+ ### tmux Isolation
153
+
154
+ ```
155
+ ┌─────────────────────────────────────────────────────────────────┐
156
+ │ tmux Execution Flow │
157
+ ├─────────────────────────────────────────────────────────────────┤
158
+ │ │
159
+ │ Attached Mode: │
160
+ │ ┌─────────────────────────────────────────────────────────┐ │
161
+ │ │ • tmux new-session -s <session> <command> │ │
162
+ │ │ • Interactive, exits when command completes │ │
163
+ │ └─────────────────────────────────────────────────────────┘ │
164
+ │ │
165
+ │ Detached Mode: │
166
+ │ ┌─────────────────────────────────────────────────────────┐ │
167
+ │ │ • Without keep-alive: tmux new-session -d -s <session> │ │
168
+ │ │ • With keep-alive: command followed by shell exec │ │
169
+ │ └─────────────────────────────────────────────────────────┘ │
170
+ │ │
171
+ └─────────────────────────────────────────────────────────────────┘
172
+ ```
173
+
174
+ ### Docker Isolation
175
+
176
+ ```
177
+ ┌─────────────────────────────────────────────────────────────────┐
178
+ │ Docker Execution Flow │
179
+ ├─────────────────────────────────────────────────────────────────┤
180
+ │ │
181
+ │ Attached Mode: │
182
+ │ ┌─────────────────────────────────────────────────────────┐ │
183
+ │ │ • docker run -it --rm --name <name> <image> sh -c cmd │ │
184
+ │ │ • Interactive, container auto-removed on exit │ │
185
+ │ └─────────────────────────────────────────────────────────┘ │
186
+ │ │
187
+ │ Detached Mode: │
188
+ │ ┌─────────────────────────────────────────────────────────┐ │
189
+ │ │ • Without keep-alive: docker run -d --name <name> ... │ │
190
+ │ │ • With keep-alive: command followed by shell exec │ │
191
+ │ └─────────────────────────────────────────────────────────┘ │
192
+ │ │
193
+ └─────────────────────────────────────────────────────────────────┘
194
+ ```
195
+
196
+ ## Logging Architecture
197
+
198
+ ```
199
+ ┌─────────────────────────────────────────────────────────────────┐
200
+ │ Logging Flow │
201
+ ├─────────────────────────────────────────────────────────────────┤
202
+ │ │
203
+ │ ┌─────────────┐ ┌──────────────┐ ┌───────────────────┐ │
204
+ │ │ Command │───▶│ Capture │───▶│ Write to │ │
205
+ │ │ Execution │ │ stdout/stderr│ │ Temp Log File │ │
206
+ │ └─────────────┘ └──────────────┘ └───────────────────┘ │
207
+ │ │
208
+ │ Log File Format: │
209
+ │ ┌─────────────────────────────────────────────────────────┐ │
210
+ │ │ === Start Command Log === │ │
211
+ │ │ Timestamp: 2024-01-15 10:30:45 │ │
212
+ │ │ Command: <command> │ │
213
+ │ │ Shell: /bin/bash │ │
214
+ │ │ Platform: linux │ │
215
+ │ │ ================================================== │ │
216
+ │ │ <command output> │ │
217
+ │ │ ================================================== │ │
218
+ │ │ Finished: 2024-01-15 10:30:46 │ │
219
+ │ │ Exit Code: 0 │ │
220
+ │ └─────────────────────────────────────────────────────────┘ │
221
+ │ │
222
+ └─────────────────────────────────────────────────────────────────┘
223
+ ```
224
+
225
+ ## File Structure
226
+
227
+ ```
228
+ start-command/
229
+ ├── src/
230
+ │ ├── bin/
231
+ │ │ └── cli.js # Main entry point
232
+ │ └── lib/
233
+ │ ├── args-parser.js # Argument parsing
234
+ │ ├── isolation.js # Isolation backends
235
+ │ ├── substitution.js # Command aliases
236
+ │ └── substitutions.lino # Alias patterns
237
+ ├── test/
238
+ │ ├── cli.test.js # CLI tests
239
+ │ ├── isolation.test.js # Isolation tests
240
+ │ ├── args-parser.test.js # Parser tests
241
+ │ └── substitution.test.js # Substitution tests
242
+ ├── docs/
243
+ │ ├── PIPES.md # Piping documentation
244
+ │ └── USAGE.md # Usage examples
245
+ ├── experiments/ # Experimental scripts
246
+ ├── REQUIREMENTS.md # Requirements specification
247
+ ├── ARCHITECTURE.md # This file
248
+ └── README.md # Project overview
249
+ ```
250
+
251
+ ## Design Decisions
252
+
253
+ ### 1. Auto-Exit by Default
254
+
255
+ All isolation environments exit automatically after command completion to:
256
+
257
+ - Prevent resource leaks from orphaned sessions
258
+ - Ensure consistent behavior across backends
259
+ - Match user expectations for command execution
260
+
261
+ ### 2. Log Capture in Attached Screen Mode
262
+
263
+ Screen's attached mode uses internal detached mode with log capture because:
264
+
265
+ - Direct attached mode loses output for quick commands
266
+ - Screen's virtual terminal is destroyed before output is visible
267
+ - Log capture ensures reliable output preservation
268
+
269
+ ### 3. Keep-Alive as Opt-In
270
+
271
+ The `--keep-alive` flag is disabled by default because:
272
+
273
+ - Most use cases don't require persistent sessions
274
+ - Prevents accidental resource consumption
275
+ - Explicit opt-in for advanced workflows
276
+
277
+ ### 4. Uniform Backend Interface
278
+
279
+ All isolation backends share a consistent interface:
280
+
281
+ ```javascript
282
+ async function runInBackend(command, options) {
283
+ return {
284
+ success: boolean,
285
+ sessionName: string,
286
+ message: string,
287
+ exitCode?: number,
288
+ output?: string
289
+ };
290
+ }
291
+ ```
292
+
293
+ This enables:
294
+
295
+ - Easy addition of new backends
296
+ - Consistent error handling
297
+ - Unified logging format
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # start-command
2
2
 
3
+ ## 0.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c484149: Add --keep-alive option for isolation environments
8
+ - All isolation environments (screen, tmux, docker) now automatically exit after command completion by default
9
+ - New --keep-alive (-k) flag keeps the isolation environment running after command completes
10
+ - Add ARCHITECTURE.md documentation describing system design
11
+ - Update REQUIREMENTS.md with new option and auto-exit behavior documentation
12
+
3
13
  ## 0.7.6
4
14
 
5
15
  ### Patch Changes
package/README.md CHANGED
@@ -162,16 +162,37 @@ $ -i tmux -s my-session -d bun start
162
162
 
163
163
  #### Isolation Options
164
164
 
165
- | Option | Description |
166
- | ---------------- | -------------------------------------------- |
167
- | `--isolated, -i` | Isolation backend (screen, tmux, docker) |
168
- | `--attached, -a` | Run in attached/foreground mode (default) |
169
- | `--detached, -d` | Run in detached/background mode |
170
- | `--session, -s` | Custom session/container name |
171
- | `--image` | Docker image (required for docker isolation) |
165
+ | Option | Description |
166
+ | -------------------------------- | ----------------------------------------------------- |
167
+ | `--isolated, -i` | Isolation backend (screen, tmux, docker) |
168
+ | `--attached, -a` | Run in attached/foreground mode (default) |
169
+ | `--detached, -d` | Run in detached/background mode |
170
+ | `--session, -s` | Custom session/container name |
171
+ | `--image` | Docker image (required for docker isolation) |
172
+ | `--keep-alive, -k` | Keep session alive after command completes |
173
+ | `--auto-remove-docker-container` | Auto-remove docker container after exit (docker only) |
172
174
 
173
175
  **Note:** Using both `--attached` and `--detached` together will result in an error - you must choose one mode.
174
176
 
177
+ #### Auto-Exit Behavior
178
+
179
+ By default, all isolation environments (screen, tmux, docker) automatically exit after the target command completes. This ensures resources are freed immediately and provides uniform behavior across all backends.
180
+
181
+ Use `--keep-alive` (`-k`) to keep the session running after command completion:
182
+
183
+ ```bash
184
+ # Default: session exits after command completes
185
+ $ -i screen -d -- echo "hello"
186
+ # Session will exit automatically after command completes.
187
+
188
+ # With --keep-alive: session stays running for interaction
189
+ $ -i screen -d -k -- echo "hello"
190
+ # Session will stay alive after command completes.
191
+ # You can reattach with: screen -r <session-name>
192
+ ```
193
+
194
+ For Docker containers, by default the container filesystem is preserved (appears in `docker ps -a`) so you can re-enter it later. Use `--auto-remove-docker-container` to remove the container immediately after exit.
195
+
175
196
  ### Graceful Degradation
176
197
 
177
198
  The tool works in any environment:
package/REQUIREMENTS.md CHANGED
@@ -142,6 +142,8 @@ Support two patterns for passing wrapper options:
142
142
  - `--detached, -d`: Run in detached/background mode
143
143
  - `--session, -s <name>`: Custom session name
144
144
  - `--image <image>`: Docker image (required for docker backend)
145
+ - `--keep-alive, -k`: Keep isolation environment alive after command exits (disabled by default)
146
+ - `--auto-remove-docker-container`: Automatically remove docker container after exit (disabled by default, only valid with --isolated docker)
145
147
 
146
148
  #### 6.3 Supported Backends
147
149
 
@@ -155,7 +157,30 @@ Support two patterns for passing wrapper options:
155
157
  - **Detached mode**: Command runs in background, session info displayed for reattachment
156
158
  - **Conflict handling**: If both --attached and --detached are specified, show error asking user to choose one
157
159
 
158
- #### 6.5 Graceful Degradation
160
+ #### 6.5 Auto-Exit Behavior
161
+
162
+ By default, all isolation environments (screen, tmux, docker) automatically exit after the target command completes execution. This ensures:
163
+
164
+ - Resources are freed immediately after command execution
165
+ - No orphaned sessions/containers remain running
166
+ - Uniform behavior across all isolation backends
167
+ - Command output is still captured and logged before exit
168
+
169
+ The `--keep-alive` flag can be used to override this behavior and keep the isolation environment running after command completion, useful for debugging or interactive workflows.
170
+
171
+ **Docker Container Filesystem Preservation:**
172
+ By default, when using Docker isolation, the container filesystem is preserved after the container exits. This allows you to re-enter the container and access any files created during command execution, which is useful for retrieving additional output files or debugging. The container appears in `docker ps -a` in an "exited" state but is not removed.
173
+
174
+ The `--auto-remove-docker-container` flag enables automatic removal of the container after exit, which is useful when you don't need to preserve the container filesystem and want to clean up resources completely. When this flag is enabled:
175
+
176
+ - The container is removed immediately after exiting (using docker's `--rm` flag)
177
+ - The container will not appear in `docker ps -a` after command completion
178
+ - You cannot re-enter the container to access files
179
+ - Resources are freed more aggressively
180
+
181
+ Note: `--auto-remove-docker-container` is only valid with `--isolated docker` and is independent of the `--keep-alive` flag.
182
+
183
+ #### 6.6 Graceful Degradation
159
184
 
160
185
  - If isolation backend is not installed, show informative error with installation instructions
161
186
  - If session creation fails, report error with details
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "start-command",
3
- "version": "0.7.6",
3
+ "version": "0.9.0",
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": {
package/src/bin/cli.js CHANGED
@@ -228,6 +228,12 @@ function printUsage() {
228
228
  console.log(
229
229
  ' --image <image> Docker image (required for docker isolation)'
230
230
  );
231
+ console.log(
232
+ ' --keep-alive, -k Keep isolation environment alive after command exits'
233
+ );
234
+ console.log(
235
+ ' --auto-remove-docker-container Automatically remove docker container after exit (disabled by default)'
236
+ );
231
237
  console.log(' --version, -v Show version information');
232
238
  console.log('');
233
239
  console.log('Examples:');
@@ -360,6 +366,8 @@ async function runWithIsolation(options, cmd) {
360
366
  session: options.session,
361
367
  image: options.image,
362
368
  detached: mode === 'detached',
369
+ keepAlive: options.keepAlive,
370
+ autoRemoveDockerContainer: options.autoRemoveDockerContainer,
363
371
  });
364
372
 
365
373
  // Get exit code
@@ -6,11 +6,13 @@
6
6
  * 2. $ [wrapper-options] command [command-options]
7
7
  *
8
8
  * Wrapper Options:
9
- * --isolated, -i <backend> Run in isolated environment (screen, tmux, docker)
10
- * --attached, -a Run in attached mode (foreground)
11
- * --detached, -d Run in detached mode (background)
12
- * --session, -s <name> Session name for isolation
13
- * --image <image> Docker image (required for docker isolation)
9
+ * --isolated, -i <backend> Run in isolated environment (screen, tmux, docker)
10
+ * --attached, -a Run in attached mode (foreground)
11
+ * --detached, -d Run in detached mode (background)
12
+ * --session, -s <name> Session name for isolation
13
+ * --image <image> Docker image (required for docker isolation)
14
+ * --keep-alive, -k Keep isolation environment alive after command exits
15
+ * --auto-remove-docker-container Automatically remove docker container after exit (disabled by default)
14
16
  */
15
17
 
16
18
  // Debug mode from environment
@@ -34,6 +36,8 @@ function parseArgs(args) {
34
36
  detached: false, // Run in detached mode
35
37
  session: null, // Session name
36
38
  image: null, // Docker image
39
+ keepAlive: false, // Keep environment alive after command exits
40
+ autoRemoveDockerContainer: false, // Auto-remove docker container after exit
37
41
  };
38
42
 
39
43
  let commandArgs = [];
@@ -171,6 +175,18 @@ function parseOption(args, index, options) {
171
175
  return 1;
172
176
  }
173
177
 
178
+ // --keep-alive or -k
179
+ if (arg === '--keep-alive' || arg === '-k') {
180
+ options.keepAlive = true;
181
+ return 1;
182
+ }
183
+
184
+ // --auto-remove-docker-container
185
+ if (arg === '--auto-remove-docker-container') {
186
+ options.autoRemoveDockerContainer = true;
187
+ return 1;
188
+ }
189
+
174
190
  // Not a recognized wrapper option
175
191
  return 0;
176
192
  }
@@ -213,6 +229,18 @@ function validateOptions(options) {
213
229
  if (options.image && options.isolated !== 'docker') {
214
230
  throw new Error('--image option is only valid with --isolated docker');
215
231
  }
232
+
233
+ // Keep-alive is only valid with isolation
234
+ if (options.keepAlive && !options.isolated) {
235
+ throw new Error('--keep-alive option is only valid with --isolated');
236
+ }
237
+
238
+ // Auto-remove-docker-container is only valid with docker isolation
239
+ if (options.autoRemoveDockerContainer && options.isolated !== 'docker') {
240
+ throw new Error(
241
+ '--auto-remove-docker-container option is only valid with --isolated docker'
242
+ );
243
+ }
216
244
  }
217
245
 
218
246
  /**