pi-bar 0.3.29 → 0.3.31

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
@@ -3,25 +3,25 @@
3
3
  [![npm version](https://img.shields.io/npm/v/pi-bar.svg)](https://www.npmjs.com/package/pi-bar)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/pi-bar.svg)](https://www.npmjs.com/package/pi-bar)
5
5
 
6
- **Never accidentally run Opus on a typo again.** pi-bar keeps your model, thinking level, context pressure, extension status, and a live one-line TLDR visible in pi's footer.
6
+ **Never accidentally run Opus on a typo again.** pi-bar keeps your model, thinking level, context pressure, a live progress update, and any extension statuses visible in pi's footer.
7
7
 
8
8
  ```text
9
- claude-opus-4.7 ❯ think:med ❯ 2.6% / 1.0M ❯ Inspecting package structure ❯ plan:active
9
+ claude-opus-4.7 ❯ think:med ❯ 2.6% / 1.0M ❯ Reviewing package structure ❯ plan:active
10
10
  ```
11
11
 
12
- ![pi-bar with low context usage](https://cdn.jsdelivr.net/npm/pi-bar@0.3.29/assets/screenshot-green.png)
13
- ![pi-bar with medium context usage](https://cdn.jsdelivr.net/npm/pi-bar@0.3.29/assets/screenshot-yellow.png)
14
- ![pi-bar with high context usage](https://cdn.jsdelivr.net/npm/pi-bar@0.3.29/assets/screenshot-red.png)
12
+ ![pi-bar with low context usage](https://cdn.jsdelivr.net/npm/pi-bar@0.3.31/assets/screenshot-green.png)
13
+ ![pi-bar with medium context usage](https://cdn.jsdelivr.net/npm/pi-bar@0.3.31/assets/screenshot-yellow.png)
14
+ ![pi-bar with high context usage](https://cdn.jsdelivr.net/npm/pi-bar@0.3.31/assets/screenshot-red.png)
15
15
 
16
16
  ## Why use it?
17
17
 
18
18
  - **See the active model at a glance** — catch accidental model switches before an expensive or sensitive task starts.
19
19
  - **Track thinking level in place** — immediately notice when you are using the wrong reasoning setting.
20
20
  - **Watch context pressure early** — context usage turns green, yellow, then red as you approach the limit.
21
- - **Follow what pi is doing** — a live TLDR keeps the current task visible without scrolling.
22
- - **Keep extension statuses visible** — statuses set with `ctx.ui.setStatus()` still appear in the custom footer.
21
+ - **Follow what pi is doing** — a one-line progress update keeps the current task visible without scrolling.
22
+ - **Keep extension statuses visible** — statuses other extensions set still appear in the footer.
23
23
 
24
- pi-bar is intentionally tiny: one small extension, no broad behavior changes, and no commands to learn. It replaces pi's built-in footer with a compact model / thinking / context / TLDR / extension-status line.
24
+ pi-bar is intentionally tiny: one small extension, no broad behavior changes, and no commands to learn. It replaces pi's built-in footer with a compact model / thinking / context / progress / extension-status line.
25
25
 
26
26
  ## Install
27
27
 
@@ -42,36 +42,35 @@ pi-bar works out of the box, but you can tune it with environment variables befo
42
42
  ### Show or hide segments
43
43
 
44
44
  ```bash
45
- PI_BAR_SHOW=model,thinking,context,tldr,extensions pi
45
+ PI_BAR_SHOW=model,thinking,context,progress,extensions pi
46
46
  PI_BAR_SHOW=model,context pi
47
47
  ```
48
48
 
49
- Allowed segments are `model`, `thinking`, `context`, `tldr`, and `extensions`. The `tldr` segment is hidden until pi-bar generates a current TLDR. The `extensions` segment is hidden when no extension has set a status.
49
+ Allowed segments are `model`, `thinking`, `context`, `progress`, and `extensions`. The `progress` segment stays hidden until pi-bar has a current update. The `extensions` segment stays hidden when no extension has set a status.
50
50
 
51
- ### Configure live TLDR
51
+ ### Configure live progress updates
52
52
 
53
- pi-bar shows a concise live TLDR of the current task in the footer. It generates TLDRs from recent prompt, assistant, and deterministic code-compressed tool facts (for example `bash ... ok`, `grep ... 4 matches`, `read file`, or `custom_tool key=value ok`). It resets when you navigate the session tree so stale summaries do not follow you across branches. Set `PI_BAR_SHOW` without `tldr` to hide it.
53
+ pi-bar shows a short, plain-English description of what pi is working on right now. It refreshes as pi works and resets when you switch branches in the session tree, so stale updates never follow you across tasks. Set `PI_BAR_SHOW` without `progress` to hide it.
54
54
 
55
- Override the TLDR model with:
55
+ Pick a specific model for the update by setting the env var or pi settings:
56
56
 
57
57
  ```bash
58
- PI_BAR_TLDR_MODEL=anthropic/claude-haiku-4-5 pi
58
+ PI_BAR_PROGRESS_MODEL=anthropic/claude-haiku-4-5 pi
59
59
  ```
60
60
 
61
- You can also set `bar.tldrModel` in pi settings.
61
+ Or in pi settings: `bar.progressModel`. Otherwise pi-bar picks a fast model you are already authenticated with.
62
62
 
63
63
  ### Configure extension statuses
64
64
 
65
- pi-bar shows statuses set by other extensions with `ctx.ui.setStatus()`.
66
- Use `/bar` inside pi to open an interactive status-visibility picker.
65
+ Other pi extensions can publish small status badges. Pi-bar collects them into the `extensions` segment. Run `/bar` inside pi to pick which ones to show:
67
66
 
68
67
  ```text
69
68
  /bar
70
69
  ```
71
70
 
72
- Toggle each status key between `shown` and `hidden`. The `New statuses` row controls whether status keys discovered later are shown or hidden by default.
71
+ Toggle each status between `shown` and `hidden`. The `New statuses` row controls the default for badges that appear later.
73
72
 
74
- Status filter choices persist globally across pi sessions. Set `PI_BAR_CONFIG=/path/to/config.json` before launching pi to override the default config path (`~/.pi/agent/pi-bar.json`). Advanced command forms like `/bar status hide <key>` still work for scripting.
73
+ Your choices persist across pi sessions in `~/.pi/agent/pi-bar.json`. Override the path with `PI_BAR_CONFIG=/some/path.json`.
75
74
 
76
75
  ### Change context thresholds
77
76
 
@@ -90,4 +89,4 @@ The first number is the warning/yellow threshold. The second number is the dange
90
89
 
91
90
  Pi extensions run with your local user permissions. Review any pi package source before installing it.
92
91
 
93
- Live TLDR generation sends short activity snippets to the selected TLDR model provider. Disable the `tldr` segment with `PI_BAR_SHOW` if that is unacceptable.
92
+ Progress updates send short snippets of your session activity to the selected model provider. Disable the `progress` segment with `PI_BAR_SHOW` if that is not acceptable.
@@ -1,18 +1,20 @@
1
1
  /**
2
- * pi-bar — footer/statusline extension.
2
+ * pi-bar — footer / statusline extension.
3
3
  *
4
- * Replaces the built-in footer with left-aligned segments:
5
- * <model name> ❯ think:<level> ❯ <context% / window> ❯ <tldr> ❯ <extension statuses>
4
+ * Replaces pi's built-in footer with left-aligned segments:
5
+ * <model> ❯ think:<level> ❯ <context% / window> ❯ <progress> ❯ <extensions>
6
6
  *
7
- * Examples:
8
- * claude-opus-4.7 ❯ think:med ❯ 2.6% / 1.0M
7
+ * Example:
8
+ * claude-opus-4.7 ❯ think:med ❯ 2.6% / 1.0M ❯ Reviewing package structure
9
9
  *
10
- * Re-renders on model change, thinking-level change, status updates,
11
- * and after each assistant turn so context usage stays current.
10
+ * Re-renders on model change, thinking-level change, status updates, and after
11
+ * each assistant turn so context usage stays current.
12
12
  *
13
- * Configuration env vars:
14
- * PI_BAR_SHOW=model,thinking,context,tldr,extensions
15
- * PI_BAR_THRESHOLDS=70,90
13
+ * Environment variables:
14
+ * PI_BAR_SHOW comma-separated list of segments to show
15
+ * PI_BAR_THRESHOLDS warning,danger context-usage percentages
16
+ * PI_BAR_PROGRESS_MODEL provider/id for the progress update model
17
+ * PI_BAR_CONFIG override the persisted status-filter config path
16
18
  */
17
19
 
18
20
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
@@ -33,39 +35,39 @@ import {
33
35
  truncateToWidth,
34
36
  } from "@earendil-works/pi-tui";
35
37
 
36
- type SegmentName = "model" | "thinking" | "context" | "tldr" | "extensions";
38
+ type SegmentName = "model" | "thinking" | "context" | "progress" | "extensions";
37
39
  type StatusFilter =
38
40
  | { mode: "all"; hidden: Set<string> }
39
41
  | { mode: "only"; shown: Set<string> };
40
42
  type SerializedStatusFilter =
41
43
  | { mode: "all"; hidden: string[] }
42
44
  | { mode: "only"; shown: string[] };
43
- type TldrActivityType =
45
+ type ProgressActivityType =
44
46
  | "user_message"
45
47
  | "assistant_update"
46
48
  | "tool_call"
47
49
  | "tool_result"
48
50
  | "assistant_final"
49
51
  | "assistant_failure";
50
- type TldrDisplayPriority = "immediate" | "normal" | "final";
51
- type TldrActivity = {
52
+ type ProgressDisplayPriority = "immediate" | "normal" | "final";
53
+ type ProgressActivity = {
52
54
  index: number;
53
- activityType: TldrActivityType;
54
- displayPriority: TldrDisplayPriority;
55
+ activityType: ProgressActivityType;
56
+ displayPriority: ProgressDisplayPriority;
55
57
  text: string;
56
58
  toolCallId?: string;
57
59
  };
58
- type TldrCheckpoint = {
60
+ type ProgressCheckpoint = {
59
61
  activityIndex: number;
60
- displayPriority: TldrDisplayPriority;
62
+ displayPriority: ProgressDisplayPriority;
61
63
  text: string;
62
64
  };
63
- type TldrCheckpointJob = {
65
+ type ProgressCheckpointJob = {
64
66
  activityIndex: number;
65
- displayPriority: TldrDisplayPriority;
67
+ displayPriority: ProgressDisplayPriority;
66
68
  runId: number;
67
69
  };
68
- type TldrModelPreference = { provider: string; id: string };
70
+ type ProgressModelPreference = { provider: string; id: string };
69
71
  type FastModelAuth = {
70
72
  model: Parameters<typeof complete>[0];
71
73
  apiKey: string;
@@ -73,7 +75,10 @@ type FastModelAuth = {
73
75
  };
74
76
 
75
77
  const STATUS_FILTER_ENTRY_TYPE = "pi-bar-status-filter";
76
- const SETTINGS_TLDR_KEY = "tldr";
78
+ const SETTINGS_PROGRESS_KEY = "progress";
79
+ // Legacy settings/env names. The `tldr` keys remain accepted so existing user
80
+ // configs continue to work after the rename to "progress updates".
81
+ const LEGACY_SETTINGS_TLDR_KEY = "tldr";
77
82
  const SETTINGS_BAR_KEY = "bar";
78
83
  const MAX_ACTIVITY_TEXT_CHARS = 800;
79
84
  const MAX_USER_TEXT_CHARS = 700;
@@ -84,10 +89,10 @@ const MAX_FINAL_TEXT_CHARS = 700;
84
89
  const MAX_RETAINED_RAW_ACTIVITIES = 128;
85
90
  const ANSI_PATTERN = /\x1B\[[0-?]*[ -/]*[@-~]/g;
86
91
  const MAX_CONTEXT_CHECKPOINTS = 8;
87
- const TLDR_MAX_TOKENS = 120;
88
- const TLDR_REQUEST_TIMEOUT_MS = 2_000;
89
- const TLDR_DISPLAY_UPDATE_INTERVAL_MS = 1_200;
90
- const TLDR_TARGET_SUMMARY_CHARS = 60;
92
+ const PROGRESS_MAX_TOKENS = 120;
93
+ const PROGRESS_REQUEST_TIMEOUT_MS = 2_000;
94
+ const PROGRESS_DISPLAY_UPDATE_INTERVAL_MS = 1_200;
95
+ const PROGRESS_TARGET_SUMMARY_CHARS = 60;
91
96
  // Burst debouncing on the generation side: coalesce rapid-fire activities into
92
97
  // one model call. Quiet window catches short bursts; max wait surfaces progress
93
98
  // during continuous activity.
@@ -97,7 +102,7 @@ const NORMAL_CHECKPOINT_QUIET_MS = 1_500;
97
102
  const NORMAL_CHECKPOINT_MAX_WAIT_MS = 2_500;
98
103
  // Defensive ceiling: even if the model ignores the length instruction, never
99
104
  // blast a large payload into the footer.
100
- const MAX_SAFE_TLDR_CHARS = 240;
105
+ const MAX_SAFE_PROGRESS_CHARS = 240;
101
106
  const CONFIG_PATH =
102
107
  process.env.PI_BAR_CONFIG ?? join(homedir(), ".pi", "agent", "pi-bar.json");
103
108
 
@@ -105,7 +110,7 @@ const DEFAULT_SEGMENTS: SegmentName[] = [
105
110
  "model",
106
111
  "thinking",
107
112
  "context",
108
- "tldr",
113
+ "progress",
109
114
  "extensions",
110
115
  ];
111
116
  const DEFAULT_WARNING_THRESHOLD = 70;
@@ -168,11 +173,14 @@ function parseSegments(): SegmentName[] {
168
173
  const raw = process.env.PI_BAR_SHOW;
169
174
  if (!raw) return DEFAULT_SEGMENTS;
170
175
 
176
+ // `tldr` is the legacy segment name, kept working as an alias so existing
177
+ // PI_BAR_SHOW configs do not break after the rename to "progress".
171
178
  const requested = raw
172
179
  .split(",")
173
180
  .map((segment) => segment.trim().toLowerCase())
181
+ .map((segment) => (segment === "tldr" ? "progress" : segment))
174
182
  .filter((segment): segment is SegmentName =>
175
- ["model", "thinking", "context", "tldr", "extensions"].includes(segment),
183
+ ["model", "thinking", "context", "progress", "extensions"].includes(segment),
176
184
  );
177
185
 
178
186
  return requested.length > 0 ? requested : DEFAULT_SEGMENTS;
@@ -206,7 +214,7 @@ function parseThresholds(): { warningThreshold: number; errorThreshold: number }
206
214
  };
207
215
  }
208
216
 
209
- function parseTldrModelSpec(value: string): TldrModelPreference | undefined {
217
+ function parseProgressModelSpec(value: string): ProgressModelPreference | undefined {
210
218
  const trimmed = value.trim();
211
219
  if (!trimmed || trimmed === "auto") return undefined;
212
220
  const separator = trimmed.indexOf("/");
@@ -217,51 +225,64 @@ function parseTldrModelSpec(value: string): TldrModelPreference | undefined {
217
225
  function settingsModelValue(settings: Record<string, unknown>): string | undefined {
218
226
  const bar = settings[SETTINGS_BAR_KEY];
219
227
  if (bar && typeof bar === "object" && !Array.isArray(bar)) {
220
- const value = (bar as Record<string, unknown>).tldrModel;
228
+ const record = bar as Record<string, unknown>;
229
+ // Canonical key after rename. Falls through to legacy `tldrModel` so old
230
+ // settings still work without manual migration.
231
+ if (typeof record.progressModel === "string") return record.progressModel;
232
+ if (typeof record.tldrModel === "string") return record.tldrModel;
233
+ }
234
+
235
+ const progressSection = settings[SETTINGS_PROGRESS_KEY];
236
+ if (progressSection && typeof progressSection === "object" && !Array.isArray(progressSection)) {
237
+ const value = (progressSection as Record<string, unknown>).model;
221
238
  if (typeof value === "string") return value;
222
239
  }
223
240
 
224
- const tldr = settings[SETTINGS_TLDR_KEY];
225
- if (tldr && typeof tldr === "object" && !Array.isArray(tldr)) {
226
- const value = (tldr as Record<string, unknown>).model;
241
+ const legacy = settings[LEGACY_SETTINGS_TLDR_KEY];
242
+ if (legacy && typeof legacy === "object" && !Array.isArray(legacy)) {
243
+ const value = (legacy as Record<string, unknown>).model;
227
244
  if (typeof value === "string") return value;
228
245
  }
229
246
 
230
247
  return undefined;
231
248
  }
232
249
 
233
- function resolveTldrModelPreference(cwd: string): TldrModelPreference | undefined {
234
- const envModel = process.env.PI_BAR_TLDR_MODEL;
235
- if (envModel) return parseTldrModelSpec(envModel);
250
+ function resolveProgressModelPreference(cwd: string): ProgressModelPreference | undefined {
251
+ // PI_BAR_PROGRESS_MODEL is the canonical env var. PI_BAR_TLDR_MODEL is
252
+ // honored as a backwards-compat alias for users who set it before the
253
+ // rename.
254
+ const envModel = process.env.PI_BAR_PROGRESS_MODEL ?? process.env.PI_BAR_TLDR_MODEL;
255
+ if (envModel) return parseProgressModelSpec(envModel);
236
256
 
237
257
  const settings = SettingsManager.create(cwd);
238
258
  const projectModel = settingsModelValue(
239
259
  settings.getProjectSettings() as Record<string, unknown>,
240
260
  );
241
- if (projectModel !== undefined) return parseTldrModelSpec(projectModel);
261
+ if (projectModel !== undefined) return parseProgressModelSpec(projectModel);
242
262
 
243
263
  const globalModel = settingsModelValue(
244
264
  settings.getGlobalSettings() as Record<string, unknown>,
245
265
  );
246
- return globalModel ? parseTldrModelSpec(globalModel) : undefined;
266
+ return globalModel ? parseProgressModelSpec(globalModel) : undefined;
247
267
  }
248
268
 
249
- // Fallback order optimizes for low latency + low cost first. The TLDR path does
250
- // not need reasoning quality; codex/gpt-5.4-mini is fastest in practice.
251
- const FAST_TLDR_MODELS: readonly TldrModelPreference[] = [
269
+ // Fallback order optimizes for low latency + low cost first. The progress
270
+ // update path does not need reasoning quality; codex/gpt-5.4-mini is fastest
271
+ // in practice.
272
+ const FAST_PROGRESS_MODELS: readonly ProgressModelPreference[] = [
252
273
  { provider: "openai-codex", id: "gpt-5.4-mini" },
253
274
  { provider: "openai-codex", id: "gpt-5.3-codex-spark" },
254
275
  { provider: "anthropic", id: "claude-haiku-4-5" },
255
276
  { provider: "anthropic", id: "claude-haiku-4-5-20251001" },
256
277
  ];
257
278
 
258
- function formatTldrModelKey(model: TldrModelPreference): string {
279
+ function formatProgressModelKey(model: ProgressModelPreference): string {
259
280
  return `${model.provider}/${model.id}`;
260
281
  }
261
282
 
262
283
  async function getModelAuth(
263
284
  ctx: ExtensionContext,
264
- preference: TldrModelPreference,
285
+ preference: ProgressModelPreference,
265
286
  ): Promise<FastModelAuth | undefined> {
266
287
  const model = ctx.modelRegistry.find(preference.provider, preference.id);
267
288
  if (!model) return undefined;
@@ -272,17 +293,17 @@ async function getModelAuth(
272
293
  : undefined;
273
294
  }
274
295
 
275
- async function getFastTldrModelAuth(
296
+ async function getFastProgressModelAuth(
276
297
  ctx: ExtensionContext,
277
- configuredModel?: TldrModelPreference,
298
+ configuredModel?: ProgressModelPreference,
278
299
  ): Promise<FastModelAuth | undefined> {
279
300
  if (configuredModel) {
280
301
  const configuredAuth = await getModelAuth(ctx, configuredModel);
281
302
  if (configuredAuth) return configuredAuth;
282
303
  }
283
304
 
284
- for (const candidate of FAST_TLDR_MODELS) {
285
- if (configuredModel && formatTldrModelKey(candidate) === formatTldrModelKey(configuredModel)) {
305
+ for (const candidate of FAST_PROGRESS_MODELS) {
306
+ if (configuredModel && formatProgressModelKey(candidate) === formatProgressModelKey(configuredModel)) {
286
307
  continue;
287
308
  }
288
309
  const auth = await getModelAuth(ctx, candidate);
@@ -450,16 +471,16 @@ function extractTextContent(content: readonly unknown[] | undefined): string | u
450
471
  return compacted.length > 0 ? compacted : undefined;
451
472
  }
452
473
 
453
- class TldrFactCollector {
474
+ class ProgressFactCollector {
454
475
  private nextIndex = 1;
455
- private readonly activities: TldrActivity[] = [];
476
+ private readonly activities: ProgressActivity[] = [];
456
477
 
457
478
  resetConversation(): void {
458
479
  this.nextIndex = 1;
459
480
  this.activities.splice(0);
460
481
  }
461
482
 
462
- recordUserMessage(prompt: string): TldrActivity {
483
+ recordUserMessage(prompt: string): ProgressActivity {
463
484
  return this.addActivity(
464
485
  "user_message",
465
486
  "immediate",
@@ -467,7 +488,7 @@ class TldrFactCollector {
467
488
  );
468
489
  }
469
490
 
470
- recordAssistantUpdate(message: unknown): TldrActivity | undefined {
491
+ recordAssistantUpdate(message: unknown): ProgressActivity | undefined {
471
492
  if (!message || typeof message !== "object") return undefined;
472
493
  const record = message as Record<string, unknown>;
473
494
  if (record.role !== "assistant") return undefined;
@@ -492,7 +513,7 @@ class TldrFactCollector {
492
513
  toolName: string;
493
514
  input?: Record<string, unknown>;
494
515
  toolCallId?: string;
495
- }): TldrActivity {
516
+ }): ProgressActivity {
496
517
  return this.addActivity(
497
518
  "tool_call",
498
519
  "normal",
@@ -508,7 +529,7 @@ class TldrFactCollector {
508
529
  content?: readonly unknown[];
509
530
  details?: unknown;
510
531
  toolCallId?: string;
511
- }): TldrActivity {
532
+ }): ProgressActivity {
512
533
  const isError = Boolean(event.isError);
513
534
  const resultText = summarizeToolResult(
514
535
  event.toolName,
@@ -540,7 +561,7 @@ class TldrFactCollector {
540
561
  );
541
562
  }
542
563
 
543
- recordMessageEnd(message: unknown): TldrActivity | "emptyFinalStop" | "ignored" {
564
+ recordMessageEnd(message: unknown): ProgressActivity | "emptyFinalStop" | "ignored" {
544
565
  if (!message || typeof message !== "object") return "ignored";
545
566
  const record = message as Record<string, unknown>;
546
567
  if (record.role !== "assistant") return "ignored";
@@ -569,7 +590,7 @@ class TldrFactCollector {
569
590
  : "ignored";
570
591
  }
571
592
 
572
- activitiesAfter(previousIndex: number, throughIndex: number): readonly TldrActivity[] {
593
+ activitiesAfter(previousIndex: number, throughIndex: number): readonly ProgressActivity[] {
573
594
  return this.activities.filter(
574
595
  (activity) => activity.index > previousIndex && activity.index <= throughIndex,
575
596
  );
@@ -591,11 +612,11 @@ class TldrFactCollector {
591
612
  }
592
613
 
593
614
  private addActivity(
594
- activityType: TldrActivityType,
595
- displayPriority: TldrDisplayPriority,
615
+ activityType: ProgressActivityType,
616
+ displayPriority: ProgressDisplayPriority,
596
617
  text: string,
597
618
  toolCallId?: string,
598
- ): TldrActivity {
619
+ ): ProgressActivity {
599
620
  const activity = {
600
621
  index: this.nextIndex,
601
622
  activityType,
@@ -612,25 +633,25 @@ class TldrFactCollector {
612
633
  }
613
634
  }
614
635
 
615
- class FooterTldrEngine {
616
- private readonly facts = new TldrFactCollector();
617
- private configuredModel?: TldrModelPreference;
636
+ class FooterProgressEngine {
637
+ private readonly facts = new ProgressFactCollector();
638
+ private configuredModel?: ProgressModelPreference;
618
639
  private runId = 0;
619
640
  private latestAcceptedActivityIndex = 0;
620
641
  private lastRenderedActivityIndex = 0;
621
642
  private lastRenderedText = "";
622
643
  private lastDisplayAt = Number.NEGATIVE_INFINITY;
623
- private readonly acceptedCheckpoints: TldrCheckpoint[] = [];
624
- private checkpointQueue: TldrCheckpointJob[] = [];
625
- private inFlightCheckpoint?: TldrCheckpointJob;
626
- private pendingDisplayCheckpoint?: TldrCheckpoint;
644
+ private readonly acceptedCheckpoints: ProgressCheckpoint[] = [];
645
+ private checkpointQueue: ProgressCheckpointJob[] = [];
646
+ private inFlightCheckpoint?: ProgressCheckpointJob;
647
+ private pendingDisplayCheckpoint?: ProgressCheckpoint;
627
648
  private displayTimer?: ReturnType<typeof setTimeout>;
628
649
  private abortController?: AbortController;
629
650
  private currentText: string | null = null;
630
651
  // Normal-priority bursts wait for a quiet window before triggering a model
631
652
  // call. Pi often emits many rapid activities (e.g. streaming reads); without
632
- // debouncing we would hammer the TLDR model for intermediate states.
633
- private pendingNormalCheckpoint?: TldrCheckpointJob;
653
+ // debouncing we would hammer the progress update model for intermediate states.
654
+ private pendingNormalCheckpoint?: ProgressCheckpointJob;
634
655
  private normalCheckpointTimer?: ReturnType<typeof setTimeout>;
635
656
  private normalCheckpointBurstStartedAt?: number;
636
657
 
@@ -641,7 +662,7 @@ class FooterTldrEngine {
641
662
  }
642
663
 
643
664
  startSession(cwd: string): void {
644
- this.configuredModel = resolveTldrModelPreference(cwd);
665
+ this.configuredModel = resolveProgressModelPreference(cwd);
645
666
  this.facts.resetConversation();
646
667
  this.startFreshRun();
647
668
  }
@@ -652,7 +673,7 @@ class FooterTldrEngine {
652
673
  }
653
674
 
654
675
  recordUserMessage(ctx: ExtensionContext, prompt: string): void {
655
- // A new user turn replaces the entire visible TLDR context, so wipe both
676
+ // A new user turn replaces the entire visible progress update context, so wipe both
656
677
  // the rendered text and the prior-turn checkpoint context that feeds the
657
678
  // model. Carrying old checkpoints biased the next turn's phrasing toward
658
679
  // the previous task.
@@ -766,7 +787,7 @@ class FooterTldrEngine {
766
787
 
767
788
  private scheduleNormalCheckpoint(
768
789
  ctx: ExtensionContext,
769
- job: TldrCheckpointJob,
790
+ job: ProgressCheckpointJob,
770
791
  ): void {
771
792
  this.pendingNormalCheckpoint = job;
772
793
  this.normalCheckpointBurstStartedAt ??= performance.now();
@@ -804,7 +825,7 @@ class FooterTldrEngine {
804
825
  this.pump(ctx);
805
826
  }
806
827
 
807
- private enqueue(ctx: ExtensionContext, activity: TldrActivity): void {
828
+ private enqueue(ctx: ExtensionContext, activity: ProgressActivity): void {
808
829
  if (!ctx.hasUI) return;
809
830
  const job = {
810
831
  activityIndex: activity.index,
@@ -868,19 +889,19 @@ class FooterTldrEngine {
868
889
  void this.runCheckpointRequest(ctx, job);
869
890
  }
870
891
 
871
- private isCurrentJob(job: TldrCheckpointJob): boolean {
892
+ private isCurrentJob(job: ProgressCheckpointJob): boolean {
872
893
  return job.runId === this.runId && this.inFlightCheckpoint === job;
873
894
  }
874
895
 
875
896
  private async runCheckpointRequest(
876
897
  ctx: ExtensionContext,
877
- job: TldrCheckpointJob,
898
+ job: ProgressCheckpointJob,
878
899
  ): Promise<void> {
879
900
  let abortController: AbortController | undefined;
880
901
  try {
881
902
  const prompt = this.checkpointPrompt(job);
882
903
  if (!prompt) return;
883
- const auth = await getFastTldrModelAuth(ctx, this.configuredModel);
904
+ const auth = await getFastProgressModelAuth(ctx, this.configuredModel);
884
905
  if (!this.isCurrentJob(job) || !auth) return;
885
906
 
886
907
  abortController = new AbortController();
@@ -891,17 +912,17 @@ class FooterTldrEngine {
891
912
  {
892
913
  apiKey: auth.apiKey,
893
914
  headers: auth.headers,
894
- maxTokens: TLDR_MAX_TOKENS,
915
+ maxTokens: PROGRESS_MAX_TOKENS,
895
916
  maxRetries: 0,
896
917
  cacheRetention: "none",
897
- timeoutMs: TLDR_REQUEST_TIMEOUT_MS,
918
+ timeoutMs: PROGRESS_REQUEST_TIMEOUT_MS,
898
919
  signal: abortController.signal,
899
920
  },
900
921
  );
901
922
  if (!this.isCurrentJob(job) || response.stopReason !== "stop") return;
902
923
 
903
924
  const rawText = extractTextContent(response.content) ?? "";
904
- const text = sanitizeTldrText(rawText);
925
+ const text = sanitizeProgressText(rawText);
905
926
  if (!text) return;
906
927
  const checkpoint = {
907
928
  activityIndex: job.activityIndex,
@@ -921,7 +942,7 @@ class FooterTldrEngine {
921
942
  }
922
943
  }
923
944
 
924
- private acceptCheckpoint(checkpoint: TldrCheckpoint): void {
945
+ private acceptCheckpoint(checkpoint: ProgressCheckpoint): void {
925
946
  this.acceptedCheckpoints.push(checkpoint);
926
947
  if (this.acceptedCheckpoints.length > MAX_CONTEXT_CHECKPOINTS) {
927
948
  this.acceptedCheckpoints.splice(
@@ -935,7 +956,7 @@ class FooterTldrEngine {
935
956
 
936
957
  private considerDisplayingCheckpoint(
937
958
  ctx: ExtensionContext,
938
- checkpoint: TldrCheckpoint,
959
+ checkpoint: ProgressCheckpoint,
939
960
  ): void {
940
961
  if (checkpoint.activityIndex <= this.lastRenderedActivityIndex) return;
941
962
  if (checkpoint.activityIndex !== this.facts.latestActivityIndex()) return;
@@ -947,7 +968,7 @@ class FooterTldrEngine {
947
968
  }
948
969
 
949
970
  const elapsedMs = performance.now() - this.lastDisplayAt;
950
- if (elapsedMs >= TLDR_DISPLAY_UPDATE_INTERVAL_MS) {
971
+ if (elapsedMs >= PROGRESS_DISPLAY_UPDATE_INTERVAL_MS) {
951
972
  this.clearPendingDisplay();
952
973
  this.renderCheckpoint(checkpoint);
953
974
  return;
@@ -961,16 +982,16 @@ class FooterTldrEngine {
961
982
  this.pendingDisplayCheckpoint = undefined;
962
983
  if (!pending || pending.activityIndex !== this.facts.latestActivityIndex()) return;
963
984
  this.renderCheckpoint(pending);
964
- }, TLDR_DISPLAY_UPDATE_INTERVAL_MS - elapsedMs);
985
+ }, PROGRESS_DISPLAY_UPDATE_INTERVAL_MS - elapsedMs);
965
986
  }
966
987
 
967
- private renderCheckpoint(checkpoint: TldrCheckpoint): void {
988
+ private renderCheckpoint(checkpoint: ProgressCheckpoint): void {
968
989
  if (checkpoint.activityIndex <= this.lastRenderedActivityIndex) return;
969
990
  if (checkpoint.text === this.lastRenderedText) return;
970
991
  // Skip near-duplicates: a burst of consecutive checkpoints often produces
971
992
  // the same action fragment with only punctuation/casing differences. They
972
993
  // flash visibly in the footer without adding information.
973
- if (isNearDuplicateTldr(checkpoint.text, this.lastRenderedText)) {
994
+ if (isNearDuplicateProgress(checkpoint.text, this.lastRenderedText)) {
974
995
  this.lastRenderedActivityIndex = checkpoint.activityIndex;
975
996
  return;
976
997
  }
@@ -981,7 +1002,7 @@ class FooterTldrEngine {
981
1002
  this.requestRender();
982
1003
  }
983
1004
 
984
- private checkpointPrompt(job: TldrCheckpointJob): UserMessage | undefined {
1005
+ private checkpointPrompt(job: ProgressCheckpointJob): UserMessage | undefined {
985
1006
  const rawActivities = this.facts.activitiesAfter(
986
1007
  this.latestAcceptedActivityIndex,
987
1008
  job.activityIndex,
@@ -993,13 +1014,13 @@ class FooterTldrEngine {
993
1014
  {
994
1015
  type: "text",
995
1016
  text: [
996
- "Prior TLDRs (context only, do not copy phrasing):",
1017
+ "Prior progress updates (context only, do not copy phrasing):",
997
1018
  previousCheckpointLines(this.acceptedCheckpoints),
998
1019
  "",
999
1020
  "New activity to summarize:",
1000
1021
  ...rawActivities.map(formatRawActivity),
1001
1022
  "",
1002
- "Write the next TLDR.",
1023
+ "Write the next progress update.",
1003
1024
  ].join("\n"),
1004
1025
  },
1005
1026
  ],
@@ -1090,11 +1111,12 @@ const BANNED_FIRST_WORDS = [
1090
1111
  "Find",
1091
1112
  ] as const;
1092
1113
 
1093
- function checkpointSystemPrompt(job: TldrCheckpointJob): string {
1094
- // Tense + exemplars vary by priority. Final TLDRs describe completed turns;
1095
- // using -ing examples for final caused all final TLDRs to slip back into
1096
- // present-progressive even though the literal instruction said past tense.
1097
- // Immediate TLDRs cover a brand-new user request; they need a rephrase
1114
+ function checkpointSystemPrompt(job: ProgressCheckpointJob): string {
1115
+ // Tense + exemplars vary by priority. Final progress updates describe
1116
+ // completed turns; using -ing examples for final caused all of them to slip
1117
+ // back into present-progressive even though the literal instruction said
1118
+ // past tense.
1119
+ // Immediate progress updates cover a brand-new user request; they need a rephrase
1098
1120
  // directive instead of generic filler like "Continuing with task".
1099
1121
  let tenseInstruction: string;
1100
1122
  let goodExamples: string;
@@ -1103,7 +1125,7 @@ function checkpointSystemPrompt(job: TldrCheckpointJob): string {
1103
1125
  tenseInstruction = "Start with a past-tense verb describing what was completed.";
1104
1126
  goodExamples = [
1105
1127
  "- Updated footer summary behavior",
1106
- "- Investigated live TLDR regressions",
1128
+ "- Investigated live progress regressions",
1107
1129
  "- Refined sanitizer for stray prefixes",
1108
1130
  "- Wrapped up extension release",
1109
1131
  ].join("\n");
@@ -1113,7 +1135,7 @@ function checkpointSystemPrompt(job: TldrCheckpointJob): string {
1113
1135
  "Rephrase the user's new request as a concise present-progressive task clause. If the request is opaque (e.g. \"continue\", \"go\", \"ok\"), name the carry-over task with a noun (e.g. \"Resuming refactor work\"), not generic filler.";
1114
1136
  goodExamples = [
1115
1137
  "- Reviewing footer summary behavior",
1116
- "- Investigating live TLDR regressions",
1138
+ "- Investigating live progress regressions",
1117
1139
  "- Refining sanitizer for stray prefixes",
1118
1140
  "- Preparing extension release",
1119
1141
  "- Resuming refactor work",
@@ -1123,7 +1145,7 @@ function checkpointSystemPrompt(job: TldrCheckpointJob): string {
1123
1145
  tenseInstruction = "Start with a present-tense -ing verb describing current work.";
1124
1146
  goodExamples = [
1125
1147
  "- Reviewing footer summary behavior",
1126
- "- Investigating live TLDR regressions",
1148
+ "- Investigating live progress regressions",
1127
1149
  "- Refining sanitizer for stray prefixes",
1128
1150
  "- Wrapping up extension release",
1129
1151
  ].join("\n");
@@ -1136,7 +1158,7 @@ function checkpointSystemPrompt(job: TldrCheckpointJob): string {
1136
1158
  // suffixes, file path leaks, and markdown formatting. The HARD CONSTRAINTS
1137
1159
  // block sits at the tail of the prompt because instruction-following models
1138
1160
  // give the most weight to the most-recent directives (recency bias).
1139
- return `Write one plain-English TLDR for a Pi coding agent.
1161
+ return `Write one plain-English progress update for a Pi coding agent.
1140
1162
  Describe the work progress as if a human developer were doing it.
1141
1163
  Focus on the task activity and current outcome, not agent mechanics.
1142
1164
  Do not mention tools, tool calls, prompts, messages, model output, or implementation details.
@@ -1145,11 +1167,11 @@ Do not use file paths, file extensions, code identifiers, package names, or vers
1145
1167
  Do not use backticks, asterisks, underscores, quotes, or any markdown formatting.
1146
1168
  Do not append filler suffixes such as "with success", "successfully", or "completed successfully".
1147
1169
  Do not claim progress or completion that is not present in the activity.
1148
- Use the prior TLDRs for context and the new activity for the update.
1170
+ Use the prior progress updates for context and the new activity for the update.
1149
1171
  Summarize the current state of work; do not narrate the history.
1150
1172
  If context is sparse, still summarize the available activity.
1151
1173
  Never ask for more information or say there is not enough context.
1152
- Return one concise status fragment under ${TLDR_TARGET_SUMMARY_CHARS} characters.
1174
+ Return one concise status fragment under ${PROGRESS_TARGET_SUMMARY_CHARS} characters.
1153
1175
  Omit subjects like "the agent" or "it".
1154
1176
  Prefer verb + direct object. Include outcome only if important.
1155
1177
  Do not address the user.
@@ -1163,7 +1185,7 @@ Bad examples:
1163
1185
  - Editing extensions/status-footer.ts with success.
1164
1186
  - Reading status-footer file completed successfully.
1165
1187
  - Publishing \`pi-bar@0.3.3\` to npm.
1166
- - Grepping for sanitizeTldrText callers.
1188
+ - Grepping for sanitizeProgressText callers.
1167
1189
  - Verifying repository status after commit.
1168
1190
  - Investigating user input responses.
1169
1191
 
@@ -1180,7 +1202,7 @@ function extractStopReason(message: unknown): string | undefined {
1180
1202
  return typeof stopReason === "string" ? stopReason : undefined;
1181
1203
  }
1182
1204
 
1183
- // Two TLDRs are near-duplicates if their normalized action fragments (lowercase,
1205
+ // Two progress updates are near-duplicates if their normalized action fragments (lowercase,
1184
1206
  // punctuation/whitespace collapsed) match. Catches bursts of cosmetic variants
1185
1207
  // like "Editing X." vs "Editing X" vs "editing x.".
1186
1208
  function normalizedActionFragment(text: string): string {
@@ -1191,25 +1213,25 @@ function normalizedActionFragment(text: string): string {
1191
1213
  .trim();
1192
1214
  }
1193
1215
 
1194
- function isNearDuplicateTldr(current: string, previous: string): boolean {
1216
+ function isNearDuplicateProgress(current: string, previous: string): boolean {
1195
1217
  if (!previous) return false;
1196
1218
  return normalizedActionFragment(current) === normalizedActionFragment(previous);
1197
1219
  }
1198
1220
 
1199
- function previousCheckpointLines(checkpoints: readonly TldrCheckpoint[]): string {
1221
+ function previousCheckpointLines(checkpoints: readonly ProgressCheckpoint[]): string {
1200
1222
  if (checkpoints.length === 0) return "none";
1201
1223
  return checkpoints
1202
1224
  .slice(-MAX_CONTEXT_CHECKPOINTS)
1203
- .map((checkpoint) => `- ${sanitizeTldrText(checkpoint.text)}`)
1225
+ .map((checkpoint) => `- ${sanitizeProgressText(checkpoint.text)}`)
1204
1226
  .join("\n");
1205
1227
  }
1206
1228
 
1207
- function formatRawActivity(activity: TldrActivity): string {
1229
+ function formatRawActivity(activity: ProgressActivity): string {
1208
1230
  return `- ${activity.text}`;
1209
1231
  }
1210
1232
 
1211
1233
  // Strips ANSI/CSI/OSC/DCS/SOS/PM/APC escape sequences from model output before
1212
- // it lands in the terminal. The TLDR text is model-controlled and renders into
1234
+ // it lands in the terminal. The progress update text is model-controlled and renders into
1213
1235
  // the footer; the system prompt asking for plain text is not a security
1214
1236
  // boundary, so we defensively remove terminal controls here.
1215
1237
  const ESC_BYTE = 0x1b;
@@ -1315,7 +1337,7 @@ function stripTerminalControls(text: string): string {
1315
1337
  // parrot them; the regex is narrow enough not to clip natural starts like
1316
1338
  // "Activity slowed after retry" or "Checkpointing release state".
1317
1339
  const LEAKED_PREFIX_PATTERN =
1318
- /^\s*(?:[-*•]\s*)?(?:(?:through\s+activity|activity|checkpoint)\s+\d+\s*[:.\-—–]\s*|(?:tldr|summary)\s*[:.\-—–]\s*)+/i;
1340
+ /^\s*(?:[-*•]\s*)?(?:(?:through\s+activity|activity|checkpoint)\s+\d+\s*[:.\-—–]\s*|(?:tldr|summary|progress\s+update|progress)\s*[:.\-—–]\s*)+/i;
1319
1341
  const LEADING_PUNCT_PATTERN = /^[\s\-—–•*:#.,;]+/;
1320
1342
  const TRAILING_PUNCT_PATTERN = /[\s\-—–•*:#.,;]+$/;
1321
1343
 
@@ -1369,6 +1391,15 @@ const DANGLING_PREP_CHAIN_PATTERN =
1369
1391
  // "Reviewing for image update". Collapse the bare verb+prep into just the verb.
1370
1392
  const VERB_BARE_PREP_PATTERN =
1371
1393
  /\b(Reviewing|Investigating|Updating|Refining|Exploring|Fixing|Implementing|Bumping|Releasing|Preparing|Drafting|Resuming|Pulling|Surveying|Recording)\s+(?:for|in|on|with|after|before|to|at|as|of|by|from)\s+/gi;
1394
+ // Identifier strip removes version numbers like "0.3.4" but can leave stranded
1395
+ // release adjectives behind: "Released latest of package" was "Released latest
1396
+ // 0.3.4 of package"; "Released new with X" was "Released new 0.3.4 with X".
1397
+ // The chain pattern keeps the noun phrase that followed the version. The
1398
+ // trailing pattern handles bare "... new" or "... latest" tails.
1399
+ const RELEASE_FRAGMENT_CHAIN_PATTERN =
1400
+ /\b(Released|Releasing|Bumped|Bumping|Published|Publishing|Updated|Updating|Shipped|Shipping)\s+(?:new|latest|version|update)\s+(?:of|with|to|and|in|on|for)\s+/gi;
1401
+ const RELEASE_FRAGMENT_TRAILING_PATTERN =
1402
+ /\s+(?:new|latest|version|update)\s*(?:of|with|to|and|in|on)?\s*[.!?,;:]?\s*$/i;
1372
1403
 
1373
1404
  function stripDanglingPrepositions(text: string): string {
1374
1405
  let cleaned = text;
@@ -1376,14 +1407,16 @@ function stripDanglingPrepositions(text: string): string {
1376
1407
  do {
1377
1408
  previous = cleaned;
1378
1409
  cleaned = cleaned.replace(VERB_BARE_PREP_PATTERN, "$1 ");
1410
+ cleaned = cleaned.replace(RELEASE_FRAGMENT_CHAIN_PATTERN, "$1 ");
1379
1411
  cleaned = cleaned.replace(DANGLING_PREP_CHAIN_PATTERN, "$2");
1412
+ cleaned = cleaned.replace(RELEASE_FRAGMENT_TRAILING_PATTERN, "").trim();
1380
1413
  cleaned = cleaned.replace(DANGLING_TRAILING_PREP_PATTERN, "").trim();
1381
1414
  } while (cleaned !== previous && cleaned.length > 0);
1382
1415
  return cleaned;
1383
1416
  }
1384
1417
 
1385
1418
  // Last-resort fallback when the model ignored the allow-list and started the
1386
- // TLDR with a banned tool-narration verb. The map preserves tense (present
1419
+ // progress update with a banned tool-narration verb. The map preserves tense (present
1387
1420
  // vs past). Case of the first letter follows the original word.
1388
1421
  const BANNED_FIRST_WORD_REWRITES: Record<string, string> = {
1389
1422
  Reading: "Reviewing",
@@ -1437,7 +1470,7 @@ function rewriteBannedFirstWord(text: string): string {
1437
1470
  const original = match[1];
1438
1471
  const replacement = BANNED_FIRST_WORD_REWRITES[original];
1439
1472
  if (!replacement) return text;
1440
- // Preserve lowercase-start TLDRs (rare but possible) by matching original case.
1473
+ // Preserve lowercase-start outputs (rare but possible) by matching original case.
1441
1474
  const cased =
1442
1475
  original[0] === original[0].toLowerCase()
1443
1476
  ? replacement[0].toLowerCase() + replacement.slice(1)
@@ -1466,9 +1499,9 @@ function stripSuccessSuffix(text: string): string {
1466
1499
  return cleaned;
1467
1500
  }
1468
1501
 
1469
- function sanitizeTldrText(
1502
+ function sanitizeProgressText(
1470
1503
  text: string,
1471
- maxChars = MAX_SAFE_TLDR_CHARS,
1504
+ maxChars = MAX_SAFE_PROGRESS_CHARS,
1472
1505
  ): string {
1473
1506
  const stripped = stripTerminalControls(text);
1474
1507
  const withoutMarkdown = stripMarkdownFormatting(stripped);
@@ -1576,7 +1609,7 @@ export default function (pi: ExtensionAPI) {
1576
1609
  let statusFilter: StatusFilter = { mode: "all", hidden: new Set() };
1577
1610
  const seenStatusKeys = new Set<string>();
1578
1611
  const refresh = () => requestRender?.();
1579
- const tldr = new FooterTldrEngine(refresh);
1612
+ const progress = new FooterProgressEngine(refresh);
1580
1613
  const restoreStatusFilter = (ctx: ExtensionContext) => {
1581
1614
  let restoredFilter = readGlobalStatusFilter();
1582
1615
  if (!restoredFilter) {
@@ -1764,32 +1797,32 @@ export default function (pi: ExtensionAPI) {
1764
1797
  pi.on("thinking_level_select", async () => refresh());
1765
1798
  pi.on("turn_end", async () => refresh());
1766
1799
  pi.on("before_agent_start", async (event, ctx) => {
1767
- tldr.recordUserMessage(ctx, event.prompt);
1800
+ progress.recordUserMessage(ctx, event.prompt);
1768
1801
  });
1769
1802
  pi.on("message_update", async (event, ctx) => {
1770
- tldr.recordAssistantUpdate(ctx, event.message);
1803
+ progress.recordAssistantUpdate(ctx, event.message);
1771
1804
  });
1772
1805
  pi.on("tool_call", async (event, ctx) => {
1773
- tldr.recordToolCall(ctx, event);
1806
+ progress.recordToolCall(ctx, event);
1774
1807
  });
1775
1808
  pi.on("tool_result", async (event, ctx) => {
1776
- tldr.recordToolResult(ctx, event);
1809
+ progress.recordToolResult(ctx, event);
1777
1810
  });
1778
1811
  pi.on("message_end", async (event, ctx) => {
1779
- tldr.recordMessageEnd(ctx, event.message);
1812
+ progress.recordMessageEnd(ctx, event.message);
1780
1813
  refresh();
1781
1814
  });
1782
1815
  pi.on("session_before_tree", async () => {
1783
- tldr.shutdown();
1816
+ progress.shutdown();
1784
1817
  });
1785
1818
  pi.on("session_tree", async (_event, ctx) => {
1786
- tldr.startSession(ctx.cwd);
1819
+ progress.startSession(ctx.cwd);
1787
1820
  restoreStatusFilter(ctx);
1788
1821
  refresh();
1789
1822
  });
1790
1823
 
1791
1824
  pi.on("session_start", async (_event, ctx) => {
1792
- tldr.startSession(ctx.cwd);
1825
+ progress.startSession(ctx.cwd);
1793
1826
  restoreStatusFilter(ctx);
1794
1827
 
1795
1828
  if (!ctx.hasUI) return;
@@ -1823,13 +1856,13 @@ export default function (pi: ExtensionAPI) {
1823
1856
  const contextText = usage
1824
1857
  ? `${usage.percent !== null ? `${usage.percent.toFixed(1)}%` : "—%"} / ${formatTokens(usage.contextWindow)}`
1825
1858
  : "—";
1826
- const tldrText = tldr.text();
1859
+ const progressText = progress.text();
1827
1860
 
1828
1861
  const segmentRenderers: Record<SegmentName, string | null> = {
1829
1862
  model: theme.fg("accent", modelName),
1830
1863
  thinking: theme.fg(thinkingColor(thinkingLevel), `think:${thinkingLevel}`),
1831
1864
  context: theme.fg(contextSegmentColor, contextText),
1832
- tldr: tldrText ? theme.fg("muted", tldrText) : null,
1865
+ progress: progressText ? theme.fg("muted", progressText) : null,
1833
1866
  extensions: extensionStatuses ? theme.fg("muted", extensionStatuses) : null,
1834
1867
  };
1835
1868
 
@@ -1846,7 +1879,7 @@ export default function (pi: ExtensionAPI) {
1846
1879
  });
1847
1880
 
1848
1881
  pi.on("session_shutdown", async (_event, ctx) => {
1849
- tldr.shutdown();
1882
+ progress.shutdown();
1850
1883
  if (ctx.hasUI) ctx.ui.setFooter(undefined);
1851
1884
  });
1852
1885
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-bar",
3
- "version": "0.3.29",
4
- "description": "Pi footer/statusline showing active model, thinking level, context pressure, live TLDR, and extension statuses.",
3
+ "version": "0.3.31",
4
+ "description": "Pi footer/statusline showing active model, thinking level, context pressure, live progress updates, and extension statuses.",
5
5
  "type": "module",
6
6
  "keywords": [
7
7
  "pi-package",
@@ -14,6 +14,7 @@
14
14
  "thinking",
15
15
  "context",
16
16
  "context-window",
17
+ "progress",
17
18
  "tldr"
18
19
  ],
19
20
  "license": "MIT",
@@ -51,6 +52,6 @@
51
52
  "extensions": [
52
53
  "./extensions/status-footer.ts"
53
54
  ],
54
- "image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.29/assets/screenshot-yellow.png"
55
+ "image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.31/assets/screenshot-yellow.png"
55
56
  }
56
57
  }