dsh-memoryleak
September 3, 2026 · View on GitHub
好记性不如烂笔头,有了 memoryleak 你就不会 memoryleak。 一个把记事本带进 DSH 的插件:记的东西全是本地 Markdown 文件,git 和任何编辑器都能直接用,换个工具也带得走。
Warning
⚠️ 隐私警示:谨慎使用本插件 ⚠️
任何 dsh 工作目录都可以把笔记写进同一个 Vault——这同样意味着你的领导很容易通过蒸馏(distill)你的 Vault 摸清你的动态。
- 当前请谨慎使用本插件,想清楚什么能记、什么不能记。
- 后续会推出**「失联即焚」**功能。在此之前,务必做好云备份,甚至不要把 Vault 放在公司电脑上。
- 在此之后,更要做好备份,防止系统自燃。
你能用它干什么
你在写代码的时候,总有各种事想顺手记下来:这个 bug 怎么修的、明天要跟进什么、哪天要交什么材料。这些事放进专门的笔记软件就散落各处,放在脑子里又会漏。这个插件把它们集中存在你自己指定的一个目录(Vault)里:
- 先选一个 Vault,只用选一次。刚装好时设置是空的,执行任意
/ml命令会引导你选一个本地文件夹:输入路径时Tab 自动补全(↑↓ 选候选,输e补盘符、~开头是用户目录),或直接用当前工作区;目录不存在会自动创建。之后所有日志、待办都固定写进这个目录,不再跟着会话的工作区跑。换项目、换会话,记的事都在同一个地方。 - 想到什么,一句话记下来。输入
/ml 明天找财务对一下发票回车,这句话就写进了今天的日志文件(2026-08-16.md)。文件就在 Vault 根目录,打开就能看,提交 git 就能留痕。 - 待办带结构和优先级。输入
/ml todo add 交季度报告(简写n或a),会弹一个固定表单问你类型和重要程度,各选一项、选完自动提交;表单全程可键盘——数字1/2/3或字母d/s/a选类型(deadline/sleep/anytime)、字母u/m/l选重要程度(urgent/medium/low),按页脚提示来即可。选到 deadline / sleep 时输入区变成日期选择器(日历 + 今天/明天/本周/本月快捷键,数字键1-4直选、←→切月)。有截止日的到期自然浮出来;不急的可以设成"睡到"某天再提醒你。 - 找文件像 VSCode 一样快。输入
/ml view加几个字母,输入框上方弹出候选列表,↑↓ 选、Tab 补全、回车打开。 - 收尾时把对话沉淀成笔记。干完一段活,输入
/ml note:把这一段对话交给当前模型,由它在会话里直接整理成工作记录、知识文件和结构化登记(详见下文「/ml note」专章)。思考、工具调用与回复全程原生显示,与普通对话完全同款。花 token 的命令有/ml note、/ml ask和/ml mail read三个,其余命令全部本地完成。 - 把工作邮箱也接进来。设置里配好 IMAP 账号(没配置时
/ml mail会弹设置引导),之后/ml mail read只读「上次读完 → 现在」的新邮件:原文只进系统临时目录、用完即删(绝不碰 Vault),下载清理全程零模型调用,再用当前模型提取重要事件 / 待办 / 待阅(详见下文「/ml mail」专章)。
安装
dsh plugin --profile web add github:warmwine/dsh-memoryleak
# 重启 dsh web 生效
# 卸载:dsh plugin --profile web remove dsh-memoryleak
装好之后在输入框敲 /ml help 看全部命令。
记的东西长什么样
全部是普通 Markdown 文件,放在 Vault 根目录。每天的日志叫 2026-08-16.md,每周的叫 2026W33.md(用哪种在设置里选):
start: 2026-08-10 ← 周志模板自带的起止日期
end: 2026-08-16
## MemoryLeak ← /ml 记的流水账都在这里
- 上午重构了扫描器
- 下午修了候选卡错位
## Todo ← /ml todo add 存的待办在这里
- [ ] (ml:deadline 2026-09-01 urgent) 完成设计稿
- [ ] (ml:sleep 2026-12-01 low) 学一遍内部源码
- [ ] (ml:anytime medium) 整理收藏夹
- [x] (ml:active low done:2026-08-16) 复盘一次上线
待办有四种类型:
| 类型 | 日期 | 是什么 |
|---|---|---|
deadline | 必填 | 有截止日的事,到点就出现在列表里,可用 /ml todo p 延期 |
sleep | 必填 | 暂时不想看见的事,到唤醒日才自动出现(出现时会自动改写成 active) |
anytime | 不填 | 随便什么时候搞一下的事 |
active | 不填 | sleep 睡醒之后的样子,系统自动转换,不用手动写 |
优先级三档:urgent(紧急)、medium(中等)、low(低)。完成一件事时(/ml todo d)会自动在后面记上完成日期(done:2026-08-16),取消完成或撤销时自动去掉;放弃一件事时(/ml todo c)复选框变 [-] 并记上取消日期(cancelled:2026-08-18),再执行一次恢复原样。就算你手动把文件的格式改坏了,那一条也只是变回普通待办,不会丢。
/ml note:把对话沉淀成笔记
干完一段活,输入 /ml note 回车(不带任何参数),插件把整理任务交给当前会话的模型,由它在会话里直接完成整理。花 token 的命令有 /ml note、/ml ask 和 /ml mail read 三个,其余命令全部本地完成。
整理范围:只处理「上一次成功整理之后 → 现在」的对话;本会话第一次执行则整理全部上下文。所以一支会话可以多次执行,每次只消化新的一段——不重复、不越压越胖。只有模型成功调用写入工具的那次才算边界:模型没干活或写入被守卫拒绝的整理不消化它身前的对话,下次执行从更早的最后一次成功整理起算,重试不丢内容。
过程与普通对话完全同款:命令敲下后出现一条任务交接消息,之后就是一场普通回复——思考过程流式显示为可折叠思考块;模型先后调用 memory_note_context(拿存量登记与记录约定)和 memory_note_write(提交整理结果)两个真实工具,工具卡片在思考与输出过程中内联出现、转圈、翻到完成态(写入清单一目了然);最后的整理确认以原生 markdown 回复落定,进入对话上下文。模型忘了干活、或写入被守卫拒绝,你都看得见——它自己也会收到错误并重试。
产出四样东西(模型只负责压缩抽取,落盘格式全部由代码决定):
① 工作记录(当天/本周日志的 ## NOTE 段,每次整理一个 ### 小节,纯追加)
## NOTE
### 14:30 · 把 /ml note 命令从零做到能跑
- core 纯逻辑:转写裁剪、协议解析、渲染
- 宿主胶水:区间定位、llm 调用、落盘
② 知识文件(MOMENTO/ 目录)
MOMENTO/
├── index.md ← 知识索引(按文件名去重更新)
├── dsh-llm-stream-用法.md ← 一条长期知识一个文件;同名再写追加「## 更新 <日期>」
├── databases.md ← ③ 结构化登记:表格由代码渲染,按「名称」主键合并
├── servers.md ← 服务器:名称 / 主机 / IP / 登录用户 / 系统 / 备注
├── credentials.md ← 凭证:只登记「在哪、什么账号」,永不保存明文密码
└── glossary.md ← 术语表:术语 / 含义 / 备注
④ 汇总回执:整理范围(含消息数与是否裁剪)、写入文件清单、模型与 token 用量;条目不合规逐条告警而非整体失败。
零丢失写入:
- 压缩前先把 Vault 里的已有登记(databases/servers 等表格行、知识条目标题)喂给模型,要求增量增补——只输出新条目或有变化的字段(留空 = 保留原值),不会把本段对话当成全部上下文重建文件;
- 合并只认声明的格式;你手写的文件、格式对不上的内容永远不会被重写——内容对不上时只会在文件末尾追加一个带日期的小节;
- 表格列宽自动对齐(写时 lint):插件写回 markdown 文件时(
/ml记录、todo 增改与 d/c/p/u、/ml note落盘),自动把该文件里所有规范表格按每列最大宽度补空格对齐(中文按 2 列宽计,分隔线随列宽伸展、对齐冒号保留)。只补空白不动内容——单元格文字逐字保留;代码围栏内、列数不齐(手改坏)、缩进 ≥4 的表一律不碰; - 每次写入前有防丢失守卫(合并后条目必须包含合并前的全部主键,否则拒绝写入);备份默认关闭(git 兜底),可在 vault 配置
noteBackup: true开启(见下); - 字段白名单、条数与长度上限、
|与换行清洗——模型输出无法破坏文件结构。
两个使用注意:
/ml note后面不要跟文字——跟在后面的内容不会发给助手也不会被记录,命令会直接报错并提示正确用法;- 模型用的是当前会话正在用的那个;整理在会话里现场完成,整理过程占一轮对话上下文(换来的是模型之后记得整理过什么,追问不失忆)。
/ml ask:把 Vault 当资料库向 AI 提问
/ml note 是把对话写进 Vault,/ml ask 反过来:把 Vault 读出来当资料,用当前模型回答你的问题。只读,绝不写 Vault。
/ml ask 生产主库的端口是多少?
/ml ask 我之前记过哪些 redis 的坑?
- 资料自动汇集:模型调用
memory_ask_gather工具拿资料包——MOMENTO 索引 → 结构化登记(databases/servers 等,含noteStructured声明的自定义目标)→ MOMENTO 知识条目(按问题关键词挑最相关的在前)→ 近期日志(最近 3 份)。总量控制在约 8 万字符预算内,超出部分从队尾丢弃、长条目截断。 - 回答要求标注来源:模型被要求只依据资料回答、引用事实标注来源文件(如「MOMENTO/databases.md」)、笔记里没有的就直说没有、不编造。
- 过程与普通对话完全同款:命令后出现一条任务交接消息 → 模型原生思考、调工具拿资料、输出 markdown 回答(与普通回复同样的渲染)→ 回答自然进入对话上下文,可以接着追问。
- Vault 里还没有任何内容时会明确报错(先
/ml note或直接写文件)。
note / ask 是纯 Vault 内的两个花 token 命令;mail read 花的 token 只用于分析邮件,不写 Vault。
/ml mail:工作邮件增量阅读
把 IMAP 邮箱接进 /ml,专门解决「攒了一收件箱没看」的问题——只读新邮件、提取要点,邮件原文绝不落 Vault。
先设置(三选一):
- 没配置时执行
/ml mail(或/ml mail read),直接弹设置引导对话框:服务器 / 账号 / 密码三项(web 端是一张表单卡,密码遮蔽输入),填完真连一次 IMAP 试登陆,通过才保存; /ml mail setup随时重配(留空提交 = 沿用当前值,换服务器不用重输密码);- GUI 设置 → MemoryLeak 的「邮箱(/ml mail)」分区:登陆方式(密码/授权码 或 OAuth2 token)、服务器、端口(默认 993)、TLS、账号、密码、token、邮件目录(默认 INBOX)、单次上限(默认 50 封)。
QQ / 163 / 126 等邮箱要在网页版设置里开启 IMAP 并生成「授权码」,密码处填授权码而不是登陆密码。密码明文只存 ~/.dsh/settings.yaml(本机统一设置位置),双写同步会把它从 Vault 设置文件里剔除——凭证不进 Vault、不随目录迁移。配置好后裸 /ml mail 显示邮箱状态与上次读完时刻。
/ml mail read 增量阅读:
- 窗口:只处理「上次 read 结束 → 现在」的新邮件,首次默认当天 00:00 起。结束时刻记在 Vault 根
.memoryleak.yaml的mailState.lastReadEnd(vault 限定键,GUI 保存不会冲掉,随 Vault 迁移);进度由两段式工具保证——memory_mail_fetch只下载解析不推进,模型输出报告后才调memory_mail_commit推进(漏调 = 下次重读同一批,绝不漏邮件)。超单次上限保留最新、丢最旧的会如实告知。 - 两条铁律:① 邮件原文只下载到系统临时目录(一次性目录,用完即删;崩溃残留由下次执行清扫 24 小时以上的旧目录),绝不写进 Vault 或当前工作区;② 下载与清理全程零模型调用——窗口内没有新邮件时工具直接说明,模型只转告一句「没有新邮件」。
- 分析(花 token,与普通对话同款原生渲染):模型通读邮件后直接输出 markdown 阅读报告——总评 / 重要事件 / 待办(需要处理,带期限)/ 待阅(值得一看),思考与输出全程在会话里可见。
- 连接失败给排障提示:认证被拒 → 检查授权码;连不上 → 检查地址端口;TLS 失败 → 检查端口与开关匹配(993 开、143 关)。自建 Exchange 报
unable to verify the first certificate不是端口问题:握手是通的,是内部 CA/自签证书 Node 不信任(Windows/Outlook 走 Windows 证书库,Node 不读它)。此时 setup 引导会弹出**「信任并保存」一问**——确认后插件自动抓取服务器证书存入设置(不落地其他文件),之后的连接以它为信任锚继续严格校验(证书链 + 主机名);拒绝或取证失败,可在 GUI 设置勾选**「跳过证书校验」**兜底(不再验证服务器身份,连接仍加密),「信任的证书」文本框也可手改。维护者排障工具:scripts/probe-mail-tls.mjs(分层定位网络/端口/证书)、scripts/export-imap-ca.ps1(导出证书链 + Node 自检)。
适配你自己的老库格式(vault 限定配置)
不想用内置的 MOMENTO/databases.md 标准表格?你的老库是自定义表头、YAML 列表、甚至「markdown 小节 + 内嵌 YAML 块」?在 Vault 根的 .memoryleak.yaml 里加一段 noteStructured,声明每类知识写到哪个文件、什么格式——这几个 note 配置键只住在这个文件里(GUI 保存不会冲掉,手改即生效),不配的类别继续用内置默认。三种格式:
noteStructured:
# ① markdown 小节 + 内嵌 yaml 块(复杂手工库):### 标题按模板定位,
# 块按复合主键匹配,命中块内合并 / 节内追加块 / 末尾未分类章追加
databases:
file: momento/databases.md
format: sections
heading: "{host}:{port}" # ### 标题模板(存储字段占位符)
key: [host, database] # 复合主键(同机多库各一条)
fields: [host, port, database, user, notes]
aliases:
notes: note # 模型字段 → 老库字段名(改名映射)
extraFields: # 老库自有字段白名单(模型可填)
- key: environment
desc: production 或 test
- key: purposes
desc: 这台库上跑的功能(列表)
# 未声明的字段(如 password)模型永不填写、合并时原样保留
# ② 纯 YAML 对象列表
servers:
file: infra/servers.yaml
format: yaml
fields: [name, host, ip] # 内置字段的子集(省略 = 全集)
key: name # 单字段或数组主键
# ③ 自定义表头的 markdown 表格
glossary:
file: infra/terms.md
format: table
header: [术语, 含义, 备注] # 你的表头(与 fields 一一对应)
规则与安全线(三种格式通用):按主键合并(新值非空才覆盖,列表字段追加去重);未声明的字段原样保留(老库的 password/rack/owner 等自有键模型不碰);格式对不上(表头不匹配 / YAML 不是列表 / 块解析不了)时只追加、绝不重写;写回 markdown 时全文件表格列宽自动对齐,但只补空格、单元格文字逐字保留,代码围栏内与列数不齐的表不动;防丢失守卫(条目只增不减,违反拒绝写入)始终生效。aliases 让模型说内置字段名(name/user/notes),落盘自动映射到你库里的字段名(hostname/admin/note);extraFields 里声明的自有字段(environment/purposes…)会连同说明一起告诉模型,转写中出现才填。
备份:默认不备份——vault 用 git 管理时版本历史就是兜底,不再产生裸露的 .bak 文件。需要时在 vault 配置里加 noteBackup: true 开启:修改已有文件前自动备份进 .backup/ 隐藏目录(保留原相对路径 + 时间戳,多次备份不互相覆盖;该目录已在默认扫描排除列表里)。
还可以放一个 note skill(记录约定):noteSkill: MOMENTO/.note-skill.md 指向一个 markdown 文件,内容会注入给模型当本 vault 的约定(命名习惯、必须标注的字段……),格式仍由代码强制:
# 本 Vault 的记录约定
- 服务器一律用「机房-编号」命名(如 bj-01),主机只写内网 IP
- databases.notes 必须标注环境(prod/staging/dev)
- credentials 只写 1Password 条目名,不写路径
命令一览
完整说明输入 /ml help 随时看,这里列个速查:
| 命令 | 干什么 |
|---|---|
/ml init | 指定/更换 Vault 目录(唯一的设置入口) |
/ml <文本> | 记一笔到今天(或本周)的日志 |
/ml todo add <内容>(简写 n / a) | 加待办,弹表单选类型和优先级(表单支持键盘快捷键) |
/ml todo list(简写 l) | 列出待办,默认只看没完成的 |
/ml todo list all / open / done | 按状态过滤 |
/ml todo list <关键词> | 按关键词过滤 |
/ml todo d <序号>(简写 done) | 把列表里第几条标成完成(或取消完成) |
/ml todo c <序号>(简写 cancel) | 取消该待办:变 [-] 并记 cancelled:日期;再执行恢复,从默认列表隐藏 |
/ml todo p <序号> [天数](简写 postpone) | deadline 型延期:不填天数延 1 天;非 deadline 报错 |
/ml todo u(简写 undo) | 反悔最近一次 d / c / p,连按可以一路撤回去 |
/ml note | 用当前模型压缩区间对话进 MOMENTO/ 与日志 ## NOTE(花 token) |
/ml ask <问题> | 反向:拿 Vault 当资料库向当前模型提问(只读,花 token) |
/ml mail | 工作邮件:未配置弹设置引导(试登陆后保存),已配置显示状态 |
/ml mail read | 增量阅读新邮件 → 提取重要事件/待办/待阅(原文只进临时目录,花 token) |
/ml mail setup | 重新走邮箱配置引导(改密码/换服务器) |
/ml view(简写 v) | 看今天(或本周)的日志 |
/ml view <文件名几个字母> | 模糊找文件直接打开 |
/ml help(简写 h) | 看这份说明 |
Vault 未设置时,除 help / init 外的所有命令都会直接报错并提示先执行 /ml init——init 是唯一严格的目录设置入口,不会自动弹引导。
输入 /ml view 后继续打字,输入框上方会弹实时候选:当前日志排第一个,下面是匹配的文件。↑↓ 换选中的,Tab 把文件名补全到命令里,回车直接打开,Esc 关掉。
设置分两层,都是手改友好的 YAML:
- 全局层存在
~/.dsh/settings.yaml的memoryleak:段(DSH 官方统一位置,和其他插件同款)。GUI 设置面板 → MemoryLeak 分区改的就是它:Vault 目录(「浏览…」弹系统目录选择对话框,「清除」一键置空)、扫描哪些扩展名、排除哪些目录、数量上限、默认过滤、用日志还是周志、两个模板的内容,以及邮箱(/ml mail)的账号、密码/授权码、登陆方式等——mail 账号键只住这一层(vault 文件里写了无效,双写同步时剔除,凭证不进 Vault)。 - Vault 层是 Vault 根目录下的
.memoryleak.yaml。GUI 保存与/ml init都会双写——全局与这个文件同步为同一份,换台机器把整个 Vault 拷走、设置跟着走。读取时此文件里的键优先级更高(vault 路径与 mail 账号键除外——它们只认全局层);缺键回退全局层,全局层也没有就用默认值;文件写坏了也不崩,按缺失处理。手改这个文件仍可读,但下次 GUI 保存会被覆盖——例外是noteStructured/noteSkill/noteBackup这几个 vault 限定键与mailState(/ml mail 的读信进度):它们只住这一层、GUI 不展示,双写同步时原样保留(见上文「适配老库格式」与「/ml mail」专章)。
两件可能让你困惑的事
新建会话里命令没反应? DSH 的设计是:一个会话在发出第一条消息之前不挂聊天记录区,所以这时跑任何斜杠命令(包括官方的 /plan)结果都看不见,但命令其实执行了,文件也写了。随便发一条消息,之前的命令卡片就会补出来。
改了代码没生效? 浏览器部分(设置窗口、候选卡、命令卡片)刷新页面就行;核心逻辑(命令处理、扫描、文件读写)在服务端,要重启 dsh web。
开发
pnpm install
pnpm test # 469 个测试
代码分四层:src/core/ 是纯逻辑(含 core/note.js:转写裁剪 / 协议解析 / 落盘渲染;core/mail.js:读信窗口 / 邮件预算 / 分析协议解析 / 流式摘要),不碰文件系统,测试直接跑;src/adapters/ 负责真实的文件读写(测试用内存版替换);src/journal.js、src/note.js、src/mail.js 是宿主胶水(日志写入、区间定位、ctx.llm.stream 压缩调用、IMAP 下载与读信编排——imapflow + mailparser 两个运行时依赖,测试全部注入伪客户端/伪模型,不碰真实网络);src/index.js 和 src/client.js 分别是服务端和浏览器两端。
给 AI 留了接口但还没启用:新的待办格式只需要注册一个新的解析策略;renderTodoJson 输出稳定的 JSON,将来 AI 可以直接按这个格式读和筛待办。
License
MIT