/** * Claude agent configuration and stream handling */ import type { AgentConfig, AgentRole, ReviewOptions } from "@/lib/types"; import { createLineFormatter, extractLastParsedValue, parseJsonlEvent } from "./core"; import type { AssistantContentBlock, AssistantEvent, ClaudeStreamEvent, ResultEvent, ToolResultBlock, UserEvent, } from "./types"; export const claudeConfig: AgentConfig = { command: "claude", buildArgs: ( _role: AgentRole, prompt: string, model?: string, _reviewOptions?: ReviewOptions, _provider?: string, _reasoning?: string ): string[] => { const baseArgs: string[] = []; if (model) { baseArgs.push("--model", model); } return [ ...baseArgs, "-p", prompt, "--dangerously-skip-permissions", "--verbose", "--output-format", "stream-json", ]; }, buildEnv: (reasoning?: string): Record => { const env: Record = { ...(process.env as Record), }; if (reasoning === "low" || reasoning === "medium" || reasoning === "high") { env.CLAUDE_CODE_EFFORT_LEVEL = reasoning; } return env; }, }; export function parseClaudeStreamEvent(line: string): ClaudeStreamEvent | null { return parseJsonlEvent(line); } function isAssistantEvent(event: ClaudeStreamEvent): event is AssistantEvent { return event.type === "assistant" && Array.isArray((event as AssistantEvent).message?.content); } function isUserEvent(event: ClaudeStreamEvent): event is UserEvent { return event.type === "user" && Array.isArray((event as UserEvent).message?.content); } function isResultEvent(event: ClaudeStreamEvent): event is ResultEvent { return event.type === "result" && typeof (event as ResultEvent).result === "string"; } function formatContentBlock(block: AssistantContentBlock): string { switch (block.type) { case "thinking": return `--- Reasoning ---\n${block.thinking}`; case "text": return block.text; case "tool_use": return `--- Tool: ${block.name} ---\nInput: ${JSON.stringify(block.input)}`; default: return ""; } } function formatToolResult(block: ToolResultBlock): string { return `--- Tool Result ---\n${block.content}`; } function formatAssistantEvent(event: AssistantEvent): string { return event.message.content.map(formatContentBlock).filter(Boolean).join("\n\n"); } function formatUserEvent(event: UserEvent): string { return event.message.content .map((block) => { if (block.type === "tool_result") { return formatToolResult(block); } if (block.type === "text") { return block.text; } return ""; }) .filter(Boolean) .join("\n\n"); } function formatResultEvent(event: ResultEvent): string { return `=== Result ===\n${event.result}`; } export function formatClaudeEventForDisplay(event: ClaudeStreamEvent): string | null { switch (event.type) { case "system": return null; case "assistant": return isAssistantEvent(event) ? formatAssistantEvent(event) : null; case "user": return isUserEvent(event) ? formatUserEvent(event) : null; case "result": return isResultEvent(event) ? formatResultEvent(event) : null; default: return null; } } export function extractClaudeResult(output: string): string | null { return extractLastParsedValue(output, parseClaudeStreamEvent, (event) => isResultEvent(event) ? event.result : null ); } export const formatClaudeLine = createLineFormatter( parseClaudeStreamEvent, formatClaudeEventForDisplay );