Skip to content
dsh.fish
Bundle

dsh-memoryleak

Notes and todos as plain Markdown in one Vault folder of your choice: /ml jots into today's journal, todos come in deadline/sleep-until/anytime flavors with auto-wakeup, /ml note distills chats into a knowledge base, /ml ask answers questions from it, /ml mail reads your work inbox incrementally into a throwaway temp dir and extracts todos/reads, /ml view fuzzy-opens any file — notes stay plain files.

Source
warmwine
stars
1 stars
License
MIT
Updated
Updated yesterday

Readme

# dsh-memoryleak

> 好记性不如烂笔头,有了 memoryleak 你就不会 memoryleak。
> 一个把记事本带进 DSH 的插件:记的东西全是本地 Markdown 文件,git 和任何编辑器都能直接用,换个工具也带得走。

> [!WARNING]
> ⚠️ **隐私警示:谨慎使用本插件** ⚠️
>
> 任何 dsh 工作目录都可以把笔记写进同一个 Vault——这同样意味着**你的领导很容易通过蒸馏(distill)你的 Vault 摸清你的动态**。
>
> - **当前请谨慎使用本插件**,想清楚什么能记、什么不能记。
> - 后续会推出**「失联即焚」**功能。在此之前,**务必做好云备份,甚至不要把 Vault 放在公司电脑上**。
> - 在此之后,**更要做好备份,防止系统自燃**。

[更新历史](CHANGELOG.md)

## 你能用它干什么

你在写代码的时候,总有各种事想顺手记下来:这个 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」专章)。

## 安装

```bash
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`(用哪种在设置里选):

```markdown
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 回复落定,进入对话上下文。模型忘了干活、或写入被守卫拒绝,你都看得见——它自己也会收到错误并重试。

**产出四样东西**(模型只负责压缩抽取,落盘格式全部由代码决定):

```markdown
① 工作记录(当天/本周日志的 ## NOTE 段,每次整理一个 ### 小节,纯追加)

## NOTE
### 14:30 · 把 /ml note 命令从零做到能跑
- core 纯逻辑:转写裁剪、协议解析、渲染
- 宿主胶水:区间定位、llm 调用、落盘
```

```markdown
② 知识文件(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**。

```text
/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 阅读报告——**总评 + 重要事项(唯一的编号清单)**,所有重要的事务与信息都在同一份清单里:需要动手处理的、需要跟进的、值得知道的;需要处理的条目标注期限与来源,思考与输出全程在会话里可见。
- **`/ml mail todo <序号>`**:把最近一次 read 报告「重要事项」清单的第 <序号> 条转成 Vault 待办——走与 `/ml todo add` 完全相同的表单(类型 / 优先级),条目里明说期限的,选 deadline 时自动带入选中的期限(不用再填日期)。清单随 `memory_mail_commit` 存进 Vault(`mailState.lastItems`),跨会话、跨重启都可引用。
- 连接失败给排障提示:认证被拒 → 检查授权码;连不上 → 检查地址端口;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 保存不会冲掉,手改即生效),不配的类别继续用内置默认。三种格式:

```yaml
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 的约定(命名习惯、必须标注的字段……),格式仍由代码强制:

```markdown
# 本 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 todo <序号>`             | 把上次 read 报告「重要事项」第 <序号> 条转成待办(同 todo add 表单) |
| `/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`。

## 开发

```bash
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

Install

dsh plugin --profile web add github:warmwine/dsh-memoryleak

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source