Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .codex
Empty file.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Roo Code Changelog

## 3.53.1

### Patch Changes

- Add the Zoo Code migration handoff flow, including a VS Code command, activation notification, and release announcement actions to prepare Roo data for import into Zoo Code.

## 3.53.0

### Minor Changes
Expand Down
2 changes: 2 additions & 0 deletions packages/types/src/vscode-extension-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ export interface WebviewMessage {
| "terminalOperation"
| "clearTask"
| "didShowAnnouncement"
| "prepareZooMigration"
| "installZooExtension"
| "selectImages"
| "exportCurrentTask"
| "shareCurrentTask"
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const commandIds = [

"setCustomStoragePath",
"importSettings",
"prepareZooMigration",

"focusInput",
"acceptInput",
Expand Down
62 changes: 62 additions & 0 deletions src/__mocks__/fs/promises.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,68 @@ const mockFs = {
throw error
}),

rm: vi.fn().mockImplementation(async (targetPath: string, options?: { recursive?: boolean; force?: boolean }) => {
if (mockFiles.has(targetPath)) {
mockFiles.delete(targetPath)
return Promise.resolve()
}

if (mockDirectories.has(targetPath)) {
for (const filePath of Array.from(mockFiles.keys())) {
if (filePath.startsWith(`${targetPath}/`)) {
mockFiles.delete(filePath)
}
}
for (const dirPath of Array.from(mockDirectories.values()) as string[]) {
if (dirPath === targetPath || dirPath.startsWith(`${targetPath}/`)) {
mockDirectories.delete(dirPath)
}
}
return Promise.resolve()
}

if (options?.force) {
return Promise.resolve()
}

const error = new Error(`ENOENT: no such file or directory, rm '${targetPath}'`)
;(error as any).code = "ENOENT"
throw error
}),

cp: vi.fn().mockImplementation(async (sourcePath: string, destinationPath: string) => {
if (mockFiles.has(sourcePath)) {
const parentDir = destinationPath.split("/").slice(0, -1).join("/")
ensureDirectoryExists(parentDir)
mockFiles.set(destinationPath, mockFiles.get(sourcePath))
return Promise.resolve()
}

if (mockDirectories.has(sourcePath)) {
ensureDirectoryExists(destinationPath)
for (const dirPath of Array.from(mockDirectories.values()) as string[]) {
if (dirPath.startsWith(`${sourcePath}/`)) {
ensureDirectoryExists(destinationPath + dirPath.slice(sourcePath.length))
}
}
for (const [filePath, content] of Array.from(mockFiles.entries())) {
if (filePath.startsWith(`${sourcePath}/`)) {
const copiedPath = destinationPath + filePath.slice(sourcePath.length)
const parentDir = copiedPath.split("/").slice(0, -1).join("/")
ensureDirectoryExists(parentDir)
mockFiles.set(copiedPath, content)
}
}
return Promise.resolve()
}

const error = new Error(`ENOENT: no such file or directory, cp '${sourcePath}'`)
;(error as any).code = "ENOENT"
throw error
}),

chmod: vi.fn().mockResolvedValue(undefined),

constants: require("fs").constants,

// Expose mock data for test assertions
Expand Down
25 changes: 25 additions & 0 deletions src/activate/registerCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { handleNewTask } from "./handleTask"
import { CodeIndexManager } from "../services/code-index/manager"
import { importSettingsWithFeedback } from "../core/config/importExport"
import { MdmService } from "../services/mdm/MdmService"
import { createZooMigrationHandoff, promptAndCreateZooMigrationHandoff } from "../services/zoo-migration/ZooMigration"
import { t } from "../i18n"

/**
Expand Down Expand Up @@ -155,6 +156,30 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
filePath,
)
},
prepareZooMigration: async (options?: { skipPrompt?: boolean; includeSecrets?: boolean }) => {
try {
const migrationOptions = {
context,
contextProxy: provider.contextProxy,
providerSettingsManager: provider.providerSettingsManager,
outputChannel,
}

if (options?.skipPrompt) {
return await createZooMigrationHandoff({
...migrationOptions,
includeSecrets: options.includeSecrets ?? false,
})
}

return await promptAndCreateZooMigrationHandoff(migrationOptions)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
outputChannel.appendLine(`[Zoo Migration] Failed to prepare migration handoff: ${message}`)
await vscode.window.showErrorMessage(t("common:zooMigration.handoffFailed", { error: message }))
return undefined
}
},
focusInput: async () => {
try {
await focusPanel(tabPanel, sidebarPanel)
Expand Down
2 changes: 1 addition & 1 deletion src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export class ClineProvider

public isViewLaunched = false
public settingsImportedAt?: number
public readonly latestAnnouncementId = "apr-2026-v3.53.0-community-handoff-gpt55-opus47" // v3.53.0 Community handoff, GPT-5.5, Claude Opus 4.7, checkpoint navigation
public readonly latestAnnouncementId = "may-2026-v3.53.1-zoo-migration-handoff" // v3.53.1 Zoo migration handoff
public readonly providerSettingsManager: ProviderSettingsManager
public readonly customModesManager: CustomModesManager

Expand Down
20 changes: 20 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ import { openImage, saveImage } from "../../integrations/misc/image-handler"
import { selectImages } from "../../integrations/misc/process-images"
import { getTheme } from "../../integrations/theme/getTheme"
import { searchWorkspaceFiles } from "../../services/search/file-search"
import {
installOrShowZooExtension,
promptAndCreateZooMigrationHandoff,
} from "../../services/zoo-migration/ZooMigration"
import { fileExistsAtPath } from "../../utils/fs"
import { playTts, setTtsEnabled, setTtsSpeed, stopTts } from "../../utils/tts"
import { searchCommits } from "../../utils/git"
Expand Down Expand Up @@ -766,6 +770,22 @@ export const webviewMessageHandler = async (
await updateGlobalState("lastShownAnnouncementId", provider.latestAnnouncementId)
await provider.postStateToWebview()
break
case "prepareZooMigration":
try {
await promptAndCreateZooMigrationHandoff({
context: provider.context,
contextProxy: provider.contextProxy,
providerSettingsManager: provider.providerSettingsManager,
})
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
provider.log(`[Zoo Migration] Failed to prepare migration handoff from announcement: ${message}`)
await vscode.window.showErrorMessage(t("common:zooMigration.handoffFailed", { error: message }))
}
break
case "installZooExtension":
await installOrShowZooExtension()
break
case "selectImages":
const images = await selectImages()
await provider.postMessageToWebview({
Expand Down
7 changes: 7 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { MdmService } from "./services/mdm/MdmService"
import { migrateSettings } from "./utils/migrateSettings"
import { autoImportSettings } from "./utils/autoImportSettings"
import { API } from "./extension/api"
import { showZooMigrationNotice } from "./services/zoo-migration/ZooMigration"

import {
handleUri,
Expand Down Expand Up @@ -317,6 +318,12 @@ export async function activate(context: vscode.ExtensionContext) {

registerCommands({ context, outputChannel, provider })

void showZooMigrationNotice(context, { outputChannel }).catch((error) => {
outputChannel.appendLine(
`[Zoo Migration] Failed to show migration notice: ${error instanceof Error ? error.message : String(error)}`,
)
})

/**
* We use the text document content provider API to show the left side for diff
* view by creating a virtual document for the original content. This makes it
Expand Down
16 changes: 16 additions & 0 deletions src/i18n/locales/ca/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions src/i18n/locales/de/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions src/i18n/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@
"delete_config_profile": "Are you sure you want to delete this configuration profile?",
"delete_custom_mode_with_rules": "Are you sure you want to delete this {scope} mode?\n\nThis will also delete the associated rules folder at:\n{rulesFolderPath}"
},
"zooMigration": {
"actions": {
"installZoo": "Install Zoo",
"prepareMigration": "Prepare Migration",
"learnMore": "Learn More",
"later": "Later",
"includeApiKeys": "Include API keys",
"skipApiKeys": "Skip API keys",
"cancel": "Cancel"
},
"notice": "Roo Code is transitioning to Zoo Code. You can prepare a copy-based migration handoff for Zoo without deleting your Roo data.",
"zooNotPublished": "Zoo Code is not published under a confirmed Marketplace extension id yet. The Extensions view has been opened with a Zoo Code search.",
"preparePrompt": "Prepare a local Zoo Code migration handoff? Including API keys writes provider secrets to a local file so Zoo can import them, then delete the handoff after import.",
"handoffPrepared": "Zoo Code migration handoff prepared at {{path}}. Install Zoo Code and import from this handoff.",
"handoffFailed": "Failed to prepare Zoo Code migration handoff: {{error}}"
},
"errors": {
"invalid_data_uri": "Invalid data URI format",
"error_copying_image": "Error copying image: {{errorMessage}}",
Expand Down
16 changes: 16 additions & 0 deletions src/i18n/locales/es/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions src/i18n/locales/fr/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions src/i18n/locales/hi/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions src/i18n/locales/id/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions src/i18n/locales/it/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading