commands Permission-checked, so a drive-by comment cannot act

conversations

It answers to its name

A reviewer that cannot be argued with is a linter. Mention @buddy in the thread to re-run the review, ask why a finding is a finding, resolve what it opened, or tell it to be quiet on this one.

@buddy review        new changes since last time
@buddy full-review   re-read the whole diff
@buddy summary       a summary, no inline notes
@buddy resolve       resolve the threads it opened
@buddy pause         stop reviewing this PR
@buddy resume        start again
@buddy fix-ci        diagnose the failing checks
@buddy plan          turn an issue into a plan
@buddy rebase        rebase a dependency update
@buddy merge         re-check auto-merge now
@buddy remember ...  keep a note for future runs
@buddy help          show the table

...anything else is treated as a question,
and answered in the thread.
🔁

Re-review on demand

@buddy review reads only what changed since the last run. @buddy full-review starts over, for when the branch has moved far enough that incremental is the wrong answer.

Ask it anything

Anything that is not a command is treated as a question about this diff, and answered in the thread with the code in front of it.

🔕

Quiet when you need it

@buddy pause stops the reviewer on a noisy pull request without disabling it repository-wide. @buddy resume starts it again.

🧹

Cleans up after itself

@buddy resolve closes the threads Buddy opened — not the ones your teammates did — so a fixed review does not leave a page of stale conversation.

🧠

Remembers corrections

@buddy remember we pin all actions to a SHA writes the note down. Later runs read it back, so telling it once is enough.

🔐

Permission-checked

A command is dispatched only after the commenter's write access is verified. On a public repository a contributor without write access lands in restricted mode, which draws from read-only tools — so the worst a hostile comment can do is ask Buddy to read something it can already read.

How it is wired

One workflow, triggered by comments:

name: Buddy Comments
on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]

jobs:
  respond:
    if: contains(github.event.comment.body, '@buddy')
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
      checks: write
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bunx @buddysh/buddy handle-comment
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

buddy setup generates this for you. The command reads the event payload from the environment, so there is nothing to wire by hand.

Issues answer too

buddy handle-issue posts Buddy's quick-links on a new issue — a set of checkboxes that turn an issue into an action without anyone having to remember the command names. Tick "plan this" and Buddy replies with an implementation plan; the plan mode draws only from read tools, so it can propose work it cannot perform.

Permissions

Buddy classifies the actor before it classifies the request:

ActorModeCan it change the repository?
Write access or aboveThe mode the command asks forYes, where the command is a writing one
No write accessrestrictedNo — read tools only

Public repositories accept input from anyone, so the actor is gated exactly as tightly as the task. An unknown mode name raises an error rather than falling back to a permissive default, because silently landing in implement on a typo would be the worst possible failure.

Third-party text — the pull request body, the comment itself, branch content — never enters the system prompt. It arrives as tool output wrapped in an untrusted-content marker, with the closing sequence escaped inside the payload so the text cannot break out of its own quotation. Details in the agent runtime.

AI code review · CI repair · Finishing touches · The agent runtime