mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Merge 745aeb36c8 into b867ec9145
This commit is contained in:
commit
ee05b781aa
21 changed files with 195 additions and 28 deletions
|
|
@ -14,12 +14,35 @@ import { safeJsonParse } from "@roo/core"
|
|||
|
||||
import { cn } from "@src/lib/utils"
|
||||
import { Button } from "@src/components/ui"
|
||||
import { useExtensionState } from "@src/context/ExtensionStateContext"
|
||||
|
||||
import CodeBlock from "../common/CodeBlock"
|
||||
import McpToolRow from "../mcp/McpToolRow"
|
||||
|
||||
import { Markdown } from "./Markdown"
|
||||
|
||||
/**
|
||||
* Truncates workspace paths in a JSON string by replacing the workspace root with "./"
|
||||
* @param jsonString - The JSON string containing paths to truncate
|
||||
* @param cwd - The current workspace directory (optional)
|
||||
* @returns The JSON string with truncated paths
|
||||
*/
|
||||
function truncateWorkspacePaths(jsonString: string, cwd?: string): string {
|
||||
if (!jsonString || !cwd) return jsonString
|
||||
|
||||
// Normalize the cwd to handle different path separators
|
||||
const normalizedCwd = cwd.replace(/\\/g, "/")
|
||||
|
||||
// Escape special regex characters in the path
|
||||
const escapedCwd = normalizedCwd.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
||||
|
||||
// Create a regex that matches the workspace path (with optional trailing slash)
|
||||
const workspacePathRegex = new RegExp(escapedCwd + "/?", "g")
|
||||
|
||||
// Replace workspace paths with "./"
|
||||
return jsonString.replace(workspacePathRegex, "./")
|
||||
}
|
||||
|
||||
interface McpExecutionProps {
|
||||
executionId: string
|
||||
text?: string
|
||||
|
|
@ -49,6 +72,7 @@ export const McpExecution = ({
|
|||
alwaysAllowMcp = false,
|
||||
}: McpExecutionProps) => {
|
||||
const { t } = useTranslation("mcp")
|
||||
const { cwd } = useExtensionState()
|
||||
|
||||
// State for tracking MCP response status
|
||||
const [status, setStatus] = useState<McpExecutionStatus | null>(null)
|
||||
|
|
@ -92,6 +116,7 @@ export const McpExecution = ({
|
|||
}, [responseText, isResponseExpanded, tryParseJson, status])
|
||||
|
||||
// Only parse arguments data when complete to avoid parsing partial JSON
|
||||
// Also apply workspace path truncation for cleaner display
|
||||
const argumentsData = useMemo(() => {
|
||||
if (!argumentsText) {
|
||||
return { isJson: false, formatted: "" }
|
||||
|
|
@ -108,19 +133,22 @@ export const McpExecution = ({
|
|||
// Try to parse, but if it fails, return as-is
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed)
|
||||
const formatted = JSON.stringify(parsed, null, 2)
|
||||
// Apply path truncation to show cleaner paths
|
||||
const truncated = truncateWorkspacePaths(formatted, cwd)
|
||||
return {
|
||||
isJson: true,
|
||||
formatted: JSON.stringify(parsed, null, 2),
|
||||
formatted: truncated,
|
||||
}
|
||||
} catch {
|
||||
// JSON structure looks complete but is invalid, return as-is
|
||||
return { isJson: false, formatted: argumentsText }
|
||||
// JSON structure looks complete but is invalid, return as-is (but still truncate paths)
|
||||
return { isJson: false, formatted: truncateWorkspacePaths(argumentsText, cwd) }
|
||||
}
|
||||
}
|
||||
|
||||
// For non-JSON or incomplete data, just return as-is
|
||||
return { isJson: false, formatted: argumentsText }
|
||||
}, [argumentsText])
|
||||
// For non-JSON or incomplete data, just return as-is (but still truncate paths)
|
||||
return { isJson: false, formatted: truncateWorkspacePaths(argumentsText, cwd) }
|
||||
}, [argumentsText, cwd])
|
||||
|
||||
const formattedResponseText = responseData.formatted
|
||||
const formattedArgumentsText = argumentsData.formatted
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import { useState } from "react"
|
||||
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
|
||||
import { ChevronDown } from "lucide-react"
|
||||
|
||||
import type { McpTool } from "@roo-code/types"
|
||||
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
import { cn } from "@src/lib/utils"
|
||||
import { StandardTooltip, ToggleSwitch } from "@/components/ui"
|
||||
|
||||
type McpToolRowProps = {
|
||||
|
|
@ -17,6 +20,7 @@ type McpToolRowProps = {
|
|||
const McpToolRow = ({ tool, serverName, serverSource, alwaysAllowMcp, isInChatContext = false }: McpToolRowProps) => {
|
||||
const { t } = useAppTranslation()
|
||||
const isToolEnabled = tool.enabledForPrompt ?? true
|
||||
const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
|
||||
|
||||
const handleAlwaysAllowChange = () => {
|
||||
if (!serverName) return
|
||||
|
|
@ -99,10 +103,39 @@ const McpToolRow = ({ tool, serverName, serverSource, alwaysAllowMcp, isInChatCo
|
|||
</div>
|
||||
{tool.description && (
|
||||
<div
|
||||
className={`mt-1 text-xs text-vscode-descriptionForeground ${
|
||||
isToolEnabled ? "opacity-80" : "opacity-40"
|
||||
}`}>
|
||||
{tool.description}
|
||||
className={cn("mt-1 text-xs text-vscode-descriptionForeground", {
|
||||
"opacity-80": isToolEnabled,
|
||||
"opacity-40": !isToolEnabled,
|
||||
})}>
|
||||
{isInChatContext ? (
|
||||
<div className="flex items-start gap-1">
|
||||
<StandardTooltip
|
||||
content={isDescriptionExpanded ? t("mcp:tool.collapse") : t("mcp:tool.expand")}>
|
||||
<button
|
||||
onClick={() => setIsDescriptionExpanded(!isDescriptionExpanded)}
|
||||
className="flex items-center p-0.5 -ml-0.5 rounded hover:bg-vscode-list-hoverBackground"
|
||||
data-testid="description-toggle">
|
||||
<ChevronDown
|
||||
className={cn("size-3 transition-transform duration-200", {
|
||||
"rotate-0": isDescriptionExpanded,
|
||||
"-rotate-90": !isDescriptionExpanded,
|
||||
})}
|
||||
/>
|
||||
</button>
|
||||
</StandardTooltip>
|
||||
<StandardTooltip content={tool.description}>
|
||||
<span
|
||||
className={cn("cursor-pointer", {
|
||||
"line-clamp-2": !isDescriptionExpanded,
|
||||
})}
|
||||
onClick={() => setIsDescriptionExpanded(!isDescriptionExpanded)}>
|
||||
{tool.description}
|
||||
</span>
|
||||
</StandardTooltip>
|
||||
</div>
|
||||
) : (
|
||||
tool.description
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{isToolEnabled &&
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ vi.mock("@src/i18n/TranslationContext", () => ({
|
|||
"mcp:tool.parameters": "Parameters",
|
||||
"mcp:tool.noDescription": "No description",
|
||||
"mcp:tool.togglePromptInclusion": "Toggle prompt inclusion",
|
||||
"mcp:tool.expand": "Expand description",
|
||||
"mcp:tool.collapse": "Collapse description",
|
||||
}
|
||||
return translations[key] || key
|
||||
},
|
||||
|
|
@ -285,4 +287,72 @@ describe("McpToolRow", () => {
|
|||
expect(toolDescription).toHaveClass("opacity-80")
|
||||
expect(toolDescription).not.toHaveClass("opacity-40")
|
||||
})
|
||||
|
||||
describe("collapsible description in chat context", () => {
|
||||
const toolWithLongDescription = {
|
||||
...mockTool,
|
||||
description:
|
||||
"This is a very long description that should be truncated to two lines when displayed in chat context. It provides detailed information about what the tool does and how it works.",
|
||||
}
|
||||
|
||||
it("shows collapsible description when isInChatContext is true", () => {
|
||||
render(<McpToolRow tool={toolWithLongDescription} isInChatContext={true} />)
|
||||
|
||||
// Should have the toggle button
|
||||
const toggleButton = screen.getByTestId("description-toggle")
|
||||
expect(toggleButton).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("does not show collapsible description when isInChatContext is false", () => {
|
||||
render(<McpToolRow tool={toolWithLongDescription} isInChatContext={false} />)
|
||||
|
||||
// Should not have the toggle button
|
||||
const toggleButton = screen.queryByTestId("description-toggle")
|
||||
expect(toggleButton).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("expands description when toggle button is clicked", () => {
|
||||
render(<McpToolRow tool={toolWithLongDescription} isInChatContext={true} />)
|
||||
|
||||
const toggleButton = screen.getByTestId("description-toggle")
|
||||
|
||||
// Initially collapsed - description should have line-clamp-2 class
|
||||
const descriptionText = screen.getByText(toolWithLongDescription.description)
|
||||
expect(descriptionText).toHaveClass("line-clamp-2")
|
||||
|
||||
// Click to expand
|
||||
fireEvent.click(toggleButton)
|
||||
|
||||
// After expanding - description should not have line-clamp-2 class
|
||||
expect(descriptionText).not.toHaveClass("line-clamp-2")
|
||||
})
|
||||
|
||||
it("collapses description when toggle button is clicked twice", () => {
|
||||
render(<McpToolRow tool={toolWithLongDescription} isInChatContext={true} />)
|
||||
|
||||
const toggleButton = screen.getByTestId("description-toggle")
|
||||
const descriptionText = screen.getByText(toolWithLongDescription.description)
|
||||
|
||||
// Click to expand
|
||||
fireEvent.click(toggleButton)
|
||||
expect(descriptionText).not.toHaveClass("line-clamp-2")
|
||||
|
||||
// Click again to collapse
|
||||
fireEvent.click(toggleButton)
|
||||
expect(descriptionText).toHaveClass("line-clamp-2")
|
||||
})
|
||||
|
||||
it("expands description when clicking on the description text", () => {
|
||||
render(<McpToolRow tool={toolWithLongDescription} isInChatContext={true} />)
|
||||
|
||||
const descriptionText = screen.getByText(toolWithLongDescription.description)
|
||||
expect(descriptionText).toHaveClass("line-clamp-2")
|
||||
|
||||
// Click on the description text
|
||||
fireEvent.click(descriptionText)
|
||||
|
||||
// Should expand
|
||||
expect(descriptionText).not.toHaveClass("line-clamp-2")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ca/mcp.json
generated
4
webview-ui/src/i18n/locales/ca/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "Permet sempre",
|
||||
"parameters": "Paràmetres",
|
||||
"noDescription": "Sense descripció",
|
||||
"togglePromptInclusion": "Canviar inclusió al prompt"
|
||||
"togglePromptInclusion": "Canviar inclusió al prompt",
|
||||
"expand": "Ampliar descripció",
|
||||
"collapse": "Reduir descripció"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Eines",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/de/mcp.json
generated
4
webview-ui/src/i18n/locales/de/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "Immer erlauben",
|
||||
"parameters": "Parameter",
|
||||
"noDescription": "Keine Beschreibung",
|
||||
"togglePromptInclusion": "Einbeziehung in Prompt umschalten"
|
||||
"togglePromptInclusion": "Einbeziehung in Prompt umschalten",
|
||||
"expand": "Beschreibung ausklappen",
|
||||
"collapse": "Beschreibung einklappen"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Tools",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@
|
|||
"alwaysAllow": "Always allow",
|
||||
"parameters": "Parameters",
|
||||
"noDescription": "No description",
|
||||
"togglePromptInclusion": "Toggle inclusion in prompt"
|
||||
"togglePromptInclusion": "Toggle inclusion in prompt",
|
||||
"expand": "Expand description",
|
||||
"collapse": "Collapse description"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Tools",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/es/mcp.json
generated
4
webview-ui/src/i18n/locales/es/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "Permitir siempre",
|
||||
"parameters": "Parámetros",
|
||||
"noDescription": "Sin descripción",
|
||||
"togglePromptInclusion": "Alternar inclusión en el prompt"
|
||||
"togglePromptInclusion": "Alternar inclusión en el prompt",
|
||||
"expand": "Expandir descripción",
|
||||
"collapse": "Contraer descripción"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Herramientas",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/fr/mcp.json
generated
4
webview-ui/src/i18n/locales/fr/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "Toujours autoriser",
|
||||
"parameters": "Paramètres",
|
||||
"noDescription": "Aucune description",
|
||||
"togglePromptInclusion": "Basculer l'inclusion dans le prompt"
|
||||
"togglePromptInclusion": "Basculer l'inclusion dans le prompt",
|
||||
"expand": "Développer la description",
|
||||
"collapse": "Réduire la description"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Outils",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/hi/mcp.json
generated
4
webview-ui/src/i18n/locales/hi/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "हमेशा अनुमति दें",
|
||||
"parameters": "पैरामीटर",
|
||||
"noDescription": "कोई विवरण नहीं",
|
||||
"togglePromptInclusion": "प्रॉम्प्ट में शामिल करना टॉगल करें"
|
||||
"togglePromptInclusion": "प्रॉम्प्ट में शामिल करना टॉगल करें",
|
||||
"expand": "विवरण विस्तारित करें",
|
||||
"collapse": "विवरण संक्षिप्त करें"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "टूल्स",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/id/mcp.json
generated
4
webview-ui/src/i18n/locales/id/mcp.json
generated
|
|
@ -20,7 +20,9 @@
|
|||
"alwaysAllow": "Selalu izinkan",
|
||||
"parameters": "Parameter",
|
||||
"noDescription": "Tidak ada deskripsi",
|
||||
"togglePromptInclusion": "Aktifkan daya untuk meminta"
|
||||
"togglePromptInclusion": "Aktifkan daya untuk meminta",
|
||||
"expand": "Perluas deskripsi",
|
||||
"collapse": "Ciutkan deskripsi"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Tools",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/it/mcp.json
generated
4
webview-ui/src/i18n/locales/it/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "Consenti sempre",
|
||||
"parameters": "Parametri",
|
||||
"noDescription": "Nessuna descrizione",
|
||||
"togglePromptInclusion": "Attiva/disattiva inclusione nel prompt"
|
||||
"togglePromptInclusion": "Attiva/disattiva inclusione nel prompt",
|
||||
"expand": "Espandi descrizione",
|
||||
"collapse": "Comprimi descrizione"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Strumenti",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ja/mcp.json
generated
4
webview-ui/src/i18n/locales/ja/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "常に許可",
|
||||
"parameters": "パラメータ",
|
||||
"noDescription": "説明なし",
|
||||
"togglePromptInclusion": "プロンプトへの含有を切り替える"
|
||||
"togglePromptInclusion": "プロンプトへの含有を切り替える",
|
||||
"expand": "説明を展開",
|
||||
"collapse": "説明を折りたたむ"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "ツール",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ko/mcp.json
generated
4
webview-ui/src/i18n/locales/ko/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "항상 허용",
|
||||
"parameters": "매개변수",
|
||||
"noDescription": "설명 없음",
|
||||
"togglePromptInclusion": "프롬프트 포함 여부 전환"
|
||||
"togglePromptInclusion": "프롬프트 포함 여부 전환",
|
||||
"expand": "설명 펼치기",
|
||||
"collapse": "설명 접기"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "도구",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/nl/mcp.json
generated
4
webview-ui/src/i18n/locales/nl/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "Altijd toestaan",
|
||||
"parameters": "Parameters",
|
||||
"noDescription": "Geen beschrijving",
|
||||
"togglePromptInclusion": "Inclusie in prompt in-/uitschakelen"
|
||||
"togglePromptInclusion": "Inclusie in prompt in-/uitschakelen",
|
||||
"expand": "Beschrijving uitvouwen",
|
||||
"collapse": "Beschrijving invouwen"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Tools",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/pl/mcp.json
generated
4
webview-ui/src/i18n/locales/pl/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "Zawsze pozwalaj",
|
||||
"parameters": "Parametry",
|
||||
"noDescription": "Brak opisu",
|
||||
"togglePromptInclusion": "Przełącz uwzględnianie w podpowiedzi"
|
||||
"togglePromptInclusion": "Przełącz uwzględnianie w podpowiedzi",
|
||||
"expand": "Rozwiń opis",
|
||||
"collapse": "Zwiń opis"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Narzędzia",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/pt-BR/mcp.json
generated
4
webview-ui/src/i18n/locales/pt-BR/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "Sempre permitir",
|
||||
"parameters": "Parâmetros",
|
||||
"noDescription": "Sem descrição",
|
||||
"togglePromptInclusion": "Alternar inclusão no prompt"
|
||||
"togglePromptInclusion": "Alternar inclusão no prompt",
|
||||
"expand": "Expandir descrição",
|
||||
"collapse": "Recolher descrição"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Ferramentas",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ru/mcp.json
generated
4
webview-ui/src/i18n/locales/ru/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "Всегда разрешать",
|
||||
"parameters": "Параметры",
|
||||
"noDescription": "Нет описания",
|
||||
"togglePromptInclusion": "Переключить включение в промпт"
|
||||
"togglePromptInclusion": "Переключить включение в промпт",
|
||||
"expand": "Развернуть описание",
|
||||
"collapse": "Свернуть описание"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Инструменты",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/tr/mcp.json
generated
4
webview-ui/src/i18n/locales/tr/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "Her zaman izin ver",
|
||||
"parameters": "Parametreler",
|
||||
"noDescription": "Açıklama yok",
|
||||
"togglePromptInclusion": "Komut isteminde dahil etmeyi aç/kapat"
|
||||
"togglePromptInclusion": "Komut isteminde dahil etmeyi aç/kapat",
|
||||
"expand": "Açıklamayı genişlet",
|
||||
"collapse": "Açıklamayı daralt"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Araçlar",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/vi/mcp.json
generated
4
webview-ui/src/i18n/locales/vi/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "Luôn cho phép",
|
||||
"parameters": "Tham số",
|
||||
"noDescription": "Không có mô tả",
|
||||
"togglePromptInclusion": "Bật/tắt bao gồm trong lời nhắc"
|
||||
"togglePromptInclusion": "Bật/tắt bao gồm trong lời nhắc",
|
||||
"expand": "Mở rộng mô tả",
|
||||
"collapse": "Thu gọn mô tả"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "Công cụ",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/zh-CN/mcp.json
generated
4
webview-ui/src/i18n/locales/zh-CN/mcp.json
generated
|
|
@ -19,7 +19,9 @@
|
|||
"alwaysAllow": "始终允许",
|
||||
"parameters": "参数",
|
||||
"noDescription": "无描述",
|
||||
"togglePromptInclusion": "切换在提示中的包含"
|
||||
"togglePromptInclusion": "切换在提示中的包含",
|
||||
"expand": "展开描述",
|
||||
"collapse": "收起描述"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "工具",
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/zh-TW/mcp.json
generated
4
webview-ui/src/i18n/locales/zh-TW/mcp.json
generated
|
|
@ -20,7 +20,9 @@
|
|||
"alwaysAllow": "總是允許",
|
||||
"parameters": "參數",
|
||||
"noDescription": "暫無說明",
|
||||
"togglePromptInclusion": "切換提示包含狀態"
|
||||
"togglePromptInclusion": "切換提示包含狀態",
|
||||
"expand": "展開說明",
|
||||
"collapse": "收合說明"
|
||||
},
|
||||
"tabs": {
|
||||
"tools": "工具",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue