cli / mcp for bitbucket
1import {
2 getRepositoriesByWorkspaceByRepoSlugPullrequests,
3 getRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestId,
4 getRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestIdComments,
5 getRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestIdDiff,
6 postRepositoriesByWorkspaceByRepoSlugPullrequests,
7 postRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestIdComments,
8 postRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestIdDecline,
9 putRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestId,
10} from '../generated/bitbucket-client/sdk.gen';
11import type { Pullrequest, PullrequestComment } from '../generated/bitbucket-client/types.gen';
12import type { PaginationParams } from '../types/pagination.types';
13import { apiError, fail, ok, type Result } from '../types/result.types';
14
15export type RepositoryParams = {
16 workspace: string;
17 repoSlug: string;
18};
19
20export type PullRequestParams = RepositoryParams & {
21 prId: number;
22};
23
24export type ListPullRequestsParams = RepositoryParams &
25 PaginationParams & {
26 state?: 'OPEN' | 'MERGED' | 'DECLINED' | 'SUPERSEDED';
27 };
28
29export type CreatePullRequestParams = RepositoryParams & {
30 sourceBranch: string;
31 destinationBranch: string;
32 title: string;
33 description?: string;
34};
35
36export type UpdatePullRequestParams = PullRequestParams & {
37 updates: Partial<Pick<Pullrequest, 'title' | 'description' | 'destination'>>;
38};
39
40export type AddCommentParams = PullRequestParams & {
41 content: string;
42};
43
44export const listPullRequests = async (
45 params: ListPullRequestsParams
46): Promise<Result<Pullrequest[]>> => {
47 const { workspace, repoSlug, state = 'OPEN' } = params;
48 const response = await getRepositoriesByWorkspaceByRepoSlugPullrequests({
49 path: { workspace, repo_slug: repoSlug },
50 query: { state },
51 });
52
53 if (response.error) {
54 return fail(apiError(response.response.status, `Failed to list PRs`, response.error));
55 }
56
57 return ok(response.data?.values ?? []);
58};
59
60export const getPullRequest = async (params: PullRequestParams): Promise<Result<Pullrequest>> => {
61 const { workspace, repoSlug, prId } = params;
62 const response = await getRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestId({
63 path: { workspace, repo_slug: repoSlug, pull_request_id: prId },
64 });
65
66 if (response.error) {
67 return fail(apiError(response.response.status, `PR #${prId} not found`, response.error));
68 }
69
70 return response.data ? ok(response.data) : fail(apiError(404, `PR #${prId} not found`));
71};
72
73export const createPullRequest = async (
74 params: CreatePullRequestParams
75): Promise<Result<Pullrequest>> => {
76 const { workspace, repoSlug, sourceBranch, destinationBranch, title, description } = params;
77 const response = await postRepositoriesByWorkspaceByRepoSlugPullrequests({
78 path: { workspace, repo_slug: repoSlug },
79 body: {
80 type: 'pullrequest',
81 title,
82 description: description ?? '',
83 source: { branch: { name: sourceBranch } },
84 destination: { branch: { name: destinationBranch } },
85 },
86 });
87
88 if (response.error) {
89 return fail(apiError(response.response.status, `Failed to create PR`, response.error));
90 }
91
92 return response.data
93 ? ok(response.data)
94 : fail(apiError(500, 'No data returned from PR creation'));
95};
96
97export const updatePullRequest = async (
98 params: UpdatePullRequestParams
99): Promise<Result<Pullrequest>> => {
100 const { workspace, repoSlug, prId, updates } = params;
101 const response = await putRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestId({
102 path: { workspace, repo_slug: repoSlug, pull_request_id: prId },
103 body: { type: 'pullrequest', ...updates },
104 });
105
106 if (response.error) {
107 return fail(apiError(response.response.status, `Failed to update PR #${prId}`, response.error));
108 }
109
110 return response.data ? ok(response.data) : fail(apiError(500, 'No data returned from PR update'));
111};
112
113export const addComment = async (params: AddCommentParams): Promise<Result<void>> => {
114 const { workspace, repoSlug, prId, content } = params;
115 const response = await postRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestIdComments({
116 path: { workspace, repo_slug: repoSlug, pull_request_id: prId },
117 body: { type: 'pullrequest_comment', content: { raw: content } },
118 });
119
120 if (response.error) {
121 return fail(apiError(response.response.status, `Failed to add comment`, response.error));
122 }
123
124 return ok(undefined);
125};
126
127export const getPullRequestComments = async (
128 params: PullRequestParams & PaginationParams
129): Promise<Result<PullrequestComment[]>> => {
130 const { workspace, repoSlug, prId } = params;
131 const response = await getRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestIdComments({
132 path: { workspace, repo_slug: repoSlug, pull_request_id: prId },
133 });
134
135 if (response.error) {
136 return fail(apiError(response.response.status, `Failed to get PR comments`, response.error));
137 }
138
139 return ok(response.data?.values ?? []);
140};
141
142export const declinePullRequest = async (params: PullRequestParams): Promise<Result<void>> => {
143 const { workspace, repoSlug, prId } = params;
144 const response = await postRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestIdDecline({
145 path: { workspace, repo_slug: repoSlug, pull_request_id: prId },
146 });
147
148 if (response.error) {
149 return fail(
150 apiError(response.response.status, `Failed to decline PR #${prId}`, response.error)
151 );
152 }
153
154 return ok(undefined);
155};
156
157export const getPullRequestDiff = async (params: PullRequestParams): Promise<Result<string>> => {
158 const { workspace, repoSlug, prId } = params;
159 const response = await getRepositoriesByWorkspaceByRepoSlugPullrequestsByPullRequestIdDiff({
160 path: { workspace, repo_slug: repoSlug, pull_request_id: prId },
161 parseAs: 'text',
162 });
163
164 if (response.error) {
165 return fail(apiError(response.response.status, `Failed to get PR diff`, response.error));
166 }
167
168 return ok((response.data as unknown as string) ?? '');
169};