chore: remove debug logging from PR #10765

- Remove 63 [TODO-DEBUG] console.log statements across 8 files
- Remove file-based debug log mechanism (ROO_CLI_DEBUG_LOG_PATH)
- Replace console.error in Task.ts with TelemetryService

Files modified:
- src/core/tools/UpdateTodoListTool.ts
- src/core/assistant-message/presentAssistantMessage.ts
- src/core/task/Task.ts
- src/shared/todo.ts
- webview-ui/src/components/chat/ChatRow.tsx
- webview-ui/src/components/chat/ChatView.tsx
- webview-ui/src/components/chat/TodoChangeDisplay.tsx
- webview-ui/src/components/chat/TodoListDisplay.tsx
This commit is contained in:
Toray Altas 2026-01-20 19:19:29 -05:00
parent 4be568874a
commit 249737dbf0
8 changed files with 15 additions and 554 deletions

View file

@ -881,11 +881,6 @@ export async function presentAssistantMessage(cline: Task) {
})
break
case "update_todo_list":
console.log("[TODO-DEBUG]", "presentAssistantMessage dispatching update_todo_list", {
toolUseId: (block as any).id,
partial: block.partial,
params: (block as any).params,
})
await updateTodoListTool.handle(cline, block as ToolUse<"update_todo_list">, {
askApproval,
handleError,

View file

@ -3286,7 +3286,14 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// IMPORTANT: keep a reference so completion/delegation can await final persisted usage/cost.
this.pendingUsageCollectionPromise = drainStreamInBackgroundToFindAllUsage(lastApiReqIndex)
.catch((error) => {
console.error("Background usage collection failed:", error)
const err = error instanceof Error ? error : new Error("Background usage collection failed")
TelemetryService.instance.captureException(err, {
location: "Task.pendingUsageCollectionPromise",
taskId: this.taskId,
instanceId: this.instanceId,
lastApiReqIndex,
originalError: error instanceof Error ? undefined : serializeError(error),
})
})
.finally(() => {
if (this.pendingUsageCollectionPromise) {

View file

@ -3,9 +3,6 @@ import { formatResponse } from "../prompts/responses"
import { BaseTool, ToolCallbacks } from "./BaseTool"
import type { ToolUse } from "../../shared/tools"
import crypto from "crypto"
import fs from "fs"
import os from "os"
import path from "path"
import { TodoItem, TodoStatus, todoStatusSchema } from "@roo-code/types"
import { getLatestTodo } from "../../shared/todo"
@ -25,54 +22,12 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> {
}
async execute(params: UpdateTodoListParams, task: Task, callbacks: ToolCallbacks): Promise<void> {
console.log("[TODO-DEBUG] execute() STEP 0: ENTERED", {
tool: "update_todo_list",
paramsTodosType: typeof params?.todos,
paramsTodosLength: typeof params?.todos === "string" ? params.todos.length : undefined,
})
const { pushToolResult, handleError, askApproval, toolProtocol } = callbacks
const { pushToolResult, handleError, askApproval } = callbacks
try {
const summarizeTodoForDebug = (t: TodoItem | undefined) => {
if (!t) return undefined
return {
id: typeof t.id === "string" ? t.id : undefined,
status: typeof t.status === "string" ? t.status : undefined,
content: typeof t.content === "string" ? t.content.slice(0, 120) : undefined,
subtaskId: typeof t.subtaskId === "string" ? t.subtaskId : undefined,
tokens: typeof t.tokens === "number" ? t.tokens : undefined,
cost: typeof t.cost === "number" ? t.cost : undefined,
added: typeof t.added === "number" ? t.added : undefined,
removed: typeof t.removed === "number" ? t.removed : undefined,
}
}
const shouldTodoDebugLog =
process.env.ROO_DEBUG_TODO_METADATA === "1" ||
process.env.ROO_DEBUG_TODO_METADATA === "true" ||
process.env.ROO_CLI_DEBUG_LOG === "1"
console.log("[TODO-DEBUG] execute() STEP 1: computed debug flags", {
shouldTodoDebugLog,
ROO_DEBUG_TODO_METADATA: process.env.ROO_DEBUG_TODO_METADATA,
ROO_CLI_DEBUG_LOG: process.env.ROO_CLI_DEBUG_LOG,
toolProtocol,
})
const previousFromMemory = getTodoListForTask(task)
console.log("[TODO-DEBUG] execute() STEP 2: previous todos from memory", {
previousFromMemoryCount: Array.isArray(previousFromMemory) ? previousFromMemory.length : 0,
previousFromMemoryPreview: Array.isArray(previousFromMemory)
? previousFromMemory.slice(0, 10).map((t) => summarizeTodoForDebug(t))
: undefined,
})
const previousFromHistory = getLatestTodo(task.clineMessages) as unknown as TodoItem[] | undefined
console.log("[TODO-DEBUG] execute() STEP 3: previous todos from history", {
previousFromHistoryCount: Array.isArray(previousFromHistory) ? previousFromHistory.length : 0,
previousFromHistoryPreview: Array.isArray(previousFromHistory)
? previousFromHistory.slice(0, 10).map((t) => summarizeTodoForDebug(t))
: undefined,
})
const historyHasMetadata =
Array.isArray(previousFromHistory) &&
@ -84,21 +39,6 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> {
t?.added !== undefined ||
t?.removed !== undefined,
)
console.log("[TODO-DEBUG] execute() STEP 4: analyzed history metadata", {
historyHasMetadata,
historyHasSubtaskId: Array.isArray(previousFromHistory)
? previousFromHistory.some((t) => typeof t?.subtaskId === "string")
: false,
historyHasTokens: Array.isArray(previousFromHistory)
? previousFromHistory.some((t) => typeof t?.tokens === "number")
: false,
historyHasCost: Array.isArray(previousFromHistory)
? previousFromHistory.some((t) => typeof t?.cost === "number")
: false,
historyHasLineChanges: Array.isArray(previousFromHistory)
? previousFromHistory.some((t) => typeof t?.added === "number" || typeof t?.removed === "number")
: false,
})
const previousTodos: TodoItem[] =
(previousFromMemory?.length ?? 0) === 0
@ -108,55 +48,14 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> {
: historyHasMetadata
? (previousFromHistory ?? [])
: (previousFromMemory ?? [])
console.log("[TODO-DEBUG] execute() STEP 5: selected previousTodos", {
selectedPreviousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : 0,
selectedPreviousTodosWithSubtaskIdCount: Array.isArray(previousTodos)
? previousTodos.filter((t) => typeof t?.subtaskId === "string").length
: 0,
selectedPreviousTodosPreview: Array.isArray(previousTodos)
? previousTodos.slice(0, 10).map((t) => summarizeTodoForDebug(t))
: undefined,
})
const todosRaw = params.todos
if (shouldTodoDebugLog) {
console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() received params.todos", {
tool: "update_todo_list",
todosRawType: typeof todosRaw,
todosRawLength: typeof todosRaw === "string" ? todosRaw.length : undefined,
todosRawPreview: typeof todosRaw === "string" ? todosRaw.slice(0, 500) : undefined,
})
console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() previousTodos summary", {
previousFromMemoryCount: Array.isArray(previousFromMemory) ? previousFromMemory.length : 0,
previousFromHistoryCount: Array.isArray(previousFromHistory) ? previousFromHistory.length : 0,
historyHasMetadata,
selectedPreviousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : 0,
previousTodosWithSubtaskIdCount: Array.isArray(previousTodos)
? previousTodos.filter((t) => typeof t?.subtaskId === "string").length
: 0,
})
}
let todos: TodoItem[]
const jsonParseResult = tryParseTodoItemsJson(todosRaw)
if (jsonParseResult.parsed) {
todos = jsonParseResult.parsed
console.log("[TODO-DEBUG] execute() STEP 6: parsed todos via JSON", {
parsedCount: todos.length,
parsedPreview: todos.slice(0, 10).map((t) => summarizeTodoForDebug(t)),
})
if (shouldTodoDebugLog) {
console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() parsed todos from JSON", {
parsedCount: todos.length,
hasAnySubtaskId: todos.some((t) => typeof t?.subtaskId === "string"),
subtaskIds: todos.map((t) => t?.subtaskId).filter(Boolean),
})
}
} else if (jsonParseResult.error) {
console.log("[TODO-DEBUG] execute() STEP 6: JSON parse/validate error", {
error: jsonParseResult.error,
todosRawPreview: typeof todosRaw === "string" ? todosRaw.slice(0, 500) : undefined,
})
task.consecutiveMistakeCount++
task.recordToolError("update_todo_list")
task.didToolFailInCurrentTurn = true
@ -165,52 +64,13 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> {
} else {
// Backward compatible: fall back to markdown checklist parsing when JSON parsing is not applicable.
todos = parseMarkdownChecklist(todosRaw || "")
console.log("[TODO-DEBUG] execute() STEP 6: parsed todos via markdown checklist", {
parsedCount: todos.length,
parsedPreview: todos.slice(0, 10).map((t) => summarizeTodoForDebug(t)),
})
if (shouldTodoDebugLog) {
console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() parsed todos from markdown checklist", {
parsedCount: todos.length,
hasAnySubtaskId: todos.some((t) => typeof t?.subtaskId === "string"),
})
}
}
// Preserve metadata (subtaskId/tokens/cost) for todos whose content matches an existing todo.
// Matching is by exact content string; duplicates are matched in order.
// NOTE: Instrumentation is enabled here (once per tool execute) to detect metadata-preservation failures.
console.log("[TODO-DEBUG] execute() STEP 7: about to call preserveTodoMetadata", {
nextTodosCount: Array.isArray(todos) ? todos.length : 0,
previousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : 0,
enableInstrumentation: true,
})
const todosWithPreservedMetadata = preserveTodoMetadata(todos, previousTodos, {
enableInstrumentation: true,
})
console.log("[TODO-DEBUG] execute() STEP 8: returned from preserveTodoMetadata", {
resultCount: todosWithPreservedMetadata.length,
resultPreview: todosWithPreservedMetadata.slice(0, 10).map((t) => summarizeTodoForDebug(t)),
})
if (shouldTodoDebugLog) {
console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() after preserveTodoMetadata()", {
nextTodosCount: todosWithPreservedMetadata.length,
todosWithSubtaskIdCount: todosWithPreservedMetadata.filter((t) => typeof t?.subtaskId === "string")
.length,
subtaskIds: todosWithPreservedMetadata.map((t) => t?.subtaskId).filter(Boolean),
hasAnyTokens: todosWithPreservedMetadata.some((t) => typeof t?.tokens === "number"),
hasAnyCost: todosWithPreservedMetadata.some((t) => typeof t?.cost === "number"),
hasAnyLineChanges: todosWithPreservedMetadata.some(
(t) => typeof t?.added === "number" || typeof t?.removed === "number",
),
})
}
const todosWithPreservedMetadata = preserveTodoMetadata(todos, previousTodos)
const { valid, error } = validateTodos(todosWithPreservedMetadata)
console.log("[TODO-DEBUG] execute() STEP 9: validateTodos", {
valid,
error,
})
if (!valid) {
task.consecutiveMistakeCount++
task.recordToolError("update_todo_list")
@ -229,17 +89,6 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> {
added: t.added,
removed: t.removed,
}))
console.log("[TODO-DEBUG] execute() STEP 10: normalizedTodos (pre-approval)", {
normalizedCount: normalizedTodos.length,
normalizedPreview: normalizedTodos.slice(0, 10).map((t) => summarizeTodoForDebug(t)),
})
if (shouldTodoDebugLog) {
console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() normalizedTodos (pre-approval)", {
normalizedCount: normalizedTodos.length,
todosWithSubtaskIdCount: normalizedTodos.filter((t) => typeof t?.subtaskId === "string").length,
subtaskIds: normalizedTodos.map((t) => t?.subtaskId).filter(Boolean),
})
}
const approvalMsg = JSON.stringify({
tool: "updateTodoList",
@ -248,57 +97,20 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> {
// TodoItem is a flat object shape; a shallow copy is sufficient here.
approvedTodoList = normalizedTodos.map((t) => ({ ...t }))
console.log("[TODO-DEBUG] execute() STEP 11: asking approval", {
approvalPayloadLength: approvalMsg.length,
normalizedCount: normalizedTodos.length,
})
const didApprove = await askApproval("tool", approvalMsg)
console.log("[TODO-DEBUG] execute() STEP 12: approval result", {
didApprove,
})
if (!didApprove) {
console.log("[TODO-DEBUG] execute() STEP 13: user declined; returning", {})
pushToolResult("User declined to update the todoList.")
return
}
const isTodoListChanged =
approvedTodoList !== undefined && JSON.stringify(normalizedTodos) !== JSON.stringify(approvedTodoList)
console.log("[TODO-DEBUG] execute() STEP 14: checked approval UI edits", {
isTodoListChanged,
})
if (isTodoListChanged) {
normalizedTodos = approvedTodoList ?? []
console.log("[TODO-DEBUG] execute() STEP 15: using user-edited todos", {
editedCount: normalizedTodos.length,
editedPreview: normalizedTodos.slice(0, 10).map((t) => summarizeTodoForDebug(t)),
})
if (shouldTodoDebugLog) {
console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() user edited todos in approval UI", {
editedCount: normalizedTodos.length,
todosWithSubtaskIdCount: normalizedTodos.filter((t) => typeof t?.subtaskId === "string").length,
subtaskIds: normalizedTodos.map((t) => t?.subtaskId).filter(Boolean),
})
}
// If the user-edited todo list dropped metadata fields, re-apply metadata preservation against
// the previous list (and keep any explicitly provided metadata in the edited list).
// NOTE: Do not instrument here to avoid double-logging within the same update.
console.log("[TODO-DEBUG] execute() STEP 16: about to re-call preserveTodoMetadata (user-edited)", {
enableInstrumentation: false,
})
normalizedTodos = preserveTodoMetadata(normalizedTodos, previousTodos, { enableInstrumentation: false })
console.log("[TODO-DEBUG] execute() STEP 17: returned from re-preserve (user-edited)", {
normalizedCount: normalizedTodos.length,
normalizedPreview: normalizedTodos.slice(0, 10).map((t) => summarizeTodoForDebug(t)),
})
if (shouldTodoDebugLog) {
console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() normalizedTodos after re-preserve", {
normalizedCount: normalizedTodos.length,
todosWithSubtaskIdCount: normalizedTodos.filter((t) => typeof t?.subtaskId === "string").length,
subtaskIds: normalizedTodos.map((t) => t?.subtaskId).filter(Boolean),
})
}
normalizedTodos = preserveTodoMetadata(normalizedTodos, previousTodos)
task.say(
"user_edit_todos",
@ -309,29 +121,15 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> {
)
}
console.log("[TODO-DEBUG] execute() STEP 18: setting todoList on task", {
finalTodosCount: normalizedTodos.length,
})
await setTodoListForTask(task, normalizedTodos)
console.log("[TODO-DEBUG] execute() STEP 19: setTodoListForTask completed", {
taskTodoListCount: Array.isArray(task?.todoList) ? task.todoList.length : undefined,
})
if (isTodoListChanged) {
const md = todoListToMarkdown(normalizedTodos)
console.log("[TODO-DEBUG] execute() STEP 20: returning tool result (user edits)", {
mdLength: md.length,
})
pushToolResult(formatResponse.toolResult("User edits todo:\n\n" + md))
} else {
console.log("[TODO-DEBUG] execute() STEP 20: returning tool result (no user edits)", {})
pushToolResult(formatResponse.toolResult("Todo list updated successfully."))
}
} catch (error) {
console.log("[TODO-DEBUG] execute() STEP 99: caught error", {
error:
error instanceof Error ? { name: error.name, message: error.message, stack: error.stack } : error,
})
await handleError("update todo list", error as Error)
}
}
@ -350,7 +148,7 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> {
}
// Avoid log spam: partial updates can stream frequently.
todos = preserveTodoMetadata(todos, previousTodos, { enableInstrumentation: false })
todos = preserveTodoMetadata(todos, previousTodos)
const approvalMsg = JSON.stringify({
tool: "updateTodoList",
@ -445,56 +243,10 @@ function normalizeStatus(status: string | undefined): TodoStatus {
* This approach ensures metadata survives status/content changes (which can alter the derived ID)
* and handles duplicates deterministically.
*/
function preserveTodoMetadata(
nextTodos: TodoItem[],
previousTodos: TodoItem[],
options?: { enableInstrumentation?: boolean },
): TodoItem[] {
console.log("[TODO-DEBUG] preserveTodoMetadata() STEP 0: ENTERED", {
nextTodosCount: Array.isArray(nextTodos) ? nextTodos.length : 0,
previousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : 0,
enableInstrumentationOption: options?.enableInstrumentation ?? false,
ROO_DEBUG_TODO_METADATA: process.env.ROO_DEBUG_TODO_METADATA,
ROO_CLI_DEBUG_LOG: process.env.ROO_CLI_DEBUG_LOG,
})
const shouldTodoDebugLogToConsole =
process.env.ROO_DEBUG_TODO_METADATA === "1" ||
process.env.ROO_DEBUG_TODO_METADATA === "true" ||
process.env.ROO_CLI_DEBUG_LOG === "1"
function preserveTodoMetadata(nextTodos: TodoItem[], previousTodos: TodoItem[]): TodoItem[] {
const safePrevious = previousTodos ?? []
const safeNext = nextTodos ?? []
const summarizeTodoForDebug = (t: TodoItem | undefined) => {
if (!t) return undefined
return {
id: typeof t.id === "string" ? t.id : undefined,
status: typeof t.status === "string" ? t.status : undefined,
content: typeof t.content === "string" ? t.content.substring(0, 50) : undefined,
subtaskId: typeof t.subtaskId === "string" ? t.subtaskId : undefined,
tokens: typeof t.tokens === "number" ? t.tokens : undefined,
cost: typeof t.cost === "number" ? t.cost : undefined,
added: typeof t.added === "number" ? t.added : undefined,
removed: typeof t.removed === "number" ? t.removed : undefined,
}
}
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata INPUT", {
previousTodosCount: safePrevious.length,
previousTodos: safePrevious.map((t) => summarizeTodoForDebug(t)),
newTodosCount: safeNext.length,
newTodos: safeNext.map((t) => summarizeTodoForDebug(t)),
})
}
// Instrumentation must never write to stdout/stderr (CLI TUI) and should be opt-in.
// Gate it behind an env var so we don't write files during normal operation.
const enableInstrumentation =
(options?.enableInstrumentation ?? false) &&
(process.env.ROO_CLI_DEBUG_LOG === "1" || process.env.ROO_DEBUG_TODO_METADATA === "1")
// Track which previous todos have been used (by their index) to avoid double-matching
const usedPreviousIndices = new Set<number>()
@ -505,22 +257,10 @@ function preserveTodoMetadata(
const previousBySubtaskId = new Map<string, Array<{ todo: TodoItem; index: number }>>()
const previousById = new Map<string, { todo: TodoItem; index: number }>()
const previousByContent = new Map<string, Array<{ todo: TodoItem; index: number }>>()
const previousMetadataIndices = new Set<number>()
for (let i = 0; i < safePrevious.length; i++) {
const prev = safePrevious[i]
if (!prev) continue
const hasMetadata =
prev.subtaskId !== undefined ||
prev.tokens !== undefined ||
prev.cost !== undefined ||
prev.added !== undefined ||
prev.removed !== undefined
if (hasMetadata) {
previousMetadataIndices.add(i)
}
if (typeof prev.subtaskId === "string") {
const list = previousBySubtaskId.get(prev.subtaskId)
if (list) list.push({ todo: prev, index: i })
@ -552,14 +292,6 @@ function preserveTodoMetadata(
continue
}
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata ITERATION", {
nextIndex,
next: summarizeTodoForDebug(next),
usedPreviousIndicesCount: usedPreviousIndices.size,
})
}
let matchedPrev: TodoItem | undefined = undefined
let matchedIndex: number | undefined = undefined
let matchStrategy: "subtaskId" | "id" | "content" | "none" = "none"
@ -568,22 +300,7 @@ function preserveTodoMetadata(
if (typeof next.subtaskId === "string") {
const candidates = previousBySubtaskId.get(next.subtaskId)
if (candidates) {
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata subtaskId candidates", {
nextIndex,
subtaskId: next.subtaskId,
candidatesCount: candidates.length,
})
}
for (const candidate of candidates) {
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata subtaskId candidate", {
nextIndex,
candidateIndex: candidate.index,
candidate: summarizeTodoForDebug(candidate.todo),
candidateAlreadyUsed: usedPreviousIndices.has(candidate.index),
})
}
if (!usedPreviousIndices.has(candidate.index)) {
matchedPrev = candidate.todo
matchedIndex = candidate.index
@ -609,23 +326,8 @@ function preserveTodoMetadata(
const normalizedContent = normalizeTodoContentForId(next.content)
const candidates = previousByContent.get(normalizedContent)
if (candidates) {
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata content candidates", {
nextIndex,
normalizedContent: normalizedContent.substring(0, 50),
candidatesCount: candidates.length,
})
}
// Find first unused candidate
for (const candidate of candidates) {
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata content candidate", {
nextIndex,
candidateIndex: candidate.index,
candidate: summarizeTodoForDebug(candidate.todo),
candidateAlreadyUsed: usedPreviousIndices.has(candidate.index),
})
}
if (!usedPreviousIndices.has(candidate.index)) {
matchedPrev = candidate.todo
matchedIndex = candidate.index
@ -638,23 +340,6 @@ function preserveTodoMetadata(
// Mark as used and apply metadata
if (matchedPrev && matchedIndex !== undefined) {
const metadataCopiedFromPrev = {
subtaskId: next.subtaskId === undefined ? matchedPrev.subtaskId : undefined,
tokens: next.tokens === undefined ? matchedPrev.tokens : undefined,
cost: next.cost === undefined ? matchedPrev.cost : undefined,
added: next.added === undefined ? matchedPrev.added : undefined,
removed: next.removed === undefined ? matchedPrev.removed : undefined,
}
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata MATCH", {
nextIndex,
matchStrategy,
matchedIndex,
matchedPrev: summarizeTodoForDebug(matchedPrev),
metadataCopiedFromPrev,
})
}
usedPreviousIndices.add(matchedIndex)
matchedPreviousIndexByNextIndex[nextIndex] = matchedIndex
result[nextIndex] = {
@ -668,13 +353,6 @@ function preserveTodoMetadata(
continue
}
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata NO_MATCH", {
nextIndex,
next: summarizeTodoForDebug(next),
})
}
result[nextIndex] = next
}
@ -693,7 +371,6 @@ function preserveTodoMetadata(
todoStatusSequenceMatchesByIndex(safePrevious, safeNext)
if (canApplyIndexRenameCarryover) {
let indexCarryoverCount = 0
for (let i = 0; i < safeNext.length; i++) {
if (matchedPreviousIndexByNextIndex[i] !== undefined) continue // already matched by stable strategy
if (usedPreviousIndices.has(i)) continue // avoid double-using a previous row
@ -701,22 +378,6 @@ function preserveTodoMetadata(
const next = result[i]
if (!prev || !next) continue
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata INDEX_CARRYOVER", {
nextIndex: i,
previousIndex: i,
prev: summarizeTodoForDebug(prev),
next: summarizeTodoForDebug(next),
metadataCopiedFromPrev: {
subtaskId: next.subtaskId === undefined ? prev.subtaskId : undefined,
tokens: next.tokens === undefined ? prev.tokens : undefined,
cost: next.cost === undefined ? prev.cost : undefined,
added: next.added === undefined ? prev.added : undefined,
removed: next.removed === undefined ? prev.removed : undefined,
},
})
}
result[i] = {
...next,
subtaskId: next.subtaskId ?? prev.subtaskId,
@ -726,15 +387,6 @@ function preserveTodoMetadata(
removed: next.removed ?? prev.removed,
}
usedPreviousIndices.add(i)
indexCarryoverCount++
}
if (enableInstrumentation && indexCarryoverCount > 0) {
appendRooCliDebugLog("[Roo-Debug] preserveTodoMetadata: applied index-based rename carryover", {
indexCarryoverCount,
previousTodosCount: safePrevious.length,
nextTodosCount: safeNext.length,
})
}
}
@ -772,57 +424,6 @@ function preserveTodoMetadata(
result[targetNextIndex] = updatedTarget
usedPreviousIndices.add(orphanedPrevIndex)
matchedPreviousIndexByNextIndex[targetNextIndex] = orphanedPrevIndex
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata ORPHAN_CARRYOVER", {
orphanedContent: orphanedPrev.content?.substring(0, 40),
orphanedSubtaskId: orphanedPrev.subtaskId,
targetContent: updatedTarget.content?.substring(0, 40),
copiedFields: {
subtaskId: updatedTarget.subtaskId,
tokens: updatedTarget.tokens,
cost: updatedTarget.cost,
},
})
}
}
// Lightweight debug instrumentation: detect when previous rows that had metadata could not be
// matched to any next todo row (and therefore their metadata could not be preserved).
//
// Keep payload minimal to avoid logging user content.
if (enableInstrumentation && previousMetadataIndices.size > 0) {
let lostMetadataRowCount = 0
for (const prevIndex of previousMetadataIndices) {
if (!usedPreviousIndices.has(prevIndex)) {
lostMetadataRowCount++
}
}
if (lostMetadataRowCount > 0) {
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata LOST_METADATA", {
lostMetadataRowCount,
previousMetadataRowCount: previousMetadataIndices.size,
previousTodosCount: safePrevious.length,
nextTodosCount: safeNext.length,
})
}
// IMPORTANT: do not use console.log here in the CLI TUI. It can corrupt rendering (e.g. dropdowns).
appendRooCliDebugLog("[Roo-Debug] preserveTodoMetadata: previous todo(s) with metadata were not matched", {
lostMetadataRowCount,
previousMetadataRowCount: previousMetadataIndices.size,
previousTodosCount: safePrevious.length,
nextTodosCount: safeNext.length,
})
}
}
if (shouldTodoDebugLogToConsole) {
console.log("[TODO-DEBUG] preserveTodoMetadata OUTPUT", {
resultCount: result.length,
resultTodos: result.map((t) => summarizeTodoForDebug(t)),
})
}
return result
@ -839,19 +440,6 @@ function todoStatusSequenceMatchesByIndex(previous: TodoItem[], next: TodoItem[]
}
return true
}
const ROO_CLI_DEBUG_LOG_PATH = path.join(os.tmpdir(), "roo-cli-debug.log")
function appendRooCliDebugLog(message: string, data?: unknown) {
try {
const timestamp = new Date().toISOString()
const entry = data ? `[${timestamp}] ${message}: ${JSON.stringify(data)}\n` : `[${timestamp}] ${message}\n`
fs.appendFileSync(ROO_CLI_DEBUG_LOG_PATH, entry)
} catch {
// Swallow errors: logging must never break tool execution.
}
}
export function parseMarkdownChecklist(md: string): TodoItem[] {
if (typeof md !== "string") return []
const lines = md

View file

@ -2,7 +2,6 @@ import { ClineMessage, TodoItem } from "@roo-code/types"
export function getLatestTodo(clineMessages: ClineMessage[]): TodoItem[] {
if (!Array.isArray(clineMessages) || clineMessages.length === 0) {
console.log("[TODO-DEBUG]", "getLatestTodo called with empty clineMessages")
return []
}
@ -31,21 +30,5 @@ export function getLatestTodo(clineMessages: ClineMessage[]): TodoItem[] {
}
}
console.log("[TODO-DEBUG]", "getLatestTodo scanned messages", {
totalMessages: clineMessages.length,
candidateMessages: candidateMessages.length,
matchedUpdateTodoListCount,
parseFailureCount,
returnedTodosCount: Array.isArray(lastTodos) ? lastTodos.length : 0,
// Only log lightweight metadata for the last few candidates (avoid dumping full message content)
lastCandidates: candidateMessages.slice(-5).map((m) => ({
ts: m.ts,
type: m.type,
ask: (m as any).ask,
say: (m as any).say,
textLength: typeof m.text === "string" ? m.text.length : 0,
})),
})
return Array.isArray(lastTodos) ? lastTodos : []
}

