mcpField
D
data-structure-protocol

data-structure-protocol

github.com/k-kolomeitsevAgent 工作流6853 次安装12 周前更新
代码仓库

关于 data-structure-protocol

用于将项目代码实体和依赖记录为 `.dsp/` 图谱的 skill 与 CLI 协议。它指导编码 Agent 建立索引、导航和分析影响,并随代码变更维护图谱。

主要功能

  1. 01以图谱表示实体和依赖
  2. 02记录用途、导入和导出
  3. 03导航并搜索项目实体
  4. 04分析依赖方及依赖路径
  5. 05随代码变更更新图谱记录
  6. 06诊断循环依赖和孤立实体

适用场景

  1. 01接手代码库时构建图谱
  2. 02修改代码前查找依赖
  3. 03评估模块或函数变更的影响
  4. 04使图谱元数据与代码编辑保持同步

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/k-kolomeitsev/data-structure-protocol --skill data-structure-protocol

安装后在 Claude Code 中输入/data-structure-protocol确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

dsp-tool-config.json
LICENSE
references/
bootstrap.md
operations.md
storage-format.md
scripts/
dsp-cli.py

详细介绍

数据结构协议(DSP)

DSP 会在 .dsp/ 目录中构建项目实体的依赖图。每个实体(模块、函数、外部依赖)都会获得一个 UID、描述、导入列表和导出索引。该图可以回答:有哪些内容、它们为何存在、哪些内容依赖哪些内容,以及哪些内容被谁使用。

DSP 不是面向人类的文档,也不是 AST 转储。 它记录的是 含义(目的)、边界(导入/导出)以及 连接的原因(为什么)。

Agent 提示词

在处理由 DSP 跟踪的项目时,嵌入以下上下文:

此项目使用 DSP(数据结构协议)。 .dsp/ 目录是此项目的实体图:模块、函数、依赖项和公共 API。它是代码结构的长期记忆。

核心规则:

  1. 更改代码前——通过 dsp-cli search、find-by-source 或 read-toc 找出受影响的实体。阅读它们的 description 和 imports,以了解上下文。
  2. 创建文件/模块时——调用 dsp-cli create-object。对于每个导出的函数,调用 create-function(并提供 --owner)。通过 create-shared 注册导出。
  3. 添加导入时——调用 dsp-cli add-import,并附上简短的 why。对于外部依赖,如果该实体尚不存在,请先调用 create-object --kind external。
  4. 移除导入/导出/文件时——分别调用 remove-import、remove-shared、remove-entity。级联清理会自动完成。
  5. 重命名/移动文件时——调用 move-entity。UID 不会改变。
  6. 如果仅更改内部实现,且不影响目的或依赖项,不要改动 DSP。
  7. TOC 成员关系——新实体会加入所有根作用域涵盖其路径的 TOC(也可以显式传入 --toc,该参数可重复使用)。使用 add-to-toc/move-to-toc 调整成员关系。
  8. 初始化——如果 .dsp/ 为空:发现各根目录(使用 --new-root --scope),将文件按批次分配到各 TOC,并按文件量均衡;然后并行对这些批次执行三轮操作——先为所有文件建立索引,再处理所有导出,最后处理所有导入。每个文件只读取一次(在第 1 轮);后续轮次复用该次读取结果。

主要命令:

dsp-cli init
dsp-cli create-object <source> <purpose> [--kind external] [--uid UID] [--toc TOC ...] [--new-root [--scope DIR]]
dsp-cli create-function <source> <purpose> [--owner UID] [--uid UID] [--toc TOC ...]
dsp-cli create-shared <exporter_uid> <shared_uid> [<shared_uid> ...]
dsp-cli add-import <importer_uid> <imported_uid> <why> [--exporter UID]
dsp-cli add-to-toc <uid> [<uid> ...] --toc TOC [--toc TOC ...]
dsp-cli move-to-toc <uid> [<uid> ...] --from TOC --to TOC
dsp-cli remove-import <importer_uid> <imported_uid> [--exporter UID]
dsp-cli remove-shared <exporter_uid> <shared_uid>
dsp-cli remove-entity <uid>
dsp-cli move-entity <uid> <new_source>
dsp-cli update-description <uid> [--source S] [--purpose P] [--kind K] [--scope DIR]
dsp-cli get-entity <uid>
dsp-cli get-children <uid> [--depth N]
dsp-cli get-parents <uid> [--depth N]
dsp-cli search <query>
dsp-cli find-by-source <path>
dsp-cli read-toc [--toc ROOT_UID]
dsp-cli get-stats

TOC 是根 UID,或字面值 default(即普通的 .dsp/TOC 文件)。

使用 CLI

脚本位于相对于此技能目录的 scripts/dsp-cli.py。

python <skill-path>/scripts/dsp-cli.py [--root <project-root>] <command> [args]

--root 默认为当前工作目录。参数中的所有路径都相对于仓库根目录。

核心概念

  • 代码 = 图。 节点是对象和函数。边是 imports 和 shared/exports。
  • 按 UID 标识,而非文件路径。 路径是一个属性;重命名或移动不会改变 UID。
  • “Shared” 会创建一个实体。 如果某项内容变为公开(已导出),它就会获得自己的 UID。
  • 导入会同时跟踪“来自哪里”和“导入什么”。 一次代码导入可能会创建两个 DSP 链接:一个指向模块,另一个指向具体的共享实体。
  • 全面覆盖导入内容。 每个导入的文件或资源都必须是 .dsp 中的一个对象——代码、图像、样式、配置等,全部都要纳入。
  • why 位于被导入实体旁边的 exports/ 目录中(反向索引)。
  • 从根开始。 每个根入口点都有自己的 TOC 文件。根可以声明一个 scope(目录子树);新实体会自动分配给根作用域覆盖其路径的每个 TOC。
  • 外部依赖——仅记录。 使用 kind: external,不深入探查 node_modules/site-packages/等目录。但 exports index 仍然有效——可显示哪些实体导入了它。
  • 持久化反向索引缓存。 .dsp/.cache/ 保存反向邻接关系(imported → importers),每个被导入实体对应一个文件。它让反向和遍历命令(get-recipients、get-parents、get-path,以及 get-entity 的“exported to”)在大型图中无需重新扫描即可快速运行。本地和正向命令(get-children、get-shared、read-toc、find-by-source、search)会读取实时文件,从不访问缓存。变更命令会增量更新缓存(仅更新受影响的条目),因此随着图的构建,缓存始终保持正确。
    • 自动构建。 如果缓存缺失,下一次反向或遍历命令,或者下一次影响反向索引的变更操作,都会自动构建缓存。正常使用时无需手动操作。
    • 缓存会与图一起提交(不会被 Git 忽略):普通的 git checkout/pull 会将缓存与 .dsp/ 一起更新。注意:涉及 .dsp/ 的 merge/rebase 可能会错误合并 .cache/ 文件或留下冲突——缓存不会自行验证(只跟踪一个哨兵文件,而非内容),因此之后运行 rebuild-cache 可确保安全。
    • rebuild-cache。 如果 .dsp/ 在此 CLI 之外发生了变更——例如手动编辑文件,或执行了涉及 .dsp/ 的 merge/rebase——请运行 python <skill-path>/scripts/dsp-cli.py rebuild-cache。遵循协议的 Agent 只会通过 dsp-cli 修改 .dsp/,因此正常工作期间很少需要执行此命令。

UID 格式

  • 对象:obj-<8 hex>(例如,obj-a1b2c3d4)
  • 函数:func-<8 hex>(例如,func-7f3a9c12)

源代码中的 UID 标记——声明前的注释 @dsp <uid>:

// @dsp func-7f3a9c12
export function calculateTotal(items) { ... }
# @dsp obj-e5f6g7h8
class UserService:

工作流

设置 DSP(分 3 轮引导)

核心经济规则:每个文件只读取一次,且只由一个子 Agent 读取——所有三轮都基于这一次读取执行。批次并行运行;唯一的同步点是第 3 轮开始前的屏障。

  1. 运行 dsp-cli init 创建 .dsp/ 目录。
  2. 阶段 0 — 根节点:确定入口点及其目录范围,并为每个入口点运行 create-object <path> <purpose> --new-root --scope <dir> 创建根节点(. = 整个仓库)。范围会让后续所有内容自动分配到 TOC。
  3. 清点与分批:列出所有项目文件及其大小(例如 git ls-files | xargs wc -c;跳过供应商代码、构建输出和锁文件)。按 TOC 分组,并将每组拆分为总量大致相等的批次——每个子 Agent 对应一个批次,并行分派。
  4. 第 1 轮 — 所有文件:每个子 Agent 对其批次中的每个文件只读取一次(记录用途、实体、导出项,以及带有使用位置的导入项),并注册文件:使用 create-object,为重要的内部实体使用 create-function --owner,并在源代码中添加 @dsp 标记。
  5. 第 2 轮 — 所有导出项:由同一子 Agent 执行,不再读取文件——每个文件运行一次 create-shared(仅限批次内,不必等待其他批次)。
  6. 屏障:所有批次完成第 1、2 轮后,子 Agent 汇报外部依赖;编排器去重后逐一注册(使用 create-object --kind external,并通过 add-to-toc 将其添加到使用它的其他根节点)。
  7. 第 3 轮 — 所有导入项:由同一子 Agent 执行,仍不再读取文件——使用基于用法的 why 运行 add-import(无效导入项已在第 1 轮读取时过滤);通过 find-by-source 解析目标。
  8. 验证:运行 get-stats、get-orphans、detect-cycles;清点的每个文件都能通过 find-by-source 解析。详情:bootstrap.md。

如果要重新索引代码中已有 @dsp 标记的项目:在每个创建步骤中通过 --uid 传入旧 UID,以稳定身份重建图。

创建实体(编写新代码时)
  1. 创建模块:dsp-cli create-object <path> <purpose>——该模块会加入所有根节点范围涵盖此路径的 TOC(使用 --toc <TOC> 覆盖,可重复指定)
  2. 创建函数:dsp-cli create-function <path>#<symbol> <purpose> --owner <module-uid>
  3. 注册导出项:dsp-cli create-shared <module-uid> <func-uid> [<func-uid> ...]——共享条目是现有实体的 UID,绝不是导出名称
  4. 注册导入项:dsp-cli add-import <this-uid> <imported-uid> <why> [--exporter <module-uid>]——所有 UID 都必须已存在
  5. 外部依赖:dsp-cli create-object <package-name> <purpose> --kind external
浏览图(阅读/理解代码时)
  • 按文件查找实体:dsp-cli find-by-source <path>
  • 按关键词搜索:dsp-cli search <query>
  • 读取 TOC:dsp-cli read-toc → 获取所有 UID,然后使用 get-entity 查看详情
  • 向下查看依赖树:dsp-cli get-children <uid> --depth N
  • 向上查看依赖树:dsp-cli get-parents <uid> --depth N
  • 影响分析:dsp-cli get-recipients <uid> — 哪些实体依赖此实体
  • 查找实体间路径:dsp-cli get-path <from> <to>
更新(修改代码时)
  • 用途变更:dsp-cli update-description <uid> --purpose <new>
  • 文件移动:dsp-cli move-entity <uid> <new-path>
  • 导入原因变更:dsp-cli update-import-why <importer> <imported> <new-why>
  • 根节点的区域变更:dsp-cli update-description <root-uid> --scope <dir>
管理 TOC 成员关系
  • 将现有实体添加到更多 TOC:dsp-cli add-to-toc <uid> [<uid> ...] --toc <TOC>(幂等;例如,某个外部依赖被第二个根节点使用)
  • 在 TOC 之间转移实体(单个或批量):dsp-cli move-to-toc <uid> [<uid> ...] --from <TOC> --to <TOC> — 全部成功或全部不执行;根节点不能离开自己的 TOC
  • <TOC> 是根节点 UID 或 default
删除(移除代码时)
  • 移除导入:dsp-cli remove-import <importer> <imported> [--exporter UID]
  • 移除导出:dsp-cli remove-shared <exporter> <shared>
  • 删除文件/模块:dsp-cli remove-entity <uid>(级联清理)
诊断
  • dsp-cli detect-cycles [--toc ROOT_UID] — 循环依赖(--toc 将范围限定为一个 TOC 根的实体)
  • dsp-cli get-orphans [--toc ROOT_UID] — 未使用的实体(--toc 将范围限定为一个 TOC 根的实体)
  • dsp-cli get-stats [--toc ROOT_UID] — 项目图概览(--toc 将范围限定为一个 TOC 根的实体)

何时更新 DSP

代码变更DSP 操作
新建文件/模块create-object + create-function + create-shared + add-import
新增导入add-import(如果是新的外部依赖,则加上 create-object --kind external)
移除导入remove-import
新增导出create-shared(如果是新函数,则加上 create-function)
移除导出remove-shared
重命名/移动文件move-entity
删除文件remove-entity
用途变更update-description
将模块移动到另一个子项目/根move-to-toc(如果路径发生变化,则加上 move-entity)
另一个根现在会用到该实体add-to-toc
仅内部变更无需更新 DSP

参考资料

评论

支持 Markdown