@pilatos/bitbucket-cli 1.0.0 → 1.2.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 +50 -230
  2. package/dist/index.js +389 -48
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -32,56 +32,20 @@
32
32
 
33
33
  If you've used GitHub's `gh` CLI and loved it, you've probably wished for something similar for Bitbucket. **Now you have it.**
34
34
 
35
- `bb` brings the power of command-line workflows to Bitbucket Cloud, letting you:
36
-
37
35
  - **Stay in your terminal** — No more context-switching to the browser
38
- - **Automate your workflow** — Script common operations with ease
36
+ - **Automate your workflow** — Script common operations with JSON output
39
37
  - **Work faster** — Clone repos, create PRs, and manage code reviews in seconds
40
- - **Maintain consistency** — Use familiar patterns if you work across GitHub and Bitbucket
41
-
42
- ---
43
-
44
- ## Table of Contents
45
-
46
- - [Installation](#installation)
47
- - [Quick Start](#quick-start)
48
- - [Commands](#commands)
49
- - [Authentication](#authentication)
50
- - [Repositories](#repositories)
51
- - [Pull Requests](#pull-requests)
52
- - [Configuration](#configuration)
53
- - [Shell Completion](#shell-completion)
54
- - [Global Options](#global-options)
55
- - [Authentication Setup](#authentication-setup)
56
- - [Configuration](#configuration-1)
57
- - [Examples](#examples)
58
- - [Development](#development)
59
- - [Contributing](#contributing)
60
- - [License](#license)
38
+ - **Smart context detection** — Automatically detects workspace/repo from git directory
61
39
 
62
40
  ---
63
41
 
64
42
  ## Installation
65
43
 
66
- ### Using npm (Recommended)
67
-
68
44
  ```bash
69
45
  npm install -g @pilatos/bitbucket-cli
70
46
  ```
71
47
 
72
- ### Using Bun
73
-
74
- ```bash
75
- bun install -g @pilatos/bitbucket-cli
76
- ```
77
-
78
- ### Using Yarn
79
-
80
- ```bash
81
- yarn global add @pilatos/bitbucket-cli
82
- ```
83
-
84
- ### Verify Installation
48
+ Verify installation:
85
49
 
86
50
  ```bash
87
51
  bb --version
@@ -93,21 +57,19 @@ bb --version
93
57
 
94
58
  ## Quick Start
95
59
 
96
- Get up and running in under a minute:
97
-
98
60
  ```bash
99
61
  # 1. Authenticate with Bitbucket
100
62
  bb auth login
101
63
 
102
64
  # 2. Clone a repository
103
65
  bb repo clone myworkspace/myrepo
66
+ cd myrepo
104
67
 
105
68
  # 3. Create a feature branch and make changes
106
- cd myrepo
107
69
  git checkout -b feature/awesome-feature
108
70
 
109
71
  # 4. Create a pull request
110
- bb pr create --title "Add awesome feature" --source feature/awesome-feature --destination main
72
+ bb pr create --title "Add awesome feature"
111
73
 
112
74
  # 5. List open pull requests
113
75
  bb pr list
@@ -115,156 +77,55 @@ bb pr list
115
77
 
116
78
  ---
117
79
 
118
- ## Commands
119
-
120
- ### Authentication
121
-
122
- Manage your Bitbucket authentication securely.
123
-
124
- | Command | Description |
125
- |---------|-------------|
126
- | `bb auth login` | Authenticate with Bitbucket using an API Token |
127
- | `bb auth logout` | Log out and remove stored credentials |
128
- | `bb auth status` | Check your current authentication status |
129
- | `bb auth token` | Print your current access token |
130
-
131
- ### Repositories
132
-
133
- Clone, create, and manage your Bitbucket repositories.
134
-
135
- | Command | Description |
136
- |---------|-------------|
137
- | `bb repo clone <repo>` | Clone a repository to your local machine |
138
- | `bb repo create <name>` | Create a new repository |
139
- | `bb repo list` | List repositories in a workspace |
140
- | `bb repo view [repo]` | View repository details and metadata |
141
- | `bb repo delete <repo>` | Delete a repository (use with caution!) |
142
-
143
- ### Pull Requests
144
-
145
- Full pull request workflow management from your terminal.
146
-
147
- | Command | Description |
148
- |---------|-------------|
149
- | `bb pr create` | Create a new pull request |
150
- | `bb pr list` | List pull requests with filtering options |
151
- | `bb pr view <id>` | View pull request details, diff, and comments |
152
- | `bb pr merge <id>` | Merge a pull request |
153
- | `bb pr approve <id>` | Approve a pull request |
154
- | `bb pr decline <id>` | Decline a pull request |
155
- | `bb pr checkout <id>` | Checkout a pull request branch locally |
156
-
157
- ### Configuration
158
-
159
- Customize your CLI experience.
160
-
161
- | Command | Description |
162
- |---------|-------------|
163
- | `bb config get <key>` | Get a configuration value |
164
- | `bb config set <key> <value>` | Set a configuration value |
165
- | `bb config list` | List all configuration values |
166
-
167
- ### Shell Completion
168
-
169
- Enable intelligent tab completion for faster command entry.
170
-
171
- | Command | Description |
172
- |---------|-------------|
173
- | `bb completion install` | Install shell completions (auto-detects shell) |
174
- | `bb completion uninstall` | Remove shell completions |
175
-
176
- **Setup:**
177
-
178
- ```bash
179
- # Install completions
180
- bb completion install
181
-
182
- # Restart your shell or source your profile
183
- source ~/.bashrc # Bash
184
- source ~/.zshrc # Zsh
185
- source ~/.config/fish/config.fish # Fish
186
- ```
80
+ ## Features
187
81
 
188
- **Usage:**
82
+ | Category | Commands |
83
+ |----------|----------|
84
+ | **Authentication** | `login`, `logout`, `status`, `token` |
85
+ | **Repositories** | `clone`, `create`, `list`, `view`, `delete` |
86
+ | **Pull Requests** | `create`, `list`, `view`, `edit`, `merge`, `approve`, `decline`, `checkout`, `diff`, `comment`, `comments` |
87
+ | **Configuration** | `get`, `set`, `list` |
88
+ | **Shell Completion** | `install`, `uninstall` |
189
89
 
190
- ```bash
191
- bb re<Tab> # bb repo
192
- bb repo cl<Tab> # bb repo clone
193
- bb pr l<Tab> # bb pr list
194
- ```
90
+ **Global Options:**
91
+ - `--json` Output results as JSON for scripting
92
+ - `-w, --workspace` Specify workspace
93
+ - `-r, --repo` Specify repository
195
94
 
196
95
  ---
197
96
 
198
- ## Global Options
97
+ ## Documentation
199
98
 
200
- These options work with any command:
99
+ Full documentation is available at **[bitbucket-cli.paulvanderlei.com](https://bitbucket-cli.paulvanderlei.com)**
201
100
 
202
- | Option | Description |
203
- |--------|-------------|
204
- | `--json` | Output results as JSON for scripting |
205
- | `-w, --workspace <workspace>` | Specify the workspace (overrides default) |
206
- | `-r, --repo <repo>` | Specify the repository (overrides default) |
207
- | `-h, --help` | Show help information |
208
- | `-v, --version` | Show version number |
101
+ - [Quick Start Guide](https://bitbucket-cli.paulvanderlei.com/getting-started/quickstart/)
102
+ - [Command Reference](https://bitbucket-cli.paulvanderlei.com/commands/auth/)
103
+ - [Scripting & Automation](https://bitbucket-cli.paulvanderlei.com/guides/scripting/)
104
+ - [CI/CD Integration](https://bitbucket-cli.paulvanderlei.com/guides/cicd/)
105
+ - [Troubleshooting](https://bitbucket-cli.paulvanderlei.com/help/troubleshooting/)
106
+ - [FAQ](https://bitbucket-cli.paulvanderlei.com/help/faq/)
209
107
 
210
108
  ---
211
109
 
212
- ## Authentication Setup
110
+ ## Authentication
213
111
 
214
- The CLI uses **Bitbucket API Tokens** for secure authentication. Here's how to set it up:
112
+ The CLI uses **Bitbucket API Tokens** for secure authentication.
215
113
 
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.
114
+ > **Note**: As of September 9, 2025, Bitbucket app passwords are deprecated. Use API tokens instead.
217
115
 
218
- ### Step 1: Create an API Token
116
+ ### Create an API Token
219
117
 
220
118
  1. Go to [Bitbucket API Tokens](https://bitbucket.org/account/settings/api-tokens/)
221
119
  2. Click **"Create API token"**
222
- 3. Enter a descriptive label (e.g., "bb CLI")
223
- 4. Select the required scopes:
224
- - **Account:** Read
225
- - **Repositories:** Read, Write, Admin (as needed)
226
- - **Pull requests:** Read, Write
227
- 5. Click **"Create"**
228
- 6. **Copy the generated token** (you won't see it again!)
120
+ 3. Select required scopes (Account, Repositories, Pull requests)
121
+ 4. Copy the token
229
122
 
230
- ### Step 2: Authenticate
123
+ ### Authenticate
231
124
 
232
125
  ```bash
233
126
  bb auth login
234
127
  ```
235
128
 
236
- Enter your Bitbucket username and the API Token when prompted.
237
-
238
- ### Step 3: Verify
239
-
240
- ```bash
241
- bb auth status
242
- ```
243
-
244
- ---
245
-
246
- ## Configuration
247
-
248
- Configuration files are stored in platform-specific locations:
249
-
250
- | Platform | Location |
251
- |----------|----------|
252
- | **macOS / Linux** | `~/.config/bb/config.json` |
253
- | **Windows** | `%APPDATA%\bb\config.json` |
254
-
255
- ### Available Settings
256
-
257
- ```bash
258
- # Set your default workspace
259
- bb config set workspace myworkspace
260
-
261
- # Set your default repository
262
- bb config set repo myrepo
263
-
264
- # View all settings
265
- bb config list
266
- ```
267
-
268
129
  ---
269
130
 
270
131
  ## Examples
@@ -272,53 +133,38 @@ bb config list
272
133
  ### Daily Workflow
273
134
 
274
135
  ```bash
275
- # Start your day: check open PRs assigned to you
276
- bb pr list --state OPEN
136
+ # Check open PRs
137
+ bb pr list
277
138
 
278
- # Review a specific PR
139
+ # Review and merge
279
140
  bb pr view 42
280
-
281
- # Approve and merge
282
141
  bb pr approve 42
283
142
  bb pr merge 42
284
143
  ```
285
144
 
286
- ### Creating a Pull Request
145
+ ### Scripting with JSON
287
146
 
288
147
  ```bash
289
- # From your feature branch
290
- bb pr create \
291
- --title "feat: Add user notifications" \
292
- --description "Implements real-time notifications using WebSockets" \
293
- --source feature/notifications \
294
- --destination main
295
- ```
148
+ # Get all open PR titles
149
+ bb pr list --json | jq '.[].title'
296
150
 
297
- ### Scripting with JSON Output
298
-
299
- ```bash
300
- # Get all open PRs as JSON for processing
301
- bb pr list --state OPEN --json | jq '.[] | .title'
302
-
303
- # List repos and filter by name
151
+ # Filter repos by name
304
152
  bb repo list --json | jq '.[] | select(.name | contains("api"))'
305
153
  ```
306
154
 
307
- ### Quick Repository Setup
155
+ ### CI/CD Usage
308
156
 
309
157
  ```bash
310
- # Create and clone a new repo in one flow
311
- bb repo create my-new-project
312
- bb repo clone myworkspace/my-new-project
313
- cd my-new-project
158
+ export BB_USERNAME=myuser
159
+ export BB_API_TOKEN=my-token
160
+ bb auth login
161
+ bb pr list -w workspace -r repo --json
314
162
  ```
315
163
 
316
164
  ---
317
165
 
318
166
  ## Development
319
167
 
320
- Want to contribute or run locally? Here's how:
321
-
322
168
  ```bash
323
169
  # Clone the repository
324
170
  git clone https://github.com/0pilatos0/bitbucket-cli.git
@@ -335,59 +181,33 @@ bun test
335
181
 
336
182
  # Build for production
337
183
  bun run build
338
-
339
- # Generate API client from OpenAPI spec
340
- bun run generate:api
341
- ```
342
-
343
- ### Project Structure
344
-
345
- ```
346
- bitbucket-cli/
347
- ├── src/
348
- │ ├── commands/ # Command implementations
349
- │ ├── core/ # Core utilities and base classes
350
- │ └── index.ts # Entry point
351
- ├── tests/ # Test files
352
- ├── docs/ # Documentation site (Astro)
353
- └── specs/ # OpenAPI specifications
354
184
  ```
355
185
 
356
186
  ---
357
187
 
358
188
  ## Contributing
359
189
 
360
- We welcome contributions from the community! Whether it's:
361
-
362
- - Reporting bugs
363
- - Suggesting new features
364
- - Improving documentation
365
- - Submitting pull requests
366
-
367
- Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
368
-
369
- ### Quick Contribution Steps
190
+ We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
370
191
 
371
192
  1. Fork the repository
372
- 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
193
+ 2. Create a feature branch
373
194
  3. Make your changes
374
195
  4. Run tests (`bun test`)
375
- 5. Commit with a descriptive message
376
- 6. Push and open a Pull Request
196
+ 5. Submit a Pull Request
377
197
 
378
198
  ---
379
199
 
380
200
  ## Acknowledgments
381
201
 
382
- - Inspired by [GitHub CLI (`gh`)](https://cli.github.com/) — the gold standard for repository CLIs
383
- - Built with [Commander.js](https://github.com/tj/commander.js) for robust command parsing
202
+ - Inspired by [GitHub CLI (`gh`)](https://cli.github.com/)
203
+ - Built with [Commander.js](https://github.com/tj/commander.js)
384
204
  - Uses the [Bitbucket Cloud REST API](https://developer.atlassian.com/cloud/bitbucket/rest/)
385
205
 
386
206
  ---
387
207
 
388
208
  ## License
389
209
 
390
- This project is licensed under the **MIT License** — see the [LICENSE](LICENSE) file for details.
210
+ MIT License — see [LICENSE](LICENSE) for details.
391
211
 
392
212
  ---
393
213
 
package/dist/index.js CHANGED
@@ -27629,7 +27629,7 @@ var require_iso2022 = __commonJS((exports, module) => {
27629
27629
 
27630
27630
  // node_modules/external-editor/node_modules/chardet/index.js
27631
27631
  var require_chardet = __commonJS((exports, module) => {
27632
- var fs = __require("fs");
27632
+ var fs2 = __require("fs");
27633
27633
  var utf8 = require_utf8();
27634
27634
  var unicode = require_unicode();
27635
27635
  var mbcs = require_mbcs();
@@ -27703,29 +27703,29 @@ var require_chardet = __commonJS((exports, module) => {
27703
27703
  var fd;
27704
27704
  var handler = function(err, buffer) {
27705
27705
  if (fd) {
27706
- fs.closeSync(fd);
27706
+ fs2.closeSync(fd);
27707
27707
  }
27708
27708
  if (err)
27709
27709
  return cb(err, null);
27710
27710
  cb(null, self2.detect(buffer, opts));
27711
27711
  };
27712
27712
  if (opts && opts.sampleSize) {
27713
- fd = fs.openSync(filepath, "r"), sample = Buffer.allocUnsafe(opts.sampleSize);
27714
- fs.read(fd, sample, 0, opts.sampleSize, null, function(err) {
27713
+ fd = fs2.openSync(filepath, "r"), sample = Buffer.allocUnsafe(opts.sampleSize);
27714
+ fs2.read(fd, sample, 0, opts.sampleSize, null, function(err) {
27715
27715
  handler(err, sample);
27716
27716
  });
27717
27717
  return;
27718
27718
  }
27719
- fs.readFile(filepath, handler);
27719
+ fs2.readFile(filepath, handler);
27720
27720
  };
27721
27721
  module.exports.detectFileSync = function(filepath, opts) {
27722
27722
  if (opts && opts.sampleSize) {
27723
- var fd = fs.openSync(filepath, "r"), sample2 = Buffer.allocUnsafe(opts.sampleSize);
27724
- fs.readSync(fd, sample2, 0, opts.sampleSize);
27725
- fs.closeSync(fd);
27723
+ var fd = fs2.openSync(filepath, "r"), sample2 = Buffer.allocUnsafe(opts.sampleSize);
27724
+ fs2.readSync(fd, sample2, 0, opts.sampleSize);
27725
+ fs2.closeSync(fd);
27726
27726
  return self2.detect(sample2, opts);
27727
27727
  }
27728
- return self2.detect(fs.readFileSync(filepath), opts);
27728
+ return self2.detect(fs2.readFileSync(filepath), opts);
27729
27729
  };
27730
27730
  module.exports.detectAll = function(buffer, opts) {
27731
27731
  if (typeof opts !== "object") {
@@ -31087,7 +31087,7 @@ var require_tmp = __commonJS((exports, module) => {
31087
31087
  *
31088
31088
  * MIT Licensed
31089
31089
  */
31090
- var fs = __require("fs");
31090
+ var fs2 = __require("fs");
31091
31091
  var path = __require("path");
31092
31092
  var crypto = __require("crypto");
31093
31093
  var osTmpDir = require_os_tmpdir();
@@ -31151,7 +31151,7 @@ var require_tmp = __commonJS((exports, module) => {
31151
31151
  return cb(new Error("Invalid template provided"));
31152
31152
  (function _getUniqueName() {
31153
31153
  const name = _generateTmpName(opts);
31154
- fs.stat(name, function(err) {
31154
+ fs2.stat(name, function(err) {
31155
31155
  if (!err) {
31156
31156
  if (tries-- > 0)
31157
31157
  return _getUniqueName();
@@ -31170,7 +31170,7 @@ var require_tmp = __commonJS((exports, module) => {
31170
31170
  do {
31171
31171
  const name = _generateTmpName(opts);
31172
31172
  try {
31173
- fs.statSync(name);
31173
+ fs2.statSync(name);
31174
31174
  } catch (e) {
31175
31175
  return name;
31176
31176
  }
@@ -31183,14 +31183,14 @@ var require_tmp = __commonJS((exports, module) => {
31183
31183
  tmpName(opts, function _tmpNameCreated(err, name) {
31184
31184
  if (err)
31185
31185
  return cb(err);
31186
- fs.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err2, fd) {
31186
+ fs2.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err2, fd) {
31187
31187
  if (err2)
31188
31188
  return cb(err2);
31189
31189
  if (opts.discardDescriptor) {
31190
- return fs.close(fd, function _discardCallback(err3) {
31190
+ return fs2.close(fd, function _discardCallback(err3) {
31191
31191
  if (err3) {
31192
31192
  try {
31193
- fs.unlinkSync(name);
31193
+ fs2.unlinkSync(name);
31194
31194
  } catch (e) {
31195
31195
  if (!isENOENT(e)) {
31196
31196
  err3 = e;
@@ -31213,9 +31213,9 @@ var require_tmp = __commonJS((exports, module) => {
31213
31213
  opts.postfix = opts.postfix || ".tmp";
31214
31214
  const discardOrDetachDescriptor = opts.discardDescriptor || opts.detachDescriptor;
31215
31215
  const name = tmpNameSync(opts);
31216
- var fd = fs.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);
31216
+ var fd = fs2.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);
31217
31217
  if (opts.discardDescriptor) {
31218
- fs.closeSync(fd);
31218
+ fs2.closeSync(fd);
31219
31219
  fd = undefined;
31220
31220
  }
31221
31221
  return {
@@ -31227,9 +31227,9 @@ var require_tmp = __commonJS((exports, module) => {
31227
31227
  function _rmdirRecursiveSync(root) {
31228
31228
  const dirs = [root];
31229
31229
  do {
31230
- var dir2 = dirs.pop(), deferred = false, files = fs.readdirSync(dir2);
31230
+ var dir2 = dirs.pop(), deferred = false, files = fs2.readdirSync(dir2);
31231
31231
  for (var i = 0, length = files.length;i < length; i++) {
31232
- var file2 = path.join(dir2, files[i]), stat = fs.lstatSync(file2);
31232
+ var file2 = path.join(dir2, files[i]), stat = fs2.lstatSync(file2);
31233
31233
  if (stat.isDirectory()) {
31234
31234
  if (!deferred) {
31235
31235
  deferred = true;
@@ -31237,11 +31237,11 @@ var require_tmp = __commonJS((exports, module) => {
31237
31237
  }
31238
31238
  dirs.push(file2);
31239
31239
  } else {
31240
- fs.unlinkSync(file2);
31240
+ fs2.unlinkSync(file2);
31241
31241
  }
31242
31242
  }
31243
31243
  if (!deferred) {
31244
- fs.rmdirSync(dir2);
31244
+ fs2.rmdirSync(dir2);
31245
31245
  }
31246
31246
  } while (dirs.length !== 0);
31247
31247
  }
@@ -31250,7 +31250,7 @@ var require_tmp = __commonJS((exports, module) => {
31250
31250
  tmpName(opts, function _tmpNameCreated(err, name) {
31251
31251
  if (err)
31252
31252
  return cb(err);
31253
- fs.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err2) {
31253
+ fs2.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err2) {
31254
31254
  if (err2)
31255
31255
  return cb(err2);
31256
31256
  cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
@@ -31260,7 +31260,7 @@ var require_tmp = __commonJS((exports, module) => {
31260
31260
  function dirSync(options) {
31261
31261
  var args = _parseArguments(options), opts = args[0];
31262
31262
  const name = tmpNameSync(opts);
31263
- fs.mkdirSync(name, opts.mode || DIR_MODE);
31263
+ fs2.mkdirSync(name, opts.mode || DIR_MODE);
31264
31264
  return {
31265
31265
  name,
31266
31266
  removeCallback: _prepareTmpDirRemoveCallback(name, opts)
@@ -31270,7 +31270,7 @@ var require_tmp = __commonJS((exports, module) => {
31270
31270
  const removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) {
31271
31271
  try {
31272
31272
  if (0 <= fdPath[0]) {
31273
- fs.closeSync(fdPath[0]);
31273
+ fs2.closeSync(fdPath[0]);
31274
31274
  }
31275
31275
  } catch (e) {
31276
31276
  if (!isEBADF(e) && !isENOENT(e)) {
@@ -31278,7 +31278,7 @@ var require_tmp = __commonJS((exports, module) => {
31278
31278
  }
31279
31279
  }
31280
31280
  try {
31281
- fs.unlinkSync(fdPath[1]);
31281
+ fs2.unlinkSync(fdPath[1]);
31282
31282
  } catch (e) {
31283
31283
  if (!isENOENT(e)) {
31284
31284
  throw e;
@@ -31291,7 +31291,7 @@ var require_tmp = __commonJS((exports, module) => {
31291
31291
  return removeCallback;
31292
31292
  }
31293
31293
  function _prepareTmpDirRemoveCallback(name, opts) {
31294
- const removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs.rmdirSync.bind(fs);
31294
+ const removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs2.rmdirSync.bind(fs2);
31295
31295
  const removeCallback = _prepareRemoveCallback(removeFunction, name);
31296
31296
  if (!opts.keep) {
31297
31297
  _removeObjects.unshift(removeCallback);
@@ -32550,13 +32550,13 @@ var require_src = __commonJS((exports, module) => {
32550
32550
 
32551
32551
  // node_modules/tabtab/lib/utils/tabtabDebug.js
32552
32552
  var require_tabtabDebug = __commonJS((exports, module) => {
32553
- var fs = __require("fs");
32553
+ var fs2 = __require("fs");
32554
32554
  var util = __require("util");
32555
32555
  var tabtabDebug = (name) => {
32556
32556
  let debug = require_src()(name);
32557
32557
  if (process.env.TABTAB_DEBUG) {
32558
32558
  const file = process.env.TABTAB_DEBUG;
32559
- const stream = fs.createWriteStream(file, {
32559
+ const stream = fs2.createWriteStream(file, {
32560
32560
  flags: "a+"
32561
32561
  });
32562
32562
  const log = (...args) => {
@@ -32700,7 +32700,7 @@ var require_promisify = __commonJS((exports) => {
32700
32700
  // node_modules/mkdirp/index.js
32701
32701
  var require_mkdirp = __commonJS((exports, module) => {
32702
32702
  var path = __require("path");
32703
- var fs = __require("fs");
32703
+ var fs2 = __require("fs");
32704
32704
  var _0777 = parseInt("0777", 8);
32705
32705
  module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;
32706
32706
  function mkdirP(p, opts, f, made) {
@@ -32711,7 +32711,7 @@ var require_mkdirp = __commonJS((exports, module) => {
32711
32711
  opts = { mode: opts };
32712
32712
  }
32713
32713
  var mode = opts.mode;
32714
- var xfs = opts.fs || fs;
32714
+ var xfs = opts.fs || fs2;
32715
32715
  if (mode === undefined) {
32716
32716
  mode = _0777;
32717
32717
  }
@@ -32751,7 +32751,7 @@ var require_mkdirp = __commonJS((exports, module) => {
32751
32751
  opts = { mode: opts };
32752
32752
  }
32753
32753
  var mode = opts.mode;
32754
- var xfs = opts.fs || fs;
32754
+ var xfs = opts.fs || fs2;
32755
32755
  if (mode === undefined) {
32756
32756
  mode = _0777;
32757
32757
  }
@@ -32791,10 +32791,10 @@ var require_systemShell = __commonJS((exports, module) => {
32791
32791
 
32792
32792
  // node_modules/tabtab/lib/utils/exists.js
32793
32793
  var require_exists = __commonJS((exports, module) => {
32794
- var fs = __require("fs");
32794
+ var fs2 = __require("fs");
32795
32795
  var untildify = require_untildify();
32796
32796
  var { promisify: promisify2 } = require_promisify();
32797
- var readFile = promisify2(fs.readFile);
32797
+ var readFile = promisify2(fs2.readFile);
32798
32798
  module.exports = async (file) => {
32799
32799
  let fileExists;
32800
32800
  try {
@@ -32838,16 +32838,16 @@ var require_constants = __commonJS((exports, module) => {
32838
32838
  // node_modules/tabtab/lib/installer.js
32839
32839
  var require_installer = __commonJS((exports, module) => {
32840
32840
  var __dirname = "/home/runner/work/bitbucket-cli/bitbucket-cli/node_modules/tabtab/lib";
32841
- var fs = __require("fs");
32841
+ var fs2 = __require("fs");
32842
32842
  var path = __require("path");
32843
32843
  var untildify = require_untildify();
32844
32844
  var { promisify: promisify2 } = require_promisify();
32845
32845
  var mkdirp = promisify2(require_mkdirp());
32846
32846
  var { tabtabDebug, systemShell, exists } = require_utils2();
32847
32847
  var debug = tabtabDebug("tabtab:installer");
32848
- var readFile = promisify2(fs.readFile);
32849
- var writeFile = promisify2(fs.writeFile);
32850
- var unlink = promisify2(fs.unlink);
32848
+ var readFile = promisify2(fs2.readFile);
32849
+ var writeFile = promisify2(fs2.writeFile);
32850
+ var unlink = promisify2(fs2.unlink);
32851
32851
  var {
32852
32852
  BASH_LOCATION,
32853
32853
  FISH_LOCATION,
@@ -32907,7 +32907,7 @@ var require_installer = __commonJS((exports, module) => {
32907
32907
  const filepath = untildify(filename);
32908
32908
  debug("Creating directory for %s file", filepath);
32909
32909
  mkdirp(path.dirname(filepath)).then(() => {
32910
- const stream = fs.createWriteStream(filepath, { flags: "a" });
32910
+ const stream = fs2.createWriteStream(filepath, { flags: "a" });
32911
32911
  stream.on("error", reject);
32912
32912
  stream.on("finish", () => resolve());
32913
32913
  debug("Writing to shell configuration file (%s)", filename);
@@ -33258,11 +33258,16 @@ var ServiceTokens = {
33258
33258
  CreatePRCommand: "CreatePRCommand",
33259
33259
  ListPRsCommand: "ListPRsCommand",
33260
33260
  ViewPRCommand: "ViewPRCommand",
33261
+ EditPRCommand: "EditPRCommand",
33261
33262
  MergePRCommand: "MergePRCommand",
33262
33263
  ApprovePRCommand: "ApprovePRCommand",
33263
33264
  DeclinePRCommand: "DeclinePRCommand",
33264
33265
  CheckoutPRCommand: "CheckoutPRCommand",
33265
33266
  DiffPRCommand: "DiffPRCommand",
33267
+ CommentPRCommand: "CommentPRCommand",
33268
+ ListCommentsPRCommand: "ListCommentsPRCommand",
33269
+ EditCommentPRCommand: "EditCommentPRCommand",
33270
+ DeleteCommentPRCommand: "DeleteCommentPRCommand",
33266
33271
  GetConfigCommand: "GetConfigCommand",
33267
33272
  SetConfigCommand: "SetConfigCommand",
33268
33273
  ListConfigCommand: "ListConfigCommand",
@@ -34278,6 +34283,11 @@ class HttpClient {
34278
34283
  body: body ? JSON.stringify(body) : undefined,
34279
34284
  signal: controller.signal
34280
34285
  });
34286
+ if (process.env.DEBUG === "true") {
34287
+ console.debug(`[HTTP] ${method} ${url} - ${response.status}`);
34288
+ console.debug(`[HTTP] Response Headers:`, Object.fromEntries(response.headers.entries()));
34289
+ console.debug(`[HTTP] Response Body:`, await response.clone().text());
34290
+ }
34281
34291
  clearTimeout(timeoutId);
34282
34292
  return acceptText ? this.handleTextResponse(response) : this.handleResponse(response);
34283
34293
  } catch (error) {
@@ -34298,11 +34308,12 @@ class HttpClient {
34298
34308
  }
34299
34309
  async handleResponse(response) {
34300
34310
  if (!response.ok) {
34311
+ const textBody = await response.text();
34301
34312
  let errorBody;
34302
34313
  try {
34303
- errorBody = await response.json();
34314
+ errorBody = JSON.parse(textBody);
34304
34315
  } catch {
34305
- errorBody = await response.text();
34316
+ errorBody = textBody;
34306
34317
  }
34307
34318
  const message = this.extractErrorMessage(errorBody, response.statusText);
34308
34319
  return Result.err(new APIError(message, response.status, errorBody, {
@@ -34325,11 +34336,12 @@ class HttpClient {
34325
34336
  }
34326
34337
  async handleTextResponse(response) {
34327
34338
  if (!response.ok) {
34339
+ const textBody = await response.text();
34328
34340
  let errorBody;
34329
34341
  try {
34330
- errorBody = await response.json();
34342
+ errorBody = JSON.parse(textBody);
34331
34343
  } catch {
34332
- errorBody = await response.text();
34344
+ errorBody = textBody;
34333
34345
  }
34334
34346
  const message = this.extractErrorMessage(errorBody, response.statusText);
34335
34347
  return Result.err(new APIError(message, response.status, errorBody, {
@@ -34391,6 +34403,16 @@ class UserRepository {
34391
34403
  return this.httpClient.get("/user");
34392
34404
  }
34393
34405
  }
34406
+ // src/constants.ts
34407
+ var API_PAGELEN_LIMITS = {
34408
+ PULL_REQUESTS: 50,
34409
+ REPOSITORIES: 100
34410
+ };
34411
+ var DEFAULT_PAGELEN = {
34412
+ PULL_REQUESTS: 25,
34413
+ REPOSITORIES: 25
34414
+ };
34415
+
34394
34416
  // src/repositories/repo.repository.ts
34395
34417
  class RepoRepository {
34396
34418
  httpClient;
@@ -34401,7 +34423,8 @@ class RepoRepository {
34401
34423
  return this.httpClient.get(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}`);
34402
34424
  }
34403
34425
  async list(workspace, limit = 25) {
34404
- return this.httpClient.get(`/repositories/${encodeURIComponent(workspace)}?pagelen=${limit}`);
34426
+ const safeLimit = Math.min(limit, API_PAGELEN_LIMITS.REPOSITORIES);
34427
+ return this.httpClient.get(`/repositories/${encodeURIComponent(workspace)}?pagelen=${safeLimit}`);
34405
34428
  }
34406
34429
  async create(workspace, request) {
34407
34430
  const repoSlug = request.name.toLowerCase().replace(/\s+/g, "-");
@@ -34425,19 +34448,23 @@ class PullRequestRepository {
34425
34448
  return this.httpClient.get(this.buildPath(workspace, repoSlug, `/${id}`));
34426
34449
  }
34427
34450
  async list(workspace, repoSlug, state = "OPEN", limit = 25) {
34428
- return this.httpClient.get(this.buildPath(workspace, repoSlug, `?state=${state}&pagelen=${limit}`));
34451
+ const safeLimit = Math.min(limit, API_PAGELEN_LIMITS.PULL_REQUESTS);
34452
+ return this.httpClient.get(this.buildPath(workspace, repoSlug, `?state=${state}&pagelen=${safeLimit}`));
34429
34453
  }
34430
34454
  async create(workspace, repoSlug, request) {
34431
34455
  return this.httpClient.post(this.buildPath(workspace, repoSlug), request);
34432
34456
  }
34457
+ async update(workspace, repoSlug, id, request) {
34458
+ return this.httpClient.put(this.buildPath(workspace, repoSlug, `/${id}`), request);
34459
+ }
34433
34460
  async merge(workspace, repoSlug, id, request) {
34434
34461
  return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/merge`), request);
34435
34462
  }
34436
34463
  async approve(workspace, repoSlug, id) {
34437
- return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/approve`));
34464
+ return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/approve`), {});
34438
34465
  }
34439
34466
  async decline(workspace, repoSlug, id) {
34440
- return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/decline`));
34467
+ return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/decline`), {});
34441
34468
  }
34442
34469
  async getDiff(workspace, repoSlug, id) {
34443
34470
  return this.httpClient.getText(this.buildPath(workspace, repoSlug, `/${id}/diff`));
@@ -34445,6 +34472,22 @@ class PullRequestRepository {
34445
34472
  async getDiffstat(workspace, repoSlug, id) {
34446
34473
  return this.httpClient.get(this.buildPath(workspace, repoSlug, `/${id}/diffstat`));
34447
34474
  }
34475
+ async listComments(workspace, repoSlug, prId, limit = 25) {
34476
+ const safeLimit = Math.min(limit, API_PAGELEN_LIMITS.PULL_REQUESTS);
34477
+ return this.httpClient.get(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${prId}/comments?pagelen=${safeLimit}`);
34478
+ }
34479
+ async getComment(workspace, repoSlug, prId, commentId) {
34480
+ return this.httpClient.get(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${prId}/comments/${commentId}`);
34481
+ }
34482
+ async createComment(workspace, repoSlug, prId, content) {
34483
+ return this.httpClient.post(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${prId}/comments`, { content: { raw: content } });
34484
+ }
34485
+ async updateComment(workspace, repoSlug, prId, commentId, content) {
34486
+ return this.httpClient.put(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${prId}/comments/${commentId}`, { content: { raw: content } });
34487
+ }
34488
+ async deleteComment(workspace, repoSlug, prId, commentId) {
34489
+ return this.httpClient.delete(`/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${prId}/comments/${commentId}`);
34490
+ }
34448
34491
  }
34449
34492
  // src/core/base-command.ts
34450
34493
  class BaseCommand {
@@ -35069,6 +35112,98 @@ class ViewPRCommand extends BaseCommand {
35069
35112
  }
35070
35113
  }
35071
35114
 
35115
+ // src/commands/pr/edit.command.ts
35116
+ import * as fs from "fs";
35117
+ class EditPRCommand extends BaseCommand {
35118
+ prRepository;
35119
+ contextService;
35120
+ gitService;
35121
+ name = "edit";
35122
+ description = "Edit a pull request";
35123
+ constructor(prRepository, contextService, gitService, output) {
35124
+ super(output);
35125
+ this.prRepository = prRepository;
35126
+ this.contextService = contextService;
35127
+ this.gitService = gitService;
35128
+ }
35129
+ async execute(options, context) {
35130
+ const repoContextResult = await this.contextService.requireRepoContext({
35131
+ ...context.globalOptions,
35132
+ ...options
35133
+ });
35134
+ if (!repoContextResult.success) {
35135
+ this.handleResult(repoContextResult, context);
35136
+ return repoContextResult;
35137
+ }
35138
+ const { workspace, repoSlug } = repoContextResult.value;
35139
+ let prId;
35140
+ if (options.id) {
35141
+ prId = parseInt(options.id, 10);
35142
+ } else {
35143
+ const branchResult = await this.gitService.getCurrentBranch();
35144
+ if (!branchResult.success) {
35145
+ this.handleResult(branchResult, context);
35146
+ return branchResult;
35147
+ }
35148
+ const currentBranch = branchResult.value;
35149
+ const prsResult = await this.prRepository.list(workspace, repoSlug, "OPEN", DEFAULT_PAGELEN.PULL_REQUESTS);
35150
+ if (!prsResult.success) {
35151
+ this.handleResult(prsResult, context);
35152
+ return prsResult;
35153
+ }
35154
+ const matchingPR = prsResult.value.values.find((pr) => pr.source.branch.name === currentBranch);
35155
+ if (!matchingPR) {
35156
+ const error = new ValidationError("id", `No open pull request found for current branch '${currentBranch}'. Specify a PR ID explicitly.`);
35157
+ this.output.error(error.message);
35158
+ if (true) {
35159
+ process.exitCode = 1;
35160
+ }
35161
+ return Result.err(error);
35162
+ }
35163
+ prId = matchingPR.id;
35164
+ }
35165
+ let body = options.body;
35166
+ if (options.bodyFile) {
35167
+ try {
35168
+ body = fs.readFileSync(options.bodyFile, "utf-8");
35169
+ } catch (err) {
35170
+ const error = new ValidationError("bodyFile", `Failed to read file '${options.bodyFile}': ${err instanceof Error ? err.message : "Unknown error"}`);
35171
+ this.output.error(error.message);
35172
+ if (true) {
35173
+ process.exitCode = 1;
35174
+ }
35175
+ return Result.err(error);
35176
+ }
35177
+ }
35178
+ if (!options.title && !body) {
35179
+ const error = new ValidationError("title", "At least one of --title or --body (or --body-file) is required.");
35180
+ this.output.error(error.message);
35181
+ if (true) {
35182
+ process.exitCode = 1;
35183
+ }
35184
+ return Result.err(error);
35185
+ }
35186
+ const request = {};
35187
+ if (options.title) {
35188
+ request.title = options.title;
35189
+ }
35190
+ if (body) {
35191
+ request.description = body;
35192
+ }
35193
+ const result = await this.prRepository.update(workspace, repoSlug, prId, request);
35194
+ this.handleResult(result, context, (pr) => {
35195
+ this.output.success(`Updated pull request #${pr.id}`);
35196
+ this.output.text(` ${source_default.dim("Title:")} ${pr.title}`);
35197
+ if (pr.description) {
35198
+ const truncatedDesc = pr.description.length > 100 ? pr.description.substring(0, 100) + "..." : pr.description;
35199
+ this.output.text(` ${source_default.dim("Description:")} ${truncatedDesc}`);
35200
+ }
35201
+ this.output.text(` ${source_default.dim("URL:")} ${pr.links.html.href}`);
35202
+ });
35203
+ return result;
35204
+ }
35205
+ }
35206
+
35072
35207
  // src/commands/pr/merge.command.ts
35073
35208
  class MergePRCommand extends BaseCommand {
35074
35209
  prRepository;
@@ -35279,7 +35414,7 @@ class DiffPRCommand extends BaseCommand {
35279
35414
  return Result.err(error);
35280
35415
  }
35281
35416
  const currentBranch = currentBranchResult.value;
35282
- const prsResult = await this.prRepository.list(workspace, repoSlug, "OPEN", 100);
35417
+ const prsResult = await this.prRepository.list(workspace, repoSlug, "OPEN", DEFAULT_PAGELEN.PULL_REQUESTS);
35283
35418
  if (!prsResult.success) {
35284
35419
  this.handleResult(prsResult, context);
35285
35420
  return prsResult;
@@ -35445,6 +35580,139 @@ class DiffPRCommand extends BaseCommand {
35445
35580
  }
35446
35581
  }
35447
35582
 
35583
+ // src/commands/pr/comment.command.ts
35584
+ class CommentPRCommand extends BaseCommand {
35585
+ prRepository;
35586
+ contextService;
35587
+ name = "comment";
35588
+ description = "Add a comment to a pull request";
35589
+ constructor(prRepository, contextService, output) {
35590
+ super(output);
35591
+ this.prRepository = prRepository;
35592
+ this.contextService = contextService;
35593
+ }
35594
+ async execute(options, context) {
35595
+ const repoContextResult = await this.contextService.requireRepoContext({
35596
+ ...context.globalOptions,
35597
+ ...options
35598
+ });
35599
+ if (!repoContextResult.success) {
35600
+ this.handleResult(repoContextResult, context);
35601
+ return repoContextResult;
35602
+ }
35603
+ const { workspace, repoSlug } = repoContextResult.value;
35604
+ const prId = parseInt(options.id, 10);
35605
+ const result = await this.prRepository.createComment(workspace, repoSlug, prId, options.message);
35606
+ this.handleResult(result, context, () => {
35607
+ this.output.success(`Added comment to pull request #${prId}`);
35608
+ });
35609
+ return result;
35610
+ }
35611
+ }
35612
+
35613
+ // src/commands/pr/comments.list.command.ts
35614
+ class ListCommentsPRCommand extends BaseCommand {
35615
+ prRepository;
35616
+ contextService;
35617
+ name = "comments";
35618
+ description = "List comments on a pull request";
35619
+ constructor(prRepository, contextService, output) {
35620
+ super(output);
35621
+ this.prRepository = prRepository;
35622
+ this.contextService = contextService;
35623
+ }
35624
+ async execute(options, context) {
35625
+ const repoContextResult = await this.contextService.requireRepoContext({
35626
+ ...context.globalOptions,
35627
+ ...options
35628
+ });
35629
+ if (!repoContextResult.success) {
35630
+ this.handleResult(repoContextResult, context);
35631
+ return repoContextResult;
35632
+ }
35633
+ const { workspace, repoSlug } = repoContextResult.value;
35634
+ const prId = parseInt(options.id, 10);
35635
+ const limit = options.limit ? parseInt(options.limit, 10) : 25;
35636
+ const result = await this.prRepository.listComments(workspace, repoSlug, prId, limit);
35637
+ this.handleResult(result, context, (data) => {
35638
+ if (data.values.length === 0) {
35639
+ this.output.info("No comments found on this pull request");
35640
+ return;
35641
+ }
35642
+ const rows = data.values.map((comment) => [
35643
+ comment.id.toString(),
35644
+ comment.author?.username ?? comment.author?.display_name ?? "Unknown",
35645
+ comment.content.raw.slice(0, 60) + (comment.content.raw.length > 60 ? "..." : ""),
35646
+ this.output.formatDate(comment.created_on)
35647
+ ]);
35648
+ this.output.table(["ID", "Author", "Content", "Date"], rows);
35649
+ });
35650
+ return result;
35651
+ }
35652
+ }
35653
+
35654
+ // src/commands/pr/comments.edit.command.ts
35655
+ class EditCommentPRCommand extends BaseCommand {
35656
+ prRepository;
35657
+ contextService;
35658
+ name = "edit";
35659
+ description = "Edit a comment on a pull request";
35660
+ constructor(prRepository, contextService, output) {
35661
+ super(output);
35662
+ this.prRepository = prRepository;
35663
+ this.contextService = contextService;
35664
+ }
35665
+ async execute(options, context) {
35666
+ const repoContextResult = await this.contextService.requireRepoContext({
35667
+ ...context.globalOptions,
35668
+ ...options
35669
+ });
35670
+ if (!repoContextResult.success) {
35671
+ this.handleResult(repoContextResult, context);
35672
+ return repoContextResult;
35673
+ }
35674
+ const { workspace, repoSlug } = repoContextResult.value;
35675
+ const prId = parseInt(options.prId, 10);
35676
+ const commentId = parseInt(options.commentId, 10);
35677
+ const result = await this.prRepository.updateComment(workspace, repoSlug, prId, commentId, options.message);
35678
+ this.handleResult(result, context, () => {
35679
+ this.output.success(`Updated comment #${commentId}`);
35680
+ });
35681
+ return result;
35682
+ }
35683
+ }
35684
+
35685
+ // src/commands/pr/comments.delete.command.ts
35686
+ class DeleteCommentPRCommand extends BaseCommand {
35687
+ prRepository;
35688
+ contextService;
35689
+ name = "delete";
35690
+ description = "Delete a comment on a pull request";
35691
+ constructor(prRepository, contextService, output) {
35692
+ super(output);
35693
+ this.prRepository = prRepository;
35694
+ this.contextService = contextService;
35695
+ }
35696
+ async execute(options, context) {
35697
+ const repoContextResult = await this.contextService.requireRepoContext({
35698
+ ...context.globalOptions,
35699
+ ...options
35700
+ });
35701
+ if (!repoContextResult.success) {
35702
+ this.handleResult(repoContextResult, context);
35703
+ return repoContextResult;
35704
+ }
35705
+ const { workspace, repoSlug } = repoContextResult.value;
35706
+ const prId = parseInt(options.prId, 10);
35707
+ const commentId = parseInt(options.commentId, 10);
35708
+ const result = await this.prRepository.deleteComment(workspace, repoSlug, prId, commentId);
35709
+ this.handleResult(result, context, () => {
35710
+ this.output.success(`Deleted comment #${commentId} from PR #${prId}`);
35711
+ });
35712
+ return result;
35713
+ }
35714
+ }
35715
+
35448
35716
  // src/types/config.ts
35449
35717
  var SETTABLE_CONFIG_KEYS = ["defaultWorkspace"];
35450
35718
  var READABLE_CONFIG_KEYS = ["username", "defaultWorkspace"];
@@ -35722,6 +35990,13 @@ function bootstrap() {
35722
35990
  const output = container.resolve(ServiceTokens.OutputService);
35723
35991
  return new ViewPRCommand(prRepo, contextService, output);
35724
35992
  });
35993
+ container.register(ServiceTokens.EditPRCommand, () => {
35994
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
35995
+ const contextService = container.resolve(ServiceTokens.ContextService);
35996
+ const gitService = container.resolve(ServiceTokens.GitService);
35997
+ const output = container.resolve(ServiceTokens.OutputService);
35998
+ return new EditPRCommand(prRepo, contextService, gitService, output);
35999
+ });
35725
36000
  container.register(ServiceTokens.MergePRCommand, () => {
35726
36001
  const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
35727
36002
  const contextService = container.resolve(ServiceTokens.ContextService);
@@ -35754,6 +36029,30 @@ function bootstrap() {
35754
36029
  const output = container.resolve(ServiceTokens.OutputService);
35755
36030
  return new DiffPRCommand(prRepo, contextService, gitService, output);
35756
36031
  });
36032
+ container.register(ServiceTokens.CommentPRCommand, () => {
36033
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
36034
+ const contextService = container.resolve(ServiceTokens.ContextService);
36035
+ const output = container.resolve(ServiceTokens.OutputService);
36036
+ return new CommentPRCommand(prRepo, contextService, output);
36037
+ });
36038
+ container.register(ServiceTokens.ListCommentsPRCommand, () => {
36039
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
36040
+ const contextService = container.resolve(ServiceTokens.ContextService);
36041
+ const output = container.resolve(ServiceTokens.OutputService);
36042
+ return new ListCommentsPRCommand(prRepo, contextService, output);
36043
+ });
36044
+ container.register(ServiceTokens.EditCommentPRCommand, () => {
36045
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
36046
+ const contextService = container.resolve(ServiceTokens.ContextService);
36047
+ const output = container.resolve(ServiceTokens.OutputService);
36048
+ return new EditCommentPRCommand(prRepo, contextService, output);
36049
+ });
36050
+ container.register(ServiceTokens.DeleteCommentPRCommand, () => {
36051
+ const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
36052
+ const contextService = container.resolve(ServiceTokens.ContextService);
36053
+ const output = container.resolve(ServiceTokens.OutputService);
36054
+ return new DeleteCommentPRCommand(prRepo, contextService, output);
36055
+ });
35757
36056
  container.register(ServiceTokens.GetConfigCommand, () => {
35758
36057
  const configService = container.resolve(ServiceTokens.ConfigService);
35759
36058
  const output = container.resolve(ServiceTokens.OutputService);
@@ -35804,7 +36103,7 @@ if (process.argv.includes("--get-yargs-completions") || process.env.COMP_LINE) {
35804
36103
  } else if (env2.prev === "repo") {
35805
36104
  completions.push("clone", "create", "list", "view", "delete");
35806
36105
  } else if (env2.prev === "pr") {
35807
- completions.push("create", "list", "view", "merge", "approve", "decline", "checkout", "diff");
36106
+ completions.push("create", "list", "view", "edit", "merge", "approve", "decline", "checkout", "diff", "comments");
35808
36107
  } else if (env2.prev === "config") {
35809
36108
  completions.push("get", "set", "list");
35810
36109
  } else if (env2.prev === "completion") {
@@ -35930,6 +36229,14 @@ prCmd.command("view <id>").description("View pull request details").action(async
35930
36229
  process.exit(1);
35931
36230
  }
35932
36231
  });
36232
+ prCmd.command("edit [id]").description("Edit a pull request").option("-t, --title <title>", "New pull request title").option("-b, --body <body>", "New pull request description").option("-F, --body-file <file>", "Read description from file").action(async (id, options) => {
36233
+ const cmd = container.resolve(ServiceTokens.EditPRCommand);
36234
+ const context = createContext(cli);
36235
+ const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
36236
+ if (!result.success) {
36237
+ process.exit(1);
36238
+ }
36239
+ });
35933
36240
  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) => {
35934
36241
  const cmd = container.resolve(ServiceTokens.MergePRCommand);
35935
36242
  const context = createContext(cli);
@@ -35970,7 +36277,41 @@ prCmd.command("diff [id]").description("View pull request diff").option("--color
35970
36277
  process.exit(1);
35971
36278
  }
35972
36279
  });
36280
+ var prCommentsCmd = new Command("comments").description("Manage pull request comments");
36281
+ prCommentsCmd.command("list <id>").description("List comments on a pull request").option("--limit <number>", "Maximum number of comments (default: 25)").action(async (id, options) => {
36282
+ const cmd = container.resolve(ServiceTokens.ListCommentsPRCommand);
36283
+ const context = createContext(cli);
36284
+ const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
36285
+ if (!result.success) {
36286
+ process.exit(1);
36287
+ }
36288
+ });
36289
+ prCommentsCmd.command("add <id> <message>").description("Add a comment to a pull request").action(async (id, message, options) => {
36290
+ const cmd = container.resolve(ServiceTokens.CommentPRCommand);
36291
+ const context = createContext(cli);
36292
+ const result = await cmd.execute(withGlobalOptions({ id, message }, context), context);
36293
+ if (!result.success) {
36294
+ process.exit(1);
36295
+ }
36296
+ });
36297
+ prCommentsCmd.command("edit <pr-id> <comment-id> <message>").description("Edit a comment on a pull request").action(async (prId, commentId, message, options) => {
36298
+ const cmd = container.resolve(ServiceTokens.EditCommentPRCommand);
36299
+ const context = createContext(cli);
36300
+ const result = await cmd.execute(withGlobalOptions({ prId, commentId, message }, context), context);
36301
+ if (!result.success) {
36302
+ process.exit(1);
36303
+ }
36304
+ });
36305
+ prCommentsCmd.command("delete <pr-id> <comment-id>").description("Delete a comment on a pull request").action(async (prId, commentId, options) => {
36306
+ const cmd = container.resolve(ServiceTokens.DeleteCommentPRCommand);
36307
+ const context = createContext(cli);
36308
+ const result = await cmd.execute(withGlobalOptions({ prId, commentId }, context), context);
36309
+ if (!result.success) {
36310
+ process.exit(1);
36311
+ }
36312
+ });
35973
36313
  cli.addCommand(prCmd);
36314
+ prCmd.addCommand(prCommentsCmd);
35974
36315
  var configCmd = new Command("config").description("Manage configuration");
35975
36316
  configCmd.command("get <key>").description("Get a configuration value").action(async (key) => {
35976
36317
  const cmd = container.resolve(ServiceTokens.GetConfigCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pilatos/bitbucket-cli",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "A command-line interface for Bitbucket Cloud",
5
5
  "author": "",
6
6
  "license": "MIT",