Sync Claude Code's own plugin registry with the refreshed marketplace bundle. Claude Code copies installed plugins into a versioned cache (`~/.claude/plugins/cache/...`) recorded in `installed_plugins.json`, so refreshing the marketplace directory alone leaves running installs on the stale cached version. Drive Claude Code's own CLI (`claude plugin install|update`) instead of writing its internal
(home: &Path)
| 489 | /// runs against the real user home (temp-home installs in tests skip it), |
| 490 | /// and a missing/failed `claude` CLI degrades to the existing restart hint. |
| 491 | fn sync_claude_plugin_cache(home: &Path) { |
| 492 | let is_real_home = dirs::home_dir().is_some_and(|real| real == home); |
| 493 | if !is_real_home { |
| 494 | return; |
| 495 | } |
| 496 | let registry = load_json_file(&home.join(".claude/plugins/installed_plugins.json")); |
| 497 | let installed = registry |
| 498 | .get("plugins") |
| 499 | .and_then(|plugins| plugins.get(PLUGIN_IDENTIFIER)) |
| 500 | .is_some(); |
| 501 | let action = if installed { "update" } else { "install" }; |
| 502 | let output = std::process::Command::new("claude") |
| 503 | .args(["plugin", action, PLUGIN_IDENTIFIER]) |
| 504 | .output(); |
| 505 | match output { |
| 506 | Ok(output) if output.status.success() => { |
| 507 | eprintln!( |
| 508 | "\x1b[32m\u{2714}\x1b[0m Synced Claude Code plugin cache (claude plugin {action})" |
| 509 | ); |
| 510 | } |
| 511 | Ok(output) => { |
| 512 | let stderr = String::from_utf8_lossy(&output.stderr); |
| 513 | eprintln!( |
| 514 | " Could not sync Claude Code plugin cache (claude plugin {action}): {}", |
| 515 | stderr.trim().lines().last().unwrap_or("unknown error") |
| 516 | ); |
| 517 | } |
| 518 | Err(err) => { |
| 519 | eprintln!(" Could not run claude plugin {action}: {err}"); |
| 520 | } |
| 521 | } |
| 522 | } |
| 523 | |
| 524 | /// Remove the tracedecay marketplace entry from `known_marketplaces.json`, |
| 525 | /// preserving every other marketplace. Idempotent. |
no test coverage detected