pi-claude-code-ui 1.0.91 → 1.0.93

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.
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import net from "node:net";
3
3
  import { homedir } from "node:os";
4
- import { join } from "node:path";
4
+ import { basename, join } from "node:path";
5
5
 
6
6
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
7
7
  import { Loader } from "@earendil-works/pi-tui";
@@ -476,6 +476,61 @@ function pickVerb(): string {
476
476
  }
477
477
 
478
478
  /** Format elapsed ms as compact duration: 5s, 1m 23s, 1h 2m 3s */
479
+ // What a running tool is doing, for the working line ("Running npm test…",
480
+ // "Reading stats.py…"). Random verbs stay for the time the model is thinking.
481
+ function clip(text: string, max: number): string {
482
+ const oneLine = text.split("\n")[0]?.trim() ?? "";
483
+ return oneLine.length > max ? `${oneLine.slice(0, max - 1)}…` : oneLine;
484
+ }
485
+
486
+ function humanizeToolName(name: string): string {
487
+ const direct = /^mcp__.+?__(.+)$/.exec(name);
488
+ return (direct ? direct[1] : name)
489
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
490
+ .replace(/[_-]+/g, " ")
491
+ .replace(/\b\w/g, (char) => char.toUpperCase());
492
+ }
493
+
494
+ export function describeToolActivity(name: string, args: any): string {
495
+ const str = (...keys: string[]): string => {
496
+ for (const key of keys) {
497
+ const value = args?.[key];
498
+ if (typeof value === "string" && value.trim()) return value.trim();
499
+ }
500
+ return "";
501
+ };
502
+ const file = basename(str("path", "file_path"));
503
+ switch (name) {
504
+ case "bash": {
505
+ const command = clip(str("command"), 50);
506
+ return command ? `Running ${command}` : "Running a shell command";
507
+ }
508
+ case "read":
509
+ return file ? `Reading ${file}` : "Reading a file";
510
+ case "edit":
511
+ return file ? `Editing ${file}` : "Editing a file";
512
+ case "write":
513
+ return file ? `Writing ${file}` : "Writing a file";
514
+ case "grep":
515
+ case "find": {
516
+ const pattern = clip(str("pattern"), 40);
517
+ return pattern ? `Searching for ${pattern}` : "Searching";
518
+ }
519
+ case "ls":
520
+ return `Listing ${str("path") || "."}`;
521
+ case "Agent": {
522
+ const description = clip(str("description"), 50);
523
+ return description ? `Waiting for agent: ${description}` : "Waiting for an agent";
524
+ }
525
+ case "ask_user_question":
526
+ case "questionnaire":
527
+ case "question":
528
+ return "Waiting for your answer";
529
+ default:
530
+ return `Running ${humanizeToolName(name)}`;
531
+ }
532
+ }
533
+
479
534
  function formatDuration(ms: number): string {
480
535
  const totalSec = Math.floor(ms / 1000);
481
536
  const h = Math.floor(totalSec / 3600);
@@ -730,6 +785,8 @@ export default function (pi: ExtensionAPI) {
730
785
  let turnActive = false;
731
786
  let lastWorkingMessage: string | null = null;
732
787
  let activeCtx: { ui: any; hasUI: boolean } | null = null;
788
+ // Tools running right now, in start order; the latest one names the working line.
789
+ const runningTools = new Map<string, { name: string; args: unknown }>();
733
790
  // Root interactive session only — nested/non-UI contexts must not fight
734
791
  // herdr state authority for this pane.
735
792
  let herdrRootSession = false;
@@ -763,7 +820,12 @@ export default function (pi: ExtensionAPI) {
763
820
  statusParts.push(formatDuration(elapsed));
764
821
  }
765
822
 
766
- let message = `${CLAUDE_ORANGE}${currentVerb}…${RESET}`;
823
+ const running = [...runningTools.values()];
824
+ const latest = running[running.length - 1];
825
+ const verb = latest
826
+ ? `${describeToolActivity(latest.name, latest.args)}${running.length > 1 ? ` (+${running.length - 1} more)` : ""}`
827
+ : currentVerb;
828
+ let message = `${CLAUDE_ORANGE}${verb}…${RESET}`;
767
829
  if (statusParts.length > 0) {
768
830
  message += statusText(` (${statusParts.join(" · ")})`);
769
831
  }
@@ -1025,6 +1087,19 @@ export default function (pi: ExtensionAPI) {
1025
1087
  }
1026
1088
  });
1027
1089
 
1090
+ pi.on("tool_execution_start", async (event: any, ctx) => {
1091
+ if (typeof event?.toolCallId !== "string") return;
1092
+ activeCtx = ctx;
1093
+ runningTools.set(event.toolCallId, { name: String(event.toolName ?? ""), args: event.args });
1094
+ if (turnActive) syncWorkingMessage(true);
1095
+ });
1096
+
1097
+ pi.on("tool_execution_end", async (event: any, ctx) => {
1098
+ if (typeof event?.toolCallId !== "string" || !runningTools.delete(event.toolCallId)) return;
1099
+ activeCtx = ctx;
1100
+ if (turnActive) syncWorkingMessage(true);
1101
+ });
1102
+
1028
1103
  pi.on("turn_end", async (_event, ctx) => {
1029
1104
  turnActive = false;
1030
1105
  activeCtx = ctx;
@@ -1067,6 +1142,7 @@ export default function (pi: ExtensionAPI) {
1067
1142
  pi.on("agent_end", async () => {
1068
1143
  turnActive = false;
1069
1144
  agentStartTime = 0;
1145
+ runningTools.clear();
1070
1146
  // Preserve the just-finished "Turn took …" line. Pi emits agent_end
1071
1147
  // immediately after the final turn, so clearing here made the completion
1072
1148
  // status disappear before users could see it.
@@ -1088,6 +1164,7 @@ export default function (pi: ExtensionAPI) {
1088
1164
 
1089
1165
  pi.on("session_shutdown", async () => {
1090
1166
  turnActive = false;
1167
+ runningTools.clear();
1091
1168
  clearDisplay();
1092
1169
  herdrMarkIdle(undefined, true);
1093
1170
  herdrRootSession = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-claude-code-ui",
3
- "version": "1.0.91",
3
+ "version": "1.0.93",
4
4
  "description": "Claude Code-style grouped tool rows for pi with Ctrl+O/Ctrl+Shift+O previews, math rendering, and consistent built-in, MCP, and custom tool rendering",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -32,6 +32,7 @@
32
32
  "extensions/spinner.ts",
33
33
  "extensions/settings-path.ts",
34
34
  "extensions/bash-command.ts",
35
+ "extensions/bash-file-edit.ts",
35
36
  "extensions/theme-colors.ts",
36
37
  "extensions/terminal-theme.ts",
37
38
  "config/config.example.json",