fleet-memory/hindsight-cli/src/commands/mental_model.rs
Nicolò Boschi 7097716204
feat: improve mental models ux on control plane (#297)
* feat: improve mental models ux on control plane

* feat: improve mental models ux on control plane

* gen

* feat(cli): add --id flag to mental model create command

* fix(cli): revert unused variable underscore prefix that breaks compilation

The underscore prefix on stdout/stderr variables was added to suppress
warnings, but these variables are actually used in assert messages,
causing compilation errors. Reverting to original names.
2026-02-04 15:49:03 +01:00

287 lines
7.7 KiB
Rust

//! Mental model commands for managing user-curated summaries.
use anyhow::Result;
use crate::api::ApiClient;
use crate::output::{self, OutputFormat};
use crate::ui;
use hindsight_client::types;
/// List mental models for a bank
pub fn list(
client: &ApiClient,
bank_id: &str,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
let spinner = if output_format == OutputFormat::Pretty {
Some(ui::create_spinner("Fetching mental models..."))
} else {
None
};
let response = client.list_mental_models(bank_id, verbose);
if let Some(mut sp) = spinner {
sp.finish();
}
match response {
Ok(result) => {
if output_format == OutputFormat::Pretty {
ui::print_section_header(&format!("Mental Models: {}", bank_id));
if result.items.is_empty() {
println!(" {}", ui::dim("No mental models found."));
} else {
for mental_model in &result.items {
println!(
" {} {}",
ui::gradient_start(&mental_model.id),
mental_model.name
);
// Show content preview
let preview: String = mental_model.content.chars().take(80).collect();
let ellipsis = if mental_model.content.len() > 80 { "..." } else { "" };
println!(" {}{}", ui::dim(&preview), ellipsis);
println!();
}
}
} else {
output::print_output(&result, output_format)?;
}
Ok(())
}
Err(e) => Err(e),
}
}
/// Get a specific mental model
pub fn get(
client: &ApiClient,
bank_id: &str,
mental_model_id: &str,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
let spinner = if output_format == OutputFormat::Pretty {
Some(ui::create_spinner("Fetching mental model..."))
} else {
None
};
let response = client.get_mental_model(bank_id, mental_model_id, verbose);
if let Some(mut sp) = spinner {
sp.finish();
}
match response {
Ok(mental_model) => {
if output_format == OutputFormat::Pretty {
print_mental_model_detail(&mental_model);
} else {
output::print_output(&mental_model, output_format)?;
}
Ok(())
}
Err(e) => Err(e),
}
}
/// Create a new mental model
pub fn create(
client: &ApiClient,
bank_id: &str,
name: &str,
source_query: &str,
id: Option<&str>,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
let spinner = if output_format == OutputFormat::Pretty {
Some(ui::create_spinner("Creating mental model..."))
} else {
None
};
let request = types::CreateMentalModelRequest {
id: id.map(|s| s.to_string()),
name: name.to_string(),
source_query: source_query.to_string(),
max_tokens: 2048,
tags: vec![],
trigger: None,
};
let response = client.create_mental_model(bank_id, &request, verbose);
if let Some(mut sp) = spinner {
sp.finish();
}
match response {
Ok(result) => {
if output_format == OutputFormat::Pretty {
ui::print_success(&format!("Mental model created, operation_id: {}", result.operation_id));
} else {
output::print_output(&result, output_format)?;
}
Ok(())
}
Err(e) => Err(e),
}
}
/// Update a mental model
pub fn update(
client: &ApiClient,
bank_id: &str,
mental_model_id: &str,
name: Option<String>,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
if name.is_none() {
anyhow::bail!("--name must be provided");
}
let spinner = if output_format == OutputFormat::Pretty {
Some(ui::create_spinner("Updating mental model..."))
} else {
None
};
let request = types::UpdateMentalModelRequest {
name,
source_query: None,
max_tokens: None,
tags: None,
trigger: None,
};
let response = client.update_mental_model(bank_id, mental_model_id, &request, verbose);
if let Some(mut sp) = spinner {
sp.finish();
}
match response {
Ok(mental_model) => {
if output_format == OutputFormat::Pretty {
ui::print_success(&format!("Mental model '{}' updated successfully", mental_model_id));
println!();
print_mental_model_detail(&mental_model);
} else {
output::print_output(&mental_model, output_format)?;
}
Ok(())
}
Err(e) => Err(e),
}
}
/// Delete a mental model
pub fn delete(
client: &ApiClient,
bank_id: &str,
mental_model_id: &str,
yes: bool,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
// Confirmation prompt unless -y flag is used
if !yes && output_format == OutputFormat::Pretty {
let message = format!(
"Are you sure you want to delete mental model '{}'? This cannot be undone.",
mental_model_id
);
let confirmed = ui::prompt_confirmation(&message)?;
if !confirmed {
ui::print_info("Operation cancelled");
return Ok(());
}
}
let spinner = if output_format == OutputFormat::Pretty {
Some(ui::create_spinner("Deleting mental model..."))
} else {
None
};
let response = client.delete_mental_model(bank_id, mental_model_id, verbose);
if let Some(mut sp) = spinner {
sp.finish();
}
match response {
Ok(_) => {
if output_format == OutputFormat::Pretty {
ui::print_success(&format!("Mental model '{}' deleted successfully", mental_model_id));
} else {
println!("{{\"success\": true}}");
}
Ok(())
}
Err(e) => Err(e),
}
}
/// Refresh a mental model
pub fn refresh(
client: &ApiClient,
bank_id: &str,
mental_model_id: &str,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
let spinner = if output_format == OutputFormat::Pretty {
Some(ui::create_spinner("Submitting mental model refresh..."))
} else {
None
};
let response = client.refresh_mental_model(bank_id, mental_model_id, verbose);
if let Some(mut sp) = spinner {
sp.finish();
}
match response {
Ok(operation) => {
if output_format == OutputFormat::Pretty {
ui::print_success(&format!(
"Mental model refresh submitted. Operation ID: {}",
operation.operation_id
));
println!(" {} {}", ui::dim("Status:"), operation.status);
println!();
println!("{}", ui::dim("Use 'hindsight operations get' to check the operation status."));
} else {
output::print_output(&operation, output_format)?;
}
Ok(())
}
Err(e) => Err(e),
}
}
// Helper function to print mental model details
fn print_mental_model_detail(mental_model: &types::MentalModelResponse) {
ui::print_section_header(&mental_model.name);
println!(" {} {}", ui::dim("ID:"), ui::gradient_start(&mental_model.id));
println!(" {} {}", ui::dim("Source Query:"), &mental_model.source_query);
println!();
println!("{}", ui::gradient_text("─── Content ───"));
println!();
println!("{}", &mental_model.content);
println!();
}