Inspired by Azmi Mengü's vision about building an agent that takes Jira tickets, writes code, runs tests, deploys, and closes the ticket automatically.
Azmi Bot is a GitHub Actions-based automation system that orchestrates AI agents (Claude Opus 4.5 for planning & Gemini 3 Pro for coding/review) to transform Jira tickets into production code through a quality-gated, multi-repository workflow.
New to Azmi Bot? Start here:
- 🚀 Quick Start Guide (30 min) - Step-by-step setup for beginners
- 📖 Full Documentation - Detailed guide below
Jira Ticket (+ Images) → Quality Gate (Router) → Target Repo → TSD → Implementation → PR → Review → Merge → Deploy
- Quality Gate: Gemini 3 Pro scores Jira tickets (0-100) for clarity, completeness, and testability
- ✨ Vision Support: AI can see and analyze attached images (mockups, screenshots, diagrams)
- Routing: Passes high-quality tickets to the appropriate repository
- Planning: Claude Opus 4.5 creates detailed implementation plans (TSD)
- Implementation: Gemini 3 Pro implements code based on the plan
- Review: Gemini 3 Pro reviews PRs and auto-fixes issues
- Automation: PRs are auto-created, reviewed, and merged
- Receives Jira webhook triggers
- Scores ticket quality with Gemini 3 Pro (Structured Outputs)
- Updates Jira custom fields (score, verdict, comments)
- Routes approved tickets to target repositories via
repository_dispatch
- Receive
ai-coding-taskevents - Run 2-stage AI workflow:
- Claude Opus 4.5: Creates Technical Specification Document (TSD) with full code examples
- Gemini 3 Pro: Implements code based on TSD, opens PR
- Auto-merge after Gemini 3 Pro review
- Custom fields:
AI Quality Score,AI Quality Verdict,AI Target Repo - Automation rule: "Assignee is Azmi Bot" → triggers router
- Smart routing based on repo selection field
Azmi Bot can now see and analyze images attached to Jira tickets! This dramatically improves implementation accuracy for UI/UX tasks, bug reports with screenshots, and architectural diagrams.
Attach mockups, wireframes, or design screenshots:
- ✅ AI reads exact colors, spacing, layouts from images
- ✅ Matches visual specifications in implementation
- ✅ Reduces back-and-forth for visual requirements
Example:
Title: "Implement new dashboard layout"
Description: "See attached mockup for the new dashboard design"
Attachments: dashboard-mockup.png
→ AI sees the mockup and implements matching the exact visual design
Attach error screenshots or visual bugs:
- ✅ AI identifies issues from error messages in images
- ✅ Understands visual bugs (layout issues, missing elements)
- ✅ Creates targeted fixes based on visual evidence
Example:
Title: "Login button not visible on mobile"
Description: "Button is cut off on iPhone 13"
Attachments: mobile-bug-screenshot.png
→ AI sees the screenshot and fixes the responsive layout issue
Attach system architecture, flowcharts, or database schemas:
- ✅ Understands system relationships from diagrams
- ✅ Implements according to architectural patterns shown
- ✅ Follows data flow illustrated in images
Example:
Title: "Implement user authentication flow"
Description: "Follow the authentication flow in the attached diagram"
Attachments: auth-flow-diagram.png
→ AI understands the flow and implements each step correctly
Comment with #AZMI + screenshot showing the issue:
- ✅ AI sees before/after comparisons in comment attachments
- ✅ Identifies visual differences or bugs from screenshots
- ✅ Implements fixes matching visual expectations
- ✅ Optimized images passed efficiently via dispatch payload
- ✅ Claude Code receives full visual context for accurate fixes
How Revision Images Work:
- Add
#AZMIcomment to Jira issue - Attach screenshot(s) showing the problem
- Router fetches & optimizes images (800x600 JPEG)
- Passes optimized images in dispatch payload to target repo
- Claude Code implements fix with full visual context
Example:
Comment: "#AZMI The spacing is wrong, see attached comparison"
Attachments: before-after-comparison.png
→ AI sees both images, identifies exact spacing issue, implements fix
Example with Bootstrap classes:
Comment: "#AZMI Use Bootstrap btn btn-outline-danger class instead of custom CSS"
Attachments: design-mockup.png
→ AI sees the design, uses exact Bootstrap class as requested (not custom CSS)
- ✅ PNG, JPG, JPEG: Standard image formats
- ✅ GIF, WebP: Modern image formats
- ✅ PDF: Automatically converted to PNG (first page only)
- ❌ SVG: Not supported (attach PNG export instead)
Image Processing Pipeline:
Jira Attachment → Download → Optimize (800x600, JPEG quality 75) → Encode (Base64) → Embed in Prompt → AI Vision API
Aggressive Image Optimization:
- Resolution: Resized to max 800x600 (preserves aspect ratio)
- Format: PNG converted to JPEG (quality 75)
- Metadata: Stripped for smaller file size
- Compression: 90-95% size reduction (2-3MB → 50-100KB)
- PDFs: Converted to PNG (first page only, 200 DPI), then optimized
- Large files (> 10MB) are skipped with warning
- Benefits:
- Fast AI processing (smaller tokens)
- Fits in GitHub Actions dispatch payloads
- Single Jira API fetch (efficient)
- Still maintains quality for AI vision
Quality Gate Scoring with Images:
- If images clearly show WHAT needs to be done → +20-30 bonus points
- Visual specifications can substitute for detailed text descriptions
- AI analyzes images BEFORE evaluating ticket quality
- Optimized images analyzed by GPT-5 Vision for quality scoring
Implementation with Images:
- Router: Fetches and optimizes images once, passes in dispatch payload
- Codex Planning: Receives only image count notification (doesn't need full images for planning)
- Claude Code Implementation: Receives full optimized images for visual-accurate coding
- Revision Flow: Same optimization applied, images passed via dispatch payload
Initial Ticket with Images:
- Create Jira ticket with clear title
- Add description (can be brief if image is clear)
- Attach images (mockups, screenshots, diagrams)
- Assign to Azmi Bot
- Select target repo
→ Router fetches images, AI evaluates with visual context, routes if approved
Revision with Images:
- Comment on Jira ticket:
#AZMI fix the layout issue - Attach screenshot showing the problem
- AZMI evaluates revision request with visual context
- If approved → Creates revision plan with image context → Implements fix
Do:
- ✅ Use high-quality screenshots (clear, not blurry)
- ✅ Annotate images with arrows/highlights if helpful
- ✅ Include before/after comparisons for changes
- ✅ Attach multiple angles for complex UI
- ✅ Use design tool exports (Figma screenshots, etc.)
Don't:
- ❌ Attach huge files (> 10MB will be skipped)
- ❌ Use low-resolution images (hard for AI to read text)
- ❌ Attach unrelated images (confuses AI)
- ❌ Rely solely on images without any text description
- Max 3 images for revisions (configurable in scripts, prevents payload bloat)
- Max file size: 10MB per image before optimization (larger files skipped)
- After optimization: ~50-100KB per image (90-95% size reduction)
- Total payload size: Safe for dispatch (~300KB for 3 images)
- PDF support: First page only (multi-page PDFs truncated, then optimized)
- No SVG support (export to PNG instead)
- Image quality: 800x600 JPEG (sufficient for AI vision, optimized for speed)
Images not appearing in AI context:
# Check workflow logs for:
"🖼️ Building structured input with X image(s)"Empty or corrupt images:
# Check logs for:
"❌ Downloaded file is empty (0 bytes), skipping..."Large files skipped:
# Check logs for:
"⚠️ File too large (XXX MB), skipping..."Image optimization working:
# Check logs for successful optimization:
"✅ Optimized: 2.3MB → 78KB (-96%)"
"📸 Including optimized image context in payload (3 images)""Argument list too long" errors: This was a common issue before optimization. If you see this error:
/usr/bin/jq: Argument list too long- Ensure
fetch-jira-attachments.shhas image optimization enabled - Check that images are being resized to 800x600 max
- Verify PNG → JPEG conversion is working
- Should see 90-95% size reduction in logs
- Jira Cloud instance
- GitHub organization with repos
- Google AI API key (Gemini 3 Pro)
- Anthropic API key (Claude Opus 4.5)
git clone https://github.com/YOUR_ORG/router.git
cd routerGo to Settings → Secrets and variables → Actions and add:
| Secret | Description | How to Get |
|---|---|---|
GEMINI_API_KEY |
Google AI API key | aistudio.google.com/apikey |
JIRA_BASE |
Your Jira URL | https://yourcompany.atlassian.net |
JIRA_EMAIL |
Bot account email | Create a service account in Jira |
JIRA_API_TOKEN |
Jira API token | id.atlassian.com → Security → API tokens |
DISPATCH_PAT |
GitHub Personal Access Token | See below ⬇️ |
This token allows the router to trigger workflows in target repos:
- Go to GitHub → Settings (your profile, not repo)
- Developer settings → Personal access tokens → Fine-grained tokens
- Generate new token
- Name:
azmi-bot-router - Expiration: 90 days (or your preference)
- Repository access: Only select repositories
- Select all target repos (e.g.,
org/win-room,org/api-service)
- Select all target repos (e.g.,
- Permissions:
- ✅ Contents: Read and write
- ✅ Pull requests: Read and write
- ✅ Metadata: Read-only (automatic)
- Name:
- Generate token → Copy it (you won't see it again!)
- Add as
DISPATCH_PATsecret in router repo
In Jira → Settings → Issues → Custom fields:
-
AI Quality Score
- Type: Number
- Add to all screens where "AI Coding" issues appear
-
AI Quality Verdict
- Type: Select List (single choice)
- Options:
pass,fail
-
AI Target Repo
- Type: Select List (single choice)
- Options:
htuzel/win-roomhtuzel/api-servicehtuzel/mobile-backend- (add all your repos)
Find Custom Field IDs: After creating fields, go to any issue → Edit → Inspect element on the field → find customfield_XXXXX in HTML. You'll need these IDs for the workflow.
- Create a user: azmi-bot@yourcompany.com
- Give it project access
- Generate API token for this user (link above)
Project Settings → Automation → Create rule:
Trigger:
When: Work item transitioned
Condition applied: Assignee is "Azmi Bot"
Action: Send web request
URL: https://api.github.com/repos/YOUR_ORG/router/dispatches
Method: POST
Headers:
Accept: application/vnd.github+json
Authorization: Bearer YOUR_GITHUB_PAT
Content-Type: application/json
Body (Custom data):
{
"event_type": "ai-coding-request",
"client_payload": {
"issue_key": "{{issue.key}}",
"title": "{{issue.summary}}",
"description": "{{issue.description}}",
"target_repo": "{{issue.AI Target Repo}}",
"url": "{{issue.url}}"
}
}
Replace YOUR_ORG/router and YOUR_GITHUB_PAT with your values.
Note: You can use the same PAT as DISPATCH_PAT or create a separate one just for Jira automation.
Edit .github/workflows/router.yml:
Line 131-136: Update custom field IDs
# Change customfield_12345 and 12346 to your actual field IDs
curl -sS -X PUT "$JIRA_BASE/rest/api/3/issue/$ISSUE" \
-H "Authorization: Basic $AUTH" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"fields":{"customfield_12345":'"$SCORE"',"customfield_12346":"'"$VERDICT"'"}}'To find your field IDs:
curl -H "Authorization: Basic $(echo -n 'your-email:api-token' | base64)" \
https://yourcompany.atlassian.net/rest/api/3/field | jq '.[] | select(.name | contains("AI"))'For each target repo (e.g., win-room, api-service):
Settings → Secrets and variables → Actions:
| Secret | Value | Description |
|---|---|---|
GEMINI_API_KEY |
Same as router | For Gemini 3 Pro coding and review |
ANTHROPIC_API_KEY |
From console.anthropic.com | For Claude Opus 4.5 planning |
JIRA_BASE |
Same as router | https://yourcompany.atlassian.net |
JIRA_EMAIL |
Same as router | Bot account email |
JIRA_API_TOKEN |
Same as router | Jira API token |
GH_PAT |
GitHub Personal Access Token | For creating PRs and triggering workflows (see below) |
This token is needed because secrets.GITHUB_TOKEN doesn't trigger other workflows. Create it same way as DISPATCH_PAT:
- Go to GitHub → Settings (your profile)
- Developer settings → Personal access tokens → Fine-grained tokens
- Generate new token
- Name:
azmi-bot-target-repo - Repository access: Only select repositories → Select this target repo
- Permissions:
- ✅ Contents: Read and write
- ✅ Pull requests: Read and write
- ✅ Workflows: Read and write (to trigger other workflows)
- Name:
- Generate token → Add as
GH_PATsecret in target repo
Note: You can reuse the same DISPATCH_PAT token as GH_PAT if it has access to the target repo.
Settings → Actions → General → Workflow permissions:
- ✅ Read and write permissions
- ✅ Allow GitHub Actions to create and approve pull requests
Create these files:
.github/workflows/ai-coding.yml
name: AI Coding
on:
repository_dispatch:
types: [ai-coding-task]
permissions:
contents: write
pull-requests: write
issues: write
env:
BASE_BRANCH: main
jobs:
codex-plan:
runs-on: ubuntu-latest
env:
JIRA_BASE: ${{ secrets.JIRA_BASE }}
JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Extract issue_key from payload
id: payload
run: |
PAYLOAD='${{ toJSON(github.event.client_payload) }}'
echo "issue_key=$(jq -r '.issue_key' <<< "$PAYLOAD")" >> $GITHUB_OUTPUT
- name: Fetch issue from Jira
id: jira
shell: bash
run: |
set -euo pipefail
ISSUE=$(jq -r '.issue_key' <<< '${{ toJSON(github.event.client_payload) }}')
if [ -z "$ISSUE" ] || [ "$ISSUE" = "null" ]; then
echo "ERROR: Issue key is missing from payload"
exit 1
fi
echo "Fetching issue: $ISSUE from Jira..."
echo "JIRA_BASE: $JIRA_BASE"
echo "JIRA_EMAIL: $JIRA_EMAIL"
echo "Full URL: $JIRA_BASE/rest/api/3/issue/$ISSUE?expand=renderedFields"
AUTH=$(printf "%s:%s" "$JIRA_EMAIL" "$JIRA_API_TOKEN" | base64 | tr -d '\n')
mkdir -p .artifacts
curl -sS -H "Authorization: Basic $AUTH" \
-H "Accept: application/json" \
"$JIRA_BASE/rest/api/3/issue/$ISSUE?expand=renderedFields" \
-o .artifacts/jira-issue.json
echo "Issue fetched successfully"
TITLE=$(jq -r '.fields.summary' .artifacts/jira-issue.json)
# Use renderedFields.description (HTML string) instead of fields.description (ADF JSON)
DESCRIPTION=$(jq -r '.renderedFields.description // "No description"' .artifacts/jira-issue.json)
echo "Title: $TITLE"
echo "Description preview: ${DESCRIPTION:0:100}..."
# Multi-line output requires EOF delimiter in GitHub Actions
echo "title=$TITLE" >> $GITHUB_OUTPUT
echo "description<<EOF" >> $GITHUB_OUTPUT
echo "$DESCRIPTION" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Codex - produce plan (TSD) as text
id: codex
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt: |
You are a senior software engineer and a senior planner. Given the Jira task, output a clear, concise implementation plan as Markdown.
The plan is for Claude Code to implement.
Task ID: ${{ steps.payload.outputs.issue_key }}
Title: ${{ steps.jira.outputs.title }}
Description:
${{ steps.jira.outputs.description }}
Output sections:
1) goal
2) scope - list of files or modules to touch
3) acceptance_criteria - 3 to 6 bullet points
4) test_plan - unit and integration bullets
5) risks_and_mitigations - brief list
6) runbook - how to run & rollback
7) code - code to be implemented
8) Output should be a detailed tsd.md file.
Constraints:
- Keep it specific to this repo.
- Do not write any code here. Only the plan.
effort: "high"
- name: Save plan to artifact
run: |
mkdir -p .artifacts
cat > .artifacts/PLAN.md << 'EOF'
${{ steps.codex.outputs.final-message }}
EOF
- name: Upload plan artifact
uses: actions/upload-artifact@v4
with:
name: ai-plan
path: .artifacts/PLAN.md
claude-implement:
runs-on: ubuntu-latest
needs: codex-plan
permissions:
contents: write
pull-requests: write
issues: write
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: ai-plan
path: .artifacts
- name: Read plan from artifact
id: read-plan
run: |
PLAN=$(cat .artifacts/PLAN.md)
echo "plan<<EOF" >> $GITHUB_OUTPUT
echo "$PLAN" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Claude Code - review plan and implement, then open PR
uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GH_PAT }}
show_full_output: true
prompt: |
You are Claude Code working on ${{ github.repository }}.
CONTEXT:
- Repository: Win Room v2.0 - Next.js 14 + PostgreSQL + Socket.IO sales platform
- Architecture documented in TSD.md
- Task: ${{ github.event.client_payload.issue_key || 'Task' }}
IMPLEMENTATION PLAN FROM CODEX:
---
${{ steps.read-plan.outputs.plan }}
---
YOUR WORKFLOW:
1. VALIDATE THE PLAN:
- Read the plan carefully
- Check if requirements are already implemented in the codebase
- If already implemented: document what exists and suggest only verification/testing steps
- If gaps exist: refine the plan with specific file paths and implementation details
- Create your "Final Implementation Plan" (can be same as Codex plan if it's good)
2. IMPLEMENT THE CHANGES:
- Follow the Final Implementation Plan step by step
- Make minimal, safe changes
- Ensure code follows existing patterns in the repo
- Add appropriate error handling
- Update types/interfaces as needed
3. ADD TESTS:
- Follow the test_plan from the implementation plan
- Add unit tests for new functions/components
- Add integration tests for API endpoints
- Ensure tests pass before creating PR
4. COMMIT AND PUSH:
- Create feature branch: `git checkout -b feature/${{ github.event.client_payload.issue_key || 'ai-task' }}`
- Stage changes: `git add -A`
- Commit with message: `git commit -m "Implement ${{ github.event.client_payload.issue_key || 'task' }}"`
- Push to remote: `git push -u origin feature/${{ github.event.client_payload.issue_key || 'ai-task' }}`
5. CREATE PULL REQUEST:
- Use GitHub CLI to create PR: `gh pr create --title "..." --body "..." --base ${{ env.BASE_BRANCH }}`
- PR Title: "AI Coding - ${{ github.event.client_payload.issue_key || 'Task' }}"
- PR Body must include:
* **Final Implementation Plan** (your validated/refined plan)
* **Changes Made** (bullet list of files changed and why)
* **Testing Done** (what tests were added/run)
* **Notes** (any important considerations for reviewers)
- CRITICAL: You MUST create the PR. Use gh pr create command.
- Do not ask for approval. Execute the gh pr create command automatically.
IMPORTANT RULES:
- If the feature is already 90%+ implemented, create a PR that just adds tests/documentation
- Always create a PR even if changes are minimal - the PR is the deliverable
- Include clear commit messages
- Make sure all file paths in commits are correct
- Run any relevant build/lint commands before pushing
CRITICAL: You are running in CI/CD environment. DO NOT ASK FOR APPROVAL.
Automatically proceed with all file operations, git commands, and implementations.
No human is available to approve - execute all changes autonomously.
BEGIN IMPLEMENTATION NOW.
claude_args: |
--dangerously-skip-permissions
--max-turns 100
--model claude-sonnet-4-5-20250929Note: See "Add Secrets" section above for GH_PAT setup instructions.
.github/workflows/codex-review.yml
name: Codex Review and Merge
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: write
pull-requests: write
jobs:
codex-review:
runs-on: ubuntu-latest
env:
JIRA_BASE: ${{ secrets.JIRA_BASE }}
JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
steps:
- uses: actions/checkout@v4
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- name: Fetch PR heads
run: |
git fetch --no-tags origin \
${{ github.event.pull_request.base.ref }} \
+refs/pull/${{ github.event.pull_request.number }}/head
- name: Extract Jira issue key from PR
id: jira-key
run: |
# Try to extract from PR title first (format: "AI Coding - GET-1234")
TITLE="${{ github.event.pull_request.title }}"
ISSUE_KEY=$(echo "$TITLE" | grep -oE '[A-Z]+-[0-9]+' | head -1 || echo "")
if [ -z "$ISSUE_KEY" ]; then
# Try from PR body
ISSUE_KEY=$(echo "${{ github.event.pull_request.body }}" | grep -oE '[A-Z]+-[0-9]+' | head -1 || echo "")
fi
echo "issue_key=$ISSUE_KEY" >> $GITHUB_OUTPUT
echo "Found Jira issue: $ISSUE_KEY"
- name: Codex - review PR and, if needed, output a unified diff patch
id: codex
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt: |
You are a senior reviewer. Review ONLY the changes in this PR for ${{ github.repository }}.
1) If there are blocking issues, output a unified diff patch that fixes them.
The patch must be between markers:
```patch
*** BEGIN PATCH
<unified diff here>
*** END PATCH
```
2) If there are no blocking issues, output a short approval message.
Also verify that the PR body contains a "Final TSD" and that acceptance criteria are met by tests.
effort: "high"
- name: Try to extract patch
id: patch
run: |
set -e
echo "${{ steps.codex.outputs.final-message }}" > .codex_msg.txt
if grep -q "\*\*\* BEGIN PATCH" .codex_msg.txt; then
awk '/\*\*\* BEGIN PATCH/{flag=1; next} /\*\*\* END PATCH/{flag=0} flag' .codex_msg.txt > codex.patch
echo "has_patch=true" >> $GITHUB_OUTPUT
else
echo "has_patch=false" >> $GITHUB_OUTPUT
fi
- name: Apply patch if exists
if: steps.patch.outputs.has_patch == 'true'
run: |
git config user.name "codex-bot"
git config user.email "codex-bot@users.noreply.github.com"
git checkout ${{ github.event.pull_request.head.ref }}
git apply --whitespace=fix codex.patch
git commit -am "Codex fix patch"
git push origin HEAD
- name: Claude Code - secondary review
id: claude
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
prompt: |
You are a senior code reviewer performing a secondary review of this PR.
Codex has already done an initial review. Your role is to provide additional insights.
Review this PR for ${{ github.repository }} against the TSD.md architecture.
Focus on:
1. **Security**: SQL injection, XSS, authentication bypass, sensitive data exposure
2. **Performance**: N+1 queries, unnecessary loops, memory leaks, inefficient algorithms
3. **Correctness**: Logic errors, edge cases, null handling, race conditions
4. **Code Quality**: Consistent patterns, proper error handling, code duplication
5. **Testing**: Coverage of acceptance criteria, edge case tests, integration tests
6. **Architecture**: Follows Win Room patterns (TSD.md), proper separation of concerns
Output format:
- If you find issues: List them with severity (CRITICAL/HIGH/MEDIUM/LOW)
- If all looks good: "LGTM - Claude Code secondary review passed"
Keep your review concise but specific. Reference file paths and line numbers.
- name: Post Claude review as comment
uses: actions/github-script@v7
with:
script: |
const claudeReview = `${{ steps.claude.outputs.response }}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: `## 🤖 Claude Code Secondary Review\n\n${claudeReview}`
});
- name: Approve PR if no blockers
if: steps.patch.outputs.has_patch == 'false'
uses: actions/github-script@v7
with:
script: |
await github.rest.pulls.createReview({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
event: "APPROVE",
body: "LGTM by Codex."
});
- name: Post review summary to Jira
if: steps.jira-key.outputs.issue_key != ''
run: |
ISSUE_KEY="${{ steps.jira-key.outputs.issue_key }}"
if [ -z "$ISSUE_KEY" ]; then
echo "No Jira issue found, skipping Jira update"
exit 0
fi
AUTH=$(printf "%s:%s" "$JIRA_EMAIL" "$JIRA_API_TOKEN" | base64 | tr -d '\n')
PR_URL="${{ github.event.pull_request.html_url }}"
# Determine review status
if [ "${{ steps.patch.outputs.has_patch }}" = "true" ]; then
REVIEW_STATUS="⚠️ Issues Found - Auto-fixed by Codex"
else
REVIEW_STATUS="✅ Approved"
fi
# Create ADF (Atlassian Document Format) comment
COMMENT_BODY=$(jq -n \
--arg status "$REVIEW_STATUS" \
--arg pr_url "$PR_URL" \
--arg codex_msg "${{ steps.codex.outputs.final-message }}" \
'{
body: {
type: "doc",
version: 1,
content: [
{
type: "heading",
attrs: { level: 2 },
content: [{ type: "text", text: "🤖 AI Code Review Completed" }]
},
{
type: "paragraph",
content: [
{ type: "text", text: "Status: ", marks: [{ type: "strong" }] },
{ type: "text", text: $status }
]
},
{
type: "paragraph",
content: [
{ type: "text", text: "Pull Request: ", marks: [{ type: "strong" }] },
{ type: "text", text: $pr_url, marks: [{ type: "link", attrs: { href: $pr_url } }] }
]
},
{
type: "heading",
attrs: { level: 3 },
content: [{ type: "text", text: "Codex Review:" }]
},
{
type: "codeBlock",
content: [{ type: "text", text: $codex_msg }]
},
{
type: "paragraph",
content: [
{ type: "text", text: "Claude Code secondary review also completed.", marks: [{ type: "em" }] }
]
}
]
}
}')
curl -sS -X POST "$JIRA_BASE/rest/api/3/issue/$ISSUE_KEY/comment" \
-H "Authorization: Basic $AUTH" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data "$COMMENT_BODY"
echo "Posted review summary to Jira issue $ISSUE_KEY"
- name: Move Jira issue to Review status
if: steps.jira-key.outputs.issue_key != ''
run: |
ISSUE_KEY="${{ steps.jira-key.outputs.issue_key }}"
if [ -z "$ISSUE_KEY" ]; then
echo "No Jira issue found, skipping status transition"
exit 0
fi
AUTH=$(printf "%s:%s" "$JIRA_EMAIL" "$JIRA_API_TOKEN" | base64 | tr -d '\n')
# Get available transitions for this issue
TRANSITIONS=$(curl -sS -X GET "$JIRA_BASE/rest/api/3/issue/$ISSUE_KEY/transitions" \
-H "Authorization: Basic $AUTH" \
-H "Accept: application/json")
echo "Available transitions:"
echo "$TRANSITIONS" | jq '.transitions[] | {id: .id, name: .name}'
# Find transition ID for "Review" status (try common names)
TRANSITION_ID=$(echo "$TRANSITIONS" | jq -r '.transitions[] | select(.name | test("Review|In Review|Code Review"; "i")) | .id' | head -1)
if [ -z "$TRANSITION_ID" ] || [ "$TRANSITION_ID" = "null" ]; then
echo "⚠️ No 'Review' transition found for issue $ISSUE_KEY"
echo "Available transitions are listed above. Please configure Jira workflow."
exit 0
fi
# Perform transition
TRANSITION_BODY=$(jq -n --arg id "$TRANSITION_ID" '{transition: {id: $id}}')
curl -sS -X POST "$JIRA_BASE/rest/api/3/issue/$ISSUE_KEY/transitions" \
-H "Authorization: Basic $AUTH" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data "$TRANSITION_BODY"
echo "✅ Moved Jira issue $ISSUE_KEY to Review status (transition ID: $TRANSITION_ID)"
- name: Merge PR if approved and checks pass
uses: actions/github-script@v7
with:
script: |
// try merge; if not ready, do nothing
try {
await github.rest.pulls.merge({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
merge_method: "squash"
});
core.info("Merged successfully.");
} catch (e) {
core.info("Merge not possible yet. Waiting for checks or approvals.");
}Create CLAUDE.md in the root of each target repo:
# Development Guidelines for Azmi Bot
## Code Standards
- Follow existing patterns in the repository
- Use TypeScript with strict mode
- Write self-documenting code with clear variable names
- Add JSDoc comments for complex functions
## Testing Requirements
- Unit tests for all new functions (>= 80% coverage)
- Integration tests for API endpoints
- Use existing test framework (Jest/Vitest)
## PR Requirements
- Include "Final TSD" section in PR body
- List all changed files with brief explanations
- Add "Testing Done" section
- Keep PRs focused and minimal
## Architecture
- Next.js App Router structure
- API routes under `app/api/`
- Components in `components/`
- Shared utilities in `lib/`
- Database operations in `lib/db/`
## Never Do
- Break existing API contracts
- Remove existing tests
- Deploy without testing
- Commit secrets or API keys1. Create Jira Ticket
└─ Assign to "Azmi Bot"
└─ Select "AI Target Repo"
2. Jira Automation Triggers
└─ Sends webhook to Router repo
3. Router Repository
├─ Gemini 3 Pro scores ticket (0-100)
├─ Updates Jira fields
├─ Posts suggestions as comment
└─ If score >= 80:
└─ Sends repository_dispatch to target repo
4. Target Repository
├─ Claude Opus 4.5 creates TSD (Technical Spec + Code Examples)
├─ Gemini 3 Pro implements code from TSD
├─ Gemini 3 Pro opens PR
└─ Triggers Gemini Review workflow
5. Code Review Workflow
├─ Gemini 3 Pro reviews PR
├─ If issues: Creates patch → Applies → Commits
├─ Posts to Jira with PR link
├─ Moves Jira to "Review" status
└─ If approved: Auto-merges PR
6. Deploy (Your CI/CD)
└─ After merge, your deploy pipeline runs
Check:
- Jira automation is active
- GitHub PAT has correct permissions
- Webhook payload matches expected format
Debug:
# Manually trigger router
curl -X POST \
-H "Authorization: Bearer YOUR_PAT" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/YOUR_ORG/router/dispatches \
-d '{"event_type":"ai-coding-request","client_payload":{"issue_key":"TEST-1","title":"Test","description":"Test description","target_repo":"YOUR_ORG/YOUR_REPO"}}'Check:
DISPATCH_PAThas access to target repo- Target repo has
repository_dispatchtrigger forai-coding-task - Jira score >= 80 (check router logs)
Check:
GH_PATsecret exists in target repoGH_TOKENenvironment variable set in Claude Code step- Workflow permissions: "Read and write" + "Allow PR creation"
Check:
- Branch protection rules (may require approvals)
- CI checks must pass first
- Codex review completed without blockers
After code is merged and deployed to staging, you might discover bugs or need adjustments. Instead of creating a new Jira ticket, you can request revisions directly in the original ticket by commenting with #AZMI.
Merged PR → Staging Testing → Find Issue → Comment "#AZMI fix XYZ" → AZMI Implements Fix → New PR
- Test in staging and find an issue
- Comment on the Jira ticket with
#AZMIfollowed by revision request - Router evaluates the revision request (quality check)
- If approved: Task moves to "In Progress", AZMI creates revision plan
- Claude Code implements the fix with context from previous PR
- New PR is created for review and merge
The router repo already has revision-router.yml which handles revision requests.
Copy target_repo/ai-revision.yml to your target repository's .github/workflows/ folder.
This workflow:
- Receives
ai-revision-taskevents - Fetches previous PR changes for context
- Creates revision plan with Codex
- Implements fixes with Claude Code
- Opens new PR with revision
Project Settings → Automation → Create rule:
Trigger:
When: Comment is added
Condition: Comment contains "#AZMI" (case insensitive)
Action: Send web request
URL: https://api.github.com/repos/YOUR_ORG/router/dispatches
Method: POST
Headers:
Accept: application/vnd.github+json
Authorization: Bearer YOUR_GITHUB_PAT
Content-Type: application/json
Body (Custom data):
{
"event_type": "ai-revision-request",
"client_payload": {
"issue_key": "{{issue.key}}",
"comment_body": "{{comment.body}}",
"target_repo": "{{issue.AI Target Repo}}",
"previous_pr_url": ""
}
}
Note: previous_pr_url can be extracted from Jira comments if PRs post their links. Otherwise, AZMI will search through Jira comments to find the PR.
After a PR is merged and you test in staging:
Example 1: Bug Fix
#AZMI The login button on mobile devices is not working.
Error in console: "Cannot read property 'email' of undefined"
Fix the null check in src/components/LoginForm.tsx line 45
Example 2: UI Adjustment
#AZMI The dashboard cards need more spacing.
Increase the gap between cards from 16px to 24px in src/app/dashboard/page.tsx
Example 3: Performance Issue
#AZMI The user list is loading slowly (5+ seconds)
Add pagination to the /api/users endpoint, limit to 50 users per page
AZMI evaluates revision comments on:
- Clarity (30 points): What needs to be changed?
- Specificity (30 points): File paths, function names, error messages?
- Actionability (40 points): Can AI implement this directly?
Passing score: 70+
Good revision request:
#AZMI Fix the authentication bug in production:
- File: src/lib/auth.ts line 67
- Issue: Token expiry check is using wrong timezone (UTC vs local)
- Fix: Use UTC consistently for token expiry comparison
- Test: Verify users can stay logged in across timezone boundaries
Poor revision request:
#AZMI fix the bug
1. Comment on Jira: "#AZMI fix the login bug..."
└─ Jira automation triggers
2. Router Repository (revision-router.yml)
├─ AI quality check on revision request (0-100)
├─ Posts assessment to Jira
├─ Moves task to "In Progress" if approved
└─ Dispatches to target repo with previous PR context
3. Target Repository (ai-revision.yml)
├─ Fetches previous PR changes
├─ Codex creates revision plan
├─ Claude Code implements fixes
└─ Opens new PR: "AZMI Revision - TASK-123"
4. Review & Merge (codex-review.yml)
├─ Same review process as initial implementation
├─ Codex reviews, Claude Code validates
└─ Auto-merge if approved
- Fast iterations: No need to create new tickets
- Context preservation: AZMI sees previous implementation
- Quality gated: Poor revision requests are rejected with suggestions
- Audit trail: All revisions linked to original task in Jira
- Minimal overhead: Just comment "#AZMI" + description
Edit router/.github/workflows/router.yml line 228:
if: steps.jira.outputs.verdict == 'pass' # Change logic here- Add repo option in Jira "AI Target Repo" field
- Give
DISPATCH_PATaccess to new repo - Add workflows to new repo
- Quality scoring:
router.ymlline 81-91 - TSD generation:
ai-coding.ymlline 77-98 - Implementation:
ai-coding.ymlline 217-266 - Code review:
codex-review.ymlline 49-61
Edit codex-review.yml:
- Auto-fix: Keep patch application logic
- Review only: Remove patch application, keep comment
- Block merge: Add
exit 1if issues found
- Quick Start Guide (30 min) - Beginner-friendly setup guide
- Setup Checklist - Track your configuration progress
- Full README - Comprehensive documentation (this file)
- Contributing Guidelines - How to contribute
- Code of Conduct - Community standards
- Security Policy - Security best practices
- Changelog - Version history
- ARTICLE.md - Building Azmi Bot: The full story and technical deep dive
- OpenAI Codex Action
- Claude Code GitHub Actions
- Jira REST API
- GitHub Actions Workflow Syntax
- Jira Automation
This is an open-source implementation inspired by Azmi Mengü's vision of autonomous code agents. Feel free to:
- Fork and adapt for your organization
- Submit improvements via PR
- Share your experience
MIT License - See LICENSE file
- Inspired by Azmi Mengü's vision of AI agents that code, test, and deploy
- Planning powered by Claude Opus 4.5
- Coding & Review powered by Google Gemini 3 Pro
- Orchestrated with GitHub Actions
Star ⭐ if this helped you! | Report Issues | Contribute