1mod load;
10mod runtime;
11mod schema;
12mod validate;
13
14pub use load::{Setting, read_layer};
15pub use schema::*;
16
17use anyhow::Result;
18use scv_client::Layout;
19
20#[derive(Debug, Clone)]
23pub(crate) struct Instance {
24 pub(crate) layout: Layout,
25 pub(crate) overrides: ConfigOverrides,
26}
27
28impl Instance {
29 fn files(&self) -> ConfigOverrides {
32 ConfigOverrides {
33 config_file: self.overrides.config_file.clone(),
34 ..ConfigOverrides::default()
35 }
36 }
37
38 pub(crate) fn load(&self, workspace: &std::path::Path) -> Result<Config> {
40 Config::load(&self.layout, workspace, self.files())
41 }
42
43 pub(crate) fn load_user(&self) -> Result<Config> {
45 Config::load_user(&self.layout, self.files())
46 }
47}
48
49fn claude_warnings_for_layout(layout: &Layout) -> Vec<String> {
51 let mut warnings = Vec::new();
52 let agents = std::fs::read_to_string(layout.config())
53 .ok()
54 .and_then(|text| text.parse::<toml::Value>().ok())
55 .and_then(|value| value.get("agents").cloned())
56 .and_then(|value| value.as_table().cloned());
57 let explicit_claude = agents
58 .as_ref()
59 .is_some_and(|agents| agents.contains_key("claude"));
60 let explicit_claude_ai = agents
61 .as_ref()
62 .is_some_and(|agents| agents.contains_key("claude-ai"));
63 if explicit_claude
64 && !explicit_claude_ai
65 && scv_tools::stores::claude_status(
66 scv_tools::adapters::AgentAuth::ClaudeSubscription,
67 &layout.agent_home("claude"),
68 )
69 .is_ok_and(|status| status.ready)
70 {
71 warnings.push(
72 "claude now bills an API key and claude-ai uses the Claude subscription. agents/claude holds a Claude subscription login, so claude will not run. To keep using the subscription, move agents/claude to agents/claude-ai and [agents.claude] to [agents.claude-ai] (see docs/configuration.md).".into(),
73 );
74 }
75 if std::path::Path::new("/etc/claude-code/managed-settings.json").is_file() {
76 warnings.push(
77 "machine policy /etc/claude-code/managed-settings.json outranks SCV's pinned Claude Code credentials for claude and claude-ai".into(),
78 );
79 }
80 warnings
81}
82
83impl Config {
84 pub fn claude_warnings(&self) -> Vec<String> {
85 claude_warnings_for_layout(&Layout::new(&self.instance_home))
86 }
87}
88
89impl Instance {
90 pub(crate) fn claude_warnings(&self) -> Vec<String> {
91 claude_warnings_for_layout(&self.layout)
92 }
93}
94
95#[cfg(test)]
96mod tests;