@pilatos/bitbucket-cli 1.16.1 → 1.18.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.
package/dist/index.js CHANGED
@@ -18495,6 +18495,54 @@ var chalk = createChalk();
18495
18495
  var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
18496
18496
  var source_default = chalk;
18497
18497
 
18498
+ // src/services/locale.ts
18499
+ var DEFAULT_LOCALE = "en-US";
18500
+ function normalizePosixLocale(value) {
18501
+ const trimmed = value.trim();
18502
+ if (trimmed.length === 0) {
18503
+ return;
18504
+ }
18505
+ const base = trimmed.split(/[.@]/, 1)[0];
18506
+ if (base.length === 0) {
18507
+ return;
18508
+ }
18509
+ const upper = base.toUpperCase();
18510
+ if (upper === "C" || upper === "POSIX") {
18511
+ return DEFAULT_LOCALE;
18512
+ }
18513
+ return base.replace(/_/g, "-");
18514
+ }
18515
+ function detectSystemLocale(env2 = process.env) {
18516
+ const candidates = [env2.LC_TIME, env2.LC_ALL, env2.LANG];
18517
+ for (const candidate of candidates) {
18518
+ if (typeof candidate !== "string") {
18519
+ continue;
18520
+ }
18521
+ const normalized = normalizePosixLocale(candidate);
18522
+ if (normalized) {
18523
+ return normalized;
18524
+ }
18525
+ }
18526
+ return DEFAULT_LOCALE;
18527
+ }
18528
+ function resolveLocale(options) {
18529
+ const env2 = options.env ?? process.env;
18530
+ if (typeof options.explicit === "string") {
18531
+ const trimmed = options.explicit.trim();
18532
+ if (trimmed.length > 0) {
18533
+ return trimmed;
18534
+ }
18535
+ }
18536
+ const fromEnvVar = env2.BB_LOCALE;
18537
+ if (typeof fromEnvVar === "string") {
18538
+ const trimmed = fromEnvVar.trim();
18539
+ if (trimmed.length > 0) {
18540
+ return trimmed;
18541
+ }
18542
+ }
18543
+ return detectSystemLocale(env2);
18544
+ }
18545
+
18498
18546
  // src/services/output.project.ts
