@hasna/statusline 0.0.2 → 0.0.4
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 +68 -1
- package/dist/accounts.d.ts +50 -0
- package/dist/cli.js +329 -51
- package/dist/config.d.ts +2 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +326 -52
- package/dist/install.d.ts +12 -1
- package/dist/mcp/index.js +6518 -18984
- package/dist/providers/types.d.ts +22 -0
- package/dist/render.d.ts +5 -1
- package/dist/settings.d.ts +11 -0
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,35 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
4
2
|
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);
|
|
33
3
|
var __returnValue = (v) => v;
|
|
34
4
|
function __exportSetter(name, newValue) {
|
|
35
5
|
this[name] = __returnValue.bind(null, newValue);
|
|
@@ -51,6 +21,7 @@ import { dirname, join } from "path";
|
|
|
51
21
|
function defaultConfig() {
|
|
52
22
|
return {
|
|
53
23
|
separator: " \xB7 ",
|
|
24
|
+
colors: true,
|
|
54
25
|
segments: [
|
|
55
26
|
"machine",
|
|
56
27
|
"project",
|
|
@@ -78,7 +49,8 @@ function loadConfig(path = configPath()) {
|
|
|
78
49
|
const fallback = defaultConfig();
|
|
79
50
|
return {
|
|
80
51
|
separator: typeof raw.separator === "string" ? raw.separator : fallback.separator,
|
|
81
|
-
segments: Array.isArray(raw.segments) ? raw.segments.filter((s) => typeof s === "string") : fallback.segments
|
|
52
|
+
segments: Array.isArray(raw.segments) ? raw.segments.filter((s) => typeof s === "string") : fallback.segments,
|
|
53
|
+
colors: typeof raw.colors === "boolean" ? raw.colors : fallback.colors
|
|
82
54
|
};
|
|
83
55
|
} catch {
|
|
84
56
|
return defaultConfig();
|
|
@@ -106,8 +78,15 @@ function parseClaudeInput(input) {
|
|
|
106
78
|
} : undefined,
|
|
107
79
|
transcriptPath: input?.transcript_path || undefined,
|
|
108
80
|
sessionId: input?.session_id || undefined,
|
|
81
|
+
sessionName: input?.session_name || undefined,
|
|
109
82
|
version: input?.version || undefined,
|
|
110
|
-
outputStyle: input?.output_style?.name || undefined
|
|
83
|
+
outputStyle: input?.output_style?.name || undefined,
|
|
84
|
+
effort: input?.effort?.level || undefined,
|
|
85
|
+
thinking: typeof input?.thinking?.enabled === "boolean" ? input.thinking.enabled : undefined,
|
|
86
|
+
rateLimits: input?.rate_limits ? {
|
|
87
|
+
fiveHour: { usedPercentage: numberOr(input.rate_limits.five_hour?.used_percentage) },
|
|
88
|
+
sevenDay: { usedPercentage: numberOr(input.rate_limits.seven_day?.used_percentage) }
|
|
89
|
+
} : undefined
|
|
111
90
|
};
|
|
112
91
|
}
|
|
113
92
|
function numberOr(v) {
|
|
@@ -252,6 +231,158 @@ function windowSize(ctx) {
|
|
|
252
231
|
return ctx.model?.id?.includes("[1m]") ? 1e6 : 200000;
|
|
253
232
|
}
|
|
254
233
|
|
|
234
|
+
// 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";
|
|
238
|
+
var CONFIG_DIR_ENV = "CLAUDE_CONFIG_DIR";
|
|
239
|
+
var DEFAULT_CONFIG_SUBDIR = ".claude";
|
|
240
|
+
var STORE_PATH_ENV = "ACCOUNTS_STORE_PATH";
|
|
241
|
+
var ACCOUNTS_HOME_ENV = "ACCOUNTS_HOME";
|
|
242
|
+
var DEFAULT_ACCOUNTS_HOME = [".hasna", "accounts"];
|
|
243
|
+
var STORE_FILE = "accounts.json";
|
|
244
|
+
var PROFILES_SUBDIR = "profiles";
|
|
245
|
+
var STATE_FILE_PREFERRED = ".config.json";
|
|
246
|
+
var STATE_FILE_PREFIX = ".claude";
|
|
247
|
+
var STATE_FILE_SUFFIX = ".json";
|
|
248
|
+
var CUSTOM_OAUTH_ENV = "CLAUDE_CODE_CUSTOM_OAUTH_URL";
|
|
249
|
+
var OAUTH_VARIANTS = ["", "-custom-oauth", "-local-oauth", "-staging-oauth"];
|
|
250
|
+
function str(value) {
|
|
251
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
252
|
+
}
|
|
253
|
+
function home(env) {
|
|
254
|
+
return str(env.HOME) ?? homedir2();
|
|
255
|
+
}
|
|
256
|
+
function expandHome(path, env) {
|
|
257
|
+
return path === "~" || path.startsWith(`~${sep}`) ? join2(home(env), path.slice(1)) : path;
|
|
258
|
+
}
|
|
259
|
+
function canonical(path, env) {
|
|
260
|
+
const abs = resolve(expandHome(path, env));
|
|
261
|
+
try {
|
|
262
|
+
return realpathSync(abs);
|
|
263
|
+
} catch {
|
|
264
|
+
return abs;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function readJson(path) {
|
|
268
|
+
try {
|
|
269
|
+
return JSON.parse(readFileSync3(path, "utf8"));
|
|
270
|
+
} catch {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
var ACCOUNTS_STORAGE_MODULE = "@hasna/accounts/storage";
|
|
275
|
+
var storagePromise;
|
|
276
|
+
function accountsStorage() {
|
|
277
|
+
storagePromise ??= import(ACCOUNTS_STORAGE_MODULE).then((mod) => typeof mod?.storePath === "function" && typeof mod?.profilesDir === "function" ? mod : null, () => null);
|
|
278
|
+
return storagePromise;
|
|
279
|
+
}
|
|
280
|
+
async function accountsPaths(env) {
|
|
281
|
+
const homeOverride = str(env[ACCOUNTS_HOME_ENV]);
|
|
282
|
+
const storeOverride = str(env[STORE_PATH_ENV]);
|
|
283
|
+
if (!homeOverride && !storeOverride) {
|
|
284
|
+
const storage = await accountsStorage();
|
|
285
|
+
try {
|
|
286
|
+
if (storage)
|
|
287
|
+
return { store: storage.storePath(), profiles: storage.profilesDir() };
|
|
288
|
+
} catch {}
|
|
289
|
+
}
|
|
290
|
+
const base = homeOverride ?? join2(home(env), ...DEFAULT_ACCOUNTS_HOME);
|
|
291
|
+
return {
|
|
292
|
+
store: storeOverride ?? join2(base, STORE_FILE),
|
|
293
|
+
profiles: join2(base, PROFILES_SUBDIR)
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function registryEntry(store, configDir, env, tool) {
|
|
297
|
+
if (!existsSync3(store))
|
|
298
|
+
return null;
|
|
299
|
+
const entries = readJson(store)?.profiles;
|
|
300
|
+
if (!Array.isArray(entries))
|
|
301
|
+
return null;
|
|
302
|
+
let untyped = null;
|
|
303
|
+
let first = null;
|
|
304
|
+
for (const entry of entries) {
|
|
305
|
+
const dir = str(entry?.dir);
|
|
306
|
+
if (!dir || canonical(dir, env) !== configDir)
|
|
307
|
+
continue;
|
|
308
|
+
const entryTool = str(entry?.tool);
|
|
309
|
+
if (tool && entryTool === tool)
|
|
310
|
+
return entry;
|
|
311
|
+
if (!entryTool)
|
|
312
|
+
untyped ??= entry;
|
|
313
|
+
first ??= entry;
|
|
314
|
+
}
|
|
315
|
+
return tool ? untyped : first;
|
|
316
|
+
}
|
|
317
|
+
function layoutProfile(profilesRoot, configDir, env) {
|
|
318
|
+
if (!existsSync3(configDir))
|
|
319
|
+
return null;
|
|
320
|
+
const prefix = canonical(profilesRoot, env) + sep;
|
|
321
|
+
if (!configDir.startsWith(prefix))
|
|
322
|
+
return null;
|
|
323
|
+
const parts = configDir.slice(prefix.length).split(sep);
|
|
324
|
+
if (parts.length !== 2)
|
|
325
|
+
return null;
|
|
326
|
+
const [tool, profile] = parts;
|
|
327
|
+
if (!tool || !profile)
|
|
328
|
+
return null;
|
|
329
|
+
return { configDir, profile, tool, source: "layout" };
|
|
330
|
+
}
|
|
331
|
+
function sessionConfigDir(env = process.env) {
|
|
332
|
+
return canonical(str(env[CONFIG_DIR_ENV]) ?? join2(home(env), DEFAULT_CONFIG_SUBDIR), env);
|
|
333
|
+
}
|
|
334
|
+
function sessionStateFile(env = process.env) {
|
|
335
|
+
const base = canonical(str(env[CONFIG_DIR_ENV]) ?? home(env), env);
|
|
336
|
+
const preferred = join2(base, STATE_FILE_PREFERRED);
|
|
337
|
+
if (existsSync3(preferred))
|
|
338
|
+
return preferred;
|
|
339
|
+
const variants = str(env[CUSTOM_OAUTH_ENV]) ? ["-custom-oauth", ...OAUTH_VARIANTS] : OAUTH_VARIANTS;
|
|
340
|
+
for (const variant of variants) {
|
|
341
|
+
const candidate = join2(base, `${STATE_FILE_PREFIX}${variant}${STATE_FILE_SUFFIX}`);
|
|
342
|
+
if (existsSync3(candidate))
|
|
343
|
+
return candidate;
|
|
344
|
+
}
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
async function sessionAccount(env = process.env, tool) {
|
|
348
|
+
const configDir = sessionConfigDir(env);
|
|
349
|
+
const unresolved = { configDir, profile: null, tool: null, source: null };
|
|
350
|
+
try {
|
|
351
|
+
const paths = await accountsPaths(env);
|
|
352
|
+
const entry = registryEntry(paths.store, configDir, env, tool);
|
|
353
|
+
const name = str(entry?.name);
|
|
354
|
+
if (name)
|
|
355
|
+
return { configDir, profile: name, tool: str(entry?.tool), source: "registry" };
|
|
356
|
+
return layoutProfile(paths.profiles, configDir, env) ?? unresolved;
|
|
357
|
+
} catch {
|
|
358
|
+
return unresolved;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async function sessionAccountEmail(env = process.env, tool) {
|
|
362
|
+
try {
|
|
363
|
+
const stateFile = sessionStateFile(env);
|
|
364
|
+
const live = stateFile ? str(readJson(stateFile)?.oauthAccount?.emailAddress) : null;
|
|
365
|
+
if (live)
|
|
366
|
+
return live;
|
|
367
|
+
const paths = await accountsPaths(env);
|
|
368
|
+
return str(registryEntry(paths.store, sessionConfigDir(env), env, tool)?.email);
|
|
369
|
+
} catch {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/settings.ts
|
|
375
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
376
|
+
import { join as join3 } from "path";
|
|
377
|
+
function sessionFastMode(env = process.env) {
|
|
378
|
+
try {
|
|
379
|
+
const raw = readFileSync4(join3(sessionConfigDir(env), "settings.json"), "utf8");
|
|
380
|
+
return JSON.parse(raw)?.fastMode === true;
|
|
381
|
+
} catch {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
255
386
|
// src/segments/index.ts
|
|
256
387
|
function friendlyModel(id) {
|
|
257
388
|
let tag = null;
|
|
@@ -269,6 +400,14 @@ function friendlyModel(id) {
|
|
|
269
400
|
return { name: name || mid, tag };
|
|
270
401
|
}
|
|
271
402
|
var dir = (ctx) => ctx.cwd || process.cwd();
|
|
403
|
+
function modelName(ctx) {
|
|
404
|
+
if (ctx.model?.displayName)
|
|
405
|
+
return ctx.model.displayName.toLowerCase();
|
|
406
|
+
return ctx.model?.id ? friendlyModel(ctx.model.id).name : null;
|
|
407
|
+
}
|
|
408
|
+
function limitPercent(pct) {
|
|
409
|
+
return typeof pct === "number" ? pct : 0;
|
|
410
|
+
}
|
|
272
411
|
var segments = [
|
|
273
412
|
{
|
|
274
413
|
id: "machine",
|
|
@@ -347,6 +486,73 @@ var segments = [
|
|
|
347
486
|
return tag ? `${name} ${tag}` : name;
|
|
348
487
|
}
|
|
349
488
|
},
|
|
489
|
+
{
|
|
490
|
+
id: "model-with-reasoning",
|
|
491
|
+
description: "Model name with its reasoning effort (or thinking, when on)",
|
|
492
|
+
defaultEnabled: false,
|
|
493
|
+
color: "cyan",
|
|
494
|
+
render(ctx) {
|
|
495
|
+
const name = modelName(ctx);
|
|
496
|
+
if (!name)
|
|
497
|
+
return null;
|
|
498
|
+
if (ctx.effort)
|
|
499
|
+
return `${name} (${ctx.effort.toLowerCase()})`;
|
|
500
|
+
if (ctx.thinking)
|
|
501
|
+
return `${name} (thinking)`;
|
|
502
|
+
return name;
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
id: "fast-mode",
|
|
507
|
+
description: `Shows "fast" when fast mode is on for this session's config dir`,
|
|
508
|
+
defaultEnabled: false,
|
|
509
|
+
color: "yellow",
|
|
510
|
+
render: () => sessionFastMode(process.env) ? "fast" : null
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
id: "auth-profile",
|
|
514
|
+
description: "Account profile this session runs as (omitted when unmanaged)",
|
|
515
|
+
defaultEnabled: false,
|
|
516
|
+
color: "magenta",
|
|
517
|
+
async render(ctx) {
|
|
518
|
+
const account = await sessionAccount(process.env, ctx.provider);
|
|
519
|
+
return account.profile ?? await sessionAccountEmail(process.env, ctx.provider);
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
id: "auth-email",
|
|
524
|
+
description: "Account email this session is logged in as (omitted when unknown)",
|
|
525
|
+
defaultEnabled: false,
|
|
526
|
+
color: "magenta",
|
|
527
|
+
render: (ctx) => sessionAccountEmail(process.env, ctx.provider)
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
id: "five-hour-limit",
|
|
531
|
+
description: "Percentage of the 5-hour rate limit used (omitted when unreported)",
|
|
532
|
+
defaultEnabled: false,
|
|
533
|
+
color: (ctx) => limitPercent(ctx.rateLimits?.fiveHour?.usedPercentage) >= 80 ? "red" : "yellow",
|
|
534
|
+
render(ctx) {
|
|
535
|
+
const pct = ctx.rateLimits?.fiveHour?.usedPercentage;
|
|
536
|
+
return typeof pct === "number" ? `5h:${Math.round(pct)}%` : null;
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
id: "seven-day-limit",
|
|
541
|
+
description: "Percentage of the 7-day rate limit used (omitted when unreported)",
|
|
542
|
+
defaultEnabled: false,
|
|
543
|
+
color: (ctx) => limitPercent(ctx.rateLimits?.sevenDay?.usedPercentage) >= 80 ? "red" : "yellow",
|
|
544
|
+
render(ctx) {
|
|
545
|
+
const pct = ctx.rateLimits?.sevenDay?.usedPercentage;
|
|
546
|
+
return typeof pct === "number" ? `7d:${Math.round(pct)}%` : null;
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
id: "thread-title",
|
|
551
|
+
description: "Thread title set with /rename (omitted until named)",
|
|
552
|
+
defaultEnabled: false,
|
|
553
|
+
color: "blue",
|
|
554
|
+
render: (ctx) => ctx.sessionName || null
|
|
555
|
+
},
|
|
350
556
|
{
|
|
351
557
|
id: "context-used",
|
|
352
558
|
description: "Percentage of context window used (omitted when unknown)",
|
|
@@ -430,6 +636,12 @@ var segments = [
|
|
|
430
636
|
description: "Session identifier (short)",
|
|
431
637
|
defaultEnabled: false,
|
|
432
638
|
render: (ctx) => ctx.sessionId ? ctx.sessionId.split("-")[0] : null
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
id: "newline",
|
|
642
|
+
description: "Row break \u2014 segments after it render on the next line",
|
|
643
|
+
defaultEnabled: false,
|
|
644
|
+
render: () => null
|
|
433
645
|
}
|
|
434
646
|
];
|
|
435
647
|
function getSegment(id) {
|
|
@@ -437,24 +649,51 @@ function getSegment(id) {
|
|
|
437
649
|
}
|
|
438
650
|
|
|
439
651
|
// src/render.ts
|
|
652
|
+
var ANSI = {
|
|
653
|
+
cyan: "\x1B[36m",
|
|
654
|
+
magenta: "\x1B[35m",
|
|
655
|
+
blue: "\x1B[34m",
|
|
656
|
+
green: "\x1B[32m",
|
|
657
|
+
yellow: "\x1B[33m",
|
|
658
|
+
red: "\x1B[31m",
|
|
659
|
+
bold: "\x1B[1m",
|
|
660
|
+
dim: "\x1B[2m"
|
|
661
|
+
};
|
|
662
|
+
var RESET = "\x1B[0m";
|
|
663
|
+
function colorsEnabled(cfg, env = process.env) {
|
|
664
|
+
if (env.NO_COLOR)
|
|
665
|
+
return false;
|
|
666
|
+
return cfg.colors !== false;
|
|
667
|
+
}
|
|
668
|
+
function paint(segment, text, ctx) {
|
|
669
|
+
const color = typeof segment.color === "function" ? segment.color(ctx) : segment.color;
|
|
670
|
+
const code = color ? ANSI[color] : undefined;
|
|
671
|
+
return code ? `${code}${text}${RESET}` : text;
|
|
672
|
+
}
|
|
440
673
|
async function renderLine(ctx, cfg) {
|
|
441
|
-
const
|
|
674
|
+
const colorize = colorsEnabled(cfg);
|
|
675
|
+
const rows = [[]];
|
|
442
676
|
for (const id of cfg.segments) {
|
|
677
|
+
if (id === "newline") {
|
|
678
|
+
rows.push([]);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
443
681
|
const segment = getSegment(id);
|
|
444
682
|
if (!segment)
|
|
445
683
|
continue;
|
|
446
684
|
try {
|
|
447
685
|
const out = await segment.render(ctx);
|
|
448
686
|
if (out)
|
|
449
|
-
|
|
687
|
+
rows[rows.length - 1].push(colorize ? paint(segment, out, ctx) : out);
|
|
450
688
|
} catch {}
|
|
451
689
|
}
|
|
452
|
-
return parts.join(cfg.separator)
|
|
690
|
+
return rows.map((parts) => parts.join(cfg.separator)).filter((row) => row.length > 0).join(`
|
|
691
|
+
`);
|
|
453
692
|
}
|
|
454
693
|
// package.json
|
|
455
694
|
var package_default = {
|
|
456
695
|
name: "@hasna/statusline",
|
|
457
|
-
version: "0.0.
|
|
696
|
+
version: "0.0.4",
|
|
458
697
|
description: "Tiny composable statusline CLI for AI coding agents \u2014 pre-built toggleable segments for Claude Code, with a provider model for others",
|
|
459
698
|
license: "Apache-2.0",
|
|
460
699
|
type: "module",
|
|
@@ -514,30 +753,52 @@ var package_default = {
|
|
|
514
753
|
bun: ">=1.0.0"
|
|
515
754
|
},
|
|
516
755
|
dependencies: {
|
|
517
|
-
"@modelcontextprotocol/sdk": "
|
|
518
|
-
zod: "
|
|
756
|
+
"@modelcontextprotocol/sdk": "1.12.1",
|
|
757
|
+
zod: "3.25.76"
|
|
519
758
|
},
|
|
520
759
|
devDependencies: {
|
|
521
760
|
"@types/bun": "^1.2.4",
|
|
522
|
-
typescript: "
|
|
761
|
+
typescript: "5.9.3"
|
|
523
762
|
}
|
|
524
763
|
};
|
|
525
764
|
// src/install.ts
|
|
526
|
-
import { copyFileSync, existsSync as
|
|
527
|
-
import { homedir as
|
|
528
|
-
import { dirname as dirname2, join as
|
|
529
|
-
function
|
|
765
|
+
import { copyFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
766
|
+
import { homedir as homedir3 } from "os";
|
|
767
|
+
import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
|
|
768
|
+
function defaultSettingsPath(env) {
|
|
769
|
+
return join4(env.HOME || homedir3(), ".claude", "settings.json");
|
|
770
|
+
}
|
|
771
|
+
function claudeSettingsPath(env = process.env) {
|
|
772
|
+
const home2 = env.HOME || homedir3();
|
|
773
|
+
const raw = env.CLAUDE_CONFIG_DIR?.trim();
|
|
774
|
+
if (!raw)
|
|
775
|
+
return defaultSettingsPath(env);
|
|
776
|
+
const configDir = raw === "~" || raw.startsWith("~/") ? join4(home2, raw.slice(1)) : raw;
|
|
777
|
+
return join4(configDir, "settings.json");
|
|
778
|
+
}
|
|
779
|
+
function installClaude(settingsPath, env = process.env) {
|
|
780
|
+
const target = settingsPath ?? claudeSettingsPath(env);
|
|
530
781
|
const binary = Bun.which("statusline") || "statusline";
|
|
531
782
|
let settings = {};
|
|
532
|
-
if (
|
|
533
|
-
|
|
534
|
-
|
|
783
|
+
if (existsSync4(target)) {
|
|
784
|
+
try {
|
|
785
|
+
settings = JSON.parse(readFileSync5(target, "utf8"));
|
|
786
|
+
} catch (error) {
|
|
787
|
+
throw new Error(`${target} is not valid JSON \u2014 fix or move it first`);
|
|
788
|
+
}
|
|
789
|
+
copyFileSync(target, `${target}.bak-statusline`);
|
|
535
790
|
}
|
|
536
|
-
settings.statusLine = { type: "command", command: `${binary} render` };
|
|
537
|
-
|
|
538
|
-
|
|
791
|
+
settings.statusLine = { ...settings.statusLine, type: "command", command: `${binary} render` };
|
|
792
|
+
const configDir = dirname2(target);
|
|
793
|
+
if (!existsSync4(configDir)) {
|
|
794
|
+
if (resolve2(target) !== resolve2(defaultSettingsPath(env))) {
|
|
795
|
+
throw new Error(`config dir ${configDir} does not exist \u2014 create the profile first`);
|
|
796
|
+
}
|
|
797
|
+
mkdirSync2(configDir, { recursive: true });
|
|
798
|
+
}
|
|
799
|
+
writeFileSync2(target, JSON.stringify(settings, null, 2) + `
|
|
539
800
|
`);
|
|
540
|
-
return
|
|
801
|
+
return target;
|
|
541
802
|
}
|
|
542
803
|
|
|
543
804
|
// src/index.ts
|
|
@@ -589,6 +850,9 @@ function setSeparator(separator, config = loadConfig()) {
|
|
|
589
850
|
throw new Error("separator must be a non-empty string");
|
|
590
851
|
return { config: { ...config, separator }, changed: ["separator"] };
|
|
591
852
|
}
|
|
853
|
+
function setColors(colors, config = loadConfig()) {
|
|
854
|
+
return { config: { ...config, colors }, changed: ["colors"] };
|
|
855
|
+
}
|
|
592
856
|
function resetStatuslineConfig() {
|
|
593
857
|
return defaultConfig();
|
|
594
858
|
}
|
|
@@ -601,7 +865,11 @@ function sampleClaudeInput(cwd = process.cwd()) {
|
|
|
601
865
|
model: { id: "claude-fable-5[1m]", display_name: "Fable" },
|
|
602
866
|
cost: { total_cost_usd: 12.34, total_duration_ms: 5400000, total_lines_added: 42, total_lines_removed: 7 },
|
|
603
867
|
version: statuslineVersion,
|
|
604
|
-
session_id: "preview0-0000-0000-0000-000000000000"
|
|
868
|
+
session_id: "preview0-0000-0000-0000-000000000000",
|
|
869
|
+
session_name: "preview thread",
|
|
870
|
+
effort: { level: "high" },
|
|
871
|
+
thinking: { enabled: true },
|
|
872
|
+
rate_limits: { five_hour: { used_percentage: 42 } }
|
|
605
873
|
};
|
|
606
874
|
}
|
|
607
875
|
async function previewStatusline(config = loadConfig(), cwd = process.cwd()) {
|
|
@@ -615,6 +883,11 @@ export {
|
|
|
615
883
|
trackedLineCount,
|
|
616
884
|
statuslineVersion,
|
|
617
885
|
setSeparator,
|
|
886
|
+
setColors,
|
|
887
|
+
sessionStateFile,
|
|
888
|
+
sessionConfigDir,
|
|
889
|
+
sessionAccountEmail,
|
|
890
|
+
sessionAccount,
|
|
618
891
|
segments,
|
|
619
892
|
saveUpdatedConfig,
|
|
620
893
|
saveConfig,
|
|
@@ -642,5 +915,6 @@ export {
|
|
|
642
915
|
configPath,
|
|
643
916
|
compactNum,
|
|
644
917
|
compactDuration,
|
|
645
|
-
compactAge
|
|
918
|
+
compactAge,
|
|
919
|
+
claudeSettingsPath
|
|
646
920
|
};
|
package/dist/install.d.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings file the running agent actually reads. When a session is bound to
|
|
3
|
+
* an isolated config dir (multi-account setups export `CLAUDE_CONFIG_DIR`),
|
|
4
|
+
* `~/.claude/settings.json` is never loaded — installing there wires up a
|
|
5
|
+
* statusline that silently never runs.
|
|
6
|
+
*/
|
|
7
|
+
export declare function claudeSettingsPath(env?: Record<string, string | undefined>): string;
|
|
1
8
|
/**
|
|
2
9
|
* Wire the statusline into Claude Code's user settings. Existing settings
|
|
3
10
|
* are preserved; the previous file is backed up alongside it.
|
|
11
|
+
*
|
|
12
|
+
* `env` resolves the default target and decides whether a missing dir may be
|
|
13
|
+
* created; pass the same object you would pass to `claudeSettingsPath()` so a
|
|
14
|
+
* caller driving several profiles is not steered by its own shell.
|
|
4
15
|
*/
|
|
5
|
-
export declare function installClaude(settingsPath?: string): string;
|
|
16
|
+
export declare function installClaude(settingsPath?: string, env?: Record<string, string | undefined>): string;
|