copilot-statusline 0.1.11 → 0.1.13

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
@@ -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.11";
52916
+ var PACKAGE_VERSION = "0.1.13";
52917
52917
  function getPackageVersion() {
52918
52918
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
52919
52919
  return PACKAGE_VERSION;
@@ -54074,14 +54074,18 @@ function getContextWindowMetrics(data) {
54074
54074
  const contextWindow = data?.context_window;
54075
54075
  const empty = {
54076
54076
  windowSize: null,
54077
+ displayedContextLimit: null,
54077
54078
  usedTokens: null,
54078
54079
  contextLengthTokens: null,
54080
+ currentContextTokens: null,
54081
+ currentContextUsedPercentage: null,
54079
54082
  usedPercentage: null,
54080
54083
  remainingPercentage: null,
54081
54084
  totalInputTokens: null,
54082
54085
  totalOutputTokens: null,
54083
54086
  cachedTokens: null,
54084
54087
  totalTokens: null,
54088
+ reasoningTokens: null,
54085
54089
  remainingTokens: null,
54086
54090
  lastCallInputTokens: null,
54087
54091
  lastCallOutputTokens: null,
@@ -54093,6 +54097,12 @@ function getContextWindowMetrics(data) {
54093
54097
  }
54094
54098
  const rawWindowSize = toFiniteNonNegativeNumber(contextWindow.context_window_size);
54095
54099
  const windowSize = rawWindowSize !== null && rawWindowSize > 0 ? rawWindowSize : null;
54100
+ const rawDisplayedLimit = toFiniteNonNegativeNumber(contextWindow.displayed_context_limit);
54101
+ const displayedContextLimit = rawDisplayedLimit !== null && rawDisplayedLimit > 0 ? rawDisplayedLimit : null;
54102
+ const currentContextTokens = toFiniteNonNegativeNumber(contextWindow.current_context_tokens);
54103
+ const rawCurrentPct = toFiniteNonNegativeNumber(contextWindow.current_context_used_percentage);
54104
+ const currentContextUsedPercentage = rawCurrentPct !== null ? clampPercentage(rawCurrentPct) : null;
54105
+ const reasoningTokens = toFiniteNonNegativeNumber(contextWindow.total_reasoning_tokens);
54096
54106
  const totalInputTokens = toFiniteNonNegativeNumber(contextWindow.total_input_tokens);
54097
54107
  const totalOutputTokens = toFiniteNonNegativeNumber(contextWindow.total_output_tokens);
54098
54108
  const cacheReadTokens = toFiniteNonNegativeNumber(contextWindow.total_cache_read_tokens);
@@ -54123,14 +54133,18 @@ function getContextWindowMetrics(data) {
54123
54133
  const contextLengthFromAuthoritative = usedTokensFromRemaining ?? usedTokensFromPercentage;
54124
54134
  return {
54125
54135
  windowSize,
54136
+ displayedContextLimit,
54126
54137
  usedTokens,
54127
54138
  contextLengthTokens: contextLengthFromAuthoritative ?? contextLengthTokens ?? usedTokens,
54139
+ currentContextTokens,
54140
+ currentContextUsedPercentage,
54128
54141
  usedPercentage,
54129
54142
  remainingPercentage,
54130
54143
  totalInputTokens,
54131
54144
  totalOutputTokens,
54132
54145
  cachedTokens,
54133
54146
  totalTokens,
54147
+ reasoningTokens,
54134
54148
  remainingTokens,
54135
54149
  lastCallInputTokens,
54136
54150
  lastCallOutputTokens,
@@ -54255,6 +54269,40 @@ class TokensCachedWidget {
54255
54269
  return true;
54256
54270
  }
54257
54271
  }
54272
+ // src/widgets/TokensReasoning.ts
54273
+ class TokensReasoningWidget {
54274
+ getDefaultColor() {
54275
+ return "magenta";
54276
+ }
54277
+ getDescription() {
54278
+ return "Shows total reasoning (thinking) tokens consumed this session";
54279
+ }
54280
+ getDisplayName() {
54281
+ return "Tokens Reasoning";
54282
+ }
54283
+ getCategory() {
54284
+ return "Tokens";
54285
+ }
54286
+ getEditorDisplay(_item) {
54287
+ return { displayText: this.getDisplayName() };
54288
+ }
54289
+ render(item, context, _settings) {
54290
+ if (context.isPreview) {
54291
+ return formatRawOrLabeledValue(item, "Reasoning: ", "1.2k");
54292
+ }
54293
+ const metrics = getContextWindowMetrics(context.data);
54294
+ if (metrics.reasoningTokens !== null) {
54295
+ return formatRawOrLabeledValue(item, "Reasoning: ", formatTokens(metrics.reasoningTokens));
54296
+ }
54297
+ return null;
54298
+ }
54299
+ supportsRawValue() {
54300
+ return true;
54301
+ }
54302
+ supportsColors(_item) {
54303
+ return true;
54304
+ }
54305
+ }
54258
54306
  // src/widgets/TokensTotal.ts
54259
54307
  class TokensTotalWidget {
54260
54308
  getDefaultColor() {
@@ -54295,7 +54343,7 @@ class ContextLengthWidget {
54295
54343
  return "brightBlack";
54296
54344
  }
54297
54345
  getDescription() {
54298
- return "Shows context window size";
54346
+ return "Shows current context length in tokens (live)";
54299
54347
  }
54300
54348
  getDisplayName() {
54301
54349
  return "Context Length";
@@ -54308,11 +54356,11 @@ class ContextLengthWidget {
54308
54356
  }
54309
54357
  render(item, context, settings) {
54310
54358
  if (context.isPreview) {
54311
- return formatRawOrLabeledValue(item, "Ctx: ", "200k");
54359
+ return formatRawOrLabeledValue(item, "Ctx: ", "33k");
54312
54360
  }
54313
54361
  const metrics = getContextWindowMetrics(context.data);
54314
- if (metrics.windowSize !== null) {
54315
- return formatRawOrLabeledValue(item, "Ctx: ", formatTokens(metrics.windowSize));
54362
+ if (metrics.currentContextTokens !== null) {
54363
+ return formatRawOrLabeledValue(item, "Ctx: ", formatTokens(metrics.currentContextTokens));
54316
54364
  }
54317
54365
  return null;
54318
54366
  }
@@ -54323,6 +54371,41 @@ class ContextLengthWidget {
54323
54371
  return true;
54324
54372
  }
54325
54373
  }
54374
+ // src/widgets/ContextWindow.ts
54375
+ class ContextWindowWidget {
54376
+ getDefaultColor() {
54377
+ return "brightBlack";
54378
+ }
54379
+ getDescription() {
54380
+ return "Shows the model's context window size (max tokens)";
54381
+ }
54382
+ getDisplayName() {
54383
+ return "Context Window";
54384
+ }
54385
+ getCategory() {
54386
+ return "Context";
54387
+ }
54388
+ getEditorDisplay(_item) {
54389
+ return { displayText: this.getDisplayName() };
54390
+ }
54391
+ render(item, context, _settings) {
54392
+ if (context.isPreview) {
54393
+ return formatRawOrLabeledValue(item, "Window: ", "1000k");
54394
+ }
54395
+ const metrics = getContextWindowMetrics(context.data);
54396
+ const size2 = metrics.displayedContextLimit ?? metrics.windowSize;
54397
+ if (size2 !== null) {
54398
+ return formatRawOrLabeledValue(item, "Window: ", formatTokens(size2));
54399
+ }
54400
+ return null;
54401
+ }
54402
+ supportsRawValue() {
54403
+ return true;
54404
+ }
54405
+ supportsColors(_item) {
54406
+ return true;
54407
+ }
54408
+ }
54326
54409
  // src/widgets/shared/editor-display.ts
54327
54410
  function makeModifierText(modifiers) {
54328
54411
  return modifiers.length > 0 ? `(${modifiers.join(", ")})` : undefined;
@@ -54388,8 +54471,9 @@ class ContextPercentageWidget {
54388
54471
  return formatRawOrLabeledValue(item, "Ctx: ", previewValue);
54389
54472
  }
54390
54473
  const metrics = getContextWindowMetrics(context.data);
54391
- if (metrics.usedPercentage !== null) {
54392
- const displayPercentage = isInverse ? 100 - metrics.usedPercentage : metrics.usedPercentage;
54474
+ if (metrics.currentContextUsedPercentage !== null) {
54475
+ const pct = metrics.currentContextUsedPercentage;
54476
+ const displayPercentage = isInverse ? 100 - pct : pct;
54393
54477
  return formatRawOrLabeledValue(item, "Ctx: ", `${displayPercentage.toFixed(1)}%`);
54394
54478
  }
54395
54479
  return null;
@@ -54406,36 +54490,13 @@ class ContextPercentageWidget {
54406
54490
  return true;
54407
54491
  }
54408
54492
  }
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
54493
  // src/widgets/ContextPercentageUsable.ts
54433
54494
  class ContextPercentageUsableWidget {
54434
54495
  getDefaultColor() {
54435
54496
  return "blue";
54436
54497
  }
54437
54498
  getDescription() {
54438
- return "Shows percentage of usable context window used or remaining (80% of total)";
54499
+ return "Shows percentage of usable context (current_context_tokens / displayed_context_limit)";
54439
54500
  }
54440
54501
  getDisplayName() {
54441
54502
  return "Context % (Usable)";
@@ -54459,9 +54520,8 @@ class ContextPercentageUsableWidget {
54459
54520
  return formatRawOrLabeledValue(item, "Usable: ", previewValue);
54460
54521
  }
54461
54522
  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);
54523
+ if (metrics.currentContextTokens !== null && metrics.displayedContextLimit !== null && metrics.displayedContextLimit > 0) {
54524
+ const usablePercent = Math.min(100, metrics.currentContextTokens / metrics.displayedContextLimit * 100);
54465
54525
  const displayPercentage = isInverse ? 100 - usablePercent : usablePercent;
54466
54526
  return formatRawOrLabeledValue(item, "Usable: ", `${displayPercentage.toFixed(1)}%`);
54467
54527
  }
@@ -54544,8 +54604,8 @@ class ContextBarWidget {
54544
54604
  return item.rawValue ? previewDisplay : `Context: ${previewDisplay}`;
54545
54605
  }
54546
54606
  const contextWindowMetrics = getContextWindowMetrics(context.data);
54547
- const total = contextWindowMetrics.windowSize;
54548
- const used = contextWindowMetrics.contextLengthTokens;
54607
+ const total = contextWindowMetrics.displayedContextLimit ?? contextWindowMetrics.windowSize;
54608
+ const used = contextWindowMetrics.currentContextTokens;
54549
54609
  if (used === null || total === null || total <= 0) {
54550
54610
  return null;
54551
54611
  }
@@ -54810,7 +54870,7 @@ class RemainingTokensWidget {
54810
54870
  return "brightBlack";
54811
54871
  }
54812
54872
  getDescription() {
54813
- return "Shows absolute remaining context tokens";
54873
+ return "Shows remaining tokens in current context (displayed_context_limit − current_context_tokens)";
54814
54874
  }
54815
54875
  getDisplayName() {
54816
54876
  return "Remaining Tokens";
@@ -54826,8 +54886,10 @@ class RemainingTokensWidget {
54826
54886
  return formatRawOrLabeledValue(item, "Remaining: ", "164.8k");
54827
54887
  }
54828
54888
  const metrics = getContextWindowMetrics(context.data);
54829
- if (metrics.remainingTokens !== null) {
54830
- return formatRawOrLabeledValue(item, "Remaining: ", formatTokens(metrics.remainingTokens));
54889
+ const limit = metrics.displayedContextLimit ?? metrics.windowSize;
54890
+ if (limit !== null && metrics.currentContextTokens !== null) {
54891
+ const remaining = Math.max(0, limit - metrics.currentContextTokens);
54892
+ return formatRawOrLabeledValue(item, "Remaining: ", formatTokens(remaining));
54831
54893
  }
54832
54894
  return null;
54833
54895
  }
@@ -56633,6 +56695,114 @@ class GitIsForkWidget {
56633
56695
  return true;
56634
56696
  }
56635
56697
  }
56698
+ // src/widgets/GitWorktree.ts
56699
+ class GitWorktreeWidget {
56700
+ getDefaultColor() {
56701
+ return "blue";
56702
+ }
56703
+ getDescription() {
56704
+ return "Shows the current git worktree name";
56705
+ }
56706
+ getDisplayName() {
56707
+ return "Git Worktree";
56708
+ }
56709
+ getCategory() {
56710
+ return "Git";
56711
+ }
56712
+ getEditorDisplay(item) {
56713
+ return {
56714
+ displayText: this.getDisplayName(),
56715
+ modifierText: getHideNoGitModifierText(item)
56716
+ };
56717
+ }
56718
+ handleEditorAction(action, item) {
56719
+ return handleToggleNoGitAction(action, item);
56720
+ }
56721
+ render(item, context) {
56722
+ const hideNoGit = isHideNoGitEnabled(item);
56723
+ if (context.isPreview)
56724
+ return item.rawValue ? "main" : "\uD81A\uDC30 main";
56725
+ if (!isInsideGitWorkTree(context)) {
56726
+ return hideNoGit ? null : "\uD81A\uDC30 no git";
56727
+ }
56728
+ const worktree = this.getGitWorktree(context);
56729
+ if (worktree)
56730
+ return item.rawValue ? worktree : `\uD81A\uDC30 ${worktree}`;
56731
+ return hideNoGit ? null : "\uD81A\uDC30 no git";
56732
+ }
56733
+ getGitWorktree(context) {
56734
+ const worktreeDir = runGit("rev-parse --git-dir", context);
56735
+ if (!worktreeDir)
56736
+ return null;
56737
+ const normalizedGitDir = worktreeDir.replace(/\\/g, "/");
56738
+ if (normalizedGitDir.endsWith("/.git") || normalizedGitDir === ".git")
56739
+ return "main";
56740
+ const repoMarker = ".git/worktrees/";
56741
+ const repoMarkerIndex = normalizedGitDir.lastIndexOf(repoMarker);
56742
+ if (repoMarkerIndex !== -1) {
56743
+ const worktree2 = normalizedGitDir.slice(repoMarkerIndex + repoMarker.length);
56744
+ return worktree2.length > 0 ? worktree2 : null;
56745
+ }
56746
+ const bareMarker = "/worktrees/";
56747
+ const bareMarkerIndex = normalizedGitDir.lastIndexOf(bareMarker);
56748
+ if (bareMarkerIndex === -1)
56749
+ return null;
56750
+ const worktree = normalizedGitDir.slice(bareMarkerIndex + bareMarker.length);
56751
+ return worktree.length > 0 ? worktree : null;
56752
+ }
56753
+ getCustomKeybinds() {
56754
+ return getHideNoGitKeybinds();
56755
+ }
56756
+ supportsRawValue() {
56757
+ return true;
56758
+ }
56759
+ supportsColors(_item) {
56760
+ return true;
56761
+ }
56762
+ }
56763
+ // src/widgets/GitWorktreeMode.ts
56764
+ class GitWorktreeModeWidget {
56765
+ getDefaultColor() {
56766
+ return "yellow";
56767
+ }
56768
+ getDescription() {
56769
+ return "Shows indicator when current dir is a non-main git worktree";
56770
+ }
56771
+ getDisplayName() {
56772
+ return "Git Worktree Mode";
56773
+ }
56774
+ getCategory() {
56775
+ return "Git";
56776
+ }
56777
+ getEditorDisplay(_item) {
56778
+ return { displayText: this.getDisplayName() };
56779
+ }
56780
+ render(item, context) {
56781
+ const isInWorktree = context.isPreview ? true : this.isInLinkedWorktree(context);
56782
+ if (item.rawValue) {
56783
+ return isInWorktree ? "true" : "false";
56784
+ }
56785
+ if (!isInWorktree) {
56786
+ return null;
56787
+ }
56788
+ return "⎇";
56789
+ }
56790
+ isInLinkedWorktree(context) {
56791
+ if (!isInsideGitWorkTree(context))
56792
+ return false;
56793
+ const gitDir = runGit("rev-parse --git-dir", context);
56794
+ if (!gitDir)
56795
+ return false;
56796
+ const normalized = gitDir.replace(/\\/g, "/");
56797
+ return normalized.includes("/worktrees/");
56798
+ }
56799
+ supportsRawValue() {
56800
+ return true;
56801
+ }
56802
+ supportsColors(_item) {
56803
+ return true;
56804
+ }
56805
+ }
56636
56806
  // src/widgets/CurrentWorkingDir.tsx
56637
56807
  var import_react29 = __toESM(require_react(), 1);
56638
56808
  import * as os7 from "node:os";
@@ -58043,8 +58213,10 @@ var WIDGET_MANIFEST = [
58043
58213
  { type: "tokens-input", create: () => new TokensInputWidget },
58044
58214
  { type: "tokens-output", create: () => new TokensOutputWidget },
58045
58215
  { type: "tokens-cached", create: () => new TokensCachedWidget },
58216
+ { type: "tokens-reasoning", create: () => new TokensReasoningWidget },
58046
58217
  { type: "tokens-total", create: () => new TokensTotalWidget },
58047
58218
  { type: "context-length", create: () => new ContextLengthWidget },
58219
+ { type: "context-window", create: () => new ContextWindowWidget },
58048
58220
  { type: "context-percentage", create: () => new ContextPercentageWidget },
58049
58221
  { type: "context-percentage-usable", create: () => new ContextPercentageUsableWidget },
58050
58222
  { type: "context-bar", create: () => new ContextBarWidget },
@@ -58077,6 +58249,8 @@ var WIDGET_MANIFEST = [
58077
58249
  { type: "git-upstream-repo", create: () => new GitUpstreamRepoWidget },
58078
58250
  { type: "git-upstream-owner-repo", create: () => new GitUpstreamOwnerRepoWidget },
58079
58251
  { type: "git-is-fork", create: () => new GitIsForkWidget },
58252
+ { type: "git-worktree", create: () => new GitWorktreeWidget },
58253
+ { type: "git-worktree-mode", create: () => new GitWorktreeModeWidget },
58080
58254
  { type: "current-working-dir", create: () => new CurrentWorkingDirWidget },
58081
58255
  { type: "terminal-width", create: () => new TerminalWidthWidget },
58082
58256
  { type: "free-memory", create: () => new FreeMemoryWidget },
@@ -63312,6 +63486,7 @@ var CopilotPayloadSchema = exports_external.object({
63312
63486
  reasoning_effort: exports_external.string().nullable().optional()
63313
63487
  }).optional(),
63314
63488
  workspace: exports_external.object({ current_dir: exports_external.string().optional() }).optional(),
63489
+ remote: exports_external.object({ connected: exports_external.boolean().optional() }).optional(),
63315
63490
  version: exports_external.string().optional(),
63316
63491
  cost: exports_external.object({
63317
63492
  total_api_duration_ms: exports_external.number().optional(),
@@ -63326,12 +63501,16 @@ var CopilotPayloadSchema = exports_external.object({
63326
63501
  total_output_tokens: exports_external.number().optional(),
63327
63502
  total_cache_read_tokens: exports_external.number().optional(),
63328
63503
  total_cache_write_tokens: exports_external.number().optional(),
63504
+ total_reasoning_tokens: exports_external.number().optional(),
63329
63505
  total_tokens: exports_external.number().optional(),
63330
63506
  used_percentage: OptionalNumber,
63331
63507
  remaining_percentage: OptionalNumber,
63332
63508
  remaining_tokens: OptionalNumber,
63333
63509
  last_call_input_tokens: exports_external.number().optional(),
63334
63510
  last_call_output_tokens: exports_external.number().optional(),
63511
+ current_context_tokens: OptionalNumber,
63512
+ current_context_used_percentage: OptionalNumber,
63513
+ displayed_context_limit: OptionalNumber,
63335
63514
  current_usage: exports_external.object({
63336
63515
  input_tokens: exports_external.number().optional(),
63337
63516
  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.11",
3
+ "version": "0.1.13",
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",