@vinciworks-devs/bitbucket-pr
v0.13.2
Published
Deterministic Bitbucket PR CLI for coding agents — one JSON object on stdout, secure token storage via the git credential store.
Readme
@vinciworks-devs/bitbucket-pr
Deterministic Bitbucket PR CLI for coding agents. Zero dependencies (Node >= 18).
Every command prints exactly one JSON object to stdout — callers branch on
status / code, never on prose. Exit 0 on success or benign no-op
(PR_ALREADY_EXISTS), exit 1 with {"status":"ERROR","code":...} otherwise.
Everything human-readable goes to stderr.
Install
npm i -g @vinciworks-devs/bitbucket-prAuth (once, in YOUR terminal — never through an agent)
bitbucket-pr setupStores a scoped Bitbucket API token (read:pullrequest:bitbucket +
write:pullrequest:bitbucket) in your OS keychain via the git credential
store, under host api.bitbucket.org. The token is validated against
Bitbucket before and after storing, and setup refuses plaintext credential
helpers. App passwords are EOL (2026-07-28) — use API tokens.
CI / agents can instead set BITBUCKET_EMAIL + BITBUCKET_API_TOKEN env vars.
Usage
REMOTE = [email protected]:ws/repo.git (or [email protected]:…) | https://bitbucket.org/ws/repo |
ws/repo | a git remote name of the current repo (origin, upstream, …).
Omit it to use the origin remote of the current directory. A bare repo name
(mod-editagent) is not accepted — the workspace can't be guessed.
Flags take --flag value or --flag=value.
bitbucket-pr create $REMOTE --from=$FROM --to=$TO --title "feat: thing" --body "..."
# → {"status":"CREATED","id":42,"url":"https://bitbucket.org/...","from":"...","to":"..."}
# re-run → {"status":"PR_ALREADY_EXISTS",...} exit 0 — idempotentcreateis pure API: it does not push — push the branch first.- Cut the branch from the PR target (or rebase onto it) before
create: the PR diff shows every commit not on the target, so a branch cut frommastertargetingdevelopmentdrags foreign trunk commits into the diff. --toomitted → the repo's main branch, from the remote'sHEADsymref.--to-dev→ the repo's dev branch:developordevelopment(developwins if both exist).- Both resolve via
git ls-remote— your git transport auth, no extra API token scope — and cache 24h per remote in the user cache dir (BITBUCKET_PR_CACHE_DIRoverrides the location). Reading either from the API would needread:repository:bitbucket, which the token below deliberately does not carry. Ifls-remotecannot reach the remote,--toomitted falls back to the API lookup, which 403s without that scope — pass--to <branch>to skip resolution entirely. --title-file/--body-file/--text-fileread the value from a file.- Refuses to open a PR from
main/master/develop(PROTECTED_BRANCH).
bitbucket-pr comment $REMOTE --pr 42 --text "Paired KB PR: ..."
bitbucket-pr comments $REMOTE --pr 42 # every comment, oldest first, all pages
# → {"status":"LISTED","pr":42,"comments":[{"id":7,"author":"Ann","created":...,
# "file":"src/a.js","line":12,"replyTo":null,"resolved":false,"text":"..."}]}
# file/line null for a general comment; deleted comments left out
bitbucket-pr decline $REMOTE --pr 42 --text "Replaced by #57" # --text optional
# → {"status":"DECLINED","id":42,"url":...,"commentId":7}
# the text is posted as a comment after the decline (a refused decline posts none); ALREADY_DECLINED (exit 0)
# writes nothing; NOT_OPEN refuses a merged or superseded PR, writing nothing
bitbucket-pr edit $REMOTE --pr 42 --body-file DESCRIPTION.md # and/or --title
# → {"status":"EDITED","id":42,"url":...,"fields":["description"]}
# markdown in; the field you don't pass is kept, reviewers are preserved
bitbucket-pr reviewers $REMOTE --pr 42 --add "Roi Avram,Jonah" --remove Rafi
# → {"status":"REVIEWERS_SET","id":42,"url":...,"reviewers":["Roi Avram","Jonah Eisenberg"]}
# names: full name, a unique first name, or {uuid}; looked up among recent PRs' people
# UNCHANGED when the list would not change; UNKNOWN_USER / AMBIGUOUS_USER (+ "candidates")
# / AUTHOR_CANNOT_REVIEW change nothing
bitbucket-pr pr $REMOTE --pr 42 # merge status by PR id
bitbucket-pr pr $REMOTE --pr 42 --full # + "description" (raw markdown, to edit and send back),
# "heads" (source commits pushed, newest first)
# and "commentTexts" (each comment's raw text)
bitbucket-pr pr $REMOTE --from=$FROM # newest PR from that branch, any state
bitbucket-pr pr $REMOTE --from=$FROM --all # → {"status":"LISTED","prs":[{"id":42,"state":"OPEN"},...]}
# → {"status":"MERGED","id":42,"title":...,"from":...,"to":...,"url":...,"mergeCommit":"abc1234",
# "approvedBy":["Ann"],"changesRequestedBy":[],"comments":3,"openTasks":0,
# "builds":[{"name":"UI build","state":"SUCCESSFUL"}]}
# status = OPEN | MERGED | DECLINED | SUPERSEDED; NO_PR if the branch has none
# builds = the PR's commit statuses; [] when CI doesn't report back to Bitbucket
bitbucket-pr merge $REMOTE --pr 42 --to development --head $(git rev-parse HEAD)
# → {"status":"MERGED","id":42,"url":...,"mergeCommit":"abc1234"}
# merge commit. Deletes the source branch unless it's main, master, develop,
# development or staging (create ticks "close source branch" the same way).
# Refuses first, merging nothing:
# WRONG_DESTINATION (not --to), HEAD_MOVED (PR not at --head),
# CHANGES_REQUESTED (someone asked for changes).
# A project with its own merge policy enforces it in front of this command.
# A slow merge (HTTP 202) is polled for up to a minute, then MERGE_PENDING:
# the merge may still finish. A Bitbucket refusal (conflict, failed merge
# check, no permission) is an ERROR with its reason
# MERGED_UNCHECKED_HEAD (exit 1): merged, but at a commit other than --head,
# pushed between the check and the merge; review it and revert if needed
bitbucket-pr status # local check, no network: READY | SETUP_REQUIRED
bitbucket-pr doctor $REMOTE # end-to-end: credential → API auth → repo access
bitbucket-pr helpError codes
BAD_ARGS, NO_REMOTE, NO_PR, PROTECTED_BRANCH, AUTH_REQUIRED,
TOKEN_REJECTED, REPO_ACCESS, API_ERROR, NO_DEST, NOT_A_TTY,
INSECURE_CREDENTIAL_HELPER, NO_CREDENTIAL_HELPER, STORE_FAILED,
MERGE_PENDING, WRONG_DESTINATION, HEAD_MOVED, CHANGES_REQUESTED,
MERGED_UNCHECKED_HEAD, NOT_OPEN, UNEXPECTED.
Development
npm test