| 299 | } |
| 300 | |
| 301 | fn remove_cursor_plugin_install(install_dir: &Path) -> Result<()> { |
| 302 | let Ok(metadata) = std::fs::symlink_metadata(install_dir) else { |
| 303 | return Ok(()); |
| 304 | }; |
| 305 | if metadata.file_type().is_symlink() || metadata.is_file() { |
| 306 | std::fs::remove_file(install_dir).map_err(|e| TraceDecayError::Config { |
| 307 | message: format!("failed to remove {}: {e}", install_dir.display()), |
| 308 | })?; |
| 309 | return Ok(()); |
| 310 | } |
| 311 | if !metadata.is_dir() { |
| 312 | return Err(TraceDecayError::Config { |
| 313 | message: format!( |
| 314 | "refusing to replace non-directory Cursor plugin path {}", |
| 315 | install_dir.display() |
| 316 | ), |
| 317 | }); |
| 318 | } |
| 319 | if !cursor_plugin_dir_is_tracedecay(install_dir) { |
| 320 | return Err(TraceDecayError::Config { |
| 321 | message: format!( |
| 322 | "refusing to replace unmanaged Cursor plugin directory {}", |
| 323 | install_dir.display() |
| 324 | ), |
| 325 | }); |
| 326 | } |
| 327 | // The directory is tracedecay-owned. Sweep every skill dir the *current* |
| 328 | // bundle no longer ships (retired dispatcher/workflow/memory skills), then |
| 329 | // remove the managed skill overlay. Deriving the keep-set from the live |
| 330 | // bundle means a newly retired skill is swept automatically — no |
| 331 | // hand-maintained legacy list to fall out of date. User-added files |
| 332 | // outside `skills/` (and any non-tracedecay skill dir) are preserved. |
| 333 | sweep_retired_bundle_skill_dirs(install_dir); |
| 334 | remove_cursor_managed_skill_overlay(install_dir); |
| 335 | if cursor_plugin_dir_has_only_managed_files(install_dir) { |
| 336 | std::fs::remove_dir_all(install_dir).map_err(|e| TraceDecayError::Config { |
| 337 | message: format!("failed to remove {}: {e}", install_dir.display()), |
| 338 | })?; |
| 339 | } else { |
| 340 | for path in cursor_plugin_managed_paths(install_dir) { |
| 341 | std::fs::remove_file(&path).ok(); |
| 342 | } |
| 343 | } |
| 344 | Ok(()) |
| 345 | } |
| 346 | |
| 347 | fn remove_cursor_managed_skill_overlay(install_dir: &Path) { |
| 348 | std::fs::remove_dir_all(install_dir.join("skills/agent-managed")).ok(); |