pi-claude-code-ui 1.0.78 → 1.0.82

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/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  > [!IMPORTANT]
4
4
  > **1.0.69 — package rename (permanent).** Canonical npm name is now [`pi-claude-code-ui`](https://www.npmjs.com/package/pi-claude-code-ui). `pi-claude-style-tools` is legacy and will not receive further releases. Install with `pi install npm:pi-claude-code-ui` or `npm i pi-claude-code-ui`.
5
5
 
6
+ ## 1.0.80 — 2026-08-24
7
+
8
+ ### Fixed
9
+
10
+ - **Scrub Magic Context tags at the terminal writer** — last-resort display filter at the `ProcessTerminal.write` choke point removes every complete `§N§` token from painted output, covering any surface the targeted render strips can't reach: mid-sentence tag references in ctx_reduce/system-reminder tool output, replayed history on resume, overlays, and search hits. Display only — session storage, LLM context, copy sources, and ANSI sequences are untouched, so plugin functionality is unaffected.
11
+
12
+ ## 1.0.79 — 2026-08-23
13
+
14
+ ### Fixed
15
+
16
+ - **Hide Magic Context tags in tool output** — tool result renderers now receive sanitized text, so transient `§N§` tags (including live-prefixed streaming chunks and queued ctx_reduce output replayed from history) no longer appear in tool rows. Storage is never mutated — result blocks are cloned only when a tag is present, so context management keeps its data. Also covers the `formatToolExecution` fallback path for renderer-less tools.
17
+
6
18
  ## 1.0.78 — 2026-08-22
7
19
 
8
20
  ### Fixed
@@ -21,6 +33,13 @@
21
33
 
22
34
  - **Hide transient context tags** — hide transient context tags in streaming prose.
23
35
 
36
+ ## Unreleased
37
+
38
+ ### Fixed
39
+
40
+ - **Grouped Bash commands show live progress** (Raine Virta) - collapsed running Bash rows display their latest non-empty output line beneath the command.
41
+ - **Final timing status stays presentation-only** (Raine Virta) - the `Turn took` line renders as a styled TUI component without adding ANSI escapes or display text to persisted assistant messages.
42
+
24
43
  ## 1.0.75 — 2026-07-29
25
44
 
26
45
  ### Fixed
package/README.md CHANGED
@@ -21,7 +21,7 @@ Claude Code inspired tool rendering for Pi — Shiki-powered diffs, status dots,
21
21
  - **Adaptive edit/write diffs** with split or unified layouts, syntax highlighting, and inline word-level emphasis
22
22
  - **Diff stat bar** with colored add/remove summary and hunk metadata
23
23
  - **Progressive collapsed diff hints** that shorten on narrow terminals
24
- - **Thinking labels** during streaming and final messages, with context sanitization
24
+ - **Live-only thinking** (default) — only the actively-streaming thinking renders expanded; finished thinking collapses to a one-line `Thought for Xs` row (`/cc-tools thinking full` restores always-expanded, `Ctrl+O` still expands anything)
25
25
  - **MCP-aware rendering** with hidden, summary, and preview modes
26
26
  - **Configurable output modes** for read, search, bash, and MCP results
27
27
  - **Live running previews** that show a few output lines for active tool calls (latest lines for bash), persisting until the next tool/text activity
@@ -31,7 +31,7 @@ Claude Code inspired tool rendering for Pi — Shiki-powered diffs, status dots,
31
31
  - **Theme-adaptive palette** — borders, branch connectors, dim text, spinner accent, and diff backgrounds automatically follow the active pi theme (set `themeAdaptive: false` to keep the fixed Claude-style palette)
32
32
  - **Light Ghostty-sync themes** — edit/write diffs use `github-light` highlighting and light-tinted diff rows; tool pending dots use softer chrome colors
33
33
  - **Transparent edit/write diffs** with universal red/green diff colors
34
- - **Grouped consecutive tool calls** with a compact status header and per-tool glance rows (set `groupToolCalls: false` to disable)
34
+ - **Grouped consecutive tool calls** with single-row summaries for repeated targets and per-tool glance rows for mixed work (set `groupToolCalls: false` to disable)
35
35
  - **Extra detail toggle** with `Ctrl+Shift+O`, increasing expanded preview caps without making the default view heavy
36
36
  - **Global border patch** for all tool rows, including unknown/custom tools
37
37
 
@@ -50,8 +50,10 @@ Set in `.pi/settings.json` or `~/.pi/settings.json`:
50
50
  "extraExpandedPreviewMaxLines": 12000,
51
51
  "extraToolOutputExpanded": false,
52
52
  "groupToolCalls": true,
53
+ "thinkingMode": "live",
53
54
  "bashOutputMode": "opencode",
54
55
  "bashCollapsedLines": 10,
56
+ "bashCommandPreviewLines": 8,
55
57
  "liveToolPreview": true,
56
58
  "liveToolPreviewLines": 5,
57
59
  "diffCollapsedLines": 24,
@@ -69,7 +71,7 @@ When `themeAdaptive` is `true` (default), the following colors are derived from
69
71
  | User box, tool rules, code fences | `dim` → `muted` → `borderMuted` → `thinkingText` |
70
72
  | Branch connectors (`├`, `└`, `│`) | **fixed rgb(72)** by default (theme-independent); `/cc-tools branch theme` to follow pi theme |
71
73
  | "✻ Turn took Ns" line (final message only, with session total + turn count) | `muted` |
72
- | Thinking-block text and `∴` marker (marker hidden when thinking is collapsed) | `muted` |
74
+ | Expanded thinking-block text and `∴` marker | `muted` |
73
75
  | Diff add/remove accents | `toolDiffAdded` / `toolDiffRemoved` |
74
76
  | Diff background tints | mixed against `toolSuccessBg` base |
75
77
  | Spinner verb text (`Working…`) | `borderAccent` (fallback: `accent`) |
@@ -121,6 +123,8 @@ Use `/cc-tools` to control tool UI at runtime:
121
123
  /cc-tools outlines # tool style: outlines, transparent, or default
122
124
  /cc-tools group toggle # toggle grouped adjacent/concurrent tool calls
123
125
  /cc-tools group off # disable grouping (also ungroups current grouped rows)
126
+ /cc-tools thinking live # default: only the streaming thinking expands; finished ones collapse
127
+ /cc-tools thinking full # always render thinking expanded, like stock pi
124
128
  /cc-tools detail toggle # same mode as Ctrl+Shift+O
125
129
  ```
126
130
 
@@ -141,8 +145,10 @@ Use `/cc-tools` to control tool UI at runtime:
141
145
  | `expandedPreviewMaxLines` | `4000` | Max lines when expanded with Ctrl+O |
142
146
  | `extraExpandedPreviewMaxLines` | `12000` | Max lines after Ctrl+Shift+O extra-detail mode |
143
147
  | `extraToolOutputExpanded` | `false` | Start with Ctrl+Shift+O extra-detail mode enabled |
144
- | `groupToolCalls` | `true` | Group adjacent/concurrent tool calls under a compact status header |
148
+ | `groupToolCalls` | `true` | Group adjacent/concurrent calls, collapsing repeated targets into one row |
149
+ | `thinkingMode` | `live` | `live` = only streaming thinking expands (finished collapse to `Thought for Xs`); `full` = always expanded |
145
150
  | `bashCollapsedLines` | `10` | Lines for collapsed bash output |
151
+ | `bashCommandPreviewLines` | `8` | Verbatim script lines shown while bash runs or after failure; `0` disables them |
146
152
  | `liveToolPreview` | `true` | Show a small live output preview while tools are still running |
147
153
  | `liveToolPreviewLines` | `5` | Lines shown in the collapsed live preview |
148
154
  | `diffCollapsedLines` | `24` | Diff lines before collapsing |
@@ -8,8 +8,10 @@
8
8
  "extraExpandedPreviewMaxLines": 12000,
9
9
  "extraToolOutputExpanded": false,
10
10
  "groupToolCalls": true,
11
+ "thinkingMode": "live",
11
12
  "bashOutputMode": "opencode",
12
13
  "bashCollapsedLines": 10,
14
+ "bashCommandPreviewLines": 8,
13
15
  "liveToolPreview": true,
14
16
  "liveToolPreviewLines": 5,
15
17
  "diffCollapsedLines": 24,
@@ -0,0 +1,84 @@
1
+ export interface BashCommandPresentation {
2
+ headline: string;
3
+ sourceLines: string[];
4
+ sourceLineCount: number;
5
+ }
6
+
7
+ const CONTROL_CHAR_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
8
+ const ANSI_ESCAPE_RE = /\x1b(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1b\\))/g;
9
+ const HEADLINE_SKIP_RE = /^(?:#|set(?:\s|$)|cd(?:\s|$)|(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*\s*=|(?:\{|\}|do|done|then|fi|esac)$)/;
10
+
11
+ function normalizeHeadline(line: string): string {
12
+ return line.replace(/\s+/g, " ").trim();
13
+ }
14
+
15
+ function summarize(text: string, max = 180): string {
16
+ if (text.length <= max) return text;
17
+ const head = Math.max(1, Math.ceil((max - 3) * 0.65));
18
+ const tail = Math.max(1, max - 3 - head);
19
+ return `${text.slice(0, head)}...${text.slice(-tail)}`;
20
+ }
21
+
22
+ function sanitizeSource(command: string): string[] {
23
+ const lines = command
24
+ .replace(/\r\n?/g, "\n")
25
+ .replace(CONTROL_CHAR_RE, "")
26
+ .replace(/\t/g, " ")
27
+ .split("\n");
28
+ while (lines.length > 1 && lines[0].trim() === "") lines.shift();
29
+ while (lines.length > 1 && lines[lines.length - 1].trim() === "") lines.pop();
30
+ return lines;
31
+ }
32
+
33
+ export function buildBashCommandPresentation(command: string): BashCommandPresentation {
34
+ const sourceLines = sanitizeSource(command);
35
+ const nonBlank = sourceLines.map(normalizeHeadline).filter(Boolean);
36
+ const operative = nonBlank.find((line) => !HEADLINE_SKIP_RE.test(line)) ?? nonBlank[0] ?? "command";
37
+ let headline = summarize(operative);
38
+ if (sourceLines.length > 1) headline += ` · ${sourceLines.length} lines`;
39
+ return {
40
+ headline,
41
+ sourceLines,
42
+ sourceLineCount: sourceLines.length,
43
+ };
44
+ }
45
+
46
+ export function describeBashSource(presentation: BashCommandPresentation): string {
47
+ return presentation.sourceLineCount === 1 ? "command" : `script · ${presentation.sourceLineCount} lines`;
48
+ }
49
+
50
+ export function formatBashDuration(ms: number): string {
51
+ const safeMs = Math.max(0, Number.isFinite(ms) ? ms : 0);
52
+ if (safeMs < 1_000) return "<1s";
53
+ const totalSeconds = Math.floor(safeMs / 1_000);
54
+ if (totalSeconds < 60) return `${totalSeconds}s`;
55
+ const totalMinutes = Math.floor(totalSeconds / 60);
56
+ const seconds = totalSeconds % 60;
57
+ if (totalMinutes < 60) return `${totalMinutes}m ${String(seconds).padStart(2, "0")}s`;
58
+ const hours = Math.floor(totalMinutes / 60);
59
+ const minutes = totalMinutes % 60;
60
+ return `${hours}h ${String(minutes).padStart(2, "0")}m`;
61
+ }
62
+
63
+ export function getLastBashOutputLine(output: string): string | undefined {
64
+ const lines = output
65
+ .replace(/\r\n/g, "\n")
66
+ .replace(/\r/g, "\n")
67
+ .split("\n");
68
+ for (let index = lines.length - 1; index >= 0; index--) {
69
+ const line = lines[index]
70
+ .replace(ANSI_ESCAPE_RE, "")
71
+ .replace(CONTROL_CHAR_RE, "")
72
+ .trim();
73
+ if (line) return line;
74
+ }
75
+ return undefined;
76
+ }
77
+
78
+ export function buildBashPreview(sourceLines: string[], limit: number): string[] {
79
+ if (limit <= 0 || sourceLines.length === 0) return [];
80
+ if (sourceLines.length <= limit) return [...sourceLines];
81
+ if (limit === 1) return [`... ${sourceLines.length} more lines`];
82
+ const shown = sourceLines.slice(0, limit - 1);
83
+ return [...shown, `... ${sourceLines.length - shown.length} more lines`];
84
+ }