@hasna/statusline 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,35 @@
1
1
  // @bun
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
2
4
  var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
32
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
3
33
  var __returnValue = (v) => v;
4
34
  function __exportSetter(name, newValue) {
5
35
  this[name] = __returnValue.bind(null, newValue);
@@ -29,6 +59,7 @@ function defaultConfig() {
29
59
  "loc",
30
60
  "model-context",
31
61
  "context-remaining",
62
+ "cache-rate",
32
63
  "cost"
33
64
  ]
34
65
  };
@@ -94,7 +125,7 @@ function numberOr(v) {
94
125
  }
95
126
 
96
127
  // src/segments/index.ts
97
- import { hostname } from "os";
128
+ import { homedir as homedir5, hostname } from "os";
98
129
 
99
130
  // src/format.ts
100
131
  function compactAge(seconds) {
@@ -134,10 +165,10 @@ function compactDuration(ms) {
134
165
  return `${s}s`;
135
166
  if (s < 3600) {
136
167
  const rem2 = s % 60;
137
- return rem2 ? `${Math.floor(s / 60)}m${rem2}s` : `${Math.floor(s / 60)}m`;
168
+ return rem2 ? `${Math.floor(s / 60)}m ${rem2}s` : `${Math.floor(s / 60)}m`;
138
169
  }
139
170
  const rem = Math.floor(s % 3600 / 60);
140
- return rem ? `${Math.floor(s / 3600)}h${rem}m` : `${Math.floor(s / 3600)}h`;
171
+ return rem ? `${Math.floor(s / 3600)}h ${rem}m` : `${Math.floor(s / 3600)}h`;
141
172
  }
142
173
 
143
174
  // src/git.ts
@@ -194,14 +225,145 @@ function trackedLineCount(cwd) {
194
225
  }
195
226
 
196
227
  // src/context-window.ts
197
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
198
- function contextUsage(ctx) {
199
- const path = ctx.transcriptPath;
228
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
229
+
230
+ // src/providers/codex.ts
231
+ import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
232
+ import { homedir as homedir2 } from "os";
233
+ import { join as join2 } from "path";
234
+ function latestCodexSessionPath(home = homedir2()) {
235
+ const root = join2(home, ".codex", "sessions");
236
+ if (!existsSync2(root))
237
+ return null;
238
+ let newest = null;
239
+ let newestMtime = 0;
240
+ try {
241
+ for (const year of readdirSync(root)) {
242
+ const yearDir = join2(root, year);
243
+ if (!statSync(yearDir, { throwIfNoEntry: false })?.isDirectory())
244
+ continue;
245
+ for (const month of readdirSync(yearDir)) {
246
+ const monthDir = join2(yearDir, month);
247
+ if (!statSync(monthDir, { throwIfNoEntry: false })?.isDirectory())
248
+ continue;
249
+ for (const day of readdirSync(monthDir)) {
250
+ const dayDir = join2(monthDir, day);
251
+ if (!statSync(dayDir, { throwIfNoEntry: false })?.isDirectory())
252
+ continue;
253
+ for (const file of readdirSync(dayDir)) {
254
+ if (!file.startsWith("rollout-") || !file.endsWith(".jsonl"))
255
+ continue;
256
+ const full = join2(dayDir, file);
257
+ const mtime = statSync(full, { throwIfNoEntry: false })?.mtimeMs ?? 0;
258
+ if (mtime > newestMtime) {
259
+ newestMtime = mtime;
260
+ newest = full;
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+ } catch {
267
+ return null;
268
+ }
269
+ return newest;
270
+ }
271
+ function codexCacheRate(sessionPath) {
272
+ const path = sessionPath ?? latestCodexSessionPath();
200
273
  if (!path || !existsSync2(path))
201
274
  return null;
202
275
  let lines;
203
276
  try {
204
277
  lines = readFileSync2(path, "utf8").split(`
278
+ `);
279
+ } catch {
280
+ return null;
281
+ }
282
+ let usage = null;
283
+ for (const line of lines) {
284
+ if (!line || !line.includes("token_count"))
285
+ continue;
286
+ try {
287
+ const entry = JSON.parse(line);
288
+ if (entry?.type !== "event_msg")
289
+ continue;
290
+ if (entry?.payload?.type !== "token_count")
291
+ continue;
292
+ const info = entry?.payload?.info?.total_token_usage;
293
+ if (info && typeof info === "object")
294
+ usage = info;
295
+ } catch {
296
+ continue;
297
+ }
298
+ }
299
+ if (!usage)
300
+ return null;
301
+ const read = usage.cached_input_tokens || 0;
302
+ const input = usage.input_tokens || 0;
303
+ const write = usage.cache_write_input_tokens || 0;
304
+ const divisor = input + read + write;
305
+ if (divisor <= 0)
306
+ return null;
307
+ return Math.min(1, Math.max(0, read / divisor));
308
+ }
309
+
310
+ // src/providers/opencode.ts
311
+ import { existsSync as existsSync3 } from "fs";
312
+ import { homedir as homedir3 } from "os";
313
+ import { join as join3 } from "path";
314
+ import { Database } from "bun:sqlite";
315
+ function opencodeCacheRate(opts = {}) {
316
+ const dbPath = opts.dbPath ?? join3(homedir3(), ".local", "share", "opencode", "opencode.db");
317
+ if (!existsSync3(dbPath))
318
+ return null;
319
+ let db;
320
+ try {
321
+ db = new Database(dbPath, { readonly: true });
322
+ } catch {
323
+ return null;
324
+ }
325
+ try {
326
+ let row = tokenRow(db, "session", opts.sessionId);
327
+ if (!row)
328
+ row = tokenRow(db, "session_v2", opts.sessionId);
329
+ if (!row)
330
+ return null;
331
+ const input = row.tokens_input ?? 0;
332
+ const read = row.tokens_cache_read ?? 0;
333
+ const write = row.tokens_cache_write ?? 0;
334
+ const divisor = input + read + write;
335
+ if (divisor <= 0)
336
+ return null;
337
+ return Math.min(1, Math.max(0, read / divisor));
338
+ } catch {
339
+ return null;
340
+ } finally {
341
+ db.close();
342
+ }
343
+ }
344
+ function tokenRow(db, table, sessionId) {
345
+ try {
346
+ if (sessionId) {
347
+ return db.query(`SELECT tokens_input, tokens_cache_read, tokens_cache_write
348
+ FROM ${table} WHERE id = ? LIMIT 1`).get(sessionId);
349
+ }
350
+ return db.query(`SELECT tokens_input, tokens_cache_read, tokens_cache_write
351
+ FROM ${table}
352
+ WHERE tokens_input > 0 OR tokens_cache_read > 0 OR tokens_cache_write > 0
353
+ ORDER BY time_updated DESC LIMIT 1`).get();
354
+ } catch {
355
+ return null;
356
+ }
357
+ }
358
+
359
+ // src/context-window.ts
360
+ function lastUsageBlock(ctx) {
361
+ const path = ctx.transcriptPath;
362
+ if (!path || !existsSync4(path))
363
+ return null;
364
+ let lines;
365
+ try {
366
+ lines = readFileSync3(path, "utf8").split(`
205
367
  `);
206
368
  } catch {
207
369
  return null;
@@ -215,26 +377,48 @@ function contextUsage(ctx) {
215
377
  const usage = entry?.message?.usage;
216
378
  if (!usage || typeof usage.input_tokens !== "number")
217
379
  continue;
218
- const used = usage.input_tokens + (usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0);
219
- return {
220
- used,
221
- output: usage.output_tokens || 0,
222
- window: windowSize(ctx)
223
- };
380
+ return usage;
224
381
  } catch {
225
382
  continue;
226
383
  }
227
384
  }
228
385
  return null;
229
386
  }
387
+ function contextUsage(ctx) {
388
+ const usage = lastUsageBlock(ctx);
389
+ if (!usage)
390
+ return null;
391
+ const used = usage.input_tokens + (usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0);
392
+ return {
393
+ used,
394
+ output: usage.output_tokens || 0,
395
+ window: windowSize(ctx)
396
+ };
397
+ }
398
+ function cacheRate(ctx) {
399
+ if (ctx.provider === "codex")
400
+ return codexCacheRate();
401
+ if (ctx.provider === "opencode")
402
+ return opencodeCacheRate();
403
+ const usage = lastUsageBlock(ctx);
404
+ if (!usage)
405
+ return null;
406
+ const read = usage.cache_read_input_tokens || 0;
407
+ const creation = usage.cache_creation_input_tokens || 0;
408
+ const input = usage.input_tokens || 0;
409
+ const divisor = input + creation + read;
410
+ if (divisor <= 0)
411
+ return null;
412
+ return read / divisor;
413
+ }
230
414
  function windowSize(ctx) {
231
415
  return ctx.model?.id?.includes("[1m]") ? 1e6 : 200000;
232
416
  }
233
417
 
234
418
  // src/accounts.ts
235
- import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync } from "fs";
236
- import { homedir as homedir2 } from "os";
237
- import { join as join2, resolve, sep } from "path";
419
+ import { existsSync as existsSync5, readFileSync as readFileSync4, realpathSync } from "fs";
420
+ import { homedir as homedir4 } from "os";
421
+ import { join as join4, resolve, sep } from "path";
238
422
  var CONFIG_DIR_ENV = "CLAUDE_CONFIG_DIR";
239
423
  var DEFAULT_CONFIG_SUBDIR = ".claude";
240
424
  var STORE_PATH_ENV = "ACCOUNTS_STORE_PATH";
@@ -258,10 +442,10 @@ function str(value) {
258
442
  return typeof value === "string" && value.trim() ? value.trim() : null;
259
443
  }
260
444
  function home(env) {
261
- return str(env.HOME) ?? homedir2();
445
+ return str(env.HOME) ?? homedir4();
262
446
  }
263
447
  function expandHome(path, env) {
264
- return path === "~" || path.startsWith(`~${sep}`) ? join2(home(env), path.slice(1)) : path;
448
+ return path === "~" || path.startsWith(`~${sep}`) ? join4(home(env), path.slice(1)) : path;
265
449
  }
266
450
  function canonical(path, env) {
267
451
  const abs = resolve(expandHome(path, env));
@@ -273,7 +457,7 @@ function canonical(path, env) {
273
457
  }
274
458
  function readJson(path) {
275
459
  try {
276
- return JSON.parse(readFileSync3(path, "utf8"));
460
+ return JSON.parse(readFileSync4(path, "utf8"));
277
461
  } catch {
278
462
  return null;
279
463
  }
@@ -294,14 +478,14 @@ async function accountsPaths(env) {
294
478
  return { store: storage.storePath(), profiles: storage.profilesDir() };
295
479
  } catch {}
296
480
  }
297
- const base = homeOverride ?? join2(home(env), ...DEFAULT_ACCOUNTS_HOME);
481
+ const base = homeOverride ?? join4(home(env), ...DEFAULT_ACCOUNTS_HOME);
298
482
  return {
299
- store: storeOverride ?? join2(base, STORE_FILE),
300
- profiles: join2(base, PROFILES_SUBDIR)
483
+ store: storeOverride ?? join4(base, STORE_FILE),
484
+ profiles: join4(base, PROFILES_SUBDIR)
301
485
  };
302
486
  }
303
487
  function registryEntry(store, configDir, env, tool) {
304
- if (!existsSync3(store))
488
+ if (!existsSync5(store))
305
489
  return null;
306
490
  const entries = readJson(store)?.profiles;
307
491
  if (!Array.isArray(entries))
@@ -322,7 +506,7 @@ function registryEntry(store, configDir, env, tool) {
322
506
  return tool ? untyped : first;
323
507
  }
324
508
  function layoutProfile(profilesRoot, configDir, env) {
325
- if (!existsSync3(configDir))
509
+ if (!existsSync5(configDir))
326
510
  return null;
327
511
  const prefix = canonical(profilesRoot, env) + sep;
328
512
  if (!configDir.startsWith(prefix))
@@ -336,7 +520,7 @@ function layoutProfile(profilesRoot, configDir, env) {
336
520
  return { configDir, profile, tool, source: "layout" };
337
521
  }
338
522
  function switchedOccupant(configDir) {
339
- const raw = readJson(join2(configDir, AUTH_STATE_SUBDIR, SWITCHED_ACCOUNT_MARKER_FILE));
523
+ const raw = readJson(join4(configDir, AUTH_STATE_SUBDIR, SWITCHED_ACCOUNT_MARKER_FILE));
340
524
  if (!raw)
341
525
  return null;
342
526
  const profile = str(raw.profile);
@@ -344,17 +528,17 @@ function switchedOccupant(configDir) {
344
528
  return profile || email ? { profile, email } : null;
345
529
  }
346
530
  function sessionConfigDir(env = process.env) {
347
- return canonical(str(env[CONFIG_DIR_ENV]) ?? join2(home(env), DEFAULT_CONFIG_SUBDIR), env);
531
+ return canonical(str(env[CONFIG_DIR_ENV]) ?? join4(home(env), DEFAULT_CONFIG_SUBDIR), env);
348
532
  }
349
533
  function sessionStateFile(env = process.env) {
350
534
  const base = canonical(str(env[CONFIG_DIR_ENV]) ?? home(env), env);
351
- const preferred = join2(base, STATE_FILE_PREFERRED);
352
- if (existsSync3(preferred))
535
+ const preferred = join4(base, STATE_FILE_PREFERRED);
536
+ if (existsSync5(preferred))
353
537
  return preferred;
354
538
  const variants = str(env[CUSTOM_OAUTH_ENV]) ? ["-custom-oauth", ...OAUTH_VARIANTS] : OAUTH_VARIANTS;
355
539
  for (const variant of variants) {
356
- const candidate = join2(base, `${STATE_FILE_PREFIX}${variant}${STATE_FILE_SUFFIX}`);
357
- if (existsSync3(candidate))
540
+ const candidate = join4(base, `${STATE_FILE_PREFIX}${variant}${STATE_FILE_SUFFIX}`);
541
+ if (existsSync5(candidate))
358
542
  return candidate;
359
543
  }
360
544
  return null;
@@ -402,7 +586,7 @@ async function sessionAccountEmail(env = process.env, tool) {
402
586
  }
403
587
  function accountsHomeDir(env) {
404
588
  const override = str(env[ACCOUNTS_HOME_ENV]);
405
- return override ? expandHome(override, env) : join2(home(env), ...DEFAULT_ACCOUNTS_HOME);
589
+ return override ? expandHome(override, env) : join4(home(env), ...DEFAULT_ACCOUNTS_HOME);
406
590
  }
407
591
  function sessionAccountUuid(env = process.env) {
408
592
  try {
@@ -475,8 +659,8 @@ function sessionUsage(env = process.env, now = new Date) {
475
659
  const uuid = sessionAccountUuid(env);
476
660
  if (!uuid)
477
661
  return empty;
478
- const path = join2(accountsHomeDir(env), CACHE_SUBDIR, USAGE_CACHE_SUBDIR, `${uuid}.json`);
479
- if (!existsSync3(path))
662
+ const path = join4(accountsHomeDir(env), CACHE_SUBDIR, USAGE_CACHE_SUBDIR, `${uuid}.json`);
663
+ if (!existsSync5(path))
480
664
  return empty;
481
665
  const entry = readJson(path);
482
666
  if (!entry || entry.accountUuid !== uuid)
@@ -491,11 +675,11 @@ function sessionUsage(env = process.env, now = new Date) {
491
675
  }
492
676
 
493
677
  // src/settings.ts
494
- import { readFileSync as readFileSync4 } from "fs";
495
- import { join as join3 } from "path";
678
+ import { readFileSync as readFileSync5 } from "fs";
679
+ import { join as join5 } from "path";
496
680
  function sessionFastMode(env = process.env) {
497
681
  try {
498
- const raw = readFileSync4(join3(sessionConfigDir(env), "settings.json"), "utf8");
682
+ const raw = readFileSync5(join5(sessionConfigDir(env), "settings.json"), "utf8");
499
683
  return JSON.parse(raw)?.fastMode === true;
500
684
  } catch {
501
685
  return false;
@@ -519,6 +703,29 @@ function friendlyModel(id) {
519
703
  return { name: name || mid, tag };
520
704
  }
521
705
  var dir = (ctx) => ctx.cwd || process.cwd();
706
+ function abbreviateHome(path) {
707
+ const home2 = homedir5();
708
+ if (!home2 || home2 === "/")
709
+ return path;
710
+ if (path === home2)
711
+ return "~";
712
+ return path.startsWith(`${home2}/`) ? `~${path.slice(home2.length)}` : path;
713
+ }
714
+ var CWD_MAX = 40;
715
+ function truncateLeft(path, max) {
716
+ return path.length <= max ? path : `\u2026${path.slice(path.length - (max - 1))}`;
717
+ }
718
+ function contextColor(ctx) {
719
+ const u = contextUsage(ctx);
720
+ if (!u || !u.window)
721
+ return null;
722
+ const pct = Math.min(100, Math.round(u.used / u.window * 100));
723
+ if (pct >= 80)
724
+ return "red";
725
+ if (pct >= 50)
726
+ return "yellow";
727
+ return "green";
728
+ }
522
729
  function modelName(ctx) {
523
730
  if (ctx.model?.displayName)
524
731
  return ctx.model.displayName.toLowerCase();
@@ -527,6 +734,12 @@ function modelName(ctx) {
527
734
  function limitPercent(pct) {
528
735
  return typeof pct === "number" ? pct : 0;
529
736
  }
737
+ function anthropicSession(ctx) {
738
+ if (ctx.rateLimits)
739
+ return true;
740
+ const id = ctx.model?.id;
741
+ return typeof id === "string" && /claude/i.test(id);
742
+ }
530
743
  function usageLabel(prefix, headroom) {
531
744
  return headroom === null ? `${prefix} \u2014` : `${prefix} ${headroom}%`;
532
745
  }
@@ -596,10 +809,23 @@ var segments = [
596
809
  defaultEnabled: false,
597
810
  render: (ctx) => dir(ctx).split("/").pop() || null
598
811
  },
812
+ {
813
+ id: "cwd",
814
+ description: "Current working directory (full path)",
815
+ defaultEnabled: false,
816
+ color: "blue",
817
+ render(ctx) {
818
+ const path = ctx.cwd;
819
+ if (typeof path !== "string" || !path)
820
+ return null;
821
+ return truncateLeft(abbreviateHome(path), CWD_MAX);
822
+ }
823
+ },
599
824
  {
600
825
  id: "model",
601
826
  description: "Current model name",
602
827
  defaultEnabled: false,
828
+ color: "dim",
603
829
  render(ctx) {
604
830
  if (!ctx.model?.id)
605
831
  return null;
@@ -610,6 +836,7 @@ var segments = [
610
836
  id: "model-context",
611
837
  description: "Model name with context-size tag (e.g. [1m])",
612
838
  defaultEnabled: true,
839
+ color: "dim",
613
840
  render(ctx) {
614
841
  if (!ctx.model?.id)
615
842
  return null;
@@ -621,7 +848,7 @@ var segments = [
621
848
  id: "model-with-reasoning",
622
849
  description: "Model name with its reasoning effort (or thinking, when on)",
623
850
  defaultEnabled: false,
624
- color: "cyan",
851
+ color: "dim",
625
852
  render(ctx) {
626
853
  const name = modelName(ctx);
627
854
  if (!name)
@@ -681,15 +908,15 @@ var segments = [
681
908
  id: "usage-session",
682
909
  description: "Session (5-hour) usage remaining for this pane's own account, from the local accounts usage cache",
683
910
  defaultEnabled: false,
684
- color: () => usageColor(sessionUsage(process.env).sessionHeadroom),
685
- render: () => usageLabel("5h", sessionUsage(process.env).sessionHeadroom)
911
+ color: (ctx) => anthropicSession(ctx) ? usageColor(sessionUsage(process.env).sessionHeadroom) : null,
912
+ render: (ctx) => anthropicSession(ctx) ? usageLabel("5h", sessionUsage(process.env).sessionHeadroom) : null
686
913
  },
687
914
  {
688
915
  id: "usage-weekly",
689
916
  description: "Weekly usage remaining for this pane's own account \u2014 the binding limit \u2014 from the local accounts usage cache",
690
917
  defaultEnabled: false,
691
- color: () => usageColor(sessionUsage(process.env).weeklyHeadroom, true),
692
- render: () => usageLabel("7d", sessionUsage(process.env).weeklyHeadroom)
918
+ color: (ctx) => anthropicSession(ctx) ? usageColor(sessionUsage(process.env).weeklyHeadroom, true) : null,
919
+ render: (ctx) => anthropicSession(ctx) ? usageLabel("7d", sessionUsage(process.env).weeklyHeadroom) : null
693
920
  },
694
921
  {
695
922
  id: "thread-title",
@@ -702,17 +929,19 @@ var segments = [
702
929
  id: "context-used",
703
930
  description: "Percentage of context window used (omitted when unknown)",
704
931
  defaultEnabled: false,
932
+ color: contextColor,
705
933
  render(ctx) {
706
934
  const u = contextUsage(ctx);
707
935
  if (!u)
708
936
  return null;
709
- return `${Math.min(100, Math.round(u.used / u.window * 100))}%`;
937
+ return `ctx ${Math.min(100, Math.round(u.used / u.window * 100))}%`;
710
938
  }
711
939
  },
712
940
  {
713
941
  id: "context-remaining",
714
942
  description: "Percentage of context window remaining (omitted when unknown)",
715
943
  defaultEnabled: true,
944
+ color: contextColor,
716
945
  render(ctx) {
717
946
  const u = contextUsage(ctx);
718
947
  if (!u)
@@ -731,10 +960,20 @@ var segments = [
731
960
  return `${compactNum(u.used + u.output)} tok`;
732
961
  }
733
962
  },
963
+ {
964
+ id: "cache-rate",
965
+ description: "Percentage of input tokens served from cache (omitted when unknown)",
966
+ defaultEnabled: true,
967
+ render(ctx) {
968
+ const r = cacheRate(ctx);
969
+ return r === null ? null : `${Math.round(r * 100)}% cached`;
970
+ }
971
+ },
734
972
  {
735
973
  id: "cost",
736
974
  description: "Session cost in USD (omitted when zero)",
737
975
  defaultEnabled: true,
976
+ color: "dim",
738
977
  render(ctx) {
739
978
  const c = ctx.cost?.totalCostUsd;
740
979
  return c ? money(c) : null;
@@ -744,6 +983,7 @@ var segments = [
744
983
  id: "duration",
745
984
  description: "Session wall-clock duration",
746
985
  defaultEnabled: false,
986
+ color: "dim",
747
987
  render(ctx) {
748
988
  const ms = ctx.cost?.totalDurationMs;
749
989
  return ms ? compactDuration(ms) : null;
@@ -838,7 +1078,7 @@ async function renderLine(ctx, cfg) {
838
1078
  // package.json
839
1079
  var package_default = {
840
1080
  name: "@hasna/statusline",
841
- version: "0.0.7",
1081
+ version: "0.0.8",
842
1082
  description: "Tiny composable statusline CLI for AI coding agents \u2014 pre-built toggleable segments for Claude Code, with a provider model for others",
843
1083
  license: "Apache-2.0",
844
1084
  type: "module",
@@ -877,11 +1117,12 @@ var package_default = {
877
1117
  },
878
1118
  repository: {
879
1119
  type: "git",
880
- url: "git+https://github.com/hasna/statusline.git"
1120
+ url: "https://github.com/hasna/apps.git",
1121
+ directory: "apps/statusline"
881
1122
  },
882
- homepage: "https://github.com/hasna/statusline#readme",
1123
+ homepage: "https://github.com/hasna/apps/tree/main/apps/statusline#readme",
883
1124
  bugs: {
884
- url: "https://github.com/hasna/statusline/issues"
1125
+ url: "https://github.com/hasna/apps/issues"
885
1126
  },
886
1127
  keywords: [
887
1128
  "statusline",
@@ -898,36 +1139,37 @@ var package_default = {
898
1139
  bun: ">=1.0.0"
899
1140
  },
900
1141
  dependencies: {
901
- "@modelcontextprotocol/sdk": "1.12.1",
1142
+ "@modelcontextprotocol/sdk": "^1.26.0",
902
1143
  zod: "3.25.76"
903
1144
  },
904
1145
  devDependencies: {
905
- "@types/bun": "^1.2.4",
906
- typescript: "5.9.3"
1146
+ "@types/bun": "1.3.14",
1147
+ typescript: "5.9.3",
1148
+ "bun-types": "1.3.14"
907
1149
  }
908
1150
  };
909
1151
  // src/install.ts
910
- import { copyFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
911
- import { homedir as homedir3 } from "os";
912
- import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
1152
+ import { copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
1153
+ import { homedir as homedir6 } from "os";
1154
+ import { dirname as dirname2, join as join6, resolve as resolve2 } from "path";
913
1155
  function defaultSettingsPath(env) {
914
- return join4(env.HOME || homedir3(), ".claude", "settings.json");
1156
+ return join6(env.HOME || homedir6(), ".claude", "settings.json");
915
1157
  }
916
1158
  function claudeSettingsPath(env = process.env) {
917
- const home2 = env.HOME || homedir3();
1159
+ const home2 = env.HOME || homedir6();
918
1160
  const raw = env.CLAUDE_CONFIG_DIR?.trim();
919
1161
  if (!raw)
920
1162
  return defaultSettingsPath(env);
921
- const configDir = raw === "~" || raw.startsWith("~/") ? join4(home2, raw.slice(1)) : raw;
922
- return join4(configDir, "settings.json");
1163
+ const configDir = raw === "~" || raw.startsWith("~/") ? join6(home2, raw.slice(1)) : raw;
1164
+ return join6(configDir, "settings.json");
923
1165
  }
924
1166
  function installClaude(settingsPath, env = process.env) {
925
1167
  const target = settingsPath ?? claudeSettingsPath(env);
926
1168
  const binary = Bun.which("statusline") || "statusline";
927
1169
  let settings = {};
928
- if (existsSync4(target)) {
1170
+ if (existsSync6(target)) {
929
1171
  try {
930
- settings = JSON.parse(readFileSync5(target, "utf8"));
1172
+ settings = JSON.parse(readFileSync6(target, "utf8"));
931
1173
  } catch (error) {
932
1174
  throw new Error(`${target} is not valid JSON \u2014 fix or move it first`);
933
1175
  }
@@ -935,7 +1177,7 @@ function installClaude(settingsPath, env = process.env) {
935
1177
  }
936
1178
  settings.statusLine = { ...settings.statusLine, type: "command", command: `${binary} render` };
937
1179
  const configDir = dirname2(target);
938
- if (!existsSync4(configDir)) {
1180
+ if (!existsSync6(configDir)) {
939
1181
  if (resolve2(target) !== resolve2(defaultSettingsPath(env))) {
940
1182
  throw new Error(`config dir ${configDir} does not exist \u2014 create the profile first`);
941
1183
  }
@@ -1002,6 +1244,14 @@ function resetStatuslineConfig() {
1002
1244
  return defaultConfig();
1003
1245
  }
1004
1246
  async function renderStatusline(input = {}, config = loadConfig()) {
1247
+ const provider = process.env.STATUSLINE_PROVIDER;
1248
+ if (provider === "codex" || provider === "opencode") {
1249
+ const ctx = {
1250
+ provider,
1251
+ cwd: typeof input.cwd === "string" ? input.cwd : process.cwd()
1252
+ };
1253
+ return renderLine(ctx, config);
1254
+ }
1005
1255
  return renderLine(parseClaudeInput(input), config);
1006
1256
  }
1007
1257
  function sampleClaudeInput(cwd = process.cwd()) {
@@ -1044,9 +1294,12 @@ export {
1044
1294
  previewStatusline,
1045
1295
  parseClaudeInput,
1046
1296
  orderSegments,
1297
+ opencodeCacheRate,
1047
1298
  money,
1048
1299
  loadConfig,
1049
1300
  listSegments,
1301
+ latestCodexSessionPath,
1302
+ lastUsageBlock,
1050
1303
  lastCommitEpoch,
1051
1304
  installClaude,
1052
1305
  gitRoot,
@@ -1061,5 +1314,7 @@ export {
1061
1314
  compactNum,
1062
1315
  compactDuration,
1063
1316
  compactAge,
1064
- claudeSettingsPath
1317
+ codexCacheRate,
1318
+ claudeSettingsPath,
1319
+ cacheRate
1065
1320
  };