Skip to main content

claudectl_core/
config.rs

1//! Configuration data types shared between the binary and (future) TUI crate.
2//!
3//! The binary still owns *parsing* (TOML, CLI flags, layering) — but the
4//! resulting `BrainConfig` / `IdleConfig` structs live here so downstream
5//! crates (notably the TUI extracted under #275) can hold them without
6//! depending back on the binary's `crate::config` module.
7
8/// Configuration for the optional local LLM brain.
9/// When `None`, brain is completely disabled with zero overhead.
10#[derive(Debug, Clone)]
11pub struct BrainConfig {
12    pub enabled: bool,
13    pub endpoint: String,
14    pub model: String,
15    pub auto_mode: bool,
16    pub timeout_ms: u64,
17    pub max_context_tokens: u32,
18    pub few_shot_count: usize,
19    pub max_sessions: usize,
20    pub orchestrate: bool,
21    pub orchestrate_interval_secs: u64,
22    /// Command prefixes that identify test-runner invocations. When one of
23    /// these fails (non-zero exit), the reaper fans the failure out to recent
24    /// brain-approved edits as a `TestFailed` outcome (#238). Empty disables
25    /// test-failure attribution.
26    pub test_runners: Vec<String>,
27}
28
29/// Default test-runner command prefixes. Matched as command-line prefix on
30/// the normalized command (whitespace-collapsed, lowercased). Users override
31/// via `test_runners` in the `[brain]` config section.
32pub fn default_test_runners() -> Vec<String> {
33    [
34        "cargo test",
35        "cargo nextest",
36        "npm test",
37        "npm run test",
38        "pnpm test",
39        "yarn test",
40        "bun test",
41        "pytest",
42        "go test",
43        "jest",
44        "vitest",
45        "mix test",
46        "rspec",
47    ]
48    .iter()
49    .map(|s| s.to_string())
50    .collect()
51}
52
53impl Default for BrainConfig {
54    fn default() -> Self {
55        Self {
56            enabled: true,
57            endpoint: "http://localhost:11434/api/generate".into(),
58            model: "gemma4:e4b".into(),
59            auto_mode: false,
60            timeout_ms: 5000,
61            max_context_tokens: 4000,
62            few_shot_count: 5,
63            max_sessions: 10,
64            orchestrate: false,
65            orchestrate_interval_secs: 30,
66            test_runners: default_test_runners(),
67        }
68    }
69}
70
71/// Configuration for idle mode and unattended work queue.
72#[allow(dead_code)]
73#[derive(Debug, Clone)]
74pub struct IdleConfig {
75    pub enabled: bool,
76    pub after_idle_mins: u64,
77    pub max_concurrent: usize,
78    pub max_cost_usd: f64,
79    pub tasks: Vec<IdleTask>,
80}
81
82#[allow(dead_code)]
83#[derive(Debug, Clone)]
84pub struct IdleTask {
85    pub prompt: String,
86    pub cwd: Option<String>,
87    pub priority: u32,
88}
89
90impl Default for IdleConfig {
91    fn default() -> Self {
92        Self {
93            enabled: false,
94            after_idle_mins: 15,
95            max_concurrent: 2,
96            max_cost_usd: 5.0,
97            tasks: Vec::new(),
98        }
99    }
100}