Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ inquire = "0.7"
console = "0.15"
regex = "1.10"
lazy_static = "1.4"
dotenvy = "0.15"

[features]
default = ["reqwest"]
Expand Down
14 changes: 14 additions & 0 deletions migrations/012_add_flowcharts.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- Add flowcharts table for storing session context flow analysis
CREATE TABLE flowcharts (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
nodes TEXT NOT NULL, -- JSON array of FlowchartNode
edges TEXT NOT NULL, -- JSON array of FlowchartEdge
created_at TEXT NOT NULL DEFAULT (datetime('now', 'utc')),
token_usage INTEGER,
FOREIGN KEY (session_id) REFERENCES chat_sessions(id) ON DELETE CASCADE
);

-- Indexes for performance
CREATE INDEX idx_flowcharts_session_id ON flowcharts(session_id);
CREATE INDEX idx_flowcharts_created_at ON flowcharts(created_at);
139 changes: 139 additions & 0 deletions src/cli/flowchart.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use anyhow::{Context, Result};
use clap::Subcommand;
use std::sync::Arc;

use crate::database::DatabaseManager;
use crate::services::{FlowchartService, GoogleAiClient, GoogleAiConfig};

#[derive(Subcommand)]
pub enum FlowchartCommands {
/// Generate flowchart for a session
Generate {
/// Session ID to generate flowchart for
session_id: String,

/// Force regenerate even if flowchart exists
#[arg(short, long)]
force: bool,
},
/// Show flowchart for a session
Show {
/// Session ID to show flowchart for
session_id: String,
},
/// Delete flowchart for a session
Delete {
/// Session ID to delete flowchart for
session_id: String,
},
}

pub async fn handle_flowchart_command(command: FlowchartCommands) -> Result<()> {
match command {
FlowchartCommands::Generate { session_id, force } => {
generate_flowchart(&session_id, force).await
}
FlowchartCommands::Show { session_id } => show_flowchart(&session_id).await,
FlowchartCommands::Delete { session_id } => delete_flowchart(&session_id).await,
}
}

async fn generate_flowchart(session_id: &str, force: bool) -> Result<()> {
let db_path = crate::database::config::get_default_db_path()?;
let db_manager = Arc::new(DatabaseManager::new(&db_path).await?);

// Get Google AI API key
let api_key = std::env::var("GOOGLE_AI_API_KEY")
.context("GOOGLE_AI_API_KEY not set. Please set this environment variable.")?;

let config = GoogleAiConfig::new(api_key);
let client = GoogleAiClient::new(config)?;
let service = FlowchartService::new(db_manager, client);

println!("Generating flowchart for session: {session_id}");

if force {
// Delete existing flowchart first
service
.delete_flowchart(session_id)
.await
.map_err(|e| anyhow::anyhow!("Failed to delete existing flowchart: {e}"))?;
}

let flowchart = service
.get_or_generate_flowchart(session_id)
.await
.map_err(|e| anyhow::anyhow!("Failed to generate flowchart: {e}"))?;

println!("✓ Flowchart generated successfully!");
println!(" - {} nodes", flowchart.nodes.len());
println!(" - {} edges", flowchart.edges.len());
if let Some(tokens) = flowchart.token_usage {
println!(" - {tokens} tokens used");
}
println!("\nUse 'retrochat flowchart show {session_id}' to view the flowchart");
Ok(())
}

async fn show_flowchart(session_id: &str) -> Result<()> {
let db_path = crate::database::config::get_default_db_path()?;
let db_manager = Arc::new(DatabaseManager::new(&db_path).await?);
let flowchart_repo = crate::database::FlowchartRepository::new(db_manager);

let flowcharts = flowchart_repo
.get_by_session_id(session_id)
.await
.map_err(|e| anyhow::anyhow!("Error loading flowchart: {e}"))?;

if let Some(flowchart) = flowcharts.first() {
println!("Flowchart for session: {session_id}\n");

// Render using the same renderer as TUI
use crate::tui::flowchart_renderer::FlowchartRenderer;
let renderer = FlowchartRenderer::new(80);
let lines = renderer.render(flowchart);

for line in lines {
// Extract text from Line (ratatui type)
let text: String = line
.spans
.iter()
.map(|span| span.content.as_ref())
.collect();
println!("{text}");
}

println!("\nMetadata:");
println!(
" Created: {}",
flowchart.created_at.format("%Y-%m-%d %H:%M:%S")
);
if let Some(tokens) = flowchart.token_usage {
println!(" Tokens: {tokens}");
}
println!(" Nodes: {}", flowchart.nodes.len());
println!(" Edges: {}", flowchart.edges.len());

Ok(())
} else {
println!("No flowchart found for session: {session_id}");
println!("Generate one with: retrochat flowchart generate {session_id}");
Ok(())
}
}

async fn delete_flowchart(session_id: &str) -> Result<()> {
let db_path = crate::database::config::get_default_db_path()?;
let db_manager = Arc::new(DatabaseManager::new(&db_path).await?);
let flowchart_repo = crate::database::FlowchartRepository::new(db_manager);

println!("Deleting flowchart for session: {session_id}");

flowchart_repo
.delete_by_session_id(session_id)
.await
.map_err(|e| anyhow::anyhow!("Failed to delete flowchart: {e}"))?;

println!("✓ Flowchart deleted successfully");
Ok(())
}
10 changes: 10 additions & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod analytics;
pub mod flowchart;
pub mod help;
pub mod import;
pub mod init;
Expand All @@ -14,6 +15,7 @@ use tokio::runtime::Runtime;

use crate::env::apis as env_vars;
use crate::models::Provider;
use flowchart::FlowchartCommands;
use retrospect::RetrospectCommands;

#[derive(Parser)]
Expand Down Expand Up @@ -94,6 +96,11 @@ pub enum Commands {
#[command(subcommand)]
command: RetrospectCommands,
},
/// Generate context flowchart for chat sessions
Flowchart {
#[command(subcommand)]
command: FlowchartCommands,
},
/// Interactive setup wizard for first-time users
Setup,
/// [Alias for 'import'] Add chat files interactively or from providers
Expand Down Expand Up @@ -326,6 +333,9 @@ impl Cli {
retrospect::handle_cancel_command(request_id, all).await
}
},
Commands::Flowchart { command } => {
flowchart::handle_flowchart_command(command).await
}
// New commands
Commands::Setup => setup::run_setup_wizard().await,
Commands::Add {
Expand Down
Loading