# AGENTS.md - AI Agent Development Guide

> This file provides essential information for AI coding agents working in this repository.

## Project Overview

**mcp-keep-feedback** is an MCP (Model Context Protocol) server for interactive user feedback in AI-assisted development. It provides a Web UI with intelligent environment detection.

- **Language**: Python 3.11+
- **Package Manager**: uv
- **Build Backend**: Hatchling
- **Framework**: FastMCP, FastAPI

---

## Code Style Guidelines

### Python Version
- Target: Python 3.11+
- Use modern type syntax: `list[str]`, `dict[str, Any]`, `str | None` (not `Optional[str]`)

### Imports
Imports are managed by ruff/isort with these rules:
- `known-first-party = ["mcp_keep_feedback"]`
- `lines-after-imports = 2`
- Order: standard library → third-party → local imports

```python
# Standard library
import asyncio
import os
from pathlib import Path
from typing import Any

# Third-party
from fastapi import WebSocket
from pydantic import Field

# Local imports
from .debug import debug_log
from .utils.error_handler import ErrorHandler, ErrorType
```

### Formatting (Ruff)
- Line length: 88 characters
- Indent: 4 spaces
- Quotes: double quotes (`"`)
- Line ending: auto

### Type Annotations
Always use type annotations for function parameters and return types:

```python
def process_images(self, images: list[dict]) -> list[dict]:
    """Process image data with type safety."""
    ...

async def submit_feedback(
    self,
    feedback: str,
    images: list[dict[str, Any]],
    settings: dict[str, Any] | None = None,
) -> None:
    """Submit feedback and images."""
    ...
```

### Naming Conventions
- **Classes**: PascalCase (`WebFeedbackSession`, `ErrorHandler`)
- **Functions/Methods**: snake_case (`process_images`, `get_error_solutions`)
- **Constants**: UPPER_SNAKE_CASE (`MAX_IMAGE_SIZE`, `SUPPORTED_IMAGE_TYPES`)
- **Private methods**: prefix with underscore (`_process_images`, `_safe_close_websocket`)
- **Module-level private**: prefix with underscore (`_encoding_initialized`)

### Docstrings
Use Chinese (Traditional) for docstrings in this project:

```python
def classify_error(error: Exception) -> ErrorType:
    """
    根據異常類型自動分類錯誤

    Args:
        error: Python 異常對象

    Returns:
        ErrorType: 錯誤類型
    """
    ...
```

---

## Error Handling

### Use the ErrorHandler Framework
Do not use bare `except` or print errors directly. Use the unified `ErrorHandler`:

```python
from .utils.error_handler import ErrorHandler, ErrorType

try:
    # ... operation
    pass
except Exception as e:
    error_id = ErrorHandler.log_error_with_context(
        e,
        context={"operation": "描述操作", "file_path": path},
        error_type=ErrorType.FILE_IO,
    )
    user_message = ErrorHandler.format_user_error(e, include_technical=False)
```

### Error Types Available
- `NETWORK` - Network-related errors
- `FILE_IO` - File I/O errors
- `PROCESS` - Process-related errors
- `TIMEOUT` - Timeout errors
- `USER_CANCEL` - User cancelled operation
- `SYSTEM` - System errors
- `PERMISSION` - Permission errors
- `VALIDATION` - Data validation errors
- `DEPENDENCY` - Dependency errors
- `CONFIGURATION` - Configuration errors

---

## Async Patterns

### Use async/await properly
```python
# Correct: async context manager for WebSocket
async with self.websocket:
    await self.websocket.send_json({"type": "message"})

# Correct: async task creation
asyncio.create_task(read_output())

# Correct: run blocking code in executor
await asyncio.get_event_loop().run_in_executor(None, blocking_function)
```

---

## Internationalization (i18n)

The project supports three languages:
- `zh-TW` - Traditional Chinese (default)
- `zh-CN` - Simplified Chinese
- `en` - English

Use the i18n manager for user-facing messages:

```python
from .i18n import get_i18n_manager

i18n = get_i18n_manager()
message = i18n.t("key.path.to.message")
```

---

## MCP Tool Definition Pattern

Use FastMCP decorators with Pydantic Field for parameter descriptions:

```python
from fastmcp import FastMCP
from pydantic import Field
from typing import Annotated

mcp = FastMCP("Server Name")

@mcp.tool()
async def interactive_feedback(
    project_directory: Annotated[str, Field(description="項目目錄路徑")],
    full_report_and_output: Annotated[str, Field(description="完整報告內容")],
) -> list:
    """Tool description here."""
    ...
```

---

## Pre-commit Hooks

Pre-commit hooks run automatically on commit. Key hooks:
- `trailing-whitespace` - Remove trailing whitespace
- `end-of-file-fixer` - Ensure files end with newline
- `check-yaml` / `check-toml` / `check-json` - Validate config files
- `ruff` - Linting with auto-fix
- `ruff-format` - Code formatting

Manual commands:
```bash
uv run pre-commit run           # Run on staged files
uv run pre-commit run --all-files  # Run on all files
```

---

## Key Directories

```
src/mcp_keep_feedback/
├── __init__.py           # Package exports
├── __main__.py           # CLI entry point
├── server.py             # MCP server and tools
├── debug.py              # Debug logging utilities
├── i18n.py               # Internationalization
├── web/                  # Web UI module
│   ├── main.py           # WebUIManager
│   ├── routes/           # FastAPI routes
│   ├── models/           # Data models
│   ├── utils/            # Web utilities
│   └── constants/        # Message codes
├── utils/                # Shared utilities
│   ├── error_handler.py  # Error handling framework
│   └── resource_manager.py # Resource cleanup
tests/
├── conftest.py           # Pytest fixtures
├── unit/                 # Unit tests
├── integration/          # Integration tests
├── fixtures/             # Test data
└── helpers/              # Test utilities
```

---

## Ruff Linting Rules

Key enabled rules:
- `E`, `W` - pycodestyle errors/warnings
- `F` - Pyflakes
- `I` - isort
- `B` - flake8-bugbear
- `UP` - pyupgrade
- `S` - bandit (security)
- `PL` - Pylint
- `RUF` - Ruff-specific

Key ignored rules (see pyproject.toml for full list):
- `E501` - Line length (handled by formatter)
- `S101` - Allow assert in tests
- `T201` - Allow print for debugging
- `RUF001-003` - Allow Chinese characters

---

## Important Notes

1. **Never suppress type errors** with `# type: ignore` or `as any` without justification
2. **Never use bare `except:`** - always specify exception types
3. **Always clean up resources** - use the resource manager for processes, files, etc.
4. **JSON RPC safety** - Error handling must not affect stdout (used for MCP communication)
5. **WebSocket lifecycle** - Use `_safe_close_websocket()` for proper cleanup
6. **Subprocess security** - Use `shlex.split()` and `shell=False` for command execution

---

## Publishing

```bash
# 发布到 PyPI
make publish

# 发布到 TestPyPI
make publish-test
```

环境变量：在 `.env` 文件中设置 `PYPI_TOKEN=pypi-xxx`

---

## Tunable Prompts (可调优提示词)

项目中包含四个核心提示词，用于控制 AI 行为协议。修改时需注意保持一致性。

### 1. prompt-engineering.md (提示工程文档)

- **位置**: `docs/prompt-engineering.md`
- **用途**: 用户参考文档，包含完整的 MCP 模式规则和示例
- **核心内容**: 绝对视觉隔离、强制闭环调用、无限循环、超时重试等规则

### 2. AUTO_REPLY_MESSAGE (自动回复消息)

- **位置**: `src/mcp_keep_feedback/web/models/feedback_session.py` (约第 69-80 行)
- **用途**: 当用户在 `MCP_AUTO_REPLY_TIMEOUT` 设定时间内未响应时，系统自动发送此消息
- **核心内容**: 引导 AI 继续调用 `mcp-keep-feedback` 工具，不放弃、不派发子代理

### 3. append_message (强制提醒协议)

- **位置**:
  - `src/mcp_keep_feedback/web/models/feedback_session.py` (约第 441-465 行)
  - `src/mcp_keep_feedback/web/main.py` (约第 500-524 行) - 重复定义，需同步修改
- **用途**: 每次用户提交反馈后，自动附加此协议提醒到反馈内容末尾
- **核心内容**: 强制闭环调用、无限循环、超时重试、禁止子代理派发等规则

### 4. CLI 模式协议提醒 (S-CORE 协议提醒)

- **位置**: `src/mcp_keep_feedback/utils/cli_client.py`（在「正在等待您的反馈提交」输出之后，约 186-198 行）
- **用途**: 用户通过 `echo "汇报内容" | uvx mcp-keep-feedback feedback --port 18181` 降级到 CLI 时，在等待期间打印的协议提醒，避免 AI 在长时间等待或超时后忘记「永不终止、禁止放弃、循环重试」
- **核心内容**: 永不终止、建议调用方超时 1 小时、超时后 sleep(60) 重试 MCP/CLI、循环直至成功、汇报后仍须询问下一指令

### 修改注意事项

1. **两处 `append_message` 必须同步修改** - `feedback_session.py` 和 `main.py` 中的定义需保持一致
2. **协议逻辑关联** - 上述四个 prompt（含 CLI 提醒）共同构成完整的闭环协议，调优时若修改协议语义，需同步检查并更新 `cli_client.py` 中的 CLI 协议提醒
3. **关键词保持** - `[MUST]`、`[STRICTLY FORBIDDEN]`、`[NEVER]` 等强调词对 AI 行为有显著影响
