diff --git a/.golangci.yml b/.golangci.yml index f7e02cba7..4b463cc9d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,30 +3,62 @@ run: tests: true linters: enable: + - asasalint + - asciicheck + - bidichk - containedctx - copyloopvar + - decorder - depguard + - dogsled + - durationcheck - errcheck + - errname + - errorlint + - exptostd + - fatcontext + - forbidigo - ginkgolinter + - gocheckcompilerdirectives + - gochecknoinits + - gochecksumtype - gocritic + - gomoddirectives + - gomodguard + - goprintffuncname - govet + - grouper + - iface - importas # Enforces consistent import aliases. + - inamedparam - ineffassign - intrange + - iotamixing + - loggercheck - misspell + - mirror - nakedret - nolintlint + - nosprintfhostport + - nilnesserr + - predeclared + - reassign + - recvcheck - revive + - rowserrcheck + - sloglint - staticcheck + - testableexamples - testifylint - thelper - unconvert - unparam - unused - usestdlibvars - - forbidigo - - iotamixing - - gochecknoinits + - unqueryvet + - usetesting + - whitespace + - wastedassign settings: forbidigo: forbid: @@ -63,6 +95,9 @@ linters: deny: - pkg: github.com/stretchr/testify desc: don't use testify in production code + gomoddirectives: + replace-allow-list: + - github.com/charmbracelet/ultraviolet gocritic: disabled-checks: - dupImport diff --git a/cmd/root/run.go b/cmd/root/run.go index 552d330ff..1f2ae169a 100644 --- a/cmd/root/run.go +++ b/cmd/root/run.go @@ -2,6 +2,7 @@ package root import ( "context" + "errors" "fmt" "io" "log/slog" @@ -422,7 +423,8 @@ func (f *runExecFlags) handleExecMode(ctx context.Context, out *cli.Printer, rt OutputJSON: f.outputJSON, AutoApprove: f.autoApprove, }, rt, sess, execArgs) - if cliErr, ok := err.(cli.RuntimeError); ok { + var cliErr cli.RuntimeError + if errors.As(err, &cliErr) { return RuntimeError{Err: cliErr.Err} } return err diff --git a/pkg/app/transcript/transcript.go b/pkg/app/transcript/transcript.go index c1e65713c..91a18a206 100644 --- a/pkg/app/transcript/transcript.go +++ b/pkg/app/transcript/transcript.go @@ -87,7 +87,7 @@ func toJSONString(builder *strings.Builder, in string) { if err := json.Unmarshal([]byte(in), &content); err == nil { if formatted, err := json.MarshalIndent(content, "", " "); err == nil { builder.WriteString("```json\n") - builder.WriteString(string(formatted)) + builder.Write(formatted) builder.WriteString("\n```\n") } else { builder.WriteString(in) diff --git a/pkg/config/auto.go b/pkg/config/auto.go index 5b206aa95..73ff67062 100644 --- a/pkg/config/auto.go +++ b/pkg/config/auto.go @@ -32,11 +32,11 @@ var cloudProviders = []providerConfig{ }, "AWS_ACCESS_KEY_ID (or AWS_PROFILE, AWS_ROLE_ARN, AWS_BEARER_TOKEN_BEDROCK)"}, } -// ErrAutoModelFallback is returned when auto model selection fails because +// AutoModelFallbackError is returned when auto model selection fails because // no providers are available (no API keys configured and DMR not installed). -type ErrAutoModelFallback struct{} +type AutoModelFallbackError struct{} -func (e *ErrAutoModelFallback) Error() string { +func (e *AutoModelFallbackError) Error() string { var hints []string for _, p := range cloudProviders { hints = append(hints, fmt.Sprintf(" - %s: %s", p.name, p.hint)) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 5b339360a..44ccbd2c6 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -271,7 +271,9 @@ func TestCheckRequiredEnvVars(t *testing.T) { require.NoError(t, err) } else { require.Error(t, err) - assert.Equal(t, test.expectedMissing, err.(*environment.RequiredEnvError).Missing) + var reqErr *environment.RequiredEnvError + require.ErrorAs(t, err, &reqErr) + assert.Equal(t, test.expectedMissing, reqErr.Missing) } }) } diff --git a/pkg/config/latest/types.go b/pkg/config/latest/types.go index 6f7412ed1..4c6cbb5d8 100644 --- a/pkg/config/latest/types.go +++ b/pkg/config/latest/types.go @@ -484,7 +484,7 @@ type SandboxConfig struct { // DeferConfig represents the deferred loading configuration for a toolset. // It can be either a boolean (true to defer all tools) or a slice of strings // (list of tool names to defer). -type DeferConfig struct { +type DeferConfig struct { //nolint:recvcheck // MarshalYAML must use value receiver for YAML slice encoding, UnmarshalYAML must use pointer // DeferAll is true when all tools should be deferred DeferAll bool `json:"-"` // Tools is the list of specific tool names to defer (empty if DeferAll is true) @@ -635,7 +635,7 @@ func (c *RAGConfig) GetRespectVCS() bool { // RAGStrategyConfig represents a single retrieval strategy configuration // Strategy-specific fields are stored in Params (validated by strategy implementation) -type RAGStrategyConfig struct { +type RAGStrategyConfig struct { //nolint:recvcheck // Marshal methods must use value receiver for YAML/JSON slice encoding, Unmarshal must use pointer Type string `json:"type"` // Strategy type: "chunked-embeddings", "bm25", etc. Docs []string `json:"docs,omitempty"` // Strategy-specific documents (augments shared docs) Database RAGDatabaseConfig `json:"database,omitempty"` // Database configuration diff --git a/pkg/config/types/commands.go b/pkg/config/types/commands.go index a383fad0d..75dfa1f2e 100644 --- a/pkg/config/types/commands.go +++ b/pkg/config/types/commands.go @@ -112,7 +112,7 @@ func (c *Commands) UnmarshalYAML(unmarshal func(any) error) error { // parseCommandValue parses a command value which can be either: // - a simple string (becomes the instruction) -// - a map with description/instruction fields +// - a map with description/instruction fields. func parseCommandValue(v any) (Command, error) { switch val := v.(type) { case string: diff --git a/pkg/config/v0/types.go b/pkg/config/v0/types.go index 630ce7989..d0cbf519c 100644 --- a/pkg/config/v0/types.go +++ b/pkg/config/v0/types.go @@ -8,7 +8,7 @@ import ( const Version = "0" -// Toolset represents a tool configuration +// Toolset represents a tool configuration. type Toolset struct { Type string `json:"type,omitempty" yaml:"type,omitempty"` Command string `json:"command,omitempty" yaml:"command,omitempty"` @@ -25,7 +25,7 @@ type Remote struct { Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"` } -// Ensure that either Command or Remote is set, but not both empty +// Ensure that either Command or Remote is set, but not both empty. func (t *Toolset) validate() error { if t.Type != "mcp" { return nil diff --git a/pkg/config/v2/types.go b/pkg/config/v2/types.go index d071c3fbf..fb100d361 100644 --- a/pkg/config/v2/types.go +++ b/pkg/config/v2/types.go @@ -251,7 +251,7 @@ type RAGConfig struct { // RAGStrategyConfig represents a single retrieval strategy configuration // Strategy-specific fields are stored in Params (validated by strategy implementation) -type RAGStrategyConfig struct { +type RAGStrategyConfig struct { //nolint:recvcheck // Marshal methods must use value receiver for YAML/JSON slice encoding, Unmarshal must use pointer Type string `json:"type"` // Strategy type: "chunked-embeddings", "bm25", etc. Docs []string `json:"docs,omitempty"` // Strategy-specific documents (augments shared docs) Database RAGDatabaseConfig `json:"database,omitempty"` // Database configuration diff --git a/pkg/config/v3/types.go b/pkg/config/v3/types.go index 88ce4cf6c..54d084c98 100644 --- a/pkg/config/v3/types.go +++ b/pkg/config/v3/types.go @@ -230,7 +230,7 @@ type SandboxConfig struct { // DeferConfig represents the deferred loading configuration for a toolset. // It can be either a boolean (true to defer all tools) or a slice of strings // (list of tool names to defer). -type DeferConfig struct { +type DeferConfig struct { //nolint:recvcheck // MarshalYAML must use value receiver for YAML slice encoding, UnmarshalYAML must use pointer // DeferAll is true when all tools should be deferred DeferAll bool `json:"-"` // Tools is the list of specific tool names to defer (empty if DeferAll is true) @@ -381,7 +381,7 @@ func (c *RAGConfig) GetRespectVCS() bool { // RAGStrategyConfig represents a single retrieval strategy configuration // Strategy-specific fields are stored in Params (validated by strategy implementation) -type RAGStrategyConfig struct { +type RAGStrategyConfig struct { //nolint:recvcheck // Marshal methods must use value receiver for YAML/JSON slice encoding, Unmarshal must use pointer Type string `json:"type"` // Strategy type: "chunked-embeddings", "bm25", etc. Docs []string `json:"docs,omitempty"` // Strategy-specific documents (augments shared docs) Database RAGDatabaseConfig `json:"database,omitempty"` // Database configuration diff --git a/pkg/environment/keychain.go b/pkg/environment/keychain.go index 85b529dcc..07c3d9b02 100644 --- a/pkg/environment/keychain.go +++ b/pkg/environment/keychain.go @@ -13,9 +13,9 @@ import ( // via the `security` command-line tool. type KeychainProvider struct{} -type ErrKeychainNotAvailable struct{} +type KeychainNotAvailableError struct{} -func (ErrKeychainNotAvailable) Error() string { +func (KeychainNotAvailableError) Error() string { return "security command is not available (macOS keychain access)" } @@ -27,7 +27,7 @@ func NewKeychainProvider() (*KeychainProvider, error) { slog.Warn("failed to lookup `security` binary", "error", err) } if path == "" { - return nil, ErrKeychainNotAvailable{} + return nil, KeychainNotAvailableError{} } return &KeychainProvider{}, nil } diff --git a/pkg/environment/pass.go b/pkg/environment/pass.go index 892fa4952..cade97fe1 100644 --- a/pkg/environment/pass.go +++ b/pkg/environment/pass.go @@ -13,9 +13,9 @@ import ( // manager. type PassProvider struct{} -type ErrPassNotAvailable struct{} +type PassNotAvailableError struct{} -func (ErrPassNotAvailable) Error() string { +func (PassNotAvailableError) Error() string { return "pass is not installed" } @@ -26,7 +26,7 @@ func NewPassProvider() (*PassProvider, error) { slog.Warn("failed to lookup `pass` binary", "error", err) } if path == "" { - return nil, ErrPassNotAvailable{} + return nil, PassNotAvailableError{} } return &PassProvider{}, nil } diff --git a/pkg/evaluation/eval.go b/pkg/evaluation/eval.go index f060a39fb..d5ad81478 100644 --- a/pkg/evaluation/eval.go +++ b/pkg/evaluation/eval.go @@ -272,7 +272,7 @@ func (r *Runner) preBuildImages(ctx context.Context, out io.Writer, evals []Inpu } if len(errs) > 0 { - return fmt.Errorf("failed to build %d image(s): %v", len(errs), errs[0]) + return fmt.Errorf("failed to build %d image(s): %w", len(errs), errs[0]) } return nil diff --git a/pkg/fake/proxy.go b/pkg/fake/proxy.go index 46c138e32..ab0d872f4 100644 --- a/pkg/fake/proxy.go +++ b/pkg/fake/proxy.go @@ -6,6 +6,7 @@ import ( "bufio" "bytes" "context" + "errors" "fmt" "io" "log/slog" @@ -428,7 +429,7 @@ func StreamCopy(c echo.Context, resp *http.Response) error { } if result.err != nil { // io.EOF or context canceled means normal completion - if result.err == io.EOF || ctx.Err() != nil { + if errors.Is(result.err, io.EOF) || ctx.Err() != nil { return nil } slog.ErrorContext(ctx, "stream read error", "error", result.err) diff --git a/pkg/fsx/fs.go b/pkg/fsx/fs.go index 34d5d20bc..ed9a83c73 100644 --- a/pkg/fsx/fs.go +++ b/pkg/fsx/fs.go @@ -2,6 +2,7 @@ package fsx import ( "context" + "errors" "io/fs" "os" "path/filepath" @@ -209,7 +210,7 @@ func WalkFiles(ctx context.Context, root string, opts WalkFilesOptions) ([]strin return nil }) - if err != nil && err != context.Canceled && err != context.DeadlineExceeded { + if err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { return files, err } diff --git a/pkg/hooks/executor.go b/pkg/hooks/executor.go index 84876977f..7d86c2027 100644 --- a/pkg/hooks/executor.go +++ b/pkg/hooks/executor.go @@ -5,6 +5,7 @@ import ( "cmp" "context" "encoding/json" + "errors" "fmt" "log/slog" "os" @@ -264,7 +265,8 @@ func (e *Executor) executeHook(ctx context.Context, hook Hook, inputJSON []byte) exitCode := 0 if err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { exitCode = exitErr.ExitCode() } else { return nil, stdout.String(), stderr.String(), -1, err diff --git a/pkg/mcp/server.go b/pkg/mcp/server.go index fbd51f637..3e60254fa 100644 --- a/pkg/mcp/server.go +++ b/pkg/mcp/server.go @@ -3,6 +3,7 @@ package mcp import ( "cmp" "context" + "errors" "fmt" "log/slog" "net" @@ -74,7 +75,7 @@ func StartHTTPServer(ctx context.Context, agentFilename, agentName string, runCo case <-ctx.Done(): return httpServer.Shutdown(context.Background()) case err := <-errCh: - if err == http.ErrServerClosed { + if errors.Is(err, http.ErrServerClosed) { return nil } return err diff --git a/pkg/memory/database/sqlite/sqlite.go b/pkg/memory/database/sqlite/sqlite.go index 515d0df56..deebaaec7 100644 --- a/pkg/memory/database/sqlite/sqlite.go +++ b/pkg/memory/database/sqlite/sqlite.go @@ -55,6 +55,10 @@ func (m *MemoryDatabase) GetMemories(ctx context.Context) ([]database.UserMemory memories = append(memories, memory) } + if err := rows.Err(); err != nil { + return nil, err + } + return memories, nil } diff --git a/pkg/model/provider/custom_provider_test.go b/pkg/model/provider/custom_provider_test.go index 17bc1facc..557f16e73 100644 --- a/pkg/model/provider/custom_provider_test.go +++ b/pkg/model/provider/custom_provider_test.go @@ -3,6 +3,7 @@ package provider import ( "context" "encoding/json" + "errors" "io" "net/http" "net/http/httptest" @@ -502,7 +503,7 @@ func drainStream(t *testing.T, stream chat.MessageStream) { t.Helper() for { _, err := stream.Recv() - if err == io.EOF { + if errors.Is(err, io.EOF) { return } if err != nil { diff --git a/pkg/model/provider/dmr/client.go b/pkg/model/provider/dmr/client.go index 7e825adce..d7a8f7346 100644 --- a/pkg/model/provider/dmr/client.go +++ b/pkg/model/provider/dmr/client.go @@ -193,13 +193,13 @@ func getDMRFallbackURLs(containerized bool) []string { // Inside a container: try Docker internal hostnames and bridge gateway return []string{ fmt.Sprintf("http://%s%s/v1/", dmrModelRunnerInternal, dmrInferencePrefix), - fmt.Sprintf("http://%s:%s%s/v1/", dmrHostDockerInternal, dmrDefaultPort, dmrInferencePrefix), - fmt.Sprintf("http://%s:%s%s/v1/", dmrDockerBridgeGateway, dmrDefaultPort, dmrInferencePrefix), + "http://" + net.JoinHostPort(dmrHostDockerInternal, dmrDefaultPort) + dmrInferencePrefix + "/v1/", + "http://" + net.JoinHostPort(dmrDockerBridgeGateway, dmrDefaultPort) + dmrInferencePrefix + "/v1/", } } // On the host: only localhost makes sense as a fallback return []string{ - fmt.Sprintf("http://%s:%s%s/v1/", dmrLocalhost, dmrDefaultPort, dmrInferencePrefix), + "http://" + net.JoinHostPort(dmrLocalhost, dmrDefaultPort) + dmrInferencePrefix + "/v1/", } } diff --git a/pkg/rag/strategy/bm25_database.go b/pkg/rag/strategy/bm25_database.go index 8f142309a..7e70f012d 100644 --- a/pkg/rag/strategy/bm25_database.go +++ b/pkg/rag/strategy/bm25_database.go @@ -3,6 +3,7 @@ package strategy import ( "context" "database/sql" + "errors" "fmt" "log/slog" "os" @@ -96,7 +97,7 @@ func (d *bm25DB) AddDocument(ctx context.Context, doc database.Document) error { fmt.Sprintf("SELECT 1 FROM %s WHERE source_path = ? AND chunk_index = ?", d.docsTable), doc.SourcePath, doc.ChunkIndex).Scan(&exists) - if err != nil && err != sql.ErrNoRows { + if err != nil && !errors.Is(err, sql.ErrNoRows) { return fmt.Errorf("failed to check existing document: %w", err) } diff --git a/pkg/runtime/connectrpc_client.go b/pkg/runtime/connectrpc_client.go index a8f1a226a..27610b142 100644 --- a/pkg/runtime/connectrpc_client.go +++ b/pkg/runtime/connectrpc_client.go @@ -3,6 +3,7 @@ package runtime import ( "context" "encoding/json" + "errors" "fmt" "io" "log/slog" @@ -241,7 +242,7 @@ func (c *ConnectRPCClient) runAgentWithAgentName(ctx context.Context, sessionID, } } - if err := stream.Err(); err != nil && err != io.EOF { + if err := stream.Err(); err != nil && !errors.Is(err, io.EOF) { slog.Error("Stream error", "error", err) eventChan <- Error(fmt.Sprintf("stream error: %v", err)) } diff --git a/pkg/session/migrations.go b/pkg/session/migrations.go index ab7ec534b..5aa5c29d2 100644 --- a/pkg/session/migrations.go +++ b/pkg/session/migrations.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "encoding/json" + "errors" "fmt" "log/slog" "time" @@ -161,6 +162,10 @@ func (m *MigrationManager) GetAppliedMigrations(ctx context.Context) ([]Migratio migrations = append(migrations, migration) } + if err := rows.Err(); err != nil { + return nil, err + } + return migrations, nil } @@ -406,7 +411,7 @@ func migrateItem(ctx context.Context, db *sql.DB, sessionID string, position int var exists int err := db.QueryRowContext(ctx, "SELECT 1 FROM sessions WHERE id = ?", subSessionID).Scan(&exists) switch { - case err == sql.ErrNoRows: + case errors.Is(err, sql.ErrNoRows): // Create the sub-session subMessagesJSON, jsonErr := json.Marshal(item.SubSession.Messages) if jsonErr != nil { diff --git a/pkg/session/store.go b/pkg/session/store.go index 024829670..d022fe3be 100644 --- a/pkg/session/store.go +++ b/pkg/session/store.go @@ -375,7 +375,7 @@ func NewSQLiteSessionStore(path string) (Store, error) { if backupErr != nil { // Return the original error if backup failed slog.Error("Failed to backup database for recovery", "error", backupErr) - return nil, fmt.Errorf("migration failed: %w (backup also failed: %v)", err, backupErr) + return nil, fmt.Errorf("migration failed: %w (backup also failed: %w)", err, backupErr) } // Try again with a fresh database @@ -870,6 +870,10 @@ func (s *SQLiteSessionStore) GetSessionSummaries(ctx context.Context) ([]Summary }) } + if err := rows.Err(); err != nil { + return nil, err + } + return summaries, nil } diff --git a/pkg/sqliteutil/sqlite.go b/pkg/sqliteutil/sqlite.go index ed4108cc6..90616fbd3 100644 --- a/pkg/sqliteutil/sqlite.go +++ b/pkg/sqliteutil/sqlite.go @@ -88,5 +88,5 @@ func DiagnoseDBOpenError(path string, originalErr error) error { return fmt.Errorf("cannot create database at %q: %q is not a directory", path, dir) } - return fmt.Errorf("cannot create database at %q: permission denied or file cannot be created in %q (original error: %v)", path, dir, originalErr) + return fmt.Errorf("cannot create database at %q: permission denied or file cannot be created in %q (original error: %w)", path, dir, originalErr) } diff --git a/pkg/teamloader/teamloader.go b/pkg/teamloader/teamloader.go index b9c648c4d..ea5fe878c 100644 --- a/pkg/teamloader/teamloader.go +++ b/pkg/teamloader/teamloader.go @@ -162,7 +162,7 @@ func LoadWithConfig(ctx context.Context, agentSource config.Source, runConfig *c if err != nil { // Return auto model fallback errors and DMR not installed errors directly // without wrapping to provide cleaner messages - var autoErr *config.ErrAutoModelFallback + var autoErr *config.AutoModelFallbackError if errors.As(err, &autoErr) || errors.Is(err, dmr.ErrNotInstalled) { return nil, err } @@ -314,7 +314,7 @@ func getModelsForAgent(ctx context.Context, cfg *latest.Config, a *latest.AgentC if err != nil { // Return a cleaner error message for auto model selection failures if isAutoModel { - return nil, false, &config.ErrAutoModelFallback{} + return nil, false, &config.AutoModelFallbackError{} } return nil, false, err } diff --git a/pkg/teamloader/teamloader_test.go b/pkg/teamloader/teamloader_test.go index 8e270a89e..e5a0e3a08 100644 --- a/pkg/teamloader/teamloader_test.go +++ b/pkg/teamloader/teamloader_test.go @@ -256,8 +256,8 @@ func TestAutoModelFallbackError(t *testing.T) { _, err = Load(t.Context(), agentSource, runConfig) require.Error(t, err) - var autoErr *config.ErrAutoModelFallback - require.ErrorAs(t, err, &autoErr, "expected ErrAutoModelFallback when auto model selection fails") + var autoErr *config.AutoModelFallbackError + require.ErrorAs(t, err, &autoErr, "expected AutoModelFallbackError when auto model selection fails") } func TestIsThinkingBudgetDisabled(t *testing.T) { diff --git a/pkg/tools/builtin/api.go b/pkg/tools/builtin/api.go index b8ce16a04..b8ee47711 100644 --- a/pkg/tools/builtin/api.go +++ b/pkg/tools/builtin/api.go @@ -56,14 +56,14 @@ func (t *APITool) callTool(ctx context.Context, toolCall tools.ToolCall) (*tools } jsonData, err := json.Marshal(params) if err != nil { - return nil, fmt.Errorf("failed to marshal request body: %v", err) + return nil, fmt.Errorf("failed to marshal request body: %w", err) } reqBody = bytes.NewReader(jsonData) } req, err := http.NewRequestWithContext(ctx, t.config.Method, endpoint, reqBody) if err != nil { - return nil, fmt.Errorf("failed to create request: %v", err) + return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("User-Agent", useragent.Header) @@ -77,14 +77,14 @@ func (t *APITool) callTool(ctx context.Context, toolCall tools.ToolCall) (*tools resp, err := client.Do(req) if err != nil { - return nil, fmt.Errorf("request failed: %v", err) + return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() maxSize := int64(1 << 20) body, err := io.ReadAll(io.LimitReader(resp.Body, maxSize)) if err != nil { - return nil, fmt.Errorf("failed to read response body: %v", err) + return nil, fmt.Errorf("failed to read response body: %w", err) } return tools.ResultSuccess(limitOutput(string(body))), nil diff --git a/pkg/tools/builtin/filesystem.go b/pkg/tools/builtin/filesystem.go index 25e2a8159..9581243ae 100644 --- a/pkg/tools/builtin/filesystem.go +++ b/pkg/tools/builtin/filesystem.go @@ -299,7 +299,6 @@ func (t *FilesystemTool) executePostEditCommands(ctx context.Context, filePath s if err := cmd.Run(); err != nil { return fmt.Errorf("post-edit command failed for %s: %w", filePath, err) } - } return nil } diff --git a/pkg/tools/builtin/shell.go b/pkg/tools/builtin/shell.go index 5e9609112..88735a843 100644 --- a/pkg/tools/builtin/shell.go +++ b/pkg/tools/builtin/shell.go @@ -4,6 +4,7 @@ import ( "bytes" "cmp" "context" + "errors" "fmt" "os" "os/exec" @@ -242,7 +243,8 @@ func (h *shellHandler) monitorJob(job *backgroundJob, cmd *exec.Cmd) { } if err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { job.exitCode = exitErr.ExitCode() } else { job.exitCode = -1 diff --git a/pkg/tools/mcp/mcp.go b/pkg/tools/mcp/mcp.go index 867843509..4596d9e60 100644 --- a/pkg/tools/mcp/mcp.go +++ b/pkg/tools/mcp/mcp.go @@ -124,7 +124,6 @@ func (ts *Toolset) doStart(ctx context.Context) error { // // Only retry when initialization fails due to sending the initialized notification. if !isInitNotificationSendError(err) { - // EOF means the MCP server is unavailable or closed the connection. // This is not a fatal error and should not fail the agent execution. if errors.Is(err, io.EOF) { diff --git a/pkg/tools/mcp/oauth.go b/pkg/tools/mcp/oauth.go index 0f74e237e..5502a77ef 100644 --- a/pkg/tools/mcp/oauth.go +++ b/pkg/tools/mcp/oauth.go @@ -272,8 +272,8 @@ func (t *oauthTransport) handleManagedOAuthFlow(ctx context.Context, authServer, redirectURI := callbackServer.GetRedirectURI() slog.Debug("Using redirect URI", "uri", redirectURI) - clientID := "" - clientSecret := "" + var clientID string + var clientSecret string if authServerMetadata.RegistrationEndpoint != "" { slog.Debug("Attempting dynamic client registration") diff --git a/pkg/tui/components/markdown/fast_renderer.go b/pkg/tui/components/markdown/fast_renderer.go index be332152d..f58bc7cb7 100644 --- a/pkg/tui/components/markdown/fast_renderer.go +++ b/pkg/tui/components/markdown/fast_renderer.go @@ -2303,7 +2303,7 @@ func (p *parser) wrapText(text string, width int) string { wordWidth := ws.width // Determine if we need to wrap - only then do we need the previous styles - needsWrap := false + var needsWrap bool if wordWidth > width { needsWrap = currentLine.Len() > 0 } else { diff --git a/pkg/tui/core/layout/layout.go b/pkg/tui/core/layout/layout.go index 54c8e9688..b00a91e6b 100644 --- a/pkg/tui/core/layout/layout.go +++ b/pkg/tui/core/layout/layout.go @@ -30,7 +30,7 @@ type Help interface { // Model is the base interface for all TUI models type Model interface { Init() tea.Cmd - Update(tea.Msg) (Model, tea.Cmd) + Update(msg tea.Msg) (Model, tea.Cmd) View() string Sizeable }