ccstatusline 2.1.7 → 2.1.9

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.7";
51587
+ var PACKAGE_VERSION = "2.1.9";
51482
51588
  function getPackageVersion() {
51483
51589
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
51484
51590
  return PACKAGE_VERSION;
@@ -52032,23 +52138,27 @@ function applyColors(text, foregroundColor, backgroundColor, bold, colorLevel =
52032
52138
  if (!foregroundColor && !backgroundColor && !bold) {
52033
52139
  return text;
52034
52140
  }
52035
- let result = text;
52141
+ let prefix = "";
52142
+ let suffix = "";
52143
+ if (bold) {
52144
+ prefix += "\x1B[1m";
52145
+ suffix = "\x1B[22m" + suffix;
52146
+ }
52036
52147
  if (backgroundColor) {
52037
- const bgChalk = getChalkColor(backgroundColor, colorLevel, true);
52038
- if (bgChalk) {
52039
- result = bgChalk(result);
52148
+ const bgCode = getColorAnsiCode(backgroundColor, colorLevel, true);
52149
+ if (bgCode) {
52150
+ prefix += bgCode;
52151
+ suffix = "\x1B[49m" + suffix;
52040
52152
  }
52041
52153
  }
52042
52154
  if (foregroundColor) {
52043
- const fgChalk = getChalkColor(foregroundColor, colorLevel, false);
52044
- if (fgChalk) {
52045
- result = fgChalk(result);
52155
+ const fgCode = getColorAnsiCode(foregroundColor, colorLevel, false);
52156
+ if (fgCode) {
52157
+ prefix += fgCode;
52158
+ suffix = "\x1B[39m" + suffix;
52046
52159
  }
52047
52160
  }
52048
- if (bold) {
52049
- result = source_default.bold(result);
52050
- }
52051
- return result;
52161
+ return prefix + text + suffix;
52052
52162
  }
52053
52163
  function getColorAnsiCode(colorName, colorLevel = "ansi16", isBackground = false) {
52054
52164
  if (!colorName)
@@ -54566,54 +54676,309 @@ var CustomCommandEditor = ({ widget, onComplete, onCancel, action }) => {
54566
54676
  children: "Unknown editor mode"
54567
54677
  }, undefined, false, undefined, this);
54568
54678
  };
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
54679
+ // src/utils/usage-fetch.ts
54680
+ import { execSync as execSync6 } from "child_process";
54579
54681
  import * as fs6 from "fs";
54580
- import { createHash } from "node:crypto";
54581
- import os6 from "node:os";
54582
- import path6 from "node:path";
54583
-
54584
- // node_modules/tinyglobby/dist/index.mjs
54585
- import path5, { posix } from "path";
54682
+ import * as https from "https";
54683
+ import * as os6 from "os";
54684
+ import * as path5 from "path";
54685
+
54686
+ // src/utils/usage-types.ts
54687
+ var FIVE_HOUR_BLOCK_MS = 5 * 60 * 60 * 1000;
54688
+ var SEVEN_DAY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
54689
+ var UsageErrorSchema = exports_external.enum(["no-credentials", "timeout", "api-error", "parse-error"]);
54690
+
54691
+ // src/utils/usage-fetch.ts
54692
+ var CACHE_DIR = path5.join(os6.homedir(), ".cache", "ccstatusline");
54693
+ var CACHE_FILE = path5.join(CACHE_DIR, "usage.json");
54694
+ var LOCK_FILE = path5.join(CACHE_DIR, "usage.lock");
54695
+ var CACHE_MAX_AGE = 180;
54696
+ var LOCK_MAX_AGE = 30;
54697
+ var TOKEN_CACHE_MAX_AGE = 3600;
54698
+ var UsageCredentialsSchema = exports_external.object({ claudeAiOauth: exports_external.object({ accessToken: exports_external.string().nullable().optional() }).optional() });
54699
+ var CachedUsageDataSchema = exports_external.object({
54700
+ sessionUsage: exports_external.number().nullable().optional(),
54701
+ sessionResetAt: exports_external.string().nullable().optional(),
54702
+ weeklyUsage: exports_external.number().nullable().optional(),
54703
+ weeklyResetAt: exports_external.string().nullable().optional(),
54704
+ extraUsageEnabled: exports_external.boolean().nullable().optional(),
54705
+ extraUsageLimit: exports_external.number().nullable().optional(),
54706
+ extraUsageUsed: exports_external.number().nullable().optional(),
54707
+ extraUsageUtilization: exports_external.number().nullable().optional(),
54708
+ error: exports_external.string().nullable().optional()
54709
+ });
54710
+ var UsageApiResponseSchema = exports_external.object({
54711
+ five_hour: exports_external.object({
54712
+ utilization: exports_external.number().nullable().optional(),
54713
+ resets_at: exports_external.string().nullable().optional()
54714
+ }).optional(),
54715
+ seven_day: exports_external.object({
54716
+ utilization: exports_external.number().nullable().optional(),
54717
+ resets_at: exports_external.string().nullable().optional()
54718
+ }).optional(),
54719
+ extra_usage: exports_external.object({
54720
+ is_enabled: exports_external.boolean().nullable().optional(),
54721
+ monthly_limit: exports_external.number().nullable().optional(),
54722
+ used_credits: exports_external.number().nullable().optional(),
54723
+ utilization: exports_external.number().nullable().optional()
54724
+ }).optional()
54725
+ });
54726
+ function parseJsonWithSchema(rawJson, schema) {
54727
+ try {
54728
+ const parsed = schema.safeParse(JSON.parse(rawJson));
54729
+ return parsed.success ? parsed.data : null;
54730
+ } catch {
54731
+ return null;
54732
+ }
54733
+ }
54734
+ function parseUsageAccessToken(rawJson) {
54735
+ const parsed = parseJsonWithSchema(rawJson, UsageCredentialsSchema);
54736
+ return parsed?.claudeAiOauth?.accessToken ?? null;
54737
+ }
54738
+ function parseCachedUsageData(rawJson) {
54739
+ const parsed = parseJsonWithSchema(rawJson, CachedUsageDataSchema);
54740
+ if (!parsed) {
54741
+ return null;
54742
+ }
54743
+ const parsedError = UsageErrorSchema.safeParse(parsed.error);
54744
+ return {
54745
+ sessionUsage: parsed.sessionUsage ?? undefined,
54746
+ sessionResetAt: parsed.sessionResetAt ?? undefined,
54747
+ weeklyUsage: parsed.weeklyUsage ?? undefined,
54748
+ weeklyResetAt: parsed.weeklyResetAt ?? undefined,
54749
+ extraUsageEnabled: parsed.extraUsageEnabled ?? undefined,
54750
+ extraUsageLimit: parsed.extraUsageLimit ?? undefined,
54751
+ extraUsageUsed: parsed.extraUsageUsed ?? undefined,
54752
+ extraUsageUtilization: parsed.extraUsageUtilization ?? undefined,
54753
+ error: parsedError.success ? parsedError.data : undefined
54754
+ };
54755
+ }
54756
+ function parseUsageApiResponse(rawJson) {
54757
+ const parsed = parseJsonWithSchema(rawJson, UsageApiResponseSchema);
54758
+ if (!parsed) {
54759
+ return null;
54760
+ }
54761
+ return {
54762
+ sessionUsage: parsed.five_hour?.utilization ?? undefined,
54763
+ sessionResetAt: parsed.five_hour?.resets_at ?? undefined,
54764
+ weeklyUsage: parsed.seven_day?.utilization ?? undefined,
54765
+ weeklyResetAt: parsed.seven_day?.resets_at ?? undefined,
54766
+ extraUsageEnabled: parsed.extra_usage?.is_enabled ?? undefined,
54767
+ extraUsageLimit: parsed.extra_usage?.monthly_limit ?? undefined,
54768
+ extraUsageUsed: parsed.extra_usage?.used_credits ?? undefined,
54769
+ extraUsageUtilization: parsed.extra_usage?.utilization ?? undefined
54770
+ };
54771
+ }
54772
+ var cachedUsageData = null;
54773
+ var usageCacheTime = 0;
54774
+ var cachedUsageToken = null;
54775
+ var usageTokenCacheTime = 0;
54776
+ function setCachedUsageError(error43, now) {
54777
+ const errorData = { error: error43 };
54778
+ cachedUsageData = errorData;
54779
+ usageCacheTime = now;
54780
+ return errorData;
54781
+ }
54782
+ function getStaleUsageOrError(error43, now) {
54783
+ const stale = readStaleUsageCache();
54784
+ if (stale && !stale.error) {
54785
+ cachedUsageData = stale;
54786
+ usageCacheTime = now;
54787
+ return stale;
54788
+ }
54789
+ return setCachedUsageError(error43, now);
54790
+ }
54791
+ function getUsageToken() {
54792
+ const now = Math.floor(Date.now() / 1000);
54793
+ if (cachedUsageToken && now - usageTokenCacheTime < TOKEN_CACHE_MAX_AGE) {
54794
+ return cachedUsageToken;
54795
+ }
54796
+ try {
54797
+ const isMac = process.platform === "darwin";
54798
+ if (isMac) {
54799
+ const result = execSync6('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
54800
+ const token2 = parseUsageAccessToken(result);
54801
+ if (token2) {
54802
+ cachedUsageToken = token2;
54803
+ usageTokenCacheTime = now;
54804
+ }
54805
+ return token2;
54806
+ }
54807
+ const credFile = path5.join(getClaudeConfigDir(), ".credentials.json");
54808
+ const token = parseUsageAccessToken(fs6.readFileSync(credFile, "utf8"));
54809
+ if (token) {
54810
+ cachedUsageToken = token;
54811
+ usageTokenCacheTime = now;
54812
+ }
54813
+ return token;
54814
+ } catch {
54815
+ return null;
54816
+ }
54817
+ }
54818
+ function readStaleUsageCache() {
54819
+ try {
54820
+ return parseCachedUsageData(fs6.readFileSync(CACHE_FILE, "utf8"));
54821
+ } catch {
54822
+ return null;
54823
+ }
54824
+ }
54825
+ var USAGE_API_HOST = "api.anthropic.com";
54826
+ var USAGE_API_PATH = "/api/oauth/usage";
54827
+ async function fetchFromUsageApi(token) {
54828
+ return new Promise((resolve3) => {
54829
+ let settled = false;
54830
+ const finish = (value) => {
54831
+ if (settled) {
54832
+ return;
54833
+ }
54834
+ settled = true;
54835
+ resolve3(value);
54836
+ };
54837
+ const request2 = https.request({
54838
+ hostname: USAGE_API_HOST,
54839
+ path: USAGE_API_PATH,
54840
+ method: "GET",
54841
+ headers: {
54842
+ Authorization: `Bearer ${token}`,
54843
+ "anthropic-beta": "oauth-2025-04-20"
54844
+ },
54845
+ timeout: 5000
54846
+ }, (response) => {
54847
+ let data = "";
54848
+ response.setEncoding("utf8");
54849
+ response.on("data", (chunk) => {
54850
+ data += chunk;
54851
+ });
54852
+ response.on("end", () => {
54853
+ if (response.statusCode === 200 && data) {
54854
+ finish(data);
54855
+ return;
54856
+ }
54857
+ finish(null);
54858
+ });
54859
+ });
54860
+ request2.on("error", () => {
54861
+ finish(null);
54862
+ });
54863
+ request2.on("timeout", () => {
54864
+ request2.destroy();
54865
+ finish(null);
54866
+ });
54867
+ request2.end();
54868
+ });
54869
+ }
54870
+ async function fetchUsageData() {
54871
+ const now = Math.floor(Date.now() / 1000);
54872
+ if (cachedUsageData) {
54873
+ const cacheAge = now - usageCacheTime;
54874
+ if (!cachedUsageData.error && cacheAge < CACHE_MAX_AGE) {
54875
+ return cachedUsageData;
54876
+ }
54877
+ if (cachedUsageData.error && cacheAge < LOCK_MAX_AGE) {
54878
+ return cachedUsageData;
54879
+ }
54880
+ }
54881
+ try {
54882
+ const stat = fs6.statSync(CACHE_FILE);
54883
+ const fileAge = now - Math.floor(stat.mtimeMs / 1000);
54884
+ if (fileAge < CACHE_MAX_AGE) {
54885
+ const fileData = parseCachedUsageData(fs6.readFileSync(CACHE_FILE, "utf8"));
54886
+ if (fileData && !fileData.error) {
54887
+ cachedUsageData = fileData;
54888
+ usageCacheTime = now;
54889
+ return fileData;
54890
+ }
54891
+ }
54892
+ } catch {}
54893
+ const token = getUsageToken();
54894
+ if (!token) {
54895
+ return getStaleUsageOrError("no-credentials", now);
54896
+ }
54897
+ try {
54898
+ const lockStat = fs6.statSync(LOCK_FILE);
54899
+ const lockAge = now - Math.floor(lockStat.mtimeMs / 1000);
54900
+ if (lockAge < LOCK_MAX_AGE) {
54901
+ const stale = readStaleUsageCache();
54902
+ if (stale && !stale.error)
54903
+ return stale;
54904
+ return { error: "timeout" };
54905
+ }
54906
+ } catch {}
54907
+ try {
54908
+ const lockDir = path5.dirname(LOCK_FILE);
54909
+ if (!fs6.existsSync(lockDir)) {
54910
+ fs6.mkdirSync(lockDir, { recursive: true });
54911
+ }
54912
+ fs6.writeFileSync(LOCK_FILE, "");
54913
+ } catch {}
54914
+ try {
54915
+ const response = await fetchFromUsageApi(token);
54916
+ if (!response) {
54917
+ return getStaleUsageOrError("api-error", now);
54918
+ }
54919
+ const usageData = parseUsageApiResponse(response);
54920
+ if (!usageData) {
54921
+ return getStaleUsageOrError("parse-error", now);
54922
+ }
54923
+ if (usageData.sessionUsage === undefined && usageData.weeklyUsage === undefined) {
54924
+ return getStaleUsageOrError("parse-error", now);
54925
+ }
54926
+ try {
54927
+ if (!fs6.existsSync(CACHE_DIR)) {
54928
+ fs6.mkdirSync(CACHE_DIR, { recursive: true });
54929
+ }
54930
+ fs6.writeFileSync(CACHE_FILE, JSON.stringify(usageData));
54931
+ } catch {}
54932
+ cachedUsageData = usageData;
54933
+ usageCacheTime = now;
54934
+ return usageData;
54935
+ } catch {
54936
+ return getStaleUsageOrError("parse-error", now);
54937
+ }
54938
+ }
54939
+ // src/utils/jsonl-cache.ts
54940
+ import * as fs9 from "fs";
54941
+ import { createHash } from "node:crypto";
54942
+ import os7 from "node:os";
54943
+ import path8 from "node:path";
54944
+
54945
+ // src/utils/jsonl-blocks.ts
54946
+ import * as fs8 from "fs";
54947
+ import path7 from "node:path";
54948
+
54949
+ // node_modules/tinyglobby/dist/index.mjs
54950
+ import path6, { posix } from "path";
54586
54951
 
54587
54952
  // node_modules/fdir/dist/index.mjs
54588
54953
  import { createRequire as createRequire2 } from "module";
54589
- import { basename as basename2, dirname as dirname2, normalize, relative, resolve as resolve2, sep } from "path";
54954
+ import { basename as basename2, dirname as dirname4, normalize, relative, resolve as resolve3, sep } from "path";
54590
54955
  import * as nativeFs from "fs";
54591
54956
  var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
54592
- function cleanPath(path5) {
54593
- let normalized = normalize(path5);
54957
+ function cleanPath(path6) {
54958
+ let normalized = normalize(path6);
54594
54959
  if (normalized.length > 1 && normalized[normalized.length - 1] === sep)
54595
54960
  normalized = normalized.substring(0, normalized.length - 1);
54596
54961
  return normalized;
54597
54962
  }
54598
54963
  var SLASHES_REGEX = /[\\/]/g;
54599
- function convertSlashes(path5, separator) {
54600
- return path5.replace(SLASHES_REGEX, separator);
54964
+ function convertSlashes(path6, separator) {
54965
+ return path6.replace(SLASHES_REGEX, separator);
54601
54966
  }
54602
54967
  var WINDOWS_ROOT_DIR_REGEX = /^[a-z]:[\\/]$/i;
54603
- function isRootDirectory(path5) {
54604
- return path5 === "/" || WINDOWS_ROOT_DIR_REGEX.test(path5);
54968
+ function isRootDirectory(path6) {
54969
+ return path6 === "/" || WINDOWS_ROOT_DIR_REGEX.test(path6);
54605
54970
  }
54606
- function normalizePath(path5, options) {
54971
+ function normalizePath(path6, options) {
54607
54972
  const { resolvePaths, normalizePath: normalizePath$1, pathSeparator } = options;
54608
- const pathNeedsCleaning = process.platform === "win32" && path5.includes("/") || path5.startsWith(".");
54973
+ const pathNeedsCleaning = process.platform === "win32" && path6.includes("/") || path6.startsWith(".");
54609
54974
  if (resolvePaths)
54610
- path5 = resolve2(path5);
54975
+ path6 = resolve3(path6);
54611
54976
  if (normalizePath$1 || pathNeedsCleaning)
54612
- path5 = cleanPath(path5);
54613
- if (path5 === ".")
54977
+ path6 = cleanPath(path6);
54978
+ if (path6 === ".")
54614
54979
  return "";
54615
- const needsSeperator = path5[path5.length - 1] !== pathSeparator;
54616
- return convertSlashes(needsSeperator ? path5 + pathSeparator : path5, pathSeparator);
54980
+ const needsSeperator = path6[path6.length - 1] !== pathSeparator;
54981
+ return convertSlashes(needsSeperator ? path6 + pathSeparator : path6, pathSeparator);
54617
54982
  }
54618
54983
  function joinPathWithBasePath(filename, directoryPath) {
54619
54984
  return directoryPath + filename;
@@ -54653,9 +55018,9 @@ var pushDirectory = (directoryPath, paths) => {
54653
55018
  paths.push(directoryPath || ".");
54654
55019
  };
54655
55020
  var pushDirectoryFilter = (directoryPath, paths, filters) => {
54656
- const path5 = directoryPath || ".";
54657
- if (filters.every((filter) => filter(path5, true)))
54658
- paths.push(path5);
55021
+ const path6 = directoryPath || ".";
55022
+ if (filters.every((filter) => filter(path6, true)))
55023
+ paths.push(path6);
54659
55024
  };
54660
55025
  var empty$2 = () => {};
54661
55026
  function build$6(root, options) {
@@ -54712,29 +55077,29 @@ var empty = () => {};
54712
55077
  function build$3(options) {
54713
55078
  return options.group ? groupFiles : empty;
54714
55079
  }
54715
- var resolveSymlinksAsync = function(path5, state, callback$1) {
54716
- const { queue, fs: fs6, options: { suppressErrors } } = state;
55080
+ var resolveSymlinksAsync = function(path6, state, callback$1) {
55081
+ const { queue, fs: fs7, options: { suppressErrors } } = state;
54717
55082
  queue.enqueue();
54718
- fs6.realpath(path5, (error43, resolvedPath) => {
55083
+ fs7.realpath(path6, (error43, resolvedPath) => {
54719
55084
  if (error43)
54720
55085
  return queue.dequeue(suppressErrors ? null : error43, state);
54721
- fs6.stat(resolvedPath, (error$1, stat) => {
55086
+ fs7.stat(resolvedPath, (error$1, stat) => {
54722
55087
  if (error$1)
54723
55088
  return queue.dequeue(suppressErrors ? null : error$1, state);
54724
- if (stat.isDirectory() && isRecursive(path5, resolvedPath, state))
55089
+ if (stat.isDirectory() && isRecursive(path6, resolvedPath, state))
54725
55090
  return queue.dequeue(null, state);
54726
55091
  callback$1(stat, resolvedPath);
54727
55092
  queue.dequeue(null, state);
54728
55093
  });
54729
55094
  });
54730
55095
  };
54731
- var resolveSymlinks = function(path5, state, callback$1) {
54732
- const { queue, fs: fs6, options: { suppressErrors } } = state;
55096
+ var resolveSymlinks = function(path6, state, callback$1) {
55097
+ const { queue, fs: fs7, options: { suppressErrors } } = state;
54733
55098
  queue.enqueue();
54734
55099
  try {
54735
- const resolvedPath = fs6.realpathSync(path5);
54736
- const stat = fs6.statSync(resolvedPath);
54737
- if (stat.isDirectory() && isRecursive(path5, resolvedPath, state))
55100
+ const resolvedPath = fs7.realpathSync(path6);
55101
+ const stat = fs7.statSync(resolvedPath);
55102
+ if (stat.isDirectory() && isRecursive(path6, resolvedPath, state))
54738
55103
  return;
54739
55104
  callback$1(stat, resolvedPath);
54740
55105
  } catch (e) {
@@ -54747,10 +55112,10 @@ function build$2(options, isSynchronous) {
54747
55112
  return null;
54748
55113
  return isSynchronous ? resolveSymlinks : resolveSymlinksAsync;
54749
55114
  }
54750
- function isRecursive(path5, resolved, state) {
55115
+ function isRecursive(path6, resolved, state) {
54751
55116
  if (state.options.useRealPaths)
54752
55117
  return isRecursiveUsingRealPaths(resolved, state);
54753
- let parent = dirname2(path5);
55118
+ let parent = dirname4(path6);
54754
55119
  let depth = 1;
54755
55120
  while (parent !== state.root && depth < 2) {
54756
55121
  const resolvedPath = state.symlinks.get(parent);
@@ -54758,9 +55123,9 @@ function isRecursive(path5, resolved, state) {
54758
55123
  if (isSameRoot)
54759
55124
  depth++;
54760
55125
  else
54761
- parent = dirname2(parent);
55126
+ parent = dirname4(parent);
54762
55127
  }
54763
- state.symlinks.set(path5, resolved);
55128
+ state.symlinks.set(path6, resolved);
54764
55129
  return depth > 1;
54765
55130
  }
54766
55131
  function isRecursiveUsingRealPaths(resolved, state) {
@@ -54816,23 +55181,23 @@ var walkAsync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
54816
55181
  state.queue.enqueue();
54817
55182
  if (currentDepth < 0)
54818
55183
  return state.queue.dequeue(null, state);
54819
- const { fs: fs6 } = state;
55184
+ const { fs: fs7 } = state;
54820
55185
  state.visited.push(crawlPath);
54821
55186
  state.counts.directories++;
54822
- fs6.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
55187
+ fs7.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
54823
55188
  callback$1(entries, directoryPath, currentDepth);
54824
55189
  state.queue.dequeue(state.options.suppressErrors ? null : error43, state);
54825
55190
  });
54826
55191
  };
54827
55192
  var walkSync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
54828
- const { fs: fs6 } = state;
55193
+ const { fs: fs7 } = state;
54829
55194
  if (currentDepth < 0)
54830
55195
  return;
54831
55196
  state.visited.push(crawlPath);
54832
55197
  state.counts.directories++;
54833
55198
  let entries = [];
54834
55199
  try {
54835
- entries = fs6.readdirSync(crawlPath || ".", readdirOpts);
55200
+ entries = fs7.readdirSync(crawlPath || ".", readdirOpts);
54836
55201
  } catch (e) {
54837
55202
  if (!state.options.suppressErrors)
54838
55203
  throw e;
@@ -54938,23 +55303,23 @@ var Walker = class {
54938
55303
  const filename = this.joinPath(entry.name, directoryPath);
54939
55304
  this.pushFile(filename, files, this.state.counts, filters);
54940
55305
  } else if (entry.isDirectory()) {
54941
- let path5 = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
54942
- if (exclude && exclude(entry.name, path5))
55306
+ let path6 = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
55307
+ if (exclude && exclude(entry.name, path6))
54943
55308
  continue;
54944
- this.pushDirectory(path5, paths, filters);
54945
- this.walkDirectory(this.state, path5, path5, depth - 1, this.walk);
55309
+ this.pushDirectory(path6, paths, filters);
55310
+ this.walkDirectory(this.state, path6, path6, depth - 1, this.walk);
54946
55311
  } else if (this.resolveSymlink && entry.isSymbolicLink()) {
54947
- let path5 = joinPathWithBasePath(entry.name, directoryPath);
54948
- this.resolveSymlink(path5, this.state, (stat, resolvedPath) => {
55312
+ let path6 = joinPathWithBasePath(entry.name, directoryPath);
55313
+ this.resolveSymlink(path6, this.state, (stat, resolvedPath) => {
54949
55314
  if (stat.isDirectory()) {
54950
55315
  resolvedPath = normalizePath(resolvedPath, this.state.options);
54951
- if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path5 + pathSeparator))
55316
+ if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path6 + pathSeparator))
54952
55317
  return;
54953
- this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path5 + pathSeparator, depth - 1, this.walk);
55318
+ this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path6 + pathSeparator, depth - 1, this.walk);
54954
55319
  } else {
54955
- resolvedPath = useRealPaths ? resolvedPath : path5;
55320
+ resolvedPath = useRealPaths ? resolvedPath : path6;
54956
55321
  const filename = basename2(resolvedPath);
54957
- const directoryPath$1 = normalizePath(dirname2(resolvedPath), this.state.options);
55322
+ const directoryPath$1 = normalizePath(dirname4(resolvedPath), this.state.options);
54958
55323
  resolvedPath = this.joinPath(filename, directoryPath$1);
54959
55324
  this.pushFile(resolvedPath, files, this.state.counts, filters);
54960
55325
  }
@@ -55112,7 +55477,7 @@ var Builder = class {
55112
55477
  isMatch = globFn(patterns, ...options);
55113
55478
  this.globCache[patterns.join("\x00")] = isMatch;
55114
55479
  }
55115
- this.options.filters.push((path5) => isMatch(path5));
55480
+ this.options.filters.push((path6) => isMatch(path6));
55116
55481
  return this;
55117
55482
  }
55118
55483
  };
@@ -55191,7 +55556,7 @@ function normalizePattern(pattern, expandDirectories, cwd2, props, isIgnore) {
55191
55556
  if (!result.endsWith("*") && expandDirectories)
55192
55557
  result += "/**";
55193
55558
  const escapedCwd = escapePath(cwd2);
55194
- if (path5.isAbsolute(result.replace(ESCAPING_BACKSLASHES, "")))
55559
+ if (path6.isAbsolute(result.replace(ESCAPING_BACKSLASHES, "")))
55195
55560
  result = posix.relative(escapedCwd, result);
55196
55561
  else
55197
55562
  result = posix.normalize(result);
@@ -55228,7 +55593,7 @@ function normalizePattern(pattern, expandDirectories, cwd2, props, isIgnore) {
55228
55593
  }
55229
55594
  props.depthOffset = newCommonPath.length;
55230
55595
  props.commonPath = newCommonPath;
55231
- props.root = newCommonPath.length > 0 ? path5.posix.join(cwd2, ...newCommonPath) : cwd2;
55596
+ props.root = newCommonPath.length > 0 ? path6.posix.join(cwd2, ...newCommonPath) : cwd2;
55232
55597
  }
55233
55598
  return result;
55234
55599
  }
@@ -55361,184 +55726,37 @@ function globSync(patternsOrOptions, options) {
55361
55726
  ...options,
55362
55727
  patterns: patternsOrOptions
55363
55728
  } : patternsOrOptions;
55364
- const cwd2 = opts.cwd ? path5.resolve(opts.cwd).replace(BACKSLASHES, "/") : process.cwd().replace(BACKSLASHES, "/");
55729
+ const cwd2 = opts.cwd ? path6.resolve(opts.cwd).replace(BACKSLASHES, "/") : process.cwd().replace(BACKSLASHES, "/");
55365
55730
  return crawl(opts, cwd2, true);
55366
55731
  }
55367
55732
 
55368
- // src/utils/jsonl.ts
55733
+ // src/utils/jsonl-lines.ts
55734
+ import * as fs7 from "fs";
55369
55735
  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
- }
55736
+ var readFile4 = promisify(fs7.readFile);
55737
+ var readFileSync5 = fs7.readFileSync;
55738
+ function splitJsonlContent(content) {
55739
+ return content.trim().split(`
55740
+ `).filter((line) => line.length > 0);
55412
55741
  }
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 {}
55742
+ async function readJsonlLines(filePath) {
55743
+ const content = await readFile4(filePath, "utf-8");
55744
+ return splitJsonlContent(content);
55427
55745
  }
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;
55746
+ function readJsonlLinesSync(filePath) {
55747
+ const content = readFileSync5(filePath, "utf-8");
55748
+ return splitJsonlContent(content);
55447
55749
  }
55448
- async function getSessionDuration(transcriptPath) {
55750
+ function parseJsonlLine(line) {
55449
55751
  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
- }
55752
+ return JSON.parse(line);
55496
55753
  } catch {
55497
55754
  return null;
55498
55755
  }
55499
55756
  }
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
- }
55757
+
55758
+ // src/utils/jsonl-blocks.ts
55759
+ var statSync5 = fs8.statSync;
55542
55760
  function getBlockMetrics() {
55543
55761
  const claudeDir = getClaudeConfigDir();
55544
55762
  if (!claudeDir)
@@ -55552,7 +55770,7 @@ function getBlockMetrics() {
55552
55770
  function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
55553
55771
  const sessionDurationMs = sessionDurationHours * 60 * 60 * 1000;
55554
55772
  const now = new Date;
55555
- const pattern = path6.posix.join(rootDir.replace(/\\/g, "/"), "projects", "**", "*.jsonl");
55773
+ const pattern = path7.posix.join(rootDir.replace(/\\/g, "/"), "projects", "**", "*.jsonl");
55556
55774
  const files = globSync([pattern], {
55557
55775
  absolute: true,
55558
55776
  cwd: rootDir
@@ -55560,7 +55778,7 @@ function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
55560
55778
  if (files.length === 0)
55561
55779
  return null;
55562
55780
  const filesWithStats = files.map((file2) => {
55563
- const stats = statSync4(file2);
55781
+ const stats = statSync5(file2);
55564
55782
  return { file: file2, mtime: stats.mtime };
55565
55783
  });
55566
55784
  filesWithStats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
@@ -55628,315 +55846,225 @@ function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
55628
55846
  currentBlockStart = floorToHour(timestamp);
55629
55847
  currentBlockEnd = new Date(currentBlockStart.getTime() + sessionDurationMs);
55630
55848
  blocks.push({ start: currentBlockStart, end: currentBlockEnd });
55631
- }
55632
- }
55633
- for (const block of blocks) {
55634
- if (now.getTime() >= block.start.getTime() && now.getTime() <= block.end.getTime()) {
55635
- const hasActivity = timestamps.some((t) => t.getTime() >= block.start.getTime() && t.getTime() <= block.end.getTime());
55636
- if (hasActivity) {
55637
- return {
55638
- startTime: block.start,
55639
- lastActivity: mostRecentTimestamp
55640
- };
55641
- }
55642
- }
55643
- }
55644
- return null;
55645
- }
55646
- function getAllTimestampsFromFile(filePath) {
55647
- const timestamps = [];
55648
- try {
55649
- const content = readFileSync4(filePath, "utf-8");
55650
- const lines = content.trim().split(`
55651
- `).filter((line) => line.length > 0);
55652
- 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 {
55671
- continue;
55672
- }
55673
- }
55674
- return timestamps;
55675
- } catch {
55676
- return [];
55677
- }
55678
- }
55679
- function floorToHour(timestamp) {
55680
- const floored = new Date(timestamp);
55681
- floored.setUTCMinutes(0, 0, 0);
55682
- return floored;
55683
- }
55684
-
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 SEVEN_DAY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
55694
- var UsageErrorSchema = exports_external.enum(["no-credentials", "timeout", "api-error", "parse-error"]);
55695
- var UsageCredentialsSchema = exports_external.object({ claudeAiOauth: exports_external.object({ accessToken: exports_external.string().nullable().optional() }).optional() });
55696
- var CachedUsageDataSchema = exports_external.object({
55697
- sessionUsage: exports_external.number().nullable().optional(),
55698
- sessionResetAt: exports_external.string().nullable().optional(),
55699
- weeklyUsage: exports_external.number().nullable().optional(),
55700
- weeklyResetAt: exports_external.string().nullable().optional(),
55701
- extraUsageEnabled: exports_external.boolean().nullable().optional(),
55702
- extraUsageLimit: exports_external.number().nullable().optional(),
55703
- extraUsageUsed: exports_external.number().nullable().optional(),
55704
- extraUsageUtilization: exports_external.number().nullable().optional(),
55705
- error: exports_external.string().nullable().optional()
55706
- });
55707
- var UsageApiResponseSchema = exports_external.object({
55708
- five_hour: exports_external.object({
55709
- utilization: exports_external.number().nullable().optional(),
55710
- resets_at: exports_external.string().nullable().optional()
55711
- }).optional(),
55712
- seven_day: exports_external.object({
55713
- utilization: exports_external.number().nullable().optional(),
55714
- resets_at: exports_external.string().nullable().optional()
55715
- }).optional(),
55716
- extra_usage: exports_external.object({
55717
- is_enabled: exports_external.boolean().nullable().optional(),
55718
- monthly_limit: exports_external.number().nullable().optional(),
55719
- used_credits: exports_external.number().nullable().optional(),
55720
- utilization: exports_external.number().nullable().optional()
55721
- }).optional()
55722
- });
55723
- function parseJsonWithSchema(rawJson, schema) {
55724
- try {
55725
- const parsed = schema.safeParse(JSON.parse(rawJson));
55726
- return parsed.success ? parsed.data : null;
55727
- } catch {
55728
- return null;
55729
- }
55730
- }
55731
- function parseUsageAccessToken(rawJson) {
55732
- const parsed = parseJsonWithSchema(rawJson, UsageCredentialsSchema);
55733
- return parsed?.claudeAiOauth?.accessToken ?? null;
55734
- }
55735
- function parseCachedUsageData(rawJson) {
55736
- const parsed = parseJsonWithSchema(rawJson, CachedUsageDataSchema);
55737
- if (!parsed) {
55738
- return null;
55739
- }
55740
- const parsedError = UsageErrorSchema.safeParse(parsed.error);
55741
- return {
55742
- sessionUsage: parsed.sessionUsage ?? undefined,
55743
- sessionResetAt: parsed.sessionResetAt ?? undefined,
55744
- weeklyUsage: parsed.weeklyUsage ?? undefined,
55745
- weeklyResetAt: parsed.weeklyResetAt ?? undefined,
55746
- extraUsageEnabled: parsed.extraUsageEnabled ?? undefined,
55747
- extraUsageLimit: parsed.extraUsageLimit ?? undefined,
55748
- extraUsageUsed: parsed.extraUsageUsed ?? undefined,
55749
- extraUsageUtilization: parsed.extraUsageUtilization ?? undefined,
55750
- error: parsedError.success ? parsedError.data : undefined
55751
- };
55752
- }
55753
- function parseUsageApiResponse(rawJson) {
55754
- const parsed = parseJsonWithSchema(rawJson, UsageApiResponseSchema);
55755
- if (!parsed) {
55756
- return null;
55757
- }
55758
- return {
55759
- sessionUsage: parsed.five_hour?.utilization ?? undefined,
55760
- sessionResetAt: parsed.five_hour?.resets_at ?? undefined,
55761
- weeklyUsage: parsed.seven_day?.utilization ?? undefined,
55762
- weeklyResetAt: parsed.seven_day?.resets_at ?? undefined,
55763
- extraUsageEnabled: parsed.extra_usage?.is_enabled ?? undefined,
55764
- extraUsageLimit: parsed.extra_usage?.monthly_limit ?? undefined,
55765
- extraUsageUsed: parsed.extra_usage?.used_credits ?? undefined,
55766
- extraUsageUtilization: parsed.extra_usage?.utilization ?? undefined
55767
- };
55768
- }
55769
- var cachedUsageData = null;
55770
- var usageCacheTime = 0;
55771
- var cachedUsageToken = null;
55772
- var usageTokenCacheTime = 0;
55773
- function setCachedUsageError(error43, now) {
55774
- const errorData = { error: error43 };
55775
- cachedUsageData = errorData;
55776
- usageCacheTime = now;
55777
- return errorData;
55778
- }
55779
- function getStaleUsageOrError(error43, now) {
55780
- const stale = readStaleUsageCache();
55781
- if (stale && !stale.error) {
55782
- cachedUsageData = stale;
55783
- usageCacheTime = now;
55784
- return stale;
55785
- }
55786
- return setCachedUsageError(error43, now);
55787
- }
55788
- function getUsageToken() {
55789
- const now = Math.floor(Date.now() / 1000);
55790
- if (cachedUsageToken && now - usageTokenCacheTime < TOKEN_CACHE_MAX_AGE) {
55791
- return cachedUsageToken;
55849
+ }
55792
55850
  }
55793
- try {
55794
- const isMac = process.platform === "darwin";
55795
- if (isMac) {
55796
- const result = execSync6('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
55797
- const token2 = parseUsageAccessToken(result);
55798
- if (token2) {
55799
- cachedUsageToken = token2;
55800
- usageTokenCacheTime = now;
55851
+ for (const block of blocks) {
55852
+ if (now.getTime() >= block.start.getTime() && now.getTime() <= block.end.getTime()) {
55853
+ const hasActivity = timestamps.some((t) => t.getTime() >= block.start.getTime() && t.getTime() <= block.end.getTime());
55854
+ if (hasActivity) {
55855
+ return {
55856
+ startTime: block.start,
55857
+ lastActivity: mostRecentTimestamp
55858
+ };
55801
55859
  }
55802
- return token2;
55803
- }
55804
- const credFile = path7.join(getClaudeConfigDir(), ".credentials.json");
55805
- const token = parseUsageAccessToken(fs7.readFileSync(credFile, "utf8"));
55806
- if (token) {
55807
- cachedUsageToken = token;
55808
- usageTokenCacheTime = now;
55809
55860
  }
55810
- return token;
55811
- } catch {
55812
- return null;
55813
55861
  }
55862
+ return null;
55814
55863
  }
55815
- function readStaleUsageCache() {
55864
+ function getAllTimestampsFromFile(filePath) {
55865
+ const timestamps = [];
55816
55866
  try {
55817
- return parseCachedUsageData(fs7.readFileSync(CACHE_FILE, "utf8"));
55867
+ const lines = readJsonlLinesSync(filePath);
55868
+ for (const line of lines) {
55869
+ const json2 = parseJsonlLine(line);
55870
+ if (!json2) {
55871
+ continue;
55872
+ }
55873
+ const usage = json2.message?.usage;
55874
+ if (!usage)
55875
+ continue;
55876
+ const hasInputTokens = typeof usage.input_tokens === "number";
55877
+ const hasOutputTokens = typeof usage.output_tokens === "number";
55878
+ if (!hasInputTokens || !hasOutputTokens)
55879
+ continue;
55880
+ if (json2.isSidechain === true)
55881
+ continue;
55882
+ const timestamp = json2.timestamp;
55883
+ if (typeof timestamp !== "string")
55884
+ continue;
55885
+ const date5 = new Date(timestamp);
55886
+ if (!Number.isNaN(date5.getTime()))
55887
+ timestamps.push(date5);
55888
+ }
55889
+ return timestamps;
55818
55890
  } catch {
55819
- return null;
55891
+ return [];
55820
55892
  }
55821
55893
  }
55822
- function fetchFromUsageApi(token) {
55823
- const script = `
55824
- const token = process.env.TOKEN;
55825
- if (!token) {
55826
- process.exit(1);
55827
- }
55828
-
55829
- const https = require('https');
55830
- const options = {
55831
- hostname: 'api.anthropic.com',
55832
- path: '/api/oauth/usage',
55833
- method: 'GET',
55834
- headers: {
55835
- 'Authorization': 'Bearer ' + token,
55836
- 'anthropic-beta': 'oauth-2025-04-20'
55837
- },
55838
- timeout: 5000
55839
- };
55840
-
55841
- const req = https.request(options, (res) => {
55842
- let data = '';
55843
- res.on('data', chunk => data += chunk);
55844
- res.on('end', () => {
55845
- if (res.statusCode === 200 && data) {
55846
- process.stdout.write(data);
55847
- } else {
55848
- process.exit(1);
55849
- }
55850
- });
55851
- });
55894
+ function floorToHour(timestamp) {
55895
+ const floored = new Date(timestamp);
55896
+ floored.setUTCMinutes(0, 0, 0);
55897
+ return floored;
55898
+ }
55852
55899
 
55853
- req.on('error', () => process.exit(1));
55854
- req.on('timeout', () => {
55855
- req.destroy();
55856
- process.exit(1);
55857
- });
55858
- req.end();
55859
- `;
55860
- const runtimePath = process.execPath || "node";
55861
- const result = spawnSync2(runtimePath, ["-e", script], {
55862
- encoding: "utf8",
55863
- timeout: 6000,
55864
- env: { ...process.env, TOKEN: token }
55865
- });
55866
- if (result.error || result.status !== 0 || !result.stdout) {
55867
- return null;
55868
- }
55869
- return result.stdout;
55900
+ // src/utils/jsonl-cache.ts
55901
+ var readFileSync7 = fs9.readFileSync;
55902
+ var writeFileSync3 = fs9.writeFileSync;
55903
+ var mkdirSync4 = fs9.mkdirSync;
55904
+ var existsSync8 = fs9.existsSync;
55905
+ function normalizeConfigDir(configDir) {
55906
+ return path8.resolve(configDir);
55870
55907
  }
55871
- function fetchUsageData() {
55872
- const now = Math.floor(Date.now() / 1000);
55873
- if (cachedUsageData) {
55874
- const cacheAge = now - usageCacheTime;
55875
- if (!cachedUsageData.error && cacheAge < CACHE_MAX_AGE) {
55876
- return cachedUsageData;
55908
+ function getBlockCachePath(configDir = getClaudeConfigDir()) {
55909
+ const normalizedConfigDir = normalizeConfigDir(configDir);
55910
+ const configHash = createHash("sha256").update(normalizedConfigDir).digest("hex").slice(0, 16);
55911
+ return path8.join(os7.homedir(), ".cache", "ccstatusline", `block-cache-${configHash}.json`);
55912
+ }
55913
+ function readBlockCache(expectedConfigDir) {
55914
+ try {
55915
+ const normalizedExpectedConfigDir = expectedConfigDir !== undefined ? normalizeConfigDir(expectedConfigDir) : undefined;
55916
+ const cachePath = getBlockCachePath(normalizedExpectedConfigDir);
55917
+ if (!existsSync8(cachePath)) {
55918
+ return null;
55877
55919
  }
55878
- if (cachedUsageData.error && cacheAge < LOCK_MAX_AGE) {
55879
- return cachedUsageData;
55920
+ const content = readFileSync7(cachePath, "utf-8");
55921
+ const cache3 = JSON.parse(content);
55922
+ if (typeof cache3.startTime !== "string") {
55923
+ return null;
55880
55924
  }
55881
- }
55882
- try {
55883
- const stat = fs7.statSync(CACHE_FILE);
55884
- const fileAge = now - Math.floor(stat.mtimeMs / 1000);
55885
- if (fileAge < CACHE_MAX_AGE) {
55886
- const fileData = parseCachedUsageData(fs7.readFileSync(CACHE_FILE, "utf8"));
55887
- if (fileData && !fileData.error) {
55888
- cachedUsageData = fileData;
55889
- usageCacheTime = now;
55890
- return fileData;
55925
+ if (normalizedExpectedConfigDir !== undefined) {
55926
+ if (typeof cache3.configDir !== "string") {
55927
+ return null;
55928
+ }
55929
+ if (cache3.configDir !== normalizedExpectedConfigDir) {
55930
+ return null;
55891
55931
  }
55892
55932
  }
55893
- } catch {}
55894
- const token = getUsageToken();
55895
- if (!token) {
55896
- return getStaleUsageOrError("no-credentials", now);
55933
+ const date5 = new Date(cache3.startTime);
55934
+ if (Number.isNaN(date5.getTime())) {
55935
+ return null;
55936
+ }
55937
+ return date5;
55938
+ } catch {
55939
+ return null;
55897
55940
  }
55941
+ }
55942
+ function writeBlockCache(startTime, configDir = getClaudeConfigDir()) {
55898
55943
  try {
55899
- const lockStat = fs7.statSync(LOCK_FILE);
55900
- const lockAge = now - Math.floor(lockStat.mtimeMs / 1000);
55901
- if (lockAge < LOCK_MAX_AGE) {
55902
- const stale = readStaleUsageCache();
55903
- if (stale && !stale.error)
55904
- return stale;
55905
- return { error: "timeout" };
55944
+ const normalizedConfigDir = normalizeConfigDir(configDir);
55945
+ const cachePath = getBlockCachePath(normalizedConfigDir);
55946
+ const cacheDir = path8.dirname(cachePath);
55947
+ if (!existsSync8(cacheDir)) {
55948
+ mkdirSync4(cacheDir, { recursive: true });
55906
55949
  }
55950
+ const cache3 = {
55951
+ startTime: startTime.toISOString(),
55952
+ configDir: normalizedConfigDir
55953
+ };
55954
+ writeFileSync3(cachePath, JSON.stringify(cache3), "utf-8");
55907
55955
  } catch {}
55908
- try {
55909
- const lockDir = path7.dirname(LOCK_FILE);
55910
- if (!fs7.existsSync(lockDir)) {
55911
- fs7.mkdirSync(lockDir, { recursive: true });
55956
+ }
55957
+ function getCachedBlockMetrics(sessionDurationHours = 5) {
55958
+ const sessionDurationMs = sessionDurationHours * 60 * 60 * 1000;
55959
+ const now = new Date;
55960
+ const activeConfigDir = getClaudeConfigDir();
55961
+ const cachedStartTime = readBlockCache(activeConfigDir);
55962
+ if (cachedStartTime) {
55963
+ const blockEndTime = new Date(cachedStartTime.getTime() + sessionDurationMs);
55964
+ if (now.getTime() <= blockEndTime.getTime()) {
55965
+ return {
55966
+ startTime: cachedStartTime,
55967
+ lastActivity: now
55968
+ };
55912
55969
  }
55913
- fs7.writeFileSync(LOCK_FILE, "");
55914
- } catch {}
55970
+ }
55971
+ const metrics = getBlockMetrics();
55972
+ if (metrics) {
55973
+ writeBlockCache(metrics.startTime, activeConfigDir);
55974
+ }
55975
+ return metrics;
55976
+ }
55977
+ // src/utils/jsonl-metrics.ts
55978
+ import * as fs10 from "fs";
55979
+ async function getSessionDuration(transcriptPath) {
55915
55980
  try {
55916
- const response = fetchFromUsageApi(token);
55917
- if (!response) {
55918
- return getStaleUsageOrError("api-error", now);
55981
+ if (!fs10.existsSync(transcriptPath)) {
55982
+ return null;
55919
55983
  }
55920
- const usageData = parseUsageApiResponse(response);
55921
- if (!usageData) {
55922
- return getStaleUsageOrError("parse-error", now);
55984
+ const lines = await readJsonlLines(transcriptPath);
55985
+ if (lines.length === 0) {
55986
+ return null;
55923
55987
  }
55924
- if (usageData.sessionUsage === undefined && usageData.weeklyUsage === undefined) {
55925
- return getStaleUsageOrError("parse-error", now);
55988
+ let firstTimestamp = null;
55989
+ let lastTimestamp = null;
55990
+ for (const line of lines) {
55991
+ const data = parseJsonlLine(line);
55992
+ if (data?.timestamp) {
55993
+ firstTimestamp = new Date(data.timestamp);
55994
+ break;
55995
+ }
55926
55996
  }
55927
- try {
55928
- if (!fs7.existsSync(CACHE_DIR)) {
55929
- fs7.mkdirSync(CACHE_DIR, { recursive: true });
55997
+ for (let i = lines.length - 1;i >= 0; i--) {
55998
+ const line = lines[i];
55999
+ if (!line) {
56000
+ continue;
55930
56001
  }
55931
- fs7.writeFileSync(CACHE_FILE, JSON.stringify(usageData));
55932
- } catch {}
55933
- cachedUsageData = usageData;
55934
- usageCacheTime = now;
55935
- return usageData;
56002
+ const data = parseJsonlLine(line);
56003
+ if (data?.timestamp) {
56004
+ lastTimestamp = new Date(data.timestamp);
56005
+ break;
56006
+ }
56007
+ }
56008
+ if (!firstTimestamp || !lastTimestamp) {
56009
+ return null;
56010
+ }
56011
+ const durationMs = lastTimestamp.getTime() - firstTimestamp.getTime();
56012
+ const totalMinutes = Math.floor(durationMs / (1000 * 60));
56013
+ if (totalMinutes < 1) {
56014
+ return "<1m";
56015
+ }
56016
+ const hours = Math.floor(totalMinutes / 60);
56017
+ const minutes = totalMinutes % 60;
56018
+ if (hours === 0) {
56019
+ return `${minutes}m`;
56020
+ } else if (minutes === 0) {
56021
+ return `${hours}hr`;
56022
+ } else {
56023
+ return `${hours}hr ${minutes}m`;
56024
+ }
55936
56025
  } catch {
55937
- return getStaleUsageOrError("parse-error", now);
56026
+ return null;
56027
+ }
56028
+ }
56029
+ async function getTokenMetrics(transcriptPath) {
56030
+ try {
56031
+ if (!fs10.existsSync(transcriptPath)) {
56032
+ return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
56033
+ }
56034
+ const lines = await readJsonlLines(transcriptPath);
56035
+ let inputTokens = 0;
56036
+ let outputTokens = 0;
56037
+ let cachedTokens = 0;
56038
+ let contextLength = 0;
56039
+ let mostRecentMainChainEntry = null;
56040
+ let mostRecentTimestamp = null;
56041
+ for (const line of lines) {
56042
+ const data = parseJsonlLine(line);
56043
+ if (data?.message?.usage) {
56044
+ inputTokens += data.message.usage.input_tokens || 0;
56045
+ outputTokens += data.message.usage.output_tokens || 0;
56046
+ cachedTokens += data.message.usage.cache_read_input_tokens ?? 0;
56047
+ cachedTokens += data.message.usage.cache_creation_input_tokens ?? 0;
56048
+ if (data.isSidechain !== true && data.timestamp && !data.isApiErrorMessage) {
56049
+ const entryTime = new Date(data.timestamp);
56050
+ if (!mostRecentTimestamp || entryTime > mostRecentTimestamp) {
56051
+ mostRecentTimestamp = entryTime;
56052
+ mostRecentMainChainEntry = data;
56053
+ }
56054
+ }
56055
+ }
56056
+ }
56057
+ if (mostRecentMainChainEntry?.message?.usage) {
56058
+ const usage = mostRecentMainChainEntry.message.usage;
56059
+ contextLength = (usage.input_tokens || 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
56060
+ }
56061
+ const totalTokens = inputTokens + outputTokens + cachedTokens;
56062
+ return { inputTokens, outputTokens, cachedTokens, totalTokens, contextLength };
56063
+ } catch {
56064
+ return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
55938
56065
  }
55939
56066
  }
56067
+ // src/utils/usage-windows.ts
55940
56068
  function clamp(value, min, max) {
55941
56069
  return Math.max(min, Math.min(max, value));
55942
56070
  }
@@ -56023,7 +56151,6 @@ function makeUsageProgressBar(percent, width = 15) {
56023
56151
  const empty2 = width - filled;
56024
56152
  return "[" + "█".repeat(filled) + "░".repeat(empty2) + "]";
56025
56153
  }
56026
-
56027
56154
  // src/widgets/shared/usage-display.ts
56028
56155
  function getUsageDisplayMode(item) {
56029
56156
  const mode = item.metadata?.display;
@@ -56121,7 +56248,7 @@ class BlockTimerWidget {
56121
56248
  }
56122
56249
  return formatRawOrLabeledValue(item, "Block: ", "3hr 45m");
56123
56250
  }
56124
- const usageData = fetchUsageData();
56251
+ const usageData = context.usageData ?? {};
56125
56252
  const window2 = resolveUsageWindowWithFallback(usageData, context.blockMetrics);
56126
56253
  if (!window2) {
56127
56254
  if (isUsageProgressMode(displayMode)) {
@@ -56302,27 +56429,27 @@ class CurrentWorkingDirWidget {
56302
56429
  supportsColors(item) {
56303
56430
  return true;
56304
56431
  }
56305
- abbreviateHomeDir(path8) {
56432
+ abbreviateHomeDir(path9) {
56306
56433
  const homeDir = os8.homedir();
56307
- if (path8 === homeDir) {
56434
+ if (path9 === homeDir) {
56308
56435
  return "~";
56309
56436
  }
56310
- if (path8.startsWith(homeDir)) {
56311
- const boundaryChar = path8[homeDir.length];
56437
+ if (path9.startsWith(homeDir)) {
56438
+ const boundaryChar = path9[homeDir.length];
56312
56439
  if (boundaryChar !== "/" && boundaryChar !== "\\") {
56313
- return path8;
56440
+ return path9;
56314
56441
  }
56315
- return "~" + path8.slice(homeDir.length);
56442
+ return "~" + path9.slice(homeDir.length);
56316
56443
  }
56317
- return path8;
56444
+ return path9;
56318
56445
  }
56319
- abbreviatePath(path8) {
56446
+ abbreviatePath(path9) {
56320
56447
  const homeDir = os8.homedir();
56321
- const useBackslash = path8.includes("\\") && !path8.includes("/");
56448
+ const useBackslash = path9.includes("\\") && !path9.includes("/");
56322
56449
  const sep2 = useBackslash ? "\\" : "/";
56323
- let normalizedPath = path8;
56324
- if (path8.startsWith(homeDir)) {
56325
- normalizedPath = "~" + path8.slice(homeDir.length);
56450
+ let normalizedPath = path9;
56451
+ if (path9.startsWith(homeDir)) {
56452
+ normalizedPath = "~" + path9.slice(homeDir.length);
56326
56453
  }
56327
56454
  const parts = normalizedPath.split(/[\\/]+/).filter((part) => part !== "");
56328
56455
  const abbreviated = parts.map((part, index) => {
@@ -56522,7 +56649,7 @@ class FreeMemoryWidget {
56522
56649
  }
56523
56650
  }
56524
56651
  // src/widgets/SessionName.ts
56525
- import * as fs8 from "fs";
56652
+ import * as fs11 from "fs";
56526
56653
 
56527
56654
  class SessionNameWidget {
56528
56655
  getDefaultColor() {
@@ -56549,7 +56676,7 @@ class SessionNameWidget {
56549
56676
  return null;
56550
56677
  }
56551
56678
  try {
56552
- const content = fs8.readFileSync(transcriptPath, "utf-8");
56679
+ const content = fs11.readFileSync(transcriptPath, "utf-8");
56553
56680
  const lines = content.split(`
56554
56681
  `);
56555
56682
  for (let i = lines.length - 1;i >= 0; i--) {
@@ -56615,7 +56742,7 @@ class SessionUsageWidget {
56615
56742
  }
56616
56743
  return formatRawOrLabeledValue(item, "Session: ", `${previewPercent.toFixed(1)}%`);
56617
56744
  }
56618
- const data = fetchUsageData();
56745
+ const data = context.usageData ?? {};
56619
56746
  if (data.error)
56620
56747
  return getUsageErrorMessage(data.error);
56621
56748
  if (data.sessionUsage === undefined)
@@ -56684,7 +56811,7 @@ class WeeklyUsageWidget {
56684
56811
  }
56685
56812
  return formatRawOrLabeledValue(item, "Weekly: ", `${previewPercent.toFixed(1)}%`);
56686
56813
  }
56687
- const data = fetchUsageData();
56814
+ const data = context.usageData ?? {};
56688
56815
  if (data.error)
56689
56816
  return getUsageErrorMessage(data.error);
56690
56817
  if (data.weeklyUsage === undefined)
@@ -56759,7 +56886,7 @@ class BlockResetTimerWidget {
56759
56886
  }
56760
56887
  return formatRawOrLabeledValue(item, "Reset: ", "4hr 30m");
56761
56888
  }
56762
- const usageData = fetchUsageData();
56889
+ const usageData = context.usageData ?? {};
56763
56890
  const window2 = resolveUsageWindowWithFallback(usageData, context.blockMetrics);
56764
56891
  if (!window2) {
56765
56892
  if (usageData.error) {
@@ -56838,7 +56965,7 @@ class WeeklyResetTimerWidget {
56838
56965
  }
56839
56966
  return formatRawOrLabeledValue(item, "Weekly Reset: ", "36hr 30m");
56840
56967
  }
56841
- const usageData = fetchUsageData();
56968
+ const usageData = context.usageData ?? {};
56842
56969
  const window2 = resolveWeeklyUsageWindow(usageData);
56843
56970
  if (!window2) {
56844
56971
  if (usageData.error) {
@@ -57145,46 +57272,64 @@ var LinkEditor = ({ widget, onComplete, onCancel, action }) => {
57145
57272
  ]
57146
57273
  }, undefined, true, undefined, this);
57147
57274
  };
57275
+ // src/utils/widget-manifest.ts
57276
+ var WIDGET_MANIFEST = [
57277
+ { type: "model", create: () => new ModelWidget },
57278
+ { type: "output-style", create: () => new OutputStyleWidget },
57279
+ { type: "git-branch", create: () => new GitBranchWidget },
57280
+ { type: "git-changes", create: () => new GitChangesWidget },
57281
+ { type: "git-insertions", create: () => new GitInsertionsWidget },
57282
+ { type: "git-deletions", create: () => new GitDeletionsWidget },
57283
+ { type: "git-root-dir", create: () => new GitRootDirWidget },
57284
+ { type: "git-worktree", create: () => new GitWorktreeWidget },
57285
+ { type: "current-working-dir", create: () => new CurrentWorkingDirWidget },
57286
+ { type: "tokens-input", create: () => new TokensInputWidget },
57287
+ { type: "tokens-output", create: () => new TokensOutputWidget },
57288
+ { type: "tokens-cached", create: () => new TokensCachedWidget },
57289
+ { type: "tokens-total", create: () => new TokensTotalWidget },
57290
+ { type: "context-length", create: () => new ContextLengthWidget },
57291
+ { type: "context-percentage", create: () => new ContextPercentageWidget },
57292
+ { type: "context-percentage-usable", create: () => new ContextPercentageUsableWidget },
57293
+ { type: "session-clock", create: () => new SessionClockWidget },
57294
+ { type: "session-cost", create: () => new SessionCostWidget },
57295
+ { type: "block-timer", create: () => new BlockTimerWidget },
57296
+ { type: "terminal-width", create: () => new TerminalWidthWidget },
57297
+ { type: "version", create: () => new VersionWidget },
57298
+ { type: "custom-text", create: () => new CustomTextWidget },
57299
+ { type: "custom-command", create: () => new CustomCommandWidget },
57300
+ { type: "link", create: () => new LinkWidget },
57301
+ { type: "claude-session-id", create: () => new ClaudeSessionIdWidget },
57302
+ { type: "session-name", create: () => new SessionNameWidget },
57303
+ { type: "free-memory", create: () => new FreeMemoryWidget },
57304
+ { type: "session-usage", create: () => new SessionUsageWidget },
57305
+ { type: "weekly-usage", create: () => new WeeklyUsageWidget },
57306
+ { type: "reset-timer", create: () => new BlockResetTimerWidget },
57307
+ { type: "weekly-reset-timer", create: () => new WeeklyResetTimerWidget },
57308
+ { type: "context-bar", create: () => new ContextBarWidget }
57309
+ ];
57310
+ var LAYOUT_WIDGET_MANIFEST = [
57311
+ {
57312
+ type: "separator",
57313
+ displayName: "Separator",
57314
+ description: "A separator character between status line widgets",
57315
+ category: "Layout"
57316
+ },
57317
+ {
57318
+ type: "flex-separator",
57319
+ displayName: "Flex Separator",
57320
+ description: "Expands to fill available terminal width",
57321
+ category: "Layout"
57322
+ }
57323
+ ];
57324
+
57148
57325
  // src/utils/widgets.ts
57149
- var widgetRegistry = new Map([
57150
- ["model", new ModelWidget],
57151
- ["output-style", new OutputStyleWidget],
57152
- ["git-branch", new GitBranchWidget],
57153
- ["git-changes", new GitChangesWidget],
57154
- ["git-insertions", new GitInsertionsWidget],
57155
- ["git-deletions", new GitDeletionsWidget],
57156
- ["git-root-dir", new GitRootDirWidget],
57157
- ["git-worktree", new GitWorktreeWidget],
57158
- ["current-working-dir", new CurrentWorkingDirWidget],
57159
- ["tokens-input", new TokensInputWidget],
57160
- ["tokens-output", new TokensOutputWidget],
57161
- ["tokens-cached", new TokensCachedWidget],
57162
- ["tokens-total", new TokensTotalWidget],
57163
- ["context-length", new ContextLengthWidget],
57164
- ["context-percentage", new ContextPercentageWidget],
57165
- ["context-percentage-usable", new ContextPercentageUsableWidget],
57166
- ["session-clock", new SessionClockWidget],
57167
- ["session-cost", new SessionCostWidget],
57168
- ["block-timer", new BlockTimerWidget],
57169
- ["terminal-width", new TerminalWidthWidget],
57170
- ["version", new VersionWidget],
57171
- ["custom-text", new CustomTextWidget],
57172
- ["custom-command", new CustomCommandWidget],
57173
- ["link", new LinkWidget],
57174
- ["claude-session-id", new ClaudeSessionIdWidget],
57175
- ["session-name", new SessionNameWidget],
57176
- ["free-memory", new FreeMemoryWidget],
57177
- ["session-usage", new SessionUsageWidget],
57178
- ["weekly-usage", new WeeklyUsageWidget],
57179
- ["reset-timer", new BlockResetTimerWidget],
57180
- ["weekly-reset-timer", new WeeklyResetTimerWidget],
57181
- ["context-bar", new ContextBarWidget]
57182
- ]);
57326
+ var widgetRegistry = new Map(WIDGET_MANIFEST.map((entry) => [entry.type, entry.create()]));
57327
+ var layoutWidgetTypes = new Set(LAYOUT_WIDGET_MANIFEST.map((entry) => entry.type));
57183
57328
  function getWidget(type) {
57184
57329
  return widgetRegistry.get(type) ?? null;
57185
57330
  }
57186
57331
  function getAllWidgetTypes(settings) {
57187
- const allTypes = Array.from(widgetRegistry.keys());
57332
+ const allTypes = WIDGET_MANIFEST.map((entry) => entry.type);
57188
57333
  if (!settings.powerline.enabled) {
57189
57334
  if (!settings.defaultSeparator) {
57190
57335
  allTypes.push("separator");
@@ -57193,30 +57338,18 @@ function getAllWidgetTypes(settings) {
57193
57338
  }
57194
57339
  return allTypes;
57195
57340
  }
57196
- var LAYOUT_WIDGETS = {
57197
- separator: {
57198
- displayName: "Separator",
57199
- description: "A separator character between status line widgets",
57200
- category: "Layout"
57201
- },
57202
- "flex-separator": {
57203
- displayName: "Flex Separator",
57204
- description: "Expands to fill available terminal width",
57205
- category: "Layout"
57341
+ var layoutCatalogEntries = new Map(LAYOUT_WIDGET_MANIFEST.map((entry) => [
57342
+ entry.type,
57343
+ {
57344
+ type: entry.type,
57345
+ displayName: entry.displayName,
57346
+ description: entry.description,
57347
+ category: entry.category,
57348
+ searchText: `${entry.displayName} ${entry.description} ${entry.type}`.toLowerCase()
57206
57349
  }
57207
- };
57350
+ ]));
57208
57351
  function getLayoutCatalogEntry(type) {
57209
- const layout = LAYOUT_WIDGETS[type];
57210
- if (!layout) {
57211
- return null;
57212
- }
57213
- return {
57214
- type,
57215
- displayName: layout.displayName,
57216
- description: layout.description,
57217
- category: layout.category,
57218
- searchText: `${layout.displayName} ${layout.description} ${type}`.toLowerCase()
57219
- };
57352
+ return layoutCatalogEntries.get(type) ?? null;
57220
57353
  }
57221
57354
  function getWidgetCatalog(settings) {
57222
57355
  return getAllWidgetTypes(settings).map((type) => {
@@ -59772,8 +59905,9 @@ var PowerlineSeparatorEditor = ({
59772
59905
  const inversionText = mode === "separator" && invertBg ? " [Inverted]" : "";
59773
59906
  return `${preset.char} - ${preset.name}${inversionText}`;
59774
59907
  }
59775
- const hexCode = char.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0");
59776
- return `${char} - Custom (\\u${hexCode})${invertBg ? " [Inverted]" : ""}`;
59908
+ const codePoint = char.codePointAt(0) ?? 0;
59909
+ const hexCode = codePoint.toString(16).toUpperCase().padStart(4, "0");
59910
+ return `${char} - Custom (U+${hexCode})${invertBg ? " [Inverted]" : ""}`;
59777
59911
  };
59778
59912
  const updateSeparators = (newSeparators, newInvertBgs) => {
59779
59913
  const updatedPowerline = { ...powerlineConfig };
@@ -59801,23 +59935,26 @@ var PowerlineSeparatorEditor = ({
59801
59935
  setHexInput("");
59802
59936
  setCursorPos(0);
59803
59937
  } else if (key.return) {
59804
- if (hexInput.length === 4) {
59805
- const char = String.fromCharCode(parseInt(hexInput, 16));
59806
- const newSeparators = [...separators];
59807
- if (separators.length === 0) {
59808
- newSeparators.push(char);
59809
- } else {
59810
- newSeparators[selectedIndex] = char;
59938
+ if (hexInput.length >= 4 && hexInput.length <= 6) {
59939
+ const codePoint = parseInt(hexInput, 16);
59940
+ if (codePoint >= 0 && codePoint <= 1114111) {
59941
+ const char = String.fromCodePoint(codePoint);
59942
+ const newSeparators = [...separators];
59943
+ if (separators.length === 0) {
59944
+ newSeparators.push(char);
59945
+ } else {
59946
+ newSeparators[selectedIndex] = char;
59947
+ }
59948
+ updateSeparators(newSeparators);
59949
+ setHexInputMode(false);
59950
+ setHexInput("");
59951
+ setCursorPos(0);
59811
59952
  }
59812
- updateSeparators(newSeparators);
59813
- setHexInputMode(false);
59814
- setHexInput("");
59815
- setCursorPos(0);
59816
59953
  }
59817
59954
  } else if (key.backspace && cursorPos > 0) {
59818
59955
  setHexInput(hexInput.slice(0, cursorPos - 1) + hexInput.slice(cursorPos));
59819
59956
  setCursorPos(cursorPos - 1);
59820
- } else if (shouldInsertInput(input, key) && /[0-9a-fA-F]/.test(input) && hexInput.length < 4) {
59957
+ } else if (shouldInsertInput(input, key) && /[0-9a-fA-F]/.test(input) && hexInput.length < 6) {
59821
59958
  setHexInput(hexInput.slice(0, cursorPos) + input.toUpperCase() + hexInput.slice(cursorPos));
59822
59959
  setCursorPos(cursorPos + 1);
59823
59960
  }
@@ -59941,7 +60078,7 @@ var PowerlineSeparatorEditor = ({
59941
60078
  children: [
59942
60079
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
59943
60080
  children: [
59944
- "Enter 4-digit hex code for",
60081
+ "Enter hex code (4-6 digits) for",
59945
60082
  " ",
59946
60083
  mode === "separator" ? "separator" : "cap",
59947
60084
  separators.length > 0 ? ` ${selectedIndex + 1}` : "",
@@ -59950,7 +60087,7 @@ var PowerlineSeparatorEditor = ({
59950
60087
  }, undefined, true, undefined, this),
59951
60088
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
59952
60089
  children: [
59953
- "\\u",
60090
+ "U+",
59954
60091
  hexInput.slice(0, cursorPos),
59955
60092
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
59956
60093
  backgroundColor: "gray",
@@ -59958,15 +60095,19 @@ var PowerlineSeparatorEditor = ({
59958
60095
  children: hexInput[cursorPos] ?? "_"
59959
60096
  }, undefined, false, undefined, this),
59960
60097
  hexInput.slice(cursorPos + 1),
59961
- hexInput.length < 4 && hexInput.length === cursorPos && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
60098
+ hexInput.length < 6 && hexInput.length === cursorPos && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
59962
60099
  dimColor: true,
59963
- children: "_".repeat(4 - hexInput.length - 1)
60100
+ children: "_".repeat(6 - hexInput.length - 1)
59964
60101
  }, undefined, false, undefined, this)
59965
60102
  ]
59966
60103
  }, undefined, true, undefined, this),
59967
60104
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
59968
60105
  dimColor: true,
59969
- children: "Enter 4 hex digits (0-9, A-F), then press Enter. ESC to cancel."
60106
+ children: "Enter 4-6 hex digits (0-9, A-F) for a Unicode code point, then press Enter. ESC to cancel."
60107
+ }, undefined, false, undefined, this),
60108
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
60109
+ dimColor: true,
60110
+ children: "Examples: E0B0 (powerline), 1F984 (\uD83E\uDD84), 2764 (❤)"
59970
60111
  }, undefined, false, undefined, this)
59971
60112
  ]
59972
60113
  }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(jsx_dev_runtime12.Fragment, {
@@ -61299,7 +61440,7 @@ var App2 = () => {
61299
61440
  });
61300
61441
  const handleInstallSelection = import_react46.useCallback((command, displayName, useBunx) => {
61301
61442
  getExistingStatusLine().then((existing) => {
61302
- const isAlreadyInstalled = [CCSTATUSLINE_COMMANDS.NPM, CCSTATUSLINE_COMMANDS.BUNX, CCSTATUSLINE_COMMANDS.SELF_MANAGED].includes(existing ?? "");
61443
+ const isAlreadyInstalled = isKnownCommand(existing ?? "");
61303
61444
  let message;
61304
61445
  if (existing && !isAlreadyInstalled) {
61305
61446
  message = `This will modify ${getClaudeSettingsPath()}
@@ -61446,6 +61587,10 @@ ${GITHUB_REPO_URL}`,
61446
61587
  }, undefined, false, undefined, this)
61447
61588
  ]
61448
61589
  }, undefined, true, undefined, this),
61590
+ isCustomConfigPath() && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
61591
+ dimColor: true,
61592
+ children: `Config: ${getConfigPath()}`
61593
+ }, undefined, false, undefined, this),
61449
61594
  /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(StatusLinePreview, {
61450
61595
  lines: settings.lines,
61451
61596
  terminalWidth,
@@ -61677,6 +61822,24 @@ var StatusJSONSchema = exports_external.looseObject({
61677
61822
  }).nullable().optional()
61678
61823
  });
61679
61824
 
61825
+ // src/utils/usage-prefetch.ts
61826
+ var USAGE_WIDGET_TYPES = new Set([
61827
+ "session-usage",
61828
+ "weekly-usage",
61829
+ "block-timer",
61830
+ "reset-timer",
61831
+ "weekly-reset-timer"
61832
+ ]);
61833
+ function hasUsageDependentWidgets(lines) {
61834
+ return lines.some((line) => line.some((item) => USAGE_WIDGET_TYPES.has(item.type)));
61835
+ }
61836
+ async function prefetchUsageDataIfNeeded(lines) {
61837
+ if (!hasUsageDependentWidgets(lines)) {
61838
+ return null;
61839
+ }
61840
+ return await fetchUsageData();
61841
+ }
61842
+
61680
61843
  // src/ccstatusline.ts
61681
61844
  function hasSessionDurationInStatusJson(data) {
61682
61845
  const durationMs = data.cost?.total_duration_ms;
@@ -61727,9 +61890,11 @@ async function renderMultipleLines(data) {
61727
61890
  if (hasSessionClock && !hasSessionDurationInStatusJson(data) && data.transcript_path) {
61728
61891
  sessionDuration = await getSessionDuration(data.transcript_path);
61729
61892
  }
61893
+ const usageData = await prefetchUsageDataIfNeeded(lines);
61730
61894
  const context = {
61731
61895
  data,
61732
61896
  tokenMetrics,
61897
+ usageData,
61733
61898
  sessionDuration,
61734
61899
  isPreview: false
61735
61900
  };
@@ -61768,7 +61933,20 @@ async function renderMultipleLines(data) {
61768
61933
  }
61769
61934
  }
61770
61935
  }
61936
+ function parseConfigArg() {
61937
+ const idx = process.argv.indexOf("--config");
61938
+ if (idx === -1)
61939
+ return;
61940
+ const configPath = process.argv[idx + 1];
61941
+ if (!configPath || configPath.startsWith("--")) {
61942
+ console.error("--config requires a file path argument");
61943
+ process.exit(1);
61944
+ }
61945
+ process.argv.splice(idx, 2);
61946
+ return configPath;
61947
+ }
61771
61948
  async function main() {
61949
+ initConfigPath(parseConfigArg());
61772
61950
  if (!process.stdin.isTTY) {
61773
61951
  await ensureWindowsUtf8CodePage();
61774
61952
  const input = await readStdin();