@pilatos/bitbucket-cli 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +12 -1
  2. package/dist/index.js +506 -24
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -83,7 +83,7 @@ bb pr list
83
83
  |----------|----------|
84
84
  | **Authentication** | `login`, `logout`, `status`, `token` |
85
85
  | **Repositories** | `clone`, `create`, `list`, `view`, `delete` |
86
- | **Pull Requests** | `create`, `list`, `view`, `edit`, `merge`, `approve`, `decline`, `checkout`, `diff` |
86
+ | **Pull Requests** | `create`, `list`, `view`, `edit`, `merge`, `approve`, `decline`, `ready`, `checkout`, `diff`, `comment`, `comments` |
87
87
  | **Configuration** | `get`, `set`, `list` |
88
88
  | **Shell Completion** | `install`, `uninstall` |
89
89
 
@@ -138,10 +138,21 @@ bb pr list
138
138
 
139
139
  # Review and merge
140
140
  bb pr view 42
141
+ bb pr activity 42
141
142
  bb pr approve 42
142
143
  bb pr merge 42
143
144
  ```
144
145
 
146
+ ### Draft Pull Requests
147
+
148
+ ```bash
149
+ # Create a draft PR for early feedback
150
+ bb pr create --title "WIP: Add new feature" --draft
151
+
152
+ # Mark it ready for review when done
153
+ bb pr ready 123
154
+ ```
155
+
145
156
  ### Scripting with JSON
146
157
 
147
158
  ```bash
