本文件是 Project Compass(项目罗盘)6 个模型可见工具与 /compass 命令的完整参考。
工具的真实契约是
lib/tools.js里注册的 JSON Schema 与实现。本文与实现不一致时, 按代码为准,并把它当成文档缺陷上报。
- 调用约定
- project_compass_scan
- project_compass_analyze
- project_compass_report
- project_compass_ask
- project_compass_update
- project_compass_status
- 典型调用序列
- AI Agent 如何用 project_compass_ask 获取项目上下文
- 错误与降级行为
/compass命令- 命令行自测通道
| 约定 | 说明 |
|---|---|
projectPath |
目标项目根目录;所有工具都接受,省略时使用当前会话工作目录。指向不存在或不是目录的路径属于入参非法,直接报错 |
| 路径格式 | 工具返回与产物中出现的路径一律是项目根相对 posix 路径(src/api/orders.ts);行号一律 1-based |
| 参数的两种来源 | ① profile 里的 config(默认值的来源);② 单次调用显式传入(覆盖默认)。单次参数会叠加在配置之上(ignore / include 是追加,不是替换) |
| 只读性 | 工具只读项目源码。写入只发生在该项目的 .project-compass/(机器状态)与报告输出目录(默认 docs/project-compass/) |
| 幂等 | 同一输入重复调用结果稳定;报告是确定性渲染,可安全 diff(开启 withLlm 的叙述文字除外) |
| 网络 | 任何工具都不会主动发起网络请求。llm.enabled 与 withLlm 同时为真时,叙事/润色走宿主 LLM 服务 |
| 预算 | analyze / report / update / ask 受配置的 budget.* 约束,触顶即截断并在 warnings 与 summary 中说明 |
多数工具只返回摘要视图(模块概览、计数、枢纽、流程清单等),完整数据落盘在 ir.json。
这样做的目的是让工具返回值保持小巧——需要细节时读产物,而不是把整个 IR 塞进上下文。
用途:侦察项目并生成结构化「项目画像」(第一步)。目录扫描、忽略规则、语言分布、技术栈与 包管理器、可执行命令、入口点、配置 / CI / 容器 / IaC、测试框架,以及已能确定的工程缺口。
结论全部来自磁盘证据,不含猜测;敏感文件只登记路径与类型,绝不读取内容。
参数
| 名称 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
projectPath |
string | 否 | 当前工作目录 | 目标项目根目录 |
force |
boolean | 否 | true |
为 true 时忽略既有画像重新扫描 |
ignore |
string[] | 否 | [] |
额外忽略规则(.gitignore 语法,支持 ! 取反),叠加在配置与项目 .gitignore 之上 |
include |
string[] | 否 | [] |
强制包含的路径(命中则无视忽略规则;敏感文件仍不读内容) |
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
projectPath |
string | 解析后的项目绝对路径 |
projectName |
string | 项目名 |
scanFile |
string | 画像文件路径 |
kinds |
string[] | 识别到的项目类型 |
ecosystems |
string[] | 技术栈(kind(manifest) 形式) |
commands |
string[] | 可执行命令(preset: 命令行 形式) |
entrypoints |
string[] | 入口点(path (kind) 形式) |
languages |
string[] | 语言分布(name: N 文件 / M 行,最多 8 项) |
tests |
object | { frameworks, testFiles, coverageConfig } |
gaps |
string[] | 已确认的工程缺口(优先级 + 标题) |
sensitive |
string[] | 敏感文件路径与类型(未读取内容) |
stats |
object | { files, sourceFiles, dirs, bytes, skipped, truncated, todoCount, debugStatements, secretSuspects, analyzableSources, durationMs } |
summary |
string | 一句话结论与建议的下一步 |
产物:<项目>/.project-compass/scan.json(完整 Profile,字段远多于上面的摘要)。
备注
- 敏感登记只出现在
sensitive与stats.secretSuspects(计数)里,没有内容; commands只报告「发现」了什么命令,不执行任何命令;- 单次
scan不做深度解析,符号 / 导入 / 调用 / 路由属于analyze。
用途:解析代码并构建统一中间表示(IR)与依赖知识图谱(第二步):多语言符号 / 导入 / 调用 / 路由抽取 → 模块·文件·符号三级依赖图 → 关键流程识别。
基于内容哈希做增量:未变更的文件直接复用上次解析结果,只有内容、语言或解析器版本变化才重新解析。 跨文件绑定只走显式 import 声明,无法确定的一律标记为未解析——宁可断边也不猜。
参数
| 名称 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
projectPath |
string | 否 | 当前工作目录 | 目标项目根目录 |
force |
boolean | 否 | false |
忽略解析缓存,强制重新解析全部文件 |
maxFiles |
number | 否 | 配置的 budget.maxFiles |
本次纳入分析的文件数上限 |
concurrency |
number | 否 | 配置的 concurrency(默认 8) |
解析并发度(1–64) |
ignore |
string[] | 否 | [] |
额外忽略规则 |
include |
string[] | 否 | [] |
强制包含的路径 |
本工具不接受
withLlm:分析阶段不调用 LLM,叙事增强属于report。
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
projectPath |
string | 项目绝对路径 |
irFile |
string | IR 文件路径 |
modules |
array | 模块概览:{ id, kind, language, files, loc, symbols, dependsOn, risk }(最多 30 个) |
counts |
object | { modules, files, sourceFiles, symbols, imports, calls, routes, flows, loc, unresolvedCalls, externalImports } |
graph |
object | { stats, hubs, cycles, riskModules }(hubs / riskModules 各最多 10 项,cycles 最多 5 组) |
flows |
array | [{ id, name, kind, confidence, entry, steps }](最多 10 条) |
cache |
object | 解析缓存命中统计与 parserVersion |
changes |
object | null | { added, changed, removed, unchanged };首次分析为 null |
warnings |
string[] | 降级与截断说明(最多 20 条) |
durationMs |
number | 本次分析耗时 |
summary |
string | 一句话结论与建议的下一步 |
产物:<项目>/.project-compass/ir.json(完整 IR)+ .project-compass/cache/(解析缓存)。
备注
- 已有画像时直接复用(不重复扫描);没有画像时先扫描一次并落盘;
warnings里可能出现「未解析调用占比过高」这类提示,此时应下调对图的信任度, 并用ask补充验证。
用途:生成面向开发者的入门报告(第三步),输出 6 份产物:
ONBOARDING.md、ARCHITECTURE.md、MODULE_MAP.md、KEY_FLOWS.md、GETTING_STARTED.md、
project-compass.json。
报告包含 Mermaid 架构图与依赖图、关键调用链时序图、按角色(后端 / 前端 / 测试 / 运维 / 数据)
的阅读路线,每条结论都附 path:line 证据。默认不调用 LLM——全部结论来自静态证据。
参数
| 名称 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
projectPath |
string | 否 | 当前工作目录 | 目标项目根目录 |
outputDir |
string | 否 | 配置的 outputDir(默认 docs/project-compass) |
报告输出目录(相对项目根) |
withLlm |
boolean | 否 | false |
是否调用 LLM 生成叙事(需要用户明确同意消耗额度) |
includeMermaid |
boolean | 否 | true |
是否内嵌 Mermaid 图;false 时图的位置替换为说明文字 |
role |
string | 否 | 不限 | 只渲染某个角色的阅读路线:backend / frontend / test / devops / data |
maxNodes |
number | 否 | 40 |
Mermaid 单图节点上限(5–200) |
withIndex |
boolean | 否 | true |
是否同时构建检索索引,让后续 ask 立即可用 |
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
projectPath |
string | 项目绝对路径 |
reportDir |
string | 报告目录 |
files |
object | 6 份产物的键与路径 |
counts |
object | 报告统计(模块 / 文件 / 符号 / 路由 / 流程 / 风险) |
llm |
object | { used, provider, model, reason, droppedClaims } |
roleRoutes |
number | 生成的角色路线条数 |
index |
object | null | 检索索引统计(withIndex: false 时为 null) |
warnings |
string[] | 渲染告警 |
summary |
string | 一句话结论与建议的下一步 |
产物:<outputDir>/ 下 6 个文件(默认 docs/project-compass/)。结构与字段见
OUTPUT-FORMAT.md。
LLM 与验证器:withLlm: true 时使用会话默认模型(或在配置里指定 provider/model)润色叙事,
并且每一条 LLM 声明都必须带引用并通过验证器(路径存在于 IR、行号落在 1..loc 内、
符号能在 IR 中找到)。验证不通过的声明直接丢弃,条数计入 llm.droppedClaims 与报告元信息。
如果配置里 llm.enabled 为 false 而调用方传了 withLlm: true,会记录一条 warning 并尝试使用
会话默认模型。
前置条件:需要已有 IR;没有会明确报错并提示先调用 analyze。
用途:就项目提问并获得带引用的答案。本地 BM25 + 向量混合检索 → RRF 融合 → 重排,
命中源码分块后给出结论与 path:line 证据。默认抽取式回答,不联网、不调用 LLM。
参数
| 名称 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
projectPath |
string | 否 | 当前工作目录 | 目标项目根目录 |
question |
string | 是 | — | 问题;建议包含具体功能名、符号名或文件名以提高召回质量 |
limit |
number | 否 | 8 |
返回的证据条数上限(1–50) |
withLlm |
boolean | 否 | false |
是否用 LLM 润色答案(需要 llm.enabled) |
rebuildIndex |
boolean | 否 | false |
强制重建索引后重新回答(索引缺失时会自动构建) |
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
projectPath |
string | 项目绝对路径 |
question |
string | 原问题 |
answer |
string | 带引用的答案(截断到 6000 字符) |
confidence |
string | high / medium / low |
mode |
string | extractive(默认)/ hybrid / llm |
citations |
array | [{ text, path, line, symbol, why, score }](最多 20 条;why 截断到 200 字符) |
relatedSymbols |
array | 相关符号(最多 10 条) |
relatedFlows |
array | 相关关键流程(最多 5 条) |
notes |
string[] | 说明与降级原因(最多 10 条) |
indexRebuilt |
boolean | 本次是否顺带重建了索引 |
elapsedMs |
number | 检索与回答耗时 |
产物:不留报告文件;问答留痕追加到 <项目>/.project-compass/qa-log.jsonl(留痕失败不影响回答)。
前置条件:需要已有 IR;没有会明确报错并提示先调用 analyze。
隐私:默认模式下提问内容与命中片段只在本机处理;withLlm: true 才会把检索到的片段作为
唯一事实来源交给模型润色。
用途:增量更新分析结果与报告(第四步):重新扫描 → 只重新解析变更文件 → 刷新 IR / 图谱 / 流程 → 增量更新检索索引 → 重新生成报告。
参数
| 名称 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
projectPath |
string | 否 | 当前工作目录 | 目标项目根目录 |
outputDir |
string | 否 | 配置的 outputDir |
报告输出目录(相对项目根) |
withLlm |
boolean | 否 | false |
是否调用 LLM 生成叙事 |
withReport |
boolean | 否 | true |
是否重新生成报告;只想刷新 IR 时设 false |
withIndex |
boolean | 否 | true |
是否增量更新检索索引 |
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
projectPath |
string | 项目绝对路径 |
changes |
object | { added, changed, removed, unchanged } 文件数 |
changedSample |
string[] | 变更文件示例(最多 20 个) |
irFile |
string | IR 文件路径 |
reports |
object | null | 报告路径(withReport: false 时为 null) |
index |
object | null | 索引统计(withIndex: false 时为 null) |
durationMs |
number | 耗时 |
summary |
string | 一句话结论 |
产物:刷新 .project-compass/scan.json、ir.json、index.json,并按需重写报告。
边界:如果从未分析过(没有 IR),update 不会报错,而是做一次全量分析并在 summary
里明确写出「此前没有分析基线,本轮做了全量分析」——全量与增量的成本差异必须让调用方知情。
用途:查看当前项目的罗盘状态。不修改任何文件。 适合回答「这个项目分析过没有」 「报告在哪」「结果是不是过时了」。
参数
| 名称 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
projectPath |
string | 否 | 当前工作目录 | 目标项目根目录 |
detail |
string | 否 | compact |
compact 或 full;full 时 ir.topModules 给出 20 个模块(默认 5 个) |
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
projectPath |
string | 项目绝对路径 |
analyzed |
boolean | 是否已有可用 IR |
hasScan |
boolean | 是否有扫描画像 |
hasIndex |
boolean | 是否有检索索引 |
files |
object | { scan, ir, state, index } 状态文件路径 |
artifacts |
object | 每份报告的 { path, exists, bytes } |
ir |
object | null | { generatedAt, stats, truncated, budget, moduleCount, topModules } |
cache |
object | { files, bytes } 解析缓存规模 |
index |
object | null | 索引统计(chunks / files / dim / builtAt) |
state |
object | 运行状态:上次分析时间、预算、产物、问答计数 |
answerCount |
number | 累计问答次数 |
nextSteps |
string[] | 建议的下一步(按状态动态生成) |
summary |
string | 一句话结论 |
典型返回(示意)
{
"analyzed": true,
"hasScan": true,
"hasIndex": true,
"artifacts": {
"onboarding": { "path": "/repo/app/docs/project-compass/ONBOARDING.md", "exists": true, "bytes": 18240 }
},
"ir": { "stats": { "files": 213, "symbols": 1480, "routes": 26 }, "truncated": false },
"cache": { "files": 131, "bytes": 4980736 },
"index": { "chunks": 2140, "files": 198 },
"nextSteps": ["调用 project_compass_ask 就具体功能提问(带引用回答)"],
"summary": "已分析:213 文件 / 1480 符号 / 26 路由;报告产物 6/6 份;缓存 131 个条目(4864 KB);索引 2140 块"
}project_compass_status { projectPath } # 先确认状态,避免重复大分析
project_compass_scan { projectPath } # 快速看清这是什么项目(可选,analyze 会复用画像)
project_compass_analyze { projectPath } # 解析 + IR + 图谱(首次可能较慢)
project_compass_report { projectPath } # 生成 6 份产物 + 检索索引
project_compass_ask { projectPath, question: "认证流程经过哪些模块?" }
project_compass_update { projectPath } # 只重算变更文件,刷新 IR/索引/报告
project_compass_status { projectPath } # 有 IR 就直接问
project_compass_ask { projectPath, question: "订单金额在哪里计算?" }
project_compass_status { projectPath } # 看 artifacts 与 nextSteps
project_compass_analyze { projectPath } # 增量
project_compass_analyze { projectPath, force: true } # 强制全量重算
project_compass_ask { projectPath, question: "…", rebuildIndex: true }
这一节写给「读这段文档的模型」:在动手改代码之前,用 ask 换取有出处的上下文,
而不是靠猜或靠零散 grep。
推荐流程
-
先看状态,别急着重算
{ "projectPath": "/repo/app" }用
project_compass_status确认是否已有 IR 与索引。analyzed: false时先analyze; 已有 IR 但代码刚改过,就用update。 -
用具体问题换带引用的答案
{ "projectPath": "/repo/app", "question": "登录流程经过哪些模块?" }返回里的
citations[]是硬通货:path+line指向真实代码位置,why说明它支撑了哪条结论。 只使用citations里出现过的路径与行号去引用代码。 -
校验后再引用
如果答案里出现了一个文件名,但
citations里没有它,就不要把它写进回答或改动计划。 这是本工具的核心承诺:验证器只放行能在 IR 中核对到的路径、行号与符号。 -
读置信度决定后续动作
confidence建议动作 high可以直接据此规划;引用时带上 path:linemedium顺着 relatedSymbols/relatedFlows再读两三个文件确认边界low当成线索而不是结论:换更具体的问法(带上模块名、函数名、报错信息),或用 rebuildIndex: true重建索引后重试 -
顺着结果继续追问,而不是重新开一轮
{ "projectPath": "/repo/app", "question": "createSession 被哪些地方调用?改动它的返回值会影响谁?" }relatedFlows给出这条链路在KEY_FLOWS.md里的对应条目,可以对照报告一起看。 -
要长期上下文就生成报告,而不是反复问
如果你需要的是「整个项目的地图」,一次
project_compass_report产出的 6 份产物比十次问答更省上下文; 之后把 Markdown 当资料读,只在具体问题上用ask。 -
一次问一个问题,并把答案里的引用当唯一事实来源
多个不相关问题混在一句里会让检索分散命中、置信度下降。拆分提问,并用
limit控制证据条数 (默认 8;要更宽的召回可以到 20–30,但要注意上下文成本)。
提问技巧
| 目标 | 好的问法 | 为什么 |
|---|---|---|
| 找入口 | 「HTTP 请求进来后第一个业务函数是谁?」 | 指向入口点与路由,能命中路由 / 入口符号 |
| 找链路 | 「A 调用 B 之后数据去了哪里?」 | 指向调用关系,命中 calls 与 flows |
| 找影响面 | 「改 X 的签名会影响哪些模块?」 | 指向扇入与跨模块依赖 |
| 找约定 | 「错误是怎么统一处理的?有没有中间件?」 | 指向跨文件的同类符号 |
| 找数据 | 「这个表/模型在哪些地方被读写?」 | 指向导入与调用点 |
| 找配置 | 「这个服务从哪读配置?」 | 指向配置读取与清单文件 |
| 避免的问法 | 原因 |
|---|---|
| 「这个项目好不好?」 | 没有可检索的落点,只能给低置信度回答 |
| 「帮我重构 X」 | ask 是检索与引用,不做改写;请拿到上下文后自行规划并执行 |
| 一次塞五个不相关问题 | 检索会分散命中,置信度下降;拆成多次更准 |
只有入参非法或前置产物缺失才抛错,其余一切情况都尽量返回可用结果:
| 情况 | 行为 |
|---|---|
projectPath 指向不存在的路径 |
报错:项目路径不存在:<path> |
projectPath 指向文件而非目录 |
报错(路径校验失败) |
question 缺失或为空字符串 |
报错(必填参数校验) |
参数类型错误(例如 maxFiles: "many") |
报错,由工具入口的参数校验负责 |
report / ask 在未分析的项目上调用 |
报错:项目尚未分析:… 请先调用 project_compass_analyze |
concurrency / maxNodes / limit 超出范围 |
报错(1–64 / 5–200 / 1–50) |
参数校验集中在 lib/tools.js——这是全插件唯一因为入参问题抛错的地方。
| 情况 | 降级方式 |
|---|---|
| 单个文件不可读 / 权限不足 | 跳过该文件并记入画像与 warnings,分析继续 |
| 文件是二进制 | 不解析,仅登记元信息 |
文件超过 budget.maxFileBytes |
跳过解析,标记 skipped,计入统计 |
触顶 budget.maxFiles / maxTotalBytes / maxDurationMs / maxChunks |
停止扩大范围,truncated: true,warnings 说明触顶项 |
| 符号链接 | 默认不跟随(避免目录环与越界);跳过并记 warning |
| 某语言解析器抛错 | 捕获后该文件降级为轻量或跳过,文件级 warnings 记录原因 |
| 语言无法判定 | 按 text 处理,只做轻量抽取 |
| 缓存条目损坏 / JSON 解析失败 | 视为未命中,重新解析该文件;不中断分析 |
| 解析器版本变化 | 缓存整体失效并重算(缓存条目带 parserVersion) |
| import specifier 无法解析到文件 | 记为外部依赖或未解析,不猜测目标 |
| 调用无法绑定 | resolution: 'unresolved',toSymbolId: null;counts.unresolvedCalls 可见 |
| 图中存在循环 | 正常记录,不报错、不中断 |
报告节点数超过 maxNodes |
裁剪 Mermaid 图并在报告中标注 |
| 报告渲染某段失败 | 该段降级为「数据不足」段落,整份报告仍然产出(渲染层不抛错) |
索引缺失时 ask |
自动构建索引;构建失败则有限检索并在 notes 说明 |
| 问答留痕写盘失败 | 忽略(try/catch),回答照常返回 |
llm.enabled: false 时调用 withLlm |
ask / update 走确定性路径;report 记 warning 并尝试会话默认模型 |
| 宿主没有 LLM 服务 | available() === false,降级为确定性输出,不报错 |
| LLM 输出含无法核对的声明 | 该声明被丢弃,计入 llm.droppedClaims 与报告元信息 |
.project-compass/ 不存在 |
status 正常返回「尚未分析」;update 做全量并在 summary 说明 |
设计原则:分析失败不应该让工具失败。缓存损坏、单个文件畸形、缺 LLM 服务,都不应阻断 「拿到一份可用的项目画像」这件事。
面向人的命令入口,一个命令 + 子命令:
| 子命令 | 对应工具 | 说明 |
|---|---|---|
/compass analyze |
project_compass_analyze |
全量或增量分析 |
/compass report |
project_compass_report |
生成 6 份产物 |
/compass ask <问题> |
project_compass_ask |
带引用问答 |
/compass update |
project_compass_update |
增量更新 |
/compass status |
project_compass_status |
查看状态与下一步建议 |
/compass help |
— | 用法速查 |
/compass status
/compass analyze
/compass report
/compass ask 登录流程经过哪些模块?
不需要 DSH 宿主即可自测同样的链路:
node --test
node scripts/compass-cli.mjs analyze <项目路径>
node scripts/compass-cli.mjs report <项目路径>
node scripts/compass-cli.mjs ask <项目路径> "登录流程经过哪些模块?"
node scripts/compass-cli.mjs status <项目路径>CLI 与工具走同一条实现路径,因此可以放心用它做回归;区别是它不经过宿主,
因此 llm.* 相关能力不可用(按「宿主无 LLM 服务」降级)。