@narumitw/pi-statusline 0.1.21 โ†’ 0.1.22

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
@@ -10,9 +10,8 @@ Use it to monitor model selection, thinking level, git branch, working directory
10
10
 
11
11
  - Replaces the default Pi footer with a compact rich statusline.
12
12
  - Shows model, thinking level, git branch, project directory, active tool, context usage, tokens, cost, and clock.
13
- - Displays compact statuses from other extensions without knowing about specific packages.
13
+ - Displays compact statuses published through Pi's generic extension status API.
14
14
  - Preserves extension-provided status icons when the status text starts with one.
15
- - Shows active subagent count and execution mode while subagent tools are running.
16
15
  - Warns when the same extension package is installed from multiple sources.
17
16
  - Uses emoji-labeled segments for readability.
18
17
  - Adapts to terminal width and truncates safely.
@@ -53,14 +52,14 @@ The default statusline includes:
53
52
 
54
53
  Statuses from other extensions appear on their own compact line below the main statusline and are separated with `๎‚ฑ`.
55
54
 
56
- `pi-statusline` is extension-agnostic: it does not map package names to icons. If an extension wants a custom icon, it should include that icon at the start of its status text, for example `ctx.ui.setStatus("goal", "๐ŸŽฏ active")`. Statuses without a leading icon use the generic `๐Ÿ”Œ` icon.
55
+ `pi-statusline` is extension-agnostic: it consumes Pi's generic extension status API and does not import or depend on status-producing extensions. If an extension wants a custom icon, it should include that icon at the start of its status text, for example `ctx.ui.setStatus("goal", "๐ŸŽฏ active")`. Statuses without a leading icon use the generic `๐Ÿ”Œ` icon.
57
56
 
58
57
  Examples:
59
58
 
60
59
  - `๐Ÿ”Œ active` for a plain status such as `goal: active`.
61
60
  - `๐ŸŽฏ active` when the producing extension sets `๐ŸŽฏ active`.
62
61
  - `๐Ÿ ty โœ“ ruff โœ“` when the producing extension sets a Python status with a leading icon.
63
- - `๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ 2 parallel` while subagent tool calls are active.
62
+ - `๐Ÿงช running` when any extension publishes an activity status with its own icon.
64
63
  - `โš ๏ธ dup biome-lsp` when local and npm installs register the same extension.
65
64
 
66
65
  ## ๐Ÿง  Use cases
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-statusline",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "Pi extension that replaces the footer with an information-rich statusline.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/statusline.ts CHANGED
@@ -40,7 +40,6 @@ interface StatuslineConfig {
40
40
  interface RuntimeState {
41
41
  turnCount: number;
42
42
  activeTools: Map<string, number>;
43
- activeSubagents: Map<string, SubagentActivity>;
44
43
  lastTool?: string;
45
44
  lastCompletedTool?: string;
46
45
  isStreaming: boolean;
@@ -49,11 +48,6 @@ interface RuntimeState {
49
48
  requestRender?: () => void;
50
49
  }
51
50
 
52
- interface SubagentActivity {
53
- mode: string;
54
- count: number;
55
- }
56
-
57
51
  interface TokenTotals {
58
52
  input: number;
59
53
  output: number;
@@ -98,7 +92,6 @@ export default function statusline(pi: ExtensionAPI) {
98
92
  const runtime: RuntimeState = {
99
93
  turnCount: 0,
100
94
  activeTools: new Map(),
101
- activeSubagents: new Map(),
102
95
  isStreaming: false,
103
96
  thinkingLevel: "off",
104
97
  duplicateExtensions: [],
@@ -175,9 +168,6 @@ export default function statusline(pi: ExtensionAPI) {
175
168
  pi.on("tool_execution_start", (event) => {
176
169
  const currentCount = runtime.activeTools.get(event.toolName) ?? 0;
177
170
  runtime.activeTools.set(event.toolName, currentCount + 1);
178
- if (event.toolName === "subagent") {
179
- runtime.activeSubagents.set(event.toolCallId, parseSubagentActivity(event.args));
180
- }
181
171
  runtime.lastTool = event.toolName;
182
172
  refresh();
183
173
  });
@@ -186,7 +176,6 @@ export default function statusline(pi: ExtensionAPI) {
186
176
  const currentCount = runtime.activeTools.get(event.toolName) ?? 0;
187
177
  if (currentCount <= 1) runtime.activeTools.delete(event.toolName);
188
178
  else runtime.activeTools.set(event.toolName, currentCount - 1);
189
- if (event.toolName === "subagent") runtime.activeSubagents.delete(event.toolCallId);
190
179
 
191
180
  runtime.lastCompletedTool = event.toolName;
192
181
  refresh();
@@ -412,7 +401,6 @@ function formatExtensionStatuses(
412
401
  ): string {
413
402
  const separator = theme.fg("dim", " ๎‚ฑ ");
414
403
  const visibleStatuses = [
415
- ...formatSubagentStatus(runtime, theme),
416
404
  ...formatDuplicateExtensionStatus(runtime, theme),
417
405
  ...[...statuses.entries()]
418
406
  .filter(([key, value]) => key !== STATUSLINE_KEY && value.trim().length > 0)
@@ -430,16 +418,6 @@ function formatExtensionStatus(key: string, value: string, theme: Theme): string
430
418
  return `${theme.fg(color, status.icon)} ${theme.fg(textColor, text)}`;
431
419
  }
432
420
 
433
- function formatSubagentStatus(runtime: RuntimeState, theme: Theme): string[] {
434
- const activities = [...runtime.activeSubagents.values()];
435
- if (activities.length === 0) return [];
436
-
437
- const total = activities.reduce((sum, activity) => sum + activity.count, 0);
438
- const mode = activities[0]?.mode ?? "active";
439
- const suffix = activities.length > 1 ? ` +${activities.length - 1}` : "";
440
- return [`${theme.fg("accent", "๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘")} ${theme.fg("muted", `${total} ${mode}${suffix}`)}`];
441
- }
442
-
443
421
  function formatDuplicateExtensionStatus(runtime: RuntimeState, theme: Theme): string[] {
444
422
  if (runtime.duplicateExtensions.length === 0) return [];
445
423
  const names = runtime.duplicateExtensions.slice(0, 2).join(", ");
@@ -488,18 +466,6 @@ function escapeRegExp(value: string): string {
488
466
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
489
467
  }
490
468
 
491
- function parseSubagentActivity(args: unknown): SubagentActivity {
492
- const input = args && typeof args === "object" ? (args as Record<string, unknown>) : {};
493
- const tasks = Array.isArray(input.tasks) ? input.tasks.length : 0;
494
- const chain = Array.isArray(input.chain) ? input.chain.length : 0;
495
- const hasAggregator = input.aggregator !== undefined;
496
-
497
- if (chain > 0) return { mode: "chain", count: chain };
498
- if (tasks > 0 && hasAggregator) return { mode: "fan-in", count: tasks };
499
- if (tasks > 0) return { mode: "parallel", count: tasks };
500
- return { mode: "single", count: 1 };
501
- }
502
-
503
469
  function findDuplicateExtensions(cwd: string): string[] {
504
470
  const settingsFiles = [
505
471
  join(process.env.HOME ?? "", ".pi", "agent", "settings.json"),