From eaee2fcb9154233cd306750acdab4ce2614ca176 Mon Sep 17 00:00:00 2001 From: axb Date: Thu, 26 Jun 2025 21:59:33 +0800 Subject: [PATCH 01/12] add todos --- packages/types/src/global-settings.ts | 2 + packages/types/src/index.ts | 1 + packages/types/src/message.ts | 1 + packages/types/src/todo.ts | 19 + packages/types/src/tool.ts | 1 + .../presentAssistantMessage.ts | 6 + src/core/environment/getEnvironmentDetails.ts | 4 +- src/core/environment/reminder.ts | 38 ++ src/core/prompts/system.ts | 5 +- src/core/prompts/tools/index.ts | 2 + src/core/prompts/tools/update-todo-list.ts | 76 +++ src/core/task/Task.ts | 8 +- src/core/tools/updateTodoListTool.ts | 237 ++++++++ src/core/webview/ClineProvider.ts | 3 + src/core/webview/webviewMessageHandler.ts | 13 + src/shared/ExtensionMessage.ts | 1 + src/shared/WebviewMessage.ts | 8 + src/shared/todo.ts | 23 + src/shared/tools.ts | 3 + .../src/components/chat/AutoApproveMenu.tsx | 8 + webview-ui/src/components/chat/ChatRow.tsx | 20 + webview-ui/src/components/chat/ChatView.tsx | 33 ++ webview-ui/src/components/chat/TaskHeader.tsx | 4 + .../src/components/chat/TodoListDisplay.tsx | 210 +++++++ .../chat/UpdateTodoListToolBlock.tsx | 550 ++++++++++++++++++ .../settings/AutoApproveSettings.tsx | 1 + .../components/settings/AutoApproveToggle.tsx | 8 + .../__tests__/AutoApproveToggle.spec.tsx | 1 + .../src/context/ExtensionStateContext.tsx | 7 + webview-ui/src/i18n/locales/ca/settings.json | 4 + webview-ui/src/i18n/locales/de/settings.json | 4 + webview-ui/src/i18n/locales/en/settings.json | 4 + webview-ui/src/i18n/locales/es/settings.json | 4 + webview-ui/src/i18n/locales/fr/settings.json | 4 + webview-ui/src/i18n/locales/hi/settings.json | 4 + webview-ui/src/i18n/locales/id/settings.json | 4 + webview-ui/src/i18n/locales/it/settings.json | 4 + webview-ui/src/i18n/locales/ja/settings.json | 4 + webview-ui/src/i18n/locales/ko/settings.json | 4 + webview-ui/src/i18n/locales/nl/settings.json | 4 + webview-ui/src/i18n/locales/pl/settings.json | 4 + .../src/i18n/locales/pt-BR/settings.json | 4 + webview-ui/src/i18n/locales/ru/settings.json | 4 + webview-ui/src/i18n/locales/tr/settings.json | 4 + webview-ui/src/i18n/locales/vi/settings.json | 4 + .../src/i18n/locales/zh-CN/settings.json | 4 + .../src/i18n/locales/zh-TW/settings.json | 4 + 47 files changed, 1362 insertions(+), 3 deletions(-) create mode 100644 packages/types/src/todo.ts create mode 100644 src/core/environment/reminder.ts create mode 100644 src/core/prompts/tools/update-todo-list.ts create mode 100644 src/core/tools/updateTodoListTool.ts create mode 100644 src/shared/todo.ts create mode 100644 webview-ui/src/components/chat/TodoListDisplay.tsx create mode 100644 webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index 6a0dda1cf2..79a09ff017 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -47,6 +47,7 @@ export const globalSettingsSchema = z.object({ alwaysAllowExecute: z.boolean().optional(), alwaysAllowFollowupQuestions: z.boolean().optional(), followupAutoApproveTimeoutMs: z.number().optional(), + alwaysAllowUpdateTodoList: z.boolean().optional(), allowedCommands: z.array(z.string()).optional(), allowedMaxRequests: z.number().nullish(), autoCondenseContext: z.boolean().optional(), @@ -195,6 +196,7 @@ export const EVALS_SETTINGS: RooCodeSettings = { alwaysAllowSubtasks: true, alwaysAllowExecute: true, alwaysAllowFollowupQuestions: true, + alwaysAllowUpdateTodoList: true, followupAutoApproveTimeoutMs: 0, allowedCommands: ["*"], diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 6c6db9ccd5..44937da235 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -20,3 +20,4 @@ export * from "./terminal.js" export * from "./tool.js" export * from "./type-fu.js" export * from "./vscode.js" +export * from "./todo.js" diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index 914f02ecd6..49d5203c66 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -106,6 +106,7 @@ export const clineSays = [ "condense_context", "condense_context_error", "codebase_search_result", + "user_edit_todos", ] as const export const clineSaySchema = z.enum(clineSays) diff --git a/packages/types/src/todo.ts b/packages/types/src/todo.ts new file mode 100644 index 0000000000..9400b5e7bd --- /dev/null +++ b/packages/types/src/todo.ts @@ -0,0 +1,19 @@ +import { z } from "zod" + +/** + * TodoStatus + */ +export const todoStatusSchema = z.enum(["pending", "in_progress", "completed", "cancelled"] as const) + +export type TodoStatus = z.infer + +/** + * TodoItem + */ +export const todoItemSchema = z.object({ + id: z.string().optional(), + content: z.string(), + status: todoStatusSchema, +}) + +export type TodoItem = z.infer diff --git a/packages/types/src/tool.ts b/packages/types/src/tool.ts index 9e807d639d..7a3fd21199 100644 --- a/packages/types/src/tool.ts +++ b/packages/types/src/tool.ts @@ -33,6 +33,7 @@ export const toolNames = [ "new_task", "fetch_instructions", "codebase_search", + "update_todo_list", ] as const export const toolNamesSchema = z.enum(toolNames) diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts index 21c973ab50..ab158c7ff6 100644 --- a/src/core/assistant-message/presentAssistantMessage.ts +++ b/src/core/assistant-message/presentAssistantMessage.ts @@ -26,6 +26,7 @@ import { attemptCompletionTool } from "../tools/attemptCompletionTool" import { newTaskTool } from "../tools/newTaskTool" import { checkpointSave } from "../checkpoints" +import { updateTodoListTool } from "../tools/updateTodoListTool" import { formatResponse } from "../prompts/responses" import { validateToolUse } from "../tools/validateToolUse" @@ -211,6 +212,8 @@ export async function presentAssistantMessage(cline: Task) { const modeName = getModeBySlug(mode, customModes)?.name ?? mode return `[${block.name} in ${modeName} mode: '${message}']` } + default: + return `[${block.name}]` } } @@ -410,6 +413,9 @@ export async function presentAssistantMessage(cline: Task) { case "write_to_file": await writeToFileTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) break + case "update_todo_list": + await updateTodoListTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break case "apply_diff": { // Get the provider and state to check experiment settings const provider = cline.providerRef.deref() diff --git a/src/core/environment/getEnvironmentDetails.ts b/src/core/environment/getEnvironmentDetails.ts index fa0bc7bb82..7a3847334d 100644 --- a/src/core/environment/getEnvironmentDetails.ts +++ b/src/core/environment/getEnvironmentDetails.ts @@ -18,6 +18,7 @@ import { arePathsEqual } from "../../utils/path" import { formatResponse } from "../prompts/responses" import { Task } from "../task/Task" +import { formatReminderSection } from "./reminder" export async function getEnvironmentDetails(cline: Task, includeFileDetails: boolean = false) { let details = "" @@ -273,5 +274,6 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo } } - return `\n${details.trim()}\n` + const reminderSection = formatReminderSection(cline.todoList) + return `\n${details.trim()}\n${reminderSection}\n` } diff --git a/src/core/environment/reminder.ts b/src/core/environment/reminder.ts new file mode 100644 index 0000000000..fc188c1bc3 --- /dev/null +++ b/src/core/environment/reminder.ts @@ -0,0 +1,38 @@ +import { TodoItem } from "@roo-code/types" + +/** + * Format the reminders section as a markdown block in English, with basic instructions. + */ +export function formatReminderSection(todoList?: TodoItem[]): string { + if (!todoList || todoList.length === 0) { + return "" + } + const statusMap: Record = { + pending: "Pending", + in_progress: "In Progress", + completed: "Completed", + } + const lines: string[] = [ + "====", + "", + "REMINDERS", + "", + "Below is your current list of reminders for this task. Keep them updated as you progress.", + "", + ] + + lines.push("| # | Content | Status |") + lines.push("|---|---------|--------|") + todoList.forEach((item, idx) => { + const escapedContent = item.content.replace(/\\/g, "\\\\").replace(/\|/g, "\\|") + lines.push(`| ${idx + 1} | ${escapedContent} | ${statusMap[item.status] || item.status} |`) + }) + lines.push("") + + lines.push( + "", + "IMPORTANT: When task status changes, remember to call the `update_todo_list` tool to update your progress.", + "", + ) + return lines.join("\n") +} diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 61fd9df81e..be3b91f146 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -1,7 +1,7 @@ import * as vscode from "vscode" import * as os from "os" -import type { ModeConfig, PromptComponent, CustomModePrompts } from "@roo-code/types" +import type { ModeConfig, PromptComponent, CustomModePrompts, TodoItem } from "@roo-code/types" import { Mode, modes, defaultModeSlug, getModeBySlug, getGroupName, getModeSelection } from "../../shared/modes" import { DiffStrategy } from "../../shared/tools" @@ -44,6 +44,7 @@ async function generatePrompt( rooIgnoreInstructions?: string, partialReadsEnabled?: boolean, settings?: Record, + todoList?: TodoItem[], ): Promise { if (!context) { throw new Error("Extension context is required for generating system prompt") @@ -122,6 +123,7 @@ export const SYSTEM_PROMPT = async ( rooIgnoreInstructions?: string, partialReadsEnabled?: boolean, settings?: Record, + todoList?: TodoItem[], ): Promise => { if (!context) { throw new Error("Extension context is required for generating system prompt") @@ -195,5 +197,6 @@ ${customInstructions}` rooIgnoreInstructions, partialReadsEnabled, settings, + todoList, ) } diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 736c716a27..3fd5a636a4 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -22,6 +22,7 @@ import { getAccessMcpResourceDescription } from "./access-mcp-resource" import { getSwitchModeDescription } from "./switch-mode" import { getNewTaskDescription } from "./new-task" import { getCodebaseSearchDescription } from "./codebase-search" +import { getUpdateTodoListDescription } from "./update-todo-list" import { CodeIndexManager } from "../../../services/code-index/manager" // Map of tool names to their description functions @@ -45,6 +46,7 @@ const toolDescriptionMap: Record string | undefined> search_and_replace: (args) => getSearchAndReplaceDescription(args), apply_diff: (args) => args.diffStrategy ? args.diffStrategy.getToolDescription({ cwd: args.cwd, toolOptions: args.toolOptions }) : "", + update_todo_list: (args) => getUpdateTodoListDescription(args), } export function getToolDescriptionsForMode( diff --git a/src/core/prompts/tools/update-todo-list.ts b/src/core/prompts/tools/update-todo-list.ts new file mode 100644 index 0000000000..96271c4838 --- /dev/null +++ b/src/core/prompts/tools/update-todo-list.ts @@ -0,0 +1,76 @@ +import { ToolArgs } from "./types" + +/** + * Get the description for the update_todo_list tool. + */ +export function getUpdateTodoListDescription(args?: ToolArgs): string { + return `## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. +` +} diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 4f0d32c8c1..31260cd6fa 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -20,6 +20,7 @@ import { type ToolProgressStatus, type HistoryItem, TelemetryEventName, + TodoItem, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" import { CloudService } from "@roo-code/cloud" @@ -85,6 +86,7 @@ import { processUserContentMentions } from "../mentions/processUserContentMentio import { ApiMessage } from "../task-persistence/apiMessages" import { getMessagesSinceLastSummary, summarizeConversation } from "../condense" import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning" +import { restoreTodoListForTask } from "../tools/updateTodoListTool" // Constants const MAX_EXPONENTIAL_BACKOFF_SECONDS = 600 // 10 minutes @@ -122,6 +124,7 @@ export type TaskOptions = { } export class Task extends EventEmitter { + todoList?: TodoItem[] readonly taskId: string readonly instanceId: string @@ -370,6 +373,7 @@ export class Task extends EventEmitter { public async overwriteClineMessages(newMessages: ClineMessage[]) { this.clineMessages = newMessages + restoreTodoListForTask(this) await this.saveClineMessages() } @@ -1716,7 +1720,9 @@ export class Task extends EventEmitter { const contextWindow = modelInfo.contextWindow - const currentProfileId = state?.listApiConfigMeta.find((profile) => profile.name === state?.currentApiConfigName)?.id ?? "default"; + const currentProfileId = + state?.listApiConfigMeta.find((profile) => profile.name === state?.currentApiConfigName)?.id ?? + "default" const truncateResult = await truncateConversationIfNeeded({ messages: this.apiConversationHistory, diff --git a/src/core/tools/updateTodoListTool.ts b/src/core/tools/updateTodoListTool.ts new file mode 100644 index 0000000000..7a196cb33c --- /dev/null +++ b/src/core/tools/updateTodoListTool.ts @@ -0,0 +1,237 @@ +import { Task } from "../task/Task" +import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" +import { formatResponse } from "../prompts/responses" + +import cloneDeep from "clone-deep" +import crypto from "crypto" +import { TodoItem, TodoStatus } from "@roo-code/types" +import { getLatestTodo } from "../../shared/todo" + +const VALID_STATUS: string[] = ["pending", "in_progress", "completed", "todo", "doing", "done"] +let approvedTodoList: TodoItem[] | undefined = undefined + +/** + * Add a todo item to the task's todoList. + */ +export function addTodoToTask(cline: Task, content: string, status: TodoStatus = "pending", id?: string): TodoItem { + const todo: TodoItem = { + id: id ?? crypto.randomUUID(), + content, + status, + } + if (!cline.todoList) cline.todoList = [] + cline.todoList.push(todo) + return todo +} + +/** + * Update the status of a todo item by id. + */ +export function updateTodoStatusForTask(cline: Task, id: string, nextStatus: TodoStatus): boolean { + if (!cline.todoList) return false + const idx = cline.todoList.findIndex((t) => t.id === id) + if (idx === -1) return false + const current = cline.todoList[idx] + if ( + (current.status === "pending" && nextStatus === "in_progress") || + (current.status === "in_progress" && nextStatus === "completed") || + current.status === nextStatus + ) { + cline.todoList[idx] = { ...current, status: nextStatus } + return true + } + return false +} + +/** + * Remove a todo item by id. + */ +export function removeTodoFromTask(cline: Task, id: string): boolean { + if (!cline.todoList) return false + const idx = cline.todoList.findIndex((t) => t.id === id) + if (idx === -1) return false + cline.todoList.splice(idx, 1) + return true +} + +/** + * Get a copy of the todoList. + */ +export function getTodoListForTask(cline: Task): TodoItem[] | undefined { + return cline.todoList?.slice() +} + +/** + * Set the todoList for the task. + */ +export async function setTodoListForTask(cline?: Task, todos?: TodoItem[]) { + if (cline === undefined) return + cline.todoList = Array.isArray(todos) ? todos : [] +} + +/** + * Restore the todoList from argument or from clineMessages. + */ +export function restoreTodoListForTask(cline: Task, todoList?: TodoItem[]) { + if (todoList) { + cline.todoList = Array.isArray(todoList) ? todoList : [] + return + } + cline.todoList = getLatestTodo(cline.clineMessages) +} +/** + * Convert TodoItem[] to markdown checklist string. + * @param todos TodoItem array + * @returns markdown checklist string + */ +function todoListToMarkdown(todos: TodoItem[]): string { + return todos + .map((t) => { + let box = "[ ]" + if (t.status === "completed") box = "[x]" + else if (t.status === "in_progress") box = "[-]" + return `${box} ${t.content}` + }) + .join("\n") +} + +function normalizeStatus(status: string | undefined): TodoStatus { + if (status === "completed" || status === "done") return "completed" + if (status === "in_progress" || status === "doing" || status === "working") return "in_progress" + return "pending" +} + +function parseMarkdownChecklist(md: string): TodoItem[] { + if (typeof md !== "string") return [] + const lines = md + .split(/\r?\n/) + .map((l) => l.trim()) + .filter(Boolean) + const todos: TodoItem[] = [] + for (const line of lines) { + const match = line.match(/^\[\s*([ xX\-~])\s*\]\s+(.+)$/) + if (!match) continue + let status: TodoStatus = "pending" + if (match[1] === "x" || match[1] === "X") status = "completed" + else if (match[1] === "-" || match[1] === "~") status = "in_progress" + const id = crypto + .createHash("md5") + .update(match[2] + status) + .digest("hex") + todos.push({ + id, + content: match[2], + status, + }) + } + return todos +} + +export function setPendingTodoList(todos: TodoItem[]) { + approvedTodoList = todos +} + +function validateTodos(todos: any[]): { valid: boolean; error?: string } { + if (!Array.isArray(todos)) return { valid: false, error: "todos must be an array" } + for (const [i, t] of todos.entries()) { + if (!t || typeof t !== "object") return { valid: false, error: `Item ${i + 1} is not an object` } + if (!t.id || typeof t.id !== "string") return { valid: false, error: `Item ${i + 1} is missing id` } + if (!t.content || typeof t.content !== "string") + return { valid: false, error: `Item ${i + 1} is missing content` } + if (t.status && !VALID_STATUS.includes(t.status)) + return { valid: false, error: `Item ${i + 1} has invalid status` } + } + return { valid: true } +} + +/** + * Update the todo list for a task. + * @param cline Task instance + * @param block ToolUse block + * @param askApproval AskApproval function + * @param handleError HandleError function + * @param pushToolResult PushToolResult function + * @param removeClosingTag RemoveClosingTag function + * @param userEdited If true, only show "User Edit Succee" and do nothing else + */ +export async function updateTodoListTool( + cline: Task, + block: ToolUse, + askApproval: AskApproval, + handleError: HandleError, + pushToolResult: PushToolResult, + removeClosingTag: RemoveClosingTag, + userEdited?: boolean, +) { + // If userEdited is true, only show "User Edit Succee" and do nothing else + if (userEdited === true) { + pushToolResult("User Edit Succeeded") + return + } + try { + const todosRaw = block.params.todos + + let todos: TodoItem[] + try { + todos = parseMarkdownChecklist(todosRaw || "") + } catch { + cline.consecutiveMistakeCount++ + cline.recordToolError("update_todo_list") + pushToolResult(formatResponse.toolError("The todos parameter is not valid markdown checklist or JSON")) + return + } + + const { valid, error } = validateTodos(todos) + if (!valid && !block.partial) { + cline.consecutiveMistakeCount++ + cline.recordToolError("update_todo_list") + pushToolResult(formatResponse.toolError(error || "todos parameter validation failed")) + return + } + + let normalizedTodos: TodoItem[] = todos.map((t) => ({ + id: t.id, + content: t.content, + status: normalizeStatus(t.status), + })) + + const approvalMsg = JSON.stringify({ + tool: "updateTodoList", + todos: normalizedTodos, + }) + if (block.partial) { + await cline.ask("tool", approvalMsg, block.partial).catch(() => {}) + return + } + approvedTodoList = cloneDeep(normalizedTodos) + const didApprove = await askApproval("tool", approvalMsg) + if (!didApprove) { + pushToolResult("User declined to update the todoList.") + return + } + const isTodoListChanged = + approvedTodoList !== undefined && JSON.stringify(normalizedTodos) !== JSON.stringify(approvedTodoList) + if (isTodoListChanged) { + normalizedTodos = approvedTodoList ?? [] + cline.say( + "user_edit_todos", + JSON.stringify({ + tool: "updateTodoList", + todos: normalizedTodos, + }), + ) + } + + await setTodoListForTask(cline, normalizedTodos) + + // If todo list changed, output new todo list in markdown format + if (isTodoListChanged) { + const md = todoListToMarkdown(normalizedTodos) + pushToolResult(formatResponse.toolResult("User edits todo:\n\n" + md)) + } else { + pushToolResult(formatResponse.toolResult("Todo list updated successfully.")) + } + } catch (error) { + await handleError("update todo list", error) + } +} diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index d8aca8fbcb..8eb2e70a8c 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1347,6 +1347,7 @@ export class ClineProvider alwaysAllowMcp, alwaysAllowModeSwitch, alwaysAllowSubtasks, + alwaysAllowUpdateTodoList, allowedMaxRequests, autoCondenseContext, autoCondenseContextPercent, @@ -1433,6 +1434,7 @@ export class ClineProvider alwaysAllowMcp: alwaysAllowMcp ?? false, alwaysAllowModeSwitch: alwaysAllowModeSwitch ?? false, alwaysAllowSubtasks: alwaysAllowSubtasks ?? false, + alwaysAllowUpdateTodoList: alwaysAllowUpdateTodoList ?? false, allowedMaxRequests, autoCondenseContext: autoCondenseContext ?? true, autoCondenseContextPercent: autoCondenseContextPercent ?? 100, @@ -1601,6 +1603,7 @@ export class ClineProvider alwaysAllowModeSwitch: stateValues.alwaysAllowModeSwitch ?? false, alwaysAllowSubtasks: stateValues.alwaysAllowSubtasks ?? false, alwaysAllowFollowupQuestions: stateValues.alwaysAllowFollowupQuestions ?? false, + alwaysAllowUpdateTodoList: stateValues.alwaysAllowUpdateTodoList ?? false, followupAutoApproveTimeoutMs: stateValues.followupAutoApproveTimeoutMs ?? 60000, allowedMaxRequests: stateValues.allowedMaxRequests, autoCondenseContext: stateValues.autoCondenseContext ?? true, diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 0ec14ca27e..b8e21e6040 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -46,6 +46,7 @@ import { getCommand } from "../../utils/commands" const ALLOWED_VSCODE_SETTINGS = new Set(["terminal.integrated.inheritEnv"]) import { MarketplaceManager, MarketplaceItemType } from "../../services/marketplace" +import { setPendingTodoList } from "../tools/updateTodoListTool" export const webviewMessageHandler = async ( provider: ClineProvider, @@ -185,6 +186,10 @@ export const webviewMessageHandler = async ( await updateGlobalState("alwaysAllowSubtasks", message.bool) await provider.postStateToWebview() break + case "alwaysAllowUpdateTodoList": + await updateGlobalState("alwaysAllowUpdateTodoList", message.bool) + await provider.postStateToWebview() + break case "askResponse": provider.getCurrentCline()?.handleWebviewAskResponse(message.askResponse!, message.text, message.images) break @@ -1317,6 +1322,14 @@ export const webviewMessageHandler = async ( } break } + case "updateTodoList": { + const payload = message.payload as { todos?: any[] } + const todos = payload?.todos + if (Array.isArray(todos)) { + await setPendingTodoList(todos) + } + break + } case "saveApiConfiguration": if (message.text && message.apiConfiguration) { try { diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 9db0889c88..953c0c1070 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -181,6 +181,7 @@ export type ExtensionState = Pick< | "alwaysAllowModeSwitch" | "alwaysAllowSubtasks" | "alwaysAllowExecute" + | "alwaysAllowUpdateTodoList" | "allowedCommands" | "allowedMaxRequests" | "browserToolEnabled" diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index a50e30b67e..89fa21b7b7 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -18,8 +18,13 @@ export type PromptMode = Mode | "enhance" export type AudioType = "notification" | "celebration" | "progress_loop" +export interface UpdateTodoListPayload { + todos: any[] +} + export interface WebviewMessage { type: + | "updateTodoList" | "deleteMultipleTasksWithIds" | "currentApiConfigName" | "saveApiConfiguration" @@ -38,6 +43,7 @@ export interface WebviewMessage { | "alwaysAllowWriteProtected" | "alwaysAllowExecute" | "alwaysAllowFollowupQuestions" + | "alwaysAllowUpdateTodoList" | "followupAutoApproveTimeoutMs" | "webviewDidLaunch" | "newTask" @@ -73,6 +79,7 @@ export interface WebviewMessage { | "alwaysAllowModeSwitch" | "allowedMaxRequests" | "alwaysAllowSubtasks" + | "alwaysAllowUpdateTodoList" | "autoCondenseContext" | "autoCondenseContextPercent" | "condensingApiConfigId" @@ -287,3 +294,4 @@ export type WebViewMessagePayload = | IndexingStatusPayload | IndexClearedPayload | InstallMarketplaceItemWithParametersPayload + | UpdateTodoListPayload diff --git a/src/shared/todo.ts b/src/shared/todo.ts new file mode 100644 index 0000000000..16e7d085e2 --- /dev/null +++ b/src/shared/todo.ts @@ -0,0 +1,23 @@ +import { ClineMessage } from "@roo-code/types" +export function getLatestTodo(clineMessages: ClineMessage[]) { + const todos = clineMessages + .filter( + (msg) => + (msg.type === "ask" && msg.ask === "tool") || (msg.type === "say" && msg.say === "user_edit_todos"), + ) + .map((msg) => { + try { + return JSON.parse(msg.text ?? "{}") + } catch { + return null + } + }) + .filter((item) => item && item.tool === "updateTodoList" && Array.isArray(item.todos)) + .map((item) => item.todos) + .pop() + if (todos) { + return todos + } else { + return [] + } +} diff --git a/src/shared/tools.ts b/src/shared/tools.ts index 0725e2e4d6..67972243fe 100644 --- a/src/shared/tools.ts +++ b/src/shared/tools.ts @@ -64,6 +64,7 @@ export const toolParamNames = [ "end_line", "query", "args", + "todos", ] as const export type ToolParamName = (typeof toolParamNames)[number] @@ -188,6 +189,7 @@ export const TOOL_DISPLAY_NAMES: Record = { insert_content: "insert content", search_and_replace: "search and replace", codebase_search: "codebase search", + update_todo_list: "update todo list", } as const // Define available tool groups. @@ -226,6 +228,7 @@ export const ALWAYS_AVAILABLE_TOOLS: ToolName[] = [ "attempt_completion", "switch_mode", "new_task", + "update_todo_list", ] as const export type DiffResult = diff --git a/webview-ui/src/components/chat/AutoApproveMenu.tsx b/webview-ui/src/components/chat/AutoApproveMenu.tsx index c25feef0d1..ae363a7b63 100644 --- a/webview-ui/src/components/chat/AutoApproveMenu.tsx +++ b/webview-ui/src/components/chat/AutoApproveMenu.tsx @@ -26,6 +26,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { alwaysAllowSubtasks, alwaysApproveResubmit, alwaysAllowFollowupQuestions, + alwaysAllowUpdateTodoList, allowedMaxRequests, setAlwaysAllowReadOnly, setAlwaysAllowWrite, @@ -36,6 +37,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { setAlwaysAllowSubtasks, setAlwaysApproveResubmit, setAlwaysAllowFollowupQuestions, + setAlwaysAllowUpdateTodoList, setAllowedMaxRequests, } = useExtensionState() @@ -73,6 +75,9 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { case "alwaysAllowFollowupQuestions": setAlwaysAllowFollowupQuestions(value) break + case "alwaysAllowUpdateTodoList": + setAlwaysAllowUpdateTodoList(value) + break } }, [ @@ -85,6 +90,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { setAlwaysAllowSubtasks, setAlwaysApproveResubmit, setAlwaysAllowFollowupQuestions, + setAlwaysAllowUpdateTodoList, ], ) @@ -101,6 +107,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { alwaysAllowSubtasks: alwaysAllowSubtasks, alwaysApproveResubmit: alwaysApproveResubmit, alwaysAllowFollowupQuestions: alwaysAllowFollowupQuestions, + alwaysAllowUpdateTodoList: alwaysAllowUpdateTodoList, }), [ alwaysAllowReadOnly, @@ -112,6 +119,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { alwaysAllowSubtasks, alwaysApproveResubmit, alwaysAllowFollowupQuestions, + alwaysAllowUpdateTodoList, ], ) diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index f61eb8c5e0..9c90da503f 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -21,6 +21,7 @@ import { getLanguageFromPath } from "@src/utils/getLanguageFromPath" import { Button } from "@src/components/ui" import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock" +import UpdateTodoListToolBlock from "./UpdateTodoListToolBlock" import CodeAccordian from "../common/CodeAccordian" import CodeBlock from "../common/CodeBlock" import MarkdownBlock from "../common/MarkdownBlock" @@ -52,6 +53,7 @@ interface ChatRowProps { onSuggestionClick?: (suggestion: SuggestionItem, event?: React.MouseEvent) => void onBatchFileResponse?: (response: { [key: string]: boolean }) => void onFollowUpUnmount?: () => void + editable?: boolean } // eslint-disable-next-line @typescript-eslint/no-empty-object-type @@ -102,6 +104,7 @@ export const ChatRowContent = ({ onSuggestionClick, onFollowUpUnmount, onBatchFileResponse, + editable, }: ChatRowContentProps) => { const { t } = useTranslation() const { mcpServers, alwaysAllowMcp, currentCheckpoint } = useExtensionState() @@ -431,6 +434,21 @@ export const ChatRowContent = ({ ) } + case "updateTodoList" as any: { + const todos = (tool as any).todos || [] + return ( + { + if (typeof vscode !== "undefined" && vscode?.postMessage) { + vscode.postMessage({ type: "updateTodoList", payload: { todos: updatedTodos } }) + } + }} + editable={editable && isLast} + /> + ) + } case "newFileCreated": return ( <> @@ -1089,6 +1107,8 @@ export const ChatRowContent = ({ const { query = "", results = [] } = parsed?.content || {} return + case "user_edit_todos": + return {}} /> default: return ( <> diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 39e593f2aa..84bb7b89a2 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -46,6 +46,7 @@ import AutoApproveMenu from "./AutoApproveMenu" import SystemPromptWarning from "./SystemPromptWarning" import ProfileViolationWarning from "./ProfileViolationWarning" import { CheckpointWarning } from "./CheckpointWarning" +import { getLatestTodo } from "@roo/todo" export interface ChatViewProps { isHidden: boolean @@ -96,6 +97,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction messages.at(0), [messages]) + const latestTodos = useMemo(() => { + return getLatestTodo(messages) + }, [messages]) + const modifiedMessages = useMemo(() => combineApiRequests(combineCommandSequences(messages.slice(1))), [messages]) // Has to be after api_req_finished are all reduced into api_req_started messages. @@ -907,6 +913,10 @@ const ChatViewComponent: React.ForwardRefRenderFunction { + let tool: any = {} + try { + tool = JSON.parse(messageOrGroup.text || "{}") + } catch (_) { + if (messageOrGroup.text?.includes("updateTodoList")) { + tool = { tool: "updateTodoList" } + } + } + if (tool.tool === "updateTodoList" && alwaysAllowUpdateTodoList) { + return false + } + return tool.tool === "updateTodoList" && enableButtons && !!primaryButtonText + })() + } /> ) }, @@ -1293,6 +1322,9 @@ const ChatViewComponent: React.ForwardRefRenderFunction {hasSystemPromptOverride && ( diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index 0d75f8f250..2087071fdb 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -20,6 +20,7 @@ import { TaskActions } from "./TaskActions" import { ShareButton } from "./ShareButton" import { ContextWindowProgress } from "./ContextWindowProgress" import { Mention } from "./Mention" +import { TodoListDisplay } from "./TodoListDisplay" export interface TaskHeaderProps { task: ClineMessage @@ -32,6 +33,7 @@ export interface TaskHeaderProps { buttonsDisabled: boolean handleCondenseContext: (taskId: string) => void onClose: () => void + todos?: any[] } const TaskHeader = ({ @@ -45,6 +47,7 @@ const TaskHeader = ({ buttonsDisabled, handleCondenseContext, onClose, + todos, }: TaskHeaderProps) => { const { t } = useTranslation() const { apiConfiguration, currentTaskItem } = useExtensionState() @@ -214,6 +217,7 @@ const TaskHeader = ({ )} + ) } diff --git a/webview-ui/src/components/chat/TodoListDisplay.tsx b/webview-ui/src/components/chat/TodoListDisplay.tsx new file mode 100644 index 0000000000..df6a046431 --- /dev/null +++ b/webview-ui/src/components/chat/TodoListDisplay.tsx @@ -0,0 +1,210 @@ +import { useState, useRef, useMemo, useEffect } from "react" + +export function TodoListDisplay({ todos }: { todos: any[] }) { + const [isCollapsed, setIsCollapsed] = useState(false) + const ulRef = useRef(null) + const itemRefs = useRef<(HTMLLIElement | null)[]>([]) + const scrollIndex = useMemo(() => { + const inProgressIdx = todos.findIndex((todo: any) => todo.status === "in_progress") + if (inProgressIdx !== -1) return inProgressIdx + return todos.findIndex((todo: any) => todo.status !== "completed") + }, [todos]) + useEffect(() => { + if (isCollapsed) return + if (!ulRef.current) return + if (scrollIndex === -1) return + const target = itemRefs.current[scrollIndex] + if (target && ulRef.current) { + const ul = ulRef.current + const targetTop = target.offsetTop - ul.offsetTop + const targetHeight = target.offsetHeight + const ulHeight = ul.clientHeight + const scrollTo = targetTop - (ulHeight / 2 - targetHeight / 2) + ul.scrollTop = scrollTo + } + }, [todos, isCollapsed, scrollIndex]) + if (!Array.isArray(todos) || todos.length === 0) return null + + const totalCount = todos.length + const completedCount = todos.filter((todo: any) => todo.status === "completed").length + + return ( +
+
setIsCollapsed((v) => !v)}> + + Todo List + + {completedCount}/{totalCount} + + +
+ {!isCollapsed && ( +
    2 ? "auto" : "visible", + transition: "max-height 0.2s", + }}> + {todos.map((todo: any, idx: number) => { + let icon + if (todo.status === "completed") { + icon = ( + + + + + + ) + } else if (todo.status === "in_progress") { + icon = ( + + + + + + + ) + } else { + icon = ( + + ) + } + return ( +
  • (itemRefs.current[idx] = el)} + style={{ + marginBottom: 2, + display: "flex", + alignItems: "center", + minHeight: 20, + }}> + {icon} + + {todo.content} + +
  • + ) + })} +
+ )} +
+ ) +} diff --git a/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx new file mode 100644 index 0000000000..2208263a8a --- /dev/null +++ b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx @@ -0,0 +1,550 @@ +import React, { useState, useEffect, useRef } from "react" +import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock" +import MarkdownBlock from "../common/MarkdownBlock" +import { useTranslation } from "react-i18next" + +interface TodoItem { + id?: string + content: string + status?: "completed" | "in_progress" | string +} + +/** + * @description + * Editable Todo List component. Each time the todo list changes (edit, add, delete, status switch), the parent component will be notified via the onChange callback. + * The parent component should synchronize the latest todos to the model in onChange. + */ +interface UpdateTodoListToolBlockProps { + todos?: TodoItem[] + content?: string + /** + * Callback when todos change, be sure to implement and notify the model with the latest todos + * @param todos Latest todo list + */ + onChange: (todos: TodoItem[]) => void + /** Whether editing is allowed (controlled externally) */ + editable?: boolean + userEdited?: boolean +} + +const STATUS_OPTIONS = [ + { value: "", label: "Not Started", color: "var(--vscode-foreground)", border: "#bbb", bg: "transparent" }, + { + value: "in_progress", + label: "In Progress", + color: "var(--vscode-charts-yellow)", + border: "var(--vscode-charts-yellow)", + bg: "rgba(255, 221, 51, 0.15)", + }, + { + value: "completed", + label: "Completed", + color: "var(--vscode-charts-green)", + border: "var(--vscode-charts-green)", + bg: "var(--vscode-charts-green)", + }, +] + +const genId = () => Math.random().toString(36).slice(2, 10) + +const UpdateTodoListToolBlock: React.FC = ({ + todos = [], + content, + onChange, + editable = true, + userEdited = false, +}) => { + const { t } = useTranslation() + const [editTodos, setEditTodos] = useState( + todos.length > 0 ? todos.map((todo) => ({ ...todo, id: todo.id || genId() })) : [], + ) + const [adding, setAdding] = useState(false) + const [newContent, setNewContent] = useState("") + const newInputRef = useRef(null) + const [deleteId, setDeleteId] = useState(null) + const [isEditing, setIsEditing] = useState(false) + + // Automatically exit edit mode when external editable becomes false + useEffect(() => { + if (!editable && isEditing) { + setIsEditing(false) + } + }, [editable, isEditing]) + + // Check if onChange is passed + useEffect(() => { + if (typeof onChange !== "function") { + console.warn( + "UpdateTodoListToolBlock: onChange callback not passed, cannot notify model after todo changes!", + ) + } + // Only check once on mount + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + // Sync when external props.todos changes + useEffect(() => { + setEditTodos(todos.length > 0 ? todos.map((todo) => ({ ...todo, id: todo.id || genId() })) : []) + }, [todos]) + + // Auto focus on new item + useEffect(() => { + if (adding && newInputRef.current) { + newInputRef.current.focus() + } + }, [adding]) + + // Edit content + const handleContentChange = (id: string, value: string) => { + const newTodos = editTodos.map((todo) => (todo.id === id ? { ...todo, content: value } : todo)) + setEditTodos(newTodos) + onChange?.(newTodos) + } + + // Change status + const handleStatusChange = (id: string, status: string) => { + const newTodos = editTodos.map((todo) => (todo.id === id ? { ...todo, status } : todo)) + setEditTodos(newTodos) + onChange?.(newTodos) + } + + // Delete (confirmation dialog) + const handleDelete = (id: string) => { + setDeleteId(id) + } + const confirmDelete = () => { + if (!deleteId) return + const newTodos = editTodos.filter((todo) => todo.id !== deleteId) + setEditTodos(newTodos) + onChange?.(newTodos) + setDeleteId(null) + } + const cancelDelete = () => setDeleteId(null) + + // Add + const handleAdd = () => { + if (!newContent.trim()) return + const newTodo: TodoItem = { + id: genId(), + content: newContent.trim(), + status: "", + } + const newTodos = [...editTodos, newTodo] + setEditTodos(newTodos) + onChange?.(newTodos) + setNewContent("") + setAdding(false) + } + + // Add on Enter + const handleNewInputKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + handleAdd() + } else if (e.key === "Escape") { + setAdding(false) + setNewContent("") + } + } + + if (userEdited) { + return ( + + +
+ + + User Edit + +
+
+ +
+ User Edits +
+ + ) + } + + return ( + <> + + +
+ + + Todo List Updated + +
+ {editable && ( + + )} +
+ +
+ {Array.isArray(editTodos) && editTodos.length > 0 ? ( +
    + {editTodos.map((todo, idx) => { + let icon + if (todo.status === "completed") { + icon = ( + + + + + + ) + } else if (todo.status === "in_progress") { + icon = ( + + + + + + + ) + } else { + icon = ( + + ) + } + return ( +
  • + {icon} + {isEditing ? ( + handleContentChange(todo.id!, e.target.value)} + style={{ + flex: 1, + minWidth: 0, + fontWeight: 500, + color: "var(--vscode-input-foreground)", + background: "var(--vscode-input-background)", + border: "none", + outline: "none", + fontSize: 13, + marginRight: 6, + padding: "1px 3px", + borderBottom: "1px solid var(--vscode-input-border)", + }} + onBlur={(e) => { + if (!e.target.value.trim()) { + handleDelete(todo.id!) + } + }} + /> + ) : ( + + {todo.content} + + )} + {isEditing && ( + + )} + {isEditing && ( + + )} +
  • + ) + })} + {adding ? ( +
  • + + setNewContent(e.target.value)} + onKeyDown={handleNewInputKeyDown} + style={{ + flex: 1, + minWidth: 0, + fontWeight: 500, + color: "var(--vscode-foreground)", + background: "transparent", + border: "none", + outline: "none", + fontSize: 13, + marginRight: 6, + padding: "1px 3px", + borderBottom: "1px solid #eee", + }} + /> + + +
  • + ) : ( +
  • + {isEditing && ( + + )} +
  • + )} +
+ ) : ( + + )} +
+ {/* Delete confirmation dialog */} + {deleteId && ( +
+
e.stopPropagation()}> +
+ Are you sure you want to delete this todo item? +
+
+ + +
+
+
+ )} + + + ) +} + +export default UpdateTodoListToolBlock diff --git a/webview-ui/src/components/settings/AutoApproveSettings.tsx b/webview-ui/src/components/settings/AutoApproveSettings.tsx index 1f9bb282a8..71283a833b 100644 --- a/webview-ui/src/components/settings/AutoApproveSettings.tsx +++ b/webview-ui/src/components/settings/AutoApproveSettings.tsx @@ -45,6 +45,7 @@ type AutoApproveSettingsProps = HTMLAttributes & { | "alwaysAllowFollowupQuestions" | "followupAutoApproveTimeoutMs" | "allowedCommands" + | "alwaysAllowUpdateTodoList" > } diff --git a/webview-ui/src/components/settings/AutoApproveToggle.tsx b/webview-ui/src/components/settings/AutoApproveToggle.tsx index 6c82d3c984..e8b51b01ef 100644 --- a/webview-ui/src/components/settings/AutoApproveToggle.tsx +++ b/webview-ui/src/components/settings/AutoApproveToggle.tsx @@ -15,6 +15,7 @@ type AutoApproveToggles = Pick< | "alwaysAllowSubtasks" | "alwaysAllowExecute" | "alwaysAllowFollowupQuestions" + | "alwaysAllowUpdateTodoList" > export type AutoApproveSetting = keyof AutoApproveToggles @@ -91,6 +92,13 @@ export const autoApproveSettingsConfig: Record { alwaysAllowSubtasks: false, alwaysAllowExecute: true, alwaysAllowFollowupQuestions: false, + alwaysAllowUpdateTodoList: true, onToggle: mockOnToggle, } diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 3e33c2d6ec..df7cee5627 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -129,6 +129,8 @@ export interface ExtensionStateContextType extends ExtensionState { autoCondenseContextPercent: number setAutoCondenseContextPercent: (value: number) => void routerModels?: RouterModels + alwaysAllowUpdateTodoList?: boolean + setAlwaysAllowUpdateTodoList: (value: boolean) => void } export const ExtensionStateContext = createContext(undefined) @@ -221,6 +223,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode codebaseIndexSearchMinScore: undefined, }, codebaseIndexModels: { ollama: {}, openai: {} }, + alwaysAllowUpdateTodoList: true, }) const [didHydrateState, setDidHydrateState] = useState(false) @@ -459,6 +462,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setCustomCondensingPrompt: (value) => setState((prevState) => ({ ...prevState, customCondensingPrompt: value })), setProfileThresholds: (value) => setState((prevState) => ({ ...prevState, profileThresholds: value })), + alwaysAllowUpdateTodoList: state.alwaysAllowUpdateTodoList, + setAlwaysAllowUpdateTodoList: (value) => { + setState((prevState) => ({ ...prevState, alwaysAllowUpdateTodoList: value })) + }, } return {children} diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 634ac28d32..2e2197f123 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Introduïu prefix de comanda (ex. 'git ')", "addButton": "Afegir" }, + "updateTodoList": { + "label": "Todo", + "description": "La llista de tasques es actualitza automàticament sense aprovació" + }, "apiRequestLimit": { "title": "Màximes Sol·licituds", "description": "Fes aquesta quantitat de sol·licituds API automàticament abans de demanar aprovació per continuar amb la tasca.", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 7c328cacec..14af077b23 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Befehlspräfix eingeben (z.B. 'git ')", "addButton": "Hinzufügen" }, + "updateTodoList": { + "label": "Todo", + "description": "To-Do-Liste wird automatisch aktualisiert, ohne dass du zustimmen musst" + }, "apiRequestLimit": { "title": "Maximale Anfragen", "description": "Automatisch so viele API-Anfragen stellen, bevor du um die Erlaubnis gebeten wirst, mit der Aufgabe fortzufahren.", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index a849df5b7d..ea7191f224 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Enter command prefix (e.g., 'git ')", "addButton": "Add" }, + "updateTodoList": { + "label": "Todo", + "description": "Automatically update the to-do list without requiring approval" + }, "apiRequestLimit": { "title": "Max Requests", "description": "Automatically make this many API requests before asking for approval to continue with the task.", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 180d91060a..d6cebea206 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Ingrese prefijo de comando (ej. 'git ')", "addButton": "Añadir" }, + "updateTodoList": { + "label": "Todo", + "description": "La lista de tareas se actualiza automáticamente sin aprobación" + }, "apiRequestLimit": { "title": "Solicitudes máximas", "description": "Realizar automáticamente esta cantidad de solicitudes a la API antes de pedir aprobación para continuar con la tarea.", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 4546e595ad..281fb705fc 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Entrez le préfixe de commande (ex. 'git ')", "addButton": "Ajouter" }, + "updateTodoList": { + "label": "Todo", + "description": "La liste de tâches est mise à jour automatiquement sans approbation" + }, "apiRequestLimit": { "title": "Requêtes maximales", "description": "Effectuer automatiquement ce nombre de requêtes API avant de demander l'approbation pour continuer la tâche.", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 249973fa98..4b8132768f 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "कमांड प्रीफिक्स दर्ज करें (उदा. 'git ')", "addButton": "जोड़ें" }, + "updateTodoList": { + "label": "टूडू", + "description": "अनुमोदन की आवश्यकता के बिना स्वचालित रूप से टूडू सूची अपडेट करें" + }, "apiRequestLimit": { "title": "अधिकतम अनुरोध", "description": "कार्य जारी रखने के लिए अनुमति मांगने से पहले स्वचालित रूप से इतने API अनुरोध करें।", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 52a339330e..ac1454ab7b 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -166,6 +166,10 @@ "label": "Tampilkan menu auto-approve di tampilan chat", "description": "Ketika diaktifkan, menu auto-approve akan ditampilkan di bagian bawah tampilan chat, memungkinkan akses cepat ke pengaturan auto-approve" }, + "updateTodoList": { + "label": "Todo", + "description": "Daftar tugas diperbarui secara otomatis tanpa persetujuan" + }, "apiRequestLimit": { "title": "Permintaan Maks", "description": "Secara otomatis membuat sejumlah permintaan API ini sebelum meminta persetujuan untuk melanjutkan tugas.", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 2206822dfc..7627486863 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Inserisci prefisso comando (es. 'git ')", "addButton": "Aggiungi" }, + "updateTodoList": { + "label": "Todo", + "description": "La lista delle cose da fare viene aggiornata automaticamente senza approvazione" + }, "apiRequestLimit": { "title": "Richieste massime", "description": "Esegui automaticamente questo numero di richieste API prima di chiedere l'approvazione per continuare con l'attività.", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 94b93b043a..f779749cd4 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "コマンドプレフィックスを入力(例:'git ')", "addButton": "追加" }, + "updateTodoList": { + "label": "Todo", + "description": "承認なしで自動的にToDoリストを更新" + }, "apiRequestLimit": { "title": "最大リクエスト数", "description": "タスクを続行するための承認を求める前に、自動的にこの数のAPIリクエストを行います。", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index d6436cc6e2..2ea16f7834 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "명령 접두사 입력(예: 'git ')", "addButton": "추가" }, + "updateTodoList": { + "label": "Todo", + "description": "승인 없이 자동으로 할 일 목록이 업데이트됩니다" + }, "apiRequestLimit": { "title": "최대 요청 수", "description": "작업을 계속하기 위한 승인을 요청하기 전에 자동으로 이 수의 API 요청을 수행합니다.", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index ea8c75d974..92b00cf3ff 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Voer commando-prefix in (bijv. 'git ')", "addButton": "Toevoegen" }, + "updateTodoList": { + "label": "Todo", + "description": "De takenlijst wordt automatisch bijgewerkt zonder goedkeuring" + }, "apiRequestLimit": { "title": "Maximale verzoeken", "description": "Voer automatisch dit aantal API-verzoeken uit voordat om goedkeuring wordt gevraagd om door te gaan met de taak.", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index bd15c4a2c0..34e0ac52ef 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Wprowadź prefiks polecenia (np. 'git ')", "addButton": "Dodaj" }, + "updateTodoList": { + "label": "Todo", + "description": "Lista zadań jest automatycznie aktualizowana bez zatwierdzenia" + }, "apiRequestLimit": { "title": "Maksymalna liczba żądań", "description": "Automatycznie wykonaj tyle żądań API przed poproszeniem o zgodę na kontynuowanie zadania.", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index b826bcc06b..e696ab0f77 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Digite o prefixo do comando (ex. 'git ')", "addButton": "Adicionar" }, + "updateTodoList": { + "label": "Todo", + "description": "A lista de tarefas é atualizada automaticamente sem aprovação" + }, "apiRequestLimit": { "title": "Máximo de Solicitações", "description": "Fazer automaticamente este número de requisições à API antes de pedir aprovação para continuar com a tarefa.", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 9dfaf0bccb..3e6a7a9d76 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Введите префикс команды (например, 'git ')", "addButton": "Добавить" }, + "updateTodoList": { + "label": "Todo", + "description": "Список дел обновляется автоматически без подтверждения" + }, "apiRequestLimit": { "title": "Максимум запросов", "description": "Автоматически выполнять это количество API-запросов перед запросом разрешения на продолжение задачи.", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 7ceaf46a32..c0a3b8f501 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Komut öneki girin (örn. 'git ')", "addButton": "Ekle" }, + "updateTodoList": { + "label": "Todo", + "description": "Yapılacaklar listesi onay gerektirmeden otomatik olarak güncellenir" + }, "apiRequestLimit": { "title": "Maksimum İstek", "description": "Göreve devam etmek için onay istemeden önce bu sayıda API isteği otomatik olarak yap.", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 134c4f476e..e553f5d0c2 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "Nhập tiền tố lệnh (ví dụ: 'git ')", "addButton": "Thêm" }, + "updateTodoList": { + "label": "Todo", + "description": "Danh sách việc cần làm được cập nhật tự động mà không cần phê duyệt" + }, "apiRequestLimit": { "title": "Số lượng yêu cầu tối đa", "description": "Tự động thực hiện số lượng API request này trước khi yêu cầu phê duyệt để tiếp tục với nhiệm vụ.", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 9007569757..d8d875c85f 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "输入命令前缀(例如 'git ')", "addButton": "添加" }, + "updateTodoList": { + "label": "待办", + "description": "无需批准即可自动更新待办清单" + }, "apiRequestLimit": { "title": "最大请求数", "description": "在请求批准以继续执行任务之前,自动发出此数量的 API 请求。", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 332a234f86..f08cb0dbba 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -162,6 +162,10 @@ "commandPlaceholder": "輸入命令前綴(例如 'git ')", "addButton": "新增" }, + "updateTodoList": { + "label": "待辦", + "description": "自動更新待辦清單無需批准" + }, "apiRequestLimit": { "title": "最大請求數", "description": "在請求批准以繼續執行工作之前,自動發出此數量的 API 請求。", From f18d007826d72dc6ef3ca5a5981a1f8990b25861 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 7 Jul 2025 10:37:09 -0400 Subject: [PATCH 02/12] UI tweaks --- webview-ui/src/components/chat/TaskHeader.tsx | 5 +- .../src/components/chat/TodoListDisplay.tsx | 520 +++++++++++++----- 2 files changed, 376 insertions(+), 149 deletions(-) diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index 2087071fdb..1896df486b 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -71,11 +71,14 @@ const TaskHeader = ({ ) + const hasTodos = todos && Array.isArray(todos) && todos.length > 0 + return (
(null) const itemRefs = useRef<(HTMLLIElement | null)[]>([]) const scrollIndex = useMemo(() => { @@ -9,6 +9,13 @@ export function TodoListDisplay({ todos }: { todos: any[] }) { if (inProgressIdx !== -1) return inProgressIdx return todos.findIndex((todo: any) => todo.status !== "completed") }, [todos]) + + // Find the most important todo to display when collapsed + const mostImportantTodo = useMemo(() => { + const inProgress = todos.find((todo: any) => todo.status === "in_progress") + if (inProgress) return inProgress + return todos.find((todo: any) => todo.status !== "completed") + }, [todos]) useEffect(() => { if (isCollapsed) return if (!ulRef.current) return @@ -28,182 +35,399 @@ export function TodoListDisplay({ todos }: { todos: any[] }) { const totalCount = todos.length const completedCount = todos.filter((todo: any) => todo.status === "completed").length + const allCompleted = completedCount === totalCount && totalCount > 0 + + // Create the status icon for the most important todo + const getMostImportantTodoIcon = () => { + if (allCompleted) { + return ( + + + + + + ) + } + + if (!mostImportantTodo) { + return ( + + ) + } + + if (mostImportantTodo.status === "in_progress") { + return ( + + + + + + + ) + } + + // Default not-started todo + return ( + + ) + } + return (
setIsCollapsed((v) => !v)}> - - Todo List + {getMostImportantTodoIcon()} - {completedCount}/{totalCount} + {allCompleted ? "All tasks completed!" : mostImportantTodo?.content || "No pending tasks"} - +
+ + + {completedCount}/{totalCount} + +
+ {/* Floating panel for expanded state */} {!isCollapsed && ( -
    2 ? "auto" : "visible", - transition: "max-height 0.2s", - }}> - {todos.map((todo: any, idx: number) => { - let icon - if (todo.status === "completed") { - icon = ( + <> + {/* Backdrop */} +
    setIsCollapsed(true)} + /> + {/* Floating panel */} +
    + {/* Panel header */} +
    +
    + + Todo List - - +
    + { + e.stopPropagation() + setIsCollapsed(true) + }} + onMouseEnter={(e) => { + e.currentTarget.style.opacity = "1" + e.currentTarget.style.background = "var(--vscode-toolbar-hoverBackground)" + }} + onMouseLeave={(e) => { + e.currentTarget.style.opacity = "0.7" + e.currentTarget.style.background = "transparent" + }} + /> +
    + {/* Todo list */} +
      + {todos.map((todo: any, idx: number) => { + let icon + if (todo.status === "completed") { + icon = ( + + + + + + ) + } else if (todo.status === "in_progress") { + icon = ( + + + + + + + ) + } else { + icon = ( + - - - ) - } else if (todo.status === "in_progress") { - icon = ( - - (itemRefs.current[idx] = el)} style={{ - position: "absolute", - top: 1, - left: 1, + marginBottom: 8, + display: "flex", + alignItems: "flex-start", + minHeight: 20, + lineHeight: "1.4", }}> - - - - - ) - } else { - icon = ( - - ) - } - return ( -
    • (itemRefs.current[idx] = el)} - style={{ - marginBottom: 2, - display: "flex", - alignItems: "center", - minHeight: 20, - }}> - {icon} - - {todo.content} - -
    • - ) - })} -
    + fontWeight: 500, + color: + todo.status === "completed" + ? "var(--vscode-charts-green)" + : todo.status === "in_progress" + ? "var(--vscode-charts-yellow)" + : "var(--vscode-foreground)", + wordBreak: "break-word", + }}> + {todo.content} + + + ) + })} +
