From 523abdb25d28ca1ee581de2f5e2becedbbe073fd Mon Sep 17 00:00:00 2001 From: daniel-lxs Date: Mon, 19 Jan 2026 23:16:57 -0500 Subject: [PATCH] fix: resolve merge conflict, use startBackgroundRetentionPurge() for cleaner code - Resolved merge conflict in extension.ts, keeping extracted function approach - startBackgroundRetentionPurge() encapsulates all purge logic in task-history-retention.ts - Added getStorageBasePath mock to task-storage-size tests (addresses roomote review) --- src/__tests__/extension.spec.ts | 4 +- src/extension.ts | 48 +++---------- src/utils/__tests__/task-storage-size.spec.ts | 5 ++ src/utils/task-history-retention.ts | 71 +++++++++++++++++++ 4 files changed, 89 insertions(+), 39 deletions(-) diff --git a/src/__tests__/extension.spec.ts b/src/__tests__/extension.spec.ts index 1d21549311..1803eb4d36 100644 --- a/src/__tests__/extension.spec.ts +++ b/src/__tests__/extension.spec.ts @@ -159,9 +159,9 @@ vi.mock("../utils/autoImportSettings", () => ({ autoImportSettings: vi.fn().mockResolvedValue(undefined), })) -// Avoid filesystem access during activation by stubbing purge +// Avoid filesystem access during activation by stubbing background purge vi.mock("../utils/task-history-retention", () => ({ - purgeOldTasks: vi.fn().mockResolvedValue({ purgedCount: 0, cutoff: null }), + startBackgroundRetentionPurge: vi.fn(), })) // Ensure storage base path resolves to provided path to avoid touching VS Code config diff --git a/src/extension.ts b/src/extension.ts index ae188a0d5f..cff5ea0d46 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -44,7 +44,7 @@ import { } from "./activate" import { initializeI18n } from "./i18n" import { flushModels, initializeModelCacheRefresh, refreshModels } from "./api/providers/fetchers/modelCache" -import { purgeOldTasks, RetentionSetting } from "./utils/task-history-retention" +import { startBackgroundRetentionPurge } from "./utils/task-history-retention" /** * Built using https://github.com/microsoft/vscode-webview-ui-toolkit @@ -389,42 +389,16 @@ export async function activate(context: vscode.ExtensionContext) { vscode.commands.executeCommand(`${Package.name}.activationCompleted`) // Task history retention purge (runs in background after activation) - // Fire-and-forget to avoid blocking extension startup - void (async () => { - try { - const config = vscode.workspace.getConfiguration(Package.name) - const retention = config.get("taskHistoryRetention", "never") ?? "never" - - // Skip if retention is disabled (default) - if (retention === "never") { - return - } - - outputChannel.appendLine(`[Retention] Starting background purge: setting=${retention}`) - - const result = await purgeOldTasks( - retention as RetentionSetting, - contextProxy.globalStorageUri.fsPath, - (m) => outputChannel.appendLine(m), - false, - async (taskId: string) => { - // Reuse the same internal deletion logic as the History view so that - // checkpoints, shadow repositories, and task state are cleaned up consistently. - await provider.deleteTaskWithId(taskId) - }, - ) - - if (result.purgedCount > 0) { - outputChannel.appendLine( - `[Retention] Background purge complete: purged=${result.purgedCount}, cutoff=${result.cutoff ? new Date(result.cutoff).toISOString() : "none"}`, - ) - } - } catch (error) { - outputChannel.appendLine( - `[Retention] Failed during background purge: ${error instanceof Error ? error.message : String(error)}`, - ) - } - })() + // By this point, provider is fully initialized and ready to handle deletions + startBackgroundRetentionPurge({ + globalStoragePath: contextProxy.globalStorageUri.fsPath, + log: (m) => outputChannel.appendLine(m), + deleteTaskById: async (taskId: string) => { + // Reuse the same internal deletion logic as the History view so that + // checkpoints, shadow repositories, and task state are cleaned up consistently. + await provider.deleteTaskWithId(taskId) + }, + }) // Implements the `RooCodeAPI` interface. const socketPath = process.env.ROO_CODE_IPC_SOCKET_PATH diff --git a/src/utils/__tests__/task-storage-size.spec.ts b/src/utils/__tests__/task-storage-size.spec.ts index f2ae2ad98f..cc7997b146 100644 --- a/src/utils/__tests__/task-storage-size.spec.ts +++ b/src/utils/__tests__/task-storage-size.spec.ts @@ -1,6 +1,11 @@ import * as path from "path" import { calculateTaskStorageSize, formatBytes } from "../task-storage-size" +// Mock storage to avoid VS Code config access during tests +vi.mock("../storage", () => ({ + getStorageBasePath: (p: string) => Promise.resolve(p), +})) + // Mock fs/promises const mockReaddir = vi.fn() const mockStat = vi.fn() diff --git a/src/utils/task-history-retention.ts b/src/utils/task-history-retention.ts index 98ce4b8e19..1b132f2174 100644 --- a/src/utils/task-history-retention.ts +++ b/src/utils/task-history-retention.ts @@ -1,9 +1,11 @@ +import * as vscode from "vscode" import * as path from "path" import * as fs from "fs/promises" import type { Dirent } from "fs" import { getStorageBasePath } from "./storage" import { GlobalFileNames } from "../shared/globalFileNames" +import { Package } from "../shared/package" /** * Allowed retention day values (as numbers). @@ -281,3 +283,72 @@ function normalizeDays(value: RetentionSetting): number { const n = typeof value === "number" ? value : parseInt(String(value), 10) return Number.isFinite(n) && n > 0 ? Math.trunc(n) : 0 } + +/** + * Options for starting the background retention purge. + */ +export interface BackgroundPurgeOptions { + /** VS Code global storage fsPath */ + globalStoragePath: string + /** Logger function */ + log: (message: string) => void + /** Function to delete a task by ID (should use ClineProvider.deleteTaskWithId for full cleanup) */ + deleteTaskById: (taskId: string, taskDirPath: string) => Promise +} + +/** + * Starts the task history retention purge in the background. + * This function is designed to be called after extension activation completes, + * using a fire-and-forget pattern (void) to avoid blocking activation. + * + * It reads the retention setting from VS Code configuration, executes the purge, + * and shows a notification if tasks were deleted. + * + * @param options Configuration options for the background purge + */ +export function startBackgroundRetentionPurge(options: BackgroundPurgeOptions): void { + const { globalStoragePath, log, deleteTaskById } = options + + void (async () => { + try { + const config = vscode.workspace.getConfiguration(Package.name) + const retention = config.get("taskHistoryRetention", "never") ?? "never" + + // Skip if retention is disabled + if (retention === "never") { + log("[Retention] Background purge skipped: retention is set to 'never'") + return + } + + log(`[Retention] Starting background purge: setting=${retention}`) + + const result = await purgeOldTasks( + retention as RetentionSetting, + globalStoragePath, + log, + false, + deleteTaskById, + ) + + log( + `[Retention] Background purge complete: purged=${result.purgedCount}, cutoff=${result.cutoff ?? "none"}`, + ) + + // Show user notification if tasks were deleted + if (result.purgedCount > 0) { + const message = `Roo Code deleted ${result.purgedCount} task${result.purgedCount === 1 ? "" : "s"} older than ${retention} day${retention === "1" ? "" : "s"}` + + vscode.window.showInformationMessage(message, "View Settings", "Dismiss").then((action) => { + if (action === "View Settings") { + vscode.commands.executeCommand( + "workbench.action.openSettings", + "roo-cline.taskHistoryRetention", + ) + } + }) + } + } catch (error) { + log(`[Retention] Failed during background purge: ${error instanceof Error ? error.message : String(error)}`) + } + })() +}