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            eprintln!("  Would remove plugin: {}", settings_path.display());
73        } else {
74            eprintln!("  Would modify: {}", settings_path.display());
75        }
76    } else {
77        eprint!("Disabling mi6 for {}... ", adapter.name());
78
79        // Use uninstall_hooks which handles both config-based and plugin-based frameworks
80        adapter
81            .uninstall_hooks(opts.local, opts.settings_local)
82            .context("failed to uninstall hooks")?;
83
84        eprintln!("{}done{}", colors.green, colors.reset);
85        if adapter.name() == "claude" {
86            // Show plugin directory for Claude
87            eprintln!(
88                "  Removed plugin: {}{}{}",
89                colors.bold,
90                settings_path.display(),
91                colors.reset
92            );
93        } else {
94            eprintln!(
95                "  Removed hooks from: {}{}{}",
96                colors.bold,
97                settings_path.display(),
98                colors.reset
99            );
100        }
101    }
102
103    Ok(())
104}
105
106/// Disable mi6 hooks for a single framework silently (no output).
107///
108/// Used by the uninstall command when disabling all frameworks.
109pub fn disable_framework_silent(adapter: &dyn FrameworkAdapter) -> Result<()> {
110    // Use uninstall_hooks which handles both config-based and plugin-based frameworks
111    adapter
112        .uninstall_hooks(false, false)
113        .context("failed to uninstall hooks")?;
114    Ok(())
115}