1use serde::Deserialize;
2use std::path::Path;
3
4#[derive(Debug, Default, Deserialize)]
8pub struct UserConfig {
9 #[serde(default)]
10 pub repo: RepoConfig,
11 #[serde(default)]
12 pub agent: AgentConfig,
13 #[serde(default)]
14 pub shell: ShellConfig,
15}
16
17#[derive(Debug, Default, Deserialize)]
18pub struct RepoConfig {
19 #[serde(default)]
20 pub register: RepoRegisterConfig,
21}
22
23#[derive(Debug, Default, Deserialize)]
24pub struct RepoRegisterConfig {
25 #[serde(default)]
26 pub hooks: Vec<Hook>,
27}
28
29#[derive(Debug, Clone, Deserialize)]
31pub struct Hook {
32 pub run: String,
33}
34
35#[derive(Debug, Default, Deserialize)]
36pub struct AgentConfig {
37 pub command: Option<String>,
40}
41
42#[derive(Debug, Default, Deserialize)]
43pub struct ShellConfig {
44 pub binary: Option<String>,
47}
48
49impl UserConfig {
50 pub fn load(vex_home: &Path) -> Self {
51 let path = vex_home.join("config.yaml");
52 if !path.exists() {
53 return Self::default();
54 }
55 match std::fs::read_to_string(&path) {
56 Ok(content) => serde_yaml::from_str(&content).unwrap_or_default(),
57 Err(_) => Self::default(),
58 }
59 }
60
61 pub fn agent_command(&self) -> &str {
62 self.agent
63 .command
64 .as_deref()
65 .unwrap_or("claude --dangerously-skip-permissions")
66 }
67
68 pub fn register_hooks(&self) -> &[Hook] {
69 &self.repo.register.hooks
70 }
71
72 pub fn shell_binary(&self) -> String {
75 if let Some(ref bin) = self.shell.binary {
76 return bin.clone();
77 }
78 std::env::var("SHELL").unwrap_or_else(|_| "/bin/bash".to_string())
79 }
80}