Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit e6ea70f

Browse files
committed
feat(zoo-migration): new service to support community-driven extension
1 parent ad25634 commit e6ea70f

43 files changed

Lines changed: 912 additions & 1 deletion

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/types/src/vscode.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const commandIds = [
4242

4343
"setCustomStoragePath",
4444
"importSettings",
45+
"prepareZooMigration",
4546

4647
"focusInput",
4748
"acceptInput",

src/__mocks__/fs/promises.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,68 @@ const mockFs = {
150150
throw error
151151
}),
152152

153+
rm: vi.fn().mockImplementation(async (targetPath: string, options?: { recursive?: boolean; force?: boolean }) => {
154+
if (mockFiles.has(targetPath)) {
155+
mockFiles.delete(targetPath)
156+
return Promise.resolve()
157+
}
158+
159+
if (mockDirectories.has(targetPath)) {
160+
for (const filePath of Array.from(mockFiles.keys())) {
161+
if (filePath.startsWith(`${targetPath}/`)) {
162+
mockFiles.delete(filePath)
163+
}
164+
}
165+
for (const dirPath of Array.from(mockDirectories.values()) as string[]) {
166+
if (dirPath === targetPath || dirPath.startsWith(`${targetPath}/`)) {
167+
mockDirectories.delete(dirPath)
168+
}
169+
}
170+
return Promise.resolve()
171+
}
172+
173+
if (options?.force) {
174+
return Promise.resolve()
175+
}
176+
177+
const error = new Error(`ENOENT: no such file or directory, rm '${targetPath}'`)
178+
;(error as any).code = "ENOENT"
179+
throw error
180+
}),
181+
182+
cp: vi.fn().mockImplementation(async (sourcePath: string, destinationPath: string) => {
183+
if (mockFiles.has(sourcePath)) {
184+
const parentDir = destinationPath.split("/").slice(0, -1).join("/")
185+
ensureDirectoryExists(parentDir)
186+
mockFiles.set(destinationPath, mockFiles.get(sourcePath))
187+
return Promise.resolve()
188+
}
189+
190+
if (mockDirectories.has(sourcePath)) {
191+
ensureDirectoryExists(destinationPath)
192+
for (const dirPath of Array.from(mockDirectories.values()) as string[]) {
193+
if (dirPath.startsWith(`${sourcePath}/`)) {
194+
ensureDirectoryExists(destinationPath + dirPath.slice(sourcePath.length))
195+
}
196+
}
197+
for (const [filePath, content] of Array.from(mockFiles.entries())) {
198+
if (filePath.startsWith(`${sourcePath}/`)) {
199+
const copiedPath = destinationPath + filePath.slice(sourcePath.length)
200+
const parentDir = copiedPath.split("/").slice(0, -1).join("/")
201+
ensureDirectoryExists(parentDir)
202+
mockFiles.set(copiedPath, content)
203+
}
204+
}
205+
return Promise.resolve()
206+
}
207+
208+
const error = new Error(`ENOENT: no such file or directory, cp '${sourcePath}'`)
209+
;(error as any).code = "ENOENT"
210+
throw error
211+
}),
212+
213+
chmod: vi.fn().mockResolvedValue(undefined),
214+
153215
constants: require("fs").constants,
154216

155217
// Expose mock data for test assertions

src/activate/registerCommands.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { handleNewTask } from "./handleTask"
1313
import { CodeIndexManager } from "../services/code-index/manager"
1414
import { importSettingsWithFeedback } from "../core/config/importExport"
1515
import { MdmService } from "../services/mdm/MdmService"
16+
import { createZooMigrationHandoff, promptAndCreateZooMigrationHandoff } from "../services/zoo-migration/ZooMigration"
1617
import { t } from "../i18n"
1718

1819
/**
@@ -155,6 +156,30 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
155156
filePath,
156157
)
157158
},
159+
prepareZooMigration: async (options?: { skipPrompt?: boolean; includeSecrets?: boolean }) => {
160+
try {
161+
const migrationOptions = {
162+
context,
163+
contextProxy: provider.contextProxy,
164+
providerSettingsManager: provider.providerSettingsManager,
165+
outputChannel,
166+
}
167+
168+
if (options?.skipPrompt) {
169+
return await createZooMigrationHandoff({
170+
...migrationOptions,
171+
includeSecrets: options.includeSecrets ?? false,
172+
})
173+
}
174+
175+
return await promptAndCreateZooMigrationHandoff(migrationOptions)
176+
} catch (error) {
177+
const message = error instanceof Error ? error.message : String(error)
178+
outputChannel.appendLine(`[Zoo Migration] Failed to prepare migration handoff: ${message}`)
179+
await vscode.window.showErrorMessage(t("common:zooMigration.handoffFailed", { error: message }))
180+
return undefined
181+
}
182+
},
158183
focusInput: async () => {
159184
try {
160185
await focusPanel(tabPanel, sidebarPanel)

src/extension.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { MdmService } from "./services/mdm/MdmService"
3939
import { migrateSettings } from "./utils/migrateSettings"
4040
import { autoImportSettings } from "./utils/autoImportSettings"
4141
import { API } from "./extension/api"
42+
import { showZooMigrationNotice } from "./services/zoo-migration/ZooMigration"
4243

4344
import {
4445
handleUri,
@@ -317,6 +318,12 @@ export async function activate(context: vscode.ExtensionContext) {
317318

318319
registerCommands({ context, outputChannel, provider })
319320

321+
void showZooMigrationNotice(context, { outputChannel }).catch((error) => {
322+
outputChannel.appendLine(
323+
`[Zoo Migration] Failed to show migration notice: ${error instanceof Error ? error.message : String(error)}`,
324+
)
325+
})
326+
320327
/**
321328
* We use the text document content provider API to show the left side for diff
322329
* view by creating a virtual document for the original content. This makes it

src/i18n/locales/ca/common.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/i18n/locales/de/common.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/i18n/locales/en/common.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@
1919
"delete_config_profile": "Are you sure you want to delete this configuration profile?",
2020
"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}"
2121
},
22+
"zooMigration": {
23+
"actions": {
24+
"installZoo": "Install Zoo",
25+
"prepareMigration": "Prepare Migration",
26+
"learnMore": "Learn More",
27+
"later": "Later",
28+
"includeApiKeys": "Include API keys",
29+
"skipApiKeys": "Skip API keys",
30+
"cancel": "Cancel"
31+
},
32+
"notice": "Roo Code is transitioning to Zoo Code. You can prepare a copy-based migration handoff for Zoo without deleting your Roo data.",
33+
"zooNotPublished": "Zoo Code is not published under a confirmed Marketplace extension id yet. The Extensions view has been opened with a Zoo Code search.",
34+
"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.",
35+
"handoffPrepared": "Zoo Code migration handoff prepared at {{path}}. Install Zoo Code and import from this handoff.",
36+
"handoffFailed": "Failed to prepare Zoo Code migration handoff: {{error}}"
37+
},
2238
"errors": {
2339
"invalid_data_uri": "Invalid data URI format",
2440
"error_copying_image": "Error copying image: {{errorMessage}}",

src/i18n/locales/es/common.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/i18n/locales/fr/common.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/i18n/locales/hi/common.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)