@pi-kaush/pi-tool-call-markers 0.2.3 → 0.2.5

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
@@ -2,20 +2,26 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
- - Compact multiline singleton calls into one width-safe summary after settlement, including timeout metadata, while preserving the full native call under Ctrl+O.
6
- - Compose capped in-progress rows before the Box paints its background, fixing elapsed tails leaking outside the tool background and restoring the block's bottom padding.
7
-
8
- - Render in-progress calls as a single header line (elapsed time inline for `bash`) that settles into the final summary at the same height, so the transcript flows downwards without jumping.
9
- - Show the real duration in successful bash summaries (`→ done · 2.3s`) instead of dropping the elapsed time on completion.
10
- - Group adjacent calls while they are still running, so new bullets grow downward and successful settlement updates outcomes in place without upward reflow.
11
- - Add `PI_TOOL_CALL_MARKERS_COLLAPSE_PARALLEL`, enabled by default, to keep same-assistant-message calls individual while still grouping sequential calls across quiet turns.
12
- - Only invalidate grouped-render caches on real state transitions, so bash's per-second ticks and resize invalidations stop busting them.
13
-
14
- - Keep failed calls collapsed by default while retaining their native error background and Ctrl+O expansion.
15
- - Add compact result tails for common successful tools in singleton and grouped summaries.
16
- - Keep grouped bullets to one line and preserve the useful result tail on narrow terminals.
17
- - Merge calls across assistant turns as soon as they appear when no visible prose or thinking separates them.
18
- - Move adjacent thinking-block merging to a separate extension entrypoint bundled in this package.
5
+ - Render subagent calls as an unboxed plan `% subagent` heading with chain/parallel counts and numbered steps as they execute, agent names in accent with emojis scraped from the native plan component (args fallback) — replacing the accent-rail card; failed subagents follow the full-red failure tone.
6
+ - Strip display sequences and control bytes (notably `\r` from progress writers like git) from collapsed-row text so command output cannot return the cursor to column 0 and overwrite the row.
7
+ - Color the truncation ellipsis to match its row tone (`muted` settled, `error` failed) instead of the terminal default foreground left by pi-tui's truncation reset.
8
+ - Render failed tool rows entirely in `error` marker, call label, and outcome tail including the arrow and truncation ellipsis so errored calls stand out as full red lines.
9
+ - Render settled tool rows in one uniform `muted` tone — group headings, bullets, summaries, and outcomes while keeping `warning` pending and `error` failure states semantic.
10
+ - Compose container-level rendering through the shared
11
+ `kg.pi.chatContainerHooks.v1` registry so grouping no longer shadows (or is
12
+ shadowed by) `pi-content-layout`'s system-message inset in either load order.
13
+ - Label self-rendered `edit` rows by path (`edit <path>`), matching Pi's native
14
+ call line instead of dumping the raw `{ path, edits }` JSON payload.
15
+ - Redesign collapsed tools as unboxed, background-free transcript lines with a two-column outer inset, `%` tool/group headings, `•` grouped children, and semantic low-contrast foregrounds with clear warning/error states.
16
+ - Preserve compact outcomes, right-hand tail reservation, `bash` duration, errors, image output, quiet-turn grouping, grouped-render caching, and MCP/self-rendered tool labels in the new shell.
17
+ - Keep expanded rows fully native so `Ctrl+O` restores complete results, custom renderers, and errors without collapsed decoration.
18
+ - Label hidden local reasoning with Pi's native braille spinner sequence (`⠋ Thinking…`, `⠙ Thinking…`, …) while streaming and `+ Thought · X.Xs` when finalized; use `+ Thought` for restored messages and older runtimes without streaming metadata.
19
+ - Track thinking duration per assistant row in a `WeakMap`, forward optional/future `updateContent` arguments, retain display-only adjacent thinking merging, and add no timer or render loop.
20
+ - Feature-detect private tool and thinking component shapes and fail open to Pi's native rendering while retaining Pi `>=0.80.6` support.
21
+ - Compact multiline singleton calls into one width-safe summary after settlement, including timeout metadata, while preserving the full native call under `Ctrl+O`.
22
+ - Group adjacent calls while they are still running, update pending outcomes in place, and merge sequential calls across quiet assistant turns.
23
+ - Add `PI_TOOL_CALL_MARKERS_COLLAPSE_PARALLEL`, enabled by default, to optionally keep same-assistant-message calls individual.
24
+ - Only invalidate grouped-render caches on meaningful state transitions.
19
25
 
20
26
  ## 0.1.2
21
27
 
package/README.md CHANGED
@@ -1,34 +1,59 @@
1
1
  # @pi-kaush/pi-tool-call-markers
2
2
 
3
- Collapse Pi's adjacent successful tool calls into one compact, gear-headed block per tool type, so a run of similar calls reads as a tidy bulleted list instead of a wall of repeated headers and results.
3
+ Give Pi's collapsed tool calls a quiet, width-safe transcript shell while preserving native details under `Ctrl+O`. The package also includes a display-only thinking-block adapter.
4
4
 
5
5
  ## What it changes
6
6
 
7
- When several tool calls run in a row, Pi normally renders each one as its own expanded block. This extension groups them:
7
+ Collapsed tool rows use semantic theme colors with no gear, background fill, box padding, or filled blank rows:
8
8
 
9
- - **One gear header per contiguous tool type.** A run of `read` calls shares a single `⚙️ read` header; the following `write` run gets its own `⚙️ write` header.
10
- - **Bulleted call summaries.** Each call in a group becomes one bullet with a short summary and, for common tools, a compact outcome such as `→ done`, `→ 42 lines`, or `→ +2/-1`.
11
- - **Vertical spacing between tool types.** A blank line separates one tool group from the next.
12
- - **One-line, width-safe summaries.** Long targets truncate before their useful outcome tail instead of wrapping into taller blocks.
13
- - **Running calls group immediately.** Adjacent calls become bullets as they appear, and each pending marker or elapsed `bash` time updates to the final outcome in place. Successful settlement does not shrink the block, so the transcript only grows downwards.
14
- - **Sequential calls merge across quiet turns.** A later call joins the existing group immediately when no visible prose or thinking separates it; visible assistant content remains a hard boundary.
15
- - **Self-rendered tools get the same treatment.** Tools that own their framing (e.g. MCP adapter rows) collapse to the same one-line summary args label plus outcome — with pending, success, and error backgrounds; expanding restores their full custom render.
16
- - **Image results stay visible.** Image-bearing results are not collapsed into text-only groups. Partial text output is held back while a call runs and surfaces through the final summary.
17
- - **Errors stay compact and visibly failed.** A failed call keeps an error-colored block: native collapsed detail for built-in tools, or a one-line summary with the first error line for self-rendered tools.
18
- - **Ctrl+O restores full blocks.** Expanding tools (`setToolsExpanded(true)`) brings back Pi's individual full blocks, including complete error details and successful results.
9
+ ```text
10
+ % Read
11
+ src/a.ts 42 lines
12
+ src/b.ts 18 lines
13
+ ```
14
+
15
+ - **Two-column outer inset.** Tool markers and image output align with an inset conversation surface. Very narrow terminals reduce the decoration before useful content.
16
+ - **`%` tool headings.** A singleton stays on one line when its summary and outcome fit. A multi-call group has one `%` heading per contiguous tool type.
17
+ - **`•` grouped children.** Bullets appear only for members of a multi-call group.
18
+ - **Semantic, low-contrast status.** Tool names are emphasized, summaries and settled metadata are muted, pending state is warning-colored, and failures remain error-colored. Ordinary tool states have no background.
19
+ - **Width-safe outcome tails.** Long summaries truncate before useful tails such as `→ done`, `→ 42 lines`, `→ +2/-1`, or a `bash` duration.
20
+ - **Stable running groups.** Adjacent calls group as they appear. Pending state and elapsed `bash` time settle into the final outcome without changing the row count.
21
+ - **Quiet-turn grouping.** Sequential calls can join across an assistant row with no visible prose or thinking. Visible assistant content remains a boundary.
22
+ - **MCP and self-rendered tools.** Their stable call labels, compact arguments, pending state, success, and first error line use the same collapsed shell. Native self-rendered details return when expanded.
23
+ - **Images remain visible.** Image fallback text and terminal image components render below the corresponding marker with the same inset.
24
+ - **Native expansion remains authoritative.** `Ctrl+O` restores Pi's full individual tool rendering, including complete results, custom renderers, and error details.
25
+
26
+ ## Subagent plans
27
+
28
+ A recognized `subagent` call renders as an unboxed plan in the shared tool aesthetic:
29
+
30
+ ```text
31
+ % subagent chain (3 steps) [repo-review]
32
+ 1. 🐝 bee [workhorse] Challenge the compatibility conclusion…
33
+ 2. 🐝 bee …
34
+ ```
35
+
36
+ Single calls stay on one `% subagent <agent> [profile] <task preview>` row; chain calls get a heading with the kind, count, and scope followed by numbered steps (parallel tasks list without numbers). Agent display names (emoji + name) are scraped from the native plan component with an args fallback, and render in `accent`; everything else stays muted, and failed subagents go full red. Subagents never join ordinary tool groups.
37
+
38
+ Malformed, ambiguous, future, or too-narrow shapes fall back to the generic `% subagent …` collapsed row rather than dropping information, and `Ctrl+O` still exposes the native subagent renderer.
19
39
 
