Pipe large prompts to CLI tools via temp file + shell redirect to bypass argv length limits and stdin propagation issues
When piping a large text payload (>30KB) into a CLI tool, neither direct argv nor Node's stdin pipe reliably work cross-platform. The robust pattern is: write to a temp file, then spawn the CLI via shell with input-redirect < file.txt.
❌ Direct argv
execFile('claude', ['-p', longPrompt])
Fails with ENAMETOOLONG on Windows (32 KB argv limit) and Linux (~128 KB).
❌ Node stdin pipe
const child = spawn('claude', ['-p'], { stdio: ['pipe', ...], shell: true });
child.stdin.write(longPrompt); child.stdin.end();
On Windows with shell: true, Node spawns cmd.exe which spawns the CLI. The stdin pipe terminates at , not the child — the CLI waits on an empty stdin and times out.
cmd.exe❌ Node stdin pipe without shell
spawn('claude', ['-p'], { shell: false })
On Windows this can't find claude — npm CLIs ship as claude.cmd which the non-shell spawn doesn't resolve. Workarounds (using claude.cmd directly) are brittle.
Write to a temp file, then use shell redirect:
const { spawn } = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
async function askCli(prompt, timeout = 900000) {
const tmpFile = path.join(os.tmpdir(), `prompt-${Date.now()}-${Math.random().toString(36).slice(2,8)}.txt`);
fs.writeFileSync(tmpFile, prompt, 'utf8');
const fSlash = tmpFile.replace(/\\/g, '/');
const cmd = `claude -p < "${fSlash}"`;
return new Promise((resolve, reject) => {
const child = spawn(cmd, [], { shell: true, stdio: ['ignore', 'pipe', 'pipe'] });
let stdout = '', stderr = '';
const timer = setTimeout(() => { child.kill(); cleanup(); reject(new Error('timeout')); }, timeout);
const cleanup = () => { try { fs.unlinkSync(tmpFile); } catch {} };
child.stdout.on('data', d => stdout += d.toString());
child.stderr.on('data', d => stderr += d.toString());
child.on('close', code => {
clearTimeout(timer); cleanup();
if (code !== 0) reject(new Error(`exit ${code}: ${stderr.split('\n')[0]}`));
else resolve(stdout.trim());
});
});
}
< file.txt — the OS kernel opens the file and sets it as stdin for the child. Works identically on bash and cmd.exe.stdio: ['ignore', ...] — no stdin pipe from Node, avoids the cmd.exe stdin propagation bug.cmd.exe (via shell in git-bash on Windows) and POSIX shells accept them."..."), avoid spaces if possible — use os.tmpdir() rather than user-chosen dirs