Compare commits

...

8 Commits

Author SHA1 Message Date
Cline Evaluation 36a37f5951 Revert "gather user system info"
This reverts commit fb16c72224.
2025-05-09 01:00:58 -07:00
0xtoshii fb16c72224 gather user system info 2025-05-09 00:57:56 -07:00
pashpashpash d40acc2d73 Update webview-ui/src/components/chat/ChatView.tsx
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-05-08 23:34:09 -07:00
Cline Evaluation 3c016f6bf3 only asking for non-algorithmically derived info 2025-05-08 23:30:08 -07:00
Cline Evaluation e644f39614 sigh, portible way to open urls with proper escaping because vs code api is broken 2025-05-08 22:54:50 -07:00
0xtoshii c725d17c35 nits 2025-05-08 17:08:04 -07:00
0xtoshii f6a403ccd0 nits 2025-05-08 16:58:58 -07:00
0xtoshii e1fc921757 slash command report bug 2025-05-08 02:18:18 -07:00
12 changed files with 512 additions and 3 deletions
+6
View File
@@ -25,6 +25,7 @@ export const toolUseNames = [
"attempt_completion",
"new_task",
"condense",
"report_bug",
"new_rule",
] as const
@@ -53,6 +54,11 @@ export const toolParamNames = [
"response",
"result",
"context",
"title",
"what_happened",
"steps_to_reproduce",
"api_request_output",
"additional_context",
] as const
export type ToolParamName = (typeof toolParamNames)[number]
+3
View File
@@ -275,6 +275,9 @@ export class Controller {
case "condense":
this.task?.handleWebviewAskResponse("yesButtonClicked")
break
case "reportBug":
this.task?.handleWebviewAskResponse("yesButtonClicked")
break
case "apiConfiguration":
if (message.apiConfiguration) {
await updateApiConfiguration(this.context, message.apiConfiguration)
+31
View File
@@ -145,3 +145,34 @@ Example:
Below is the user's input when they indicated that they wanted to create a new Cline rule file.
</explicit_instructions>\n
`
export const reportBugToolResponse = () =>
`<explicit_instructions type="report_bug">
The user has explicitly asked you to help them submit a bug to the Cline github page (you MUST now help them with this irrespective of what your conversation up to this point in time was). To do so you will use the report_bug tool which is defined below. However, you must first ensure that you have collected all required information to fill in all the parameters for the tool call. If any of the the required information is apparent through your previous conversation with the user, you can suggest how to fill in those entries. However you should NOT assume you know what the issue about unless it's clear.
Otherwise, you should converse with the user until you are able to gather all the required details. When conversing with the user, make sure you ask for/reference all required information/fields. When referencing the required fields, use human friendly versions like "Steps to reproduce" rather than "steps_to_reproduce". Only then should you use the report_bug tool call.
The report_bug tool can be used in either of the PLAN or ACT modes.
The report_bug tool call is defined below:
Description:
Your task is to fill in all of the required fields for a issue/bug report on github. You should attempt to get the user to be as verbose as possible with their description of the bug/issue they encountered. Still, it's okay, when the user is unaware of some of the details, to set those fields as "N/A".
Parameters:
- title: (required) Concise description of the issue.
- what_happened: (required) What happened and also what the user expected to happen instead.
- steps_to_reproduce: (required) What steps are required to reproduce the bug.
- api_request_output: (optional) Relevant API request output.
- additional_context: (optional) Any other context about this bug not already mentioned.
Usage:
<report_bug>
<title>Title of the issue</title>
<what_happened>Description of the issue</what_happened>
<steps_to_reproduce>Steps to reproduce the issue</steps_to_reproduce>
<api_request_output>Output from the LLM API related to the bug</api_request_output>
<additional_context>Other issue details not already covered</additional_context>
</report_bug>
Below is the user's input when they indicated that they wanted to create a new Cline rule file.
</explicit_instructions>\n
`
+3 -2
View File
@@ -1,17 +1,18 @@
import { newTaskToolResponse, condenseToolResponse, newRuleToolResponse } from "../prompts/commands"
import { newTaskToolResponse, condenseToolResponse, newRuleToolResponse, reportBugToolResponse } from "../prompts/commands"
/**
* Processes text for slash commands and transforms them with appropriate instructions
* This is called after parseMentions() to process any slash commands in the user's message
*/
export function parseSlashCommands(text: string): { processedText: string; needsClinerulesFileCheck: boolean } {
const SUPPORTED_COMMANDS = ["newtask", "smol", "compact", "newrule"]
const SUPPORTED_COMMANDS = ["newtask", "smol", "compact", "newrule", "reportbug"]
const commandReplacements: Record<string, string> = {
newtask: newTaskToolResponse(),
smol: condenseToolResponse(),
compact: condenseToolResponse(),
newrule: newRuleToolResponse(),
reportbug: reportBugToolResponse(),
}
// this currently allows matching prepended whitespace prior to /slash-command
+132
View File
@@ -57,6 +57,7 @@ import { DEFAULT_LANGUAGE_SETTINGS, getLanguageKey, LanguageDisplay } from "@sha
import { ClineAskResponse, ClineCheckpointRestore } from "@shared/WebviewMessage"
import { calculateApiCostAnthropic } from "@utils/cost"
import { fileExistsAtPath } from "@utils/fs"
import { createAndOpenGitHubIssue } from "@utils/github-url-utils"
import { arePathsEqual, getReadablePath, isLocatedInWorkspace } from "@utils/path"
import { fixModelHtmlEscaping, removeInvalidChars } from "@utils/string"
import { AssistantMessageContent, parseAssistantMessage, ToolParamName, ToolUseName } from "@core/assistant-message"
@@ -1711,6 +1712,8 @@ export class Task {
return `[${block.name} for creating a new task]`
case "condense":
return `[${block.name}]`
case "report_bug":
return `[${block.name}]`
case "new_rule":
return `[${block.name} for '${block.params.path}']`
}
@@ -3138,6 +3141,135 @@ export class Task {
break
}
}
case "report_bug": {
const title = block.params.title
const what_happened = block.params.what_happened
const steps_to_reproduce = block.params.steps_to_reproduce
const api_request_output = block.params.api_request_output
const additional_context = block.params.additional_context
try {
if (block.partial) {
await this.ask(
"report_bug",
JSON.stringify({
title: removeClosingTag("title", title),
what_happened: removeClosingTag("what_happened", what_happened),
steps_to_reproduce: removeClosingTag("steps_to_reproduce", steps_to_reproduce),
api_request_output: removeClosingTag("api_request_output", api_request_output),
additional_context: removeClosingTag("additional_context", additional_context),
}),
block.partial,
).catch(() => {})
break
} else {
if (!title) {
this.consecutiveMistakeCount++
pushToolResult(await this.sayAndCreateMissingParamError("report_bug", "title"))
await this.saveCheckpoint()
break
}
if (!what_happened) {
this.consecutiveMistakeCount++
pushToolResult(await this.sayAndCreateMissingParamError("report_bug", "what_happened"))
await this.saveCheckpoint()
break
}
if (!steps_to_reproduce) {
this.consecutiveMistakeCount++
pushToolResult(await this.sayAndCreateMissingParamError("report_bug", "steps_to_reproduce"))
await this.saveCheckpoint()
break
}
if (!api_request_output) {
this.consecutiveMistakeCount++
pushToolResult(await this.sayAndCreateMissingParamError("report_bug", "api_request_output"))
await this.saveCheckpoint()
break
}
if (!additional_context) {
this.consecutiveMistakeCount++
pushToolResult(await this.sayAndCreateMissingParamError("report_bug", "additional_context"))
await this.saveCheckpoint()
break
}
this.consecutiveMistakeCount = 0
if (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) {
showSystemNotification({
subtitle: "Cline wants to create a github issue...",
message: `Cline is suggesting to create a github issue with the title: ${title}`,
})
}
// Derive system information values algorithmically
const operatingSystem = os.platform() + " " + os.release()
const clineVersion =
vscode.extensions.getExtension("saoudrizwan.claude-dev")?.packageJSON.version || "Unknown"
const systemInfo = `VSCode: ${vscode.version}, Node.js: ${process.version}, Architecture: ${os.arch()}`
const providerAndModel = `${(await getGlobalState(this.getContext(), "apiProvider")) as string} / ${this.api.getModel().id}`
// Ask user for confirmation
const bugReportData = JSON.stringify({
title,
what_happened,
steps_to_reproduce,
api_request_output,
additional_context,
// Include derived values in the JSON for display purposes
provider_and_model: providerAndModel,
operating_system: operatingSystem,
system_info: systemInfo,
cline_version: clineVersion,
})
const { text, images } = await this.ask("report_bug", bugReportData, false)
// If the user provided a response, treat it as feedback
if (text || images?.length) {
await this.say("user_feedback", text ?? "", images)
pushToolResult(
formatResponse.toolResult(
`The user provided feedback on the Github issue generated:\n<feedback>\n${text}\n</feedback>`,
images,
),
)
} else {
// If no response, the user accepted the condensed version
pushToolResult(
formatResponse.toolResult(`The user accepted the creation of the Github issue.`),
)
try {
// Create a Map of parameters for the GitHub issue
const params = new Map<string, string>()
params.set("title", title)
params.set("operating-system", operatingSystem)
params.set("cline-version", clineVersion)
params.set("system-info", systemInfo)
params.set("additional-context", additional_context)
params.set("what-happened", what_happened)
params.set("steps", steps_to_reproduce)
params.set("provider-model", providerAndModel)
params.set("logs", api_request_output)
// Use our utility function to create and open the GitHub issue URL
// This bypasses VS Code's URI handling issues with special characters
await createAndOpenGitHubIssue("cline", "cline", "bug_report.yml", params)
} catch (error) {
console.error(`An error occurred while attempting to report the bug: ${error}`)
}
}
await this.saveCheckpoint()
break
}
} catch (error) {
await handleError("reporting bug", error)
await this.saveCheckpoint()
break
}
}
case "plan_mode_respond": {
const response: string | undefined = block.params.response
const optionsRaw: string | undefined = block.params.options
+1
View File
@@ -179,6 +179,7 @@ export type ClineAsk =
| "use_mcp_server"
| "new_task"
| "condense"
| "report_bug"
export type ClineSay =
| "task"
+1
View File
@@ -13,6 +13,7 @@ export interface WebviewMessage {
| "webviewDidLaunch"
| "newTask"
| "condense"
| "reportBug"
| "askResponse"
| "didShowAnnouncement"
| "selectImages"
+208
View File
@@ -0,0 +1,208 @@
/**
* github-url-utils.ts
*
* Portable utility functions for creating and opening GitHub issue URLs
* with proper URL encoding that bypasses VS Code's URI handling issues.
*
* This utility addresses a longstanding issue in VS Code's URI handling:
* https://github.com/microsoft/vscode/issues/85930
*
* The issue causes URLs with special characters in query parameters to be incorrectly
* encoded when opened through VS Code's standard APIs (vscode.Uri.parse followed by
* vscode.env.openExternal). This particularly affects GitHub issue URLs with pre-filled
* fields containing special characters.
*/
import * as vscode from "vscode"
import * as cp from "child_process"
import * as os from "os"
import * as util from "util"
/**
* Creates a properly encoded GitHub issue URL.
*
* This function manually encodes each parameter value using encodeURIComponent()
* to ensure consistent and correct encoding of all special characters. This is
* necessary because VS Code's URI handling (vscode.Uri.parse) has issues with
* encoding/decoding URL parameters, as documented in:
* https://github.com/microsoft/vscode/issues/85930
*
* Specifically, VS Code's URI handling:
* - Double-encodes some characters like # (hash) becoming %2523 instead of %23
* - Inconsistently handles other characters like & (ampersand) and + (plus)
* - Can corrupt query parameters containing special characters
*
* @param baseUrl The base GitHub repository URL (e.g., 'https://github.com/owner/repo/issues/new')
* @param params Map of parameter names to values for the issue form
* @returns The properly encoded full URL
*/
export function createGitHubIssueUrl(baseUrl: string, params: Map<string, string>): string {
// Build query string manually with proper encoding
const queryParts: string[] = []
for (const [key, value] of params.entries()) {
const encodedKey = encodeURIComponent(key)
const encodedValue = encodeURIComponent(value)
queryParts.push(`${encodedKey}=${encodedValue}`)
}
// Determine the proper separator (? or &) based on whether baseUrl already has parameters
const separator = baseUrl.includes("?") ? "&" : "?"
// Join all parts to create the final URL
const queryString = queryParts.join("&")
return `${baseUrl}${separator}${queryString}`
}
/**
* Opens a URL using platform-specific commands to bypass VS Code's URI handling issues.
*
* IMPORTANT: This function intentionally avoids using VS Code's built-in URI handling
* (vscode.Uri.parse() and vscode.env.openExternal()) due to known encoding issues with URLs
* that contain special characters in query parameters. See:
* https://github.com/microsoft/vscode/issues/85930
*
* The specific issues with VS Code's URI handling include:
* 1. Double-encoding of certain characters (e.g., # becomes %23 then %2523)
* 2. Inconsistent handling where some characters are encoded and others are decoded
* 3. Issues with parameters in the query string being incorrectly processed
*
* Instead, this function:
* - Uses direct OS commands to open the browser with the URL
* - Preserves the exact encoding of the URL as provided
* - Provides multiple fallback approaches if the primary method fails
*
* @param url The URL to open
* @returns A promise that resolves when an attempt to open the URL has completed
*/
export async function openUrlInBrowser(url: string): Promise<void> {
// For debugging
console.log(`Opening URL: ${url}`)
// Always copy to clipboard as a fallback
try {
await vscode.env.clipboard.writeText(url)
console.log("URL copied to clipboard as backup")
} catch (error) {
console.error(`Failed to copy URL to clipboard: ${error}`)
}
// Try to open the URL using platform-specific commands
try {
const platform = os.platform()
console.log(`Detected platform: ${platform}`)
// Use promisify for better async error handling
const execPromise = util.promisify(cp.exec)
// Use platform-specific commands
if (platform === "win32") {
// Windows - try multiple approaches
try {
await execPromise(`start "" "${url}"`)
console.log("Opened URL with Windows 'start' command")
return
} catch (winError) {
console.error(`Error with Windows 'start' command: ${winError}`)
try {
await execPromise(`powershell.exe -Command "Start-Process '${url}'"`)
console.log("Opened URL with PowerShell command")
return
} catch (psError) {
console.error(`Error with PowerShell command: ${psError}`)
// Fall through to the fallbacks
}
}
} else if (platform === "darwin") {
// macOS
await execPromise(`open "${url}"`)
console.log("Opened URL with macOS 'open' command")
return
} else {
// Linux and others - try multiple commands
const linuxCommands = ["xdg-open", "gnome-open", "kde-open", "wslview"]
for (const cmd of linuxCommands) {
try {
await execPromise(`${cmd} "${url}"`)
console.log(`Opened URL with '${cmd}' command`)
return
} catch (cmdError) {
console.error(`Error with '${cmd}' command: ${cmdError}`)
// Try next command
}
}
}
// If we got here, none of the OS commands worked
throw new Error("All OS commands failed")
} catch (error) {
console.error(`OS commands failed: ${error}`)
// First fallback: Try VS Code's openExternal
// Note: This will likely have encoding issues per https://github.com/microsoft/vscode/issues/85930
// but we include it as a fallback in case OS commands completely fail
try {
// The 'true' parameter might help preserve some encodings, but this is not guaranteed
await vscode.env.openExternal(vscode.Uri.parse(url, true))
console.log("Opened URL with vscode.env.openExternal (note: URL encoding may be affected)")
return
} catch (vscodeError) {
console.error(`Error with vscode.env.openExternal: ${vscodeError}`)
// Last fallback: Show a message with instructions
vscode.window
.showInformationMessage(
"Couldn't open the URL automatically. It has been copied to your clipboard.",
"Copy URL Again",
)
.then((selection) => {
if (selection === "Copy URL Again") {
vscode.env.clipboard.writeText(url)
}
})
}
}
}
/**
* Utility function to create and open a GitHub issue with the specified parameters.
*
* This is a high-level function that combines URL creation and opening while
* working around VS Code's URI handling limitations (issue #85930). It provides
* a simple API for the common use case of opening GitHub issue templates with
* pre-filled fields.
*
* The function:
* 1. Constructs a correctly formatted GitHub issue URL
* 2. Properly encodes all special characters in parameters
* 3. Opens the URL directly using OS commands to avoid VS Code's problematic URI handling
* 4. Provides fallback options if opening fails
*
* Reference for the VS Code URI handling issue:
* https://github.com/microsoft/vscode/issues/85930
*
* @param repoOwner GitHub repository owner/organization
* @param repoName GitHub repository name
* @param issueTemplate Template name to use (e.g., 'bug_report.yml')
* @param params Map of parameter names to values for the issue form
*/
export async function createAndOpenGitHubIssue(
repoOwner: string,
repoName: string,
issueTemplate: string | null,
params: Map<string, string>,
): Promise<void> {
// Construct the base URL
let baseUrl = `https://github.com/${repoOwner}/${repoName}/issues/new`
// Add template parameter if provided
if (issueTemplate) {
params.set("template", issueTemplate)
}
// Create the URL and open it
const issueUrl = createGitHubIssueUrl(baseUrl, params)
await openUrlInBrowser(issueUrl)
}
@@ -89,6 +89,7 @@ import { highlightText } from "./TaskHeader"
import SuccessButton from "@/components/common/SuccessButton"
import TaskFeedbackButtons from "@/components/chat/TaskFeedbackButtons"
import NewTaskPreview from "./NewTaskPreview"
import ReportBugPreview from "./ReportBugPreview"
import McpResourceRow from "@/components/mcp/configuration/tabs/installed/server-row/McpResourceRow"
import UserMessage from "./UserMessage"
import QuoteButton from "./QuoteButton"
@@ -1475,6 +1476,23 @@ export const ChatRowContent = ({
<NewTaskPreview context={message.text || ""} />
</>
)
case "report_bug":
return (
<>
<div style={headerStyle}>
<span
className="codicon codicon-new-file"
style={{
color: normalColor,
marginBottom: "-1.5px",
}}></span>
<span style={{ color: normalColor, fontWeight: "bold" }}>
Cline wants to create a Github issue:
</span>
</div>
<ReportBugPreview data={message.text || ""} />
</>
)
case "plan_mode_respond": {
let response: string | undefined
let options: string[] | undefined
+21 -1
View File
@@ -288,6 +288,13 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
setPrimaryButtonText("Condense Conversation")
setSecondaryButtonText(undefined)
break
case "report_bug":
setSendingDisabled(isPartial)
setClineAsk("report_bug")
setEnableButtons(!isPartial)
setPrimaryButtonText("Report GitHub issue")
setSecondaryButtonText(undefined)
break
}
break
case "say":
@@ -416,6 +423,14 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
images,
})
break
case "report_bug":
vscode.postMessage({
type: "askResponse",
askResponse: "messageResponse",
text: messageToSend,
images,
})
break
// there is no other case that a textfield should be enabled
}
}
@@ -490,6 +505,12 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
text: lastMessage?.text,
})
break
case "report_bug":
vscode.postMessage({
type: "reportBug",
text: lastMessage?.text,
})
break
}
setSendingDisabled(true)
setClineAsk(undefined)
@@ -1101,7 +1122,6 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
</>
)}
{(() => {
console.log("[ChatView] Rendering - activeQuote:", activeQuote) // Log here
return activeQuote ? (
<div style={{ marginBottom: "-12px", marginTop: "10px" }}>
<QuotedMessagePreview
@@ -0,0 +1,84 @@
import React from "react"
import MarkdownBlock from "../common/MarkdownBlock"
interface ReportBugPreviewProps {
data: string
}
const ReportBugPreview: React.FC<ReportBugPreviewProps> = ({ data }) => {
// Parse the JSON data from the context string
const bugData = React.useMemo(() => {
try {
return JSON.parse(data || "{}")
} catch (e) {
console.error("Failed to parse bug report data", e)
return {}
}
}, [data])
return (
<div className="bg-[var(--vscode-badge-background)] text-[var(--vscode-badge-foreground)] rounded-[3px] p-[14px]">
<h3 className="font-bold text-base mb-3 mt-0">{bugData.title || "Bug Report"}</h3>
<div className="space-y-3 text-sm">
{bugData.what_happened && (
<div>
<div className="font-semibold">What Happened?</div>
<MarkdownBlock markdown={bugData.what_happened} />
</div>
)}
{bugData.steps_to_reproduce && (
<div>
<div className="font-semibold">Steps to Reproduce</div>
<MarkdownBlock markdown={bugData.steps_to_reproduce} />
</div>
)}
{bugData.api_request_output && (
<div>
<div className="font-semibold">Relevant API Request Output</div>
<MarkdownBlock markdown={bugData.api_request_output} />
</div>
)}
{bugData.provider_and_model && (
<div>
<div className="font-semibold">Provider/Model</div>
<MarkdownBlock markdown={bugData.provider_and_model} />
</div>
)}
{bugData.operating_system && (
<div>
<div className="font-semibold">Operating System</div>
<MarkdownBlock markdown={bugData.operating_system} />
</div>
)}
{bugData.system_info && (
<div>
<div className="font-semibold">System Info</div>
<MarkdownBlock markdown={bugData.system_info} />
</div>
)}
{bugData.cline_version && (
<div>
<div className="font-semibold">Cline Version</div>
<MarkdownBlock markdown={bugData.cline_version} />
</div>
)}
{bugData.additional_context && (
<div>
<div className="font-semibold">Additional Context</div>
<MarkdownBlock markdown={bugData.additional_context} />
</div>
)}
</div>
</div>
)
}
export default ReportBugPreview
+4
View File
@@ -16,6 +16,10 @@ export const SUPPORTED_SLASH_COMMANDS: SlashCommand[] = [
name: "newrule",
description: "Create a new Cline rule based on your conversation",
},
{
name: "reportbug",
description: "Create a Github issue with Cline",
},
]
// Regex for detecting slash commands in text