import { z } from "zod";
import { AdoClient } from "../../ado-client.js";
import * as GitInterfaces from "azure-devops-node-api/interfaces/GitInterfaces";
export const completePullRequestSchema = z.object({
project: z.string().optional().describe("Project name, defaults to ADO_PROJECT env var"),
repository: z.string().describe("Repository name or ID"),
pullRequestId: z.number().describe("Pull request ID"),
mergeStrategy: z
.enum(["noFastForward", "squash", "rebase", "rebaseMerge"])
.default("noFastForward")
.describe("Merge strategy"),
deleteSourceBranch: z.boolean().optional().describe("Delete source branch after merge"),
commitMessage: z.string().optional().describe("Custom merge commit message"),
bypassPolicy: z.boolean().default(false).describe("Bypass branch policies (requires permission)"),
});
export const completePullRequestTool = {
name: "complete_pull_request",
description: "Complete (merge) an approved pull request into the target branch. Choose merge strategy (merge commit, squash, rebase). Optionally delete source branch after merge and provide custom commit message. Requires all policies to pass unless bypassing.",
inputSchema: {
type: "object" as const,
properties: {
project: {
type: "string",
description: "Project name, defaults to ADO_PROJECT env var",
},
repository: {
type: "string",
description: "Repository name or ID",
},
pullRequestId: {
type: "number",
description: "Pull request ID",
},
mergeStrategy: {
type: "string",
enum: ["noFastForward", "squash", "rebase", "rebaseMerge"],
description: "Merge strategy, default 'noFastForward'",
},
deleteSourceBranch: {
type: "boolean",
description: "Delete source branch after merge",
},
commitMessage: {
type: "string",
description: "Custom merge commit message",
},
bypassPolicy: {
type: "boolean",
description: "Bypass branch policies (requires permission)",
},
},
required: ["repository", "pullRequestId"],
},
};
export interface CompletePullRequestResult {
id: number;
status: string;
mergeCommitId?: string;
message: string;
}
export async function completePullRequest(
client: AdoClient,
params: z.infer<typeof completePullRequestSchema>
): Promise<CompletePullRequestResult> {
const validatedParams = completePullRequestSchema.parse(params);
const project = client.resolveProject(validatedParams.project);
const gitApi = await client.getGitApi();
// Get current PR to get the last merge source commit
const currentPr = await gitApi.getPullRequest(
validatedParams.repository,
validatedParams.pullRequestId,
project
);
if (!currentPr) {
throw new Error(`Pull request ${validatedParams.pullRequestId} not found`);
}
// Map merge strategy
let mergeStrategy: GitInterfaces.GitPullRequestMergeStrategy;
switch (validatedParams.mergeStrategy) {
case "squash":
mergeStrategy = GitInterfaces.GitPullRequestMergeStrategy.Squash;
break;
case "rebase":
mergeStrategy = GitInterfaces.GitPullRequestMergeStrategy.Rebase;
break;
case "rebaseMerge":
mergeStrategy = GitInterfaces.GitPullRequestMergeStrategy.RebaseMerge;
break;
default:
mergeStrategy = GitInterfaces.GitPullRequestMergeStrategy.NoFastForward;
}
const updatePr: GitInterfaces.GitPullRequest = {
status: GitInterfaces.PullRequestStatus.Completed,
lastMergeSourceCommit: currentPr.lastMergeSourceCommit,
completionOptions: {
mergeStrategy: mergeStrategy,
deleteSourceBranch: validatedParams.deleteSourceBranch,
mergeCommitMessage: validatedParams.commitMessage,
bypassPolicy: validatedParams.bypassPolicy,
},
};
const completedPr = await gitApi.updatePullRequest(
updatePr,
validatedParams.repository,
validatedParams.pullRequestId,
project
);
if (!completedPr) {
throw new Error(`Failed to complete pull request ${validatedParams.pullRequestId}`);
}
return {
id: completedPr.pullRequestId || 0,
status: "completed",
mergeCommitId: completedPr.lastMergeCommit?.commitId,
message: `Successfully completed pull request ${validatedParams.pullRequestId}`,
};
}