关于 data-structure-protocol
用于将项目代码实体和依赖记录为 `.dsp/` 图谱的 skill 与 CLI 协议。它指导编码 Agent 建立索引、导航和分析影响,并随代码变更维护图谱。
主要功能
- 01以图谱表示实体和依赖
- 02记录用途、导入和导出
- 03导航并搜索项目实体
- 04分析依赖方及依赖路径
- 05随代码变更更新图谱记录
- 06诊断循环依赖和孤立实体
适用场景
- 01接手代码库时构建图谱
- 02修改代码前查找依赖
- 03评估模块或函数变更的影响
- 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.jsonLICENSEreferences/bootstrap.mdoperations.mdstorage-format.mdscripts/dsp-cli.py详细介绍
数据结构协议(DSP)
DSP 会在 .dsp/ 目录中构建项目实体的依赖图。每个实体(模块、函数、外部依赖)都会获得一个 UID、描述、导入列表和导出索引。该图可以回答:有哪些内容、它们为何存在、哪些内容依赖哪些内容,以及哪些内容被谁使用。
DSP 不是面向人类的文档,也不是 AST 转储。 它记录的是 含义(目的)、边界(导入/导出)以及 连接的原因(为什么)。
Agent 提示词
在处理由 DSP 跟踪的项目时,嵌入以下上下文:
此项目使用 DSP(数据结构协议)。
.dsp/目录是此项目的实体图:模块、函数、依赖项和公共 API。它是代码结构的长期记忆。核心规则:
- 更改代码前——通过
dsp-cli search、find-by-source或read-toc找出受影响的实体。阅读它们的description和imports,以了解上下文。- 创建文件/模块时——调用
dsp-cli create-object。对于每个导出的函数,调用create-function(并提供--owner)。通过create-shared注册导出。- 添加导入时——调用
dsp-cli add-import,并附上简短的why。对于外部依赖,如果该实体尚不存在,请先调用create-object --kind external。- 移除导入/导出/文件时——分别调用
remove-import、remove-shared、remove-entity。级联清理会自动完成。- 重命名/移动文件时——调用
move-entity。UID 不会改变。- 如果仅更改内部实现,且不影响目的或依赖项,不要改动 DSP。
- TOC 成员关系——新实体会加入所有根作用域涵盖其路径的 TOC(也可以显式传入
--toc,该参数可重复使用)。使用add-to-toc/move-to-toc调整成员关系。- 初始化——如果
.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 轮开始前的屏障。
- 运行
dsp-cli init创建.dsp/目录。 - 阶段 0 — 根节点:确定入口点及其目录范围,并为每个入口点运行
create-object <path> <purpose> --new-root --scope <dir>创建根节点(.= 整个仓库)。范围会让后续所有内容自动分配到 TOC。 - 清点与分批:列出所有项目文件及其大小(例如
git ls-files | xargs wc -c;跳过供应商代码、构建输出和锁文件)。按 TOC 分组,并将每组拆分为总量大致相等的批次——每个子 Agent 对应一个批次,并行分派。 - 第 1 轮 — 所有文件:每个子 Agent 对其批次中的每个文件只读取一次(记录用途、实体、导出项,以及带有使用位置的导入项),并注册文件:使用
create-object,为重要的内部实体使用create-function --owner,并在源代码中添加@dsp标记。 - 第 2 轮 — 所有导出项:由同一子 Agent 执行,不再读取文件——每个文件运行一次
create-shared(仅限批次内,不必等待其他批次)。 - 屏障:所有批次完成第 1、2 轮后,子 Agent 汇报外部依赖;编排器去重后逐一注册(使用
create-object --kind external,并通过add-to-toc将其添加到使用它的其他根节点)。 - 第 3 轮 — 所有导入项:由同一子 Agent 执行,仍不再读取文件——使用基于用法的
why运行add-import(无效导入项已在第 1 轮读取时过滤);通过find-by-source解析目标。 - 验证:运行
get-stats、get-orphans、detect-cycles;清点的每个文件都能通过find-by-source解析。详情:bootstrap.md。
如果要重新索引代码中已有 @dsp 标记的项目:在每个创建步骤中通过 --uid 传入旧 UID,以稳定身份重建图。
创建实体(编写新代码时)
- 创建模块:
dsp-cli create-object <path> <purpose>——该模块会加入所有根节点范围涵盖此路径的 TOC(使用--toc <TOC>覆盖,可重复指定) - 创建函数:
dsp-cli create-function <path>#<symbol> <purpose> --owner <module-uid> - 注册导出项:
dsp-cli create-shared <module-uid> <func-uid> [<func-uid> ...]——共享条目是现有实体的 UID,绝不是导出名称 - 注册导入项:
dsp-cli add-import <this-uid> <imported-uid> <why> [--exporter <module-uid>]——所有 UID 都必须已存在 - 外部依赖:
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