Foam 0.46 新特性深度解析:Smart Folders、Foam CLI、图谱重构与 Query 体系

原创2026-10-10 02:52:441,363 阅读
文章标签:知识管理知识库开发工具MCP 服务

Foam 0.46 新特性深度解析:Smart Folders、Foam CLI、图谱重构与 Query 体系

导读

Foam 是一个运行于 VS Code 的个人知识管理(PKM)系统,通过 Wikilinks、Tag、双链图谱把零散的 Markdown 笔记编织成可检索、可导航的知识库。本文以仓库内 packages/foam-vscode/WHATS_NEW.md(更新日志)为主线,逐项拆解 Foam 近期版本引入的核心能力:用查询驱动的 Smart Folders(智能文件夹)、无需打开 VS Code 即可操作工作区的 Foam CLI、支持节点分组与命名视图的 图谱重构、可嵌入笔记的 Block 引用、以及贯穿编辑器/CLI/MCP 三端的 Foam Query 查询体系。读完本文,你将掌握这些功能的配置语法、使用流程与其底层实现原理,能够在自己的笔记工作区中直接落地。


一、Smart Folders:用查询驱动侧边栏视图

1.1 什么是 Smart Folders

Smart Folders 是 Foam 把“已保存的查询”呈现在 VS Code 侧边栏的方式。每个 Smart Folder 就是一个存放在 .foam/queries/<id>.yaml 的查询文件——它不是磁盘上的真实目录,而是一个动态视图:filter 命中哪些笔记,面板里就显示哪些笔记。官方文档把它定位为“在大型工作区中导航,而不必被僵化的标签层级锁死”的工具(见 docs/user/features/smart-folders.md)。

其价值在于:当你想查看“所有标记为 #wip 且未归档的笔记”时,不需要为每类场景新建文件夹,只需一个查询文件即可随时在侧边栏生成视图。

1.2 查询语法与配置示例

Smart Folders 的过滤语法与 foam-query 代码块完全一致。WHATS_NEW.md 给出的标准示例是创建一个 “Work in Progress” 智能文件夹:

# .foam/queries/work-in-progress.yaml
name: Work in Progress
filter:
  and:
    - tag: '#wip'
    - not:
        tag: '#archive'
sort: title ASC

将其中的要点展开:

  • name(可选):面板中显示的文件夹名,缺省时由文件名人性化生成;
  • filter:笔记筛选条件,支持 and / or / not 组合结构化过滤器;
  • sort:排序规则,如 title ASC、backlink-count DESC;
  • 此外还可选 description、limit 等字段,最小的合法文件只需一行 filter: "#wip"。

1.3 创建、编辑与删除的完整流程

在侧边栏 Smart Folders 面板点击 Create Smart Folder,或执行命令 Foam: Create Smart Folder,向导会依次询问:

  1. 文件夹名称(如 "Work in Progress");
  2. 需要包含的标签(多选;按 Esc 跳过,直接手动编辑 YAML)。

Foam 随即在 .foam/queries/<name>.yaml 创建文件并自动打开编辑。编辑时点击面板中的铅笔图标或直接改 YAML 文件,变更会被自动监听并刷新;删除时点击垃圾桶图标并确认即可(详见 docs/user/features/smart-folders.md)。

1.4 源码实现:从 YAML 文件到树形面板

Smart Folders 功能在仓库中由三个模块协作完成(目录 packages/foam-vscode/src/vscode/features/smart-folders):

  • smart-folder-storage.ts:VS Code 侧对核心 QueryStore 的适配层。它用 vscode.workspace.createFileSystemWatcher 监听 .foam/queries/ 目录的增删改,并在内存中缓存 LoadedQuery,使面板刷新时无需重新读取全部 YAML。
  • smart-folders-explorer.ts:树形视图提供器。每个 Smart Folder 对应一个 SmartFolderTreeItem,展开时调用 executeQuery 执行查询并列出命中的笔记 URI;若 YAML 存在解析错误,则显示 SmartFolderErrorTreeItem(带 warning 图标,点击可直接打开查询文件)。值得注意的细节是,查询结果通过 buildFolderTree 按工作区相对路径组装成文件夹树,并支持“按文件夹分组 / 平铺列表”两种展示模式(groupBy 状态持久化在 ContextMemento 中)。
  • create-smart-folder.ts:创建向导。通过 Quick Pick 选取现有工作区标签,用 buildOrFilter 把多个标签组装为 or 过滤器;工作区无标签时则回退为 * 过滤器,交由用户后续手动编辑 YAML 细化。
  • index.ts:功能激活入口,注册 create / edit / delete / refresh 等命令,并监听 foam.graph.onDidUpdate 与工作区信任状态变更自动刷新面板。

