systemprompt-cli 0.2.1

Unified CLI for systemprompt.io AI governance: agent orchestration, MCP governance, analytics, profiles, cloud deploy, and self-hosted operations.
Documentation
use anyhow::{Context, Result};
use clap::Args;
use std::fs;

use crate::CliConfig;
use crate::shared::CommandResult;
use systemprompt_logging::CliService;
use systemprompt_models::content_config::ContentConfigRaw;
use systemprompt_models::profile_bootstrap::ProfileBootstrap;

use super::super::types::{SitemapRoute, SitemapShowOutput};

#[derive(Debug, Clone, Copy, Args)]
pub struct ShowArgs {
    #[arg(long, help = "Show XML preview")]
    pub preview: bool,
}

pub fn execute(args: ShowArgs, _config: &CliConfig) -> Result<CommandResult<SitemapShowOutput>> {
    let profile = ProfileBootstrap::get().context("Failed to get profile")?;
    let content_config_path = profile.paths.content_config();

    let content = fs::read_to_string(&content_config_path)
        .with_context(|| format!("Failed to read content config at {}", content_config_path))?;

    let config: ContentConfigRaw = serde_yaml::from_str(&content)
        .with_context(|| format!("Failed to parse content config at {}", content_config_path))?;

    let mut routes: Vec<SitemapRoute> = Vec::new();

    for (name, source) in &config.content_sources {
        if !source.enabled {
            continue;
        }

        if let Some(sitemap) = &source.sitemap {
            if !sitemap.enabled {
                continue;
            }

            if let Some(parent) = &sitemap.parent_route {
                if parent.enabled {
                    routes.push(SitemapRoute {
                        url: parent.url.clone(),
                        priority: parent.priority,
                        changefreq: parent.changefreq.clone(),
                        source: format!("{} (parent)", name),
                    });
                }
            }

            routes.push(SitemapRoute {
                url: sitemap.url_pattern.clone(),
                priority: sitemap.priority,
                changefreq: sitemap.changefreq.clone(),
                source: name.clone(),
            });
        }
    }

    routes.sort_by(|a, b| {
        b.priority
            .partial_cmp(&a.priority)
            .unwrap_or(std::cmp::Ordering::Equal)
    });

    let total_routes = routes.len();

    if args.preview {
        let xml = generate_xml_preview(&routes);
        CliService::output(&xml);
    }

    let output = SitemapShowOutput {
        routes,
        total_routes,
    };

    Ok(CommandResult::table(output)
        .with_title("Sitemap Configuration")
        .with_columns(vec![
            "url".to_string(),
            "priority".to_string(),
            "changefreq".to_string(),
            "source".to_string(),
        ]))
}

fn generate_xml_preview(routes: &[SitemapRoute]) -> String {
    let mut xml = String::from(concat!(
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
        "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n"
    ));

    for route in routes {
        xml.push_str(&format!(
            "  <url>\n    <loc>{}</loc>\n    <priority>{:.1}</priority>\n    \
             <changefreq>{}</changefreq>\n  </url>\n",
            route.url, route.priority, route.changefreq
        ));
    }

    xml.push_str("</urlset>\n");
    xml
}