mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
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:
parent
4be568874a
commit
249737dbf0
8 changed files with 15 additions and 554 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 : []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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":
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue