2 Commits

Author SHA1 Message Date
6cc3d5eb00 Finished?
Some checks failed
Build and publish / Build Container (push) Failing after 3s
2025-12-27 14:11:40 -05:00
b2673b79e7 init 2025-12-27 13:24:25 -05:00
6 changed files with 1917 additions and 76 deletions

44
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: Build and publish
run-name: Build and publish
on:
push:
jobs:
container:
name: Build Container
runs-on: ubuntu-latest
container: docker
steps:
- name: Build Container
run: |
git clone -b "${{github.ref_name}}" "$(echo ${{github.server_url}}/${{github.repository}}.git | sed s%://%://${{github.token}}@% )" .
DOCKER_HUB=$([ -n "${{secrets.DOCKER_HUB_USER}}" ] && [ -n "${{secrets.DOCKER_HUB_TOKEN}}" ] && [ -n "${{secrets.DOCKER_HUB_IMAGE}}" ] && echo "true" || echo "false")
REGISTRY="$(echo "${{github.server_url}}" | sed -E 's|https?://||')"
VERSION="$(cat VERSION)"
docker login -u "${{github.repository_owner}}" -p "${{secrets.DEPLOY_TOKEN}}" "$REGISTRY"
if [ "$DOCKER_HUB" = "true" ]; then docker login -u "${{secrets.DOCKER_HUB_USER}}" -p "${{secrets.DOCKER_HUB_TOKEN}}" docker.io; fi
docker build -t "$REGISTRY/${{github.repository}}:${{github.ref_name}}" .
docker push "$REGISTRY/${{github.repository}}:${{github.ref_name}}"
if [ "$DOCKER_HUB" = "true" ]; then
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:${{github.ref_name}}"
docker push "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:${{github.ref_name}}"
fi
if [ "${{github.ref_name}}" = "master" ]; then
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "$REGISTRY/${{github.repository}}:$VERSION"
docker push "$REGISTRY/${{github.repository}}:$VERSION"
if [ "$DOCKER_HUB" = "true" ]; then
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:$VERSION"
docker push "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:$VERSION"
fi
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "$REGISTRY/${{github.repository}}:latest"
docker push "$REGISTRY/${{github.repository}}:latest"
if [ "$DOCKER_HUB" = "true" ]; then
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:latest"
docker push "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:latest"
fi
fi

7
Dockerfile Normal file
View File

@@ -0,0 +1,7 @@
FROM node:22
COPY . /ai
RUN cd /ai && npm ci
WORKDIR /app
ENTRYPOINT ["npx", "--yes", "/ai", "review", "/app"]

View File

