/** * Antigrav Executor — PhaseExecutor for Antigravity CLI (agy). * * Antigrav is a general-purpose agentic CLI by Google. * Supports sandboxed execution, plugins, and multi-model routing. * * Integration: `agy --print ""` for non-interactive execution. * * Binary: C:\Users\Artale\AppData\Local\agy\bin\agy.exe (install via irm) * Docs: https://antigravity.google/cli * * Usage: * const ag = new AntigravExecutor({ model: "gemini-2.5-pro" }); * const result = await feedbackLoop.run(task, ag); */ 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 findAgyBin(): string { const candidates = [ path.join(process.env.LOCALAPPDATA || "", "agy", "bin", "agy.exe"), path.join(process.env.HOME || "", "AppData", "Local", "agy", "bin", "agy.exe"), "agy", ]; for (const c of candidates) { try { if (fs.existsSync(c)) return c; } catch {} } return "agy"; } export interface AntigravConfig { /** agy binary path */ bin?: string; /** Model name */ model?: string; /** Use sandbox mode */ sandbox?: boolean; /** Timeout per phase in ms */ timeoutMs?: number; } export class AntigravExecutor implements PhaseExecutor { private config: AntigravConfig; constructor(config?: AntigravConfig) { this.config = { bin: process.env.AGY_BIN || findAgyBin(), model: process.env.AGY_MODEL || "gemini-2.5-pro", sandbox: false, timeoutMs: 120000, ...config, }; } async plan(task: string, previousIteration: LoopIteration | null): Promise { const prev = previousIteration ? `\nPrevious: ${previousIteration.reflection?.slice(0, 200)}` : ""; return this.call(`You are in the PLAN phase.\nTask: ${task}${prev}`); } async execute(plan: string): Promise { return this.call(`EXECUTE:\n${plan}`); } async observe(executed: string): Promise { return this.call(`OBSERVE:\n${executed}`); } async reflect(observation: string, previousIteration: LoopIteration | null): Promise { const prev = previousIteration ? `\nExpected: ${previousIteration.refinement?.slice(0, 200)}` : ""; return this.call(`REFLECT:\n${observation}${prev}`); } async refine(reflection: string): Promise { return this.call(`REFINE:\n${reflection}`); } private async call(prompt: string): Promise { const modelFlag = `--model "${this.config.model}"`; const sandboxFlag = this.config.sandbox ? "--sandbox" : ""; const cmd = `"${this.config.bin}" --print ${modelFlag} ${sandboxFlag} "${prompt.replace(/"/g, '\\"')}" 2>/dev/null`; return new Promise((resolve) => { exec(cmd, { timeout: this.config.timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (err, stdout) => { resolve(stdout.trim() || `[Antigrav error: ${err?.message?.slice(0, 200) ?? "unknown"}]`); }); }); } static describeConfig(): string { const bin = process.env.AGY_BIN || findAgyBin(); return [ "Antigrav (agy) Executor", ` Binary: ${bin}`, ` Model: ${process.env.AGY_MODEL || "gemini-2.5-pro (set AGY_MODEL)"}`, " Mode: --print (non-interactive)", "", "Install: irm https://antigravity.google/cli/install.ps1 | iex", "Docs: https://antigravity.google/cli", ].join("\n"); } }