mcpField
W
wiki-export

wiki-export

github.com/ar9av数据与数据库3.6k3.2k 次安装1 天前更新
代码仓库

关于 wiki-export

wiki-export 是用于导出 Obsidian 知识库 wikilink 图谱的技能,输出可供图谱工具、数据库和浏览器可视化使用的格式。支持按项目或可见性筛选,也可另行导出 Markdown 文档包。

主要功能

  1. 01导出 JSON、GraphML、Cypher、SQL 和 HTML 图谱
  2. 02提取 wikilink 与类型化关系
  3. 03按标签为页面划分社群
  4. 04按项目或可见性筛选
  5. 05可选导出 OKF Markdown 文档包

适用场景

  1. 01将知识库页面和链接载入 Neo4j 或 Postgres
  2. 02在 Gephi 或浏览器中探索知识库图谱
  3. 03导出指定项目或面向公众的页面
  4. 04生成供内容复用的 Markdown 文档包

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ar9av/obsidian-wiki --skill wiki-export

安装后在 Claude Code 中输入/wiki-export确认已加载。

详细介绍

Wiki 导出 — 知识图谱导出

将 Wiki 的 wikilink 图导出为结构化格式,以便在外部工具(Gephi、Neo4j、自定义脚本、浏览器可视化)中使用。

开始之前

  1. 解析配置 — 按照 llm-wiki/SKILL.md 中的配置解析协议操作(内联 @name 覆盖 → 沿 CWD 向上查找 .env → 全局配置 → 提示设置)。这会提供 OBSIDIAN_VAULT_PATH
  2. 确认保管库中有可供导出的页面 — 如果页面少于 5 个,请警告用户并停止

项目筛选(可选)

如果用户的调用中包含项目名称 — 例如 /wiki-export prismor、"export the prismor project"、"export project:security" — 启用项目筛选模式:

  1. 从参数或短语中提取项目名称。标准化处理:转换为小写,并去掉单词 "project"。
  2. 仅保留符合任一条件的页面:
    • 页面 id 以 projects/<name>/ 开头(基于路径匹配)
    • 页面的 tags 数组包含 <name>(基于标签匹配)
  3. 删除任一端点已被排除的边。
  4. 在摘要中注明筛选条件:(filtered: project:<name> — X of Y pages)
  5. 在 JSON 输出中设置 graph.graph.filter = "project:<name>"。

如果项目筛选和可见性筛选都已启用,则同时应用两者(先应用项目筛选,再对剩余页面应用可见性筛选)。

可见性筛选(可选)

默认情况下,导出所有页面,不受可见性标签影响。这样可以保持现有行为。

如果用户请求筛选导出 — 例如 "public export"、"user-facing export"、"exclude internal"、"no internal pages" — 启用可见性筛选模式:

  • 构建一个屏蔽标签集合:{visibility/internal, visibility/pii}
  • 构建节点列表时,跳过 frontmatter 标签中包含屏蔽标签的页面
  • 跳过任一端点已被排除的边
  • 在摘要中注明筛选条件:(filtered: visibility/internal, visibility/pii excluded)

没有 visibility/ 标签或带有 visibility/public 标签的页面始终会被包含。

步骤 1:构建节点列表和边列表

遍历 vault 中所有 .md 文件(排除 _archives/、_raw/、_readouts/、.obsidian/、index.md、log.md、_insights.md)。收集完整文件列表后,再应用所有有效筛选条件(项目和/或可见性)。

对于每个页面,从 frontmatter 中提取:

  • id — 相对于 vault 根目录的路径,不带 .md 扩展名(例如 concepts/transformers)
  • label — frontmatter 中的 title 字段;如果缺失,则使用文件名
  • category — 目录前缀(concepts、entities、skills、references、synthesis、projects 或 journal)
  • tags — frontmatter tags 字段中的数组
  • summary — 存在时使用 frontmatter 的 summary 字段

这就是你的节点列表。

对于每个页面,在正文中搜索 \[\[.*?\]\],提取所有 wikilink:

  • 解析每个 [[target]] 或 [[target|display]] — 仅使用 target 部分
  • 将 target 解析为节点 id(规范化:转为小写,将空格替换为连字符,移除 .md)
  • 跳过指向节点列表之外的链接(断开的链接)
  • 每个解析成功的链接都会成为一条边:{source: page_id, target: linked_id, relation: "wikilink", confidence: "EXTRACTED"}
  • 如果链接所在句子以 ^[inferred] 或 ^[ambiguous] 结尾,则相应覆盖 confidence

类型化边增强: 构建完 wikilink 边列表后,读取每个页面 frontmatter 中的 relationships: 块。对于每个 {target, type} 条目:

  • target YAML 值是带引号的 wikilink 字符串,例如 "[[concepts/lstm]]"。去掉外围的 [[ 和 ]] 字符,然后按相同规则规范化(转为小写,将空格替换为连字符,移除 .md),得到节点 id。
  • 跳过解析后的目标不在节点列表中的条目(断开的链接)
  • 如果此 (source, target) 对应的边已存在,则将其 relation 字段覆盖为类型值(例如 "contradicts"),并设置 typed: true
  • 如果此对的边尚不存在,则添加一条:{source: page_id, target: target_id, relation: <type>, confidence: "EXTRACTED", typed: true}

这意味着,relation: "wikilink" 是普通未类型化链接的默认值;relationships: 条目会将其升级为具名语义类型。同时由正文 wikilink 和 relationships: 条目共同产生的边只保留一条记录 — 以类型化版本为准。

这就是你的边列表。

步骤 2:分配社区 ID

按标签聚类将页面分组到不同社区:

  • 具有相同主导标签的页面属于同一个社区
  • 主导标签 = 页面 frontmatter tags 数组中的第一个标签
  • 没有标签的页面,其社区 id 为 null
  • 社区编号从 0 开始,按大小降序排列(最大社区 = 0)

这可用于 HTML 可视化中的社区着色,以及 Gephi 等工具。

步骤 3:编写输出文件

如果 vault 根目录下不存在 wiki-export/,则创建该目录。写入以下全部五个文件:


3a. graph.json

NetworkX node_link 格式 — 图工具和脚本的标准格式:

{
  "directed": false,
  "multigraph": false,
  "graph": {
    "exported_at": "<ISO timestamp>",
    "vault": "<OBSIDIAN_VAULT_PATH>",
    "total_nodes": N,
    "total_edges": M
  },
  "nodes": [
    {
      "id": "concepts/transformers",
      "label": "Transformer Architecture",
      "category": "concepts",
      "tags": ["ml", "architecture"],
      "summary": "The attention-based architecture introduced in Attention Is All You Need.",
      "community": 0
    }
  ],
  "links": [
    {
      "source": "concepts/transformers",
      "target": "entities/vaswani",
      "relation": "wikilink",
      "confidence": "EXTRACTED"
    },
    {
      "source": "concepts/transformers",
      "target": "concepts/lstm",
      "relation": "contradicts",
      "confidence": "EXTRACTED",
      "typed": true
    }
  ]
}

3b. graph.graphml

GraphML XML 格式 — 可在 Gephi、yEd 和 Cytoscape 中加载:

<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/graphml">
  <key id="label" for="node" attr.name="label" attr.type="string"/>
  <key id="category" for="node" attr.name="category" attr.type="string"/>
  <key id="tags" for="node" attr.name="tags" attr.type="string"/>
  <key id="community" for="node" attr.name="community" attr.type="int"/>
  <key id="relation" for="edge" attr.name="relation" attr.type="string"/>
  <key id="type" for="edge" attr.name="type" attr.type="string"/>
  <key id="confidence" for="edge" attr.name="confidence" attr.type="string"/>
  <graph id="wiki" edgedefault="undirected">
    <node id="concepts/transformers">
      <data key="label">Transformer Architecture</data>
      <data key="category">concepts</data>
      <data key="tags">ml, architecture</data>
      <data key="community">0</data>
    </node>
    
    <edge source="concepts/transformers" target="entities/vaswani">
      <data key="relation">wikilink</data>
      <data key="confidence">EXTRACTED</data>
    </edge>
    
    <edge source="concepts/transformers" target="concepts/lstm">
      <data key="relation">contradicts</data>
      <data key="type">contradicts</data>
      <data key="confidence">EXTRACTED</data>
    </edge>
  </graph>
</graphml>

