@@ -289,4 +289,135 @@ describe("runClaudeCode", () => {
289289 consoleErrorSpy . mockRestore ( )
290290 await generator . return ( undefined )
291291 } )
292+
293+ test ( "should use stdin for large system prompts on non-Windows platforms" , async ( ) => {
294+ const { runClaudeCode } = await import ( "../run" )
295+ const os = await import ( "os" )
296+ vi . mocked ( os . platform ) . mockReturnValue ( "linux" )
297+
298+ // Create a large system prompt (over 100KB)
299+ const largeSystemPrompt = "A" . repeat ( 101 * 1024 ) // 101KB
300+ const messages = [ { role : "user" as const , content : "Hello" } ]
301+ const options = {
302+ systemPrompt : largeSystemPrompt ,
303+ messages,
304+ }
305+
306+ const generator = runClaudeCode ( options )
307+
308+ // Consume at least one item to trigger process spawn
309+ await generator . next ( )
310+
311+ // On non-Windows with large system prompt, should NOT have --system-prompt in args
312+ const [ , args ] = mockExeca . mock . calls [ 0 ]
313+ expect ( args ) . not . toContain ( "--system-prompt" )
314+ expect ( args ) . not . toContain ( largeSystemPrompt )
315+
316+ // Should pass both system prompt and messages via stdin (like Windows)
317+ const expectedStdinData = JSON . stringify ( { systemPrompt : largeSystemPrompt , messages } )
318+ expect ( mockStdin . write ) . toHaveBeenCalledWith ( expectedStdinData , "utf8" , expect . any ( Function ) )
319+
320+ // Clean up
321+ await generator . return ( undefined )
322+ } )
323+
324+ test ( "should use command line args for small system prompts on non-Windows platforms" , async ( ) => {
325+ const { runClaudeCode } = await import ( "../run" )
326+ const os = await import ( "os" )
327+ vi . mocked ( os . platform ) . mockReturnValue ( "linux" )
328+
329+ // Create a small system prompt (under 100KB)
330+ const smallSystemPrompt = "You are a helpful assistant"
331+ const messages = [ { role : "user" as const , content : "Hello" } ]
332+ const options = {
333+ systemPrompt : smallSystemPrompt ,
334+ messages,
335+ }
336+
337+ const generator = runClaudeCode ( options )
338+
339+ // Consume at least one item to trigger process spawn
340+ await generator . next ( )
341+
342+ // On non-Windows with small system prompt, should have --system-prompt in args
343+ const [ , args ] = mockExeca . mock . calls [ 0 ]
344+ expect ( args ) . toContain ( "--system-prompt" )
345+ expect ( args ) . toContain ( smallSystemPrompt )
346+
347+ // Should only pass messages via stdin
348+ expect ( mockStdin . write ) . toHaveBeenCalledWith ( JSON . stringify ( messages ) , "utf8" , expect . any ( Function ) )
349+
350+ // Clean up
351+ await generator . return ( undefined )
352+ } )
353+
354+ test ( "should always use stdin for system prompts on Windows regardless of size" , async ( ) => {
355+ const { runClaudeCode } = await import ( "../run" )
356+ const os = await import ( "os" )
357+ vi . mocked ( os . platform ) . mockReturnValue ( "win32" )
358+
359+ // Test with both small and large system prompts
360+ const testCases = [
361+ { name : "small" , systemPrompt : "You are a helpful assistant" } ,
362+ { name : "large" , systemPrompt : "A" . repeat ( 101 * 1024 ) } , // 101KB
363+ ]
364+
365+ for ( const testCase of testCases ) {
366+ vi . clearAllMocks ( )
367+ mockExeca . mockReturnValue ( createMockProcess ( ) )
368+
369+ const messages = [ { role : "user" as const , content : "Hello" } ]
370+ const options = {
371+ systemPrompt : testCase . systemPrompt ,
372+ messages,
373+ }
374+
375+ const generator = runClaudeCode ( options )
376+
377+ // Consume at least one item to trigger process spawn
378+ await generator . next ( )
379+
380+ // On Windows, should never have --system-prompt in args regardless of size
381+ const [ , args ] = mockExeca . mock . calls [ 0 ]
382+ expect ( args ) . not . toContain ( "--system-prompt" )
383+ expect ( args ) . not . toContain ( testCase . systemPrompt )
384+
385+ // Should always pass both system prompt and messages via stdin
386+ const expectedStdinData = JSON . stringify ( { systemPrompt : testCase . systemPrompt , messages } )
387+ expect ( mockStdin . write ) . toHaveBeenCalledWith ( expectedStdinData , "utf8" , expect . any ( Function ) )
388+
389+ // Clean up
390+ await generator . return ( undefined )
391+ }
392+ } )
393+
394+ test ( "should handle edge case system prompt at exactly 100KB threshold" , async ( ) => {
395+ const { runClaudeCode } = await import ( "../run" )
396+ const os = await import ( "os" )
397+ vi . mocked ( os . platform ) . mockReturnValue ( "linux" )
398+
399+ // Create a system prompt at exactly 100KB
400+ const exactThresholdPrompt = "A" . repeat ( 100 * 1024 ) // Exactly 100KB
401+ const messages = [ { role : "user" as const , content : "Hello" } ]
402+ const options = {
403+ systemPrompt : exactThresholdPrompt ,
404+ messages,
405+ }
406+
407+ const generator = runClaudeCode ( options )
408+
409+ // Consume at least one item to trigger process spawn
410+ await generator . next ( )
411+
412+ // At exactly 100KB, should still use command line args (threshold is exclusive)
413+ const [ , args ] = mockExeca . mock . calls [ 0 ]
414+ expect ( args ) . toContain ( "--system-prompt" )
415+ expect ( args ) . toContain ( exactThresholdPrompt )
416+
417+ // Should only pass messages via stdin
418+ expect ( mockStdin . write ) . toHaveBeenCalledWith ( JSON . stringify ( messages ) , "utf8" , expect . any ( Function ) )
419+
420+ // Clean up
421+ await generator . return ( undefined )
422+ } )
292423} )
0 commit comments