Repository navigation
Releases
v2.2.1
目录排除规则统一到全应用
Flymd 有一套目录排除规则,用来把模板 / 附件目录挡在 AI 的视野之外。
但它此前只管住了 MCP 一条通道——其余三条 AI 通道各走各的:
| AI 通道 | 此前 |
|---|---|
| MCP 客户端 | ✅ 完整生效 |
定时任务(YAML recipes 的 {{files}}) |
❌ 全树遍历,无过滤 |
| AI 助手的知识库(flymd-RAG) | ❌ 插件自己的 excludeDirs,默认空 |
| 插件的库文件枚举 | ❌ 无全局规则 |
于是同一个模板目录,在 MCP 里 AI 看不见,在定时任务与知识库里却能出现。
边界只是「看起来有」,实际并不成立。
现在一套规则,全应用生效。
规则仍然只有一份实现
实现在 Rust 的 flymd_mcp::config::McpConfig 里。桌面应用本来就以路径依赖引入了
mcp-server 的 lib 目标(当初为共享 AI 写入护栏核心),所以把已有能力暴露给应用即可:
- 不复刻第二份匹配逻辑——不存在「两边规则慢慢漂移」的问题
- 不给应用新增任何依赖
- 判定放在 Rust 侧,一次 IPC 判一批,插件与宿主零规则副本
具体落点:
flymd_list_markdown_files改为按规则剪枝遍历——被排除的目录整棵不进结果- 新增两个命令:
flymd_ai_scope(取生效规则)、flymd_ai_scope_filter(批量判定) - 插件侧新增
context.getAiScope()/context.filterAiExcludedPaths()
配置文件改名
<库根>/.flymd/mcp.json → <库根>/.flymd/ai-scope.json(它现在管的不只是 MCP)。
旧名继续生效——两个文件都读、都合并,用户已有的配置不会因为改名而失效。
AI 助手的知识库自动继承
flymd-RAG 的批量路径走宿主的 listLibraryFiles,宿主已经过滤过,零改动继承。
它的增量监听路径绕过 listLibraryFiles(走文件 watcher),单独补了判定。
如果某个模板目录此前已被索引进知识库,需要重建一次索引才会清掉。
插件 API
新增库内受限读写
context.readLibraryFile() / context.writeLibraryFile():
库根约束 + 排除规则约束的库内文件读写,写入自动留 AI 写入护栏快照,可回滚。
修复:AI 助手的「TODO 便签」
此前它调用的 read_text_file_any / write_text_file_any 在插件 denylist 里
(denylist 落地在那次调用之后 1206 个提交,加 denylist 时没扫到这处调用),
所以这个功能一直是坏的,用户看到的是「读取 TODO 文件失败」。
现在改走库内受限读写。
没有给 denylist 开特例——那两个命令接受任意绝对路径,放进 denylist 是对的。
新 API 是「受限入口」,不是「放开原有入口」。
补记文档
context.aiGuardList() / context.aiGuardRollbackLast() 此前没有写进插件文档,现已补上。
安全边界
- 库根由宿主决定,不接受插件传入(与
ai_guard_*同一条规矩) - 路径必须是库内相对路径:绝对路径、前导斜杠、
..越界一律拒绝
(前导斜杠也拒绝——约定与 MCP 的resolve_write_path一致) - 落在被排除目录里的路径拒绝读写:AI 不该看见的地方,插件也不该碰
- 判定失败时拒绝放行——宁可一次合法写入被拒,也不能在规则失效时放行
版本号约定调整
此前一天之内连发 v2.0.0 → v2.0.1 → v2.1.0 → v2.2.0,minor 位在六小时内从 0 走到 2。
这样 minor 号承载不了「这次带来了什么」的信息。
从 v2.2.1 起改为:
- 第三位(patch) 承载日常增量:功能补齐、一致性修复、bug 修复、文档
- 第二位(minor) 留给成规模的版本
- 第一位(major) 留给破坏性变更
MCP 服务器同理:0.3.0 → 0.3.1。
验证
- 新增 5 个 Rust 单测(
src-tauri)+ 3 个 MCP 单测 + 12 个 TS 单测 - MCP 服务器 60 个单测、桌面应用侧 27+15 个单测、前端 1141 个测试全过
- typecheck 与构建门禁通过,mcp-server clippy 零警告
- 真实库实测:AI 视角的待办里,模板目录下的 30 条假待办全部不可见
- 新旧配置文件名均实测生效
v2.2.0
定时/触发式 AI 任务(YAML Recipes)
AI 此前只在你守在编辑器前时才工作:打开 AI 助手、选文档、下指令、审阅草稿、手动插入。
所有加工动作都要人在环。现在你可以写一条 YAML 配方,让 Flymd 按 cron 或保存事件自动加工笔记。
配方放在 <库根>/.flymd/recipes/*.yaml,随库走、能被 WebDAV 同步。
三十秒上手
id: daily-report
name: 每日汇总
trigger:
type: schedule
cron: "0 9 * * *"
prompt: |
汇总 {{date}} 的日记要点。
output:
mode: note
path: "AI/日报.md"每天 09:00 自动跑一次,结果追加到 AI/日报.md。
两种触发器
| 触发器 | 说明 |
|---|---|
schedule |
标准 5 段 cron(分 时 日 月 周),支持 * / 列表 / 范围 / 步长,以及 @daily @hourly @weekly @monthly @yearly 宏 |
on-save |
保存文件时触发,paths 用 glob 过滤(*.md / 日记/*.md / **/*.md),并有防抖窗口 |
cron 的日与周同时写死时按惯例取或:0 9 1 * 1 = 每月 1 号或每周一。
提示词变量
{{date}} {{time}} {{datetime}} {{weekday}} {{library}} {{file}} {{content}} {{todos}} {{files}}
{{todos}}只取未完成的待办(- [ ] xxx){{files}}是全库文件清单,会触发一次全库扫描——只在真的引用时才付这个代价- 变量名拼错不报错,但会替换成空串并记进运行日志(不会静默让你以为"AI 没读到数据")
三种输出
output.mode |
行为 |
|---|---|
none |
不落盘,只在运行日志留摘要(最安全,适合先试) |
note |
追加到 output.path,已存在就往下追加并加分隔标题 |
replace |
覆盖 output.path |
写回照样能回滚
所有落盘都先过 AI 写入护栏:写入前的原内容先快照到 <库根>/.flymd/ai-guard/。
- 快照失败(内容超 1 MB 等)会中止写入,不会留下写了一半的文件
- 结果可用 MCP 的
rollback_write,或 AI 助手的「回滚上一次 AI 写入」菜单撤掉 - 定时任务的写入与 MCP 客户端的写入落在同一个快照目录,互相可回滚
AI 配置不用重复填
优先级:recipe 内 ai: 段 > AI 助手插件的 ai.config > 内置免费模式。
也就是说你在「插件 → AI 助手」里配好的 baseUrl / apiKey / model,recipes 直接复用——
在 AI 助手里改一次 key,所有配方立刻跟着生效。没配过就走免费模式。
运行留痕
每次执行往 <库根>/.flymd/recipes/runs.jsonl 追加一行:成功与否、耗时、写到哪个文件、
对应的 snapshotId、AI 输出摘要。
「昨天那次定时任务到底跑了没、写了什么、能不能回滚」——一眼可查。
runs.jsonl 是设备本地的,不参与 WebDAV 同步;配方本身(*.yaml)跟着库走。
为什么放在核心而不是做成插件
三样能力插件都拿不到:
- on-save 触发源:插件 API 没有事件订阅机制,没有任何
onSave/subscribe钩子 - 全库扫描:
flymd_list_markdown_files/flymd_search_files_content在插件 denylist 里(防插件扫全库) - 护栏写入:
ai_guard_snapshot等四个命令同样在 denylist,插件只能走宿主提供的可信入口
走插件路线要先给宿主补两套新 API(事件总线 + 库结构化访问)、再改 76KB 的插件文档,
而这三样恰好都是核心层天然拥有的。所以定为核心模块 src/core/recipes/。
其它
- 配方写错不会静默失效:控制台会打出是哪个文件、哪个字段、期望什么,其余配方照常运行
output.path必须是库内相对路径,绝对路径与..越界一律拒绝- WebDAV 默认排除
.flymd/recipes/runs.jsonl(设备本地痕迹),但配方本身要同步 - 新增 104 个单测(含用真实文件系统的目录读写测试,以及出厂示例的解析校验)
- 引擎按需动态加载——
js-yamlchunk 不进入口静态闭包(仓库既有的 NFR-2 门禁)
v2.1.0
MCP:AI 写入护栏与回滚
MCP 从只读迈出第一步:现在可以写入你的笔记,但每一次写入都留痕、可预览、可回滚。
之前 MCP 只能读,是为了先把只读能力做扎实。现在只读已经稳定,该补上护栏了——
否则「AI 把笔记写坏了且无法回滚」这个风险会直接暴露给用户,而竞品
(SoloMD / Markra)都已经上了各自的护栏。
四个写入工具
| 工具 | 作用 | 写盘 |
|---|---|---|
preview_write |
给定路径与新内容,返回 unified diff | 否 |
write_note |
快照 → 写入 → 返回 diff 与快照 ID | 是 |
list_writes |
列出本库可回滚的写入 | 否 |
rollback_write |
回滚到指定快照 | 是 |
写入的硬约束
- 写入前强制快照:原内容存进
<库根>/.flymd/ai-guard/。
快照写不成功就中止写入——宁可写不进去,也不能留下没有回滚路径的改动 - 回滚可以再回滚:回滚本身也生成快照,来回切不会丢历史
- 单次写入上限 1 MB,一次只改一个文件
- 不得写入被排除的目录(排除目录是你划定的边界)
- 目标路径必须落在库根内,绝对路径、
..、指向库外的符号链接一律拒绝
护栏内核只有一份实现
快照存储与 diff 实现在 mcp-server 的 lib 目标里,桌面应用以路径依赖引用同一份代码——
不是两边各写一遍再对齐约定。这样:
- 快照格式的一致性由构造保证
- 应用内的 AI 写入与 MCP 客户端的写入落在同一个快照目录,互相可回滚
- 不给桌面应用新增任何依赖(lib 只依赖 serde / serde_json,两者本就有)
应用内:插件改写文档自动留快照
- 宿主层统一拦截插件的三个文档写入口(
setEditorValue/replaceRange/insertAtCursor),
一处覆盖所有插件 - AI 助手新增右键菜单项 「回滚上一次 AI 写入」
- 在应用里能回滚 MCP 的写入,在 MCP 里也能回滚应用的写入
安全加固
- 库根不可由插件指定:
ai_guard_*命令对插件禁用,改由宿主提供库根可信的入口。
否则恶意插件可传libraryRoot="/"让回滚写到库外任意路径 - WebDAV 同步默认排除
.flymd/ai-guard/:快照存的是写入前的旧内容,
同步出去等于泄露历史版本
修复
- CI:推主干此前不触发任何 CI(workflow 的 on.push 只配了 tags),
补上branches: [main] - CI:Tauri 侧 Rust 代码此前只在三平台 build job 里才编译(30 分钟后才知道编不过),
check 门禁新增cargo check --manifest-path src-tauri/Cargo.toml - CI:check job 补装 GTK/WebKit 开发包,让 cargo check 能跑起来
- 单测:
themeBgUnify放宽文件级超时——它的首个用例要 1.5s,贴着 vitest 默认 5s 上限,
高负载下随机失败挡住发版门禁 - 主题:专注模式侧栏背景的 50ms 延迟回调在环境销毁后触发会抛未捕获的
ReferenceError: window is not defined——单测断言全过却因此以退出码 1 失败
文档
- AI 写入护栏设计文档
- MCP 服务器使用指南 第 8 节
资产
| 平台 | 文件 |
|---|---|
| Windows x64 | flymd_2.1.0_x64-setup.exe、flymd_portable_x64.zip |
| macOS (Intel + Apple Silicon) | flymd_2.1.0_universal.dmg、flymd_universal.app.zip |
| Linux x64 | flymd_2.1.0_amd64.deb、flymd_2.1.0_amd64.AppImage |
| MCP 服务器(独立二进制) | flymd-mcp-windows-x64.exe、flymd-mcp-macos-universal、flymd-mcp-linux-x64 |
MCP 服务器是独立进程,不依赖桌面应用运行。
用 Claude Code 时:claude mcp add flymd -- /绝对路径/flymd-mcp
v2.0.1
MCP:目录排除机制
模板与附件目录不再进入 AI 的视野。
Obsidian 之类的库里常有 media/(附件)、templates/(模板),它们不是笔记,
但里面可能写着 - [ ] 某某订阅 这样的占位符——一旦被索引,AI 就会把它当成
你的真实待办,搜索结果里也会混进模板内容。
- 新增目录排除配置,四个来源按优先级并集合并:
内置默认 → 用户级~/.config/com.flymd/mcp.json→ 库级<库根>/.flymd/mcp.json
→ 环境变量FLYMD_MCP_EXCLUDE→ 命令行--exclude - 内置默认排除技术目录与
mediaassetsattachmentstemplates附件模板等资源目录 - 匹配语义:不含
/按目录名任意层级命中;含/按相对库根路径前缀命中;
大小写不敏感;allowDirs优先 - 排除是全屏障:被排除目录整棵不进索引,
read_note读到被排除路径直接报错
(否则 AI 猜到路径仍能读到,排除形同虚设) - 新增
--exclude <目录>(可重复)与--print-config(打印生效配置 + 实际被排除的目录 + 笔记总数) - 服务器启动时往 stderr 打印被排除的目录——排除不是黑箱
flymd-mcp --exclude 我的草稿 --exclude 工作/归档
flymd-mcp --print-config// <库根>/.flymd/mcp.json
{
"excludeDirs": ["我的草稿", "工作/归档"],
"allowDirs": ["media/重要笔记"]
}效果:真实库实测,索引笔记从 1134 篇降到 1062 篇,
media/.obsidian/.flymd/
textgenerator/templates/被排除;模板里的假待办不再出现在list_tasks的结果中。
修复
- CI:Linux 与 macOS 的 MCP 产物默认都叫
flymd-mcp,而 GitHub Release 不允许同名资产
——构建脚本改为按平台带后缀(flymd-mcp-linux-x64/flymd-mcp-macos-universal/
flymd-mcp-windows-x64.exe),CI 上传指向带后缀的副本
文档
- MCP 服务器使用指南:
新增「排除目录」一节,含配置格式、匹配规则与--print-config自查方法 - MCP 服务器开发文档
资产
| 平台 | 文件 |
|---|---|
| Windows x64 | flymd_2.0.1_x64-setup.exe、flymd_portable_x64.zip |
| macOS (Intel + Apple Silicon) | flymd_2.0.1_universal.dmg、flymd_universal.app.zip |
| Linux x64 | flymd_2.0.1_amd64.deb、flymd_2.0.1_amd64.AppImage |
| MCP 服务器(独立二进制) | flymd-mcp-windows-x64.exe、flymd-mcp-macos-universal、flymd-mcp-linux-x64 |
MCP 服务器是独立进程,不依赖桌面应用运行。用 Claude Code 时:
claude mcp add flymd -- /绝对路径/flymd-mcp
v2.0.0
全新:MCP 服务器
- 新增
flymd-mcp:独立 Rust crate,把本地笔记库以只读方式暴露给 Claude Code / Cursor / Codex 等 MCP 客户端 - 6 个工具:
list_notesread_notesearchget_outlinelist_tasksget_backlinks - 库根自动定位(读取应用设置中的当前激活库),也可用
--library或FLYMD_LIBRARY指定 - 安全边界:拒绝绝对路径、
..与指向库外的符号链接;单篇读取上限 4 MB,输出上限 12 万字符 - Release 提供各平台独立二进制,并随应用包一并分发 → 使用指南
库私有化 v2
- 插件数据改写入
<库根>/.flymd/local.json,库目录搬走时数据跟随 - 新增插件 library-scoped 存储 API
context.storage.scoped.{get,set,remove} - Rust 侧原子写 + 跨进程文件锁基础设施
- WebDAV 同步默认排除
.flymd/local.json;存量数据自动迁移
架构与性能
main.ts拆分:新增src/core/63 个模块;样式体系重构为src/styles/29 个文件 +scripts/style-parity一致性校验- 大文件性能:视口渲染 + CM6 流畅编辑、文件树整体懒加载、预览渲染管线抽取与快照锁定
编辑体验
- 所见模式支持 GFM 任务列表复选框渲染与点击切换;渲染
sub/sup/abbr等 HTML 内联标签 - 复杂块编辑:发现性增强 + 块级↔行内互转 + 内嵌错误条;HTML 源码编辑加固(设置开关、脏检查、锁竞态修复)
- 代码块:全局自动换行开关、行号列动态宽度与配色、全高缩放切换、
Ctrl+Home/End块内跳转 - 全文检索支持多关键词(空格=AND、引号=短语);callout 整个标题栏均可点击折叠
界面
- 内容区底部常驻状态栏:路径 / 消息槽 / 行·列·字数 / 未保存 / 缩放%
- 左下角当前文件路径徽章;「彩色库树」开关;库树连接线按层级切 alpha
- 主题设置:编辑与阅读背景合并为单一设置,锚点统一到
readBg
可靠性
- 打开文件的外部更改监听接入,新增文本对比视图(diff v2):初始定位、同步滚动、行内复制、忽略空白
- 删除命令增加后端路径校验(纵深防御)
- 修复 Linux(Wayland) 下窗口完全无法调整大小;修复 macOS 退出与窗口调整死锁
Web 端与 AI 网关
- 新增
web/:Fastify 5 服务端 + Vue 3 客户端 - 服务端 RAG:5 阶段管线 + SSRF 防护 + RAG 闭环;AI 网关多 provider 实例与多通道告警通知
其他
- 许可证声明对齐为 GPL-3.0 全文(修正
package.json中与 README「允许商业用途」自述冲突的 NC 自定义标识,GitHub 现可正确识别) - 仓库地址切换至
htmambo/flymd作为发布主线
⚠️ 升级注意
- winget
flyhunterl.FlyMD与 AURflymd的清单仍指向上一条发布线(v1.4.4),在清单更新前请从本 Release 直接下载安装包 - 旧版本(v1.4.x)不会收到自动升级提示——更新检查已指向新仓库
产物清单
| 平台 | 文件 |
|---|---|
| Windows x64 | flymd_2.0.0_x64-setup.exe / flymd_portable_x64.zip |
| macOS universal | flymd_2.0.0_universal.dmg / flymd_universal.app.zip |
| Linux x64 | flymd_2.0.0_amd64.deb / flymd_2.0.0_amd64.AppImage |
| MCP 服务器 | flymd-mcp-windows-x64.exe / flymd-mcp-macos-universal / flymd-mcp-linux-x64 |
完整变更清单见 ROADMAP.md