aft/config.rs
1use std::path::PathBuf;
2
3/// Runtime configuration for the aft process.
4///
5/// Holds project-scoped settings and tuning knobs. Values are set at startup
6/// and remain immutable for the lifetime of the process.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum SemanticBackend {
9 Fastembed,
10 OpenAiCompatible,
11 Ollama,
12}
13
14impl SemanticBackend {
15 pub const fn as_str(&self) -> &'static str {
16 match self {
17 Self::Fastembed => "fastembed",
18 Self::OpenAiCompatible => "openai_compatible",
19 Self::Ollama => "ollama",
20 }
21 }
22
23 pub fn from_name(name: &str) -> Option<Self> {
24 match name {
25 "fastembed" => Some(Self::Fastembed),
26 "openai_compatible" => Some(Self::OpenAiCompatible),
27 "ollama" => Some(Self::Ollama),
28 _ => None,
29 }
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct SemanticBackendConfig {
35 pub backend: SemanticBackend,
36 pub model: String,
37 pub base_url: Option<String>,
38 pub api_key_env: Option<String>,
39 pub timeout_ms: u64,
40 pub max_batch_size: usize,
41}
42
43impl Default for SemanticBackendConfig {
44 fn default() -> Self {
45 Self {
46 backend: SemanticBackend::Fastembed,
47 model: DEFAULT_SEMANTIC_MODEL.to_string(),
48 base_url: None,
49 api_key_env: None,
50 // Keep the default below the plugin bridge timeout to avoid bridge-killed
51 // semantic_search requests when callers do not set an explicit timeout.
52 timeout_ms: 25_000,
53 max_batch_size: 64,
54 }
55 }
56}
57
58pub const DEFAULT_SEMANTIC_MODEL: &str = "all-MiniLM-L6-v2";
59
60impl Config {
61 pub fn semantic_backend_label(&self) -> &'static str {
62 self.semantic.backend.as_str()
63 }
64}
65
66pub struct Config {
67 /// Root directory of the project being analyzed. `None` if not scoped.
68 pub project_root: Option<PathBuf>,
69 /// How many levels of call-graph edges to follow during validation (default: 1).
70 pub validation_depth: u32,
71 /// Hours before a checkpoint expires and is eligible for cleanup (default: 24).
72 pub checkpoint_ttl_hours: u32,
73 /// Maximum depth for recursive symbol resolution (default: 10).
74 pub max_symbol_depth: u32,
75 /// Seconds before killing a formatter subprocess (default: 10).
76 pub formatter_timeout_secs: u32,
77 /// Seconds before killing a type-checker subprocess (default: 30).
78 pub type_checker_timeout_secs: u32,
79 /// Whether to auto-format files after edits (default: true).
80 pub format_on_edit: bool,
81 /// Whether to auto-validate files after edits (default: false).
82 /// When "syntax", only tree-sitter parse check. When "full", runs type checker.
83 pub validate_on_edit: Option<String>,
84 /// Per-language formatter overrides. Keys: "typescript", "python", "rust", "go".
85 /// Values: "biome", "prettier", "deno", "ruff", "black", "rustfmt", "goimports", "gofmt", "none".
86 pub formatter: std::collections::HashMap<String, String>,
87 /// Per-language type checker overrides. Keys: "typescript", "python", "rust", "go".
88 /// Values: "tsc", "biome", "pyright", "ruff", "cargo", "go", "staticcheck", "none".
89 pub checker: std::collections::HashMap<String, String>,
90 /// Whether to restrict file operations to within `project_root` (default: false).
91 /// When true, write-capable commands reject paths outside the project root.
92 pub restrict_to_project_root: bool,
93 /// Enable the experimental trigram search index (default: false).
94 pub experimental_search_index: bool,
95 /// Enable the experimental semantic search index (default: false).
96 pub experimental_semantic_search: bool,
97 /// Maximum file size to fully index in bytes (default: 1MB).
98 pub search_index_max_file_size: u64,
99 pub semantic: SemanticBackendConfig,
100 /// Persistent storage directory for indexes (trigram, semantic).
101 /// Set by the plugin to the XDG-compliant path (e.g. ~/.local/share/opencode/storage/plugin/aft/).
102 /// Falls back to ~/.cache/aft/ if not set.
103 pub storage_dir: Option<PathBuf>,
104}
105
106impl Default for Config {
107 fn default() -> Self {
108 Config {
109 project_root: None,
110 validation_depth: 1,
111 checkpoint_ttl_hours: 24,
112 max_symbol_depth: 10,
113 formatter_timeout_secs: 10,
114 type_checker_timeout_secs: 30,
115 format_on_edit: true,
116 validate_on_edit: None,
117 formatter: std::collections::HashMap::new(),
118 checker: std::collections::HashMap::new(),
119 // Default to false to match OpenCode's existing permission-based model.
120 // The plugin opts into root restriction explicitly when desired.
121 restrict_to_project_root: false,
122 experimental_search_index: false,
123 experimental_semantic_search: false,
124 search_index_max_file_size: 1_048_576,
125 semantic: SemanticBackendConfig::default(),
126 storage_dir: None,
127 }
128 }
129}