claudish 4.5.1 → 4.5.3

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
@@ -28447,28 +28447,130 @@ function configExists() {
28447
28447
  function getConfigPath() {
28448
28448
  return CONFIG_FILE;
28449
28449
  }
28450
- function getProfile(name) {
28450
+ function getLocalConfigPath() {
28451
+ return join5(process.cwd(), LOCAL_CONFIG_FILENAME);
28452
+ }
28453
+ function localConfigExists() {
28454
+ return existsSync5(getLocalConfigPath());
28455
+ }
28456
+ function isProjectDirectory() {
28457
+ const cwd = process.cwd();
28458
+ return [".git", "package.json", "Cargo.toml", "go.mod", "pyproject.toml", ".claudish.json"].some((f) => existsSync5(join5(cwd, f)));
28459
+ }
28460
+ function loadLocalConfig() {
28461
+ const localPath = getLocalConfigPath();
28462
+ if (!existsSync5(localPath)) {
28463
+ return null;
28464
+ }
28465
+ try {
28466
+ const content = readFileSync4(localPath, "utf-8");
28467
+ const config3 = JSON.parse(content);
28468
+ return {
28469
+ version: config3.version || DEFAULT_CONFIG.version,
28470
+ defaultProfile: config3.defaultProfile || "",
28471
+ profiles: config3.profiles || {}
28472
+ };
28473
+ } catch (error46) {
28474
+ console.error(`Warning: Failed to load local config: ${error46}`);
28475
+ return null;
28476
+ }
28477
+ }
28478
+ function saveLocalConfig(config3) {
28479
+ writeFileSync3(getLocalConfigPath(), JSON.stringify(config3, null, 2), "utf-8");
28480
+ }
28481
+ function loadConfigForScope(scope) {
28482
+ if (scope === "local") {
28483
+ return loadLocalConfig() || { version: "1.0.0", defaultProfile: "", profiles: {} };
28484
+ }
28485
+ return loadConfig();
28486
+ }
28487
+ function saveConfigForScope(config3, scope) {
28488
+ if (scope === "local") {
28489
+ saveLocalConfig(config3);
28490
+ } else {
28491
+ saveConfig(config3);
28492
+ }
28493
+ }
28494
+ function configExistsForScope(scope) {
28495
+ if (scope === "local") {
28496
+ return localConfigExists();
28497
+ }
28498
+ return configExists();
28499
+ }
28500
+ function getConfigPathForScope(scope) {
28501
+ if (scope === "local") {
28502
+ return getLocalConfigPath();
28503
+ }
28504
+ return getConfigPath();
28505
+ }
28506
+ function getProfile(name, scope) {
28507
+ if (scope === "local") {
28508
+ const local2 = loadLocalConfig();
28509
+ return local2?.profiles[name];
28510
+ }
28511
+ if (scope === "global") {
28512
+ const config4 = loadConfig();
28513
+ return config4.profiles[name];
28514
+ }
28515
+ const local = loadLocalConfig();
28516
+ if (local?.profiles[name]) {
28517
+ return local.profiles[name];
28518
+ }
28451
28519
  const config3 = loadConfig();
28452
28520
  return config3.profiles[name];
28453
28521
  }
28454
- function getDefaultProfile() {
28455
- const config3 = loadConfig();
28456
- const profile = config3.profiles[config3.defaultProfile];
28457
- if (!profile) {
28458
- const firstProfile = Object.values(config3.profiles)[0];
28459
- if (firstProfile) {
28460
- return firstProfile;
28522
+ function getDefaultProfile(scope) {
28523
+ if (scope === "local") {
28524
+ const local2 = loadLocalConfig();
28525
+ if (local2 && local2.defaultProfile && local2.profiles[local2.defaultProfile]) {
28526
+ return local2.profiles[local2.defaultProfile];
28461
28527
  }
28462
28528
  return DEFAULT_CONFIG.profiles.default;
28463
28529
  }
28464
- return profile;
28465
- }
28466
- function getProfileNames() {
28530
+ if (scope === "global") {
28531
+ const config4 = loadConfig();
28532
+ const profile2 = config4.profiles[config4.defaultProfile];
28533
+ if (profile2)
28534
+ return profile2;
28535
+ const firstProfile2 = Object.values(config4.profiles)[0];
28536
+ if (firstProfile2)
28537
+ return firstProfile2;
28538
+ return DEFAULT_CONFIG.profiles.default;
28539
+ }
28540
+ const local = loadLocalConfig();
28541
+ if (local && local.defaultProfile) {
28542
+ const profile2 = getProfile(local.defaultProfile);
28543
+ if (profile2)
28544
+ return profile2;
28545
+ }
28467
28546
  const config3 = loadConfig();
28468
- return Object.keys(config3.profiles);
28469
- }
28470
- function setProfile(profile) {
28547
+ const profile = config3.profiles[config3.defaultProfile];
28548
+ if (profile)
28549
+ return profile;
28550
+ const firstProfile = Object.values(config3.profiles)[0];
28551
+ if (firstProfile)
28552
+ return firstProfile;
28553
+ return DEFAULT_CONFIG.profiles.default;
28554
+ }
28555
+ function getProfileNames(scope) {
28556
+ if (scope === "local") {
28557
+ const local2 = loadLocalConfig();
28558
+ return local2 ? Object.keys(local2.profiles) : [];
28559
+ }
28560
+ if (scope === "global") {
28561
+ const config4 = loadConfig();
28562
+ return Object.keys(config4.profiles);
28563
+ }
28564
+ const local = loadLocalConfig();
28471
28565
  const config3 = loadConfig();
28566
+ const names = new Set([
28567
+ ...local ? Object.keys(local.profiles) : [],
28568
+ ...Object.keys(config3.profiles)
28569
+ ]);
28570
+ return [...names];
28571
+ }
28572
+ function setProfile(profile, scope = "global") {
28573
+ const config3 = loadConfigForScope(scope);
28472
28574
  const existingProfile = config3.profiles[profile.name];
28473
28575
  if (existingProfile) {
28474
28576
  profile.createdAt = existingProfile.createdAt;
@@ -28477,31 +28579,34 @@ function setProfile(profile) {
28477
28579
  }
28478
28580
  profile.updatedAt = new Date().toISOString();
28479
28581
  config3.profiles[profile.name] = profile;
28480
- saveConfig(config3);
28582
+ saveConfigForScope(config3, scope);
28481
28583
  }
28482
- function deleteProfile(name) {
28483
- const config3 = loadConfig();
28584
+ function deleteProfile(name, scope = "global") {
28585
+ const config3 = loadConfigForScope(scope);
28484
28586
  if (!config3.profiles[name]) {
28485
28587
  return false;
28486
28588
  }
28487
- const profileCount = Object.keys(config3.profiles).length;
28488
- if (profileCount <= 1) {
28489
- throw new Error("Cannot delete the last profile");
28589
+ if (scope === "global") {
28590
+ const profileCount = Object.keys(config3.profiles).length;
28591
+ if (profileCount <= 1) {
28592
+ throw new Error("Cannot delete the last global profile");
28593
+ }
28490
28594
  }
28491
28595
  delete config3.profiles[name];
28492
28596
  if (config3.defaultProfile === name) {
28493
- config3.defaultProfile = Object.keys(config3.profiles)[0];
28597
+ const remaining = Object.keys(config3.profiles);
28598
+ config3.defaultProfile = remaining.length > 0 ? remaining[0] : "";
28494
28599
  }
28495
- saveConfig(config3);
28600
+ saveConfigForScope(config3, scope);
28496
28601
  return true;
28497
28602
  }
28498
- function setDefaultProfile(name) {
28499
- const config3 = loadConfig();
28603
+ function setDefaultProfile(name, scope = "global") {
28604
+ const config3 = loadConfigForScope(scope);
28500
28605
  if (!config3.profiles[name]) {
28501
- throw new Error(`Profile "${name}" does not exist`);
28606
+ throw new Error(`Profile "${name}" does not exist in ${scope} config`);
28502
28607
  }
28503
28608
  config3.defaultProfile = name;
28504
- saveConfig(config3);
28609
+ saveConfigForScope(config3, scope);
28505
28610
  }
28506
28611
  function getModelMapping(profileName) {
28507
28612
  const profile = profileName ? getProfile(profileName) : getDefaultProfile();
@@ -28510,7 +28615,7 @@ function getModelMapping(profileName) {
28510
28615
  }
28511
28616
  return profile.models;
28512
28617
  }
28513
- function createProfile(name, models, description) {
28618
+ function createProfile(name, models, description, scope = "global") {
28514
28619
  const now = new Date().toISOString();
28515
28620
  const profile = {
28516
28621
  name,
@@ -28519,17 +28624,34 @@ function createProfile(name, models, description) {
28519
28624
  createdAt: now,
28520
28625
  updatedAt: now
28521
28626
  };
28522
- setProfile(profile);
28627
+ setProfile(profile, scope);
28523
28628
  return profile;
28524
28629
  }
28525
- function listProfiles() {
28526
- const config3 = loadConfig();
28527
- return Object.values(config3.profiles).map((profile) => ({
28528
- ...profile,
28529
- isDefault: profile.name === config3.defaultProfile
28530
- }));
28630
+ function listAllProfiles() {
28631
+ const globalConfig2 = loadConfig();
28632
+ const localConfig = loadLocalConfig();
28633
+ const result = [];
28634
+ if (localConfig) {
28635
+ for (const profile of Object.values(localConfig.profiles)) {
28636
+ result.push({
28637
+ ...profile,
28638
+ scope: "local",
28639
+ isDefault: profile.name === localConfig.defaultProfile
28640
+ });
28641
+ }
28642
+ }
28643
+ const localNames = localConfig ? new Set(Object.keys(localConfig.profiles)) : new Set;
28644
+ for (const profile of Object.values(globalConfig2.profiles)) {
28645
+ result.push({
28646
+ ...profile,
28647
+ scope: "global",
28648
+ isDefault: profile.name === globalConfig2.defaultProfile,
28649
+ shadowed: localNames.has(profile.name)
28650
+ });
28651
+ }
28652
+ return result;
28531
28653
  }
28532
- var CONFIG_DIR, CONFIG_FILE, DEFAULT_CONFIG;
28654
+ var CONFIG_DIR, CONFIG_FILE, LOCAL_CONFIG_FILENAME = ".claudish.json", DEFAULT_CONFIG;
28533
28655
  var init_profile_config = __esm(() => {
28534
28656
  CONFIG_DIR = join5(homedir4(), ".claudish");
28535
28657
  CONFIG_FILE = join5(CONFIG_DIR, "config.json");
@@ -31072,6 +31194,129 @@ async function fetchOpenAIModels() {
31072
31194
  return [];
31073
31195
  }
31074
31196
  }
31197
+ async function fetchGLMCodingModels() {
31198
+ const apiKey = process.env.GLM_CODING_API_KEY;
31199
+ if (!apiKey) {
31200
+ return [];
31201
+ }
31202
+ try {
31203
+ const response = await fetch("https://models.dev/api.json", {
31204
+ signal: AbortSignal.timeout(5000)
31205
+ });
31206
+ if (!response.ok) {
31207
+ return [];
31208
+ }
31209
+ const data = await response.json();
31210
+ const codingPlan = data["zai-coding-plan"];
31211
+ if (!codingPlan?.models)
31212
+ return [];
31213
+ return Object.entries(codingPlan.models).filter(([_, m]) => m.tool_call === true).map(([id, m]) => {
31214
+ const inputModalities = m.modalities?.input || [];
31215
+ const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
31216
+ const contextLength = m.limit?.context || 131072;
31217
+ return {
31218
+ id: `gc@${id}`,
31219
+ name: m.name || id,
31220
+ description: `GLM Coding Plan (subscription)`,
31221
+ provider: "GLM Coding",
31222
+ pricing: {
31223
+ input: "SUB",
31224
+ output: "SUB",
31225
+ average: "SUB"
31226
+ },
31227
+ context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
31228
+ contextLength,
31229
+ supportsTools: true,
31230
+ supportsReasoning: m.reasoning || false,
31231
+ supportsVision,
31232
+ isFree: false,
31233
+ source: "GLM Coding"
31234
+ };
31235
+ });
31236
+ } catch {
31237
+ return [];
31238
+ }
31239
+ }
31240
+ async function fetchGLMDirectModels() {
31241
+ try {
31242
+ const response = await fetch("https://models.dev/api.json", {
31243
+ signal: AbortSignal.timeout(5000)
31244
+ });
31245
+ if (!response.ok) {
31246
+ return [];
31247
+ }
31248
+ const data = await response.json();
31249
+ const codingPlan = data["zai-coding-plan"];
31250
+ if (!codingPlan?.models)
31251
+ return [];
31252
+ return Object.entries(codingPlan.models).filter(([_, m]) => m.tool_call === true).map(([id, m]) => {
31253
+ const inputModalities = m.modalities?.input || [];
31254
+ const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
31255
+ const contextLength = m.limit?.context || 131072;
31256
+ const inputCost = m.cost?.input || 0;
31257
+ const outputCost = m.cost?.output || 0;
31258
+ const isFree = inputCost === 0 && outputCost === 0;
31259
+ return {
31260
+ id: `glm@${id}`,
31261
+ name: m.name || id,
31262
+ description: `GLM/Zhipu direct API`,
31263
+ provider: "GLM",
31264
+ pricing: {
31265
+ input: isFree ? "FREE" : `$${inputCost.toFixed(2)}`,
31266
+ output: isFree ? "FREE" : `$${outputCost.toFixed(2)}`,
31267
+ average: isFree ? "FREE" : `$${((inputCost + outputCost) / 2).toFixed(2)}/1M`
31268
+ },
31269
+ context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
31270
+ contextLength,
31271
+ supportsTools: true,
31272
+ supportsReasoning: m.reasoning || false,
31273
+ supportsVision,
31274
+ isFree,
31275
+ source: "GLM"
31276
+ };
31277
+ });
31278
+ } catch {
31279
+ return [];
31280
+ }
31281
+ }
31282
+ async function fetchOllamaCloudModels() {
31283
+ try {
31284
+ const response = await fetch("https://models.dev/api.json", {
31285
+ signal: AbortSignal.timeout(5000)
31286
+ });
31287
+ if (!response.ok) {
31288
+ return [];
31289
+ }
31290
+ const data = await response.json();
31291
+ const ollamaCloud = data["ollama-cloud"];
31292
+ if (!ollamaCloud?.models)
31293
+ return [];
31294
+ return Object.entries(ollamaCloud.models).filter(([_, m]) => m.tool_call === true).map(([id, m]) => {
31295
+ const inputModalities = m.modalities?.input || [];
31296
+ const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
31297
+ const contextLength = m.limit?.context || 131072;
31298
+ return {
31299
+ id: `oc@${id}`,
31300
+ name: m.name || id,
31301
+ description: `OllamaCloud`,
31302
+ provider: "OllamaCloud",
31303
+ pricing: {
31304
+ input: "N/A",
31305
+ output: "N/A",
31306
+ average: "N/A"
31307
+ },
31308
+ context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
31309
+ contextLength,
31310
+ supportsTools: true,
31311
+ supportsReasoning: m.reasoning || false,
31312
+ supportsVision,
31313
+ source: "OllamaCloud"
31314
+ };
31315
+ });
31316
+ } catch {
31317
+ return [];
31318
+ }
31319
+ }
31075
31320
  function shouldRefreshForFreeModels() {
31076
31321
  if (!existsSync6(ALL_MODELS_JSON_PATH)) {
31077
31322
  return true;
@@ -31117,15 +31362,18 @@ async function getFreeModels() {
31117
31362
  return combined;
31118
31363
  }
31119
31364
  async function getAllModelsForSearch() {
31120
- const [openRouterModels, xaiModels, geminiModels, openaiModels, zenModels] = await Promise.all([
31365
+ const [openRouterModels, xaiModels, geminiModels, openaiModels, glmDirectModels, glmCodingModels, ollamaCloudModels, zenModels] = await Promise.all([
31121
31366
  fetchAllModels().then((models) => models.map(toModelInfo)),
31122
31367
  fetchXAIModels(),
31123
31368
  fetchGeminiModels(),
31124
31369
  fetchOpenAIModels(),
31370
+ fetchGLMDirectModels(),
31371
+ fetchGLMCodingModels(),
31372
+ fetchOllamaCloudModels(),
31125
31373
  fetchZenFreeModels()
31126
31374
  ]);
31127
- const directApiModels = [...xaiModels, ...geminiModels, ...openaiModels];
31128
- const allModels = [...zenModels, ...directApiModels, ...openRouterModels];
31375
+ const directApiModels = [...xaiModels, ...geminiModels, ...openaiModels, ...glmDirectModels, ...glmCodingModels];
31376
+ const allModels = [...zenModels, ...ollamaCloudModels, ...directApiModels, ...openRouterModels];
31129
31377
  return allModels;
31130
31378
  }
31131
31379
  function formatModelChoice(model, showSource = false) {
@@ -31143,7 +31391,10 @@ function formatModelChoice(model, showSource = false) {
31143
31391
  OpenRouter: "OR",
31144
31392
  xAI: "xAI",
31145
31393
  Gemini: "Gem",
31146
- OpenAI: "OAI"
31394
+ OpenAI: "OAI",
31395
+ GLM: "GLM",
31396
+ "GLM Coding": "GC",
31397
+ OllamaCloud: "OC"
31147
31398
  };
31148
31399
  const sourceTag = sourceTagMap[model.source] || model.source;
31149
31400
  return `${sourceTag} ${model.id} (${priceStr}, ${ctxStr}${capsStr})`;
@@ -31157,6 +31408,13 @@ function fuzzyMatch(text, query) {
31157
31408
  return 1;
31158
31409
  if (lowerText.includes(lowerQuery))
31159
31410
  return 0.8;
31411
+ const normSep = (s) => s.replace(/[\s\-_.]/g, "");
31412
+ const tn = normSep(lowerText);
31413
+ const qn = normSep(lowerQuery);
31414
+ if (tn === qn)
31415
+ return 0.95;
31416
+ if (tn.includes(qn))
31417
+ return 0.75;
31160
31418
  let queryIdx = 0;
31161
31419
  let score = 0;
31162
31420
  for (let i = 0;i < lowerText.length && queryIdx < lowerQuery.length; i++) {
@@ -31240,12 +31498,12 @@ function getKnownModels(provider) {
31240
31498
  { id: "google@gemini-2.0-flash", name: "Gemini 2.0 Flash", context: "1M" }
31241
31499
  ],
31242
31500
  openai: [
31501
+ { id: "oai@gpt-5.3-codex", name: "GPT-5.3 Codex", context: "400K", description: "Latest coding model" },
31502
+ { id: "oai@gpt-5.2-codex", name: "GPT-5.2 Codex", context: "400K", description: "Coding model" },
31503
+ { id: "oai@gpt-5.1-codex-mini", name: "GPT-5.1 Codex Mini", context: "400K", description: "Fast coding model" },
31243
31504
  { id: "oai@o3", name: "o3", context: "200K", description: "Reasoning model" },
31244
31505
  { id: "oai@o4-mini", name: "o4-mini", context: "200K", description: "Fast reasoning model" },
31245
- { id: "oai@gpt-4.1", name: "GPT-4.1", context: "1M", description: "Latest model" },
31246
- { id: "oai@gpt-4.1-mini", name: "GPT-4.1 Mini", context: "1M", description: "Latest mini model" },
31247
- { id: "oai@gpt-4o", name: "GPT-4o", context: "128K", description: "Multimodal model" },
31248
- { id: "oai@gpt-4o-mini", name: "GPT-4o Mini", context: "128K", description: "Fast multimodal model" }
31506
+ { id: "oai@gpt-4.1", name: "GPT-4.1", context: "1M", description: "Large context model" }
31249
31507
  ],
31250
31508
  xai: [
31251
31509
  { id: "xai@grok-4", name: "Grok 4", context: "256K" },
@@ -31260,15 +31518,22 @@ function getKnownModels(provider) {
31260
31518
  { id: "kimi@moonshot-v1-128k", name: "Moonshot V1 128K", context: "128K" }
31261
31519
  ],
31262
31520
  glm: [
31263
- { id: "glm@glm-4-plus", name: "GLM-4 Plus", context: "128K" },
31264
- { id: "glm@glm-4-flash", name: "GLM-4 Flash", context: "128K" }
31521
+ { id: "glm@glm-5", name: "GLM-5", context: "200K", description: "Latest GLM model with reasoning" },
31522
+ { id: "glm@glm-4.7", name: "GLM-4.7", context: "200K", description: "GLM 4.7 with reasoning" },
31523
+ { id: "glm@glm-4.7-flash", name: "GLM-4.7 Flash", context: "200K", description: "Fast GLM 4.7" },
31524
+ { id: "glm@glm-4.6", name: "GLM-4.6", context: "200K" },
31525
+ { id: "glm@glm-4.5-flash", name: "GLM-4.5 Flash", context: "128K" }
31265
31526
  ],
31266
31527
  zai: [
31267
31528
  { id: "zai@glm-4.7", name: "GLM 4.7 (Z.AI)", context: "128K" }
31268
31529
  ],
31269
31530
  ollamacloud: [
31270
- { id: "oc@llama-3.3-70b", name: "Llama 3.3 70B", context: "128K" },
31271
- { id: "oc@llama-3.1-405b", name: "Llama 3.1 405B", context: "128K" }
31531
+ { id: "oc@glm-5", name: "GLM-5", context: "203K", description: "GLM-5 on OllamaCloud" },
31532
+ { id: "oc@deepseek-v3.2", name: "DeepSeek V3.2", context: "164K", description: "DeepSeek V3.2 on OllamaCloud" },
31533
+ { id: "oc@gemini-3-pro-preview", name: "Gemini 3 Pro Preview", context: "1M", description: "Gemini 3 Pro on OllamaCloud" },
31534
+ { id: "oc@kimi-k2.5", name: "Kimi K2.5", context: "262K", description: "Kimi K2.5 on OllamaCloud" },
31535
+ { id: "oc@qwen3-coder-next", name: "Qwen3 Coder Next", context: "262K", description: "Qwen3 Coder on OllamaCloud" },
31536
+ { id: "oc@minimax-m2.1", name: "MiniMax M2.1", context: "205K", description: "MiniMax M2.1 on OllamaCloud" }
31272
31537
  ]
31273
31538
  };
31274
31539
  const providerDisplay = provider.charAt(0).toUpperCase() + provider.slice(1);
@@ -31490,7 +31755,7 @@ var init_model_selector = __esm(() => {
31490
31755
  { name: "Kimi / Moonshot", value: "kimi", description: "Direct API (MOONSHOT_API_KEY)" },
31491
31756
  { name: "GLM / Zhipu", value: "glm", description: "Direct API (ZHIPU_API_KEY)" },
31492
31757
  { name: "Z.AI", value: "zai", description: "Z.AI API (ZAI_API_KEY)" },
31493
- { name: "OllamaCloud", value: "ollamacloud", description: "Cloud Llama models (OLLAMA_API_KEY)" },
31758
+ { name: "OllamaCloud", value: "ollamacloud", description: "Cloud models (OLLAMA_API_KEY)" },
31494
31759
  { name: "Ollama (local)", value: "ollama", description: "Local Ollama instance" },
31495
31760
  { name: "LM Studio (local)", value: "lmstudio", description: "Local LM Studio instance" },
31496
31761
  { name: "Enter custom model", value: "custom", description: "Type a provider@model specification" }
@@ -31514,6 +31779,8 @@ var init_model_selector = __esm(() => {
31514
31779
  google: "Gemini",
31515
31780
  openai: "OpenAI",
31516
31781
  xai: "xAI",
31782
+ glm: "GLM",
31783
+ ollamacloud: "OllamaCloud",
31517
31784
  zen: "Zen"
31518
31785
  };
31519
31786
  });
@@ -31530,13 +31797,58 @@ __export(exports_profile_commands, {
31530
31797
  profileAddCommand: () => profileAddCommand,
31531
31798
  initCommand: () => initCommand
31532
31799
  });
31533
- async function initCommand() {
31800
+ function parseScopeFlag(args) {
31801
+ const remainingArgs = [];
31802
+ let scope;
31803
+ for (const arg of args) {
31804
+ if (arg === "--local") {
31805
+ scope = "local";
31806
+ } else if (arg === "--global") {
31807
+ scope = "global";
31808
+ } else {
31809
+ remainingArgs.push(arg);
31810
+ }
31811
+ }
31812
+ return { scope, remainingArgs };
31813
+ }
31814
+ async function resolveScope(scopeFlag) {
31815
+ if (scopeFlag)
31816
+ return scopeFlag;
31817
+ const inProject = isProjectDirectory();
31818
+ const defaultScope = inProject ? "local" : "global";
31819
+ return dist_default5({
31820
+ message: "Where should this be saved?",
31821
+ choices: [
31822
+ {
31823
+ name: `Local (.claudish.json in this project)${inProject ? " (recommended)" : ""}`,
31824
+ value: "local"
31825
+ },
31826
+ {
31827
+ name: `Global (~/.claudish/config.json)${!inProject ? " (recommended)" : ""}`,
31828
+ value: "global"
31829
+ }
31830
+ ],
31831
+ default: defaultScope
31832
+ });
31833
+ }
31834
+ function scopeBadge(scope, shadowed) {
31835
+ if (scope === "local") {
31836
+ return `${MAGENTA}[local]${RESET}`;
31837
+ }
31838
+ if (shadowed) {
31839
+ return `${DIM}[global, shadowed]${RESET}`;
31840
+ }
31841
+ return `${DIM}[global]${RESET}`;
31842
+ }
31843
+ async function initCommand(scopeFlag) {
31534
31844
  console.log(`
31535
31845
  ${BOLD}${CYAN}Claudish Setup Wizard${RESET}
31536
31846
  `);
31537
- if (configExists()) {
31847
+ const scope = await resolveScope(scopeFlag);
31848
+ const configPath = getConfigPathForScope(scope);
31849
+ if (configExistsForScope(scope)) {
31538
31850
  const overwrite = await dist_default2({
31539
- message: "Configuration already exists. Do you want to reconfigure?",
31851
+ message: `${scope === "local" ? "Local" : "Global"} configuration already exists. Do you want to reconfigure?`,
31540
31852
  default: false
31541
31853
  });
31542
31854
  if (!overwrite) {
@@ -31551,158 +31863,277 @@ ${BOLD}${CYAN}Claudish Setup Wizard${RESET}
31551
31863
  console.log(`${DIM}These models will be used when Claude Code requests specific model types.${RESET}
31552
31864
  `);
31553
31865
  const models = await selectModelsForProfile();
31554
- const profile = createProfile(profileName, models);
31555
- setDefaultProfile(profileName);
31866
+ const profile = createProfile(profileName, models, undefined, scope);
31867
+ setDefaultProfile(profileName, scope);
31556
31868
  console.log(`
31557
- ${GREEN}✓${RESET} Configuration saved to: ${CYAN}${getConfigPath()}${RESET}`);
31869
+ ${GREEN}✓${RESET} Configuration saved to: ${CYAN}${configPath}${RESET}`);
31558
31870
  console.log(`
31559
31871
  ${BOLD}Profile created:${RESET}`);
31560
- printProfile(profile, true);
31872
+ printProfile(profile, true, false, scope);
31561
31873
  console.log(`
31562
31874
  ${BOLD}Usage:${RESET}`);
31563
31875
  console.log(` ${CYAN}claudish${RESET} # Use default profile`);
31564
31876
  console.log(` ${CYAN}claudish profile add${RESET} # Add another profile`);
31877
+ if (scope === "local") {
31878
+ console.log(`
31879
+ ${DIM}Local config applies only when running from this directory.${RESET}`);
31880
+ }
31565
31881
  console.log("");
31566
31882
  }
31567
- async function profileListCommand() {
31568
- const profiles = listProfiles();
31569
- const config3 = loadConfig();
31883
+ async function profileListCommand(scopeFilter) {
31884
+ const allProfiles = listAllProfiles();
31885
+ const profiles = scopeFilter ? allProfiles.filter((p) => p.scope === scopeFilter) : allProfiles;
31570
31886
  if (profiles.length === 0) {
31571
- console.log("No profiles found. Run 'claudish init' to create one.");
31887
+ if (scopeFilter) {
31888
+ console.log(`No ${scopeFilter} profiles found. Run 'claudish init --${scopeFilter}' to create one.`);
31889
+ } else {
31890
+ console.log("No profiles found. Run 'claudish init' to create one.");
31891
+ }
31572
31892
  return;
31573
31893
  }
31574
31894
  console.log(`
31575
31895
  ${BOLD}Claudish Profiles${RESET}
31576
31896
  `);
31577
- console.log(`${DIM}Config: ${getConfigPath()}${RESET}
31578
- `);
31897
+ console.log(`${DIM}Global: ${getConfigPath()}${RESET}`);
31898
+ if (localConfigExists()) {
31899
+ console.log(`${DIM}Local: ${getLocalConfigPath()}${RESET}`);
31900
+ }
31901
+ console.log("");
31579
31902
  for (const profile of profiles) {
31580
- const isDefault = profile.name === config3.defaultProfile;
31581
- printProfile(profile, isDefault);
31903
+ printProfileWithScope(profile);
31582
31904
  console.log("");
31583
31905
  }
31584
31906
  }
31585
- async function profileAddCommand() {
31907
+ async function profileAddCommand(scopeFlag) {
31586
31908
  console.log(`
31587
31909
  ${BOLD}${CYAN}Add New Profile${RESET}
31588
31910
  `);
31589
- const existingNames = getProfileNames();
31911
+ const scope = await resolveScope(scopeFlag);
31912
+ const existingNames = getProfileNames(scope);
31590
31913
  const name = await promptForProfileName(existingNames);
31591
31914
  const description = await promptForProfileDescription();
31592
31915
  console.log(`
31593
31916
  ${BOLD}Select models for this profile:${RESET}
31594
31917
  `);
31595
31918
  const models = await selectModelsForProfile();
31596
- const profile = createProfile(name, models, description);
31919
+ const profile = createProfile(name, models, description, scope);
31597
31920
  console.log(`
31598
- ${GREEN}✓${RESET} Profile "${name}" created.`);
31599
- printProfile(profile, false);
31921
+ ${GREEN}✓${RESET} Profile "${name}" created ${scopeBadge(scope)}.`);
31922
+ printProfile(profile, false, false, scope);
31600
31923
  const setAsDefault = await dist_default2({
31601
- message: "Set this profile as default?",
31924
+ message: `Set this profile as default in ${scope} config?`,
31602
31925
  default: false
31603
31926
  });
31604
31927
  if (setAsDefault) {
31605
- setDefaultProfile(name);
31606
- console.log(`${GREEN}✓${RESET} "${name}" is now the default profile.`);
31928
+ setDefaultProfile(name, scope);
31929
+ console.log(`${GREEN}✓${RESET} "${name}" is now the default ${scope} profile.`);
31607
31930
  }
31608
31931
  }
31609
- async function profileRemoveCommand(name) {
31610
- const profiles = getProfileNames();
31611
- if (profiles.length === 0) {
31612
- console.log("No profiles to remove.");
31613
- return;
31614
- }
31615
- if (profiles.length === 1) {
31616
- console.log("Cannot remove the last profile. Create another one first.");
31617
- return;
31618
- }
31932
+ async function profileRemoveCommand(name, scopeFlag) {
31933
+ let scope = scopeFlag;
31619
31934
  let profileName = name;
31620
31935
  if (!profileName) {
31621
- const profileList = listProfiles();
31622
- profileName = await selectProfile(profileList.map((p) => ({
31623
- name: p.name,
31624
- description: p.description,
31625
- isDefault: p.name === loadConfig().defaultProfile
31626
- })));
31936
+ const allProfiles = listAllProfiles();
31937
+ const selectable = scope ? allProfiles.filter((p) => p.scope === scope) : allProfiles;
31938
+ if (selectable.length === 0) {
31939
+ console.log("No profiles to remove.");
31940
+ return;
31941
+ }
31942
+ const choice = await dist_default5({
31943
+ message: "Select a profile to remove:",
31944
+ choices: selectable.map((p) => ({
31945
+ name: `${p.name} ${scopeBadge(p.scope)}${p.isDefault ? ` ${YELLOW}(default)${RESET}` : ""}`,
31946
+ value: `${p.scope}:${p.name}`
31947
+ }))
31948
+ });
31949
+ const [chosenScope, ...nameParts] = choice.split(":");
31950
+ scope = chosenScope;
31951
+ profileName = nameParts.join(":");
31952
+ } else if (!scope) {
31953
+ const localConfig = loadLocalConfig();
31954
+ const globalConfig2 = loadConfig();
31955
+ const inLocal = localConfig?.profiles[profileName] !== undefined;
31956
+ const inGlobal = globalConfig2.profiles[profileName] !== undefined;
31957
+ if (inLocal && inGlobal) {
31958
+ scope = await dist_default5({
31959
+ message: `Profile "${profileName}" exists in both local and global. Which one to remove?`,
31960
+ choices: [
31961
+ { name: "Local", value: "local" },
31962
+ { name: "Global", value: "global" }
31963
+ ]
31964
+ });
31965
+ } else if (inLocal) {
31966
+ scope = "local";
31967
+ } else if (inGlobal) {
31968
+ scope = "global";
31969
+ } else {
31970
+ console.log(`Profile "${profileName}" not found.`);
31971
+ return;
31972
+ }
31627
31973
  }
31628
- const profile = getProfile(profileName);
31974
+ if (scope === "global") {
31975
+ const globalNames = getProfileNames("global");
31976
+ if (globalNames.length <= 1 && globalNames.includes(profileName)) {
31977
+ console.log("Cannot remove the last global profile. Create another one first.");
31978
+ return;
31979
+ }
31980
+ }
31981
+ const profile = getProfile(profileName, scope);
31629
31982
  if (!profile) {
31630
- console.log(`Profile "${profileName}" not found.`);
31983
+ console.log(`Profile "${profileName}" not found in ${scope} config.`);
31631
31984
  return;
31632
31985
  }
31633
- const confirmed = await confirmAction(`Are you sure you want to delete profile "${profileName}"?`);
31986
+ const confirmed = await confirmAction(`Are you sure you want to delete profile "${profileName}" from ${scope} config?`);
31634
31987
  if (!confirmed) {
31635
31988
  console.log("Cancelled.");
31636
31989
  return;
31637
31990
  }
31638
31991
  try {
31639
- deleteProfile(profileName);
31640
- console.log(`${GREEN}✓${RESET} Profile "${profileName}" deleted.`);
31992
+ deleteProfile(profileName, scope);
31993
+ console.log(`${GREEN}✓${RESET} Profile "${profileName}" deleted from ${scope} config.`);
31641
31994
  } catch (error46) {
31642
31995
  console.error(`Error: ${error46}`);
31643
31996
  }
31644
31997
  }
31645
- async function profileUseCommand(name) {
31646
- const profiles = getProfileNames();
31647
- if (profiles.length === 0) {
31648
- console.log("No profiles found. Run 'claudish init' to create one.");
31649
- return;
31650
- }
31998
+ async function profileUseCommand(name, scopeFlag) {
31999
+ let scope = scopeFlag;
31651
32000
  let profileName = name;
31652
32001
  if (!profileName) {
31653
- const profileList = listProfiles();
31654
- profileName = await selectProfile(profileList.map((p) => ({
31655
- name: p.name,
31656
- description: p.description,
31657
- isDefault: p.name === loadConfig().defaultProfile
31658
- })));
32002
+ const allProfiles = listAllProfiles();
32003
+ const selectable = scope ? allProfiles.filter((p) => p.scope === scope) : allProfiles;
32004
+ if (selectable.length === 0) {
32005
+ console.log("No profiles found. Run 'claudish init' to create one.");
32006
+ return;
32007
+ }
32008
+ const choice = await dist_default5({
32009
+ message: "Select a profile to set as default:",
32010
+ choices: selectable.map((p) => ({
32011
+ name: `${p.name} ${scopeBadge(p.scope)}${p.isDefault ? ` ${YELLOW}(default)${RESET}` : ""}`,
32012
+ value: `${p.scope}:${p.name}`
32013
+ }))
32014
+ });
32015
+ const [chosenScope, ...nameParts] = choice.split(":");
32016
+ scope = chosenScope;
32017
+ profileName = nameParts.join(":");
32018
+ }
32019
+ if (!scope) {
32020
+ const localConfig = loadLocalConfig();
32021
+ const globalConfig2 = loadConfig();
32022
+ const inLocal = localConfig?.profiles[profileName] !== undefined;
32023
+ const inGlobal = globalConfig2.profiles[profileName] !== undefined;
32024
+ if (inLocal && inGlobal) {
32025
+ scope = await dist_default5({
32026
+ message: `Profile "${profileName}" exists in both configs. Set as default in which?`,
32027
+ choices: [
32028
+ { name: "Local", value: "local" },
32029
+ { name: "Global", value: "global" }
32030
+ ]
32031
+ });
32032
+ } else if (inLocal) {
32033
+ scope = "local";
32034
+ } else if (inGlobal) {
32035
+ scope = "global";
32036
+ } else {
32037
+ console.log(`Profile "${profileName}" not found.`);
32038
+ return;
32039
+ }
31659
32040
  }
31660
- const profile = getProfile(profileName);
32041
+ const profile = getProfile(profileName, scope);
31661
32042
  if (!profile) {
31662
- console.log(`Profile "${profileName}" not found.`);
32043
+ console.log(`Profile "${profileName}" not found in ${scope} config.`);
31663
32044
  return;
31664
32045
  }
31665
- setDefaultProfile(profileName);
31666
- console.log(`${GREEN}✓${RESET} "${profileName}" is now the default profile.`);
32046
+ setDefaultProfile(profileName, scope);
32047
+ console.log(`${GREEN}✓${RESET} "${profileName}" is now the default ${scope} profile.`);
31667
32048
  }
31668
- async function profileShowCommand(name) {
32049
+ async function profileShowCommand(name, scopeFlag) {
31669
32050
  let profileName = name;
32051
+ let scope = scopeFlag;
31670
32052
  if (!profileName) {
31671
- const config4 = loadConfig();
31672
- profileName = config4.defaultProfile;
32053
+ const defaultProfile = scope ? getDefaultProfile(scope) : getDefaultProfile();
32054
+ profileName = defaultProfile.name;
32055
+ if (!scope) {
32056
+ const localConfig = loadLocalConfig();
32057
+ if (localConfig?.profiles[profileName]) {
32058
+ scope = "local";
32059
+ } else {
32060
+ scope = "global";
32061
+ }
32062
+ }
32063
+ }
32064
+ if (!scope) {
32065
+ const localConfig = loadLocalConfig();
32066
+ if (localConfig?.profiles[profileName]) {
32067
+ scope = "local";
32068
+ } else {
32069
+ scope = "global";
32070
+ }
31673
32071
  }
31674
- const profile = getProfile(profileName);
32072
+ const profile = getProfile(profileName, scope);
31675
32073
  if (!profile) {
31676
32074
  console.log(`Profile "${profileName}" not found.`);
31677
32075
  return;
31678
32076
  }
31679
- const config3 = loadConfig();
31680
- const isDefault = profileName === config3.defaultProfile;
32077
+ let isDefault = false;
32078
+ if (scope === "local") {
32079
+ const localConfig = loadLocalConfig();
32080
+ isDefault = localConfig?.defaultProfile === profileName;
32081
+ } else {
32082
+ const config3 = loadConfig();
32083
+ isDefault = config3.defaultProfile === profileName;
32084
+ }
31681
32085
  console.log("");
31682
- printProfile(profile, isDefault, true);
32086
+ printProfile(profile, isDefault, true, scope);
31683
32087
  }
31684
- async function profileEditCommand(name) {
31685
- const profiles = getProfileNames();
31686
- if (profiles.length === 0) {
31687
- console.log("No profiles found. Run 'claudish init' to create one.");
31688
- return;
31689
- }
32088
+ async function profileEditCommand(name, scopeFlag) {
32089
+ let scope = scopeFlag;
31690
32090
  let profileName = name;
31691
32091
  if (!profileName) {
31692
- const profileList = listProfiles();
31693
- profileName = await selectProfile(profileList.map((p) => ({
31694
- name: p.name,
31695
- description: p.description,
31696
- isDefault: p.name === loadConfig().defaultProfile
31697
- })));
32092
+ const allProfiles = listAllProfiles();
32093
+ const selectable = scope ? allProfiles.filter((p) => p.scope === scope) : allProfiles;
32094
+ if (selectable.length === 0) {
32095
+ console.log("No profiles found. Run 'claudish init' to create one.");
32096
+ return;
32097
+ }
32098
+ const choice = await dist_default5({
32099
+ message: "Select a profile to edit:",
32100
+ choices: selectable.map((p) => ({
32101
+ name: `${p.name} ${scopeBadge(p.scope)}${p.isDefault ? ` ${YELLOW}(default)${RESET}` : ""}`,
32102
+ value: `${p.scope}:${p.name}`
32103
+ }))
32104
+ });
32105
+ const [chosenScope, ...nameParts] = choice.split(":");
32106
+ scope = chosenScope;
32107
+ profileName = nameParts.join(":");
32108
+ } else if (!scope) {
32109
+ const localConfig = loadLocalConfig();
32110
+ const globalConfig2 = loadConfig();
32111
+ const inLocal = localConfig?.profiles[profileName] !== undefined;
32112
+ const inGlobal = globalConfig2.profiles[profileName] !== undefined;
32113
+ if (inLocal && inGlobal) {
32114
+ scope = await dist_default5({
32115
+ message: `Profile "${profileName}" exists in both configs. Which one to edit?`,
32116
+ choices: [
32117
+ { name: "Local", value: "local" },
32118
+ { name: "Global", value: "global" }
32119
+ ]
32120
+ });
32121
+ } else if (inLocal) {
32122
+ scope = "local";
32123
+ } else if (inGlobal) {
32124
+ scope = "global";
32125
+ } else {
32126
+ console.log(`Profile "${profileName}" not found.`);
32127
+ return;
32128
+ }
31698
32129
  }
31699
- const profile = getProfile(profileName);
32130
+ const profile = getProfile(profileName, scope);
31700
32131
  if (!profile) {
31701
- console.log(`Profile "${profileName}" not found.`);
32132
+ console.log(`Profile "${profileName}" not found in ${scope} config.`);
31702
32133
  return;
31703
32134
  }
31704
32135
  console.log(`
31705
- ${BOLD}Editing profile: ${profileName}${RESET}
32136
+ ${BOLD}Editing profile: ${profileName}${RESET} ${scopeBadge(scope)}
31706
32137
  `);
31707
32138
  console.log(`${DIM}Current models:${RESET}`);
31708
32139
  printModelMapping(profile.models);
@@ -31725,14 +32156,14 @@ ${BOLD}Editing profile: ${profileName}${RESET}
31725
32156
  if (whatToEdit === "description") {
31726
32157
  const newDescription = await promptForProfileDescription();
31727
32158
  profile.description = newDescription;
31728
- setProfile(profile);
32159
+ setProfile(profile, scope);
31729
32160
  console.log(`${GREEN}✓${RESET} Description updated.`);
31730
32161
  return;
31731
32162
  }
31732
32163
  if (whatToEdit === "all") {
31733
32164
  const models = await selectModelsForProfile();
31734
32165
  profile.models = { ...profile.models, ...models };
31735
- setProfile(profile);
32166
+ setProfile(profile, scope);
31736
32167
  console.log(`${GREEN}✓${RESET} All models updated.`);
31737
32168
  return;
31738
32169
  }
@@ -31742,12 +32173,13 @@ ${BOLD}Editing profile: ${profileName}${RESET}
31742
32173
  message: `Select new model for ${tierName}:`
31743
32174
  });
31744
32175
  profile.models[tier] = newModel;
31745
- setProfile(profile);
32176
+ setProfile(profile, scope);
31746
32177
  console.log(`${GREEN}✓${RESET} ${tierName} model updated to: ${newModel}`);
31747
32178
  }
31748
- function printProfile(profile, isDefault, verbose = false) {
32179
+ function printProfile(profile, isDefault, verbose = false, scope) {
31749
32180
  const defaultBadge = isDefault ? ` ${YELLOW}(default)${RESET}` : "";
31750
- console.log(`${BOLD}${profile.name}${RESET}${defaultBadge}`);
32181
+ const scopeTag = scope ? ` ${scopeBadge(scope)}` : "";
32182
+ console.log(`${BOLD}${profile.name}${RESET}${defaultBadge}${scopeTag}`);
31751
32183
  if (profile.description) {
31752
32184
  console.log(` ${DIM}${profile.description}${RESET}`);
31753
32185
  }
@@ -31757,6 +32189,18 @@ function printProfile(profile, isDefault, verbose = false) {
31757
32189
  console.log(` ${DIM}Updated: ${profile.updatedAt}${RESET}`);
31758
32190
  }
31759
32191
  }
32192
+ function printProfileWithScope(profile) {
32193
+ const defaultBadge = profile.isDefault ? ` ${YELLOW}(default)${RESET}` : "";
32194
+ const badge = scopeBadge(profile.scope, profile.shadowed);
32195
+ console.log(`${BOLD}${profile.name}${RESET}${defaultBadge} ${badge}`);
32196
+ if (profile.shadowed) {
32197
+ console.log(` ${DIM}(overridden by local profile of same name)${RESET}`);
32198
+ }
32199
+ if (profile.description) {
32200
+ console.log(` ${DIM}${profile.description}${RESET}`);
32201
+ }
32202
+ printModelMapping(profile.models);
32203
+ }
31760
32204
  function printModelMapping(models) {
31761
32205
  console.log(` ${CYAN}opus${RESET}: ${models.opus || DIM + "not set" + RESET}`);
31762
32206
  console.log(` ${CYAN}sonnet${RESET}: ${models.sonnet || DIM + "not set" + RESET}`);
@@ -31766,34 +32210,35 @@ function printModelMapping(models) {
31766
32210
  }
31767
32211
  }
31768
32212
  async function profileCommand(args) {
31769
- const subcommand = args[0];
31770
- const name = args[1];
32213
+ const { scope, remainingArgs } = parseScopeFlag(args);
32214
+ const subcommand = remainingArgs[0];
32215
+ const name = remainingArgs[1];
31771
32216
  switch (subcommand) {
31772
32217
  case "list":
31773
32218
  case "ls":
31774
- await profileListCommand();
32219
+ await profileListCommand(scope);
31775
32220
  break;
31776
32221
  case "add":
31777
32222
  case "new":
31778
32223
  case "create":
31779
- await profileAddCommand();
32224
+ await profileAddCommand(scope);
31780
32225
  break;
31781
32226
  case "remove":
31782
32227
  case "rm":
31783
32228
  case "delete":
31784
- await profileRemoveCommand(name);
32229
+ await profileRemoveCommand(name, scope);
31785
32230
  break;
31786
32231
  case "use":
31787
32232
  case "default":
31788
32233
  case "set":
31789
- await profileUseCommand(name);
32234
+ await profileUseCommand(name, scope);
31790
32235
  break;
31791
32236
  case "show":
31792
32237
  case "view":
31793
- await profileShowCommand(name);
32238
+ await profileShowCommand(name, scope);
31794
32239
  break;
31795
32240
  case "edit":
31796
- await profileEditCommand(name);
32241
+ await profileEditCommand(name, scope);
31797
32242
  break;
31798
32243
  default:
31799
32244
  printProfileHelp();
@@ -31811,14 +32256,22 @@ ${BOLD}Commands:${RESET}
31811
32256
  ${CYAN}show${RESET} ${DIM}[name]${RESET} Show profile details
31812
32257
  ${CYAN}edit${RESET} ${DIM}[name]${RESET} Edit a profile
31813
32258
 
32259
+ ${BOLD}Scope Flags:${RESET}
32260
+ ${CYAN}--local${RESET} Target .claudish.json in the current directory
32261
+ ${CYAN}--global${RESET} Target ~/.claudish/config.json (default)
32262
+ ${DIM}If neither flag is given, you'll be prompted interactively.${RESET}
32263
+
31814
32264
  ${BOLD}Examples:${RESET}
31815
32265
  claudish profile list
31816
- claudish profile add
31817
- claudish profile use frontend
31818
- claudish profile remove debug
32266
+ claudish profile list --local
32267
+ claudish profile add --local
32268
+ claudish profile add --global
32269
+ claudish profile use frontend --local
32270
+ claudish profile remove debug --global
32271
+ claudish init --local
31819
32272
  `);
31820
32273
  }
31821
- var RESET = "\x1B[0m", BOLD = "\x1B[1m", DIM = "\x1B[2m", GREEN = "\x1B[32m", YELLOW = "\x1B[33m", CYAN = "\x1B[36m";
32274
+ var RESET = "\x1B[0m", BOLD = "\x1B[1m", DIM = "\x1B[2m", GREEN = "\x1B[32m", YELLOW = "\x1B[33m", CYAN = "\x1B[36m", MAGENTA = "\x1B[35m";
31822
32275
  var init_profile_commands = __esm(() => {
31823
32276
  init_profile_config();
31824
32277
  init_model_selector();
@@ -32007,6 +32460,15 @@ function fuzzyScore2(text, query) {
32007
32460
  return 0.8;
32008
32461
  if (t.includes(q))
32009
32462
  return 0.6;
32463
+ const normSep = (s) => s.replace(/[\s\-_.]/g, "");
32464
+ const tn = normSep(t);
32465
+ const qn = normSep(q);
32466
+ if (tn === qn)
32467
+ return 0.95;
32468
+ if (tn.startsWith(qn))
32469
+ return 0.85;
32470
+ if (tn.includes(qn))
32471
+ return 0.65;
32010
32472
  let score = 0;
32011
32473
  let tIdx = 0;
32012
32474
  let qIdx = 0;
@@ -32139,6 +32601,7 @@ var init_model_parser = __esm(() => {
32139
32601
  kc: "kimi-coding",
32140
32602
  glm: "glm",
32141
32603
  zhipu: "glm",
32604
+ gc: "glm-coding",
32142
32605
  zai: "zai",
32143
32606
  oc: "ollamacloud",
32144
32607
  zen: "opencode-zen",
@@ -32163,6 +32626,7 @@ var init_model_parser = __esm(() => {
32163
32626
  "kimi",
32164
32627
  "kimi-coding",
32165
32628
  "glm",
32629
+ "glm-coding",
32166
32630
  "zai",
32167
32631
  "ollamacloud",
32168
32632
  "opencode-zen",
@@ -32211,6 +32675,7 @@ var init_model_parser = __esm(() => {
32211
32675
  { prefix: "kc/", provider: "kimi-coding", stripPrefix: true },
32212
32676
  { prefix: "glm/", provider: "glm", stripPrefix: true },
32213
32677
  { prefix: "zhipu/", provider: "glm", stripPrefix: true },
32678
+ { prefix: "gc/", provider: "glm-coding", stripPrefix: true },
32214
32679
  { prefix: "zai/", provider: "zai", stripPrefix: true },
32215
32680
  { prefix: "oc/", provider: "ollamacloud", stripPrefix: true },
32216
32681
  { prefix: "zen/", provider: "opencode-zen", stripPrefix: true },
@@ -32383,6 +32848,7 @@ function resolveRemoteProvider(modelId) {
32383
32848
  kimi: "kimi",
32384
32849
  "kimi-coding": "kimi-coding",
32385
32850
  glm: "glm",
32851
+ "glm-coding": "glm-coding",
32386
32852
  zai: "zai",
32387
32853
  ollamacloud: "ollamacloud",
32388
32854
  "opencode-zen": "opencode-zen",
@@ -32530,6 +32996,20 @@ var getRemoteProviders = () => [
32530
32996
  supportsReasoning: true
32531
32997
  }
32532
32998
  },
32999
+ {
33000
+ name: "glm-coding",
33001
+ baseUrl: "https://api.z.ai",
33002
+ apiPath: "/api/coding/paas/v4/chat/completions",
33003
+ apiKeyEnvVar: "GLM_CODING_API_KEY",
33004
+ prefixes: ["gc/"],
33005
+ capabilities: {
33006
+ supportsTools: true,
33007
+ supportsVision: true,
33008
+ supportsStreaming: true,
33009
+ supportsJsonMode: true,
33010
+ supportsReasoning: true
33011
+ }
33012
+ },
32533
33013
  {
32534
33014
  name: "zai",
32535
33015
  baseUrl: process.env.ZAI_BASE_URL || "https://api.z.ai",
@@ -32943,6 +33423,12 @@ var init_provider_resolver = __esm(() => {
32943
33423
  url: "https://open.bigmodel.cn/",
32944
33424
  aliases: ["GLM_API_KEY"]
32945
33425
  },
33426
+ "glm-coding": {
33427
+ envVar: "GLM_CODING_API_KEY",
33428
+ description: "GLM Coding Plan API Key",
33429
+ url: "https://z.ai/subscribe",
33430
+ aliases: ["ZAI_CODING_API_KEY"]
33431
+ },
32946
33432
  ollamacloud: {
32947
33433
  envVar: "OLLAMA_API_KEY",
32948
33434
  description: "OllamaCloud API Key",
@@ -32969,6 +33455,7 @@ var init_provider_resolver = __esm(() => {
32969
33455
  kimi: "Kimi",
32970
33456
  "kimi-coding": "Kimi Coding",
32971
33457
  glm: "GLM",
33458
+ "glm-coding": "GLM Coding",
32972
33459
  zai: "Z.AI",
32973
33460
  ollamacloud: "OllamaCloud",
32974
33461
  "opencode-zen": "OpenCode Zen"
@@ -33322,6 +33809,15 @@ async function searchAndPrintModels(query, forceUpdate) {
33322
33809
  }
33323
33810
  } catch {}
33324
33811
  }
33812
+ if (process.env.GLM_CODING_API_KEY) {
33813
+ try {
33814
+ const glmCodingModels = await fetchGLMCodingModels2();
33815
+ if (glmCodingModels.length > 0) {
33816
+ console.error(`\uD83D\uDD11 Found ${glmCodingModels.length} GLM Coding Plan models`);
33817
+ models = [...glmCodingModels, ...models];
33818
+ }
33819
+ } catch {}
33820
+ }
33325
33821
  const results = models.map((model) => {
33326
33822
  const nameScore = fuzzyScore2(model.name || "", query);
33327
33823
  const idScore = fuzzyScore2(model.id || "", query);
@@ -33796,13 +34292,18 @@ OPTIONS:
33796
34292
  --init Install Claudish skill in current project (.claude/skills/)
33797
34293
 
33798
34294
  PROFILE MANAGEMENT:
33799
- claudish init Setup wizard - create config and first profile
33800
- claudish profile list List all profiles
33801
- claudish profile add Add a new profile
33802
- claudish profile remove Remove a profile (interactive or claudish profile remove <name>)
33803
- claudish profile use Set default profile (interactive or claudish profile use <name>)
33804
- claudish profile show Show profile details (default profile or claudish profile show <name>)
33805
- claudish profile edit Edit a profile (interactive or claudish profile edit <name>)
34295
+ claudish init [--local|--global] Setup wizard - create config and first profile
34296
+ claudish profile list [--local|--global] List all profiles (both scopes by default)
34297
+ claudish profile add [--local|--global] Add a new profile
34298
+ claudish profile remove [name] [--local|--global] Remove a profile
34299
+ claudish profile use [name] [--local|--global] Set default profile
34300
+ claudish profile show [name] [--local|--global] Show profile details
34301
+ claudish profile edit [name] [--local|--global] Edit a profile
34302
+
34303
+ Scope flags:
34304
+ --local Target .claudish.json in the current directory (project-specific)
34305
+ --global Target ~/.claudish/config.json (shared across projects)
34306
+ (omit) Prompted interactively; suggests local if in a project directory
33806
34307
 
33807
34308
  UPDATE:
33808
34309
  claudish update Check for updates and install latest version
@@ -34209,7 +34710,38 @@ async function fetchZenModels() {
34209
34710
  return [];
34210
34711
  }
34211
34712
  }
34212
- var __filename5, __dirname5, VERSION = "4.5.1", CACHE_MAX_AGE_DAYS3 = 2, MODELS_JSON_PATH, CLAUDISH_CACHE_DIR3, ALL_MODELS_JSON_PATH2;
34713
+ async function fetchGLMCodingModels2() {
34714
+ try {
34715
+ const response = await fetch("https://models.dev/api.json", {
34716
+ signal: AbortSignal.timeout(5000)
34717
+ });
34718
+ if (!response.ok) {
34719
+ return [];
34720
+ }
34721
+ const data = await response.json();
34722
+ const codingPlan = data["zai-coding-plan"];
34723
+ if (!codingPlan?.models)
34724
+ return [];
34725
+ return Object.entries(codingPlan.models).map(([id, m]) => {
34726
+ const inputModalities = m.modalities?.input || [];
34727
+ return {
34728
+ id: `gc/${id}`,
34729
+ name: m.name || id,
34730
+ description: `GLM Coding Plan model (subscription)`,
34731
+ context_length: m.limit?.context || 131072,
34732
+ pricing: { prompt: "0", completion: "0" },
34733
+ isGLMCoding: true,
34734
+ isSubscription: true,
34735
+ supportsTools: m.tool_call || false,
34736
+ supportsReasoning: m.reasoning || false,
34737
+ supportsVision: inputModalities.includes("image") || inputModalities.includes("video")
34738
+ };
34739
+ });
34740
+ } catch {
34741
+ return [];
34742
+ }
34743
+ }
34744
+ var __filename5, __dirname5, VERSION = "4.5.3", CACHE_MAX_AGE_DAYS3 = 2, MODELS_JSON_PATH, CLAUDISH_CACHE_DIR3, ALL_MODELS_JSON_PATH2;
34213
34745
  var init_cli = __esm(() => {
34214
34746
  init_config();
34215
34747
  init_model_loader();
@@ -34343,12 +34875,12 @@ function createTempSettingsFile(modelDisplay, port) {
34343
34875
  const CYAN2 = "\\033[96m";
34344
34876
  const YELLOW2 = "\\033[93m";
34345
34877
  const GREEN2 = "\\033[92m";
34346
- const MAGENTA = "\\033[95m";
34878
+ const MAGENTA2 = "\\033[95m";
34347
34879
  const DIM2 = "\\033[2m";
34348
34880
  const RESET2 = "\\033[0m";
34349
34881
  const BOLD2 = "\\033[1m";
34350
34882
  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; }`;
34351
- 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="" && TOKEN_MODEL="" && 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) && TOKEN_MODEL=$(echo "$TOKENS" | grep -o '"model_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="\${TOKEN_MODEL:-$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"`;
34883
+ 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="" && TOKEN_MODEL="" && 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) && TOKEN_MODEL=$(echo "$TOKENS" | grep -o '"model_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="\${TOKEN_MODEL:-$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} ${MAGENTA2}%s${RESET2}\\n" "$DIR" "$MODEL_DISPLAY" "$COST_DISPLAY" "$CTX_DISPLAY"`;
34352
34884
  }
34353
34885
  const settings = {
34354
34886
  statusLine: {
@@ -38952,6 +39484,9 @@ function getModelPricing(provider, modelName) {
38952
39484
  if (FREE_PROVIDERS.has(p)) {
38953
39485
  return { inputCostPer1M: 0, outputCostPer1M: 0, isFree: true };
38954
39486
  }
39487
+ if (SUBSCRIPTION_PROVIDERS.has(p)) {
39488
+ return { inputCostPer1M: 0, outputCostPer1M: 0, isSubscription: true };
39489
+ }
38955
39490
  if (_dynamicLookup) {
38956
39491
  const dynamic = _dynamicLookup(p, modelName);
38957
39492
  if (dynamic)
@@ -38966,7 +39501,7 @@ function calculateCost(provider, modelName, inputTokens, outputTokens) {
38966
39501
  const outputCost = outputTokens / 1e6 * pricing.outputCostPer1M;
38967
39502
  return inputCost + outputCost;
38968
39503
  }
38969
- var PROVIDER_DEFAULTS, FREE_PROVIDERS, PROVIDER_ALIAS, _dynamicLookup = null;
39504
+ var PROVIDER_DEFAULTS, FREE_PROVIDERS, SUBSCRIPTION_PROVIDERS, PROVIDER_ALIAS, _dynamicLookup = null;
38970
39505
  var init_remote_provider_types = __esm(() => {
38971
39506
  PROVIDER_DEFAULTS = {
38972
39507
  gemini: { inputCostPer1M: 0.5, outputCostPer1M: 2, isEstimate: true },
@@ -38977,12 +39512,14 @@ var init_remote_provider_types = __esm(() => {
38977
39512
  ollamacloud: { inputCostPer1M: 1, outputCostPer1M: 4, isEstimate: true }
38978
39513
  };
38979
39514
  FREE_PROVIDERS = new Set(["opencode-zen", "zen"]);
39515
+ SUBSCRIPTION_PROVIDERS = new Set(["kimi-coding", "glm-coding"]);
38980
39516
  PROVIDER_ALIAS = {
38981
39517
  google: "gemini",
38982
39518
  oai: "openai",
38983
39519
  mm: "minimax",
38984
39520
  moonshot: "kimi",
38985
39521
  zhipu: "glm",
39522
+ "glm-coding": "glm",
38986
39523
  oc: "ollamacloud"
38987
39524
  };
38988
39525
  });
@@ -61924,6 +62461,26 @@ class OpenAIHandler {
61924
62461
  this.contextWindow = 262144;
61925
62462
  } else if (model.includes("kimi")) {
61926
62463
  this.contextWindow = 131072;
62464
+ } else if (model.includes("glm-5")) {
62465
+ this.contextWindow = 204800;
62466
+ } else if (model.includes("glm-4.7-flash")) {
62467
+ this.contextWindow = 200000;
62468
+ } else if (model.includes("glm-4.7")) {
62469
+ this.contextWindow = 204800;
62470
+ } else if (model.includes("glm-4.6v")) {
62471
+ this.contextWindow = 128000;
62472
+ } else if (model.includes("glm-4.6")) {
62473
+ this.contextWindow = 204800;
62474
+ } else if (model.includes("glm-4.5v")) {
62475
+ this.contextWindow = 64000;
62476
+ } else if (model.includes("glm-4.5-flash")) {
62477
+ this.contextWindow = 131072;
62478
+ } else if (model.includes("glm-4.5-air")) {
62479
+ this.contextWindow = 131072;
62480
+ } else if (model.includes("glm-4.5")) {
62481
+ this.contextWindow = 131072;
62482
+ } else if (model.includes("glm-")) {
62483
+ this.contextWindow = 131072;
61927
62484
  } else if (model.includes("gpt-4o") || model.includes("gpt-4-turbo")) {
61928
62485
  this.contextWindow = 128000;
61929
62486
  } else if (model.includes("gpt-5")) {
@@ -64917,7 +65474,7 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
64917
65474
  } else if (resolved.provider.name === "minimax" || resolved.provider.name === "kimi" || resolved.provider.name === "kimi-coding" || resolved.provider.name === "zai") {
64918
65475
  handler = new AnthropicCompatHandler(resolved.provider, resolved.modelName, apiKey, port);
64919
65476
  log(`[Proxy] Created ${resolved.provider.name} handler: ${resolved.modelName}`);
64920
- } else if (resolved.provider.name === "glm") {
65477
+ } else if (resolved.provider.name === "glm" || resolved.provider.name === "glm-coding") {
64921
65478
  handler = new OpenAIHandler(resolved.provider, resolved.modelName, apiKey, port);
64922
65479
  log(`[Proxy] Created ${resolved.provider.name} handler: ${resolved.modelName}`);
64923
65480
  } else if (resolved.provider.name === "opencode-zen") {
@@ -65336,7 +65893,8 @@ if (isMcpMode) {
65336
65893
  } else if (isUpdateCommand) {
65337
65894
  runSelfUpdate();
65338
65895
  } else if (isInitCommand) {
65339
- Promise.resolve().then(() => (init_profile_commands(), exports_profile_commands)).then((pc) => pc.initCommand().catch(handlePromptExit));
65896
+ const scopeFlag = args.includes("--local") ? "local" : args.includes("--global") ? "global" : undefined;
65897
+ Promise.resolve().then(() => (init_profile_commands(), exports_profile_commands)).then((pc) => pc.initCommand(scopeFlag).catch(handlePromptExit));
65340
65898
  } else if (isProfileCommand) {
65341
65899
  const profileArgIndex = args.findIndex((a) => a === "profile");
65342
65900
  Promise.resolve().then(() => (init_profile_commands(), exports_profile_commands)).then((pc) => pc.profileCommand(args.slice(profileArgIndex + 1)).catch(handlePromptExit));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "4.5.1",
3
+ "version": "4.5.3",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "1.1.5",
3
- "lastUpdated": "2026-02-09",
2
+ "version": "1.2.0",
3
+ "lastUpdated": "2026-02-12",
4
4
  "source": "https://openrouter.ai/models?categories=programming&fmt=cards&order=top-weekly",
5
5
  "models": [
6
6
  {
@@ -47,8 +47,8 @@
47
47
  },
48
48
  {
49
49
  "id": "z-ai/glm-4.7",
50
- "name": "Z.AI: GLM 4.7",
51
- "description": "GLM-4.7 is Z.AI’s latest flagship model, featuring upgrades in two key areas: enhanced programming capabilities and more stable multi-step reasoning/execution. It demonstrates significant improvements in executing complex agent tasks while delivering more natural conversational experiences and superior front-end aesthetics.",
50
+ "name": "Z.ai: GLM 4.7",
51
+ "description": "GLM-4.7 is Z.ai’s latest flagship model, featuring upgrades in two key areas: enhanced programming capabilities and more stable multi-step reasoning/execution. It demonstrates significant improvements in executing complex agent tasks while delivering more natural conversational experiences and superior front-end aesthetics.",
52
52
  "provider": "Z-ai",
53
53
  "category": "reasoning",
54
54
  "priority": 3,