claudish 4.4.0 → 4.4.2

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.
Files changed (2) hide show
  1. package/dist/index.js +667 -393
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28163,12 +28163,8 @@ var init_profile_config = __esm(() => {
28163
28163
  profiles: {
28164
28164
  default: {
28165
28165
  name: "default",
28166
- description: "Default profile - balanced performance and cost",
28167
- models: {
28168
- opus: "xai@grok-4-0709",
28169
- sonnet: "xai@grok-4-1-fast-reasoning",
28170
- haiku: "xai@grok-code-fast-1"
28171
- },
28166
+ description: "Default profile - shows model selector when no model specified",
28167
+ models: {},
28172
28168
  createdAt: new Date().toISOString(),
28173
28169
  updatedAt: new Date().toISOString()
28174
28170
  }
@@ -30495,7 +30491,7 @@ function toModelInfo(model) {
30495
30491
  async function fetchZenFreeModels() {
30496
30492
  try {
30497
30493
  const response = await fetch("https://models.dev/api.json", {
30498
- signal: AbortSignal.timeout(1e4)
30494
+ signal: AbortSignal.timeout(5000)
30499
30495
  });
30500
30496
  if (!response.ok) {
30501
30497
  return [];
@@ -30508,23 +30504,189 @@ async function fetchZenFreeModels() {
30508
30504
  const isFree = m.cost?.input === 0 && m.cost?.output === 0;
30509
30505
  const supportsTools = m.tool_call === true;
30510
30506
  return isFree && supportsTools;
30511
- }).map(([id, m]) => ({
30512
- id: `zen@${id}`,
30513
- name: m.name || id,
30514
- description: `OpenCode Zen free model`,
30515
- provider: "Zen",
30507
+ }).map(([id, m]) => {
30508
+ const inputModalities = m.modalities?.input || [];
30509
+ const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
30510
+ return {
30511
+ id: `zen@${id}`,
30512
+ name: m.name || id,
30513
+ description: `OpenCode Zen free model`,
30514
+ provider: "Zen",
30515
+ pricing: {
30516
+ input: "FREE",
30517
+ output: "FREE",
30518
+ average: "FREE"
30519
+ },
30520
+ context: m.limit?.context ? `${Math.round(m.limit.context / 1000)}K` : "128K",
30521
+ contextLength: m.limit?.context || 128000,
30522
+ supportsTools: true,
30523
+ supportsReasoning: m.reasoning || false,
30524
+ supportsVision,
30525
+ isFree: true,
30526
+ source: "Zen"
30527
+ };
30528
+ });
30529
+ } catch {
30530
+ return [];
30531
+ }
30532
+ }
30533
+ function getXAIContextWindow(modelId) {
30534
+ const id = modelId.toLowerCase();
30535
+ if (id.includes("grok-4.1-fast") || id.includes("grok-4-1-fast")) {
30536
+ return { context: "2M", contextLength: 2000000 };
30537
+ }
30538
+ if (id.includes("grok-4-fast")) {
30539
+ return { context: "2M", contextLength: 2000000 };
30540
+ }
30541
+ if (id.includes("grok-code-fast")) {
30542
+ return { context: "256K", contextLength: 256000 };
30543
+ }
30544
+ if (id.includes("grok-4")) {
30545
+ return { context: "256K", contextLength: 256000 };
30546
+ }
30547
+ if (id.includes("grok-3")) {
30548
+ return { context: "131K", contextLength: 131072 };
30549
+ }
30550
+ if (id.includes("grok-2")) {
30551
+ return { context: "131K", contextLength: 131072 };
30552
+ }
30553
+ return { context: "131K", contextLength: 131072 };
30554
+ }
30555
+ async function fetchXAIModels() {
30556
+ const apiKey = process.env.XAI_API_KEY;
30557
+ if (!apiKey) {
30558
+ return [];
30559
+ }
30560
+ try {
30561
+ const response = await fetch("https://api.x.ai/v1/language-models", {
30562
+ headers: {
30563
+ Authorization: `Bearer ${apiKey}`,
30564
+ "Content-Type": "application/json"
30565
+ },
30566
+ signal: AbortSignal.timeout(5000)
30567
+ });
30568
+ if (!response.ok) {
30569
+ return [];
30570
+ }
30571
+ const data = await response.json();
30572
+ if (!data.models || !Array.isArray(data.models)) {
30573
+ return [];
30574
+ }
30575
+ return data.models.filter((model) => !model.id.includes("image") && !model.id.includes("imagine")).map((model) => {
30576
+ const inputPricePerM = (model.prompt_text_token_price || 0) / 1000;
30577
+ const outputPricePerM = (model.completion_text_token_price || 0) / 1000;
30578
+ const avgPrice = (inputPricePerM + outputPricePerM) / 2;
30579
+ const { context, contextLength } = getXAIContextWindow(model.id);
30580
+ const supportsVision = (model.input_modalities || []).includes("image");
30581
+ const supportsReasoning = model.id.includes("reasoning");
30582
+ return {
30583
+ id: `xai@${model.id}`,
30584
+ name: model.id,
30585
+ description: `xAI ${supportsReasoning ? "reasoning " : ""}model`,
30586
+ provider: "xAI",
30587
+ pricing: {
30588
+ input: `$${inputPricePerM.toFixed(2)}`,
30589
+ output: `$${outputPricePerM.toFixed(2)}`,
30590
+ average: `$${avgPrice.toFixed(2)}/1M`
30591
+ },
30592
+ context,
30593
+ contextLength,
30594
+ supportsTools: true,
30595
+ supportsReasoning,
30596
+ supportsVision,
30597
+ isFree: false,
30598
+ source: "xAI"
30599
+ };
30600
+ });
30601
+ } catch {
30602
+ return [];
30603
+ }
30604
+ }
30605
+ async function fetchGeminiModels() {
30606
+ const apiKey = process.env.GEMINI_API_KEY;
30607
+ if (!apiKey) {
30608
+ return [];
30609
+ }
30610
+ try {
30611
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`, {
30612
+ signal: AbortSignal.timeout(5000)
30613
+ });
30614
+ if (!response.ok) {
30615
+ return [];
30616
+ }
30617
+ const data = await response.json();
30618
+ if (!data.models || !Array.isArray(data.models)) {
30619
+ return [];
30620
+ }
30621
+ return data.models.filter((model) => {
30622
+ const methods = model.supportedGenerationMethods || [];
30623
+ return methods.includes("generateContent");
30624
+ }).map((model) => {
30625
+ const modelName = model.name.replace("models/", "");
30626
+ return {
30627
+ id: `google@${modelName}`,
30628
+ name: model.displayName || modelName,
30629
+ description: model.description || `Google Gemini model`,
30630
+ provider: "Gemini",
30631
+ pricing: {
30632
+ input: "$0.50",
30633
+ output: "$2.00",
30634
+ average: "$1.25/1M"
30635
+ },
30636
+ context: "128K",
30637
+ contextLength: 128000,
30638
+ supportsTools: true,
30639
+ supportsReasoning: false,
30640
+ supportsVision: true,
30641
+ isFree: false,
30642
+ source: "Gemini"
30643
+ };
30644
+ });
30645
+ } catch {
30646
+ return [];
30647
+ }
30648
+ }
30649
+ async function fetchOpenAIModels() {
30650
+ const apiKey = process.env.OPENAI_API_KEY;
30651
+ if (!apiKey) {
30652
+ return [];
30653
+ }
30654
+ try {
30655
+ const response = await fetch("https://api.openai.com/v1/models", {
30656
+ headers: {
30657
+ Authorization: `Bearer ${apiKey}`,
30658
+ "Content-Type": "application/json"
30659
+ },
30660
+ signal: AbortSignal.timeout(5000)
30661
+ });
30662
+ if (!response.ok) {
30663
+ return [];
30664
+ }
30665
+ const data = await response.json();
30666
+ if (!data.data || !Array.isArray(data.data)) {
30667
+ return [];
30668
+ }
30669
+ const chatModels = data.data.filter((model) => {
30670
+ const id = model.id.toLowerCase();
30671
+ return id.startsWith("gpt-") || id.startsWith("o1-") || id.startsWith("o3-") || id.startsWith("chatgpt-");
30672
+ });
30673
+ return chatModels.map((model) => ({
30674
+ id: `oai@${model.id}`,
30675
+ name: model.id,
30676
+ description: `OpenAI model`,
30677
+ provider: "OpenAI",
30516
30678
  pricing: {
30517
- input: "FREE",
30518
- output: "FREE",
30519
- average: "FREE"
30679
+ input: "$2.00",
30680
+ output: "$8.00",
30681
+ average: "$5.00/1M"
30520
30682
  },
30521
- context: m.limit?.context ? `${Math.round(m.limit.context / 1000)}K` : "128K",
30522
- contextLength: m.limit?.context || 128000,
30683
+ context: "128K",
30684
+ contextLength: 128000,
30523
30685
  supportsTools: true,
30524
- supportsReasoning: m.reasoning || false,
30525
- supportsVision: false,
30526
- isFree: true,
30527
- source: "Zen"
30686
+ supportsReasoning: model.id.startsWith("o1-") || model.id.startsWith("o3-"),
30687
+ supportsVision: model.id.includes("vision") || model.id.startsWith("gpt-4"),
30688
+ isFree: false,
30689
+ source: "OpenAI"
30528
30690
  }));
30529
30691
  } catch {
30530
30692
  return [];
@@ -30575,8 +30737,16 @@ async function getFreeModels() {
30575
30737
  return combined;
30576
30738
  }
30577
30739
  async function getAllModelsForSearch() {
30578
- const allModels = await fetchAllModels();
30579
- return allModels.map(toModelInfo);
30740
+ const [openRouterModels, xaiModels, geminiModels, openaiModels, zenModels] = await Promise.all([
30741
+ fetchAllModels().then((models) => models.map(toModelInfo)),
30742
+ fetchXAIModels(),
30743
+ fetchGeminiModels(),
30744
+ fetchOpenAIModels(),
30745
+ fetchZenFreeModels()
30746
+ ]);
30747
+ const directApiModels = [...xaiModels, ...geminiModels, ...openaiModels];
30748
+ const allModels = [...zenModels, ...directApiModels, ...openRouterModels];
30749
+ return allModels;
30580
30750
  }
30581
30751
  function formatModelChoice(model, showSource = false) {
30582
30752
  const caps = [
@@ -30588,7 +30758,14 @@ function formatModelChoice(model, showSource = false) {
30588
30758
  const priceStr = model.pricing?.average || "N/A";
30589
30759
  const ctxStr = model.context || "N/A";
30590
30760
  if (showSource && model.source) {
30591
- const sourceTag = model.source === "Zen" ? "Zen" : "OR";
30761
+ const sourceTagMap = {
30762
+ Zen: "Zen",
30763
+ OpenRouter: "OR",
30764
+ xAI: "xAI",
30765
+ Gemini: "Gem",
30766
+ OpenAI: "OAI"
30767
+ };
30768
+ const sourceTag = sourceTagMap[model.source] || model.source;
30592
30769
  return `${sourceTag} ${model.id} (${priceStr}, ${ctxStr}${capsStr})`;
30593
30770
  }
30594
30771
  return `${model.id} (${model.provider}, ${priceStr}, ${ctxStr}${capsStr})`;
@@ -30618,24 +30795,46 @@ async function selectModel(options = {}) {
30618
30795
  if (models.length === 0) {
30619
30796
  throw new Error("No free models available");
30620
30797
  }
30621
- } else if (recommended) {
30622
- const recommendedModels = loadRecommendedModels2();
30623
- if (recommendedModels.length > 0) {
30624
- models = recommendedModels;
30625
- } else {
30626
- const allModels = await getAllModelsForSearch();
30627
- models = allModels.slice(0, 20);
30628
- }
30629
30798
  } else {
30630
- models = await getAllModelsForSearch();
30799
+ const [allModels, recommendedModels] = await Promise.all([
30800
+ getAllModelsForSearch(),
30801
+ Promise.resolve(recommended ? loadRecommendedModels2() : [])
30802
+ ]);
30803
+ const seenIds = new Set;
30804
+ models = [];
30805
+ for (const m of allModels.filter((m2) => m2.source === "Zen")) {
30806
+ if (!seenIds.has(m.id)) {
30807
+ seenIds.add(m.id);
30808
+ models.push(m);
30809
+ }
30810
+ }
30811
+ for (const m of recommendedModels) {
30812
+ if (!seenIds.has(m.id)) {
30813
+ seenIds.add(m.id);
30814
+ models.push(m);
30815
+ }
30816
+ }
30817
+ for (const m of allModels.filter((m2) => m2.source && m2.source !== "Zen" && m2.source !== "OpenRouter")) {
30818
+ if (!seenIds.has(m.id)) {
30819
+ seenIds.add(m.id);
30820
+ models.push(m);
30821
+ }
30822
+ }
30823
+ for (const m of allModels.filter((m2) => m2.source === "OpenRouter")) {
30824
+ if (!seenIds.has(m.id)) {
30825
+ seenIds.add(m.id);
30826
+ models.push(m);
30827
+ }
30828
+ }
30631
30829
  }
30632
30830
  const promptMessage = message || (freeOnly ? "Select a FREE model:" : "Select a model (type to search):");
30633
30831
  const selected = await dist_default4({
30634
30832
  message: promptMessage,
30833
+ pageSize: 20,
30635
30834
  source: async (term) => {
30636
30835
  if (!term) {
30637
- return models.slice(0, 15).map((m) => ({
30638
- name: formatModelChoice(m, freeOnly),
30836
+ return models.slice(0, 30).map((m) => ({
30837
+ name: formatModelChoice(m, true),
30639
30838
  value: m.id,
30640
30839
  description: m.description?.slice(0, 80)
30641
30840
  }));
@@ -30643,9 +30842,9 @@ async function selectModel(options = {}) {
30643
30842
  const results = models.map((m) => ({
30644
30843
  model: m,
30645
30844
  score: Math.max(fuzzyMatch(m.id, term), fuzzyMatch(m.name, term), fuzzyMatch(m.provider, term) * 0.5)
30646
- })).filter((r) => r.score > 0.1).sort((a, b) => b.score - a.score).slice(0, 15);
30845
+ })).filter((r) => r.score > 0.1).sort((a, b) => b.score - a.score).slice(0, 30);
30647
30846
  return results.map((r) => ({
30648
- name: formatModelChoice(r.model, freeOnly),
30847
+ name: formatModelChoice(r.model, true),
30649
30848
  value: r.model.id,
30650
30849
  description: r.model.description?.slice(0, 80)
30651
30850
  }));
@@ -31113,332 +31312,19 @@ var init_config = __esm(() => {
31113
31312
  };
31114
31313
  });
31115
31314
 
31116
- // src/claude-runner.ts
31117
- var exports_claude_runner = {};
31118
- __export(exports_claude_runner, {
31119
- runClaudeWithProxy: () => runClaudeWithProxy,
31120
- checkClaudeInstalled: () => checkClaudeInstalled
31121
- });
31122
- import { spawn } from "node:child_process";
31123
- import { writeFileSync as writeFileSync5, unlinkSync as unlinkSync2, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "node:fs";
31124
- import { tmpdir, homedir as homedir5 } from "node:os";
31125
- import { join as join6 } from "node:path";
31126
- function isWindows() {
31127
- return process.platform === "win32";
31128
- }
31129
- function createStatusLineScript(tokenFilePath) {
31130
- const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir();
31131
- const claudishDir = join6(homeDir, ".claudish");
31132
- const timestamp = Date.now();
31133
- const scriptPath = join6(claudishDir, `status-${timestamp}.js`);
31134
- const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
31135
- const script = `
31136
- const fs = require('fs');
31137
- const path = require('path');
31138
-
31139
- const CYAN = "\\x1b[96m";
31140
- const YELLOW = "\\x1b[93m";
31141
- const GREEN = "\\x1b[92m";
31142
- const MAGENTA = "\\x1b[95m";
31143
- const DIM = "\\x1b[2m";
31144
- const RESET = "\\x1b[0m";
31145
- const BOLD = "\\x1b[1m";
31146
-
31147
- // Format token count with k/M suffix
31148
- function formatTokens(n) {
31149
- if (n >= 1000000) return (n / 1000000).toFixed(n >= 10000000 ? 0 : 1).replace(/\\.0$/, '') + 'M';
31150
- if (n >= 1000) return (n / 1000).toFixed(n >= 10000 ? 0 : 1).replace(/\\.0$/, '') + 'k';
31151
- return String(n);
31152
- }
31153
-
31154
- let input = '';
31155
- process.stdin.setEncoding('utf8');
31156
- process.stdin.on('data', chunk => input += chunk);
31157
- process.stdin.on('end', () => {
31158
- try {
31159
- let dir = path.basename(process.cwd());
31160
- if (dir.length > 15) dir = dir.substring(0, 12) + '...';
31161
-
31162
- let ctx = 100, cost = 0, inputTokens = 0, contextWindow = 0;
31163
- const model = process.env.CLAUDISH_ACTIVE_MODEL_NAME || 'unknown';
31164
- const isLocal = process.env.CLAUDISH_IS_LOCAL === 'true';
31165
-
31166
- let isFree = false, isEstimated = false, providerName = '';
31167
- try {
31168
- const tokens = JSON.parse(fs.readFileSync('${escapedTokenPath}', 'utf-8'));
31169
- cost = tokens.total_cost || 0;
31170
- ctx = tokens.context_left_percent || 100;
31171
- inputTokens = tokens.input_tokens || 0;
31172
- contextWindow = tokens.context_window || 0;
31173
- isFree = tokens.is_free || false;
31174
- isEstimated = tokens.is_estimated || false;
31175
- providerName = tokens.provider_name || '';
31176
- } catch (e) {
31177
- try {
31178
- const json = JSON.parse(input);
31179
- cost = json.total_cost_usd || 0;
31180
- } catch {}
31181
- }
31182
-
31183
- let costDisplay;
31184
- if (isLocal) {
31185
- costDisplay = 'LOCAL';
31186
- } else if (isFree) {
31187
- costDisplay = 'FREE';
31188
- } else if (isEstimated) {
31189
- costDisplay = '~$' + cost.toFixed(3);
31190
- } else {
31191
- costDisplay = '$' + cost.toFixed(3);
31192
- }
31193
- const modelDisplay = providerName ? providerName + ' ' + model : model;
31194
- // Format context display: "96% (37k/1M)" or just "96%" if no token data
31195
- let ctxDisplay = ctx + '%';
31196
- if (inputTokens > 0 && contextWindow > 0) {
31197
- ctxDisplay = ctx + '% (' + formatTokens(inputTokens) + '/' + formatTokens(contextWindow) + ')';
31198
- }
31199
- console.log(\`\${CYAN}\${BOLD}\${dir}\${RESET} \${DIM}•\${RESET} \${YELLOW}\${modelDisplay}\${RESET} \${DIM}•\${RESET} \${GREEN}\${costDisplay}\${RESET} \${DIM}•\${RESET} \${MAGENTA}\${ctxDisplay}\${RESET}\`);
31200
- } catch (e) {
31201
- console.log('claudish');
31202
- }
31203
- });
31204
- `;
31205
- writeFileSync5(scriptPath, script, "utf-8");
31206
- return scriptPath;
31207
- }
31208
- function createTempSettingsFile(modelDisplay, port) {
31209
- const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir();
31210
- const claudishDir = join6(homeDir, ".claudish");
31211
- try {
31212
- mkdirSync5(claudishDir, { recursive: true });
31213
- } catch {}
31214
- const timestamp = Date.now();
31215
- const tempPath = join6(claudishDir, `settings-${timestamp}.json`);
31216
- const tokenFilePath = join6(claudishDir, `tokens-${port}.json`);
31217
- let statusCommand;
31218
- if (isWindows()) {
31219
- const scriptPath = createStatusLineScript(tokenFilePath);
31220
- statusCommand = `node "${scriptPath}"`;
31221
- } else {
31222
- const CYAN2 = "\\033[96m";
31223
- const YELLOW2 = "\\033[93m";
31224
- const GREEN2 = "\\033[92m";
31225
- const MAGENTA = "\\033[95m";
31226
- const DIM2 = "\\033[2m";
31227
- const RESET2 = "\\033[0m";
31228
- const BOLD2 = "\\033[1m";
31229
- const formatTokensBash = `fmt_tok() { local n=\${1:-0}; if [ "$n" -ge 1000000 ]; then echo "$((n/1000000))M"; elif [ "$n" -ge 1000 ]; then echo "$((n/1000))k"; else echo "$n"; fi; }`;
31230
- statusCommand = `JSON=$(cat) && DIR=$(basename "$(pwd)") && [ \${#DIR} -gt 15 ] && DIR="\${DIR:0:12}..." || true && CTX=100 && COST="0" && IS_FREE="false" && IS_EST="false" && PROVIDER="" && IN_TOK=0 && CTX_WIN=0 && ${formatTokensBash} && if [ -f "${tokenFilePath}" ]; then TOKENS=$(cat "${tokenFilePath}" 2>/dev/null | tr -d ' \\n') && REAL_CTX=$(echo "$TOKENS" | grep -o '"context_left_percent":[0-9]*' | grep -o '[0-9]*') && if [ ! -z "$REAL_CTX" ]; then CTX="$REAL_CTX"; fi && REAL_COST=$(echo "$TOKENS" | grep -o '"total_cost":[0-9.]*' | cut -d: -f2) && if [ ! -z "$REAL_COST" ]; then COST="$REAL_COST"; fi && IN_TOK=$(echo "$TOKENS" | grep -o '"input_tokens":[0-9]*' | grep -o '[0-9]*') && CTX_WIN=$(echo "$TOKENS" | grep -o '"context_window":[0-9]*' | grep -o '[0-9]*') && IS_FREE=$(echo "$TOKENS" | grep -o '"is_free":[a-z]*' | cut -d: -f2) && IS_EST=$(echo "$TOKENS" | grep -o '"is_estimated":[a-z]*' | cut -d: -f2) && PROVIDER=$(echo "$TOKENS" | grep -o '"provider_name":"[^"]*"' | cut -d'"' -f4); fi && if [ "$CLAUDISH_IS_LOCAL" = "true" ]; then COST_DISPLAY="LOCAL"; elif [ "$IS_FREE" = "true" ]; then COST_DISPLAY="FREE"; elif [ "$IS_EST" = "true" ]; then COST_DISPLAY=$(printf "~\\$%.3f" "$COST"); else COST_DISPLAY=$(printf "\\$%.3f" "$COST"); fi && MODEL_DISPLAY="$CLAUDISH_ACTIVE_MODEL_NAME" && if [ ! -z "$PROVIDER" ]; then MODEL_DISPLAY="$PROVIDER $MODEL_DISPLAY"; fi && if [ "$IN_TOK" -gt 0 ] 2>/dev/null && [ "$CTX_WIN" -gt 0 ] 2>/dev/null; then CTX_DISPLAY="$CTX% ($(fmt_tok $IN_TOK)/$(fmt_tok $CTX_WIN))"; else CTX_DISPLAY="$CTX%"; fi && printf "${CYAN2}${BOLD2}%s${RESET2} ${DIM2}•${RESET2} ${YELLOW2}%s${RESET2} ${DIM2}•${RESET2} ${GREEN2}%s${RESET2} ${DIM2}•${RESET2} ${MAGENTA}%s${RESET2}\\n" "$DIR" "$MODEL_DISPLAY" "$COST_DISPLAY" "$CTX_DISPLAY"`;
31231
- }
31232
- const settings = {
31233
- statusLine: {
31234
- type: "command",
31235
- command: statusCommand,
31236
- padding: 0
31237
- }
31238
- };
31239
- writeFileSync5(tempPath, JSON.stringify(settings, null, 2), "utf-8");
31240
- return tempPath;
31241
- }
31242
- async function runClaudeWithProxy(config3, proxyUrl) {
31243
- const modelId = config3.model || "unknown";
31244
- const portMatch = proxyUrl.match(/:(\d+)/);
31245
- const port = portMatch ? portMatch[1] : "unknown";
31246
- const tempSettingsPath = createTempSettingsFile(modelId, port);
31247
- const claudeArgs = [];
31248
- claudeArgs.push("--settings", tempSettingsPath);
31249
- if (config3.interactive) {
31250
- if (config3.autoApprove) {
31251
- claudeArgs.push("--dangerously-skip-permissions");
31252
- }
31253
- if (config3.dangerous) {
31254
- claudeArgs.push("--dangerouslyDisableSandbox");
31255
- }
31256
- } else {
31257
- claudeArgs.push("-p");
31258
- if (config3.autoApprove) {
31259
- claudeArgs.push("--dangerously-skip-permissions");
31260
- }
31261
- if (config3.dangerous) {
31262
- claudeArgs.push("--dangerouslyDisableSandbox");
31263
- }
31264
- if (config3.jsonOutput) {
31265
- claudeArgs.push("--output-format", "json");
31266
- }
31267
- if (config3.agent && config3.claudeArgs.length > 0) {
31268
- const modifiedArgs = [...config3.claudeArgs];
31269
- const agentId = config3.agent.startsWith("@agent-") ? config3.agent : `@agent-${config3.agent}`;
31270
- modifiedArgs[0] = `Use the ${agentId} agent to: ${modifiedArgs[0]}`;
31271
- claudeArgs.push(...modifiedArgs);
31272
- } else {
31273
- claudeArgs.push(...config3.claudeArgs);
31274
- }
31275
- }
31276
- const isLocalModel = modelId.startsWith("ollama/") || modelId.startsWith("ollama:") || modelId.startsWith("lmstudio/") || modelId.startsWith("lmstudio:") || modelId.startsWith("vllm/") || modelId.startsWith("vllm:") || modelId.startsWith("mlx/") || modelId.startsWith("mlx:") || modelId.startsWith("http://") || modelId.startsWith("https://");
31277
- const env = {
31278
- ...process.env,
31279
- ANTHROPIC_BASE_URL: proxyUrl,
31280
- [ENV.CLAUDISH_ACTIVE_MODEL_NAME]: modelId,
31281
- CLAUDISH_IS_LOCAL: isLocalModel ? "true" : "false"
31282
- };
31283
- if (config3.monitor) {
31284
- delete env.ANTHROPIC_API_KEY;
31285
- delete env.ANTHROPIC_AUTH_TOKEN;
31286
- if (modelId && modelId !== "unknown") {
31287
- env[ENV.ANTHROPIC_MODEL] = modelId;
31288
- env[ENV.ANTHROPIC_SMALL_FAST_MODEL] = modelId;
31289
- }
31290
- } else {
31291
- env[ENV.ANTHROPIC_MODEL] = modelId;
31292
- env[ENV.ANTHROPIC_SMALL_FAST_MODEL] = modelId;
31293
- env.ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY || "sk-ant-api03-placeholder-not-used-proxy-handles-auth-with-openrouter-key-xxxxxxxxxxxxxxxxxxxxx";
31294
- env.ANTHROPIC_AUTH_TOKEN = process.env.ANTHROPIC_AUTH_TOKEN || "placeholder-token-not-used-proxy-handles-auth";
31295
- }
31296
- const log2 = (message) => {
31297
- if (!config3.quiet) {
31298
- console.log(message);
31299
- }
31300
- };
31301
- if (config3.interactive) {
31302
- log2(`
31303
- [claudish] Model: ${modelId}
31304
- `);
31305
- } else {
31306
- log2(`
31307
- [claudish] Model: ${modelId}`);
31308
- log2(`[claudish] Arguments: ${claudeArgs.join(" ")}
31309
- `);
31310
- }
31311
- const claudeBinary = await findClaudeBinary();
31312
- if (!claudeBinary) {
31313
- console.error("Error: Claude Code CLI not found");
31314
- console.error("Install it from: https://claude.com/claude-code");
31315
- console.error(`
31316
- Or set CLAUDE_PATH to your custom installation:`);
31317
- const home = homedir5();
31318
- const localPath = isWindows() ? join6(home, ".claude", "local", "claude.exe") : join6(home, ".claude", "local", "claude");
31319
- console.error(` export CLAUDE_PATH=${localPath}`);
31320
- process.exit(1);
31321
- }
31322
- const proc = spawn(claudeBinary, claudeArgs, {
31323
- env,
31324
- stdio: "inherit",
31325
- shell: false
31326
- });
31327
- setupSignalHandlers(proc, tempSettingsPath, config3.quiet);
31328
- const exitCode = await new Promise((resolve) => {
31329
- proc.on("exit", (code) => {
31330
- resolve(code ?? 1);
31331
- });
31332
- });
31333
- try {
31334
- unlinkSync2(tempSettingsPath);
31335
- } catch (error46) {}
31336
- return exitCode;
31337
- }
31338
- function setupSignalHandlers(proc, tempSettingsPath, quiet) {
31339
- const signals2 = isWindows() ? ["SIGINT", "SIGTERM"] : ["SIGINT", "SIGTERM", "SIGHUP"];
31340
- for (const signal of signals2) {
31341
- process.on(signal, () => {
31342
- if (!quiet) {
31343
- console.log(`
31344
- [claudish] Received ${signal}, shutting down...`);
31345
- }
31346
- proc.kill();
31347
- try {
31348
- unlinkSync2(tempSettingsPath);
31349
- } catch {}
31350
- process.exit(0);
31351
- });
31352
- }
31353
- }
31354
- async function findClaudeBinary() {
31355
- const isWindows2 = process.platform === "win32";
31356
- if (process.env.CLAUDE_PATH) {
31357
- if (existsSync6(process.env.CLAUDE_PATH)) {
31358
- return process.env.CLAUDE_PATH;
31359
- }
31360
- }
31361
- const home = homedir5();
31362
- const localPath = isWindows2 ? join6(home, ".claude", "local", "claude.exe") : join6(home, ".claude", "local", "claude");
31363
- if (existsSync6(localPath)) {
31364
- return localPath;
31365
- }
31366
- if (isWindows2) {
31367
- const windowsPaths = [
31368
- join6(home, "AppData", "Roaming", "npm", "claude.cmd"),
31369
- join6(home, ".npm-global", "claude.cmd"),
31370
- join6(home, "node_modules", ".bin", "claude.cmd")
31371
- ];
31372
- for (const path of windowsPaths) {
31373
- if (existsSync6(path)) {
31374
- return path;
31375
- }
31376
- }
31377
- } else {
31378
- const commonPaths = [
31379
- "/usr/local/bin/claude",
31380
- "/opt/homebrew/bin/claude",
31381
- join6(home, ".npm-global/bin/claude"),
31382
- join6(home, ".local/bin/claude"),
31383
- join6(home, "node_modules/.bin/claude"),
31384
- "/data/data/com.termux/files/usr/bin/claude",
31385
- join6(home, "../usr/bin/claude")
31386
- ];
31387
- for (const path of commonPaths) {
31388
- if (existsSync6(path)) {
31389
- return path;
31390
- }
31391
- }
31392
- }
31393
- try {
31394
- const shellCommand = isWindows2 ? "where claude" : "command -v claude";
31395
- const proc = spawn(shellCommand, [], {
31396
- stdio: "pipe",
31397
- shell: true
31398
- });
31399
- let output = "";
31400
- proc.stdout?.on("data", (data) => {
31401
- output += data.toString();
31402
- });
31403
- const exitCode = await new Promise((resolve) => {
31404
- proc.on("exit", (code) => {
31405
- resolve(code ?? 1);
31406
- });
31407
- });
31408
- if (exitCode === 0 && output.trim()) {
31409
- const lines = output.trim().split(/\r?\n/);
31410
- if (isWindows2) {
31411
- const cmdPath = lines.find((line) => line.endsWith(".cmd"));
31412
- if (cmdPath) {
31413
- return cmdPath;
31414
- }
31415
- }
31416
- return lines[0];
31417
- }
31418
- } catch {}
31419
- return null;
31420
- }
31421
- async function checkClaudeInstalled() {
31422
- const binary = await findClaudeBinary();
31423
- return binary !== null;
31424
- }
31425
- var init_claude_runner = __esm(() => {
31426
- init_config();
31427
- });
31428
-
31429
31315
  // src/model-loader.ts
31430
- import { readFileSync as readFileSync5, existsSync as existsSync7 } from "node:fs";
31431
- import { join as join7, dirname as dirname3 } from "node:path";
31316
+ import { readFileSync as readFileSync5, existsSync as existsSync6 } from "node:fs";
31317
+ import { join as join6, dirname as dirname3 } from "node:path";
31432
31318
  import { fileURLToPath as fileURLToPath3 } from "node:url";
31433
31319
  function getRecommendedModelsPath() {
31434
- return join7(__dirname4, "../recommended-models.json");
31320
+ return join6(__dirname4, "../recommended-models.json");
31435
31321
  }
31436
31322
  function loadRecommendedModelsJSON() {
31437
31323
  if (_cachedRecommendedModels) {
31438
31324
  return _cachedRecommendedModels;
31439
31325
  }
31440
31326
  const jsonPath = getRecommendedModelsPath();
31441
- if (!existsSync7(jsonPath)) {
31327
+ if (!existsSync6(jsonPath)) {
31442
31328
  throw new Error(`recommended-models.json not found at ${jsonPath}. ` + `Run 'claudish --update-models' to fetch the latest model list.`);
31443
31329
  }
31444
31330
  try {
@@ -32496,10 +32382,10 @@ __export(exports_cli, {
32496
32382
  getMissingKeyResolutions: () => getMissingKeyResolutions,
32497
32383
  getMissingKeyError: () => getMissingKeyError
32498
32384
  });
32499
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync6, copyFileSync } from "node:fs";
32385
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync5, copyFileSync } from "node:fs";
32500
32386
  import { fileURLToPath as fileURLToPath4 } from "node:url";
32501
- import { dirname as dirname4, join as join8 } from "node:path";
32502
- import { homedir as homedir6 } from "node:os";
32387
+ import { dirname as dirname4, join as join7 } from "node:path";
32388
+ import { homedir as homedir5 } from "node:os";
32503
32389
  function getVersion() {
32504
32390
  return VERSION;
32505
32391
  }
@@ -32757,7 +32643,7 @@ async function fetchOllamaModels() {
32757
32643
  }
32758
32644
  async function searchAndPrintModels(query, forceUpdate) {
32759
32645
  let models = [];
32760
- if (!forceUpdate && existsSync8(ALL_MODELS_JSON_PATH2)) {
32646
+ if (!forceUpdate && existsSync7(ALL_MODELS_JSON_PATH2)) {
32761
32647
  try {
32762
32648
  const cacheData = JSON.parse(readFileSync6(ALL_MODELS_JSON_PATH2, "utf-8"));
32763
32649
  const lastUpdated = new Date(cacheData.lastUpdated);
@@ -32776,8 +32662,8 @@ async function searchAndPrintModels(query, forceUpdate) {
32776
32662
  throw new Error(`API returned ${response.status}`);
32777
32663
  const data = await response.json();
32778
32664
  models = data.data;
32779
- mkdirSync6(CLAUDISH_CACHE_DIR3, { recursive: true });
32780
- writeFileSync6(ALL_MODELS_JSON_PATH2, JSON.stringify({
32665
+ mkdirSync5(CLAUDISH_CACHE_DIR3, { recursive: true });
32666
+ writeFileSync5(ALL_MODELS_JSON_PATH2, JSON.stringify({
32781
32667
  lastUpdated: new Date().toISOString(),
32782
32668
  models
32783
32669
  }), "utf-8");
@@ -32865,7 +32751,7 @@ Found ${results.length} matching models:
32865
32751
  async function printAllModels(jsonOutput, forceUpdate) {
32866
32752
  let models = [];
32867
32753
  const [ollamaModels, zenModels] = await Promise.all([fetchOllamaModels(), fetchZenModels()]);
32868
- if (!forceUpdate && existsSync8(ALL_MODELS_JSON_PATH2)) {
32754
+ if (!forceUpdate && existsSync7(ALL_MODELS_JSON_PATH2)) {
32869
32755
  try {
32870
32756
  const cacheData = JSON.parse(readFileSync6(ALL_MODELS_JSON_PATH2, "utf-8"));
32871
32757
  const lastUpdated = new Date(cacheData.lastUpdated);
@@ -32887,8 +32773,8 @@ async function printAllModels(jsonOutput, forceUpdate) {
32887
32773
  throw new Error(`API returned ${response.status}`);
32888
32774
  const data = await response.json();
32889
32775
  models = data.data;
32890
- mkdirSync6(CLAUDISH_CACHE_DIR3, { recursive: true });
32891
- writeFileSync6(ALL_MODELS_JSON_PATH2, JSON.stringify({
32776
+ mkdirSync5(CLAUDISH_CACHE_DIR3, { recursive: true });
32777
+ writeFileSync5(ALL_MODELS_JSON_PATH2, JSON.stringify({
32892
32778
  lastUpdated: new Date().toISOString(),
32893
32779
  models
32894
32780
  }), "utf-8");
@@ -33042,7 +32928,7 @@ async function printAllModels(jsonOutput, forceUpdate) {
33042
32928
  console.log("Top models: claudish --top-models");
33043
32929
  }
33044
32930
  function isCacheStale() {
33045
- if (!existsSync8(MODELS_JSON_PATH)) {
32931
+ if (!existsSync7(MODELS_JSON_PATH)) {
33046
32932
  return true;
33047
32933
  }
33048
32934
  try {
@@ -33141,7 +33027,7 @@ async function updateModelsFromOpenRouter() {
33141
33027
  providers.add(provider);
33142
33028
  }
33143
33029
  let version2 = "1.1.5";
33144
- if (existsSync8(MODELS_JSON_PATH)) {
33030
+ if (existsSync7(MODELS_JSON_PATH)) {
33145
33031
  try {
33146
33032
  const existing = JSON.parse(readFileSync6(MODELS_JSON_PATH, "utf-8"));
33147
33033
  version2 = existing.version || version2;
@@ -33153,7 +33039,7 @@ async function updateModelsFromOpenRouter() {
33153
33039
  source: "https://openrouter.ai/models?categories=programming&fmt=cards&order=top-weekly",
33154
33040
  models: recommendations
33155
33041
  };
33156
- writeFileSync6(MODELS_JSON_PATH, JSON.stringify(updatedData, null, 2), "utf-8");
33042
+ writeFileSync5(MODELS_JSON_PATH, JSON.stringify(updatedData, null, 2), "utf-8");
33157
33043
  console.error(`✅ Updated ${recommendations.length} models (last updated: ${updatedData.lastUpdated})`);
33158
33044
  } catch (error46) {
33159
33045
  console.error(`❌ Failed to update models: ${error46 instanceof Error ? error46.message : String(error46)}`);
@@ -33468,7 +33354,7 @@ MORE INFO:
33468
33354
  }
33469
33355
  function printAIAgentGuide() {
33470
33356
  try {
33471
- const guidePath = join8(__dirname5, "../AI_AGENT_GUIDE.md");
33357
+ const guidePath = join7(__dirname5, "../AI_AGENT_GUIDE.md");
33472
33358
  const guideContent = readFileSync6(guidePath, "utf-8");
33473
33359
  console.log(guideContent);
33474
33360
  } catch (error46) {
@@ -33485,19 +33371,19 @@ async function initializeClaudishSkill() {
33485
33371
  console.log(`\uD83D\uDD27 Initializing Claudish skill in current project...
33486
33372
  `);
33487
33373
  const cwd = process.cwd();
33488
- const claudeDir = join8(cwd, ".claude");
33489
- const skillsDir = join8(claudeDir, "skills");
33490
- const claudishSkillDir = join8(skillsDir, "claudish-usage");
33491
- const skillFile = join8(claudishSkillDir, "SKILL.md");
33492
- if (existsSync8(skillFile)) {
33374
+ const claudeDir = join7(cwd, ".claude");
33375
+ const skillsDir = join7(claudeDir, "skills");
33376
+ const claudishSkillDir = join7(skillsDir, "claudish-usage");
33377
+ const skillFile = join7(claudishSkillDir, "SKILL.md");
33378
+ if (existsSync7(skillFile)) {
33493
33379
  console.log("✅ Claudish skill already installed at:");
33494
33380
  console.log(` ${skillFile}
33495
33381
  `);
33496
33382
  console.log("\uD83D\uDCA1 To reinstall, delete the file and run 'claudish --init' again.");
33497
33383
  return;
33498
33384
  }
33499
- const sourceSkillPath = join8(__dirname5, "../skills/claudish-usage/SKILL.md");
33500
- if (!existsSync8(sourceSkillPath)) {
33385
+ const sourceSkillPath = join7(__dirname5, "../skills/claudish-usage/SKILL.md");
33386
+ if (!existsSync7(sourceSkillPath)) {
33501
33387
  console.error("❌ Error: Claudish skill file not found in installation.");
33502
33388
  console.error(` Expected at: ${sourceSkillPath}`);
33503
33389
  console.error(`
@@ -33506,16 +33392,16 @@ async function initializeClaudishSkill() {
33506
33392
  process.exit(1);
33507
33393
  }
33508
33394
  try {
33509
- if (!existsSync8(claudeDir)) {
33510
- mkdirSync6(claudeDir, { recursive: true });
33395
+ if (!existsSync7(claudeDir)) {
33396
+ mkdirSync5(claudeDir, { recursive: true });
33511
33397
  console.log("\uD83D\uDCC1 Created .claude/ directory");
33512
33398
  }
33513
- if (!existsSync8(skillsDir)) {
33514
- mkdirSync6(skillsDir, { recursive: true });
33399
+ if (!existsSync7(skillsDir)) {
33400
+ mkdirSync5(skillsDir, { recursive: true });
33515
33401
  console.log("\uD83D\uDCC1 Created .claude/skills/ directory");
33516
33402
  }
33517
- if (!existsSync8(claudishSkillDir)) {
33518
- mkdirSync6(claudishSkillDir, { recursive: true });
33403
+ if (!existsSync7(claudishSkillDir)) {
33404
+ mkdirSync5(claudishSkillDir, { recursive: true });
33519
33405
  console.log("\uD83D\uDCC1 Created .claude/skills/claudish-usage/ directory");
33520
33406
  }
33521
33407
  copyFileSync(sourceSkillPath, skillFile);
@@ -33557,7 +33443,7 @@ function printAvailableModels() {
33557
33443
  let lastUpdated = "unknown";
33558
33444
  let models = [];
33559
33445
  try {
33560
- if (existsSync8(MODELS_JSON_PATH)) {
33446
+ if (existsSync7(MODELS_JSON_PATH)) {
33561
33447
  const data = JSON.parse(readFileSync6(MODELS_JSON_PATH, "utf-8"));
33562
33448
  lastUpdated = data.lastUpdated || "unknown";
33563
33449
  models = data.models || [];
@@ -33611,7 +33497,7 @@ Force update: claudish --list-models --force-update
33611
33497
  `);
33612
33498
  }
33613
33499
  function printAvailableModelsJSON() {
33614
- const jsonPath = join8(__dirname5, "../recommended-models.json");
33500
+ const jsonPath = join7(__dirname5, "../recommended-models.json");
33615
33501
  try {
33616
33502
  const jsonContent = readFileSync6(jsonPath, "utf-8");
33617
33503
  const data = JSON.parse(jsonContent);
@@ -33683,12 +33569,325 @@ var init_cli = __esm(() => {
33683
33569
  __filename5 = fileURLToPath4(import.meta.url);
33684
33570
  __dirname5 = dirname4(__filename5);
33685
33571
  try {
33686
- const packageJson = JSON.parse(readFileSync6(join8(__dirname5, "../package.json"), "utf-8"));
33572
+ const packageJson = JSON.parse(readFileSync6(join7(__dirname5, "../package.json"), "utf-8"));
33687
33573
  VERSION = packageJson.version;
33688
33574
  } catch {}
33689
- MODELS_JSON_PATH = join8(__dirname5, "../recommended-models.json");
33690
- CLAUDISH_CACHE_DIR3 = join8(homedir6(), ".claudish");
33691
- ALL_MODELS_JSON_PATH2 = join8(CLAUDISH_CACHE_DIR3, "all-models.json");
33575
+ MODELS_JSON_PATH = join7(__dirname5, "../recommended-models.json");
33576
+ CLAUDISH_CACHE_DIR3 = join7(homedir5(), ".claudish");
33577
+ ALL_MODELS_JSON_PATH2 = join7(CLAUDISH_CACHE_DIR3, "all-models.json");
33578
+ });
33579
+
33580
+ // src/claude-runner.ts
33581
+ var exports_claude_runner = {};
33582
+ __export(exports_claude_runner, {
33583
+ runClaudeWithProxy: () => runClaudeWithProxy,
33584
+ checkClaudeInstalled: () => checkClaudeInstalled
33585
+ });
33586
+ import { spawn } from "node:child_process";
33587
+ import { writeFileSync as writeFileSync6, unlinkSync as unlinkSync2, mkdirSync as mkdirSync6, existsSync as existsSync8 } from "node:fs";
33588
+ import { tmpdir, homedir as homedir6 } from "node:os";
33589
+ import { join as join8 } from "node:path";
33590
+ function isWindows() {
33591
+ return process.platform === "win32";
33592
+ }
33593
+ function createStatusLineScript(tokenFilePath) {
33594
+ const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir();
33595
+ const claudishDir = join8(homeDir, ".claudish");
33596
+ const timestamp = Date.now();
33597
+ const scriptPath = join8(claudishDir, `status-${timestamp}.js`);
33598
+ const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
33599
+ const script = `
33600
+ const fs = require('fs');
33601
+ const path = require('path');
33602
+
33603
+ const CYAN = "\\x1b[96m";
33604
+ const YELLOW = "\\x1b[93m";
33605
+ const GREEN = "\\x1b[92m";
33606
+ const MAGENTA = "\\x1b[95m";
33607
+ const DIM = "\\x1b[2m";
33608
+ const RESET = "\\x1b[0m";
33609
+ const BOLD = "\\x1b[1m";
33610
+
33611
+ // Format token count with k/M suffix
33612
+ function formatTokens(n) {
33613
+ if (n >= 1000000) return (n / 1000000).toFixed(n >= 10000000 ? 0 : 1).replace(/\\.0$/, '') + 'M';
33614
+ if (n >= 1000) return (n / 1000).toFixed(n >= 10000 ? 0 : 1).replace(/\\.0$/, '') + 'k';
33615
+ return String(n);
33616
+ }
33617
+
33618
+ let input = '';
33619
+ process.stdin.setEncoding('utf8');
33620
+ process.stdin.on('data', chunk => input += chunk);
33621
+ process.stdin.on('end', () => {
33622
+ try {
33623
+ let dir = path.basename(process.cwd());
33624
+ if (dir.length > 15) dir = dir.substring(0, 12) + '...';
33625
+
33626
+ let ctx = 100, cost = 0, inputTokens = 0, contextWindow = 0;
33627
+ const model = process.env.CLAUDISH_ACTIVE_MODEL_NAME || 'unknown';
33628
+ const isLocal = process.env.CLAUDISH_IS_LOCAL === 'true';
33629
+
33630
+ let isFree = false, isEstimated = false, providerName = '';
33631
+ try {
33632
+ const tokens = JSON.parse(fs.readFileSync('${escapedTokenPath}', 'utf-8'));
33633
+ cost = tokens.total_cost || 0;
33634
+ ctx = tokens.context_left_percent || 100;
33635
+ inputTokens = tokens.input_tokens || 0;
33636
+ contextWindow = tokens.context_window || 0;
33637
+ isFree = tokens.is_free || false;
33638
+ isEstimated = tokens.is_estimated || false;
33639
+ providerName = tokens.provider_name || '';
33640
+ } catch (e) {
33641
+ try {
33642
+ const json = JSON.parse(input);
33643
+ cost = json.total_cost_usd || 0;
33644
+ } catch {}
33645
+ }
33646
+
33647
+ let costDisplay;
33648
+ if (isLocal) {
33649
+ costDisplay = 'LOCAL';
33650
+ } else if (isFree) {
33651
+ costDisplay = 'FREE';
33652
+ } else if (isEstimated) {
33653
+ costDisplay = '~$' + cost.toFixed(3);
33654
+ } else {
33655
+ costDisplay = '$' + cost.toFixed(3);
33656
+ }
33657
+ const modelDisplay = providerName ? providerName + ' ' + model : model;
33658
+ // Format context display: "96% (37k/1M)" or just "96%" if no token data
33659
+ let ctxDisplay = ctx + '%';
33660
+ if (inputTokens > 0 && contextWindow > 0) {
33661
+ ctxDisplay = ctx + '% (' + formatTokens(inputTokens) + '/' + formatTokens(contextWindow) + ')';
33662
+ }
33663
+ console.log(\`\${CYAN}\${BOLD}\${dir}\${RESET} \${DIM}•\${RESET} \${YELLOW}\${modelDisplay}\${RESET} \${DIM}•\${RESET} \${GREEN}\${costDisplay}\${RESET} \${DIM}•\${RESET} \${MAGENTA}\${ctxDisplay}\${RESET}\`);
33664
+ } catch (e) {
33665
+ console.log('claudish');
33666
+ }
33667
+ });
33668
+ `;
33669
+ writeFileSync6(scriptPath, script, "utf-8");
33670
+ return scriptPath;
33671
+ }
33672
+ function createTempSettingsFile(modelDisplay, port) {
33673
+ const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir();
33674
+ const claudishDir = join8(homeDir, ".claudish");
33675
+ try {
33676
+ mkdirSync6(claudishDir, { recursive: true });
33677
+ } catch {}
33678
+ const timestamp = Date.now();
33679
+ const tempPath = join8(claudishDir, `settings-${timestamp}.json`);
33680
+ const tokenFilePath = join8(claudishDir, `tokens-${port}.json`);
33681
+ let statusCommand;
33682
+ if (isWindows()) {
33683
+ const scriptPath = createStatusLineScript(tokenFilePath);
33684
+ statusCommand = `node "${scriptPath}"`;
33685
+ } else {
33686
+ const CYAN2 = "\\033[96m";
33687
+ const YELLOW2 = "\\033[93m";
33688
+ const GREEN2 = "\\033[92m";
33689
+ const MAGENTA = "\\033[95m";
33690
+ const DIM2 = "\\033[2m";
33691
+ const RESET2 = "\\033[0m";
33692
+ const BOLD2 = "\\033[1m";
33693
+ const formatTokensBash = `fmt_tok() { local n=\${1:-0}; if [ "$n" -ge 1000000 ]; then echo "$((n/1000000))M"; elif [ "$n" -ge 1000 ]; then echo "$((n/1000))k"; else echo "$n"; fi; }`;
33694
+ statusCommand = `JSON=$(cat) && DIR=$(basename "$(pwd)") && [ \${#DIR} -gt 15 ] && DIR="\${DIR:0:12}..." || true && CTX=100 && COST="0" && IS_FREE="false" && IS_EST="false" && PROVIDER="" && IN_TOK=0 && CTX_WIN=0 && ${formatTokensBash} && if [ -f "${tokenFilePath}" ]; then TOKENS=$(cat "${tokenFilePath}" 2>/dev/null | tr -d ' \\n') && REAL_CTX=$(echo "$TOKENS" | grep -o '"context_left_percent":[0-9]*' | grep -o '[0-9]*') && if [ ! -z "$REAL_CTX" ]; then CTX="$REAL_CTX"; fi && REAL_COST=$(echo "$TOKENS" | grep -o '"total_cost":[0-9.]*' | cut -d: -f2) && if [ ! -z "$REAL_COST" ]; then COST="$REAL_COST"; fi && IN_TOK=$(echo "$TOKENS" | grep -o '"input_tokens":[0-9]*' | grep -o '[0-9]*') && CTX_WIN=$(echo "$TOKENS" | grep -o '"context_window":[0-9]*' | grep -o '[0-9]*') && IS_FREE=$(echo "$TOKENS" | grep -o '"is_free":[a-z]*' | cut -d: -f2) && IS_EST=$(echo "$TOKENS" | grep -o '"is_estimated":[a-z]*' | cut -d: -f2) && PROVIDER=$(echo "$TOKENS" | grep -o '"provider_name":"[^"]*"' | cut -d'"' -f4); fi && if [ "$CLAUDISH_IS_LOCAL" = "true" ]; then COST_DISPLAY="LOCAL"; elif [ "$IS_FREE" = "true" ]; then COST_DISPLAY="FREE"; elif [ "$IS_EST" = "true" ]; then COST_DISPLAY=$(printf "~\\$%.3f" "$COST"); else COST_DISPLAY=$(printf "\\$%.3f" "$COST"); fi && MODEL_DISPLAY="$CLAUDISH_ACTIVE_MODEL_NAME" && if [ ! -z "$PROVIDER" ]; then MODEL_DISPLAY="$PROVIDER $MODEL_DISPLAY"; fi && if [ "$IN_TOK" -gt 0 ] 2>/dev/null && [ "$CTX_WIN" -gt 0 ] 2>/dev/null; then CTX_DISPLAY="$CTX% ($(fmt_tok $IN_TOK)/$(fmt_tok $CTX_WIN))"; else CTX_DISPLAY="$CTX%"; fi && printf "${CYAN2}${BOLD2}%s${RESET2} ${DIM2}•${RESET2} ${YELLOW2}%s${RESET2} ${DIM2}•${RESET2} ${GREEN2}%s${RESET2} ${DIM2}•${RESET2} ${MAGENTA}%s${RESET2}\\n" "$DIR" "$MODEL_DISPLAY" "$COST_DISPLAY" "$CTX_DISPLAY"`;
33695
+ }
33696
+ const settings = {
33697
+ statusLine: {
33698
+ type: "command",
33699
+ command: statusCommand,
33700
+ padding: 0
33701
+ }
33702
+ };
33703
+ writeFileSync6(tempPath, JSON.stringify(settings, null, 2), "utf-8");
33704
+ return tempPath;
33705
+ }
33706
+ async function runClaudeWithProxy(config3, proxyUrl) {
33707
+ const modelId = config3.model || "unknown";
33708
+ const portMatch = proxyUrl.match(/:(\d+)/);
33709
+ const port = portMatch ? portMatch[1] : "unknown";
33710
+ const tempSettingsPath = createTempSettingsFile(modelId, port);
33711
+ const claudeArgs = [];
33712
+ claudeArgs.push("--settings", tempSettingsPath);
33713
+ if (config3.interactive) {
33714
+ if (config3.autoApprove) {
33715
+ claudeArgs.push("--dangerously-skip-permissions");
33716
+ }
33717
+ if (config3.dangerous) {
33718
+ claudeArgs.push("--dangerouslyDisableSandbox");
33719
+ }
33720
+ } else {
33721
+ claudeArgs.push("-p");
33722
+ if (config3.autoApprove) {
33723
+ claudeArgs.push("--dangerously-skip-permissions");
33724
+ }
33725
+ if (config3.dangerous) {
33726
+ claudeArgs.push("--dangerouslyDisableSandbox");
33727
+ }
33728
+ if (config3.jsonOutput) {
33729
+ claudeArgs.push("--output-format", "json");
33730
+ }
33731
+ if (config3.agent && config3.claudeArgs.length > 0) {
33732
+ const modifiedArgs = [...config3.claudeArgs];
33733
+ const agentId = config3.agent.startsWith("@agent-") ? config3.agent : `@agent-${config3.agent}`;
33734
+ modifiedArgs[0] = `Use the ${agentId} agent to: ${modifiedArgs[0]}`;
33735
+ claudeArgs.push(...modifiedArgs);
33736
+ } else {
33737
+ claudeArgs.push(...config3.claudeArgs);
33738
+ }
33739
+ }
33740
+ const isLocalModel2 = modelId.startsWith("ollama/") || modelId.startsWith("ollama:") || modelId.startsWith("lmstudio/") || modelId.startsWith("lmstudio:") || modelId.startsWith("vllm/") || modelId.startsWith("vllm:") || modelId.startsWith("mlx/") || modelId.startsWith("mlx:") || modelId.startsWith("http://") || modelId.startsWith("https://");
33741
+ const env = {
33742
+ ...process.env,
33743
+ ANTHROPIC_BASE_URL: proxyUrl,
33744
+ [ENV.CLAUDISH_ACTIVE_MODEL_NAME]: modelId,
33745
+ CLAUDISH_IS_LOCAL: isLocalModel2 ? "true" : "false"
33746
+ };
33747
+ if (config3.monitor) {
33748
+ delete env.ANTHROPIC_API_KEY;
33749
+ delete env.ANTHROPIC_AUTH_TOKEN;
33750
+ if (modelId && modelId !== "unknown") {
33751
+ env[ENV.ANTHROPIC_MODEL] = modelId;
33752
+ env[ENV.ANTHROPIC_SMALL_FAST_MODEL] = modelId;
33753
+ }
33754
+ } else {
33755
+ env[ENV.ANTHROPIC_MODEL] = modelId;
33756
+ env[ENV.ANTHROPIC_SMALL_FAST_MODEL] = modelId;
33757
+ env.ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY || "sk-ant-api03-placeholder-not-used-proxy-handles-auth-with-openrouter-key-xxxxxxxxxxxxxxxxxxxxx";
33758
+ env.ANTHROPIC_AUTH_TOKEN = process.env.ANTHROPIC_AUTH_TOKEN || "placeholder-token-not-used-proxy-handles-auth";
33759
+ }
33760
+ const log2 = (message) => {
33761
+ if (!config3.quiet) {
33762
+ console.log(message);
33763
+ }
33764
+ };
33765
+ if (config3.interactive) {
33766
+ log2(`
33767
+ [claudish] Model: ${modelId}
33768
+ `);
33769
+ } else {
33770
+ log2(`
33771
+ [claudish] Model: ${modelId}`);
33772
+ log2(`[claudish] Arguments: ${claudeArgs.join(" ")}
33773
+ `);
33774
+ }
33775
+ const claudeBinary = await findClaudeBinary();
33776
+ if (!claudeBinary) {
33777
+ console.error("Error: Claude Code CLI not found");
33778
+ console.error("Install it from: https://claude.com/claude-code");
33779
+ console.error(`
33780
+ Or set CLAUDE_PATH to your custom installation:`);
33781
+ const home = homedir6();
33782
+ const localPath = isWindows() ? join8(home, ".claude", "local", "claude.exe") : join8(home, ".claude", "local", "claude");
33783
+ console.error(` export CLAUDE_PATH=${localPath}`);
33784
+ process.exit(1);
33785
+ }
33786
+ const proc = spawn(claudeBinary, claudeArgs, {
33787
+ env,
33788
+ stdio: "inherit",
33789
+ shell: false
33790
+ });
33791
+ setupSignalHandlers(proc, tempSettingsPath, config3.quiet);
33792
+ const exitCode = await new Promise((resolve) => {
33793
+ proc.on("exit", (code) => {
33794
+ resolve(code ?? 1);
33795
+ });
33796
+ });
33797
+ try {
33798
+ unlinkSync2(tempSettingsPath);
33799
+ } catch (error46) {}
33800
+ return exitCode;
33801
+ }
33802
+ function setupSignalHandlers(proc, tempSettingsPath, quiet) {
33803
+ const signals2 = isWindows() ? ["SIGINT", "SIGTERM"] : ["SIGINT", "SIGTERM", "SIGHUP"];
33804
+ for (const signal of signals2) {
33805
+ process.on(signal, () => {
33806
+ if (!quiet) {
33807
+ console.log(`
33808
+ [claudish] Received ${signal}, shutting down...`);
33809
+ }
33810
+ proc.kill();
33811
+ try {
33812
+ unlinkSync2(tempSettingsPath);
33813
+ } catch {}
33814
+ process.exit(0);
33815
+ });
33816
+ }
33817
+ }
33818
+ async function findClaudeBinary() {
33819
+ const isWindows2 = process.platform === "win32";
33820
+ if (process.env.CLAUDE_PATH) {
33821
+ if (existsSync8(process.env.CLAUDE_PATH)) {
33822
+ return process.env.CLAUDE_PATH;
33823
+ }
33824
+ }
33825
+ const home = homedir6();
33826
+ const localPath = isWindows2 ? join8(home, ".claude", "local", "claude.exe") : join8(home, ".claude", "local", "claude");
33827
+ if (existsSync8(localPath)) {
33828
+ return localPath;
33829
+ }
33830
+ if (isWindows2) {
33831
+ const windowsPaths = [
33832
+ join8(home, "AppData", "Roaming", "npm", "claude.cmd"),
33833
+ join8(home, ".npm-global", "claude.cmd"),
33834
+ join8(home, "node_modules", ".bin", "claude.cmd")
33835
+ ];
33836
+ for (const path of windowsPaths) {
33837
+ if (existsSync8(path)) {
33838
+ return path;
33839
+ }
33840
+ }
33841
+ } else {
33842
+ const commonPaths = [
33843
+ "/usr/local/bin/claude",
33844
+ "/opt/homebrew/bin/claude",
33845
+ join8(home, ".npm-global/bin/claude"),
33846
+ join8(home, ".local/bin/claude"),
33847
+ join8(home, "node_modules/.bin/claude"),
33848
+ "/data/data/com.termux/files/usr/bin/claude",
33849
+ join8(home, "../usr/bin/claude")
33850
+ ];
33851
+ for (const path of commonPaths) {
33852
+ if (existsSync8(path)) {
33853
+ return path;
33854
+ }
33855
+ }
33856
+ }
33857
+ try {
33858
+ const shellCommand = isWindows2 ? "where claude" : "command -v claude";
33859
+ const proc = spawn(shellCommand, [], {
33860
+ stdio: "pipe",
33861
+ shell: true
33862
+ });
33863
+ let output = "";
33864
+ proc.stdout?.on("data", (data) => {
33865
+ output += data.toString();
33866
+ });
33867
+ const exitCode = await new Promise((resolve) => {
33868
+ proc.on("exit", (code) => {
33869
+ resolve(code ?? 1);
33870
+ });
33871
+ });
33872
+ if (exitCode === 0 && output.trim()) {
33873
+ const lines = output.trim().split(/\r?\n/);
33874
+ if (isWindows2) {
33875
+ const cmdPath = lines.find((line) => line.endsWith(".cmd"));
33876
+ if (cmdPath) {
33877
+ return cmdPath;
33878
+ }
33879
+ }
33880
+ return lines[0];
33881
+ }
33882
+ } catch {}
33883
+ return null;
33884
+ }
33885
+ async function checkClaudeInstalled() {
33886
+ const binary = await findClaudeBinary();
33887
+ return binary !== null;
33888
+ }
33889
+ var init_claude_runner = __esm(() => {
33890
+ init_config();
33692
33891
  });
33693
33892
 
33694
33893
  // src/port-manager.ts
@@ -60948,6 +61147,12 @@ class OpenAIHandler {
60948
61147
  this.contextWindow = 131072;
60949
61148
  } else if (model.includes("grok")) {
60950
61149
  this.contextWindow = 131072;
61150
+ } else if (model.includes("kimi-k2.5") || model.includes("kimi-k2-5")) {
61151
+ this.contextWindow = 262144;
61152
+ } else if (model.includes("kimi-k2")) {
61153
+ this.contextWindow = 262144;
61154
+ } else if (model.includes("kimi")) {
61155
+ this.contextWindow = 131072;
60951
61156
  } else if (model.includes("gpt-4o") || model.includes("gpt-4-turbo")) {
60952
61157
  this.contextWindow = 128000;
60953
61158
  } else if (model.includes("gpt-5")) {
@@ -60978,6 +61183,8 @@ class OpenAIHandler {
60978
61183
  return "Zen";
60979
61184
  if (name === "glm")
60980
61185
  return "GLM";
61186
+ if (name === "openai")
61187
+ return "OpenAI";
60981
61188
  return name.charAt(0).toUpperCase() + name.slice(1);
60982
61189
  };
60983
61190
  const pricing = this.getPricing();
@@ -61187,7 +61394,7 @@ class OpenAIHandler {
61187
61394
  }
61188
61395
  return payload;
61189
61396
  }
61190
- async handleResponsesStreaming(c, response, _adapter, _claudeRequest) {
61397
+ async handleResponsesStreaming(c, response, _adapter, _claudeRequest, toolNameMap) {
61191
61398
  const reader = response.body?.getReader();
61192
61399
  if (!reader) {
61193
61400
  return c.json({ error: "No response body" }, 500);
@@ -61283,7 +61490,8 @@ data: ${JSON.stringify(data)}
61283
61490
  const itemId = event.item.id;
61284
61491
  const openaiCallId = event.item.call_id || itemId;
61285
61492
  const callId = openaiCallId.startsWith("toolu_") ? openaiCallId : `toolu_${openaiCallId.replace(/^fc_/, "")}`;
61286
- const fnName = event.item.name || "";
61493
+ const rawFnName = event.item.name || "";
61494
+ const fnName = toolNameMap?.get(rawFnName) || rawFnName;
61287
61495
  const fnIndex = blockIndex + functionCalls.size + (hasTextContent ? 1 : 0);
61288
61496
  log(`[OpenAIHandler] Function call: itemId=${itemId}, openaiCallId=${openaiCallId}, claudeId=${callId}, name=${fnName}, index=${fnIndex}`);
61289
61497
  const fnCallData = {
@@ -61524,6 +61732,7 @@ data: ${JSON.stringify(data)}
61524
61732
  if (typeof adapter.reset === "function")
61525
61733
  adapter.reset();
61526
61734
  adapter.prepareRequest(apiPayload, claudeRequest);
61735
+ const toolNameMap = adapter.getToolNameMap();
61527
61736
  await this.middlewareManager.beforeRequest({
61528
61737
  modelId: `openai/${this.modelName}`,
61529
61738
  messages,
@@ -61586,9 +61795,9 @@ data: ${JSON.stringify(data)}
61586
61795
  }
61587
61796
  if (isCodex) {
61588
61797
  log(`[OpenAIHandler] Using Responses API streaming handler for Codex model`);
61589
- return this.handleResponsesStreaming(c, response, adapter, claudeRequest);
61798
+ return this.handleResponsesStreaming(c, response, adapter, claudeRequest, toolNameMap);
61590
61799
  }
61591
- return createStreamingResponseHandler(c, response, adapter, `openai/${this.modelName}`, this.middlewareManager, (input, output) => this.updateTokenTracking(input, output), claudeRequest.tools);
61800
+ return createStreamingResponseHandler(c, response, adapter, `openai/${this.modelName}`, this.middlewareManager, (input, output) => this.updateTokenTracking(input, output), claudeRequest.tools, toolNameMap);
61592
61801
  }
61593
61802
  async shutdown() {}
61594
61803
  }
@@ -64262,6 +64471,9 @@ var args = process.argv.slice(2);
64262
64471
  var firstArg = args[0];
64263
64472
  var isGeminiLogin = args.includes("--gemini-login");
64264
64473
  var isGeminiLogout = args.includes("--gemini-logout");
64474
+ var isUpdateCommand = args.includes("update");
64475
+ var isInitCommand = args[0] === "init" || args.includes("init");
64476
+ var isProfileCommand = args[0] === "profile" || args.some((a, i) => a === "profile" && (i === 0 || !args[i - 1]?.startsWith("-")));
64265
64477
  if (isMcpMode) {
64266
64478
  Promise.resolve().then(() => (init_mcp_server(), exports_mcp_server)).then((mcp) => mcp.startMcpServer());
64267
64479
  } else if (isGeminiLogin) {
@@ -64291,13 +64503,75 @@ if (isMcpMode) {
64291
64503
  process.exit(1);
64292
64504
  }
64293
64505
  });
64294
- } else if (firstArg === "init") {
64506
+ } else if (isUpdateCommand) {
64507
+ runSelfUpdate();
64508
+ } else if (isInitCommand) {
64295
64509
  Promise.resolve().then(() => (init_profile_commands(), exports_profile_commands)).then((pc) => pc.initCommand());
64296
- } else if (firstArg === "profile") {
64297
- Promise.resolve().then(() => (init_profile_commands(), exports_profile_commands)).then((pc) => pc.profileCommand(args.slice(1)));
64510
+ } else if (isProfileCommand) {
64511
+ const profileArgIndex = args.findIndex((a) => a === "profile");
64512
+ Promise.resolve().then(() => (init_profile_commands(), exports_profile_commands)).then((pc) => pc.profileCommand(args.slice(profileArgIndex + 1)));
64298
64513
  } else {
64299
64514
  runCli();
64300
64515
  }
64516
+ function detectInstallMethod() {
64517
+ const scriptPath = process.argv[1] || "";
64518
+ if (scriptPath.includes("/.bun/")) {
64519
+ return "bun";
64520
+ }
64521
+ if (scriptPath.includes("/Cellar/") || scriptPath.includes("/homebrew/")) {
64522
+ return "brew";
64523
+ }
64524
+ if (scriptPath.includes("/node_modules/") || scriptPath.includes("/.nvm/") || scriptPath.includes("/npm/")) {
64525
+ return "npm";
64526
+ }
64527
+ return "unknown";
64528
+ }
64529
+ function getUpdateCommand2(method) {
64530
+ switch (method) {
64531
+ case "bun":
64532
+ return "bun install -g claudish@latest";
64533
+ case "brew":
64534
+ return "brew upgrade claudish";
64535
+ case "npm":
64536
+ default:
64537
+ return "npm install -g claudish@latest";
64538
+ }
64539
+ }
64540
+ async function runSelfUpdate() {
64541
+ const { getVersion: getVersion2 } = await Promise.resolve().then(() => (init_cli(), exports_cli));
64542
+ const currentVersion = getVersion2();
64543
+ const installMethod = detectInstallMethod();
64544
+ console.log(`claudish v${currentVersion}`);
64545
+ console.log(`Installation: ${installMethod}`);
64546
+ console.log(`
64547
+ Checking for updates...
64548
+ `);
64549
+ try {
64550
+ const response = await fetch("https://registry.npmjs.org/claudish/latest", {
64551
+ signal: AbortSignal.timeout(5000)
64552
+ });
64553
+ if (!response.ok) {
64554
+ console.error("Failed to check for updates");
64555
+ process.exit(1);
64556
+ }
64557
+ const data = await response.json();
64558
+ const latestVersion = data.version;
64559
+ if (latestVersion === currentVersion) {
64560
+ console.log(`✓ claudish is up to date (v${currentVersion})`);
64561
+ process.exit(0);
64562
+ }
64563
+ console.log(`New version available: v${currentVersion} → v${latestVersion}
64564
+ `);
64565
+ const updateCmd = getUpdateCommand2(installMethod);
64566
+ console.log(`To update, run:
64567
+ ${updateCmd}
64568
+ `);
64569
+ process.exit(0);
64570
+ } catch (error46) {
64571
+ console.error("Failed to check for updates:", error46 instanceof Error ? error46.message : error46);
64572
+ process.exit(1);
64573
+ }
64574
+ }
64301
64575
  async function runCli() {
64302
64576
  const { checkClaudeInstalled: checkClaudeInstalled2, runClaudeWithProxy: runClaudeWithProxy2 } = await Promise.resolve().then(() => (init_claude_runner(), exports_claude_runner));
64303
64577
  const { parseArgs: parseArgs2, getVersion: getVersion2 } = await Promise.resolve().then(() => (init_cli(), exports_cli));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "4.4.0",
3
+ "version": "4.4.2",
4
4
  "description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",