copilot-statusline 0.1.12 → 0.1.14

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
@@ -24,6 +24,8 @@
24
24
 
25
25
  > **Based on [ccstatusline](https://github.com/sirmalloc/ccstatusline)** by [@sirmalloc](https://github.com/sirmalloc) — the excellent customizable statusline for Claude Code CLI. This project adapts its architecture, TUI, and rendering engine for the GitHub Copilot CLI payload format. Huge thanks to Matthew Breedlove for the original work.
26
26
 
27
+ > **Requirements:** GitHub Copilot CLI **≥ 1.0.35** for live context widgets (`Context %`, `Context Bar`, `Context Length`, `Remaining Tokens`). Older versions miss the underlying `current_context_tokens` / `displayed_context_limit` fields and these widgets render blank.
28
+
27
29
  ---
28
30
 
29
31
  ## 📚 Table of Contents
@@ -274,7 +276,7 @@ Copilot CLI spawns your status line command on every state change, passing sessi
274
276
  |--------|------|-------------|
275
277
  | Context Length | `context-length` | Context window size |
276
278
  | Context % | `context-percentage` | Percentage of context window used or remaining |
277
- | Context % (Usable) | `context-percentage-usable` | Percentage of usable context (80% of max) |
279
+ | Context % | `context-percentage` | Live context % from Copilot (`current_context_used_percentage`) |
278
280
  | Context Bar | `context-bar` | Visual progress bar for context usage |
279
281
  | Remaining Tokens | `remaining-tokens` | Absolute remaining context tokens |
280
282
 
@@ -52913,7 +52913,7 @@ import { execSync as execSync3 } from "child_process";
52913
52913
  import * as fs5 from "fs";
52914
52914
  import * as path4 from "path";
52915
52915
  var __dirname = "/Users/ts/workspace/active/statusline/copilot_statusline/src/utils";
52916
- var PACKAGE_VERSION = "0.1.12";
52916
+ var PACKAGE_VERSION = "0.1.14";
52917
52917
  function getPackageVersion() {
52918
52918
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
52919
52919
  return PACKAGE_VERSION;
@@ -54074,15 +54074,14 @@ function getContextWindowMetrics(data) {
54074
54074
  const contextWindow = data?.context_window;
54075
54075
  const empty = {
54076
54076
  windowSize: null,
54077
- usedTokens: null,
54078
- contextLengthTokens: null,
54079
- usedPercentage: null,
54080
- remainingPercentage: null,
54077
+ displayedContextLimit: null,
54078
+ currentContextTokens: null,
54079
+ currentContextUsedPercentage: null,
54081
54080
  totalInputTokens: null,
54082
54081
  totalOutputTokens: null,
54083
54082
  cachedTokens: null,
54084
54083
  totalTokens: null,
54085
- remainingTokens: null,
54084
+ reasoningTokens: null,
54086
54085
  lastCallInputTokens: null,
54087
54086
  lastCallOutputTokens: null,
54088
54087
  cacheReadTokens: null,
@@ -54093,45 +54092,30 @@ function getContextWindowMetrics(data) {
54093
54092
  }
54094
54093
  const rawWindowSize = toFiniteNonNegativeNumber(contextWindow.context_window_size);
54095
54094
  const windowSize = rawWindowSize !== null && rawWindowSize > 0 ? rawWindowSize : null;
54095
+ const rawDisplayedLimit = toFiniteNonNegativeNumber(contextWindow.displayed_context_limit);
54096
+ const displayedContextLimit = rawDisplayedLimit !== null && rawDisplayedLimit > 0 ? rawDisplayedLimit : null;
54097
+ const currentContextTokens = toFiniteNonNegativeNumber(contextWindow.current_context_tokens);
54098
+ const rawCurrentPct = toFiniteNonNegativeNumber(contextWindow.current_context_used_percentage);
54099
+ const currentContextUsedPercentage = rawCurrentPct !== null ? clampPercentage(rawCurrentPct) : null;
54100
+ const reasoningTokens = toFiniteNonNegativeNumber(contextWindow.total_reasoning_tokens);
54096
54101
  const totalInputTokens = toFiniteNonNegativeNumber(contextWindow.total_input_tokens);
54097
54102
  const totalOutputTokens = toFiniteNonNegativeNumber(contextWindow.total_output_tokens);
54098
54103
  const cacheReadTokens = toFiniteNonNegativeNumber(contextWindow.total_cache_read_tokens);
54099
54104
  const cacheWriteTokens = toFiniteNonNegativeNumber(contextWindow.total_cache_write_tokens);
54100
- const remainingTokens = toFiniteNonNegativeNumber(contextWindow.remaining_tokens);
54101
54105
  const lastCallInputTokens = toFiniteNonNegativeNumber(contextWindow.last_call_input_tokens);
54102
54106
  const lastCallOutputTokens = toFiniteNonNegativeNumber(contextWindow.last_call_output_tokens);
54103
54107
  const cachedTokens = cacheReadTokens !== null || cacheWriteTokens !== null ? (cacheReadTokens ?? 0) + (cacheWriteTokens ?? 0) : null;
54104
- let currentUsageTotalTokens = null;
54105
- let contextLengthTokens = null;
54106
- if (contextWindow.current_usage && typeof contextWindow.current_usage === "object") {
54107
- const usage = contextWindow.current_usage;
54108
- const inputTokens = toFiniteNonNegativeNumber(usage.input_tokens) ?? 0;
54109
- const outputTokens = toFiniteNonNegativeNumber(usage.output_tokens) ?? 0;
54110
- const cacheCreationTokens = toFiniteNonNegativeNumber(usage.cache_creation_input_tokens) ?? 0;
54111
- const cacheReadInputTokens = toFiniteNonNegativeNumber(usage.cache_read_input_tokens) ?? 0;
54112
- currentUsageTotalTokens = inputTokens + outputTokens + cacheCreationTokens + cacheReadInputTokens;
54113
- contextLengthTokens = inputTokens + cacheCreationTokens + cacheReadInputTokens;
54114
- }
54115
- const rawUsedPercentage = toFiniteNonNegativeNumber(contextWindow.used_percentage);
54116
- const rawRemainingPercentage = toFiniteNonNegativeNumber(contextWindow.remaining_percentage);
54117
- const usedTokensFromRemaining = windowSize !== null && remainingTokens !== null ? windowSize - remainingTokens : null;
54118
- const usedTokensFromPercentage = rawUsedPercentage !== null && windowSize !== null ? rawUsedPercentage / 100 * windowSize : null;
54119
- const usedTokens = usedTokensFromRemaining ?? usedTokensFromPercentage ?? currentUsageTotalTokens;
54120
- const usedPercentage = rawUsedPercentage !== null ? clampPercentage(rawUsedPercentage) : usedTokens !== null && windowSize !== null && windowSize > 0 ? clampPercentage(usedTokens / windowSize * 100) : null;
54121
- const remainingPercentage = rawRemainingPercentage !== null ? clampPercentage(rawRemainingPercentage) : usedPercentage !== null ? 100 - usedPercentage : null;
54122
- const totalTokens = currentUsageTotalTokens ?? toFiniteNonNegativeNumber(contextWindow.total_tokens) ?? (totalInputTokens !== null && totalOutputTokens !== null ? totalInputTokens + totalOutputTokens : null);
54123
- const contextLengthFromAuthoritative = usedTokensFromRemaining ?? usedTokensFromPercentage;
54108
+ const totalTokens = toFiniteNonNegativeNumber(contextWindow.total_tokens) ?? (totalInputTokens !== null && totalOutputTokens !== null ? totalInputTokens + totalOutputTokens : null);
54124
54109
  return {
54125
54110
  windowSize,
54126
- usedTokens,
54127
- contextLengthTokens: contextLengthFromAuthoritative ?? contextLengthTokens ?? usedTokens,
54128
- usedPercentage,
54129
- remainingPercentage,
54111
+ displayedContextLimit,
54112
+ currentContextTokens,
54113
+ currentContextUsedPercentage,
54130
54114
  totalInputTokens,
54131
54115
  totalOutputTokens,
54132
54116
  cachedTokens,
54133
54117
  totalTokens,
54134
- remainingTokens,
54118
+ reasoningTokens,
54135
54119
  lastCallInputTokens,
54136
54120
  lastCallOutputTokens,
54137
54121
  cacheReadTokens,
@@ -54255,6 +54239,40 @@ class TokensCachedWidget {
54255
54239
  return true;
54256
54240
  }
54257
54241
  }
54242
+ // src/widgets/TokensReasoning.ts
54243
+ class TokensReasoningWidget {
54244
+ getDefaultColor() {
54245
+ return "magenta";
54246
+ }
54247
+ getDescription() {
54248
+ return "Shows total reasoning (thinking) tokens consumed this session";
54249
+ }
54250
+ getDisplayName() {
54251
+ return "Tokens Reasoning";
54252
+ }
54253
+ getCategory() {
54254
+ return "Tokens";
54255
+ }
54256
+ getEditorDisplay(_item) {
54257
+ return { displayText: this.getDisplayName() };
54258
+ }
54259
+ render(item, context, _settings) {
54260
+ if (context.isPreview) {
54261
+ return formatRawOrLabeledValue(item, "Reasoning: ", "1.2k");
54262
+ }
54263
+ const metrics = getContextWindowMetrics(context.data);
54264
+ if (metrics.reasoningTokens !== null) {
54265
+ return formatRawOrLabeledValue(item, "Reasoning: ", formatTokens(metrics.reasoningTokens));
54266
+ }
54267
+ return null;
54268
+ }
54269
+ supportsRawValue() {
54270
+ return true;
54271
+ }
54272
+ supportsColors(_item) {
54273
+ return true;
54274
+ }
54275
+ }
54258
54276
  // src/widgets/TokensTotal.ts
54259
54277
  class TokensTotalWidget {
54260
54278
  getDefaultColor() {
@@ -54295,7 +54313,7 @@ class ContextLengthWidget {
54295
54313
  return "brightBlack";
54296
54314
  }
54297
54315
  getDescription() {
54298
- return "Shows context window size";
54316
+ return "Shows current context length in tokens (live)";
54299
54317
  }
54300
54318
  getDisplayName() {
54301
54319
  return "Context Length";
@@ -54308,11 +54326,11 @@ class ContextLengthWidget {
54308
54326
  }
54309
54327
  render(item, context, settings) {
54310
54328
  if (context.isPreview) {
54311
- return formatRawOrLabeledValue(item, "Ctx: ", "200k");
54329
+ return formatRawOrLabeledValue(item, "Ctx: ", "33k");
54312
54330
  }
54313
54331
  const metrics = getContextWindowMetrics(context.data);
54314
- if (metrics.windowSize !== null) {
54315
- return formatRawOrLabeledValue(item, "Ctx: ", formatTokens(metrics.windowSize));
54332
+ if (metrics.currentContextTokens !== null) {
54333
+ return formatRawOrLabeledValue(item, "Ctx: ", formatTokens(metrics.currentContextTokens));
54316
54334
  }
54317
54335
  return null;
54318
54336
  }
@@ -54323,6 +54341,41 @@ class ContextLengthWidget {
54323
54341
  return true;
54324
54342
  }
54325
54343
  }
54344
+ // src/widgets/ContextWindow.ts
54345
+ class ContextWindowWidget {
54346
+ getDefaultColor() {
54347
+ return "brightBlack";
54348
+ }
54349
+ getDescription() {
54350
+ return "Shows the model's context window size (max tokens)";
54351
+ }
54352
+ getDisplayName() {
54353
+ return "Context Window";
54354
+ }
54355
+ getCategory() {
54356
+ return "Context";
54357
+ }
54358
+ getEditorDisplay(_item) {
54359
+ return { displayText: this.getDisplayName() };
54360
+ }
54361
+ render(item, context, _settings) {
54362
+ if (context.isPreview) {
54363
+ return formatRawOrLabeledValue(item, "Window: ", "1000k");
54364
+ }
54365
+ const metrics = getContextWindowMetrics(context.data);
54366
+ const size2 = metrics.displayedContextLimit ?? metrics.windowSize;
54367
+ if (size2 !== null) {
54368
+ return formatRawOrLabeledValue(item, "Window: ", formatTokens(size2));
54369
+ }
54370
+ return null;
54371
+ }
54372
+ supportsRawValue() {
54373
+ return true;
54374
+ }
54375
+ supportsColors(_item) {
54376
+ return true;
54377
+ }
54378
+ }
54326
54379
  // src/widgets/shared/editor-display.ts
54327
54380
  function makeModifierText(modifiers) {
54328
54381
  return modifiers.length > 0 ? `(${modifiers.join(", ")})` : undefined;
@@ -54388,8 +54441,9 @@ class ContextPercentageWidget {
54388
54441
  return formatRawOrLabeledValue(item, "Ctx: ", previewValue);
54389
54442
  }
54390
54443
  const metrics = getContextWindowMetrics(context.data);
54391
- if (metrics.usedPercentage !== null) {
54392
- const displayPercentage = isInverse ? 100 - metrics.usedPercentage : metrics.usedPercentage;
54444
+ if (metrics.currentContextUsedPercentage !== null) {
54445
+ const pct = metrics.currentContextUsedPercentage;
54446
+ const displayPercentage = isInverse ? 100 - pct : pct;
54393
54447
  return formatRawOrLabeledValue(item, "Ctx: ", `${displayPercentage.toFixed(1)}%`);
54394
54448
  }
54395
54449
  return null;
@@ -54406,79 +54460,6 @@ class ContextPercentageWidget {
54406
54460
  return true;
54407
54461
  }
54408
54462
  }
54409
- // src/utils/model-context.ts
54410
- var DEFAULT_CONTEXT_WINDOW_SIZE = 200000;
54411
- var USABLE_CONTEXT_RATIO = 0.8;
54412
- function toValidWindowSize(value) {
54413
- if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
54414
- return null;
54415
- }
54416
- return value;
54417
- }
54418
- function getContextConfig(contextWindowSize) {
54419
- const statusWindowSize = toValidWindowSize(contextWindowSize);
54420
- if (statusWindowSize !== null) {
54421
- return {
54422
- maxTokens: statusWindowSize,
54423
- usableTokens: Math.floor(statusWindowSize * USABLE_CONTEXT_RATIO)
54424
- };
54425
- }
54426
- return {
54427
- maxTokens: DEFAULT_CONTEXT_WINDOW_SIZE,
54428
- usableTokens: Math.floor(DEFAULT_CONTEXT_WINDOW_SIZE * USABLE_CONTEXT_RATIO)
54429
- };
54430
- }
54431
-
54432
- // src/widgets/ContextPercentageUsable.ts
54433
- class ContextPercentageUsableWidget {
54434
- getDefaultColor() {
54435
- return "blue";
54436
- }
54437
- getDescription() {
54438
- return "Shows percentage of usable context window used or remaining (80% of total)";
54439
- }
54440
- getDisplayName() {
54441
- return "Context % (Usable)";
54442
- }
54443
- getCategory() {
54444
- return "Context";
54445
- }
54446
- getEditorDisplay(item) {
54447
- return {
54448
- displayText: this.getDisplayName(),
54449
- modifierText: getContextInverseModifierText(item)
54450
- };
54451
- }
54452
- handleEditorAction(action, item) {
54453
- return handleContextInverseAction(action, item);
54454
- }
54455
- render(item, context, settings) {
54456
- const isInverse = isContextInverse(item);
54457
- if (context.isPreview) {
54458
- const previewValue = isInverse ? "88.4%" : "11.6%";
54459
- return formatRawOrLabeledValue(item, "Usable: ", previewValue);
54460
- }
54461
- const metrics = getContextWindowMetrics(context.data);
54462
- if (metrics.usedTokens !== null && metrics.windowSize !== null) {
54463
- const contextConfig = getContextConfig(metrics.windowSize);
54464
- const usablePercent = Math.min(100, metrics.usedTokens / contextConfig.usableTokens * 100);
54465
- const displayPercentage = isInverse ? 100 - usablePercent : usablePercent;
54466
- return formatRawOrLabeledValue(item, "Usable: ", `${displayPercentage.toFixed(1)}%`);
54467
- }
54468
- return null;
54469
- }
54470
- getCustomKeybinds() {
54471
- return [
54472
- { key: "u", label: "(u)sed/remaining", action: "toggle-inverse" }
54473
- ];
54474
- }
54475
- supportsRawValue() {
54476
- return true;
54477
- }
54478
- supportsColors(item) {
54479
- return true;
54480
- }
54481
- }
54482
54463
  // src/utils/progress-bar.ts
54483
54464
  var PROGRESS_BAR_FG_RESTORE = "\x1B[0;999;0q";
54484
54465
  function makeUsageProgressBar(percent, width = 15, powerlineMode = false) {
@@ -54544,13 +54525,13 @@ class ContextBarWidget {
54544
54525
  return item.rawValue ? previewDisplay : `Context: ${previewDisplay}`;
54545
54526
  }
54546
54527
  const contextWindowMetrics = getContextWindowMetrics(context.data);
54547
- const total = contextWindowMetrics.windowSize;
54548
- const used = contextWindowMetrics.contextLengthTokens;
54549
- if (used === null || total === null || total <= 0) {
54528
+ const total = contextWindowMetrics.displayedContextLimit ?? contextWindowMetrics.windowSize;
54529
+ const used = contextWindowMetrics.currentContextTokens;
54530
+ const upstreamPct = contextWindowMetrics.currentContextUsedPercentage;
54531
+ if (used === null || total === null || total <= 0 || upstreamPct === null) {
54550
54532
  return null;
54551
54533
  }
54552
- const percent = used / total * 100;
54553
- const clampedPercent = Math.max(0, Math.min(100, percent));
54534
+ const clampedPercent = Math.max(0, Math.min(100, upstreamPct));
54554
54535
  const usedK = Math.round(used / 1000);
54555
54536
  const totalK = Math.round(total / 1000);
54556
54537
  const display = `${makeUsageProgressBar(clampedPercent, barWidth, powerlineMode)} ${usedK}k/${totalK}k (${Math.round(clampedPercent)}%)`;
@@ -54810,7 +54791,7 @@ class RemainingTokensWidget {
54810
54791
  return "brightBlack";
54811
54792
  }
54812
54793
  getDescription() {
54813
- return "Shows absolute remaining context tokens";
54794
+ return "Shows remaining tokens in current context (displayed_context_limit − current_context_tokens)";
54814
54795
  }
54815
54796
  getDisplayName() {
54816
54797
  return "Remaining Tokens";
@@ -54826,8 +54807,10 @@ class RemainingTokensWidget {
54826
54807
  return formatRawOrLabeledValue(item, "Remaining: ", "164.8k");
54827
54808
  }
54828
54809
  const metrics = getContextWindowMetrics(context.data);
54829
- if (metrics.remainingTokens !== null) {
54830
- return formatRawOrLabeledValue(item, "Remaining: ", formatTokens(metrics.remainingTokens));
54810
+ const limit = metrics.displayedContextLimit ?? metrics.windowSize;
54811
+ if (limit !== null && metrics.currentContextTokens !== null) {
54812
+ const remaining = Math.max(0, limit - metrics.currentContextTokens);
54813
+ return formatRawOrLabeledValue(item, "Remaining: ", formatTokens(remaining));
54831
54814
  }
54832
54815
  return null;
54833
54816
  }
@@ -58151,10 +58134,11 @@ var WIDGET_MANIFEST = [
58151
58134
  { type: "tokens-input", create: () => new TokensInputWidget },
58152
58135
  { type: "tokens-output", create: () => new TokensOutputWidget },
58153
58136
  { type: "tokens-cached", create: () => new TokensCachedWidget },
58137
+ { type: "tokens-reasoning", create: () => new TokensReasoningWidget },
58154
58138
  { type: "tokens-total", create: () => new TokensTotalWidget },
58155
58139
  { type: "context-length", create: () => new ContextLengthWidget },
58140
+ { type: "context-window", create: () => new ContextWindowWidget },
58156
58141
  { type: "context-percentage", create: () => new ContextPercentageWidget },
58157
- { type: "context-percentage-usable", create: () => new ContextPercentageUsableWidget },
58158
58142
  { type: "context-bar", create: () => new ContextBarWidget },
58159
58143
  { type: "session-clock", create: () => new SessionClockWidget },
58160
58144
  { type: "premium-requests", create: () => new PremiumRequestsWidget },
@@ -62043,8 +62027,8 @@ function advanceGlobalPowerlineThemeIndex(currentIndex, entries) {
62043
62027
  // src/utils/context-percentage.ts
62044
62028
  function calculateContextPercentage(context) {
62045
62029
  const contextWindowMetrics = getContextWindowMetrics(context.data);
62046
- if (contextWindowMetrics.usedPercentage !== null) {
62047
- return contextWindowMetrics.usedPercentage;
62030
+ if (contextWindowMetrics.currentContextUsedPercentage !== null) {
62031
+ return contextWindowMetrics.currentContextUsedPercentage;
62048
62032
  }
62049
62033
  return 0;
62050
62034
  }
@@ -63422,6 +63406,7 @@ var CopilotPayloadSchema = exports_external.object({
63422
63406
  reasoning_effort: exports_external.string().nullable().optional()
63423
63407
  }).optional(),
63424
63408
  workspace: exports_external.object({ current_dir: exports_external.string().optional() }).optional(),
63409
+ remote: exports_external.object({ connected: exports_external.boolean().optional() }).optional(),
63425
63410
  version: exports_external.string().optional(),
63426
63411
  cost: exports_external.object({
63427
63412
  total_api_duration_ms: exports_external.number().optional(),
@@ -63436,12 +63421,16 @@ var CopilotPayloadSchema = exports_external.object({
63436
63421
  total_output_tokens: exports_external.number().optional(),
63437
63422
  total_cache_read_tokens: exports_external.number().optional(),
63438
63423
  total_cache_write_tokens: exports_external.number().optional(),
63424
+ total_reasoning_tokens: exports_external.number().optional(),
63439
63425
  total_tokens: exports_external.number().optional(),
63440
63426
  used_percentage: OptionalNumber,
63441
63427
  remaining_percentage: OptionalNumber,
63442
63428
  remaining_tokens: OptionalNumber,
63443
63429
  last_call_input_tokens: exports_external.number().optional(),
63444
63430
  last_call_output_tokens: exports_external.number().optional(),
63431
+ current_context_tokens: OptionalNumber,
63432
+ current_context_used_percentage: OptionalNumber,
63433
+ displayed_context_limit: OptionalNumber,
63445
63434
  current_usage: exports_external.object({
63446
63435
  input_tokens: exports_external.number().optional(),
63447
63436
  output_tokens: exports_external.number().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "copilot-statusline",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "A customizable status line formatter for GitHub Copilot CLI — based on ccstatusline",
5
5
  "module": "src/copilot-statusline.ts",
6
6
  "type": "module",