一个关键约束:Smart Folder 查询可能包含 Jexl 表达式,因此只有当工作区处于**可信(trusted)**状态时才会执行(isTrusted: () => vscode.workspace.isTrusted),与 foam-query 块共用同一信任门控,默认失败安全(fail-safe)。

1.5 面板视图选项

在面板标题栏可以:

  • Group By Folder / Flat list:按工作区路径分组显示命中笔记,或平铺成扁平列表;
  • Refresh:手动重新执行所有查询。

二、Foam CLI:在终端里操作工作区

2.1 安装与快速上手

WHATS_NEW.md 指出,Foam 现在随附命令行界面(CLI),让你无需打开 VS Code 就能与工作区交互。两种使用方式:

# 免安装直接运行
npx foam-cli <command> [options]

# 或全局安装
npm install -g foam-cli
foam <command> [options]

快速上手(来源 packages/foam-cli/README.md):

cd /path/to/your/notes
foam            # 查看可用命令
foam list notes # 列出所有笔记
foam lint       # 运行工作区检查

2.2 工作区解析与全局选项

工作区根目录按以下顺序解析:

  1. --workspace <dir> 参数;
  2. FOAM_WORKSPACE 环境变量;
  3. 当前工作目录。

全局选项包括 --format text|json、--color / --no-color、--help、--version。所有命令都支持 --format json,便于脚本化消费;JSON 输出会自动禁用颜色以保证机器可读(实现见 packages/foam-cli/src/index.ts)。

从 packages/foam-cli/src/index.ts 的帮助文本可以看到,CLI 命令覆盖了 lint、list、note、outline、links、graph、daily、tag、grep、search、rename、query、mcp、update、config 等操作,其中 query 子命令正是为 Smart Folders 设计的(见下文)。

2.3 用 foam query 管理 Smart Folders

WHATS_NEW.md 明确说明:“Queries used for Smart Folders are also exposed in the CLI and via MCP tools.” 在 CLI 中,foam query 提供三个子命令(实现见 packages/foam-cli/src/commands/query.ts):

foam query <subcommand> [options]

Subcommands:
  list                  List all saved queries
  run <id>              Run a saved query and print matching notes
  show <id>             Print the YAML body of a saved query

Common options:
  --workspace <dir>     Workspace root (default: FOAM_WORKSPACE env var, then cwd)
  --format <fmt>        Output: text (default) or json

示例:

foam query list                       # 列出所有已保存查询(含命中数量)
foam query run work-in-progress       # 执行查询并打印匹配笔记的路径
foam query show work-in-progress      # 打印该查询的 YAML 内容

实现细节上,list 会对每个查询调用 countMatches 统计当前命中数;run 通过 executeQuery 执行查询并输出匹配笔记的工作区相对路径;两者均支持 --format json 结构化输出。此外 CLI 还提供 foam update 检查并提示更新命令(npm install -g foam-cli@latest,见 packages/foam-cli/src/commands/update.ts)。

2.4 MCP 工具:让 AI Agent 也能执行查询

同一批查询通过 Model Context Protocol 暴露给 AI 客户端。在 packages/foam-mcp/src/tools/queries.ts 中注册了三个工具:

  • list_queries:列出工作区内所有已保存查询(.foam/queries/ 下的 YAML),含 id、name、description、matchCount、errors;
  • get_query:按 id 返回某个查询的描述符(filter、sort、limit 等)与元数据;
  • run_query:执行查询并返回匹配资源——可以传 id 运行已保存查询,或传 descriptor 运行临时查询(两者必须恰好提供一个)。

这说明 Smart Folders 的查询语法是三端共享的:VS Code 面板、CLI、MCP 工具最终都调用 @foam/core 中的 executeQuery(详见后文)。


三、图谱重构:节点分组与命名视图

3.1 从 “Show Graph” 到交互式知识网络

WHATS_NEW.md 总结了图谱视图的三大改进:更优的 UX 重设计、节点分组(Node Groups)、新增命名视图(Graph Views)。图谱将笔记与标签呈现为节点,笔记间的链接与文件-标签关系呈现为边,节点随连接数增大而放大(见 docs/user/features/graph-view.md)。

