feat: add ability to edit subtask instructions

- Added edit button to subtask instructions in orchestrator mode
- Users can now modify subtask content before approval
- Reduces unnecessary LLM calls and token usage
- Implements inline editing similar to user message editing

Fixes #9593
This commit is contained in:
Roo Code 2025-11-25 23:53:38 +00:00
parent 3c05cae722
commit aa35136558
3 changed files with 137 additions and 4 deletions

View file

@ -1542,6 +1542,56 @@ export const webviewMessageHandler = async (
}
break
}
case "submitEditedSubtask": {
// Handle editing of subtask instructions
const currentCline = provider.getCurrentTask()
if (currentCline && message.messageTs && message.editedSubtaskContent) {
// Find the message with the newTask tool
const messageIndex = currentCline.clineMessages.findIndex(
(msg: ClineMessage) => msg.ts === message.messageTs,
)
if (messageIndex !== -1) {
const targetMessage = currentCline.clineMessages[messageIndex]
// Parse the tool content to update it
if (targetMessage.ask === "tool" && targetMessage.text) {
try {
const tool = JSON.parse(targetMessage.text)
if (tool.tool === "newTask") {
// Update the content with the edited subtask instructions
tool.content = message.editedSubtaskContent
// Update the message with the new content
currentCline.clineMessages[messageIndex].text = JSON.stringify(tool)
// Save the updated messages
await saveTaskMessages({
messages: currentCline.clineMessages,
taskId: currentCline.taskId,
globalStoragePath: provider.contextProxy.globalStorageUri.fsPath,
})
// Update the UI to reflect the changes
await provider.postStateToWebview()
// Now approve the subtask with the edited content
// This simulates the user clicking "Approve" after editing
currentCline.handleWebviewAskResponse("yesButtonClicked")
}
} catch (error) {
console.error("Error updating subtask instructions:", error)
vscode.window.showErrorMessage(
t("common:errors.message.error_editing_subtask", {
error: error instanceof Error ? error.message : String(error),
}),
)
}
}
}
}
break
}
case "hasOpenedModeSelector":
await updateGlobalState("hasOpenedModeSelector", message.bool ?? true)

View file

@ -94,6 +94,7 @@ export interface WebviewMessage {
| "deleteMessageConfirm"
| "submitEditedMessage"
| "editMessageConfirm"
| "submitEditedSubtask"
| "enableMcpServerCreation"
| "remoteControlEnabled"
| "taskSyncEnabled"
@ -181,6 +182,7 @@ export interface WebviewMessage {
| "requestClaudeCodeRateLimits"
text?: string
editedMessageContent?: string
editedSubtaskContent?: string
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "cloud"
disabled?: boolean
context?: string

View file

@ -174,6 +174,10 @@ export const ChatRowContent = ({
const [editMode, setEditMode] = useState<Mode>(mode || "code")
const [editImages, setEditImages] = useState<string[]>([])
// State for editing subtask instructions
const [isEditingSubtask, setIsEditingSubtask] = useState(false)
const [editedSubtaskContent, setEditedSubtaskContent] = useState("")
// Handle message events for image selection during edit mode
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
@ -792,6 +796,30 @@ export const ChatRowContent = ({
</>
)
case "newTask":
// Handle edit button click for subtask
const handleEditSubtaskClick = () => {
setIsEditingSubtask(true)
setEditedSubtaskContent(tool.content || "")
}
// Handle cancel edit for subtask
const handleCancelEditSubtask = () => {
setIsEditingSubtask(false)
setEditedSubtaskContent(tool.content || "")
}
// Handle save edit for subtask
const handleSaveEditSubtask = () => {
setIsEditingSubtask(false)
// Send edited subtask content to backend
vscode.postMessage({
type: "submitEditedSubtask",
messageTs: message.ts,
editedSubtaskContent: editedSubtaskContent,
mode: tool.mode,
})
}
return (
<>
<div style={headerStyle}>
@ -805,6 +833,7 @@ export const ChatRowContent = ({
</span>
</div>
<div
className="group"
style={{
marginTop: "4px",
backgroundColor: "var(--vscode-badge-background)",
@ -812,6 +841,7 @@ export const ChatRowContent = ({
borderRadius: "4px 4px 0 0",
overflow: "hidden",
marginBottom: "2px",
position: "relative",
}}>
<div
style={{
@ -823,13 +853,64 @@ export const ChatRowContent = ({
color: "var(--vscode-badge-foreground)",
display: "flex",
alignItems: "center",
gap: "6px",
justifyContent: "space-between",
}}>
<span className="codicon codicon-arrow-right"></span>
{t("chat:subtasks.newTaskContent")}
<div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
<span className="codicon codicon-arrow-right"></span>
{t("chat:subtasks.newTaskContent")}
</div>
{/* Edit button for subtask instructions */}
{message.type === "ask" && !isStreaming && !isEditingSubtask && (
<div
className="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity"
onClick={(e) => {
e.stopPropagation()
handleEditSubtaskClick()
}}
title={t("chat:editSubtaskInstructions")}
style={{
padding: "2px 4px",
borderRadius: "3px",
display: "flex",
alignItems: "center",
gap: "4px",
}}>
<Edit className="w-4 h-4" aria-label="Edit subtask instructions" />
</div>
)}
</div>
<div style={{ padding: "12px 16px", backgroundColor: "var(--vscode-editor-background)" }}>
<MarkdownBlock markdown={tool.content} />
{isEditingSubtask ? (
<div className="flex flex-col gap-2">
<textarea
value={editedSubtaskContent}
onChange={(e) => setEditedSubtaskContent(e.target.value)}
className="w-full p-2 border border-vscode-input-border bg-vscode-input-background text-vscode-input-foreground rounded"
style={{
minHeight: "200px",
resize: "vertical",
fontFamily: "var(--vscode-editor-font-family)",
fontSize: "var(--vscode-editor-font-size)",
}}
placeholder={t("chat:editSubtaskInstructions.placeholder")}
autoFocus
/>
<div className="flex gap-2 justify-end">
<button
onClick={handleCancelEditSubtask}
className="px-3 py-1 rounded bg-vscode-button-secondaryBackground text-vscode-button-secondaryForeground hover:bg-vscode-button-secondaryHoverBackground">
{t("chat:cancel")}
</button>
<button
onClick={handleSaveEditSubtask}
className="px-3 py-1 rounded bg-vscode-button-background text-vscode-button-foreground hover:bg-vscode-button-hoverBackground">
{t("chat:save")}
</button>
</div>
</div>
) : (
<MarkdownBlock markdown={tool.content} />
)}
</div>
</div>
</>