每个页面写入一个 <node>,每条链接写入一个 <edge>。对于类型化边(边列表中 typed: true 的边),同时输出带有语义类型值的 <data key="relation"> 和具有相同值的 <data key="type"> — 这样既能让已使用 relation 的工具继续读取,也能让支持类型的工具通过专用 type 键进行筛选。未类型化的 wikilink 完全省略 <data key="type"> 元素。


3c. cypher.txt

Neo4j Cypher MERGE 语句 — 粘贴到 Neo4j Browser 中,或通过 cypher-shell 运行:

// Wiki knowledge graph export — <TIMESTAMP>
// Load with: cypher-shell -u neo4j -p password < cypher.txt

// Nodes
MERGE (n:Page {id: "concepts/transformers"}) SET n.label = "Transformer Architecture", n.category = "concepts", n.tags = ["ml","architecture"], n.community = 0;
MERGE (n:Page {id: "entities/vaswani"}) SET n.label = "Ashish Vaswani", n.category = "entities", n.tags = ["person","ml"], n.community = 0;
MERGE (n:Page {id: "concepts/lstm"}) SET n.label = "LSTM", n.category = "concepts", n.tags = ["ml","rnn"], n.community = 0;

// Relationships
// Untyped wikilinks use [:WIKILINK]
MATCH (a:Page {id: "concepts/transformers"}), (b:Page {id: "entities/vaswani"}) MERGE (a)-[:WIKILINK {relation: "wikilink", confidence: "EXTRACTED"}]->(b);
// Typed edges use the relationship type as the label (UPPERCASE)
MATCH (a:Page {id: "concepts/transformers"}), (b:Page {id: "concepts/lstm"}) MERGE (a)-[:CONTRADICTS {relation: "contradicts", confidence: "EXTRACTED"}]->(b);

每个页面写入一条节点 MERGE 语句,然后为每条边写入一条关系 MATCH/MERGE 语句。对于类型化边,将 type 值转为大写,作为 Cypher 关系标签(例如,contradicts → [:CONTRADICTS],derived_from → [:DERIVED_FROM])。未类型化的 wikilink 始终使用 [:WIKILINK]。


3d. postgres.sql

纯 SQL——可通过 psql -f postgres.sql 或迁移运行器加载到任意 Postgres 数据库(本地、Supabase、RDS、Neon 等)。包含两张表:wiki_pages(节点)和 wiki_edges(链接),并使用 ON CONFLICT upsert,因此重新运行导出安全且幂等,与 cypher.txt 的 MERGE 语义一致。

-- Wiki knowledge graph export — <TIMESTAMP>
-- Load with: psql -d yourdb -f postgres.sql

CREATE TABLE IF NOT EXISTS wiki_pages (
  id        TEXT PRIMARY KEY,
  label     TEXT NOT NULL,
  category  TEXT,
  tags      JSONB NOT NULL DEFAULT '[]'::jsonb,
  summary   TEXT,
  community INT
);

CREATE TABLE IF NOT EXISTS wiki_edges (
  source     TEXT NOT NULL REFERENCES wiki_pages(id) ON DELETE CASCADE,
  target     TEXT NOT NULL REFERENCES wiki_pages(id) ON DELETE CASCADE,
  relation   TEXT NOT NULL DEFAULT 'wikilink',
  confidence TEXT,
  typed      BOOLEAN NOT NULL DEFAULT false,
  PRIMARY KEY (source, target, relation)
);

CREATE INDEX IF NOT EXISTS wiki_edges_source_idx ON wiki_edges(source);
CREATE INDEX IF NOT EXISTS wiki_edges_target_idx ON wiki_edges(target);

-- Nodes
INSERT INTO wiki_pages (id, label, category, tags, summary, community)
VALUES ('concepts/transformers', 'Transformer Architecture', 'concepts', '["ml","architecture"]'::jsonb, 'The attention-based architecture introduced in Attention Is All You Need.', 0)
ON CONFLICT (id) DO UPDATE SET
  label = EXCLUDED.label, category = EXCLUDED.category, tags = EXCLUDED.tags,
  summary = EXCLUDED.summary, community = EXCLUDED.community;