View file

@ -397,18 +397,8 @@ export const ChatRowContent = ({
if (message.ask !== "tool") return null
const parsed = safeJsonParse<ClineSayTool>(message.text)
// TODO debugging: verify tool JSON is actually being parsed in the webview.
if ((parsed as any)?.tool === "updateTodoList") {
console.log("[TODO-DEBUG]", "ChatRow parsed tool JSON", {
messageTs: message.ts,
toolName: (parsed as any)?.tool,
newTodosCount: Array.isArray((parsed as any)?.todos) ? (parsed as any).todos.length : undefined,
parsed,
})
}
return parsed
}, [message.ask, message.text, message.ts])
}, [message.ask, message.text])
// Unified diff content (provided by backend when relevant)
const unifiedDiff = useMemo(() => {
@ -581,15 +571,6 @@ export const ChatRowContent = ({
// Get previous todos from the latest todos in the task context
const previousTodos = getPreviousTodos(clineMessages, message.ts)
console.log("[TODO-DEBUG]", "ChatRow rendering TodoChangeDisplay", {
messageTs: message.ts,
previousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : undefined,
newTodosCount: Array.isArray(todos) ? todos.length : undefined,
previousTodos,
newTodos: todos,
parsedTool: tool,
})
return <TodoChangeDisplay previousTodos={previousTodos} newTodos={todos} />
}
case "newFileCreated":

View file

@ -126,13 +126,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
}, [messages, currentTaskTodos])
useEffect(() => {
// TODO debugging: ensure todo extraction runs and surfaces state that should drive UI.
console.log("[TODO-DEBUG]", "ChatView latestTodos computed", {
messagesCount: Array.isArray(messages) ? messages.length : undefined,
currentTaskTodosCount: Array.isArray(currentTaskTodos) ? currentTaskTodos.length : undefined,
latestTodosCount: Array.isArray(latestTodos) ? latestTodos.length : undefined,
latestTodos,
})
// Intentionally left blank: keep dependencies so todo extraction logic remains reactive.
}, [messages, currentTaskTodos, latestTodos])
const modifiedMessages = useMemo(() => combineApiRequests(combineCommandSequences(messages.slice(1))), [messages])

View file

@ -26,17 +26,6 @@ function getTodoIcon(status: TodoStatus | null) {
}
export function TodoChangeDisplay({ previousTodos, newTodos }: TodoChangeDisplayProps) {
console.log("[TODO-DEBUG]", "TodoChangeDisplay compare todos", {
previousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : 0,
newTodosCount: Array.isArray(newTodos) ? newTodos.length : 0,
previousTodos: Array.isArray(previousTodos)
? previousTodos.map((t) => ({ id: t.id, content: t.content, status: t.status }))
: [],
newTodos: Array.isArray(newTodos)
? newTodos.map((t) => ({ id: t.id, content: t.content, status: t.status }))
: [],
})
const isInitialState = previousTodos.length === 0
// Determine which todos to display
@ -45,45 +34,21 @@ export function TodoChangeDisplay({ previousTodos, newTodos }: TodoChangeDisplay
if (isInitialState && newTodos.length > 0) {
// For initial state, show all todos in their original order
todosToDisplay = newTodos
console.log("[TODO-DEBUG]", "TodoChangeDisplay selection: initial state -> show all newTodos", {
todosToDisplayCount: todosToDisplay.length,
})
} else {
// For updates, only show changes (completed or started) in their original order
todosToDisplay = newTodos.filter((newTodo) => {
if (newTodo.status === "completed") {
const previousTodo = previousTodos.find((p) => p.id === newTodo.id || p.content === newTodo.content)
const include = !previousTodo || previousTodo.status !== "completed"
console.log("[TODO-DEBUG]", "TodoChangeDisplay selection: completed todo", {
newTodo: { id: newTodo.id, content: newTodo.content, status: newTodo.status },
matchedPreviousTodo: previousTodo
? { id: previousTodo.id, content: previousTodo.content, status: previousTodo.status }
: undefined,
include,
})
return include
}
if (newTodo.status === "in_progress") {
const previousTodo = previousTodos.find((p) => p.id === newTodo.id || p.content === newTodo.content)
const include = !previousTodo || previousTodo.status !== "in_progress"
console.log("[TODO-DEBUG]", "TodoChangeDisplay selection: in_progress todo", {
newTodo: { id: newTodo.id, content: newTodo.content, status: newTodo.status },
matchedPreviousTodo: previousTodo
? { id: previousTodo.id, content: previousTodo.content, status: previousTodo.status }
: undefined,
include,
})
return include
}
console.log("[TODO-DEBUG]", "TodoChangeDisplay selection: ignored todo (not completed/in_progress)", {
newTodo: { id: newTodo.id, content: newTodo.content, status: newTodo.status },
})
return false
})
console.log("[TODO-DEBUG]", "TodoChangeDisplay selection result", {
todosToDisplayCount: todosToDisplay.length,
todosToDisplay: todosToDisplay.map((t) => ({ id: t.id, content: t.content, status: t.status })),
})
}
// If no todos to display, don't render anything

View file

@ -41,16 +41,6 @@ export interface TodoListDisplayProps {
}
export function TodoListDisplay({ todos, subtaskDetails, onSubtaskClick }: TodoListDisplayProps) {
useEffect(() => {
console.log("[TODO-DEBUG]", "TodoListDisplay props received", {
todosCount: Array.isArray(todos) ? todos.length : 0,
todoSubtaskIds: Array.isArray(todos) ? todos.map((t) => t?.subtaskId).filter(Boolean) : [],
subtaskDetailsCount: Array.isArray(subtaskDetails) ? subtaskDetails.length : 0,
subtaskDetailsIds: Array.isArray(subtaskDetails) ? subtaskDetails.map((s) => s?.id).filter(Boolean) : [],
hasOnSubtaskClick: Boolean(onSubtaskClick),
})
}, [todos, subtaskDetails, onSubtaskClick])
const [isCollapsed, setIsCollapsed] = useState(true)
const ulRef = useRef<HTMLUListElement>(null)
const itemRefs = useRef<(HTMLLIElement | null)[]>([])
@ -118,25 +108,10 @@ export function TodoListDisplay({ todos, subtaskDetails, onSubtaskClick }: TodoL
const todoStatus = (todo.status as TodoStatus) ?? "pending"
const icon = getTodoIcon(todoStatus)
const isClickable = Boolean(todo.subtaskId && onSubtaskClick)
console.log("[TODO-DEBUG]", "TodoListDisplay subtask match start", {
todoIndex: idx,
todoId: todo.id,
todoContent: todo.content,
todoSubtaskId: todo.subtaskId,
availableSubtaskDetailIds: Array.isArray(subtaskDetails)
? subtaskDetails.map((s) => s?.id).filter(Boolean)
: [],
})
const subtaskById =
subtaskDetails && todo.subtaskId
? subtaskDetails.find((s) => s.id === todo.subtaskId)
: undefined
console.log("[TODO-DEBUG]", "TodoListDisplay subtask match result", {
todoIndex: idx,
todoSubtaskId: todo.subtaskId,
matched: Boolean(subtaskById),
matchedSubtaskId: subtaskById?.id,
})
const displayTokens = todo.tokens ?? subtaskById?.tokens
const displayCost = todo.cost ?? subtaskById?.cost
const shouldShowCost = typeof displayTokens === "number" && typeof displayCost === "number"
@ -164,33 +139,6 @@ export function TodoListDisplay({ todos, subtaskDetails, onSubtaskClick }: TodoL
const shouldShowLineChanges = hasValidSubtaskLink && (canRenderAdded || canRenderRemoved)
console.log("[TODO-DEBUG]", "TodoListDisplay metadata computed", {
todoIndex: idx,
todoSubtaskId: todo.subtaskId,
fromTodo: {
tokens: todo.tokens,
cost: todo.cost,
added: todo.added,
removed: todo.removed,
},
fromSubtaskDetails: subtaskById
? {
tokens: subtaskById.tokens,
cost: subtaskById.cost,
added: subtaskById.added,
removed: subtaskById.removed,
}
: undefined,
display: {
displayTokens,
displayCost,
displayAdded,
displayRemoved,
},
shouldShowCost,
shouldShowLineChanges,
})
const isAddedPositive = canRenderAdded && (displayAdded as number) > 0
const isRemovedPositive = canRenderRemoved && (displayRemoved as number) > 0
const isAddedZero = canRenderAdded && displayAdded === 0