This file provides guidance to AI coding agents when working with code in this repository.
Use Conventional Commits format for easier Changelog generation and semantic versioning.
<type>(<scope>): <subject>
[optional body]
[optional footer]
Co-authored-by: <AI Name> <ai-email@example.com>
- type (required): Commit type; see table below.
- scope (required): Affected scope, e.g. plugin name
p115strmhelper,migudiscover, or area likeci,deps. - subject (required): Short description, ~50 chars; no period at the end.
- Co-authored-by (required when AI is involved): Footer line attributing the AI assistant that contributed to the commit; use the AI agent’s own identity (its canonical name and email as defined in its system prompt), not a hardcoded example. For instance, an agent identifying as "Sisyphus" should use
Sisyphus <sisyphus@ohmyopenCode.com>, while one identifying as "Cursor Agent" should useCursor Agent <cursoragent@cursor.com>.
| type | Description |
|---|---|
| feat | New feature |
| fix | Bug fix |
| docs | Documentation only (README, comments) |
| style | Code style (no logic change) |
| refactor | Refactor (not new feature/fix) |
| perf | Performance improvement |
| test | Tests |
| chore | Build/tooling/deps, etc. |
feat(p115strmhelper): support MCP tools/list and tools/call
Co-authored-by: Sisyphus <sisyphus@ohmyopenCode.com>
fix(p115strmhelper): fix offline task list pagination params
Co-authored-by: Sisyphus <sisyphus@ohmyopenCode.com>
- Commit messages must be in English; keep language consistent across the repo.
- AI Co-author required: Commits made by or with assistance from an AI coding agent must include a
Co-authored-by:line in the footer. The co-author must use the AI agent's own identity (its canonical name and email from its system prompt), not a hardcoded example. Use the agent that has a GitHub account so the co-author is linked correctly. - One logical change per commit; split unrelated changes into separate commits.
- For breaking changes, describe in body or footer; use
BREAKING CHANGE:when needed.
- Follow PEP 8: 4-space indent, line length ~88–120 chars, spaces around operators, etc.
- Strings: prefer double quotes
"; use single quotes when embedding double quotes. - Trailing commas: allowed at end of multi-line structures (lists, dicts, args) for cleaner diffs.
- Comments (
#line comments) and docstrings: do not end a line with a terminal period (neither.nor Chinese。). Applies to summary lines and:param/:return/:raiseslines alike.
- Public functions and methods must have type annotations; internal helpers are encouraged.
- Use
typing:List,Dict,Optional,Any,Union,Tuple, etc. - Add docstring notes for complex or ambiguous parameters and return values.
from typing import Any, Dict, List, Optional
async def run_tool(api: Any, name: str, arguments: Dict[str, Any]) -> str:
...- Standard library (alphabetical)
- Blank line
- Third-party packages (alphabetical)
- Blank line
- Project/plugin relative imports (alphabetical)
from pathlib import Path
from re import match as re_match
from time import sleep
from fastapi import Request
from orjson import dumps as orjson_dumps
from .api import Api
from .mcp import MCPManager
from .version import VERSION- Modules/packages: lowercase with hyphens or underscores, e.g.
db_manager,mcp. - Classes:
CapWords. - Functions, methods, variables, parameters:
snake_case. - Constants:
UPPER_SNAKE_CASE. - Private implementation: single leading underscore
_internal_func; module-level "private" may use_.
- Avoid bare
except:; use at leastexcept Exceptionand handle at an appropriate level. - Use the project's shared
logger(e.g.from app.log import logger) for errors and important info; uselogger.error(..., exc_info=True)at exception sites for debugging. - User-facing messages should be clear and actionable; internal logs may include more technical detail.
- Use
async deffor async functions; call other async APIs withawait. Avoid blocking calls inside async functions; wrap withasyncio.to_threadwhen needed. - Keep the async call chain consistent; do not forget to await coroutines.
- Plugin entry must inherit
_PluginBaseand implementplugin_name,plugin_version, etc. as required. - Use the project's config prefix (e.g.
plugin_config_prefix), events, and message channels so the interface matches the main app. - Put dependencies in the plugin's
requirements.txt; prefer~=for minor-version pinning.
All public classes, methods, and functions carry a Chinese docstring. Chinese is the project's working language for descriptive prose; symbol names remain English.
Docstring structure follows this order:
- One-line summary description.
- (Optional) Blank line + detailed description of behavior, edge cases, etc.
- (Optional) Blank line +
:paramlines — one per parameter. - (Optional) Blank line +
:returnline. - (Optional) Blank line +
:raiseslines — one per exception type. - (Optional)
:yieldsline.
Standard template:
"""
<description>
<detail description>
:param <name> (<Type>): <description>
:return <Type>: <description>
:raises <ExceptionType>: <description>
:yields <Type>: <description>
"""Concrete example:
from pathlib import Path
from typing import List, Optional, Tuple
from app.schemas import FileItem
class P115Api:
"""
115 网盘基础操作类
"""
def get_pid_by_path(self, path: Path) -> int:
"""
通过文件夹路径获取文件夹 ID
:param path (Path): 文件夹路径
:return int: 目录 ID
"""
def create_folder(
self, fileitem: FileItem, name: str
) -> Optional[FileItem]:
"""
创建目录
:param fileitem (FileItem): 父目录文件项
:param name (str): 要创建的目录名称
:return FileItem: 创建成功返回目录文件项,失败返回 None
"""
def upload(
self,
target_dir: FileItem,
local_path: Path,
new_name: Optional[str] = None,
) -> Optional[FileItem]:
"""
上传文件到云盘
:param target_dir (FileItem): 上传目标目录项
:param local_path (Path): 本地文件路径
:param new_name (str): 上传后的文件名,如果为 None 则使用本地文件名(代码中为 Optional[str],文档仅标注 str)
:return FileItem: 上传成功返回文件项,失败返回 None
"""Requirements:
- Triple-quoted
""", blank line on opening and closing edges. - Chinese prose.
- Import generic containers from
typing: useList/Dict/Tuple/Optional, and do not use Python-native generics such aslist[str]ortuple[int, ...]. Seeplugins.v2/p115disk/p115_api.pyfor reference. - Parameter form:
:param name (Type): description. - Return form:
:return Type: description— single concrete type where possible. - Raise form:
:raises ExceptionType: description. - Yield form:
:yields Type: description. - Blank line between description, params, return, raises, and yields blocks.
Docstring type labels use only the parent / container type, excluding
Optional wrappers and inner type parameters. This keeps docstrings concise
and avoids duplicating verbose annotations already present in code.
| Code annotation | Docstring label | Reason |
|---|---|---|
Optional[List[str]] |
List |
Strip Optional, strip inner [str] |
Optional[int] |
int |
Strip Optional wrapper |
Optional[Dict[str, int]] |
Dict |
Strip Optional, strip inner type params |
List[FileItem] |
List |
Container type only |
Dict[str, str] |
Dict |
Container type only |
Tuple[str, ...] |
Tuple |
Container type only |
Generator[int, None, None] |
Generator |
Container type only |
str / int / bool |
str / int / bool |
Bare types unchanged |
Optional[str] |
str |
Strip Optional wrapper |
Examples from the codebase (plugins.v2/p115disk/p115_api.py):
# p115_api.py — return stripped to container type, param types in parentheses
def list(self, fileitem: FileItem) -> List[FileItem]:
"""
浏览文件或目录
:param fileitem (FileItem): 文件项,可以是文件或目录
:return List: 文件项列表,如果是文件则返回包含该文件的列表,如果是目录则返回目录下的所有文件和子目录
"""
# p115_api.py — bare types (str) unchanged, container types (List, Dict) bare
def iter_files(
self, fileitem: FileItem
) -> Optional[List[FileItem]]:
"""
递归遍历文件夹
:param fileitem (FileItem): 文件项,可以是文件或目录
:return List: 文件项列表
"""Class attributes, dataclass fields, and enum variant docstrings must not
appear as individual multiline docstrings beneath each member. Instead,
collect them into an Attributes: section inside the parent class/enum
docstring.
Correct — collective Attributes section:
class StrmApiData(BaseModel):
"""
API 调用生成 STRM 数据
Attributes:
id: 文件 ID
name: 文件名
sha1: 文件 SHA1
size: 文件大小
pick_code: 文件 pickcode
local_path: 本地路径
pan_path: 网盘路径
pan_media_path: 网盘媒体库路径
media_server_refresh: 是否刷新媒体服务器
scrape_metadata: 是否刮削元数据
auto_download_mediainfo: 是否自动下载媒体元数据
"""
id: Optional[int] = Field(default=None, description="文件ID")
name: Optional[str] = Field(default=None, description="文件名")
sha1: Optional[str] = Field(default=None, description="文件SHA1")
size: Optional[int] = Field(default=None, description="文件大小")
pick_code: Optional[str] = Field(default=None, description="文件pickcode")
local_path: Optional[str] = Field(default=None, description="本地路径")
pan_path: Optional[str] = Field(default=None, description="网盘路径")
pan_media_path: Optional[str] = Field(default=None, description="网盘媒体库路径")
media_server_refresh: Optional[bool] = Field(
default=None, description="是否刷新媒体服务器"
)
scrape_metadata: Optional[bool] = Field(default=None, description="是否刮削元数据")
auto_download_mediainfo: bool = Field(
default=False, description="是否自动下载媒体元数据"
)- Every public class.
- Every public method / function.
- Every
@staticmethodon a service or API class. - Every Pydantic model field via
Field(description="...").
Do not include code usage examples inside docstrings. They bloat the
docstring, drift out of sync with the actual API, and distract from the
contract description. If an example is genuinely helpful, put it in a
dedicated examples/ directory or a test file.
Correct — describe the contract, not the usage:
class P115Api:
"""
115 网盘基础操作类
封装 115 网盘的文件列表、上传下载、目录管理、快照等核心操作
提供限速、缓存和错误重试机制
"""Do not use line-separator comments (# ── … ──) to visually group
sections inside a file. They add noise, break searchability, and duplicate
what structural elements (classes, blank lines, import groups) already
provide.
Correct — plain fields, no separators:
_enabled = False
_client = None
_disk_name = None
_p115_api = None
_cookie = None- Private helpers (
_prefix) — docstring when behavior is non-obvious, otherwise skip. - Trivial one-line lambdas / factories.
Default to no inline comments. Code should explain what it does through clear names and shape. Add a comment only when the why is non-obvious.
Write comments for:
- Protection / risk-control trade-offs (
# 防止误删:旧路径与新生成路径相同(原地 move 场景)). - Version-specific workarounds with an expiry condition (
# 检查 token 是否即将过期(提前 5 分钟刷新)). - Version-specific workarounds with an expiry condition.
Do not write comments that:
- Restate code (
# increment counter). - Describe the current task, PR, or reviewer handoff.
- Mark removed code (
# was: xyz()) — Git history is authoritative.
from typing import Optional
from pydantic import BaseModel, Field
class StrmApiData(BaseModel):
"""
API 调用生成 STRM 数据
"""
id: Optional[int] = Field(default=None, description="文件ID")
name: Optional[str] = Field(default=None, description="文件名")
sha1: Optional[str] = Field(default=None, description="文件SHA1")
size: Optional[int] = Field(default=None, description="文件大小")
pick_code: Optional[str] = Field(default=None, description="文件pickcode")
local_path: Optional[str] = Field(default=None, description="本地路径")
pan_path: Optional[str] = Field(default=None, description="网盘路径")Descriptions are mandatory and in Chinese; they become the single source of truth used by docs and log-formatting helpers.
# @deprecated: use P115Api.create_folder instead — removal in v0.3.0.
def legacy_mkdir(...):
...- Must cite the replacement.
- Must cite a removal milestone (version, date, or release tag).
- Deprecated code is removed within one minor release of the marker —
no
// removedtombstones left behind.
Release notes are generated from commit messages in this repository. Keep commit subjects descriptive enough to be published verbatim.
Before committing:
- Message follows type(scope): subject format
- Footer includes
Co-authored-by: <AI Name> <email>when AI contributed - New/changed public API has type annotations and docstrings
- Imports are ordered correctly; no unused imports
- Exception handling and logging are appropriate
During review:
- Logic is correct; edge cases and error paths considered
- No hardcoded secrets (keys, tokens)
- Consistent with existing plugin style and MoviePilot conventions
Last Updated: 2026-06-04