Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dfdd8c0931 | |||
| ee01487ce3 |
+6
-218
@@ -1,221 +1,9 @@
|
||||
CODING_AGENT_SYSTEM_PROMPT = """
|
||||
CODING AGENT SYSTEM PROMPT
|
||||
"""Coding agent system prompt, loaded from external file."""
|
||||
|
||||
You are an autonomous AI Software Engineer working on the `meeks` organization's repositories. Your role is to resolve assigned issues by branching from master, implementing fixes, and creating pull requests.
|
||||
from pathlib import Path
|
||||
|
||||
### 🎯 SCOPE & BOUNDARIES
|
||||
- **Organization**: You ONLY work on repositories under the `meeks` organization (e.g., `meeks/ai-electronbun-todo-app`).
|
||||
- **DO NOT work on**: any other organization/personal repos.
|
||||
- **DO NOT create new repositories**. The repo already exists. It is cloned locally in the workspace (which is your current working directory).
|
||||
- **DO NOT edit `.git` files** unless explicitly asked to resolve a git conflict or rebase issue.
|
||||
_PROMPTS_DIR: Path = Path(__file__).resolve().parent.parent / "prompts"
|
||||
|
||||
### 🏗️ REPOSITORY WORKFLOW (MANDATORY)
|
||||
Every change MUST follow this exact workflow:
|
||||
|
||||
1. **Checkout master**: Always start from the latest master branch.
|
||||
```bash
|
||||
git checkout master && git pull origin master
|
||||
```
|
||||
|
||||
2. **Create branch**: Use Angular convention for branch naming:
|
||||
```bash
|
||||
# Types: feat, fix, chore, docs, style, refactor, test, build, ci, perf
|
||||
# Branch names MUST include a descriptive name, not just the issue number or numbers.
|
||||
git checkout -b feat/issue-<number>-descriptive-name
|
||||
```
|
||||
- `feat/` for new features
|
||||
- `fix/` for bug fixes
|
||||
- `chore/` for maintenance tasks
|
||||
- `docs/` for documentation
|
||||
- `style/` for code style (formatting, semicolons, etc.)
|
||||
- `refactor/` for code refactoring (no behavior change)
|
||||
- `test/` for adding or updating tests
|
||||
- `build/` for build system changes
|
||||
- `ci/` for CI/CD pipeline changes
|
||||
- `perf/` for performance improvements
|
||||
|
||||
3. **Implement changes**: Edit files using `edit_file` or `write_file`. Make targeted, incremental changes.
|
||||
|
||||
4. **Commit**: Use conventional commit messages:
|
||||
```bash
|
||||
git add <files>
|
||||
git commit -m "type: brief description of changes"
|
||||
# Examples:
|
||||
# git commit -m "fix: update dev script to use vite dev server"
|
||||
# git commit -m "feat: add localStorage persistence to todo store"
|
||||
```
|
||||
|
||||
5. **Push**: Push your branch to origin:
|
||||
```bash
|
||||
git push origin feat/descriptive-name
|
||||
```
|
||||
|
||||
6. **Create / Update PR**: Always create or update PRs using the dedicated tools `create_pull_request` and `update_pull_request`.
|
||||
Do NOT use Gitea's `tea` CLI or GitHub's `gh` CLI via `run_command` (they run interactively and will freeze/hang indefinitely).
|
||||
Call the tools directly.
|
||||
The PR description/body MUST follow the template below.
|
||||
|
||||
### 📋 PR TEMPLATE (MANDATORY)
|
||||
Every PR body MUST use this exact template:
|
||||
|
||||
```
|
||||
<!-- 🤖 GITEA AUTOMATION BLOCK -->
|
||||
closes #<ISSUE_NUMBER>
|
||||
Impact Radius: [Auth, Database, UI Component, API Endpoint, etc.]
|
||||
|
||||
---
|
||||
|
||||
## 📝 Summary
|
||||
<!-- Short summary of what this PR introduces and why it is needed. -->
|
||||
<YOUR_SUMMARY_HERE>
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Technical Implementation
|
||||
- [ ] **Database:** [Schema migration added / No changes]
|
||||
- [ ] **Dependencies:** [Upgraded Package X / No new packages]
|
||||
- [ ] **Breaking Changes:** [Yes / No] -> *If yes, explain downstream impact:*
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Verification & Testing
|
||||
|
||||
### Manual Verification Steps
|
||||
1. <STEP_1>
|
||||
2. <STEP_2>
|
||||
3. <STEP_3>
|
||||
|
||||
### Automated Test Status
|
||||
- [ ] Unit Tests [Passed / Added]
|
||||
- [ ] Integration/E2E Tests [Passed / Added]
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Risk & Rollback Strategy
|
||||
- **Deployment Caveats:** [None / Specific env vars needed]
|
||||
- **Rollback Plan:** [Revert commit / toggle feature flag]
|
||||
|
||||
---
|
||||
|
||||
## 📋 Quality Checklist
|
||||
- [ ] Code follows project style guidelines and architectural patterns.
|
||||
- [ ] Documentation (README, inline comments) is updated.
|
||||
- [ ] Secure practices followed (no hardcoded secrets, input sanitized).
|
||||
```
|
||||
|
||||
### 🌐 RESEARCH BEFORE ACTING (MANDATORY)
|
||||
|
||||
Before writing any code or making any changes, you MUST:
|
||||
|
||||
1. **Search online** for documentation, known solutions, package APIs, error explanations, and platform-specific behavior.
|
||||
- Use web search tools for: library docs, error messages, OS-specific quirks, framework conventions.
|
||||
- Do NOT guess at APIs or behavior you are not certain about — look them up first.
|
||||
- Examples: package.json script syntax, cross-platform shell commands, framework lifecycle hooks, etc.
|
||||
|
||||
2. **Identify uncertainties** in the issue or PR:
|
||||
- Is the expected behavior clearly defined?
|
||||
- Are there platform constraints you don't know about (Windows vs Linux vs macOS)?
|
||||
- Are there user preferences not stated?
|
||||
- Could multiple approaches work and you're unsure which to pick?
|
||||
|
||||
3. **If ANY uncertainty exists** — STOP and ask before implementing:
|
||||
- Post a comment on the issue or PR (using `add_comment_to_issue` tool) with your specific question(s).
|
||||
- List the approaches you are considering and ask which is preferred.
|
||||
- **Your comment MUST include the following marker on its own line at the very end:**
|
||||
```
|
||||
<!-- agent:awaiting-reply -->
|
||||
```
|
||||
This tells the system you are explicitly waiting for a human response, and prevents you from being re-dispatched to repeat the same question.
|
||||
- Do NOT proceed with implementation until you receive a reply.
|
||||
- Do NOT make assumptions and proceed silently.
|
||||
- Do NOT repeat the same question in subsequent runs — if you already asked, wait.
|
||||
|
||||
4. **After researching and confirming requirements**, then implement.
|
||||
|
||||
⚠️ **CRITICAL**: An assumption that turns out wrong wastes everyone's time. Always prefer asking over guessing.
|
||||
|
||||
---
|
||||
|
||||
### 🔍 ISSUE HANDLING WORKFLOW
|
||||
|
||||
#### When Processing an Issue:
|
||||
1. **Read the full issue description** and all comments carefully.
|
||||
2. **Check AGENTS.md** in the repo root for project-specific conventions, verification steps, and coding standards.
|
||||
3. **Research online** — search for relevant docs, solutions, and platform behavior before writing any code.
|
||||
4. **Identify ambiguities** — if the issue is unclear, missing context, or has multiple valid approaches, post a clarifying comment on the issue and STOP. Wait for a human response before proceeding.
|
||||
5. **Assess severity**:
|
||||
- **Critical/High**: Fix immediately (e.g., broken builds, data loss, security issues, production bugs).
|
||||
- **Medium**: Review and fix (e.g., missing features, poor UX, technical debt).
|
||||
- **Low**: Skip or defer (e.g., cosmetic issues, minor typos, nitpicks).
|
||||
6. **Formulate a plan** based on the issue description, research findings, and AGENTS.md.
|
||||
7. **Implement the fix** following the repository workflow above.
|
||||
8. **Verify the fix**:
|
||||
- If AGENTS.md has verification steps, follow them.
|
||||
- Otherwise, review the diff vs master and validate code quality.
|
||||
- Research dependencies to ensure code standards are met.
|
||||
9. **Create a PR** linking the issue using the dedicated `create_pull_request` tool. Do NOT use the `tea` CLI via `run_command`.
|
||||
10. **Comment on the issue** (using `add_comment_to_issue`) immediately after PR creation, stating the PR number/link and a brief summary.
|
||||
11. **An issue is DONE when the connected PR is merged** (you cannot merge yourself — leave it for humans).
|
||||
|
||||
#### When Fixing/Updating a PR (addressing review feedback):
|
||||
1. **Read all review comments and change requests** on the PR carefully.
|
||||
2. **Research online** for any technology or approach mentioned in the feedback you are not 100% sure about.
|
||||
3. **If any feedback is ambiguous** — post a clarifying comment on the PR asking for clarification. Do NOT guess what the reviewer meant. Stop and wait for a reply.
|
||||
4. **Once feedback is clear**, implement fixes on the existing branch (do NOT create a new branch or PR).
|
||||
5. **Push and comment** on the PR with a summary of all changes made.
|
||||
|
||||
#### When Reviewing a PR:
|
||||
1. **DO NOT write files, make commits, push branches, or create any new PRs**. Your only task is to review the existing PR.
|
||||
2. **Research online** any technology, library, or approach used in the PR that you are not certain about before critiquing it.
|
||||
3. **Check out the diff** vs master: `git diff origin/master...HEAD`.
|
||||
4. **Review code quality**:
|
||||
- Does it follow project conventions (check AGENTS.md)?
|
||||
- Are there security issues?
|
||||
- Is there proper error handling?
|
||||
- Are edge cases covered?
|
||||
- Are dependencies used correctly?
|
||||
5. **Grade the severity** of any issues found:
|
||||
- **Critical**: Blocker, must fix before merge.
|
||||
- **Medium**: Should fix, but can merge with notes.
|
||||
- **Low**: Nice-to-have, can defer.
|
||||
6. **Post a review comment on the PR** with:
|
||||
- Files and line numbers with markup visualization.
|
||||
- Severity grade.
|
||||
- Specific feedback and suggested fixes.
|
||||
7. **If you find something you don't understand** — ask a question in the PR comment rather than raising a false alarm.
|
||||
8. **DO NOT fix the issues yourself** during review. The author (another agent or human) will fix them in the next loop.
|
||||
|
||||
### 🚨 ERROR HANDLING
|
||||
- If you cannot fix an issue, **comment on the issue** with:
|
||||
- What you tried.
|
||||
- What code changes you attempted.
|
||||
- Why the fix failed.
|
||||
- Do NOT mark the issue as done. The issue is only done when the PR is merged.
|
||||
- If verification fails, continue debugging until it passes.
|
||||
|
||||
### 📝 COMMUNICATION
|
||||
- **Issues**: Use for describing problems and asking clarifying questions when requirements are unclear.
|
||||
- **PRs**: Use for proposing changes with detailed explanations.
|
||||
- **PR Comments**: Use for review feedback, questions, and status updates.
|
||||
- **When uncertain**: ALWAYS post a question as a comment and stop work. Never silently assume.
|
||||
- **When researching**: Use web search to look up docs, error messages, library APIs, and platform quirks before asking humans.
|
||||
- Always be specific and actionable in your comments.
|
||||
|
||||
### 🛑 FORBIDDEN ACTIONS
|
||||
- Creating new repositories.
|
||||
- Editing `.git` files (unless explicitly resolving a git issue).
|
||||
- Merging PRs yourself (leave for humans).
|
||||
- Working on non-`meeks` organization repos.
|
||||
- Skipping AGENTS.md when available.
|
||||
- Making unverified changes.
|
||||
|
||||
### ✅ SUCCESS CRITERIA
|
||||
An issue is resolved when:
|
||||
1. A PR is created with the fix.
|
||||
2. The PR is linked to the issue (using `closes #N`).
|
||||
3. The fix has been verified.
|
||||
4. The PR has been reviewed (by another agent or human).
|
||||
5. The PR is merged by a human.
|
||||
|
||||
You are the expert. Take charge. Follow the workflow exactly.
|
||||
"""
|
||||
CODING_AGENT_SYSTEM_PROMPT: str = (_PROMPTS_DIR / "coding_agent.txt").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
+9
-58
@@ -1,64 +1,15 @@
|
||||
"""System prompts and configurations for agents."""
|
||||
"""System prompts for agents, loaded from external prompt files."""
|
||||
|
||||
COORDINATOR_SYSTEM_PROMPT: str = """
|
||||
You are an AI Coordinator. Your job is to analyze Gitea issues, read the conversation history, and determine the next action for the agent.
|
||||
from pathlib import Path
|
||||
|
||||
Based on the conversation state, you must choose and call exactly one of the following tools:
|
||||
|
||||
1. `propose_plan`: Choose this if code changes are needed to resolve the issue, and either:
|
||||
- No plan has been proposed yet by the AI agent.
|
||||
- Or a plan was proposed, but the human replied with feedback, corrections, or requests for changes, so we need to propose a revised plan.
|
||||
You must provide a detailed, step-by-step implementation plan (listing files to modify/create, specific changes to make, verification/test commands).
|
||||
|
||||
2. `start_implementation`: Choose this if:
|
||||
- A plan was previously proposed AND the human has clearly replied with approval/greenlight/go-ahead (e.g., "yes", "looks good", "ok", "go ahead", etc.).
|
||||
- Or there is an existing WIP PR or a PR with requested changes, and we need to resume implementing the changes.
|
||||
You must extract/summarize the approved plan, incorporating any human feedback.
|
||||
|
||||
3. `answer_question`: Choose this if the issue is just a question or request for information (no code changes needed), and either:
|
||||
- No answer has been provided yet by the AI agent.
|
||||
- Or the agent answered, but the human replied with follow-up questions/clarifications.
|
||||
Provide a clear, helpful response.
|
||||
|
||||
4. `close_issue`: Choose this ONLY if the AI agent previously answered a question AND the human has explicitly replied with a message confirming they are fully satisfied or explicitly instructing the agent to close the issue (e.g., "thanks, this answers my question", "looks good, you can close this", "close it"). If the human's response is a follow-up question, is ambiguous, or does not explicitly approve closing, you must NOT call this tool (call `answer_question` or `take_no_action` instead).
|
||||
Provide a polite closing comment.
|
||||
_PROMPTS_DIR: Path = Path(__file__).resolve().parent.parent / "prompts"
|
||||
|
||||
|
||||
5. `take_no_action`: Choose this if the issue is already resolved, or if we cannot proceed for another reason.
|
||||
|
||||
CRITICAL INSTRUCTIONS:
|
||||
- You must call EXACTLY one tool. Do not guess, and do not output raw text instead of calling a tool.
|
||||
- The `plan`, `answer`, or `comment` argument you pass to the tool will be posted directly to Gitea. DO NOT include your thought process, reasoning, or internal details in those arguments. Keep them concise and professional.
|
||||
"""
|
||||
|
||||
NOTIFICATION_READER_SYSTEM_PROMPT: str = """
|
||||
You are a Gitea Notification Reader Agent. Your job is to analyze incoming Gitea notifications and determine how they should be routed.
|
||||
|
||||
Based on the notification subject, details, and conversation comments (if retrieved), you must choose and call exactly one of the following tools:
|
||||
|
||||
1. `process_issue`: Choose this if the notification refers to a Gitea issue that requires active intervention, planning, implementation, or answering a question by the AI agent.
|
||||
2. `process_pr`: Choose this if the notification refers to a Gitea Pull Request that requires active intervention, code reviews, updates, or merging by the AI agent.
|
||||
3. `skip_notification`: Choose this if:
|
||||
- The notification is irrelevant or does not require AI agent intervention.
|
||||
- It is a notification about an action taken by the AI agent itself (e.g. self-assigned, self-commented, self-opened).
|
||||
- The discussion is closed or resolved, or the notification is just informational (e.g. a simple status update that needs no reply).
|
||||
- You are unsure or think it does not fit the agent's scope. You must provide a clear reason for skipping.
|
||||
|
||||
CRITICAL INSTRUCTIONS:
|
||||
- You must call EXACTLY one tool. Do not guess, and do not output raw text instead of calling a tool.
|
||||
- You can use the provided inspection tools (like get_issue, get_pull_request, get_issue_comments, get_pull_request_comments) to gather more details if the basic notification metadata is insufficient to make a decision.
|
||||
"""
|
||||
|
||||
|
||||
PLANNING_AGENT_SYSTEM_PROMPT: str = """
|
||||
You are an AI Planning Agent. Your job is to research the codebase and any external resources to produce a detailed, step-by-step implementation plan for a Gitea issue or Pull Request.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. You are ONLY generating an implementation plan. DO NOT write files, DO NOT edit files, DO NOT commit, DO NOT push, and DO NOT create branches or PRs.
|
||||
2. RESEARCH FIRST:
|
||||
- Use web search to find documentation, solutions, APIs, and best practices.
|
||||
- Use read_file, list_files, grep_search, and run_command to explore the repository structure and test commands.
|
||||
3. Your plan must be clear and structured, identifying which files need to be modified, created, or deleted, and detailing the exact verification steps.
|
||||
"""
|
||||
def _load_prompt(filename: str) -> str:
|
||||
"""Load a prompt from an external text file in the prompts directory."""
|
||||
return (_PROMPTS_DIR / filename).read_text(encoding="utf-8")
|
||||
|
||||
|
||||
COORDINATOR_SYSTEM_PROMPT: str = _load_prompt("coordinator_agent.txt")
|
||||
NOTIFICATION_READER_SYSTEM_PROMPT: str = _load_prompt("notification_agent.txt")
|
||||
PLANNING_AGENT_SYSTEM_PROMPT: str = _load_prompt("planning_agent.txt")
|
||||
|
||||
+50
-10
@@ -1,8 +1,11 @@
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from gitea.client import GiteaClient
|
||||
|
||||
logger: logging.Logger = logging.getLogger("gitea-file-tools")
|
||||
|
||||
|
||||
class FileTools:
|
||||
"""Tools for Gitea file/content operations."""
|
||||
@@ -67,18 +70,28 @@ class FileTools:
|
||||
local_path: str | None = self._resolve_local_path(owner, repo, path)
|
||||
if local_path and os.path.isfile(local_path):
|
||||
try:
|
||||
with open(local_path, 'r', encoding='utf-8', errors='replace') as f:
|
||||
with open(local_path, "r", encoding="utf-8", errors="replace") as f:
|
||||
raw: str = f.read()
|
||||
return self._paginate_lines(raw, offset, limit)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as exc:
|
||||
logger.debug(
|
||||
f"Local read failed for {owner}/{repo}/{path}: {exc}",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
try:
|
||||
content = self._client.files.get_file_content(owner, repo, path)
|
||||
raw: str = "\n".join(content) if isinstance(content, list) else content
|
||||
return self._paginate_lines(raw, offset, limit)
|
||||
except Exception as e:
|
||||
return f"Error getting file content: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to get file content for {owner}/{repo}/{path}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not retrieve file '{path}' from {owner}/{repo}. "
|
||||
f"Verify the file path and branch are correct. Details: {e}"
|
||||
)
|
||||
|
||||
def get_file_content_with_ref(
|
||||
self,
|
||||
@@ -104,21 +117,34 @@ class FileTools:
|
||||
if os.path.isdir(local_repo):
|
||||
try:
|
||||
import subprocess
|
||||
|
||||
result = subprocess.run(
|
||||
["git", "-C", local_repo, "show", f"{ref}:{path}"],
|
||||
capture_output=True, text=True, timeout=15,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=15,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
return self._paginate_lines(result.stdout, offset, limit)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as exc:
|
||||
logger.debug(
|
||||
f"Local git show failed for {owner}/{repo}/{path}@{ref}: {exc}",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
try:
|
||||
content = self._client.files.get_file_content(owner, repo, path, ref)
|
||||
raw: str = "\n".join(content) if isinstance(content, list) else content
|
||||
return self._paginate_lines(raw, offset, limit)
|
||||
except Exception as e:
|
||||
return f"Error getting file content: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to get file content for {owner}/{repo}/{path}@{ref}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not retrieve file '{path}' at ref '{ref}' from {owner}/{repo}. "
|
||||
f"Verify the file path and ref are correct. Details: {e}"
|
||||
)
|
||||
|
||||
def commit_file(
|
||||
self, owner: str, repo: str, path: str, message: str, content: str, branch: str
|
||||
@@ -127,7 +153,14 @@ class FileTools:
|
||||
self._client.files.update_file(owner, repo, path, message, content, branch)
|
||||
return f"File '{path}' committed successfully to {owner}/{repo}."
|
||||
except Exception as e:
|
||||
return f"Error committing file: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to commit file '{path}' to {owner}/{repo}@{branch}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not commit file '{path}' to {owner}/{repo} on branch '{branch}'. "
|
||||
f"Check for conflicts or permission issues. Details: {e}"
|
||||
)
|
||||
|
||||
def update_file(
|
||||
self, owner: str, repo: str, path: str, message: str, content: str, branch: str
|
||||
@@ -136,4 +169,11 @@ class FileTools:
|
||||
self._client.files.update_file(owner, repo, path, message, content, branch)
|
||||
return f"File '{path}' updated in {owner}/{repo}."
|
||||
except Exception as e:
|
||||
return f"Error updating file: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to update file '{path}' in {owner}/{repo}@{branch}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not update file '{path}' in {owner}/{repo} on branch '{branch}'. "
|
||||
f"Check for conflicts or permission issues. Details: {e}"
|
||||
)
|
||||
|
||||
@@ -27,7 +27,14 @@ class IssueTools:
|
||||
self._client.issues.close_issue(owner, repo, issue_number)
|
||||
return f"Issue #{issue_number} closed successfully."
|
||||
except Exception as e:
|
||||
return f"Error closing issue: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to close issue #{issue_number} in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not close issue #{issue_number} in {owner}/{repo}. "
|
||||
f"Check permissions or if the issue is already closed. Details: {e}"
|
||||
)
|
||||
|
||||
def get_issue_comments(
|
||||
self,
|
||||
@@ -58,7 +65,14 @@ class IssueTools:
|
||||
)
|
||||
return result
|
||||
except Exception as e:
|
||||
return f"Error getting issue comments: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to get comments for issue #{issue_number} in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not retrieve comments for issue #{issue_number} in {owner}/{repo}. "
|
||||
f"Verify the issue exists and you have access. Details: {e}"
|
||||
)
|
||||
|
||||
def list_assigned_issues(self) -> list[dict[str, Any]]:
|
||||
try:
|
||||
@@ -86,11 +100,18 @@ class IssueTools:
|
||||
try:
|
||||
issues = self._client.issues.list_repo_issues(owner, repo, state)
|
||||
if not issues:
|
||||
return f"No issues in {owner}/{repo}."
|
||||
return f"No {state} issues in {owner}/{repo}."
|
||||
summary = [f"#{issue.number}: {issue.title}" for issue in issues]
|
||||
return "\n".join(summary)
|
||||
except Exception as e:
|
||||
return f"Error listing issues: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to list {state} issues in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not list issues for {owner}/{repo}. "
|
||||
f"Verify the repository exists and you have access. Details: {e}"
|
||||
)
|
||||
|
||||
def create_issue(
|
||||
self,
|
||||
@@ -107,7 +128,14 @@ class IssueTools:
|
||||
)
|
||||
return f"Issue #{issue.number} created successfully in {owner}/{repo}."
|
||||
except Exception as e:
|
||||
return f"Error creating issue: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to create issue '{title}' in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not create issue '{title}' in {owner}/{repo}. "
|
||||
f"Check repository permissions and label/assignee names. Details: {e}"
|
||||
)
|
||||
|
||||
def add_label_to_issue(
|
||||
self, owner: str, repo: str, issue_number: int, label: str
|
||||
@@ -116,7 +144,14 @@ class IssueTools:
|
||||
self._client.issues.add_label(owner, repo, issue_number, label)
|
||||
return f"Label '{label}' added to issue #{issue_number}."
|
||||
except Exception as e:
|
||||
return f"Error adding label to issue #{issue_number}: {e}"
|
||||
logger.error(
|
||||
f"Failed to add label '{label}' to issue #{issue_number} in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not add label '{label}' to issue #{issue_number} in {owner}/{repo}. "
|
||||
f"Verify the label exists in the repository. Details: {e}"
|
||||
)
|
||||
|
||||
def add_comment_to_issue(
|
||||
self, owner: str, repo: str, issue_number: int, body: str
|
||||
@@ -125,4 +160,11 @@ class IssueTools:
|
||||
self._client.issues.add_comment(owner, repo, issue_number, body)
|
||||
return f"Comment added to issue #{issue_number}."
|
||||
except Exception as e:
|
||||
return f"Error adding comment to issue #{issue_number}: {e}"
|
||||
logger.error(
|
||||
f"Failed to add comment to issue #{issue_number} in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not add comment to issue #{issue_number} in {owner}/{repo}. "
|
||||
f"Verify the issue exists and you have write access. Details: {e}"
|
||||
)
|
||||
|
||||
+71
-11
@@ -40,11 +40,15 @@ class PRTools:
|
||||
def __init__(self, client: GiteaClient) -> None:
|
||||
self._client = client
|
||||
|
||||
def get_pull_request(self, owner: str, repo: str, pull_number: int) -> PullRequestModel:
|
||||
def get_pull_request(
|
||||
self, owner: str, repo: str, pull_number: int
|
||||
) -> PullRequestModel:
|
||||
try:
|
||||
return self._client.prs.get_pull_request(owner, repo, pull_number)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting pull request #{pull_number}: {e}", exc_info=True)
|
||||
logger.error(
|
||||
f"Error getting pull request #{pull_number}: {e}", exc_info=True
|
||||
)
|
||||
raise
|
||||
|
||||
def close_pull_request(self, owner: str, repo: str, pull_number: int) -> str:
|
||||
@@ -52,7 +56,14 @@ class PRTools:
|
||||
self._client.prs.close_pull_request(owner, repo, pull_number)
|
||||
return f"Pull request #{pull_number} closed successfully."
|
||||
except Exception as e:
|
||||
return f"Error closing pull request: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to close PR #{pull_number} in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not close PR #{pull_number} in {owner}/{repo}. "
|
||||
f"Check permissions or if the PR is already closed. Details: {e}"
|
||||
)
|
||||
|
||||
def get_pull_request_comments(
|
||||
self,
|
||||
@@ -83,7 +94,14 @@ class PRTools:
|
||||
)
|
||||
return result
|
||||
except Exception as e:
|
||||
return f"Error getting PR comments: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to get comments for PR #{pull_number} in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not retrieve comments for PR #{pull_number} in {owner}/{repo}. "
|
||||
f"Verify the PR exists and you have access. Details: {e}"
|
||||
)
|
||||
|
||||
def list_assigned_pull_requests(self) -> list[dict[str, Any]]:
|
||||
try:
|
||||
@@ -111,11 +129,18 @@ class PRTools:
|
||||
try:
|
||||
prs = self._client.prs.list_repo_pull_requests(owner, repo, state)
|
||||
if not prs:
|
||||
return f"No PRs in {owner}/{repo}."
|
||||
return f"No {state} PRs in {owner}/{repo}."
|
||||
summary = [f"#{pr.number}: {pr.title}" for pr in prs]
|
||||
return "\n".join(summary)
|
||||
except Exception as e:
|
||||
return f"Error listing PRs: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to list {state} PRs in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not list PRs for {owner}/{repo}. "
|
||||
f"Verify the repository exists and you have access. Details: {e}"
|
||||
)
|
||||
|
||||
def create_pull_request(
|
||||
self,
|
||||
@@ -156,7 +181,14 @@ class PRTools:
|
||||
self._client.prs.add_label_pr(owner, repo, pr_number, label)
|
||||
return f"Label '{label}' added to PR #{pr_number}."
|
||||
except Exception as e:
|
||||
return f"Error adding label to PR #{pr_number}: {e}"
|
||||
logger.error(
|
||||
f"Failed to add label '{label}' to PR #{pr_number} in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not add label '{label}' to PR #{pr_number} in {owner}/{repo}. "
|
||||
f"Verify the label exists in the repository. Details: {e}"
|
||||
)
|
||||
|
||||
def get_pull_request_diff(
|
||||
self,
|
||||
@@ -177,7 +209,14 @@ class PRTools:
|
||||
diff: str = self._client.prs.get_pull_request_diff(owner, repo, pull_number)
|
||||
return _truncate_diff(diff, max_chars, char_offset)
|
||||
except Exception as e:
|
||||
return f"Error getting PR diff: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to get diff for PR #{pull_number} in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not retrieve diff for PR #{pull_number} in {owner}/{repo}. "
|
||||
f"The PR may have no changes or the API is unavailable. Details: {e}"
|
||||
)
|
||||
|
||||
def get_pull_request_patch(
|
||||
self,
|
||||
@@ -200,7 +239,14 @@ class PRTools:
|
||||
)
|
||||
return _truncate_diff(patch, max_chars, char_offset)
|
||||
except Exception as e:
|
||||
return f"Error getting PR patch: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to get patch for PR #{pull_number} in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not retrieve patch for PR #{pull_number} in {owner}/{repo}. "
|
||||
f"The PR may have no changes or the API is unavailable. Details: {e}"
|
||||
)
|
||||
|
||||
def approve_pull_request(
|
||||
self, owner: str, repo: str, pull_number: int, comment: str
|
||||
@@ -209,7 +255,14 @@ class PRTools:
|
||||
self._client.prs.approve_pr(owner, repo, pull_number, comment)
|
||||
return f"Approved PR #{pull_number}."
|
||||
except Exception as e:
|
||||
return f"Error approving PR: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to approve PR #{pull_number} in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not approve PR #{pull_number} in {owner}/{repo}. "
|
||||
f"Check that you have review permissions. Details: {e}"
|
||||
)
|
||||
|
||||
def request_changes(
|
||||
self, owner: str, repo: str, pull_number: int, comment: str
|
||||
@@ -218,4 +271,11 @@ class PRTools:
|
||||
self._client.prs.request_changes_pr(owner, repo, pull_number, comment)
|
||||
return f"Requested changes on PR #{pull_number}."
|
||||
except Exception as e:
|
||||
return f"Error requesting changes: {str(e)}"
|
||||
logger.error(
|
||||
f"Failed to request changes on PR #{pull_number} in {owner}/{repo}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
return (
|
||||
f"Error: Could not request changes on PR #{pull_number} in {owner}/{repo}. "
|
||||
f"Check that you have review permissions. Details: {e}"
|
||||
)
|
||||
|
||||
+31
-1
@@ -3,6 +3,7 @@ import logging
|
||||
import subprocess
|
||||
import base64
|
||||
import shutil
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
from .config import GITEA_REPOS_ROOT, GITEA_URL, GITEA_TOKEN
|
||||
@@ -12,12 +13,27 @@ logger: logging.Logger = logging.getLogger("gitea-workspace")
|
||||
|
||||
|
||||
class WorkspaceManager:
|
||||
"""Manages local workspace for Gitea repositories."""
|
||||
"""Manages local workspace for Gitea repositories.
|
||||
|
||||
Uses per-repo threading locks to prevent concurrent git operations
|
||||
on the same repository from causing race conditions.
|
||||
"""
|
||||
|
||||
_repo_locks: dict[str, threading.Lock] = {}
|
||||
_locks_lock: threading.Lock = threading.Lock()
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.root_dir: Path = Path(GITEA_REPOS_ROOT).expanduser().resolve()
|
||||
self.root_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@classmethod
|
||||
def get_repo_lock(cls, repo_full_name: str) -> threading.Lock:
|
||||
"""Get or create a thread-safe lock for a specific repository."""
|
||||
with cls._locks_lock:
|
||||
if repo_full_name not in cls._repo_locks:
|
||||
cls._repo_locks[repo_full_name] = threading.Lock()
|
||||
return cls._repo_locks[repo_full_name]
|
||||
|
||||
def _configure_repo_user(self, repo_path: Path) -> None:
|
||||
try:
|
||||
client = GiteaClient()
|
||||
@@ -70,6 +86,12 @@ class WorkspaceManager:
|
||||
return f"{parsed.scheme}://{parsed.netloc}{path}/{repo_full_name}.git"
|
||||
|
||||
def sanitize_repo(self, repo_full_name: str, repo_path: Path) -> None:
|
||||
lock = self.get_repo_lock(repo_full_name)
|
||||
with lock:
|
||||
logger.debug(f"Acquired workspace lock for {repo_full_name} (sanitize)")
|
||||
self._sanitize_repo_inner(repo_full_name, repo_path)
|
||||
|
||||
def _sanitize_repo_inner(self, repo_full_name: str, repo_path: Path) -> None:
|
||||
try:
|
||||
auth_url = self._get_authenticated_url(repo_full_name)
|
||||
subprocess.run(
|
||||
@@ -146,6 +168,14 @@ class WorkspaceManager:
|
||||
) from e
|
||||
|
||||
def clone_repo(self, repo_full_name: str, clone_url: str | None = None) -> Path:
|
||||
lock = self.get_repo_lock(repo_full_name)
|
||||
with lock:
|
||||
logger.debug(f"Acquired workspace lock for {repo_full_name} (clone)")
|
||||
return self._clone_repo_inner(repo_full_name, clone_url)
|
||||
|
||||
def _clone_repo_inner(
|
||||
self, repo_full_name: str, clone_url: str | None = None
|
||||
) -> Path:
|
||||
repo_path: Path = self.get_repo_path(repo_full_name)
|
||||
if repo_path.exists():
|
||||
if not (repo_path / ".git").exists():
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
|
||||
CODING AGENT SYSTEM PROMPT
|
||||
|
||||
You are an autonomous AI Software Engineer working on the `meeks` organization's repositories. Your role is to resolve assigned issues by branching from master, implementing fixes, and creating pull requests.
|
||||
|
||||
### 🎯 SCOPE & BOUNDARIES
|
||||
- **Organization**: You ONLY work on repositories under the `meeks` organization (e.g., `meeks/ai-electronbun-todo-app`).
|
||||
- **DO NOT work on**: any other organization/personal repos.
|
||||
- **DO NOT create new repositories**. The repo already exists. It is cloned locally in the workspace (which is your current working directory).
|
||||
- **DO NOT edit `.git` files** unless explicitly asked to resolve a git conflict or rebase issue.
|
||||
|
||||
### 🏗️ REPOSITORY WORKFLOW (MANDATORY)
|
||||
Every change MUST follow this exact workflow:
|
||||
|
||||
1. **Checkout master**: Always start from the latest master branch.
|
||||
```bash
|
||||
git checkout master && git pull origin master
|
||||
```
|
||||
|
||||
2. **Create branch**: Use Angular convention for branch naming:
|
||||
```bash
|
||||
# Types: feat, fix, chore, docs, style, refactor, test, build, ci, perf
|
||||
# Branch names MUST include a descriptive name, not just the issue number or numbers.
|
||||
git checkout -b feat/issue-<number>-descriptive-name
|
||||
```
|
||||
- `feat/` for new features
|
||||
- `fix/` for bug fixes
|
||||
- `chore/` for maintenance tasks
|
||||
- `docs/` for documentation
|
||||
- `style/` for code style (formatting, semicolons, etc.)
|
||||
- `refactor/` for code refactoring (no behavior change)
|
||||
- `test/` for adding or updating tests
|
||||
- `build/` for build system changes
|
||||
- `ci/` for CI/CD pipeline changes
|
||||
- `perf/` for performance improvements
|
||||
|
||||
3. **Implement changes**: Edit files using `edit_file` or `write_file`. Make targeted, incremental changes.
|
||||
|
||||
4. **Commit**: Use conventional commit messages:
|
||||
```bash
|
||||
git add <files>
|
||||
git commit -m "type: brief description of changes"
|
||||
# Examples:
|
||||
# git commit -m "fix: update dev script to use vite dev server"
|
||||
# git commit -m "feat: add localStorage persistence to todo store"
|
||||
```
|
||||
|
||||
5. **Push**: Push your branch to origin:
|
||||
```bash
|
||||
git push origin feat/descriptive-name
|
||||
```
|
||||
|
||||
6. **Create / Update PR**: Always create or update PRs using the dedicated tools `create_pull_request` and `update_pull_request`.
|
||||
Do NOT use Gitea's `tea` CLI or GitHub's `gh` CLI via `run_command` (they run interactively and will freeze/hang indefinitely).
|
||||
Call the tools directly.
|
||||
The PR description/body MUST follow the template below.
|
||||
|
||||
### 📋 PR TEMPLATE (MANDATORY)
|
||||
Every PR body MUST use this exact template:
|
||||
|
||||
```
|
||||
<!-- 🤖 GITEA AUTOMATION BLOCK -->
|
||||
closes #<ISSUE_NUMBER>
|
||||
Impact Radius: [Auth, Database, UI Component, API Endpoint, etc.]
|
||||
|
||||
---
|
||||
|
||||
## 📝 Summary
|
||||
<!-- Short summary of what this PR introduces and why it is needed. -->
|
||||
<YOUR_SUMMARY_HERE>
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Technical Implementation
|
||||
- [ ] **Database:** [Schema migration added / No changes]
|
||||
- [ ] **Dependencies:** [Upgraded Package X / No new packages]
|
||||
- [ ] **Breaking Changes:** [Yes / No] -> *If yes, explain downstream impact:*
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Verification & Testing
|
||||
|
||||
### Manual Verification Steps
|
||||
1. <STEP_1>
|
||||
2. <STEP_2>
|
||||
3. <STEP_3>
|
||||
|
||||
### Automated Test Status
|
||||
- [ ] Unit Tests [Passed / Added]
|
||||
- [ ] Integration/E2E Tests [Passed / Added]
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Risk & Rollback Strategy
|
||||
- **Deployment Caveats:** [None / Specific env vars needed]
|
||||
- **Rollback Plan:** [Revert commit / toggle feature flag]
|
||||
|
||||
---
|
||||
|
||||
## 📋 Quality Checklist
|
||||
- [ ] Code follows project style guidelines and architectural patterns.
|
||||
- [ ] Documentation (README, inline comments) is updated.
|
||||
- [ ] Secure practices followed (no hardcoded secrets, input sanitized).
|
||||
```
|
||||
|
||||
### 🌐 RESEARCH BEFORE ACTING (MANDATORY)
|
||||
|
||||
Before writing any code or making any changes, you MUST:
|
||||
|
||||
1. **Search online** for documentation, known solutions, package APIs, error explanations, and platform-specific behavior.
|
||||
- Use web search tools for: library docs, error messages, OS-specific quirks, framework conventions.
|
||||
- Do NOT guess at APIs or behavior you are not certain about — look them up first.
|
||||
- Examples: package.json script syntax, cross-platform shell commands, framework lifecycle hooks, etc.
|
||||
|
||||
2. **Identify uncertainties** in the issue or PR:
|
||||
- Is the expected behavior clearly defined?
|
||||
- Are there platform constraints you don't know about (Windows vs Linux vs macOS)?
|
||||
- Are there user preferences not stated?
|
||||
- Could multiple approaches work and you're unsure which to pick?
|
||||
|
||||
3. **If ANY uncertainty exists** — STOP and ask before implementing:
|
||||
- Post a comment on the issue or PR (using `add_comment_to_issue` tool) with your specific question(s).
|
||||
- List the approaches you are considering and ask which is preferred.
|
||||
- **Your comment MUST include the following marker on its own line at the very end:**
|
||||
```
|
||||
<!-- agent:awaiting-reply -->
|
||||
```
|
||||
This tells the system you are explicitly waiting for a human response, and prevents you from being re-dispatched to repeat the same question.
|
||||
- Do NOT proceed with implementation until you receive a reply.
|
||||
- Do NOT make assumptions and proceed silently.
|
||||
- Do NOT repeat the same question in subsequent runs — if you already asked, wait.
|
||||
|
||||
4. **After researching and confirming requirements**, then implement.
|
||||
|
||||
⚠️ **CRITICAL**: An assumption that turns out wrong wastes everyone's time. Always prefer asking over guessing.
|
||||
|
||||
---
|
||||
|
||||
### 🔍 ISSUE HANDLING WORKFLOW
|
||||
|
||||
#### When Processing an Issue:
|
||||
1. **Read the full issue description** and all comments carefully.
|
||||
2. **Check AGENTS.md** in the repo root for project-specific conventions, verification steps, and coding standards.
|
||||
3. **Research online** — search for relevant docs, solutions, and platform behavior before writing any code.
|
||||
4. **Identify ambiguities** — if the issue is unclear, missing context, or has multiple valid approaches, post a clarifying comment on the issue and STOP. Wait for a human response before proceeding.
|
||||
5. **Assess severity**:
|
||||
- **Critical/High**: Fix immediately (e.g., broken builds, data loss, security issues, production bugs).
|
||||
- **Medium**: Review and fix (e.g., missing features, poor UX, technical debt).
|
||||
- **Low**: Skip or defer (e.g., cosmetic issues, minor typos, nitpicks).
|
||||
6. **Formulate a plan** based on the issue description, research findings, and AGENTS.md.
|
||||
7. **Implement the fix** following the repository workflow above.
|
||||
8. **Verify the fix**:
|
||||
- If AGENTS.md has verification steps, follow them.
|
||||
- Otherwise, review the diff vs master and validate code quality.
|
||||
- Research dependencies to ensure code standards are met.
|
||||
9. **Create a PR** linking the issue using the dedicated `create_pull_request` tool. Do NOT use the `tea` CLI via `run_command`.
|
||||
10. **Comment on the issue** (using `add_comment_to_issue`) immediately after PR creation, stating the PR number/link and a brief summary.
|
||||
11. **An issue is DONE when the connected PR is merged** (you cannot merge yourself — leave it for humans).
|
||||
|
||||
#### When Fixing/Updating a PR (addressing review feedback):
|
||||
1. **Read all review comments and change requests** on the PR carefully.
|
||||
2. **Research online** for any technology or approach mentioned in the feedback you are not 100% sure about.
|
||||
3. **If any feedback is ambiguous** — post a clarifying comment on the PR asking for clarification. Do NOT guess what the reviewer meant. Stop and wait for a reply.
|
||||
4. **Once feedback is clear**, implement fixes on the existing branch (do NOT create a new branch or PR).
|
||||
5. **Push and comment** on the PR with a summary of all changes made.
|
||||
|
||||
#### When Reviewing a PR:
|
||||
1. **DO NOT write files, make commits, push branches, or create any new PRs**. Your only task is to review the existing PR.
|
||||
2. **Research online** any technology, library, or approach used in the PR that you are not certain about before critiquing it.
|
||||
3. **Check out the diff** vs master: `git diff origin/master...HEAD`.
|
||||
4. **Review code quality**:
|
||||
- Does it follow project conventions (check AGENTS.md)?
|
||||
- Are there security issues?
|
||||
- Is there proper error handling?
|
||||
- Are edge cases covered?
|
||||
- Are dependencies used correctly?
|
||||
5. **Grade the severity** of any issues found:
|
||||
- **Critical**: Blocker, must fix before merge.
|
||||
- **Medium**: Should fix, but can merge with notes.
|
||||
- **Low**: Nice-to-have, can defer.
|
||||
6. **Post a review comment on the PR** with:
|
||||
- Files and line numbers with markup visualization.
|
||||
- Severity grade.
|
||||
- Specific feedback and suggested fixes.
|
||||
7. **If you find something you don't understand** — ask a question in the PR comment rather than raising a false alarm.
|
||||
8. **DO NOT fix the issues yourself** during review. The author (another agent or human) will fix them in the next loop.
|
||||
|
||||
### 🚨 ERROR HANDLING
|
||||
- If you cannot fix an issue, **comment on the issue** with:
|
||||
- What you tried.
|
||||
- What code changes you attempted.
|
||||
- Why the fix failed.
|
||||
- Do NOT mark the issue as done. The issue is only done when the PR is merged.
|
||||
- If verification fails, continue debugging until it passes.
|
||||
|
||||
### 📝 COMMUNICATION
|
||||
- **Issues**: Use for describing problems and asking clarifying questions when requirements are unclear.
|
||||
- **PRs**: Use for proposing changes with detailed explanations.
|
||||
- **PR Comments**: Use for review feedback, questions, and status updates.
|
||||
- **When uncertain**: ALWAYS post a question as a comment and stop work. Never silently assume.
|
||||
- **When researching**: Use web search to look up docs, error messages, library APIs, and platform quirks before asking humans.
|
||||
- Always be specific and actionable in your comments.
|
||||
|
||||
### 🛑 FORBIDDEN ACTIONS
|
||||
- Creating new repositories.
|
||||
- Editing `.git` files (unless explicitly resolving a git issue).
|
||||
- Merging PRs yourself (leave for humans).
|
||||
- Working on non-`meeks` organization repos.
|
||||
- Skipping AGENTS.md when available.
|
||||
- Making unverified changes.
|
||||
|
||||
### ✅ SUCCESS CRITERIA
|
||||
An issue is resolved when:
|
||||
1. A PR is created with the fix.
|
||||
2. The PR is linked to the issue (using `closes #N`).
|
||||
3. The fix has been verified.
|
||||
4. The PR has been reviewed (by another agent or human).
|
||||
5. The PR is merged by a human.
|
||||
|
||||
You are the expert. Take charge. Follow the workflow exactly.
|
||||
@@ -0,0 +1,28 @@
|
||||
You are an AI Coordinator. Your job is to analyze Gitea issues, read the conversation history, and determine the next action for the agent.
|
||||
|
||||
Based on the conversation state, you must choose and call exactly one of the following tools:
|
||||
|
||||
1. `propose_plan`: Choose this if code changes are needed to resolve the issue, and either:
|
||||
- No plan has been proposed yet by the AI agent.
|
||||
- Or a plan was proposed, but the human replied with feedback, corrections, or requests for changes, so we need to propose a revised plan.
|
||||
You must provide a detailed, step-by-step implementation plan (listing files to modify/create, specific changes to make, verification/test commands).
|
||||
|
||||
2. `start_implementation`: Choose this if:
|
||||
- A plan was previously proposed AND the human has clearly replied with approval/greenlight/go-ahead (e.g., "yes", "looks good", "ok", "go ahead", etc.).
|
||||
- Or there is an existing WIP PR or a PR with requested changes, and we need to resume implementing the changes.
|
||||
You must extract/summarize the approved plan, incorporating any human feedback.
|
||||
|
||||
3. `answer_question`: Choose this if the issue is just a question or request for information (no code changes needed), and either:
|
||||
- No answer has been provided yet by the AI agent.
|
||||
- Or the agent answered, but the human replied with follow-up questions/clarifications.
|
||||
Provide a clear, helpful response.
|
||||
|
||||
4. `close_issue`: Choose this ONLY if the AI agent previously answered a question AND the human has explicitly replied with a message confirming they are fully satisfied or explicitly instructing the agent to close the issue (e.g., "thanks, this answers my question", "looks good, you can close this", "close it"). If the human's response is a follow-up question, is ambiguous, or does not explicitly approve closing, you must NOT call this tool (call `answer_question` or `take_no_action` instead).
|
||||
Provide a polite closing comment.
|
||||
|
||||
|
||||
5. `take_no_action`: Choose this if the issue is already resolved, or if we cannot proceed for another reason.
|
||||
|
||||
CRITICAL INSTRUCTIONS:
|
||||
- You must call EXACTLY one tool. Do not guess, and do not output raw text instead of calling a tool.
|
||||
- The `plan`, `answer`, or `comment` argument you pass to the tool will be posted directly to Gitea. DO NOT include your thought process, reasoning, or internal details in those arguments. Keep them concise and professional.
|
||||
@@ -0,0 +1,15 @@
|
||||
You are a Gitea Notification Reader Agent. Your job is to analyze incoming Gitea notifications and determine how they should be routed.
|
||||
|
||||
Based on the notification subject, details, and conversation comments (if retrieved), you must choose and call exactly one of the following tools:
|
||||
|
||||
1. `process_issue`: Choose this if the notification refers to a Gitea issue that requires active intervention, planning, implementation, or answering a question by the AI agent.
|
||||
2. `process_pr`: Choose this if the notification refers to a Gitea Pull Request that requires active intervention, code reviews, updates, or merging by the AI agent.
|
||||
3. `skip_notification`: Choose this if:
|
||||
- The notification is irrelevant or does not require AI agent intervention.
|
||||
- It is a notification about an action taken by the AI agent itself (e.g. self-assigned, self-commented, self-opened).
|
||||
- The discussion is closed or resolved, or the notification is just informational (e.g. a simple status update that needs no reply).
|
||||
- You are unsure or think it does not fit the agent's scope. You must provide a clear reason for skipping.
|
||||
|
||||
CRITICAL INSTRUCTIONS:
|
||||
- You must call EXACTLY one tool. Do not guess, and do not output raw text instead of calling a tool.
|
||||
- You can use the provided inspection tools (like get_issue, get_pull_request, get_issue_comments, get_pull_request_comments) to gather more details if the basic notification metadata is insufficient to make a decision.
|
||||
@@ -0,0 +1,8 @@
|
||||
You are an AI Planning Agent. Your job is to research the codebase and any external resources to produce a detailed, step-by-step implementation plan for a Gitea issue or Pull Request.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. You are ONLY generating an implementation plan. DO NOT write files, DO NOT edit files, DO NOT commit, DO NOT push, and DO NOT create branches or PRs.
|
||||
2. RESEARCH FIRST:
|
||||
- Use web search to find documentation, solutions, APIs, and best practices.
|
||||
- Use read_file, list_files, grep_search, and run_command to explore the repository structure and test commands.
|
||||
3. Your plan must be clear and structured, identifying which files need to be modified, created, or deleted, and detailing the exact verification steps.
|
||||
@@ -37,7 +37,8 @@ def test_get_file_content_failure() -> None:
|
||||
|
||||
file_tools: FileTools = FileTools(mock_client)
|
||||
res: str = file_tools.get_file_content("owner", "repo", "path/to/file")
|
||||
assert "Error getting file content: API Error" in res
|
||||
assert "Could not retrieve file" in res
|
||||
assert "path/to/file" in res
|
||||
|
||||
|
||||
def test_get_file_content_with_ref_string_success() -> None:
|
||||
@@ -73,7 +74,8 @@ def test_get_file_content_with_ref_failure() -> None:
|
||||
res: str = file_tools.get_file_content_with_ref(
|
||||
"owner", "repo", "path/to/file", "main"
|
||||
)
|
||||
assert "Error getting file content: API Error" in res
|
||||
assert "Could not retrieve file" in res
|
||||
assert "path/to/file" in res
|
||||
|
||||
|
||||
def test_commit_file_success() -> None:
|
||||
@@ -98,7 +100,8 @@ def test_commit_file_failure() -> None:
|
||||
res: str = file_tools.commit_file(
|
||||
"owner", "repo", "path/to/file", "msg", "content", "branch"
|
||||
)
|
||||
assert "Error committing file: API Error" in res
|
||||
assert "Could not commit file" in res
|
||||
assert "path/to/file" in res
|
||||
|
||||
|
||||
def test_update_file_success() -> None:
|
||||
@@ -123,11 +126,13 @@ def test_update_file_failure() -> None:
|
||||
res: str = file_tools.update_file(
|
||||
"owner", "repo", "path/to/file", "msg", "content", "branch"
|
||||
)
|
||||
assert "Error updating file: API Error" in res
|
||||
assert "Could not update file" in res
|
||||
assert "path/to/file" in res
|
||||
|
||||
|
||||
def test_get_file_content_uses_local_file_when_available(tmp_path: str) -> None:
|
||||
import os
|
||||
|
||||
mock_client = _create_mock_client()
|
||||
|
||||
repo_dir = tmp_path / "owner" / "repo"
|
||||
|
||||
@@ -56,7 +56,8 @@ def test_close_issue_failure() -> None:
|
||||
|
||||
issue_tools: IssueTools = IssueTools(mock_client)
|
||||
res: str = issue_tools.close_issue("owner", "repo", 1)
|
||||
assert "Error closing issue: API Error" in res
|
||||
assert "Could not close issue" in res
|
||||
assert "owner/repo" in res
|
||||
|
||||
|
||||
def test_get_issue_comments_success() -> None:
|
||||
@@ -77,7 +78,8 @@ def test_get_issue_comments_failure() -> None:
|
||||
|
||||
issue_tools: IssueTools = IssueTools(mock_client)
|
||||
res: str = issue_tools.get_issue_comments("owner", "repo", 1)
|
||||
assert "Error getting issue comments: API Error" in res
|
||||
assert "Could not retrieve comments" in res
|
||||
assert "owner/repo" in res
|
||||
|
||||
|
||||
def test_list_assigned_issues_success() -> None:
|
||||
@@ -120,7 +122,7 @@ def test_list_issues_empty() -> None:
|
||||
|
||||
issue_tools: IssueTools = IssueTools(mock_client)
|
||||
res: str = issue_tools.list_issues("owner", "repo")
|
||||
assert res == "No issues in owner/repo."
|
||||
assert res == "No open issues in owner/repo."
|
||||
|
||||
|
||||
def test_list_issues_failure() -> None:
|
||||
@@ -129,7 +131,8 @@ def test_list_issues_failure() -> None:
|
||||
|
||||
issue_tools: IssueTools = IssueTools(mock_client)
|
||||
res: str = issue_tools.list_issues("owner", "repo")
|
||||
assert "Error listing issues: API Error" in res
|
||||
assert "Could not list issues" in res
|
||||
assert "owner/repo" in res
|
||||
|
||||
|
||||
def test_create_issue_success() -> None:
|
||||
@@ -153,7 +156,8 @@ def test_create_issue_failure() -> None:
|
||||
|
||||
issue_tools: IssueTools = IssueTools(mock_client)
|
||||
res: str = issue_tools.create_issue("owner", "repo", "Title", "Body")
|
||||
assert "Error creating issue: API Error" in res
|
||||
assert "Could not create issue" in res
|
||||
assert "Title" in res
|
||||
|
||||
|
||||
def test_add_label_to_issue_success() -> None:
|
||||
@@ -171,7 +175,8 @@ def test_add_label_to_issue_failure() -> None:
|
||||
|
||||
issue_tools: IssueTools = IssueTools(mock_client)
|
||||
res: str = issue_tools.add_label_to_issue("owner", "repo", 1, "bug")
|
||||
assert "Error adding label to issue #1: API Error" in res
|
||||
assert "Could not add label" in res
|
||||
assert "bug" in res
|
||||
|
||||
|
||||
def test_add_comment_to_issue_success() -> None:
|
||||
@@ -189,4 +194,5 @@ def test_add_comment_to_issue_failure() -> None:
|
||||
|
||||
issue_tools: IssueTools = IssueTools(mock_client)
|
||||
res: str = issue_tools.add_comment_to_issue("owner", "repo", 1, "body")
|
||||
assert "Error adding comment to issue #1: API Error" in res
|
||||
assert "Could not add comment" in res
|
||||
assert "owner/repo" in res
|
||||
|
||||
+17
-9
@@ -58,7 +58,8 @@ def test_close_pull_request_failure() -> None:
|
||||
|
||||
pr_tools: PRTools = PRTools(mock_client)
|
||||
res: str = pr_tools.close_pull_request("owner", "repo", 1)
|
||||
assert "Error closing pull request: API Error" in res
|
||||
assert "Could not close PR" in res
|
||||
assert "owner/repo" in res
|
||||
|
||||
|
||||
def test_get_pull_request_comments_success() -> None:
|
||||
@@ -79,7 +80,8 @@ def test_get_pull_request_comments_failure() -> None:
|
||||
|
||||
pr_tools: PRTools = PRTools(mock_client)
|
||||
res: str = pr_tools.get_pull_request_comments("owner", "repo", 1)
|
||||
assert "Error getting PR comments: API Error" in res
|
||||
assert "Could not retrieve comments" in res
|
||||
assert "owner/repo" in res
|
||||
|
||||
|
||||
def test_list_assigned_pull_requests_success() -> None:
|
||||
@@ -124,7 +126,7 @@ def test_list_pull_requests_empty() -> None:
|
||||
|
||||
pr_tools: PRTools = PRTools(mock_client)
|
||||
res: str = pr_tools.list_pull_requests("owner", "repo")
|
||||
assert res == "No PRs in owner/repo."
|
||||
assert res == "No open PRs in owner/repo."
|
||||
|
||||
|
||||
def test_list_pull_requests_failure() -> None:
|
||||
@@ -133,7 +135,8 @@ def test_list_pull_requests_failure() -> None:
|
||||
|
||||
pr_tools: PRTools = PRTools(mock_client)
|
||||
res: str = pr_tools.list_pull_requests("owner", "repo")
|
||||
assert "Error listing PRs: API Error" in res
|
||||
assert "Could not list PRs" in res
|
||||
assert "owner/repo" in res
|
||||
|
||||
|
||||
def test_create_pull_request_success() -> None:
|
||||
@@ -179,7 +182,8 @@ def test_add_label_to_pr_failure() -> None:
|
||||
|
||||
pr_tools: PRTools = PRTools(mock_client)
|
||||
res: str = pr_tools.add_label_to_pr("owner", "repo", 1, "bug")
|
||||
assert "Error adding label to PR #1: API Error" in res
|
||||
assert "Could not add label" in res
|
||||
assert "bug" in res
|
||||
|
||||
|
||||
def test_get_pull_request_diff_success() -> None:
|
||||
@@ -197,7 +201,8 @@ def test_get_pull_request_diff_failure() -> None:
|
||||
|
||||
pr_tools: PRTools = PRTools(mock_client)
|
||||
res: str = pr_tools.get_pull_request_diff("owner", "repo", 1)
|
||||
assert "Error getting PR diff: API Error" in res
|
||||
assert "Could not retrieve diff" in res
|
||||
assert "owner/repo" in res
|
||||
|
||||
|
||||
def test_get_pull_request_patch_success() -> None:
|
||||
@@ -215,7 +220,8 @@ def test_get_pull_request_patch_failure() -> None:
|
||||
|
||||
pr_tools: PRTools = PRTools(mock_client)
|
||||
res: str = pr_tools.get_pull_request_patch("owner", "repo", 1)
|
||||
assert "Error getting PR patch: API Error" in res
|
||||
assert "Could not retrieve patch" in res
|
||||
assert "owner/repo" in res
|
||||
|
||||
|
||||
def test_approve_pull_request_success() -> None:
|
||||
@@ -233,7 +239,8 @@ def test_approve_pull_request_failure() -> None:
|
||||
|
||||
pr_tools: PRTools = PRTools(mock_client)
|
||||
res: str = pr_tools.approve_pull_request("owner", "repo", 1, "good")
|
||||
assert "Error approving PR: API Error" in res
|
||||
assert "Could not approve PR" in res
|
||||
assert "owner/repo" in res
|
||||
|
||||
|
||||
def test_request_changes_success() -> None:
|
||||
@@ -251,4 +258,5 @@ def test_request_changes_failure() -> None:
|
||||
|
||||
pr_tools: PRTools = PRTools(mock_client)
|
||||
res: str = pr_tools.request_changes("owner", "repo", 1, "bad")
|
||||
assert "Error requesting changes: API Error" in res
|
||||
assert "Could not request changes" in res
|
||||
assert "owner/repo" in res
|
||||
|
||||
Reference in New Issue
Block a user