mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-06 08:18:39 +00:00
fix: properly implement stable project IDs for Sprint 1
- Fix generateProjectId to check for existing ID before creating new one - Add workspace filtering to task history based on project ID/workspace path - Add migration logic when generating project ID for existing workspace - Add missing translation keys - Update tests to cover new functionality This ensures that task history actually follows the project when it's moved, which was the core requirement that was missing from the original implementation.
This commit is contained in:
parent
64fbf59d86
commit
fc7b68b261
5 changed files with 91 additions and 9 deletions
|
|
@ -228,13 +228,38 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
|
|||
}
|
||||
|
||||
try {
|
||||
const projectId = await generateProjectId(workspacePath)
|
||||
vscode.window.showInformationMessage(t("common:info.project_id_generated", { projectId }))
|
||||
// Check if project ID already exists
|
||||
const { getProjectId } = await import("../utils/projectId")
|
||||
const existingId = await getProjectId(workspacePath)
|
||||
|
||||
// Notify the provider to update any cached state
|
||||
if (existingId) {
|
||||
vscode.window.showInformationMessage(
|
||||
t("common:info.project_id_already_exists", { projectId: existingId }),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const projectId = await generateProjectId(workspacePath)
|
||||
|
||||
// Migrate existing tasks to use the new project ID
|
||||
const visibleProvider = getVisibleProviderOrLog(outputChannel)
|
||||
if (visibleProvider) {
|
||||
const migrated = await visibleProvider.migrateTasksToProjectId(workspacePath, projectId)
|
||||
|
||||
if (migrated > 0) {
|
||||
vscode.window.showInformationMessage(
|
||||
t("common:info.project_id_generated_with_migration", {
|
||||
projectId,
|
||||
count: migrated,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
vscode.window.showInformationMessage(t("common:info.project_id_generated", { projectId }))
|
||||
}
|
||||
|
||||
await visibleProvider.postStateToWebview()
|
||||
} else {
|
||||
vscode.window.showInformationMessage(t("common:info.project_id_generated", { projectId }))
|
||||
}
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(
|
||||
|
|
|
|||
|
|
@ -1643,6 +1643,16 @@ export class ClineProvider
|
|||
const currentMode = mode ?? defaultModeSlug
|
||||
const hasSystemPromptOverride = await this.hasFileBasedSystemPromptOverride(currentMode)
|
||||
|
||||
// Get the workspace storage key (project ID or workspace path)
|
||||
const { getWorkspaceStorageKey } = await import("../../utils/projectId")
|
||||
const workspaceStorageKey = await getWorkspaceStorageKey(cwd)
|
||||
|
||||
// Filter task history to only show tasks for the current workspace
|
||||
const filteredTaskHistory = (taskHistory || [])
|
||||
.filter((item: HistoryItem) => item.workspace === workspaceStorageKey)
|
||||
.filter((item: HistoryItem) => item.ts && item.task)
|
||||
.sort((a: HistoryItem, b: HistoryItem) => b.ts - a.ts)
|
||||
|
||||
return {
|
||||
version: this.context.extension?.packageJSON?.version ?? "",
|
||||
apiConfiguration,
|
||||
|
|
@ -1664,12 +1674,10 @@ export class ClineProvider
|
|||
autoCondenseContextPercent: autoCondenseContextPercent ?? 100,
|
||||
uriScheme: vscode.env.uriScheme,
|
||||
currentTaskItem: this.getCurrentCline()?.taskId
|
||||
? (taskHistory || []).find((item: HistoryItem) => item.id === this.getCurrentCline()?.taskId)
|
||||
? filteredTaskHistory.find((item: HistoryItem) => item.id === this.getCurrentCline()?.taskId)
|
||||
: undefined,
|
||||
clineMessages: this.getCurrentCline()?.clineMessages || [],
|
||||
taskHistory: (taskHistory || [])
|
||||
.filter((item: HistoryItem) => item.ts && item.task)
|
||||
.sort((a: HistoryItem, b: HistoryItem) => b.ts - a.ts),
|
||||
taskHistory: filteredTaskHistory,
|
||||
soundEnabled: soundEnabled ?? false,
|
||||
ttsEnabled: ttsEnabled ?? false,
|
||||
ttsSpeed: ttsSpeed ?? 1.0,
|
||||
|
|
@ -2114,6 +2122,32 @@ export class ClineProvider
|
|||
...gitInfo,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate existing tasks to use the new project ID
|
||||
* @param workspacePath The workspace path to migrate from
|
||||
* @param projectId The new project ID to migrate to
|
||||
* @returns The number of tasks migrated
|
||||
*/
|
||||
async migrateTasksToProjectId(workspacePath: string, projectId: string): Promise<number> {
|
||||
const taskHistory = this.getGlobalState("taskHistory") ?? []
|
||||
let migratedCount = 0
|
||||
|
||||
// Update all tasks that match the workspace path
|
||||
const updatedHistory = taskHistory.map((item: HistoryItem) => {
|
||||
if (item.workspace === workspacePath) {
|
||||
migratedCount++
|
||||
return { ...item, workspace: projectId }
|
||||
}
|
||||
return item
|
||||
})
|
||||
|
||||
if (migratedCount > 0) {
|
||||
await this.updateGlobalState("taskHistory", updatedHistory)
|
||||
}
|
||||
|
||||
return migratedCount
|
||||
}
|
||||
}
|
||||
|
||||
class OrganizationAllowListViolationError extends Error {
|
||||
|
|
|
|||
|
|
@ -120,7 +120,11 @@
|
|||
"image_copied_to_clipboard": "Image data URI copied to clipboard",
|
||||
"image_saved": "Image saved to {{path}}",
|
||||
"mode_exported": "Mode '{{mode}}' exported successfully",
|
||||
"mode_imported": "Mode imported successfully"
|
||||
"mode_imported": "Mode imported successfully",
|
||||
"project_id_generated": "Project ID generated: {{projectId}}",
|
||||
"project_id_already_exists": "Project ID already exists: {{projectId}}",
|
||||
"project_id_generated_with_migration": "Project ID generated: {{projectId}}. Migrated {{count}} task(s) to use the new ID.",
|
||||
"project_id_generation_failed": "Failed to generate project ID: {{error}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Yes",
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ describe("projectId", () => {
|
|||
|
||||
describe("generateProjectId", () => {
|
||||
it("should generate and save a new project ID", async () => {
|
||||
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
||||
vi.mocked(fs.writeFile).mockResolvedValue()
|
||||
|
||||
const result = await generateProjectId(mockWorkspaceRoot)
|
||||
|
|
@ -84,7 +85,18 @@ describe("projectId", () => {
|
|||
expect(fs.writeFile).toHaveBeenCalledWith(mockProjectIdPath, result, "utf8")
|
||||
})
|
||||
|
||||
it("should return existing project ID if already exists", async () => {
|
||||
vi.mocked(fileExistsAtPath).mockResolvedValue(true)
|
||||
vi.mocked(fs.readFile).mockResolvedValue(mockProjectId)
|
||||
|
||||
const result = await generateProjectId(mockWorkspaceRoot)
|
||||
|
||||
expect(result).toBe(mockProjectId)
|
||||
expect(fs.writeFile).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should throw error if write fails", async () => {
|
||||
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
||||
vi.mocked(fs.writeFile).mockRejectedValue(new Error("Write failed"))
|
||||
|
||||
await expect(generateProjectId(mockWorkspaceRoot)).rejects.toThrow("Write failed")
|
||||
|
|
|
|||
|
|
@ -38,11 +38,18 @@ export async function getProjectId(workspaceRoot: string): Promise<string | null
|
|||
|
||||
/**
|
||||
* Generates a new project ID and writes it to the .rooprojectid file.
|
||||
* If a project ID already exists, returns the existing one.
|
||||
*
|
||||
* @param workspaceRoot The root directory of the workspace
|
||||
* @returns The generated project ID
|
||||
* @returns The generated or existing project ID
|
||||
*/
|
||||
export async function generateProjectId(workspaceRoot: string): Promise<string> {
|
||||
// Check if project ID already exists
|
||||
const existingId = await getProjectId(workspaceRoot)
|
||||
if (existingId) {
|
||||
return existingId
|
||||
}
|
||||
|
||||
const projectId = uuidv4()
|
||||
const projectIdPath = path.join(workspaceRoot, PROJECT_ID_FILENAME)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue