fleet-memory/hindsight-cli/src/commands/directive.rs
Nicolò Boschi 9db64ecda3
feat: revisit mental models, directives and reflections (#179)
* chore: run benchmarks with reflect mode

* chore: run benchmarks with reflect mode

* fixes

* new mm

* bunch of fixes

* initial commit

* fixes

* fixes

* fixes

* fix: sometimes memories gets extracted in the wrong language
2026-01-22 17:13:16 +01:00

266 lines
7.1 KiB
Rust

//! Directive commands for managing behavioral rules.
use anyhow::Result;
use crate::api::ApiClient;
use crate::output::{self, OutputFormat};
use crate::ui;
use hindsight_client::types;
/// List directives 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 directives..."))
} else {
None
};
let response = client.list_directives(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!("Directives: {}", bank_id));
if result.items.is_empty() {
println!(" {}", ui::dim("No directives found."));
} else {
for directive in &result.items {
let status = if directive.is_active {
ui::gradient_start("active")
} else {
ui::dim("inactive")
};
println!(
" {} {} [{}]",
ui::gradient_start(&directive.id),
directive.name,
status
);
// Show content preview
let preview: String = directive.content.chars().take(80).collect();
let ellipsis = if directive.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 directive
pub fn get(
client: &ApiClient,
bank_id: &str,
directive_id: &str,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
let spinner = if output_format == OutputFormat::Pretty {
Some(ui::create_spinner("Fetching directive..."))
} else {
None
};
let response = client.get_directive(bank_id, directive_id, verbose);
if let Some(mut sp) = spinner {
sp.finish();
}
match response {
Ok(directive) => {
if output_format == OutputFormat::Pretty {
print_directive_detail(&directive);
} else {
output::print_output(&directive, output_format)?;
}
Ok(())
}
Err(e) => Err(e),
}
}
/// Create a new directive
pub fn create(
client: &ApiClient,
bank_id: &str,
name: &str,
content: &str,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
let spinner = if output_format == OutputFormat::Pretty {
Some(ui::create_spinner("Creating directive..."))
} else {
None
};
let request = types::CreateDirectiveRequest {
name: name.to_string(),
content: content.to_string(),
is_active: true,
priority: 0,
tags: vec![],
};
let response = client.create_directive(bank_id, &request, verbose);
if let Some(mut sp) = spinner {
sp.finish();
}
match response {
Ok(directive) => {
if output_format == OutputFormat::Pretty {
ui::print_success(&format!("Directive '{}' created successfully", directive.id));
println!();
print_directive_detail(&directive);
} else {
output::print_output(&directive, output_format)?;
}
Ok(())
}
Err(e) => Err(e),
}
}
/// Update a directive
pub fn update(
client: &ApiClient,
bank_id: &str,
directive_id: &str,
name: Option<String>,
content: Option<String>,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
if name.is_none() && content.is_none() {
anyhow::bail!("At least one of --name or --content must be provided");
}
let spinner = if output_format == OutputFormat::Pretty {
Some(ui::create_spinner("Updating directive..."))
} else {
None
};
let request = types::UpdateDirectiveRequest {
name,
content,
is_active: None,
priority: None,
tags: None,
};
let response = client.update_directive(bank_id, directive_id, &request, verbose);
if let Some(mut sp) = spinner {
sp.finish();
}
match response {
Ok(directive) => {
if output_format == OutputFormat::Pretty {
ui::print_success(&format!("Directive '{}' updated successfully", directive_id));
println!();
print_directive_detail(&directive);
} else {
output::print_output(&directive, output_format)?;
}
Ok(())
}
Err(e) => Err(e),
}
}
/// Delete a directive
pub fn delete(
client: &ApiClient,
bank_id: &str,
directive_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 directive '{}'? This cannot be undone.",
directive_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 directive..."))
} else {
None
};
let response = client.delete_directive(bank_id, directive_id, verbose);
if let Some(mut sp) = spinner {
sp.finish();
}
match response {
Ok(_) => {
if output_format == OutputFormat::Pretty {
ui::print_success(&format!("Directive '{}' deleted successfully", directive_id));
} else {
println!("{{\"success\": true}}");
}
Ok(())
}
Err(e) => Err(e),
}
}
// Helper function to print directive details
fn print_directive_detail(directive: &types::DirectiveResponse) {
ui::print_section_header(&directive.name);
println!(" {} {}", ui::dim("ID:"), ui::gradient_start(&directive.id));
let status = if directive.is_active {
ui::gradient_start("active")
} else {
ui::dim("inactive")
};
println!(" {} {}", ui::dim("Status:"), status);
println!(" {} {}", ui::dim("Priority:"), directive.priority);
if !directive.tags.is_empty() {
println!(" {} {}", ui::dim("Tags:"), directive.tags.join(", "));
}
println!();
println!("{}", ui::gradient_text("─── Content ───"));
println!();
println!("{}", &directive.content);
println!();
}