Alokai

AI code review

Set up the Alokai AI reviewer to post inline findings on your pull requests, from your own CI and under your own bot identity.

Introduced in Alokai 2.4.0

Alokai ships an AI code reviewer that posts findings as inline review comments on your pull requests, and can apply the fix for a finding on request. It knows your project's conventions, because it reviews against the same skills and version-matched docs your coding agents use - see AI Coding Support.

The reviewer is the @alokai/cli-plugin-review plugin for the Alokai CLI. You run it from your own CI: GitHub Actions and GitLab CI are both supported, and the setup is the same shape on each - give the bot an identity you own, add a job, set a handful of variables.

The criteria the bot reviews against also ship as skills your coding agent can run, so you can review your own branch before the bot ever sees it, and fix the bot's findings without leaving the agent - see Using it.

Where your code goes

The review runs entirely in your CI, under an identity you own. Alokai hosts no shared bot and no Alokai service reads your repository. The one thing that leaves it is what the reviewer sends to the model: the diff under review and the file context around it, sent to the LLM endpoint Alokai provisions for your project.

Using it

Setup (below) is a one-time job, usually done by one person on the team. This section is for everyone else - the day-to-day of working with the reviewer.

Review before the bot does

The criteria the bot enforces aren't locked inside CI. They also ship as two skills in @alokai/ai-toolkit - alokai-review (review a change and report findings) and autofix (apply accepted findings) - and alokai ai sync links them into your coding agents' skill directories alongside the rest (Syncing skills). Any coding agent that reads skills can run them; the examples on this page use Claude Code.

When you finish a piece of work, run the review on your own branch before opening the pull request:

/alokai-review

or just ask - "review my branch". The agent reviews the same way the CI bot does - same skills, same conventions, same version-matched docs - but reports in the conversation and posts nothing anywhere. What you fix here never shows up as a bot comment, and the CI review on the eventual pull request stays short.

The bot's review also runs from your machine against an open pull or merge request, with the same LLM configuration in your environment:

# Review a merge request (iid and project path, host from the git remote):
yarn alokai ai review --pr 42 --repo my-group/my-project

# Self-managed instance - pass the host explicitly:
yarn alokai ai review --pr 42 --repo my-group/my-project --host gitlab.acme.com

Automatic review

Every non-draft pull or merge request gets reviewed when it's opened and on every push, on gates built into the CI job you'll add in Set it up: bot branches and PRs labelled skip-ai-review are skipped, and a new push cancels the review still running for the previous one.

Skip a single pull request

Add the skip-ai-review label to it. On an open PR the label takes effect from the next push.

Incremental re-reviews

Reviews are incremental by default: when the bot has already reviewed a pull request, the next run covers only what changed since that review, instead of the whole diff. Pushing a fix doesn't bring back findings on code nobody touched, and re-runs stay short.

AI_REVIEW_INCREMENTAL and the --incremental flag pin that behavior explicitly. You don't need to set the variable to get it.

Fix a finding

The reviewer can apply its own findings, committing the change and replying in the thread of the comment it fixed. There are three ways to trigger a fix, in order of convenience.

1. Reply /fix in the pull request. Nothing to run, no checkout needed - the fix runs in CI and pushes to the branch.

With the /fix command workflow in place, the author of a pull request fixes findings from the PR itself:

  • Reply /fix to an inline finding - fixes that one finding.
  • Comment /fix on the pull request - fixes every open finding on it.
  • Add context after the command - /fix use the existing helper instead of a new one - and the bot takes it into account.

The fix is committed and pushed to the PR head branch by Alokai AI Review, which then replies with what it changed. Only the pull request's author can run the command.

With the comment-triggered fix in place, the MR author replies /fix to a finding and the pipeline applies it - the same chatops GitHub gets from the command workflow.

2. Ask your coding agent. In Claude Code:

/autofix https://github.com/acme/store/pull/42

or just "fix the review comments on my PR". The agent reads the review threads, applies the smallest edit per finding, skips stale ones with a stated reason, and asks you before pushing or replying to anything. Unlike the /fix comment, this path has a human in the loop: a finding that needs a judgement call gets asked, not guessed. It runs on the agent's own model, so AI_REVIEW_API_KEY isn't needed here.

3. Run the CLI locally. yarn alokai ai fix from your checkout does exactly what CI does, but needs the LLM configuration in your local environment - reach for it when neither of the above is available:

# Fix every open finding - paste the pull or merge request URL:
yarn alokai ai fix https://gitlab.com/my-group/my-project/-/merge_requests/42

# Fix one finding - copy the URL of its comment:
yarn alokai ai fix https://gitlab.com/my-group/my-project/-/merge_requests/42#note_123

Add --instruction "..." to give the fix extra context.

Turn it off

  • Disable it: leave AI_REVIEW_ENABLED unset, or set it to any value other than true. The job no-ops.
  • Remove it entirely: delete .github/workflows/ai-review.yml, or the ai-review job from .gitlab-ci.yml.

Set it up

What you need

  1. A bot identity you own. Comments are posted by an account you control, so the reviewer never speaks as a shared third party. On GitHub that's a GitHub App registered under your organization; on GitLab it's a project access token, which acts as its own bot user. Name it Alokai AI Review either way - the name is what shows on every comment it posts.
  2. An LLM API key. Provisioned by Alokai - contact your Alokai representative to get the key for your project, and do not commit it into the repository. The endpoint and model come with sensible defaults, so most projects need nothing else; override them only if your representative gives you project-specific values.

Install the plugin

Add the plugin as a dev dependency, so CI picks it up with the rest of your install:

yarn add -D @alokai/cli-plugin-review

The criteria the reviewer applies come from @alokai/ai-toolkit, synced into the checkout by alokai ai sync. Generated Alokai storefronts have the toolkit in devDependencies and run sync on postinstall, so a normal yarn install in CI is enough. If your CI installs with lifecycle scripts disabled, add yarn alokai ai sync as an explicit step - see Syncing skills.

Configuration

Every provider reads the same variables. The API key is the only one you have to set - the rest have defaults that suit most projects. Set them in your CI platform, not in the repository, and mark the API key as a secret (masked) variable.

NameSecretValue
AI_REVIEW_ENABLEDtrue to turn the review on. Anything else (including unset) makes the job no-op.
AI_REVIEW_API_KEYThe LLM API key provided by Alokai.
AI_REVIEW_BASE_URL(optional) An OpenAI-compatible LLM endpoint, when Alokai provisioned a project-specific one. Unset uses the default.
AI_REVIEW_MODEL(optional) A model name, when Alokai gave you one. Unset uses the default.
AI_REVIEW_INCREMENTAL(optional) Incremental review is already the default - see Incremental re-reviews.
AI_REVIEW_PROVIDER(optional) github or gitlab, when the host can't be recognized - see Provider detection.

Each platform adds the variables for its own bot identity on top of these; they're listed in the setup steps below.

On GitHub the reviewer runs as a turnkey Actions workflow.

Register a GitHub App

Register a GitHub App under your own organization - you own it, and it is not shared with Alokai or other customers. The app's name is the bot's identity on every review comment, so brand it Alokai AI Review.

Register the app by hand with these repository permissions:

  • Contents: Read-only - read the code to review.
  • Issues: Read & write - post and update review comments.
  • Pull requests: Read & write - post inline review comments and replies.

Generate a private key for the app and note its App ID - you'll need both in step 3. Finally, install the app on the repositories that should get AI review (Settings → GitHub Apps → Install).

Setting up many repositories at once

A manifest pre-fills the name and permissions and returns the App ID and private key in one exchange, which is worth the extra machinery if you're scripting this. See Registering from a manifest.

Add the workflow

Create .github/workflows/ai-review.yml with the following content:

name: "AI Review"

# Alokai AI code review. Disabled by default: set the repo variable
# AI_REVIEW_ENABLED to 'true' after completing the setup.
on:
  pull_request:
    types: [opened, synchronize, reopened, ready_for_review]

concurrency:
  group: "ai-review-${{ github.event.pull_request.number }}"
  cancel-in-progress: true

# Branches that should not be reviewed (automated/bot PRs).
# SKIP_BRANCHES: comma-separated head-branch prefixes, e.g. "dependabot/,renovate/".
env:
  SKIP_BRANCHES: "dependabot/"

defaults:
  run:
    shell: "bash"

jobs:
  review:
    name: "AI Review"
    runs-on: "ubuntu-latest"
    if: >-
      ${{
        vars.AI_REVIEW_ENABLED == 'true'
        && github.event.pull_request.draft == false
        && !contains(github.event.pull_request.labels.*.name, 'skip-ai-review')
      }}
    timeout-minutes: 30
    permissions:
      contents: "read"
      id-token: "write"
      issues: "write"
      pull-requests: "write"
    steps:
      - name: "Mint identity token"
        id: "mint_identity_token"
        if: |-
          ${{ vars.AI_REVIEW_APP_ID }}
        uses: "actions/create-github-app-token@v2"
        with:
          app-id: "${{ vars.AI_REVIEW_APP_ID }}"
          private-key: "${{ secrets.AI_REVIEW_APP_PRIVATE_KEY }}"
          permission-contents: "read"
          permission-issues: "write"
          permission-pull-requests: "write"

      - name: "Checkout repository"
        uses: "actions/checkout@v5"
        with:
          fetch-depth: 0

      - name: "Check if review should run"
        id: "gate"
        env:
          HEAD_REF: "${{ github.head_ref }}"
        run: |
          HEAD="${HEAD_REF}"

          IFS=',' read -ra PATTERNS <<< "${SKIP_BRANCHES}"
          for pattern in "${PATTERNS[@]}"; do
            [[ -z "${pattern}" ]] && continue
            if [[ "${HEAD}" == ${pattern}* ]]; then
              echo "⏭ Skipping review: branch '${HEAD}' matches skip pattern '${pattern}'"
              echo "skip=true" >> "${GITHUB_OUTPUT}"
              exit 0
            fi
          done

          echo "✓ Branch '${HEAD}' is eligible for review"
          echo "skip=false" >> "${GITHUB_OUTPUT}"

      - name: "Install dependencies"
        if: ${{ steps.gate.outputs.skip != 'true' }}
        uses: "./.github/actions/setup"
        with:
          npm_user: "${{ vars.NPM_USER }}"
          npm_password: "${{ secrets.NPM_PASS }}"
          npm_email: "${{ vars.NPM_EMAIL }}"
          npm_registry_url: "${{ vars.NPM_REGISTRY_URL }}"

      - name: "Install review plugin into Alokai CLI"
        if: ${{ steps.gate.outputs.skip != 'true' }}
        run: yarn alokai plugins link node_modules/@alokai/cli-plugin-review

      - name: "🔄 Sync @alokai/ai-toolkit skills"
        if: ${{ steps.gate.outputs.skip != 'true' }}
        run: yarn alokai ai sync --yes

      - name: "🚀 Run AI review"
        if: ${{ steps.gate.outputs.skip != 'true' }}
        env:
          GITHUB_TOKEN: "${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}"
          GITHUB_REPOSITORY: "${{ github.repository }}"
          PR_NUMBER: "${{ github.event.pull_request.number }}"
          AI_REVIEW_API_KEY: "${{ secrets.AI_REVIEW_API_KEY }}"
          AI_REVIEW_BASE_URL: "${{ vars.AI_REVIEW_BASE_URL }}"
          AI_REVIEW_MODEL: "${{ vars.AI_REVIEW_MODEL }}"
          # Incremental review is the default; this pins it explicitly.
          AI_REVIEW_INCREMENTAL: "${{ vars.AI_REVIEW_INCREMENTAL }}"
        run: |
          # Flags are added only when their variable is set, so unset vars fall
          # back to the plugin defaults instead of overriding them with "".
          ARGS=()
          if [[ -n "${AI_REVIEW_BASE_URL}" ]]; then ARGS+=(--base-url "${AI_REVIEW_BASE_URL}"); fi
          if [[ -n "${AI_REVIEW_MODEL}" ]]; then ARGS+=(--model "${AI_REVIEW_MODEL}"); fi
          if [[ "${AI_REVIEW_INCREMENTAL}" == "true" ]]; then ARGS+=(--incremental); fi
          yarn alokai ai review "${ARGS[@]}"

