#!/bin/sh -
':'; /*-
test1=$(bun --version 2>&1) && exec bun "$0" "$@"
test2=$(node --version 2>&1) && exec node "$0" "$@"
exec printf '%s\n' "$test1" "$test2" 1>&2
*/
// @bun
var __defProp = Object.defineProperty;
var __returnValue = (v) => v;
function __exportSetter(name, newValue) {
  this[name] = __returnValue.bind(null, newValue);
}
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, {
      get: all[name],
      enumerable: true,
      configurable: true,
      set: __exportSetter.bind(all, name)
    });
};
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
var __require = import.meta.require;

// ../core/custom-verified-providers.json
var custom_verified_providers_default;
var init_custom_verified_providers = __esm(() => {
  custom_verified_providers_default = {
    "custom-verified-providers": {
      "zai-code-anth": {
        id: "zai-code-anth",
        name: "zai-code-anth",
        baseUrl: "https://api.z.ai/api/anthropic/v1",
        type: "anthropic",
        models: {
          "glm-4-5": {
            id: "glm-4.5",
            name: "GLM-4.5-anth"
          },
          "glm-4-5-air": {
            id: "glm-4.5-air",
            name: "GLM-4.5-air-anth"
          },
          "glm-4-6": {
            id: "glm-4.6",
            name: "GLM-4.6-anth"
          }
        }
      },
      "zai-china-anth-base": {
        id: "zai-china-anth-base",
        name: "zai-china-anth-base",
        baseUrl: "https://open.bigmodel.cn/api/anthropic/v1",
        type: "anthropic",
        models: {
          "glm-4-5": {
            id: "glm-4.5",
            name: "GLM-4.5-china-anth"
          },
          "glm-4-5-air": {
            id: "glm-4.5-air",
            name: "GLM-4.5-air-china-anth"
          },
          "glm-4-6": {
            id: "glm-4.6",
            name: "GLM-4.6-china-anth"
          }
        }
      },
      "extra-models-dev": {
        chutes: {
          "deepseek-ai/DeepSeek-V3.1-Terminus": {
            id: "deepseek-ai/DeepSeek-V3.1-Terminus",
            name: "DeepSeek V3.1 Terminus"
          },
          "meituan-longcat/LongCat-Flash-Thinking-FP8": {
            id: "meituan-longcat/LongCat-Flash-Thinking-FP8",
            name: "LongCat Flash Thinking FP8"
          }
        }
      },
      "nanogpt-plan": {
        id: "nanogpt-plan",
        name: "nanogpt-plan",
        baseUrl: "https://nano-gpt.com/api/v1",
        type: "openai-compatible",
        models: {
          "deepseek-ai/DeepSeek-V3.1-Terminus": {
            id: "deepseek-ai/DeepSeek-V3.1-Terminus",
            name: "DeepSeek V3.1 Terminus"
          },
          "deepseek-ai/DeepSeek-V3.1": {
            id: "deepseek-ai/DeepSeek-V3.1",
            name: "DeepSeek V3.1"
          },
          "deepseek-ai/DeepSeek-V3.1-Terminus:thinking": {
            id: "deepseek-ai/DeepSeek-V3.1-Terminus:thinking",
            name: "DeepSeek V3.1 Terminus Thinking"
          },
          "zai-org/GLM-4.5-FP8": {
            id: "zai-org/GLM-4.5-FP8",
            name: "GLM 4.5 FP8"
          },
          "zai-org/GLM-4.5-FP8:thinking": {
            id: "zai-org/GLM-4.5-FP8:thinking",
            name: "GLM 4.5 FP8 Thinking"
          },
          "zai-org/GLM-4.5-Air": {
            id: "zai-org/GLM-4.5-Air",
            name: "GLM 4.5 Air"
          },
          "moonshotai/Kimi-K2-Instruct": {
            id: "moonshotai/Kimi-K2-Instruct",
            name: "Kimi K2 Instruct"
          },
          "moonshotai/Kimi-K2-Instruct-0905": {
            id: "moonshotai/Kimi-K2-Instruct-0905",
            name: "Kimi K2 Instruct 0905"
          },
          "moonshotai/kimi-k2-thinking": {
            id: "moonshotai/kimi-k2-thinking",
            name: "Kimi K2 Thinking"
          },
          "deepseek-ai/deepseek-v3.2-exp": {
            id: "deepseek-ai/deepseek-v3.2-exp",
            name: "DeepSeek V3.2 Exp"
          },
          "z-ai/glm-4.6": {
            id: "z-ai/glm-4.6",
            name: "GLM 4.6"
          },
          "z-ai/glm-4.6:thinking": {
            id: "z-ai/glm-4.6:thinking",
            name: "GLM 4.6 Thinking"
          },
          "qwen3-vl-235b-a22b-instruct": {
            id: "qwen3-vl-235b-a22b-instruct",
            name: "Qwen3 VL 235B A22B Instruct"
          },
          "MiniMax-M2": {
            id: "MiniMax-M2",
            name: "MiniMax-M2"
          }
        }
      }
    }
  };
});

// ../core/src/models-dev.ts
var exports_models_dev = {};
__export(exports_models_dev, {
  transformModelsDevData: () => transformModelsDevData,
  searchProviders: () => searchProviders,
  refreshData: () => refreshData,
  loadCustomVerifiedProviders: () => loadCustomVerifiedProviders,
  getModelsForProvider: () => getModelsForProvider,
  getAllProviders: () => getAllProviders,
  fetchFromAPI: () => fetchFromAPI,
  clearCache: () => clearCache
});
import fs from "fs";
import path from "path";
import { homedir } from "os";
function ensureCacheDir() {
  try {
    if (!fs.existsSync(CACHE_DIR)) {
      fs.mkdirSync(CACHE_DIR, { recursive: true });
    }
  } catch (error) {
    console.warn("Warning: Could not create cache directory:", error.message);
  }
}
function isCacheExpired(cacheData) {
  if (!cacheData.timestamp)
    return true;
  return Date.now() - cacheData.timestamp > 60 * 60 * 1000;
}
function loadCache() {
  try {
    if (fs.existsSync(CACHE_FILE)) {
      const data = fs.readFileSync(CACHE_FILE, "utf8");
      const parsed = JSON.parse(data);
      if (isCacheExpired(parsed))
        return null;
      return parsed;
    }
  } catch (error) {
    console.warn("Warning: Could not load cache:", error.message);
  }
  return null;
}
function saveCache(data) {
  try {
    ensureCacheDir();
    const cacheData = { ...data, timestamp: Date.now() };
    fs.writeFileSync(CACHE_FILE, JSON.stringify(cacheData, null, 2));
  } catch (error) {
    console.warn("Warning: Could not save cache:", error.message);
  }
}
async function fetchFromAPI() {
  try {
    const response = await fetch("https://models.dev/api.json");
    if (!response.ok)
      throw new Error(`HTTP error! status: ${response.status}`);
    return await response.json();
  } catch (error) {
    console.warn("Warning: Could not fetch from models.dev API:", error.message);
    return null;
  }
}
function getCustomProvidersJson() {
  let data = custom_verified_providers_default;
  const customProvidersPath = path.join(process.cwd(), "custom-verified-providers.json");
  if (fs.existsSync(customProvidersPath)) {
    try {
      data = JSON.parse(fs.readFileSync(customProvidersPath, "utf8"));
    } catch (fileError) {
      console.warn("Warning: Could not load custom providers from file, using embedded data:", fileError.message);
    }
  }
  return data;
}
function loadCustomVerifiedProviders() {
  try {
    const customProviders = getCustomProvidersJson();
    const providers = [];
    if (customProviders["custom-verified-providers"]) {
      for (const [, providerData] of Object.entries(customProviders["custom-verified-providers"])) {
        const entry = providerData;
        if (entry.id === "extra-models-dev")
          continue;
        if (entry.id && entry.name && entry.models) {
          providers.push({
            id: entry.id,
            name: entry.name,
            baseUrl: entry.baseUrl || "",
            type: entry.type || "openai-compatible",
            models: Object.values(entry.models).map((m) => ({ id: m.id, name: m.name }))
          });
        }
      }
    }
    return providers;
  } catch (error) {
    console.warn("Warning: Could not load custom verified providers:", error.message);
    return [];
  }
}
function loadExtraModels() {
  try {
    const customProviders = getCustomProvidersJson();
    const extraModels = {};
    const section = customProviders["custom-verified-providers"]?.["extra-models-dev"];
    if (section) {
      for (const [providerId, models] of Object.entries(section)) {
        extraModels[providerId] = Object.values(models).map((m) => ({ id: m.id, name: m.name }));
      }
    }
    return extraModels;
  } catch (error) {
    console.warn("Warning: Could not load extra models:", error.message);
    return {};
  }
}
function transformModelsDevData(apiData) {
  const providers = [];
  const customProviders = loadCustomVerifiedProviders();
  providers.push(...customProviders);
  const extraModels = loadExtraModels();
  if (apiData) {
    for (const [, providerData] of Object.entries(apiData)) {
      if (providerData.id && providerData.name && providerData.models) {
        const models = Object.values(providerData.models).map((m) => ({ id: m.id, name: m.name }));
        if (extraModels[providerData.id]) {
          models.push(...extraModels[providerData.id]);
        }
        providers.push({
          id: providerData.id,
          name: providerData.name,
          baseUrl: providerData.api || providerData.baseUrl || "",
          type: providerData.npm ? providerData.npm.includes("anthropic") ? "anthropic" : "openai-compatible" : "openai-compatible",
          models
        });
      }
    }
  }
  return providers.length === 0 ? FALLBACK_PROVIDERS : providers;
}
async function getAllProviders() {
  const cachedData = loadCache();
  if (cachedData?.providers) {
    const customVerifiedProviders = loadCustomVerifiedProviders();
    const existingIds = new Set(cachedData.providers.map((p) => p.id));
    const missing = customVerifiedProviders.filter((p) => !existingIds.has(p.id));
    if (missing.length > 0)
      cachedData.providers.push(...missing);
    return cachedData.providers;
  }
  const apiData = await fetchFromAPI();
  if (apiData) {
    const transformed = transformModelsDevData(apiData);
    saveCache({ providers: transformed });
    return transformed;
  }
  const fallback = [...FALLBACK_PROVIDERS];
  const extraModels = loadExtraModels();
  const customVerified = loadCustomVerifiedProviders();
  fallback.forEach((p) => {
    if (extraModels[p.id])
      p.models.push(...extraModels[p.id]);
  });
  fallback.push(...customVerified);
  return fallback;
}
async function searchProviders(query) {
  const providers = await getAllProviders();
  const q = query.toLowerCase();
  return providers.filter((p) => p.name.toLowerCase().includes(q) || p.id.toLowerCase().includes(q));
}
async function getModelsForProvider(providerId) {
  const providers = await getAllProviders();
  return providers.find((p) => p.id === providerId)?.models ?? [];
}
async function refreshData() {
  const apiData = await fetchFromAPI();
  if (apiData) {
    const transformed = transformModelsDevData(apiData);
    saveCache({ providers: transformed });
    return transformed;
  }
  const cached = loadCache();
  if (cached?.providers)
    return cached.providers;
  const fallback = [...FALLBACK_PROVIDERS];
  const extraModels = loadExtraModels();
  fallback.forEach((p) => {
    if (extraModels[p.id])
      p.models.push(...extraModels[p.id]);
  });
  return fallback;
}
function clearCache() {
  try {
    if (fs.existsSync(CACHE_FILE))
      fs.unlinkSync(CACHE_FILE);
  } catch (error) {
    console.warn("Warning: Could not clear cache:", error.message);
  }
}
var CACHE_DIR, CACHE_FILE, FALLBACK_PROVIDERS;
var init_models_dev = __esm(() => {
  init_custom_verified_providers();
  CACHE_DIR = path.join(homedir(), ".cache", "ai-speedometer");
  CACHE_FILE = path.join(CACHE_DIR, "models.json");
  FALLBACK_PROVIDERS = [
    {
      id: "openai",
      name: "OpenAI",
      baseUrl: "https://api.openai.com/v1",
      type: "openai-compatible",
      models: [
        { id: "gpt-4o", name: "GPT-4o" },
        { id: "gpt-4o-mini", name: "GPT-4o Mini" },
        { id: "gpt-4-turbo", name: "GPT-4 Turbo" },
        { id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo" }
      ]
    },
    {
      id: "anthropic",
      name: "Anthropic",
      baseUrl: "https://api.anthropic.com",
      type: "anthropic",
      models: [
        { id: "claude-3-5-sonnet-20241022", name: "Claude 3.5 Sonnet" },
        { id: "claude-3-5-haiku-20241022", name: "Claude 3.5 Haiku" },
        { id: "claude-3-opus-20240229", name: "Claude 3 Opus" }
      ]
    },
    {
      id: "openrouter",
      name: "OpenRouter",
      baseUrl: "https://openrouter.ai/api/v1",
      type: "openai-compatible",
      models: [
        { id: "anthropic/claude-3.5-sonnet", name: "Claude 3.5 Sonnet" },
        { id: "openai/gpt-4o", name: "GPT-4o" },
        { id: "openai/gpt-4o-mini", name: "GPT-4o Mini" }
      ]
    }
  ];
});

// ../core/src/ai-config.ts
var exports_ai_config = {};
__export(exports_ai_config, {
  writeThemeToConfig: () => writeThemeToConfig,
  writeAIConfig: () => writeAIConfig,
  removeVerifiedProvider: () => removeVerifiedProvider,
  removeCustomProvider: () => removeCustomProvider,
  readThemeFromConfig: () => readThemeFromConfig,
  readAIConfig: () => readAIConfig,
  getVerifiedProvidersFromConfig: () => getVerifiedProvidersFromConfig,
  getRecentModels: () => getRecentModels,
  getCustomProvidersFromConfig: () => getCustomProvidersFromConfig,
  getAIConfigDebugPaths: () => getAIConfigDebugPaths,
  clearRecentModels: () => clearRecentModels,
  cleanupRecentModelsFromConfig: () => cleanupRecentModelsFromConfig,
  addVerifiedProvider: () => addVerifiedProvider,
  addToRecentModels: () => addToRecentModels,
  addModelToCustomProvider: () => addModelToCustomProvider,
  addCustomProvider: () => addCustomProvider
});
import fs2 from "fs";
import path2 from "path";
import { homedir as homedir2 } from "os";
var getAIConfigPaths = () => {
  const aiConfigDir = process.env.XDG_CONFIG_HOME || path2.join(homedir2(), ".config");
  const aiSpeedometerConfigDir = path2.join(aiConfigDir, "ai-speedometer");
  return {
    configDir: aiSpeedometerConfigDir,
    configJson: path2.join(aiSpeedometerConfigDir, "ai-benchmark-config.json"),
    recentModelsCache: path2.join(aiSpeedometerConfigDir, "recent-models.json")
  };
}, ensureAIConfigDirectory = () => {
  const { configDir } = getAIConfigPaths();
  if (!fs2.existsSync(configDir)) {
    fs2.mkdirSync(configDir, { recursive: true });
  }
}, readAIConfig = async () => {
  const { configJson } = getAIConfigPaths();
  try {
    if (!fs2.existsSync(configJson)) {
      return { verifiedProviders: {}, customProviders: [] };
    }
    const data = fs2.readFileSync(configJson, "utf8");
    return JSON.parse(data);
  } catch (error) {
    console.warn("Warning: Could not read ai-benchmark-config.json:", error.message);
    return { verifiedProviders: {}, customProviders: [] };
  }
}, readThemeFromConfig = async () => {
  const config = await readAIConfig();
  return config.theme ?? "tokyonight";
}, writeThemeToConfig = async (theme) => {
  const config = await readAIConfig();
  await writeAIConfig({ ...config, theme });
}, writeAIConfig = async (config) => {
  const { configJson } = getAIConfigPaths();
  try {
    ensureAIConfigDirectory();
    fs2.writeFileSync(configJson, JSON.stringify(config, null, 2));
    return true;
  } catch (error) {
    console.error("Error writing ai-benchmark-config.json:", error.message);
    return false;
  }
}, addVerifiedProvider = async (providerId, apiKey) => {
  const config = await readAIConfig();
  config.verifiedProviders = config.verifiedProviders || {};
  config.verifiedProviders[providerId] = apiKey;
  return writeAIConfig(config);
}, addCustomProvider = async (providerData) => {
  const config = await readAIConfig();
  config.customProviders = config.customProviders || [];
  const existingIndex = config.customProviders.findIndex((p) => p.id === providerData.id);
  if (existingIndex >= 0) {
    config.customProviders[existingIndex] = providerData;
  } else {
    config.customProviders.push(providerData);
  }
  return writeAIConfig(config);
}, addModelToCustomProvider = async (providerId, modelData) => {
  const config = await readAIConfig();
  if (!config.customProviders) {
    console.error("No custom providers found");
    return false;
  }
  const provider = config.customProviders.find((p) => p.id === providerId);
  if (!provider) {
    console.error(`Custom provider ${providerId} not found`);
    return false;
  }
  provider.models = provider.models || [];
  const existingIndex = provider.models.findIndex((m) => m.id === modelData.id);
  if (existingIndex >= 0) {
    provider.models[existingIndex] = modelData;
  } else {
    provider.models.push(modelData);
  }
  return writeAIConfig(config);
}, getCustomProvidersFromConfig = async () => {
  const config = await readAIConfig();
  return config.customProviders || [];
}, getVerifiedProvidersFromConfig = async () => {
  const config = await readAIConfig();
  return config.verifiedProviders || {};
}, removeCustomProvider = async (providerId) => {
  const config = await readAIConfig();
  if (config.customProviders) {
    config.customProviders = config.customProviders.filter((p) => p.id !== providerId);
    return writeAIConfig(config);
  }
  return true;
}, removeVerifiedProvider = async (providerId) => {
  const config = await readAIConfig();
  if (config.verifiedProviders && config.verifiedProviders[providerId]) {
    delete config.verifiedProviders[providerId];
    return writeAIConfig(config);
  }
  return true;
}, addToRecentModels = async (models) => {
  const { recentModelsCache } = getAIConfigPaths();
  ensureAIConfigDirectory();
  let recentModels = [];
  try {
    if (fs2.existsSync(recentModelsCache)) {
      const data = fs2.readFileSync(recentModelsCache, "utf8");
      recentModels = JSON.parse(data);
    }
  } catch {
    recentModels = [];
  }
  models.forEach((model) => {
    recentModels = recentModels.filter((r) => r.modelId !== model.modelId);
    recentModels.unshift({
      modelId: model.modelId,
      modelName: model.modelName,
      providerName: model.providerName,
      timestamp: Date.now()
    });
  });
  recentModels = recentModels.slice(0, 5);
  fs2.writeFileSync(recentModelsCache, JSON.stringify(recentModels, null, 2));
  return recentModels;
}, getRecentModels = async () => {
  const { recentModelsCache } = getAIConfigPaths();
  try {
    if (fs2.existsSync(recentModelsCache)) {
      const data = fs2.readFileSync(recentModelsCache, "utf8");
      return JSON.parse(data);
    }
  } catch {}
  return [];
}, clearRecentModels = async () => {
  const { recentModelsCache } = getAIConfigPaths();
  try {
    if (fs2.existsSync(recentModelsCache)) {
      fs2.unlinkSync(recentModelsCache);
    }
  } catch {}
  return [];
}, cleanupRecentModelsFromConfig = async () => {
  const config = await readAIConfig();
  if (config.recentModels && config.recentModels.length > 0) {
    const { recentModelsCache } = getAIConfigPaths();
    ensureAIConfigDirectory();
    try {
      fs2.writeFileSync(recentModelsCache, JSON.stringify(config.recentModels, null, 2));
    } catch (error) {
      console.warn("Warning: Could not migrate recent models to cache:", error.message);
    }
    delete config.recentModels;
    await writeAIConfig(config);
  }
  return config;
}, getAIConfigDebugPaths = () => {
  const paths = getAIConfigPaths();
  return { ...paths, configExists: fs2.existsSync(paths.configJson) };
};
var init_ai_config = () => {};

// ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
function createScanner(text, ignoreTrivia = false) {
  const len = text.length;
  let pos = 0, value = "", tokenOffset = 0, token = 16, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0;
  function scanHexDigits(count, exact) {
    let digits = 0;
    let value2 = 0;
    while (digits < count || !exact) {
      let ch = text.charCodeAt(pos);
      if (ch >= 48 && ch <= 57) {
        value2 = value2 * 16 + ch - 48;
      } else if (ch >= 65 && ch <= 70) {
        value2 = value2 * 16 + ch - 65 + 10;
      } else if (ch >= 97 && ch <= 102) {
        value2 = value2 * 16 + ch - 97 + 10;
      } else {
        break;
      }
      pos++;
      digits++;
    }
    if (digits < count) {
      value2 = -1;
    }
    return value2;
  }
  function setPosition(newPosition) {
    pos = newPosition;
    value = "";
    tokenOffset = 0;
    token = 16;
    scanError = 0;
  }
  function scanNumber() {
    let start = pos;
    if (text.charCodeAt(pos) === 48) {
      pos++;
    } else {
      pos++;
      while (pos < text.length && isDigit(text.charCodeAt(pos))) {
        pos++;
      }
    }
    if (pos < text.length && text.charCodeAt(pos) === 46) {
      pos++;
      if (pos < text.length && isDigit(text.charCodeAt(pos))) {
        pos++;
        while (pos < text.length && isDigit(text.charCodeAt(pos))) {
          pos++;
        }
      } else {
        scanError = 3;
        return text.substring(start, pos);
      }
    }
    let end = pos;
    if (pos < text.length && (text.charCodeAt(pos) === 69 || text.charCodeAt(pos) === 101)) {
      pos++;
      if (pos < text.length && text.charCodeAt(pos) === 43 || text.charCodeAt(pos) === 45) {
        pos++;
      }
      if (pos < text.length && isDigit(text.charCodeAt(pos))) {
        pos++;
        while (pos < text.length && isDigit(text.charCodeAt(pos))) {
          pos++;
        }
        end = pos;
      } else {
        scanError = 3;
      }
    }
    return text.substring(start, end);
  }
  function scanString() {
    let result = "", start = pos;
    while (true) {
      if (pos >= len) {
        result += text.substring(start, pos);
        scanError = 2;
        break;
      }
      const ch = text.charCodeAt(pos);
      if (ch === 34) {
        result += text.substring(start, pos);
        pos++;
        break;
      }
      if (ch === 92) {
        result += text.substring(start, pos);
        pos++;
        if (pos >= len) {
          scanError = 2;
          break;
        }
        const ch2 = text.charCodeAt(pos++);
        switch (ch2) {
          case 34:
            result += '"';
            break;
          case 92:
            result += "\\";
            break;
          case 47:
            result += "/";
            break;
          case 98:
            result += "\b";
            break;
          case 102:
            result += "\f";
            break;
          case 110:
            result += `
`;
            break;
          case 114:
            result += "\r";
            break;
          case 116:
            result += "\t";
            break;
          case 117:
            const ch3 = scanHexDigits(4, true);
            if (ch3 >= 0) {
              result += String.fromCharCode(ch3);
            } else {
              scanError = 4;
            }
            break;
          default:
            scanError = 5;
        }
        start = pos;
        continue;
      }
      if (ch >= 0 && ch <= 31) {
        if (isLineBreak(ch)) {
          result += text.substring(start, pos);
          scanError = 2;
          break;
        } else {
          scanError = 6;
        }
      }
      pos++;
    }
    return result;
  }
  function scanNext() {
    value = "";
    scanError = 0;
    tokenOffset = pos;
    lineStartOffset = lineNumber;
    prevTokenLineStartOffset = tokenLineStartOffset;
    if (pos >= len) {
      tokenOffset = len;
      return token = 17;
    }
    let code = text.charCodeAt(pos);
    if (isWhiteSpace(code)) {
      do {
        pos++;
        value += String.fromCharCode(code);
        code = text.charCodeAt(pos);
      } while (isWhiteSpace(code));
      return token = 15;
    }
    if (isLineBreak(code)) {
      pos++;
      value += String.fromCharCode(code);
      if (code === 13 && text.charCodeAt(pos) === 10) {
        pos++;
        value += `
`;
      }
      lineNumber++;
      tokenLineStartOffset = pos;
      return token = 14;
    }
    switch (code) {
      case 123:
        pos++;
        return token = 1;
      case 125:
        pos++;
        return token = 2;
      case 91:
        pos++;
        return token = 3;
      case 93:
        pos++;
        return token = 4;
      case 58:
        pos++;
        return token = 6;
      case 44:
        pos++;
        return token = 5;
      case 34:
        pos++;
        value = scanString();
        return token = 10;
      case 47:
        const start = pos - 1;
        if (text.charCodeAt(pos + 1) === 47) {
          pos += 2;
          while (pos < len) {
            if (isLineBreak(text.charCodeAt(pos))) {
              break;
            }
            pos++;
          }
          value = text.substring(start, pos);
          return token = 12;
        }
        if (text.charCodeAt(pos + 1) === 42) {
          pos += 2;
          const safeLength = len - 1;
          let commentClosed = false;
          while (pos < safeLength) {
            const ch = text.charCodeAt(pos);
            if (ch === 42 && text.charCodeAt(pos + 1) === 47) {
              pos += 2;
              commentClosed = true;
              break;
            }
            pos++;
            if (isLineBreak(ch)) {
              if (ch === 13 && text.charCodeAt(pos) === 10) {
                pos++;
              }
              lineNumber++;
              tokenLineStartOffset = pos;
            }
          }
          if (!commentClosed) {
            pos++;
            scanError = 1;
          }
          value = text.substring(start, pos);
          return token = 13;
        }
        value += String.fromCharCode(code);
        pos++;
        return token = 16;
      case 45:
        value += String.fromCharCode(code);
        pos++;
        if (pos === len || !isDigit(text.charCodeAt(pos))) {
          return token = 16;
        }
      case 48:
      case 49:
      case 50:
      case 51:
      case 52:
      case 53:
      case 54:
      case 55:
      case 56:
      case 57:
        value += scanNumber();
        return token = 11;
      default:
        while (pos < len && isUnknownContentCharacter(code)) {
          pos++;
          code = text.charCodeAt(pos);
        }
        if (tokenOffset !== pos) {
          value = text.substring(tokenOffset, pos);
          switch (value) {
            case "true":
              return token = 8;
            case "false":
              return token = 9;
            case "null":
              return token = 7;
          }
          return token = 16;
        }
        value += String.fromCharCode(code);
        pos++;
        return token = 16;
    }
  }
  function isUnknownContentCharacter(code) {
    if (isWhiteSpace(code) || isLineBreak(code)) {
      return false;
    }
    switch (code) {
      case 125:
      case 93:
      case 123:
      case 91:
      case 34:
      case 58:
      case 44:
      case 47:
        return false;
    }
    return true;
  }
  function scanNextNonTrivia() {
    let result;
    do {
      result = scanNext();
    } while (result >= 12 && result <= 15);
    return result;
  }
  return {
    setPosition,
    getPosition: () => pos,
    scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
    getToken: () => token,
    getTokenValue: () => value,
    getTokenOffset: () => tokenOffset,
    getTokenLength: () => pos - tokenOffset,
    getTokenStartLine: () => lineStartOffset,
    getTokenStartCharacter: () => tokenOffset - prevTokenLineStartOffset,
    getTokenError: () => scanError
  };
}
function isWhiteSpace(ch) {
  return ch === 32 || ch === 9;
}
function isLineBreak(ch) {
  return ch === 10 || ch === 13;
}
function isDigit(ch) {
  return ch >= 48 && ch <= 57;
}
var CharacterCodes;
var init_scanner = __esm(() => {
  (function(CharacterCodes2) {
    CharacterCodes2[CharacterCodes2["lineFeed"] = 10] = "lineFeed";
    CharacterCodes2[CharacterCodes2["carriageReturn"] = 13] = "carriageReturn";
    CharacterCodes2[CharacterCodes2["space"] = 32] = "space";
    CharacterCodes2[CharacterCodes2["_0"] = 48] = "_0";
    CharacterCodes2[CharacterCodes2["_1"] = 49] = "_1";
    CharacterCodes2[CharacterCodes2["_2"] = 50] = "_2";
    CharacterCodes2[CharacterCodes2["_3"] = 51] = "_3";
    CharacterCodes2[CharacterCodes2["_4"] = 52] = "_4";
    CharacterCodes2[CharacterCodes2["_5"] = 53] = "_5";
    CharacterCodes2[CharacterCodes2["_6"] = 54] = "_6";
    CharacterCodes2[CharacterCodes2["_7"] = 55] = "_7";
    CharacterCodes2[CharacterCodes2["_8"] = 56] = "_8";
    CharacterCodes2[CharacterCodes2["_9"] = 57] = "_9";
    CharacterCodes2[CharacterCodes2["a"] = 97] = "a";
    CharacterCodes2[CharacterCodes2["b"] = 98] = "b";
    CharacterCodes2[CharacterCodes2["c"] = 99] = "c";
    CharacterCodes2[CharacterCodes2["d"] = 100] = "d";
    CharacterCodes2[CharacterCodes2["e"] = 101] = "e";
    CharacterCodes2[CharacterCodes2["f"] = 102] = "f";
    CharacterCodes2[CharacterCodes2["g"] = 103] = "g";
    CharacterCodes2[CharacterCodes2["h"] = 104] = "h";
    CharacterCodes2[CharacterCodes2["i"] = 105] = "i";
    CharacterCodes2[CharacterCodes2["j"] = 106] = "j";
    CharacterCodes2[CharacterCodes2["k"] = 107] = "k";
    CharacterCodes2[CharacterCodes2["l"] = 108] = "l";
    CharacterCodes2[CharacterCodes2["m"] = 109] = "m";
    CharacterCodes2[CharacterCodes2["n"] = 110] = "n";
    CharacterCodes2[CharacterCodes2["o"] = 111] = "o";
    CharacterCodes2[CharacterCodes2["p"] = 112] = "p";
    CharacterCodes2[CharacterCodes2["q"] = 113] = "q";
    CharacterCodes2[CharacterCodes2["r"] = 114] = "r";
    CharacterCodes2[CharacterCodes2["s"] = 115] = "s";
    CharacterCodes2[CharacterCodes2["t"] = 116] = "t";
    CharacterCodes2[CharacterCodes2["u"] = 117] = "u";
    CharacterCodes2[CharacterCodes2["v"] = 118] = "v";
    CharacterCodes2[CharacterCodes2["w"] = 119] = "w";
    CharacterCodes2[CharacterCodes2["x"] = 120] = "x";
    CharacterCodes2[CharacterCodes2["y"] = 121] = "y";
    CharacterCodes2[CharacterCodes2["z"] = 122] = "z";
    CharacterCodes2[CharacterCodes2["A"] = 65] = "A";
    CharacterCodes2[CharacterCodes2["B"] = 66] = "B";
    CharacterCodes2[CharacterCodes2["C"] = 67] = "C";
    CharacterCodes2[CharacterCodes2["D"] = 68] = "D";
    CharacterCodes2[CharacterCodes2["E"] = 69] = "E";
    CharacterCodes2[CharacterCodes2["F"] = 70] = "F";
    CharacterCodes2[CharacterCodes2["G"] = 71] = "G";
    CharacterCodes2[CharacterCodes2["H"] = 72] = "H";
    CharacterCodes2[CharacterCodes2["I"] = 73] = "I";
    CharacterCodes2[CharacterCodes2["J"] = 74] = "J";
    CharacterCodes2[CharacterCodes2["K"] = 75] = "K";
    CharacterCodes2[CharacterCodes2["L"] = 76] = "L";
    CharacterCodes2[CharacterCodes2["M"] = 77] = "M";
    CharacterCodes2[CharacterCodes2["N"] = 78] = "N";
    CharacterCodes2[CharacterCodes2["O"] = 79] = "O";
    CharacterCodes2[CharacterCodes2["P"] = 80] = "P";
    CharacterCodes2[CharacterCodes2["Q"] = 81] = "Q";
    CharacterCodes2[CharacterCodes2["R"] = 82] = "R";
    CharacterCodes2[CharacterCodes2["S"] = 83] = "S";
    CharacterCodes2[CharacterCodes2["T"] = 84] = "T";
    CharacterCodes2[CharacterCodes2["U"] = 85] = "U";
    CharacterCodes2[CharacterCodes2["V"] = 86] = "V";
    CharacterCodes2[CharacterCodes2["W"] = 87] = "W";
    CharacterCodes2[CharacterCodes2["X"] = 88] = "X";
    CharacterCodes2[CharacterCodes2["Y"] = 89] = "Y";
    CharacterCodes2[CharacterCodes2["Z"] = 90] = "Z";
    CharacterCodes2[CharacterCodes2["asterisk"] = 42] = "asterisk";
    CharacterCodes2[CharacterCodes2["backslash"] = 92] = "backslash";
    CharacterCodes2[CharacterCodes2["closeBrace"] = 125] = "closeBrace";
    CharacterCodes2[CharacterCodes2["closeBracket"] = 93] = "closeBracket";
    CharacterCodes2[CharacterCodes2["colon"] = 58] = "colon";
    CharacterCodes2[CharacterCodes2["comma"] = 44] = "comma";
    CharacterCodes2[CharacterCodes2["dot"] = 46] = "dot";
    CharacterCodes2[CharacterCodes2["doubleQuote"] = 34] = "doubleQuote";
    CharacterCodes2[CharacterCodes2["minus"] = 45] = "minus";
    CharacterCodes2[CharacterCodes2["openBrace"] = 123] = "openBrace";
    CharacterCodes2[CharacterCodes2["openBracket"] = 91] = "openBracket";
    CharacterCodes2[CharacterCodes2["plus"] = 43] = "plus";
    CharacterCodes2[CharacterCodes2["slash"] = 47] = "slash";
    CharacterCodes2[CharacterCodes2["formFeed"] = 12] = "formFeed";
    CharacterCodes2[CharacterCodes2["tab"] = 9] = "tab";
  })(CharacterCodes || (CharacterCodes = {}));
});

// ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/string-intern.js
var cachedSpaces, maxCachedValues = 200, cachedBreakLinesWithSpaces;
var init_string_intern = __esm(() => {
  cachedSpaces = new Array(20).fill(0).map((_, index) => {
    return " ".repeat(index);
  });
  cachedBreakLinesWithSpaces = {
    " ": {
      "\n": new Array(maxCachedValues).fill(0).map((_, index) => {
        return `
` + " ".repeat(index);
      }),
      "\r": new Array(maxCachedValues).fill(0).map((_, index) => {
        return "\r" + " ".repeat(index);
      }),
      "\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
        return `\r
` + " ".repeat(index);
      })
    },
    "\t": {
      "\n": new Array(maxCachedValues).fill(0).map((_, index) => {
        return `
` + "\t".repeat(index);
      }),
      "\r": new Array(maxCachedValues).fill(0).map((_, index) => {
        return "\r" + "\t".repeat(index);
      }),
      "\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
        return `\r
` + "\t".repeat(index);
      })
    }
  };
});

// ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/format.js
var init_format = __esm(() => {
  init_scanner();
  init_string_intern();
});

// ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/parser.js
function parse(text, errors = [], options = ParseOptions.DEFAULT) {
  let currentProperty = null;
  let currentParent = [];
  const previousParents = [];
  function onValue(value) {
    if (Array.isArray(currentParent)) {
      currentParent.push(value);
    } else if (currentProperty !== null) {
      currentParent[currentProperty] = value;
    }
  }
  const visitor = {
    onObjectBegin: () => {
      const object = {};
      onValue(object);
      previousParents.push(currentParent);
      currentParent = object;
      currentProperty = null;
    },
    onObjectProperty: (name) => {
      currentProperty = name;
    },
    onObjectEnd: () => {
      currentParent = previousParents.pop();
    },
    onArrayBegin: () => {
      const array = [];
      onValue(array);
      previousParents.push(currentParent);
      currentParent = array;
      currentProperty = null;
    },
    onArrayEnd: () => {
      currentParent = previousParents.pop();
    },
    onLiteralValue: onValue,
    onError: (error, offset, length) => {
      errors.push({ error, offset, length });
    }
  };
  visit(text, visitor, options);
  return currentParent[0];
}
function visit(text, visitor, options = ParseOptions.DEFAULT) {
  const _scanner = createScanner(text, false);
  const _jsonPath = [];
  let suppressedCallbacks = 0;
  function toNoArgVisit(visitFunction) {
    return visitFunction ? () => suppressedCallbacks === 0 && visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
  }
  function toOneArgVisit(visitFunction) {
    return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
  }
  function toOneArgVisitWithPath(visitFunction) {
    return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice()) : () => true;
  }
  function toBeginVisit(visitFunction) {
    return visitFunction ? () => {
      if (suppressedCallbacks > 0) {
        suppressedCallbacks++;
      } else {
        let cbReturn = visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice());
        if (cbReturn === false) {
          suppressedCallbacks = 1;
        }
      }
    } : () => true;
  }
  function toEndVisit(visitFunction) {
    return visitFunction ? () => {
      if (suppressedCallbacks > 0) {
        suppressedCallbacks--;
      }
      if (suppressedCallbacks === 0) {
        visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter());
      }
    } : () => true;
  }
  const onObjectBegin = toBeginVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisitWithPath(visitor.onObjectProperty), onObjectEnd = toEndVisit(visitor.onObjectEnd), onArrayBegin = toBeginVisit(visitor.onArrayBegin), onArrayEnd = toEndVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisitWithPath(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);
  const disallowComments = options && options.disallowComments;
  const allowTrailingComma = options && options.allowTrailingComma;
  function scanNext() {
    while (true) {
      const token = _scanner.scan();
      switch (_scanner.getTokenError()) {
        case 4:
          handleError(14);
          break;
        case 5:
          handleError(15);
          break;
        case 3:
          handleError(13);
          break;
        case 1:
          if (!disallowComments) {
            handleError(11);
          }
          break;
        case 2:
          handleError(12);
          break;
        case 6:
          handleError(16);
          break;
      }
      switch (token) {
        case 12:
        case 13:
          if (disallowComments) {
            handleError(10);
          } else {
            onComment();
          }
          break;
        case 16:
          handleError(1);
          break;
        case 15:
        case 14:
          break;
        default:
          return token;
      }
    }
  }
  function handleError(error, skipUntilAfter = [], skipUntil = []) {
    onError(error);
    if (skipUntilAfter.length + skipUntil.length > 0) {
      let token = _scanner.getToken();
      while (token !== 17) {
        if (skipUntilAfter.indexOf(token) !== -1) {
          scanNext();
          break;
        } else if (skipUntil.indexOf(token) !== -1) {
          break;
        }
        token = scanNext();
      }
    }
  }
  function parseString(isValue) {
    const value = _scanner.getTokenValue();
    if (isValue) {
      onLiteralValue(value);
    } else {
      onObjectProperty(value);
      _jsonPath.push(value);
    }
    scanNext();
    return true;
  }
  function parseLiteral() {
    switch (_scanner.getToken()) {
      case 11:
        const tokenValue = _scanner.getTokenValue();
        let value = Number(tokenValue);
        if (isNaN(value)) {
          handleError(2);
          value = 0;
        }
        onLiteralValue(value);
        break;
      case 7:
        onLiteralValue(null);
        break;
      case 8:
        onLiteralValue(true);
        break;
      case 9:
        onLiteralValue(false);
        break;
      default:
        return false;
    }
    scanNext();
    return true;
  }
  function parseProperty() {
    if (_scanner.getToken() !== 10) {
      handleError(3, [], [2, 5]);
      return false;
    }
    parseString(false);
    if (_scanner.getToken() === 6) {
      onSeparator(":");
      scanNext();
      if (!parseValue()) {
        handleError(4, [], [2, 5]);
      }
    } else {
      handleError(5, [], [2, 5]);
    }
    _jsonPath.pop();
    return true;
  }
  function parseObject() {
    onObjectBegin();
    scanNext();
    let needsComma = false;
    while (_scanner.getToken() !== 2 && _scanner.getToken() !== 17) {
      if (_scanner.getToken() === 5) {
        if (!needsComma) {
          handleError(4, [], []);
        }
        onSeparator(",");
        scanNext();
        if (_scanner.getToken() === 2 && allowTrailingComma) {
          break;
        }
      } else if (needsComma) {
        handleError(6, [], []);
      }
      if (!parseProperty()) {
        handleError(4, [], [2, 5]);
      }
      needsComma = true;
    }
    onObjectEnd();
    if (_scanner.getToken() !== 2) {
      handleError(7, [2], []);
    } else {
      scanNext();
    }
    return true;
  }
  function parseArray() {
    onArrayBegin();
    scanNext();
    let isFirstElement = true;
    let needsComma = false;
    while (_scanner.getToken() !== 4 && _scanner.getToken() !== 17) {
      if (_scanner.getToken() === 5) {
        if (!needsComma) {
          handleError(4, [], []);
        }
        onSeparator(",");
        scanNext();
        if (_scanner.getToken() === 4 && allowTrailingComma) {
          break;
        }
      } else if (needsComma) {
        handleError(6, [], []);
      }
      if (isFirstElement) {
        _jsonPath.push(0);
        isFirstElement = false;
      } else {
        _jsonPath[_jsonPath.length - 1]++;
      }
      if (!parseValue()) {
        handleError(4, [], [4, 5]);
      }
      needsComma = true;
    }
    onArrayEnd();
    if (!isFirstElement) {
      _jsonPath.pop();
    }
    if (_scanner.getToken() !== 4) {
      handleError(8, [4], []);
    } else {
      scanNext();
    }
    return true;
  }
  function parseValue() {
    switch (_scanner.getToken()) {
      case 3:
        return parseArray();
      case 1:
        return parseObject();
      case 10:
        return parseString(true);
      default:
        return parseLiteral();
    }
  }
  scanNext();
  if (_scanner.getToken() === 17) {
    if (options.allowEmptyContent) {
      return true;
    }
    handleError(4, [], []);
    return false;
  }
  if (!parseValue()) {
    handleError(4, [], []);
    return false;
  }
  if (_scanner.getToken() !== 17) {
    handleError(9, [], []);
  }
  return true;
}
var ParseOptions;
var init_parser = __esm(() => {
  init_scanner();
  (function(ParseOptions2) {
    ParseOptions2.DEFAULT = {
      allowTrailingComma: false
    };
  })(ParseOptions || (ParseOptions = {}));
});

// ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/edit.js
var init_edit = __esm(() => {
  init_format();
  init_parser();
});

// ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js
var ScanError, SyntaxKind, parse2, ParseErrorCode;
var init_main = __esm(() => {
  init_format();
  init_edit();
  init_scanner();
  init_parser();
  (function(ScanError2) {
    ScanError2[ScanError2["None"] = 0] = "None";
    ScanError2[ScanError2["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
    ScanError2[ScanError2["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
    ScanError2[ScanError2["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
    ScanError2[ScanError2["InvalidUnicode"] = 4] = "InvalidUnicode";
    ScanError2[ScanError2["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
    ScanError2[ScanError2["InvalidCharacter"] = 6] = "InvalidCharacter";
  })(ScanError || (ScanError = {}));
  (function(SyntaxKind2) {
    SyntaxKind2[SyntaxKind2["OpenBraceToken"] = 1] = "OpenBraceToken";
    SyntaxKind2[SyntaxKind2["CloseBraceToken"] = 2] = "CloseBraceToken";
    SyntaxKind2[SyntaxKind2["OpenBracketToken"] = 3] = "OpenBracketToken";
    SyntaxKind2[SyntaxKind2["CloseBracketToken"] = 4] = "CloseBracketToken";
    SyntaxKind2[SyntaxKind2["CommaToken"] = 5] = "CommaToken";
    SyntaxKind2[SyntaxKind2["ColonToken"] = 6] = "ColonToken";
    SyntaxKind2[SyntaxKind2["NullKeyword"] = 7] = "NullKeyword";
    SyntaxKind2[SyntaxKind2["TrueKeyword"] = 8] = "TrueKeyword";
    SyntaxKind2[SyntaxKind2["FalseKeyword"] = 9] = "FalseKeyword";
    SyntaxKind2[SyntaxKind2["StringLiteral"] = 10] = "StringLiteral";
    SyntaxKind2[SyntaxKind2["NumericLiteral"] = 11] = "NumericLiteral";
    SyntaxKind2[SyntaxKind2["LineCommentTrivia"] = 12] = "LineCommentTrivia";
    SyntaxKind2[SyntaxKind2["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
    SyntaxKind2[SyntaxKind2["LineBreakTrivia"] = 14] = "LineBreakTrivia";
    SyntaxKind2[SyntaxKind2["Trivia"] = 15] = "Trivia";
    SyntaxKind2[SyntaxKind2["Unknown"] = 16] = "Unknown";
    SyntaxKind2[SyntaxKind2["EOF"] = 17] = "EOF";
  })(SyntaxKind || (SyntaxKind = {}));
  parse2 = parse;
  (function(ParseErrorCode2) {
    ParseErrorCode2[ParseErrorCode2["InvalidSymbol"] = 1] = "InvalidSymbol";
    ParseErrorCode2[ParseErrorCode2["InvalidNumberFormat"] = 2] = "InvalidNumberFormat";
    ParseErrorCode2[ParseErrorCode2["PropertyNameExpected"] = 3] = "PropertyNameExpected";
    ParseErrorCode2[ParseErrorCode2["ValueExpected"] = 4] = "ValueExpected";
    ParseErrorCode2[ParseErrorCode2["ColonExpected"] = 5] = "ColonExpected";
    ParseErrorCode2[ParseErrorCode2["CommaExpected"] = 6] = "CommaExpected";
    ParseErrorCode2[ParseErrorCode2["CloseBraceExpected"] = 7] = "CloseBraceExpected";
    ParseErrorCode2[ParseErrorCode2["CloseBracketExpected"] = 8] = "CloseBracketExpected";
    ParseErrorCode2[ParseErrorCode2["EndOfFileExpected"] = 9] = "EndOfFileExpected";
    ParseErrorCode2[ParseErrorCode2["InvalidCommentToken"] = 10] = "InvalidCommentToken";
    ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfComment"] = 11] = "UnexpectedEndOfComment";
    ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfString"] = 12] = "UnexpectedEndOfString";
    ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfNumber"] = 13] = "UnexpectedEndOfNumber";
    ParseErrorCode2[ParseErrorCode2["InvalidUnicode"] = 14] = "InvalidUnicode";
    ParseErrorCode2[ParseErrorCode2["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
    ParseErrorCode2[ParseErrorCode2["InvalidCharacter"] = 16] = "InvalidCharacter";
  })(ParseErrorCode || (ParseErrorCode = {}));
});

// ../core/src/opencode-integration.ts
var exports_opencode_integration = {};
__export(exports_opencode_integration, {
  writeOpencodeConfig: () => writeOpencodeConfig,
  writeAuthJson: () => writeAuthJson,
  verifyProvider: () => verifyProvider,
  removeApiKey: () => removeApiKey,
  readOpencodeConfig: () => readOpencodeConfig,
  readAuthJson: () => readAuthJson,
  migrateFromOldConfig: () => migrateFromOldConfig,
  isProviderAuthenticated: () => isProviderAuthenticated,
  getProviderAuth: () => getProviderAuth,
  getOpencodeGlobalConfigProviders: () => getOpencodeGlobalConfigProviders,
  getDebugInfo: () => getDebugInfo,
  getCustomProviders: () => getCustomProviders,
  getAuthenticatedProviders: () => getAuthenticatedProviders,
  getAllAvailableProviders: () => getAllAvailableProviders,
  addApiKey: () => addApiKey
});
import fs3 from "fs";
import path3 from "path";
import { homedir as homedir3 } from "os";
var getXDGPaths = () => ({
  data: path3.join(process.env.XDG_DATA_HOME || path3.join(homedir3(), ".local", "share"), "opencode"),
  config: path3.join(process.env.XDG_CONFIG_HOME || path3.join(homedir3(), ".config"), "opencode")
}), getFilePaths = () => {
  const paths = getXDGPaths();
  return {
    authJson: path3.join(paths.data, "auth.json"),
    opencodeJson: path3.join(paths.config, "opencode.json")
  };
}, ensureDirectories = () => {
  const paths = getXDGPaths();
  [paths.data, paths.config].forEach((dir) => {
    if (!fs3.existsSync(dir))
      fs3.mkdirSync(dir, { recursive: true });
  });
}, readAuthJson = async () => {
  const { authJson } = getFilePaths();
  try {
    if (!fs3.existsSync(authJson))
      return {};
    const data = fs3.readFileSync(authJson, "utf8");
    const errors = [];
    const parsed = parse2(data, errors, { allowTrailingComma: true });
    if (errors.length > 0) {
      console.warn("Warning: JSONC parsing errors in auth.json:", errors.map((e) => e.error).join(", "));
      return parsed || {};
    }
    return parsed;
  } catch (error) {
    console.warn("Warning: Could not read auth.json:", error.message);
    return {};
  }
}, writeAuthJson = async (authData) => {
  const { authJson } = getFilePaths();
  try {
    ensureDirectories();
    fs3.writeFileSync(authJson, JSON.stringify(authData, null, 2));
    fs3.chmodSync(authJson, 384);
    return true;
  } catch (error) {
    console.error("Error writing auth.json:", error.message);
    return false;
  }
}, writeOpencodeConfig = async () => {
  console.warn("Warning: opencode.json is no longer used. Use ai-benchmark-config.json instead.");
  return false;
}, readOpencodeConfig = async () => {
  console.warn("Warning: opencode.json is no longer used. Use ai-benchmark-config.json instead.");
  return { provider: {} };
}, readOpencodeGlobalConfig = () => {
  const configDir = path3.join(process.env.XDG_CONFIG_HOME || path3.join(homedir3(), ".config"), "opencode");
  const candidates = ["config.json", "opencode.json", "opencode.jsonc"];
  let merged = {};
  for (const filename of candidates) {
    const filePath = path3.join(configDir, filename);
    try {
      if (!fs3.existsSync(filePath))
        continue;
      const text = fs3.readFileSync(filePath, "utf8");
      const errors = [];
      const parsed = parse2(text, errors, { allowTrailingComma: true });
      if (parsed && typeof parsed === "object") {
        merged = {
          ...merged,
          ...parsed,
          provider: { ...merged.provider ?? {}, ...parsed.provider ?? {} }
        };
      }
    } catch (error) {
      console.warn(`Warning: Could not read opencode config file ${filename}:`, error.message);
    }
  }
  return merged;
}, getOpencodeGlobalConfigProviders = async () => {
  return getAuthenticatedProviders();
}, getAuthenticatedProviders = async () => {
  try {
    const [allModelsDevProviders, authData, globalConfig] = await Promise.all([
      getAllProviders(),
      readAuthJson(),
      Promise.resolve(readOpencodeGlobalConfig())
    ]);
    const database = new Map;
    for (const mdProvider of allModelsDevProviders) {
      const modelMap = new Map;
      for (const m of mdProvider.models) {
        modelMap.set(`${mdProvider.id}_${m.id}`, { id: `${mdProvider.id}_${m.id}`, name: m.name });
      }
      database.set(mdProvider.id, {
        id: mdProvider.id,
        name: mdProvider.name,
        type: mdProvider.type,
        baseUrl: mdProvider.baseUrl,
        models: modelMap
      });
    }
    for (const [providerID, entry] of Object.entries(globalConfig.provider ?? {})) {
      const existing = database.get(providerID);
      const modelMap = existing ? new Map(existing.models) : new Map;
      for (const [modelKey, m] of Object.entries(entry.models ?? {})) {
        const resolvedId = `${providerID}_${m.id ?? modelKey}`;
        modelMap.set(resolvedId, { id: resolvedId, name: m.name ?? m.id ?? modelKey });
      }
      database.set(providerID, {
        id: providerID,
        name: entry.name ?? existing?.name ?? providerID,
        type: existing?.type ?? "openai-compatible",
        baseUrl: entry.options?.baseURL ?? entry.api ?? existing?.baseUrl ?? "",
        models: modelMap,
        npm: entry.npm
      });
    }
    const providerMap = new Map;
    for (const [providerID, authInfo] of Object.entries(authData)) {
      if (authInfo.type !== "api" || !authInfo.key)
        continue;
      const dbEntry = database.get(providerID);
      if (!dbEntry)
        continue;
      const configEntry = globalConfig.provider?.[providerID];
      const npm = dbEntry.npm ?? configEntry?.npm;
      const type = npm?.includes("anthropic") ? "anthropic" : dbEntry.type;
      providerMap.set(providerID, {
        id: providerID,
        name: dbEntry.name,
        type,
        baseUrl: dbEntry.baseUrl,
        apiKey: authInfo.key,
        models: Array.from(dbEntry.models.values())
      });
    }
    for (const [providerID, entry] of Object.entries(globalConfig.provider ?? {})) {
      if (!entry.options?.apiKey)
        continue;
      const dbEntry = database.get(providerID);
      if (!dbEntry)
        continue;
      const npm = dbEntry.npm ?? entry.npm;
      const type = npm?.includes("anthropic") ? "anthropic" : dbEntry.type;
      providerMap.set(providerID, {
        id: providerID,
        name: dbEntry.name,
        type,
        baseUrl: dbEntry.baseUrl,
        apiKey: entry.options.apiKey,
        models: Array.from(dbEntry.models.values())
      });
    }
    return Array.from(providerMap.values());
  } catch (error) {
    console.warn("Warning: Could not load providers:", error.message);
    return [];
  }
}, getCustomProviders = async () => [], verifyProvider = async (providerId) => {
  try {
    const allProviders = await getAllProviders();
    return allProviders.find((p) => p.id === providerId) ?? null;
  } catch (error) {
    console.warn("Warning: Could not verify provider:", error.message);
    return null;
  }
}, addApiKey = async (providerId, apiKey) => {
  const authData = await readAuthJson();
  authData[providerId] = { type: "api", key: apiKey };
  return writeAuthJson(authData);
}, removeApiKey = async (providerId) => {
  const authData = await readAuthJson();
  if (authData[providerId]) {
    delete authData[providerId];
    return writeAuthJson(authData);
  }
  return true;
}, getAllAvailableProviders = async (includeAllProviders = false) => {
  const [opencodeProviders, customProvidersFromConfig, customVerifiedProviders] = await Promise.all([
    getAuthenticatedProviders(),
    (async () => {
      try {
        return await getCustomProvidersFromConfig();
      } catch (error) {
        console.warn("Warning: Could not load custom providers:", error.message);
        return [];
      }
    })(),
    (async () => {
      try {
        return loadCustomVerifiedProviders();
      } catch (error) {
        console.warn("Warning: Could not load custom verified providers:", error.message);
        return [];
      }
    })()
  ]);
  const providerMap = new Map;
  customVerifiedProviders.forEach((p) => providerMap.set(p.id, p));
  opencodeProviders.forEach((p) => providerMap.set(p.id, p));
  customProvidersFromConfig.forEach((p) => providerMap.set(p.id, p));
  if (includeAllProviders) {
    try {
      const allModelsDevProviders = await getAllProviders();
      const authenticatedIds = new Set(opencodeProviders.map((p) => p.id));
      const customIds = new Set(customProvidersFromConfig.map((p) => p.id));
      const customVerifiedIds = new Set(customVerifiedProviders.map((p) => p.id));
      allModelsDevProviders.forEach((provider) => {
        if (!authenticatedIds.has(provider.id) && !customIds.has(provider.id) && !customVerifiedIds.has(provider.id)) {
          providerMap.set(provider.id, {
            ...provider,
            type: provider.type,
            apiKey: "",
            models: provider.models.map((m) => ({ ...m, id: `${provider.id}_${m.id}` }))
          });
        }
      });
    } catch (error) {
      console.warn("Warning: Could not load all models.dev providers:", error.message);
    }
  }
  return Array.from(providerMap.values());
}, isProviderAuthenticated = async (providerId) => {
  const authData = await readAuthJson();
  return authData[providerId]?.type === "api";
}, getProviderAuth = async (providerId) => {
  const authData = await readAuthJson();
  return authData[providerId] ?? null;
}, migrateFromOldConfig = async (oldConfig) => {
  const results = { migrated: 0, failed: 0, errors: [] };
  try {
    if (oldConfig.verifiedProviders) {
      for (const [providerId, apiKey] of Object.entries(oldConfig.verifiedProviders)) {
        const providerInfo = await verifyProvider(providerId);
        if (providerInfo) {
          const success = await addApiKey(providerId, apiKey);
          if (success)
            results.migrated++;
          else {
            results.failed++;
            results.errors.push(`Failed to migrate ${providerId}`);
          }
        } else {
          results.failed++;
          results.errors.push(`Provider ${providerId} not found in models.dev`);
        }
      }
    }
    if (oldConfig.customProviders && oldConfig.customProviders.length > 0) {
      try {
        const { readAIConfig: readAIConfig2, writeAIConfig: writeAIConfig2 } = await Promise.resolve().then(() => (init_ai_config(), exports_ai_config));
        const config = await readAIConfig2();
        for (const provider of oldConfig.customProviders) {
          config.customProviders = config.customProviders || [];
          config.customProviders.push({
            id: provider.id,
            name: provider.name,
            type: provider.type,
            baseUrl: provider.baseUrl,
            apiKey: provider.apiKey,
            models: provider.models || []
          });
          results.migrated++;
        }
        await writeAIConfig2(config);
      } catch (error) {
        results.failed++;
        results.errors.push(`Failed to migrate custom providers: ${error.message}`);
      }
    }
    return results;
  } catch (error) {
    results.failed++;
    results.errors.push(`Migration failed: ${error.message}`);
    return results;
  }
}, getDebugInfo = async () => {
  const opencodePaths = getFilePaths();
  const { getAIConfigDebugPaths: getAIConfigDebugPaths2, readAIConfig: readAIConfig2 } = await Promise.resolve().then(() => (init_ai_config(), exports_ai_config));
  const aiConfigPaths = getAIConfigDebugPaths2();
  const authData = await readAuthJson();
  const aiConfigData = await readAIConfig2();
  return {
    opencodePaths,
    authExists: fs3.existsSync(opencodePaths.authJson),
    configExists: fs3.existsSync(opencodePaths.opencodeJson),
    authData: Object.keys(authData),
    configProviders: [],
    aiConfigPaths,
    aiConfigData: {
      verifiedProviders: Object.keys(aiConfigData.verifiedProviders || {}),
      customProviders: (aiConfigData.customProviders || []).map((p) => p.id)
    },
    xdgPaths: getXDGPaths()
  };
};
var init_opencode_integration = __esm(() => {
  init_models_dev();
  init_ai_config();
  init_main();
});

// ../core/src/constants.ts
import { execSync } from "child_process";
function getOpencodeSystemPrompt(modelId) {
  const mid = modelId.toLowerCase();
  if (mid.includes("gpt-4") || mid.includes("o1") || mid.includes("o3"))
    return PROMPT_BEAST;
  if (mid.includes("gpt")) {
    if (mid.includes("codex"))
      return PROMPT_CODEX;
    return PROMPT_GPT;
  }
  if (mid.includes("gemini-"))
    return PROMPT_GEMINI;
  if (mid.includes("claude"))
    return PROMPT_ANTHROPIC;
  if (mid.includes("trinity"))
    return PROMPT_TRINITY;
  if (mid.includes("kimi"))
    return PROMPT_KIMI;
  return PROMPT_DEFAULT;
}
function getOpencodeEnvBlock(modelId, providerId, cwd, isGitRepo) {
  return [
    `You are powered by the model named ${modelId}. The exact model ID is ${providerId}/${modelId}`,
    "Here is some useful information about the environment you are running in:",
    "<env>",
    `  Working directory: ${cwd}`,
    `  Workspace root folder: ${cwd}`,
    `  Is directory a git repo: ${isGitRepo ? "yes" : "no"}`,
    `  Platform: ${process.platform}`,
    `  Today's date: ${new Date().toDateString()}`,
    "</env>"
  ].join(`
`);
}
function getOpencodeVersion() {
  try {
    const v = execSync("opencode --version", { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
    if (v)
      return v;
  } catch {
    try {
      return execSync(`grep -E '^\\s*"version"' /home/idc/proj/opencode/packages/opencode/package.json | sed 's/.*: "\\([^"]*\\)".*/\\1/'`, {
        encoding: "utf8",
        stdio: ["ignore", "pipe", "ignore"]
      }).trim();
    } catch {}
  }
  return "1.14.37";
}
function getOpencodeHeaders(providerType) {
  const ver = getOpencodeVersion();
  const base = {
    "User-Agent": `opencode/${ver}`,
    "x-session-affinity": crypto.randomUUID()
  };
  if (providerType === "anthropic") {
    base["anthropic-beta"] = "interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14";
  }
  return base;
}
var TEST_PROMPT = `Explain the concept of recursion in programming and provide a simple example in Python, in about 300 words. Do not use any tools \u2014 just reply in text.`, PROMPT_BEAST, PROMPT_GPT, PROMPT_CODEX, PROMPT_GEMINI, PROMPT_ANTHROPIC, PROMPT_TRINITY, PROMPT_KIMI, PROMPT_DEFAULT, OPENCODE_TOOLS;
var init_constants = __esm(() => {
  PROMPT_BEAST = `You are opencode, an agent - please keep going until the user\u2019s query is completely resolved, before ending your turn and yielding back to the user.

Your thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.

You MUST iterate and keep going until the problem is solved.

You have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.

Only terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.

THE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.

You must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.

Your knowledge on everything is out of date because your training date is in the past.

You CANNOT successfully complete this task without using Google to verify your understanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.

Always tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.

If the user request is "resume" or "continue" or "try again", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.

Take your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.

You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.

You MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say "Next I will do X" or "Now I will do Y" or "I will do X", you MUST actually do X or Y instead just saying that you will do it.

You are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.`.trim();
  PROMPT_GPT = `You are OpenCode, You and the user share the same workspace and collaborate to achieve the user's goals.

You are a deeply pragmatic, effective software engineer. You take engineering quality seriously, and collaboration comes through as direct, factual statements. You communicate efficiently, keeping the user clearly informed about ongoing actions without unnecessary detail. You build context by examining the codebase first without making assumptions or jumping to conclusions. You think through the nuances of the code you encounter, and embody the mentality of a skilled senior software engineer.

- When searching for text or files, prefer using Glob and Grep tools (they are powered by \`rg\`)
- Parallelize tool calls whenever possible - especially file reads. Use \`multi_tool_use.parallel\` to parallelize tool calls and only this. Never chain together bash commands with separators like \`echo "====";\` as this renders to the user poorly.

## Editing Approach

- The best changes are often the smallest correct changes.
- When you are weighing two correct approaches, prefer the more minimal one (less new names, helpers, tests, etc).
- Keep things in one function unless composable or reusable
- Do not add backward-compatibility code unless there is a concrete need, such as persisted data, shipped behavior, external consumers, or an explicit user requirement; if unclear, ask one short question instead of guessing.

## Autonomy and persistence

Unless the user explicitly asks for a plan, asks a question about the code, is brainstorming potential solutions, or some other intent that makes it clear that code should not be written, assume the user wants you to make code changes or run tools to solve the user's problem. In these cases, it's bad to output your proposed solution in a message, you should go ahead and actually implement the change. If you encounter challenges or blockers, you should attempt to resolve them yourself.

Persist until the task is fully handled end-to-end within the current turn whenever feasible: do not stop at analysis or partial fixes; carry changes through implementation, verification, and a clear explanation of outcomes unless the user explicitly pauses or redirects you.

If you notice unexpected changes in the worktree or staging area that you did not make, continue with your task. NEVER revert, undo, or modify changes you did not make unless the user explicitly asks you to. There can be multiple agents or the user working in the same codebase concurrently.

## Editing constraints

- Default to ASCII when editing or creating files. Only introduce non-ASCII or other Unicode characters when there is a clear justification and the file already uses them.
- Add succinct code comments that explain what is going on if code is not self-explanatory. You should not add comments like "Assigns the value to the variable", but a brief comment might be useful ahead of a complex code block that the user would otherwise have to spend time parsing out. Usage of these comments should be rare.
- Always use apply_patch for manual code edits. Do not use cat or any other commands when creating or editing files. Formatting commands or bulk edits don't need to be done with apply_patch.
- Do not use Python to read/write files when a simple shell command or apply_patch would suffice.
- You may be in a dirty git worktree.
  * NEVER revert existing changes you did not make unless explicitly requested, since these changes were made by the user.
  * If asked to make a commit or code edits and there are unrelated changes to your work or changes that you didn't make in those files, don't revert those changes.
  * If the changes are in files you've touched recently, you should read carefully and understand how you can work with the changes rather than reverting them.
  * If the changes are in unrelated files, just ignore them and don't revert them.
- Do not amend a commit unless explicitly requested.
- While you are working, you might notice unexpected changes that you didn't make. It's likely the user made them, or were autogenerated. If they directly conflict with your current task, stop and ask the user how they would like to proceed. Otherwise, focus on the task at hand.
- **NEVER** use destructive commands like \`git reset --hard\` or \`git checkout --\` unless specifically requested or approved by the user.
- You struggle using the git interactive console. **ALWAYS** prefer using non-interactive git commands.

## Special user requests

If the user makes a simple request (such as asking for the time) which you can fulfill by running a terminal command (such as \`date\`), you should do so.

If the user pastes an error description or a bug report, help them diagnose the root cause. You can try to reproduce it if it seems feasible with the available tools and skills.

If the user asks for a "review", default to a code review mindset: prioritise identifying bugs, risks, behavioural regressions, and missing tests. Findings must be the primary focus of the response - keep summaries or overviews brief and only after enumerating the issues. Present findings first (ordered by severity with file/line references), follow with open questions or assumptions, and offer a change-summary only as a secondary detail. If no findings are discovered, state that explicitly and mention any residual risks or testing gaps.

## Frontend tasks

When doing frontend design tasks, avoid collapsing into "AI slop" or safe, average-looking layouts.
- Ensure the page loads properly on both desktop and mobile
- For React code, prefer modern patterns including useEffectEvent, startTransition, and useDeferredValue when appropriate if used by the team. Do not add useMemo/useCallback by default unless already used; follow the repo's React Compiler guidance.
- Overall: Avoid boilerplate layouts and interchangeable UI patterns. Vary themes, type families, and visual languages across outputs.

Exception: If working within an existing website or design system, preserve the established patterns, structure, and visual language.

# Working with the user

## General

Do not begin responses with conversational interjections or meta commentary. Avoid openers such as acknowledgements ("Done \u2014", "Got it", "Great question, ") or framing phrases.

Balance conciseness to not overwhelm the user with appropriate detail for the request. Do not narrate abstractly; explain what you are doing and why.

Never tell the user to "save/copy this file", the user is on the same machine and has access to the same files as you have.

## Formatting rules

Your responses are rendered as GitHub-flavored Markdown.

Never use nested bullets. Keep lists flat (single level). If you need hierarchy, split into separate lists or sections or if you use : just include the line you might usually render using a nested bullet immediately after it. For numbered lists, only use the \`1. 2. 3.\` style markers (with a period), never \`1)\`.

Headers are optional, only use them when you think they are necessary. If you do use them, use short Title Case (1-3 words) wrapped in **\u2026**. Don't add a blank line.

Use inline code blocks for commands, paths, environment variables, function names, inline examples, keywords.

Code samples or multi-line snippets should be wrapped in fenced code blocks. Include a language tag when possible.

Don\u2019t use emojis or em dashes unless explicitly instructed.

## Response channels

Use commentary for short progress updates while working and final for the completed response.

### \`commentary\` channel

Only use \`commentary\` for intermediary updates. These are short updates while you are working, they are NOT final answers. Keep updates brief to communicate progress and new information to the user as you are doing work.

Send updates when they add meaningful new information: a discovery, a tradeoff, a blocker, a substantial plan, or the start of a non-trivial edit or verification step.

Do not narrate routine reads, searches, obvious next steps, or minor confirmations. Combine related progress into a single update.

Do not begin responses with conversational interjections or meta commentary. Avoid openers such as acknowledgements ("Done \u2014", "Got it", "Great question") or framing phrases.

Before substantial work, send a short update describing your first step. Before editing files, send an update describing the edit.

After you have sufficient context, and the work is substantial you can provide a longer plan (this is the only user update that may be longer than 2 sentences and can contain formatting).

### \`final\` channel

Use final for the completed response.

Structure your final response if necessary. The complexity of the answer should match the task. If the task is simple, your answer should be a one-liner. Order sections from general to specific to supporting.

If the user asks for a code explanation, include code references. For simple tasks, just state the outcome without heavy formatting. For large or complex changes, lead with the solution, then explain what you did and why. For casual chat, just chat. If something couldn\u2019t be done (tests, builds, etc.), say so. Suggest next steps only when they are natural and useful; if you list options, use numbered items.`.trim();
  PROMPT_CODEX = `You are OpenCode, the best coding agent on the planet.

You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.

## Editing constraints
- Default to ASCII when editing or creating files. Only introduce non-ASCII or other Unicode characters when there is a clear justification and the file already uses them.
- Only add comments if they are necessary to make a non-obvious block easier to understand.
- Try to use apply_patch for single file edits, but it is fine to explore other options to make the edit if it does not work well. Do not use apply_patch for changes that are auto-generated (i.e. generating package.json or running a lint or format command like gofmt) or when scripting is more efficient (such as search and replacing a string across a codebase).

## Tool usage
- Prefer specialized tools over shell for file operations:
  - Use Read to view files, Edit to modify files, and Write only when needed.
  - Use Glob to find files by name and Grep to search file contents.
- Use Bash for terminal operations (git, bun, builds, tests, running scripts).
- Run tool calls in parallel when neither call needs the other\u2019s output; otherwise run sequentially.

## Git and workspace hygiene
- You may be in a dirty git worktree.
    * NEVER revert existing changes you did not make unless explicitly requested, since these changes were made by the user.
    * If asked to make a commit or code edits and there are unrelated changes to your work or changes that you didn't make in those files, don't revert those changes.
    * If the changes are in files you've touched recently, you should read carefully and understand how you can work with the changes rather than reverting them.
    * If the changes are in unrelated files, just ignore them and don't revert them.
- Do not amend commits unless explicitly requested.
- **NEVER** use destructive commands like \`git reset --hard\` or \`git checkout --\` unless specifically requested or approved by the user.

## Frontend tasks
When doing frontend design tasks, avoid collapsing into bland, generic layouts.
Aim for interfaces that feel intentional and deliberate.
- Typography: Use expressive, purposeful fonts and avoid default stacks (Inter, Roboto, Arial, system).
- Color & Look: Choose a clear visual direction; define CSS variables; avoid purple-on-white defaults. No purple bias or dark mode bias.
- Motion: Use a few meaningful animations (page-load, staggered reveals) instead of generic micro-motions.
- Background: Don't rely on flat, single-color backgrounds; use gradients, shapes, or subtle patterns to build atmosphere.
- Overall: Avoid boilerplate layouts and interchangeable UI patterns. Vary themes, type families, and visual languages across outputs.
- Ensure the page loads properly on both desktop and mobile.

Exception: If working within an existing website or design system, preserve the established patterns, structure, and visual language.

## Presenting your work and final message

You are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.

- Default: be very concise; friendly coding teammate tone.
- Default: do the work without asking questions. Treat short tasks as sufficient direction; infer missing details by reading the codebase and following existing conventions.
- Questions: only ask when you are truly blocked after checking relevant context AND you cannot safely pick a reasonable default. This usually means one of:
  * The request is ambiguous in a way that materially changes the result and you cannot disambiguate by reading the repo.
  * The action is destructive/irreversible, touches production, or changes billing/security posture.
  * You need a secret/credential/value that cannot be inferred (API key, account id, etc.).
- If you must ask: do all non-blocked work first, then ask exactly one targeted question, include your recommended default, and state what would change based on the answer.
- Never ask permission questions like "Should I proceed?" or "Do you want me to run tests?"; proceed with the most reasonable option and mention what you did.
- For substantial work, summarize clearly; follow final\u2011answer formatting.
- Skip heavy formatting for simple confirmations.
- Don't dump large files you've written; reference paths only.
- No "save/copy this file" - User is on the same machine.
- Offer logical next steps (tests, commits, build) briefly; add verify steps if you couldn't do something.
- For code changes:
  * Lead with a quick explanation of the change, and then give more details on the context covering where and why a change was made. Do not start this explanation with "summary", just jump right in.
  * If there are natural next steps the user may want to take, suggest them at the end of your response. Do not make suggestions if there are no natural next steps.
  * When suggesting multiple options, use numeric lists for the suggestions so the user can quickly respond with a single number.
- The user does not command execution outputs. When asked to show the output of a command (e.g. \`git show\`), relay the important details in your answer or summarize the key lines so the user understands the result.

## Final answer structure and style guidelines

- Plain text; CLI handles styling. Use structure only when it helps scannability.
- Headers: optional; short Title Case (1-3 words) wrapped in **\u2026**; no blank line before the first bullet; add only if they truly help.
- Bullets: use - ; merge related points; keep to one line when possible; 4\u20136 per list ordered by importance; keep phrasing consistent.
- Monospace: backticks for commands/paths/env vars/code ids and inline examples; use for literal keyword bullets; never combine with **.
- Code samples or multi-line snippets should be wrapped in fenced code blocks; include an info string as often as possible.
- Structure: group related bullets; order sections general \u2192 specific \u2192 supporting; match complexity to the task.
- Tone: collaborative, concise, factual; present tense, active voice; self\u2011contained; no "above/below"; parallel wording.
- Don'ts: no nested bullets/hierarchies; no ANSI codes; don't cram unrelated keywords; keep keyword lists short\u2014wrap/reformat if long; avoid naming formatting styles in answers.
- Adaptation: code explanations \u2192 precise, structured with code refs; simple tasks \u2192 lead with outcome; big changes \u2192 logical walkthrough + rationale + next actions; casual one-offs \u2192 plain sentences, no headers/bullets.
- File References: When referencing files in your response follow the below rules:
  * Use inline code to make file paths clickable.
  * Each reference should have a stand alone path. Even if it's the same file.
  * Accepted: absolute, workspace\u2011relative, a/ or b/ diff prefixes, or bare filename/suffix.
  * Optionally include line/column (1-based): :line[:column] or #Lline[Ccolumn] (column defaults to 1).
  * Do not use URIs like file://, vscode://, or https://.
  * Do not provide range of lines
  * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5`.trim();
  PROMPT_GEMINI = `You are opencode, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and efficiently, adhering strictly to the following instructions and utilizing your available tools.

# Core Mandates

- **Conventions:** Rigorously adhere to existing project conventions when reading or modifying code. Analyze surrounding code, tests, and configuration first.
- **Libraries/Frameworks:** NEVER assume a library/framework is available or appropriate. Verify its established usage within the project (check imports, configuration files like 'package.json', 'Cargo.toml', 'requirements.txt', 'build.gradle', etc., or observe neighboring files) before employing it.
- **Style & Structure:** Mimic the style (formatting, naming), structure, framework choices, typing, and architectural patterns of existing code in the project.
- **Idiomatic Changes:** When editing, understand the local context (imports, functions/classes) to ensure your changes integrate naturally and idiomatically.
- **Comments:** Add code comments sparingly. Focus on *why* something is done, especially for complex logic, rather than *what* is done. Only add high-value comments if necessary for clarity or if requested by the user. Do not edit comments that are separate from the code you are changing. *NEVER* talk to the user or describe your changes through comments.
- **Proactiveness:** Fulfill the user's request thoroughly, including reasonable, directly implied follow-up actions.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If asked *how* to do something, explain first, don't just do it.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Path Construction:** Before using any file system tool (e.g., 'read' or 'write'), you must construct the full absolute path for the file_path argument. Always combine the absolute path of the project's root directory with the file's path relative to the root.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user.

# Primary Workflows

## Software Engineering Tasks
1. **Understand:** Think about the user's request and the relevant codebase context.
2. **Plan:** Build a coherent plan.
3. **Implement:** Use the available tools to act on the plan.
4. **Verify (Tests):** If applicable and feasible, verify using project testing procedures.
5. **Verify (Standards):** After making code changes, execute build, linting and type-checking commands.

## New Applications
**Goal:** Autonomously implement and deliver a visually appealing, functional prototype.

1. **Understand Requirements**
2. **Propose Plan**
3. **User Approval**
4. **Implementation**
5. **Verify**
6. **Solicit Feedback**

# Operational Guidelines

## Tone and Style (CLI Interaction)
- **Concise & Direct:** fewer than 3 lines of text output per response whenever practical.
- **Clarity over Brevity (When Needed):**
- **No Chitchat:** Avoid conversational filler, preambles, or postambles.
- **Formatting:** Use GitHub-flavored Markdown.
- **Tools vs. Text:** Use tools for actions, text output only for communication.
- **Handling Inability:** State so briefly (1-2 sentences) without excessive justification.

## Security and Safety Rules
- **Explain Critical Commands:** Before executing commands that modify the file system or codebase.
- **Security First:** Never expose secrets or keys.

## Tool Usage
- **File Paths:** Always use absolute paths when referring to files with tools like 'read' or 'write'.
- **Parallelism:** Execute multiple independent tool calls in parallel when feasible.
- **Background Processes:** Use background processes (via \`&\`) for commands that are unlikely to stop on their own.
- **Interactive Commands:** Avoid shell commands requiring user interaction.

## Interaction Details
- **Help Command:** The user can use '/help' to display help information.
- **Feedback:** To report a bug, use the /bug command.`.trim();
  PROMPT_ANTHROPIC = `You are OpenCode, the best coding agent on the planet.

You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.

IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident that the URLs are for helping the user with programming. You may use URLs provided by the user in their messages or local files.

If the user asks for help or wants to give feedback inform them of the following:
- ctrl+p to list available actions
- To give feedback, users should report the issue at https://github.com/anomalyco/opencode

When the user directly asks about OpenCode (eg. "can OpenCode do...", "does OpenCode have..."), or asks in second person (eg. "are you able...", "can you do..."), or asks how to use a specific OpenCode feature (eg. implement a hook, write a slash command, or install an MCP server), use the WebFetch tool to gather information to answer the question from OpenCode docs. The list of available docs is available at https://opencode.ai/docs

# Tone and style
- Only use emojis if the user explicitly requests it. Avoid using emojis in all communication unless asked.
- Your output will be displayed on a command line interface. Your responses should be short and concise. You can use GitHub-flavored markdown for formatting, and will be rendered in a monospace font using the CommonMark specification.
- Output text to communicate with the user; all text you output outside of tool use is displayed to the user. Only use tools to complete tasks. Never use tools like Bash or code comments as means to communicate with the user during the session.
- NEVER create files unless they're absolutely necessary for achieving your goal. ALWAYS prefer editing an existing file to creating a new one. This includes markdown files.

# Professional objectivity
Prioritize technical accuracy and truthfulness over validating the user's beliefs. Focus on facts and problem-solving, providing direct, objective technical info without any unnecessary superlatives, praise, or emotional validation. It is best for the user if OpenCode honestly applies the same rigorous standards to all ideas and disagrees when necessary, even if it may not be what the user wants to hear. Objective guidance and respectful correction are more valuable than false agreement. Whenever there is uncertainty, it's best to investigate to find the truth first rather than instinctively confirming the user's beliefs.

# Task Management
You have access to the TodoWrite tools to help you manage and plan tasks. Use these tools VERY frequently to ensure that you are tracking your tasks and giving the user visibility into your progress.
These tools are also EXTREMELY helpful for planning tasks, and for breaking down larger complex tasks into smaller steps. If you do not use this tool when planning, you may forget to do important tasks - and that is unacceptable.

It is critical that you mark todos as completed as soon as you are done with a task. Do not batch up multiple tasks before marking them as completed.

Examples:

<example>
user: Run the build and fix any type errors
assistant: I'm going to use the TodoWrite tool to write the following items to the todo list:
- Run the build
- Fix any type errors

I'm now going to run the build using Bash.

Looks like I found 10 type errors. I'm going to use the TodoWrite tool to write 10 items to the todo list.

marking the first todo as in_progress

Let me start working on the first item...

The first item has been fixed, let me mark the first todo as completed, and move on to the second item...
..
..
</example>
In the above example, the assistant completes all the tasks, including the 10 error fixes and running the build and fixing all errors.

<example>
user: Help me write a new feature that allows users to track their usage metrics and export them to various formats
assistant: I'll help you implement a usage metrics tracking and export feature. Let me first use the TodoWrite tool to plan this task.
Adding the following todos to the todo list:
1. Research existing metrics tracking in the codebase
2. Design the metrics collection system
3. Implement core metrics tracking functionality
4. Create export functionality for different formats

Let me start by researching the existing codebase...
</example>

# Doing tasks
The user will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended:
-
- Use the TodoWrite tool to plan the task if required

- Tool results and user messages may include <system-reminder> tags. <system-reminder> tags contain useful information and reminders. They are automatically added by the system, and bear no direct relation to the specific tool results or user messages in which they appear.

# Tool usage policy
- When doing file search, prefer to use the Task tool in order to reduce context usage.
- You should proactively use the Task tool with specialized agents when the task at hand matches the agent's description.

- When WebFetch returns a message about a redirect to a different host, you should immediately make a new WebFetch request with the redirect URL provided in the response.
- You can call multiple tools in a single response. If you intend to call multiple tools and there are no dependencies between them, make all independent tool calls in parallel. Maximize use of parallel tool calls where possible to increase efficiency. However, if some tool calls depend on previous calls to inform dependent values, do NOT call these tools in parallel and instead call them sequentially. For instance, if one operation must complete before another starts, run these operations sequentially instead. Never use placeholders or guess missing parameters in tool calls.
- If the user specifies that they want you to run tools "in parallel", you MUST send a single message with multiple tool use content blocks. For example, if you need to launch multiple agents in parallel, send a single message with multiple Task tool calls.
- Use specialized tools instead of bash commands when possible, as this provides a better user experience. For file operations, use dedicated tools: Read for reading instead of cat/head/tail, Edit for editing instead of sed/awk, and Write for creating files instead of cat with heredoc or echo redirection. Reserve bash tools exclusively for actual system commands and terminal operations that require shell execution. NEVER use bash echo or other command-line tools to communicate thoughts, explanations, or instructions to the user. Output all communication directly in your response text instead.
- VERY IMPORTANT: When exploring the codebase to gather context or to answer a question that is not a needle query for a specific file/class/function, it is CRITICAL that you use the Task tool instead of running search commands directly.
<example>
user: Where are errors from the client handled?
assistant: Uses the Task tool to find the files that handle client errors instead of using Glob or Grep directly]
</example>
<example>
user: What is the codebase structure?
assistant: Uses the Task tool
</example>

IMPORTANT: Always use the TodoWrite tool to plan and track tasks throughout the conversation.

# Code References

When referencing specific functions or pieces of code include the pattern \`file_path:line_number\` to allow the user to easily navigate to the source code location.

<example>
user: Where are errors from the client handled?
assistant: Clients are marked as failed in the \`connectToServer\` function in src/services/process.ts:712.
</example>`.trim();
  PROMPT_TRINITY = `You are opencode, an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.

# Tone and style
You should be concise, direct, and to the point. When you run a non-trivial bash command, you should explain what the command does and why you are running it, to make sure the user understands what you are doing (this is especially important when you are running a command that will make changes to the user's system).
Remember that your output will be displayed on a command line interface. Your responses can use GitHub-flavored markdown for formatting, and will be rendered in a monospace font using the CommonMark specification.
Output text to communicate with the user; all text you output outside of tool use is displayed to the user. Only use tools to complete tasks. Never use tools like Bash or code comments as means to communicate with the user during the session.
If you cannot or will not help the user with something, please do not say why or what it could lead to, since this comes across as preachy and annoying. Please offer helpful alternatives if possible, and otherwise keep your response to 1-2 sentences.
Only use emojis if the user explicitly requests it. Avoid using emojis in all communication unless asked.
IMPORTANT: You should minimize output tokens as much as possible while maintaining helpfulness, quality, and accuracy. Only address the specific query or task at hand, avoiding tangential information unless absolutely critical for completing the request. If you can answer in 1-3 sentences or a short paragraph, please do.
IMPORTANT: You should NOT answer with unnecessary preamble or postamble (such as explaining your code or summarizing your action), unless the user asks you to.
IMPORTANT: Keep your responses short, since they will be displayed on a command line interface. You MUST answer concisely with fewer than 4 lines (not including tool use or code generation), unless user asks for detail. Answer the user's question directly, without elaboration, explanation, or details. One word answers are best. Avoid introductions, conclusions, and explanations. You MUST avoid text before/after your response, such as "The answer is <answer>.", "Here is the content of the file..." or "Based on the information provided, the answer is..." or "Here is what I will do next...". Here are some examples to demonstrate appropriate verbosity:
<example>
user: 2 + 2
assistant: 4
</example>
<example>
user: what is 2+2?
assistant: 4
</example>
<example>
user: is 11 a prime number?
assistant: Yes
</example>
<example>
user: what command should I run to list files in the current directory?
assistant: ls
</example>
<example>
user: How many golf balls fit inside a jetta?
assistant: 150000
</example>

# Proactiveness
You are allowed to be proactive, but only when the user asks you to do something. You should strive to strike a balance between:
1. Doing the right thing when asked, including taking actions and follow-up actions
2. Not surprising the user with actions you take without asking
3. Do not add additional code explanation summary unless requested by the user. After working on a file, just stop, rather than providing an explanation of what you did.

# Following conventions
When making changes to files, first understand the file's code conventions. Mimic code style, use existing libraries and utilities, and follow existing patterns.
- NEVER assume that a given library is available, even if it is well known. Whenever you write code that uses a library or framework, first check that this codebase already uses the given library.
- Always follow security best practices. Never introduce code that exposes or logs secrets and keys. Never commit secrets or keys to the repository.

# Code style
- IMPORTANT: DO NOT ADD ***ANY*** COMMENTS unless asked

# Doing tasks
The user will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended:
- Use the available search tools to understand the codebase and the user's query.
- Implement the solution using all tools available to you
- Verify the solution if possible with tests.
- VERY IMPORTANT: When you have completed a task, you MUST run the lint and typecheck commands with Bash if they were provided to you to ensure your code is correct.
NEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise the user will feel that you are being too proactive.

- Tool results and user messages may include <system-reminder> tags. <system-reminder> tags contain useful information and reminders. They are NOT part of the user's provided input or the tool result.

# Tool usage policy
- When doing file search, prefer to use the Task tool in order to reduce context usage.
- Use exactly one tool per assistant message. After each tool call, wait for the result before continuing.
- When the user's request is vague, use the question tool to clarify before reading files or making changes.
- Avoid repeating the same tool with the same parameters once you have useful results. Use the result to take the next step (e.g. pick one match, read that file, then act); do not search again in a loop.

You MUST answer concisely with fewer than 4 lines of text (not including tool use or code generation), unless user asks for detail.

# Code References

When referencing specific functions or pieces of code include the pattern \`file_path:line_number\` to allow the user to easily navigate to the source code location.`.trim();
  PROMPT_KIMI = `You are OpenCode, an interactive general AI agent running on a user's computer.

Your primary goal is to help users with software engineering tasks by taking action \u2014 use the tools available to you to make real changes on the user's system. You should also answer questions when asked. Always adhere strictly to the following system instructions and the user's requirements.

# Prompt and Tool Use

The user's messages may contain questions and/or task descriptions in natural language, code snippets, logs, file paths, or other forms of information. Read them, understand them and do what the user requested. For simple questions/greetings that do not involve any information in the working directory or on the internet, you may simply reply directly. For anything else, default to taking action with tools. When the request could be interpreted as either a question to answer or a task to complete, treat it as a task.

When handling the user's request, if it involves creating, modifying, or running code or files, you MUST use the appropriate tools to make actual changes \u2014 do not just describe the solution in text. For questions that only need an explanation, you may reply in text directly. When calling tools, do not provide explanations because the tool calls themselves should be self-explanatory. You MUST follow the description of each tool and its parameters when calling tools.

If the \`task\` tool is available, you can use it to delegate a focused subtask to a subagent instance. When delegating, provide a complete prompt with all necessary context because a newly created subagent does not automatically see your current context.

You have the capability to output any number of tool calls in a single response. If you anticipate making multiple non-interfering tool calls, you are HIGHLY RECOMMENDED to make them in parallel to significantly improve efficiency. This is very important to your performance.

The results of the tool calls will be returned to you in a tool message. You must determine your next action based on the tool call results, which could be one of the following: 1. Continue working on the task, 2. Inform the user that the task is completed or has failed, or 3. Ask the user for more information.

Tool results and user messages may include \`<system-reminder>\` tags. These are authoritative system directives that you MUST follow. They bear no direct relation to the specific tool results or user messages in which they appear. Always read them carefully and comply with their instructions \u2014 they may override or constrain your normal behavior (e.g., restricting you to read-only actions during plan mode).

When responding to the user, you MUST use the SAME language as the user, unless explicitly instructed to do otherwise.

# General Guidelines for Coding

When building something from scratch, you should:

- Understand the user's requirements.
- Ask the user for clarification if there is anything unclear.
- Design the architecture and make a plan for the implementation.
- Write the code in a modular and maintainable way.

Always use tools to implement your code changes:

- Use \`write\`/\`edit\` to create or modify source files. Code that only appears in your text response is NOT saved to the file system and will not take effect.
- Use \`bash\` to run and test your code after writing it.
- Iterate: if tests fail, read the error, fix the code with \`write\`/\`edit\`, and re-test with \`bash\`.

When working on an existing codebase, you should:

- Understand the codebase by reading it with tools (\`read\`, \`glob\`, \`grep\`) before making changes. Identify the ultimate goal and the most important criteria to achieve the goal.
- For a bug fix, you typically need to check error logs or failed tests, scan over the codebase to find the root cause, and figure out a fix. If user mentioned any failed tests, you should make sure they pass after the changes.
- For a feature, you typically need to design the architecture, and write the code in a modular and maintainable way, with minimal intrusions to existing code. Add new tests if the project already has tests.
- For a code refactoring, you typically need to update all the places that call the code you are refactoring if the interface changes. DO NOT change any existing logic especially in tests, focus only on fixing any errors caused by the interface changes.
- Make MINIMAL changes to achieve the goal. This is very important to your performance.
- Follow the coding style of existing code in the project.

DO NOT run \`git commit\`, \`git push\`, \`git reset\`, \`git rebase\` and/or do any other git mutations unless explicitly asked to do so. Ask for confirmation each time when you need to do git mutations, even if the user has confirmed in earlier conversations.

# General Guidelines for Research and Data Processing

The user may ask you to research on certain topics, process or generate certain multimedia files. When doing such tasks, you must:

- Understand the user's requirements thoroughly, ask for clarification before you start if needed.
- Make plans before doing deep or wide research, to ensure you are always on track.
- Search on the Internet if possible, with carefully-designed search queries to improve efficiency and accuracy.
- Use proper tools or shell commands or Python packages to process or generate images, videos, PDFs, docs, spreadsheets, presentations, or other multimedia files. Detect if there are already such tools in the environment. If you have to install third-party tools/packages, you MUST ensure that they are installed in a virtual/isolated environment.
- Once you generate or edit any images, videos or other media files, try to read it again before proceed, to ensure that the content is as expected.
- Avoid installing or deleting anything to/from outside of the current working directory. If you have to do so, ask the user for confirmation.

# Working Environment

## Operating System

The operating environment is not in a sandbox. Any actions you do will immediately affect the user's system. So you MUST be extremely cautious. Unless being explicitly instructed to do so, you should never access (read/write/execute) files outside of the working directory.

## Working Directory

The working directory should be considered as the project root if you are instructed to perform tasks on the project. Every file system operation will be relative to the working directory if you do not explicitly specify the absolute path. Tools may require absolute paths for some parameters, IF SO, YOU MUST use absolute paths for these parameters.

# Project Information

Markdown files named \`AGENTS.md\` usually contain the background, structure, coding styles, user preferences and other relevant information about the project. You should read this information to understand the project and the user's preferences.

If you modified any files/styles/structures/configurations/workflows/... mentioned in \`AGENTS.md\` files, you MUST update the corresponding \`AGENTS.md\` files to keep them up-to-date.

# Ultimate Reminders

At any time, you should be HELPFUL, CONCISE, and ACCURATE. Be thorough in your actions \u2014 test what you build, verify what you change \u2014 not in your explanations.

- Never diverge from the requirements and the goals of the task you work on. Stay on track.
- Never give the user more than what they want.
- Try your best to avoid any hallucination. Do fact checking before providing any factual information.
- Think about the best approach, then take action decisively.
- Do not give up too early.
- ALWAYS, keep it stupidly simple. Do not overcomplicate things.
- When the task requires creating or modifying files, always use tools to do so. Never treat displaying code in your response as a substitute for actually writing it to the file system.`.trim();
  PROMPT_DEFAULT = `You are opencode, an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.

IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident that the URLs are for helping the user with programming. You may use URLs provided by the user in their messages or local files.

If the user asks for help or wants to give feedback inform them of the following:
- /help: Get help with using opencode
- To give feedback, users should report the issue at https://github.com/anomalyco/opencode/issues

When the user directly asks about opencode (eg 'can opencode do...', 'does opencode have...') or asks in second person (eg 'are you able...', 'can you do...'), first use the WebFetch tool to gather information to answer the question from opencode docs at https://opencode.ai

# Tone and style
You should be concise, direct, and to the point. When you run a non-trivial bash command, you should explain what the command does and why you are running it, to make sure the user understands what you are doing (this is especially important when you are running a command that will make changes to the user's system).
Remember that your output will be displayed on a command line interface. Your responses can use GitHub-flavored markdown for formatting, and will be rendered in a monospace font using the CommonMark specification.
Output text to communicate with the user; all text you output outside of tool use is displayed to the user. Only use tools to complete tasks. Never use tools like Bash or code comments as means to communicate with the user during the session.
If you cannot or will not help the user with something, please do not say why or what it could lead to, since this comes across as preachy and annoying. Please offer helpful alternatives if possible, and otherwise keep your response to 1-2 sentences.
Only use emojis if the user explicitly requests it. Avoid using emojis in all communication unless asked.
IMPORTANT: You should minimize output tokens as much as possible while maintaining helpfulness, quality, and accuracy. Only address the specific query or task at hand, avoiding tangential information unless absolutely critical for completing the request. If you can answer in 1-3 sentences or a short paragraph, please do.
IMPORTANT: You should NOT answer with unnecessary preamble or postamble (such as explaining your code or summarizing your action), unless the user asks you to.
IMPORTANT: Keep your responses short, since they will be displayed on a command line interface. You MUST answer concisely with fewer than 4 lines (not including tool use or code generation), unless user asks for detail. Answer the user's question directly, without elaboration, explanation, or details. One word answers are best. Avoid introductions, conclusions, and explanations. You MUST avoid text before/after your response, such as "The answer is <answer>.", "Here is the content of the file..." or "Based on the information provided, the answer is..." or "Here is what I will do next...". Here are some examples to demonstrate appropriate verbosity:
<example>
user: 2 + 2
assistant: 4
</example>
<example>
user: what is 2+2?
assistant: 4
</example>
<example>
user: is 11 a prime number?
assistant: Yes
</example>
<example>
user: what command should I run to list files in the current directory?
assistant: ls
</example>
<example>
user: what files are in the directory src/?
assistant: [runs ls and sees foo.c, bar.c, baz.c]
user: which file contains the implementation of foo?
assistant: src/foo.c
</example>
<example>
user: write tests for new feature
assistant: [uses grep and glob search tools to find where similar tests are defined, uses concurrent read file tool use blocks in one tool call to read relevant files at the same time, uses edit file tool to write new tests]
</example>

# Proactiveness
You are allowed to be proactive, but only when the user asks you to do something. You should strive to strike a balance between:
1. Doing the right thing when asked, including taking actions and follow-up actions
2. Not surprising the user with actions you take without asking
For example, if the user asks you how to approach something, you should do your best to answer their question first, and not immediately jump into taking actions.
3. Do not add additional code explanation summary unless requested by the user. After working on a file, just stop, rather than providing an explanation of what you did.

# Following conventions
When making changes to files, first understand the file's code conventions. Mimic code style, use existing libraries and utilities, and follow existing patterns.
- NEVER assume that a given library is available, even if it is well known. Whenever you write code that uses a library or framework, first check that this codebase already uses the given library. For example, you might look at neighboring files, or check the package.json (or cargo.toml, and so on depending on the language).
- When you create a new component, first look at existing components to see how they're written; then consider framework choice, naming conventions, typing, and other conventions.
- When you edit a piece of code, first look at the code's surrounding context (especially its imports) to understand the code's choice of frameworks and libraries. Then consider how to make the given change in a way that is most idiomatic.
- Always follow security best practices. Never introduce code that exposes or logs secrets and keys. Never commit secrets or keys to the repository.

# Code style
- IMPORTANT: DO NOT ADD ***ANY*** COMMENTS unless asked

# Doing tasks
The user will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended:
- Use the available search tools to understand the codebase and the user's query. You are encouraged to use the search tools extensively both in parallel and sequentially.
- Implement the solution using all tools available to you
- Verify the solution if possible with tests. NEVER assume specific test framework or test script. Check the README or search codebase to determine the testing approach.
- VERY IMPORTANT: When you have completed a task, you MUST run the lint and typecheck commands (e.g. npm run lint, npm run typecheck, ruff, etc.) with Bash if they were provided to you to ensure your code is correct. If you are unable to find the correct command, ask the user for the command to run and if they supply it, proactively suggest writing it to AGENTS.md so that you will know to run it next time.
NEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise the user will feel that you are being too proactive.

- Tool results and user messages may include <system-reminder> tags. <system-reminder> tags contain useful information and reminders. They are NOT part of the user's provided input or the tool result.

# Tool usage policy
- When doing file search, prefer to use the Task tool in order to reduce context usage.
- You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. When making multiple bash tool calls, you MUST send a single message with multiple tools calls to run the calls in parallel. For example, if you need to run "git status" and "git diff", send a single message with two tool calls to run the calls in parallel.

You MUST answer concisely with fewer than 4 lines of text (not including tool use or code generation), unless user asks for detail.

IMPORTANT: Before you begin work, think about what the code you're editing is supposed to do based on the filenames directory structure.

# Code References

When referencing specific functions or pieces of code include the pattern \`file_path:line_number\` to allow the user to easily navigate to the source code location.

<example>
user: Where are errors from the client handled?
assistant: Clients are marked as failed in the \`connectToServer\` function in src/services/process.ts:712.
</example>`.trim();
  OPENCODE_TOOLS = {
    read: {
      name: "read",
      description: `Read a file or directory from the local filesystem. If the path does not exist, an error is returned.

Usage:
- The filePath parameter should be an absolute path.
- By default, this tool returns up to 2000 lines from the start of the file.
- The offset parameter is the line number to start from (1-indexed).
- To read later sections, call this tool again with a larger offset.
- Use the grep tool to find specific content in large files or files with long lines.
- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.
- Contents are returned with each line prefixed by its line number as \`<line>: <content>\`.
- Any line longer than 2000 characters is truncated.
- Call this tool in parallel when you know there are multiple files you want to read.
- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.
- This tool can read image files and PDFs and return them as file attachments.`,
      parameters: {
        type: "object",
        properties: {
          filePath: { type: "string", description: "The absolute path to the file or directory to read" },
          offset: { type: "integer", description: "The line number to start reading from (1-indexed)" },
          limit: { type: "integer", description: "The maximum number of lines to read (defaults to 2000)" }
        },
        required: ["filePath"]
      }
    },
    write: {
      name: "write",
      description: `Writes a file to the local filesystem.

Usage:
- This tool will overwrite the existing file if there is one at the provided path.
- If this is an existing file, you MUST use the Read tool first to read the file's contents.
- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.`,
      parameters: {
        type: "object",
        properties: {
          content: { type: "string", description: "The content to write to the file" },
          filePath: { type: "string", description: "The absolute path to the file to write (must be absolute, not relative)" }
        },
        required: ["filePath", "content"]
      }
    },
    edit: {
      name: "edit",
      description: `Performs exact string replacements in files.

Usage:
- You must use your \`Read\` tool at least once in the conversation before editing.
- When editing text from Read tool output, ensure you preserve the exact indentation (tabs/spaces).
- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
- Only use emojis if the user explicitly requests it.
- The edit will FAIL if \`oldString\` is not found in the file with an error "oldString not found in content".
- The edit will FAIL if \`oldString\` is found multiple times in the file. Use \`replaceAll\` to change every instance.
- Use \`replaceAll\` for replacing and renaming strings across the file.`,
      parameters: {
        type: "object",
        properties: {
          filePath: { type: "string", description: "The absolute path to the file to modify" },
          oldString: { type: "string", description: "The text to replace" },
          newString: { type: "string", description: "The text to replace it with (must be different from oldString)" },
          replaceAll: { type: "boolean", description: "Replace all occurrences of oldString (default false)" }
        },
        required: ["filePath", "oldString", "newString"]
      }
    },
    bash: {
      name: "bash",
      description: `Execute a bash command in a persistent shell session.

Be aware: OS: linux, Shell: bash

IMPORTANT: This tool is for terminal operations like git, npm, docker, etc. DO NOT use it for file operations (reading, writing, editing, searching, finding files) - use the specialized tools for this instead.

When issuing multiple commands:
- If the commands are independent and can run in parallel, make multiple tool calls in a single message.
- If the commands depend on each other and must run sequentially, use a single Bash call with '&&' to chain them together.

# Committing changes with git

Only create commits when requested by the user. If unclear, ask first. When the user asks you to create a new git commit, follow these steps carefully:

Git Safety Protocol:
- NEVER update the git config
- NEVER run destructive/irreversible git commands (like push --force, hard reset, etc) unless the user explicitly requests them
- NEVER skip hooks (--no-verify, --no-gpg-sign, etc) unless the user explicitly requests it
- NEVER run force push to main/master, warn the user if they request it
- Avoid git commit --amend. ONLY use --amend when ALL conditions are met.

1. You can call multiple tools in a single response. When multiple independent pieces of information are requested and all commands are likely to succeed, run multiple tool calls in parallel for optimal performance.
2. Analyze all staged changes and draft a commit message.
3. You can call multiple tools in a single response. When multiple independent pieces of information are requested and all commands are likely to succeed, run multiple tool calls in parallel for optimal performance. run the following commands in parallel:
   - Add relevant untracked files to the staging area.
   - Create the commit with a message
   - Run git status after the commit completes to verify success.
4. If the commit fails due to pre-commit hook, fix the issue and create a NEW commit.

Important notes:
- NEVER use the TodoWrite or Task tools
- DO NOT push to the remote repository unless the user explicitly asks you to do so
- IMPORTANT: Never use git commands with the -i flag since they require interactive input which is not supported.
- If there are no changes to commit, do not create an empty commit`,
      parameters: {
        type: "object",
        properties: {
          command: { type: "string", description: "The command to execute" },
          timeout: { type: "integer", description: "Optional timeout in milliseconds" },
          workdir: { type: "string", description: "The working directory to run the command in" },
          description: { type: "string", description: "Clear, concise description of what this command does in 5-10 words" }
        },
        required: ["command", "description"]
      }
    },
    glob: {
      name: "glob",
      description: `- Fast file pattern matching tool that works with any codebase size
- Supports glob patterns like "**/*.js" or "src/**/*.ts"
- Returns matching file paths sorted by modification time
- Use this tool when you need to find files by name patterns
- When you are doing an open-ended search that may require multiple rounds of globbing and grepping, use the Task tool instead
- You have the capability to call multiple tools in a single response. It is always better to speculatively perform multiple searches as a batch that are potentially useful.`,
      parameters: {
        type: "object",
        properties: {
          pattern: { type: "string", description: "The glob pattern to match files against" },
          path: { type: "string", description: "The directory to search in. Omit for default directory." }
        },
        required: ["pattern"]
      }
    },
    grep: {
      name: "grep",
      description: `- Fast content search tool that works with any codebase size
- Searches file contents using regular expressions
- Supports full regex syntax
- Filter files by pattern with the include parameter (eg. "*.js", "*.{ts,tsx}")
- Returns file paths and line numbers with at least one match sorted by modification time
- Use this tool when you need to find files containing specific patterns
- If you need to identify/count the number of matches within files, use the Bash tool with \`rg\` (ripgrep) directly. Do NOT use \`grep\`.
- When you are doing an open-ended search that may require multiple rounds of globbing and grepping, use the Task tool instead`,
      parameters: {
        type: "object",
        properties: {
          pattern: { type: "string", description: "The regex pattern to search for in file contents" },
          path: { type: "string", description: "The directory to search in. Defaults to cwd." },
          include: { type: "string", description: 'File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")' }
        },
        required: ["pattern"]
      }
    },
    task: {
      name: "task",
      description: `Launch a new agent to handle complex, multistep tasks autonomously.

When using the Task tool, you must specify a subagent_type parameter to select which agent type to use.

When to use the Task tool:
- When you are instructed to execute custom slash commands. Use the Task tool with the slash command invocation as the entire prompt.

When NOT to use the Task tool:
- If you want to read a specific file path, use the Read or Glob tool instead.
- If you are searching for a specific class definition, use the Glob tool instead.
- Other tasks that are not related to the agent descriptions above

Usage notes:
1. Launch multiple agents concurrently whenever possible, to maximize performance; use a single message with multiple tool uses
2. When the agent is done, it will return a single message back to you.
3. Each agent invocation starts with a fresh context unless you provide task_id to resume the same subagent session.
4. The agent's outputs should generally be trusted.
5. Clearly tell the agent whether you expect it to write code or just to do research.
6. If the agent description mentions that it should be used proactively, then you should try to use it without the user having to ask first.`,
      parameters: {
        type: "object",
        properties: {
          description: { type: "string", description: "A short (3-5 words) description of the task" },
          prompt: { type: "string", description: "The task for the agent to perform" },
          subagent_type: { type: "string", description: "The type of specialized agent to use" },
          task_id: { type: "string", description: "This should only be set if you mean to resume a previous task" },
          command: { type: "string", description: "The command that triggered this task" }
        },
        required: ["description", "prompt", "subagent_type"]
      }
    },
    webfetch: {
      name: "fetch",
      description: `Fetches content from a specified URL.

Usage notes:
- IMPORTANT: if another tool is present that offers better web fetching capabilities, prefer using that tool instead.
- The URL must be a fully-formed valid URL.
- HTTP URLs will be automatically upgraded to HTTPS.
- Format options: "markdown" (default), "text", or "html".
- This tool is read-only and does not modify any files.
- Results may be summarized if the content is very large.`,
      parameters: {
        type: "object",
        properties: {
          url: { type: "string", description: "The URL to fetch content from" },
          format: {
            type: "string",
            enum: ["text", "markdown", "html"],
            description: "The format to return the content in. Defaults to markdown."
          },
          timeout: { type: "integer", description: "Optional timeout in seconds (max 120)" }
        },
        required: ["url"]
      }
    },
    websearch: {
      name: "search",
      description: `Search the web using Exa AI - performs real-time web searches and can scrape content from specific URLs.

Usage notes:
- Supports live crawling modes: 'fallback' or 'preferred'
- Search types: 'auto' (balanced), 'fast' (quick results), 'deep' (comprehensive search)
- Configurable context length for optimal LLM integration

The current year is 2026. You MUST use this year when searching for recent information or current events`,
      parameters: {
        type: "object",
        properties: {
          query: { type: "string", description: "Websearch query" },
          numResults: { type: "integer", description: "Number of search results to return (default: 8)" },
          livecrawl: {
            type: "string",
            enum: ["fallback", "preferred"],
            description: "Live crawl mode - 'fallback': use live crawling as backup if cached content unavailable"
          },
          type: {
            type: "string",
            enum: ["auto", "fast", "deep"],
            description: "Search type - 'auto': balanced search (default), 'fast': quick results, 'deep': comprehensive search"
          },
          contextMaxCharacters: {
            type: "integer",
            description: "Maximum characters for context string optimized for LLMs (default: 10000)"
          }
        },
        required: ["query"]
      }
    },
    todowrite: {
      name: "todo",
      description: `Use this tool to create and manage a structured task list for your current coding session.

## When to Use This Tool
Use this tool proactively in these scenarios:
1. Complex multistep tasks - When a task requires 3 or more distinct steps
2. Non-trivial and complex tasks
3. User explicitly requests todo list
4. User provides multiple tasks (numbered or comma-separated)
5. After receiving new instructions - Immediately capture user requirements as todos
6. After completing a task - Mark it complete and add any new follow-up tasks
7. When you start working on a new task, mark the todo as in_progress

## When NOT to Use This Tool
Skip when:
1. Single, straightforward task
2. Trivial task
3. Can be completed in less than 3 trivial steps
4. Purely conversational or informational

## Task States
- pending: Task not yet started
- in_progress: Currently working on (limit to ONE task at a time)
- completed: Task finished successfully
- cancelled: Task no longer needed`,
      parameters: {
        type: "object",
        properties: {
          todos: {
            type: "array",
            description: "The updated todo list",
            items: {
              type: "object",
              properties: {
                content: { type: "string", description: "Brief description of the task" },
                status: {
                  type: "string",
                  enum: ["pending", "in_progress", "completed", "cancelled"],
                  description: "Current status"
                },
                priority: {
                  type: "string",
                  enum: ["high", "medium", "low"],
                  description: "Priority level"
                }
              },
              required: ["content", "status", "priority"]
            }
          }
        },
        required: ["todos"]
      }
    },
    question: {
      name: "question",
      description: `Use this tool when you need to ask the user questions during execution.

Usage notes:
- When \`custom\` is enabled (default), a "Type your own answer" option is added automatically.
- Answers are returned as arrays of labels; set \`multiple: true\` to allow selecting more than one.
- If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end.`,
      parameters: {
        type: "object",
        properties: {
          questions: {
            type: "array",
            items: {
              type: "object",
              properties: {
                question: { type: "string" },
                header: { type: "string" },
                options: {
                  type: "array",
                  items: {
                    type: "object",
                    properties: {
                      label: { type: "string" },
                      description: { type: "string" }
                    }
                  }
                },
                multiple: { type: "boolean" }
              }
            }
          }
        },
        required: ["questions"]
      }
    },
    skill: {
      name: "skill",
      description: `Load a specialized skill when the task at hand matches one of the skills listed in the system prompt.

Use this tool to inject the skill's instructions and resources into current conversation. The skill name must match one of the skills listed in your system prompt.`,
      parameters: {
        type: "object",
        properties: {
          name: { type: "string", description: "The name of the skill from available_skills" }
        },
        required: ["name"]
      }
    },
    apply_patch: {
      name: "patch",
      description: `Use the \`apply_patch\` tool to edit files. A stripped-down, file-oriented diff format.
Headers: \`*** Add File\`, \`*** Delete File\`, \`*** Update File\` with optional \`*** Move to\`.`,
      parameters: {
        type: "object",
        properties: {
          patchText: { type: "string", description: "The full patch text that describes all changes to be made" }
        },
        required: ["patchText"]
      }
    },
    invalid: {
      name: "invalid",
      description: "Do not use this tool.",
      parameters: {
        type: "object",
        properties: {
          tool: { type: "string" },
          error: { type: "string" }
        },
        required: ["tool", "error"]
      }
    }
  };
});

// ../core/src/benchmark.ts
var exports_benchmark = {};
__export(exports_benchmark, {
  benchmarkSingleModelRest: () => benchmarkSingleModelRest
});
function getModelIdForApi(model) {
  if (model.id && model.id.includes("_")) {
    return model.id.split("_")[1].trim();
  } else if (model.id) {
    return model.id.trim();
  }
  return model.name.trim();
}
function isGitRepo(dir) {
  try {
    const { execSync: execSync2 } = __require("child_process");
    execSync2("git rev-parse --git-dir", { cwd: dir, stdio: "ignore" });
    return true;
  } catch {
    return false;
  }
}
function buildSystemMessages(modelId, providerId, cwd) {
  const sysPrompt = getOpencodeSystemPrompt(modelId);
  const envBlock = getOpencodeEnvBlock(modelId, providerId, cwd, isGitRepo(cwd));
  return [
    { role: "system", content: sysPrompt },
    { role: "system", content: envBlock }
  ];
}
function buildToolList(modelId) {
  const mid = modelId.toLowerCase();
  const usePatch = mid.includes("gpt-") && !mid.includes("oss") && !mid.includes("gpt-4");
  const all = [];
  const add = (id, desc, params) => {
    all.push({
      type: "function",
      function: { name: id, description: desc, parameters: params }
    });
  };
  add(OPENCODE_TOOLS.read.name, OPENCODE_TOOLS.read.description, OPENCODE_TOOLS.read.parameters);
  add(OPENCODE_TOOLS.glob.name, OPENCODE_TOOLS.glob.description, OPENCODE_TOOLS.glob.parameters);
  add(OPENCODE_TOOLS.grep.name, OPENCODE_TOOLS.grep.description, OPENCODE_TOOLS.grep.parameters);
  add(OPENCODE_TOOLS.task.name, OPENCODE_TOOLS.task.description, OPENCODE_TOOLS.task.parameters);
  add(OPENCODE_TOOLS.webfetch.name, OPENCODE_TOOLS.webfetch.description, OPENCODE_TOOLS.webfetch.parameters);
  add(OPENCODE_TOOLS.websearch.name, OPENCODE_TOOLS.websearch.description, OPENCODE_TOOLS.websearch.parameters);
  add(OPENCODE_TOOLS.todowrite.name, OPENCODE_TOOLS.todowrite.description, OPENCODE_TOOLS.todowrite.parameters);
  add(OPENCODE_TOOLS.question.name, OPENCODE_TOOLS.question.description, OPENCODE_TOOLS.question.parameters);
  add(OPENCODE_TOOLS.skill.name, OPENCODE_TOOLS.skill.description, OPENCODE_TOOLS.skill.parameters);
  if (usePatch) {
    add(OPENCODE_TOOLS.apply_patch.name, OPENCODE_TOOLS.apply_patch.description, OPENCODE_TOOLS.apply_patch.parameters);
  } else {
    add(OPENCODE_TOOLS.edit.name, OPENCODE_TOOLS.edit.description, OPENCODE_TOOLS.edit.parameters);
    add(OPENCODE_TOOLS.write.name, OPENCODE_TOOLS.write.description, OPENCODE_TOOLS.write.parameters);
  }
  add("bash", OPENCODE_TOOLS.bash.description, OPENCODE_TOOLS.bash.parameters);
  return all;
}
async function benchmarkSingleModelRest(model, logger) {
  try {
    if (!model.providerConfig || !model.providerConfig.apiKey) {
      throw new Error(`Missing API key for provider ${model.providerName}`);
    }
    if (!model.providerConfig.baseUrl) {
      throw new Error(`Missing base URL for provider ${model.providerName}`);
    }
    const actualModelId = getModelIdForApi(model);
    await logger?.logHeader(model.name, model.providerName, model.providerConfig.apiKey);
    const startTime = Date.now();
    let firstTokenTime = null;
    let streamedText = "";
    let inputTokens = 0;
    let outputTokens = 0;
    let endpoint;
    if (model.providerConfig.endpointFormat) {
      endpoint = "/" + model.providerConfig.endpointFormat;
    } else if (model.providerType === "anthropic") {
      endpoint = "/messages";
    } else if (model.providerType === "google") {
      endpoint = "/models/" + actualModelId + ":streamGenerateContent";
    } else {
      endpoint = "/chat/completions";
    }
    const baseUrl = model.providerConfig.baseUrl.replace(/\/$/, "");
    const url = `${baseUrl}${endpoint}`;
    const cwd = process.cwd();
    const headers = {
      "Content-Type": "application/json",
      Authorization: `Bearer ${model.providerConfig.apiKey}`,
      ...getOpencodeHeaders(model.providerType === "anthropic" ? "anthropic" : "default")
    };
    if (model.providerType === "anthropic") {
      headers["x-api-key"] = model.providerConfig.apiKey;
      headers["anthropic-version"] = "2023-06-01";
    } else if (model.providerType === "google") {
      delete headers["Authorization"];
      headers["x-goog-api-key"] = model.providerConfig.apiKey;
    }
    const systemMessages = buildSystemMessages(actualModelId, model.providerId, cwd);
    const userMessage = { role: "user", content: TEST_PROMPT };
    const tools = buildToolList(actualModelId);
    const body = {
      model: actualModelId,
      messages: [...systemMessages, userMessage],
      max_tokens: 500,
      stream: true,
      stream_options: { include_usage: true },
      tools,
      tool_choice: "auto"
    };
    if (model.providerType === "google") {
      body["contents"] = [
        { parts: systemMessages.map((m) => ({ text: m.content })) },
        { parts: [{ text: userMessage.content }] }
      ];
      body["generationConfig"] = { maxOutputTokens: 500 };
      delete body["messages"];
      delete body["max_tokens"];
      delete body["stream"];
      delete body["stream_options"];
      delete body["tools"];
      delete body["tool_choice"];
    } else if (model.providerType === "anthropic") {
      delete body["stream_options"];
      body["tool_choice"] = { type: "auto" };
      body["system"] = systemMessages.map((m) => m.content).join(`

`);
      body["messages"] = [userMessage];
      body["tools"] = tools.map((t) => {
        const fn = t.function;
        return {
          name: fn.name,
          description: fn.description,
          input_schema: fn.parameters
        };
      });
    }
    const response = await fetch(url, {
      method: "POST",
      headers,
      body: JSON.stringify(body)
    });
    if (!response.ok) {
      const errBody = await response.text();
      let errDetail = "";
      try {
        const parsed = JSON.parse(errBody);
        if (typeof parsed.error === "object" && parsed.error?.message)
          errDetail = parsed.error.message;
        else if (typeof parsed.error === "string")
          errDetail = parsed.error;
        else if (parsed.message)
          errDetail = parsed.message;
        else
          errDetail = errBody.slice(0, 200);
      } catch {
        errDetail = errBody.slice(0, 200);
      }
      throw new Error(`${response.status} ${response.statusText}${errDetail ? ": " + errDetail : ""}`);
    }
    const reader = response.body.getReader();
    const decoder = new TextDecoder;
    let buffer = "";
    let firstParsedTokenTime = null;
    while (true) {
      const { done, value } = await reader.read();
      if (done)
        break;
      if (!firstTokenTime)
        firstTokenTime = Date.now();
      buffer += decoder.decode(value, { stream: true });
      const lines = buffer.split(`
`);
      buffer = lines.pop() || "";
      for (const line of lines) {
        const trimmedLine = line.trim();
        if (trimmedLine)
          await logger?.logRaw(trimmedLine);
        if (!trimmedLine)
          continue;
        try {
          if (model.providerType === "anthropic") {
            const anthropicDataPrefix = trimmedLine.startsWith("data: ") ? 6 : trimmedLine.startsWith("data:") ? 5 : -1;
            if (anthropicDataPrefix !== -1) {
              const jsonStr = trimmedLine.slice(anthropicDataPrefix);
              if (jsonStr === "[DONE]")
                continue;
              const chunk = JSON.parse(jsonStr);
              const chunkTyped = chunk;
              if (chunkTyped.type === "content_block_delta" && (chunkTyped.delta?.text || chunkTyped.delta?.thinking)) {
                if (!firstParsedTokenTime)
                  firstParsedTokenTime = Date.now();
                streamedText += chunkTyped.delta?.text || chunkTyped.delta?.thinking || "";
              } else if (chunkTyped.type === "message_start" && chunkTyped.message?.usage) {
                inputTokens = chunkTyped.message.usage.input_tokens || 0;
              } else if (chunkTyped.type === "message_delta") {
                if (chunkTyped.usage?.output_tokens)
                  outputTokens = chunkTyped.usage.output_tokens;
                if (chunkTyped.usage?.input_tokens && !inputTokens)
                  inputTokens = chunkTyped.usage.input_tokens;
              }
            } else if (trimmedLine.startsWith("event: ")) {
              continue;
            } else {
              const chunk = JSON.parse(trimmedLine);
              if (chunk.type === "content_block_delta" && (chunk.delta?.text || chunk.delta?.thinking)) {
                if (!firstParsedTokenTime)
                  firstParsedTokenTime = Date.now();
                streamedText += chunk.delta?.text || chunk.delta?.thinking || "";
              } else if (chunk.type === "message_start" && chunk.message?.usage) {
                inputTokens = chunk.message.usage.input_tokens || 0;
              } else if (chunk.type === "message_delta") {
                if (chunk.usage?.output_tokens)
                  outputTokens = chunk.usage.output_tokens;
                if (chunk.usage?.input_tokens && !inputTokens)
                  inputTokens = chunk.usage.input_tokens;
              }
            }
          } else if (model.providerType === "google") {
            const chunk = JSON.parse(trimmedLine);
            if (chunk.candidates?.[0]?.content?.parts?.[0]?.text) {
              if (!firstParsedTokenTime)
                firstParsedTokenTime = Date.now();
              streamedText += chunk.candidates[0].content.parts[0].text;
            }
            if (chunk.usageMetadata?.promptTokenCount)
              inputTokens = chunk.usageMetadata.promptTokenCount;
            if (chunk.usageMetadata?.candidatesTokenCount)
              outputTokens = chunk.usageMetadata.candidatesTokenCount;
          } else {
            const dataPrefix = trimmedLine.startsWith("data: ") ? 6 : trimmedLine.startsWith("data:") ? 5 : -1;
            if (dataPrefix === -1)
              continue;
            const jsonStr = trimmedLine.slice(dataPrefix);
            if (jsonStr === "[DONE]")
              continue;
            const chunk = JSON.parse(jsonStr);
            if (chunk.choices?.[0]?.delta?.content) {
              if (!firstParsedTokenTime)
                firstParsedTokenTime = Date.now();
              streamedText += chunk.choices[0].delta.content;
            } else if (chunk.choices?.[0]?.delta?.reasoning) {
              if (!firstParsedTokenTime)
                firstParsedTokenTime = Date.now();
              streamedText += chunk.choices[0].delta.reasoning;
            } else if (chunk.choices?.[0]?.delta?.reasoning_content) {
              if (!firstParsedTokenTime)
                firstParsedTokenTime = Date.now();
              streamedText += chunk.choices[0].delta.reasoning_content;
            } else if (chunk.type === "content_block_delta" && chunk.delta?.text) {
              if (!firstParsedTokenTime)
                firstParsedTokenTime = Date.now();
              streamedText += chunk.delta.text;
            } else if (chunk.type === "content_block_delta" && chunk.delta?.thinking) {
              if (!firstParsedTokenTime)
                firstParsedTokenTime = Date.now();
              streamedText += chunk.delta.thinking;
            }
            if (chunk.usage?.prompt_tokens)
              inputTokens = chunk.usage.prompt_tokens;
            if (chunk.usage?.completion_tokens)
              outputTokens = chunk.usage.completion_tokens;
            if (chunk.type === "message_start" && chunk.message?.usage?.input_tokens)
              inputTokens = chunk.message.usage.input_tokens;
            if (chunk.type === "message_delta" && chunk.usage?.output_tokens) {
              outputTokens = chunk.usage.output_tokens;
            }
          }
        } catch {
          continue;
        }
      }
    }
    await logger?.flush();
    const endTime = Date.now();
    const totalTime = endTime - startTime;
    const effectiveFirstToken = firstParsedTokenTime ?? firstTokenTime;
    const timeToFirstToken = effectiveFirstToken ? effectiveFirstToken - startTime : totalTime;
    const generationTime = totalTime - timeToFirstToken;
    const usedEstimateForOutput = !outputTokens;
    const usedEstimateForInput = !inputTokens;
    const finalOutputTokens = outputTokens || Math.round(streamedText.length / 4);
    const finalInputTokens = inputTokens || Math.round(TEST_PROMPT.length / 4);
    const totalTokens = finalInputTokens + finalOutputTokens;
    const tokensPerSecond = generationTime > 0 ? finalOutputTokens / generationTime * 1000 : 0;
    const f1000 = tokensPerSecond > 0 ? 1000 * (timeToFirstToken / 1000 + 300 / tokensPerSecond) / 3600 : Infinity;
    return {
      model: model.name,
      provider: model.providerName,
      totalTime,
      timeToFirstToken,
      tokenCount: finalOutputTokens,
      tokensPerSecond,
      f1000,
      promptTokens: finalInputTokens,
      totalTokens,
      usedEstimateForOutput,
      usedEstimateForInput,
      success: true
    };
  } catch (error) {
    await logger?.flush();
    return {
      model: model.name,
      provider: model.providerName,
      totalTime: 0,
      timeToFirstToken: 0,
      tokenCount: 0,
      tokensPerSecond: 0,
      f1000: Infinity,
      promptTokens: 0,
      totalTokens: 0,
      usedEstimateForOutput: true,
      usedEstimateForInput: true,
      success: false,
      error: error.message
    };
  }
}
var init_benchmark = __esm(() => {
  init_constants();
});

// ../core/src/headless.ts
var exports_headless = {};
__export(exports_headless, {
  runHeadlessBenchmark: () => runHeadlessBenchmark
});
function parseProviderModel(arg) {
  const firstColonIndex = arg.indexOf(":");
  if (firstColonIndex === -1) {
    throw new Error(`Invalid format. Use provider:model (e.g., openai:gpt-4)`);
  }
  return {
    provider: arg.substring(0, firstColonIndex),
    model: arg.substring(firstColonIndex + 1)
  };
}
function createCustomProviderFromCli(cliArgs) {
  const { provider, model } = parseProviderModel(cliArgs.benchCustom);
  if (!cliArgs.baseUrl)
    throw new Error("--base-url is required for custom provider benchmarking");
  if (!cliArgs.apiKey)
    throw new Error("--api-key is required for custom provider benchmarking");
  const endpointFormat = cliArgs.endpointFormat || "chat/completions";
  return {
    id: provider,
    name: provider,
    type: "openai-compatible",
    baseUrl: cliArgs.baseUrl,
    apiKey: cliArgs.apiKey,
    endpointFormat,
    models: [{ name: model, id: model }]
  };
}
async function loadConfig(includeAll) {
  try {
    const providers = await getAllAvailableProviders(includeAll);
    return { providers };
  } catch (error) {
    console.error("Error: Failed to load providers:", error.message);
    return { providers: [] };
  }
}
function buildJsonOutput(providerName, providerId, modelName, modelId, result, formatted) {
  const jsonOutput = {
    provider: providerName,
    providerId,
    model: modelName,
    modelId,
    method: "rest-api",
    success: result.success,
    totalTime: result.totalTime,
    totalTimeSeconds: result.totalTime / 1000,
    timeToFirstToken: result.timeToFirstToken,
    timeToFirstTokenSeconds: result.timeToFirstToken / 1000,
    tokensPerSecond: result.tokensPerSecond,
    f1000: result.f1000,
    f1000Hours: result.f1000 === Infinity ? null : result.f1000,
    outputTokens: result.tokenCount,
    promptTokens: result.promptTokens,
    totalTokens: result.totalTokens,
    is_estimated: !!(result.usedEstimateForOutput || result.usedEstimateForInput),
    error: result.error || null
  };
  return JSON.stringify(jsonOutput, null, formatted ? 2 : 0);
}
async function runHeadlessBenchmark(cliArgs) {
  try {
    if (cliArgs.benchCustom) {
      const customProvider = createCustomProviderFromCli(cliArgs);
      const modelDef = customProvider.models[0];
      const modelConfig2 = {
        id: modelDef.id,
        name: modelDef.name,
        providerName: customProvider.name,
        providerType: customProvider.type,
        providerId: customProvider.id,
        providerConfig: {
          baseUrl: customProvider.baseUrl,
          apiKey: customProvider.apiKey,
          endpointFormat: customProvider.endpointFormat
        }
      };
      const result2 = await benchmarkSingleModelRest(modelConfig2);
      if (!result2.success && result2.error)
        console.error(`Error: Benchmark failed: ${result2.error}`);
      console.log(buildJsonOutput(customProvider.name, customProvider.id, modelDef.name, modelDef.id, result2, cliArgs.formatted));
      process.exit(result2.success ? 0 : 1);
    }
    const benchSpec = cliArgs.bench;
    const colonIndex = benchSpec.indexOf(":");
    if (colonIndex === -1) {
      console.error("Error: Invalid --bench format. Use: provider:model");
      process.exit(1);
    }
    const providerSpec = benchSpec.substring(0, colonIndex);
    let modelName = benchSpec.substring(colonIndex + 1);
    if (modelName.startsWith('"') && modelName.endsWith('"') || modelName.startsWith("'") && modelName.endsWith("'")) {
      modelName = modelName.slice(1, -1);
    }
    if (!providerSpec || !modelName) {
      console.error("Error: Invalid --bench format. Use: provider:model");
      process.exit(1);
    }
    const config = await loadConfig(true);
    const provider = config.providers.find((p) => p.id?.toLowerCase() === providerSpec.toLowerCase() || p.name?.toLowerCase() === providerSpec.toLowerCase());
    if (!provider) {
      console.error(`Error: Provider '${providerSpec}' not found`);
      console.error("Available providers:");
      config.providers.forEach((p) => console.error(`  - ${p.id || p.name}`));
      process.exit(1);
    }
    const model = provider.models.find((m) => {
      const modelIdLower = m.id?.toLowerCase() || "";
      const modelNameLower = m.name?.toLowerCase() || "";
      const searchLower = modelName.toLowerCase();
      if (modelIdLower === searchLower)
        return true;
      const idWithoutPrefix = modelIdLower.includes("_") ? modelIdLower.split("_").slice(1).join("_") : modelIdLower;
      if (idWithoutPrefix === searchLower)
        return true;
      if (modelNameLower === searchLower)
        return true;
      return false;
    });
    if (!model) {
      console.error(`Error: Model '${modelName}' not found in provider '${provider.name}'`);
      console.error("Available models:");
      provider.models.forEach((m) => {
        const idWithoutPrefix = m.id?.includes("_") ? m.id.split("_").slice(1).join("_") : m.id;
        console.error(`  - ${m.name} (id: ${idWithoutPrefix})`);
      });
      process.exit(1);
    }
    const finalApiKey = cliArgs.apiKey || provider.apiKey;
    if (!finalApiKey) {
      console.error(`Error: No API key found for provider '${provider.name}'`);
      console.error("Please provide --api-key flag or configure the provider first");
      process.exit(1);
    }
    const modelConfig = {
      id: model.id,
      name: model.name,
      providerName: provider.name,
      providerType: provider.type,
      providerId: provider.id,
      providerConfig: {
        ...provider,
        apiKey: finalApiKey,
        baseUrl: provider.baseUrl || ""
      }
    };
    const result = await benchmarkSingleModelRest(modelConfig);
    if (!result.success && result.error)
      console.error(`Error: Benchmark failed: ${result.error}`);
    console.log(buildJsonOutput(provider.name, provider.id, model.name, model.id, result, cliArgs.formatted));
    process.exit(result.success ? 0 : 1);
  } catch (error) {
    console.error("Error: " + error.message);
    process.exit(1);
  }
}
var init_headless = __esm(() => {
  init_opencode_integration();
  init_benchmark();
});

// ../core/src/logger.ts
var exports_logger = {};
__export(exports_logger, {
  getLogPath: () => getLogPath,
  createRunId: () => createRunId,
  createBenchLogger: () => createBenchLogger
});
import { mkdir, appendFile } from "fs/promises";
import { homedir as homedir4 } from "os";
import { join } from "path";
function generateRunId() {
  const now = new Date;
  const date = now.toISOString().slice(0, 10);
  const time = now.toTimeString().slice(0, 8).replace(/:/g, "");
  const rand = Math.random().toString(16).slice(2, 6);
  return `${date}_${time}_${rand}`;
}
function redactSecrets(line, apiKey) {
  if (!apiKey)
    return line;
  return line.split(apiKey).join("[REDACTED]");
}
function createRunId() {
  return generateRunId();
}
function getLogPath(runId) {
  return join(homedir4(), ".local", "share", "ai-speedometer", "logs", `${runId}.log`);
}
async function createBenchLogger(runId) {
  const logPath = getLogPath(runId);
  const logDir = join(homedir4(), ".local", "share", "ai-speedometer", "logs");
  await mkdir(logDir, { recursive: true });
  let currentApiKey = "";
  let buffer = "";
  return {
    logPath,
    runId,
    logHeader: async (modelName, providerName, apiKey = "") => {
      currentApiKey = apiKey;
      const ts = new Date().toISOString();
      buffer = `
=== ${modelName} | ${providerName} | ${ts} ===
`;
    },
    logRaw: async (line) => {
      const safe = redactSecrets(line, currentApiKey);
      buffer += safe + `
`;
    },
    flush: async () => {
      buffer += `
` + "=".repeat(60) + `
`;
      await appendFile(logPath, buffer, "utf8");
      buffer = "";
    }
  };
}
var init_logger = () => {};

// src/tui/context/AppContext.tsx
import { createContext, useContext, useReducer, useEffect } from "react";
import { jsxDEV } from "@opentui/react/jsx-dev-runtime";
function appReducer(state, action) {
  switch (action.type) {
    case "NAVIGATE":
      return { ...state, screen: action.screen };
    case "SET_CONFIG":
      return { ...state, config: action.config, isLoadingConfig: false };
    case "SET_SELECTED_MODELS":
      return { ...state, selectedModels: action.models };
    case "BENCH_START":
      return {
        ...state,
        benchResults: action.models.map((model) => ({
          model,
          status: "pending"
        }))
      };
    case "BENCH_MODEL_RUNNING":
      return {
        ...state,
        benchResults: state.benchResults.map((r) => r.model.id === action.modelId ? { ...r, status: "running", startedAt: Date.now() } : r)
      };
    case "BENCH_MODEL_DONE":
      return {
        ...state,
        benchResults: state.benchResults.map((r) => r.model.id === action.modelId ? { ...r, status: "done", result: action.result } : r)
      };
    case "BENCH_MODEL_ERROR":
      return {
        ...state,
        benchResults: state.benchResults.map((r) => r.model.id === action.modelId ? { ...r, status: "error", error: action.error } : r)
      };
    case "BENCH_RESET":
      return { ...state, benchResults: [], selectedModels: [] };
    case "SET_LOG_INFO":
      return { ...state, logMode: action.logMode, logPath: action.logPath, runId: action.runId };
    default:
      return state;
  }
}
function AppProvider({ children, logMode = false }) {
  const [state, dispatch] = useReducer(appReducer, initialState);
  useEffect(() => {
    if (logMode) {
      Promise.resolve().then(() => (init_logger(), exports_logger)).then(({ createRunId: createRunId2, getLogPath: getLogPath2 }) => {
        const runId = createRunId2();
        const logPath = getLogPath2(runId);
        dispatch({ type: "SET_LOG_INFO", logMode: true, logPath, runId });
      });
    }
  }, [logMode]);
  useEffect(() => {
    let cancelled = false;
    async function loadConfig2() {
      try {
        const { getAllAvailableProviders: getAllAvailableProviders2 } = await Promise.resolve().then(() => (init_opencode_integration(), exports_opencode_integration));
        const providers = await getAllAvailableProviders2(false);
        if (!cancelled) {
          dispatch({ type: "SET_CONFIG", config: { providers } });
        }
      } catch {
        if (!cancelled) {
          dispatch({ type: "SET_CONFIG", config: { providers: [] } });
        }
      }
    }
    loadConfig2();
    return () => {
      cancelled = true;
    };
  }, []);
  return /* @__PURE__ */ jsxDEV(AppContext.Provider, {
    value: { state, dispatch },
    children
  }, undefined, false, undefined, this);
}
function useAppContext() {
  const ctx = useContext(AppContext);
  if (!ctx)
    throw new Error("useAppContext must be used within AppProvider");
  return ctx;
}
function useNavigate() {
  const { dispatch } = useAppContext();
  return (screen) => dispatch({ type: "NAVIGATE", screen });
}
var initialState, AppContext;
var init_AppContext = __esm(() => {
  initialState = {
    screen: "main-menu",
    config: null,
    selectedModels: [],
    benchResults: [],
    isLoadingConfig: true,
    logMode: false,
    logPath: null,
    runId: null
  };
  AppContext = createContext(null);
});

// src/tui/theme/themes.ts
function getTheme(name) {
  return THEMES[name] ?? THEMES[DEFAULT_THEME];
}
var THEMES, DEFAULT_THEME = "tokyonight", THEME_NAMES;
var init_themes = __esm(() => {
  THEMES = {
    tokyonight: {
      background: "#1a1b26",
      surface: "#1e2030",
      border: "#292e42",
      dim: "#565f89",
      text: "#c0caf5",
      primary: "#7aa2f7",
      accent: "#7dcfff",
      secondary: "#bb9af7",
      success: "#9ece6a",
      error: "#f7768e",
      warning: "#ff9e64"
    },
    dracula: {
      background: "#282a36",
      surface: "#21222c",
      border: "#44475a",
      dim: "#6272a4",
      text: "#f8f8f2",
      primary: "#bd93f9",
      accent: "#8be9fd",
      secondary: "#ff79c6",
      success: "#50fa7b",
      error: "#ff5555",
      warning: "#f1fa8c"
    },
    catppuccin: {
      background: "#1e1e2e",
      surface: "#181825",
      border: "#313244",
      dim: "#585b70",
      text: "#cdd6f4",
      primary: "#89b4fa",
      accent: "#89dceb",
      secondary: "#cba6f7",
      success: "#a6e3a1",
      error: "#f38ba8",
      warning: "#fab387"
    },
    "catppuccin-frappe": {
      background: "#303446",
      surface: "#292c3c",
      border: "#414559",
      dim: "#b5bfe2",
      text: "#c6d0f5",
      primary: "#8da4e2",
      accent: "#f4b8e4",
      secondary: "#ca9ee6",
      success: "#a6d189",
      error: "#e78284",
      warning: "#e5c890"
    },
    "catppuccin-macchiato": {
      background: "#24273a",
      surface: "#1e2030",
      border: "#363a4f",
      dim: "#b8c0e0",
      text: "#cad3f5",
      primary: "#8aadf4",
      accent: "#f5bde6",
      secondary: "#c6a0f6",
      success: "#a6da95",
      error: "#ed8796",
      warning: "#eed49f"
    },
    kanagawa: {
      background: "#1F1F28",
      surface: "#2A2A37",
      border: "#54546D",
      dim: "#727169",
      text: "#DCD7BA",
      primary: "#7E9CD8",
      accent: "#7FB4CA",
      secondary: "#957FB8",
      success: "#98BB6C",
      error: "#E82424",
      warning: "#D7A657"
    },
    rosepine: {
      background: "#191724",
      surface: "#1f1d2e",
      border: "#403d52",
      dim: "#6e6a86",
      text: "#e0def4",
      primary: "#9ccfd8",
      accent: "#ebbcba",
      secondary: "#c4a7e7",
      success: "#31748f",
      error: "#eb6f92",
      warning: "#f6c177"
    },
    nord: {
      background: "#2E3440",
      surface: "#3B4252",
      border: "#434C5E",
      dim: "#8B95A7",
      text: "#ECEFF4",
      primary: "#88C0D0",
      accent: "#8FBCBB",
      secondary: "#81A1C1",
      success: "#A3BE8C",
      error: "#BF616A",
      warning: "#D08770"
    },
    aura: {
      background: "#0f0f0f",
      surface: "#15141b",
      border: "#2d2d2d",
      dim: "#6d6d6d",
      text: "#edecee",
      primary: "#a277ff",
      accent: "#a277ff",
      secondary: "#f694ff",
      success: "#61ffca",
      error: "#ff6767",
      warning: "#ffca85"
    },
    ayu: {
      background: "#0B0E14",
      surface: "#0F131A",
      border: "#6C7380",
      dim: "#565B66",
      text: "#BFBDB6",
      primary: "#59C2FF",
      accent: "#E6B450",
      secondary: "#D2A6FF",
      success: "#7FD962",
      error: "#D95757",
      warning: "#E6B673"
    },
    carbonfox: {
      background: "#161616",
      surface: "#1a1a1a",
      border: "#303030",
      dim: "#7d848f",
      text: "#f2f4f8",
      primary: "#33b1ff",
      accent: "#ff7eb6",
      secondary: "#78a9ff",
      success: "#25be6a",
      error: "#ee5396",
      warning: "#f1c21b"
    },
    cobalt2: {
      background: "#193549",
      surface: "#122738",
      border: "#1f4662",
      dim: "#adb7c9",
      text: "#ffffff",
      primary: "#0088ff",
      accent: "#2affdf",
      secondary: "#9a5feb",
      success: "#9eff80",
      error: "#ff0088",
      warning: "#ffc600"
    },
    cursor: {
      background: "#181818",
      surface: "#141414",
      border: "#333333",
      dim: "#666666",
      text: "#e4e4e4",
      primary: "#88c0d0",
      accent: "#88c0d0",
      secondary: "#81a1c1",
      success: "#3fa266",
      error: "#e34671",
      warning: "#f1b467"
    },
    gruvbox: {
      background: "#282828",
      surface: "#3c3836",
      border: "#665c54",
      dim: "#928374",
      text: "#ebdbb2",
      primary: "#83a598",
      accent: "#8ec07c",
      secondary: "#d3869b",
      success: "#b8bb26",
      error: "#fb4934",
      warning: "#fe8019"
    },
    material: {
      background: "#263238",
      surface: "#1e272c",
      border: "#37474f",
      dim: "#546e7a",
      text: "#eeffff",
      primary: "#82aaff",
      accent: "#89ddff",
      secondary: "#c792ea",
      success: "#c3e88d",
      error: "#f07178",
      warning: "#ffcb6b"
    },
    matrix: {
      background: "#0a0e0a",
      surface: "#0e130d",
      border: "#1e2a1b",
      dim: "#8ca391",
      text: "#62ff94",
      primary: "#2eff6a",
      accent: "#c770ff",
      secondary: "#00efff",
      success: "#62ff94",
      error: "#ff4b4b",
      warning: "#e6ff57"
    },
    monokai: {
      background: "#272822",
      surface: "#1e1f1c",
      border: "#3e3d32",
      dim: "#75715e",
      text: "#f8f8f2",
      primary: "#66d9ef",
      accent: "#a6e22e",
      secondary: "#ae81ff",
      success: "#a6e22e",
      error: "#f92672",
      warning: "#e6db74"
    },
    nightowl: {
      background: "#011627",
      surface: "#0b253a",
      border: "#5f7e97",
      dim: "#5f7e97",
      text: "#d6deeb",
      primary: "#82AAFF",
      accent: "#c792ea",
      secondary: "#7fdbca",
      success: "#c5e478",
      error: "#EF5350",
      warning: "#ecc48d"
    },
    "one-dark": {
      background: "#282c34",
      surface: "#21252b",
      border: "#393f4a",
      dim: "#5c6370",
      text: "#abb2bf",
      primary: "#61afef",
      accent: "#56b6c2",
      secondary: "#c678dd",
      success: "#98c379",
      error: "#e06c75",
      warning: "#e5c07b"
    },
    opencode: {
      background: "#0a0a0a",
      surface: "#141414",
      border: "#484848",
      dim: "#808080",
      text: "#eeeeee",
      primary: "#fab283",
      accent: "#9d7cd8",
      secondary: "#5c9cf5",
      success: "#7fd88f",
      error: "#e06c75",
      warning: "#f5a742"
    },
    "osaka-jade": {
      background: "#111c18",
      surface: "#1a2520",
      border: "#3d4a44",
      dim: "#53685B",
      text: "#C1C497",
      primary: "#2DD5B7",
      accent: "#549e6a",
      secondary: "#D2689C",
      success: "#549e6a",
      error: "#FF5345",
      warning: "#E5C736"
    },
    palenight: {
      background: "#292d3e",
      surface: "#1e2132",
      border: "#32364a",
      dim: "#676e95",
      text: "#a6accd",
      primary: "#82aaff",
      accent: "#89ddff",
      secondary: "#c792ea",
      success: "#c3e88d",
      error: "#f07178",
      warning: "#ffcb6b"
    },
    synthwave84: {
      background: "#262335",
      surface: "#1e1a29",
      border: "#495495",
      dim: "#848bbd",
      text: "#ffffff",
      primary: "#36f9f6",
      accent: "#b084eb",
      secondary: "#ff7edb",
      success: "#72f1b8",
      error: "#fe4450",
      warning: "#fede5d"
    },
    vesper: {
      background: "#101010",
      surface: "#181818",
      border: "#282828",
      dim: "#A0A0A0",
      text: "#ffffff",
      primary: "#FFC799",
      accent: "#FFC799",
      secondary: "#99FFE4",
      success: "#99FFE4",
      error: "#FF8080",
      warning: "#FFC799"
    },
    zenburn: {
      background: "#3f3f3f",
      surface: "#4f4f4f",
      border: "#5f5f5f",
      dim: "#9f9f9f",
      text: "#dcdccc",
      primary: "#8cd0d3",
      accent: "#93e0e3",
      secondary: "#dc8cc3",
      success: "#7f9f7f",
      error: "#cc9393",
      warning: "#f0dfaf"
    },
    orng: {
      background: "#0a0a0a",
      surface: "#141414",
      border: "#EC5B2B",
      dim: "#808080",
      text: "#eeeeee",
      primary: "#EC5B2B",
      accent: "#FFF7F1",
      secondary: "#EE7948",
      success: "#6ba1e6",
      error: "#e06c75",
      warning: "#EC5B2B"
    },
    "lucent-orng": {
      background: "#000000",
      surface: "#0a0a0a",
      border: "#EC5B2B",
      dim: "#808080",
      text: "#eeeeee",
      primary: "#EC5B2B",
      accent: "#FFF7F1",
      secondary: "#EE7948",
      success: "#6ba1e6",
      error: "#e06c75",
      warning: "#EC5B2B"
    },
    github: {
      background: "#ffffff",
      surface: "#f6f8fa",
      border: "#d0d7de",
      dim: "#57606a",
      text: "#24292f",
      primary: "#0969da",
      accent: "#1b7c83",
      secondary: "#8250df",
      success: "#1a7f37",
      error: "#cf222e",
      warning: "#9a6700"
    },
    everforest: {
      background: "#fdf6e3",
      surface: "#efebd4",
      border: "#939f91",
      dim: "#a6b0a0",
      text: "#5c6a72",
      primary: "#8da101",
      accent: "#3a94c5",
      secondary: "#df69ba",
      success: "#8da101",
      error: "#f85552",
      warning: "#f57d26"
    },
    solarized: {
      background: "#fdf6e3",
      surface: "#eee8d5",
      border: "#cdc8b1",
      dim: "#93a1a1",
      text: "#657b83",
      primary: "#268bd2",
      accent: "#2aa198",
      secondary: "#6c71c4",
      success: "#859900",
      error: "#dc322f",
      warning: "#b58900"
    },
    flexoki: {
      background: "#FFFCF0",
      surface: "#F2F0E5",
      border: "#B7B5AC",
      dim: "#6F6E69",
      text: "#100F0F",
      primary: "#205EA6",
      accent: "#BC5215",
      secondary: "#5E409D",
      success: "#66800B",
      error: "#AF3029",
      warning: "#BC5215"
    },
    mercury: {
      background: "#ffffff",
      surface: "#fbfcfd",
      border: "#e0e0e8",
      dim: "#70707d",
      text: "#363644",
      primary: "#5266eb",
      accent: "#8da4f5",
      secondary: "#465bd1",
      success: "#036e43",
      error: "#b0175f",
      warning: "#a44200"
    },
    vercel: {
      background: "#FFFFFF",
      surface: "#FAFAFA",
      border: "#EAEAEA",
      dim: "#666666",
      text: "#171717",
      primary: "#0070F3",
      accent: "#8E4EC6",
      secondary: "#0062D1",
      success: "#388E3C",
      error: "#DC3545",
      warning: "#FF9500"
    }
  };
  THEME_NAMES = Object.keys(THEMES);
});

// src/tui/theme/ThemeContext.tsx
import { createContext as createContext2, useContext as useContext2, useState, useCallback } from "react";
import { jsxDEV as jsxDEV2 } from "@opentui/react/jsx-dev-runtime";
function ThemeProvider({ name, children }) {
  const [themeName, setThemeName] = useState(name);
  const setTheme = useCallback(async (next) => {
    setThemeName(next);
    try {
      const { writeThemeToConfig: writeThemeToConfig2 } = await Promise.resolve().then(() => (init_ai_config(), exports_ai_config));
      await writeThemeToConfig2(next);
    } catch {}
  }, []);
  return /* @__PURE__ */ jsxDEV2(ThemeContext.Provider, {
    value: { theme: getTheme(themeName), themeName, setTheme },
    children
  }, undefined, false, undefined, this);
}
function useTheme() {
  return useContext2(ThemeContext).theme;
}
function useThemeCtx() {
  return useContext2(ThemeContext);
}
var ThemeContext;
var init_ThemeContext = __esm(() => {
  init_themes();
  ThemeContext = createContext2({
    theme: getTheme(DEFAULT_THEME),
    themeName: DEFAULT_THEME,
    setTheme: () => {}
  });
});

// src/tui/context/ModalContext.tsx
import { createContext as createContext3, useContext as useContext3, useState as useState2 } from "react";
import { jsxDEV as jsxDEV3 } from "@opentui/react/jsx-dev-runtime";
function ModalProvider({ children }) {
  const [modalOpen, setModalOpen] = useState2(false);
  return /* @__PURE__ */ jsxDEV3(ModalContext.Provider, {
    value: { modalOpen, setModalOpen },
    children
  }, undefined, false, undefined, this);
}
function useModal() {
  return useContext3(ModalContext);
}
var ModalContext;
var init_ModalContext = __esm(() => {
  ModalContext = createContext3({ modalOpen: false, setModalOpen: () => {} });
});

// src/tui/components/ThemePicker.tsx
import { useState as useState3, useEffect as useEffect2, useRef } from "react";
import { useKeyboard } from "@opentui/react";
import { jsxDEV as jsxDEV4 } from "@opentui/react/jsx-dev-runtime";
function ThemePicker({ onClose }) {
  const { theme, themeName, setTheme } = useThemeCtx();
  const [query, setQuery] = useState3("");
  const [cursor, setCursor] = useState3(0);
  const scrollRef = useRef(null);
  const filtered = THEME_NAMES.filter((n) => n.includes(query.toLowerCase()));
  useEffect2(() => {
    setCursor(0);
    const name = filtered[0];
    if (name)
      setTheme(name);
  }, [query]);
  useEffect2(() => {
    const sb = scrollRef.current;
    if (!sb)
      return;
    if (cursor < sb.scrollTop)
      sb.scrollTo(cursor);
    else if (cursor >= sb.scrollTop + LIST_H)
      sb.scrollTo(cursor - LIST_H + 1);
  }, [cursor]);
  useEffect2(() => {
    const idx = filtered.indexOf(themeName);
    if (idx >= 0)
      setCursor(idx);
  }, []);
  useKeyboard((key) => {
    if (key.name === "escape" || !key.ctrl && key.sequence === "T") {
      onClose();
      return;
    }
    if (key.name === "up") {
      const n = Math.max(0, cursor - 1);
      setCursor(n);
      const name = filtered[n];
      if (name)
        setTheme(name);
      return;
    }
    if (key.name === "down") {
      const n = Math.min(filtered.length - 1, cursor + 1);
      setCursor(n);
      const name = filtered[n];
      if (name)
        setTheme(name);
      return;
    }
    if (key.name === "return" || key.name === "enter") {
      onClose();
      return;
    }
    if (key.name === "backspace" || key.name === "delete") {
      setQuery((q) => q.slice(0, -1));
      return;
    }
    if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta && key.sequence >= " ") {
      setQuery((q) => q + key.sequence);
    }
  });
  const W = 32;
  const LIST_H = 9;
  return /* @__PURE__ */ jsxDEV4("box", {
    position: "absolute",
    top: 3,
    right: 3,
    width: W,
    flexDirection: "column",
    border: true,
    borderStyle: "rounded",
    borderColor: theme.primary,
    backgroundColor: theme.surface,
    zIndex: 100,
    children: [
      /* @__PURE__ */ jsxDEV4("box", {
        height: 1,
        flexDirection: "row",
        paddingLeft: 2,
        paddingRight: 2,
        paddingTop: 1,
        children: [
          /* @__PURE__ */ jsxDEV4("text", {
            fg: theme.primary,
            children: "\uDB80\uDFD8 "
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV4("text", {
            fg: theme.text,
            children: "Themes"
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV4("text", {
            fg: theme.dim,
            children: [
              "  ",
              themeName
            ]
          }, undefined, true, undefined, this)
        ]
      }, undefined, true, undefined, this),
      /* @__PURE__ */ jsxDEV4("box", {
        height: 1,
        backgroundColor: theme.border
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV4("box", {
        height: 1,
        flexDirection: "row",
        paddingLeft: 2,
        paddingRight: 2,
        paddingTop: 1,
        paddingBottom: 1,
        children: [
          /* @__PURE__ */ jsxDEV4("text", {
            fg: theme.dim,
            children: "  "
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV4("text", {
            fg: query ? theme.text : theme.dim,
            children: query || "search..."
          }, undefined, false, undefined, this),
          query.length > 0 && /* @__PURE__ */ jsxDEV4("text", {
            fg: theme.accent,
            children: "_"
          }, undefined, false, undefined, this)
        ]
      }, undefined, true, undefined, this),
      /* @__PURE__ */ jsxDEV4("box", {
        height: 1,
        backgroundColor: theme.border
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV4("scrollbox", {
        ref: scrollRef,
        height: LIST_H,
        focused: false,
        children: filtered.length === 0 ? /* @__PURE__ */ jsxDEV4("box", {
          height: 1,
          paddingLeft: 3,
          children: /* @__PURE__ */ jsxDEV4("text", {
            fg: theme.dim,
            children: "no match"
          }, undefined, false, undefined, this)
        }, undefined, false, undefined, this) : filtered.map((name, i) => {
          const isActive = i === cursor;
          const isCurrent = name === themeName;
          const isDark = !LIGHT_THEMES.has(name);
          const tag = isDark ? "\u25C6" : "\u25C7";
          const tagColor = isDark ? theme.secondary : theme.accent;
          return /* @__PURE__ */ jsxDEV4("box", {
            height: 1,
            flexDirection: "row",
            backgroundColor: isActive ? theme.border : "transparent",
            paddingLeft: 2,
            paddingRight: 2,
            children: [
              /* @__PURE__ */ jsxDEV4("text", {
                fg: isCurrent ? theme.success : theme.dim,
                width: 2,
                children: isCurrent ? "\u2713" : " "
              }, undefined, false, undefined, this),
              /* @__PURE__ */ jsxDEV4("text", {
                fg: tagColor,
                width: 2,
                children: tag
              }, undefined, false, undefined, this),
              /* @__PURE__ */ jsxDEV4("text", {
                fg: isActive ? theme.text : theme.dim,
                children: [
                  " ",
                  name
                ]
              }, undefined, true, undefined, this),
              isActive && /* @__PURE__ */ jsxDEV4("text", {
                fg: theme.primary,
                children: "  \u203A"
              }, undefined, false, undefined, this)
            ]
          }, name, true, undefined, this);
        })
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV4("box", {
        height: 1,
        backgroundColor: theme.border
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV4("box", {
        height: 1,
        flexDirection: "row",
        paddingLeft: 2,
        paddingRight: 2,
        paddingTop: 1,
        paddingBottom: 1,
        children: [
          /* @__PURE__ */ jsxDEV4("text", {
            fg: theme.secondary,
            children: "\u25C6"
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV4("text", {
            fg: theme.dim,
            children: " dark  "
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV4("text", {
            fg: theme.accent,
            children: "\u25C7"
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV4("text", {
            fg: theme.dim,
            children: " light  "
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV4("text", {
            fg: theme.dim,
            children: "  [\u2191\u2193] [Esc]"
          }, undefined, false, undefined, this)
        ]
      }, undefined, true, undefined, this)
    ]
  }, undefined, true, undefined, this);
}
var LIGHT_THEMES;
var init_ThemePicker = __esm(() => {
  init_ThemeContext();
  init_themes();
  LIGHT_THEMES = new Set(["github", "everforest", "solarized", "flexoki", "mercury", "vercel"]);
});

// package.json
var package_default;
var init_package = __esm(() => {
  package_default = {
    name: "ai-speedometer",
    version: "2.3.3",
    description: "A comprehensive CLI tool for benchmarking AI models across multiple providers with parallel execution and professional metrics",
    bin: {
      "ai-speedometer": "dist/ai-speedometer",
      aispeed: "dist/ai-speedometer"
    },
    engines: {
      bun: ">=1.0.0"
    },
    scripts: {
      start: "bun src/index.ts",
      dev: "bun --watch src/index.ts",
      build: "bun build src/index.ts --outdir dist --target bun --external '@opentui/core' --external '@opentui/react' --external 'react' --external 'react-reconciler' && cat ../../scripts/shebang dist/index.js > dist/ai-speedometer && chmod +x dist/ai-speedometer && rm dist/index.js",
      typecheck: "bun tsc --noEmit",
      prepublishOnly: "bun run build"
    },
    keywords: [
      "ai",
      "benchmark",
      "cli",
      "speedometer",
      "performance",
      "llm",
      "models",
      "testing",
      "openai",
      "anthropic",
      "ai-sdk",
      "parallel",
      "metrics"
    ],
    author: "",
    license: "MIT",
    repository: {
      type: "git",
      url: "git+https://github.com/aptdnfapt/Ai-speedometer.git"
    },
    bugs: {
      url: "https://github.com/aptdnfapt/Ai-speedometer/issues"
    },
    homepage: "https://github.com/aptdnfapt/Ai-speedometer#readme",
    files: [
      "dist/",
      "../../docs/",
      "../../README.md",
      "../../ai-benchmark-config.json.template"
    ],
    dependencies: {
      "@opentui/core": "0.1.79",
      "@opentui/react": "0.1.79",
      react: "^19.0.0",
      "react-reconciler": "^0.32.0"
    },
    devDependencies: {
      "@ai-speedometer/core": "workspace:*",
      "jsonc-parser": "^3.3.1"
    }
  };
});

// src/tui/components/Header.tsx
import { jsxDEV as jsxDEV5 } from "@opentui/react/jsx-dev-runtime";
function Header({ screen }) {
  const theme = useTheme();
  if (screen === "main-menu")
    return null;
  return /* @__PURE__ */ jsxDEV5("box", {
    height: 1,
    flexDirection: "row",
    alignItems: "center",
    paddingLeft: 2,
    backgroundColor: theme.background,
    children: [
      /* @__PURE__ */ jsxDEV5("text", {
        fg: theme.primary,
        children: "AI-SPEEDOMETER"
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV5("text", {
        fg: theme.border,
        children: "  \xB7  "
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV5("text", {
        fg: theme.dim,
        children: [
          "v",
          package_default.version
        ]
      }, undefined, true, undefined, this)
    ]
  }, undefined, true, undefined, this);
}
var init_Header = __esm(() => {
  init_package();
  init_ThemeContext();
});

// src/tui/components/Footer.tsx
import { jsxDEV as jsxDEV6 } from "@opentui/react/jsx-dev-runtime";
function Footer({ hints }) {
  const theme = useTheme();
  const joined = hints.join("  \xB7  ");
  return /* @__PURE__ */ jsxDEV6("box", {
    height: 1,
    flexDirection: "row",
    alignItems: "center",
    paddingLeft: 1,
    paddingRight: 1,
    backgroundColor: theme.background,
    children: /* @__PURE__ */ jsxDEV6("text", {
      fg: theme.dim,
      children: joined
    }, undefined, false, undefined, this)
  }, undefined, false, undefined, this);
}
var init_Footer = __esm(() => {
  init_ThemeContext();
});

// src/tui/hooks/useAppKeyboard.ts
import { useKeyboard as useKeyboard2 } from "@opentui/react";
function useAppKeyboard(handler) {
  const { modalOpen } = useModal();
  useKeyboard2((key) => {
    if (modalOpen)
      return;
    handler(key);
  });
}
var init_useAppKeyboard = __esm(() => {
  init_ModalContext();
});

// src/tui/screens/MainMenuScreen.tsx
import { useState as useState4 } from "react";
import { useRenderer } from "@opentui/react";
import { jsxDEV as jsxDEV7 } from "@opentui/react/jsx-dev-runtime";
function MainMenuScreen() {
  const navigate = useNavigate();
  const renderer = useRenderer();
  const theme = useTheme();
  const [cursor, setCursor] = useState4(0);
  const ITEMS = [
    { label: "\u26A1 Run Benchmark", desc: "test model speed & throughput", color: theme.accent },
    { label: "\u2699  Manage Models", desc: "add providers and configure", color: theme.secondary },
    { label: "?  FAQ / Learn", desc: "how metrics work & resources", color: theme.primary },
    { label: "\u2715  Exit", desc: "quit the application", color: theme.error }
  ];
  useAppKeyboard((key) => {
    if (key.name === "up") {
      setCursor((i) => (i - 1 + ITEMS.length) % ITEMS.length);
    } else if (key.name === "down") {
      setCursor((i) => (i + 1) % ITEMS.length);
    } else if (key.name === "enter" || key.name === "return") {
      if (cursor === 0)
        navigate("model-select");
      else if (cursor === 1)
        navigate("model-menu");
      else if (cursor === 2)
        navigate("faq");
      else if (cursor === 3)
        renderer.destroy();
    }
  });
  return /* @__PURE__ */ jsxDEV7("box", {
    flexDirection: "column",
    flexGrow: 1,
    alignItems: "center",
    justifyContent: "center",
    children: [
      /* @__PURE__ */ jsxDEV7("box", {
        flexDirection: "column",
        alignItems: "center",
        marginBottom: 2,
        children: [
          /* @__PURE__ */ jsxDEV7("ascii-font", {
            text: "AI-SPEEDOMETER",
            font: "tiny",
            color: theme.primary
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV7("text", {
            fg: theme.dim,
            children: [
              "v",
              package_default.version
            ]
          }, undefined, true, undefined, this)
        ]
      }, undefined, true, undefined, this),
      /* @__PURE__ */ jsxDEV7("box", {
        flexDirection: "column",
        border: true,
        borderStyle: "rounded",
        borderColor: theme.border,
        backgroundColor: theme.background,
        width: 48,
        children: ITEMS.map((item, i) => {
          const active = i === cursor;
          return /* @__PURE__ */ jsxDEV7("box", {
            flexDirection: "row",
            alignItems: "center",
            backgroundColor: active ? theme.border : "transparent",
            paddingLeft: 2,
            paddingRight: 2,
            paddingTop: 1,
            paddingBottom: 1,
            children: [
              /* @__PURE__ */ jsxDEV7("box", {
                flexDirection: "column",
                flexGrow: 1,
                children: [
                  /* @__PURE__ */ jsxDEV7("text", {
                    fg: active ? item.color : theme.dim,
                    children: item.label
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV7("text", {
                    fg: active ? theme.dim : theme.border,
                    children: item.desc
                  }, undefined, false, undefined, this)
                ]
              }, undefined, true, undefined, this),
              active && /* @__PURE__ */ jsxDEV7("text", {
                fg: item.color,
                children: "\u203A"
              }, undefined, false, undefined, this)
            ]
          }, i, true, undefined, this);
        })
      }, undefined, false, undefined, this)
    ]
  }, undefined, true, undefined, this);
}
var init_MainMenuScreen = __esm(() => {
  init_useAppKeyboard();
  init_AppContext();
  init_ThemeContext();
  init_package();
});

// src/tui/components/MenuList.tsx
import { useState as useState5 } from "react";
import { jsxDEV as jsxDEV8 } from "@opentui/react/jsx-dev-runtime";
function MenuList({ items, selectedIndex: initialIndex = 0, onSelect, onNavigate }) {
  const theme = useTheme();
  const [cursor, setCursor] = useState5(initialIndex);
  useAppKeyboard((key) => {
    if (key.name === "up") {
      const next = (cursor - 1 + items.length) % items.length;
      setCursor(next);
      onNavigate?.(next);
    } else if (key.name === "down") {
      const next = (cursor + 1) % items.length;
      setCursor(next);
      onNavigate?.(next);
    } else if (key.name === "enter" || key.name === "return") {
      onSelect(cursor);
    }
  });
  return /* @__PURE__ */ jsxDEV8("box", {
    flexDirection: "column",
    children: items.map((item, i) => {
      const isSelected = i === cursor;
      return /* @__PURE__ */ jsxDEV8("box", {
        flexDirection: "row",
        alignItems: "center",
        backgroundColor: isSelected ? theme.border : "transparent",
        paddingLeft: 1,
        paddingRight: 1,
        children: [
          /* @__PURE__ */ jsxDEV8("box", {
            flexDirection: "column",
            flexGrow: 1,
            children: [
              /* @__PURE__ */ jsxDEV8("text", {
                fg: isSelected ? theme.text : theme.dim,
                children: item.label
              }, undefined, false, undefined, this),
              item.description ? /* @__PURE__ */ jsxDEV8("text", {
                fg: isSelected ? theme.dim : theme.border,
                children: item.description
              }, undefined, false, undefined, this) : null
            ]
          }, undefined, true, undefined, this),
          isSelected && /* @__PURE__ */ jsxDEV8("text", {
            fg: theme.primary,
            children: "\u203A"
          }, undefined, false, undefined, this)
        ]
      }, i, true, undefined, this);
    })
  }, undefined, false, undefined, this);
}
var init_MenuList = __esm(() => {
  init_useAppKeyboard();
  init_ThemeContext();
});

// src/tui/screens/ModelMenuScreen.tsx
import { jsxDEV as jsxDEV9 } from "@opentui/react/jsx-dev-runtime";
function ModelMenuScreen() {
  const navigate = useNavigate();
  const theme = useTheme();
  useAppKeyboard((key) => {
    if (key.name === "escape" || key.name === "q") {
      navigate("main-menu");
    }
  });
  function handleSelect(index) {
    if (index === 0)
      navigate("add-verified");
    else if (index === 1)
      navigate("add-custom");
    else if (index === 2)
      navigate("add-models");
    else if (index === 3)
      navigate("list-providers");
    else if (index === 4)
      navigate("main-menu");
  }
  return /* @__PURE__ */ jsxDEV9("box", {
    flexDirection: "column",
    flexGrow: 1,
    padding: 1,
    children: [
      /* @__PURE__ */ jsxDEV9("text", {
        fg: theme.primary,
        children: "Model Management"
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV9("box", {
        marginTop: 1,
        children: /* @__PURE__ */ jsxDEV9(MenuList, {
          items: ITEMS,
          onSelect: handleSelect
        }, undefined, false, undefined, this)
      }, undefined, false, undefined, this)
    ]
  }, undefined, true, undefined, this);
}
var ITEMS;
var init_ModelMenuScreen = __esm(() => {
  init_useAppKeyboard();
  init_AppContext();
  init_ThemeContext();
  init_MenuList();
  ITEMS = [
    { label: "Add Verified Provider" },
    { label: "Add Custom Provider" },
    { label: "Add Models to Provider" },
    { label: "List Providers" },
    { label: "Back" }
  ];
});

// src/tui/hooks/usePaste.ts
import { useEffect as useEffect3, useRef as useRef2 } from "react";
import { useRenderer as useRenderer2 } from "@opentui/react";
function usePaste(onPaste) {
  const renderer = useRenderer2();
  const callbackRef = useRef2(onPaste);
  callbackRef.current = onPaste;
  useEffect3(() => {
    const handler = (event) => callbackRef.current(event.text);
    renderer.keyInput.on("paste", handler);
    return () => {
      renderer.keyInput.off("paste", handler);
    };
  }, [renderer]);
}
var init_usePaste = () => {};

// src/tui/screens/ModelSelectScreen.tsx
import { useState as useState6, useEffect as useEffect4, useCallback as useCallback2, useRef as useRef3 } from "react";
import { useTerminalDimensions } from "@opentui/react";
import { jsxDEV as jsxDEV10 } from "@opentui/react/jsx-dev-runtime";
function ModelSelectScreen() {
  const { state, dispatch } = useAppContext();
  const navigate = useNavigate();
  const theme = useTheme();
  const { height, width } = useTerminalDimensions();
  const [searchQuery, setSearchQuery] = useState6("");
  const [cursor, setCursor] = useState6(0);
  const [selected, setSelected] = useState6(new Set);
  const [allModels, setAllModels] = useState6([]);
  const [recentKeys, setRecentKeys] = useState6(new Set);
  const [debouncedQuery, setDebouncedQuery] = useState6("");
  const scrollboxRef = useRef3(null);
  const PAGE_SIZE = Math.max(3, height - 14);
  const CARD_W = Math.min(60, width - 4);
  useEffect4(() => {
    const providers = state.config?.providers ?? [];
    const models = [];
    for (const provider of providers) {
      for (const m of provider.models) {
        models.push({ id: m.id, name: m.name || m.id, providerName: provider.name, providerType: provider.type, providerId: provider.id, providerConfig: { baseUrl: provider.baseUrl, apiKey: provider.apiKey }, key: `${provider.id}::${m.id}` });
      }
    }
    setAllModels(models);
    async function loadRecents() {
      try {
        const { getRecentModels: getRecentModels2, cleanupRecentModelsFromConfig: cleanupRecentModelsFromConfig2 } = await Promise.resolve().then(() => (init_ai_config(), exports_ai_config));
        await cleanupRecentModelsFromConfig2();
        const recents = await getRecentModels2();
        const keys = new Set;
        for (const r of recents) {
          keys.add(r.modelId);
          if (r.providerName)
            keys.add(`${r.modelId}|${r.providerName}`);
        }
        setRecentKeys(keys);
      } catch {
        setRecentKeys(new Set);
      }
    }
    loadRecents();
  }, [state.config]);
  useEffect4(() => {
    const t = setTimeout(() => setDebouncedQuery(searchQuery), DEBOUNCE_MS);
    return () => clearTimeout(t);
  }, [searchQuery]);
  useEffect4(() => {
    setCursor(0);
  }, [debouncedQuery]);
  function isRecent(m) {
    return recentKeys.has(m.id) || recentKeys.has(`${m.id}|${m.providerName}`);
  }
  const orderedModels = (() => {
    if (!debouncedQuery) {
      const r = allModels.filter((m) => isRecent(m));
      return [...r, ...allModels.filter((m) => !isRecent(m))];
    }
    const words = debouncedQuery.toLowerCase().split(/\s+/).filter((w) => w.length > 0);
    return allModels.filter((m) => {
      const h = `${m.name} ${m.providerName} ${m.providerId} ${m.id}`.toLowerCase();
      return words.every((w) => h.includes(w));
    });
  })();
  const recentCount = !debouncedQuery ? orderedModels.filter((m) => isRecent(m)).length : 0;
  const allRows = (() => {
    const rows = [];
    let modelIdx = 0;
    for (let i = 0;i < orderedModels.length; i++) {
      const m = orderedModels[i];
      const mIsRecent = isRecent(m);
      const prevIsRecent = i > 0 ? isRecent(orderedModels[i - 1]) : false;
      if (!debouncedQuery && recentCount > 0) {
        if (i === 0 && mIsRecent)
          rows.push({ kind: "separator", label: "\u2500\u2500 recent \u2500\u2500" });
        else if (!mIsRecent && prevIsRecent)
          rows.push({ kind: "separator", label: "\u2500\u2500 all models \u2500\u2500" });
      }
      rows.push({ kind: "model", model: m, idx: modelIdx++ });
    }
    return rows;
  })();
  const allModelRows = allRows.filter((r) => r.kind === "model");
  const cursorModel = allModelRows[cursor]?.model;
  useEffect4(() => {
    const sb = scrollboxRef.current;
    if (!sb)
      return;
    const top = sb.scrollTop;
    const visible = PAGE_SIZE;
    if (cursor < top)
      sb.scrollTo(cursor);
    else if (cursor >= top + visible)
      sb.scrollTo(cursor - visible + 1);
  }, [cursor, PAGE_SIZE]);
  const launchBench = useCallback2((models) => {
    dispatch({ type: "BENCH_START", models });
    navigate("benchmark");
  }, [dispatch, navigate]);
  usePaste((text) => {
    setSearchQuery((q) => q + text.replace(/[\r\n]/g, ""));
  });
  useAppKeyboard((key) => {
    if (key.name === "escape") {
      navigate("main-menu");
      return;
    }
    if (key.name === "up") {
      setCursor((c) => Math.max(0, c - 1));
      return;
    }
    if (key.name === "down") {
      setCursor((c) => Math.min(allModelRows.length - 1, c + 1));
      return;
    }
    if (key.name === "pageup") {
      setCursor((c) => Math.max(0, c - PAGE_SIZE));
      return;
    }
    if (key.name === "pagedown") {
      setCursor((c) => Math.min(allModelRows.length - 1, c + PAGE_SIZE));
      return;
    }
    if (key.name === "tab") {
      if (!cursorModel)
        return;
      setSelected((prev) => {
        const next = new Set(prev);
        if (next.has(cursorModel.key))
          next.delete(cursorModel.key);
        else
          next.add(cursorModel.key);
        return next;
      });
      return;
    }
    if (key.name === "return" || key.name === "enter") {
      if (selected.size > 0)
        launchBench(allModels.filter((m) => selected.has(m.key)));
      else if (cursorModel)
        launchBench([cursorModel]);
      return;
    }
    if (!searchQuery && key.sequence === "A") {
      setSelected(new Set(orderedModels.map((m) => m.key)));
      return;
    }
    if (!searchQuery && key.sequence === "N") {
      setSelected(new Set);
      return;
    }
    if (!searchQuery && recentCount > 0 && key.sequence === "R") {
      launchBench(orderedModels.slice(0, recentCount));
      return;
    }
    if (key.name === "backspace" || key.name === "delete") {
      setSearchQuery((q) => q.slice(0, -1));
      return;
    }
    if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta && key.sequence >= " ")
      setSearchQuery((q) => q + key.sequence);
  });
  if (state.isLoadingConfig) {
    return /* @__PURE__ */ jsxDEV10("box", {
      flexDirection: "column",
      flexGrow: 1,
      alignItems: "center",
      justifyContent: "center",
      children: /* @__PURE__ */ jsxDEV10("text", {
        fg: theme.dim,
        children: "Loading config..."
      }, undefined, false, undefined, this)
    }, undefined, false, undefined, this);
  }
  const nameW = Math.floor((CARD_W - 10) / 2);
  const provW = CARD_W - nameW - 10;
  return /* @__PURE__ */ jsxDEV10("box", {
    flexDirection: "column",
    flexGrow: 1,
    alignItems: "center",
    justifyContent: "center",
    children: /* @__PURE__ */ jsxDEV10("box", {
      flexDirection: "column",
      border: true,
      borderStyle: "rounded",
      borderColor: theme.border,
      backgroundColor: theme.background,
      width: CARD_W,
      children: [
        /* @__PURE__ */ jsxDEV10("box", {
          flexDirection: "row",
          paddingLeft: 2,
          paddingRight: 2,
          paddingTop: 1,
          paddingBottom: 1,
          children: [
            /* @__PURE__ */ jsxDEV10("text", {
              fg: theme.accent,
              children: "Search: "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV10("text", {
              fg: theme.text,
              children: [
                searchQuery,
                "_"
              ]
            }, undefined, true, undefined, this)
          ]
        }, undefined, true, undefined, this),
        /* @__PURE__ */ jsxDEV10("box", {
          height: 1,
          backgroundColor: theme.border
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV10("scrollbox", {
          ref: scrollboxRef,
          height: PAGE_SIZE,
          style: { scrollbarOptions: { showArrows: true, trackOptions: { foregroundColor: theme.primary, backgroundColor: theme.border } } },
          children: [
            allRows.length === 0 && /* @__PURE__ */ jsxDEV10("box", {
              height: 1,
              paddingLeft: 2,
              children: /* @__PURE__ */ jsxDEV10("text", {
                fg: theme.dim,
                children: "No models found"
              }, undefined, false, undefined, this)
            }, undefined, false, undefined, this),
            (() => {
              let modelCursor = 0;
              return allRows.map((row, i) => {
                if (row.kind === "separator")
                  return /* @__PURE__ */ jsxDEV10("box", {
                    height: 1,
                    paddingLeft: 2,
                    children: /* @__PURE__ */ jsxDEV10("text", {
                      fg: theme.dim,
                      children: row.label
                    }, undefined, false, undefined, this)
                  }, `sep-${i}`, false, undefined, this);
                const localCursor = modelCursor++;
                const isActive = localCursor === cursor;
                const isSel = selected.has(row.model.key);
                let nameFg = theme.dim;
                if (isActive && isSel)
                  nameFg = theme.accent;
                else if (isActive)
                  nameFg = theme.text;
                else if (isSel)
                  nameFg = theme.success;
                return /* @__PURE__ */ jsxDEV10("box", {
                  height: 1,
                  width: "100%",
                  flexDirection: "row",
                  backgroundColor: isActive ? theme.border : "transparent",
                  children: [
                    /* @__PURE__ */ jsxDEV10("text", {
                      fg: theme.dim,
                      width: 2,
                      children: " "
                    }, undefined, false, undefined, this),
                    /* @__PURE__ */ jsxDEV10("text", {
                      fg: nameFg,
                      width: nameW,
                      children: row.model.name
                    }, undefined, false, undefined, this),
                    /* @__PURE__ */ jsxDEV10("text", {
                      fg: isActive ? theme.primary : theme.dim,
                      width: provW,
                      children: row.model.providerName
                    }, undefined, false, undefined, this),
                    /* @__PURE__ */ jsxDEV10("text", {
                      fg: theme.success,
                      width: 2,
                      children: isSel ? "\u2713" : " "
                    }, undefined, false, undefined, this),
                    /* @__PURE__ */ jsxDEV10("text", {
                      fg: theme.accent,
                      width: 2,
                      children: isActive ? "\u203A" : " "
                    }, undefined, false, undefined, this)
                  ]
                }, row.model.key, true, undefined, this);
              });
            })()
          ]
        }, undefined, true, undefined, this),
        /* @__PURE__ */ jsxDEV10("box", {
          height: 1,
          backgroundColor: theme.border
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV10("box", {
          flexDirection: "row",
          paddingLeft: 2,
          paddingRight: 2,
          paddingTop: 1,
          paddingBottom: 1,
          children: [
            /* @__PURE__ */ jsxDEV10("text", {
              fg: theme.secondary,
              children: [
                "Selected: ",
                selected.size,
                " model",
                selected.size !== 1 ? "s" : ""
              ]
            }, undefined, true, undefined, this),
            recentCount > 0 && /* @__PURE__ */ jsxDEV10("text", {
              fg: theme.dim,
              children: [
                "   [R] recent (",
                recentCount,
                ")"
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV10("text", {
              fg: theme.dim,
              children: "   [\u2191\u2193/PgUp/PgDn/wheel] scroll"
            }, undefined, false, undefined, this)
          ]
        }, undefined, true, undefined, this)
      ]
    }, undefined, true, undefined, this)
  }, undefined, false, undefined, this);
}
var DEBOUNCE_MS = 50;
var init_ModelSelectScreen = __esm(() => {
  init_useAppKeyboard();
  init_AppContext();
  init_ThemeContext();
  init_usePaste();
});

// src/tui/components/BarChart.tsx
import { jsxDEV as jsxDEV11 } from "@opentui/react/jsx-dev-runtime";
function BarChart({ value, max, width, color, inverted = false }) {
  const normalizedValue = inverted && max > 0 ? Math.max(0, max - value) : value;
  const filled = max === 0 ? 0 : Math.round(normalizedValue / max * width);
  const empty = width - filled;
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
  return /* @__PURE__ */ jsxDEV11("text", {
    fg: color,
    children: bar
  }, undefined, false, undefined, this);
}
var init_BarChart = () => {};

// src/tui/components/GlowBar.tsx
import { useState as useState7, useEffect as useEffect5 } from "react";
import { engine, Timeline } from "@opentui/core";
import { jsxDEV as jsxDEV12 } from "@opentui/react/jsx-dev-runtime";
function blendHex(hex, toward, t) {
  const parse3 = (h) => [
    parseInt(h.slice(1, 3), 16),
    parseInt(h.slice(3, 5), 16),
    parseInt(h.slice(5, 7), 16)
  ];
  const [r1, g1, b1] = parse3(hex.length === 7 ? hex : "#ffffff");
  const [r2, g2, b2] = parse3(toward.length === 7 ? toward : "#ffffff");
  const r = Math.round(r1 + (r2 - r1) * t);
  const g = Math.round(g1 + (g2 - g1) * t);
  const b = Math.round(b1 + (b2 - b1) * t);
  return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
}
function GlowBar({ done, total, running }) {
  const theme = useTheme();
  const [pos, setPos] = useState7(0);
  useEffect5(() => {
    const tl = new Timeline({ loop: true, duration: 2200 });
    const target = { pos: 0 };
    tl.add(target, { pos: 1, duration: 2200, ease: "linear", onUpdate: (anim) => {
      setPos(anim.targets[0].pos);
    } }, 0);
    tl.play();
    engine.register(tl);
    return () => {
      tl.pause();
      engine.unregister(tl);
    };
  }, []);
  const filled = Math.round(done / (total || 1) * BAR_W);
  const barChars = Array.from({ length: BAR_W }, (_, i) => {
    if (i >= filled)
      return { ch: "\u2591", fg: theme.border };
    const phase = pos - i / BAR_W;
    const intensity = Math.sin(Math.PI * phase * 1.5);
    const fg = intensity >= 0 ? blendHex(theme.accent, "#ffffff", intensity * 0.55) : blendHex(theme.accent, theme.background, Math.abs(intensity) * 0.5);
    return { ch: "\u2588", fg };
  });
  return /* @__PURE__ */ jsxDEV12("box", {
    height: 1,
    flexDirection: "row",
    paddingLeft: 2,
    children: [
      /* @__PURE__ */ jsxDEV12("text", {
        fg: theme.dim,
        children: "Benchmarking  "
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV12("text", {
        fg: theme.accent,
        children: [
          done,
          "/",
          total,
          "  "
        ]
      }, undefined, true, undefined, this),
      barChars.map((b, i) => /* @__PURE__ */ jsxDEV12("text", {
        fg: b.fg,
        children: b.ch
      }, i, false, undefined, this)),
      /* @__PURE__ */ jsxDEV12("text", {
        fg: theme.warning,
        children: [
          "  ",
          running,
          " running..."
        ]
      }, undefined, true, undefined, this)
    ]
  }, undefined, true, undefined, this);
}
var BAR_W = 25;
var init_GlowBar = __esm(() => {
  init_ThemeContext();
});

// src/tui/components/ResultsTable.tsx
import { jsxDEV as jsxDEV13 } from "@opentui/react/jsx-dev-runtime";
function lpad(s, w) {
  return s.length >= w ? s.slice(0, w) : " ".repeat(w - s.length) + s;
}
function rpad(s, w) {
  return s.length >= w ? s.slice(0, w) : s + " ".repeat(w - s.length);
}
function trunc(s, w) {
  return s.length > w ? s.slice(0, w - 1) + "\u2026" : s;
}
function ResultsTable({ results, pendingCount }) {
  const theme = useTheme();
  const sorted = [...results].sort((a, b) => b.tokensPerSecond - a.tokensPerSecond);
  const C = { rank: 4, model: 16, prov: 10, time: 8, ttft: 7, tps: 9, f1000: 8, out: 6, inp: 6, tot: 6 };
  const totalW = C.rank + C.model + C.prov + C.time + C.ttft + C.tps + C.f1000 + C.out + C.inp + C.tot + 10;
  const sep = "\u2500".repeat(totalW);
  const maxTps = Math.max(...results.map((r) => r.tokensPerSecond), 0);
  const minTtft = Math.min(...results.map((r) => r.timeToFirstToken), Infinity);
  const validF1000s = results.map((r) => r.f1000).filter((f) => f !== Infinity);
  const minF1000 = validF1000s.length > 0 ? Math.min(...validF1000s) : Infinity;
  function row(rank, model, prov, time, ttft, tps, f1000, out, inp, tot) {
    return lpad(rank, C.rank) + " \u2502 " + rpad(model, C.model) + " \u2502 " + rpad(prov, C.prov) + " \u2502 " + lpad(time, C.time) + " \u2502 " + lpad(ttft, C.ttft) + " \u2502 " + lpad(tps, C.tps) + " \u2502 " + lpad(f1000, C.f1000) + " \u2502 " + lpad(out, C.out) + " \u2502 " + lpad(inp, C.inp) + " \u2502 " + lpad(tot, C.tot);
  }
  const header = row("#", "Model", "Provider", "Time(s)", "TTFT(s)", "Tok/s", "F1000(h)", "Out", "In", "Total");
  return /* @__PURE__ */ jsxDEV13("box", {
    flexDirection: "column",
    paddingLeft: 1,
    paddingRight: 1,
    children: [
      /* @__PURE__ */ jsxDEV13("box", {
        height: 1,
        children: /* @__PURE__ */ jsxDEV13("text", {
          fg: theme.accent,
          children: header
        }, undefined, false, undefined, this)
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV13("box", {
        height: 1,
        children: /* @__PURE__ */ jsxDEV13("text", {
          fg: theme.border,
          children: sep
        }, undefined, false, undefined, this)
      }, undefined, false, undefined, this),
      sorted.map((r, i) => {
        const rank = `${i + 1}`;
        const timeSec = (r.totalTime / 1000).toFixed(1);
        const ttftSec = (r.timeToFirstToken / 1000).toFixed(2);
        const tps = r.tokensPerSecond.toFixed(0);
        const f1000Val = r.f1000 === Infinity ? "\u221E" : r.f1000.toFixed(2);
        const outTok = r.tokenCount.toString() + (r.usedEstimateForOutput ? "*" : "");
        const inTok = r.promptTokens.toString() + (r.usedEstimateForInput ? "*" : "");
        const totTok = r.totalTokens.toString() + (r.usedEstimateForOutput || r.usedEstimateForInput ? "*" : "");
        const hasEst = r.usedEstimateForOutput || r.usedEstimateForInput;
        const isBestTps = r.tokensPerSecond === maxTps && maxTps > 0;
        const isBestTtft = r.timeToFirstToken === minTtft;
        const isBestF1000 = r.f1000 === minF1000 && r.f1000 !== Infinity;
        return /* @__PURE__ */ jsxDEV13("box", {
          height: 1,
          flexDirection: "row",
          children: [
            /* @__PURE__ */ jsxDEV13("text", {
              fg: theme.dim,
              children: [
                lpad(rank, C.rank),
                " \u2502 "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV13("text", {
              fg: theme.text,
              children: [
                rpad(trunc(r.model, C.model), C.model),
                " \u2502 "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV13("text", {
              fg: theme.dim,
              children: [
                rpad(trunc(r.provider, C.prov), C.prov),
                " \u2502 "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV13("text", {
              fg: theme.dim,
              children: [
                lpad(timeSec, C.time),
                " \u2502 "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV13("text", {
              fg: isBestTtft ? theme.success : theme.dim,
              children: [
                lpad(ttftSec, C.ttft),
                " \u2502 "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV13("text", {
              fg: isBestTps ? theme.success : theme.dim,
              children: [
                lpad(tps, C.tps),
                " \u2502 "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV13("text", {
              fg: isBestF1000 ? theme.success : theme.dim,
              children: [
                lpad(f1000Val, C.f1000),
                " \u2502 "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV13("text", {
              fg: theme.dim,
              children: [
                lpad(outTok, C.out),
                " \u2502 "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV13("text", {
              fg: theme.dim,
              children: [
                lpad(inTok, C.inp),
                " \u2502 "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV13("text", {
              fg: theme.dim,
              children: lpad(totTok, C.tot)
            }, undefined, false, undefined, this),
            hasEst && /* @__PURE__ */ jsxDEV13("text", {
              fg: theme.warning,
              children: " [est]"
            }, undefined, false, undefined, this)
          ]
        }, `${r.model}-${r.provider}-${i}`, true, undefined, this);
      }),
      /* @__PURE__ */ jsxDEV13("box", {
        height: 1,
        children: /* @__PURE__ */ jsxDEV13("text", {
          fg: theme.border,
          children: sep
        }, undefined, false, undefined, this)
      }, undefined, false, undefined, this),
      pendingCount > 0 && /* @__PURE__ */ jsxDEV13("box", {
        height: 1,
        children: /* @__PURE__ */ jsxDEV13("text", {
          fg: theme.dim,
          children: [
            "  Waiting for ",
            pendingCount,
            " more result",
            pendingCount !== 1 ? "s" : "",
            "..."
          ]
        }, undefined, true, undefined, this)
      }, undefined, false, undefined, this)
    ]
  }, undefined, true, undefined, this);
}
var init_ResultsTable = __esm(() => {
  init_ThemeContext();
});

// src/tui/screens/BenchmarkScreen.tsx
import { useState as useState8, useEffect as useEffect6, useRef as useRef4, useMemo } from "react";
import { jsxDEV as jsxDEV14 } from "@opentui/react/jsx-dev-runtime";
function rankBadge(rank) {
  if (rank === 1)
    return "1st";
  if (rank === 2)
    return "2nd";
  if (rank === 3)
    return "3rd";
  return `${rank}th`;
}
function BenchmarkScreen() {
  const { state, dispatch } = useAppContext();
  const navigate = useNavigate();
  const theme = useTheme();
  const [modelStates, setModelStates] = useState8([]);
  const [spinnerFrame, setSpinnerFrame] = useState8(0);
  const [allDone, setAllDone] = useState8(false);
  const [runKey, setRunKey] = useState8(0);
  const spinnerRef = useRef4(null);
  useEffect6(() => {
    const models = state.benchResults.map((r) => r.model);
    if (models.length === 0)
      return;
    setModelStates(models.map((m) => ({ model: m, status: "pending" })));
    setAllDone(false);
    spinnerRef.current = setInterval(() => setSpinnerFrame((f) => f + 1), 80);
    setModelStates((prev) => prev.map((s) => ({ ...s, status: "running", startedAt: Date.now() })));
    async function runAll() {
      const { benchmarkSingleModelRest: benchmarkSingleModelRest2 } = await Promise.resolve().then(() => (init_benchmark(), exports_benchmark));
      const logEnabled = state.logMode && !!state.runId;
      const { createBenchLogger: createBenchLogger2 } = logEnabled ? await Promise.resolve().then(() => (init_logger(), exports_logger)) : { createBenchLogger: null };
      const promises = models.map(async (model) => {
        const logger = logEnabled && createBenchLogger2 ? await createBenchLogger2(state.runId) : undefined;
        try {
          const result = await benchmarkSingleModelRest2(model, logger);
          if (!result.success) {
            const errMsg = result.error ?? "Request failed";
            setModelStates((prev) => prev.map((s) => s.model.id === model.id && s.model.providerId === model.providerId ? { ...s, status: "error", error: errMsg } : s));
            dispatch({ type: "BENCH_MODEL_ERROR", modelId: model.id, error: errMsg });
          } else {
            setModelStates((prev) => prev.map((s) => s.model.id === model.id && s.model.providerId === model.providerId ? { ...s, status: "done", result } : s));
            dispatch({ type: "BENCH_MODEL_DONE", modelId: model.id, result });
          }
        } catch (err) {
          const errMsg = err instanceof Error ? err.message : String(err);
          setModelStates((prev) => prev.map((s) => s.model.id === model.id && s.model.providerId === model.providerId ? { ...s, status: "error", error: errMsg } : s));
          dispatch({ type: "BENCH_MODEL_ERROR", modelId: model.id, error: errMsg });
        }
      });
      await Promise.allSettled(promises);
      if (spinnerRef.current) {
        clearInterval(spinnerRef.current);
        spinnerRef.current = null;
      }
      setAllDone(true);
      try {
        const { addToRecentModels: addToRecentModels2 } = await Promise.resolve().then(() => (init_ai_config(), exports_ai_config));
        await addToRecentModels2(models.map((m) => ({ modelId: m.id, modelName: m.name, providerName: m.providerName })));
      } catch {}
    }
    runAll();
    return () => {
      if (spinnerRef.current) {
        clearInterval(spinnerRef.current);
        spinnerRef.current = null;
      }
    };
  }, [runKey]);
  const rerun = () => {
    if (spinnerRef.current) {
      clearInterval(spinnerRef.current);
      spinnerRef.current = null;
    }
    setRunKey((k) => k + 1);
  };
  const done = modelStates.filter((m) => m.status === "done");
  const running = modelStates.filter((m) => m.status === "running");
  const pending = modelStates.filter((m) => m.status === "pending");
  const errors = modelStates.filter((m) => m.status === "error");
  const maxTps = Math.max(...done.map((m) => m.result?.tokensPerSecond ?? 0), 1);
  const maxTtft = Math.max(...done.map((m) => (m.result?.timeToFirstToken ?? 0) / 1000), 1);
  const tpsRanked = done.slice().sort((a, b) => (b.result?.tokensPerSecond ?? 0) - (a.result?.tokensPerSecond ?? 0));
  const ttftRanked = done.slice().sort((a, b) => (a.result?.timeToFirstToken ?? 0) - (b.result?.timeToFirstToken ?? 0));
  const f1000Ranked = done.slice().sort((a, b) => (a.result?.f1000 ?? Infinity) - (b.result?.f1000 ?? Infinity));
  const maxTtftForBar = Math.max(...done.map((m) => (m.result?.timeToFirstToken ?? 0) / 1000), 1);
  const maxF1000 = Math.max(...done.map((m) => m.result?.f1000 ?? 0), 1);
  const doneResults = tpsRanked.map((m) => m.result);
  const pendingCount = running.length + pending.length;
  const allRows = useMemo(() => {
    const rows = [];
    if (!allDone) {
      rows.push(/* @__PURE__ */ jsxDEV14(GlowBar, {
        done: done.length + errors.length,
        total: modelStates.length,
        running: running.length
      }, "progress-bar", false, undefined, this));
      for (const s of modelStates.filter((s2) => s2.status === "done" || s2.status === "error")) {
        if (s.status === "done") {
          rows.push(/* @__PURE__ */ jsxDEV14("box", {
            height: 1,
            flexDirection: "row",
            paddingLeft: 2,
            children: [
              /* @__PURE__ */ jsxDEV14("text", {
                fg: theme.success,
                children: "  \u2713  "
              }, undefined, false, undefined, this),
              /* @__PURE__ */ jsxDEV14("text", {
                fg: theme.text,
                children: [
                  s.model.name,
                  "  "
                ]
              }, undefined, true, undefined, this),
              /* @__PURE__ */ jsxDEV14("text", {
                fg: theme.accent,
                children: [
                  (s.result?.tokensPerSecond ?? 0).toFixed(1),
                  " tok/s  "
                ]
              }, undefined, true, undefined, this),
              /* @__PURE__ */ jsxDEV14("text", {
                fg: theme.secondary,
                children: [
                  ((s.result?.timeToFirstToken ?? 0) / 1000).toFixed(2),
                  "s TTFT"
                ]
              }, undefined, true, undefined, this)
            ]
          }, `prog-${s.model.id}-${s.model.providerId}`, true, undefined, this));
        } else {
          rows.push(/* @__PURE__ */ jsxDEV14("box", {
            height: 1,
            flexDirection: "row",
            paddingLeft: 2,
            children: [
              /* @__PURE__ */ jsxDEV14("text", {
                fg: theme.error,
                children: "  \u2717  "
              }, undefined, false, undefined, this),
              /* @__PURE__ */ jsxDEV14("text", {
                fg: theme.text,
                children: [
                  s.model.name,
                  "  "
                ]
              }, undefined, true, undefined, this),
              /* @__PURE__ */ jsxDEV14("text", {
                fg: theme.error,
                children: s.error ?? "error"
              }, undefined, false, undefined, this)
            ]
          }, `prog-${s.model.id}-${s.model.providerId}`, true, undefined, this));
        }
      }
      rows.push(/* @__PURE__ */ jsxDEV14("box", {
        height: 1
      }, "prog-spacer", false, undefined, this));
    }
    if (tpsRanked.length > 0) {
      rows.push(/* @__PURE__ */ jsxDEV14("box", {
        height: 1,
        backgroundColor: theme.border
      }, "div-tps", false, undefined, this));
      rows.push(/* @__PURE__ */ jsxDEV14("box", {
        height: 1,
        flexDirection: "row",
        paddingLeft: 1,
        children: /* @__PURE__ */ jsxDEV14("text", {
          fg: theme.primary,
          children: " TOKENS/SEC RANKING  (higher is better) "
        }, undefined, false, undefined, this)
      }, "hdr-tps", false, undefined, this));
      for (const [i, s] of tpsRanked.entries()) {
        const rank = i + 1;
        const rankFg = rank === 1 ? theme.accent : rank === 2 ? theme.secondary : theme.dim;
        const tps = s.result?.tokensPerSecond ?? 0;
        const timeSec = (s.result?.totalTime ?? 0) / 1000;
        const badge = rankBadge(rank).padStart(3);
        const modelCol = s.model.name.padEnd(18).slice(0, 18);
        const provCol = s.model.providerName.padEnd(12).slice(0, 12);
        rows.push(/* @__PURE__ */ jsxDEV14("box", {
          height: 1,
          flexDirection: "row",
          paddingLeft: 2,
          children: [
            /* @__PURE__ */ jsxDEV14("text", {
              fg: rankFg,
              children: [
                badge,
                "  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: " \u2502 "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.accent,
              children: [
                tps.toFixed(1).padStart(8),
                " tok/s  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: " \u2502 "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.secondary,
              children: [
                timeSec.toFixed(2).padStart(6),
                "s  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: " \u2502 "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.text,
              children: [
                modelCol,
                "  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: [
                provCol,
                "  \u2502  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14(BarChart, {
              value: tps,
              max: maxTps,
              width: BAR_W2,
              color: theme.accent
            }, undefined, false, undefined, this)
          ]
        }, `tps-${s.model.id}-${s.model.providerId}`, true, undefined, this));
      }
    }
    if (ttftRanked.length > 0) {
      rows.push(/* @__PURE__ */ jsxDEV14("box", {
        height: 1,
        backgroundColor: theme.border
      }, "div-ttft", false, undefined, this));
      rows.push(/* @__PURE__ */ jsxDEV14("box", {
        height: 1,
        flexDirection: "row",
        paddingLeft: 1,
        children: /* @__PURE__ */ jsxDEV14("text", {
          fg: theme.primary,
          children: " TIME TO FIRST TOKEN RANKING  (lower is better) "
        }, undefined, false, undefined, this)
      }, "hdr-ttft", false, undefined, this));
      for (const [i, s] of ttftRanked.entries()) {
        const rank = i + 1;
        const rankFg = rank === 1 ? theme.accent : rank === 2 ? theme.secondary : theme.dim;
        const ttft = (s.result?.timeToFirstToken ?? 0) / 1000;
        const tps = s.result?.tokensPerSecond ?? 0;
        const badge = rankBadge(rank).padStart(3);
        const modelCol = s.model.name.padEnd(18).slice(0, 18);
        const provCol = s.model.providerName.padEnd(12).slice(0, 12);
        rows.push(/* @__PURE__ */ jsxDEV14("box", {
          height: 1,
          flexDirection: "row",
          paddingLeft: 2,
          children: [
            /* @__PURE__ */ jsxDEV14("text", {
              fg: rankFg,
              children: [
                badge,
                "  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: " \u2502 "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.secondary,
              children: [
                ttft.toFixed(2).padStart(7),
                "s  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: " \u2502 "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.accent,
              children: [
                tps.toFixed(1).padStart(8),
                " tok/s  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: " \u2502 "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.text,
              children: [
                modelCol,
                "  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: [
                provCol,
                "  \u2502  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14(BarChart, {
              value: ttft,
              max: maxTtftForBar,
              width: BAR_W2,
              color: theme.secondary
            }, undefined, false, undefined, this)
          ]
        }, `ttft-${s.model.id}-${s.model.providerId}`, true, undefined, this));
      }
    }
    if (f1000Ranked.length > 0) {
      rows.push(/* @__PURE__ */ jsxDEV14("box", {
        height: 1,
        backgroundColor: theme.border
      }, "div-f1000", false, undefined, this));
      rows.push(/* @__PURE__ */ jsxDEV14("box", {
        height: 1,
        flexDirection: "row",
        paddingLeft: 1,
        children: /* @__PURE__ */ jsxDEV14("text", {
          fg: theme.primary,
          children: " F1000 RANKING - First to 1000 Requests  (lower is better) "
        }, undefined, false, undefined, this)
      }, "hdr-f1000", false, undefined, this));
      for (const [i, s] of f1000Ranked.entries()) {
        const rank = i + 1;
        const rankFg = rank === 1 ? theme.accent : rank === 2 ? theme.secondary : theme.dim;
        const f1000 = s.result?.f1000 ?? Infinity;
        const f1000Str = f1000 === Infinity ? "\u221E" : f1000.toFixed(2);
        const ttft = (s.result?.timeToFirstToken ?? 0) / 1000;
        const tps = s.result?.tokensPerSecond ?? 0;
        const badge = rankBadge(rank).padStart(3);
        const modelCol = s.model.name.padEnd(18).slice(0, 18);
        const provCol = s.model.providerName.padEnd(12).slice(0, 12);
        rows.push(/* @__PURE__ */ jsxDEV14("box", {
          height: 1,
          flexDirection: "row",
          paddingLeft: 2,
          children: [
            /* @__PURE__ */ jsxDEV14("text", {
              fg: rankFg,
              children: [
                badge,
                "  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: " \u2502 "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.primary,
              children: [
                f1000Str.padStart(7),
                "h  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: " \u2502 "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.secondary,
              children: [
                ttft.toFixed(2).padStart(5),
                "s  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: " \u2502 "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.accent,
              children: [
                tps.toFixed(0).padStart(5),
                " tok/s  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: " \u2502 "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.text,
              children: [
                modelCol,
                "  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("text", {
              fg: theme.dim,
              children: [
                provCol,
                "  \u2502  "
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14(BarChart, {
              value: f1000 === Infinity ? maxF1000 : f1000,
              max: maxF1000,
              width: BAR_W2,
              color: theme.primary
            }, undefined, false, undefined, this)
          ]
        }, `f1000-${s.model.id}-${s.model.providerId}`, true, undefined, this));
      }
    }
    if (allDone && errors.length > 0) {
      rows.push(/* @__PURE__ */ jsxDEV14("box", {
        height: 1,
        backgroundColor: theme.border
      }, "div-errors", false, undefined, this));
      rows.push(/* @__PURE__ */ jsxDEV14("box", {
        height: 1,
        flexDirection: "row",
        paddingLeft: 1,
        children: /* @__PURE__ */ jsxDEV14("text", {
          fg: theme.error,
          children: [
            " FAILED (",
            errors.length,
            ") "
          ]
        }, undefined, true, undefined, this)
      }, "hdr-errors", false, undefined, this));
      for (const s of errors) {
        rows.push(/* @__PURE__ */ jsxDEV14("box", {
          flexDirection: "column",
          paddingLeft: 2,
          paddingTop: 1,
          paddingBottom: 1,
          children: [
            /* @__PURE__ */ jsxDEV14("box", {
              height: 1,
              flexDirection: "row",
              children: [
                /* @__PURE__ */ jsxDEV14("text", {
                  fg: theme.error,
                  children: "\u2717  "
                }, undefined, false, undefined, this),
                /* @__PURE__ */ jsxDEV14("text", {
                  fg: theme.text,
                  children: [
                    s.model.name,
                    "  "
                  ]
                }, undefined, true, undefined, this),
                /* @__PURE__ */ jsxDEV14("text", {
                  fg: theme.dim,
                  children: [
                    "(",
                    s.model.providerName,
                    ")"
                  ]
                }, undefined, true, undefined, this)
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV14("box", {
              height: 1,
              paddingLeft: 3,
              children: /* @__PURE__ */ jsxDEV14("text", {
                fg: theme.error,
                children: s.error ?? "Unknown error"
              }, undefined, false, undefined, this)
            }, undefined, false, undefined, this)
          ]
        }, `err-${s.model.id}-${s.model.providerId}`, true, undefined, this));
      }
    }
    rows.push(/* @__PURE__ */ jsxDEV14("box", {
      height: 1,
      backgroundColor: theme.border
    }, "div-results", false, undefined, this));
    rows.push(/* @__PURE__ */ jsxDEV14("box", {
      height: 1,
      flexDirection: "row",
      paddingLeft: 1,
      children: /* @__PURE__ */ jsxDEV14("text", {
        fg: theme.primary,
        children: " RESULTS "
      }, undefined, false, undefined, this)
    }, "hdr-results", false, undefined, this));
    if (doneResults.length > 0) {
      rows.push(/* @__PURE__ */ jsxDEV14("box", {
        flexDirection: "column",
        children: /* @__PURE__ */ jsxDEV14(ResultsTable, {
          results: doneResults,
          pendingCount
        }, undefined, false, undefined, this)
      }, "results-table", false, undefined, this));
    } else {
      rows.push(/* @__PURE__ */ jsxDEV14("box", {
        paddingLeft: 2,
        paddingBottom: 1,
        children: /* @__PURE__ */ jsxDEV14("text", {
          fg: theme.dim,
          children: "No results yet..."
        }, undefined, false, undefined, this)
      }, "results-empty", false, undefined, this));
    }
    return rows;
  }, [modelStates, allDone, tpsRanked, ttftRanked, f1000Ranked, doneResults, pendingCount, maxTps, maxTtftForBar, maxF1000, theme]);
  useAppKeyboard((key) => {
    if (!allDone)
      return;
    if (key.shift && key.name === "r") {
      rerun();
      return;
    }
    if (key.name === "q" || key.name === "return" || key.name === "enter") {
      dispatch({ type: "BENCH_RESET" });
      navigate("main-menu");
    }
  });
  const statusLine = allDone ? /* @__PURE__ */ jsxDEV14("box", {
    flexDirection: "row",
    children: [
      /* @__PURE__ */ jsxDEV14("text", {
        fg: theme.success,
        children: "All done!  [R] rerun  [Enter]/[Q] return  [\u2191\u2193/PgUp/PgDn/wheel] scroll"
      }, undefined, false, undefined, this),
      state.logMode && state.logPath && /* @__PURE__ */ jsxDEV14("text", {
        fg: theme.dim,
        children: [
          "  log: ",
          state.logPath
        ]
      }, undefined, true, undefined, this)
    ]
  }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV14("box", {
    flexDirection: "row",
    children: [
      running.length > 0 && /* @__PURE__ */ jsxDEV14("text", {
        fg: theme.warning,
        children: [
          running.length,
          " running  "
        ]
      }, undefined, true, undefined, this),
      done.length > 0 && /* @__PURE__ */ jsxDEV14("text", {
        fg: theme.success,
        children: [
          done.length,
          " done  "
        ]
      }, undefined, true, undefined, this),
      errors.length > 0 && /* @__PURE__ */ jsxDEV14("text", {
        fg: theme.error,
        children: [
          errors.length,
          " errors  "
        ]
      }, undefined, true, undefined, this),
      /* @__PURE__ */ jsxDEV14("text", {
        fg: theme.dim,
        children: "[\u2191\u2193/wheel] scroll"
      }, undefined, false, undefined, this)
    ]
  }, undefined, true, undefined, this);
  return /* @__PURE__ */ jsxDEV14("box", {
    flexDirection: "column",
    flexGrow: 1,
    padding: 1,
    children: [
      /* @__PURE__ */ jsxDEV14("box", {
        alignItems: "center",
        justifyContent: "center",
        marginBottom: 1,
        children: /* @__PURE__ */ jsxDEV14("ascii-font", {
          text: "AI-SPEEDOMETER",
          font: "tiny",
          color: theme.primary
        }, undefined, false, undefined, this)
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV14("box", {
        flexDirection: "column",
        border: true,
        borderStyle: "rounded",
        borderColor: theme.border,
        backgroundColor: theme.background,
        flexGrow: 1,
        children: [
          /* @__PURE__ */ jsxDEV14("box", {
            height: 1,
            paddingLeft: 2,
            paddingRight: 2,
            flexDirection: "row",
            children: [
              /* @__PURE__ */ jsxDEV14("text", {
                fg: theme.accent,
                children: "LIVE BENCHMARK  "
              }, undefined, false, undefined, this),
              statusLine
            ]
          }, undefined, true, undefined, this),
          /* @__PURE__ */ jsxDEV14("box", {
            height: 1,
            backgroundColor: theme.border
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV14("scrollbox", {
            focused: true,
            flexGrow: 1,
            stickyScroll: true,
            stickyStart: "bottom",
            style: { scrollbarOptions: { showArrows: true, trackOptions: { foregroundColor: theme.primary, backgroundColor: theme.border } } },
            children: allRows
          }, undefined, false, undefined, this)
        ]
      }, undefined, true, undefined, this)
    ]
  }, undefined, true, undefined, this);
}
var BAR_W2 = 25;
var init_BenchmarkScreen = __esm(() => {
  init_useAppKeyboard();
  init_AppContext();
  init_ThemeContext();
  init_BarChart();
  init_GlowBar();
  init_ResultsTable();
});

// src/tui/screens/AddVerifiedScreen.tsx
import { useState as useState9, useEffect as useEffect7, useRef as useRef5 } from "react";
import { useTerminalDimensions as useTerminalDimensions2 } from "@opentui/react";
import { jsxDEV as jsxDEV15 } from "@opentui/react/jsx-dev-runtime";
function AddVerifiedScreen() {
  const { dispatch } = useAppContext();
  const navigate = useNavigate();
  const theme = useTheme();
  const { height, width } = useTerminalDimensions2();
  const PAGE_SIZE = Math.max(3, height - 16);
  const CARD_W = Math.min(62, width - 4);
  const [step, setStep] = useState9("browse");
  const [allProviders, setAllProviders] = useState9([]);
  const [filtered, setFiltered] = useState9([]);
  const [cursor, setCursor] = useState9(0);
  const [searchQuery, setSearchQuery] = useState9("");
  const scrollboxRef = useRef5(null);
  const [selectedProvider, setSelectedProvider] = useState9(null);
  const [apiKey, setApiKey] = useState9("");
  const [saving, setSaving] = useState9(false);
  const [saveError, setSaveError] = useState9("");
  const [saveSuccess, setSaveSuccess] = useState9(false);
  const [loadError, setLoadError] = useState9("");
  useEffect7(() => {
    async function load() {
      try {
        const { getAllProviders: getAllProviders2 } = await Promise.resolve().then(() => (init_models_dev(), exports_models_dev));
        const providers = await getAllProviders2();
        setAllProviders(providers);
        setFiltered(providers);
      } catch (e) {
        setLoadError(e instanceof Error ? e.message : String(e));
      }
    }
    load();
  }, []);
  useEffect7(() => {
    if (!searchQuery) {
      setFiltered(allProviders);
    } else {
      const q = searchQuery.toLowerCase();
      setFiltered(allProviders.filter((p) => p.name.toLowerCase().includes(q) || p.id.toLowerCase().includes(q)));
    }
    setCursor(0);
  }, [searchQuery, allProviders]);
  useEffect7(() => {
    const sb = scrollboxRef.current;
    if (!sb)
      return;
    const top = sb.scrollTop;
    const visible = PAGE_SIZE;
    if (cursor < top) {
      sb.scrollTo(cursor);
    } else if (cursor >= top + visible) {
      sb.scrollTo(cursor - visible + 1);
    }
  }, [cursor, PAGE_SIZE]);
  async function save() {
    if (!selectedProvider || !apiKey.trim())
      return;
    setSaving(true);
    setSaveError("");
    try {
      const { addApiKey: addApiKey2 } = await Promise.resolve().then(() => (init_opencode_integration(), exports_opencode_integration));
      const { addVerifiedProvider: addVerifiedProvider2 } = await Promise.resolve().then(() => (init_ai_config(), exports_ai_config));
      await addApiKey2(selectedProvider.id, apiKey.trim());
      await addVerifiedProvider2(selectedProvider.id, apiKey.trim());
      const { getAllAvailableProviders: getAllAvailableProviders2 } = await Promise.resolve().then(() => (init_opencode_integration(), exports_opencode_integration));
      const providers = await getAllAvailableProviders2(false);
      dispatch({ type: "SET_CONFIG", config: { providers } });
      setSaveSuccess(true);
    } catch (e) {
      setSaveError(e instanceof Error ? e.message : String(e));
    } finally {
      setSaving(false);
    }
  }
  usePaste((text) => {
    const clean = text.replace(/[\r\n]/g, "");
    if (step === "browse")
      setSearchQuery((q) => q + clean);
    else if (step === "confirm")
      setApiKey((k) => k + clean);
  });
  useAppKeyboard((key) => {
    if (step === "browse") {
      if (key.name === "escape" || key.sequence === "q" || key.sequence === "Q") {
        navigate("model-menu");
        return;
      }
      if (key.name === "up") {
        setCursor((c) => Math.max(0, c - 1));
        return;
      }
      if (key.name === "down") {
        setCursor((c) => Math.min(filtered.length - 1, c + 1));
        return;
      }
      if (key.name === "pageup") {
        setCursor((c) => Math.max(0, c - PAGE_SIZE));
        return;
      }
      if (key.name === "pagedown") {
        setCursor((c) => Math.min(filtered.length - 1, c + PAGE_SIZE));
        return;
      }
      if (key.name === "return" || key.name === "enter") {
        const prov = filtered[cursor];
        if (prov) {
          setSelectedProvider(prov);
          setApiKey("");
          setSaveError("");
          setSaveSuccess(false);
          setStep("confirm");
        }
        return;
      }
      if (key.name === "backspace" || key.name === "delete") {
        setSearchQuery((q) => q.slice(0, -1));
        return;
      }
      if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta && key.sequence >= " ") {
        setSearchQuery((q) => q + key.sequence);
      }
      return;
    }
    if (step === "confirm") {
      if (saveSuccess) {
        if (key.name === "return" || key.name === "enter")
          navigate("model-menu");
        return;
      }
      if (saving)
        return;
      if (key.name === "escape") {
        setStep("browse");
        return;
      }
      if (key.name === "return" || key.name === "enter") {
        save();
        return;
      }
      if (key.name === "backspace" || key.name === "delete") {
        setApiKey((k) => k.slice(0, -1));
        return;
      }
      if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta && key.sequence >= " ") {
        setApiKey((k) => k + key.sequence);
      }
    }
  });
  if (step === "confirm" && selectedProvider) {
    return /* @__PURE__ */ jsxDEV15("box", {
      flexDirection: "column",
      flexGrow: 1,
      alignItems: "center",
      justifyContent: "center",
      children: /* @__PURE__ */ jsxDEV15("box", {
        flexDirection: "column",
        border: true,
        borderStyle: "rounded",
        borderColor: theme.border,
        backgroundColor: theme.background,
        width: CARD_W,
        children: [
          /* @__PURE__ */ jsxDEV15("box", {
            height: 1,
            paddingLeft: 2,
            paddingTop: 1,
            children: /* @__PURE__ */ jsxDEV15("text", {
              fg: theme.accent,
              children: "Add Verified Provider"
            }, undefined, false, undefined, this)
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV15("box", {
            height: 1,
            backgroundColor: theme.border
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV15("box", {
            flexDirection: "column",
            paddingLeft: 2,
            paddingRight: 2,
            paddingTop: 1,
            paddingBottom: 1,
            children: [
              /* @__PURE__ */ jsxDEV15("box", {
                height: 1,
                flexDirection: "row",
                children: [
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.primary,
                    children: "Provider: "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.text,
                    children: selectedProvider.name
                  }, undefined, false, undefined, this)
                ]
              }, undefined, true, undefined, this),
              /* @__PURE__ */ jsxDEV15("box", {
                height: 1,
                flexDirection: "row",
                children: [
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.dim,
                    children: "Type:     "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.dim,
                    children: selectedProvider.type
                  }, undefined, false, undefined, this)
                ]
              }, undefined, true, undefined, this),
              /* @__PURE__ */ jsxDEV15("box", {
                height: 1,
                flexDirection: "row",
                children: [
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.dim,
                    children: "URL:      "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.dim,
                    children: selectedProvider.baseUrl
                  }, undefined, false, undefined, this)
                ]
              }, undefined, true, undefined, this),
              /* @__PURE__ */ jsxDEV15("box", {
                height: 1,
                flexDirection: "row",
                children: [
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.dim,
                    children: "Models:   "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.dim,
                    children: [
                      selectedProvider.models.length,
                      " available"
                    ]
                  }, undefined, true, undefined, this)
                ]
              }, undefined, true, undefined, this)
            ]
          }, undefined, true, undefined, this),
          /* @__PURE__ */ jsxDEV15("box", {
            height: 1,
            backgroundColor: theme.border
          }, undefined, false, undefined, this),
          saveSuccess ? /* @__PURE__ */ jsxDEV15("box", {
            flexDirection: "column",
            paddingLeft: 2,
            paddingRight: 2,
            paddingTop: 1,
            paddingBottom: 1,
            children: [
              /* @__PURE__ */ jsxDEV15("box", {
                height: 1,
                children: /* @__PURE__ */ jsxDEV15("text", {
                  fg: theme.success,
                  children: [
                    "Provider added!  ",
                    selectedProvider.models.length,
                    " models available"
                  ]
                }, undefined, true, undefined, this)
              }, undefined, false, undefined, this),
              /* @__PURE__ */ jsxDEV15("box", {
                height: 1,
                children: /* @__PURE__ */ jsxDEV15("text", {
                  fg: theme.dim,
                  children: "Press [Enter] to return"
                }, undefined, false, undefined, this)
              }, undefined, false, undefined, this)
            ]
          }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV15("box", {
            flexDirection: "column",
            paddingLeft: 2,
            paddingRight: 2,
            paddingTop: 1,
            paddingBottom: 1,
            children: [
              /* @__PURE__ */ jsxDEV15("box", {
                height: 1,
                flexDirection: "row",
                children: [
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.accent,
                    children: "API Key: "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.text,
                    children: [
                      apiKey,
                      "_"
                    ]
                  }, undefined, true, undefined, this)
                ]
              }, undefined, true, undefined, this),
              saveError ? /* @__PURE__ */ jsxDEV15("box", {
                height: 1,
                children: /* @__PURE__ */ jsxDEV15("text", {
                  fg: theme.error,
                  children: [
                    "Error: ",
                    saveError
                  ]
                }, undefined, true, undefined, this)
              }, undefined, false, undefined, this) : null,
              saving ? /* @__PURE__ */ jsxDEV15("box", {
                height: 1,
                children: /* @__PURE__ */ jsxDEV15("text", {
                  fg: theme.warning,
                  children: "Saving..."
                }, undefined, false, undefined, this)
              }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV15("box", {
                height: 1,
                children: /* @__PURE__ */ jsxDEV15("text", {
                  fg: theme.dim,
                  children: "[Enter] save   [Esc] back to list"
                }, undefined, false, undefined, this)
              }, undefined, false, undefined, this)
            ]
          }, undefined, true, undefined, this)
        ]
      }, undefined, true, undefined, this)
    }, undefined, false, undefined, this);
  }
  return /* @__PURE__ */ jsxDEV15("box", {
    flexDirection: "column",
    flexGrow: 1,
    alignItems: "center",
    justifyContent: "center",
    children: /* @__PURE__ */ jsxDEV15("box", {
      flexDirection: "column",
      border: true,
      borderStyle: "rounded",
      borderColor: theme.border,
      backgroundColor: theme.background,
      width: CARD_W,
      children: [
        /* @__PURE__ */ jsxDEV15("box", {
          flexDirection: "row",
          paddingLeft: 2,
          paddingRight: 2,
          paddingTop: 1,
          paddingBottom: 1,
          children: [
            /* @__PURE__ */ jsxDEV15("text", {
              fg: theme.accent,
              children: "Search: "
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV15("text", {
              fg: theme.text,
              children: [
                searchQuery,
                "_"
              ]
            }, undefined, true, undefined, this)
          ]
        }, undefined, true, undefined, this),
        /* @__PURE__ */ jsxDEV15("box", {
          height: 1,
          backgroundColor: theme.border
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV15("scrollbox", {
          ref: scrollboxRef,
          height: PAGE_SIZE,
          style: { scrollbarOptions: { showArrows: true, trackOptions: { foregroundColor: theme.primary, backgroundColor: theme.border } } },
          children: [
            loadError && /* @__PURE__ */ jsxDEV15("box", {
              height: 1,
              paddingLeft: 2,
              children: /* @__PURE__ */ jsxDEV15("text", {
                fg: theme.error,
                children: [
                  "Error loading providers: ",
                  loadError
                ]
              }, undefined, true, undefined, this)
            }, undefined, false, undefined, this),
            !loadError && filtered.length === 0 && /* @__PURE__ */ jsxDEV15("box", {
              height: 1,
              paddingLeft: 2,
              children: /* @__PURE__ */ jsxDEV15("text", {
                fg: theme.dim,
                children: allProviders.length === 0 ? "Loading..." : "No providers found"
              }, undefined, false, undefined, this)
            }, undefined, false, undefined, this),
            filtered.map((prov, i) => {
              const isActive = i === cursor;
              return /* @__PURE__ */ jsxDEV15("box", {
                height: 1,
                width: "100%",
                flexDirection: "row",
                backgroundColor: isActive ? theme.border : "transparent",
                children: [
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.dim,
                    width: 2,
                    children: " "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: isActive ? theme.text : theme.dim,
                    width: Math.floor((CARD_W - 10) / 2),
                    children: prov.name
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: isActive ? theme.primary : theme.border,
                    width: Math.floor((CARD_W - 10) / 2),
                    children: prov.type
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV15("text", {
                    fg: theme.accent,
                    width: 2,
                    children: isActive ? "\u203A" : " "
                  }, undefined, false, undefined, this)
                ]
              }, prov.id, true, undefined, this);
            })
          ]
        }, undefined, true, undefined, this),
        /* @__PURE__ */ jsxDEV15("box", {
          height: 1,
          backgroundColor: theme.border
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV15("box", {
          flexDirection: "row",
          paddingLeft: 2,
          paddingRight: 2,
          paddingTop: 1,
          paddingBottom: 1,
          children: [
            /* @__PURE__ */ jsxDEV15("text", {
              fg: theme.dim,
              children: [
                filtered.length,
                " providers"
              ]
            }, undefined, true, undefined, this),
            /* @__PURE__ */ jsxDEV15("text", {
              fg: theme.dim,
              children: "   [\u2191\u2193/PgUp/PgDn/wheel] scroll"
            }, undefined, false, undefined, this)
          ]
        }, undefined, true, undefined, this)
      ]
    }, undefined, true, undefined, this)
  }, undefined, false, undefined, this);
}
var init_AddVerifiedScreen = __esm(() => {
  init_useAppKeyboard();
  init_AppContext();
  init_ThemeContext();
  init_usePaste();
});

// src/tui/screens/AddCustomScreen.tsx
import { useState as useState10 } from "react";
import { useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
import { jsxDEV as jsxDEV16 } from "@opentui/react/jsx-dev-runtime";
function stepIndex(s) {
  return STEPS.indexOf(s);
}
function AddCustomScreen() {
  const { dispatch } = useAppContext();
  const navigate = useNavigate();
  const theme = useTheme();
  const { width } = useTerminalDimensions3();
  const CARD_W = Math.min(60, width - 4);
  const [step, setStep] = useState10("type");
  const [providerType, setProviderType] = useState10("openai-compatible");
  const [typeCursor, setTypeCursor] = useState10(0);
  const [providerId, setProviderId] = useState10("");
  const [providerName, setProviderName] = useState10("");
  const [baseUrl, setBaseUrl] = useState10("");
  const [apiKey, setApiKey] = useState10("");
  const [modelInput, setModelInput] = useState10("");
  const [models, setModels] = useState10([]);
  const [saveError, setSaveError] = useState10("");
  const [savedModelCount, setSavedModelCount] = useState10(0);
  const [inputError, setInputError] = useState10("");
  const typeItems = ["OpenAI Compatible", "Anthropic", "Back"];
  async function doSave() {
    setStep("saving");
    setSaveError("");
    try {
      const { addCustomProvider: addCustomProvider2 } = await Promise.resolve().then(() => (init_ai_config(), exports_ai_config));
      await addCustomProvider2({
        id: providerId.trim(),
        name: providerName.trim(),
        type: providerType,
        baseUrl: baseUrl.trim(),
        apiKey: apiKey.trim(),
        models: models.map((m) => ({ id: m, name: m }))
      });
      const { getAllAvailableProviders: getAllAvailableProviders2 } = await Promise.resolve().then(() => (init_opencode_integration(), exports_opencode_integration));
      const providers = await getAllAvailableProviders2(false);
      dispatch({ type: "SET_CONFIG", config: { providers } });
      setSavedModelCount(models.length);
      setStep("done");
    } catch (e) {
      setSaveError(e instanceof Error ? e.message : String(e));
      setStep("models");
    }
  }
  usePaste((text) => {
    const clean = text.replace(/[\r\n]/g, "");
    if (step === "id")
      setProviderId((v) => v + clean);
    else if (step === "name")
      setProviderName((v) => v + clean);
    else if (step === "url")
      setBaseUrl((v) => v + clean);
    else if (step === "key")
      setApiKey((v) => v + clean);
    else if (step === "models")
      setModelInput((v) => v + clean);
  });
  useAppKeyboard((key) => {
    if (step === "done") {
      if (key.name === "return" || key.name === "enter")
        navigate("model-menu");
      return;
    }
    if (step === "saving")
      return;
    if (step === "type") {
      if (key.name === "escape") {
        navigate("model-menu");
        return;
      }
      if (key.name === "up") {
        setTypeCursor((c) => Math.max(0, c - 1));
        return;
      }
      if (key.name === "down") {
        setTypeCursor((c) => Math.min(typeItems.length - 1, c + 1));
        return;
      }
      if (key.name === "return" || key.name === "enter") {
        if (typeCursor === 2) {
          navigate("model-menu");
          return;
        }
        setProviderType(typeCursor === 1 ? "anthropic" : "openai-compatible");
        setStep("id");
      }
      return;
    }
    if (key.name === "escape") {
      setInputError("");
      const idx = stepIndex(step);
      if (idx <= 1) {
        setStep("type");
        return;
      }
      setStep(STEPS[idx - 1]);
      return;
    }
    if (step === "id") {
      if (key.name === "return" || key.name === "enter") {
        if (!providerId.trim()) {
          setInputError("ID is required");
          return;
        }
        setInputError("");
        setStep("name");
        return;
      }
      if (key.name === "backspace" || key.name === "delete") {
        setProviderId((v) => v.slice(0, -1));
        return;
      }
      if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta && key.sequence >= " ")
        setProviderId((v) => v + key.sequence);
      return;
    }
    if (step === "name") {
      if (key.name === "return" || key.name === "enter") {
        if (!providerName.trim())
          setProviderName(providerId);
        setInputError("");
        setStep("url");
        return;
      }
      if (key.name === "backspace" || key.name === "delete") {
        setProviderName((v) => v.slice(0, -1));
        return;
      }
      if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta && key.sequence >= " ")
        setProviderName((v) => v + key.sequence);
      return;
    }
    if (step === "url") {
      if (key.name === "return" || key.name === "enter") {
        if (!baseUrl.trim()) {
          setInputError("URL is required");
          return;
        }
        if (!baseUrl.trim().startsWith("http")) {
          setInputError("URL must start with http");
          return;
        }
        setInputError("");
        setStep("key");
        return;
      }
      if (key.name === "backspace" || key.name === "delete") {
        setBaseUrl((v) => v.slice(0, -1));
        return;
      }
      if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta && key.sequence >= " ")
        setBaseUrl((v) => v + key.sequence);
      return;
    }
    if (step === "key") {
      if (key.name === "return" || key.name === "enter") {
        setInputError("");
        setStep("models");
        return;
      }
      if (key.name === "backspace" || key.name === "delete") {
        setApiKey((v) => v.slice(0, -1));
        return;
      }
      if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta && key.sequence >= " ")
        setApiKey((v) => v + key.sequence);
      return;
    }
    if (step === "models") {
      if (key.name === "return" || key.name === "enter") {
        if (modelInput.trim()) {
          setModels((ms) => [...ms, modelInput.trim()]);
          setModelInput("");
          setInputError("");
        } else {
          if (models.length === 0) {
            setInputError("Add at least one model");
            return;
          }
          doSave();
        }
        return;
      }
      if (key.name === "backspace" || key.name === "delete") {
        setModelInput((v) => v.slice(0, -1));
        return;
      }
      if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta && key.sequence >= " ")
        setModelInput((v) => v + key.sequence);
    }
  });
  const curStepIdx = stepIndex(step);
  function ProgressBar() {
    return /* @__PURE__ */ jsxDEV16("box", {
      height: 1,
      paddingLeft: 2,
      paddingRight: 2,
      flexDirection: "row",
      children: STEPS.map((s, i) => /* @__PURE__ */ jsxDEV16("box", {
        flexDirection: "row",
        children: [
          /* @__PURE__ */ jsxDEV16("text", {
            fg: i < curStepIdx ? theme.success : i === curStepIdx ? theme.accent : theme.border,
            children: [
              STEP_NUM[i],
              " ",
              STEP_LABELS[i]
            ]
          }, undefined, true, undefined, this),
          i < STEPS.length - 1 && /* @__PURE__ */ jsxDEV16("text", {
            fg: theme.border,
            children: "  "
          }, undefined, false, undefined, this)
        ]
      }, s, true, undefined, this))
    }, undefined, false, undefined, this);
  }
  if (step === "done") {
    return /* @__PURE__ */ jsxDEV16("box", {
      flexDirection: "column",
      flexGrow: 1,
      alignItems: "center",
      justifyContent: "center",
      children: /* @__PURE__ */ jsxDEV16("box", {
        flexDirection: "column",
        border: true,
        borderStyle: "rounded",
        borderColor: theme.border,
        backgroundColor: theme.background,
        width: CARD_W,
        children: /* @__PURE__ */ jsxDEV16("box", {
          flexDirection: "column",
          paddingLeft: 2,
          paddingRight: 2,
          paddingTop: 1,
          paddingBottom: 1,
          children: [
            /* @__PURE__ */ jsxDEV16("box", {
              height: 1,
              children: /* @__PURE__ */ jsxDEV16("text", {
                fg: theme.success,
                children: [
                  "Custom provider added!  ",
                  savedModelCount,
                  " model",
                  savedModelCount !== 1 ? "s" : "",
                  " configured."
                ]
              }, undefined, true, undefined, this)
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV16("box", {
              height: 1,
              children: /* @__PURE__ */ jsxDEV16("text", {
                fg: theme.dim,
                children: "Press [Enter] to return"
              }, undefined, false, undefined, this)
            }, undefined, false, undefined, this)
          ]
        }, undefined, true, undefined, this)
      }, undefined, false, undefined, this)
    }, undefined, false, undefined, this);
  }
  if (step === "saving") {
    return /* @__PURE__ */ jsxDEV16("box", {
      flexDirection: "column",
      flexGrow: 1,
      alignItems: "center",
      justifyContent: "center",
      children: /* @__PURE__ */ jsxDEV16("box", {
        flexDirection: "column",
        border: true,
        borderStyle: "rounded",
        borderColor: theme.border,
        backgroundColor: theme.background,
        width: CARD_W,
        children: /* @__PURE__ */ jsxDEV16("box", {
          paddingLeft: 2,
          paddingTop: 1,
          paddingBottom: 1,
          children: /* @__PURE__ */ jsxDEV16("text", {
            fg: theme.warning,
            children: "\u2839 Saving..."
          }, undefined, false, undefined, this)
        }, undefined, false, undefined, this)
      }, undefined, false, undefined, this)
    }, undefined, false, undefined, this);
  }
  return /* @__PURE__ */ jsxDEV16("box", {
    flexDirection: "column",
    flexGrow: 1,
    alignItems: "center",
    justifyContent: "center",
    children: /* @__PURE__ */ jsxDEV16("box", {
      flexDirection: "column",
      border: true,
      borderStyle: "rounded",
      borderColor: theme.border,
      backgroundColor: theme.background,
      width: CARD_W,
      children: [
        /* @__PURE__ */ jsxDEV16("box", {
          height: 1,
          paddingLeft: 2,
          paddingTop: 1,
          children: /* @__PURE__ */ jsxDEV16("text", {
            fg: theme.accent,
            children: "Add Custom Provider"
          }, undefined, false, undefined, this)
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV16("box", {
          height: 1,
          backgroundColor: theme.border
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV16("box", {
          paddingTop: 1,
          paddingBottom: 1,
          children: /* @__PURE__ */ jsxDEV16(ProgressBar, {}, undefined, false, undefined, this)
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV16("box", {
          height: 1,
          backgroundColor: theme.border
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV16("box", {
          flexDirection: "column",
          paddingLeft: 2,
          paddingRight: 2,
          paddingTop: 1,
          paddingBottom: 1,
          children: [
            step === "type" && /* @__PURE__ */ jsxDEV16("box", {
              flexDirection: "column",
              children: typeItems.map((item, i) => /* @__PURE__ */ jsxDEV16("box", {
                height: 1,
                width: "100%",
                flexDirection: "row",
                backgroundColor: i === typeCursor ? theme.border : "transparent",
                children: [
                  /* @__PURE__ */ jsxDEV16("text", {
                    fg: i === typeCursor ? theme.text : theme.dim,
                    children: [
                      " ",
                      item
                    ]
                  }, undefined, true, undefined, this),
                  i === typeCursor && /* @__PURE__ */ jsxDEV16("text", {
                    fg: theme.accent,
                    children: " \u203A"
                  }, undefined, false, undefined, this)
                ]
              }, item, true, undefined, this))
            }, undefined, false, undefined, this),
            step === "id" && /* @__PURE__ */ jsxDEV16("box", {
              flexDirection: "column",
              children: [
                /* @__PURE__ */ jsxDEV16("box", {
                  height: 1,
                  flexDirection: "row",
                  children: [
                    /* @__PURE__ */ jsxDEV16("text", {
                      fg: theme.primary,
                      children: "Provider ID: "
                    }, undefined, false, undefined, this),
                    /* @__PURE__ */ jsxDEV16("text", {
                      fg: theme.text,
                      children: [
                        providerId,
                        "_"
                      ]
                    }, undefined, true, undefined, this)
                  ]
                }, undefined, true, undefined, this),
                /* @__PURE__ */ jsxDEV16("box", {
                  height: 1,
                  children: /* @__PURE__ */ jsxDEV16("text", {
                    fg: theme.dim,
                    children: "e.g. my-openai"
                  }, undefined, false, undefined, this)
                }, undefined, false, undefined, this)
              ]
            }, undefined, true, undefined, this),
            step === "name" && /* @__PURE__ */ jsxDEV16("box", {
              flexDirection: "column",
              children: [
                /* @__PURE__ */ jsxDEV16("box", {
                  height: 1,
                  flexDirection: "row",
                  children: [
                    /* @__PURE__ */ jsxDEV16("text", {
                      fg: theme.primary,
                      children: "Display Name: "
                    }, undefined, false, undefined, this),
                    /* @__PURE__ */ jsxDEV16("text", {
                      fg: theme.text,
                      children: [
                        providerName,
                        "_"
                      ]
                    }, undefined, true, undefined, this)
                  ]
                }, undefined, true, undefined, this),
                /* @__PURE__ */ jsxDEV16("box", {
                  height: 1,
                  children: /* @__PURE__ */ jsxDEV16("text", {
                    fg: theme.dim,
                    children: [
                      'e.g. My OpenAI  (Enter to use "',
                      providerId,
                      '")'
                    ]
                  }, undefined, true, undefined, this)
                }, undefined, false, undefined, this)
              ]
            }, undefined, true, undefined, this),
            step === "url" && /* @__PURE__ */ jsxDEV16("box", {
              flexDirection: "column",
              children: [
                /* @__PURE__ */ jsxDEV16("box", {
                  height: 1,
                  flexDirection: "row",
                  children: [
                    /* @__PURE__ */ jsxDEV16("text", {
                      fg: theme.primary,
                      children: "Base URL: "
                    }, undefined, false, undefined, this),
                    /* @__PURE__ */ jsxDEV16("text", {
                      fg: theme.text,
                      children: [
                        baseUrl,
                        "_"
                      ]
                    }, undefined, true, undefined, this)
                  ]
                }, undefined, true, undefined, this),
                /* @__PURE__ */ jsxDEV16("box", {
                  height: 1,
                  children: /* @__PURE__ */ jsxDEV16("text", {
                    fg: theme.dim,
                    children: "e.g. https://api.example.com/v1"
                  }, undefined, false, undefined, this)
                }, undefined, false, undefined, this)
              ]
            }, undefined, true, undefined, this),
            step === "key" && /* @__PURE__ */ jsxDEV16("box", {
              flexDirection: "column",
              children: [
                /* @__PURE__ */ jsxDEV16("box", {
                  height: 1,
                  flexDirection: "row",
                  children: [
                    /* @__PURE__ */ jsxDEV16("text", {
                      fg: theme.primary,
                      children: "API Key: "
                    }, undefined, false, undefined, this),
                    /* @__PURE__ */ jsxDEV16("text", {
                      fg: theme.text,
                      children: [
                        apiKey,
                        "_"
                      ]
                    }, undefined, true, undefined, this)
                  ]
                }, undefined, true, undefined, this),
                /* @__PURE__ */ jsxDEV16("box", {
                  height: 1,
                  children: /* @__PURE__ */ jsxDEV16("text", {
                    fg: theme.dim,
                    children: "sk-...   (leave empty if not needed)"
                  }, undefined, false, undefined, this)
                }, undefined, false, undefined, this)
              ]
            }, undefined, true, undefined, this),
            step === "models" && /* @__PURE__ */ jsxDEV16("box", {
              flexDirection: "column",
              children: [
                models.length > 0 && /* @__PURE__ */ jsxDEV16("box", {
                  flexDirection: "column",
                  children: models.map((m) => /* @__PURE__ */ jsxDEV16("box", {
                    height: 1,
                    children: /* @__PURE__ */ jsxDEV16("text", {
                      fg: theme.dim,
                      children: [
                        "  \xB7 ",
                        m
                      ]
                    }, undefined, true, undefined, this)
                  }, m, false, undefined, this))
                }, undefined, false, undefined, this),
                /* @__PURE__ */ jsxDEV16("box", {
                  height: 1,
                  flexDirection: "row",
                  children: [
                    /* @__PURE__ */ jsxDEV16("text", {
                      fg: theme.primary,
                      children: "Model name: "
                    }, undefined, false, undefined, this),
                    /* @__PURE__ */ jsxDEV16("text", {
                      fg: theme.text,
                      children: [
                        modelInput,
                        "_"
                      ]
                    }, undefined, true, undefined, this)
                  ]
                }, undefined, true, undefined, this),
                models.length > 0 && /* @__PURE__ */ jsxDEV16("box", {
                  height: 1,
                  children: /* @__PURE__ */ jsxDEV16("text", {
                    fg: theme.success,
                    children: [
                      "  ",
                      models.length,
                      " model",
                      models.length !== 1 ? "s" : "",
                      " added  (empty [Enter] to finish)"
                    ]
                  }, undefined, true, undefined, this)
                }, undefined, false, undefined, this),
                saveError && /* @__PURE__ */ jsxDEV16("box", {
                  height: 1,
                  children: /* @__PURE__ */ jsxDEV16("text", {
                    fg: theme.error,
                    children: [
                      "Error: ",
                      saveError
                    ]
                  }, undefined, true, undefined, this)
                }, undefined, false, undefined, this)
              ]
            }, undefined, true, undefined, this),
            inputError ? /* @__PURE__ */ jsxDEV16("box", {
              height: 1,
              children: /* @__PURE__ */ jsxDEV16("text", {
                fg: theme.error,
                children: inputError
              }, undefined, false, undefined, this)
            }, undefined, false, undefined, this) : null
          ]
        }, undefined, true, undefined, this)
      ]
    }, undefined, true, undefined, this)
  }, undefined, false, undefined, this);
}
var STEPS, STEP_LABELS, STEP_NUM;
var init_AddCustomScreen = __esm(() => {
  init_useAppKeyboard();
  init_AppContext();
  init_ThemeContext();
  init_usePaste();
  STEPS = ["type", "id", "name", "url", "key", "models"];
  STEP_LABELS = ["Type", "ID", "Name", "URL", "Key", "Models"];
  STEP_NUM = ["\u2460", "\u2461", "\u2462", "\u2463", "\u2464", "\u2465"];
});

// src/tui/screens/AddModelsScreen.tsx
import { useState as useState11, useEffect as useEffect8 } from "react";
import { useTerminalDimensions as useTerminalDimensions4 } from "@opentui/react";
import { jsxDEV as jsxDEV17 } from "@opentui/react/jsx-dev-runtime";
function AddModelsScreen() {
  const { dispatch } = useAppContext();
  const navigate = useNavigate();
  const theme = useTheme();
  const { width } = useTerminalDimensions4();
  const CARD_W = Math.min(60, width - 4);
  const [step, setStep] = useState11("pick");
  const [providers, setProviders] = useState11([]);
  const [cursor, setCursor] = useState11(0);
  const [selectedProvider, setSelectedProvider] = useState11(null);
  const [modelInput, setModelInput] = useState11("");
  const [addedModels, setAddedModels] = useState11([]);
  const [loadError, setLoadError] = useState11("");
  const [saveError, setSaveError] = useState11("");
  const [inputError, setInputError] = useState11("");
  const [done, setDone] = useState11(false);
  useEffect8(() => {
    async function load() {
      try {
        const { getCustomProvidersFromConfig: getCustomProvidersFromConfig2 } = await Promise.resolve().then(() => (init_ai_config(), exports_ai_config));
        const provs = await getCustomProvidersFromConfig2();
        setProviders(provs);
      } catch (e) {
        setLoadError(e instanceof Error ? e.message : String(e));
      }
    }
    load();
  }, []);
  usePaste((text) => {
    const clean = text.replace(/[\r\n]/g, "");
    if (step === "add")
      setModelInput((v) => v + clean);
  });
  useAppKeyboard((key) => {
    if (done) {
      if (key.name === "return" || key.name === "enter")
        navigate("model-menu");
      return;
    }
    if (step === "pick") {
      if (key.name === "escape" || key.sequence === "q" || key.sequence === "Q") {
        navigate("model-menu");
        return;
      }
      if (key.name === "up") {
        setCursor((c) => Math.max(0, c - 1));
        return;
      }
      if (key.name === "down") {
        setCursor((c) => Math.min(providers.length - 1, c + 1));
        return;
      }
      if (key.name === "return" || key.name === "enter") {
        const prov = providers[cursor];
        if (prov) {
          setSelectedProvider(prov);
          setModelInput("");
          setAddedModels([]);
          setSaveError("");
          setInputError("");
          setStep("add");
        }
        return;
      }
    }
    if (step === "add") {
      if (key.name === "escape") {
        setStep("pick");
        return;
      }
      if (key.name === "return" || key.name === "enter") {
        if (modelInput.trim()) {
          addModel();
        } else {
          if (addedModels.length === 0) {
            setInputError("Add at least one model");
            return;
          }
          finishAdding();
        }
        return;
      }
      if (key.name === "backspace" || key.name === "delete") {
        setModelInput((v) => v.slice(0, -1));
        return;
      }
      if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta && key.sequence >= " ")
        setModelInput((v) => v + key.sequence);
    }
  });
  async function addModel() {
    if (!selectedProvider || !modelInput.trim())
      return;
    setInputError("");
    setSaveError("");
    const name = modelInput.trim();
    try {
      const { addModelToCustomProvider: addModelToCustomProvider2 } = await Promise.resolve().then(() => (init_ai_config(), exports_ai_config));
      await addModelToCustomProvider2(selectedProvider.id, { id: name.toLowerCase().replace(/[^a-z0-9-]/g, "-"), name });
      setAddedModels((ms) => [...ms, name]);
      setModelInput("");
    } catch (e) {
      setSaveError(e instanceof Error ? e.message : String(e));
    }
  }
  async function finishAdding() {
    const { getAllAvailableProviders: getAllAvailableProviders2 } = await Promise.resolve().then(() => (init_opencode_integration(), exports_opencode_integration));
    const provs = await getAllAvailableProviders2(false);
    dispatch({ type: "SET_CONFIG", config: { providers: provs } });
    setDone(true);
  }
  if (done) {
    return /* @__PURE__ */ jsxDEV17("box", {
      flexDirection: "column",
      flexGrow: 1,
      alignItems: "center",
      justifyContent: "center",
      children: /* @__PURE__ */ jsxDEV17("box", {
        flexDirection: "column",
        border: true,
        borderStyle: "rounded",
        borderColor: theme.border,
        backgroundColor: theme.background,
        width: CARD_W,
        children: /* @__PURE__ */ jsxDEV17("box", {
          flexDirection: "column",
          paddingLeft: 2,
          paddingRight: 2,
          paddingTop: 1,
          paddingBottom: 1,
          children: [
            /* @__PURE__ */ jsxDEV17("box", {
              height: 1,
              children: /* @__PURE__ */ jsxDEV17("text", {
                fg: theme.success,
                children: [
                  "Done! Added ",
                  addedModels.length,
                  " model",
                  addedModels.length !== 1 ? "s" : "",
                  " to ",
                  selectedProvider?.name,
                  "."
                ]
              }, undefined, true, undefined, this)
            }, undefined, false, undefined, this),
            /* @__PURE__ */ jsxDEV17("box", {
              height: 1,
              children: /* @__PURE__ */ jsxDEV17("text", {
                fg: theme.dim,
                children: "Press [Enter] to return"
              }, undefined, false, undefined, this)
            }, undefined, false, undefined, this)
          ]
        }, undefined, true, undefined, this)
      }, undefined, false, undefined, this)
    }, undefined, false, undefined, this);
  }
  if (step === "add" && selectedProvider) {
    return /* @__PURE__ */ jsxDEV17("box", {
      flexDirection: "column",
      flexGrow: 1,
      alignItems: "center",
      justifyContent: "center",
      children: /* @__PURE__ */ jsxDEV17("box", {
        flexDirection: "column",
        border: true,
        borderStyle: "rounded",
        borderColor: theme.border,
        backgroundColor: theme.background,
        width: CARD_W,
        children: [
          /* @__PURE__ */ jsxDEV17("box", {
            height: 1,
            paddingLeft: 2,
            paddingTop: 1,
            flexDirection: "row",
            children: [
              /* @__PURE__ */ jsxDEV17("text", {
                fg: theme.accent,
                children: "Add Models to: "
              }, undefined, false, undefined, this),
              /* @__PURE__ */ jsxDEV17("text", {
                fg: theme.text,
                children: selectedProvider.name
              }, undefined, false, undefined, this)
            ]
          }, undefined, true, undefined, this),
          /* @__PURE__ */ jsxDEV17("box", {
            height: 1,
            backgroundColor: theme.border
          }, undefined, false, undefined, this),
          /* @__PURE__ */ jsxDEV17("box", {
            flexDirection: "column",
            paddingLeft: 2,
            paddingRight: 2,
            paddingTop: 1,
            paddingBottom: 1,
            children: [
              (selectedProvider.models.length > 0 || addedModels.length > 0) && /* @__PURE__ */ jsxDEV17("box", {
                flexDirection: "column",
                children: [
                  /* @__PURE__ */ jsxDEV17("box", {
                    height: 1,
                    children: /* @__PURE__ */ jsxDEV17("text", {
                      fg: theme.primary,
                      children: "Current models:"
                    }, undefined, false, undefined, this)
                  }, undefined, false, undefined, this),
                  selectedProvider.models.map((m) => /* @__PURE__ */ jsxDEV17("box", {
                    height: 1,
                    children: /* @__PURE__ */ jsxDEV17("text", {
                      fg: theme.dim,
                      children: [
                        "  \xB7 ",
                        m.name || m.id
                      ]
                    }, undefined, true, undefined, this)
                  }, m.id, false, undefined, this)),
                  addedModels.map((m) => /* @__PURE__ */ jsxDEV17("box", {
                    height: 1,
                    children: /* @__PURE__ */ jsxDEV17("text", {
                      fg: theme.success,
                      children: [
                        "  \xB7 ",
                        m,
                        " (new)"
                      ]
                    }, undefined, true, undefined, this)
                  }, m, false, undefined, this))
                ]
              }, undefined, true, undefined, this),
              /* @__PURE__ */ jsxDEV17("box", {
                height: 1,
                flexDirection: "row",
                children: [
                  /* @__PURE__ */ jsxDEV17("text", {
                    fg: theme.primary,
                    children: "Model name: "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV17("text", {
                    fg: theme.text,
                    children: [
                      modelInput,
                      "_"
                    ]
                  }, undefined, true, undefined, this)
                ]
              }, undefined, true, undefined, this),
              addedModels.length > 0 && /* @__PURE__ */ jsxDEV17("box", {
                height: 1,
                children: /* @__PURE__ */ jsxDEV17("text", {
                  fg: theme.success,
                  children: [
                    "Added ",
                    addedModels.length,
                    " model",
                    addedModels.length !== 1 ? "s" : "",
                    " so far"
                  ]
                }, undefined, true, undefined, this)
              }, undefined, false, undefined, this),
              saveError && /* @__PURE__ */ jsxDEV17("box", {
                height: 1,
                children: /* @__PURE__ */ jsxDEV17("text", {
                  fg: theme.error,
                  children: [
                    "Error: ",
                    saveError
                  ]
                }, undefined, true, undefined, this)
              }, undefined, false, undefined, this),
              inputError && /* @__PURE__ */ jsxDEV17("box", {
                height: 1,
                children: /* @__PURE__ */ jsxDEV17("text", {
                  fg: theme.error,
                  children: inputError
                }, undefined, false, undefined, this)
              }, undefined, false, undefined, this)
            ]
          }, undefined, true, undefined, this)
        ]
      }, undefined, true, undefined, this)
    }, undefined, false, undefined, this);
  }
  return /* @__PURE__ */ jsxDEV17("box", {
    flexDirection: "column",
    flexGrow: 1,
    alignItems: "center",
    justifyContent: "center",
    children: /* @__PURE__ */ jsxDEV17("box", {
      flexDirection: "column",
      border: true,
      borderStyle: "rounded",
      borderColor: theme.border,
      backgroundColor: theme.background,
      width: CARD_W,
      children: [
        /* @__PURE__ */ jsxDEV17("box", {
          height: 1,
          paddingLeft: 2,
          paddingTop: 1,
          children: /* @__PURE__ */ jsxDEV17("text", {
            fg: theme.accent,
            children: "Add Models to Provider"
          }, undefined, false, undefined, this)
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV17("box", {
          height: 1,
          backgroundColor: theme.border
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV17("box", {
          flexDirection: "column",
          paddingLeft: 2,
          paddingRight: 2,
          paddingTop: 1,
          paddingBottom: 1,
          children: [
            loadError && /* @__PURE__ */ jsxDEV17("box", {
              height: 1,
              children: /* @__PURE__ */ jsxDEV17("text", {
                fg: theme.error,
                children: [
                  "Error: ",
                  loadError
                ]
              }, undefined, true, undefined, this)
            }, undefined, false, undefined, this),
            !loadError && providers.length === 0 && /* @__PURE__ */ jsxDEV17("box", {
              height: 1,
              children: /* @__PURE__ */ jsxDEV17("text", {
                fg: theme.warning,
                children: "No custom providers yet. Add one first."
              }, undefined, false, undefined, this)
            }, undefined, false, undefined, this),
            providers.map((prov, i) => {
              const isActive = i === cursor;
              return /* @__PURE__ */ jsxDEV17("box", {
                height: 1,
                width: "100%",
                flexDirection: "row",
                backgroundColor: isActive ? theme.border : "transparent",
                children: [
                  /* @__PURE__ */ jsxDEV17("text", {
                    fg: theme.dim,
                    width: 2,
                    children: " "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV17("text", {
                    fg: isActive ? theme.text : theme.dim,
                    width: Math.floor((CARD_W - 8) * 0.6),
                    children: prov.name
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV17("text", {
                    fg: isActive ? theme.primary : theme.border,
                    width: Math.floor((CARD_W - 8) * 0.3),
                    children: [
                      prov.models.length,
                      " models"
                    ]
                  }, undefined, true, undefined, this),
                  /* @__PURE__ */ jsxDEV17("text", {
                    fg: theme.accent,
                    width: 2,
                    children: isActive ? "\u203A" : " "
                  }, undefined, false, undefined, this)
                ]
              }, prov.id, true, undefined, this);
            })
          ]
        }, undefined, true, undefined, this)
      ]
    }, undefined, true, undefined, this)
  }, undefined, false, undefined, this);
}
var init_AddModelsScreen = __esm(() => {
  init_useAppKeyboard();
  init_AppContext();
  init_ThemeContext();
  init_usePaste();
});

// src/tui/screens/ListProvidersScreen.tsx
import { jsxDEV as jsxDEV18 } from "@opentui/react/jsx-dev-runtime";
function ListProvidersScreen() {
  const { state } = useAppContext();
  const navigate = useNavigate();
  const theme = useTheme();
  useAppKeyboard((key) => {
    if (key.name === "escape" || key.name === "q") {
      navigate("model-menu");
    }
  });
  if (state.isLoadingConfig) {
    return /* @__PURE__ */ jsxDEV18("box", {
      flexDirection: "column",
      flexGrow: 1,
      padding: 1,
      children: /* @__PURE__ */ jsxDEV18("text", {
        fg: theme.dim,
        children: "Loading providers..."
      }, undefined, false, undefined, this)
    }, undefined, false, undefined, this);
  }
  const providers = state.config?.providers ?? [];
  if (providers.length === 0) {
    return /* @__PURE__ */ jsxDEV18("box", {
      flexDirection: "column",
      flexGrow: 1,
      padding: 1,
      children: [
        /* @__PURE__ */ jsxDEV18("text", {
          fg: theme.primary,
          children: "Configured Providers"
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV18("box", {
          marginTop: 1,
          children: /* @__PURE__ */ jsxDEV18("text", {
            fg: theme.warning,
            children: "No providers configured yet."
          }, undefined, false, undefined, this)
        }, undefined, false, undefined, this)
      ]
    }, undefined, true, undefined, this);
  }
  return /* @__PURE__ */ jsxDEV18("box", {
    flexDirection: "column",
    flexGrow: 1,
    padding: 1,
    children: [
      /* @__PURE__ */ jsxDEV18("text", {
        fg: theme.primary,
        children: "Configured Providers"
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV18("box", {
        marginTop: 1,
        flexGrow: 1,
        children: /* @__PURE__ */ jsxDEV18("scrollbox", {
          focused: true,
          children: providers.map((provider, i) => /* @__PURE__ */ jsxDEV18("box", {
            flexDirection: "column",
            border: true,
            borderStyle: "rounded",
            borderColor: theme.border,
            backgroundColor: theme.background,
            marginBottom: 1,
            padding: 1,
            children: [
              /* @__PURE__ */ jsxDEV18("box", {
                flexDirection: "row",
                children: [
                  /* @__PURE__ */ jsxDEV18("text", {
                    fg: theme.accent,
                    children: provider.name
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV18("text", {
                    fg: theme.dim,
                    children: [
                      "  [",
                      provider.type,
                      "]"
                    ]
                  }, undefined, true, undefined, this)
                ]
              }, undefined, true, undefined, this),
              provider.models.map((model, j) => /* @__PURE__ */ jsxDEV18("text", {
                fg: theme.dim,
                children: [
                  "  \xB7 ",
                  model.name || model.id
                ]
              }, j, true, undefined, this))
            ]
          }, i, true, undefined, this))
        }, undefined, false, undefined, this)
      }, undefined, false, undefined, this)
    ]
  }, undefined, true, undefined, this);
}
var init_ListProvidersScreen = __esm(() => {
  init_useAppKeyboard();
  init_AppContext();
  init_ThemeContext();
});

// src/tui/screens/FAQScreen.tsx
import { jsxDEV as jsxDEV19 } from "@opentui/react/jsx-dev-runtime";
function FAQScreen() {
  const navigate = useNavigate();
  const theme = useTheme();
  useAppKeyboard((key) => {
    if (key.name === "escape" || key.name === "q") {
      navigate("main-menu");
    }
  });
  return /* @__PURE__ */ jsxDEV19("box", {
    flexDirection: "column",
    flexGrow: 1,
    alignItems: "center",
    padding: 1,
    children: /* @__PURE__ */ jsxDEV19("box", {
      flexDirection: "column",
      width: 70,
      children: [
        /* @__PURE__ */ jsxDEV19("box", {
          marginBottom: 1,
          children: /* @__PURE__ */ jsxDEV19("text", {
            fg: theme.primary,
            bold: true,
            children: "FAQ / Learn"
          }, undefined, false, undefined, this)
        }, undefined, false, undefined, this),
        /* @__PURE__ */ jsxDEV19("scrollbox", {
          focused: true,
          flexGrow: 1,
          children: /* @__PURE__ */ jsxDEV19("box", {
            flexDirection: "column",
            children: [
              /* @__PURE__ */ jsxDEV19("box", {
                flexDirection: "column",
                border: true,
                borderStyle: "rounded",
                borderColor: theme.border,
                padding: 1,
                marginBottom: 1,
                children: [
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.accent,
                    bold: true,
                    children: "METRICS EXPLAINED"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: " "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.secondary,
                    bold: true,
                    children: "TPS (Tokens Per Second)"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  How fast a model generates tokens after the first one."
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.dim,
                    children: "  Formula: output_tokens / generation_time"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.success,
                    children: "  \u2192 Higher is better (faster streaming)"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: " "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.secondary,
                    bold: true,
                    children: "TTFT (Time To First Token)"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  Time from request to receiving the first token."
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.dim,
                    children: "  Formula: first_token_time - request_start_time"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.success,
                    children: "  \u2192 Lower is better (less waiting)"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: " "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.secondary,
                    bold: true,
                    children: "F1000 (First to 1000)"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  Time to complete 1000 agentic requests (~300 tokens each)."
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.dim,
                    children: "  Formula: 1000 \xD7 (TTFT + 300/TPS) = hours"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.success,
                    children: "  \u2192 Lower is better"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: " "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  Why it matters: In agentic coding (Cursor, Copilot, OpenCode),"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  models make hundreds of tool calls \u2014 TTFT adds up massively."
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  A 30 tok/s + 1s TTFT model can match a 60 tok/s + 6s TTFT model."
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: " "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.secondary,
                    bold: true,
                    children: "Total Time"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  Complete request duration from start to finish."
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.dim,
                    children: "  Includes: connection, TTFT, and generation time"
                  }, undefined, false, undefined, this)
                ]
              }, undefined, true, undefined, this),
              /* @__PURE__ */ jsxDEV19("box", {
                flexDirection: "column",
                border: true,
                borderStyle: "rounded",
                borderColor: theme.border,
                padding: 1,
                marginBottom: 1,
                children: [
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.accent,
                    bold: true,
                    children: "LINKS"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: " "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.secondary,
                    bold: true,
                    children: "Discord Community"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  https://discord.gg/6S7HwCxbMy"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.dim,
                    children: "  Join for help, updates, and discussions"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: " "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.secondary,
                    bold: true,
                    children: "Website & Leaderboard"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  https://ai-speedometer.oliveowl.xyz/"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.dim,
                    children: "  Track OSS model speeds over time"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: " "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.secondary,
                    bold: true,
                    children: "GitHub"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  https://github.com/anomaly/ai-speedometer"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.dim,
                    children: "  Source code, issues, contributions"
                  }, undefined, false, undefined, this)
                ]
              }, undefined, true, undefined, this),
              /* @__PURE__ */ jsxDEV19("box", {
                flexDirection: "column",
                border: true,
                borderStyle: "rounded",
                borderColor: theme.border,
                padding: 1,
                marginBottom: 1,
                children: [
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.accent,
                    bold: true,
                    children: "TIPS"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: " "
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  \u2022 Press [T] anytime to open the theme picker"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  \u2022 Use --log flag to save raw SSE data for debugging"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  \u2022 Run headless with ai-speedometer-headless for CI/CD"
                  }, undefined, false, undefined, this),
                  /* @__PURE__ */ jsxDEV19("text", {
                    fg: theme.text,
                    children: "  \u2022 [*] in results means token count was estimated"
                  }, undefined, false, undefined, this)
                ]
              }, undefined, true, undefined, this),
              /* @__PURE__ */ jsxDEV19("box", {
                flexDirection: "row",
                justifyContent: "center",
                marginTop: 1,
                children: /* @__PURE__ */ jsxDEV19("text", {
                  fg: theme.dim,
                  children: "Press [Q] or [Esc] to return to main menu"
                }, undefined, false, undefined, this)
              }, undefined, false, undefined, this)
            ]
          }, undefined, true, undefined, this)
        }, undefined, false, undefined, this)
      ]
    }, undefined, true, undefined, this)
  }, undefined, false, undefined, this);
}
var init_FAQScreen = __esm(() => {
  init_useAppKeyboard();
  init_AppContext();
  init_ThemeContext();
});

// src/tui/App.tsx
import { useKeyboard as useKeyboard3, useRenderer as useRenderer3 } from "@opentui/react";
import { jsxDEV as jsxDEV20 } from "@opentui/react/jsx-dev-runtime";
function getHints(screen, benchResults) {
  switch (screen) {
    case "main-menu":
      return ["[\u2191\u2193] navigate", "[Enter] select", "[T] theme", "[Ctrl+C] quit"];
    case "model-menu":
      return ["[\u2191\u2193] navigate", "[Enter] select", "[Q] back"];
    case "model-select":
      return ["[\u2191\u2193] navigate", "[Tab] select", "[Enter] run", "[A] all", "[N] none", "[R] recent", "[Esc] back"];
    case "benchmark": {
      const allDone = benchResults.length > 0 && benchResults.every((r) => r.status === "done" || r.status === "error");
      return allDone ? ["[R] rerun", "[Enter] back to menu", "[Q] back to menu"] : ["Benchmark in progress..."];
    }
    case "list-providers":
      return ["[\u2191\u2193] scroll", "[Q] back"];
    case "faq":
      return ["[\u2191\u2193] scroll", "[Q] back"];
    case "add-verified":
      return ["[\u2191\u2193] navigate", "[Enter] select", "[Q] back"];
    case "add-custom":
      return ["[\u2191\u2193] navigate", "[Enter] confirm", "[Esc] back"];
    case "add-models":
      return ["[\u2191\u2193] navigate", "[Enter] add / finish", "[Esc] back"];
    default:
      return ["[Q] back"];
  }
}
function ActiveScreen() {
  const { state } = useAppContext();
  switch (state.screen) {
    case "main-menu":
      return /* @__PURE__ */ jsxDEV20(MainMenuScreen, {}, undefined, false, undefined, this);
    case "model-menu":
      return /* @__PURE__ */ jsxDEV20(ModelMenuScreen, {}, undefined, false, undefined, this);
    case "model-select":
      return /* @__PURE__ */ jsxDEV20(ModelSelectScreen, {}, undefined, false, undefined, this);
    case "benchmark":
      return /* @__PURE__ */ jsxDEV20(BenchmarkScreen, {}, undefined, false, undefined, this);
    case "add-verified":
      return /* @__PURE__ */ jsxDEV20(AddVerifiedScreen, {}, undefined, false, undefined, this);
    case "add-custom":
      return /* @__PURE__ */ jsxDEV20(AddCustomScreen, {}, undefined, false, undefined, this);
    case "add-models":
      return /* @__PURE__ */ jsxDEV20(AddModelsScreen, {}, undefined, false, undefined, this);
    case "list-providers":
      return /* @__PURE__ */ jsxDEV20(ListProvidersScreen, {}, undefined, false, undefined, this);
    case "faq":
      return /* @__PURE__ */ jsxDEV20(FAQScreen, {}, undefined, false, undefined, this);
  }
}
function Shell() {
  const renderer = useRenderer3();
  const { state } = useAppContext();
  const theme = useTheme();
  const { modalOpen, setModalOpen } = useModal();
  useKeyboard3((key) => {
    if (key.ctrl && key.name === "c") {
      renderer.destroy();
      return;
    }
    if (!key.ctrl && !key.meta && key.sequence === "T") {
      setModalOpen(!modalOpen);
    }
  });
  return /* @__PURE__ */ jsxDEV20("box", {
    flexDirection: "column",
    height: "100%",
    width: "100%",
    backgroundColor: theme.background,
    children: [
      /* @__PURE__ */ jsxDEV20(Header, {
        screen: state.screen
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV20("box", {
        flexGrow: 1,
        flexDirection: "column",
        children: /* @__PURE__ */ jsxDEV20(ActiveScreen, {}, undefined, false, undefined, this)
      }, undefined, false, undefined, this),
      /* @__PURE__ */ jsxDEV20(Footer, {
        hints: getHints(state.screen, state.benchResults)
      }, undefined, false, undefined, this),
      modalOpen && /* @__PURE__ */ jsxDEV20(ThemePicker, {
        onClose: () => setModalOpen(false)
      }, undefined, false, undefined, this)
    ]
  }, undefined, true, undefined, this);
}
function App({ logMode = false, theme = "tokyonight" }) {
  return /* @__PURE__ */ jsxDEV20(ThemeProvider, {
    name: theme,
    children: /* @__PURE__ */ jsxDEV20(ModalProvider, {
      children: /* @__PURE__ */ jsxDEV20(AppProvider, {
        logMode,
        children: /* @__PURE__ */ jsxDEV20(Shell, {}, undefined, false, undefined, this)
      }, undefined, false, undefined, this)
    }, undefined, false, undefined, this)
  }, undefined, false, undefined, this);
}
var init_App = __esm(() => {
  init_AppContext();
  init_ThemeContext();
  init_ModalContext();
  init_ThemePicker();
  init_Header();
  init_Footer();
  init_MainMenuScreen();
  init_ModelMenuScreen();
  init_ModelSelectScreen();
  init_BenchmarkScreen();
  init_AddVerifiedScreen();
  init_AddCustomScreen();
  init_AddModelsScreen();
  init_ListProvidersScreen();
  init_FAQScreen();
});

// src/tui/index.tsx
var exports_tui = {};
__export(exports_tui, {
  startTui: () => startTui
});
import { createCliRenderer } from "@opentui/core";
import { createRoot } from "@opentui/react";
import { jsxDEV as jsxDEV21 } from "@opentui/react/jsx-dev-runtime";
async function startTui(logMode = false) {
  const { readThemeFromConfig: readThemeFromConfig2 } = await Promise.resolve().then(() => (init_ai_config(), exports_ai_config));
  const theme = await readThemeFromConfig2();
  const renderer = await createCliRenderer({
    exitOnCtrlC: false
  });
  process.stdout.write(ENABLE_BRACKETED_PASTE);
  const cleanup = () => {
    process.stdout.write(DISABLE_BRACKETED_PASTE);
  };
  renderer.on("destroy", cleanup);
  process.on("SIGINT", () => {
    cleanup();
    renderer.destroy();
    process.exit(0);
  });
  createRoot(renderer).render(/* @__PURE__ */ jsxDEV21(App, {
    logMode,
    theme
  }, undefined, false, undefined, this));
}
var ENABLE_BRACKETED_PASTE = "\x1B[?2004h", DISABLE_BRACKETED_PASTE = "\x1B[?2004l";
var init_tui = __esm(() => {
  init_App();
});

// src/index.ts
function parseCliArgs() {
  const args = process.argv.slice(2);
  const parsed = {
    debug: false,
    log: false,
    bench: null,
    benchCustom: null,
    apiKey: null,
    baseUrl: null,
    endpointFormat: null,
    formatted: false,
    help: false
  };
  for (let i = 0;i < args.length; i++) {
    const arg = args[i];
    if (arg === "--debug")
      parsed.debug = true;
    else if (arg === "--log")
      parsed.log = true;
    else if (arg === "--bench")
      parsed.bench = args[++i] ?? null;
    else if (arg === "--bench-custom")
      parsed.benchCustom = args[++i] ?? null;
    else if (arg === "--api-key")
      parsed.apiKey = args[++i] ?? null;
    else if (arg === "--base-url")
      parsed.baseUrl = args[++i] ?? null;
    else if (arg === "--endpoint-format")
      parsed.endpointFormat = args[++i] ?? null;
    else if (arg === "--formatted")
      parsed.formatted = true;
    else if (arg === "--help" || arg === "-h")
      parsed.help = true;
  }
  return parsed;
}
function showHelp() {
  console.log("ai-speedometer - Benchmark AI models");
  console.log("");
  console.log("Usage:");
  console.log("  ai-speedometer                                  # Interactive mode");
  console.log("  ai-speedometer --bench <provider:model>         # Headless benchmark");
  console.log("  ai-speedometer --bench-custom <provider:model>  # Custom provider benchmark");
  console.log("");
  console.log("Options:");
  console.log("  --bench <provider:model>        Run benchmark in headless mode");
  console.log("  --bench-custom <provider:model> Run custom provider benchmark");
  console.log("  --base-url <url>                Base URL for custom provider");
  console.log("  --api-key <key>                 API key for custom provider");
  console.log("  --endpoint-format <format>      Endpoint format (default: chat/completions)");
  console.log("  --formatted                     Format JSON output for human readability");
  console.log("  --log                           Log raw SSE streams to ~/.local/share/ai-speedometer/logs/");
  console.log("  --debug                         Enable debug logging");
  console.log("  --help, -h                      Show this help message");
  console.log("");
  console.log("Examples:");
  console.log("  ai-speedometer --bench openai:gpt-4");
  console.log('  ai-speedometer --bench anthropic:claude-3-opus --api-key "sk-..."');
  console.log('  ai-speedometer --bench-custom openai:gpt-4 --base-url "https://api.openai.com/v1" --api-key "sk-..."');
}
var cliArgs = parseCliArgs();
if (cliArgs.help) {
  showHelp();
  process.exit(0);
} else if (cliArgs.bench || cliArgs.benchCustom) {
  const { runHeadlessBenchmark: runHeadlessBenchmark2 } = await Promise.resolve().then(() => (init_headless(), exports_headless));
  await runHeadlessBenchmark2(cliArgs);
} else {
  const { startTui: startTui2 } = await Promise.resolve().then(() => (init_tui(), exports_tui));
  await startTui2(cliArgs.log);
}
