claude-use 11.64.1 → 11.65.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/dist/index.cjs CHANGED
@@ -229,18 +229,24 @@ var init_names = __esm({
229
229
  function drawsNothing(text) {
230
230
  return (0, import_string_width.default)(text.replace(BLANK_CELLS, "")) === 0;
231
231
  }
232
- var import_string_width, BLANK_CELLS;
232
+ var import_string_width, BLANK_CELLS, BLANK_CELLS_BUT_SPACE;
233
233
  var init_blank = __esm({
234
234
  "src/statusline/blank.ts"() {
235
235
  "use strict";
236
236
  import_string_width = __toESM(require("string-width"), 1);
237
237
  BLANK_CELLS = /[\p{Zs}\u2800\u3164\uFFA0]/gu;
238
+ BLANK_CELLS_BUT_SPACE = new RegExp(`[${BLANK_CELLS.source}--[ ]]`, "gv");
238
239
  }
239
240
  });
240
241
 
241
242
  // src/statusline/graphemes.ts
243
+ function* graphemeStream(text) {
244
+ for (const part of SEGMENTER.segment(text)) {
245
+ yield part.segment;
246
+ }
247
+ }
242
248
  function graphemesOf(text) {
243
- return Array.from(SEGMENTER.segment(text), (part) => part.segment);
249
+ return Array.from(graphemeStream(text));
244
250
  }
245
251
  var SEGMENTER;
246
252
  var init_graphemes = __esm({
@@ -318,7 +324,7 @@ function resolveStatusline(settings) {
318
324
  }
319
325
  return resolved;
320
326
  }
321
- var import_string_width2, import_zod2, STATUSLINE_HEADER_SEGMENTS, STATUSLINE_ROWS, STATUSLINE_SESSION_TIME_PARTS, PROMPT_CACHE_TTL_SECONDS, STATUSLINE_COLOUR_NAMES, DEFAULT_HEADER_SEGMENTS, DEFAULT_ROWS, STATUSLINE_PRESETS, GIT_WITH_DIRTY_MARKER, JJ_OFF, PRESET_BUNDLES, STATUSLINE_PATH_LEVELS, STATUSLINE_USAGE_VALUES, STATUSLINE_TIME_FORMATS, STATUSLINE_HOUR_CYCLES, MAX_PERCENT, MAX_STATUSLINE_PALETTE_INDEX, MAX_STATUSLINE_LABEL_COLUMNS, MIN_STATUSLINE_COLUMNS, MAX_STATUSLINE_COLUMNS, MAX_BAR_CELLS, MAX_ACCOUNT_USER_CHARS, MAX_DISPLAY_TEXT_CHARS, LABEL_RE2, OPTIONAL_LABEL_RE, mergeGroups, colourValue, barCharacter, STATUSLINE_CONTEXT_VALUES, STATUSLINE_CONTEXT_ESTIMATES, AUTOCOMPACT_RESERVE_FRACTION, AUTOCOMPACT_RESERVE_RAMP_START, AUTOCOMPACT_RESERVE_RAMP_END, labelWithinColumns, labelWithinColumnsMessage, labelShowsSomething, labelShowsSomethingMessage, labelIsPlainText, labelIsPlainTextMessage, windowLabel, overrideText, wholePercent, thresholdPercent, SHAPES, UNSET_NOTE, LANGUAGE_DESCRIPTION, LANGUAGE_TAGS, LANGUAGE_ALIASES, TOP_LEVEL, StatuslineSchema, ResolvedStatuslineSchema, STATUSLINE_DEFAULTS, STATUSLINE_ENV;
327
+ var import_string_width2, import_zod2, STATUSLINE_HEADER_SEGMENTS, STATUSLINE_ROWS, STATUSLINE_SESSION_TIME_PARTS, PROMPT_CACHE_TTL_SECONDS, ORANGE_PALETTE_INDEX, STATUSLINE_COLOUR_NAMES, DEFAULT_HEADER_SEGMENTS, DEFAULT_ROWS, STATUSLINE_PRESETS, GIT_WITH_DIRTY_MARKER, JJ_OFF, PRESET_BUNDLES, STATUSLINE_PATH_LEVELS, STATUSLINE_USAGE_VALUES, STATUSLINE_TIME_FORMATS, STATUSLINE_HOUR_CYCLES, MAX_PERCENT, MAX_STATUSLINE_PALETTE_INDEX, MAX_STATUSLINE_LABEL_COLUMNS, MIN_STATUSLINE_COLUMNS, MAX_STATUSLINE_COLUMNS, MAX_BAR_CELLS, MAX_ACCOUNT_USER_CHARS, MAX_DISPLAY_TEXT_CHARS, MAX_EXTRA_COMMAND_ARGUMENTS, MAX_EXTRA_COMMAND_ARGUMENT_CHARS, STATUSLINE_EXTRA_GROUP, commandArgument, LABEL_RE2, OPTIONAL_LABEL_RE, mergeGroups, colourValue, barCharacter, STATUSLINE_CONTEXT_VALUES, STATUSLINE_CONTEXT_ESTIMATES, AUTOCOMPACT_RESERVE_FRACTION, AUTOCOMPACT_RESERVE_RAMP_START, AUTOCOMPACT_RESERVE_RAMP_END, labelWithinColumns, labelWithinColumnsMessage, labelShowsSomething, labelShowsSomethingMessage, labelIsPlainText, labelIsPlainTextMessage, windowLabel, overrideText, wholePercent, thresholdPercent, SHAPES, UNSET_NOTE, LANGUAGE_DESCRIPTION, LANGUAGE_TAGS, LANGUAGE_ALIASES, TOP_LEVEL, StatuslineSchema, ResolvedStatuslineSchema, STATUSLINE_DEFAULTS, STATUSLINE_ENV;
322
328
  var init_statusline = __esm({
323
329
  "src/config/statusline.ts"() {
324
330
  "use strict";
@@ -327,10 +333,11 @@ var init_statusline = __esm({
327
333
  init_blank();
328
334
  init_graphemes();
329
335
  init_languages();
330
- STATUSLINE_HEADER_SEGMENTS = ["model", "provider", "route", "account", "identity", "headroom", "project", "vcs", "effort", "advisor", "version", "outputStyle", "cost", "dailyCost", "sessionName", "duration", "speed"];
336
+ STATUSLINE_HEADER_SEGMENTS = ["model", "provider", "route", "account", "identity", "headroom", "project", "vcs", "effort", "advisor", "version", "outputStyle", "cost", "dailyCost", "sessionName", "duration", "speed", "extra"];
331
337
  STATUSLINE_ROWS = ["context", "quota", "addedDirs", "gitFiles", "memory", "environment", "sessionTime", "promptCache", "compactions", "tokens", "tools", "skills", "mcp", "agents", "todos"];
332
338
  STATUSLINE_SESSION_TIME_PARTS = ["started", "lastReply"];
333
339
  PROMPT_CACHE_TTL_SECONDS = { fiveMinutes: 300, oneHour: 3600 };
340
+ ORANGE_PALETTE_INDEX = 208;
334
341
  STATUSLINE_COLOUR_NAMES = [
335
342
  "default",
336
343
  "dim",
@@ -359,14 +366,14 @@ var init_statusline = __esm({
359
366
  PRESET_BUNDLES = {
360
367
  standard: { layout: { header: DEFAULT_HEADER_SEGMENTS, rows: DEFAULT_ROWS }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: false }, context: { breakdown: "never" } },
361
368
  full: {
362
- layout: { header: ["model", "project", "vcs", "effort", "advisor", "version", "outputStyle", "cost", "sessionName", "duration"], rows: ["context", "quota", "addedDirs", "memory", "environment", "tools", "skills", "mcp", "agents", "todos", "promptCache", "compactions", "tokens"] },
369
+ layout: { header: ["model", "project", "vcs", "effort", "advisor", "version", "extra", "outputStyle", "cost", "sessionName", "duration"], rows: ["context", "quota", "addedDirs", "memory", "environment", "tools", "skills", "mcp", "agents", "todos", "promptCache", "compactions", "tokens"] },
363
370
  git: GIT_WITH_DIRTY_MARKER,
364
371
  jj: { enabled: true, showDirty: true, showConflicts: true },
365
372
  usage: { resetLabel: true },
366
373
  context: { breakdown: "critical" }
367
374
  },
368
- essential: { layout: { header: ["model", "project", "vcs", "duration"], rows: ["context", "addedDirs", "tools", "agents", "todos"] }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: true }, context: { breakdown: "never" } },
369
- minimal: { layout: { header: ["model", "project", "vcs"], rows: ["context", "addedDirs"] }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: true }, context: { breakdown: "never" } }
375
+ essential: { layout: { header: ["model", "project", "vcs", "extra", "duration"], rows: ["context", "addedDirs", "tools", "agents", "todos"] }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: true }, context: { breakdown: "never" } },
376
+ minimal: { layout: { header: ["model", "project", "vcs", "extra"], rows: ["context", "addedDirs"] }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: true }, context: { breakdown: "never" } }
370
377
  };
371
378
  STATUSLINE_PATH_LEVELS = ["1", "2", "3", "full"];
372
379
  STATUSLINE_USAGE_VALUES = ["used", "remaining"];
@@ -380,6 +387,10 @@ var init_statusline = __esm({
380
387
  MAX_BAR_CELLS = 40;
381
388
  MAX_ACCOUNT_USER_CHARS = 254;
382
389
  MAX_DISPLAY_TEXT_CHARS = 80;
390
+ MAX_EXTRA_COMMAND_ARGUMENTS = 16;
391
+ MAX_EXTRA_COMMAND_ARGUMENT_CHARS = 1024;
392
+ STATUSLINE_EXTRA_GROUP = "extra";
393
+ commandArgument = import_zod2.z.string().max(MAX_EXTRA_COMMAND_ARGUMENT_CHARS).refine((argument) => !argument.includes("\0"), "must not contain a NUL character");
383
394
  LABEL_RE2 = /^[^\p{C}\p{Z}]+(?: [^\p{C}\p{Z}]+)*$/u;
384
395
  OPTIONAL_LABEL_RE = /^(?:[^\p{C}\p{Z}]+(?: [^\p{C}\p{Z}]+)*)?$/u;
385
396
  mergeGroups = import_zod2.z.array(distinctList(import_zod2.z.enum(STATUSLINE_ROWS)).min(2)).refine((groups) => {
@@ -442,6 +453,15 @@ var init_statusline = __esm({
442
453
  estimate: import_zod2.z.enum(STATUSLINE_CONTEXT_ESTIMATES).describe(`What to draw when the client reports no percentage of the window in use, or reports 0 while tokens are already held (which is how a client says it has not worked it out yet): never (nothing is estimated: a percentage reported, 0 included, is drawn as it is, and none reported draws nothing), raw (the tokens held over the window), or buffered (that, plus the ${String(AUTOCOMPACT_RESERVE_FRACTION * MAX_PERCENT)}% of the window Claude Code reserves for compaction, rising from nothing at ${String(AUTOCOMPACT_RESERVE_RAMP_START * MAX_PERCENT)}% of the window held to the whole reserve at ${String(AUTOCOMPACT_RESERVE_RAMP_END * MAX_PERCENT)}%).`),
443
454
  breakdown: import_zod2.z.enum(["never", "critical"]).describe("Whether to add the input and cache tokens of the last request to the context row once its use reaches context.criticalAt.")
444
455
  },
456
+ customLine: {
457
+ text: overrideText.describe(`A line of your own text to draw, in colours.custom; empty draws nothing. It is refused when longer than ${String(MAX_DISPLAY_TEXT_CHARS)} characters or when it holds control, format or separator characters, and words are separated by single spaces. A character that passes but draws nothing, such as a braille blank, is removed, and the line is cut to the usable width.`),
458
+ position: import_zod2.z.enum(["first", "last"]).describe("Where the custom line sits: first draws it above the header, and last below the last row, as a line of its own in either layout mode.")
459
+ },
460
+ extra: {
461
+ command: import_zod2.z.array(commandArgument).max(MAX_EXTRA_COMMAND_ARGUMENTS).refine((command) => command[0]?.startsWith("-") !== true, "the program, the first item, must not start with a dash, which the system would read as an option: name it by an absolute path").describe(
462
+ `The program and arguments the extra segment runs, as a list and not a shell string (for example ["git", "describe", "--tags"]). The first line of its standard output that shows something, or the label of a JSON object it prints, is the segment; a line with nothing a person could see in it, such as spaces, zero-width characters or an escape sequence alone, is skipped. Escape sequences are removed from the whole output first, so an operating system command with no terminator removes everything up to the next escape character or the end of the output, line breaks included. A command that fails or prints nothing draws nothing. It is refused when it has more than ${String(MAX_EXTRA_COMMAND_ARGUMENTS)} arguments (the program included), when an argument is longer than ${String(MAX_EXTRA_COMMAND_ARGUMENT_CHARS)} characters or holds a NUL, and when the program starts with a dash. It runs on every refresh with your privileges and the statusline's whole environment. Its output is written to a file the system limits in size. That limit applies to every file it writes and every process it starts, so a program that writes a large cache is stopped and draws nothing, and so does one whose output reaches the limit. A process it starts is left running when it is killed for outrunning its time limit or when a newer update cancels the refresh. Because it runs a program with your privileges, only your own configuration may set it. That means the global config, a profile in your config-profiles directory, a directory rule or a command-line assignment. A project file such as .agent-shim.json is refused, though it may select one of your profiles by name, and a config write through the door's API (an MCP agent or the web client with the writes grant) is refused the extra settings for the same reason.`
463
+ )
464
+ },
445
465
  bands: {
446
466
  warnAt: wholePercent.describe("The whole percentage of use at which a bar turns yellow; it must be below criticalAt."),
447
467
  criticalAt: wholePercent.describe("The whole percentage of use at which a bar turns red.")
@@ -498,7 +518,8 @@ var init_statusline = __esm({
498
518
  git: colourValue.describe("The colour of the version control segment's wrapper text (`git:(` and `)`)."),
499
519
  gitBranch: colourValue.describe("The colour of the branch or bookmark and its ahead, behind and dirty markers."),
500
520
  activity: colourValue.describe("The colour of the names of the tools, skills and MCP servers the tools, skills and mcp rows draw. Their running markers take colours.warning and their finished markers colours.context."),
501
- agent: colourValue.describe("The colour of an agent's type in the agents row.")
521
+ agent: colourValue.describe("The colour of an agent's type in the agents row."),
522
+ custom: colourValue.describe("The colour of the custom line.")
502
523
  },
503
524
  project: {
504
525
  pathLevels: import_zod2.z.enum(STATUSLINE_PATH_LEVELS).describe("How many trailing directories of the working directory the project segment shows: 1, 2 or 3, or full for the whole absolute path."),
@@ -525,7 +546,7 @@ var init_statusline = __esm({
525
546
  LANGUAGE_ALIASES = { zh: "zh-Hans", "zh-TW": "zh-Hant" };
526
547
  TOP_LEVEL = {
527
548
  language: import_zod2.z.enum(STATUSLINE_LANGUAGES).describe(LANGUAGE_DESCRIPTION),
528
- preset: import_zod2.z.enum(STATUSLINE_PRESETS).describe("A named bundle of settings (layout.header, layout.rows, git.enabled, git.showDirty, git.showAheadBehind, git.showLineDiff, jj.enabled, jj.showDirty, jj.showConflicts, usage.resetLabel and context.breakdown) that this layer sets beneath the keys it names itself and above everything the shallower layers set: standard is the statusline's own; full, essential and minimal are claude-hud's presets. Each draws the model, project and version control segments and the context row; full adds the effort, advisor, version, output style, cost, session name and duration segments and the quota, added directories, memory, environment, tools, skills, mcp, agents, todos, prompt cache, compactions and session tokens rows, with the token breakdown at high use, essential adds the duration, the added directories row and the tools, agents and todos rows, and minimal adds the added directories row alone.")
549
+ preset: import_zod2.z.enum(STATUSLINE_PRESETS).describe("A named bundle of settings (layout.header, layout.rows, git.enabled, git.showDirty, git.showAheadBehind, git.showLineDiff, jj.enabled, jj.showDirty, jj.showConflicts, usage.resetLabel and context.breakdown) that this layer sets beneath the keys it names itself and above everything the shallower layers set: standard is the statusline's own; full, essential and minimal are claude-hud's presets. Each draws the model, project and version control segments, the extra segment once extra.command is set, and the context row; full adds the effort, advisor, version, output style, cost, session name and duration segments and the quota, added directories, memory, environment, tools, skills, mcp, agents, todos, prompt cache, compactions and session tokens rows, with the token breakdown at high use, essential adds the duration, the added directories row and the tools, agents and todos rows, and minimal adds the added directories row alone.")
529
550
  };
530
551
  StatuslineSchema = import_zod2.z.strictObject({
531
552
  language: import_zod2.z.enum(LANGUAGE_TAGS).transform(canonicalLanguage).describe(LANGUAGE_DESCRIPTION).optional(),
@@ -537,6 +558,8 @@ var init_statusline = __esm({
537
558
  effort: import_zod2.z.strictObject(SHAPES.effort).partial().optional(),
538
559
  advisor: import_zod2.z.strictObject(SHAPES.advisor).partial().optional(),
539
560
  context: import_zod2.z.strictObject({ ...SHAPES.context, warnAt: unsettable(SHAPES.context.warnAt), criticalAt: unsettable(SHAPES.context.criticalAt), autoCompactWindow: unsettable(SHAPES.context.autoCompactWindow) }).partial().optional(),
561
+ customLine: import_zod2.z.strictObject(SHAPES.customLine).partial().optional(),
562
+ extra: import_zod2.z.strictObject(SHAPES.extra).partial().optional(),
540
563
  bands: import_zod2.z.strictObject(SHAPES.bands).partial().optional(),
541
564
  labels: import_zod2.z.strictObject(SHAPES.labels).partial().optional(),
542
565
  bar: import_zod2.z.strictObject(SHAPES.bar).partial().optional(),
@@ -562,6 +585,8 @@ var init_statusline = __esm({
562
585
  effort: import_zod2.z.strictObject(SHAPES.effort),
563
586
  advisor: import_zod2.z.strictObject(SHAPES.advisor),
564
587
  context: import_zod2.z.strictObject(SHAPES.context),
588
+ customLine: import_zod2.z.strictObject(SHAPES.customLine),
589
+ extra: import_zod2.z.strictObject(SHAPES.extra),
565
590
  bands: import_zod2.z.strictObject(SHAPES.bands),
566
591
  labels: import_zod2.z.strictObject(SHAPES.labels),
567
592
  bar: import_zod2.z.strictObject(SHAPES.bar),
@@ -587,6 +612,8 @@ var init_statusline = __esm({
587
612
  effort: { format: "full" },
588
613
  advisor: { override: "" },
589
614
  context: { bar: "shown", value: "both", estimate: "never", breakdown: "never" },
615
+ customLine: { text: "", position: "last" },
616
+ extra: { command: [] },
590
617
  bands: { warnAt: 70, criticalAt: 90 },
591
618
  labels: { fiveHour: "", sevenDay: "" },
592
619
  bar: { width: 10, filled: "\u2588", empty: "\u2591" },
@@ -610,7 +637,8 @@ var init_statusline = __esm({
610
637
  git: "magenta",
611
638
  gitBranch: "cyan",
612
639
  activity: "cyan",
613
- agent: "magenta"
640
+ agent: "magenta",
641
+ custom: ORANGE_PALETTE_INDEX
614
642
  },
615
643
  project: { pathLevels: "1", addedDirsLayout: "inline" },
616
644
  git: { enabled: true, showDirty: true, showAheadBehind: false, showLineDiff: false, branchOverflow: "truncate", pushWarnAt: 0, pushCriticalAt: 0 },
@@ -1121,6 +1149,10 @@ var init_schema = __esm({
1121
1149
  configProfile: FileNameSchema.optional(),
1122
1150
  identity: IdentitySelectorSchema.optional(),
1123
1151
  when: WhenSchema.optional()
1152
+ }).superRefine((config, context) => {
1153
+ if (config.launch?.statusline?.[STATUSLINE_EXTRA_GROUP] !== void 0) {
1154
+ context.addIssue({ code: "custom", path: ["launch", "statusline", STATUSLINE_EXTRA_GROUP], message: "a project file cannot set the statusline's extra command: it runs a program, so only your own configuration (the global config, a profile, a directory rule) may set it" });
1155
+ }
1124
1156
  });
1125
1157
  HEADROOM_MODES = ["cache", "token"];
1126
1158
  HEADROOM_CCR_MODES = ["default", "lossless", "none"];
@@ -4704,7 +4736,7 @@ var init_package = __esm({
4704
4736
  "package.json"() {
4705
4737
  package_default = {
4706
4738
  name: "claude-use",
4707
- version: "11.64.1",
4739
+ version: "11.65.0",
4708
4740
  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.",
4709
4741
  license: "MIT",
4710
4742
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -7945,6 +7977,9 @@ function keyOf(path41, declared) {
7945
7977
  if (leaf instanceof import_zod16.z.ZodArray && leaf.element instanceof import_zod16.z.ZodArray && leaf.element.element instanceof import_zod16.z.ZodEnum) {
7946
7978
  return { ...common, kind: "groups", choices: Object.values(leaf.element.element.enum).map(String) };
7947
7979
  }
7980
+ if (leaf instanceof import_zod16.z.ZodArray && leaf.element instanceof import_zod16.z.ZodString) {
7981
+ return { ...common, kind: "textList", choices: [] };
7982
+ }
7948
7983
  throw new TypeError(`statusline setting ${path41.join(".")} has a kind the key listing does not describe`);
7949
7984
  }
7950
7985
  function walk(shape, prefix) {
@@ -18054,6 +18089,7 @@ init_providersStore();
18054
18089
  // src/frontdoor/configSchemas.ts
18055
18090
  var import_zod30 = require("zod");
18056
18091
  init_schema();
18092
+ init_statusline();
18057
18093
  var RemovalConfirmationSchema = import_zod30.z.boolean();
18058
18094
  var CredentialWriteSchema = (targets) => import_zod30.z.strictObject({
18059
18095
  sources: import_zod30.z.array(RemoteCredentialSourceSchema).min(1).optional(),
@@ -18106,6 +18142,13 @@ var ConfigIdentitySetInputSchema = import_zod30.z.strictObject({
18106
18142
  var ConfigIdentitySetOutputSchema = storedMutationOutput("identity", "updated", IdentityValueSchema);
18107
18143
  var ConfigIdentityRemoveInputSchema = import_zod30.z.strictObject({ name: import_zod30.z.string().min(1), confirm: RemovalConfirmationSchema });
18108
18144
  var ConfigIdentityRemoveOutputSchema = mutationOutput("identity", "removed");
18145
+ var ConfigStatuslineSetSchema = import_zod30.z.record(import_zod30.z.string(), import_zod30.z.unknown()).superRefine((settings, context) => {
18146
+ for (const key of Object.keys(settings)) {
18147
+ if (key === STATUSLINE_EXTRA_GROUP || key.startsWith(`${STATUSLINE_EXTRA_GROUP}.`)) {
18148
+ context.addIssue({ code: "custom", path: [key], message: `a config write through the API cannot set "${key}": the extra segment's command runs a program, so only your own configuration (the global config, a profile you edit yourself, a directory rule or a command-line assignment) may set it` });
18149
+ }
18150
+ }
18151
+ });
18109
18152
  var ConfigIdentityUseOutputSchema = mutationOutput("identity", "selected");
18110
18153
  var ConfigProfileAddInputSchema = import_zod30.z.strictObject({
18111
18154
  name: import_zod30.z.string().min(1),
@@ -18131,7 +18174,7 @@ var ConfigProfileSetInputSchema = import_zod30.z.strictObject({
18131
18174
  explain: import_zod30.z.boolean().optional(),
18132
18175
  proxyBypass: import_zod30.z.union([import_zod30.z.array(import_zod30.z.string().min(1)), import_zod30.z.literal(false)]).optional(),
18133
18176
  statusline: import_zod30.z.strictObject({
18134
- set: import_zod30.z.record(import_zod30.z.string(), import_zod30.z.unknown()).optional(),
18177
+ set: ConfigStatuslineSetSchema.optional(),
18135
18178
  unset: import_zod30.z.array(import_zod30.z.string()).optional()
18136
18179
  }).optional()
18137
18180
  }).optional()
package/dist/index.mjs CHANGED
@@ -208,17 +208,23 @@ import stringWidth from "string-width";
208
208
  function drawsNothing(text) {
209
209
  return stringWidth(text.replace(BLANK_CELLS, "")) === 0;
210
210
  }
211
- var BLANK_CELLS;
211
+ var BLANK_CELLS, BLANK_CELLS_BUT_SPACE;
212
212
  var init_blank = __esm({
213
213
  "src/statusline/blank.ts"() {
214
214
  "use strict";
215
215
  BLANK_CELLS = /[\p{Zs}\u2800\u3164\uFFA0]/gu;
216
+ BLANK_CELLS_BUT_SPACE = new RegExp(`[${BLANK_CELLS.source}--[ ]]`, "gv");
216
217
  }
217
218
  });
218
219
 
219
220
  // src/statusline/graphemes.ts
221
+ function* graphemeStream(text) {
222
+ for (const part of SEGMENTER.segment(text)) {
223
+ yield part.segment;
224
+ }
225
+ }
220
226
  function graphemesOf(text) {
221
- return Array.from(SEGMENTER.segment(text), (part) => part.segment);
227
+ return Array.from(graphemeStream(text));
222
228
  }
223
229
  var SEGMENTER;
224
230
  var init_graphemes = __esm({
@@ -298,17 +304,18 @@ function resolveStatusline(settings) {
298
304
  }
299
305
  return resolved;
300
306
  }
301
- var STATUSLINE_HEADER_SEGMENTS, STATUSLINE_ROWS, STATUSLINE_SESSION_TIME_PARTS, PROMPT_CACHE_TTL_SECONDS, STATUSLINE_COLOUR_NAMES, DEFAULT_HEADER_SEGMENTS, DEFAULT_ROWS, STATUSLINE_PRESETS, GIT_WITH_DIRTY_MARKER, JJ_OFF, PRESET_BUNDLES, STATUSLINE_PATH_LEVELS, STATUSLINE_USAGE_VALUES, STATUSLINE_TIME_FORMATS, STATUSLINE_HOUR_CYCLES, MAX_PERCENT, MAX_STATUSLINE_PALETTE_INDEX, MAX_STATUSLINE_LABEL_COLUMNS, MIN_STATUSLINE_COLUMNS, MAX_STATUSLINE_COLUMNS, MAX_BAR_CELLS, MAX_ACCOUNT_USER_CHARS, MAX_DISPLAY_TEXT_CHARS, LABEL_RE2, OPTIONAL_LABEL_RE, mergeGroups, colourValue, barCharacter, STATUSLINE_CONTEXT_VALUES, STATUSLINE_CONTEXT_ESTIMATES, AUTOCOMPACT_RESERVE_FRACTION, AUTOCOMPACT_RESERVE_RAMP_START, AUTOCOMPACT_RESERVE_RAMP_END, labelWithinColumns, labelWithinColumnsMessage, labelShowsSomething, labelShowsSomethingMessage, labelIsPlainText, labelIsPlainTextMessage, windowLabel, overrideText, wholePercent, thresholdPercent, SHAPES, UNSET_NOTE, LANGUAGE_DESCRIPTION, LANGUAGE_TAGS, LANGUAGE_ALIASES, TOP_LEVEL, StatuslineSchema, ResolvedStatuslineSchema, STATUSLINE_DEFAULTS, STATUSLINE_ENV;
307
+ var STATUSLINE_HEADER_SEGMENTS, STATUSLINE_ROWS, STATUSLINE_SESSION_TIME_PARTS, PROMPT_CACHE_TTL_SECONDS, ORANGE_PALETTE_INDEX, STATUSLINE_COLOUR_NAMES, DEFAULT_HEADER_SEGMENTS, DEFAULT_ROWS, STATUSLINE_PRESETS, GIT_WITH_DIRTY_MARKER, JJ_OFF, PRESET_BUNDLES, STATUSLINE_PATH_LEVELS, STATUSLINE_USAGE_VALUES, STATUSLINE_TIME_FORMATS, STATUSLINE_HOUR_CYCLES, MAX_PERCENT, MAX_STATUSLINE_PALETTE_INDEX, MAX_STATUSLINE_LABEL_COLUMNS, MIN_STATUSLINE_COLUMNS, MAX_STATUSLINE_COLUMNS, MAX_BAR_CELLS, MAX_ACCOUNT_USER_CHARS, MAX_DISPLAY_TEXT_CHARS, MAX_EXTRA_COMMAND_ARGUMENTS, MAX_EXTRA_COMMAND_ARGUMENT_CHARS, STATUSLINE_EXTRA_GROUP, commandArgument, LABEL_RE2, OPTIONAL_LABEL_RE, mergeGroups, colourValue, barCharacter, STATUSLINE_CONTEXT_VALUES, STATUSLINE_CONTEXT_ESTIMATES, AUTOCOMPACT_RESERVE_FRACTION, AUTOCOMPACT_RESERVE_RAMP_START, AUTOCOMPACT_RESERVE_RAMP_END, labelWithinColumns, labelWithinColumnsMessage, labelShowsSomething, labelShowsSomethingMessage, labelIsPlainText, labelIsPlainTextMessage, windowLabel, overrideText, wholePercent, thresholdPercent, SHAPES, UNSET_NOTE, LANGUAGE_DESCRIPTION, LANGUAGE_TAGS, LANGUAGE_ALIASES, TOP_LEVEL, StatuslineSchema, ResolvedStatuslineSchema, STATUSLINE_DEFAULTS, STATUSLINE_ENV;
302
308
  var init_statusline = __esm({
303
309
  "src/config/statusline.ts"() {
304
310
  "use strict";
305
311
  init_blank();
306
312
  init_graphemes();
307
313
  init_languages();
308
- STATUSLINE_HEADER_SEGMENTS = ["model", "provider", "route", "account", "identity", "headroom", "project", "vcs", "effort", "advisor", "version", "outputStyle", "cost", "dailyCost", "sessionName", "duration", "speed"];
314
+ STATUSLINE_HEADER_SEGMENTS = ["model", "provider", "route", "account", "identity", "headroom", "project", "vcs", "effort", "advisor", "version", "outputStyle", "cost", "dailyCost", "sessionName", "duration", "speed", "extra"];
309
315
  STATUSLINE_ROWS = ["context", "quota", "addedDirs", "gitFiles", "memory", "environment", "sessionTime", "promptCache", "compactions", "tokens", "tools", "skills", "mcp", "agents", "todos"];
310
316
  STATUSLINE_SESSION_TIME_PARTS = ["started", "lastReply"];
311
317
  PROMPT_CACHE_TTL_SECONDS = { fiveMinutes: 300, oneHour: 3600 };
318
+ ORANGE_PALETTE_INDEX = 208;
312
319
  STATUSLINE_COLOUR_NAMES = [
313
320
  "default",
314
321
  "dim",
@@ -337,14 +344,14 @@ var init_statusline = __esm({
337
344
  PRESET_BUNDLES = {
338
345
  standard: { layout: { header: DEFAULT_HEADER_SEGMENTS, rows: DEFAULT_ROWS }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: false }, context: { breakdown: "never" } },
339
346
  full: {
340
- layout: { header: ["model", "project", "vcs", "effort", "advisor", "version", "outputStyle", "cost", "sessionName", "duration"], rows: ["context", "quota", "addedDirs", "memory", "environment", "tools", "skills", "mcp", "agents", "todos", "promptCache", "compactions", "tokens"] },
347
+ layout: { header: ["model", "project", "vcs", "effort", "advisor", "version", "extra", "outputStyle", "cost", "sessionName", "duration"], rows: ["context", "quota", "addedDirs", "memory", "environment", "tools", "skills", "mcp", "agents", "todos", "promptCache", "compactions", "tokens"] },
341
348
  git: GIT_WITH_DIRTY_MARKER,
342
349
  jj: { enabled: true, showDirty: true, showConflicts: true },
343
350
  usage: { resetLabel: true },
344
351
  context: { breakdown: "critical" }
345
352
  },
346
- essential: { layout: { header: ["model", "project", "vcs", "duration"], rows: ["context", "addedDirs", "tools", "agents", "todos"] }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: true }, context: { breakdown: "never" } },
347
- minimal: { layout: { header: ["model", "project", "vcs"], rows: ["context", "addedDirs"] }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: true }, context: { breakdown: "never" } }
353
+ essential: { layout: { header: ["model", "project", "vcs", "extra", "duration"], rows: ["context", "addedDirs", "tools", "agents", "todos"] }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: true }, context: { breakdown: "never" } },
354
+ minimal: { layout: { header: ["model", "project", "vcs", "extra"], rows: ["context", "addedDirs"] }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: true }, context: { breakdown: "never" } }
348
355
  };
349
356
  STATUSLINE_PATH_LEVELS = ["1", "2", "3", "full"];
350
357
  STATUSLINE_USAGE_VALUES = ["used", "remaining"];
@@ -358,6 +365,10 @@ var init_statusline = __esm({
358
365
  MAX_BAR_CELLS = 40;
359
366
  MAX_ACCOUNT_USER_CHARS = 254;
360
367
  MAX_DISPLAY_TEXT_CHARS = 80;
368
+ MAX_EXTRA_COMMAND_ARGUMENTS = 16;
369
+ MAX_EXTRA_COMMAND_ARGUMENT_CHARS = 1024;
370
+ STATUSLINE_EXTRA_GROUP = "extra";
371
+ commandArgument = z2.string().max(MAX_EXTRA_COMMAND_ARGUMENT_CHARS).refine((argument) => !argument.includes("\0"), "must not contain a NUL character");
361
372
  LABEL_RE2 = /^[^\p{C}\p{Z}]+(?: [^\p{C}\p{Z}]+)*$/u;
362
373
  OPTIONAL_LABEL_RE = /^(?:[^\p{C}\p{Z}]+(?: [^\p{C}\p{Z}]+)*)?$/u;
363
374
  mergeGroups = z2.array(distinctList(z2.enum(STATUSLINE_ROWS)).min(2)).refine((groups) => {
@@ -420,6 +431,15 @@ var init_statusline = __esm({
420
431
  estimate: z2.enum(STATUSLINE_CONTEXT_ESTIMATES).describe(`What to draw when the client reports no percentage of the window in use, or reports 0 while tokens are already held (which is how a client says it has not worked it out yet): never (nothing is estimated: a percentage reported, 0 included, is drawn as it is, and none reported draws nothing), raw (the tokens held over the window), or buffered (that, plus the ${String(AUTOCOMPACT_RESERVE_FRACTION * MAX_PERCENT)}% of the window Claude Code reserves for compaction, rising from nothing at ${String(AUTOCOMPACT_RESERVE_RAMP_START * MAX_PERCENT)}% of the window held to the whole reserve at ${String(AUTOCOMPACT_RESERVE_RAMP_END * MAX_PERCENT)}%).`),
421
432
  breakdown: z2.enum(["never", "critical"]).describe("Whether to add the input and cache tokens of the last request to the context row once its use reaches context.criticalAt.")
422
433
  },
434
+ customLine: {
435
+ text: overrideText.describe(`A line of your own text to draw, in colours.custom; empty draws nothing. It is refused when longer than ${String(MAX_DISPLAY_TEXT_CHARS)} characters or when it holds control, format or separator characters, and words are separated by single spaces. A character that passes but draws nothing, such as a braille blank, is removed, and the line is cut to the usable width.`),
436
+ position: z2.enum(["first", "last"]).describe("Where the custom line sits: first draws it above the header, and last below the last row, as a line of its own in either layout mode.")
437
+ },
438
+ extra: {
439
+ command: z2.array(commandArgument).max(MAX_EXTRA_COMMAND_ARGUMENTS).refine((command) => command[0]?.startsWith("-") !== true, "the program, the first item, must not start with a dash, which the system would read as an option: name it by an absolute path").describe(
440
+ `The program and arguments the extra segment runs, as a list and not a shell string (for example ["git", "describe", "--tags"]). The first line of its standard output that shows something, or the label of a JSON object it prints, is the segment; a line with nothing a person could see in it, such as spaces, zero-width characters or an escape sequence alone, is skipped. Escape sequences are removed from the whole output first, so an operating system command with no terminator removes everything up to the next escape character or the end of the output, line breaks included. A command that fails or prints nothing draws nothing. It is refused when it has more than ${String(MAX_EXTRA_COMMAND_ARGUMENTS)} arguments (the program included), when an argument is longer than ${String(MAX_EXTRA_COMMAND_ARGUMENT_CHARS)} characters or holds a NUL, and when the program starts with a dash. It runs on every refresh with your privileges and the statusline's whole environment. Its output is written to a file the system limits in size. That limit applies to every file it writes and every process it starts, so a program that writes a large cache is stopped and draws nothing, and so does one whose output reaches the limit. A process it starts is left running when it is killed for outrunning its time limit or when a newer update cancels the refresh. Because it runs a program with your privileges, only your own configuration may set it. That means the global config, a profile in your config-profiles directory, a directory rule or a command-line assignment. A project file such as .agent-shim.json is refused, though it may select one of your profiles by name, and a config write through the door's API (an MCP agent or the web client with the writes grant) is refused the extra settings for the same reason.`
441
+ )
442
+ },
423
443
  bands: {
424
444
  warnAt: wholePercent.describe("The whole percentage of use at which a bar turns yellow; it must be below criticalAt."),
425
445
  criticalAt: wholePercent.describe("The whole percentage of use at which a bar turns red.")
@@ -476,7 +496,8 @@ var init_statusline = __esm({
476
496
  git: colourValue.describe("The colour of the version control segment's wrapper text (`git:(` and `)`)."),
477
497
  gitBranch: colourValue.describe("The colour of the branch or bookmark and its ahead, behind and dirty markers."),
478
498
  activity: colourValue.describe("The colour of the names of the tools, skills and MCP servers the tools, skills and mcp rows draw. Their running markers take colours.warning and their finished markers colours.context."),
479
- agent: colourValue.describe("The colour of an agent's type in the agents row.")
499
+ agent: colourValue.describe("The colour of an agent's type in the agents row."),
500
+ custom: colourValue.describe("The colour of the custom line.")
480
501
  },
481
502
  project: {
482
503
  pathLevels: z2.enum(STATUSLINE_PATH_LEVELS).describe("How many trailing directories of the working directory the project segment shows: 1, 2 or 3, or full for the whole absolute path."),
@@ -503,7 +524,7 @@ var init_statusline = __esm({
503
524
  LANGUAGE_ALIASES = { zh: "zh-Hans", "zh-TW": "zh-Hant" };
504
525
  TOP_LEVEL = {
505
526
  language: z2.enum(STATUSLINE_LANGUAGES).describe(LANGUAGE_DESCRIPTION),
506
- preset: z2.enum(STATUSLINE_PRESETS).describe("A named bundle of settings (layout.header, layout.rows, git.enabled, git.showDirty, git.showAheadBehind, git.showLineDiff, jj.enabled, jj.showDirty, jj.showConflicts, usage.resetLabel and context.breakdown) that this layer sets beneath the keys it names itself and above everything the shallower layers set: standard is the statusline's own; full, essential and minimal are claude-hud's presets. Each draws the model, project and version control segments and the context row; full adds the effort, advisor, version, output style, cost, session name and duration segments and the quota, added directories, memory, environment, tools, skills, mcp, agents, todos, prompt cache, compactions and session tokens rows, with the token breakdown at high use, essential adds the duration, the added directories row and the tools, agents and todos rows, and minimal adds the added directories row alone.")
527
+ preset: z2.enum(STATUSLINE_PRESETS).describe("A named bundle of settings (layout.header, layout.rows, git.enabled, git.showDirty, git.showAheadBehind, git.showLineDiff, jj.enabled, jj.showDirty, jj.showConflicts, usage.resetLabel and context.breakdown) that this layer sets beneath the keys it names itself and above everything the shallower layers set: standard is the statusline's own; full, essential and minimal are claude-hud's presets. Each draws the model, project and version control segments, the extra segment once extra.command is set, and the context row; full adds the effort, advisor, version, output style, cost, session name and duration segments and the quota, added directories, memory, environment, tools, skills, mcp, agents, todos, prompt cache, compactions and session tokens rows, with the token breakdown at high use, essential adds the duration, the added directories row and the tools, agents and todos rows, and minimal adds the added directories row alone.")
507
528
  };
508
529
  StatuslineSchema = z2.strictObject({
509
530
  language: z2.enum(LANGUAGE_TAGS).transform(canonicalLanguage).describe(LANGUAGE_DESCRIPTION).optional(),
@@ -515,6 +536,8 @@ var init_statusline = __esm({
515
536
  effort: z2.strictObject(SHAPES.effort).partial().optional(),
516
537
  advisor: z2.strictObject(SHAPES.advisor).partial().optional(),
517
538
  context: z2.strictObject({ ...SHAPES.context, warnAt: unsettable(SHAPES.context.warnAt), criticalAt: unsettable(SHAPES.context.criticalAt), autoCompactWindow: unsettable(SHAPES.context.autoCompactWindow) }).partial().optional(),
539
+ customLine: z2.strictObject(SHAPES.customLine).partial().optional(),
540
+ extra: z2.strictObject(SHAPES.extra).partial().optional(),
518
541
  bands: z2.strictObject(SHAPES.bands).partial().optional(),
519
542
  labels: z2.strictObject(SHAPES.labels).partial().optional(),
520
543
  bar: z2.strictObject(SHAPES.bar).partial().optional(),
@@ -540,6 +563,8 @@ var init_statusline = __esm({
540
563
  effort: z2.strictObject(SHAPES.effort),
541
564
  advisor: z2.strictObject(SHAPES.advisor),
542
565
  context: z2.strictObject(SHAPES.context),
566
+ customLine: z2.strictObject(SHAPES.customLine),
567
+ extra: z2.strictObject(SHAPES.extra),
543
568
  bands: z2.strictObject(SHAPES.bands),
544
569
  labels: z2.strictObject(SHAPES.labels),
545
570
  bar: z2.strictObject(SHAPES.bar),
@@ -565,6 +590,8 @@ var init_statusline = __esm({
565
590
  effort: { format: "full" },
566
591
  advisor: { override: "" },
567
592
  context: { bar: "shown", value: "both", estimate: "never", breakdown: "never" },
593
+ customLine: { text: "", position: "last" },
594
+ extra: { command: [] },
568
595
  bands: { warnAt: 70, criticalAt: 90 },
569
596
  labels: { fiveHour: "", sevenDay: "" },
570
597
  bar: { width: 10, filled: "\u2588", empty: "\u2591" },
@@ -588,7 +615,8 @@ var init_statusline = __esm({
588
615
  git: "magenta",
589
616
  gitBranch: "cyan",
590
617
  activity: "cyan",
591
- agent: "magenta"
618
+ agent: "magenta",
619
+ custom: ORANGE_PALETTE_INDEX
592
620
  },
593
621
  project: { pathLevels: "1", addedDirsLayout: "inline" },
594
622
  git: { enabled: true, showDirty: true, showAheadBehind: false, showLineDiff: false, branchOverflow: "truncate", pushWarnAt: 0, pushCriticalAt: 0 },
@@ -1099,6 +1127,10 @@ var init_schema = __esm({
1099
1127
  configProfile: FileNameSchema.optional(),
1100
1128
  identity: IdentitySelectorSchema.optional(),
1101
1129
  when: WhenSchema.optional()
1130
+ }).superRefine((config, context) => {
1131
+ if (config.launch?.statusline?.[STATUSLINE_EXTRA_GROUP] !== void 0) {
1132
+ context.addIssue({ code: "custom", path: ["launch", "statusline", STATUSLINE_EXTRA_GROUP], message: "a project file cannot set the statusline's extra command: it runs a program, so only your own configuration (the global config, a profile, a directory rule) may set it" });
1133
+ }
1102
1134
  });
1103
1135
  HEADROOM_MODES = ["cache", "token"];
1104
1136
  HEADROOM_CCR_MODES = ["default", "lossless", "none"];
@@ -4680,7 +4712,7 @@ var init_package = __esm({
4680
4712
  "package.json"() {
4681
4713
  package_default = {
4682
4714
  name: "claude-use",
4683
- version: "11.64.1",
4715
+ version: "11.65.0",
4684
4716
  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.",
4685
4717
  license: "MIT",
4686
4718
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -4786,7 +4818,6 @@ var init_package = __esm({
4786
4818
 
4787
4819
  // src/blockingSpawn.ts
4788
4820
  import {
4789
- execFileSync,
4790
4821
  spawnSync
4791
4822
  } from "node:child_process";
4792
4823
  function spawnSyncBlocking(operation, command, args, options) {
@@ -7922,6 +7953,9 @@ function keyOf(path41, declared) {
7922
7953
  if (leaf instanceof z16.ZodArray && leaf.element instanceof z16.ZodArray && leaf.element.element instanceof z16.ZodEnum) {
7923
7954
  return { ...common, kind: "groups", choices: Object.values(leaf.element.element.enum).map(String) };
7924
7955
  }
7956
+ if (leaf instanceof z16.ZodArray && leaf.element instanceof z16.ZodString) {
7957
+ return { ...common, kind: "textList", choices: [] };
7958
+ }
7925
7959
  throw new TypeError(`statusline setting ${path41.join(".")} has a kind the key listing does not describe`);
7926
7960
  }
7927
7961
  function walk(shape, prefix) {
@@ -17640,6 +17674,7 @@ init_providersStore();
17640
17674
 
17641
17675
  // src/frontdoor/configSchemas.ts
17642
17676
  init_schema();
17677
+ init_statusline();
17643
17678
  import { z as z30 } from "zod";
17644
17679
  var RemovalConfirmationSchema = z30.boolean();
17645
17680
  var CredentialWriteSchema = (targets) => z30.strictObject({
@@ -17693,6 +17728,13 @@ var ConfigIdentitySetInputSchema = z30.strictObject({
17693
17728
  var ConfigIdentitySetOutputSchema = storedMutationOutput("identity", "updated", IdentityValueSchema);
17694
17729
  var ConfigIdentityRemoveInputSchema = z30.strictObject({ name: z30.string().min(1), confirm: RemovalConfirmationSchema });
17695
17730
  var ConfigIdentityRemoveOutputSchema = mutationOutput("identity", "removed");
17731
+ var ConfigStatuslineSetSchema = z30.record(z30.string(), z30.unknown()).superRefine((settings, context) => {
17732
+ for (const key of Object.keys(settings)) {
17733
+ if (key === STATUSLINE_EXTRA_GROUP || key.startsWith(`${STATUSLINE_EXTRA_GROUP}.`)) {
17734
+ context.addIssue({ code: "custom", path: [key], message: `a config write through the API cannot set "${key}": the extra segment's command runs a program, so only your own configuration (the global config, a profile you edit yourself, a directory rule or a command-line assignment) may set it` });
17735
+ }
17736
+ }
17737
+ });
17696
17738
  var ConfigIdentityUseOutputSchema = mutationOutput("identity", "selected");
17697
17739
  var ConfigProfileAddInputSchema = z30.strictObject({
17698
17740
  name: z30.string().min(1),
@@ -17718,7 +17760,7 @@ var ConfigProfileSetInputSchema = z30.strictObject({
17718
17760
  explain: z30.boolean().optional(),
17719
17761
  proxyBypass: z30.union([z30.array(z30.string().min(1)), z30.literal(false)]).optional(),
17720
17762
  statusline: z30.strictObject({
17721
- set: z30.record(z30.string(), z30.unknown()).optional(),
17763
+ set: ConfigStatuslineSetSchema.optional(),
17722
17764
  unset: z30.array(z30.string()).optional()
17723
17765
  }).optional()
17724
17766
  }).optional()