package/dist/index.js CHANGED
@@ -4302,7 +4302,7 @@ var require_lodash = __commonJS((exports, module) => {
4302
4302
  function unicodeWords(string) {
4303
4303
  return string.match(reUnicodeWord) || [];
4304
4304
  }
4305
- var runInContext = function runInContext(context) {
4305
+ var runInContext = function runInContext2(context) {
4306
4306
  context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps));
4307
4307
  var { Array: Array2, Date: Date2, Error: Error2, Function: Function2, Math: Math2, Object: Object2, RegExp: RegExp2, String: String2, TypeError: TypeError2 } = context;
4308
4308
  var arrayProto = Array2.prototype, funcProto = Function2.prototype, objectProto = Object2.prototype;
@@ -8815,7 +8815,7 @@ __p += '`;
8815
8815
  lodash.replace = replace;
8816
8816
  lodash.result = result;
8817
8817
  lodash.round = round;
8818
- lodash.runInContext = runInContext;
8818
+ lodash.runInContext = runInContext2;
8819
8819
  lodash.sample = sample2;
8820
8820
  lodash.size = size;
8821
8821
  lodash.snakeCase = snakeCase;
@@ -17469,7 +17469,7 @@ var require_multicast = __commonJS((exports) => {
17469
17469
  if (typeof subjectOrSubjectFactory === "function") {
17470
17470
  subjectFactory = subjectOrSubjectFactory;
17471
17471
  } else {
17472
- subjectFactory = function subjectFactory() {
17472
+ subjectFactory = function subjectFactory2() {
17473
17473
  return subjectOrSubjectFactory;
17474
17474
  };
17475
17475
  }
@@ -20940,7 +20940,7 @@ var require_signal_exit = __commonJS((exports, module) => {
20940
20940
  emitter.on(ev, cb);
20941
20941
  return remove;
20942
20942
  };
20943
- unload = function unload() {
20943
+ unload = function unload2() {
20944
20944
  if (!loaded || !processOk(global.process)) {
20945
20945
  return;
20946
20946
  }
@@ -20955,7 +20955,7 @@ var require_signal_exit = __commonJS((exports, module) => {
20955
20955
  emitter.count -= 1;
20956
20956
  };
20957
20957
  module.exports.unload = unload;
20958
- emit = function emit(event, code, signal) {
20958
+ emit = function emit2(event, code, signal) {
20959
20959
  if (emitter.emitted[event]) {
20960
20960
  return;
20961
20961
  }
@@ -20984,7 +20984,7 @@ var require_signal_exit = __commonJS((exports, module) => {
20984
20984
  return signals;
20985
20985
  };
20986
20986
  loaded = false;
20987
- load = function load() {
20987
+ load = function load2() {
20988
20988
  if (loaded || !processOk(global.process)) {
20989
20989
  return;
20990
20990
  }
@@ -21003,7 +21003,7 @@ var require_signal_exit = __commonJS((exports, module) => {
21003
21003
  };
21004
21004
  module.exports.load = load;
21005
21005
  originalProcessReallyExit = process3.reallyExit;
21006
- processReallyExit = function processReallyExit(code) {
21006
+ processReallyExit = function processReallyExit2(code) {
21007
21007
  if (!processOk(global.process)) {
21008
21008
  return;
21009
21009
  }
@@ -21013,7 +21013,7 @@ var require_signal_exit = __commonJS((exports, module) => {
21013
21013
  originalProcessReallyExit.call(process3, process3.exitCode);
21014
21014
  };
21015
21015
  originalProcessEmit = process3.emit;
21016
- processEmit = function processEmit(ev, arg) {
21016
+ processEmit = function processEmit2(ev, arg) {
21017
21017
  if (ev === "exit" && processOk(global.process)) {
21018
21018
  if (arg !== undefined) {
21019
21019
  process3.exitCode = arg;
@@ -33262,8 +33262,14 @@ var ServiceTokens = {
33262
33262
  MergePRCommand: "MergePRCommand",
33263
33263
  ApprovePRCommand: "ApprovePRCommand",
33264
33264
  DeclinePRCommand: "DeclinePRCommand",
33265
+ ReadyPRCommand: "ReadyPRCommand",
33265
33266
  CheckoutPRCommand: "CheckoutPRCommand",
33266
33267
  DiffPRCommand: "DiffPRCommand",
33268
+ ActivityPRCommand: "ActivityPRCommand",
33269
+ CommentPRCommand: "CommentPRCommand",
33270
+ ListCommentsPRCommand: "ListCommentsPRCommand",
33271
+ EditCommentPRCommand: "EditCommentPRCommand",
33272
+ DeleteCommentPRCommand: "DeleteCommentPRCommand",
33267
33273
  GetConfigCommand: "GetConfigCommand",
33268
33274
  SetConfigCommand: "SetConfigCommand",
33269
33275
  ListConfigCommand: "ListConfigCommand",
@@ -34245,10 +34251,22 @@ class HttpClient {
34245
34251
  configService;
34246
34252
  baseUrl;
34247
34253
  timeout;
34254
+ debug;
34248
34255
  constructor(configService, config) {
34249
34256
  this.configService = configService;
34250
34257
  this.baseUrl = config?.baseUrl ?? "https://api.bitbucket.org/2.0";
34251
34258
  this.timeout = config?.timeout ?? 30000;
34259
+ this.debug = config?.debug ?? process.env.DEBUG === "true";
34260
+ }
34261
+ logDebug(message, data) {
34262
+ if (!this.debug) {
34263
+ return;
34264
+ }
34265
+ if (data === undefined) {
34266
+ console.log(`[debug] ${message}`);
34267
+ return;
34268
+ }
34269
+ console.log(`[debug] ${message}`, data);
34252
34270
  }
34253
34271
  async getAuthHeader() {
34254
34272
  const credentialsResult = await this.configService.getCredentials();
@@ -34270,18 +34288,47 @@ class HttpClient {
34270
34288
  Accept: acceptText ? "text/plain" : "application/json"
34271
34289
  };
34272
34290
  const url = `${this.baseUrl}${path}`;
34291
+ const requestBody = body ? JSON.stringify(body) : undefined;
34273
34292
  try {
34274
34293
  const controller = new AbortController;
34275
34294
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
34295
+ this.logDebug("Request", {
34296
+ url,
34297
+ method,
34298
+ headers,
34299
+ body: requestBody
34300
+ });
34276
34301
  const response = await fetch(url, {
34277
34302
  method,
34278
34303
  headers,
34279
- body: body ? JSON.stringify(body) : undefined,
34304
+ body: requestBody,
34280
34305
  signal: controller.signal
34281
34306
  });
34307
+ if (process.env.DEBUG === "true") {
34308
+ console.debug(`[HTTP] ${method} ${url} - ${response.status}`);
34309
+ console.debug(`[HTTP] Response Headers:`, Object.fromEntries(response.headers.entries()));
34310
+ console.debug(`[HTTP] Response Body:`, await response.clone().text());
34311
+ }
34282
34312
  clearTimeout(timeoutId);
34313
+ if (this.debug) {
34314
+ const responseClone = response.clone();
34315
+ const responseText = await responseClone.text();
34316
+ this.logDebug("Response", {
34317
+ url,
34318
+ method,
34319
+ status: response.status,
34320
+ statusText: response.statusText,
34321
+ headers: Object.fromEntries(response.headers.entries()),
34322
+ body: responseText
34323
+ });
34324
+ }
34283
34325
  return acceptText ? this.handleTextResponse(response) : this.handleResponse(response);
34284
34326
  } catch (error) {
34327
+ this.logDebug("Request error", {
34328
+ url,
34329
+ method,
34330
+ error: error instanceof Error ? error.message : error
34331
+ });
34285
34332
  if (error instanceof Error && error.name === "AbortError") {
34286
34333
  return Result.err(new BBError({
34287
34334
  code: 2001 /* API_REQUEST_FAILED */,
@@ -34394,6 +34441,16 @@ class UserRepository {
34394
34441
  return this.httpClient.get("/user");
34395
34442
  }
34396
34443
  }
34444
+ // src/constants.ts
34445
+ var API_PAGELEN_LIMITS = {
34446
+ PULL_REQUESTS: 50,
34447
+ REPOSITORIES: 100
34448
+ };
34449
+ var DEFAULT_PAGELEN = {
34450
+ PULL_REQUESTS: 25,
34451
+ REPOSITORIES: 25
34452
+ };
34453
+
34397
34454
  // src/repositories/repo.repository.ts
34398
34455
  class RepoRepository {
34399
34456
  httpClient;
@@ -34404,7 +34461,8 @@ class RepoRepository {
34404
34461
  return this.httpClient.get(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}`);
34405
34462
  }
34406
34463
  async list(workspace, limit = 25) {
34407
- return this.httpClient.get(`/repositories/${encodeURIComponent(workspace)}?pagelen=${limit}`);
34464
+ const safeLimit = Math.min(limit, API_PAGELEN_LIMITS.REPOSITORIES);
34465
+ return this.httpClient.get(`/repositories/${encodeURIComponent(workspace)}?pagelen=${safeLimit}`);
34408
34466
  }
34409
34467
  async create(workspace, request) {
34410
34468
  const repoSlug = request.name.toLowerCase().replace(/\s+/g, "-");
@@ -34428,7 +34486,8 @@ class PullRequestRepository {
34428
34486
  return this.httpClient.get(this.buildPath(workspace, repoSlug, `/${id}`));
34429
34487
  }
34430
34488
  async list(workspace, repoSlug, state = "OPEN", limit = 25) {
34431
- return this.httpClient.get(this.buildPath(workspace, repoSlug, `?state=${state}&pagelen=${limit}`));
34489
+ const safeLimit = Math.min(limit, API_PAGELEN_LIMITS.PULL_REQUESTS);
34490
+ return this.httpClient.get(this.buildPath(workspace, repoSlug, `?state=${state}&pagelen=${safeLimit}`));
34432
34491
  }
34433
34492
  async create(workspace, repoSlug, request) {
34434
34493
  return this.httpClient.post(this.buildPath(workspace, repoSlug), request);
@@ -34440,10 +34499,10 @@ class PullRequestRepository {
34440
34499
  return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/merge`), request);
34441
34500
  }
34442
34501
  async approve(workspace, repoSlug, id) {
34443
- return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/approve`));
34502
+ return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/approve`), {});
34444
34503
  }
34445
34504
  async decline(workspace, repoSlug, id) {
34446
- return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/decline`));
34505
+ return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/decline`), {});
34447
34506
  }
34448
34507
  async getDiff(workspace, repoSlug, id) {
34449
34508
  return this.httpClient.getText(this.buildPath(workspace, repoSlug, `/${id}/diff`));
@@ -34451,6 +34510,26 @@ class PullRequestRepository {
34451
34510
  async getDiffstat(workspace, repoSlug, id) {
34452
34511
  return this.httpClient.get(this.buildPath(workspace, repoSlug, `/${id}/diffstat`));
34453
34512
  }
34513
+ async listActivity(workspace, repoSlug, prId, limit = 25) {
34514
+ const safeLimit = Math.min(limit, API_PAGELEN_LIMITS.PULL_REQUESTS);
34515
+ return this.httpClient.get(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${prId}/activity?pagelen=${safeLimit}`);
34516
+ }
34517
+ async listComments(workspace, repoSlug, prId, limit = 25) {
34518
+ const safeLimit = Math.min(limit, API_PAGELEN_LIMITS.PULL_REQUESTS);
34519
+ return this.httpClient.get(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${prId}/comments?pagelen=${safeLimit}`);
34520
+ }
34521
+ async getComment(workspace, repoSlug, prId, commentId) {
34522
+ return this.httpClient.get(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${prId}/comments/${commentId}`);
34523
+ }
34524
+ async createComment(workspace, repoSlug, prId, content) {
34525
+ return this.httpClient.post(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${prId}/comments`, { content: { raw: content } });
34526
+ }
34527
+ async updateComment(workspace, repoSlug, prId, commentId, content) {
34528
+ return this.httpClient.put(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${prId}/comments/${commentId}`, { content: { raw: content } });
34529
+ }
34530
+ async deleteComment(workspace, repoSlug, prId, commentId) {
34531
+ return this.httpClient.delete(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${prId}/comments/${commentId}`);
34532
+ }
34454
34533
  }
34455
34534
  // src/core/base-command.ts
34456
34535
  class BaseCommand {
@@ -34954,6 +35033,9 @@ class CreatePRCommand extends BaseCommand {
34954
35033
  if (options.closeSourceBranch) {
34955
35034
  request.close_source_branch = true;
34956
35035
  }
35036
+ if (options.draft) {
35037
+ request.draft = true;
35038
+ }
34957
35039
  const result = await this.prRepository.create(workspace, repoSlug, request);
34958
35040
  this.handleResult(result, context, (pr) => {
34959
35041
  this.output.success(`Created pull request #${pr.id}`);
@@ -34993,12 +35075,15 @@ class ListPRsCommand extends BaseCommand {
34993
35075
  this.output.text(`No ${state.toLowerCase()} pull requests found`);
34994
35076
  return;
34995
35077
  }
34996
- const rows = data.values.map((pr) => [
34997
- `#${pr.id}`,
34998
- this.truncate(pr.title, 50),
34999
- pr.author.display_name,
35000
- `${pr.source.branch.name} → ${pr.destination.branch.name}`
35001
- ]);
35078
+ const rows = data.values.map((pr) => {
35079
+ const title = pr.draft ? `[DRAFT] ${pr.title}` : pr.title;
35080
+ return [
35081
+ `#${pr.id}`,
35082
+ this.truncate(title, 50),
35083
+ pr.author.display_name,
35084
+ `${pr.source.branch.name} → ${pr.destination.branch.name}`
35085
+ ];
35086
+ });
35002
35087
  this.output.table(["ID", "TITLE", "AUTHOR", "BRANCHES"], rows);
35003
35088
  });
35004
35089
  return result;
@@ -35036,7 +35121,8 @@ class ViewPRCommand extends BaseCommand {
35036
35121
  const result = await this.prRepository.get(workspace, repoSlug, prId);
35037
35122
  this.handleResult(result, context, (pr) => {
35038
35123
  const stateColor = this.getStateColor(pr.state);
35039
- this.output.text(`${source_default.bold(`#${pr.id}`)} ${pr.title} ${stateColor(`[${pr.state}]`)}`);
35124
+ const draftLabel = pr.draft ? source_default.yellow("[DRAFT]") : "";
35125
+ this.output.text(`${source_default.bold(`#${pr.id}`)} ${pr.title} ${stateColor(`[${pr.state}]`)}${draftLabel ? ` ${draftLabel}` : ""}`);
35040
35126
  this.output.text("");
35041
35127
  if (pr.description) {
35042
35128
  this.output.text(pr.description);
@@ -35109,7 +35195,7 @@ class EditPRCommand extends BaseCommand {
35109
35195
  return branchResult;
35110
35196
  }
35111
35197
  const currentBranch = branchResult.value;
35112
- const prsResult = await this.prRepository.list(workspace, repoSlug, "OPEN");
35198
+ const prsResult = await this.prRepository.list(workspace, repoSlug, "OPEN", DEFAULT_PAGELEN.PULL_REQUESTS);
35113
35199
  if (!prsResult.success) {
35114
35200
  this.handleResult(prsResult, context);
35115
35201
  return prsResult;
@@ -35267,6 +35353,40 @@ class DeclinePRCommand extends BaseCommand {
35267
35353
  }
35268
35354
  }
35269
35355
 
35356
+ // src/commands/pr/ready.command.ts
35357
+ class ReadyPRCommand extends BaseCommand {
35358
+ prRepository;
35359
+ contextService;
35360
+ name = "ready";
35361
+ description = "Mark a draft pull request as ready for review";
35362
+ constructor(prRepository, contextService, output) {
35363
+ super(output);
35364
+ this.prRepository = prRepository;
35365
+ this.contextService = contextService;
35366
+ }
35367
+ async execute(options, context) {
35368
+ const repoContextResult = await this.contextService.requireRepoContext({
35369
+ ...context.globalOptions,
35370
+ ...options
35371
+ });
35372
+ if (!repoContextResult.success) {
35373
+ this.handleResult(repoContextResult, context);
35374
+ return repoContextResult;
35375
+ }
35376
+ const { workspace, repoSlug } = repoContextResult.value;
35377
+ const prId = parseInt(options.id, 10);
35378
+ const request = {
35379
+ draft: false
35380
+ };
35381
+ const result = await this.prRepository.update(workspace, repoSlug, prId, request);
35382
+ this.handleResult(result, context, (pr) => {
35383
+ this.output.success(`Marked pull request #${prId} as ready for review`);
35384
+ this.output.text(` ${pr.title}`);
35385
+ });
35386
+ return result;
35387
+ }
35388
+ }
35389
+
35270
35390
  // src/commands/pr/checkout.command.ts
35271
35391
  class CheckoutPRCommand extends BaseCommand {
35272
35392
  prRepository;
@@ -35377,7 +35497,7 @@ class DiffPRCommand extends BaseCommand {
35377
35497
  return Result.err(error);
35378
35498
  }
35379
35499
  const currentBranch = currentBranchResult.value;
35380
- const prsResult = await this.prRepository.list(workspace, repoSlug, "OPEN", 100);
35500
+ const prsResult = await this.prRepository.list(workspace, repoSlug, "OPEN", DEFAULT_PAGELEN.PULL_REQUESTS);
35381
35501
  if (!prsResult.success) {
35382
35502
  this.handleResult(prsResult, context);
35383
35503
  return prsResult;
@@ -35543,6 +35663,282 @@ class DiffPRCommand extends BaseCommand {
35543
35663
  }
35544
35664
  }
35545
35665
 
35666
+ // src/commands/pr/activity.command.ts
35667
+ class ActivityPRCommand extends BaseCommand {
35668
+ prRepository;
35669
+ contextService;
35670
+ name = "activity";
35671
+ description = "Show pull request activity history";
35672
+ constructor(prRepository, contextService, output) {
35673
+ super(output);
35674
+ this.prRepository = prRepository;
35675
+ this.contextService = contextService;
35676
+ }
35677
+ async execute(options, context) {
35678
+ const repoContextResult = await this.contextService.requireRepoContext({
35679
+ ...context.globalOptions,
35680
+ ...options
35681
+ });
35682
+ if (!repoContextResult.success) {
35683
+ this.handleResult(repoContextResult, context);
35684
+ return repoContextResult;
35685
+ }
35686
+ const { workspace, repoSlug } = repoContextResult.value;
35687
+ const prId = parseInt(options.id, 10);
35688
+ const limit = options.limit ? parseInt(options.limit, 10) : DEFAULT_PAGELEN.PULL_REQUESTS;
35689
+ const result = await this.prRepository.listActivity(workspace, repoSlug, prId, limit);
35690
+ this.handleResult(result, context, (data) => {
35691
+ const filterTypes = this.parseTypeFilter(options.type);
35692
+ const activities = filterTypes.length > 0 ? data.values.filter((activity) => filterTypes.includes(this.getActivityType(activity))) : data.values;
35693
+ if (activities.length === 0) {
35694
+ if (filterTypes.length > 0) {
35695
+ this.output.info("No activity entries matched the requested filter");
35696
+ } else {
35697
+ this.output.info("No activity found on this pull request");
35698
+ }
35699
+ return;
35700
+ }
35701
+ const rows = activities.map((activity) => {
35702
+ const activityType = this.getActivityType(activity);
35703
+ return [
35704
+ activityType.toUpperCase(),
35705
+ this.getActorName(activity),
35706
+ this.formatActivityDate(activity),
35707
+ this.buildActivityDetails(activity, activityType)
35708
+ ];
35709
+ });
35710
+ this.output.table(["TYPE", "ACTOR", "DATE", "DETAILS"], rows);
35711
+ });
35712
+ return result;
35713
+ }
35714
+ parseTypeFilter(typeOption) {
35715
+ if (!typeOption) {
35716
+ return [];
35717
+ }
35718
+ return typeOption.split(",").map((type) => type.trim().toLowerCase()).filter((type) => type.length > 0);
35719
+ }
35720
+ getActivityType(activity) {
35721
+ if (activity.comment) {
35722
+ return "comment";
35723
+ }
35724
+ if (activity.approval) {
35725
+ return "approval";
35726
+ }
35727
+ if (activity.changes_requested) {
35728
+ return "changes_requested";
35729
+ }
35730
+ if (activity.merge) {
35731
+ return "merge";
35732
+ }
35733
+ if (activity.decline) {
35734
+ return "decline";
35735
+ }
35736
+ if (activity.commit) {
35737
+ return "commit";
35738
+ }
35739
+ if (activity.update) {
35740
+ return "update";
35741
+ }
35742
+ return activity.type ? activity.type.toLowerCase() : "activity";
35743
+ }
35744
+ getActorName(activity) {
35745
+ const user = activity.comment?.user ?? activity.comment?.author ?? activity.approval?.user ?? activity.update?.author ?? activity.changes_requested?.user ?? activity.merge?.user ?? activity.decline?.user ?? activity.commit?.author?.user ?? activity.user;
35746
+ if (!user) {
35747
+ return "Unknown";
35748
+ }
35749
+ return user.display_name || user.username || "Unknown";
35750
+ }
35751
+ formatActivityDate(activity) {
35752
+ const date = activity.comment?.created_on ?? activity.approval?.date ?? activity.update?.date ?? activity.changes_requested?.date ?? activity.merge?.date ?? activity.decline?.date ?? activity.commit?.date;
35753
+ if (!date) {
35754
+ return "-";
35755
+ }
35756
+ return this.output.formatDate(date);
35757
+ }
35758
+ buildActivityDetails(activity, type) {
35759
+ switch (type) {
35760
+ case "comment": {
35761
+ const content = activity.comment?.content?.raw ?? "";
35762
+ const id = activity.comment?.id ? `#${activity.comment.id}` : "";
35763
+ const snippet = this.truncate(content, 80);
35764
+ return [id, snippet].filter(Boolean).join(" ");
35765
+ }
35766
+ case "approval":
35767
+ return "approved";
35768
+ case "changes_requested": {
35769
+ const reason = activity.changes_requested?.reason;
35770
+ return reason ? this.truncate(reason, 80) : "changes requested";
35771
+ }
35772
+ case "merge":
35773
+ return this.formatCommitDetail(activity.merge?.commit?.hash, "merged");
35774
+ case "decline":
35775
+ return "declined";
35776
+ case "commit":
35777
+ return this.formatCommitDetail(activity.commit?.hash, "commit");
35778
+ case "update": {
35779
+ if (activity.update?.state) {
35780
+ return `state: ${activity.update.state}`;
35781
+ }
35782
+ if (activity.update?.title) {
35783
+ return `title: ${this.truncate(activity.update.title, 60)}`;
35784
+ }
35785
+ if (activity.update?.description) {
35786
+ return "description updated";
35787
+ }
35788
+ return "updated";
35789
+ }
35790
+ default:
35791
+ return "";
35792
+ }
35793
+ }
35794
+ formatCommitDetail(hash, label) {
35795
+ if (!hash) {
35796
+ return label ?? "";
35797
+ }
35798
+ const shortHash = hash.slice(0, 7);
35799
+ return label ? `${label} ${shortHash}` : shortHash;
35800
+ }
35801
+ truncate(text, maxLength) {
35802
+ if (text.length <= maxLength) {
35803
+ return text;
35804
+ }
35805
+ return text.substring(0, maxLength - 3) + "...";
35806
+ }
35807
+ }
35808
+
35809
+ // src/commands/pr/comment.command.ts
35810
+ class CommentPRCommand extends BaseCommand {
35811
+ prRepository;
35812
+ contextService;
35813
+ name = "comment";
35814
+ description = "Add a comment to a pull request";
35815
+ constructor(prRepository, contextService, output) {
35816
+ super(output);
35817
+ this.prRepository = prRepository;
35818
+ this.contextService = contextService;
35819
+ }
35820
+ async execute(options, context) {
35821
+ const repoContextResult = await this.contextService.requireRepoContext({
35822
+ ...context.globalOptions,
35823
+ ...options
35824
+ });
35825
+ if (!repoContextResult.success) {
35826
+ this.handleResult(repoContextResult, context);
35827
+ return repoContextResult;
35828
+ }
35829
+ const { workspace, repoSlug } = repoContextResult.value;
35830
+ const prId = parseInt(options.id, 10);
35831
+ const result = await this.prRepository.createComment(workspace, repoSlug, prId, options.message);
35832
+ this.handleResult(result, context, () => {
35833
+ this.output.success(`Added comment to pull request #${prId}`);
35834
+ });
35835
+ return result;
35836
+ }
35837
+ }
35838
+
35839
+ // src/commands/pr/comments.list.command.ts
35840
+ class ListCommentsPRCommand extends BaseCommand {
35841
+ prRepository;
35842
+ contextService;
35843
+ name = "comments";
35844
+ description = "List comments on a pull request";
35845
+ constructor(prRepository, contextService, output) {
35846
+ super(output);
35847
+ this.prRepository = prRepository;
35848
+ this.contextService = contextService;
35849
+ }
35850
+ async execute(options, context) {
35851
+ const repoContextResult = await this.contextService.requireRepoContext({
35852
+ ...context.globalOptions,
35853
+ ...options
35854
+ });
35855
+ if (!repoContextResult.success) {
35856
+ this.handleResult(repoContextResult, context);
35857
+ return repoContextResult;
35858
+ }
35859
+ const { workspace, repoSlug } = repoContextResult.value;
35860
+ const prId = parseInt(options.id, 10);
35861
+ const limit = options.limit ? parseInt(options.limit, 10) : 25;
35862
+ const result = await this.prRepository.listComments(workspace, repoSlug, prId, limit);
35863
+ this.handleResult(result, context, (data) => {
35864
+ if (data.values.length === 0) {
35865
+ this.output.info("No comments found on this pull request");
35866
+ return;
35867
+ }
35868
+ const rows = data.values.map((comment) => [
35869
+ comment.id.toString(),
35870
+ comment.author?.username ?? comment.author?.display_name ?? "Unknown",
35871
+ comment.content.raw.slice(0, 60) + (comment.content.raw.length > 60 ? "..." : ""),
35872
+ this.output.formatDate(comment.created_on)
35873
+ ]);
35874
+ this.output.table(["ID", "Author", "Content", "Date"], rows);
35875
+ });
35876
+ return result;
35877
+ }
35878
+ }
35879
+
35880
+ // src/commands/pr/comments.edit.command.ts
35881
+ class EditCommentPRCommand extends BaseCommand {
35882
+ prRepository;
35883
+ contextService;
35884
+ name = "edit";
35885
+ description = "Edit a comment on a pull request";
35886
+ constructor(prRepository, contextService, output) {
35887
+ super(output);
35888
+ this.prRepository = prRepository;
35889
+ this.contextService = contextService;
35890
+ }
35891
+ async execute(options, context) {
35892
+ const repoContextResult = await this.contextService.requireRepoContext({
35893
+ ...context.globalOptions,
35894
+ ...options
35895
+ });
35896
+ if (!repoContextResult.success) {
35897
+ this.handleResult(repoContextResult, context);
35898
+ return repoContextResult;
35899
+ }
35900
+ const { workspace, repoSlug } = repoContextResult.value;
35901
+ const prId = parseInt(options.prId, 10);
35902
+ const commentId = parseInt(options.commentId, 10);
35903
+ const result = await this.prRepository.updateComment(workspace, repoSlug, prId, commentId, options.message);
35904
+ this.handleResult(result, context, () => {
35905
+ this.output.success(`Updated comment #${commentId}`);
35906
+ });
35907
+ return result;
35908
+ }
35909
+ }
35910
+
35911
+ // src/commands/pr/comments.delete.command.ts
35912
+ class DeleteCommentPRCommand extends BaseCommand {
35913
+ prRepository;
35914
+ contextService;
35915
+ name = "delete";
35916
+ description = "Delete a comment on a pull request";
35917
+ constructor(prRepository, contextService, output) {
35918
+ super(output);
35919
+ this.prRepository = prRepository;
35920
+ this.contextService = contextService;
35921
+ }
35922
+ async execute(options, context) {
35923
+ const repoContextResult = await this.contextService.requireRepoContext({
35924
+ ...context.globalOptions,
35925
+ ...options
35926
+ });
35927
+ if (!repoContextResult.success) {
35928
+ this.handleResult(repoContextResult, context);
35929
+ return repoContextResult;
35930
+ }
35931
+ const { workspace, repoSlug } = repoContextResult.value;
35932
+ const prId = parseInt(options.prId, 10);
35933
+ const commentId = parseInt(options.commentId, 10);
35934
+ const result = await this.prRepository.deleteComment(workspace, repoSlug, prId, commentId);
35935
+ this.handleResult(result, context, () => {
35936
+ this.output.success(`Deleted comment #${commentId} from PR #${prId}`);
35937
+ });
35938
+ return result;
35939
+ }
35940
+ }
35941
+
35546
35942
  // src/types/config.ts
35547
35943
  var SETTABLE_CONFIG_KEYS = ["defaultWorkspace"];
35548
35944
  var READABLE_CONFIG_KEYS = ["username", "defaultWorkspace"];
@@ -35845,6 +36241,12 @@ function bootstrap() {
35845
36241
  const output = container.resolve(ServiceTokens.OutputService);
35846
36242
  return new DeclinePRCommand(prRepo, contextService, output);
35847
36243
  });
36244
+ container.register(ServiceTokens.ReadyPRCommand, () => {
36245
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
36246
+ const contextService = container.resolve(ServiceTokens.ContextService);
36247
+ const output = container.resolve(ServiceTokens.OutputService);
36248
+ return new ReadyPRCommand(prRepo, contextService, output);
36249
+ });
35848
36250
  container.register(ServiceTokens.CheckoutPRCommand, () => {
35849
36251
  const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
35850
36252
  const contextService = container.resolve(ServiceTokens.ContextService);
@@ -35859,6 +36261,36 @@ function bootstrap() {
35859
36261
  const output = container.resolve(ServiceTokens.OutputService);
35860
36262
  return new DiffPRCommand(prRepo, contextService, gitService, output);
35861
36263
  });
36264
+ container.register(ServiceTokens.ActivityPRCommand, () => {
36265
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
36266
+ const contextService = container.resolve(ServiceTokens.ContextService);
36267
+ const output = container.resolve(ServiceTokens.OutputService);
36268
+ return new ActivityPRCommand(prRepo, contextService, output);
36269
+ });
36270
+ container.register(ServiceTokens.CommentPRCommand, () => {
36271
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
36272
+ const contextService = container.resolve(ServiceTokens.ContextService);
36273
+ const output = container.resolve(ServiceTokens.OutputService);
36274
+ return new CommentPRCommand(prRepo, contextService, output);
36275
+ });
36276
+ container.register(ServiceTokens.ListCommentsPRCommand, () => {
36277
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
36278
+ const contextService = container.resolve(ServiceTokens.ContextService);
36279
+ const output = container.resolve(ServiceTokens.OutputService);
36280
+ return new ListCommentsPRCommand(prRepo, contextService, output);
36281
+ });
36282
+ container.register(ServiceTokens.EditCommentPRCommand, () => {
36283
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
36284
+ const contextService = container.resolve(ServiceTokens.ContextService);
36285
+ const output = container.resolve(ServiceTokens.OutputService);
36286
+ return new EditCommentPRCommand(prRepo, contextService, output);
36287
+ });
36288
+ container.register(ServiceTokens.DeleteCommentPRCommand, () => {
36289
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
36290
+ const contextService = container.resolve(ServiceTokens.ContextService);
36291
+ const output = container.resolve(ServiceTokens.OutputService);
36292
+ return new DeleteCommentPRCommand(prRepo, contextService, output);
36293
+ });
35862
36294
  container.register(ServiceTokens.GetConfigCommand, () => {
35863
36295
  const configService = container.resolve(ServiceTokens.ConfigService);
35864
36296
  const output = container.resolve(ServiceTokens.OutputService);
@@ -35909,7 +36341,7 @@ if (process.argv.includes("--get-yargs-completions") || process.env.COMP_LINE) {
35909
36341
  } else if (env2.prev === "repo") {
35910
36342
  completions.push("clone", "create", "list", "view", "delete");
35911
36343
  } else if (env2.prev === "pr") {
35912
- completions.push("create", "list", "view", "edit", "merge", "approve", "decline", "checkout", "diff");
36344
+ completions.push("create", "list", "view", "activity", "edit", "merge", "approve", "decline", "ready", "checkout", "diff", "comments");
35913
36345
  } else if (env2.prev === "config") {
35914
36346
  completions.push("get", "set", "list");
35915
36347
  } else if (env2.prev === "completion") {
@@ -36011,7 +36443,7 @@ repoCmd.command("delete <repository>").description("Delete a repository").option
36011
36443
  });
36012
36444
  cli.addCommand(repoCmd);
36013
36445
  var prCmd = new Command("pr").description("Manage pull requests");
36014
- prCmd.command("create").description("Create a pull request").option("-t, --title <title>", "Pull request title").option("-b, --body <body>", "Pull request description").option("-s, --source <branch>", "Source branch (default: current branch)").option("-d, --destination <branch>", "Destination branch (default: main)").option("--close-source-branch", "Close source branch after merge").action(async (options) => {
36446
+ prCmd.command("create").description("Create a pull request").option("-t, --title <title>", "Pull request title").option("-b, --body <body>", "Pull request description").option("-s, --source <branch>", "Source branch (default: current branch)").option("-d, --destination <branch>", "Destination branch (default: main)").option("--close-source-branch", "Close source branch after merge").option("--draft", "Create the pull request as draft").action(async (options) => {
36015
36447
  const cmd = container.resolve(ServiceTokens.CreatePRCommand);
36016
36448
  const context = createContext(cli);
36017
36449
  const result = await cmd.execute(withGlobalOptions(options, context), context);
@@ -36035,6 +36467,14 @@ prCmd.command("view <id>").description("View pull request details").action(async
36035
36467
  process.exit(1);
36036
36468
  }
36037
36469
  });
36470
+ prCmd.command("activity <id>").description("Show pull request activity log").option("--limit <number>", "Maximum number of activity entries", "25").option("--type <types>", "Filter activity by type (comma-separated)").action(async (id, options) => {
36471
+ const cmd = container.resolve(ServiceTokens.ActivityPRCommand);
36472
+ const context = createContext(cli);
36473
+ const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
36474
+ if (!result.success) {
36475
+ process.exit(1);
36476
+ }
36477
+ });
36038
36478
  prCmd.command("edit [id]").description("Edit a pull request").option("-t, --title <title>", "New pull request title").option("-b, --body <body>", "New pull request description").option("-F, --body-file <file>", "Read description from file").action(async (id, options) => {
36039
36479
  const cmd = container.resolve(ServiceTokens.EditPRCommand);
36040
36480
  const context = createContext(cli);
@@ -36067,6 +36507,14 @@ prCmd.command("decline <id>").description("Decline a pull request").action(async
36067
36507
  process.exit(1);
36068
36508
  }
36069
36509
  });
36510
+ prCmd.command("ready <id>").description("Mark a draft pull request as ready for review").action(async (id, options) => {
36511
+ const cmd = container.resolve(ServiceTokens.ReadyPRCommand);
36512
+ const context = createContext(cli);
36513
+ const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
36514
+ if (!result.success) {
36515
+ process.exit(1);
36516
+ }
36517
+ });
36070
36518
  prCmd.command("checkout <id>").description("Checkout a pull request locally").action(async (id, options) => {
36071
36519
  const cmd = container.resolve(ServiceTokens.CheckoutPRCommand);
36072
36520
  const context = createContext(cli);
@@ -36083,7 +36531,41 @@ prCmd.command("diff [id]").description("View pull request diff").option("--color
36083
36531
  process.exit(1);
36084
36532
  }
36085
36533
  });
36534
+ var prCommentsCmd = new Command("comments").description("Manage pull request comments");
36535
+ prCommentsCmd.command("list <id>").description("List comments on a pull request").option("--limit <number>", "Maximum number of comments (default: 25)").action(async (id, options) => {
36536
+ const cmd = container.resolve(ServiceTokens.ListCommentsPRCommand);
36537
+ const context = createContext(cli);
36538
+ const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
36539
+ if (!result.success) {
36540
+ process.exit(1);
36541
+ }
36542
+ });
36543
+ prCommentsCmd.command("add <id> <message>").description("Add a comment to a pull request").action(async (id, message, options) => {
36544
+ const cmd = container.resolve(ServiceTokens.CommentPRCommand);
36545
+ const context = createContext(cli);
36546
+ const result = await cmd.execute(withGlobalOptions({ id, message }, context), context);
36547
+ if (!result.success) {
36548
+ process.exit(1);
36549
+ }
36550
+ });
36551
+ prCommentsCmd.command("edit <pr-id> <comment-id> <message>").description("Edit a comment on a pull request").action(async (prId, commentId, message, options) => {
36552
+ const cmd = container.resolve(ServiceTokens.EditCommentPRCommand);
36553
+ const context = createContext(cli);
36554
+ const result = await cmd.execute(withGlobalOptions({ prId, commentId, message }, context), context);
36555
+ if (!result.success) {
36556
+ process.exit(1);
36557
+ }
36558
+ });
36559
+ prCommentsCmd.command("delete <pr-id> <comment-id>").description("Delete a comment on a pull request").action(async (prId, commentId, options) => {
36560
+ const cmd = container.resolve(ServiceTokens.DeleteCommentPRCommand);
36561
+ const context = createContext(cli);
36562
+ const result = await cmd.execute(withGlobalOptions({ prId, commentId }, context), context);
36563
+ if (!result.success) {
36564
+ process.exit(1);
36565
+ }
36566
+ });
36086
36567
  cli.addCommand(prCmd);
36568
+ prCmd.addCommand(prCommentsCmd);
36087
36569
  var configCmd = new Command("config").description("Manage configuration");
36088
36570
  configCmd.command("get <key>").description("Get a configuration value").action(async (key) => {
36089
36571
  const cmd = container.resolve(ServiceTokens.GetConfigCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pilatos/bitbucket-cli",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "A command-line interface for Bitbucket Cloud",
5
5
  "author": "",
6
6
  "license": "MIT",