@pi-kaush/pi-tool-call-markers 0.3.3 → 0.3.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/README.md CHANGED
@@ -15,20 +15,16 @@ Restart Pi or run `/reload`.
15
15
  Collapsed tool rows use semantic theme colors with no gear, background fill, box padding, or filled blank rows:
16
16
 
17
17
  ```text
18
- % Read
19
- │ src/a.ts 42 lines
20
- │ src/b.ts 18 lines
21
-
22
- % Bash
23
- │ $: npm test → done
18
+ │ ●: src/a.ts 42 lines
19
+ │ ●: src/b.ts 18 lines
20
+ │ $: npm test → done
24
21
  ```
25
22
 
26
23
  - **Two-column outer inset.** Tool markers and image output align with an inset conversation surface. Very narrow terminals reduce the decoration before useful content.
27
- - **`%` 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, with a blank line before each subsequent heading.
28
- - **`│` grouped children.** A vertical rail — fainter than the call text — spans the members of a multi-call group; singletons carry no rail.
29
- - **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.
24
+ - **`│` tool-call rail.** Every collapsed call anchors on a glyph instead of its tool name: `●` read, `+` write, `±` edit, `○` local search, `≡` ls, `↗` web search and fetch, and `$` bash; unmapped tools fall back to `*`. Grouped rows flow directly across tool types.
25
+ - **Semantic, low-contrast status.** The anchor and the call content share the collapsed mute, pending state is warning-colored, and failures remain error-colored. Ordinary tool states have no background.
30
26
  - **Width-safe outcome tails.** Long summaries truncate before useful tails such as `→ done`, `→ 42 lines`, `→ +2/-1`, or a `bash` duration.
31
- - **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.
27
+ - **Stable running groups.** Adjacent calls group as they appear, including settled failures, which stay error-colored inside the group. Pending state and elapsed `bash` time settle into the final outcome without changing the row count.
32
28
  - **Quiet-turn grouping.** Sequential calls can join across an assistant row with no visible prose or thinking. Visible assistant content remains a boundary.
33
29
  - **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.
34
30
  - **Images remain visible.** Image fallback text and terminal image components render below the corresponding marker with the same inset.
@@ -37,7 +33,7 @@ Collapsed tool rows use semantic theme colors with no gear, background fill, box
37
33
 
38
34
  ## Subagent plans
39
35
 
40
- A recognized `subagent` call renders as an unboxed plan in the shared tool aesthetic, marked with `↪` instead of the ordinary `%` tool marker:
36
+ A recognized `subagent` call renders as an unboxed plan in the shared tool aesthetic, marked with `↪` instead of the ordinary `│` tool-call rail:
41
37
 
42
38
  ```text
43
39
  ↪ subagent chain (3 steps) [repo-review]
@@ -53,7 +49,7 @@ Malformed, ambiguous, future, or too-narrow shapes fall back to the generic `↪
53
49
 
54
50
  ## Edit diffs
55
51
 
56
- Settled `edit` calls keep their change visible without expanding: the call line gains a `+added/-removed` outcome stat, and the hunk renders as a bounded diff block underneath (`+` lines in the added tone, `-` in the removed tone, context muted, folded regions as `...`, capped at 12 lines with a count tail). The full native diff — line numbers, intra-line word highlights — still returns with `Ctrl+O`.
52
+ Settled `edit` calls collapse like every other row: the call line keeps its path and a `+added/-removed` outcome stat, and the hunk itself stays out of the collapsed row. `Ctrl+O` restores Pi's full native diff, including line numbers and intra-line word highlights. To keep edits expanded without pressing `Ctrl+O`, list `edit` in `PI_ALWAYS_EXPANDED_TOOL_CALL_MARKERS` (see Configuration).
57
53
 
58
54
  Files and paths in collapsed rows are preserved as displayed by Pi: hyperlink-wrapped paths (Pi wraps `read` call paths in OSC 8 hyperlinks) keep their visible text when sanitized for one-line rows.
59
55
 
@@ -103,9 +99,18 @@ pi
103
99
 
104
100
  `0`, `false`, `no`, and `off` disable parallel grouping. `1`, `true`, `yes`, and `on` enable it. The value is read when the extension loads.
105
101
 
102
+ To render chosen tools as Pi's native expanded block by default — full result and diff without `Ctrl+O` — list their names:
103
+
104
+ ```fish
105
+ set -lx PI_ALWAYS_EXPANDED_TOOL_CALL_MARKERS edit,write
106
+ pi
107
+ ```
108
+
109
+ Names are comma-separated and matched exactly. Listed rows never join a group, and the list stays authoritative: collapsing the transcript with `Ctrl+O` re-expands them. The value is read when the extension loads.
110
+
106
111
  ### Collapsed-row colors
107
112
 
108
- Collapsed rows (tool calls, `+ Thought`, subagents) default to the theme's `syntaxComment` color — it ships with every Pi theme and reads as a muted tone — with the bolded tool name and the call content sharing it. Failures stay error-colored and live spinners keep their existing tints.
113
+ Collapsed rows (tool calls, `+ Thought`, subagents) default to the theme's `syntaxComment` color — it ships with every Pi theme and reads as a muted tone — with the row's anchor and the call content sharing it. Failures stay error-colored and live spinners keep their existing tints.
109
114
 
110
115
  A theme can override each collapsed kind independently with optional color tokens:
111
116
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-kaush/pi-tool-call-markers",
3
- "version": "0.3.3",
3
+ "version": "0.3.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",
package/src/index.ts CHANGED
@@ -19,14 +19,30 @@ import { installInfoVisibility } from "./info-visibility.ts";
19
19
  import { fgCollapsed, fgCollapsedRail } from "./muted.ts";
20
20
 
21
21
  const OUTER_INSET = 2;
22
- const GROUP_MARKER = "%";
23
22
  const SUBAGENT_MARKER = "↪";
24
23
  const GROUP_CALL_MARKER = "│";
24
+ // Collapsed rows anchor with a glyph instead of repeating the tool name;
25
+ // tools that read as one family share a glyph, and unmapped tools fall back
26
+ // to `*`.
27
+ const TOOL_CALL_GLYPHS: ReadonlyMap<string, string> = new Map([
28
+ ["read", "●"],
29
+ ["write", "+"],
30
+ ["edit", "±"],
31
+ ["grep", "○"],
32
+ ["ffgrep", "○"],
33
+ ["find", "○"],
34
+ ["fffind", "○"],
35
+ ["ls", "≡"],
36
+ ["web_search", "↗"],
37
+ ["fetch_content", "↗"],
38
+ ["get_search_content", "↗"],
39
+ ]);
25
40
  const PRESENTATION_PATCHED = Symbol.for("kg.pi.toolPresentation.v3");
26
41
  const LEGACY_PRESENTATION_PATCHED = Symbol.for("kg.pi.toolPresentation.v2");
27
42
  const GROUPING_PATCHED = Symbol.for("kg.pi.toolGrouping.v1");
