From 3a2a045c3edb8e53b36cb71951f91213e4c3fb5c Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Thu, 29 Jan 2026 21:12:43 -0300 Subject: [PATCH] fix: improve logs, standardize capitalized (#2047) * fix: improve logs, standarize capitalized Signed-off-by: Carlos Alexandro Becker * Update Taskfile.yaml Co-authored-by: Andrey Nering * chore: lint Signed-off-by: Carlos Alexandro Becker --------- Signed-off-by: Carlos Alexandro Becker Co-authored-by: Andrey Nering --- AGENTS.md | 2 ++ Taskfile.yaml | 6 ++++++ internal/agent/agent.go | 28 ++++++++++++++-------------- internal/agent/coordinator.go | 12 ++++++------ internal/agent/hyper/provider.go | 2 +- internal/agent/tools/mcp/init.go | 12 ++++++------ internal/agent/tools/mcp/prompts.go | 2 +- internal/agent/tools/mcp/tools.go | 2 +- internal/app/app.go | 26 +++++++++++++++----------- internal/app/lsp.go | 2 +- internal/cmd/run.go | 12 +++++++++++- internal/config/config.go | 4 ++-- internal/fsext/ls.go | 12 ++++++------ internal/home/home.go | 2 +- internal/lsp/client.go | 8 ++++---- internal/session/session.go | 2 +- internal/ui/image/image.go | 2 +- internal/ui/model/history.go | 2 +- internal/ui/model/ui.go | 6 +++--- scripts/check_log_capitalization.sh | 5 +++++ 20 files changed, 88 insertions(+), 61 deletions(-) create mode 100644 scripts/check_log_capitalization.sh diff --git a/AGENTS.md b/AGENTS.md index 7fab72afb836136020500b7f27e905f3dcfc72da..654f1cd0a7fe1cbb50a3026f86f31b68e04f8043 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,8 @@ need of a temporary directory. This directory does not need to be removed. - **JSON tags**: Use snake_case for JSON field names - **File permissions**: Use octal notation (0o755, 0o644) for file permissions +- **Log messages**: Log messages must start with a capital letter (e.g., "Failed to save session" not "failed to save session") + - This is enforced by `task lint:log` which runs as part of `task lint` - **Comments**: End comments in periods unless comments are at the end of the line. ## Testing with Mock Providers diff --git a/Taskfile.yaml b/Taskfile.yaml index 9ffe8923d6bbd92caf441d872726de48352b2faa..bf22d6593bfd099972a7a11a806cfee939511df2 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -23,10 +23,16 @@ tasks: lint: desc: Run base linters cmds: + - task: lint:log - golangci-lint run --path-mode=abs --config=".golangci.yml" --timeout=5m env: GOEXPERIMENT: null + lint:log: + desc: Check that log messages start with capital letters + cmds: + - ./scripts/check_log_capitalization.sh + lint:fix: desc: Run base linters and fix issues cmds: diff --git a/internal/agent/agent.go b/internal/agent/agent.go index 74a1a9f0c94483268b4b3558c7d4ca7a9899c7ef..d46f42334f9bb5de656cd2f6e442cb4c414a6968 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -802,22 +802,22 @@ func (a *sessionAgent) generateTitle(ctx context.Context, sessionID string, user resp, err := agent.Stream(ctx, streamCall) if err == nil { // We successfully generated a title with the small model. - slog.Info("generated title with small model") + slog.Debug("Generated title with small model") } else { // It didn't work. Let's try with the big model. - slog.Error("error generating title with small model; trying big model", "err", err) + slog.Error("Error generating title with small model; trying big model", "err", err) model = largeModel agent = newAgent(model.Model, titlePrompt, maxOutputTokens) resp, err = agent.Stream(ctx, streamCall) if err == nil { - slog.Info("generated title with large model") + slog.Debug("Generated title with large model") } else { // Welp, the large model didn't work either. Use the default // session name and return. - slog.Error("error generating title with large model", "err", err) + slog.Error("Error generating title with large model", "err", err) saveErr := a.sessions.UpdateTitleAndUsage(ctx, sessionID, defaultSessionName, 0, 0, 0) if saveErr != nil { - slog.Error("failed to save session title and usage", "error", saveErr) + slog.Error("Failed to save session title and usage", "error", saveErr) } return } @@ -826,10 +826,10 @@ func (a *sessionAgent) generateTitle(ctx context.Context, sessionID string, user if resp == nil { // Actually, we didn't get a response so we can't. Use the default // session name and return. - slog.Error("response is nil; can't generate title") + slog.Error("Response is nil; can't generate title") saveErr := a.sessions.UpdateTitleAndUsage(ctx, sessionID, defaultSessionName, 0, 0, 0) if saveErr != nil { - slog.Error("failed to save session title and usage", "error", saveErr) + slog.Error("Failed to save session title and usage", "error", saveErr) } return } @@ -843,7 +843,7 @@ func (a *sessionAgent) generateTitle(ctx context.Context, sessionID string, user title = strings.TrimSpace(title) if title == "" { - slog.Warn("empty title; using fallback") + slog.Debug("Empty title; using fallback") title = defaultSessionName } @@ -878,7 +878,7 @@ func (a *sessionAgent) generateTitle(ctx context.Context, sessionID string, user // concurrent session updates. saveErr := a.sessions.UpdateTitleAndUsage(ctx, sessionID, title, promptTokens, completionTokens, cost) if saveErr != nil { - slog.Error("failed to save session title and usage", "error", saveErr) + slog.Error("Failed to save session title and usage", "error", saveErr) return } } @@ -921,25 +921,25 @@ func (a *sessionAgent) Cancel(sessionID string) { // fully completes (including error handling that may access the DB). // The defer in processRequest will clean up the entry. if cancel, ok := a.activeRequests.Get(sessionID); ok && cancel != nil { - slog.Info("Request cancellation initiated", "session_id", sessionID) + slog.Debug("Request cancellation initiated", "session_id", sessionID) cancel() } // Also check for summarize requests. if cancel, ok := a.activeRequests.Get(sessionID + "-summarize"); ok && cancel != nil { - slog.Info("Summarize cancellation initiated", "session_id", sessionID) + slog.Debug("Summarize cancellation initiated", "session_id", sessionID) cancel() } if a.QueuedPrompts(sessionID) > 0 { - slog.Info("Clearing queued prompts", "session_id", sessionID) + slog.Debug("Clearing queued prompts", "session_id", sessionID) a.messageQueue.Del(sessionID) } } func (a *sessionAgent) ClearQueue(sessionID string) { if a.QueuedPrompts(sessionID) > 0 { - slog.Info("Clearing queued prompts", "session_id", sessionID) + slog.Debug("Clearing queued prompts", "session_id", sessionID) a.messageQueue.Del(sessionID) } } @@ -1099,7 +1099,7 @@ func (a *sessionAgent) workaroundProviderMediaLimitations(messages []fantasy.Mes if media, ok := fantasy.AsToolResultOutputType[fantasy.ToolResultOutputContentMedia](toolResult.Output); ok { decoded, err := base64.StdEncoding.DecodeString(media.Data) if err != nil { - slog.Warn("failed to decode media data", "error", err) + slog.Warn("Failed to decode media data", "error", err) textParts = append(textParts, part) continue } diff --git a/internal/agent/coordinator.go b/internal/agent/coordinator.go index fd65072fd4eb297b8eddcb38aafe50d595601f82..40b7e029e465cc40f285ced7b5b77dd61109a2a0 100644 --- a/internal/agent/coordinator.go +++ b/internal/agent/coordinator.go @@ -151,7 +151,7 @@ func (c *coordinator) Run(ctx context.Context, sessionID string, prompt string, mergedOptions, temp, topP, topK, freqPenalty, presPenalty := mergeCallOptions(model, providerCfg) if providerCfg.OAuthToken != nil && providerCfg.OAuthToken.IsExpired() { - slog.Info("Token needs to be refreshed", "provider", providerCfg.ID) + slog.Debug("Token needs to be refreshed", "provider", providerCfg.ID) if err := c.refreshOAuth2Token(ctx, providerCfg); err != nil { return nil, err } @@ -176,18 +176,18 @@ func (c *coordinator) Run(ctx context.Context, sessionID string, prompt string, if c.isUnauthorized(originalErr) { switch { case providerCfg.OAuthToken != nil: - slog.Info("Received 401. Refreshing token and retrying", "provider", providerCfg.ID) + slog.Debug("Received 401. Refreshing token and retrying", "provider", providerCfg.ID) if err := c.refreshOAuth2Token(ctx, providerCfg); err != nil { return nil, originalErr } - slog.Info("Retrying request with refreshed OAuth token", "provider", providerCfg.ID) + slog.Debug("Retrying request with refreshed OAuth token", "provider", providerCfg.ID) return run() case strings.Contains(providerCfg.APIKeyTemplate, "$"): - slog.Info("Received 401. Refreshing API Key template and retrying", "provider", providerCfg.ID) + slog.Debug("Received 401. Refreshing API Key template and retrying", "provider", providerCfg.ID) if err := c.refreshApiKeyTemplate(ctx, providerCfg); err != nil { return nil, originalErr } - slog.Info("Retrying request with refreshed API key", "provider", providerCfg.ID) + slog.Debug("Retrying request with refreshed API key", "provider", providerCfg.ID) return run() } } @@ -428,7 +428,7 @@ func (c *coordinator) buildTools(ctx context.Context, agent config.Agent) ([]fan } if len(agent.AllowedMCP) == 0 { // No MCPs allowed - slog.Debug("no MCPs allowed", "tool", tool.Name(), "agent", agent.Name) + slog.Debug("No MCPs allowed", "tool", tool.Name(), "agent", agent.Name) break } diff --git a/internal/agent/hyper/provider.go b/internal/agent/hyper/provider.go index 03278ae99f87608c65263b0ffef7fb473cd58e31..6194593a719b388d1676f51568e72f45628fdae4 100644 --- a/internal/agent/hyper/provider.go +++ b/internal/agent/hyper/provider.go @@ -49,7 +49,7 @@ var Enabled = sync.OnceValue(func() bool { var Embedded = sync.OnceValue(func() catwalk.Provider { var provider catwalk.Provider if err := json.Unmarshal(embedded, &provider); err != nil { - slog.Error("could not use embedded provider data", "err", err) + slog.Error("Could not use embedded provider data", "err", err) } return provider }) diff --git a/internal/agent/tools/mcp/init.go b/internal/agent/tools/mcp/init.go index e1e7d609efc86d0dcb510fa5963552f7d487a134..05ac2eaeba29c2ce4411c8acc355d645037a6f55 100644 --- a/internal/agent/tools/mcp/init.go +++ b/internal/agent/tools/mcp/init.go @@ -140,7 +140,7 @@ func Initialize(ctx context.Context, permissions permission.Service, cfg *config for name, m := range cfg.MCP { if m.Disabled { updateState(name, StateDisabled, nil, nil, Counts{}) - slog.Debug("skipping disabled mcp", "name", name) + slog.Debug("Skipping disabled MCP", "name", name) continue } @@ -162,7 +162,7 @@ func Initialize(ctx context.Context, permissions permission.Service, cfg *config err = fmt.Errorf("panic: %v", v) } updateState(name, StateError, err, nil, Counts{}) - slog.Error("panic in mcp client initialization", "error", err, "name", name) + slog.Error("Panic in MCP client initialization", "error", err, "name", name) } }() @@ -174,7 +174,7 @@ func Initialize(ctx context.Context, permissions permission.Service, cfg *config tools, err := getTools(ctx, session) if err != nil { - slog.Error("error listing tools", "error", err) + slog.Error("Error listing tools", "error", err) updateState(name, StateError, err, nil, Counts{}) session.Close() return @@ -182,7 +182,7 @@ func Initialize(ctx context.Context, permissions permission.Service, cfg *config prompts, err := getPrompts(ctx, session) if err != nil { - slog.Error("error listing prompts", "error", err) + slog.Error("Error listing prompts", "error", err) updateState(name, StateError, err, nil, Counts{}) session.Close() return @@ -277,7 +277,7 @@ func createSession(ctx context.Context, name string, m config.MCPConfig, resolve transport, err := createTransport(mcpCtx, m, resolver) if err != nil { updateState(name, StateError, err, nil, Counts{}) - slog.Error("error creating mcp client", "error", err, "name", name) + slog.Error("Error creating MCP client", "error", err, "name", name) cancel() cancelTimer.Stop() return nil, err @@ -319,7 +319,7 @@ func createSession(ctx context.Context, name string, m config.MCPConfig, resolve } cancelTimer.Stop() - slog.Info("MCP client initialized", "name", name) + slog.Debug("MCP client initialized", "name", name) return session, nil } diff --git a/internal/agent/tools/mcp/prompts.go b/internal/agent/tools/mcp/prompts.go index 0bd6e665dd80dad90c844d7d31c61c506ea83803..ea208a57716d2a273fde1b6faa3988ca2e57b012 100644 --- a/internal/agent/tools/mcp/prompts.go +++ b/internal/agent/tools/mcp/prompts.go @@ -49,7 +49,7 @@ func GetPromptMessages(ctx context.Context, clientName, promptName string, args func RefreshPrompts(ctx context.Context, name string) { session, ok := sessions.Get(name) if !ok { - slog.Warn("refresh prompts: no session", "name", name) + slog.Warn("Refresh prompts: no session", "name", name) return } diff --git a/internal/agent/tools/mcp/tools.go b/internal/agent/tools/mcp/tools.go index 779baa55d93bc54523bac81c5094bacee7fc68fb..65ef5a9d8b3e7304a49bd708ecdd53a3cc400b17 100644 --- a/internal/agent/tools/mcp/tools.go +++ b/internal/agent/tools/mcp/tools.go @@ -111,7 +111,7 @@ func RunTool(ctx context.Context, name, toolName string, input string) (ToolResu func RefreshTools(ctx context.Context, name string) { session, ok := sessions.Get(name) if !ok { - slog.Warn("refresh tools: no session", "name", name) + slog.Warn("Refresh tools: no session", "name", name) return } diff --git a/internal/app/app.go b/internal/app/app.go index f914600457061056648cb23baa7901ca8d946f24..88af5345eb55cc7f3e6c3c5923806967cc0a1632 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -135,7 +135,7 @@ func (app *App) Config() *config.Config { // RunNonInteractive runs the application in non-interactive mode with the // given prompt, printing to stdout. -func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt, largeModel, smallModel string, quiet bool) error { +func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt, largeModel, smallModel string, hideSpinner bool) error { slog.Info("Running in non-interactive mode") ctx, cancel := context.WithCancel(ctx) @@ -160,10 +160,9 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt, } stderrTTY = term.IsTerminal(os.Stderr.Fd()) stdinTTY = term.IsTerminal(os.Stdin.Fd()) - progress = app.config.Options.Progress == nil || *app.config.Options.Progress - if !quiet && stderrTTY { + if !hideSpinner && stderrTTY { t := styles.CurrentTheme() // Detect background color to set the appropriate color for the @@ -188,7 +187,7 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt, // Helper function to stop spinner once. stopSpinner := func() { - if !quiet && spinner != nil { + if !hideSpinner && spinner != nil { spinner.Stop() spinner = nil } @@ -245,6 +244,7 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt, messageEvents := app.Messages.Subscribe(ctx) messageReadBytes := make(map[string]int) + var printed bool defer func() { if progress && stderrTTY { @@ -268,7 +268,7 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt, stopSpinner() if result.err != nil { if errors.Is(result.err, context.Canceled) || errors.Is(result.err, agent.ErrRequestCancelled) { - slog.Info("Non-interactive: agent processing cancelled", "session_id", sess.ID) + slog.Debug("Non-interactive: agent processing cancelled", "session_id", sess.ID) return nil } return fmt.Errorf("agent processing failed: %w", result.err) @@ -294,7 +294,11 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt, if readBytes == 0 { part = strings.TrimLeft(part, " \t") } - fmt.Fprint(output, part) + // Ignore initial whitespace-only messages. + if printed || strings.TrimSpace(part) != "" { + printed = true + fmt.Fprint(output, part) + } messageReadBytes[msg.ID] = len(content) } @@ -433,20 +437,20 @@ func setupSubscriber[T any]( select { case event, ok := <-subCh: if !ok { - slog.Debug("subscription channel closed", "name", name) + slog.Debug("Subscription channel closed", "name", name) return } var msg tea.Msg = event select { case outputCh <- msg: case <-time.After(2 * time.Second): - slog.Warn("message dropped due to slow consumer", "name", name) + slog.Debug("Message dropped due to slow consumer", "name", name) case <-ctx.Done(): - slog.Debug("subscription cancelled", "name", name) + slog.Debug("Subscription cancelled", "name", name) return } case <-ctx.Done(): - slog.Debug("subscription cancelled", "name", name) + slog.Debug("Subscription cancelled", "name", name) return } } @@ -511,7 +515,7 @@ func (app *App) Subscribe(program *tea.Program) { // Shutdown performs a graceful shutdown of the application. func (app *App) Shutdown() { start := time.Now() - defer func() { slog.Info("Shutdown took " + time.Since(start).String()) }() + defer func() { slog.Debug("Shutdown took " + time.Since(start).String()) }() // First, cancel all agents and wait for them to finish. This must complete // before closing the DB so agents can finish writing their state. diff --git a/internal/app/lsp.go b/internal/app/lsp.go index 39e03d3cb4f2f5a9dc7720f8ce1f7286d4efd6b2..14f1c99587bf4bfe052f9ac2078cdf03d859cfa1 100644 --- a/internal/app/lsp.go +++ b/internal/app/lsp.go @@ -140,7 +140,7 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, config updateLSPState(name, lsp.StateReady, nil, lspClient, 0) } - slog.Info("LSP client initialized", "name", name) + slog.Debug("LSP client initialized", "name", name) // Add to map with mutex protection before starting goroutine app.LSPClients.Set(name, lspClient) diff --git a/internal/cmd/run.go b/internal/cmd/run.go index e4d72b41be13684e28ca6c2b85b79bfdcea52fc7..50005a548bad0308bdca3a2afbe17503c1f86c56 100644 --- a/internal/cmd/run.go +++ b/internal/cmd/run.go @@ -8,6 +8,7 @@ import ( "os/signal" "strings" + "charm.land/log/v2" "github.com/charmbracelet/crush/internal/event" "github.com/spf13/cobra" ) @@ -29,9 +30,13 @@ crush run "What is this code doing?" <<< prrr.go # Run in quiet mode (hide the spinner) crush run --quiet "Generate a README for this project" + +# Run in verbose mode +crush run --verbose "Generate a README for this project" `, RunE: func(cmd *cobra.Command, args []string) error { quiet, _ := cmd.Flags().GetBool("quiet") + verbose, _ := cmd.Flags().GetBool("verbose") largeModel, _ := cmd.Flags().GetString("model") smallModel, _ := cmd.Flags().GetString("small-model") @@ -49,6 +54,10 @@ crush run --quiet "Generate a README for this project" return fmt.Errorf("no providers configured - please run 'crush' to set up a provider interactively") } + if verbose { + slog.SetDefault(slog.New(log.New(os.Stderr))) + } + prompt := strings.Join(args, " ") prompt, err = MaybePrependStdin(prompt) @@ -64,7 +73,7 @@ crush run --quiet "Generate a README for this project" event.SetNonInteractive(true) event.AppInitialized() - return app.RunNonInteractive(ctx, os.Stdout, prompt, largeModel, smallModel, quiet) + return app.RunNonInteractive(ctx, os.Stdout, prompt, largeModel, smallModel, quiet || verbose) }, PostRun: func(cmd *cobra.Command, args []string) { event.AppExited() @@ -73,6 +82,7 @@ crush run --quiet "Generate a README for this project" func init() { runCmd.Flags().BoolP("quiet", "q", false, "Hide spinner") + runCmd.Flags().BoolP("verbose", "v", false, "Show logs") runCmd.Flags().StringP("model", "m", "", "Model to use. Accepts 'model' or 'provider/model' to disambiguate models with the same name across providers") runCmd.Flags().String("small-model", "", "Small model to use. If not provided, uses the default small model for the provider") } diff --git a/internal/config/config.go b/internal/config/config.go index b9bc5259d36390d53aa21befd524ea7043261905..ca585d80e8a9dcdc0f9f7b2d38999ce2cac74243 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -317,7 +317,7 @@ func (m MCPConfig) ResolvedHeaders() map[string]string { var err error m.Headers[e], err = resolver.ResolveValue(v) if err != nil { - slog.Error("error resolving header variable", "error", err, "variable", e, "value", v) + slog.Error("Error resolving header variable", "error", err, "variable", e, "value", v) continue } } @@ -840,7 +840,7 @@ func resolveEnvs(envs map[string]string) []string { var err error envs[e], err = resolver.ResolveValue(v) if err != nil { - slog.Error("error resolving environment variable", "error", err, "variable", e, "value", v) + slog.Error("Error resolving environment variable", "error", err, "variable", e, "value", v) continue } } diff --git a/internal/fsext/ls.go b/internal/fsext/ls.go index c22b960ad02a42bf6adac7768b7d99e55a9390ee..b541a4a0fedd78c866fa274fc183fabe4c833edd 100644 --- a/internal/fsext/ls.go +++ b/internal/fsext/ls.go @@ -144,20 +144,20 @@ func (dl *directoryLister) shouldIgnore(path string, ignorePatterns []string) bo } if commonIgnorePatterns().MatchesPath(relPath) { - slog.Debug("ignoring common pattern", "path", relPath) + slog.Debug("Ignoring common pattern", "path", relPath) return true } parentDir := filepath.Dir(path) ignoreParser := dl.getIgnore(parentDir) if ignoreParser.MatchesPath(relPath) { - slog.Debug("ignoring dir pattern", "path", relPath, "dir", parentDir) + slog.Debug("Ignoring dir pattern", "path", relPath, "dir", parentDir) return true } // For directories, also check with trailing slash (gitignore convention) if ignoreParser.MatchesPath(relPath + "/") { - slog.Debug("ignoring dir pattern with slash", "path", relPath+"/", "dir", parentDir) + slog.Debug("Ignoring dir pattern with slash", "path", relPath+"/", "dir", parentDir) return true } @@ -166,7 +166,7 @@ func (dl *directoryLister) shouldIgnore(path string, ignorePatterns []string) bo } if homeIgnore().MatchesPath(relPath) { - slog.Debug("ignoring home dir pattern", "path", relPath) + slog.Debug("Ignoring home dir pattern", "path", relPath) return true } @@ -177,7 +177,7 @@ func (dl *directoryLister) checkParentIgnores(path string) bool { parent := filepath.Dir(filepath.Dir(path)) for parent != "." && path != "." { if dl.getIgnore(parent).MatchesPath(path) { - slog.Debug("ingoring parent dir pattern", "path", path, "dir", parent) + slog.Debug("Ignoring parent dir pattern", "path", path, "dir", parent) return true } if parent == dl.rootPath { @@ -210,7 +210,7 @@ func ListDirectory(initialPath string, ignorePatterns []string, depth, limit int found := csync.NewSlice[string]() dl := NewDirectoryLister(initialPath) - slog.Debug("listing directory", "path", initialPath, "depth", depth, "limit", limit, "ignorePatterns", ignorePatterns) + slog.Debug("Listing directory", "path", initialPath, "depth", depth, "limit", limit, "ignorePatterns", ignorePatterns) conf := fastwalk.Config{ Follow: true, diff --git a/internal/home/home.go b/internal/home/home.go index e44649235ff5bb24c8bb644ae90e9002add45237..80fb1ea2e01630597c2547eaa8e4e55150ec6976 100644 --- a/internal/home/home.go +++ b/internal/home/home.go @@ -12,7 +12,7 @@ var homedir, homedirErr = os.UserHomeDir() func init() { if homedirErr != nil { - slog.Error("failed to get user home directory", "error", homedirErr) + slog.Error("Failed to get user home directory", "error", homedirErr) } } diff --git a/internal/lsp/client.go b/internal/lsp/client.go index 98aa75966160ba97af8c431d98c642fb558e5dc7..05ee570b9d5ad7a0d667b48084289bf0fe5d3dde 100644 --- a/internal/lsp/client.go +++ b/internal/lsp/client.go @@ -317,12 +317,12 @@ func (c *Client) HandlesFile(path string) bool { // Check if file is within working directory. absPath, err := filepath.Abs(path) if err != nil { - slog.Debug("cannot resolve path", "name", c.name, "file", path, "error", err) + slog.Debug("Cannot resolve path", "name", c.name, "file", path, "error", err) return false } relPath, err := filepath.Rel(c.workDir, absPath) if err != nil || strings.HasPrefix(relPath, "..") { - slog.Debug("file outside workspace", "name", c.name, "file", path, "workDir", c.workDir) + slog.Debug("File outside workspace", "name", c.name, "file", path, "workDir", c.workDir) return false } @@ -339,11 +339,11 @@ func (c *Client) HandlesFile(path string) bool { suffix = "." + suffix } if strings.HasSuffix(name, suffix) || filetype == string(kind) { - slog.Debug("handles file", "name", c.name, "file", name, "filetype", filetype, "kind", kind) + slog.Debug("Handles file", "name", c.name, "file", name, "filetype", filetype, "kind", kind) return true } } - slog.Debug("doesn't handle file", "name", c.name, "file", name) + slog.Debug("Doesn't handle file", "name", c.name, "file", name) return false } diff --git a/internal/session/session.go b/internal/session/session.go index 905ee1cf1417b148019d9688985c1f5200209d69..0ef6cfe22bebbf35df48f0db1fbe00c6d128251b 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -203,7 +203,7 @@ func (s *service) List(ctx context.Context) ([]Session, error) { func (s service) fromDBItem(item db.Session) Session { todos, err := unmarshalTodos(item.Todos.String) if err != nil { - slog.Error("failed to unmarshal todos", "session_id", item.ID, "error", err) + slog.Error("Failed to unmarshal todos", "session_id", item.ID, "error", err) } return Session{ ID: item.ID, diff --git a/internal/ui/image/image.go b/internal/ui/image/image.go index 5644146fec5b1e4e1e3a96c92a315c0bf986180d..07039433dded1647646704959791dfcad7d3d69f 100644 --- a/internal/ui/image/image.go +++ b/internal/ui/image/image.go @@ -168,7 +168,7 @@ func (e Encoding) Transmit(id string, img image.Image, cs CellSize, cols, rows i return chunk }, }); err != nil { - slog.Error("failed to encode image for kitty graphics", "err", err) + slog.Error("Failed to encode image for kitty graphics", "err", err) return uiutil.InfoMsg{ Type: uiutil.InfoTypeError, Msg: "failed to encode image", diff --git a/internal/ui/model/history.go b/internal/ui/model/history.go index 5acc6ef5feabdab2bcb7a81ba8a60f5f224dab11..5d2284ab1756257cc06b76de4621849f1e3071ba 100644 --- a/internal/ui/model/history.go +++ b/internal/ui/model/history.go @@ -27,7 +27,7 @@ func (m *UI) loadPromptHistory() tea.Cmd { messages, err = m.com.App.Messages.ListAllUserMessages(ctx) } if err != nil { - slog.Error("failed to load prompt history", "error", err) + slog.Error("Failed to load prompt history", "error", err) return promptHistoryLoadedMsg{messages: nil} } diff --git a/internal/ui/model/ui.go b/internal/ui/model/ui.go index eca2cf80a343214babbcfbdb37916a61d07a1ced..1b828dffd1ce86db8ae6efb53e1b23465dfa20f0 100644 --- a/internal/ui/model/ui.go +++ b/internal/ui/model/ui.go @@ -330,7 +330,7 @@ func (m *UI) loadCustomCommands() tea.Cmd { return func() tea.Msg { customCommands, err := commands.LoadCustomCommands(m.com.Config()) if err != nil { - slog.Error("failed to load custom commands", "error", err) + slog.Error("Failed to load custom commands", "error", err) } return userCommandsLoadedMsg{Commands: customCommands} } @@ -341,7 +341,7 @@ func (m *UI) loadMCPrompts() tea.Cmd { return func() tea.Msg { prompts, err := commands.LoadMCPPrompts() if err != nil { - slog.Error("failed to load mcp prompts", "error", err) + slog.Error("Failed to load MCP prompts", "error", err) } if prompts == nil { // flag them as loaded even if there is none or an error @@ -683,7 +683,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case uv.KittyGraphicsEvent: if !bytes.HasPrefix(msg.Payload, []byte("OK")) { - slog.Warn("unexpected Kitty graphics response", + slog.Warn("Unexpected Kitty graphics response", "response", string(msg.Payload), "options", msg.Options) } diff --git a/scripts/check_log_capitalization.sh b/scripts/check_log_capitalization.sh new file mode 100644 index 0000000000000000000000000000000000000000..fa5f651dfb1a7dc53876018029599edd3479d94f --- /dev/null +++ b/scripts/check_log_capitalization.sh @@ -0,0 +1,5 @@ +#!/bin/bash +if grep -rE 'slog\.(Error|Info|Warn|Debug|Fatal|Print|Println|Printf)\(["\"][a-z]' --include="*.go" . 2>/dev/null; then + echo "❌ Log messages must start with a capital letter. Found lowercase logs above." + exit 1 +fi