6 Commits

Author SHA1 Message Date
19392b70a6 Bump
All checks were successful
Publish Library / Build NPM Project (push) Successful in 6s
Publish Library / Tag Version (push) Successful in 5s
2026-01-14 16:02:36 -05:00
e3d38d2df8 Updated readme
All checks were successful
Publish Library / Build NPM Project (push) Successful in 5s
Publish Library / Tag Version (push) Successful in 5s
2026-01-14 16:00:07 -05:00
9ff7beee1e Fixed output
All checks were successful
Publish Library / Build NPM Project (push) Successful in 6s
Publish Library / Tag Version (push) Successful in 5s
2026-01-14 15:56:44 -05:00
8cfcb3f95c Added checks
All checks were successful
Publish Library / Build NPM Project (push) Successful in 4s
Publish Library / Tag Version (push) Successful in 16s
Code review / review (pull_request) Successful in 38s
2026-01-14 15:56:07 -05:00
e625782eec Removed --no-cache, we are already using latest tag
All checks were successful
Publish Library / Build NPM Project (push) Successful in 3s
Publish Library / Tag Version (push) Successful in 5s
Code review / review (pull_request) Successful in 40s
2026-01-14 15:47:54 -05:00
72ffe3dcc7 Added a release bot to create release notes from closed milestones
All checks were successful
Publish Library / Build NPM Project (push) Successful in 3s
Publish Library / Tag Version (push) Successful in 5s
2026-01-14 15:43:53 -05:00
7 changed files with 127 additions and 6 deletions

View File

@@ -15,7 +15,7 @@ jobs:
git checkout ${{ github.event.pull_request.head.sha }}
git fetch origin ${{ github.event.pull_request.base.ref }}
- name: Run AI Review
- name: Create review
run: npx -y -p @ztimson/ai-agents@latest review $GITHUB_WORKSPACE
env:
AI_HOST: anthropic

27
.github/workflows/release-creator.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Release Bot
on:
milestone:
types: [closed]
jobs:
release:
runs-on: ubuntu-latest
container: node:22
steps:
- name: Checkout
run: |
git clone "$(echo ${{github.server_url}}/${{github.repository}}.git | sed s%://%://${{github.token}}@% )" .
git checkout ${{ github.event.repository.default_branch }}
- name: Create release
run: npx -y @ztimson/ai-agents@latest release
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 }}
MILESTONE: ${{ github.event.milestone.number }}

View File

@@ -14,7 +14,7 @@ jobs:
git clone "$(echo ${{github.server_url}}/${{github.repository}}.git | sed s%://%://${{github.token}}@% )" .
git checkout ${{ github.event.repository.default_branch }}
- name: Run AI Formatter
- name: Refine ticket
run: npx -y -p @ztimson/ai-agents@latest refine
env:
AI_HOST: anthropic

View File

@@ -37,9 +37,12 @@ AI-powered Gitea agents for automating reviews and administration
## About
Only supports Gitea
AI-powered Gitea agents for automating administration of code repos:
- Code Review
- Release Notes
- Ticket Refinement
Use LLM models from Anthropic, OpenAI, or Ollama to automate ticket refinement, code reviews, and releases.
Only supports Gitea, copy the relevant `.github/workflows/______.yml` action to start using it
### Built With
[![Docker](https://img.shields.io/badge/Docker-384d54?style=for-the-badge&logo=docker)](https://docker.com/)

View File

@@ -1,6 +1,6 @@
{
"name": "@ztimson/ai-agents",
"version": "0.1.2",
"version": "0.1.4",
"description": "AI agents",
"keywords": ["ai", "review"],
"author": "ztimson",
@@ -8,6 +8,7 @@
"type": "module",
"bin": {
"refine": "./src/refine.mjs",
"release": "./src/release.mjs",
"review": "./src/review.mjs"
},
"dependencies": {

90
src/release.mjs Normal file
View File

@@ -0,0 +1,90 @@
#!/usr/bin/env node
import {Ai} from '@ztimson/ai';
import * as dotenv from 'dotenv';
import {$} from '@ztimson/node-utils';
dotenv.config({quiet: true, debug: false});
dotenv.config({path: '.env.local', override: true, quiet: true, debug: false});
(async () => {
const git = process.env['GIT_HOST'],
owner = process.env['GIT_OWNER'],
repo = process.env['GIT_REPO'],
auth = process.env['GIT_TOKEN'],
milestone = process.env['MILESTONE'],
host = process.env['AI_HOST'] || 'ollama',
model = process.env['AI_MODEL'] || 'llama3',
token = process.env['AI_TOKEN'];
// Get milestone info
const milestoneData = await fetch(`${git}/api/v1/repos/${owner}/${repo}/milestones/${milestone}`, {
headers: {'Authorization': `token ${auth}`}
}).then(resp => resp.ok ? resp.json() : {});
// Get closed issues
const issues = await fetch(`${git}/api/v1/repos/${owner}/${repo}/issues?state=closed&milestone=${milestone}`, {
headers: {'Authorization': `token ${auth}`}
}).then(resp => resp.ok ? resp.json() : []);
// Get closed PRs
const prs = await fetch(`${git}/api/v1/repos/${owner}/${repo}/pulls?state=closed&milestone=${milestone}`, {
headers: {'Authorization': `token ${auth}`}
}).then(resp => resp.ok ? resp.json() : []);
// Get latest tag
const latestTag = await $`git describe --tags --abbrev=0`.text();
if(!latestTag) {
console.error('At least one Git tag is required');
process.exit(1);
}
// Build context
let context = `Milestone: ${milestoneData.title}
Description:
\`\`\`md
${milestoneData.description || ''}
\`\`\`
PRs:
${prs.map(pr => `- ${pr.title}\n${pr.body || ''}`).join('\n\n')}
Issues:
${issues.filter(i => !i.pull_request).map(i => `- ${i.title}\n${i.body || ''}`).join('\n\n')}`;
// Generate release notes
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],
system: `You are a release notes writer. Format the provided milestone info, PRs, and issues into clean, organized release notes. Use markdown with sections like "Features", "Bug Fixes", "Breaking Changes", etc. Be concise but informative. Include issue/PR numbers in format #123.`
});
const body = (await ai.chat(context)).pop()?.content;
if(!body) {
console.error('No release notes were generated');
process.exit(1);
}
// Create release
const name = latestTag.trim();
await fetch(`${git}/api/v1/repos/${owner}/${repo}/releases`, {
method: 'POST',
headers: {
'Authorization': `token ${auth}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name,
tag_name: name,
body
})
}).then(resp => { if(!resp.ok) throw new Error(resp.status + ' ' + resp.statusText) });
console.log(`Title: ${name}\nDescription:\n${body}`);
})().catch(err => {
console.error(`Error: ${err.message || err.toString()}`);
process.exit(1);
});

View File

@@ -25,7 +25,7 @@ dotenv.config({path: '.env.local', override: true, quiet: true, debug: false});
model = process.env['AI_MODEL'],
token = process.env['AI_TOKEN'];
console.log(`Reviewing: ${root}\n`);
console.log(`Reviewing: ${root}`);
const info = await fetch(`${git}/api/v1/repos/${owner}/${repo}/pulls/${pr}`)
.then(async resp => {
if(resp.ok) return resp.json();