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