--- name: claude_stream kind: function lang: go domain: core version: "1.0.0" purity: impure signature: "func StreamClaude(ctx context.Context, opts ClaudeStreamOpts) (<-chan ClaudeEvent, error)" description: "Lanza `claude -p --output-format stream-json --verbose` como subprocess y retorna un canal de eventos decodificados (text_delta, tool_use, tool_result, result, error). Expande automaticamente los bloques de contenido de los mensajes assistant/user en eventos sinteticos de grano fino." tags: [claude, streaming, subprocess, agent, ndjson, tool-use] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: - bufio - context - encoding/json - fmt - io - os - os/exec - strings tested: true tests: - "system event" - "text delta" - "multiple text blocks" - "tool use" - "tool result string content" - "tool result array content" - "result event" - "non-zero exit" - "non-json line" - "ctx cancel" test_file_path: "functions/core/claude_stream_test.go" file_path: "functions/core/claude_stream.go" params: - name: ctx desc: "Contexto de cancelacion. Al cancelar, el subprocess recibe SIGTERM/SIGKILL y el canal se cierra." - name: opts desc: "Opciones de lanzamiento: Bin (path al binario claude, default 'claude'), Args (args extra sin -p ni --output-format ni --verbose), Stdin (prompt como io.Reader), Workdir (CWD del subprocess), Env (env extra mergeado con os.Environ()), Stderr (destino del stderr del subprocess)." output: "Canal de ClaudeEvent cerrado cuando el subprocess termina. Cada evento tiene Type discriminador y campos especificos segun el tipo. Raw contiene siempre la linea NDJSON original. Retorna error solo si el spawn falla." --- ## Tipos exportados **ClaudeEventType** — constantes de tipo de evento: - `system` — evento de inicializacion con session_id y model - `assistant` — mensaje raw del asistente (tambien genera text_delta y/o tool_use sinteticos) - `user` — mensaje raw de usuario/tool_result (tambien genera tool_result sintetico) - `result` — evento final con stop_reason, is_error, result - `text_delta` — (sintetico) porcion de texto del asistente - `tool_use` — (sintetico) llamada a herramienta con tool_use_id, tool_name, tool_input - `tool_result` — (sintetico) resultado de herramienta con tool_result_id, content, is_error - `error` — (sintetico) linea no-JSON o exit code != 0 **ClaudeStreamOpts** — configura el subprocess: - `Bin string` — path al binario. Si vacio, usa `exec.LookPath("claude")`. - `Args []string` — args extra. Se anteponen automaticamente `-p --output-format stream-json --verbose`. - `Stdin io.Reader` — prompt user. Puede ser `strings.NewReader("prompt")` o nil. - `Workdir string` — CWD del subprocess. - `Env map[string]string` — variables extra mergeadas con `os.Environ()`. - `Stderr io.Writer` — destino del stderr en vivo (ej. `os.Stderr` para debug). Si nil, se descarta. ## Ejemplo ```go ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() ch, err := core.StreamClaude(ctx, core.ClaudeStreamOpts{ Args: []string{"responde en una frase: que es Go"}, Stderr: os.Stderr, }) if err != nil { log.Fatal(err) } for ev := range ch { switch ev.Type { case core.ClaudeEventTextDelta: fmt.Print(ev.Text) case core.ClaudeEventToolUse: fmt.Printf("\n[tool] %s(%s)\n", ev.ToolName, ev.ToolInput) case core.ClaudeEventToolResult: fmt.Printf("[result] %s\n", ev.ToolResultContent) case core.ClaudeEventResult: fmt.Printf("\n[done] stop_reason=%s\n", ev.StopReason) case core.ClaudeEventError: fmt.Fprintf(os.Stderr, "[error] %s\n", ev.Error) } } ``` ## Notas - El caller DEBE consumir el canal hasta que se cierre, o cancelar el ctx. No consumir bloquea la goroutine interna. - El canal tiene buffer de 64 para absorber ráfagas sin bloquear la lectura de stdout. - Los eventos `assistant` y `user` raw se emiten ademas de los sinteticos, para casos no contemplados. - `tool_result.content` puede ser string o array `[{type:text,text:"..."}]` — la funcion concatena los bloques text en ambos casos. - Los tests usan un fake claude bash; se skipean si bash no esta disponible en el PATH. - Equivalente Go de `projects/osint_graph/apps/graph_explorer/chat.cpp` (C++).