Skip to content

Repository files navigation

mendbot

License: MIT Python 3.14+

An AI-powered pipeline that turns a Jira bug ticket into a reviewed, ready-to-merge pull request.

What it does

A Slack bot watches for Jira ticket links. When one appears, it fetches the ticket and its Sentry error context, asks Claude to diagnose the root cause, and posts the diagnosis back to the thread for a human to approve. On approval it applies the fix in an isolated git worktree, verifies it (lint / type-check), runs a multi-model code review, and opens a pull request — posting the PR link back to the same thread.

Input: a Jira ticket URL. Output: an open pull request, plus a full Markdown audit trail of every stage.

Architecture

Single Python process. The Slack bot runs in the main thread; each pipeline run executes in its own worker thread. The bot never calls AI APIs directly — every AI stage (diagnosis, fix, review) shells out to the Claude Code CLI as a subprocess, so each stage gets full read/write codebase access without the orchestrator managing context windows or token accounting itself.

graph TD
    Slack[Slack Bot] --> Orchestrator[Pipeline Orchestrator]
    Orchestrator --> Jira[jira CLI]
    Orchestrator --> Sentry[sentry CLI]
    Orchestrator --> Agent[AgentClient]
    Agent --> Claude[Claude Code CLI]
    Orchestrator --> Git[git / gh CLI]
    Agent -. optional base_url .-> LiteLLM[LiteLLM proxy]
    LiteLLM -.-> AnyModel[Any OpenAI-compatible model]
Loading

AgentClient() talks to Anthropic directly. AgentClient(base_url="http://127.0.0.1:4444") routes the same Claude Code CLI subprocess through a local LiteLLM proxy instead, so a non-Anthropic model can answer while the CLI keeps its full agentic capability (file reads, grep, following imports). This is what powers the optional secondary reviewer (see Multi-model review).

Pipeline flow

The orchestrator runs 10 ordered stages per ticket:

flowchart TD
    A[Resolve repo] --> B[Resolve branch]
    B --> C[Fetch Sentry context]
    C --> D[Diagnose root cause]
    D --> E{Approve in Slack thread}
    E -->|fix| F[Apply fix in git worktree]
    E -->|skip| Z[Stop]
    F --> G["Verify (lint / tsc)"]
    G --> H["Review (multi-model)"]
    H --> I[Open PR]
    I --> J[Cleanup worktree]
Loading
  1. Resolve repo — map the Sentry project to a GitHub repo (config, keyword match, or ask)
  2. Resolve branch — map the Jira fix version to a base branch (config, or ask)
  3. Sentry fetch — pull the issue and latest event (stacktrace, request, breadcrumbs, tags)
  4. Diagnose — Claude, read-only, proposes a root cause and a fix
  5. Approve — post the diagnosis to the Slack thread and wait for fix or skip
  6. Fix — Claude, edit mode, applies the fix inside a dedicated git worktree
  7. Verify — run lint / type-check, auto-retrying type errors up to 2 rounds
  8. Review — one or two models review the diff against the diagnosis
  9. PR — commit, push, and open a pull request
  10. Cleanup — offer to remove the worktree

Quickstart

git clone https://github.com/your-org/mendbot.git
cd mendbot
uv sync
cp .env.example .env
# edit .env, config/repos.yaml, and config/branches.yaml for your org
uv run python main.py                 # Slack bot (default channel)
uv run python main.py cli PROJ-123   # or run a ticket straight from the terminal

Prerequisites

Tool Purpose Link
uv Python package manager docs.astral.sh/uv
Python 3.14+ Runtime
Claude Code CLI Runs diagnosis, fix, and review as a subprocess docs.anthropic.com/en/docs/claude-code
jira-cli Fetches ticket metadata and attachment listings github.com/ankitpokhrel/jira-cli
sentry CLI (npm package sentry) Fetches issue + event context — not the older Rust sentry-cli github.com/getsentry/cli
GitHub CLI (gh) Opens pull requests cli.github.com
LiteLLM (optional) Proxy for routing the secondary reviewer to a non-Anthropic model github.com/BerriAI/litellm

Configuration

Copy .env.example to .env and fill in these variables:

Variable Required Description Example
SLACK_BOT_TOKEN Yes Slack bot token (Socket Mode app) xoxb-...
SLACK_APP_TOKEN Yes Slack app-level token (Socket Mode) xapp-...
REPOS_BASE_PATH Yes Local path where target repos are cloned /path/to/local/repos
GITHUB_ORG Yes GitHub organization that owns the target repos your-org
JIRA_BASE_URL Yes Jira instance base URL https://your-org.atlassian.net
JIRA_EMAIL Yes Jira account email used for API auth you@example.com
JIRA_API_TOKEN Yes Jira API token ...
SENTRY_BASE_URL Yes Sentry instance base URL https://sentry.example.com
SENTRY_AUTH_TOKEN Yes Sentry auth token sntryu_...
SENTRY_ORG Yes Sentry organization slug your-org
DOCS_PATH No Path to a local docs repo used for extra diagnosis context /path/to/business-docs
SECONDARY_REVIEW_ENABLED No Turns on the secondary reviewer model true
SECONDARY_REVIEW_MODEL No Model name routed through the LiteLLM proxy gpt-5-mini
SECONDARY_REVIEW_BASE_URL No LiteLLM proxy URL http://127.0.0.1:4444

config/repos.yaml maps a Sentry project to a GitHub repo and its language:

