From 6db63e0ec7bb87af72d973f9027531e9659a4ce2 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:14:53 +0200 Subject: [PATCH 01/18] Rename generate_man_pages.py to gen_man_pages.py --- scripts/{generate_man_pages.py => gen_man_pages.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts/{generate_man_pages.py => gen_man_pages.py} (100%) diff --git a/scripts/generate_man_pages.py b/scripts/gen_man_pages.py similarity index 100% rename from scripts/generate_man_pages.py rename to scripts/gen_man_pages.py From 89ee7ea6b055156d22be555bf6366510c0675758 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:16:25 +0200 Subject: [PATCH 02/18] Rename .github/workflows/ai-code-review.yml to ai-code-review.yaml --- .github/workflows/ai-code-review.yml => ai-code-review.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/ai-code-review.yml => ai-code-review.yaml (100%) diff --git a/.github/workflows/ai-code-review.yml b/ai-code-review.yaml similarity index 100% rename from .github/workflows/ai-code-review.yml rename to ai-code-review.yaml From 942925cb326a89a4734197ccd8c5368f177106fd Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:17:18 +0200 Subject: [PATCH 03/18] Create gen-man.yml --- .github/workflows/gen-man.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/workflows/gen-man.yml diff --git a/.github/workflows/gen-man.yml b/.github/workflows/gen-man.yml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.github/workflows/gen-man.yml @@ -0,0 +1 @@ + From 46a7317d71159364e73e0c2d12e95a3a9d07be82 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:25:25 +0200 Subject: [PATCH 04/18] Update tasks.py --- app/tasks.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/app/tasks.py b/app/tasks.py index cc192c8..4f9eb4f 100644 --- a/app/tasks.py +++ b/app/tasks.py @@ -1,8 +1,36 @@ from celery import Celery +from typing import List +from celery import shared_task +from .utils.github_client import fetch_pull_request_diff +from .utils.llm_client import analyze_code_with_llm +from .models import save_review_comments import httpx import os import difflib -from typing import List + +@shared_task +def analyze_pull_request(repo_url: str, pr_number: int): + try: + # 1. Fetch PR diff + diff_text = fetch_pull_request_diff(repo_url, pr_number) + if not diff_text: + return {"success": False, "error": "Unable to fetch diff"} + + # 2. Use LLM to analyze PR diff + review_comments = analyze_code_with_llm(diff_text) + + # 3. Persist and/or post comments + save_review_comments(repo_url, pr_number, review_comments) + + return { + "success": True, + "comments": review_comments, + } + except Exception as e: + return { + "success": False, + "error": str(e), + } celery_app = Celery( 'tasks', From b3a581aa85238915bbe8571dbac1d7215b6a7f9c Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:26:46 +0200 Subject: [PATCH 05/18] Create github_client.py --- utils/github_client.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 utils/github_client.py diff --git a/utils/github_client.py b/utils/github_client.py new file mode 100644 index 0000000..972e496 --- /dev/null +++ b/utils/github_client.py @@ -0,0 +1,13 @@ +# utils/github_client.py + +import requests + +def fetch_pull_request_diff(repo_url: str, pr_number: int) -> str: + owner_repo = repo_url.replace("https://github.com/", "") + headers = { + "Accept": "application/vnd.github.v3.diff", + "Authorization": f"Bearer YOUR_GITHUB_TOKEN" + } + url = f"https://api.github.com/repos/{owner_repo}/pulls/{pr_number}" + response = requests.get(url, headers=headers) + return response.text if response.status_code == 200 else "" From e23b151afd49da07d1d0bdf381aa0e176b3b64f1 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:28:33 +0200 Subject: [PATCH 06/18] Create llm_client.py --- utils/llm_client.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 utils/llm_client.py diff --git a/utils/llm_client.py b/utils/llm_client.py new file mode 100644 index 0000000..3840efe --- /dev/null +++ b/utils/llm_client.py @@ -0,0 +1,21 @@ +# utils/llm_client.py + +from openai import OpenAI + +openai_client = OpenAI(api_key="sk-...") + +def analyze_code_with_llm(diff: str) -> list: + system_prompt = ( + "You are a senior code reviewer. Analyze this GitHub PR diff and return improvement suggestions, " + "style issues, refactor ideas, and security concerns." + ) + response = openai_client.chat.completions.create( + model="gpt-4", # or gpt-3.5-turbo, llama3, etc. + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": diff} + ], + temperature=0.3 + ) + suggestions = response.choices[0].message.content.strip() + return suggestions.split("\n") From 5617e0ee47965d27dd40ab669c8ff8ccc2416dab Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:29:20 +0200 Subject: [PATCH 07/18] Create database.py --- database.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 database.py diff --git a/database.py b/database.py new file mode 100644 index 0000000..fc73192 --- /dev/null +++ b/database.py @@ -0,0 +1,6 @@ +# models.py or database.py + +def save_review_comments(repo_url: str, pr_number: int, comments: list): + print(f"Saving review for {repo_url} PR #{pr_number}") + for comment in comments: + print(f"- {comment}") From 86c186344f753c6577a8aa6fcadca4fd6686d917 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:35:26 +0200 Subject: [PATCH 08/18] Update and rename backend/app.py to backend/app/main.py --- backend/{app.py => app/main.py} | 9 +++++++++ 1 file changed, 9 insertions(+) rename backend/{app.py => app/main.py} (81%) diff --git a/backend/app.py b/backend/app/main.py similarity index 81% rename from backend/app.py rename to backend/app/main.py index 1399ebc..06ea15a 100644 --- a/backend/app.py +++ b/backend/app/main.py @@ -38,6 +38,15 @@ async def predict(request: PredictRequest): @app.get("/health") async def health_check(): return {"status": "healthy"} +# in FastAPI backend +@app.post("/api/submit") +async def submit_pr(payload: dict): + repo_url = payload.get("repo_url") + pr_number = payload.get("pr_number") + # enqueue task to Celery + task = review_pr.delay(repo_url, pr_number) + return {"success": True, "task_id": task.id} + if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000) From d664096ab94c3bcc55c62752e59f51340ab9ff16 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:36:31 +0200 Subject: [PATCH 09/18] Rename gen-man-publish.yml to .github/workflows/gen-man-publish.yml --- gen-man-publish.yml => .github/workflows/gen-man-publish.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename gen-man-publish.yml => .github/workflows/gen-man-publish.yml (100%) diff --git a/gen-man-publish.yml b/.github/workflows/gen-man-publish.yml similarity index 100% rename from gen-man-publish.yml rename to .github/workflows/gen-man-publish.yml From 731b7279639f620fdbab3c94d02277ddd1b49448 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:38:27 +0200 Subject: [PATCH 10/18] Update main.py --- main.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/main.py b/main.py index e824c69..f568919 100644 --- a/main.py +++ b/main.py @@ -8,6 +8,14 @@ from submit_pr import router as submit_pr_router from providers.enclovai_provider import call_enclovai +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # or specific origin + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + def process_prompt(prompt, model="auto"): return call_enclovai(prompt, model=model) From 3a6329a9305ff4be27ad9df5c21d224489ef52f2 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:53:36 +0200 Subject: [PATCH 11/18] Update index.html --- index.html | 315 ++++++++++++++++++----------------------------------- 1 file changed, 107 insertions(+), 208 deletions(-) diff --git a/index.html b/index.html index 0de8708..bb99cd0 100644 --- a/index.html +++ b/index.html @@ -3,256 +3,181 @@ - enclov-AI โ€” GitHub PR Code Reviewer + enclovAI โ€” GitHub PR Code Reviewer -
- - +

enclovAI

+

AI-Powered GitHub PR Code Reviewer

-

enclov-AI

-

- Supercharge your GitHub PR workflow with real-time AI-driven review automation. - Powered by OpenAI, FastAPI, Redis, and Celery. -

+
+ + - View on GitHub + + + + +
-
- ๐Ÿ”„ Waiting for review... +
+ ๐Ÿง  AI review feedback will appear here...
+
-
-

๐Ÿ“ Submit GitHub PR for Review

-
- - +
+ ยฉ 2025 enclovAI โ€” Built with โค๏ธ for developers +
- - + From 38fc4659d6bc2173e5e1955aab9a0bb5df95277d Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:54:30 +0200 Subject: [PATCH 12/18] Update main.py --- app/main.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/app/main.py b/app/main.py index a1af575..b4ca24a 100644 --- a/app/main.py +++ b/app/main.py @@ -1,28 +1,26 @@ -from fastapi import FastAPI, HTTPException +from fastapi import FastAPI, Request +from fastapi.staticfiles import StaticFiles +from fastapi.responses import JSONResponse, FileResponse from pydantic import BaseModel -from celery.result import AsyncResult -from app.tasks import analyze_pull_request import os +from app.ai_review import analyze_pr app = FastAPI() +app.mount("/static", StaticFiles(directory="static"), name="static") + +@app.get("/") +async def serve_index(): + return FileResponse("static/index.html") + class PRRequest(BaseModel): repo_url: str pr_number: int @app.post("/api/submit") -async def submit_pr(pr: PRRequest): - task = analyze_pull_request.delay(pr.repo_url, pr.pr_number) - return {"job_id": task.id} - -@app.get("/api/status/{job_id}") -async def get_status(job_id: str): - task_result = AsyncResult(job_id) - if task_result.state == 'PENDING': - return {"status": "pending"} - elif task_result.state == 'SUCCESS': - return {"status": "completed", "result": task_result.result} - elif task_result.state == 'FAILURE': - return {"status": "failed", "error": str(task_result.result)} - else: - return {"status": task_result.state} +async def submit_review(pr: PRRequest): + try: + result = analyze_pr(pr.repo_url, pr.pr_number) + return JSONResponse(content={"success": True, "comments": result}) + except Exception as e: + return JSONResponse(content={"success": False, "error": str(e)}) From af4c68d3baa3e63a21dc45f0235b3f3817328810 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:55:41 +0200 Subject: [PATCH 13/18] Create ai_review.py --- app/ai_review.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 app/ai_review.py diff --git a/app/ai_review.py b/app/ai_review.py new file mode 100644 index 0000000..c92befa --- /dev/null +++ b/app/ai_review.py @@ -0,0 +1,12 @@ +import random + +def analyze_pr(repo_url: str, pr_number: int): + # Real logic would clone repo, checkout PR diff, run LLM, etc. + mock_comments = [ + f"โœ… Efficient use of data structures in PR #{pr_number}.", + "๐Ÿงน Consider removing unused imports.", + "๐Ÿ”’ Security tip: mask sensitive keys in logs.", + "๐Ÿ“ฆ Use semantic versioning in your package updates.", + "๐Ÿง  Consider adding docstrings to helper functions." + ] + return random.sample(mock_comments, k=3) From 34c6acf913bf286f1903472c319fbc442ce25cd0 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:56:59 +0200 Subject: [PATCH 14/18] Create submit --- api/submit | 1 + 1 file changed, 1 insertion(+) create mode 100644 api/submit diff --git a/api/submit b/api/submit new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/api/submit @@ -0,0 +1 @@ + From 140978a3b831ff3dcacd168564d551175d127bf2 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:58:34 +0200 Subject: [PATCH 15/18] Create run.sh --- app/run.sh | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 app/run.sh diff --git a/app/run.sh b/app/run.sh new file mode 100644 index 0000000..ab8a9bd --- /dev/null +++ b/app/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash +uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload +chmod +x run.sh From f1419ed83540e9a9dde786074fd95479d117971d Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 03:03:06 +0200 Subject: [PATCH 16/18] Delete .github/workflows/nextjs.yml --- .github/workflows/nextjs.yml | 93 ------------------------------------ 1 file changed, 93 deletions(-) delete mode 100644 .github/workflows/nextjs.yml diff --git a/.github/workflows/nextjs.yml b/.github/workflows/nextjs.yml deleted file mode 100644 index ed74736..0000000 --- a/.github/workflows/nextjs.yml +++ /dev/null @@ -1,93 +0,0 @@ -# Sample workflow for building and deploying a Next.js site to GitHub Pages -# -# To get started with Next.js see: https://nextjs.org/docs/getting-started -# -name: Deploy Next.js site to Pages - -on: - # Runs on pushes targeting the default branch - push: - branches: ["main"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - # Build job - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Detect package manager - id: detect-package-manager - run: | - if [ -f "${{ github.workspace }}/yarn.lock" ]; then - echo "manager=yarn" >> $GITHUB_OUTPUT - echo "command=install" >> $GITHUB_OUTPUT - echo "runner=yarn" >> $GITHUB_OUTPUT - exit 0 - elif [ -f "${{ github.workspace }}/package.json" ]; then - echo "manager=npm" >> $GITHUB_OUTPUT - echo "command=ci" >> $GITHUB_OUTPUT - echo "runner=npx --no-install" >> $GITHUB_OUTPUT - exit 0 - else - echo "Unable to determine package manager" - exit 1 - fi - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: "20" - cache: ${{ steps.detect-package-manager.outputs.manager }} - - name: Setup Pages - uses: actions/configure-pages@v5 - with: - # Automatically inject basePath in your Next.js configuration file and disable - # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). - # - # You may remove this line if you want to manage the configuration yourself. - static_site_generator: next - - name: Restore cache - uses: actions/cache@v4 - with: - path: | - .next/cache - # Generate a new cache whenever packages or source files change. - key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} - # If source files changed but packages didn't, rebuild from a prior cache. - restore-keys: | - ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- - - name: Install dependencies - run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} - - name: Build with Next.js - run: ${{ steps.detect-package-manager.outputs.runner }} next build - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ./out - - # Deployment job - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 From 747d55ee3001b97dd9e314251ef4318c5a1e5944 Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 03:03:55 +0200 Subject: [PATCH 17/18] Create astro.yml --- .github/workflows/astro.yml | 90 +++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 .github/workflows/astro.yml diff --git a/.github/workflows/astro.yml b/.github/workflows/astro.yml new file mode 100644 index 0000000..54d4be4 --- /dev/null +++ b/.github/workflows/astro.yml @@ -0,0 +1,90 @@ +# Sample workflow for building and deploying an Astro site to GitHub Pages +# +# To get started with Astro see: https://docs.astro.build/en/getting-started/ +# +name: Deploy Astro site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +env: + BUILD_PATH: "." # default value when not using subfolders + # BUILD_PATH: subfolder + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Detect package manager + id: detect-package-manager + run: | + if [ -f "${{ github.workspace }}/yarn.lock" ]; then + echo "manager=yarn" >> $GITHUB_OUTPUT + echo "command=install" >> $GITHUB_OUTPUT + echo "runner=yarn" >> $GITHUB_OUTPUT + echo "lockfile=yarn.lock" >> $GITHUB_OUTPUT + exit 0 + elif [ -f "${{ github.workspace }}/package.json" ]; then + echo "manager=npm" >> $GITHUB_OUTPUT + echo "command=ci" >> $GITHUB_OUTPUT + echo "runner=npx --no-install" >> $GITHUB_OUTPUT + echo "lockfile=package-lock.json" >> $GITHUB_OUTPUT + exit 0 + else + echo "Unable to determine package manager" + exit 1 + fi + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: ${{ steps.detect-package-manager.outputs.manager }} + cache-dependency-path: ${{ env.BUILD_PATH }}/${{ steps.detect-package-manager.outputs.lockfile }} + - name: Setup Pages + id: pages + uses: actions/configure-pages@v5 + - name: Install dependencies + run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} + working-directory: ${{ env.BUILD_PATH }} + - name: Build with Astro + run: | + ${{ steps.detect-package-manager.outputs.runner }} astro build \ + --site "${{ steps.pages.outputs.origin }}" \ + --base "${{ steps.pages.outputs.base_path }}" + working-directory: ${{ env.BUILD_PATH }} + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ${{ env.BUILD_PATH }}/dist + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + needs: build + runs-on: ubuntu-latest + name: Deploy + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From c2d9720efb3c325a057790ebf1243c94fae623ae Mon Sep 17 00:00:00 2001 From: Web4 <167559384+Web4application@users.noreply.github.com> Date: Sat, 7 Jun 2025 03:05:14 +0200 Subject: [PATCH 18/18] Rename index.html to static/index.html --- index.html => static/index.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename index.html => static/index.html (100%) diff --git a/index.html b/static/index.html similarity index 100% rename from index.html rename to static/index.html