claudish 6.13.1 → 6.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -17321,188 +17321,6 @@ var init_openrouter_queue = __esm(() => {
17321
17321
  init_logger();
17322
17322
  });
17323
17323
 
17324
- // src/model-loader.ts
17325
- import { readFileSync as readFileSync2, existsSync as existsSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
17326
- import { join as join4, dirname } from "path";
17327
- import { fileURLToPath } from "url";
17328
- import { homedir as homedir3 } from "os";
17329
- import { createHash } from "crypto";
17330
- function getRecommendedModelsPath() {
17331
- return join4(__dirname2, "../recommended-models.json");
17332
- }
17333
- function loadRecommendedModelsJSON() {
17334
- if (_cachedRecommendedModels) {
17335
- return _cachedRecommendedModels;
17336
- }
17337
- if (existsSync3(RECOMMENDED_CACHE_PATH)) {
17338
- try {
17339
- const cacheData = JSON.parse(readFileSync2(RECOMMENDED_CACHE_PATH, "utf-8"));
17340
- if (cacheData.models && cacheData.models.length > 0) {
17341
- const generatedAt = cacheData.generatedAt;
17342
- if (generatedAt) {
17343
- const ageHours = (Date.now() - new Date(generatedAt).getTime()) / (1000 * 60 * 60);
17344
- if (ageHours <= RECOMMENDED_CACHE_MAX_AGE_HOURS) {
17345
- _cachedRecommendedModels = cacheData;
17346
- return cacheData;
17347
- }
17348
- } else {
17349
- _cachedRecommendedModels = cacheData;
17350
- return cacheData;
17351
- }
17352
- }
17353
- } catch {}
17354
- }
17355
- const jsonPath = getRecommendedModelsPath();
17356
- if (!existsSync3(jsonPath)) {
17357
- throw new Error(`recommended-models.json not found at ${jsonPath}. ` + `Run 'claudish --update-models' to fetch the latest model list.`);
17358
- }
17359
- try {
17360
- const jsonContent = readFileSync2(jsonPath, "utf-8");
17361
- _cachedRecommendedModels = JSON.parse(jsonContent);
17362
- return _cachedRecommendedModels;
17363
- } catch (error2) {
17364
- throw new Error(`Failed to parse recommended-models.json: ${error2}`);
17365
- }
17366
- }
17367
- function loadModelInfo() {
17368
- if (_cachedModelInfo) {
17369
- return _cachedModelInfo;
17370
- }
17371
- const data = loadRecommendedModelsJSON();
17372
- const modelInfo = {};
17373
- for (const model of data.models) {
17374
- modelInfo[model.id] = {
17375
- name: model.name,
17376
- description: model.description,
17377
- priority: model.priority,
17378
- provider: model.provider
17379
- };
17380
- }
17381
- modelInfo.custom = {
17382
- name: "Custom Model",
17383
- description: "Enter any OpenRouter model ID manually",
17384
- priority: 999,
17385
- provider: "Custom"
17386
- };
17387
- _cachedModelInfo = modelInfo;
17388
- return modelInfo;
17389
- }
17390
- function getAvailableModels() {
17391
- if (_cachedModelIds) {
17392
- return _cachedModelIds;
17393
- }
17394
- const data = loadRecommendedModelsJSON();
17395
- const modelIds = data.models.sort((a, b) => a.priority - b.priority).map((m) => m.id);
17396
- const result = [...modelIds, "custom"];
17397
- _cachedModelIds = result;
17398
- return result;
17399
- }
17400
- function getCachedOpenRouterModels() {
17401
- return _cachedOpenRouterModels;
17402
- }
17403
- async function ensureOpenRouterModelsLoaded() {
17404
- if (_cachedOpenRouterModels)
17405
- return _cachedOpenRouterModels;
17406
- try {
17407
- const response = await fetch("https://openrouter.ai/api/v1/models");
17408
- if (response.ok) {
17409
- const data = await response.json();
17410
- _cachedOpenRouterModels = data.data || [];
17411
- return _cachedOpenRouterModels;
17412
- }
17413
- } catch {}
17414
- return [];
17415
- }
17416
- async function fetchLiteLLMModels(baseUrl, apiKey, forceUpdate = false) {
17417
- const hash = createHash("sha256").update(baseUrl).digest("hex").substring(0, 16);
17418
- const cacheDir = join4(homedir3(), ".claudish");
17419
- const cachePath = join4(cacheDir, `litellm-models-${hash}.json`);
17420
- if (!forceUpdate && existsSync3(cachePath)) {
17421
- try {
17422
- const cacheData = JSON.parse(readFileSync2(cachePath, "utf-8"));
17423
- const timestamp = new Date(cacheData.timestamp);
17424
- const now = new Date;
17425
- const ageInHours = (now.getTime() - timestamp.getTime()) / (1000 * 60 * 60);
17426
- if (ageInHours < LITELLM_CACHE_MAX_AGE_HOURS) {
17427
- return cacheData.models;
17428
- }
17429
- } catch {}
17430
- }
17431
- try {
17432
- const url = `${baseUrl.replace(/\/$/, "")}/model_group/info`;
17433
- const response = await fetch(url, {
17434
- headers: {
17435
- Authorization: `Bearer ${apiKey}`
17436
- },
17437
- signal: AbortSignal.timeout(1e4)
17438
- });
17439
- if (!response.ok) {
17440
- console.error(`Failed to fetch LiteLLM models: ${response.status} ${response.statusText}`);
17441
- if (existsSync3(cachePath)) {
17442
- try {
17443
- const cacheData2 = JSON.parse(readFileSync2(cachePath, "utf-8"));
17444
- return cacheData2.models;
17445
- } catch {
17446
- return [];
17447
- }
17448
- }
17449
- return [];
17450
- }
17451
- const responseData = await response.json();
17452
- const rawModels = responseData.data || responseData;
17453
- const transformedModels = rawModels.filter((m) => m.mode === "chat" && m.supports_function_calling).map((m) => {
17454
- const inputCostPerM = (m.input_cost_per_token || 0) * 1e6;
17455
- const outputCostPerM = (m.output_cost_per_token || 0) * 1e6;
17456
- const avgCost = (inputCostPerM + outputCostPerM) / 2;
17457
- const isFree = inputCostPerM === 0 && outputCostPerM === 0;
17458
- const contextLength = m.max_input_tokens || 128000;
17459
- const contextStr = contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`;
17460
- return {
17461
- id: `litellm@${m.model_group}`,
17462
- name: m.model_group,
17463
- description: `LiteLLM model (providers: ${m.providers.join(", ")})`,
17464
- provider: "LiteLLM",
17465
- pricing: {
17466
- input: isFree ? "FREE" : `$${inputCostPerM.toFixed(2)}`,
17467
- output: isFree ? "FREE" : `$${outputCostPerM.toFixed(2)}`,
17468
- average: isFree ? "FREE" : `$${avgCost.toFixed(2)}/1M`
17469
- },
17470
- context: contextStr,
17471
- contextLength,
17472
- supportsTools: m.supports_function_calling || false,
17473
- supportsReasoning: m.supports_reasoning || false,
17474
- supportsVision: m.supports_vision || false,
17475
- isFree,
17476
- source: "LiteLLM"
17477
- };
17478
- });
17479
- mkdirSync4(cacheDir, { recursive: true });
17480
- const cacheData = {
17481
- timestamp: new Date().toISOString(),
17482
- models: transformedModels
17483
- };
17484
- writeFileSync4(cachePath, JSON.stringify(cacheData, null, 2), "utf-8");
17485
- return transformedModels;
17486
- } catch (error2) {
17487
- console.error(`Failed to fetch LiteLLM models: ${error2}`);
17488
- if (existsSync3(cachePath)) {
17489
- try {
17490
- const cacheData = JSON.parse(readFileSync2(cachePath, "utf-8"));
17491
- return cacheData.models;
17492
- } catch {
17493
- return [];
17494
- }
17495
- }
17496
- return [];
17497
- }
17498
- }
17499
- var __filename2, __dirname2, _cachedModelInfo = null, _cachedModelIds = null, _cachedRecommendedModels = null, RECOMMENDED_CACHE_PATH, RECOMMENDED_CACHE_MAX_AGE_HOURS = 12, _cachedOpenRouterModels = null, LITELLM_CACHE_MAX_AGE_HOURS = 24;
17500
- var init_model_loader = __esm(() => {
17501
- __filename2 = fileURLToPath(import.meta.url);
17502
- __dirname2 = dirname(__filename2);
17503
- RECOMMENDED_CACHE_PATH = join4(homedir3(), ".claudish", "recommended-models-cache.json");
17504
- });
17505
-
17506
17324
  // src/providers/transport/openrouter.ts
17507
17325
  class OpenRouterProviderTransport {
17508
17326
  name = "openrouter";
@@ -17510,10 +17328,8 @@ class OpenRouterProviderTransport {
17510
17328
  streamFormat = "openai-sse";
17511
17329
  apiKey;
17512
17330
  queue;
17513
- modelId;
17514
- constructor(apiKey, modelId) {
17331
+ constructor(apiKey, _modelId) {
17515
17332
  this.apiKey = apiKey;
17516
- this.modelId = modelId || "";
17517
17333
  this.queue = OpenRouterRequestQueue.getInstance();
17518
17334
  }
17519
17335
  overrideStreamFormat() {
@@ -17533,15 +17349,12 @@ class OpenRouterProviderTransport {
17533
17349
  return this.queue.enqueue(fetchFn);
17534
17350
  }
17535
17351
  getContextWindow() {
17536
- const models = this.modelId ? getCachedOpenRouterModels() : null;
17537
- const model = models?.find((m) => m.id === this.modelId);
17538
- return model?.context_length || model?.top_provider?.context_length || 0;
17352
+ return 0;
17539
17353
  }
17540
17354
  }
17541
17355
  var OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions";
17542
17356
  var init_openrouter = __esm(() => {
17543
17357
  init_openrouter_queue();
17544
- init_model_loader();
17545
17358
  });
17546
17359
 
17547
17360
  // src/adapters/tool-name-utils.ts
@@ -17621,11 +17434,7 @@ var init_remote_provider_types = __esm(() => {
17621
17434
  // src/adapters/model-catalog.ts
17622
17435
  function lookupModel(modelId) {
17623
17436
  const lower = modelId.toLowerCase();
17624
- let unprefixed = lower;
17625
- if (lower.includes("@"))
17626
- unprefixed = lower.substring(lower.indexOf("@") + 1);
17627
- else if (lower.includes("/"))
17628
- unprefixed = lower.substring(lower.lastIndexOf("/") + 1);
17437
+ const unprefixed = lower.includes("/") ? lower.substring(lower.lastIndexOf("/") + 1) : lower;
17629
17438
  for (const entry of MODEL_CATALOG) {
17630
17439
  if (unprefixed.includes(entry.pattern) || lower.includes(entry.pattern)) {
17631
17440
  return entry;
@@ -17975,7 +17784,7 @@ var init_openai_tools = __esm(() => {
17975
17784
  function matchesModelFamily(modelId, family) {
17976
17785
  const lower = modelId.toLowerCase();
17977
17786
  const fam = family.toLowerCase();
17978
- return lower.startsWith(fam) || lower.includes(`/${fam}`) || lower.includes(`@${fam}`);
17787
+ return lower.startsWith(fam) || lower.includes(`/${fam}`);
17979
17788
  }
17980
17789
 
17981
17790
  class BaseAPIFormat {
@@ -20949,9 +20758,9 @@ var init_middleware = __esm(() => {
20949
20758
  });
20950
20759
 
20951
20760
  // src/handlers/shared/token-tracker.ts
20952
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
20953
- import { homedir as homedir4 } from "os";
20954
- import { join as join5 } from "path";
20761
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
20762
+ import { homedir as homedir3 } from "os";
20763
+ import { join as join4 } from "path";
20955
20764
 
20956
20765
  class TokenTracker {
20957
20766
  port;
@@ -21085,9 +20894,9 @@ class TokenTracker {
21085
20894
  if (this.quotaRemaining !== undefined) {
21086
20895
  data.quota_remaining = this.quotaRemaining;
21087
20896
  }
21088
- const claudishDir = join5(homedir4(), ".claudish");
21089
- mkdirSync5(claudishDir, { recursive: true });
21090
- writeFileSync5(join5(claudishDir, `tokens-${this.port}.json`), JSON.stringify(data), "utf-8");
20897
+ const claudishDir = join4(homedir3(), ".claudish");
20898
+ mkdirSync4(claudishDir, { recursive: true });
20899
+ writeFileSync4(join4(claudishDir, `tokens-${this.port}.json`), JSON.stringify(data), "utf-8");
21091
20900
  } catch (e) {
21092
20901
  log(`[TokenTracker] Error writing token file: ${e}`);
21093
20902
  }
@@ -22041,21 +21850,21 @@ __export(exports_profile_config, {
22041
21850
  configExistsForScope: () => configExistsForScope,
22042
21851
  configExists: () => configExists
22043
21852
  });
22044
- import { existsSync as existsSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
22045
- import { homedir as homedir5 } from "os";
22046
- import { join as join6 } from "path";
21853
+ import { existsSync as existsSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync5 } from "fs";
21854
+ import { homedir as homedir4 } from "os";
21855
+ import { join as join5 } from "path";
22047
21856
  function ensureConfigDir() {
22048
- if (!existsSync4(CONFIG_DIR)) {
22049
- mkdirSync6(CONFIG_DIR, { recursive: true });
21857
+ if (!existsSync3(CONFIG_DIR)) {
21858
+ mkdirSync5(CONFIG_DIR, { recursive: true });
22050
21859
  }
22051
21860
  }
22052
21861
  function loadConfig() {
22053
21862
  ensureConfigDir();
22054
- if (!existsSync4(CONFIG_FILE)) {
21863
+ if (!existsSync3(CONFIG_FILE)) {
22055
21864
  return { ...DEFAULT_CONFIG };
22056
21865
  }
22057
21866
  try {
22058
- const content = readFileSync3(CONFIG_FILE, "utf-8");
21867
+ const content = readFileSync2(CONFIG_FILE, "utf-8");
22059
21868
  const config2 = JSON.parse(content);
22060
21869
  const merged = {
22061
21870
  version: config2.version || DEFAULT_CONFIG.version,
@@ -22088,31 +21897,31 @@ function loadConfig() {
22088
21897
  }
22089
21898
  function saveConfig(config2) {
22090
21899
  ensureConfigDir();
22091
- writeFileSync6(CONFIG_FILE, JSON.stringify(config2, null, 2), "utf-8");
21900
+ writeFileSync5(CONFIG_FILE, JSON.stringify(config2, null, 2), "utf-8");
22092
21901
  }
22093
21902
  function configExists() {
22094
- return existsSync4(CONFIG_FILE);
21903
+ return existsSync3(CONFIG_FILE);
22095
21904
  }
22096
21905
  function getConfigPath() {
22097
21906
  return CONFIG_FILE;
22098
21907
  }
22099
21908
  function getLocalConfigPath() {
22100
- return join6(process.cwd(), LOCAL_CONFIG_FILENAME);
21909
+ return join5(process.cwd(), LOCAL_CONFIG_FILENAME);
22101
21910
  }
22102
21911
  function localConfigExists() {
22103
- return existsSync4(getLocalConfigPath());
21912
+ return existsSync3(getLocalConfigPath());
22104
21913
  }
22105
21914
  function isProjectDirectory() {
22106
21915
  const cwd = process.cwd();
22107
- return [".git", "package.json", "Cargo.toml", "go.mod", "pyproject.toml", ".claudish.json"].some((f) => existsSync4(join6(cwd, f)));
21916
+ return [".git", "package.json", "Cargo.toml", "go.mod", "pyproject.toml", ".claudish.json"].some((f) => existsSync3(join5(cwd, f)));
22108
21917
  }
22109
21918
  function loadLocalConfig() {
22110
21919
  const localPath = getLocalConfigPath();
22111
- if (!existsSync4(localPath)) {
21920
+ if (!existsSync3(localPath)) {
22112
21921
  return null;
22113
21922
  }
22114
21923
  try {
22115
- const content = readFileSync3(localPath, "utf-8");
21924
+ const content = readFileSync2(localPath, "utf-8");
22116
21925
  const config2 = JSON.parse(content);
22117
21926
  const local = {
22118
21927
  version: config2.version || DEFAULT_CONFIG.version,
@@ -22129,7 +21938,7 @@ function loadLocalConfig() {
22129
21938
  }
22130
21939
  }
22131
21940
  function saveLocalConfig(config2) {
22132
- writeFileSync6(getLocalConfigPath(), JSON.stringify(config2, null, 2), "utf-8");
21941
+ writeFileSync5(getLocalConfigPath(), JSON.stringify(config2, null, 2), "utf-8");
22133
21942
  }
22134
21943
  function loadConfigForScope(scope) {
22135
21944
  if (scope === "local") {
@@ -22349,8 +22158,8 @@ function removeEndpoint(name) {
22349
22158
  }
22350
22159
  var CONFIG_DIR, CONFIG_FILE, LOCAL_CONFIG_FILENAME = ".claudish.json", DEFAULT_CONFIG;
22351
22160
  var init_profile_config = __esm(() => {
22352
- CONFIG_DIR = join6(homedir5(), ".claudish");
22353
- CONFIG_FILE = join6(CONFIG_DIR, "config.json");
22161
+ CONFIG_DIR = join5(homedir4(), ".claudish");
22162
+ CONFIG_FILE = join5(CONFIG_DIR, "config.json");
22354
22163
  DEFAULT_CONFIG = {
22355
22164
  version: "1.0.0",
22356
22165
  defaultProfile: "default",
@@ -22367,11 +22176,12 @@ var init_profile_config = __esm(() => {
22367
22176
  });
22368
22177
 
22369
22178
  // src/version.ts
22370
- var VERSION = "6.13.1";
22179
+ var VERSION = "6.14.0";
22371
22180
 
22372
22181
  // src/telemetry.ts
22373
22182
  var exports_telemetry = {};
22374
22183
  __export(exports_telemetry, {
22184
+ setClaudeCodeRunning: () => setClaudeCodeRunning,
22375
22185
  sanitizeModelId: () => sanitizeModelId,
22376
22186
  sanitizeMessage: () => sanitizeMessage,
22377
22187
  runConsentPrompt: () => runConsentPrompt,
@@ -22581,6 +22391,8 @@ async function sendReport(report) {
22581
22391
  function showConsentPromptAsync(ctx) {
22582
22392
  if (consentPromptActive)
22583
22393
  return;
22394
+ if (claudeCodeRunning)
22395
+ return;
22584
22396
  try {
22585
22397
  const profileConfig = loadConfig();
22586
22398
  if (profileConfig.telemetry?.askedAt !== undefined)
@@ -22654,9 +22466,12 @@ function initTelemetry(config2) {
22654
22466
  claudishVersion = getVersion();
22655
22467
  installMethod = detectInstallMethod();
22656
22468
  }
22469
+ function setClaudeCodeRunning(running) {
22470
+ claudeCodeRunning = running;
22471
+ }
22657
22472
  function reportError2(ctx) {
22658
22473
  if (!initialized || !consentEnabled) {
22659
- if (initialized && !consentEnabled && ctx.isInteractive && process.stderr.isTTY) {
22474
+ if (initialized && !consentEnabled && ctx.isInteractive && process.stderr.isTTY && !claudeCodeRunning) {
22660
22475
  showConsentPromptAsync(ctx);
22661
22476
  }
22662
22477
  return;
@@ -22759,7 +22574,7 @@ Usage: claudish telemetry on|off|status|reset
22759
22574
  process.exit(1);
22760
22575
  }
22761
22576
  }
22762
- var TELEMETRY_ENDPOINT = "https://claudish.com/v1/report", MAX_REPORT_BYTES = 4096, KNOWN_PUBLIC_HOSTS, PUBLIC_PROVIDERS, consentEnabled = false, sessionId = "", initialized = false, claudishVersion = "", installMethod = "unknown", consentPromptActive = false;
22577
+ var TELEMETRY_ENDPOINT = "https://claudish.com/v1/report", MAX_REPORT_BYTES = 4096, KNOWN_PUBLIC_HOSTS, PUBLIC_PROVIDERS, consentEnabled = false, sessionId = "", initialized = false, claudishVersion = "", installMethod = "unknown", consentPromptActive = false, claudeCodeRunning = false;
22763
22578
  var init_telemetry = __esm(() => {
22764
22579
  init_profile_config();
22765
22580
  init_logger();
@@ -22791,9 +22606,9 @@ var init_telemetry = __esm(() => {
22791
22606
  });
22792
22607
 
22793
22608
  // src/providers/provider-definitions.ts
22794
- import { existsSync as existsSync5 } from "fs";
22795
- import { join as join7 } from "path";
22796
- import { homedir as homedir6 } from "os";
22609
+ import { existsSync as existsSync4 } from "fs";
22610
+ import { join as join6 } from "path";
22611
+ import { homedir as homedir5 } from "os";
22797
22612
  function ensureProviderByNameCache() {
22798
22613
  if (!_providerByNameCache) {
22799
22614
  _providerByNameCache = new Map;
@@ -22933,7 +22748,7 @@ function isProviderAvailable(def) {
22933
22748
  }
22934
22749
  if (def.oauthFallback) {
22935
22750
  try {
22936
- if (existsSync5(join7(homedir6(), ".claudish", def.oauthFallback)))
22751
+ if (existsSync4(join6(homedir5(), ".claudish", def.oauthFallback)))
22937
22752
  return true;
22938
22753
  } catch {}
22939
22754
  }
@@ -23552,25 +23367,25 @@ var init_model_parser = __esm(() => {
23552
23367
 
23553
23368
  // src/stats-buffer.ts
23554
23369
  import {
23555
- existsSync as existsSync6,
23556
- mkdirSync as mkdirSync7,
23557
- readFileSync as readFileSync4,
23370
+ existsSync as existsSync5,
23371
+ mkdirSync as mkdirSync6,
23372
+ readFileSync as readFileSync3,
23558
23373
  renameSync,
23559
23374
  unlinkSync as unlinkSync2,
23560
- writeFileSync as writeFileSync7
23375
+ writeFileSync as writeFileSync6
23561
23376
  } from "fs";
23562
- import { homedir as homedir7 } from "os";
23563
- import { join as join8 } from "path";
23377
+ import { homedir as homedir6 } from "os";
23378
+ import { join as join7 } from "path";
23564
23379
  function ensureDir() {
23565
- if (!existsSync6(CLAUDISH_DIR)) {
23566
- mkdirSync7(CLAUDISH_DIR, { recursive: true });
23380
+ if (!existsSync5(CLAUDISH_DIR)) {
23381
+ mkdirSync6(CLAUDISH_DIR, { recursive: true });
23567
23382
  }
23568
23383
  }
23569
23384
  function readFromDisk() {
23570
23385
  try {
23571
- if (!existsSync6(BUFFER_FILE))
23386
+ if (!existsSync5(BUFFER_FILE))
23572
23387
  return [];
23573
- const raw2 = readFileSync4(BUFFER_FILE, "utf-8");
23388
+ const raw2 = readFileSync3(BUFFER_FILE, "utf-8");
23574
23389
  const parsed = JSON.parse(raw2);
23575
23390
  if (!Array.isArray(parsed.events))
23576
23391
  return [];
@@ -23594,8 +23409,8 @@ function writeToDisk(events) {
23594
23409
  ensureDir();
23595
23410
  const trimmed = enforceSizeCap([...events]);
23596
23411
  const payload = { version: 1, events: trimmed };
23597
- const tmpFile = join8(CLAUDISH_DIR, `stats-buffer.tmp.${process.pid}.json`);
23598
- writeFileSync7(tmpFile, JSON.stringify(payload, null, 2), "utf-8");
23412
+ const tmpFile = join7(CLAUDISH_DIR, `stats-buffer.tmp.${process.pid}.json`);
23413
+ writeFileSync6(tmpFile, JSON.stringify(payload, null, 2), "utf-8");
23599
23414
  renameSync(tmpFile, BUFFER_FILE);
23600
23415
  memoryCache = trimmed;
23601
23416
  } catch {}
@@ -23639,7 +23454,7 @@ function clearBuffer() {
23639
23454
  try {
23640
23455
  memoryCache = [];
23641
23456
  eventsSinceLastFlush = 0;
23642
- if (existsSync6(BUFFER_FILE)) {
23457
+ if (existsSync5(BUFFER_FILE)) {
23643
23458
  unlinkSync2(BUFFER_FILE);
23644
23459
  }
23645
23460
  } catch {}
@@ -23668,8 +23483,8 @@ function syncFlushOnExit() {
23668
23483
  var BUFFER_MAX_BYTES, CLAUDISH_DIR, BUFFER_FILE, memoryCache = null, eventsSinceLastFlush = 0, lastFlushTime, flushScheduled = false;
23669
23484
  var init_stats_buffer = __esm(() => {
23670
23485
  BUFFER_MAX_BYTES = 64 * 1024;
23671
- CLAUDISH_DIR = join8(homedir7(), ".claudish");
23672
- BUFFER_FILE = join8(CLAUDISH_DIR, "stats-buffer.json");
23486
+ CLAUDISH_DIR = join7(homedir6(), ".claudish");
23487
+ BUFFER_FILE = join7(CLAUDISH_DIR, "stats-buffer.json");
23673
23488
  lastFlushTime = Date.now();
23674
23489
  process.on("exit", syncFlushOnExit);
23675
23490
  process.on("SIGTERM", () => {
@@ -24115,29 +23930,34 @@ class ComposedHandler {
24115
23930
  middlewareManager;
24116
23931
  tokenTracker;
24117
23932
  targetModel;
23933
+ bareModelName;
24118
23934
  options;
24119
23935
  isInteractive;
24120
23936
  pendingFallbackMeta;
24121
23937
  constructor(provider, targetModel, modelName, port, options = {}) {
23938
+ if (modelName.includes("@")) {
23939
+ throw new Error(`ComposedHandler: modelName must not contain '@' (got "${modelName}"). ` + `Strip the provider routing prefix before passing modelName. ` + `If you need the full routed form, pass it as targetModel.`);
23940
+ }
24122
23941
  this.provider = provider;
24123
23942
  this.targetModel = targetModel;
23943
+ this.bareModelName = modelName;
24124
23944
  this.options = options;
24125
23945
  this.explicitAdapter = options.adapter;
24126
23946
  this.isInteractive = options.isInteractive ?? false;
24127
- this.adapterManager = new DialectManager(targetModel);
23947
+ this.adapterManager = new DialectManager(this.bareModelName);
24128
23948
  const resolvedModelAdapter = this.adapterManager.getAdapter();
24129
23949
  if (resolvedModelAdapter.getName() !== "DefaultAPIFormat") {
24130
23950
  this.modelAdapter = resolvedModelAdapter;
24131
23951
  }
24132
23952
  this.middlewareManager = new MiddlewareManager;
24133
- if (targetModel.includes("gemini") || targetModel.includes("google/")) {
23953
+ if (this.bareModelName.includes("gemini") || this.bareModelName.includes("google/")) {
24134
23954
  this.middlewareManager.register(new GeminiThoughtSignatureMiddleware);
24135
23955
  }
24136
- this.middlewareManager.initialize().catch((err) => log(`[ComposedHandler:${targetModel}] Middleware init error: ${err}`));
23956
+ this.middlewareManager.initialize().catch((err) => log(`[ComposedHandler:${this.bareModelName}] Middleware init error: ${err}`));
24137
23957
  this.tokenTracker = new TokenTracker(port, {
24138
23958
  contextWindow: this.getModelContextWindow(),
24139
23959
  providerName: provider.name,
24140
- modelName,
23960
+ modelName: this.bareModelName,
24141
23961
  providerDisplayName: provider.displayName
24142
23962
  });
24143
23963
  }
@@ -24166,9 +23986,9 @@ class ComposedHandler {
24166
23986
  adapter.reset();
24167
23987
  const messages = adapter.convertMessages(claudeRequest, filterIdentity);
24168
23988
  let tools = adapter.convertTools(claudeRequest, this.options.summarizeTools);
24169
- const maxToolCount = lookupModel(this.targetModel)?.maxToolCount;
23989
+ const maxToolCount = lookupModel(this.bareModelName)?.maxToolCount;
24170
23990
  if (maxToolCount && tools.length > maxToolCount) {
24171
- log(`[ComposedHandler] Truncating tools from ${tools.length} to ${maxToolCount} (model limit for ${this.targetModel})`);
23991
+ log(`[ComposedHandler] Truncating tools from ${tools.length} to ${maxToolCount} (model limit for ${this.bareModelName})`);
24172
23992
  tools = tools.slice(0, maxToolCount);
24173
23993
  }
24174
23994
  if (!this.getModelSupportsVision()) {
@@ -24293,7 +24113,7 @@ class ComposedHandler {
24293
24113
  requestPayload = this.provider.transformPayload(requestPayload);
24294
24114
  }
24295
24115
  await this.middlewareManager.beforeRequest({
24296
- modelId: this.targetModel,
24116
+ modelId: this.bareModelName,
24297
24117
  messages,
24298
24118
  tools,
24299
24119
  stream: true
@@ -24341,7 +24161,7 @@ class ComposedHandler {
24341
24161
  error_code,
24342
24162
  token_strategy: this.options.tokenStrategy ?? "standard",
24343
24163
  adapter_name: this.getActiveAdapterName(),
24344
- middleware_names: this.middlewareManager.getActiveNames(this.targetModel),
24164
+ middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
24345
24165
  fallback_used: fallbackMeta !== undefined,
24346
24166
  fallback_chain: fallbackMeta?.chain,
24347
24167
  fallback_attempts: fallbackMeta?.attempts,
@@ -24403,7 +24223,7 @@ class ComposedHandler {
24403
24223
  error_code,
24404
24224
  token_strategy: this.options.tokenStrategy ?? "standard",
24405
24225
  adapter_name: this.getActiveAdapterName(),
24406
- middleware_names: this.middlewareManager.getActiveNames(this.targetModel),
24226
+ middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
24407
24227
  fallback_used: fallbackMeta !== undefined,
24408
24228
  fallback_chain: fallbackMeta?.chain,
24409
24229
  fallback_attempts: fallbackMeta?.attempts,
@@ -24440,7 +24260,7 @@ class ComposedHandler {
24440
24260
  error_code,
24441
24261
  token_strategy: this.options.tokenStrategy ?? "standard",
24442
24262
  adapter_name: this.getActiveAdapterName(),
24443
- middleware_names: this.middlewareManager.getActiveNames(this.targetModel),
24263
+ middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
24444
24264
  fallback_used: fallbackMeta !== undefined,
24445
24265
  fallback_chain: fallbackMeta?.chain,
24446
24266
  fallback_attempts: fallbackMeta?.attempts,
@@ -24487,7 +24307,7 @@ class ComposedHandler {
24487
24307
  error_code,
24488
24308
  token_strategy: this.options.tokenStrategy ?? "standard",
24489
24309
  adapter_name: this.getActiveAdapterName(),
24490
- middleware_names: this.middlewareManager.getActiveNames(this.targetModel),
24310
+ middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
24491
24311
  fallback_used: fallbackMeta !== undefined,
24492
24312
  fallback_chain: fallbackMeta?.chain,
24493
24313
  fallback_attempts: fallbackMeta?.attempts,
@@ -24524,7 +24344,7 @@ class ComposedHandler {
24524
24344
  is_free_model: isFreeModel,
24525
24345
  token_strategy: this.options.tokenStrategy ?? "standard",
24526
24346
  adapter_name: this.getActiveAdapterName(),
24527
- middleware_names: this.middlewareManager.getActiveNames(this.targetModel),
24347
+ middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
24528
24348
  fallback_used: fallbackMeta !== undefined,
24529
24349
  fallback_chain: fallbackMeta?.chain,
24530
24350
  fallback_attempts: fallbackMeta?.attempts,
@@ -24559,19 +24379,19 @@ class ComposedHandler {
24559
24379
  onComplete = undefined;
24560
24380
  }
24561
24381
  };
24562
- const streamFormat = this.provider.overrideStreamFormat?.() ?? this.modelAdapter?.getStreamFormat() ?? this.getAdapter().getStreamFormat();
24382
+ const streamFormat = this.provider.overrideStreamFormat?.() ?? (this.explicitAdapter?.getStreamFormat() ?? this.modelAdapter?.getStreamFormat()) ?? this.getAdapter().getStreamFormat();
24563
24383
  switch (streamFormat) {
24564
24384
  case "openai-sse":
24565
- return createStreamingResponseHandler(c, response, adapter, this.targetModel, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap);
24385
+ return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap);
24566
24386
  case "openai-responses-sse":
24567
24387
  return createResponsesStreamHandler(c, response, {
24568
- modelName: this.targetModel,
24388
+ modelName: this.bareModelName,
24569
24389
  onTokenUpdate,
24570
24390
  toolNameMap: adapter.getToolNameMap()
24571
24391
  });
24572
24392
  case "anthropic-sse":
24573
24393
  return createAnthropicPassthroughStream(c, response, {
24574
- modelName: this.targetModel,
24394
+ modelName: this.bareModelName,
24575
24395
  onTokenUpdate
24576
24396
  });
24577
24397
  case "gemini-sse": {
@@ -24581,7 +24401,7 @@ class ComposedHandler {
24581
24401
  }
24582
24402
  };
24583
24403
  return createGeminiSseStream(c, response, {
24584
- modelName: this.targetModel,
24404
+ modelName: this.bareModelName,
24585
24405
  adapter,
24586
24406
  middlewareManager: this.middlewareManager,
24587
24407
  onTokenUpdate,
@@ -24591,7 +24411,7 @@ class ComposedHandler {
24591
24411
  }
24592
24412
  case "ollama-jsonl":
24593
24413
  return createOllamaJsonlStream(c, response, {
24594
- modelName: this.targetModel,
24414
+ modelName: this.bareModelName,
24595
24415
  onTokenUpdate
24596
24416
  });
24597
24417
  default:
@@ -24606,8 +24426,7 @@ class ComposedHandler {
24606
24426
  const fn = this.provider.getQuotaRemaining;
24607
24427
  if (typeof fn !== "function")
24608
24428
  return;
24609
- const bareModel = this.targetModel.includes("@") ? this.targetModel.split("@")[1] : this.targetModel;
24610
- const remaining = await fn.call(this.provider, bareModel);
24429
+ const remaining = await fn.call(this.provider, this.bareModelName);
24611
24430
  if (typeof remaining === "number") {
24612
24431
  this.tokenTracker.setQuotaRemaining(remaining);
24613
24432
  this.tokenTracker.rewrite();
@@ -24863,18 +24682,18 @@ var init_remote_provider_registry = __esm(() => {
24863
24682
  });
24864
24683
 
24865
24684
  // src/auth/oauth-registry.ts
24866
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
24867
- import { join as join9 } from "path";
24868
- import { homedir as homedir8 } from "os";
24685
+ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
24686
+ import { join as join8 } from "path";
24687
+ import { homedir as homedir7 } from "os";
24869
24688
  function hasValidOAuthCredentials(descriptor) {
24870
- const credPath = join9(homedir8(), ".claudish", descriptor.credentialFile);
24871
- if (!existsSync7(credPath))
24689
+ const credPath = join8(homedir7(), ".claudish", descriptor.credentialFile);
24690
+ if (!existsSync6(credPath))
24872
24691
  return false;
24873
24692
  if (descriptor.validationMode === "file-exists") {
24874
24693
  return true;
24875
24694
  }
24876
24695
  try {
24877
- const data = JSON.parse(readFileSync5(credPath, "utf-8"));
24696
+ const data = JSON.parse(readFileSync4(credPath, "utf-8"));
24878
24697
  if (!data.access_token)
24879
24698
  return false;
24880
24699
  if (data.refresh_token)
@@ -24961,11 +24780,49 @@ var init_static_fallback = __esm(() => {
24961
24780
  };
24962
24781
  });
24963
24782
 
24964
- // src/providers/catalog-resolvers/openrouter.ts
24965
- import { readFileSync as readFileSync6, existsSync as existsSync8, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "fs";
24966
- import { join as join10 } from "path";
24967
- import { homedir as homedir9 } from "os";
24783
+ // src/providers/all-models-cache.ts
24784
+ import { readFileSync as readFileSync5, existsSync as existsSync7, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
24785
+ import { join as join9, dirname } from "path";
24786
+ import { homedir as homedir8 } from "os";
24787
+ function readAllModelsCache(path = ALL_MODELS_CACHE_PATH) {
24788
+ if (!existsSync7(path))
24789
+ return null;
24790
+ let raw2;
24791
+ try {
24792
+ raw2 = JSON.parse(readFileSync5(path, "utf-8"));
24793
+ } catch {
24794
+ return null;
24795
+ }
24796
+ if (!raw2 || typeof raw2 !== "object")
24797
+ return null;
24798
+ const data = raw2;
24799
+ const lastUpdated = typeof data.lastUpdated === "string" ? data.lastUpdated : new Date(0).toISOString();
24800
+ const models = Array.isArray(data.models) ? data.models : [];
24801
+ const entries = Array.isArray(data.entries) ? data.entries : [];
24802
+ return {
24803
+ version: 2,
24804
+ lastUpdated,
24805
+ entries,
24806
+ models
24807
+ };
24808
+ }
24809
+ function writeAllModelsCache(data, path = ALL_MODELS_CACHE_PATH) {
24810
+ const existing = readAllModelsCache(path);
24811
+ const merged = {
24812
+ version: 2,
24813
+ lastUpdated: data.lastUpdated ?? new Date().toISOString(),
24814
+ entries: data.entries ?? existing?.entries ?? [],
24815
+ models: data.models ?? existing?.models ?? []
24816
+ };
24817
+ mkdirSync7(dirname(path), { recursive: true });
24818
+ writeFileSync7(path, JSON.stringify(merged), "utf-8");
24819
+ }
24820
+ var ALL_MODELS_CACHE_PATH;
24821
+ var init_all_models_cache = __esm(() => {
24822
+ ALL_MODELS_CACHE_PATH = join9(homedir8(), ".claudish", "all-models.json");
24823
+ });
24968
24824
 
24825
+ // src/providers/catalog-resolvers/openrouter.ts
24969
24826
  class OpenRouterCatalogResolver {
24970
24827
  provider = "openrouter";
24971
24828
  resolveSync(userInput) {
@@ -25051,22 +24908,20 @@ class OpenRouterCatalogResolver {
25051
24908
  _getEntries() {
25052
24909
  if (_memCache)
25053
24910
  return _memCache;
25054
- if (existsSync8(DISK_CACHE_PATH)) {
25055
- try {
25056
- const data = JSON.parse(readFileSync6(DISK_CACHE_PATH, "utf-8"));
25057
- if (data.version === 2 && Array.isArray(data.entries) && data.entries.length > 0) {
25058
- _memCache = data.entries;
25059
- return _memCache;
25060
- }
25061
- if (Array.isArray(data.models) && data.models.length > 0) {
25062
- _memCache = data.models.map((m) => ({
25063
- modelId: m.id.includes("/") ? m.id.split("/").slice(1).join("/") : m.id,
25064
- aliases: [],
25065
- sources: { "openrouter-api": { externalId: m.id } }
25066
- }));
25067
- return _memCache;
25068
- }
25069
- } catch {}
24911
+ const cache = readAllModelsCache();
24912
+ if (!cache)
24913
+ return null;
24914
+ if (cache.entries.length > 0) {
24915
+ _memCache = cache.entries;
24916
+ return _memCache;
24917
+ }
24918
+ if (cache.models.length > 0) {
24919
+ _memCache = cache.models.map((m) => ({
24920
+ modelId: m.id.includes("/") ? m.id.split("/").slice(1).join("/") : m.id,
24921
+ aliases: [],
24922
+ sources: { "openrouter-api": { externalId: m.id } }
24923
+ }));
24924
+ return _memCache;
25070
24925
  }
25071
24926
  return null;
25072
24927
  }
@@ -25089,35 +24944,30 @@ class OpenRouterCatalogResolver {
25089
24944
  backwardCompatModels.push({ id: orSource.externalId });
25090
24945
  }
25091
24946
  }
25092
- const cacheDir = join10(homedir9(), ".claudish");
25093
- mkdirSync8(cacheDir, { recursive: true });
25094
- const diskData = {
25095
- version: 2,
25096
- lastUpdated: new Date().toISOString(),
24947
+ writeAllModelsCache({
25097
24948
  entries: data.models,
25098
24949
  models: backwardCompatModels
25099
- };
25100
- writeFileSync8(DISK_CACHE_PATH, JSON.stringify(diskData), "utf-8");
24950
+ });
25101
24951
  } catch {}
25102
24952
  }
25103
24953
  }
25104
- var FIREBASE_CATALOG_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000", DISK_CACHE_PATH, _memCache = null, _warmPromise = null;
24954
+ var FIREBASE_CATALOG_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000", _memCache = null, _warmPromise = null;
25105
24955
  var init_openrouter2 = __esm(() => {
25106
24956
  init_static_fallback();
25107
- DISK_CACHE_PATH = join10(homedir9(), ".claudish", "all-models.json");
24957
+ init_all_models_cache();
25108
24958
  });
25109
24959
 
25110
24960
  // src/providers/catalog-resolvers/litellm.ts
25111
- import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
25112
- import { join as join11 } from "path";
25113
- import { homedir as homedir10 } from "os";
25114
- import { createHash as createHash2 } from "crypto";
24961
+ import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
24962
+ import { join as join10 } from "path";
24963
+ import { homedir as homedir9 } from "os";
24964
+ import { createHash } from "crypto";
25115
24965
  function getCachePath() {
25116
24966
  const baseUrl = process.env.LITELLM_BASE_URL;
25117
24967
  if (!baseUrl)
25118
24968
  return null;
25119
- const hash = createHash2("sha256").update(baseUrl).digest("hex").substring(0, 16);
25120
- return join11(homedir10(), ".claudish", `litellm-models-${hash}.json`);
24969
+ const hash = createHash("sha256").update(baseUrl).digest("hex").substring(0, 16);
24970
+ return join10(homedir9(), ".claudish", `litellm-models-${hash}.json`);
25121
24971
  }
25122
24972
 
25123
24973
  class LiteLLMCatalogResolver {
@@ -25145,10 +24995,10 @@ class LiteLLMCatalogResolver {
25145
24995
  }
25146
24996
  async warmCache() {
25147
24997
  const path = getCachePath();
25148
- if (!path || !existsSync9(path))
24998
+ if (!path || !existsSync8(path))
25149
24999
  return;
25150
25000
  try {
25151
- const data = JSON.parse(readFileSync7(path, "utf-8"));
25001
+ const data = JSON.parse(readFileSync6(path, "utf-8"));
25152
25002
  if (Array.isArray(data.models)) {
25153
25003
  _memCache2 = data.models.map((m) => m.name ?? m.id?.replace("litellm@", "") ?? "");
25154
25004
  }
@@ -25165,10 +25015,10 @@ class LiteLLMCatalogResolver {
25165
25015
  if (_memCache2)
25166
25016
  return _memCache2;
25167
25017
  const path = getCachePath();
25168
- if (!path || !existsSync9(path))
25018
+ if (!path || !existsSync8(path))
25169
25019
  return null;
25170
25020
  try {
25171
- const data = JSON.parse(readFileSync7(path, "utf-8"));
25021
+ const data = JSON.parse(readFileSync6(path, "utf-8"));
25172
25022
  if (Array.isArray(data.models)) {
25173
25023
  _memCache2 = data.models.map((m) => m.name ?? m.id?.replace("litellm@", "") ?? "");
25174
25024
  return _memCache2;
@@ -25233,17 +25083,17 @@ var init_model_catalog_resolver = __esm(() => {
25233
25083
  });
25234
25084
 
25235
25085
  // src/providers/auto-route.ts
25236
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
25237
- import { join as join12 } from "path";
25238
- import { homedir as homedir11 } from "os";
25239
- import { createHash as createHash3 } from "crypto";
25086
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
25087
+ import { join as join11 } from "path";
25088
+ import { homedir as homedir10 } from "os";
25089
+ import { createHash as createHash2 } from "crypto";
25240
25090
  function readLiteLLMCacheSync(baseUrl) {
25241
- const hash = createHash3("sha256").update(baseUrl).digest("hex").substring(0, 16);
25242
- const cachePath = join12(homedir11(), ".claudish", `litellm-models-${hash}.json`);
25243
- if (!existsSync10(cachePath))
25091
+ const hash = createHash2("sha256").update(baseUrl).digest("hex").substring(0, 16);
25092
+ const cachePath = join11(homedir10(), ".claudish", `litellm-models-${hash}.json`);
25093
+ if (!existsSync9(cachePath))
25244
25094
  return null;
25245
25095
  try {
25246
- const data = JSON.parse(readFileSync8(cachePath, "utf-8"));
25096
+ const data = JSON.parse(readFileSync7(cachePath, "utf-8"));
25247
25097
  if (!Array.isArray(data.models))
25248
25098
  return null;
25249
25099
  return data.models;
@@ -25364,17 +25214,17 @@ async function warmZenModelCache() {
25364
25214
  const models = (data.data ?? []).map((m) => ({ id: m.id }));
25365
25215
  if (models.length === 0)
25366
25216
  return;
25367
- const cacheDir = join12(homedir11(), ".claudish");
25368
- const { mkdirSync: mkdirSync9, writeFileSync: writeSync } = await import("fs");
25369
- mkdirSync9(cacheDir, { recursive: true });
25370
- writeSync(join12(cacheDir, "zen-models.json"), JSON.stringify({ models, fetchedAt: new Date().toISOString() }));
25217
+ const cacheDir = join11(homedir10(), ".claudish");
25218
+ const { mkdirSync: mkdirSync8, writeFileSync: writeSync } = await import("fs");
25219
+ mkdirSync8(cacheDir, { recursive: true });
25220
+ writeSync(join11(cacheDir, "zen-models.json"), JSON.stringify({ models, fetchedAt: new Date().toISOString() }));
25371
25221
  }
25372
25222
  function readZenGoModelCacheSync() {
25373
- const cachePath = join12(homedir11(), ".claudish", "zen-go-models.json");
25374
- if (!existsSync10(cachePath))
25223
+ const cachePath = join11(homedir10(), ".claudish", "zen-go-models.json");
25224
+ if (!existsSync9(cachePath))
25375
25225
  return null;
25376
25226
  try {
25377
- const data = JSON.parse(readFileSync8(cachePath, "utf-8"));
25227
+ const data = JSON.parse(readFileSync7(cachePath, "utf-8"));
25378
25228
  if (!Array.isArray(data.models))
25379
25229
  return null;
25380
25230
  return new Set(data.models.map((m) => m.id));
@@ -25401,10 +25251,10 @@ async function warmZenGoModelCache() {
25401
25251
  const models = (data.data ?? []).map((m) => ({ id: m.id }));
25402
25252
  if (models.length === 0)
25403
25253
  return;
25404
- const cacheDir = join12(homedir11(), ".claudish");
25405
- const { mkdirSync: mkdirSync9, writeFileSync: writeSync } = await import("fs");
25406
- mkdirSync9(cacheDir, { recursive: true });
25407
- writeSync(join12(cacheDir, "zen-go-models.json"), JSON.stringify({ models, fetchedAt: new Date().toISOString() }));
25254
+ const cacheDir = join11(homedir10(), ".claudish");
25255
+ const { mkdirSync: mkdirSync8, writeFileSync: writeSync } = await import("fs");
25256
+ mkdirSync8(cacheDir, { recursive: true });
25257
+ writeSync(join11(cacheDir, "zen-go-models.json"), JSON.stringify({ models, fetchedAt: new Date().toISOString() }));
25408
25258
  }
25409
25259
  function hasProviderCredentials(provider) {
25410
25260
  const keyInfo = getApiKeyEnvVars(provider);
@@ -25541,9 +25391,9 @@ __export(exports_provider_resolver, {
25541
25391
  getMissingKeyResolutions: () => getMissingKeyResolutions,
25542
25392
  getMissingKeyError: () => getMissingKeyError
25543
25393
  });
25544
- import { existsSync as existsSync11 } from "fs";
25545
- import { join as join13 } from "path";
25546
- import { homedir as homedir12 } from "os";
25394
+ import { existsSync as existsSync10 } from "fs";
25395
+ import { join as join12 } from "path";
25396
+ import { homedir as homedir11 } from "os";
25547
25397
  function getApiKeyInfoForProvider(providerName) {
25548
25398
  const lookupName = providerName === "gemini" ? "google" : providerName;
25549
25399
  const info = getApiKeyInfo(lookupName);
@@ -25578,8 +25428,8 @@ function isApiKeyAvailable(info) {
25578
25428
  }
25579
25429
  if (info.oauthFallback) {
25580
25430
  try {
25581
- const credPath = join13(homedir12(), ".claudish", info.oauthFallback);
25582
- if (existsSync11(credPath)) {
25431
+ const credPath = join12(homedir11(), ".claudish", info.oauthFallback);
25432
+ if (existsSync10(credPath)) {
25583
25433
  return true;
25584
25434
  }
25585
25435
  } catch {}
@@ -25880,9 +25730,9 @@ var init_provider_resolver = __esm(() => {
25880
25730
  });
25881
25731
 
25882
25732
  // src/services/pricing-cache.ts
25883
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, existsSync as existsSync12, mkdirSync as mkdirSync9, statSync } from "fs";
25884
- import { homedir as homedir13 } from "os";
25885
- import { join as join14 } from "path";
25733
+ import { readFileSync as readFileSync8, existsSync as existsSync11, statSync } from "fs";
25734
+ import { homedir as homedir12 } from "os";
25735
+ import { join as join13 } from "path";
25886
25736
  function getDynamicPricingSync(provider, modelName) {
25887
25737
  if (provider === "openrouter") {
25888
25738
  const direct = pricingMap.get(modelName);
@@ -25923,36 +25773,21 @@ async function warmPricingCache() {
25923
25773
  const diskFresh = loadDiskCache();
25924
25774
  if (diskFresh) {
25925
25775
  log("[PricingCache] Loaded pricing from disk cache");
25926
- return;
25927
- }
25928
- log("[PricingCache] Disk cache stale or missing, fetching from OpenRouter API...");
25929
- const models = await ensureOpenRouterModelsLoaded();
25930
- if (models.length === 0) {
25931
- const cached2 = getCachedOpenRouterModels();
25932
- if (cached2 && cached2.length > 0) {
25933
- populateFromOpenRouterModels(cached2);
25934
- saveDiskCache();
25935
- log(`[PricingCache] Populated from existing model-loader cache (${pricingMap.size} models)`);
25936
- return;
25937
- }
25938
- log("[PricingCache] No models available, will use provider defaults");
25939
- return;
25776
+ } else {
25777
+ log("[PricingCache] Disk cache stale or missing, using provider defaults");
25940
25778
  }
25941
- populateFromOpenRouterModels(models);
25942
- saveDiskCache();
25943
- log(`[PricingCache] Fetched and cached pricing for ${pricingMap.size} models`);
25944
25779
  } catch (error2) {
25945
25780
  log(`[PricingCache] Error warming cache: ${error2}`);
25946
25781
  }
25947
25782
  }
25948
25783
  function loadDiskCache() {
25949
25784
  try {
25950
- if (!existsSync12(CACHE_FILE))
25785
+ if (!existsSync11(CACHE_FILE))
25951
25786
  return false;
25952
25787
  const stat = statSync(CACHE_FILE);
25953
25788
  const age = Date.now() - stat.mtimeMs;
25954
25789
  const isFresh = age < CACHE_TTL_MS;
25955
- const raw2 = readFileSync9(CACHE_FILE, "utf-8");
25790
+ const raw2 = readFileSync8(CACHE_FILE, "utf-8");
25956
25791
  const data = JSON.parse(raw2);
25957
25792
  for (const [key, pricing] of Object.entries(data)) {
25958
25793
  pricingMap.set(key, pricing);
@@ -25962,45 +25797,13 @@ function loadDiskCache() {
25962
25797
  return false;
25963
25798
  }
25964
25799
  }
25965
- function saveDiskCache() {
25966
- try {
25967
- mkdirSync9(CACHE_DIR, { recursive: true });
25968
- const data = {};
25969
- for (const [key, pricing] of pricingMap) {
25970
- data[key] = pricing;
25971
- }
25972
- writeFileSync9(CACHE_FILE, JSON.stringify(data), "utf-8");
25973
- } catch (error2) {
25974
- log(`[PricingCache] Error saving disk cache: ${error2}`);
25975
- }
25976
- }
25977
- function populateFromOpenRouterModels(models) {
25978
- for (const model of models) {
25979
- if (!model.id || !model.pricing)
25980
- continue;
25981
- const promptPrice = parseFloat(model.pricing.prompt || "0");
25982
- const completionPrice = parseFloat(model.pricing.completion || "0");
25983
- if (isNaN(promptPrice) || isNaN(completionPrice))
25984
- continue;
25985
- const inputCostPer1M = promptPrice * 1e6;
25986
- const outputCostPer1M = completionPrice * 1e6;
25987
- const isFree = inputCostPer1M === 0 && outputCostPer1M === 0;
25988
- pricingMap.set(model.id, {
25989
- inputCostPer1M,
25990
- outputCostPer1M,
25991
- isEstimate: true,
25992
- ...isFree ? { isFree: true } : {}
25993
- });
25994
- }
25995
- }
25996
25800
  var pricingMap, CACHE_DIR, CACHE_FILE, CACHE_TTL_MS, cacheWarmed = false, PROVIDER_TO_OR_PREFIX;
25997
25801
  var init_pricing_cache = __esm(() => {
25998
25802
  init_logger();
25999
- init_model_loader();
26000
25803
  init_remote_provider_types();
26001
25804
  pricingMap = new Map;
26002
- CACHE_DIR = join14(homedir13(), ".claudish");
26003
- CACHE_FILE = join14(CACHE_DIR, "pricing-cache.json");
25805
+ CACHE_DIR = join13(homedir12(), ".claudish");
25806
+ CACHE_FILE = join13(CACHE_DIR, "pricing-cache.json");
26004
25807
  CACHE_TTL_MS = 24 * 60 * 60 * 1000;
26005
25808
  PROVIDER_TO_OR_PREFIX = {
26006
25809
  openai: ["openai/"],
@@ -26018,6 +25821,358 @@ var init_pricing_cache = __esm(() => {
26018
25821
  };
26019
25822
  });
26020
25823
 
25824
+ // src/model-loader.ts
25825
+ import { readFileSync as readFileSync9, existsSync as existsSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "fs";
25826
+ import { join as join14, dirname as dirname2 } from "path";
25827
+ import { fileURLToPath } from "url";
25828
+ import { homedir as homedir13 } from "os";
25829
+ import { createHash as createHash3 } from "crypto";
25830
+ function getBundledRecommendedModelsPath() {
25831
+ return join14(__dirname2, "../recommended-models.json");
25832
+ }
25833
+ function groupRecommendedModels(entries) {
25834
+ const byId = new Map;
25835
+ for (const entry of entries) {
25836
+ const list = byId.get(entry.id);
25837
+ if (list)
25838
+ list.push(entry);
25839
+ else
25840
+ byId.set(entry.id, [entry]);
25841
+ }
25842
+ const flagship = [];
25843
+ const fast = [];
25844
+ for (const [id, members] of byId.entries()) {
25845
+ const primary = members.find((m) => m.category !== "subscription") ?? members[0];
25846
+ const subscriptions = members.filter((m) => m.category === "subscription");
25847
+ const bucket = primary.category === "programming" || primary.category === "vision" || primary.category === "reasoning" ? "flagship" : "fast";
25848
+ const group = { id, primary, subscriptions, bucket };
25849
+ if (bucket === "flagship")
25850
+ flagship.push(group);
25851
+ else
25852
+ fast.push(group);
25853
+ }
25854
+ return { flagship, fast };
25855
+ }
25856
+ function collectRoutingPrefixes(group, getNativePrefix) {
25857
+ const slug = (group.primary.provider || "").toLowerCase();
25858
+ const native = getNativePrefix(slug);
25859
+ const seen = new Set;
25860
+ const out = [];
25861
+ if (native) {
25862
+ out.push(native);
25863
+ seen.add(native);
25864
+ }
25865
+ for (const sub of group.subscriptions) {
25866
+ const p = sub.subscription?.prefix;
25867
+ if (!p || seen.has(p))
25868
+ continue;
25869
+ seen.add(p);
25870
+ out.push(p);
25871
+ }
25872
+ return out;
25873
+ }
25874
+ function parsePriceAvg(s) {
25875
+ if (!s || s === "N/A")
25876
+ return Infinity;
25877
+ if (s === "FREE")
25878
+ return 0;
25879
+ const m = s.match(/\$([\d.]+)/);
25880
+ return m ? parseFloat(m[1]) : Infinity;
25881
+ }
25882
+ function parseCtx(s) {
25883
+ if (!s || s === "N/A")
25884
+ return 0;
25885
+ const upper = s.toUpperCase();
25886
+ if (upper.includes("M"))
25887
+ return parseFloat(upper) * 1e6;
25888
+ if (upper.includes("K"))
25889
+ return parseFloat(upper) * 1000;
25890
+ return parseInt(s, 10) || 0;
25891
+ }
25892
+ function normalizePricingDisplay(raw2) {
25893
+ const pricing = raw2 || "N/A";
25894
+ if (pricing.includes("-1000000"))
25895
+ return "varies";
25896
+ if (pricing === "$0.00/1M" || pricing === "FREE")
25897
+ return "FREE";
25898
+ return pricing;
25899
+ }
25900
+ function computeQuickPicks(primaries) {
25901
+ if (primaries.length === 0) {
25902
+ return {
25903
+ budget: null,
25904
+ largeContext: null,
25905
+ mostCapable: null,
25906
+ visionCoding: null,
25907
+ agentic: null
25908
+ };
25909
+ }
25910
+ const priced = primaries.filter((m) => {
25911
+ const p = parsePriceAvg(m.pricing?.average);
25912
+ return p > 0 && p !== Infinity;
25913
+ }).sort((a, b) => parsePriceAvg(a.pricing?.average) - parsePriceAvg(b.pricing?.average));
25914
+ const budget = priced[0] ?? null;
25915
+ const byCtx = [...primaries].sort((a, b) => parseCtx(b.context) - parseCtx(a.context));
25916
+ const largeContext = byCtx[0] ?? null;
25917
+ const byPrice = [...primaries].sort((a, b) => parsePriceAvg(b.pricing?.average) - parsePriceAvg(a.pricing?.average));
25918
+ const mostCapable = byPrice.find((m) => parsePriceAvg(m.pricing?.average) !== Infinity) ?? null;
25919
+ const visionCoding = primaries.find((m) => m.supportsVision === true && m.id !== budget?.id && m.id !== mostCapable?.id) ?? null;
25920
+ const agentic = primaries.find((m) => m.supportsReasoning === true && m.id !== mostCapable?.id) ?? null;
25921
+ return { budget, largeContext, mostCapable, visionCoding, agentic };
25922
+ }
25923
+ async function getRecommendedModels(opts = {}) {
25924
+ const { forceRefresh = false } = opts;
25925
+ if (!forceRefresh && _cachedRecommendedModels) {
25926
+ return _cachedRecommendedModels;
25927
+ }
25928
+ if (!forceRefresh && existsSync12(RECOMMENDED_MODELS_CACHE_PATH)) {
25929
+ try {
25930
+ const cacheData = JSON.parse(readFileSync9(RECOMMENDED_MODELS_CACHE_PATH, "utf-8"));
25931
+ if (cacheData.models && cacheData.models.length > 0 && isFreshEnough(cacheData)) {
25932
+ _cachedRecommendedModels = cacheData;
25933
+ return cacheData;
25934
+ }
25935
+ } catch {}
25936
+ }
25937
+ try {
25938
+ const response = await fetch(FIREBASE_RECOMMENDED_URL, {
25939
+ signal: AbortSignal.timeout(RECOMMENDED_FETCH_TIMEOUT_MS)
25940
+ });
25941
+ if (response.ok) {
25942
+ const data = await response.json();
25943
+ if (data.models && data.models.length > 0) {
25944
+ _cachedRecommendedModels = data;
25945
+ try {
25946
+ const cacheDir = join14(homedir13(), ".claudish");
25947
+ mkdirSync8(cacheDir, { recursive: true });
25948
+ writeFileSync8(RECOMMENDED_MODELS_CACHE_PATH, JSON.stringify(data), "utf-8");
25949
+ } catch {}
25950
+ return data;
25951
+ }
25952
+ }
25953
+ } catch {}
25954
+ return loadBundledRecommendedModels();
25955
+ }
25956
+ function getRecommendedModelsSync() {
25957
+ if (_cachedRecommendedModels)
25958
+ return _cachedRecommendedModels;
25959
+ if (existsSync12(RECOMMENDED_MODELS_CACHE_PATH)) {
25960
+ try {
25961
+ const cacheData = JSON.parse(readFileSync9(RECOMMENDED_MODELS_CACHE_PATH, "utf-8"));
25962
+ if (cacheData.models && cacheData.models.length > 0) {
25963
+ _cachedRecommendedModels = cacheData;
25964
+ return cacheData;
25965
+ }
25966
+ } catch {}
25967
+ }
25968
+ return loadBundledRecommendedModels();
25969
+ }
25970
+ async function warmRecommendedModels() {
25971
+ try {
25972
+ return await getRecommendedModels({ forceRefresh: true });
25973
+ } catch {
25974
+ return null;
25975
+ }
25976
+ }
25977
+ function isFreshEnough(doc2) {
25978
+ const generatedAt = doc2.generatedAt;
25979
+ if (!generatedAt)
25980
+ return true;
25981
+ const ageHours = (Date.now() - new Date(generatedAt).getTime()) / (1000 * 60 * 60);
25982
+ return ageHours <= RECOMMENDED_CACHE_MAX_AGE_HOURS;
25983
+ }
25984
+ function loadBundledRecommendedModels() {
25985
+ const jsonPath = getBundledRecommendedModelsPath();
25986
+ if (!existsSync12(jsonPath)) {
25987
+ throw new Error(`recommended-models.json not found at ${jsonPath}. ` + `Run 'claudish --top-models --force-update' to refresh from Firebase.`);
25988
+ }
25989
+ try {
25990
+ const doc2 = JSON.parse(readFileSync9(jsonPath, "utf-8"));
25991
+ _cachedRecommendedModels = doc2;
25992
+ return doc2;
25993
+ } catch (error2) {
25994
+ throw new Error(`Failed to parse bundled recommended-models.json: ${error2}`);
25995
+ }
25996
+ }
25997
+ async function searchModels(query, limit = 50) {
25998
+ const url = `${FIREBASE_BASE_URL}?search=${encodeURIComponent(query)}&limit=${limit}&status=active`;
25999
+ const response = await fetch(url, {
26000
+ signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
26001
+ });
26002
+ if (!response.ok) {
26003
+ throw new Error(`Firebase search returned ${response.status} ${response.statusText}`);
26004
+ }
26005
+ const data = await response.json();
26006
+ return data.models ?? [];
26007
+ }
26008
+ async function getTop100Models() {
26009
+ const url = `${FIREBASE_BASE_URL}?catalog=top100`;
26010
+ const response = await fetch(url, {
26011
+ signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
26012
+ });
26013
+ if (!response.ok) {
26014
+ throw new Error(`Firebase top100 fetch failed: ${response.status} ${response.statusText}`);
26015
+ }
26016
+ const data = await response.json();
26017
+ return data;
26018
+ }
26019
+ async function getProviderList() {
26020
+ const url = `${FIREBASE_BASE_URL}?catalog=providers`;
26021
+ const response = await fetch(url, {
26022
+ signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
26023
+ });
26024
+ if (!response.ok) {
26025
+ throw new Error(`Firebase providers fetch failed: ${response.status} ${response.statusText}`);
26026
+ }
26027
+ const data = await response.json();
26028
+ return data.providers ?? [];
26029
+ }
26030
+ async function getModelsByProvider(provider, limit = 200) {
26031
+ const url = `${FIREBASE_BASE_URL}?provider=${encodeURIComponent(provider)}&status=active&limit=${limit}`;
26032
+ const response = await fetch(url, {
26033
+ signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
26034
+ });
26035
+ if (!response.ok) {
26036
+ throw new Error(`Firebase provider query returned ${response.status} ${response.statusText}`);
26037
+ }
26038
+ const data = await response.json();
26039
+ if (Array.isArray(data))
26040
+ return data;
26041
+ return data.models ?? [];
26042
+ }
26043
+ function loadModelInfo() {
26044
+ if (_cachedModelInfo) {
26045
+ return _cachedModelInfo;
26046
+ }
26047
+ const data = getRecommendedModelsSync();
26048
+ const modelInfo = {};
26049
+ for (const model of data.models) {
26050
+ modelInfo[model.id] = {
26051
+ name: model.name,
26052
+ description: model.description,
26053
+ priority: model.priority,
26054
+ provider: model.provider
26055
+ };
26056
+ }
26057
+ modelInfo.custom = {
26058
+ name: "Custom Model",
26059
+ description: "Enter any model ID manually",
26060
+ priority: 999,
26061
+ provider: "Custom"
26062
+ };
26063
+ _cachedModelInfo = modelInfo;
26064
+ return modelInfo;
26065
+ }
26066
+ function getAvailableModels() {
26067
+ if (_cachedModelIds) {
26068
+ return _cachedModelIds;
26069
+ }
26070
+ const data = getRecommendedModelsSync();
26071
+ const modelIds = data.models.sort((a, b) => a.priority - b.priority).map((m) => m.id);
26072
+ const result = [...modelIds, "custom"];
26073
+ _cachedModelIds = result;
26074
+ return result;
26075
+ }
26076
+ async function fetchLiteLLMModels(baseUrl, apiKey, forceUpdate = false) {
26077
+ const hash = createHash3("sha256").update(baseUrl).digest("hex").substring(0, 16);
26078
+ const cacheDir = join14(homedir13(), ".claudish");
26079
+ const cachePath = join14(cacheDir, `litellm-models-${hash}.json`);
26080
+ if (!forceUpdate && existsSync12(cachePath)) {
26081
+ try {
26082
+ const cacheData = JSON.parse(readFileSync9(cachePath, "utf-8"));
26083
+ const timestamp = new Date(cacheData.timestamp);
26084
+ const now = new Date;
26085
+ const ageInHours = (now.getTime() - timestamp.getTime()) / (1000 * 60 * 60);
26086
+ if (ageInHours < LITELLM_CACHE_MAX_AGE_HOURS) {
26087
+ return cacheData.models;
26088
+ }
26089
+ } catch {}
26090
+ }
26091
+ try {
26092
+ const url = `${baseUrl.replace(/\/$/, "")}/model_group/info`;
26093
+ const response = await fetch(url, {
26094
+ headers: {
26095
+ Authorization: `Bearer ${apiKey}`
26096
+ },
26097
+ signal: AbortSignal.timeout(1e4)
26098
+ });
26099
+ if (!response.ok) {
26100
+ console.error(`Failed to fetch LiteLLM models: ${response.status} ${response.statusText}`);
26101
+ if (existsSync12(cachePath)) {
26102
+ try {
26103
+ const cacheData2 = JSON.parse(readFileSync9(cachePath, "utf-8"));
26104
+ return cacheData2.models;
26105
+ } catch {
26106
+ return [];
26107
+ }
26108
+ }
26109
+ return [];
26110
+ }
26111
+ const responseData = await response.json();
26112
+ const rawModels = Array.isArray(responseData) ? responseData : responseData.data || [];
26113
+ const transformedModels = rawModels.filter((m) => m.mode === "chat" && m.supports_function_calling).map((m) => {
26114
+ const inputCostPerM = (m.input_cost_per_token || 0) * 1e6;
26115
+ const outputCostPerM = (m.output_cost_per_token || 0) * 1e6;
26116
+ const avgCost = (inputCostPerM + outputCostPerM) / 2;
26117
+ const isFree = inputCostPerM === 0 && outputCostPerM === 0;
26118
+ const contextLength = m.max_input_tokens || 128000;
26119
+ const contextStr = contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`;
26120
+ return {
26121
+ id: `litellm@${m.model_group}`,
26122
+ name: m.model_group,
26123
+ description: `LiteLLM model (providers: ${m.providers.join(", ")})`,
26124
+ provider: "LiteLLM",
26125
+ pricing: {
26126
+ input: isFree ? "FREE" : `$${inputCostPerM.toFixed(2)}`,
26127
+ output: isFree ? "FREE" : `$${outputCostPerM.toFixed(2)}`,
26128
+ average: isFree ? "FREE" : `$${avgCost.toFixed(2)}/1M`
26129
+ },
26130
+ context: contextStr,
26131
+ contextLength,
26132
+ supportsTools: m.supports_function_calling || false,
26133
+ supportsReasoning: m.supports_reasoning || false,
26134
+ supportsVision: m.supports_vision || false,
26135
+ isFree,
26136
+ source: "LiteLLM"
26137
+ };
26138
+ });
26139
+ mkdirSync8(cacheDir, { recursive: true });
26140
+ const cacheData = {
26141
+ timestamp: new Date().toISOString(),
26142
+ models: transformedModels
26143
+ };
26144
+ writeFileSync8(cachePath, JSON.stringify(cacheData, null, 2), "utf-8");
26145
+ return transformedModels;
26146
+ } catch (error2) {
26147
+ console.error(`Failed to fetch LiteLLM models: ${error2}`);
26148
+ if (existsSync12(cachePath)) {
26149
+ try {
26150
+ const cacheData = JSON.parse(readFileSync9(cachePath, "utf-8"));
26151
+ return cacheData.models;
26152
+ } catch {
26153
+ return [];
26154
+ }
26155
+ }
26156
+ return [];
26157
+ }
26158
+ }
26159
+ var __filename2, __dirname2, _cachedModelInfo = null, _cachedModelIds = null, _cachedRecommendedModels = null, FIREBASE_BASE_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels", FIREBASE_RECOMMENDED_URL, RECOMMENDED_MODELS_CACHE_PATH, RECOMMENDED_CACHE_MAX_AGE_HOURS = 12, RECOMMENDED_FETCH_TIMEOUT_MS = 5000, SEARCH_FETCH_TIMEOUT_MS = 1e4, FIREBASE_SLUG_TO_PROVIDER_NAME, LITELLM_CACHE_MAX_AGE_HOURS = 24;
26160
+ var init_model_loader = __esm(() => {
26161
+ __filename2 = fileURLToPath(import.meta.url);
26162
+ __dirname2 = dirname2(__filename2);
26163
+ FIREBASE_RECOMMENDED_URL = `${FIREBASE_BASE_URL}?catalog=recommended`;
26164
+ RECOMMENDED_MODELS_CACHE_PATH = join14(homedir13(), ".claudish", "recommended-models-cache.json");
26165
+ FIREBASE_SLUG_TO_PROVIDER_NAME = {
26166
+ openai: "openai",
26167
+ google: "google",
26168
+ "x-ai": "xai",
26169
+ "z-ai": "zai",
26170
+ moonshotai: "kimi",
26171
+ minimax: "minimax",
26172
+ qwen: "qwen"
26173
+ };
26174
+ });
26175
+
26021
26176
  // src/handlers/fallback-handler.ts
26022
26177
  class FallbackHandler {
26023
26178
  candidates;
@@ -26536,8 +26691,8 @@ Details: ${e.message}`);
26536
26691
  const credPath = this.getCredentialsPath();
26537
26692
  const claudishDir = join15(homedir14(), ".claudish");
26538
26693
  if (!existsSync13(claudishDir)) {
26539
- const { mkdirSync: mkdirSync10 } = __require("fs");
26540
- mkdirSync10(claudishDir, { recursive: true });
26694
+ const { mkdirSync: mkdirSync9 } = __require("fs");
26695
+ mkdirSync9(claudishDir, { recursive: true });
26541
26696
  }
26542
26697
  const fd = openSync(credPath, "w", 384);
26543
26698
  try {
@@ -27383,8 +27538,8 @@ Details: ${e.message}`);
27383
27538
  const credPath = this.getCredentialsPath();
27384
27539
  const claudishDir = join16(homedir15(), ".claudish");
27385
27540
  if (!existsSync14(claudishDir)) {
27386
- const { mkdirSync: mkdirSync10 } = __require("fs");
27387
- mkdirSync10(claudishDir, { recursive: true });
27541
+ const { mkdirSync: mkdirSync9 } = __require("fs");
27542
+ mkdirSync9(claudishDir, { recursive: true });
27388
27543
  }
27389
27544
  const fd = openSync2(credPath, "w", 384);
27390
27545
  try {
@@ -27686,8 +27841,8 @@ class KimiOAuth {
27686
27841
  const deviceIdPath = this.getDeviceIdPath();
27687
27842
  const claudishDir = join18(homedir17(), ".claudish");
27688
27843
  if (!existsSync16(claudishDir)) {
27689
- const { mkdirSync: mkdirSync10 } = __require("fs");
27690
- mkdirSync10(claudishDir, { recursive: true });
27844
+ const { mkdirSync: mkdirSync9 } = __require("fs");
27845
+ mkdirSync9(claudishDir, { recursive: true });
27691
27846
  }
27692
27847
  if (existsSync16(deviceIdPath)) {
27693
27848
  try {
@@ -27965,8 +28120,8 @@ Waiting for authorization...`);
27965
28120
  const credPath = this.getCredentialsPath();
27966
28121
  const claudishDir = join18(homedir17(), ".claudish");
27967
28122
  if (!existsSync16(claudishDir)) {
27968
- const { mkdirSync: mkdirSync10 } = __require("fs");
27969
- mkdirSync10(claudishDir, { recursive: true });
28123
+ const { mkdirSync: mkdirSync9 } = __require("fs");
28124
+ mkdirSync9(claudishDir, { recursive: true });
27970
28125
  }
27971
28126
  const fd = openSync3(credPath, "w", 384);
27972
28127
  try {
@@ -29307,6 +29462,7 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
29307
29462
  port = actualPort;
29308
29463
  log(`[Proxy] Server started on port ${port}`);
29309
29464
  warmPricingCache().catch(() => {});
29465
+ warmRecommendedModels().catch(() => {});
29310
29466
  const catalogProvidersToWarm = ["openrouter"];
29311
29467
  if (process.env.LITELLM_BASE_URL)
29312
29468
  catalogProvidersToWarm.push("litellm");
@@ -29315,7 +29471,7 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
29315
29471
  port,
29316
29472
  url: `http://127.0.0.1:${port}`,
29317
29473
  shutdown: async () => {
29318
- return new Promise((resolve3) => server.close((e) => resolve3()));
29474
+ return new Promise((resolve3) => server.close(() => resolve3()));
29319
29475
  }
29320
29476
  };
29321
29477
  }
@@ -29380,53 +29536,18 @@ var init_port_manager = () => {};
29380
29536
  // src/mcp-server.ts
29381
29537
  var exports_mcp_server = {};
29382
29538
  __export(exports_mcp_server, {
29383
- startMcpServer: () => startMcpServer
29539
+ startMcpServer: () => startMcpServer,
29540
+ runPromptViaProxy: () => runPromptViaProxy,
29541
+ parseAnthropicSse: () => parseAnthropicSse
29384
29542
  });
29385
- import { readFileSync as readFileSync18, existsSync as existsSync21, writeFileSync as writeFileSync10, mkdirSync as mkdirSync10, readdirSync as readdirSync3 } from "fs";
29386
- import { join as join23, dirname as dirname2 } from "path";
29543
+ import { readFileSync as readFileSync18, existsSync as existsSync21, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, readdirSync as readdirSync3 } from "fs";
29544
+ import { join as join23, dirname as dirname3 } from "path";
29387
29545
  import { homedir as homedir22 } from "os";
29388
29546
  import { fileURLToPath as fileURLToPath2 } from "url";
29389
- function loadRecommendedModels() {
29390
- const cachedPath = join23(CLAUDISH_CACHE_DIR, "recommended-models-cache.json");
29391
- if (existsSync21(cachedPath)) {
29392
- try {
29393
- const data = JSON.parse(readFileSync18(cachedPath, "utf-8"));
29394
- if (data.models && data.models.length > 0)
29395
- return data.models;
29396
- } catch {}
29397
- }
29398
- if (existsSync21(RECOMMENDED_MODELS_PATH)) {
29399
- try {
29400
- const data = JSON.parse(readFileSync18(RECOMMENDED_MODELS_PATH, "utf-8"));
29401
- return data.models || [];
29402
- } catch {
29403
- return [];
29404
- }
29405
- }
29406
- return [];
29407
- }
29408
- function parsePriceAvg(s) {
29409
- if (!s || s === "N/A")
29410
- return Infinity;
29411
- if (s === "FREE")
29412
- return 0;
29413
- const m = s.match(/\$([\d.]+)/);
29414
- return m ? parseFloat(m[1]) : Infinity;
29415
- }
29416
- function parseCtx(s) {
29417
- if (!s || s === "N/A")
29418
- return 0;
29419
- const upper = s.toUpperCase();
29420
- if (upper.includes("M"))
29421
- return parseFloat(upper) * 1e6;
29422
- if (upper.includes("K"))
29423
- return parseFloat(upper) * 1000;
29424
- return parseInt(s) || 0;
29425
- }
29426
29547
  async function loadAllModels(forceRefresh = false) {
29427
- if (!forceRefresh && existsSync21(ALL_MODELS_CACHE_PATH)) {
29548
+ if (!forceRefresh && existsSync21(ALL_MODELS_CACHE_PATH2)) {
29428
29549
  try {
29429
- const cacheData = JSON.parse(readFileSync18(ALL_MODELS_CACHE_PATH, "utf-8"));
29550
+ const cacheData = JSON.parse(readFileSync18(ALL_MODELS_CACHE_PATH2, "utf-8"));
29430
29551
  const lastUpdated = new Date(cacheData.lastUpdated);
29431
29552
  const ageInDays = (Date.now() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24);
29432
29553
  if (ageInDays <= CACHE_MAX_AGE_DAYS) {
@@ -29440,12 +29561,12 @@ async function loadAllModels(forceRefresh = false) {
29440
29561
  throw new Error(`API returned ${response.status}`);
29441
29562
  const data = await response.json();
29442
29563
  const models = data.data || [];
29443
- mkdirSync10(CLAUDISH_CACHE_DIR, { recursive: true });
29444
- writeFileSync10(ALL_MODELS_CACHE_PATH, JSON.stringify({ lastUpdated: new Date().toISOString(), models }), "utf-8");
29564
+ mkdirSync9(CLAUDISH_CACHE_DIR, { recursive: true });
29565
+ writeFileSync9(ALL_MODELS_CACHE_PATH2, JSON.stringify({ lastUpdated: new Date().toISOString(), models }), "utf-8");
29445
29566
  return models;
29446
29567
  } catch {
29447
- if (existsSync21(ALL_MODELS_CACHE_PATH)) {
29448
- const cacheData = JSON.parse(readFileSync18(ALL_MODELS_CACHE_PATH, "utf-8"));
29568
+ if (existsSync21(ALL_MODELS_CACHE_PATH2)) {
29569
+ const cacheData = JSON.parse(readFileSync18(ALL_MODELS_CACHE_PATH2, "utf-8"));
29449
29570
  return cacheData.models || [];
29450
29571
  }
29451
29572
  return [];
@@ -29650,8 +29771,20 @@ Tokens: ${result.usage.input} input, ${result.usage.output} output`;
29650
29771
  inputSchema: { type: "object" },
29651
29772
  group: "low-level",
29652
29773
  handler: async () => {
29653
- const models = loadRecommendedModels();
29654
- if (models.length === 0) {
29774
+ let doc2;
29775
+ try {
29776
+ doc2 = getRecommendedModelsSync();
29777
+ } catch {
29778
+ return {
29779
+ content: [
29780
+ {
29781
+ type: "text",
29782
+ text: "No recommended models found. Try search_models instead."
29783
+ }
29784
+ ]
29785
+ };
29786
+ }
29787
+ if (!doc2.models || doc2.models.length === 0) {
29655
29788
  return {
29656
29789
  content: [
29657
29790
  {
@@ -29661,43 +29794,81 @@ Tokens: ${result.usage.input} input, ${result.usage.output} output`;
29661
29794
  ]
29662
29795
  };
29663
29796
  }
29797
+ const { flagship, fast } = groupRecommendedModels(doc2.models);
29798
+ const providerByName = new Map(BUILTIN_PROVIDERS.map((p) => [p.name, p]));
29799
+ const getNativePrefix = (firebaseSlug) => {
29800
+ const canonical = FIREBASE_SLUG_TO_PROVIDER_NAME[firebaseSlug];
29801
+ if (!canonical)
29802
+ return null;
29803
+ const def = providerByName.get(canonical);
29804
+ if (!def || !def.shortcuts || def.shortcuts.length === 0)
29805
+ return null;
29806
+ return def.shortcuts[0];
29807
+ };
29808
+ const renderGroup = (group) => {
29809
+ const m = group.primary;
29810
+ const pricing = normalizePricingDisplay(m.pricing?.average);
29811
+ const ctx = m.context || "N/A";
29812
+ const caps = [];
29813
+ if (m.supportsTools)
29814
+ caps.push("tools");
29815
+ if (m.supportsReasoning)
29816
+ caps.push("reasoning");
29817
+ if (m.supportsVision)
29818
+ caps.push("vision");
29819
+ const capsLine = caps.length > 0 ? caps.join(", ") : "none";
29820
+ const prefixes = collectRoutingPrefixes(group, getNativePrefix);
29821
+ const accessLine = prefixes.length > 0 ? prefixes.map((p) => `\`${p}@${m.id}\``).join(" \xB7 ") : `\`${m.id}\``;
29822
+ return [
29823
+ `### ${m.id}`,
29824
+ `- **Pricing**: ${pricing} avg \xB7 ${ctx} context`,
29825
+ `- **Capabilities**: ${capsLine}`,
29826
+ `- **Access**: ${accessLine}`,
29827
+ ""
29828
+ ].join(`
29829
+ `);
29830
+ };
29664
29831
  let output = `# Recommended Models
29665
29832
 
29666
29833
  `;
29667
- output += `| Model | Provider | Pricing | Context | Tools | Reasoning | Vision |
29668
- `;
29669
- output += `|-------|----------|---------|---------|-------|-----------|--------|
29834
+ output += `_Last updated: ${doc2.lastUpdated || "unknown"}_
29835
+
29670
29836
  `;
29671
- for (const model of models) {
29672
- const t = model.supportsTools ? "\u2713" : "\xB7";
29673
- const r = model.supportsReasoning ? "\u2713" : "\xB7";
29674
- const v = model.supportsVision ? "\u2713" : "\xB7";
29675
- output += `| ${model.id} | ${model.provider} | ${model.pricing?.average || "N/A"} | ${model.context || "N/A"} | ${t} | ${r} | ${v} |
29837
+ if (flagship.length > 0) {
29838
+ output += `## Flagship models
29839
+
29676
29840
  `;
29841
+ for (const group of flagship)
29842
+ output += renderGroup(group);
29677
29843
  }
29678
- output += `
29679
- ## Quick Picks
29680
- `;
29681
- const cheapest = [...models].sort((a, b) => parsePriceAvg(a.pricing?.average) - parsePriceAvg(b.pricing?.average))[0];
29682
- const bigCtx = [...models].sort((a, b) => parseCtx(b.context) - parseCtx(a.context))[0];
29683
- const priciest = [...models].sort((a, b) => parsePriceAvg(b.pricing?.average) - parsePriceAvg(a.pricing?.average))[0];
29684
- const vision = models.find((m) => m.supportsVision);
29685
- const reasoning = models.find((m) => m.supportsReasoning && m.id !== priciest?.id);
29686
- if (cheapest)
29687
- output += `- **Budget**: \`${cheapest.id}\` (${cheapest.pricing?.average || "N/A"})
29688
- `;
29689
- if (bigCtx && bigCtx.id !== cheapest?.id)
29690
- output += `- **Large context**: \`${bigCtx.id}\` (${bigCtx.context || "N/A"} tokens)
29691
- `;
29692
- if (priciest && priciest.id !== cheapest?.id)
29693
- output += `- **Most advanced**: \`${priciest.id}\` (${priciest.pricing?.average || "N/A"})
29844
+ if (fast.length > 0) {
29845
+ output += `## Fast variants
29846
+
29694
29847
  `;
29695
- if (vision && vision.id !== cheapest?.id && vision.id !== priciest?.id)
29696
- output += `- **Vision + coding**: \`${vision.id}\` (${vision.pricing?.average || "N/A"})
29848
+ for (const group of fast)
29849
+ output += renderGroup(group);
29850
+ }
29851
+ const primaries = [...flagship, ...fast].map((g) => g.primary);
29852
+ const picks = computeQuickPicks(primaries);
29853
+ const pickLines = [];
29854
+ if (picks.budget)
29855
+ pickLines.push(`- **Budget**: \`${picks.budget.id}\` (${normalizePricingDisplay(picks.budget.pricing?.average)})`);
29856
+ if (picks.largeContext)
29857
+ pickLines.push(`- **Large context**: \`${picks.largeContext.id}\` (${picks.largeContext.context || "N/A"})`);
29858
+ if (picks.mostCapable)
29859
+ pickLines.push(`- **Most capable**: \`${picks.mostCapable.id}\``);
29860
+ if (picks.visionCoding)
29861
+ pickLines.push(`- **Vision + coding**: \`${picks.visionCoding.id}\``);
29862
+ if (picks.agentic)
29863
+ pickLines.push(`- **Agentic**: \`${picks.agentic.id}\``);
29864
+ if (pickLines.length > 0) {
29865
+ output += `## Quick picks
29866
+
29697
29867
  `;
29698
- if (reasoning && reasoning.id !== cheapest?.id)
29699
- output += `- **Agentic**: \`${reasoning.id}\` (${reasoning.pricing?.average || "N/A"})
29868
+ output += pickLines.join(`
29869
+ `) + `
29700
29870
  `;
29871
+ }
29701
29872
  return { content: [{ type: "text", text: output }] };
29702
29873
  }
29703
29874
  });
@@ -30321,7 +30492,7 @@ function startMcpServer() {
30321
30492
  process.exit(1);
30322
30493
  });
30323
30494
  }
30324
- var import_dotenv2, __filename3, __dirname3, RECOMMENDED_MODELS_PATH, CLAUDISH_CACHE_DIR, ALL_MODELS_CACHE_PATH, CACHE_MAX_AGE_DAYS = 2, INSTRUCTIONS = `Claudish MCP server provides access to external AI models (OpenRouter, Ollama, LM Studio, etc.) for coding tasks.
30495
+ var import_dotenv2, __filename3, __dirname3, CLAUDISH_CACHE_DIR, ALL_MODELS_CACHE_PATH2, CACHE_MAX_AGE_DAYS = 2, INSTRUCTIONS = `Claudish MCP server provides access to external AI models (OpenRouter, Ollama, LM Studio, etc.) for coding tasks.
30325
30496
 
30326
30497
  ## Channel Mode \u2014 External Model Sessions
30327
30498
 
@@ -30354,13 +30525,14 @@ var init_mcp_server = __esm(() => {
30354
30525
  init_channel();
30355
30526
  init_proxy_server();
30356
30527
  init_port_manager();
30528
+ init_model_loader();
30529
+ init_provider_definitions();
30357
30530
  import_dotenv2 = __toESM(require_main(), 1);
30358
30531
  import_dotenv2.config();
30359
30532
  __filename3 = fileURLToPath2(import.meta.url);
30360
- __dirname3 = dirname2(__filename3);
30361
- RECOMMENDED_MODELS_PATH = join23(__dirname3, "../recommended-models.json");
30533
+ __dirname3 = dirname3(__filename3);
30362
30534
  CLAUDISH_CACHE_DIR = join23(homedir22(), ".claudish");
30363
- ALL_MODELS_CACHE_PATH = join23(CLAUDISH_CACHE_DIR, "all-models.json");
30535
+ ALL_MODELS_CACHE_PATH2 = join23(CLAUDISH_CACHE_DIR, "all-models.json");
30364
30536
  });
30365
30537
 
30366
30538
  // ../../node_modules/.bun/@inquirer+core@11.0.1+04f2146be16c61ef/node_modules/@inquirer/core/dist/lib/key.js
@@ -41634,7 +41806,7 @@ var init_RemoveFileError = __esm(() => {
41634
41806
 
41635
41807
  // ../../node_modules/.bun/@inquirer+external-editor@2.0.1+04f2146be16c61ef/node_modules/@inquirer/external-editor/dist/index.js
41636
41808
  import { spawn as spawn3, spawnSync } from "child_process";
41637
- import { readFileSync as readFileSync19, unlinkSync as unlinkSync6, writeFileSync as writeFileSync11 } from "fs";
41809
+ import { readFileSync as readFileSync19, unlinkSync as unlinkSync6, writeFileSync as writeFileSync10 } from "fs";
41638
41810
  import path from "path";
41639
41811
  import os from "os";
41640
41812
  import { randomUUID as randomUUID3 } from "crypto";
@@ -41743,7 +41915,7 @@ class ExternalEditor {
41743
41915
  if (Object.prototype.hasOwnProperty.call(this.fileOptions, "mode")) {
41744
41916
  opt.mode = this.fileOptions.mode;
41745
41917
  }
41746
- writeFileSync11(this.tempFile, this.text, opt);
41918
+ writeFileSync10(this.tempFile, this.text, opt);
41747
41919
  } catch (createFileError) {
41748
41920
  throw new CreateFileError(createFileError);
41749
41921
  }
@@ -43157,50 +43329,6 @@ var init_config = __esm(() => {
43157
43329
  };
43158
43330
  });
43159
43331
 
43160
- // src/utils.ts
43161
- function fuzzyScore2(text, query) {
43162
- if (!text || !query)
43163
- return 0;
43164
- const t = text.toLowerCase();
43165
- const q = query.toLowerCase();
43166
- if (t === q)
43167
- return 1;
43168
- if (t.startsWith(q))
43169
- return 0.9;
43170
- if (t.includes(` ${q}`) || t.includes(`-${q}`) || t.includes(`/${q}`))
43171
- return 0.8;
43172
- if (t.includes(q))
43173
- return 0.6;
43174
- const normSep = (s) => s.replace(/[\s\-_.]/g, "");
43175
- const tn = normSep(t);
43176
- const qn = normSep(q);
43177
- if (tn === qn)
43178
- return 0.95;
43179
- if (tn.startsWith(qn))
43180
- return 0.85;
43181
- if (tn.includes(qn))
43182
- return 0.65;
43183
- let score = 0;
43184
- let tIdx = 0;
43185
- let qIdx = 0;
43186
- let consecutive = 0;
43187
- while (tIdx < t.length && qIdx < q.length) {
43188
- if (t[tIdx] === q[qIdx]) {
43189
- score += 1 + consecutive * 0.5;
43190
- consecutive++;
43191
- qIdx++;
43192
- } else {
43193
- consecutive = 0;
43194
- }
43195
- tIdx++;
43196
- }
43197
- if (qIdx === q.length) {
43198
- const compactness = q.length / (tIdx + 1);
43199
- return 0.1 + 0.4 * compactness * (score / (q.length * 2));
43200
- }
43201
- return 0;
43202
- }
43203
-
43204
43332
  // src/providers/api-key-map.ts
43205
43333
  var API_KEY_MAP;
43206
43334
  var init_api_key_map = __esm(() => {
@@ -43540,7 +43668,7 @@ import { EventEmitter } from "events";
43540
43668
  import { Buffer as Buffer2 } from "buffer";
43541
43669
  import { Buffer as Buffer3 } from "buffer";
43542
43670
  import { EventEmitter as EventEmitter2 } from "events";
43543
- import { resolve as resolve3, dirname as dirname3 } from "path";
43671
+ import { resolve as resolve3, dirname as dirname4 } from "path";
43544
43672
  import { fileURLToPath as fileURLToPath3 } from "url";
43545
43673
  import { resolve as resolve22, isAbsolute, parse as parse6 } from "path";
43546
43674
  import { existsSync as existsSync22 } from "fs";
@@ -43549,7 +43677,7 @@ import os2 from "os";
43549
43677
  import path2 from "path";
43550
43678
  import { EventEmitter as EventEmitter3 } from "events";
43551
43679
  import { dlopen, toArrayBuffer as toArrayBuffer4, JSCallback, ptr as ptr4 } from "bun:ffi";
43552
- import { existsSync as existsSync23, writeFileSync as writeFileSync12 } from "fs";
43680
+ import { existsSync as existsSync23, writeFileSync as writeFileSync11 } from "fs";
43553
43681
  import { EventEmitter as EventEmitter4 } from "events";
43554
43682
  import { toArrayBuffer, ptr } from "bun:ffi";
43555
43683
  import { ptr as ptr2, toArrayBuffer as toArrayBuffer2 } from "bun:ffi";
@@ -45885,24 +46013,24 @@ function getParsers() {
45885
46013
  {
45886
46014
  filetype: "javascript",
45887
46015
  queries: {
45888
- highlights: [resolve3(dirname3(fileURLToPath3(import.meta.url)), highlights_default)]
46016
+ highlights: [resolve3(dirname4(fileURLToPath3(import.meta.url)), highlights_default)]
45889
46017
  },
45890
- wasm: resolve3(dirname3(fileURLToPath3(import.meta.url)), tree_sitter_javascript_default)
46018
+ wasm: resolve3(dirname4(fileURLToPath3(import.meta.url)), tree_sitter_javascript_default)
45891
46019
  },
45892
46020
  {
45893
46021
  filetype: "typescript",
45894
46022
  queries: {
45895
- highlights: [resolve3(dirname3(fileURLToPath3(import.meta.url)), highlights_default2)]
46023
+ highlights: [resolve3(dirname4(fileURLToPath3(import.meta.url)), highlights_default2)]
45896
46024
  },
45897
- wasm: resolve3(dirname3(fileURLToPath3(import.meta.url)), tree_sitter_typescript_default)
46025
+ wasm: resolve3(dirname4(fileURLToPath3(import.meta.url)), tree_sitter_typescript_default)
45898
46026
  },
45899
46027
  {
45900
46028
  filetype: "markdown",
45901
46029
  queries: {
45902
- highlights: [resolve3(dirname3(fileURLToPath3(import.meta.url)), highlights_default3)],
45903
- injections: [resolve3(dirname3(fileURLToPath3(import.meta.url)), injections_default)]
46030
+ highlights: [resolve3(dirname4(fileURLToPath3(import.meta.url)), highlights_default3)],
46031
+ injections: [resolve3(dirname4(fileURLToPath3(import.meta.url)), injections_default)]
45904
46032
  },
45905
- wasm: resolve3(dirname3(fileURLToPath3(import.meta.url)), tree_sitter_markdown_default),
46033
+ wasm: resolve3(dirname4(fileURLToPath3(import.meta.url)), tree_sitter_markdown_default),
45906
46034
  injectionMapping: {
45907
46035
  nodeTypes: {
45908
46036
  inline: "markdown_inline",
@@ -45921,16 +46049,16 @@ function getParsers() {
45921
46049
  {
45922
46050
  filetype: "markdown_inline",
45923
46051
  queries: {
45924
- highlights: [resolve3(dirname3(fileURLToPath3(import.meta.url)), highlights_default4)]
46052
+ highlights: [resolve3(dirname4(fileURLToPath3(import.meta.url)), highlights_default4)]
45925
46053
  },
45926
- wasm: resolve3(dirname3(fileURLToPath3(import.meta.url)), tree_sitter_markdown_inline_default)
46054
+ wasm: resolve3(dirname4(fileURLToPath3(import.meta.url)), tree_sitter_markdown_inline_default)
45927
46055
  },
45928
46056
  {
45929
46057
  filetype: "zig",
45930
46058
  queries: {
45931
- highlights: [resolve3(dirname3(fileURLToPath3(import.meta.url)), highlights_default5)]
46059
+ highlights: [resolve3(dirname4(fileURLToPath3(import.meta.url)), highlights_default5)]
45932
46060
  },
45933
- wasm: resolve3(dirname3(fileURLToPath3(import.meta.url)), tree_sitter_zig_default)
46061
+ wasm: resolve3(dirname4(fileURLToPath3(import.meta.url)), tree_sitter_zig_default)
45934
46062
  }
45935
46063
  ];
45936
46064
  }
@@ -48895,7 +49023,7 @@ function convertToDebugSymbols(symbols) {
48895
49023
  if (env.OTUI_DEBUG_FFI && globalFFILogPath) {
48896
49024
  const logPath = globalFFILogPath;
48897
49025
  const writeSync4 = (msg) => {
48898
- writeFileSync12(logPath, msg + `
49026
+ writeFileSync11(logPath, msg + `
48899
49027
  `, { flag: "a" });
48900
49028
  };
48901
49029
  Object.entries(symbols).forEach(([key, value]) => {
@@ -100229,15 +100357,14 @@ __export(exports_cli, {
100229
100357
  });
100230
100358
  import {
100231
100359
  readFileSync as readFileSync20,
100232
- writeFileSync as writeFileSync13,
100233
100360
  existsSync as existsSync24,
100234
- mkdirSync as mkdirSync11,
100361
+ mkdirSync as mkdirSync10,
100235
100362
  copyFileSync,
100236
100363
  readdirSync as readdirSync4,
100237
100364
  unlinkSync as unlinkSync7
100238
100365
  } from "fs";
100239
100366
  import { fileURLToPath as fileURLToPath4 } from "url";
100240
- import { dirname as dirname4, join as join25 } from "path";
100367
+ import { dirname as dirname5, join as join25 } from "path";
100241
100368
  import { homedir as homedir23 } from "os";
100242
100369
  function getVersion3() {
100243
100370
  return VERSION;
@@ -100246,7 +100373,7 @@ function clearAllModelCaches() {
100246
100373
  const cacheDir = join25(homedir23(), ".claudish");
100247
100374
  if (!existsSync24(cacheDir))
100248
100375
  return;
100249
- const cachePatterns = ["all-models.json", "pricing-cache.json"];
100376
+ const cachePatterns = ["pricing-cache.json", "recommended-models-cache.json"];
100250
100377
  let cleared = 0;
100251
100378
  try {
100252
100379
  const files = readdirSync4(cacheDir);
@@ -100444,25 +100571,54 @@ async function parseArgs(args) {
100444
100571
  const forceUpdate = args.includes("--force-update");
100445
100572
  if (forceUpdate)
100446
100573
  clearAllModelCaches();
100447
- await checkAndUpdateModelsCache(forceUpdate);
100448
- if (hasJsonFlag) {
100449
- printAvailableModelsJSON();
100450
- } else {
100451
- printAvailableModels();
100452
- }
100574
+ await printRecommendedModels(hasJsonFlag, forceUpdate);
100453
100575
  process.exit(0);
100576
+ } else if (arg === "--list-providers") {
100577
+ const hasJsonFlag = args.includes("--json");
100578
+ try {
100579
+ const providers = await getProviderList();
100580
+ if (hasJsonFlag) {
100581
+ console.log(JSON.stringify({ providers, total: providers.length }, null, 2));
100582
+ } else {
100583
+ console.log(`
100584
+ Providers in Firebase catalog:
100585
+ `);
100586
+ console.log(" Slug Active models");
100587
+ console.log(" " + "\u2500".repeat(40));
100588
+ for (const { slug, count } of providers) {
100589
+ console.log(` ${slug.padEnd(20)} ${String(count).padStart(5)}`);
100590
+ }
100591
+ console.log(`
100592
+ Usage: claudish --list-models --provider <slug>`);
100593
+ console.log(` claudish -s <query> (fuzzy search)
100594
+ `);
100595
+ }
100596
+ process.exit(0);
100597
+ } catch (err) {
100598
+ console.error(`Failed to fetch providers: ${err instanceof Error ? err.message : String(err)}`);
100599
+ process.exit(1);
100600
+ }
100454
100601
  } else if (arg === "--models" || arg === "--list-models" || arg === "-s" || arg === "--search") {
100455
100602
  const nextArg = args[i + 1];
100456
100603
  const hasQuery = nextArg && !nextArg.startsWith("--");
100457
100604
  const query = hasQuery ? args[++i] : null;
100458
100605
  const hasJsonFlag = args.includes("--json");
100459
100606
  const forceUpdate = args.includes("--force-update");
100607
+ const providerIdx = args.indexOf("--provider");
100608
+ const providerSlug = providerIdx !== -1 && providerIdx + 1 < args.length ? args[providerIdx + 1] : null;
100460
100609
  if (forceUpdate)
100461
100610
  clearAllModelCaches();
100611
+ if (query && providerSlug) {
100612
+ console.error("Use --provider together with --list-models (without a query) to filter the catalog.");
100613
+ console.error("For keyword search, drop --provider: claudish -s <query>");
100614
+ process.exit(1);
100615
+ }
100462
100616
  if (query) {
100463
- await searchAndPrintModels(query, forceUpdate);
100617
+ await searchAndPrintModels(query, hasJsonFlag);
100618
+ } else if (providerSlug) {
100619
+ await printByProvider(providerSlug, hasJsonFlag);
100464
100620
  } else {
100465
- await printAllModels(hasJsonFlag, forceUpdate);
100621
+ await printTop100(hasJsonFlag);
100466
100622
  }
100467
100623
  process.exit(0);
100468
100624
  } else if (arg === "--summarize-tools") {
@@ -100585,502 +100741,298 @@ async function fetchOllamaModels() {
100585
100741
  return [];
100586
100742
  }
100587
100743
  }
100588
- async function searchAndPrintModels(query, forceUpdate) {
100589
- let models = [];
100590
- if (!forceUpdate && existsSync24(ALL_MODELS_JSON_PATH)) {
100591
- try {
100592
- const cacheData = JSON.parse(readFileSync20(ALL_MODELS_JSON_PATH, "utf-8"));
100593
- const lastUpdated = new Date(cacheData.lastUpdated);
100594
- const now = new Date;
100595
- const ageInDays = (now.getTime() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24);
100596
- if (ageInDays <= CACHE_MAX_AGE_DAYS2) {
100597
- models = cacheData.models;
100598
- }
100599
- } catch (e) {}
100744
+ function formatModelDocPricing(pricing) {
100745
+ if (!pricing)
100746
+ return "N/A";
100747
+ const input = typeof pricing.input === "number" ? pricing.input : undefined;
100748
+ const output = typeof pricing.output === "number" ? pricing.output : undefined;
100749
+ if (input === undefined && output === undefined)
100750
+ return "N/A";
100751
+ if ((input ?? 0) === 0 && (output ?? 0) === 0)
100752
+ return "FREE";
100753
+ const avg = ((input ?? 0) + (output ?? 0)) / 2;
100754
+ return `$${avg.toFixed(2)}/1M`;
100755
+ }
100756
+ function formatModelDocContext(ctx) {
100757
+ if (!ctx || ctx <= 0)
100758
+ return "N/A";
100759
+ if (ctx >= 1e6)
100760
+ return `${Math.round(ctx / 1e6)}M`;
100761
+ return `${Math.round(ctx / 1000)}K`;
100762
+ }
100763
+ function formatModelDocCaps(caps) {
100764
+ if (!caps)
100765
+ return "\xB7";
100766
+ const parts = [];
100767
+ if (caps.tools)
100768
+ parts.push("T");
100769
+ if (caps.thinking)
100770
+ parts.push("R");
100771
+ if (caps.vision)
100772
+ parts.push("V");
100773
+ return parts.length > 0 ? parts.join("") : "\xB7";
100774
+ }
100775
+ async function searchAndPrintModels(query, jsonOutput) {
100776
+ let results;
100777
+ try {
100778
+ console.error(`\uD83D\uDD04 Searching Firebase catalog for "${query}"...`);
100779
+ results = await searchModels(query, 50);
100780
+ } catch (error2) {
100781
+ console.error(`\u274C Failed to reach Firebase model catalog: ${error2 instanceof Error ? error2.message : String(error2)}`);
100782
+ console.error(" Check your network connection.");
100783
+ process.exit(1);
100600
100784
  }
100601
- if (models.length === 0) {
100602
- console.error("\uD83D\uDD04 Fetching all models from OpenRouter (this may take a moment)...");
100603
- try {
100604
- const response = await fetch("https://openrouter.ai/api/v1/models");
100605
- if (!response.ok)
100606
- throw new Error(`API returned ${response.status}`);
100607
- const data = await response.json();
100608
- models = data.data;
100609
- mkdirSync11(CLAUDISH_CACHE_DIR2, { recursive: true });
100610
- writeFileSync13(ALL_MODELS_JSON_PATH, JSON.stringify({
100611
- lastUpdated: new Date().toISOString(),
100612
- models
100613
- }), "utf-8");
100614
- console.error(`\u2705 Cached ${models.length} models`);
100615
- } catch (error2) {
100616
- console.error(`\u274C Failed to fetch models: ${error2}`);
100617
- process.exit(1);
100785
+ if (results.length === 0) {
100786
+ if (jsonOutput) {
100787
+ console.log(JSON.stringify({ query, count: 0, models: [] }, null, 2));
100788
+ } else {
100789
+ console.log(`No models found matching "${query}"`);
100618
100790
  }
100791
+ return;
100619
100792
  }
100620
- const ollamaModels = await fetchOllamaModels();
100621
- if (ollamaModels.length > 0) {
100622
- console.error(`\uD83C\uDFE0 Found ${ollamaModels.length} local Ollama models`);
100623
- models = [...ollamaModels, ...models];
100624
- }
100625
- if (process.env.OPENAI_API_KEY) {
100626
- try {
100627
- const modelsDevResponse = await fetch("https://models.dev/api.json", {
100628
- signal: AbortSignal.timeout(5000)
100629
- });
100630
- if (modelsDevResponse.ok) {
100631
- const modelsDevData = await modelsDevResponse.json();
100632
- const openaiData = modelsDevData.openai;
100633
- if (openaiData?.models) {
100634
- const openaiModels = Object.entries(openaiData.models).filter(([id, _2]) => {
100635
- const lowerId = id.toLowerCase();
100636
- return lowerId.startsWith("gpt-") || lowerId.startsWith("o1-") || lowerId.startsWith("o3-") || lowerId.startsWith("o4-") || lowerId.startsWith("chatgpt-");
100637
- }).map(([id, m2]) => {
100638
- const inputCost = m2.cost?.input || 2;
100639
- const outputCost = m2.cost?.output || 8;
100640
- const contextLen = m2.limit?.context || 128000;
100641
- const inputModalities = m2.modalities?.input || [];
100642
- return {
100643
- id: `oai/${id}`,
100644
- name: m2.name || id,
100645
- description: `OpenAI direct model`,
100646
- context_length: contextLen,
100647
- pricing: {
100648
- prompt: String(inputCost / 1e6),
100649
- completion: String(outputCost / 1e6)
100650
- },
100651
- isOAIDirect: true,
100652
- supportsTools: m2.tool_call === true,
100653
- supportsReasoning: m2.reasoning === true,
100654
- supportsVision: inputModalities.includes("image") || inputModalities.includes("video")
100655
- };
100656
- });
100657
- console.error(`\uD83D\uDD11 Found ${openaiModels.length} OpenAI direct models`);
100658
- models = [...openaiModels, ...models];
100659
- }
100660
- }
100661
- } catch {}
100662
- }
100663
- if (process.env.GLM_CODING_API_KEY) {
100664
- try {
100665
- const glmCodingModels = await fetchGLMCodingModels();
100666
- if (glmCodingModels.length > 0) {
100667
- console.error(`\uD83D\uDD11 Found ${glmCodingModels.length} GLM Coding Plan models`);
100668
- models = [...glmCodingModels, ...models];
100669
- }
100670
- } catch {}
100671
- }
100672
- if (process.env.LITELLM_BASE_URL && process.env.LITELLM_API_KEY) {
100673
- try {
100674
- const litellmModels = await fetchLiteLLMModels(process.env.LITELLM_BASE_URL, process.env.LITELLM_API_KEY, forceUpdate);
100675
- if (litellmModels.length > 0) {
100676
- console.error(`\uD83D\uDD17 Found ${litellmModels.length} LiteLLM models`);
100677
- models = [...litellmModels, ...models];
100678
- }
100679
- } catch {}
100680
- }
100681
- const results = models.map((model) => {
100682
- const nameScore = fuzzyScore2(model.name || "", query);
100683
- const idScore = fuzzyScore2(model.id || "", query);
100684
- const descScore = fuzzyScore2(model.description || "", query) * 0.5;
100685
- return {
100686
- model,
100687
- score: Math.max(nameScore, idScore, descScore)
100688
- };
100689
- }).filter((item) => item.score > 0.2).sort((a, b2) => b2.score - a.score).slice(0, 20);
100690
- if (results.length === 0) {
100691
- console.log(`No models found matching "${query}"`);
100793
+ if (jsonOutput) {
100794
+ console.log(JSON.stringify({
100795
+ query,
100796
+ count: results.length,
100797
+ models: results.map((m2) => ({
100798
+ id: m2.modelId,
100799
+ provider: m2.provider,
100800
+ contextWindow: m2.contextWindow,
100801
+ pricing: m2.pricing,
100802
+ capabilities: m2.capabilities,
100803
+ aliases: m2.aliases,
100804
+ status: m2.status
100805
+ }))
100806
+ }, null, 2));
100692
100807
  return;
100693
100808
  }
100694
- const RED2 = "\x1B[31m";
100695
- const GREEN = "\x1B[32m";
100696
- const RESET = "\x1B[0m";
100697
- const DIM = "\x1B[2m";
100698
100809
  console.log(`
100699
100810
  Found ${results.length} matching models:
100700
100811
  `);
100701
- console.log(" Model Provider Pricing Context Score");
100812
+ console.log(" Model Provider Pricing Context Caps");
100702
100813
  console.log(" " + "\u2500".repeat(80));
100703
- for (const { model, score } of results) {
100704
- let fullModelId;
100705
- if (model.isLocal) {
100706
- fullModelId = model.id.replace("ollama/", "ollama@");
100707
- } else if (model.id.startsWith("zen/")) {
100708
- fullModelId = model.id.replace("zen/", "zen@");
100709
- } else if (model.id.startsWith("oai/") || model.isOAIDirect) {
100710
- fullModelId = model.id.replace("oai/", "oai@");
100711
- } else if (model.source === "LiteLLM" || model.id.startsWith("litellm@")) {
100712
- fullModelId = model.id;
100713
- } else {
100714
- fullModelId = `openrouter@${model.id}`;
100715
- }
100716
- const modelId = fullModelId.length > 30 ? fullModelId.substring(0, 27) + "..." : fullModelId;
100717
- const modelIdPadded = modelId.padEnd(30);
100718
- const providerName = model.id.split("/")[0];
100719
- const provider = providerName.length > 10 ? providerName.substring(0, 7) + "..." : providerName;
100720
- const providerPadded = provider.padEnd(10);
100721
- let pricing;
100722
- if (model.isLocal) {
100723
- pricing = "LOCAL";
100724
- } else {
100725
- const promptPrice = parseFloat(model.pricing?.prompt || "0") * 1e6;
100726
- const completionPrice = parseFloat(model.pricing?.completion || "0") * 1e6;
100727
- const avg = (promptPrice + completionPrice) / 2;
100728
- if (avg < 0) {
100729
- pricing = "varies";
100730
- } else if (avg === 0) {
100731
- pricing = "FREE";
100732
- } else {
100733
- pricing = `$${avg.toFixed(2)}/1M`;
100734
- }
100735
- }
100736
- const pricingPadded = pricing.padEnd(10);
100737
- const contextLen = model.context_length || model.top_provider?.context_length || 0;
100738
- const context = contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A";
100739
- const contextPadded = context.padEnd(7);
100740
- if (model.isLocal && model.supportsTools === false) {
100741
- console.log(` ${RED2}${modelIdPadded} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}% \u2717 no tools${RESET}`);
100742
- } else if (model.isLocal && model.supportsTools === true) {
100743
- console.log(` ${GREEN}${modelIdPadded}${RESET} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}%`);
100744
- } else {
100745
- console.log(` ${modelIdPadded} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}%`);
100746
- }
100814
+ for (const m2 of results) {
100815
+ const id = m2.modelId.length > 30 ? m2.modelId.substring(0, 27) + "..." : m2.modelId;
100816
+ const idPadded = id.padEnd(30);
100817
+ const prov = (m2.provider || "").padEnd(10);
100818
+ const price = formatModelDocPricing(m2.pricing).padEnd(10);
100819
+ const ctx = formatModelDocContext(m2.contextWindow).padEnd(7);
100820
+ const caps = formatModelDocCaps(m2.capabilities);
100821
+ console.log(` ${idPadded} ${prov} ${price} ${ctx} ${caps}`);
100747
100822
  }
100748
100823
  console.log("");
100749
- console.log(`${DIM}Local models: ${RED2}red${RESET}${DIM} = no tool support (incompatible), ${GREEN}green${RESET}${DIM} = compatible${RESET}`);
100824
+ console.log("Caps: T = tools R = reasoning V = vision");
100750
100825
  console.log("");
100751
- console.log("Use OpenRouter model: claudish --model openrouter@<provider/model-id>");
100752
- console.log("OpenAI direct model: claudish --model oai@<model-name>");
100753
- console.log("Local Ollama model: claudish --model ollama@<model-name>");
100754
- console.log("OpenCode Zen model: claudish --model zen@<model-id>");
100755
- console.log("LiteLLM proxy model: claudish --model litellm@<model-group>");
100756
- }
100757
- async function printAllModels(jsonOutput, forceUpdate) {
100758
- let models = [];
100759
- const [ollamaModels, zenModels] = await Promise.all([fetchOllamaModels(), fetchZenModels()]);
100760
- if (!forceUpdate && existsSync24(ALL_MODELS_JSON_PATH)) {
100826
+ console.log("Use any model by its ID: claudish --model <model-id>");
100827
+ console.log("Provider shortcuts: claudish --model or@<id> | google@<id> | oai@<id>");
100828
+ }
100829
+ function renderModelDocTable(models, showRank) {
100830
+ const header = showRank ? " # Model Provider Pricing Context Caps" : " Model Provider Pricing Context Caps";
100831
+ console.log(header);
100832
+ console.log(" " + "\u2500".repeat(80));
100833
+ for (const m2 of models) {
100834
+ const rankCell = showRank ? String(m2.rank ?? "").padStart(3) + " " : " ";
100835
+ const rawId = m2.modelId;
100836
+ const id = rawId.length > 30 ? rawId.substring(0, 27) + "..." : rawId;
100837
+ const idPadded = id.padEnd(30);
100838
+ const prov = (m2.provider || "").padEnd(10);
100839
+ const price = formatModelDocPricing(m2.pricing).padEnd(10);
100840
+ const ctx = formatModelDocContext(m2.contextWindow).padEnd(7);
100841
+ const caps = formatModelDocCaps(m2.capabilities);
100842
+ console.log(` ${rankCell}${idPadded} ${prov} ${price} ${ctx} ${caps}`);
100843
+ }
100844
+ }
100845
+ async function printLocalProvidersFooter() {
100846
+ console.log(`
100847
+ Local providers`);
100848
+ console.log(" " + "\u2500".repeat(70));
100849
+ let ollamaLine = " Ollama: not running";
100850
+ try {
100851
+ const ollamaModels = await fetchOllamaModels();
100852
+ if (ollamaModels.length > 0) {
100853
+ const toolCount = ollamaModels.filter((m2) => m2.supportsTools).length;
100854
+ ollamaLine = ` Ollama: ${ollamaModels.length} models installed (${toolCount} with tools) \u2014 use: claudish --model ollama@<name>`;
100855
+ }
100856
+ } catch {}
100857
+ console.log(ollamaLine);
100858
+ let litellmLine = " LiteLLM: not configured (set LITELLM_BASE_URL + LITELLM_API_KEY)";
100859
+ if (process.env.LITELLM_BASE_URL && process.env.LITELLM_API_KEY) {
100761
100860
  try {
100762
- const cacheData = JSON.parse(readFileSync20(ALL_MODELS_JSON_PATH, "utf-8"));
100763
- const lastUpdated = new Date(cacheData.lastUpdated);
100764
- const now = new Date;
100765
- const ageInDays = (now.getTime() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24);
100766
- if (ageInDays <= CACHE_MAX_AGE_DAYS2) {
100767
- models = cacheData.models;
100768
- if (!jsonOutput) {
100769
- console.error(`\u2713 Using cached models (last updated: ${cacheData.lastUpdated.split("T")[0]})`);
100770
- }
100861
+ const litellmModels = await fetchLiteLLMModels(process.env.LITELLM_BASE_URL, process.env.LITELLM_API_KEY, false);
100862
+ if (litellmModels.length > 0) {
100863
+ litellmLine = ` LiteLLM: ${litellmModels.length} model groups configured \u2014 use: claudish --model litellm@<group>`;
100864
+ } else {
100865
+ litellmLine = " LiteLLM: reachable but no model groups returned";
100771
100866
  }
100772
- } catch (e) {}
100773
- }
100774
- if (models.length === 0) {
100775
- console.error("\uD83D\uDD04 Fetching all models from OpenRouter...");
100776
- try {
100777
- const response = await fetch("https://openrouter.ai/api/v1/models");
100778
- if (!response.ok)
100779
- throw new Error(`API returned ${response.status}`);
100780
- const data = await response.json();
100781
- models = data.data;
100782
- mkdirSync11(CLAUDISH_CACHE_DIR2, { recursive: true });
100783
- writeFileSync13(ALL_MODELS_JSON_PATH, JSON.stringify({
100784
- lastUpdated: new Date().toISOString(),
100785
- models
100786
- }), "utf-8");
100787
- console.error(`\u2705 Cached ${models.length} models`);
100788
- } catch (error2) {
100789
- console.error(`\u274C Failed to fetch models: ${error2}`);
100790
- process.exit(1);
100867
+ } catch {
100868
+ litellmLine = " LiteLLM: configured but unreachable";
100791
100869
  }
100792
100870
  }
100871
+ console.log(litellmLine);
100872
+ }
100873
+ async function printTop100(jsonOutput) {
100874
+ let response;
100875
+ try {
100876
+ response = await getTop100Models();
100877
+ } catch (error2) {
100878
+ console.error(`\u274C Failed to load top-100 models from Firebase: ${error2 instanceof Error ? error2.message : String(error2)}`);
100879
+ console.error(" Check your network connection.");
100880
+ process.exit(1);
100881
+ }
100793
100882
  if (jsonOutput) {
100794
- const allModels = [...ollamaModels, ...zenModels, ...models];
100795
- console.log(JSON.stringify({
100796
- count: allModels.length,
100797
- localCount: ollamaModels.length,
100798
- zenCount: zenModels.length,
100799
- lastUpdated: new Date().toISOString().split("T")[0],
100800
- models: allModels.map((m2) => {
100801
- let id;
100802
- if (m2.isLocal) {
100803
- id = m2.id.replace("ollama/", "ollama@");
100804
- } else if (m2.isZen || m2.id.startsWith("zen/")) {
100805
- id = m2.id.replace("zen/", "zen@");
100806
- } else if (m2.source === "LiteLLM" || m2.id.startsWith("litellm@")) {
100807
- id = m2.id;
100808
- } else {
100809
- id = `openrouter@${m2.id}`;
100810
- }
100811
- return {
100812
- id,
100813
- name: m2.name,
100814
- context: m2.context_length || m2.top_provider?.context_length,
100815
- pricing: m2.pricing,
100816
- isLocal: m2.isLocal || false,
100817
- isZen: m2.isZen || false
100818
- };
100819
- })
100820
- }, null, 2));
100883
+ console.log(JSON.stringify(response, null, 2));
100821
100884
  return;
100822
100885
  }
100823
- const RED2 = "\x1B[31m";
100824
- const GREEN = "\x1B[32m";
100825
- const RESET = "\x1B[0m";
100826
- const DIM = "\x1B[2m";
100827
- if (ollamaModels.length > 0) {
100828
- const toolCapableCount = ollamaModels.filter((m2) => m2.supportsTools).length;
100829
- console.log(`
100830
- \uD83C\uDFE0 LOCAL OLLAMA MODELS (${ollamaModels.length} installed, ${toolCapableCount} with tool support):
100886
+ console.log(`
100887
+ Top ${response.total} models from Firebase (pool: ${response.poolSize} eligible)
100831
100888
  `);
100832
- console.log(" Model Size Params Tools");
100833
- console.log(" " + "\u2500".repeat(76));
100834
- for (const model of ollamaModels) {
100835
- const fullId = model.id.replace("ollama/", "ollama@");
100836
- const modelId = fullId.length > 35 ? fullId.substring(0, 32) + "..." : fullId;
100837
- const modelIdPadded = modelId.padEnd(38);
100838
- const size = model.size ? `${(model.size / 1e9).toFixed(1)}GB` : "N/A";
100839
- const sizePadded = size.padEnd(12);
100840
- const params = model.details?.parameter_size || "N/A";
100841
- const paramsPadded = params.padEnd(8);
100842
- if (model.supportsTools) {
100843
- console.log(` ${modelIdPadded} ${sizePadded} ${paramsPadded} ${GREEN}\u2713${RESET}`);
100844
- } else {
100845
- console.log(` ${RED2}${modelIdPadded} ${sizePadded} ${paramsPadded} \u2717 no tools${RESET}`);
100846
- }
100847
- }
100848
- console.log("");
100849
- console.log(` ${GREEN}\u2713${RESET} = Compatible with Claude Code (supports tool calling)`);
100850
- console.log(` ${RED2}\u2717${RESET} = Not compatible ${DIM}(Claude Code requires tool support)${RESET}`);
100851
- console.log("");
100852
- console.log(" Use: claudish --model ollama@<model-name>");
100853
- console.log(" Pull a compatible model: ollama pull llama3.2");
100889
+ if (response.models.length === 0) {
100890
+ console.log(" No eligible models in the catalog.");
100854
100891
  } else {
100855
- console.log(`
100856
- \uD83C\uDFE0 LOCAL OLLAMA: Not running or no models installed`);
100857
- console.log(" Start Ollama: ollama serve");
100858
- console.log(" Pull a model: ollama pull llama3.2");
100859
- }
100860
- if (zenModels.length > 0) {
100861
- const freeCount = zenModels.filter((m2) => m2.isFree).length;
100862
- console.log(`
100863
- \uD83D\uDD2E OPENCODE ZEN (${zenModels.length} models, ${freeCount} FREE - no API key needed):
100864
- `);
100865
- console.log(" Model Context Pricing Tools");
100866
- console.log(" " + "\u2500".repeat(68));
100867
- const sortedModels = [...zenModels].sort((a, b2) => {
100868
- if (a.isFree && !b2.isFree)
100869
- return -1;
100870
- if (!a.isFree && b2.isFree)
100871
- return 1;
100872
- return (b2.context_length || 0) - (a.context_length || 0);
100873
- });
100874
- for (const model of sortedModels) {
100875
- const fullId = model.id.replace("zen/", "zen@");
100876
- const modelId = fullId.length > 30 ? fullId.substring(0, 27) + "..." : fullId;
100877
- const modelIdPadded = modelId.padEnd(32);
100878
- const contextLen = model.context_length || 0;
100879
- const context = contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A";
100880
- const contextPadded = context.padEnd(10);
100881
- const pricing = model.isFree ? `${GREEN}FREE${RESET}` : `$${(parseFloat(model.pricing?.prompt || "0") + parseFloat(model.pricing?.completion || "0")).toFixed(1)}/M`;
100882
- const pricingPadded = model.isFree ? "FREE " : pricing.padEnd(12);
100883
- const tools = model.supportsTools ? `${GREEN}\u2713${RESET}` : `${RED2}\u2717${RESET}`;
100884
- console.log(` ${modelIdPadded} ${contextPadded} ${pricingPadded} ${tools}`);
100885
- }
100892
+ renderModelDocTable(response.models, true);
100886
100893
  console.log("");
100887
- console.log(` ${DIM}FREE models work without API key!${RESET}`);
100888
- console.log(" Use: claudish --model zen@<model-id>");
100894
+ console.log(" Caps: T = tools R = reasoning V = vision");
100889
100895
  }
100890
- if (process.env.LITELLM_BASE_URL && process.env.LITELLM_API_KEY) {
100891
- try {
100892
- const litellmModels = await fetchLiteLLMModels(process.env.LITELLM_BASE_URL, process.env.LITELLM_API_KEY, forceUpdate);
100893
- if (litellmModels.length > 0) {
100894
- console.log(`
100895
- \uD83D\uDD17 LITELLM PROXY (${litellmModels.length} model groups):
100896
- `);
100897
- console.log(" Model Context Pricing Tools");
100898
- console.log(" " + "\u2500".repeat(68));
100899
- for (const model of litellmModels) {
100900
- const modelId = model.id.length > 30 ? model.id.substring(0, 27) + "..." : model.id;
100901
- const modelIdPadded = modelId.padEnd(32);
100902
- const contextPadded = (model.context || "N/A").padEnd(10);
100903
- const pricingStr = model.isFree ? `${GREEN}FREE${RESET}` : model.pricing?.average || "N/A";
100904
- const pricingPadded = model.isFree ? "FREE " : pricingStr.padEnd(12);
100905
- const tools = model.supportsTools ? `${GREEN}\u2713${RESET}` : `${RED2}\u2717${RESET}`;
100906
- console.log(` ${modelIdPadded} ${contextPadded} ${pricingPadded} ${tools}`);
100907
- }
100908
- console.log("");
100909
- console.log(" Use: claudish --model litellm@<model-group>");
100910
- }
100911
- } catch {}
100896
+ await printLocalProvidersFooter();
100897
+ console.log("");
100898
+ console.log("Filter by provider: claudish --list-models --provider <slug>");
100899
+ console.log(" (e.g. opencode-zen, anthropic, openai, google, x-ai)");
100900
+ console.log("All providers: claudish --list-providers");
100901
+ console.log("Search by keyword: claudish -s <query>");
100902
+ console.log("Top recommended: claudish --top-models");
100903
+ console.log("");
100904
+ }
100905
+ async function printByProvider(providerSlug, jsonOutput) {
100906
+ let models;
100907
+ try {
100908
+ models = await getModelsByProvider(providerSlug, 200);
100909
+ } catch (error2) {
100910
+ console.error(`\u274C Failed to load provider catalog from Firebase: ${error2 instanceof Error ? error2.message : String(error2)}`);
100911
+ console.error(" Check your network connection.");
100912
+ process.exit(1);
100912
100913
  }
100913
- const byProvider = new Map;
100914
- for (const model of models) {
100915
- const provider = model.id.split("/")[0];
100916
- if (!byProvider.has(provider)) {
100917
- byProvider.set(provider, []);
100918
- }
100919
- byProvider.get(provider).push(model);
100914
+ if (jsonOutput) {
100915
+ console.log(JSON.stringify({ provider: providerSlug, count: models.length, models }, null, 2));
100916
+ return;
100920
100917
  }
100921
- const sortedProviders = [...byProvider.keys()].sort();
100922
- console.log(`
100923
- \u2601\uFE0F OPENROUTER MODELS (${models.length} total):
100924
- `);
100925
- for (const provider of sortedProviders) {
100926
- const providerModels = byProvider.get(provider);
100918
+ if (models.length === 0) {
100927
100919
  console.log(`
100928
- ${provider.toUpperCase()} (${providerModels.length} models)`);
100929
- console.log(" " + "\u2500".repeat(70));
100930
- for (const model of providerModels) {
100931
- const fullId = `openrouter@${model.id}`;
100932
- const modelId = fullId.length > 40 ? fullId.substring(0, 37) + "..." : fullId;
100933
- const modelIdPadded = modelId.padEnd(42);
100934
- const promptPrice = parseFloat(model.pricing?.prompt || "0") * 1e6;
100935
- const completionPrice = parseFloat(model.pricing?.completion || "0") * 1e6;
100936
- const avg = (promptPrice + completionPrice) / 2;
100937
- let pricing;
100938
- if (avg < 0) {
100939
- pricing = "varies";
100940
- } else if (avg === 0) {
100941
- pricing = "FREE";
100942
- } else {
100943
- pricing = `$${avg.toFixed(2)}/1M`;
100944
- }
100945
- const pricingPadded = pricing.padEnd(12);
100946
- const contextLen = model.context_length || model.top_provider?.context_length || 0;
100947
- const context = contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A";
100948
- const contextPadded = context.padEnd(8);
100949
- console.log(` ${modelIdPadded} ${pricingPadded} ${contextPadded}`);
100950
- }
100920
+ No active models found for provider "${providerSlug}". Try \`claudish -s <query>\` to search the full catalog.
100921
+ `);
100922
+ return;
100951
100923
  }
100952
100924
  console.log(`
100925
+ Provider: ${providerSlug} (${models.length} active models)
100953
100926
  `);
100954
- console.log("Use OpenRouter model: claudish --model openrouter@<provider/model-id>");
100955
- console.log(" Example: claudish --model openrouter@google/gemini-2.0-flash-exp:free");
100956
- console.log("Local Ollama model: claudish --model ollama@<model-name>");
100957
- console.log("OpenCode Zen model: claudish --model zen@<model-id>");
100958
- console.log("LiteLLM proxy model: claudish --model litellm@<model-group>");
100959
- console.log("Search: claudish --search <query>");
100960
- console.log("Top models: claudish --top-models");
100961
- }
100962
- function isCacheStale() {
100963
- const cachePath = existsSync24(CACHED_MODELS_PATH) ? CACHED_MODELS_PATH : BUNDLED_MODELS_PATH;
100964
- if (!existsSync24(cachePath)) {
100965
- return true;
100966
- }
100927
+ renderModelDocTable(models, false);
100928
+ console.log("");
100929
+ console.log(" Caps: T = tools R = reasoning V = vision");
100930
+ console.log("");
100931
+ console.log("Use any model: claudish --model <model-id>");
100932
+ console.log("Provider shortcuts: claudish --model or@<id> | google@<id> | oai@<id>");
100933
+ console.log("");
100934
+ }
100935
+ async function printRecommendedModels(jsonOutput, forceUpdate) {
100936
+ let doc2;
100967
100937
  try {
100968
- const jsonContent = readFileSync20(cachePath, "utf-8");
100969
- const data = JSON.parse(jsonContent);
100970
- if (!data.lastUpdated) {
100971
- return true;
100972
- }
100973
- const lastUpdated = new Date(data.lastUpdated);
100974
- const now = new Date;
100975
- const ageInDays = (now.getTime() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24);
100976
- return ageInDays > CACHE_MAX_AGE_DAYS2;
100938
+ doc2 = await getRecommendedModels({ forceRefresh: forceUpdate });
100977
100939
  } catch (error2) {
100978
- return true;
100940
+ console.error(`\u274C Failed to load recommended models: ${error2 instanceof Error ? error2.message : String(error2)}`);
100941
+ process.exit(1);
100979
100942
  }
100980
- }
100981
- async function updateModelsFromOpenRouter() {
100982
- console.error("\uD83D\uDD04 Updating model recommendations from OpenRouter...");
100983
- try {
100984
- const apiResponse = await fetch("https://openrouter.ai/api/v1/models?category=programming&order=top-weekly");
100985
- if (!apiResponse.ok) {
100986
- throw new Error(`OpenRouter API returned ${apiResponse.status}`);
100987
- }
100988
- const openrouterData = await apiResponse.json();
100989
- const topModels = openrouterData.data;
100990
- const recommendations = [];
100991
- const providers = new Set;
100992
- for (const model of topModels) {
100993
- const modelId = model.id;
100994
- const provider = modelId.split("/")[0];
100995
- if (provider === "anthropic") {
100996
- continue;
100997
- }
100998
- if (provider === "openrouter") {
100999
- continue;
101000
- }
101001
- if (providers.has(provider)) {
101002
- continue;
101003
- }
101004
- const name = model.name || modelId;
101005
- const description = model.description || `${name} model`;
101006
- const architecture = model.architecture || {};
101007
- const topProvider = model.top_provider || {};
101008
- const supportedParams = model.supported_parameters || [];
101009
- const promptPrice = parseFloat(model.pricing?.prompt || "0");
101010
- const completionPrice = parseFloat(model.pricing?.completion || "0");
101011
- const inputPrice = promptPrice > 0 ? `$${(promptPrice * 1e6).toFixed(2)}/1M` : "FREE";
101012
- const outputPrice = completionPrice > 0 ? `$${(completionPrice * 1e6).toFixed(2)}/1M` : "FREE";
101013
- const avgPrice = promptPrice > 0 || completionPrice > 0 ? `$${((promptPrice + completionPrice) / 2 * 1e6).toFixed(2)}/1M` : "FREE";
101014
- let category = "programming";
101015
- const lowerDesc = description.toLowerCase() + " " + name.toLowerCase();
101016
- if (lowerDesc.includes("vision") || lowerDesc.includes("vl-") || lowerDesc.includes("multimodal")) {
101017
- category = "vision";
101018
- } else if (lowerDesc.includes("reason")) {
101019
- category = "reasoning";
101020
- }
101021
- const bareId = modelId.split("/").pop().replace(/:free$/, "");
101022
- recommendations.push({
101023
- id: bareId,
101024
- openrouterId: modelId,
101025
- name,
101026
- description,
101027
- provider: provider.charAt(0).toUpperCase() + provider.slice(1),
101028
- category,
101029
- priority: recommendations.length + 1,
101030
- pricing: {
101031
- input: inputPrice,
101032
- output: outputPrice,
101033
- average: avgPrice
101034
- },
101035
- context: topProvider.context_length ? `${Math.floor(topProvider.context_length / 1000)}K` : "N/A",
101036
- maxOutputTokens: topProvider.max_completion_tokens || null,
101037
- modality: architecture.modality || "text->text",
101038
- supportsTools: supportedParams.includes("tools") || supportedParams.includes("tool_choice"),
101039
- supportsReasoning: supportedParams.includes("reasoning") || supportedParams.includes("include_reasoning"),
101040
- supportsVision: (architecture.input_modalities || []).includes("image") || (architecture.input_modalities || []).includes("video"),
101041
- isModerated: topProvider.is_moderated || false,
101042
- recommended: true
101043
- });
101044
- providers.add(provider);
100943
+ if (jsonOutput) {
100944
+ console.log(JSON.stringify(doc2, null, 2));
100945
+ return;
100946
+ }
100947
+ const lastUpdated = doc2.lastUpdated || "unknown";
100948
+ const { flagship, fast } = groupRecommendedModels(doc2.models);
100949
+ const providerByName = new Map(BUILTIN_PROVIDERS.map((p) => [p.name, p]));
100950
+ const getNativePrefix = (firebaseSlug) => {
100951
+ const canonical = FIREBASE_SLUG_TO_PROVIDER_NAME[firebaseSlug];
100952
+ if (!canonical)
100953
+ return null;
100954
+ const def = providerByName.get(canonical);
100955
+ if (!def || !def.shortcuts || def.shortcuts.length === 0)
100956
+ return null;
100957
+ return def.shortcuts[0];
100958
+ };
100959
+ const renderGroup = (group) => {
100960
+ const m2 = group.primary;
100961
+ const rawId = m2.id;
100962
+ const modelId = rawId.length > 28 ? rawId.substring(0, 25) + "..." : rawId;
100963
+ const modelIdPadded = modelId.padEnd(28);
100964
+ const pricing = normalizePricingDisplay(m2.pricing?.average);
100965
+ const pricingPadded = pricing.padEnd(10);
100966
+ const context = m2.context || "N/A";
100967
+ const contextPadded = context.padEnd(6);
100968
+ const caps = [];
100969
+ if (m2.supportsTools)
100970
+ caps.push("\uD83D\uDD27");
100971
+ if (m2.supportsReasoning)
100972
+ caps.push("\uD83E\uDDE0");
100973
+ if (m2.supportsVision)
100974
+ caps.push("\uD83D\uDC41\uFE0F");
100975
+ const capabilities = caps.join(" ");
100976
+ console.log(` ${modelIdPadded} ${pricingPadded} ${contextPadded} ${capabilities}`);
100977
+ const prefixes = collectRoutingPrefixes(group, getNativePrefix);
100978
+ if (prefixes.length > 0) {
100979
+ const viaLine = prefixes.map((p) => `${p}@`).join(" \xB7 ");
100980
+ console.log(` via: ${viaLine}`);
101045
100981
  }
101046
- let version2 = "1.2.0";
101047
- const existingPath = existsSync24(CACHED_MODELS_PATH) ? CACHED_MODELS_PATH : BUNDLED_MODELS_PATH;
101048
- if (existsSync24(existingPath)) {
101049
- try {
101050
- const existing = JSON.parse(readFileSync20(existingPath, "utf-8"));
101051
- version2 = existing.version || version2;
101052
- } catch {}
100982
+ };
100983
+ console.log(`
100984
+ Recommended Models (last updated: ${lastUpdated}):
100985
+ `);
100986
+ if (flagship.length > 0) {
100987
+ console.log("Flagship models");
100988
+ console.log(" " + "\u2500".repeat(70));
100989
+ for (let i = 0;i < flagship.length; i++) {
100990
+ renderGroup(flagship[i]);
100991
+ if (i < flagship.length - 1)
100992
+ console.log("");
101053
100993
  }
101054
- const updatedData = {
101055
- version: version2,
101056
- lastUpdated: new Date().toISOString().split("T")[0],
101057
- source: "https://openrouter.ai/models?categories=programming&fmt=cards&order=top-weekly",
101058
- models: recommendations
101059
- };
101060
- mkdirSync11(CLAUDISH_CACHE_DIR2, { recursive: true });
101061
- writeFileSync13(CACHED_MODELS_PATH, JSON.stringify(updatedData, null, 2), "utf-8");
101062
- console.error(`\u2705 Updated ${recommendations.length} models (last updated: ${updatedData.lastUpdated})`);
101063
- } catch (error2) {
101064
- console.error(`\u274C Failed to update models: ${error2 instanceof Error ? error2.message : String(error2)}`);
101065
- console.error(" Using cached models (if available)");
101066
100994
  }
101067
- }
101068
- async function checkAndUpdateModelsCache(forceUpdate = false) {
101069
- if (forceUpdate) {
101070
- console.error("\uD83D\uDD04 Force update requested...");
101071
- await updateModelsFromOpenRouter();
101072
- return;
100995
+ if (fast.length > 0) {
100996
+ if (flagship.length > 0)
100997
+ console.log("");
100998
+ console.log("Fast variants");
100999
+ console.log(" " + "\u2500".repeat(70));
101000
+ for (let i = 0;i < fast.length; i++) {
101001
+ renderGroup(fast[i]);
101002
+ if (i < fast.length - 1)
101003
+ console.log("");
101004
+ }
101073
101005
  }
101074
- if (isCacheStale()) {
101075
- console.error("\u26A0\uFE0F Model cache is stale (>2 days old), updating...");
101076
- await updateModelsFromOpenRouter();
101077
- } else {
101078
- try {
101079
- const cachePath = existsSync24(CACHED_MODELS_PATH) ? CACHED_MODELS_PATH : BUNDLED_MODELS_PATH;
101080
- const data = JSON.parse(readFileSync20(cachePath, "utf-8"));
101081
- console.error(`\u2713 Using cached models (last updated: ${data.lastUpdated})`);
101082
- } catch {}
101006
+ console.log("");
101007
+ console.log(" Capabilities: \uD83D\uDD27 Tools \uD83E\uDDE0 Reasoning \uD83D\uDC41\uFE0F Vision");
101008
+ const primaries = [...flagship, ...fast].map((g2) => g2.primary);
101009
+ const picks = computeQuickPicks(primaries);
101010
+ const pickLines = [];
101011
+ if (picks.budget)
101012
+ pickLines.push(` Budget \u2192 ${picks.budget.id} (${normalizePricingDisplay(picks.budget.pricing?.average)})`);
101013
+ if (picks.largeContext)
101014
+ pickLines.push(` Large ctx \u2192 ${picks.largeContext.id} (${picks.largeContext.context || "N/A"})`);
101015
+ if (picks.mostCapable)
101016
+ pickLines.push(` Most capable \u2192 ${picks.mostCapable.id}`);
101017
+ if (picks.visionCoding)
101018
+ pickLines.push(` Vision+code \u2192 ${picks.visionCoding.id}`);
101019
+ if (picks.agentic)
101020
+ pickLines.push(` Agentic \u2192 ${picks.agentic.id}`);
101021
+ if (pickLines.length > 0) {
101022
+ console.log("");
101023
+ console.log(" Quick picks:");
101024
+ for (const line of pickLines)
101025
+ console.log(line);
101083
101026
  }
101027
+ console.log("");
101028
+ console.log(" Set default: export CLAUDISH_MODEL=<model>");
101029
+ console.log(" or: claudish --model <model> ...");
101030
+ console.log("");
101031
+ console.log(" For more: claudish --list-models (browse full catalog)");
101032
+ console.log(" claudish --list-providers (list all providers + counts)");
101033
+ console.log(" claudish -s <query> (search by keyword)");
101034
+ console.log(" claudish --top-models --force-update (refresh from Firebase)");
101035
+ console.log("");
101084
101036
  }
101085
101037
  function printVersion() {
101086
101038
  console.log(`claudish version ${VERSION}`);
@@ -101530,9 +101482,15 @@ OPTIONS:
101530
101482
  --cost-tracker Enable cost tracking for API usage (NB!)
101531
101483
  --audit-costs Show cost analysis report
101532
101484
  --reset-costs Reset accumulated cost statistics
101533
- --models List ALL models (OpenRouter + OpenCode Zen + Ollama)
101534
- --models <query> Fuzzy search all models by name, ID, or description
101535
- --top-models List recommended/top programming models (curated)
101485
+ --list-models Top 100 ranked models from Firebase + local providers
101486
+ --list-models --provider <slug>
101487
+ Filter Firebase catalog to one provider
101488
+ (e.g. --provider opencode-zen, --provider anthropic)
101489
+ --list-providers List every provider + active-model count
101490
+ -s, --search <query> Search Firebase catalog by keyword \u2014 matches model ID,
101491
+ brand synonyms (chatgpt, claude, grok), gateway names
101492
+ (zen, oc, codex), or capabilities (reasoning, vision, free)
101493
+ --top-models List the curated recommended models (flagship + fast)
101536
101494
  --team <models> Run multiple models in parallel (comma-separated)
101537
101495
  Example: --team minimax-m2.5,kimi-k2.5 "prompt"
101538
101496
  --mode <mode> Team mode: default (grid), interactive, json
@@ -101541,7 +101499,7 @@ OPTIONS:
101541
101499
  1-token request (diagnostic, may incur tiny cost)
101542
101500
  --no-probe Skip live requests, show static chain only
101543
101501
  --probe-timeout <secs> Per-link timeout for live probes (default: 40)
101544
- --json Output in JSON format (use with --models, --top-models, --probe)
101502
+ --json Output in JSON format (use with --list-models, --top-models, --probe)
101545
101503
  --force-update Force refresh model cache from OpenRouter API
101546
101504
  --version Show version information
101547
101505
  -h, --help Show this help message
@@ -101590,7 +101548,7 @@ MODEL MAPPING (per-role override):
101590
101548
  --model-subagent <model> Model for sub-agents (Task tool)
101591
101549
 
101592
101550
  CUSTOM MODELS:
101593
- Claudish accepts ANY valid OpenRouter model ID, even if not in --list-models
101551
+ Claudish accepts ANY valid model ID from the Firebase catalog, even if not in --list-models
101594
101552
  Example: claudish --model openrouter@your_provider/custom-model-123 "task"
101595
101553
 
101596
101554
  MODES:
@@ -101758,14 +101716,14 @@ LOCAL MODELS (Ollama, LM Studio, vLLM):
101758
101716
  OLLAMA_HOST=http://192.168.1.50:11434 claudish --model ollama/llama3.2 "task"
101759
101717
 
101760
101718
  AVAILABLE MODELS:
101761
- List all models: claudish --models (includes OpenRouter, OpenCode Zen, Ollama)
101762
- Search models: claudish --models <query>
101763
- Top recommended: claudish --top-models
101719
+ Top 100 ranked: claudish --list-models (Firebase-ranked list + local providers)
101720
+ By provider: claudish --list-models --provider <slug> (e.g. opencode-zen, anthropic, openai, google, x-ai)
101721
+ All providers: claudish --list-providers (every provider + active-model count)
101722
+ Search models: claudish -s <query> (fuzzy: id, brand synonyms, gateways, capabilities)
101723
+ Top recommended: claudish --top-models (curated flagship + fast)
101764
101724
  Probe routing: claudish --probe minimax-m2.5 kimi-k2.5 gemini-3.1-pro-preview
101765
- Free models only: claudish --free (interactive selector with free models)
101766
- JSON output: claudish --models --json
101767
- Force cache update: claudish --models --force-update
101768
- (Cache auto-updates every 2 days)
101725
+ Free models only: claudish --free (interactive selector with free models)
101726
+ JSON output: claudish --list-models --json | claudish --top-models --json
101769
101727
 
101770
101728
  MORE INFO:
101771
101729
  GitHub: https://github.com/MadAppGang/claude-code
@@ -101813,15 +101771,15 @@ async function initializeClaudishSkill() {
101813
101771
  }
101814
101772
  try {
101815
101773
  if (!existsSync24(claudeDir)) {
101816
- mkdirSync11(claudeDir, { recursive: true });
101774
+ mkdirSync10(claudeDir, { recursive: true });
101817
101775
  console.log("\uD83D\uDCC1 Created .claude/ directory");
101818
101776
  }
101819
101777
  if (!existsSync24(skillsDir)) {
101820
- mkdirSync11(skillsDir, { recursive: true });
101778
+ mkdirSync10(skillsDir, { recursive: true });
101821
101779
  console.log("\uD83D\uDCC1 Created .claude/skills/ directory");
101822
101780
  }
101823
101781
  if (!existsSync24(claudishSkillDir)) {
101824
- mkdirSync11(claudishSkillDir, { recursive: true });
101782
+ mkdirSync10(claudishSkillDir, { recursive: true });
101825
101783
  console.log("\uD83D\uDCC1 Created .claude/skills/claudish-usage/ directory");
101826
101784
  }
101827
101785
  copyFileSync(sourceSkillPath, skillFile);
@@ -101860,151 +101818,27 @@ async function initializeClaudishSkill() {
101860
101818
  }
101861
101819
  }
101862
101820
  function printAvailableModels() {
101863
- let lastUpdated = "unknown";
101864
- let models = [];
101865
101821
  try {
101866
- const cachePath = existsSync24(CACHED_MODELS_PATH) ? CACHED_MODELS_PATH : BUNDLED_MODELS_PATH;
101867
- if (existsSync24(cachePath)) {
101868
- const data = JSON.parse(readFileSync20(cachePath, "utf-8"));
101869
- lastUpdated = data.lastUpdated || "unknown";
101870
- models = data.models || [];
101871
- }
101872
- } catch {
101873
101822
  const basicModels = getAvailableModels();
101874
101823
  const modelInfo = loadModelInfo();
101824
+ console.log("\nAvailable models (type `claudish --top-models` for full table):\n");
101875
101825
  for (const model of basicModels) {
101876
101826
  const info = modelInfo[model];
101827
+ if (!info)
101828
+ continue;
101877
101829
  console.log(` ${model}`);
101878
101830
  console.log(` ${info.name} - ${info.description}`);
101879
- console.log("");
101880
101831
  }
101881
- return;
101882
- }
101883
- console.log(`
101884
- Recommended Models (last updated: ${lastUpdated}):
101885
- `);
101886
- console.log(" Model Pricing Context Capabilities");
101887
- console.log(" " + "\u2500".repeat(66));
101888
- for (const model of models) {
101889
- const modelId = model.id.length > 28 ? model.id.substring(0, 25) + "..." : model.id;
101890
- const modelIdPadded = modelId.padEnd(28);
101891
- let pricing = model.pricing?.average || "N/A";
101892
- if (pricing.includes("-1000000")) {
101893
- pricing = "varies";
101894
- } else if (pricing === "$0.00/1M" || pricing === "FREE") {
101895
- pricing = "FREE";
101896
- }
101897
- const pricingPadded = pricing.padEnd(10);
101898
- const context = model.context || "N/A";
101899
- const contextPadded = context.padEnd(7);
101900
- const tools = model.supportsTools ? "\uD83D\uDD27" : " ";
101901
- const reasoning = model.supportsReasoning ? "\uD83E\uDDE0" : " ";
101902
- const vision = model.supportsVision ? "\uD83D\uDC41\uFE0F " : " ";
101903
- const capabilities = `${tools} ${reasoning} ${vision}`;
101904
- console.log(` ${modelIdPadded} ${pricingPadded} ${contextPadded} ${capabilities}`);
101905
- }
101906
- console.log("");
101907
- console.log(" Capabilities: \uD83D\uDD27 Tools \uD83E\uDDE0 Reasoning \uD83D\uDC41\uFE0F Vision");
101908
- console.log("");
101909
- console.log("Set default with: export CLAUDISH_MODEL=<model>");
101910
- console.log(" or: export ANTHROPIC_MODEL=<model>");
101911
- console.log("Or use: claudish --model <model> ...");
101912
- console.log(`
101913
- Force update: claudish --list-models --force-update
101914
- `);
101915
- }
101916
- function printAvailableModelsJSON() {
101917
- const jsonPath = existsSync24(CACHED_MODELS_PATH) ? CACHED_MODELS_PATH : BUNDLED_MODELS_PATH;
101918
- try {
101919
- const jsonContent = readFileSync20(jsonPath, "utf-8");
101920
- const data = JSON.parse(jsonContent);
101921
- console.log(JSON.stringify(data, null, 2));
101832
+ console.log("");
101922
101833
  } catch (error2) {
101923
- const models = getAvailableModels();
101924
- const modelInfo = loadModelInfo();
101925
- const output = {
101926
- version: VERSION,
101927
- lastUpdated: new Date().toISOString().split("T")[0],
101928
- source: "runtime",
101929
- models: models.filter((m2) => m2 !== "custom").map((modelId) => {
101930
- const info = modelInfo[modelId];
101931
- return {
101932
- id: modelId,
101933
- name: info.name,
101934
- description: info.description,
101935
- provider: info.provider,
101936
- priority: info.priority
101937
- };
101938
- })
101939
- };
101940
- console.log(JSON.stringify(output, null, 2));
101834
+ console.error(`Failed to load available models: ${error2 instanceof Error ? error2.message : String(error2)}`);
101941
101835
  }
101942
101836
  }
101943
- async function fetchZenModels() {
101944
- try {
101945
- const response = await fetch("https://models.dev/api.json", {
101946
- signal: AbortSignal.timeout(1e4)
101947
- });
101948
- if (!response.ok) {
101949
- return [];
101950
- }
101951
- const data = await response.json();
101952
- const opencode = data.opencode;
101953
- if (!opencode?.models)
101954
- return [];
101955
- return Object.entries(opencode.models).map(([id, m2]) => {
101956
- const isFree = m2.cost?.input === 0 && m2.cost?.output === 0;
101957
- return {
101958
- id: `zen/${id}`,
101959
- name: m2.name || id,
101960
- context_length: m2.limit?.context || 128000,
101961
- max_output: m2.limit?.output || 32000,
101962
- pricing: isFree ? { prompt: "0", completion: "0" } : { prompt: String(m2.cost?.input || 0), completion: String(m2.cost?.output || 0) },
101963
- isZen: true,
101964
- isFree,
101965
- supportsTools: m2.tool_call || false,
101966
- supportsReasoning: m2.reasoning || false
101967
- };
101968
- });
101969
- } catch {
101970
- return [];
101971
- }
101972
- }
101973
- async function fetchGLMCodingModels() {
101974
- try {
101975
- const response = await fetch("https://models.dev/api.json", {
101976
- signal: AbortSignal.timeout(5000)
101977
- });
101978
- if (!response.ok) {
101979
- return [];
101980
- }
101981
- const data = await response.json();
101982
- const codingPlan = data["zai-coding-plan"];
101983
- if (!codingPlan?.models)
101984
- return [];
101985
- return Object.entries(codingPlan.models).map(([id, m2]) => {
101986
- const inputModalities = m2.modalities?.input || [];
101987
- return {
101988
- id: `gc/${id}`,
101989
- name: m2.name || id,
101990
- description: `GLM Coding Plan model (subscription)`,
101991
- context_length: m2.limit?.context || 131072,
101992
- pricing: { prompt: "0", completion: "0" },
101993
- isGLMCoding: true,
101994
- isSubscription: true,
101995
- supportsTools: m2.tool_call || false,
101996
- supportsReasoning: m2.reasoning || false,
101997
- supportsVision: inputModalities.includes("image") || inputModalities.includes("video")
101998
- };
101999
- });
102000
- } catch {
102001
- return [];
102002
- }
102003
- }
102004
- var __filename4, __dirname4, CACHE_MAX_AGE_DAYS2 = 2, CLAUDISH_CACHE_DIR2, BUNDLED_MODELS_PATH, CACHED_MODELS_PATH, ALL_MODELS_JSON_PATH;
101837
+ var __filename4, __dirname4;
102005
101838
  var init_cli = __esm(async () => {
102006
101839
  init_config();
102007
101840
  init_model_loader();
101841
+ init_provider_definitions();
102008
101842
  init_profile_config();
102009
101843
  init_model_parser();
102010
101844
  init_auto_route();
@@ -102016,11 +101850,7 @@ var init_cli = __esm(async () => {
102016
101850
  init_provider_resolver();
102017
101851
  await init_probe_tui_runtime();
102018
101852
  __filename4 = fileURLToPath4(import.meta.url);
102019
- __dirname4 = dirname4(__filename4);
102020
- CLAUDISH_CACHE_DIR2 = join25(homedir23(), ".claudish");
102021
- BUNDLED_MODELS_PATH = join25(__dirname4, "../recommended-models.json");
102022
- CACHED_MODELS_PATH = join25(CLAUDISH_CACHE_DIR2, "recommended-models.json");
102023
- ALL_MODELS_JSON_PATH = join25(CLAUDISH_CACHE_DIR2, "all-models.json");
101853
+ __dirname4 = dirname5(__filename4);
102024
101854
  });
102025
101855
 
102026
101856
  // src/update-checker.ts
@@ -102031,7 +101861,7 @@ __export(exports_update_checker, {
102031
101861
  clearCache: () => clearCache,
102032
101862
  checkForUpdates: () => checkForUpdates
102033
101863
  });
102034
- import { existsSync as existsSync26, mkdirSync as mkdirSync12, readFileSync as readFileSync21, unlinkSync as unlinkSync8, writeFileSync as writeFileSync14 } from "fs";
101864
+ import { existsSync as existsSync26, mkdirSync as mkdirSync11, readFileSync as readFileSync21, unlinkSync as unlinkSync8, writeFileSync as writeFileSync12 } from "fs";
102035
101865
  import { homedir as homedir24, platform as platform2, tmpdir } from "os";
102036
101866
  import { join as join26 } from "path";
102037
101867
  function getCacheFilePath() {
@@ -102044,7 +101874,7 @@ function getCacheFilePath() {
102044
101874
  }
102045
101875
  try {
102046
101876
  if (!existsSync26(cacheDir)) {
102047
- mkdirSync12(cacheDir, { recursive: true });
101877
+ mkdirSync11(cacheDir, { recursive: true });
102048
101878
  }
102049
101879
  return join26(cacheDir, "update-check.json");
102050
101880
  } catch {
@@ -102070,7 +101900,7 @@ function writeCache(latestVersion) {
102070
101900
  lastCheck: Date.now(),
102071
101901
  latestVersion
102072
101902
  };
102073
- writeFileSync14(cachePath, JSON.stringify(data), "utf-8");
101903
+ writeFileSync12(cachePath, JSON.stringify(data), "utf-8");
102074
101904
  } catch {}
102075
101905
  }
102076
101906
  function isCacheValid(cache) {
@@ -102381,218 +102211,36 @@ __export(exports_model_selector, {
102381
102211
  promptForApiKey: () => promptForApiKey,
102382
102212
  confirmAction: () => confirmAction
102383
102213
  });
102384
- import { readFileSync as readFileSync22, writeFileSync as writeFileSync15, existsSync as existsSync27, mkdirSync as mkdirSync13 } from "fs";
102385
- import { join as join27, dirname as dirname5 } from "path";
102386
- import { homedir as homedir25 } from "os";
102387
- import { fileURLToPath as fileURLToPath5 } from "url";
102388
- function loadRecommendedModels2() {
102389
- const cachedPath = join27(CLAUDISH_CACHE_DIR3, "recommended-models-cache.json");
102390
- if (existsSync27(cachedPath)) {
102391
- try {
102392
- const data = JSON.parse(readFileSync22(cachedPath, "utf-8"));
102393
- if (data.models && data.models.length > 0) {
102394
- return data.models.map((model) => ({
102395
- ...model,
102396
- source: "Recommended"
102397
- }));
102398
- }
102399
- } catch {}
102400
- }
102401
- if (existsSync27(RECOMMENDED_MODELS_JSON_PATH)) {
102402
- try {
102403
- const content = readFileSync22(RECOMMENDED_MODELS_JSON_PATH, "utf-8");
102404
- const data = JSON.parse(content);
102405
- return (data.models || []).map((model) => ({
102406
- ...model,
102407
- source: "Recommended"
102408
- }));
102409
- } catch {
102410
- return [];
102411
- }
102412
- }
102413
- return [];
102414
- }
102415
- async function fetchAllModels(forceUpdate = false) {
102416
- if (!forceUpdate && existsSync27(ALL_MODELS_JSON_PATH2)) {
102417
- try {
102418
- const cacheData = JSON.parse(readFileSync22(ALL_MODELS_JSON_PATH2, "utf-8"));
102419
- const lastUpdated = new Date(cacheData.lastUpdated);
102420
- const now = new Date;
102421
- const ageInDays = (now.getTime() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24);
102422
- if (ageInDays <= CACHE_MAX_AGE_DAYS3) {
102423
- return cacheData.models;
102424
- }
102425
- } catch {}
102426
- }
102427
- console.log("Fetching models from OpenRouter...");
102428
- try {
102429
- const response = await fetch("https://openrouter.ai/api/v1/models");
102430
- if (!response.ok)
102431
- throw new Error(`API returned ${response.status}`);
102432
- const data = await response.json();
102433
- const models = data.data;
102434
- mkdirSync13(CLAUDISH_CACHE_DIR3, { recursive: true });
102435
- writeFileSync15(ALL_MODELS_JSON_PATH2, JSON.stringify({
102436
- lastUpdated: new Date().toISOString(),
102437
- models
102438
- }), "utf-8");
102439
- console.log(`Cached ${models.length} models`);
102440
- return models;
102441
- } catch (error2) {
102442
- console.error(`Failed to fetch models: ${error2}`);
102443
- return [];
102444
- }
102445
- }
102446
- function toModelInfo(model) {
102447
- const provider = model.id.split("/")[0];
102448
- const contextLen = model.context_length || model.top_provider?.context_length || 0;
102449
- const promptPrice = parseFloat(model.pricing?.prompt || "0");
102450
- const completionPrice = parseFloat(model.pricing?.completion || "0");
102451
- const isFree = promptPrice === 0 && completionPrice === 0;
102452
- let pricingStr = "N/A";
102453
- if (isFree) {
102454
- pricingStr = "FREE";
102455
- } else if (model.pricing) {
102456
- const avgPrice = (promptPrice + completionPrice) / 2;
102457
- if (avgPrice < 0.001) {
102458
- pricingStr = `$${(avgPrice * 1e6).toFixed(2)}/1M`;
102459
- } else {
102460
- pricingStr = `$${avgPrice.toFixed(4)}/1K`;
102461
- }
102462
- }
102463
- return {
102464
- id: `openrouter@${model.id}`,
102465
- name: model.name || model.id,
102466
- description: model.description || "",
102467
- provider: provider.charAt(0).toUpperCase() + provider.slice(1),
102468
- pricing: {
102469
- input: model.pricing?.prompt || "N/A",
102470
- output: model.pricing?.completion || "N/A",
102471
- average: pricingStr
102472
- },
102473
- context: contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A",
102474
- contextLength: contextLen,
102475
- supportsTools: (model.supported_parameters || []).includes("tools"),
102476
- supportsReasoning: (model.supported_parameters || []).includes("reasoning"),
102477
- supportsVision: (model.architecture?.input_modalities || []).includes("image"),
102478
- isFree,
102479
- source: "OpenRouter"
102480
- };
102481
- }
102482
- async function fetchZenGoModels() {
102483
- const apiKey = process.env.OPENCODE_API_KEY;
102484
- if (!apiKey)
102485
- return [];
102486
- const ZEN_GO_BASE = process.env.OPENCODE_BASE_URL ? process.env.OPENCODE_BASE_URL.replace("/zen", "/zen/go") : "https://opencode.ai/zen/go";
102214
+ function loadRecommendedModels() {
102487
102215
  try {
102488
- const mdevResp = await fetch("https://models.dev/api.json", {
102489
- signal: AbortSignal.timeout(5000)
102490
- });
102491
- if (!mdevResp.ok)
102492
- return [];
102493
- const mdevData = await mdevResp.json();
102494
- const ocModels = mdevData?.opencode?.models ?? {};
102495
- const candidateIds = Object.keys(ocModels);
102496
- const fallbackIds = ["glm-5"];
102497
- const probeResults = await Promise.all(candidateIds.map(async (modelId) => {
102498
- try {
102499
- const r = await fetch(`${ZEN_GO_BASE}/v1/chat/completions`, {
102500
- method: "POST",
102501
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
102502
- body: JSON.stringify({
102503
- model: modelId,
102504
- messages: [{ role: "user", content: "hi" }],
102505
- max_tokens: 1
102506
- }),
102507
- signal: AbortSignal.timeout(8000)
102508
- });
102509
- if (!r.ok)
102510
- return null;
102511
- const body = await r.json().catch(() => ({}));
102512
- return Array.isArray(body?.choices) && body.choices.length > 0 ? modelId : null;
102513
- } catch {
102514
- return null;
102515
- }
102216
+ const doc2 = getRecommendedModelsSync();
102217
+ return doc2.models.map((model) => ({
102218
+ id: model.id,
102219
+ name: model.name,
102220
+ description: model.description,
102221
+ provider: model.provider,
102222
+ pricing: model.pricing,
102223
+ context: model.context,
102224
+ contextLength: parseContextString(model.context),
102225
+ supportsTools: model.supportsTools,
102226
+ supportsReasoning: model.supportsReasoning,
102227
+ supportsVision: model.supportsVision,
102228
+ source: "Recommended"
102516
102229
  }));
102517
- const discoveredIds = probeResults.filter(Boolean);
102518
- const goModelIds = discoveredIds.length > 0 ? discoveredIds : fallbackIds;
102519
- return goModelIds.map((id) => {
102520
- const m2 = ocModels[id];
102521
- if (!m2)
102522
- return null;
102523
- const inputModalities = m2.modalities?.input ?? [];
102524
- return {
102525
- id: `zgo@${id}`,
102526
- name: m2.name || id,
102527
- description: `OpenCode Zen Go plan model`,
102528
- provider: "Zen Go",
102529
- pricing: { input: "PLAN", output: "PLAN", average: "PLAN" },
102530
- context: m2.limit?.context ? `${Math.round(m2.limit.context / 1000)}K` : "128K",
102531
- contextLength: m2.limit?.context || 128000,
102532
- supportsTools: m2.tool_call === true,
102533
- supportsReasoning: m2.reasoning || false,
102534
- supportsVision: inputModalities.includes("image") || inputModalities.includes("video"),
102535
- isFree: false,
102536
- source: "Zen"
102537
- };
102538
- }).filter(Boolean);
102539
102230
  } catch {
102540
102231
  return [];
102541
102232
  }
102542
102233
  }
102543
- async function fetchZenFreeModels() {
102544
- try {
102545
- const ZEN_BASE = process.env.OPENCODE_BASE_URL || "https://opencode.ai/zen";
102546
- const ZEN_API_KEY = process.env.OPENCODE_API_KEY || "public";
102547
- const [mdevResp, liveResp] = await Promise.all([
102548
- fetch("https://models.dev/api.json", { signal: AbortSignal.timeout(5000) }),
102549
- fetch(`${ZEN_BASE}/v1/models`, {
102550
- headers: { Authorization: `Bearer ${ZEN_API_KEY}` },
102551
- signal: AbortSignal.timeout(5000)
102552
- })
102553
- ]);
102554
- if (!mdevResp.ok)
102555
- return [];
102556
- const mdevData = await mdevResp.json();
102557
- const opencode = mdevData.opencode;
102558
- if (!opencode?.models)
102559
- return [];
102560
- const liveIds = new Set;
102561
- if (liveResp.ok) {
102562
- const liveData = await liveResp.json();
102563
- for (const m2 of liveData.data ?? [])
102564
- liveIds.add(m2.id);
102565
- }
102566
- return Object.entries(opencode.models).filter(([id, m2]) => {
102567
- const isFree = m2.cost?.input === 0 && m2.cost?.output === 0;
102568
- const supportsTools = m2.tool_call === true;
102569
- const isLive = liveIds.size === 0 || liveIds.has(id);
102570
- return isFree && supportsTools && isLive;
102571
- }).map(([id, m2]) => {
102572
- const inputModalities = m2.modalities?.input || [];
102573
- const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
102574
- return {
102575
- id: `zen@${id}`,
102576
- name: m2.name || id,
102577
- description: `OpenCode Zen free model`,
102578
- provider: "Zen",
102579
- pricing: {
102580
- input: "FREE",
102581
- output: "FREE",
102582
- average: "FREE"
102583
- },
102584
- context: m2.limit?.context ? `${Math.round(m2.limit.context / 1000)}K` : "128K",
102585
- contextLength: m2.limit?.context || 128000,
102586
- supportsTools: true,
102587
- supportsReasoning: m2.reasoning || false,
102588
- supportsVision,
102589
- isFree: true,
102590
- source: "Zen"
102591
- };
102592
- });
102593
- } catch {
102594
- return [];
102595
- }
102234
+ function parseContextString(ctx) {
102235
+ if (!ctx || ctx === "N/A")
102236
+ return 0;
102237
+ const upper = ctx.toUpperCase();
102238
+ if (upper.endsWith("M"))
102239
+ return parseFloat(upper) * 1e6;
102240
+ if (upper.endsWith("K"))
102241
+ return parseFloat(upper) * 1000;
102242
+ const n = parseInt(upper, 10);
102243
+ return isNaN(n) ? 0 : n;
102596
102244
  }
102597
102245
  function getXAIContextWindow(modelId) {
102598
102246
  const id = modelId.toLowerCase();
@@ -102743,239 +102391,15 @@ async function fetchGeminiModels() {
102743
102391
  return [];
102744
102392
  }
102745
102393
  }
102746
- async function fetchOpenAIModels() {
102747
- const apiKey = process.env.OPENAI_API_KEY;
102748
- if (!apiKey) {
102749
- return [];
102750
- }
102751
- try {
102752
- const response = await fetch("https://models.dev/api.json", {
102753
- signal: AbortSignal.timeout(5000)
102754
- });
102755
- if (!response.ok) {
102756
- return [];
102757
- }
102758
- const data = await response.json();
102759
- const openaiData = data.openai;
102760
- if (!openaiData?.models)
102761
- return [];
102762
- return Object.entries(openaiData.models).filter(([id, _2]) => {
102763
- const lowerId = id.toLowerCase();
102764
- return lowerId.startsWith("gpt-") || lowerId.startsWith("o1-") || lowerId.startsWith("o3-") || lowerId.startsWith("o4-") || lowerId.startsWith("chatgpt-");
102765
- }).map(([id, m2]) => {
102766
- const inputCost = m2.cost?.input || 2;
102767
- const outputCost = m2.cost?.output || 8;
102768
- const avgCost = (inputCost + outputCost) / 2;
102769
- const contextLength = m2.limit?.context || 128000;
102770
- const contextStr = contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`;
102771
- const inputModalities = m2.modalities?.input || [];
102772
- const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
102773
- return {
102774
- id: `oai@${id}`,
102775
- name: m2.name || id,
102776
- description: `OpenAI model`,
102777
- provider: "OpenAI",
102778
- pricing: {
102779
- input: `$${inputCost.toFixed(2)}`,
102780
- output: `$${outputCost.toFixed(2)}`,
102781
- average: `$${avgCost.toFixed(2)}/1M`
102782
- },
102783
- context: contextStr,
102784
- contextLength,
102785
- supportsTools: m2.tool_call === true,
102786
- supportsReasoning: m2.reasoning === true,
102787
- supportsVision,
102788
- isFree: false,
102789
- source: "OpenAI"
102790
- };
102791
- });
102792
- } catch {
102793
- return [];
102794
- }
102795
- }
102796
- async function fetchGLMCodingModels2() {
102797
- const apiKey = process.env.GLM_CODING_API_KEY;
102798
- if (!apiKey) {
102799
- return [];
102800
- }
102801
- try {
102802
- const response = await fetch("https://models.dev/api.json", {
102803
- signal: AbortSignal.timeout(5000)
102804
- });
102805
- if (!response.ok) {
102806
- return [];
102807
- }
102808
- const data = await response.json();
102809
- const codingPlan = data["zai-coding-plan"];
102810
- if (!codingPlan?.models)
102811
- return [];
102812
- return Object.entries(codingPlan.models).filter(([_2, m2]) => m2.tool_call === true).map(([id, m2]) => {
102813
- const inputModalities = m2.modalities?.input || [];
102814
- const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
102815
- const contextLength = m2.limit?.context || 131072;
102816
- return {
102817
- id: `gc@${id}`,
102818
- name: m2.name || id,
102819
- description: `GLM Coding Plan (subscription)`,
102820
- provider: "GLM Coding",
102821
- pricing: {
102822
- input: "SUB",
102823
- output: "SUB",
102824
- average: "SUB"
102825
- },
102826
- context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
102827
- contextLength,
102828
- supportsTools: true,
102829
- supportsReasoning: m2.reasoning || false,
102830
- supportsVision,
102831
- isFree: false,
102832
- source: "GLM Coding"
102833
- };
102834
- });
102835
- } catch {
102836
- return [];
102837
- }
102838
- }
102839
- async function fetchGLMDirectModels() {
102840
- try {
102841
- const response = await fetch("https://models.dev/api.json", {
102842
- signal: AbortSignal.timeout(5000)
102843
- });
102844
- if (!response.ok) {
102845
- return [];
102846
- }
102847
- const data = await response.json();
102848
- const codingPlan = data["zai-coding-plan"];
102849
- if (!codingPlan?.models)
102850
- return [];
102851
- return Object.entries(codingPlan.models).filter(([_2, m2]) => m2.tool_call === true).map(([id, m2]) => {
102852
- const inputModalities = m2.modalities?.input || [];
102853
- const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
102854
- const contextLength = m2.limit?.context || 131072;
102855
- const inputCost = m2.cost?.input || 0;
102856
- const outputCost = m2.cost?.output || 0;
102857
- const isFree = inputCost === 0 && outputCost === 0;
102858
- return {
102859
- id: `glm@${id}`,
102860
- name: m2.name || id,
102861
- description: `GLM/Zhipu direct API`,
102862
- provider: "GLM",
102863
- pricing: {
102864
- input: isFree ? "FREE" : `$${inputCost.toFixed(2)}`,
102865
- output: isFree ? "FREE" : `$${outputCost.toFixed(2)}`,
102866
- average: isFree ? "FREE" : `$${((inputCost + outputCost) / 2).toFixed(2)}/1M`
102867
- },
102868
- context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
102869
- contextLength,
102870
- supportsTools: true,
102871
- supportsReasoning: m2.reasoning || false,
102872
- supportsVision,
102873
- isFree,
102874
- source: "GLM"
102875
- };
102876
- });
102877
- } catch {
102878
- return [];
102879
- }
102880
- }
102881
- async function fetchOllamaCloudModels() {
102882
- try {
102883
- const response = await fetch("https://models.dev/api.json", {
102884
- signal: AbortSignal.timeout(5000)
102885
- });
102886
- if (!response.ok) {
102887
- return [];
102888
- }
102889
- const data = await response.json();
102890
- const ollamaCloud = data["ollama-cloud"];
102891
- if (!ollamaCloud?.models)
102892
- return [];
102893
- return Object.entries(ollamaCloud.models).filter(([_2, m2]) => m2.tool_call === true).map(([id, m2]) => {
102894
- const inputModalities = m2.modalities?.input || [];
102895
- const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
102896
- const contextLength = m2.limit?.context || 131072;
102897
- return {
102898
- id: `oc@${id}`,
102899
- name: m2.name || id,
102900
- description: `OllamaCloud`,
102901
- provider: "OllamaCloud",
102902
- pricing: {
102903
- input: "N/A",
102904
- output: "N/A",
102905
- average: "N/A"
102906
- },
102907
- context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
102908
- contextLength,
102909
- supportsTools: true,
102910
- supportsReasoning: m2.reasoning || false,
102911
- supportsVision,
102912
- source: "OllamaCloud"
102913
- };
102914
- });
102915
- } catch {
102916
- return [];
102917
- }
102918
- }
102919
- function shouldRefreshForFreeModels() {
102920
- if (!existsSync27(ALL_MODELS_JSON_PATH2)) {
102921
- return true;
102922
- }
102923
- try {
102924
- const cacheData = JSON.parse(readFileSync22(ALL_MODELS_JSON_PATH2, "utf-8"));
102925
- const lastUpdated = new Date(cacheData.lastUpdated);
102926
- const now = new Date;
102927
- const ageInHours = (now.getTime() - lastUpdated.getTime()) / (1000 * 60 * 60);
102928
- return ageInHours > FREE_MODELS_CACHE_MAX_AGE_HOURS;
102929
- } catch {
102930
- return true;
102931
- }
102932
- }
102933
102394
  async function getFreeModels() {
102934
- const forceUpdate = shouldRefreshForFreeModels();
102935
- const [allModels, zenModels] = await Promise.all([
102936
- fetchAllModels(forceUpdate),
102937
- fetchZenFreeModels()
102938
- ]);
102939
- const openRouterFreeModels = allModels.filter((model) => {
102940
- if (!model.id?.endsWith(":free"))
102941
- return false;
102942
- const supportsTools = (model.supported_parameters || []).includes("tools");
102943
- if (!supportsTools)
102944
- return false;
102945
- return true;
102946
- });
102947
- openRouterFreeModels.sort((a, b2) => {
102948
- const contextA = a.context_length || a.top_provider?.context_length || 0;
102949
- const contextB = b2.context_length || b2.top_provider?.context_length || 0;
102950
- return contextB - contextA;
102951
- });
102952
- const openRouterModels = openRouterFreeModels.slice(0, 20).map(toModelInfo);
102953
- const combined = [...zenModels, ...openRouterModels];
102954
- combined.sort((a, b2) => {
102955
- if (a.source === "Zen" && b2.source !== "Zen")
102956
- return -1;
102957
- if (a.source !== "Zen" && b2.source === "Zen")
102958
- return 1;
102959
- return (b2.contextLength || 0) - (a.contextLength || 0);
102960
- });
102961
- return combined;
102395
+ return [];
102962
102396
  }
102963
102397
  async function getAllModelsForSearch(forceUpdate = false) {
102964
102398
  const litellmBaseUrl = process.env.LITELLM_BASE_URL;
102965
102399
  const litellmApiKey = process.env.LITELLM_API_KEY;
102966
102400
  const allEntries = [
102967
- {
102968
- name: "OpenRouter",
102969
- promise: () => fetchAllModels(forceUpdate).then((models) => models.map(toModelInfo))
102970
- },
102971
102401
  { name: "xAI", provider: "xai", promise: () => fetchXAIModels() },
102972
102402
  { name: "Gemini", provider: "google", promise: () => fetchGeminiModels() },
102973
- { name: "OpenAI", provider: "openai", promise: () => fetchOpenAIModels() },
102974
- { name: "GLM", provider: "glm", promise: () => fetchGLMDirectModels() },
102975
- { name: "GLM Coding", provider: "glm-coding", promise: () => fetchGLMCodingModels2() },
102976
- { name: "OllamaCloud", provider: "ollamacloud", promise: () => fetchOllamaCloudModels() },
102977
- { name: "Zen", provider: "opencode-zen", promise: () => fetchZenFreeModels() },
102978
- { name: "Zen Go", provider: "opencode-zen-go", promise: () => fetchZenGoModels() },
102979
102403
  {
102980
102404
  name: "MiniMax",
102981
102405
  provider: "minimax",
@@ -103020,22 +102444,15 @@ async function getAllModelsForSearch(forceUpdate = false) {
103020
102444
  }
103021
102445
  const r = (name) => fetchResults[name] || [];
103022
102446
  const allModels = [
103023
- ...r("Zen"),
103024
- ...r("Zen Go"),
103025
- ...r("OllamaCloud"),
103026
102447
  ...r("xAI"),
103027
102448
  ...r("Gemini"),
103028
- ...r("OpenAI"),
103029
102449
  ...r("OpenAI Codex"),
103030
- ...r("GLM"),
103031
- ...r("GLM Coding"),
103032
102450
  ...r("MiniMax"),
103033
102451
  ...r("MiniMax Coding"),
103034
102452
  ...r("Kimi"),
103035
102453
  ...r("Kimi Coding"),
103036
102454
  ...r("Z.AI"),
103037
- ...r("LiteLLM"),
103038
- ...r("OpenRouter")
102455
+ ...r("LiteLLM")
103039
102456
  ];
103040
102457
  return allModels;
103041
102458
  }
@@ -103131,23 +102548,17 @@ async function selectModel(options = {}) {
103131
102548
  } else {
103132
102549
  const [allModels, recommendedModels] = await Promise.all([
103133
102550
  getAllModelsForSearch(forceUpdate),
103134
- Promise.resolve(recommended ? loadRecommendedModels2() : [])
102551
+ Promise.resolve(recommended ? loadRecommendedModels() : [])
103135
102552
  ]);
103136
102553
  const seenIds = new Set;
103137
102554
  models = [];
103138
- for (const m2 of allModels.filter((m3) => m3.source === "Zen")) {
103139
- if (!seenIds.has(m2.id)) {
103140
- seenIds.add(m2.id);
103141
- models.push(m2);
103142
- }
103143
- }
103144
102555
  for (const m2 of recommendedModels) {
103145
102556
  if (!seenIds.has(m2.id)) {
103146
102557
  seenIds.add(m2.id);
103147
102558
  models.push(m2);
103148
102559
  }
103149
102560
  }
103150
- for (const m2 of allModels.filter((m3) => m3.source && m3.source !== "Zen" && m3.source !== "OpenRouter")) {
102561
+ for (const m2 of allModels.filter((m3) => m3.source && m3.source !== "OpenRouter")) {
103151
102562
  if (!seenIds.has(m2.id)) {
103152
102563
  seenIds.add(m2.id);
103153
102564
  models.push(m2);
@@ -103517,7 +102928,7 @@ async function selectModelsForProfile() {
103517
102928
  Loading available models...`);
103518
102929
  const [fetchedModels, recommendedModels] = await Promise.all([
103519
102930
  getAllModelsForSearch(),
103520
- Promise.resolve(loadRecommendedModels2())
102931
+ Promise.resolve(loadRecommendedModels())
103521
102932
  ]);
103522
102933
  const tiers = [
103523
102934
  { key: "opus", name: "Opus", description: "Most capable, used for complex reasoning" },
@@ -103611,16 +103022,11 @@ async function selectProfile(profiles) {
103611
103022
  async function confirmAction(message) {
103612
103023
  return dist_default4({ message, default: false });
103613
103024
  }
103614
- var __filename5, __dirname5, CLAUDISH_CACHE_DIR3, ALL_MODELS_JSON_PATH2, RECOMMENDED_MODELS_JSON_PATH, CACHE_MAX_AGE_DAYS3 = 2, FREE_MODELS_CACHE_MAX_AGE_HOURS = 3, PROVIDER_FILTER_ALIASES, ALL_PROVIDER_CHOICES, PROVIDER_MODEL_PREFIX, PROVIDER_SOURCE_FILTER;
103025
+ var PROVIDER_FILTER_ALIASES, ALL_PROVIDER_CHOICES, PROVIDER_MODEL_PREFIX, PROVIDER_SOURCE_FILTER;
103615
103026
  var init_model_selector = __esm(() => {
103616
103027
  init_dist17();
103617
103028
  init_model_loader();
103618
103029
  init_provider_definitions();
103619
- __filename5 = fileURLToPath5(import.meta.url);
103620
- __dirname5 = dirname5(__filename5);
103621
- CLAUDISH_CACHE_DIR3 = join27(homedir25(), ".claudish");
103622
- ALL_MODELS_JSON_PATH2 = join27(CLAUDISH_CACHE_DIR3, "all-models.json");
103623
- RECOMMENDED_MODELS_JSON_PATH = join27(__dirname5, "../recommended-models.json");
103624
103030
  PROVIDER_FILTER_ALIASES = {
103625
103031
  zen: "Zen",
103626
103032
  openrouter: "OpenRouter",
@@ -107277,9 +106683,9 @@ __export(exports_claude_runner, {
107277
106683
  checkClaudeInstalled: () => checkClaudeInstalled
107278
106684
  });
107279
106685
  import { spawn as spawn4 } from "child_process";
107280
- import { writeFileSync as writeFileSync16, unlinkSync as unlinkSync9, mkdirSync as mkdirSync14, existsSync as existsSync28, readFileSync as readFileSync23 } from "fs";
107281
- import { tmpdir as tmpdir2, homedir as homedir26 } from "os";
107282
- import { join as join28 } from "path";
106686
+ import { writeFileSync as writeFileSync13, unlinkSync as unlinkSync9, mkdirSync as mkdirSync12, existsSync as existsSync27, readFileSync as readFileSync22 } from "fs";
106687
+ import { tmpdir as tmpdir2, homedir as homedir25 } from "os";
106688
+ import { join as join27 } from "path";
107283
106689
  function hasNativeAnthropicMapping(config3) {
107284
106690
  const models = [
107285
106691
  config3.model,
@@ -107295,9 +106701,9 @@ function isWindows2() {
107295
106701
  }
107296
106702
  function createStatusLineScript(tokenFilePath) {
107297
106703
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
107298
- const claudishDir = join28(homeDir, ".claudish");
106704
+ const claudishDir = join27(homeDir, ".claudish");
107299
106705
  const timestamp = Date.now();
107300
- const scriptPath = join28(claudishDir, `status-${timestamp}.js`);
106706
+ const scriptPath = join27(claudishDir, `status-${timestamp}.js`);
107301
106707
  const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
107302
106708
  const script = `
107303
106709
  const fs = require('fs');
@@ -107389,18 +106795,18 @@ process.stdin.on('end', () => {
107389
106795
  }
107390
106796
  });
107391
106797
  `;
107392
- writeFileSync16(scriptPath, script, "utf-8");
106798
+ writeFileSync13(scriptPath, script, "utf-8");
107393
106799
  return scriptPath;
107394
106800
  }
107395
106801
  function createTempSettingsFile(modelDisplay, port) {
107396
106802
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
107397
- const claudishDir = join28(homeDir, ".claudish");
106803
+ const claudishDir = join27(homeDir, ".claudish");
107398
106804
  try {
107399
- mkdirSync14(claudishDir, { recursive: true });
106805
+ mkdirSync12(claudishDir, { recursive: true });
107400
106806
  } catch {}
107401
106807
  const timestamp = Date.now();
107402
- const tempPath = join28(claudishDir, `settings-${timestamp}.json`);
107403
- const tokenFilePath = join28(claudishDir, `tokens-${port}.json`);
106808
+ const tempPath = join27(claudishDir, `settings-${timestamp}.json`);
106809
+ const tokenFilePath = join27(claudishDir, `tokens-${port}.json`);
107404
106810
  let statusCommand;
107405
106811
  if (isWindows2()) {
107406
106812
  const scriptPath = createStatusLineScript(tokenFilePath);
@@ -107422,7 +106828,7 @@ function createTempSettingsFile(modelDisplay, port) {
107422
106828
  padding: 0
107423
106829
  };
107424
106830
  const settings = { statusLine };
107425
- writeFileSync16(tempPath, JSON.stringify(settings, null, 2), "utf-8");
106831
+ writeFileSync13(tempPath, JSON.stringify(settings, null, 2), "utf-8");
107426
106832
  return { path: tempPath, statusLine };
107427
106833
  }
107428
106834
  function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine) {
@@ -107436,11 +106842,11 @@ function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine) {
107436
106842
  if (userSettingsValue.trimStart().startsWith("{")) {
107437
106843
  userSettings = JSON.parse(userSettingsValue);
107438
106844
  } else {
107439
- const rawUserSettings = readFileSync23(userSettingsValue, "utf-8");
106845
+ const rawUserSettings = readFileSync22(userSettingsValue, "utf-8");
107440
106846
  userSettings = JSON.parse(rawUserSettings);
107441
106847
  }
107442
106848
  userSettings.statusLine = statusLine;
107443
- writeFileSync16(tempSettingsPath, JSON.stringify(userSettings, null, 2), "utf-8");
106849
+ writeFileSync13(tempSettingsPath, JSON.stringify(userSettings, null, 2), "utf-8");
107444
106850
  } catch {
107445
106851
  if (!config3.quiet) {
107446
106852
  console.warn(`[claudish] Warning: could not merge user settings: ${userSettingsValue}`);
@@ -107528,13 +106934,14 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
107528
106934
  console.error("Install it from: https://claude.com/claude-code");
107529
106935
  console.error(`
107530
106936
  Or set CLAUDE_PATH to your custom installation:`);
107531
- const home = homedir26();
107532
- const localPath = isWindows2() ? join28(home, ".claude", "local", "claude.exe") : join28(home, ".claude", "local", "claude");
106937
+ const home = homedir25();
106938
+ const localPath = isWindows2() ? join27(home, ".claude", "local", "claude.exe") : join27(home, ".claude", "local", "claude");
107533
106939
  console.error(` export CLAUDE_PATH=${localPath}`);
107534
106940
  process.exit(1);
107535
106941
  }
107536
106942
  const needsShell = isWindows2() && claudeBinary.endsWith(".cmd");
107537
106943
  const spawnCommand = needsShell ? `"${claudeBinary}"` : claudeBinary;
106944
+ setClaudeCodeRunning(true);
107538
106945
  const proc = spawn4(spawnCommand, claudeArgs, {
107539
106946
  env: env2,
107540
106947
  stdio: "inherit",
@@ -107543,6 +106950,7 @@ Or set CLAUDE_PATH to your custom installation:`);
107543
106950
  setupSignalHandlers(proc, tempSettingsPath, config3.quiet, onCleanup);
107544
106951
  const exitCode = await new Promise((resolve4) => {
107545
106952
  proc.on("exit", (code) => {
106953
+ setClaudeCodeRunning(false);
107546
106954
  resolve4(code ?? 1);
107547
106955
  });
107548
106956
  });
@@ -107575,23 +106983,23 @@ function setupSignalHandlers(proc, tempSettingsPath, quiet, onCleanup) {
107575
106983
  async function findClaudeBinary() {
107576
106984
  const isWindows3 = process.platform === "win32";
107577
106985
  if (process.env.CLAUDE_PATH) {
107578
- if (existsSync28(process.env.CLAUDE_PATH)) {
106986
+ if (existsSync27(process.env.CLAUDE_PATH)) {
107579
106987
  return process.env.CLAUDE_PATH;
107580
106988
  }
107581
106989
  }
107582
- const home = homedir26();
107583
- const localPath = isWindows3 ? join28(home, ".claude", "local", "claude.exe") : join28(home, ".claude", "local", "claude");
107584
- if (existsSync28(localPath)) {
106990
+ const home = homedir25();
106991
+ const localPath = isWindows3 ? join27(home, ".claude", "local", "claude.exe") : join27(home, ".claude", "local", "claude");
106992
+ if (existsSync27(localPath)) {
107585
106993
  return localPath;
107586
106994
  }
107587
106995
  if (isWindows3) {
107588
106996
  const windowsPaths = [
107589
- join28(home, "AppData", "Roaming", "npm", "claude.cmd"),
107590
- join28(home, ".npm-global", "claude.cmd"),
107591
- join28(home, "node_modules", ".bin", "claude.cmd")
106997
+ join27(home, "AppData", "Roaming", "npm", "claude.cmd"),
106998
+ join27(home, ".npm-global", "claude.cmd"),
106999
+ join27(home, "node_modules", ".bin", "claude.cmd")
107592
107000
  ];
107593
107001
  for (const path3 of windowsPaths) {
107594
- if (existsSync28(path3)) {
107002
+ if (existsSync27(path3)) {
107595
107003
  return path3;
107596
107004
  }
107597
107005
  }
@@ -107599,14 +107007,14 @@ async function findClaudeBinary() {
107599
107007
  const commonPaths = [
107600
107008
  "/usr/local/bin/claude",
107601
107009
  "/opt/homebrew/bin/claude",
107602
- join28(home, ".npm-global/bin/claude"),
107603
- join28(home, ".local/bin/claude"),
107604
- join28(home, "node_modules/.bin/claude"),
107010
+ join27(home, ".npm-global/bin/claude"),
107011
+ join27(home, ".local/bin/claude"),
107012
+ join27(home, "node_modules/.bin/claude"),
107605
107013
  "/data/data/com.termux/files/usr/bin/claude",
107606
- join28(home, "../usr/bin/claude")
107014
+ join27(home, "../usr/bin/claude")
107607
107015
  ];
107608
107016
  for (const path3 of commonPaths) {
107609
- if (existsSync28(path3)) {
107017
+ if (existsSync27(path3)) {
107610
107018
  return path3;
107611
107019
  }
107612
107020
  }
@@ -107646,6 +107054,7 @@ async function checkClaudeInstalled() {
107646
107054
  var init_claude_runner = __esm(() => {
107647
107055
  init_config();
107648
107056
  init_model_parser();
107057
+ init_telemetry();
107649
107058
  });
107650
107059
 
107651
107060
  // src/diag-output.ts
@@ -107655,18 +107064,18 @@ __export(exports_diag_output, {
107655
107064
  NullDiagOutput: () => NullDiagOutput,
107656
107065
  LogFileDiagOutput: () => LogFileDiagOutput
107657
107066
  });
107658
- import { createWriteStream as createWriteStream3, mkdirSync as mkdirSync15, writeFileSync as writeFileSync17, unlinkSync as unlinkSync10 } from "fs";
107659
- import { homedir as homedir27 } from "os";
107660
- import { join as join29 } from "path";
107067
+ import { createWriteStream as createWriteStream3, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, unlinkSync as unlinkSync10 } from "fs";
107068
+ import { homedir as homedir26 } from "os";
107069
+ import { join as join28 } from "path";
107661
107070
  function getClaudishDir() {
107662
- const dir = join29(homedir27(), ".claudish");
107071
+ const dir = join28(homedir26(), ".claudish");
107663
107072
  try {
107664
- mkdirSync15(dir, { recursive: true });
107073
+ mkdirSync13(dir, { recursive: true });
107665
107074
  } catch {}
107666
107075
  return dir;
107667
107076
  }
107668
107077
  function getDiagLogPath() {
107669
- return join29(getClaudishDir(), `diag-${process.pid}.log`);
107078
+ return join28(getClaudishDir(), `diag-${process.pid}.log`);
107670
107079
  }
107671
107080
 
107672
107081
  class LogFileDiagOutput {
@@ -107675,7 +107084,7 @@ class LogFileDiagOutput {
107675
107084
  constructor() {
107676
107085
  this.logPath = getDiagLogPath();
107677
107086
  try {
107678
- writeFileSync17(this.logPath, `--- claudish diag session ${new Date().toISOString()} ---
107087
+ writeFileSync14(this.logPath, `--- claudish diag session ${new Date().toISOString()} ---
107679
107088
  `);
107680
107089
  } catch {}
107681
107090
  this.stream = createWriteStream3(this.logPath, { flags: "a" });
@@ -107725,12 +107134,12 @@ __export(exports_team_grid, {
107725
107134
  });
107726
107135
  import { spawn as spawn5 } from "child_process";
107727
107136
  import {
107728
- existsSync as existsSync29,
107729
- readFileSync as readFileSync24,
107730
- writeFileSync as writeFileSync18
107137
+ existsSync as existsSync28,
107138
+ readFileSync as readFileSync23,
107139
+ writeFileSync as writeFileSync15
107731
107140
  } from "fs";
107732
- import { dirname as dirname6, join as join30 } from "path";
107733
- import { fileURLToPath as fileURLToPath6 } from "url";
107141
+ import { dirname as dirname6, join as join29 } from "path";
107142
+ import { fileURLToPath as fileURLToPath5 } from "url";
107734
107143
  import { execSync as execSync2 } from "child_process";
107735
107144
  import { connect as netConnect } from "net";
107736
107145
  import { setTimeout as wait } from "timers/promises";
@@ -107815,20 +107224,20 @@ function buildPaneHeader(model, prompt, bg2) {
107815
107224
  return lines.join(" ");
107816
107225
  }
107817
107226
  function findMagmuxBinary() {
107818
- const thisFile = fileURLToPath6(import.meta.url);
107227
+ const thisFile = fileURLToPath5(import.meta.url);
107819
107228
  const thisDir = dirname6(thisFile);
107820
- const pkgRoot = join30(thisDir, "..");
107229
+ const pkgRoot = join29(thisDir, "..");
107821
107230
  const platform3 = process.platform;
107822
107231
  const arch = process.arch;
107823
- const bundledMagmux = join30(pkgRoot, "native", `magmux-${platform3}-${arch}`);
107824
- if (existsSync29(bundledMagmux))
107232
+ const bundledMagmux = join29(pkgRoot, "native", `magmux-${platform3}-${arch}`);
107233
+ if (existsSync28(bundledMagmux))
107825
107234
  return bundledMagmux;
107826
107235
  try {
107827
107236
  const pkgName = `@claudish/magmux-${platform3}-${arch}`;
107828
107237
  let searchDir = pkgRoot;
107829
107238
  for (let i = 0;i < 5; i++) {
107830
- const candidate = join30(searchDir, "node_modules", pkgName, "bin", "magmux");
107831
- if (existsSync29(candidate))
107239
+ const candidate = join29(searchDir, "node_modules", pkgName, "bin", "magmux");
107240
+ if (existsSync28(candidate))
107832
107241
  return candidate;
107833
107242
  const parent = dirname6(searchDir);
107834
107243
  if (parent === searchDir)
@@ -107847,7 +107256,7 @@ function findMagmuxBinary() {
107847
107256
  async function subscribeToMagmux(sockPath, onEvent) {
107848
107257
  let client = null;
107849
107258
  for (let attempt = 0;attempt < 40; attempt++) {
107850
- if (existsSync29(sockPath)) {
107259
+ if (existsSync28(sockPath)) {
107851
107260
  try {
107852
107261
  client = await new Promise((resolve4, reject) => {
107853
107262
  const s = netConnect(sockPath);
@@ -107934,9 +107343,9 @@ async function runWithGrid(sessionPath, models, input, opts) {
107934
107343
  const keep = opts?.keep ?? false;
107935
107344
  const manifest = setupSession(sessionPath, models, input);
107936
107345
  const startedAt = new Date().toISOString();
107937
- const gridfilePath = join30(sessionPath, "gridfile.txt");
107938
- const prompt = readFileSync24(join30(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
107939
- const rawPrompt = readFileSync24(join30(sessionPath, "input.md"), "utf-8");
107346
+ const gridfilePath = join29(sessionPath, "gridfile.txt");
107347
+ const prompt = readFileSync23(join29(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
107348
+ const rawPrompt = readFileSync23(join29(sessionPath, "input.md"), "utf-8");
107940
107349
  const usedBannerColors = new Set;
107941
107350
  const gridLines = Object.entries(manifest.models).map(([anonId]) => {
107942
107351
  const model = manifest.models[anonId].model;
@@ -107947,7 +107356,7 @@ async function runWithGrid(sessionPath, models, input, opts) {
107947
107356
  const header = buildPaneHeader(model, rawPrompt, bg2);
107948
107357
  return `${header} claudish --model ${model} -y --quiet '${prompt}'`;
107949
107358
  });
107950
- writeFileSync18(gridfilePath, gridLines.join(`
107359
+ writeFileSync15(gridfilePath, gridLines.join(`
107951
107360
  `) + `
107952
107361
  `, "utf-8");
107953
107362
  const magmuxPath = findMagmuxBinary();
@@ -107967,8 +107376,8 @@ async function runWithGrid(sessionPath, models, input, opts) {
107967
107376
  });
107968
107377
  const [{ results }] = await Promise.all([subscription, procExit]);
107969
107378
  const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
107970
- const statusPath = join30(sessionPath, "status.json");
107971
- writeFileSync18(statusPath, JSON.stringify(status, null, 2), "utf-8");
107379
+ const statusPath = join29(sessionPath, "status.json");
107380
+ writeFileSync15(statusPath, JSON.stringify(status, null, 2), "utf-8");
107972
107381
  return status;
107973
107382
  }
107974
107383
  var BANNER_BG_COLORS;
@@ -107990,16 +107399,16 @@ var init_team_grid = __esm(() => {
107990
107399
 
107991
107400
  // src/index.ts
107992
107401
  var import_dotenv3 = __toESM(require_main(), 1);
107993
- import { existsSync as existsSync30, readFileSync as readFileSync25 } from "fs";
107994
- import { homedir as homedir28 } from "os";
107995
- import { join as join31 } from "path";
107402
+ import { existsSync as existsSync29, readFileSync as readFileSync24 } from "fs";
107403
+ import { homedir as homedir27 } from "os";
107404
+ import { join as join30 } from "path";
107996
107405
  import_dotenv3.config({ quiet: true });
107997
107406
  function loadStoredApiKeys() {
107998
107407
  try {
107999
- const configPath = join31(homedir28(), ".claudish", "config.json");
108000
- if (!existsSync30(configPath))
107408
+ const configPath = join30(homedir27(), ".claudish", "config.json");
107409
+ if (!existsSync29(configPath))
108001
107410
  return;
108002
- const raw2 = readFileSync25(configPath, "utf-8");
107411
+ const raw2 = readFileSync24(configPath, "utf-8");
108003
107412
  const cfg = JSON.parse(raw2);
108004
107413
  if (cfg.apiKeys) {
108005
107414
  for (const [envVar, value] of Object.entries(cfg.apiKeys)) {
@@ -108113,14 +107522,14 @@ async function runCli() {
108113
107522
  if (cliConfig.team && cliConfig.team.length > 0) {
108114
107523
  let prompt = cliConfig.claudeArgs.join(" ");
108115
107524
  if (cliConfig.inputFile) {
108116
- prompt = readFileSync25(cliConfig.inputFile, "utf-8");
107525
+ prompt = readFileSync24(cliConfig.inputFile, "utf-8");
108117
107526
  }
108118
107527
  if (!prompt.trim()) {
108119
107528
  console.error("Error: --team requires a prompt (positional args or -f <file>)");
108120
107529
  process.exit(1);
108121
107530
  }
108122
107531
  const mode = cliConfig.teamMode ?? "default";
108123
- const sessionPath = join31(process.cwd(), `.claudish-team-${Date.now()}`);
107532
+ const sessionPath = join30(process.cwd(), `.claudish-team-${Date.now()}`);
108124
107533
  if (mode === "json") {
108125
107534
  const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
108126
107535
  setupSession2(sessionPath, cliConfig.team, prompt);
@@ -108130,9 +107539,9 @@ async function runCli() {
108130
107539
  });
108131
107540
  const result = { ...status2, responses: {} };
108132
107541
  for (const anonId of Object.keys(status2.models)) {
108133
- const responsePath = join31(sessionPath, `response-${anonId}.md`);
107542
+ const responsePath = join30(sessionPath, `response-${anonId}.md`);
108134
107543
  try {
108135
- const raw2 = readFileSync25(responsePath, "utf-8").trim();
107544
+ const raw2 = readFileSync24(responsePath, "utf-8").trim();
108136
107545
  try {
108137
107546
  result.responses[anonId] = JSON.parse(raw2);
108138
107547
  } catch {
@@ -108264,6 +107673,14 @@ Team Status`);
108264
107673
  }
108265
107674
  }
108266
107675
  }
107676
+ if (cliConfig.interactive && !cliConfig.monitor && process.stdin.isTTY) {
107677
+ if (typeof process.stdin.setRawMode === "function") {
107678
+ process.stdin.setRawMode(false);
107679
+ }
107680
+ process.stdin.pause();
107681
+ process.stdin.removeAllListeners("data");
107682
+ process.stdin.removeAllListeners("keypress");
107683
+ }
108267
107684
  if (!cliConfig.quiet) {
108268
107685
  const modelsToCheck = [
108269
107686
  cliConfig.model,