关于 wiki-import
wiki-import 用于从 wiki-export 生成的 graph.json 或 OKF 包,将知识导入当前 Obsidian Vault。它支持导入图结构页面存根或保留正文的完整页面,并提供合并、跳过和覆盖冲突模式。
主要功能
- 01导入 graph.json 或 OKF 包
- 02从图数据重建页面存根和关系
- 03从 OKF 包还原完整页面
- 04支持合并、跳过和覆盖模式
- 05更新 Vault 元数据并转换页面链接
适用场景
- 01将 graph.json 导出的结构导入 Vault
- 02在 Vault 之间迁移完整 Markdown 页面
- 03按所选冲突模式合并导入页面
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ar9av/ obsidian-wiki --skill wiki-import安装后在 Claude Code 中输入/wiki-import确认已加载。
详细介绍
Wiki 导入 — 从导出文件重建页面
你正在将某个知识库中的知识导入当前知识库,来源是 wiki-export 生成的以下两种文件之一:
graph.json— 图谱骨架。用于重建页面存根(frontmatter、带类型的关系和## Related链接列表,不含正文)。会丢失部分信息。- OKF 捆绑包(
wiki-export/okf/目录)— 实际的 Markdown 文件。用于重建包含原始正文的完整页面。无损。要在知识库之间进行真正的迁移,请使用此方式。
无论哪种方式,导入都会写入带有正确 frontmatter 和双链的页面,然后更新所有知识库元数据。第 2 步、第 3 步(仅图谱)和第 5 步通用;第 4 步根据来源类型分流。
开始之前
**写作配置:**在起草或改写自然语言 Markdown 之前,请阅读并应用 llm-wiki/SKILL.md 中的 Writing Profile Resolution 部分。框架架构、来源信息、安全和特定操作要求优先。
保留导入来源中的原文;仅对新生成的元数据或存根应用 WRITING.md 中的偏好。
- 解析配置 — 遵循
llm-wiki/SKILL.md中的 Config Resolution Protocol(内联@name覆盖 → 从当前工作目录向上查找.env→ 全局配置 → 提示设置)。由此获得OBSIDIAN_VAULT_PATH。 - 如果
$OBSIDIAN_VAULT_PATH/AGENTS.md存在,则读取该文件,并遵循其中针对所有者的约定。
第 1 步:定位并检测来源类型
查找导入来源:
- 如果用户提供了路径参数,请直接使用。
- 否则按以下顺序自动检测:
./wiki-export/okf/(目录)→./wiki-export/graph.json(文件)。 - 如果两者都不存在,请向用户询问路径。
检测来源类型:
- 路径是一个以
.json结尾的文件 → graph.json 导入(先按下文验证,再执行第 3 步和第 4 步-Graph)。 - 路径是一个包含
.md文件的目录,这些文件带有 OKF frontmatter(含type:键),和/或根目录下有一个包含okf_version的index.md→ OKF 捆绑包导入(跳过第 3 步;转到第 4 步-OKF)。接受okf_version为 0.1 和 0.2 的捆绑包;第 4 步-OKF 可读取这两种版本。 - 其他情况 → 报告问题并停止。
验证 graph.json 来源:
- 必须是有效的 JSON
- 顶层必须包含以下键:
nodes(数组)、links(数组)、graph(对象) - 至少必须有 1 个节点
如果验证失败,请报告问题并停止。
验证 OKF 捆绑包来源:
- 必须至少包含 1 个非保留
.md文件(即不是index.md/log.md),且该文件包含可解析的 YAML frontmatter,其中有非空的type。 - 没有 frontmatter 或没有
type的.md文件会被跳过(并计数),不会导致失败 — OKF 使用方采取宽松策略(OKF §11)。
导入前显示预览:
graph.json:
Import preview (graph.json — stubs)
Source: <path> (exported at <graph.exported_at>)
Nodes: N total (concepts: A, entities: B, skills: C, references: D, ...)
Links: M edges (X typed, Y untyped)
Target: $OBSIDIAN_VAULT_PATH
OKF 捆绑包:
Import preview (OKF bundle — full pages)
Source: <dir> (okf_version <ver if present>)
Pages: N total (concepts: A, entities: B, skills: C, references: D, ...)
Target: $OBSIDIAN_VAULT_PATH
第 2 步:确定冲突解决模式
根据用户的措辞确定模式。默认模式为 merge。
| 模式 | 触发短语 | 行为 |
|---|---|---|
merge | (默认,无特殊措辞) | 对于现有页面:更新 frontmatter 标签/摘要/关系,并添加缺失的双链;对于新页面:创建存根。 |
skip | “skip existing”、“don't overwrite”、“only new pages” | 完全不更改现有页面;仅创建尚不存在的页面。 |
overwrite | “overwrite”、“replace existing”、“force import” | 无论现有内容如何,都用重新构建的存根替换所有匹配页面。 |
第 3 步:构建内部映射 (仅限 graph.json — OKF 捆绑包跳过)
写入任何内容之前,先从 links 数组构建两个映射:
邻接映射 — 对于每个节点 ID,收集所有邻居 ID(包括两个方向的边):
adjacency["concepts/transformers"] = ["entities/vaswani", "concepts/lstm", ...]
带类型的边映射 — 对于每个节点 ID,仅收集向外的带类型边(typed: true):
typed_edges["concepts/transformers"] = [
{target: "concepts/lstm", relation: "contradicts"},
...
]
步骤 4-Graph:从 graph.json 重建页面 (仅限 graph.json 源)
记录计数:created = 0、skipped = 0、merged = 0。
对于 nodes 中的每个节点:
- 计算
page_path = $VAULT/<node.id>.md - 确保父目录存在(例如
$VAULT/concepts/) - 检查文件是否已存在:
- 合并模式(默认)+ 文件存在 → 读取现有文件,应用合并逻辑(见下文),
merged加 1 - 合并模式(默认)+ 文件不存在 → 创建存根,
created加 1 - 跳过模式 + 文件存在 →
skipped加 1,继续处理下一个节点 - 跳过模式 + 文件不存在 → 创建存根,
created加 1 - 覆盖模式 + 文件存在 → 写入新的存根(覆盖),
merged加 1 - 覆盖模式 + 文件不存在 → 创建存根,
created加 1
- 合并模式(默认)+ 文件存在 → 读取现有文件,应用合并逻辑(见下文),
页面模板(新建或覆盖)
---
title: <node.label>
category: <node.category>
tags: <node.tags as YAML list>
sources:
- "imported from <graph.json path>"
<if node.summary exists>
summary: "<node.summary>"
</if>
<if typed_edges[node.id] is non-empty>
relationships:
<for each {target, relation} in typed_edges[node.id]>
- target: "[[<target>]]"
type: <relation>
</for>
</if>
lifecycle: draft
lifecycle_changed: <today YYYY-MM-DD>
base_confidence: 0.5
tier: supporting
created: <ISO timestamp>
updated: <ISO timestamp>
---
# <node.label>
<node.summary paragraph if available, else omit>
## Related
<for each neighbour in adjacency[node.id], sorted alphabetically>
<if edge is typed>
- [[<neighbour>]] — <relation>
<else>
- [[<neighbour>]]
</if>
</for>
如果 adjacency[node.id] 为空,则完全省略 ## Related 部分。
合并逻辑(合并模式,页面已存在)
- 读取现有页面的 frontmatter。
- 标签:合并现有标签和
node.tags(去重,保留现有顺序,将新标签追加到末尾)。 - 摘要:如果现有页面没有
summary字段,且node.summary存在,则添加该字段。 - 关系:合并现有的
relationships:条目和typed_edges[node.id],跳过已存在相同(target, type)对的条目。 - 更新时间:将
updated设置为当前 ISO 时间戳。 - 正文:查找
## Related部分。如果存在,则追加adjacency[node.id]中尚未在正文任何位置链接的缺失维基链接。如果不存在,则追加一个包含缺失链接的部分。 - 保持正文的其余部分不变。
步骤 4-OKF:从 OKF 包重建页面 (仅限 OKF 源)
记录计数:created = 0、skipped = 0、merged = 0、unparseable = 0。
遍历包目录树。对于每个非保留文件(index.md、log.md)的 .md 文件:
- 解析 YAML frontmatter。如果没有 frontmatter 或没有非空的
type,则将unparseable加 1 并跳过该文件。 - 计算概念 ID = 文件相对于 bundle 根目录的路径,去掉
.md(例如,concepts/transformers.md→concepts/transformers)。目标页面为$VAULT/<concept-id>.md。 - 反向映射 frontmatter(即
wiki-export第 3.5 步中规范映射表的逆向操作):title←title。category← 如存在,则取保留的category扩展键;否则,将概念 ID 的目录前缀转为小写(concepts/…→concepts);否则,根据type推导(Concept→concepts,Entity→entities,Skill→skills,Reference→references,Synthesis→synthesis,Project→projects,Journal→journal)。tags←tags。summary←description。updated← 如存在,则取保留的updated扩展键;否则取generated.at;否则取旧版 v0.1 的timestamp;否则取当前时间。created← 如存在,则取保留的created扩展键;否则取当前时间。sources← OKFsources,作为字符串列表:对于每个映射条目,取其resource;普通字符串保持原样(旧版导出生成的 v0.1 bundle 会以这种方式携带原生列表)。如果 bundle 没有sources,则使用["imported from OKF bundle <bundle path>"]。如果存在顶层resourceURL,且它尚未在列表中,则将其添加进去。lifecycle← 如存在,则取保留的lifecycle扩展键;否则根据status设置:draft→draft,deprecated→archived,stable或缺省 → 不设置lifecycle。(OKF 没有与reviewed/verified/disputed等效的字段,因此只有通过保留键,才能恢复我们自己的导出内容。)- 原样保留其他任何扩展键(
relationships、lifecycle_changed、tier、base_confidence等),以及 OKF 专属键generated、verified、stale_after和usage_window,以免后续导出时丢失。这些键可确保往返转换无损。将单独的verified映射视为单元素列表(OKF §5.2)。 - 但排除由我们自己的导出器推导出的内容。 如果
generated.by以obsidian-wiki/开头,则丢弃generated(它是根据updated计算得出的);丢弃by为human:vault-owner的verified条目(它们是根据源知识库的信任账本计算得出的,不会随内容传递)。保留这些内容会添加源页面中原本不存在的键。来自其他任何生成方的generated或verified块都应保留。
- 反向转换正文中的链接 — 将指向
.md路径的 Markdown 链接转换为 wikilink(这会恢复真实的交叉链接,以及导出器根据wiki-export第 3.5 步保留的前向引用):[text](../concepts/transformers.md)或[text](/concepts/transformers.md)→ 将路径解析(相对于此文件所在目录;以/开头的绝对路径则相对于 bundle 根目录)为概念 ID →[[concepts/transformers]];如果text与目标标题不同,则使用[[concepts/transformers|text]]。目标标题取自 bundle 中对应页面;如果页面不存在,导出器写入的链接文本就是目标 ID,因此当文本等于完整 ID(或其最后一个路径段)时,不添加别名。- 应首先将 Markdown 链接目标视为文件路径:相对于当前文件规范化
.md路径,然后去掉解析后文件路径末尾的.md,以还原页面 ID。不要在解析完整文件路径之前,尝试根据目录遍历片段推断 ID。这样可保留文件夹笔记布局的往返转换,例如projects/social-twitter.md加上projects/social-twitter/...,其中../../social-twitter.md必须还原为projects/social-twitter。 - 即使目标页面不在 bundle 中,也要应用此规则——指向路径的链接应还原为指向尚未创建页面的
[[wikilink]](Obsidian 支持这种链接;OKF §11 规定不得拒绝)。不要将其保留为 Markdown 链接。 - 当配置中设置了
OBSIDIAN_LINK_FORMAT=markdown时,保留 Markdown 链接(只需将路径改写为相对于知识库根目录的路径);不要转换为 wikilink。 - 外部
http(s)://链接和# Citations部分保持不变。
- 写入页面,采用第 2 步中的冲突模式:
- merge + 存在 → 反向映射 frontmatter 并将其合并到现有页面(合并
tags;仅在缺失时填入summary/sources;合并relationships;刷新updated)。对于正文,OKF 携带的是真实正文:仅当现有页面为存根(正文只有标题 +## Related)时,才用 bundle 正文替换现有正文;否则保留现有正文,并追加尚未存在的 bundle# Citations/ 新##部分。将merged加 1。 - merge + 不存在 → 写入完整页面(frontmatter + 完整 bundle 正文)。将
created加 1。 - skip + 存在 → 将
skipped加 1,然后继续。 - skip + 不存在 → 写入完整页面。将
created加 1。 - overwrite + 存在 → 写入完整页面,替换现有页面。将
merged加 1(在摘要中标记为Replaced)。 - overwrite + 不存在 → 写入完整页面。将
created加 1。
- merge + 存在 → 反向映射 frontmatter 并将其合并到现有页面(合并
- 写入前确保父目录(
$VAULT/concepts/等)存在。 与 graph.json 路径不同,不要生成## Related占位章节——bundle 正文中已包含真实的交叉链接。
第 5 步:更新 Vault 元数据
.manifest.json
添加一条新记录,以 graph.json 文件的规范路径为键:
"<absolute path to source>": {
"ingested_at": "<ISO timestamp>",
"source_type": "wiki-export",
"pages_created": ["list/of/created/pages.md"],
"pages_updated": ["list/of/merged/pages.md"]
}
对于 graph.json 导入,将 source_type 设为 "wiki-export";对于 OKF bundle 导入,则设为 "okf-bundle"。以来源的绝对路径作为键(graph.json 文件或 bundle 目录)。
同时递增:
stats.total_sources_ingested加 1stats.total_pages加上实际创建的页面数(不包括跳过或合并的页面)
如果 .manifest.json 不存在,请按标准结构创建:
{
"stats": {
"total_sources_ingested": 1,
"total_pages": <created count>
},
"<graph.json path>": { ... }
}
index.md、log.md 和 hot.md
一次加锁调用。索引会根据磁盘上的现有页面进行协调,因此每个创建或合并的页面都会归入相应类别,并使用文档规定的 ( #tag) 间距:
obsidian-wiki memory sync IMPORT \
source="<bundle path>" \
pages_created=<N> pages_skipped=<K> pages_merged=<M> \
--takeaways "Imported <bundle>: <what it adds to the picture>"
如果导入没有改变整体情况,请省略 --takeaways;之前的要点会保留。切勿手动编辑 index.md、log.md 或 hot.md——该命令会获取锁,防止并行写入者丢失你的更新。
完整流程请参阅 .skills/llm-wiki/references/MEMORY.md。
第 6 步:打印摘要
Wiki import complete → $OBSIDIAN_VAULT_PATH
Source: <source path> (<graph.json | OKF bundle>)
<graph: exported at <graph.exported_at>, N nodes, M links | okf: N pages, okf_version <ver>>
Created: <X> pages
Merged: <Z> pages (existing pages updated)
Skipped: <Y> pages (only when --skip mode was used)
只有在明确请求 skip 模式时,才显示 Skipped 行。如果使用了 overwrite 模式,则将合并的页面标记为 Replaced。对于 OKF 导入,如果 U > 0,还需报告 Unparseable: <U> files (no frontmatter/type, skipped)。
备注
- 占位页面与完整页面:graph.json 导入会生成占位页面——包含结构和 wikilink,但没有正文,可作为后续摄取的起点。OKF bundle 导入会生成带有真实正文的完整页面——这是无损且包含内容的路径,也是 vault 到 vault 传输的正确选择。
- 可安全重复运行:默认的
merge模式具有幂等性——对未更改的导出重复运行会更新时间戳,但不会破坏内容。只有在需要将页面完全重置为占位页面时,才使用overwrite。 - 创建目录:写入页面前,始终先创建缺失的类别目录。
- 失效的 wikilink:由于这些页面是从同一导出中一起创建的,大多数链接都能解析。任何在
links中引用、但不在nodes中的节点(原始导出中的失效链接)仍会显示为 wikilink——只是不会有对应的页面文件,这是有效的。 - 经过筛选的导出:如果源
graph.json是通过可见性筛选生成的(记录在graph.metadata中),导入的页面将只反映筛选后的集合。如果graph.graph包含filtered键,请在摘要中注明这一点。