20
40
  ## Bundled thinking-block extension
21
41
 
22
- The package ships a second extension entrypoint, `src/thinking-block-merger.ts`. Pi loads it independently from the tool presentation extension, so its `AssistantMessageComponent` patch and shutdown lifecycle stay isolated while install, update, and removal remain one package operation.
42
+ The second package entrypoint, `src/thinking-block-merger.ts`, combines only directly adjacent `thinking` blocks in a display copy. Tool calls, text, provider blocks, signatures, and stored session messages are unchanged.
23
43
 
24
- It combines only directly adjacent `thinking` blocks for display. Tool calls, text, and other content remain boundaries; provider blocks and signatures are not modified.
44
+ When Pi exposes its per-row hidden-thinking and streaming fields, hidden reasoning uses these native-themed labels:
25
45
 
26
- ## Install
46
+ ```text
47
+ ⠋ Thinking… → ⠙ Thinking… → …
48
+ + Thought · 2.5s
49
+ ```
27
50
 
28
- After the first npm release:
51
+ The live label samples Pi's native braille spinner sequence from the content updates Pi already renders; it does not add a timer. The adapter stores the first local streaming timestamp per assistant row in a `WeakMap`. A restored message or an older runtime with no streaming argument uses `+ Thought`. Visible-thinking mode remains native. There is no interval, timeout, render request, model call, or network work.
52
+
53
+ ## Install
29
54
 
30
55
  ```bash
31
- pi install npm:@pi-kaush/pi-tool-call-markers@0.1.0
56
+ pi install npm:@pi-kaush/pi-tool-call-markers
32
57
  ```
33
58
 
34
59
  For local development:
@@ -41,7 +66,7 @@ pi \
41
66
 
42
67
  ## Configuration
43
68
 
44
- Grouping calls from the same assistant message is enabled by default. Pi normally executes those calls in parallel. To keep those same-message calls as individual compact rows while continuing to group sequential calls across quiet turns, start Pi with:
69
+ Grouping calls from the same assistant message is enabled by default. Pi normally executes those calls in parallel. To keep same-message calls as individual compact rows while continuing to group sequential calls across quiet turns:
45
70
 
46
71
  ```fish
47
72
  set -lx PI_TOOL_CALL_MARKERS_COLLAPSE_PARALLEL 0
@@ -50,35 +75,37 @@ pi
50
75
 
51
76
  `0`, `false`, `no`, and `off` disable parallel grouping. `1`, `true`, `yes`, and `on` enable it. The value is read when the extension loads.
52
77
 
53
- ## Compatibility and risk
78
+ ## Compatibility and fallback policy
54
79
 
55
- The tool presentation entrypoint currently relies on **guarded, reversible prototype patches** against two Pi component classes:
80
+ **Compatible Pi version:** `@earendil-works/pi-coding-agent` and `@earendil-works/pi-tui` `>=0.80.6`.
56
81
 
57
- - `ToolExecutionComponent` (render + display presentation),
58
- - `Container` (transcript grouping).
82
+ Pi has no public hook for native tool rows, transcript grouping, or per-message hidden-thinking labels. The package therefore uses three small guarded prototype adapters:
59
83
 
60
- The bundled thinking-block entrypoint separately patches `AssistantMessageComponent.updateContent`. Its patch and lifecycle do not share state with tool presentation.
84
+ - `ToolExecutionComponent` for collapsed presentation;
85
+ - `Container` for adjacent grouping; and
86
+ - `AssistantMessageComponent.updateContent` for display-only thinking merging and lifecycle labels.
61
87
 
62
- Pi exposes no public transcript or tool-grouping hook today, so the extension patches those prototypes and restores the originals on `session_shutdown`. Every patch is wrapped in `try`/`catch` with an idempotency guard (`Symbol.for(...)` markers), so if Pi's internals change the extension silently no-ops and Pi's default rendering is preserved.
88
+ Each adapter feature-detects the fields and methods it needs, keeps the original method, uses an idempotency symbol, catches cosmetic failures, and restores the original on `session_shutdown` when it still owns the patch. Unsupported shapes fail open to Pi's native rendering. The thinking adapter continues adjacent merging even when the private label shape is unavailable.
63
89
 
64
- **Compatible Pi version:** `@earendil-works/pi-coding-agent` and `@earendil-works/pi-tui` `>=0.80.6`. Because the patches touch internal prototype methods, a future Pi release that renames or restructures those methods can silently disable the affected presentation until this package is updated. All original methods are restored on shutdown.
90
+ Expanded tools always use Pi's native renderer. The collapsed tool shell owns its two-column inset directly; transcript layout extensions should leave tool rows unchanged, preventing load-order-dependent double padding.
65
91
 
66
- > TODO: migrate to a public Pi tool/transcript rendering API when one becomes available, and remove the prototype patches.
92
+ > TODO: migrate these adapters to public Pi transcript and tool-rendering APIs when available.
67
93
 
68
94
  ## Design
69
95
 
70
96
  - No runtime dependencies.
71
- - Startup only registers `session_start` / `session_shutdown` handlers and installs the reversible patches; no I/O, subprocesses, model requests, or timers.
72
- - Grouped output is cached per row and invalidated when any member's display version changes, so repeated renders reuse work while stale groups refresh on demand.
73
- - Removing the package restores Pi's default rendering on the next session.
97
+ - No mutation of tool arguments, tool results, provider content, or session messages.
98
+ - Group output is cached per row and invalidated on meaningful display transitions.
99
+ - No timers or independent render loops.
74
100
 
75
101
  ## Development
76
102
 
77
103
  From the repository root:
78
104
 
79
105
  ```bash
80
- npm ci --ignore-scripts
81
- npm run check
106
+ npx vitest run extensions/pi-tool-call-markers/test
107
+ npm run typecheck
108
+ npm run package:check --workspace @pi-kaush/pi-tool-call-markers
82
109
  ```
83
110
 
84
111
  Inspect the publish payload:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-kaush/pi-tool-call-markers",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Compact and group Pi tool calls, with a bundled extension for adjacent thinking blocks.",
5
5
  "license": "MIT",
6
6
  "author": "Kaushik Gopal",
@@ -0,0 +1,57 @@
1
+ // Shared chat-container composition contract. pi-content-layout and
2
+ // pi-tool-call-markers each replace Container.prototype.render for different
3
+ // concerns (system-text inset vs tool grouping), and a whole-container
4
+ // wrapper always shadows whatever wrapped the prototype before it. Both
5
+ // packages instead run every registered hook before rendering and restore
6
+ // after, so child-level decoration composes in either load order. The
7
+ // registry is a globalThis set keyed by a well-known symbol: no module
8
+ // coupling, and an empty registry is a no-op when the other package is
9
+ // absent. This file is a deliberate copy of pi-content-layout's contract
10
+ // module: the Symbol key is the shared contract, duplicated to keep both
11
+ // packages free of runtime dependencies on each other.
12
+
13
+ export type ChatContainerHook = (
14
+ container: object,
15
+ children: unknown[],
16
+ width: number,
17
+ ) => (() => void) | undefined;
18
+
19
+ export const CHAT_CONTAINER_HOOKS = Symbol.for("kg.pi.chatContainerHooks.v1");
20
+
21
+ type HookRegistry = Set<ChatContainerHook>;
22
+
23
+ export function chatContainerHooks(): HookRegistry {
24
+ const root = globalThis as Record<symbol, HookRegistry | undefined>;
25
+ const existing = root[CHAT_CONTAINER_HOOKS];
26
+ if (existing) return existing;
27
+ const created: HookRegistry = new Set();
28
+ root[CHAT_CONTAINER_HOOKS] = created;
29
+ return created;
30
+ }
31
+
32
+ export function runChatContainerHooks(
33
+ container: object,
34
+ children: unknown,
35
+ width: number,
36
+ ): () => void {
37
+ const restores: Array<() => void> = [];
38
+ if (Array.isArray(children)) {
39
+ for (const hook of chatContainerHooks()) {
40
+ try {
41
+ const restore = hook(container, children, width);
42
+ if (typeof restore === "function") restores.push(restore);
43
+ } catch {
44
+ // Hooks are cosmetic; one failing hook must not break rendering.
45
+ }
46
+ }
47
+ }
48
+ return () => {
49
+ for (const restore of restores.reverse()) {
50
+ try {
51
+ restore();
52
+ } catch {
53
+ // Best-effort restore; rendering already completed.
54
+ }
55
+ }
56
+ };
57
+ }