18499
18547
  function projectFields(item, fields) {
18500
18548
  if (item === null || typeof item !== "object") {
@@ -18521,7 +18569,143 @@ function deepGet(source, path) {
18521
18569
  return current === undefined ? null : current;
18522
18570
  }
18523
18571
 
18572
+ // src/services/spinner.ts
18573
+ var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
18574
+ var FRAME_INTERVAL_MS = 80;
18575
+ var ESC = "\x1B";
18576
+ var CLEAR_LINE = `\r${ESC}[2K`;
18577
+ var HIDE_CURSOR = `${ESC}[?25l`;
18578
+ var SHOW_CURSOR = `${ESC}[?25h`;
18579
+ var COLOR_CYAN = `${ESC}[36m`;
18580
+ var COLOR_GREEN = `${ESC}[32m`;
18581
+ var COLOR_RED = `${ESC}[31m`;
18582
+ var COLOR_RESET = `${ESC}[0m`;
18583
+
18584
+ class Spinner {
18585
+ text;
18586
+ enabled;
18587
+ noColor;
18588
+ stream;
18589
+ onStop;
18590
+ timer = null;
18591
+ frameIndex = 0;
18592
+ active = false;
18593
+ exitHandler = null;
18594
+ constructor(text, options) {
18595
+ this.text = text;
18596
+ this.enabled = options.enabled;
18597
+ this.noColor = options.noColor;
18598
+ this.stream = options.stream;
18599
+ this.onStop = options.onStop;
18600
+ }
18601
+ start() {
18602
+ if (!this.enabled || this.active) {
18603
+ return this;
18604
+ }
18605
+ this.active = true;
18606
+ this.stream.write(HIDE_CURSOR);
18607
+ this.render();
18608
+ this.timer = setInterval(() => {
18609
+ this.frameIndex = (this.frameIndex + 1) % FRAMES.length;
18610
+ this.render();
18611
+ }, FRAME_INTERVAL_MS);
18612
+ this.timer.unref?.();
18613
+ this.exitHandler = () => {
18614
+ if (this.active) {
18615
+ this.active = false;
18616
+ if (this.timer) {
18617
+ clearInterval(this.timer);
18618
+ this.timer = null;
18619
+ }
18620
+ this.stream.write(`${CLEAR_LINE}${SHOW_CURSOR}`);
18621
+ }
18622
+ };
18623
+ process.once("SIGINT", this.exitHandler);
18624
+ process.once("SIGTERM", this.exitHandler);
18625
+ process.once("exit", this.exitHandler);
18626
+ return this;
18627
+ }
18628
+ stop() {
18629
+ if (!this.active) {
18630
+ return this.detach();
18631
+ }
18632
+ this.active = false;
18633
+ if (this.timer) {
18634
+ clearInterval(this.timer);
18635
+ this.timer = null;
18636
+ }
18637
+ this.stream.write(`${CLEAR_LINE}${SHOW_CURSOR}`);
18638
+ return this.detach();
18639
+ }
18640
+ succeed(message) {
18641
+ this.stop();
18642
+ if (this.enabled && message) {
18643
+ const symbol = this.colorize("\u2713", COLOR_GREEN);
18644
+ this.stream.write(`${symbol} ${message}
18645
+ `);
18646
+ }
18647
+ return this;
18648
+ }
18649
+ fail(message) {
18650
+ this.stop();
18651
+ if (this.enabled && message) {
18652
+ const symbol = this.colorize("\u2717", COLOR_RED);
18653
+ this.stream.write(`${symbol} ${message}
18654
+ `);
18655
+ }
18656
+ return this;
18657
+ }
18658
+ setText(text) {
18659
+ this.text = text;
18660
+ if (this.active) {
18661
+ this.render();
18662
+ }
18663
+ return this;
18664
+ }
18665
+ render() {
18666
+ if (!this.active)
18667
+ return;
18668
+ const frame = FRAMES[this.frameIndex] ?? FRAMES[0];
18669
+ const symbol = this.colorize(frame, COLOR_CYAN);
18670
+ this.stream.write(`${CLEAR_LINE}${symbol} ${this.text}`);
18671
+ }
18672
+ colorize(text, color) {
18673
+ return this.noColor ? text : `${color}${text}${COLOR_RESET}`;
18674
+ }
18675
+ detach() {
18676
+ if (this.exitHandler) {
18677
+ process.removeListener("SIGINT", this.exitHandler);
18678
+ process.removeListener("SIGTERM", this.exitHandler);
18679
+ process.removeListener("exit", this.exitHandler);
18680
+ this.exitHandler = null;
18681
+ }
18682
+ if (this.onStop) {
18683
+ const cb = this.onStop;
18684
+ this.onStop = undefined;
18685
+ cb();
18686
+ }
18687
+ return this;
18688
+ }
18689
+ }
18690
+ function createNoopSpinner() {
18691
+ const noop = {
18692
+ start: () => noop,
18693
+ stop: () => noop,
18694
+ succeed: () => noop,
18695
+ fail: () => noop,
18696
+ setText: () => noop
18697
+ };
18698
+ return noop;
18699
+ }
18700
+
18524
18701
  // src/services/output.service.ts
18702
+ var DATE_FORMAT_OPTIONS = {
18703
+ year: "numeric",
18704
+ month: "short",
18705
+ day: "numeric",
18706
+ hour: "2-digit",
18707
+ minute: "2-digit"
18708
+ };
18525
18709
  var CONTROL_CHARS = /(\x1b\[[0-9;?]*m)|\x1b\[[0-9;?]*[A-Za-ln-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F\x9B\x9D]/g;
18526
18710
  function stripControl(value) {
18527
18711
  return value.replace(CONTROL_CHARS, (_match, sgr) => sgr ?? "");
@@ -18540,14 +18724,42 @@ var WRAPPER_ARRAY_KEYS = [
18540
18724
 
18541
18725
  class OutputService {
18542
18726
  noColor;
18727
+ noUnicode;
18728
+ locale;
18543
18729
  jsonFormatOptions = {};
18730
+ activeSpinner = null;
18544
18731
  constructor(options) {
18545
18732
  this.noColor = options?.noColor ?? false;
18733
+ this.noUnicode = options?.noUnicode ?? false;
18734
+ this.locale = options?.locale ?? DEFAULT_LOCALE;
18546
18735
  }
18547
18736
  setJsonFormatOptions(options) {
18548
18737
  this.jsonFormatOptions = { ...options };
18549
18738
  }
18739
+ isJsonMode() {
18740
+ return this.jsonFormatOptions.json === true;
18741
+ }
18742
+ spinner(text) {
18743
+ const enabled = !this.isJsonMode() && !!process.stdout.isTTY && true;
18744
+ if (!enabled) {
18745
+ return createNoopSpinner();
18746
+ }
18747
+ this.activeSpinner?.stop();
18748
+ const spinner = new Spinner(text, {
18749
+ enabled: true,
18750
+ noColor: this.noColor,
18751
+ stream: process.stdout,
18752
+ onStop: () => {
18753
+ if (this.activeSpinner === spinner) {
18754
+ this.activeSpinner = null;
18755
+ }
18756
+ }
18757
+ });
18758
+ this.activeSpinner = spinner;
18759
+ return spinner;
18760
+ }
18550
18761
  async json(data) {
18762
+ this.stopActiveSpinner();
18551
18763
  const { fields, jq } = this.jsonFormatOptions;
18552
18764
  let result = data;
18553
18765
  if (fields && fields.length > 0) {
@@ -18565,14 +18777,17 @@ class OutputService {
18565
18777
  console.log(JSON.stringify(result, null, 2));
18566
18778
  }
18567
18779
  jsonError(data) {
18780
+ this.stopActiveSpinner();
18568
18781
  console.error(JSON.stringify(data));
18569
18782
  }
18570
18783
  table(headers, rows) {
18784
+ this.stopActiveSpinner();
18571
18785
  if (rows.length === 0) {
18572
18786
  return;
18573
18787
  }
18574
- const sanitizedHeaders = headers.map(stripControl);
18575
- const sanitizedRows = rows.map((row) => row.map((cell) => stripControl(cell || "")));
18788
+ const sanitizeCell = (cell) => stripControl(cell).replace(/[\t\n\r]+/g, " ");
18789
+ const sanitizedHeaders = headers.map(sanitizeCell);
18790
+ const sanitizedRows = rows.map((row) => row.map((cell) => sanitizeCell(cell || "")));
18576
18791
  const widths = sanitizedHeaders.map((header, index) => {
18577
18792
  const maxRowWidth = Math.max(...sanitizedRows.map((row) => (row[index] || "").length));
18578
18793
  return Math.max(header.length, maxRowWidth);
@@ -18586,24 +18801,47 @@ class OutputService {
18586
18801
  }
18587
18802
  }
18588
18803
  success(message) {
18589
- const symbol = this.format("\u2713", source_default.green);
18804
+ this.stopActiveSpinner();
18805
+ const symbol = this.format(this.symbol("\u2713", "OK"), source_default.green);
18590
18806
  console.log(`${symbol} ${stripControl(message)}`);
18591
18807
  }
18592
18808
  error(message) {
18593
- const symbol = this.format("\u2717", source_default.red);
18809
+ this.stopActiveSpinner();
18810
+ const symbol = this.format(this.symbol("\u2717", "ERR"), source_default.red);
18594
18811
  console.error(`${symbol} ${stripControl(message)}`);
18595
18812
  }
18596
18813
  warning(message) {
18597
- const symbol = this.format("\u26A0", source_default.yellow);
18814
+ this.stopActiveSpinner();
18815
+ const symbol = this.format(this.symbol("\u26A0", "!!"), source_default.yellow);
18598
18816
  console.warn(`${symbol} ${stripControl(message)}`);
18599
18817
  }
18600
18818
  info(message) {
18601
- const symbol = this.format("\u2139", source_default.blue);
18819
+ this.stopActiveSpinner();
18820
+ const symbol = this.format(this.symbol("\u2139", "i"), source_default.blue);
18602
18821
  console.log(`${symbol} ${stripControl(message)}`);
18603
18822
  }
18823
+ symbol(unicode, ascii) {
18824
+ return this.noUnicode ? ascii : unicode;
18825
+ }
18604
18826
  text(message) {
18827
+ this.stopActiveSpinner();
18605
18828
  console.log(stripControl(message));
18606
18829
  }
18830
+ separator(width = 60) {
18831
+ this.stopActiveSpinner();
18832
+ if (width <= 0) {
18833
+ console.log("");
18834
+ return;
18835
+ }
18836
+ console.log(this.format(this.symbol("\u2500", "-").repeat(width), source_default.gray));
18837
+ }
18838
+ stopActiveSpinner() {
18839
+ if (this.activeSpinner) {
18840
+ const spinner = this.activeSpinner;
18841
+ this.activeSpinner = null;
18842
+ spinner.stop();
18843
+ }
18844
+ }
18607
18845
  truncate(text, maxLength, suffix = "...") {
18608
18846
  if (maxLength <= 0 || text.length <= maxLength) {
18609
18847
  return text;
@@ -18615,13 +18853,11 @@ class OutputService {
18615
18853
  }
18616
18854
  formatDate(date) {
18617
18855
  const d = typeof date === "string" ? new Date(date) : date;
18618
- return d.toLocaleDateString("en-US", {
18619
- year: "numeric",
18620
- month: "short",
18621
- day: "numeric",
18622
- hour: "2-digit",
18623
- minute: "2-digit"
18624
- });
18856
+ try {
18857
+ return d.toLocaleDateString(this.locale, DATE_FORMAT_OPTIONS);
18858
+ } catch {
18859
+ return d.toLocaleDateString(DEFAULT_LOCALE, DATE_FORMAT_OPTIONS);
18860
+ }
18625
18861
  }
18626
18862
  format(text, formatter) {
18627
18863
  if (this.noColor) {
@@ -22650,7 +22886,7 @@ function redactRequestUrl(requestUrl, baseUrl) {
22650
22886
  return queryIdx === -1 ? raw : `${raw.slice(0, queryIdx)}?[redacted]`;
22651
22887
  }
22652
22888
  }
22653
- function createApiClient(credentialStore, oauthService) {
22889
+ function createApiClient(credentialStore, output, oauthService) {
22654
22890
  const instance = axios_default.create({
22655
22891
  baseURL: BASE_URL,
22656
22892
  headers: {
@@ -22714,7 +22950,9 @@ function createApiClient(credentialStore, oauthService) {
22714
22950
  const delay = getRetryDelay(error, config.__retryCount);
22715
22951
  const status = error.response.status;
22716
22952
  const label = status === 429 ? "Rate limited" : `Server error (${status})`;
22717
- console.error(`${label}, retrying in ${(delay / 1000).toFixed(1)}s (attempt ${config.__retryCount}/${MAX_RETRIES})...`);
22953
+ if (!output.isJsonMode()) {
22954
+ output.warning(`${label}, retrying in ${(delay / 1000).toFixed(1)}s (attempt ${config.__retryCount}/${MAX_RETRIES})...`);
22955
+ }
22718
22956
  await sleep(delay);
22719
22957
  return instance(config);
22720
22958
  }
@@ -23333,11 +23571,14 @@ function parseLimit(limit, fallback = DEFAULT_LIMIT) {
23333
23571
  }
23334
23572
  return parsed;
23335
23573
  }
23336
- async function collectPages(options) {
23574
+ function resolveLimit(options) {
23575
+ return options.all ? Number.POSITIVE_INFINITY : parseLimit(options.limit);
23576
+ }
23577
+ async function collectPagesWithMeta(options) {
23337
23578
  const { fetchPage, shouldInclude } = options;
23338
23579
  const limit = Math.max(0, options.limit);
23339
23580
  if (limit === 0) {
23340
- return [];
23581
+ return { items: [], hasMore: false };
23341
23582
  }
23342
23583
  const requestedPageSize = options.pageSize ?? limit;
23343
23584
  const pagelen = Math.max(1, Math.min(requestedPageSize, MAX_PAGE_LENGTH));
@@ -23349,13 +23590,15 @@ async function collectPages(options) {
23349
23590
  if (pageValues.length === 0) {
23350
23591
  break;
23351
23592
  }
23352
- for (const value of pageValues) {
23593
+ for (let i = 0;i < pageValues.length; i += 1) {
23594
+ const value = pageValues[i];
23353
23595
  if (shouldInclude && !shouldInclude(value)) {
23354
23596
  continue;
23355
23597
  }
23356
23598
  items.push(value);
23357
23599
  if (items.length >= limit) {
23358
- return items;
23600
+ const moreOnThisPage = pageValues.slice(i + 1).some((rest) => !shouldInclude || shouldInclude(rest));
23601
+ return { items, hasMore: moreOnThisPage || Boolean(data.next) };
23359
23602
  }
23360
23603
  }
23361
23604
  if (!data.next) {
@@ -23363,7 +23606,10 @@ async function collectPages(options) {
23363
23606
  }
23364
23607
  page += 1;
23365
23608
  }
23366
- return items;
23609
+ return { items, hasMore: false };
23610
+ }
23611
+ async function collectPages(options) {
23612
+ return (await collectPagesWithMeta(options)).items;
23367
23613
  }
23368
23614
 
23369
23615
  // src/services/default-reviewer.service.ts
@@ -27245,6 +27491,7 @@ class BaseCommand {
27245
27491
  }
27246
27492
  async run(options, context) {
27247
27493
  this.output.setJsonFormatOptions({
27494
+ json: !!context.globalOptions.json,
27248
27495
  fields: context.globalOptions.jsonFields,
27249
27496
  jq: context.globalOptions.jq
27250
27497
  });
@@ -27340,6 +27587,26 @@ class BaseCommand {
27340
27587
  }
27341
27588
  return parsed;
27342
27589
  }
27590
+ truncateText(text, maxLength, opts = {}) {
27591
+ if (opts.noTruncate) {
27592
+ return text;
27593
+ }
27594
+ return this.output.truncate(text, maxLength);
27595
+ }
27596
+ printMoreHint(shown, hasMore, noun = "results") {
27597
+ if (!hasMore)
27598
+ return;
27599
+ this.output.text(this.output.dim(`Showing ${shown} ${noun}. Use --limit <n> or --all to see more.`));
27600
+ }
27601
+ requireConfirmation(confirmed, warning) {
27602
+ if (confirmed)
27603
+ return;
27604
+ throw new BBError({
27605
+ code: 5001 /* VALIDATION_REQUIRED */,
27606
+ message: `${warning}
27607
+ Use --yes to confirm.`
27608
+ });
27609
+ }
27343
27610
  parseEnumOption(value, name, allowed) {
27344
27611
  if (!allowed.includes(value)) {
27345
27612
  throw new BBError({
@@ -27628,7 +27895,12 @@ class CloneCommand extends BaseCommand {
27628
27895
  async execute(options, context) {
27629
27896
  const { repository, directory } = options;
27630
27897
  const repoUrl = await this.resolveRepositoryUrl(repository);
27631
- await this.gitService.clone(repoUrl, directory);
27898
+ const spinner = this.output.spinner(`Cloning ${repository}...`).start();
27899
+ try {
27900
+ await this.gitService.clone(repoUrl, directory);
27901
+ } finally {
27902
+ spinner.stop();
27903
+ }
27632
27904
  const targetDir = directory || this.extractRepoName(repository);
27633
27905
  if (context.globalOptions.json) {
27634
27906
  await this.output.json({
@@ -28020,8 +28292,8 @@ class ListReposCommand extends BaseCommand {
28020
28292
  }
28021
28293
  async execute(options, context) {
28022
28294
  const workspace = await this.contextService.requireWorkspace(options.workspace ?? context.globalOptions.workspace);
28023
- const limit = parseLimit(options.limit);
28024
- const repos = await collectPages({
28295
+ const limit = resolveLimit(options);
28296
+ const { items: repos, hasMore } = await collectPagesWithMeta({
28025
28297
  limit,
28026
28298
  fetchPage: async (page, pagelen) => {
28027
28299
  const response = await this.repositoriesApi.repositoriesWorkspaceGet({
@@ -28041,15 +28313,16 @@ class ListReposCommand extends BaseCommand {
28041
28313
  return;
28042
28314
  }
28043
28315
  if (repos.length === 0) {
28044
- this.output.text("No repositories found");
28316
+ this.output.info("No repositories found");
28045
28317
  return;
28046
28318
  }
28047
28319
  const rows = repos.map((repo) => [
28048
28320
  repo.full_name ?? "",
28049
28321
  repo.is_private ? "private" : "public",
28050
- (repo.description || "").substring(0, 50)
28322
+ this.truncateText(repo.description ?? "", 50, context.globalOptions)
28051
28323
  ]);
28052
28324
  this.output.table(["REPOSITORY", "VISIBILITY", "DESCRIPTION"], rows);
28325
+ this.printMoreHint(repos.length, hasMore, "repositories");
28053
28326
  }
28054
28327
  }
28055
28328
 
@@ -28132,13 +28405,7 @@ class DeleteRepoCommand extends BaseCommand {
28132
28405
  contextOptions.repo = repository;
28133
28406
  }
28134
28407
  const repoContext = await this.contextService.requireRepoContext(contextOptions);
28135
- if (!yes) {
28136
- throw new BBError({
28137
- code: 5001 /* VALIDATION_REQUIRED */,
28138
- message: `This will permanently delete ${repoContext.workspace}/${repoContext.repoSlug}.
28139
- ` + "Use --yes to confirm deletion."
28140
- });
28141
- }
28408
+ this.requireConfirmation(yes, `This will permanently delete ${repoContext.workspace}/${repoContext.repoSlug}.`);
28142
28409
  await this.repositoriesApi.repositoriesWorkspaceRepoSlugDelete({
28143
28410
  workspace: repoContext.workspace,
28144
28411
  repoSlug: repoContext.repoSlug
@@ -28245,13 +28512,7 @@ class RemoveDefaultReviewerCommand extends BaseCommand {
28245
28512
  }
28246
28513
  async execute(options, context) {
28247
28514
  const repoContext = await this.contextService.requireRepoContextFor(options, context);
28248
- if (!options.yes) {
28249
- throw new BBError({
28250
- code: 5001 /* VALIDATION_REQUIRED */,
28251
- message: `This will remove ${options.username} from the default reviewers of ` + `${repoContext.workspace}/${repoContext.repoSlug}.
28252
- ` + "Use --yes to confirm."
28253
- });
28254
- }
28515
+ this.requireConfirmation(options.yes, `This will remove ${options.username} from the default reviewers of ` + `${repoContext.workspace}/${repoContext.repoSlug}.`);
28255
28516
  const userResponse = await this.usersApi.usersSelectedUserGet({
28256
28517
  selectedUser: options.username
28257
28518
  });
@@ -28327,12 +28588,18 @@ class CreatePRCommand extends BaseCommand {
28327
28588
  if (reviewers.length > 0) {
28328
28589
  request.reviewers = reviewers.map((r) => ({ type: "user", uuid: r.uuid }));
28329
28590
  }
28330
- const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPost({
28331
- workspace: repoContext.workspace,
28332
- repoSlug: repoContext.repoSlug,
28333
- pullrequest: request
28334
- });
28335
- const pr = response.data;
28591
+ const spinner = this.output.spinner("Creating pull request...").start();
28592
+ let pr;
28593
+ try {
28594
+ const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPost({
28595
+ workspace: repoContext.workspace,
28596
+ repoSlug: repoContext.repoSlug,
28597
+ pullrequest: request
28598
+ });
28599
+ pr = response.data;
28600
+ } finally {
28601
+ spinner.stop();
28602
+ }
28336
28603
  const links = pr.links;
28337
28604
  if (context.globalOptions.json) {
28338
28605
  await this.output.json(pr);
@@ -28423,9 +28690,9 @@ class ListPRsCommand extends BaseCommand {
28423
28690
  async execute(options, context) {
28424
28691
  const repoContext = await this.contextService.requireRepoContextFor(options, context);
28425
28692
  const state = options.state ? this.parseEnumOption(options.state, "state", PR_STATES) : "OPEN";
28426
- const limit = parseLimit(options.limit);
28693
+ const limit = resolveLimit(options);
28427
28694
  const reviewerQuery = options.mine ? await this.buildMineFilter() : undefined;
28428
- const values = await collectPages({
28695
+ const { items: values, hasMore } = await collectPagesWithMeta({
28429
28696
  limit,
28430
28697
  fetchPage: async (page, pagelen) => {
28431
28698
  const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsGet({
@@ -28456,21 +28723,23 @@ class ListPRsCommand extends BaseCommand {
28456
28723
  return;
28457
28724
  }
28458
28725
  if (values.length === 0) {
28459
- this.output.text(`No ${state.toLowerCase()} pull requests found`);
28726
+ this.output.info(`No ${state.toLowerCase()} pull requests found`);
28460
28727
  return;
28461
28728
  }
28729
+ const arrow = this.output.symbol("\u2192", "->");
28462
28730
  const rows = values.map((pr) => {
28463
28731
  const title = pr.draft ? `[DRAFT] ${pr.title}` : pr.title;
28464
28732
  const source = pr.source;
28465
28733
  const destination = pr.destination;
28466
28734
  return [
28467
28735
  `#${pr.id}`,
28468
- this.output.truncate(title ?? "", 50),
28736
+ this.truncateText(title ?? "", 50, context.globalOptions),
28469
28737
  pr.author?.display_name ?? "Unknown",
28470
- `${source?.branch?.name ?? "unknown"} \u2192 ${destination?.branch?.name ?? "unknown"}`
28738
+ `${source?.branch?.name ?? "unknown"} ${arrow} ${destination?.branch?.name ?? "unknown"}`
28471
28739
  ];
28472
28740
  });
28473
28741
  this.output.table(["ID", "TITLE", "AUTHOR", "BRANCHES"], rows);
28742
+ this.printMoreHint(values.length, hasMore, "pull requests");
28474
28743
  }
28475
28744
  async buildMineFilter() {
28476
28745
  const response = await this.usersApi.userGet();
@@ -28519,7 +28788,7 @@ class ViewPRCommand extends BaseCommand {
28519
28788
  const draftLabel = pr.draft ? this.output.yellow(" [DRAFT]") : "";
28520
28789
  this.output.text("");
28521
28790
  this.output.text(`${this.output.bold(`#${pr.id}`)} ${pr.title}${draftLabel} ${stateColor(`[${pr.state}]`)}`);
28522
- this.output.text(this.output.gray("\u2500".repeat(60)));
28791
+ this.output.separator();
28523
28792
  }
28524
28793
  renderDescription(pr) {
28525
28794
  if (pr.description) {
@@ -28532,7 +28801,7 @@ class ViewPRCommand extends BaseCommand {
28532
28801
  const destination = pr.destination;
28533
28802
  const sourceBranch = this.output.cyan(source?.branch?.name ?? "unknown");
28534
28803
  const destBranch = this.output.cyan(destination?.branch?.name ?? "unknown");
28535
- const arrow = this.output.gray(" \u2192 ");
28804
+ const arrow = this.output.gray(this.output.symbol(" \u2192 ", " -> "));
28536
28805
  this.output.text(`${this.output.dim("Branch:")} ${sourceBranch}${arrow}${destBranch}`);
28537
28806
  if (source?.commit?.hash || destination?.commit?.hash) {
28538
28807
  const sourceHash = source?.commit?.hash ? this.output.gray(source.commit.hash.slice(0, 7)) : this.output.gray("unknown");
@@ -28554,7 +28823,7 @@ class ViewPRCommand extends BaseCommand {
28554
28823
  if (mergeCommit?.hash) {
28555
28824
  this.output.text(`${this.output.dim("Merge:")} ${this.output.magenta(mergeCommit.hash.slice(0, 7))}`);
28556
28825
  }
28557
- const closeBranchIndicator = pr.close_source_branch ? this.output.green("\u2713") : this.output.gray("\u2717");
28826
+ const closeBranchIndicator = pr.close_source_branch ? this.output.green(this.output.symbol("\u2713", "yes")) : this.output.gray(this.output.symbol("\u2717", "no"));
28558
28827
  this.output.text(`${this.output.dim("Close Src:")} ${closeBranchIndicator} ${this.output.gray("(close source branch on merge)")}`);
28559
28828
  this.output.text(`${this.output.dim("Activity:")} ${pr.comment_count ?? 0} comments \xB7 ${pr.task_count ?? 0} tasks`);
28560
28829
  }
@@ -28563,7 +28832,7 @@ class ViewPRCommand extends BaseCommand {
28563
28832
  const reviewers = participants.filter((p) => p.role === "REVIEWER");
28564
28833
  if (reviewers.length === 0) {
28565
28834
  this.output.text("");
28566
- this.output.text(this.output.gray("No reviewers assigned"));
28835
+ this.output.info("No reviewers assigned");
28567
28836
  return;
28568
28837
  }
28569
28838
  this.output.text("");
@@ -28578,17 +28847,26 @@ class ViewPRCommand extends BaseCommand {
28578
28847
  }
28579
28848
  getReviewerStatus(reviewer) {
28580
28849
  if (reviewer.approved) {
28581
- return { icon: this.output.green("\u2713"), label: "approved" };
28850
+ return {
28851
+ icon: this.output.green(this.output.symbol("\u2713", "[OK]")),
28852
+ label: "approved"
28853
+ };
28582
28854
  }
28583
28855
  if (reviewer.state === "changes_requested") {
28584
- return { icon: this.output.red("\u2717"), label: "changes requested" };
28856
+ return {
28857
+ icon: this.output.red(this.output.symbol("\u2717", "[X]")),
28858
+ label: "changes requested"
28859
+ };
28585
28860
  }
28586
- return { icon: this.output.yellow("\u25CB"), label: "pending" };
28861
+ return {
28862
+ icon: this.output.yellow(this.output.symbol("\u25CB", "[ ]")),
28863
+ label: "pending"
28864
+ };
28587
28865
  }
28588
28866
  renderFooter(pr) {
28589
28867
  const links = pr.links;
28590
28868
  this.output.text("");
28591
- this.output.text(this.output.gray("\u2500".repeat(60)));
28869
+ this.output.separator();
28592
28870
  this.output.text(`${this.output.dim("URL:")} ${this.output.underline(this.output.blue(links?.html?.href ?? ""))}`);
28593
28871
  this.output.text("");
28594
28872
  }
@@ -28699,7 +28977,7 @@ class EditPRCommand extends BaseCommand {
28699
28977
  this.output.success(`Updated pull request #${pr.id}`);
28700
28978
  this.output.text(` ${this.output.dim("Title:")} ${pr.title}`);
28701
28979
  if (pr.description) {
28702
- const truncatedDesc = this.output.truncate(pr.description, 100);
28980
+ const truncatedDesc = this.truncateText(pr.description, 100, context.globalOptions);
28703
28981
  this.output.text(` ${this.output.dim("Description:")} ${truncatedDesc}`);
28704
28982
  }
28705
28983
  this.output.text(` ${this.output.dim("URL:")} ${links?.html?.href}`);
@@ -28734,13 +29012,19 @@ class MergePRCommand extends BaseCommand {
28734
29012
  if (options.strategy) {
28735
29013
  request.merge_strategy = this.parseEnumOption(options.strategy, "strategy", VALID_STRATEGIES);
28736
29014
  }
28737
- const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdMergePost({
28738
- workspace: repoContext.workspace,
28739
- repoSlug: repoContext.repoSlug,
28740
- pullRequestId: prId,
28741
- pullrequestMergeParameters: request
28742
- });
28743
- const pr = response.data;
29015
+ const spinner = this.output.spinner(`Merging pull request #${prId}...`).start();
29016
+ let pr;
29017
+ try {
29018
+ const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdMergePost({
29019
+ workspace: repoContext.workspace,
29020
+ repoSlug: repoContext.repoSlug,
29021
+ pullRequestId: prId,
29022
+ pullrequestMergeParameters: request
29023
+ });
29024
+ pr = response.data;
29025
+ } finally {
29026
+ spinner.stop();
29027
+ }
28744
29028
  if (context.globalOptions.json) {
28745
29029
  await this.output.json({
28746
29030
  success: true,
@@ -29153,8 +29437,8 @@ class ActivityPRCommand extends BaseCommand {
29153
29437
  const repoContext = await this.contextService.requireRepoContextFor(options, context);
29154
29438
  const prId = this.parsePositiveInt(options.id, "id");
29155
29439
  const filterTypes = this.parseTypeFilter(options.type);
29156
- const limit = parseLimit(options.limit);
29157
- const activities = await collectPages({
29440
+ const limit = resolveLimit(options);
29441
+ const { items: activities, hasMore } = await collectPagesWithMeta({
29158
29442
  limit,
29159
29443
  fetchPage: async (page, pagelen) => {
29160
29444
  const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdActivityGet({
@@ -29200,10 +29484,11 @@ class ActivityPRCommand extends BaseCommand {
29200
29484
  activityType.toUpperCase(),
29201
29485
  this.getActorName(activity),
29202
29486
  this.formatActivityDate(activity),
29203
- this.buildActivityDetails(activity, activityType)
29487
+ this.buildActivityDetails(activity, activityType, context.globalOptions)
29204
29488
  ];
29205
29489
  });
29206
29490
  this.output.table(["TYPE", "ACTOR", "DATE", "DETAILS"], rows);
29491
+ this.printMoreHint(activities.length, hasMore, "activity entries");
29207
29492
  }
29208
29493
  parseTypeFilter(typeOption) {
29209
29494
  if (!typeOption) {
@@ -29255,19 +29540,19 @@ class ActivityPRCommand extends BaseCommand {
29255
29540
  }
29256
29541
  return this.output.formatDate(date);
29257
29542
  }
29258
- buildActivityDetails(activity, type) {
29543
+ buildActivityDetails(activity, type, globalOptions) {
29259
29544
  switch (type) {
29260
29545
  case "comment": {
29261
29546
  const content = getRawContent(activity.comment?.content) ?? "";
29262
29547
  const id = activity.comment?.id ? `#${activity.comment.id}` : "";
29263
- const snippet = this.output.truncate(content, 80);
29548
+ const snippet = this.truncateText(content, 80, globalOptions);
29264
29549
  return [id, snippet].filter(Boolean).join(" ");
29265
29550
  }
29266
29551
  case "approval":
29267
29552
  return "approved";
29268
29553
  case "changes_requested": {
29269
29554
  const reason = activity.changes_requested?.reason;
29270
- return reason ? this.output.truncate(reason, 80) : "changes requested";
29555
+ return reason ? this.truncateText(reason, 80, globalOptions) : "changes requested";
29271
29556
  }
29272
29557
  case "merge":
29273
29558
  return this.formatCommitDetail(activity.merge?.commit?.hash, "merged");
@@ -29280,7 +29565,7 @@ class ActivityPRCommand extends BaseCommand {
29280
29565
  return `state: ${activity.update.state}`;
29281
29566
  }
29282
29567
  if (activity.update?.title) {
29283
- return `title: ${this.output.truncate(activity.update.title, 60)}`;
29568
+ return `title: ${this.truncateText(activity.update.title, 60, globalOptions)}`;
29284
29569
  }
29285
29570
  if (activity.update?.description) {
29286
29571
  return "description updated";
@@ -29390,8 +29675,8 @@ class ListCommentsPRCommand extends BaseCommand {
29390
29675
  async execute(options, context) {
29391
29676
  const repoContext = await this.contextService.requireRepoContextFor(options, context);
29392
29677
  const prId = this.parsePositiveInt(options.id, "id");
29393
- const limit = parseLimit(options.limit);
29394
- const values = await collectPages({
29678
+ const limit = resolveLimit(options);
29679
+ const { items: values, hasMore } = await collectPagesWithMeta({
29395
29680
  limit,
29396
29681
  fetchPage: async (page, pagelen) => {
29397
29682
  const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdCommentsGet({
@@ -29423,11 +29708,12 @@ class ListCommentsPRCommand extends BaseCommand {
29423
29708
  return [
29424
29709
  comment.id?.toString() ?? "",
29425
29710
  getUserDisplayName(comment.user) ?? "Unknown",
29426
- comment.deleted ? "[deleted]" : options.truncate === false ? content : this.output.truncate(content, 60),
29711
+ comment.deleted ? "[deleted]" : this.truncateText(content, 60, context.globalOptions),
29427
29712
  this.output.formatDate(comment.created_on ?? "")
29428
29713
  ];
29429
29714
  });
29430
29715
  this.output.table(["ID", "Author", "Content", "Date"], rows);
29716
+ this.printMoreHint(values.length, hasMore, "comments");
29431
29717
  }
29432
29718
  }
29433
29719
 
@@ -29486,13 +29772,7 @@ class DeleteCommentPRCommand extends BaseCommand {
29486
29772
  const repoContext = await this.contextService.requireRepoContextFor(options, context);
29487
29773
  const prId = this.parsePositiveInt(options.prId, "pr-id");
29488
29774
  const commentId = this.parsePositiveInt(options.commentId, "comment-id");
29489
- if (!options.yes) {
29490
- throw new BBError({
29491
- code: 5001 /* VALIDATION_REQUIRED */,
29492
- message: `This will permanently delete comment #${commentId} on PR #${prId}.
29493
- ` + "Use --yes to confirm deletion."
29494
- });
29495
- }
29775
+ this.requireConfirmation(options.yes, `This will permanently delete comment #${commentId} on PR #${prId}.`);
29496
29776
  await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdCommentsCommentIdDelete({
29497
29777
  workspace: repoContext.workspace,
29498
29778
  repoSlug: repoContext.repoSlug,
@@ -29666,7 +29946,7 @@ class ChecksPRCommand extends BaseCommand {
29666
29946
  return;
29667
29947
  }
29668
29948
  this.renderHeader(prId, statuses.length);
29669
- this.renderStatuses(statuses, summary);
29949
+ this.renderStatuses(statuses, summary, context.globalOptions);
29670
29950
  }
29671
29951
  formatStatusForJson(status) {
29672
29952
  return {
@@ -29685,9 +29965,9 @@ class ChecksPRCommand extends BaseCommand {
29685
29965
  this.output.text("");
29686
29966
  const title = this.output.bold("Pull Request #" + prId);
29687
29967
  this.output.text(`${title} - ${count} check${count === 1 ? "" : "s"}`);
29688
- this.output.text(this.output.gray("-".repeat(60)));
29968
+ this.output.separator();
29689
29969
  }
29690
- renderStatuses(statuses, summary) {
29970
+ renderStatuses(statuses, summary, globalOptions) {
29691
29971
  const rows = statuses.map((status) => {
29692
29972
  const stateIcon = this.getStateIcon(status.state);
29693
29973
  const stateLabel = this.getStateLabel(status.state);
@@ -29696,7 +29976,7 @@ class ChecksPRCommand extends BaseCommand {
29696
29976
  return [
29697
29977
  `${stateIcon} ${stateLabel}`,
29698
29978
  this.output.bold(name),
29699
- this.output.truncate(description, 40),
29979
+ this.truncateText(description, 40, globalOptions),
29700
29980
  status.updated_on ? this.output.formatDate(status.updated_on) : "-"
29701
29981
  ];
29702
29982
  });
@@ -29763,9 +30043,9 @@ class ListSnippetsCommand extends BaseCommand {
29763
30043
  }
29764
30044
  async execute(options, context) {
29765
30045
  const workspace = await this.contextService.requireWorkspace(options.workspace ?? context.globalOptions.workspace);
29766
- const limit = parseLimit(options.limit);
30046
+ const limit = resolveLimit(options);
29767
30047
  const role = options.role ? this.parseEnumOption(options.role, "role", VALID_ROLES) : undefined;
29768
- const snippets = await collectPages({
30048
+ const { items: snippets, hasMore } = await collectPagesWithMeta({
29769
30049
  limit,
29770
30050
  fetchPage: async (page, pagelen) => {
29771
30051
  const response = await this.snippetsApi.snippetsWorkspaceGet({
@@ -29786,7 +30066,7 @@ class ListSnippetsCommand extends BaseCommand {
29786
30066
  return;
29787
30067
  }
29788
30068
  if (snippets.length === 0) {
29789
- this.output.text("No snippets found");
30069
+ this.output.info("No snippets found");
29790
30070
  return;
29791
30071
  }
29792
30072
  const rows = snippets.map((snippet) => [
@@ -29797,6 +30077,7 @@ class ListSnippetsCommand extends BaseCommand {
29797
30077
  this.output.formatDate(snippet.updated_on ?? "")
29798
30078
  ]);
29799
30079
  this.output.table(["ID", "TITLE", "VISIBILITY", "CREATOR", "UPDATED"], rows);
30080
+ this.printMoreHint(snippets.length, hasMore, "snippets");
29800
30081
  }
29801
30082
  }
29802
30083
 
@@ -29847,9 +30128,10 @@ class ViewSnippetCommand extends BaseCommand {
29847
30128
  return;
29848
30129
  }
29849
30130
  this.renderSnippet(snippet, fileNames);
30131
+ const fileSep = this.output.symbol("\u2500\u2500", "--");
29850
30132
  for (const name of fileNames) {
29851
30133
  this.output.text("");
29852
- this.output.text(this.output.bold(`\u2500\u2500 ${name} \u2500\u2500`));
30134
+ this.output.text(this.output.bold(`${fileSep} ${name} ${fileSep}`));
29853
30135
  this.output.text(contents[name] ?? "");
29854
30136
  }
29855
30137
  return;
@@ -29871,7 +30153,7 @@ class ViewSnippetCommand extends BaseCommand {
29871
30153
  const visibility = snippet.is_private ? "private" : "public";
29872
30154
  this.output.text("");
29873
30155
  this.output.text(`${this.output.bold(String(snippet.id ?? ""))} ${snippet.title ?? "Untitled"} ${this.output.gray(`[${visibility}]`)}`);
29874
- this.output.text(this.output.gray("\u2500".repeat(60)));
30156
+ this.output.separator();
29875
30157
  const creator = getUserDisplayName(snippet.creator);
29876
30158
  if (creator) {
29877
30159
  this.output.text(`Creator: ${creator}`);
@@ -30032,13 +30314,7 @@ class DeleteSnippetCommand extends BaseCommand {
30032
30314
  }
30033
30315
  async execute(options, context) {
30034
30316
  const workspace = await this.contextService.requireWorkspace(options.workspace ?? context.globalOptions.workspace);
30035
- if (!options.yes) {
30036
- throw new BBError({
30037
- code: 5001 /* VALIDATION_REQUIRED */,
30038
- message: `This will permanently delete snippet ${options.id}.
30039
- ` + "Use --yes to confirm deletion."
30040
- });
30041
- }
30317
+ this.requireConfirmation(options.yes, `This will permanently delete snippet ${options.id}.`);
30042
30318
  await this.snippetsApi.snippetsWorkspaceEncodedIdDelete({
30043
30319
  workspace,
30044
30320
  encodedId: options.id
@@ -30126,8 +30402,8 @@ class ListSnippetCommentsCommand extends BaseCommand {
30126
30402
  }
30127
30403
  async execute(options, context) {
30128
30404
  const workspace = await this.contextService.requireWorkspace(options.workspace ?? context.globalOptions.workspace);
30129
- const limit = parseLimit(options.limit);
30130
- const comments = await collectPages({
30405
+ const limit = resolveLimit(options);
30406
+ const { items: comments, hasMore } = await collectPagesWithMeta({
30131
30407
  limit,
30132
30408
  fetchPage: async (page, pagelen) => {
30133
30409
  const response = await this.snippetsApi.snippetsWorkspaceEncodedIdCommentsGet({
@@ -30158,10 +30434,11 @@ class ListSnippetCommentsCommand extends BaseCommand {
30158
30434
  String(comment.id ?? ""),
30159
30435
  getUserDisplayName(comment.user) ?? "Unknown",
30160
30436
  this.output.formatDate(comment.created_on ?? ""),
30161
- this.output.truncate(content, 60)
30437
+ this.truncateText(content, 60, context.globalOptions)
30162
30438
  ];
30163
30439
  });
30164
30440
  this.output.table(["ID", "AUTHOR", "DATE", "CONTENT"], rows);
30441
+ this.printMoreHint(comments.length, hasMore, "comments");
30165
30442
  }
30166
30443
  }
30167
30444
 
@@ -30256,13 +30533,7 @@ class DeleteSnippetCommentCommand extends BaseCommand {
30256
30533
  async execute(options, context) {
30257
30534
  const workspace = await this.contextService.requireWorkspace(options.workspace ?? context.globalOptions.workspace);
30258
30535
  const commentId = this.parsePositiveInt(options.commentId, "comment-id");
30259
- if (!options.yes) {
30260
- throw new BBError({
30261
- code: 5001 /* VALIDATION_REQUIRED */,
30262
- message: `This will permanently delete comment #${commentId} on snippet ${options.snippetId}.
30263
- ` + "Use --yes to confirm deletion."
30264
- });
30265
- }
30536
+ this.requireConfirmation(options.yes, `This will permanently delete comment #${commentId} on snippet ${options.snippetId}.`);
30266
30537
  await this.snippetsApi.snippetsWorkspaceEncodedIdCommentsCommentIdDelete({
30267
30538
  workspace,
30268
30539
  encodedId: options.snippetId,
@@ -30406,7 +30677,7 @@ class ListConfigCommand extends BaseCommand {
30406
30677
  String(value)
30407
30678
  ]);
30408
30679
  if (rows.length === 0) {
30409
- this.output.text("No configuration set");
30680
+ this.output.info("No configuration set");
30410
30681
  } else {
30411
30682
  this.output.table(["KEY", "VALUE"], rows);
30412
30683
  }
@@ -30667,7 +30938,8 @@ function registerApiClient(container, token, ctor) {
30667
30938
  container.register(token, () => {
30668
30939
  const credentialStore = container.resolve(ServiceTokens.CredentialStore);
30669
30940
  const oauthService = container.resolve(ServiceTokens.OAuthService);
30670
- const axiosInstance = createApiClient(credentialStore, oauthService);
30941
+ const outputService = container.resolve(ServiceTokens.OutputService);
30942
+ const axiosInstance = createApiClient(credentialStore, outputService, oauthService);
30671
30943
  return new ctor(undefined, undefined, axiosInstance);
30672
30944
  });
30673
30945
  }
@@ -30682,7 +30954,11 @@ function bootstrap(options = {}) {
30682
30954
  container.register(ServiceTokens.ConfigService, () => new ConfigService);
30683
30955
  container.register(ServiceTokens.CredentialStore, () => container.resolve(ServiceTokens.ConfigService));
30684
30956
  container.register(ServiceTokens.GitService, () => new GitService);
30685
- container.register(ServiceTokens.OutputService, () => new OutputService({ noColor: options.noColor }));
30957
+ container.register(ServiceTokens.OutputService, () => new OutputService({
30958
+ noColor: options.noColor,
30959
+ noUnicode: options.noUnicode,
30960
+ locale: options.locale
30961
+ }));
30686
30962
  registerCommand(container, ServiceTokens.OAuthService, OAuthService, [
30687
30963
  ServiceTokens.ConfigService,
30688
30964
  ServiceTokens.CredentialStore
@@ -30698,7 +30974,8 @@ function bootstrap(options = {}) {
30698
30974
  container.register(ServiceTokens.SnippetsAxios, () => {
30699
30975
  const credentialStore = container.resolve(ServiceTokens.CredentialStore);
30700
30976
  const oauthService = container.resolve(ServiceTokens.OAuthService);
30701
- return createApiClient(credentialStore, oauthService);
30977
+ const outputService = container.resolve(ServiceTokens.OutputService);
30978
+ return createApiClient(credentialStore, outputService, oauthService);
30702
30979
  });
30703
30980
  container.register(ServiceTokens.SnippetsApi, () => {
30704
30981
  const axiosInstance = container.resolve(ServiceTokens.SnippetsAxios);
@@ -31043,8 +31320,11 @@ var ROOT_COMPLETIONS = [
31043
31320
  "--version",
31044
31321
  "--json",
31045
31322
  "--no-color",
31323
+ "--no-unicode",
31324
+ "--no-truncate",
31046
31325
  "--workspace",
31047
- "--repo"
31326
+ "--repo",
31327
+ "--locale"
31048
31328
  ];
31049
31329
  var SUBCOMMAND_COMPLETIONS = new Map([
31050
31330
  ["auth", ["login", "logout", "status", "token"]],
@@ -31111,6 +31391,25 @@ if (process.argv.includes("--get-yargs-completions") || process.env.COMP_LINE) {
31111
31391
  process.exit(0);
31112
31392
  }
31113
31393
  }
31394
+ function extractLocaleArg(argv) {
31395
+ for (let i = 0;i < argv.length; i++) {
31396
+ const arg = argv[i];
31397
+ if (arg === undefined) {
31398
+ continue;
31399
+ }
31400
+ if (arg === "--locale") {
31401
+ const next = argv[i + 1];
31402
+ if (typeof next === "string" && !next.startsWith("-")) {
31403
+ return next;
31404
+ }
31405
+ return;
31406
+ }
31407
+ if (arg.startsWith("--locale=")) {
31408
+ return arg.slice("--locale=".length);
31409
+ }
31410
+ }
31411
+ return;
31412
+ }
31114
31413
  function resolveNoColorSetting(argv, env2) {
31115
31414
  const hasColorArg = argv.includes("--color");
31116
31415
  const hasNoColorArg = argv.includes("--no-color");
@@ -31127,9 +31426,20 @@ function resolveNoColorSetting(argv, env2) {
31127
31426
  }
31128
31427
  return hasNoColorEnv;
31129
31428
  }
31429
+ function resolveNoUnicodeSetting(argv, env2) {
31430
+ if (argv.includes("--no-unicode")) {
31431
+ return true;
31432
+ }
31433
+ return env2.BB_NO_UNICODE !== undefined && env2.BB_NO_UNICODE !== "";
31434
+ }
31130
31435
  var noColor = resolveNoColorSetting(process.argv, process.env);
31436
+ var noUnicode = resolveNoUnicodeSetting(process.argv, process.env);
31131
31437
  var buildHelpText = createHelpTextBuilder(noColor);
31132
- var container = bootstrap({ noColor });
31438
+ var locale = resolveLocale({
31439
+ explicit: extractLocaleArg(process.argv),
31440
+ env: process.env
31441
+ });
31442
+ var container = bootstrap({ noColor, noUnicode, locale });
31133
31443
  function createContext(program2) {
31134
31444
  const opts = program2.opts();
31135
31445
  const jsonOpt = opts.json;
@@ -31160,6 +31470,8 @@ function createContext(program2) {
31160
31470
  jsonFields,
31161
31471
  jq: jqOpt,
31162
31472
  noColor: opts.color === false,
31473
+ noUnicode: opts.unicode === false || noUnicode,
31474
+ noTruncate: opts.truncate === false,
31163
31475
  workspace: opts.workspace,
31164
31476
  repo: opts.repo
31165
31477
  },
@@ -31189,13 +31501,15 @@ function withGlobalOptions(options, context) {
31189
31501
  };
31190
31502
  }
31191
31503
  var cli = new Command;
31192
- cli.name("bb").description("A command-line interface for Bitbucket Cloud").version(pkg2.version).option("--json [fields]", "Output as JSON; optionally project to a comma-separated field list (e.g. number,title,author.display_name)").option("--jq <expression>", `Filter the JSON output through a jq expression \u2014 runs in-process via embedded jq, requires --json (e.g. '.pullRequests[] | select(.state == "OPEN") | .title')`).option("--no-color", "Disable color output").option("-w, --workspace <workspace>", "Specify workspace").option("-r, --repo <repo>", "Specify repository").addHelpText("after", buildHelpText({
31504
+ cli.name("bb").description("A command-line interface for Bitbucket Cloud").version(pkg2.version).option("--json [fields]", "Output as JSON; optionally project to a comma-separated field list (e.g. number,title,author.display_name)").option("--jq <expression>", `Filter the JSON output through a jq expression \u2014 runs in-process via embedded jq, requires --json (e.g. '.pullRequests[] | select(.state == "OPEN") | .title')`).option("--no-color", "Disable color output").option("--no-unicode", "Use ASCII fallbacks for symbols (separators, arrows, status icons) \u2014 also enabled by BB_NO_UNICODE").option("--no-truncate", "Show full values in table output without truncation").option("--locale <locale>", "BCP-47 locale tag for date/time formatting (e.g. de-DE, ja-JP). Falls back to BB_LOCALE, then LC_TIME/LC_ALL/LANG, then en-US.").option("-w, --workspace <workspace>", "Specify workspace").option("-r, --repo <repo>", "Specify repository").addHelpText("after", buildHelpText({
31193
31505
  envVars: {
31194
31506
  BB_USERNAME: "Bitbucket username (fallback for auth login)",
31195
31507
  BB_API_TOKEN: "Bitbucket API token (fallback for auth login)",
31196
31508
  NO_COLOR: "Disable color output when set",
31197
31509
  FORCE_COLOR: "Force color output when set (and not '0')",
31198
- DEBUG: "Enable HTTP debug logging when 'true'"
31510
+ BB_NO_UNICODE: "Use ASCII fallbacks for symbols when set (any non-empty value)",
31511
+ DEBUG: "Enable HTTP debug logging when 'true'",
31512
+ BB_LOCALE: "BCP-47 locale tag for date/time formatting; --locale takes precedence"
31199
31513
  },
31200
31514
  seeAlso: [
31201
31515
  {
@@ -31219,11 +31533,11 @@ cli.name("bb").description("A command-line interface for Bitbucket Cloud").versi
31219
31533
  const result = await versionService.checkForUpdate();
31220
31534
  if (result?.updateAvailable) {
31221
31535
  output.text("");
31222
- output.text("\u2500".repeat(50));
31223
- output.text(`\u26A0 A new version is available: ${result.latestVersion} (you have ${result.currentVersion})`);
31224
- output.text(` Run '${versionService.getInstallCommand()}' to update`);
31225
- output.text(` Or disable with 'bb config set skipVersionCheck true'`);
31226
- output.text("\u2500".repeat(50));
31536
+ output.separator(50);
31537
+ output.warning(`A new version is available: ${result.latestVersion} (you have ${result.currentVersion})
31538
+ ` + ` Run '${versionService.getInstallCommand()}' to update
31539
+ ` + ` Or disable with 'bb config set skipVersionCheck true'`);
31540
+ output.separator(50);
31227
31541
  }
31228
31542
  } catch {}
31229
31543
  try {
@@ -31292,10 +31606,11 @@ repoCmd.command("create <name>").description("Create a new repository").option("
31292
31606
  const context = createContext(cli);
31293
31607
  await runCommand(ServiceTokens.CreateRepoCommand, withGlobalOptions({ name, ...options }, context), cli, context);
31294
31608
  });
31295
- repoCmd.command("list").description("List repositories").option("--limit <number>", "Maximum number of repositories to list", "25").addHelpText("after", buildHelpText({
31609
+ repoCmd.command("list").description("List repositories").option("--limit <number>", "Maximum number of repositories to list", "25").option("--all", "List all repositories (overrides --limit)").addHelpText("after", buildHelpText({
31296
31610
  examples: [
31297
31611
  "bb repo list",
31298
31612
  "bb repo list --limit 50",
31613
+ "bb repo list --all",
31299
31614
  "bb repo list --json"
31300
31615
  ],
31301
31616
  defaults: { limit: "25" }
@@ -31381,10 +31696,11 @@ prCmd.command("create").description("Create a pull request").option("-t, --title
31381
31696
  const context = createContext(cli);
31382
31697
  await runCommand(ServiceTokens.CreatePRCommand, withGlobalOptions(options, context), cli, context);
31383
31698
  });
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({
31699
+ 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("--all", "List all pull requests (overrides --limit)").option("--mine", "Show only PRs where you are a reviewer (not authored by you)").addHelpText("after", buildHelpText({
31385
31700
  examples: [
31386
31701
  "bb pr list",
31387
31702
  "bb pr list -s MERGED --limit 10",
31703
+ "bb pr list --all",
31388
31704
  "bb pr list --mine",
31389
31705
  "bb pr list --json"
31390
31706
  ],
@@ -31412,10 +31728,11 @@ prCmd.command("view <id>").description("View pull request details").addHelpText(
31412
31728
  const context = createContext(cli);
31413
31729
  await runCommand(ServiceTokens.ViewPRCommand, withGlobalOptions({ id, ...options }, context), cli, context);
31414
31730
  });
31415
- 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)").addHelpText("after", buildHelpText({
31731
+ prCmd.command("activity <id>").description("Show pull request activity log").option("--limit <number>", "Maximum number of activity entries", "25").option("--all", "Show all activity entries (overrides --limit)").option("--type <types>", "Filter activity by type (comma-separated)").addHelpText("after", buildHelpText({
31416
31732
  examples: [
31417
31733
  "bb pr activity 42",
31418
31734
  "bb pr activity 42 --type comment,approval",
31735
+ "bb pr activity 42 --all",
31419
31736
  "bb pr activity 42 --limit 10 --json"
31420
31737
  ],
31421
31738
  validValues: {
@@ -31530,10 +31847,11 @@ prCmd.command("diff [id]").description("View pull request diff").option("--color
31530
31847
  await runCommand(ServiceTokens.DiffPRCommand, withGlobalOptions({ id, ...options }, context), cli, context);
31531
31848
  });
31532
31849
  var prCommentsCmd = new Command("comments").description("Manage pull request comments");
31533
- prCommentsCmd.command("list <id>").description("List comments on a pull request").option("--limit <number>", "Maximum number of comments (default: 25)").option("--no-truncate", "Show full comment content without truncation").addHelpText("after", buildHelpText({
31850
+ prCommentsCmd.command("list <id>").description("List comments on a pull request").option("--limit <number>", "Maximum number of comments (default: 25)").option("--all", "List all comments (overrides --limit)").addHelpText("after", buildHelpText({
31534
31851
  examples: [
31535
31852
  "bb pr comments list 42",
31536
31853
  "bb pr comments list 42 --no-truncate",
31854
+ "bb pr comments list 42 --all",
31537
31855
  "bb pr comments list 42 --limit 50 --json"
31538
31856
  ],
31539
31857
  defaults: { limit: "25" }
@@ -31597,10 +31915,11 @@ cli.addCommand(prCmd);
31597
31915
  prCmd.addCommand(prCommentsCmd);
31598
31916
  prCmd.addCommand(prReviewersCmd);
31599
31917
  var snippetCmd = new Command("snippet").description("Manage snippets");
31600
- snippetCmd.command("list").description("List snippets in a workspace").option("--role <role>", "Filter by role (owner, contributor, member)").option("--limit <number>", "Maximum number of snippets to list", "25").addHelpText("after", buildHelpText({
31918
+ snippetCmd.command("list").description("List snippets in a workspace").option("--role <role>", "Filter by role (owner, contributor, member)").option("--limit <number>", "Maximum number of snippets to list", "25").option("--all", "List all snippets (overrides --limit)").addHelpText("after", buildHelpText({
31601
31919
  examples: [
31602
31920
  "bb snippet list",
31603
31921
  "bb snippet list --role owner",
31922
+ "bb snippet list --all",
31604
31923
  "bb snippet list --limit 50 --json"
31605
31924
  ],
31606
31925
  validValues: {
@@ -31664,9 +31983,10 @@ snippetCmd.command("unwatch <id>").description("Stop watching a snippet").addHel
31664
31983
  await runCommand(ServiceTokens.UnwatchSnippetCommand, withGlobalOptions({ id, ...options }, context), cli, context);
31665
31984
  });
31666
31985
  var snippetCommentsCmd = new Command("comments").description("Manage snippet comments");
31667
- snippetCommentsCmd.command("list <id>").description("List comments on a snippet").option("--limit <number>", "Maximum number of comments", "25").addHelpText("after", buildHelpText({
31986
+ snippetCommentsCmd.command("list <id>").description("List comments on a snippet").option("--limit <number>", "Maximum number of comments", "25").option("--all", "List all comments (overrides --limit)").addHelpText("after", buildHelpText({
31668
31987
  examples: [
31669
31988
  "bb snippet comments list kypj",
31989
+ "bb snippet comments list kypj --all",
31670
31990
  "bb snippet comments list kypj --limit 50 --json"
31671
31991
  ],
31672
31992
  defaults: { limit: "25" }
@@ -31798,5 +32118,5 @@ if (typeof Bun === "undefined") {
31798
32118
  }
31799
32119
  cli.parse(process.argv);
31800
32120
 
31801
- //# debugId=A7729A51C3738B3664756E2164756E21
32121
+ //# debugId=B7A5A47929AA84A364756E2164756E21
31802
32122
  //# sourceMappingURL=index.js.map