import { spawn } from "child_process"; export interface ClaudeResult { sessionId: string; result: string; } interface CliJsonResult { result?: string; session_id?: string; is_error?: boolean; } function runClaude(args: string[], cwd: string): Promise { return new Promise((resolve, reject) => { const proc = spawn("npx", ["--yes", "@anthropic-ai/claude-code", ...args], { cwd, shell: true, env: { ...process.env }, }); let stdout = ""; let stderr = ""; proc.stdout.on("data", (data: Buffer) => { stdout += data.toString(); }); proc.stderr.on("data", (data: Buffer) => { stderr += data.toString(); }); proc.on("close", (code: number | null) => { if (code !== 0 && !stdout) { reject(new Error(stderr || `claude exited with code ${code}`)); } else { resolve(stdout); } }); proc.on("error", reject); }); } const APPEND_PROMPT = "Always respond in Russian. Be concise."; export class ClaudeService { async createSession(prompt: string, cwd: string): Promise { const raw = await runClaude( ["-p", prompt, "--output-format", "json", "--dangerously-skip-permissions", "--append-system-prompt", APPEND_PROMPT], cwd, ); const parsed = JSON.parse(raw.trim().split("\n").pop()!) as CliJsonResult; return { sessionId: parsed.session_id ?? "", result: parsed.result ?? "", }; } async sendMessage(prompt: string, sessionId: string): Promise { const raw = await runClaude( ["-p", prompt, "--output-format", "json", "--resume", sessionId, "--dangerously-skip-permissions", "--append-system-prompt", APPEND_PROMPT], process.cwd(), ); const parsed = JSON.parse(raw.trim().split("\n").pop()!) as CliJsonResult; return parsed.result ?? ""; } }