@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/README.md +86 -31
- package/dist/cli.js +308 -58
- package/dist/context-window.d.ts +22 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +314 -59
- package/dist/mcp/index.js +19823 -6484
- package/dist/providers/codex.d.ts +19 -0
- package/dist/providers/opencode.d.ts +16 -0
- package/dist/providers/types.d.ts +1 -1
- package/package.json +10 -8
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
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
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
|
|
236
|
-
import { homedir as
|
|
237
|
-
import { join as
|
|
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) ??
|
|
445
|
+
return str(env.HOME) ?? homedir4();
|
|
262
446
|
}
|
|
263
447
|
function expandHome(path, env) {
|
|
264
|
-
return path === "~" || path.startsWith(`~${sep}`) ?
|
|
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(
|
|
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 ??
|
|
481
|
+
const base = homeOverride ?? join4(home(env), ...DEFAULT_ACCOUNTS_HOME);
|
|
298
482
|
return {
|
|
299
|
-
store: storeOverride ??
|
|
300
|
-
profiles:
|
|
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 (!
|
|
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 (!
|
|
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(
|
|
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]) ??
|
|
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 =
|
|
352
|
-
if (
|
|
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 =
|
|
357
|
-
if (
|
|
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) :
|
|
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 =
|
|
479
|
-
if (!
|
|
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
|
|
495
|
-
import { join as
|
|
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 =
|
|
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: "
|
|
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
|
|
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.
|
|
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: "
|
|
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/
|
|
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.
|
|
1142
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
902
1143
|
zod: "3.25.76"
|
|
903
1144
|
},
|
|
904
1145
|
devDependencies: {
|
|
905
|
-
"@types/bun": "
|
|
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
|
|
911
|
-
import { homedir as
|
|
912
|
-
import { dirname as dirname2, join as
|
|
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
|
|
1156
|
+
return join6(env.HOME || homedir6(), ".claude", "settings.json");
|
|
915
1157
|
}
|
|
916
1158
|
function claudeSettingsPath(env = process.env) {
|
|
917
|
-
const home2 = env.HOME ||
|
|
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("~/") ?
|
|
922
|
-
return
|
|
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 (
|
|
1170
|
+
if (existsSync6(target)) {
|
|
929
1171
|
try {
|
|
930
|
-
settings = JSON.parse(
|
|
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 (!
|
|
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
|
-
|
|
1317
|
+
codexCacheRate,
|
|
1318
|
+
claudeSettingsPath,
|
|
1319
|
+
cacheRate
|
|
1065
1320
|
};
|