diff --git a/src/cli/config-manager.test.ts b/src/cli/config-manager.test.ts index cd95438da..1df99d9c7 100644 --- a/src/cli/config-manager.test.ts +++ b/src/cli/config-manager.test.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from "bun:test" -import { ANTIGRAVITY_PROVIDER_CONFIG } from "./config-manager" +import { ANTIGRAVITY_PROVIDER_CONFIG, generateOmoConfig } from "./config-manager" +import type { InstallConfig } from "./types" describe("config-manager ANTIGRAVITY_PROVIDER_CONFIG", () => { test("Gemini models include full spec (limit + modalities)", () => { @@ -32,3 +33,133 @@ describe("config-manager ANTIGRAVITY_PROVIDER_CONFIG", () => { } }) }) + +describe("generateOmoConfig - GitHub Copilot fallback", () => { + test("frontend-ui-ux-engineer uses Copilot when no native providers", () => { + // #given user has only Copilot (no Claude, ChatGPT, Gemini) + const config: InstallConfig = { + hasClaude: false, + isMax20: false, + hasChatGPT: false, + hasGemini: false, + hasCopilot: true, + } + + // #when generating config + const result = generateOmoConfig(config) + + // #then frontend-ui-ux-engineer should use Copilot Gemini + const agents = result.agents as Record + expect(agents["frontend-ui-ux-engineer"]?.model).toBe("github-copilot/gemini-3-pro-preview") + }) + + test("document-writer uses Copilot when no native providers", () => { + // #given user has only Copilot + const config: InstallConfig = { + hasClaude: false, + isMax20: false, + hasChatGPT: false, + hasGemini: false, + hasCopilot: true, + } + + // #when generating config + const result = generateOmoConfig(config) + + // #then document-writer should use Copilot Gemini Flash + const agents = result.agents as Record + expect(agents["document-writer"]?.model).toBe("github-copilot/gemini-3-flash-preview") + }) + + test("multimodal-looker uses Copilot when no native providers", () => { + // #given user has only Copilot + const config: InstallConfig = { + hasClaude: false, + isMax20: false, + hasChatGPT: false, + hasGemini: false, + hasCopilot: true, + } + + // #when generating config + const result = generateOmoConfig(config) + + // #then multimodal-looker should use Copilot Gemini Flash + const agents = result.agents as Record + expect(agents["multimodal-looker"]?.model).toBe("github-copilot/gemini-3-flash-preview") + }) + + test("explore uses Copilot grok-code when no native providers", () => { + // #given user has only Copilot + const config: InstallConfig = { + hasClaude: false, + isMax20: false, + hasChatGPT: false, + hasGemini: false, + hasCopilot: true, + } + + // #when generating config + const result = generateOmoConfig(config) + + // #then explore should use Copilot Grok + const agents = result.agents as Record + expect(agents["explore"]?.model).toBe("github-copilot/grok-code-fast-1") + }) + + test("native Gemini takes priority over Copilot for frontend-ui-ux-engineer", () => { + // #given user has both Gemini and Copilot + const config: InstallConfig = { + hasClaude: false, + isMax20: false, + hasChatGPT: false, + hasGemini: true, + hasCopilot: true, + } + + // #when generating config + const result = generateOmoConfig(config) + + // #then native Gemini should be used (NOT Copilot) + const agents = result.agents as Record + expect(agents["frontend-ui-ux-engineer"]?.model).toBe("google/antigravity-gemini-3-pro-high") + }) + + test("native Claude takes priority over Copilot for frontend-ui-ux-engineer", () => { + // #given user has Claude and Copilot but no Gemini + const config: InstallConfig = { + hasClaude: true, + isMax20: false, + hasChatGPT: false, + hasGemini: false, + hasCopilot: true, + } + + // #when generating config + const result = generateOmoConfig(config) + + // #then native Claude should be used (NOT Copilot) + const agents = result.agents as Record + expect(agents["frontend-ui-ux-engineer"]?.model).toBe("anthropic/claude-opus-4-5") + }) + + test("categories use Copilot models when no native Gemini", () => { + // #given user has Copilot but no Gemini + const config: InstallConfig = { + hasClaude: false, + isMax20: false, + hasChatGPT: false, + hasGemini: false, + hasCopilot: true, + } + + // #when generating config + const result = generateOmoConfig(config) + + // #then categories should use Copilot models + const categories = result.categories as Record + expect(categories?.["visual-engineering"]?.model).toBe("github-copilot/gemini-3-pro-preview") + expect(categories?.["artistry"]?.model).toBe("github-copilot/gemini-3-pro-preview") + expect(categories?.["writing"]?.model).toBe("github-copilot/gemini-3-flash-preview") + }) +}) diff --git a/src/cli/config-manager.ts b/src/cli/config-manager.ts index a48c6fe59..c889653b7 100644 --- a/src/cli/config-manager.ts +++ b/src/cli/config-manager.ts @@ -283,6 +283,8 @@ export function generateOmoConfig(installConfig: InstallConfig): Record 0) { config.agents = agents } - // Categories: override model for Antigravity auth (gemini-3-pro-preview → gemini-3-pro-high) + // Categories: override model for Antigravity auth or GitHub Copilot fallback if (installConfig.hasGemini) { config.categories = { "visual-engineering": { model: "google/gemini-3-pro-high" }, artistry: { model: "google/gemini-3-pro-high" }, writing: { model: "google/gemini-3-flash-high" }, } + } else if (installConfig.hasCopilot) { + config.categories = { + "visual-engineering": { model: "github-copilot/gemini-3-pro-preview" }, + artistry: { model: "github-copilot/gemini-3-pro-preview" }, + writing: { model: "github-copilot/gemini-3-flash-preview" }, + } } return config diff --git a/src/cli/install.ts b/src/cli/install.ts index 79ea22fb6..8e648ae26 100644 --- a/src/cli/install.ts +++ b/src/cli/install.ts @@ -311,25 +311,10 @@ async function runNonTuiInstall(args: InstallArgs): Promise { printBox(formatConfigSummary(config), isUpdate ? "Updated Configuration" : "Installation Complete") - if (!config.hasClaude && !config.hasChatGPT && !config.hasGemini) { + if (!config.hasClaude && !config.hasChatGPT && !config.hasGemini && !config.hasCopilot) { printWarning("No model providers configured. Using opencode/glm-4.7-free as fallback.") } - if ((config.hasClaude || config.hasChatGPT || config.hasGemini) && !args.skipAuth) { - console.log(color.bold("Next Steps - Authenticate your providers:")) - console.log() - if (config.hasClaude) { - console.log(` ${SYMBOLS.arrow} ${color.dim("opencode auth login")} ${color.gray("(select Anthropic → Claude Pro/Max)")}`) - } - if (config.hasChatGPT) { - console.log(` ${SYMBOLS.arrow} ${color.dim("opencode auth login")} ${color.gray("(select OpenAI → ChatGPT Plus/Pro)")}`) - } - if (config.hasGemini) { - console.log(` ${SYMBOLS.arrow} ${color.dim("opencode auth login")} ${color.gray("(select Google → OAuth with Antigravity)")}`) - } - console.log() - } - console.log(`${SYMBOLS.star} ${color.bold(color.green(isUpdate ? "Configuration updated!" : "Installation complete!"))}`) console.log(` Run ${color.cyan("opencode")} to start!`) console.log() @@ -347,6 +332,17 @@ async function runNonTuiInstall(args: InstallArgs): Promise { console.log(color.dim("oMoMoMoMo... Enjoy!")) console.log() + if ((config.hasClaude || config.hasChatGPT || config.hasGemini || config.hasCopilot) && !args.skipAuth) { + printBox( + `Run ${color.cyan("opencode auth login")} and select your provider:\n` + + (config.hasClaude ? ` ${SYMBOLS.bullet} Anthropic ${color.gray("→ Claude Pro/Max")}\n` : "") + + (config.hasChatGPT ? ` ${SYMBOLS.bullet} OpenAI ${color.gray("→ ChatGPT Plus/Pro")}\n` : "") + + (config.hasGemini ? ` ${SYMBOLS.bullet} Google ${color.gray("→ OAuth with Antigravity")}\n` : "") + + (config.hasCopilot ? ` ${SYMBOLS.bullet} GitHub ${color.gray("→ Copilot")}` : ""), + "🔐 Authenticate Your Providers" + ) + } + return 0 } @@ -421,26 +417,12 @@ export async function install(args: InstallArgs): Promise { } s.stop(`Config written to ${color.cyan(omoResult.configPath)}`) - if (!config.hasClaude && !config.hasChatGPT && !config.hasGemini) { + if (!config.hasClaude && !config.hasChatGPT && !config.hasGemini && !config.hasCopilot) { p.log.warn("No model providers configured. Using opencode/glm-4.7-free as fallback.") } p.note(formatConfigSummary(config), isUpdate ? "Updated Configuration" : "Installation Complete") - if ((config.hasClaude || config.hasChatGPT || config.hasGemini) && !args.skipAuth) { - const steps: string[] = [] - if (config.hasClaude) { - steps.push(`${color.dim("opencode auth login")} ${color.gray("(select Anthropic → Claude Pro/Max)")}`) - } - if (config.hasChatGPT) { - steps.push(`${color.dim("opencode auth login")} ${color.gray("(select OpenAI → ChatGPT Plus/Pro)")}`) - } - if (config.hasGemini) { - steps.push(`${color.dim("opencode auth login")} ${color.gray("(select Google → OAuth with Antigravity)")}`) - } - p.note(steps.join("\n"), "Next Steps - Authenticate your providers") - } - p.log.success(color.bold(isUpdate ? "Configuration updated!" : "Installation complete!")) p.log.message(`Run ${color.cyan("opencode")} to start!`) @@ -456,5 +438,22 @@ export async function install(args: InstallArgs): Promise { p.outro(color.green("oMoMoMoMo... Enjoy!")) + if ((config.hasClaude || config.hasChatGPT || config.hasGemini || config.hasCopilot) && !args.skipAuth) { + const providers: string[] = [] + if (config.hasClaude) providers.push(`Anthropic ${color.gray("→ Claude Pro/Max")}`) + if (config.hasChatGPT) providers.push(`OpenAI ${color.gray("→ ChatGPT Plus/Pro")}`) + if (config.hasGemini) providers.push(`Google ${color.gray("→ OAuth with Antigravity")}`) + if (config.hasCopilot) providers.push(`GitHub ${color.gray("→ Copilot")}`) + + console.log() + console.log(color.bold("🔐 Authenticate Your Providers")) + console.log() + console.log(` Run ${color.cyan("opencode auth login")} and select:`) + for (const provider of providers) { + console.log(` ${SYMBOLS.bullet} ${provider}`) + } + console.log() + } + return 0 }