Skip to main content

systemprompt_cli/commands/core/files/
delete.rs

1use 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}