From 0c452a3ebc651690fe391b0ae2aebb345886a9f7 Mon Sep 17 00:00:00 2001 From: Scott Date: Sun, 18 Jan 2026 12:59:46 -0700 Subject: [PATCH] fix: add cross-platform Node.js launcher for Windows CMD/PowerShell support The `./start-automaker.sh` script doesn't work when invoked from Windows CMD or PowerShell because: 1. The `./` prefix is Unix-style path notation 2. Windows shells don't execute .sh files directly This adds a Node.js launcher (`start-automaker.mjs`) that: - Detects the platform and finds bash (Git Bash, MSYS2, Cygwin, or WSL) - Converts Windows paths to Unix-style for bash compatibility - Passes all arguments through to the original bash script - Provides helpful error messages if bash isn't found The npm scripts now use `node start-automaker.mjs` which works on all platforms while preserving the full functionality of the bash TUI launcher. --- package.json | 4 +- start-automaker.mjs | 127 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 start-automaker.mjs diff --git a/package.json b/package.json index 1c884bc5..0756f868 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "scripts": { "postinstall": "node -e \"const fs=require('fs');if(process.platform==='darwin'){['darwin-arm64','darwin-x64'].forEach(a=>{const p='node_modules/node-pty/prebuilds/'+a+'/spawn-helper';if(fs.existsSync(p))fs.chmodSync(p,0o755)})}\" && node scripts/fix-lockfile-urls.mjs", "fix:lockfile": "node scripts/fix-lockfile-urls.mjs", - "dev": "./start-automaker.sh", - "start": "./start-automaker.sh --production", + "dev": "node start-automaker.mjs", + "start": "node start-automaker.mjs --production", "_dev:web": "npm run dev:web --workspace=apps/ui", "_dev:electron": "npm run dev:electron --workspace=apps/ui", "_dev:electron:debug": "npm run dev:electron:debug --workspace=apps/ui", diff --git a/start-automaker.mjs b/start-automaker.mjs new file mode 100644 index 00000000..3106f792 --- /dev/null +++ b/start-automaker.mjs @@ -0,0 +1,127 @@ +#!/usr/bin/env node +/** + * Cross-platform launcher for Automaker + * Works on Windows (CMD, PowerShell, Git Bash) and Unix (macOS, Linux) + */ + +import { spawn } from 'child_process'; +import { existsSync } from 'fs'; +import { platform } from 'os'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const isWindows = platform() === 'win32'; +const args = process.argv.slice(2); + +/** + * Find bash executable on Windows + */ +function findBashOnWindows() { + const possiblePaths = [ + // Git Bash (most common) + 'C:\\Program Files\\Git\\bin\\bash.exe', + 'C:\\Program Files (x86)\\Git\\bin\\bash.exe', + // MSYS2 + 'C:\\msys64\\usr\\bin\\bash.exe', + 'C:\\msys32\\usr\\bin\\bash.exe', + // Cygwin + 'C:\\cygwin64\\bin\\bash.exe', + 'C:\\cygwin\\bin\\bash.exe', + // WSL bash (available in PATH on Windows 10+) + 'bash.exe', + ]; + + for (const bashPath of possiblePaths) { + if (bashPath === 'bash.exe') { + // Check if bash is in PATH + try { + const result = spawn.sync?.('where', ['bash.exe'], { stdio: 'pipe' }); + if (result?.status === 0) { + return 'bash.exe'; + } + } catch { + // where command failed, continue checking + } + } else if (existsSync(bashPath)) { + return bashPath; + } + } + + return null; +} + +/** + * Run the bash script + */ +function runBashScript() { + const scriptPath = join(__dirname, 'start-automaker.sh'); + + if (!existsSync(scriptPath)) { + console.error('Error: start-automaker.sh not found'); + process.exit(1); + } + + let bashCmd; + let bashArgs; + + if (isWindows) { + bashCmd = findBashOnWindows(); + + if (!bashCmd) { + console.error('Error: Could not find bash on Windows.'); + console.error('Please install Git for Windows from https://git-scm.com/download/win'); + console.error(''); + console.error('Alternatively, you can run these commands directly:'); + console.error(' npm run dev:web - Web browser mode'); + console.error(' npm run dev:electron - Desktop app mode'); + process.exit(1); + } + + // Convert Windows path to Unix-style for bash + // Handle both C:\path and /c/path styles + let unixPath = scriptPath.replace(/\\/g, '/'); + if (/^[A-Za-z]:/.test(unixPath)) { + // Convert C:/path to /c/path for MSYS/Git Bash + unixPath = '/' + unixPath[0].toLowerCase() + unixPath.slice(2); + } + + bashArgs = [unixPath, ...args]; + } else { + bashCmd = '/bin/bash'; + bashArgs = [scriptPath, ...args]; + } + + const child = spawn(bashCmd, bashArgs, { + stdio: 'inherit', + env: { + ...process.env, + // Ensure proper terminal handling + TERM: process.env.TERM || 'xterm-256color', + }, + // On Windows, we need to use shell for proper signal handling + shell: false, + }); + + child.on('error', (err) => { + if (err.code === 'ENOENT') { + console.error(`Error: Could not find bash at "${bashCmd}"`); + console.error('Please ensure Git Bash or another bash shell is installed.'); + } else { + console.error('Error launching Automaker:', err.message); + } + process.exit(1); + }); + + child.on('exit', (code) => { + process.exit(code ?? 0); + }); + + // Forward signals to child process + process.on('SIGINT', () => child.kill('SIGINT')); + process.on('SIGTERM', () => child.kill('SIGTERM')); +} + +runBashScript();