docs: update readme with new installation methods and cli options
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user