mi6_cli/commands/
disable.rs

1//! Disable command - remove mi6 hooks from AI coding frameworks.
2
3use anyhow::{Context, Result};
4
5use mi6_core::{FrameworkAdapter, FrameworkResolutionMode, resolve_frameworks};
6
7use crate::display::StderrColors;
8
9/// Options for the disable command
10pub struct DisableOptions {
11    /// Frameworks to disable (empty = all that have mi6 enabled)
12    pub frameworks: Vec<String>,
13    /// Remove from project config instead of global
14    pub local: bool,
15    /// Remove from project local config
16    pub settings_local: bool,
17    /// Print what would be removed without modifying files
18    pub print: bool,
19}
20
21/// Run the disable command.
22pub fn run_disable(opts: DisableOptions) -> Result<()> {
23    let colors = StderrColors::new();
24
25    // Resolve which frameworks to disable (auto-detect enabled hooks if none specified)
26    let mode = FrameworkResolutionMode::Active {
27        local: opts.local,
28        settings_local: opts.settings_local,
29    };
30    let adapters =
31        resolve_frameworks(&opts.frameworks, Some(mode)).map_err(|e| anyhow::anyhow!("{}", e))?;
32
33    for adapter in adapters {
34        disable_framework(adapter, &opts, &colors)?;
35    }
36
37    Ok(())
38}
39
40/// Disable mi6 for a single framework
41fn disable_framework(
42    adapter: &dyn FrameworkAdapter,
43    opts: &DisableOptions,
44    colors: &StderrColors,
45) -> Result<()> {
46    // Check if mi6 is enabled for this framework
47    if !adapter.has_mi6_hooks(opts.local, opts.settings_local) {
48        eprintln!(
49            "{}mi6 is not enabled for {}{}",
50            colors.yellow,
51            adapter.name(),
52            colors.reset
53        );
54        return Ok(());
55    }
56
57    let settings_path = adapter
58        .settings_path(opts.local, opts.settings_local)
59        .context("failed to determine settings path")?;
60
61    if opts.print {
62        eprintln!(
63            "{}Would disable{} mi6 for {}{}{}:",
64            colors.cyan,
65            colors.reset,
66            colors.bold,
67            adapter.name(),
68            colors.reset
69        );
70        // For plugin-based frameworks, show plugin directory; otherwise show settings file
71        if adapter.name() == "claude" {
72            // Show the plugin directory (parent of hooks/hooks.json)
73            if let Some(plugin_dir) = settings_path.parent().and_then(|p| p.parent()) {
74                eprintln!("  Would remove plugin: {}", plugin_dir.display());
75            }
76        } else {
77            eprintln!("  Would modify: {}", settings_path.display());
78        }
79    } else {
80        eprint!(
81            "{}Disabling{} mi6 for {}{}{}... ",
82            colors.green,
83            colors.reset,
84            colors.bold,
85            adapter.name(),
86            colors.reset
87        );
88
89        // Use uninstall_hooks which handles both config-based and plugin-based frameworks
90        adapter
91            .uninstall_hooks(opts.local, opts.settings_local)
92            .context("failed to uninstall hooks")?;
93
94        eprintln!("{}done{}", colors.green, colors.reset);
95        if adapter.name() == "claude" {
96            // Show plugin directory for Claude
97            if let Some(plugin_dir) = settings_path.parent().and_then(|p| p.parent()) {
98                eprintln!(
99                    "  {}Removed plugin:{} {}",
100                    colors.cyan,
101                    colors.reset,
102                    plugin_dir.display()
103                );
104            }
105        } else {
106            eprintln!(
107                "  {}Removed hooks from:{} {}",
108                colors.cyan,
109                colors.reset,
110                settings_path.display()
111            );
112        }
113    }
114
115    Ok(())
116}
117
118/// Disable mi6 hooks for a single framework silently (no output).
119///
120/// Used by the uninstall command when disabling all frameworks.
121pub fn disable_framework_silent(adapter: &dyn FrameworkAdapter) -> Result<()> {
122    // Use uninstall_hooks which handles both config-based and plugin-based frameworks
123    adapter
124        .uninstall_hooks(false, false)
125        .context("failed to uninstall hooks")?;
126    Ok(())
127}