打开方式:命令面板(Ctrl+Shift+P / Cmd+Shift+P)→ “Foam: Show Graph” → 回车。默认点击节点会在编辑器中打开源文件;若希望打开 Markdown 预览,可配置:

"foam.graph.navigateToPreview": true

3.2 节点分组(Groups)的匹配规则

图谱右上角的 Groups 面板控制节点可见性与配色,由三部分组成:

  • Color by:默认配色策略——None(各节点类型独立配色)、Type(按 frontmatter type 属性)、Directory(按所在目录);
  • Built-in types:tag、attachment、image、placeholder 四类节点的复选框与色点,取消勾选可隐藏,点击色点可改色;
  • Custom groups:自定义分组规则,按属性匹配笔记子集并赋色。

自定义分组支持的匹配属性(详见 docs/user/features/graph-view.md):

属性 匹配方式 示例
type 精确匹配笔记类型 project
path 文件路径子串匹配 journal
tag 精确匹配标签 daily
title 标题子串匹配 任意标题片段
任意 frontmatter 键 精确匹配自定义属性 任意属性值

/regex/ 语法可用于模式匹配,例如 /^2024/ 匹配以 2024 开头的路径。分组叠加在默认配色之上,最后一个匹配的分组获胜;取消勾选某分组会隐藏仅属于该分组的笔记。

源码层,packages/foam-graph/src/lib/groups.ts 中的 matchesGroup 实现了上述匹配逻辑——若 match.value 以 /…/ 包裹则编译为正则(编译失败回退为精确匹配),resolveGroupColor 遍历启用的分组、后匹配者覆盖先匹配者,与文档所述“last match wins”完全一致。

3.3 命名视图:把图谱配置存进 settings.json

在 foam.graph.views 中预定义视图,名为 "Default" 的视图会在图谱打开时自动应用。完整示例:

"foam.graph.views": [
  {
    "name": "Default",
    "colorBy": "directory",
    "show": {
      "tag": { "enabled": false },
      "placeholder": { "enabled": false }
    }
  },
  {
    "name": "Journal",
    "colorBy": "directory",
    "show": {
      "tag": { "enabled": false },
      "placeholder": { "enabled": false }
    },
    "groups": [
      {
        "id": "journal",
        "label": "path=journal",
        "color": "#6bcb77",
        "enabled": true,
        "match": { "property": "path", "value": "journal" }
      }
    ]
  }
]

视图配置字段一览(均可选):

字段 说明
name 面板标题显示名;用 "Default" 可在打开时自动应用
colorBy "none" / "directory" / "type"
groups 自定义分组规则数组
show 内置类型配置,如 { "tag": { "enabled": true, "color": "#ff0000" } }
background / fontSize / fontFamily / lineColor 背景、字号、字体、连线颜色覆盖

可通过 keybindings.json 为命名视图绑定快捷键:

{
  "key": "ctrl+shift+j",
  "command": "foam-vscode.show-graph",
  "args": { "view": "Journal" }
}

也可以不定义命名视图,直接内联传入配置(args.config)。当同时提供 view 与 config 时,config 会合并覆盖在命名视图之上——这套解析逻辑实现在 packages/foam-vscode/src/vscode/features/graph-webview/index.ts 的 resolveViewStyle / mergeStyles / viewConfigToStyle 中。

遗留配置提示:foam.graph.style 已标记为废弃(deprecated),建议改用 foam.graph.views 并定义一个 "Default" 视图来替代旧样式;但旧配置仍会作为最底层样式生效(见 docs/user/features/graph-view.md)。


四、Block 引用:链接到笔记内的具体区块

4.1 锚点语法

WHATS_NEW.md 提醒:Foam 已在编辑器与预览中完整支持区块的引用与嵌入(Block references)。Block anchors 允许你链接到笔记中的某个段落、列表项、标题或引用块,而不只是整篇笔记或章节标题(详见 docs/user/features/block-anchors.md)。

在任意块元素末尾放置 ^your-id(ID 可含字母、数字与连字符),^id 标记在预览中隐藏,属于元数据而非可见文本:

This is an important finding from the experiment. ^key-finding

## Methodology ^methodology

- Mix dry ingredients thoroughly ^dry-step
  - 2 cups flour
  - 1 tsp salt

多行段落把锚点放在最后一行末尾;列表项的锚点作用于整个条目(含子项);要锚定整个列表,可在最后一项之后紧接着写 ^shopping-list(不留空行);代码块与表格则把 ^id 写在闭合围栏/表格之后的自成一行(允许一个空行,兼容格式化器自动插入空行的情况)。

4.2 链接、嵌入与重命名

  • 链接:[[research-notes#^key-insight]] 直接跳到区块;在 wikilink 内输入 #^ 会获得块 ID 自动补全;支持显示文本 [[research-notes#^key-insight|See the key insight]]。
  • 嵌入:![[research-notes#^key-insight]] 仅内联展示被引用区块的内容,而非整篇笔记。
  • 重命名:光标放在 ^blockid 锚点上按 F2,Foam 会同步更新锚点及工作区内所有引用它的 wikilink(对应重命名提供器见 packages/foam-vscode/src/vscode/features/editing/block-rename-provider.ts)。
  • 诊断:当块链接指向目标笔记中不存在的 ^id 时,Foam 给出警告并提供快速修复(从可用块 ID 中选择);同一文件内重复使用相同 ^id 也会被标记,快速修复会替换为全新生成的唯一 ID。

五、Foam Queries:在预览中嵌入动态笔记列表

5.1 基本用法

WHATS_NEW.md 展示了 foam-query 代码块——直接在 Markdown 预览中嵌入动态、自动更新的笔记列表:

```foam-query
filter: "#project"
sort: title ASC
select: [title, path]
```

渲染结果是一个指向匹配笔记的可点击链接列表,且会随着笔记变化自动更新。查询会渲染在 Markdown 预览中(而非编辑器内),结果可点击跳回对应笔记。

5.2 查询选项与过滤器全解

查询选项包括:

  • filter:选择包含哪些笔记;
  • select:选择展示哪些字段,默认 title 与 path;
  • sort:排序,如 title ASC、backlink-count DESC(排序字段需同时出现在 select 中,否则投影后不可用);
  • limit / offset:只显示前 n 条 / 跳过前 n 条;
  • format:渲染为 list、table 或 count。

过滤器分为简写与结构化两类(完整语法见 docs/user/features/foam-queries.md):

简写过滤器:

  • "#tag":含有该标签的笔记;
  • "[[note-id]]":链接到或自该笔记的笔记(使用与 wikilink 相同的标识符);
  • "/regex/":路径匹配正则的笔记;
  • "*":全部笔记。

结构化过滤器(YAML):

```foam-query
filter:
  and:
    - tag: "#research"
    - not:
        path: "^/archive/"
select: [title, tags, backlink-count]
sort: title ASC
```

支持的过滤器键(可组合 and / or / not):

键 含义
tag 含有该标签(如 tag: "#research")
type Foam 检测到的文件类型——note(Markdown)或 image/attachment;注意这不是笔记 frontmatter 里的 type,按 frontmatter 过滤需用 jexl
path 路径匹配正则(如 path: "^/projects/")
title 标题匹配正则
links_to / links_from 链接到/被给定笔记标识符链接;"$current" 指代包含查询的当前笔记
jexl 针对每条笔记求值的 Jexl 表达式,如 `"resource.tags
and / or / not 逻辑组合

5.3 可展示字段与表格定制

可 select 的字段包括:title、path、filename、folder、extension、type、tags、aliases、sections、blocks、properties(可用 properties.<name> 取单项)、backlink-count、outlink-count、body(全文,frontmatter 已剔除、保留 H1)、content(同 body 但不含 H1 标题)、section[Label](指定小节内容)。选择多个字段时默认渲染为表格。

表格支持自定义列头与单元格链接行为:

```foam-query
filter: "#projects"
format: table
select:
  - field: title
    link: false             # 将 title 渲染为纯文本
  - field: filename
    link: true              # 改为让 filename 可点击
  - properties.Status
```

默认只有 title 列可点击(链接到所在行笔记),其余字段为纯文本;{ field, label, link } 对象形式可逐项覆盖。注意 body、content、section[...] 在 VS Code Web 中不受支持(会显示内联警告)。

5.4 JavaScript 查询(foam-query-js)

当 YAML 不够用时,可用 foam-query-js 代码块,但**仅限可信工作区(trusted workspace)**执行——在可信工作区中它拥有与 VS Code 其余部分相同的权限(可读写文件、发起网络请求),因此只应在你信任的笔记中使用(详见 docs/user/features/foam-queries.md):

```foam-query-js
const recentResearch = foam.pages('#research')
  .sortBy('title')
  .limit(5)
  .format('list');

render('Recent research notes:');
render(recentResearch);
```

foam.pages(filter?) 返回查询构建器,提供 where(fn)、sortBy(field, direction?)、limit(n)、offset(n)、select(fields)、format(fmt)、toArray() 等方法;foam.current 指代包含查询的笔记 URI(无活动文档时为 null),可写出相对当前笔记的查询。相较之下,jexl 过滤器在任意工作区都可运行——Jexl 语言设计上即为沙箱(无宿主全局、无文件系统、无网络)。

5.5 查询引擎的实现路径

整个查询体系在 @foam/core 中实现(核心文件 packages/foam-core/src/query/index.ts):

  • parseFilter:递归地把 QueryFilter 编译为资源谓词;path/title 正则先经 tryBuildUserRegex 用 safe-regex2 做“灾难性回溯”防护,非法或不安全的正则被拒绝并产生警告;jexl 通过独立的 Jexl 实例编译求值,内置 length、lower、upper 三个 transform。值得注意的历史包袱:旧的 expression 字段因曾使用 eval() 存在 RCE 风险,现已移除求值——遗留查询会匹配空集并给出弃用警告。
  • projectResource:按 select 投影字段,uri 恒定写入每行结果;body/content/section<a href="https://link.gitcode.com/i/84896a91a54d02a77e3bb1d6299c81fa" target="_blank">...] 属于“需源字段”,必须由宿主注入 SourceReader 才能解析(VS Code 端实现见 [packages/foam-vscode/src/vscode/features/preview/foam-query-renderer.ts)。
  • executeQuery:执行顺序为“先 filter → 排序 → offset/limit → 源字段懒加载”,这样 select: [body] 的大范围查询不会同步读取所有命中笔记(注释中明确说明此优化意图)。
  • QueryResult:foam-query-js 暴露的流式构建器,其 .where() 会切换到慢路径(先取全字段再过滤、排序、切片)。

六、Footnotes:脚注的渲染与导航

WHATS_NEW.md 提到 Foam 现已在编辑器和 Markdown 预览中支持脚注的渲染与导航。使用标准 Markdown 脚注语法即可(详见 docs/user/features/footnotes.md):

The study confirmed the hypothesis.[^1] Further work is needed.[^note]

[^1]: Smith et al., 2023, Journal of Examples.
[^note]: See the appendix for the full methodology.

脚注 ID 可以是数字或单词,定义可出现在文件任意位置(Foam 不依赖位置查找)。交互细节:

  • 悬停引用 [^1] 可内联查看定义;
  • 按 F12(或 Ctrl+Click / Cmd+Click)从引用跳转到定义;
  • 脚注引用沿用 wikilink 的着色样式,预览中脚注渲染在笔记底部;
  • 同一脚注可被多次引用:This point[^caveat] is elaborated elsewhere.[^caveat]。

七、Notes Explorer 过滤:在大库中快速定位

最后,WHATS_NEW.md 提到 Notes Explorer 面板现已支持过滤,便于在大型笔记库中导航。相关实现见 packages/foam-vscode/src/vscode/features/notes/notes-explorer.ts:面板提供 foam-vscode.views.notes-explorer.set-filter 命令,弹出输入框后按 标题或路径 实时过滤(res.title 或 res.uri.path 的 toLowerCase().includes(needle) 匹配);过滤激活时折叠目录会被自动展开,并可通过 clear-filter 命令清除。同时该面板还内置“仅笔记 / 全部资源”切换、连接数显示(↑links ↓backlinks)、层级/扁平视图切换等能力。


八、把这些能力串起来:一个完整的实战闭环

以“管理进行中项目”为例,上述特性可以无缝衔接成一套工作流:

  1. 定义查询:在 .foam/queries/work-in-progress.yaml 写入 filter: { and: [ { tag: '#wip' }, { not: { tag: '#archive' } } ] },sort: title ASC;
  2. 侧边栏查看:Smart Folders 面板即时出现该文件夹,命中笔记按目录分组展开;
  3. 终端批处理:foam query run work-in-progress 在 CI 或脚本中列出所有进行中的笔记路径;
  4. AI 集成:通过 MCP 工具 run_query(传 id 或临时 descriptor)让 Agent 查询工作区状态;
  5. 可视化:在图谱中为 #wip 笔记建立自定义分组并配色,随时观察项目网络结构;
  6. 细节锚定:在预览中用 foam-query 块嵌入相关笔记列表,用 ![[note#^block]] 嵌入关键结论段落。

参考文档与源码位置

登录后查看全文
foam