Skip to main content

systemprompt_cli/commands/analytics/conversations/
mod.rs

1//! Conversation analytics: aggregate stats, time-series trends, and listings.
2//!
3//! Defines the [`ConversationsCommands`] subcommand tree and the typed output
4//! shapes ([`ConversationStatsOutput`], [`ConversationTrendsOutput`],
5//! [`ConversationListOutput`]) rendered by the `analytics conversations`
6//! commands.
7
8mod list;
9mod stats;
10mod trends;
11
12use anyhow::Result;
13use clap::Subcommand;
14use schemars::JsonSchema;
15use serde::{Deserialize, Serialize};
16
17use crate::context::CommandContext;
18use crate::shared::render_result;
19
20#[derive(Debug, Subcommand)]
21pub enum ConversationsCommands {
22    #[command(about = "Conversation statistics")]
23    Stats(stats::StatsArgs),
24
25    #[command(about = "Conversation trends over time")]
26    Trends(trends::TrendsArgs),
27
28    #[command(about = "List conversations")]
29    List(list::ListArgs),
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
33pub struct ConversationStatsOutput {
34    pub period: String,
35    pub total_contexts: i64,
36    pub total_tasks: i64,
37    pub total_messages: i64,
38    pub avg_messages_per_task: f64,
39    pub avg_task_duration_ms: i64,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
43pub struct ConversationTrendPoint {
44    pub timestamp: String,
45    pub context_count: i64,
46    pub task_count: i64,
47    pub message_count: i64,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
51pub struct ConversationTrendsOutput {
52    pub period: String,
53    pub group_by: String,
54    pub points: Vec<ConversationTrendPoint>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
58pub struct ConversationListRow {
59    #[serde(rename = "context_id")]
60    pub context: String,
61    pub name: Option<String>,
62    pub task_count: i64,
63    pub message_count: i64,
64    pub created_at: String,
65    pub updated_at: String,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
69pub struct ConversationListOutput {
70    pub conversations: Vec<ConversationListRow>,
71    pub total: i64,
72}
73
74pub async fn execute(command: ConversationsCommands, ctx: &CommandContext) -> Result<()> {
75    let db_ctx = ctx.database().await?;
76    match command {
77        ConversationsCommands::Stats(args) => {
78            let result = stats::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
79            render_result(&result, &ctx.cli);
80            Ok(())
81        },
82        ConversationsCommands::Trends(args) => {
83            let result = trends::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
84            render_result(&result, &ctx.cli);
85            Ok(())
86        },
87        ConversationsCommands::List(args) => {
88            let result = list::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
89            render_result(&result, &ctx.cli);
90            Ok(())
91        },
92    }
93}