imhcode 2.0.5 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -129,9 +129,14 @@ export class ClaudeCodeCLIAdapter implements EngineAdapter {
129
129
  const binary = resolveBinary("claude", [
130
130
  "~/.local/bin/claude",
131
131
  "/usr/local/bin/claude",
132
+ "/opt/homebrew/bin/claude",
132
133
  ]);
133
134
 
134
- const args = ["-p"];
135
+ const args = [
136
+ "-p",
137
+ "--dangerously-skip-permissions",
138
+ "--no-session-persistence",
139
+ ];
135
140
  if (model && model !== "claude" && model !== "default") {
136
141
  args.push("--model", model);
137
142
  }
@@ -155,8 +160,9 @@ export class OpenCodeCLIAdapter implements EngineAdapter {
155
160
  if (model && model !== "opencode" && model !== "default") {
156
161
  args.push("-m", model);
157
162
  }
163
+ args.push(prompt);
158
164
 
159
- const rawOutput = await runCliWithStdin(binary, args, prompt, "OpenCode");
165
+ const rawOutput = await runCliWithStdin(binary, args, "", "OpenCode");
160
166
 
161
167
  // OpenCode prints progress lines starting with "> build ·" or similar.
162
168
  // Filter these out to get a clean result.
@@ -212,28 +218,78 @@ export class CodexCLIAdapter implements EngineAdapter {
212
218
  }
213
219
  }
214
220
 
221
+ // ─── Codex Fugu CLI Adapter ────────────────────────────────────────────────────
222
+
223
+ export class CodexFuguCLIAdapter implements EngineAdapter {
224
+ readonly name = "codex-fugu";
225
+
226
+ async run(prompt: string, model: string): Promise<string> {
227
+ const binary = resolveBinary("codex-fugu", [
228
+ "~/.local/bin/codex-fugu",
229
+ "/usr/local/bin/codex-fugu",
230
+ "/opt/homebrew/bin/codex-fugu",
231
+ ]);
232
+
233
+ const tempFile = path.join(
234
+ process.cwd(),
235
+ `.codex-fugu-out-${crypto.randomBytes(6).toString("hex")}.txt`
236
+ );
237
+
238
+ const args = [
239
+ "--dangerously-bypass-approvals-and-sandbox",
240
+ "exec",
241
+ "-",
242
+ "-o",
243
+ tempFile,
244
+ ];
245
+ if (model && model !== "codex-fugu" && model !== "default") {
246
+ args.push("-m", model);
247
+ }
248
+
249
+ try {
250
+ await runCliWithStdin(binary, args, prompt, "Codex Fugu");
251
+ if (!fs.existsSync(tempFile)) {
252
+ throw new Error(`Codex Fugu execution succeeded but output file was not created at ${tempFile}`);
253
+ }
254
+ const output = fs.readFileSync(tempFile, "utf8");
255
+ return output;
256
+ } finally {
257
+ if (fs.existsSync(tempFile)) {
258
+ fs.unlinkSync(tempFile);
259
+ }
260
+ }
261
+ }
262
+ }
263
+
215
264
  // ─── Antigravity CLI Adapter (agy) ────────────────────────────────────────────
216
265
  // Google Antigravity CLI — binary: `agy`
217
266
  // Docs: https://antigravity.google/docs/cli-using
218
267
  // Headless: `agy -p "<prompt>"` or via stdin with `agy -p`
219
268
 
220
269
  export class AgyCLIAdapter implements EngineAdapter {
221
- readonly name = "agy";
270
+ readonly name: string;
271
+
272
+ constructor(name: string = "agy") {
273
+ this.name = name;
274
+ }
222
275
 
223
276
  async run(prompt: string, model: string): Promise<string> {
224
- const binary = resolveBinary("agy", [
225
- "~/.local/bin/agy",
226
- "/usr/local/bin/agy",
227
- "/opt/homebrew/bin/agy",
277
+ const binary = resolveBinary(this.name, [
278
+ `~/.local/bin/${this.name}`,
279
+ `/usr/local/bin/${this.name}`,
280
+ `/opt/homebrew/bin/${this.name}`,
228
281
  ]);
229
282
 
230
- const args: string[] = [];
231
- if (model && model !== "agy" && model !== "default") {
232
- args.push("--model", model);
283
+ const args: string[] = [
284
+ "--dangerously-skip-permissions",
285
+ "-p",
286
+ prompt,
287
+ ];
288
+ if (model && model !== this.name && model !== "agy" && model !== "default") {
289
+ args.unshift("--model", model);
233
290
  }
234
- args.push("-p", "-");
235
291
 
236
- return await runCliWithStdin(binary, args, prompt, "Antigravity (agy)");
292
+ return await runCliWithStdin(binary, args, "", `Antigravity (${this.name})`);
237
293
  }
238
294
  }
239
295
 
@@ -311,8 +367,11 @@ export function getAdapter(
311
367
  if (engine === "codex") {
312
368
  return new CodexCLIAdapter();
313
369
  }
314
- if (engine === "agy" || engine === "antigravity") {
315
- return new AgyCLIAdapter();
370
+ if (engine === "codex-fugu" || engine === "codexfugu" || engine === "fugu") {
371
+ return new CodexFuguCLIAdapter();
372
+ }
373
+ if (engine === "agy" || engine === "antigravity" || /^agy\d+$/.test(engine)) {
374
+ return new AgyCLIAdapter(engine === "antigravity" ? "agy" : engine);
316
375
  }
317
376
  if (engine === "qwen" || engine === "qwencode" || engine === "qwen-code") {
318
377
  return new QwenCodeCLIAdapter();
@@ -325,6 +384,9 @@ export function getAdapter(
325
384
  if (engine.startsWith("claude")) {
326
385
  return new ClaudeCodeCLIAdapter();
327
386
  }
387
+ if (engine.startsWith("codex-fugu") || engine.startsWith("fugu")) {
388
+ return new CodexFuguCLIAdapter();
389
+ }
328
390
  if (engine.startsWith("gpt") || engine.startsWith("o1") || engine.startsWith("o3") || engine.startsWith("o4")) {
329
391
  return new CodexCLIAdapter();
330
392
  }
@@ -364,6 +426,9 @@ export function getAdapter(
364
426
  if (binaryExists("claude", ["~/.local/bin/claude"])) {
365
427
  return new ClaudeCodeCLIAdapter();
366
428
  }
429
+ if (binaryExists("codex-fugu", ["~/.local/bin/codex-fugu", "/opt/homebrew/bin/codex-fugu"])) {
430
+ return new CodexFuguCLIAdapter();
431
+ }
367
432
  if (binaryExists("codex", ["~/Library/PhpWebStudy/env/node/bin/codex"])) {
368
433
  return new CodexCLIAdapter();
369
434
  }
@@ -379,7 +444,7 @@ export function getAdapter(
379
444
 
380
445
  throw new Error(
381
446
  `❌ No active CLI agent engine could be auto-detected.\n` +
382
- ` Supported engines: claude, opencode, codex, agy, qwen, mimo\n` +
447
+ ` Supported engines: claude, opencode, codex, codex-fugu, agy, qwen, mimo\n` +
383
448
  ` Install one and re-run, or specify with: --engine <engine-name>\n` +
384
449
  ` Run "imhcode --help" for more information.`
385
450
  );
@@ -39,6 +39,7 @@ export {
39
39
  ClaudeCodeCLIAdapter,
40
40
  OpenCodeCLIAdapter,
41
41
  CodexCLIAdapter,
42
+ CodexFuguCLIAdapter,
42
43
  AgyCLIAdapter,
43
44
  QwenCodeCLIAdapter,
44
45
  MimoCodeCLIAdapter,
@@ -106,34 +107,6 @@ export const AGENT_CATEGORY_MAP: Record<string, string> = {
106
107
  * Model routing: recommended models per category and engine.
107
108
  * Preferences: Mimo v2.5 Pro → frontend, DeepSeek → backend,
108
109
  * GPT → testing/review, Claude → planning, DeepSeek Flash → fast
109
- */
110
- const DEFAULT_MODEL_PREFERENCES: Record<string, { engines: string[]; models: string[] }> = {
111
- frontend: {
112
- engines: ["mimo", "codex", "claude", "opencode"],
113
- models: ["mimo-vl-v2.5-pro", "gpt-5.5", "claude-opus-4-6", "gemini-3.5-flash"],
114
- },
115
- backend: {
116
- engines: ["opencode", "codex", "claude", "qwen"],
117
- models: ["deepseek-v4-pro", "gpt-5.5", "claude-sonnet-4-5", "qwen3-coder-max"],
118
- },
119
- planning: {
120
- engines: ["claude", "codex", "opencode"],
121
- models: ["claude-opus-4-6", "gpt-5.5", "deepseek-v4-pro"],
122
- },
123
- testing: {
124
- engines: ["codex", "claude", "opencode"],
125
- models: ["gpt-5.5", "claude-opus-4-6", "deepseek-v4-pro"],
126
- },
127
- review: {
128
- engines: ["codex", "claude", "opencode"],
129
- models: ["gpt-5.5", "claude-sonnet-4-5", "deepseek-v4"],
130
- },
131
- fast: {
132
- engines: ["opencode", "codex", "qwen", "claude"],
133
- models: ["deepseek-v4-flash", "gpt-5.4-mini", "qwen3-coder-flash", "claude-haiku-3-5"],
134
- },
135
- };
136
-
137
110
  // ─── Config Loader ────────────────────────────────────────────────────────────
138
111
 
139
112
  /**
@@ -217,29 +190,7 @@ function resolveModelForTask(
217
190
  };
218
191
  }
219
192
 
220
- // 2. Find best available model from installed engines
221
- const prefs = DEFAULT_MODEL_PREFERENCES[effectiveCategory];
222
- if (prefs && config?.available_engines) {
223
- for (let i = 0; i < prefs.engines.length; i++) {
224
- const preferredEngine = prefs.engines[i];
225
- const preferredModel = prefs.models[i];
226
- const engineData = config.available_engines[preferredEngine];
227
-
228
- if (engineData?.models?.length > 0) {
229
- // Check if preferred model is available
230
- const exactMatch = engineData.models.find((m: string) =>
231
- m.toLowerCase().includes(preferredModel.toLowerCase().replace(/-/g, ""))
232
- );
233
- if (exactMatch) {
234
- return { engine: preferredEngine, model: exactMatch };
235
- }
236
- // Use any available model from this preferred engine
237
- return { engine: preferredEngine, model: engineData.models[0] };
238
- }
239
- }
240
- }
241
-
242
- // 3. Fallback: use primary engine + default model
193
+ // 2. Fallback: use primary engine + default model
243
194
  const primaryEngine = config?.primary_engine || engine;
244
195
  const defaultModel = config?.default_model || agent.manifest.default_model;
245
196
  return { engine: primaryEngine, model: defaultModel };
@@ -295,6 +246,7 @@ export async function runAgent(
295
246
  if (resolvedEngine === "antigravity" || resolvedEngine === "antigravity-ide") resolvedEngine = "agy";
296
247
  if (resolvedEngine === "qwencode" || resolvedEngine === "qwen-code") resolvedEngine = "qwen";
297
248
  if (resolvedEngine === "mimocode" || resolvedEngine === "mimo-code") resolvedEngine = "mimo";
249
+ if (resolvedEngine === "codexfugu" || resolvedEngine === "fugu") resolvedEngine = "codex-fugu";
298
250
 
299
251
  // 2. Build the initial prompt (category-aware, compact context)
300
252
  const finalPrompt = buildPrompt(agent, task, acceptanceCriteria, cwd);
@@ -305,9 +257,11 @@ export async function runAgent(
305
257
  // 3. Load config for routing checks
306
258
  const config = loadConfig(cwd);
307
259
  const category = AGENT_CATEGORY_MAP[agent.manifest.id] ?? "backend";
260
+ const complexity = detectTaskComplexity(task);
261
+ const effectiveCategory = complexity === "light" ? "fast" : category;
308
262
 
309
263
  // 4. Build the failover queue of engines to try
310
- const failoverQueue = getFailoverQueue(agent, config, resolvedEngine);
264
+ const failoverQueue = getFailoverQueue(agent, config, resolvedEngine, effectiveCategory);
311
265
 
312
266
  let currentEngineIndex = 0;
313
267
  let currentEngine = resolvedEngine;
@@ -320,7 +274,7 @@ export async function runAgent(
320
274
  while (currentEngineIndex < failoverQueue.length) {
321
275
  currentEngine = failoverQueue[currentEngineIndex];
322
276
  if (currentEngineIndex > 0) {
323
- currentModel = getModelForEngine(config, currentEngine, category, agent);
277
+ currentModel = getModelForEngine(config, currentEngine, effectiveCategory, agent);
324
278
  } else {
325
279
  currentModel = resolvedModel;
326
280
  }
@@ -475,10 +429,11 @@ function getInstalledEngines(config: any): string[] {
475
429
  /**
476
430
  * Builds the ordered array of fallback engines to attempt during execution.
477
431
  */
478
- function getFailoverQueue(
432
+ export function getFailoverQueue(
479
433
  agent: LoadedAgent,
480
434
  config: any,
481
- initialEngine: string
435
+ initialEngine: string,
436
+ category?: string
482
437
  ): string[] {
483
438
  const installed = getInstalledEngines(config);
484
439
  const queue = [initialEngine];
@@ -510,13 +465,29 @@ function getFailoverQueue(
510
465
  }
511
466
  }
512
467
 
513
- return queue;
468
+ // 4. Expand agy to include agy2, agy3, agy4... etc. right after agy
469
+ const expandedQueue: string[] = [];
470
+ for (const eng of queue) {
471
+ if (!expandedQueue.includes(eng)) {
472
+ expandedQueue.push(eng);
473
+ }
474
+ if (eng === "agy" || eng === "antigravity") {
475
+ for (let i = 2; i <= 10; i++) {
476
+ const key = `agy${i}`;
477
+ if (installed.includes(key) && !expandedQueue.includes(key)) {
478
+ expandedQueue.push(key);
479
+ }
480
+ }
481
+ }
482
+ }
483
+
484
+ return expandedQueue;
514
485
  }
515
486
 
516
487
  /**
517
488
  * Resolves the model to use for a failover engine.
518
489
  */
519
- function getModelForEngine(
490
+ export function getModelForEngine(
520
491
  config: any,
521
492
  engineName: string,
522
493
  category: string,
@@ -67,7 +67,7 @@ export interface LoadedAgent {
67
67
  // ─── Execution ────────────────────────────────────────────────────────────────
68
68
 
69
69
  export interface ExecutionOptions {
70
- /** Override the CLI engine (claude, opencode, codex, antigravity) */
70
+ /** Override the CLI engine (claude, opencode, codex, codex-fugu, antigravity) */
71
71
  engine?: string;
72
72
  /** Explicitly override the model (e.g. gpt-4o, gemini-3.5-flash) */
73
73
  model?: string;