diff --git a/apps/cli/src/commands/core/git.rs b/apps/cli/src/commands/core/git.rs index 617f5f4..9873670 100644 --- a/apps/cli/src/commands/core/git.rs +++ b/apps/cli/src/commands/core/git.rs @@ -47,6 +47,12 @@ pub fn tag_exists(path: &Path, tag: &str) -> bool { .unwrap_or(false) } +/// Fetch tags from remote to sync local tags with remote +pub fn fetch_tags(path: &Path, remote: &str) -> Result<()> { + git_cmd(&["fetch", remote, "--tags", "--force"], path)?; + Ok(()) +} + /// List all tags sorted by version (newest first) pub fn list_tags(path: &Path) -> Result> { let output = git_cmd(&["tag", "-l", "--sort=-v:refname"], path)?; diff --git a/apps/cli/src/commands/publish.rs b/apps/cli/src/commands/publish.rs index c2db84f..93266a1 100644 --- a/apps/cli/src/commands/publish.rs +++ b/apps/cli/src/commands/publish.rs @@ -64,6 +64,11 @@ fn filter_semver_tags(tags: &[String]) -> Vec { .collect() } +/// Get the latest semver version from a list of tags +fn get_latest_version(tags: &[String]) -> Option { + filter_semver_tags(tags).into_iter().next() +} + /// Prompt user to select version bump type, existing tag, or enter custom version fn prompt_tag_selection(existing_tags: &[String], current_version: &str) -> Result { // Filter to only semver tags @@ -255,8 +260,19 @@ pub async fn run(args: PublishArgs) -> Result<()> { } // Step 4: Determine which tag to use + // First, sync local tags with remote to ensure we have the latest state from GitHub + print!(" Syncing tags from origin... "); + match git::fetch_tags(&skill_path, remote) { + Ok(()) => println!("✓"), + Err(e) => println!("⚠ ({})", e), + } + + // Now list local tags (which are synced with remote) let existing_tags = git::list_tags(&skill_path)?; + // Get the latest version from tags for bump calculations + let latest_version = get_latest_version(&existing_tags); + let (tag, needs_create) = if let Some(explicit_tag) = args.tag.clone() { // User explicitly provided a tag via --tag flag - validate it's semver let tag_to_check = if explicit_tag.starts_with('v') { @@ -271,8 +287,9 @@ pub async fn run(args: PublishArgs) -> Result<()> { } (tag_to_check, false) } else if args.yes { - // Non-interactive mode: create patch bump - let (major, minor, patch) = parse_version(current_version)?; + // Non-interactive mode: create patch bump based on latest tag version + let base_version = latest_version.as_deref().unwrap_or(current_version); + let (major, minor, patch) = parse_version(base_version)?; let new_tag = format!("v{}.{}.{}", major, minor, patch + 1); if git::tag_exists(&skill_path, &new_tag) { bail!("Tag {} already exists.", new_tag); @@ -281,7 +298,8 @@ pub async fn run(args: PublishArgs) -> Result<()> { } else { // Interactive mode: let user choose bump type or existing tag println!(); - match prompt_tag_selection(&existing_tags, current_version)? { + let base_version = latest_version.as_deref().unwrap_or(current_version); + match prompt_tag_selection(&existing_tags, base_version)? { TagSelection::New(tag) => { if git::tag_exists(&skill_path, &tag) { bail!("Tag {} already exists.", tag);