Repository: clap-rs/clap
Pull request #5705 (2024-08-29 02:02:56)

fix(complete)!: Remove CompleteCommand
This just has too many caveats to be worth maintaining

<!--
Thanks for helping out!

Please link the appropriate issue from your PR.

If you don't have an issue, we'd recommend starting with one first so the PR can focus on the
implementation (unless its an obvious bug or documentation fix that will have
little conversation).
-->



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 278ec600..79915ec0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -485,7 +485,6 @@ dependencies = [
  "shlex",
  "snapbox",
  "trycmd",
- "unicode-xid",
 ]
 
 [[package]]
diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml
index f359b82c..1397450c 100644
--- a/clap_complete/Cargo.toml
+++ b/clap_complete/Cargo.toml
@@ -38,7 +38,6 @@ clap = { path = "../", version = "4.5.15", default-features = false, features =
 clap_lex = { path = "../clap_lex", version = "0.7.0", optional = true }
 is_executable = { version = "1.0.1", optional = true }
 shlex = { version = "1.3.0", optional = true }
-unicode-xid = { version = "0.2.2", optional = true }
 
 [dev-dependencies]
 snapbox = { version = "0.6.0", features = ["diff", "dir", "examples"] }
@@ -51,13 +50,12 @@ automod = "1.0.14"
 
 [[example]]
 name = "dynamic"
-required-features = ["unstable-dynamic", "unstable-command"]
+required-features = ["unstable-dynamic"]
 
 [features]
 default = []
-unstable-doc = ["unstable-dynamic", "unstable-command"] # for docs.rs
+unstable-doc = ["unstable-dynamic"] # for docs.rs
 unstable-dynamic = ["dep:clap_lex", "dep:shlex", "dep:is_executable", "clap/unstable-ext"]
-unstable-command = ["unstable-dynamic", "dep:unicode-xid", "clap/derive", "dep:is_executable", "clap/unstable-ext"]
 debug = ["clap/debug"]
 
 [lints]
diff --git a/clap_complete/examples/dynamic.rs b/clap_complete/examples/dynamic.rs
index ba61f5fd..2c393a3e 100644
--- a/clap_complete/examples/dynamic.rs
+++ b/clap_complete/examples/dynamic.rs
@@ -1,8 +1,5 @@
-use clap::FromArgMatches;
-use clap::Subcommand;
-
 fn command() -> clap::Command {
-    let cmd = clap::Command::new("dynamic")
+    clap::Command::new("dynamic")
         .arg(
             clap::Arg::new("input")
                 .long("input")
@@ -15,8 +12,7 @@ fn command() -> clap::Command {
                 .short('F')
                 .value_parser(["json", "yaml", "toml"]),
         )
-        .args_conflicts_with_subcommands(true);
-    clap_complete::CompleteCommand::augment_subcommands(cmd)
+        .args_conflicts_with_subcommands(true)
 }
 
 fn main() {
@@ -24,11 +20,7 @@ fn main() {
 
     let cmd = command();
     let matches = cmd.get_matches();
-    if let Ok(completions) = clap_complete::CompleteCommand::from_arg_matches(&matches) {
-        completions.complete(&mut command());
-    } else {
-        println!("{matches:#?}");
-    }
+    println!("{matches:#?}");
 }
 
 #[test]
diff --git a/clap_complete/examples/exhaustive.rs b/clap_complete/examples/exhaustive.rs
index 83050bdd..cbe604ac 100644
--- a/clap_complete/examples/exhaustive.rs
+++ b/clap_complete/examples/exhaustive.rs
@@ -1,6 +1,4 @@
 use clap::builder::PossibleValue;
-#[cfg(feature = "unstable-command")]
-use clap::{FromArgMatches, Subcommand};
 use clap_complete::{generate, Generator, Shell};
 
 fn main() {
@@ -15,12 +13,6 @@ fn main() {
         return;
     }
 
-    #[cfg(feature = "unstable-command")]
-    if let Ok(completions) = clap_complete::CompleteCommand::from_arg_matches(&matches) {
-        completions.complete(&mut cli());
-        return;
-    };
-
     println!("{:?}", matches);
 }
 
@@ -30,7 +22,7 @@ fn print_completions<G: Generator>(gen: G, cmd: &mut clap::Command) {
 
 #[allow(clippy::let_and_return)]
 fn cli() -> clap::Command {
-    let cli = clap::Command::new("exhaustive")
+    clap::Command::new("exhaustive")
         .version("3.0")
         .propagate_version(true)
         .args([
@@ -197,8 +189,5 @@ fn cli() -> clap::Command {
                     .long("email")
                     .value_hint(clap::ValueHint::EmailAddress),
             ]),
-        ]);
-    #[cfg(feature = "unstable-command")]
-    let cli = clap_complete::CompleteCommand::augment_subcommands(cli);
-    cli
+        ])
 }
diff --git a/clap_complete/src/command/mod.rs b/clap_complete/src/command/mod.rs
deleted file mode 100644
index ed3ebeab..00000000
--- a/clap_complete/src/command/mod.rs
+++ /dev/null
@@ -1,253 +0,0 @@
-//! [`<bin> complete`][CompleteCommand] completion integration
-//!
-//! - If you aren't using a subcommand, see [`CompleteCommand`]
-//! - If you are using subcommands, see [`CompleteArgs`]
-//!
-//! To source your completions:
-//!
-//! **WARNING:** We recommend re-sourcing your completions on upgrade.
-//! These completions work by generating shell code that calls into `your_program` while completing.
-//! That interface is unstable and a mismatch between the shell code and `your_program` may result
-//! in either invalid completions or no completions being generated.
-//! For this reason, we recommend generating the shell code anew on shell startup so that it is
-//! "self-correcting" on shell launch, rather than writing the generated completions to a file.
-//!
-//! Bash
-//! ```bash
-//! echo "source <(your_program complete bash)" >> ~/.bashrc
-//! ```
-//!
-//! Elvish
-//! ```elvish
-//! echo "eval (your_program complete elvish)" >> ~/.elvish/rc.elv
-//! ```
-//!
-//! Fish
-//! ```fish
-//! echo "source (your_program complete fish | psub)" >> ~/.config/fish/config.fish
-//! ```
-//!
-//! Powershell
-//! ```powershell
-//! echo "your_program complete powershell | Invoke-Expression" >> $PROFILE
-//! ```
-//!
-//! Zsh
-//! ```zsh
-//! echo "source <(your_program complete zsh)" >> ~/.zshrc
-//! ```
-
-mod shells;
-
-use std::ffi::OsString;
-use std::io::Write as _;
-
-pub use shells::*;
-
-/// A completion subcommand to add to your CLI
-///
-/// To customize completions, see
-/// - [`ValueHint`][crate::ValueHint]
-/// - [`ValueEnum`][clap::ValueEnum]
-/// - [`ArgValueCandidates`][crate::ArgValueCandidates]
-/// - [`ArgValueCompleter`][crate::ArgValueCompleter]
-///
-/// **Warning:** `stdout` should not be written to before [`CompleteCommand::complete`] has had a
-/// chance to run.
-///
-/// # Examples
-///
-/// To integrate completions into an application without subcommands:
-/// ```no_run
-/// // src/main.rs
-/// use clap::{CommandFactory, FromArgMatches, Parser, Subcommand};
-/// use clap_complete::CompleteCommand;
-///
-/// #[derive(Parser, Debug)]
-/// #[clap(name = "dynamic", about = "A dynamic command line tool")]
-/// struct Cli {
-///     /// The subcommand to run complete
-///     #[command(subcommand)]
-///     complete: Option<CompleteCommand>,
-///
-///     /// Input file path
-///     #[clap(short, long, value_hint = clap::ValueHint::FilePath)]
-///     input: Option<String>,
-///     /// Output format
-///     #[clap(short = 'F', long, value_parser = ["json", "yaml", "toml"])]
-///     format: Option<String>,
-/// }
-///
-/// fn main() {
-///     let cli = Cli::parse();
-///     if let Some(completions) = cli.complete {
-///         completions.complete(&mut Cli::command());
-///     }
-///
-///     // normal logic continues...
-/// }
-///```
-#[derive(clap::Subcommand)]
-#[allow(missing_docs)]
-#[derive(Clone, Debug)]
-#[command(about = None, long_about = None)]
-pub enum CompleteCommand {
-    /// Register shell completions for this program
-    #[command(hide = true)]
-    Complete(CompleteArgs),
-}
-
-impl CompleteCommand {
-    /// Process the completion request and exit
-    ///
-    /// **Warning:** `stdout` should not be written to before this has had a
-    /// chance to run.
-    pub fn complete(&self, cmd: &mut clap::Command) -> std::convert::Infallible {
-        self.try_complete(cmd).unwrap_or_else(|e| e.exit());
-        std::process::exit(0)
-    }
-
-    /// Process the completion request
-    ///
-    /// **Warning:** `stdout` should not be written to before or after this has run.
-    pub fn try_complete(&self, cmd: &mut clap::Command) -> clap::error::Result<()> {
-        debug!("CompleteCommand::try_complete: {self:?}");
-        let CompleteCommand::Complete(args) = self;
-        args.try_complete(cmd)
-    }
-}
-
-/// A completion subcommand to add to your CLI
-///
-/// To customize completions, see
-/// - [`ValueHint`][crate::ValueHint]
-/// - [`ValueEnum`][clap::ValueEnum]
-/// - [`ArgValueCandidates`][crate::ArgValueCandidates]
-/// - [`ArgValueCompleter`][crate::ArgValueCompleter]
-///
-/// **Warning:** `stdout` should not be written to before [`CompleteArgs::complete`] has had a
-/// chance to run.
-///
-/// # Examples
-///
-/// To integrate completions into an application without subcommands:
-/// ```no_run
-/// // src/main.rs
-/// use clap::{CommandFactory, FromArgMatches, Parser, Subcommand};
-/// use clap_complete::CompleteArgs;
-///
-/// #[derive(Parser, Debug)]
-/// #[clap(name = "dynamic", about = "A dynamic command line tool")]
-/// struct Cli {
-///     #[command(subcommand)]
-///     complete: Command,
-/// }
-///
-/// #[derive(Subcommand, Debug)]
-/// enum Command {
-///     Complete(CompleteArgs),
-///     Print,
-/// }
-///
-/// fn main() {
-///     let cli = Cli::parse();
-///     match cli.complete {
-///         Command::Complete(completions) => {
-///             completions.complete(&mut Cli::command());
-///         },
-///         Command::Print => {
-///             println!("Hello world!");
-///         }
-///     }
-/// }
-///```
-#[derive(clap::Args, Clone, Debug)]
-#[command(about = None, long_about = None)]
-pub struct CompleteArgs {
-    /// Specify shell to complete for
-    #[arg(value_name = "NAME")]
-    shell: Option<Shell>,
-
-    #[arg(raw = true, value_name = "ARG", hide = true)]
-    comp_words: Option<Vec<OsString>>,
-}
-
-impl CompleteArgs {
-    /// Process the completion request and exit
-    ///
-    /// **Warning:** `stdout` should not be written to before this has had a
-    /// chance to run.
-    pub fn complete(&self, cmd: &mut clap::Command) -> std::convert::Infallible {
-        self.try_complete(cmd).unwrap_or_else(|e| e.exit());
-        std::process::exit(0)
-    }
-
-    /// Process the completion request
-    ///
-    /// **Warning:** `stdout` should not be written to before or after this has run.
-    pub fn try_complete(&self, cmd: &mut clap::Command) -> clap::error::Result<()> {
-        debug!("CompleteCommand::try_complete: {self:?}");
-
-        let shell = self.shell.or_else(Shell::from_env).ok_or_else(|| {
-            std::io::Error::new(
-                std::io::ErrorKind::Other,
-                "unknown shell, please specify the name of your shell",
-            )
-        })?;
-
-        if let Some(comp_words) = self.comp_words.as_ref() {
-            let current_dir = std::env::current_dir().ok();
-
-            let mut buf = Vec::new();
-            shell.write_complete(cmd, comp_words.clone(), current_dir.as_deref(), &mut buf)?;
-            std::io::stdout().write_all(&buf)?;
-        } else {
-            let name = cmd.get_name();
-            let bin = cmd.get_bin_name().unwrap_or_else(|| cmd.get_name());
-
-            let mut buf = Vec::new();
-            shell.write_registration(name, bin, bin, &mut buf)?;
-            std::io::stdout().write_all(&buf)?;
-        }
-
-        Ok(())
-    }
-}
-
-/// Shell-integration for completions
-///
-/// This will generally be called by [`CompleteCommand`] or [`CompleteArgs`].
-///
-/// This handles adapting between the shell and [`completer`][crate::engine::complete()].
-/// A `CommandCompleter` can choose how much of that lives within the registration script and or
-/// lives in [`CommandCompleter::write_complete`].
-pub trait CommandCompleter {
-    /// Register for completions
-    ///
-    /// Write the `buf` the logic needed for calling into `<cmd> complete`, passing needed
-    /// arguments to [`CommandCompleter::write_complete`] through the environment.
-    ///
-    /// **WARNING:** There are no stability guarantees between the call to
-    /// [`CommandCompleter::write_complete`] that this generates and actually calling [`CommandCompleter::write_complete`].
-    /// Caching the results of this call may result in invalid or no completions to be generated.
-    fn write_registration(
-        &self,
-        name: &str,
-        bin: &str,
-        completer: &str,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error>;
-    /// Complete the given command
-    ///
-    /// Adapt information from arguments and [`CommandCompleter::write_registration`]-defined env
-    /// variables to what is needed for [`completer`][crate::engine::complete()].
-    ///
-    /// Write out the [`CompletionCandidate`][crate::engine::CompletionCandidate]s in a way the shell will understand.
-    fn write_complete(
-        &self,
-        cmd: &mut clap::Command,
-        args: Vec<OsString>,
-        current_dir: Option<&std::path::Path>,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error>;
-}
diff --git a/clap_complete/src/command/shells.rs b/clap_complete/src/command/shells.rs
deleted file mode 100644
index cf1cbdd8..00000000
--- a/clap_complete/src/command/shells.rs
+++ /dev/null
@@ -1,529 +0,0 @@
-use std::ffi::OsString;
-use std::fmt::Display;
-use std::str::FromStr;
-
-use clap::builder::PossibleValue;
-use clap::ValueEnum;
-use unicode_xid::UnicodeXID as _;
-
-use super::CommandCompleter;
-
-/// Completion support for built-in shells
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-#[non_exhaustive]
-pub enum Shell {
-    /// Bourne Again `SHell` (bash)
-    Bash,
-    /// Elvish shell
-    Elvish,
-    /// Friendly Interactive `SHell` (fish)
-    Fish,
-    /// `PowerShell`
-    Powershell,
-    /// Z `SHell` (zsh)
-    Zsh,
-}
-
-impl Shell {
-    /// Parse a shell from a path to the executable for the shell
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use clap_complete::shells::Shell;
-    ///
-    /// assert_eq!(Shell::from_shell_path("/bin/bash"), Some(Shell::Bash));
-    /// assert_eq!(Shell::from_shell_path("/usr/bin/zsh"), Some(Shell::Zsh));
-    /// assert_eq!(Shell::from_shell_path("/opt/my_custom_shell"), None);
-    /// ```
-    pub fn from_shell_path(path: impl AsRef<std::path::Path>) -> Option<Shell> {
-        parse_shell_from_path(path.as_ref())
-    }
-
-    /// Determine the user's current shell from the environment
-    ///
-    /// This will read the SHELL environment variable and try to determine which shell is in use
-    /// from that.
-    ///
-    /// If SHELL is not set, then on windows, it will default to powershell, and on
-    /// other operating systems it will return `None`.
-    ///
-    /// If SHELL is set, but contains a value that doesn't correspond to one of the supported shell
-    /// types, then return `None`.
-    ///
-    /// # Example:
-    ///
-    /// ```no_run
-    /// # use clap::Command;
-    /// use clap_complete::{generate, shells::Shell};
-    /// # fn build_cli() -> Command {
-    /// #     Command::new("compl")
-    /// # }
-    /// let shell = Shell::from_env();
-    /// println!("{shell:?}");
-    /// ```
-    pub fn from_env() -> Option<Shell> {
-        if let Some(env_shell) = std::env::var_os("SHELL") {
-            Shell::from_shell_path(env_shell)
-        } else {
-            None
-        }
-    }
-
-    fn completer(&self) -> &dyn CommandCompleter {
-        match self {
-            Self::Bash => &Bash,
-            Self::Elvish => &Elvish,
-            Self::Fish => &Fish,
-            Self::Powershell => &Powershell,
-            Self::Zsh => &Zsh,
-        }
-    }
-}
-
-impl CommandCompleter for Shell {
-    fn write_registration(
-        &self,
-        name: &str,
-        bin: &str,
-        completer: &str,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        self.completer()
-            .write_registration(name, bin, completer, buf)
-    }
-    fn write_complete(
-        &self,
-        cmd: &mut clap::Command,
-        args: Vec<OsString>,
-        current_dir: Option<&std::path::Path>,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        self.completer().write_complete(cmd, args, current_dir, buf)
-    }
-}
-
-// use a separate function to avoid having to monomorphize the entire function due
-// to from_shell_path being generic
-fn parse_shell_from_path(path: &std::path::Path) -> Option<Shell> {
-    let name = path.file_stem()?.to_str()?;
-    match name {
-        "bash" => Some(Shell::Bash),
-        "elvish" => Some(Shell::Elvish),
-        "fish" => Some(Shell::Fish),
-        "powershell" | "powershell_ise" => Some(Shell::Powershell),
-        "zsh" => Some(Shell::Zsh),
-        _ => None,
-    }
-}
-
-impl Display for Shell {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        self.to_possible_value()
-            .expect("no values are skipped")
-            .get_name()
-            .fmt(f)
-    }
-}
-
-impl FromStr for Shell {
-    type Err = String;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        for variant in Self::value_variants() {
-            if variant.to_possible_value().unwrap().matches(s, false) {
-                return Ok(*variant);
-            }
-        }
-        Err(format!("invalid variant: {s}"))
-    }
-}
-
-// Hand-rolled so it can work even when `derive` feature is disabled
-impl ValueEnum for Shell {
-    fn value_variants<'a>() -> &'a [Self] {
-        &[
-            Shell::Bash,
-            Shell::Elvish,
-            Shell::Fish,
-            Shell::Powershell,
-            Shell::Zsh,
-        ]
-    }
-
-    fn to_possible_value(&self) -> Option<PossibleValue> {
-        Some(match self {
-            Shell::Bash => PossibleValue::new("bash"),
-            Shell::Elvish => PossibleValue::new("elvish"),
-            Shell::Fish => PossibleValue::new("fish"),
-            Shell::Powershell => PossibleValue::new("powershell"),
-            Shell::Zsh => PossibleValue::new("zsh"),
-        })
-    }
-}
-
-/// Bash completion adapter
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct Bash;
-
-impl CommandCompleter for Bash {
-    fn write_registration(
-        &self,
-        name: &str,
-        bin: &str,
-        completer: &str,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        let escaped_name = name.replace('-', "_");
-        debug_assert!(
-            escaped_name.chars().all(|c| c.is_xid_continue()),
-            "`name` must be an identifier, got `{escaped_name}`"
-        );
-        let mut upper_name = escaped_name.clone();
-        upper_name.make_ascii_uppercase();
-
-        let completer =
-            shlex::try_quote(completer).unwrap_or(std::borrow::Cow::Borrowed(completer));
-
-        let script = r#"
-_clap_complete_NAME() {
-    local IFS=$'\013'
-    local _CLAP_COMPLETE_INDEX=${COMP_CWORD}
-    local _CLAP_COMPLETE_COMP_TYPE=${COMP_TYPE}
-    if compopt +o nospace 2> /dev/null; then
-        local _CLAP_COMPLETE_SPACE=false
-    else
-        local _CLAP_COMPLETE_SPACE=true
-    fi
-    COMPREPLY=( $( \
-        IFS="$IFS" \
-        _CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
-        _CLAP_COMPLETE_COMP_TYPE="$_CLAP_COMPLETE_COMP_TYPE" \
-        _CLAP_COMPLETE_SPACE="$_CLAP_COMPLETE_SPACE" \
-        "COMPLETER" complete bash -- "${COMP_WORDS[@]}" \
-    ) )
-    if [[ $? != 0 ]]; then
-        unset COMPREPLY
-    elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
-        compopt -o nospace
-    fi
-}
-if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
-    complete -o nospace -o bashdefault -o nosort -F _clap_complete_NAME BIN
-else
-    complete -o nospace -o bashdefault -F _clap_complete_NAME BIN
-fi
-"#
-        .replace("NAME", &escaped_name)
-        .replace("BIN", bin)
-        .replace("COMPLETER", &completer)
-        .replace("UPPER", &upper_name);
-
-        writeln!(buf, "{script}")?;
-        Ok(())
-    }
-    fn write_complete(
-        &self,
-        cmd: &mut clap::Command,
-        args: Vec<OsString>,
-        current_dir: Option<&std::path::Path>,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        let index: usize = std::env::var("_CLAP_COMPLETE_INDEX")
-            .ok()
-            .and_then(|i| i.parse().ok())
-            .unwrap_or_default();
-        let _comp_type: CompType = std::env::var("_CLAP_COMPLETE_COMP_TYPE")
-            .ok()
-            .and_then(|i| i.parse().ok())
-            .unwrap_or_default();
-        let _space: Option<bool> = std::env::var("_CLAP_COMPLETE_SPACE")
-            .ok()
-            .and_then(|i| i.parse().ok());
-        let ifs: Option<String> = std::env::var("IFS").ok().and_then(|i| i.parse().ok());
-        let completions = crate::engine::complete(cmd, args, index, current_dir)?;
-
-        for (i, candidate) in completions.iter().enumerate() {
-            if i != 0 {
-                write!(buf, "{}", ifs.as_deref().unwrap_or("\n"))?;
-            }
-            write!(buf, "{}", candidate.get_value().to_string_lossy())?;
-        }
-        Ok(())
-    }
-}
-
-/// Type of completion attempted that caused a completion function to be called
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-#[non_exhaustive]
-enum CompType {
-    /// Normal completion
-    Normal,
-    /// List completions after successive tabs
-    Successive,
-    /// List alternatives on partial word completion
-    Alternatives,
-    /// List completions if the word is not unmodified
-    Unmodified,
-    /// Menu completion
-    Menu,
-}
-
-impl FromStr for CompType {
-    type Err = String;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "9" => Ok(Self::Normal),
-            "63" => Ok(Self::Successive),
-            "33" => Ok(Self::Alternatives),
-            "64" => Ok(Self::Unmodified),
-            "37" => Ok(Self::Menu),
-            _ => Err(format!("unsupported COMP_TYPE `{}`", s)),
-        }
-    }
-}
-
-impl Default for CompType {
-    fn default() -> Self {
-        Self::Normal
-    }
-}
-
-/// Elvish completion adapter
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct Elvish;
-
-impl CommandCompleter for Elvish {
-    fn write_registration(
-        &self,
-        _name: &str,
-        bin: &str,
-        completer: &str,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        let bin = shlex::try_quote(bin).unwrap_or(std::borrow::Cow::Borrowed(bin));
-        let completer =
-            shlex::try_quote(completer).unwrap_or(std::borrow::Cow::Borrowed(completer));
-
-        let script = r#"
-set edit:completion:arg-completer[BIN] = { |@words|
-    set E:_CLAP_IFS = "\n"
-
-    var index = (count $words)
-    set index = (- $index 1)
-    set E:_CLAP_COMPLETE_INDEX = (to-string $index)
-
-    put (COMPLETER complete elvish -- $@words) | to-lines
-}
-"#
-        .replace("COMPLETER", &completer)
-        .replace("BIN", &bin);
-
-        writeln!(buf, "{script}")?;
-        Ok(())
-    }
-    fn write_complete(
-        &self,
-        cmd: &mut clap::Command,
-        args: Vec<OsString>,
-        current_dir: Option<&std::path::Path>,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        let index: usize = std::env::var("_CLAP_COMPLETE_INDEX")
-            .ok()
-            .and_then(|i| i.parse().ok())
-            .unwrap_or_default();
-        let ifs: Option<String> = std::env::var("_CLAP_IFS").ok().and_then(|i| i.parse().ok());
-        let completions = crate::engine::complete(cmd, args, index, current_dir)?;
-
-        for (i, candidate) in completions.iter().enumerate() {
-            if i != 0 {
-                write!(buf, "{}", ifs.as_deref().unwrap_or("\n"))?;
-            }
-            write!(buf, "{}", candidate.get_value().to_string_lossy())?;
-        }
-        Ok(())
-    }
-}
-
-/// Fish completion adapter
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct Fish;
-
-impl CommandCompleter for Fish {
-    fn write_registration(
-        &self,
-        _name: &str,
-        bin: &str,
-        completer: &str,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        let bin = shlex::try_quote(bin).unwrap_or(std::borrow::Cow::Borrowed(bin));
-        let completer =
-            shlex::try_quote(completer).unwrap_or(std::borrow::Cow::Borrowed(completer));
-
-        writeln!(
-            buf,
-            r#"complete -x -c {bin} -a "("'{completer}'" complete fish -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))""#
-        )
-    }
-    fn write_complete(
-        &self,
-        cmd: &mut clap::Command,
-        args: Vec<OsString>,
-        current_dir: Option<&std::path::Path>,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        let index = args.len() - 1;
-        let completions = crate::engine::complete(cmd, args, index, current_dir)?;
-
-        for candidate in completions {
-            write!(buf, "{}", candidate.get_value().to_string_lossy())?;
-            if let Some(help) = candidate.get_help() {
-                write!(
-                    buf,
-                    "\t{}",
-                    help.to_string().lines().next().unwrap_or_default()
-                )?;
-            }
-            writeln!(buf)?;
-        }
-        Ok(())
-    }
-}
-
-/// Powershell completion adapter
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct Powershell;
-
-impl CommandCompleter for Powershell {
-    fn write_registration(
-        &self,
-        _name: &str,
-        bin: &str,
-        completer: &str,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        let bin = shlex::try_quote(bin).unwrap_or(std::borrow::Cow::Borrowed(bin));
-        let completer =
-            shlex::try_quote(completer).unwrap_or(std::borrow::Cow::Borrowed(completer));
-
-        writeln!(
-            buf,
-            r#"
-Register-ArgumentCompleter -Native -CommandName {bin} -ScriptBlock {{
-    param($wordToComplete, $commandAst, $cursorPosition)
-
-    $results = Invoke-Expression "&{completer} complete powershell -- $($commandAst.ToString())";
-    $results | ForEach-Object {{
-        $split = $_.Split("`t");
-        $cmd = $split[0];
-
-        if ($split.Length -eq 2) {{
-            $help = $split[1];
-        }}
-        else {{
-            $help = $split[0];
-        }}
-
-        [System.Management.Automation.CompletionResult]::new($cmd, $cmd, 'ParameterValue', $help)
-    }}
-}};
-        "#
-        )
-    }
-
-    fn write_complete(
-        &self,
-        cmd: &mut clap::Command,
-        args: Vec<OsString>,
-        current_dir: Option<&std::path::Path>,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        let index = args.len() - 1;
-        let completions = crate::engine::complete(cmd, args, index, current_dir)?;
-
-        for candidate in completions {
-            write!(buf, "{}", candidate.get_value().to_string_lossy())?;
-            if let Some(help) = candidate.get_help() {
-                write!(
-                    buf,
-                    "\t{}",
-                    help.to_string().lines().next().unwrap_or_default()
-                )?;
-            }
-            writeln!(buf)?;
-        }
-        Ok(())
-    }
-}
-
-/// Zsh completion adapter
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct Zsh;
-
-impl CommandCompleter for Zsh {
-    fn write_registration(
-        &self,
-        _name: &str,
-        bin: &str,
-        completer: &str,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        let bin = shlex::try_quote(bin).unwrap_or(std::borrow::Cow::Borrowed(bin));
-        let completer =
-            shlex::try_quote(completer).unwrap_or(std::borrow::Cow::Borrowed(completer));
-
-        let script = r#"#compdef BIN
-function _clap_dynamic_completer() {
-    local _CLAP_COMPLETE_INDEX=$(expr $CURRENT - 1)
-    local _CLAP_IFS=$'\n'
-
-    local completions=("${(@f)$( \
-        _CLAP_IFS="$_CLAP_IFS" \
-        _CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
-        COMPLETER complete zsh -- ${words} 2>/dev/null \
-    )}")
-
-    if [[ -n $completions ]]; then
-        compadd -a completions
-    fi
-}
-
-compdef _clap_dynamic_completer BIN"#
-            .replace("COMPLETER", &completer)
-            .replace("BIN", &bin);
-
-        writeln!(buf, "{script}")?;
-        Ok(())
-    }
-    fn write_complete(
-        &self,
-        cmd: &mut clap::Command,
-        args: Vec<OsString>,
-        current_dir: Option<&std::path::Path>,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error> {
-        let index: usize = std::env::var("_CLAP_COMPLETE_INDEX")
-            .ok()
-            .and_then(|i| i.parse().ok())
-            .unwrap_or_default();
-        let ifs: Option<String> = std::env::var("_CLAP_IFS").ok().and_then(|i| i.parse().ok());
-
-        // If the current word is empty, add an empty string to the args
-        let mut args = args.clone();
-        if args.len() == index {
-            args.push("".into());
-        }
-        let completions = crate::engine::complete(cmd, args, index, current_dir)?;
-
-        for (i, candidate) in completions.iter().enumerate() {
-            if i != 0 {
-                write!(buf, "{}", ifs.as_deref().unwrap_or("\n"))?;
-            }
-            write!(buf, "{}", candidate.get_value().to_string_lossy())?;
-        }
-        Ok(())
-    }
-}
diff --git a/clap_complete/src/lib.rs b/clap_complete/src/lib.rs
index 099ed692..43feab1e 100644
--- a/clap_complete/src/lib.rs
+++ b/clap_complete/src/lib.rs
@@ -66,18 +66,12 @@ const INTERNAL_ERROR_MSG: &str = "Fatal internal error. Please consider filing a
 mod macros;
 
 pub mod aot;
-#[cfg(feature = "unstable-command")]
-pub mod command;
 #[cfg(feature = "unstable-dynamic")]
 pub mod engine;
 #[cfg(feature = "unstable-dynamic")]
 pub mod env;
 
 pub use clap::ValueHint;
-#[cfg(feature = "unstable-command")]
-pub use command::CompleteArgs;
-#[cfg(feature = "unstable-command")]
-pub use command::CompleteCommand;
 #[doc(inline)]
 #[cfg(feature = "unstable-dynamic")]
 pub use engine::ArgValueCandidates;


Test patch:
diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/bash/.bashrc b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/bash/.bashrc
deleted file mode 100644
index 71a223df..00000000
--- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/bash/.bashrc
+++ /dev/null
@@ -1,32 +0,0 @@
-PS1='% '
-. /etc/bash_completion
-
-_clap_complete_exhaustive() {
-    local IFS=$'\013'
-    local _CLAP_COMPLETE_INDEX=${COMP_CWORD}
-    local _CLAP_COMPLETE_COMP_TYPE=${COMP_TYPE}
-    if compopt +o nospace 2> /dev/null; then
-        local _CLAP_COMPLETE_SPACE=false
-    else
-        local _CLAP_COMPLETE_SPACE=true
-    fi
-    COMPREPLY=( $( \
-        IFS="$IFS" \
-        _CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
-        _CLAP_COMPLETE_COMP_TYPE="$_CLAP_COMPLETE_COMP_TYPE" \
-        _CLAP_COMPLETE_SPACE="$_CLAP_COMPLETE_SPACE" \
-        "exhaustive" complete bash -- "${COMP_WORDS[@]}" \
-    ) )
-    if [[ $? != 0 ]]; then
-        unset COMPREPLY
-    elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
-        compopt -o nospace
-    fi
-}
-if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
-    complete -o nospace -o bashdefault -o nosort -F _clap_complete_exhaustive exhaustive
-else
-    complete -o nospace -o bashdefault -F _clap_complete_exhaustive exhaustive
-fi
-
-
diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/elvish/elvish/rc.elv b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/elvish/elvish/rc.elv
deleted file mode 100644
index 614e4a29..00000000
--- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/elvish/elvish/rc.elv
+++ /dev/null
@@ -1,14 +0,0 @@
-set edit:rprompt = (constantly "")
-set edit:prompt = (constantly "% ")
-
-set edit:completion:arg-completer[exhaustive] = { |@words|
-    set E:_CLAP_IFS = "\n"
-
-    var index = (count $words)
-    set index = (- $index 1)
-    set E:_CLAP_COMPLETE_INDEX = (to-string $index)
-
-    put (exhaustive complete elvish -- $@words) | to-lines
-}
-
-
diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/completions/exhaustive.fish b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/completions/exhaustive.fish
deleted file mode 100644
index 34301ce9..00000000
--- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/completions/exhaustive.fish
+++ /dev/null
@@ -1,1 +0,0 @@
-complete -x -c exhaustive -a "("'exhaustive'" complete fish -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))"
diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/config.fish b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/config.fish
deleted file mode 100644
index 74bd2f00..00000000
--- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/config.fish
+++ /dev/null
@@ -1,7 +0,0 @@
-set -U fish_greeting ""
-set -U fish_autosuggestion_enabled 0
-function fish_title
-end
-function fish_prompt
-    printf '%% '
-end;
diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/.zshenv b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/.zshenv
deleted file mode 100644
index 6d309f24..00000000
--- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/.zshenv
+++ /dev/null
@@ -1,5 +0,0 @@
-fpath=($fpath $ZDOTDIR/zsh)
-autoload -U +X compinit && compinit -u # bypass compaudit security checking
-precmd_functions=""  # avoid the prompt being overwritten
-PS1='%% '
-PROMPT='%% '
diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/zsh/_exhaustive b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/zsh/_exhaustive
deleted file mode 100644
index fbc8bc36..00000000
--- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/zsh/_exhaustive
+++ /dev/null
@@ -1,17 +0,0 @@
-#compdef exhaustive
-function _clap_dynamic_completer() {
-    local _CLAP_COMPLETE_INDEX=$(expr $CURRENT - 1)
-    local _CLAP_IFS=$'\n'
-
-    local completions=("${(@f)$( \
-        _CLAP_IFS="$_CLAP_IFS" \
-        _CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
-        exhaustive complete zsh -- ${words} 2>/dev/null \
-    )}")
-
-    if [[ -n $completions ]]; then
-        compadd -a completions
-    fi
-}
-
-compdef _clap_dynamic_completer exhaustive
diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc
index 68a5a1a1..b8ce1da5 100644
--- a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc
+++ b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc
@@ -20,9 +20,6 @@ _exhaustive() {
             exhaustive,alias)
                 cmd="exhaustive__alias"
                 ;;
