From 280032b1f0c9ed029945cd2ebe9c877b4f91a639 Mon Sep 17 00:00:00 2001 From: uliboooo Date: Sat, 11 Oct 2025 12:17:25 +0900 Subject: [PATCH 1/9] feat: add --stdin option to read diff from stdin --- src/cli.rs | 3 +++ src/helper.rs | 3 ++- src/llm.rs | 6 +++--- src/main.rs | 46 ++++++++++++++++++++++++++++++++-------------- 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 873cfe3..912ee3e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -80,6 +80,9 @@ pub struct RootOptions { #[arg(long = "oneline", help = "show only llm's return for cli pipes")] oneline: bool, + + #[arg(long = "stdin", help = "use stdin as diff content")] + stdin: bool, } pub trait DiffOption { diff --git a/src/helper.rs b/src/helper.rs index e054802..c46ec40 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -1,5 +1,6 @@ use std::{ - fs, io, path::{Path, PathBuf} + fs, io, + path::{Path, PathBuf}, }; // #[derive(Debug)] diff --git a/src/llm.rs b/src/llm.rs index ef2a6f7..836b120 100644 --- a/src/llm.rs +++ b/src/llm.rs @@ -1,4 +1,5 @@ -use std::fmt::Display; +use crate::cli_helper::Spinner; +use crate::config; use derive_getters::Getters; use llm_api_rs::{ self, LlmProvider, @@ -6,8 +7,7 @@ use llm_api_rs::{ providers::{Anthropic, DeepSeek, Gemini, OpenAI}, }; use ollama_rs::{Ollama, generation::completion::request::GenerationRequest}; -use crate::cli_helper::Spinner; -use crate::config; +use std::fmt::Display; #[derive(Debug)] pub enum Error { diff --git a/src/main.rs b/src/main.rs index 3963e13..968b4d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ mod which_sem; use crate::{ cli::{DiffOption, RootOption}, config::Config, - get_input::yes_no, + get_input::{get_input, yes_no}, git::get_git_status, helper::{find_readme, get_now}, }; @@ -23,7 +23,7 @@ use std::{ env, fmt::Display, fs::OpenOptions, - io::Write, + io::{Read, Write}, path::{Path, PathBuf}, }; @@ -251,13 +251,22 @@ async fn main() -> Result<(), Error> { let extra = root_options.extra().as_ref(); let git_status = get_git_status(&work_path)?; + // + // let diff = if root_options.stdin() { + // todo!() + // } else { + // } match &cli.subcommand { cli::Commands::Commit(commit) => { - let diff = { + let diff = if *root_options.stdin() { + let mut input = String::new(); + std::io::stdin().read_to_string(&mut input)?; + input + } else { let diff_opt = commit.resolve_diff_commit(); - git::get_diff(diff_opt, &work_path) - }?; + git::get_diff(diff_opt, &work_path)? + }; let msg = commit_gen::gen_commit_msg(diff, git_status, model_info, lang, extra).await?; if *commit.get_root_options().oneline() { @@ -312,15 +321,20 @@ async fn main() -> Result<(), Error> { Ok(f.write_all(readme_content.as_bytes())?) } } - cli::Commands::SumDiff(_diff_sum) => { - let diff = { - let diff_s = _diff_sum.resolve_diff_commit(); - git::get_diff(diff_s, &work_path) - }?; + cli::Commands::SumDiff(diff_sum) => { + let diff = if *root_options.stdin() { + let mut input = String::new(); + std::io::stdin().read_to_string(&mut input)?; + input + } else { + let diff_s = diff_sum.resolve_diff_commit(); + git::get_diff(diff_s, &work_path)? + }; + let res = diff_sum_gen::sum_diff(diff, git_status, model_info, lang.cloned(), extra.cloned()) .await?; - if *_diff_sum.get_root_options().oneline() { + if *diff_sum.get_root_options().oneline() { println!("{res}"); Ok(()) } else { @@ -329,10 +343,14 @@ async fn main() -> Result<(), Error> { } } cli::Commands::WhichSem(which) => { - let diff = { + let diff = if *root_options.stdin() { + let mut input = String::new(); + std::io::stdin().read_to_string(&mut input)?; + input + } else { let diff_s = which.resolve_diff_commit(); - git::get_diff(diff_s, &work_path) - }?; + git::get_diff(diff_s, &work_path)? + }; let res = which_sem::whichi_sem(diff, git_status, model_info, lang.cloned(), extra.cloned()) .await?; From 9afc331fc29e6638d12f816264196695bacf5e90 Mon Sep 17 00:00:00 2001 From: uliboooo Date: Sat, 11 Oct 2025 12:18:58 +0900 Subject: [PATCH 2/9] feat(diff): Read diff content from stdin --- src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 968b4d7..3707503 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,7 @@ use std::{ env, fmt::Display, fs::OpenOptions, - io::{Read, Write}, + io::{stdout, Read, Write}, path::{Path, PathBuf}, }; @@ -262,6 +262,7 @@ async fn main() -> Result<(), Error> { let diff = if *root_options.stdin() { let mut input = String::new(); std::io::stdin().read_to_string(&mut input)?; + stdout().flush()?; input } else { let diff_opt = commit.resolve_diff_commit(); From 5e95723a88b7df2211dec6898a68d8cdba752f21 Mon Sep 17 00:00:00 2001 From: uliboooo Date: Sat, 11 Oct 2025 12:28:39 +0900 Subject: [PATCH 3/9] feat: allow commit command to read diff from stdin --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 3707503..f38f3fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -261,7 +261,7 @@ async fn main() -> Result<(), Error> { cli::Commands::Commit(commit) => { let diff = if *root_options.stdin() { let mut input = String::new(); - std::io::stdin().read_to_string(&mut input)?; + std::io::stdin().lock().read_to_string(&mut input)?; stdout().flush()?; input } else { From 143a9a041ff43d2957263c55610abed4cce21b89 Mon Sep 17 00:00:00 2001 From: uliboooo Date: Sat, 11 Oct 2025 12:33:06 +0900 Subject: [PATCH 4/9] refactor: use atty crate for stdin TTY check --- Cargo.lock | 43 +++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 3 ++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 630a99a..260bdd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,6 +115,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -445,6 +456,7 @@ dependencies = [ name = "ghost_git_writer" version = "0.16.0" dependencies = [ + "atty", "chrono", "clap", "derive-getters", @@ -512,6 +524,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "home" version = "0.5.11" @@ -1982,6 +2003,28 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.62.0" diff --git a/Cargo.toml b/Cargo.toml index 61541ce..2d5add4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,3 +32,4 @@ easy_storage = "0.4.*" indicatif = "0.18.*" unicode-width = "0.2.1" url = "2.5.7" +atty = "0.2.14" diff --git a/src/main.rs b/src/main.rs index f38f3fb..10c5b25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -259,7 +259,8 @@ async fn main() -> Result<(), Error> { match &cli.subcommand { cli::Commands::Commit(commit) => { - let diff = if *root_options.stdin() { + // 👇 is from pipe. + let diff = if !atty::is(atty::Stream::Stdin) { let mut input = String::new(); std::io::stdin().lock().read_to_string(&mut input)?; stdout().flush()?; From 6bb5cf03a1f40243c7c433b62598b3f1825d62a6 Mon Sep 17 00:00:00 2001 From: uliboooo Date: Mon, 20 Oct 2025 15:05:47 +0900 Subject: [PATCH 5/9] fix(cli): read user confirmation directly from TTY --- src/llm.rs | 2 +- src/main.rs | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/llm.rs b/src/llm.rs index 836b120..0a299ed 100644 --- a/src/llm.rs +++ b/src/llm.rs @@ -70,7 +70,7 @@ impl TryFrom<&str> for Provider { "ollama" => Ok(Self::Ollama), "openai" => Ok(Self::OpenAI), "gemini" => Ok(Self::Gemini), - "authropic" => Ok(Self::Anthropic), + "anthropic" => Ok(Self::Anthropic), "deepseek" => Ok(Self::DeepSeek), _ => Err(Error::NotSuppoeredProvider), } diff --git a/src/main.rs b/src/main.rs index 10c5b25..3cfa64b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,8 +22,8 @@ use easy_storage::Storeable; use std::{ env, fmt::Display, - fs::OpenOptions, - io::{stdout, Read, Write}, + fs::{File, OpenOptions}, + io::{stdout, BufRead, BufReader, Read, Write}, path::{Path, PathBuf}, }; @@ -271,6 +271,13 @@ async fn main() -> Result<(), Error> { }; let msg = commit_gen::gen_commit_msg(diff, git_status, model_info, lang, extra).await?; + let conti = || { + let mut tty = BufReader::new(File::open("/dev/tty").unwrap()); + stdout().flush().unwrap(); + let mut ans = String::new(); + tty.read_line(&mut ans).unwrap(); + matches!(ans.trim(), "y" | "Y" | "Yes" | "yes") + }; if *commit.get_root_options().oneline() { println!("{msg}"); Ok(()) @@ -278,7 +285,10 @@ async fn main() -> Result<(), Error> { let fd_msg = cli_helper::Printer::from(&msg); println!("Generated msg:\n{fd_msg}"); - if *commit.auto_commit() || yes_no("commit?(y/n)") { + if *commit.auto_commit() || + //yes_no("commit?(y/n)") + conti() + { git::git_commit(&work_path, &msg, git_user.0, git_user.1)?; Ok(()) } else { From fc6754fbd1f443a5a90e6efe90377a56420ed3b8 Mon Sep 17 00:00:00 2001 From: uliboooo Date: Mon, 20 Oct 2025 15:08:24 +0900 Subject: [PATCH 6/9] feat: Improve commit confirmation prompt --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 260bdd4..71ee3de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -454,7 +454,7 @@ dependencies = [ [[package]] name = "ghost_git_writer" -version = "0.16.0" +version = "0.16.1" dependencies = [ "atty", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 2d5add4..f54077b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ghost_git_writer" description = "write a git commit message, README or Diff Summary by LLM services." -version = "0.16.0" +version = "0.16.1" repository = "https://github.com/Uliboooo/ghost_git_writer" edition = "2024" license = "MIT OR Apache-2.0" diff --git a/src/main.rs b/src/main.rs index 3cfa64b..9926466 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,7 @@ use std::{ env, fmt::Display, fs::{File, OpenOptions}, - io::{stdout, BufRead, BufReader, Read, Write}, + io::{BufRead, BufReader, Read, Write, stdout}, path::{Path, PathBuf}, }; @@ -276,6 +276,8 @@ async fn main() -> Result<(), Error> { stdout().flush().unwrap(); let mut ans = String::new(); tty.read_line(&mut ans).unwrap(); + print!("continue?(y/n)>"); + std::io::stdout().flush().unwrap(); matches!(ans.trim(), "y" | "Y" | "Yes" | "yes") }; if *commit.get_root_options().oneline() { @@ -286,7 +288,7 @@ async fn main() -> Result<(), Error> { println!("Generated msg:\n{fd_msg}"); if *commit.auto_commit() || - //yes_no("commit?(y/n)") + //yes_no("commit?(y/n)") conti() { git::git_commit(&work_path, &msg, git_user.0, git_user.1)?; From 1c17c956af4da51924b138c5df2e50b563eef6db Mon Sep 17 00:00:00 2001 From: uliboooo Date: Mon, 20 Oct 2025 15:11:02 +0900 Subject: [PATCH 7/9] fix(prompt): display continue prompt before reading user input --- src/main.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9926466..400731c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -274,10 +274,13 @@ async fn main() -> Result<(), Error> { let conti = || { let mut tty = BufReader::new(File::open("/dev/tty").unwrap()); stdout().flush().unwrap(); - let mut ans = String::new(); - tty.read_line(&mut ans).unwrap(); + // let mut ans = String::new(); + // tty.read_line(&mut ans).unwrap(); print!("continue?(y/n)>"); std::io::stdout().flush().unwrap(); + let mut ans = String::new(); + tty.read_line(&mut ans).unwrap(); + matches!(ans.trim(), "y" | "Y" | "Yes" | "yes") }; if *commit.get_root_options().oneline() { From e8e7fa369d65d3aa3553d16721b9906581b27332 Mon Sep 17 00:00:00 2001 From: uliboooo Date: Mon, 20 Oct 2025 15:13:00 +0900 Subject: [PATCH 8/9] chore: bump version to 0.16.2 --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 71ee3de..ab5e39c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -454,7 +454,7 @@ dependencies = [ [[package]] name = "ghost_git_writer" -version = "0.16.1" +version = "0.16.2" dependencies = [ "atty", "chrono", diff --git a/Cargo.toml b/Cargo.toml index f54077b..53ccc8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ghost_git_writer" description = "write a git commit message, README or Diff Summary by LLM services." -version = "0.16.1" +version = "0.16.2" repository = "https://github.com/Uliboooo/ghost_git_writer" edition = "2024" license = "MIT OR Apache-2.0" diff --git a/src/main.rs b/src/main.rs index 400731c..7dc3ff9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ mod which_sem; use crate::{ cli::{DiffOption, RootOption}, config::Config, - get_input::{get_input, yes_no}, + get_input::yes_no, git::get_git_status, helper::{find_readme, get_now}, }; From 855502d51f1e69598a05fd8b56205e3fdb5bb3ec Mon Sep 17 00:00:00 2001 From: uliboooo Date: Mon, 20 Oct 2025 15:26:02 +0900 Subject: [PATCH 9/9] feat: add support for reading diff from stdin --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab5e39c..f9c81f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -454,7 +454,7 @@ dependencies = [ [[package]] name = "ghost_git_writer" -version = "0.16.2" +version = "0.17.0" dependencies = [ "atty", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 53ccc8b..effda1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ghost_git_writer" description = "write a git commit message, README or Diff Summary by LLM services." -version = "0.16.2" +version = "0.17.0" repository = "https://github.com/Uliboooo/ghost_git_writer" edition = "2024" license = "MIT OR Apache-2.0" diff --git a/src/main.rs b/src/main.rs index 7dc3ff9..2596298 100644 --- a/src/main.rs +++ b/src/main.rs @@ -260,7 +260,7 @@ async fn main() -> Result<(), Error> { match &cli.subcommand { cli::Commands::Commit(commit) => { // 👇 is from pipe. - let diff = if !atty::is(atty::Stream::Stdin) { + let diff = if !atty::is(atty::Stream::Stdin) && *root_options.stdin() { let mut input = String::new(); std::io::stdin().lock().read_to_string(&mut input)?; stdout().flush()?;