+
+ )}
) From 0f5340b8af5ece0116d1436c4fcd789e76a60b3f Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 7 Jul 2025 11:00:36 -0400 Subject: [PATCH 03/12] More visual tweaks --- .../src/components/chat/TodoListDisplay.tsx | 180 +++++------------- .../chat/UpdateTodoListToolBlock.tsx | 88 ++------- 2 files changed, 69 insertions(+), 199 deletions(-) diff --git a/webview-ui/src/components/chat/TodoListDisplay.tsx b/webview-ui/src/components/chat/TodoListDisplay.tsx index acbae0a137..c9a4d8f4d0 100644 --- a/webview-ui/src/components/chat/TodoListDisplay.tsx +++ b/webview-ui/src/components/chat/TodoListDisplay.tsx @@ -44,37 +44,15 @@ export function TodoListDisplay({ todos }: { todos: any[] }) { - - - - + }} + /> ) } @@ -85,6 +63,25 @@ export function TodoListDisplay({ todos }: { todos: any[] }) { style={{ color: "var(--vscode-foreground)", marginRight: 8, + marginLeft: 2, + flexShrink: 0, + fontSize: 14, + }} + /> + ) + } + + if (mostImportantTodo.status === "completed") { + return ( + @@ -96,39 +93,15 @@ export function TodoListDisplay({ todos }: { todos: any[] }) { - - - - - + }} + /> ) } @@ -137,13 +110,13 @@ export function TodoListDisplay({ todos }: { todos: any[] }) { @@ -163,7 +136,7 @@ export function TodoListDisplay({ todos }: { todos: any[] }) { style={{ display: "flex", alignItems: "center", - gap: 6, + gap: 2, marginBottom: 0, cursor: "pointer", userSelect: "none", @@ -302,96 +275,43 @@ export function TodoListDisplay({ todos }: { todos: any[] }) { - - - - + }} + /> ) } else if (todo.status === "in_progress") { icon = ( - - - - - + }} + /> ) } else { icon = ( diff --git a/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx index 2208263a8a..2ca0607302 100644 --- a/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx +++ b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx @@ -215,94 +215,44 @@ const UpdateTodoListToolBlock: React.FC = ({ - - - - + marginTop: 7, + flexShrink: 0, + }} + /> ) } else if (todo.status === "in_progress") { icon = ( - - - - - + marginTop: 7, + flexShrink: 0, + }} + /> ) } else { icon = ( ) @@ -313,7 +263,7 @@ const UpdateTodoListToolBlock: React.FC = ({ style={{ marginBottom: 2, display: "flex", - alignItems: "center", + alignItems: "flex-start", minHeight: 20, }}> {icon} From 547387f2cb136f7f88d81815586ae3943d5e5023 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 7 Jul 2025 11:41:42 -0400 Subject: [PATCH 04/12] Show diffs in todo list updates --- webview-ui/src/components/chat/ChatRow.tsx | 24 +- .../chat/UpdateTodoListToolBlock.tsx | 658 ++++++++++++------ .../UpdateTodoListToolBlock.spec.tsx | 188 +++++ webview-ui/src/i18n/locales/ca/chat.json | 12 +- webview-ui/src/i18n/locales/de/chat.json | 12 +- webview-ui/src/i18n/locales/en/chat.json | 12 +- webview-ui/src/i18n/locales/es/chat.json | 12 +- webview-ui/src/i18n/locales/fr/chat.json | 12 +- webview-ui/src/i18n/locales/hi/chat.json | 12 +- webview-ui/src/i18n/locales/id/chat.json | 10 + webview-ui/src/i18n/locales/it/chat.json | 12 +- webview-ui/src/i18n/locales/ja/chat.json | 12 +- webview-ui/src/i18n/locales/ko/chat.json | 12 +- webview-ui/src/i18n/locales/nl/chat.json | 12 +- webview-ui/src/i18n/locales/pl/chat.json | 12 +- webview-ui/src/i18n/locales/pt-BR/chat.json | 12 +- webview-ui/src/i18n/locales/ru/chat.json | 12 +- webview-ui/src/i18n/locales/tr/chat.json | 12 +- webview-ui/src/i18n/locales/vi/chat.json | 12 +- webview-ui/src/i18n/locales/zh-CN/chat.json | 12 +- webview-ui/src/i18n/locales/zh-TW/chat.json | 12 +- 21 files changed, 861 insertions(+), 223 deletions(-) create mode 100644 webview-ui/src/components/chat/__tests__/UpdateTodoListToolBlock.spec.tsx diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 9c90da503f..7201aabb2c 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -107,7 +107,7 @@ export const ChatRowContent = ({ editable, }: ChatRowContentProps) => { const { t } = useTranslation() - const { mcpServers, alwaysAllowMcp, currentCheckpoint } = useExtensionState() + const { mcpServers, alwaysAllowMcp, currentCheckpoint, clineMessages } = useExtensionState() const [reasoningCollapsed, setReasoningCollapsed] = useState(true) const [isDiffErrorExpanded, setIsDiffErrorExpanded] = useState(false) const [showCopySuccess, setShowCopySuccess] = useState(false) @@ -436,9 +436,31 @@ export const ChatRowContent = ({ } case "updateTodoList" as any: { const todos = (tool as any).todos || [] + + // Try to find previous todo list from earlier messages + const currentMessageIndex = clineMessages.findIndex((msg) => msg.ts === message.ts) + let previousTodos: any[] = [] + + // Look backwards through messages to find the most recent updateTodoList + for (let i = currentMessageIndex - 1; i >= 0; i--) { + const prevMessage = clineMessages[i] + if (prevMessage.ask === "tool" && prevMessage.text) { + try { + const prevTool = JSON.parse(prevMessage.text) + if (prevTool.tool === "updateTodoList" && prevTool.todos) { + previousTodos = prevTool.todos + break + } + } catch { + // Ignore parsing errors + } + } + } + return ( { if (typeof vscode !== "undefined" && vscode?.postMessage) { diff --git a/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx index 2ca0607302..6d7662f26d 100644 --- a/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx +++ b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from "react" +import React, { useState, useEffect, useRef, useMemo } from "react" import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock" import MarkdownBlock from "../common/MarkdownBlock" import { useTranslation } from "react-i18next" @@ -16,6 +16,7 @@ interface TodoItem { */ interface UpdateTodoListToolBlockProps { todos?: TodoItem[] + previousTodos?: TodoItem[] content?: string /** * Callback when todos change, be sure to implement and notify the model with the latest todos @@ -27,6 +28,12 @@ interface UpdateTodoListToolBlockProps { userEdited?: boolean } +interface TodoChange { + type: "added" | "completed" | "started" | "removed" | "reordered" | "reverted" + todo: TodoItem + previousStatus?: string +} + const STATUS_OPTIONS = [ { value: "", label: "Not Started", color: "var(--vscode-foreground)", border: "#bbb", bg: "transparent" }, { @@ -47,8 +54,65 @@ const STATUS_OPTIONS = [ const genId = () => Math.random().toString(36).slice(2, 10) +const calculateTodoChanges = (previousTodos: TodoItem[] = [], currentTodos: TodoItem[] = []): TodoChange[] => { + const changes: TodoChange[] = [] + + // Create maps with content as key and include position info + const prevMap = new Map(previousTodos.map((todo, idx) => [todo.content, { todo, index: idx }])) + const currentMap = new Map(currentTodos.map((todo, idx) => [todo.content, { todo, index: idx }])) + + // Find added todos + for (const todo of currentTodos) { + if (!prevMap.has(todo.content)) { + changes.push({ type: "added", todo }) + } + } + + // Find removed todos + for (const todo of previousTodos) { + if (!currentMap.has(todo.content)) { + changes.push({ type: "removed", todo }) + } + } + + // Find status changes and reordering for existing todos + for (const todo of currentTodos) { + const prevEntry = prevMap.get(todo.content) + const currentEntry = currentMap.get(todo.content) + + if (prevEntry && currentEntry) { + const { todo: prevTodo, index: prevIndex } = prevEntry + const { index: currentIndex } = currentEntry + + // Check for status changes first (they take priority) + if (prevTodo.status !== todo.status) { + // Check for backward/regression status changes first + const isRegression = + (prevTodo.status === "completed" && (todo.status === "" || todo.status === "in_progress")) || + (prevTodo.status === "in_progress" && todo.status === "") + + if (isRegression) { + changes.push({ type: "reverted", todo, previousStatus: prevTodo.status }) + } else if (todo.status === "completed" && prevTodo.status !== "completed") { + // Forward progress: completing a task + changes.push({ type: "completed", todo, previousStatus: prevTodo.status }) + } else if (todo.status === "in_progress" && prevTodo.status !== "in_progress") { + // Forward progress: starting a task + changes.push({ type: "started", todo, previousStatus: prevTodo.status }) + } + } else if (prevIndex !== currentIndex) { + // No status change, but position changed - this is reordering + changes.push({ type: "reordered", todo }) + } + } + } + + return changes +} + const UpdateTodoListToolBlock: React.FC = ({ todos = [], + previousTodos = [], content, onChange, editable = true, @@ -64,6 +128,18 @@ const UpdateTodoListToolBlock: React.FC = ({ const [deleteId, setDeleteId] = useState(null) const [isEditing, setIsEditing] = useState(false) + // For now, let's show changes only when previousTodos is explicitly provided + // This ensures we only show diffs when we have a proper previous state + const todoChanges = useMemo(() => { + if (previousTodos.length === 0) { + // If no previous todos provided, show all current todos as "added" + return todos.map((todo) => ({ type: "added" as const, todo })) + } + return calculateTodoChanges(previousTodos, todos) + }, [todos, previousTodos]) + + const hasChanges = todoChanges.length > 0 + // Automatically exit edit mode when external editable becomes false useEffect(() => { if (!editable && isEditing) { @@ -146,6 +222,174 @@ const UpdateTodoListToolBlock: React.FC = ({ } } + const renderChanges = () => { + if (!hasChanges) { + return ( + + {t("chat:fileOperations.todoChanges.noChanges")} + + ) + } + + return ( +
    + {todoChanges.map((change: TodoChange, idx: number) => { + let icon + let changeText + let textColor = "var(--vscode-foreground)" + + switch (change.type) { + case "added": + icon = ( + + ) + changeText = t("chat:fileOperations.todoChanges.added", { content: change.todo.content }) + textColor = "var(--vscode-charts-green)" + break + case "completed": + icon = ( + + ) + changeText = t("chat:fileOperations.todoChanges.completed", { + content: change.todo.content, + }) + textColor = "var(--vscode-charts-green)" + break + case "started": + icon = ( + + ) + changeText = t("chat:fileOperations.todoChanges.started", { content: change.todo.content }) + textColor = "var(--vscode-charts-yellow)" + break + case "removed": + icon = ( + + ) + changeText = t("chat:fileOperations.todoChanges.removed", { content: change.todo.content }) + textColor = "var(--vscode-descriptionForeground)" + break + case "reordered": + icon = ( + + ↕ + + ) + changeText = t("chat:fileOperations.todoChanges.reordered", { + content: change.todo.content, + }) + textColor = "var(--vscode-descriptionForeground)" + break + case "reverted": + icon = ( + + ) + changeText = t("chat:fileOperations.todoChanges.reverted", { content: change.todo.content }) + textColor = "var(--vscode-charts-orange)" + break + default: + return null + } + + return ( +
  • + {icon} + + {changeText} + +
  • + ) + })} +
+ ) + } + if (userEdited) { return ( @@ -206,230 +450,234 @@ const UpdateTodoListToolBlock: React.FC = ({
- {Array.isArray(editTodos) && editTodos.length > 0 ? ( -
    - {editTodos.map((todo, idx) => { - let icon - if (todo.status === "completed") { - icon = ( - - ) - } else if (todo.status === "in_progress") { - icon = ( - - ) - } else { - icon = ( - - ) - } - return ( -
  • - {icon} - {isEditing ? ( - handleContentChange(todo.id!, e.target.value)} + {isEditing ? ( + Array.isArray(editTodos) && editTodos.length > 0 ? ( +
      + {editTodos.map((todo, idx) => { + let icon + if (todo.status === "completed") { + icon = ( + { - if (!e.target.value.trim()) { - handleDelete(todo.id!) - } + marginTop: 7, + flexShrink: 0, }} /> - ) : ( + ) + } else if (todo.status === "in_progress") { + icon = ( - {todo.content} - - )} - {isEditing && ( - - )} - {isEditing && ( - - )} - - ) - })} - {adding ? ( -
    • - - setNewContent(e.target.value)} - onKeyDown={handleNewInputKeyDown} - style={{ - flex: 1, - minWidth: 0, - fontWeight: 500, - color: "var(--vscode-foreground)", - background: "transparent", - border: "none", - outline: "none", - fontSize: 13, - marginRight: 6, - padding: "1px 3px", - borderBottom: "1px solid #eee", - }} - /> - - -
    • - ) : ( -
    • - {isEditing && ( + /> + ) + } + return ( +
    • + {icon} + {isEditing ? ( + handleContentChange(todo.id!, e.target.value)} + style={{ + flex: 1, + minWidth: 0, + fontWeight: 500, + color: "var(--vscode-input-foreground)", + background: "var(--vscode-input-background)", + border: "none", + outline: "none", + fontSize: 13, + marginRight: 6, + padding: "1px 3px", + borderBottom: "1px solid var(--vscode-input-border)", + }} + onBlur={(e) => { + if (!e.target.value.trim()) { + handleDelete(todo.id!) + } + }} + /> + ) : ( + + {todo.content} + + )} + {isEditing && ( + + )} + {isEditing && ( + + )} +
    • + ) + })} + {adding ? ( +
    • + + setNewContent(e.target.value)} + onKeyDown={handleNewInputKeyDown} + style={{ + flex: 1, + minWidth: 0, + fontWeight: 500, + color: "var(--vscode-foreground)", + background: "transparent", + border: "none", + outline: "none", + fontSize: 13, + marginRight: 6, + padding: "1px 3px", + borderBottom: "1px solid #eee", + }} + /> + - )} -
    • - )} -
    +
  • + ) : ( +
  • + {isEditing && ( + + )} +
  • + )} +
+ ) : ( + + ) ) : ( - + renderChanges() )}
{/* Delete confirmation dialog */} diff --git a/webview-ui/src/components/chat/__tests__/UpdateTodoListToolBlock.spec.tsx b/webview-ui/src/components/chat/__tests__/UpdateTodoListToolBlock.spec.tsx new file mode 100644 index 0000000000..7c5143a346 --- /dev/null +++ b/webview-ui/src/components/chat/__tests__/UpdateTodoListToolBlock.spec.tsx @@ -0,0 +1,188 @@ +import { render, screen } from "@testing-library/react" +import { describe, it, expect, vi } from "vitest" +import UpdateTodoListToolBlock from "../UpdateTodoListToolBlock" + +// Mock react-i18next +vi.mock("react-i18next", () => ({ + useTranslation: () => ({ + t: (key: string, options?: { content?: string }) => { + const translations: Record = { + "chat:fileOperations.todoChanges.noChanges": "No changes", + "chat:fileOperations.todoChanges.added": `Added: ${options?.content || ""}`, + "chat:fileOperations.todoChanges.completed": `Completed: ${options?.content || ""}`, + "chat:fileOperations.todoChanges.started": `Started: ${options?.content || ""}`, + "chat:fileOperations.todoChanges.removed": `Removed: ${options?.content || ""}`, + "chat:fileOperations.todoChanges.reordered": `Reordered: ${options?.content || ""}`, + "chat:fileOperations.todoChanges.reverted": `Reverted: ${options?.content || ""}`, + "chat:fileOperations.todoListUpdated": "Todo list updated", + } + return translations[key] || key + }, + }), + initReactI18next: { + type: "3rdParty", + init: vi.fn(), + }, +})) + +describe("UpdateTodoListToolBlock", () => { + const mockOnChange = vi.fn() + + beforeEach(() => { + vi.clearAllMocks() + }) + + it("should show added todos when no previous todos", () => { + const todos = [{ id: "1", content: "New task", status: "" }] + + render() + + expect(screen.getByText("Added: New task")).toBeInTheDocument() + }) + + it("should show completed todos", () => { + const previousTodos = [{ id: "1", content: "Task 1", status: "" }] + const todos = [{ id: "1", content: "Task 1", status: "completed" }] + + render() + + expect(screen.getByText("Completed: Task 1")).toBeInTheDocument() + }) + + it("should show started todos", () => { + const previousTodos = [{ id: "1", content: "Task 1", status: "" }] + const todos = [{ id: "1", content: "Task 1", status: "in_progress" }] + + render() + + expect(screen.getByText("Started: Task 1")).toBeInTheDocument() + }) + + it("should show removed todos", () => { + const previousTodos = [ + { id: "1", content: "Task 1", status: "" }, + { id: "2", content: "Task 2", status: "" }, + ] + const todos = [{ id: "1", content: "Task 1", status: "" }] + + render() + + expect(screen.getByText("Removed: Task 2")).toBeInTheDocument() + }) + + it("should show no changes when todos are identical", () => { + const todos = [{ id: "1", content: "Task 1", status: "" }] + + render() + + expect(screen.getByText("No changes")).toBeInTheDocument() + }) + + it("should show multiple changes", () => { + const previousTodos = [ + { id: "1", content: "Task 1", status: "" }, + { id: "2", content: "Task 2", status: "in_progress" }, + { id: "3", content: "Task 3", status: "" }, + ] + const todos = [ + { id: "1", content: "Task 1", status: "completed" }, + { id: "2", content: "Task 2", status: "completed" }, + { id: "4", content: "New Task", status: "" }, + ] + + render() + + expect(screen.getByText("Completed: Task 1")).toBeInTheDocument() + expect(screen.getByText("Completed: Task 2")).toBeInTheDocument() + expect(screen.getByText("Removed: Task 3")).toBeInTheDocument() + expect(screen.getByText("Added: New Task")).toBeInTheDocument() + }) + + it("should show reordered todos when position changes", () => { + const previousTodos = [ + { id: "1", content: "Task 1", status: "" }, + { id: "2", content: "Task 2", status: "" }, + { id: "3", content: "Task 3", status: "" }, + ] + const todos = [ + { id: "3", content: "Task 3", status: "" }, + { id: "1", content: "Task 1", status: "" }, + { id: "2", content: "Task 2", status: "" }, + ] + + render() + + expect(screen.getByText("Reordered: Task 3")).toBeInTheDocument() + expect(screen.getByText("Reordered: Task 1")).toBeInTheDocument() + expect(screen.getByText("Reordered: Task 2")).toBeInTheDocument() + }) + + it("should show reverted when todo goes from completed to pending", () => { + const previousTodos = [{ id: "1", content: "Task 1", status: "completed" }] + const todos = [{ id: "1", content: "Task 1", status: "" }] + + render() + + expect(screen.getByText("Reverted: Task 1")).toBeInTheDocument() + }) + + it("should show reverted when todo goes from in_progress to pending", () => { + const previousTodos = [{ id: "1", content: "Task 1", status: "in_progress" }] + const todos = [{ id: "1", content: "Task 1", status: "" }] + + render() + + expect(screen.getByText("Reverted: Task 1")).toBeInTheDocument() + }) + + it("should show reverted when todo goes from completed to in_progress", () => { + const previousTodos = [{ id: "1", content: "Task 1", status: "completed" }] + const todos = [{ id: "1", content: "Task 1", status: "in_progress" }] + + render() + + expect(screen.getByText("Reverted: Task 1")).toBeInTheDocument() + }) + + it("should prioritize status change over reordering", () => { + const previousTodos = [ + { id: "1", content: "Task 1", status: "" }, + { id: "2", content: "Task 2", status: "" }, + ] + const todos = [ + { id: "2", content: "Task 2", status: "completed" }, + { id: "1", content: "Task 1", status: "" }, + ] + + render() + + // Should show completed status change, not reordering + expect(screen.getByText("Completed: Task 2")).toBeInTheDocument() + expect(screen.getByText("Reordered: Task 1")).toBeInTheDocument() + // Task 2 should not show as reordered since it has a status change + expect(screen.queryByText("Reordered: Task 2")).not.toBeInTheDocument() + }) + + it("should handle mixed changes correctly", () => { + const previousTodos = [ + { id: "1", content: "Task 1", status: "completed" }, + { id: "2", content: "Task 2", status: "" }, + { id: "3", content: "Task 3", status: "in_progress" }, + { id: "4", content: "Task 4", status: "" }, + ] + const todos = [ + { id: "4", content: "Task 4", status: "" }, // reordered (3→0) + { id: "2", content: "Task 2", status: "completed" }, // completed + { id: "1", content: "Task 1", status: "" }, // reverted + { id: "5", content: "New Task", status: "" }, // added + ] + + render() + + expect(screen.getByText("Added: New Task")).toBeInTheDocument() + expect(screen.getByText("Removed: Task 3")).toBeInTheDocument() + expect(screen.getByText("Completed: Task 2")).toBeInTheDocument() + expect(screen.getByText("Reverted: Task 1")).toBeInTheDocument() + expect(screen.getByText("Reordered: Task 4")).toBeInTheDocument() + }) +}) diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index 1dd892a39f..b625224fe9 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "Roo vol afegir contingut al final d'aquest fitxer:", "wantsToReadAndXMore": "En Roo vol llegir aquest fitxer i {{count}} més:", "wantsToReadMultiple": "Roo vol llegir diversos fitxers:", - "wantsToApplyBatchChanges": "Roo vol aplicar canvis a múltiples fitxers:" + "wantsToApplyBatchChanges": "Roo vol aplicar canvis a múltiples fitxers:", + "todoListUpdated": "Llista de tasques actualitzada", + "todoChanges": { + "noChanges": "No hi ha canvis a la llista de tasques", + "added": "Afegit: {{content}}", + "completed": "Completat: {{content}}", + "started": "Iniciat: {{content}}", + "removed": "Eliminat: {{content}}", + "reordered": "Reordenat: {{content}}", + "reverted": "Revertit: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo vol veure els fitxers de nivell superior en aquest directori:", diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index c62fe9d3bb..943e4f627b 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -169,7 +169,17 @@ "wantsToInsertWithLineNumber": "Roo möchte Inhalte in diese Datei in Zeile {{lineNumber}} einfügen:", "wantsToInsertAtEnd": "Roo möchte Inhalte am Ende dieser Datei anhängen:", "wantsToReadMultiple": "Roo möchte mehrere Dateien lesen:", - "wantsToApplyBatchChanges": "Roo möchte Änderungen an mehreren Dateien vornehmen:" + "wantsToApplyBatchChanges": "Roo möchte Änderungen an mehreren Dateien vornehmen:", + "todoListUpdated": "Todo-Liste aktualisiert", + "todoChanges": { + "noChanges": "Keine Änderungen", + "added": "Hinzugefügt: {{content}}", + "completed": "Abgeschlossen: {{content}}", + "started": "Begonnen: {{content}}", + "removed": "Entfernt: {{content}}", + "reordered": "Neu angeordnet: {{content}}", + "reverted": "Rückgängig gemacht: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo möchte die Dateien auf oberster Ebene in diesem Verzeichnis anzeigen:", diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index ea4f8920f5..f0b3e7c545 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -178,7 +178,17 @@ "didSearchReplace": "Roo performed search and replace on this file:", "wantsToInsert": "Roo wants to insert content into this file:", "wantsToInsertWithLineNumber": "Roo wants to insert content into this file at line {{lineNumber}}:", - "wantsToInsertAtEnd": "Roo wants to append content to the end of this file:" + "wantsToInsertAtEnd": "Roo wants to append content to the end of this file:", + "todoListUpdated": "Todo list updated", + "todoChanges": { + "noChanges": "No changes", + "added": "Added: {{content}}", + "completed": "Completed: {{content}}", + "started": "Started: {{content}}", + "removed": "Removed: {{content}}", + "reordered": "Reordered: {{content}}", + "reverted": "Reverted: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo wants to view the top level files in this directory:", diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index a4349b13dc..56ecf62d65 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "Roo quiere añadir contenido al final de este archivo:", "wantsToReadAndXMore": "Roo quiere leer este archivo y {{count}} más:", "wantsToReadMultiple": "Roo quiere leer varios archivos:", - "wantsToApplyBatchChanges": "Roo quiere aplicar cambios a múltiples archivos:" + "wantsToApplyBatchChanges": "Roo quiere aplicar cambios a múltiples archivos:", + "todoListUpdated": "Lista de tareas actualizada", + "todoChanges": { + "noChanges": "Sin cambios", + "added": "Añadido: {{content}}", + "completed": "Completado: {{content}}", + "started": "Iniciado: {{content}}", + "removed": "Eliminado: {{content}}", + "reordered": "Reordenado: {{content}}", + "reverted": "Revertido: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo quiere ver los archivos de nivel superior en este directorio:", diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index b5f06354bb..836efffd89 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -166,7 +166,17 @@ "wantsToInsertAtEnd": "Roo veut ajouter du contenu à la fin de ce fichier :", "wantsToReadAndXMore": "Roo veut lire ce fichier et {{count}} de plus :", "wantsToReadMultiple": "Roo souhaite lire plusieurs fichiers :", - "wantsToApplyBatchChanges": "Roo veut appliquer des modifications à plusieurs fichiers :" + "wantsToApplyBatchChanges": "Roo veut appliquer des modifications à plusieurs fichiers :", + "todoListUpdated": "Liste de tâches mise à jour", + "todoChanges": { + "noChanges": "Aucun changement", + "added": "Ajouté : {{content}}", + "completed": "Terminé : {{content}}", + "started": "Commencé : {{content}}", + "removed": "Supprimé : {{content}}", + "reordered": "Réorganisé : {{content}}", + "reverted": "Annulé : {{content}}" + } }, "instructions": { "wantsToFetch": "Roo veut récupérer des instructions détaillées pour aider à la tâche actuelle" diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 9afdcbdd38..e9ad22fd87 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "Roo इस फ़ाइल के अंत में सामग्री जोड़ना चाहता है:", "wantsToReadAndXMore": "रू इस फ़ाइल को और {{count}} अन्य को पढ़ना चाहता है:", "wantsToReadMultiple": "Roo कई फ़ाइलें पढ़ना चाहता है:", - "wantsToApplyBatchChanges": "Roo कई फ़ाइलों में परिवर्तन लागू करना चाहता है:" + "wantsToApplyBatchChanges": "Roo कई फ़ाइलों में परिवर्तन लागू करना चाहता है:", + "todoListUpdated": "कार्य सूची अपडेट की गई", + "todoChanges": { + "noChanges": "कार्य सूची में कोई परिवर्तन नहीं", + "added": "जोड़ा गया: {{content}}", + "completed": "पूर्ण: {{content}}", + "started": "शुरू किया गया: {{content}}", + "removed": "हटाया गया: {{content}}", + "reordered": "पुनः क्रमबद्ध: {{content}}", + "reverted": "वापस किया गया: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo इस निर्देशिका में शीर्ष स्तर की फ़ाइलें देखना चाहता है:", diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index fc0bd5056f..0a103afbbe 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -179,6 +179,16 @@ "wantsToEditOutsideWorkspace": "Roo ingin mengedit file ini di luar workspace:", "wantsToEditProtected": "Roo ingin mengedit file konfigurasi yang dilindungi:", "wantsToApplyBatchChanges": "Roo ingin menerapkan perubahan ke beberapa file:", + "todoListUpdated": "Daftar tugas diperbarui", + "todoChanges": { + "noChanges": "Tidak ada perubahan pada daftar tugas", + "added": "Ditambahkan: {{content}}", + "completed": "Selesai: {{content}}", + "started": "Dimulai: {{content}}", + "removed": "Dihapus: {{content}}", + "reordered": "Diurutkan ulang: {{content}}", + "reverted": "Dikembalikan: {{content}}" + }, "wantsToCreate": "Roo ingin membuat file baru:", "wantsToSearchReplace": "Roo ingin mencari dan mengganti di file ini:", "didSearchReplace": "Roo melakukan pencarian dan penggantian pada file ini:", diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index f49a25dfa6..fd8c258505 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "Roo vuole aggiungere contenuto alla fine di questo file:", "wantsToReadAndXMore": "Roo vuole leggere questo file e altri {{count}}:", "wantsToReadMultiple": "Roo vuole leggere più file:", - "wantsToApplyBatchChanges": "Roo vuole applicare modifiche a più file:" + "wantsToApplyBatchChanges": "Roo vuole applicare modifiche a più file:", + "todoListUpdated": "Lista todo aggiornata", + "todoChanges": { + "noChanges": "Nessuna modifica alla lista todo", + "added": "Aggiunto: {{content}}", + "completed": "Completato: {{content}}", + "started": "Iniziato: {{content}}", + "removed": "Rimosso: {{content}}", + "reordered": "Riordinato: {{content}}", + "reverted": "Ripristinato: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo vuole visualizzare i file di primo livello in questa directory:", diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index cb5ebcdafd..469f5cefe0 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "Rooはこのファイルの末尾にコンテンツを追加したい:", "wantsToReadAndXMore": "Roo はこのファイルと他に {{count}} 個のファイルを読み込もうとしています:", "wantsToReadMultiple": "Rooは複数のファイルを読み取ろうとしています:", - "wantsToApplyBatchChanges": "Rooは複数のファイルに変更を適用したい:" + "wantsToApplyBatchChanges": "Rooは複数のファイルに変更を適用したい:", + "todoListUpdated": "Todoリストが更新されました", + "todoChanges": { + "noChanges": "Todoリストに変更はありません", + "added": "追加: {{content}}", + "completed": "完了: {{content}}", + "started": "開始: {{content}}", + "removed": "削除: {{content}}", + "reordered": "並び替え: {{content}}", + "reverted": "元に戻す: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Rooはこのディレクトリのトップレベルファイルを表示したい:", diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index 1f86dc8cf4..a1a6c9c1fe 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "Roo가 이 파일의 끝에 내용을 추가하고 싶어합니다:", "wantsToReadAndXMore": "Roo가 이 파일과 {{count}}개의 파일을 더 읽으려고 합니다:", "wantsToReadMultiple": "Roo가 여러 파일을 읽으려고 합니다:", - "wantsToApplyBatchChanges": "Roo가 여러 파일에 변경 사항을 적용하고 싶어합니다:" + "wantsToApplyBatchChanges": "Roo가 여러 파일에 변경 사항을 적용하고 싶어합니다:", + "todoListUpdated": "할 일 목록이 업데이트되었습니다", + "todoChanges": { + "noChanges": "할 일 목록에 변경 사항이 없습니다", + "added": "추가됨: {{content}}", + "completed": "완료됨: {{content}}", + "started": "시작됨: {{content}}", + "removed": "제거됨: {{content}}", + "reordered": "재정렬됨: {{content}}", + "reverted": "되돌림: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo가 이 디렉토리의 최상위 파일을 보고 싶어합니다:", diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index d228d7b0c2..c384943129 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -164,7 +164,17 @@ "wantsToInsertAtEnd": "Roo wil inhoud toevoegen aan het einde van dit bestand:", "wantsToReadAndXMore": "Roo wil dit bestand en nog {{count}} andere lezen:", "wantsToReadMultiple": "Roo wil meerdere bestanden lezen:", - "wantsToApplyBatchChanges": "Roo wil wijzigingen toepassen op meerdere bestanden:" + "wantsToApplyBatchChanges": "Roo wil wijzigingen toepassen op meerdere bestanden:", + "todoListUpdated": "Todo-lijst bijgewerkt", + "todoChanges": { + "noChanges": "Geen wijzigingen in de todo-lijst", + "added": "Toegevoegd: {{content}}", + "completed": "Voltooid: {{content}}", + "started": "Gestart: {{content}}", + "removed": "Verwijderd: {{content}}", + "reordered": "Hergerangschikt: {{content}}", + "reverted": "Teruggedraaid: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo wil de bovenliggende bestanden in deze map bekijken:", diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index fdb39b0851..7f50956c7b 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "Roo chce dodać zawartość na końcu tego pliku:", "wantsToReadAndXMore": "Roo chce przeczytać ten plik i {{count}} więcej:", "wantsToReadMultiple": "Roo chce odczytać wiele plików:", - "wantsToApplyBatchChanges": "Roo chce zastosować zmiany do wielu plików:" + "wantsToApplyBatchChanges": "Roo chce zastosować zmiany do wielu plików:", + "todoListUpdated": "Lista zadań zaktualizowana", + "todoChanges": { + "noChanges": "Brak zmian w liście zadań", + "added": "Dodano: {{content}}", + "completed": "Ukończono: {{content}}", + "started": "Rozpoczęto: {{content}}", + "removed": "Usunięto: {{content}}", + "reordered": "Zmieniono kolejność: {{content}}", + "reverted": "Cofnięto: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo chce zobaczyć pliki najwyższego poziomu w tym katalogu:", diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index b37879f2f0..f60335d255 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "Roo quer adicionar conteúdo ao final deste arquivo:", "wantsToReadAndXMore": "Roo quer ler este arquivo e mais {{count}}:", "wantsToReadMultiple": "Roo deseja ler múltiplos arquivos:", - "wantsToApplyBatchChanges": "Roo quer aplicar alterações a múltiplos arquivos:" + "wantsToApplyBatchChanges": "Roo quer aplicar alterações a múltiplos arquivos:", + "todoListUpdated": "Lista de tarefas atualizada", + "todoChanges": { + "noChanges": "Nenhuma alteração na lista de tarefas", + "added": "Adicionado: {{content}}", + "completed": "Concluído: {{content}}", + "started": "Iniciado: {{content}}", + "removed": "Removido: {{content}}", + "reordered": "Reordenado: {{content}}", + "reverted": "Revertido: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo quer visualizar os arquivos de nível superior neste diretório:", diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 5865e41a53..8642560fc4 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -164,7 +164,17 @@ "wantsToInsertAtEnd": "Roo хочет добавить содержимое в конец этого файла:", "wantsToReadAndXMore": "Roo хочет прочитать этот файл и еще {{count}}:", "wantsToReadMultiple": "Roo хочет прочитать несколько файлов:", - "wantsToApplyBatchChanges": "Roo хочет применить изменения к нескольким файлам:" + "wantsToApplyBatchChanges": "Roo хочет применить изменения к нескольким файлам:", + "todoListUpdated": "Список задач обновлен", + "todoChanges": { + "noChanges": "Нет изменений в списке задач", + "added": "Добавлено: {{content}}", + "completed": "Завершено: {{content}}", + "started": "Начато: {{content}}", + "removed": "Удалено: {{content}}", + "reordered": "Переупорядочено: {{content}}", + "reverted": "Отменено: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo хочет просмотреть файлы верхнего уровня в этой директории:", diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 6c5bd20353..14143878d8 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "Roo bu dosyanın sonuna içerik eklemek istiyor:", "wantsToReadAndXMore": "Roo bu dosyayı ve {{count}} tane daha okumak istiyor:", "wantsToReadMultiple": "Roo birden fazla dosya okumak istiyor:", - "wantsToApplyBatchChanges": "Roo birden fazla dosyaya değişiklik uygulamak istiyor:" + "wantsToApplyBatchChanges": "Roo birden fazla dosyaya değişiklik uygulamak istiyor:", + "todoListUpdated": "Yapılacaklar listesi güncellendi", + "todoChanges": { + "noChanges": "Yapılacaklar listesinde değişiklik yok", + "added": "Eklendi: {{content}}", + "completed": "Tamamlandı: {{content}}", + "started": "Başlatıldı: {{content}}", + "removed": "Kaldırıldı: {{content}}", + "reordered": "Yeniden sıralandı: {{content}}", + "reverted": "Geri alındı: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo bu dizindeki üst düzey dosyaları görüntülemek istiyor:", diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index eb7cdc2306..c9ad329ca9 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "Roo muốn thêm nội dung vào cuối tệp này:", "wantsToReadAndXMore": "Roo muốn đọc tệp này và {{count}} tệp khác:", "wantsToReadMultiple": "Roo muốn đọc nhiều tệp:", - "wantsToApplyBatchChanges": "Roo muốn áp dụng thay đổi cho nhiều tệp:" + "wantsToApplyBatchChanges": "Roo muốn áp dụng thay đổi cho nhiều tệp:", + "todoListUpdated": "Danh sách việc cần làm đã được cập nhật", + "todoChanges": { + "noChanges": "Không có thay đổi trong danh sách việc cần làm", + "added": "Đã thêm: {{content}}", + "completed": "Đã hoàn thành: {{content}}", + "started": "Đã bắt đầu: {{content}}", + "removed": "Đã xóa: {{content}}", + "reordered": "Đã sắp xếp lại: {{content}}", + "reverted": "Đã hoàn nguyên: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo muốn xem các tệp cấp cao nhất trong thư mục này:", diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 93494e6f50..e06d836f8d 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "需要在文件末尾添加内容:", "wantsToReadAndXMore": "Roo 想读取此文件以及另外 {{count}} 个文件:", "wantsToReadMultiple": "Roo 想要读取多个文件:", - "wantsToApplyBatchChanges": "Roo 想要对多个文件应用更改:" + "wantsToApplyBatchChanges": "Roo 想要对多个文件应用更改:", + "todoListUpdated": "待办列表已更新", + "todoChanges": { + "noChanges": "无变更", + "added": "新增: {{content}}", + "completed": "已完成: {{content}}", + "started": "已开始: {{content}}", + "removed": "已移除: {{content}}", + "reordered": "已重新排序: {{content}}", + "reverted": "已还原: {{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "需要查看目录文件列表:", diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index e7a476cb37..27352b3f9e 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -169,7 +169,17 @@ "wantsToInsertAtEnd": "Roo 想要在此檔案末尾新增內容:", "wantsToReadAndXMore": "Roo 想要讀取此檔案以及另外 {{count}} 個檔案:", "wantsToReadMultiple": "Roo 想要讀取多個檔案:", - "wantsToApplyBatchChanges": "Roo 想要對多個檔案套用變更:" + "wantsToApplyBatchChanges": "Roo 想要對多個檔案套用變更:", + "todoListUpdated": "待辦清單已更新", + "todoChanges": { + "noChanges": "待辦清單無變更", + "added": "已新增:{{content}}", + "completed": "已完成:{{content}}", + "started": "已開始:{{content}}", + "removed": "已移除:{{content}}", + "reordered": "已重新排序:{{content}}", + "reverted": "已還原:{{content}}" + } }, "directoryOperations": { "wantsToViewTopLevel": "Roo 想要檢視此目錄中最上層的檔案:", From 764b23251e1b6c37aa0e99e7dc1cde42a6ba5539 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 7 Jul 2025 11:54:33 -0400 Subject: [PATCH 05/12] Fix tests --- .../architect-mode-prompt.snap | 70 +++++++++++++++++++ .../ask-mode-prompt.snap | 70 +++++++++++++++++++ .../mcp-server-creation-disabled.snap | 70 +++++++++++++++++++ .../mcp-server-creation-enabled.snap | 70 +++++++++++++++++++ .../partial-reads-enabled.snap | 70 +++++++++++++++++++ .../consistent-system-prompt.snap | 70 +++++++++++++++++++ .../with-computer-use-support.snap | 70 +++++++++++++++++++ .../with-diff-enabled-false.snap | 70 +++++++++++++++++++ .../system-prompt/with-diff-enabled-true.snap | 70 +++++++++++++++++++ .../with-diff-enabled-undefined.snap | 70 +++++++++++++++++++ .../with-different-viewport-size.snap | 70 +++++++++++++++++++ .../system-prompt/with-mcp-hub-provided.snap | 70 +++++++++++++++++++ .../system-prompt/with-undefined-mcp-hub.snap | 70 +++++++++++++++++++ 13 files changed, 910 insertions(+) diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap index 47edfe467a..42e34bea6e 100644 --- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap +++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap @@ -368,6 +368,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap index e665309432..9062b97453 100644 --- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap +++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap @@ -265,6 +265,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap index c964d5f6ed..982a9c6b41 100644 --- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap +++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap @@ -417,6 +417,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap index 5a25768204..acab13612a 100644 --- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap +++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap @@ -417,6 +417,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap index f74e394d2d..425acbcd39 100644 --- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap +++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap @@ -373,6 +373,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap index 47edfe467a..42e34bea6e 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap @@ -368,6 +368,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap index ecd22a6d6e..09ebc0fef2 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap @@ -421,6 +421,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap index 47edfe467a..42e34bea6e 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap @@ -368,6 +368,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap index 9514c93ea3..0b5da823d4 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap @@ -456,6 +456,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap index 47edfe467a..42e34bea6e 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap @@ -368,6 +368,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap index 32acd4c169..eb76871b13 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap @@ -421,6 +421,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap index 5a25768204..acab13612a 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap @@ -417,6 +417,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap index 47edfe467a..42e34bea6e 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap @@ -368,6 +368,76 @@ Example: +## update_todo_list + +**Description:** +Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks. + +**Checklist Format:** +- Use a single-level markdown checklist (no nesting or subtasks). +- List todos in the intended execution order. +- Status options: + - [ ] Task description (pending) + - [x] Task description (completed) + - [-] Task description (in progress) + +**Status Rules:** +- [ ] = pending (not started) +- [x] = completed (fully finished, no unresolved issues) +- [-] = in_progress (currently being worked on) + +**Core Principles:** +- Before updating, always confirm which todos have been completed since the last update. +- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress). +- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately. +- Do not remove any unfinished todos unless explicitly instructed. +- Always retain all unfinished tasks, updating their status as needed. +- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies). +- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved. +- Remove tasks only if they are no longer relevant or if the user requests deletion. + +**Usage Example:** + + +[x] Analyze requirements +[x] Design architecture +[-] Implement core logic +[ ] Write tests +[ ] Update documentation + + + +*After completing "Implement core logic" and starting "Write tests":* + + +[x] Analyze requirements +[x] Design architecture +[x] Implement core logic +[-] Write tests +[ ] Update documentation +[ ] Add performance benchmarks + + + +**When to Use:** +- The task involves multiple steps or requires ongoing tracking. +- You need to update the status of several todos at once. +- New actionable items are discovered during task execution. +- The user requests a todo list or provides multiple tasks. +- The task is complex and benefits from clear, stepwise progress tracking. + +**When NOT to Use:** +- There is only a single, trivial task. +- The task can be completed in one or two simple steps. +- The request is purely conversational or informational. + +**Task Management Guidelines:** +- Mark task as completed immediately after all work of the current task is done. +- Start the next task by marking it as in_progress. +- Add new todos as soon as they are identified. +- Use clear, descriptive task names. + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. From 74dc06c43fa7d8dd07bae8827d10ea420be68e9b Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 7 Jul 2025 12:02:07 -0400 Subject: [PATCH 06/12] Explicit assistant message --- src/core/assistant-message/presentAssistantMessage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts index ab158c7ff6..ee3fa148b4 100644 --- a/src/core/assistant-message/presentAssistantMessage.ts +++ b/src/core/assistant-message/presentAssistantMessage.ts @@ -206,14 +206,14 @@ export async function presentAssistantMessage(cline: Task) { return `[${block.name} to '${block.params.mode_slug}'${block.params.reason ? ` because: ${block.params.reason}` : ""}]` case "codebase_search": // Add case for the new tool return `[${block.name} for '${block.params.query}']` + case "update_todo_list": + return `[${block.name}]` case "new_task": { const mode = block.params.mode ?? defaultModeSlug const message = block.params.message ?? "(no message)" const modeName = getModeBySlug(mode, customModes)?.name ?? mode return `[${block.name} in ${modeName} mode: '${message}']` } - default: - return `[${block.name}]` } } From f41260eaf306a777eefa7aff92093a3bc7282ec5 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 7 Jul 2025 12:26:04 -0400 Subject: [PATCH 07/12] Alignment --- .../add-custom-instructions/architect-mode-prompt.snap | 2 +- .../__snapshots__/add-custom-instructions/ask-mode-prompt.snap | 2 +- .../add-custom-instructions/mcp-server-creation-disabled.snap | 2 +- .../add-custom-instructions/mcp-server-creation-enabled.snap | 2 +- .../add-custom-instructions/partial-reads-enabled.snap | 2 +- .../__snapshots__/system-prompt/consistent-system-prompt.snap | 2 +- .../__snapshots__/system-prompt/with-computer-use-support.snap | 2 +- .../__snapshots__/system-prompt/with-diff-enabled-false.snap | 2 +- .../__snapshots__/system-prompt/with-diff-enabled-true.snap | 2 +- .../system-prompt/with-diff-enabled-undefined.snap | 2 +- .../system-prompt/with-different-viewport-size.snap | 2 +- .../__snapshots__/system-prompt/with-mcp-hub-provided.snap | 2 +- .../__snapshots__/system-prompt/with-undefined-mcp-hub.snap | 2 +- src/core/prompts/tools/update-todo-list.ts | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap index 42e34bea6e..31b49ec682 100644 --- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap +++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap @@ -384,7 +384,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap index 9062b97453..33b6addf28 100644 --- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap +++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap @@ -281,7 +281,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap index 982a9c6b41..b2b0abfad4 100644 --- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap +++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap @@ -433,7 +433,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap index acab13612a..0eecf0482e 100644 --- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap +++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap @@ -433,7 +433,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap index 425acbcd39..731f4b29a7 100644 --- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap +++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap @@ -389,7 +389,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap index 42e34bea6e..31b49ec682 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap @@ -384,7 +384,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap index 09ebc0fef2..fda93c1c77 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap @@ -437,7 +437,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap index 42e34bea6e..31b49ec682 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap @@ -384,7 +384,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap index 0b5da823d4..8db4d0b377 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap @@ -472,7 +472,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap index 42e34bea6e..31b49ec682 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap @@ -384,7 +384,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap index eb76871b13..44d5b58327 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap @@ -437,7 +437,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap index acab13612a..0eecf0482e 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap @@ -433,7 +433,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap index 42e34bea6e..31b49ec682 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap @@ -384,7 +384,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. diff --git a/src/core/prompts/tools/update-todo-list.ts b/src/core/prompts/tools/update-todo-list.ts index 96271c4838..528d5a1b51 100644 --- a/src/core/prompts/tools/update-todo-list.ts +++ b/src/core/prompts/tools/update-todo-list.ts @@ -20,7 +20,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st **Status Rules:** - [ ] = pending (not started) - [x] = completed (fully finished, no unresolved issues) -- [-] = in_progress (currently being worked on) +- [-] = in_progress (currently being worked on) **Core Principles:** - Before updating, always confirm which todos have been completed since the last update. From 2c66e8b7c9d2775bc7b3d456862f1ce6f0d450fa Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 7 Jul 2025 13:03:53 -0400 Subject: [PATCH 08/12] Revert "Show diffs in todo list updates" This reverts commit 547387f2cb136f7f88d81815586ae3943d5e5023. --- webview-ui/src/components/chat/ChatRow.tsx | 24 +- .../chat/UpdateTodoListToolBlock.tsx | 658 ++++++------------ .../UpdateTodoListToolBlock.spec.tsx | 188 ----- webview-ui/src/i18n/locales/ca/chat.json | 12 +- webview-ui/src/i18n/locales/de/chat.json | 12 +- webview-ui/src/i18n/locales/en/chat.json | 12 +- webview-ui/src/i18n/locales/es/chat.json | 12 +- webview-ui/src/i18n/locales/fr/chat.json | 12 +- webview-ui/src/i18n/locales/hi/chat.json | 12 +- webview-ui/src/i18n/locales/id/chat.json | 10 - webview-ui/src/i18n/locales/it/chat.json | 12 +- webview-ui/src/i18n/locales/ja/chat.json | 12 +- webview-ui/src/i18n/locales/ko/chat.json | 12 +- webview-ui/src/i18n/locales/nl/chat.json | 12 +- webview-ui/src/i18n/locales/pl/chat.json | 12 +- webview-ui/src/i18n/locales/pt-BR/chat.json | 12 +- webview-ui/src/i18n/locales/ru/chat.json | 12 +- webview-ui/src/i18n/locales/tr/chat.json | 12 +- webview-ui/src/i18n/locales/vi/chat.json | 12 +- webview-ui/src/i18n/locales/zh-CN/chat.json | 12 +- webview-ui/src/i18n/locales/zh-TW/chat.json | 12 +- 21 files changed, 223 insertions(+), 861 deletions(-) delete mode 100644 webview-ui/src/components/chat/__tests__/UpdateTodoListToolBlock.spec.tsx diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 7201aabb2c..9c90da503f 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -107,7 +107,7 @@ export const ChatRowContent = ({ editable, }: ChatRowContentProps) => { const { t } = useTranslation() - const { mcpServers, alwaysAllowMcp, currentCheckpoint, clineMessages } = useExtensionState() + const { mcpServers, alwaysAllowMcp, currentCheckpoint } = useExtensionState() const [reasoningCollapsed, setReasoningCollapsed] = useState(true) const [isDiffErrorExpanded, setIsDiffErrorExpanded] = useState(false) const [showCopySuccess, setShowCopySuccess] = useState(false) @@ -436,31 +436,9 @@ export const ChatRowContent = ({ } case "updateTodoList" as any: { const todos = (tool as any).todos || [] - - // Try to find previous todo list from earlier messages - const currentMessageIndex = clineMessages.findIndex((msg) => msg.ts === message.ts) - let previousTodos: any[] = [] - - // Look backwards through messages to find the most recent updateTodoList - for (let i = currentMessageIndex - 1; i >= 0; i--) { - const prevMessage = clineMessages[i] - if (prevMessage.ask === "tool" && prevMessage.text) { - try { - const prevTool = JSON.parse(prevMessage.text) - if (prevTool.tool === "updateTodoList" && prevTool.todos) { - previousTodos = prevTool.todos - break - } - } catch { - // Ignore parsing errors - } - } - } - return ( { if (typeof vscode !== "undefined" && vscode?.postMessage) { diff --git a/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx index 6d7662f26d..2ca0607302 100644 --- a/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx +++ b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef, useMemo } from "react" +import React, { useState, useEffect, useRef } from "react" import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock" import MarkdownBlock from "../common/MarkdownBlock" import { useTranslation } from "react-i18next" @@ -16,7 +16,6 @@ interface TodoItem { */ interface UpdateTodoListToolBlockProps { todos?: TodoItem[] - previousTodos?: TodoItem[] content?: string /** * Callback when todos change, be sure to implement and notify the model with the latest todos @@ -28,12 +27,6 @@ interface UpdateTodoListToolBlockProps { userEdited?: boolean } -interface TodoChange { - type: "added" | "completed" | "started" | "removed" | "reordered" | "reverted" - todo: TodoItem - previousStatus?: string -} - const STATUS_OPTIONS = [ { value: "", label: "Not Started", color: "var(--vscode-foreground)", border: "#bbb", bg: "transparent" }, { @@ -54,65 +47,8 @@ const STATUS_OPTIONS = [ const genId = () => Math.random().toString(36).slice(2, 10) -const calculateTodoChanges = (previousTodos: TodoItem[] = [], currentTodos: TodoItem[] = []): TodoChange[] => { - const changes: TodoChange[] = [] - - // Create maps with content as key and include position info - const prevMap = new Map(previousTodos.map((todo, idx) => [todo.content, { todo, index: idx }])) - const currentMap = new Map(currentTodos.map((todo, idx) => [todo.content, { todo, index: idx }])) - - // Find added todos - for (const todo of currentTodos) { - if (!prevMap.has(todo.content)) { - changes.push({ type: "added", todo }) - } - } - - // Find removed todos - for (const todo of previousTodos) { - if (!currentMap.has(todo.content)) { - changes.push({ type: "removed", todo }) - } - } - - // Find status changes and reordering for existing todos - for (const todo of currentTodos) { - const prevEntry = prevMap.get(todo.content) - const currentEntry = currentMap.get(todo.content) - - if (prevEntry && currentEntry) { - const { todo: prevTodo, index: prevIndex } = prevEntry - const { index: currentIndex } = currentEntry - - // Check for status changes first (they take priority) - if (prevTodo.status !== todo.status) { - // Check for backward/regression status changes first - const isRegression = - (prevTodo.status === "completed" && (todo.status === "" || todo.status === "in_progress")) || - (prevTodo.status === "in_progress" && todo.status === "") - - if (isRegression) { - changes.push({ type: "reverted", todo, previousStatus: prevTodo.status }) - } else if (todo.status === "completed" && prevTodo.status !== "completed") { - // Forward progress: completing a task - changes.push({ type: "completed", todo, previousStatus: prevTodo.status }) - } else if (todo.status === "in_progress" && prevTodo.status !== "in_progress") { - // Forward progress: starting a task - changes.push({ type: "started", todo, previousStatus: prevTodo.status }) - } - } else if (prevIndex !== currentIndex) { - // No status change, but position changed - this is reordering - changes.push({ type: "reordered", todo }) - } - } - } - - return changes -} - const UpdateTodoListToolBlock: React.FC = ({ todos = [], - previousTodos = [], content, onChange, editable = true, @@ -128,18 +64,6 @@ const UpdateTodoListToolBlock: React.FC = ({ const [deleteId, setDeleteId] = useState(null) const [isEditing, setIsEditing] = useState(false) - // For now, let's show changes only when previousTodos is explicitly provided - // This ensures we only show diffs when we have a proper previous state - const todoChanges = useMemo(() => { - if (previousTodos.length === 0) { - // If no previous todos provided, show all current todos as "added" - return todos.map((todo) => ({ type: "added" as const, todo })) - } - return calculateTodoChanges(previousTodos, todos) - }, [todos, previousTodos]) - - const hasChanges = todoChanges.length > 0 - // Automatically exit edit mode when external editable becomes false useEffect(() => { if (!editable && isEditing) { @@ -222,174 +146,6 @@ const UpdateTodoListToolBlock: React.FC = ({ } } - const renderChanges = () => { - if (!hasChanges) { - return ( - - {t("chat:fileOperations.todoChanges.noChanges")} - - ) - } - - return ( -
    - {todoChanges.map((change: TodoChange, idx: number) => { - let icon - let changeText - let textColor = "var(--vscode-foreground)" - - switch (change.type) { - case "added": - icon = ( - - ) - changeText = t("chat:fileOperations.todoChanges.added", { content: change.todo.content }) - textColor = "var(--vscode-charts-green)" - break - case "completed": - icon = ( - - ) - changeText = t("chat:fileOperations.todoChanges.completed", { - content: change.todo.content, - }) - textColor = "var(--vscode-charts-green)" - break - case "started": - icon = ( - - ) - changeText = t("chat:fileOperations.todoChanges.started", { content: change.todo.content }) - textColor = "var(--vscode-charts-yellow)" - break - case "removed": - icon = ( - - ) - changeText = t("chat:fileOperations.todoChanges.removed", { content: change.todo.content }) - textColor = "var(--vscode-descriptionForeground)" - break - case "reordered": - icon = ( - - ↕ - - ) - changeText = t("chat:fileOperations.todoChanges.reordered", { - content: change.todo.content, - }) - textColor = "var(--vscode-descriptionForeground)" - break - case "reverted": - icon = ( - - ) - changeText = t("chat:fileOperations.todoChanges.reverted", { content: change.todo.content }) - textColor = "var(--vscode-charts-orange)" - break - default: - return null - } - - return ( -
  • - {icon} - - {changeText} - -
  • - ) - })} -
- ) - } - if (userEdited) { return ( @@ -450,234 +206,230 @@ const UpdateTodoListToolBlock: React.FC = ({
- {isEditing ? ( - Array.isArray(editTodos) && editTodos.length > 0 ? ( -
    - {editTodos.map((todo, idx) => { - let icon - if (todo.status === "completed") { - icon = ( - 0 ? ( +
      + {editTodos.map((todo, idx) => { + let icon + if (todo.status === "completed") { + icon = ( + + ) + } else if (todo.status === "in_progress") { + icon = ( + + ) + } else { + icon = ( + + ) + } + return ( +
    • + {icon} + {isEditing ? ( + handleContentChange(todo.id!, e.target.value)} style={{ - display: "inline-block", - width: 8, - height: 8, - borderRadius: "50%", - background: "var(--vscode-charts-green)", + flex: 1, + minWidth: 0, + fontWeight: 500, + color: "var(--vscode-input-foreground)", + background: "var(--vscode-input-background)", + border: "none", + outline: "none", + fontSize: 13, marginRight: 6, - marginTop: 7, - flexShrink: 0, + padding: "1px 3px", + borderBottom: "1px solid var(--vscode-input-border)", + }} + onBlur={(e) => { + if (!e.target.value.trim()) { + handleDelete(todo.id!) + } }} /> - ) - } else if (todo.status === "in_progress") { - icon = ( + ) : ( - ) - } else { - icon = ( - + {todo.content} + + )} + {isEditing && ( + + )} + {isEditing && ( + - )} -
    • - ) - })} - {adding ? ( -
    • - - setNewContent(e.target.value)} - onKeyDown={handleNewInputKeyDown} - style={{ - flex: 1, - minWidth: 0, - fontWeight: 500, - color: "var(--vscode-foreground)", - background: "transparent", - border: "none", - outline: "none", - fontSize: 13, - marginRight: 6, - padding: "1px 3px", - borderBottom: "1px solid #eee", - }} - /> - + title="Remove"> + × + + )} +
    • + ) + })} + {adding ? ( +
    • + + setNewContent(e.target.value)} + onKeyDown={handleNewInputKeyDown} + style={{ + flex: 1, + minWidth: 0, + fontWeight: 500, + color: "var(--vscode-foreground)", + background: "transparent", + border: "none", + outline: "none", + fontSize: 13, + marginRight: 6, + padding: "1px 3px", + borderBottom: "1px solid #eee", + }} + /> + + +
    • + ) : ( +
    • + {isEditing && ( -
    • - ) : ( -
    • - {isEditing && ( - - )} -
    • - )} -
    - ) : ( - - ) + )} + + )} +
) : ( - renderChanges() + )}
{/* Delete confirmation dialog */} diff --git a/webview-ui/src/components/chat/__tests__/UpdateTodoListToolBlock.spec.tsx b/webview-ui/src/components/chat/__tests__/UpdateTodoListToolBlock.spec.tsx deleted file mode 100644 index 7c5143a346..0000000000 --- a/webview-ui/src/components/chat/__tests__/UpdateTodoListToolBlock.spec.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { render, screen } from "@testing-library/react" -import { describe, it, expect, vi } from "vitest" -import UpdateTodoListToolBlock from "../UpdateTodoListToolBlock" - -// Mock react-i18next -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ - t: (key: string, options?: { content?: string }) => { - const translations: Record = { - "chat:fileOperations.todoChanges.noChanges": "No changes", - "chat:fileOperations.todoChanges.added": `Added: ${options?.content || ""}`, - "chat:fileOperations.todoChanges.completed": `Completed: ${options?.content || ""}`, - "chat:fileOperations.todoChanges.started": `Started: ${options?.content || ""}`, - "chat:fileOperations.todoChanges.removed": `Removed: ${options?.content || ""}`, - "chat:fileOperations.todoChanges.reordered": `Reordered: ${options?.content || ""}`, - "chat:fileOperations.todoChanges.reverted": `Reverted: ${options?.content || ""}`, - "chat:fileOperations.todoListUpdated": "Todo list updated", - } - return translations[key] || key - }, - }), - initReactI18next: { - type: "3rdParty", - init: vi.fn(), - }, -})) - -describe("UpdateTodoListToolBlock", () => { - const mockOnChange = vi.fn() - - beforeEach(() => { - vi.clearAllMocks() - }) - - it("should show added todos when no previous todos", () => { - const todos = [{ id: "1", content: "New task", status: "" }] - - render() - - expect(screen.getByText("Added: New task")).toBeInTheDocument() - }) - - it("should show completed todos", () => { - const previousTodos = [{ id: "1", content: "Task 1", status: "" }] - const todos = [{ id: "1", content: "Task 1", status: "completed" }] - - render() - - expect(screen.getByText("Completed: Task 1")).toBeInTheDocument() - }) - - it("should show started todos", () => { - const previousTodos = [{ id: "1", content: "Task 1", status: "" }] - const todos = [{ id: "1", content: "Task 1", status: "in_progress" }] - - render() - - expect(screen.getByText("Started: Task 1")).toBeInTheDocument() - }) - - it("should show removed todos", () => { - const previousTodos = [ - { id: "1", content: "Task 1", status: "" }, - { id: "2", content: "Task 2", status: "" }, - ] - const todos = [{ id: "1", content: "Task 1", status: "" }] - - render() - - expect(screen.getByText("Removed: Task 2")).toBeInTheDocument() - }) - - it("should show no changes when todos are identical", () => { - const todos = [{ id: "1", content: "Task 1", status: "" }] - - render() - - expect(screen.getByText("No changes")).toBeInTheDocument() - }) - - it("should show multiple changes", () => { - const previousTodos = [ - { id: "1", content: "Task 1", status: "" }, - { id: "2", content: "Task 2", status: "in_progress" }, - { id: "3", content: "Task 3", status: "" }, - ] - const todos = [ - { id: "1", content: "Task 1", status: "completed" }, - { id: "2", content: "Task 2", status: "completed" }, - { id: "4", content: "New Task", status: "" }, - ] - - render() - - expect(screen.getByText("Completed: Task 1")).toBeInTheDocument() - expect(screen.getByText("Completed: Task 2")).toBeInTheDocument() - expect(screen.getByText("Removed: Task 3")).toBeInTheDocument() - expect(screen.getByText("Added: New Task")).toBeInTheDocument() - }) - - it("should show reordered todos when position changes", () => { - const previousTodos = [ - { id: "1", content: "Task 1", status: "" }, - { id: "2", content: "Task 2", status: "" }, - { id: "3", content: "Task 3", status: "" }, - ] - const todos = [ - { id: "3", content: "Task 3", status: "" }, - { id: "1", content: "Task 1", status: "" }, - { id: "2", content: "Task 2", status: "" }, - ] - - render() - - expect(screen.getByText("Reordered: Task 3")).toBeInTheDocument() - expect(screen.getByText("Reordered: Task 1")).toBeInTheDocument() - expect(screen.getByText("Reordered: Task 2")).toBeInTheDocument() - }) - - it("should show reverted when todo goes from completed to pending", () => { - const previousTodos = [{ id: "1", content: "Task 1", status: "completed" }] - const todos = [{ id: "1", content: "Task 1", status: "" }] - - render() - - expect(screen.getByText("Reverted: Task 1")).toBeInTheDocument() - }) - - it("should show reverted when todo goes from in_progress to pending", () => { - const previousTodos = [{ id: "1", content: "Task 1", status: "in_progress" }] - const todos = [{ id: "1", content: "Task 1", status: "" }] - - render() - - expect(screen.getByText("Reverted: Task 1")).toBeInTheDocument() - }) - - it("should show reverted when todo goes from completed to in_progress", () => { - const previousTodos = [{ id: "1", content: "Task 1", status: "completed" }] - const todos = [{ id: "1", content: "Task 1", status: "in_progress" }] - - render() - - expect(screen.getByText("Reverted: Task 1")).toBeInTheDocument() - }) - - it("should prioritize status change over reordering", () => { - const previousTodos = [ - { id: "1", content: "Task 1", status: "" }, - { id: "2", content: "Task 2", status: "" }, - ] - const todos = [ - { id: "2", content: "Task 2", status: "completed" }, - { id: "1", content: "Task 1", status: "" }, - ] - - render() - - // Should show completed status change, not reordering - expect(screen.getByText("Completed: Task 2")).toBeInTheDocument() - expect(screen.getByText("Reordered: Task 1")).toBeInTheDocument() - // Task 2 should not show as reordered since it has a status change - expect(screen.queryByText("Reordered: Task 2")).not.toBeInTheDocument() - }) - - it("should handle mixed changes correctly", () => { - const previousTodos = [ - { id: "1", content: "Task 1", status: "completed" }, - { id: "2", content: "Task 2", status: "" }, - { id: "3", content: "Task 3", status: "in_progress" }, - { id: "4", content: "Task 4", status: "" }, - ] - const todos = [ - { id: "4", content: "Task 4", status: "" }, // reordered (3→0) - { id: "2", content: "Task 2", status: "completed" }, // completed - { id: "1", content: "Task 1", status: "" }, // reverted - { id: "5", content: "New Task", status: "" }, // added - ] - - render() - - expect(screen.getByText("Added: New Task")).toBeInTheDocument() - expect(screen.getByText("Removed: Task 3")).toBeInTheDocument() - expect(screen.getByText("Completed: Task 2")).toBeInTheDocument() - expect(screen.getByText("Reverted: Task 1")).toBeInTheDocument() - expect(screen.getByText("Reordered: Task 4")).toBeInTheDocument() - }) -}) diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index b625224fe9..1dd892a39f 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "Roo vol afegir contingut al final d'aquest fitxer:", "wantsToReadAndXMore": "En Roo vol llegir aquest fitxer i {{count}} més:", "wantsToReadMultiple": "Roo vol llegir diversos fitxers:", - "wantsToApplyBatchChanges": "Roo vol aplicar canvis a múltiples fitxers:", - "todoListUpdated": "Llista de tasques actualitzada", - "todoChanges": { - "noChanges": "No hi ha canvis a la llista de tasques", - "added": "Afegit: {{content}}", - "completed": "Completat: {{content}}", - "started": "Iniciat: {{content}}", - "removed": "Eliminat: {{content}}", - "reordered": "Reordenat: {{content}}", - "reverted": "Revertit: {{content}}" - } + "wantsToApplyBatchChanges": "Roo vol aplicar canvis a múltiples fitxers:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo vol veure els fitxers de nivell superior en aquest directori:", diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index 943e4f627b..c62fe9d3bb 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -169,17 +169,7 @@ "wantsToInsertWithLineNumber": "Roo möchte Inhalte in diese Datei in Zeile {{lineNumber}} einfügen:", "wantsToInsertAtEnd": "Roo möchte Inhalte am Ende dieser Datei anhängen:", "wantsToReadMultiple": "Roo möchte mehrere Dateien lesen:", - "wantsToApplyBatchChanges": "Roo möchte Änderungen an mehreren Dateien vornehmen:", - "todoListUpdated": "Todo-Liste aktualisiert", - "todoChanges": { - "noChanges": "Keine Änderungen", - "added": "Hinzugefügt: {{content}}", - "completed": "Abgeschlossen: {{content}}", - "started": "Begonnen: {{content}}", - "removed": "Entfernt: {{content}}", - "reordered": "Neu angeordnet: {{content}}", - "reverted": "Rückgängig gemacht: {{content}}" - } + "wantsToApplyBatchChanges": "Roo möchte Änderungen an mehreren Dateien vornehmen:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo möchte die Dateien auf oberster Ebene in diesem Verzeichnis anzeigen:", diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index f0b3e7c545..ea4f8920f5 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -178,17 +178,7 @@ "didSearchReplace": "Roo performed search and replace on this file:", "wantsToInsert": "Roo wants to insert content into this file:", "wantsToInsertWithLineNumber": "Roo wants to insert content into this file at line {{lineNumber}}:", - "wantsToInsertAtEnd": "Roo wants to append content to the end of this file:", - "todoListUpdated": "Todo list updated", - "todoChanges": { - "noChanges": "No changes", - "added": "Added: {{content}}", - "completed": "Completed: {{content}}", - "started": "Started: {{content}}", - "removed": "Removed: {{content}}", - "reordered": "Reordered: {{content}}", - "reverted": "Reverted: {{content}}" - } + "wantsToInsertAtEnd": "Roo wants to append content to the end of this file:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo wants to view the top level files in this directory:", diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index 56ecf62d65..a4349b13dc 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "Roo quiere añadir contenido al final de este archivo:", "wantsToReadAndXMore": "Roo quiere leer este archivo y {{count}} más:", "wantsToReadMultiple": "Roo quiere leer varios archivos:", - "wantsToApplyBatchChanges": "Roo quiere aplicar cambios a múltiples archivos:", - "todoListUpdated": "Lista de tareas actualizada", - "todoChanges": { - "noChanges": "Sin cambios", - "added": "Añadido: {{content}}", - "completed": "Completado: {{content}}", - "started": "Iniciado: {{content}}", - "removed": "Eliminado: {{content}}", - "reordered": "Reordenado: {{content}}", - "reverted": "Revertido: {{content}}" - } + "wantsToApplyBatchChanges": "Roo quiere aplicar cambios a múltiples archivos:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo quiere ver los archivos de nivel superior en este directorio:", diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 836efffd89..b5f06354bb 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -166,17 +166,7 @@ "wantsToInsertAtEnd": "Roo veut ajouter du contenu à la fin de ce fichier :", "wantsToReadAndXMore": "Roo veut lire ce fichier et {{count}} de plus :", "wantsToReadMultiple": "Roo souhaite lire plusieurs fichiers :", - "wantsToApplyBatchChanges": "Roo veut appliquer des modifications à plusieurs fichiers :", - "todoListUpdated": "Liste de tâches mise à jour", - "todoChanges": { - "noChanges": "Aucun changement", - "added": "Ajouté : {{content}}", - "completed": "Terminé : {{content}}", - "started": "Commencé : {{content}}", - "removed": "Supprimé : {{content}}", - "reordered": "Réorganisé : {{content}}", - "reverted": "Annulé : {{content}}" - } + "wantsToApplyBatchChanges": "Roo veut appliquer des modifications à plusieurs fichiers :" }, "instructions": { "wantsToFetch": "Roo veut récupérer des instructions détaillées pour aider à la tâche actuelle" diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index e9ad22fd87..9afdcbdd38 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "Roo इस फ़ाइल के अंत में सामग्री जोड़ना चाहता है:", "wantsToReadAndXMore": "रू इस फ़ाइल को और {{count}} अन्य को पढ़ना चाहता है:", "wantsToReadMultiple": "Roo कई फ़ाइलें पढ़ना चाहता है:", - "wantsToApplyBatchChanges": "Roo कई फ़ाइलों में परिवर्तन लागू करना चाहता है:", - "todoListUpdated": "कार्य सूची अपडेट की गई", - "todoChanges": { - "noChanges": "कार्य सूची में कोई परिवर्तन नहीं", - "added": "जोड़ा गया: {{content}}", - "completed": "पूर्ण: {{content}}", - "started": "शुरू किया गया: {{content}}", - "removed": "हटाया गया: {{content}}", - "reordered": "पुनः क्रमबद्ध: {{content}}", - "reverted": "वापस किया गया: {{content}}" - } + "wantsToApplyBatchChanges": "Roo कई फ़ाइलों में परिवर्तन लागू करना चाहता है:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo इस निर्देशिका में शीर्ष स्तर की फ़ाइलें देखना चाहता है:", diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index 0a103afbbe..fc0bd5056f 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -179,16 +179,6 @@ "wantsToEditOutsideWorkspace": "Roo ingin mengedit file ini di luar workspace:", "wantsToEditProtected": "Roo ingin mengedit file konfigurasi yang dilindungi:", "wantsToApplyBatchChanges": "Roo ingin menerapkan perubahan ke beberapa file:", - "todoListUpdated": "Daftar tugas diperbarui", - "todoChanges": { - "noChanges": "Tidak ada perubahan pada daftar tugas", - "added": "Ditambahkan: {{content}}", - "completed": "Selesai: {{content}}", - "started": "Dimulai: {{content}}", - "removed": "Dihapus: {{content}}", - "reordered": "Diurutkan ulang: {{content}}", - "reverted": "Dikembalikan: {{content}}" - }, "wantsToCreate": "Roo ingin membuat file baru:", "wantsToSearchReplace": "Roo ingin mencari dan mengganti di file ini:", "didSearchReplace": "Roo melakukan pencarian dan penggantian pada file ini:", diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index fd8c258505..f49a25dfa6 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "Roo vuole aggiungere contenuto alla fine di questo file:", "wantsToReadAndXMore": "Roo vuole leggere questo file e altri {{count}}:", "wantsToReadMultiple": "Roo vuole leggere più file:", - "wantsToApplyBatchChanges": "Roo vuole applicare modifiche a più file:", - "todoListUpdated": "Lista todo aggiornata", - "todoChanges": { - "noChanges": "Nessuna modifica alla lista todo", - "added": "Aggiunto: {{content}}", - "completed": "Completato: {{content}}", - "started": "Iniziato: {{content}}", - "removed": "Rimosso: {{content}}", - "reordered": "Riordinato: {{content}}", - "reverted": "Ripristinato: {{content}}" - } + "wantsToApplyBatchChanges": "Roo vuole applicare modifiche a più file:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo vuole visualizzare i file di primo livello in questa directory:", diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index 469f5cefe0..cb5ebcdafd 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "Rooはこのファイルの末尾にコンテンツを追加したい:", "wantsToReadAndXMore": "Roo はこのファイルと他に {{count}} 個のファイルを読み込もうとしています:", "wantsToReadMultiple": "Rooは複数のファイルを読み取ろうとしています:", - "wantsToApplyBatchChanges": "Rooは複数のファイルに変更を適用したい:", - "todoListUpdated": "Todoリストが更新されました", - "todoChanges": { - "noChanges": "Todoリストに変更はありません", - "added": "追加: {{content}}", - "completed": "完了: {{content}}", - "started": "開始: {{content}}", - "removed": "削除: {{content}}", - "reordered": "並び替え: {{content}}", - "reverted": "元に戻す: {{content}}" - } + "wantsToApplyBatchChanges": "Rooは複数のファイルに変更を適用したい:" }, "directoryOperations": { "wantsToViewTopLevel": "Rooはこのディレクトリのトップレベルファイルを表示したい:", diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index a1a6c9c1fe..1f86dc8cf4 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "Roo가 이 파일의 끝에 내용을 추가하고 싶어합니다:", "wantsToReadAndXMore": "Roo가 이 파일과 {{count}}개의 파일을 더 읽으려고 합니다:", "wantsToReadMultiple": "Roo가 여러 파일을 읽으려고 합니다:", - "wantsToApplyBatchChanges": "Roo가 여러 파일에 변경 사항을 적용하고 싶어합니다:", - "todoListUpdated": "할 일 목록이 업데이트되었습니다", - "todoChanges": { - "noChanges": "할 일 목록에 변경 사항이 없습니다", - "added": "추가됨: {{content}}", - "completed": "완료됨: {{content}}", - "started": "시작됨: {{content}}", - "removed": "제거됨: {{content}}", - "reordered": "재정렬됨: {{content}}", - "reverted": "되돌림: {{content}}" - } + "wantsToApplyBatchChanges": "Roo가 여러 파일에 변경 사항을 적용하고 싶어합니다:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo가 이 디렉토리의 최상위 파일을 보고 싶어합니다:", diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index c384943129..d228d7b0c2 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -164,17 +164,7 @@ "wantsToInsertAtEnd": "Roo wil inhoud toevoegen aan het einde van dit bestand:", "wantsToReadAndXMore": "Roo wil dit bestand en nog {{count}} andere lezen:", "wantsToReadMultiple": "Roo wil meerdere bestanden lezen:", - "wantsToApplyBatchChanges": "Roo wil wijzigingen toepassen op meerdere bestanden:", - "todoListUpdated": "Todo-lijst bijgewerkt", - "todoChanges": { - "noChanges": "Geen wijzigingen in de todo-lijst", - "added": "Toegevoegd: {{content}}", - "completed": "Voltooid: {{content}}", - "started": "Gestart: {{content}}", - "removed": "Verwijderd: {{content}}", - "reordered": "Hergerangschikt: {{content}}", - "reverted": "Teruggedraaid: {{content}}" - } + "wantsToApplyBatchChanges": "Roo wil wijzigingen toepassen op meerdere bestanden:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo wil de bovenliggende bestanden in deze map bekijken:", diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index 7f50956c7b..fdb39b0851 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "Roo chce dodać zawartość na końcu tego pliku:", "wantsToReadAndXMore": "Roo chce przeczytać ten plik i {{count}} więcej:", "wantsToReadMultiple": "Roo chce odczytać wiele plików:", - "wantsToApplyBatchChanges": "Roo chce zastosować zmiany do wielu plików:", - "todoListUpdated": "Lista zadań zaktualizowana", - "todoChanges": { - "noChanges": "Brak zmian w liście zadań", - "added": "Dodano: {{content}}", - "completed": "Ukończono: {{content}}", - "started": "Rozpoczęto: {{content}}", - "removed": "Usunięto: {{content}}", - "reordered": "Zmieniono kolejność: {{content}}", - "reverted": "Cofnięto: {{content}}" - } + "wantsToApplyBatchChanges": "Roo chce zastosować zmiany do wielu plików:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo chce zobaczyć pliki najwyższego poziomu w tym katalogu:", diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index f60335d255..b37879f2f0 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "Roo quer adicionar conteúdo ao final deste arquivo:", "wantsToReadAndXMore": "Roo quer ler este arquivo e mais {{count}}:", "wantsToReadMultiple": "Roo deseja ler múltiplos arquivos:", - "wantsToApplyBatchChanges": "Roo quer aplicar alterações a múltiplos arquivos:", - "todoListUpdated": "Lista de tarefas atualizada", - "todoChanges": { - "noChanges": "Nenhuma alteração na lista de tarefas", - "added": "Adicionado: {{content}}", - "completed": "Concluído: {{content}}", - "started": "Iniciado: {{content}}", - "removed": "Removido: {{content}}", - "reordered": "Reordenado: {{content}}", - "reverted": "Revertido: {{content}}" - } + "wantsToApplyBatchChanges": "Roo quer aplicar alterações a múltiplos arquivos:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo quer visualizar os arquivos de nível superior neste diretório:", diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 8642560fc4..5865e41a53 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -164,17 +164,7 @@ "wantsToInsertAtEnd": "Roo хочет добавить содержимое в конец этого файла:", "wantsToReadAndXMore": "Roo хочет прочитать этот файл и еще {{count}}:", "wantsToReadMultiple": "Roo хочет прочитать несколько файлов:", - "wantsToApplyBatchChanges": "Roo хочет применить изменения к нескольким файлам:", - "todoListUpdated": "Список задач обновлен", - "todoChanges": { - "noChanges": "Нет изменений в списке задач", - "added": "Добавлено: {{content}}", - "completed": "Завершено: {{content}}", - "started": "Начато: {{content}}", - "removed": "Удалено: {{content}}", - "reordered": "Переупорядочено: {{content}}", - "reverted": "Отменено: {{content}}" - } + "wantsToApplyBatchChanges": "Roo хочет применить изменения к нескольким файлам:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo хочет просмотреть файлы верхнего уровня в этой директории:", diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 14143878d8..6c5bd20353 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "Roo bu dosyanın sonuna içerik eklemek istiyor:", "wantsToReadAndXMore": "Roo bu dosyayı ve {{count}} tane daha okumak istiyor:", "wantsToReadMultiple": "Roo birden fazla dosya okumak istiyor:", - "wantsToApplyBatchChanges": "Roo birden fazla dosyaya değişiklik uygulamak istiyor:", - "todoListUpdated": "Yapılacaklar listesi güncellendi", - "todoChanges": { - "noChanges": "Yapılacaklar listesinde değişiklik yok", - "added": "Eklendi: {{content}}", - "completed": "Tamamlandı: {{content}}", - "started": "Başlatıldı: {{content}}", - "removed": "Kaldırıldı: {{content}}", - "reordered": "Yeniden sıralandı: {{content}}", - "reverted": "Geri alındı: {{content}}" - } + "wantsToApplyBatchChanges": "Roo birden fazla dosyaya değişiklik uygulamak istiyor:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo bu dizindeki üst düzey dosyaları görüntülemek istiyor:", diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index c9ad329ca9..eb7cdc2306 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "Roo muốn thêm nội dung vào cuối tệp này:", "wantsToReadAndXMore": "Roo muốn đọc tệp này và {{count}} tệp khác:", "wantsToReadMultiple": "Roo muốn đọc nhiều tệp:", - "wantsToApplyBatchChanges": "Roo muốn áp dụng thay đổi cho nhiều tệp:", - "todoListUpdated": "Danh sách việc cần làm đã được cập nhật", - "todoChanges": { - "noChanges": "Không có thay đổi trong danh sách việc cần làm", - "added": "Đã thêm: {{content}}", - "completed": "Đã hoàn thành: {{content}}", - "started": "Đã bắt đầu: {{content}}", - "removed": "Đã xóa: {{content}}", - "reordered": "Đã sắp xếp lại: {{content}}", - "reverted": "Đã hoàn nguyên: {{content}}" - } + "wantsToApplyBatchChanges": "Roo muốn áp dụng thay đổi cho nhiều tệp:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo muốn xem các tệp cấp cao nhất trong thư mục này:", diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index e06d836f8d..93494e6f50 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "需要在文件末尾添加内容:", "wantsToReadAndXMore": "Roo 想读取此文件以及另外 {{count}} 个文件:", "wantsToReadMultiple": "Roo 想要读取多个文件:", - "wantsToApplyBatchChanges": "Roo 想要对多个文件应用更改:", - "todoListUpdated": "待办列表已更新", - "todoChanges": { - "noChanges": "无变更", - "added": "新增: {{content}}", - "completed": "已完成: {{content}}", - "started": "已开始: {{content}}", - "removed": "已移除: {{content}}", - "reordered": "已重新排序: {{content}}", - "reverted": "已还原: {{content}}" - } + "wantsToApplyBatchChanges": "Roo 想要对多个文件应用更改:" }, "directoryOperations": { "wantsToViewTopLevel": "需要查看目录文件列表:", diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 27352b3f9e..e7a476cb37 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -169,17 +169,7 @@ "wantsToInsertAtEnd": "Roo 想要在此檔案末尾新增內容:", "wantsToReadAndXMore": "Roo 想要讀取此檔案以及另外 {{count}} 個檔案:", "wantsToReadMultiple": "Roo 想要讀取多個檔案:", - "wantsToApplyBatchChanges": "Roo 想要對多個檔案套用變更:", - "todoListUpdated": "待辦清單已更新", - "todoChanges": { - "noChanges": "待辦清單無變更", - "added": "已新增:{{content}}", - "completed": "已完成:{{content}}", - "started": "已開始:{{content}}", - "removed": "已移除:{{content}}", - "reordered": "已重新排序:{{content}}", - "reverted": "已還原:{{content}}" - } + "wantsToApplyBatchChanges": "Roo 想要對多個檔案套用變更:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo 想要檢視此目錄中最上層的檔案:", From 276daaf40cc5e5fc6a83a3b99fda39f549614518 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 7 Jul 2025 13:10:53 -0400 Subject: [PATCH 09/12] Remove cancelled status --- packages/types/src/todo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/todo.ts b/packages/types/src/todo.ts index 9400b5e7bd..8af51cbbea 100644 --- a/packages/types/src/todo.ts +++ b/packages/types/src/todo.ts @@ -3,7 +3,7 @@ import { z } from "zod" /** * TodoStatus */ -export const todoStatusSchema = z.enum(["pending", "in_progress", "completed", "cancelled"] as const) +export const todoStatusSchema = z.enum(["pending", "in_progress", "completed"] as const) export type TodoStatus = z.infer From 15a4bddc6d866074537e3c43bef72c8f2bc8e4a0 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 7 Jul 2025 13:17:18 -0400 Subject: [PATCH 10/12] Use type --- src/core/environment/reminder.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/environment/reminder.ts b/src/core/environment/reminder.ts index fc188c1bc3..eb1b39dfb5 100644 --- a/src/core/environment/reminder.ts +++ b/src/core/environment/reminder.ts @@ -1,4 +1,4 @@ -import { TodoItem } from "@roo-code/types" +import { TodoItem, TodoStatus } from "@roo-code/types" /** * Format the reminders section as a markdown block in English, with basic instructions. @@ -7,7 +7,7 @@ export function formatReminderSection(todoList?: TodoItem[]): string { if (!todoList || todoList.length === 0) { return "" } - const statusMap: Record = { + const statusMap: Record = { pending: "Pending", in_progress: "In Progress", completed: "Completed", From bb218b631ee4208cb59aa4ebc60bbe34cfce47e1 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 7 Jul 2025 13:30:32 -0400 Subject: [PATCH 11/12] Fixes --- packages/types/src/todo.ts | 2 +- src/core/tools/updateTodoListTool.ts | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/types/src/todo.ts b/packages/types/src/todo.ts index 8af51cbbea..4e874e1750 100644 --- a/packages/types/src/todo.ts +++ b/packages/types/src/todo.ts @@ -11,7 +11,7 @@ export type TodoStatus = z.infer * TodoItem */ export const todoItemSchema = z.object({ - id: z.string().optional(), + id: z.string(), content: z.string(), status: todoStatusSchema, }) diff --git a/src/core/tools/updateTodoListTool.ts b/src/core/tools/updateTodoListTool.ts index 7a196cb33c..cbb90338d3 100644 --- a/src/core/tools/updateTodoListTool.ts +++ b/src/core/tools/updateTodoListTool.ts @@ -4,10 +4,9 @@ import { formatResponse } from "../prompts/responses" import cloneDeep from "clone-deep" import crypto from "crypto" -import { TodoItem, TodoStatus } from "@roo-code/types" +import { TodoItem, TodoStatus, todoStatusSchema } from "@roo-code/types" import { getLatestTodo } from "../../shared/todo" -const VALID_STATUS: string[] = ["pending", "in_progress", "completed", "todo", "doing", "done"] let approvedTodoList: TodoItem[] | undefined = undefined /** @@ -96,8 +95,8 @@ function todoListToMarkdown(todos: TodoItem[]): string { } function normalizeStatus(status: string | undefined): TodoStatus { - if (status === "completed" || status === "done") return "completed" - if (status === "in_progress" || status === "doing" || status === "working") return "in_progress" + if (status === "completed") return "completed" + if (status === "in_progress") return "in_progress" return "pending" } @@ -138,7 +137,7 @@ function validateTodos(todos: any[]): { valid: boolean; error?: string } { if (!t.id || typeof t.id !== "string") return { valid: false, error: `Item ${i + 1} is missing id` } if (!t.content || typeof t.content !== "string") return { valid: false, error: `Item ${i + 1} is missing content` } - if (t.status && !VALID_STATUS.includes(t.status)) + if (t.status && !todoStatusSchema.options.includes(t.status as TodoStatus)) return { valid: false, error: `Item ${i + 1} has invalid status` } } return { valid: true } @@ -152,7 +151,7 @@ function validateTodos(todos: any[]): { valid: boolean; error?: string } { * @param handleError HandleError function * @param pushToolResult PushToolResult function * @param removeClosingTag RemoveClosingTag function - * @param userEdited If true, only show "User Edit Succee" and do nothing else + * @param userEdited If true, only show "User Edit Succeeded" and do nothing else */ export async function updateTodoListTool( cline: Task, @@ -163,7 +162,7 @@ export async function updateTodoListTool( removeClosingTag: RemoveClosingTag, userEdited?: boolean, ) { - // If userEdited is true, only show "User Edit Succee" and do nothing else + // If userEdited is true, only show "User Edit Succeeded" and do nothing else if (userEdited === true) { pushToolResult("User Edit Succeeded") return From 2e1d5230862dbff819390da06220b77332ce9ca9 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 7 Jul 2025 13:37:13 -0400 Subject: [PATCH 12/12] Missing string --- webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx index 2ca0607302..e851284505 100644 --- a/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx +++ b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx @@ -1,7 +1,6 @@ import React, { useState, useEffect, useRef } from "react" import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock" import MarkdownBlock from "../common/MarkdownBlock" -import { useTranslation } from "react-i18next" interface TodoItem { id?: string @@ -54,7 +53,6 @@ const UpdateTodoListToolBlock: React.FC = ({ editable = true, userEdited = false, }) => { - const { t } = useTranslation() const [editTodos, setEditTodos] = useState( todos.length > 0 ? todos.map((todo) => ({ ...todo, id: todo.id || genId() })) : [], ) @@ -429,7 +427,7 @@ const UpdateTodoListToolBlock: React.FC = ({ )} ) : ( - + )}
{/* Delete confirmation dialog */}