@pilatos/bitbucket-cli 0.3.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +10 -8
  2. package/dist/index.js +301 -34
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -123,7 +123,7 @@ Manage your Bitbucket authentication securely.
123
123
 
124
124
  | Command | Description |
125
125
  |---------|-------------|
126
- | `bb auth login` | Authenticate with Bitbucket using an App Password |
126
+ | `bb auth login` | Authenticate with Bitbucket using an API Token |
127
127
  | `bb auth logout` | Log out and remove stored credentials |
128
128
  | `bb auth status` | Check your current authentication status |
129
129
  | `bb auth token` | Print your current access token |
@@ -211,19 +211,21 @@ These options work with any command:
211
211
 
212
212
  ## Authentication Setup
213
213
 
214
- The CLI uses **Bitbucket App Passwords** for secure authentication. Here's how to set it up:
214
+ The CLI uses **Bitbucket API Tokens** for secure authentication. Here's how to set it up:
215
215
 
216
- ### Step 1: Create an App Password
216
+ > **Note**: As of September 9, 2025, Bitbucket app passwords are deprecated and can no longer be created. All existing app passwords will be disabled on June 9, 2026. Use API tokens instead.
217
217
 
218
- 1. Go to [Bitbucket App Passwords](https://bitbucket.org/account/settings/app-passwords/)
219
- 2. Click **"Create app password"**
218
+ ### Step 1: Create an API Token
219
+
220
+ 1. Go to [Bitbucket API Tokens](https://bitbucket.org/account/settings/api-tokens/)
221
+ 2. Click **"Create API token"**
220
222
  3. Enter a descriptive label (e.g., "bb CLI")
221
- 4. Select the required permissions:
223
+ 4. Select the required scopes:
222
224
  - **Account:** Read
223
225
  - **Repositories:** Read, Write, Admin (as needed)
224
226
  - **Pull requests:** Read, Write
225
227
  5. Click **"Create"**
226
- 6. **Copy the generated password** (you won't see it again!)
228
+ 6. **Copy the generated token** (you won't see it again!)
227
229
 
228
230
  ### Step 2: Authenticate
229
231
 
@@ -231,7 +233,7 @@ The CLI uses **Bitbucket App Passwords** for secure authentication. Here's how t
231
233
  bb auth login
232
234
  ```
233
235
 
234
- Enter your Bitbucket username and the App Password when prompted.
236
+ Enter your Bitbucket username and the API Token when prompted.
235
237
 
236
238
  ### Step 3: Verify
237
239
 
package/dist/index.js CHANGED
@@ -32653,14 +32653,14 @@ var require_promisify = __commonJS((exports) => {
32653
32653
  Object.defineProperty(exports, "__esModule", {
32654
32654
  value: true
32655
32655
  });
32656
- exports.promisify = promisify;
32656
+ exports.promisify = promisify2;
32657
32657
  var customArgumentsToken = "__ES6-PROMISIFY--CUSTOM-ARGUMENTS__";
32658
- function promisify(original) {
32658
+ function promisify2(original) {
32659
32659
  if (typeof original !== "function") {
32660
32660
  throw new TypeError("Argument to promisify must be a function");
32661
32661
  }
32662
32662
  var argumentNames = original[customArgumentsToken];
32663
- var ES6Promise = promisify.Promise || Promise;
32663
+ var ES6Promise = promisify2.Promise || Promise;
32664
32664
  if (typeof ES6Promise !== "function") {
32665
32665
  throw new Error("No Promise implementation found; do you need a polyfill?");
32666
32666
  }
@@ -32693,8 +32693,8 @@ var require_promisify = __commonJS((exports) => {
32693
32693
  });
32694
32694
  };
32695
32695
  }
32696
- promisify.argumentNames = customArgumentsToken;
32697
- promisify.Promise = undefined;
32696
+ promisify2.argumentNames = customArgumentsToken;
32697
+ promisify2.Promise = undefined;
32698
32698
  });
32699
32699
 
32700
32700
  // node_modules/mkdirp/index.js
@@ -32793,8 +32793,8 @@ var require_systemShell = __commonJS((exports, module) => {
32793
32793
  var require_exists = __commonJS((exports, module) => {
32794
32794
  var fs = __require("fs");
32795
32795
  var untildify = require_untildify();
32796
- var { promisify } = require_promisify();
32797
- var readFile = promisify(fs.readFile);
32796
+ var { promisify: promisify2 } = require_promisify();
32797
+ var readFile = promisify2(fs.readFile);
32798
32798
  module.exports = async (file) => {
32799
32799
  let fileExists;
32800
32800
  try {
@@ -32841,13 +32841,13 @@ var require_installer = __commonJS((exports, module) => {
32841
32841
  var fs = __require("fs");
32842
32842
  var path = __require("path");
32843
32843
  var untildify = require_untildify();
32844
- var { promisify } = require_promisify();
32845
- var mkdirp = promisify(require_mkdirp());
32844
+ var { promisify: promisify2 } = require_promisify();
32845
+ var mkdirp = promisify2(require_mkdirp());
32846
32846
  var { tabtabDebug, systemShell, exists } = require_utils2();
32847
32847
  var debug = tabtabDebug("tabtab:installer");
32848
- var readFile = promisify(fs.readFile);
32849
- var writeFile = promisify(fs.writeFile);
32850
- var unlink = promisify(fs.unlink);
32848
+ var readFile = promisify2(fs.readFile);
32849
+ var writeFile = promisify2(fs.writeFile);
32850
+ var unlink = promisify2(fs.unlink);
32851
32851
  var {
32852
32852
  BASH_LOCATION,
32853
32853
  FISH_LOCATION,
@@ -33262,6 +33262,7 @@ var ServiceTokens = {
33262
33262
  ApprovePRCommand: "ApprovePRCommand",
33263
33263
  DeclinePRCommand: "DeclinePRCommand",
33264
33264
  CheckoutPRCommand: "CheckoutPRCommand",
33265
+ DiffPRCommand: "DiffPRCommand",
33265
33266
  GetConfigCommand: "GetConfigCommand",
33266
33267
  SetConfigCommand: "SetConfigCommand",
33267
33268
  ListConfigCommand: "ListConfigCommand",
@@ -33465,14 +33466,14 @@ class ConfigService {
33465
33466
  if (!configResult.success) {
33466
33467
  return configResult;
33467
33468
  }
33468
- const { username, appPassword } = configResult.value;
33469
- if (!username || !appPassword) {
33469
+ const { username, apiToken } = configResult.value;
33470
+ if (!username || !apiToken) {
33470
33471
  return Result.err(new BBError({
33471
33472
  code: 1001 /* AUTH_REQUIRED */,
33472
33473
  message: "Authentication required. Run 'bb auth login' to authenticate."
33473
33474
  }));
33474
33475
  }
33475
- return Result.ok({ username, appPassword });
33476
+ return Result.ok({ username, apiToken });
33476
33477
  }
33477
33478
  async setCredentials(credentials) {
33478
33479
  const configResult = await this.getConfig();
@@ -33482,7 +33483,7 @@ class ConfigService {
33482
33483
  return this.setConfig({
33483
33484
  ...configResult.value,
33484
33485
  username: credentials.username,
33485
- appPassword: credentials.appPassword
33486
+ apiToken: credentials.apiToken
33486
33487
  });
33487
33488
  }
33488
33489
  async clearConfig() {
@@ -34253,11 +34254,11 @@ class HttpClient {
34253
34254
  if (!credentialsResult.success) {
34254
34255
  return credentialsResult;
34255
34256
  }
34256
- const { username, appPassword } = credentialsResult.value;
34257
- const encoded = Buffer.from(`${username}:${appPassword}`).toString("base64");
34257
+ const { username, apiToken } = credentialsResult.value;
34258
+ const encoded = Buffer.from(`${username}:${apiToken}`).toString("base64");
34258
34259
  return Result.ok(`Basic ${encoded}`);
34259
34260
  }
34260
- async request(method, path, body) {
34261
+ async request(method, path, body, acceptText = false) {
34261
34262
  const authResult = await this.getAuthHeader();
34262
34263
  if (!authResult.success) {
34263
34264
  return authResult;
@@ -34265,7 +34266,7 @@ class HttpClient {
34265
34266
  const headers = {
34266
34267
  Authorization: authResult.value,
34267
34268
  "Content-Type": "application/json",
34268
- Accept: "application/json"
34269
+ Accept: acceptText ? "text/plain" : "application/json"
34269
34270
  };
34270
34271
  const url = `${this.baseUrl}${path}`;
34271
34272
  try {
@@ -34278,7 +34279,7 @@ class HttpClient {
34278
34279
  signal: controller.signal
34279
34280
  });
34280
34281
  clearTimeout(timeoutId);
34281
- return this.handleResponse(response);
34282
+ return acceptText ? this.handleTextResponse(response) : this.handleResponse(response);
34282
34283
  } catch (error) {
34283
34284
  if (error instanceof Error && error.name === "AbortError") {
34284
34285
  return Result.err(new BBError({
@@ -34322,6 +34323,33 @@ class HttpClient {
34322
34323
  }));
34323
34324
  }
34324
34325
  }
34326
+ async handleTextResponse(response) {
34327
+ if (!response.ok) {
34328
+ let errorBody;
34329
+ try {
34330
+ errorBody = await response.json();
34331
+ } catch {
34332
+ errorBody = await response.text();
34333
+ }
34334
+ const message = this.extractErrorMessage(errorBody, response.statusText);
34335
+ return Result.err(new APIError(message, response.status, errorBody, {
34336
+ url: response.url
34337
+ }));
34338
+ }
34339
+ if (response.status === 204) {
34340
+ return Result.ok(undefined);
34341
+ }
34342
+ try {
34343
+ const data = await response.text();
34344
+ return Result.ok(data);
34345
+ } catch (error) {
34346
+ return Result.err(new BBError({
34347
+ code: 2001 /* API_REQUEST_FAILED */,
34348
+ message: "Failed to read response text",
34349
+ cause: error instanceof Error ? error : undefined
34350
+ }));
34351
+ }
34352
+ }
34325
34353
  extractErrorMessage(body, fallback) {
34326
34354
  if (typeof body === "object" && body !== null) {
34327
34355
  const obj = body;
@@ -34340,6 +34368,9 @@ class HttpClient {
34340
34368
  async get(path) {
34341
34369
  return this.request("GET", path);
34342
34370
  }
34371
+ async getText(path) {
34372
+ return this.request("GET", path, undefined, true);
34373
+ }
34343
34374
  async post(path, body) {
34344
34375
  return this.request("POST", path, body);
34345
34376
  }
@@ -34408,6 +34439,12 @@ class PullRequestRepository {
34408
34439
  async decline(workspace, repoSlug, id) {
34409
34440
  return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/decline`));
34410
34441
  }
34442
+ async getDiff(workspace, repoSlug, id) {
34443
+ return this.httpClient.getText(this.buildPath(workspace, repoSlug, `/${id}/diff`));
34444
+ }
34445
+ async getDiffstat(workspace, repoSlug, id) {
34446
+ return this.httpClient.get(this.buildPath(workspace, repoSlug, `/${id}/diffstat`));
34447
+ }
34411
34448
  }
