start-command 0.3.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # start-command
2
2
 
3
+ ## 0.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Test patch release
8
+
9
+ ## 0.5.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 95d8760: Unify output experience for isolation mode
14
+ - Change terminology from "Backend" to "Environment" in isolation output
15
+ - Add unified logging with timestamps for isolation modes (screen, tmux, docker, zellij)
16
+ - Save log files for all execution modes with consistent format
17
+ - Display start/end timestamps, exit code, and log file path uniformly across all modes
18
+
19
+ ## 0.4.1
20
+
21
+ ### Patch Changes
22
+
23
+ - 73635f9: Make it bun first - update shebangs and installation docs
24
+
25
+ ## 0.4.0
26
+
27
+ ### Minor Changes
28
+
29
+ - e8bec3c: Add process isolation support with --isolated option
30
+
31
+ This release adds the ability to run commands in isolated environments:
32
+
33
+ **New Features:**
34
+ - `--isolated` / `-i` option to run commands in screen, tmux, zellij, or docker
35
+ - `--attached` / `-a` and `--detached` / `-d` modes for foreground/background execution
36
+ - `--session` / `-s` option for custom session names
37
+ - `--image` option for Docker container image specification
38
+ - Two command syntax patterns: `$ [options] -- [command]` or `$ [options] command`
39
+
40
+ **Supported Backends:**
41
+ - GNU Screen - classic terminal multiplexer
42
+ - tmux - modern terminal multiplexer
43
+ - zellij - modern terminal workspace
44
+ - Docker - container isolation
45
+
46
+ **Examples:**
47
+
48
+ ```bash
49
+ $ --isolated tmux -- npm start
50
+ $ -i screen -d npm start
51
+ $ --isolated docker --image node:20 -- npm install
52
+ ```
53
+
3
54
  ## 0.3.1
4
55
 
5
56
  ### Patch Changes
package/README.md CHANGED
@@ -4,6 +4,14 @@ Gamification of coding - execute any command with automatic logging and ability
4
4
 
5
5
  ## Installation
6
6
 
