Skip to content

Commit a99e68b

Browse files
mrubenscte
authored andcommitted
Move insert_content to a tool file (#2095)
1 parent f4b3ea0 commit a99e68b

File tree

2 files changed

+164
-146
lines changed

2 files changed

+164
-146
lines changed

src/core/Cline.ts

Lines changed: 3 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import { parseXml } from "../utils/xml"
8787
import { getWorkspacePath } from "../utils/path"
8888
import { writeToFileTool } from "./tools/writeToFileTool"
8989
import { applyDiffTool } from "./tools/applyDiffTool"
90+
import { insertContentTool } from "./tools/insertContentTool"
9091

9192
export type ToolResponse = string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam>
9293
type UserContent = Array<Anthropic.Messages.ContentBlockParam>
@@ -1572,153 +1573,9 @@ export class Cline extends EventEmitter<ClineEvents> {
15721573
case "apply_diff":
15731574
await applyDiffTool(this, block, askApproval, handleError, pushToolResult, removeClosingTag)
15741575
break
1575-
case "insert_content": {
1576-
const relPath: string | undefined = block.params.path
1577-
const operations: string | undefined = block.params.operations
1578-
1579-
const sharedMessageProps: ClineSayTool = {
1580-
tool: "appliedDiff",
1581-
path: getReadablePath(this.cwd, removeClosingTag("path", relPath)),
1582-
}
1583-
1584-
try {
1585-
if (block.partial) {
1586-
const partialMessage = JSON.stringify(sharedMessageProps)
1587-
await this.ask("tool", partialMessage, block.partial).catch(() => {})
1588-
break
1589-
}
1590-
1591-
// Validate required parameters
1592-
if (!relPath) {
1593-
this.consecutiveMistakeCount++
1594-
pushToolResult(await this.sayAndCreateMissingParamError("insert_content", "path"))
1595-
break
1596-
}
1597-
1598-
if (!operations) {
1599-
this.consecutiveMistakeCount++
1600-
pushToolResult(await this.sayAndCreateMissingParamError("insert_content", "operations"))
1601-
break
1602-
}
1603-
1604-
const absolutePath = path.resolve(this.cwd, relPath)
1605-
const fileExists = await fileExistsAtPath(absolutePath)
1606-
1607-
if (!fileExists) {
1608-
this.consecutiveMistakeCount++
1609-
const formattedError = `File does not exist at path: ${absolutePath}\n\n<error_details>\nThe specified file could not be found. Please verify the file path and try again.\n</error_details>`
1610-
await this.say("error", formattedError)
1611-
pushToolResult(formattedError)
1612-
break
1613-
}
1614-
1615-
let parsedOperations: Array<{
1616-
start_line: number
1617-
content: string
1618-
}>
1619-
1620-
try {
1621-
parsedOperations = JSON.parse(operations)
1622-
if (!Array.isArray(parsedOperations)) {
1623-
throw new Error("Operations must be an array")
1624-
}
1625-
} catch (error) {
1626-
this.consecutiveMistakeCount++
1627-
await this.say("error", `Failed to parse operations JSON: ${error.message}`)
1628-
pushToolResult(formatResponse.toolError("Invalid operations JSON format"))
1629-
break
1630-
}
1631-
1632-
this.consecutiveMistakeCount = 0
1633-
1634-
// Read the file
1635-
const fileContent = await fs.readFile(absolutePath, "utf8")
1636-
this.diffViewProvider.editType = "modify"
1637-
this.diffViewProvider.originalContent = fileContent
1638-
const lines = fileContent.split("\n")
1639-
1640-
const updatedContent = insertGroups(
1641-
lines,
1642-
parsedOperations.map((elem) => {
1643-
return {
1644-
index: elem.start_line - 1,
1645-
elements: elem.content.split("\n"),
1646-
}
1647-
}),
1648-
).join("\n")
1649-
1650-
// Show changes in diff view
1651-
if (!this.diffViewProvider.isEditing) {
1652-
await this.ask("tool", JSON.stringify(sharedMessageProps), true).catch(() => {})
1653-
// First open with original content
1654-
await this.diffViewProvider.open(relPath)
1655-
await this.diffViewProvider.update(fileContent, false)
1656-
this.diffViewProvider.scrollToFirstDiff()
1657-
await delay(200)
1658-
}
1659-
1660-
const diff = formatResponse.createPrettyPatch(relPath, fileContent, updatedContent)
1661-
1662-
if (!diff) {
1663-
pushToolResult(`No changes needed for '${relPath}'`)
1664-
break
1665-
}
1666-
1667-
await this.diffViewProvider.update(updatedContent, true)
1668-
1669-
const completeMessage = JSON.stringify({
1670-
...sharedMessageProps,
1671-
diff,
1672-
} satisfies ClineSayTool)
1673-
1674-
const didApprove = await this.ask("tool", completeMessage, false).then(
1675-
(response) => response.response === "yesButtonClicked",
1676-
)
1677-
1678-
if (!didApprove) {
1679-
await this.diffViewProvider.revertChanges()
1680-
pushToolResult("Changes were rejected by the user.")
1681-
break
1682-
}
1683-
1684-
const { newProblemsMessage, userEdits, finalContent } =
1685-
await this.diffViewProvider.saveChanges()
1686-
this.didEditFile = true
1687-
1688-
if (!userEdits) {
1689-
pushToolResult(
1690-
`The content was successfully inserted in ${relPath.toPosix()}.${newProblemsMessage}`,
1691-
)
1692-
await this.diffViewProvider.reset()
1693-
break
1694-
}
1695-
1696-
const userFeedbackDiff = JSON.stringify({
1697-
tool: "appliedDiff",
1698-
path: getReadablePath(this.cwd, relPath),
1699-
diff: userEdits,
1700-
} satisfies ClineSayTool)
1701-
1702-
console.debug("[DEBUG] User made edits, sending feedback diff:", userFeedbackDiff)
1703-
await this.say("user_feedback_diff", userFeedbackDiff)
1704-
pushToolResult(
1705-
`The user made the following updates to your content:\n\n${userEdits}\n\n` +
1706-
`The updated content, which includes both your original modifications and the user's edits, has been successfully saved to ${relPath.toPosix()}. Here is the full, updated content of the file:\n\n` +
1707-
`<final_file_content path="${relPath.toPosix()}">\n${finalContent}\n</final_file_content>\n\n` +
1708-
`Please note:\n` +
1709-
`1. You do not need to re-write the file with these changes, as they have already been applied.\n` +
1710-
`2. Proceed with the task using this updated file content as the new baseline.\n` +
1711-
`3. If the user's edits have addressed part of the task or changed the requirements, adjust your approach accordingly.` +
1712-
`${newProblemsMessage}`,
1713-
)
1714-
await this.diffViewProvider.reset()
1715-
} catch (error) {
1716-
handleError("insert content", error)
1717-
await this.diffViewProvider.reset()
1718-
}
1576+
case "insert_content":
1577+
await insertContentTool(this, block, askApproval, handleError, pushToolResult, removeClosingTag)
17191578
break
1720-
}
1721-
17221579
case "search_and_replace": {
17231580
const relPath: string | undefined = block.params.path
17241581
const operations: string | undefined = block.params.operations
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { getReadablePath } from "../../utils/path"
2+
import { Cline } from "../Cline"
3+
import { ToolUse } from "../assistant-message"
4+
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
5+
import { formatResponse } from "../prompts/responses"
6+
import { ClineSayTool } from "../../shared/ExtensionMessage"
7+
import path from "path"
8+
import { fileExistsAtPath } from "../../utils/fs"
9+
import { insertGroups } from "../diff/insert-groups"
10+
import delay from "delay"
11+
import fs from "fs/promises"
12+
13+
export async function insertContentTool(
14+
cline: Cline,
15+
block: ToolUse,
16+
askApproval: AskApproval,
17+
handleError: HandleError,
18+
pushToolResult: PushToolResult,
19+
removeClosingTag: RemoveClosingTag,
20+
) {
21+
const relPath: string | undefined = block.params.path
22+
const operations: string | undefined = block.params.operations
23+
24+
const sharedMessageProps: ClineSayTool = {
25+
tool: "appliedDiff",
26+
path: getReadablePath(cline.cwd, removeClosingTag("path", relPath)),
27+
}
28+
29+
try {
30+
if (block.partial) {
31+
const partialMessage = JSON.stringify(sharedMessageProps)
32+
await cline.ask("tool", partialMessage, block.partial).catch(() => {})
33+
return
34+
}
35+
36+
// Validate required parameters
37+
if (!relPath) {
38+
cline.consecutiveMistakeCount++
39+
pushToolResult(await cline.sayAndCreateMissingParamError("insert_content", "path"))
40+
return
41+
}
42+
43+
if (!operations) {
44+
cline.consecutiveMistakeCount++
45+
pushToolResult(await cline.sayAndCreateMissingParamError("insert_content", "operations"))
46+
return
47+
}
48+
49+
const absolutePath = path.resolve(cline.cwd, relPath)
50+
const fileExists = await fileExistsAtPath(absolutePath)
51+
52+
if (!fileExists) {
53+
cline.consecutiveMistakeCount++
54+
const formattedError = `File does not exist at path: ${absolutePath}\n\n<error_details>\nThe specified file could not be found. Please verify the file path and try again.\n</error_details>`
55+
await cline.say("error", formattedError)
56+
pushToolResult(formattedError)
57+
return
58+
}
59+
60+
let parsedOperations: Array<{
61+
start_line: number
62+
content: string
63+
}>
64+
65+
try {
66+
parsedOperations = JSON.parse(operations)
67+
if (!Array.isArray(parsedOperations)) {
68+
throw new Error("Operations must be an array")
69+
}
70+
} catch (error) {
71+
cline.consecutiveMistakeCount++
72+
await cline.say("error", `Failed to parse operations JSON: ${error.message}`)
73+
pushToolResult(formatResponse.toolError("Invalid operations JSON format"))
74+
return
75+
}
76+
77+
cline.consecutiveMistakeCount = 0
78+
79+
// Read the file
80+
const fileContent = await fs.readFile(absolutePath, "utf8")
81+
cline.diffViewProvider.editType = "modify"
82+
cline.diffViewProvider.originalContent = fileContent
83+
const lines = fileContent.split("\n")
84+
85+
const updatedContent = insertGroups(
86+
lines,
87+
parsedOperations.map((elem) => {
88+
return {
89+
index: elem.start_line - 1,
90+
elements: elem.content.split("\n"),
91+
}
92+
}),
93+
).join("\n")
94+
95+
// Show changes in diff view
96+
if (!cline.diffViewProvider.isEditing) {
97+
await cline.ask("tool", JSON.stringify(sharedMessageProps), true).catch(() => {})
98+
// First open with original content
99+
await cline.diffViewProvider.open(relPath)
100+
await cline.diffViewProvider.update(fileContent, false)
101+
cline.diffViewProvider.scrollToFirstDiff()
102+
await delay(200)
103+
}
104+
105+
const diff = formatResponse.createPrettyPatch(relPath, fileContent, updatedContent)
106+
107+
if (!diff) {
108+
pushToolResult(`No changes needed for '${relPath}'`)
109+
return
110+
}
111+
112+
await cline.diffViewProvider.update(updatedContent, true)
113+
114+
const completeMessage = JSON.stringify({
115+
...sharedMessageProps,
116+
diff,
117+
} satisfies ClineSayTool)
118+
119+
const didApprove = await cline
120+
.ask("tool", completeMessage, false)
121+
.then((response) => response.response === "yesButtonClicked")
122+
123+
if (!didApprove) {
124+
await cline.diffViewProvider.revertChanges()
125+
pushToolResult("Changes were rejected by the user.")
126+
return
127+
}
128+
129+
const { newProblemsMessage, userEdits, finalContent } = await cline.diffViewProvider.saveChanges()
130+
cline.didEditFile = true
131+
132+
if (!userEdits) {
133+
pushToolResult(`The content was successfully inserted in ${relPath.toPosix()}.${newProblemsMessage}`)
134+
await cline.diffViewProvider.reset()
135+
return
136+
}
137+
138+
const userFeedbackDiff = JSON.stringify({
139+
tool: "appliedDiff",
140+
path: getReadablePath(cline.cwd, relPath),
141+
diff: userEdits,
142+
} satisfies ClineSayTool)
143+
144+
console.debug("[DEBUG] User made edits, sending feedback diff:", userFeedbackDiff)
145+
await cline.say("user_feedback_diff", userFeedbackDiff)
146+
pushToolResult(
147+
`The user made the following updates to your content:\n\n${userEdits}\n\n` +
148+
`The updated content, which includes both your original modifications and the user's edits, has been successfully saved to ${relPath.toPosix()}. Here is the full, updated content of the file:\n\n` +
149+
`<final_file_content path="${relPath.toPosix()}">\n${finalContent}\n</final_file_content>\n\n` +
150+
`Please note:\n` +
151+
`1. You do not need to re-write the file with these changes, as they have already been applied.\n` +
152+
`2. Proceed with the task using cline updated file content as the new baseline.\n` +
153+
`3. If the user's edits have addressed part of the task or changed the requirements, adjust your approach accordingly.` +
154+
`${newProblemsMessage}`,
155+
)
156+
await cline.diffViewProvider.reset()
157+
} catch (error) {
158+
handleError("insert content", error)
159+
await cline.diffViewProvider.reset()
160+
}
161+
}

0 commit comments

Comments
 (0)