claude-use 11.49.1 → 11.50.0

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 CHANGED
@@ -99,6 +99,8 @@ agent-shim profile set <name> [--extends <profile>]... [--no-extends] [--descrip
99
99
  agent-shim profile set <name> [--[no-]launch-skip-permissions] [--[no-]launch-remote-control] [--[no-]launch-headroom] [--[no-]launch-track-usage]
100
100
  agent-shim profile set <name> [--launch-claude-version <version> | --no-launch-claude-version] [--json] [--json]
101
101
  agent-shim profile set <name> [--launch-delay <duration> | --no-launch-delay] [--[no-]launch-pause] [--[no-]launch-explain] [--launch-proxy-bypass <host>... | --no-launch-proxy-bypass] [--json]
102
+ agent-shim profile set <name> [--statusline-header <segment>... | --no-statusline-header] [--statusline-bars <group>... | --no-statusline-bars] [--statusline-warn-at <percent> | --no-statusline-warn-at] [--statusline-critical-at <percent> | --no-statusline-critical-at]
103
+ agent-shim profile set <name> [--statusline-five-hour-label <text> | --no-statusline-five-hour-label] [--statusline-seven-day-label <text> | --no-statusline-seven-day-label] [--statusline-bar-width <cells> | --no-statusline-bar-width]
102
104
  agent-shim profile set <name> [--launch-provider <provider> | --no-launch-provider] [--json]
103
105
  agent-shim profile list [--json]
104
106
  agent-shim profile show <name> [--json]
package/dist/cli.cjs CHANGED
@@ -25036,6 +25036,68 @@ var init_proxyBypass = __esm({
25036
25036
  }
25037
25037
  });
25038
25038
 
25039
+ // src/config/statusline.ts
25040
+ function distinctList(member) {
25041
+ return external_exports.array(member).refine((items) => new Set(items).size === items.length, "must not repeat a segment");
25042
+ }
25043
+ function resolveStatusline(settings2) {
25044
+ const resolved = {
25045
+ header: settings2?.header ?? STATUSLINE_DEFAULTS.header,
25046
+ bars: settings2?.bars ?? STATUSLINE_DEFAULTS.bars,
25047
+ warnAt: settings2?.warnAt ?? STATUSLINE_DEFAULTS.warnAt,
25048
+ criticalAt: settings2?.criticalAt ?? STATUSLINE_DEFAULTS.criticalAt,
25049
+ fiveHourLabel: settings2?.fiveHourLabel ?? STATUSLINE_DEFAULTS.fiveHourLabel,
25050
+ sevenDayLabel: settings2?.sevenDayLabel ?? STATUSLINE_DEFAULTS.sevenDayLabel,
25051
+ barWidth: settings2?.barWidth ?? STATUSLINE_DEFAULTS.barWidth
25052
+ };
25053
+ if (resolved.warnAt >= resolved.criticalAt) {
25054
+ throw new RangeError(`statusline warnAt (${String(resolved.warnAt)}) must be below criticalAt (${String(resolved.criticalAt)})`);
25055
+ }
25056
+ return resolved;
25057
+ }
25058
+ var STATUSLINE_HEADER_SEGMENTS, STATUSLINE_BAR_GROUPS, MAX_PERCENT, MAX_STATUSLINE_LABEL_CHARS, MAX_BAR_CELLS, LABEL_RE2, wholePercent, StatuslineSchema, StatuslineChangeSchema, STATUSLINE_DEFAULTS, STATUSLINE_ENV;
25059
+ var init_statusline = __esm({
25060
+ "src/config/statusline.ts"() {
25061
+ "use strict";
25062
+ init_zod();
25063
+ STATUSLINE_HEADER_SEGMENTS = ["model", "provider", "route", "account", "identity", "headroom"];
25064
+ STATUSLINE_BAR_GROUPS = ["context", "quota"];
25065
+ MAX_PERCENT = 100;
25066
+ MAX_STATUSLINE_LABEL_CHARS = 12;
25067
+ MAX_BAR_CELLS = 40;
25068
+ LABEL_RE2 = /^[^\p{C}\p{Z}]+(?: [^\p{C}\p{Z}]+)*$/u;
25069
+ wholePercent = external_exports.number().int().min(1).max(MAX_PERCENT);
25070
+ StatuslineSchema = external_exports.strictObject({
25071
+ header: distinctList(external_exports.enum(STATUSLINE_HEADER_SEGMENTS)).optional(),
25072
+ bars: distinctList(external_exports.enum(STATUSLINE_BAR_GROUPS)).optional(),
25073
+ warnAt: wholePercent.optional(),
25074
+ criticalAt: wholePercent.optional(),
25075
+ fiveHourLabel: external_exports.string().max(MAX_STATUSLINE_LABEL_CHARS).regex(LABEL_RE2, "must be visible text without control characters").optional(),
25076
+ sevenDayLabel: external_exports.string().max(MAX_STATUSLINE_LABEL_CHARS).regex(LABEL_RE2, "must be visible text without control characters").optional(),
25077
+ barWidth: external_exports.number().int().min(1).max(MAX_BAR_CELLS).optional()
25078
+ });
25079
+ StatuslineChangeSchema = external_exports.strictObject({
25080
+ header: external_exports.union([StatuslineSchema.shape.header.unwrap(), external_exports.literal(false)]).optional(),
25081
+ bars: external_exports.union([StatuslineSchema.shape.bars.unwrap(), external_exports.literal(false)]).optional(),
25082
+ warnAt: external_exports.union([StatuslineSchema.shape.warnAt.unwrap(), external_exports.literal(false)]).optional(),
25083
+ criticalAt: external_exports.union([StatuslineSchema.shape.criticalAt.unwrap(), external_exports.literal(false)]).optional(),
25084
+ fiveHourLabel: external_exports.union([StatuslineSchema.shape.fiveHourLabel.unwrap(), external_exports.literal(false)]).optional(),
25085
+ sevenDayLabel: external_exports.union([StatuslineSchema.shape.sevenDayLabel.unwrap(), external_exports.literal(false)]).optional(),
25086
+ barWidth: external_exports.union([StatuslineSchema.shape.barWidth.unwrap(), external_exports.literal(false)]).optional()
25087
+ });
25088
+ STATUSLINE_DEFAULTS = {
25089
+ header: STATUSLINE_HEADER_SEGMENTS,
25090
+ bars: STATUSLINE_BAR_GROUPS,
25091
+ warnAt: 70,
25092
+ criticalAt: 90,
25093
+ fiveHourLabel: "5h",
25094
+ sevenDayLabel: "Wk",
25095
+ barWidth: 10
25096
+ };
25097
+ STATUSLINE_ENV = "AGENT_SHIM_STATUSLINE";
25098
+ }
25099
+ });
25100
+
25039
25101
  // node_modules/.pnpm/trilean@1.7.1/node_modules/trilean/dist/complex.js
25040
25102
  var complexFromPolar;
25041
25103
  var init_complex = __esm({
@@ -27037,6 +27099,7 @@ var init_schema = __esm({
27037
27099
  "src/config/schema.ts"() {
27038
27100
  "use strict";
27039
27101
  init_proxyBypass();
27102
+ init_statusline();
27040
27103
  import_node_path4 = __toESM(require("node:path"), 1);
27041
27104
  init_zod();
27042
27105
  init_dist2();
@@ -27088,7 +27151,9 @@ var init_schema = __esm({
27088
27151
  context.addIssue({ code: "custom", message: `"${entry}" ${problem}` });
27089
27152
  }
27090
27153
  })
27091
- ).optional()
27154
+ ).optional(),
27155
+ /** How the launched session's built-in statusline looks, exported to it as `AGENT_SHIM_STATUSLINE`. Merged key by key across layers, unlike the flags above, which are single values. */
27156
+ statusline: StatuslineSchema.optional()
27092
27157
  });
27093
27158
  CREDENTIAL_TARGETS = ["bearer", "apiKey", "oauthToken"];
27094
27159
  PROVIDER_CREDENTIAL_TARGETS = ["bearer", "apiKey"];
@@ -31902,6 +31967,17 @@ function removeProfile(paths, name) {
31902
31967
  requireProfileExists(paths, name);
31903
31968
  import_node_fs5.default.rmSync(profileJsonPath(paths, name));
31904
31969
  }
