()
| 2994 | // ─── Main ──────────────────────────────────────────────────────────────────── |
| 2995 | |
| 2996 | async function main() { |
| 2997 | config = loadConfig(); |
| 2998 | |
| 2999 | // Initialize plugins early so they can handle setup (e.g. /provider wizard) |
| 3000 | pluginLoader = new PluginLoader(process.cwd()).loadAll(); |
| 3001 | await pluginLoader.runInit({ config, cwd: process.cwd() }); |
| 3002 | skillManager = new SkillManager(process.cwd()); |
| 3003 | |
| 3004 | // Check model is configured |
| 3005 | if (!config.model.name) { |
| 3006 | // Allow /provider commands even without a model configured |
| 3007 | const providerArg = positional.find(a => a === '/provider' || a === '/provider/status' || a === 'provider'); |
| 3008 | if (providerArg) { |
| 3009 | const cmd = providerArg.startsWith('/') ? providerArg : '/provider'; |
| 3010 | const rest = positional.filter(a => a !== providerArg).join(' '); |
| 3011 | const createCommandHandler = require('./commands'); |
| 3012 | const handleCmd = createCommandHandler(config, [], 0, null, null, 0, null, null, null); |
| 3013 | const mockRl = { prompt: () => {}, close: () => {}, on: () => {}, question: (q, cb) => cb('') }; |
| 3014 | await handleCmd(rest ? `${cmd} ${rest}` : cmd, mockRl); |
| 3015 | return; |
| 3016 | } |
| 3017 | console.log('\n ⚡ SmallCode — no model configured.\n'); |
| 3018 | console.log(' Type /provider to configure a model, or /provider status to check.\n'); |
| 3019 | startMinimalTUI(); |
| 3020 | return; |
| 3021 | } |
| 3022 | |
| 3023 | // Initialize escalation engine |
| 3024 | escalationEngine = new EscalationEngine(config.escalation || {}); |
| 3025 | |
| 3026 | // Detect model profile (drives routing mode, tool format, context budget) |
| 3027 | const modelProfile = getProfile(config.model.name, config.context.detected_window); |
| 3028 | if (modelProfile.matched_key) { |
| 3029 | // Apply profile-detected context window if not already set |
| 3030 | if (!config.context.detected_window && modelProfile.context_length) { |
| 3031 | config.context.detected_window = modelProfile.context_length; |
| 3032 | } |
| 3033 | } |
| 3034 | |
| 3035 | // Initialize plugins and skills |
| 3036 | pluginLoader = new PluginLoader(process.cwd()).loadAll(); |
| 3037 | await pluginLoader.runInit({ config, cwd: process.cwd() }); |
| 3038 | |
| 3039 | // Run plugin shutdown handlers on exit |
| 3040 | process.on('beforeExit', () => { |
| 3041 | if (pluginLoader) pluginLoader.runShutdown({ config, cwd: process.cwd() }).catch(() => {}); |
| 3042 | }); |
| 3043 | |
| 3044 | skillManager = new SkillManager(process.cwd()); |
| 3045 | |
| 3046 | // Initialize MCP client (connect to external MCP servers) |
| 3047 | let mcpClient = null; |
| 3048 | const mcpClientInstance = new MCPClient(process.cwd()); |
| 3049 | if (mcpClientInstance.loadConfig() > 0) { |
| 3050 | mcpClient = mcpClientInstance; |
| 3051 | // Connect asynchronously — don't block boot |
| 3052 | mcpClient.connectAll().then(toolCount => { |
| 3053 | if (toolCount > 0 && _fullscreenRef) { |
no test coverage detected