mirror of
https://github.com/cline/cline.git
synced 2026-09-04 11:44:01 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f1624aaf71 | |||
| 71c4f71b29 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
add checkpoints after more messages
|
||||
+73
-45
@@ -927,6 +927,7 @@ export class Task {
|
||||
let responseImages: string[] | undefined
|
||||
if (response === "messageResponse") {
|
||||
await this.say("user_feedback", text, images)
|
||||
await this.saveCheckpoint()
|
||||
responseText = text
|
||||
responseImages = images
|
||||
}
|
||||
@@ -1326,6 +1327,7 @@ export class Task {
|
||||
|
||||
if (userFeedback) {
|
||||
await this.say("user_feedback", userFeedback.text, userFeedback.images)
|
||||
await this.saveCheckpoint()
|
||||
return [
|
||||
true,
|
||||
formatResponse.toolResult(
|
||||
@@ -1763,6 +1765,7 @@ export class Task {
|
||||
if (text || images?.length) {
|
||||
pushAdditionalToolFeedback(text, images)
|
||||
await this.say("user_feedback", text, images)
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
this.didRejectTool = true // Prevent further tool uses in this message
|
||||
return false
|
||||
@@ -1771,6 +1774,7 @@ export class Task {
|
||||
if (text || images?.length) {
|
||||
pushAdditionalToolFeedback(text, images)
|
||||
await this.say("user_feedback", text, images)
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1845,7 +1849,7 @@ export class Task {
|
||||
if (!accessAllowed) {
|
||||
await this.say("clineignore_error", relPath)
|
||||
pushToolResult(formatResponse.toolError(formatResponse.clineIgnoreError(relPath)))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1901,6 +1905,7 @@ export class Task {
|
||||
)
|
||||
await this.diffViewProvider.revertChanges()
|
||||
await this.diffViewProvider.reset()
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} else if (content) {
|
||||
@@ -1958,28 +1963,28 @@ export class Task {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError(block.name, "path"))
|
||||
await this.diffViewProvider.reset()
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
if (block.name === "replace_in_file" && !diff) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("replace_in_file", "diff"))
|
||||
await this.diffViewProvider.reset()
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
if (block.name === "write_to_file" && !content) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("write_to_file", "content"))
|
||||
await this.diffViewProvider.reset()
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
if (block.name === "new_rule" && !content) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("new_rule", "content"))
|
||||
await this.diffViewProvider.reset()
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2038,6 +2043,7 @@ export class Task {
|
||||
if (text || images?.length) {
|
||||
pushAdditionalToolFeedback(text, images)
|
||||
await this.say("user_feedback", text, images)
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
this.didRejectTool = true
|
||||
didApprove = false
|
||||
@@ -2047,12 +2053,14 @@ export class Task {
|
||||
if (text || images?.length) {
|
||||
pushAdditionalToolFeedback(text, images)
|
||||
await this.say("user_feedback", text, images)
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, true)
|
||||
}
|
||||
|
||||
if (!didApprove) {
|
||||
await this.diffViewProvider.revertChanges()
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2113,7 +2121,7 @@ export class Task {
|
||||
await handleError("writing file", error)
|
||||
await this.diffViewProvider.revertChanges()
|
||||
await this.diffViewProvider.reset()
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2142,7 +2150,7 @@ export class Task {
|
||||
if (!relPath) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("read_file", "path"))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2150,7 +2158,7 @@ export class Task {
|
||||
if (!accessAllowed) {
|
||||
await this.say("clineignore_error", relPath)
|
||||
pushToolResult(formatResponse.toolError(formatResponse.clineIgnoreError(relPath)))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2173,6 +2181,7 @@ export class Task {
|
||||
this.removeLastPartialMessageIfExistsWithType("say", "tool")
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, false)
|
||||
break
|
||||
}
|
||||
@@ -2185,12 +2194,12 @@ export class Task {
|
||||
await this.fileContextTracker.trackFileContext(relPath, "read_tool")
|
||||
|
||||
pushToolResult(content)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("reading file", error)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2221,7 +2230,7 @@ export class Task {
|
||||
if (!relDirPath) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("list_files", "path"))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2254,17 +2263,18 @@ export class Task {
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
if (!didApprove) {
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, false)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, true)
|
||||
}
|
||||
pushToolResult(result)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("listing files", error)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2293,7 +2303,7 @@ export class Task {
|
||||
if (!relDirPath) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("list_code_definition_names", "path"))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2323,17 +2333,18 @@ export class Task {
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
if (!didApprove) {
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, false)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, true)
|
||||
}
|
||||
pushToolResult(result)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("parsing source code definitions", error)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2366,13 +2377,13 @@ export class Task {
|
||||
if (!relDirPath) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("search_files", "path"))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
if (!regex) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("search_files", "regex"))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2404,17 +2415,18 @@ export class Task {
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
if (!didApprove) {
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, false)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, true)
|
||||
}
|
||||
pushToolResult(results)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("searching files", error)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2430,6 +2442,7 @@ export class Task {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("browser_action", "action"))
|
||||
await this.browserSession.closeBrowser()
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -2473,7 +2486,7 @@ export class Task {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("browser_action", "url"))
|
||||
await this.browserSession.closeBrowser()
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2489,6 +2502,7 @@ export class Task {
|
||||
this.removeLastPartialMessageIfExistsWithType("say", "browser_action_launch")
|
||||
const didApprove = await askApproval("browser_action_launch", url)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2514,7 +2528,7 @@ export class Task {
|
||||
await this.sayAndCreateMissingParamError("browser_action", "coordinate"),
|
||||
)
|
||||
await this.browserSession.closeBrowser()
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break // can't be within an inner switch
|
||||
}
|
||||
}
|
||||
@@ -2523,7 +2537,7 @@ export class Task {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("browser_action", "text"))
|
||||
await this.browserSession.closeBrowser()
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2572,7 +2586,7 @@ export class Task {
|
||||
browserActionResult.screenshot ? [browserActionResult.screenshot] : [],
|
||||
),
|
||||
)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
case "close":
|
||||
pushToolResult(
|
||||
@@ -2580,7 +2594,7 @@ export class Task {
|
||||
`The browser has been closed. You may now proceed to using other tools.`,
|
||||
),
|
||||
)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2589,7 +2603,7 @@ export class Task {
|
||||
} catch (error) {
|
||||
await this.browserSession.closeBrowser() // if any error occurs, the browser session is terminated
|
||||
await handleError("executing browser action", error)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2617,7 +2631,7 @@ export class Task {
|
||||
if (!command) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("execute_command", "command"))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
if (!requiresApprovalRaw) {
|
||||
@@ -2625,7 +2639,7 @@ export class Task {
|
||||
pushToolResult(
|
||||
await this.sayAndCreateMissingParamError("execute_command", "requires_approval"),
|
||||
)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2641,7 +2655,7 @@ export class Task {
|
||||
pushToolResult(
|
||||
formatResponse.toolError(formatResponse.clineIgnoreError(ignoredFileAttemptedToAccess)),
|
||||
)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2673,6 +2687,7 @@ export class Task {
|
||||
`${this.shouldAutoApproveTool(block.name) && requiresApprovalPerLLM ? COMMAND_REQ_APP_STRING : ""}`, // ugly hack until we refactor combineCommandSequences
|
||||
)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2708,7 +2723,7 @@ export class Task {
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("executing command", error)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2738,13 +2753,13 @@ export class Task {
|
||||
if (!server_name) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("use_mcp_tool", "server_name"))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
if (!tool_name) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("use_mcp_tool", "tool_name"))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
// arguments are optional, but if they are provided they must be valid JSON
|
||||
@@ -2768,7 +2783,7 @@ export class Task {
|
||||
formatResponse.invalidMcpToolArgumentError(server_name, tool_name),
|
||||
),
|
||||
)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2795,6 +2810,7 @@ export class Task {
|
||||
this.removeLastPartialMessageIfExistsWithType("say", "use_mcp_server")
|
||||
const didApprove = await askApproval("use_mcp_server", completeMessage)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2846,7 +2862,7 @@ export class Task {
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("executing MCP tool", error)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2874,13 +2890,13 @@ export class Task {
|
||||
if (!server_name) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("access_mcp_resource", "server_name"))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
if (!uri) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("access_mcp_resource", "uri"))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2901,6 +2917,7 @@ export class Task {
|
||||
this.removeLastPartialMessageIfExistsWithType("say", "use_mcp_server")
|
||||
const didApprove = await askApproval("use_mcp_server", completeMessage)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2920,12 +2937,12 @@ export class Task {
|
||||
.join("\n\n") || "(Empty response)"
|
||||
await this.say("mcp_server_response", resourceResultPretty)
|
||||
pushToolResult(formatResponse.toolResult(resourceResultPretty))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("accessing MCP resource", error)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2944,7 +2961,7 @@ export class Task {
|
||||
if (!question) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("ask_followup_question", "question"))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2981,12 +2998,12 @@ export class Task {
|
||||
}
|
||||
|
||||
pushToolResult(formatResponse.toolResult(`<answer>\n${text}\n</answer>`, images))
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("asking question", error)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -3000,6 +3017,7 @@ export class Task {
|
||||
if (!context) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("new_task", "context"))
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -3028,10 +3046,12 @@ export class Task {
|
||||
formatResponse.toolResult(`The user has created a new task with the provided context.`),
|
||||
)
|
||||
}
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("creating new task", error)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -3045,6 +3065,7 @@ export class Task {
|
||||
if (!context) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("condense", "context"))
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -3087,10 +3108,12 @@ export class Task {
|
||||
await ensureTaskDirectoryExists(this.getContext(), this.taskId),
|
||||
)
|
||||
}
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("condensing context window", error)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -3151,6 +3174,7 @@ export class Task {
|
||||
if (text || images?.length) {
|
||||
telemetryService.captureOptionsIgnored(this.taskId, options.length, "plan")
|
||||
await this.say("user_feedback", text ?? "", images)
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3297,12 +3321,14 @@ export class Task {
|
||||
// complete command message
|
||||
const didApprove = await askApproval("command", command)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
const [userRejected, execCommandResult] = await this.executeCommandTool(command!)
|
||||
if (userRejected) {
|
||||
this.didRejectTool = true
|
||||
pushToolResult(execCommandResult)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
// user didn't reject, but the command may have output
|
||||
@@ -3321,6 +3347,7 @@ export class Task {
|
||||
break
|
||||
}
|
||||
await this.say("user_feedback", text ?? "", images)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
const toolResults: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[] = []
|
||||
if (commandResult) {
|
||||
@@ -3349,7 +3376,7 @@ export class Task {
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("attempting completion", error)
|
||||
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -3451,9 +3478,6 @@ export class Task {
|
||||
|
||||
// Save checkpoint if this is the first API request
|
||||
const isFirstRequest = this.clineMessages.filter((m) => m.say === "api_req_started").length === 0
|
||||
if (isFirstRequest) {
|
||||
await this.say("checkpoint_created") // no hash since we need to wait for CheckpointTracker to be initialized
|
||||
}
|
||||
|
||||
// getting verbose details is an expensive operation, it uses globby to top-down build file structure of project which for large projects can take a few seconds
|
||||
// for the best UX we show a placeholder api_req_started message with a loading spinner as this happens
|
||||
@@ -3464,6 +3488,10 @@ export class Task {
|
||||
}),
|
||||
)
|
||||
|
||||
if (isFirstRequest) {
|
||||
await this.say("checkpoint_created") // no hash since we need to wait for CheckpointTracker to be initialized
|
||||
}
|
||||
|
||||
// use this opportunity to initialize the checkpoint tracker (can be expensive to initialize in the constructor)
|
||||
// FIXME: right now we're letting users init checkpoints for old tasks, but this could be a problem if opening a task in the wrong workspace
|
||||
// isNewTask &&
|
||||
|
||||
Reference in New Issue
Block a user