28
43
  const ANSI_RE = /\u001b\[[0-9;]*m/g;
29
44
  const COLLAPSE_PARALLEL_ENV = "PI_TOOL_CALL_MARKERS_COLLAPSE_PARALLEL";
45
+ const EXPANDED_TOOLS_ENV = "PI_ALWAYS_EXPANDED_TOOL_CALL_MARKERS";
30
46
 
31
47
  type ThemeLike = {
32
48
  bold(text: string): string;
@@ -73,6 +89,7 @@ type PresentationPatchState = {
73
89
  owners: number;
74
90
  theme?: ThemeLike;
75
91
  collapseParallel: boolean;
92
+ expandedTools: ReadonlySet<string>;
76
93
  groupCache: WeakMap<ToolExecutionRow, GroupRenderCache>;
77
94
  collapsedCache: WeakMap<ToolExecutionRow, CollapsedRenderCache>;
78
95
  rowVersions: WeakMap<ToolExecutionRow, number>;
@@ -95,9 +112,7 @@ type GroupingPatchState = {
95
112
 
96
113
  type GroupRenderCache = {
97
114
  lines: string[];
98
- // Per-member click-routing accounting for the group block: the first
99
- // member absorbs the leading blank and the tool-name header, later
100
- // members carry their own line (plus edit diffs).
115
+ // The first member absorbs the leading blank; each member owns its line.
101
116
  heights: Array<{ component: ToolExecutionRow; height: number }>;
102
117
  members: ToolExecutionRow[];
103
118
  memberVersions: number[];
@@ -121,6 +136,19 @@ function envEnabled(name: string, defaultValue: boolean): boolean {
121
136
  return defaultValue;
122
137
  }
123
138
 
139
+ // Comma-separated tool names, e.g. `edit,write`. Blank entries and padding
140
+ // are ignored; an unset or empty variable yields no names.
141
+ function envToolNames(name: string): ReadonlySet<string> {
142
+ const value = process.env[name]?.trim();
143
+ if (!value) return new Set();
144
+ return new Set(
145
+ value
146
+ .split(",")
147
+ .map((entry) => entry.trim())
148
+ .filter(Boolean),
149
+ );
150
+ }
151
+
124
152
  function stripAnsi(text: string): string {
125
153
  return text.replace(ANSI_RE, "");
126
154
  }
@@ -498,6 +526,18 @@ function isCollapsibleSuccess(row: ToolExecutionRow): boolean {
498
526
  );
499
527
  }
500
528
 
529
+ // Settled failures join groups like settled successes; the row's error
530
+ // color carries the failure, so the group stays visually unbroken.
531
+ function isCollapsibleFailure(row: ToolExecutionRow): boolean {
532
+ return (
533
+ row.expanded === false &&
534
+ row.isPartial === false &&
535
+ !!row.result &&
536
+ rowHasFailed(row) &&
537
+ !hasImageResult(row)
538
+ );
539
+ }
540
+
501
541
  function hasToolSiblingInAssistantBatch(
502
542
  children: unknown[],
503
543
  index: number,
@@ -526,7 +566,12 @@ function isGroupableToolRow(
526
566
  state: PresentationPatchState,
527
567
  ): boolean {
528
568
  if (row.toolName === "subagent") return false;
529
- if (!isLiveRow(row) && !isCollapsibleSuccess(row)) return false;
569
+ if (
570
+ !isLiveRow(row) &&
571
+ !isCollapsibleSuccess(row) &&
572
+ !isCollapsibleFailure(row)
573
+ )
574
+ return false;
530
575
  return (
531
576
  state.collapseParallel ||
532
577
  !hasToolSiblingInAssistantBatch(children, index, renderAt)
@@ -613,15 +658,8 @@ function diffCounts(diff: string): { added: number; removed: number } {
613
658
  }
614
659
 
615
660
  // Pi's edit tool (renderShell "self") stores its display diff in
616
- // result.details.diff — lines like `+27 <content>`, `-27 <content>`,
617
- // ` 28 <context>`, plus `...` for folded regions. Self-rendered rows
618
- // normally keep only their one-line call label; edits additionally show
619
- // the change as a bounded diff block under the summary and a +a/-b stat in
620
- // the outcome tail, so the hunk stays visible without Ctrl+O while
621
- // expanded rows keep Pi's native rendering.
622
- const EDIT_DIFF_LINE_RE = /^([+\-\s])(\s*\d*)(.*)$/;
623
- const MAX_EDIT_DIFF_LINES = 12;
624
-
661
+ // result.details.diff; collapsed rows show only its +a/-b stat, while the
662
+ // full hunk returns with Ctrl+O.
625
663
  function editDiffText(row: ToolExecutionRow): string | undefined {
626
664
  if (row.toolName !== "edit") return undefined;
627
665
  if (!row.result || row.isPartial !== false || rowHasFailed(row))
@@ -637,43 +675,6 @@ function renderedEditDiffStat(row: ToolExecutionRow): string | undefined {
637
675
  return added + removed === 0 ? "applied" : `+${added}/-${removed}`;
638
676
  }
639
677
 
640
- function renderedEditDiffLines(
641
- row: ToolExecutionRow,
642
- theme: ThemeLike,
643
- ): string[] {
644
- const diff = editDiffText(row);
645
- if (diff === undefined) return [];
646
- const raw = diff.split("\n");
647
- if (raw[raw.length - 1] === "") raw.pop();
648
- const lines: string[] = [];
649
- for (const line of raw.slice(0, MAX_EDIT_DIFF_LINES)) {
650
- const clean = sanitizeInline(line).trimEnd();
651
- if (clean.trim() === "...") {
652
- lines.push(fgCollapsed(theme, "muted", " ..."));
653
- continue;
654
- }
655
- const match = EDIT_DIFF_LINE_RE.exec(clean);
656
- if (!match) continue;
657
- const color =
658
- match[1] === "+"
659
- ? "toolDiffAdded"
660
- : match[1] === "-"
661
- ? "toolDiffRemoved"
662
- : "toolDiffContext";
663
- lines.push(theme.fg(color, ` ${match[1]}${match[2]}${match[3]}`));
664
- }
665
- if (raw.length > MAX_EDIT_DIFF_LINES) {
666
- lines.push(
667
- fgCollapsed(
668
- theme,
669
- "muted",
670
- ` ... +${raw.length - MAX_EDIT_DIFF_LINES} more`,
671
- ),
672
- );
673
- }
674
- return lines;
675
- }
676
-
677
678
  function outcomeSummary(row: ToolExecutionRow): string | undefined {
678
679
  // Self-rendered tools own their result framing; the built-in heuristics
679
680
  // (line counts, diff stats) describe default-shell rows only, so their
@@ -725,22 +726,6 @@ function renderedOutcome(
725
726
  return fgCollapsed(theme, "toolOutput", `→ ${summary}`);
726
727
  }
727
728
 
728
- function renderedGroupedOutcome(
729
- row: ToolExecutionRow,
730
- theme: ThemeLike,
731
- ): string | undefined {
732
- const outcome = renderedOutcome(row, theme);
733
- if (outcome) return outcome;
734
- if (isLiveRow(row)) {
735
- const elapsed = liveElapsedText(row);
736
- return theme.fg("warning", elapsed ? `… · ${elapsed}` : "…");
737
- }
738
- // Self-rendered tools have no per-tool outcome summary; keep the group's
739
- // settled tail consistent with collapsed singletons.
740
- if (row.getRenderShell?.() === "self") return renderedGenericOutcome(theme);
741
- return undefined;
742
- }
743
-
744
729
  // A budget too small for the suffix keeps literal styled text instead, so a
745
730
  // one-column label never collapses into a bare ellipsis or loses its status.
746
731
  function truncateStyled(text: string, width: number, suffix = "…"): string {
@@ -901,6 +886,23 @@ function collapsedCallLabel(
901
886
  return styledCallLabel(label, theme, color);
902
887
  }
903
888
 
889
+ // Collapsed rows anchor their call text with a glyph instead of repeating
890
+ // the tool name, grouped or not. Bash carries `$:` inside its own label and
891
+ // the subagent fallback keeps its `↪ subagent` heading, so neither takes a
892
+ // glyph.
893
+ function collapsedCallMarker(
894
+ row: ToolExecutionRow,
895
+ theme: ThemeLike,
896
+ color: string,
897
+ ): string {
898
+ if (row.toolName === "bash" || row.toolName === "subagent") return "";
899
+ return fgCollapsed(
900
+ theme,
901
+ color,
902
+ `${TOOL_CALL_GLYPHS.get(row.toolName ?? "") ?? "*"}: `,
903
+ );
904
+ }
905
+
904
906
  function collapsedOutcome(
905
907
  row: ToolExecutionRow,
906
908
  width: number,
@@ -931,15 +933,15 @@ function collapsedHeadline(
931
933
  theme: ThemeLike,
932
934
  ): string {
933
935
  // Failed rows render entirely in error so the row reads as the one that
934
- // failed, not just its outcome tail. Only the tool name is bold.
936
+ // failed, not just its outcome tail. Only the row's anchor stays bold.
935
937
  const tone = rowHasFailed(row) ? "error" : "muted";
936
- const marker = `${fgCollapsed(
937
- theme,
938
- tone,
939
- row.toolName === "subagent" ? SUBAGENT_MARKER : GROUP_MARKER,
940
- )} `;
938
+ const marker =
939
+ row.toolName === "subagent"
940
+ ? `${fgCollapsed(theme, tone, SUBAGENT_MARKER)} `
941
+ : `${fgCollapsedRail(theme, GROUP_CALL_MARKER)} `;
942
+ const glyph = collapsedCallMarker(row, theme, tone);
941
943
  const budget = Math.max(1, width - visibleWidth(marker));
942
- const label = collapsedCallLabel(row, budget, theme, tone);
944
+ const label = glyph + collapsedChildLabel(row, budget, theme, tone);
943
945
  const outcome = collapsedOutcome(row, budget, theme);
944
946
  // The truncation suffix inherits the row tone; pi-tui's truncation resets
945
947
  // around a plain suffix, which would render it in the terminal default
@@ -1250,20 +1252,20 @@ function renderCollapsedToolRow(
1250
1252
  ? renderSubagentPlan(row, layout.contentWidth, theme)
1251
1253
  : undefined;
1252
1254
  const body = plan ?? [collapsedHeadline(row, layout.contentWidth, theme)];
1253
- if (row.toolName === "edit") {
1254
- body.push(...renderedEditDiffLines(row, theme));
1255
- }
1256
1255
  body.push(...imageResultLines(row, layout.contentWidth, theme));
1257
1256
  return ["", ...insetLines(body, width)];
1258
1257
  }
1259
1258
 
1260
- function groupedChildLabel(
1259
+ // Drops the call label's tool-name token: the row's glyph anchors the call
1260
+ // instead. The `↪` subagent fallback keeps its own heading.
1261
+ function collapsedChildLabel(
1261
1262
  row: ToolExecutionRow,
1262
1263
  width: number,
1263
1264
  theme: ThemeLike,
1264
1265
  color: string = "muted",
1265
1266
  ): string {
1266
1267
  const label = collapsedCallLabel(row, width, theme, color);
1268
+ if (row.toolName === "subagent") return label;
1267
1269
  const plain = stripAnsi(label);
1268
1270
  const toolName = row.toolName ?? "tool";
1269
1271
  let prefix = "";
@@ -1289,36 +1291,26 @@ function renderGroupedCallLines(
1289
1291
  theme: ThemeLike,
1290
1292
  ): Array<{ row: ToolExecutionRow; lines: string[] }> {
1291
1293
  const segments: Array<{ row: ToolExecutionRow; lines: string[] }> = [];
1292
- let previousToolName: string | undefined;
1293
1294
  for (const row of rows) {
1294
1295
  const lines: string[] = [];
1295
- const toolName = row.toolName ?? "tool";
1296
- if (toolName !== previousToolName) {
1297
- // One blank line between tool-name subgroups, mirroring the block's
1298
- // leading blank so each `%` heading starts its own visual run. The
1299
- // blank belongs to the new run's first row, keeping the per-member
1300
- // click-routing heights aligned with the drawn lines.
1301
- if (previousToolName !== undefined) lines.push("");
1302
- lines.push(
1303
- `${fgCollapsed(theme, "muted", GROUP_MARKER)} ${fgCollapsed(theme, "toolTitle", toolName, true)}`,
1304
- );
1305
- previousToolName = toolName;
1306
- }
1307
1296
 
1308
1297
  const color = rowHasFailed(row) ? "error" : "muted";
1309
- const prefix = ` ${fgCollapsedRail(theme, color, GROUP_CALL_MARKER)} `;
1310
- const budget = Math.max(1, width - visibleWidth(prefix));
1311
- const label = groupedChildLabel(row, budget, theme, color);
1298
+ const prefix = `${fgCollapsedRail(theme, GROUP_CALL_MARKER)} `;
1299
+ const marker = collapsedCallMarker(row, theme, color);
1300
+ const budget = Math.max(
1301
+ 1,
1302
+ width - visibleWidth(prefix) - visibleWidth(marker),
1303
+ );
1304
+ const label = collapsedChildLabel(row, budget, theme, color);
1312
1305
  const outcome = collapsedOutcome(row, budget, theme);
1313
1306
  const suffix = fgCollapsed(theme, color, "…");
1314
1307
  lines.push(
1315
1308
  prefix +
1309
+ marker +
1316
1310
  (outcome
1317
1311
  ? fitSummaryTail(label, outcome, budget, suffix)
1318
1312
  : fitSummary(label, budget, suffix)),
1319
1313
  );
1320
- if (row.toolName === "edit")
1321
- lines.push(...renderedEditDiffLines(row, theme));
1322
1314
  segments.push({ row, lines });
1323
1315
  }
1324
1316
  return segments;
@@ -1388,8 +1380,7 @@ function renderGroupedToolRows(
1388
1380
  width,
1389
1381
  ),
1390
1382
  ];
1391
- // The leading blank and the first segment (tool-name header + call line)
1392
- // both belong to the first member; every later member owns its lines.
1383
+ // The first member owns the leading blank; every member owns its call line.
1393
1384
  const heights = segments.map((segment, index) => ({
1394
1385
  component: segment.row,
1395
1386
  height: index === 0 ? segment.lines.length + 1 : segment.lines.length,
@@ -1617,6 +1608,7 @@ function installPresentationPatch(): PresentationPatchState | undefined {
1617
1608
  const state: PresentationPatchState = {
1618
1609
  owners: 1,
1619
1610
  collapseParallel: envEnabled(COLLAPSE_PARALLEL_ENV, true),
1611
+ expandedTools: envToolNames(EXPANDED_TOOLS_ENV),
1620
1612
  groupCache: new WeakMap(),
1621
1613
  collapsedCache: new WeakMap(),
1622
1614
  rowVersions: new WeakMap(),
@@ -1624,7 +1616,7 @@ function installPresentationPatch(): PresentationPatchState | undefined {
1624
1616
  originalRender: proto.render,
1625
1617
  originalUpdateDisplay: proto.updateDisplay,
1626
1618
  };
1627
- const patchedUpdateDisplay = function updateDisplayWithCollapsedResult(
1619
+ const patchedUpdateDisplay = function updateDisplayWithToolPresentation(
1628
1620
  this: ToolExecutionRow,
1629
1621
  ): void {
1630
1622
  // Transcript rerenders reuse settled caches, but a real component
@@ -1634,6 +1626,17 @@ function installPresentationPatch(): PresentationPatchState | undefined {
1634
1626
  state.groupCache.delete(this);
1635
1627
  state.collapsedCache.delete(this);
1636
1628
  state.originalUpdateDisplay.call(this);
1629
+ // Rows for an always-expanded tool skip the collapsed presentation.
1630
+ // Forcing the flag here, before grouping decides whether the row is
1631
+ // eligible, keeps Pi's native expanded block authoritative; it also
1632
+ // re-expands the row after a global collapse.
1633
+ if (
1634
+ this.expanded === false &&
1635
+ state.expandedTools.has(this.toolName ?? "") &&
1636
+ typeof this.setExpanded === "function"
1637
+ ) {
1638
+ this.setExpanded(true);
1639
+ }
1637
1640
  };
1638
1641
  // Pi 0.85 wraps settled tool results in a click-to-expand MouseRegion.
1639
1642
  // The custom collapsed row replaces those lines with a single summary, so
package/src/muted.ts CHANGED
@@ -83,12 +83,8 @@ export function fgCollapsed(
83
83
  // guaranteed lighter than the collapsed color (e.g. catppuccin-latte's `dim`
84
84
  // resolves to the same overlay0 as syntaxComment), so apply the terminal's
85
85
  // faint attribute: it lightens toward the background on light themes and
86
- // darkens on dark ones. Error rails keep their full-strength semantic red.
87
- export function fgCollapsedRail(
88
- theme: CollapsedTheme,
89
- color: string,
90
- text: string,
91
- ): string {
92
- const styled = fgCollapsed(theme, color, text);
93
- return color === "error" ? styled : `\x1b[2m${styled}\x1b[22m`;
86
+ // darkens on dark ones. The rail keeps this muted weight even on failed
87
+ // rows, where only the row's text turns error-colored.
88
+ export function fgCollapsedRail(theme: CollapsedTheme, text: string): string {
89
+ return `\x1b[2m${fgCollapsed(theme, "muted", text)}\x1b[22m`;
94
90
  }