Skip to main content

systemprompt_cli/commands/core/artifacts/
list.rs

1//! `core artifacts list` command.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use anyhow::{Context, Result};
7use clap::Args;
8use systemprompt_agent::repository::content::artifact::ArtifactRepository;
9use systemprompt_database::DbPool;
10use systemprompt_identifiers::ContextId;
11use systemprompt_logging::CliService;
12
13use super::types::{ArtifactListOutput, ArtifactSummary};
14use crate::cli_settings::CliConfig;
15use crate::context::CommandContext;
16use crate::presentation::tables::artifact_list_table;
17use crate::session::get_or_create_session;
18use crate::shared::CommandOutput;
19
20#[derive(Debug, Args)]
21pub struct ListArgs {
22    #[arg(long = "context-id", short = 'c', help = "Filter by context ID")]
23    pub context: Option<String>,
24
25    #[arg(
26        long,
27        short = 'l',
28        default_value = "20",
29        help = "Maximum artifacts to show"
30    )]
31    pub limit: i32,
32}
33
34pub(super) async fn execute(args: ListArgs, ctx: &CommandContext) -> Result<CommandOutput> {
35    let session_ctx = get_or_create_session(ctx).await?;
36    let pool = ctx.db_pool().await?;
37    execute_with_pool(args, &session_ctx.session.user_id, &pool, &ctx.cli).await
38}
39
40pub async fn execute_with_pool(
41    args: ListArgs,
42    user_id: &systemprompt_identifiers::UserId,
43    pool: &DbPool,
44    config: &CliConfig,
45) -> Result<CommandOutput> {
46    let repo = ArtifactRepository::new(pool)?;
47
48    let artifacts = if let Some(ref ctx_id) = args.context {
49        let context_id = ContextId::new(ctx_id);
50        repo.get_artifacts_by_context(&context_id)
51            .await
52            .context("Failed to fetch artifacts by context")?
53    } else {
54        repo.get_artifacts_by_user_id(user_id, Some(args.limit))
55            .await
56            .context("Failed to fetch artifacts")?
57    };
58
59    let summaries: Vec<ArtifactSummary> = artifacts
60        .iter()
61        .take(args.limit as usize)
62        .map(|a| ArtifactSummary {
63            artifact_id: a.id.clone(),
64            name: a.title.clone(),
65            artifact_type: a.metadata.artifact_type.clone(),
66            tool_name: a.metadata.tool_name.clone(),
67            task_id: a.metadata.task_id.clone(),
68            created_at: chrono::DateTime::parse_from_rfc3339(&a.metadata.created_at)
69                .map_or_else(|_| chrono::Utc::now(), |dt| dt.with_timezone(&chrono::Utc)),
70        })
71        .collect();
72
73    let total = summaries.len();
74
75    let output = ArtifactListOutput {
76        artifacts: summaries.clone(),
77        total,
78        context: args.context.clone(),
79    };
80
81    if !config.is_json_output() {
82        CliService::section("Artifacts");
83
84        if summaries.is_empty() {
85            CliService::info("No artifacts found");
86        } else {
87            CliService::output(&artifact_list_table(&summaries));
88
89            CliService::info(&format!("Showing {} artifact(s)", total));
90        }
91    }
92
93    Ok(CommandOutput::table_of(
94        vec!["id", "name", "artifact_type", "tool_name", "created_at"],
95        &output.artifacts,
96    )
97    .with_title("Artifacts"))
98}