pi-cc-extensions 0.8.27 → 0.8.29

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.
@@ -2354,9 +2354,46 @@ function singleToolCallSummary(
2354
2354
  ): { main: string; detail: string } {
2355
2355
  const title = label === toolName ? humanizeToolLabel(label) : label;
2356
2356
  if (!args || typeof args !== "object") return { main: title, detail: "" };
2357
+ const name = toolName.toLowerCase();
2358
+ const value = (fallback: string, ...keys: string[]) => {
2359
+ const found = keys.map((key) => args[key]).find((item) => typeof item === "string" && item);
2360
+ return `${title} ${oneLine(found || fallback, Infinity)}`;
2361
+ };
2357
2362
  if (AGENT_FAMILY_TOOL_NAMES.has(toolName) && args.agent_id) {
2358
2363
  return { main: `${title} ${oneLine(args.agent_id, Infinity)}`, detail: "" };
2359
2364
  }
2365
+ if (name === "agent" || name === "agents") {
2366
+ return {
2367
+ main: value(name === "agent" ? "launch agent" : "launch agents", "description", "prompt"),
2368
+ detail: "",
2369
+ };
2370
+ }
2371
+ if (name === "skill") return { main: value("run skill", "name"), detail: "" };
2372
+ if (name === "enterplanmode" || name === "enter_plan_mode") {
2373
+ return { main: `${title} enable read-only planning`, detail: "" };
2374
+ }
2375
+ if (name === "exitplanmode" || name === "exit_plan_mode") {
2376
+ return { main: `${title} present plan`, detail: "" };
2377
+ }
2378
+ if (name === "taskcreate") return { main: value("create task", "subject"), detail: "" };
2379
+ if (name === "tasklist") return { main: `${title} task list`, detail: "" };
2380
+ if (name === "taskget" || name === "taskupdate") {
2381
+ return { main: value("task", "taskId", "task_id"), detail: "" };
2382
+ }
2383
+ if (name === "taskoutput" || name === "taskstop") {
2384
+ return { main: value("background task", "task_id", "taskId"), detail: "" };
2385
+ }
2386
+ if (name === "taskexecute") {
2387
+ const ids = Array.isArray(args.task_ids)
2388
+ ? args.task_ids
2389
+ : Array.isArray(args.taskIds)
2390
+ ? args.taskIds
2391
+ : [];
2392
+ const summary = ids.length
2393
+ ? `${ids[0]}${ids.length > 1 ? ` (+${ids.length - 1} tasks)` : ""}`
2394
+ : "start tasks";
2395
+ return { main: `${title} ${summary}`, detail: "" };
2396
+ }
2360
2397
  if (toolName === "read") {
2361
2398
  const details = [
2362
2399
  args.offset !== undefined ? `offset=${args.offset}` : "",
@@ -2367,7 +2404,7 @@ function singleToolCallSummary(
2367
2404
  detail: details.length ? ` (${details.join(", ")})` : "",
2368
2405
  };
2369
2406
  }
2370
- const value =
2407
+ const preferred =
2371
2408
  args.path ??
2372
2409
  args.file_path ??
2373
2410
  args.command ??
@@ -2382,8 +2419,8 @@ function singleToolCallSummary(
2382
2419
  args.message;
2383
2420
  return {
2384
2421
  main:
2385
- value !== undefined && value !== null && typeof value !== "object"
2386
- ? `${title} ${oneLine(value, Infinity)}`
2422
+ preferred !== undefined && preferred !== null && typeof preferred !== "object"
2423
+ ? `${title} ${oneLine(preferred, Infinity)}`
2387
2424
  : title,
2388
2425
  detail: "",
2389
2426
  };
@@ -2415,6 +2452,74 @@ export function shouldRenderRichDiff(
2415
2452
  return mode === "on" && !isError && (toolName === "edit" || toolName === "write");
2416
2453
  }
2417
2454
 
2455
+ type ParsedTask = { id: string; status: string; subject: string };
2456
+
2457
+ function parseTaskList(text: string): ParsedTask[] {
2458
+ return text
2459
+ .split("\n")
2460
+ .map((line) => line.match(/^#(\d+) \[([^\]]+)] (.+)$/))
2461
+ .filter((match): match is RegExpMatchArray => Boolean(match))
2462
+ .map((match) => ({ id: match[1]!, status: match[2]!, subject: match[3]! }));
2463
+ }
2464
+
2465
+ function taskListSummary(tasks: ParsedTask[]): string {
2466
+ const counts = { pending: 0, in_progress: 0, completed: 0 };
2467
+ for (const task of tasks) {
2468
+ if (task.status in counts) counts[task.status as keyof typeof counts]++;
2469
+ }
2470
+ return [
2471
+ `${tasks.length} tasks`,
2472
+ counts.in_progress ? `${counts.in_progress} in progress` : "",
2473
+ counts.pending ? `${counts.pending} pending` : "",
2474
+ counts.completed ? `${counts.completed} completed` : "",
2475
+ ]
2476
+ .filter(Boolean)
2477
+ .join(" • ");
2478
+ }
2479
+
2480
+ function renderExpandedTaskResult(
2481
+ toolName: string,
2482
+ text: string,
2483
+ theme: any,
2484
+ isError: boolean,
2485
+ ): any | undefined {
2486
+ if (isError) return undefined;
2487
+ if (toolName === "TaskList") {
2488
+ const tasks = parseTaskList(text);
2489
+ if (!tasks.length) return undefined;
2490
+ const limit = Math.max(1, config.expandedPreviewMaxLines);
2491
+ const rows = tasks.slice(0, limit).map((task) => {
2492
+ const color =
2493
+ task.status === "completed"
2494
+ ? "success"
2495
+ : task.status === "in_progress"
2496
+ ? "warning"
2497
+ : "muted";
2498
+ return ` ${theme.fg("accent", `#${task.id}`)} ${theme.fg(color, task.status)} ${theme.fg("dim", task.subject)}`;
2499
+ });
2500
+ if (tasks.length > rows.length)
2501
+ rows.push(theme.fg("muted", ` … ${tasks.length - rows.length} more tasks`));
2502
+ return new Text(` ↳ ${theme.fg("muted", taskListSummary(tasks))}\n${rows.join("\n")}`, 0, 0);
2503
+ }
2504
+ const line = text.trim();
2505
+ if (!line || line.includes("\n")) return undefined;
2506
+ let formatted: string | undefined;
2507
+ let match: RegExpMatchArray | null;
2508
+ if (
2509
+ toolName === "TaskCreate" &&
2510
+ (match = line.match(/^Task #(\d+) created successfully: (.+)$/))
2511
+ ) {
2512
+ formatted = `${theme.fg("success", "Created task")} ${theme.fg("accent", `#${match[1]}`)} ${theme.fg("muted", match[2])}`;
2513
+ } else if (toolName === "TaskUpdate" && (match = line.match(/^Updated task #(\d+) (.+)$/))) {
2514
+ formatted = `${theme.fg("success", "Updated task")} ${theme.fg("accent", `#${match[1]}`)} ${theme.fg("muted", match[2])}`;
2515
+ } else if (toolName === "TaskExecute") {
2516
+ formatted = `${theme.fg("success", "Started")} ${theme.fg("muted", line)}`;
2517
+ } else if (toolName === "TaskStop") {
2518
+ formatted = `${theme.fg("success", "Stopped")} ${theme.fg("muted", line)}`;
2519
+ }
2520
+ return formatted ? new Text(` ↳ ${formatted}`, 0, 0) : undefined;
2521
+ }
2522
+
2418
2523
  /** Wrap an arbitrary tool definition with ccstyle call/result rendering. */
2419
2524
  function createCcstyleTool(
2420
2525
  originalTool: any,
@@ -2493,17 +2598,24 @@ function createCcstyleTool(
2493
2598
 
2494
2599
  const text = textFromResult(result, expanded);
2495
2600
  const args = context?.args;
2601
+ if (expanded) {
2602
+ const taskResult = renderExpandedTaskResult(toolName, text, theme, Boolean(isError));
2603
+ if (taskResult) return taskResult;
2604
+ }
2605
+ const tasks = !isError && toolName === "TaskList" ? parseTaskList(text) : [];
2496
2606
  const outputLines = outputLineCount(result) || countLines(text);
2497
2607
  const lineWord = outputLines === 1 ? "line" : "lines";
2498
2608
  const action = toolName === "read" ? "loaded" : "returned";
2499
- const rendered = isError
2500
- ? text
2501
- ? oneLine(text)
2502
- : "Failed"
2503
- : outputLines
2504
- ? `${outputLines} ${lineWord} ${action}`
2505
- : "Done";
2506
- const expandable = !expanded && hasExpandableDetail(text, args);
2609
+ const rendered = tasks.length
2610
+ ? taskListSummary(tasks)
2611
+ : isError
2612
+ ? text
2613
+ ? oneLine(text)
2614
+ : "Failed"
2615
+ : outputLines
2616
+ ? `${outputLines} ${lineWord} ${action}`
2617
+ : "Done";
2618
+ const expandable = !expanded && (tasks.length > 0 || hasExpandableDetail(text, args));
2507
2619
  const hint = expandable ? theme.fg("muted", " • click to show more") : "";
2508
2620
  const hoveredHint = expandable ? theme.fg("text", " • click to show more") : "";
2509
2621
  if (expanded) {
@@ -2546,7 +2658,7 @@ function createCcstyleTool(
2546
2658
  * Apart from the write override used to capture pre-write content, renderers are
2547
2659
  * applied through ToolExecutionComponent. Patch its lookup once so tools use the
2548
2660
  * same compact fallback shell by default. Tools named in excludeRenderers keep
2549
- * their original renderer; subagent rendering remains protected.
2661
+ * their original renderer.
2550
2662
  */
2551
2663
  const GLOBAL_TOOL_RENDER_PATCH = Symbol.for("pi.ccstyle.global-tool-render-patch");
2552
2664
  const COMPONENT_TOOL_RENDER_MODE = Symbol.for("pi.ccstyle.component-tool-render-mode");
@@ -2557,7 +2669,6 @@ const AGENT_FAMILY_TOOL_NAMES = new Set([
2557
2669
  "get_subagent_result",
2558
2670
  "steer_subagent",
2559
2671
  ]);
2560
- const DEDICATED_SUBAGENT_TOOL_NAMES = new Set(["Agent"]);
2561
2672
 
2562
2673
  type ToolRenderMethods = {
2563
2674
  hasRendererDefinition: (...args: any[]) => boolean;
@@ -2611,7 +2722,6 @@ export function preservesOriginalRenderer(
2611
2722
  builtInToolDefinition?: any,
2612
2723
  excludeRenderers: readonly string[] = config.excludeRenderers,
2613
2724
  ): boolean {
2614
- if (DEDICATED_SUBAGENT_TOOL_NAMES.has(toolName)) return true;
2615
2725
  if (!excludeRenderers.includes(toolName)) return false;
2616
2726
  return [extensionDefinition, builtInToolDefinition].some(
2617
2727
  (definition) =>
@@ -2652,16 +2762,9 @@ function shouldGloballyStyleTool(component: any, patch: GlobalToolRenderPatch):
2652
2762
  return useCcstyle;
2653
2763
  }
2654
2764
 
2655
- function shouldUseSelfShell(component: any, patch: GlobalToolRenderPatch): boolean {
2656
- const definition = component.toolDefinition ?? component.builtInToolDefinition;
2657
- const toolName = String(component.toolName || definition?.name || "");
2658
- const useSelfShell =
2659
- patch.enabled() &&
2660
- DEDICATED_SUBAGENT_TOOL_NAMES.has(toolName) &&
2661
- definition != null &&
2662
- definition.renderShell === undefined;
2663
- component[COMPONENT_TOOL_SELF_SHELL_MODE] = useSelfShell;
2664
- return useSelfShell;
2765
+ function shouldUseSelfShell(component: any, _patch: GlobalToolRenderPatch): boolean {
2766
+ component[COMPONENT_TOOL_SELF_SHELL_MODE] = false;
2767
+ return false;
2665
2768
  }
2666
2769
 
2667
2770
  function getGloballyStyledTool(component: any, patch: GlobalToolRenderPatch): any {
@@ -110,7 +110,55 @@ function humanizeToolName(name: string): string {
110
110
 
111
111
  function toolSummary(tool: any): { main: string; detail: string } {
112
112
  const name = toolName(tool);
113
+ const lowerName = name.toLowerCase();
113
114
  const args = tool?.args ?? {};
115
+ const titled = humanizeToolName(name);
116
+ const value = (fallback: string, ...keys: string[]) => {
117
+ const found = keys.map((key) => args[key]).find((item) => typeof item === "string" && item);
118
+ return `${titled} ${oneLine(found || fallback)}`;
119
+ };
120
+ if (lowerName === "agent" || lowerName === "agents") {
121
+ return {
122
+ main: value(
123
+ lowerName === "agent" ? "launch agent" : "launch agents",
124
+ "description",
125
+ "prompt",
126
+ ),
127
+ detail: "",
128
+ };
129
+ }
130
+ if (lowerName === "get_subagent_result" || lowerName === "steer_subagent") {
131
+ return {
132
+ main: value(lowerName === "get_subagent_result" ? "agent result" : "steer agent", "agent_id"),
133
+ detail: "",
134
+ };
135
+ }
136
+ if (lowerName === "skill") return { main: value("run skill", "name"), detail: "" };
137
+ if (lowerName === "enterplanmode" || lowerName === "enter_plan_mode") {
138
+ return { main: `${titled} enable read-only planning`, detail: "" };
139
+ }
140
+ if (lowerName === "exitplanmode" || lowerName === "exit_plan_mode") {
141
+ return { main: `${titled} present plan`, detail: "" };
142
+ }
143
+ if (lowerName === "taskcreate") return { main: value("create task", "subject"), detail: "" };
144
+ if (lowerName === "tasklist") return { main: `${titled} task list`, detail: "" };
145
+ if (lowerName === "taskget" || lowerName === "taskupdate") {
146
+ return { main: value("task", "taskId", "task_id"), detail: "" };
147
+ }
148
+ if (lowerName === "taskoutput" || lowerName === "taskstop") {
149
+ return { main: value("background task", "task_id", "taskId"), detail: "" };
150
+ }
151
+ if (lowerName === "taskexecute") {
152
+ const ids = Array.isArray(args.task_ids)
153
+ ? args.task_ids
154
+ : Array.isArray(args.taskIds)
155
+ ? args.taskIds
156
+ : [];
157
+ return {
158
+ main: `${titled} ${ids.length ? `${ids[0]}${ids.length > 1 ? ` (+${ids.length - 1} tasks)` : ""}` : "start tasks"}`,
159
+ detail: "",
160
+ };
161
+ }
114
162
  if (name === "read") {
115
163
  const details = [
116
164
  args.offset !== undefined ? `offset=${args.offset}` : "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cc-extensions",
3
- "version": "0.8.27",
3
+ "version": "0.8.29",
4
4
  "description": "A Pi productivity suite with Claude Code-style UI, structured questionnaires, context inspection, and agent/session references",
5
5
  "type": "module",
6
6
  "keywords": [