# frozen_string_literal: true module DiscourseAi module AiBot module Tools class GithubFileContent < Tool def self.signature { name: name, description: "Retrieves the content of specified GitHub files", parameters: [ { name: "repo_name", description: "The name of the GitHub repository (e.g., 'discourse/discourse')", type: "string", required: true, }, { name: "file_paths", description: "The paths of the files to retrieve within the repository", type: "array", item_type: "string", required: true, }, { name: "branch", description: "The branch or commit SHA to retrieve the files from (default: 'main')", type: "string", required: false, }, ], } end def self.name "github_file_content" end def repo_name parameters[:repo_name] end def file_paths parameters[:file_paths] end def branch parameters[:branch] || "main" end def description_args { repo_name: repo_name, file_paths: file_paths.join(", "), branch: branch } end def invoke owner, repo = repo_name.split("/") file_contents = {} missing_files = [] file_paths.each do |file_path| api_url = "https://api.github.com/repos/#{owner}/#{repo}/contents/#{file_path}?ref=#{branch}" response_code = "-1 unknown" body = nil send_http_request( api_url, headers: { "Accept" => "application/vnd.github.v3+json", }, authenticate_github: true, ) do |response| response_code = response.code body = read_response_body(response) end if response_code == "200" file_data = JSON.parse(body) content = Base64.decode64(file_data["content"]) file_contents[file_path] = content else missing_files << file_path end end result = {} unless file_contents.empty? blob = file_contents.map { |path, content| "File Path: #{path}:\n#{content}" }.join("\n") truncated_blob = truncate(blob, max_length: 20_000, percent_length: 0.3, llm: llm) result[:file_contents] = truncated_blob end result[:missing_files] = missing_files unless missing_files.empty? result.empty? ? { error: "No files found or retrieved." } : result end end end end end