pi-bar 0.3.19 → 0.3.20

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
@@ -9,7 +9,7 @@
9
9
  claude-opus-4.7 ❯ think:med ❯ 2.6% / 1.0M ❯ Inspecting package structure ❯ plan:active
10
10
  ```
11
11
 
12
- ![pi-bar screenshot, three rows showing green, yellow, and red context usage](https://cdn.jsdelivr.net/npm/pi-bar@0.3.19/assets/screenshot.png)
12
+ ![pi-bar screenshot, three rows showing green, yellow, and red context usage](https://cdn.jsdelivr.net/npm/pi-bar@0.3.20/assets/screenshot.png)
13
13
 
14
14
  ## Why use it?
15
15
 
@@ -53,6 +53,7 @@ type TldrActivity = {
53
53
  activityType: TldrActivityType;
54
54
  displayPriority: TldrDisplayPriority;
55
55
  text: string;
56
+ toolCallId?: string;
56
57
  };
57
58
  type TldrCheckpoint = {
58
59
  activityIndex: number;
@@ -365,12 +366,18 @@ function usefulLineCount(text: string): number {
365
366
  .filter(Boolean).length;
366
367
  }
367
368
 
369
+ const ERROR_LINE_PATTERN = /(error|failed|TS\d+|E[A-Z]+\d*|Command exited|denied|refused|aborted|panic|fatal|exception)/i;
370
+
368
371
  function errorSummary(text: string | undefined): string | undefined {
369
372
  if (!text) return undefined;
370
- const compacted = compactText(text);
371
- const exitMatch = compacted.match(/Command exited with code \d+/);
372
- if (exitMatch) return truncateText(exitMatch[0], MAX_TOOL_RESULT_ERROR_CHARS);
373
- return truncateText(compacted, MAX_TOOL_RESULT_ERROR_CHARS);
373
+ const cleaned = text.replace(ANSI_PATTERN, "");
374
+ const lines = cleaned
375
+ .split("\n")
376
+ .map((line) => line.trim())
377
+ .filter(Boolean);
378
+ if (lines.length === 0) return undefined;
379
+ const matched = lines.find((line) => ERROR_LINE_PATTERN.test(line)) ?? lines[lines.length - 1];
380
+ return truncateText(compactText(matched), MAX_TOOL_RESULT_ERROR_CHARS);
374
381
  }
375
382
 
376
383
  function summarizeToolResult(
@@ -442,20 +449,32 @@ class TldrFactCollector {
442
449
  const record = message as Record<string, unknown>;
443
450
  if (record.role !== "assistant") return undefined;
444
451
  const text = extractTextContent(record.content as readonly unknown[] | undefined);
445
- return text
446
- ? this.addActivity(
447
- "assistant_update",
448
- "normal",
449
- `assistant: ${tailText(text, MAX_ASSISTANT_UPDATE_CHARS)}`,
450
- )
451
- : undefined;
452
+ if (!text) return undefined;
453
+
454
+ // Streaming emits many partial updates per message. Keep only the latest by
455
+ // dropping the previous assistant_update so the model sees one accurate snapshot.
456
+ const last = this.activities[this.activities.length - 1];
457
+ if (last?.activityType === "assistant_update") {
458
+ this.activities.pop();
459
+ }
460
+
461
+ return this.addActivity(
462
+ "assistant_update",
463
+ "normal",
464
+ `assistant: ${tailText(text, MAX_ASSISTANT_UPDATE_CHARS)}`,
465
+ );
452
466
  }
453
467
 
454
- recordToolCall(event: { toolName: string; input?: Record<string, unknown> }): TldrActivity {
468
+ recordToolCall(event: {
469
+ toolName: string;
470
+ input?: Record<string, unknown>;
471
+ toolCallId?: string;
472
+ }): TldrActivity {
455
473
  return this.addActivity(
456
474
  "tool_call",
457
475
  "normal",
458
476
  `tool: ${compactToolInput(event.toolName, event.input)}`,
477
+ event.toolCallId,
459
478
  );
460
479
  }
461
480
 
@@ -465,6 +484,7 @@ class TldrFactCollector {
465
484
  isError?: boolean;
466
485
  content?: readonly unknown[];
467
486
  details?: unknown;
487
+ toolCallId?: string;
468
488
  }): TldrActivity {
469
489
  const isError = Boolean(event.isError);
470
490
  const resultText = summarizeToolResult(
@@ -475,12 +495,25 @@ class TldrFactCollector {
475
495
  );
476
496
  const status = isError ? "error" : "ok";
477
497
  const tool = compactToolInput(event.toolName, event.input);
498
+
499
+ // Result fact subsumes the prior tool_call fact for the same call. Drop the
500
+ // matching tool_call so the model sees one combined fact instead of two.
501
+ if (event.toolCallId) {
502
+ const idx = this.activities.findIndex(
503
+ (activity) =>
504
+ activity.activityType === "tool_call" &&
505
+ activity.toolCallId === event.toolCallId,
506
+ );
507
+ if (idx !== -1) this.activities.splice(idx, 1);
508
+ }
509
+
478
510
  return this.addActivity(
479
511
  "tool_result",
480
512
  "normal",
481
513
  resultText
482
514
  ? `result: ${tool} ${status}; ${resultText}`
483
515
  : `result: ${tool} ${status}`,
516
+ event.toolCallId,
484
517
  );
485
518
  }
486
519
 
@@ -538,12 +571,14 @@ class TldrFactCollector {
538
571
  activityType: TldrActivityType,
539
572
  displayPriority: TldrDisplayPriority,
540
573
  text: string,
574
+ toolCallId?: string,
541
575
  ): TldrActivity {
542
576
  const activity = {
543
577
  index: this.nextIndex,
544
578
  activityType,
545
579
  displayPriority,
546
580
  text: truncateText(text, MAX_ACTIVITY_TEXT_CHARS),
581
+ toolCallId,
547
582
  };
548
583
  this.nextIndex++;
549
584
  this.activities.push(activity);
@@ -600,7 +635,7 @@ class FooterTldrEngine {
600
635
 
601
636
  recordToolCall(
602
637
  ctx: ExtensionContext,
603
- event: { toolName: string; input?: Record<string, unknown> },
638
+ event: { toolName: string; input?: Record<string, unknown>; toolCallId?: string },
604
639
  ): void {
605
640
  this.enqueue(ctx, this.facts.recordToolCall(event));
606
641
  }
@@ -613,6 +648,7 @@ class FooterTldrEngine {
613
648
  isError?: boolean;
614
649
  content?: readonly unknown[];
615
650
  details?: unknown;
651
+ toolCallId?: string;
616
652
  },
617
653
  ): void {
618
654
  this.enqueue(ctx, this.facts.recordToolResult(event));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-bar",
3
- "version": "0.3.19",
3
+ "version": "0.3.20",
4
4
  "description": "Pi footer/statusline showing active model, thinking level, context pressure, live TLDR, and extension statuses.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -51,6 +51,6 @@
51
51
  "extensions": [
52
52
  "./extensions/status-footer.ts"
53
53
  ],
54
- "image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.19/assets/screenshot.png"
54
+ "image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.20/assets/screenshot.png"
55
55
  }
56
56
  }