/** * Grok CLI Executor — PhaseExecutor using the `grok` CLI. * * Grok CLI is xAI's agentic coding tool (grok.exe at ~/.grok/bin/grok.exe). * Routes through local proxy at :18901 (configured in ~/.grok/config.toml). * * Integration: shells out to `grok -m -p ""`. * * Usage: * const grok = new GrokExecutor({ model: "grok-3" }); * const result = await feedbackLoop.run(task, grok); */ import { exec } from "node:child_process"; import * as path from "node:path"; import * as fs from "node:fs"; import type { LoopIteration } from "../../core/types.js"; import type { PhaseExecutor } from "../../tier2-primitives/loops/feedback-loop.js"; function findGrokBin(): string { const candidates = [ path.join(process.env.HOME || "", ".grok", "bin", "grok.exe"), path.join(process.env.USERPROFILE || "", ".grok", "bin", "grok.exe"), "grok", ]; for (const c of candidates) { try { if (fs.existsSync(c)) return c; } catch {} } return "grok"; } export interface GrokConfig { /** Grok CLI binary path */ bin?: string; /** Model name */ model?: string; /** Proxy URL */ proxyUrl?: string; /** Timeout per phase in ms */ timeoutMs?: number; } export class GrokExecutor implements PhaseExecutor { private config: GrokConfig; constructor(config?: GrokConfig) { this.config = { bin: findGrokBin(), model: process.env.GROK_MODEL ?? "grok-3", proxyUrl: process.env.GROK_PROXY ?? "http://localhost:18901/v1", timeoutMs: 120000, ...config, }; } async plan(task: string, previousIteration: LoopIteration | null): Promise { const prev = previousIteration ? `\nPrevious: ${previousIteration.reflection?.slice(0, 200)}` : ""; return this.callGrok(`You are in the PLAN phase.\nTask: ${task}${prev}`); } async execute(plan: string): Promise { return this.callGrok(`EXECUTE:\n${plan}`); } async observe(executed: string): Promise { return this.callGrok(`OBSERVE:\n${executed}`); } async reflect(observation: string, previousIteration: LoopIteration | null): Promise { const prev = previousIteration ? `\nExpected: ${previousIteration.refinement?.slice(0, 200)}` : ""; return this.callGrok(`REFLECT:\n${observation}${prev}`); } async refine(reflection: string): Promise { return this.callGrok(`REFINE:\n${reflection}`); } private async callGrok(prompt: string): Promise { const modelFlag = `-m "${this.config.model}"`; const cmd = `"${this.config.bin}" ${modelFlag} -p "${prompt.replace(/"/g, '\\"')}" --output-format text 2>/dev/null`; return new Promise((resolve) => { exec(cmd, { timeout: this.config.timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (err, stdout) => { resolve(stdout.trim() || `[Grok error: ${err?.message?.slice(0, 200) ?? "unknown"}]`); }); }); } static describeConfig(): string { return [ "Grok CLI Executor", ` Binary: ${findGrokBin()}`, ` Model: ${process.env.GROK_MODEL ?? "grok-3 (set GROK_MODEL)"}`, ` Proxy: ${process.env.GROK_PROXY ?? "http://localhost:18901/v1"}`, " Mode: CLI -p (non-interactive)", "", "See ~/.grok/config.toml for proxy/model configuration.", ].join("\n"); } }