ccstatusline 2.1.6 → 2.1.8

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.
@@ -32056,8 +32056,8 @@ var require_utils = __commonJS((exports) => {
32056
32056
  }
32057
32057
  return output;
32058
32058
  };
32059
- exports.basename = (path5, { windows } = {}) => {
32060
- const segs = path5.split(windows ? /[\\/]/ : "/");
32059
+ exports.basename = (path6, { windows } = {}) => {
32060
+ const segs = path6.split(windows ? /[\\/]/ : "/");
32061
32061
  const last = segs[segs.length - 1];
32062
32062
  if (last === "") {
32063
32063
  return segs[segs.length - 2];
@@ -32408,7 +32408,7 @@ var require_parse = __commonJS((exports, module) => {
32408
32408
  var syntaxError = (type, char) => {
32409
32409
  return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
32410
32410
  };
32411
- var parse5 = (input, options) => {
32411
+ var parse6 = (input, options) => {
32412
32412
  if (typeof input !== "string") {
32413
32413
  throw new TypeError("Expected a string");
32414
32414
  }
@@ -32557,7 +32557,7 @@ var require_parse = __commonJS((exports, module) => {
32557
32557
  output = token.close = `)$))${extglobStar}`;
32558
32558
  }
32559
32559
  if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
32560
- const expression = parse5(rest, { ...options, fastpaths: false }).output;
32560
+ const expression = parse6(rest, { ...options, fastpaths: false }).output;
32561
32561
  output = token.close = `)${expression})${extglobStar})`;
32562
32562
  }
32563
32563
  if (token.prev.type === "bos") {
@@ -33083,7 +33083,7 @@ var require_parse = __commonJS((exports, module) => {
33083
33083
  }
33084
33084
  return state;
33085
33085
  };
33086
- parse5.fastpaths = (input, options) => {
33086
+ parse6.fastpaths = (input, options) => {
33087
33087
  const opts = { ...options };
33088
33088
  const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
33089
33089
  const len = input.length;
@@ -33151,13 +33151,13 @@ var require_parse = __commonJS((exports, module) => {
33151
33151
  }
33152
33152
  return source;
33153
33153
  };
33154
- module.exports = parse5;
33154
+ module.exports = parse6;
33155
33155
  });
33156
33156
 
33157
33157
  // node_modules/picomatch/lib/picomatch.js
33158
33158
  var require_picomatch = __commonJS((exports, module) => {
33159
33159
  var scan = require_scan();
33160
- var parse5 = require_parse();
33160
+ var parse6 = require_parse();
33161
33161
  var utils = require_utils();
33162
33162
  var constants2 = require_constants3();
33163
33163
  var isObject2 = (val) => val && typeof val === "object" && !Array.isArray(val);
@@ -33247,7 +33247,7 @@ var require_picomatch = __commonJS((exports, module) => {
33247
33247
  picomatch.parse = (pattern, options) => {
33248
33248
  if (Array.isArray(pattern))
33249
33249
  return pattern.map((p) => picomatch.parse(p, options));
33250
- return parse5(pattern, { ...options, fastpaths: false });
33250
+ return parse6(pattern, { ...options, fastpaths: false });
33251
33251
  };
33252
33252
  picomatch.scan = (input, options) => scan(input, options);
33253
33253
  picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => {
@@ -33273,10 +33273,10 @@ var require_picomatch = __commonJS((exports, module) => {
33273
33273
  }
33274
33274
  let parsed = { negated: false, fastpaths: true };
33275
33275
  if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) {
33276
- parsed.output = parse5.fastpaths(input, options);
33276
+ parsed.output = parse6.fastpaths(input, options);
33277
33277
  }
33278
33278
  if (!parsed.output) {
33279
- parsed = parse5(input, options);
33279
+ parsed = parse6(input, options);
33280
33280
  }
33281
33281
  return picomatch.compileRe(parsed, options, returnOutput, returnState);
33282
33282
  };
@@ -39277,108 +39277,15 @@ var import_react46 = __toESM(require_react(), 1);
39277
39277
 
39278
39278
  // src/utils/claude-settings.ts
39279
39279
  import { execSync } from "child_process";
39280
- import * as fs2 from "fs";
39281
- import * as os2 from "os";
39282
- import * as path from "path";
39283
- var readFile = fs2.promises.readFile;
39284
- var writeFile = fs2.promises.writeFile;
39285
- var mkdir = fs2.promises.mkdir;
39286
- var CCSTATUSLINE_COMMANDS = {
39287
- NPM: "npx -y ccstatusline@latest",
39288
- BUNX: "bunx -y ccstatusline@latest",
39289
- SELF_MANAGED: "ccstatusline"
39290
- };
39291
- function getClaudeConfigDir() {
39292
- const envConfigDir = process.env.CLAUDE_CONFIG_DIR;
39293
- if (envConfigDir) {
39294
- try {
39295
- const resolvedPath = path.resolve(envConfigDir);
39296
- if (fs2.existsSync(resolvedPath)) {
39297
- const stats = fs2.statSync(resolvedPath);
39298
- if (stats.isDirectory()) {
39299
- return resolvedPath;
39300
- }
39301
- } else {
39302
- return resolvedPath;
39303
- }
39304
- } catch {}
39305
- }
39306
- return path.join(os2.homedir(), ".claude");
39307
- }
39308
- function getClaudeSettingsPath() {
39309
- return path.join(getClaudeConfigDir(), "settings.json");
39310
- }
39311
- async function loadClaudeSettings() {
39312
- try {
39313
- const settingsPath = getClaudeSettingsPath();
39314
- if (!fs2.existsSync(settingsPath)) {
39315
- return {};
39316
- }
39317
- const content = await readFile(settingsPath, "utf-8");
39318
- return JSON.parse(content);
39319
- } catch {
39320
- return {};
39321
- }
39322
- }
39323
- async function saveClaudeSettings(settings) {
39324
- const settingsPath = getClaudeSettingsPath();
39325
- const dir = path.dirname(settingsPath);
39326
- await mkdir(dir, { recursive: true });
39327
- await writeFile(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
39328
- }
39329
- async function isInstalled() {
39330
- const settings = await loadClaudeSettings();
39331
- const validCommands = [
39332
- CCSTATUSLINE_COMMANDS.NPM,
39333
- CCSTATUSLINE_COMMANDS.BUNX,
39334
- CCSTATUSLINE_COMMANDS.SELF_MANAGED
39335
- ];
39336
- return validCommands.includes(settings.statusLine?.command ?? "") && (settings.statusLine?.padding === 0 || settings.statusLine?.padding === undefined);
39337
- }
39338
- function isBunxAvailable() {
39339
- try {
39340
- const command = process.platform === "win32" ? "where bunx" : "which bunx";
39341
- execSync(command, { stdio: "ignore" });
39342
- return true;
39343
- } catch {
39344
- return false;
39345
- }
39346
- }
39347
- async function installStatusLine(useBunx = false) {
39348
- const settings = await loadClaudeSettings();
39349
- settings.statusLine = {
39350
- type: "command",
39351
- command: useBunx ? CCSTATUSLINE_COMMANDS.BUNX : CCSTATUSLINE_COMMANDS.NPM,
39352
- padding: 0
39353
- };
39354
- await saveClaudeSettings(settings);
39355
- }
39356
- async function uninstallStatusLine() {
39357
- const settings = await loadClaudeSettings();
39358
- if (settings.statusLine) {
39359
- delete settings.statusLine;
39360
- await saveClaudeSettings(settings);
39361
- }
39362
- }
39363
- async function getExistingStatusLine() {
39364
- const settings = await loadClaudeSettings();
39365
- return settings.statusLine?.command ?? null;
39366
- }
39367
-
39368
- // src/utils/clone-settings.ts
39369
- function cloneSettings(settings) {
39370
- const cloneFn = globalThis.structuredClone;
39371
- if (typeof cloneFn === "function") {
39372
- return cloneFn(settings);
39373
- }
39374
- return JSON.parse(JSON.stringify(settings));
39375
- }
39376
-
39377
- // src/utils/config.ts
39378
39280
  import * as fs3 from "fs";
39379
39281
  import * as os3 from "os";
39380
39282
  import * as path2 from "path";
39381
39283
 
39284
+ // src/utils/config.ts
39285
+ import * as fs2 from "fs";
39286
+ import * as os2 from "os";
39287
+ import * as path from "path";
39288
+
39382
39289
  // node_modules/zod/v4/classic/external.js
39383
39290
  var exports_external = {};
39384
39291
  __export(exports_external, {
@@ -40060,15 +39967,15 @@ function mergeDefs(...defs) {
40060
39967
  function cloneDef(schema) {
40061
39968
  return mergeDefs(schema._zod.def);
40062
39969
  }
40063
- function getElementAtPath(obj, path2) {
40064
- if (!path2)
39970
+ function getElementAtPath(obj, path) {
39971
+ if (!path)
40065
39972
  return obj;
40066
- return path2.reduce((acc, key) => acc?.[key], obj);
39973
+ return path.reduce((acc, key) => acc?.[key], obj);
40067
39974
  }
40068
39975
  function promiseAllObject(promisesObj) {
40069
39976
  const keys = Object.keys(promisesObj);
40070
- const promises2 = keys.map((key) => promisesObj[key]);
40071
- return Promise.all(promises2).then((results) => {
39977
+ const promises = keys.map((key) => promisesObj[key]);
39978
+ return Promise.all(promises).then((results) => {
40072
39979
  const resolvedObj = {};
40073
39980
  for (let i = 0;i < keys.length; i++) {
40074
39981
  resolvedObj[keys[i]] = results[i];
@@ -40398,11 +40305,11 @@ function aborted(x, startIndex = 0) {
40398
40305
  }
40399
40306
  return false;
40400
40307
  }
40401
- function prefixIssues(path2, issues) {
40308
+ function prefixIssues(path, issues) {
40402
40309
  return issues.map((iss) => {
40403
40310
  var _a;
40404
40311
  (_a = iss).path ?? (_a.path = []);
40405
- iss.path.unshift(path2);
40312
+ iss.path.unshift(path);
40406
40313
  return iss;
40407
40314
  });
40408
40315
  }
@@ -40533,7 +40440,7 @@ function treeifyError(error, _mapper) {
40533
40440
  return issue2.message;
40534
40441
  };
40535
40442
  const result = { errors: [] };
40536
- const processError = (error2, path2 = []) => {
40443
+ const processError = (error2, path = []) => {
40537
40444
  var _a, _b;
40538
40445
  for (const issue2 of error2.issues) {
40539
40446
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -40543,7 +40450,7 @@ function treeifyError(error, _mapper) {
40543
40450
  } else if (issue2.code === "invalid_element") {
40544
40451
  processError({ issues: issue2.issues }, issue2.path);
40545
40452
  } else {
40546
- const fullpath = [...path2, ...issue2.path];
40453
+ const fullpath = [...path, ...issue2.path];
40547
40454
  if (fullpath.length === 0) {
40548
40455
  result.errors.push(mapper(issue2));
40549
40456
  continue;
@@ -40575,8 +40482,8 @@ function treeifyError(error, _mapper) {
40575
40482
  }
40576
40483
  function toDotPath(_path) {
40577
40484
  const segs = [];
40578
- const path2 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
40579
- for (const seg of path2) {
40485
+ const path = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
40486
+ for (const seg of path) {
40580
40487
  if (typeof seg === "number")
40581
40488
  segs.push(`[${seg}]`);
40582
40489
  else if (typeof seg === "symbol")
@@ -50999,6 +50906,84 @@ function generateGuid() {
50999
50906
  function isRecord(value) {
51000
50907
  return typeof value === "object" && value !== null && !Array.isArray(value);
51001
50908
  }
50909
+ var V1_FIELD_RULES = [
50910
+ {
50911
+ key: "flexMode",
50912
+ isValid: (value) => typeof value === "string"
50913
+ },
50914
+ {
50915
+ key: "compactThreshold",
50916
+ isValid: (value) => typeof value === "number"
50917
+ },
50918
+ {
50919
+ key: "colorLevel",
50920
+ isValid: (value) => typeof value === "number"
50921
+ },
50922
+ {
50923
+ key: "defaultSeparator",
50924
+ isValid: (value) => typeof value === "string"
50925
+ },
50926
+ {
50927
+ key: "defaultPadding",
50928
+ isValid: (value) => typeof value === "string"
50929
+ },
50930
+ {
50931
+ key: "inheritSeparatorColors",
50932
+ isValid: (value) => typeof value === "boolean"
50933
+ },
50934
+ {
50935
+ key: "overrideBackgroundColor",
50936
+ isValid: (value) => typeof value === "string"
50937
+ },
50938
+ {
50939
+ key: "overrideForegroundColor",
50940
+ isValid: (value) => typeof value === "string"
50941
+ },
50942
+ {
50943
+ key: "globalBold",
50944
+ isValid: (value) => typeof value === "boolean"
50945
+ }
50946
+ ];
50947
+ function toWidgetLine(line, stripSeparators) {
50948
+ const lineToProcess = stripSeparators ? line.filter((item) => {
50949
+ if (isRecord(item)) {
50950
+ return item.type !== "separator";
50951
+ }
50952
+ return true;
50953
+ }) : line;
50954
+ const typedLine = [];
50955
+ for (const item of lineToProcess) {
50956
+ if (isRecord(item) && typeof item.type === "string") {
50957
+ typedLine.push({
50958
+ ...item,
50959
+ id: generateGuid(),
50960
+ type: item.type
50961
+ });
50962
+ }
50963
+ }
50964
+ return typedLine;
50965
+ }
50966
+ function migrateV1Lines(data) {
50967
+ if (!Array.isArray(data.lines)) {
50968
+ return;
50969
+ }
50970
+ const stripSeparators = Boolean(data.defaultSeparator);
50971
+ const processedLines = [];
50972
+ for (const line of data.lines) {
50973
+ if (Array.isArray(line)) {
50974
+ processedLines.push(toWidgetLine(line, stripSeparators));
50975
+ }
50976
+ }
50977
+ return processedLines;
50978
+ }
50979
+ function copyV1Fields(data, target) {
50980
+ for (const rule of V1_FIELD_RULES) {
50981
+ const value = data[rule.key];
50982
+ if (rule.isValid(value)) {
50983
+ target[rule.key] = value;
50984
+ }
50985
+ }
50986
+ }
51002
50987
  var migrations = [
51003
50988
  {
51004
50989
  fromVersion: 1,
@@ -51006,52 +50991,11 @@ var migrations = [
51006
50991
  description: "Migrate from v1 to v2",
51007
50992
  migrate: (data) => {
51008
50993
  const migrated = {};
51009
- if (data.lines && Array.isArray(data.lines)) {
51010
- const processedLines = [];
51011
- for (const line of data.lines) {
51012
- if (Array.isArray(line)) {
51013
- let processedLine = line;
51014
- if (data.defaultSeparator) {
51015
- processedLine = line.filter((item) => {
51016
- if (isRecord(item)) {
51017
- return item.type !== "separator";
51018
- }
51019
- return true;
51020
- });
51021
- }
51022
- const typedLine = [];
51023
- for (const item of processedLine) {
51024
- if (isRecord(item) && typeof item.type === "string") {
51025
- typedLine.push({
51026
- ...item,
51027
- id: generateGuid(),
51028
- type: item.type
51029
- });
51030
- }
51031
- }
51032
- processedLines.push(typedLine);
51033
- }
51034
- }
50994
+ const processedLines = migrateV1Lines(data);
50995
+ if (processedLines) {
51035
50996
  migrated.lines = processedLines;
51036
50997
  }
51037
- if (typeof data.flexMode === "string")
51038
- migrated.flexMode = data.flexMode;
51039
- if (typeof data.compactThreshold === "number")
51040
- migrated.compactThreshold = data.compactThreshold;
51041
- if (typeof data.colorLevel === "number")
51042
- migrated.colorLevel = data.colorLevel;
51043
- if (typeof data.defaultSeparator === "string")
51044
- migrated.defaultSeparator = data.defaultSeparator;
51045
- if (typeof data.defaultPadding === "string")
51046
- migrated.defaultPadding = data.defaultPadding;
51047
- if (typeof data.inheritSeparatorColors === "boolean")
51048
- migrated.inheritSeparatorColors = data.inheritSeparatorColors;
51049
- if (typeof data.overrideBackgroundColor === "string")
51050
- migrated.overrideBackgroundColor = data.overrideBackgroundColor;
51051
- if (typeof data.overrideForegroundColor === "string")
51052
- migrated.overrideForegroundColor = data.overrideForegroundColor;
51053
- if (typeof data.globalBold === "boolean")
51054
- migrated.globalBold = data.globalBold;
50998
+ copyV1Fields(data, migrated);
51055
50999
  migrated.version = 2;
51056
51000
  migrated.updatemessage = {
51057
51001
  message: "ccstatusline updated to v2.0.0, launch tui to use new settings",
@@ -51101,85 +51045,224 @@ function needsMigration(data, targetVersion) {
51101
51045
  }
51102
51046
 
51103
51047
  // src/utils/config.ts
51104
- var readFile2 = fs3.promises.readFile;
51105
- var writeFile2 = fs3.promises.writeFile;
51106
- var mkdir2 = fs3.promises.mkdir;
51107
- var CONFIG_DIR = path2.join(os3.homedir(), ".config", "ccstatusline");
51108
- var SETTINGS_PATH = path2.join(CONFIG_DIR, "settings.json");
51109
- var SETTINGS_BACKUP_PATH = path2.join(CONFIG_DIR, "settings.bak");
51110
- async function backupBadSettings() {
51048
+ var readFile = fs2.promises.readFile;
51049
+ var writeFile = fs2.promises.writeFile;
51050
+ var mkdir = fs2.promises.mkdir;
51051
+ var DEFAULT_SETTINGS_PATH = path.join(os2.homedir(), ".config", "ccstatusline", "settings.json");
51052
+ var settingsPath = DEFAULT_SETTINGS_PATH;
51053
+ function initConfigPath(filePath) {
51054
+ settingsPath = filePath ? path.resolve(filePath) : DEFAULT_SETTINGS_PATH;
51055
+ }
51056
+ function getConfigPath() {
51057
+ return settingsPath;
51058
+ }
51059
+ function isCustomConfigPath() {
51060
+ return settingsPath !== DEFAULT_SETTINGS_PATH;
51061
+ }
51062
+ function getSettingsPaths() {
51063
+ const configDir = path.dirname(settingsPath);
51064
+ const parsedPath = path.parse(settingsPath);
51065
+ const backupBaseName = parsedPath.ext ? `${parsedPath.name}.bak` : `${parsedPath.base}.bak`;
51066
+ return {
51067
+ configDir,
51068
+ settingsPath,
51069
+ settingsBackupPath: path.join(configDir, backupBaseName)
51070
+ };
51071
+ }
51072
+ async function writeSettingsJson(settings, paths) {
51073
+ await mkdir(paths.configDir, { recursive: true });
51074
+ await writeFile(paths.settingsPath, JSON.stringify(settings, null, 2), "utf-8");
51075
+ }
51076
+ async function backupBadSettings(paths) {
51111
51077
  try {
51112
- if (fs3.existsSync(SETTINGS_PATH)) {
51113
- const content = await readFile2(SETTINGS_PATH, "utf-8");
51114
- await writeFile2(SETTINGS_BACKUP_PATH, content, "utf-8");
51115
- console.error(`Bad settings backed up to ${SETTINGS_BACKUP_PATH}`);
51078
+ if (fs2.existsSync(paths.settingsPath)) {
51079
+ const content = await readFile(paths.settingsPath, "utf-8");
51080
+ await writeFile(paths.settingsBackupPath, content, "utf-8");
51081
+ console.error(`Bad settings backed up to ${paths.settingsBackupPath}`);
51116
51082
  }
51117
51083
  } catch (error43) {
51118
51084
  console.error("Failed to backup bad settings:", error43);
51119
51085
  }
51120
51086
  }
51121
- async function writeDefaultSettings() {
51087
+ async function writeDefaultSettings(paths) {
51122
51088
  const defaults = SettingsSchema.parse({});
51123
51089
  const settingsWithVersion = {
51124
51090
  ...defaults,
51125
51091
  version: CURRENT_VERSION
51126
51092
  };
51127
51093
  try {
51128
- await mkdir2(CONFIG_DIR, { recursive: true });
51129
- await writeFile2(SETTINGS_PATH, JSON.stringify(settingsWithVersion, null, 2), "utf-8");
51130
- console.error(`Default settings written to ${SETTINGS_PATH}`);
51094
+ await writeSettingsJson(settingsWithVersion, paths);
51095
+ console.error(`Default settings written to ${paths.settingsPath}`);
51131
51096
  } catch (error43) {
51132
51097
  console.error("Failed to write default settings:", error43);
51133
51098
  }
51134
51099
  return defaults;
51135
51100
  }
51101
+ async function recoverWithDefaults(paths) {
51102
+ await backupBadSettings(paths);
51103
+ return await writeDefaultSettings(paths);
51104
+ }
51136
51105
  async function loadSettings() {
51106
+ const paths = getSettingsPaths();
51137
51107
  try {
51138
- if (!fs3.existsSync(SETTINGS_PATH))
51139
- return await writeDefaultSettings();
51140
- const content = await readFile2(SETTINGS_PATH, "utf-8");
51108
+ if (!fs2.existsSync(paths.settingsPath))
51109
+ return await writeDefaultSettings(paths);
51110
+ const content = await readFile(paths.settingsPath, "utf-8");
51141
51111
  let rawData;
51142
51112
  try {
51143
51113
  rawData = JSON.parse(content);
51144
51114
  } catch {
51145
51115
  console.error("Failed to parse settings.json, backing up and using defaults");
51146
- await backupBadSettings();
51147
- return await writeDefaultSettings();
51116
+ return await recoverWithDefaults(paths);
51148
51117
  }
51149
51118
  const hasVersion = typeof rawData === "object" && rawData !== null && "version" in rawData;
51150
51119
  if (!hasVersion) {
51151
51120
  const v1Result = SettingsSchema_v1.safeParse(rawData);
51152
51121
  if (!v1Result.success) {
51153
51122
  console.error("Invalid v1 settings format:", v1Result.error);
51154
- await backupBadSettings();
51155
- return await writeDefaultSettings();
51123
+ return await recoverWithDefaults(paths);
51156
51124
  }
51157
51125
  rawData = migrateConfig(rawData, CURRENT_VERSION);
51158
- await writeFile2(SETTINGS_PATH, JSON.stringify(rawData, null, 2), "utf-8");
51126
+ await writeSettingsJson(rawData, paths);
51159
51127
  } else if (needsMigration(rawData, CURRENT_VERSION)) {
51160
51128
  rawData = migrateConfig(rawData, CURRENT_VERSION);
51161
- await writeFile2(SETTINGS_PATH, JSON.stringify(rawData, null, 2), "utf-8");
51129
+ await writeSettingsJson(rawData, paths);
51162
51130
  }
51163
51131
  const result = SettingsSchema.safeParse(rawData);
51164
51132
  if (!result.success) {
51165
51133
  console.error("Failed to parse settings:", result.error);
51166
- await backupBadSettings();
51167
- return await writeDefaultSettings();
51134
+ return await recoverWithDefaults(paths);
51168
51135
  }
51169
51136
  return result.data;
51170
51137
  } catch (error43) {
51171
51138
  console.error("Error loading settings:", error43);
51172
- await backupBadSettings();
51173
- return await writeDefaultSettings();
51139
+ return await recoverWithDefaults(paths);
51174
51140
  }
51175
51141
  }
51176
51142
  async function saveSettings(settings) {
51177
- await mkdir2(CONFIG_DIR, { recursive: true });
51143
+ const paths = getSettingsPaths();
51178
51144
  const settingsWithVersion = {
51179
51145
  ...settings,
51180
51146
  version: CURRENT_VERSION
51181
51147
  };
51182
- await writeFile2(SETTINGS_PATH, JSON.stringify(settingsWithVersion, null, 2), "utf-8");
51148
+ await writeSettingsJson(settingsWithVersion, paths);
51149
+ }
51150
+
51151
+ // src/utils/claude-settings.ts
51152
+ var readFile2 = fs3.promises.readFile;
51153
+ var writeFile2 = fs3.promises.writeFile;
51154
+ var mkdir2 = fs3.promises.mkdir;
51155
+ var CCSTATUSLINE_COMMANDS = {
51156
+ NPM: "npx -y ccstatusline@latest",
51157
+ BUNX: "bunx -y ccstatusline@latest",
51158
+ SELF_MANAGED: "ccstatusline"
51159
+ };
51160
+ function isKnownCommand(command) {
51161
+ const prefixes = [CCSTATUSLINE_COMMANDS.NPM, CCSTATUSLINE_COMMANDS.BUNX, CCSTATUSLINE_COMMANDS.SELF_MANAGED];
51162
+ return prefixes.some((prefix) => command === prefix || command.startsWith(`${prefix} --config `));
51163
+ }
51164
+ function needsQuoting(filePath) {
51165
+ if (process.platform === "win32") {
51166
+ return /[\s&()<>|^"]/.test(filePath);
51167
+ }
51168
+ return /[\s()[\];&#|'"\\$`]/.test(filePath);
51169
+ }
51170
+ function quotePathIfNeeded(filePath) {
51171
+ if (!needsQuoting(filePath)) {
51172
+ return filePath;
51173
+ }
51174
+ if (process.platform === "win32") {
51175
+ return `"${filePath.replace(/"/g, '""')}"`;
51176
+ }
51177
+ return `'${filePath.replace(/'/g, "'\\''")}'`;
51178
+ }
51179
+ function getClaudeConfigDir() {
51180
+ const envConfigDir = process.env.CLAUDE_CONFIG_DIR;
51181
+ if (envConfigDir) {
51182
+ try {
51183
+ const resolvedPath = path2.resolve(envConfigDir);
51184
+ if (fs3.existsSync(resolvedPath)) {
51185
+ const stats = fs3.statSync(resolvedPath);
51186
+ if (stats.isDirectory()) {
51187
+ return resolvedPath;
51188
+ }
51189
+ } else {
51190
+ return resolvedPath;
51191
+ }
51192
+ } catch {}
51193
+ }
51194
+ return path2.join(os3.homedir(), ".claude");
51195
+ }
51196
+ function getClaudeSettingsPath() {
51197
+ return path2.join(getClaudeConfigDir(), "settings.json");
51198
+ }
51199
+ async function loadClaudeSettings() {
51200
+ try {
51201
+ const settingsPath2 = getClaudeSettingsPath();
51202
+ if (!fs3.existsSync(settingsPath2)) {
51203
+ return {};
51204
+ }
51205
+ const content = await readFile2(settingsPath2, "utf-8");
51206
+ return JSON.parse(content);
51207
+ } catch {
51208
+ return {};
51209
+ }
51210
+ }
51211
+ async function saveClaudeSettings(settings) {
51212
+ const settingsPath2 = getClaudeSettingsPath();
51213
+ const dir = path2.dirname(settingsPath2);
51214
+ await mkdir2(dir, { recursive: true });
51215
+ await writeFile2(settingsPath2, JSON.stringify(settings, null, 2), "utf-8");
51216
+ }
51217
+ async function isInstalled() {
51218
+ const settings = await loadClaudeSettings();
51219
+ const command = settings.statusLine?.command ?? "";
51220
+ return isKnownCommand(command) && (settings.statusLine?.padding === 0 || settings.statusLine?.padding === undefined);
51221
+ }
51222
+ function isBunxAvailable() {
51223
+ try {
51224
+ const command = process.platform === "win32" ? "where bunx" : "which bunx";
51225
+ execSync(command, { stdio: "ignore" });
51226
+ return true;
51227
+ } catch {
51228
+ return false;
51229
+ }
51230
+ }
51231
+ function buildCommand(baseCommand) {
51232
+ if (isCustomConfigPath()) {
51233
+ return `${baseCommand} --config ${quotePathIfNeeded(getConfigPath())}`;
51234
+ }
51235
+ return baseCommand;
51236
+ }
51237
+ async function installStatusLine(useBunx = false) {
51238
+ const settings = await loadClaudeSettings();
51239
+ const baseCommand = useBunx ? CCSTATUSLINE_COMMANDS.BUNX : CCSTATUSLINE_COMMANDS.NPM;
51240
+ settings.statusLine = {
51241
+ type: "command",
51242
+ command: buildCommand(baseCommand),
51243
+ padding: 0
51244
+ };
51245
+ await saveClaudeSettings(settings);
51246
+ }
51247
+ async function uninstallStatusLine() {
51248
+ const settings = await loadClaudeSettings();
51249
+ if (settings.statusLine) {
51250
+ delete settings.statusLine;
51251
+ await saveClaudeSettings(settings);
51252
+ }
51253
+ }
51254
+ async function getExistingStatusLine() {
51255
+ const settings = await loadClaudeSettings();
51256
+ return settings.statusLine?.command ?? null;
51257
+ }
51258
+
51259
+ // src/utils/clone-settings.ts
51260
+ function cloneSettings(settings) {
51261
+ const cloneFn = globalThis.structuredClone;
51262
+ if (typeof cloneFn === "function") {
51263
+ return cloneFn(settings);
51264
+ }
51265
+ return JSON.parse(JSON.stringify(settings));
51183
51266
  }
51184
51267
 
51185
51268
  // src/utils/open-url.ts
@@ -51201,6 +51284,32 @@ function runOpenCommand(command, args) {
51201
51284
  }
51202
51285
  return null;
51203
51286
  }
51287
+ var PLATFORM_OPEN_PLANS = {
51288
+ darwin: [
51289
+ {
51290
+ command: "open",
51291
+ args: (url2) => [url2]
51292
+ }
51293
+ ],
51294
+ win32: [
51295
+ {
51296
+ command: "cmd",
51297
+ args: (url2) => ["/c", "start", "", url2]
51298
+ }
51299
+ ],
51300
+ linux: [
51301
+ {
51302
+ command: "xdg-open",
51303
+ args: (url2) => [url2],
51304
+ errorPrefix: "xdg-open failed: "
51305
+ },
51306
+ {
51307
+ command: "gio",
51308
+ args: (url2) => ["open", url2],
51309
+ errorPrefix: "gio open failed: "
51310
+ }
51311
+ ]
51312
+ };
51204
51313
  function openExternalUrl(url2) {
51205
51314
  let parsedUrl;
51206
51315
  try {
@@ -51218,31 +51327,28 @@ function openExternalUrl(url2) {
51218
51327
  };
51219
51328
  }
51220
51329
  const platform3 = os4.platform();
51221
- if (platform3 === "darwin") {
51222
- const commandError = runOpenCommand("open", [url2]);
51223
- return commandError ? { success: false, error: commandError } : { success: true };
51224
- }
51225
- if (platform3 === "win32") {
51226
- const commandError = runOpenCommand("cmd", ["/c", "start", "", url2]);
51227
- return commandError ? { success: false, error: commandError } : { success: true };
51330
+ const plans = PLATFORM_OPEN_PLANS[platform3];
51331
+ if (!plans) {
51332
+ return {
51333
+ success: false,
51334
+ error: `Unsupported platform: ${platform3}`
51335
+ };
51228
51336
  }
51229
- if (platform3 === "linux") {
51230
- const xdgError = runOpenCommand("xdg-open", [url2]);
51231
- if (!xdgError) {
51337
+ const errors3 = [];
51338
+ for (const plan of plans) {
51339
+ const commandError = runOpenCommand(plan.command, plan.args(url2));
51340
+ if (!commandError) {
51232
51341
  return { success: true };
51233
51342
  }
51234
- const gioError = runOpenCommand("gio", ["open", url2]);
51235
- if (!gioError) {
51236
- return { success: true };
51343
+ if (plan.errorPrefix) {
51344
+ errors3.push(`${plan.errorPrefix}${commandError}`);
51345
+ } else {
51346
+ errors3.push(commandError);
51237
51347
  }
51238
- return {
51239
- success: false,
51240
- error: `xdg-open failed: ${xdgError}; gio open failed: ${gioError}`
51241
- };
51242
51348
  }
51243
51349
  return {
51244
51350
  success: false,
51245
- error: `Unsupported platform: ${platform3}`
51351
+ error: errors3.join("; ")
51246
51352
  };
51247
51353
  }
51248
51354
 
@@ -51478,7 +51584,7 @@ import { execSync as execSync3 } from "child_process";
51478
51584
  import * as fs5 from "fs";
51479
51585
  import * as path4 from "path";
51480
51586
  var __dirname = "/Users/sirmalloc/Projects/Personal/ccstatusline/src/utils";
51481
- var PACKAGE_VERSION = "2.1.6";
51587
+ var PACKAGE_VERSION = "2.1.8";
51482
51588
  function getPackageVersion() {
51483
51589
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
51484
51590
  return PACKAGE_VERSION;
@@ -54566,54 +54672,309 @@ var CustomCommandEditor = ({ widget, onComplete, onCancel, action }) => {
54566
54672
  children: "Unknown editor mode"
54567
54673
  }, undefined, false, undefined, this);
54568
54674
  };
54569
- // src/utils/usage.ts
54570
- import {
54571
- execSync as execSync6,
54572
- spawnSync as spawnSync2
54573
- } from "child_process";
54574
- import * as fs7 from "fs";
54575
- import * as os7 from "os";
54576
- import * as path7 from "path";
54577
-
54578
- // src/utils/jsonl.ts
54675
+ // src/utils/usage-fetch.ts
54676
+ import { execSync as execSync6 } from "child_process";
54579
54677
  import * as fs6 from "fs";
54580
- import { createHash } from "node:crypto";
54581
- import os6 from "node:os";
54582
- import path6 from "node:path";
54678
+ import * as https from "https";
54679
+ import * as os6 from "os";
54680
+ import * as path5 from "path";
54583
54681
 
54584
- // node_modules/tinyglobby/dist/index.mjs
54585
- import path5, { posix } from "path";
54682
+ // src/utils/usage-types.ts
54683
+ var FIVE_HOUR_BLOCK_MS = 5 * 60 * 60 * 1000;
54684
+ var SEVEN_DAY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
54685
+ var UsageErrorSchema = exports_external.enum(["no-credentials", "timeout", "api-error", "parse-error"]);
54586
54686
 
54587
- // node_modules/fdir/dist/index.mjs
54588
- import { createRequire as createRequire2 } from "module";
54589
- import { basename as basename2, dirname as dirname2, normalize, relative, resolve as resolve2, sep } from "path";
54590
- import * as nativeFs from "fs";
54591
- var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
54592
- function cleanPath(path5) {
54593
- let normalized = normalize(path5);
54687
+ // src/utils/usage-fetch.ts
54688
+ var CACHE_DIR = path5.join(os6.homedir(), ".cache", "ccstatusline");
54689
+ var CACHE_FILE = path5.join(CACHE_DIR, "usage.json");
54690
+ var LOCK_FILE = path5.join(CACHE_DIR, "usage.lock");
54691
+ var CACHE_MAX_AGE = 180;
54692
+ var LOCK_MAX_AGE = 30;
54693
+ var TOKEN_CACHE_MAX_AGE = 3600;
54694
+ var UsageCredentialsSchema = exports_external.object({ claudeAiOauth: exports_external.object({ accessToken: exports_external.string().nullable().optional() }).optional() });
54695
+ var CachedUsageDataSchema = exports_external.object({
54696
+ sessionUsage: exports_external.number().nullable().optional(),
54697
+ sessionResetAt: exports_external.string().nullable().optional(),
54698
+ weeklyUsage: exports_external.number().nullable().optional(),
54699
+ weeklyResetAt: exports_external.string().nullable().optional(),
54700
+ extraUsageEnabled: exports_external.boolean().nullable().optional(),
54701
+ extraUsageLimit: exports_external.number().nullable().optional(),
54702
+ extraUsageUsed: exports_external.number().nullable().optional(),
54703
+ extraUsageUtilization: exports_external.number().nullable().optional(),
54704
+ error: exports_external.string().nullable().optional()
54705
+ });
54706
+ var UsageApiResponseSchema = exports_external.object({
54707
+ five_hour: exports_external.object({
54708
+ utilization: exports_external.number().nullable().optional(),
54709
+ resets_at: exports_external.string().nullable().optional()
54710
+ }).optional(),
54711
+ seven_day: exports_external.object({
54712
+ utilization: exports_external.number().nullable().optional(),
54713
+ resets_at: exports_external.string().nullable().optional()
54714
+ }).optional(),
54715
+ extra_usage: exports_external.object({
54716
+ is_enabled: exports_external.boolean().nullable().optional(),
54717
+ monthly_limit: exports_external.number().nullable().optional(),
54718
+ used_credits: exports_external.number().nullable().optional(),
54719
+ utilization: exports_external.number().nullable().optional()
54720
+ }).optional()
54721
+ });
54722
+ function parseJsonWithSchema(rawJson, schema) {
54723
+ try {
54724
+ const parsed = schema.safeParse(JSON.parse(rawJson));
54725
+ return parsed.success ? parsed.data : null;
54726
+ } catch {
54727
+ return null;
54728
+ }
54729
+ }
54730
+ function parseUsageAccessToken(rawJson) {
54731
+ const parsed = parseJsonWithSchema(rawJson, UsageCredentialsSchema);
54732
+ return parsed?.claudeAiOauth?.accessToken ?? null;
54733
+ }
54734
+ function parseCachedUsageData(rawJson) {
54735
+ const parsed = parseJsonWithSchema(rawJson, CachedUsageDataSchema);
54736
+ if (!parsed) {
54737
+ return null;
54738
+ }
54739
+ const parsedError = UsageErrorSchema.safeParse(parsed.error);
54740
+ return {
54741
+ sessionUsage: parsed.sessionUsage ?? undefined,
54742
+ sessionResetAt: parsed.sessionResetAt ?? undefined,
54743
+ weeklyUsage: parsed.weeklyUsage ?? undefined,
54744
+ weeklyResetAt: parsed.weeklyResetAt ?? undefined,
54745
+ extraUsageEnabled: parsed.extraUsageEnabled ?? undefined,
54746
+ extraUsageLimit: parsed.extraUsageLimit ?? undefined,
54747
+ extraUsageUsed: parsed.extraUsageUsed ?? undefined,
54748
+ extraUsageUtilization: parsed.extraUsageUtilization ?? undefined,
54749
+ error: parsedError.success ? parsedError.data : undefined
54750
+ };
54751
+ }
54752
+ function parseUsageApiResponse(rawJson) {
54753
+ const parsed = parseJsonWithSchema(rawJson, UsageApiResponseSchema);
54754
+ if (!parsed) {
54755
+ return null;
54756
+ }
54757
+ return {
54758
+ sessionUsage: parsed.five_hour?.utilization ?? undefined,
54759
+ sessionResetAt: parsed.five_hour?.resets_at ?? undefined,
54760
+ weeklyUsage: parsed.seven_day?.utilization ?? undefined,
54761
+ weeklyResetAt: parsed.seven_day?.resets_at ?? undefined,
54762
+ extraUsageEnabled: parsed.extra_usage?.is_enabled ?? undefined,
54763
+ extraUsageLimit: parsed.extra_usage?.monthly_limit ?? undefined,
54764
+ extraUsageUsed: parsed.extra_usage?.used_credits ?? undefined,
54765
+ extraUsageUtilization: parsed.extra_usage?.utilization ?? undefined
54766
+ };
54767
+ }
54768
+ var cachedUsageData = null;
54769
+ var usageCacheTime = 0;
54770
+ var cachedUsageToken = null;
54771
+ var usageTokenCacheTime = 0;
54772
+ function setCachedUsageError(error43, now) {
54773
+ const errorData = { error: error43 };
54774
+ cachedUsageData = errorData;
54775
+ usageCacheTime = now;
54776
+ return errorData;
54777
+ }
54778
+ function getStaleUsageOrError(error43, now) {
54779
+ const stale = readStaleUsageCache();
54780
+ if (stale && !stale.error) {
54781
+ cachedUsageData = stale;
54782
+ usageCacheTime = now;
54783
+ return stale;
54784
+ }
54785
+ return setCachedUsageError(error43, now);
54786
+ }
54787
+ function getUsageToken() {
54788
+ const now = Math.floor(Date.now() / 1000);
54789
+ if (cachedUsageToken && now - usageTokenCacheTime < TOKEN_CACHE_MAX_AGE) {
54790
+ return cachedUsageToken;
54791
+ }
54792
+ try {
54793
+ const isMac = process.platform === "darwin";
54794
+ if (isMac) {
54795
+ const result = execSync6('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
54796
+ const token2 = parseUsageAccessToken(result);
54797
+ if (token2) {
54798
+ cachedUsageToken = token2;
54799
+ usageTokenCacheTime = now;
54800
+ }
54801
+ return token2;
54802
+ }
54803
+ const credFile = path5.join(getClaudeConfigDir(), ".credentials.json");
54804
+ const token = parseUsageAccessToken(fs6.readFileSync(credFile, "utf8"));
54805
+ if (token) {
54806
+ cachedUsageToken = token;
54807
+ usageTokenCacheTime = now;
54808
+ }
54809
+ return token;
54810
+ } catch {
54811
+ return null;
54812
+ }
54813
+ }
54814
+ function readStaleUsageCache() {
54815
+ try {
54816
+ return parseCachedUsageData(fs6.readFileSync(CACHE_FILE, "utf8"));
54817
+ } catch {
54818
+ return null;
54819
+ }
54820
+ }
54821
+ var USAGE_API_HOST = "api.anthropic.com";
54822
+ var USAGE_API_PATH = "/api/oauth/usage";
54823
+ async function fetchFromUsageApi(token) {
54824
+ return new Promise((resolve3) => {
54825
+ let settled = false;
54826
+ const finish = (value) => {
54827
+ if (settled) {
54828
+ return;
54829
+ }
54830
+ settled = true;
54831
+ resolve3(value);
54832
+ };
54833
+ const request2 = https.request({
54834
+ hostname: USAGE_API_HOST,
54835
+ path: USAGE_API_PATH,
54836
+ method: "GET",
54837
+ headers: {
54838
+ Authorization: `Bearer ${token}`,
54839
+ "anthropic-beta": "oauth-2025-04-20"
54840
+ },
54841
+ timeout: 5000
54842
+ }, (response) => {
54843
+ let data = "";
54844
+ response.setEncoding("utf8");
54845
+ response.on("data", (chunk) => {
54846
+ data += chunk;
54847
+ });
54848
+ response.on("end", () => {
54849
+ if (response.statusCode === 200 && data) {
54850
+ finish(data);
54851
+ return;
54852
+ }
54853
+ finish(null);
54854
+ });
54855
+ });
54856
+ request2.on("error", () => {
54857
+ finish(null);
54858
+ });
54859
+ request2.on("timeout", () => {
54860
+ request2.destroy();
54861
+ finish(null);
54862
+ });
54863
+ request2.end();
54864
+ });
54865
+ }
54866
+ async function fetchUsageData() {
54867
+ const now = Math.floor(Date.now() / 1000);
54868
+ if (cachedUsageData) {
54869
+ const cacheAge = now - usageCacheTime;
54870
+ if (!cachedUsageData.error && cacheAge < CACHE_MAX_AGE) {
54871
+ return cachedUsageData;
54872
+ }
54873
+ if (cachedUsageData.error && cacheAge < LOCK_MAX_AGE) {
54874
+ return cachedUsageData;
54875
+ }
54876
+ }
54877
+ try {
54878
+ const stat = fs6.statSync(CACHE_FILE);
54879
+ const fileAge = now - Math.floor(stat.mtimeMs / 1000);
54880
+ if (fileAge < CACHE_MAX_AGE) {
54881
+ const fileData = parseCachedUsageData(fs6.readFileSync(CACHE_FILE, "utf8"));
54882
+ if (fileData && !fileData.error) {
54883
+ cachedUsageData = fileData;
54884
+ usageCacheTime = now;
54885
+ return fileData;
54886
+ }
54887
+ }
54888
+ } catch {}
54889
+ const token = getUsageToken();
54890
+ if (!token) {
54891
+ return getStaleUsageOrError("no-credentials", now);
54892
+ }
54893
+ try {
54894
+ const lockStat = fs6.statSync(LOCK_FILE);
54895
+ const lockAge = now - Math.floor(lockStat.mtimeMs / 1000);
54896
+ if (lockAge < LOCK_MAX_AGE) {
54897
+ const stale = readStaleUsageCache();
54898
+ if (stale && !stale.error)
54899
+ return stale;
54900
+ return { error: "timeout" };
54901
+ }
54902
+ } catch {}
54903
+ try {
54904
+ const lockDir = path5.dirname(LOCK_FILE);
54905
+ if (!fs6.existsSync(lockDir)) {
54906
+ fs6.mkdirSync(lockDir, { recursive: true });
54907
+ }
54908
+ fs6.writeFileSync(LOCK_FILE, "");
54909
+ } catch {}
54910
+ try {
54911
+ const response = await fetchFromUsageApi(token);
54912
+ if (!response) {
54913
+ return getStaleUsageOrError("api-error", now);
54914
+ }
54915
+ const usageData = parseUsageApiResponse(response);
54916
+ if (!usageData) {
54917
+ return getStaleUsageOrError("parse-error", now);
54918
+ }
54919
+ if (usageData.sessionUsage === undefined && usageData.weeklyUsage === undefined) {
54920
+ return getStaleUsageOrError("parse-error", now);
54921
+ }
54922
+ try {
54923
+ if (!fs6.existsSync(CACHE_DIR)) {
54924
+ fs6.mkdirSync(CACHE_DIR, { recursive: true });
54925
+ }
54926
+ fs6.writeFileSync(CACHE_FILE, JSON.stringify(usageData));
54927
+ } catch {}
54928
+ cachedUsageData = usageData;
54929
+ usageCacheTime = now;
54930
+ return usageData;
54931
+ } catch {
54932
+ return getStaleUsageOrError("parse-error", now);
54933
+ }
54934
+ }
54935
+ // src/utils/jsonl-cache.ts
54936
+ import * as fs9 from "fs";
54937
+ import { createHash } from "node:crypto";
54938
+ import os7 from "node:os";
54939
+ import path8 from "node:path";
54940
+
54941
+ // src/utils/jsonl-blocks.ts
54942
+ import * as fs8 from "fs";
54943
+ import path7 from "node:path";
54944
+
54945
+ // node_modules/tinyglobby/dist/index.mjs
54946
+ import path6, { posix } from "path";
54947
+
54948
+ // node_modules/fdir/dist/index.mjs
54949
+ import { createRequire as createRequire2 } from "module";
54950
+ import { basename as basename2, dirname as dirname4, normalize, relative, resolve as resolve3, sep } from "path";
54951
+ import * as nativeFs from "fs";
54952
+ var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
54953
+ function cleanPath(path6) {
54954
+ let normalized = normalize(path6);
54594
54955
  if (normalized.length > 1 && normalized[normalized.length - 1] === sep)
54595
54956
  normalized = normalized.substring(0, normalized.length - 1);
54596
54957
  return normalized;
54597
54958
  }
54598
54959
  var SLASHES_REGEX = /[\\/]/g;
54599
- function convertSlashes(path5, separator) {
54600
- return path5.replace(SLASHES_REGEX, separator);
54960
+ function convertSlashes(path6, separator) {
54961
+ return path6.replace(SLASHES_REGEX, separator);
54601
54962
  }
54602
54963
  var WINDOWS_ROOT_DIR_REGEX = /^[a-z]:[\\/]$/i;
54603
- function isRootDirectory(path5) {
54604
- return path5 === "/" || WINDOWS_ROOT_DIR_REGEX.test(path5);
54964
+ function isRootDirectory(path6) {
54965
+ return path6 === "/" || WINDOWS_ROOT_DIR_REGEX.test(path6);
54605
54966
  }
54606
- function normalizePath(path5, options) {
54967
+ function normalizePath(path6, options) {
54607
54968
  const { resolvePaths, normalizePath: normalizePath$1, pathSeparator } = options;
54608
- const pathNeedsCleaning = process.platform === "win32" && path5.includes("/") || path5.startsWith(".");
54969
+ const pathNeedsCleaning = process.platform === "win32" && path6.includes("/") || path6.startsWith(".");
54609
54970
  if (resolvePaths)
54610
- path5 = resolve2(path5);
54971
+ path6 = resolve3(path6);
54611
54972
  if (normalizePath$1 || pathNeedsCleaning)
54612
- path5 = cleanPath(path5);
54613
- if (path5 === ".")
54973
+ path6 = cleanPath(path6);
54974
+ if (path6 === ".")
54614
54975
  return "";
54615
- const needsSeperator = path5[path5.length - 1] !== pathSeparator;
54616
- return convertSlashes(needsSeperator ? path5 + pathSeparator : path5, pathSeparator);
54976
+ const needsSeperator = path6[path6.length - 1] !== pathSeparator;
54977
+ return convertSlashes(needsSeperator ? path6 + pathSeparator : path6, pathSeparator);
54617
54978
  }
54618
54979
  function joinPathWithBasePath(filename, directoryPath) {
54619
54980
  return directoryPath + filename;
@@ -54653,9 +55014,9 @@ var pushDirectory = (directoryPath, paths) => {
54653
55014
  paths.push(directoryPath || ".");
54654
55015
  };
54655
55016
  var pushDirectoryFilter = (directoryPath, paths, filters) => {
54656
- const path5 = directoryPath || ".";
54657
- if (filters.every((filter) => filter(path5, true)))
54658
- paths.push(path5);
55017
+ const path6 = directoryPath || ".";
55018
+ if (filters.every((filter) => filter(path6, true)))
55019
+ paths.push(path6);
54659
55020
  };
54660
55021
  var empty$2 = () => {};
54661
55022
  function build$6(root, options) {
@@ -54712,29 +55073,29 @@ var empty = () => {};
54712
55073
  function build$3(options) {
54713
55074
  return options.group ? groupFiles : empty;
54714
55075
  }
54715
- var resolveSymlinksAsync = function(path5, state, callback$1) {
54716
- const { queue, fs: fs6, options: { suppressErrors } } = state;
55076
+ var resolveSymlinksAsync = function(path6, state, callback$1) {
55077
+ const { queue, fs: fs7, options: { suppressErrors } } = state;
54717
55078
  queue.enqueue();
54718
- fs6.realpath(path5, (error43, resolvedPath) => {
55079
+ fs7.realpath(path6, (error43, resolvedPath) => {
54719
55080
  if (error43)
54720
55081
  return queue.dequeue(suppressErrors ? null : error43, state);
54721
- fs6.stat(resolvedPath, (error$1, stat) => {
55082
+ fs7.stat(resolvedPath, (error$1, stat) => {
54722
55083
  if (error$1)
54723
55084
  return queue.dequeue(suppressErrors ? null : error$1, state);
54724
- if (stat.isDirectory() && isRecursive(path5, resolvedPath, state))
55085
+ if (stat.isDirectory() && isRecursive(path6, resolvedPath, state))
54725
55086
  return queue.dequeue(null, state);
54726
55087
  callback$1(stat, resolvedPath);
54727
55088
  queue.dequeue(null, state);
54728
55089
  });
54729
55090
  });
54730
55091
  };
54731
- var resolveSymlinks = function(path5, state, callback$1) {
54732
- const { queue, fs: fs6, options: { suppressErrors } } = state;
55092
+ var resolveSymlinks = function(path6, state, callback$1) {
55093
+ const { queue, fs: fs7, options: { suppressErrors } } = state;
54733
55094
  queue.enqueue();
54734
55095
  try {
54735
- const resolvedPath = fs6.realpathSync(path5);
54736
- const stat = fs6.statSync(resolvedPath);
54737
- if (stat.isDirectory() && isRecursive(path5, resolvedPath, state))
55096
+ const resolvedPath = fs7.realpathSync(path6);
55097
+ const stat = fs7.statSync(resolvedPath);
55098
+ if (stat.isDirectory() && isRecursive(path6, resolvedPath, state))
54738
55099
  return;
54739
55100
  callback$1(stat, resolvedPath);
54740
55101
  } catch (e) {
@@ -54747,10 +55108,10 @@ function build$2(options, isSynchronous) {
54747
55108
  return null;
54748
55109
  return isSynchronous ? resolveSymlinks : resolveSymlinksAsync;
54749
55110
  }
54750
- function isRecursive(path5, resolved, state) {
55111
+ function isRecursive(path6, resolved, state) {
54751
55112
  if (state.options.useRealPaths)
54752
55113
  return isRecursiveUsingRealPaths(resolved, state);
54753
- let parent = dirname2(path5);
55114
+ let parent = dirname4(path6);
54754
55115
  let depth = 1;
54755
55116
  while (parent !== state.root && depth < 2) {
54756
55117
  const resolvedPath = state.symlinks.get(parent);
@@ -54758,9 +55119,9 @@ function isRecursive(path5, resolved, state) {
54758
55119
  if (isSameRoot)
54759
55120
  depth++;
54760
55121
  else
54761
- parent = dirname2(parent);
55122
+ parent = dirname4(parent);
54762
55123
  }
54763
- state.symlinks.set(path5, resolved);
55124
+ state.symlinks.set(path6, resolved);
54764
55125
  return depth > 1;
54765
55126
  }
54766
55127
  function isRecursiveUsingRealPaths(resolved, state) {
@@ -54816,23 +55177,23 @@ var walkAsync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
54816
55177
  state.queue.enqueue();
54817
55178
  if (currentDepth < 0)
54818
55179
  return state.queue.dequeue(null, state);
54819
- const { fs: fs6 } = state;
55180
+ const { fs: fs7 } = state;
54820
55181
  state.visited.push(crawlPath);
54821
55182
  state.counts.directories++;
54822
- fs6.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
55183
+ fs7.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
54823
55184
  callback$1(entries, directoryPath, currentDepth);
54824
55185
  state.queue.dequeue(state.options.suppressErrors ? null : error43, state);
54825
55186
  });
54826
55187
  };
54827
55188
  var walkSync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
54828
- const { fs: fs6 } = state;
55189
+ const { fs: fs7 } = state;
54829
55190
  if (currentDepth < 0)
54830
55191
  return;
54831
55192
  state.visited.push(crawlPath);
54832
55193
  state.counts.directories++;
54833
55194
  let entries = [];
54834
55195
  try {
54835
- entries = fs6.readdirSync(crawlPath || ".", readdirOpts);
55196
+ entries = fs7.readdirSync(crawlPath || ".", readdirOpts);
54836
55197
  } catch (e) {
54837
55198
  if (!state.options.suppressErrors)
54838
55199
  throw e;
@@ -54938,23 +55299,23 @@ var Walker = class {
54938
55299
  const filename = this.joinPath(entry.name, directoryPath);
54939
55300
  this.pushFile(filename, files, this.state.counts, filters);
54940
55301
  } else if (entry.isDirectory()) {
54941
- let path5 = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
54942
- if (exclude && exclude(entry.name, path5))
55302
+ let path6 = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
55303
+ if (exclude && exclude(entry.name, path6))
54943
55304
  continue;
54944
- this.pushDirectory(path5, paths, filters);
54945
- this.walkDirectory(this.state, path5, path5, depth - 1, this.walk);
55305
+ this.pushDirectory(path6, paths, filters);
55306
+ this.walkDirectory(this.state, path6, path6, depth - 1, this.walk);
54946
55307
  } else if (this.resolveSymlink && entry.isSymbolicLink()) {
54947
- let path5 = joinPathWithBasePath(entry.name, directoryPath);
54948
- this.resolveSymlink(path5, this.state, (stat, resolvedPath) => {
55308
+ let path6 = joinPathWithBasePath(entry.name, directoryPath);
55309
+ this.resolveSymlink(path6, this.state, (stat, resolvedPath) => {
54949
55310
  if (stat.isDirectory()) {
54950
55311
  resolvedPath = normalizePath(resolvedPath, this.state.options);
54951
- if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path5 + pathSeparator))
55312
+ if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path6 + pathSeparator))
54952
55313
  return;
54953
- this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path5 + pathSeparator, depth - 1, this.walk);
55314
+ this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path6 + pathSeparator, depth - 1, this.walk);
54954
55315
  } else {
54955
- resolvedPath = useRealPaths ? resolvedPath : path5;
55316
+ resolvedPath = useRealPaths ? resolvedPath : path6;
54956
55317
  const filename = basename2(resolvedPath);
54957
- const directoryPath$1 = normalizePath(dirname2(resolvedPath), this.state.options);
55318
+ const directoryPath$1 = normalizePath(dirname4(resolvedPath), this.state.options);
54958
55319
  resolvedPath = this.joinPath(filename, directoryPath$1);
54959
55320
  this.pushFile(resolvedPath, files, this.state.counts, filters);
54960
55321
  }
@@ -55112,7 +55473,7 @@ var Builder = class {
55112
55473
  isMatch = globFn(patterns, ...options);
55113
55474
  this.globCache[patterns.join("\x00")] = isMatch;
55114
55475
  }
55115
- this.options.filters.push((path5) => isMatch(path5));
55476
+ this.options.filters.push((path6) => isMatch(path6));
55116
55477
  return this;
55117
55478
  }
55118
55479
  };
@@ -55191,7 +55552,7 @@ function normalizePattern(pattern, expandDirectories, cwd2, props, isIgnore) {
55191
55552
  if (!result.endsWith("*") && expandDirectories)
55192
55553
  result += "/**";
55193
55554
  const escapedCwd = escapePath(cwd2);
55194
- if (path5.isAbsolute(result.replace(ESCAPING_BACKSLASHES, "")))
55555
+ if (path6.isAbsolute(result.replace(ESCAPING_BACKSLASHES, "")))
55195
55556
  result = posix.relative(escapedCwd, result);
55196
55557
  else
55197
55558
  result = posix.normalize(result);
@@ -55228,7 +55589,7 @@ function normalizePattern(pattern, expandDirectories, cwd2, props, isIgnore) {
55228
55589
  }
55229
55590
  props.depthOffset = newCommonPath.length;
55230
55591
  props.commonPath = newCommonPath;
55231
- props.root = newCommonPath.length > 0 ? path5.posix.join(cwd2, ...newCommonPath) : cwd2;
55592
+ props.root = newCommonPath.length > 0 ? path6.posix.join(cwd2, ...newCommonPath) : cwd2;
55232
55593
  }
55233
55594
  return result;
55234
55595
  }
@@ -55361,184 +55722,37 @@ function globSync(patternsOrOptions, options) {
55361
55722
  ...options,
55362
55723
  patterns: patternsOrOptions
55363
55724
  } : patternsOrOptions;
55364
- const cwd2 = opts.cwd ? path5.resolve(opts.cwd).replace(BACKSLASHES, "/") : process.cwd().replace(BACKSLASHES, "/");
55725
+ const cwd2 = opts.cwd ? path6.resolve(opts.cwd).replace(BACKSLASHES, "/") : process.cwd().replace(BACKSLASHES, "/");
55365
55726
  return crawl(opts, cwd2, true);
55366
55727
  }
55367
55728
 
55368
- // src/utils/jsonl.ts
55729
+ // src/utils/jsonl-lines.ts
55730
+ import * as fs7 from "fs";
55369
55731
  import { promisify } from "util";
55370
- var readFile4 = promisify(fs6.readFile);
55371
- var readFileSync4 = fs6.readFileSync;
55372
- var statSync4 = fs6.statSync;
55373
- var writeFileSync2 = fs6.writeFileSync;
55374
- var mkdirSync3 = fs6.mkdirSync;
55375
- var existsSync7 = fs6.existsSync;
55376
- function normalizeConfigDir(configDir) {
55377
- return path6.resolve(configDir);
55378
- }
55379
- function getBlockCachePath(configDir = getClaudeConfigDir()) {
55380
- const normalizedConfigDir = normalizeConfigDir(configDir);
55381
- const configHash = createHash("sha256").update(normalizedConfigDir).digest("hex").slice(0, 16);
55382
- return path6.join(os6.homedir(), ".cache", "ccstatusline", `block-cache-${configHash}.json`);
55383
- }
55384
- function readBlockCache(expectedConfigDir) {
55385
- try {
55386
- const normalizedExpectedConfigDir = expectedConfigDir !== undefined ? normalizeConfigDir(expectedConfigDir) : undefined;
55387
- const cachePath = getBlockCachePath(normalizedExpectedConfigDir);
55388
- if (!existsSync7(cachePath)) {
55389
- return null;
55390
- }
55391
- const content = readFileSync4(cachePath, "utf-8");
55392
- const cache3 = JSON.parse(content);
55393
- if (typeof cache3.startTime !== "string") {
55394
- return null;
55395
- }
55396
- if (normalizedExpectedConfigDir !== undefined) {
55397
- if (typeof cache3.configDir !== "string") {
55398
- return null;
55399
- }
55400
- if (cache3.configDir !== normalizedExpectedConfigDir) {
55401
- return null;
55402
- }
55403
- }
55404
- const date5 = new Date(cache3.startTime);
55405
- if (Number.isNaN(date5.getTime())) {
55406
- return null;
55407
- }
55408
- return date5;
55409
- } catch {
55410
- return null;
55411
- }
55732
+ var readFile4 = promisify(fs7.readFile);
55733
+ var readFileSync5 = fs7.readFileSync;
55734
+ function splitJsonlContent(content) {
55735
+ return content.trim().split(`
55736
+ `).filter((line) => line.length > 0);
55412
55737
  }
55413
- function writeBlockCache(startTime, configDir = getClaudeConfigDir()) {
55414
- try {
55415
- const normalizedConfigDir = normalizeConfigDir(configDir);
55416
- const cachePath = getBlockCachePath(normalizedConfigDir);
55417
- const cacheDir = path6.dirname(cachePath);
55418
- if (!existsSync7(cacheDir)) {
55419
- mkdirSync3(cacheDir, { recursive: true });
55420
- }
55421
- const cache3 = {
55422
- startTime: startTime.toISOString(),
55423
- configDir: normalizedConfigDir
55424
- };
55425
- writeFileSync2(cachePath, JSON.stringify(cache3), "utf-8");
55426
- } catch {}
55738
+ async function readJsonlLines(filePath) {
55739
+ const content = await readFile4(filePath, "utf-8");
55740
+ return splitJsonlContent(content);
55427
55741
  }
55428
- function getCachedBlockMetrics(sessionDurationHours = 5) {
55429
- const sessionDurationMs = sessionDurationHours * 60 * 60 * 1000;
55430
- const now = new Date;
55431
- const activeConfigDir = getClaudeConfigDir();
55432
- const cachedStartTime = readBlockCache(activeConfigDir);
55433
- if (cachedStartTime) {
55434
- const blockEndTime = new Date(cachedStartTime.getTime() + sessionDurationMs);
55435
- if (now.getTime() <= blockEndTime.getTime()) {
55436
- return {
55437
- startTime: cachedStartTime,
55438
- lastActivity: now
55439
- };
55440
- }
55441
- }
55442
- const metrics = getBlockMetrics();
55443
- if (metrics) {
55444
- writeBlockCache(metrics.startTime, activeConfigDir);
55445
- }
55446
- return metrics;
55742
+ function readJsonlLinesSync(filePath) {
55743
+ const content = readFileSync5(filePath, "utf-8");
55744
+ return splitJsonlContent(content);
55447
55745
  }
55448
- async function getSessionDuration(transcriptPath) {
55746
+ function parseJsonlLine(line) {
55449
55747
  try {
55450
- if (!fs6.existsSync(transcriptPath)) {
55451
- return null;
55452
- }
55453
- const content = await readFile4(transcriptPath, "utf-8");
55454
- const lines = content.trim().split(`
55455
- `).filter((line) => line.trim());
55456
- if (lines.length === 0) {
55457
- return null;
55458
- }
55459
- let firstTimestamp = null;
55460
- let lastTimestamp = null;
55461
- for (const line of lines) {
55462
- try {
55463
- const data = JSON.parse(line);
55464
- if (data.timestamp) {
55465
- firstTimestamp = new Date(data.timestamp);
55466
- break;
55467
- }
55468
- } catch {}
55469
- }
55470
- for (let i = lines.length - 1;i >= 0; i--) {
55471
- try {
55472
- const data = JSON.parse(lines[i] ?? "");
55473
- if (data.timestamp) {
55474
- lastTimestamp = new Date(data.timestamp);
55475
- break;
55476
- }
55477
- } catch {}
55478
- }
55479
- if (!firstTimestamp || !lastTimestamp) {
55480
- return null;
55481
- }
55482
- const durationMs = lastTimestamp.getTime() - firstTimestamp.getTime();
55483
- const totalMinutes = Math.floor(durationMs / (1000 * 60));
55484
- if (totalMinutes < 1) {
55485
- return "<1m";
55486
- }
55487
- const hours = Math.floor(totalMinutes / 60);
55488
- const minutes = totalMinutes % 60;
55489
- if (hours === 0) {
55490
- return `${minutes}m`;
55491
- } else if (minutes === 0) {
55492
- return `${hours}hr`;
55493
- } else {
55494
- return `${hours}hr ${minutes}m`;
55495
- }
55748
+ return JSON.parse(line);
55496
55749
  } catch {
55497
55750
  return null;
55498
55751
  }
55499
55752
  }
55500
- async function getTokenMetrics(transcriptPath) {
55501
- try {
55502
- if (!fs6.existsSync(transcriptPath)) {
55503
- return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
55504
- }
55505
- const content = await readFile4(transcriptPath, "utf-8");
55506
- const lines = content.trim().split(`
55507
- `);
55508
- let inputTokens = 0;
55509
- let outputTokens = 0;
55510
- let cachedTokens = 0;
55511
- let contextLength = 0;
55512
- let mostRecentMainChainEntry = null;
55513
- let mostRecentTimestamp = null;
55514
- for (const line of lines) {
55515
- try {
55516
- const data = JSON.parse(line);
55517
- if (data.message?.usage) {
55518
- inputTokens += data.message.usage.input_tokens || 0;
55519
- outputTokens += data.message.usage.output_tokens || 0;
55520
- cachedTokens += data.message.usage.cache_read_input_tokens ?? 0;
55521
- cachedTokens += data.message.usage.cache_creation_input_tokens ?? 0;
55522
- if (data.isSidechain !== true && data.timestamp && !data.isApiErrorMessage) {
55523
- const entryTime = new Date(data.timestamp);
55524
- if (!mostRecentTimestamp || entryTime > mostRecentTimestamp) {
55525
- mostRecentTimestamp = entryTime;
55526
- mostRecentMainChainEntry = data;
55527
- }
55528
- }
55529
- }
55530
- } catch {}
55531
- }
55532
- if (mostRecentMainChainEntry?.message?.usage) {
55533
- const usage = mostRecentMainChainEntry.message.usage;
55534
- contextLength = (usage.input_tokens || 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
55535
- }
55536
- const totalTokens = inputTokens + outputTokens + cachedTokens;
55537
- return { inputTokens, outputTokens, cachedTokens, totalTokens, contextLength };
55538
- } catch {
55539
- return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
55540
- }
55541
- }
55753
+
55754
+ // src/utils/jsonl-blocks.ts
55755
+ var statSync5 = fs8.statSync;
55542
55756
  function getBlockMetrics() {
55543
55757
  const claudeDir = getClaudeConfigDir();
55544
55758
  if (!claudeDir)
@@ -55552,7 +55766,7 @@ function getBlockMetrics() {
55552
55766
  function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
55553
55767
  const sessionDurationMs = sessionDurationHours * 60 * 60 * 1000;
55554
55768
  const now = new Date;
55555
- const pattern = path6.posix.join(rootDir.replace(/\\/g, "/"), "projects", "**", "*.jsonl");
55769
+ const pattern = path7.posix.join(rootDir.replace(/\\/g, "/"), "projects", "**", "*.jsonl");
55556
55770
  const files = globSync([pattern], {
55557
55771
  absolute: true,
55558
55772
  cwd: rootDir
@@ -55560,7 +55774,7 @@ function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
55560
55774
  if (files.length === 0)
55561
55775
  return null;
55562
55776
  const filesWithStats = files.map((file2) => {
55563
- const stats = statSync4(file2);
55777
+ const stats = statSync5(file2);
55564
55778
  return { file: file2, mtime: stats.mtime };
55565
55779
  });
55566
55780
  filesWithStats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
@@ -55646,30 +55860,27 @@ function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
55646
55860
  function getAllTimestampsFromFile(filePath) {
55647
55861
  const timestamps = [];
55648
55862
  try {
55649
- const content = readFileSync4(filePath, "utf-8");
55650
- const lines = content.trim().split(`
55651
- `).filter((line) => line.length > 0);
55863
+ const lines = readJsonlLinesSync(filePath);
55652
55864
  for (const line of lines) {
55653
- try {
55654
- const json2 = JSON.parse(line);
55655
- const usage = json2.message?.usage;
55656
- if (!usage)
55657
- continue;
55658
- const hasInputTokens = typeof usage.input_tokens === "number";
55659
- const hasOutputTokens = typeof usage.output_tokens === "number";
55660
- if (!hasInputTokens || !hasOutputTokens)
55661
- continue;
55662
- if (json2.isSidechain === true)
55663
- continue;
55664
- const timestamp = json2.timestamp;
55665
- if (typeof timestamp !== "string")
55666
- continue;
55667
- const date5 = new Date(timestamp);
55668
- if (!Number.isNaN(date5.getTime()))
55669
- timestamps.push(date5);
55670
- } catch {
55865
+ const json2 = parseJsonlLine(line);
55866
+ if (!json2) {
55671
55867
  continue;
55672
55868
  }
55869
+ const usage = json2.message?.usage;
55870
+ if (!usage)
55871
+ continue;
55872
+ const hasInputTokens = typeof usage.input_tokens === "number";
55873
+ const hasOutputTokens = typeof usage.output_tokens === "number";
55874
+ if (!hasInputTokens || !hasOutputTokens)
55875
+ continue;
55876
+ if (json2.isSidechain === true)
55877
+ continue;
55878
+ const timestamp = json2.timestamp;
55879
+ if (typeof timestamp !== "string")
55880
+ continue;
55881
+ const date5 = new Date(timestamp);
55882
+ if (!Number.isNaN(date5.getTime()))
55883
+ timestamps.push(date5);
55673
55884
  }
55674
55885
  return timestamps;
55675
55886
  } catch {
@@ -55682,268 +55893,187 @@ function floorToHour(timestamp) {
55682
55893
  return floored;
55683
55894
  }
55684
55895
 
55685
- // src/utils/usage.ts
55686
- var CACHE_DIR = path7.join(os7.homedir(), ".cache", "ccstatusline");
55687
- var CACHE_FILE = path7.join(CACHE_DIR, "usage.json");
55688
- var LOCK_FILE = path7.join(CACHE_DIR, "usage.lock");
55689
- var CACHE_MAX_AGE = 180;
55690
- var LOCK_MAX_AGE = 30;
55691
- var TOKEN_CACHE_MAX_AGE = 3600;
55692
- var FIVE_HOUR_BLOCK_MS = 5 * 60 * 60 * 1000;
55693
- var UsageErrorSchema = exports_external.enum(["no-credentials", "timeout", "api-error", "parse-error"]);
55694
- var UsageCredentialsSchema = exports_external.object({ claudeAiOauth: exports_external.object({ accessToken: exports_external.string().nullable().optional() }).optional() });
55695
- var CachedUsageDataSchema = exports_external.object({
55696
- sessionUsage: exports_external.number().nullable().optional(),
55697
- sessionResetAt: exports_external.string().nullable().optional(),
55698
- weeklyUsage: exports_external.number().nullable().optional(),
55699
- extraUsageEnabled: exports_external.boolean().nullable().optional(),
55700
- extraUsageLimit: exports_external.number().nullable().optional(),
55701
- extraUsageUsed: exports_external.number().nullable().optional(),
55702
- extraUsageUtilization: exports_external.number().nullable().optional(),
55703
- error: exports_external.string().nullable().optional()
55704
- });
55705
- var UsageApiResponseSchema = exports_external.object({
55706
- five_hour: exports_external.object({
55707
- utilization: exports_external.number().nullable().optional(),
55708
- reset_at: exports_external.string().nullable().optional(),
55709
- resets_at: exports_external.string().nullable().optional()
55710
- }).optional(),
55711
- seven_day: exports_external.object({ utilization: exports_external.number().nullable().optional() }).optional(),
55712
- extra_usage: exports_external.object({
55713
- is_enabled: exports_external.boolean().nullable().optional(),
55714
- monthly_limit: exports_external.number().nullable().optional(),
55715
- used_credits: exports_external.number().nullable().optional(),
55716
- utilization: exports_external.number().nullable().optional()
55717
- }).optional()
55718
- });
55719
- function parseJsonWithSchema(rawJson, schema) {
55720
- try {
55721
- const parsed = schema.safeParse(JSON.parse(rawJson));
55722
- return parsed.success ? parsed.data : null;
55723
- } catch {
55724
- return null;
55725
- }
55726
- }
55727
- function parseUsageAccessToken(rawJson) {
55728
- const parsed = parseJsonWithSchema(rawJson, UsageCredentialsSchema);
55729
- return parsed?.claudeAiOauth?.accessToken ?? null;
55730
- }
55731
- function parseCachedUsageData(rawJson) {
55732
- const parsed = parseJsonWithSchema(rawJson, CachedUsageDataSchema);
55733
- if (!parsed) {
55734
- return null;
55735
- }
55736
- const parsedError = UsageErrorSchema.safeParse(parsed.error);
55737
- return {
55738
- sessionUsage: parsed.sessionUsage ?? undefined,
55739
- sessionResetAt: parsed.sessionResetAt ?? undefined,
55740
- weeklyUsage: parsed.weeklyUsage ?? undefined,
55741
- extraUsageEnabled: parsed.extraUsageEnabled ?? undefined,
55742
- extraUsageLimit: parsed.extraUsageLimit ?? undefined,
55743
- extraUsageUsed: parsed.extraUsageUsed ?? undefined,
55744
- extraUsageUtilization: parsed.extraUsageUtilization ?? undefined,
55745
- error: parsedError.success ? parsedError.data : undefined
55746
- };
55747
- }
55748
- function parseUsageApiResponse(rawJson) {
55749
- const parsed = parseJsonWithSchema(rawJson, UsageApiResponseSchema);
55750
- if (!parsed) {
55751
- return null;
55752
- }
55753
- return {
55754
- sessionUsage: parsed.five_hour?.utilization ?? undefined,
55755
- sessionResetAt: parsed.five_hour?.resets_at ?? parsed.five_hour?.reset_at ?? undefined,
55756
- weeklyUsage: parsed.seven_day?.utilization ?? undefined,
55757
- extraUsageEnabled: parsed.extra_usage?.is_enabled ?? undefined,
55758
- extraUsageLimit: parsed.extra_usage?.monthly_limit ?? undefined,
55759
- extraUsageUsed: parsed.extra_usage?.used_credits ?? undefined,
55760
- extraUsageUtilization: parsed.extra_usage?.utilization ?? undefined
55761
- };
55762
- }
55763
- var cachedUsageData = null;
55764
- var usageCacheTime = 0;
55765
- var cachedUsageToken = null;
55766
- var usageTokenCacheTime = 0;
55767
- function setCachedUsageError(error43, now) {
55768
- const errorData = { error: error43 };
55769
- cachedUsageData = errorData;
55770
- usageCacheTime = now;
55771
- return errorData;
55896
+ // src/utils/jsonl-cache.ts
55897
+ var readFileSync7 = fs9.readFileSync;
55898
+ var writeFileSync3 = fs9.writeFileSync;
55899
+ var mkdirSync4 = fs9.mkdirSync;
55900
+ var existsSync8 = fs9.existsSync;
55901
+ function normalizeConfigDir(configDir) {
55902
+ return path8.resolve(configDir);
55772
55903
  }
55773
- function getStaleUsageOrError(error43, now) {
55774
- const stale = readStaleUsageCache();
55775
- if (stale && !stale.error) {
55776
- cachedUsageData = stale;
55777
- usageCacheTime = now;
55778
- return stale;
55779
- }
55780
- return setCachedUsageError(error43, now);
55904
+ function getBlockCachePath(configDir = getClaudeConfigDir()) {
55905
+ const normalizedConfigDir = normalizeConfigDir(configDir);
55906
+ const configHash = createHash("sha256").update(normalizedConfigDir).digest("hex").slice(0, 16);
55907
+ return path8.join(os7.homedir(), ".cache", "ccstatusline", `block-cache-${configHash}.json`);
55781
55908
  }
55782
- function getUsageToken() {
55783
- const now = Math.floor(Date.now() / 1000);
55784
- if (cachedUsageToken && now - usageTokenCacheTime < TOKEN_CACHE_MAX_AGE) {
55785
- return cachedUsageToken;
55786
- }
55909
+ function readBlockCache(expectedConfigDir) {
55787
55910
  try {
55788
- const isMac = process.platform === "darwin";
55789
- if (isMac) {
55790
- const result = execSync6('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
55791
- const token2 = parseUsageAccessToken(result);
55792
- if (token2) {
55793
- cachedUsageToken = token2;
55794
- usageTokenCacheTime = now;
55911
+ const normalizedExpectedConfigDir = expectedConfigDir !== undefined ? normalizeConfigDir(expectedConfigDir) : undefined;
55912
+ const cachePath = getBlockCachePath(normalizedExpectedConfigDir);
55913
+ if (!existsSync8(cachePath)) {
55914
+ return null;
55915
+ }
55916
+ const content = readFileSync7(cachePath, "utf-8");
55917
+ const cache3 = JSON.parse(content);
55918
+ if (typeof cache3.startTime !== "string") {
55919
+ return null;
55920
+ }
55921
+ if (normalizedExpectedConfigDir !== undefined) {
55922
+ if (typeof cache3.configDir !== "string") {
55923
+ return null;
55924
+ }
55925
+ if (cache3.configDir !== normalizedExpectedConfigDir) {
55926
+ return null;
55795
55927
  }
55796
- return token2;
55797
55928
  }
55798
- const credFile = path7.join(getClaudeConfigDir(), ".credentials.json");
55799
- const token = parseUsageAccessToken(fs7.readFileSync(credFile, "utf8"));
55800
- if (token) {
55801
- cachedUsageToken = token;
55802
- usageTokenCacheTime = now;
55929
+ const date5 = new Date(cache3.startTime);
55930
+ if (Number.isNaN(date5.getTime())) {
55931
+ return null;
55803
55932
  }
55804
- return token;
55933
+ return date5;
55805
55934
  } catch {
55806
55935
  return null;
55807
55936
  }
55808
55937
  }
55809
- function readStaleUsageCache() {
55938
+ function writeBlockCache(startTime, configDir = getClaudeConfigDir()) {
55810
55939
  try {
55811
- return parseCachedUsageData(fs7.readFileSync(CACHE_FILE, "utf8"));
55812
- } catch {
55813
- return null;
55814
- }
55940
+ const normalizedConfigDir = normalizeConfigDir(configDir);
55941
+ const cachePath = getBlockCachePath(normalizedConfigDir);
55942
+ const cacheDir = path8.dirname(cachePath);
55943
+ if (!existsSync8(cacheDir)) {
55944
+ mkdirSync4(cacheDir, { recursive: true });
55945
+ }
55946
+ const cache3 = {
55947
+ startTime: startTime.toISOString(),
55948
+ configDir: normalizedConfigDir
55949
+ };
55950
+ writeFileSync3(cachePath, JSON.stringify(cache3), "utf-8");
55951
+ } catch {}
55815
55952
  }
55816
- function fetchFromUsageApi(token) {
55817
- const script = `
55818
- const token = process.env.TOKEN;
55819
- if (!token) {
55820
- process.exit(1);
55821
- }
55822
-
55823
- const https = require('https');
55824
- const options = {
55825
- hostname: 'api.anthropic.com',
55826
- path: '/api/oauth/usage',
55827
- method: 'GET',
55828
- headers: {
55829
- 'Authorization': 'Bearer ' + token,
55830
- 'anthropic-beta': 'oauth-2025-04-20'
55831
- },
55832
- timeout: 5000
55833
- };
55834
-
55835
- const req = https.request(options, (res) => {
55836
- let data = '';
55837
- res.on('data', chunk => data += chunk);
55838
- res.on('end', () => {
55839
- if (res.statusCode === 200 && data) {
55840
- process.stdout.write(data);
55841
- } else {
55842
- process.exit(1);
55843
- }
55844
- });
55845
- });
55846
-
55847
- req.on('error', () => process.exit(1));
55848
- req.on('timeout', () => {
55849
- req.destroy();
55850
- process.exit(1);
55851
- });
55852
- req.end();
55853
- `;
55854
- const runtimePath = process.execPath || "node";
55855
- const result = spawnSync2(runtimePath, ["-e", script], {
55856
- encoding: "utf8",
55857
- timeout: 6000,
55858
- env: { ...process.env, TOKEN: token }
55859
- });
55860
- if (result.error || result.status !== 0 || !result.stdout) {
55861
- return null;
55953
+ function getCachedBlockMetrics(sessionDurationHours = 5) {
55954
+ const sessionDurationMs = sessionDurationHours * 60 * 60 * 1000;
55955
+ const now = new Date;
55956
+ const activeConfigDir = getClaudeConfigDir();
55957
+ const cachedStartTime = readBlockCache(activeConfigDir);
55958
+ if (cachedStartTime) {
55959
+ const blockEndTime = new Date(cachedStartTime.getTime() + sessionDurationMs);
55960
+ if (now.getTime() <= blockEndTime.getTime()) {
55961
+ return {
55962
+ startTime: cachedStartTime,
55963
+ lastActivity: now
55964
+ };
55965
+ }
55966
+ }
55967
+ const metrics = getBlockMetrics();
55968
+ if (metrics) {
55969
+ writeBlockCache(metrics.startTime, activeConfigDir);
55862
55970
  }
55863
- return result.stdout;
55971
+ return metrics;
55864
55972
  }
55865
- function fetchUsageData() {
55866
- const now = Math.floor(Date.now() / 1000);
55867
- if (cachedUsageData) {
55868
- const cacheAge = now - usageCacheTime;
55869
- if (!cachedUsageData.error && cacheAge < CACHE_MAX_AGE) {
55870
- return cachedUsageData;
55973
+ // src/utils/jsonl-metrics.ts
55974
+ import * as fs10 from "fs";
55975
+ async function getSessionDuration(transcriptPath) {
55976
+ try {
55977
+ if (!fs10.existsSync(transcriptPath)) {
55978
+ return null;
55871
55979
  }
55872
- if (cachedUsageData.error && cacheAge < LOCK_MAX_AGE) {
55873
- return cachedUsageData;
55980
+ const lines = await readJsonlLines(transcriptPath);
55981
+ if (lines.length === 0) {
55982
+ return null;
55874
55983
  }
55875
- }
55876
- try {
55877
- const stat = fs7.statSync(CACHE_FILE);
55878
- const fileAge = now - Math.floor(stat.mtimeMs / 1000);
55879
- if (fileAge < CACHE_MAX_AGE) {
55880
- const fileData = parseCachedUsageData(fs7.readFileSync(CACHE_FILE, "utf8"));
55881
- if (fileData && !fileData.error) {
55882
- cachedUsageData = fileData;
55883
- usageCacheTime = now;
55884
- return fileData;
55984
+ let firstTimestamp = null;
55985
+ let lastTimestamp = null;
55986
+ for (const line of lines) {
55987
+ const data = parseJsonlLine(line);
55988
+ if (data?.timestamp) {
55989
+ firstTimestamp = new Date(data.timestamp);
55990
+ break;
55885
55991
  }
55886
55992
  }
55887
- } catch {}
55888
- const token = getUsageToken();
55889
- if (!token) {
55890
- return getStaleUsageOrError("no-credentials", now);
55891
- }
55892
- try {
55893
- const lockStat = fs7.statSync(LOCK_FILE);
55894
- const lockAge = now - Math.floor(lockStat.mtimeMs / 1000);
55895
- if (lockAge < LOCK_MAX_AGE) {
55896
- const stale = readStaleUsageCache();
55897
- if (stale && !stale.error)
55898
- return stale;
55899
- return { error: "timeout" };
55993
+ for (let i = lines.length - 1;i >= 0; i--) {
55994
+ const line = lines[i];
55995
+ if (!line) {
55996
+ continue;
55997
+ }
55998
+ const data = parseJsonlLine(line);
55999
+ if (data?.timestamp) {
56000
+ lastTimestamp = new Date(data.timestamp);
56001
+ break;
56002
+ }
55900
56003
  }
55901
- } catch {}
55902
- try {
55903
- const lockDir = path7.dirname(LOCK_FILE);
55904
- if (!fs7.existsSync(lockDir)) {
55905
- fs7.mkdirSync(lockDir, { recursive: true });
56004
+ if (!firstTimestamp || !lastTimestamp) {
56005
+ return null;
55906
56006
  }
55907
- fs7.writeFileSync(LOCK_FILE, "");
55908
- } catch {}
55909
- try {
55910
- const response = fetchFromUsageApi(token);
55911
- if (!response) {
55912
- return getStaleUsageOrError("api-error", now);
56007
+ const durationMs = lastTimestamp.getTime() - firstTimestamp.getTime();
56008
+ const totalMinutes = Math.floor(durationMs / (1000 * 60));
56009
+ if (totalMinutes < 1) {
56010
+ return "<1m";
55913
56011
  }
55914
- const usageData = parseUsageApiResponse(response);
55915
- if (!usageData) {
55916
- return getStaleUsageOrError("parse-error", now);
56012
+ const hours = Math.floor(totalMinutes / 60);
56013
+ const minutes = totalMinutes % 60;
56014
+ if (hours === 0) {
56015
+ return `${minutes}m`;
56016
+ } else if (minutes === 0) {
56017
+ return `${hours}hr`;
56018
+ } else {
56019
+ return `${hours}hr ${minutes}m`;
55917
56020
  }
55918
- if (usageData.sessionUsage === undefined && usageData.weeklyUsage === undefined) {
55919
- return getStaleUsageOrError("parse-error", now);
56021
+ } catch {
56022
+ return null;
56023
+ }
56024
+ }
56025
+ async function getTokenMetrics(transcriptPath) {
56026
+ try {
56027
+ if (!fs10.existsSync(transcriptPath)) {
56028
+ return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
55920
56029
  }
55921
- try {
55922
- if (!fs7.existsSync(CACHE_DIR)) {
55923
- fs7.mkdirSync(CACHE_DIR, { recursive: true });
56030
+ const lines = await readJsonlLines(transcriptPath);
56031
+ let inputTokens = 0;
56032
+ let outputTokens = 0;
56033
+ let cachedTokens = 0;
56034
+ let contextLength = 0;
56035
+ let mostRecentMainChainEntry = null;
56036
+ let mostRecentTimestamp = null;
56037
+ for (const line of lines) {
56038
+ const data = parseJsonlLine(line);
56039
+ if (data?.message?.usage) {
56040
+ inputTokens += data.message.usage.input_tokens || 0;
56041
+ outputTokens += data.message.usage.output_tokens || 0;
56042
+ cachedTokens += data.message.usage.cache_read_input_tokens ?? 0;
56043
+ cachedTokens += data.message.usage.cache_creation_input_tokens ?? 0;
56044
+ if (data.isSidechain !== true && data.timestamp && !data.isApiErrorMessage) {
56045
+ const entryTime = new Date(data.timestamp);
56046
+ if (!mostRecentTimestamp || entryTime > mostRecentTimestamp) {
56047
+ mostRecentTimestamp = entryTime;
56048
+ mostRecentMainChainEntry = data;
56049
+ }
56050
+ }
55924
56051
  }
55925
- fs7.writeFileSync(CACHE_FILE, JSON.stringify(usageData));
55926
- } catch {}
55927
- cachedUsageData = usageData;
55928
- usageCacheTime = now;
55929
- return usageData;
56052
+ }
56053
+ if (mostRecentMainChainEntry?.message?.usage) {
56054
+ const usage = mostRecentMainChainEntry.message.usage;
56055
+ contextLength = (usage.input_tokens || 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
56056
+ }
56057
+ const totalTokens = inputTokens + outputTokens + cachedTokens;
56058
+ return { inputTokens, outputTokens, cachedTokens, totalTokens, contextLength };
55930
56059
  } catch {
55931
- return getStaleUsageOrError("parse-error", now);
56060
+ return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
55932
56061
  }
55933
56062
  }
56063
+ // src/utils/usage-windows.ts
55934
56064
  function clamp(value, min, max) {
55935
56065
  return Math.max(min, Math.min(max, value));
55936
56066
  }
55937
- function buildUsageWindow(resetAtMs, nowMs) {
55938
- if (!Number.isFinite(resetAtMs) || !Number.isFinite(nowMs)) {
56067
+ function buildUsageWindow(resetAtMs, nowMs, durationMs) {
56068
+ if (!Number.isFinite(resetAtMs) || !Number.isFinite(nowMs) || !Number.isFinite(durationMs) || durationMs <= 0) {
55939
56069
  return null;
55940
56070
  }
55941
- const startAtMs = resetAtMs - FIVE_HOUR_BLOCK_MS;
55942
- const elapsedMs = clamp(nowMs - startAtMs, 0, FIVE_HOUR_BLOCK_MS);
55943
- const remainingMs = FIVE_HOUR_BLOCK_MS - elapsedMs;
55944
- const elapsedPercent = elapsedMs / FIVE_HOUR_BLOCK_MS * 100;
56071
+ const startAtMs = resetAtMs - durationMs;
56072
+ const elapsedMs = clamp(nowMs - startAtMs, 0, durationMs);
56073
+ const remainingMs = durationMs - elapsedMs;
56074
+ const elapsedPercent = elapsedMs / durationMs * 100;
55945
56075
  return {
55946
- sessionDurationMs: FIVE_HOUR_BLOCK_MS,
56076
+ sessionDurationMs: durationMs,
55947
56077
  elapsedMs,
55948
56078
  remainingMs,
55949
56079
  elapsedPercent,
@@ -55958,14 +56088,14 @@ function getUsageWindowFromResetAt(sessionResetAt, nowMs = Date.now()) {
55958
56088
  if (Number.isNaN(resetAtMs)) {
55959
56089
  return null;
55960
56090
  }
55961
- return buildUsageWindow(resetAtMs, nowMs);
56091
+ return buildUsageWindow(resetAtMs, nowMs, FIVE_HOUR_BLOCK_MS);
55962
56092
  }
55963
56093
  function getUsageWindowFromBlockMetrics(blockMetrics, nowMs = Date.now()) {
55964
56094
  const startAtMs = blockMetrics.startTime.getTime();
55965
56095
  if (Number.isNaN(startAtMs)) {
55966
56096
  return null;
55967
56097
  }
55968
- return buildUsageWindow(startAtMs + FIVE_HOUR_BLOCK_MS, nowMs);
56098
+ return buildUsageWindow(startAtMs + FIVE_HOUR_BLOCK_MS, nowMs, FIVE_HOUR_BLOCK_MS);
55969
56099
  }
55970
56100
  function resolveUsageWindowWithFallback(usageData, blockMetrics, nowMs = Date.now()) {
55971
56101
  const usageWindow = getUsageWindowFromResetAt(usageData.sessionResetAt, nowMs);
@@ -55978,6 +56108,19 @@ function resolveUsageWindowWithFallback(usageData, blockMetrics, nowMs = Date.no
55978
56108
  }
55979
56109
  return getUsageWindowFromBlockMetrics(fallbackMetrics, nowMs);
55980
56110
  }
56111
+ function getWeeklyUsageWindowFromResetAt(weeklyResetAt, nowMs = Date.now()) {
56112
+ if (!weeklyResetAt) {
56113
+ return null;
56114
+ }
56115
+ const resetAtMs = Date.parse(weeklyResetAt);
56116
+ if (Number.isNaN(resetAtMs)) {
56117
+ return null;
56118
+ }
56119
+ return buildUsageWindow(resetAtMs, nowMs, SEVEN_DAY_WINDOW_MS);
56120
+ }
56121
+ function resolveWeeklyUsageWindow(usageData, nowMs = Date.now()) {
56122
+ return getWeeklyUsageWindowFromResetAt(usageData.weeklyResetAt, nowMs);
56123
+ }
55981
56124
  function formatUsageDuration(durationMs) {
55982
56125
  const clampedMs = Math.max(0, durationMs);
55983
56126
  const elapsedHours = Math.floor(clampedMs / (1000 * 60 * 60));
@@ -56004,7 +56147,6 @@ function makeUsageProgressBar(percent, width = 15) {
56004
56147
  const empty2 = width - filled;
56005
56148
  return "[" + "█".repeat(filled) + "░".repeat(empty2) + "]";
56006
56149
  }
56007
-
56008
56150
  // src/widgets/shared/usage-display.ts
56009
56151
  function getUsageDisplayMode(item) {
56010
56152
  const mode = item.metadata?.display;
@@ -56102,7 +56244,7 @@ class BlockTimerWidget {
56102
56244
  }
56103
56245
  return formatRawOrLabeledValue(item, "Block: ", "3hr 45m");
56104
56246
  }
56105
- const usageData = fetchUsageData();
56247
+ const usageData = context.usageData ?? {};
56106
56248
  const window2 = resolveUsageWindowWithFallback(usageData, context.blockMetrics);
56107
56249
  if (!window2) {
56108
56250
  if (isUsageProgressMode(displayMode)) {
@@ -56283,27 +56425,27 @@ class CurrentWorkingDirWidget {
56283
56425
  supportsColors(item) {
56284
56426
  return true;
56285
56427
  }
56286
- abbreviateHomeDir(path8) {
56428
+ abbreviateHomeDir(path9) {
56287
56429
  const homeDir = os8.homedir();
56288
- if (path8 === homeDir) {
56430
+ if (path9 === homeDir) {
56289
56431
  return "~";
56290
56432
  }
56291
- if (path8.startsWith(homeDir)) {
56292
- const boundaryChar = path8[homeDir.length];
56433
+ if (path9.startsWith(homeDir)) {
56434
+ const boundaryChar = path9[homeDir.length];
56293
56435
  if (boundaryChar !== "/" && boundaryChar !== "\\") {
56294
- return path8;
56436
+ return path9;
56295
56437
  }
56296
- return "~" + path8.slice(homeDir.length);
56438
+ return "~" + path9.slice(homeDir.length);
56297
56439
  }
56298
- return path8;
56440
+ return path9;
56299
56441
  }
56300
- abbreviatePath(path8) {
56442
+ abbreviatePath(path9) {
56301
56443
  const homeDir = os8.homedir();
56302
- const useBackslash = path8.includes("\\") && !path8.includes("/");
56444
+ const useBackslash = path9.includes("\\") && !path9.includes("/");
56303
56445
  const sep2 = useBackslash ? "\\" : "/";
56304
- let normalizedPath = path8;
56305
- if (path8.startsWith(homeDir)) {
56306
- normalizedPath = "~" + path8.slice(homeDir.length);
56446
+ let normalizedPath = path9;
56447
+ if (path9.startsWith(homeDir)) {
56448
+ normalizedPath = "~" + path9.slice(homeDir.length);
56307
56449
  }
56308
56450
  const parts = normalizedPath.split(/[\\/]+/).filter((part) => part !== "");
56309
56451
  const abbreviated = parts.map((part, index) => {
@@ -56503,7 +56645,7 @@ class FreeMemoryWidget {
56503
56645
  }
56504
56646
  }
56505
56647
  // src/widgets/SessionName.ts
56506
- import * as fs8 from "fs";
56648
+ import * as fs11 from "fs";
56507
56649
 
56508
56650
  class SessionNameWidget {
56509
56651
  getDefaultColor() {
@@ -56530,7 +56672,7 @@ class SessionNameWidget {
56530
56672
  return null;
56531
56673
  }
56532
56674
  try {
56533
- const content = fs8.readFileSync(transcriptPath, "utf-8");
56675
+ const content = fs11.readFileSync(transcriptPath, "utf-8");
56534
56676
  const lines = content.split(`
56535
56677
  `);
56536
56678
  for (let i = lines.length - 1;i >= 0; i--) {
@@ -56596,7 +56738,7 @@ class SessionUsageWidget {
56596
56738
  }
56597
56739
  return formatRawOrLabeledValue(item, "Session: ", `${previewPercent.toFixed(1)}%`);
56598
56740
  }
56599
- const data = fetchUsageData();
56741
+ const data = context.usageData ?? {};
56600
56742
  if (data.error)
56601
56743
  return getUsageErrorMessage(data.error);
56602
56744
  if (data.sessionUsage === undefined)
@@ -56665,7 +56807,7 @@ class WeeklyUsageWidget {
56665
56807
  }
56666
56808
  return formatRawOrLabeledValue(item, "Weekly: ", `${previewPercent.toFixed(1)}%`);
56667
56809
  }
56668
- const data = fetchUsageData();
56810
+ const data = context.usageData ?? {};
56669
56811
  if (data.error)
56670
56812
  return getUsageErrorMessage(data.error);
56671
56813
  if (data.weeklyUsage === undefined)
@@ -56692,7 +56834,7 @@ class WeeklyUsageWidget {
56692
56834
  return true;
56693
56835
  }
56694
56836
  }
56695
- // src/widgets/ResetTimer.ts
56837
+ // src/widgets/BlockResetTimer.ts
56696
56838
  function makeTimerProgressBar2(percent, width) {
56697
56839
  const clampedPercent = Math.max(0, Math.min(100, percent));
56698
56840
  const filledWidth = Math.floor(clampedPercent / 100 * width);
@@ -56700,15 +56842,15 @@ function makeTimerProgressBar2(percent, width) {
56700
56842
  return "█".repeat(filledWidth) + "░".repeat(emptyWidth);
56701
56843
  }
56702
56844
 
56703
- class ResetTimerWidget {
56845
+ class BlockResetTimerWidget {
56704
56846
  getDefaultColor() {
56705
56847
  return "brightBlue";
56706
56848
  }
56707
56849
  getDescription() {
56708
- return "Shows time remaining until current 5hr block reset";
56850
+ return "Shows time remaining until current 5hr block reset window";
56709
56851
  }
56710
56852
  getDisplayName() {
56711
- return "Reset Timer";
56853
+ return "Block Reset Timer";
56712
56854
  }
56713
56855
  getCategory() {
56714
56856
  return "Usage";
@@ -56740,7 +56882,7 @@ class ResetTimerWidget {
56740
56882
  }
56741
56883
  return formatRawOrLabeledValue(item, "Reset: ", "4hr 30m");
56742
56884
  }
56743
- const usageData = fetchUsageData();
56885
+ const usageData = context.usageData ?? {};
56744
56886
  const window2 = resolveUsageWindowWithFallback(usageData, context.blockMetrics);
56745
56887
  if (!window2) {
56746
56888
  if (usageData.error) {
@@ -56771,6 +56913,85 @@ class ResetTimerWidget {
56771
56913
  return true;
56772
56914
  }
56773
56915
  }
56916
+ // src/widgets/WeeklyResetTimer.ts
56917
+ function makeTimerProgressBar3(percent, width) {
56918
+ const clampedPercent = Math.max(0, Math.min(100, percent));
56919
+ const filledWidth = Math.floor(clampedPercent / 100 * width);
56920
+ const emptyWidth = width - filledWidth;
56921
+ return "█".repeat(filledWidth) + "░".repeat(emptyWidth);
56922
+ }
56923
+
56924
+ class WeeklyResetTimerWidget {
56925
+ getDefaultColor() {
56926
+ return "brightBlue";
56927
+ }
56928
+ getDescription() {
56929
+ return "Shows time remaining until weekly usage reset";
56930
+ }
56931
+ getDisplayName() {
56932
+ return "Weekly Reset Timer";
56933
+ }
56934
+ getCategory() {
56935
+ return "Usage";
56936
+ }
56937
+ getEditorDisplay(item) {
56938
+ return {
56939
+ displayText: this.getDisplayName(),
56940
+ modifierText: getUsageDisplayModifierText(item)
56941
+ };
56942
+ }
56943
+ handleEditorAction(action, item) {
56944
+ if (action === "toggle-progress") {
56945
+ return cycleUsageDisplayMode(item);
56946
+ }
56947
+ if (action === "toggle-invert") {
56948
+ return toggleUsageInverted(item);
56949
+ }
56950
+ return null;
56951
+ }
56952
+ render(item, context, settings) {
56953
+ const displayMode = getUsageDisplayMode(item);
56954
+ const inverted = isUsageInverted(item);
56955
+ if (context.isPreview) {
56956
+ const previewPercent = inverted ? 90 : 10;
56957
+ if (isUsageProgressMode(displayMode)) {
56958
+ const barWidth = getUsageProgressBarWidth(displayMode);
56959
+ const progressBar = makeTimerProgressBar3(previewPercent, barWidth);
56960
+ return formatRawOrLabeledValue(item, "Weekly Reset ", `[${progressBar}] ${previewPercent.toFixed(1)}%`);
56961
+ }
56962
+ return formatRawOrLabeledValue(item, "Weekly Reset: ", "36hr 30m");
56963
+ }
56964
+ const usageData = context.usageData ?? {};
56965
+ const window2 = resolveWeeklyUsageWindow(usageData);
56966
+ if (!window2) {
56967
+ if (usageData.error) {
56968
+ return getUsageErrorMessage(usageData.error);
56969
+ }
56970
+ return null;
56971
+ }
56972
+ if (isUsageProgressMode(displayMode)) {
56973
+ const barWidth = getUsageProgressBarWidth(displayMode);
56974
+ const percent = inverted ? window2.remainingPercent : window2.elapsedPercent;
56975
+ const progressBar = makeTimerProgressBar3(percent, barWidth);
56976
+ const percentage = percent.toFixed(1);
56977
+ return formatRawOrLabeledValue(item, "Weekly Reset ", `[${progressBar}] ${percentage}%`);
56978
+ }
56979
+ const remainingTime = formatUsageDuration(window2.remainingMs);
56980
+ return formatRawOrLabeledValue(item, "Weekly Reset: ", remainingTime);
56981
+ }
56982
+ getCustomKeybinds() {
56983
+ return [
56984
+ { key: "p", label: "(p)rogress toggle", action: "toggle-progress" },
56985
+ { key: "v", label: "in(v)ert fill", action: "toggle-invert" }
56986
+ ];
56987
+ }
56988
+ supportsRawValue() {
56989
+ return true;
56990
+ }
56991
+ supportsColors(item) {
56992
+ return true;
56993
+ }
56994
+ }
56774
56995
  // src/widgets/ContextBar.ts
56775
56996
  function getDisplayMode(item) {
56776
56997
  return item.metadata?.display === "progress" ? "progress" : "progress-short";
@@ -57047,45 +57268,64 @@ var LinkEditor = ({ widget, onComplete, onCancel, action }) => {
57047
57268
  ]
57048
57269
  }, undefined, true, undefined, this);
57049
57270
  };
57271
+ // src/utils/widget-manifest.ts
57272
+ var WIDGET_MANIFEST = [
57273
+ { type: "model", create: () => new ModelWidget },
57274
+ { type: "output-style", create: () => new OutputStyleWidget },
57275
+ { type: "git-branch", create: () => new GitBranchWidget },
57276
+ { type: "git-changes", create: () => new GitChangesWidget },
57277
+ { type: "git-insertions", create: () => new GitInsertionsWidget },
57278
+ { type: "git-deletions", create: () => new GitDeletionsWidget },
57279
+ { type: "git-root-dir", create: () => new GitRootDirWidget },
57280
+ { type: "git-worktree", create: () => new GitWorktreeWidget },
57281
+ { type: "current-working-dir", create: () => new CurrentWorkingDirWidget },
57282
+ { type: "tokens-input", create: () => new TokensInputWidget },
57283
+ { type: "tokens-output", create: () => new TokensOutputWidget },
57284
+ { type: "tokens-cached", create: () => new TokensCachedWidget },
57285
+ { type: "tokens-total", create: () => new TokensTotalWidget },
57286
+ { type: "context-length", create: () => new ContextLengthWidget },
57287
+ { type: "context-percentage", create: () => new ContextPercentageWidget },
57288
+ { type: "context-percentage-usable", create: () => new ContextPercentageUsableWidget },
57289
+ { type: "session-clock", create: () => new SessionClockWidget },
57290
+ { type: "session-cost", create: () => new SessionCostWidget },
57291
+ { type: "block-timer", create: () => new BlockTimerWidget },
57292
+ { type: "terminal-width", create: () => new TerminalWidthWidget },
57293
+ { type: "version", create: () => new VersionWidget },
57294
+ { type: "custom-text", create: () => new CustomTextWidget },
57295
+ { type: "custom-command", create: () => new CustomCommandWidget },
57296
+ { type: "link", create: () => new LinkWidget },
57297
+ { type: "claude-session-id", create: () => new ClaudeSessionIdWidget },
57298
+ { type: "session-name", create: () => new SessionNameWidget },
57299
+ { type: "free-memory", create: () => new FreeMemoryWidget },
57300
+ { type: "session-usage", create: () => new SessionUsageWidget },
57301
+ { type: "weekly-usage", create: () => new WeeklyUsageWidget },
57302
+ { type: "reset-timer", create: () => new BlockResetTimerWidget },
57303
+ { type: "weekly-reset-timer", create: () => new WeeklyResetTimerWidget },
57304
+ { type: "context-bar", create: () => new ContextBarWidget }
57305
+ ];
57306
+ var LAYOUT_WIDGET_MANIFEST = [
57307
+ {
57308
+ type: "separator",
57309
+ displayName: "Separator",
57310
+ description: "A separator character between status line widgets",
57311
+ category: "Layout"
57312
+ },
57313
+ {
57314
+ type: "flex-separator",
57315
+ displayName: "Flex Separator",
57316
+ description: "Expands to fill available terminal width",
57317
+ category: "Layout"
57318
+ }
57319
+ ];
57320
+
57050
57321
  // src/utils/widgets.ts
57051
- var widgetRegistry = new Map([
57052
- ["model", new ModelWidget],
57053
- ["output-style", new OutputStyleWidget],
57054
- ["git-branch", new GitBranchWidget],
57055
- ["git-changes", new GitChangesWidget],
57056
- ["git-insertions", new GitInsertionsWidget],
57057
- ["git-deletions", new GitDeletionsWidget],
57058
- ["git-root-dir", new GitRootDirWidget],
57059
- ["git-worktree", new GitWorktreeWidget],
57060
- ["current-working-dir", new CurrentWorkingDirWidget],
57061
- ["tokens-input", new TokensInputWidget],
57062
- ["tokens-output", new TokensOutputWidget],
57063
- ["tokens-cached", new TokensCachedWidget],
57064
- ["tokens-total", new TokensTotalWidget],
57065
- ["context-length", new ContextLengthWidget],
57066
- ["context-percentage", new ContextPercentageWidget],
57067
- ["context-percentage-usable", new ContextPercentageUsableWidget],
57068
- ["session-clock", new SessionClockWidget],
57069
- ["session-cost", new SessionCostWidget],
57070
- ["block-timer", new BlockTimerWidget],
57071
- ["terminal-width", new TerminalWidthWidget],
57072
- ["version", new VersionWidget],
57073
- ["custom-text", new CustomTextWidget],
57074
- ["custom-command", new CustomCommandWidget],
57075
- ["link", new LinkWidget],
57076
- ["claude-session-id", new ClaudeSessionIdWidget],
57077
- ["session-name", new SessionNameWidget],
57078
- ["free-memory", new FreeMemoryWidget],
57079
- ["session-usage", new SessionUsageWidget],
57080
- ["weekly-usage", new WeeklyUsageWidget],
57081
- ["reset-timer", new ResetTimerWidget],
57082
- ["context-bar", new ContextBarWidget]
57083
- ]);
57322
+ var widgetRegistry = new Map(WIDGET_MANIFEST.map((entry) => [entry.type, entry.create()]));
57323
+ var layoutWidgetTypes = new Set(LAYOUT_WIDGET_MANIFEST.map((entry) => entry.type));
57084
57324
  function getWidget(type) {
57085
57325
  return widgetRegistry.get(type) ?? null;
57086
57326
  }
57087
57327
  function getAllWidgetTypes(settings) {
57088
- const allTypes = Array.from(widgetRegistry.keys());
57328
+ const allTypes = WIDGET_MANIFEST.map((entry) => entry.type);
57089
57329
  if (!settings.powerline.enabled) {
57090
57330
  if (!settings.defaultSeparator) {
57091
57331
  allTypes.push("separator");
@@ -57094,30 +57334,18 @@ function getAllWidgetTypes(settings) {
57094
57334
  }
57095
57335
  return allTypes;
57096
57336
  }
57097
- var LAYOUT_WIDGETS = {
57098
- separator: {
57099
- displayName: "Separator",
57100
- description: "A separator character between status line widgets",
57101
- category: "Layout"
57102
- },
57103
- "flex-separator": {
57104
- displayName: "Flex Separator",
57105
- description: "Expands to fill available terminal width",
57106
- category: "Layout"
57337
+ var layoutCatalogEntries = new Map(LAYOUT_WIDGET_MANIFEST.map((entry) => [
57338
+ entry.type,
57339
+ {
57340
+ type: entry.type,
57341
+ displayName: entry.displayName,
57342
+ description: entry.description,
57343
+ category: entry.category,
57344
+ searchText: `${entry.displayName} ${entry.description} ${entry.type}`.toLowerCase()
57107
57345
  }
57108
- };
57346
+ ]));
57109
57347
  function getLayoutCatalogEntry(type) {
57110
- const layout = LAYOUT_WIDGETS[type];
57111
- if (!layout) {
57112
- return null;
57113
- }
57114
- return {
57115
- type,
57116
- displayName: layout.displayName,
57117
- description: layout.description,
57118
- category: layout.category,
57119
- searchText: `${layout.displayName} ${layout.description} ${type}`.toLowerCase()
57120
- };
57348
+ return layoutCatalogEntries.get(type) ?? null;
57121
57349
  }
57122
57350
  function getWidgetCatalog(settings) {
57123
57351
  return getAllWidgetTypes(settings).map((type) => {
@@ -61200,7 +61428,7 @@ var App2 = () => {
61200
61428
  });
61201
61429
  const handleInstallSelection = import_react46.useCallback((command, displayName, useBunx) => {
61202
61430
  getExistingStatusLine().then((existing) => {
61203
- const isAlreadyInstalled = [CCSTATUSLINE_COMMANDS.NPM, CCSTATUSLINE_COMMANDS.BUNX, CCSTATUSLINE_COMMANDS.SELF_MANAGED].includes(existing ?? "");
61431
+ const isAlreadyInstalled = isKnownCommand(existing ?? "");
61204
61432
  let message;
61205
61433
  if (existing && !isAlreadyInstalled) {
61206
61434
  message = `This will modify ${getClaudeSettingsPath()}
@@ -61347,6 +61575,10 @@ ${GITHUB_REPO_URL}`,
61347
61575
  }, undefined, false, undefined, this)
61348
61576
  ]
61349
61577
  }, undefined, true, undefined, this),
61578
+ isCustomConfigPath() && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
61579
+ dimColor: true,
61580
+ children: `Config: ${getConfigPath()}`
61581
+ }, undefined, false, undefined, this),
61350
61582
  /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(StatusLinePreview, {
61351
61583
  lines: settings.lines,
61352
61584
  terminalWidth,
@@ -61578,6 +61810,24 @@ var StatusJSONSchema = exports_external.looseObject({
61578
61810
  }).nullable().optional()
61579
61811
  });
61580
61812
 
61813
+ // src/utils/usage-prefetch.ts
61814
+ var USAGE_WIDGET_TYPES = new Set([
61815
+ "session-usage",
61816
+ "weekly-usage",
61817
+ "block-timer",
61818
+ "reset-timer",
61819
+ "weekly-reset-timer"
61820
+ ]);
61821
+ function hasUsageDependentWidgets(lines) {
61822
+ return lines.some((line) => line.some((item) => USAGE_WIDGET_TYPES.has(item.type)));
61823
+ }
61824
+ async function prefetchUsageDataIfNeeded(lines) {
61825
+ if (!hasUsageDependentWidgets(lines)) {
61826
+ return null;
61827
+ }
61828
+ return await fetchUsageData();
61829
+ }
61830
+
61581
61831
  // src/ccstatusline.ts
61582
61832
  function hasSessionDurationInStatusJson(data) {
61583
61833
  const durationMs = data.cost?.total_duration_ms;
@@ -61628,9 +61878,11 @@ async function renderMultipleLines(data) {
61628
61878
  if (hasSessionClock && !hasSessionDurationInStatusJson(data) && data.transcript_path) {
61629
61879
  sessionDuration = await getSessionDuration(data.transcript_path);
61630
61880
  }
61881
+ const usageData = await prefetchUsageDataIfNeeded(lines);
61631
61882
  const context = {
61632
61883
  data,
61633
61884
  tokenMetrics,
61885
+ usageData,
61634
61886
  sessionDuration,
61635
61887
  isPreview: false
61636
61888
  };
@@ -61669,7 +61921,20 @@ async function renderMultipleLines(data) {
61669
61921
  }
61670
61922
  }
61671
61923
  }
61924
+ function parseConfigArg() {
61925
+ const idx = process.argv.indexOf("--config");
61926
+ if (idx === -1)
61927
+ return;
61928
+ const configPath = process.argv[idx + 1];
61929
+ if (!configPath || configPath.startsWith("--")) {
61930
+ console.error("--config requires a file path argument");
61931
+ process.exit(1);
61932
+ }
61933
+ process.argv.splice(idx, 2);
61934
+ return configPath;
61935
+ }
61672
61936
  async function main() {
61937
+ initConfigPath(parseConfigArg());
61673
61938
  if (!process.stdin.isTTY) {
61674
61939
  await ensureWindowsUtf8CodePage();
61675
61940
  const input = await readStdin();