/** * invoke-llm.ts * Unified LLM invocation for plan2code-metrics. * Supports Claude Code (temp file → stdin) and Copilot CLI (stdin string). * Mirrors the agent pattern from plan2code-loop. */ import { execa } from 'execa'; import { writeFileSync, unlinkSync } from 'fs'; import { join } from 'path'; import { tmpdir } from 'os'; // ── Agent definitions ──────────────────────────────────────────────────────── export type AgentType = 'claude-code' | 'copilot-cli'; export interface AgentDef { name: AgentType; displayName: string; command: string; defaultModel: string; } export const AGENTS: Record = { 'claude-code': { name: 'claude-code', displayName: 'Claude Code', command: 'claude', defaultModel: 'default', }, 'copilot-cli': { name: 'copilot-cli', displayName: 'GitHub Copilot CLI', command: 'copilot', defaultModel: 'claude-sonnet-4', }, }; // ── Invocation ─────────────────────────────────────────────────────────────── export interface InvokeLLMOptions { prompt: string; model: string; agent: AgentType; timeout?: number; } export async function invokeLLM(opts: InvokeLLMOptions): Promise { const { prompt, model, agent, timeout = 300_000 } = opts; const def = AGENTS[agent]; if (agent === 'claude-code') { // Write prompt to temp file — more reliable than stdin on Windows const tempFile = join(tmpdir(), `plan2code-metrics-prompt-${Date.now()}.txt`); writeFileSync(tempFile, prompt, 'utf-8'); try { const args: string[] = [ '--print', '--dangerously-skip-permissions', ]; // Only add --model if not using default if (model && model !== 'default') { args.push('--model', model); } const result = await execa(def.command, args, { inputFile: tempFile, timeout, }); return result.stdout; } finally { try { unlinkSync(tempFile); } catch { /* ignore cleanup errors */ } } } else { // Copilot CLI: pipe prompt via stdin string const args: string[] = []; // Only add --model if not using default if (model && model !== 'default') { args.push('--model', model); } args.push('--allow-all-tools', '-s'); const result = await execa(def.command, args, { input: prompt, timeout, }); return result.stdout; } }