Просмотр исходного кода

chore: remove sourcegraph tool

adamdottv 9 месяцев назад
Родитель
Сommit
4c998d4f4f
5 измененных файлов с 8 добавлено и 412 удалено
  1. 5 6
      README.md
  2. 0 6
      go.sum
  3. 3 7
      internal/llm/agent/tools.go
  4. 0 383
      internal/llm/tools/sourcegraph.go
  5. 0 10
      internal/tui/components/chat/message.go

+ 5 - 6
README.md

@@ -295,12 +295,11 @@ OpenCode's AI assistant has access to various tools to help with coding tasks:
 
 ### Other Tools
 
-| Tool          | Description                            | Parameters                                                                                |
-| ------------- | -------------------------------------- | ----------------------------------------------------------------------------------------- |
-| `bash`        | Execute shell commands                 | `command` (required), `timeout` (optional)                                                |
-| `fetch`       | Fetch data from URLs                   | `url` (required), `format` (required), `timeout` (optional)                               |
-| `sourcegraph` | Search code across public repositories | `query` (required), `count` (optional), `context_window` (optional), `timeout` (optional) |
-| `agent`       | Run sub-tasks with the AI agent        | `prompt` (required)                                                                       |
+| Tool    | Description                     | Parameters                                                  |
+| ------- | ------------------------------- | ----------------------------------------------------------- |
+| `bash`  | Execute shell commands          | `command` (required), `timeout` (optional)                  |
+| `fetch` | Fetch data from URLs            | `url` (required), `format` (required), `timeout` (optional) |
+| `agent` | Run sub-tasks with the AI agent | `prompt` (required)                                         |
 
 ## Theming
 

+ 0 - 6
go.sum

@@ -2,8 +2,6 @@ cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
 cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
 cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
 cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
-cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
-cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
 cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
 cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
@@ -289,8 +287,6 @@ golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
 golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
 golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
-golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
-golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -336,8 +332,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/api v0.215.0 h1:jdYF4qnyczlEz2ReWIsosNLDuzXyvFHJtI5gcr0J7t0=
-google.golang.org/api v0.215.0/go.mod h1:fta3CVtuJYOEdugLNWm6WodzOS8KdFckABwN4I40hzY=
 google.golang.org/genai v1.3.0 h1:tXhPJF30skOjnnDY7ZnjK3q7IKy4PuAlEA0fk7uEaEI=
 google.golang.org/genai v1.3.0/go.mod h1:TyfOKRz/QyCaj6f/ZDt505x+YreXnY40l2I6k8TvgqY=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=

+ 3 - 7
internal/llm/agent/tools.go

@@ -19,11 +19,8 @@ func PrimaryAgentTools(
 	lspClients map[string]*lsp.Client,
 ) []tools.BaseTool {
 	ctx := context.Background()
-	otherTools := GetMcpTools(ctx, permissions)
 
-	// Always add the Diagnostics tool even if lspClients is empty
-	// The tool will handle the case when no clients are available
-	otherTools = append(otherTools, tools.NewDiagnosticsTool(lspClients))
+	mcpTools := GetMcpTools(ctx, permissions)
 
 	return append(
 		[]tools.BaseTool{
@@ -33,12 +30,12 @@ func PrimaryAgentTools(
 			tools.NewGlobTool(),
 			tools.NewGrepTool(),
 			tools.NewLsTool(),
-			// tools.NewSourcegraphTool(),
 			tools.NewViewTool(lspClients),
 			tools.NewPatchTool(lspClients, permissions, history),
 			tools.NewWriteTool(lspClients, permissions, history),
+			tools.NewDiagnosticsTool(lspClients),
 			NewAgentTool(sessions, messages, lspClients),
-		}, otherTools...,
+		}, mcpTools...,
 	)
 }
 
@@ -47,7 +44,6 @@ func TaskAgentTools(lspClients map[string]*lsp.Client) []tools.BaseTool {
 		tools.NewGlobTool(),
 		tools.NewGrepTool(),
 		tools.NewLsTool(),
-		tools.NewSourcegraphTool(),
 		tools.NewViewTool(lspClients),
 	}
 }

