DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

wonderandyou /

wonderandyou/dsh-memo

Verified

给 DSH 加一组按需查备忘录的工具:目录 / 按节 / 全文搜索 / 项目卡 / 速查 / 精简,一次几百 token 就能查到事实,不用把整份 MEMO.md 读进上下文;附带分类面板与三维书架动画。

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@3e1b71a6

dsh-memo

给 DSH(DeepSeek Harness)加一组按需查备忘录的工具,外加一个会围着书架转圈、抽书查看的小动画。

一句话:把一份 19 万字符的 MEMO.md 切成几百段,每次只取需要的那一段——而不是把整份读进上下文。


它解决什么问题

AI 助手在长期工作中会攒出一份越来越大的备忘录(本机的 MEMO.md 已经 19 万字符 ≈ 3 万 token)。 每次要查"那个端口是多少",如果把整份读进去,一次就烧掉 3 万 token,而且大部分内容跟当前问题无关。

这个插件把备忘录做成可按需索引的知识库:

  • 想找东西 → 先看目录(约 600 token),再取那一节(几百到几千 token)
  • 想全局扫一眼 → 取「精简版」(约为原文 1/5)
  • 每次查询省了多少,右上角面板实时显示

安装

DSH 插件的安装位置有三个,缺一个都不生效:

$profile = "$env:USERPROFILE\.dsh\profiles\desktop"
$src = "C:\path\to\dsh-memo"     # 你解压出来的目录

# 1) 依赖:在 profile 的 package.json 里加一行
#    "dsh-memo": "link:<上面那个 $src>"

# 2) 启动清单:同文件的 dsh.profile.bundles 数组里加 "dsh-memo"

# 3) 软链:
cmd /c mklink /J "$profile\node_modules\dsh-memo" "$src"

然后关掉 DSH 窗口重新打开(client 插件不热重载)。

仓库里的 scripts/install-local.mjs 可以自动做上面三步:

node scripts/install-local.mjs --profile desktop

工具(模型用)

工具 用途 典型消耗
memo_outline 目录:节号 / 标题 / 行号 / 字符数 / 估算 token ~600 tok
memo_section 按节号取原文(0.2、3.6、5 …) 0.5~4k tok
memo_search 关键词或正则搜索,带上下文与所属节 ~300 tok
memo_project 按项目名取整张卡(支持中文名与别名) 1~4k tok
memo_quick 速查:machine / ports / interpreters / dirs / commands ~150 tok
memo_status §6 当前状态表 ~500 tok
memo_playbook §5 里的可复用套路 ~300 tok
memo_append 追加内容(自动备份 + 读回校验 + 失败回滚) 一行确认
memo_lite 取精简版(全局速览) 1~3k tok
memo_compact 生成精简版 MEMO.lite.md(原文件一字不动) 一行确认

面板(人用)

右上角一个书签按钮,点开是面板:

  • 备忘录规模(字符 / 行 / 节 / 项目卡)
  • 本次运行统计:查询次数、累计返回字符、比整份读省了多少、精简版省了多少
  • 最近查询:时间 / 工具 / 参数 / 返回大小 / 节省比例(失败标红)
  • 分类:按用途分组(速查 / 环境与坑 / 项目 / 套路 / 状态 / 专题),组可折叠,点任意节就地预览
  • 搜索框:自己搜备忘录
  • 精简:点按钮 → 确认 → 生成 MEMO.lite.md

原理

1. 索引:把 Markdown 切成节

lib/memo-core.js 只做一件事:把 MEMO.md 解析成「节」的数组。

  • 只认 ## 和 ### 两级标题(# 当文件标题)
  • 每节的区间 = 本节标题行 → 下一个「同级或更高级」标题之前
  • 节号从标题前缀提取:## 3.6 QQ 机器人 → id 3.6;没有数字前缀的用 h<行号>
  • 每次查询前先 stat 比对 mtime 和 size,文件没变就复用上次的索引

所有查询共用这个索引,返回统一的 { text, chars, tokens }。

2. 为什么能省 token

操作 返回量
整份读 ~191,000 字符
memo_outline ~2,000 字符
memo_section 0.2 880 字符
memo_quick ports 880 字符

一次「查端口」从 19 万字符降到 880,省 99.5%。

3. 分类:按节号前缀归类

规则只看节号前缀,不看标题文字,所以换一份备忘录只要节号约定相同就自动生效:

0.x            → 速查
1 / 2 / 4 / 7  → 环境与坑
3 / 3.x        → 项目
5              → 套路
6              → 状态
其余            → 专题

⚠ 一个坑:父节的字符数包含全部子节(§3 的 range 覆盖了 3.1~3.18)。 统计总数时只能累加叶子节,否则数目会翻倍——第一次实现就是这么错的。

4. 精简备忘录:逐行打分

不是"删东西",而是另存一份高密度版,原文件一个字不动。

对每一行按「信息密度」打分:

特征 分数
标题行 必留(100)
表格行 | ... | 45
含路径(C:\ / ~/ / /api/ / 文件扩展名) 42
含端口 / 127.0.0.1 / localhost 36
含命令(pip / npm / git / curl / pwsh …) 30
含「坑 / 踩过 / 注意 / 不要 / 禁止 / 必须 / 铁律」 30
列表项 22
又长又没有信号(>160 字且分数 <25) 0 —— 直接丢

然后按节的预算截断:

预算 = max(280, 节字符数 × ratio)      // ratio 默认 0.16
短节(<700 字符)整段保留,不值得压
代码块只留前 3 行
超出预算时从最低分行开始丢

