Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3c1b9cb
Merge pull request #14 from codefox-lab/dev
URLbug Mar 12, 2026
1bf4f7c
Merge pull request #15 from codefox-lab/dev
URLbug Mar 12, 2026
0ed3050
update: publish.yml
Mar 12, 2026
fb19f03
update version
Mar 12, 2026
6f9f241
update: publish.yml
Mar 12, 2026
c9d3885
update: publish.yml
Mar 12, 2026
fb4eb51
update version to 0.4.0 in pyproject.toml and setup.py
Mar 12, 2026
5e416e9
Update README.md
URLbug Mar 14, 2026
56dcc20
Update README.md
URLbug Mar 14, 2026
6733aac
Update README.md
URLbug Mar 14, 2026
abf412f
chore: enhance package metadata
Mar 14, 2026
470d466
Merge branch 'main' of github.com:URLbug/CodeFox-CLI
Mar 14, 2026
5f0ab9b
chore: update minor version
Mar 14, 2026
42f1b13
chore: updated action.yml
Mar 16, 2026
9939eda
update
Mar 16, 2026
223ddc3
chore: add new command for index file. Add template for gitlab ci/cd.…
Mar 16, 2026
1ab6ae2
feat: add stage to codefox-template.yml
Mar 20, 2026
f772ab4
chore: add rulers and fix stages
Mar 20, 2026
5df777c
chore: replace gitlab_token to ci_job_token
Mar 20, 2026
ad38384
fix: CODEFOX_BRANCH
Mar 20, 2026
4c0b652
feat: add default variable
Mar 20, 2026
4b8d548
feat: make gitlab ci/cd integration
Mar 20, 2026
8b363cf
feat: add gitlab bot to command scan
Mar 20, 2026
e74b0e1
fix: add python-gitlab sdk to project
Mar 20, 2026
62418ce
debug
Mar 20, 2026
18cc9de
feat
Mar 20, 2026
7f149a8
fix: add method get
Mar 20, 2026
10b33c3
feat
Mar 20, 2026
b8555c5
debug
Mar 20, 2026
ae32911
debug
Mar 20, 2026
b45b897
debug
Mar 20, 2026
d04dad5
debug
Mar 20, 2026
9c61328
feat: disable debug mod
Mar 20, 2026
d7f930c
redfactor
Mar 20, 2026
9200ece
refactor: use job token
Mar 20, 2026
12fd101
feat
Mar 20, 2026
dbe9b28
feat
Mar 20, 2026
ace19ce
debug
Mar 20, 2026
ba40ab3
debug
Mar 20, 2026
b852a93
feat
Mar 20, 2026
5b8fa25
fix: improve error handling in GitLabBot for merge request notes
Mar 20, 2026
91f7f73
debug
Mar 20, 2026
b295fc7
debug
Mar 20, 2026
d15783d
fix: change GITLAB_BOT to GITLAB_TOKEN
Mar 20, 2026
1b6ba76
feat: removee debug
Mar 20, 2026
fe434dc
chore:
Mar 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
.vscode/
.idea/
.venv/
.codefox/
.*_cache/
.env

__pycache__/
build/
codefox.egg-info/

.codefoxenv
.codefoxignore
.codefox.yml
.env

venv*/
build/
__pycache__/
*.pyc
.codefox/
codefox.egg-info/

*.pyc
110 changes: 73 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,101 +4,137 @@

<h1 align="center">CodeFox-CLI</h1>
<p align="center">
Intelligent automated code review system
Diff-aware AI code review for terminal and CI workflows
</p>

<p align="center">
<a href="https://github.com/URLbug/CodeFox-CLI/actions?query=branch%3Amain"><img src="https://github.com/URLbug/CodeFox-CLI/workflows/CI/badge.svg" alt="CI" /></a>
<a href="https://github.com/URLbug/CodeFox-CLI/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License" /></a>
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.11+-green.svg" alt="Python 3.11+" /></a>
<a href="https://github.com/codefox-lab/CodeFox-CLI/wiki"><img src="https://img.shields.io/badge/docs-WIKI-blue?logo=readme" alt="Wiki" /></a>
<a href="https://github.com/codefox-lab/CodeFox-CLI/wiki"><img src="https://img.shields.io/badge/docs-Wiki-blue?logo=readme" alt="Wiki" /></a>
<a href="https://pepy.tech/projects/codefox"><img src="https://static.pepy.tech/personalized-badge/codefox?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads" alt="PyPI Downloads"></a>
</p>

<p align="center">
📚 <a href="https://github.com/codefox-lab/CodeFox-CLI/wiki">Documentation</a> •
🚀 <a href="#-quick-start">Quick Start</a> •
🐛 <a href="https://github.com/URLbug/CodeFox-CLI/issues">Report Issue</a>
🐛 <a href="https://github.com/URLbug/CodeFox-CLI/issues">Report Issue</a> •
📝 <a href="https://github.com/codefox-lab/Demo-PR-Action">Demo PRs</a>
</p>

---

## 🦊 Overview

**CodeFox-CLI** is an intelligent automated code review system that takes over routine security and code quality checks, allowing senior developers to focus on architecture and complex tasks.
**CodeFox-CLI** is a CLI-first AI code review tool for **git diffs, pull requests, and CI workflows**.

It analyzes code changes, retrieves relevant project context, and produces review feedback directly in the terminal or inside automated review pipelines.

CodeFox supports both:
- **local reviews with Ollama** for self-hosted workflows
- **cloud LLM providers** such as Gemini and OpenRouter when remote inference is preferred

Unlike traditional linters, CodeFox understands the context of the entire project and its business logic, delivering not just review comments but **ready-to-apply fixes** (Auto-Fix). Works with **Gemini**, **Ollama**, and **OpenRouter** - use your preferred AI backend.
It is designed for developers and teams who want a **CLI-first review workflow** for local checks, pull requests, and CI/CD pipelines.

---

| vs Linters | vs AI code review (e.g. CodeRabbit) |
|------------|-------------------------------------|
| Understands full project context & business logic | Self-hosted / local (Ollama), no vendor lock-in |
| Suggests fixes, not only rules | Configurable models, security/performance/style rules |
| RAG over your codebase for relevant hints | CLI-first: `git diff` → review in seconds |
## Why CodeFox?

- Reviews **git changes**, not just isolated files
- Uses **relevant codebase context** to improve review quality
- Works with **local or cloud models**
- Fits naturally into **terminal-based and CI workflows**
- Supports configurable review focus such as **security**, **performance**, and **style**

| Compared to linters | Compared to hosted AI reviewers |
|---|---|
| Reviews diffs with codebase context, not only static rules | Can run locally with Ollama |
| Can suggest fixes, not only flag issues | No hard vendor lock-in |
| Flexible review focus: security, performance, style | CLI-first workflow for local and CI usage |

<p align="center">
<img src="https://raw.githubusercontent.com/URLbug/CodeFox-CLI/refs/heads/main/assets/work_review.gif" alt="CodeFox scan demo" width="800" />
</p>

---

## 📥 Installation
## What CodeFox is and is not

Choose the installation method that fits your workflow.
CodeFox is a **CLI for automated AI review of git changes**.

### 🔹 Install dependencies (local setup)
It is **not** an IDE coding assistant like Cursor or Claude Code.
It is built for **diff review workflows**, terminal usage, and CI/CD automation.

```bash
pip install -r requirements.txt
```
### 🔹 Development mode (editable install)
---

Provides the local codefox CLI command and enables live code changes.
## Integrations

```bash
python3 -m pip install -e .
```
Current:
- GitHub Actions

### 🔹 Install from GitHub
Planned:
- GitLab
- Bitbucket

🐍 Using pip
---

## Privacy

- With **Ollama**, reviews can run fully locally on your machine
- With **cloud providers**, code and context may be sent to external APIs depending on your configuration
- Use `.codefoxignore` to exclude files from analysis

---

## 📥 Installation

### For users

**uv**
```bash
python3 -m pip install codefox
# or python3 -m pip install git+https://github.com/URLbug/CodeFox-CLI.git@main
uv tool install codefox
```

⚡ Using uv (recommended for CLI usage)
**pip**
```bash
uv tool install codefox
# or uv tool install git+https://github.com/URLbug/CodeFox-CLI.git@main
python3 -m pip install codefox
```

---

✅ Verify installation
## Verify installation

```bash
codefox version
```
Or
```bash
python3 -m codefox version
```

## 🚀 Quick Start
---

### Initialize (stores your API key)
## 🚀 Quick Start

1. Initialize CodeFox
```bash
codefox init
```

### Run a scan (uses the current git diff)
This stores your provider token locally and creates the initial config files.

2. Review your current git changes
```bash
codefox scan
```

### Show version
What happens during `scan`:

- collects the current git diff

- loads relevant project context based on your configuration

- sends the review request to the configured model

- returns review comments and optional fix suggestions

3. Show version
```bash
codefox version
```
Expand Down
14 changes: 12 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ inputs:
required: true

codefox-branch:
description: 'Git branch of the CodeFox repository to install and run (e.g., main, dev, or a specific release branch).'
description: 'Git branch of the CodeFox repository to install and run.'
default: 'main'
required: false

github-token:
description: 'GitHub token used to authenticate with the GitHub API for posting review comments and interacting with pull requests.'
description: 'GitHub token used to authenticate with the GitHub API.'
default: ${{ github.token }}
required: false

Expand All @@ -28,6 +28,16 @@ runs:
with:
python-version: '3.12'

- name: Restore CodeFox cache
uses: actions/cache@v4
with:
path: ${{ pwd }}/.codefox/
key: ${{ runner.os }}-codefox-${{ github.repository }}-${{ github.base_ref }}-${{ inputs.codefox-branch }}
restore-keys: |
${{ runner.os }}-codefox-${{ github.repository }}-${{ github.base_ref }}-
${{ runner.os }}-codefox-${{ github.repository }}-
${{ runner.os }}-codefox-

