@narumitw/pi-statusline 0.1.17 → 0.1.18

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,8 +10,9 @@ 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 icon statuses from other extensions, such as goal mode and LSP readiness.
14
- - Shows active subagent count and execution mode while `pi-subagents` is running.
13
+ - Displays compact statuses from other extensions without knowing about specific packages.
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.
15
16
  - Warns when the same extension package is installed from multiple sources.
16
17
  - Uses emoji-labeled segments for readability.
17
18
  - Adapts to terminal width and truncates safely.
@@ -50,15 +51,16 @@ The default statusline includes:
50
51
  - 💰 estimated cost.
51
52
  - 🕒 clock.
52
53
 
53
- Statuses from other extensions, such as goal mode, appear on their own compact icon line below the main statusline and are separated with ``.
54
+ Statuses from other extensions appear on their own compact line below the main statusline and are separated with ``.
55
+
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.
54
57
 
55
58
  Examples:
56
59
 
57
- - `🎯 active` for goal mode.
58
- - `💊 awake` while pi-caffeinate is preventing sleep.
59
- - `🧬 ✓` for Biome LSP readiness.
60
- - `🐍 ty ✓ ruff ✓` for Python LSP readiness.
61
- - `🧑‍🤝‍🧑 2 parallel` while subagents are active.
60
+ - `🔌 active` for a plain status such as `goal: active`.
61
+ - `🎯 active` when the producing extension sets `🎯 active`.
62
+ - `🐍 ty ✓ ruff ✓` when the producing extension sets a Python status with a leading icon.
63
+ - `🧑‍🤝‍🧑 2 parallel` while subagent tool calls are active.
62
64
  - `⚠️ dup biome-lsp` when local and npm installs register the same extension.
63
65
 
64
66
  ## 🧠 Use cases
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-statusline",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
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
@@ -423,10 +423,11 @@ function formatExtensionStatuses(
423
423
  }
424
424
 
425
425
  function formatExtensionStatus(key: string, value: string, theme: Theme): string {
426
- const text = truncateToWidth(simplifyExtensionStatus(key, value), 22, "…");
426
+ const status = splitExtensionStatusIcon(stripExtensionStatusPrefix(key, value));
427
+ const text = truncateToWidth(simplifyExtensionStatusText(status.text), 22, "…");
427
428
  const color = extensionColor(key, value);
428
- const textColor = key.toLowerCase().includes("codex") ? color : "muted";
429
- return `${theme.fg(color, extensionIcon(key))} ${theme.fg(textColor, text)}`;
429
+ const textColor = color === "warning" ? "warning" : "muted";
430
+ return `${theme.fg(color, status.icon)} ${theme.fg(textColor, text)}`;
430
431
  }
431
432
 
432
433
  function formatSubagentStatus(runtime: RuntimeState, theme: Theme): string[] {
@@ -446,20 +447,19 @@ function formatDuplicateExtensionStatus(runtime: RuntimeState, theme: Theme): st
446
447
  return [`${theme.fg("warning", "⚠️")} ${theme.fg("warning", `dup ${names}${suffix}`)}`];
447
448
  }
448
449
 
449
- function extensionIcon(key: string): string {
450
- const normalizedKey = key.toLowerCase();
451
- if (normalizedKey.includes("biome")) return "🧬";
452
- if (normalizedKey.includes("python") || normalizedKey.includes("ruff") || normalizedKey.includes("ty"))
453
- return "🐍";
454
- if (normalizedKey.includes("subagent")) return "🧑‍🤝‍🧑";
455
- if (normalizedKey.includes("caffeinate")) return "💊";
456
- if (normalizedKey.includes("chrome") || normalizedKey.includes("devtools") || normalizedKey === "cdp")
457
- return "🌐";
458
- if (normalizedKey.includes("codex")) return "📊";
459
- if (normalizedKey.includes("firecrawl")) return "🔥";
460
- if (normalizedKey.includes("goal")) return "🎯";
461
- if (normalizedKey.includes("retry")) return "🔁";
462
- return "🔌";
450
+ function splitExtensionStatusIcon(value: string): { icon: string; text: string } {
451
+ const trimmed = value.trim();
452
+ const [firstToken, ...restTokens] = trimmed.split(/\s+/);
453
+ if (firstToken && isEmojiOnlyToken(firstToken)) {
454
+ return { icon: firstToken, text: restTokens.join(" ") };
455
+ }
456
+ return { icon: "🔌", text: trimmed };
457
+ }
458
+
459
+ function isEmojiOnlyToken(value: string): boolean {
460
+ return /^(?=.*(?:\p{Extended_Pictographic}|\p{Regional_Indicator}|[0-9#*]\ufe0f?\u20e3))(?:\p{Extended_Pictographic}|\p{Emoji_Modifier}|\p{Regional_Indicator}|\u200d|\ufe0f|[0-9#*]\ufe0f?\u20e3)+$/u.test(
461
+ value,
462
+ );
463
463
  }
464
464
 
465
465
  function extensionColor(key: string, value: string): ThemeColor {
@@ -470,12 +470,13 @@ function extensionColor(key: string, value: string): ThemeColor {
470
470
  return "muted";
471
471
  }
472
472
 
473
- function simplifyExtensionStatus(key: string, value: string): string {
473
+ function stripExtensionStatusPrefix(key: string, value: string): string {
474
+ return value.trim().replace(new RegExp(`^${escapeRegExp(key)}\\s*:\\s*`, "iu"), "");
475
+ }
476
+
477
+ function simplifyExtensionStatusText(value: string): string {
474
478
  return value
475
479
  .trim()
476
- .replace(new RegExp(`^${escapeRegExp(key)}\\s*:\\s*`, "iu"), "")
477
- .replace(/^python-lsp\s*:\s*/iu, "")
478
- .replace(/^biome-lsp\s*:\s*/iu, "")
479
480
  .replace(/\bready\b/giu, "✓")
480
481
  .replace(/\bmissing\b/giu, "✗")
481
482
  .replace(/,\s*/g, " ")