7
+ We recommend using [Bun](https://bun.sh) for the best performance:
8
+
9
+ ```bash
10
+ bun install -g start-command
11
+ ```
12
+
13
+ Or using npm:
14
+
7
15
  ```bash
8
16
  npm install -g start-command
9
17
  ```
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  /**
3
3
  * Debug script to understand regex generation
4
4
  */
@@ -0,0 +1,27 @@
1
+ #!/bin/bash
2
+ # Experiment to understand screen output behavior
3
+
4
+ echo "=== Test 1: Direct screen with command ==="
5
+ screen -S test1 /bin/sh -c 'echo "hello from test1"'
6
+
7
+ echo ""
8
+ echo "=== Test 2: Screen with -L logging ==="
9
+ cd /tmp
10
+ screen -L -Logfile screen-test.log -S test2 /bin/sh -c 'echo "hello from test2"'
11
+ echo "Log contents:"
12
+ cat /tmp/screen-test.log 2>/dev/null || echo "No log file created"
13
+
14
+ echo ""
15
+ echo "=== Test 3: Screen detached then capture ==="
16
+ screen -dmS test3 /bin/sh -c 'echo "hello from test3" > /tmp/screen-test3-out.txt'
17
+ sleep 0.5
18
+ echo "Output from test3:"
19
+ cat /tmp/screen-test3-out.txt 2>/dev/null || echo "No output file"
20
+
21
+ echo ""
22
+ echo "=== Test 4: Screen with wrap using script command ==="
23
+ script -q /dev/null -c 'screen -S test4 /bin/sh -c "echo hello from test4"' 2>/dev/null || echo "Script method failed"
24
+
25
+ echo ""
26
+ echo "Cleanup"
27
+ rm -f /tmp/screen-test.log /tmp/screen-test3-out.txt
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  /**
3
3
  * Test script for the substitution engine
4
4
  * Tests pattern matching with various inputs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "start-command",
3
- "version": "0.3.1",
3
+ "version": "0.5.1",
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": {
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  /**
4
4
  * Custom changeset version script that ensures package-lock.json is synchronized
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  /**
4
4
  * Check for files exceeding the maximum allowed line count
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  /**
4
4
  * Create GitHub Release from CHANGELOG.md
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  /**
4
4
  * Create a changeset file for manual releases
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  /**
4
4
  * Format GitHub release notes using the format-release-notes.mjs script
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  /**
4
4
  * Script to format GitHub release notes with proper formatting:
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  /**
4
4
  * Instant version bump script for manual releases
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  /**
4
4
  * Publish to npm using OIDC trusted publishing
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  /**
4
4
  * Update npm for OIDC trusted publishing
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  /**
4
4
  * Validate changeset for CI - ensures exactly one valid changeset exists
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  /**
4
4
  * Version packages and commit to main
package/src/bin/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  const { spawn, execSync } = require('child_process');
4
4
  const process = require('process');
@@ -13,7 +13,14 @@ const {
13
13
  hasIsolation,
14
14
  getEffectiveMode,
15
15
  } = require('../lib/args-parser');
16
- const { runIsolated } = require('../lib/isolation');
16
+ const {
17
+ runIsolated,
18
+ getTimestamp,
19
+ createLogHeader,
20
+ createLogFooter,
21
+ writeLogFile,
22
+ createLogPath,
23
+ } = require('../lib/isolation');
17
24
 
18
25
  // Configuration from environment variables
19
26
  const config = {
@@ -55,7 +62,7 @@ function printUsage() {
55
62
  console.log('');
56
63
  console.log('Options:');
57
64
  console.log(
58
- ' --isolated, -i <backend> Run in isolated environment (screen, tmux, docker, zellij)'
65
+ ' --isolated, -i <environment> Run in isolated environment (screen, tmux, docker, zellij)'
59
66
  );
60
67
  console.log(' --attached, -a Run in attached mode (foreground)');
61
68
  console.log(' --detached, -d Run in detached mode (background)');
@@ -143,36 +150,70 @@ if (!config.disableSubstitutions) {
143
150
  * @param {string} cmd - Command to execute
144
151
  */
145
152
  async function runWithIsolation(options, cmd) {
146
- const backend = options.isolated;
153
+ const environment = options.isolated;
147
154
  const mode = getEffectiveMode(options);
155
+ const startTime = getTimestamp();
156
+
157
+ // Create log file path
158
+ const logFilePath = createLogPath(environment);
159
+
160
+ // Get session name (will be generated by runIsolated if not provided)
161
+ const sessionName =
162
+ options.session ||
163
+ `${environment}-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
164
+
165
+ // Print start message (unified format)
166
+ console.log(`[${startTime}] Starting: ${cmd}`);
167
+ console.log('');
148
168
 
149
169
  // Log isolation info
150
- console.log(`[Isolation] Backend: ${backend}, Mode: ${mode}`);
170
+ console.log(`[Isolation] Environment: ${environment}, Mode: ${mode}`);
151
171
  if (options.session) {
152
172
  console.log(`[Isolation] Session: ${options.session}`);
153
173
  }
154
174
  if (options.image) {
155
175
  console.log(`[Isolation] Image: ${options.image}`);
156
176
  }
157
- console.log(`[Isolation] Command: ${cmd}`);
158
177
  console.log('');
159
178
 
179
+ // Create log content
180
+ let logContent = createLogHeader({
181
+ command: cmd,
182
+ environment,
183
+ mode,
184
+ sessionName,
185
+ image: options.image,
186
+ startTime,
187
+ });
188
+
160
189
  // Run in isolation
161
- const result = await runIsolated(backend, cmd, {
190
+ const result = await runIsolated(environment, cmd, {
162
191
  session: options.session,
163
192
  image: options.image,
164
193
  detached: mode === 'detached',
165
194
  });
166
195
 
167
- // Print result
196
+ // Get exit code
197
+ const exitCode =
198
+ result.exitCode !== undefined ? result.exitCode : result.success ? 0 : 1;
199
+ const endTime = getTimestamp();
200
+
201
+ // Add result to log content
202
+ logContent += `${result.message}\n`;
203
+ logContent += createLogFooter(endTime, exitCode);
204
+
205
+ // Write log file
206
+ writeLogFile(logFilePath, logContent);
207
+
208
+ // Print result and footer (unified format)
168
209
  console.log('');
169
210
  console.log(result.message);
211
+ console.log('');
212
+ console.log(`[${endTime}] Finished`);
213
+ console.log(`Exit code: ${exitCode}`);
214
+ console.log(`Log saved: ${logFilePath}`);
170
215
 
171
- if (result.success) {
172
- process.exit(result.exitCode || 0);
173
- } else {
174
- process.exit(1);
175
- }
216
+ process.exit(exitCode);
176
217
  }
177
218
 
178
219
  /**
@@ -301,12 +342,10 @@ function runDirect(cmd) {
301
342
  });
302
343
  }
303
344
 
304
- // Generate timestamp for logging
305
- function getTimestamp() {
306
- return new Date().toISOString().replace('T', ' ').replace('Z', '');
307
- }
308
-
309
- // Generate unique log filename
345
+ /**
346
+ * Generate unique log filename for direct execution
347
+ * @returns {string} Log filename
348
+ */
310
349
  function generateLogFilename() {
311
350
  const timestamp = Date.now();
312
351
  const random = Math.random().toString(36).substring(2, 8);
@@ -9,6 +9,9 @@
9
9
  */
10
10
 
11
11
  const { execSync, spawn } = require('child_process');
12
+ const fs = require('fs');
13
+ const os = require('os');
14
+ const path = require('path');
12
15
  const { generateSessionName } = require('./args-parser');
13
16
 
14
17
  // Debug mode from environment
@@ -409,6 +412,101 @@ function runIsolated(backend, command, options = {}) {
409
412
  }
410
413
  }
411
414
 
415
+ /**
416
+ * Generate timestamp for logging
417
+ * @returns {string} ISO timestamp without 'T' and 'Z'
418
+ */
419
+ function getTimestamp() {
420
+ return new Date().toISOString().replace('T', ' ').replace('Z', '');
421
+ }
422
+
423
+ /**
424
+ * Generate unique log filename
425
+ * @param {string} environment - The isolation environment name
426
+ * @returns {string} Log filename
427
+ */
428
+ function generateLogFilename(environment) {
429
+ const timestamp = Date.now();
430
+ const random = Math.random().toString(36).substring(2, 8);
431
+ return `start-command-${environment}-${timestamp}-${random}.log`;
432
+ }
433
+
434
+ /**
435
+ * Create log content header
436
+ * @param {object} params - Log parameters
437
+ * @param {string} params.command - The command being executed
438
+ * @param {string} params.environment - The isolation environment
439
+ * @param {string} params.mode - attached or detached
440
+ * @param {string} params.sessionName - Session/container name
441
+ * @param {string} [params.image] - Docker image (for docker environment)
442
+ * @param {string} params.startTime - Start timestamp
443
+ * @returns {string} Log header content
444
+ */
445
+ function createLogHeader(params) {
446
+ let content = `=== Start Command Log ===\n`;
447
+ content += `Timestamp: ${params.startTime}\n`;
448
+ content += `Command: ${params.command}\n`;
449
+ content += `Environment: ${params.environment}\n`;
450
+ content += `Mode: ${params.mode}\n`;
451
+ content += `Session: ${params.sessionName}\n`;
452
+ if (params.image) {
453
+ content += `Image: ${params.image}\n`;
454
+ }
455
+ content += `Platform: ${process.platform}\n`;
456
+ content += `Node Version: ${process.version}\n`;
457
+ content += `Working Directory: ${process.cwd()}\n`;
458
+ content += `${'='.repeat(50)}\n\n`;
459
+ return content;
460
+ }
461
+
462
+ /**
463
+ * Create log content footer
464
+ * @param {string} endTime - End timestamp
465
+ * @param {number} exitCode - Exit code
466
+ * @returns {string} Log footer content
467
+ */
468
+ function createLogFooter(endTime, exitCode) {
469
+ let content = `\n${'='.repeat(50)}\n`;
470
+ content += `Finished: ${endTime}\n`;
471
+ content += `Exit Code: ${exitCode}\n`;
472
+ return content;
473
+ }
474
+
475
+ /**
476
+ * Write log file
477
+ * @param {string} logPath - Path to log file
478
+ * @param {string} content - Log content
479
+ * @returns {boolean} Success status
480
+ */
481
+ function writeLogFile(logPath, content) {
482
+ try {
483
+ fs.writeFileSync(logPath, content, 'utf8');
484
+ return true;
485
+ } catch (err) {
486
+ console.error(`\nWarning: Could not save log file: ${err.message}`);
487
+ return false;
488
+ }
489
+ }
490
+
491
+ /**
492
+ * Get log directory from environment or use system temp
493
+ * @returns {string} Log directory path
494
+ */
495
+ function getLogDir() {
496
+ return process.env.START_LOG_DIR || os.tmpdir();
497
+ }
498
+
499
+ /**
500
+ * Create log file path
501
+ * @param {string} environment - The isolation environment
502
+ * @returns {string} Full path to log file
503
+ */
504
+ function createLogPath(environment) {
505
+ const logDir = getLogDir();
506
+ const logFilename = generateLogFilename(environment);
507
+ return path.join(logDir, logFilename);
508
+ }
509
+
412
510
  module.exports = {
413
511
  isCommandAvailable,
414
512
  runInScreen,
@@ -416,4 +514,12 @@ module.exports = {
416
514
  runInZellij,
417
515
  runInDocker,
418
516
  runIsolated,
517
+ // Export logging utilities for unified experience
518
+ getTimestamp,
519
+ generateLogFilename,
520
+ createLogHeader,
521
+ createLogFooter,
522
+ writeLogFile,
523
+ getLogDir,
524
+ createLogPath,
419
525
  };
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  /**
3
3
  * Unit tests for the argument parser
4
4
  * Tests wrapper options parsing, validation, and command extraction
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  /**
3
3
  * Unit tests for the isolation module
4
4
  * Tests command availability checking and session name generation
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  /**
3
3
  * Unit tests for the substitution engine
4
4
  * Tests pattern matching, variable substitution, and rule precedence
@@ -1,30 +0,0 @@
1
- ---
2
- 'start-command': minor
3
- ---
4
-
5
- Add process isolation support with --isolated option
6
-
7
- This release adds the ability to run commands in isolated environments:
8
-
9
- **New Features:**
10
-
11
- - `--isolated` / `-i` option to run commands in screen, tmux, zellij, or docker
12
- - `--attached` / `-a` and `--detached` / `-d` modes for foreground/background execution
13
- - `--session` / `-s` option for custom session names
14
- - `--image` option for Docker container image specification
15
- - Two command syntax patterns: `$ [options] -- [command]` or `$ [options] command`
16
-
17
- **Supported Backends:**
18
-
19
- - GNU Screen - classic terminal multiplexer
20
- - tmux - modern terminal multiplexer
21
- - zellij - modern terminal workspace
22
- - Docker - container isolation
23
-
24
- **Examples:**
25
-
26
- ```bash
27
- $ --isolated tmux -- npm start
28
- $ -i screen -d npm start
29
- $ --isolated docker --image node:20 -- npm install
30
- ```