Files
owlen/crates/platform/config/src/lib.rs
vikingowl a6cf8585ef feat(permissions): implement permission system with plan mode enforcement (M1 complete)
This commit implements the complete M1 milestone (Config & Permissions) including:

- New permissions crate with Tool, Action, Mode, and PermissionManager
- Three permission modes: Plan (read-only default), AcceptEdits, Code
- Pattern matching for permission rules (exact match and prefix with *)
- Integration with config-agent for mode-based permission management
- CLI integration with --mode flag to override configured mode
- Permission checks for Read, Glob, and Grep operations
- Comprehensive test suite (10 tests in permissions, 4 in config, 4 in CLI)

Also fixes:
- Fixed failing test in tools-fs (glob pattern issue)
- Improved glob_list() root extraction to handle patterns like "/*.txt"

All 21 workspace tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 19:14:54 +01:00

77 lines
2.2 KiB
Rust

use directories::ProjectDirs;
use figment::{
Figment,
providers::{Env, Format, Serialized, Toml},
};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use permissions::{Mode, PermissionManager};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Settings {
#[serde(default = "default_ollama_url")]
pub ollama_url: String,
#[serde(default = "default_model")]
pub model: String,
#[serde(default = "default_mode")]
pub mode: String, // "plan" (read-only) for now
#[serde(default)]
pub api_key: Option<String>, // For Ollama Cloud or other API authentication
}
fn default_ollama_url() -> String {
"http://localhost:11434".into()
}
fn default_model() -> String {
"qwen3:8b".into()
}
fn default_mode() -> String {
"plan".into()
}
impl Default for Settings {
fn default() -> Self {
Self {
ollama_url: default_ollama_url(),
model: default_model(),
mode: default_mode(),
api_key: None,
}
}
}
impl Settings {
/// Create a PermissionManager based on the configured mode
pub fn create_permission_manager(&self) -> PermissionManager {
let mode = Mode::from_str(&self.mode).unwrap_or(Mode::Plan);
PermissionManager::new(mode)
}
/// Get the Mode enum from the mode string
pub fn get_mode(&self) -> Mode {
Mode::from_str(&self.mode).unwrap_or(Mode::Plan)
}
}
pub fn load_settings(project_root: Option<&str>) -> Result<Settings, figment::Error> {
let mut fig = Figment::from(Serialized::defaults(Settings::default()));
// User file: ~/.config/owlen/config.toml
if let Some(pd) = ProjectDirs::from("dev", "owlibou", "owlen") {
let user = pd.config_dir().join("config.toml");
fig = fig.merge(Toml::file(user));
}
// Project file: <root>/.owlen.toml
if let Some(root) = project_root {
fig = fig.merge(Toml::file(PathBuf::from(root).join(".owlen.toml")));
}
// Environment variables have highest precedence
fig = fig.merge(Env::prefixed("OWLEN_").split("__"));
// Support OLLAMA_API_KEY, OLLAMA_MODEL, etc. (without nesting)
fig = fig.merge(Env::prefixed("OLLAMA_"));
fig.extract()
}