# Maps Sentry project names -> GitHub repo + language.
# Edit for your org.
sentry_project_map:
  BILLING-SERVICE:
    repo: billing-service
    lang: typescript
  PAYMENT-API:
    repo: payment-api
    lang: python

config/branches.yaml maps a Jira fix version to a base branch, with per-repo overrides:

# Maps Jira fix-version -> base branch, with per-repo overrides.
# Edit for your org.
default:
  "1.0": main
  "1.1": develop
overrides:
  payment-api:
    "1.0": release/1.0
pr_target: main

config/pricing.json holds per-model token pricing (input / output / cache read / cache write) used to estimate the USD cost reported in 05-metrics.md. Edit it to match your model rates.

Slack app setup

Create a Slack app with:

  • Socket Mode enabled
  • Bot scopes: chat:write, reactions:write, channels:history, groups:history
  • Event subscriptions: message.channels, message.groups, app_mention

Usage

The entry point dispatches by channel: slack (default) or cli. Each channel is a package implementing the same PipelineUI protocol — a new channel (telegram, discord, ...) is a <channel>/frontend.py plus one entry in main.py's FRONTENDS registry.

Slack channel

  1. Post a Jira ticket URL in the configured Slack channel
  2. The bot reacts with 👀 and starts processing
  3. It posts the diagnosis (root cause, affected files, proposed fix) to the thread
  4. Reply fix to apply it, or skip to stop
  5. On fix, the bot verifies, reviews, and opens a PR, then posts the PR link back to the thread

CLI channel (no Slack)

uv run python main.py cli PROJ-12345                   # interactive: prompts fix/skip
uv run python main.py cli https://…/browse/PROJ-12345  # URLs work too
uv run python main.py cli PROJ-1 PROJ-2                # several tickets, sequential
uv run python main.py cli PROJ-12345 --yes             # auto-approve the fix
uv run python main.py cli PROJ-12345 --diagnose-only   # report only, never fix
uv run python main.py cli PROJ-12345 --verbose         # show INFO logs

Same pipeline, same report artifacts — stage progress, the diagnosis report, and PR links render in the terminal. Exit code is non-zero if any ticket errored (deliberate skips exit 0). --yes auto-answers the fix/skip gate and takes the safe default on the worktree-cleanup question; unknown repo or missing fix version still prompt. Slack credentials are not required for this channel.

Multi-model review

Different models catch different bugs — one might flag a logic error the other misses. When SECONDARY_REVIEW_ENABLED=true, a second reviewer runs in parallel with the primary Claude review, and their findings are merged into a single report tagged by source model. The secondary reviewer is never blocking: if it fails, the primary review result still stands.

The secondary reviewer needs a LiteLLM proxy running locally. Example config.yaml:

model_list:
  - model_name: gpt-5-mini
    litellm_params:
      model: openai/gpt-5-mini
      api_key: os.environ/OPENAI_API_KEY
litellm_settings:
  drop_params: true

Run it with (the proxy runs on Python 3.12 in its own venv; uvloop is not yet 3.14-compatible):

OPENAI_API_KEY=sk-... uvx --python 3.12 --from 'litellm[proxy]' \
  litellm --config config.yaml --port 4444

Then set:

SECONDARY_REVIEW_ENABLED=true
SECONDARY_REVIEW_MODEL=gpt-5-mini
SECONDARY_REVIEW_BASE_URL=http://127.0.0.1:4444

Jira attachments

Attachments on the ticket (screenshots, log files) are automatically downloaded and fed into the diagnosis prompt as extra context. Only images and text/log files up to 5 MB are downloaded — everything else is listed as skipped, with the reason, in the ticket report.

  • Allowed: png, jpg, jpeg, gif, webp, svg, txt, log, csv, json, xml, yaml, yml, md, har
  • Size limit: 5 MB per file

jira-cli lists attachment metadata (filename, size, content URL); the actual bytes are fetched over authenticated HTTP, since jira-cli itself has no download command.

Report artifacts

Every run writes a full audit trail to reports/<TICKET_ID>/ (e.g. reports/PROJ-123/):

File Contents
01-ticket.md Jira metadata — key, summary, repo, base branch, fix version, Sentry URL
01b-sentry.md Issue title, stacktrace, request data, breadcrumbs, tag distributions
02-diagnosis.md Root cause, data flow, affected files, proposed fix, risk level
03-fix-summary.md Lint / type-check output after the fix
03b-review.md Review findings by dimension and severity, with model attribution
04-result.md Outcome (fixed / no_changes / error / skipped) and PR URL
05-metrics.md Per-stage token usage, wall-clock duration, and estimated cost

Testing

uv run pytest tests/

228 tests cover Slack parsing, the Claude CLI wrapper, orchestration, review, Sentry integration, git operations, config loading, and report generation. External CLIs (jira, sentry, git, gh) are mocked.

Roadmap

  • Per-repo review guidelines injected into the review prompt
  • Fix retry loop when the first patch attempt doesn't resolve the diagnosed cause
  • Streaming progress updates to Slack during diagnosis and fix stages
  • Complexity-based model routing (cheaper/faster models for simple bugs)
  • Learning from dismissed review findings to reduce false positives over time
  • Auto-generated regression tests alongside each fix

License

MIT — see LICENSE.

About

AI-powered bug-fix pipeline: Jira ticket in, PR out — Slack bot orchestrating Claude Code CLI

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages