docs: update readme with new installation methods and cli options

This commit is contained in:
2026-02-01 13:50:09 +00:00
parent dba6d94eab
commit bfc1812ebf
8 changed files with 531 additions and 86 deletions

View File

@@ -8,7 +8,7 @@ use std::path::PathBuf;
use crate::config::{Language, manager::ConfigManager};
use crate::generator::ContentGenerator;
use crate::git::find_repo;
use crate::git::{changelog::*, CommitInfo, GitRepo};
use crate::git::{changelog::*, CommitInfo};
use crate::i18n::{Messages, translate_changelog_category};
/// Generate changelog
@@ -120,7 +120,7 @@ impl ChangelogCommand {
// Generate changelog
let changelog = if self.generate || (config.changelog.auto_generate && !self.yes) {
self.generate_with_ai(&repo, &version, &commits, &messages).await?
self.generate_with_ai(&version, &commits, &messages).await?
} else {
self.generate_with_template(format, &version, &commits, language)?
};
@@ -173,18 +173,18 @@ impl ChangelogCommand {
async fn generate_with_ai(
&self,
repo: &GitRepo,
version: &str,
commits: &[CommitInfo],
messages: &Messages,
) -> Result<String> {
let manager = ConfigManager::new()?;
let config = manager.config();
let language = manager.get_language().unwrap_or(Language::English);
println!("{}", messages.ai_generating_changelog());
let generator = ContentGenerator::new(&config.llm).await?;
generator.generate_changelog_entry(version, commits).await
generator.generate_changelog_entry(version, commits, language).await
}
fn generate_with_template(

View File

@@ -8,7 +8,7 @@ use crate::config::CommitFormat;
use crate::generator::ContentGenerator;
use crate::git::{find_repo, GitRepo};
use crate::git::commit::{CommitBuilder, create_date_commit_message};
use crate::i18n::{Messages, translate_commit_type};
use crate::i18n::Messages;
use crate::utils::validators::get_commit_types;
/// Generate and execute conventional commits
@@ -114,6 +114,12 @@ impl CommitCommand {
println!("{}", messages.auto_stage_changes().yellow());
repo.stage_all()?;
println!("{}", messages.staged_all().green());
// Re-check status after staging to ensure changes are detected
let new_status = repo.status_summary()?;
if new_status.staged == 0 {
bail!("Failed to stage changes. Please try running 'git add -A' manually.");
}
}
// Stage all if requested

View File

@@ -266,6 +266,7 @@ impl TagCommand {
async fn generate_tag_message(&self, repo: &GitRepo, version: &str, messages: &Messages) -> Result<String> {
let manager = ConfigManager::new()?;
let config = manager.config();
let language = manager.get_language().unwrap_or(Language::English);
// Get commits since last tag
let tags = repo.get_tags()?;
@@ -282,7 +283,7 @@ impl TagCommand {
println!("{}", messages.ai_generating_tag(commits.len()));
let generator = ContentGenerator::new(&config.llm).await?;
generator.generate_tag_message(version, &commits).await
generator.generate_tag_message(version, &commits, language).await
}
fn input_message_interactive(&self, version: &str, messages: &Messages) -> Result<String> {

View File

@@ -62,13 +62,14 @@ impl ContentGenerator {
&self,
version: &str,
commits: &[CommitInfo],
language: Language,
) -> Result<String> {
let commit_messages: Vec<String> = commits
.iter()
.map(|c| c.subject().to_string())
.collect();
self.llm_client.generate_tag_message(version, &commit_messages).await
self.llm_client.generate_tag_message(version, &commit_messages, language).await
}
/// Generate changelog entry
@@ -76,6 +77,7 @@ impl ContentGenerator {
&self,
version: &str,
commits: &[CommitInfo],
language: Language,
) -> Result<String> {
let typed_commits: Vec<(String, String)> = commits
.iter()
@@ -85,7 +87,7 @@ impl ContentGenerator {
})
.collect();
self.llm_client.generate_changelog_entry(version, &typed_commits).await
self.llm_client.generate_changelog_entry(version, &typed_commits, language).await
}
/// Generate changelog from repository
@@ -94,6 +96,7 @@ impl ContentGenerator {
repo: &GitRepo,
version: &str,
from_tag: Option<&str>,
language: Language,
) -> Result<String> {
let commits = if let Some(tag) = from_tag {
repo.get_commits_between(tag, "HEAD")?
@@ -101,7 +104,7 @@ impl ContentGenerator {
repo.get_commits(50)?
};
self.generate_changelog_entry(version, &commits).await
self.generate_changelog_entry(version, &commits, language).await
}
/// Interactive commit generation with user feedback

View File

@@ -157,28 +157,19 @@ impl GitRepo {
/// Get staged diff
pub fn get_staged_diff(&self) -> Result<String> {
let head = self.repo.head().ok();
let head_tree = head.as_ref()
.and_then(|h| h.peel_to_tree().ok());
// Use git CLI to get staged diff for better compatibility
let output = std::process::Command::new("git")
.args(&["diff", "--cached"])
.current_dir(&self.path)
.output()
.with_context(|| "Failed to get staged diff with git command")?;
let mut index = self.repo.index()?;
let index_tree = index.write_tree()?;
let index_tree = self.repo.find_tree(index_tree)?;
let diff = if let Some(head) = head_tree {
self.repo.diff_tree_to_index(Some(&head), Some(&index), None)?
} else {
self.repo.diff_tree_to_index(None, Some(&index), None)?
};
let mut diff_text = String::new();
diff.print(git2::DiffFormat::Patch, |_delta, _hunk, line| {
if let Ok(content) = std::str::from_utf8(line.content()) {
diff_text.push_str(content);
}
true
})?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("Failed to get staged diff: {}", stderr);
}
let diff_text = String::from_utf8_lossy(&output.stdout).to_string();
Ok(diff_text)
}
@@ -277,6 +268,9 @@ impl GitRepo {
bail!("Failed to stage changes: {}", stderr);
}
// Force refresh the git2 index to pick up changes from git CLI
let _ = self.repo.index()?.write();
Ok(())
}
@@ -562,33 +556,50 @@ impl GitRepo {
/// Get repository status summary
pub fn status_summary(&self) -> Result<StatusSummary> {
let statuses = self.repo.statuses(Some(StatusOptions::new().include_untracked(true)))?;
// Use git CLI for more reliable status detection
let output = std::process::Command::new("git")
.args(&["status", "--porcelain"])
.current_dir(&self.path)
.output()
.with_context(|| "Failed to get status with git command")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("Failed to get status: {}", stderr);
}
let stdout = String::from_utf8_lossy(&output.stdout);
let mut staged = 0;
let mut unstaged = 0;
let mut untracked = 0;
let mut conflicted = 0;
for entry in statuses.iter() {
let status = entry.status();
for line in stdout.lines() {
if line.len() >= 2 {
let index_status = line.chars().next().unwrap();
let worktree_status = line.chars().nth(1).unwrap();
if status.is_index_new() || status.is_index_modified() ||
status.is_index_deleted() || status.is_index_renamed() ||
status.is_index_typechange() {
staged += 1;
}
// Staged changes (first column not space)
if index_status != ' ' && index_status != '?' {
staged += 1;
}
if status.is_wt_modified() || status.is_wt_deleted() ||
status.is_wt_renamed() || status.is_wt_typechange() {
unstaged += 1;
}
// Unstaged changes (second column not space)
if worktree_status != ' ' && worktree_status != '?' {
unstaged += 1;
}
if status.is_wt_new() {
untracked += 1;
}
// Untracked files (both columns are ?)
if index_status == '?' && worktree_status == '?' {
untracked += 1;
}
if status.is_conflicted() {
conflicted += 1;
// Conflicted files (both columns are U or DD, AA, etc.)
if (index_status == 'U' || worktree_status == 'U') ||
(index_status == 'A' && worktree_status == 'A') ||
(index_status == 'D' && worktree_status == 'D') {
conflicted += 1;
}
}
}

View File

@@ -136,8 +136,9 @@ impl LlmClient {
&self,
version: &str,
commits: &[String],
language: Language,
) -> Result<String> {
let system_prompt = TAG_MESSAGE_SYSTEM_PROMPT;
let system_prompt = get_tag_system_prompt(language);
let commits_text = commits.join("\n");
let prompt = format!("Version: {}\n\nCommits:\n{}", version, commits_text);
@@ -149,8 +150,9 @@ impl LlmClient {
&self,
version: &str,
commits: &[(String, String)], // (type, message)
language: Language,
) -> Result<String> {
let system_prompt = CHANGELOG_SYSTEM_PROMPT;
let system_prompt = get_changelog_system_prompt(language);
let commits_text = commits
.iter()
@@ -385,6 +387,30 @@ fn get_commit_system_prompt(format: crate::config::CommitFormat, language: Langu
}
}
fn get_tag_system_prompt(language: Language) -> &'static str {
match language {
Language::Chinese => TAG_MESSAGE_SYSTEM_PROMPT_ZH,
Language::Japanese => TAG_MESSAGE_SYSTEM_PROMPT_JA,
Language::Korean => TAG_MESSAGE_SYSTEM_PROMPT_KO,
Language::Spanish => TAG_MESSAGE_SYSTEM_PROMPT_ES,
Language::French => TAG_MESSAGE_SYSTEM_PROMPT_FR,
Language::German => TAG_MESSAGE_SYSTEM_PROMPT_DE,
_ => TAG_MESSAGE_SYSTEM_PROMPT,
}
}
fn get_changelog_system_prompt(language: Language) -> &'static str {
match language {
Language::Chinese => CHANGELOG_SYSTEM_PROMPT_ZH,
Language::Japanese => CHANGELOG_SYSTEM_PROMPT_JA,
Language::Korean => CHANGELOG_SYSTEM_PROMPT_KO,
Language::Spanish => CHANGELOG_SYSTEM_PROMPT_ES,
Language::French => CHANGELOG_SYSTEM_PROMPT_FR,
Language::German => CHANGELOG_SYSTEM_PROMPT_DE,
_ => CHANGELOG_SYSTEM_PROMPT,
}
}
// System prompts for LLM
const CONVENTIONAL_COMMIT_SYSTEM_PROMPT: &str = r#"You are a helpful assistant that generates conventional commit messages.