@agentuity/coder-tui 3.0.0-alpha.6 → 3.0.0-beta.0

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.
Files changed (55) hide show
  1. package/dist/agentuity-cli.d.ts +12 -0
  2. package/dist/agentuity-cli.d.ts.map +1 -0
  3. package/dist/agentuity-cli.js +178 -0
  4. package/dist/agentuity-cli.js.map +1 -0
  5. package/dist/aigateway.d.ts +4 -0
  6. package/dist/aigateway.d.ts.map +1 -0
  7. package/dist/aigateway.js +178 -0
  8. package/dist/aigateway.js.map +1 -0
  9. package/dist/footer.d.ts +3 -2
  10. package/dist/footer.d.ts.map +1 -1
  11. package/dist/footer.js +50 -16
  12. package/dist/footer.js.map +1 -1
  13. package/dist/hub-overlay-state.d.ts.map +1 -1
  14. package/dist/hub-overlay-state.js +3 -1
  15. package/dist/hub-overlay-state.js.map +1 -1
  16. package/dist/hub-overlay.d.ts.map +1 -1
  17. package/dist/hub-overlay.js +12 -3
  18. package/dist/hub-overlay.js.map +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +53 -30
  22. package/dist/index.js.map +1 -1
  23. package/dist/local-init-filter.d.ts +5 -0
  24. package/dist/local-init-filter.d.ts.map +1 -0
  25. package/dist/local-init-filter.js +40 -0
  26. package/dist/local-init-filter.js.map +1 -0
  27. package/dist/protocol.d.ts +2 -0
  28. package/dist/protocol.d.ts.map +1 -1
  29. package/dist/remote-session.d.ts.map +1 -1
  30. package/dist/remote-session.js +12 -6
  31. package/dist/remote-session.js.map +1 -1
  32. package/dist/renderers.d.ts.map +1 -1
  33. package/dist/renderers.js +53 -1
  34. package/dist/renderers.js.map +1 -1
  35. package/dist/startup-logo.d.ts +3 -0
  36. package/dist/startup-logo.d.ts.map +1 -0
  37. package/dist/startup-logo.js +212 -0
  38. package/dist/startup-logo.js.map +1 -0
  39. package/dist/subagent-tool-selection.d.ts +3 -0
  40. package/dist/subagent-tool-selection.d.ts.map +1 -0
  41. package/dist/subagent-tool-selection.js +22 -0
  42. package/dist/subagent-tool-selection.js.map +1 -0
  43. package/package.json +6 -6
  44. package/src/agentuity-cli.ts +225 -0
  45. package/src/aigateway.ts +256 -0
  46. package/src/footer.ts +62 -15
  47. package/src/hub-overlay-state.ts +4 -1
  48. package/src/hub-overlay.ts +14 -3
  49. package/src/index.ts +59 -32
  50. package/src/local-init-filter.ts +54 -0
  51. package/src/protocol.ts +2 -0
  52. package/src/remote-session.ts +12 -6
  53. package/src/renderers.ts +61 -1
  54. package/src/startup-logo.ts +255 -0
  55. package/src/subagent-tool-selection.ts +33 -0
