@@ -29,6 +29,16 @@ const (
2929 StreamJSONOutput OutputFormat = "stream-json"
3030)
3131
32+ // InputFormat defines the input format for Claude Code requests
33+ type InputFormat string
34+
35+ const (
36+ // TextInput sends plain text input (default)
37+ TextInput InputFormat = "text"
38+ // StreamJSONInput sends streaming JSON input for multiple prompts
39+ StreamJSONInput InputFormat = "stream-json"
40+ )
41+
3242// ClaudeClient is the main client for interacting with Claude Code
3343type ClaudeClient struct {
3444 // BinPath is the path to the Claude Code binary
@@ -41,6 +51,8 @@ type ClaudeClient struct {
4151type RunOptions struct {
4252 // Format specifies the output format (text, json, stream-json)
4353 Format OutputFormat
54+ // InputFormat specifies the input format (text, stream-json)
55+ InputFormat InputFormat
4456 // SystemPrompt overrides the default system prompt
4557 SystemPrompt string
4658 // AppendPrompt appends to the default system prompt
@@ -126,6 +138,18 @@ type QueryOptions struct {
126138 BufferConfig * buffer.Config `json:"-"`
127139}
128140
141+ // StreamInputMessage represents a message for streaming input to Claude Code
142+ type StreamInputMessage struct {
143+ Type string `json:"type"`
144+ Message StreamInputContent `json:"message"`
145+ }
146+
147+ // StreamInputContent represents the content of a streaming input message
148+ type StreamInputContent struct {
149+ Role string `json:"role"`
150+ Content string `json:"content"`
151+ }
152+
129153// Message represents a message from Claude Code in streaming mode
130154// Aligned with Python SDK message structure
131155type Message struct {
@@ -321,8 +345,23 @@ func (c *ClaudeClient) RunPromptCtx(ctx context.Context, prompt string, opts *Ru
321345 }, nil
322346}
323347
324- // StreamPrompt executes a prompt with Claude Code and streams the results through a channel
348+ // StreamPrompt executes a prompt with Claude Code and streams the results thro ugh a channel
325349func (c * ClaudeClient ) StreamPrompt (ctx context.Context , prompt string , opts * RunOptions ) (<- chan Message , <- chan error ) {
350+ // Create a channel for a single prompt
351+ promptCh := make (chan string , 1 )
352+ promptCh <- prompt
353+ close (promptCh )
354+
355+ // Use the multi-prompt streaming function
356+ return c .StreamPromptsToSession (ctx , promptCh , opts )
357+ }
358+
359+ // StreamPromptsToSession starts a Claude Code session and streams prompts to it continuously
360+ func (c * ClaudeClient ) StreamPromptsToSession (
361+ ctx context.Context ,
362+ promptCh <- chan string ,
363+ opts * RunOptions ,
364+ ) (<- chan Message , <- chan error ) {
326365 messageCh := make (chan Message )
327366 errCh := make (chan error , 1 )
328367
@@ -333,11 +372,13 @@ func (c *ClaudeClient) StreamPrompt(ctx context.Context, prompt string, opts *Ru
333372 // Force stream-json format for streaming
334373 streamOpts := * opts
335374 streamOpts .Format = StreamJSONOutput
375+ streamOpts .InputFormat = StreamJSONInput
336376
337377 // Claude CLI requires --verbose when using --output-format=stream-json with --print
338378 streamOpts .Verbose = true
339379
340- args := BuildArgs (prompt , & streamOpts )
380+ // Remove the initial prompt since we'll stream through stdin
381+ args := BuildArgs ("" , & streamOpts )
341382
342383 go func () {
343384 defer close (messageCh )
@@ -346,6 +387,12 @@ func (c *ClaudeClient) StreamPrompt(ctx context.Context, prompt string, opts *Ru
346387 // Create a custom command that supports context
347388 cmd := execCommand (ctx , c .BinPath , args ... )
348389
390+ stdin , err := cmd .StdinPipe ()
391+ if err != nil {
392+ errCh <- fmt .Errorf ("failed to get stdin pipe: %w" , err )
393+ return
394+ }
395+
349396 stdout , err := cmd .StdoutPipe ()
350397 if err != nil {
351398 errCh <- fmt .Errorf ("failed to get stdout pipe: %w" , err )
@@ -376,6 +423,67 @@ func (c *ClaudeClient) StreamPrompt(ctx context.Context, prompt string, opts *Ru
376423 return
377424 }
378425
426+ // Channel to signal when command is ready to receive input
427+ cmdReady := make (chan struct {})
428+
429+ // Start a goroutine to handle input prompts
430+ go func () {
431+ defer stdin .Close ()
432+
433+ // Wait for command to be ready before processing prompts
434+ select {
435+ case <- cmdReady :
436+ // Command is ready, proceed with prompt processing
437+ case <- ctx .Done ():
438+ return
439+ }
440+
441+ for {
442+ select {
443+ case prompt , ok := <- promptCh :
444+ if ! ok {
445+ // Prompt channel closed, close stdin
446+ return
447+ }
448+
449+ // Create JSON message for streaming input
450+ streamMsg := StreamInputMessage {
451+ Type : "user" ,
452+ Message : StreamInputContent {
453+ Role : "user" ,
454+ Content : prompt ,
455+ },
456+ }
457+
458+ // Encode as JSON and send
459+ jsonData , err := json .Marshal (streamMsg )
460+ if err != nil {
461+ errCh <- fmt .Errorf ("failed to marshal stream input message: %w" , err )
462+ return
463+ }
464+
465+ if _ , err := fmt .Fprintln (stdin , string (jsonData )); err != nil {
466+ errCh <- fmt .Errorf ("failed to write JSON prompt to stdin: %w" , err )
467+ return
468+ }
469+ case <- ctx .Done ():
470+ return
471+ }
472+ }
473+ }()
474+
475+ // Give the command a moment to initialize before signaling ready
476+ // This prevents the race condition where we write to stdin before
477+ // the Claude process is ready to read from it
478+ go func () {
479+ select {
480+ case <- time .After (100 * time .Millisecond ):
481+ close (cmdReady )
482+ case <- ctx .Done ():
483+ return
484+ }
485+ }()
486+
379487 // Use buffered reader with configurable buffer size instead of scanner
380488 reader := bufio .NewReaderSize (stdout , int (bufferConfig .MaxStdoutSize / 1000 )) // Use reasonable buffer size
381489
@@ -642,6 +750,10 @@ func BuildArgs(prompt string, opts *RunOptions) []string {
642750 args = append (args , "--output-format" , string (opts .Format ))
643751 }
644752
753+ if opts .InputFormat != "" {
754+ args = append (args , "--input-format" , string (opts .InputFormat ))
755+ }
756+
645757 if opts .SystemPrompt != "" {
646758 args = append (args , "--system-prompt" , opts .SystemPrompt )
647759 }
0 commit comments