+ 0 - 383
internal/llm/tools/sourcegraph.go

@@ -1,383 +0,0 @@
-package tools
-
-import (
-	"bytes"
-	"context"
-	"encoding/json"
-	"fmt"
-	"io"
-	"net/http"
-	"strings"
-	"time"
-)
-
-type SourcegraphParams struct {
-	Query         string `json:"query"`
-	Count         int    `json:"count,omitempty"`
-	ContextWindow int    `json:"context_window,omitempty"`
-	Timeout       int    `json:"timeout,omitempty"`
-}
-
-type SourcegraphResponseMetadata struct {
-	NumberOfMatches int  `json:"number_of_matches"`
-	Truncated       bool `json:"truncated"`
-}
-
-type sourcegraphTool struct {
-	client *http.Client
-}
-
-const (
-	SourcegraphToolName        = "sourcegraph"
-	sourcegraphToolDescription = `Search code across public repositories using Sourcegraph's GraphQL API.
-
-WHEN TO USE THIS TOOL:
-- Use when you need to find code examples or implementations across public repositories
-- Helpful for researching how others have solved similar problems
-- Useful for discovering patterns and best practices in open source code
-
-HOW TO USE:
-- Provide a search query using Sourcegraph's query syntax
-- Optionally specify the number of results to return (default: 10)
-- Optionally set a timeout for the request
-
-QUERY SYNTAX:
-- Basic search: "fmt.Println" searches for exact matches
-- File filters: "file:.go fmt.Println" limits to Go files
-- Repository filters: "repo:^github\.com/golang/go$ fmt.Println" limits to specific repos
-- Language filters: "lang:go fmt.Println" limits to Go code
-- Boolean operators: "fmt.Println AND log.Fatal" for combined terms
-- Regular expressions: "fmt\.(Print|Printf|Println)" for pattern matching
-- Quoted strings: "\"exact phrase\"" for exact phrase matching
-- Exclude filters: "-file:test" or "-repo:forks" to exclude matches
-
-ADVANCED FILTERS:
-- Repository filters:
-  * "repo:name" - Match repositories with name containing "name"
-  * "repo:^github\.com/org/repo$" - Exact repository match
-  * "repo:org/repo@branch" - Search specific branch
-  * "repo:org/repo rev:branch" - Alternative branch syntax
-  * "-repo:name" - Exclude repositories
-  * "fork:yes" or "fork:only" - Include or only show forks
-  * "archived:yes" or "archived:only" - Include or only show archived repos
-  * "visibility:public" or "visibility:private" - Filter by visibility
-
-- File filters:
-  * "file:\.js$" - Files with .js extension
-  * "file:internal/" - Files in internal directory
-  * "-file:test" - Exclude test files
-  * "file:has.content(Copyright)" - Files containing "Copyright"
-  * "file:has.contributor([email protected])" - Files with specific contributor
-
-- Content filters:
-  * "content:\"exact string\"" - Search for exact string
-  * "-content:\"unwanted\"" - Exclude files with unwanted content
-  * "case:yes" - Case-sensitive search
-
-- Type filters:
-  * "type:symbol" - Search for symbols (functions, classes, etc.)
-  * "type:file" - Search file content only
-  * "type:path" - Search filenames only
-  * "type:diff" - Search code changes
-  * "type:commit" - Search commit messages
-
-- Commit/diff search:
-  * "after:\"1 month ago\"" - Commits after date
-  * "before:\"2023-01-01\"" - Commits before date
-  * "author:name" - Commits by author
-  * "message:\"fix bug\"" - Commits with message
-
-- Result selection:
-  * "select:repo" - Show only repository names
-  * "select:file" - Show only file paths
-  * "select:content" - Show only matching content
-  * "select:symbol" - Show only matching symbols
-
-- Result control:
-  * "count:100" - Return up to 100 results
-  * "count:all" - Return all results
-  * "timeout:30s" - Set search timeout
-
-EXAMPLES:
-- "file:.go context.WithTimeout" - Find Go code using context.WithTimeout
-- "lang:typescript useState type:symbol" - Find TypeScript React useState hooks
-- "repo:^github\.com/kubernetes/kubernetes$ pod list type:file" - Find Kubernetes files related to pod listing
-- "repo:sourcegraph/sourcegraph$ after:\"3 months ago\" type:diff database" - Recent changes to database code
-- "file:Dockerfile (alpine OR ubuntu) -content:alpine:latest" - Dockerfiles with specific base images
-- "repo:has.path(\.py) file:requirements.txt tensorflow" - Python projects using TensorFlow
-
-BOOLEAN OPERATORS:
-- "term1 AND term2" - Results containing both terms
-- "term1 OR term2" - Results containing either term
-- "term1 NOT term2" - Results with term1 but not term2
-- "term1 and (term2 or term3)" - Grouping with parentheses
-
-LIMITATIONS:
-- Only searches public repositories
-- Rate limits may apply
-- Complex queries may take longer to execute
-- Maximum of 20 results per query
-
-TIPS:
-- Use specific file extensions to narrow results
-- Add repo: filters for more targeted searches
-- Use type:symbol to find function/method definitions
-- Use type:file to find relevant files`
-)
-
-func NewSourcegraphTool() BaseTool {
-	return &sourcegraphTool{
-		client: &http.Client{
-			Timeout: 30 * time.Second,
-		},
-	}
-}
-
-func (t *sourcegraphTool) Info() ToolInfo {
-	return ToolInfo{
-		Name:        SourcegraphToolName,
-		Description: sourcegraphToolDescription,
-		Parameters: map[string]any{
-			"query": map[string]any{
-				"type":        "string",
-				"description": "The Sourcegraph search query",
-			},
-			"count": map[string]any{
-				"type":        "number",
-				"description": "Optional number of results to return (default: 10, max: 20)",
-			},
-			"context_window": map[string]any{
-				"type":        "number",
-				"description": "The context around the match to return (default: 10 lines)",
-			},
-			"timeout": map[string]any{
-				"type":        "number",
-				"description": "Optional timeout in seconds (max 120)",
-			},
-		},
-		Required: []string{"query"},
-	}
-}
-
-func (t *sourcegraphTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error) {
-	var params SourcegraphParams
-	if err := json.Unmarshal([]byte(call.Input), &params); err != nil {
-		return NewTextErrorResponse("Failed to parse sourcegraph parameters: " + err.Error()), nil
-	}
-
-	if params.Query == "" {
-		return NewTextErrorResponse("Query parameter is required"), nil
-	}
-
-	if params.Count <= 0 {
-		params.Count = 10
-	} else if params.Count > 20 {
-		params.Count = 20 // Limit to 20 results
-	}
-
-	if params.ContextWindow <= 0 {
-		params.ContextWindow = 10 // Default context window
-	}
-	client := t.client
-	if params.Timeout > 0 {
-		maxTimeout := 120 // 2 minutes
-		if params.Timeout > maxTimeout {
-			params.Timeout = maxTimeout
-		}
-		client = &http.Client{
-			Timeout: time.Duration(params.Timeout) * time.Second,
-		}
-	}
-
-	type graphqlRequest struct {
-		Query     string `json:"query"`
-		Variables struct {
-			Query string `json:"query"`
-		} `json:"variables"`
-	}
-
-	request := graphqlRequest{
-		Query: "query Search($query: String!) { search(query: $query, version: V2, patternType: keyword ) { results { matchCount, limitHit, resultCount, approximateResultCount, missing { name }, timedout { name }, indexUnavailable, results { __typename, ... on FileMatch { repository { name }, file { path, url, content }, lineMatches { preview, lineNumber, offsetAndLengths } } } } } }",
-	}
-	request.Variables.Query = params.Query
-
-	graphqlQueryBytes, err := json.Marshal(request)
-	if err != nil {
-		return ToolResponse{}, fmt.Errorf("failed to marshal GraphQL request: %w", err)
-	}
-	graphqlQuery := string(graphqlQueryBytes)
-
-	req, err := http.NewRequestWithContext(
-		ctx,
-		"POST",
-		"https://sourcegraph.com/.api/graphql",
-		bytes.NewBuffer([]byte(graphqlQuery)),
-	)
-	if err != nil {
-		return ToolResponse{}, fmt.Errorf("failed to create request: %w", err)
-	}
-
-	req.Header.Set("Content-Type", "application/json")
-	req.Header.Set("User-Agent", "opencode/1.0")
-
-	resp, err := client.Do(req)
-	if err != nil {
-		return ToolResponse{}, fmt.Errorf("failed to fetch URL: %w", err)
-	}
-	defer resp.Body.Close()
-
-	if resp.StatusCode != http.StatusOK {
-		body, _ := io.ReadAll(resp.Body)
-		if len(body) > 0 {
-			return NewTextErrorResponse(fmt.Sprintf("Request failed with status code: %d, response: %s", resp.StatusCode, string(body))), nil
-		}
-
-		return NewTextErrorResponse(fmt.Sprintf("Request failed with status code: %d", resp.StatusCode)), nil
-	}
-	body, err := io.ReadAll(resp.Body)
-	if err != nil {
-		return ToolResponse{}, fmt.Errorf("failed to read response body: %w", err)
-	}
-
-	var result map[string]any
-	if err = json.Unmarshal(body, &result); err != nil {
-		return ToolResponse{}, fmt.Errorf("failed to unmarshal response: %w", err)
-	}
-
-	formattedResults, err := formatSourcegraphResults(result, params.ContextWindow)
-	if err != nil {
-		return NewTextErrorResponse("Failed to format results: " + err.Error()), nil
-	}
-
-	return NewTextResponse(formattedResults), nil
-}
-
-func formatSourcegraphResults(result map[string]any, contextWindow int) (string, error) {
-	var buffer strings.Builder
-
-	if errors, ok := result["errors"].([]any); ok && len(errors) > 0 {
-		buffer.WriteString("## Sourcegraph API Error\n\n")
-		for _, err := range errors {
-			if errMap, ok := err.(map[string]any); ok {
-				if message, ok := errMap["message"].(string); ok {
-					buffer.WriteString(fmt.Sprintf("- %s\n", message))
-				}
-			}
-		}
-		return buffer.String(), nil
-	}
-
-	data, ok := result["data"].(map[string]any)
-	if !ok {
-		return "", fmt.Errorf("invalid response format: missing data field")
-	}
-
-	search, ok := data["search"].(map[string]any)
-	if !ok {
-		return "", fmt.Errorf("invalid response format: missing search field")
-	}
-
-	searchResults, ok := search["results"].(map[string]any)
-	if !ok {
-		return "", fmt.Errorf("invalid response format: missing results field")
-	}
-
-	matchCount, _ := searchResults["matchCount"].(float64)
-	resultCount, _ := searchResults["resultCount"].(float64)
-	limitHit, _ := searchResults["limitHit"].(bool)
-
-	buffer.WriteString("# Sourcegraph Search Results\n\n")
-	buffer.WriteString(fmt.Sprintf("Found %d matches across %d results\n", int(matchCount), int(resultCount)))
-
-	if limitHit {
-		buffer.WriteString("(Result limit reached, try a more specific query)\n")
-	}
-
-	buffer.WriteString("\n")
-
-	results, ok := searchResults["results"].([]any)
-	if !ok || len(results) == 0 {
-		buffer.WriteString("No results found. Try a different query.\n")
-		return buffer.String(), nil
-	}
-
-	maxResults := 10
-	if len(results) > maxResults {
-		results = results[:maxResults]
-	}
-
-	for i, res := range results {
-		fileMatch, ok := res.(map[string]any)
-		if !ok {
-			continue
-		}
-
-		typeName, _ := fileMatch["__typename"].(string)
-		if typeName != "FileMatch" {
-			continue
-		}
-
-		repo, _ := fileMatch["repository"].(map[string]any)
-		file, _ := fileMatch["file"].(map[string]any)
-		lineMatches, _ := fileMatch["lineMatches"].([]any)
-
-		if repo == nil || file == nil {
-			continue
-		}
-
-		repoName, _ := repo["name"].(string)
-		filePath, _ := file["path"].(string)
-		fileURL, _ := file["url"].(string)
-		fileContent, _ := file["content"].(string)
-
-		buffer.WriteString(fmt.Sprintf("## Result %d: %s/%s\n\n", i+1, repoName, filePath))
-
-		if fileURL != "" {
-			buffer.WriteString(fmt.Sprintf("URL: %s\n\n", fileURL))
-		}
-
-		if len(lineMatches) > 0 {
-			for _, lm := range lineMatches {
-				lineMatch, ok := lm.(map[string]any)
-				if !ok {
-					continue
-				}
-
-				lineNumber, _ := lineMatch["lineNumber"].(float64)
-				preview, _ := lineMatch["preview"].(string)
-
-				if fileContent != "" {
-					lines := strings.Split(fileContent, "\n")
-
-					buffer.WriteString("```\n")
-
-					startLine := max(1, int(lineNumber)-contextWindow)
-
-					for j := startLine - 1; j < int(lineNumber)-1 && j < len(lines); j++ {
-						if j >= 0 {
-							buffer.WriteString(fmt.Sprintf("%d| %s\n", j+1, lines[j]))
-						}
-					}
-
-					buffer.WriteString(fmt.Sprintf("%d|  %s\n", int(lineNumber), preview))
-
-					endLine := int(lineNumber) + contextWindow
-
-					for j := int(lineNumber); j < endLine && j < len(lines); j++ {
-						if j < len(lines) {
-							buffer.WriteString(fmt.Sprintf("%d| %s\n", j+1, lines[j]))
-						}
-					}
-
-					buffer.WriteString("```\n\n")
-				} else {
-					buffer.WriteString("```\n")
-					buffer.WriteString(fmt.Sprintf("%d| %s\n", int(lineNumber), preview))
-					buffer.WriteString("```\n\n")
-				}
-			}
-		}
-	}
-
-	return buffer.String(), nil
-}