-            exhaustive,complete)
-                cmd="exhaustive__complete"
-                ;;
             exhaustive,help)
                 cmd="exhaustive__help"
                 ;;
@@ -47,9 +44,6 @@ _exhaustive() {
             exhaustive__help,alias)
                 cmd="exhaustive__help__alias"
                 ;;
-            exhaustive__help,complete)
-                cmd="exhaustive__help__complete"
-                ;;
             exhaustive__help,help)
                 cmd="exhaustive__help__help"
                 ;;
@@ -168,7 +162,7 @@ _exhaustive() {
 
     case "${cmd}" in
         exhaustive)
-            opts="-h -V --global --generate --help --version action quote value pacman last alias hint complete help"
+            opts="-h -V --global --generate --help --version action quote value pacman last alias hint help"
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -237,22 +231,8 @@ _exhaustive() {
             COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
             return 0
             ;;
-        exhaustive__complete)
-            opts="-h -V --global --help --version bash elvish fish powershell zsh [ARG]..."
-            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
-                return 0
-            fi
-            case "${prev}" in
-                *)
-                    COMPREPLY=()
-                    ;;
-            esac
-            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
-            return 0
-            ;;
         exhaustive__help)
-            opts="action quote value pacman last alias hint complete help"
+            opts="action quote value pacman last alias hint help"
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -293,20 +273,6 @@ _exhaustive() {
             COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
             return 0
             ;;
-        exhaustive__help__complete)
-            opts=""
-            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
-                return 0
-            fi
-            case "${prev}" in
-                *)
-                    COMPREPLY=()
-                    ;;
-            esac
-            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
-            return 0
-            ;;
         exhaustive__help__help)
             opts=""
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv b/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv
index 2fc89206..afde72f4 100644
--- a/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv
+++ b/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv
@@ -33,7 +33,6 @@ set edit:completion:arg-completer[exhaustive] = {|@words|
             cand last 'last'
             cand alias 'alias'
             cand hint 'hint'
-            cand complete 'Register shell completions for this program'
             cand help 'Print this message or the help of the given subcommand(s)'
         }
         &'exhaustive;action'= {
@@ -238,13 +237,6 @@ set edit:completion:arg-completer[exhaustive] = {|@words|
             cand -V 'Print version'
             cand --version 'Print version'
         }
-        &'exhaustive;complete'= {
-            cand --global 'everywhere'
-            cand -h 'Print help'
-            cand --help 'Print help'
-            cand -V 'Print version'
-            cand --version 'Print version'
-        }
         &'exhaustive;help'= {
             cand action 'action'
             cand quote 'quote'
@@ -253,7 +245,6 @@ set edit:completion:arg-completer[exhaustive] = {|@words|
             cand last 'last'
             cand alias 'alias'
             cand hint 'hint'
-            cand complete 'Register shell completions for this program'
             cand help 'Print this message or the help of the given subcommand(s)'
         }
         &'exhaustive;help;action'= {
@@ -297,8 +288,6 @@ set edit:completion:arg-completer[exhaustive] = {|@words|
         }
         &'exhaustive;help;hint'= {
         }
-        &'exhaustive;help;complete'= {
-        }
         &'exhaustive;help;help'= {
         }
     ]
diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish
index 92d6d42e..dd17413c 100644
--- a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish
+++ b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish
@@ -35,7 +35,6 @@ complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "pacman"
 complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "last"
 complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "alias"
 complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "hint"
-complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "complete" -d 'Register shell completions for this program'
 complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -l set -d 'value' -r
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -l choice -d 'enum' -r -f -a "{first\t'',second\t''}"
@@ -136,18 +135,14 @@ complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l email -r
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l global -d 'everywhere'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -s h -l help -d 'Print help'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -s V -l version -d 'Print version'
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -l global -d 'everywhere'
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -s h -l help -d 'Print help'
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -s V -l version -d 'Print version'
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "action"
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "quote"
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "value"
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "pacman"
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "last"
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "alias"
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "hint"
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "complete" -d 'Register shell completions for this program'
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
+complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "action"
+complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "quote"
+complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "value"
+complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "pacman"
+complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "last"
+complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "alias"
+complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "hint"
+complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and __fish_seen_subcommand_from quote" -f -a "cmd-single-quotes" -d 'Can be \'always\', \'auto\', or \'never\''
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and __fish_seen_subcommand_from quote" -f -a "cmd-double-quotes" -d 'Can be "always", "auto", or "never"'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and __fish_seen_subcommand_from quote" -f -a "cmd-backticks" -d 'For more information see `echo test`'
diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive
index 73f57fdf..4ceaca2d 100644
--- a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive
+++ b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive
@@ -323,17 +323,6 @@ _arguments "${_arguments_options[@]}" : \
 '*::command_with_args:_cmdambivalent' \
 && ret=0
 ;;
-(complete)
-_arguments "${_arguments_options[@]}" : \
-'--global[everywhere]' \
-'-h[Print help]' \
-'--help[Print help]' \
-'-V[Print version]' \
-'--version[Print version]' \
-'::shell -- Specify shell to complete for:(bash elvish fish powershell zsh)' \
-'*::comp_words:' \
-&& ret=0
-;;
 (help)
 _arguments "${_arguments_options[@]}" : \
 ":: :_exhaustive__help_commands" \
@@ -434,10 +423,6 @@ _arguments "${_arguments_options[@]}" : \
 _arguments "${_arguments_options[@]}" : \
 && ret=0
 ;;
-(complete)
-_arguments "${_arguments_options[@]}" : \
-&& ret=0
-;;
 (help)
 _arguments "${_arguments_options[@]}" : \
 && ret=0
@@ -461,7 +446,6 @@ _exhaustive_commands() {
 'last:' \
 'alias:' \
 'hint:' \
-'complete:Register shell completions for this program' \
 'help:Print this message or the help of the given subcommand(s)' \
     )
     _describe -t commands 'exhaustive commands' commands "$@"
@@ -476,11 +460,6 @@ _exhaustive__alias_commands() {
     local commands; commands=()
     _describe -t commands 'exhaustive alias commands' commands "$@"
 }
-(( $+functions[_exhaustive__complete_commands] )) ||
-_exhaustive__complete_commands() {
-    local commands; commands=()
-    _describe -t commands 'exhaustive complete commands' commands "$@"
-}
 (( $+functions[_exhaustive__help_commands] )) ||
 _exhaustive__help_commands() {
     local commands; commands=(
@@ -491,7 +470,6 @@ _exhaustive__help_commands() {
 'last:' \
 'alias:' \
 'hint:' \
-'complete:Register shell completions for this program' \
 'help:Print this message or the help of the given subcommand(s)' \
     )
     _describe -t commands 'exhaustive help commands' commands "$@"
@@ -506,11 +484,6 @@ _exhaustive__help__alias_commands() {
     local commands; commands=()
     _describe -t commands 'exhaustive help alias commands' commands "$@"
 }
-(( $+functions[_exhaustive__help__complete_commands] )) ||
-_exhaustive__help__complete_commands() {
-    local commands; commands=()
-    _describe -t commands 'exhaustive help complete commands' commands "$@"
-}
 (( $+functions[_exhaustive__help__help_commands] )) ||
 _exhaustive__help__help_commands() {
     local commands; commands=()
diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs
index 00c57c8f..a90b180b 100644
--- a/clap_complete/tests/testsuite/bash.rs
+++ b/clap_complete/tests/testsuite/bash.rs
@@ -111,24 +111,6 @@ fn value_terminator() {
     );
 }
 
-#[cfg(feature = "unstable-command")]
-#[test]
-fn register_minimal() {
-    use clap_complete::command::CommandCompleter as _;
-
-    let name = "my-app";
-    let bin = name;
-    let completer = name;
-
-    let mut buf = Vec::new();
-    clap_complete::command::Bash
-        .write_registration(name, bin, completer, &mut buf)
-        .unwrap();
-    snapbox::Assert::new()
-        .action_env("SNAPSHOTS")
-        .eq(buf, snapbox::file!["../snapshots/register_minimal.bash"]);
-}
-
 #[test]
 fn two_multi_valued_arguments() {
     let name = "my-app";
@@ -171,15 +153,17 @@ fn complete() {
         common::load_runtime::<completest_pty::BashRuntimeBuilder>("static", "exhaustive");
 
     let input = "exhaustive \t\t";
-    let expected = r#"% 
--h          --global    --help      action      value       last        hint        help        
--V          --generate  --version   quote       pacman      alias       complete    "#;
+    let expected = snapbox::str![[r#"
+% 
+-h          --global    --help      action      value       last        hint        
+-V          --generate  --version   quote       pacman      alias       help        
+"#]];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
 
     // Issue 5239 (https://github.com/clap-rs/clap/issues/5239)
     let input = "exhaustive hint --file test\t";
-    let expected = "exhaustive hint --file test     % exhaustive hint --file tests/";
+    let expected = snapbox::str!["exhaustive hint --file test     % exhaustive hint --file tests/"];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
 
@@ -285,41 +269,3 @@ fn complete_dynamic_env() {
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
 }
-
-#[test]
-#[cfg(all(unix, feature = "unstable-command"))]
-fn register_dynamic_command() {
-    common::register_example::<completest_pty::BashRuntimeBuilder>("dynamic-command", "exhaustive");
-}
-
-#[test]
-#[cfg(all(unix, feature = "unstable-command"))]
-fn complete_dynamic_command() {
-    if !common::has_command("bash") {
-        return;
-    }
-
-    let term = completest::Term::new();
-    let mut runtime =
-        common::load_runtime::<completest_pty::BashRuntimeBuilder>("dynamic-command", "exhaustive");
-
-    let input = "exhaustive \t\t";
-    let expected = snapbox::str![[r#"
-% 
---global    --help      -h          action      help        last        quote       
---generate  --version   -V          alias       hint        pacman      value       
-"#]];
-    let actual = runtime.complete(input, &term).unwrap();
-    assert_data_eq!(actual, expected);
-
-    let input = "exhaustive quote \t\t";
-    let expected = snapbox::str![[r#"
-% 
---single-quotes    --brackets         --help             cmd-backslash      cmd-expansions     
---double-quotes    --expansions       --version          cmd-backticks      cmd-single-quotes  
---backticks        --choice           -h                 cmd-brackets       escape-help        
---backslash        --global           -V                 cmd-double-quotes  help               
-"#]];
-    let actual = runtime.complete(input, &term).unwrap();
-    assert_data_eq!(actual, expected);
-}
diff --git a/clap_complete/tests/testsuite/common.rs b/clap_complete/tests/testsuite/common.rs
index 1c6290f8..d388718d 100644
--- a/clap_complete/tests/testsuite/common.rs
+++ b/clap_complete/tests/testsuite/common.rs
@@ -321,7 +321,6 @@ pub(crate) fn register_example<R: completest::RuntimeBuilder>(context: &str, nam
             // Unconditionally include to avoid completion file tests failing based on the how
             // `cargo test` is invoked
             "--features=unstable-dynamic",
-            "--features=unstable-command",
         ],
     )
     .unwrap();
@@ -381,7 +380,6 @@ where
             // Unconditionally include to avoid completion file tests failing based on the how
             // `cargo test` is invoked
             "--features=unstable-dynamic",
-            "--features=unstable-command",
         ],
     )
     .unwrap();
diff --git a/clap_complete/tests/testsuite/elvish.rs b/clap_complete/tests/testsuite/elvish.rs
index 137c2662..8cc49700 100644
--- a/clap_complete/tests/testsuite/elvish.rs
+++ b/clap_complete/tests/testsuite/elvish.rs
@@ -163,7 +163,6 @@ fn complete() {
 -h             Print help                                               
 action         action                                                   
 alias          alias                                                    
-complete       Register shell completions for this program              
 help           Print this message or the help of the given subcommand(s)
 hint           hint                                                     
 last           last                                                     
@@ -214,48 +213,3 @@ fn complete_dynamic_env() {
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
 }
-
-#[test]
-#[cfg(all(unix, feature = "unstable-command"))]
-fn register_dynamic_command() {
-    common::register_example::<completest_pty::ElvishRuntimeBuilder>(
-        "dynamic-command",
-        "exhaustive",
-    );
-}
-
-#[test]
-#[cfg(all(unix, feature = "unstable-command"))]
-fn complete_dynamic_command() {
-    if !common::has_command("elvish") {
-        return;
-    }
-
-    let term = completest::Term::new();
-    let mut runtime = common::load_runtime::<completest_pty::ElvishRuntimeBuilder>(
-        "dynamic-command",
-        "exhaustive",
-    );
-
-    let input = "exhaustive \t";
-    let expected = snapbox::str![[r#"
-% exhaustive --generate
- COMPLETING argument  
---generate  --help     -V  action  help  last    quote
---global    --version  -h  alias   hint  pacman  value
-"#]];
-    let actual = runtime.complete(input, &term).unwrap();
-    assert_data_eq!(actual, expected);
-
-    let input = "exhaustive quote \t";
-    let expected = snapbox::str![[r#"
-% exhaustive quote --backslash
- COMPLETING argument  
---backslash  --double-quotes  --single-quotes  cmd-backslash      cmd-expansions   
---backticks  --expansions     --version        cmd-backticks      cmd-single-quotes
---brackets   --global         -V               cmd-brackets       escape-help      
---choice     --help           -h               cmd-double-quotes  help             
-"#]];
-    let actual = runtime.complete(input, &term).unwrap();
-    assert_data_eq!(actual, expected);
-}
diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs
index d50d5878..04dc559d 100644
--- a/clap_complete/tests/testsuite/fish.rs
+++ b/clap_complete/tests/testsuite/fish.rs
@@ -152,16 +152,20 @@ fn complete() {
         common::load_runtime::<completest_pty::FishRuntimeBuilder>("static", "exhaustive");
 
     let input = "exhaustive \t";
-    let expected = r#"% exhaustive 
-action  complete            (Register shell completions for this program)  hint  pacman  value
-alias   help  (Print this message or the help of the given subcommand(s))  last  quote   "#;
+    let expected = snapbox::str![[r#"
+% exhaustive 
+action  help  (Print this message or the help of the given subcommand(s))  last    quote
+alias   hint                                                               pacman  value
+"#]];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
 
     let input = "exhaustive quote --choice \t";
     let actual = runtime.complete(input, &term).unwrap();
-    let expected = r#"% exhaustive quote --choice 
-bash  (bash (shell))  fish  (fish shell)  zsh  (zsh shell)"#;
+    let expected = snapbox::str![[r#"
+% exhaustive quote --choice 
+bash  (bash (shell))  fish  (fish shell)  zsh  (zsh shell)
+"#]];
     assert_data_eq!(actual, expected);
 }
 
@@ -221,60 +225,3 @@ help  (Print this message or the help of the given subcommand(s))
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
 }
-
-#[test]
-#[cfg(all(unix, feature = "unstable-command"))]
-fn register_dynamic_command() {
-    common::register_example::<completest_pty::FishRuntimeBuilder>("dynamic-command", "exhaustive");
-}
-
-#[test]
-#[cfg(all(unix, feature = "unstable-command"))]
-fn complete_dynamic_command() {
-    if !common::has_command("fish") {
-        return;
-    }
-
-    let term = completest::Term::new();
-    let mut runtime =
-        common::load_runtime::<completest_pty::FishRuntimeBuilder>("dynamic-command", "exhaustive");
-
-    let input = "exhaustive \t\t";
-    let expected = snapbox::str![[r#"
-% exhaustive action 
-action                                                             pacman               --generate      (generate)
-alias                                                              quote                --global      (everywhere)
-help  (Print this message or the help of the given subcommand(s))  value                --help        (Print help)
-hint                                                               -h     (Print help)  --version  (Print version)
-last                                                               -V  (Print version)  
-"#]];
-    let actual = runtime.complete(input, &term).unwrap();
-    assert_data_eq!(actual, expected);
-
-    let input = "exhaustive quote \t\t";
-    let expected = snapbox::str![[r#"
-% exhaustive quote 
-cmd-backslash                                        (Avoid '/n')
-cmd-backticks              (For more information see `echo test`)
-cmd-brackets                             (List packages [filter])
-cmd-double-quotes           (Can be "always", "auto", or "never")
-cmd-expansions            (Execute the shell command with $SHELL)
-cmd-single-quotes           (Can be 'always', 'auto', or 'never')
-escape-help                                             (/tab "')
-help  (Print this message or the help of the given subcommand(s))
--h                          (Print help (see more with '--help'))
--V                                                (Print version)
---backslash                                          (Avoid '/n')
---backticks                (For more information see `echo test`)
---brackets                               (List packages [filter])
---choice                                                         
---double-quotes             (Can be "always", "auto", or "never")
---expansions              (Execute the shell command with $SHELL)
---global                                             (everywhere)
---help                      (Print help (see more with '--help'))
---single-quotes             (Can be 'always', 'auto', or 'never')
---version                                         (Print version)
-"#]];
-    let actual = runtime.complete(input, &term).unwrap();
-    assert_data_eq!(actual, expected);
-}
diff --git a/clap_complete/tests/testsuite/zsh.rs b/clap_complete/tests/testsuite/zsh.rs
index 02e662b2..17a5e347 100644
--- a/clap_complete/tests/testsuite/zsh.rs
+++ b/clap_complete/tests/testsuite/zsh.rs
@@ -155,9 +155,8 @@ fn complete() {
     let input = "exhaustive \t";
     let expected = snapbox::str![[r#"
 % exhaustive
-complete                                           -- Register shell completions for this program                     
-help                                               -- Print this message or the help of the given subcommand(s)       
-pacman    action  alias  value  quote  hint  last  --                                                                 
+help                                             -- Print this message or the help of the given subcommand(s)         
+pacman  action  alias  value  quote  hint  last  --                                                                   
 "#]];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
@@ -200,41 +199,3 @@ fn complete_dynamic_env() {
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
 }
-
-#[test]
-#[cfg(all(unix, feature = "unstable-command"))]
-fn register_dynamic_command() {
-    common::register_example::<completest_pty::ZshRuntimeBuilder>("dynamic-command", "exhaustive");
-}
-
-#[test]
-#[cfg(all(unix, feature = "unstable-command"))]
-fn complete_dynamic_command() {
-    if !common::has_command("zsh") {
-        return;
-    }
-
-    let term = completest::Term::new();
-    let mut runtime =
-        common::load_runtime::<completest_pty::ZshRuntimeBuilder>("dynamic-command", "exhaustive");
-
-    let input = "exhaustive \t\t";
-    let expected = snapbox::str![[r#"
-% exhaustive
---generate  --help      -V          action      help        last        quote       
---global    --version   -h          alias       hint        pacman      value       
-"#]];
-    let actual = runtime.complete(input, &term).unwrap();
-    assert_data_eq!(actual, expected);
-
-    let input = "exhaustive quote \t\t";
-    let expected = snapbox::str![[r#"
-% exhaustive quote
---backslash        --double-quotes    --single-quotes    cmd-backslash      cmd-expansions     
---backticks        --expansions       --version          cmd-backticks      cmd-single-quotes  
---brackets         --global           -V                 cmd-brackets       escape-help        
---choice           --help             -h                 cmd-double-quotes  help               
-"#]];
-    let actual = runtime.complete(input, &term).unwrap();
-    assert_data_eq!(actual, expected);
-}