mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-07 08:27:12 +00:00
Use typed enums in cli.toml config and simplify defaults
- Deserialize permissions/output_format as typed enums instead of strings so invalid values in cli.toml fail at parse time - Use Option::or/or_else combinators instead of if-is_none pattern - Load cli.toml only for agent/llm commands, not all CLI invocations - Standalone arc-agent binary calls apply_cli_defaults for single source of hardcoded defaults - Remove redundant #[serde(default)] on Option fields Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d0c7ac342e
commit
40b707b077
3 changed files with 29 additions and 38 deletions
|
|
@ -57,13 +57,15 @@ struct Cli {
|
|||
args: AgentArgs,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, ValueEnum)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, ValueEnum, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum OutputFormat {
|
||||
Text,
|
||||
Json,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, ValueEnum)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, ValueEnum, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum PermissionLevel {
|
||||
ReadOnly,
|
||||
ReadWrite,
|
||||
|
|
@ -76,32 +78,20 @@ impl AgentArgs {
|
|||
&mut self,
|
||||
provider: Option<&str>,
|
||||
model: Option<&str>,
|
||||
permissions: Option<&str>,
|
||||
output_format: Option<&str>,
|
||||
permissions: Option<PermissionLevel>,
|
||||
output_format: Option<OutputFormat>,
|
||||
) {
|
||||
if self.provider.is_none() {
|
||||
self.provider = Some(
|
||||
provider
|
||||
.map(String::from)
|
||||
.unwrap_or_else(|| "anthropic".to_string()),
|
||||
);
|
||||
}
|
||||
if self.model.is_none() {
|
||||
self.model = model.map(String::from);
|
||||
}
|
||||
if self.permissions.is_none() {
|
||||
self.permissions = Some(match permissions {
|
||||
Some("read-only") => PermissionLevel::ReadOnly,
|
||||
Some("full") => PermissionLevel::Full,
|
||||
_ => PermissionLevel::ReadWrite,
|
||||
});
|
||||
}
|
||||
if self.output_format.is_none() {
|
||||
self.output_format = Some(match output_format {
|
||||
Some("json") => OutputFormat::Json,
|
||||
_ => OutputFormat::Text,
|
||||
});
|
||||
}
|
||||
self.provider = self
|
||||
.provider
|
||||
.take()
|
||||
.or_else(|| provider.map(String::from))
|
||||
.or_else(|| Some("anthropic".to_string()));
|
||||
self.model = self.model.take().or_else(|| model.map(String::from));
|
||||
self.permissions = self.permissions.or(permissions).or(Some(PermissionLevel::ReadWrite));
|
||||
self.output_format = self
|
||||
.output_format
|
||||
.or(output_format)
|
||||
.or(Some(OutputFormat::Text));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -627,7 +617,9 @@ pub async fn run_with_args(args: AgentArgs) -> anyhow::Result<()> {
|
|||
pub async fn run() -> anyhow::Result<()> {
|
||||
let _ = dotenvy::dotenv();
|
||||
let cli = Cli::parse();
|
||||
run_with_args(cli.args).await
|
||||
let mut args = cli.args;
|
||||
args.apply_cli_defaults(None, None, None, None);
|
||||
run_with_args(args).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::path::Path;
|
||||
|
||||
use arc_agent::cli::{OutputFormat, PermissionLevel};
|
||||
use serde::Deserialize;
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -7,8 +8,8 @@ use tracing::debug;
|
|||
pub struct AgentDefaults {
|
||||
pub provider: Option<String>,
|
||||
pub model: Option<String>,
|
||||
pub permissions: Option<String>,
|
||||
pub output_format: Option<String>,
|
||||
pub permissions: Option<PermissionLevel>,
|
||||
pub output_format: Option<OutputFormat>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
|
||||
|
|
@ -18,9 +19,7 @@ pub struct LlmDefaults {
|
|||
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
|
||||
pub struct CliConfig {
|
||||
#[serde(default)]
|
||||
pub agent: Option<AgentDefaults>,
|
||||
#[serde(default)]
|
||||
pub llm: Option<LlmDefaults>,
|
||||
}
|
||||
|
||||
|
|
@ -72,8 +71,8 @@ model = "claude-sonnet-4-5"
|
|||
let agent = config.agent.unwrap();
|
||||
assert_eq!(agent.provider.as_deref(), Some("anthropic"));
|
||||
assert_eq!(agent.model.as_deref(), Some("claude-opus-4-6"));
|
||||
assert_eq!(agent.permissions.as_deref(), Some("read-write"));
|
||||
assert_eq!(agent.output_format.as_deref(), Some("text"));
|
||||
assert_eq!(agent.permissions, Some(PermissionLevel::ReadWrite));
|
||||
assert_eq!(agent.output_format, Some(OutputFormat::Text));
|
||||
let llm = config.llm.unwrap();
|
||||
assert_eq!(llm.model.as_deref(), Some("claude-sonnet-4-5"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,8 +93,6 @@ async fn main() -> Result<()> {
|
|||
eprintln!("Warning: failed to initialize logging: {err:#}");
|
||||
}
|
||||
|
||||
let cli_config = cli_config::load_cli_config(None)?;
|
||||
|
||||
let command_name = match &cli.command {
|
||||
Command::Llm { .. } => "llm",
|
||||
Command::Agent(_) => "agent",
|
||||
|
|
@ -109,6 +107,7 @@ async fn main() -> Result<()> {
|
|||
|
||||
match cli.command {
|
||||
Command::Llm { command } => {
|
||||
let cli_config = cli_config::load_cli_config(None)?;
|
||||
let llm_defaults = cli_config.llm.as_ref();
|
||||
match command {
|
||||
LlmCommand::Prompt(mut args) => {
|
||||
|
|
@ -126,12 +125,13 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
Command::Agent(mut args) => {
|
||||
let cli_config = cli_config::load_cli_config(None)?;
|
||||
let agent_defaults = cli_config.agent.as_ref();
|
||||
args.apply_cli_defaults(
|
||||
agent_defaults.and_then(|a| a.provider.as_deref()),
|
||||
agent_defaults.and_then(|a| a.model.as_deref()),
|
||||
agent_defaults.and_then(|a| a.permissions.as_deref()),
|
||||
agent_defaults.and_then(|a| a.output_format.as_deref()),
|
||||
agent_defaults.and_then(|a| a.permissions),
|
||||
agent_defaults.and_then(|a| a.output_format),
|
||||
);
|
||||
arc_agent::cli::run_with_args(args).await?
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue