diff --git a/.github/workflows/golangci.yml b/.github/workflows/golangci.yml new file mode 100644 index 00000000..c81e2398 --- /dev/null +++ b/.github/workflows/golangci.yml @@ -0,0 +1,27 @@ +name: golangci-lint +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +jobs: + golangci: + strategy: + matrix: + go: [stable] + os: [ubuntu-latest, macos-latest, windows-latest] + name: lint + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go }} + - name: golangci-lint + uses: golangci/golangci-lint-action@v7 + with: + version: v2.0 \ No newline at end of file diff --git a/cmd/root.go b/cmd/root.go index 74ace366..c2b36810 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -8,6 +8,7 @@ import ( "time" tea "github.com/charmbracelet/bubbletea" + zone "github.com/lrstanley/bubblezone" "github.com/opencode-ai/opencode/internal/app" "github.com/opencode-ai/opencode/internal/config" "github.com/opencode-ai/opencode/internal/db" @@ -16,7 +17,6 @@ import ( "github.com/opencode-ai/opencode/internal/pubsub" "github.com/opencode-ai/opencode/internal/tui" "github.com/opencode-ai/opencode/internal/version" - zone "github.com/lrstanley/bubblezone" "github.com/spf13/cobra" ) @@ -29,8 +29,7 @@ to assist developers in writing, debugging, and understanding code directly from RunE: func(cmd *cobra.Command, args []string) error { // If the help flag is set, show the help message if cmd.Flag("help").Changed { - cmd.Help() - return nil + return cmd.Help() } if cmd.Flag("version").Changed { fmt.Println(version.Version) diff --git a/internal/app/lsp.go b/internal/app/lsp.go index 872532fd..9766595d 100644 --- a/internal/app/lsp.go +++ b/internal/app/lsp.go @@ -25,7 +25,7 @@ func (app *App) initLSPClients(ctx context.Context) { func (app *App) createAndStartLSPClient(ctx context.Context, name string, command string, args ...string) { // Create a specific context for initialization with a timeout logging.Info("Creating LSP client", "name", name, "command", command, "args", args) - + // Create the LSP client lspClient, err := lsp.NewClient(ctx, command, args...) if err != nil { @@ -36,13 +36,13 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, comman // Create a longer timeout for initialization (some servers take time to start) initCtx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() - + // Initialize with the initialization context _, err = lspClient.InitializeLSPClient(initCtx, config.WorkingDirectory()) if err != nil { logging.Error("Initialize failed", "name", name, "error", err) // Clean up the client to prevent resource leaks - lspClient.Close() + lspClient.Close() //nolint:errcheck return } @@ -57,13 +57,15 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, comman } logging.Info("LSP client initialized", "name", name) - + // Create a child context that can be canceled when the app is shutting down watchCtx, cancelFunc := context.WithCancel(ctx) - + // Create a context with the server name for better identification + //nolint:staticcheck + //lint:ignore SA1029 will be resolved in the future watchCtx = context.WithValue(watchCtx, "serverName", name) - + // Create the workspace watcher workspaceWatcher := watcher.NewWorkspaceWatcher(lspClient) diff --git a/internal/config/config.go b/internal/config/config.go index 4864ef18..36aba05b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -127,7 +127,9 @@ func Load(workingDir string, debug bool) (*Config, error) { } // Load and merge local config - mergeLocalConfig(workingDir) + if err := mergeLocalConfig(workingDir); err != nil { + return cfg, err + } // Apply configuration to the struct if err := viper.Unmarshal(cfg); err != nil { @@ -315,7 +317,7 @@ func readConfig(err error) error { } // mergeLocalConfig loads and merges configuration from the local directory. -func mergeLocalConfig(workingDir string) { +func mergeLocalConfig(workingDir string) error { local := viper.New() local.SetConfigName(fmt.Sprintf(".%s", appName)) local.SetConfigType("json") @@ -323,8 +325,9 @@ func mergeLocalConfig(workingDir string) { // Merge local config if it exists if err := local.ReadInConfig(); err == nil { - viper.MergeConfigMap(local.AllSettings()) + return viper.MergeConfigMap(local.AllSettings()) } + return nil } // applyDefaultValues sets default values for configuration fields that need processing. diff --git a/internal/config/init.go b/internal/config/init.go index e0a1c6da..c7a6ee3e 100644 --- a/internal/config/init.go +++ b/internal/config/init.go @@ -54,8 +54,7 @@ func MarkProjectInitialized() error { if err != nil { return fmt.Errorf("failed to create init flag file: %w", err) } - defer file.Close() + defer file.Close() //nolint:errcheck return nil } - diff --git a/internal/db/connect.go b/internal/db/connect.go index b8fcb736..bb132941 100644 --- a/internal/db/connect.go +++ b/internal/db/connect.go @@ -32,7 +32,7 @@ func Connect() (*sql.DB, error) { // Verify connection if err = db.Ping(); err != nil { - db.Close() + db.Close() //nolint:errcheck return nil, fmt.Errorf("failed to connect to database: %w", err) } diff --git a/internal/history/file.go b/internal/history/file.go index 9cdb2e47..2bf7cea9 100644 --- a/internal/history/file.go +++ b/internal/history/file.go @@ -121,7 +121,7 @@ func (s *service) createWithVersion(ctx context.Context, sessionID, path, conten }) if txErr != nil { // Rollback the transaction - tx.Rollback() + tx.Rollback() //nolint:errcheck // Check if this is a uniqueness constraint violation if strings.Contains(txErr.Error(), "UNIQUE constraint failed") { diff --git a/internal/llm/agent/agent.go b/internal/llm/agent/agent.go index c5f02407..ef4121fe 100644 --- a/internal/llm/agent/agent.go +++ b/internal/llm/agent/agent.go @@ -222,7 +222,7 @@ func (a *agent) processGeneration(ctx context.Context, sessionID, content string if err != nil { if errors.Is(err, context.Canceled) { agentMessage.AddFinish(message.FinishReasonCanceled) - a.messages.Update(context.Background(), agentMessage) + a.messages.Update(context.Background(), agentMessage) //nolint:errcheck return a.err(ErrRequestCancelled) } return a.err(fmt.Errorf("failed to process events: %w", err)) diff --git a/internal/llm/agent/mcp-tools.go b/internal/llm/agent/mcp-tools.go index 23756064..e865d324 100644 --- a/internal/llm/agent/mcp-tools.go +++ b/internal/llm/agent/mcp-tools.go @@ -42,7 +42,7 @@ func (b *mcpTool) Info() tools.ToolInfo { } func runTool(ctx context.Context, c MCPClient, toolName string, input string) (tools.ToolResponse, error) { - defer c.Close() + defer c.Close() //nolint:errcheck initRequest := mcp.InitializeRequest{} initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION initRequest.Params.ClientInfo = mcp.Implementation{ @@ -158,7 +158,7 @@ func getTools(ctx context.Context, name string, m config.MCPServer, permissions for _, t := range tools.Tools { stdioTools = append(stdioTools, NewMcpTool(name, t, permissions, m)) } - defer c.Close() + defer c.Close() //nolint:errcheck return stdioTools } diff --git a/internal/llm/prompt/prompt.go b/internal/llm/prompt/prompt.go index 83ec7442..1b0dca0c 100644 --- a/internal/llm/prompt/prompt.go +++ b/internal/llm/prompt/prompt.go @@ -71,7 +71,7 @@ func processContextPaths(workDir string, paths []string) string { defer wg.Done() if strings.HasSuffix(p, "/") { - filepath.WalkDir(filepath.Join(workDir, p), func(path string, d os.DirEntry, err error) error { + filepath.WalkDir(filepath.Join(workDir, p), func(path string, d os.DirEntry, err error) error { //nolint:errcheck if err != nil { return err } diff --git a/internal/llm/provider/anthropic.go b/internal/llm/provider/anthropic.go index fc131d34..31c1adbf 100644 --- a/internal/llm/provider/anthropic.go +++ b/internal/llm/provider/anthropic.go @@ -12,7 +12,6 @@ import ( "github.com/anthropics/anthropic-sdk-go" "github.com/anthropics/anthropic-sdk-go/bedrock" "github.com/anthropics/anthropic-sdk-go/option" - "github.com/opencode-ai/opencode/internal/config" "github.com/opencode-ai/opencode/internal/llm/tools" "github.com/opencode-ai/opencode/internal/logging" "github.com/opencode-ai/opencode/internal/message" @@ -194,11 +193,6 @@ func (a *anthropicClient) preparedMessages(messages []anthropic.MessageParam, to func (a *anthropicClient) send(ctx context.Context, messages []message.Message, tools []tools.BaseTool) (resposne *ProviderResponse, err error) { preparedMessages := a.preparedMessages(a.convertMessages(messages), a.convertTools(tools)) - cfg := config.Get() - if cfg.Debug { - // jsonData, _ := json.Marshal(preparedMessages) - // logging.Debug("Prepared messages", "messages", string(jsonData)) - } attempts := 0 for { attempts++ @@ -241,11 +235,6 @@ func (a *anthropicClient) send(ctx context.Context, messages []message.Message, func (a *anthropicClient) stream(ctx context.Context, messages []message.Message, tools []tools.BaseTool) <-chan ProviderEvent { preparedMessages := a.preparedMessages(a.convertMessages(messages), a.convertTools(tools)) - cfg := config.Get() - if cfg.Debug { - // jsonData, _ := json.Marshal(preparedMessages) - // logging.Debug("Prepared messages", "messages", string(jsonData)) - } attempts := 0 eventChan := make(chan ProviderEvent) go func() { @@ -268,9 +257,10 @@ func (a *anthropicClient) stream(ctx context.Context, messages []message.Message switch event := event.AsAny().(type) { case anthropic.ContentBlockStartEvent: - if event.ContentBlock.Type == "text" { + switch event.ContentBlock.Type { + case "text": eventChan <- ProviderEvent{Type: EventContentStart} - } else if event.ContentBlock.Type == "tool_use" { + case "tool_use": currentToolCallID = event.ContentBlock.ID eventChan <- ProviderEvent{ Type: EventToolUseStart, diff --git a/internal/llm/provider/bedrock.go b/internal/llm/provider/bedrock.go index ca0d508c..9fa3ca87 100644 --- a/internal/llm/provider/bedrock.go +++ b/internal/llm/provider/bedrock.go @@ -55,7 +55,7 @@ func newBedrockClient(opts providerClientOptions) BedrockClient { if strings.Contains(string(opts.model.APIModel), "anthropic") { // Create Anthropic client with Bedrock configuration anthropicOpts := opts - anthropicOpts.anthropicOptions = append(anthropicOpts.anthropicOptions, + anthropicOpts.anthropicOptions = append(anthropicOpts.anthropicOptions, WithAnthropicBedrock(true), WithAnthropicDisableCache(), ) @@ -84,7 +84,7 @@ func (b *bedrockClient) send(ctx context.Context, messages []message.Message, to func (b *bedrockClient) stream(ctx context.Context, messages []message.Message, tools []tools.BaseTool) <-chan ProviderEvent { eventChan := make(chan ProviderEvent) - + if b.childProvider == nil { go func() { eventChan <- ProviderEvent{ @@ -95,6 +95,6 @@ func (b *bedrockClient) stream(ctx context.Context, messages []message.Message, }() return eventChan } - + return b.childProvider.stream(ctx, messages, tools) -} \ No newline at end of file +} diff --git a/internal/llm/provider/gemini.go b/internal/llm/provider/gemini.go index d8fd6619..82e4a270 100644 --- a/internal/llm/provider/gemini.go +++ b/internal/llm/provider/gemini.go @@ -141,10 +141,10 @@ func (g *geminiClient) convertTools(tools []tools.BaseTool) []*genai.Tool { } func (g *geminiClient) finishReason(reason genai.FinishReason) message.FinishReason { - switch { - case reason == genai.FinishReasonStop: + switch reason { + case genai.FinishReasonStop: return message.FinishReasonEndTurn - case reason == genai.FinishReasonMaxTokens: + case genai.FinishReasonMaxTokens: return message.FinishReasonMaxTokens default: return message.FinishReasonUnknown @@ -392,7 +392,7 @@ func (g *geminiClient) shouldRetry(attempts int, err error) (bool, int64, error) } errMsg := err.Error() - isRateLimit := false + var isRateLimit bool // Check for common rate limit error messages if contains(errMsg, "rate limit", "quota exceeded", "too many requests") { @@ -411,6 +411,7 @@ func (g *geminiClient) shouldRetry(attempts int, err error) (bool, int64, error) return true, int64(retryMs), nil } +//lint:ignore U1000 will be used in the future func (g *geminiClient) toolCalls(resp *genai.GenerateContentResponse) []message.ToolCall { var toolCalls []message.ToolCall diff --git a/internal/llm/provider/openai.go b/internal/llm/provider/openai.go index 4d45aebf..84519bd6 100644 --- a/internal/llm/provider/openai.go +++ b/internal/llm/provider/openai.go @@ -148,7 +148,7 @@ func (o *openaiClient) preparedParams(messages []openai.ChatCompletionMessagePar Tools: tools, } - if o.providerOptions.model.CanReason == true { + if o.providerOptions.model.CanReason { params.MaxCompletionTokens = openai.Int(o.providerOptions.maxTokens) switch o.options.reasoningEffort { case "low": diff --git a/internal/llm/tools/fetch.go b/internal/llm/tools/fetch.go index 863532a0..2ee414f4 100644 --- a/internal/llm/tools/fetch.go +++ b/internal/llm/tools/fetch.go @@ -158,7 +158,7 @@ func (t *fetchTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error if err != nil { return ToolResponse{}, fmt.Errorf("failed to fetch URL: %w", err) } - defer resp.Body.Close() + defer resp.Body.Close() //nolint:errcheck if resp.StatusCode != http.StatusOK { return NewTextErrorResponse(fmt.Sprintf("Request failed with status code: %d", resp.StatusCode)), nil diff --git a/internal/llm/tools/grep.go b/internal/llm/tools/grep.go index 0dd42304..ef46c5ea 100644 --- a/internal/llm/tools/grep.go +++ b/internal/llm/tools/grep.go @@ -328,7 +328,7 @@ func fileContainsPattern(filePath string, pattern *regexp.Regexp) (bool, int, st if err != nil { return false, 0, "", err } - defer file.Close() + defer file.Close() //nolint:errcheck scanner := bufio.NewScanner(file) lineNum := 0 diff --git a/internal/llm/tools/ls_test.go b/internal/llm/tools/ls_test.go index 508cb98d..9507f415 100644 --- a/internal/llm/tools/ls_test.go +++ b/internal/llm/tools/ls_test.go @@ -27,7 +27,7 @@ func TestLsTool_Run(t *testing.T) { // Create a temporary directory for testing tempDir, err := os.MkdirTemp("", "ls_tool_test") require.NoError(t, err) - defer os.RemoveAll(tempDir) + defer os.RemoveAll(tempDir) //nolint:errcheck // Create a test directory structure testDirs := []string{ @@ -83,19 +83,19 @@ func TestLsTool_Run(t *testing.T) { response, err := tool.Run(context.Background(), call) require.NoError(t, err) - + // Check that visible directories and files are included assert.Contains(t, response.Content, "dir1") assert.Contains(t, response.Content, "dir2") assert.Contains(t, response.Content, "dir3") assert.Contains(t, response.Content, "file1.txt") assert.Contains(t, response.Content, "file2.txt") - + // Check that hidden files and directories are not included assert.NotContains(t, response.Content, ".hidden_dir") assert.NotContains(t, response.Content, ".hidden_file.txt") assert.NotContains(t, response.Content, ".hidden_root_file.txt") - + // Check that __pycache__ is not included assert.NotContains(t, response.Content, "__pycache__") }) @@ -122,7 +122,7 @@ func TestLsTool_Run(t *testing.T) { t.Run("handles empty path parameter", func(t *testing.T) { // For this test, we need to mock the config.WorkingDirectory function // Since we can't easily do that, we'll just check that the response doesn't contain an error message - + tool := NewLsTool() params := LSParams{ Path: "", @@ -138,7 +138,7 @@ func TestLsTool_Run(t *testing.T) { response, err := tool.Run(context.Background(), call) require.NoError(t, err) - + // The response should either contain a valid directory listing or an error // We'll just check that it's not empty assert.NotEmpty(t, response.Content) @@ -173,11 +173,11 @@ func TestLsTool_Run(t *testing.T) { response, err := tool.Run(context.Background(), call) require.NoError(t, err) - + // The output format is a tree, so we need to check for specific patterns // Check that file1.txt is not directly mentioned assert.NotContains(t, response.Content, "- file1.txt") - + // Check that dir1/ is not directly mentioned assert.NotContains(t, response.Content, "- dir1/") }) @@ -187,14 +187,14 @@ func TestLsTool_Run(t *testing.T) { origWd, err := os.Getwd() require.NoError(t, err) defer func() { - os.Chdir(origWd) + os.Chdir(origWd) //nolint:errcheck }() - + // Change to a directory above the temp directory parentDir := filepath.Dir(tempDir) err = os.Chdir(parentDir) require.NoError(t, err) - + tool := NewLsTool() params := LSParams{ Path: filepath.Base(tempDir), @@ -210,7 +210,7 @@ func TestLsTool_Run(t *testing.T) { response, err := tool.Run(context.Background(), call) require.NoError(t, err) - + // Should list the temp directory contents assert.Contains(t, response.Content, "dir1") assert.Contains(t, response.Content, "file1.txt") @@ -291,22 +291,22 @@ func TestCreateFileTree(t *testing.T) { } tree := createFileTree(paths) - + // Check the structure of the tree assert.Len(t, tree, 1) // Should have one root node - + // Check the root node rootNode := tree[0] assert.Equal(t, "path", rootNode.Name) assert.Equal(t, "directory", rootNode.Type) assert.Len(t, rootNode.Children, 1) - + // Check the "to" node toNode := rootNode.Children[0] assert.Equal(t, "to", toNode.Name) assert.Equal(t, "directory", toNode.Type) assert.Len(t, toNode.Children, 3) // file1.txt, dir1, dir2 - + // Find the dir1 node var dir1Node *TreeNode for _, child := range toNode.Children { @@ -315,7 +315,7 @@ func TestCreateFileTree(t *testing.T) { break } } - + require.NotNil(t, dir1Node) assert.Equal(t, "directory", dir1Node.Type) assert.Len(t, dir1Node.Children, 2) // file2.txt and subdir @@ -354,9 +354,9 @@ func TestPrintTree(t *testing.T) { Type: "file", }, } - + result := printTree(tree, "/root") - + // Check the output format assert.Contains(t, result, "- /root/") assert.Contains(t, result, " - dir1/") @@ -370,7 +370,7 @@ func TestListDirectory(t *testing.T) { // Create a temporary directory for testing tempDir, err := os.MkdirTemp("", "list_directory_test") require.NoError(t, err) - defer os.RemoveAll(tempDir) + defer os.RemoveAll(tempDir) //nolint:errcheck // Create a test directory structure testDirs := []string{ @@ -405,7 +405,7 @@ func TestListDirectory(t *testing.T) { files, truncated, err := listDirectory(tempDir, []string{}, 1000) require.NoError(t, err) assert.False(t, truncated) - + // Check that visible files and directories are included containsPath := func(paths []string, target string) bool { targetPath := filepath.Join(tempDir, target) @@ -416,12 +416,12 @@ func TestListDirectory(t *testing.T) { } return false } - + assert.True(t, containsPath(files, "dir1")) assert.True(t, containsPath(files, "file1.txt")) assert.True(t, containsPath(files, "file2.txt")) assert.True(t, containsPath(files, "dir1/file3.txt")) - + // Check that hidden files and directories are not included assert.False(t, containsPath(files, ".hidden_dir")) assert.False(t, containsPath(files, ".hidden_file.txt")) @@ -438,12 +438,12 @@ func TestListDirectory(t *testing.T) { files, truncated, err := listDirectory(tempDir, []string{"*.txt"}, 1000) require.NoError(t, err) assert.False(t, truncated) - + // Check that no .txt files are included for _, file := range files { assert.False(t, strings.HasSuffix(file, ".txt"), "Found .txt file: %s", file) } - + // But directories should still be included containsDir := false for _, file := range files { @@ -454,4 +454,4 @@ func TestListDirectory(t *testing.T) { } assert.True(t, containsDir) }) -} \ No newline at end of file +} diff --git a/internal/llm/tools/shell/shell.go b/internal/llm/tools/shell/shell.go index 5731faec..34100144 100644 --- a/internal/llm/tools/shell/shell.go +++ b/internal/llm/tools/shell/shell.go @@ -97,10 +97,7 @@ func newPersistentShell(cwd string) *PersistentShell { }() go func() { - err := cmd.Wait() - if err != nil { - // Log the error if needed - } + cmd.Wait() //nolint:errcheck shell.isAlive = false close(shell.commandQueue) }() @@ -134,10 +131,10 @@ func (s *PersistentShell) execCommand(command string, timeout time.Duration, ctx cwdFile := filepath.Join(tempDir, fmt.Sprintf("opencode-cwd-%d", time.Now().UnixNano())) defer func() { - os.Remove(stdoutFile) - os.Remove(stderrFile) - os.Remove(statusFile) - os.Remove(cwdFile) + os.Remove(stdoutFile) //nolint:errcheck + os.Remove(stderrFile) //nolint:errcheck + os.Remove(statusFile) //nolint:errcheck + os.Remove(cwdFile) //nolint:errcheck }() fullCommand := fmt.Sprintf(` @@ -204,7 +201,7 @@ echo $EXEC_EXIT_CODE > %s exitCode := 0 if exitCodeStr != "" { - fmt.Sscanf(exitCodeStr, "%d", &exitCode) + fmt.Sscanf(exitCodeStr, "%d", &exitCode) //nolint:errcheck } else if interrupted { exitCode = 143 stderr += "\nCommand execution timed out or was interrupted" @@ -236,11 +233,11 @@ func (s *PersistentShell) killChildren() { for pidStr := range strings.SplitSeq(string(output), "\n") { if pidStr = strings.TrimSpace(pidStr); pidStr != "" { var pid int - fmt.Sscanf(pidStr, "%d", &pid) + fmt.Sscanf(pidStr, "%d", &pid) //nolint:errcheck if pid > 0 { proc, err := os.FindProcess(pid) if err == nil { - proc.Signal(syscall.SIGTERM) + proc.Signal(syscall.SIGTERM) //nolint:errcheck } } } @@ -274,9 +271,9 @@ func (s *PersistentShell) Close() { return } - s.stdin.Write([]byte("exit\n")) + s.stdin.Write([]byte("exit\n")) //nolint:errcheck - s.cmd.Process.Kill() + s.cmd.Process.Kill() //nolint:errcheck s.isAlive = false } diff --git a/internal/llm/tools/sourcegraph.go b/internal/llm/tools/sourcegraph.go index 0d38c975..098a7b40 100644 --- a/internal/llm/tools/sourcegraph.go +++ b/internal/llm/tools/sourcegraph.go @@ -224,7 +224,7 @@ func (t *sourcegraphTool) Run(ctx context.Context, call ToolCall) (ToolResponse, if err != nil { return ToolResponse{}, fmt.Errorf("failed to fetch URL: %w", err) } - defer resp.Body.Close() + defer resp.Body.Close() //nolint:errcheck if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) diff --git a/internal/llm/tools/view.go b/internal/llm/tools/view.go index 6d800ce6..4d30c20b 100644 --- a/internal/llm/tools/view.go +++ b/internal/llm/tools/view.go @@ -224,7 +224,7 @@ func readTextFile(filePath string, offset, limit int) (string, int, error) { if err != nil { return "", 0, err } - defer file.Close() + defer file.Close() //nolint:errcheck lineCount := 0 diff --git a/internal/logging/logger.go b/internal/logging/logger.go index 7ae2e7b8..5cd0a993 100644 --- a/internal/logging/logger.go +++ b/internal/logging/logger.go @@ -60,12 +60,12 @@ func RecoverPanic(name string, cleanup func()) { if err != nil { ErrorPersist(fmt.Sprintf("Failed to create panic log: %v", err)) } else { - defer file.Close() + defer file.Close() //nolint:errcheck // Write panic information and stack trace - fmt.Fprintf(file, "Panic in %s: %v\n\n", name, r) - fmt.Fprintf(file, "Time: %s\n\n", time.Now().Format(time.RFC3339)) - fmt.Fprintf(file, "Stack Trace:\n%s\n", debug.Stack()) + fmt.Fprintf(file, "Panic in %s: %v\n\n", name, r) //nolint:errcheck + fmt.Fprintf(file, "Time: %s\n\n", time.Now().Format(time.RFC3339)) //nolint:errcheck + fmt.Fprintf(file, "Stack Trace:\n%s\n", debug.Stack()) //nolint:errcheck InfoPersist(fmt.Sprintf("Panic details written to %s", filename)) } diff --git a/internal/lsp/client.go b/internal/lsp/client.go index 5caf20e8..20d138e2 100644 --- a/internal/lsp/client.go +++ b/internal/lsp/client.go @@ -389,7 +389,7 @@ func (c *Client) openKeyConfigFiles(ctx context.Context) { filepath.Join(workDir, "package.json"), filepath.Join(workDir, "jsconfig.json"), } - + // Also find and open a few TypeScript files to help the server initialize c.openTypeScriptFiles(ctx, workDir) case ServerTypeGo: @@ -547,12 +547,12 @@ func (c *Client) openTypeScriptFiles(ctx context.Context, workDir string) { // shouldSkipDir returns true if the directory should be skipped during file search func shouldSkipDir(path string) bool { dirName := filepath.Base(path) - + // Skip hidden directories if strings.HasPrefix(dirName, ".") { return true } - + // Skip common directories that won't contain relevant source files skipDirs := map[string]bool{ "node_modules": true, @@ -562,7 +562,7 @@ func shouldSkipDir(path string) bool { "vendor": true, "target": true, } - + return skipDirs[dirName] } diff --git a/internal/lsp/watcher/watcher.go b/internal/lsp/watcher/watcher.go index 69d34121..40ab2788 100644 --- a/internal/lsp/watcher/watcher.go +++ b/internal/lsp/watcher/watcher.go @@ -309,11 +309,15 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str w.workspacePath = workspacePath // Store the watcher in the context for later use + //nolint:staticcheck + //lint:ignore SA1029 will be resolved in the future ctx = context.WithValue(ctx, "workspaceWatcher", w) // If the server name isn't already in the context, try to detect it if _, ok := ctx.Value("serverName").(string); !ok { serverName := getServerNameFromContext(ctx) + //nolint:staticcheck + //lint:ignore SA1029 will be resolved in the future ctx = context.WithValue(ctx, "serverName", serverName) } @@ -329,7 +333,7 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str if err != nil { logging.Error("Error creating watcher", "error", err) } - defer watcher.Close() + defer watcher.Close() //nolint:errcheck // Watch the workspace recursively err = filepath.WalkDir(workspacePath, func(path string, d os.DirEntry, err error) error { diff --git a/internal/tui/components/chat/editor.go b/internal/tui/components/chat/editor.go index 88ac3e75..4a90efa5 100644 --- a/internal/tui/components/chat/editor.go +++ b/internal/tui/components/chat/editor.go @@ -26,6 +26,7 @@ type EditorKeyMaps struct { OpenEditor key.Binding } +//lint:ignore U1000 will be used in the future type bluredEditorKeyMaps struct { Send key.Binding Focus key.Binding @@ -53,7 +54,7 @@ func openEditor() tea.Cmd { if err != nil { return util.ReportError(err) } - tmpfile.Close() + tmpfile.Close() //nolint:errcheck c := exec.Command(editor, tmpfile.Name()) //nolint:gosec c.Stdin = os.Stdin c.Stdout = os.Stdout @@ -69,7 +70,7 @@ func openEditor() tea.Cmd { if len(content) == 0 { return util.ReportWarn("Message is empty") } - os.Remove(tmpfile.Name()) + os.Remove(tmpfile.Name()) //nolint:errcheck return SendMsg{ Text: string(content), } diff --git a/internal/tui/components/chat/message.go b/internal/tui/components/chat/message.go index 53ec7ea3..f6983ba6 100644 --- a/internal/tui/components/chat/message.go +++ b/internal/tui/components/chat/message.go @@ -41,6 +41,7 @@ type uiMessage struct { content string } +//lint:ignore U1000 will be used in the future type renderCache struct { mutex sync.Mutex cache map[string][]uiMessage @@ -160,7 +161,10 @@ func renderAssistantMessage( position++ // for the space } else if thinking && thinkingContent != "" { // Render the thinking content + //nolint:ineffassign,staticcheck + //lint:ignore SA4006 will be resolved in the future content = renderMessage(thinkingContent, false, msg.ID == focusedUIMessageId, width) + } for i, toolCall := range msg.ToolCalls() { @@ -292,18 +296,10 @@ func renderParams(paramsWidth int, params ...string) string { func removeWorkingDirPrefix(path string) string { wd := config.WorkingDirectory() - if strings.HasPrefix(path, wd) { - path = strings.TrimPrefix(path, wd) - } - if strings.HasPrefix(path, "/") { - path = strings.TrimPrefix(path, "/") - } - if strings.HasPrefix(path, "./") { - path = strings.TrimPrefix(path, "./") - } - if strings.HasPrefix(path, "../") { - path = strings.TrimPrefix(path, "../") - } + path = strings.TrimPrefix(path, wd) + path = strings.TrimPrefix(path, "/") + path = strings.TrimPrefix(path, "./") + path = strings.TrimPrefix(path, "../") return path } @@ -312,22 +308,22 @@ func renderToolParams(paramWidth int, toolCall message.ToolCall) string { switch toolCall.Name { case agent.AgentToolName: var params agent.AgentParams - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck prompt := strings.ReplaceAll(params.Prompt, "\n", " ") return renderParams(paramWidth, prompt) case tools.BashToolName: var params tools.BashParams - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck command := strings.ReplaceAll(params.Command, "\n", " ") return renderParams(paramWidth, command) case tools.EditToolName: var params tools.EditParams - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck filePath := removeWorkingDirPrefix(params.FilePath) return renderParams(paramWidth, filePath) case tools.FetchToolName: var params tools.FetchParams - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck url := params.URL toolParams := []string{ url, @@ -341,7 +337,7 @@ func renderToolParams(paramWidth int, toolCall message.ToolCall) string { return renderParams(paramWidth, toolParams...) case tools.GlobToolName: var params tools.GlobParams - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck pattern := params.Pattern toolParams := []string{ pattern, @@ -352,7 +348,7 @@ func renderToolParams(paramWidth int, toolCall message.ToolCall) string { return renderParams(paramWidth, toolParams...) case tools.GrepToolName: var params tools.GrepParams - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck pattern := params.Pattern toolParams := []string{ pattern, @@ -369,7 +365,7 @@ func renderToolParams(paramWidth int, toolCall message.ToolCall) string { return renderParams(paramWidth, toolParams...) case tools.LSToolName: var params tools.LSParams - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck path := params.Path if path == "" { path = "." @@ -377,11 +373,11 @@ func renderToolParams(paramWidth int, toolCall message.ToolCall) string { return renderParams(paramWidth, path) case tools.SourcegraphToolName: var params tools.SourcegraphParams - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck return renderParams(paramWidth, params.Query) case tools.ViewToolName: var params tools.ViewParams - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck filePath := removeWorkingDirPrefix(params.FilePath) toolParams := []string{ filePath, @@ -395,7 +391,7 @@ func renderToolParams(paramWidth int, toolCall message.ToolCall) string { return renderParams(paramWidth, toolParams...) case tools.WriteToolName: var params tools.WriteParams - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck filePath := removeWorkingDirPrefix(params.FilePath) return renderParams(paramWidth, filePath) default: @@ -437,13 +433,13 @@ func renderToolResponse(toolCall message.ToolCall, response message.ToolResult, ) case tools.EditToolName: metadata := tools.EditResponseMetadata{} - json.Unmarshal([]byte(response.Metadata), &metadata) + json.Unmarshal([]byte(response.Metadata), &metadata) //nolint:errcheck truncDiff := truncateHeight(metadata.Diff, maxResultHeight) formattedDiff, _ := diff.FormatDiff(truncDiff, diff.WithTotalWidth(width), diff.WithStyle(diffStyle)) return formattedDiff case tools.FetchToolName: var params tools.FetchParams - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck mdFormat := "markdown" switch params.Format { case "text": @@ -466,7 +462,7 @@ func renderToolResponse(toolCall message.ToolCall, response message.ToolResult, return styles.BaseStyle.Width(width).Foreground(styles.ForgroundMid).Render(resultContent) case tools.ViewToolName: metadata := tools.ViewResponseMetadata{} - json.Unmarshal([]byte(response.Metadata), &metadata) + json.Unmarshal([]byte(response.Metadata), &metadata) //nolint:errcheck ext := filepath.Ext(metadata.FilePath) if ext == "" { ext = "" @@ -480,9 +476,9 @@ func renderToolResponse(toolCall message.ToolCall, response message.ToolResult, ) case tools.WriteToolName: params := tools.WriteParams{} - json.Unmarshal([]byte(toolCall.Input), ¶ms) + json.Unmarshal([]byte(toolCall.Input), ¶ms) //nolint:errcheck metadata := tools.WriteResponseMetadata{} - json.Unmarshal([]byte(response.Metadata), &metadata) + json.Unmarshal([]byte(response.Metadata), &metadata) //nolint:errcheck ext := filepath.Ext(params.FilePath) if ext == "" { ext = "" @@ -545,7 +541,7 @@ func renderToolMessage( progressText := styles.BaseStyle. Width(width - 2 - lipgloss.Width(toolName)). Foreground(styles.ForgroundDim). - Render(fmt.Sprintf("%s", toolAction)) + Render(toolAction) content := style.Render(lipgloss.JoinHorizontal(lipgloss.Left, toolName, progressText)) toolMsg := uiMessage{ diff --git a/internal/tui/components/chat/sidebar.go b/internal/tui/components/chat/sidebar.go index 5baac3cd..2384af42 100644 --- a/internal/tui/components/chat/sidebar.go +++ b/internal/tui/components/chat/sidebar.go @@ -144,7 +144,7 @@ func (m *sidebarCmp) modifiedFiles() string { modifiedFiles := styles.BaseStyle.Width(m.width).Foreground(styles.PrimaryColor).Bold(true).Render("Modified Files:") // If no modified files, show a placeholder message - if m.modFiles == nil || len(m.modFiles) == 0 { + if len(m.modFiles) == 0 { message := "No modified files" remainingWidth := m.width - lipgloss.Width(message) if remainingWidth > 0 { diff --git a/internal/tui/layout/overlay.go b/internal/tui/layout/overlay.go index 379747e6..405e219e 100644 --- a/internal/tui/layout/overlay.go +++ b/internal/tui/layout/overlay.go @@ -43,7 +43,7 @@ func PlaceOverlay( fgHeight := len(fgLines) if shadow { - var shadowbg string = "" + var shadowbg string shadowchar := lipgloss.NewStyle(). Background(styles.BackgroundDarker). Foreground(styles.Background). diff --git a/internal/tui/page/chat.go b/internal/tui/page/chat.go index e801d73c..aabfe6d7 100644 --- a/internal/tui/page/chat.go +++ b/internal/tui/page/chat.go @@ -115,7 +115,7 @@ func (p *chatPage) sendMessage(text string) tea.Cmd { cmds = append(cmds, util.CmdHandler(chat.SessionSelectedMsg(session))) } - p.app.CoderAgent.Run(context.Background(), p.session.ID, text) + p.app.CoderAgent.Run(context.Background(), p.session.ID, text) //nolint:errcheck return tea.Batch(cmds...) } diff --git a/internal/tui/styles/icons.go b/internal/tui/styles/icons.go index 96d1b897..045fe865 100644 --- a/internal/tui/styles/icons.go +++ b/internal/tui/styles/icons.go @@ -10,4 +10,4 @@ const ( HintIcon string = "i" SpinnerIcon string = "..." LoadingIcon string = "⟳" -) \ No newline at end of file +)