gwtr 0.6.0

A simple Git worktree manager
Documentation
use anyhow::Result;
use clap::{CommandFactory, Parser, Subcommand};
use clap_complete::{Shell, generate};
use std::env;
use std::io;

/// A simple Git worktree manager
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(arg_required_else_help = true)]
struct Cli {
    #[command(subcommand)]
    command: Option<Commands>,
}

#[derive(Subcommand)]
enum Commands {
    /// Add a new worktree
    Add {
        /// Name of the worktree
        name: String,
    },
    /// List all worktrees
    List,
    /// Remove a worktree
    Remove {
        /// Name of the worktree to remove
        name: String,
    },
    /// Show status of all worktrees
    Status,
    /// Pull changes in worktrees
    Pull {
        /// Pull all worktrees
        #[arg(long, short)]
        all: bool,
        /// Specific worktree name to pull (optional)
        name: Option<String>,
    },
    /// Prune merged worktrees
    Prune {
        /// Show what would be pruned without actually removing
        #[arg(long)]
        dry_run: bool,
        /// Skip confirmation prompt
        #[arg(long, short)]
        force: bool,
    },
    /// Add or show a note for a worktree
    Note {
        /// Name of the worktree
        name: String,
        /// Note text to save
        text: Option<String>,
        /// External reference (e.g. JIRA-123, GitHub issue URL)
        #[arg(long)]
        ref_: Option<String>,
    },
    /// Show context of worktrees for AI
    Context {
        /// Name of the worktree (optional, shows all if omitted)
        name: Option<String>,
        /// Save context to a file (.gwtr-context.md)
        #[arg(long)]
        save: bool,
    },
    /// Audit dependencies for supply chain attacks
    Audit {
        /// Only run typosquatting detection (skips cargo audit)
        #[arg(long)]
        typo_only: bool,
        /// Run audit on all worktrees
        #[arg(long, short)]
        all: bool,
    },
    /// Generate shell completion script
    Completions {
        /// Shell to generate completions for (zsh, bash, fish, powershell, elvish)
        shell: Shell,
    },
}

fn main() {
    if let Err(e) = run() {
        eprintln!("Error: {}", e);
        std::process::exit(1);
    }
}

fn run() -> Result<()> {
    let cli = Cli::parse();

    match &cli.command {
        Some(Commands::Add { name }) => {
            // Validate git repository
            let current_dir = env::current_dir()?;
            let repo = gwtr::ensure_git_repository(&current_dir)?;

            // Create worktree
            gwtr::create_worktree(&repo, name)?;
        }
        Some(Commands::List) => {
            // Validate git repository
            let current_dir = env::current_dir()?;
            let repo = gwtr::ensure_git_repository(&current_dir)?;

            // List worktrees
            gwtr::list_worktrees(&repo)?;
        }
        Some(Commands::Remove { name }) => {
            // Validate git repository
            let current_dir = env::current_dir()?;
            let repo = gwtr::ensure_git_repository(&current_dir)?;

            // Remove worktree
            gwtr::remove_worktree(&repo, name)?;
        }
        Some(Commands::Status) => {
            // Validate git repository
            let current_dir = env::current_dir()?;
            let repo = gwtr::ensure_git_repository(&current_dir)?;

            // Show worktrees status
            gwtr::show_worktrees_status(&repo)?;
        }
        Some(Commands::Pull { all, name }) => {
            // Validate git repository
            let current_dir = env::current_dir()?;
            let repo = gwtr::ensure_git_repository(&current_dir)?;

            // Pull worktrees
            if *all {
                gwtr::pull_all_worktrees(&repo)?;
            } else if let Some(worktree_name) = name {
                gwtr::pull_worktree(&repo, worktree_name)?;
            } else {
                // Pull current worktree
                gwtr::pull_current_worktree(&repo)?;
            }
        }
        Some(Commands::Prune { dry_run, force }) => {
            // Validate git repository
            let current_dir = env::current_dir()?;
            let repo = gwtr::ensure_git_repository(&current_dir)?;

            // Prune merged worktrees
            gwtr::prune_merged_worktrees(&repo, *dry_run, *force)?;
        }
        Some(Commands::Note { name, text, ref_ }) => {
            let current_dir = env::current_dir()?;
            let repo = gwtr::ensure_git_repository(&current_dir)?;
            gwtr::manage_note(&repo, name, text.as_deref(), ref_.as_deref())?;
        }
        Some(Commands::Context { name, save }) => {
            let current_dir = env::current_dir()?;
            let repo = gwtr::ensure_git_repository(&current_dir)?;
            gwtr::show_context(&repo, name.as_deref(), *save)?;
        }
        Some(Commands::Audit { typo_only, all }) => {
            let current_dir = env::current_dir()?;
            gwtr::run_audit(&current_dir, *typo_only, *all)?;
        }
        Some(Commands::Completions { shell }) => {
            let mut cmd = Cli::command();
            let bin_name = cmd.get_name().to_string();
            generate(*shell, &mut cmd, bin_name, &mut io::stdout());
        }
        None => {
            // This shouldn't happen with arg_required_else_help
        }
    }

    Ok(())
}