@pilatos/bitbucket-cli 0.3.1 → 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 +391 -56
  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,15 +34491,20 @@ 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
- return Result.err(new ValidationError("username", "Username is required. Use --username option or set BB_USERNAME environment variable."));
34496
+ const error = Result.err(new ValidationError("username", "Username is required. Use --username option or set BB_USERNAME environment variable."));
34497
+ this.handleResult(error, context);
34498
+ return error;
34460
34499
  }
34461
- if (!appPassword) {
34462
- return 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."));
34502
+ this.handleResult(error, context);
34503
+ return error;
34463
34504
  }
34464
- const setResult = await this.configService.setCredentials({ username, appPassword });
34505
+ const setResult = await this.configService.setCredentials({ username, apiToken });
34465
34506
  if (!setResult.success) {
34507
+ this.handleResult(setResult, context);
34466
34508
  return setResult;
34467
34509
  }
34468
34510
  const userRepository = this.userRepositoryFactory(this.configService);
@@ -34515,7 +34557,7 @@ class StatusCommand extends BaseCommand {
34515
34557
  return configResult;
34516
34558
  }
34517
34559
  const config = configResult.value;
34518
- if (!config.username || !config.appPassword) {
34560
+ if (!config.username || !config.apiToken) {
34519
34561
  const status2 = { authenticated: false };
34520
34562
  this.handleResult(Result.ok(status2), context, () => {
34521
34563
  this.output.info("Not logged in");
@@ -34569,8 +34611,8 @@ class TokenCommand extends BaseCommand {
34569
34611
  }
34570
34612
  return credentialsResult;
34571
34613
  }
34572
- const { username, appPassword } = credentialsResult.value;
34573
- const token = Buffer.from(`${username}:${appPassword}`).toString("base64");
34614
+ const { username, apiToken } = credentialsResult.value;
34615
+ const token = Buffer.from(`${username}:${apiToken}`).toString("base64");
34574
34616
  this.output.text(token);
34575
34617
  return Result.ok(token);
34576
34618
  }
@@ -35188,6 +35230,221 @@ class CheckoutPRCommand extends BaseCommand {
35188
35230
  }
35189
35231
  }
35190
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
+
35191
35448
  // src/types/config.ts
35192
35449
  var SETTABLE_CONFIG_KEYS = ["defaultWorkspace"];
35193
35450
  var READABLE_CONFIG_KEYS = ["username", "defaultWorkspace"];
@@ -35203,7 +35460,7 @@ class GetConfigCommand extends BaseCommand {
35203
35460
  configService;
35204
35461
  name = "get";
35205
35462
  description = "Get a configuration value";
35206
- static HIDDEN_KEYS = ["appPassword"];
35463
+ static HIDDEN_KEYS = ["apiToken"];
35207
35464
  constructor(configService, output) {
35208
35465
  super(output);
35209
35466
  this.configService = configService;
@@ -35248,7 +35505,7 @@ class SetConfigCommand extends BaseCommand {
35248
35505
  configService;
35249
35506
  name = "set";
35250
35507
  description = "Set a configuration value";
35251
- static PROTECTED_KEYS = ["username", "appPassword"];
35508
+ static PROTECTED_KEYS = ["username", "apiToken"];
35252
35509
  constructor(configService, output) {
35253
35510
  super(output);
35254
35511
  this.configService = configService;
@@ -35304,7 +35561,7 @@ class ListConfigCommand extends BaseCommand {
35304
35561
  const displayConfig = {
35305
35562
  username: config.username || "",
35306
35563
  defaultWorkspace: config.defaultWorkspace || "",
35307
- appPassword: config.appPassword ? "********" : ""
35564
+ apiToken: config.apiToken ? "********" : ""
35308
35565
  };
35309
35566
  this.handleResult(Result.ok(displayConfig), context, (data) => {
35310
35567
  this.output.text(source_default.dim(`Config file: ${this.configService.getConfigPath()}`));
@@ -35490,6 +35747,13 @@ function bootstrap() {
35490
35747
  const output = container.resolve(ServiceTokens.OutputService);
35491
35748
  return new CheckoutPRCommand(prRepo, contextService, gitService, output);
35492
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
+ });
35493
35757
  container.register(ServiceTokens.GetConfigCommand, () => {
35494
35758
  const configService = container.resolve(ServiceTokens.ConfigService);
35495
35759
  const output = container.resolve(ServiceTokens.OutputService);
@@ -35540,7 +35804,7 @@ if (process.argv.includes("--get-yargs-completions") || process.env.COMP_LINE) {
35540
35804
  } else if (env2.prev === "repo") {
35541
35805
  completions.push("clone", "create", "list", "view", "delete");
35542
35806
  } else if (env2.prev === "pr") {
35543
- completions.push("create", "list", "view", "merge", "approve", "decline", "checkout");
35807
+ completions.push("create", "list", "view", "merge", "approve", "decline", "checkout", "diff");
35544
35808
  } else if (env2.prev === "config") {
35545
35809
  completions.push("get", "set", "list");
35546
35810
  } else if (env2.prev === "completion") {
@@ -35571,108 +35835,179 @@ function withGlobalOptions(options, context) {
35571
35835
  var cli = new Command;
35572
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");
35573
35837
  var authCmd = new Command("auth").description("Authenticate with Bitbucket");
35574
- 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) => {
35575
35839
  const cmd = container.resolve(ServiceTokens.LoginCommand);
35576
- await cmd.execute(options, createContext(cli));
35840
+ const result = await cmd.execute(options, createContext(cli));
35841
+ if (!result.success) {
35842
+ process.exit(1);
35843
+ }
35577
35844
  });
35578
35845
  authCmd.command("logout").description("Log out of Bitbucket").action(async () => {
35579
35846
  const cmd = container.resolve(ServiceTokens.LogoutCommand);
35580
- await cmd.execute(undefined, createContext(cli));
35847
+ const result = await cmd.execute(undefined, createContext(cli));
35848
+ if (!result.success) {
35849
+ process.exit(1);
35850
+ }
35581
35851
  });
35582
35852
  authCmd.command("status").description("Show authentication status").action(async () => {
35583
35853
  const cmd = container.resolve(ServiceTokens.StatusCommand);
35584
- await cmd.execute(undefined, createContext(cli));
35854
+ const result = await cmd.execute(undefined, createContext(cli));
35855
+ if (!result.success) {
35856
+ process.exit(1);
35857
+ }
35585
35858
  });
35586
35859
  authCmd.command("token").description("Print the current access token").action(async () => {
35587
35860
  const cmd = container.resolve(ServiceTokens.TokenCommand);
35588
- await cmd.execute(undefined, createContext(cli));
35861
+ const result = await cmd.execute(undefined, createContext(cli));
35862
+ if (!result.success) {
35863
+ process.exit(1);
35864
+ }
35589
35865
  });
35590
35866
  cli.addCommand(authCmd);
35591
35867
  var repoCmd = new Command("repo").description("Manage repositories");
35592
35868
  repoCmd.command("clone <repository>").description("Clone a Bitbucket repository").option("-d, --directory <dir>", "Directory to clone into").action(async (repository, options) => {
35593
35869
  const cmd = container.resolve(ServiceTokens.CloneCommand);
35594
- await cmd.execute({ repository, ...options }, createContext(cli));
35870
+ const result = await cmd.execute({ repository, ...options }, createContext(cli));
35871
+ if (!result.success) {
35872
+ process.exit(1);
35873
+ }
35595
35874
  });
35596
35875
  repoCmd.command("create <name>").description("Create a new repository").option("-d, --description <description>", "Repository description").option("--private", "Create a private repository (default)").option("--public", "Create a public repository").option("-p, --project <project>", "Project key").action(async (name, options) => {
35597
35876
  const cmd = container.resolve(ServiceTokens.CreateRepoCommand);
35598
35877
  const context = createContext(cli);
35599
- await cmd.execute(withGlobalOptions({ name, ...options }, context), context);
35878
+ const result = await cmd.execute(withGlobalOptions({ name, ...options }, context), context);
35879
+ if (!result.success) {
35880
+ process.exit(1);
35881
+ }
35600
35882
  });
35601
35883
  repoCmd.command("list").description("List repositories").option("--limit <number>", "Maximum number of repositories to list", "25").action(async (options) => {
35602
35884
  const cmd = container.resolve(ServiceTokens.ListReposCommand);
35603
35885
  const context = createContext(cli);
35604
- await cmd.execute(withGlobalOptions(options, context), context);
35886
+ const result = await cmd.execute(withGlobalOptions(options, context), context);
35887
+ if (!result.success) {
35888
+ process.exit(1);
35889
+ }
35605
35890
  });
35606
35891
  repoCmd.command("view [repository]").description("View repository details").action(async (repository, options) => {
35607
35892
  const cmd = container.resolve(ServiceTokens.ViewRepoCommand);
35608
35893
  const context = createContext(cli);
35609
- await cmd.execute(withGlobalOptions({ repository, ...options }, context), context);
35894
+ const result = await cmd.execute(withGlobalOptions({ repository, ...options }, context), context);
35895
+ if (!result.success) {
35896
+ process.exit(1);
35897
+ }
35610
35898
  });
35611
35899
  repoCmd.command("delete <repository>").description("Delete a repository").option("-y, --yes", "Skip confirmation prompt").action(async (repository, options) => {
35612
35900
  const cmd = container.resolve(ServiceTokens.DeleteRepoCommand);
35613
35901
  const context = createContext(cli);
35614
- await cmd.execute(withGlobalOptions({ repository, ...options }, context), context);
35902
+ const result = await cmd.execute(withGlobalOptions({ repository, ...options }, context), context);
35903
+ if (!result.success) {
35904
+ process.exit(1);
35905
+ }
35615
35906
  });
35616
35907
  cli.addCommand(repoCmd);
35617
35908
  var prCmd = new Command("pr").description("Manage pull requests");
35618
35909
  prCmd.command("create").description("Create a pull request").option("-t, --title <title>", "Pull request title").option("-b, --body <body>", "Pull request description").option("-s, --source <branch>", "Source branch (default: current branch)").option("-d, --destination <branch>", "Destination branch (default: main)").option("--close-source-branch", "Close source branch after merge").action(async (options) => {
35619
35910
  const cmd = container.resolve(ServiceTokens.CreatePRCommand);
35620
35911
  const context = createContext(cli);
35621
- await cmd.execute(withGlobalOptions(options, context), context);
35912
+ const result = await cmd.execute(withGlobalOptions(options, context), context);
35913
+ if (!result.success) {
35914
+ process.exit(1);
35915
+ }
35622
35916
  });
35623
35917
  prCmd.command("list").description("List pull requests").option("-s, --state <state>", "Filter by state (OPEN, MERGED, DECLINED, SUPERSEDED)", "OPEN").option("--limit <number>", "Maximum number of PRs to list", "25").action(async (options) => {
35624
35918
  const cmd = container.resolve(ServiceTokens.ListPRsCommand);
35625
35919
  const context = createContext(cli);
35626
- await cmd.execute(withGlobalOptions(options, context), context);
35920
+ const result = await cmd.execute(withGlobalOptions(options, context), context);
35921
+ if (!result.success) {
35922
+ process.exit(1);
35923
+ }
35627
35924
  });
35628
35925
  prCmd.command("view <id>").description("View pull request details").action(async (id, options) => {
35629
35926
  const cmd = container.resolve(ServiceTokens.ViewPRCommand);
35630
35927
  const context = createContext(cli);
35631
- await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
35928
+ const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
35929
+ if (!result.success) {
35930
+ process.exit(1);
35931
+ }
35632
35932
  });
35633
35933
  prCmd.command("merge <id>").description("Merge a pull request").option("-m, --message <message>", "Merge commit message").option("--close-source-branch", "Delete the source branch after merging").option("--strategy <strategy>", "Merge strategy (merge_commit, squash, fast_forward)").action(async (id, options) => {
35634
35934
  const cmd = container.resolve(ServiceTokens.MergePRCommand);
35635
35935
  const context = createContext(cli);
35636
- await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
35936
+ const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
35937
+ if (!result.success) {
35938
+ process.exit(1);
35939
+ }
35637
35940
  });
35638
35941
  prCmd.command("approve <id>").description("Approve a pull request").action(async (id, options) => {
35639
35942
  const cmd = container.resolve(ServiceTokens.ApprovePRCommand);
35640
35943
  const context = createContext(cli);
35641
- await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
35944
+ const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
35945
+ if (!result.success) {
35946
+ process.exit(1);
35947
+ }
35642
35948
  });
35643
35949
  prCmd.command("decline <id>").description("Decline a pull request").action(async (id, options) => {
35644
35950
  const cmd = container.resolve(ServiceTokens.DeclinePRCommand);
35645
35951
  const context = createContext(cli);
35646
- await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
35952
+ const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
35953
+ if (!result.success) {
35954
+ process.exit(1);
35955
+ }
35647
35956
  });
35648
35957
  prCmd.command("checkout <id>").description("Checkout a pull request locally").action(async (id, options) => {
35649
35958
  const cmd = container.resolve(ServiceTokens.CheckoutPRCommand);
35650
35959
  const context = createContext(cli);
35651
- await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
35960
+ const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
35961
+ if (!result.success) {
35962
+ process.exit(1);
35963
+ }
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
+ }
35652
35972
  });
35653
35973
  cli.addCommand(prCmd);
35654
35974
  var configCmd = new Command("config").description("Manage configuration");
35655
35975
  configCmd.command("get <key>").description("Get a configuration value").action(async (key) => {
35656
35976
  const cmd = container.resolve(ServiceTokens.GetConfigCommand);
35657
- await cmd.execute({ key }, createContext(cli));
35977
+ const result = await cmd.execute({ key }, createContext(cli));
35978
+ if (!result.success) {
35979
+ process.exit(1);
35980
+ }
35658
35981
  });
35659
35982
  configCmd.command("set <key> <value>").description("Set a configuration value").action(async (key, value) => {
35660
35983
  const cmd = container.resolve(ServiceTokens.SetConfigCommand);
35661
- await cmd.execute({ key, value }, createContext(cli));
35984
+ const result = await cmd.execute({ key, value }, createContext(cli));
35985
+ if (!result.success) {
35986
+ process.exit(1);
35987
+ }
35662
35988
  });
35663
35989
  configCmd.command("list").description("List all configuration values").action(async () => {
35664
35990
  const cmd = container.resolve(ServiceTokens.ListConfigCommand);
35665
- await cmd.execute(undefined, createContext(cli));
35991
+ const result = await cmd.execute(undefined, createContext(cli));
35992
+ if (!result.success) {
35993
+ process.exit(1);
35994
+ }
35666
35995
  });
35667
35996
  cli.addCommand(configCmd);
35668
35997
  var completionCmd = new Command("completion").description("Shell completion utilities");
35669
35998
  completionCmd.command("install").description("Install shell completions for bash, zsh, or fish").action(async () => {
35670
35999
  const cmd = container.resolve(ServiceTokens.InstallCompletionCommand);
35671
- await cmd.execute(undefined, createContext(cli));
36000
+ const result = await cmd.execute(undefined, createContext(cli));
36001
+ if (!result.success) {
36002
+ process.exit(1);
36003
+ }
35672
36004
  });
35673
36005
  completionCmd.command("uninstall").description("Uninstall shell completions").action(async () => {
35674
36006
  const cmd = container.resolve(ServiceTokens.UninstallCompletionCommand);
35675
- await cmd.execute(undefined, createContext(cli));
36007
+ const result = await cmd.execute(undefined, createContext(cli));
36008
+ if (!result.success) {
36009
+ process.exit(1);
36010
+ }
35676
36011
  });
35677
36012
  cli.addCommand(completionCmd);
35678
36013
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pilatos/bitbucket-cli",
3
- "version": "0.3.1",
3
+ "version": "1.0.0",
4
4
  "description": "A command-line interface for Bitbucket Cloud",
5
5
  "author": "",
6
6
  "license": "MIT",