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.
- package/bin/imhcode.js +289 -217
- package/dist/orchestrator/executor.d.ts +6 -1
- package/dist/orchestrator/executor.d.ts.map +1 -1
- package/dist/orchestrator/executor.js +73 -16
- package/dist/orchestrator/executor.js.map +1 -1
- package/dist/orchestrator/index.d.ts +9 -1
- package/dist/orchestrator/index.d.ts.map +1 -1
- package/dist/orchestrator/index.js +29 -51
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/types.d.ts +1 -1
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/orchestrator/executor.ts +80 -15
- package/src/orchestrator/index.ts +28 -57
- package/src/orchestrator/types.ts +1 -1
|
@@ -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 = [
|
|
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,
|
|
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
|
|
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(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
232
|
-
|
|
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,
|
|
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 === "
|
|
315
|
-
return new
|
|
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.
|
|
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,
|
|
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
|
-
|
|
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;
|