🌐 AI搜索 & 代理 主页
Skip to content

Commit ba2d7ce

Browse files
committed
Fix logical path when file or directory not found
1 parent 3f52d21 commit ba2d7ce

File tree

1 file changed

+89
-96
lines changed

1 file changed

+89
-96
lines changed

pkg/github/repositories.go

Lines changed: 89 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -623,128 +623,121 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t
623623
if respContents != nil {
624624
defer func() { _ = respContents.Body.Close() }()
625625
}
626-
if err != nil {
627-
return ghErrors.NewGitHubAPIErrorResponse(ctx,
628-
"failed to get file SHA",
629-
respContents,
630-
err,
631-
), nil, nil
632-
}
633626

634-
// file content or file SHA is nil which means it's a directory
635-
if fileContent == nil || fileContent.SHA == nil {
636-
defer func() { _ = respContents.Body.Close() }()
637-
r, err := json.Marshal(dirContent)
627+
// The path does not point to a file or directory.
628+
// Instead let's try to find it in the Git Tree by matching the end of the path.
629+
if err != nil || (fileContent == nil && dirContent == nil) {
630+
// Step 1: Get Git Tree recursively
631+
tree, response, err := client.Git.GetTree(ctx, owner, repo, ref, true)
638632
if err != nil {
639-
return utils.NewToolResultError("failed to marshal response"), nil, nil
633+
return ghErrors.NewGitHubAPIErrorResponse(ctx,
634+
"failed to get git tree",
635+
response,
636+
err,
637+
), nil, nil
640638
}
641-
return utils.NewToolResultText(string(r)), nil, nil
642-
}
639+
defer func() { _ = response.Body.Close() }()
643640

644-
fileSHA = *fileContent.SHA
645-
646-
rawClient, err := getRawClient(ctx)
647-
if err != nil {
648-
return utils.NewToolResultError("failed to get GitHub raw content client"), nil, nil
649-
}
650-
resp, err := rawClient.GetRawContent(ctx, owner, repo, path, rawOpts)
651-
if err != nil {
652-
return utils.NewToolResultError("failed to get raw repository content"), nil, nil
641+
// Step 2: Filter tree for matching paths
642+
const maxMatchingFiles = 3
643+
matchingFiles := filterPaths(tree.Entries, path, maxMatchingFiles)
644+
if len(matchingFiles) > 0 {
645+
matchingFilesJSON, err := json.Marshal(matchingFiles)
646+
if err != nil {
647+
return utils.NewToolResultError(fmt.Sprintf("failed to marshal matching files: %s", err)), nil, nil
648+
}
649+
resolvedRefs, err := json.Marshal(rawOpts)
650+
if err != nil {
651+
return utils.NewToolResultError(fmt.Sprintf("failed to marshal resolved refs: %s", err)), nil, nil
652+
}
653+
return utils.NewToolResultError(fmt.Sprintf("Resolved potential matches in the repository tree (resolved refs: %s, matching files: %s), but the raw content API returned an unexpected status code %d.", string(resolvedRefs), string(matchingFilesJSON), rawAPIResponseCode)), nil, nil
654+
}
655+
return utils.NewToolResultError("Failed to get file contents. The path does not point to a file or directory, or the file does not exist in the repository."), nil, nil
653656
}
654-
defer func() {
655-
_ = resp.Body.Close()
656-
}()
657657

658-
if resp.StatusCode == http.StatusOK {
659-
// If the raw content is found, return it directly
660-
body, err := io.ReadAll(resp.Body)
658+
if fileContent != nil && fileContent.SHA != nil {
659+
fileSHA = *fileContent.SHA
660+
661+
rawClient, err := getRawClient(ctx)
661662
if err != nil {
662-
return utils.NewToolResultError("failed to read response body"), nil, nil
663+
return utils.NewToolResultError("failed to get GitHub raw content client"), nil, nil
663664
}
664-
contentType := resp.Header.Get("Content-Type")
665+
resp, err := rawClient.GetRawContent(ctx, owner, repo, path, rawOpts)
666+
if err != nil {
667+
return utils.NewToolResultError("failed to get raw repository content"), nil, nil
668+
}
669+
defer func() {
670+
_ = resp.Body.Close()
671+
}()
665672

666-
var resourceURI string
667-
switch {
668-
case sha != "":
669-
resourceURI, err = url.JoinPath("repo://", owner, repo, "sha", sha, "contents", path)
673+
if resp.StatusCode == http.StatusOK {
674+
// If the raw content is found, return it directly
675+
body, err := io.ReadAll(resp.Body)
670676
if err != nil {
671-
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
677+
return utils.NewToolResultError("failed to read response body"), nil, nil
672678
}
673-
case ref != "":
674-
resourceURI, err = url.JoinPath("repo://", owner, repo, ref, "contents", path)
675-
if err != nil {
676-
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
677-
}
678-
default:
679-
resourceURI, err = url.JoinPath("repo://", owner, repo, "contents", path)
680-
if err != nil {
681-
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
679+
contentType := resp.Header.Get("Content-Type")
680+
681+
var resourceURI string
682+
switch {
683+
case sha != "":
684+
resourceURI, err = url.JoinPath("repo://", owner, repo, "sha", sha, "contents", path)
685+
if err != nil {
686+
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
687+
}
688+
case ref != "":
689+
resourceURI, err = url.JoinPath("repo://", owner, repo, ref, "contents", path)
690+
if err != nil {
691+
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
692+
}
693+
default:
694+
resourceURI, err = url.JoinPath("repo://", owner, repo, "contents", path)
695+
if err != nil {
696+
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
697+
}
682698
}
683-
}
684699

685-
// Determine if content is text or binary
686-
isTextContent := strings.HasPrefix(contentType, "text/") ||
687-
contentType == "application/json" ||
688-
contentType == "application/xml" ||
689-
strings.HasSuffix(contentType, "+json") ||
690-
strings.HasSuffix(contentType, "+xml")
700+
// Determine if content is text or binary
701+
isTextContent := strings.HasPrefix(contentType, "text/") ||
702+
contentType == "application/json" ||
703+
contentType == "application/xml" ||
704+
strings.HasSuffix(contentType, "+json") ||
705+
strings.HasSuffix(contentType, "+xml")
706+
707+
if isTextContent {
708+
result := &mcp.ResourceContents{
709+
URI: resourceURI,
710+
Text: string(body),
711+
MIMEType: contentType,
712+
}
713+
// Include SHA in the result metadata
714+
if fileSHA != "" {
715+
return utils.NewToolResultResource(fmt.Sprintf("successfully downloaded text file (SHA: %s)", fileSHA), result), nil, nil
716+
}
717+
return utils.NewToolResultResource("successfully downloaded text file", result), nil, nil
718+
}
691719

692-
if isTextContent {
693720
result := &mcp.ResourceContents{
694721
URI: resourceURI,
695-
Text: string(body),
722+
Blob: body,
696723
MIMEType: contentType,
697724
}
698725
// Include SHA in the result metadata
699726
if fileSHA != "" {
700-
return utils.NewToolResultResource(fmt.Sprintf("successfully downloaded text file (SHA: %s)", fileSHA), result), nil, nil
727+
return utils.NewToolResultResource(fmt.Sprintf("successfully downloaded binary file (SHA: %s)", fileSHA), result), nil, nil
701728
}
702-
return utils.NewToolResultResource("successfully downloaded text file", result), nil, nil
703-
}
704-
705-
result := &mcp.ResourceContents{
706-
URI: resourceURI,
707-
Blob: body,
708-
MIMEType: contentType,
709-
}
710-
// Include SHA in the result metadata
711-
if fileSHA != "" {
712-
return utils.NewToolResultResource(fmt.Sprintf("successfully downloaded binary file (SHA: %s)", fileSHA), result), nil, nil
713-
}
714-
return utils.NewToolResultResource("successfully downloaded binary file", result), nil, nil
715-
}
716-
rawAPIResponseCode = resp.StatusCode
717-
718-
// The path does not point to a file or directory.
719-
// Instead let's try to find it in the Git Tree by matching the end of the path.
720-
721-
// Step 1: Get Git Tree recursively
722-
tree, response, err := client.Git.GetTree(ctx, owner, repo, ref, true)
723-
if err != nil {
724-
return ghErrors.NewGitHubAPIErrorResponse(ctx,
725-
"failed to get git tree",
726-
response,
727-
err,
728-
), nil, nil
729-
}
730-
defer func() { _ = response.Body.Close() }()
731-
732-
// Step 2: Filter tree for matching paths
733-
const maxMatchingFiles = 3
734-
matchingFiles := filterPaths(tree.Entries, path, maxMatchingFiles)
735-
if len(matchingFiles) > 0 {
736-
matchingFilesJSON, err := json.Marshal(matchingFiles)
737-
if err != nil {
738-
return utils.NewToolResultError(fmt.Sprintf("failed to marshal matching files: %s", err)), nil, nil
729+
return utils.NewToolResultResource("successfully downloaded binary file", result), nil, nil
739730
}
740-
resolvedRefs, err := json.Marshal(rawOpts)
731+
} else if dirContent != nil {
732+
// file content or file SHA is nil which means it's a directory
733+
r, err := json.Marshal(dirContent)
741734
if err != nil {
742-
return utils.NewToolResultError(fmt.Sprintf("failed to marshal resolved refs: %s", err)), nil, nil
735+
return utils.NewToolResultError("failed to marshal response"), nil, nil
743736
}
744-
return utils.NewToolResultError(fmt.Sprintf("Resolved potential matches in the repository tree (resolved refs: %s, matching files: %s), but the raw content API returned an unexpected status code %d.", string(resolvedRefs), string(matchingFilesJSON), rawAPIResponseCode)), nil, nil
737+
return utils.NewToolResultText(string(r)), nil, nil
745738
}
746739

747-
return utils.NewToolResultError("Failed to get file contents. The path does not point to a file or directory, or the file does not exist in the repository."), nil, nil
740+
return utils.NewToolResultError("failed to get file contents"), nil, nil
748741
})
749742

750743
return tool, handler

0 commit comments

Comments
 (0)