-- Edges
-- Untyped wikilink
INSERT INTO wiki_edges (source, target, relation, confidence, typed)
VALUES ('concepts/transformers', 'entities/vaswani', 'wikilink', 'EXTRACTED', false)
ON CONFLICT (source, target, relation) DO UPDATE SET confidence = EXCLUDED.confidence, typed = EXCLUDED.typed;

-- Typed edge from relationships: block
INSERT INTO wiki_edges (source, target, relation, confidence, typed)
VALUES ('concepts/transformers', 'concepts/lstm', 'contradicts', 'EXTRACTED', true)
ON CONFLICT (source, target, relation) DO UPDATE SET confidence = EXCLUDED.confidence, typed = EXCLUDED.typed;

每个页面写入一条 INSERT ... ON CONFLICT (id) DO UPDATE 语句(对值进行转义:单引号加倍,tags 序列化为 JSON 数组字面量并转换为 jsonb),然后每条边写入一条 INSERT ... ON CONFLICT (source, target, relation) DO UPDATE 语句。这里的 relation 保持小写(不同于大写的 Cypher 关系标签),因为它是普通列值,而不是架构标识符——这样可以直接用 WHERE relation = 'contradicts' 进行筛选,或直接联接,无需统一大小写。只有由 relationships: frontmatter 条目提升的边,其 typed 才为 true;普通 wikilink 保持 false。

如果某个页面的 id 仅在先前运行触发 ON CONFLICT 子句后发生冲突,则跳过该页面——不要尝试对合成多重边去重(例如,同一 source/target 同时存在 wikilink 和有类型的关系),因为复合主键 (source, target, relation) 已将它们保留为不同的行,这与 graph.json/graph.graphml 在查询层的“有类型版本优先”合并行为一致(SELECT * FROM wiki_edges WHERE source=$1 AND target=$2 ORDER BY typed DESC LIMIT 1)。


3e. graph.html

使用 vis.js CDN 的独立交互式可视化(无本地依赖)。用户可在任意浏览器中打开此文件——无需服务器。

