@pi-kaush/pi-tool-call-markers 0.3.0 → 0.3.1

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,6 +2,12 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Restyle subagent rows: the subagent marker is now `↪` (was `&`), and single
6
+ calls drop the `subagent` tool label — `↪ 🤖 [coder][c3po] Implement the…` —
7
+ with chain/parallel step lines reordered to emoji, profile badge, name badge
8
+ (`1. 🐝 [workhorse][bee] …`). Single-call headings are now scraped too, so
9
+ agent emojis appear on single rows, not just chain/parallel steps.
10
+
5
11
  - Color collapsed rows (tool calls, `+ Thought`, subagents) from the
6
12
  theme's `syntaxComment` token — which ships with every theme — instead of
7
13
  the louder muted/toolTitle/toolOutput split. The bolded tool name and the
package/README.md CHANGED
@@ -34,19 +34,19 @@ Collapsed tool rows use semantic theme colors with no gear, background fill, box
34
34
 
35
35
  ## Subagent plans
36
36
 
37
- A recognized `subagent` call renders as an unboxed plan in the shared tool aesthetic, marked with `&` instead of the ordinary `%` tool marker:
37
+ A recognized `subagent` call renders as an unboxed plan in the shared tool aesthetic, marked with `↪` instead of the ordinary `%` tool marker:
38
38
 
39
39
  ```text
40
- & subagent chain (3 steps) [repo-review]
40
+ subagent chain (3 steps) [repo-review]
41
41
  1. 🐝 bee [workhorse] Challenge the compatibility conclusion…
42
42
  2. 🐝 bee …
43
43
  ```
44
44
 
45
- 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.
45
+ Single calls stay on one `↪ [<emoji>] [<profile>][<agent>] <task preview>` row — no tool label; the `↪` marker identifies it. Chain calls get a heading with the kind, count, and scope followed by numbered steps (parallel tasks list without numbers), with each step ordered emoji, profile badge, name badge. Agent display names (emoji + name) are scraped from the native plan component — including the single-call heading — with an args fallback, and render in `accent`; everything else stays muted, and failed subagents go full red. Subagents never join ordinary tool groups.
46
46
 
47
47
  While a subagent runs, the plan headline's tail shows live progress from the streamed result details — `→ 1 turn · provider/model` in the warning tone — and a settled call keeps the same `→ N turns · provider/model` summary in muted (turns aggregate across tasks; the model shows only when every task used the same one).
48
48
 
49
- 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.
49
+ 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.
50
50
 
51
51
  ## Edit diffs
52
52
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-kaush/pi-tool-call-markers",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
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
@@ -20,7 +20,7 @@ import { fgCollapsed } from "./muted.ts";
20
20
 
21
21
  const OUTER_INSET = 2;
22
22
  const GROUP_MARKER = "%";
23
- const SUBAGENT_MARKER = "&";
23
+ const SUBAGENT_MARKER = "";
24
24
  const PRESENTATION_PATCHED = Symbol.for("kg.pi.toolPresentation.v3");
25
25
  const LEGACY_PRESENTATION_PATCHED = Symbol.for("kg.pi.toolPresentation.v2");
26
26
  const GROUPING_PATCHED = Symbol.for("kg.pi.toolGrouping.v1");
@@ -1028,6 +1028,19 @@ function parseStepContent(content: string): {
1028
1028
  };
1029
1029
  }
1030
1030
 
1031
+ // Splits a scraped "emoji name" display into its emoji prefix and bare name;
1032
+ // plain names pass through with no emoji.
1033
+ function splitEmojiName(
1034
+ displayName: string,
1035
+ bareName: string,
1036
+ ): { emoji: string; name: string } {
1037
+ const tokens = displayName.trim().split(/\s+/).filter(Boolean);
1038
+ if (tokens.length > 1 && tokens[tokens.length - 1] === bareName) {
1039
+ return { emoji: tokens.slice(0, -1).join(" "), name: bareName };
1040
+ }
1041
+ return { emoji: "", name: bareName };
1042
+ }
1043
+
1031
1044
  // Agent display names (emoji + name) come from the subagent extension's own
1032
1045
  // call component when available; args only carry the bare agent name.
1033
1046
  function scrapeSubagentDisplayNames(
@@ -1042,10 +1055,16 @@ function scrapeSubagentDisplayNames(
1042
1055
  try {
1043
1056
  for (const raw of component.render(120)) {
1044
1057
  const line = sanitizeInline(stripAnsi(raw)).trim();
1045
- if (!line || /^subagent\b/.test(line)) continue;
1046
- const { displayName, bareName } = parseStepContent(
1047
- line.replace(/^\d+\.\s*/, ""),
1048
- );
1058
+ if (!line) continue;
1059
+ // Single calls carry the agent name on their "subagent <name>" heading
1060
+ // line; stripping the heading word (and step numbers) lets both shapes
1061
+ // flow through the same parse. Heading remainders without an emoji
1062
+ // (e.g. "chain (2 steps)") degrade to displayName === bareName and are
1063
+ // skipped by the guard below.
1064
+ const content = line
1065
+ .replace(/^subagent\b\s*/, "")
1066
+ .replace(/^\d+\.\s*/, "");
1067
+ const { displayName, bareName } = parseStepContent(content);
1049
1068
  if (displayName && bareName && displayName !== bareName) {
1050
1069
  names.set(bareName, displayName);
1051
1070
  }
@@ -1091,10 +1110,11 @@ function subagentProgressText(row: ToolExecutionRow): string | undefined {
1091
1110
  return parts.length > 0 ? parts.join(" · ") : undefined;
1092
1111
  }
1093
1112
 
1094
- // Subagents render as an ordinary unboxed tool block: a `%` heading with the
1113
+ // Subagents render as an ordinary unboxed tool block: a `↪` heading with the
1095
1114
  // plan kind/count/scope, then the numbered chain steps or parallel tasks with
1096
- // agent names in accent. Everything else stays muted (or error on failure),
1097
- // matching the shared tool-row aesthetic.
1115
+ // agent identities (emoji, profile badge, name badge) in accent. Single calls
1116
+ // collapse to one row: `↪ <emoji> [<profile>][<name>] <task preview>`. Everything
1117
+ // else stays muted (or error on failure), matching the shared tool-row aesthetic.
1098
1118
  function renderSubagentPlan(
1099
1119
  row: ToolExecutionRow,
1100
1120
  width: number,
@@ -1110,19 +1130,21 @@ function renderSubagentPlan(
1110
1130
  const displayNames = scrapeSubagentDisplayNames(row);
1111
1131
  // Agent/profile/task values are model-supplied; sanitize them like every
1112
1132
  // other collapsed-row text so control bytes cannot reach the terminal.
1113
- const displayOf = (agent: string) =>
1114
- fgCollapsed(
1115
- theme,
1116
- nameColor,
1117
- displayNames.get(agent) ?? sanitizeInline(agent),
1118
- );
1133
+ // Identity order: emoji, then the profile and agent-name badges.
1134
+ const identityOf = (step: SubagentStep) => {
1135
+ const displayName =
1136
+ displayNames.get(step.agent) ?? sanitizeInline(step.agent);
1137
+ const { emoji, name } = splitEmojiName(displayName, step.agent);
1138
+ const accentPart = (text: string) => fgCollapsed(theme, nameColor, text);
1139
+ const emojiPart = emoji ? `${accentPart(emoji)} ` : "";
1140
+ const profilePart = step.profile
1141
+ ? accentPart(`[${sanitizeInline(step.profile)}]`)
1142
+ : "";
1143
+ return `${emojiPart}${profilePart}${accentPart(`[${sanitizeInline(name)}]`)}`;
1144
+ };
1119
1145
  const detailColor = failed ? "error" : "toolOutput";
1120
- const detailOf = (step: SubagentStep) =>
1121
- fgCollapsed(
1122
- theme,
1123
- detailColor,
1124
- `${step.profile ? ` [${sanitizeInline(step.profile)}]` : ""} ${subagentStepPreview(step.task)}`,
1125
- );
1146
+ const previewOf = (step: SubagentStep) =>
1147
+ fgCollapsed(theme, detailColor, ` ${subagentStepPreview(step.task)}`);
1126
1148
 
1127
1149
  const marker = `${fgCollapsed(theme, color, SUBAGENT_MARKER, true)} `;
1128
1150
  const budget = Math.max(1, width - visibleWidth(marker));
@@ -1146,14 +1168,7 @@ function renderSubagentPlan(
1146
1168
 
1147
1169
  if (plan.kind === "single") {
1148
1170
  const step = plan.steps[0]!;
1149
- return [
1150
- headline(
1151
- fgCollapsed(theme, failed ? "error" : "toolTitle", "subagent", true) +
1152
- " " +
1153
- displayOf(step.agent) +
1154
- detailOf(step),
1155
- ),
1156
- ];
1171
+ return [headline(identityOf(step) + previewOf(step))];
1157
1172
  }
1158
1173
 
1159
1174
  const kindLabel =
@@ -1175,7 +1190,7 @@ function renderSubagentPlan(
1175
1190
  plan.kind === "chain"
1176
1191
  ? `${fgCollapsed(theme, color, `${index + 1}.`)} `
1177
1192
  : "";
1178
- lines.push(` ${number}${displayOf(step.agent)}${detailOf(step)}`);
1193
+ lines.push(` ${number}${identityOf(step)}${previewOf(step)}`);
1179
1194
  }
1180
1195
  if (plan.steps.length > shown.length) {
1181
1196
  lines.push(