diff --git a/Cargo.lock b/Cargo.lock index d16de3e..99956d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1116,6 +1116,16 @@ dependencies = [ "shlex", ] +[[package]] +name = "clap_complete_nushell" +version = "4.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "811159f339691baacdf7d534df2946b9d217014081099e23d31d887d99521e70" +dependencies = [ + "clap", + "clap_complete", +] + [[package]] name = "clap_derive" version = "4.5.49" @@ -1637,7 +1647,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2576,7 +2586,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.1", "tokio", "tower-service", "tracing", @@ -3073,6 +3083,7 @@ dependencies = [ "chrono", "clap", "clap_complete", + "clap_complete_nushell", "colored", "crossterm 0.28.1", "dirs", @@ -5421,7 +5432,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix 1.0.7", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 718d5f8..6f7cd42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ rustpython-ast = "0.4.0" colored = "3.0.0" clap_complete = { version = "4.5.58", features = ["unstable-dynamic"] } gman = "0.2.3" +clap_complete_nushell = "4.5.9" [dependencies.reqwest] version = "0.12.0" diff --git a/src/cli/completer.rs b/src/cli/completer.rs index f696acb..0c457b6 100644 --- a/src/cli/completer.rs +++ b/src/cli/completer.rs @@ -1,7 +1,34 @@ use crate::client::{list_models, ModelType}; use crate::config::{list_agents, Config}; -use clap_complete::CompletionCandidate; +use clap_complete::{generate, CompletionCandidate, Shell}; +use clap_complete_nushell::Nushell; use std::ffi::OsStr; +use std::io; + +const LOKI_CLI_NAME: &str = "loki"; + +#[derive(Clone, Copy, Debug, clap::ValueEnum)] +pub enum ShellCompletion { + Bash, + Elvish, + Fish, + PowerShell, + Zsh, + Nushell, +} + +impl ShellCompletion { + pub fn generate_completions(self, cmd: &mut clap::Command) { + match self { + Self::Bash => generate(Shell::Bash, cmd, LOKI_CLI_NAME, &mut io::stdout()), + Self::Elvish => generate(Shell::Elvish, cmd, LOKI_CLI_NAME, &mut io::stdout()), + Self::Fish => generate(Shell::Fish, cmd, LOKI_CLI_NAME, &mut io::stdout()), + Self::PowerShell => generate(Shell::PowerShell, cmd, LOKI_CLI_NAME, &mut io::stdout()), + Self::Zsh => generate(Shell::Zsh, cmd, LOKI_CLI_NAME, &mut io::stdout()), + Self::Nushell => generate(Nushell, cmd, LOKI_CLI_NAME, &mut io::stdout()), + } + } +} pub(super) fn model_completer(current: &OsStr) -> Vec { let cur = current.to_string_lossy(); diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 4a81219..aa2e5c4 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -2,7 +2,7 @@ mod completer; use crate::cli::completer::{ agent_completer, macro_completer, model_completer, rag_completer, role_completer, - secrets_completer, session_completer, + secrets_completer, session_completer, ShellCompletion, }; use anyhow::{Context, Result}; use clap::ValueHint; @@ -130,6 +130,9 @@ pub struct Cli { /// List all secrets stored in the Loki vault #[arg(long, exclusive = true)] pub list_secrets: bool, + /// Generate static shell completion scripts + #[arg(long, value_enum)] + pub completions: Option, } impl Cli { diff --git a/src/main.rs b/src/main.rs index 4a02a38..addbfbf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,6 +47,11 @@ async fn main() -> Result<()> { CompleteEnv::with_factory(Cli::command).complete(); let cli = Cli::parse(); + if let Some(shell) = cli.completions { + let mut cmd = Cli::command(); + shell.generate_completions(&mut cmd); + return Ok(()); + } if cli.tail_logs { tail_logs(cli.disable_log_colors).await; return Ok(());