The "Install dependencies" step uses the ./.github/actions/setup composite action that ships with generated Alokai storefronts (npm registry auth + yarn install). If your repository doesn't have it, replace that step with your own Node setup and yarn install.

Add variables and secrets

In Settings → Secrets and variables → Actions, add everything from Configuration - AI_REVIEW_API_KEY as a secret, the rest as variables - plus the two that identify your GitHub App:

NameKindValue
AI_REVIEW_APP_IDVariableYour GitHub App's ID.
AI_REVIEW_APP_PRIVATE_KEYSecretYour GitHub App's private key (.pem contents).

Leave AI_REVIEW_ENABLED unset for now.

Enable it and check the first run

Set AI_REVIEW_ENABLED to true, then open a pull request (or push to an open one) and watch the Actions tab:

  • No "AI Review" run at all - AI_REVIEW_ENABLED isn't true, the PR is a draft, or it carries the skip-ai-review label.
  • The run ends at "Check if review should run" - the head branch matched a SKIP_BRANCHES prefix. The step log names the pattern it matched.
  • The run completes - findings appear as inline comments within a minute or two, posted by Alokai AI Review.

If the run fails, see Troubleshooting.

Fixing findings from a pull request comment

The reviewer can also apply its own findings. Add a second workflow, and the PR author fixes a finding by replying /fix to it - no local checkout, no CLI:

name: "AI Review Command"

# Chatops for the AI review: the PR author comments `/fix [extra context]` to
# have the bot apply fixes. A `/fix` reply on an inline finding fixes that one
# finding; a top-level `/fix` comment fixes every open finding on the PR. The
# fix is committed and pushed to the PR head branch.
on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]

concurrency:
  group: "ai-review-command-${{ github.event.issue.number || github.event.pull_request.number }}"
  cancel-in-progress: false

defaults:
  run:
    shell: "bash"

