From 85405ebdc63a3b040c1d467268d62c5de4e83e4d Mon Sep 17 00:00:00 2001 From: Peolite001 Date: Tue, 2 Jun 2026 17:26:35 +0100 Subject: [PATCH] feat: add --purge flag to 'starforge template remove' - Without --purge: only removes registry metadata (safe default) - With --purge: also deletes cached and stored template assets - Implements #253 --- README.md | 6 ++++++ src/commands/template.rs | 17 ++++++++++++++--- src/utils/templates.rs | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6fbda40d..c6e673f6 100644 --- a/README.md +++ b/README.md @@ -500,3 +500,9 @@ StarForge has comprehensive documentation covering all aspects of the project: For a complete overview, see [DOCUMENTATION_INDEX.md](DOCUMENTATION_INDEX.md). + +# Remove a template +starforge template remove my-template + +# Remove template + delete all local files +starforge template remove my-template --purge \ No newline at end of file diff --git a/src/commands/template.rs b/src/commands/template.rs index 753d48e1..15353e96 100644 --- a/src/commands/template.rs +++ b/src/commands/template.rs @@ -98,6 +98,10 @@ pub enum TemplateCommands { Remove { /// Template name name: String, + + /// Also delete cached files and downloaded assets + #[arg(long)] + purge: bool, }, /// Initialize the template registry with example templates Init, @@ -198,6 +202,8 @@ pub fn handle(cmd: TemplateCommands) -> Result<()> { force, } => install(source, name, version, force), TemplateCommands::Update { name, all } => update(name, all), + // In the match arm +TemplateCommands::Remove { name, purge } => remove(name, purge), } } @@ -539,9 +545,14 @@ fn print_quality_signals(template: &templates::TemplateEntry) { } } -fn remove(name: String) -> Result<()> { - templates::remove_template(&name)?; - p::success(&format!("Template '{}' removed", name)); +fn remove(name: String, purge: bool) -> Result<()> { + templates::remove_template(&name, purge)?; + + if purge { + p::success(&format!("Template '{}' and all local assets removed", name)); + } else { + p::success(&format!("Template '{}' removed from registry (use --purge to also delete cached files)", name)); + } Ok(()) } diff --git a/src/utils/templates.rs b/src/utils/templates.rs index bbfd86d5..1bf87f3e 100644 --- a/src/utils/templates.rs +++ b/src/utils/templates.rs @@ -792,9 +792,12 @@ pub fn add_template(entry: TemplateEntry) -> Result<()> { Ok(()) } -pub fn remove_template(name: &str) -> Result<()> { +/// Remove a template from the registry. +/// If `purge` is true, also deletes any cached/downloaded assets. +pub fn remove_template(name: &str, purge: bool) -> Result<()> { let mut registry = load_registry()?; let before = registry.templates.len(); + registry.templates.retain(|t| t.name != name); if registry.templates.len() == before { @@ -802,6 +805,35 @@ pub fn remove_template(name: &str) -> Result<()> { } save_registry(®istry)?; + + // Purge local assets if requested + if purge { + purge_template_assets(name)?; + } + + Ok(()) +} + +/// Delete all local cached and stored assets for a template +fn purge_template_assets(name: &str) -> Result<()> { + // 1. Purge from template storage directory + if let Ok(storage_dir) = template_storage_dir() { + let template_path = storage_dir.join(name); + if template_path.exists() { + fs::remove_dir_all(&template_path) + .with_context(|| format!("Failed to purge stored template at {}", template_path.display()))?; + } + } + + // 2. Purge from cache directory + if let Ok(cache_dir) = template_cache_dir() { + let cache_path = cache_dir.join(name); + if cache_path.exists() { + fs::remove_dir_all(&cache_path) + .with_context(|| format!("Failed to purge cached template at {}", cache_path.display()))?; + } + } + Ok(()) }