31970
+ function withStatuslineChange(existing, change) {
31971
+ const merged = new Map(Object.entries({ ...existing }));
31972
+ for (const [key, value2] of Object.entries(change)) {
31973
+ if (value2 === false) {
31974
+ merged.delete(key);
31975
+ } else {
31976
+ merged.set(key, value2);
31977
+ }
31978
+ }
31979
+ return merged.size === 0 ? void 0 : StatuslineSchema.parse(Object.fromEntries(merged));
31980
+ }
31905
31981
  function applyProfileChange(paths, name, change) {
31906
31982
  let touched = false;
31907
31983
  if (change.category !== void 0) {
@@ -31950,6 +32026,9 @@ function applyProfileChange(paths, name, change) {
31950
32026
  if (change.launchExplain !== void 0) {
31951
32027
  launchPatch.explain = change.launchExplain;
31952
32028
  }
32029
+ if (change.launchStatusline !== void 0) {
32030
+ launchPatch.statusline = withStatuslineChange(readProfile(paths, name)?.launch?.statusline, change.launchStatusline);
32031
+ }
31953
32032
  if (Object.keys(launchPatch).length > 0) {
31954
32033
  setProfileLaunchFlags(paths, name, launchPatch);
31955
32034
  touched = true;
@@ -31965,6 +32044,7 @@ var init_configProfilesStore = __esm({
31965
32044
  init_store();
31966
32045
  init_schema();
31967
32046
  init_cliError();
32047
+ init_statusline();
31968
32048
  init_load();
31969
32049
  ProfileNotFoundError = class extends CliError {
31970
32050
  constructor(profileName) {
@@ -32922,6 +33002,9 @@ function flattenLayers(layers, options) {
32922
33002
  if (layer.launch.proxyBypass !== void 0) {
32923
33003
  launch.proxyBypass = layer.launch.proxyBypass;
32924
33004
  }
33005
+ if (layer.launch.statusline !== void 0) {
33006
+ launch.statusline = { ...launch.statusline, ...layer.launch.statusline };
33007
+ }
32925
33008
  }
32926
33009
  if (layer.models !== void 0) {
32927
33010
  models = mergeModels(models, layer.models);
@@ -35927,7 +36010,7 @@ var init_package = __esm({
35927
36010
  "package.json"() {
35928
36011
  package_default = {
35929
36012
  name: "claude-use",
35930
- version: "11.49.1",
36013
+ version: "11.50.0",
35931
36014
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
35932
36015
  license: "MIT",
35933
36016
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -64666,16 +64749,22 @@ function channelRefusalMessage(channel) {
64666
64749
  function underBrewPrefix(executableRealPath) {
64667
64750
  return BREW_PREFIXES.some((prefix) => executableRealPath === prefix || executableRealPath.startsWith(`${prefix}/`));
64668
64751
  }
64752
+ function inHomebrewKeg(executableRealPath) {
64753
+ return underBrewPrefix(executableRealPath) && executableRealPath.split(/[\\/]/).includes("Cellar");
64754
+ }
64669
64755
  function detectChannel(env) {
64670
64756
  if (env.platform === "win32") {
64671
64757
  return { kind: "scoop" };
64672
64758
  }
64673
- if (underBrewPrefix(env.executableRealPath)) {
64759
+ if (inHomebrewKeg(env.executableRealPath)) {
64674
64760
  return { kind: "brew" };
64675
64761
  }
64676
64762
  if (env.executableRealPath.split(/[\\/]/).includes("node_modules")) {
64677
64763
  return { kind: "npm", reason: "node_modules" };
64678
64764
  }
64765
+ if (underBrewPrefix(env.executableRealPath)) {
64766
+ return { kind: "brew" };
64767
+ }
64679
64768
  if ((env.fileHead ?? "").startsWith("#!")) {
64680
64769
  return { kind: "npm", reason: "script" };
64681
64770
  }
@@ -65262,8 +65351,39 @@ var init_headers = __esm({
65262
65351
  }
65263
65352
  });
65264
65353
 
65354
+ // src/statusline/settings.ts
65355
+ function checkedStatusline(settings2, source) {
65356
+ try {
65357
+ resolveStatusline(settings2);
65358
+ } catch (error63) {
65359
+ throw new UsageError(`${source}: ${error63 instanceof Error ? error63.message : String(error63)}.`);
65360
+ }
65361
+ return settings2;
65362
+ }
65363
+ function statuslineSettingsFromEnv(raw, source) {
65364
+ let parsed;
65365
+ try {
65366
+ parsed = JSON.parse(raw);
65367
+ } catch {
65368
+ throw new UsageError(`${source}: not valid JSON.`);
65369
+ }
65370
+ const result = StatuslineSchema.safeParse(parsed);
65371
+ if (!result.success) {
65372
+ throw new UsageError(`${source}: ${result.error.issues.map((issue2) => `${issue2.path.join(".") || "(root)"}: ${issue2.message}`).join("; ")}.`);
65373
+ }
65374
+ return checkedStatusline(result.data, source);
65375
+ }
65376
+ var init_settings2 = __esm({
65377
+ "src/statusline/settings.ts"() {
65378
+ "use strict";
65379
+ init_cliError();
65380
+ init_statusline();
65381
+ }
65382
+ });
65383
+
65265
65384
  // src/launcher/flags.ts
65266
65385
  function resolveLaunchFlags(params) {
65386
+ const statusline = resolveStatuslineSettings(params);
65267
65387
  return {
65268
65388
  skipPermissions: params.flags?.skipPermissions ?? parseEnvBool("AGENT_SHIM_SKIP_PERMISSIONS", params.env.AGENT_SHIM_SKIP_PERMISSIONS) ?? params.cascade?.skipPermissions ?? false,
65269
65389
  remoteControl: params.flags?.remoteControl ?? parseEnvBool("AGENT_SHIM_REMOTE_CONTROL", params.env.AGENT_SHIM_REMOTE_CONTROL) ?? params.cascade?.remoteControl ?? false,
@@ -65272,9 +65392,17 @@ function resolveLaunchFlags(params) {
65272
65392
  pause: params.flags?.pause ?? parseEnvBool("AGENT_SHIM_PAUSE", params.env.AGENT_SHIM_PAUSE) ?? params.cascade?.pause ?? false,
65273
65393
  explain: params.flags?.explain ?? parseEnvBool("AGENT_SHIM_EXPLAIN", params.env.AGENT_SHIM_EXPLAIN) ?? params.cascade?.explain ?? false,
65274
65394
  delayMs: resolveDelayMs(params),
65275
- proxyBypass: resolveProxyBypass(params)
65395
+ proxyBypass: resolveProxyBypass(params),
65396
+ ...statusline === void 0 ? {} : { statusline }
65276
65397
  };
65277
65398
  }
65399
+ function resolveStatuslineSettings(params) {
65400
+ const fromEnv = params.env[STATUSLINE_ENV] === "" ? void 0 : params.env[STATUSLINE_ENV];
65401
+ if (fromEnv !== void 0) {
65402
+ return statuslineSettingsFromEnv(fromEnv, STATUSLINE_ENV);
65403
+ }
65404
+ return params.cascade?.statusline === void 0 ? void 0 : checkedStatusline(params.cascade.statusline, "launch.statusline");
65405
+ }
65278
65406
  function resolveProxyBypass(params) {
65279
65407
  const fromEnv = params.env.AGENT_SHIM_PROXY_BYPASS === "" ? void 0 : params.env.AGENT_SHIM_PROXY_BYPASS;
65280
65408
  return fromEnv !== void 0 ? checkedProxyBypass(fromEnv.split(",").map((entry) => entry.trim()), "AGENT_SHIM_PROXY_BYPASS") : checkedProxyBypass(params.cascade?.proxyBypass ?? [], "launch.proxyBypass");
@@ -65358,6 +65486,9 @@ function buildEnv(params) {
65358
65486
  env.NO_PROXY = withBypassHosts(env.NO_PROXY, bypassed);
65359
65487
  env.no_proxy = withBypassHosts(env.no_proxy, bypassed);
65360
65488
  }
65489
+ if (params.statusline !== void 0) {
65490
+ env[STATUSLINE_ENV] = JSON.stringify(params.statusline);
65491
+ }
65361
65492
  return env;
65362
65493
  }
65363
65494
  var import_node_path42, LOOPBACK_HOSTS, PROVIDER_SESSION_PLACEHOLDER;
@@ -65373,6 +65504,8 @@ var init_flags = __esm({
65373
65504
  init_headers();
65374
65505
  init_capability();
65375
65506
  init_route();
65507
+ init_statusline();
65508
+ init_settings2();
65376
65509
  init_proxyBypass2();
65377
65510
  LOOPBACK_HOSTS = ["127.0.0.1", "localhost", "::1"];
65378
65511
  PROVIDER_SESSION_PLACEHOLDER = "agent-shim-provider-session";
@@ -67874,7 +68007,8 @@ function prepareLaunch(params) {
67874
68007
  sessionId,
67875
68008
  remoteControl: resolvedFlags.remoteControl,
67876
68009
  ...decided.models?.tiers === void 0 ? {} : { tierModels: decided.models.tiers },
67877
- proxyBypass: resolvedFlags.proxyBypass
68010
+ proxyBypass: resolvedFlags.proxyBypass,
68011
+ ...resolvedFlags.statusline === void 0 ? {} : { statusline: resolvedFlags.statusline }
67878
68012
  });
67879
68013
  const releases = [...headroom === void 0 || headroomPort === void 0 ? [] : [headroomPort.release], ...frontDoorRelease === void 0 ? [] : [frontDoorRelease]];
67880
68014
  const release = () => {
@@ -67902,7 +68036,8 @@ function resolveLaunch(params) {
67902
68036
  ...decided.flags.headroom ? { headroom: { socketPath: "", projectId: "" } } : {},
67903
68037
  sessionId: "",
67904
68038
  remoteControl: decided.flags.remoteControl,
67905
- proxyBypass: decided.flags.proxyBypass
68039
+ proxyBypass: decided.flags.proxyBypass,
68040
+ ...decided.flags.statusline === void 0 ? {} : { statusline: decided.flags.statusline }
67906
68041
  });
67907
68042
  const credentialNames = decided.identityCredentialBlock === void 0 ? [] : Object.keys(credentialVariables(decided.identityCredentialBlock.target ?? "bearer", ""));
67908
68043
  const environment = [.../* @__PURE__ */ new Set([...Object.keys(built).filter((name) => built[name] !== env[name]), ...credentialNames])].sort();
@@ -114982,6 +115117,7 @@ async function resolveFarmConflicts(params) {
114982
115117
  // src/configProfiles.ts
114983
115118
  init_commandDeps();
114984
115119
  init_cliError();
115120
+ init_statusline();
114985
115121
  init_proxyBypass2();
114986
115122
  init_configProfilesStore();
114987
115123
  async function ensureProfileExists(deps, name) {
@@ -115008,6 +115144,29 @@ async function ensureProfileExists(deps, name) {
115008
115144
  function toProfileView(name, profile, globalDefault) {
115009
115145
  return { name, globalDefault: name === globalDefault, ...profile, $schema: void 0 };
115010
115146
  }
115147
+ function segmentList(values) {
115148
+ return values.length === 1 && values[0] === "none" ? [] : values;
115149
+ }
115150
+ function statuslineChangeOf(options) {
115151
+ const raw = {
115152
+ header: options.statuslineHeader === void 0 || options.statuslineHeader === false ? options.statuslineHeader : segmentList(options.statuslineHeader),
115153
+ bars: options.statuslineBars === void 0 || options.statuslineBars === false ? options.statuslineBars : segmentList(options.statuslineBars),
115154
+ warnAt: options.statuslineWarnAt === void 0 || options.statuslineWarnAt === false ? options.statuslineWarnAt : Number(options.statuslineWarnAt),
115155
+ criticalAt: options.statuslineCriticalAt === void 0 || options.statuslineCriticalAt === false ? options.statuslineCriticalAt : Number(options.statuslineCriticalAt),
115156
+ fiveHourLabel: options.statuslineFiveHourLabel,
115157
+ sevenDayLabel: options.statuslineSevenDayLabel,
115158
+ barWidth: options.statuslineBarWidth === void 0 || options.statuslineBarWidth === false ? options.statuslineBarWidth : Number(options.statuslineBarWidth)
115159
+ };
115160
+ const given = Object.entries(raw).filter(([, value2]) => value2 !== void 0);
115161
+ if (given.length === 0) {
115162
+ return void 0;
115163
+ }
115164
+ const result = StatuslineSchema.safeParse(Object.fromEntries(given.filter(([, value2]) => value2 !== false)));
115165
+ if (!result.success) {
115166
+ throw new UsageError(`--statusline-*: ${result.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`).join("; ")}.`);
115167
+ }
115168
+ return Object.fromEntries(given);
115169
+ }
115011
115170
  function registerProfileCommand(program2, deps) {
115012
115171
  const { paths } = deps;
115013
115172
  const profile = withExamples(
@@ -115043,7 +115202,7 @@ function registerProfileCommand(program2, deps) {
115043
115202
  withExamples(
115044
115203
  profile.command("set <name>").description(
115045
115204
  "Update a configuration profile's categories, entries, extends list, description or launch settings. With no options on a terminal, walks through its categories."
115046
- ).option("--category <category=bool>", "Share (true) or hide (false) a whole category (repeatable).", collectBoolPair).option("--entry <path=bool>", "Share (true) or hide (false) one <category>/<path> entry (repeatable).", collectBoolPair).option("--extends <profile>", "Replace the extends list with these profiles (repeatable, in order).", collectRepeated).option("--no-extends", "Clear the extends list.").option("--description <text>", "Replace the description.").option("--no-description", "Clear the description.").option("--launch-skip-permissions", "Launches under this profile skip permission prompts.").option("--no-launch-skip-permissions", "Launches under this profile keep permission prompts.").option("--launch-remote-control", "Launches under this profile enable Remote Control.").option("--no-launch-remote-control", "Launches under this profile leave Remote Control off.").option("--launch-headroom", "Launches under this profile route through headroom.").option("--no-launch-headroom", "Launches under this profile route direct.").option("--launch-track-usage", "Launches under this profile route through the front door so their requests and quota are recorded, with no headroom needed.").option("--no-launch-track-usage", "Launches under this profile are not recorded unless a provider or headroom routes them anyway.").option("--launch-provider <provider>", "Launches under this profile route through this provider.").option("--no-launch-provider", "Clear this profile's provider selection.").option("--launch-claude-version <version>", "Launches under this profile run exactly this installed Claude Code version (such as 2.1.220); a version that is not installed fails the launch.").option("--no-launch-claude-version", "Clear this profile's Claude Code version pin.").option("--launch-delay <duration>", "Launches under this profile wait this long (such as 5s) after the launcher's own output before starting claude, so the output can be read.", parseDurationLiteral).option("--no-launch-delay", "Clear this profile's launch delay.").option("--launch-pause", "Launches under this profile wait for a key press after the launcher's own output before starting claude.").option("--no-launch-pause", "Launches under this profile start claude without waiting for a key press.").option("--launch-explain", "Launches under this profile that pick from a pool list every member the pick ranked and why, in the launcher's report.").option("--no-launch-explain", "Launches under this profile name only the member a pool pick chose.").option("--launch-proxy-bypass <host>", "Launches under this profile keep this host off the front door by adding it to NO_PROXY, so its traffic goes direct (repeatable; replaces the profile's list).", collectRepeated).option("--no-launch-proxy-bypass", "Clear this profile's list of hosts kept off the front door.").option("--json", "Print the result as JSON.").action(async (name, allOptions) => {
115205
+ ).option("--category <category=bool>", "Share (true) or hide (false) a whole category (repeatable).", collectBoolPair).option("--entry <path=bool>", "Share (true) or hide (false) one <category>/<path> entry (repeatable).", collectBoolPair).option("--extends <profile>", "Replace the extends list with these profiles (repeatable, in order).", collectRepeated).option("--no-extends", "Clear the extends list.").option("--description <text>", "Replace the description.").option("--no-description", "Clear the description.").option("--launch-skip-permissions", "Launches under this profile skip permission prompts.").option("--no-launch-skip-permissions", "Launches under this profile keep permission prompts.").option("--launch-remote-control", "Launches under this profile enable Remote Control.").option("--no-launch-remote-control", "Launches under this profile leave Remote Control off.").option("--launch-headroom", "Launches under this profile route through headroom.").option("--no-launch-headroom", "Launches under this profile route direct.").option("--launch-track-usage", "Launches under this profile route through the front door so their requests and quota are recorded, with no headroom needed.").option("--no-launch-track-usage", "Launches under this profile are not recorded unless a provider or headroom routes them anyway.").option("--launch-provider <provider>", "Launches under this profile route through this provider.").option("--no-launch-provider", "Clear this profile's provider selection.").option("--launch-claude-version <version>", "Launches under this profile run exactly this installed Claude Code version (such as 2.1.220); a version that is not installed fails the launch.").option("--no-launch-claude-version", "Clear this profile's Claude Code version pin.").option("--launch-delay <duration>", "Launches under this profile wait this long (such as 5s) after the launcher's own output before starting claude, so the output can be read.", parseDurationLiteral).option("--no-launch-delay", "Clear this profile's launch delay.").option("--launch-pause", "Launches under this profile wait for a key press after the launcher's own output before starting claude.").option("--no-launch-pause", "Launches under this profile start claude without waiting for a key press.").option("--launch-explain", "Launches under this profile that pick from a pool list every member the pick ranked and why, in the launcher's report.").option("--no-launch-explain", "Launches under this profile name only the member a pool pick chose.").option("--launch-proxy-bypass <host>", "Launches under this profile keep this host off the front door by adding it to NO_PROXY, so its traffic goes direct (repeatable; replaces the profile's list).", collectRepeated).option("--no-launch-proxy-bypass", "Clear this profile's list of hosts kept off the front door.").option("--statusline-header <segment>", `The statusline header segments to draw, in drawing order (repeatable; one of ${STATUSLINE_HEADER_SEGMENTS.join(", ")}, or "none" to hide the header).`, collectRepeated).option("--no-statusline-header", "Clear this profile's header segment list, restoring the default.").option("--statusline-bars <group>", `The statusline bar groups to draw, in drawing order (repeatable; one of ${STATUSLINE_BAR_GROUPS.join(", ")}, or "none" to hide the bars).`, collectRepeated).option("--no-statusline-bars", "Clear this profile's bar group list, restoring the default.").option("--statusline-warn-at <percent>", "The whole percentage of use at which a statusline bar turns yellow.").option("--no-statusline-warn-at", "Clear this profile's warning threshold.").option("--statusline-critical-at <percent>", "The whole percentage of use at which a statusline bar turns red.").option("--no-statusline-critical-at", "Clear this profile's critical threshold.").option("--statusline-five-hour-label <text>", "The statusline's name for the five-hour window.").option("--no-statusline-five-hour-label", "Clear this profile's five-hour window label.").option("--statusline-seven-day-label <text>", "The statusline's name for the weekly window.").option("--no-statusline-seven-day-label", "Clear this profile's weekly window label.").option("--statusline-bar-width <cells>", "The width of a statusline bar in cells, before the line's spare width is shared out among the bars.").option("--no-statusline-bar-width", "Clear this profile's bar width.").option("--json", "Print the result as JSON.").action(async (name, allOptions) => {
115047
115206
  const { json: json2, ...options } = allOptions;
115048
115207
  requireProfileExists(paths, name);
115049
115208
  if (Object.entries(allOptions).every(([key, value2]) => key === "json" || value2 === void 0)) {
@@ -115056,8 +115215,10 @@ function registerProfileCommand(program2, deps) {
115056
115215
  throw new PromptCancelledError();
115057
115216
  }
115058
115217
  } else {
115218
+ const launchStatusline = statuslineChangeOf(options);
115059
115219
  applyProfileChange(paths, name, {
115060
115220
  ...options,
115221
+ ...launchStatusline === void 0 ? {} : { launchStatusline },
115061
115222
  ...options.launchProxyBypass === void 0 || options.launchProxyBypass === false ? {} : { launchProxyBypass: checkedProxyBypass(options.launchProxyBypass, "--launch-proxy-bypass") }
115062
115223
  });
115063
115224
  }
@@ -129352,6 +129513,7 @@ init_providersStore();
129352
129513
  // src/frontdoor/configSchemas.ts
129353
129514
  init_zod();
129354
129515
  init_schema();
129516
+ init_statusline();
129355
129517
  var RemovalConfirmationSchema = external_exports.boolean();
129356
129518
  var CredentialWriteSchema = (targets) => external_exports.strictObject({
129357
129519
  sources: external_exports.array(RemoteCredentialSourceSchema).min(1).optional(),
@@ -129427,7 +129589,8 @@ var ConfigProfileSetInputSchema = external_exports.strictObject({
129427
129589
  delay: external_exports.union([external_exports.string().regex(DURATION_RE), external_exports.literal(false)]).optional(),
129428
129590
  pause: external_exports.boolean().optional(),
129429
129591
  explain: external_exports.boolean().optional(),
129430
- proxyBypass: external_exports.union([external_exports.array(external_exports.string().min(1)), external_exports.literal(false)]).optional()
129592
+ proxyBypass: external_exports.union([external_exports.array(external_exports.string().min(1)), external_exports.literal(false)]).optional(),
129593
+ statusline: StatuslineChangeSchema.optional()
129431
129594
  }).optional()
129432
129595
  });
129433
129596
  var ConfigProfileSetOutputSchema = storedMutationOutput("profile", "updated", ProfileValueSchema);
@@ -129640,7 +129803,8 @@ function createConfigApiRouter(deps) {
129640
129803
  ...launch?.delay === void 0 ? {} : { launchDelay: launch.delay },
129641
129804
  ...launch?.pause === void 0 ? {} : { launchPause: launch.pause },
129642
129805
  ...launch?.explain === void 0 ? {} : { launchExplain: launch.explain },
129643
- ...launch?.proxyBypass === void 0 ? {} : { launchProxyBypass: launch.proxyBypass === false ? false : checkedProxyBypass(launch.proxyBypass, "launch.proxyBypass") }
129806
+ ...launch?.proxyBypass === void 0 ? {} : { launchProxyBypass: launch.proxyBypass === false ? false : checkedProxyBypass(launch.proxyBypass, "launch.proxyBypass") },
129807
+ ...launch?.statusline === void 0 ? {} : { launchStatusline: launch.statusline }
129644
129808
  });
129645
129809
  return { action: "updated", kind: "profile", name: input2.name, value: profileValue(input2.name, paths) };
129646
129810
  })
@@ -129911,6 +130075,7 @@ init_cliError();
129911
130075
  // src/frontdoor/launchSchemas.ts
129912
130076
  var import_node_path51 = __toESM(require("node:path"), 1);
129913
130077
  init_zod();
130078
+ init_statusline();
129914
130079
  init_claudeVersion();
129915
130080
  init_identity();
129916
130081
  var LaunchResolveInputSchema = external_exports.strictObject({
@@ -129945,7 +130110,9 @@ var LaunchFlagsSchema = external_exports.strictObject({
129945
130110
  /** How long the launch waits after its own output before it starts claude, in milliseconds. */
129946
130111
  delayMs: external_exports.number(),
129947
130112
  /** The hosts the launch keeps off the front door by adding them to the child's `NO_PROXY`. */
129948
- proxyBypass: external_exports.array(external_exports.string()).readonly()
130113
+ proxyBypass: external_exports.array(external_exports.string()).readonly(),
130114
+ /** How the session's built-in statusline looks, when the launch configures any of it. */
130115
+ statusline: StatuslineSchema.optional()
129949
130116
  });
129950
130117
  var LaunchResolveOutputSchema = external_exports.strictObject({
129951
130118
  decision: LaunchDecisionSchema,
@@ -136388,6 +136555,7 @@ var import_node_child_process7 = require("node:child_process");
136388
136555
  var import_node_os12 = __toESM(require("node:os"), 1);
136389
136556
  var import_node_path56 = __toESM(require("node:path"), 1);
136390
136557
  init_commandDeps();
136558
+ init_statusline();
136391
136559
  init_providersStore();
136392
136560
  init_realPorts();
136393
136561
  init_account();
@@ -136712,6 +136880,9 @@ function visibleLength(text2) {
136712
136880
  return text2.replace(ANSI_REGEX, "").length;
136713
136881
  }
136714
136882
 
136883
+ // src/statusline/render.ts
136884
+ init_statusline();
136885
+
136715
136886
  // src/statusline/barGrid.ts
136716
136887
  var BRACKET_COLUMNS = 2;
136717
136888
  function chunk(items, size) {
@@ -136783,7 +136954,6 @@ function layoutBarGrid(groups, maxWidth, separator, baseBarCells) {
136783
136954
  }
136784
136955
 
136785
136956
  // src/statusline/render.ts
136786
- var BAR_WIDTH = 10;
136787
136957
  var SECONDS_PER_MINUTE2 = 60;
136788
136958
  var MINUTES_PER_HOUR4 = 60;
136789
136959
  var SECONDS_PER_DAY = 86400;
@@ -136795,8 +136965,6 @@ var RESERVED_COLUMNS = 4;
136795
136965
  var SEPARATOR2 = " | ";
136796
136966
  var FULL_PERCENT3 = 100;
136797
136967
  var ONE_PERCENT = 1;
136798
- var USED_RED_AT = 0.9;
136799
- var USED_YELLOW_AT = 0.7;
136800
136968
  var RESET = `${ESCAPE}[0m`;
136801
136969
  var DIM = `${ESCAPE}[90m`;
136802
136970
  var GREEN = `${ESCAPE}[32m`;
@@ -136805,20 +136973,22 @@ var RED = `${ESCAPE}[31m`;
136805
136973
  function colorize() {
136806
136974
  return process.env.NO_COLOR === void 0;
136807
136975
  }
136808
- function statusColor(fraction, isUsedMetric) {
136976
+ function statusColor(fraction, isUsedMetric, bands) {
136809
136977
  if (!colorize()) {
136810
136978
  return "";
136811
136979
  }
136980
+ const yellowAt = bands.warnAt / FULL_PERCENT3;
136981
+ const redAt = bands.criticalAt / FULL_PERCENT3;
136812
136982
  if (isUsedMetric) {
136813
- if (fraction >= USED_RED_AT) {
136983
+ if (fraction >= redAt) {
136814
136984
  return RED;
136815
136985
  }
136816
- return fraction >= USED_YELLOW_AT ? YELLOW : GREEN;
136986
+ return fraction >= yellowAt ? YELLOW : GREEN;
136817
136987
  }
136818
- if (fraction <= 1 - USED_RED_AT) {
136988
+ if (fraction <= 1 - redAt) {
136819
136989
  return RED;
136820
136990
  }
136821
- return fraction <= 1 - USED_YELLOW_AT ? YELLOW : GREEN;
136991
+ return fraction <= 1 - yellowAt ? YELLOW : GREEN;
136822
136992
  }
136823
136993
  function formatTokens(num) {
136824
136994
  if (num >= MILLION) {
@@ -136858,18 +137028,18 @@ function headroomText(stats) {
136858
137028
  ].filter((part) => part !== void 0);
136859
137029
  return parts.length === 0 ? void 0 : `headroom ${parts.join(" \xB7 ")}`;
136860
137030
  }
136861
- function cacheHitText(hitFraction) {
137031
+ function cacheHitText(hitFraction, bands) {
136862
137032
  const clamped = Math.max(0, Math.min(1, hitFraction));
136863
- return ` (Cache: ${statusColor(clamped, false)}${(clamped * FULL_PERCENT3).toFixed(0)}%${RESET})`;
137033
+ return ` (Cache: ${statusColor(clamped, false, bands)}${(clamped * FULL_PERCENT3).toFixed(0)}%${RESET})`;
136864
137034
  }
136865
- function cacheShareText(view) {
137035
+ function cacheShareText(view, bands) {
136866
137036
  const hitRatio = view.cacheHitRatio;
136867
137037
  if (hitRatio !== void 0 && Number.isFinite(hitRatio)) {
136868
- return cacheHitText(hitRatio);
137038
+ return cacheHitText(hitRatio, bands);
136869
137039
  }
136870
137040
  const promptTotal = (view.cacheReadTokens ?? 0) + (view.freshInputTokens ?? 0);
136871
137041
  if (promptTotal > 0 && (view.cacheReadTokens ?? 0) > 0) {
136872
- return cacheHitText((view.cacheReadTokens ?? 0) / promptTotal);
137042
+ return cacheHitText((view.cacheReadTokens ?? 0) / promptTotal, bands);
136873
137043
  }
136874
137044
  return "";
136875
137045
  }
@@ -136928,14 +137098,14 @@ function secondsUntil(resetAt, nowMs) {
136928
137098
  const resetMs = Date.parse(resetAt);
136929
137099
  return Number.isNaN(resetMs) ? void 0 : Math.max(0, Math.round((resetMs - nowMs) / THOUSAND));
136930
137100
  }
136931
- function drawBar(fraction, cells) {
137101
+ function drawBar(fraction, cells, bands) {
136932
137102
  const clamped = Math.max(0, Math.min(1, fraction));
136933
137103
  const filled = Math.round(clamped * cells);
136934
- const color = statusColor(clamped, true);
137104
+ const color = statusColor(clamped, true, bands);
136935
137105
  return color === "" ? `[${"\u2588".repeat(filled)}${"\u2591".repeat(cells - filled)}]` : `[${color}${"\u2588".repeat(filled)}${DIM}${"\u2591".repeat(cells - filled)}${RESET}]`;
136936
137106
  }
136937
- function barCell(prefix, suffix, fraction) {
136938
- return { prefix, suffix, draw: (cells) => drawBar(fraction, cells) };
137107
+ function barCell(prefix, suffix, fraction, bands) {
137108
+ return { prefix, suffix, draw: (cells) => drawBar(fraction, cells, bands) };
136939
137109
  }
136940
137110
  function justify(texts, maxWidth, separator) {
136941
137111
  if (texts.length < 2) {
@@ -136986,49 +137156,51 @@ function layoutHeader(texts, maxWidth, separator) {
136986
137156
  }
136987
137157
  return lines.map((line) => justify(line, maxWidth, separator)).join("\n");
136988
137158
  }
136989
- function renderStatuslineBlock(summary, view, terminalWidth, nowMs) {
136990
- const header = [];
137159
+ function renderStatuslineBlock(summary, view, terminalWidth, nowMs, settings2 = STATUSLINE_DEFAULTS) {
137160
+ const segments = {};
136991
137161
  const choiceNamesProvider = summary.modelChoice !== void 0 && (summary.modelChoice.providers.length > 1 || summary.routedTarget !== void 0);
136992
137162
  const modelSegment = summary.modelChoice !== void 0 && view.modelDisplayName !== void 0 && view.modelDisplayName !== "" ? `${view.modelDisplayName} -> ${choiceNamesProvider ? `${summary.modelChoice.provider}/${summary.modelChoice.served}` : summary.modelChoice.served}` : view.modelDisplayName;
136993
137163
  if (modelSegment !== void 0 && modelSegment !== "") {
136994
- header.push(modelSegment);
137164
+ segments.model = modelSegment;
136995
137165
  }
136996
137166
  if (summary.provider !== void 0) {
136997
137167
  const account = [summary.providerAccountEmail, summary.providerPlan].filter((part) => part !== void 0);
136998
- header.push(account.length > 0 ? `${summary.provider} \xB7 ${account.join(" \xB7 ")}` : summary.provider);
137168
+ segments.provider = account.length > 0 ? `${summary.provider} \xB7 ${account.join(" \xB7 ")}` : summary.provider;
136999
137169
  }
137000
137170
  if (summary.routedTarget !== void 0 && !choiceNamesProvider) {
137001
- header.push(`via ${summary.routedTarget}`);
137171
+ segments.route = `via ${summary.routedTarget}`;
137002
137172
  }
137003
137173
  const loginPlan = summary.provider === void 0 ? summary.loginPlan ?? accountPlanLabel(summary) : void 0;
137004
137174
  const loginOrgName = summary.loginOrganizationType === "claude_team" || summary.loginOrganizationType === "claude_enterprise" ? summary.loginOrganizationName : void 0;
137005
137175
  const loginParts = [summary.loginAccountEmail, loginOrgName, loginPlan].filter((part) => part !== void 0);
137006
137176
  if (summary.loginAccountEmail !== void 0 && summary.loginAccountEmail !== summary.providerAccountEmail) {
137007
- header.push(loginParts.join(" \xB7 "));
137177
+ segments.account = loginParts.join(" \xB7 ");
137008
137178
  } else if (summary.provider === void 0 && loginParts.length > 0) {
137009
- header.push(loginParts.join(" \xB7 "));
137179
+ segments.account = loginParts.join(" \xB7 ");
137010
137180
  }
137011
137181
  if (summary.identity !== void 0 && summary.provider !== void 0 && summary.identity !== summary.providerAccountEmail && summary.identity !== summary.loginAccountEmail) {
137012
- header.push(summary.identity);
137182
+ segments.identity = summary.identity;
137013
137183
  }
137014
137184
  const savings = headroomText(summary.headroom);
137015
137185
  if (savings !== void 0) {
137016
- header.push(savings);
137186
+ segments.headroom = savings;
137017
137187
  }
137188
+ const header = settings2.header.flatMap((name) => segments[name] ?? []);
137189
+ const bands = { warnAt: settings2.warnAt, criticalAt: settings2.criticalAt };
137018
137190
  const contextGroup = [];
137019
137191
  if (view.contextUsedPercentage !== void 0) {
137020
137192
  const fraction = Math.max(0, Math.min(1, view.contextUsedPercentage / FULL_PERCENT3));
137021
- const color = statusColor(fraction, true);
137193
+ const color = statusColor(fraction, true, bands);
137022
137194
  const percent2 = `${color}${view.contextUsedPercentage.toFixed(1)}%${RESET}`;
137023
137195
  const tokens = view.contextTotalTokens !== void 0 && view.contextWindowTokens !== void 0 ? `${formatTokens(view.contextTotalTokens)}/${formatTokens(view.contextWindowTokens)} ` : "";
137024
- const cache = cacheShareText(view);
137025
- contextGroup.push(barCell(`Ctx ${tokens}`, ` ${percent2}${cache}`, fraction));
137196
+ const cache = cacheShareText(view, bands);
137197
+ contextGroup.push(barCell(`Ctx ${tokens}`, ` ${percent2}${cache}`, fraction, bands));
137026
137198
  }
137027
137199
  const quotaGroup = [];
137028
137200
  const drawWindow = (label, fraction, resetsAt) => {
137029
137201
  const clamped = Math.max(0, Math.min(1, fraction));
137030
- const color = statusColor(clamped, true);
137031
- quotaGroup.push(barCell(`${label} `, ` ${color}${(fraction * FULL_PERCENT3).toFixed(1)}%${RESET} (${formatDuration(secondsUntil(resetsAt, nowMs))})`, clamped));
137202
+ const color = statusColor(clamped, true, bands);
137203
+ quotaGroup.push(barCell(`${label} `, ` ${color}${(fraction * FULL_PERCENT3).toFixed(1)}%${RESET} (${formatDuration(secondsUntil(resetsAt, nowMs))})`, clamped, bands));
137032
137204
  };
137033
137205
  if (summary.quotaFamilies.length > 0) {
137034
137206
  for (const window of summary.quotaFamilies) {
@@ -137036,10 +137208,10 @@ function renderStatuslineBlock(summary, view, terminalWidth, nowMs) {
137036
137208
  }
137037
137209
  } else {
137038
137210
  if (summary.fiveHourUsed !== void 0) {
137039
- drawWindow("5h", summary.fiveHourUsed / FULL_PERCENT3, summary.fiveHourResetAt);
137211
+ drawWindow(settings2.fiveHourLabel, summary.fiveHourUsed / FULL_PERCENT3, summary.fiveHourResetAt);
137040
137212
  }
137041
137213
  if (summary.sevenDayUsed !== void 0) {
137042
- drawWindow("Wk", summary.sevenDayUsed / FULL_PERCENT3, summary.sevenDayResetAt);
137214
+ drawWindow(settings2.sevenDayLabel, summary.sevenDayUsed / FULL_PERCENT3, summary.sevenDayResetAt);
137043
137215
  }
137044
137216
  if (summary.fiveHourUsed === void 0 && summary.sevenDayUsed === void 0) {
137045
137217
  for (const window of summary.providerQuotaWindows) {
@@ -137052,10 +137224,14 @@ function renderStatuslineBlock(summary, view, terminalWidth, nowMs) {
137052
137224
  if (header.length > 0) {
137053
137225
  lines.push(layoutHeader(header, width, SEPARATOR2));
137054
137226
  }
137055
- lines.push(...layoutBarGrid([contextGroup, quotaGroup], width, SEPARATOR2, BAR_WIDTH));
137227
+ const groups = { context: contextGroup, quota: quotaGroup };
137228
+ lines.push(...layoutBarGrid(settings2.bars.map((name) => groups[name]), width, SEPARATOR2, settings2.barWidth));
137056
137229
  return lines.join("\n");
137057
137230
  }
137058
137231
 
137232
+ // src/statusline/commands.ts
137233
+ init_settings2();
137234
+
137059
137235
  // src/statusline/width.ts
137060
137236
  var DEFAULT_COLUMNS = 80;
137061
137237
  function terminalWidthOf(payload, env) {
@@ -137188,7 +137364,9 @@ function registerStatuslineCommand(program2, deps) {
137188
137364
  }
137189
137365
  }
137190
137366
  const view = { ...payloadViewOf(payload), ...stackedUnderDefault ? { modelDisplayName: void 0 } : {} };
137191
- console.log(renderStatuslineBlock(summary, view, terminalWidthOf(payload, process.env), nowMs));
137367
+ const rawSettings = process.env[STATUSLINE_ENV];
137368
+ const settings2 = resolveStatusline(rawSettings === void 0 || rawSettings === "" ? void 0 : statuslineSettingsFromEnv(rawSettings, STATUSLINE_ENV));
137369
+ console.log(renderStatuslineBlock(summary, view, terminalWidthOf(payload, process.env), nowMs, settings2));
137192
137370
  }),
137193
137371
  ["$ agent-shim statusline # a client's statusline setting names this; it renders or delegates"]
137194
137372
  );