@stigmer/react 0.0.52 → 0.0.53
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/deployment-mode.d.ts +35 -0
- package/deployment-mode.d.ts.map +1 -0
- package/deployment-mode.js +41 -0
- package/deployment-mode.js.map +1 -0
- package/execution/ApprovalCard.d.ts +8 -6
- package/execution/ApprovalCard.d.ts.map +1 -1
- package/execution/ApprovalCard.js +34 -96
- package/execution/ApprovalCard.js.map +1 -1
- package/execution/McpToolDetail.d.ts +48 -0
- package/execution/McpToolDetail.d.ts.map +1 -0
- package/execution/McpToolDetail.js +159 -0
- package/execution/McpToolDetail.js.map +1 -0
- package/execution/ToolArgsView.d.ts +41 -0
- package/execution/ToolArgsView.d.ts.map +1 -0
- package/execution/ToolArgsView.js +132 -0
- package/execution/ToolArgsView.js.map +1 -0
- package/execution/ToolCallDetail.d.ts +11 -4
- package/execution/ToolCallDetail.d.ts.map +1 -1
- package/execution/ToolCallDetail.js +30 -101
- package/execution/ToolCallDetail.js.map +1 -1
- package/execution/ToolCallGroup.d.ts.map +1 -1
- package/execution/ToolCallGroup.js +3 -2
- package/execution/ToolCallGroup.js.map +1 -1
- package/execution/ToolCallItem.d.ts +2 -0
- package/execution/ToolCallItem.d.ts.map +1 -1
- package/execution/ToolCallItem.js +6 -2
- package/execution/ToolCallItem.js.map +1 -1
- package/execution/index.d.ts +7 -1
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +4 -1
- package/execution/index.js.map +1 -1
- package/execution/tool-categories.d.ts +35 -8
- package/execution/tool-categories.d.ts.map +1 -1
- package/execution/tool-categories.js +76 -10
- package/execution/tool-categories.js.map +1 -1
- package/execution/tool-rendering-primitives.d.ts +61 -0
- package/execution/tool-rendering-primitives.d.ts.map +1 -0
- package/execution/tool-rendering-primitives.js +106 -0
- package/execution/tool-rendering-primitives.js.map +1 -0
- package/index.d.ts +5 -2
- package/index.d.ts.map +1 -1
- package/index.js +5 -1
- package/index.js.map +1 -1
- package/internal/CloudFeatureNotice.d.ts +19 -0
- package/internal/CloudFeatureNotice.d.ts.map +1 -0
- package/internal/CloudFeatureNotice.js +21 -0
- package/internal/CloudFeatureNotice.js.map +1 -0
- package/mcp-server/McpServerDetailView.d.ts +15 -1
- package/mcp-server/McpServerDetailView.d.ts.map +1 -1
- package/mcp-server/McpServerDetailView.js +11 -3
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/package.json +4 -4
- package/provider.d.ts +14 -2
- package/provider.d.ts.map +1 -1
- package/provider.js +3 -2
- package/provider.js.map +1 -1
- package/src/deployment-mode.ts +46 -0
- package/src/execution/ApprovalCard.tsx +130 -283
- package/src/execution/McpToolDetail.tsx +283 -0
- package/src/execution/ToolArgsView.tsx +277 -0
- package/src/execution/ToolCallDetail.tsx +51 -219
- package/src/execution/ToolCallGroup.tsx +3 -2
- package/src/execution/ToolCallItem.tsx +14 -2
- package/src/execution/index.ts +25 -0
- package/src/execution/tool-categories.ts +89 -9
- package/src/execution/tool-rendering-primitives.tsx +253 -0
- package/src/index.ts +13 -0
- package/src/internal/CloudFeatureNotice.tsx +60 -0
- package/src/mcp-server/McpServerDetailView.tsx +24 -2
- package/src/provider.tsx +18 -2
- package/styles.css +1 -1
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { formatDuration } from "./ToolCallDetail";
|
|
6
|
+
import { humanizeToolName } from "./tool-categories";
|
|
7
|
+
import {
|
|
8
|
+
CollapsiblePre,
|
|
9
|
+
CollapsibleJsonBlock,
|
|
10
|
+
McpServerIcon,
|
|
11
|
+
formatJson,
|
|
12
|
+
isScalar,
|
|
13
|
+
humanizeArgKey,
|
|
14
|
+
} from "./tool-rendering-primitives";
|
|
15
|
+
|
|
16
|
+
export interface McpToolDetailProps {
|
|
17
|
+
readonly toolCall: ToolCall;
|
|
18
|
+
readonly className?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* MCP-aware detail renderer for tool calls originating from an MCP
|
|
23
|
+
* server.
|
|
24
|
+
*
|
|
25
|
+
* Replaces the generic "dump args + result as raw JSON" fallback
|
|
26
|
+
* with structured formatting:
|
|
27
|
+
*
|
|
28
|
+
* - **Arguments** are rendered as a labelled key-value list.
|
|
29
|
+
* Scalars display inline; objects/arrays collapse into formatted
|
|
30
|
+
* JSON blocks.
|
|
31
|
+
* - **Results** are parsed through {@link parseMcpResult} which
|
|
32
|
+
* handles MCP content-block arrays, embedded JSON, and Python
|
|
33
|
+
* repr artefacts before rendering.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* <McpToolDetail toolCall={toolCall} />
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function McpToolDetail({ toolCall, className }: McpToolDetailProps) {
|
|
41
|
+
const duration = formatDuration(toolCall.startedAt, toolCall.completedAt);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className={cn("space-y-3 text-xs", className)}>
|
|
45
|
+
<McpMetadataRow
|
|
46
|
+
mcpServerSlug={toolCall.mcpServerSlug}
|
|
47
|
+
toolName={toolCall.name}
|
|
48
|
+
duration={duration}
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
{toolCall.args && Object.keys(toolCall.args).length > 0 && (
|
|
52
|
+
<McpArgsView args={toolCall.args as Record<string, unknown>} />
|
|
53
|
+
)}
|
|
54
|
+
|
|
55
|
+
{toolCall.result && (
|
|
56
|
+
<McpResultView result={toolCall.result} />
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Metadata
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
export function McpMetadataRow({
|
|
67
|
+
mcpServerSlug,
|
|
68
|
+
toolName,
|
|
69
|
+
duration,
|
|
70
|
+
}: {
|
|
71
|
+
mcpServerSlug: string;
|
|
72
|
+
toolName: string;
|
|
73
|
+
duration: string | null;
|
|
74
|
+
}) {
|
|
75
|
+
const hasMetadata = mcpServerSlug || duration;
|
|
76
|
+
if (!hasMetadata) return null;
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-muted-foreground">
|
|
80
|
+
{mcpServerSlug && (
|
|
81
|
+
<span className="inline-flex items-center gap-1.5 rounded bg-muted px-1.5 py-0.5 font-mono">
|
|
82
|
+
<McpServerIcon />
|
|
83
|
+
{mcpServerSlug}
|
|
84
|
+
<span className="text-muted-foreground/60">/</span>
|
|
85
|
+
<span className="text-foreground">{humanizeToolName(toolName)}</span>
|
|
86
|
+
</span>
|
|
87
|
+
)}
|
|
88
|
+
{duration && <span>{duration}</span>}
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// Arguments — structured key-value rendering
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
export function McpArgsView({ args }: { args: Record<string, unknown> }) {
|
|
98
|
+
const entries = Object.entries(args);
|
|
99
|
+
if (entries.length === 0) return null;
|
|
100
|
+
|
|
101
|
+
const scalars: [string, string][] = [];
|
|
102
|
+
const complex: [string, unknown][] = [];
|
|
103
|
+
|
|
104
|
+
for (const [key, value] of entries) {
|
|
105
|
+
if (isScalar(value)) {
|
|
106
|
+
scalars.push([key, String(value)]);
|
|
107
|
+
} else {
|
|
108
|
+
complex.push([key, value]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div className="space-y-2">
|
|
114
|
+
<span className="font-medium text-muted-foreground">Arguments</span>
|
|
115
|
+
|
|
116
|
+
{scalars.length > 0 && (
|
|
117
|
+
<dl className="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 rounded-md border border-border bg-muted/30 px-2.5 py-2">
|
|
118
|
+
{scalars.map(([key, value]) => (
|
|
119
|
+
<ScalarRow key={key} label={key} value={value} />
|
|
120
|
+
))}
|
|
121
|
+
</dl>
|
|
122
|
+
)}
|
|
123
|
+
|
|
124
|
+
{complex.map(([key, value]) => (
|
|
125
|
+
<CollapsibleJsonBlock
|
|
126
|
+
key={key}
|
|
127
|
+
label={humanizeArgKey(key)}
|
|
128
|
+
content={formatJson(value)}
|
|
129
|
+
/>
|
|
130
|
+
))}
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function ScalarRow({ label, value }: { label: string; value: string }) {
|
|
136
|
+
const isMultiline = value.includes("\n");
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<>
|
|
140
|
+
<dt className="whitespace-nowrap font-mono text-muted-foreground">
|
|
141
|
+
{humanizeArgKey(label)}
|
|
142
|
+
</dt>
|
|
143
|
+
{isMultiline ? (
|
|
144
|
+
<dd className="min-w-0">
|
|
145
|
+
<pre className="whitespace-pre-wrap break-words rounded border border-border bg-muted/40 px-2 py-1 font-mono text-foreground">
|
|
146
|
+
{value}
|
|
147
|
+
</pre>
|
|
148
|
+
</dd>
|
|
149
|
+
) : (
|
|
150
|
+
<dd className="min-w-0 truncate font-mono text-foreground" title={value}>
|
|
151
|
+
{value}
|
|
152
|
+
</dd>
|
|
153
|
+
)}
|
|
154
|
+
</>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Result — intelligent parsing
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
function McpResultView({ result }: { result: string }) {
|
|
163
|
+
const parsed = parseMcpResult(result);
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<div className="space-y-1">
|
|
167
|
+
<span className="font-medium text-muted-foreground">Result</span>
|
|
168
|
+
<CollapsiblePre
|
|
169
|
+
content={parsed}
|
|
170
|
+
className="max-h-80 overflow-auto rounded-md border border-border bg-muted/40 p-2 text-foreground"
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Extracts human-readable content from an MCP tool result string.
|
|
178
|
+
*
|
|
179
|
+
* Handles three common formats that arrive from the backend:
|
|
180
|
+
*
|
|
181
|
+
* 1. **MCP content-block array** — `[{"type":"text","text":"..."}]`.
|
|
182
|
+
* Text parts are extracted and, if they are themselves valid
|
|
183
|
+
* JSON, pretty-printed.
|
|
184
|
+
* 2. **Python repr** — `[{'type': 'text', 'text': '...'}]`. Single
|
|
185
|
+
* quotes are normalised to double quotes before parsing.
|
|
186
|
+
* 3. **Plain JSON / text** — returned formatted when valid JSON,
|
|
187
|
+
* or as-is otherwise.
|
|
188
|
+
*/
|
|
189
|
+
export function parseMcpResult(result: string): string {
|
|
190
|
+
const trimmed = result.trim();
|
|
191
|
+
|
|
192
|
+
// Fast path: try standard JSON parse first.
|
|
193
|
+
const jsonParsed = tryParseJson(trimmed);
|
|
194
|
+
if (jsonParsed !== undefined) {
|
|
195
|
+
const extracted = tryExtractContentBlocks(jsonParsed);
|
|
196
|
+
if (extracted !== null) return extracted;
|
|
197
|
+
return JSON.stringify(jsonParsed, null, 2);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Attempt to fix Python repr (single-quoted dicts/lists).
|
|
201
|
+
const fixed = tryFixPythonRepr(trimmed);
|
|
202
|
+
if (fixed !== undefined) {
|
|
203
|
+
const extracted = tryExtractContentBlocks(fixed);
|
|
204
|
+
if (extracted !== null) return extracted;
|
|
205
|
+
return JSON.stringify(fixed, null, 2);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return trimmed;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
// Content-block extraction
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
interface McpContentBlock {
|
|
216
|
+
type: string;
|
|
217
|
+
text?: string;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function isMcpContentBlockArray(val: unknown): val is McpContentBlock[] {
|
|
221
|
+
if (!Array.isArray(val)) return false;
|
|
222
|
+
if (val.length === 0) return false;
|
|
223
|
+
return val.every(
|
|
224
|
+
(item) =>
|
|
225
|
+
typeof item === "object" &&
|
|
226
|
+
item !== null &&
|
|
227
|
+
"type" in item &&
|
|
228
|
+
typeof (item as Record<string, unknown>).type === "string",
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function tryExtractContentBlocks(parsed: unknown): string | null {
|
|
233
|
+
if (!isMcpContentBlockArray(parsed)) return null;
|
|
234
|
+
|
|
235
|
+
const textParts: string[] = [];
|
|
236
|
+
for (const block of parsed) {
|
|
237
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
238
|
+
const innerJson = tryParseJson(block.text.trim());
|
|
239
|
+
if (innerJson !== undefined) {
|
|
240
|
+
textParts.push(JSON.stringify(innerJson, null, 2));
|
|
241
|
+
} else {
|
|
242
|
+
textParts.push(block.text);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return textParts.length > 0 ? textParts.join("\n\n") : null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
// JSON / Python repr helpers
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
|
|
254
|
+
function tryParseJson(str: string): unknown | undefined {
|
|
255
|
+
try {
|
|
256
|
+
return JSON.parse(str);
|
|
257
|
+
} catch {
|
|
258
|
+
return undefined;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Attempts to convert a Python repr string (single-quoted
|
|
264
|
+
* dicts/lists with True/False/None) into a parsed JS value.
|
|
265
|
+
*
|
|
266
|
+
* This is intentionally conservative: it only handles the
|
|
267
|
+
* most common patterns and bails on ambiguity.
|
|
268
|
+
*/
|
|
269
|
+
function tryFixPythonRepr(str: string): unknown | undefined {
|
|
270
|
+
if (!str.startsWith("[") && !str.startsWith("{")) return undefined;
|
|
271
|
+
|
|
272
|
+
let fixed = str
|
|
273
|
+
.replace(/'/g, '"')
|
|
274
|
+
.replace(/\bTrue\b/g, "true")
|
|
275
|
+
.replace(/\bFalse\b/g, "false")
|
|
276
|
+
.replace(/\bNone\b/g, "null");
|
|
277
|
+
|
|
278
|
+
// Handle trailing commas before ] or } (common in Python repr).
|
|
279
|
+
fixed = fixed.replace(/,\s*([}\]])/g, "$1");
|
|
280
|
+
|
|
281
|
+
return tryParseJson(fixed);
|
|
282
|
+
}
|
|
283
|
+
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
import {
|
|
6
|
+
resolveToolCategory,
|
|
7
|
+
extractWriteContentFromPreview,
|
|
8
|
+
} from "./tool-categories";
|
|
9
|
+
import type { ToolCategory, ToolCategoryInfo } from "./tool-categories";
|
|
10
|
+
import { FilePathLink } from "./FilePathLink";
|
|
11
|
+
import { McpArgsView, McpMetadataRow } from "./McpToolDetail";
|
|
12
|
+
import {
|
|
13
|
+
CollapsibleCode,
|
|
14
|
+
FilePathIcon,
|
|
15
|
+
formatJson,
|
|
16
|
+
} from "./tool-rendering-primitives";
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Public API
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
export interface ToolArgsViewProps {
|
|
23
|
+
/**
|
|
24
|
+
* Raw tool name as it appears on the ToolCall or PendingApproval.
|
|
25
|
+
* Used for category resolution and MCP metadata display.
|
|
26
|
+
*/
|
|
27
|
+
readonly toolName: string;
|
|
28
|
+
/**
|
|
29
|
+
* Parsed tool arguments — either from `ToolCall.args` or
|
|
30
|
+
* `JSON.parse(PendingApproval.argsPreview)`.
|
|
31
|
+
*/
|
|
32
|
+
readonly args: Record<string, unknown> | null;
|
|
33
|
+
/** MCP server slug for MCP tool classification and metadata. */
|
|
34
|
+
readonly mcpServerSlug?: string;
|
|
35
|
+
readonly className?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Unified tool-arguments renderer used by both {@link ApprovalCard}
|
|
40
|
+
* (pre-execution) and {@link ToolCallDetail} (post-execution).
|
|
41
|
+
*
|
|
42
|
+
* Resolves the tool category from `toolName` + `mcpServerSlug`,
|
|
43
|
+
* extracts the relevant primary argument, and dispatches to the
|
|
44
|
+
* appropriate category-specific view:
|
|
45
|
+
*
|
|
46
|
+
* - **Shell** — terminal-style command block
|
|
47
|
+
* - **File (read/write/edit/delete)** — file icon + path + optional content
|
|
48
|
+
* - **Search / List** — pattern display
|
|
49
|
+
* - **MCP** — metadata row + scalar key-value grid + collapsible JSON
|
|
50
|
+
* - **Generic / Unknown** — formatted JSON args
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```tsx
|
|
54
|
+
* // In approval card (from argsPreview string):
|
|
55
|
+
* const args = JSON.parse(pendingApproval.argsPreview);
|
|
56
|
+
* <ToolArgsView toolName={toolName} args={args} mcpServerSlug={slug} />
|
|
57
|
+
*
|
|
58
|
+
* // In detail view (from ToolCall):
|
|
59
|
+
* <ToolArgsView toolName={tc.name} args={tc.args} mcpServerSlug={tc.mcpServerSlug} />
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export function ToolArgsView({
|
|
63
|
+
toolName,
|
|
64
|
+
args,
|
|
65
|
+
mcpServerSlug,
|
|
66
|
+
className,
|
|
67
|
+
}: ToolArgsViewProps) {
|
|
68
|
+
const categoryInfo = useMemo(
|
|
69
|
+
() => resolveToolCategory(toolName, mcpServerSlug),
|
|
70
|
+
[toolName, mcpServerSlug],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const primaryArg = useMemo(
|
|
74
|
+
() => extractPrimaryArgValue(args, categoryInfo),
|
|
75
|
+
[args, categoryInfo],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div className={cn("text-xs", className)}>
|
|
80
|
+
<CategoryArgsDispatch
|
|
81
|
+
category={categoryInfo.category}
|
|
82
|
+
toolName={toolName}
|
|
83
|
+
args={args}
|
|
84
|
+
primaryArg={primaryArg}
|
|
85
|
+
mcpServerSlug={mcpServerSlug}
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Dispatch
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
function CategoryArgsDispatch({
|
|
96
|
+
category,
|
|
97
|
+
toolName,
|
|
98
|
+
args,
|
|
99
|
+
primaryArg,
|
|
100
|
+
mcpServerSlug,
|
|
101
|
+
}: {
|
|
102
|
+
category: ToolCategory;
|
|
103
|
+
toolName: string;
|
|
104
|
+
args: Record<string, unknown> | null;
|
|
105
|
+
primaryArg: string | null;
|
|
106
|
+
mcpServerSlug?: string;
|
|
107
|
+
}) {
|
|
108
|
+
switch (category) {
|
|
109
|
+
case "shell":
|
|
110
|
+
return primaryArg ? <ShellArgsView command={primaryArg} /> : null;
|
|
111
|
+
|
|
112
|
+
case "read":
|
|
113
|
+
case "write":
|
|
114
|
+
case "edit":
|
|
115
|
+
case "delete":
|
|
116
|
+
return primaryArg ? (
|
|
117
|
+
<FileArgsView path={primaryArg} category={category} args={args} />
|
|
118
|
+
) : null;
|
|
119
|
+
|
|
120
|
+
case "search":
|
|
121
|
+
case "list":
|
|
122
|
+
return primaryArg ? <SearchArgsView pattern={primaryArg} /> : null;
|
|
123
|
+
|
|
124
|
+
case "mcp":
|
|
125
|
+
return (
|
|
126
|
+
<McpArgsPreview
|
|
127
|
+
toolName={toolName}
|
|
128
|
+
args={args}
|
|
129
|
+
mcpServerSlug={mcpServerSlug ?? ""}
|
|
130
|
+
/>
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
default:
|
|
134
|
+
return args && Object.keys(args).length > 0 ? (
|
|
135
|
+
<GenericArgsView args={args} />
|
|
136
|
+
) : null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Category-specific views
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
function ShellArgsView({ command }: { command: string }) {
|
|
145
|
+
return (
|
|
146
|
+
<div className="rounded-md border border-border bg-[var(--stgm-terminal-bg,#1a1a2e)] p-2.5">
|
|
147
|
+
<pre className="whitespace-pre-wrap break-words font-mono text-xs text-[var(--stgm-terminal-fg,#e0e0e0)]">
|
|
148
|
+
<span className="select-none text-[var(--stgm-terminal-prompt,#6b7280)]">
|
|
149
|
+
${" "}
|
|
150
|
+
</span>
|
|
151
|
+
{command}
|
|
152
|
+
</pre>
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function FileArgsView({
|
|
158
|
+
path,
|
|
159
|
+
category,
|
|
160
|
+
args,
|
|
161
|
+
}: {
|
|
162
|
+
path: string;
|
|
163
|
+
category: string;
|
|
164
|
+
args: Record<string, unknown> | null;
|
|
165
|
+
}) {
|
|
166
|
+
const writeContent = useMemo(() => {
|
|
167
|
+
if (category !== "write" && category !== "edit") return null;
|
|
168
|
+
if (!args) return null;
|
|
169
|
+
return extractWriteContentFromArgs(args);
|
|
170
|
+
}, [category, args]);
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<div className="space-y-1.5">
|
|
174
|
+
<div className="flex items-center gap-1.5 text-xs">
|
|
175
|
+
<FilePathIcon />
|
|
176
|
+
<FilePathLink path={path} className="text-xs" />
|
|
177
|
+
</div>
|
|
178
|
+
{writeContent && (
|
|
179
|
+
<CollapsibleCode label="Content" content={writeContent} />
|
|
180
|
+
)}
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function SearchArgsView({ pattern }: { pattern: string }) {
|
|
186
|
+
return (
|
|
187
|
+
<div className="flex items-center gap-1.5 text-xs">
|
|
188
|
+
<span className="text-muted-foreground">Pattern:</span>
|
|
189
|
+
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-foreground">
|
|
190
|
+
{pattern}
|
|
191
|
+
</code>
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function McpArgsPreview({
|
|
197
|
+
toolName,
|
|
198
|
+
args,
|
|
199
|
+
mcpServerSlug,
|
|
200
|
+
}: {
|
|
201
|
+
toolName: string;
|
|
202
|
+
args: Record<string, unknown> | null;
|
|
203
|
+
mcpServerSlug: string;
|
|
204
|
+
}) {
|
|
205
|
+
return (
|
|
206
|
+
<div className="space-y-2">
|
|
207
|
+
<McpMetadataRow
|
|
208
|
+
mcpServerSlug={mcpServerSlug}
|
|
209
|
+
toolName={toolName}
|
|
210
|
+
duration={null}
|
|
211
|
+
/>
|
|
212
|
+
{args && Object.keys(args).length > 0 && <McpArgsView args={args} />}
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function GenericArgsView({ args }: { args: Record<string, unknown> }) {
|
|
218
|
+
return (
|
|
219
|
+
<CollapsibleCode label="Arguments" content={formatJson(args)} />
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
// Utilities
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
|
|
227
|
+
const WRITE_CONTENT_FIELDS = [
|
|
228
|
+
"contents",
|
|
229
|
+
"content",
|
|
230
|
+
"file_content",
|
|
231
|
+
"new_text",
|
|
232
|
+
"new_string",
|
|
233
|
+
"replacement",
|
|
234
|
+
] as const;
|
|
235
|
+
|
|
236
|
+
function extractWriteContentFromArgs(
|
|
237
|
+
args: Record<string, unknown>,
|
|
238
|
+
): string | null {
|
|
239
|
+
for (const field of WRITE_CONTENT_FIELDS) {
|
|
240
|
+
const val = args[field];
|
|
241
|
+
if (typeof val === "string" && val.length > 0) return val;
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function extractPrimaryArgValue(
|
|
247
|
+
args: Record<string, unknown> | null,
|
|
248
|
+
info: ToolCategoryInfo,
|
|
249
|
+
): string | null {
|
|
250
|
+
if (!args) return null;
|
|
251
|
+
|
|
252
|
+
const tryField = (field: string): string | null => {
|
|
253
|
+
const val = args[field];
|
|
254
|
+
if (typeof val === "string" && val.length > 0) return val;
|
|
255
|
+
return null;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
if (info.primaryArgField) {
|
|
259
|
+
const v = tryField(info.primaryArgField);
|
|
260
|
+
if (v) return v;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
for (const fb of info.fallbackArgFields) {
|
|
264
|
+
const v = tryField(fb);
|
|
265
|
+
if (v) return v;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (info.category === "unknown" || info.category === "mcp") {
|
|
269
|
+
const keys = Object.keys(args);
|
|
270
|
+
if (keys.length > 0) {
|
|
271
|
+
const val = args[keys[0]];
|
|
272
|
+
if (typeof val === "string") return val;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return null;
|
|
277
|
+
}
|