Skip to main content

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    /// Maximum number of source files allowed for call-graph operations
100    /// (`callers`, `trace_to`, `trace_data`, `impact`). When a project
101    /// exceeds this count the reverse index is not built and those
102    /// commands return a `project_too_large` error. Does not affect
103    /// `grep`, `glob`, `read`, `edit`, or other non-callgraph features.
104    /// Default: 20_000 (covers typical monorepos; rejects OS-wide roots).
105    pub max_callgraph_files: usize,
106    pub semantic: SemanticBackendConfig,
107    /// Persistent storage directory for indexes (trigram, semantic).
108    /// Set by the plugin to the XDG-compliant path (e.g. ~/.local/share/opencode/storage/plugin/aft/).
109    /// Falls back to ~/.cache/aft/ if not set.
110    pub storage_dir: Option<PathBuf>,
111}
112
113impl Default for Config {
114    fn default() -> Self {
115        Config {
116            project_root: None,
117            validation_depth: 1,
118            checkpoint_ttl_hours: 24,
119            max_symbol_depth: 10,
120            formatter_timeout_secs: 10,
121            type_checker_timeout_secs: 30,
122            format_on_edit: true,
123            validate_on_edit: None,
124            formatter: std::collections::HashMap::new(),
125            checker: std::collections::HashMap::new(),
126            // Default to false to match OpenCode's existing permission-based model.
127            // The plugin opts into root restriction explicitly when desired.
128            restrict_to_project_root: false,
129            experimental_search_index: false,
130            experimental_semantic_search: false,
131            search_index_max_file_size: 1_048_576,
132            // Projects larger than this skip call-graph reverse index construction.
133            // Chosen to cover typical monorepos (AFT ~2K, OpenCode ~5K, Reth ~8K)
134            // while rejecting OS-wide roots (/home, ~/Work) that would otherwise
135            // walk hundreds of thousands of files per callers/trace_to query.
136            max_callgraph_files: 20_000,
137            semantic: SemanticBackendConfig::default(),
138            storage_dir: None,
139        }
140    }
141}