mirror of
https://github.com/block/buzz.git
synced 2026-11-01 15:01:12 +00:00
Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <wpfleger@block.xyz> Signed-off-by: Will Pfleger <wpfleger@squareup.com> Signed-off-by: Will Pfleger <wpfleger96@gmail.com> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub1fgdl5qqnh3k3f2xkqrvt7cujalhm623x4s7fdjdj5yrtp5fzjl9qrjpucw <4a1bfa0013bc6d14a8d600d8bf6392efefbd2a26ac3c96c9b2a106b0d12297ca@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub16v54tttfqacx9ycvc3k0ut0npj564ahcuajzy6qjvh57ntmsf4uq4806j2 <d32955ad69077062930cc46cfe2df30ca9aaf6f8e76422681265e9e9af704d78@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Will Pfleger <wpfleger96@gmail.com>
152 lines
4.9 KiB
Rust
152 lines
4.9 KiB
Rust
//! `buzz pack` subcommands — local persona pack operations.
|
|
//!
|
|
//! These commands operate on local pack directories. No relay connection needed.
|
|
|
|
use std::path::Path;
|
|
|
|
use crate::error::CliError;
|
|
|
|
/// Run `buzz pack validate <path>`.
|
|
///
|
|
/// Calls `validate_pack()` from the persona crate, prints diagnostics,
|
|
/// and exits with the appropriate code:
|
|
/// - 0: valid (may have warnings)
|
|
/// - 1: errors found
|
|
pub fn cmd_validate(path: &str) -> Result<(), CliError> {
|
|
let pack_dir = Path::new(path);
|
|
if !pack_dir.exists() {
|
|
return Err(CliError::Usage(format!("path does not exist: {path}")));
|
|
}
|
|
if !pack_dir.is_dir() {
|
|
return Err(CliError::Usage(format!("not a directory: {path}")));
|
|
}
|
|
|
|
let report = buzz_persona::validate::validate_pack(pack_dir);
|
|
|
|
for diag in &report.diagnostics {
|
|
match diag {
|
|
buzz_persona::validate::ValidationDiagnostic::Error(msg) => {
|
|
eprintln!(" ERROR: {msg}");
|
|
}
|
|
buzz_persona::validate::ValidationDiagnostic::Warning(msg) => {
|
|
eprintln!(" WARN: {msg}");
|
|
}
|
|
}
|
|
}
|
|
|
|
if report.has_errors() {
|
|
return Err(CliError::Usage("Validation failed.".into()));
|
|
} else if report.has_warnings() {
|
|
println!("Valid (with warnings).");
|
|
} else {
|
|
println!("Valid.");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Run `buzz pack inspect <path>`.
|
|
///
|
|
/// Loads and resolves a pack, then pretty-prints a summary of each persona's
|
|
/// effective configuration.
|
|
pub fn cmd_inspect(path: &str) -> Result<(), CliError> {
|
|
let pack_dir = Path::new(path);
|
|
if !pack_dir.exists() {
|
|
return Err(CliError::Usage(format!("path does not exist: {path}")));
|
|
}
|
|
if !pack_dir.is_dir() {
|
|
return Err(CliError::Usage(format!("not a directory: {path}")));
|
|
}
|
|
|
|
// Resolve the pack — shows fully effective config (post-merge, post-split).
|
|
let pack = buzz_persona::resolve::resolve_pack(pack_dir)
|
|
.map_err(|e| CliError::Other(format!("failed to resolve pack: {e}")))?;
|
|
|
|
// Header
|
|
println!("Pack: {} ({})", pack.name, pack.id);
|
|
println!("Version: {}", pack.version);
|
|
println!("Personas: {}", pack.personas.len());
|
|
println!();
|
|
|
|
// Per-persona summary (fully resolved effective config)
|
|
for persona in &pack.personas {
|
|
println!(" {}", persona.name);
|
|
println!(" Display: {}", persona.display_name);
|
|
println!(" Description: {}", persona.description);
|
|
|
|
if let Some(ref llm_provider) = persona.llm_provider {
|
|
if let Some(ref model) = persona.model {
|
|
println!(" Model: {llm_provider}:{model}");
|
|
} else {
|
|
println!(" Provider: {llm_provider}");
|
|
}
|
|
} else if let Some(ref model) = persona.model {
|
|
println!(" Model: {model}");
|
|
}
|
|
if let Some(temp) = persona.temperature {
|
|
println!(" Temperature: {temp}");
|
|
}
|
|
if let Some(ctx) = persona.max_context_tokens {
|
|
println!(" Max context tokens: {ctx}");
|
|
}
|
|
|
|
if !persona.subscribe.is_empty() {
|
|
println!(" Subscribe: {}", persona.subscribe.join(", "));
|
|
}
|
|
|
|
let rt = &persona.triggers;
|
|
let mut parts = Vec::new();
|
|
if rt.mentions {
|
|
parts.push("mentions".to_string());
|
|
}
|
|
if !rt.keywords.is_empty() {
|
|
parts.push(format!("keywords {:?}", rt.keywords));
|
|
}
|
|
if rt.all_messages {
|
|
parts.push("all_messages".to_string());
|
|
}
|
|
if !parts.is_empty() {
|
|
println!(" Triggers: {}", parts.join(" + "));
|
|
}
|
|
|
|
println!(" Thread replies: {}", persona.thread_replies);
|
|
println!(" Broadcast replies: {}", persona.broadcast_replies);
|
|
|
|
if !persona.mcp_servers.is_empty() {
|
|
println!(" MCP servers: {}", persona.mcp_servers.len());
|
|
}
|
|
|
|
if !persona.skills.is_empty() {
|
|
println!(" Skills: {}", persona.skills.join(", "));
|
|
}
|
|
|
|
if let Some(ref avatar) = persona.avatar {
|
|
println!(" Avatar: {avatar}");
|
|
}
|
|
|
|
let prompt_preview = if persona.system_prompt.chars().count() > 80 {
|
|
let truncated: String = persona.system_prompt.chars().take(77).collect();
|
|
format!("{truncated}...")
|
|
} else {
|
|
persona.system_prompt.clone()
|
|
};
|
|
println!(
|
|
" System prompt: {} chars ({})",
|
|
persona.system_prompt.len(),
|
|
prompt_preview.replace('\n', " ")
|
|
);
|
|
|
|
if !persona.runtime_env_vars.is_empty() {
|
|
let env_str: Vec<String> = persona
|
|
.runtime_env_vars
|
|
.iter()
|
|
.map(|(k, v)| format!("{k}={v}"))
|
|
.collect();
|
|
println!(" Env vars: {}", env_str.join(", "));
|
|
}
|
|
println!();
|
|
}
|
|
|
|
Ok(())
|
|
}
|