⚠ 第二个坑:父节只输出标题,正文交给子节各自压缩。 否则 §3 会把所有 3.x 再压一遍,同一段内容在精简版里出现两次(第一次实现输出 6.4 万字符、内容重复)。

实测(19 万字符的备忘录):191,790 → 36,053 字符,省 81%,86 个章节标题全部保留。

产物落在同目录的 MEMO.lite.md,随时可以重新生成。

5. 书架动画

分两层,主方案是预渲染视频,兜底才是实时绘制。

5.1 场景设计

  • 三个双面书架摆成等边三角形(边长 2.45,重心在原点),每个书架朝外转,中间留出空地
  • 每层 8 本书,书是有厚度的三维盒子(前面 / 左面 / 右面三个面),高矮胖瘦用确定性伪随机,正反两面都摆满
  • 相机绕 Y 轴 8 秒转满一圈,同时抬高 2.5 米(约 25° 俯视),所以三角形的三个顶点分得开
  • 每转 90° 抽一本书:那本书从它自己的槽位离开(架上当场留一个洞),朝镜头飞 2.8 米、边飞边自转一圈、体积放大到 2.2 倍,然后沿原路落回同一个洞

5.2 为什么落地看不出接缝

架上书和飞行书用同一个绘制函数、同一组尺寸与颜色:

u=0   飞行书 = 原尺寸,位置 = 架上位置   → 与架上那本完全重合
u=1   飞行书 = 原尺寸,位置 = 架上位置   → 落回原位
中间  放大 1 + sin(πu)×1.15 倍,飞到镜头前

所以「动画放完突然补一本书上去」这种事不会发生——它从来没有消失过,只是离开了原位。

早期版本踩过两次:

  • 飞行书单独画成亮边框的盒子 → 落地瞬间换皮,像凭空补一本
  • 书架上不画书(只有格子)→ 抽出来就是凭空出现

5.3 怎么渲染成视频

用 Remotion 逐帧渲染成 VP9 + alpha 透明的 webm:

# 透明版(给插件用)
npx remotion render MemoShelf out/memo-shelf.webm `
  --codec=vp9 --pixel-format=yuva420p --image-format=png --crf=45

# 带界面底图的演示片(给人看)
npx remotion render MemoShelfButton out/demo.mp4 --codec=h264
  • --pixel-format=yuva420p 必须有,否则没有 alpha 通道
  • --crf=45 把 8 秒 1080p 压到 5~7 MB(不带这个参数会到 15 MB)
  • 8 秒正好转满一圈,首尾角度一致 → 可以无缝循环播放

5.4 触发与播放

host 侧:每次工具调用 → stats.calls++ → 写 query-log.jsonl
client 侧:每 1.4 秒拉 /api/memo/ping.json
           发现 calls 变大 → 按钮加 playing 类 → 播视频
           播满 8 秒没有新查询 → 收回成书签
  • 动画不是全屏浮层,而是右上角书签按钮自己从 34×34 展开成 200×200 的圆角小窗(CSS transition)
  • 视频是按钮的子元素(position:absolute; inset:0; object-fit:cover; border-radius:inherit),所以展开收起时位置自动跟随
  • 1600×900 的画面塞进正方形窗口时居中裁切

5.5 Canvas 兜底

如果视频加载失败(没打包 / 解码失败 / 浏览器不支持 alpha),lib/client.js 里的 Canvas 场景会用同一套投影数学实时画一遍:

  • 同样的三角函数投影(绕 Y 轴旋转 + 25° 俯仰 + 透视)
  • 同样的三角形布局与抽书逻辑
  • 尺寸固定 200×200,画在按钮里

兜底版比视频版朴素一些(没有辉光滤镜),但布局和动作一致。


效果一致性(装好就是视频里那样)

保证项 做法
动画画面 视频文件打包在 assets/memo-shelf.webm,不是运行时现算的,安装即带
动画参数 全硬编码在源码里,不读配置文件,不会被环境改动
摆放与节奏 三角形、绕圈速度、抽书间隔都是常量
兜底 视频不可用时用 Canvas 画同一套场景,不会变成"什么都没有"
备忘录路径 file 留空时自动用 ~/.dsh/MEMO.md,换机器不用改

唯一的运行时依赖是 Chromium 内核(DSH 桌面端是 Electron,天然支持 VP9 alpha webm)。 如果你的宿主不是 Chromium,动画会退到 Canvas 兜底版。


配置

cordis.patch.yml:

- insert:
    - id: memo
      name: 'dsh-memo'
      config:
        # file: ''        # 留空 = 自动取 ~/.dsh/MEMO.md
        tokenChars: 3.2   # token 估算系数(中英混排经验值,只用于展示)

自测

node scripts/selftest-core.mjs        # 解析 / 检索 / 追加 / 分类 / 精简,41 项
node scripts/preview/screenshot.mjs   # 用无头 Edge 截图面板(离线,不连 DSH)

scripts/preview/index.html 加载的是真实的 lib/client.js,只是用假宿主和假 /api 响应, 所以它能当 UI 的离线回归测试用: #cats 截分类面板、#confirm 截精简确认层、#anim 截按钮动画。


边界

  • 只依赖 DSH 提供的 tools、webServer、slots 服务,不 spawn 任何外部进程,不需要 Python / CLI / 本地服务
  • webServer 缺失时工具照常可用,只是没有面板和动画
  • 面板端点只放行本机请求(127.0.0.1 / localhost)
  • 精简版和备份都写在备忘录同目录:MEMO.lite.md 和 memo-backups/

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 3e1b71a62a38

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout