diff --git a/Makefile b/Makefile index 38eeae4..9f33b5f 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ dashboard: @cd ui && npm install --silent && npm run build @rm -rf internal/worker/static @mkdir -p internal/worker/static + @touch internal/worker/static/placeholder.html @cp -r ui/dist/* internal/worker/static/ # Build worker service diff --git a/internal/update/update.go b/internal/update/update.go index 88d660f..d96be29 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -537,14 +537,16 @@ func isNewerVersion(latest, current string) bool { latest = strings.TrimPrefix(latest, "v") current = strings.TrimPrefix(current, "v") - // Handle dev/dirty versions - if strings.Contains(current, "-dirty") || strings.Contains(current, "-dev") { - return true // Always show update available for dev builds + // For dev/dirty builds, extract the base version for comparison + // e.g., "0.3.5-2-gca711a8-dirty" -> "0.3.5" + currentBase := current + if idx := strings.Index(current, "-"); idx > 0 { + currentBase = current[:idx] } - // Simple semver comparison + // Simple semver comparison using base version latestParts := strings.Split(latest, ".") - currentParts := strings.Split(current, ".") + currentParts := strings.Split(currentBase, ".") for i := 0; i < len(latestParts) && i < len(currentParts); i++ { latestNum, _ := strconv.Atoi(latestParts[i]) diff --git a/internal/worker/handlers.go b/internal/worker/handlers.go index 1cbabb2..95f55a2 100644 --- a/internal/worker/handlers.go +++ b/internal/worker/handlers.go @@ -2,6 +2,7 @@ package worker import ( + "context" "encoding/json" "net/http" "strconv" @@ -153,7 +154,7 @@ func (s *Service) handleSessionInit(w http.ResponseWriter, r *http.Request) { log.Warn().Err(err).Msg("Failed to save user prompt") // Non-fatal: continue with session initialization } else if s.chromaSync != nil { - // Sync to vector DB + // Sync to vector DB asynchronously (non-blocking) now := time.Now() promptWithSession := &models.UserPromptWithSession{ UserPrompt: models.UserPrompt{ @@ -168,9 +169,13 @@ func (s *Service) handleSessionInit(w http.ResponseWriter, r *http.Request) { Project: req.Project, SDKSessionID: req.ClaudeSessionID, } - if err := s.chromaSync.SyncUserPrompt(r.Context(), promptWithSession); err != nil { - log.Warn().Err(err).Int64("id", promptID).Msg("Failed to sync user prompt to ChromaDB") - } + go func() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + if err := s.chromaSync.SyncUserPrompt(ctx, promptWithSession); err != nil { + log.Warn().Err(err).Int64("id", promptID).Msg("Failed to sync user prompt to ChromaDB") + } + }() } log.Info(). diff --git a/pkg/hooks/worker.go b/pkg/hooks/worker.go index b1912d1..168b8ae 100644 --- a/pkg/hooks/worker.go +++ b/pkg/hooks/worker.go @@ -58,9 +58,13 @@ func EnsureWorkerRunning() (int, error) { // Check if already running and healthy if IsWorkerRunning(port) { - // Check version - if mismatch, restart + // Check version - if mismatch, restart (unless both are dev builds) if runningVersion := GetWorkerVersion(port); runningVersion != "" { if runningVersion != Version { + // For dev/dirty builds, don't restart if base versions match + if versionsCompatible(runningVersion, Version) { + return port, nil + } fmt.Fprintf(os.Stderr, "[claude-mnemonic] Worker version mismatch (running: %s, expected: %s), restarting...\n", runningVersion, Version) if err := KillProcessOnPort(port); err != nil { fmt.Fprintf(os.Stderr, "[claude-mnemonic] Warning: failed to kill old worker: %v\n", err) @@ -275,3 +279,34 @@ func GET(port int, path string) (map[string]interface{}, error) { return result, nil } + +// versionsCompatible checks if two versions are compatible for dev builds. +// Returns true if both versions share the same base version (ignoring -dirty, -dev, commit suffixes). +// This prevents unnecessary restarts during development. +func versionsCompatible(v1, v2 string) bool { + // If either is a plain "dev" version, consider it compatible with anything + if v1 == "dev" || v2 == "dev" { + return true + } + + // Extract base versions (e.g., "v0.3.5" from "v0.3.5-2-gca711a8-dirty") + base1 := extractBaseVersion(v1) + base2 := extractBaseVersion(v2) + + // If base versions match, they're compatible + return base1 == base2 +} + +// extractBaseVersion extracts the semver base from a version string. +// e.g., "v0.3.5-2-gca711a8-dirty" -> "0.3.5" +func extractBaseVersion(version string) string { + // Remove leading 'v' if present + v := strings.TrimPrefix(version, "v") + + // Find first hyphen (start of suffix like -2-gcommit-dirty) + if idx := strings.Index(v, "-"); idx > 0 { + v = v[:idx] + } + + return v +}