import "dotenv/config"; import { query, type Options, type SDKMessage } from "@anthropic-ai/claude-agent-sdk"; import { pathToFileURL } from "node:url"; import { buildClaudeAuthEnv, getConfig, resolveAnthropicToken, type AppConfig, } from "../config.js"; import { createSessionContext } from "./session-context.js"; function requiredPrompt(argv: string[]): string { const prompt = argv.slice(2).join(" ").trim(); if (!prompt) { throw new Error('Usage: npm run claude -- "your prompt"'); } return prompt; } function buildOptions(config = getConfig()): Options { return { maxTurns: config.provider.claudeMaxTurns, ...(config.provider.claudeModel ? { model: config.provider.claudeModel } : {}), ...(config.provider.claudeCodePath ? { pathToClaudeCodeExecutable: config.provider.claudeCodePath } : {}), }; } type ClaudeAuthOptionOverrides = { apiKey?: string; authToken?: string; }; function buildClaudeAuthOptionOverrides( config: Readonly, ): ClaudeAuthOptionOverrides { if (config.provider.anthropicOauthToken) { return { authToken: config.provider.anthropicOauthToken }; } const token = resolveAnthropicToken(config.provider); return token ? { apiKey: token } : {}; } export async function readClaudeResult( session: AsyncIterable, ): Promise { let result = ""; for await (const message of session) { if (message.type === "result" && message.subtype === "success") { result = message.result.trim(); } if (message.type === "result" && message.subtype !== "success") { const detail = message.errors.join("; "); throw new Error( `Claude query failed (${message.subtype})${detail ? `: ${detail}` : ""}`, ); } } if (!result) { throw new Error("Claude run completed without a final result."); } return result; } type RunClaudePromptDependencies = { config?: Readonly; createSessionContextFn?: typeof createSessionContext; queryFn?: typeof query; writeOutput?: (output: string) => void; }; export async function runClaudePrompt( prompt: string, dependencies: RunClaudePromptDependencies = {}, ): Promise { const config = dependencies.config ?? getConfig(); const createSessionContextFn = dependencies.createSessionContextFn ?? createSessionContext; const queryFn = dependencies.queryFn ?? query; const writeOutput = dependencies.writeOutput ?? ((output: string) => console.log(output)); const sessionContext = await createSessionContextFn("claude", { prompt, workspaceRoot: process.cwd(), config, }); try { const runtimeEnv = { ...sessionContext.runtimeInjection.env, ...buildClaudeAuthEnv(config.provider), }; const authOptionOverrides = buildClaudeAuthOptionOverrides(config); const finalResponse = await sessionContext.runInSession(async () => { const session = queryFn({ prompt: sessionContext.promptWithContext, options: { ...buildOptions(config), ...authOptionOverrides, ...(sessionContext.mcp.claudeMcpServers ? { mcpServers: sessionContext.mcp.claudeMcpServers as Options["mcpServers"] } : {}), cwd: sessionContext.runtimeInjection.workingDirectory, env: runtimeEnv, }, }); try { return await readClaudeResult(session); } finally { session.close(); } }); writeOutput(finalResponse); } finally { await sessionContext.close(); } } async function main(): Promise { const prompt = requiredPrompt(process.argv); await runClaudePrompt(prompt); } if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main().catch((error: unknown) => { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1; }); }