+ 0 - 10
internal/tui/components/chat/message.go

@@ -260,8 +260,6 @@ func toolName(name string) string {
 		return "Grep"
 	case tools.LSToolName:
 		return "List"
-	case tools.SourcegraphToolName:
-		return "Sourcegraph"
 	case tools.ViewToolName:
 		return "View"
 	case tools.WriteToolName:
@@ -288,8 +286,6 @@ func getToolAction(name string) string {
 		return "Searching content..."
 	case tools.LSToolName:
 		return "Listing directory..."
-	case tools.SourcegraphToolName:
-		return "Searching code..."
 	case tools.ViewToolName:
 		return "Reading file..."
 	case tools.WriteToolName:
@@ -428,10 +424,6 @@ func renderToolParams(paramWidth int, toolCall message.ToolCall) string {
 			path = "."
 		}
 		return renderParams(paramWidth, path)
-	case tools.SourcegraphToolName:
-		var params tools.SourcegraphParams
-		json.Unmarshal([]byte(toolCall.Input), &params)
-		return renderParams(paramWidth, params.Query)
 	case tools.ViewToolName:
 		var params tools.ViewParams
 		json.Unmarshal([]byte(toolCall.Input), &params)
@@ -518,8 +510,6 @@ func renderToolResponse(toolCall message.ToolCall, response message.ToolResult,
 		return baseStyle.Width(width).Foreground(t.TextMuted()).Render(resultContent)
 	case tools.LSToolName:
 		return baseStyle.Width(width).Foreground(t.TextMuted()).Render(resultContent)
-	case tools.SourcegraphToolName:
-		return baseStyle.Width(width).Foreground(t.TextMuted()).Render(resultContent)
 	case tools.ViewToolName:
 		metadata := tools.ViewResponseMetadata{}
 		json.Unmarshal([]byte(response.Metadata), &metadata)