Skip to main content

systemprompt_cli/commands/core/content/
mod.rs

1//! `core content` command group: manage and inspect published content.
2//!
3//! Dispatches the [`ContentCommands`] subcommands — list/show/search,
4//! edit/delete, ingest verification and status, plus the `link`, `analytics`,
5//! and `files` sub-groups. On a `--database-url` invocation only the read-only
6//! commands are served; the mutating commands require a full profile context.
7//!
8//! Copyright (c) systemprompt.io — Business Source License 1.1.
9//! See <https://systemprompt.io> for licensing details.
10
11pub mod analytics;
12pub mod delete;
13pub mod delete_source;
14pub mod edit;
15pub mod edit_apply;
16pub mod files;
17pub mod link;
18pub mod list;
19pub mod popular;
20pub mod search;
21pub mod show;
22pub mod status;
23pub mod types;
24pub mod verify;
25
26use crate::context::CommandContext;
27use crate::shared::render_result;
28use anyhow::{Context, Result, bail};
29use clap::Subcommand;
30
31#[derive(Debug, Subcommand)]
32pub enum ContentCommands {
33    #[command(about = "List content with pagination")]
34    List(list::ListArgs),
35
36    #[command(about = "Show content details")]
37    Show(show::ShowArgs),
38
39    #[command(about = "Search content")]
40    Search(search::SearchArgs),
41
42    #[command(about = "Edit content fields")]
43    Edit(edit::EditArgs),
44
45    #[command(about = "Delete content by ID")]
46    Delete(delete::DeleteArgs),
47
48    #[command(name = "delete-source", about = "Delete all content from a source")]
49    DeleteSource(delete_source::DeleteSourceArgs),
50
51    #[command(about = "Get popular content")]
52    Popular(popular::PopularArgs),
53
54    #[command(about = "Verify content is published and accessible")]
55    Verify(verify::VerifyArgs),
56
57    #[command(about = "Show content health status for a source")]
58    Status(status::StatusArgs),
59
60    #[command(subcommand, about = "Link generation and management")]
61    Link(link::LinkCommands),
62
63    #[command(subcommand, about = "Content analytics")]
64    Analytics(analytics::AnalyticsCommands),
65
66    #[command(subcommand, about = "Content-file operations (link, unlink, featured)")]
67    Files(files::ContentFilesCommands),
68}
69
70fn ensure_full_profile(command: &ContentCommands, ctx: &CommandContext) -> Result<()> {
71    if ctx.is_database_scoped()
72        && matches!(
73            command,
74            ContentCommands::Edit(_)
75                | ContentCommands::Delete(_)
76                | ContentCommands::DeleteSource(_)
77                | ContentCommands::Verify(_)
78                | ContentCommands::Link(_)
79                | ContentCommands::Files(_)
80        )
81    {
82        bail!("This content command requires full profile context");
83    }
84    Ok(())
85}
86
87pub async fn execute(command: ContentCommands, ctx: &CommandContext) -> Result<()> {
88    ensure_full_profile(&command, ctx)?;
89
90    match command {
91        ContentCommands::List(args) => {
92            let result = list::execute(args, ctx)
93                .await
94                .context("Failed to list content")?;
95            render_result(&result, &ctx.cli);
96        },
97        ContentCommands::Show(args) => {
98            let result = show::execute(args, ctx)
99                .await
100                .context("Failed to show content")?;
101            render_result(&result, &ctx.cli);
102        },
103        ContentCommands::Search(args) => {
104            let result = search::execute(args, ctx)
105                .await
106                .context("Failed to search content")?;
107            render_result(&result, &ctx.cli);
108        },
109        ContentCommands::Edit(args) => {
110            let result = edit::execute(args, ctx.prompter(), &ctx.cli)
111                .await
112                .context("Failed to edit content")?;
113            render_result(&result, &ctx.cli);
114        },
115        ContentCommands::Delete(args) => {
116            let result = delete::execute(args, ctx)
117                .await
118                .context("Failed to delete content")?;
119            render_result(&result, &ctx.cli);
120        },
121        ContentCommands::DeleteSource(args) => {
122            let result = delete_source::execute(args, ctx.prompter(), &ctx.cli)
123                .await
124                .context("Failed to delete source content")?;
125            render_result(&result, &ctx.cli);
126        },
127        ContentCommands::Popular(args) => {
128            let result = popular::execute(args, ctx)
129                .await
130                .context("Failed to get popular content")?;
131            render_result(&result, &ctx.cli);
132        },
133        ContentCommands::Verify(args) => {
134            let result = verify::execute(args, ctx)
135                .await
136                .context("Failed to verify content")?;
137            render_result(&result, &ctx.cli);
138        },
139        ContentCommands::Status(args) => {
140            let result = status::execute(args, ctx)
141                .await
142                .context("Failed to get content status")?;
143            render_result(&result, &ctx.cli);
144        },
145        ContentCommands::Link(cmd) => {
146            link::execute(cmd, ctx).await?;
147        },
148        ContentCommands::Analytics(cmd) => {
149            analytics::execute(cmd, ctx).await?;
150        },
151        ContentCommands::Files(cmd) => {
152            files::execute(cmd, ctx.prompter(), &ctx.cli).await?;
153        },
154    }
155    Ok(())
156}