lean_ctx/core/editor_registry/writers/
mod.rs1use super::types::{ConfigType, EditorTarget};
5
6mod install;
7mod shared;
8mod uninstall;
9
10pub use install::{COMMANDCODE_MCP_INSTRUCTIONS, validate_claude_binary};
11pub use shared::auto_approve_tools;
12pub use uninstall::remove_lean_ctx_mcp_server;
13#[allow(clippy::wildcard_imports)]
16use install::*;
17#[allow(clippy::wildcard_imports)]
18use uninstall::*;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum WriteAction {
22 Created,
23 Updated,
24 Already,
25}
26
27#[derive(Debug, Clone, Copy, Default)]
28pub struct WriteOptions {
29 pub overwrite_invalid: bool,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct WriteResult {
34 pub action: WriteAction,
35 pub note: Option<String>,
36}
37
38pub fn write_config(target: &EditorTarget, binary: &str) -> Result<WriteResult, String> {
39 write_config_with_options(target, binary, WriteOptions::default())
40}
41
42pub fn write_config_with_options(
43 target: &EditorTarget,
44 binary: &str,
45 opts: WriteOptions,
46) -> Result<WriteResult, String> {
47 let allow_rule_steering = crate::core::config::Config::load().rules_injection_effective()
48 != crate::core::config::RulesInjection::Off;
49
50 write_config_with_options_and_rule_steering(target, binary, opts, allow_rule_steering)
51}
52
53pub fn write_config_with_options_and_rule_steering(
54 target: &EditorTarget,
55 binary: &str,
56 opts: WriteOptions,
57 allow_rule_steering: bool,
58) -> Result<WriteResult, String> {
59 if crate::core::governor_hosts::suppresses_mcp(&target.agent_key, &target.config_path) {
62 return Ok(WriteResult {
63 action: WriteAction::Already,
64 note: Some("governor setup: no MCP server".to_string()),
65 });
66 }
67 if let Some(parent) = target.config_path.parent() {
68 std::fs::create_dir_all(parent).map_err(|e| e.to_string())?;
69 }
70
71 match target.config_type {
72 ConfigType::McpJson => write_mcp_json(target, binary, opts),
73 ConfigType::Zed => write_zed_config(target, binary, opts),
74 ConfigType::Codex => write_codex_config(target, binary),
75 ConfigType::VsCodeMcp => write_vscode_mcp(target, binary, opts),
76 ConfigType::CopilotCli => write_copilot_cli(target, binary, opts),
77 ConfigType::OpenCode => write_opencode_config(target, binary, opts),
78 ConfigType::Crush => write_crush_config(target, binary, opts),
79 ConfigType::JetBrains => write_jetbrains_config(target, binary, opts),
80 ConfigType::Amp => write_amp_config(target, binary, opts),
81 ConfigType::HermesYaml => write_hermes_yaml(target, binary, opts),
82 ConfigType::GooseYaml => write_goose_yaml(target, binary, opts),
83 ConfigType::GeminiSettings => write_gemini_settings(target, binary, opts),
84 ConfigType::QoderSettings => write_qoder_settings(target, binary, opts),
85 ConfigType::AugmentVsCode => write_augment_vscode(target, binary, opts),
86 ConfigType::OpenClaw => write_openclaw_config(target, binary, opts),
87 ConfigType::VibeToml => write_vibe_toml(target, binary, opts),
88 ConfigType::CommandCode => {
89 write_commandcode_config_with_rule_steering(target, binary, opts, allow_rule_steering)
90 }
91 ConfigType::ClineCli => write_cline_cli_config(target, binary, opts),
92 ConfigType::OmpMcp => write_omp_mcp(target, binary, opts),
93 ConfigType::CodeWhale => write_codewhale_config(target, binary, opts),
94 ConfigType::MuseSettings => write_muse_settings(target, binary, opts),
95 }
96}
97
98pub fn remove_lean_ctx_server(
99 target: &EditorTarget,
100 opts: WriteOptions,
101) -> Result<WriteResult, String> {
102 match target.config_type {
103 ConfigType::McpJson
104 | ConfigType::JetBrains
105 | ConfigType::GeminiSettings
106 | ConfigType::QoderSettings
107 | ConfigType::CommandCode
108 | ConfigType::ClineCli
109 | ConfigType::OmpMcp
110 | ConfigType::CopilotCli => remove_lean_ctx_mcp_server(&target.config_path, opts),
113 ConfigType::VsCodeMcp => remove_lean_ctx_vscode_server(&target.config_path, opts),
114 ConfigType::Codex => remove_lean_ctx_codex_server(&target.config_path),
115 ConfigType::OpenCode | ConfigType::Crush => {
116 remove_lean_ctx_named_json_server(&target.config_path, "mcp", opts)
117 }
118 ConfigType::Zed => {
119 remove_lean_ctx_named_json_server(&target.config_path, "context_servers", opts)
120 }
121 ConfigType::Amp => remove_lean_ctx_amp_server(&target.config_path, opts),
122 ConfigType::HermesYaml => remove_lean_ctx_hermes_yaml_server(&target.config_path),
123 ConfigType::GooseYaml => remove_lean_ctx_goose_yaml_server(&target.config_path),
124 ConfigType::AugmentVsCode => {
125 remove_lean_ctx_augment_vscode_server(&target.config_path, opts)
126 }
127 ConfigType::OpenClaw => remove_lean_ctx_openclaw_server(&target.config_path, opts),
128 ConfigType::VibeToml => remove_lean_ctx_vibe_toml_server(&target.config_path, opts),
129 ConfigType::CodeWhale => {
133 let servers = remove_lean_ctx_named_json_server(&target.config_path, "servers", opts)?;
134 let mcp_servers =
135 remove_lean_ctx_named_json_server(&target.config_path, "mcpServers", opts)?;
136 Ok(if servers.action == WriteAction::Updated {
137 servers
138 } else {
139 mcp_servers
140 })
141 }
142 ConfigType::MuseSettings => {
147 let canonical =
148 remove_lean_ctx_named_json_server(&target.config_path, MUSE_MCP_ROOT, opts)?;
149 let legacy =
150 remove_lean_ctx_named_json_server(&target.config_path, MUSE_LEGACY_MCP_ROOT, opts)?;
151 Ok(if canonical.action == WriteAction::Updated {
152 canonical
153 } else {
154 legacy
155 })
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests;