Skip to content
dsh.fish
Bundle

@local/dsh-knowledgenet

把本地 KnowledgeNet 知识库(v2 开放文件格式)接进 DSH:知识点与前置依赖工具,加上会话内的图谱卡片

Source
Du010902
stars
2 stars
License
MIT
Updated
Updated 5 days ago

Readme

# @local/dsh-knowledgenet

把本地知识图谱接进 DSH:**一个节点就是一个 markdown 文件**,节点之间记「前置依赖」,
用右侧栏的「知识库图谱」面板看全局、随手补点,用 `kn_*` 工具让模型按图谱讲、按纪律写。

## 它是什么 / 不是什么

- **是**:DSH 的一个插件。给**每个工作区**提供一个知识库(工作区里的 `.dsh_knowledge/`),
  以及一组让模型读写这个知识库的工具。
- **不是**:通用笔记软件,也不是把工作区变成"知识库工作区"的开关 —— 任何工作区都可以有知识库,
  有没有完全由"目录里有没有 `.dsh_knowledge/`"决定。

## 存储格式(v3,一节点 = 一个 markdown)

```
<工作区>/.dsh_knowledge/
  library.json          { formatVersion: 3, libraryId, title, createdAt, storage: "single-file-markdown" }
  Nodes/注意力机制.md      ← 一个节点 = 一个文件
  Nodes/注意力机制-2.md    ← 允许同名,自动加后缀
  graph.json            { revision, edges: [ A → B + 为什么 + 出处 ] }
```

节点文件长这样 —— 头部是元数据,**正文就是笔记本身**:

```md
---
id: 01M3NHE78B55MHMDQ9D192MXAM   ← 唯一标识(ULID):**永不随标题或文件名变化**
title: 注意力机制
status: todo                      ← todo | learning | done
aliases: [Attention, 注意机制]
createdAt: 2026-09-29T03:00:00.000Z
updatedAt: 2026-09-29T03:05:00.000Z
rev: 2
---

这里是正文,随便写;用编辑器直接改也不会坏(front-matter 之后的都是正文)。
```

### 身份与名称彻底解耦

| 你做的事 | 结果 |
|---|---|
| 改 `title:` | 身份不变,关系不断 ✓ |
| 重命名文件(`注意力机制.md` → `Attn.md`) | 身份不变,关系不断 ✓(面板按 `id` 找) |
| 建两个同名节点 | 两个不同身份 ✓(`标题.md` / `标题-2.md`) |
| 手工丢一个没有 front-matter 的 md 进 `Nodes/` | 先给临时身份(`adopted-…`),**首次写入时固化成正式 ULID** ✓ |

### 不兼容老格式(v2)

老格式(一节点一个**文件夹** + `.meta/knowledgenet/node.json` + 每个节点一份 `relations.json`)
**已不再支持** ✓。遇到它时不给含糊结果,而是明确报错:

```
unsupported_format:这个知识库是老格式(formatVersion=2),当前版本只支持新格式…
请把 <库根> 整个删掉,然后在面板里点「知识库图谱」重新创建。
```

**迁移方式**:删掉那个目录 → 打开「知识库图谱」→ 自动建一个新的 v3 库(空库,不会动工作区其它文件)✓。

## 库 ↔ 工作区

- **任意工作区都可以有一个知识库**,位置固定为 `<工作区>/.dsh_knowledge/` ✓。
- 打开方式:右侧栏「知识库图谱」标签页(**所有工作区都可见** ✓)。第一次打开若还没有库,
  会**静默创建**一个(不弹窗、不问问题 ✓);已经有的直接显示。
- 会话 cwd 在库的子目录里也能向上找到库 ✓。
- 也可以在工作区之外用配置固定一个库(`cordis.patch.yml` 的 `libraryRoot`)。

### 「看不到插件」是怎么做到的(隔离)

**没有知识库的工作区**:`kn_*` 工具对该会话被 `restrict` 移除、协议提示词渲染为空 ⇒
模型完全看不到插件 ✓。**有知识库的工作区**才可见 ✓,而且**建库后当前会话立刻放开**
(不用等下一个会话 ✓)。

## 工具

| 工具 | 作用 | 读写 |
|---|---|---|
| `kn_list_graph` | 列出节点与依赖(可按 `focusId` 看一跳邻里) | 只读 |
| `kn_find_node` | 按标题/别名查(判重、找复用) | 只读 |
| `kn_read_node` | 读一个节点的正文与关系(含"为什么依赖"、出处) | 只读 |
| `kn_enter_node` / `kn_back` | 进入前置 / 返回上一层(学习栈) | 会话内状态 |
| `kn_write_note` | 写正文(带**内容指纹冲突守卫**,绝不静默覆盖) | 写 |
| `kn_add_prerequisite` | 记一条前置(必要时建点;命中相近候选会先要你确认) | 写 |
| `kn_propose_prerequisites` | 一次提案多个前置(**只写计划文件**,不建点) | 写计划 |
| `kn_plan_status` | 查提案状态(落地了哪些、有没有失败) | 只读 |
| `kn_status` | 自诊断(路由是否注册、缓存/扫描指标、隔离判定留痕) | 只读 |

**写入纪律**(写进系统提示,模型必须遵守):搜索类请求不得调用写入工具;建点前必须说明并等你同意;
一轮最多一次 `kn_add_prerequisite`;要一次处理多个概念只能提案,**落地只能由你在面板里点击**。

## 界面

- **右侧栏「知识库图谱」**:所有工作区可见 ✓。顶部只有「重新整理 / 刷新」两个按钮
  (库名与计数不再显示:库必然在当前工作区的 `.dsh_knowledge` 里,属冗余信息 ✓)。
- **建节点**:面板空白处**右键** → 菜单「创建节点」。(**不再有双击建点**:那是右键菜单坏掉时的临时兜底,已移除 ✓。)
- **加前置**:右键**节点** → 「添加前置节点…」(2–3 条推荐 + 搜索)。
- **删依赖**:右键**连线** → 「删除这条依赖」。
- **删节点**:右键节点 → 「删除当前节点」→ 确认后**直接删掉那个 `.md`**(不可恢复 ✓)。
  节点就是一个文档,所以**没有回收站、也没有墓碑**:想留底就自己复制一份 `.md` ✓。
- **提案审阅**:agent 提交的提案显示在图上方的审阅区,勾选后点「落地」;已落地的可以「撤销」
  (把这次**新建**的节点删掉 ✓ —— 落地时它们是空节点,所以不会丢内容 ✓)。
- **节点笔记编辑器**(右键节点或选中条上的「编辑笔记」打开):打开后以**图谱区域内的留边弹窗**显示,保留背景图谱和上方工具栏 ✓
  (3D 图谱、状态灯、视角图、提示条一起让位 ✓;点 × 关闭即原样回来、镜头不重置 ✓),
  面板够宽时正文收成一条**居中的阅读栏** ✓(一行不会拖太长 ✓);
  只有**一条标题栏**(节点名 + 关闭),高度全留给正文 ✓;**没有模式切换**,打开就是正文(所见即所得)。
  **没有保存按钮**:改完按 `Ctrl / ⌘ + S` 保存(提示挂在标题栏悬停里);
  未保存时节点名右上角有个 `*`,保存中转成「正在保存…」,保存失败与冲突另有提示条与重试入口 ✓。
  正文里若有富编辑器保不住的语法(原始 HTML、脚注、指令等)**自动改用纯文本**编辑、原文一字不动 ✓;
  想回正文需显式点一次「仍要用正文编辑」,把那些语法删干净后会自动回正文 ✓。
  **提示按实际模式说** ✓:还在正文模式时只说明"这里有保不住的语法、保存时这一处可能被改写",
  只有真的切到纯文本才说"已改用纯文本编辑"(不再"没切换却说切了"✗)。
  **代码块复制会带上块结构** ✓:点代码块的「复制」同时写入**裸代码**(粘到终端 / IDE 照旧 ✓)、
  转义后的 `<pre><code>` ✓,以及本插件自己的一份载荷 ✓ ——
  在本插件正文里粘贴时会**还原成一个完整代码块**(语言、缩进、空行、`<iostream>` 一字不改 ✓,
  一次撤销回到粘贴前 ✓);在已有代码块里粘贴仍然只插字符、不嵌套新块 ✓,
  从别处复制的 Markdown / HTML 继续走原有粘贴 ✓。
