bitbucketdc-cli 1.0.13 → 1.0.15

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 +121 -0
  2. package/dist/index.js +149 -86
  3. package/package.json +4 -4
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # bitbucketdc-cli
2
+
3
+ Command-line interface for [Bitbucket Data Center](https://developer.atlassian.com/server/bitbucket/rest/v819/intro/). 33 commands across 6 domains — pull requests, commits, files, projects, repos, and comparisons.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g bitbucketdc-cli
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ```bash
14
+ export BITBUCKET_URL="https://bitbucket.example.com" # Base URL of your Bitbucket instance
15
+ export BITBUCKET_TOKEN="your-personal-access-token" # HTTP Access Token from Bitbucket
16
+ ```
17
+
18
+ ## Commands
19
+
20
+ All commands output JSON. Add `--pretty` to pretty-print.
21
+
22
+ ### pr
23
+
24
+ | Command | Description |
25
+ |---------|-------------|
26
+ | `bitbucketdc pr inbox` | List pull requests in your reviewer inbox |
27
+ | `bitbucketdc pr get <project> <repo> <prId>` | Get pull request details |
28
+ | `bitbucketdc pr changes <project> <repo> <prId>` | List changed files |
29
+ | `bitbucketdc pr diff <project> <repo> <prId>` | Get full diff (`--format`: text/json, `--whitespace`: show/ignore-all, `--context-lines`) |
30
+ | `bitbucketdc pr file-diff <project> <repo> <prId> <path>` | Get diff for a specific file |
31
+ | `bitbucketdc pr activities <project> <repo> <prId>` | List activities/events on a PR |
32
+ | `bitbucketdc pr create <project> <repo>` | Create a PR (auto-fetches default reviewers) |
33
+ | `bitbucketdc pr comment <project> <repo> <prId>` | Add a general comment |
34
+ | `bitbucketdc pr file-comment <project> <repo> <prId>` | Add a comment on a specific file |
35
+ | `bitbucketdc pr line-comment <project> <repo> <prId>` | Add an inline comment on a specific line |
36
+ | `bitbucketdc pr delete-comment <project> <repo> <prId> <commentId>` | Delete a comment |
37
+ | `bitbucketdc pr edit-comment <project> <repo> <prId> <commentId>` | Edit a comment |
38
+ | `bitbucketdc pr reaction-add <project> <repo> <prId> <commentId>` | Add emoji reaction to a comment |
39
+ | `bitbucketdc pr reaction-remove <project> <repo> <prId> <commentId>` | Remove emoji reaction from a comment |
40
+ | `bitbucketdc pr review <project> <repo> <prId> <status>` | Set review status (APPROVED, NEEDS_WORK, UNAPPROVED) |
41
+ | `bitbucketdc pr update <project> <repo> <prId>` | Update PR title/description |
42
+ | `bitbucketdc pr can-merge <project> <repo> <prId>` | Check merge eligibility |
43
+ | `bitbucketdc pr merge <project> <repo> <prId>` | Merge a PR (`--strategy`: merge-commit, squash, ff-only) |
44
+ | `bitbucketdc pr decline <project> <repo> <prId>` | Decline/close a PR |
45
+ | `bitbucketdc pr delete <project> <repo> <prId>` | Delete a PR |
46
+
47
+ ### commit
48
+
49
+ | Command | Description |
50
+ |---------|-------------|
51
+ | `bitbucketdc commit changes <project> <repo> <commitId>` | List changed files in a commit |
52
+ | `bitbucketdc commit diff <project> <repo> <commitId>` | Get diff for a commit (`--path` to filter) |
53
+
54
+ ### compare
55
+
56
+ | Command | Description |
57
+ |---------|-------------|
58
+ | `bitbucketdc compare changes <project> <repo>` | List changed files between refs (`--from`, `--to`) |
59
+ | `bitbucketdc compare diff <project> <repo>` | Get diff between refs |
60
+
61
+ ### file
62
+
63
+ | Command | Description |
64
+ |---------|-------------|
65
+ | `bitbucketdc file list <project> <repo>` | List files and directories (`--path`, `--at` ref) |
66
+ | `bitbucketdc file show <project> <repo> <path>` | Show file content (`--at` ref) |
67
+
68
+ ### project
69
+
70
+ | Command | Description |
71
+ |---------|-------------|
72
+ | `bitbucketdc project list` | List Bitbucket projects |
73
+
74
+ ### repo
75
+
76
+ | Command | Description |
77
+ |---------|-------------|
78
+ | `bitbucketdc repo list` | List repositories (`--project`, `--name` filter) |
79
+ | `bitbucketdc repo clone <project> <repo>` | Clone a repository |
80
+ | `bitbucketdc repo attachment download <project> <repo> <path>` | Download a repo attachment |
81
+
82
+ ## Pagination
83
+
84
+ List commands accept `--limit` to control page size. Responses include `nextPage` — pass it back as `--start` to fetch the next page. When `nextPage` is `null`, there are no more results.
85
+
86
+ ## Examples
87
+
88
+ ```bash
89
+ # Check your PR inbox
90
+ bitbucketdc pr inbox
91
+
92
+ # Get a PR with full details
93
+ bitbucketdc pr get AI my-repo 42
94
+
95
+ # Review the diff
96
+ bitbucketdc pr diff AI my-repo 42 --context-lines 5
97
+
98
+ # Diff a single file
99
+ bitbucketdc pr file-diff AI my-repo 42 src/index.ts
100
+
101
+ # Approve a PR
102
+ bitbucketdc pr review AI my-repo 42 APPROVED
103
+
104
+ # Leave an inline comment on a specific line
105
+ bitbucketdc pr line-comment AI my-repo 42 --path src/index.ts --line 15 --line-type ADDED --text "Nitpick: rename this variable"
106
+
107
+ # Create a PR (auto-adds default reviewers)
108
+ bitbucketdc pr create AI my-repo --from feature/x --to main --title "Add feature X"
109
+
110
+ # Merge with squash
111
+ bitbucketdc pr merge AI my-repo 42 --strategy squash
112
+
113
+ # Browse files at a specific branch
114
+ bitbucketdc file list AI my-repo --path src/ --at develop
115
+
116
+ # Show file content
117
+ bitbucketdc file show AI my-repo src/config.ts --at main
118
+
119
+ # Search repos by name within a project
120
+ bitbucketdc repo list --project AI --name "tool"
121
+ ```
package/dist/index.js CHANGED
@@ -1,8 +1,40 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
+ import { readFileSync } from "fs";
5
+ import { dirname, join } from "path";
6
+ import { fileURLToPath } from "url";
4
7
  import { styleText } from "util";
5
- import { Command as Command8 } from "commander";
8
+ import { Command as Command9 } from "commander";
9
+
10
+ // src/utils/cli.ts
11
+ import { InvalidArgumentError } from "commander";
12
+ function intInRange(min, max) {
13
+ return (raw) => {
14
+ const n = parseInt(raw, 10);
15
+ if (Number.isNaN(n) || !Number.isFinite(n)) {
16
+ throw new InvalidArgumentError(`Not a number: "${raw}"`);
17
+ }
18
+ if (n < min || n > max) {
19
+ throw new InvalidArgumentError(`Must be between ${min} and ${max} (got ${n})`);
20
+ }
21
+ return n;
22
+ };
23
+ }
24
+ function nonNegativeInt(raw) {
25
+ const n = parseInt(raw, 10);
26
+ if (Number.isNaN(n) || n < 0) {
27
+ throw new InvalidArgumentError(`Must be a non-negative integer (got "${raw}")`);
28
+ }
29
+ return n;
30
+ }
31
+ function positiveInt(raw) {
32
+ const n = parseInt(raw, 10);
33
+ if (Number.isNaN(n) || n < 1) {
34
+ throw new InvalidArgumentError(`Must be a positive integer (got "${raw}")`);
35
+ }
36
+ return n;
37
+ }
6
38
 
7
39
  // src/utils/client.ts
8
40
  import { BitbucketClient } from "bitbucket-data-center-client";
@@ -131,15 +163,23 @@ function handleError(err) {
131
163
  `
132
164
  );
133
165
  } else {
134
- process.stderr.write(`${JSON.stringify({ error: message })}
135
- `);
166
+ const responseData = err?.response?.data;
167
+ const detail = responseData && typeof responseData === "object" ? responseData : void 0;
168
+ process.stderr.write(
169
+ `${JSON.stringify({
170
+ error: message,
171
+ ...axiosStatus !== void 0 && { statusCode: axiosStatus },
172
+ ...detail && { detail }
173
+ })}
174
+ `
175
+ );
136
176
  }
137
177
  process.exit(1);
138
178
  }
139
179
 
140
180
  // src/commands/commit/changes.ts
141
181
  function changes(parent) {
142
- parent.command("changes").description("List files changed in a specific commit").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--commit <hash>", "Commit hash").option("--limit <n>", "Number of items to return (default: 25)", parseInt).option("--start <n>", "Start index for pagination (default: 0)", parseInt).addHelpText(
182
+ parent.command("changes").description("List files changed in a specific commit").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--commit <hash>", "Commit hash").option("--limit <n>", "Number of items to return (1-1000)", intInRange(1, 1e3), 25).option("--start <n>", "Start index for pagination", nonNegativeInt).addHelpText(
143
183
  "after",
144
184
  `
145
185
  Examples:
@@ -160,8 +200,10 @@ Examples:
160
200
 
161
201
  // src/commands/commit/diff.ts
162
202
  import { Option } from "commander";
203
+ var WHITESPACE_MODES = ["show", "ignore-all"];
204
+ var DIFF_FORMATS = ["text", "json"];
163
205
  function diff(parent) {
164
- parent.command("diff").description("Get diff for a specific commit").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--commit <hash>", "Commit hash").option("--path <path>", "Scope diff to a single file").option("--context <n>", "Number of context lines around changes", parseInt).addOption(new Option("--whitespace <mode>", "Whitespace handling").choices(["show", "ignore-all"])).addOption(new Option("--format <fmt>", "Response format (default: text)").choices(["text", "json"])).addHelpText(
206
+ parent.command("diff").description("Get diff for a specific commit").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--commit <hash>", "Commit hash").option("--path <path>", "Scope diff to a single file").option("--context <n>", "Number of context lines around changes (0-500)", intInRange(0, 500)).addOption(new Option("--whitespace <mode>", "Whitespace handling").choices(WHITESPACE_MODES)).addOption(new Option("--format <fmt>", "Response format").choices(DIFF_FORMATS).default("text")).addHelpText(
165
207
  "after",
166
208
  `
167
209
  Examples:
@@ -178,7 +220,7 @@ Examples:
178
220
  path: opts.path,
179
221
  contextLines: opts.context,
180
222
  whitespace: opts.whitespace,
181
- format: opts.format || "text"
223
+ format: opts.format
182
224
  });
183
225
  if (typeof result === "string") {
184
226
  process.stdout.write(result);
@@ -206,7 +248,7 @@ Examples:
206
248
 
207
249
  // src/commands/compare/changes.ts
208
250
  function changes2(parent) {
209
- parent.command("changes").description("List files that differ between two refs").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--from <ref>", "Source ref (branch, tag, or commit)").requiredOption("--to <ref>", "Target ref (branch, tag, or commit)").option("--limit <n>", "Number of items to return (default: 25)", parseInt).option("--start <n>", "Start index for pagination (default: 0)", parseInt).addHelpText(
251
+ parent.command("changes").description("List files that differ between two refs").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--from <ref>", "Source ref (branch, tag, or commit)").requiredOption("--to <ref>", "Target ref (branch, tag, or commit)").option("--limit <n>", "Number of items to return (1-1000)", intInRange(1, 1e3), 25).option("--start <n>", "Start index for pagination", nonNegativeInt).addHelpText(
210
252
  "after",
211
253
  `
212
254
  Examples:
@@ -231,8 +273,9 @@ Examples:
231
273
 
232
274
  // src/commands/compare/diff.ts
233
275
  import { Option as Option2 } from "commander";
276
+ var WHITESPACE_MODES2 = ["show", "ignore-all"];
234
277
  function diff2(parent) {
235
- parent.command("diff").description("Get diff between two refs (structured JSON)").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--from <ref>", "Source ref (branch, tag, or commit)").requiredOption("--to <ref>", "Target ref (branch, tag, or commit)").option("--path <path>", "Scope diff to a single file").option("--context <n>", "Number of context lines around changes", parseInt).addOption(new Option2("--whitespace <mode>", "Whitespace handling").choices(["show", "ignore-all"])).addHelpText(
278
+ parent.command("diff").description("Get diff between two refs (structured JSON)").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--from <ref>", "Source ref (branch, tag, or commit)").requiredOption("--to <ref>", "Target ref (branch, tag, or commit)").option("--path <path>", "Scope diff to a single file").option("--context <n>", "Number of context lines around changes (0-500)", intInRange(0, 500)).addOption(new Option2("--whitespace <mode>", "Whitespace handling").choices(WHITESPACE_MODES2)).addHelpText(
236
279
  "after",
237
280
  `
238
281
  Examples:
@@ -272,7 +315,7 @@ Examples:
272
315
 
273
316
  // src/commands/file/list.ts
274
317
  function list(parent) {
275
- parent.command("list").description("List directory contents in a repository").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").option("--path <path>", "Directory path (defaults to root)").option("--at <ref>", "Branch, tag, or commit hash").option("--branch <ref>", "Alias for --at").option("--limit <n>", "Number of items to return (default: 500)", parseInt).option("--start <n>", "Start index for pagination (default: 0)", parseInt).addHelpText(
318
+ parent.command("list").description("List directory contents in a repository").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").option("--path <path>", "Directory path (defaults to root)").option("--at <ref>", "Branch, tag, or commit hash").option("--branch <ref>", "Alias for --at").option("--limit <n>", "Number of items to return (1-1000)", intInRange(1, 1e3), 25).option("--start <n>", "Start index for pagination", nonNegativeInt).addHelpText(
276
319
  "after",
277
320
  `
278
321
  Examples:
@@ -334,10 +377,10 @@ Examples:
334
377
 
335
378
  // src/commands/pr/activities.ts
336
379
  function activities(parent) {
337
- parent.command("activities").description("Get activity on a pull request (comments, approvals, merges, etc.)").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID").option(
380
+ parent.command("activities").description("Get activity on a pull request (comments, approvals, merges, etc.)").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).option(
338
381
  "--types <types>",
339
382
  "Comma-separated activity types (APPROVED, COMMENTED, DECLINED, MERGED, OPENED, REOPENED, RESCOPED, REVIEW_COMMENTED, REVIEW_DISCARDED, REVIEW_FINISHED, REVIEWED, UNAPPROVED, UPDATED)"
340
- ).option("--start <n>", "Starting index for pagination (default: 0)", parseInt).option("--limit <n>", "Maximum number of items to return (default: 25)", parseInt).addHelpText(
383
+ ).option("--start <n>", "Starting index for pagination", nonNegativeInt).option("--limit <n>", "Maximum number of items to return (1-1000)", intInRange(1, 1e3), 25).addHelpText(
341
384
  "after",
342
385
  `
343
386
  Examples:
@@ -350,7 +393,7 @@ Examples:
350
393
  const result = await client.pullRequests.getActivities({
351
394
  projectKey: opts.project,
352
395
  repositorySlug: opts.repo,
353
- pullRequestId: parseInt(opts.id),
396
+ pullRequestId: opts.id,
354
397
  start: opts.start,
355
398
  limit: opts.limit
356
399
  });
@@ -367,7 +410,7 @@ Examples:
367
410
 
368
411
  // src/commands/pr/can-merge.ts
369
412
  function canMerge(parent) {
370
- parent.command("can-merge").description("Check if a pull request can be merged").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", parseInt).addHelpText("after", "\nExamples:\n bitbucketdc pr can-merge --project PROJ --repo my-repo --id 42").action(async (opts) => {
413
+ parent.command("can-merge").description("Check if a pull request can be merged").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).addHelpText("after", "\nExamples:\n bitbucketdc pr can-merge --project PROJ --repo my-repo --id 42").action(async (opts) => {
371
414
  const client = getClient();
372
415
  const result = await client.pullRequests.canMerge({
373
416
  projectKey: opts.project,
@@ -380,7 +423,7 @@ function canMerge(parent) {
380
423
 
381
424
  // src/commands/pr/changes.ts
382
425
  function changes3(parent) {
383
- parent.command("changes").description("List changed files in a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID").option("--limit <n>", "Number of items to return (default: 25)", parseInt).addHelpText(
426
+ parent.command("changes").description("List changed files in a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).option("--limit <n>", "Number of items to return (1-1000)", intInRange(1, 1e3), 25).addHelpText(
384
427
  "after",
385
428
  "\nExamples:\n bitbucketdc pr changes --project PROJ --repo my-repo --id 42\n bitbucketdc pr changes --project PROJ --repo my-repo --id 42 --limit 100"
386
429
  ).action(async (opts) => {
@@ -388,7 +431,7 @@ function changes3(parent) {
388
431
  const result = await client.pullRequests.getChanges({
389
432
  projectKey: opts.project,
390
433
  repositorySlug: opts.repo,
391
- pullRequestId: parseInt(opts.id),
434
+ pullRequestId: opts.id,
392
435
  limit: opts.limit
393
436
  });
394
437
  output(result);
@@ -397,7 +440,7 @@ function changes3(parent) {
397
440
 
398
441
  // src/commands/pr/comment.ts
399
442
  function comment(parent) {
400
- parent.command("comment").description("Add a general comment to a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID").requiredOption("--text <text>", "Comment text").option("--parent <n>", "Parent comment ID to reply to").addHelpText(
443
+ parent.command("comment").description("Add a general comment to a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).requiredOption("--text <text>", "Comment text").option("--parent <n>", "Parent comment ID to reply to", positiveInt).addHelpText(
401
444
  "after",
402
445
  `
403
446
  Examples:
@@ -408,9 +451,9 @@ Examples:
408
451
  const result = await client.pullRequests.addComment({
409
452
  projectKey: opts.project,
410
453
  repositorySlug: opts.repo,
411
- pullRequestId: parseInt(opts.id),
454
+ pullRequestId: opts.id,
412
455
  text: opts.text,
413
- parentId: opts.parent ? parseInt(opts.parent) : void 0
456
+ parentId: opts.parent
414
457
  });
415
458
  outputCommentResult(result);
416
459
  });
@@ -460,7 +503,7 @@ Examples:
460
503
 
461
504
  // src/commands/pr/decline.ts
462
505
  function decline(parent) {
463
- parent.command("decline").description("Decline a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", parseInt).addHelpText("after", "\nExamples:\n bitbucketdc pr decline --project PROJ --repo my-repo --id 42").action(async (opts) => {
506
+ parent.command("decline").description("Decline a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).addHelpText("after", "\nExamples:\n bitbucketdc pr decline --project PROJ --repo my-repo --id 42").action(async (opts) => {
464
507
  const client = getClient();
465
508
  const result = await client.pullRequests.decline({
466
509
  projectKey: opts.project,
@@ -473,7 +516,7 @@ function decline(parent) {
473
516
 
474
517
  // src/commands/pr/delete-comment.ts
475
518
  function deleteComment(parent) {
476
- parent.command("delete-comment").description("Delete a pull request comment").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", parseInt).requiredOption("--comment-id <n>", "Comment ID to delete", parseInt).addHelpText(
519
+ parent.command("delete-comment").description("Delete a pull request comment").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).requiredOption("--comment-id <n>", "Comment ID to delete", positiveInt).addHelpText(
477
520
  "after",
478
521
  "\nExamples:\n bitbucketdc pr delete-comment --project PROJ --repo my-repo --id 42 --comment-id 123"
479
522
  ).action(async (opts) => {
@@ -497,7 +540,7 @@ function deleteComment(parent) {
497
540
 
498
541
  // src/commands/pr/delete.ts
499
542
  function deletePr(parent) {
500
- parent.command("delete").description("Delete a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", parseInt).addHelpText("after", "\nExamples:\n bitbucketdc pr delete --project PROJ --repo my-repo --id 42").action(async (opts) => {
543
+ parent.command("delete").description("Delete a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).addHelpText("after", "\nExamples:\n bitbucketdc pr delete --project PROJ --repo my-repo --id 42").action(async (opts) => {
501
544
  const client = getClient();
502
545
  await client.pullRequests.deletePullRequest({
503
546
  projectKey: opts.project,
@@ -510,8 +553,10 @@ function deletePr(parent) {
510
553
 
511
554
  // src/commands/pr/diff.ts
512
555
  import { Option as Option3 } from "commander";
556
+ var WHITESPACE_MODES3 = ["show", "ignore-all"];
557
+ var DIFF_FORMATS2 = ["text", "json"];
513
558
  function diff3(parent) {
514
- parent.command("diff").description("Get diff for a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID").option("--path <path>", "File path (omit for full PR diff)").option("--since <hash>", "Since commit hash").option("--until <hash>", "Until commit hash").option("--context <n>", "Number of context lines around changes (default: 10)", parseInt).addOption(new Option3("--whitespace <mode>", "Whitespace handling (default: show)").choices(["show", "ignore-all"])).addOption(new Option3("--format <fmt>", "Response format (default: text)").choices(["text", "json"])).addHelpText(
559
+ parent.command("diff").description("Get diff for a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).option("--path <path>", "File path (omit for full PR diff)").option("--since <hash>", "Since commit hash").option("--until <hash>", "Until commit hash").option("--context <n>", "Number of context lines around changes (0-500)", intInRange(0, 500), 10).addOption(new Option3("--whitespace <mode>", "Whitespace handling").choices(WHITESPACE_MODES3).default("show")).addOption(new Option3("--format <fmt>", "Response format").choices(DIFF_FORMATS2).default("text")).addHelpText(
515
560
  "after",
516
561
  `
517
562
  Examples:
@@ -525,13 +570,13 @@ Examples:
525
570
  const result = await client.pullRequests.getDiff({
526
571
  projectKey: opts.project,
527
572
  repositorySlug: opts.repo,
528
- pullRequestId: parseInt(opts.id),
573
+ pullRequestId: opts.id,
529
574
  path: opts.path,
530
575
  sinceId: opts.since,
531
576
  untilId: opts.until,
532
577
  contextLines: opts.context,
533
578
  whitespace: opts.whitespace,
534
- format: opts.format || "text"
579
+ format: opts.format
535
580
  });
536
581
  if (typeof result === "string") {
537
582
  process.stdout.write(result);
@@ -544,7 +589,7 @@ Examples:
544
589
 
545
590
  // src/commands/pr/edit-comment.ts
546
591
  function editComment(parent) {
547
- parent.command("edit-comment").description("Edit a pull request comment").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", parseInt).requiredOption("--comment-id <n>", "Comment ID to edit", parseInt).requiredOption("--text <content>", "New comment text").addHelpText(
592
+ parent.command("edit-comment").description("Edit a pull request comment").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).requiredOption("--comment-id <n>", "Comment ID to edit", positiveInt).requiredOption("--text <content>", "New comment text").addHelpText(
548
593
  "after",
549
594
  '\nExamples:\n bitbucketdc pr edit-comment --project PROJ --repo my-repo --id 42 --comment-id 123 --text "Updated text"'
550
595
  ).action(async (opts) => {
@@ -569,7 +614,7 @@ function editComment(parent) {
569
614
 
570
615
  // src/commands/pr/file-comment.ts
571
616
  function fileComment(parent) {
572
- parent.command("file-comment").description("Add a file-level comment to a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID").requiredOption("--text <text>", "Comment text").requiredOption("--path <path>", "File path to attach comment to (e.g., src/main.ts)").addHelpText(
617
+ parent.command("file-comment").description("Add a file-level comment to a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).requiredOption("--text <text>", "Comment text").requiredOption("--path <path>", "File path to attach comment to (e.g., src/main.ts)").addHelpText(
573
618
  "after",
574
619
  '\nExamples:\n bitbucketdc pr file-comment --project PROJ --repo my-repo --id 42 --text "This file needs refactoring" --path src/main.ts'
575
620
  ).action(async (opts) => {
@@ -577,7 +622,7 @@ function fileComment(parent) {
577
622
  const result = await client.pullRequests.addComment({
578
623
  projectKey: opts.project,
579
624
  repositorySlug: opts.repo,
580
- pullRequestId: parseInt(opts.id),
625
+ pullRequestId: opts.id,
581
626
  text: opts.text,
582
627
  path: opts.path
583
628
  });
@@ -587,7 +632,7 @@ function fileComment(parent) {
587
632
 
588
633
  // src/commands/pr/file-diff.ts
589
634
  function fileDiff(parent) {
590
- parent.command("file-diff").description("Get structured line-by-line diff for a specific file").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID").requiredOption("--path <path>", "File path (e.g., src/main.ts)").option("--context <n>", "Number of context lines around changes (default: 10)", parseInt).addHelpText(
635
+ parent.command("file-diff").description("Get structured line-by-line diff for a specific file").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).requiredOption("--path <path>", "File path (e.g., src/main.ts)").option("--context <n>", "Number of context lines around changes (0-500)", intInRange(0, 500), 10).addHelpText(
591
636
  "after",
592
637
  "\nExamples:\n bitbucketdc pr file-diff --project PROJ --repo my-repo --id 42 --path src/main.ts\n bitbucketdc pr file-diff --project PROJ --repo my-repo --id 42 --path src/main.ts --context 5"
593
638
  ).action(async (opts) => {
@@ -595,7 +640,7 @@ function fileDiff(parent) {
595
640
  const result = await client.pullRequests.getFileDiff({
596
641
  projectKey: opts.project,
597
642
  repositorySlug: opts.repo,
598
- pullRequestId: parseInt(opts.id),
643
+ pullRequestId: opts.id,
599
644
  path: opts.path,
600
645
  contextLines: opts.context
601
646
  });
@@ -605,12 +650,12 @@ function fileDiff(parent) {
605
650
 
606
651
  // src/commands/pr/get.ts
607
652
  function get(parent) {
608
- parent.command("get").description("Get pull request details").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID").addHelpText("after", "\nExamples:\n bitbucketdc pr get --project PROJ --repo my-repo --id 42").action(async (opts) => {
653
+ parent.command("get").description("Get pull request details").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).addHelpText("after", "\nExamples:\n bitbucketdc pr get --project PROJ --repo my-repo --id 42").action(async (opts) => {
609
654
  const client = getClient();
610
655
  const result = await client.pullRequests.get({
611
656
  projectKey: opts.project,
612
657
  repositorySlug: opts.repo,
613
- pullRequestId: parseInt(opts.id)
658
+ pullRequestId: opts.id
614
659
  });
615
660
  output(result);
616
661
  });
@@ -618,7 +663,7 @@ function get(parent) {
618
663
 
619
664
  // src/commands/pr/inbox.ts
620
665
  function inbox(parent) {
621
- parent.command("inbox").description("List pull requests in your reviewer inbox").option("--start <n>", "Starting index for pagination (default: 0)", parseInt).option("--limit <n>", "Maximum number of pull requests to return (default: 25)", parseInt).addHelpText(
666
+ parent.command("inbox").description("List pull requests in your reviewer inbox").option("--start <n>", "Starting index for pagination", nonNegativeInt).option("--limit <n>", "Maximum number of pull requests to return (1-1000)", intInRange(1, 1e3), 25).addHelpText(
622
667
  "after",
623
668
  "\nExamples:\n bitbucketdc pr inbox\n bitbucketdc pr inbox --limit 10\n bitbucketdc pr inbox --start 25 --limit 25"
624
669
  ).action(async (opts) => {
@@ -633,11 +678,11 @@ function inbox(parent) {
633
678
 
634
679
  // src/commands/pr/line-comment.ts
635
680
  import { Option as Option4 } from "commander";
681
+ var LINE_TYPES = ["ADDED", "REMOVED", "CONTEXT"];
682
+ var FILE_TYPES = ["FROM", "TO"];
636
683
  function lineComment(parent) {
637
- parent.command("line-comment").description("Add an inline comment to a specific line in a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID").requiredOption("--text <text>", "Comment text").requiredOption("--path <path>", "File path (e.g., src/main.ts)").requiredOption("--line <n>", "Line number to comment on").addOption(
638
- new Option4("--line-type <type>", "Type of line").choices(["ADDED", "REMOVED", "CONTEXT"]).makeOptionMandatory()
639
- ).addOption(
640
- new Option4("--file-type <type>", "Side of diff: FROM (source/old) or TO (destination/new)").choices(["FROM", "TO"]).makeOptionMandatory()
684
+ parent.command("line-comment").description("Add an inline comment to a specific line in a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).requiredOption("--text <text>", "Comment text").requiredOption("--path <path>", "File path (e.g., src/main.ts)").requiredOption("--line <n>", "Line number to comment on", positiveInt).addOption(new Option4("--line-type <type>", "Type of line").choices(LINE_TYPES).makeOptionMandatory()).addOption(
685
+ new Option4("--file-type <type>", "Side of diff: FROM (source/old) or TO (destination/new)").choices(FILE_TYPES).makeOptionMandatory()
641
686
  ).addHelpText(
642
687
  "after",
643
688
  `
@@ -650,10 +695,10 @@ Examples:
650
695
  const result = await client.pullRequests.addComment({
651
696
  projectKey: opts.project,
652
697
  repositorySlug: opts.repo,
653
- pullRequestId: parseInt(opts.id),
698
+ pullRequestId: opts.id,
654
699
  text: opts.text,
655
700
  path: opts.path,
656
- line: parseInt(opts.line),
701
+ line: opts.line,
657
702
  lineType: opts.lineType,
658
703
  fileType: opts.fileType
659
704
  });
@@ -664,7 +709,7 @@ Examples:
664
709
 
665
710
  // src/commands/pr/merge.ts
666
711
  function merge(parent) {
667
- parent.command("merge").description("Merge a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", parseInt).option("--strategy <id>", "Merge strategy (e.g., squash, no-ff, ff, ff-only, rebase-no-ff, rebase-ff-only)").option("--message <text>", "Custom merge commit message").addHelpText(
712
+ parent.command("merge").description("Merge a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).option("--strategy <id>", "Merge strategy (e.g., squash, no-ff, ff, ff-only, rebase-no-ff, rebase-ff-only)").option("--message <text>", "Custom merge commit message").addHelpText(
668
713
  "after",
669
714
  `
670
715
  Examples:
@@ -687,7 +732,7 @@ Examples:
687
732
  // src/commands/pr/reaction-add.ts
688
733
  import { Option as Option5 } from "commander";
689
734
  function reactionAdd(parent) {
690
- parent.command("reaction-add").description("Add an emoticon reaction to a PR comment").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID").requiredOption("--comment-id <n>", "Comment ID").addOption(
735
+ parent.command("reaction-add").description("Add an emoticon reaction to a PR comment").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).requiredOption("--comment-id <n>", "Comment ID", positiveInt).addOption(
691
736
  new Option5("--emoticon <name>", "Emoticon to react with").choices(["thumbsup", "thumbsdown", "heart", "thinking_face", "laughing"]).makeOptionMandatory()
692
737
  ).addHelpText(
693
738
  "after",
@@ -695,48 +740,51 @@ function reactionAdd(parent) {
695
740
  Examples:
696
741
  bitbucketdc pr reaction-add --project PROJ --repo my-repo --id 42 --comment-id 123 --emoticon thumbsup
697
742
  bitbucketdc pr reaction-add --project PROJ --repo my-repo --id 42 --comment-id 123 --emoticon heart`
698
- ).action(async (opts) => {
699
- const client = getClient();
700
- const result = await client.pullRequests.addReaction({
701
- projectKey: opts.project,
702
- repositorySlug: opts.repo,
703
- pullRequestId: parseInt(opts.id),
704
- commentId: parseInt(opts.commentId),
705
- emoticon: opts.emoticon
706
- });
707
- output(result);
708
- });
743
+ ).action(
744
+ async (opts) => {
745
+ const client = getClient();
746
+ const result = await client.pullRequests.addReaction({
747
+ projectKey: opts.project,
748
+ repositorySlug: opts.repo,
749
+ pullRequestId: opts.id,
750
+ commentId: opts.commentId,
751
+ emoticon: opts.emoticon
752
+ });
753
+ output(result);
754
+ }
755
+ );
709
756
  }
710
757
 
711
758
  // src/commands/pr/reaction-remove.ts
712
759
  import { Option as Option6 } from "commander";
713
760
  function reactionRemove(parent) {
714
- parent.command("reaction-remove").description("Remove an emoticon reaction from a PR comment").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID").requiredOption("--comment-id <n>", "Comment ID").addOption(
761
+ parent.command("reaction-remove").description("Remove an emoticon reaction from a PR comment").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).requiredOption("--comment-id <n>", "Comment ID", positiveInt).addOption(
715
762
  new Option6("--emoticon <name>", "Emoticon to remove").choices(["thumbsup", "thumbsdown", "heart", "thinking_face", "laughing"]).makeOptionMandatory()
716
763
  ).addHelpText(
717
764
  "after",
718
765
  `
719
766
  Examples:
720
767
  bitbucketdc pr reaction-remove --project PROJ --repo my-repo --id 42 --comment-id 123 --emoticon thumbsup`
721
- ).action(async (opts) => {
722
- const client = getClient();
723
- await client.pullRequests.removeReaction({
724
- projectKey: opts.project,
725
- repositorySlug: opts.repo,
726
- pullRequestId: parseInt(opts.id),
727
- commentId: parseInt(opts.commentId),
728
- emoticon: opts.emoticon
729
- });
730
- output({ removed: true, emoticon: opts.emoticon });
731
- });
768
+ ).action(
769
+ async (opts) => {
770
+ const client = getClient();
771
+ await client.pullRequests.removeReaction({
772
+ projectKey: opts.project,
773
+ repositorySlug: opts.repo,
774
+ pullRequestId: opts.id,
775
+ commentId: opts.commentId,
776
+ emoticon: opts.emoticon
777
+ });
778
+ output({ removed: true, emoticon: opts.emoticon });
779
+ }
780
+ );
732
781
  }
733
782
 
734
783
  // src/commands/pr/review.ts
735
784
  import { Option as Option7 } from "commander";
785
+ var REVIEW_STATUSES = ["APPROVED", "NEEDS_WORK", "UNAPPROVED"];
736
786
  function review(parent) {
737
- parent.command("review").description("Update review status for a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID").addOption(
738
- new Option7("--status <status>", "Review status").choices(["APPROVED", "NEEDS_WORK", "UNAPPROVED"]).makeOptionMandatory()
739
- ).addHelpText(
787
+ parent.command("review").description("Update review status for a pull request").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).addOption(new Option7("--status <status>", "Review status").choices(REVIEW_STATUSES).makeOptionMandatory()).addHelpText(
740
788
  "after",
741
789
  `
742
790
  Examples:
@@ -748,7 +796,7 @@ Examples:
748
796
  const result = await client.pullRequests.updateReviewStatus({
749
797
  projectKey: opts.project,
750
798
  repositorySlug: opts.repo,
751
- pullRequestId: parseInt(opts.id),
799
+ pullRequestId: opts.id,
752
800
  status: opts.status
753
801
  });
754
802
  output(result);
@@ -757,7 +805,7 @@ Examples:
757
805
 
758
806
  // src/commands/pr/update.ts
759
807
  function update(parent) {
760
- parent.command("update").description("Update a pull request title and/or description").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", parseInt).option("--title <title>", "New pull request title").option("--description <text>", "New pull request description").addHelpText(
808
+ parent.command("update").description("Update a pull request title and/or description").requiredOption("--project <key>", "Bitbucket project key").requiredOption("--repo <slug>", "Repository slug").requiredOption("--id <n>", "Pull request ID", positiveInt).option("--title <title>", "New pull request title").option("--description <text>", "New pull request description").addHelpText(
761
809
  "after",
762
810
  `
763
811
  Examples:
@@ -826,7 +874,7 @@ Examples:
826
874
 
827
875
  // src/commands/project/list.ts
828
876
  function list2(parent) {
829
- parent.command("list").description("List projects").option("--name <name>", "Filter projects by name (partial match)").option("--permission <permission>", "Filter by permission (e.g., PROJECT_READ, PROJECT_WRITE, PROJECT_ADMIN)").option("--start <start>", "Starting index for pagination", parseInt).option("--limit <limit>", "Maximum number of projects to return", parseInt).addHelpText(
877
+ parent.command("list").description("List projects").option("--name <name>", "Filter projects by name (partial match)").option("--permission <permission>", "Filter by permission (e.g., PROJECT_READ, PROJECT_WRITE, PROJECT_ADMIN)").option("--start <start>", "Starting index for pagination", nonNegativeInt).option("--limit <limit>", "Maximum number of projects to return (1-1000)", intInRange(1, 1e3), 25).addHelpText(
830
878
  "after",
831
879
  '\nExamples:\n bitbucketdc project list\n bitbucketdc project list --name "My Project"\n bitbucketdc project list --permission PROJECT_READ --limit 10'
832
880
  ).action(async (opts) => {
@@ -879,20 +927,22 @@ function upload(parent) {
879
927
  ).action(async (opts) => {
880
928
  const client = getClient();
881
929
  const filePaths = opts.files.split(",").map((p) => p.trim()).filter(Boolean);
882
- const results = [];
883
- const failed = [];
884
- for (const filePath of filePaths) {
885
- try {
886
- const result = await client.repositories.uploadAttachment({
887
- projectKey: opts.project,
888
- repositorySlug: opts.repo,
889
- filePath
890
- });
891
- results.push({ id: result.id, url: result.url });
892
- } catch (err) {
893
- failed.push({ filePath, error: err instanceof Error ? err.message : String(err) });
894
- }
895
- }
930
+ const outcomes = await Promise.all(
931
+ filePaths.map(async (filePath) => {
932
+ try {
933
+ const result = await client.repositories.uploadAttachment({
934
+ projectKey: opts.project,
935
+ repositorySlug: opts.repo,
936
+ filePath
937
+ });
938
+ return { ok: true, id: result.id, url: result.url };
939
+ } catch (err) {
940
+ return { ok: false, filePath, error: err instanceof Error ? err.message : String(err) };
941
+ }
942
+ })
943
+ );
944
+ const results = outcomes.filter((o) => o.ok).map(({ id, url }) => ({ id, url }));
945
+ const failed = outcomes.filter((o) => !o.ok).map(({ filePath, error }) => ({ filePath, error }));
896
946
  output({
897
947
  project: opts.project,
898
948
  repo: opts.repo,
@@ -945,8 +995,11 @@ Examples:
945
995
  }
946
996
 
947
997
  // src/commands/repo/list.ts
998
+ import { Option as Option8 } from "commander";
999
+ var VISIBILITIES = ["public", "private"];
1000
+ var ARCHIVE_STATES = ["ACTIVE", "ARCHIVED", "ALL"];
948
1001
  function list3(parent) {
949
- parent.command("list").description("List repositories with optional filters").option("--name <name>", "Filter by repository name (case-insensitive)").option("--project-key <key>", "Filter by project key").option("--project-name <name>", "Filter by project name (case-insensitive)").option("--visibility <type>", "Filter by visibility (public or private)").option("--archived <status>", "Filter by archived status (ACTIVE, ARCHIVED, ALL)").option("--start <n>", "Starting index for pagination (default: 0)", parseInt).option("--limit <n>", "Maximum results to return (default: 25)", parseInt).addHelpText(
1002
+ parent.command("list").description("List repositories with optional filters").option("--name <name>", "Filter by repository name (case-insensitive)").option("--project-key <key>", "Filter by project key").option("--project-name <name>", "Filter by project name (case-insensitive)").addOption(new Option8("--visibility <type>", "Filter by visibility").choices(VISIBILITIES)).addOption(new Option8("--archived <status>", "Filter by archived status").choices(ARCHIVE_STATES)).option("--start <n>", "Starting index for pagination", nonNegativeInt).option("--limit <n>", "Maximum results to return (1-1000)", intInRange(1, 1e3), 25).addHelpText(
950
1003
  "after",
951
1004
  `
952
1005
  Examples:
@@ -1021,10 +1074,20 @@ Examples:
1021
1074
  }
1022
1075
 
1023
1076
  // src/index.ts
1077
+ function readPackageVersion() {
1078
+ try {
1079
+ const here = dirname(fileURLToPath(import.meta.url));
1080
+ const pkgPath = join(here, "..", "package.json");
1081
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
1082
+ return pkg.version ?? "0.0.0";
1083
+ } catch {
1084
+ return "0.0.0";
1085
+ }
1086
+ }
1024
1087
  var DIM = "\x1B[2m";
1025
1088
  var RESET = "\x1B[0m";
1026
- var program = new Command8();
1027
- program.name("bitbucketdc").description("Bitbucket Data Center CLI").version("1.0.0").configureHelp({
1089
+ var program = new Command9();
1090
+ program.name("bitbucketdc").description("Bitbucket Data Center CLI").version(readPackageVersion()).configureHelp({
1028
1091
  styleTitle: (str) => styleText("bold", str),
1029
1092
  styleUsage: (str) => styleText("dim", str),
1030
1093
  styleCommandDescription: (str) => styleText("dim", str),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitbucketdc-cli",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "publish": true,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "dependencies": {
14
14
  "commander": "^13.1.0",
15
- "bitbucket-data-center-client": "1.4.15"
15
+ "bitbucket-data-center-client": "1.4.17"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/node": "24.10.4",
@@ -22,8 +22,8 @@
22
22
  "tsx": "^4.19.2",
23
23
  "typescript": "^5.7.2",
24
24
  "vitest": "^4.0.16",
25
- "config-typescript": "0.0.0",
26
- "config-eslint": "0.0.0"
25
+ "config-eslint": "0.0.0",
26
+ "config-typescript": "0.0.0"
27
27
  },
28
28
  "engines": {
29
29
  "node": ">=22.0.0"