diff --git a/src/cli.rs b/src/cli.rs index 57ccfc0..8eeecb7 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -4,7 +4,28 @@ use clap::{Parser, Subcommand}; #[derive(Parser)] #[command(name = "gw")] -#[command(about = "Git workflow CLI - type-safe worktree-aware git operations")] +#[command(about = "Worktree-aware git workflow: branch -> PR -> cleanup, safely.")] +#[command(long_about = "\ +Worktree-aware git workflow: branch -> PR -> cleanup, safely. + +For developers using a PR-per-branch workflow. gw drives each change from a +fresh branch through review to a cleaned-up merge, and keeps parallel worktrees +in sync so day-to-day work never touches `main` directly. + +\"home branch\": the branch a worktree returns to (the main worktree's home is +`main`). `gw home` switches to it and syncs with origin/main. + +TYPICAL FLOW: + gw new feature/login # branch off a fresh origin/main + # ...edit, then: git commit -> git push -u origin -> gh pr create + gw await --open # wait for CI, open the PR, watch to merge, then clean up + +COMMANDS BY SITUATION: + Everyday: new, status, open, sync, cleanup, home + Recovery: pause, abandon, undo + Worktrees: worktree pool (give parallel agents isolated worktrees) + +Lost? Run `gw status` -- it prints the single next command for where you are.")] #[command(version)] pub struct Cli { #[command(subcommand)] diff --git a/src/main.rs b/src/main.rs index ad9204a..383a568 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use std::process::ExitCode; use clap::Parser; +use clap::error::ErrorKind; use git_workflow::cli::{Cli, Commands, PoolCommands, WorktreeCommands}; use git_workflow::commands; @@ -10,7 +11,10 @@ use git_workflow::error::GwError; use git_workflow::output; fn main() -> ExitCode { - let cli = Cli::parse(); + let cli = match Cli::try_parse() { + Ok(cli) => cli, + Err(e) => return handle_parse_error(e), + }; let result = match cli.command { Commands::Home => commands::home::run(cli.verbose), @@ -65,3 +69,29 @@ fn main() -> ExitCode { } } } + +/// Render clap parse failures, then guide the user to a runnable next command. +/// +/// `--help`/`--version` aren't failures: print them as-is and exit 0. Real usage +/// errors (bad subcommand, missing argument) get clap's message plus a `Try:` +/// footer pointing at `gw status` -- the command that names the next step -- so +/// the user never has to reconstruct the syntax themselves. +fn handle_parse_error(e: clap::Error) -> ExitCode { + let kind = e.kind(); + // clap writes help to stdout and errors to stderr; let it pick the stream. + let _ = e.print(); + + match kind { + ErrorKind::DisplayHelp + | ErrorKind::DisplayVersion + | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand => ExitCode::SUCCESS, + _ => { + output::error_footer(&[ + "gw status # show where you are and the next command to run", + "gw --help # full command reference", + ]); + // Clap uses exit code 2 for usage errors; mirror that. + ExitCode::from(2) + } + } +} diff --git a/src/output/style.rs b/src/output/style.rs index 1bb16b4..0536690 100644 --- a/src/output/style.rs +++ b/src/output/style.rs @@ -58,3 +58,12 @@ pub fn hints(lines: &[&str]) { println!(" {line}"); } } + +/// Print a `Try:` footer (to stderr) listing runnable next commands after an error. +pub fn error_footer(lines: &[&str]) { + eprintln!(); + eprintln!("{BOLD}Try:{RESET}"); + for line in lines { + eprintln!(" {line}"); + } +}