- **打开有多快**:读正文**不再扫全库** ✓ —— 宿主按"节点身份 → 文件路径"的索引**只读目标文件**
  (图谱/工具刚扫过的结果会直接喂给索引,所以通常一个文件都不多扫 ✓),
  接口的格式检查也只看 `library.json` 一个小文件 ✓(不再顺手加载整个图谱 ✗);
  索引可以活很久 ✓(每次读都现场校验身份 + 一次 `readdir` 的目录变动检测 ✓),
  建索引时**每个文件只读头部 4KB** ✓(只取 front-matter 的 id、不读整篇正文 ✓;头部不够会自动退回读整份 ✓),
  冷启动的扫描与建索引都是**有界并发** ✓(不再一个文件一次串行往返 ✓);
  保存前**仍然现场读磁盘文件比对指纹** ✓,缓存不参与判定 ✓。
  界面上两段提示是分开的:读取阶段只说「正在读取笔记」,组件真的挂载后才说「正在准备编辑器」✓
  (`kn_status` 里 `status.docReads` 记宿主走了 `index` / `index-miss` / `full-scan` 与毫秒,
  `status.noteApi` 记**接口端到端**耗时(含根目录解析与格式检查 ✓),
  客户端记 `note-open-start / -loaded / -ready` 并带**同一个 requestId** ⇒ 两端可对齐 ✓,
  **卡在读盘还是卡在编辑器创建一眼可辨** ✓)。
- **保存 / 关闭 / 再打开**:未保存时关闭或切节点会问一次,按钮写明动作(「保存并关闭」/「保存并切换」)✓;
  保存确认后**先同步落缓存、再通知离开** ⇒ 重新打开直接显示刚保存的正文,
  不会再把自己的保存当成"文件在外部发生了变化"✓。**"磁盘基线"与"编辑器快照"分开存**:
  宿主落盘会去掉末尾空白、编辑器输出会补回末尾换行,这类**编辑器自己的写法**不算未保存修改 ✓
  (真的改了内容 —— 包括行首缩进与空行 —— 照旧算 ✓)。真冲突仍受保护,但默认只有一句话 +「比较修改」,
  点开才分栏比较「我的修改」/「文件最新版本」,且只对差异行做局部标注 ✓。
- **表格**:就是文档里的普通表格 —— 没有常驻的十字线 / 加号 / 行列抓手,划过单元格也不会点亮整行整列;
  点击落的是**原生文字插入光标**(编辑器不再装虚拟光标,颜色跟随正文 ✓)。Crepe 自带的表格节点视图会
  接管单元格点击、并异步地选中整段 ✗ —— 插件在**事务派发这一层**(所有选区变化的唯一漏斗 ✓)
  把它改写成**离点击最近**的文字位置 ✓:点击后是折叠光标、输入只插入、原有文字不被替换 ✓,
  也不会被上游那次 `scrollIntoView()` 拽动正文 ✓。
  **光标进表格、或鼠标悬停表格**时,表格角上会出现一个带文字的小入口「表格操作」✓(只读 / 保存中保留但禁用 ✓);
  入口纵向**跟着当前单元格 / 正文可见区走** ✓ —— 长表格往下滚时它不会留在早已滚出视口的表格顶部被裁掉 ✓
  (表格整个滚出视口时明确收起 ✓,菜单也保证整块落在正文视口里 ✓)。
  **入口就贴在当前单元格的右上角** ✓,而且是**在格子外面**(纵向放在这一格上方、横向贴它的右边 ✓),
  所以**不会挡住你正在编辑的那一格** ✓;这一格就在可见区最顶上、上面也放不下时,
  它会先退到"表格顶部之外",实在没地方才压回格子角上并**收成 22px 小图标** ✓(尽量少挡 ✓,
  仍然半透明、悬停显示「表格操作」✓)。
  定位用的是**正文表格**本身 ✓ —— Crepe 在正文表格前面还放了一张隐藏的拖拽预览表,
  按"块里第一张 table"取会拿到那张零尺寸的预览表、入口就永远不显示 ✓;
  现在按"排除预览表 + 必须在 table-wrapper 里 + 必须有尺寸"过滤 ✓(有光标单元格时直接用它所在的表格 ✓),
  收起时也会说明是「没找到正文表格 / 尺寸为零 / 滚出视口 / 放不下」哪一种 ✓;
  多表格时**鼠标悬停的那张优先** ✓,菜单打开期间**锁定**目标表格 ✗(不会跨表格误操作 ✓)。
  **表格在阅读栏里横向居中** ✓(短表格不再贴左 ✓;比可视宽度还宽时 auto 外边距按 0 处理,
  仍然从左开始、由表格外层横向滚动 ✓,不会出现"左边那截滚不到"✓)。
  **点开**才是完整菜单,一共十三条:上/下插行、左/右插列、左/中/右对齐、**上移/下移本行、左移/右移本列**、
  删本行、删本列 ✓。移动是**整体搬整行 / 整列**(内容、对齐、列宽都跟着走,可撤销 ✓);
  到边界、表头行、多行多列选择或有合并单元格时会禁用并写出原因 ✓。Esc 关掉并回到正文、键盘也能用 ✓
  (面板跑在 Shadow DOM 里:判"点的是不是菜单内部"用 `composedPath` + 节点引用 ✓,点菜单项不会被当成点外面 ✓;
  打开菜单会把焦点放进菜单 ✓,↑/↓ 与 Esc 才真正生效 ✓)。
  多单元格选择的底纹是低对比的 10%,复制矩形区域照常可以 ✓,拖列宽的手柄也还在 ✓。
- **只有空间视图**(三维)。聚焦(二维)视图按需求已整体移除 ✓。

## 后台开销(桌面端流畅度)

面板只在**真的看得见**时工作 ✓:

- 面板自身可见性由 `IntersectionObserver` 判断:收起侧栏、被覆盖、零尺寸时 ⇒ **不取数、不渲染三维**
  (`requestAnimationFrame` 一起停 ✓);
- 切换标签页即卸载(`keepMounted: false`)✓;
- 取数有请求序号 + `AbortController`:切换工作区/刷新时旧请求被取消,过期响应不会覆盖新数据 ✓;
- 划词浮条**先判断有没有选中文字**,没有就一个请求都不发(普通点击、拖拽结束不再打扰宿主 ✓)。

## 安全边界

- 库根必须存在、且写操作**不越出库根**(路径守卫,拒绝 `..` 与绝对路径)。
- 写正文用**内容指纹**做乐观并发:外部编辑器改过就拒绝,并回传实际指纹。
- 关系写在 `graph.json`:加边**幂等**、自环与成环**拒绝**;删节点会摘掉它的边。
- 提案的落地/撤销**只有界面能触发**,agent 无法自己落地 ✓。
- 删除是**彻底删除**(无回收站、无墓碑文件);`Backup/` 与 `.knowledgenet/` 都已随该决定移除(老库里若还留着,可以手动删掉 ✓)。
- 旧格式(v2)库不会被动过:只会报 `unsupported_format` 并告诉你怎么办 ✓。

## 安装

profile 的 `cordis.patch.yml` 插入本插件(`@local/dsh-knowledgenet`)。
插件零运行时依赖(第三方代码内联),安装时不执行任何脚本。

## 开发

```bash
node build.mjs                 # 构建宿主 + 客户端两个半
node --test "tests/*.test.mjs" # 全部测试
node scripts/sync-vendor.mjs --check   # 上游副本必须逐字节一致(77 个文件)
```

- 上游(`src/vendor/upstream/**`)是**逐字节冻结**的开放格式实现:只读、不手改;
  需要改行为时用 `build.mjs` 的 needle 补丁,或写在自己模块里。
- 类型体检:`node_modules/.bin/tsc --noEmit --ignoreDeprecations 6.0 -p tsconfig.json`。
  目前有约 85 条**既有**类型宽松(`never`、缺 `@types/node` 等)—— 建议当**棘轮**用:
  只看你这次改动有没有引入**新**错误(它抓过"本地函数漏定义"这类构建不报、运行时才炸的问题 ✓)。

## 已知取舍 / 尚未实现

- **v2 兼容代码仍在源码里但不可达**(`loadLibrary` 只认 v3)⇒ 可以整体删除,属机械清理。
- 一个工作区**一个**知识库(固定的 `.dsh_knowledge/`);多库、跨工作区共享尚未实现。
- `kn_list_graph` 一次返回整张图(默认上限 400 节点);大库分页未做。
- 三维视图为上游实现(构建期补丁注入四元数 trackball);标签密度等仅有固定策略。

笔记弹窗标题栏提供前置、被依赖列表和添加前置;正文选中文字后可创建前置节点。表格结构操作由单元格右键菜单进入,支持删除整张表格并撤销。

Install

dsh plugin --profile web add github:Du010902/dsh-knowledgenet-plugin

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source