claudish 9.0.8 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +340 -191
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -715,7 +715,7 @@ var init_onepassword_config = __esm(() => {
715
715
  });
716
716
 
717
717
  // src/version.ts
718
- var VERSION = "9.0.8";
718
+ var VERSION = "9.1.0";
719
719
 
720
720
  // src/logger.ts
721
721
  import { appendFile, existsSync as existsSync2, mkdirSync, readdirSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
@@ -22953,7 +22953,7 @@ function defaultReadStore() {
22953
22953
  return cachedRawStore.value;
22954
22954
  let value = null;
22955
22955
  try {
22956
- const out = execFileSync("security", ["find-generic-password", "-s", KC_SERVICE, "-a", KC_ACCOUNT, "-w"], { encoding: "utf8" });
22956
+ const out = execFileSync("security", ["find-generic-password", "-s", KC_SERVICE, "-a", KC_ACCOUNT, "-w"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
22957
22957
  const trimmed = out.trim();
22958
22958
  value = trimmed.length > 0 ? trimmed : null;
22959
22959
  } catch {
@@ -56013,25 +56013,214 @@ var init_api_key_map = __esm(() => {
56013
56013
  };
56014
56014
  });
56015
56015
 
56016
+ // src/branding.ts
56017
+ function paint(line) {
56018
+ const { RESET, BOLD, CYAN, BLUE, DIM } = cliAnsi();
56019
+ if (!CYAN && !BLUE)
56020
+ return line;
56021
+ return line.replace(/\u2588+/g, (run) => `${BOLD}${CYAN}${run}${RESET}`).replace(/[\u2557\u255D\u2551\u2550\u2554\u255A]+/g, (run) => `${DIM}${BLUE}${run}${RESET}`);
56022
+ }
56023
+ function fitsWordmark(stream) {
56024
+ if (process.env.TERM === "dumb")
56025
+ return false;
56026
+ const columns = stream.columns ?? 0;
56027
+ return columns >= MIN_LOGO_WIDTH;
56028
+ }
56029
+ function wordmarkLines() {
56030
+ return WORDMARK.map((line) => ` ${paint(line)}`);
56031
+ }
56032
+ function compactLogo() {
56033
+ const { RESET, BOLD, CYAN, DIM } = cliAnsi();
56034
+ return ` ${BOLD}${CYAN}claudish${RESET} ${DIM}\xB7${RESET} ${TAGLINE}`;
56035
+ }
56036
+ function printLogo(stream, options = {}) {
56037
+ const { version, trailingBlankLine = true } = options;
56038
+ const { RESET, BOLD, DIM, GRAY } = cliAnsi();
56039
+ stream.write(`
56040
+ `);
56041
+ if (fitsWordmark(stream)) {
56042
+ for (const line of wordmarkLines())
56043
+ stream.write(`${line}
56044
+ `);
56045
+ stream.write(`
56046
+ ${GRAY}${TAGLINE}${RESET}
56047
+ `);
56048
+ } else {
56049
+ stream.write(`${compactLogo()}
56050
+ `);
56051
+ }
56052
+ if (version) {
56053
+ stream.write(` ${DIM}claudish version${RESET} ${BOLD}${version}${RESET}
56054
+ `);
56055
+ }
56056
+ if (trailingBlankLine)
56057
+ stream.write(`
56058
+ `);
56059
+ }
56060
+ var MIN_LOGO_WIDTH = 64, TAGLINE = "Claude Code. Any Model.", WORDMARK;
56061
+ var init_branding = __esm(() => {
56062
+ init_ansi();
56063
+ WORDMARK = [
56064
+ " \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
56065
+ "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551",
56066
+ "\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551",
56067
+ "\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551",
56068
+ "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551",
56069
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D"
56070
+ ];
56071
+ });
56072
+
56073
+ // src/update-checker.ts
56074
+ import { existsSync as existsSync28, mkdirSync as mkdirSync16, readFileSync as readFileSync28, unlinkSync as unlinkSync7, writeFileSync as writeFileSync17 } from "fs";
56075
+ import { homedir as homedir33, platform as platform2, tmpdir } from "os";
56076
+ import { join as join38 } from "path";
56077
+ function getCacheFilePath() {
56078
+ let cacheDir;
56079
+ if (isWindows) {
56080
+ const localAppData = process.env.LOCALAPPDATA || join38(homedir33(), "AppData", "Local");
56081
+ cacheDir = join38(localAppData, "claudish");
56082
+ } else {
56083
+ cacheDir = join38(homedir33(), ".cache", "claudish");
56084
+ }
56085
+ try {
56086
+ if (!existsSync28(cacheDir)) {
56087
+ mkdirSync16(cacheDir, { recursive: true });
56088
+ }
56089
+ return join38(cacheDir, "update-check.json");
56090
+ } catch {
56091
+ return join38(tmpdir(), "claudish-update-check.json");
56092
+ }
56093
+ }
56094
+ function readCache() {
56095
+ try {
56096
+ const cachePath = getCacheFilePath();
56097
+ if (!existsSync28(cachePath)) {
56098
+ return null;
56099
+ }
56100
+ const data = JSON.parse(readFileSync28(cachePath, "utf-8"));
56101
+ return data;
56102
+ } catch {
56103
+ return null;
56104
+ }
56105
+ }
56106
+ function writeCache(latestVersion) {
56107
+ try {
56108
+ const cachePath = getCacheFilePath();
56109
+ const data = {
56110
+ lastCheck: Date.now(),
56111
+ latestVersion
56112
+ };
56113
+ writeFileSync17(cachePath, JSON.stringify(data), "utf-8");
56114
+ } catch {}
56115
+ }
56116
+ function isCacheValid(cache) {
56117
+ const age = Date.now() - cache.lastCheck;
56118
+ return age < CACHE_MAX_AGE_MS;
56119
+ }
56120
+ function clearCache() {
56121
+ try {
56122
+ const cachePath = getCacheFilePath();
56123
+ if (existsSync28(cachePath)) {
56124
+ unlinkSync7(cachePath);
56125
+ }
56126
+ } catch {}
56127
+ }
56128
+ function compareVersions(v1, v2) {
56129
+ const parts1 = v1.replace(/^v/, "").split(".").map(Number);
56130
+ const parts2 = v2.replace(/^v/, "").split(".").map(Number);
56131
+ for (let i = 0;i < Math.max(parts1.length, parts2.length); i++) {
56132
+ const p1 = parts1[i] || 0;
56133
+ const p2 = parts2[i] || 0;
56134
+ if (p1 > p2)
56135
+ return 1;
56136
+ if (p1 < p2)
56137
+ return -1;
56138
+ }
56139
+ return 0;
56140
+ }
56141
+ async function fetchLatestVersionOrThrow(options = {}) {
56142
+ const { timeoutMs = 5000, retries = 0 } = options;
56143
+ let lastError = new Error("unknown error");
56144
+ for (let attempt = 0;attempt <= retries; attempt++) {
56145
+ const controller = new AbortController;
56146
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
56147
+ try {
56148
+ const response = await fetch(NPM_REGISTRY_URL, {
56149
+ signal: controller.signal,
56150
+ headers: { Accept: "application/json" }
56151
+ });
56152
+ if (!response.ok) {
56153
+ throw new Error(`npm registry returned HTTP ${response.status}`);
56154
+ }
56155
+ const data = await response.json();
56156
+ if (!data.version) {
56157
+ throw new Error("npm registry response contained no version field");
56158
+ }
56159
+ return data.version;
56160
+ } catch (error) {
56161
+ lastError = error instanceof Error && error.name === "AbortError" ? new Error(`request timed out after ${timeoutMs}ms`) : error instanceof Error ? error : new Error(String(error));
56162
+ if (attempt < retries) {
56163
+ await new Promise((resolve) => setTimeout(resolve, 300 * (attempt + 1)));
56164
+ }
56165
+ } finally {
56166
+ clearTimeout(timeout);
56167
+ }
56168
+ }
56169
+ throw lastError;
56170
+ }
56171
+ async function fetchLatestVersion(options = {}) {
56172
+ try {
56173
+ return await fetchLatestVersionOrThrow(options);
56174
+ } catch {
56175
+ return null;
56176
+ }
56177
+ }
56178
+ async function getLatestVersionCached(options = {}) {
56179
+ const cache = readCache();
56180
+ if (cache && isCacheValid(cache)) {
56181
+ return cache.latestVersion;
56182
+ }
56183
+ const latestVersion = await fetchLatestVersion(options);
56184
+ writeCache(latestVersion);
56185
+ return latestVersion;
56186
+ }
56187
+ function isUpgrade(latestVersion, currentVersion) {
56188
+ if (!latestVersion)
56189
+ return false;
56190
+ return compareVersions(latestVersion, currentVersion) > 0;
56191
+ }
56192
+ function formatUpdateNotice(currentVersion, latestVersion, options = {}) {
56193
+ const { hint = true } = options;
56194
+ const { RESET, BOLD, GREEN, CYAN, DIM } = cliAnsi();
56195
+ const runHint = hint ? ` ${DIM}Run:${RESET} ${BOLD}${CYAN}claudish update${RESET}` : "";
56196
+ return ` ${CYAN}\u250C${RESET} ${BOLD}Update available:${RESET} ${currentVersion} ${DIM}\u2192${RESET} ${GREEN}${latestVersion}${RESET}${runHint}`;
56197
+ }
56198
+ var isWindows, NPM_REGISTRY_URL = "https://registry.npmjs.org/claudish/latest", CACHE_MAX_AGE_MS;
56199
+ var init_update_checker = __esm(() => {
56200
+ init_ansi();
56201
+ isWindows = platform2() === "win32";
56202
+ CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
56203
+ });
56204
+
56016
56205
  // src/cli.ts
56017
56206
  import {
56018
56207
  copyFileSync as copyFileSync2,
56019
- existsSync as existsSync28,
56020
- mkdirSync as mkdirSync16,
56021
- readFileSync as readFileSync28,
56208
+ existsSync as existsSync29,
56209
+ mkdirSync as mkdirSync17,
56210
+ readFileSync as readFileSync29,
56022
56211
  readdirSync as readdirSync7,
56023
- unlinkSync as unlinkSync7,
56024
- writeFileSync as writeFileSync17
56212
+ unlinkSync as unlinkSync8,
56213
+ writeFileSync as writeFileSync18
56025
56214
  } from "fs";
56026
- import { homedir as homedir33 } from "os";
56027
- import { dirname as dirname11, join as join38 } from "path";
56215
+ import { homedir as homedir34 } from "os";
56216
+ import { dirname as dirname11, join as join39 } from "path";
56028
56217
  import { fileURLToPath as fileURLToPath3 } from "url";
56029
56218
  function getVersion3() {
56030
56219
  return VERSION;
56031
56220
  }
56032
56221
  function clearAllModelCaches() {
56033
- const cacheDir = join38(homedir33(), ".claudish");
56034
- if (!existsSync28(cacheDir))
56222
+ const cacheDir = join39(homedir34(), ".claudish");
56223
+ if (!existsSync29(cacheDir))
56035
56224
  return;
56036
56225
  const cachePatterns = ["pricing-cache.json", "recommended-models-cache.json"];
56037
56226
  let cleared = 0;
@@ -56039,7 +56228,7 @@ function clearAllModelCaches() {
56039
56228
  const files = readdirSync7(cacheDir);
56040
56229
  for (const file of files) {
56041
56230
  if (cachePatterns.includes(file)) {
56042
- unlinkSync7(join38(cacheDir, file));
56231
+ unlinkSync8(join39(cacheDir, file));
56043
56232
  cleared++;
56044
56233
  }
56045
56234
  }
@@ -56310,7 +56499,7 @@ async function parseArgs(args) {
56310
56499
  } else if (arg === "--cost-reset") {
56311
56500
  config.resetCosts = true;
56312
56501
  } else if (arg === "--version") {
56313
- printVersion();
56502
+ await printVersion();
56314
56503
  process.exit(0);
56315
56504
  } else if (arg === "--help" || arg === "-h") {
56316
56505
  printHelp2();
@@ -56496,15 +56685,15 @@ Usage: claudish --models --provider <slug>`);
56496
56685
  });
56497
56686
  config.resolvedDefaultProvider = resolved;
56498
56687
  if (resolved.legacyAutoPromoted && !config.quiet) {
56499
- const markerFile = join38(homedir33(), ".claudish", ".legacy-litellm-hint-shown");
56500
- if (!existsSync28(markerFile)) {
56688
+ const markerFile = join39(homedir34(), ".claudish", ".legacy-litellm-hint-shown");
56689
+ if (!existsSync29(markerFile)) {
56501
56690
  const hint = buildLegacyHint(resolved);
56502
56691
  if (hint) {
56503
56692
  console.error(hint);
56504
56693
  }
56505
56694
  try {
56506
- mkdirSync16(dirname11(markerFile), { recursive: true });
56507
- writeFileSync17(markerFile, new Date().toISOString(), "utf-8");
56695
+ mkdirSync17(dirname11(markerFile), { recursive: true });
56696
+ writeFileSync18(markerFile, new Date().toISOString(), "utf-8");
56508
56697
  } catch {}
56509
56698
  }
56510
56699
  }
@@ -56797,8 +56986,20 @@ Recommended Models (last updated: ${lastUpdated}):
56797
56986
  console.log(" claudish --models-top --models-refresh (refresh from Firebase)");
56798
56987
  console.log("");
56799
56988
  }
56800
- function printVersion() {
56801
- console.log(`claudish version ${VERSION}`);
56989
+ async function printVersion() {
56990
+ if (!process.stdout.isTTY) {
56991
+ console.log(`claudish version ${VERSION}`);
56992
+ return;
56993
+ }
56994
+ await Promise.resolve().then(() => init_branding());
56995
+ printLogo(process.stdout, { version: VERSION, trailingBlankLine: false });
56996
+ await Promise.resolve().then(() => init_update_checker());
56997
+ const latestVersion = await getLatestVersionCached({ timeoutMs: 1500 });
56998
+ if (latestVersion && isUpgrade(latestVersion, VERSION)) {
56999
+ console.log("");
57000
+ console.log(formatUpdateNotice(VERSION, latestVersion));
57001
+ }
57002
+ console.log("");
56802
57003
  }
56803
57004
  async function probeModelRouting(models, jsonOutput, options = { live: true, timeoutMs: 40000 }) {
56804
57005
  const userRoutingKeys = new Set([
@@ -57629,8 +57830,8 @@ ${h("MORE INFO")}
57629
57830
  }
57630
57831
  function printAIAgentGuide() {
57631
57832
  try {
57632
- const guidePath = join38(__dirname3, "../AI_AGENT_GUIDE.md");
57633
- const guideContent = readFileSync28(guidePath, "utf-8");
57833
+ const guidePath = join39(__dirname3, "../AI_AGENT_GUIDE.md");
57834
+ const guideContent = readFileSync29(guidePath, "utf-8");
57634
57835
  console.log(guideContent);
57635
57836
  } catch (error) {
57636
57837
  console.error("Error reading AI Agent Guide:");
@@ -57646,19 +57847,19 @@ async function initializeClaudishSkill() {
57646
57847
  console.log(`\uD83D\uDD27 Initializing Claudish skill in current project...
57647
57848
  `);
57648
57849
  const cwd = process.cwd();
57649
- const claudeDir = join38(cwd, ".claude");
57650
- const skillsDir = join38(claudeDir, "skills");
57651
- const claudishSkillDir = join38(skillsDir, "claudish-usage");
57652
- const skillFile = join38(claudishSkillDir, "SKILL.md");
57653
- if (existsSync28(skillFile)) {
57850
+ const claudeDir = join39(cwd, ".claude");
57851
+ const skillsDir = join39(claudeDir, "skills");
57852
+ const claudishSkillDir = join39(skillsDir, "claudish-usage");
57853
+ const skillFile = join39(claudishSkillDir, "SKILL.md");
57854
+ if (existsSync29(skillFile)) {
57654
57855
  console.log("\u2705 Claudish skill already installed at:");
57655
57856
  console.log(` ${skillFile}
57656
57857
  `);
57657
57858
  console.log("\uD83D\uDCA1 To reinstall, delete the file and run 'claudish --init' again.");
57658
57859
  return;
57659
57860
  }
57660
- const sourceSkillPath = join38(__dirname3, "../skills/claudish-usage/SKILL.md");
57661
- if (!existsSync28(sourceSkillPath)) {
57861
+ const sourceSkillPath = join39(__dirname3, "../skills/claudish-usage/SKILL.md");
57862
+ if (!existsSync29(sourceSkillPath)) {
57662
57863
  console.error("\u274C Error: Claudish skill file not found in installation.");
57663
57864
  console.error(` Expected at: ${sourceSkillPath}`);
57664
57865
  console.error(`
@@ -57667,16 +57868,16 @@ async function initializeClaudishSkill() {
57667
57868
  process.exit(1);
57668
57869
  }
57669
57870
  try {
57670
- if (!existsSync28(claudeDir)) {
57671
- mkdirSync16(claudeDir, { recursive: true });
57871
+ if (!existsSync29(claudeDir)) {
57872
+ mkdirSync17(claudeDir, { recursive: true });
57672
57873
  console.log("\uD83D\uDCC1 Created .claude/ directory");
57673
57874
  }
57674
- if (!existsSync28(skillsDir)) {
57675
- mkdirSync16(skillsDir, { recursive: true });
57875
+ if (!existsSync29(skillsDir)) {
57876
+ mkdirSync17(skillsDir, { recursive: true });
57676
57877
  console.log("\uD83D\uDCC1 Created .claude/skills/ directory");
57677
57878
  }
57678
- if (!existsSync28(claudishSkillDir)) {
57679
- mkdirSync16(claudishSkillDir, { recursive: true });
57879
+ if (!existsSync29(claudishSkillDir)) {
57880
+ mkdirSync17(claudishSkillDir, { recursive: true });
57680
57881
  console.log("\uD83D\uDCC1 Created .claude/skills/claudish-usage/ directory");
57681
57882
  }
57682
57883
  copyFileSync2(sourceSkillPath, skillFile);
@@ -57756,141 +57957,6 @@ var init_cli = __esm(() => {
57756
57957
  __dirname3 = dirname11(__filename3);
57757
57958
  });
57758
57959
 
57759
- // src/update-checker.ts
57760
- import { existsSync as existsSync29, mkdirSync as mkdirSync17, readFileSync as readFileSync29, unlinkSync as unlinkSync8, writeFileSync as writeFileSync18 } from "fs";
57761
- import { homedir as homedir34, platform as platform2, tmpdir } from "os";
57762
- import { join as join39 } from "path";
57763
- function getCacheFilePath() {
57764
- let cacheDir;
57765
- if (isWindows) {
57766
- const localAppData = process.env.LOCALAPPDATA || join39(homedir34(), "AppData", "Local");
57767
- cacheDir = join39(localAppData, "claudish");
57768
- } else {
57769
- cacheDir = join39(homedir34(), ".cache", "claudish");
57770
- }
57771
- try {
57772
- if (!existsSync29(cacheDir)) {
57773
- mkdirSync17(cacheDir, { recursive: true });
57774
- }
57775
- return join39(cacheDir, "update-check.json");
57776
- } catch {
57777
- return join39(tmpdir(), "claudish-update-check.json");
57778
- }
57779
- }
57780
- function readCache() {
57781
- try {
57782
- const cachePath = getCacheFilePath();
57783
- if (!existsSync29(cachePath)) {
57784
- return null;
57785
- }
57786
- const data = JSON.parse(readFileSync29(cachePath, "utf-8"));
57787
- return data;
57788
- } catch {
57789
- return null;
57790
- }
57791
- }
57792
- function writeCache(latestVersion) {
57793
- try {
57794
- const cachePath = getCacheFilePath();
57795
- const data = {
57796
- lastCheck: Date.now(),
57797
- latestVersion
57798
- };
57799
- writeFileSync18(cachePath, JSON.stringify(data), "utf-8");
57800
- } catch {}
57801
- }
57802
- function isCacheValid(cache) {
57803
- const age = Date.now() - cache.lastCheck;
57804
- return age < CACHE_MAX_AGE_MS;
57805
- }
57806
- function clearCache() {
57807
- try {
57808
- const cachePath = getCacheFilePath();
57809
- if (existsSync29(cachePath)) {
57810
- unlinkSync8(cachePath);
57811
- }
57812
- } catch {}
57813
- }
57814
- function compareVersions(v1, v2) {
57815
- const parts1 = v1.replace(/^v/, "").split(".").map(Number);
57816
- const parts2 = v2.replace(/^v/, "").split(".").map(Number);
57817
- for (let i = 0;i < Math.max(parts1.length, parts2.length); i++) {
57818
- const p1 = parts1[i] || 0;
57819
- const p2 = parts2[i] || 0;
57820
- if (p1 > p2)
57821
- return 1;
57822
- if (p1 < p2)
57823
- return -1;
57824
- }
57825
- return 0;
57826
- }
57827
- async function fetchLatestVersionOrThrow(options = {}) {
57828
- const { timeoutMs = 5000, retries = 0 } = options;
57829
- let lastError = new Error("unknown error");
57830
- for (let attempt = 0;attempt <= retries; attempt++) {
57831
- const controller = new AbortController;
57832
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
57833
- try {
57834
- const response = await fetch(NPM_REGISTRY_URL, {
57835
- signal: controller.signal,
57836
- headers: { Accept: "application/json" }
57837
- });
57838
- if (!response.ok) {
57839
- throw new Error(`npm registry returned HTTP ${response.status}`);
57840
- }
57841
- const data = await response.json();
57842
- if (!data.version) {
57843
- throw new Error("npm registry response contained no version field");
57844
- }
57845
- return data.version;
57846
- } catch (error) {
57847
- lastError = error instanceof Error && error.name === "AbortError" ? new Error(`request timed out after ${timeoutMs}ms`) : error instanceof Error ? error : new Error(String(error));
57848
- if (attempt < retries) {
57849
- await new Promise((resolve) => setTimeout(resolve, 300 * (attempt + 1)));
57850
- }
57851
- } finally {
57852
- clearTimeout(timeout);
57853
- }
57854
- }
57855
- throw lastError;
57856
- }
57857
- async function fetchLatestVersion(options = {}) {
57858
- try {
57859
- return await fetchLatestVersionOrThrow(options);
57860
- } catch {
57861
- return null;
57862
- }
57863
- }
57864
- async function checkForUpdates(currentVersion, options = {}) {
57865
- const { quiet = false } = options;
57866
- let latestVersion = null;
57867
- const cache = readCache();
57868
- if (cache && isCacheValid(cache)) {
57869
- latestVersion = cache.latestVersion;
57870
- } else {
57871
- latestVersion = await fetchLatestVersion();
57872
- writeCache(latestVersion);
57873
- }
57874
- if (!latestVersion) {
57875
- return;
57876
- }
57877
- if (compareVersions(latestVersion, currentVersion) <= 0) {
57878
- return;
57879
- }
57880
- if (!quiet) {
57881
- const { RESET, BOLD, GREEN, CYAN, DIM } = cliAnsi();
57882
- console.error("");
57883
- console.error(` ${CYAN}\u250C${RESET} ${BOLD}Update available:${RESET} ${currentVersion} ${DIM}\u2192${RESET} ${GREEN}${latestVersion}${RESET} ${DIM}Run:${RESET} ${BOLD}${CYAN}claudish update${RESET}`);
57884
- console.error("");
57885
- }
57886
- }
57887
- var isWindows, NPM_REGISTRY_URL = "https://registry.npmjs.org/claudish/latest", CACHE_MAX_AGE_MS;
57888
- var init_update_checker = __esm(() => {
57889
- init_ansi();
57890
- isWindows = platform2() === "win32";
57891
- CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
57892
- });
57893
-
57894
57960
  // src/update-command.ts
57895
57961
  import { execSync as execSync2 } from "child_process";
57896
57962
  function refreshAnsi2() {
@@ -58083,6 +58149,24 @@ async function resolveLatestVersion() {
58083
58149
  return { version: viaNpm };
58084
58150
  return { error: fetchError };
58085
58151
  }
58152
+ async function performUpdate() {
58153
+ refreshAnsi2();
58154
+ const installInfo = detectInstallationMethod();
58155
+ if (installInfo.method === "unknown") {
58156
+ printManualInstructions();
58157
+ return { status: "unsupported" };
58158
+ }
58159
+ const command = getUpdateCommand(installInfo.method);
58160
+ console.log(`
58161
+ ${DIM}Updating...${RESET}
58162
+ `);
58163
+ const success = await executeUpdate(command);
58164
+ if (success) {
58165
+ clearCache();
58166
+ return { status: "updated", method: installInfo.method };
58167
+ }
58168
+ return { status: "failed", method: installInfo.method };
58169
+ }
58086
58170
  async function updateCommand() {
58087
58171
  refreshAnsi2();
58088
58172
  const currentVersion = getVersion3();
@@ -58111,19 +58195,13 @@ async function updateCommand() {
58111
58195
  process.exit(0);
58112
58196
  }
58113
58197
  console.log(` ${BOLD}claudish${RESET} ${YELLOW}v${currentVersion}${RESET} ${DIM}\u2192${RESET} ${GREEN}v${latestVersion}${RESET} ${DIM}(${installInfo.method})${RESET}`);
58114
- if (installInfo.method === "unknown") {
58115
- printManualInstructions();
58198
+ const outcome = await performUpdate();
58199
+ if (outcome.status === "unsupported") {
58116
58200
  process.exit(1);
58117
58201
  }
58118
- const command = getUpdateCommand(installInfo.method);
58119
- console.log(`
58120
- ${DIM}Updating...${RESET}
58121
- `);
58122
- const success = await executeUpdate(command);
58123
- if (success) {
58202
+ if (outcome.status === "updated") {
58124
58203
  console.log(`
58125
58204
  ${GREEN}\u2713${RESET} ${BOLD}Updated successfully${RESET}`);
58126
- clearCache();
58127
58205
  const changelog = await fetchChangelog(currentVersion, latestVersion);
58128
58206
  displayChangelog(changelog);
58129
58207
  console.log("");
@@ -66293,6 +66371,67 @@ function createDiagOutput(options) {
66293
66371
  }
66294
66372
  var init_diag_output = () => {};
66295
66373
 
66374
+ // src/update-prompt.ts
66375
+ async function confirm(question) {
66376
+ const { createInterface } = await import("readline");
66377
+ return new Promise((resolve) => {
66378
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
66379
+ let answered = false;
66380
+ rl.question(question, (value) => {
66381
+ answered = true;
66382
+ rl.close();
66383
+ const normalized = value.trim().toLowerCase();
66384
+ resolve(!(normalized === "n" || normalized === "no"));
66385
+ });
66386
+ rl.on("close", () => {
66387
+ if (!answered)
66388
+ resolve(false);
66389
+ });
66390
+ });
66391
+ }
66392
+ async function checkForUpdatesInteractive(currentVersion, options = {}) {
66393
+ const { quiet = false, canPrompt = true } = options;
66394
+ const latestVersion = await getLatestVersionCached();
66395
+ if (!latestVersion || !isUpgrade(latestVersion, currentVersion)) {
66396
+ return "continue";
66397
+ }
66398
+ if (quiet) {
66399
+ return "continue";
66400
+ }
66401
+ const { RESET, BOLD, GREEN, CYAN, DIM, YELLOW } = cliAnsi();
66402
+ if (!canPrompt || !process.stdin.isTTY) {
66403
+ console.error("");
66404
+ console.error(formatUpdateNotice(currentVersion, latestVersion));
66405
+ console.error("");
66406
+ return "continue";
66407
+ }
66408
+ console.error("");
66409
+ console.error(formatUpdateNotice(currentVersion, latestVersion, { hint: false }));
66410
+ console.error("");
66411
+ const accepted = await confirm(` ${BOLD}Update now?${RESET} ${DIM}[Y/n]${RESET} `);
66412
+ if (!accepted) {
66413
+ console.error(` ${DIM}Skipped. Run${RESET} ${BOLD}${CYAN}claudish update${RESET} ${DIM}when you are ready.${RESET}
66414
+ `);
66415
+ return "continue";
66416
+ }
66417
+ const outcome = await performUpdate();
66418
+ if (outcome.status === "updated") {
66419
+ console.error("");
66420
+ console.error(` ${GREEN}\u2713${RESET} ${BOLD}Updated to ${latestVersion}.${RESET} ${DIM}This process is still running ${currentVersion}.${RESET}`);
66421
+ console.error(` ${DIM}Run${RESET} ${BOLD}${CYAN}claudish${RESET} ${DIM}again to start.${RESET}`);
66422
+ console.error("");
66423
+ return "restart-required";
66424
+ }
66425
+ console.error(` ${YELLOW}Continuing on ${currentVersion}.${RESET}
66426
+ `);
66427
+ return "continue";
66428
+ }
66429
+ var init_update_prompt = __esm(() => {
66430
+ init_ansi();
66431
+ init_update_checker();
66432
+ init_update_command();
66433
+ });
66434
+
66296
66435
  // src/launcher/catalog-warm.ts
66297
66436
  function shouldWarmCatalog(args) {
66298
66437
  if (args.skipModelsUpdate)
@@ -68647,7 +68786,7 @@ function bg(hex) {
68647
68786
  const [r, g, b] = rgb(hex);
68648
68787
  return `\x1B[48;2;${r};${g};${b}m`;
68649
68788
  }
68650
- function paint(text, hex, bold = false) {
68789
+ function paint2(text, hex, bold = false) {
68651
68790
  return `${bold ? BOLD4 : ""}${fg(hex)}${text}${RESET3}`;
68652
68791
  }
68653
68792
  function meter(pct, width, ramp = ramps.load) {
@@ -68655,7 +68794,7 @@ function meter(pct, width, ramp = ramps.load) {
68655
68794
  if (!Number.isFinite(cells) || cells <= 0)
68656
68795
  return "";
68657
68796
  if (Number.isNaN(pct))
68658
- return paint(NODATA2.repeat(cells), tokens.dead);
68797
+ return paint2(NODATA2.repeat(cells), tokens.dead);
68659
68798
  const cols = rampFor(cells, ramp);
68660
68799
  const filled = fillCells(pct, cells);
68661
68800
  let out = "";
@@ -68676,7 +68815,7 @@ function stackedBar(segments, width) {
68676
68815
  return "";
68677
68816
  const split = splitCells(segments.map((s) => s.value), cells);
68678
68817
  if (split.reduce((a, b) => a + b, 0) === 0)
68679
- return paint(TRACK2.repeat(cells), tokens.border);
68818
+ return paint2(TRACK2.repeat(cells), tokens.border);
68680
68819
  let out = "";
68681
68820
  for (let i = 0;i < split.length; i++) {
68682
68821
  const n = split[i];
@@ -68801,14 +68940,14 @@ function renderSessionSummary(input) {
68801
68940
  const W = cardWidth();
68802
68941
  const inner = W - CHROME;
68803
68942
  const out = [];
68804
- const dim = (s) => paint(s, tokens.subtle);
68805
- const body = (s) => paint(s, tokens.text);
68943
+ const dim = (s) => paint2(s, tokens.subtle);
68944
+ const body = (s) => paint2(s, tokens.text);
68806
68945
  const titleText = exitCode === 0 ? " session " : " session \xB7 failed ";
68807
68946
  const titleHex = exitCode === 0 ? tokens.accent : tokens.error;
68808
68947
  const rule = "\u2500".repeat(Math.max(0, W - 2 - visibleWidth(titleText) - 1));
68809
- out.push(paint("\u256D\u2500", tokens.border) + paint(titleText, titleHex) + paint(rule + "\u256E", tokens.border));
68948
+ out.push(paint2("\u256D\u2500", tokens.border) + paint2(titleText, titleHex) + paint2(rule + "\u256E", tokens.border));
68810
68949
  const row = (s) => {
68811
- out.push(`${paint("\u2502", tokens.border)} ${padVisible2(s, inner)} ${paint("\u2502", tokens.border)}`);
68950
+ out.push(`${paint2("\u2502", tokens.border)} ${padVisible2(s, inner)} ${paint2("\u2502", tokens.border)}`);
68812
68951
  };
68813
68952
  const blank = () => row("");
68814
68953
  const chips = [badge(truncate3(modelSpec, 34), tokens.accent)];
@@ -68858,13 +68997,13 @@ function renderSessionSummary(input) {
68858
68997
  if (rest > 0)
68859
68998
  segs.push({ value: rest, color: area(other) });
68860
68999
  dataRow("tools", stackedBar(segs, barW), body(padStartTo(String(stats.toolCallTotal), 4)) + dim(" calls"));
68861
- const legend = shown.map((t, i) => paint(`${t.name} ${t.count}`, toolCols[i])).concat(rest > 0 ? [paint(`other ${rest}`, other)] : []);
69000
+ const legend = shown.map((t, i) => paint2(`${t.name} ${t.count}`, toolCols[i])).concat(rest > 0 ? [paint2(`other ${rest}`, other)] : []);
68862
69001
  for (const line of wrapStyled(legend, dim(" \xB7 "), inner - LABEL_W)) {
68863
69002
  row(" ".repeat(LABEL_W) + line);
68864
69003
  }
68865
69004
  }
68866
69005
  blank();
68867
- row(dim(padTo("cost", LABEL_W)) + paint(stats.isFree ? "free" : usd(stats.costUsd), tokens.text, true) + (stats.isEstimated && !stats.isFree ? dim(" estimated") : ""));
69006
+ row(dim(padTo("cost", LABEL_W)) + paint2(stats.isFree ? "free" : usd(stats.costUsd), tokens.text, true) + (stats.isEstimated && !stats.isFree ? dim(" estimated") : ""));
68868
69007
  const dearest = Math.max(0, ...stats.savings.map((s) => s.baselineUsd));
68869
69008
  for (const s of stats.savings) {
68870
69009
  const label = padTo(`vs ${s.label}`, LABEL_W);
@@ -68873,10 +69012,10 @@ function renderSessionSummary(input) {
68873
69012
  const share = dearest > 0 ? s.savedUsd / dearest * 100 : 0;
68874
69013
  dataRow(label, meter(share, barW, areaRamp(ramps.volume)), dim(`${padStartTo(`${Math.round(pct)}%`, 4)} saved `) + body(usd(s.savedUsd)));
68875
69014
  } else {
68876
- dataRow(label, meter(0, barW, areaRamp(ramps.volume)), dim("over by ") + paint(usd(-s.savedUsd), tokens.error));
69015
+ dataRow(label, meter(0, barW, areaRamp(ramps.volume)), dim("over by ") + paint2(usd(-s.savedUsd), tokens.error));
68877
69016
  }
68878
69017
  }
68879
- out.push(paint(`\u2570${"\u2500".repeat(W - 2)}\u256F`, tokens.border));
69018
+ out.push(paint2(`\u2570${"\u2500".repeat(W - 2)}\u256F`, tokens.border));
68880
69019
  if (resumeId) {
68881
69020
  out.push("");
68882
69021
  out.push(dim("Resume this session with:"));
@@ -69170,7 +69309,8 @@ async function runCli() {
69170
69309
  await Promise.resolve().then(() => init_diag_output());
69171
69310
  await Promise.resolve().then(() => init_port_manager());
69172
69311
  await Promise.resolve().then(() => init_proxy_server());
69173
- await Promise.resolve().then(() => init_update_checker());
69312
+ await Promise.resolve().then(() => init_update_prompt());
69313
+ await Promise.resolve().then(() => init_branding());
69174
69314
  await Promise.resolve().then(() => init_catalog_warm());
69175
69315
  endImports();
69176
69316
  async function readStdin() {
@@ -69241,6 +69381,9 @@ Team Status`);
69241
69381
  }
69242
69382
  process.exit(0);
69243
69383
  }
69384
+ if (cliConfig.interactive && !cliConfig.quiet && process.stderr.isTTY) {
69385
+ printLogo(process.stderr, { version: getVersion3() });
69386
+ }
69244
69387
  const rawArgs = process.argv.slice(2);
69245
69388
  const explicitNoAutoApprove = rawArgs.includes("--no-auto-approve");
69246
69389
  if (cliConfig.autoApprove && !explicitNoAutoApprove && !cliConfig.stdin && cliConfig.interactive) {
@@ -69295,7 +69438,13 @@ Team Status`);
69295
69438
  }
69296
69439
  }
69297
69440
  if (cliConfig.interactive && !cliConfig.jsonOutput) {
69298
- await traceSpan("startup:update-check", () => checkForUpdates(getVersion3(), { quiet: cliConfig.quiet }));
69441
+ const updateStep = await traceSpan("startup:update-check", () => checkForUpdatesInteractive(getVersion3(), {
69442
+ quiet: cliConfig.quiet,
69443
+ canPrompt: !cliConfig.stdin
69444
+ }), { mayIncludeUserPrompt: true });
69445
+ if (updateStep === "restart-required") {
69446
+ process.exit(0);
69447
+ }
69299
69448
  }
69300
69449
  if (!await traceSpan("startup:claude-detect", () => checkClaudeInstalled())) {
69301
69450
  console.error("Error: Claude Code CLI not found");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "9.0.8",
3
+ "version": "9.1.0",
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",
@@ -60,10 +60,10 @@
60
60
  "ai"
61
61
  ],
62
62
  "optionalDependencies": {
63
- "@claudish/magmux-darwin-arm64": "9.0.8",
64
- "@claudish/magmux-darwin-x64": "9.0.8",
65
- "@claudish/magmux-linux-arm64": "9.0.8",
66
- "@claudish/magmux-linux-x64": "9.0.8"
63
+ "@claudish/magmux-darwin-arm64": "9.1.0",
64
+ "@claudish/magmux-darwin-x64": "9.1.0",
65
+ "@claudish/magmux-linux-arm64": "9.1.0",
66
+ "@claudish/magmux-linux-x64": "9.1.0"
67
67
  },
68
68
  "author": "Jack Rudenko <i@madappgang.com>",
69
69
  "license": "MIT",