fix: remove auto-update cmd, cleanup

Carlos Alexandro Becker created

Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>

Change summary

go.mod                    |  1 
go.sum                    |  2 +
internal/cmd/update.go    | 51 -----------------------------------------
internal/tui/tui.go       |  4 +-
internal/tui/util/util.go |  1 
internal/update/update.go | 51 ++++++++--------------------------------
6 files changed, 16 insertions(+), 94 deletions(-)

Detailed changes

go.mod šŸ”—

@@ -5,6 +5,7 @@ go 1.25.0
 require (
 	github.com/JohannesKaufmann/html-to-markdown v1.6.0
 	github.com/MakeNowJust/heredoc v1.0.0
+	github.com/Masterminds/semver/v3 v3.4.0
 	github.com/PuerkitoBio/goquery v1.10.3
 	github.com/alecthomas/chroma/v2 v2.20.0
 	github.com/anthropics/anthropic-sdk-go v1.13.0

go.sum šŸ”—

@@ -18,6 +18,8 @@ github.com/JohannesKaufmann/html-to-markdown v1.6.0 h1:04VXMiE50YYfCfLboJCLcgqF5
 github.com/JohannesKaufmann/html-to-markdown v1.6.0/go.mod h1:NUI78lGg/a7vpEJTz/0uOcYMaibytE4BUOQS8k78yPQ=
 github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
 github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
+github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
+github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
 github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
 github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
 github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=

internal/cmd/update.go šŸ”—

@@ -1,51 +0,0 @@
-package cmd
-
-import (
-	"context"
-	"fmt"
-	"time"
-
-	"github.com/charmbracelet/crush/internal/format"
-	"github.com/charmbracelet/crush/internal/update"
-	"github.com/spf13/cobra"
-)
-
-func init() {
-	rootCmd.AddCommand(updateCmd)
-}
-
-var updateCmd = &cobra.Command{
-	Use:   "check-update",
-	Short: "Check for updates",
-	Long:  `Check if a new version of crush is available.`,
-	Example: `
-# Check for updates
-crush check-update
-  `,
-	RunE: func(cmd *cobra.Command, args []string) error {
-		ctx, cancel := context.WithTimeout(cmd.Context(), 10*time.Second)
-		defer cancel()
-
-		sp := format.NewSpinner(ctx, cancel, "Checking for updates")
-		sp.Start()
-		defer sp.Stop()
-
-		info, err := update.CheckForUpdate(ctx)
-		if err != nil {
-			return fmt.Errorf("failed to check for updates: %w", err)
-		}
-		sp.Stop()
-
-		if !info.Available {
-			fmt.Printf("You are running the latest version: %s\n", info.CurrentVersion)
-			return nil
-		}
-
-		fmt.Printf("\nšŸŽ‰ A new version of crush is available!\n\n")
-		fmt.Printf("Current version: %s\n", info.CurrentVersion)
-		fmt.Printf("Latest version:  %s\n\n", info.LatestVersion)
-		fmt.Printf("Visit %s to download the latest version.\n", info.ReleaseURL)
-
-		return nil
-	},
-}

internal/tui/tui.go šŸ”—

@@ -287,11 +287,11 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	// Update Available
 	case pubsub.UpdateAvailableMsg:
 		// Show update notification in status bar
-		statusMsg := fmt.Sprintf("šŸŽ‰ Update available! v%s → v%s. Run 'crush check-update' for details.", msg.CurrentVersion, msg.LatestVersion)
+		statusMsg := fmt.Sprintf("šŸŽ‰ New Crush version available: v%s → v%s.", msg.CurrentVersion, msg.LatestVersion)
 		s, statusCmd := a.status.Update(util.InfoMsg{
 			Type: util.InfoTypeInfo,
 			Msg:  statusMsg,
-			TTL:  10 * time.Second,
+			TTL:  30 * time.Second,
 		})
 		a.status = s.(status.StatusCmp)
 		return a, statusCmd

internal/update/update.go šŸ”—

@@ -10,6 +10,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/Masterminds/semver/v3"
 	"github.com/charmbracelet/crush/internal/version"
 )
 
@@ -38,8 +39,9 @@ func CheckForUpdate(ctx context.Context) (*UpdateInfo, error) {
 		CurrentVersion: version.Version,
 	}
 
-	// Skip update check for development versions.
-	if strings.Contains(version.Version, "unknown") {
+	cv, err := semver.NewVersion(version.Version)
+	if err != nil {
+		// its devel, unknown, etc
 		return info, nil
 	}
 
@@ -48,13 +50,14 @@ func CheckForUpdate(ctx context.Context) (*UpdateInfo, error) {
 		return nil, fmt.Errorf("failed to fetch latest release: %w", err)
 	}
 
+	lv, err := semver.NewVersion(release.TagName)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse latest version: %w", err)
+	}
+
 	info.LatestVersion = strings.TrimPrefix(release.TagName, "v")
 	info.ReleaseURL = release.HTMLURL
-
-	// Compare versions.
-	if compareVersions(info.CurrentVersion, info.LatestVersion) < 0 {
-		info.Available = true
-	}
+	info.Available = lv.GreaterThan(cv)
 
 	return info, nil
 }
@@ -91,40 +94,6 @@ func fetchLatestRelease(ctx context.Context) (*Release, error) {
 	return &release, nil
 }
 
-// compareVersions compares two semantic versions.
-// Returns -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2.
-func compareVersions(v1, v2 string) int {
-	// Remove 'v' prefix if present.
-	v1 = strings.TrimPrefix(v1, "v")
-	v2 = strings.TrimPrefix(v2, "v")
-
-	// Split versions into parts.
-	parts1 := strings.Split(v1, ".")
-	parts2 := strings.Split(v2, ".")
-
-	// Compare each part.
-	for i := 0; i < len(parts1) && i < len(parts2); i++ {
-		var n1, n2 int
-		fmt.Sscanf(parts1[i], "%d", &n1)
-		fmt.Sscanf(parts2[i], "%d", &n2)
-
-		if n1 < n2 {
-			return -1
-		} else if n1 > n2 {
-			return 1
-		}
-	}
-
-	// If all parts are equal, compare lengths.
-	if len(parts1) < len(parts2) {
-		return -1
-	} else if len(parts1) > len(parts2) {
-		return 1
-	}
-
-	return 0
-}
-
 // CheckForUpdateAsync performs an update check in the background and returns immediately.
 // If an update is available, it returns the update info through the channel.
 func CheckForUpdateAsync(ctx context.Context, dataDir string) <-chan *UpdateInfo {