@ztimson/ai-agents 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  ### AI Agents
10
10
 
11
11
  <!-- Description -->
12
- Automated AI-powered agents for automated reviews and code assistance
12
+ AI-powered Gitea agents for automating reviews and administration
13
13
 
14
14
  <!-- Repo badges -->
15
15
  [![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-agents/tags&query=$[0].name)](https://git.zakscode.com/ztimson/ai-agents/tags)
@@ -37,7 +37,9 @@ Automated AI-powered agents for automated reviews and code assistance
37
37
 
38
38
  ## About
39
39
 
40
- 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.
40
+ Only supports Gitea
41
+
42
+ Use LLM models from Anthropic, OpenAI, or Ollama to automate ticket refinement, code reviews, and releases.
41
43
 
42
44
  ### Built With
43
45
  [![Docker](https://img.shields.io/badge/Docker-384d54?style=for-the-badge&logo=docker)](https://docker.com/)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ztimson/ai-agents",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "AI agents",
5
5
  "keywords": ["ai", "review"],
6
6
  "author": "ztimson",
package/src/refine.mjs CHANGED
@@ -14,8 +14,6 @@ dotenv.config({path: '.env.local', override: true, quiet: true});
14
14
  if(p === 'refine' || p.endsWith('refine.mjs')) p = null;
15
15
  if(!/^(\/|[A-Z]:)/m.test(p)) p = path.join(process.cwd(), p);
16
16
 
17
- if(!p || !fs.existsSync(p)) throw new Error('Please provide a template');
18
-
19
17
  const git = process.env['GIT_HOST'],
20
18
  owner = process.env['GIT_OWNER'],
21
19
  repo = process.env['GIT_REPO'],
@@ -38,9 +36,49 @@ dotenv.config({path: '.env.local', override: true, quiet: true});
38
36
  return process.exit();
39
37
  }
40
38
 
41
- let readme = '', readmeP = path.join(process.cwd(), 'README.md');
39
+ let title = '', type = '', readme = '', readmeP = path.join(process.cwd(), 'README.md');
42
40
  if(fs.existsSync(readmeP)) readme = fs.readFileSync(readmeP, 'utf-8');
43
- const template = fs.readFileSync(p, 'utf-8');
41
+ const template = p ? fs.readFileSync(p, 'utf-8') : `## Description
42
+
43
+ A clear explanation of the request
44
+
45
+ ---
46
+
47
+ ## Current Behavior
48
+
49
+ what's happening now or the current state/gap
50
+
51
+ ## Expected Behavior
52
+
53
+ What should happen instead
54
+
55
+ ## Steps to Reproduce / Desired Flow
56
+
57
+ 1. First step
58
+ 2. Second step
59
+ 3. Third step
60
+
61
+ ## Additional Context
62
+
63
+ Logs, screenshots, links, related issues
64
+
65
+ ## Acceptance Criteria
66
+
67
+ - [ ] Todo requirement
68
+ - [X] Completed requirement
69
+
70
+ ## Technical Notes
71
+
72
+ Implementation details, constraints, dependencies, design decisions
73
+
74
+
75
+ | Effort / Weight | Score |
76
+ |-----------------|----------|
77
+ | Size | 0-5 |
78
+ | Complexity | 0-5 |
79
+ | Unknowns | 0-5 |
80
+ | **Total** | **0-15** |
81
+ `;
44
82
 
45
83
  let options = {ollama: {model, host}};
46
84
  if(host === 'anthropic') options = {anthropic: {model, token}};
@@ -49,26 +87,47 @@ dotenv.config({path: '.env.local', override: true, quiet: true});
49
87
  ...options,
50
88
  model: [host, model],
51
89
  path: process.env['path'] || os.tmpdir(),
52
- system: `You are a ticket formatter. Transform raw issue descriptions into structured tickets.
53
-
54
- **CRITICAL RULES:**
55
- 1. Identify the ticket type (Bug, DevOps, Enhancement, Refactor, Security)
56
- 2. Output MUST only contain the new ticket information in markdown, no extra fluff
57
- 3. Follow the template structure EXACTLY:
58
- - Title format: [Module] - [Verb] [noun]
59
- Example: Storage - Fix file uploads
60
- - Fill in the identified ticket type
61
- - Write a clear description
62
- - For bugs: fill Steps to Reproduce with numbered list
63
- - For enhancements/refactors: REMOVE the Steps to Reproduce section entirely
64
- - Acceptance Criteria: convert requirements into checkboxes (- [ ])
65
- - Weight scoring (0-5 each):
66
- * Size: Number of modules, layers & files affected by change
67
- * Complexity: Technical difficulty to implement
68
- * Unknowns: Research/uncertainty in work estimation
69
- * Calculate Total as sum of the three
70
- - Remove sections that are not applicable based on ticket type
71
- - Use proper markdown headers (##)
90
+ tools: [{
91
+ name: 'title',
92
+ description: 'Set the ticket title, must be called EXACTLY ONCE',
93
+ args: {title: {type: 'string', description: 'Ticket title, must match format: [Module] - [Verb] [noun]', required: true}},
94
+ fn: (args) => title = args.title
95
+ }, {
96
+ name: 'type',
97
+ description: 'Set the ticket type, must be called EXACTLY ONCE',
98
+ args: {type: {type: 'string', description: 'Ticket type', enum: ['Bug', 'DevOps', 'Document', 'Enhancement', 'Refactor', 'Security'], required: true}},
99
+ fn: (args) => type = args.type
100
+ }],
101
+ system: `Transform raw tickets into structured markdown following the template EXACTLY.
102
+
103
+ **MANDATORY STEPS:**
104
+ 1. Identify ticket type: Bug, DevOps, Document, Enhancement, Refactor, or Security
105
+ 2. Call \`type\` tool EXACTLY ONCE with the type from step 1
106
+ 3. Call \`title\` tool EXACTLY ONCE in format: "[Module] - [Verb] [subject]"
107
+ 4. Output formatted markdown matching template structure below
108
+
109
+ **TEMPLATE RULES:**
110
+ - Use ## headers (match template exactly)
111
+ - Description: Clear summary of the request
112
+ - Current Behavior: What's happening now (remove for Document tickets)
113
+ - Expected Behavior: What should happen (remove for Document tickets)
114
+ - Steps to Reproduce: Numbered list for bugs, flow for enhancements, remove if not applicable
115
+ - Additional Context: Logs, screenshots, links provided by user
116
+ - Acceptance Criteria: Convert to checkboxes (- [ ] format)
117
+ - Technical Notes: Implementation approach, constraints, dependencies
118
+ - Weight table (use exact format below):
119
+
120
+ | Effort / Weight | Score |
121
+ |-----------------|----------|
122
+ | Size | 0-5 |
123
+ | Complexity | 0-5 |
124
+ | Unknowns | 0-5 |
125
+ | **Total** | **0-15** |
126
+
127
+ **SCORING:**
128
+ - Size: # of modules/layers/files changed
129
+ - Complexity: Technical difficulty
130
+ - Unknowns: Research/uncertainty needed
72
131
 
73
132
  **README:**
74
133
  \`\`\`markdown
@@ -80,19 +139,14 @@ ${readme.trim() || 'No README available'}
80
139
  ${template.trim()}
81
140
  \`\`\`
82
141
 
83
- Output ONLY the formatted ticket, no explanation.`
84
- })
142
+ Output ONLY markdown. No explanations, labels, or extra formatting.`});
85
143
 
86
144
  const messages = await ai.language.ask(`Title: ${issueData.title}\n\nDescription:\n${issueData.body || 'No description provided'}`).catch(() => []);
87
- const content = messages?.pop()?.content;
88
- if(!content) {
145
+ const body = messages?.pop()?.content;
146
+ if(!body) {
89
147
  console.log('Invalid response from AI');
90
148
  return process.exit(1);
91
149
  }
92
- const title = /^# (.+)$/m.exec(content)?.[1] || issueData.title;
93
- const typeMatch = /^## Type:\s*(.+)$/m.exec(content);
94
- const type = typeMatch?.[1]?.split('/')[0]?.trim() || 'Unassigned';
95
- const body = content.replace(/^# .+$/m, '').replace(/^## Type:.+$/m, '').trim();
96
150
  const updateRes = await fetch(`${git}/api/v1/repos/${owner}/${repo}/issues/${ticket}`, {
97
151
  method: 'PATCH',
98
152
  headers: {
@@ -102,9 +156,20 @@ Output ONLY the formatted ticket, no explanation.`
102
156
  body: JSON.stringify({
103
157
  title,
104
158
  body,
105
- labels: type?.length ? [`Kind/${type[0].toUpperCase() + type.slice(1).toLowerCase()}`] : []
106
159
  })
107
160
  });
108
161
  if(!updateRes.ok) throw new Error(`${updateRes.status} ${await updateRes.text()}`);
109
- console.log(body);
162
+ if(type) {
163
+ const resp = await fetch(`${git}/api/v1/repos/${owner}/${repo}/issues/${ticket}/labels`, {
164
+ method: 'POST',
165
+ headers: {
166
+ 'Authorization': `token ${auth}`,
167
+ 'Content-Type': 'application/json'
168
+ },
169
+ body: JSON.stringify([`Kind/${type[0].toUpperCase() + type.slice(1).toLowerCase()}`])
170
+ });
171
+ if(!resp.ok) throw new Error(`${resp.status} ${await resp.text()}`);
172
+ }
173
+
174
+ console.log(`Title: ${title}\nType: ${type}\nBody:\n${body}`);
110
175
  })();
package/src/review.mjs CHANGED
@@ -35,19 +35,15 @@ dotenv.config({path: '.env.local', override: true, quiet: true, debug: false});
35
35
  return process.exit();
36
36
  }
37
37
 
38
- let existingComments = '';
38
+ let existingComments = 'Existing Comments:\n';
39
39
  if(git && pr) {
40
- const res = await fetch(`${git}/api/v1/repos/${owner}/${repo}/pulls/${pr}/reviews`, {
40
+ const reviews = await fetch(`${git}/api/v1/repos/${owner}/${repo}/pulls/${pr}/reviews`, {
41
41
  headers: {'Authorization': `token ${auth}`}
42
- });
43
- if(res.ok) {
44
- const reviews = await res.json();
45
- const allComments = reviews.flatMap(r => r.comments || []);
46
- if(allComments.length) {
47
- existingComments = '\n\nExisting review comments (DO NOT repeat these):\n' +
48
- allComments.map(c => `- ${c.path}:${c.line || c.position}: ${c.body}`).join('\n');
49
- }
50
- }
42
+ }).then(resp => resp.ok ? resp.json() : []);
43
+ const comments = await Promise.all(reviews.map(r => fetch(`${git}/api/v1/repos/${owner}/${repo}/pulls/${pr}/reviews/${r.id}/comments`, {
44
+ headers: {'Authorization': `token ${auth}`}
45
+ }).then(resp => resp.ok ? resp.json() : [])));
46
+ existingComments += comments.flatten().map(c => `${c.path}:${c.position}\n${c.body}`).join('\n\n');
51
47
  }
52
48
 
53
49
  let options = {ollama: {model, host}};