Project Context Map
为开发项目生成一张紧凑、可检索的"地图"(project-map.md),内容包括:目录树、每文件一行摘要、关键符号索引(class/function/type)、文档大纲、依赖关系(文字)、调用关系(文字)、API 路由、死代码提示、核心文件重要性排序、密钥脱敏、以及部署架构角色(自动推断,文字);并可额外导出机器可读的 project-map.json。此后每次阅读项目资料前先查这张地图,按关键词定位到具体文件,只读命中的文件,避免盲目 Glob/Grep 全项目,显著节省 token。v1.6 在保持「离线 / 纯标准库 / 无 pip / 无 API key」根基不变的前提下,补齐了重要性排序、调用图、框架路由、死代码、密钥脱敏、JSON 导出与增量更新。
> v1.8(零依赖增量版):路由识别扩充(head/options/trace/all/api_route/websocket + NestJS @Get/@Post 装饰器 + Fastify);修复装饰器行污染调用图(@app.get(...) 不再把 get 计为调用,进而修正由此导致的死代码漏判);增量缓存版本化(version:2/mode:regex,旧缓存或未来切换提取模式时自动失效重建)。MCP 暴露与 Tree-sitter 精确解析经评估后暂缓(理由见下方「版本路线」)。
角色说明解决的是"哪个是服务器/客户端/设备端、谁连谁"这类部署架构问题——它基于代码指纹(systemd 单元 / PyInstaller spec / 服务器框架 / 配置文件里的外部端点 URL / 前端框架)自动推断逻辑角色与通信通道,并标注"本仓库服务器 vs 仓库外(仅桩)"。这是与"代码依赖分析"(import 关系)不同的另一类信息。
自动激活(核心 · 装完即用)
本技能设计为零配置自启:安装到用户级技能目录(~/.workbuddy/skills/)后,无需任何手动配置,只要会话工作在一个含源代码的项目/工作区,agent 会自动加载本技能并按下方协议建立/刷新地图——这就是"对每个会话/项目全量覆盖"的落地方式。
自动激活协议(agent 加载本技能后立即执行)
- 确定活动项目根:取当前工作区根目录(agent 打开的项目根);若用户明确指向某子目录项目,则用该目录。
- 确保地图在且新:运行
bash python3 scripts/build_map.py "<活动项目根>" --min-files 20
- 脚本用文件 hash 增量缓存(.workbuddy/project-map.cache.json),重跑只算变更文件,速度很快;
---min-files 20:文件数 < 20 的小项目自动跳过(直接读更快),不生成地图;
- 已存在地图且结构无大变动时,增量刷新通常 1–2 秒完成。 - 载入上下文:读取
<根>/.workbuddy/project-map.md作为本会话项目全局认知;之后查阅优先查地图、只读命中文件(见下方 search)。 - 会话中刷新:若本次会话项目结构发生较大变动,重新运行上面的命令刷新。
> 无需你每次提醒:每次进入含代码的项目,地图都会被自动保证最新,agent 不需要你动手调。
首次加载自检:补齐硬保证(一次性,可选)
agent 驱动的自启已够用;若你想要"会话开机即生成、完全不依赖 agent 匹配"的硬保证,本技能随包附带 scripts/hook_wrapper.sh(SessionStart hook)。
首次加载时,agent 应自检:读取 ~/.workbuddy/settings.json(WorkBuddy)或 ~/.codebuddy/settings.json(CodeBuddy Code),检查 hooks.SessionStart 是否已包含对该 hook_wrapper.sh 的引用:
- 已包含 → 什么都不做,硬保证已就位;
- 未包含 → 提示你并(经确认后)自动写入该 hook 项(只合并,不覆盖你其它配置)。
> 这样换机器 / 重装技能后,首次会话会自动发现"没配 hook"并一键帮你补上,你不用手写 JSON。本机若已配好则跳过。
何时使用
- 本技能默认已在含代码的项目会话自动激活(见上),一般无需手动触发。
- 手动触发场景:显式要求重建/刷新地图、对他人项目单独建图、或调试地图生成本身。
不适用于:极小项目(< 20 文件直接读更快,已被 --min-files 20 自动跳过)、纯二进制/资源仓库。
核心工作流
1. build — 生成/刷新项目地图
默认输出到 <项目根>/.workbuddy/project-map.md。
python3 scripts/build_map.py "<项目根>"
常用参数:
python3 scripts/build_map.py . --max-depth 5 --max-files 5000
python3 scripts/build_map.py . --no-symbols # 只建文件索引,更快
python3 scripts/build_map.py . --ignore dist,tmp,logs
python3 scripts/build_map.py . --depth-level 2 # 额外列出文件级依赖
python3 scripts/build_map.py . --alias "@=src,@app=src/app" # 补充路径别名(也会自动读 tsconfig/jsconfig 的 paths)
python3 scripts/build_map.py . --json # 额外导出 project-map.json(机器可读,供 RAG/agent)
python3 scripts/build_map.py . --map-tokens 4000 # 文件索引按重要性排序,超预算折叠低优尾部
参数说明:
| 参数 | 说明 | 默认值 |
|------|------|--------|
| root | 项目根目录 | 当前工作目录 |
| -o / --output | 输出路径 | <root>/.workbuddy/project-map.md |
| --max-depth | 目录树最大深度 | 4 |
| --max-files | 最多索引文件数 | 3000 |
| --ignore | 额外忽略(逗号分隔) | 无 |
| --no-symbols | 跳过符号提取 | 关闭 |
| --no-graph | 跳过部署角色推断与依赖/调用/路由/死代码分析(纯目录树+文件摘要,更快) | 关闭 |
| --depth-level | 依赖分析层级:1=模块级(默认),2=额外列出文件级依赖 | 1 |
| --alias | 额外路径别名,逗号分隔,如 @=src,@app=src/app;也会自动读取 tsconfig.json/jsconfig.json 的 paths | 无 |
| --json | 额外导出机器可读的 project-map.json(含 files/symbols/deps/calls/routes/roles/importance) | 关闭 |
| --map-tokens | 文件索引的 token 预算(估算);超出后按重要性折叠低优文件尾部 | 0(不限) |
| --timeout | 整体超时(秒,0=不限);超时后写出已扫描的部分地图并提示,防止超大项目卡顿 | 0(不限) |
| --min-files | 文件数低于该值则跳过生成地图(小项目直接读更快) | 0(不跳过) |
脚本纯 Python 标准库、跨平台,无需 pip install。自动跳过 node_modules/.git/dist/build/__pycache__/.venv 及二进制/图片/锁文件。
2. search — 先查地图再深入(省 token 关键,且会被真实用量统计记录)
> 真实用量统计(重要):本技能已内置埋点。每次 build/refresh 和每次 search 都会向
> ~/.workbuddy/pcm_usage.jsonl 追加一行 JSON 事件。查地图务必走下面的 --search 命令,
> 不要直接用 Read+Grep 手动翻地图——否则这次查询不会被计入真实用量,每日监控也就统计不到。
- 建/刷新地图(自动记一笔 build 事件):
bash python3 scripts/build_map.py "<项目根>" - 查地图定位并顺带读命中(自动记一笔 query 事件,并打印命中位置、自动读取命中文件内容,一步到位):
bash python3 scripts/build_map.py "<项目根>" --search "ConnectionPool"
命令会在地图内检索关键词,输出候选文件路径与行号,并默认自动读取前几个命中文件(每文件前 200 行),无需再手动Read。
- 只想看命中位置、不想读内容:--no-read
- 实际读取量(文件数/字节)也记入用量日志,供监控统计
禁止:在尚未查阅地图时,对整个项目目录做宽泛的 Glob/Grep 或逐文件 Read;也禁止绕过 --search 手动翻地图(会丢失真实用量记录)。
3. refresh — 项目变更后刷新
项目结构有较大变动后,重新运行 build 命令覆盖生成地图。
4. 硬保证:SessionStart hook(agent 首次加载会自动补,无需手写 JSON)
上面的"技能自动激活"是零配置软保证;若你想要"会话开机即生成、完全不依赖 agent 匹配"的硬保证,用随包附带的 scripts/hook_wrapper.sh(SessionStart hook)。它在会话开始时读取传入的项目目录环境变量,跑 build_map.py --timeout 300 --min-files 20,日志写入 <项目>/.workbuddy/project-map.log(不静默失败)。
不用你手写 JSON:技能首次加载时会自检 settings.json 是否已有该 hook,没有就提示你、确认后自动写入(只合并 hook 项,不覆盖其它配置)。本机若已配好则跳过。
已验证:本机 ~/.workbuddy/settings.json 的 hooks.SessionStart 已包含该引用,且每个会话开始都会实际触发(见 <项目>/.workbuddy/project-map.log)。
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "bash \"$HOME/.workbuddy/skills/project-context-map/scripts/hook_wrapper.sh\""
}
]
}
]
}
}
输出结构
project-map.md 依次包含:
- 项目概览:生成时间、根目录、文件数、总大小、语言分布
- 技术栈推断:根据
package.json/pyproject.toml/go.mod/Cargo.toml/pom.xml等指纹文件推断 - 目录结构:带深度的树形视图,每个目录标注文件数
- 文件索引表:
路径 | 语言 | 大小 | 摘要,摘要取自首行 docstring/注释/标题 - 关键符号索引:按文件列出 class/function/type(脚本支持 Python/JS/TS/TSX/Go/Rust/Java/Kotlin/C/C++/C#/Ruby/PHP/Swift/Dart/SQL)
- 文档大纲:Markdown/reST 文件的标题层级
- 依赖关系(文字):解析 import 反查本地文件,聚合到二级包级(如
device/core,而非只取首段device)生成有向依赖文字列表(A → B表示 A 依赖 B,括注依赖次数);支持相对路径、绝对包名、绝对导入省略顶层目录的后缀兜底(如from storage.x import实际位于<root>/device/storage/x)、别名路径如@/*;高连接度核心模块加粗标注。--depth-level 2额外列出文件级依赖;无跨模块内部依赖时标注"各文件相互独立" - 部署架构角色(自动推断,文字):放在地图靠前位置,回答"哪个是服务器/客户端/设备端、谁连谁"。基于代码指纹启发式推断逻辑角色并以文字列出,附"推断依据"说明:
- 设备端:检测
*.service(systemd 守护进程)、*.spec(PyInstaller 打包)、代码中的systemd/PyInstaller/edge device等强信号 - 服务器:检测
websockets.serve/uvicorn/FastAPI/flask/express/gin/SpringApplication等框架;文件名为mock/stub/dummy/fake的判为联调桩(非真实后端),并将服务器标为"仓库外" - 客户端:检测前端框架(
react/vue/angular/svelte/next/nuxt/createRoot) - 本地管理界面:检测
http.server/HTTPServer(区别于后端服务器) - 外部端点:扫描配置文件(
*.json/yaml/ini/env/toml)里的http(s)://、ws(s)://、amqp/mqtt/tcpURL,标注"被引用但不在仓库内" - 入口点:
main.py/__main__.py、含if __name__=="__main__"且用asyncio/argparse的文件、*.service的ExecStart指向脚本 - 通信通道:扫描代码中
websocket/ws://→ WebSocket;requests/httpx/axios/fetch/upload→ HTTP;pika/rabbitmq/amqp/kafka/mqtt→ 消息队列 -
角色为逻辑角色(非文件夹),纯启发式概览,不保证 100% 准确;无法跨仓库确认真实后端位置时,只标"仓库外/被引用"
-
核心文件(按重要性排序 Top 10):基于 import 引用图做 PageRank-lite 排名,分数越高越核心;文件索引表也按重要性降序排列,核心文件优先呈现,便于快速定位主干。配合
--map-tokens可在预算内截断低优尾部。 - 调用关系(文字):基于符号定义 + 调用名解析,列出文件级调用边(
caller → callee,括注调用次数),支持影响面分析。覆盖 Python/JS/TS(正则启发式,动态/字符串调用可能漏)。 - API 路由:自动识别 Flask/FastAPI/Django/Express/NestJS(
@Get/@Post装饰器)/Next.js 等框架路由,覆盖 get/post/put/delete/patch/head/options/trace/all/api_route/websocket 方法,列出METHOD /path → file:line,服务器类项目极有用。 - 死代码提示:在符号索引中标注"疑似未用"符号(
⚠疑似未用)——无入边(无人调用/import)且非入口点的定义,纯启发式提示,避免误删需结合人工判断。 - 密钥脱敏:生成前扫描文件内容,命中 AWS Key / 私钥块 /
token=赋值 / 含密码连接串 / 高熵串 等处,在地图展示层替换为***REDACTED***(源文件不动),并在头部提示"检测到 N 处疑似密钥"。 - 增量更新:维护
.workbuddy/project-map.cache.json(文件 hash + 已分析数据);重跑时跳过未变文件复用缓存,仅重算变更部分并合并,头部标注"增量刷新:M 变更 / 复用 K"。
实测对比
以下为 2026-07-09 在 redis-py 项目(227 文件 / 7MB)上的真实测试结果。
查询任务:定位 ConnectionPool 类定义位置。
| 维度 | 用地图(方法A) | 直接 Grep 全项目(方法B) |
|---|---|---|
| 能否找到 | ✅ 秒级命中,直指文件路径 | ❌ 0 命中(ripgrep 默认跳过隐藏目录 .deps) |
| token 成本 | ~4.5K(1 次 Grep + 读 1 文件) | 0 命中 + 多轮排查 ≥ 10K |
| 误判风险 | 无 | 高:会误以为符号不存在 |
关键发现:build_map.py 不忽略隐藏目录(如 .deps),而 Grep 工具(ripgrep)默认跳过以 . 开头的目录。因此在依赖装在隐藏目录的项目中,地图能检索到 Grep 工具"静默漏掉"的内容。
压缩比:
| 项目规模 | 原始体积 | 地图体积 | 压缩比 |
|---|---|---|---|
| 小项目(8 文件/8KB) | 8 KB | 1.9 KB | ~4× |
| 中项目(9 文件/52KB) | 52 KB | 2.5 KB | ~20× |
| 大项目(227 文件/7.2MB) | 7.2 MB | 72.8 KB | ~99× |
大项目首次建立认知时,用地图比盲目逐文件读取省 80-90% 的 token。
> 建议:小于 20 文件的项目可跳过地图直接读取,此时地图的索引开销反而更大。hook_wrapper.sh 已默认带 --min-files 20 自动跳过,也可手动加 --min-files 自行控制门槛。
反模式与常见错误(Anti-patterns & FAQ)
把容易踩的坑集中在这里,使用前先扫一眼,避免白做功。
反模式
-
没看地图就全局搜 / 逐文件读(最重要的一条)
在尚未Read目标项目的project-map.md时,就对整项目做宽泛Glob/Grep或逐文件Read。这直接抵消了"省 token"的全部价值。正确顺序:读地图 → 在地图内 Grep 关键词 → 只读命中的少数文件。 -
小项目硬建地图
小于 ~20 文件的项目,建图本身的索引开销比直接读还大。用--min-files 20跳过,或交给hook_wrapper.sh(已默认--min-files 20)自动跳过。 -
项目变了不刷新地图
地图是某一时刻的快照。代码结构有大变动后,用过时地图定位会"找不到文件/路径已变"。重新运行 build(或靠 SessionStart hook)刷新。 -
--max-files设太小导致地图被截断
输出里若出现"已达--max-files上限,部分文件未索引"提示,说明有文件被漏掉。调高--max-files(如 5000/10000)重跑。 -
把符号索引当成 100% 准确
符号提取是正则启发式,对复杂 / 动态代码风格(装饰器链、宏、代码生成、字符串里拼的类名等)可能漏提或误提。符号名只作"线索",文件级定位才是可靠结论。
常见错误
- "运行后好像没反应" —— 多半是 Python 环境问题
build_map.py现在会在启动时自检 Python 版本并明确报错(不再静默失败)。请确认: python3 --version应为 3.8+;- 若走 hook,失败原因写在
<项目>/.workbuddy/project-map.log,先查日志; - 本机
python3若指向 Windows 应用商店占位符(运行后提示去商店安装),需安装真实 Python 并加入 PATH。 - 中文 / 含空格路径
脚本用pathlib处理,正常支持;Windows Git Bash 下 hook 会自动cygpath转换。
FAQ
- Q:地图会过时吗? 会。结构变动后重跑 build,或靠 SessionStart hook 每次新会话自动刷新(小项目会被
--min-files跳过)。增量缓存只复用未变文件,改 1 个文件下次只重算那 1 个。 - Q:装完为什么没看到地图? 两种正常情况:① 当前项目文件数 < 20,
--min-files 20自动跳过(小项目直接读更快);② 还没进入"含代码的项目会话",技能未触发。真实项目里会生成<根>/.workbuddy/project-map.md。 - Q:自动激活没触发怎么办? 触发依赖"含代码的项目会话"。若没触发:① 确认会话在项目根打开;② 显式说一句"生成项目地图"即可手动触发;③ 想要每次开机硬触发,按"硬保证"节补 SessionStart hook。
- Q:hook 配了但不生成地图? 检查
python3是否在 PATH 且 ≥3.8;看.workbuddy/project-map.log;小项目被--min-files跳过属正常。本机已验证hooks.SessionStart配好且每会话 firing。 - Q:支持哪些语言? Python / JS / TS / Vue / Go / Rust / Java / Kotlin / C / C++ / C# / Ruby / PHP / Swift / Dart / SQL 等(见脚本
SYMBOL_PATTERNS)。 - Q:只想看文件结构、不提符号? 加
--no-symbols,更快、更省时。 - Q:超大项目担心卡顿? 加
--timeout 300(或更小),超时即写出部分地图并在顶部标⚠️ 超时,不会卡死;也可先--no-symbols提速。详见下方"性能调优指南"。 - Q:输出里出现"部分结果/超时"是什么意思? 说明达到
--timeout上限,地图只覆盖到截止时间已扫描的文件。提高--timeout或加--no-symbols重跑即得完整地图。 - Q:符号/路由/调用图不准? 提取是正则启发式,对装饰器链、宏、动态/字符串调用会漏提或误提;文件级定位(路径)才是可靠结论。死代码
⚠疑似未用也是启发式,删除前请人工确认。 - Q:文件路径含中文/空格? 脚本用
pathlib处理,正常支持;Windows Git Bash 下 hook 会自动cygpath转换。极少数超长路径(Windows >260 字符)会被自动跳过并计数,不影响整体。 - Q:Python 报错"需要 Python 3.8+"或去应用商店?
python3可能指向 Windows 应用商店占位符。安装真实 Python 3.8+ 并加入 PATH,或在命令里用绝对路径(如C:\Python311\python.exe)。 - Q:运行后"没反应"? 多半是 Python 环境或项目过小。先看 stderr 提示(脚本会明确报错,不再静默失败);走 hook 看
.workbuddy/project-map.log;小项目被跳过属预期。 - Q:
.workbuddy/project-map.cache.json是什么?可以删吗? 增量缓存(文件 hash + 已分析数据),删了下次会全量重建,无害;若该文件损坏脚本会自动忽略并重建(会打印 WARNING)。v1.8 起缓存带version/mode字段,升级或切换提取方式时旧缓存自动失效重建。 - Q:monorepo / 多项目怎么用? 把子项目目录当作 root 传入即可,如
python3 scripts/build_map.py ./services/order。每个子项目独立生成自己的project-map.md。 - Q:密钥会被泄露吗? 不会。扫描命中 AWS Key/私钥/
token=/含密码连接串/高熵串等,只在地图展示层替换为***REDACTED***,源文件原样不动,且地图头部提示"检测到 N 处疑似密钥"。 - Q:地图会写去哪里、会不会动我的代码? 只写
<根>/.workbuddy/(地图 + 缓存 + 用量日志),绝不修改你的源码,也不联网。 - Q:想完全不看地图直接读? 可以。
--no-graph只建目录树+文件摘要(最快);或直接不装/不触发本技能。
安全声明(给评审 / 谨慎用户)
本技能完全本地、离线、无害:
- 不联网:
build_map.py纯标准库实现,无任何requests/http/socket 调用,不访问外部服务、不上传任何数据。 - 无需 API key:零密钥依赖,离线可用。
- 只读源码:只读取你传入项目根下的源代码与配置,不写、不改源文件。
- 只写本地
.workbuddy目录:输出project-map.md/project-map.cache.json/ 用量日志均落在<根>/.workbuddy/,可随时删除。 - 密钥自动脱敏:地图展示层对疑似密钥打码,源文件不动(见 FAQ)。
- hook 脚本来自本地固定路径:
SessionStarthook 仅运行随包附带的scripts/hook_wrapper.sh(路径写死在配置里),不执行任何远程/动态下载的代码。 - 失败不静默:所有异常在 stderr 明确报错并给出排查建议;单文件/单目录解析失败会跳过并计 WARNING,不中断整体。
> 若仍要最高限度隔离,可在受限环境或容器中运行,或仅手动调用、不配置 hook。
性能调优指南
不同规模项目的推荐参数(实测参考,详见上方"实测对比"):
| 项目规模 | 文件数 | 推荐命令 | 预期耗时 | 说明 |
|---|---|---|---|---|
| 小 | < 20 | (默认跳过) | — | 直接读更快,--min-files 20 自动跳过 |
| 中 | 20–200 | build_map.py . |
1–3 秒 | 增量默认开,二次秒级 |
| 大 | 200–2000 | build_map.py . --timeout 600 |
数秒–数十秒 | 默认即可;卡顿就加 --no-symbols |
| 超大 | 2000+ | build_map.py . --timeout 300 --no-symbols |
取决于上限 | 先出结构+依赖,再按需对子目录建全量图 |
加速要点:
- 增量更新:第二次起只重算变更文件(stderr 打印
增量: M 变更 / K 复用),大项目重生成很快。 --no-symbols:跳过正则符号提取(最耗时的步骤之一),结构/依赖/角色分析照常,提速明显。--timeout N:硬性超时护栏,超时即写出部分地图并标注,绝不无限卡死。--max-files/--max-depth:超大项目先缩小范围(--max-depth 2看主干),再深入。--map-tokens:给文件索引设 token 预算,超出后按重要性折叠低优尾部,地图更短、更省上下文。- 内存:文本仅读前
MAX_TEXT_READ=200_000字符/文件,且缓存不含文本,不会因大项目爆内存。
版本路线(v1.8 -> 后续)
- v1.8(当前):零依赖增量改进,全员受益--路由识别扩充、装饰器调用图修复、缓存版本化。不引入任何可选依赖,默认行为与 v1.7 一致。
- MCP 暴露 / Tree-sitter 精确解析:暂缓。经评估:(1) MCP 的增量价值主要面向"外部 agent / 其他 IDE",而当前 IDE 内 agent 已能直接读
project-map.md/ 跑--search,需先确认有"当前做不到、只有 MCP 能做"的真实场景;(2) Tree-sitter 是可选依赖,默认零依赖模式下精度问题不变、全员受益有限,且其增量缓存须与引入同批落地(否则大项目装了反而更慢);(3) 若推进 MCP,前置必做:把_run解耦成纯函数build()(只计算不 IO)+ 所有 MCP 工具补真实用量埋点 + 缓存按提取模式分桶。故 MCP/Tree-sitter 待需求验证后再立项,版本号届时按语义化判定(非破坏性应为 1.9,破坏性才升 2.0)。
依赖
- Python 3.8+(仅标准库,无需 pip install)
- 跨平台:Windows / macOS / Linux 均可运行
- 不再静默失败:异常会明确报错并给出排查建议;hook 模式把成功/失败写入
.workbuddy/project-map.log
资源
scripts/build_map.py
项目地图生成器(纯标准库 Python 3.8+)。直接 python3 scripts/build_map.py <项目根> 执行即可。新增参数:--timeout(防卡顿)、--min-files(小项目自动跳过);启动会自检 Python 版本并明确报错,异常不再静默失败。
scripts/hook_wrapper.sh
SessionStart hook 入口脚本(bash)。自动处理不同平台的路径格式差异,兼容 Windows Git Bash / Linux / macOS;默认 --timeout 300 --min-files 20,并把运行日志写入 .workbuddy/project-map.log。
tests/test_build_map.py
回归测试套件(纯标准库)。动态生成 24 类 fixture 项目并对生成的 project-map.md 断言,当前 66/66 全过,覆盖:角色推断(设备端/真实后端/联调桩/客户端/本地管理界面/外部端点/入口点/CLI 独立程序,断言为文字段落而非图)、多语言后端(Flask/FastAPI/Go gin/Express)、依赖分析(@/* 别名解析、绝对导入省略顶层目录的后缀兜底、二级包粒度、文字列表格式)、边界(纯文档无角色说明、空项目不崩溃),以及 v1.6 新能力:JSON 导出结构校验、密钥脱敏(警告+摘要脱敏)、重要性排序(util 比 leaf 更重要)、调用图(main→util 边)、死代码(⚠疑似未用 标注)、框架路由(GET /api/health)、增量更新(二次复用/变更仅 1 文件重算);v1.8 新增:FastAPI 装饰器链 + 扩充方法路由(GET /items/DELETE /items/{id}/WS /ws)、NestJS @Get/@Post 装饰器路由、装饰器不污染调用图(get 不被误计为调用)、缓存版本失效重建(旧 v1 缓存触发警告并重建为 v2)。改脚本后运行 python tests/test_build_map.py,退出码 0=全过。