imhcode 2.0.4 → 3.0.0
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 +395 -150
- 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 +59 -12
- 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 +62 -13
- 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,
|
|
@@ -121,12 +122,12 @@ const DEFAULT_MODEL_PREFERENCES: Record<string, { engines: string[]; models: str
|
|
|
121
122
|
models: ["claude-opus-4-6", "gpt-5.5", "deepseek-v4-pro"],
|
|
122
123
|
},
|
|
123
124
|
testing: {
|
|
124
|
-
engines: ["codex", "claude", "opencode"],
|
|
125
|
-
models: ["gpt-5.5", "claude-opus-4-6", "deepseek-v4-pro"],
|
|
125
|
+
engines: ["codex-fugu", "codex", "claude", "opencode"],
|
|
126
|
+
models: ["fugu-ultra", "gpt-5.5", "claude-opus-4-6", "deepseek-v4-pro"],
|
|
126
127
|
},
|
|
127
128
|
review: {
|
|
128
|
-
engines: ["codex", "claude", "opencode"],
|
|
129
|
-
models: ["gpt-5.5", "claude-sonnet-4-5", "deepseek-v4"],
|
|
129
|
+
engines: ["codex-fugu", "codex", "claude", "opencode"],
|
|
130
|
+
models: ["fugu-ultra", "gpt-5.5", "claude-sonnet-4-5", "deepseek-v4"],
|
|
130
131
|
},
|
|
131
132
|
fast: {
|
|
132
133
|
engines: ["opencode", "codex", "qwen", "claude"],
|
|
@@ -295,6 +296,7 @@ export async function runAgent(
|
|
|
295
296
|
if (resolvedEngine === "antigravity" || resolvedEngine === "antigravity-ide") resolvedEngine = "agy";
|
|
296
297
|
if (resolvedEngine === "qwencode" || resolvedEngine === "qwen-code") resolvedEngine = "qwen";
|
|
297
298
|
if (resolvedEngine === "mimocode" || resolvedEngine === "mimo-code") resolvedEngine = "mimo";
|
|
299
|
+
if (resolvedEngine === "codexfugu" || resolvedEngine === "fugu") resolvedEngine = "codex-fugu";
|
|
298
300
|
|
|
299
301
|
// 2. Build the initial prompt (category-aware, compact context)
|
|
300
302
|
const finalPrompt = buildPrompt(agent, task, acceptanceCriteria, cwd);
|
|
@@ -305,9 +307,11 @@ export async function runAgent(
|
|
|
305
307
|
// 3. Load config for routing checks
|
|
306
308
|
const config = loadConfig(cwd);
|
|
307
309
|
const category = AGENT_CATEGORY_MAP[agent.manifest.id] ?? "backend";
|
|
310
|
+
const complexity = detectTaskComplexity(task);
|
|
311
|
+
const effectiveCategory = complexity === "light" ? "fast" : category;
|
|
308
312
|
|
|
309
313
|
// 4. Build the failover queue of engines to try
|
|
310
|
-
const failoverQueue = getFailoverQueue(agent, config, resolvedEngine);
|
|
314
|
+
const failoverQueue = getFailoverQueue(agent, config, resolvedEngine, effectiveCategory);
|
|
311
315
|
|
|
312
316
|
let currentEngineIndex = 0;
|
|
313
317
|
let currentEngine = resolvedEngine;
|
|
@@ -320,7 +324,7 @@ export async function runAgent(
|
|
|
320
324
|
while (currentEngineIndex < failoverQueue.length) {
|
|
321
325
|
currentEngine = failoverQueue[currentEngineIndex];
|
|
322
326
|
if (currentEngineIndex > 0) {
|
|
323
|
-
currentModel = getModelForEngine(config, currentEngine,
|
|
327
|
+
currentModel = getModelForEngine(config, currentEngine, effectiveCategory, agent);
|
|
324
328
|
} else {
|
|
325
329
|
currentModel = resolvedModel;
|
|
326
330
|
}
|
|
@@ -475,15 +479,27 @@ function getInstalledEngines(config: any): string[] {
|
|
|
475
479
|
/**
|
|
476
480
|
* Builds the ordered array of fallback engines to attempt during execution.
|
|
477
481
|
*/
|
|
478
|
-
function getFailoverQueue(
|
|
482
|
+
export function getFailoverQueue(
|
|
479
483
|
agent: LoadedAgent,
|
|
480
484
|
config: any,
|
|
481
|
-
initialEngine: string
|
|
485
|
+
initialEngine: string,
|
|
486
|
+
category: string
|
|
482
487
|
): string[] {
|
|
483
488
|
const installed = getInstalledEngines(config);
|
|
484
489
|
const queue = [initialEngine];
|
|
485
490
|
|
|
486
|
-
// 1. Add preferred engines from
|
|
491
|
+
// 1. Add category preferred engines from DEFAULT_MODEL_PREFERENCES
|
|
492
|
+
const prefs = DEFAULT_MODEL_PREFERENCES[category];
|
|
493
|
+
if (prefs) {
|
|
494
|
+
for (const eng of prefs.engines) {
|
|
495
|
+
const clean = eng.toLowerCase().trim();
|
|
496
|
+
if (!queue.includes(clean) && installed.includes(clean)) {
|
|
497
|
+
queue.push(clean);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// 2. Add preferred engines from manifest
|
|
487
503
|
if (agent.manifest.preferred_engines) {
|
|
488
504
|
for (const eng of agent.manifest.preferred_engines) {
|
|
489
505
|
const clean = eng.toLowerCase().trim();
|
|
@@ -493,7 +509,7 @@ function getFailoverQueue(
|
|
|
493
509
|
}
|
|
494
510
|
}
|
|
495
511
|
|
|
496
|
-
//
|
|
512
|
+
// 3. Add fallback engines from manifest
|
|
497
513
|
if (agent.manifest.fallback_engines) {
|
|
498
514
|
for (const eng of agent.manifest.fallback_engines) {
|
|
499
515
|
const clean = eng.toLowerCase().trim();
|
|
@@ -503,20 +519,36 @@ function getFailoverQueue(
|
|
|
503
519
|
}
|
|
504
520
|
}
|
|
505
521
|
|
|
506
|
-
//
|
|
522
|
+
// 4. Add any other installed engines as a last resort
|
|
507
523
|
for (const eng of installed) {
|
|
508
524
|
if (!queue.includes(eng)) {
|
|
509
525
|
queue.push(eng);
|
|
510
526
|
}
|
|
511
527
|
}
|
|
512
528
|
|
|
513
|
-
|
|
529
|
+
// 5. Expand agy to include agy2, agy3, agy4... etc. right after agy
|
|
530
|
+
const expandedQueue: string[] = [];
|
|
531
|
+
for (const eng of queue) {
|
|
532
|
+
if (!expandedQueue.includes(eng)) {
|
|
533
|
+
expandedQueue.push(eng);
|
|
534
|
+
}
|
|
535
|
+
if (eng === "agy" || eng === "antigravity") {
|
|
536
|
+
for (let i = 2; i <= 10; i++) {
|
|
537
|
+
const key = `agy${i}`;
|
|
538
|
+
if (installed.includes(key) && !expandedQueue.includes(key)) {
|
|
539
|
+
expandedQueue.push(key);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return expandedQueue;
|
|
514
546
|
}
|
|
515
547
|
|
|
516
548
|
/**
|
|
517
549
|
* Resolves the model to use for a failover engine.
|
|
518
550
|
*/
|
|
519
|
-
function getModelForEngine(
|
|
551
|
+
export function getModelForEngine(
|
|
520
552
|
config: any,
|
|
521
553
|
engineName: string,
|
|
522
554
|
category: string,
|
|
@@ -527,6 +559,23 @@ function getModelForEngine(
|
|
|
527
559
|
return config.model_routing[category].model;
|
|
528
560
|
}
|
|
529
561
|
|
|
562
|
+
// Check if there is a category preferred model for this engine in DEFAULT_MODEL_PREFERENCES
|
|
563
|
+
const lookupEngine = (engineName.startsWith("agy") || engineName === "antigravity") ? "agy" : engineName;
|
|
564
|
+
const prefs = DEFAULT_MODEL_PREFERENCES[category];
|
|
565
|
+
if (prefs) {
|
|
566
|
+
const engineIdx = prefs.engines.indexOf(lookupEngine);
|
|
567
|
+
if (engineIdx >= 0) {
|
|
568
|
+
const preferredMdl = prefs.models[engineIdx];
|
|
569
|
+
const engineData = config?.available_engines?.[engineName];
|
|
570
|
+
if (engineData?.models?.length > 0) {
|
|
571
|
+
const exactMatch = engineData.models.find((m: string) =>
|
|
572
|
+
m.toLowerCase().includes(preferredMdl.toLowerCase().replace(/-/g, ""))
|
|
573
|
+
);
|
|
574
|
+
if (exactMatch) return exactMatch;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
530
579
|
// Check available models in config for this engine
|
|
531
580
|
const engineData = config?.available_engines?.[engineName];
|
|
532
581
|
if (engineData?.models?.length > 0) {
|
|
@@ -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;
|