Compare commits

...
Author SHA1 Message Date
Paul Bakaus fd78be5630 Fix Claude copy-edit prompt transport
Pass staged copy-edit prompts over stdin so large batches do not exceed platform argv limits.

AI assistance: Implemented and validated with OpenAI Codex under maintainer authorization.
2026-08-06 09:22:41 -07:00
2 changed files with 43 additions and 2 deletions
+1 -2
View File
@@ -470,12 +470,11 @@ function runClaude(prompt, { cwd, env, resultPath, logPath, timeoutMs = DEFAULT_
if (env.IMPECCABLE_LIVE_COPY_AGENT_MODEL) {
args.push('--model', env.IMPECCABLE_LIVE_COPY_AGENT_MODEL);
}
args.push(prompt);
// Forward env as-is so CLAUDE_CODE_OAUTH_TOKEN and ANTHROPIC_API_KEY flow
// through. On macOS, `claude /login` stores creds in the Keychain, which a
// non-TTY subprocess cannot read; setting CLAUDE_CODE_OAUTH_TOKEN (via
// `claude setup-token`) is the supported headless auth path.
return runAgentProcess('claude', args, '', { cwd, env, logPath, timeoutMs, mirrorOutputPath: resultPath });
return runAgentProcess('claude', args, prompt, { cwd, env, logPath, timeoutMs, mirrorOutputPath: resultPath });
}
function runAgentProcess(command, args, stdin, { cwd, env, logPath, timeoutMs, mirrorOutputPath }) {
+42
View File
@@ -305,6 +305,48 @@ describe('live-copy-edit-agent', () => {
);
});
it('passes large Claude prompts on stdin instead of argv', async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'copy-agent-claude-stdin-'));
try {
const fakeClaude = path.join(tmp, 'claude');
fs.writeFileSync(fakeClaude, [
'#!/usr/bin/env node',
"let input = '';",
"process.stdin.setEncoding('utf8');",
"process.stdin.on('data', (chunk) => { input += chunk; });",
"process.stdin.on('end', () => {",
" const promptLeakedToArgv = process.argv.slice(2).some((arg) => arg.includes('large-prompt-sentinel'));",
" if (!input.includes('large-prompt-sentinel') || promptLeakedToArgv) process.exit(2);",
" process.stdout.write(JSON.stringify({ status: 'done', appliedEntryIds: ['large'], files: [], notes: [] }));",
'});',
'',
].join('\n'));
fs.chmodSync(fakeClaude, 0o755);
const result = await runCopyEditBatchAgent({
pageUrl: '/',
entries: [{
id: 'large',
pageUrl: '/',
ops: [{ originalText: 'Old', newText: `large-prompt-sentinel${'x'.repeat(1_100_000)}` }],
}],
}, {
provider: 'claude',
outDir: path.join(tmp, 'out'),
timeoutMs: 5_000,
env: {
...process.env,
PATH: `${tmp}${path.delimiter}${process.env.PATH || ''}`,
},
});
assert.equal(result.status, 'done');
assert.deepEqual(result.appliedEntryIds, ['large']);
} finally {
fs.rmSync(tmp, { recursive: true, force: true });
}
});
it('describeNoProviderError mentions starting impeccable live when chat is the missing piece', () => {
const noChatPolling = describeNoProviderError({
exists: () => false,