import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname } from "node:path"; import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent"; export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh"; export function parseModelSpec(spec: string, modelRegistry: ModelRegistry) { const trimmed = spec.trim(); const separator = trimmed.includes(":") ? ":" : trimmed.includes("/") ? "/" : null; if (!separator) { return undefined; } const [provider, ...rest] = trimmed.split(separator); const id = rest.join(separator); if (!provider || !id) { return undefined; } return modelRegistry.find(provider, id); } export function normalizeThinkingLevel(value: string | undefined): ThinkingLevel | undefined { if (!value) { return undefined; } const normalized = value.toLowerCase(); if ( normalized === "off" || normalized === "minimal" || normalized === "low" || normalized === "medium" || normalized === "high" || normalized === "xhigh" ) { return normalized; } return undefined; } function choosePreferredModel( availableModels: Array<{ provider: string; id: string }>, ): { provider: string; id: string } | undefined { const preferences = [ { provider: "anthropic", id: "claude-opus-4-6" }, { provider: "anthropic", id: "claude-opus-4-5" }, { provider: "anthropic", id: "claude-sonnet-4-5" }, { provider: "openai", id: "gpt-5.4" }, { provider: "openai", id: "gpt-5" }, ]; for (const preferred of preferences) { const match = availableModels.find( (model) => model.provider === preferred.provider && model.id === preferred.id, ); if (match) { return match; } } return availableModels[0]; } export function readJson(path: string): Record { if (!existsSync(path)) { return {}; } try { return JSON.parse(readFileSync(path, "utf8")); } catch { return {}; } } export function normalizeFeynmanSettings( settingsPath: string, bundledSettingsPath: string, defaultThinkingLevel: ThinkingLevel, authPath: string, ): void { let settings: Record = {}; if (existsSync(settingsPath)) { try { settings = JSON.parse(readFileSync(settingsPath, "utf8")); } catch { settings = {}; } } else if (existsSync(bundledSettingsPath)) { try { settings = JSON.parse(readFileSync(bundledSettingsPath, "utf8")); } catch { settings = {}; } } if (!settings.defaultThinkingLevel) { settings.defaultThinkingLevel = defaultThinkingLevel; } if (settings.editorPaddingX === undefined) { settings.editorPaddingX = 1; } settings.theme = "feynman"; settings.quietStartup = true; settings.collapseChangelog = true; const authStorage = AuthStorage.create(authPath); const modelRegistry = new ModelRegistry(authStorage); const availableModels = modelRegistry.getAvailable().map((model) => ({ provider: model.provider, id: model.id, })); if ((!settings.defaultProvider || !settings.defaultModel) && availableModels.length > 0) { const preferredModel = choosePreferredModel(availableModels); if (preferredModel) { settings.defaultProvider = preferredModel.provider; settings.defaultModel = preferredModel.id; } } mkdirSync(dirname(settingsPath), { recursive: true }); writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8"); }