Skip to content

Releases

v2.2.1

Choose a tag to compare

@github-actions github-actions released this 01 Nov 13:53

目录排除规则统一到全应用

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

Choose a tag to compare

@github-actions github-actions released this 01 Nov 12:25

定时/触发式 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-yaml chunk 不进入口静态闭包(仓库既有的 NFR-2 门禁)

v2.1.0

Choose a tag to compare

@github-actions github-actions released this 01 Nov 11:02

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 失败

文档

资产

平台 文件
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

Choose a tag to compare

@github-actions github-actions released this 01 Nov 09:19

MCP:目录排除机制

模板与附件目录不再进入 AI 的视野。

Obsidian 之类的库里常有 media/(附件)、templates/(模板),它们不是笔记,
但里面可能写着 - [ ] 某某订阅 这样的占位符——一旦被索引,AI 就会把它当成
你的真实待办,搜索结果里也会混进模板内容。

  • 新增目录排除配置,四个来源按优先级并集合并:
    内置默认 → 用户级 ~/.config/com.flymd/mcp.json → 库级 <库根>/.flymd/mcp.json
    → 环境变量 FLYMD_MCP_EXCLUDE → 命令行 --exclude
  • 内置默认排除技术目录与 media assets attachments templates 附件 模板 等资源目录
  • 匹配语义:不含 / 按目录名任意层级命中;含 / 按相对库根路径前缀命中;
    大小写不敏感;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 上传指向带后缀的副本

文档

资产

平台 文件
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

Choose a tag to compare

@github-actions github-actions released this 01 Nov 06:38

全新:MCP 服务器

  • 新增 flymd-mcp:独立 Rust crate,把本地笔记库以只读方式暴露给 Claude Code / Cursor / Codex 等 MCP 客户端
  • 6 个工具:list_notes read_note search get_outline list_tasks get_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 与 AUR flymd 的清单仍指向上一条发布线(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