@@ -0,0 +1,38 @@
1+use std::{
2+ fs, io,
3+ path::{Path, PathBuf},
4+};
5+
6+use serde::Deserialize;
7+
8+pub const CONFIG_FILENAME: &str = "vibebox.toml";
9+
10+#[derive(Debug, Default, Deserialize)]
11+pub struct ProjectConfig {
12+ pub auto_shutdown_ms: Option<u64>,
13+}
14+
15+pub fn config_path(project_root: &Path) -> PathBuf {
16+ project_root.join(CONFIG_FILENAME)
17+}
18+
19+pub fn ensure_config_file(project_root: &Path) -> Result<PathBuf, io::Error> {
20+ let path = config_path(project_root);
21+ if !path.exists() {
22+ fs::write(&path, "")?;
23+ tracing::info!(path = %path.display(), "created vibebox config");
24+ }
25+ Ok(path)
26+}
27+
28+pub fn load_config(
29+ project_root: &Path,
30+) -> Result<ProjectConfig, Box<dyn std::error::Error + Send + Sync>> {
31+ let path = ensure_config_file(project_root)?;
32+ let raw = fs::read_to_string(&path)?;
33+ tracing::debug!(path = %path.display(), bytes = raw.len(), "loaded vibebox config");
34+ if raw.trim().is_empty() {
35+ return Ok(ProjectConfig::default());
36+ }
37+ Ok(toml::from_str::<ProjectConfig>(&raw)?)
38+}