# dsh-notes — 笔记(按目录分组的本地 Markdown 笔记) [English](README.md) | 中文 把笔记存成**本地 Markdown 文件**、按目录分组的 DeepSeek Harness 插件(bundle)。 DSH bundle,分两半: - **Host 半区**(`index.js`,纯 ESM,无需构建)—— 「笔记」的读写服务: **选择一个本地目录作为根目录**(默认 `/dsh-notes`,可在页面内切换并 记住,无需重启);根目录的直接子目录 = **分组**(二级目录,不存在自动创建); 分组内的 md 文件 = **笔记**;读取根/分组下的 **AGENTS.md / CLAUDE.md / *.rules.md / *规则.md** 等规则 md 作为约定上下文。loopback + 同源守卫路由供 Web UI 使用;另注册 `note_root` / `note_list` / `note_rules` / `note_create` / `note_read` / `note_delete` 六个 agent 工具。 - **客户端半区**(`src/client/*`,构建到 `lib/client.js`)—— 左侧栏「笔记」入口 打开页面:分组列表、规则查看、笔记新建(粘贴文本)/查看/编辑/重命名/删除、 切换根目录。 ## 存储模型(目录即分组,md 即笔记) ``` <根目录>/ ← 你选的本地目录(第一级) AGENTS.md ← 根级规则文件(可选,大小写不敏感识别) <分组>/ ← 第二级目录 = 分组(不存在会自动创建) AGENTS.md ← 该分组规则文件(可选) <笔记>.md ← 一条笔记(其余 .md 均视为笔记) ``` 规则文件 = 名字匹配的 md(大小写不敏感):`AGENTS.md`、`AGENTS.local.md`、 `CLAUDE.md`、`*.rules.md`、`*规则.md` / `*規則.md`。同名笔记不允许创建 (保留给规则)。宽容解析:手写的纯 Markdown 也能直接放进分组当笔记,缺标题用 文件名、缺分组用所在目录名;粘贴一段**自带 front matter 的完整 md 文件**时 原样存储,绝不二次包裹。 每条插件创建的笔记: ```markdown --- title: useState 笔记 group: 前端 created: 2025-01-01T00:00:00.000Z updated: 2025-01-01T00:00:00.000Z --- 正文(任意 Markdown)。 ``` ## 功能 - 分组 = 根目录的二级目录:列表 / 新建;写笔记时分组不存在自动创建目录。 - 笔记:粘贴文本或整理内容 → 存成 `<分组>/<笔记>.md`;可查看 / 编辑 / 重命名 / 删除;正文任意 Markdown。 - 规则上下文:根目录与各分组的 AGENTS 等规则 md 以 chip 形式展示,点击即可 阅读全文;模型写笔记前可用 `note_rules` 读取约定再归类。 - 根目录:**页面内切换、即时生效并记住**(host 把当前路径记在 `/dsh-notes/.notes-root`;配置文件显式写 `rootDir` 时优先于它)。 - agent 工具: - `note_root` — 查看/切换根目录; - `note_list` — 列分组概览,或列某分组内笔记; - `note_rules` — 读根规则(+ 指定分组规则)作为写作上下文; - `note_create` — 创建/更新笔记(分组自动创建;`update: true` 覆盖更新); - `note_read` — 读取一条笔记全文; - `note_delete` — 删除一条笔记(只删该 md,不动分组目录)。 - 中文界面;侧栏入口 DOM 与视觉语言对齐技能中心(36px 圆角行 + 24px 图标盒, 使用 shell 的 `--dsw-alias-*` 令牌,支持侧栏折叠)。 ## 布局 | 路径 | 作用 | | --- | --- | | `index.js` | Host 半区:分组/笔记/规则读写 + agent 工具 + guard 路由 | | `cordis.patch.yml` | Bundle layer:host 行 `notes` | | `src/client/*` | 浏览器半区:侧栏入口 + 笔记页覆盖层(React) | | `lib/client.js` | 构建出的客户端产物(`exports["./client"]`) | | `tsdown.config.ts` | 客户端构建(closure-factory `__ModuleLoader__` 格式) | | `scripts/smoke-host.mjs` | Host 半区冒烟(md 解析/CRUD/规则/工具,隔离 DSH_HOME) | | `scripts/loader-boot.mjs` | 真实 Loader + webServer 的 HTTP 路由冒烟 | | `scripts/smoke-client.mjs` | 客户端产物冒烟(stub 浏览器执行 closure) | ## Build 客户端半区需要构建;host 半区直接以 ESM 交付: ```sh npm run build:client # tsdown → lib/client.js (+ map) ``` `tsdown` 二进制来自 DeepSeek Harness checkout;React 通过 shell 的模块表解析 (`react`、`react-dom/client`),绝不打包。冒烟脚本需要本机可解析 `@deepseek-ai/*`(临时 `ln -s ~/.dsh/profiles/node_modules node_modules` 即可,跑完删除)。 ## Install ```sh dsh plugin --profile web add ./dsh-notes ``` 追加 `dsh-notes` layer 后重启 profile(host 与 client 变更都需重启): ```sh pnpm dsh web ``` 重启后左侧栏出现「笔记」入口。首次使用默认为根目录 `/dsh-notes`(自动创建);可在页面内「切换目录」填任意本地绝对路径 (支持 `~`,留空恢复默认)。 ## Plugin config 全部可选(默认值如下);可在 profile 自己的 `cordis.patch.yml` 里覆盖: ```yaml - id: notes config: rootDir: '' # 显式指定根目录(绝对路径/~/…);空 = 用页面记住的目录 maxRuleBytes: 524288 # 单个规则文件读取上限 maxNoteBytes: 2097152 # 单条笔记读取上限 maxBodyBytes: 1048576 # 提交的笔记正文上限 ``` ## Host routes(全部 loopback + 同源守卫) | 路由 | 方法 | 作用 | | --- | --- | --- | | `/api/notes/state` | GET | 根目录状态(当前路径/是否配置指定/是否已存在) | | `/api/notes/root` | PUT | 切换根目录(空 = 恢复默认) | | `/api/notes/groups` | GET | 分组概览(笔记数 + 组内规则文件名) | | `/api/notes/group` | POST | 新建分组目录 | | `/api/notes/rules?group=` | GET | 根(或分组)规则文件列表 | | `/api/notes/rule?group=&file=` | GET | 读取单个规则文件全文 | | `/api/notes/notes?group=` | GET | 某分组笔记列表 | | `/api/notes/note?group=&file=` | GET / DELETE | 读取 / 删除笔记 | | `/api/notes/note/create` | POST | 新建笔记(分组自动创建;重名 409) | | `/api/notes/note/update` | POST | 更新笔记(保留 created,刷新 updated) | | `/api/notes/note/rename` | POST | 重命名笔记文件 | ## 安全边界 浏览器只通过上述 guard 路由访问数据,不直接碰磁盘;分组/笔记名先清洗 (拒绝路径穿越字符与隐藏文件),再 `resolve` 后校验仍落在根目录内;根目录切换 写入独立指针文件;写 md 全部临时文件 + 原子 rename,单写队列防并发写坏; 目录缺省自动创建。规则/笔记读取有字节上限(超限 413)。 ## Remove ```sh dsh plugin --profile web remove dsh-notes ``` ## Verification record - `scripts/smoke-host.mjs`(隔离 DSH_HOME + 临时根,无网络依赖)—— 全部通过: front matter/笔记解析与幂等往返、纯手写 md 宽容解析、粘贴整份 md 原样存储 (不二次包裹)、名称清洗与保留规则名拒绝(AGENTS/CLAUDE/*rules/*规则)、 规则文件识别(根/分组)、分组 CRUD(重名 409、自动建组)、笔记增/查/重名 409/原地更新(保 created)/重命名(隐式标题跟随)/删除/重复删除 404、 超限正文 413、`note_list`/`note_rules`(根+分组规则文本)/`note_create` (自动建组)/`note_read`/`note_delete`/`note_root`。 - `scripts/loader-boot.mjs`(真实 Cordis Loader + `dsh-host-webserver`,隔离 DSH_HOME)—— 全部通过:行加载、webServer 绑定、state/root 切换并持久化、 分组/规则/笔记的 HTTP CRUD、规则文本读取、foreign-host 403、错误方法 405、 未知路由 404、坏 JSON 400、非法分组名 400、删除缺失笔记 404。 - `scripts/smoke-client.mjs`—— 全部通过:`lib/client.js` 以 `__ModuleLoader__` closure 注册 `dsh-notes`,导出 `name: notes-ui / inject / apply`,无机器路径 泄漏,仅解析模块表外链(react 家族)。 - 静态预检:`node …/dsh-plugin-development/scripts/check-artifact.mjs bundle .` → PASS,0 warning。