背景
种子用户反馈希望 llmdoc 支持团队级别的知识结构:跨仓库共用的领域知识、服务间契约、团队规约,尤其在微服务多仓库场景下价值明显。用户目前的做法是 fork V2 自行实现,并依赖 V2 的 must/ + hooks 让规约始终在上下文里。
V3 已经具备承接这件事的原语:startup.preload 是 must/ 的替代物,llmdoc.config.json 是知识控制面,relations / code.paths 是路由层。缺的只是“把仓库之外的文档挂进来”这一层。
目标与非目标
目标:
- 用一个概念
sources 覆盖两类来源:远程团队知识库(git 地址,仓库内可有多个 folder)和仓库内路径(典型是 git submodule)。
- 既有读取命令(
tree / index / search / show / context)和 startup.preload 通过 @name/ 前缀寻址 source 文档,不新增任何子命令。
- 远程文档采用松散契约:任意
.md / .mdx,只要 YAML front matter 带 description 即可,不要求遵循完整 V3 结构。
- 本地缓存目录直接 clone 远程地址,按固定间隔 fast-forward pull。
非目标(本 issue 不覆盖):
- 门禁 / CI 阻断 / PreToolUse 拦截。
- 团队级“行为规范”的执行。这部分仍建议以 team Skill 或 agent plugin 承载;llmdoc 只负责让规约正文可检索、可 preload。
- 对 source 文档的写入。source 文档只在它自己的仓库里维护。
设计
配置
llmdoc.config.json 新增 sources 字段。一个 source 要么是 git,要么是 path,二者互斥:
{
"schema": "llmdoc.config/v2",
"sources": {
"team": { "git": "git@github.com:acme/team-knowledge.git", "ref": "main", "dir": "conventions", "refresh": "24h" },
"shared": { "path": "libs/shared-proto" }
},
"startup": {
"preload": ["architecture.mdx", "@team/service-baseline.md"]
}
}
dir:远程仓库内的 folder,省略则挂整个仓库。同一 git 地址挂多个 folder 时只 clone 一份,name 只是视图。
ref:分支、tag 或 commit;给 commit 即锁版本。
refresh:pull 间隔,缺省值待定(建议 24h)。
- 配置 schema 当前是封闭对象(
additionalProperties: false),新增字段必须升到 llmdoc.config/v2。旧版 CLI 读到 v2 应明确报“版本过低”,而不是像现在一样整份判无效再静默退回默认 reminder。
文档根与松散契约
- 挂载目录下存在
llmdoc/ 就以它为文档根(标准 V3 仓库挂进来后 ID 里没有多余的 llmdoc/ 段);不存在则以挂载目录本身为根,目录深度不限。
- source 内任意
.md / .mdx,front matter 有 description 即为一篇文档。kind 缺省 reference;code.paths、relations 可选。无 front matter 的文件直接忽略,不报错。
- source 自带的
meta.json 消费方不解析。
- source 文档的
code.paths 只做语法检查,不校验文件存在——团队规范的 glob 在某个具体仓库里匹配不到文件是常态。
寻址与只读
- 文档 ID =
@name/ + 相对文档根的路径。show / search / index / context / tree / startup.preload 全部识别该前缀。
index --topic @team 列整个 source,--topic @team/proto 列其子目录,不加新 flag。
- source 文档只参与读取命令;
new / mv / adopt / fingerprint / commit / prune 遇到 @ 前缀一律拒绝。
- 本地文档可以
requires 一篇 @team/ 文档,反向不允许。
@ 成为保留字符,本地 topic 名不允许以它开头。
团队级配置只开一个口
source 目录里如果有 llmdoc.config.json,其 startup.preload 默认被消费方继承(路径相对 source 文档根)。消费方在 source 条目上写 "preload": false 可关闭继承。这就是“团队规约一定在上下文里”的团队侧开关,复用同一个 schema,不引入新文件格式。其他行为类配置不接。
路径匹配规则
context 用消费仓库的文件路径匹配 source 文档的 code.paths 时,同时尝试仓库相对路径与 source 相对路径,任一命中即算。团队规范写 **/Dockerfile 走前者,submodule 自己的文档写 proto/** 走后者,不需要两套语义。
同步、展示与新鲜度
缓存目录与同步
- 缓存目录建议
.llmdoc-tmp/sources/<name>/:该前缀已在 implementation surface 排除列表中,也已约定 gitignore,零新增代码。
- 同步规则:读取命令首次遇到缺失缓存时同步 clone 一次并提示;之后只有 SessionStart hook 和
status 会触碰网络,超过 refresh 间隔就跑一次 fast-forward pull(带超时),失败则继续用旧缓存并在信号里说明。search / show 永远不在网络上等待。
- 这保持了 hook 只读且 fail-open 的既有不变量:pull 写的是缓存,不是知识。
tree 直接列出 sources
llmdoc/ (8 docs, ~2863 tokens)
...
sources (2 configured)
@team git acme/team-knowledge#main:conventions 5 docs, synced 3h ago, at 1a2b3c4
@shared path libs/shared-proto 3 docs, submodule at 9f8e7d6
hint: `index --topic @team` 查看某个 source;`status` 查看同步状态与上游落后数
- 未配置任何 source 时,
tree 末尾只加一行提示可在配置里加 sources 挂载团队或共享知识。
- 缓存缺失或 pull 失败时对应行显示原因与 remedy。
status 追加一段 source 状态:分支型 ref 显示上游是否有新 commit,commit 型 ref 显示 pinned。
新鲜度耦合沿用现有模型
- 消费方
llmdoc/meta.json 增加 sources.<name>.validatedRevision,记录本地文档上次验证时该 source 的 commit。
delta 在 source 目录里 diff 记录值到当前 HEAD,变化的 source 文档沿 reverse requires 把本地文档标为 needs-review。
fingerprint / commit 顺带推进该记录。source 目录不是 git 仓库时 revision 为空,按现有规则保守抬到 deep。
- source 文档本身永远不出现在
impacts 里——消费方改不了它们。
submodule:不需要专门命令
submodule 就是一个 path 类 source。已核对:submodule 的 .git 是文件,但 findNearestGitRootOrNull 用 existsSync 判定,所以在 submodule 目录内运行 CLI 已经把它当作独立 root,update / commit 直接可跑。
更新共享库文档的流程写进 skill reference 即可:进入 submodule 目录 → 建分支 → 普通 update + commit → 推送开 PR → 回到外层 bump gitlink。
顺手需要修两处:
commit 当前不检查 detached HEAD;submodule 检出后默认就是 detached,直接提交会产生游离 commit,外层一次 git submodule update 就丢。应拒绝并提示先建分支(主仓库同样受益)。
- gitlink 路径不会命中
dir/** 映射(已用 minimatch 实测),bump 一次就多一条 unmapped 噪音。把已配置的 source 路径排除出 implementation surface 即可。
安全提醒(需写进文档)
preload 会把远程正文原样注入每个消费仓库的会话,团队库被篡改等于对全团队做 prompt 注入。v1 不做签名校验,但文档中应建议对外部团队库用 tag 或 commit 锁定,status 始终显示当前 commit。
实现落点
- source 文档放在
WorkspaceData.sourceDocuments,不混进 documents。维护类命令按构造就看不到它们,只有五个读取命令需要合并输出。
- 新增文件只有
cli/src/lib/sources.ts:解析配置、保证缓存存在、按松散契约扫描并产出带 @name/ 的 ParsedDocument。
- 其余改动:schema 升 v2、config 解析
@ 前缀、state.ts 排除 source 路径、hook 与 status 加同步与展示。
建议分三步交付:
- 只读挂载:配置 v2、loader、五个读取命令的
@ 寻址、preload 继承、tree 提示、clone 与定时 pull。这一步即可覆盖核心诉求。
- 新鲜度耦合:meta 里的 source revision、
delta 的 needs-review 传播、status 的上游落后数。
- submodule 顺手项:detached HEAD 拒绝、gitlink 排除、skill 里的共享库更新流程。
待拍板
- 缓存目录用
.llmdoc-tmp/sources/ 还是独立可见的 .mdoc/。推荐前者(零新增排除规则);若选后者,代价只是多一个排除前缀。
- 首次 clone 是否允许在
search / show 这类读取命令里同步发生。推荐允许,仅限首次。
context 输出里 source 文档与本地文档混排还是单独一段。推荐混排,靠 @ 前缀区分。
commit 遇到 detached HEAD 是拒绝还是只警告。推荐拒绝。
refresh 缺省值与网络超时值。
已核对的现有实现约束
cli/schemas/config.schema.json:封闭对象,schema 为 const "llmdoc.config/v1"。
cli/src/lib/fs.ts findProjectRootOrNull:只接受最近 Git 根直属的 llmdoc/;.git 用 existsSync 判定,submodule 的 .git 文件同样命中。
cli/src/lib/state.ts isImplementationSurfacePath:llmdoc.config.json、llmdoc/、.llmdoc-tmp/ 已排除。
cli/src/lib/workspace.ts:llmdoc/ 内只允许 .mdx 与根级 meta.json,topic 不允许嵌套,code.paths 精确路径不存在或 glob 空匹配为阻塞错误——因此 source 不能挂在 llmdoc/ 内部,且 source 文档的路径校验必须走独立分支。
cli/src/commands/commit.ts:不检查 detached HEAD。
背景
种子用户反馈希望 llmdoc 支持团队级别的知识结构:跨仓库共用的领域知识、服务间契约、团队规约,尤其在微服务多仓库场景下价值明显。用户目前的做法是 fork V2 自行实现,并依赖 V2 的
must/+ hooks 让规约始终在上下文里。V3 已经具备承接这件事的原语:
startup.preload是must/的替代物,llmdoc.config.json是知识控制面,relations/code.paths是路由层。缺的只是“把仓库之外的文档挂进来”这一层。目标与非目标
目标:
sources覆盖两类来源:远程团队知识库(git 地址,仓库内可有多个 folder)和仓库内路径(典型是 git submodule)。tree/index/search/show/context)和startup.preload通过@name/前缀寻址 source 文档,不新增任何子命令。.md/.mdx,只要 YAML front matter 带description即可,不要求遵循完整 V3 结构。非目标(本 issue 不覆盖):
设计
配置
llmdoc.config.json新增sources字段。一个 source 要么是git,要么是path,二者互斥:{ "schema": "llmdoc.config/v2", "sources": { "team": { "git": "git@github.com:acme/team-knowledge.git", "ref": "main", "dir": "conventions", "refresh": "24h" }, "shared": { "path": "libs/shared-proto" } }, "startup": { "preload": ["architecture.mdx", "@team/service-baseline.md"] } }dir:远程仓库内的 folder,省略则挂整个仓库。同一 git 地址挂多个 folder 时只 clone 一份,name 只是视图。ref:分支、tag 或 commit;给 commit 即锁版本。refresh:pull 间隔,缺省值待定(建议24h)。additionalProperties: false),新增字段必须升到llmdoc.config/v2。旧版 CLI 读到 v2 应明确报“版本过低”,而不是像现在一样整份判无效再静默退回默认 reminder。文档根与松散契约
llmdoc/就以它为文档根(标准 V3 仓库挂进来后 ID 里没有多余的llmdoc/段);不存在则以挂载目录本身为根,目录深度不限。.md/.mdx,front matter 有description即为一篇文档。kind缺省reference;code.paths、relations可选。无 front matter 的文件直接忽略,不报错。meta.json消费方不解析。code.paths只做语法检查,不校验文件存在——团队规范的 glob 在某个具体仓库里匹配不到文件是常态。寻址与只读
@name/+ 相对文档根的路径。show/search/index/context/tree/startup.preload全部识别该前缀。index --topic @team列整个 source,--topic @team/proto列其子目录,不加新 flag。new/mv/adopt/fingerprint/commit/prune遇到@前缀一律拒绝。requires一篇@team/文档,反向不允许。@成为保留字符,本地 topic 名不允许以它开头。团队级配置只开一个口
source 目录里如果有
llmdoc.config.json,其startup.preload默认被消费方继承(路径相对 source 文档根)。消费方在 source 条目上写"preload": false可关闭继承。这就是“团队规约一定在上下文里”的团队侧开关,复用同一个 schema,不引入新文件格式。其他行为类配置不接。路径匹配规则
context用消费仓库的文件路径匹配 source 文档的code.paths时,同时尝试仓库相对路径与 source 相对路径,任一命中即算。团队规范写**/Dockerfile走前者,submodule 自己的文档写proto/**走后者,不需要两套语义。同步、展示与新鲜度
缓存目录与同步
.llmdoc-tmp/sources/<name>/:该前缀已在 implementation surface 排除列表中,也已约定 gitignore,零新增代码。status会触碰网络,超过refresh间隔就跑一次 fast-forward pull(带超时),失败则继续用旧缓存并在信号里说明。search/show永远不在网络上等待。tree直接列出 sourcestree末尾只加一行提示可在配置里加sources挂载团队或共享知识。status追加一段 source 状态:分支型 ref 显示上游是否有新 commit,commit 型 ref 显示 pinned。新鲜度耦合沿用现有模型
llmdoc/meta.json增加sources.<name>.validatedRevision,记录本地文档上次验证时该 source 的 commit。delta在 source 目录里 diff 记录值到当前 HEAD,变化的 source 文档沿 reverserequires把本地文档标为 needs-review。fingerprint/commit顺带推进该记录。source 目录不是 git 仓库时 revision 为空,按现有规则保守抬到 deep。impacts里——消费方改不了它们。submodule:不需要专门命令
submodule 就是一个
path类 source。已核对:submodule 的.git是文件,但findNearestGitRootOrNull用existsSync判定,所以在 submodule 目录内运行 CLI 已经把它当作独立 root,update/commit直接可跑。更新共享库文档的流程写进 skill reference 即可:进入 submodule 目录 → 建分支 → 普通 update + commit → 推送开 PR → 回到外层 bump gitlink。
顺手需要修两处:
commit当前不检查 detached HEAD;submodule 检出后默认就是 detached,直接提交会产生游离 commit,外层一次git submodule update就丢。应拒绝并提示先建分支(主仓库同样受益)。dir/**映射(已用 minimatch 实测),bump 一次就多一条 unmapped 噪音。把已配置的 source 路径排除出 implementation surface 即可。安全提醒(需写进文档)
preload 会把远程正文原样注入每个消费仓库的会话,团队库被篡改等于对全团队做 prompt 注入。v1 不做签名校验,但文档中应建议对外部团队库用 tag 或 commit 锁定,
status始终显示当前 commit。实现落点
WorkspaceData.sourceDocuments,不混进documents。维护类命令按构造就看不到它们,只有五个读取命令需要合并输出。cli/src/lib/sources.ts:解析配置、保证缓存存在、按松散契约扫描并产出带@name/的ParsedDocument。@前缀、state.ts排除 source 路径、hook 与status加同步与展示。建议分三步交付:
@寻址、preload 继承、tree提示、clone 与定时 pull。这一步即可覆盖核心诉求。delta的 needs-review 传播、status的上游落后数。待拍板
.llmdoc-tmp/sources/还是独立可见的.mdoc/。推荐前者(零新增排除规则);若选后者,代价只是多一个排除前缀。search/show这类读取命令里同步发生。推荐允许,仅限首次。context输出里 source 文档与本地文档混排还是单独一段。推荐混排,靠@前缀区分。commit遇到 detached HEAD 是拒绝还是只警告。推荐拒绝。refresh缺省值与网络超时值。已核对的现有实现约束
cli/schemas/config.schema.json:封闭对象,schema为const "llmdoc.config/v1"。cli/src/lib/fs.tsfindProjectRootOrNull:只接受最近 Git 根直属的llmdoc/;.git用existsSync判定,submodule 的.git文件同样命中。cli/src/lib/state.tsisImplementationSurfacePath:llmdoc.config.json、llmdoc/、.llmdoc-tmp/已排除。cli/src/lib/workspace.ts:llmdoc/内只允许.mdx与根级meta.json,topic 不允许嵌套,code.paths精确路径不存在或 glob 空匹配为阻塞错误——因此 source 不能挂在llmdoc/内部,且 source 文档的路径校验必须走独立分支。cli/src/commands/commit.ts:不检查 detached HEAD。