feat(github): add github read pr & write comment tools to the github block, tested & works

This commit is contained in:
Waleed Latif
2025-02-24 00:03:58 -08:00
parent 3f4e6041ce
commit 9ff1783dcf
5 changed files with 387 additions and 19 deletions
+110 -19
View File
@@ -1,15 +1,29 @@
import { RepoInfoResponse } from '@/tools/github/repo'
import { GithubIcon } from '../../components/icons'
import { GithubIcon } from '@/components/icons'
import { CreateCommentResponse, PullRequestResponse } from '@/tools/github/types'
import { BlockConfig } from '../types'
export const GitHubBlock: BlockConfig<RepoInfoResponse> = {
type GitHubResponse = PullRequestResponse | CreateCommentResponse
export const GitHubBlock: BlockConfig<GitHubResponse> = {
type: 'github',
name: 'GitHub',
description: 'Fetch GitHub repository',
description: 'Interact with GitHub repositories and PRs',
category: 'tools',
bgColor: '#181C1E',
icon: GithubIcon,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
layout: 'full',
options: [
{ label: 'Get PR details', id: 'github_pr' },
{ label: 'Create PR comment', id: 'github_comment' },
{ label: 'Get repository info', id: 'github_repoinfo' },
],
value: () => 'github_pr',
},
{
id: 'owner',
title: 'Repository Owner',
@@ -25,15 +39,20 @@ export const GitHubBlock: BlockConfig<RepoInfoResponse> = {
placeholder: 'e.g., vscode',
},
{
id: 'action',
title: 'Action',
type: 'dropdown',
id: 'pullNumber',
title: 'Pull Request Number',
type: 'short-input',
layout: 'half',
placeholder: 'e.g., 123',
condition: { field: 'operation', value: 'github_pr' },
},
{
id: 'body',
title: 'Comment',
type: 'long-input',
layout: 'full',
options: [
{ label: 'Get general info', id: 'general_info' },
{ label: 'Get pull requests', id: 'pull_requests' },
],
value: () => 'general_info',
placeholder: 'Enter comment text',
condition: { field: 'operation', value: 'github_comment' },
},
{
id: 'apiKey',
@@ -43,23 +62,95 @@ export const GitHubBlock: BlockConfig<RepoInfoResponse> = {
placeholder: 'Enter GitHub Token',
password: true,
},
{
id: 'commentType',
title: 'Comment Type',
type: 'dropdown',
layout: 'half',
options: [
{ label: 'General PR Comment', id: 'pr_comment' },
{ label: 'File-specific Comment', id: 'file_comment' },
],
condition: { field: 'operation', value: 'github_comment' },
},
{
id: 'path',
title: 'File Path',
type: 'short-input',
layout: 'half',
placeholder: 'e.g., src/main.ts',
condition: {
field: 'operation',
value: 'github_comment',
and: {
field: 'commentType',
value: 'file_comment',
},
},
},
{
id: 'line',
title: 'Line Number',
type: 'short-input',
layout: 'half',
placeholder: 'e.g., 42',
condition: {
field: 'operation',
value: 'github_comment',
and: {
field: 'commentType',
value: 'file_comment',
},
},
},
],
tools: {
access: ['github_repoinfo'],
access: ['github_pr', 'github_comment', 'github_repoinfo'],
config: {
tool: (params) => {
switch (params.operation) {
case 'github_pr':
return 'github_pr'
case 'github_comment':
return 'github_comment'
case 'github_repoinfo':
default:
return 'github_repoinfo'
}
},
},
},
inputs: {
operation: { type: 'string', required: true },
owner: { type: 'string', required: true },
repo: { type: 'string', required: true },
pullNumber: { type: 'number', required: false },
body: { type: 'string', required: false },
apiKey: { type: 'string', required: true },
commentType: { type: 'string', required: false },
path: { type: 'string', required: false },
line: { type: 'number', required: false },
side: { type: 'string', required: false },
commitId: { type: 'string', required: false },
},
outputs: {
response: {
type: {
name: 'string',
description: 'string',
stars: 'number',
forks: 'number',
openIssues: 'number',
language: 'string',
body: 'string',
html_url: 'string',
created_at: 'string',
updated_at: 'string',
number: 'number',
title: 'string',
state: 'string',
diff_url: 'string',
files: 'any',
comments: 'any',
id: 'number',
path: 'any',
line: 'any',
side: 'any',
commit_id: 'any',
},
},
},
+122
View File
@@ -0,0 +1,122 @@
import { ToolConfig } from '../types'
import { CreateCommentParams, CreateCommentResponse } from './types'
export const commentTool: ToolConfig<CreateCommentParams, CreateCommentResponse> = {
id: 'github_comment',
name: 'GitHub PR Commenter',
description: 'Create comments on GitHub PRs',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
description: 'Repository name',
},
pullNumber: {
type: 'number',
required: true,
description: 'Pull request number',
},
body: {
type: 'string',
required: true,
description: 'Comment content',
},
path: {
type: 'string',
required: false,
description: 'File path for review comment',
},
position: {
type: 'number',
required: false,
description: 'Line number for review comment',
},
apiKey: {
type: 'string',
required: true,
requiredForToolCall: true,
description: 'GitHub API token',
},
commentType: {
type: 'string',
required: false,
description: 'Type of comment (pr_comment or file_comment)',
},
line: {
type: 'number',
required: false,
description: 'Line number for review comment',
},
side: {
type: 'string',
required: false,
description: 'Side of the diff (LEFT or RIGHT)',
default: 'RIGHT',
},
commitId: {
type: 'string',
required: false,
description: 'The SHA of the commit to comment on',
},
},
request: {
url: (params) => {
if (params.path) {
return `https://api.github.com/repos/${params.owner}/${params.repo}/pulls/${params.pullNumber}/comments`
} else {
return `https://api.github.com/repos/${params.owner}/${params.repo}/pulls/${params.pullNumber}/reviews`
}
},
method: 'POST',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
body: (params) => {
if (params.commentType === 'file_comment') {
return {
body: params.body,
commit_id: params.commitId,
path: params.path,
line: params.line || params.position,
side: params.side || 'RIGHT',
}
}
return {
body: params.body,
event: 'COMMENT',
}
},
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
body: data.body,
path: data.path,
line: data.line || data.position,
side: data.side,
commit_id: data.commit_id,
created_at: data.created_at,
updated_at: data.updated_at,
html_url: data.html_url,
},
}
},
transformError: (error) => {
return error instanceof Error ? error.message : 'Failed to create comment'
},
}
+86
View File
@@ -0,0 +1,86 @@
import { ToolConfig } from '../types'
import { PROperationParams, PullRequestResponse } from './types'
export const prTool: ToolConfig<PROperationParams, PullRequestResponse> = {
id: 'github_pr',
name: 'GitHub PR Reader',
description: 'Fetch PR details including diff and files changed',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
description: 'Repository name',
},
pullNumber: {
type: 'number',
required: true,
description: 'Pull request number',
},
apiKey: {
type: 'string',
required: true,
requiredForToolCall: true,
description: 'GitHub API token',
},
},
request: {
url: (params) =>
`https://api.github.com/repos/${params.owner}/${params.repo}/pulls/${params.pullNumber}`,
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response) => {
const pr = await response.json()
// Fetch the PR diff
const diffResponse = await fetch(pr.diff_url)
const diff = await diffResponse.text()
// Fetch files changed
const filesResponse = await fetch(
`https://api.github.com/repos/${pr.base.repo.owner.login}/${pr.base.repo.name}/pulls/${pr.number}/files`
)
const files = await filesResponse.json()
return {
success: true,
output: {
number: pr.number,
title: pr.title,
body: pr.body || '',
state: pr.state,
html_url: pr.html_url,
diff_url: pr.diff_url,
created_at: pr.created_at,
updated_at: pr.updated_at,
diff,
files: files.map((file: any) => ({
filename: file.filename,
additions: file.additions,
deletions: file.deletions,
changes: file.changes,
patch: file.patch,
blob_url: file.blob_url,
raw_url: file.raw_url,
status: file.status,
})),
},
}
},
transformError: (error) => {
return error instanceof Error ? error.message : 'Failed to fetch PR details'
},
}
+65
View File
@@ -0,0 +1,65 @@
import { ToolResponse } from '../types'
export interface PullRequestResponse extends ToolResponse {
output: {
number: number
title: string
body: string
state: string
html_url: string
diff_url: string
created_at: string
updated_at: string
files?: {
filename: string
additions: number
deletions: number
changes: number
patch?: string
blob_url: string
raw_url: string
status: string
}[]
comments?: {
id: number
body: string
path?: string
line?: number
commit_id: string
created_at: string
updated_at: string
html_url: string
}[]
}
}
export interface CreateCommentResponse extends ToolResponse {
output: {
id: number
body: string
path?: string
line?: number
side?: string
commit_id?: string
created_at: string
updated_at: string
html_url: string
}
}
export interface PROperationParams {
owner: string
repo: string
pullNumber: number
apiKey: string
}
export interface CreateCommentParams extends PROperationParams {
body: string
path?: string
position?: number
line?: number // Add line number support
side?: string // Add side parameter
commitId?: string
commentType?: 'pr_comment' | 'file_comment' // Add comment type
}
+4
View File
@@ -4,6 +4,8 @@ import { chatTool as deepseekChat } from './deepseek/chat'
import { reasonerTool as deepseekReasoner } from './deepseek/reasoner'
import { scrapeTool } from './firecrawl/scrape'
import { functionExecuteTool as functionExecute } from './function/execute'
import { commentTool } from './github/comment'
import { prTool } from './github/pr'
import { repoInfoTool } from './github/repo'
import { gmailReadTool } from './gmail/read'
import { gmailSearchTool } from './gmail/search'
@@ -68,6 +70,8 @@ export const tools: Record<string, ToolConfig> = {
pinecone_generate_embeddings: pineconeGenerateEmbeddingsTool,
pinecone_search_text: pineconeSearchTextTool,
pinecone_upsert_text: pineconeUpsertTextTool,
github_pr: prTool,
github_comment: commentTool,
}
// Get a tool by its ID