@@ -1,3 +1,5 @@
1
+ import { formatToolDisplay } from './agentuity-cli.ts';
2
+
1
3
  export interface StreamBuffer {
2
4
  output: string;
3
5
  thinking: string;
@@ -78,7 +80,8 @@ export function buildProjectionFromEntries(
78
80
  const type = typeof entry.type === 'string' ? entry.type : '';
79
81
  if (type === 'tool_call') {
80
82
  const toolName = typeof entry.toolName === 'string' ? entry.toolName : 'tool';
81
- append('output', `[tool_call] ${toolName}\n\n`, entry.taskId);
83
+ const display = formatToolDisplay(toolName, entry.toolArgs);
84
+ append('output', `[tool_call] ${display.fullLabel}\n\n`, entry.taskId);
82
85
  continue;
83
86
  }
84
87
 
@@ -10,6 +10,7 @@ import {
10
10
  type StreamProjectionSource,
11
11
  } from './hub-overlay-state.ts';
12
12
  import { applyCoderAuthHeaders } from './auth.ts';
13
+ import { formatToolDisplay } from './agentuity-cli.ts';
13
14
  import { truncateToWidth } from './renderers.ts';
14
15
  import type {
15
16
  ConversationEntry as HubConversationEntry,
@@ -2128,6 +2129,13 @@ export class HubOverlay implements Component, Focusable {
2128
2129
  ? data.toolName
2129
2130
  : 'tool';
2130
2131
  const input = data?.args ?? data?.input;
2132
+ const display = formatToolDisplay(
2133
+ name,
2134
+ typeof input === 'string' || (input && typeof input === 'object')
2135
+ ? (input as string | Record<string, unknown>)
2136
+ : undefined
2137
+ );
2138
+ if (display.branded) return `tool_call ${display.fullLabel}`;
2131
2139
  const summarized = summarizeToolCall(name, input);
2132
2140
  if (summarized) return summarized;
2133
2141
  const argsPreview = summarizeArgs(input, 90);
@@ -2171,15 +2179,18 @@ export class HubOverlay implements Component, Focusable {
2171
2179
  const failed = data?.isError === true || details?.error === true;
2172
2180
  return `${header}\n${failed ? 'failed' : `done${duration}`}`;
2173
2181
  }
2174
- return `tool_result ${name}`;
2182
+ const display = formatToolDisplay(name, input);
2183
+ return `tool_result ${display.toolName}`;
2175
2184
  }
2176
2185
 
2177
2186
  if (eventName === 'agent_progress') {
2178
2187
  const agent = typeof data?.agentName === 'string' ? data.agentName : 'agent';
2179
2188
  const status = typeof data?.status === 'string' ? data.status : 'progress';
2180
- const toolName = typeof data?.currentTool === 'string' ? data.currentTool : '';
2189
+ const rawToolName = typeof data?.currentTool === 'string' ? data.currentTool : '';
2181
2190
  const toolArgsRaw = typeof data?.currentToolArgs === 'string' ? data.currentToolArgs : '';
2182
- const toolArgs = toolArgsRaw ? truncateToWidth(normalize(toolArgsRaw), 80) : '';
2191
+ const display = formatToolDisplay(rawToolName, toolArgsRaw || undefined);
2192
+ const toolName = display.toolName;
2193
+ const toolArgs = display.toolArgs ? truncateToWidth(normalize(display.toolArgs), 80) : '';
2183
2194
 
2184
2195
  // Deltas are already represented in rendered stream mode; skip them in event mode
2185
2196
  // to avoid noisy, low-signal token lines.
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type {
1
+ import {
2
+ createBashToolDefinition,
2
3
  AgentToolResult,
3
4
  ExtensionAPI,
4
5
  ExtensionContext,
@@ -13,6 +14,7 @@ import { processActions } from './handlers.ts';
13
14
  import { getToolRenderers } from './renderers.ts';
14
15
  import { setupCoderFooter, type ObserverState } from './footer.ts';
15
16
  import { setupTitlebar } from './titlebar.ts';
17
+ import { setupStartupLogo } from './startup-logo.ts';
16
18
  import { registerAgentCommands } from './commands.ts';
17
19
  import { AgentManagerOverlay } from './overlay.ts';
18
20
  import { ChainEditorOverlay, type ChainResult } from './chain-preview.ts';
@@ -22,6 +24,10 @@ import { setNativeRemoteExtensionContext } from './native-remote-ui-context.ts';
22
24
  import { handleRemoteUiRequest } from './remote-ui-handler.ts';
23
25
  import { buildInboundRpcPromptText, getInboundRpcDeliverAs } from './inbound-rpc.ts';
24
26
  import { applyCoderAuthHeaders, getCoderAuthCurlArgs } from './auth.ts';
27
+ import { formatToolDisplay } from './agentuity-cli.ts';
28
+ import { setupAIGateway } from './aigateway.ts';
29
+ import { adaptInitMessageForLocalTui } from './local-init-filter.ts';
30
+ import { selectSubAgentToolNames } from './subagent-tool-selection.ts';
25
31
  import type {
26
32
  HubAction,
27
33
  HubResponse,
@@ -275,6 +281,15 @@ async function fetchInitMessage(hubUrl: string, agentRole?: string): Promise<Ini
275
281
  }
276
282
 
277
283
  export function agentuityCoderHub(pi: ExtensionAPI) {
284
+ process.env.AGENTUITY_AGENT_MODE = 'coder'; // let the agentuity cli know we're inside coder
285
+
286
+ // Register the startup header before Hub bootstrap so `pi -e .` works for
287
+ // local visual testing without Agentuity Coder environment variables.
288
+ setupStartupLogo(pi);
289
+
290
+ // Register the AI Gateway
291
+ setupAIGateway(pi);
292
+
278
293
  const hubUrl = process.env[HUB_URL_ENV];
279
294
  if (!hubUrl) return;
280
295
 
@@ -283,6 +298,7 @@ export function agentuityCoderHub(pi: ExtensionAPI) {
283
298
  // to an existing sandbox session. The full UI is set up (tools, commands, /hub)
284
299
  // but user input is relayed to the remote sandbox instead of the local Pi agent.
285
300
  const remoteSessionId = process.env[REMOTE_SESSION_ENV] || null;
301
+ const isRemoteSession = Boolean(remoteSessionId);
286
302
  const isNativeRemote = !!process.env[NATIVE_REMOTE_ENV];
287
303
  if (remoteSessionId) {
288
304
  log(`Remote mode: will connect as controller to session ${remoteSessionId}`);
@@ -298,7 +314,10 @@ export function agentuityCoderHub(pi: ExtensionAPI) {
298
314
  // This is how we discover what tools/agents the server provides.
299
315
  // ══════════════════════════════════════════════
300
316
 
301
- const initMsg = fetchInitMessageSync(hubUrl, agentRole);
317
+ const initialInitMsg = fetchInitMessageSync(hubUrl, agentRole);
318
+ const initMsg = initialInitMsg
319
+ ? adaptInitMessageForLocalTui(initialInitMsg, { isRemoteSession })
320
+ : null;
302
321
 
303
322
  if (!initMsg) {
304
323
  log('Hub not reachable — no tools or agents registered');
@@ -411,6 +430,18 @@ export function agentuityCoderHub(pi: ExtensionAPI) {
411
430
  // Titlebar: branding + spinner (registers its own event handlers)
412
431
  setupTitlebar(pi);
413
432
 
433
+ // Override Pi's built-in bash call-row rendering so local transcript rows
434
+ // can brand Agentuity CLI invocations without changing bash execution/result behavior.
435
+ const hasBashTool = serverTools.some((tool) => tool.name === 'bash');
436
+ const localBashRenderers = hasBashTool ? getToolRenderers('bash') : undefined;
437
+ if (hasBashTool && localBashRenderers?.renderCall) {
438
+ const bashToolDefinition = createBashToolDefinition(process.cwd());
439
+ pi.registerTool({
440
+ ...bashToolDefinition,
441
+ renderCall: localBashRenderers.renderCall as ToolDefinition['renderCall'],
442
+ });
443
+ }
444
+
414
445
  // ══════════════════════════════════════════════
415
446
  // WebSocket client for runtime communication (tool execution + events)
416
447
  // ══════════════════════════════════════════════
@@ -453,9 +484,10 @@ export function agentuityCoderHub(pi: ExtensionAPI) {
453
484
  }
454
485
 
455
486
  function applyInitMessage(nextInit: InitMessage): void {
456
- cachedInitMessage = nextInit;
457
- if (nextInit.sessionId) currentSessionId = nextInit.sessionId;
458
- if (nextInit.config) hubConfig = nextInit.config;
487
+ const effectiveInit = adaptInitMessageForLocalTui(nextInit, { isRemoteSession });
488
+ cachedInitMessage = effectiveInit;
489
+ if (effectiveInit.sessionId) currentSessionId = effectiveInit.sessionId;
490
+ if (effectiveInit.config) hubConfig = effectiveInit.config;
459
491
  }
460
492
 
461
493
  client.onInitMessage = (nextInit) => {
@@ -1448,7 +1480,7 @@ export function agentuityCoderHub(pi: ExtensionAPI) {
1448
1480
  }
1449
1481
 
1450
1482
  // Set up Coder footer (powerline: model or active agent > branch > status + observer count)
1451
- setupCoderFooter(ctx, getHubUiStatus, getObserverState);
1483
+ setupCoderFooter(pi, ctx, getHubUiStatus, getObserverState);
1452
1484
 
1453
1485
  // Fire-and-forget: fetch session snapshot for label + initial observer count.
1454
1486
  // Uses the Hub REST endpoint — non-blocking, best-effort.
@@ -1755,13 +1787,7 @@ async function runSubAgent(
1755
1787
  const { piSdk, piAi } = await loadPiSdk();
1756
1788
  // Runtime-resolved dynamic imports — exact types unavailable statically
1757
1789
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1758
- const {
1759
- createAgentSession,
1760
- DefaultResourceLoader,
1761
- SessionManager,
1762
- createCodingTools,
1763
- createReadOnlyTools,
1764
- } = piSdk as any;
1790
+ const { createAgentSession, DefaultResourceLoader, getAgentDir, SessionManager } = piSdk as any;
1765
1791
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1766
1792
  const { getModel } = piAi as any;
1767
1793
 
@@ -1783,13 +1809,16 @@ async function runSubAgent(
1783
1809
  // Sub-agents get Hub tools (memory, context7, etc.) via extensionFactories
1784
1810
  // so they work in both driver and TUI mode.
1785
1811
  const hubTools = agentConfig.hubTools ?? [];
1812
+ const cwd = process.cwd();
1813
+ const agentDir = getAgentDir();
1786
1814
 
1787
1815
  // Resource loader — no extensions (prevents recursive task tool registration),
1788
1816
  // no skills, agent's system prompt injected directly.
1789
1817
  // Hub tools are injected via extensionFactories so sub-agents can use
1790
1818
  // memory_recall, context7_search, etc.
1791
1819
  const subLoader = new DefaultResourceLoader({
1792
- cwd: process.cwd(),
1820
+ cwd,
1821
+ agentDir,
1793
1822
  noExtensions: true,
1794
1823
  extensionFactories:
1795
1824
  hubTools.length > 0
@@ -1808,9 +1837,12 @@ async function runSubAgent(
1808
1837
  });
1809
1838
  await subLoader.reload();
1810
1839
 
1811
- // Select tools based on readOnly flag
1812
- const cwd = process.cwd();
1813
- const tools = agentConfig.readOnly ? createReadOnlyTools(cwd) : createCodingTools(cwd);
1840
+ // Pi v0.68.x uses a name allowlist for both built-in and extension/custom tools.
1841
+ const builtInToolNames = selectSubAgentToolNames(agentConfig);
1842
+ const hubToolNames = hubTools
1843
+ .map((tool) => (typeof tool.name === 'string' ? tool.name.trim() : ''))
1844
+ .filter((name): name is string => name.length > 0);
1845
+ const tools = Array.from(new Set([...builtInToolNames, ...hubToolNames]));
1814
1846
 
1815
1847
  const { session } = await createAgentSession({
1816
1848
  // subModel is already untyped (from dynamic import) — createAgentSession is also dynamically imported
@@ -1824,7 +1856,8 @@ async function runSubAgent(
1824
1856
  | 'xhigh',
1825
1857
  tools,
1826
1858
  resourceLoader: subLoader,
1827
- sessionManager: SessionManager.inMemory('/tmp'),
1859
+ // Pi now tracks cwd per session, so bind in-memory sub-agents to the actual repo cwd.
1860
+ sessionManager: SessionManager.inMemory(cwd),
1828
1861
  });
1829
1862
  await session.bindExtensions({});
1830
1863
 
@@ -1860,25 +1893,19 @@ async function runSubAgent(
1860
1893
 
1861
1894
  if (evt.type === 'tool_execution_start') {
1862
1895
  const toolName = evt.toolName || evt.name || evt.tool || 'unknown';
1863
- let toolArgs = '';
1864
- if (evt.args && typeof evt.args === 'object') {
1865
- const args = evt.args as Record<string, unknown>;
1866
- if (args.command) toolArgs = String(args.command).slice(0, 60);
1867
- else if (args.filePath || args.path)
1868
- toolArgs = String(args.filePath || args.path);
1869
- else if (args.pattern) toolArgs = String(args.pattern).slice(0, 40);
1870
- else {
1871
- const first = Object.values(args)[0];
1872
- if (first) toolArgs = String(first).slice(0, 40);
1873
- }
1874
- }
1896
+ const display = formatToolDisplay(
1897
+ toolName,
1898
+ typeof evt.args === 'string' || (evt.args && typeof evt.args === 'object')
1899
+ ? (evt.args as string | Record<string, unknown>)
1900
+ : undefined
1901
+ );
1875
1902
 
1876
1903
  onProgress({
1877
1904
  agentName: agentConfig.name,
1878
1905
  status: 'tool_start',
1879
1906
  toolCallId: typeof evt.toolCallId === 'string' ? evt.toolCallId : undefined,
1880
- currentTool: toolName,
1881
- currentToolArgs: toolArgs,
1907
+ currentTool: display.toolName,
1908
+ currentToolArgs: display.toolArgs,
1882
1909
  elapsed,
1883
1910
  });
1884
1911
  } else if (evt.type === 'tool_execution_end') {
@@ -0,0 +1,54 @@
1
+ import type { AgentDefinition, HubToolDefinition, InitMessage } from './protocol.ts';
2
+
3
+ const LOCAL_TUI_HIDDEN_HUB_TOOL_NAMES = new Set(['sandbox_exec']);
4
+
5
+ function filterHubToolsForLocalTui(tools?: HubToolDefinition[]): HubToolDefinition[] | undefined {
6
+ if (!tools) return tools;
7
+
8
+ const filtered = tools.filter((tool) => !LOCAL_TUI_HIDDEN_HUB_TOOL_NAMES.has(tool.name));
9
+ return filtered.length === tools.length ? tools : filtered;
10
+ }
11
+
12
+ function filterAgentHubToolsForLocalTui(agents?: AgentDefinition[]): AgentDefinition[] | undefined {
13
+ if (!agents) return agents;
14
+
15
+ let changed = false;
16
+ const filteredAgents = agents.map((agent) => {
17
+ const filteredHubTools = filterHubToolsForLocalTui(agent.hubTools);
18
+ if (filteredHubTools === agent.hubTools) {
19
+ return agent;
20
+ }
21
+
22
+ changed = true;
23
+ return {
24
+ ...agent,
25
+ hubTools: filteredHubTools && filteredHubTools.length > 0 ? filteredHubTools : undefined,
26
+ };
27
+ });
28
+
29
+ return changed ? filteredAgents : agents;
30
+ }
31
+
32
+ export function adaptInitMessageForLocalTui(
33
+ init: InitMessage,
34
+ options: {
35
+ isRemoteSession: boolean;
36
+ }
37
+ ): InitMessage {
38
+ if (options.isRemoteSession) {
39
+ return init;
40
+ }
41
+
42
+ const tools = filterHubToolsForLocalTui(init.tools);
43
+ const agents = filterAgentHubToolsForLocalTui(init.agents);
44
+
45
+ if (tools === init.tools && agents === init.agents) {
46
+ return init;
47
+ }
48
+
49
+ return {
50
+ ...init,
51
+ tools,
52
+ agents,
53
+ };
54
+ }
package/src/protocol.ts CHANGED
@@ -75,6 +75,7 @@ export type HubAction =
75
75
  export interface AgentDefinition {
76
76
  name: string;
77
77
  displayName?: string;
78
+ source?: 'builtin' | 'custom';
78
79
  description: string;
79
80
  systemPrompt: string;
80
81
  model?: string;
@@ -84,6 +85,7 @@ export interface AgentDefinition {
84
85
  readOnly?: boolean;
85
86
  hubTools?: HubToolDefinition[];
86
87
  capabilities?: string[];
88
+ strictToolSelection?: boolean;
87
89
  status?: 'available' | 'busy' | 'offline';
88
90
  }
89
91
 
@@ -23,6 +23,7 @@ import {
23
23
  type RemoteLifecycleState,
24
24
  } from './remote-lifecycle.ts';
25
25
  import { resolveCoderOrgId } from './auth.ts';
26
+ import { formatToolDisplay } from './agentuity-cli.ts';
26
27
 
27
28
  const DEBUG = !!process.env['AGENTUITY_DEBUG'];
28
29
 
@@ -1001,18 +1002,23 @@ export async function setupRemoteMode(
1001
1002
  break;
1002
1003
 
1003
1004
  case 'tool_execution_start': {
1004
- const tool = (event as { toolName?: string }).toolName ?? 'tool';
1005
- currentTool = tool;
1005
+ const rawTool = (event as { toolName?: string }).toolName ?? 'tool';
1006
+ const rawArgs = (event as { args?: string | Record<string, unknown> }).args;
1007
+ const display = formatToolDisplay(rawTool, rawArgs);
1008
+ currentTool = display.toolName;
1006
1009
  if (extensionCtxRef?.hasUI) {
1007
- setNonLifecycleWorkingMessage(`Running ${tool}...`);
1008
- extensionCtxRef.ui.setStatus('remote_activity', `Running ${tool}...`);
1010
+ setNonLifecycleWorkingMessage(`Running ${display.fullLabel}...`);
1011
+ extensionCtxRef.ui.setStatus('remote_activity', `Running ${display.fullLabel}...`);
1009
1012
  }
1010
- log(`Tool: ${tool}`);
1013
+ log(`Tool: ${display.fullLabel}`);
1011
1014
  break;
1012
1015
  }
1013
1016
 
1014
1017
  case 'tool_execution_end': {
1015
- const tool = (event as { toolName?: string }).toolName ?? currentTool ?? 'tool';
1018
+ const rawTool = (event as { toolName?: string }).toolName ?? currentTool ?? 'tool';
1019
+ const rawArgs = (event as { args?: string | Record<string, unknown> }).args;
1020
+ const display = rawArgs ? formatToolDisplay(rawTool, rawArgs) : null;
1021
+ const tool = display?.fullLabel ?? currentTool ?? rawTool;
1016
1022
  currentTool = null;
1017
1023
  if (extensionCtxRef?.hasUI) {
1018
1024
  clearWorkingMessage();
package/src/renderers.ts CHANGED
@@ -12,6 +12,7 @@ import type {
12
12
  AgentToolResult,
13
13
  } from '@mariozechner/pi-coding-agent';
14
14
  import { Box, Text, Container, type Component } from '@mariozechner/pi-tui';
15
+ import { formatToolDisplay } from './agentuity-cli.ts';
15
16
 
16
17
  // ──────────────────────────────────────────────
17
18
  // Line-safety helper — must be declared before SimpleText so
@@ -125,6 +126,32 @@ function truncate(str: string, max: number): string {
125
126
  return str.slice(0, max - 1) + '\u2026';
126
127
  }
127
128
 
129
+ function toSingleLinePreview(value: string): string {
130
+ return value.replace(/\s+/g, ' ').trim();
131
+ }
132
+
133
+ function isCommandToolName(toolName: string): boolean {
134
+ const normalized = toolName.trim().toLowerCase();
135
+ return normalized === 'bash' || normalized === 'execute_command' || normalized.includes('shell');
136
+ }
137
+
138
+ function getCommandArg(args: Record<string, unknown>): string | undefined {
139
+ const value = args['command'] ?? args['cmd'];
140
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
141
+ }
142
+
143
+ function getCommandTimeoutLabel(args: Record<string, unknown>): string | undefined {
144
+ const timeout = args['timeout'];
145
+ if (typeof timeout === 'number' && Number.isFinite(timeout) && timeout > 0) {
146
+ return `${timeout}s`;
147
+ }
148
+ if (typeof timeout === 'string' && timeout.trim()) {
149
+ const value = timeout.trim();
150
+ return /s$/i.test(value) ? value : `${value}s`;
151
+ }
152
+ return undefined;
153
+ }
154
+
128
155
  // ──────────────────────────────────────────────
129
156
  // Individual tool renderers
130
157
  // ──────────────────────────────────────────────
@@ -709,6 +736,37 @@ function parallelTasksRenderers(): ToolRenderers {
709
736
  };
710
737
  }
711
738
 
739
+ function commandToolRenderers(toolName: string): ToolRenderers {
740
+ return {
741
+ renderCall(args, theme) {
742
+ const display = formatToolDisplay(toolName, args);
743
+ const timeout = getCommandTimeoutLabel(args);
744
+
745
+ if (display.branded) {
746
+ let text = theme.fg('toolTitle', theme.bold(display.toolName));
747
+ if (display.toolArgs) {
748
+ text += theme.fg('accent', ` ${display.toolArgs}`);
749
+ }
750
+ if (timeout) {
751
+ text += theme.fg('dim', ` (timeout ${timeout})`);
752
+ }
753
+ return new SimpleText(text);
754
+ }
755
+
756
+ let text = theme.fg('toolTitle', theme.bold('$ '));
757
+ const commandPreview = getCommandArg(args);
758
+ text += theme.fg(
759
+ 'accent',
760
+ truncate(commandPreview ? toSingleLinePreview(commandPreview) : display.toolName, 80)
761
+ );
762
+ if (timeout) {
763
+ text += theme.fg('dim', ` (timeout ${timeout})`);
764
+ }
765
+ return new SimpleText(text);
766
+ },
767
+ };
768
+ }
769
+
712
770
  // ──────────────────────────────────────────────
713
771
 
714
772
  const RENDERERS: Record<string, () => ToolRenderers> = {
@@ -736,5 +794,7 @@ const RENDERERS: Record<string, () => ToolRenderers> = {
736
794
  */
737
795
  export function getToolRenderers(toolName: string): ToolRenderers | undefined {
738
796
  const factory = RENDERERS[toolName];
739
- return factory?.();
797
+ if (factory) return factory();
798
+ if (isCommandToolName(toolName)) return commandToolRenderers(toolName);
799
+ return undefined;
740
800
  }