jobs:
  fix:
    name: "AI Fix"
    runs-on: "ubuntu-latest"
    # Cheap pre-filter, tightened to an exact `/fix` token in the resolve step:
    # a PR comment, never the bot's own (so a fix reply can't re-trigger a fix),
    # and only from the PR author. The inactive side of `||` is null and never
    # matches.
    if: >-
      ${{
        github.event.comment.user.type != 'Bot'
        && github.event.comment.user.login == (github.event.issue.user.login || github.event.pull_request.user.login)
        && (
          (github.event_name == 'issue_comment'
            && github.event.issue.pull_request != null
            && startsWith(github.event.comment.body, '/fix'))
          || (github.event_name == 'pull_request_review_comment'
            && startsWith(github.event.comment.body, '/fix'))
        )
      }}
    timeout-minutes: 30
    permissions:
      contents: "write"
      id-token: "write"
      issues: "write"
      pull-requests: "write"
    steps:
      - name: "Mint identity token"
        id: "mint_identity_token"
        if: |-
          ${{ vars.AI_REVIEW_APP_ID }}
        uses: "actions/create-github-app-token@v2"
        with:
          app-id: "${{ vars.AI_REVIEW_APP_ID }}"
          private-key: "${{ secrets.AI_REVIEW_APP_PRIVATE_KEY }}"
          permission-contents: "write"
          permission-issues: "write"
          permission-pull-requests: "write"

      - name: "Resolve trigger"
        id: "trigger"
        env:
          GH_TOKEN: "${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}"
          # Comment body via env, never interpolated into the script, to avoid command injection.
          COMMENT_BODY: "${{ github.event.comment.body }}"
          EVENT_NAME: "${{ github.event_name }}"
          ISSUE_PR_NUMBER: "${{ github.event.issue.number }}"
          REVIEW_HEAD_REF: "${{ github.event.pull_request.head.ref }}"
        run: |
          set -euo pipefail

          # `/fix` must be the exact first token, so `/fixup` does not trigger a fix.
          first_line="$(printf '%s' "${COMMENT_BODY}" | head -n1)"
          if [[ ! "${first_line}" =~ ^/fix([[:space:]]|$) ]]; then
            echo "Comment does not start with the /fix command token; skipping."
            echo "match=false" >> "${GITHUB_OUTPUT}"
            exit 0
          fi
          echo "match=true" >> "${GITHUB_OUTPUT}"

          # An inline-comment event carries the PR head ref; a conversation
          # comment does not, so resolve it.
          if [[ "${EVENT_NAME}" == "pull_request_review_comment" ]]; then
            head_ref="${REVIEW_HEAD_REF}"
          else
            head_ref="$(gh pr view "${ISSUE_PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json headRefName -q .headRefName)"
          fi
          echo "head_ref=${head_ref}" >> "${GITHUB_OUTPUT}"

          # Everything after the leading `/fix` is extra context for the fix.
          instruction="$(printf '%s' "${COMMENT_BODY}" | sed -E '1 s|^[[:space:]]*/fix[[:space:]]*||')"
          {
            echo "INSTRUCTION<<__FIX_EOF__"
            echo "${instruction}"
            echo "__FIX_EOF__"
          } >> "${GITHUB_ENV}"

      - name: "Checkout PR head branch"
        if: ${{ steps.trigger.outputs.match == 'true' }}
        uses: "actions/checkout@v5"
        with:
          ref: "${{ steps.trigger.outputs.head_ref }}"
          fetch-depth: 0
          token: "${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}"

      - name: "Install dependencies"
        if: ${{ steps.trigger.outputs.match == 'true' }}
        uses: "./.github/actions/setup"
        with:
          npm_user: "${{ vars.NPM_USER }}"
          npm_password: "${{ secrets.NPM_PASS }}"
          npm_email: "${{ vars.NPM_EMAIL }}"
          npm_registry_url: "${{ vars.NPM_REGISTRY_URL }}"

      - name: "Install review plugin into Alokai CLI"
        if: ${{ steps.trigger.outputs.match == 'true' }}
        run: yarn alokai plugins link node_modules/@alokai/cli-plugin-review

      - name: "🔄 Sync @alokai/ai-toolkit skills"
        if: ${{ steps.trigger.outputs.match == 'true' }}
        run: yarn alokai ai sync --yes

      - name: "Configure git identity"
        if: ${{ steps.trigger.outputs.match == 'true' }}
        run: |
          git config user.name "Alokai AI Review"
          git config user.email "alokai-ai-review@users.noreply.github.com"

      - name: "🛠 Run AI fix"
        if: ${{ steps.trigger.outputs.match == 'true' }}
        env:
          GITHUB_TOKEN: "${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}"
          GITHUB_REPOSITORY: "${{ github.repository }}"
          AI_REVIEW_API_KEY: "${{ secrets.AI_REVIEW_API_KEY }}"
          AI_REVIEW_BASE_URL: "${{ vars.AI_REVIEW_BASE_URL }}"
          AI_REVIEW_MODEL: "${{ vars.AI_REVIEW_MODEL }}"
        run: |
          yarn alokai ai fix \
            "${{ github.event.comment.html_url }}" \
            --instruction "${INSTRUCTION}"

This workflow needs no variables beyond the ones you already set - but note it grants Contents: write, because it commits to the PR branch. Two guards keep that narrow: only the pull request's own author can trigger a fix, and the bot's own comments are ignored so a fix reply can't trigger another fix.

Unlike the review workflow, this one has no AI_REVIEW_ENABLED gate: it only ever runs when someone types the command.

Registering from a manifest

A manifest pre-fills the app's name and permissions and hands back its credentials, which saves time when you're setting up several organizations or scripting the process. This is the manifest the app needs:

{
  "name": "Alokai AI Review",
  "url": "https://alokai.com",
  "public": false,
  "default_permissions": {
    "contents": "read",
    "issues": "write",
    "pull_requests": "write"
  },
  "redirect_url": "https://alokai.com"
}

GitHub only accepts a manifest as a form POST, so save it as the manifest field of a form, replace YOUR_ORG, open the file in a browser, and submit:

<form
  action="https://github.com/organizations/YOUR_ORG/settings/apps/new"
  method="post"
