@@ -146,7 +146,7 @@ describe("importRooTaskHistory", () => {
146146 } )
147147 } )
148148
149- it ( "skips Roo roots that resolve to the Zoo storage root and ignores hidden task entries " , async ( ) => {
149+ it ( "imports only top-level task history files and skips checkpoint directories " , async ( ) => {
150150 const zooGlobalStoragePath = path . join ( tempRoot , "globalStorage" , "zoocodeorganization.zoo-code" )
151151 const rooDefaultStorageRoot = path . join ( tempRoot , "globalStorage" , "rooveterinaryinc.roo-cline" )
152152 const zooCustomStorageRoot = path . join ( tempRoot , "shared-storage" )
@@ -156,35 +156,62 @@ describe("importRooTaskHistory", () => {
156156 zoo : zooCustomStorageRoot ,
157157 } )
158158
159- await fs . mkdir ( path . join ( rooDefaultStorageRoot , "tasks" , "task-visible" , "nested" ) , { recursive : true } )
159+ await fs . mkdir ( path . join ( rooDefaultStorageRoot , "tasks" , "task-visible" , "checkpoints" , ".git" , "objects" ) , {
160+ recursive : true ,
161+ } )
160162 await fs . mkdir ( path . join ( rooDefaultStorageRoot , "tasks" , ".task-hidden" ) , { recursive : true } )
161163 await fs . mkdir ( path . join ( rooDefaultStorageRoot , "tasks" , "_task-hidden" ) , { recursive : true } )
162- await fs . writeFile ( path . join ( rooDefaultStorageRoot , "tasks" , "task-visible" , "history_item.json" ) , "visible" )
163164 await fs . writeFile (
164- path . join ( rooDefaultStorageRoot , "tasks" , "task-visible" , "nested" , "ui_messages.json" ) ,
165- "nested" ,
165+ path . join ( rooDefaultStorageRoot , "tasks" , "task-visible" , "history_item.json" ) ,
166+ JSON . stringify ( { id : "task-visible" } ) ,
167+ )
168+ await fs . writeFile ( path . join ( rooDefaultStorageRoot , "tasks" , "task-visible" , "ui_messages.json" ) , "visible-ui" )
169+ await fs . writeFile (
170+ path . join ( rooDefaultStorageRoot , "tasks" , "task-visible" , "api_conversation_history.json" ) ,
171+ "visible-api" ,
166172 )
173+ await fs . writeFile ( path . join ( rooDefaultStorageRoot , "tasks" , "task-visible" , "task_metadata.json" ) , "metadata" )
167174 await fs . writeFile ( path . join ( rooDefaultStorageRoot , "tasks" , "loose.json" ) , "loose" )
168- await fs . writeFile ( path . join ( rooDefaultStorageRoot , "tasks" , ".task-hidden" , "history_item.json" ) , "hidden" )
169- await fs . writeFile ( path . join ( rooDefaultStorageRoot , "tasks" , "_task-hidden" , "history_item.json" ) , "hidden" )
175+ await fs . writeFile (
176+ path . join ( rooDefaultStorageRoot , "tasks" , "task-visible" , "checkpoints" , ".git" , "objects" , "object" ) ,
177+ "git-object" ,
178+ )
179+ await fs . writeFile ( path . join ( rooDefaultStorageRoot , "tasks" , ".task-hidden" , "history_item.json" ) , "hidden-dir" )
180+ await fs . writeFile ( path . join ( rooDefaultStorageRoot , "tasks" , "_task-hidden" , "history_item.json" ) , "hidden-dir" )
170181
171182 const result = await importRooTaskHistory ( zooGlobalStoragePath )
172183
173184 expect ( result . rooStorageRoots ) . toEqual ( [ rooDefaultStorageRoot ] )
174185 expect ( result . importedTaskCount ) . toBe ( 1 )
175- expect ( result . importedFileCount ) . toBe ( 2 )
186+ expect ( result . importedFileCount ) . toBe ( 4 )
187+ expect (
188+ await fs . readFile ( path . join ( zooCustomStorageRoot , "tasks" , "task-visible" , "ui_messages.json" ) , "utf8" ) ,
189+ ) . toBe ( "visible-ui" )
176190 expect (
177191 await fs . readFile (
178- path . join ( zooCustomStorageRoot , "tasks" , "task-visible" , "nested" , "ui_messages .json") ,
192+ path . join ( zooCustomStorageRoot , "tasks" , "task-visible" , "api_conversation_history .json" ) ,
179193 "utf8" ,
180194 ) ,
181- ) . toBe ( "nested" )
195+ ) . toBe ( "visible-api" )
196+ expect (
197+ await fs . readFile ( path . join ( zooCustomStorageRoot , "tasks" , "task-visible" , "task_metadata.json" ) , "utf8" ) ,
198+ ) . toBe ( "metadata" )
182199 await expect ( fs . access ( path . join ( zooCustomStorageRoot , "tasks" , ".task-hidden" ) ) ) . rejects . toMatchObject ( {
183200 code : "ENOENT" ,
184201 } )
185202 await expect ( fs . access ( path . join ( zooCustomStorageRoot , "tasks" , "_task-hidden" ) ) ) . rejects . toMatchObject ( {
186203 code : "ENOENT" ,
187204 } )
205+ await expect (
206+ fs . access (
207+ path . join ( zooCustomStorageRoot , "tasks" , "task-visible" , "checkpoints" , ".git" , "objects" , "object" ) ,
208+ ) ,
209+ ) . rejects . toMatchObject ( {
210+ code : "ENOENT" ,
211+ } )
212+ await expect ( fs . access ( path . join ( zooCustomStorageRoot , "tasks" , "loose.json" ) ) ) . rejects . toMatchObject ( {
213+ code : "ENOENT" ,
214+ } )
188215 } )
189216
190217 it ( "ignores missing Roo task roots while still importing from available roots" , async ( ) => {
@@ -207,6 +234,48 @@ describe("importRooTaskHistory", () => {
207234 ) . toBe ( "default" )
208235 } )
209236
237+ it ( "skips tasks that do not have an importable history_item.json" , async ( ) => {
238+ const zooGlobalStoragePath = path . join ( tempRoot , "globalStorage" , "zoocodeorganization.zoo-code" )
239+ const rooDefaultStorageRoot = path . join ( tempRoot , "globalStorage" , "rooveterinaryinc.roo-cline" )
240+
241+ mockStorageConfiguration ( )
242+
243+ await fs . mkdir ( path . join ( rooDefaultStorageRoot , "tasks" , "task-missing-history" ) , { recursive : true } )
244+ await fs . writeFile (
245+ path . join ( rooDefaultStorageRoot , "tasks" , "task-missing-history" , "ui_messages.json" ) ,
246+ "ui only" ,
247+ )
248+
249+ const result = await importRooTaskHistory ( zooGlobalStoragePath )
250+
251+ expect ( result . importedTaskCount ) . toBe ( 0 )
252+ expect ( result . importedFileCount ) . toBe ( 0 )
253+ await expect ( fs . access ( path . join ( zooGlobalStoragePath , "tasks" , "task-missing-history" ) ) ) . rejects . toMatchObject (
254+ {
255+ code : "ENOENT" ,
256+ } ,
257+ )
258+ } )
259+
260+ it ( "does not delete an existing Zoo task when the Roo task is missing history_item.json" , async ( ) => {
261+ const zooGlobalStoragePath = path . join ( tempRoot , "globalStorage" , "zoocodeorganization.zoo-code" )
262+ const rooDefaultStorageRoot = path . join ( tempRoot , "globalStorage" , "rooveterinaryinc.roo-cline" )
263+ const existingZooTaskDirectory = path . join ( zooGlobalStoragePath , "tasks" , "task-existing" )
264+
265+ mockStorageConfiguration ( )
266+
267+ await fs . mkdir ( path . join ( rooDefaultStorageRoot , "tasks" , "task-existing" ) , { recursive : true } )
268+ await fs . writeFile ( path . join ( rooDefaultStorageRoot , "tasks" , "task-existing" , "ui_messages.json" ) , "ui only" )
269+ await fs . mkdir ( existingZooTaskDirectory , { recursive : true } )
270+ await fs . writeFile ( path . join ( existingZooTaskDirectory , "history_item.json" ) , "existing" )
271+
272+ const result = await importRooTaskHistory ( zooGlobalStoragePath )
273+
274+ expect ( result . importedTaskCount ) . toBe ( 0 )
275+ expect ( result . importedFileCount ) . toBe ( 0 )
276+ expect ( await fs . readFile ( path . join ( existingZooTaskDirectory , "history_item.json" ) , "utf8" ) ) . toBe ( "existing" )
277+ } )
278+
210279 it ( "rethrows unexpected task-root errors while importing Roo history" , async ( ) => {
211280 const zooGlobalStoragePath = path . join ( tempRoot , "globalStorage" , "zoocodeorganization.zoo-code" )
212281 const rooDefaultStorageRoot = path . join ( tempRoot , "globalStorage" , "rooveterinaryinc.roo-cline" )
0 commit comments