From aac374b6f425f23ee5b653a7e9c95197edf53a44 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Thu, 13 Aug 2026 00:39:27 +0800 Subject: [PATCH] =?UTF-8?q?fix(context):=20AGENTS.md=20=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E4=BB=85=E5=8F=96=E6=A0=B9=E7=9B=AE=E5=BD=95=EF=BC=8C=E5=AD=90?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E6=96=87=E4=BB=B6=E5=8F=AA=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E7=9B=B8=E5=AF=B9=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _load_agents_md_content 由 rglob 递归改为只读根目录 AGENTS.md - 新增一级子目录扫描:仅通知相对路径(最多 20 个、30s 超时兜底),不注入内容 - 跳过通用非源码目录(.git/node_modules/.venv/dist 等),不针对本项目特化 - 注入模板新增修改时间与子目录通知占位符 --- core/main_terminal_parts/context/messages.py | 27 +++++++- core/main_terminal_parts/context/prompt.py | 71 +++++++++++++++----- prompts/agents_md_inject.txt | 4 +- 3 files changed, 83 insertions(+), 19 deletions(-) diff --git a/core/main_terminal_parts/context/messages.py b/core/main_terminal_parts/context/messages.py index 3391d0c2..0dc662e8 100644 --- a/core/main_terminal_parts/context/messages.py +++ b/core/main_terminal_parts/context/messages.py @@ -88,6 +88,21 @@ logger = setup_logger(__name__) DISABLE_LENGTH_CHECK = True +def _build_sub_agents_md_notice(sub_paths: List[str], total: int) -> str: + """构建子目录 AGENTS.md 路径通知文案(只列相对路径,不注入内容)。无子目录文件时返回空串。""" + if not sub_paths: + return "" + lines = ["另外,工作区以下子目录也存在 AGENTS.md 规范文件:", ""] + for p in sub_paths: + lines.append(f"- `{p}`") + if total > len(sub_paths): + lines.append("") + lines.append(f"...共 {total} 个") + lines.append("") + lines.append("这些子目录规范仅适用于其所在目录范围。若你的操作涉及上述目录,请先自行读取对应 AGENTS.md 了解局部规范后再动手,不得将子目录规范当作全局规范套用。") + return "\n".join(lines) + + class MessagesMixin: """MainTerminalContextMixin messages 能力 mixin。""" @@ -244,10 +259,18 @@ class MessagesMixin: agents_md_content = self._load_agents_md_content() if not agents_md_content: return "" + # 子目录 AGENTS.md 只通知相对路径,不注入内容 + sub_paths, sub_total = self._load_sub_agents_md_paths() + sub_notice = _build_sub_agents_md_notice(sub_paths, sub_total) agents_md_template = self.load_prompt("agents_md_inject").strip() if agents_md_template and "{{AGENTS_MD_CONTENT}}" in agents_md_template: - return agents_md_template.replace("{{AGENTS_MD_CONTENT}}", agents_md_content) - return f"【AGENTS.md 项目规范】\n\n{agents_md_content}\n\n---\n请注意:以上规范来自工作区根目录的 AGENTS.md 文件,若有冲突请以 AGENTS.md 为准。" + text = agents_md_template.replace("{{AGENTS_MD_CONTENT}}", agents_md_content) + text = text.replace("{{AGENTS_MD_UPDATED_AT}}", self._load_agents_md_updated_at()) + text = text.replace("{{SUB_AGENTS_MD_NOTICE}}", sub_notice) + return text + # 模板缺失/无占位符时的兜底格式 + text = f"【AGENTS.md 项目规范】{self._load_agents_md_updated_at()}\n\n{agents_md_content}\n\n---\n请注意:以上规范来自工作区根目录的 AGENTS.md 文件,若有冲突请以 AGENTS.md 为准。" + return f"{text}\n\n{sub_notice}" if sub_notice else text # AGENTS.md 注入开关可能变化,只在开启时生成并缓存,避免关闭时把空字符串冻住 agents_md_inject_enabled = ( diff --git a/core/main_terminal_parts/context/prompt.py b/core/main_terminal_parts/context/prompt.py index 230e234b..095460c5 100644 --- a/core/main_terminal_parts/context/prompt.py +++ b/core/main_terminal_parts/context/prompt.py @@ -1,5 +1,7 @@ import asyncio import json +import time +from datetime import datetime from pathlib import Path from typing import Any, Dict, List, Optional, Set @@ -86,35 +88,72 @@ from config.model_profiles import ( logger = setup_logger(__name__) DISABLE_LENGTH_CHECK = True +# 扫描一级子目录 AGENTS.md 时跳过的通用非源码目录(版本控制/依赖/构建产物/工具数据等,不针对本项目特化) +_AGENTS_MD_SKIP_DIRS = { + ".git", ".hg", ".svn", + "node_modules", "bower_components", + ".venv", "venv", "__pycache__", + "dist", "build", ".next", ".nuxt", ".output", + ".astrion", ".claude", ".cursor", ".idea", ".vscode", +} + class PromptMixin: """MainTerminalContextMixin prompt 能力 mixin。""" def _load_agents_md_content(self) -> Optional[str]: - """加载工作区根目录的 AGENTS.md 文件内容。 - - 如果存在多个 AGENTS.md 文件,返回最新更新的那一个。 - """ + """加载工作区根目录的 AGENTS.md 文件内容(仅根目录,子目录的不注入)。""" try: project_path = Path(self.project_path) - if not project_path.exists(): + root_file = project_path / "AGENTS.md" + if not root_file.is_file(): return None - - # 查找所有 AGENTS.md 文件 - agents_md_files = list(project_path.rglob("AGENTS.md")) - if not agents_md_files: - return None - - # 找到最新更新的文件 - latest_file = max(agents_md_files, key=lambda p: p.stat().st_mtime) - - # 读取文件内容 - content = latest_file.read_text(encoding='utf-8') + content = root_file.read_text(encoding='utf-8') return content.strip() if content else None except Exception as exc: logger.warning(f"[AGENTS.md] 读取失败: {exc}") return None + def _load_agents_md_updated_at(self) -> str: + """返回根目录 AGENTS.md 的最后修改时间文案(如「(最后修改:2026-08-12 17:54)」,失败返回空串)。""" + try: + root_file = Path(self.project_path) / "AGENTS.md" + if not root_file.is_file(): + return "" + mtime = root_file.stat().st_mtime + return f"(最后修改:{datetime.fromtimestamp(mtime).strftime('%Y-%m-%d %H:%M')})" + except Exception: + return "" + + def _load_sub_agents_md_paths(self, max_notice: int = 20, timeout_seconds: float = 30.0) -> tuple: + """扫描工作区一级子目录中的 AGENTS.md,返回 (相对路径列表, 实际总数)。 + + - 仅扫描根目录与一级子目录,不递归;跳过通用非源码目录(_AGENTS_MD_SKIP_DIRS)。 + - 列表最多 max_notice 个(超出截断,总数仍在第二项返回)。 + - 扫描超过 timeout_seconds 秒时停止,返回已扫描到的结果。 + """ + found: List[str] = [] + total = 0 + try: + project_path = Path(self.project_path) + if not project_path.is_dir(): + return found, total + start = time.monotonic() + for sub in project_path.iterdir(): + if time.monotonic() - start >= timeout_seconds: + logger.warning(f"[AGENTS.md] 子目录扫描超过 {timeout_seconds}s,返回已扫描到的 {len(found)} 个") + break + if not sub.is_dir() or sub.name in _AGENTS_MD_SKIP_DIRS: + continue + candidate = sub / "AGENTS.md" + if candidate.is_file(): + total += 1 + if len(found) < max_notice: + found.append(candidate.relative_to(project_path).as_posix()) + except Exception as exc: + logger.warning(f"[AGENTS.md] 子目录扫描失败: {exc}") + return found, total + def load_prompt(self, name: str) -> str: """加载提示模板""" prompt_file = Path(PROMPTS_DIR) / f"{name}.txt" diff --git a/prompts/agents_md_inject.txt b/prompts/agents_md_inject.txt index 61cbf88c..bb1cd02c 100644 --- a/prompts/agents_md_inject.txt +++ b/prompts/agents_md_inject.txt @@ -1,8 +1,10 @@ 【AGENTS.md 项目规范】 -以下是工作区根目录 AGENTS.md 文件的内容,请在回答时参考这些项目规范: +以下是工作区根目录 AGENTS.md 文件的内容{{AGENTS_MD_UPDATED_AT}},请在回答时参考这些项目规范: {{AGENTS_MD_CONTENT}} --- 注意:以上规范来自工作区根目录的 AGENTS.md 文件,若与未来代码冲突,以实际代码为准并及时更新 AGENTS.md。 + +{{SUB_AGENTS_MD_NOTICE}}