按以下步骤构建 HTML 文件:

  1. 为 vis.js 生成节点对象的 JSON 数组:
{id: "concepts/transformers", label: "Transformer Architecture", color: {background: "#4E79A7"}, size: <degree * 3 + 8>, title: "concepts | #ml #architecture", community: 0}
  • 按 community 着色(循环使用:#4E79A7、#F28E2B、#E15759、#76B7B2、#59A14F、#EDC948、#B07AA1、#FF9DA7、#9C755F、#BAB0AC)
  • 按度数(入链数 + 出链数)设置大小:size = degree * 3 + 8,最大为 60
  • title = 悬停时显示的工具提示文本:category、tags、summary(如有)
  1. 为 vis.js 生成边对象的 JSON 数组:
// Untyped wikilink
{from: "concepts/transformers", to: "entities/vaswani", dashes: false, width: 1, color: {color: "#666", opacity: 0.6}, title: "wikilink"}
// Typed edge
{from: "concepts/transformers", to: "concepts/lstm", dashes: false, width: 2, color: {color: "#E15759", opacity: 0.8}, label: "contradicts", font: {size: 9, color: "#ccc"}, title: "contradicts"}
  • 对 INFERRED 边设置 dashes: true
  • 对 AMBIGUOUS 边设置 dashes: [4,8]
  • 有类型的边(typed: true):设置 width: 2,添加显示类型的 label 字段,并应用特定类型的颜色:
类型边颜色
extends#59A14F(绿色)
implements#4E79A7(蓝色)
contradicts#E15759(红色)
derived_from#F28E2B(橙色)
uses#76B7B2(青绿色)
replaces#B07AA1(紫色)
related_to#BAB0AC(灰色——与无类型边相同)

无类型的 wikilink 边保留现有的 #666 灰色,且不添加标签。

  1. 编写完整的 HTML 文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Wiki Knowledge Graph</title>
<script src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body { background: #0f0f1a; color: #e0e0e0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; display: flex; height: 100vh; }
  #graph { flex: 1; }
  #sidebar { width: 260px; background: #1a1a2e; border-left: 1px solid #2a2a4e; padding: 14px; overflow-y: auto; font-size: 13px; }
  #sidebar h3 { color: #aaa; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 10px; }
  #info { margin-bottom: 16px; line-height: 1.6; color: #ccc; }
  .legend-item { display: flex; align-items: center; gap: 8px; padding: 3px 0; font-size: 12px; }
  .dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
  #stats { margin-top: 16px; color: #555; font-size: 11px; }
</style>
</head>
<body>
<div id="graph"></div>
<div id="sidebar">
  <h3>Wiki Knowledge Graph</h3>
  <div id="info">Click a node to see details.</div>
  <h3 style="margin-top:12px">Communities</h3>
  <div id="legend"></div>
  <div id="stats"></div>
</div>
<script>
const NODES_DATA = /* NODES_JSON */;
const EDGES_DATA = /* EDGES_JSON */;
const COMMUNITY_COLORS = ["#4E79A7","#F28E2B","#E15759","#76B7B2","#59A14F","#EDC948","#B07AA1","#FF9DA7","#9C755F","#BAB0AC"];

const nodes = new vis.DataSet(NODES_DATA);
const edges = new vis.DataSet(EDGES_DATA);
const network = new vis.Network(document.getElementById('graph'), {nodes, edges}, {
  physics: { solver: 'forceAtlas2Based', forceAtlas2Based: { gravitationalConstant: -60, springLength: 120 }, stabilization: { iterations: 200 } },
  interaction: { hover: true, tooltipDelay: 100 },
  nodes: { shape: 'dot', borderWidth: 1.5 },
  edges: { smooth: { type: 'continuous' }, arrows: { to: { enabled: true, scaleFactor: 0.4 } } }
});
network.once('stabilizationIterationsDone', () => network.setOptions({ physics: { enabled: false } }));

network.on('click', ({nodes: sel}) => {
  if (!sel.length) return;
  const n = NODES_DATA.find(x => x.id === sel[0]);
  if (!n) return;
  document.getElementById('info').innerHTML = `<b>${n.label}</b><br>Category: ${n.category||'—'}<br>Tags: ${n.tags||'—'}<br>${n.summary ? '<br>'+n.summary : ''}`;
});

// Build legend
const communities = {};
NODES_DATA.forEach(n => { if (n.community != null) communities[n.community] = (communities[n.community]||0)+1; });
const leg = document.getElementById('legend');
Object.entries(communities).sort((a,b)=>b[1]-a[1]).forEach(([cid, count]) => {
  const color = COMMUNITY_COLORS[cid % COMMUNITY_COLORS.length];
  leg.innerHTML += `<div class="legend-item"><div class="dot" style="background:${color}"></div>Community ${cid} (${count})</div>`;
});
document.getElementById('stats').textContent = `${NODES_DATA.length} pages · ${EDGES_DATA.length} links`;
</script>
</body>
</html>

将 /* NODES_JSON */ 和 /* EDGES_JSON */ 替换为第 1 步中生成的实际 JSON 数组。


第 3.5 步:OKF Bundle 导出(可选)

仅当用户要求 OKF / markdown bundle 时才运行此步骤(例如“导出为 OKF”“OKF bundle”“open knowledge format”“导出为 markdown bundle”)。此步骤是附加操作——始终生成上述五个图文件;此外还会写入一个保留完整内容的 markdown bundle。

五个图文件是有损投影(仅包含图的骨架)。OKF bundle 包含实际页面正文,因此通过 OKF 执行导出→wiki-import 往返可保留完整内容,并且 bundle 可直接用于 MkDocs、Notion、Hugo、GitHub 渲染器,或任何 Open Knowledge Format 消费端。

规范化 frontmatter 映射(obsidian-wiki ⇄ OKF)

此表是映射的唯一真实来源;wiki-import 会引用此表来处理反向映射。

OKF 键← 导出自→ 导入到备注
type(必填)category,首字母大写category(全小写)concepts→Concept,entities→Entity,skills→Skill,references→Reference,synthesis→Synthesis,projects→Project,journal→Journal。OKF 要求提供 type;使用方可以接受任意字符串。
titletitletitle原样保留。
descriptionsummarysummary我们单行的 summary: 就是 OKF 的 description(用于 index.md 条目)。
tagstagstags列表原样保留。visibility/* 系统标签原样传递。
generatedupdated → generated.at;by 是生成方updated ← generated.at写入 generated: { by: obsidian-wiki/<version>, at: <datetime> },其中 <version> 取自 obsidian-wiki --version(如果未安装 CLI,则使用纯 obsidian-wiki)。OKF §5 要求每个时间戳都是带有明确偏移量的日期时间,因此日期格式的 updated: 2026-04-12 会转换为 2026-04-12T00:00:00Z。这取代了 v0.1 的 timestamp,后者不再写入。
sourcessources(字符串列表)sources(字符串列表)OKF 的 sources 是对象列表,每个对象都必须包含 resource,因此每个原生字符串都会转换为 - resource: <string>。conversation:2026-04-12 这类不透明字符串有效:§5.1 允许使用方无法跟踪的范围描述符。每个条目不输出其他内容,以便导入时还原出完全一致的字符串。
statuslifecycle—(原生 lifecycle 会保留)draft → draft;reviewed、verified、disputed → stable;archived → deprecated。如果没有 lifecycle,则省略 status(在 OKF 中,缺省表示 stable)。
verified_meta/trust-ledger.json 中对应页面的条目—(原样保留)仅当页面有账本条目时写入:verified: { by: human:vault-owner, at: <entry reviewed_at> }。trust-record 只会写入经人工使用 --approved 批准的条目,因此 human: 操作者准确无误,并赋予页面 OKF 的 人工审核 级别(§5.3)。绝不能仅根据 lifecycle 推导 verified。
resource首个 sources: 条目,当且仅当它是 http(s):// URL—可选;没有来源 URL 时省略。大多数页面描述的是抽象知识,并无来源 URL。
(扩展字段)category、created、updated、relationships、lifecycle、lifecycle_changed、tier、base_confidence、…原样保留OKF §4.1 要求使用方保留未知键。将我们的原生键写作 OKF 扩展 frontmatter,才能实现无损往返——导入时,优先使用保留的 category/created/updated/lifecycle,而不是从 type/generated/status 重新推导。(由于 generated.at 必须是完整的日期时间,updated 会一并保留;否则仅包含日期的 updated 导入后会变成 UTC 午夜。)sources 不再是扩展字段:它是规范字段(见上方行)。
步骤

复用步骤 1 中的节点列表(包括已应用的所有活动项目/可见性筛选条件)。在 wiki-export/okf/ 下写入目录树:

  1. 每个范围内的页面对应一个文件。 对每个页面解析其 frontmatter,根据上表构建 OKF frontmatter(依次为必填的 type、title、description、tags、generated、status、verified、sources、可选的 resource,然后是保留的扩展键),转换正文链接(见下文),并写入 wiki-export/okf/<category>/<slug>.md——使用页面在知识库中的相同相对路径。

  2. 正文链接转换([[wikilinks]] → 标准 Markdown 链接):

    • [[concepts/transformers]] → [<target title>](<file-relative path>.md),例如,从 entities/foo.md 链接到 concepts/transformers,会变为 [Transformer Architecture](../concepts/transformers.md)。链接文本 = 目标页面的 title(如果未知,则使用目标 id)。
    • [[target|display]] → [display](<rel path>.md)。
    • 使用相对于文件的路径(../concepts/x.md),绝不使用以 / 开头的绝对路径——以 / 开头的链接会导致 GitHub 渲染失败。(这与 knowledge-catalog 自身的生产 Agent 一致。)
    • 根据目标文件路径计算相对路径,而不是根据页面 id:将 wikilink 目标规范化为页面 id,追加 .md,然后计算 relpath(<target-file>, <source-file-dir>)。调用 relpath() 前,绝不要忘记先追加 .md。
    • 对于常见的“文件夹笔记”布局,此步骤必不可少:页面 id 既是文件,也是目录前缀,例如 projects/social-twitter.md 和 projects/social-twitter/...。从 projects/social-twitter/concepts/mem0-memory-analysis.md 链接到 [[projects/social-twitter]] 时,必须导出为 ../../social-twitter.md,而不是 ...md。
    • 使用与步骤 1 相同的规范化方式解析链接目标(转为小写,将空格替换为连字符,去掉 .md)。根据形式处理未解析的目标,以保留前向引用,确保往返转换不丢失内容:
      • 解析为范围内的页面 → 转换为指向该页面的相对 Markdown 链接。
      • 路径形式的目标(包含 /,例如 [[concepts/attention-mechanism]])尚无对应页面,且未被活动筛选器排除 → 仍然生成相对 Markdown 链接。OKF §11 禁止因损坏的交叉链接而拒绝整个数据包,保留链接可在重新导入时无损保留用户的前向引用。(已在 st3ve 上验证:静默丢弃这些链接会删除真实的 [[wikilinks]]。)
      • 被活动项目/可见性筛选器排除 → 转为纯文本。不要生成指向已筛除内容的路径。
      • 裸标题目标没有匹配项(例如范围内不存在对应页面时的 [[tractorex]])→ 转为纯文本;没有可靠的路径可供生成。
    • 保留现有的外部 http(s):// 链接和 # Citations 部分,不作更改。
  3. 生成 index.md 文件(OKF §8 渐进式披露;这些文件不包含每个条目的 frontmatter):

    • 数据包根目录 wiki-export/okf/index.md — 包含一个 # Subdirectories 部分,列出每个类别文件夹:* [<category>](<category>/index.md) - <one-line description of the category>。这是唯一允许包含 frontmatter 的索引文件:添加一个键 okf_version: "0.2"(OKF §8、§12)。
    • 每个类别文件夹中各有一个 index.md,列出该类别的页面:* [<title>](<slug>.md) - <description from the page's summary>。
  4. 从知识库根目录的 log.md 生成 log.md,并按 OKF §9 调整格式。该规范要求使用 ## YYYY-MM-DD 日期标题,日期从新到旧排列。我们的日志是扁平的、按时间从早到晚排列的 - [<timestamp>] VERB key=value … 行,因此:以 # Wiki Update Log 开头;按 <timestamp> 的日期部分对行分组;日期组按从新到旧排列;每行格式为 * **<Verb>**: <rest of line>,并将动词转换为标题大小写(INGEST → Ingest)。不符合此模式的行归入上一行所属日期,并在 * 后原样保留。wiki-import 会忽略 log.md,因此这不会影响往返转换。

  5. **筛选器。**遵循与图谱导出相同的项目/可见性筛选器——从数据包中省略已筛除的页面,并按步骤 2 将指向这些页面的入站链接转为纯文本。

从数据包中排除(与图谱导出相同):_archives/、_raw/、_readouts/、.obsidian/、_insights.md、_meta/*.base,以及知识库根目录的 index.md(将在上文重新生成)。


步骤 4:打印摘要

Wiki export complete → wiki-export/
  graph.json    — N nodes, M edges (NetworkX node_link format)
  graph.graphml — N nodes, M edges (Gephi / yEd / Cytoscape)
  cypher.txt    — N MERGE nodes + M MERGE relationships (Neo4j)
  postgres.sql  — N upsert rows (wiki_pages) + M upsert rows (wiki_edges) (any Postgres)
  graph.html    — interactive browser visualization (open in any browser)

仅当已生成 OKF 数据包(步骤 3.5)时,追加此行:

  okf/          — OKF v0.2 markdown bundle (N pages, lossless; import via wiki-import)

启用筛选器时,追加筛选说明:

  (filtered: project:prismor — 19 of 67 pages)
  (filtered: X of Y pages excluded — visibility/internal, visibility/pii)

仅包含实际应用的筛选器对应的行。

备注

  • 可安全地重复运行 — 每次运行都会覆盖所有输出文件(以及 okf/ 文件包)
  • 跳过失效的 wikilink — 仅导出指向库中现有页面的边;在 OKF 文件包中,指向缺失或已筛除页面的 wikilink 会降级为纯文本
  • OKF 是无损格式 — 导入时,graph.json 仅重建存根,而 okf/ 文件包会保留完整的页面正文。库与库之间的传输以及外部 Markdown 工具(MkDocs、Notion、GitHub)请使用 OKF;分析工具(Gephi、Neo4j)请使用图文件
  • 如果库受版本控制,应将 wiki-export/ 目录加入 gitignore — 这些是派生文件
  • graph.json 是主要格式 — 其他格式均由它派生。如果未来有工具原生支持图查询,请将其指向 graph.json

评论

支持 Markdown