34412
34449
  // src/core/base-command.ts
34413
34450
  class BaseCommand {
@@ -34446,7 +34483,7 @@ class LoginCommand extends BaseCommand {
34446
34483
  configService;
34447
34484
  userRepositoryFactory;
34448
34485
  name = "login";
34449
- description = "Authenticate with Bitbucket using an app password";
34486
+ description = "Authenticate with Bitbucket using an API token";
34450
34487
  constructor(configService, userRepositoryFactory, output) {
34451
34488
  super(output);
34452
34489
  this.configService = configService;
@@ -34454,18 +34491,18 @@ class LoginCommand extends BaseCommand {
34454
34491
  }
34455
34492
  async execute(options, context) {
34456
34493
  const username = options.username || process.env.BB_USERNAME;
34457
- const appPassword = options.password || process.env.BB_APP_PASSWORD;
34494
+ const apiToken = options.password || process.env.BB_API_TOKEN;
34458
34495
  if (!username) {
34459
34496
  const error = Result.err(new ValidationError("username", "Username is required. Use --username option or set BB_USERNAME environment variable."));
34460
34497
  this.handleResult(error, context);
34461
34498
  return error;
34462
34499
  }
34463
- if (!appPassword) {
34464
- const error = Result.err(new ValidationError("password", "App password is required. Use --password option or set BB_APP_PASSWORD environment variable."));
34500
+ if (!apiToken) {
34501
+ const error = Result.err(new ValidationError("password", "API token is required. Use --password option or set BB_API_TOKEN environment variable."));
34465
34502
  this.handleResult(error, context);
34466
34503
  return error;
34467
34504
  }
34468
- const setResult = await this.configService.setCredentials({ username, appPassword });
34505
+ const setResult = await this.configService.setCredentials({ username, apiToken });
34469
34506
  if (!setResult.success) {
34470
34507
  this.handleResult(setResult, context);
34471
34508
  return setResult;
@@ -34520,7 +34557,7 @@ class StatusCommand extends BaseCommand {
34520
34557
  return configResult;
34521
34558
  }
34522
34559
  const config = configResult.value;
34523
- if (!config.username || !config.appPassword) {
34560
+ if (!config.username || !config.apiToken) {
34524
34561
  const status2 = { authenticated: false };
34525
34562
  this.handleResult(Result.ok(status2), context, () => {
34526
34563
  this.output.info("Not logged in");
@@ -34574,8 +34611,8 @@ class TokenCommand extends BaseCommand {
34574
34611
  }
34575
34612
  return credentialsResult;
34576
34613
  }
34577
- const { username, appPassword } = credentialsResult.value;
34578
- const token = Buffer.from(`${username}:${appPassword}`).toString("base64");
34614
+ const { username, apiToken } = credentialsResult.value;
34615
+ const token = Buffer.from(`${username}:${apiToken}`).toString("base64");
34579
34616
  this.output.text(token);
34580
34617
  return Result.ok(token);
34581
34618
  }
@@ -35193,6 +35230,221 @@ class CheckoutPRCommand extends BaseCommand {
35193
35230
  }
35194
35231
  }
35195
35232
 
35233
+ // src/commands/pr/diff.command.ts
35234
+ import { exec } from "child_process";
35235
+ import { promisify } from "util";
35236
+ var execAsync = promisify(exec);
35237
+
35238
+ class DiffPRCommand extends BaseCommand {
35239
+ prRepository;
35240
+ contextService;
35241
+ gitService;
35242
+ name = "diff";
35243
+ description = "View pull request diff";
35244
+ constructor(prRepository, contextService, gitService, output) {
35245
+ super(output);
35246
+ this.prRepository = prRepository;
35247
+ this.contextService = contextService;
35248
+ this.gitService = gitService;
35249
+ }
35250
+ async execute(options, context) {
35251
+ const repoContextResult = await this.contextService.requireRepoContext({
35252
+ ...context.globalOptions,
35253
+ ...options
35254
+ });
35255
+ if (!repoContextResult.success) {
35256
+ this.handleResult(repoContextResult, context);
35257
+ return repoContextResult;
35258
+ }
35259
+ const { workspace, repoSlug } = repoContextResult.value;
35260
+ let prId;
35261
+ if (options.id) {
35262
+ prId = parseInt(options.id, 10);
35263
+ if (isNaN(prId)) {
35264
+ const error = new BBError({
35265
+ code: 5002 /* VALIDATION_INVALID */,
35266
+ message: "Invalid PR ID"
35267
+ });
35268
+ this.handleResult(Result.err(error), context);
35269
+ return Result.err(error);
35270
+ }
35271
+ } else {
35272
+ const currentBranchResult = await this.gitService.getCurrentBranch();
35273
+ if (!currentBranchResult.success) {
35274
+ const error = new BBError({
35275
+ code: 5002 /* VALIDATION_INVALID */,
35276
+ message: "No PR ID provided and could not determine current branch"
35277
+ });
35278
+ this.handleResult(Result.err(error), context);
35279
+ return Result.err(error);
35280
+ }
35281
+ const currentBranch = currentBranchResult.value;
35282
+ const prsResult = await this.prRepository.list(workspace, repoSlug, "OPEN", 100);
35283
+ if (!prsResult.success) {
35284
+ this.handleResult(prsResult, context);
35285
+ return prsResult;
35286
+ }
35287
+ const pr = prsResult.value.values.find((p) => p.source.branch.name === currentBranch);
35288
+ if (!pr) {
35289
+ const error = new BBError({
35290
+ code: 5002 /* VALIDATION_INVALID */,
35291
+ message: `No open pull request found for branch "${currentBranch}"`
35292
+ });
35293
+ this.handleResult(Result.err(error), context);
35294
+ return Result.err(error);
35295
+ }
35296
+ prId = pr.id;
35297
+ }
35298
+ if (options.web) {
35299
+ const prResult = await this.prRepository.get(workspace, repoSlug, prId);
35300
+ if (!prResult.success) {
35301
+ this.handleResult(prResult, context);
35302
+ return prResult;
35303
+ }
35304
+ const diffUrl = prResult.value.links.diff.href;
35305
+ const webUrl = diffUrl.replace(/api\.bitbucket\.org\/2\.0\/repositories\/(.*?)\/pullrequests\/(\d+)\/diff/, "bitbucket.org/$1/pull-requests/$2/diff");
35306
+ return this.openInBrowser(webUrl, context);
35307
+ }
35308
+ if (options.stat) {
35309
+ return this.showStat(workspace, repoSlug, prId, context);
35310
+ }
35311
+ if (options.nameOnly) {
35312
+ return this.showNameOnly(workspace, repoSlug, prId, context);
35313
+ }
35314
+ return this.showDiff(workspace, repoSlug, prId, options, context);
35315
+ }
35316
+ async openInBrowser(url, context) {
35317
+ if (context.globalOptions.json) {
35318
+ this.output.json({ url });
35319
+ return Result.ok({ diff: url });
35320
+ }
35321
+ this.output.info(`Opening ${url} in your browser...`);
35322
+ try {
35323
+ const platform = process.platform;
35324
+ let command;
35325
+ if (platform === "darwin") {
35326
+ command = `open "${url}"`;
35327
+ } else if (platform === "win32") {
35328
+ command = `start "" "${url}"`;
35329
+ } else {
35330
+ command = `xdg-open "${url}"`;
35331
+ }
35332
+ await execAsync(command);
35333
+ return Result.ok({ diff: url });
35334
+ } catch (error) {
35335
+ const bbError = new BBError({
35336
+ code: 3002 /* GIT_COMMAND_FAILED */,
35337
+ message: "Failed to open browser",
35338
+ cause: error instanceof Error ? error : undefined
35339
+ });
35340
+ this.handleResult(Result.err(bbError), context);
35341
+ return Result.err(bbError);
35342
+ }
35343
+ }
35344
+ async showStat(workspace, repoSlug, prId, context) {
35345
+ const diffstatResult = await this.prRepository.getDiffstat(workspace, repoSlug, prId);
35346
+ if (!diffstatResult.success) {
35347
+ this.handleResult(diffstatResult, context);
35348
+ return diffstatResult;
35349
+ }
35350
+ const diffstat = diffstatResult.value;
35351
+ const files = diffstat.values.map((file) => {
35352
+ const path = file.new?.path || file.old?.path || "unknown";
35353
+ return {
35354
+ path,
35355
+ additions: file.lines_added,
35356
+ deletions: file.lines_removed
35357
+ };
35358
+ });
35359
+ const totalAdditions = files.reduce((sum, f) => sum + f.additions, 0);
35360
+ const totalDeletions = files.reduce((sum, f) => sum + f.deletions, 0);
35361
+ const filesChanged = files.length;
35362
+ const result = {
35363
+ stat: {
35364
+ filesChanged,
35365
+ insertions: totalAdditions,
35366
+ deletions: totalDeletions,
35367
+ files
35368
+ }
35369
+ };
35370
+ this.handleResult(Result.ok(result), context, () => {
35371
+ for (const file of files) {
35372
+ const additions = file.additions > 0 ? source_default.green(`+${file.additions}`) : "";
35373
+ const deletions = file.deletions > 0 ? source_default.red(`-${file.deletions}`) : "";
35374
+ const stats = [additions, deletions].filter(Boolean).join(" ");
35375
+ this.output.text(`${file.path} ${stats ? `| ${stats}` : ""}`);
35376
+ }
35377
+ this.output.text("");
35378
+ const summary = [
35379
+ `${filesChanged} file${filesChanged !== 1 ? "s" : ""} changed`,
35380
+ totalAdditions > 0 ? source_default.green(`${totalAdditions} insertion${totalAdditions !== 1 ? "s" : ""}(+)`) : null,
35381
+ totalDeletions > 0 ? source_default.red(`${totalDeletions} deletion${totalDeletions !== 1 ? "s" : ""}(-)`) : null
35382
+ ].filter(Boolean).join(", ");
35383
+ this.output.text(summary);
35384
+ });
35385
+ return Result.ok(result);
35386
+ }
35387
+ async showNameOnly(workspace, repoSlug, prId, context) {
35388
+ const diffstatResult = await this.prRepository.getDiffstat(workspace, repoSlug, prId);
35389
+ if (!diffstatResult.success) {
35390
+ this.handleResult(diffstatResult, context);
35391
+ return diffstatResult;
35392
+ }
35393
+ const diffstat = diffstatResult.value;
35394
+ const fileNames = diffstat.values.map((file) => file.new?.path || file.old?.path || "unknown");
35395
+ const result = {
35396
+ diff: fileNames.join(`
35397
+ `)
35398
+ };
35399
+ this.handleResult(Result.ok(result), context, () => {
35400
+ for (const fileName of fileNames) {
35401
+ this.output.text(fileName);
35402
+ }
35403
+ });
35404
+ return Result.ok(result);
35405
+ }
35406
+ async showDiff(workspace, repoSlug, prId, options, context) {
35407
+ const diffResult = await this.prRepository.getDiff(workspace, repoSlug, prId);
35408
+ if (!diffResult.success) {
35409
+ this.handleResult(diffResult, context);
35410
+ return diffResult;
35411
+ }
35412
+ const diff = diffResult.value;
35413
+ const result = { diff };
35414
+ this.handleResult(Result.ok(result), context, () => {
35415
+ const shouldColorize = this.shouldColorize(options.color);
35416
+ const colorizedDiff = shouldColorize ? this.colorizeDiff(diff) : diff;
35417
+ this.output.text(colorizedDiff);
35418
+ });
35419
+ return Result.ok(result);
35420
+ }
35421
+ shouldColorize(colorOption) {
35422
+ if (!colorOption || colorOption === "auto") {
35423
+ return process.stdout.isTTY ?? false;
35424
+ }
35425
+ return colorOption === "always";
35426
+ }
35427
+ colorizeDiff(diff) {
35428
+ const lines = diff.split(`
35429
+ `);
35430
+ return lines.map((line) => {
35431
+ if (line.startsWith("+") && !line.startsWith("+++")) {
35432
+ return source_default.green(line);
35433
+ } else if (line.startsWith("-") && !line.startsWith("---")) {
35434
+ return source_default.red(line);
35435
+ } else if (line.startsWith("@@")) {
35436
+ return source_default.cyan(line);
35437
+ } else if (line.startsWith("diff --git")) {
35438
+ return source_default.bold(line);
35439
+ } else if (line.startsWith("index ") || line.startsWith("---") || line.startsWith("+++")) {
35440
+ return source_default.dim(line);
35441
+ }
35442
+ return line;
35443
+ }).join(`
35444
+ `);
35445
+ }
35446
+ }
35447
+
35196
35448
  // src/types/config.ts
35197
35449
  var SETTABLE_CONFIG_KEYS = ["defaultWorkspace"];
35198
35450
  var READABLE_CONFIG_KEYS = ["username", "defaultWorkspace"];
@@ -35208,7 +35460,7 @@ class GetConfigCommand extends BaseCommand {
35208
35460
  configService;
35209
35461
  name = "get";
35210
35462
  description = "Get a configuration value";
35211
- static HIDDEN_KEYS = ["appPassword"];
35463
+ static HIDDEN_KEYS = ["apiToken"];
35212
35464
  constructor(configService, output) {
35213
35465
  super(output);
35214
35466
  this.configService = configService;
@@ -35253,7 +35505,7 @@ class SetConfigCommand extends BaseCommand {
35253
35505
  configService;
35254
35506
  name = "set";
35255
35507
  description = "Set a configuration value";
35256
- static PROTECTED_KEYS = ["username", "appPassword"];
35508
+ static PROTECTED_KEYS = ["username", "apiToken"];
35257
35509
  constructor(configService, output) {
35258
35510
  super(output);
35259
35511
  this.configService = configService;
@@ -35309,7 +35561,7 @@ class ListConfigCommand extends BaseCommand {
35309
35561
  const displayConfig = {
35310
35562
  username: config.username || "",
35311
35563
  defaultWorkspace: config.defaultWorkspace || "",
35312
- appPassword: config.appPassword ? "********" : ""
35564
+ apiToken: config.apiToken ? "********" : ""
35313
35565
  };
35314
35566
  this.handleResult(Result.ok(displayConfig), context, (data) => {
35315
35567
  this.output.text(source_default.dim(`Config file: ${this.configService.getConfigPath()}`));
@@ -35495,6 +35747,13 @@ function bootstrap() {
35495
35747
  const output = container.resolve(ServiceTokens.OutputService);
35496
35748
  return new CheckoutPRCommand(prRepo, contextService, gitService, output);
35497
35749
  });
35750
+ container.register(ServiceTokens.DiffPRCommand, () => {
35751
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
35752
+ const contextService = container.resolve(ServiceTokens.ContextService);
35753
+ const gitService = container.resolve(ServiceTokens.GitService);
35754
+ const output = container.resolve(ServiceTokens.OutputService);
35755
+ return new DiffPRCommand(prRepo, contextService, gitService, output);
35756
+ });
35498
35757
  container.register(ServiceTokens.GetConfigCommand, () => {
35499
35758
  const configService = container.resolve(ServiceTokens.ConfigService);
35500
35759
  const output = container.resolve(ServiceTokens.OutputService);
@@ -35545,7 +35804,7 @@ if (process.argv.includes("--get-yargs-completions") || process.env.COMP_LINE) {
35545
35804
  } else if (env2.prev === "repo") {
35546
35805
  completions.push("clone", "create", "list", "view", "delete");
35547
35806
  } else if (env2.prev === "pr") {
35548
- completions.push("create", "list", "view", "merge", "approve", "decline", "checkout");
35807
+ completions.push("create", "list", "view", "merge", "approve", "decline", "checkout", "diff");
35549
35808
  } else if (env2.prev === "config") {
35550
35809
  completions.push("get", "set", "list");
35551
35810
  } else if (env2.prev === "completion") {
@@ -35576,7 +35835,7 @@ function withGlobalOptions(options, context) {
35576
35835
  var cli = new Command;
35577
35836
  cli.name("bb").description("A command-line interface for Bitbucket Cloud").version(pkg.version).option("--json", "Output as JSON").option("-w, --workspace <workspace>", "Specify workspace").option("-r, --repo <repo>", "Specify repository");
35578
35837
  var authCmd = new Command("auth").description("Authenticate with Bitbucket");
35579
- authCmd.command("login").description("Authenticate with Bitbucket using an app password").option("-u, --username <username>", "Bitbucket username").option("-p, --password <password>", "Bitbucket app password").action(async (options) => {
35838
+ authCmd.command("login").description("Authenticate with Bitbucket using an API token").option("-u, --username <username>", "Bitbucket username").option("-p, --password <password>", "Bitbucket API token").action(async (options) => {
35580
35839
  const cmd = container.resolve(ServiceTokens.LoginCommand);
35581
35840
  const result = await cmd.execute(options, createContext(cli));
35582
35841
  if (!result.success) {
@@ -35703,6 +35962,14 @@ prCmd.command("checkout <id>").description("Checkout a pull request locally").ac
35703
35962
  process.exit(1);
35704
35963
  }
35705
35964
  });
35965
+ prCmd.command("diff [id]").description("View pull request diff").option("--color <when>", "Colorize output (auto, always, never)", "auto").option("--name-only", "Show only names of changed files").option("--stat", "Show diffstat").option("-w, --web", "Open diff in web browser").action(async (id, options) => {
35966
+ const cmd = container.resolve(ServiceTokens.DiffPRCommand);
35967
+ const context = createContext(cli);
35968
+ const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
35969
+ if (!result.success) {
35970
+ process.exit(1);
35971
+ }
35972
+ });
35706
35973
  cli.addCommand(prCmd);
35707
35974
  var configCmd = new Command("config").description("Manage configuration");
35708
35975
  configCmd.command("get <key>").description("Get a configuration value").action(async (key) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pilatos/bitbucket-cli",
3
- "version": "0.3.2",
3
+ "version": "1.0.0",
4
4
  "description": "A command-line interface for Bitbucket Cloud",
5
5
  "author": "",
6
6
  "license": "MIT",