@pilatos/bitbucket-cli 1.15.0 → 1.16.1

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.
package/dist/index.js CHANGED
@@ -17440,6 +17440,8 @@ var ServiceTokens = {
17440
17440
  AddDefaultReviewerCommand: "AddDefaultReviewerCommand",
17441
17441
  RemoveDefaultReviewerCommand: "RemoveDefaultReviewerCommand",
17442
17442
  DefaultReviewerService: "DefaultReviewerService",
17443
+ UrlBuilderService: "UrlBuilderService",
17444
+ BrowseCommand: "BrowseCommand",
17443
17445
  CreatePRCommand: "CreatePRCommand",
17444
17446
  ListPRsCommand: "ListPRsCommand",
17445
17447
  ViewPRCommand: "ViewPRCommand",
@@ -17631,7 +17633,16 @@ class ConfigService {
17631
17633
  await this.verifyPermissions(this.configDir, CONFIG_DIR_MODE, "directory");
17632
17634
  await this.verifyPermissions(this.configFile, CONFIG_FILE_MODE, "file");
17633
17635
  const data = await fs.readFile(this.configFile, "utf-8");
17634
- this.configCache = JSON.parse(data);
17636
+ try {
17637
+ this.configCache = JSON.parse(data);
17638
+ } catch (parseError) {
17639
+ throw new BBError({
17640
+ code: 4001 /* CONFIG_READ_FAILED */,
17641
+ message: `Config file is not valid JSON: ${this.configFile}. Fix the file by hand or remove it and run \`bb auth login\` again.`,
17642
+ cause: parseError instanceof Error ? parseError : undefined,
17643
+ context: { configFile: this.configFile }
17644
+ });
17645
+ }
17635
17646
  return this.configCache;
17636
17647
  } catch (error) {
17637
17648
  if (error.code === "ENOENT") {
@@ -17769,10 +17780,14 @@ class ConfigService {
17769
17780
  }
17770
17781
  }
17771
17782
  // src/services/git.service.ts
17783
+ var DEFAULT_GIT_TIMEOUT_MS = 60000;
17784
+
17772
17785
  class GitService {
17773
17786
  cwd;
17774
- constructor(cwd) {
17787
+ timeoutMs;
17788
+ constructor(cwd, options = {}) {
17775
17789
  this.cwd = cwd ?? process.cwd();
17790
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_GIT_TIMEOUT_MS;
17776
17791
  }
17777
17792
  async exec(args, cwd) {
17778
17793
  const proc = Bun.spawn(["git", ...args], {
@@ -17780,14 +17795,28 @@ class GitService {
17780
17795
  stdout: "pipe",
17781
17796
  stderr: "pipe"
17782
17797
  });
17783
- const stdout = await new Response(proc.stdout).text();
17784
- const stderr = await new Response(proc.stderr).text();
17785
- const exitCode = await proc.exited;
17786
- return {
17787
- stdout: stdout.trim(),
17788
- stderr: stderr.trim(),
17789
- exitCode
17790
- };
17798
+ let timedOut = false;
17799
+ const timer = setTimeout(() => {
17800
+ timedOut = true;
17801
+ try {
17802
+ proc.kill();
17803
+ } catch {}
17804
+ }, this.timeoutMs);
17805
+ try {
17806
+ const stdout = await new Response(proc.stdout).text();
17807
+ const stderr = await new Response(proc.stderr).text();
17808
+ const exitCode = await proc.exited;
17809
+ if (timedOut) {
17810
+ throw new GitError(`git ${args.join(" ")} timed out after ${this.timeoutMs}ms`, `git ${args.join(" ")}`, exitCode);
17811
+ }
17812
+ return {
17813
+ stdout: stdout.trim(),
17814
+ stderr: stderr.trim(),
17815
+ exitCode
17816
+ };
17817
+ } finally {
17818
+ clearTimeout(timer);
17819
+ }
17791
17820
  }
17792
17821
  async execOrError(args, cwd) {
17793
17822
  const result = await this.exec(args, cwd);
@@ -17823,6 +17852,9 @@ class GitService {
17823
17852
  async getCurrentBranch() {
17824
17853
  return this.execOrError(["rev-parse", "--abbrev-ref", "HEAD"]);
17825
17854
  }
17855
+ async getCurrentCommit() {
17856
+ return this.execOrError(["rev-parse", "HEAD"]);
17857
+ }
17826
17858
  async getRemoteUrl(remote = "origin") {
17827
17859
  const result = await this.exec(["remote", "get-url", remote]);
17828
17860
  if (result.exitCode !== 0) {
@@ -17835,7 +17867,7 @@ class GitService {
17835
17867
  return result.stdout;
17836
17868
  }
17837
17869
  withCwd(cwd) {
17838
- return new GitService(cwd);
17870
+ return new GitService(cwd, { timeoutMs: this.timeoutMs });
17839
17871
  }
17840
17872
  }
17841
17873
  // src/services/context.service.ts
@@ -17935,6 +17967,12 @@ class ContextService {
17935
17967
  }
17936
17968
  return result.context;
17937
17969
  }
17970
+ async requireRepoContextFor(options, context) {
17971
+ return this.requireRepoContext({
17972
+ ...context.globalOptions,
17973
+ ...options
17974
+ });
17975
+ }
17938
17976
  buildRepoNotFoundMessage(reason, remoteUrl) {
17939
17977
  const fallback = "Use --workspace and --repo options, or run this command from within a Bitbucket repository.";
17940
17978
  switch (reason) {
@@ -18566,6 +18604,15 @@ class OutputService {
18566
18604
  text(message) {
18567
18605
  console.log(stripControl(message));
18568
18606
  }
18607
+ truncate(text, maxLength, suffix = "...") {
18608
+ if (maxLength <= 0 || text.length <= maxLength) {
18609
+ return text;
18610
+ }
18611
+ if (suffix.length >= maxLength) {
18612
+ return text.slice(0, maxLength);
18613
+ }
18614
+ return text.slice(0, maxLength - suffix.length) + suffix;
18615
+ }
18569
18616
  formatDate(date) {
18570
18617
  const d = typeof date === "string" ? new Date(date) : date;
18571
18618
  return d.toLocaleDateString("en-US", {
@@ -18635,7 +18682,17 @@ function projectByFieldsRespectingWrapper(data, fields) {
18635
18682
  return projectFields(data, fields);
18636
18683
  }
18637
18684
  async function runJq(data, expression) {
18638
- const jq = await Promise.resolve().then(() => __toESM(require_dist(), 1));
18685
+ let jq;
18686
+ try {
18687
+ jq = await Promise.resolve().then(() => __toESM(require_dist(), 1));
18688
+ } catch (error) {
18689
+ throw new BBError({
18690
+ code: 8001 /* JQ_FAILED */,
18691
+ message: "Failed to load the embedded jq runtime (jq-wasm). Reinstall the CLI or report this issue.",
18692
+ cause: error instanceof Error ? error : undefined,
18693
+ context: { expression }
18694
+ });
18695
+ }
18639
18696
  let result;
18640
18697
  try {
18641
18698
  result = await jq.raw(data, expression);
@@ -18807,7 +18864,11 @@ class VersionService {
18807
18864
  latestVersion,
18808
18865
  updateAvailable
18809
18866
  };
18810
- } catch {
18867
+ } catch (error) {
18868
+ if (process.env.DEBUG === "true") {
18869
+ const message = error instanceof Error ? error.message : String(error);
18870
+ console.error(`[version-check] skipped: ${message}`);
18871
+ }
18811
18872
  return null;
18812
18873
  }
18813
18874
  }
@@ -22941,7 +23002,12 @@ class OAuthService {
22941
23002
  try {
22942
23003
  const open2 = (await Promise.resolve().then(() => (init_open(), exports_open))).default;
22943
23004
  await open2(authUrl);
22944
- } catch {}
23005
+ } catch (err) {
23006
+ if (process.env.DEBUG === "true") {
23007
+ const message = err instanceof Error ? err.message : String(err);
23008
+ console.error(`[oauth] could not open browser: ${message}`);
23009
+ }
23010
+ }
22945
23011
  console.error(`If the browser doesn't open, visit:
22946
23012
  ${authUrl}
22947
23013
  `);
@@ -23376,6 +23442,67 @@ function accountToEntry(account) {
23376
23442
  nickname: asUser.nickname
23377
23443
  };
23378
23444
  }
23445
+ // src/services/url-builder.service.ts
23446
+ var BITBUCKET_WEB_BASE = "https://bitbucket.org";
23447
+ function encodePathSegments(path3) {
23448
+ return path3.split("/").map((segment) => encodeURIComponent(segment)).join("/");
23449
+ }
23450
+
23451
+ class UrlBuilderService {
23452
+ base;
23453
+ constructor(base = BITBUCKET_WEB_BASE) {
23454
+ this.base = base.replace(/\/+$/, "");
23455
+ }
23456
+ repo(ctx) {
23457
+ return this.repoBase(ctx);
23458
+ }
23459
+ src(ctx, branch, path3, line) {
23460
+ const encodedBranch = encodeURIComponent(branch);
23461
+ const trimmedPath = path3?.replace(/^\/+/, "").replace(/\/+$/, "") ?? "";
23462
+ const pathPart = trimmedPath ? `/${encodePathSegments(trimmedPath)}` : "/";
23463
+ const lineFragment = typeof line === "number" && Number.isFinite(line) && line > 0 ? `#lines-${line}` : "";
23464
+ return `${this.repoBase(ctx)}/src/${encodedBranch}${pathPart}${lineFragment}`;
23465
+ }
23466
+ branchList(ctx) {
23467
+ return `${this.repoBase(ctx)}/branches/`;
23468
+ }
23469
+ commit(ctx, sha) {
23470
+ return `${this.repoBase(ctx)}/commits/${encodeURIComponent(sha)}`;
23471
+ }
23472
+ commitList(ctx) {
23473
+ return `${this.repoBase(ctx)}/commits/`;
23474
+ }
23475
+ pullRequest(ctx, id) {
23476
+ return `${this.repoBase(ctx)}/pull-requests/${id}`;
23477
+ }
23478
+ pullRequestList(ctx) {
23479
+ return `${this.repoBase(ctx)}/pull-requests/`;
23480
+ }
23481
+ pipelinesHome(ctx) {
23482
+ return `${this.repoBase(ctx)}/pipelines`;
23483
+ }
23484
+ pipelineRun(ctx, idOrUuid) {
23485
+ return `${this.repoBase(ctx)}/pipelines/results/${encodeURIComponent(idOrUuid)}`;
23486
+ }
23487
+ downloads(ctx) {
23488
+ return `${this.repoBase(ctx)}/downloads/`;
23489
+ }
23490
+ issue(ctx, id) {
23491
+ return `${this.repoBase(ctx)}/issues/${id}`;
23492
+ }
23493
+ issueList(ctx) {
23494
+ return `${this.repoBase(ctx)}/issues`;
23495
+ }
23496
+ wiki(ctx) {
23497
+ return `${this.repoBase(ctx)}/wiki`;
23498
+ }
23499
+ settings(ctx) {
23500
+ return `${this.repoBase(ctx)}/admin`;
23501
+ }
23502
+ repoBase(ctx) {
23503
+ return `${this.base}/${encodeURIComponent(ctx.workspace)}/${encodeURIComponent(ctx.repoSlug)}`;
23504
+ }
23505
+ }
23379
23506
  // src/bootstrap.ts
23380
23507
  import { createRequire } from "module";
23381
23508
 
@@ -27201,6 +27328,18 @@ class BaseCommand {
27201
27328
  }
27202
27329
  return parsed;
27203
27330
  }
27331
+ parsePositiveInt(value, name) {
27332
+ const trimmed = value.trim();
27333
+ const parsed = Number.parseInt(trimmed, 10);
27334
+ if (!Number.isFinite(parsed) || parsed < 1 || String(parsed) !== trimmed) {
27335
+ throw new BBError({
27336
+ code: 5002 /* VALIDATION_INVALID */,
27337
+ message: this.appendHelpHint(`--${name} must be a positive integer.`),
27338
+ context: { [name]: value }
27339
+ });
27340
+ }
27341
+ return parsed;
27342
+ }
27204
27343
  parseEnumOption(value, name, allowed) {
27205
27344
  if (!allowed.includes(value)) {
27206
27345
  throw new BBError({
@@ -28028,10 +28167,7 @@ class ListDefaultReviewersCommand extends BaseCommand {
28028
28167
  this.contextService = contextService;
28029
28168
  }
28030
28169
  async execute(options, context) {
28031
- const repoContext = await this.contextService.requireRepoContext({
28032
- ...context.globalOptions,
28033
- ...options
28034
- });
28170
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28035
28171
  const mode = options.repoOnly ? "direct" : "effective";
28036
28172
  const reviewers = await this.defaultReviewerService.list(repoContext, mode);
28037
28173
  if (context.globalOptions.json) {
@@ -28074,10 +28210,7 @@ class AddDefaultReviewerCommand extends BaseCommand {
28074
28210
  this.contextService = contextService;
28075
28211
  }
28076
28212
  async execute(options, context) {
28077
- const repoContext = await this.contextService.requireRepoContext({
28078
- ...context.globalOptions,
28079
- ...options
28080
- });
28213
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28081
28214
  const userResponse = await this.usersApi.usersSelectedUserGet({
28082
28215
  selectedUser: options.username
28083
28216
  });
@@ -28111,10 +28244,7 @@ class RemoveDefaultReviewerCommand extends BaseCommand {
28111
28244
  this.contextService = contextService;
28112
28245
  }
28113
28246
  async execute(options, context) {
28114
- const repoContext = await this.contextService.requireRepoContext({
28115
- ...context.globalOptions,
28116
- ...options
28117
- });
28247
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28118
28248
  if (!options.yes) {
28119
28249
  throw new BBError({
28120
28250
  code: 5001 /* VALIDATION_REQUIRED */,
@@ -28166,10 +28296,7 @@ class CreatePRCommand extends BaseCommand {
28166
28296
  message: this.appendHelpHint("Pull request title is required. Use --title option.")
28167
28297
  });
28168
28298
  }
28169
- const repoContext = await this.contextService.requireRepoContext({
28170
- ...context.globalOptions,
28171
- ...options
28172
- });
28299
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28173
28300
  let sourceBranch = options.source;
28174
28301
  if (!sourceBranch) {
28175
28302
  sourceBranch = await this.gitService.getCurrentBranch();
@@ -28294,10 +28421,7 @@ class ListPRsCommand extends BaseCommand {
28294
28421
  this.contextService = contextService;
28295
28422
  }
28296
28423
  async execute(options, context) {
28297
- const repoContext = await this.contextService.requireRepoContext({
28298
- ...context.globalOptions,
28299
- ...options
28300
- });
28424
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28301
28425
  const state = options.state ? this.parseEnumOption(options.state, "state", PR_STATES) : "OPEN";
28302
28426
  const limit = parseLimit(options.limit);
28303
28427
  const reviewerQuery = options.mine ? await this.buildMineFilter() : undefined;
@@ -28341,19 +28465,13 @@ class ListPRsCommand extends BaseCommand {
28341
28465
  const destination = pr.destination;
28342
28466
  return [
28343
28467
  `#${pr.id}`,
28344
- this.truncate(title ?? "", 50),
28468
+ this.output.truncate(title ?? "", 50),
28345
28469
  pr.author?.display_name ?? "Unknown",
28346
28470
  `${source?.branch?.name ?? "unknown"} \u2192 ${destination?.branch?.name ?? "unknown"}`
28347
28471
  ];
28348
28472
  });
28349
28473
  this.output.table(["ID", "TITLE", "AUTHOR", "BRANCHES"], rows);
28350
28474
  }
28351
- truncate(text, maxLength) {
28352
- if (text.length <= maxLength) {
28353
- return text;
28354
- }
28355
- return text.substring(0, maxLength - 3) + "...";
28356
- }
28357
28475
  async buildMineFilter() {
28358
28476
  const response = await this.usersApi.userGet();
28359
28477
  const userUuid = response.data.uuid;
@@ -28377,11 +28495,8 @@ class ViewPRCommand extends BaseCommand {
28377
28495
  this.contextService = contextService;
28378
28496
  }
28379
28497
  async execute(options, context) {
28380
- const repoContext = await this.contextService.requireRepoContext({
28381
- ...context.globalOptions,
28382
- ...options
28383
- });
28384
- const prId = this.parseIntOption(options.id, "id");
28498
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28499
+ const prId = this.parsePositiveInt(options.id, "id");
28385
28500
  const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdGet({
28386
28501
  workspace: repoContext.workspace,
28387
28502
  repoSlug: repoContext.repoSlug,
@@ -28506,13 +28621,10 @@ class EditPRCommand extends BaseCommand {
28506
28621
  this.gitService = gitService;
28507
28622
  }
28508
28623
  async execute(options, context) {
28509
- const repoContext = await this.contextService.requireRepoContext({
28510
- ...context.globalOptions,
28511
- ...options
28512
- });
28624
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28513
28625
  let prId;
28514
28626
  if (options.id) {
28515
- prId = Number.parseInt(options.id, 10);
28627
+ prId = this.parsePositiveInt(options.id, "id");
28516
28628
  } else {
28517
28629
  const currentBranch = await this.gitService.getCurrentBranch();
28518
28630
  const matches = await collectPages({
@@ -28587,7 +28699,7 @@ class EditPRCommand extends BaseCommand {
28587
28699
  this.output.success(`Updated pull request #${pr.id}`);
28588
28700
  this.output.text(` ${this.output.dim("Title:")} ${pr.title}`);
28589
28701
  if (pr.description) {
28590
- const truncatedDesc = pr.description.length > 100 ? pr.description.substring(0, 100) + "..." : pr.description;
28702
+ const truncatedDesc = this.output.truncate(pr.description, 100);
28591
28703
  this.output.text(` ${this.output.dim("Description:")} ${truncatedDesc}`);
28592
28704
  }
28593
28705
  this.output.text(` ${this.output.dim("URL:")} ${links?.html?.href}`);
@@ -28608,11 +28720,8 @@ class MergePRCommand extends BaseCommand {
28608
28720
  this.contextService = contextService;
28609
28721
  }
28610
28722
  async execute(options, context) {
28611
- const repoContext = await this.contextService.requireRepoContext({
28612
- ...context.globalOptions,
28613
- ...options
28614
- });
28615
- const prId = this.parseIntOption(options.id, "id");
28723
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28724
+ const prId = this.parsePositiveInt(options.id, "id");
28616
28725
  const request = {
28617
28726
  type: "pullrequest_merge_parameters"
28618
28727
  };
@@ -28656,11 +28765,8 @@ class ApprovePRCommand extends BaseCommand {
28656
28765
  this.contextService = contextService;
28657
28766
  }
28658
28767
  async execute(options, context) {
28659
- const repoContext = await this.contextService.requireRepoContext({
28660
- ...context.globalOptions,
28661
- ...options
28662
- });
28663
- const prId = Number.parseInt(options.id, 10);
28768
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28769
+ const prId = this.parsePositiveInt(options.id, "id");
28664
28770
  await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdApprovePost({
28665
28771
  workspace: repoContext.workspace,
28666
28772
  repoSlug: repoContext.repoSlug,
@@ -28689,11 +28795,8 @@ class DeclinePRCommand extends BaseCommand {
28689
28795
  this.contextService = contextService;
28690
28796
  }
28691
28797
  async execute(options, context) {
28692
- const repoContext = await this.contextService.requireRepoContext({
28693
- ...context.globalOptions,
28694
- ...options
28695
- });
28696
- const prId = Number.parseInt(options.id, 10);
28798
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28799
+ const prId = this.parsePositiveInt(options.id, "id");
28697
28800
  const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdDeclinePost({
28698
28801
  workspace: repoContext.workspace,
28699
28802
  repoSlug: repoContext.repoSlug,
@@ -28724,11 +28827,8 @@ class ReadyPRCommand extends BaseCommand {
28724
28827
  this.contextService = contextService;
28725
28828
  }
28726
28829
  async execute(options, context) {
28727
- const repoContext = await this.contextService.requireRepoContext({
28728
- ...context.globalOptions,
28729
- ...options
28730
- });
28731
- const prId = Number.parseInt(options.id, 10);
28830
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28831
+ const prId = this.parsePositiveInt(options.id, "id");
28732
28832
  const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdPut({
28733
28833
  workspace: repoContext.workspace,
28734
28834
  repoSlug: repoContext.repoSlug,
@@ -28766,11 +28866,8 @@ class CheckoutPRCommand extends BaseCommand {
28766
28866
  this.gitService = gitService;
28767
28867
  }
28768
28868
  async execute(options, context) {
28769
- const repoContext = await this.contextService.requireRepoContext({
28770
- ...context.globalOptions,
28771
- ...options
28772
- });
28773
- const prId = Number.parseInt(options.id, 10);
28869
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28870
+ const prId = this.parsePositiveInt(options.id, "id");
28774
28871
  const prResponse = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdGet({
28775
28872
  workspace: repoContext.workspace,
28776
28873
  repoSlug: repoContext.repoSlug,
@@ -28833,20 +28930,10 @@ class DiffPRCommand extends BaseCommand {
28833
28930
  this.gitService = gitService;
28834
28931
  }
28835
28932
  async execute(options, context) {
28836
- const repoContext = await this.contextService.requireRepoContext({
28837
- ...context.globalOptions,
28838
- ...options
28839
- });
28933
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
28840
28934
  let prId;
28841
28935
  if (options.id) {
28842
- prId = Number.parseInt(options.id, 10);
28843
- if (Number.isNaN(prId)) {
28844
- throw new BBError({
28845
- code: 5002 /* VALIDATION_INVALID */,
28846
- message: "Invalid PR ID",
28847
- context: { id: options.id }
28848
- });
28849
- }
28936
+ prId = this.parsePositiveInt(options.id, "id");
28850
28937
  } else {
28851
28938
  const currentBranch = await this.gitService.getCurrentBranch();
28852
28939
  const matches = await collectPages({
@@ -29063,11 +29150,8 @@ class ActivityPRCommand extends BaseCommand {
29063
29150
  this.contextService = contextService;
29064
29151
  }
29065
29152
  async execute(options, context) {
29066
- const repoContext = await this.contextService.requireRepoContext({
29067
- ...context.globalOptions,
29068
- ...options
29069
- });
29070
- const prId = this.parseIntOption(options.id, "id");
29153
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
29154
+ const prId = this.parsePositiveInt(options.id, "id");
29071
29155
  const filterTypes = this.parseTypeFilter(options.type);
29072
29156
  const limit = parseLimit(options.limit);
29073
29157
  const activities = await collectPages({
@@ -29176,14 +29260,14 @@ class ActivityPRCommand extends BaseCommand {
29176
29260
  case "comment": {
29177
29261
  const content = getRawContent(activity.comment?.content) ?? "";
29178
29262
  const id = activity.comment?.id ? `#${activity.comment.id}` : "";
29179
- const snippet = this.truncate(content, 80);
29263
+ const snippet = this.output.truncate(content, 80);
29180
29264
  return [id, snippet].filter(Boolean).join(" ");
29181
29265
  }
29182
29266
  case "approval":
29183
29267
  return "approved";
29184
29268
  case "changes_requested": {
29185
29269
  const reason = activity.changes_requested?.reason;
29186
- return reason ? this.truncate(reason, 80) : "changes requested";
29270
+ return reason ? this.output.truncate(reason, 80) : "changes requested";
29187
29271
  }
29188
29272
  case "merge":
29189
29273
  return this.formatCommitDetail(activity.merge?.commit?.hash, "merged");
@@ -29196,7 +29280,7 @@ class ActivityPRCommand extends BaseCommand {
29196
29280
  return `state: ${activity.update.state}`;
29197
29281
  }
29198
29282
  if (activity.update?.title) {
29199
- return `title: ${this.truncate(activity.update.title, 60)}`;
29283
+ return `title: ${this.output.truncate(activity.update.title, 60)}`;
29200
29284
  }
29201
29285
  if (activity.update?.description) {
29202
29286
  return "description updated";
@@ -29214,12 +29298,6 @@ class ActivityPRCommand extends BaseCommand {
29214
29298
  const shortHash = hash.slice(0, 7);
29215
29299
  return label ? `${label} ${shortHash}` : shortHash;
29216
29300
  }
29217
- truncate(text, maxLength) {
29218
- if (text.length <= maxLength) {
29219
- return text;
29220
- }
29221
- return text.substring(0, maxLength - 3) + "...";
29222
- }
29223
29301
  }
29224
29302
 
29225
29303
  // src/commands/pr/comment.command.ts
@@ -29253,33 +29331,14 @@ class CommentPRCommand extends BaseCommand {
29253
29331
  }
29254
29332
  });
29255
29333
  }
29256
- if (options.lineTo) {
29257
- const parsed = Number.parseInt(options.lineTo, 10);
29258
- if (Number.isNaN(parsed) || parsed < 1) {
29259
- throw new BBError({
29260
- code: 5002 /* VALIDATION_INVALID */,
29261
- message: "--line-to must be a positive integer"
29262
- });
29263
- }
29264
- }
29265
- if (options.lineFrom) {
29266
- const parsed = Number.parseInt(options.lineFrom, 10);
29267
- if (Number.isNaN(parsed) || parsed < 1) {
29268
- throw new BBError({
29269
- code: 5002 /* VALIDATION_INVALID */,
29270
- message: "--line-from must be a positive integer"
29271
- });
29272
- }
29273
- }
29274
- const repoContext = await this.contextService.requireRepoContext({
29275
- ...context.globalOptions,
29276
- ...options
29277
- });
29278
- const prId = Number.parseInt(options.id, 10);
29334
+ const lineTo = options.lineTo ? this.parsePositiveInt(options.lineTo, "line-to") : undefined;
29335
+ const lineFrom = options.lineFrom ? this.parsePositiveInt(options.lineFrom, "line-from") : undefined;
29336
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
29337
+ const prId = this.parsePositiveInt(options.id, "id");
29279
29338
  const inline = options.file ? {
29280
29339
  path: options.file,
29281
- ...options.lineTo ? { to: Number.parseInt(options.lineTo, 10) } : {},
29282
- ...options.lineFrom ? { from: Number.parseInt(options.lineFrom, 10) } : {}
29340
+ ...lineTo !== undefined ? { to: lineTo } : {},
29341
+ ...lineFrom !== undefined ? { from: lineFrom } : {}
29283
29342
  } : undefined;
29284
29343
  const body = {
29285
29344
  content: {
@@ -29329,11 +29388,8 @@ class ListCommentsPRCommand extends BaseCommand {
29329
29388
  this.contextService = contextService;
29330
29389
  }
29331
29390
  async execute(options, context) {
29332
- const repoContext = await this.contextService.requireRepoContext({
29333
- ...context.globalOptions,
29334
- ...options
29335
- });
29336
- const prId = this.parseIntOption(options.id, "id");
29391
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
29392
+ const prId = this.parsePositiveInt(options.id, "id");
29337
29393
  const limit = parseLimit(options.limit);
29338
29394
  const values = await collectPages({
29339
29395
  limit,
@@ -29350,6 +29406,8 @@ class ListCommentsPRCommand extends BaseCommand {
29350
29406
  });
29351
29407
  if (context.globalOptions.json) {
29352
29408
  await this.output.json({
29409
+ workspace: repoContext.workspace,
29410
+ repoSlug: repoContext.repoSlug,
29353
29411
  pullRequestId: prId,
29354
29412
  count: values.length,
29355
29413
  comments: values
@@ -29365,7 +29423,7 @@ class ListCommentsPRCommand extends BaseCommand {
29365
29423
  return [
29366
29424
  comment.id?.toString() ?? "",
29367
29425
  getUserDisplayName(comment.user) ?? "Unknown",
29368
- comment.deleted ? "[deleted]" : options.truncate === false ? content : content.slice(0, 60) + (content.length > 60 ? "..." : ""),
29426
+ comment.deleted ? "[deleted]" : options.truncate === false ? content : this.output.truncate(content, 60),
29369
29427
  this.output.formatDate(comment.created_on ?? "")
29370
29428
  ];
29371
29429
  });
@@ -29385,12 +29443,9 @@ class EditCommentPRCommand extends BaseCommand {
29385
29443
  this.contextService = contextService;
29386
29444
  }
29387
29445
  async execute(options, context) {
29388
- const repoContext = await this.contextService.requireRepoContext({
29389
- ...context.globalOptions,
29390
- ...options
29391
- });
29392
- const prId = this.parseIntOption(options.prId, "pr-id");
29393
- const commentId = this.parseIntOption(options.commentId, "comment-id");
29446
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
29447
+ const prId = this.parsePositiveInt(options.prId, "pr-id");
29448
+ const commentId = this.parsePositiveInt(options.commentId, "comment-id");
29394
29449
  const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdCommentsCommentIdPut({
29395
29450
  workspace: repoContext.workspace,
29396
29451
  repoSlug: repoContext.repoSlug,
@@ -29428,12 +29483,9 @@ class DeleteCommentPRCommand extends BaseCommand {
29428
29483
  this.contextService = contextService;
29429
29484
  }
29430
29485
  async execute(options, context) {
29431
- const repoContext = await this.contextService.requireRepoContext({
29432
- ...context.globalOptions,
29433
- ...options
29434
- });
29435
- const prId = this.parseIntOption(options.prId, "pr-id");
29436
- const commentId = this.parseIntOption(options.commentId, "comment-id");
29486
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
29487
+ const prId = this.parsePositiveInt(options.prId, "pr-id");
29488
+ const commentId = this.parsePositiveInt(options.commentId, "comment-id");
29437
29489
  if (!options.yes) {
29438
29490
  throw new BBError({
29439
29491
  code: 5001 /* VALIDATION_REQUIRED */,
@@ -29473,11 +29525,8 @@ class AddReviewerPRCommand extends BaseCommand {
29473
29525
  this.contextService = contextService;
29474
29526
  }
29475
29527
  async execute(options, context) {
29476
- const repoContext = await this.contextService.requireRepoContext({
29477
- ...context.globalOptions,
29478
- ...options
29479
- });
29480
- const prId = this.parseIntOption(options.id, "id");
29528
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
29529
+ const prId = this.parsePositiveInt(options.id, "id");
29481
29530
  const userResponse = await this.usersApi.usersSelectedUserGet({
29482
29531
  selectedUser: options.username
29483
29532
  });
@@ -29518,11 +29567,8 @@ class RemoveReviewerPRCommand extends BaseCommand {
29518
29567
  this.contextService = contextService;
29519
29568
  }
29520
29569
  async execute(options, context) {
29521
- const repoContext = await this.contextService.requireRepoContext({
29522
- ...context.globalOptions,
29523
- ...options
29524
- });
29525
- const prId = this.parseIntOption(options.id, "id");
29570
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
29571
+ const prId = this.parsePositiveInt(options.id, "id");
29526
29572
  const userResponse = await this.usersApi.usersSelectedUserGet({
29527
29573
  selectedUser: options.username
29528
29574
  });
@@ -29556,11 +29602,8 @@ class ListReviewersPRCommand extends BaseCommand {
29556
29602
  this.contextService = contextService;
29557
29603
  }
29558
29604
  async execute(options, context) {
29559
- const repoContext = await this.contextService.requireRepoContext({
29560
- ...context.globalOptions,
29561
- ...options
29562
- });
29563
- const prId = this.parseIntOption(options.id, "id");
29605
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
29606
+ const prId = this.parsePositiveInt(options.id, "id");
29564
29607
  const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdGet({
29565
29608
  workspace: repoContext.workspace,
29566
29609
  repoSlug: repoContext.repoSlug,
@@ -29570,6 +29613,8 @@ class ListReviewersPRCommand extends BaseCommand {
29570
29613
  const reviewers = Array.from(pr.reviewers ?? []);
29571
29614
  if (context.globalOptions.json) {
29572
29615
  await this.output.json({
29616
+ workspace: repoContext.workspace,
29617
+ repoSlug: repoContext.repoSlug,
29573
29618
  pullRequestId: prId,
29574
29619
  count: reviewers.length,
29575
29620
  reviewers
@@ -29596,11 +29641,8 @@ class ChecksPRCommand extends BaseCommand {
29596
29641
  this.contextService = contextService;
29597
29642
  }
29598
29643
  async execute(options, context) {
29599
- const repoContext = await this.contextService.requireRepoContext({
29600
- ...context.globalOptions,
29601
- ...options
29602
- });
29603
- const prId = this.parseIntOption(options.id, "id");
29644
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
29645
+ const prId = this.parsePositiveInt(options.id, "id");
29604
29646
  const response = await this.commitStatusesApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdStatusesGet({
29605
29647
  workspace: repoContext.workspace,
29606
29648
  repoSlug: repoContext.repoSlug,
@@ -29654,7 +29696,7 @@ class ChecksPRCommand extends BaseCommand {
29654
29696
  return [
29655
29697
  `${stateIcon} ${stateLabel}`,
29656
29698
  this.output.bold(name),
29657
- this.truncate(description, 40),
29699
+ this.output.truncate(description, 40),
29658
29700
  status.updated_on ? this.output.formatDate(status.updated_on) : "-"
29659
29701
  ];
29660
29702
  });
@@ -29704,12 +29746,6 @@ class ChecksPRCommand extends BaseCommand {
29704
29746
  return acc;
29705
29747
  }, { successful: 0, failed: 0, pending: 0 });
29706
29748
  }
29707
- truncate(text, maxLength) {
29708
- if (text.length <= maxLength) {
29709
- return text;
29710
- }
29711
- return text.substring(0, maxLength - 3) + "...";
29712
- }
29713
29749
  }
29714
29750
 
29715
29751
  // src/commands/snippet/list.command.ts
@@ -30122,7 +30158,7 @@ class ListSnippetCommentsCommand extends BaseCommand {
30122
30158
  String(comment.id ?? ""),
30123
30159
  getUserDisplayName(comment.user) ?? "Unknown",
30124
30160
  this.output.formatDate(comment.created_on ?? ""),
30125
- content.slice(0, 60) + (content.length > 60 ? "..." : "")
30161
+ this.output.truncate(content, 60)
30126
30162
  ];
30127
30163
  });
30128
30164
  this.output.table(["ID", "AUTHOR", "DATE", "CONTENT"], rows);
@@ -30181,7 +30217,7 @@ class EditSnippetCommentCommand extends BaseCommand {
30181
30217
  }
30182
30218
  async execute(options, context) {
30183
30219
  const workspace = await this.contextService.requireWorkspace(options.workspace ?? context.globalOptions.workspace);
30184
- const commentId = this.parseIntOption(options.commentId, "comment-id");
30220
+ const commentId = this.parsePositiveInt(options.commentId, "comment-id");
30185
30221
  const body = {
30186
30222
  type: "snippet_comment",
30187
30223
  content: {
@@ -30219,7 +30255,7 @@ class DeleteSnippetCommentCommand extends BaseCommand {
30219
30255
  }
30220
30256
  async execute(options, context) {
30221
30257
  const workspace = await this.contextService.requireWorkspace(options.workspace ?? context.globalOptions.workspace);
30222
- const commentId = this.parseIntOption(options.commentId, "comment-id");
30258
+ const commentId = this.parsePositiveInt(options.commentId, "comment-id");
30223
30259
  if (!options.yes) {
30224
30260
  throw new BBError({
30225
30261
  code: 5001 /* VALIDATION_REQUIRED */,
@@ -30451,6 +30487,179 @@ class UninstallCompletionCommand extends BaseCommand {
30451
30487
  }
30452
30488
  }
30453
30489
 
30490
+ // src/commands/browse.command.ts
30491
+ var SHA_PATTERN = /^[0-9a-f]{7,40}$/i;
30492
+ var PR_NUMBER_PATTERN = /^\d+$/;
30493
+ var PATH_LINE_PATTERN = /^(.+):(\d+)$/;
30494
+
30495
+ class BrowseCommand extends BaseCommand {
30496
+ contextService;
30497
+ gitService;
30498
+ urlBuilder;
30499
+ name = "browse";
30500
+ description = "Open a Bitbucket page (repo, file, PR, commit, etc.) in your browser";
30501
+ constructor(contextService, gitService, urlBuilder, output) {
30502
+ super(output);
30503
+ this.contextService = contextService;
30504
+ this.gitService = gitService;
30505
+ this.urlBuilder = urlBuilder;
30506
+ }
30507
+ async execute(options, context) {
30508
+ const repoContext = await this.contextService.requireRepoContextFor(options, context);
30509
+ this.validateFlagCombination(options);
30510
+ const url2 = await this.resolveUrl(options, repoContext);
30511
+ const useJson = Boolean(context.globalOptions.json);
30512
+ const printOnly = options.browser === false;
30513
+ if (useJson) {
30514
+ await this.output.json({ url: url2 });
30515
+ return { url: url2, opened: false };
30516
+ }
30517
+ if (printOnly) {
30518
+ this.output.text(url2);
30519
+ return { url: url2, opened: false };
30520
+ }
30521
+ await this.openInBrowser(url2);
30522
+ return { url: url2, opened: true };
30523
+ }
30524
+ async resolveUrl(options, ctx) {
30525
+ if (options.pr !== undefined) {
30526
+ return this.urlBuilder.pullRequest(ctx, this.parsePositiveInt(options.pr, "pr"));
30527
+ }
30528
+ if (options.prs || options.pullRequests) {
30529
+ return this.urlBuilder.pullRequestList(ctx);
30530
+ }
30531
+ if (options.branches) {
30532
+ return this.urlBuilder.branchList(ctx);
30533
+ }
30534
+ if (options.commits) {
30535
+ return this.urlBuilder.commitList(ctx);
30536
+ }
30537
+ if (options.commit !== undefined) {
30538
+ const sha = typeof options.commit === "string" && options.commit.length > 0 ? options.commit : await this.gitService.getCurrentCommit();
30539
+ return this.urlBuilder.commit(ctx, sha);
30540
+ }
30541
+ if (options.pipelines) {
30542
+ return this.urlBuilder.pipelinesHome(ctx);
30543
+ }
30544
+ if (options.pipeline !== undefined) {
30545
+ const value = options.pipeline.trim();
30546
+ if (value.length === 0) {
30547
+ throw new BBError({
30548
+ code: 5002 /* VALIDATION_INVALID */,
30549
+ message: this.appendHelpHint("--pipeline requires a run id or uuid.")
30550
+ });
30551
+ }
30552
+ return this.urlBuilder.pipelineRun(ctx, value);
30553
+ }
30554
+ if (options.downloads) {
30555
+ return this.urlBuilder.downloads(ctx);
30556
+ }
30557
+ if (options.issue !== undefined) {
30558
+ return this.urlBuilder.issue(ctx, this.parsePositiveInt(options.issue, "issue"));
30559
+ }
30560
+ if (options.issues) {
30561
+ return this.urlBuilder.issueList(ctx);
30562
+ }
30563
+ if (options.wiki) {
30564
+ return this.urlBuilder.wiki(ctx);
30565
+ }
30566
+ if (options.settings) {
30567
+ return this.urlBuilder.settings(ctx);
30568
+ }
30569
+ const target = options.target?.trim();
30570
+ if (target && target.length > 0) {
30571
+ if (PR_NUMBER_PATTERN.test(target)) {
30572
+ return this.urlBuilder.pullRequest(ctx, this.parsePositiveInt(target, "target"));
30573
+ }
30574
+ if (SHA_PATTERN.test(target)) {
30575
+ return this.urlBuilder.commit(ctx, target);
30576
+ }
30577
+ const { path: path3, line } = this.parsePathWithLine(target);
30578
+ const branch = await this.resolveBranch(options.branch);
30579
+ return this.urlBuilder.src(ctx, branch, path3, line);
30580
+ }
30581
+ if (options.branch) {
30582
+ return this.urlBuilder.src(ctx, options.branch);
30583
+ }
30584
+ return this.urlBuilder.repo(ctx);
30585
+ }
30586
+ validateFlagCombination(options) {
30587
+ const setFlags = [];
30588
+ if (options.pr !== undefined)
30589
+ setFlags.push("--pr");
30590
+ if (options.prs) {
30591
+ setFlags.push("--prs");
30592
+ } else if (options.pullRequests) {
30593
+ setFlags.push("--pull-requests");
30594
+ }
30595
+ if (options.branches)
30596
+ setFlags.push("--branches");
30597
+ if (options.commit !== undefined)
30598
+ setFlags.push("--commit");
30599
+ if (options.commits)
30600
+ setFlags.push("--commits");
30601
+ if (options.pipelines)
30602
+ setFlags.push("--pipelines");
30603
+ if (options.pipeline !== undefined)
30604
+ setFlags.push("--pipeline");
30605
+ if (options.downloads)
30606
+ setFlags.push("--downloads");
30607
+ if (options.issue !== undefined)
30608
+ setFlags.push("--issue");
30609
+ if (options.issues)
30610
+ setFlags.push("--issues");
30611
+ if (options.wiki)
30612
+ setFlags.push("--wiki");
30613
+ if (options.settings)
30614
+ setFlags.push("--settings");
30615
+ if (setFlags.length > 1) {
30616
+ throw new BBError({
30617
+ code: 5002 /* VALIDATION_INVALID */,
30618
+ message: this.appendHelpHint(`Cannot combine ${setFlags.join(" and ")}; pick one resource.`)
30619
+ });
30620
+ }
30621
+ const hasResourceFlag = setFlags.length === 1;
30622
+ const hasTarget = !!options.target?.trim();
30623
+ if (hasResourceFlag && hasTarget) {
30624
+ throw new BBError({
30625
+ code: 5002 /* VALIDATION_INVALID */,
30626
+ message: this.appendHelpHint(`Cannot use a positional target with ${setFlags[0]}.`)
30627
+ });
30628
+ }
30629
+ if (hasResourceFlag && options.branch) {
30630
+ throw new BBError({
30631
+ code: 5002 /* VALIDATION_INVALID */,
30632
+ message: this.appendHelpHint(`Cannot combine --branch with ${setFlags[0]}.`)
30633
+ });
30634
+ }
30635
+ }
30636
+ parsePathWithLine(target) {
30637
+ const match = PATH_LINE_PATTERN.exec(target);
30638
+ if (match) {
30639
+ const line = Number.parseInt(match[2], 10);
30640
+ if (Number.isFinite(line) && line > 0) {
30641
+ return { path: match[1], line };
30642
+ }
30643
+ }
30644
+ return { path: target };
30645
+ }
30646
+ async resolveBranch(explicit) {
30647
+ if (explicit && explicit.length > 0) {
30648
+ return explicit;
30649
+ }
30650
+ try {
30651
+ return await this.gitService.getCurrentBranch();
30652
+ } catch {
30653
+ return "HEAD";
30654
+ }
30655
+ }
30656
+ async openInBrowser(url2) {
30657
+ this.output.info(`Opening ${url2} in your browser...`);
30658
+ const open2 = (await Promise.resolve().then(() => (init_open(), exports_open))).default;
30659
+ await open2(url2);
30660
+ }
30661
+ }
30662
+
30454
30663
  // src/bootstrap.ts
30455
30664
  var require2 = createRequire(import.meta.url);
30456
30665
  var pkg = require2("../package.json");
@@ -30497,6 +30706,7 @@ function bootstrap(options = {}) {
30497
30706
  });
30498
30707
  registerCommand(container, ServiceTokens.SnippetFilesService, SnippetFilesService, [ServiceTokens.SnippetsAxios]);
30499
30708
  registerCommand(container, ServiceTokens.DefaultReviewerService, DefaultReviewerService, [ServiceTokens.PullrequestsApi]);
30709
+ container.register(ServiceTokens.UrlBuilderService, () => new UrlBuilderService);
30500
30710
  registerCommand(container, ServiceTokens.LoginCommand, LoginCommand, [
30501
30711
  ServiceTokens.CredentialStore,
30502
30712
  ServiceTokens.UsersApi,
@@ -30731,6 +30941,12 @@ function bootstrap(options = {}) {
30731
30941
  ServiceTokens.OutputService
30732
30942
  ]);
30733
30943
  registerCommand(container, ServiceTokens.ListConfigCommand, ListConfigCommand, [ServiceTokens.ConfigService, ServiceTokens.OutputService]);
30944
+ registerCommand(container, ServiceTokens.BrowseCommand, BrowseCommand, [
30945
+ ServiceTokens.ContextService,
30946
+ ServiceTokens.GitService,
30947
+ ServiceTokens.UrlBuilderService,
30948
+ ServiceTokens.OutputService
30949
+ ]);
30734
30950
  registerCommand(container, ServiceTokens.InstallCompletionCommand, InstallCompletionCommand, [ServiceTokens.OutputService]);
30735
30951
  registerCommand(container, ServiceTokens.UninstallCompletionCommand, UninstallCompletionCommand, [ServiceTokens.OutputService]);
30736
30952
  container.register(ServiceTokens.VersionService, () => {
@@ -30820,6 +31036,7 @@ var ROOT_COMPLETIONS = [
30820
31036
  "repo",
30821
31037
  "pr",
30822
31038
  "snippet",
31039
+ "browse",
30823
31040
  "config",
30824
31041
  "completion",
30825
31042
  "--help",
@@ -31021,7 +31238,7 @@ cli.name("bb").description("A command-line interface for Bitbucket Cloud").versi
31021
31238
  } catch {}
31022
31239
  });
31023
31240
  var authCmd = new Command("auth").description("Authenticate with Bitbucket");
31024
- authCmd.command("login").description("Authenticate with Bitbucket (OAuth or API token)").option("-u, --username <username>", "Bitbucket username (implies API token auth)").option("-p, --password <password>", "Bitbucket API token (implies API token auth)").option("--app-password", "Use API token authentication instead of OAuth").option("--client-id <clientId>", "Custom OAuth consumer client ID").option("--client-secret <clientSecret>", "Custom OAuth consumer client secret").addHelpText("before", `
31241
+ authCmd.command("login").description("Authenticate with Bitbucket (OAuth or API token)").option("-u, --username <username>", "Bitbucket username (implies API token auth)").option("-p, --password <password>", "Bitbucket API token (implies API token auth)").option("--app-password", "Use API token authentication (instead of OAuth). App passwords are deprecated; use API tokens.").option("--client-id <clientId>", "Custom OAuth consumer client ID").option("--client-secret <clientSecret>", "Custom OAuth consumer client secret").addHelpText("before", `
31025
31242
  Default: OAuth (browser-based, recommended).
31026
31243
  ` + `For CI/CD: API token via --app-password or BB_API_TOKEN env var.
31027
31244
  ` + `Note: Bitbucket app passwords are deprecated; use OAuth or an API token.
@@ -31164,7 +31381,7 @@ prCmd.command("create").description("Create a pull request").option("-t, --title
31164
31381
  const context = createContext(cli);
31165
31382
  await runCommand(ServiceTokens.CreatePRCommand, withGlobalOptions(options, context), cli, context);
31166
31383
  });
31167
- prCmd.command("list").description("List pull requests").option("-s, --state <state>", `Filter by state (${PR_STATES.join(", ")})`, "OPEN").option("--limit <number>", "Maximum number of PRs to list", "25").option("--mine", "Show only PRs where you are a reviewer").addHelpText("after", buildHelpText({
31384
+ prCmd.command("list").description("List pull requests").option("-s, --state <state>", `Filter by state (${PR_STATES.join(", ")})`, "OPEN").option("--limit <number>", "Maximum number of PRs to list", "25").option("--mine", "Show only PRs where you are a reviewer (not authored by you)").addHelpText("after", buildHelpText({
31168
31385
  examples: [
31169
31386
  "bb pr list",
31170
31387
  "bb pr list -s MERGED --limit 10",
@@ -31358,23 +31575,23 @@ prReviewersCmd.command("list <id>").description("List reviewers on a pull reques
31358
31575
  const context = createContext(cli);
31359
31576
  await runCommand(ServiceTokens.ListReviewersPRCommand, withGlobalOptions({ id, ...options }, context), cli, context);
31360
31577
  });
31361
- prReviewersCmd.command("add <id> <username>").description("Add a reviewer to a pull request").addHelpText("after", buildHelpText({
31578
+ prReviewersCmd.command("add <id> <user>").description("Add a reviewer to a pull request (user is an account ID or {uuid})").addHelpText("after", buildHelpText({
31362
31579
  examples: [
31363
31580
  'bb pr reviewers add 42 "712020:3cfed7e0-0ed6-49fc-bb35-410a00ccee6f"',
31364
31581
  'bb pr reviewers add 42 "{c1cb1bb5-2e32-456e-a373-43978dc12aa1}"'
31365
31582
  ]
31366
- })).action(async (id, username, options) => {
31583
+ })).action(async (id, user, options) => {
31367
31584
  const context = createContext(cli);
31368
- await runCommand(ServiceTokens.AddReviewerPRCommand, withGlobalOptions({ id, username, ...options }, context), cli, context);
31585
+ await runCommand(ServiceTokens.AddReviewerPRCommand, withGlobalOptions({ id, username: user, ...options }, context), cli, context);
31369
31586
  });
31370
- prReviewersCmd.command("remove <id> <username>").description("Remove a reviewer from a pull request").addHelpText("after", buildHelpText({
31587
+ prReviewersCmd.command("remove <id> <user>").description("Remove a reviewer from a pull request (user is an account ID or {uuid})").addHelpText("after", buildHelpText({
31371
31588
  examples: [
31372
31589
  'bb pr reviewers remove 42 "712020:3cfed7e0-0ed6-49fc-bb35-410a00ccee6f"',
31373
31590
  'bb pr reviewers remove 42 "{c1cb1bb5-2e32-456e-a373-43978dc12aa1}"'
31374
31591
  ]
31375
- })).action(async (id, username, options) => {
31592
+ })).action(async (id, user, options) => {
31376
31593
  const context = createContext(cli);
31377
- await runCommand(ServiceTokens.RemoveReviewerPRCommand, withGlobalOptions({ id, username, ...options }, context), cli, context);
31594
+ await runCommand(ServiceTokens.RemoveReviewerPRCommand, withGlobalOptions({ id, username: user, ...options }, context), cli, context);
31378
31595
  });
31379
31596
  cli.addCommand(prCmd);
31380
31597
  prCmd.addCommand(prCommentsCmd);
@@ -31405,7 +31622,7 @@ snippetCmd.command("view <id>").description("View snippet details").option("-f,
31405
31622
  const context = createContext(cli);
31406
31623
  await runCommand(ServiceTokens.ViewSnippetCommand, withGlobalOptions({ id, ...options }, context), cli, context);
31407
31624
  });
31408
- snippetCmd.command("create").description("Create a new snippet").option("-t, --title <title>", "Snippet title").option("-f, --file <path...>", "File(s) to include").option("--private", "Create a private snippet (default)").option("--public", "Create a public snippet").addHelpText("after", buildHelpText({
31625
+ snippetCmd.command("create").description("Create a new snippet").option("-t, --title <title>", "Snippet title").option("-f, --file <path...>", "File(s) to include (variadic; pass multiple paths or repeat the flag)").option("--private", "Create a private snippet (default)").option("--public", "Create a public snippet").addHelpText("after", buildHelpText({
31409
31626
  examples: [
31410
31627
  'bb snippet create -t "My snippet" -f file.txt',
31411
31628
  'bb snippet create -t "Config files" -f config.yml -f setup.sh --public'
@@ -31415,7 +31632,7 @@ snippetCmd.command("create").description("Create a new snippet").option("-t, --t
31415
31632
  const context = createContext(cli);
31416
31633
  await runCommand(ServiceTokens.CreateSnippetCommand, withGlobalOptions(options, context), cli, context);
31417
31634
  });
31418
- snippetCmd.command("edit <id>").description("Edit a snippet").option("-t, --title <title>", "New snippet title").option("--private", "Make snippet private").option("--public", "Make snippet public").option("-f, --file <path...>", "Replace/add file(s) (sends multipart update)").addHelpText("after", buildHelpText({
31635
+ snippetCmd.command("edit <id>").description("Edit a snippet").option("-t, --title <title>", "New snippet title").option("--private", "Make snippet private").option("--public", "Make snippet public").option("-f, --file <path...>", "Replace/add file(s) (variadic; pass multiple paths or repeat the flag; sends multipart update)").addHelpText("after", buildHelpText({
31419
31636
  examples: [
31420
31637
  'bb snippet edit kypj -t "New title"',
31421
31638
  "bb snippet edit kypj --public",
@@ -31488,6 +31705,26 @@ snippetCommentsCmd.command("delete <snippet-id> <comment-id>").description("Dele
31488
31705
  });
31489
31706
  snippetCmd.addCommand(snippetCommentsCmd);
31490
31707
  cli.addCommand(snippetCmd);
31708
+ cli.command("browse [target]").description("Open a Bitbucket page (repo home, file, PR, commit, etc.) in your browser").option("--pr <id>", "Open a specific pull request").option("--prs", "Open the pull-requests list").option("--pull-requests", "Alias for --prs").option("--branch <name>", "Open the branch source tree (or, with <target>, that path on the branch)").option("--branches", "Open the branches list").option("--commit [sha]", "Open a specific commit (defaults to HEAD when no SHA is given)").option("--commits", "Open the commits list").option("--pipelines", "Open the pipelines page").option("--pipeline <id>", "Open a specific pipeline run").option("--downloads", "Open the downloads page").option("--issue <id>", "Open a specific issue").option("--issues", "Open the issue tracker").option("--wiki", "Open the wiki").option("--settings", "Open repository settings").option("-n, --no-browser", "Print the URL to stdout instead of opening it").addHelpText("after", buildHelpText({
31709
+ examples: [
31710
+ "bb browse",
31711
+ "bb browse src/cli.ts",
31712
+ "bb browse src/cli.ts:42",
31713
+ "bb browse --branch release/2.0 src/cli.ts",
31714
+ "bb browse 217",
31715
+ "bb browse --pr 217",
31716
+ "bb browse --prs",
31717
+ "bb browse abc1234",
31718
+ "bb browse --commit",
31719
+ "bb browse --pipelines",
31720
+ "bb browse --settings",
31721
+ "bb browse --pr 217 --no-browser",
31722
+ "bb browse --pr 217 --json url"
31723
+ ]
31724
+ })).action(async (target, options) => {
31725
+ const context = createContext(cli);
31726
+ await runCommand(ServiceTokens.BrowseCommand, withGlobalOptions({ target, ...options }, context), cli, context);
31727
+ });
31491
31728
  var configCmd = new Command("config").description("Manage configuration");
31492
31729
  configCmd.command("get <key>").description("Get a configuration value").addHelpText("after", buildHelpText({
31493
31730
  examples: ["bb config get defaultWorkspace"],
@@ -31560,3 +31797,6 @@ if (typeof Bun === "undefined") {
31560
31797
  process.exit(1);
31561
31798
  }
31562
31799
  cli.parse(process.argv);
31800
+
31801
+ //# debugId=A7729A51C3738B3664756E2164756E21
31802
+ //# sourceMappingURL=index.js.map