generated from ztimson/template
Compare commits
26 Commits
dcbdb231a6
...
0.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 397c9aeb90 | |||
| 73ad9293ae | |||
| 4175bf363c | |||
| c0281cd57c | |||
| a24961a55c | |||
| e0f3a3cd82 | |||
| 6fccede7ba | |||
| f00906045a | |||
| bee0029469 | |||
| 1b3232b10c | |||
| 40ade3fef1 | |||
| f65fd15220 | |||
| 93e7be5280 | |||
| 97895096f3 | |||
| 001d016a8a | |||
| 882b845b11 | |||
| f50324a072 | |||
| 1396f4b305 | |||
| 5d3e15f0f5 | |||
| c27802ca44 | |||
| 2b11979b66 | |||
| 45d43c0977 | |||
| 1b4fd05d65 | |||
| d6857bd8e3 | |||
| 6cc3d5eb00 | |||
| b2673b79e7 |
11
.env
Normal file
11
.env
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
; DO NOT MODIFY! this is an example environment file
|
||||||
|
; Create a copy called .env.local with the needed settings
|
||||||
|
|
||||||
|
GIT_HOST=
|
||||||
|
GIT_OWNER=
|
||||||
|
GIT_REPO=
|
||||||
|
GIT_TOKEN=
|
||||||
|
PULL_REQUEST=
|
||||||
|
AI_HOST=
|
||||||
|
AI_MODEL=
|
||||||
|
AI_TOKEN=
|
||||||
45
.github/workflows/build.yml
vendored
Normal file
45
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
name: Publish Library
|
||||||
|
run-name: Publish Library
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build NPM Project
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: node:alpine
|
||||||
|
steps:
|
||||||
|
- name: Clone Repository
|
||||||
|
uses: ztimson/actions/clone@develop
|
||||||
|
|
||||||
|
- name: Publish Library
|
||||||
|
run: |
|
||||||
|
if [ "${{github.ref_name}}" = "master" ]; then
|
||||||
|
REGISTRY="${{github.server_url}}/api/packages/${{github.repository_owner}}/npm/"
|
||||||
|
npm set registry "$REGISTRY"
|
||||||
|
npm set $(echo $REGISTRY | sed s%http:%% | sed s%https:%% ):_authToken "${{secrets.DEPLOY_TOKEN}}"
|
||||||
|
npm publish || echo "Failed to publish"
|
||||||
|
|
||||||
|
REGISTRY="https://registry.npmjs.org/"
|
||||||
|
npm set registry "$REGISTRY"
|
||||||
|
npm set $(echo $REGISTRY | sed s%http:%% | sed s%https:%% ):_authToken "${{secrets.NPM_TOKEN}}"
|
||||||
|
npm publish || echo "Failed to publish"
|
||||||
|
fi
|
||||||
|
tag:
|
||||||
|
name: Tag Version
|
||||||
|
needs: build
|
||||||
|
if: ${{github.ref_name}} == 'release'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: node:alpine
|
||||||
|
steps:
|
||||||
|
- name: Clone Repository
|
||||||
|
uses: ztimson/actions/clone@develop
|
||||||
|
|
||||||
|
- name: Get Version Number
|
||||||
|
run: echo "VERSION=$(cat package.json | grep version | grep -Eo ':.+' | grep -Eo '[[:alnum:]\.\/\-]+')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Tag Version
|
||||||
|
uses: ztimson/actions/tag@develop
|
||||||
|
with:
|
||||||
|
tag: ${{env.VERSION}}
|
||||||
28
.github/workflows/review.yml
vendored
Normal file
28
.github/workflows/review.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: Code review
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: node:22
|
||||||
|
steps:
|
||||||
|
- name: Fetch code
|
||||||
|
run: |
|
||||||
|
git clone "$(echo ${{github.server_url}}/${{github.repository}}.git | sed s%://%://${{github.token}}@% )" .
|
||||||
|
git checkout ${{ github.event.pull_request.head.sha }}
|
||||||
|
git fetch origin ${{ github.event.pull_request.base.ref }}
|
||||||
|
|
||||||
|
- name: Run AI Review
|
||||||
|
run: npx -y @ztimson/ai-agents@latest review
|
||||||
|
env:
|
||||||
|
AI_HOST: anthropic
|
||||||
|
AI_MODEL: claude-sonnet-4-5
|
||||||
|
AI_TOKEN: ${{ secrets.ANTHROPIC_TOKEN }}
|
||||||
|
GIT_HOST: ${{ github.server_url }}
|
||||||
|
GIT_OWNER: ${{ github.repository_owner }}
|
||||||
|
GIT_REPO: ${{ github.event.repository.name }}
|
||||||
|
GIT_TOKEN: ${{ secrets.ASSISTANT_TOKEN }}
|
||||||
|
GIT_BRANCH: origin/${{ github.event.pull_request.base.ref }}
|
||||||
|
PULL_REQUEST: ${{ github.event.pull_request.number }}
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.env.local
|
||||||
88
README.md
88
README.md
@@ -3,94 +3,46 @@
|
|||||||
<br />
|
<br />
|
||||||
|
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
<img src="https://git.zakscode.com/repo-avatars/2b4ee6ba1f2e2618bf7694e4a52fb56d1d0ea6abafa2dcbe496ab786b86d5a76" alt="Logo" width="200" height="200">
|
<img src="https://git.zakscode.com/repo-avatars/309c233243bcd1c1e9b3f359ec3f59769bb01b655e8ed7b32587781be4c8b21c" alt="Logo" width="200" height="200">
|
||||||
|
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
### Template
|
### AI Agents
|
||||||
|
|
||||||
<!-- Description -->
|
<!-- Description -->
|
||||||
Simple repository template
|
Automated AI-powered agents for automated reviews and code assistance
|
||||||
|
|
||||||
<!-- Repo badges -->
|
<!-- Repo badges -->
|
||||||
[](https://git.zakscode.com/ztimson/template/tags)
|
[](https://git.zakscode.com/ztimson/ai-agents/tags)
|
||||||
[](https://git.zakscode.com/ztimson/template/pulls)
|
[](https://git.zakscode.com/ztimson/ai-agents/pulls)
|
||||||
[](https://git.zakscode.com/ztimson/template/issues)
|
[](https://git.zakscode.com/ztimson/ai-agents/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-agents/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-agents/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-agents/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 Agents](#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 agents 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
|
||||||
[](https://angular.io/)
|
|
||||||
[](https://www.android.com/)
|
|
||||||
[](https://www.arduino.cc/)
|
|
||||||
[](https://getbootstrap.com)
|
|
||||||
[](https://en.cppreference.com/w/c/language)
|
|
||||||
[](https://cplusplus.com/)
|
|
||||||
[](https://dotnet.microsoft.com/)
|
|
||||||
[](https://www.w3.org/Style/CSS/Overview.en.html)
|
|
||||||
[](https://www.djangoproject.com/)
|
|
||||||
[](https://docker.com/)
|
[](https://docker.com/)
|
||||||
[](https://www.electronjs.org/)
|
|
||||||
[](https://firebase.google.com/)
|
|
||||||
[](https://go.dev/)
|
|
||||||
[](https://graphql.org/)
|
|
||||||
[](https://developer.mozilla.org/en-US/docs/Glossary/HTML)
|
|
||||||
[](https://java.com/)
|
|
||||||
[](https://javascript.com/)
|
[](https://javascript.com/)
|
||||||
[](https://jquery.com )
|
|
||||||
[](https://laravel.com)
|
|
||||||
[](https://www.linux.org/)
|
|
||||||
[](https://git.zakscode.com/ztimson/momentum)
|
|
||||||
[](https://www.mongodb.com/)
|
|
||||||
[](https://www.mysql.com/)
|
|
||||||
[](https://nestjs.com/)
|
|
||||||
[](https://dotnet.microsoft.com/)
|
|
||||||
[](https://nextjs.org/)
|
|
||||||
[](https://www.nginx.com/)
|
|
||||||
[](https://nodejs.org/)
|
[](https://nodejs.org/)
|
||||||
[](https://p5js.org/)
|
|
||||||
[](https://www.php.net/)
|
|
||||||
[](https://www.postgresql.org/)
|
|
||||||
[](https://www.python.org/)
|
|
||||||
[](https://reactjs.org/)
|
|
||||||
[](https://redis.com/)
|
|
||||||
[](https://sass-lang.com/)
|
|
||||||
[](https://en.wikipedia.org/wiki/Shell_script)
|
|
||||||
[](https://www.microsoft.com/en-ca/sql-server)
|
|
||||||
[](https://www.sqlite.org/index.html)
|
|
||||||
[](https://svelte.dev/)
|
|
||||||
[](https://typescriptlang.org/)
|
|
||||||
[](https://microsoft.com/windows)
|
|
||||||
[](https://vitejs.dev/)
|
|
||||||
[](https://vuejs.org/)
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
@@ -102,11 +54,11 @@ Website: https://git.zakscode.com
|
|||||||
</summary>
|
</summary>
|
||||||
|
|
||||||
#### Prerequisites
|
#### Prerequisites
|
||||||
- [Docker](https://docs.docker.com/install/)
|
- [Node.js](https://nodejs.org/en/download)
|
||||||
|
|
||||||
#### Instructions
|
#### Instructions
|
||||||
1. Run the docker image: `docker run -p 80:80 git.zakscode.com/ztimson/template:latest`
|
1. Run using npx: `npx -y @ztimson/ai-agents@latest review`
|
||||||
2. Open [http://localhost](http://localhost)
|
|
||||||
</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.
|
||||||
|
|||||||
1757
package-lock.json
generated
Normal file
1757
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
package.json
Normal file
21
package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "@ztimson/ai-agents",
|
||||||
|
"version": "0.0.3",
|
||||||
|
"description": "AI agents",
|
||||||
|
"keywords": ["ai", "review"],
|
||||||
|
"author": "ztimson",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "module",
|
||||||
|
"bin": {
|
||||||
|
"review": "./src/review.mjs"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@ztimson/ai-utils": "^0.2.4",
|
||||||
|
"@ztimson/node-utils": "^1.0.7",
|
||||||
|
"@ztimson/utils": "^0.28.3",
|
||||||
|
"dotenv": "^17.2.3"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/"
|
||||||
|
]
|
||||||
|
}
|
||||||
113
src/review.mjs
Normal file
113
src/review.mjs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
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';
|
||||||
|
import * as dotenv from 'dotenv';
|
||||||
|
|
||||||
|
dotenv.config({quiet: true, debug: false});
|
||||||
|
dotenv.config({path: '.env.local', override: true, quiet: true, debug: false});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
let p = process.argv[process.argv.length - 1];
|
||||||
|
if(p === 'review' || p.endsWith('review.mjs')) p = null;
|
||||||
|
|
||||||
|
const root = p || process.cwd(),
|
||||||
|
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'],
|
||||||
|
host = process.env['AI_HOST'],
|
||||||
|
model = process.env['AI_MODEL'],
|
||||||
|
token = process.env['AI_TOKEN'];
|
||||||
|
|
||||||
|
console.log(`Reviewing: ${root}\n`);
|
||||||
|
const branch = process.env['GIT_BRANCH'] || await $`cd ${root} && git symbolic-ref refs/remotes/origin/HEAD`;
|
||||||
|
const comments = [];
|
||||||
|
const commit = await $`cd ${root} && git log -1 --pretty=format:%H`;
|
||||||
|
const gitDiff = await $`cd ${root} && git diff ${branch}`;
|
||||||
|
|
||||||
|
if(!gitDiff) {
|
||||||
|
console.warn('No diff found');
|
||||||
|
return process.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
let existingComments = '';
|
||||||
|
if(git && pr) {
|
||||||
|
const res = await fetch(`${git}/api/v1/repos/${owner}/${repo}/pulls/${pr}/reviews`, {
|
||||||
|
headers: {'Authorization': `token ${auth}`}
|
||||||
|
});
|
||||||
|
if(res.ok) {
|
||||||
|
const reviews = await res.json();
|
||||||
|
const allComments = reviews.flatMap(r => r.comments || []);
|
||||||
|
if(allComments.length) {
|
||||||
|
existingComments = '\n\nExisting review comments (DO NOT repeat these):\n' +
|
||||||
|
allComments.map(c => `- ${c.path}:${c.line || c.position}: ${c.body}`).join('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 directly related to changes. Ignore formatting recommendations. After making all recommendations, provide some concluding remarks about the overall state of the changes.${existingComments}`,
|
||||||
|
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'},
|
||||||
|
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 messages = await ai.language.ask(gitDiff);
|
||||||
|
const summary = messages.pop().content;
|
||||||
|
if(git) {
|
||||||
|
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()}`);
|
||||||
|
}
|
||||||
|
console.log(comments.map(c => `${c.path}${c.new_position ? `:${c.new_position}` : ''}\n${c.body}`).join('\n\n') + '\n\n' + summary);
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user