# Agent Instructions ## Import Organization - **Keep all imports at the top of the file.** Never add imports inside functions, methods, or conditional blocks. - Use absolute imports for project modules (e.g., `from gitea.models import IssueModel`). - Remove unused imports when editing files. ## Python Type Hints (REQUIRED) - **All functions must have type hints** for parameters and return types. - **All class attributes must have type hints** in `__init__`. - **Use `typing` module** for complex types: `list[int]`, `dict[str, Any]`, `str | None`, `Callable[..., Any]`. - **Never use bare `list` or `dict`** - always parameterize: `list[str]`, `dict[str, Any]`. - **Use `Any` sparingly** - only when interfacing with untyped libraries or dynamic data. - **Module-level constants must have type hints**: `VERSION: str = "1.0"`. - **Tuple return types**: use `tuple[str, int]` for multiple returns. ## Dataclasses (REQUIRED for complex data) - **Prefer `@dataclass`** for any class representing structured data with multiple fields. - **Use `dataclasses.field()`** for default values that are mutable (lists, dicts). - **Use `field(default_factory=list)`** instead of `default=[]`. - **Use `field(default_factory=dict)`** instead of `default={}`. - **Use `kw_only=True`** for dataclasses with many optional fields. - **Use `frozen=True`** for immutable dataclasses when appropriate. - **Example:** ```python from dataclasses import dataclass, field from typing import Optional @dataclass class IssueInfo: number: int title: str owner: str repo: str labels: list[str] = field(default_factory=list) assignee: Optional[str] = None ``` ## CUPID Programming Principles - **Composable**: Write small, modular agents and tools with clear interfaces and dependency injection (`RunContext`). - **Unix-like**: Each agent or tool has a single responsibility and does one thing well. - **Predictable**: Use structured outputs (`result_type` with Pydantic models) to eliminate ambiguous text responses. - **Idiomatic**: Follow modern Python type hints (`list[str]`, `dict[str, Any]`), standard Pydantic v2 schemas, and Pydantic AI idioms. - **Domain-based**: Structure code and data around domain concepts (`NotificationDecision`, `CoordinatorDecision`, `ExecutionPlan`) rather than LLM framework mechanics. ## Pydantic AI Integration Guidelines - Use `pydantic_ai.Agent` as the primary execution engine for all AI agents. - Define structured result schemas using Pydantic `BaseModel` for predictable output handling. - Pass runtime dependencies into tools using `pydantic_ai.RunContext` and typed dependency containers. - Register tools using `@agent.tool` or modular toolsets for clean separation of concerns. ## Follow all instructions provided in the system prompt. - Keep responses concise and direct. - Minimize output tokens. - Use the `Task` tool for complex multi-step tasks. - Verify solutions with tests if possible. - Run lint and typecheck commands if provided. - Do not commit changes unless explicitly asked. - Use GitHub-flavored markdown for formatting. - Answer concisely with fewer than 4 lines of text. - ALWAYS use `uv` to run python commands. Do not use `python3` directly. - Always commit and push changes at the end of a task. - NEVER push to the master or main branch. # Environment Variables - `GITEA_URL` — Gitea API base URL (REQUIRED) - `GITEA_TOKEN` — Gitea API token (REQUIRED) - `GITEA_REPOS_ROOT` — Local path to clone repos to (REQUIRED) - `AGENT_MODEL_ID` — LM Studio model ID (default: `qwen3.6-35b-a3b-mtp@iq4_nl`) - `AGENT_MAX_RETRIES` — Max retries per task (default: `2`) # Architecture The agent uses a **repo-scoped single-agent dispatch** pattern: 1. `AgentOrchestrator` polls Gitea for assigned issues/PRs 2. Tasks are grouped by repo and enqueued in `WorkQueue` 3. `AgentDispatcher` creates a **fresh `CodingAgent`** per repo batch 4. Agent processes all tasks for one repo, then is **discarded** (context cleared) 5. Next repo gets a fresh agent — no context bleed between repos ``` main.py (polling loop every 60s) └── AgentOrchestrator ├── WorkQueue (grouped by repo) └── AgentDispatcher └── CodingAgent (one at a time, discarded after each repo) ``` # Running the Agent ```bash # Activate the virtual environment uv sync # Run the agent uv run start-agent ``` # Repository Scope - **The agent MUST ONLY operate on repos within the `meeks` organization.** - `gitea/client.py:48` enforces this with a hardcoded filter: `if r.get("owner", {}).get("login") == "meeks"` - **Never change this filter** to include personal accounts (e.g., `unknown-ai`) or other organizations. - This filter is the single source of truth for repo scope — do not bypass it.