import type { GetTreeParams, TreeResponse } from '@/tools/github/types' import type { ToolConfig } from '@/tools/types' export const getTreeTool: ToolConfig = { id: 'github_get_tree', name: 'GitHub Get Repository Tree', description: 'Get the contents of a directory in a GitHub repository. Returns a list of files and subdirectories. Use empty path or omit to get root directory contents.', version: '1.0.0', params: { owner: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Repository owner (user or organization)', }, repo: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Repository name', }, path: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Directory path (e.g., "src/components"). Leave empty for root directory.', default: '', }, ref: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Branch name, tag, or commit SHA (defaults to repository default branch)', }, apiKey: { type: 'string', required: true, visibility: 'user-only', description: 'GitHub Personal Access Token', }, }, request: { url: (params) => { const path = params.path || '' const baseUrl = `https://api.github.com/repos/${params.owner}/${params.repo}/contents/${path}` return params.ref ? `${baseUrl}?ref=${params.ref}` : baseUrl }, method: 'GET', headers: (params) => ({ Accept: 'application/vnd.github+json', Authorization: `Bearer ${params.apiKey}`, 'X-GitHub-Api-Version': '2022-11-28', }), }, transformResponse: async (response) => { const data = await response.json() if (!Array.isArray(data)) { return { success: false, error: 'Path points to a file. Use github_get_file_content to get file contents.', output: { content: '', metadata: { path: '', items: [], total_count: 0, }, }, } } const items = data.map((item: any) => ({ name: item.name, path: item.path, sha: item.sha, size: item.size, type: item.type, download_url: item.download_url, html_url: item.html_url, })) const files = items.filter((item) => item.type === 'file') const dirs = items.filter((item) => item.type === 'dir') const other = items.filter((item) => item.type !== 'file' && item.type !== 'dir') let content = `Repository Tree: ${data[0]?.path ? data[0].path.split('/').slice(0, -1).join('/') || '/' : '/'} Total items: ${items.length} ` if (dirs.length > 0) { content += `Directories (${dirs.length}):\n` dirs.forEach((dir) => { content += ` - ${dir.name}/\n` }) content += '\n' } if (files.length > 0) { content += `Files (${files.length}):\n` files.forEach((file) => { const sizeKB = (file.size / 1024).toFixed(2) content += ` - ${file.name} (${sizeKB} KB)\n` }) content += '\n' } if (other.length > 0) { content += `Other (${other.length}):\n` other.forEach((item) => { content += ` - ${item.name} [${item.type}]\n` }) } return { success: true, output: { content, metadata: { path: data[0]?.path?.split('/').slice(0, -1).join('/') || '/', items, total_count: items.length, }, }, } }, outputs: { content: { type: 'string', description: 'Human-readable directory tree listing', }, metadata: { type: 'object', description: 'Directory contents metadata', properties: { path: { type: 'string', description: 'Directory path' }, items: { type: 'array', description: 'Array of files and directories', items: { type: 'object', properties: { name: { type: 'string', description: 'File or directory name' }, path: { type: 'string', description: 'Full path in repository' }, sha: { type: 'string', description: 'Git object SHA' }, size: { type: 'number', description: 'Size in bytes' }, type: { type: 'string', description: 'Type (file, dir, symlink, submodule)' }, download_url: { type: 'string', description: 'Direct download URL (files only)' }, html_url: { type: 'string', description: 'GitHub web UI URL' }, }, }, }, total_count: { type: 'number', description: 'Total number of items' }, }, }, }, } export const getTreeV2Tool: ToolConfig = { id: 'github_get_tree_v2', name: getTreeTool.name, description: getTreeTool.description, version: '2.0.0', params: getTreeTool.params, request: getTreeTool.request, transformResponse: async (response: Response) => { const data = await response.json() // API returns array for directory, object for file if (!Array.isArray(data)) { return { success: false, output: { items: [], count: 0 }, error: 'Path points to a file. Use github_get_file_content to get file contents.', } } return { success: true, output: { items: data.map((item: any) => ({ name: item.name, path: item.path, sha: item.sha, size: item.size, type: item.type, html_url: item.html_url, download_url: item.download_url ?? null, git_url: item.git_url, url: item.url, _links: item._links, })), count: data.length, }, } }, outputs: { items: { type: 'array', description: 'Array of file/directory objects', items: { type: 'object', properties: { name: { type: 'string', description: 'File or directory name' }, path: { type: 'string', description: 'Full path in repository' }, sha: { type: 'string', description: 'Git object SHA' }, size: { type: 'number', description: 'Size in bytes' }, type: { type: 'string', description: 'Type (file/dir/symlink/submodule)' }, html_url: { type: 'string', description: 'GitHub web URL' }, download_url: { type: 'string', description: 'Direct download URL', optional: true }, git_url: { type: 'string', description: 'Git blob API URL' }, url: { type: 'string', description: 'API URL for this item' }, _links: { type: 'json', description: 'Related links' }, }, }, }, count: { type: 'number', description: 'Total number of items' }, }, }