/** * Process spawning — always explicit argument arrays, never shell strings. */ import { spawn } from 'child_process'; /** * Spawn a process and stream its output. * @param {string} cmd * @param {string[]} args * @param {{ dryRun?: boolean, label?: string }} opts * @returns {Promise} */ export async function run(cmd, args, { dryRun = false, label } = {}) { const display = [cmd, ...args].join(' '); if (label) console.log(`[${label}] ${display}`); else console.log(`$ ${display}`); if (dryRun) return; return new Promise((resolve, reject) => { const child = spawn(cmd, args, { stdio: 'inherit' }); child.on('error', reject); child.on('close', code => { if (code === 0) resolve(); else reject(new Error(`${cmd} exited with code ${code}`)); }); }); }