<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cli on 潘达窝</title><link>https://daidaij.github.io/tags/cli/</link><description>Recent content in Cli on 潘达窝</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>pandazhangs</copyright><lastBuildDate>Sun, 23 Aug 2026 15:43:49 +0800</lastBuildDate><atom:link href="https://daidaij.github.io/tags/cli/index.xml" rel="self" type="application/rss+xml"/><item><title>wiki CLI 工具设计</title><link>https://daidaij.github.io/p/wiki-cli-design/</link><pubDate>Sun, 23 Aug 2026 14:47:30 +0800</pubDate><guid>https://daidaij.github.io/p/wiki-cli-design/</guid><description>&lt;img src="https://picsum.photos/seed/e782c1e9/800/600" alt="Featured image of post wiki CLI 工具设计" />&lt;h1 id="wiki-cli-工具设计">wiki CLI 工具设计
&lt;/h1>&lt;hr>
&lt;blockquote>
&lt;p>一个基于 agent hook 设计的命令行工具：会话结束 hook 自动跑同步，把项目 wiki 知识、Obsidian 校对、Hugo 博客发布串成一条自动化流水线。核心思想：hook 驱动自动化，数据面与控制流分离。&lt;/p>
&lt;/blockquote>
&lt;h2 id="动机笔记散落十几个仓库">动机：笔记散落十几个仓库
&lt;/h2>&lt;hr>
&lt;p>让 agent 做源码调研和方案分析，产出物（调研 wiki、issue 分析、踩坑记录）散落在十几个仓库的角落里，格式不一、无人索引、想找的时候不知道在哪。为每个项目 fork 一个 wiki 仓库又太重。&lt;/p>
&lt;p>my-wiki 的做法是&lt;strong>不动你的文档&lt;/strong>：笔记继续留在各自项目里（单一事实源），工具只在统一目录下维护一组目录链接，再用一张本地注册表登记每个项目的位置和介绍。任何时刻 &lt;code>grep&lt;/code> 一下就能跨项目检索，而各项目仓库保持零改动——不会把你的个人知识配置带上远程。&lt;/p>
&lt;h2 id="核心思想hook-驱动自动化优先">核心思想：hook 驱动，自动化优先
&lt;/h2>&lt;hr>
&lt;p>wiki 的定位不是&amp;quot;又一个笔记工具&amp;quot;，而是 agent 工作流里的自动同步器。设计围绕一个心跳展开：每轮会话结束，Stop hook 跑一次 &lt;code>wiki check&lt;/code>——幂等、静默、非阻塞，把项目 wiki 知识自动收进知识库。人不需要记得&amp;quot;接入&amp;quot;这件事，agent 也不需要。&lt;/p>
&lt;p>hook 驱动的前提是架构分层，wiki 把整个系统切成两层：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>数据面&lt;/strong>：知识数据本身。笔记留在各自项目里（单一事实源），注册表、发布记录、配置在 &lt;code>WIKI_ROOT&lt;/code> 指向的本地目录，&lt;code>projects/&lt;/code> 链接是机器本地的视图&lt;/li>
&lt;li>&lt;strong>控制流&lt;/strong>：工具逻辑与 agent 工作流。命令契约、钩子、规约注入——这部分可以开源、可以复制、可以升级，和数据互不污染&lt;/li>
&lt;/ul>
&lt;p>分离的直接体现是 wiki 根解析：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// config.go（简化）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">func&lt;/span> &lt;span class="nf">WikiRoot&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">env&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;WIKI_ROOT&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">env&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s">&amp;#34;&amp;#34;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">env&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 依次探测可执行文件目录、当前目录是否含 index.md 标记
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">dir&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="k">range&lt;/span> &lt;span class="p">[]&lt;/span>&lt;span class="kt">string&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">exeDir&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">cwd&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Stat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filepath&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">dir&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;index.md&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">dir&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">exeDir&lt;/span> &lt;span class="c1">// 兜底
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>工具仓库可以开源（代码 + 规约），个人数据在 &lt;code>WIKI_ROOT&lt;/code> 指向的目录，两者互不污染。换机器只要把数据目录拷过去，&lt;code>projects/&lt;/code> 链接重建一次即可。&lt;/p>
&lt;/blockquote>
&lt;p>数据面还有一个更重要的原则：&lt;strong>项目仓库零足迹&lt;/strong>。接入信息只存在 wiki 根的本地注册表，不会随项目 commit/push 泄漏到远程——你的个人知识配置永远不会出现在公开仓库里。&lt;/p>
&lt;h2 id="数据面实现注册表--目录链接">数据面实现：注册表 + 目录链接
&lt;/h2>&lt;hr>
&lt;p>注册表持久化为 &lt;code>index.md&lt;/code> 顶部的隐藏 JSON 块，其余是渲染视图：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// registry.go
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">func&lt;/span> &lt;span class="nf">saveRegistry&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">root&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reg&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Registry&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kt">error&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">b&lt;/span> &lt;span class="nx">strings&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Builder&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WriteString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;lt;!-- wiki-registry\n&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// ← JSON 数据块（机器读）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WriteString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;\n--&amp;gt;\n\n&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WriteString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;# 项目索引\n\n&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// ← 以下是 Markdown 渲染视图（人读）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">p&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="k">range&lt;/span> &lt;span class="nx">reg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Projects&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fprintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;| %s | %s | %s |\n&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Root&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Intro&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WriteFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">registryPath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">root&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">[]&lt;/span>&lt;span class="nb">byte&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">String&lt;/span>&lt;span class="p">()),&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="nx">o644&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>一个文件同时是数据源和视图，agent 和人都能读。JSON 藏在 HTML 注释里，Markdown 渲染器不会显示它，&lt;code>wiki grep&lt;/code> 也不会被它干扰。&lt;/p>
&lt;/blockquote>
&lt;p>链接层：&lt;code>projects/&amp;lt;项目&amp;gt;/&lt;/code> 下建 symlink 指向项目的 wiki/issues 目录，Windows 无权限时自动降级 junction：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">makeLink&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">link&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kt">error&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Symlink&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">link&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nx">runtime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">GOOS&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s">&amp;#34;windows&amp;#34;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">out&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">jerr&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">exec&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Command&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;cmd&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;/c&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;mklink&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;/J&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">link&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">target&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">CombinedOutput&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">jerr&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="c1">// ← junction 降级成功
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Errorf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;symlink 失败: %v；junction 降级也失败: %v: %s&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">jerr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">out&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">err&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>整体关系：&lt;/p>
&lt;pre class="mermaid" style="visibility:hidden">flowchart LR
subgraph 项目仓库["项目仓库（零足迹）"]
P1["项目A/wiki"]
P2["项目B/issues"]
end
subgraph WR["wiki 根（WIKI_ROOT）"]
R["注册表 index.md"]
L["projects/ 链接目录"]
end
P1 -- "symlink / junction" --> L
P2 -- "symlink / junction" --> L
L --> R&lt;/pre>&lt;h2 id="控制流实现为-hook-而生">控制流实现：为 hook 而生
&lt;/h2>&lt;hr>
&lt;p>控制流要回答一个问题：hook 和 agent 怎么和这个工具协作？两个设计贯穿始终。&lt;/p>
&lt;p>&lt;strong>幂等 check，为钩子而生。&lt;/strong> &lt;code>wiki check&lt;/code> 被设计为对任何钩子机制都安全：&lt;/p>
&lt;ul>
&lt;li>stdout 恒为空（部分工具会把 stdout 当 JSON 严格校验）&lt;/li>
&lt;li>日志全部走 stderr，内部错误不改变退出码&lt;/li>
&lt;li>不修改当前项目仓库的任何文件&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// EnsureRegistered 幂等地把项目接入知识库，init/register/check 共用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">func&lt;/span> &lt;span class="nf">EnsureRegistered&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">root&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">abs&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">decl&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">WikiSyncDecl&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="nx">EnsureResult&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. 跳过健康链接（EvalSymlinks 比对目标）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 2. 修复失效链接
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 3. 清理声明收缩后的孤儿链接（只删链接，绝不碰真实目录）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 4. upsert 注册表——无变化则不写盘
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>关键细节：注册表无变化则不写盘。否则每轮会话结束都触发一次文件写入，既制造噪音，也让 git 工作区永远不干净。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>宽容 flag 解析。&lt;/strong> agent 调用 CLI 时经常把位置参数放在旗标前（&lt;code>init &amp;lt;目录&amp;gt; --paths wiki&lt;/code>），标准库 flag 不接受这种顺序。&lt;code>ParseWithPositionals&lt;/code> 内部重排为「旗标在前、位置参数在后」再交给标准 FlagSet：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">ParseWithPositionals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fs&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">flag&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">FlagSet&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">args&lt;/span> &lt;span class="p">[]&lt;/span>&lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kt">error&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">flags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">pos&lt;/span> &lt;span class="p">[]&lt;/span>&lt;span class="kt">string&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">a&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">args&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">strings&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">HasPrefix&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;-&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s">&amp;#34;-&amp;#34;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">flags&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">flags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">f&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Lookup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">strings&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">TrimLeft&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;-&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span> &lt;span class="nx">f&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">bv&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">ok&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">f&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Value&lt;/span>&lt;span class="p">.(&lt;/span>&lt;span class="kd">interface&lt;/span>&lt;span class="p">{&lt;/span> &lt;span class="nf">IsBoolFlag&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="p">});&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="nx">ok&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="nx">bv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">IsBoolFlag&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="c1">// 非 bool 旗标吞掉下一个 token
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">flags&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">flags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">args&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">pos&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">pos&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">flags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">pos&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>这个函数是 agent 友好设计的缩影：CLI 的调用方是 LLM，不是人。LLM 生成命令时不会严格遵守「旗标在前」的约定，宽容解析能显著减少失败重试。&lt;/p>
&lt;/blockquote>
&lt;h2 id="打通-obsidian-和-hugo-博客仓库">打通 Obsidian 和 Hugo 博客仓库
&lt;/h2>&lt;hr>
&lt;p>知识库有两个出口：Obsidian 仓库（人阅读校对）和 Hugo 博客仓库（对外发布）。Obsidian 仓库根就是 wiki 根（pandawiki），&lt;code>projects/&lt;/code> 符号链接把各项目 wiki 聚合进来，人在里面阅读校对；校对通过的笔记才进博客流水线。调研笔记（wiki/issues）→ 知识库检索 → Obsidian 校对 → 沉淀成博客，一条链路：&lt;/p>
&lt;pre class="mermaid" style="visibility:hidden">flowchart LR
A["agent 调研产出笔记"] --> B["wiki init 接入知识库"]
B --> C["wiki grep 跨项目检索"]
C --> D["Obsidian 查看校对"]
D --> E["wiki blog new 创建文章"]
E --> F["wiki blog publish 发布"]
F --> G["GitHub Actions 自动部署"]&lt;/pre>&lt;p>&lt;code>blog new&lt;/code> 的完整流程：&lt;/p>
&lt;pre class="mermaid" style="visibility:hidden">flowchart TD
A["wiki blog new"] --> B["查重：slug / 文件名"]
B --> C["hugo new 按主题 archetype 生成模板"]
C --> D["填 title/slug/categories/tags 四字段"]
D --> E["追加正文"]
E --> F["wiki blog publish"]
F --> G["git add + commit + push"]
G --> H["GitHub Actions 自动构建部署"]&lt;/pre>&lt;p>几个关键设计：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>apply 时查重&lt;/strong>：slug 罕见重复 + 文件名存在性，先于 &lt;code>hugo new&lt;/code> 报错，避免生成一半才发现冲突&lt;/li>
&lt;li>&lt;strong>push 失败不重试&lt;/strong>：原始错误透传，文章已本地提交，用户手动补一次 &lt;code>git push&lt;/code> 即可&lt;/li>
&lt;li>&lt;strong>blog.json 懒维护&lt;/strong>：发布成功后才更新四字段记录，&lt;code>blog list&lt;/code> 据此统计分类复用频次&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// blogPublish 提交并推送；push 失败不重试
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">if&lt;/span> &lt;span class="nx">out&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;push&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;push&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Errorf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;git push 失败（不重试，原始输出透传如下）:\n%s%v\n\n&amp;#34;&lt;/span>&lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;GitHub 网络问题请用户手动处理：稍后在 %s 执行 git push 即可，文章已本地提交。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">out&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">repo&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>不重试是刻意的：push 失败几乎都是网络问题，自动重试只会放大 GitHub 的限流压力。把决定权交给人，比假装智能地重试更可靠。&lt;/p>
&lt;/blockquote>
&lt;p>知识管理对博客的反哺也在这里：&lt;code>blog list&lt;/code> 统计已有分类的使用频次，新文章优先复用——分类不会碎片化，知识库的元数据直接指导博客的写作决策。&lt;/p>
&lt;h2 id="工作流设计从调研到发布">工作流设计：从调研到发布
&lt;/h2>&lt;hr>
&lt;blockquote>
&lt;p>工具解决&amp;quot;怎么管&amp;quot;，工作流解决&amp;quot;怎么用&amp;quot;。整条链路三段：agent 驱动总结 → Obsidian 查看校对 → Hugo 发布 / git 同步。每段有明确的产出物和交接点，中间夹一道人工关卡。&lt;/p>
&lt;/blockquote>
&lt;pre class="mermaid" style="visibility:hidden">flowchart LR
A["① Agent 驱动总结"] --> B["② Obsidian 查看校对（人工关卡）"]
B --> C["③ Hugo 发布 / git 同步"]
C --> D["GitHub Actions 部署"]&lt;/pre>&lt;h3 id="agent-驱动总结">Agent 驱动总结
&lt;/h3>&lt;hr>
&lt;p>agent 调研/实践后把结论沉淀成笔记，写入项目自己的 wiki/ 目录——单一事实源，笔记跟着项目走。文风由 tech-blog skill 规范，产出物直接可读。接入零足迹：&lt;code>wiki init&lt;/code> 只写本地注册表 + 建链接；Stop hook 每轮会话结束跑 &lt;code>wiki check&lt;/code> 幂等同步，新笔记自动进知识库，不需要任何手动步骤。&lt;/p>
&lt;h3 id="obsidian-查看校对">Obsidian 查看校对
&lt;/h3>&lt;hr>
&lt;p>知识库最终要给人看。Obsidian 仓库根 = wiki 根（pandawiki），&lt;code>projects/&lt;/code> 下是各项目 wiki 的符号链接，人在 Obsidian 里阅读校对：链接通不通、结论站不站得住、有没有遗漏。这是整条链路唯一的人工关卡——agent 产出再快，发布前必须过一遍人眼。&lt;/p>
&lt;p>这个环节的关键假设是符号链接可见，实测成立（Obsidian 官方支持 symlink，约束：目标与仓库根不相交、无循环）。校对通过，笔记才进入发布。&lt;/p>
&lt;h3 id="hugo-发布--git-同步">Hugo 发布 / git 同步
&lt;/h3>&lt;hr>
&lt;p>发布走 &lt;code>wiki blog new&lt;/code> → &lt;code>wiki blog publish&lt;/code>（细节见上一节），push 成功即触发 GitHub Actions 的 hugo 构建部署。失败处理是刻意的：&lt;/p>
&lt;pre class="mermaid" style="visibility:hidden">flowchart TD
E["wiki blog publish"] --> F{"git push 成功?"}
F -- 是 --> G["GitHub Actions 构建部署"]
F -- 否 --> H["本地已提交，手动补 push"]&lt;/pre>&lt;p>push 失败不重试——几乎都是网络/代理问题，自动重试只会放大限流。git 同步管理是兜底：先查代理（本地代理没启动时 push 必挂），再手动补 push。&lt;/p>
&lt;blockquote>
&lt;p>三段产出物：笔记（项目 wiki/）→ 校对结论（人脑）→ 已发布文章（博客仓库）。交接点清晰，每段可独立重跑：agent 反复改笔记、人在 Obsidian 反复看、发布随时重来。人工关卡放在发布前而不是发布后，是这条流水线最重要的设计决定。&lt;/p>
&lt;/blockquote>
&lt;h2 id="一些取舍">一些取舍
&lt;/h2>&lt;hr>
&lt;ul>
&lt;li>&lt;strong>注册表是单机本地的&lt;/strong>，没有多用户同步——团队共享知识库不适合&lt;/li>
&lt;li>&lt;strong>刻意本地优先&lt;/strong>：要跨机器同步就用 git remote 管理数据目录，工具从不自动 push&lt;/li>
&lt;li>&lt;strong>Windows 支持&lt;/strong>：symlink 失败自动降级 junction，无需管理员权限&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>坦率说，这个工具离&amp;quot;通用知识管理&amp;quot;还有距离，但它解决了我自己的核心痛点：跨项目检索 + 博客发布自动化。工具的价值不在于功能多，而在于和 agent 工作流的契合度——每个命令都是为 LLM 调用方设计的。&lt;/p>
&lt;/blockquote>
&lt;h2 id="实践obsidian-仓库接入-sop">实践：Obsidian 仓库接入 SOP
&lt;/h2>&lt;hr>
&lt;blockquote>
&lt;p>知识库最终要给人看。Obsidian 是现成的阅读器，把 wiki 根接进去当仓库有两条路：先建仓库再配 wiki CLI（推荐），或者顺序反了之后迁移。殊途同归：Obsidian 仓库根 = wiki 根，一个目录两用。&lt;/p>
&lt;/blockquote>
&lt;h3 id="路线一先建仓库再配置-wiki-cli">路线一：先建仓库，再配置 wiki CLI
&lt;/h3>&lt;hr>
&lt;p>正确顺序是先有仓库，再让 wiki CLI 的持久化目录落进仓库：&lt;/p>
&lt;ol>
&lt;li>建一个空的 Obsidian 仓库，目录名避开 &lt;code>wiki&lt;/code>——和 &lt;code>knowledgeDirs&lt;/code> 的类型名重合，自动发现时可能把仓库目录自己误认成项目知识目录&lt;/li>
&lt;li>把 wiki CLI 持久化目录配置到仓库：&lt;code>setx WIKI_ROOT &amp;quot;D:\wiki\pandawiki&amp;quot;&lt;/code>（用户级环境变量，新终端生效）&lt;/li>
&lt;li>&lt;code>wiki init &amp;lt;项目&amp;gt;&lt;/code> 接入项目——注册表 &lt;code>index.md&lt;/code>、&lt;code>config.json&lt;/code>、&lt;code>projects/&lt;/code> 链接全部落在仓库目录里，Obsidian 里立即可见&lt;/li>
&lt;li>验证：&lt;code>wiki ls&lt;/code> 项目在册，&lt;code>obsidian vault=pandawiki folders&lt;/code> 索引完整&lt;/li>
&lt;/ol>
&lt;h3 id="路线二顺序反了迁移现有持久化目录">路线二：顺序反了，迁移现有持久化目录
&lt;/h3>&lt;hr>
&lt;p>wiki CLI 数据已经存在（比如在 &lt;code>D:\wiki&lt;/code>），迁移进 Obsidian 仓库分四步：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>迁数据&lt;/strong>：&lt;code>index.md&lt;/code>（注册表）、&lt;code>config.json&lt;/code>、&lt;code>blog.json&lt;/code>、&lt;code>projects/&lt;/code> 整个目录移进仓库&lt;/li>
&lt;li>&lt;strong>显式指定 WIKI_ROOT&lt;/strong>：wiki 根靠 &lt;code>index.md&lt;/code> 标记解析（环境变量 &amp;gt; exe 目录 &amp;gt; 当前目录），只搬文件不设环境变量，hook 会静默失效、&lt;code>wiki check&lt;/code> 空转：&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">setx WIKI_ROOT &lt;span class="s2">&amp;#34;D:\wiki\pandawiki&amp;#34;&lt;/span> &lt;span class="c1"># 用户级环境变量&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>hook 不依赖终端环境，内联设置：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cmd /c &lt;span class="s2">&amp;#34;set WIKI_ROOT=D:\wiki\pandawiki&amp;amp;&amp;amp; D:/CODE/ai/my-wiki/wiki.exe check&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ol start="3">
&lt;li>&lt;strong>注册仓库&lt;/strong>：UI 里 Open folder as vault，或直接改注册表：&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// %APPDATA%\obsidian\obsidian.json
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;vaults&amp;#34;&lt;/span>&lt;span class="p">:{&lt;/span>&lt;span class="nt">&amp;#34;c3d4bd2d547a9675&amp;#34;&lt;/span>&lt;span class="p">:{&lt;/span>&lt;span class="nt">&amp;#34;path&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;D:\\wiki\\pandawiki&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;ts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1787470151204&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;open&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">}},&lt;/span>&lt;span class="nt">&amp;#34;cli&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>&lt;code>obsidian://open?path=&lt;/code> 只能解析已注册仓库里的文件，不能注册新仓库——别在这上面浪费时间。&lt;/p>
&lt;/blockquote>
&lt;ol start="4">
&lt;li>&lt;strong>验证清单&lt;/strong>：&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>&lt;code>wiki ls&lt;/code>：项目全部在册&lt;/li>
&lt;li>&lt;code>obsidian vault=pandawiki folders/files&lt;/code>：符号链接内容全部索引（Obsidian 原生支持 symlink，约束：目标与仓库根不相交、无循环）&lt;/li>
&lt;li>最终结构：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">D:\wiki\pandawiki\ ← Obsidian 仓库根 = wiki 根
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── .obsidian/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── index.md ← 注册表（标记）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── config.json / blog.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── 欢迎.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── projects/ ← 各项目知识目录链接
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── agentscope/wiki -&amp;gt; D:\CODE\ai\agentscope\wiki
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── higress/wiki -&amp;gt; D:\CODE\ai\higress\wiki
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── ...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>两条路线殊途同归：Obsidian 仓库根 = wiki 根，一个目录两用。推荐路线一，但路线二也不复杂——迁数据、设 WIKI_ROOT、注册、验证四步，十分钟收工。&lt;/p>
&lt;/blockquote></description></item></channel></rss>