docs: restructure AGENTS.md and add docs/rules agent documentation system (#5830)

This commit is contained in:
DDSRem
2026-05-25 13:48:43 +08:00
committed by GitHub
parent 63b9994b0e
commit 784672af5c
14 changed files with 2029 additions and 121 deletions

192
AGENTS.md
View File

@@ -1,153 +1,103 @@
# MoviePilot AI Agent Guide
# AGENTS.md
This file defines the default behavior for AI agents working in the MoviePilot repository. Unless a deeper directory provides another `AGENTS.md`, these rules apply to the entire repo.
This file is the primary instruction set for all AI agents and LLMs working in this repository. Local documentation takes precedence over general training data. You must follow this file and the rule documents it references.
## 1. Project Scope
---
- This repository contains the MoviePilot backend, CLI, MCP/API, Docker assets, and AI skills.
- The backend is based on FastAPI, with most code under `app/`.
- Frontend source code is not in this repository. The frontend source repository is `MoviePilot-Frontend`.
- This repository also includes the local CLI, database migrations, developer docs, tests, Docker scripts, and AI skills.
## Task-to-Documentation Mapping
## 2. Working Principles
Before executing any task, identify the domain and load the corresponding document.
- Read the relevant implementation, tests, and docs before changing code. Do not infer behavior from directory names alone.
- Prefer the smallest correct change. Reuse existing functions, patterns, and naming whenever possible.
- Do not perform unrelated large refactors, mass renames, or formatting-only cleanup.
- Before adding a new abstraction, check whether it is actually reusable. If the logic fits well inside an existing function, class, or flow, keep it there.
- The worktree may contain user changes. Do not revert, overwrite, or reorganize changes you do not fully understand.
- Default to writing conclusions, validation results, and risk notes in Chinese unless the user asks otherwise.
### Architectural Decisions
* **Primary Reference:** `docs/rules/05-architecture.md`
* **Required Constraints:** Respect layer boundaries and dependency flow. Do not introduce circular dependencies. Verify the correct layer for any new capability before implementing.
## 3. Key Directories
### Business Logic and Design Patterns
* **Primary Reference:** `docs/rules/04-design-patterns.md`
* **Required Constraints:** Use the project's established Module, Chain, Event, and Oper structural patterns. Do not introduce abstractions the project has not adopted.
- `app/api/endpoints/`: HTTP entrypoints. Handles auth, parameters, responses, and simple CRUD.
- `app/chain/`: Business orchestration layer for search, recognition, subscriptions, downloads, messaging flows, and similar use cases.
- `app/modules/`: Dynamically loaded system modules. Encapsulates pluggable downloaders, media servers, message channels, and other backend capabilities.
- `app/plugins/`: Directory where plugins are installed and managed.
- `app/helper/`: Reusable low-level helper logic. Not a place for full business orchestration.
- `app/core/config.py`: Environment variables, deployment parameters, and startup-level settings.
- `app/schemas/types.py`: Shared enums and types such as `SystemConfigKey` and module categories.
- `app/db/`: Database models, sessions, and `*_oper.py` data access wrappers.
- `moviepilot`: Local CLI entrypoint and help text.
- `database/versions/`: Alembic migration scripts.
- `docs/`: CLI, MCP/API, and development workflow documentation.
- `skills/`: AI agent skills and related scripts.
- `tests/`: Pytest tests and a few manual test scripts.
- `config/`, `.moviepilot.env`, and `*.db`: Local config or runtime data. Do not modify or commit them unless the user explicitly asks for it.
### Coding Standards and Style
* **Primary Reference:** `docs/rules/06-code-styles.md`
* **Required Constraints:** Match the style of the surrounding file. Type annotations, Pydantic models, and async/await usage must all conform to the documented standards.
## 4. Layering And Access Boundaries
### Identifiers and Naming
* **Primary Reference:** `docs/rules/07-naming-conventions.md`
* **Required Constraints:** All filenames, class names, function names, and constants must follow the project's taxonomy. No arbitrary abbreviations or mixed casing styles.
### API / Endpoint Layer
### Comments and Documentation
* **Primary Reference:** `docs/rules/08-comment-styles.md`
* **Required Constraints:** All public classes and methods require Chinese docstrings. Comments must explain the *why*, not restate the code.
* **⚠️ MANDATORY GATE:** Code that is missing proper Chinese docstrings on public interfaces is **REJECTED** at review. No exceptions.
- Endpoints should only handle HTTP concerns: auth, parameter parsing, response models, streaming adaptation, and simple input validation.
- Simple list, detail, toggle, settings read/write, and pure CRUD endpoints may directly call `app/db/` or an existing `helper`.
- If the logic coordinates multiple modules, triggers events, touches caches, or combines search, recognition, subscription, or download workflows, move it into `chain`.
- Prefer adding new endpoints to an existing domain file. Create a new endpoint file only when introducing a new top-level resource domain.
- After adding a new endpoint, register it in `app/api/apiv1.py`.
### External Communication and Interfaces
* **Primary Reference:** `docs/rules/09-external-response.md`
* **Required Constraints:** All third-party HTTP requests must go through `RequestUtils`. Response formats must use the project's standard schemas. Error handling must follow the per-layer conventions.
### Chain Layer
### Data and Persistence
* **Primary Reference:** `docs/rules/10-data-and-persistent.md`
* **Required Constraints:** Any database model change requires a matching Alembic migration. Runtime configuration must be managed via `SystemConfigKey` + `SystemConfigOper`. Raw string keys are forbidden.
- `chain` is the business orchestration layer shared by API, CLI, message interaction, agents, schedulers, and similar entrypoints.
- `chain` is responsible for composing `module`, `helper`, `db`, events, caches, and other stable `chain` capabilities.
- Inside `chain`, prefer calling module capabilities through `run_module()` or `async_run_module()`. Only use `ModuleManager` or similar helpers directly when you truly need to enumerate modules, inspect instances, or run health checks.
- `chain` should focus on use cases and workflows. It should not hold low-level protocol details, HTTP request objects, or page-specific parameter assembly.
- Before adding a new `chain`, ask whether this is a reusable business use case shared by multiple entrypoints, or a flow that coordinates multiple modules or resources. If it is just short logic for one endpoint, do not create a new `chain`.
- `chain` may call other `chain` classes when reusing stable domain logic, but avoid introducing new circular dependencies.
### Quality and Security
* **Primary Reference:** `docs/rules/11-quality-and-security.md`
* **Required Constraints:** All code changes must pass the relevant pytest tests and pylint checks. Dependency changes require a passing safety scan.
### Module Layer
### Commands and Development Workflow
* **Primary Reference:** `docs/rules/03-commands.md`
* **Required Constraints:** Only suggest or execute commands documented in that file. Do not assume tool defaults or global flags.
- `module` is the pluggable capability layer discovered and loaded by `ModuleManager`.
- Put logic in `module` when it represents a new downloader, media server, message channel, recognition backend, filtering backend, file-management backend, or any other capability that needs lifecycle management, priority, configuration switches, or independent testing.
- New modules should follow the existing base-class contract and implement or align with `init_module()`, `init_setting()`, `get_name()`, `get_type()`, `get_subtype()`, `get_priority()`, `test()`, and `stop()`.
- A `module` should focus on one backend or one capability implementation. It should return domain results, not HTTP responses, and should not depend on endpoint auth or FastAPI request objects.
- `chain -> module` is the intended main direction. The repository contains a small number of historical `module -> chain` usages. Do not expand that pattern in new code. If a module needs shared business logic, prefer moving that logic up into `chain` or down into `helper`.
- Do not add direct `module -> module` coupling for new code. Cross-module orchestration should be handled by `chain`.
---
### Helper Layer
## Agent Execution Rules
- `helper` is for reusable low-level support logic such as path handling, config aggregation, site index loading, protocol wrappers, rate limiting, cache helpers, and page parsing.
- Add a new `helper` only when the logic is reused in multiple places, or when it is clearly a standalone low-level concern.
- If logic is used only by a single `chain` or a single `module`, prefer keeping it in the original file instead of turning `helper` into a dumping ground.
- If the code needs configuration switches, runtime loading, priorities, independent test entrypoints, or multi-implementation dispatch, it is probably a `module`, not a `helper`.
- `helper` must not become another orchestration layer. Full business workflows still belong in `chain`.
### Pre-Flight Check
### Preferred Call Directions
Before generating any code or proposing changes, you must:
- Preferred direction: `endpoint/CLI/agent/command -> chain -> module/helper/db`
- Allowed direction: `chain -> chain`, as long as the reused logic is stable and does not introduce cycles.
- Cautious direction: `endpoint -> db/model/oper/helper`, only for simple queries, simple CRUD, or input normalization.
- Avoid for new code: `module -> chain`, `module -> module`, `helper -> chain`, `helper -> endpoint`.
1. Identify the task domain (architecture / business logic / coding style / naming / comments / external interfaces / data / quality).
2. Load the corresponding document from `docs/rules/`.
3. Explicitly verify that your proposed solution does not violate the following three mandatory constraints:
- **Naming Conventions (07):** Are all files, classes, functions, and constants named correctly?
- **Architecture Boundaries (05):** Is the code placed in the correct layer? Are all call directions valid?
- **Comment Standards (08):** Do all new public classes and methods include Chinese docstrings?
## 5. Where New Capabilities Should Go
### Implementation Guidelines
- Scenario: adding a new business workflow such as search, recognition, subscription, download orchestration, or message interaction.
Action: prefer `app/chain/` so API, CLI, agents, and schedulers can share the same orchestration logic.
- Scenario: adding a new downloader, media server, message channel, or other pluggable backend integration.
Action: put it in `app/modules/`. If this introduces a new module category or subtype, also check `app/schemas/types.py` and related schemas.
- Scenario: adding a new public HTTP API.
Action: put it in `app/api/endpoints/`, register it in `app/api/apiv1.py`, and add auth, schemas, docs, and tests. Move complex logic into `chain`.
- Scenario: adding a new low-level utility, parser, config reader, or protocol wrapper.
Action: put it in `app/helper/`, but only if it is not a one-off implementation and not a full business use case.
- Scenario: adding a deployment-level, environment-level, or startup-time config such as ports, paths, proxies, switches, keys, or third-party service addresses.
Action: put it in `ConfigModel` or `Settings` inside `app/core/config.py`.
- Scenario: adding a runtime business config, user-editable rule, or persistent system option.
Action: prefer `SystemConfigKey` plus `SystemConfigOper`. Do not scatter raw string keys.
- Scenario: a config change should automatically reload a long-lived object.
Action: add `CONFIG_WATCH`, `on_config_changed()`, and `get_reload_name()` where appropriate on the related `chain`, `module`, `helper`, or manager class.
- Scenario: adding a few dozen lines of private logic inside one `chain` or `module`.
Action: prefer a private function or private method in the same file. Do not create a new `helper` by default.
* **Pattern Adherence:** Avoid generic boilerplate. If `04-design-patterns.md` defines a project-level pattern for a scenario, you are required to use it.
* **Documentation Standards:** Docstring style for any new function or module must match `08-comment-styles.md`.
* **⚠️ MANDATORY GATE:** Public classes, methods, and functions without proper Chinese docstrings are **REJECTED**. No exceptions.
* **Command Reliance:** Only suggest commands listed in `03-commands.md`. Do not rely on inferred tool defaults.
* **Minimal Change Principle:** Prefer the smallest correct change. Do not perform unrelated refactors, mass renames, or formatting-only cleanup.
* **Output Language:** Summaries, validation results, and risk notes default to Chinese unless the user requests otherwise.
## 6. Code And Comment Requirements
### Conflict Resolution
- Preserve the existing code style. Do not introduce a new abstraction layer without a clear payoff.
- The repository already uses short docstrings for many public classes and methods. For new public classes and methods, follow the local style of the surrounding file.
- Comments and docstrings should default to Chinese. If the surrounding file is already consistently in English, match the local style.
- Comments should explain why the code is written that way and what non-obvious constraints exist, such as edge cases, compatibility reasons, call ordering, cache or reload semantics, and external system limitations.
- Do not write line-by-line translation comments. Do not comment obvious assignments, branches, or straightforward calls.
- For complex notes, place the comment above the code block instead of using long end-of-line comments.
- When changing code, update or remove stale comments so the documentation stays aligned with the implementation.
- Do not add TODO or FIXME without context. Only keep one if it is genuinely useful and cannot be addressed as part of the current task.
- Do not add noisy comments like "change starts here", "change ends here", or "this is important".
If existing code appears to contradict the documentation:
## 7. Dependency And Environment Conventions
1. Stop implementation immediately.
2. Identify the specific file and line of the contradiction.
3. Prompt the user: "The documentation in `[File]` requires Pattern A, but the current implementation uses Pattern B. Which is the current standard?"
- Target Python version is `3.11+`. Current CI uses Python `3.12`.
- The dependency source file is `requirements.in`.
- `requirements.txt` is the lock file generated by `pip-compile requirements.in`. Do not maintain it manually.
- Install dependencies with `pip install -r requirements.txt`.
- When adding or upgrading dependencies:
1. Update `requirements.in`
2. Run `pip-compile requirements.in`
3. Run the relevant tests and security checks
---
## 8. Coupled Updates
## Coupled Update Rules
- When fixing a bug, prefer adding a test that reproduces it. When adding a feature, prefer the smallest useful test coverage.
- When changing CLI behavior, also check and update `moviepilot`, `docs/cli.md`, and related tests.
- When changing MCP or REST API behavior, exposed tools, or AI interaction behavior, also check and update `docs/mcp-api.md`, related `skills/*/SKILL.md` files or scripts, and related tests.
- When changing development workflow, dependency management, or security-check procedures, also update `docs/development-setup.md`.
- When changing database structure, add an Alembic migration under `database/versions/`. Do not update models without a migration.
- When changing user-visible config, defaults, or initialization flow, also check related docs, help text, setup or init flows, and tests.
- When adding a new skill, follow the existing `skills/<name>/SKILL.md` structure, keep the YAML front matter, and prefer script paths relative to the `SKILL.md` file.
When modifying the following, you must also update the listed artifacts:
## 9. Validation Requirements
| Changed Content | Must Also Update |
|---|---|
| CLI behavior | `moviepilot` entrypoint, `docs/cli.md`, related tests |
| MCP / REST API, exposed tools | `docs/mcp-api.md`, `skills/*/SKILL.md`, related tests |
| Dev workflow, dependency management, security checks | `docs/development-setup.md` |
| Database model schema | New Alembic migration under `database/versions/` |
| User-visible config or init flow | Related docs, help text, setup/init flows, tests |
| New skill | Follow `skills/<name>/SKILL.md` structure, keep YAML front matter |
- Run at least the tests directly related to the change, for example `pytest tests/test_xxx.py`.
- If the change affects common modules, startup flow, CLI, or agent runtime behavior, expand the validation scope.
- After Python code changes, at minimum ensure the change does not introduce new error-level issues in `pylint app/`.
- When changing CLI behavior, validate the relevant help output such as `moviepilot help` or the specific subcommand help.
- When changing dependencies, also run `pip-compile requirements.in` and `safety check -r requirements.txt --policy-file=safety.policy.yml`.
- If the task only changes documentation, explicitly say that tests were not run. Do not claim checks that were not executed.
---
## 10. Commit And Release Conventions
## Primary Entry Point
- Only create a commit when the user explicitly asks for one.
- Prefer Conventional Commits such as `feat: ...`, `fix: ...`, and `docs: ...`.
- This is not just stylistic. The release workflow uses Conventional Commits to categorize changelog entries.
- Do not casually change version numbers, release settings, or Docker release flow unless the task explicitly involves them.
For the full documentation map and cross-references, refer to:
## 11. Output Requirements
**[Documentation Hub Index](./docs/rules/README.md)**
- Result summaries should focus on three things: what changed, how it was validated, and what risks remain.
- Do not write vague summaries. Do not describe unexecuted checks as completed.
- If there is compatibility impact, config migration risk, or user-data risk, call it out explicitly.
*Last Updated: 2026-05-25*

View File

@@ -0,0 +1,84 @@
# 01 — Project Overview
## System Purpose
MoviePilot is a self-hosted media automation platform targeting Chinese-language users. It automates the full lifecycle of media acquisition and organization:
1. **Discovery** — monitors RSS feeds, subscription lists, and recommendation sources for new media releases.
2. **Search** — queries configured torrent indexers to locate suitable torrents for subscribed media.
3. **Download** — sends torrent tasks to a configured download client (qBittorrent, Transmission, rTorrent).
4. **Transfer** — moves or hard-links completed downloads into a structured media library.
5. **Scraping** — fetches metadata (posters, descriptions, episode info) from TMDB, TheTVDB, Douban, and Bangumi.
6. **Media Server Integration** — notifies and refreshes Emby, Jellyfin, or Plex after files are organized.
7. **Messaging** — sends status notifications through Telegram, WeChat, Feishu, Slack, Discord, and other channels.
8. **AI Agent** — provides a conversational agent interface (via MCP and LLM chain) for natural-language management tasks.
---
## Repository Boundaries
### What Is in This Repository
| Path | Content |
|---|---|
| `app/` | FastAPI backend application |
| `moviepilot` | Local CLI entrypoint (install, init, start, stop, update, agent) |
| `app/api/endpoints/` | HTTP endpoint handlers |
| `app/chain/` | Business orchestration layer |
| `app/modules/` | Pluggable backend integrations (downloaders, media servers, etc.) |
| `app/helper/` | Reusable low-level utilities |
| `app/db/` | SQLAlchemy models and data access wrappers |
| `app/core/` | Config, event system, module manager, plugin manager, security |
| `app/schemas/` | Pydantic request/response models and shared enums |
| `app/agent/` | LLM agent runtime |
| `app/workflow/` | Workflow engine |
| `database/versions/` | Alembic migration scripts |
| `docs/` | CLI, MCP/API, and development workflow documentation |
| `skills/` | AI agent skills and associated scripts |
| `tests/` | Pytest test suite |
### What Is NOT in This Repository
* **Frontend source code** — lives in the separate `MoviePilot-Frontend` repository (Vue/TypeScript). Only the built `dist/` artifact is consumed here.
* **Plugin source code** — plugins are installed into `app/plugins/` at runtime from external sources; they are not part of this repository.
* **User config and runtime data** — `config/`, `.moviepilot.env`, `*.db` files are local runtime state. Do not modify or commit them unless explicitly requested.
---
## Deployment Models
### Docker (Primary)
The standard deployment method. A Docker image bundles the backend, frontend static files, and resource data. Users configure via environment variables and mount a config directory.
### Local CLI
An alternative for users running from source. The `moviepilot` CLI handles installation, initialization, service management, and updates. See `docs/cli.md` for the full command reference.
---
## Key External Dependencies (Domain Context)
| Service Type | Supported Backends |
|---|---|
| Torrent indexers | Site-specific spiders, Jackett/Prowlarr compatible |
| Download clients | qBittorrent, Transmission, rTorrent |
| Media servers | Emby, Jellyfin, Plex, TrimMedia, Zspace, Ugreen |
| Metadata sources | TMDB, TheTVDB, Douban, Bangumi, Fanart |
| Message channels | Telegram, WeChat, WeChatClawBot, Feishu, Slack, Discord, VoceChat, Synology Chat, WebPush, QQBot |
| LLM providers | OpenAI-compatible, Anthropic, and other configurable providers |
---
## Business Domain Vocabulary
| Term | Meaning |
|---|---|
| Subscribe | A tracked media item (movie or TV series) that MoviePilot will automatically search and download |
| Transfer | The process of moving or hard-linking downloaded files into the organized media library |
| Chain | A business orchestration class that coordinates multiple modules for a use case |
| Module | A pluggable backend integration loaded by the module manager |
| Skill | A packaged AI agent capability that can be invoked via the MCP interface |
| SystemConfig | Runtime key-value configuration stored in the database and managed via `SystemConfigKey` |
*Last Updated: 2026-05-25*

144
docs/rules/02-tech-stack.md Normal file
View File

@@ -0,0 +1,144 @@
# 02 — Tech Stack
## Runtime and Language
| Item | Detail |
|---|---|
| Language | Python 3.11+ |
| CI Python version | Python 3.12 |
| Async runtime | asyncio (native), integrated with FastAPI/Uvicorn |
---
## Backend Framework
| Item | Detail |
|---|---|
| Web framework | FastAPI |
| ASGI server | Uvicorn |
| Data validation | Pydantic v2 (`BaseModel`, `BaseSettings`, `model_validator`) |
| Settings management | `pydantic-settings` (`BaseSettings` class in `app/core/config.py`) |
---
## Database
| Item | Detail |
|---|---|
| Default database | SQLite |
| Optional database | PostgreSQL (configured via `DB_TYPE` and related env vars) |
| ORM | SQLAlchemy |
| Migration tool | Alembic (`database/versions/`) |
| PostgreSQL extras | `app/modules/postgresql/` module; setup guide at `docs/postgresql-setup.md` |
---
## Caching
| Item | Detail |
|---|---|
| File-based cache | `FileCache` / `AsyncFileCache` in `app/core/cache.py` |
| Redis | Optional; `app/modules/redis/` module; used for distributed caching when configured |
| In-process cache | Decorator helpers `fresh` / `async_fresh` on `FileCache` |
---
## LLM and AI Agent
| Item | Detail |
|---|---|
| Agent runtime | `app/agent/` — custom LLM agent orchestration |
| LLM abstraction | LangChain-based with multi-provider support |
| Supported providers | OpenAI-compatible APIs, Anthropic, and other configurable providers |
| Configuration | `LLM_PROVIDER`, `LLM_MODEL`, `LLM_API_KEY`, `LLM_BASE_URL` in settings |
| Enable flag | `AI_AGENT_ENABLE` |
| MCP protocol | JSON-RPC 2.0 at `/api/v1/mcp`; see `docs/mcp-api.md` |
---
## Module Integrations
### Download Clients
| Module | Directory |
|---|---|
| qBittorrent | `app/modules/qbittorrent/` |
| Transmission | `app/modules/transmission/` |
| rTorrent | `app/modules/rtorrent/` |
### Media Servers
| Module | Directory |
|---|---|
| Emby | `app/modules/emby/` |
| Jellyfin | `app/modules/jellyfin/` |
| Plex | `app/modules/plex/` |
| TrimMedia | `app/modules/trimemedia/` |
| Zspace | `app/modules/zspace/` |
| Ugreen | `app/modules/ugreen/` |
### Message Channels
| Module | Directory |
|---|---|
| Telegram | `app/modules/telegram/` |
| WeChat | `app/modules/wechat/` |
| WeChatClawBot | `app/modules/wechatclawbot/` |
| Feishu | `app/modules/feishu/` |
| Slack | `app/modules/slack/` |
| Discord | `app/modules/discord/` |
| VoceChat | `app/modules/vocechat/` |
| Synology Chat | `app/modules/synologychat/` |
| WebPush | `app/modules/webpush/` |
| QQBot | `app/modules/qqbot/` |
### Metadata Sources
| Module | Directory |
|---|---|
| TMDB | `app/modules/themoviedb/` |
| TheTVDB | `app/modules/thetvdb/` |
| Douban | `app/modules/douban/` |
| Bangumi | `app/modules/bangumi/` |
| Fanart | `app/modules/fanart/` |
---
## Dependency Management
| Item | Detail |
|---|---|
| Source file | `requirements.in` — edit this to add or upgrade dependencies |
| Lock file | `requirements.txt` — generated by `pip-compile`; never edit manually |
| Tool | `pip-tools` (`pip-compile`, `pip-sync`) |
| Install | `pip install -r requirements.txt` |
---
## Performance Extension
| Item | Detail |
|---|---|
| Rust extension | `moviepilot_rust` — optional compiled accelerator for core processing paths |
| Build | Requires Rust `cargo`; built automatically by `moviepilot install deps` |
| Skip flag | `MOVIEPILOT_SKIP_RUST_ACCEL=1` disables build (falls back to Python implementation) |
| Toggle | Can be disabled/re-enabled at runtime via frontend Advanced Settings → Lab |
---
## Quality Tooling
| Tool | Purpose | Command |
|---|---|---|
| pytest | Test runner | `pytest tests/test_xxx.py` |
| pylint | Static analysis | `pylint app/` |
| safety | Dependency vulnerability scan | `safety check -r requirements.txt --policy-file=safety.policy.yml` |
---
## Deployment
| Method | Detail |
|---|---|
| Docker | Primary deployment; image bundles backend + frontend static files + resources |
| Local CLI | `moviepilot` CLI for source-based install; see `docs/cli.md` |
| Frontend | Vue/TypeScript SPA served from `public/`; source in `MoviePilot-Frontend` repo |
| Frontend proxy | Local Node `service.js` proxies `/api` and `/cookiecloud` to the backend |
*Last Updated: 2026-05-25*

259
docs/rules/03-commands.md Normal file
View File

@@ -0,0 +1,259 @@
# 03 — Commands
Only suggest or execute commands that appear in this document. Do not assume standard tool defaults, global flags, or operating-system-specific behavior unless explicitly listed here.
---
## Development Environment Setup
```bash
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate # macOS / Linux
.\venv\Scripts\activate # Windows
# Install pip-tools
pip install pip-tools
# Install project dependencies
pip install -r requirements.txt
```
---
## Dependency Management
```bash
# Compile requirements.txt from requirements.in (full recompile)
pip-compile requirements.in
# Upgrade a single package without touching others
pip-compile --upgrade-package <package-name> requirements.in
# Install from the generated lock file
pip install -r requirements.txt
```
**Rules:**
- Always edit `requirements.in` to add or change dependencies.
- Never edit `requirements.txt` manually — it is a generated lock file.
- After any change to `requirements.in`, re-run `pip-compile requirements.in` and commit both files together.
---
## Testing
```bash
# Run a specific test file
pytest tests/test_xxx.py
# Run all tests
pytest
# Run tests with verbose output
pytest -v tests/test_xxx.py
# Run a specific test function
pytest tests/test_xxx.py::test_function_name
```
**Rules:**
- Run at minimum the tests directly related to the change.
- If the change affects common modules, startup flow, CLI, or agent runtime behavior, expand the scope to the full test suite.
- If the task only changes documentation, state explicitly that tests were not run. Do not claim checks that were not executed.
---
## Static Analysis
```bash
# Run pylint on the application package
pylint app/
# Run pylint on a specific module
pylint app/chain/download.py
```
**Rules:**
- After Python code changes, ensure no new error-level issues are introduced.
- Warning-level issues in new code should be minimized but are not an absolute gate.
---
## Security Scan
```bash
# Run safety check against the lock file
safety check -r requirements.txt --policy-file=safety.policy.yml
# Save report to file
safety check -r requirements.txt --policy-file=safety.policy.yml > safety_report.txt
```
**Rules:**
- Run after every change to `requirements.txt`.
- No new high-severity vulnerabilities may be introduced.
---
## Local CLI — Service Management
```bash
moviepilot start
moviepilot start --timeout 60
moviepilot stop
moviepilot stop --timeout 30 --force
moviepilot restart
moviepilot restart --start-timeout 60 --stop-timeout 30
moviepilot status
moviepilot version
```
```bash
moviepilot logs
moviepilot logs --lines 100
moviepilot logs --stdio
moviepilot logs --frontend
moviepilot logs --follow
moviepilot logs --frontend --follow
moviepilot logs --stdio --follow
```
---
## Local CLI — Installation and Setup
```bash
# One-line bootstrap installer
curl -fsSL https://raw.githubusercontent.com/jxxghp/MoviePilot/v2/scripts/bootstrap-local.sh | bash
# Install backend dependencies
moviepilot install deps
moviepilot install deps --python python3.11
moviepilot install deps --venv /path/to/venv
moviepilot install deps --recreate
# Install frontend release
moviepilot install frontend
moviepilot install frontend --version latest
moviepilot install frontend --version v2.9.31
# Install resource files
moviepilot install resources
# Initialize local config
moviepilot init
moviepilot init --wizard
moviepilot init --force-token
moviepilot init --superuser admin --superuser-password 'ChangeMe123!'
# All-in-one setup
moviepilot setup
moviepilot setup --wizard
moviepilot setup --recreate
moviepilot setup --superuser admin --superuser-password 'ChangeMe123!'
# Uninstall
moviepilot uninstall
```
---
## Local CLI — Update
```bash
moviepilot update backend
moviepilot update backend --ref latest
moviepilot update backend --ref v2.9.31
moviepilot update frontend
moviepilot update frontend --frontend-version latest
moviepilot update all
moviepilot update all --ref latest --frontend-version latest
moviepilot update all --skip-resources
```
---
## Local CLI — Startup on Boot
```bash
moviepilot startup status
moviepilot startup enable
moviepilot startup disable
moviepilot startup enable --venv /path/to/venv
```
---
## Local CLI — Configuration
```bash
moviepilot config path
moviepilot config list
moviepilot config list --show-secrets
moviepilot config get PORT
moviepilot config set PORT 3001
moviepilot config keys
moviepilot config keys DB_
moviepilot config keys --show-current
moviepilot config describe PORT
moviepilot config describe API_TOKEN --show-secrets
```
---
## Local CLI — Tools and Scheduler
```bash
# List all MCP tools
moviepilot tool list
# Show tool parameters
moviepilot tool show query_schedulers
moviepilot tool show search_torrents
# Run a tool directly
moviepilot tool run query_schedulers
moviepilot tool run search_torrents media_type=movie tmdb_id=12345
# List scheduled tasks
moviepilot scheduler list
# Immediately run a scheduled task
moviepilot scheduler run subscribe_refresh
```
---
## Local CLI — Agent
```bash
moviepilot agent "Help me analyze the last search failure"
moviepilot agent --user-id admin "Check the current downloader configuration"
moviepilot agent --session cli-debug-1 "Why was the last transfer not triggered?"
moviepilot agent --new-session "Summarize any obvious problems with the current system config"
```
**Prerequisites:** `AI_AGENT_ENABLE` must be set to true, and LLM provider settings (`LLM_PROVIDER`, `LLM_MODEL`, `LLM_API_KEY`) must be configured.
---
## Local CLI — Help Discovery
```bash
moviepilot --help
moviepilot help
moviepilot commands
moviepilot help install
moviepilot help init
moviepilot help setup
moviepilot help update
moviepilot help agent
moviepilot help config
moviepilot help tool
moviepilot help scheduler
```
*Last Updated: 2026-05-25*

View File

@@ -0,0 +1,219 @@
# 04 — Design Patterns
This document defines the structural patterns used across this codebase. When implementing complex features, you are required to use these patterns rather than inventing new abstractions.
---
## 1. Module Pattern (Pluggable Backends)
**When to use:** Adding a new downloader, media server, message channel, storage backend, or any other capability that requires lifecycle management, configuration switches, priority ordering, or independent testing.
**Base class:** `_ModuleBase` in `app/modules/__init__.py`
**Specialized base classes:**
- `_DownloaderBase` — for download clients
- `_MediaServerBase` — for media servers (implied by existing patterns)
**Required methods every module must implement:**
```python
class ExampleModule(_ModuleBase, _DownloaderBase):
def init_module(self) -> None:
"""模块初始化"""
super().init_service(service_name=..., service_type=...)
def init_setting(self) -> Tuple[str, Union[str, bool]]:
"""返回控制此模块开关的配置项名称和匹配值"""
return "DOWNLOADER", "example"
@staticmethod
def get_name() -> str:
return "Example"
@staticmethod
def get_type() -> ModuleType:
return ModuleType.Downloader
@staticmethod
def get_subtype() -> DownloaderType:
return DownloaderType.Example
@staticmethod
def get_priority() -> int:
return 1
def test(self) -> Optional[Tuple[bool, str]]:
"""测试模块连通性"""
...
def stop(self):
pass
```
**Module directory convention:** `app/modules/<backend_name>/` containing at minimum `__init__.py` (the module class) and the implementation class.
**Module types** are defined in `app/schemas/types.py` as `ModuleType`, `DownloaderType`, `MediaServerType`, `MessageChannel`, `StorageSchema`, `OtherModulesType`. When adding a new category, update these enums.
---
## 2. Chain Orchestration Pattern
**When to use:** Adding a new business workflow that is shared across multiple entrypoints (API endpoint, CLI, agent, scheduler, webhook). Chains coordinate modules, helpers, databases, events, and caches.
**Base class:** `ChainBase` in `app/chain/__init__.py`
**Calling modules from a chain:**
```python
# Preferred: call via run_module / async_run_module
result = self.run_module("method_name", kwarg1=val1, kwarg2=val2)
result = await self.async_run_module("method_name", kwarg1=val1)
# Only use ModuleManager directly when you need to enumerate modules,
# inspect instances, or run health checks.
```
**Chain-to-chain calls:** A chain may call another chain to reuse stable domain logic. Avoid introducing new circular dependencies between chains.
**File convention:** `app/chain/<domain>.py`, class name `<Domain>Chain` (e.g., `DownloadChain`, `SearchChain`, `SubscribeChain`).
---
## 3. Event / Observer Pattern
**When to use:** Triggering cross-cutting reactions (e.g., notifying the media server after a transfer completes, reloading a module after config changes, dispatching user messages to message channels).
**Core classes:** `EventManager` (singleton instance `eventmanager`) and `Event` in `app/core/event.py`.
**Registering a handler:**
```python
from app.core.event import eventmanager, Event
from app.schemas.types import EventType
@eventmanager.register(EventType.TransferComplete)
def on_transfer_complete(self, event: Event):
event_data = event.event_data
...
```
**Sending an event:**
```python
eventmanager.send_event(EventType.TransferComplete, data_dict)
```
**Event types** are defined as `EventType` and `ChainEventType` enums in `app/schemas/types.py`. Add new event types there when extending the event system.
---
## 4. Repository (Oper) Pattern
**When to use:** All database reads and writes. Never issue SQLAlchemy queries directly from chain, module, or endpoint code.
**Convention:** Each SQLAlchemy model in `app/db/models/` has a corresponding `<Model>Oper` class in `app/db/<model>_oper.py`.
```
app/db/models/subscribe.py → app/db/subscribe_oper.py (SubscribeOper)
app/db/models/systemconfig.py → app/db/systemconfig_oper.py (SystemConfigOper)
app/db/models/transferhistory.py → app/db/transferhistory_oper.py (TransferHistoryOper)
```
**Usage:**
```python
from app.db.subscribe_oper import SubscribeOper
oper = SubscribeOper()
subscribe = oper.get(sid=1)
oper.add(Subscribe(name="Example", type="电影"))
```
---
## 5. Config Reload Pattern
**When to use:** A chain, module, or helper holds a long-lived object that must be rebuilt when specific configuration keys change (e.g., a downloader client reconnects when its host/port changes).
**Mixin:** `ConfigReloadMixin` in `app/utils/mixins.py`
**How it works:**
1. Inherit `ConfigReloadMixin`.
2. Define a `CONFIG_WATCH` class attribute as a set of config key names.
3. Implement `on_config_changed()` — called automatically when any watched key changes.
4. Optionally implement `get_reload_name()` to provide a descriptive name for log messages.
```python
class MyChain(ChainBase, ConfigReloadMixin):
CONFIG_WATCH = {"DOWNLOADER", "QB_HOST", "QB_PORT"}
def on_config_changed(self):
self.init_module()
```
`_ModuleBase` already inherits `ConfigReloadMixin` and calls `init_module()` from `on_config_changed()` by default. Modules typically only need to declare `CONFIG_WATCH`.
---
## 6. Singleton Pattern
**When to use:** Classes that must have exactly one instance shared application-wide (e.g., `EventManager`, `ModuleManager`, `PluginManager`).
**Implementation:** Inherit from `Singleton` in `app/utils/singleton.py`.
```python
from app.utils.singleton import Singleton
class MyManager(metaclass=Singleton):
...
```
Do not introduce new singletons unless the class genuinely manages global shared state. Prefer dependency injection or parameter passing for everything else.
---
## 7. SystemConfig Pattern
**When to use:** Storing runtime business configuration that is user-editable, persistent across restarts, and not tied to a specific deployment environment.
**Enum:** `SystemConfigKey` in `app/schemas/types.py`
**Oper class:** `SystemConfigOper` in `app/db/systemconfig_oper.py`
```python
from app.schemas.types import SystemConfigKey
from app.db.systemconfig_oper import SystemConfigOper
oper = SystemConfigOper()
value = oper.get(SystemConfigKey.RssUrls)
oper.set(SystemConfigKey.RssUrls, ["https://..."])
```
**Rule:** Never use raw string literals as SystemConfig keys. Always add a new entry to the `SystemConfigKey` enum first.
---
## 8. UserConfig Pattern
**When to use:** Per-user settings that must survive across sessions but differ by user.
**Oper class:** `UserConfigOper` in `app/db/userconfig_oper.py`
Usage mirrors `SystemConfigOper` but scoped to a `user_id`.
---
## Anti-Patterns to Avoid
| Anti-Pattern | Correct Alternative |
|---|---|
| `module -> chain` coupling | Move shared logic into `chain` or down into `helper` |
| `module -> module` direct calls | Use `chain` to orchestrate cross-module workflows |
| `helper -> chain` dependency | `helper` must remain a low-level utility; move orchestration to `chain` |
| Raw SQLAlchemy queries in endpoints or chains | Use the corresponding `*_oper.py` class |
| Raw string keys for SystemConfig | Define and use a `SystemConfigKey` enum entry |
| HTTP requests via `requests` or `httpx` directly | Use `RequestUtils` from `app/utils/http.py` |
*Last Updated: 2026-05-25*

View File

@@ -0,0 +1,169 @@
# 05 — Architecture and Modules
## Layer Overview
The application is structured as four distinct layers. Each layer has a defined responsibility, and dependency may only flow in permitted directions.
```
┌──────────────────────────────────────────────────┐
│ Entrypoints │
│ (API Endpoints / CLI / Agent / Scheduler / │
│ Webhook / Message Interaction) │
└────────────────────┬─────────────────────────────┘
┌──────────────────────────────────────────────────┐
│ Chain Layer (app/chain/) │
│ Business orchestration: search, download, │
│ subscribe, transfer, message, recommend, etc. │
└──────┬──────────────┬───────────────┬────────────┘
│ │ │
▼ ▼ ▼
┌────────────┐ ┌──────────┐ ┌────────────────┐
│ Module │ │ Helper │ │ DB / Oper │
│ Layer │ │ Layer │ │ Layer │
│ (app/ │ │ (app/ │ │ (app/db/) │
│ modules/) │ │ helper/)│ │ │
└────────────┘ └──────────┘ └────────────────┘
```
---
## Layer Responsibilities and Boundaries
### Entrypoint Layer
**Directories:** `app/api/endpoints/`, `moviepilot` (CLI), `app/agent/`, scheduler callbacks, webhook handlers, message interactions.
**Responsibilities:**
- HTTP concerns: authentication, parameter parsing, response model serialization, streaming adaptation, simple input validation.
- Simple list, detail, toggle, settings read/write, and pure CRUD endpoints may call `app/db/` or a helper directly.
- Any logic that coordinates multiple modules, triggers events, touches caches, or combines workflows must be moved into `chain`.
**Rules:**
- Prefer adding new endpoints to an existing domain file. Create a new endpoint file only when introducing a new top-level resource domain.
- After adding a new endpoint, register it in `app/api/apiv1.py`.
- Endpoints must not contain business logic that belongs in `chain`.
---
### Chain Layer
**Directory:** `app/chain/`
**Responsibilities:**
- Business orchestration shared by API, CLI, agent, scheduler, and other entrypoints.
- Composes module capabilities, helpers, database access, events, and caches.
- Focuses on use cases and workflows.
**Rules:**
- Call module capabilities via `run_module()` or `async_run_module()`. Use `ModuleManager` directly only when enumerating, inspecting, or running health checks.
- Do not hold low-level protocol details, HTTP request objects, or page-specific parameter assembly.
- Before creating a new chain file, verify the workflow is genuinely reused across multiple entrypoints, or coordinates multiple modules. If it is short logic for a single endpoint, keep it in the endpoint.
- Chain-to-chain calls are allowed when reusing stable domain logic. Avoid introducing new circular dependencies.
---
### Module Layer
**Directory:** `app/modules/`
**Responsibilities:**
- Pluggable capability implementations: downloaders, media servers, message channels, metadata sources, storage backends, subtitle backends, filter backends, etc.
- Manages lifecycle (init, stop), configuration switches, priority ordering, and independent testability.
**Module categories (defined in `app/schemas/types.py`):**
| Enum | Examples |
|---|---|
| `ModuleType.Downloader` | qBittorrent, Transmission, rTorrent |
| `ModuleType.MediaServer` | Emby, Jellyfin, Plex, TrimMedia, Zspace, Ugreen |
| `ModuleType.MessageChannel` | Telegram, WeChat, Feishu, Slack, Discord |
| `ModuleType.MetaData` | TMDB, TheTVDB, Douban, Bangumi, Fanart |
| `ModuleType.Indexer` | Site-specific torrent indexers |
| `ModuleType.Storage` | Alist, rclone, u115, local storage |
**Rules:**
- A module must focus on one backend or one capability. It returns domain result objects, not HTTP responses, and must not depend on FastAPI request objects or endpoint auth.
- Do not add direct `module → module` coupling for new code. Cross-module orchestration must go through `chain`.
- Do not expand the historical `module → chain` usage pattern. If a module needs shared business logic, move that logic into `chain` or down into `helper`.
---
### Helper Layer
**Directory:** `app/helper/`
**Responsibilities:**
- Reusable low-level support: path handling, config aggregation, site index loading, protocol wrappers, rate limiting, cache utilities, page parsing, notification helpers.
**Rules:**
- Add a new helper only when the logic is reused in multiple places, or it is clearly a standalone low-level concern.
- If logic is used only by a single chain or module, keep it in the original file. Do not turn `helper` into a dumping ground.
- If the code needs configuration switches, runtime loading, priorities, or multi-implementation dispatch, it is a `module`, not a `helper`.
- `helper` must not contain full business workflows.
---
### DB / Oper Layer
**Directory:** `app/db/`
**Responsibilities:**
- SQLAlchemy models under `app/db/models/`.
- Data access wrappers (`*_oper.py`) that encapsulate all database queries.
**Rules:**
- Never issue SQLAlchemy queries directly from chain, module, or endpoint code. Always use the corresponding `*_oper.py` class.
- Any schema change requires a new Alembic migration under `database/versions/`.
---
## Permitted Call Directions
| Direction | Status |
|---|---|
| `endpoint / CLI / agent / scheduler → chain` | ✅ Preferred |
| `endpoint / CLI / agent / scheduler → db / helper` | ✅ Allowed for simple CRUD and input normalization only |
| `chain → chain` | ✅ Allowed when reusing stable, non-circular domain logic |
| `chain → module` | ✅ Via `run_module()` / `async_run_module()` |
| `chain → helper` | ✅ Allowed |
| `chain → db` | ✅ Via `*_oper.py` classes |
| `module → chain` | ⚠️ Exists in legacy code; do not expand in new code |
| `module → module` | ❌ Forbidden in new code |
| `helper → chain` | ❌ Forbidden |
| `helper → endpoint` | ❌ Forbidden |
---
## Key File Locations
| Path | Purpose |
|---|---|
| `app/api/apiv1.py` | API router registration — register new endpoints here |
| `app/core/config.py` | `ConfigModel` and `Settings` — all deployment/env-level config |
| `app/schemas/types.py` | `SystemConfigKey`, `EventType`, `ModuleType`, and all shared enums |
| `app/core/module.py` | `ModuleManager` — discovers and manages module instances |
| `app/core/plugin.py` | `PluginManager` — discovers and manages plugin instances |
| `app/core/event.py` | `EventManager` + `Event` — the application event bus |
| `app/core/context.py` | `Context`, `MediaInfo`, `TorrentInfo` — shared domain context objects |
| `app/main.py` | Application startup and FastAPI instance |
| `database/versions/` | Alembic migration scripts |
---
## Where New Capabilities Go
| Scenario | Action |
|---|---|
| New business workflow shared by multiple entrypoints | `app/chain/` |
| New downloader, media server, message channel, or storage backend | `app/modules/<backend>/` |
| New public HTTP API endpoint | `app/api/endpoints/`, register in `app/api/apiv1.py` |
| New low-level utility reused in multiple places | `app/helper/` |
| New deployment/env/startup config (ports, paths, API keys) | `ConfigModel` in `app/core/config.py` |
| New runtime business config, user-editable rule, or persistent system option | `SystemConfigKey` + `SystemConfigOper` |
| Config change should reload a long-lived object | Add `CONFIG_WATCH` + `on_config_changed()` to the relevant class |
| Few dozen lines of private logic in one chain or module | Private function in the same file; do not create a new helper |
| New module category or subtype | Also update `app/schemas/types.py` |
*Last Updated: 2026-05-25*

View File

@@ -0,0 +1,121 @@
# 06 — Code Standards and Style
## General Principles
- Preserve the style of the surrounding file. When in doubt, read neighboring code first.
- Prefer the smallest correct change. Do not introduce a new abstraction layer without a clear payoff.
- Do not add features, refactors, or abstractions beyond what the task requires.
- Do not add error handling or validation for scenarios that cannot happen. Trust internal code and framework guarantees; only validate at system boundaries (user input, external API responses).
---
## Python Version and Typing
- Target: **Python 3.11+**. CI runs Python 3.12.
- **Type annotations are required** on all public methods and function signatures.
- Use `Optional[X]` for nullable types (do not use `X | None` — keep consistency with the existing codebase style).
- Use `Union[X, Y]` for multi-type parameters.
- Prefer `list[X]`, `dict[K, V]`, `tuple[X, Y]` built-in generics in new code (Python 3.9+); match the style of the surrounding file.
- Use `pathlib.Path` for all file path operations. Never use raw string concatenation for paths.
---
## Pydantic Models
- All request body and response models must be defined as Pydantic `BaseModel` subclasses in `app/schemas/`.
- Use `Field(...)` for required fields; use `Field(default=...)` or `Field(None)` for optional fields.
- Do not define ad-hoc `dict` return types for API responses — define a schema class.
- Settings and deployment configuration live in `ConfigModel` / `Settings` in `app/core/config.py` using `pydantic-settings`.
- Use `model_validator` for cross-field validation logic.
---
## Async and Concurrency
- Prefer `async def` for I/O-bound operations (network requests, database queries, file operations).
- Use `await` consistently; do not mix sync and async code paths in the same function without using `run_in_threadpool` from FastAPI or `asyncio.to_thread`.
- For CPU-bound work that must not block the event loop, submit to `ThreadHelper` (see `app/helper/thread.py`).
- Do not use bare `threading.Thread` in new code; use `ThreadHelper.submit()`.
---
## Imports
Order imports as follows, separated by blank lines:
1. Standard library (`import os`, `import json`, etc.)
2. Third-party packages (`from fastapi import ...`, `from pydantic import ...`)
3. Local application packages (`from app.chain import ...`, `from app.schemas import ...`)
Within each group, sort alphabetically. Do not use wildcard imports (`from module import *`) in application code.
---
## String Formatting
- Use **f-strings** for all string interpolation. Do not use `%` formatting or `.format()`.
- For log messages, use `logger.info(f"...")` — do not use lazy `%s` format in logger calls (the project does not rely on lazy evaluation here).
---
## Error Handling
- In **chain and module layers**: do not raise HTTP exceptions. Catch exceptions, log them, and return `None` or a domain-level error object so the caller can decide how to proceed.
- In **endpoint layer**: use FastAPI's `HTTPException` or the project's standard response schemas for errors.
- Never swallow exceptions silently. At minimum log the error with `logger.error(f"...: {str(err)}")`.
- Do not use bare `except:` — always catch a specific exception type or at minimum `Exception`.
```python
# Correct
try:
result = self.do_work()
except Exception as err:
logger.error(f"Failed to do work: {str(err)}")
return None
# Wrong — swallowing silently
try:
result = self.do_work()
except:
pass
```
---
## Logging
- Use `logger` from `app/log.py`. Do not import the standard library `logging` directly in application code.
- Log levels:
- `logger.debug(...)` — detailed diagnostic information, disabled by default.
- `logger.info(...)` — normal operational events.
- `logger.warning(...)` — unexpected but recoverable situations.
- `logger.error(...)` — failures that affect functionality.
- Keep log messages in Chinese unless the surrounding file consistently uses English.
---
## Constants and Magic Values
- Do not scatter raw string keys for `SystemConfig`. Add a `SystemConfigKey` enum entry and reference it.
- Do not use magic numbers or magic strings inline. Define a named constant or enum value.
---
## File Organization
- One primary class per file is the norm for chains, modules, and helpers.
- Private helper functions in the same file are preferable to extracting a new helper for single-use logic.
- Keep files focused on one domain concern.
---
## What Not To Do
- Do not introduce new third-party libraries without updating `requirements.in` and running `pip-compile`.
- Do not use `requests` or `httpx` directly for external HTTP calls — use `RequestUtils` from `app/utils/http.py`.
- Do not issue raw SQLAlchemy queries from chains, modules, or endpoints — use the `*_oper.py` classes.
- Do not add TODO or FIXME without context. Only keep one if it is genuinely deferred and cannot be addressed in the current task.
- Do not add noisy markers like `# change starts here`, `# important`, or `# this is a fix`.
- Do not write comments that restate what the code already clearly says.
*Last Updated: 2026-05-25*

View File

@@ -0,0 +1,102 @@
# 07 — Naming Conventions
All new code must follow these conventions. Consistent naming is how the codebase communicates intent without comments.
---
## Files
| Context | Convention | Examples |
|---|---|---|
| Python source files | `snake_case.py` | `download_chain.py`, `qbittorrent.py` |
| Module package directories | `snake_case/` | `qbittorrent/`, `synologychat/` |
| Test files | `test_<domain>.py` | `test_download_chain.py`, `test_subscribe_endpoint.py` |
| Alembic migrations | Auto-generated by Alembic; do not rename | `20240101_add_column.py` |
| Skill directories | `<kebab-case>/` | `transfer-failed-retry/`, `moviepilot-cli/` |
---
## Classes
| Context | Convention | Examples |
|---|---|---|
| Chain classes | `<Domain>Chain` | `DownloadChain`, `SearchChain`, `SubscribeChain` |
| Module classes | `<Backend>Module` | `QbittorrentModule`, `EmbyModule`, `TelegramModule` |
| Oper (data access) classes | `<Model>Oper` | `SubscribeOper`, `SystemConfigOper`, `TransferHistoryOper` |
| Helper classes | `<Domain>Helper` | `TorrentHelper`, `DirectoryHelper`, `MessageHelper` |
| Pydantic schema models | `PascalCase`, noun-focused | `MediaInfo`, `TorrentInfo`, `DownloadingTorrent` |
| SQLAlchemy model classes | `PascalCase`, singular noun | `Subscribe`, `TransferHistory`, `SystemConfig` |
| Enum classes | `PascalCase` | `MediaType`, `EventType`, `ModuleType` |
| Manager classes | `<Domain>Manager` | `ModuleManager`, `PluginManager`, `EventManager` |
| General classes | `PascalCase` | `MetaInfo`, `Context`, `ChainBase` |
---
## Functions and Methods
| Context | Convention | Examples |
|---|---|---|
| All functions and methods | `snake_case` | `get_subscribe`, `run_module`, `on_config_changed` |
| Private methods | `_snake_case` (leading underscore) | `_submit_download_added_task`, `_parse_result` |
| Event handler methods | `on_<event_name>` or descriptive | `on_transfer_complete`, `handle_config_changed` |
| Module interface methods | Match `_ModuleBase` contract | `init_module`, `init_setting`, `get_name`, `get_type`, `test`, `stop` |
| Oper methods | Verb + noun | `get`, `add`, `update`, `delete`, `list` |
---
## Variables and Parameters
| Context | Convention | Examples |
|---|---|---|
| Local variables | `snake_case` | `torrent_info`, `media_type`, `download_dir` |
| Instance attributes | `snake_case` | `self.download_history`, `self.config` |
| Constants (module-level) | `UPPER_SNAKE_CASE` | `DEFAULT_EVENT_PRIORITY`, `MIN_EVENT_CONSUMER_THREADS` |
| Private variables | `_snake_case` (leading underscore) | `_instance`, `_lock` |
| Type variables | `PascalCase` with `TypeVar` | `T = TypeVar("T")` |
---
## Enums
| Context | Convention | Examples |
|---|---|---|
| Enum class name | `PascalCase` | `MediaType`, `TorrentStatus`, `EventType` |
| Enum members | `PascalCase` (for complex enums) | `MediaType.MOVIE`, `EventType.TransferComplete` |
| String enum values | Match the domain language | `MediaType.MOVIE = '电影'`, `TorrentStatus.TRANSFER = '可转移'` |
| `SystemConfigKey` values | Match the config key as a string | `SystemConfigKey.RssUrls = "RssUrls"` |
---
## Configuration and Settings
| Context | Convention | Examples |
|---|---|---|
| `Settings` / `ConfigModel` fields | `UPPER_SNAKE_CASE` | `API_TOKEN`, `LLM_MODEL`, `QB_HOST` |
| `SystemConfigKey` enum members | `PascalCase` | `SystemConfigKey.RssUrls`, `SystemConfigKey.SubscribeFilter` |
| Environment variable names | `UPPER_SNAKE_CASE` | `AI_AGENT_ENABLE`, `DB_TYPE` |
---
## API Endpoints and Routers
| Context | Convention | Examples |
|---|---|---|
| Endpoint function names | `snake_case`, verb-first | `get_subscribe_list`, `add_download`, `delete_history` |
| URL path segments | `kebab-case` or `snake_case` matching existing patterns | `/api/v1/subscribe`, `/api/v1/transfer/history` |
| Router tags | Match the resource domain name | `"subscribe"`, `"download"`, `"media"` |
---
## Anti-Patterns
| Wrong | Correct |
|---|---|
| `class downloadchain:` | `class DownloadChain:` |
| `class QBModule:` | `class QbittorrentModule:` |
| `def GetSubscribe():` | `def get_subscribe():` |
| `TORRENT_info = ...` | `torrent_info = ...` |
| `def handleConfigChanged():` | `def on_config_changed():` or `def handle_config_changed():` |
| `SystemConfigOper().get("RssUrls")` | `SystemConfigOper().get(SystemConfigKey.RssUrls)` |
| `class subscribe_oper:` | `class SubscribeOper:` |
*Last Updated: 2026-05-25*

View File

@@ -0,0 +1,140 @@
# 08 — Comments and Documentation Style
## ⚠️ Mandatory Gate
All **public classes**, **public methods**, and **public functions** in this project must have Chinese docstrings. Code submitted without compliant docstrings on public interfaces will be **rejected at review**. No exceptions.
"Public" means anything not prefixed with `_`. This includes all methods on `ChainBase` subclasses, `_ModuleBase` subclasses, Pydantic schema classes, and endpoint functions.
---
## Docstring Format
### Single-line (for simple, obvious descriptions)
```python
def get_name() -> str:
"""获取模块名称"""
return "Qbittorrent"
```
### Multi-line (for methods with parameters, return values, or non-obvious behavior)
```python
def download(
self,
context: Context,
torrent: TorrentInfo,
download_dir: Path,
) -> Optional[str]:
"""
添加下载任务到下载器。
:param context: 当前媒体上下文,包含识别结果和种子选择信息
:param torrent: 要下载的种子信息
:param download_dir: 目标保存目录
:return: 成功时返回下载任务 ID失败时返回 None
"""
...
```
### Class docstrings
```python
class DownloadChain(ChainBase):
"""
下载处理链,负责协调搜索结果的种子选择、下载器调度和下载后处理。
"""
```
---
## Docstring Language Rule
- **Default:** Chinese.
- **Exception:** If the surrounding file is entirely and consistently in English, match the local style.
- Do not mix languages within a single docstring. Pick one and stay consistent for the whole file.
---
## Inline Comments
**Only add an inline or block comment when the WHY is non-obvious.** Good reasons to add a comment:
- A hidden external constraint (e.g., "this API returns stale data for up to 60 seconds after update")
- A subtle invariant the code must maintain
- A workaround for a specific third-party bug
- Call ordering or initialization requirements that are not apparent from the code
- Compatibility reasons with a specific client version or protocol
**Do not add a comment when:**
- The code already explains itself through well-named identifiers
- The comment would just restate what the code does in words
- The logic is straightforward branching or assignment
---
## Correct Examples
```python
# qBittorrent API 在添加种子后立即查询时可能返回空,需要短暂等待
time.sleep(0.5)
result = self.client.get_torrent(hash_id)
```
```python
# 此处必须先检查 module 是否已初始化,否则多线程并发调用时 get_instances() 可能返回空列表
if not self._initialized:
self.init_module()
```
---
## Incorrect Examples
```python
# 获取订阅列表 ← 这只是在重述代码,不需要
subscribes = SubscribeOper().list()
# 如果 result 为 None 则返回 ← 无意义
if result is None:
return None
# change starts here ← 噪音,禁止
# fix: handle edge case ← 噪音,改成提交信息里写
```
---
## Comment Placement
- Place block comments **above** the code they describe, not on the same line.
- Use same-line end-of-line comments only for very short clarifications (e.g., unit of a constant).
- For long explanations, prefer a block comment above the code rather than a multiline end-of-line comment.
```python
# 优先使用已有的下载目录映射,避免重复计算路径
effective_dir = self._resolve_download_dir(torrent) or download_dir
```
---
## Stale Comment Rule
When modifying code, update or remove any comment that no longer accurately describes the implementation. A stale comment is worse than no comment — it actively misleads future readers.
---
## Prohibited Patterns
| Pattern | Why |
|---|---|
| `# change starts here` / `# change ends here` | Editorial noise; belongs in git history, not source |
| `# TODO` without context or assignee | Accepted only when the deferral is genuinely unavoidable and the reason is documented |
| `# FIXME` left in submitted code | Fix it now or document exactly why it cannot be fixed |
| `# this is important` | Every line of code is important; this adds nothing |
| Commented-out dead code | Delete it; git history preserves it |
| Docstrings in English on new public interfaces | Violation of the mandatory Chinese docstring gate |
*Last Updated: 2026-05-25*

View File

@@ -0,0 +1,174 @@
# 09 — External APIs, Protocols, and Responses
## HTTP Client Conventions
**Rule:** All outbound HTTP requests must go through `RequestUtils` from `app/utils/http.py`. Do not use `requests`, `httpx`, or `aiohttp` directly.
`RequestUtils` handles:
- Proxy configuration (from `settings.PROXY_*`)
- Timeouts
- SSL verification settings
- User-Agent headers
- Retry logic
```python
from app.utils.http import RequestUtils
res = RequestUtils(
ua=settings.USER_AGENT,
proxies=settings.PROXY,
timeout=30,
).get_res(url="https://api.example.com/data")
if res and res.status_code == 200:
data = res.json()
```
---
## Response Format — REST API
All REST API responses use Pydantic schema models from `app/schemas/`. Do not return raw `dict` objects from endpoints.
### Standard Response Patterns
```python
# Success with data
from app.schemas.response import Response
return Response(success=True, message="", data=result)
# Success without data
return Response(success=True, message="操作成功")
# Error
return Response(success=False, message="错误原因描述")
```
### List Responses
For paginated lists, follow the pattern of existing endpoint files. Check `app/api/endpoints/` for examples matching the resource domain.
### Error Responses (Endpoint Layer Only)
In endpoints, raise `HTTPException` for request-level errors:
```python
from fastapi import HTTPException
raise HTTPException(status_code=404, detail="Resource not found")
raise HTTPException(status_code=403, detail="Permission denied")
```
Do not raise `HTTPException` in chain or module code. Chains and modules return `None` or domain-level error objects on failure; the endpoint translates that into an HTTP response.
---
## Error Handling by Layer
| Layer | On external API failure |
|---|---|
| Module | Log the error, return `None` or `(False, "error message")` tuple |
| Chain | Log the error, return `None` or an appropriate domain object with failure indication |
| Endpoint | Translate `None` or failure result into a `Response(success=False, ...)` or `HTTPException` |
```python
# Module layer
def test(self) -> Optional[Tuple[bool, str]]:
"""测试模块连通性"""
try:
ok = self.client.ping()
return (True, "连接成功") if ok else (False, "连接失败")
except Exception as err:
logger.error(f"测试连通性失败:{str(err)}")
return (False, str(err))
```
---
## MCP Protocol
MoviePilot exposes an MCP (Model Context Protocol) interface for AI agent integration.
- **Transport:** HTTP, JSON-RPC 2.0
- **Base path:** `/api/v1/mcp`
- **Protocol versions supported:** `2025-11-25`, `2025-06-18`, `2024-11-05`
### Authentication
```
Header: X-API-KEY: <api_key>
Query: ?apikey=<api_key>
```
### Supported Methods
| Method | Description |
|---|---|
| `initialize` | Initialize session, negotiate protocol version and capabilities |
| `notifications/initialized` | Client confirmation of initialization |
| `tools/list` | List all available tools |
| `tools/call` | Invoke a specific tool |
| `ping` | Connection liveness check |
### Error Codes
| Code | Message | Meaning |
|---|---|---|
| -32700 | Parse error | Malformed JSON |
| -32600 | Invalid Request | Invalid JSON-RPC request structure |
| -32601 | Method not found | Unknown method |
| -32602 | Invalid params | Parameter validation failure |
| -32002 | Session not found | Session does not exist or has expired |
| -32003 | Not initialized | Session has not completed initialization |
| -32603 | Internal error | Server-side error |
### Tool Response Format
MCP tools return structured content. Errors must use the JSON-RPC error object format, not HTTP status codes.
---
## Notification and Messaging
Internal notifications use the `Notification` schema and the event system:
```python
from app.schemas import Notification
from app.schemas.types import NotificationType, MessageChannel
from app.core.event import eventmanager
from app.schemas.types import EventType
eventmanager.send_event(
EventType.NoticeMessage,
{
"channel": MessageChannel.Telegram,
"type": NotificationType.Download,
"title": "下载成功",
"text": f"{media_name} 已添加到下载队列",
"image": poster_url,
}
)
```
Do not call message channel modules directly from chain code. Use the event bus to decouple senders from channels.
---
## Media Metadata API Conventions
When calling TMDB, TheTVDB, Douban, or Bangumi via the module layer:
- Always check the module return for `None` before using the result — modules return `None` when the backend is not configured or the request fails.
- Cache responses using `FileCache` / `AsyncFileCache` where the result is stable and repeated requests would be expensive.
- Return domain objects (`MediaInfo`, `TmdbEpisode`, `MediaPerson`, etc.) from modules, never raw API response dicts.
---
## Webhook Handling
Webhook payloads arrive at `app/api/endpoints/webhook.py` and are dispatched via `eventmanager.send_event(EventType.WebhookMessage, ...)`. Processing logic lives in the chain layer (`app/chain/webhook.py`).
Do not add webhook-specific business logic directly in the endpoint. The endpoint parses the payload and fires the event; the chain handles the response.
*Last Updated: 2026-05-25*

View File

@@ -0,0 +1,177 @@
# 10 — Data and Persistent Management
## Database Models
**Location:** `app/db/models/`
Models are SQLAlchemy declarative classes. Each model maps to one database table.
| Model | Table Domain |
|---|---|
| `Subscribe` | Media subscriptions |
| `SubscribeHistory` | Completed subscription records |
| `TransferHistory` | File transfer history |
| `DownloadHistory` / `DownloadFiles` | Download task history and file list |
| `MediaServerItem` | Media server library item cache |
| `SystemConfig` | Runtime key-value configuration store |
| `UserConfig` | Per-user configuration store |
| `User` | User accounts |
| `Site` / `SiteIcon` / `SiteStatistic` / `SiteUserData` | Torrent site records and statistics |
| `Message` | Message log |
| `PluginData` | Plugin-persisted data |
| `PassKey` | Passkey authentication records |
| `Workflow` | Workflow definitions |
---
## Alembic Migrations
**Location:** `database/versions/`
**Rule:** Any change to a SQLAlchemy model schema (adding a column, renaming a column, changing a column type, adding a table, removing a table) **requires a new Alembic migration script**. Never update models without a corresponding migration.
**Generating a migration:**
```bash
# Auto-generate from model diff
alembic revision --autogenerate -m "describe the change"
# Create a blank migration for manual SQL
alembic revision -m "describe the change"
```
**Review the auto-generated migration before committing** — auto-generation can miss nullable changes, index modifications, or SQLite-incompatible operations.
---
## Data Access Layer (Oper Pattern)
**Location:** `app/db/`
Each model has a corresponding `*_oper.py` file containing the data access class. Do not write SQLAlchemy queries directly in chain, module, or endpoint code.
| Oper Class | File |
|---|---|
| `SubscribeOper` | `subscribe_oper.py` |
| `SystemConfigOper` | `systemconfig_oper.py` |
| `TransferHistoryOper` | `transferhistory_oper.py` |
| `DownloadHistoryOper` | `downloadhistory_oper.py` |
| `MediaServerOper` | `mediaserver_oper.py` |
| `UserOper` | `user_oper.py` |
| `UserConfigOper` | `userconfig_oper.py` |
| `MessageOper` | `message_oper.py` |
| `SiteOper` | `site_oper.py` |
| `PluginDataOper` | `plugindata_oper.py` |
| `WorkflowOper` | `workflow_oper.py` |
**Standard Oper method conventions:**
```python
oper = SubscribeOper()
subscribe = oper.get(sid=1) # Get by primary key or filter
subscribes = oper.list() # List all
oper.add(Subscribe(...)) # Insert
oper.update(sid=1, name="New Name") # Update by key
oper.delete(sid=1) # Delete by key
```
---
## SystemConfig — Runtime Configuration
**Purpose:** Runtime business configuration that is user-editable, persisted in the database, and survives application restarts.
**Enum:** `SystemConfigKey` in `app/schemas/types.py`
**Oper:** `SystemConfigOper` in `app/db/systemconfig_oper.py`
```python
from app.schemas.types import SystemConfigKey
from app.db.systemconfig_oper import SystemConfigOper
oper = SystemConfigOper()
# Read
rss_urls = oper.get(SystemConfigKey.RssUrls)
# Write
oper.set(SystemConfigKey.RssUrls, ["https://example.com/rss"])
```
**Rule:** Never use raw string literals as `SystemConfig` keys. Always define a new `SystemConfigKey` enum entry first. Raw string key lookups are not searchable and cannot be refactored safely.
---
## UserConfig — Per-User Configuration
**Purpose:** Settings that differ per user account. Uses `UserConfigOper`.
```python
from app.db.userconfig_oper import UserConfigOper
oper = UserConfigOper()
value = oper.get(user_id=1, key="notification_enabled")
oper.set(user_id=1, key="notification_enabled", value=True)
```
---
## Settings / Environment Configuration
**Purpose:** Deployment-level, environment-level, and startup-time configuration such as ports, paths, proxies, switches, API keys, and third-party service addresses.
**Location:** `ConfigModel` and `Settings` in `app/core/config.py`
These values are read from environment variables (or `.moviepilot.env`) at startup and are immutable at runtime. They are not stored in the database.
**Access:**
```python
from app.core.config import settings
host = settings.QB_HOST
port = settings.QB_PORT
```
---
## Caching
### FileCache / AsyncFileCache
**Location:** `app/core/cache.py`
Used to cache expensive external API responses to disk. Cache entries have a configurable TTL.
```python
from app.core.cache import FileCache, fresh
cache = FileCache(cache_name="tmdb", ttl=3600)
@fresh(cache=cache, key_func=lambda tmdb_id: f"movie_{tmdb_id}")
def get_movie_detail(tmdb_id: int) -> dict:
return self._tmdb_client.get_movie(tmdb_id)
```
### Redis (Optional)
When `REDIS_HOST` is configured, `app/modules/redis/` provides a distributed cache backend. Prefer `FileCache` for single-node deployments.
---
## Data Lifecycle Rules
- **TransferHistory:** Records are inserted after every successful file transfer. Do not delete records without user confirmation.
- **DownloadHistory:** Records are inserted when a download task is added. Linked `DownloadFiles` records track individual files within a torrent.
- **SystemConfig:** Values may be read and written freely at runtime. Changes to watched config keys trigger `on_config_changed()` on registered classes via `ConfigReloadMixin`.
- **MediaServerItem:** This is a cache of the remote media server library. It is refreshed on media server sync events and can be safely cleared and rebuilt.
---
## Sensitive Data Handling
- Never log database record contents that include personal data (user credentials, passkeys, API tokens).
- `settings.API_TOKEN` and other secret fields must not be included in log output or API responses.
- The `config list --show-secrets` flag exists specifically to gate secret visibility in the CLI.
*Last Updated: 2026-05-25*

View File

@@ -0,0 +1,139 @@
# 11 — Code Quality and Security
## Testing Requirements
### What to Run
```bash
# Minimum: run tests directly related to the change
pytest tests/test_<domain>.py
# If the change affects common modules, startup flow, CLI, or agent runtime
pytest
```
### When to Expand Scope
Run the full test suite when changing:
- `app/core/` — config, event system, module manager, plugin manager
- `app/chain/__init__.py` — chain base class
- `app/modules/__init__.py` — module base class
- `app/main.py` — application startup
- The CLI entrypoint (`moviepilot`)
- Agent runtime (`app/agent/`)
- Any shared schema in `app/schemas/types.py`
### Honest Reporting
- If a task only changes documentation, state explicitly that tests were not run.
- Do not claim "all tests pass" unless you ran them.
- Do not describe unexecuted checks as completed.
### Writing New Tests
- When fixing a bug, prefer adding a test that reproduces it first.
- When adding a feature, add at minimum the smallest useful test coverage.
- Test files go in `tests/`, named `test_<domain>.py`.
- Use the patterns established in adjacent test files (fixtures, mock patterns, assertion style).
- Agent-related tests are under `tests/test_agent_*.py`. Integration-style tests may be in `tests/cases/` or `tests/manual/`.
---
## Static Analysis
```bash
pylint app/
```
- After any Python code change, ensure no new **error-level** pylint issues are introduced.
- Warning-level issues in new code should be minimized but are not an absolute gate for submission.
- Do not suppress pylint warnings with `# pylint: disable` without a documented reason.
---
## Dependency Security Scan
```bash
safety check -r requirements.txt --policy-file=safety.policy.yml
```
- Run after every change to `requirements.txt`.
- No new high-severity vulnerabilities may be introduced.
- If a vulnerability cannot be patched immediately, document it explicitly in the PR description.
---
## Authentication and Authorization
### API Authentication
All REST and MCP API endpoints require authentication. The project supports two mechanisms:
| Method | Format |
|---|---|
| Request header | `X-API-KEY: <api_key>` |
| Query parameter | `?apikey=<api_key>` |
The `API_TOKEN` value in `settings` is the source of truth. It is set at initialization and never exposed in logs or API responses.
### Endpoint Authorization
- Use the existing FastAPI dependency functions (e.g., `get_current_user`, `get_current_active_superuser`) — check `app/api/endpoints/` for usage patterns.
- Do not add manual token parsing inside endpoint functions. Always use the project's dependency injection.
- Superuser-only operations must explicitly require the superuser dependency.
---
## Input Validation
- Validate user input at the **endpoint layer only**, using Pydantic models.
- Do not duplicate validation logic in chain or module code. Trust that the endpoint has already validated what it passes down.
- For external API responses, validate using Pydantic models or explicit `None` checks before accessing fields.
---
## Secrets Management
- Never hardcode secrets (API keys, passwords, tokens) in source code.
- All secrets are configured via environment variables or `.moviepilot.env` and accessed through `settings`.
- Never log or serialize `settings.API_TOKEN`, `settings.DB_PASSWORD`, or any field with `Secret` in its name.
- Do not commit `.moviepilot.env`, `*.db`, or any file under `config/` — these are local runtime state.
---
## SQL Injection Prevention
- All database access goes through SQLAlchemy ORM via the `*_oper.py` classes. No raw SQL string construction.
- If a raw SQL query is ever genuinely necessary, use SQLAlchemy's `text()` with parameterized binds — never string interpolation.
---
## XSS and Injection in Notifications
- When constructing notification messages that include user-provided data (media titles, filenames, usernames), treat those values as untrusted strings.
- Do not render user data in HTML contexts without escaping. Notification channels that render HTML (e.g., Telegram with `parse_mode=HTML`) must escape user-controlled strings.
---
## File Path Security
- Use `pathlib.Path` for all file path operations.
- Never construct file paths by concatenating user-provided strings.
- When transferring files to a user-configured path, verify the destination is within an allowed base directory before writing.
---
## Pre-Submission Checklist
Before marking any task as complete:
- [ ] Related pytest tests pass
- [ ] No new pylint error-level issues in `pylint app/`
- [ ] If dependencies changed: `pip-compile requirements.in` was run and `safety check` passes
- [ ] If CLI behavior changed: `docs/cli.md` and related tests are updated
- [ ] If MCP/API behavior changed: `docs/mcp-api.md` and related skill files are updated
- [ ] If database schema changed: a new Alembic migration exists under `database/versions/`
- [ ] No secrets are included in code, logs, or committed files
- [ ] Public classes and methods have Chinese docstrings
*Last Updated: 2026-05-25*

View File

@@ -0,0 +1,123 @@
# 12 — Collaboration, Versioning, Build, and Release
## Commit Conventions
This project uses **Conventional Commits**. The release workflow parses commit messages to categorize changelog entries. This is not stylistic — it is functional.
### Format
```
<type>(<optional scope>): <description>
[optional body]
[optional footer]
```
### Commit Types
| Type | When to use |
|---|---|
| `feat` | A new feature visible to users |
| `fix` | A bug fix |
| `docs` | Documentation only changes |
| `chore` | Maintenance, dependency updates, tooling changes |
| `refactor` | Code restructuring without behavior change |
| `test` | Adding or modifying tests |
| `ci` | CI/CD pipeline changes |
| `perf` | Performance improvements |
### Examples
```
feat: support MiniMax audio provider
fix: sign media server image proxy URLs
docs: add MCP client configuration examples
chore: upgrade pydantic to 2.9.0
refactor: extract transfer path resolution into helper
test: add subscribe endpoint validation tests
ci: improve docker build cache
```
### Rules
- **Only create a commit when the user explicitly asks for one.**
- Keep the subject line under 72 characters.
- Use the imperative mood in the subject line ("add", "fix", "remove", not "added", "fixed", "removed").
- If a commit introduces a breaking change, append `!` after the type and include `BREAKING CHANGE:` in the footer.
---
## Branch Policy
- Do not casually create, rename, or delete branches without user instruction.
- The main development branch is the project default — check `git branch` rather than assuming it is `main` or `master`.
- Feature work lives on dedicated branches and is merged via pull request.
- Do not force-push to shared branches.
---
## Version Numbers
- Do not casually change version numbers in `version.py` or related files.
- Version changes are part of the release workflow and are only made when the task explicitly involves a release.
- The `FRONTEND_VERSION` field in `version.py` controls which frontend release the CLI and Docker build will download. Only update it as part of a coordinated frontend release.
---
## Docker Build and Release
- The primary Docker image bundles the backend (Python app), frontend static files (from `public/`), and resource data.
- Docker build and release are managed by CI. Do not manually trigger or alter the Docker release flow unless the task explicitly requires it.
- If a Dockerfile change is needed, update `Dockerfile` and verify the build locally before submitting.
---
## CI/CD
- CI runs on every push and pull request. The pipeline typically includes:
- Dependency installation
- pytest test suite
- pylint static analysis
- Docker image build (on main branch or tags)
- Do not merge code that fails CI unless there is an explicit, documented reason and user approval.
---
## Pull Request Guidelines
- Keep PRs focused on a single concern. Separate refactors, features, and bug fixes into distinct PRs when practical.
- Include in the PR description:
- What changed and why
- How the change was validated
- Any known risks or compatibility impact
- Migration steps if config or database schema changed
- Tag the PR with the appropriate label (`bug`, `feature`, `docs`, `chore`).
---
## Dependency Release Process
When updating a dependency:
1. Update `requirements.in` with the new version constraint.
2. Run `pip-compile requirements.in` to regenerate `requirements.txt`.
3. Run `safety check -r requirements.txt --policy-file=safety.policy.yml`.
4. Run the full test suite: `pytest`.
5. Commit both `requirements.in` and `requirements.txt` together.
---
## Local CLI Release
The `moviepilot` CLI is the local-mode entrypoint. Its update path is:
```bash
moviepilot update all # updates backend + frontend + resources
moviepilot update backend # git pull + reinstall deps
moviepilot update frontend
```
Bootstrap installer changes live in `scripts/bootstrap-local.sh`. Only modify this script if the task explicitly involves the bootstrap flow.
*Last Updated: 2026-05-25*

107
docs/rules/README.md Normal file
View File

@@ -0,0 +1,107 @@
# Documentation Hub
This repository maintains a structured documentation library covering the full development lifecycle. All rule documents live in the `docs/rules/` directory. This index maps each file to its technical domain and intended reader.
---
## Technical Document Index
### Section I: Foundation and Environment
* **01 Project Overview**
* File: `01-project-overview.md`
* Scope: System goals, business domain, deployment models, and what is and is not in this repository.
* **02 Tech Stack**
* File: `02-tech-stack.md`
* Scope: Frameworks, languages, libraries, runtime environments, and third-party integrations.
* **03 Commands**
* File: `03-commands.md`
* Scope: CLI reference, development triggers, testing commands, linting, and dependency management.
### Section II: Architecture and Logic
* **04 Design Patterns**
* File: `04-design-patterns.md`
* Scope: Project-specific structural, creational, and behavioral patterns: Module, Chain, Event, Oper, Config Reload, Singleton.
* **05 Architecture and Modules**
* File: `05-architecture.md`
* Scope: Layer boundaries, dependency directions, module categories, and the canonical call graph.
* **09 External APIs, Protocols, and Responses**
* File: `09-external-response.md`
* Scope: HTTP client conventions, MCP protocol, standardized response formats, and error handling by layer.
* **10 Data and Persistent Management**
* File: `10-data-and-persistent.md`
* Scope: SQLAlchemy models, Alembic migrations, Oper access layer, SystemConfig, caching patterns.
### Section III: Implementation Standards
* **06 Code Standards and Style**
* File: `06-code-styles.md`
* Scope: Type annotations, Pydantic usage, async patterns, imports, formatting, and error handling rules.
* **07 Naming Conventions**
* File: `07-naming-conventions.md`
* Scope: Strict taxonomy for files, classes, functions, constants, and schema models.
* **08 Comments and Documentation Style**
* File: `08-comment-styles.md`
* Scope: Chinese docstring requirements, inline comment rules, and prohibited comment anti-patterns.
### Section IV: Quality and Governance
* **11 Code Quality and Security**
* File: `11-quality-and-security.md`
* Scope: Testing requirements, pylint gates, safety scans, authentication patterns, and input validation rules.
* **12 Collaboration, Versioning, Build, and Release**
* File: `12-collaboration-and-distribution.md`
* Scope: Conventional Commits, branch policy, release workflow, Docker build, and version management.
---
## Reader Persona Guidance
### Core Developers and Implementers
Developers actively writing or modifying features should follow this reading path:
1. **07 Naming Conventions** — establishes the lexicon for the feature.
2. **06 Code Standards** — ensures linting and logic compliance.
3. **04 Design Patterns** — identifies the correct structural approach.
4. **03 Commands** — required for local execution and validation.
### System Architects and Reviewers
Personnel focused on system integrity and long-term maintenance:
1. **05 Architecture and Modules** — for verifying structural boundaries.
2. **10 Data and Persistent Management** — for auditing data integrity and storage efficiency.
3. **09 External APIs** — for reviewing integration security and protocol compliance.
4. **11 Code Quality and Security** — for establishing the PR approval baseline.
### Operations and Release Engineers
Those managing the application lifecycle post-development:
1. **12 Collaboration and Versioning** — for release tags and branch management.
2. **02 Tech Stack** — for environment provisioning and dependency management.
3. **11 Code Quality and Security** — for verifying deployment-ready security posture.
---
## Document Interconnectivity
* **Architecture (05)** references **Code Standards (06)** for layer isolation and module boundary rules.
* **Naming Conventions (07)** works in tandem with **Comment Styles (08)** to define overall code readability.
* **External APIs (09)** relies on **Tech Stack (02)** for transport layer specifications and HTTP client selection.
* **Data Management (10)** is governed by **Quality and Security (11)** for sensitive data handling requirements.
* **Design Patterns (04)** is the implementation reference for decisions documented in **Architecture (05)**.
---
*Last Updated: 2026-05-25*