diff --git a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap index ade0bd1f85..4aed3e9af5 100644 --- a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap +++ b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap @@ -5,6 +5,12 @@ exports[`SYSTEM_PROMPT should exclude diff strategy tool description when diffEn ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -472,6 +478,12 @@ exports[`SYSTEM_PROMPT should exclude diff strategy tool description when diffEn ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -939,6 +951,12 @@ exports[`SYSTEM_PROMPT should explicitly handle undefined mcpHub 1`] = ` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -1406,6 +1424,12 @@ exports[`SYSTEM_PROMPT should handle different browser viewport sizes 1`] = ` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -1929,6 +1953,12 @@ exports[`SYSTEM_PROMPT should include MCP server info when mcpHub is provided 1` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -2464,6 +2494,12 @@ exports[`SYSTEM_PROMPT should include browser actions when supportsComputerUse i ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -2987,6 +3023,12 @@ exports[`SYSTEM_PROMPT should include diff strategy tool description when diffEn ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -3544,6 +3586,12 @@ exports[`SYSTEM_PROMPT should maintain consistent system prompt 1`] = ` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -4053,6 +4101,12 @@ exports[`addCustomInstructions should exclude MCP server creation info when disa ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -4597,6 +4651,12 @@ exports[`addCustomInstructions should generate correct prompt for architect mode ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -5055,6 +5115,12 @@ exports[`addCustomInstructions should generate correct prompt for ask mode 1`] = ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -5430,6 +5496,12 @@ exports[`addCustomInstructions should include MCP server creation info when enab ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. diff --git a/src/core/prompts/sections/index.ts b/src/core/prompts/sections/index.ts index a9f8c9e4c6..d06dbbfde1 100644 --- a/src/core/prompts/sections/index.ts +++ b/src/core/prompts/sections/index.ts @@ -7,3 +7,4 @@ export { getMcpServersSection } from "./mcp-servers" export { getToolUseGuidelinesSection } from "./tool-use-guidelines" export { getCapabilitiesSection } from "./capabilities" export { getModesSection } from "./modes" +export { markdownFormattingSection } from "./markdown-formatting" diff --git a/src/core/prompts/sections/markdown-formatting.ts b/src/core/prompts/sections/markdown-formatting.ts new file mode 100644 index 0000000000..fe152a1a41 --- /dev/null +++ b/src/core/prompts/sections/markdown-formatting.ts @@ -0,0 +1,7 @@ +export function markdownFormattingSection(): string { + return `==== + +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in ` +} diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index f56a947663..92eba6bdc7 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -24,6 +24,7 @@ import { getCapabilitiesSection, getModesSection, addCustomInstructions, + markdownFormattingSection, } from "./sections" import { loadSystemPromptFile } from "./sections/custom-system-prompt" import { formatLanguage } from "../../shared/language" @@ -65,6 +66,8 @@ async function generatePrompt( const basePrompt = `${roleDefinition} +${markdownFormattingSection()} + ${getSharedToolUseSection()} ${getToolDescriptionsForMode( diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index cee8ec7618..8528870a63 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -336,7 +336,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We openImage(message.text!) break case "openFile": - openFile(message.text!, message.values as { create?: boolean; content?: string }) + openFile(message.text!, message.values as { create?: boolean; content?: string; line?: number }) break case "openMention": openMention(message.text) diff --git a/src/integrations/misc/open-file.ts b/src/integrations/misc/open-file.ts index b3724068b6..52352314b3 100644 --- a/src/integrations/misc/open-file.ts +++ b/src/integrations/misc/open-file.ts @@ -23,6 +23,7 @@ export async function openImage(dataUri: string) { interface OpenFileOptions { create?: boolean content?: string + line?: number } export async function openFile(filePath: string, options: OpenFileOptions = {}) { @@ -75,7 +76,12 @@ export async function openFile(filePath: string, options: OpenFileOptions = {}) } catch {} // not essential, sometimes tab operations fail const document = await vscode.workspace.openTextDocument(uri) - await vscode.window.showTextDocument(document, { preview: false }) + const selection = + options.line !== undefined ? new vscode.Selection(options.line - 1, 0, options.line - 1, 0) : undefined + await vscode.window.showTextDocument(document, { + preview: false, + selection, + }) } catch (error) { if (error instanceof Error) { vscode.window.showErrorMessage(`Could not open file: ${error.message}`) diff --git a/webview-ui/src/components/common/MarkdownBlock.tsx b/webview-ui/src/components/common/MarkdownBlock.tsx index be7d545980..a9c5ada19a 100644 --- a/webview-ui/src/components/common/MarkdownBlock.tsx +++ b/webview-ui/src/components/common/MarkdownBlock.tsx @@ -3,6 +3,7 @@ import { useRemark } from "react-remark" import styled from "styled-components" import { visit } from "unist-util-visit" +import { vscode } from "@src/utils/vscode" import { useExtensionState } from "@src/context/ExtensionStateContext" import CodeBlock from "./CodeBlock" @@ -108,11 +109,14 @@ const StyledMarkdown = styled.div` } a { - text-decoration: none; - } - a { + color: var(--vscode-textLink-foreground); + text-decoration-line: underline; + text-decoration-style: dotted; + text-decoration-color: var(--vscode-textLink-foreground); &:hover { - text-decoration: underline; + color: var(--vscode-textLink-activeForeground); + text-decoration-style: solid; + text-decoration-color: var(--vscode-textLink-activeForeground); } } ` @@ -137,6 +141,48 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => { rehypePlugins: [], rehypeReactOptions: { components: { + a: ({ href, children }: any) => { + return ( + { + // Only process file:// protocol or local file paths + const isLocalPath = + href.startsWith("file://") || href.startsWith("/") || !href.includes("://") + + if (!isLocalPath) { + return + } + + e.preventDefault() + + // Handle absolute vs project-relative paths + let filePath = href.replace("file://", "") + + // Extract line number if present + const match = filePath.match(/(.*):(\d+)(-\d+)?$/) + let values = undefined + if (match) { + filePath = match[1] + values = { line: parseInt(match[2]) } + } + + // Add ./ prefix if needed + if (!filePath.startsWith("/") && !filePath.startsWith("./")) { + filePath = "./" + filePath + } + + vscode.postMessage({ + type: "openFile", + text: filePath, + values, + }) + }}> + {children} + + ) + }, pre: ({ node: _, children }: any) => { // Check for Mermaid diagrams first if (Array.isArray(children) && children.length === 1 && React.isValidElement(children[0])) {