systemprompt_cli/commands/core/files/
delete.rs1use anyhow::{Result, anyhow};
7use clap::Args;
8use systemprompt_database::DbPool;
9use systemprompt_files::FileRepository;
10use systemprompt_identifiers::FileId;
11use systemprompt_runtime::AppContext;
12
13use super::types::FileDeleteOutput;
14use crate::CliConfig;
15use crate::interactive::Prompter;
16use crate::shared::CommandOutput;
17
18#[derive(Debug, Clone, Args)]
19pub struct DeleteArgs {
20 #[arg(value_name = "FILE_ID", help = "File ID (UUID format)")]
21 pub file: String,
22
23 #[arg(
24 short = 'y',
25 long,
26 help = "Skip confirmation (required in non-interactive mode)"
27 )]
28 pub yes: bool,
29
30 #[arg(long, help = "Preview deletion without executing")]
31 pub dry_run: bool,
32}
33
34pub(super) async fn execute(
35 args: DeleteArgs,
36 prompter: &dyn Prompter,
37 config: &CliConfig,
38) -> Result<CommandOutput> {
39 let ctx = AppContext::new().await?;
40 execute_with_pool(args, prompter, ctx.db_pool(), config).await
41}
42
43pub async fn execute_with_pool(
44 args: DeleteArgs,
45 prompter: &dyn Prompter,
46 pool: &DbPool,
47 config: &CliConfig,
48) -> Result<CommandOutput> {
49 let file_id = parse_file_id(&args.file)?;
50
51 let service = FileRepository::new(pool)?;
52
53 let file = service
54 .find_by_id(&file_id)
55 .await?
56 .ok_or_else(|| anyhow!("File not found: {}", args.file))?;
57
58 if args.dry_run {
59 let output = FileDeleteOutput {
60 file_id,
61 message: format!(
62 "[DRY-RUN] Would delete file '{}' ({})",
63 file.path, args.file
64 ),
65 };
66 return Ok(CommandOutput::card_value("File Delete (Dry Run)", &output));
67 }
68
69 if !args.yes {
70 if config.is_interactive() {
71 let confirmed = prompter.confirm(
72 &format!(
73 "Delete file '{}' ({})? This action cannot be undone.",
74 file.path, args.file
75 ),
76 false,
77 )?;
78
79 if !confirmed {
80 return Err(anyhow!("Deletion cancelled by user"));
81 }
82 } else {
83 return Err(anyhow!(
84 "--yes is required to delete files in non-interactive mode"
85 ));
86 }
87 }
88
89 service.delete(&file_id).await?;
90
91 let output = FileDeleteOutput {
92 file_id,
93 message: format!("File '{}' deleted successfully", file.path),
94 };
95
96 Ok(CommandOutput::card_value("File Deleted", &output))
97}
98
99fn parse_file_id(id: &str) -> Result<FileId> {
100 uuid::Uuid::parse_str(id).map_err(|_e| {
101 anyhow!(
102 "Invalid file ID format. Expected UUID like 'b75940ac-c50f-4d46-9fdd-ebb4970b2a7d', \
103 got '{}'",
104 id
105 )
106 })?;
107 Ok(FileId::new(id.to_owned()))
108}