>
  <input
    type="hidden"
    name="manifest"
    value='{"name":"Alokai AI Review","url":"https://alokai.com","public":false,"default_permissions":{"contents":"read","issues":"write","pull_requests":"write"},"redirect_url":"https://alokai.com"}'
  />
  <button>Register the AI Review GitHub App</button>
</form>

After you confirm the registration, GitHub redirects to the redirect_url with a temporary code query parameter. Exchange it within one hour for the app's credentials:

gh api -X POST /app-manifests/<code>/conversions

The response contains the id (your App ID) and pem (the private key). Continue from step 3 above.

On GitLab there is no dedicated CI integration - the plugin doesn't read GitLab's CI environment on its own - so a merge request pipeline passes GitLab's predefined variables to the CLI as flags. The job below mirrors the GitHub workflow, including all of its gates.

Create a project access token

Create a project access token with the api scope in Settings → Access tokens. A project access token acts as a dedicated bot user, so comments are posted under a stable identity you own - the same property the GitHub App provides on the other platform.

Name the token Alokai AI Review: the token's name becomes the bot user's display name on every comment. On a self-managed instance an administrator can also give that user an avatar (Admin area → Users → edit the bot user) to complete the branding.

CI_JOB_TOKEN cannot post review comments - the job needs the project access token.

Add the pipeline job

Add the job to .gitlab-ci.yml:

ai-review:
  image: node:22
  # With "Auto-cancel redundant pipelines" (on by default), a new push
  # cancels the in-flight review - same as the GitHub workflow's concurrency group.
  interruptible: true
  rules:
    - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
      when: never
    - if: '$AI_REVIEW_ENABLED != "true"'
      when: never
    - if: "$CI_MERGE_REQUEST_TITLE =~ /^Draft:/"
      when: never
    # Skip a single MR by adding the skip-ai-review label.
    - if: "$CI_MERGE_REQUEST_LABELS =~ /(^|,)skip-ai-review(,|$)/"
      when: never
    # Automated/bot MRs - adjust the prefixes to the bots you use.
    - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^(dependabot|renovate)\//'
      when: never
    - when: on_success
  script:
    # Authenticate to the Alokai npm registry. Skip these two lines if your
    # CI setup already writes the registry auth before yarn install.
    - npx -y npm-cli-login -u "$NPM_USER" -p "$NPM_PASSWORD" -e "$NPM_EMAIL" -r https://npm.alokai.cloud --config-path .npmrc
    - echo '@alokai:registry=https://npm.alokai.cloud' >> .npmrc
    - yarn install --frozen-lockfile
    - yarn alokai plugins link node_modules/@alokai/cli-plugin-review
    - yarn alokai ai sync
    - |
      ARGS=(--pr "${CI_MERGE_REQUEST_IID}" --repo "${CI_PROJECT_PATH}" --host "${CI_SERVER_URL#*://}")
      if [[ -n "${AI_REVIEW_BASE_URL}" ]]; then ARGS+=(--base-url "${AI_REVIEW_BASE_URL}"); fi
      if [[ -n "${AI_REVIEW_MODEL}" ]]; then ARGS+=(--model "${AI_REVIEW_MODEL}"); fi
      if [[ "${AI_REVIEW_INCREMENTAL}" == "true" ]]; then ARGS+=(--incremental); fi
      yarn alokai ai review "${ARGS[@]}"

--host "${CI_SERVER_URL#*://}" - the server URL with its scheme stripped - keeps a nonstandard port, which CI_SERVER_HOST alone would drop.

Add CI/CD variables

In Settings → CI/CD → Variables, add everything from Configuration - AI_REVIEW_API_KEY masked - plus:

NameKindValue
GITLAB_TOKENMaskedThe project access token. Comments are posted under its bot user.
NPM_USER, NPM_PASSWORD, NPM_EMAILNPM_PASSWORD maskedAlokai npm registry credentials for the auth step in the job.

The CLI reads GITLAB_TOKEN from the environment under exactly that name, so CI/CD variables reach it without ever putting the token on the command line.

Leave AI_REVIEW_ENABLED unset for now.

Enable it and check the first run