@@ -6,91 +6,43 @@
<img src="https://git.zakscode.com/repo-avatars/2b4ee6ba1f2e2618bf7694e4a52fb56d1d0ea6abafa2dcbe496ab786b86d5a76" alt="Logo" width="200" height="200"> <img src="https://git.zakscode.com/repo-avatars/2b4ee6ba1f2e2618bf7694e4a52fb56d1d0ea6abafa2dcbe496ab786b86d5a76" alt="Logo" width="200" height="200">
<!-- Title --> <!-- Title -->
### Template ### AI Reviewer
<!-- Description --> <!-- Description -->
Simple repository template Automated AI-powered code review for pull requests 🤖
<!-- Repo badges --> <!-- Repo badges -->
[![Version](https://img.shields.io/badge/dynamic/json.svg?label=Version&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/template/tags&query=$[0].name)](https://git.zakscode.com/ztimson/template/tags) [![Version](https://img.shields.io/badge/dynamic/json.svg?label=Version&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/ai-reviewer/tags&query=$[0].name)](https://git.zakscode.com/ztimson/ai-reviewer/tags)
[![Pull Requests](https://img.shields.io/badge/dynamic/json.svg?label=Pull%20Requests&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/template&query=open_pr_counter)](https://git.zakscode.com/ztimson/template/pulls) [![Pull Requests](https://img.shields.io/badge/dynamic/json.svg?label=Pull%20Requests&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/ai-reviewer&query=open_pr_counter)](https://git.zakscode.com/ztimson/ai-reviewer/pulls)
[![Issues](https://img.shields.io/badge/dynamic/json.svg?label=Issues&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/template&query=open_issues_count)](https://git.zakscode.com/ztimson/template/issues) [![Issues](https://img.shields.io/badge/dynamic/json.svg?label=Issues&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/ai-reviewer&query=open_issues_count)](https://git.zakscode.com/ztimson/ai-reviewer/issues)
<!-- Links --> <!-- Links -->
---
---
<div> <div>
<a href="https://git.zakscode.com/ztimson/template/wiki" target="_blank">Documentation</a> <a href="https://git.zakscode.com/ztimson/ai-reviewer/releases" target="_blank">Release Notes</a>
• <a href="https://git.zakscode.com/ztimson/template/releases" target="_blank">Release Notes</a> • <a href="https://git.zakscode.com/ztimson/ai-reviewer/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a>
• <a href="https://git.zakscode.com/ztimson/template/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a> • <a href="https://git.zakscode.com/ztimson/ai-reviewer/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a>
• <a href="https://git.zakscode.com/ztimson/template/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a>
</div> </div>
--- ---
</div> </div>
## Table of Contents ## Table of Contents
- [Template](#top) - [AI Reviewer](#top)
- [About](#about) - [About](#about)
- [Demo](#demo)
- [Built With](#built-with) - [Built With](#built-with)
- [Setup](#setup) - [Setup](#setup)
- [Production](#production) - [Production](#production)
- [Development](#development)
- [License](#license) - [License](#license)
## About ## About
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Automated code reviewer that uses AI to analyze git diffs and provide inline comments on pull requests. Supports Anthropic, OpenAI, and Ollama models with tool-based reviewing for precise feedback.
### Demo
Website: https://git.zakscode.com
### Built With ### Built With
[![Angular](https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular)](https://angular.io/)
[![Android](https://img.shields.io/badge/android-34A853?style=for-the-badge&logo=android&logoColor=ffffff)](https://www.android.com/)
[![Arduino](https://img.shields.io/badge/Arduino-00878F?style=for-the-badge&logo=arduino&logoColor=white)](https://www.arduino.cc/)
[![Bootstrap](https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge&logo=bootstrap&logoColor=white)](https://getbootstrap.com)
[![C](https://img.shields.io/badge/C-A8B9CC?style=for-the-badge&logo=c&logoColor=ffffff)](https://en.cppreference.com/w/c/language)
[![C++](https://img.shields.io/badge/C%2B%2B-00599C?style=for-the-badge&logo=cplusplus)](https://cplusplus.com/)
[![C#](https://img.shields.io/badge/C%23-239120?style=for-the-badge&logo=csharp)](https://dotnet.microsoft.com/)
[![CSS](https://img.shields.io/badge/CSS-1572B6?style=for-the-badge&logo=css3)](https://www.w3.org/Style/CSS/Overview.en.html)
[![Django](https://img.shields.io/badge/django-0C4B33?style=for-the-badge&logo=django)](https://www.djangoproject.com/)
[![Docker](https://img.shields.io/badge/Docker-384d54?style=for-the-badge&logo=docker)](https://docker.com/) [![Docker](https://img.shields.io/badge/Docker-384d54?style=for-the-badge&logo=docker)](https://docker.com/)
[![Electron](https://img.shields.io/badge/Electron-47848F?style=for-the-badge&logo=electron&logoColor=white)](https://www.electronjs.org/)
[![Firebase](https://img.shields.io/badge/Firebase-FFFFFF?style=for-the-badge&logo=firebase)](https://firebase.google.com/)
[![Go](https://img.shields.io/badge/Go-00ADD8?style=for-the-badge&logo=go&logoColor=ffffff)](https://go.dev/)
[![GraphQL](https://img.shields.io/badge/GraphQL-E10098?style=for-the-badge&logo=graphql)](https://graphql.org/)
[![HTML](https://img.shields.io/badge/HTML-FFFFFF?style=for-the-badge&logo=html5)](https://developer.mozilla.org/en-US/docs/Glossary/HTML)
[![Java](https://img.shields.io/badge/Java-5382A1?style=for-the-badge&logo=coffeescript&logoColor=F8981D)](https://java.com/)
[![JavaScript](https://img.shields.io/badge/JavaScript-000000?style=for-the-badge&logo=javascript)](https://javascript.com/) [![JavaScript](https://img.shields.io/badge/JavaScript-000000?style=for-the-badge&logo=javascript)](https://javascript.com/)
[![JQuery](https://img.shields.io/badge/jQuery-0769AD?style=for-the-badge&logo=jquery)](https://jquery.com )
[![Laravel](https://img.shields.io/badge/Laravel-6C6C6C?style=for-the-badge&logo=laravel)](https://laravel.com)
[![Linux](https://img.shields.io/badge/Linux-eeeeee?style=for-the-badge&logo=linux&logoColor=000000)](https://www.linux.org/)
[![Momentum](https://img.shields.io/badge/Momentum-000000?style=for-the-badge&logo=data:image/svg%2bxml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDIwMDEwOTA0Ly9FTiIgImh0dHA6Ly93d3cudzMub3JnL1RSLzIwMDEvUkVDLVNWRy0yMDAxMDkwNC9EVEQvc3ZnMTAuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI1MHB0IiBoZWlnaHQ9IjI1MHB0IiB2aWV3Qm94PSIwIDAgMjUwIDI1MCIgIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIG1lZXQiPgoJPHN0eWxlPgoJCUBtZWRpYSAocHJlZmVycy1jb2xvci1zY2hlbWU6IGxpZ2h0KSB7CgkJZyB7IGZpbGw6ICMwMDAwMDA7IH0KCQl9CgkJQG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTogZGFyaykgewoJCWcgeyBmaWxsOiAjZmZmZmZmOyB9CgkJfQoJPC9zdHlsZT4KCTxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjUwKSBzY2FsZSgwLjEsLTAuMSkiPgoJCTxwYXRoIGQ9Ik0xMTY1IDIyOTkgYy0yNDUgLTE4IC00NzAgLTEyMyAtNjUxIC0zMDMgLTExNyAtMTE4IC0yMDAgLTI1MCAtMjUyIC00MDEgLTcxIC0yMDkgLTcxIC00NjggLTEgLTY3OSAzNyAtMTA4IDExOCAtMjU0IDE3OCAtMzE5IGwyNSAtMjggLTEzMiAtMTMyIC0xMzIgLTEzMiA1MiAtNTMgNTMgLTUyIDEzMiAxMzIgMTMyIDEzMiAyOCAtMjUgYzQxIC0zOCAxMDkgLTgyIDE4OCAtMTIxIDE1MyAtNzcgMjkyIC0xMDkgNDcwIC0xMDkgMjkxIDAgNTM2IDEwMSA3NDEgMzA1IDE1MyAxNTQgMjQ0IDMyNCAyODkgNTQwIDIxIDEwMyAyMSAyOTkgLTEgNDAyIC0zMiAxNTkgLTEwNSAzMjQgLTE4NyA0MjYgbC00OSA2MSAxMjIgMTIyIDEyMyAxMjMgLTIyIDMxIGMtMTIgMTYgLTM2IDQwIC01MiA1MiBsLTMxIDIyIC0xMjEgLTEyMSAtMTIyIC0xMjEgLTYwIDQ2IGMtODEgNjEgLTI2MSAxNDcgLTM2NSAxNzMgLTEwNCAyNyAtMjM1IDM3IC0zNTUgMjl6IG0yNzQgLTE2NCBjMTAyIC0yMSAyMTcgLTY4IDI5NyAtMTIxIDEwOSAtNzIgMTA2IC02NiA0OSAtMTI0IGwtNTAgLTUwIC01MCA5IGMtODMgMTYgLTE2OSAtNSAtMzA1IC03NCBsLTc1IC0zOCA0NSAtMzggYzI1IC0yMiA1MyAtNDIgNjMgLTQ1IDExIC0zIDQ1IDYgNzcgMjAgNjggMzEgMTM1IDQ1IDE3MCAzNiAyMiAtNSAyNSAtMTIgMjggLTU2IDEyIC0yMDQgLTM2MCAtNjQ1IC02NzQgLTgwMCAtMTA3IC01MyAtMTgwIC02NiAtMjA4IC0zOCAtMjcgMjcgLTE4IDExMyAyMiAxOTggMTggMzcgMjkgNzMgMjUgODAgLTE0IDI0IC03NiA5NiAtODIgOTYgLTEyIDAgLTc4IC0xMzIgLTEwMSAtMTk5IC0xNiAtNDkgLTIxIC04NCAtMTkgLTE0OCBsMyAtODQgLTQxIC00MSBjLTUwIC01MCAtNTIgLTQ5IC0xMTkgNTggLTU1IDg4IC05OCAxOTYgLTExOSAyOTQgLTE5IDkxIC0xOSAyNzkgMCAzNjkgNzIgMzQzIDM0NyA2MjEgNjg4IDY5NSA4NSAxOSAyODkgMTkgMzc2IDF6IG01NjkgLTM5MyBjNjMgLTk1IDEwNSAtMTk1IDEyOCAtMzA1IDE4IC04OSAxOCAtMjc3IC0xIC0zNjcgLTczIC0zNDYgLTM0OSAtNjIyIC02OTUgLTY5NSAtOTEgLTE5IC0yNzggLTE5IC0zNjggMCAtMTMwIDI3IC0yNzggOTUgLTM2NyAxNjkgbC0zMCAyNSA0NSA0NSBjNDQgNDUgNDUgNDYgMTEwIDQ2IDE3MyAwIDM5MiAxMjQgNjI1IDM1NSAxMzAgMTI5IDIwNCAyMjIgMjc4IDM1MCA2MyAxMDkgODkgMTg1IDk0IDI4MCBsNSA4MCA1MSA1MyBjMjggMjggNTUgNTIgNTkgNTIgNSAwIDM1IC00MCA2NiAtODh6Ii8+CgkJPHBhdGggZD0iTTc1MiAxODM5IGMtMTk1IC05NyAtOTMgLTQzMyAyMzggLTc5MSA3MSAtNzcgNzIgLTc4IDEwMCAtNjMgMTUgOCA0MiAyNiA2MCA0MSBsMzEgMjcgLTY5IDcxIGMtMTc2IDE4MSAtMjk3IDM3NCAtMzE3IDUwOCAtMTQgOTIgMzIgMTEwIDE1OCA2NSAxMjAgLTQ0IDI2OCAtMTQ3IDQwOSAtMjg3IGw4OCAtODcgMTkgMjEgYzEwIDEyIDI4IDM2IDM5IDU1IGwyMSAzNCAtMjYgMzEgYy00NyA1NyAtMTk3IDE4NiAtMjg1IDI0NyAtMTk2IDEzNCAtMzYxIDE4MCAtNDY2IDEyOHoiLz4KCQk8cGF0aCBkPSJNMTAxMiAxNDk3IGMtMjkgLTI5IC01MiAtNTggLTUyIC02NiAwIC0xMyA1NCAtOTYgNjggLTEwNSA1IC0zIDQ0IDMwIDg2IDczIGw3OCA3OSAtNTMgMzYgYy0zMCAyMCAtNTggMzYgLTY0IDM2IC02IDAgLTM0IC0yNCAtNjMgLTUzeiIvPgoJCTxwYXRoIGQ9Ik0xNjcyIDExMzkgYy00NiAtNTYgLTQ5IC02OSAtMjggLTEwMiAyMyAtMzUgNTYgLTE0MyA1NiAtMTgxIDAgLTc4IC01OSAtODMgLTIwNiAtMjAgbC04MyAzNiAtNTEgLTQ0IGMtMjcgLTI0IC01MCAtNDYgLTUwIC01MCAwIC0xNCAxODYgLTk5IDI1MiAtMTE0IDIwMCAtNDkgMzE3IDc4IDI2MyAyODYgLTE2IDYzIC03MCAxODYgLTk3IDIyMyAtMTMgMTcgLTE3IDE0IC01NiAtMzR6Ii8+Cgk8L2c+Cjwvc3ZnPgo=)](https://git.zakscode.com/ztimson/momentum)
[![MongoDB](https://img.shields.io/badge/mongodb-000000?style=for-the-badge&logo=mongodb)](https://www.mongodb.com/)
[![MySQL](https://img.shields.io/badge/MySQL-4479A1?style=for-the-badge&logo=mysql&logoColor=ffffff)](https://www.mysql.com/)
[![Nest](https://img.shields.io/badge/nestjs-E0234E?style=for-the-badge&logo=nestjs)](https://nestjs.com/)
[![.NET](https://img.shields.io/badge/.NET-512BD4?style=for-the-badge&logo=dotnet)](https://dotnet.microsoft.com/)
[![Next](https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs)](https://nextjs.org/)
[![NGINX](https://img.shields.io/badge/NGINX-009639?style=for-the-badge&logo=nginx)](https://www.nginx.com/)
[![Node](https://img.shields.io/badge/Node.js-000000?style=for-the-badge&logo=nodedotjs)](https://nodejs.org/) [![Node](https://img.shields.io/badge/Node.js-000000?style=for-the-badge&logo=nodedotjs)](https://nodejs.org/)
[![p5.js](https://img.shields.io/badge/p5.js-ed225d?style=for-the-badge&logo=p5dotjs&logoColor=white)](https://p5js.org/)
[![PHP](https://img.shields.io/badge/PHP-474A8A?style=for-the-badge&logo=php&logoColor=white)](https://www.php.net/)
[![PostgreSQL](https://img.shields.io/badge/PostgreSQl-212121?style=for-the-badge&logo=postgresql)](https://www.postgresql.org/)
[![Python](https://img.shields.io/badge/Python-FFD43B?style=for-the-badge&logo=python)](https://www.python.org/)
[![React](https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react)](https://reactjs.org/)
[![Redis](https://img.shields.io/badge/Redis-ffffff?style=for-the-badge&logo=redis)](https://redis.com/)
[![SASS](https://img.shields.io/badge/SASS-CC6699?style=for-the-badge&logo=sass&logoColor=ffffff)](https://sass-lang.com/)
[![Shell](https://img.shields.io/badge/Shell-000000?style=for-the-badge&logo=windowsterminal&logoColor=00ff00)](https://en.wikipedia.org/wiki/Shell_script)
[![SQL Server](https://img.shields.io/badge/SQL%20Server-CC2927?style=for-the-badge&logo=microsoftsqlserver)](https://www.microsoft.com/en-ca/sql-server)
[![SQLite](https://img.shields.io/badge/SQLITE-003B57?style=for-the-badge&logo=sqlite)](https://www.sqlite.org/index.html)
[![Svelte](https://img.shields.io/badge/Svelte-4A4A55?style=for-the-badge&logo=svelte)](https://svelte.dev/)
[![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white)](https://typescriptlang.org/)
[![Windows](https://img.shields.io/badge/Windows-0078D4?style=for-the-badge&logo=windows)](https://microsoft.com/windows)
[![Vite](https://img.shields.io/badge/vite-1b1b1b?style=for-the-badge&logo=vite)](https://vitejs.dev/)
[![Vue](https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge&logo=vuedotjs)](https://vuejs.org/)
## Setup ## Setup
@@ -101,12 +53,12 @@ Website: https://git.zakscode.com
</h3> </h3>
</summary> </summary>
#### Prerequisites
- [Docker](https://docs.docker.com/install/)
#### Instructions #### Instructions
1. Run the docker image: `docker run -p 80:80 git.zakscode.com/ztimson/template:latest` 1. Add the following to your gitea build:
2. Open [http://localhost](http://localhost) ```yaml
```
</details> </details>
<details> <details>
@@ -120,13 +72,13 @@ Website: https://git.zakscode.com
- [Node.js](https://nodejs.org/en/download) - [Node.js](https://nodejs.org/en/download)
#### Instructions #### Instructions
1. Install the dependencies: `npm install` 1. Install the dependencies: `npm i`
2. Start the Angular server: `npm run start` 2. Build library: `npm run review`
3. Open [http://localhost:4200](http://localhost:4200)
</details> </details>
## License ## License
Copyright © 2023 Zakary Timson | All Rights Reserved | Available under MIT Licensing
Copyright © 2025 Zakary Timson | All Rights Reserved | Available under MIT Licensing
See the [license](./LICENSE) for more information. See the [license](./LICENSE) for more information.

1743
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

16
package.json Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "ai-reviewer",
"version": "0.0.0",
"scripts": {
"review": "node src/review.mjs"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@ztimson/ai-utils": "0.2.3",
"@ztimson/utils": "^0.28.3",
"@ztimson/node-utils": "^1.0.4"
}
}

79
src/review.mjs Normal file
View File

@@ -0,0 +1,79 @@
import {Ai} from '@ztimson/ai-utils';
import {$} from '@ztimson/node-utils';
import * as os from 'node:os';
import * as path from 'node:path';
import * as fs from 'node:fs';
(async () => {
const
git = process.env['GIT_HOST'],
owner = process.env['GIT_OWNER'],
repo = process.env['GIT_REPO'],
auth = process.env['GIT_TOKEN'],
pr = process.env['PULL_REQUEST'],
root = process.argv[2] || process.cwd(),
host = process.env['AI_HOST'],
model = process.env['AI_MODEL'],
token = process.env['AI_TOKEN'];
const comments = [];
const commit = (await $`git log -1 --pretty=format:%H`).trim();
const gitDiff = await $`git diff HEAD^..HEAD`;
let options = {ollama: {model, host}};
if(host === 'anthropic') options = {anthropic: {model, token}};
else if(host === 'openai') options = {openAi: {model, token}};
const ai = new Ai({
...options,
model: [host, model],
path: process.env['path'] || os.tmpdir(),
system: 'You are a code reviewer. Analyze the git diff and use the `recommend` tool for EACH issue you find. You must call `recommend` exactly once for every bug or improvement opportunity. After making all recommendations, provide a brief summary.',
tools: [{
name: 'read_file',
description: 'Read contents of a file',
args: {
path: {type: 'string', description: 'Path to file relative to project root'}
},
fn: (args) => fs.readFileSync(path.join(root, args.path), 'utf-8')
}, {
name: 'get_diff',
description: 'Check a file for differences using git',
args: {
path: {type: 'string', description: 'Path to file relative to project root'}
},
fn: async (args) => await $`git diff HEAD^..HEAD -- ${path.join(root, args.path)}`
}, {
name: 'recommend',
description: 'REQUIRED: Call this once for every bug, improvement, or concern identified in the review.',
args: {
file: {type: 'string', description: 'File path'},
line: {type: 'number', description: 'Line number in new file', optional: true},
comment: {type: 'string', description: 'Review comment explaining the issue'}
},
fn: (args) => {
comments.push({
path: args.file,
new_position: args.line,
body: args.comment,
});
return 'Comment recorded, continue reviewing';
}
}]
});
const summary = await ai.language.ask(gitDiff);
const res = await fetch(`${git}/api/v1/repos/${owner}/${repo}/pulls/${pr}/reviews`, {
method: 'POST',
headers: {
'Authorization': `token ${auth}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
body: summary,
commit_id: commit,
event: 'COMMENT',
comments,
})
});
if(!res.ok) throw new Error(`${res.status} ${await res.text()}`);
})();