diff --git a/docs/api-reference/merge-pull-request.mdx b/docs/api-reference/merge-pull-request.mdx new file mode 100644 index 00000000..fcedd140 --- /dev/null +++ b/docs/api-reference/merge-pull-request.mdx @@ -0,0 +1,199 @@ +--- +title: 'mergePullRequest' +description: 'Merge an existing pull request on GitHub' +--- + +## Overview + +The `mergePullRequest` method allows you to programmatically merge an existing pull request on GitHub. It supports different merge methods (merge, squash, rebase) and allows customization of the commit message. + +## Method Signature + +```typescript +async mergePullRequest( + options: MergePullRequestOptions +): Promise +``` + +## Parameters + +### MergePullRequestOptions + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `pullNumber` | `number` | Yes | The number of the pull request to merge | +| `commitTitle` | `string` | No | Custom title for the merge commit | +| `commitMessage` | `string` | No | Custom message for the merge commit | +| `mergeMethod` | `'merge' \| 'squash' \| 'rebase'` | No | The merge method to use (defaults to 'merge') | + +### Merge Methods + +- **`merge`**: Creates a merge commit with all commits from the feature branch +- **`squash`**: Squashes all commits into a single commit before merging +- **`rebase`**: Rebases the commits onto the base branch + +## Return Value + +### MergePullRequestResult + +| Property | Type | Description | +|----------|------|-------------| +| `sha` | `string` | The SHA of the merge commit | +| `merged` | `boolean` | Whether the pull request was successfully merged | +| `message` | `string` | A message describing the merge result | + +## Prerequisites + +Before using this method, ensure: + +1. **GitHub Configuration**: You only need to configure GitHub credentials: + ```typescript + vibekit.withGithub({ + token: "your-github-token", + repository: "owner/repo" + }) + ``` + Note: Unlike other methods, `mergePullRequest` does NOT require agent or sandbox configuration. + +2. **Pull Request State**: The pull request must be: + - Open and not already merged + - Free of merge conflicts + - Passing all required status checks + - Approved if required by branch protection rules + +## Usage Examples + +### Basic Merge + +```typescript +import { VibeKit } from "vibekit"; + +const vibekit = new VibeKit() + .withGithub({ + token: process.env.GITHUB_TOKEN, + repository: "myorg/myrepo" + }); + +// Merge a pull request with default settings +const mergeResult = await vibekit.mergePullRequest({ + pullNumber: 42 +}); + +console.log(`PR merged with commit SHA: ${mergeResult.sha}`); +``` + +### Squash and Merge + +```typescript +// Squash all commits and merge with a custom message +const mergeResult = await vibekit.mergePullRequest({ + pullNumber: 42, + mergeMethod: "squash", + commitTitle: "feat: Add new feature", + commitMessage: "This PR adds the new feature with the following changes:\n- Added X\n- Updated Y\n- Fixed Z" +}); +``` + +### Complete Workflow Example + +```typescript +// For operations that require code generation, you'll need agent and sandbox +const vibkitWithAgent = new VibeKit() + .withAgent({ + type: "claude", + provider: "anthropic", + apiKey: process.env.ANTHROPIC_API_KEY, + model: "claude-3-opus-20240229" + }) + .withSandbox("docker") + .withGithub({ + token: process.env.GITHUB_TOKEN, + repository: "myorg/myrepo" + }); + +// Generate code and create PR (requires agent/sandbox) +await vibkitWithAgent.generateCode({ + prompt: "Add a new user authentication feature", + mode: "code", + branch: "feature/auth" +}); + +await vibkitWithAgent.pushToBranch(); +const prResponse = await vibkitWithAgent.createPullRequest( + { + name: "feature", + color: "0366d6", + description: "New feature" + }, + "feature" +); + +console.log(`Created PR #${prResponse.number}`); + +// After review and approval, merge the PR (only needs GitHub config) +const vibkitSimple = new VibeKit() + .withGithub({ + token: process.env.GITHUB_TOKEN, + repository: "myorg/myrepo" + }); + +const mergeResult = await vibkitSimple.mergePullRequest({ + pullNumber: prResponse.number, + mergeMethod: "squash" +}); + +if (mergeResult.merged) { + console.log(`Successfully merged PR #${prResponse.number}`); +} +``` + +## Error Handling + +The method will throw an error in the following cases: + +### Configuration Errors +- Missing GitHub token or repository configuration +- Invalid repository URL format + +### Pull Request Errors +- **404**: Pull request not found +- **405**: Pull request is not mergeable (conflicts or failed checks) +- **422**: Invalid merge parameters or validation failed + +### Example Error Handling + +```typescript +try { + const result = await vibekit.mergePullRequest({ + pullNumber: 42, + mergeMethod: "squash" + }); + + if (result.merged) { + console.log("Pull request successfully merged!"); + } +} catch (error) { + if (error.message.includes("not mergeable")) { + console.error("PR has conflicts or failed status checks"); + } else if (error.message.includes("not found")) { + console.error("PR does not exist"); + } else { + console.error("Failed to merge PR:", error.message); + } +} +``` + +## Notes + +- **No agent or sandbox required**: This method only needs GitHub configuration +- The method requires appropriate GitHub permissions (write access to the repository) +- Branch protection rules will be enforced +- The merge will respect all repository settings and requirements +- After a successful merge, the source branch may be automatically deleted depending on repository settings +- This is a direct GitHub API call - no code execution or AI processing is involved + +## Related Methods + +- [`createPullRequest`](/api-reference/create-pull-request) - Create a new pull request +- [`pushToBranch`](/api-reference/push-to-branch) - Push changes to a branch +- [`generateCode`](/api-reference/generate-code) - Generate code using AI agents \ No newline at end of file diff --git a/docs/docs.json b/docs/docs.json index ba80c342..bfe0298c 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -136,6 +136,7 @@ "api-reference/generate-code", "api-reference/execute-command", "api-reference/create-pull-request", + "api-reference/merge-pull-request", "api-reference/push-to-branch", "api-reference/run-tests", "api-reference/kill-sandbox", diff --git a/docs/sdk/github-integration.mdx b/docs/sdk/github-integration.mdx index 786a7481..05e017b9 100644 --- a/docs/sdk/github-integration.mdx +++ b/docs/sdk/github-integration.mdx @@ -82,7 +82,40 @@ const result = await vibeKit.generateCode({ }); await vibeKit.pushToBranch(); +``` + +## Merging Pull Requests + +Once a pull request has been reviewed and approved, you can programmatically merge it using the `mergePullRequest` method. This method only requires GitHub configuration - no agent or sandbox needed: + +```typescript +// Create a simple VibeKit instance with just GitHub config +const vibekit = new VibeKit() + .withGithub({ + token: process.env.GITHUB_TOKEN, + repository: "myorg/myrepo" + }); +// Merge a pull request with default settings (regular merge) +const mergeResult = await vibekit.mergePullRequest({ + pullNumber: 42 +}); + +console.log(`PR merged! Commit SHA: ${mergeResult.sha}`); + +// Or use squash merge with custom commit message +const squashResult = await vibekit.mergePullRequest({ + pullNumber: 42, + mergeMethod: "squash", + commitTitle: "feat: Add user authentication", + commitMessage: "Implemented complete authentication flow with validation" +}); ``` -This comprehensive GitHub integration allows you to build powerful conversational UIs where users can iteratively request code changes, see them applied in real-time, and create pull requests when they're satisfied with the results. \ No newline at end of file +### Merge Methods + +- **`merge`** (default): Creates a merge commit with all commits from the feature branch +- **`squash`**: Squashes all commits into a single commit before merging +- **`rebase`**: Rebases the commits onto the base branch + +This comprehensive GitHub integration allows you to build powerful conversational UIs where users can iteratively request code changes, see them applied in real-time, create pull requests, and merge them programmatically when they're satisfied with the results. \ No newline at end of file diff --git a/packages/sdk/src/core/vibekit.ts b/packages/sdk/src/core/vibekit.ts index 3e5b5d7b..ac3076f7 100644 --- a/packages/sdk/src/core/vibekit.ts +++ b/packages/sdk/src/core/vibekit.ts @@ -6,6 +6,8 @@ import type { SandboxProvider, Conversation, LabelOptions, + MergePullRequestOptions, + MergePullRequestResult, } from "../types"; import { AGENT_TYPES } from "../constants/agents"; import { AgentResponse, ExecuteCommandOptions, PullRequestResult } from "../agents/base"; @@ -181,6 +183,72 @@ export class VibeKit extends EventEmitter { return this.agent.pushToBranch(branch); } + async mergePullRequest( + options: MergePullRequestOptions + ): Promise { + const { github } = this.options; + + if (!github?.token || !github?.repository) { + throw new Error( + "GitHub configuration is required for merging pull requests. Please use withGithub() to configure GitHub credentials." + ); + } + + const { pullNumber, commitTitle, commitMessage, mergeMethod = 'merge' } = options; + + if (!pullNumber || typeof pullNumber !== 'number') { + throw new Error("Pull request number is required and must be a number"); + } + + const [owner, repo] = github.repository?.split("/") || []; + + if (!owner || !repo) { + throw new Error("Invalid repository URL format. Expected format: owner/repo"); + } + + // Merge the pull request using GitHub API directly + const mergeResponse = await fetch( + `https://api.github.com/repos/${owner}/${repo}/pulls/${pullNumber}/merge`, + { + method: "PUT", + headers: { + Authorization: `token ${github.token}`, + Accept: "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + commit_title: commitTitle, + commit_message: commitMessage, + merge_method: mergeMethod, + }), + } + ); + + const responseData = await mergeResponse.json(); + + if (!mergeResponse.ok) { + // Handle specific error cases + if (mergeResponse.status === 404) { + throw new Error(`Pull request #${pullNumber} not found in ${github.repository}`); + } else if (mergeResponse.status === 405) { + throw new Error(`Pull request #${pullNumber} is not mergeable. It may have conflicts or failed status checks.`); + } else if (mergeResponse.status === 422) { + throw new Error(`Invalid merge parameters: ${responseData.message || 'Unknown validation error'}`); + } else { + throw new Error( + `Failed to merge pull request #${pullNumber}: ${mergeResponse.status} ${responseData.message || mergeResponse.statusText}` + ); + } + } + + return { + sha: responseData.sha, + merged: responseData.merged, + message: responseData.message, + }; + } + async runTests(): Promise { if (!this.agent) { await this.initializeAgent(); diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 5fa2e059..3802e4bf 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -19,6 +19,8 @@ export type { VibeKitConfig, Conversation, LabelOptions, + MergePullRequestOptions, + MergePullRequestResult, CodexStreamCallbacks, ClaudeStreamCallbacks, OpenCodeStreamCallbacks, diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index ac39ab52..335f6f84 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -89,6 +89,20 @@ export interface LabelOptions { description: string; } +// MERGE PULL REQUEST OPTIONS +export interface MergePullRequestOptions { + pullNumber: number; + commitTitle?: string; + commitMessage?: string; + mergeMethod?: 'merge' | 'squash' | 'rebase'; +} + +export interface MergePullRequestResult { + sha: string; + merged: boolean; + message: string; +} + // STREAMING CALLBACKS export interface CodexStreamCallbacks { onUpdate?: (message: string) => void;