Skip to content

[Enhancement] sources:用 @name/ 前缀挂载远程团队知识库与 submodule 文档,不新增子命令 #63

Description

@Disdjj

背景

种子用户反馈希望 llmdoc 支持团队级别的知识结构:跨仓库共用的领域知识、服务间契约、团队规约,尤其在微服务多仓库场景下价值明显。用户目前的做法是 fork V2 自行实现,并依赖 V2 的 must/ + hooks 让规约始终在上下文里。

V3 已经具备承接这件事的原语:startup.preloadmust/ 的替代物,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 缺省 referencecode.pathsrelations 可选。无 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 是文件,但 findNearestGitRootOrNullexistsSync 判定,所以在 submodule 目录内运行 CLI 已经把它当作独立 root,update / commit 直接可跑。

更新共享库文档的流程写进 skill reference 即可:进入 submodule 目录 → 建分支 → 普通 update + commit → 推送开 PR → 回到外层 bump gitlink。

顺手需要修两处:

  1. commit 当前不检查 detached HEAD;submodule 检出后默认就是 detached,直接提交会产生游离 commit,外层一次 git submodule update 就丢。应拒绝并提示先建分支(主仓库同样受益)。
  2. 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 加同步与展示。

建议分三步交付:

  1. 只读挂载:配置 v2、loader、五个读取命令的 @ 寻址、preload 继承、tree 提示、clone 与定时 pull。这一步即可覆盖核心诉求。
  2. 新鲜度耦合:meta 里的 source revision、delta 的 needs-review 传播、status 的上游落后数。
  3. submodule 顺手项:detached HEAD 拒绝、gitlink 排除、skill 里的共享库更新流程。

待拍板

  1. 缓存目录用 .llmdoc-tmp/sources/ 还是独立可见的 .mdoc/。推荐前者(零新增排除规则);若选后者,代价只是多一个排除前缀。
  2. 首次 clone 是否允许在 search / show 这类读取命令里同步发生。推荐允许,仅限首次。
  3. context 输出里 source 文档与本地文档混排还是单独一段。推荐混排,靠 @ 前缀区分。
  4. commit 遇到 detached HEAD 是拒绝还是只警告。推荐拒绝。
  5. refresh 缺省值与网络超时值。

已核对的现有实现约束

  • cli/schemas/config.schema.json:封闭对象,schemaconst "llmdoc.config/v1"
  • cli/src/lib/fs.ts findProjectRootOrNull:只接受最近 Git 根直属的 llmdoc/.gitexistsSync 判定,submodule 的 .git 文件同样命中。
  • cli/src/lib/state.ts isImplementationSurfacePathllmdoc.config.jsonllmdoc/.llmdoc-tmp/ 已排除。
  • cli/src/lib/workspace.tsllmdoc/ 内只允许 .mdx 与根级 meta.json,topic 不允许嵌套,code.paths 精确路径不存在或 glob 空匹配为阻塞错误——因此 source 不能挂在 llmdoc/ 内部,且 source 文档的路径校验必须走独立分支。
  • cli/src/commands/commit.ts:不检查 detached HEAD。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions