Merge origin/dev into feature/upgrade-minimax-m2.7 (resolve conflicts)
This commit is contained in:
+17
-1
@@ -3,6 +3,7 @@ import { install } from "./install"
|
||||
import { run } from "./run"
|
||||
import { getLocalVersion } from "./get-local-version"
|
||||
import { doctor } from "./doctor"
|
||||
import { refreshModelCapabilities } from "./refresh-model-capabilities"
|
||||
import { createMcpOAuthCommand } from "./mcp-oauth"
|
||||
import type { InstallArgs } from "./types"
|
||||
import type { RunOptions } from "./run"
|
||||
@@ -42,7 +43,7 @@ Examples:
|
||||
Model Providers (Priority: Native > Copilot > OpenCode Zen > Z.ai > Kimi):
|
||||
Claude Native anthropic/ models (Opus, Sonnet, Haiku)
|
||||
OpenAI Native openai/ models (GPT-5.4 for Oracle)
|
||||
Gemini Native google/ models (Gemini 3 Pro, Flash)
|
||||
Gemini Native google/ models (Gemini 3.1 Pro, Flash)
|
||||
Copilot github-copilot/ models (fallback)
|
||||
OpenCode Zen opencode/ models (opencode/claude-opus-4-6, etc.)
|
||||
Z.ai zai-coding-plan/glm-5 (visual-engineering fallback)
|
||||
@@ -176,6 +177,21 @@ Examples:
|
||||
process.exit(exitCode)
|
||||
})
|
||||
|
||||
program
|
||||
.command("refresh-model-capabilities")
|
||||
.description("Refresh the cached models.dev-based model capabilities snapshot")
|
||||
.option("-d, --directory <path>", "Working directory to read oh-my-opencode config from")
|
||||
.option("--source-url <url>", "Override the models.dev source URL")
|
||||
.option("--json", "Output refresh summary as JSON")
|
||||
.action(async (options) => {
|
||||
const exitCode = await refreshModelCapabilities({
|
||||
directory: options.directory,
|
||||
sourceUrl: options.sourceUrl,
|
||||
json: options.json ?? false,
|
||||
})
|
||||
process.exit(exitCode)
|
||||
})
|
||||
|
||||
program
|
||||
.command("version")
|
||||
.description("Show version information")
|
||||
|
||||
@@ -2,15 +2,15 @@ import { readFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
|
||||
import { OhMyOpenCodeConfigSchema } from "../../../config"
|
||||
import { detectConfigFile, getOpenCodeConfigDir, parseJsonc } from "../../../shared"
|
||||
import { detectPluginConfigFile, getOpenCodeConfigDir, parseJsonc } from "../../../shared"
|
||||
import { CHECK_IDS, CHECK_NAMES, PACKAGE_NAME } from "../constants"
|
||||
import type { CheckResult, DoctorIssue } from "../types"
|
||||
import { loadAvailableModelsFromCache } from "./model-resolution-cache"
|
||||
import { getModelResolutionInfoWithOverrides } from "./model-resolution"
|
||||
import type { OmoConfig } from "./model-resolution-types"
|
||||
|
||||
const USER_CONFIG_BASE = join(getOpenCodeConfigDir({ binary: "opencode" }), PACKAGE_NAME)
|
||||
const PROJECT_CONFIG_BASE = join(process.cwd(), ".opencode", PACKAGE_NAME)
|
||||
const USER_CONFIG_DIR = getOpenCodeConfigDir({ binary: "opencode" })
|
||||
const PROJECT_CONFIG_DIR = join(process.cwd(), ".opencode")
|
||||
|
||||
interface ConfigValidationResult {
|
||||
exists: boolean
|
||||
@@ -21,10 +21,10 @@ interface ConfigValidationResult {
|
||||
}
|
||||
|
||||
function findConfigPath(): string | null {
|
||||
const projectConfig = detectConfigFile(PROJECT_CONFIG_BASE)
|
||||
const projectConfig = detectPluginConfigFile(PROJECT_CONFIG_DIR)
|
||||
if (projectConfig.format !== "none") return projectConfig.path
|
||||
|
||||
const userConfig = detectConfigFile(USER_CONFIG_BASE)
|
||||
const userConfig = detectPluginConfigFile(USER_CONFIG_DIR)
|
||||
if (userConfig.format !== "none") return userConfig.path
|
||||
|
||||
return null
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
import { readFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
import { detectConfigFile, getOpenCodeConfigPaths, parseJsonc } from "../../../shared"
|
||||
import { detectPluginConfigFile, getOpenCodeConfigPaths, parseJsonc } from "../../../shared"
|
||||
import type { OmoConfig } from "./model-resolution-types"
|
||||
|
||||
const PACKAGE_NAME = "oh-my-opencode"
|
||||
const USER_CONFIG_BASE = join(
|
||||
getOpenCodeConfigPaths({ binary: "opencode", version: null }).configDir,
|
||||
PACKAGE_NAME
|
||||
)
|
||||
const PROJECT_CONFIG_BASE = join(process.cwd(), ".opencode", PACKAGE_NAME)
|
||||
const USER_CONFIG_DIR = getOpenCodeConfigPaths({ binary: "opencode", version: null }).configDir
|
||||
const PROJECT_CONFIG_DIR = join(process.cwd(), ".opencode")
|
||||
|
||||
export function loadOmoConfig(): OmoConfig | null {
|
||||
const projectDetected = detectConfigFile(PROJECT_CONFIG_BASE)
|
||||
const projectDetected = detectPluginConfigFile(PROJECT_CONFIG_DIR)
|
||||
if (projectDetected.format !== "none") {
|
||||
try {
|
||||
const content = readFileSync(projectDetected.path, "utf-8")
|
||||
@@ -21,7 +17,7 @@ export function loadOmoConfig(): OmoConfig | null {
|
||||
}
|
||||
}
|
||||
|
||||
const userDetected = detectConfigFile(USER_CONFIG_BASE)
|
||||
const userDetected = detectPluginConfigFile(USER_CONFIG_DIR)
|
||||
if (userDetected.format !== "none") {
|
||||
try {
|
||||
const content = readFileSync(userDetected.path, "utf-8")
|
||||
|
||||
@@ -4,6 +4,10 @@ import { getOpenCodeCacheDir } from "../../../shared"
|
||||
import type { AvailableModelsInfo, ModelResolutionInfo, OmoConfig } from "./model-resolution-types"
|
||||
import { formatModelWithVariant, getCategoryEffectiveVariant, getEffectiveVariant } from "./model-resolution-variant"
|
||||
|
||||
function formatCapabilityResolutionLabel(mode: string | undefined): string {
|
||||
return mode ?? "unknown"
|
||||
}
|
||||
|
||||
export function buildModelResolutionDetails(options: {
|
||||
info: ModelResolutionInfo
|
||||
available: AvailableModelsInfo
|
||||
@@ -37,7 +41,7 @@ export function buildModelResolutionDetails(options: {
|
||||
agent.effectiveModel,
|
||||
getEffectiveVariant(agent.name, agent.requirement, options.config)
|
||||
)
|
||||
details.push(` ${marker} ${agent.name}: ${display}`)
|
||||
details.push(` ${marker} ${agent.name}: ${display} [capabilities: ${formatCapabilityResolutionLabel(agent.capabilityDiagnostics?.resolutionMode)}]`)
|
||||
}
|
||||
details.push("")
|
||||
details.push("Categories:")
|
||||
@@ -47,7 +51,7 @@ export function buildModelResolutionDetails(options: {
|
||||
category.effectiveModel,
|
||||
getCategoryEffectiveVariant(category.name, category.requirement, options.config)
|
||||
)
|
||||
details.push(` ${marker} ${category.name}: ${display}`)
|
||||
details.push(` ${marker} ${category.name}: ${display} [capabilities: ${formatCapabilityResolutionLabel(category.capabilityDiagnostics?.resolutionMode)}]`)
|
||||
}
|
||||
details.push("")
|
||||
details.push("● = user override, ○ = provider fallback")
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ModelCapabilitiesDiagnostics } from "../../../shared/model-capabilities"
|
||||
import type { ModelRequirement } from "../../../shared/model-requirements"
|
||||
|
||||
export interface AgentResolutionInfo {
|
||||
@@ -7,6 +8,7 @@ export interface AgentResolutionInfo {
|
||||
userVariant?: string
|
||||
effectiveModel: string
|
||||
effectiveResolution: string
|
||||
capabilityDiagnostics?: ModelCapabilitiesDiagnostics
|
||||
}
|
||||
|
||||
export interface CategoryResolutionInfo {
|
||||
@@ -16,6 +18,7 @@ export interface CategoryResolutionInfo {
|
||||
userVariant?: string
|
||||
effectiveModel: string
|
||||
effectiveResolution: string
|
||||
capabilityDiagnostics?: ModelCapabilitiesDiagnostics
|
||||
}
|
||||
|
||||
export interface ModelResolutionInfo {
|
||||
|
||||
@@ -129,6 +129,19 @@ describe("model-resolution check", () => {
|
||||
expect(visual!.userOverride).toBe("google/gemini-3-flash-preview")
|
||||
expect(visual!.userVariant).toBe("high")
|
||||
})
|
||||
|
||||
it("attaches snapshot-backed capability diagnostics for built-in models", async () => {
|
||||
const { getModelResolutionInfoWithOverrides } = await import("./model-resolution")
|
||||
|
||||
const info = getModelResolutionInfoWithOverrides({})
|
||||
const sisyphus = info.agents.find((a) => a.name === "sisyphus")
|
||||
|
||||
expect(sisyphus).toBeDefined()
|
||||
expect(sisyphus!.capabilityDiagnostics).toMatchObject({
|
||||
resolutionMode: "snapshot-backed",
|
||||
snapshot: { source: "bundled-snapshot" },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("checkModelResolution", () => {
|
||||
@@ -162,6 +175,23 @@ describe("model-resolution check", () => {
|
||||
expect(result.details!.some((d) => d.includes("Categories:"))).toBe(true)
|
||||
// Should have legend
|
||||
expect(result.details!.some((d) => d.includes("user override"))).toBe(true)
|
||||
expect(result.details!.some((d) => d.includes("capabilities: snapshot-backed"))).toBe(true)
|
||||
})
|
||||
|
||||
it("collects warnings when configured models rely on compatibility fallback", async () => {
|
||||
const { collectCapabilityResolutionIssues, getModelResolutionInfoWithOverrides } = await import("./model-resolution")
|
||||
|
||||
const info = getModelResolutionInfoWithOverrides({
|
||||
agents: {
|
||||
oracle: { model: "custom/unknown-llm" },
|
||||
},
|
||||
})
|
||||
|
||||
const issues = collectCapabilityResolutionIssues(info)
|
||||
|
||||
expect(issues).toHaveLength(1)
|
||||
expect(issues[0]?.title).toContain("compatibility fallback")
|
||||
expect(issues[0]?.description).toContain("oracle=custom/unknown-llm")
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS } from "../../../shared/model-requirements"
|
||||
import { getModelCapabilities } from "../../../shared/model-capabilities"
|
||||
import { CHECK_IDS, CHECK_NAMES } from "../constants"
|
||||
import type { CheckResult, DoctorIssue } from "../types"
|
||||
import { loadAvailableModelsFromCache } from "./model-resolution-cache"
|
||||
@@ -7,16 +8,36 @@ import { buildModelResolutionDetails } from "./model-resolution-details"
|
||||
import { buildEffectiveResolution, getEffectiveModel } from "./model-resolution-effective-model"
|
||||
import type { AgentResolutionInfo, CategoryResolutionInfo, ModelResolutionInfo, OmoConfig } from "./model-resolution-types"
|
||||
|
||||
export function getModelResolutionInfo(): ModelResolutionInfo {
|
||||
const agents: AgentResolutionInfo[] = Object.entries(AGENT_MODEL_REQUIREMENTS).map(([name, requirement]) => ({
|
||||
name,
|
||||
requirement,
|
||||
effectiveModel: getEffectiveModel(requirement),
|
||||
effectiveResolution: buildEffectiveResolution(requirement),
|
||||
}))
|
||||
function parseProviderModel(value: string): { providerID: string; modelID: string } | null {
|
||||
const slashIndex = value.indexOf("/")
|
||||
if (slashIndex <= 0 || slashIndex === value.length - 1) {
|
||||
return null
|
||||
}
|
||||
|
||||
const categories: CategoryResolutionInfo[] = Object.entries(CATEGORY_MODEL_REQUIREMENTS).map(
|
||||
([name, requirement]) => ({
|
||||
return {
|
||||
providerID: value.slice(0, slashIndex),
|
||||
modelID: value.slice(slashIndex + 1),
|
||||
}
|
||||
}
|
||||
|
||||
function attachCapabilityDiagnostics<T extends AgentResolutionInfo | CategoryResolutionInfo>(entry: T): T {
|
||||
const parsed = parseProviderModel(entry.effectiveModel)
|
||||
if (!parsed) {
|
||||
return entry
|
||||
}
|
||||
|
||||
return {
|
||||
...entry,
|
||||
capabilityDiagnostics: getModelCapabilities({
|
||||
providerID: parsed.providerID,
|
||||
modelID: parsed.modelID,
|
||||
}).diagnostics,
|
||||
}
|
||||
}
|
||||
|
||||
export function getModelResolutionInfo(): ModelResolutionInfo {
|
||||
const agents: AgentResolutionInfo[] = Object.entries(AGENT_MODEL_REQUIREMENTS).map(([name, requirement]) =>
|
||||
attachCapabilityDiagnostics({
|
||||
name,
|
||||
requirement,
|
||||
effectiveModel: getEffectiveModel(requirement),
|
||||
@@ -24,6 +45,16 @@ export function getModelResolutionInfo(): ModelResolutionInfo {
|
||||
})
|
||||
)
|
||||
|
||||
const categories: CategoryResolutionInfo[] = Object.entries(CATEGORY_MODEL_REQUIREMENTS).map(
|
||||
([name, requirement]) =>
|
||||
attachCapabilityDiagnostics({
|
||||
name,
|
||||
requirement,
|
||||
effectiveModel: getEffectiveModel(requirement),
|
||||
effectiveResolution: buildEffectiveResolution(requirement),
|
||||
})
|
||||
)
|
||||
|
||||
return { agents, categories }
|
||||
}
|
||||
|
||||
@@ -31,34 +62,60 @@ export function getModelResolutionInfoWithOverrides(config: OmoConfig): ModelRes
|
||||
const agents: AgentResolutionInfo[] = Object.entries(AGENT_MODEL_REQUIREMENTS).map(([name, requirement]) => {
|
||||
const userOverride = config.agents?.[name]?.model
|
||||
const userVariant = config.agents?.[name]?.variant
|
||||
return {
|
||||
return attachCapabilityDiagnostics({
|
||||
name,
|
||||
requirement,
|
||||
userOverride,
|
||||
userVariant,
|
||||
effectiveModel: getEffectiveModel(requirement, userOverride),
|
||||
effectiveResolution: buildEffectiveResolution(requirement, userOverride),
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const categories: CategoryResolutionInfo[] = Object.entries(CATEGORY_MODEL_REQUIREMENTS).map(
|
||||
([name, requirement]) => {
|
||||
const userOverride = config.categories?.[name]?.model
|
||||
const userVariant = config.categories?.[name]?.variant
|
||||
return {
|
||||
return attachCapabilityDiagnostics({
|
||||
name,
|
||||
requirement,
|
||||
userOverride,
|
||||
userVariant,
|
||||
effectiveModel: getEffectiveModel(requirement, userOverride),
|
||||
effectiveResolution: buildEffectiveResolution(requirement, userOverride),
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
return { agents, categories }
|
||||
}
|
||||
|
||||
export function collectCapabilityResolutionIssues(info: ModelResolutionInfo): DoctorIssue[] {
|
||||
const issues: DoctorIssue[] = []
|
||||
const allEntries = [...info.agents, ...info.categories]
|
||||
const fallbackEntries = allEntries.filter((entry) => {
|
||||
const mode = entry.capabilityDiagnostics?.resolutionMode
|
||||
return mode === "alias-backed" || mode === "heuristic-backed" || mode === "unknown"
|
||||
})
|
||||
|
||||
if (fallbackEntries.length === 0) {
|
||||
return issues
|
||||
}
|
||||
|
||||
const summary = fallbackEntries
|
||||
.map((entry) => `${entry.name}=${entry.effectiveModel} (${entry.capabilityDiagnostics?.resolutionMode ?? "unknown"})`)
|
||||
.join(", ")
|
||||
|
||||
issues.push({
|
||||
title: "Configured models rely on compatibility fallback",
|
||||
description: summary,
|
||||
severity: "warning",
|
||||
affects: fallbackEntries.map((entry) => entry.name),
|
||||
})
|
||||
|
||||
return issues
|
||||
}
|
||||
|
||||
export async function checkModels(): Promise<CheckResult> {
|
||||
const config = loadOmoConfig() ?? {}
|
||||
const info = getModelResolutionInfoWithOverrides(config)
|
||||
@@ -75,6 +132,8 @@ export async function checkModels(): Promise<CheckResult> {
|
||||
})
|
||||
}
|
||||
|
||||
issues.push(...collectCapabilityResolutionIssues(info))
|
||||
|
||||
const overrideCount =
|
||||
info.agents.filter((agent) => Boolean(agent.userOverride)).length +
|
||||
info.categories.filter((category) => Boolean(category.userOverride)).length
|
||||
|
||||
@@ -53,6 +53,14 @@ describe("install CLI - binary check behavior", () => {
|
||||
isOpenCodeInstalledSpy = spyOn(configManager, "isOpenCodeInstalled").mockResolvedValue(false)
|
||||
getOpenCodeVersionSpy = spyOn(configManager, "getOpenCodeVersion").mockResolvedValue(null)
|
||||
|
||||
// given mock npm fetch
|
||||
globalThis.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ latest: "3.0.0" }),
|
||||
} as Response)
|
||||
) as unknown as typeof fetch
|
||||
|
||||
const args: InstallArgs = {
|
||||
tui: false,
|
||||
claude: "yes",
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { describe, expect, it, mock } from "bun:test"
|
||||
|
||||
import { refreshModelCapabilities } from "./refresh-model-capabilities"
|
||||
|
||||
describe("refreshModelCapabilities", () => {
|
||||
it("uses config source_url when CLI override is absent", async () => {
|
||||
const loadConfig = mock(() => ({
|
||||
model_capabilities: {
|
||||
source_url: "https://mirror.example/api.json",
|
||||
},
|
||||
}))
|
||||
const refreshCache = mock(async () => ({
|
||||
generatedAt: "2026-03-25T00:00:00.000Z",
|
||||
sourceUrl: "https://mirror.example/api.json",
|
||||
models: {
|
||||
"gpt-5.4": { id: "gpt-5.4" },
|
||||
},
|
||||
}))
|
||||
let stdout = ""
|
||||
|
||||
const exitCode = await refreshModelCapabilities(
|
||||
{ directory: "/repo", json: false },
|
||||
{
|
||||
loadConfig,
|
||||
refreshCache,
|
||||
stdout: {
|
||||
write: (chunk: string) => {
|
||||
stdout += chunk
|
||||
return true
|
||||
},
|
||||
} as never,
|
||||
stderr: {
|
||||
write: () => true,
|
||||
} as never,
|
||||
},
|
||||
)
|
||||
|
||||
expect(exitCode).toBe(0)
|
||||
expect(loadConfig).toHaveBeenCalledWith("/repo", null)
|
||||
expect(refreshCache).toHaveBeenCalledWith({
|
||||
sourceUrl: "https://mirror.example/api.json",
|
||||
})
|
||||
expect(stdout).toContain("Refreshed model capabilities cache (1 models)")
|
||||
})
|
||||
|
||||
it("CLI sourceUrl overrides config and supports json output", async () => {
|
||||
const refreshCache = mock(async () => ({
|
||||
generatedAt: "2026-03-25T00:00:00.000Z",
|
||||
sourceUrl: "https://override.example/api.json",
|
||||
models: {
|
||||
"gpt-5.4": { id: "gpt-5.4" },
|
||||
"claude-opus-4-6": { id: "claude-opus-4-6" },
|
||||
},
|
||||
}))
|
||||
let stdout = ""
|
||||
|
||||
const exitCode = await refreshModelCapabilities(
|
||||
{
|
||||
directory: "/repo",
|
||||
json: true,
|
||||
sourceUrl: "https://override.example/api.json",
|
||||
},
|
||||
{
|
||||
loadConfig: () => ({}),
|
||||
refreshCache,
|
||||
stdout: {
|
||||
write: (chunk: string) => {
|
||||
stdout += chunk
|
||||
return true
|
||||
},
|
||||
} as never,
|
||||
stderr: {
|
||||
write: () => true,
|
||||
} as never,
|
||||
},
|
||||
)
|
||||
|
||||
expect(exitCode).toBe(0)
|
||||
expect(refreshCache).toHaveBeenCalledWith({
|
||||
sourceUrl: "https://override.example/api.json",
|
||||
})
|
||||
expect(JSON.parse(stdout)).toEqual({
|
||||
sourceUrl: "https://override.example/api.json",
|
||||
generatedAt: "2026-03-25T00:00:00.000Z",
|
||||
modelCount: 2,
|
||||
})
|
||||
})
|
||||
|
||||
it("returns exit code 1 when refresh fails", async () => {
|
||||
let stderr = ""
|
||||
|
||||
const exitCode = await refreshModelCapabilities(
|
||||
{ directory: "/repo" },
|
||||
{
|
||||
loadConfig: () => ({}),
|
||||
refreshCache: async () => {
|
||||
throw new Error("boom")
|
||||
},
|
||||
stdout: {
|
||||
write: () => true,
|
||||
} as never,
|
||||
stderr: {
|
||||
write: (chunk: string) => {
|
||||
stderr += chunk
|
||||
return true
|
||||
},
|
||||
} as never,
|
||||
},
|
||||
)
|
||||
|
||||
expect(exitCode).toBe(1)
|
||||
expect(stderr).toContain("Failed to refresh model capabilities cache")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import { loadPluginConfig } from "../plugin-config"
|
||||
import { refreshModelCapabilitiesCache } from "../shared/model-capabilities-cache"
|
||||
|
||||
export type RefreshModelCapabilitiesOptions = {
|
||||
directory?: string
|
||||
json?: boolean
|
||||
sourceUrl?: string
|
||||
}
|
||||
|
||||
type RefreshModelCapabilitiesDeps = {
|
||||
loadConfig?: typeof loadPluginConfig
|
||||
refreshCache?: typeof refreshModelCapabilitiesCache
|
||||
stdout?: Pick<typeof process.stdout, "write">
|
||||
stderr?: Pick<typeof process.stderr, "write">
|
||||
}
|
||||
|
||||
export async function refreshModelCapabilities(
|
||||
options: RefreshModelCapabilitiesOptions,
|
||||
deps: RefreshModelCapabilitiesDeps = {},
|
||||
): Promise<number> {
|
||||
const directory = options.directory ?? process.cwd()
|
||||
const loadConfig = deps.loadConfig ?? loadPluginConfig
|
||||
const refreshCache = deps.refreshCache ?? refreshModelCapabilitiesCache
|
||||
const stdout = deps.stdout ?? process.stdout
|
||||
const stderr = deps.stderr ?? process.stderr
|
||||
|
||||
try {
|
||||
const config = loadConfig(directory, null)
|
||||
const sourceUrl = options.sourceUrl ?? config.model_capabilities?.source_url
|
||||
const snapshot = await refreshCache({ sourceUrl })
|
||||
|
||||
const summary = {
|
||||
sourceUrl: snapshot.sourceUrl,
|
||||
generatedAt: snapshot.generatedAt,
|
||||
modelCount: Object.keys(snapshot.models).length,
|
||||
}
|
||||
|
||||
if (options.json) {
|
||||
stdout.write(`${JSON.stringify(summary, null, 2)}\n`)
|
||||
} else {
|
||||
stdout.write(
|
||||
`Refreshed model capabilities cache (${summary.modelCount} models) from ${summary.sourceUrl}\n`,
|
||||
)
|
||||
}
|
||||
|
||||
return 0
|
||||
} catch (error) {
|
||||
stderr.write(`Failed to refresh model capabilities cache: ${String(error)}\n`)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ export async function promptInstallConfig(detected: DetectedConfig): Promise<Ins
|
||||
message: "Will you integrate Google Gemini?",
|
||||
options: [
|
||||
{ value: "no", label: "No", hint: "Frontend/docs agents will use fallback" },
|
||||
{ value: "yes", label: "Yes", hint: "Beautiful UI generation with Gemini 3 Pro" },
|
||||
{ value: "yes", label: "Yes", hint: "Beautiful UI generation with Gemini 3.1 Pro" },
|
||||
],
|
||||
initialValue: initial.gemini,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user