Convert opencode transcripts to otel (or agent) traces
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement shell completions with clap_complete

- Add clap_complete and home dependencies
- Add CompletionShell enum (bash, zsh, fish, PowerShell, elvish)
- Create completions module with generate_completion() function
- Add completions subcommand to CLI with --print flag
- Support both stdout output and file installation
- Auto-detect shell-specific completion directories
- Provide shell-specific installation instructions
- Completions work for all commands, flags, and value enums

Resolves exs-460

rektide a756ac0e 6da8cbfb

+187 -1
+20
Cargo.lock
··· 212 212 ] 213 213 214 214 [[package]] 215 + name = "clap_complete" 216 + version = "4.5.65" 217 + source = "registry+https://github.com/rust-lang/crates.io-index" 218 + checksum = "430b4dc2b5e3861848de79627b2bedc9f3342c7da5173a14eaa5d0f8dc18ae5d" 219 + dependencies = [ 220 + "clap", 221 + ] 222 + 223 + [[package]] 215 224 name = "clap_derive" 216 225 version = "4.5.55" 217 226 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 316 325 dependencies = [ 317 326 "anyhow", 318 327 "clap", 328 + "clap_complete", 319 329 "colored", 320 330 "comfy-table", 331 + "home", 321 332 "jiff", 322 333 "opentelemetry", 323 334 "opentelemetry-otlp", ··· 457 468 version = "0.5.0" 458 469 source = "registry+https://github.com/rust-lang/crates.io-index" 459 470 checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 471 + 472 + [[package]] 473 + name = "home" 474 + version = "0.5.12" 475 + source = "registry+https://github.com/rust-lang/crates.io-index" 476 + checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" 477 + dependencies = [ 478 + "windows-sys 0.61.2", 479 + ] 460 480 461 481 [[package]] 462 482 name = "http"
+2
Cargo.toml
··· 19 19 jiff = { version = "0.2", features = ["serde"] } 20 20 anyhow = "1.0" 21 21 clap = { version = "4", features = ["derive", "env"] } 22 + clap_complete = "4" 22 23 colored = "2" 23 24 thiserror = "1.0" 24 25 comfy-table = "7" 26 + home = "0.5"
+29 -1
src/cli.rs
··· 79 79 80 80 /// Show log metadata and statistics 81 81 Info(InfoArgs), 82 + 83 + /// Generate shell completions 84 + Completion(CompletionArgs), 82 85 } 83 86 84 87 #[derive(Parser, Debug)] ··· 148 151 149 152 #[derive(Parser, Debug)] 150 153 pub struct InfoArgs { 151 - /// Path to the log file 154 + /// Path to log file 152 155 #[arg(value_name = "FILE")] 153 156 pub file: PathBuf, 154 157 } 158 + 159 + #[derive(ValueEnum, Clone, Copy, Debug, PartialEq)] 160 + pub enum CompletionShell { 161 + /// Bash shell 162 + Bash, 163 + /// Zsh shell 164 + Zsh, 165 + /// Fish shell 166 + Fish, 167 + /// PowerShell 168 + PowerShell, 169 + /// Elvish shell 170 + Elvish, 171 + } 172 + 173 + #[derive(Parser, Debug)] 174 + pub struct CompletionArgs { 175 + /// Shell to generate completions for 176 + #[arg(value_enum)] 177 + pub shell: CompletionShell, 178 + 179 + /// Print completions to stdout instead of installing 180 + #[arg(short, long)] 181 + pub print: bool, 182 + }
+8
src/commands/completions.rs
··· 1 + use anyhow::Result; 2 + 3 + use crate::cli::CompletionArgs; 4 + use crate::completions::generate_completion; 5 + 6 + pub fn execute(args: CompletionArgs) -> Result<()> { 7 + generate_completion(args.shell, args.print) 8 + }
+3
src/commands/mod.rs
··· 1 1 pub mod export; 2 2 pub mod validate; 3 3 pub mod info; 4 + pub mod completions; 4 5 5 6 pub use export::execute as export_execute; 6 7 pub use validate::execute as validate_execute; 7 8 pub use info::execute as info_execute; 9 + pub use completions::execute as completion_execute; 8 10 9 11 use crate::cli::{ExportArgs, ValidateArgs, InfoArgs, Commands}; 10 12 use anyhow::Result; ··· 14 16 Commands::Export(args) => export_execute(args, output).await, 15 17 Commands::Validate(args) => validate_execute(args), 16 18 Commands::Info(args) => info_execute(args), 19 + Commands::Completion(args) => completion_execute(args), 17 20 } 18 21 }
+124
src/completions.rs
··· 1 + use anyhow::{Context, Result}; 2 + use clap::CommandFactory; 3 + use clap_complete::{generate, generate_to, shells::*}; 4 + use std::fs; 5 + use std::path::PathBuf; 6 + 7 + use crate::cli::{Cli, CompletionShell}; 8 + 9 + pub fn generate_completion(shell: CompletionShell, print: bool) -> Result<()> { 10 + let mut cmd = Cli::command(); 11 + let name = cmd.get_name().to_string(); 12 + 13 + if print { 14 + match shell { 15 + CompletionShell::Bash => generate(Bash, &mut cmd, name, &mut std::io::stdout()), 16 + CompletionShell::Zsh => generate(Zsh, &mut cmd, name, &mut std::io::stdout()), 17 + CompletionShell::Fish => generate(Fish, &mut cmd, name, &mut std::io::stdout()), 18 + CompletionShell::PowerShell => { 19 + generate(PowerShell, &mut cmd, name, &mut std::io::stdout()) 20 + } 21 + CompletionShell::Elvish => generate(Elvish, &mut cmd, name, &mut std::io::stdout()), 22 + } 23 + return Ok(()); 24 + } 25 + 26 + let completion_dir = get_completion_dir(shell)?; 27 + fs::create_dir_all(&completion_dir).context("Failed to create completion directory")?; 28 + 29 + match shell { 30 + CompletionShell::Bash => { 31 + generate_to(Bash, &mut cmd, name.clone(), &completion_dir) 32 + .context("Failed to generate bash completions")?; 33 + } 34 + CompletionShell::Zsh => { 35 + generate_to(Zsh, &mut cmd, name.clone(), &completion_dir) 36 + .context("Failed to generate zsh completions")?; 37 + } 38 + CompletionShell::Fish => { 39 + generate_to(Fish, &mut cmd, name.clone(), &completion_dir) 40 + .context("Failed to generate fish completions")?; 41 + } 42 + CompletionShell::PowerShell => { 43 + generate_to(PowerShell, &mut cmd, name.clone(), &completion_dir) 44 + .context("Failed to generate PowerShell completions")?; 45 + } 46 + CompletionShell::Elvish => { 47 + generate_to(Elvish, &mut cmd, name.clone(), &completion_dir) 48 + .context("Failed to generate elvish completions")?; 49 + } 50 + } 51 + 52 + let completion_file = completion_dir.join(get_completion_filename(shell, &name)); 53 + println!("Completions installed to: {}", completion_file.display()); 54 + 55 + print_instructions(shell, &completion_file); 56 + 57 + Ok(()) 58 + } 59 + 60 + fn get_completion_dir(shell: CompletionShell) -> Result<PathBuf> { 61 + let home = home::home_dir().context("Could not determine home directory")?; 62 + 63 + Ok(match shell { 64 + CompletionShell::Bash => { 65 + let local_share = home.join(".local/share/bash-completion/completions"); 66 + if local_share.exists() { 67 + local_share 68 + } else { 69 + home.join(".bash_completion.d") 70 + } 71 + } 72 + CompletionShell::Zsh => { 73 + let zsh_completions = home.join(".zsh/completions"); 74 + if zsh_completions.exists() { 75 + zsh_completions 76 + } else { 77 + home.join(".zsh/completion") 78 + } 79 + } 80 + CompletionShell::Fish => home.join(".config/fish/completions"), 81 + CompletionShell::PowerShell => home.join(".config/powershell"), 82 + CompletionShell::Elvish => home.join(".elvish/lib"), 83 + }) 84 + } 85 + 86 + fn get_completion_filename(shell: CompletionShell, name: &str) -> String { 87 + match shell { 88 + CompletionShell::Bash => format!("{}.bash", name), 89 + CompletionShell::Zsh => format!("_{}", name), 90 + CompletionShell::Fish => format!("{}.fish", name), 91 + CompletionShell::PowerShell => format!("{}.ps1", name), 92 + CompletionShell::Elvish => format!("{}.elv", name), 93 + } 94 + } 95 + 96 + fn print_instructions(shell: CompletionShell, path: &PathBuf) { 97 + match shell { 98 + CompletionShell::Bash => { 99 + println!("\nBash completions should be loaded automatically if ~/.bash_completion.d is sourced."); 100 + println!("Otherwise, add the following to your ~/.bashrc:"); 101 + println!(" source {}", path.display()); 102 + } 103 + CompletionShell::Zsh => { 104 + println!("\nAdd the following to your ~/.zshrc:"); 105 + println!(" fpath=({}, $fpath)", path.parent().unwrap().display()); 106 + println!(" compinit"); 107 + } 108 + CompletionShell::Fish => { 109 + println!("\nFish completions should be loaded automatically."); 110 + println!( 111 + "Restart your shell or run: source ~/.config/fish/completions/{}.fish", 112 + path.file_stem().unwrap().to_str().unwrap() 113 + ); 114 + } 115 + CompletionShell::PowerShell => { 116 + println!("\nAdd the following to your PowerShell profile:"); 117 + println!(" . {}", path.display()); 118 + } 119 + CompletionShell::Elvish => { 120 + println!("\nAdd the following to your ~/.elvish/rc.elv:"); 121 + println!(" use {}", path.display()); 122 + } 123 + } 124 + }
+1
src/lib.rs
··· 4 4 pub mod parser; 5 5 pub mod exporter; 6 6 pub mod formatter; 7 + pub mod completions; 7 8 8 9 pub use cli::{Cli, Commands, OutputFormat};