@ash-cloud/ash-ui 0.0.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/dist/utils.cjs ADDED
@@ -0,0 +1,415 @@
1
+ 'use strict';
2
+
3
+ // src/utils.ts
4
+ function formatToolName(name) {
5
+ if (name.startsWith("mcp__")) {
6
+ const parts = name.split("__");
7
+ if (parts.length >= 3) {
8
+ return `mcp:${parts[1]}:${parts.slice(2).join(":")}`;
9
+ }
10
+ }
11
+ return name;
12
+ }
13
+ function parseMcpToolName(name) {
14
+ if (name.startsWith("mcp__")) {
15
+ const parts = name.split("__");
16
+ if (parts.length >= 3 && parts[1] !== void 0) {
17
+ return {
18
+ serverName: parts[1],
19
+ toolName: parts.slice(2).join("__")
20
+ };
21
+ }
22
+ }
23
+ return null;
24
+ }
25
+ function mapToolToActionType(toolName, input) {
26
+ const inputObj = input || {};
27
+ switch (toolName) {
28
+ case "Bash":
29
+ return {
30
+ action: "command_run",
31
+ command: inputObj.command || "",
32
+ description: inputObj.description
33
+ };
34
+ case "Read":
35
+ return {
36
+ action: "file_read",
37
+ path: inputObj.file_path || "",
38
+ offset: inputObj.offset,
39
+ limit: inputObj.limit
40
+ };
41
+ case "Edit":
42
+ return {
43
+ action: "file_edit",
44
+ path: inputObj.file_path || "",
45
+ oldString: inputObj.old_string,
46
+ newString: inputObj.new_string,
47
+ replaceAll: inputObj.replace_all
48
+ };
49
+ case "Write":
50
+ return {
51
+ action: "file_write",
52
+ path: inputObj.file_path || "",
53
+ content: inputObj.content
54
+ };
55
+ case "Grep":
56
+ return {
57
+ action: "search",
58
+ pattern: inputObj.pattern || "",
59
+ path: inputObj.path,
60
+ glob: inputObj.glob,
61
+ type: inputObj.type
62
+ };
63
+ case "Glob":
64
+ return {
65
+ action: "glob",
66
+ pattern: inputObj.pattern || "",
67
+ path: inputObj.path
68
+ };
69
+ case "WebFetch":
70
+ return {
71
+ action: "web_fetch",
72
+ url: inputObj.url || "",
73
+ prompt: inputObj.prompt
74
+ };
75
+ case "WebSearch":
76
+ return {
77
+ action: "web_search",
78
+ query: inputObj.query || ""
79
+ };
80
+ case "TodoWrite": {
81
+ const todos = inputObj.todos || [];
82
+ const stats = {
83
+ total: todos.length,
84
+ completed: todos.filter((t) => t.status === "completed").length,
85
+ inProgress: todos.filter((t) => t.status === "in_progress").length,
86
+ pending: todos.filter((t) => t.status === "pending").length
87
+ };
88
+ return {
89
+ action: "todo_write",
90
+ todos,
91
+ stats
92
+ };
93
+ }
94
+ default: {
95
+ const mcpParts = parseMcpToolName(toolName);
96
+ if (mcpParts) {
97
+ return {
98
+ action: "mcp_tool",
99
+ serverName: mcpParts.serverName,
100
+ toolName: mcpParts.toolName,
101
+ arguments: input
102
+ };
103
+ }
104
+ return {
105
+ action: "generic_tool",
106
+ toolName: formatToolName(toolName),
107
+ arguments: input
108
+ };
109
+ }
110
+ }
111
+ }
112
+ function generateToolSummary(_toolName, _input, actionType) {
113
+ switch (actionType.action) {
114
+ case "command_run": {
115
+ const cmd = actionType.command;
116
+ return cmd.length > 60 ? cmd.substring(0, 57) + "..." : cmd;
117
+ }
118
+ case "file_read":
119
+ return actionType.path;
120
+ case "file_edit":
121
+ return actionType.path;
122
+ case "file_write":
123
+ return actionType.path;
124
+ case "search":
125
+ return `${actionType.pattern}${actionType.path ? ` in ${actionType.path}` : ""}`;
126
+ case "glob":
127
+ return actionType.pattern;
128
+ case "web_fetch":
129
+ return actionType.url;
130
+ case "web_search":
131
+ return actionType.query;
132
+ case "mcp_tool":
133
+ return `${actionType.serverName}:${actionType.toolName}`;
134
+ case "generic_tool":
135
+ return actionType.toolName;
136
+ case "todo_write": {
137
+ const { stats } = actionType;
138
+ if (stats) {
139
+ return `${stats.completed}/${stats.total} completed`;
140
+ }
141
+ return `${actionType.todos.length} tasks`;
142
+ }
143
+ default:
144
+ return "Unknown tool";
145
+ }
146
+ }
147
+ function extractTextContent(content) {
148
+ if (typeof content === "string") return content;
149
+ if (Array.isArray(content)) {
150
+ return content.filter((item) => typeof item?.text === "string").map((item) => item.text).join("\n");
151
+ }
152
+ if (content && typeof content === "object" && "text" in content) {
153
+ return String(content.text);
154
+ }
155
+ return JSON.stringify(content, null, 2);
156
+ }
157
+ function normalizeToolResult(content) {
158
+ if (typeof content === "string") {
159
+ try {
160
+ const parsed = JSON.parse(content);
161
+ return { type: "json", value: parsed };
162
+ } catch {
163
+ return { type: "markdown", value: content };
164
+ }
165
+ }
166
+ if (Array.isArray(content)) {
167
+ const texts = content.filter(
168
+ (item) => item?.type === "text" && typeof item.text === "string"
169
+ ).map((item) => item.text);
170
+ if (texts.length > 0) {
171
+ const joined = texts.join("\n\n");
172
+ try {
173
+ return { type: "json", value: JSON.parse(joined) };
174
+ } catch {
175
+ return { type: "markdown", value: joined };
176
+ }
177
+ }
178
+ }
179
+ return { type: "json", value: content };
180
+ }
181
+ function parseCommandResult(content) {
182
+ const output = extractTextContent(content);
183
+ if (typeof content === "string") {
184
+ try {
185
+ const parsed = JSON.parse(content);
186
+ if (typeof parsed.exitCode === "number") {
187
+ return {
188
+ exitCode: parsed.exitCode,
189
+ output: parsed.output || output,
190
+ success: parsed.exitCode === 0
191
+ };
192
+ }
193
+ } catch {
194
+ }
195
+ }
196
+ return {
197
+ output,
198
+ success: true
199
+ };
200
+ }
201
+ function createToolCall(toolUse) {
202
+ const actionType = mapToolToActionType(toolUse.name, toolUse.input);
203
+ const summary = generateToolSummary(toolUse.name, toolUse.input, actionType);
204
+ return {
205
+ id: toolUse.id,
206
+ toolName: toolUse.name,
207
+ actionType,
208
+ status: "pending",
209
+ summary,
210
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
211
+ };
212
+ }
213
+ function updateToolCallWithResult(toolCall, content, isError) {
214
+ const updatedToolCall = { ...toolCall };
215
+ const actionType = { ...toolCall.actionType };
216
+ updatedToolCall.status = isError ? "failed" : "success";
217
+ updatedToolCall.completedAt = (/* @__PURE__ */ new Date()).toISOString();
218
+ updatedToolCall.isError = isError;
219
+ if (actionType.action === "command_run") {
220
+ const result = parseCommandResult(content);
221
+ actionType.result = result;
222
+ if (result.exitCode !== void 0 && result.exitCode !== 0) {
223
+ updatedToolCall.status = "failed";
224
+ updatedToolCall.isError = true;
225
+ }
226
+ } else if (actionType.action === "mcp_tool" || actionType.action === "generic_tool") {
227
+ actionType.result = normalizeToolResult(content);
228
+ }
229
+ updatedToolCall.actionType = actionType;
230
+ return updatedToolCall;
231
+ }
232
+ function getActionIcon(actionType) {
233
+ switch (actionType.action) {
234
+ case "command_run":
235
+ return "terminal";
236
+ case "file_read":
237
+ return "file-text";
238
+ case "file_edit":
239
+ return "edit";
240
+ case "file_write":
241
+ return "file-plus";
242
+ case "search":
243
+ return "search";
244
+ case "glob":
245
+ return "folder-search";
246
+ case "web_fetch":
247
+ return "globe";
248
+ case "web_search":
249
+ return "search";
250
+ case "mcp_tool":
251
+ return "plug";
252
+ case "generic_tool":
253
+ return "tool";
254
+ case "todo_write":
255
+ return "list-checks";
256
+ default:
257
+ return "tool";
258
+ }
259
+ }
260
+ function getActionLabel(actionType) {
261
+ switch (actionType.action) {
262
+ case "command_run":
263
+ return "Command";
264
+ case "file_read":
265
+ return "Read";
266
+ case "file_edit":
267
+ return "Edit";
268
+ case "file_write":
269
+ return "Write";
270
+ case "search":
271
+ return "Search";
272
+ case "glob":
273
+ return "Glob";
274
+ case "web_fetch":
275
+ return "Fetch";
276
+ case "web_search":
277
+ return "Search";
278
+ case "mcp_tool":
279
+ return "MCP";
280
+ case "generic_tool":
281
+ return "Tool";
282
+ case "todo_write":
283
+ return "Tasks";
284
+ default:
285
+ return "Tool";
286
+ }
287
+ }
288
+ function formatFileSize(bytes) {
289
+ if (bytes < 1024) return `${bytes} B`;
290
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
291
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
292
+ }
293
+ function formatTimestamp(timestamp) {
294
+ try {
295
+ const date = new Date(timestamp);
296
+ return date.toLocaleTimeString("en-US", {
297
+ hour: "2-digit",
298
+ minute: "2-digit",
299
+ second: "2-digit",
300
+ hour12: false
301
+ });
302
+ } catch {
303
+ return timestamp;
304
+ }
305
+ }
306
+ function truncate(str, maxLength) {
307
+ if (str.length <= maxLength) return str;
308
+ return str.substring(0, maxLength - 3) + "...";
309
+ }
310
+ function cn(...classes) {
311
+ return classes.filter(Boolean).join(" ");
312
+ }
313
+ function groupEntriesForCompactMode(entries, config) {
314
+ const result = [];
315
+ let currentToolGroup = [];
316
+ let toolGroupCounter = 0;
317
+ const flushToolGroup = () => {
318
+ if (currentToolGroup.length > 0) {
319
+ result.push({
320
+ type: "tool_group",
321
+ entries: [...currentToolGroup],
322
+ id: `tool-group-${toolGroupCounter++}`
323
+ });
324
+ currentToolGroup = [];
325
+ }
326
+ };
327
+ for (const entry of entries) {
328
+ if (entry.entryType.type === "tool_call") {
329
+ currentToolGroup.push(entry);
330
+ if (config.breakEveryNToolCalls && config.breakEveryNToolCalls > 0 && currentToolGroup.length >= config.breakEveryNToolCalls) {
331
+ flushToolGroup();
332
+ }
333
+ } else {
334
+ flushToolGroup();
335
+ result.push({ type: "single", entry });
336
+ }
337
+ }
338
+ flushToolGroup();
339
+ return result;
340
+ }
341
+ function extractToolCallsFromGroup(entries) {
342
+ return entries.filter(
343
+ (e) => e.entryType.type === "tool_call"
344
+ ).map((e) => e.entryType.toolCall);
345
+ }
346
+ function parseOptionsFromContent(content) {
347
+ const optionPattern = /(?:\*\*)?Option\s+(\d+)(?:\*\*)?[:\-]\s*([^\n]+)(?:\n((?:(?!\n(?:\*\*)?Option\s+\d).)*?))?/gi;
348
+ const options = [];
349
+ let firstMatchStart = -1;
350
+ let match;
351
+ optionPattern.lastIndex = 0;
352
+ while ((match = optionPattern.exec(content)) !== null) {
353
+ if (firstMatchStart === -1) {
354
+ firstMatchStart = match.index;
355
+ }
356
+ const id = match[1] ?? "";
357
+ const labelRaw = match[2];
358
+ const label = labelRaw ? labelRaw.trim() : "";
359
+ const descriptionRaw = match[3];
360
+ let description;
361
+ if (descriptionRaw) {
362
+ const cleaned = descriptionRaw.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join(" ");
363
+ if (cleaned) {
364
+ description = cleaned;
365
+ }
366
+ }
367
+ options.push({ id, label, description });
368
+ }
369
+ if (options.length >= 2) {
370
+ const preamble = firstMatchStart > 0 ? content.substring(0, firstMatchStart).trim() : "";
371
+ return { preamble, options };
372
+ }
373
+ const questionPattern = /^(.*?(?:what would you like to do\??|choose an option|select.*?:|here are your options.*?:))\s*\n+((?:\d+\.\s+.+\n?)+)/ims;
374
+ const questionMatch = content.match(questionPattern);
375
+ if (questionMatch && questionMatch[1] && questionMatch[2]) {
376
+ const preamble = questionMatch[1].trim();
377
+ const listSection = questionMatch[2];
378
+ const listPattern = /(\d+)\.\s+([^\n]+)/g;
379
+ let listMatch;
380
+ while ((listMatch = listPattern.exec(listSection)) !== null) {
381
+ const listId = listMatch[1] ?? "";
382
+ const listLabelRaw = listMatch[2];
383
+ const listLabel = listLabelRaw ? listLabelRaw.trim() : "";
384
+ options.push({
385
+ id: listId,
386
+ label: listLabel
387
+ });
388
+ }
389
+ if (options.length >= 2) {
390
+ return { preamble, options };
391
+ }
392
+ }
393
+ return null;
394
+ }
395
+
396
+ exports.cn = cn;
397
+ exports.createToolCall = createToolCall;
398
+ exports.extractTextContent = extractTextContent;
399
+ exports.extractToolCallsFromGroup = extractToolCallsFromGroup;
400
+ exports.formatFileSize = formatFileSize;
401
+ exports.formatTimestamp = formatTimestamp;
402
+ exports.formatToolName = formatToolName;
403
+ exports.generateToolSummary = generateToolSummary;
404
+ exports.getActionIcon = getActionIcon;
405
+ exports.getActionLabel = getActionLabel;
406
+ exports.groupEntriesForCompactMode = groupEntriesForCompactMode;
407
+ exports.mapToolToActionType = mapToolToActionType;
408
+ exports.normalizeToolResult = normalizeToolResult;
409
+ exports.parseCommandResult = parseCommandResult;
410
+ exports.parseMcpToolName = parseMcpToolName;
411
+ exports.parseOptionsFromContent = parseOptionsFromContent;
412
+ exports.truncate = truncate;
413
+ exports.updateToolCallWithResult = updateToolCallWithResult;
414
+ //# sourceMappingURL=utils.cjs.map
415
+ //# sourceMappingURL=utils.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts"],"names":[],"mappings":"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","file":"utils.cjs","sourcesContent":["/**\n * @ash-cloud/ash-ui - Utilities\n *\n * Utility functions for normalizing and formatting tool calls\n * for display in agentic UIs.\n */\n\nimport type {\n ActionType,\n ToolResult,\n CommandRunResult,\n NormalizedToolCall,\n NormalizedEntry,\n ToolDisplayConfig,\n TodoItem,\n} from './types';\n\n// =============================================================================\n// Tool Name Formatting\n// =============================================================================\n\n/**\n * Format MCP tool names from mcp__server__tool to mcp:server:tool\n */\nexport function formatToolName(name: string): string {\n if (name.startsWith('mcp__')) {\n const parts = name.split('__');\n if (parts.length >= 3) {\n return `mcp:${parts[1]}:${parts.slice(2).join(':')}`;\n }\n }\n return name;\n}\n\n/**\n * Parse MCP tool name to extract server and tool names\n */\nexport function parseMcpToolName(name: string): { serverName: string; toolName: string } | null {\n if (name.startsWith('mcp__')) {\n const parts = name.split('__');\n if (parts.length >= 3 && parts[1] !== undefined) {\n return {\n serverName: parts[1],\n toolName: parts.slice(2).join('__'),\n };\n }\n }\n return null;\n}\n\n// =============================================================================\n// Action Type Mapping\n// =============================================================================\n\n/**\n * Map a tool name and input to a structured ActionType\n */\nexport function mapToolToActionType(toolName: string, input: unknown): ActionType {\n const inputObj = (input as Record<string, unknown>) || {};\n\n switch (toolName) {\n case 'Bash':\n return {\n action: 'command_run',\n command: (inputObj.command as string) || '',\n description: inputObj.description as string | undefined,\n };\n\n case 'Read':\n return {\n action: 'file_read',\n path: (inputObj.file_path as string) || '',\n offset: inputObj.offset as number | undefined,\n limit: inputObj.limit as number | undefined,\n };\n\n case 'Edit':\n return {\n action: 'file_edit',\n path: (inputObj.file_path as string) || '',\n oldString: inputObj.old_string as string | undefined,\n newString: inputObj.new_string as string | undefined,\n replaceAll: inputObj.replace_all as boolean | undefined,\n };\n\n case 'Write':\n return {\n action: 'file_write',\n path: (inputObj.file_path as string) || '',\n content: inputObj.content as string | undefined,\n };\n\n case 'Grep':\n return {\n action: 'search',\n pattern: (inputObj.pattern as string) || '',\n path: inputObj.path as string | undefined,\n glob: inputObj.glob as string | undefined,\n type: inputObj.type as string | undefined,\n };\n\n case 'Glob':\n return {\n action: 'glob',\n pattern: (inputObj.pattern as string) || '',\n path: inputObj.path as string | undefined,\n };\n\n case 'WebFetch':\n return {\n action: 'web_fetch',\n url: (inputObj.url as string) || '',\n prompt: inputObj.prompt as string | undefined,\n };\n\n case 'WebSearch':\n return {\n action: 'web_search',\n query: (inputObj.query as string) || '',\n };\n\n case 'TodoWrite': {\n const todos = (inputObj.todos as TodoItem[]) || [];\n const stats = {\n total: todos.length,\n completed: todos.filter((t) => t.status === 'completed').length,\n inProgress: todos.filter((t) => t.status === 'in_progress').length,\n pending: todos.filter((t) => t.status === 'pending').length,\n };\n return {\n action: 'todo_write',\n todos,\n stats,\n };\n }\n\n default: {\n // Check if it's an MCP tool\n const mcpParts = parseMcpToolName(toolName);\n if (mcpParts) {\n return {\n action: 'mcp_tool',\n serverName: mcpParts.serverName,\n toolName: mcpParts.toolName,\n arguments: input,\n };\n }\n\n // Generic tool\n return {\n action: 'generic_tool',\n toolName: formatToolName(toolName),\n arguments: input,\n };\n }\n }\n}\n\n// =============================================================================\n// Summary Generation\n// =============================================================================\n\n/**\n * Generate a human-readable summary for a tool call\n */\nexport function generateToolSummary(\n _toolName: string,\n _input: unknown,\n actionType: ActionType\n): string {\n switch (actionType.action) {\n case 'command_run': {\n const cmd = actionType.command;\n return cmd.length > 60 ? cmd.substring(0, 57) + '...' : cmd;\n }\n\n case 'file_read':\n return actionType.path;\n\n case 'file_edit':\n return actionType.path;\n\n case 'file_write':\n return actionType.path;\n\n case 'search':\n return `${actionType.pattern}${actionType.path ? ` in ${actionType.path}` : ''}`;\n\n case 'glob':\n return actionType.pattern;\n\n case 'web_fetch':\n return actionType.url;\n\n case 'web_search':\n return actionType.query;\n\n case 'mcp_tool':\n return `${actionType.serverName}:${actionType.toolName}`;\n\n case 'generic_tool':\n return actionType.toolName;\n\n case 'todo_write': {\n const { stats } = actionType;\n if (stats) {\n return `${stats.completed}/${stats.total} completed`;\n }\n return `${actionType.todos.length} tasks`;\n }\n\n default:\n return 'Unknown tool';\n }\n}\n\n// =============================================================================\n// Result Normalization\n// =============================================================================\n\n/**\n * Extract text content from various content formats\n */\nexport function extractTextContent(content: unknown): string {\n if (typeof content === 'string') return content;\n\n if (Array.isArray(content)) {\n return content\n .filter((item): item is { text: string } => typeof item?.text === 'string')\n .map((item) => item.text)\n .join('\\n');\n }\n\n if (content && typeof content === 'object' && 'text' in content) {\n return String((content as { text: unknown }).text);\n }\n\n return JSON.stringify(content, null, 2);\n}\n\n/**\n * Normalize tool result content to markdown or JSON format\n */\nexport function normalizeToolResult(content: unknown): ToolResult {\n if (typeof content === 'string') {\n try {\n const parsed = JSON.parse(content);\n return { type: 'json', value: parsed };\n } catch {\n return { type: 'markdown', value: content };\n }\n }\n\n if (Array.isArray(content)) {\n const texts = content\n .filter(\n (item): item is { type: 'text'; text: string } =>\n item?.type === 'text' && typeof item.text === 'string'\n )\n .map((item) => item.text);\n\n if (texts.length > 0) {\n const joined = texts.join('\\n\\n');\n try {\n return { type: 'json', value: JSON.parse(joined) };\n } catch {\n return { type: 'markdown', value: joined };\n }\n }\n }\n\n return { type: 'json', value: content };\n}\n\n/**\n * Parse command result from tool result content\n */\nexport function parseCommandResult(content: unknown): CommandRunResult {\n const output = extractTextContent(content);\n\n if (typeof content === 'string') {\n try {\n const parsed = JSON.parse(content);\n if (typeof parsed.exitCode === 'number') {\n return {\n exitCode: parsed.exitCode,\n output: parsed.output || output,\n success: parsed.exitCode === 0,\n };\n }\n } catch {\n // Not JSON, use raw output\n }\n }\n\n return {\n output,\n success: true,\n };\n}\n\n// =============================================================================\n// Tool Call Creation\n// =============================================================================\n\nexport interface ToolUseInput {\n id: string;\n name: string;\n input: unknown;\n}\n\n/**\n * Create a NormalizedToolCall from a tool_use content block\n */\nexport function createToolCall(toolUse: ToolUseInput): NormalizedToolCall {\n const actionType = mapToolToActionType(toolUse.name, toolUse.input);\n const summary = generateToolSummary(toolUse.name, toolUse.input, actionType);\n\n return {\n id: toolUse.id,\n toolName: toolUse.name,\n actionType,\n status: 'pending',\n summary,\n startedAt: new Date().toISOString(),\n };\n}\n\n/**\n * Update a NormalizedToolCall with its result\n */\nexport function updateToolCallWithResult(\n toolCall: NormalizedToolCall,\n content: unknown,\n isError?: boolean\n): NormalizedToolCall {\n const updatedToolCall = { ...toolCall };\n const actionType = { ...toolCall.actionType };\n\n updatedToolCall.status = isError ? 'failed' : 'success';\n updatedToolCall.completedAt = new Date().toISOString();\n updatedToolCall.isError = isError;\n\n if (actionType.action === 'command_run') {\n const result = parseCommandResult(content);\n (actionType as typeof actionType).result = result;\n if (result.exitCode !== undefined && result.exitCode !== 0) {\n updatedToolCall.status = 'failed';\n updatedToolCall.isError = true;\n }\n } else if (actionType.action === 'mcp_tool' || actionType.action === 'generic_tool') {\n (actionType as typeof actionType).result = normalizeToolResult(content);\n }\n\n updatedToolCall.actionType = actionType;\n return updatedToolCall;\n}\n\n// =============================================================================\n// Display Helpers\n// =============================================================================\n\n/**\n * Get display icon name for an action type\n */\nexport function getActionIcon(actionType: ActionType): string {\n switch (actionType.action) {\n case 'command_run':\n return 'terminal';\n case 'file_read':\n return 'file-text';\n case 'file_edit':\n return 'edit';\n case 'file_write':\n return 'file-plus';\n case 'search':\n return 'search';\n case 'glob':\n return 'folder-search';\n case 'web_fetch':\n return 'globe';\n case 'web_search':\n return 'search';\n case 'mcp_tool':\n return 'plug';\n case 'generic_tool':\n return 'tool';\n case 'todo_write':\n return 'list-checks';\n default:\n return 'tool';\n }\n}\n\n/**\n * Get display label for an action type\n */\nexport function getActionLabel(actionType: ActionType): string {\n switch (actionType.action) {\n case 'command_run':\n return 'Command';\n case 'file_read':\n return 'Read';\n case 'file_edit':\n return 'Edit';\n case 'file_write':\n return 'Write';\n case 'search':\n return 'Search';\n case 'glob':\n return 'Glob';\n case 'web_fetch':\n return 'Fetch';\n case 'web_search':\n return 'Search';\n case 'mcp_tool':\n return 'MCP';\n case 'generic_tool':\n return 'Tool';\n case 'todo_write':\n return 'Tasks';\n default:\n return 'Tool';\n }\n}\n\n// =============================================================================\n// Formatting Helpers\n// =============================================================================\n\n/**\n * Format a file size in bytes to a human-readable string\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n/**\n * Format a timestamp to a locale time string\n */\nexport function formatTimestamp(timestamp: string): string {\n try {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', {\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n } catch {\n return timestamp;\n }\n}\n\n/**\n * Truncate a string with ellipsis\n */\nexport function truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return str.substring(0, maxLength - 3) + '...';\n}\n\n/**\n * Join class names, filtering out falsy values\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\n// =============================================================================\n// Entry Grouping for Compact Mode\n// =============================================================================\n\n/**\n * A grouped entry that contains either a single entry or a group of consecutive tool calls\n */\nexport type GroupedEntry =\n | { type: 'single'; entry: NormalizedEntry }\n | { type: 'tool_group'; entries: NormalizedEntry[]; id: string };\n\n/**\n * Group consecutive tool call entries together based on display config.\n *\n * This is used in compact mode to merge consecutive tool calls into\n * a single ToolExecutionGroup, while keeping text content separate.\n *\n * @param entries - Array of normalized entries\n * @param config - Display configuration (uses breakEveryNToolCalls)\n * @returns Array of grouped entries\n */\nexport function groupEntriesForCompactMode(\n entries: NormalizedEntry[],\n config: ToolDisplayConfig\n): GroupedEntry[] {\n const result: GroupedEntry[] = [];\n let currentToolGroup: NormalizedEntry[] = [];\n let toolGroupCounter = 0;\n\n const flushToolGroup = () => {\n if (currentToolGroup.length > 0) {\n result.push({\n type: 'tool_group',\n entries: [...currentToolGroup],\n id: `tool-group-${toolGroupCounter++}`,\n });\n currentToolGroup = [];\n }\n };\n\n for (const entry of entries) {\n if (entry.entryType.type === 'tool_call') {\n currentToolGroup.push(entry);\n\n // Check if we should break the group\n if (\n config.breakEveryNToolCalls &&\n config.breakEveryNToolCalls > 0 &&\n currentToolGroup.length >= config.breakEveryNToolCalls\n ) {\n flushToolGroup();\n }\n } else {\n // Non-tool-call entry - flush any pending tool group\n flushToolGroup();\n result.push({ type: 'single', entry });\n }\n }\n\n // Flush any remaining tool group\n flushToolGroup();\n\n return result;\n}\n\n/**\n * Extract tool calls from a group of entries\n */\nexport function extractToolCallsFromGroup(entries: NormalizedEntry[]): NormalizedToolCall[] {\n return entries\n .filter((e): e is NormalizedEntry & { entryType: { type: 'tool_call'; toolCall: NormalizedToolCall } } =>\n e.entryType.type === 'tool_call'\n )\n .map((e) => e.entryType.toolCall);\n}\n\n// =============================================================================\n// Option Parsing\n// =============================================================================\n\n/**\n * A parsed option from assistant message content\n */\nexport interface ParsedOption {\n /** Option identifier (e.g., \"1\", \"2\", \"A\", \"B\") */\n id: string;\n /** Option title/label */\n label: string;\n /** Optional description text */\n description?: string;\n}\n\n/**\n * Result of parsing options from content\n */\nexport interface ParsedOptionsResult {\n /** Text before the options */\n preamble: string;\n /** Parsed options */\n options: ParsedOption[];\n}\n\n/**\n * Parse options from assistant message content.\n * Detects patterns like:\n * - \"Option 1: Title\" / \"Option 2: Title\"\n * - \"**Option 1:** Title\" (bold markdown)\n * - Numbered lists with descriptions\n *\n * @returns ParsedOptionsResult if options found, null otherwise\n */\nexport function parseOptionsFromContent(content: string): ParsedOptionsResult | null {\n // Pattern 1: \"Option N:\" format (with or without bold markdown)\n // Matches: \"Option 1:\", \"**Option 1:**\", \"Option 1 -\", etc.\n const optionPattern = /(?:\\*\\*)?Option\\s+(\\d+)(?:\\*\\*)?[:\\-]\\s*([^\\n]+)(?:\\n((?:(?!\\n(?:\\*\\*)?Option\\s+\\d).)*?))?/gi;\n\n const options: ParsedOption[] = [];\n let firstMatchStart = -1;\n let match: RegExpExecArray | null;\n\n // Reset regex\n optionPattern.lastIndex = 0;\n\n while ((match = optionPattern.exec(content)) !== null) {\n if (firstMatchStart === -1) {\n firstMatchStart = match.index;\n }\n\n const id = match[1] ?? '';\n const labelRaw = match[2];\n const label = labelRaw ? labelRaw.trim() : '';\n // Description is everything after the label until the next option\n const descriptionRaw = match[3];\n\n // Clean up description - remove leading/trailing whitespace and normalize\n let description: string | undefined;\n if (descriptionRaw) {\n const cleaned = descriptionRaw\n .split('\\n')\n .map(line => line.trim())\n .filter(line => line.length > 0)\n .join(' ');\n if (cleaned) {\n description = cleaned;\n }\n }\n\n options.push({ id, label, description });\n }\n\n // Need at least 2 options to be considered a valid options list\n if (options.length >= 2) {\n const preamble = firstMatchStart > 0\n ? content.substring(0, firstMatchStart).trim()\n : '';\n\n return { preamble, options };\n }\n\n // Pattern 2: Simple numbered list at end of message\n // Look for \"What would you like to do?\" or similar followed by numbered items\n const questionPattern = /^(.*?(?:what would you like to do\\??|choose an option|select.*?:|here are your options.*?:))\\s*\\n+((?:\\d+\\.\\s+.+\\n?)+)/ims;\n const questionMatch = content.match(questionPattern);\n\n if (questionMatch && questionMatch[1] && questionMatch[2]) {\n const preamble = questionMatch[1].trim();\n const listSection = questionMatch[2];\n\n // Parse numbered list items\n const listPattern = /(\\d+)\\.\\s+([^\\n]+)/g;\n let listMatch: RegExpExecArray | null;\n\n while ((listMatch = listPattern.exec(listSection)) !== null) {\n const listId = listMatch[1] ?? '';\n const listLabelRaw = listMatch[2];\n const listLabel = listLabelRaw ? listLabelRaw.trim() : '';\n options.push({\n id: listId,\n label: listLabel,\n });\n }\n\n if (options.length >= 2) {\n return { preamble, options };\n }\n }\n\n return null;\n}\n"]}
@@ -0,0 +1,135 @@
1
+ import { ActionType, ToolResult, CommandRunResult, NormalizedToolCall, NormalizedEntry, ToolDisplayConfig } from './types.cjs';
2
+
3
+ /**
4
+ * @ash-cloud/ash-ui - Utilities
5
+ *
6
+ * Utility functions for normalizing and formatting tool calls
7
+ * for display in agentic UIs.
8
+ */
9
+
10
+ /**
11
+ * Format MCP tool names from mcp__server__tool to mcp:server:tool
12
+ */
13
+ declare function formatToolName(name: string): string;
14
+ /**
15
+ * Parse MCP tool name to extract server and tool names
16
+ */
17
+ declare function parseMcpToolName(name: string): {
18
+ serverName: string;
19
+ toolName: string;
20
+ } | null;
21
+ /**
22
+ * Map a tool name and input to a structured ActionType
23
+ */
24
+ declare function mapToolToActionType(toolName: string, input: unknown): ActionType;
25
+ /**
26
+ * Generate a human-readable summary for a tool call
27
+ */
28
+ declare function generateToolSummary(_toolName: string, _input: unknown, actionType: ActionType): string;
29
+ /**
30
+ * Extract text content from various content formats
31
+ */
32
+ declare function extractTextContent(content: unknown): string;
33
+ /**
34
+ * Normalize tool result content to markdown or JSON format
35
+ */
36
+ declare function normalizeToolResult(content: unknown): ToolResult;
37
+ /**
38
+ * Parse command result from tool result content
39
+ */
40
+ declare function parseCommandResult(content: unknown): CommandRunResult;
41
+ interface ToolUseInput {
42
+ id: string;
43
+ name: string;
44
+ input: unknown;
45
+ }
46
+ /**
47
+ * Create a NormalizedToolCall from a tool_use content block
48
+ */
49
+ declare function createToolCall(toolUse: ToolUseInput): NormalizedToolCall;
50
+ /**
51
+ * Update a NormalizedToolCall with its result
52
+ */
53
+ declare function updateToolCallWithResult(toolCall: NormalizedToolCall, content: unknown, isError?: boolean): NormalizedToolCall;
54
+ /**
55
+ * Get display icon name for an action type
56
+ */
57
+ declare function getActionIcon(actionType: ActionType): string;
58
+ /**
59
+ * Get display label for an action type
60
+ */
61
+ declare function getActionLabel(actionType: ActionType): string;
62
+ /**
63
+ * Format a file size in bytes to a human-readable string
64
+ */
65
+ declare function formatFileSize(bytes: number): string;
66
+ /**
67
+ * Format a timestamp to a locale time string
68
+ */
69
+ declare function formatTimestamp(timestamp: string): string;
70
+ /**
71
+ * Truncate a string with ellipsis
72
+ */
73
+ declare function truncate(str: string, maxLength: number): string;
74
+ /**
75
+ * Join class names, filtering out falsy values
76
+ */
77
+ declare function cn(...classes: (string | undefined | null | false)[]): string;
78
+ /**
79
+ * A grouped entry that contains either a single entry or a group of consecutive tool calls
80
+ */
81
+ type GroupedEntry = {
82
+ type: 'single';
83
+ entry: NormalizedEntry;
84
+ } | {
85
+ type: 'tool_group';
86
+ entries: NormalizedEntry[];
87
+ id: string;
88
+ };
89
+ /**
90
+ * Group consecutive tool call entries together based on display config.
91
+ *
92
+ * This is used in compact mode to merge consecutive tool calls into
93
+ * a single ToolExecutionGroup, while keeping text content separate.
94
+ *
95
+ * @param entries - Array of normalized entries
96
+ * @param config - Display configuration (uses breakEveryNToolCalls)
97
+ * @returns Array of grouped entries
98
+ */
99
+ declare function groupEntriesForCompactMode(entries: NormalizedEntry[], config: ToolDisplayConfig): GroupedEntry[];
100
+ /**
101
+ * Extract tool calls from a group of entries
102
+ */
103
+ declare function extractToolCallsFromGroup(entries: NormalizedEntry[]): NormalizedToolCall[];
104
+ /**
105
+ * A parsed option from assistant message content
106
+ */
107
+ interface ParsedOption {
108
+ /** Option identifier (e.g., "1", "2", "A", "B") */
109
+ id: string;
110
+ /** Option title/label */
111
+ label: string;
112
+ /** Optional description text */
113
+ description?: string;
114
+ }
115
+ /**
116
+ * Result of parsing options from content
117
+ */
118
+ interface ParsedOptionsResult {
119
+ /** Text before the options */
120
+ preamble: string;
121
+ /** Parsed options */
122
+ options: ParsedOption[];
123
+ }
124
+ /**
125
+ * Parse options from assistant message content.
126
+ * Detects patterns like:
127
+ * - "Option 1: Title" / "Option 2: Title"
128
+ * - "**Option 1:** Title" (bold markdown)
129
+ * - Numbered lists with descriptions
130
+ *
131
+ * @returns ParsedOptionsResult if options found, null otherwise
132
+ */
133
+ declare function parseOptionsFromContent(content: string): ParsedOptionsResult | null;
134
+
135
+ export { type GroupedEntry, type ParsedOption, type ParsedOptionsResult, type ToolUseInput, cn, createToolCall, extractTextContent, extractToolCallsFromGroup, formatFileSize, formatTimestamp, formatToolName, generateToolSummary, getActionIcon, getActionLabel, groupEntriesForCompactMode, mapToolToActionType, normalizeToolResult, parseCommandResult, parseMcpToolName, parseOptionsFromContent, truncate, updateToolCallWithResult };