(dryRun = false)
| 226 | // ─── Main Setup Flow ────────────────────────────────────── |
| 227 | |
| 228 | export async function runSetup(dryRun = false): Promise<number> { |
| 229 | intro("Magic Context — Setup"); |
| 230 | if (dryRun) { |
| 231 | log.warn("Dry run — no files will be written and no config will be changed."); |
| 232 | log.message( |
| 233 | "[dry-run] would migrate legacy Magic Context config before setup reads or writes the shared CortexKit config.", |
| 234 | ); |
| 235 | } else { |
| 236 | const migrationWarnings = migrateConfigLocationsForCli(process.cwd(), log); |
| 237 | if (hasUserConfigLocationMigrationRefusal(migrationWarnings)) { |
| 238 | outro( |
| 239 | "Setup stopped — resolve the legacy Magic Context user config migration conflict, then rerun setup.", |
| 240 | ); |
| 241 | return 1; |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | // ─── Step 1: Check OpenCode ───────────────────────── |
| 246 | const s = spinner(); |
| 247 | s.start("Checking OpenCode installation"); |
| 248 | |
| 249 | const detection = detectOpenCode(); |
| 250 | if (detection.kind === "none") { |
| 251 | s.stop("OpenCode not found"); |
| 252 | const shouldContinue = await confirm( |
| 253 | "OpenCode not found on PATH. Continue setup anyway?", |
| 254 | false, |
| 255 | ); |
| 256 | if (!shouldContinue) { |
| 257 | log.info("Install OpenCode: https://opencode.ai"); |
| 258 | outro("Setup cancelled"); |
| 259 | return 1; |
| 260 | } |
| 261 | } else if (detection.kind === "desktop") { |
| 262 | // OpenCode Desktop ships no invocable `opencode` CLI on any OS (its |
| 263 | // server runs as a JS sidecar inside Electron), so `opencode models` |
| 264 | // and `opencode --version` are unavailable. Recognize the install and |
| 265 | // fall through to manual model entry instead of claiming OpenCode is |
| 266 | // absent. |
| 267 | s.stop("OpenCode Desktop detected (CLI not installed)"); |
| 268 | log.info( |
| 269 | "Model auto-discovery needs the OpenCode CLI; you will enter models manually. Install the CLI to auto-populate: https://opencode.ai", |
| 270 | ); |
| 271 | } else { |
| 272 | const version = getOpenCodeVersion(detection.binary); |
| 273 | s.stop(`OpenCode ${version ?? ""} detected`); |
| 274 | } |
| 275 | |
| 276 | // ─── Step 2: Get available models ─────────────────── |
| 277 | s.start("Fetching available models"); |
| 278 | |
| 279 | // Only the CLI can enumerate the authed/resolved model list; Desktop-only |
| 280 | // installs have no on-disk equivalent, so models stay empty and the model |
| 281 | // prompts fall back to free-text entry. Use the resolved binary path so a |
| 282 | // stock CLI that is not on PATH still enumerates. |
| 283 | const allModels = detection.kind === "cli" ? getAvailableModels(detection.binary) : []; |
| 284 | if (allModels.length > 0) { |
| 285 | s.stop(`Found ${allModels.length} models`); |
nothing calls this directly
no test coverage detected