Set AI_REVIEW_ENABLED to true, then open a merge request (or push to an open one) and check its Pipelines tab:

  • No ai-review job at all - one of the rules matched: AI_REVIEW_ENABLED isn't true, the MR title starts with Draft:, it carries the skip-ai-review label, or the source branch matches a bot prefix.
  • The job runs - findings appear as merge request discussions within a minute or two, posted by the Alokai AI Review bot user.

If the job fails, see Troubleshooting.

Fixing findings from a merge request comment

On GitHub the /fix command is a second turnkey workflow, because Actions can trigger on comments. GitLab pipelines can't - no .gitlab-ci.yml rule matches a comment event - so the same chatops takes one piece of glue you host: a webhook receiver that turns a /fix note into a pipeline trigger. Because the fix pushes to the MR source branch, the project access token also needs the write_repository scope on top of api - add it before wiring anything below. The shape:

  1. The MR author replies /fix to a finding. A project webhook (Settings → Webhooks, subscribed to Comments events) calls your receiver.
  2. The receiver filters the note and calls GitLab's pipeline trigger API with the comment's URL as a variable.
  3. An ai-fix job in .gitlab-ci.yml picks up the variable and runs yarn alokai ai fix.

The receiver is a few lines on whatever you already host functions with. In pseudo-code:

on POST /ai-fix-webhook(event):
  require header X-Gitlab-Token == WEBHOOK_SECRET        # the secret you set on the webhook
  require event.object_kind == "note" and event.merge_request exists
  require first token of event.object_attributes.note == "/fix"
  # Author-only, same as the GitHub workflow - and since the bot is never
  # the MR author, its own replies can't re-trigger a fix.
  require event.object_attributes.author_id == event.merge_request.author_id

  POST {CI_SERVER_URL}/api/v4/projects/{event.project.id}/trigger/pipeline
    token = TRIGGER_TOKEN                                # Settings → CI/CD → Pipeline trigger tokens
    ref   = event.merge_request.source_branch
    variables[AI_FIX_COMMENT_URL] = event.object_attributes.url
    variables[AI_FIX_INSTRUCTION] = note text after "/fix"

And the job:

ai-fix:
  image: node:22
  rules:
    - if: '$CI_PIPELINE_SOURCE == "trigger" && $AI_FIX_COMMENT_URL'
  script:
    - npx -y npm-cli-login -u "$NPM_USER" -p "$NPM_PASSWORD" -e "$NPM_EMAIL" -r https://npm.alokai.cloud --config-path .npmrc
    - echo '@alokai:registry=https://npm.alokai.cloud' >> .npmrc
    - yarn install --frozen-lockfile
    - yarn alokai plugins link node_modules/@alokai/cli-plugin-review
    - yarn alokai ai sync
    - git config user.name "Alokai AI Review"
    - git config user.email "alokai-ai-review@noreply.${CI_SERVER_HOST}"
    # The fix commits and pushes to the MR source branch, which CI_JOB_TOKEN
    # can't do - point the remote at the project access token instead.
    - git remote set-url origin "https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
    - git checkout -B "${CI_COMMIT_REF_NAME}"
    - |
      ARGS=("${AI_FIX_COMMENT_URL}")
      if [[ -n "${AI_FIX_INSTRUCTION}" ]]; then ARGS+=(--instruction "${AI_FIX_INSTRUCTION}"); fi
      if [[ -n "${AI_REVIEW_BASE_URL}" ]]; then ARGS+=(--base-url "${AI_REVIEW_BASE_URL}"); fi
      if [[ -n "${AI_REVIEW_MODEL}" ]]; then ARGS+=(--model "${AI_REVIEW_MODEL}"); fi
      yarn alokai ai fix "${ARGS[@]}"

The URL contract is the CLI's: a note URL fixes that one finding, the MR's own URL (pass event.merge_request.url instead) fixes every open finding on it.

Like the GitHub command workflow, this path has no AI_REVIEW_ENABLED gate - it only runs when the author types /fix.

Provider detection

The CLI picks the provider from your git remote's host: gitlab.com, and self-managed instances whose hostname contains gitlab, are recognized as GitLab. When the hostname identifies neither platform (for example git.acme.com), name it explicitly - pass --provider gitlab, or set AI_REVIEW_PROVIDER=gitlab.

On this page