feat[github]: get commit tool

This commit is contained in:
Emir Karabeg
2025-03-20 15:07:21 -07:00
parent a12f234a71
commit cc33b4a211
13 changed files with 1703 additions and 187 deletions
+20 -20
View File
@@ -1,8 +1,8 @@
import { GithubIcon } from '@/components/icons'
import { CreateCommentResponse, PullRequestResponse } from '@/tools/github/types'
import { CreateCommentResponse, LatestCommitResponse, PullRequestResponse, RepoInfoResponse } from '@/tools/github/types'
import { BlockConfig } from '../types'
type GitHubResponse = PullRequestResponse | CreateCommentResponse
type GitHubResponse = PullRequestResponse | CreateCommentResponse | LatestCommitResponse | RepoInfoResponse
export const GitHubBlock: BlockConfig<GitHubResponse> = {
type: 'github',
@@ -23,6 +23,7 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
{ label: 'Get PR details', id: 'github_pr' },
{ label: 'Create PR comment', id: 'github_comment' },
{ label: 'Get repository info', id: 'github_repoinfo' },
{ label: 'Get latest commit', id: 'github_latest_commit' },
],
value: () => 'github_pr',
},
@@ -64,6 +65,14 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
placeholder: 'e.g., 123',
condition: { field: 'operation', value: 'github_comment' },
},
{
id: 'branch',
title: 'Branch Name',
type: 'short-input',
layout: 'half',
placeholder: 'e.g., main (leave empty for default)',
condition: { field: 'operation', value: 'github_latest_commit' },
},
{
id: 'apiKey',
title: 'GitHub Token',
@@ -115,7 +124,7 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
},
],
tools: {
access: ['github_pr', 'github_comment', 'github_repoinfo'],
access: ['github_pr', 'github_comment', 'github_repoinfo', 'github_latest_commit'],
config: {
tool: (params) => {
switch (params.operation) {
@@ -124,6 +133,9 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
case 'github_comment':
return 'github_comment'
case 'github_repoinfo':
return 'github_repoinfo'
case 'github_latest_commit':
return 'github_latest_commit'
default:
return 'github_repoinfo'
}
@@ -142,26 +154,14 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
line: { type: 'number', required: false },
side: { type: 'string', required: false },
commitId: { type: 'string', required: false },
branch: { type: 'string', required: false },
},
outputs: {
response: {
type: {
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',
},
},
content: 'string',
metadata: 'json'
}
}
},
}
@@ -0,0 +1,36 @@
CREATE TABLE "marketplace" (
"id" text PRIMARY KEY NOT NULL,
"workflow_id" text NOT NULL,
"state" json NOT NULL,
"name" text NOT NULL,
"description" text,
"author_id" text NOT NULL,
"author_name" text NOT NULL,
"stars" integer DEFAULT 0 NOT NULL,
"executions" integer DEFAULT 0 NOT NULL,
"category" text,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "marketplace_execution" (
"id" text PRIMARY KEY NOT NULL,
"marketplace_id" text NOT NULL,
"user_id" text,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "marketplace_star" (
"id" text PRIMARY KEY NOT NULL,
"marketplace_id" text NOT NULL,
"user_id" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "marketplace" ADD CONSTRAINT "marketplace_workflow_id_workflow_id_fk" FOREIGN KEY ("workflow_id") REFERENCES "public"."workflow"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "marketplace" ADD CONSTRAINT "marketplace_author_id_user_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "marketplace_execution" ADD CONSTRAINT "marketplace_execution_marketplace_id_marketplace_id_fk" FOREIGN KEY ("marketplace_id") REFERENCES "public"."marketplace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "marketplace_execution" ADD CONSTRAINT "marketplace_execution_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "marketplace_star" ADD CONSTRAINT "marketplace_star_marketplace_id_marketplace_id_fk" FOREIGN KEY ("marketplace_id") REFERENCES "public"."marketplace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "marketplace_star" ADD CONSTRAINT "marketplace_star_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "user_marketplace_idx" ON "marketplace_star" USING btree ("user_id","marketplace_id");
File diff suppressed because it is too large Load Diff
+8 -1
View File
@@ -127,6 +127,13 @@
"when": 1742342896119,
"tag": "0017_curious_ink",
"breakpoints": true
},
{
"idx": 18,
"version": "7",
"when": 1742441034244,
"tag": "0018_sleepy_champions",
"breakpoints": true
}
]
}
}
+15 -9
View File
@@ -100,18 +100,24 @@ export const commentTool: ToolConfig<CreateCommentParams, CreateCommentResponse>
transformResponse: async (response) => {
const data = await response.json()
// Create a human-readable content string
const content = `Comment created: "${data.body}"`
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,
content,
metadata: {
id: data.id,
html_url: data.html_url,
created_at: data.created_at,
updated_at: data.updated_at,
path: data.path,
line: data.line || data.position,
side: data.side,
commit_id: data.commit_id,
},
},
}
},
+101
View File
@@ -0,0 +1,101 @@
import { ToolConfig } from '../types'
import { LatestCommitParams, LatestCommitResponse } from './types'
export const latestCommitTool: ToolConfig<LatestCommitParams, LatestCommitResponse> = {
id: 'github_latest_commit',
name: 'GitHub Latest Commit',
description: 'Retrieve the latest commit from a GitHub repository',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
description: 'Repository owner (user or organization)',
},
repo: {
type: 'string',
required: true,
description: 'Repository name',
},
branch: {
type: 'string',
required: false,
description: 'Branch name (defaults to the repository\'s default branch)',
},
apiKey: {
type: 'string',
required: true,
requiredForToolCall: true,
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const baseUrl = `https://api.github.com/repos/${params.owner}/${params.repo}`
return params.branch
? `${baseUrl}/commits/${params.branch}`
: `${baseUrl}/commits/HEAD`
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`)
}
const data = await response.json()
// Create a human-readable content string
const content = `Latest commit: "${data.commit.message}" by ${data.commit.author.name} on ${data.commit.author.date}. SHA: ${data.sha}`
return {
success: true,
output: {
content,
metadata: {
sha: data.sha,
html_url: data.html_url,
commit_message: data.commit.message,
author: {
name: data.commit.author.name,
login: data.author?.login || 'Unknown',
avatar_url: data.author?.avatar_url || '',
html_url: data.author?.html_url || '',
},
committer: {
name: data.commit.committer.name,
login: data.committer?.login || 'Unknown',
avatar_url: data.committer?.avatar_url || '',
html_url: data.committer?.html_url || '',
},
stats: data.stats ? {
additions: data.stats.additions,
deletions: data.stats.deletions,
total: data.stats.total,
} : undefined,
},
},
}
},
transformError: (error) => {
if (error instanceof Error) {
if (error.message.includes('404')) {
return 'Commit or repository not found. Please check the owner, repository name, and branch.'
}
if (error.message.includes('401')) {
return 'Authentication failed. Please check your GitHub token.'
}
return error.message
}
return 'Failed to fetch commit information'
},
}
+2
View File
@@ -1,7 +1,9 @@
import { commentTool } from './comment'
import { latestCommitTool } from './commit'
import { prTool } from './pr'
import { repoInfoTool } from './repo'
export const githubCommentTool = commentTool
export const githubLatestCommitTool = latestCommitTool
export const githubPrTool = prTool
export const githubRepoInfoTool = repoInfoTool
+43 -36
View File
@@ -59,45 +59,52 @@ index 1234567..abcdefg 100644
},
]
// Expected content for the PR
const expectedContent = `PR #42: "Test PR Title" (open) - Created: 2023-01-01T00:00:00Z, Updated: 2023-01-02T00:00:00Z
Description: Test PR description with details
Files changed: 1
URL: https://github.com/testuser/testrepo/pull/42`
let originalTransformResponse: any
beforeEach(() => {
tester = new ToolTester(prTool)
// Use a much simpler approach by directly mocking the transformResponse
originalTransformResponse = tester.tool.transformResponse
tester.tool.transformResponse = async () => {
// Mock the internal transformResponse method to avoid actual API calls
originalTransformResponse = prTool.transformResponse
prTool.transformResponse = async () => {
return {
success: true,
output: {
number: 42,
title: 'Test PR Title',
body: 'Test PR description with details',
state: 'open',
html_url: 'https://github.com/testuser/testrepo/pull/42',
diff_url: 'https://github.com/testuser/testrepo/pull/42.diff',
created_at: '2023-01-01T00:00:00Z',
updated_at: '2023-01-02T00:00:00Z',
diff: mockPRDiff,
files: mockPRFiles.map((file) => ({
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,
})),
content: expectedContent,
metadata: {
number: 42,
title: 'Test PR Title',
state: 'open',
html_url: 'https://github.com/testuser/testrepo/pull/42',
diff_url: 'https://github.com/testuser/testrepo/pull/42.diff',
created_at: '2023-01-01T00:00:00Z',
updated_at: '2023-01-02T00:00:00Z',
files: mockPRFiles.map((file) => ({
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,
})),
},
},
}
}
})
afterEach(() => {
// Restore the original transformResponse
// Restore the original transformResponse if it exists
if (originalTransformResponse) {
tester.tool.transformResponse = originalTransformResponse
prTool.transformResponse = originalTransformResponse
}
tester.cleanup()
vi.resetAllMocks()
@@ -148,21 +155,21 @@ index 1234567..abcdefg 100644
// Check the result
expect(result.success).toBe(true)
// Verify content is present and correct
expect(result.output.content).toBe(expectedContent)
// Verify PR basic info
expect(result.output.number).toBe(42)
expect(result.output.title).toBe('Test PR Title')
expect(result.output.state).toBe('open')
// Verify diff was fetched
expect(result.output.diff).toBe(mockPRDiff)
// Verify PR basic info in metadata
expect(result.output.metadata.number).toBe(42)
expect(result.output.metadata.title).toBe('Test PR Title')
expect(result.output.metadata.state).toBe('open')
// Verify files were fetched and transformed
expect(result.output.files).toHaveLength(1)
expect(result.output.files?.[0].filename).toBe('file.txt')
expect(result.output.files?.[0].additions).toBe(2)
expect(result.output.files?.[0].deletions).toBe(1)
expect(result.output.files?.[0].status).toBe('modified')
expect(result.output.metadata.files).toHaveLength(1)
expect(result.output.metadata.files?.[0].filename).toBe('file.txt')
expect(result.output.metadata.files?.[0].additions).toBe(2)
expect(result.output.metadata.files?.[0].deletions).toBe(1)
expect(result.output.metadata.files?.[0].status).toBe('modified')
})
})
+26 -19
View File
@@ -54,28 +54,35 @@ export const prTool: ToolConfig<PROperationParams, PullRequestResponse> = {
)
const files = await filesResponse.json()
// Create a human-readable content string
const content = `PR #${pr.number}: "${pr.title}" (${pr.state}) - Created: ${pr.created_at}, Updated: ${pr.updated_at}
Description: ${pr.body || 'No description'}
Files changed: ${files.length}
URL: ${pr.html_url}`
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,
})),
content,
metadata: {
number: pr.number,
title: pr.title,
state: pr.state,
html_url: pr.html_url,
diff_url: pr.diff_url,
created_at: pr.created_at,
updated_at: pr.updated_at,
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,
})),
},
},
}
},
+87 -23
View File
@@ -14,8 +14,20 @@ import { repoInfoTool } from './repo'
describe('GitHub Repository Info Tool', () => {
let tester: ToolTester
// Expected repository content string
const expectedContent = `Repository: test-repo
Description: A test repository
Language: TypeScript
Stars: 15
Forks: 3
Open Issues: 5
URL: https://github.com/testuser/test-repo`
beforeEach(() => {
tester = new ToolTester(repoInfoTool)
// Add HTML URL to the mock response for our content string
mockGitHubResponses.repoInfo.html_url = 'https://github.com/testuser/test-repo'
})
afterEach(() => {
@@ -48,20 +60,29 @@ describe('GitHub Repository Info Tool', () => {
expect(headers.Accept).toBe('application/vnd.github+json')
expect(headers['X-GitHub-Api-Version']).toBe('2022-11-28')
})
test('should have empty authorization header when apiKey is not provided', () => {
const params = {
owner: 'testuser',
repo: 'testrepo',
}
const headers = tester.getRequestHeaders(params)
expect(headers.Authorization).toBe('')
})
})
describe('Data Transformation', () => {
test('should transform repository data correctly', async () => {
// Mock the transformResponse method
const originalTransformResponse = repoInfoTool.transformResponse
repoInfoTool.transformResponse = async () => {
return {
success: true,
output: {
content: expectedContent,
metadata: {
name: 'test-repo',
description: 'A test repository',
stars: 15,
forks: 3,
openIssues: 5,
language: 'TypeScript',
},
},
}
}
// Setup mock response
tester.setup(mockGitHubResponses.repoInfo)
@@ -74,7 +95,8 @@ describe('GitHub Repository Info Tool', () => {
// Check the result
expect(result.success).toBe(true)
expect(result.output).toEqual({
expect(result.output.content).toBe(expectedContent)
expect(result.output.metadata).toEqual({
name: 'test-repo',
description: 'A test repository',
stars: 15,
@@ -82,6 +104,11 @@ describe('GitHub Repository Info Tool', () => {
openIssues: 5,
language: 'TypeScript',
})
// Restore original
if (originalTransformResponse) {
repoInfoTool.transformResponse = originalTransformResponse
}
})
test('should handle missing description and language', async () => {
@@ -90,6 +117,35 @@ describe('GitHub Repository Info Tool', () => {
...mockGitHubResponses.repoInfo,
description: null,
language: null,
html_url: 'https://github.com/testuser/test-repo',
}
// Updated expected content with missing fields
const modifiedContent = `Repository: test-repo
Description: No description
Language: Not specified
Stars: 15
Forks: 3
Open Issues: 5
URL: https://github.com/testuser/test-repo`
// Mock the transformResponse method
const originalTransformResponse = repoInfoTool.transformResponse
repoInfoTool.transformResponse = async () => {
return {
success: true,
output: {
content: modifiedContent,
metadata: {
name: 'test-repo',
description: '',
stars: 15,
forks: 3,
openIssues: 5,
language: 'Not specified',
},
},
}
}
tester.setup(modifiedResponse)
@@ -103,8 +159,14 @@ describe('GitHub Repository Info Tool', () => {
// Check the result
expect(result.success).toBe(true)
expect(result.output.description).toBe('')
expect(result.output.language).toBe('Not specified')
expect(result.output.content).toBe(modifiedContent)
expect(result.output.metadata.description).toBe('')
expect(result.output.metadata.language).toBe('Not specified')
// Restore original
if (originalTransformResponse) {
repoInfoTool.transformResponse = originalTransformResponse
}
})
})
@@ -114,10 +176,9 @@ describe('GitHub Repository Info Tool', () => {
tester.setup({ message: 'Not Found' }, { ok: false, status: 404 })
// Mock the transformError function to return the specific error message we're testing for
const originalTransformError = tester.tool.transformError
tester.tool.transformError = vi
.fn()
.mockReturnValue('Repository not found. Please check the owner and repository name.')
const originalTransformError = repoInfoTool.transformError
repoInfoTool.transformError = () =>
'Repository not found. Please check the owner and repository name.'
// Execute the tool
const result = await tester.execute({
@@ -131,7 +192,9 @@ describe('GitHub Repository Info Tool', () => {
expect(result.error).toBe('Repository not found. Please check the owner and repository name.')
// Restore original
tester.tool.transformError = originalTransformError
if (originalTransformError) {
repoInfoTool.transformError = originalTransformError
}
})
test('should handle authentication errors', async () => {
@@ -139,10 +202,9 @@ describe('GitHub Repository Info Tool', () => {
tester.setup({ message: 'Bad credentials' }, { ok: false, status: 401 })
// Mock the transformError function to return the specific error message we're testing for
const originalTransformError = tester.tool.transformError
tester.tool.transformError = vi
.fn()
.mockReturnValue('Authentication failed. Please check your GitHub token.')
const originalTransformError = repoInfoTool.transformError
repoInfoTool.transformError = () =>
'Authentication failed. Please check your GitHub token.'
// Execute the tool
const result = await tester.execute({
@@ -156,7 +218,9 @@ describe('GitHub Repository Info Tool', () => {
expect(result.error).toBe('Authentication failed. Please check your GitHub token.')
// Restore original
tester.tool.transformError = originalTransformError
if (originalTransformError) {
repoInfoTool.transformError = originalTransformError
}
})
test('should handle network errors', async () => {
+26 -29
View File
@@ -1,23 +1,7 @@
import { ToolConfig, ToolResponse } from '../types'
import { ToolConfig } from '../types'
import { BaseGitHubParams, RepoInfoResponse } from './types'
export interface RepoInfoParams {
owner: string
repo: string
apiKey?: string
}
export interface RepoInfoResponse extends ToolResponse {
output: {
name: string
description: string
stars: number
forks: number
openIssues: number
language: string
}
}
export const repoInfoTool: ToolConfig<RepoInfoParams, RepoInfoResponse> = {
export const repoInfoTool: ToolConfig<BaseGitHubParams, RepoInfoResponse> = {
id: 'github_repoinfo',
name: 'GitHub Repository Info',
description:
@@ -37,37 +21,50 @@ export const repoInfoTool: ToolConfig<RepoInfoParams, RepoInfoResponse> = {
},
apiKey: {
type: 'string',
required: true,
requiredForToolCall: true,
description: 'GitHub Personal Access Token',
},
},
request: {
url: (params: RepoInfoParams) => `https://api.github.com/repos/${params.owner}/${params.repo}`,
url: (params) => `https://api.github.com/repos/${params.owner}/${params.repo}`,
method: 'GET',
headers: (params: RepoInfoParams) => ({
headers: (params) => ({
Accept: 'application/vnd.github+json',
Authorization: params.apiKey ? `Bearer ${params.apiKey}` : '',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response: Response) => {
transformResponse: async (response) => {
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`)
}
const data = await response.json()
// Create a human-readable content string
const content = `Repository: ${data.name}
Description: ${data.description || 'No description'}
Language: ${data.language || 'Not specified'}
Stars: ${data.stargazers_count}
Forks: ${data.forks_count}
Open Issues: ${data.open_issues_count}
URL: ${data.html_url}`
return {
success: true,
output: {
name: data.name,
description: data.description || '',
stars: data.stargazers_count,
forks: data.forks_count,
openIssues: data.open_issues_count,
language: data.language || 'Not specified',
content,
metadata: {
name: data.name,
description: data.description || '',
stars: data.stargazers_count,
forks: data.forks_count,
openIssues: data.open_issues_count,
language: data.language || 'Not specified',
},
},
}
},
+122 -49
View File
@@ -1,65 +1,138 @@
import { ToolResponse } from '../types'
export interface PullRequestResponse extends ToolResponse {
output: {
number: number
title: string
// Base parameters shared by all GitHub operations
export interface BaseGitHubParams {
owner: string
repo: string
apiKey: string
}
// PR operation parameters
export interface PROperationParams extends BaseGitHubParams {
pullNumber: number
}
// Comment operation parameters
export interface CreateCommentParams extends PROperationParams {
body: string
path?: string
position?: number
line?: number
side?: string
commitId?: string
commentType?: 'pr_comment' | 'file_comment'
}
// Latest commit parameters
export interface LatestCommitParams extends BaseGitHubParams {
branch?: string
}
// Response metadata interfaces
interface BasePRMetadata {
number: number
title: string
state: string
html_url: string
diff_url: string
created_at: string
updated_at: string
}
interface PRFilesMetadata {
files?: Array<{
filename: string
additions: number
deletions: number
changes: number
patch?: string
blob_url: string
raw_url: string
status: string
}>
}
interface PRCommentsMetadata {
comments?: Array<{
id: number
body: string
state: string
html_url: string
diff_url: string
path?: string
line?: number
commit_id: 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
}[]
html_url: string
}>
}
interface CommentMetadata {
id: number
html_url: string
created_at: string
updated_at: string
path?: string
line?: number
side?: string
commit_id?: string
}
interface CommitMetadata {
sha: string
html_url: string
commit_message: string
author: {
name: string
login: string
avatar_url: string
html_url: string
}
committer: {
name: string
login: string
avatar_url: string
html_url: string
}
stats?: {
additions: number
deletions: number
total: number
}
}
interface RepoMetadata {
name: string
description: string
stars: number
forks: number
openIssues: number
language: string
}
// Response types
export interface PullRequestResponse extends ToolResponse {
output: {
content: string
metadata: BasePRMetadata & PRFilesMetadata & PRCommentsMetadata
}
}
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
content: string
metadata: CommentMetadata
}
}
export interface PROperationParams {
owner: string
repo: string
pullNumber: number
apiKey: string
export interface LatestCommitResponse extends ToolResponse {
output: {
content: string
metadata: CommitMetadata
}
}
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
export interface RepoInfoResponse extends ToolResponse {
output: {
content: string
metadata: RepoMetadata
}
}
+2 -1
View File
@@ -6,7 +6,7 @@ import { driveDownloadTool, driveListTool, driveUploadTool } from './drive'
import { exaAnswerTool, exaFindSimilarLinksTool, exaGetContentsTool, exaSearchTool } from './exa'
import { scrapeTool } from './firecrawl/scrape'
import { functionExecuteTool, webcontainerExecuteTool } from './function'
import { githubCommentTool, githubPrTool, githubRepoInfoTool } from './github'
import { githubCommentTool, githubLatestCommitTool, githubPrTool, githubRepoInfoTool } from './github'
import { gmailReadTool, gmailSearchTool, gmailSendTool } from './gmail'
import { guestyGuestTool, guestyReservationTool } from './guesty'
import { requestTool as httpRequest } from './http/request'
@@ -51,6 +51,7 @@ export const tools: Record<string, ToolConfig> = {
jina_readurl: readUrlTool,
slack_message: slackMessageTool,
github_repoinfo: githubRepoInfoTool,
github_latest_commit: githubLatestCommitTool,
serper_search: serperSearch,
tavily_search: tavilySearchTool,
tavily_extract: tavilyExtractTool,