- name: Install CodeFox
shell: bash
run: |
Expand Down
35 changes: 35 additions & 0 deletions codefox-template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
codefox_review:
stage: review
image: python:3.12-slim

variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
CODEFOX_BRANCH: "dev-docs"

cache:
key: codefox-$CI_PROJECT_ID
paths:
- ".codefox/"
- ".cache/pip/"

before_script:
- apt-get update && apt-get install -y --no-install-recommends git curl ca-certificates && rm -rf /var/lib/apt/lists/*
- python -m pip install --upgrade pip
- pip install "git+https://github.com/URLbug/CodeFox-CLI.git@${CODEFOX_BRANCH}"
- |
umask 077
{
echo "CODEFOX_API_KEY=$CODEFOX_API_KEY"
echo "GITLAB_TOKEN=${GITLAB_TOKEN}"
echo "GITLAB_URL=${CI_SERVER_URL}"
echo "GITLAB_REPOSITORY=${CI_PROJECT_ID}"
echo "PR_NUMBER=${CI_MERGE_REQUEST_IID}"
} > "$CI_PROJECT_DIR/.codefoxenv"

script:
- git fetch origin "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME":"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" || true
- git fetch origin "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME":"$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" || true
- codefox scan --target-branch "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" --source-branch "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" --ci

rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
56 changes: 56 additions & 0 deletions codefox/bots/gitlab_bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import os

from gitlab import Gitlab
from gitlab.exceptions import GitlabCreateError, GitlabGetError


class GitLabBot:
def __init__(self) -> None:
self.gitlab_token = os.getenv("GITLAB_TOKEN")
self.gitlab_url = os.getenv("GITLAB_URL", "https://gitlab.com")
self.repository = os.getenv("GITLAB_REPOSITORY")
self.mr_iid = os.getenv("PR_NUMBER")

if not self.gitlab_token:
raise ValueError(
"GITLAB_TOKEN environment variable is not set. "
"This token is required to authenticate with the GitLab API."
)

if not self.repository or not self.repository.isdigit():
raise ValueError(f"Invalid GITLAB_REPOSITORY: {self.repository!r}")

if not self.mr_iid or not self.mr_iid.isdigit():
raise ValueError(
f"Invalid PR_NUMBER value: {self.mr_iid!r}. "
"Expected a numeric merge request IID."
)

self.gitlab = Gitlab(
url=self.gitlab_url,
private_token=self.gitlab_token,
)

def send(self, message: str) -> None:
if not message or not message.strip():
raise ValueError("Message must not be empty.")

repository = self.repository
mr_iid = self.mr_iid

if repository is None or mr_iid is None:
raise RuntimeError(
"GitLab project or merge request is not configured."
)

try:
project = self.gitlab.projects.get(int(repository))
mr = project.mergerequests.get(int(mr_iid))
mr.notes.create({"body": message})
except GitlabGetError as exc:
raise RuntimeError(
f"Failed to find project '{repository}' "
f"or merge request IID {mr_iid}."
) from exc
except GitlabCreateError as exc:
raise RuntimeError("Failed to create merge request note.") from exc
22 changes: 22 additions & 0 deletions codefox/cli/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os

from rich import print
from rich.markup import escape

from codefox.api.base_api import BaseAPI
from codefox.cli.base_cli import BaseCLI


class Index(BaseCLI):
def __init__(self, model: type[BaseAPI]):
self.model = model()

def execute(self):
is_upload, error = self.model.upload_files(os.getcwd())
if not is_upload:
print(
"[red]Failed to index files: " + escape(str(error)) + "[/red]"
)
return

print("[green]Successful to index files[/green]")
19 changes: 16 additions & 3 deletions codefox/cli/init.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pathlib import Path
from typing import Any

import yaml
from dotenv import load_dotenv, set_key
Expand All @@ -11,9 +12,18 @@


class Init(BaseCLI):
def __init__(self, model_enum: ModelEnum | None = None):
self.model_enum = model_enum or self._ask_model()
def __init__(self, args: dict[str, Any] | None = None):
resolved_args = args if args is not None else {}

if not resolved_args.get("provider"):
self.model_enum = self._ask_model()
else:
self.model_enum = ModelEnum.by_name(resolved_args["provider"])

self.api_class: type[BaseAPI] = self.model_enum.api_class

self.args: dict[str, Any] = resolved_args

self.config_path = Path(".codefoxenv")
self.ignore_path = Path(".codefoxignore")
self.yaml_config_path = Path(".codefox.yml")
Expand All @@ -28,7 +38,10 @@ def _ask_model(self) -> ModelEnum:
return ModelEnum.by_name(choice)

def execute(self) -> None:
api_key = self._ask_api_key() or ""
if not self.args.get("token"):
api_key = self._ask_api_key() or ""
else:
api_key = self.args["token"]

if not self._write_config(api_key):
return
Expand Down
Loading
Loading