<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://udctl.com/zh-Hans/blog/</id>
    <title>udctl Blog</title>
    <updated>2026-07-30T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://udctl.com/zh-Hans/blog/"/>
    <subtitle>udctl Blog</subtitle>
    <icon>https://udctl.com/zh-Hans/img/favicon.svg</icon>
    <entry>
        <title type="html"><![CDATA[漏掉一笔之后，账本为什么越来越难继续]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/30/reconciliation-you-can-audit/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/30/reconciliation-you-can-audit/"/>
        <updated>2026-07-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[漏记真正破坏的是对账本的信任。一次可核对的对账如何找出遗漏、重复、退款和旧数据问题，把人的工作缩小到判断与确认。]]></summary>
        <content type="html"><![CDATA[<p>一笔没记上，最先坏掉的往往不是月度统计，而是你对这本账的信任。</p>
<p>你知道它已经不准了。下次打开账本时，补记这一笔还不够：前面是不是也漏了？同一笔会不会在两个渠道里各记了一次？退款到账以后，原来的支出有没有冲掉？这些问题只要有一个答不上来，继续记录就像在一张歪掉的表上补数字。</p>
<p>很多人就是从这里停下来的。不是不想记，而是不想再为一份自己都不信的结果花时间。</p>
<p>最近的一次真实对账覆盖了四个月、多个支付和银行渠道，以及上百笔已有记录。开始之前，并不知道问题藏在哪里。对完以后，漏记、重复、退款和旧数据问题同时浮了出来：</p>
<ul>
<li>有些支出出现在账单里，却没有进入账本；</li>
<li>有些消费在不同渠道各留下了一条记录；</li>
<li>有些已记支出后来被全额退回，原记录却还在统计里；</li>
<li>还有一批旧记录缺少日期，因此从按月统计中消失了。</li>
</ul>
<p>其中任何一类问题，靠回忆都很难补全。尤其是退款和缺少日期的旧记录，它们看起来不像错误，只会悄悄改变统计结果。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/reconciliation-you-can-audit/duplicate-channels.png" alt="跨渠道重复示意：同一笔消费在两个渠道和已有账本中留下相似记录，系统仅标为疑似重复并交给人确认" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="先对清楚再动账本">先对清楚，再动账本<a href="https://udctl.com/zh-Hans/blog/2026/07/30/reconciliation-you-can-audit/#%E5%85%88%E5%AF%B9%E6%B8%85%E6%A5%9A%E5%86%8D%E5%8A%A8%E8%B4%A6%E6%9C%AC" class="hash-link" aria-label="先对清楚，再动账本的直接链接" title="先对清楚，再动账本的直接链接">​</a></h3>
<p>这次对账没有让 agent 读完账单就直接改数据。</p>
<p>它先把账单和已有记录逐条比对，交出一份分析：哪些可以确认是漏记，哪些疑似跨渠道重复，哪些可能已经退款，哪些需要人决定。这个阶段只给证据，不写入、不删除。</p>
<p>人要做的是判断。某类转账算不算消费，一笔退款该怎样归类，两条重复记录保留哪一条，这些都取决于个人的记账口径。人确认处理方案之后，agent 才按确认过的清单写入和修正。</p>
<p>写完也不是结束。它重新读取账本，把处理后的笔数和金额与确认清单核对；涉及去重或删除时，每条都保留记录标识、处理原因和结果。这样可以从结论一路查回原始记录，也能发现写入过程本身有没有出错。</p>
<p>可核对，比“自动完成”重要。自动化如果只给一句“已经帮你记好了”，人仍然不知道它漏了什么、合并了什么。分析在前、确认后写入、写完再回读，才让这件事可以放心交出去。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/reconciliation-you-can-audit/auditable-flow.png" alt="可核对的对账流程：先分析并展示证据，经人确认后才写入，再回读核对笔数与总额，最后保留记录与原因" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="漏记不再意味着从头再来">漏记不再意味着从头再来<a href="https://udctl.com/zh-Hans/blog/2026/07/30/reconciliation-you-can-audit/#%E6%BC%8F%E8%AE%B0%E4%B8%8D%E5%86%8D%E6%84%8F%E5%91%B3%E7%9D%80%E4%BB%8E%E5%A4%B4%E5%86%8D%E6%9D%A5" class="hash-link" aria-label="漏记不再意味着从头再来的直接链接" title="漏记不再意味着从头再来的直接链接">​</a></h3>
<p>账本不需要靠每天零失误才能成立。月底把不同来源的账单交给 agent，它会把账单和已有记录放在一起检查，漏掉的会浮出来。偶尔忘记一笔，不会让前面的记录全部失去价值。</p>
<p>重复也不需要靠肉眼猜。相似的时间、金额和描述只能构成线索，拿不准的记录会被单独列出，留给人判断，而不是静默合并。</p>
<p>人的工作因此缩小到少数真正需要判断的地方：定口径、看证据、做确认。逐笔翻账单、交叉比对、补写、回读核对，则交给 agent。</p>
<p>当一次对账能说明它看过什么、准备改什么、实际改了什么，漏记就只是一项待处理的问题，不再是放弃整本账的理由。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Story" term="Story"/>
        <category label="agents" term="agents"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[没有服务器,手机也能随时访问自己的数据]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/26/your-desktop-is-the-server/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/26/your-desktop-is-the-server/"/>
        <updated>2026-07-26T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[0 元实现数据完全自持:UnDercontrol 桌面版内嵌后端 + 免费 Cloudflare Tunnel,iOS app 随时随地连接你自己的 endpoint。]]></summary>
        <content type="html"><![CDATA[<p>数据放在谁那里,是选工具时绕不过的一道分岔口。托管服务(SaaS)省事:注册就用,升级、备份、运维都不用操心,代价是数据存在服务商的服务器上。UnDercontrol 也会提供这样的托管服务。但如果你想要的是<strong>数据完全自持</strong>——任务、笔记、账本的原始数据只存在自己的设备上,备份是拷一个文件,迁移是换一台电脑,谁也拿不走——这篇教程就是为你写的。</p>
<p>自持通常意味着自己架服务器,而这正是大多数人放弃的地方。下面走通一条不需要服务器的 0 元路径<!-- -->:UnDercontrol<!-- --> 桌面版自带完整后端,数据以 SQLite 文件落在你自己的磁盘;Cloudflare Tunnel 免费把这台电脑暴露到公网;iOS app 直连你自己的 endpoint。人在地铁上照样看任务、补笔记、记账,而数据的存放位置从头到尾只有一个:你的电脑。不需要公网 IP,不需要路由器端口转发,也不需要买域名(想要固定地址时才可选)。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/your-desktop-is-the-server/concept-tunnel.png" alt="架构:桌面版内嵌 server,cloudflared 反向连接到 Cloudflare 边缘,iOS 从任意网络经 HTTPS 访问" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="第一步安装桌面版确认内嵌-server">第一步:安装桌面版,确认内嵌 server<a href="https://udctl.com/zh-Hans/blog/2026/07/26/your-desktop-is-the-server/#%E7%AC%AC%E4%B8%80%E6%AD%A5%E5%AE%89%E8%A3%85%E6%A1%8C%E9%9D%A2%E7%89%88%E7%A1%AE%E8%AE%A4%E5%86%85%E5%B5%8C-server" class="hash-link" aria-label="第一步:安装桌面版,确认内嵌 server的直接链接" title="第一步:安装桌面版,确认内嵌 server的直接链接">​</a></h2>
<p>从<a href="https://oatnil.com/download" target="_blank" rel="noopener noreferrer">下载页</a>安装 UnDercontrol 桌面版(macOS / Windows / Linux)。桌面版不是一个网页壳:它内嵌了完整的 Go 后端,启动 app 的同时,一个本地 server 已经在 8888 端口跑起来了(端口被占用时会自动顺延到下一个空闲端口),数据以 SQLite 文件的形式落在你自己的磁盘上。</p>
<p>首次打开你会看到「Welcome to UnDercontrol」和一个 Start 按钮——点一下就进入了。桌面版会自动创建一个本地账号 <code>personal@undercontrol.local</code>(默认密码 <code>personal123</code>),不需要注册,也没有邀请码。记住这组账号,第四步手机登录要用;默认密码在暴露公网前必须改掉,「安全提醒」一节有具体做法。</p>
<p>验证一下 server 活着:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">curl http://localhost:8888/health</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># {"service":"ud-go-production","status":"healthy"}</span><br></span></code></pre></div></div>
<p>看到 <code>healthy</code>,你的「服务器」已经就绪——它就是你正在用的这台电脑。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="第二步一条命令把它暴露到公网">第二步:一条命令,把它暴露到公网<a href="https://udctl.com/zh-Hans/blog/2026/07/26/your-desktop-is-the-server/#%E7%AC%AC%E4%BA%8C%E6%AD%A5%E4%B8%80%E6%9D%A1%E5%91%BD%E4%BB%A4%E6%8A%8A%E5%AE%83%E6%9A%B4%E9%9C%B2%E5%88%B0%E5%85%AC%E7%BD%91" class="hash-link" aria-label="第二步:一条命令,把它暴露到公网的直接链接" title="第二步:一条命令,把它暴露到公网的直接链接">​</a></h2>
<p>Cloudflare Tunnel 的原理是反向连接:你电脑上的 <code>cloudflared</code> 主动连到 Cloudflare 边缘节点,外部流量经 Cloudflare 转发进来。因为连接是从内向外发起的,所以不需要公网 IP,也不用碰路由器设置。</p>
<p>安装 cloudflared:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># macOS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">brew install cloudflared</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Windows</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">winget install Cloudflare.cloudflared</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Linux (Debian/Ubuntu):先按 https://pkg.cloudflare.com 添加软件源</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">sudo apt install cloudflared</span><br></span></code></pre></div></div>
<p>然后一条命令:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cloudflared tunnel --url http://localhost:8888</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/your-desktop-is-the-server/shot-terminal-quick-tunnel.png" alt="终端里的真实输出 tunnel 创建成功,curl 公网地址返回 healthy" class="img_ev3q"></p>
<p>几秒后终端会打印一个 <code>https://xxx.trycloudflare.com</code> 的随机地址——这就是你的公网 endpoint,自带 HTTPS,连 Cloudflare 账号都不用注册。拿手机验证一下(关掉 Wi-Fi 用流量,确认走的是公网):浏览器访问 <code>https://xxx.trycloudflare.com/health</code>,看到 <code>healthy</code> 说明整条链路通了。</p>
<p>快速隧道适合先把流程跑通:地址每次重启 <code>cloudflared</code> 都会变,Cloudflare 也不对它做可用性承诺。日常使用建议花五分钟升级成命名隧道。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="第三步推荐命名隧道把地址固定下来">第三步(推荐):命名隧道,把地址固定下来<a href="https://udctl.com/zh-Hans/blog/2026/07/26/your-desktop-is-the-server/#%E7%AC%AC%E4%B8%89%E6%AD%A5%E6%8E%A8%E8%8D%90%E5%91%BD%E5%90%8D%E9%9A%A7%E9%81%93%E6%8A%8A%E5%9C%B0%E5%9D%80%E5%9B%BA%E5%AE%9A%E4%B8%8B%E6%9D%A5" class="hash-link" aria-label="第三步(推荐):命名隧道,把地址固定下来的直接链接" title="第三步(推荐):命名隧道,把地址固定下来的直接链接">​</a></h2>
<p>命名隧道需要一个免费的 Cloudflare 账号和一个托管在 Cloudflare 的域名(几十块一年,这是整条链路里唯一可能花钱的地方)。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 1. 登录,浏览器里授权你的域名</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cloudflared tunnel login</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 2. 创建隧道</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cloudflared tunnel create ud</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 3. 把子域名指到隧道</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cloudflared tunnel route dns ud ud.example.com</span><br></span></code></pre></div></div>
<p>写一个配置文件 <code>~/.cloudflared/config.yml</code>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">tunnel</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ud</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">credentials-file</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /Users/you/.cloudflared/&lt;tunnel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain">.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">ingress</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">hostname</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ud.example.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">service</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//localhost</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">8888</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">service</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http_status</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">404</span><br></span></code></pre></div></div>
<p>跑起来,确认没问题后注册成开机自启的系统服务:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cloudflared tunnel run ud        # 先手动跑通</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">sudo cloudflared service install # 再装成服务,开机自启</span><br></span></code></pre></div></div>
<p>从此 <code>https://ud.example.com</code> 就是你的固定 endpoint。记得在系统电源设置里别让电脑自动休眠——它现在是你的服务器了。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="第四步-连上自己的-endpoint">第四步<!-- -->:iOS<!-- --> 连上自己的 endpoint<a href="https://udctl.com/zh-Hans/blog/2026/07/26/your-desktop-is-the-server/#%E7%AC%AC%E5%9B%9B%E6%AD%A5-%E8%BF%9E%E4%B8%8A%E8%87%AA%E5%B7%B1%E7%9A%84-endpoint" class="hash-link" aria-label="第四步-连上自己的-endpoint的直接链接" title="第四步-连上自己的-endpoint的直接链接">​</a></h2>
<p>iOS app 目前通过 <a href="https://testflight.apple.com/join/st2TnaBF" target="_blank" rel="noopener noreferrer">TestFlight 公测</a>安装(需要先装 App Store 里的 TestFlight)。</p>
<p>打开 app,登录页底部有一个 <strong>API server</strong> 区域,显示当前连接的服务器。点右侧的 <strong>Change</strong>,输入你的公网地址,<strong>注意要带 <code>/api/v1</code> 后缀</strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">https://xxx.trycloudflare.com/api/v1</span><br></span></code></pre></div></div>
<p>输入时下面的状态点会实时探测:变绿并显示 <code>connected · Personal</code>,说明手机已经摸到了你电脑上的 server。点 <strong>Save</strong>,然后用第一步的账号登录:<code>personal@undercontrol.local</code> / 你的密码。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/your-desktop-is-the-server/shot-ios-connected.png" alt="iOS 登录页 server 指向 trycloudflare 地址,状态 connected · Personal" class="img_ev3q"></p>
<p>登录后手机上看到的就是你电脑里的那份数据:任务、笔记、账本,手机上改一条,桌面端就是最新的。</p>
<p>一个细节<!-- -->:iOS<!-- --> 要求公网域名走 HTTPS(Cloudflare Tunnel 自带,不用操心);如果你填的是局域网或内网组网的 IP 地址,记得显式写 <code>http://</code>——不写协议时 app 默认按 <code>https://</code> 处理。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="安全提醒">安全提醒<a href="https://udctl.com/zh-Hans/blog/2026/07/26/your-desktop-is-the-server/#%E5%AE%89%E5%85%A8%E6%8F%90%E9%86%92" class="hash-link" aria-label="安全提醒的直接链接" title="安全提醒的直接链接">​</a></h2>
<p>公网暴露意味着任何拿到 URL 的人都能访问你的 endpoint。三件事,按重要程度排:</p>
<p><strong>1. 先改默认密码。</strong> 所有数据接口都要求登录,而桌面版默认账号密码 <code>personal123</code> 是公开已知的——不改就暴露公网,等于把门钥匙贴在门上。桌面版的密码由 <code>PERSONAL_TIER_PASSWORD</code> 环境变量控制,macOS 上从终端带着它启动:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">PERSONAL_TIER_PASSWORD='你的强密码' /Applications/UnderControl.app/Contents/MacOS/UnderControl</span><br></span></code></pre></div></div>
<p>Windows 在「系统环境变量」里添加 <code>PERSONAL_TIER_PASSWORD</code> 后重启 app。之后手机、CLI 都用新密码登录。</p>
<p><strong>2. 随机 URL 不是安全机制。</strong> trycloudflare 的随机地址不容易被猜到,但不要把它当作保护;分享链接前想清楚。</p>
<p><strong>3. 想再加一层门禁</strong>,Cloudflare 免费套餐里的 <a href="https://developers.cloudflare.com/cloudflare-one/policies/access/" target="_blank" rel="noopener noreferrer">Cloudflare Access</a> 可以给命名隧道套一个邮箱验证码验证,配置一次,所有设备生效。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="不想暴露公网">不想暴露公网?<a href="https://udctl.com/zh-Hans/blog/2026/07/26/your-desktop-is-the-server/#%E4%B8%8D%E6%83%B3%E6%9A%B4%E9%9C%B2%E5%85%AC%E7%BD%91" class="hash-link" aria-label="不想暴露公网?的直接链接" title="不想暴露公网?的直接链接">​</a></h2>
<p>如果你只想「自己的设备互相可达」,可以完全不碰公网:用内网组网工具把手机和电脑拉进同一个虚拟局域网——各设备装上客户端就互相可见,电脑会得到一个虚拟局域网 IP,iOS app 里把 endpoint 填成 <code>http://&lt;虚拟局域网IP&gt;:8888/api/v1</code> 就行(记得显式写 <code>http://</code>)。零公网暴露,这类工具的免费额度对个人使用通常足够。具体的组网配置这里不展开,各家官方文档都有手机 + 电脑的接入指引。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="跑通之后">跑通之后<a href="https://udctl.com/zh-Hans/blog/2026/07/26/your-desktop-is-the-server/#%E8%B7%91%E9%80%9A%E4%B9%8B%E5%90%8E" class="hash-link" aria-label="跑通之后的直接链接" title="跑通之后的直接链接">​</a></h2>
<p>盘一下成本<!-- -->:VPS<!-- --> 0 元,隧道 0 元,HTTPS 证书 0 元;唯一可选的开销是一个几十块一年的域名。</p>
<ul>
<li>桌面版下载:<a href="https://oatnil.com/download" target="_blank" rel="noopener noreferrer">https://oatnil.com/download</a></li>
<li>iOS TestFlight:<a href="https://testflight.apple.com/join/st2TnaBF" target="_blank" rel="noopener noreferrer">https://testflight.apple.com/join/st2TnaBF</a></li>
</ul>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Tutorial" term="Tutorial"/>
        <category label="Self-Hosting" term="Self-Hosting"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[把评论钉在正文的那一行上：UnDercontrol 的锚定评论]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/25/anchored-comments/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/25/anchored-comments/"/>
        <updated>2026-07-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[选中任务正文里的一句话就地评论，引文和高亮跟着原文走；线程像 code review 一样 resolve，@ 一下 agent 就能让它在同一条线程里回复。]]></summary>
        <content type="html"><![CDATA[<p>需求写完发出去，讨论就开始散架。同事在群里回一句"第三段那个字段是不是应该可空"，你得翻回文档、数到第三段、再猜他说的是哪个字段。隔天再看到这条消息，上下文已经没了。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/anchored-comments/concept-anchored.png" alt="锚定评论：讨论钉在它所讨论的那一行上" class="img_ev3q"></p>
<p>UnDercontrol 的锚定评论把讨论留在原文旁边。在任务正文里选中一句话，气泡菜单点 Comment，右边侧边栏就多出一条带着这句引文的评论，正文里那句话同时变成高亮。以后谁点这块高亮，侧边栏就跳到对应的讨论。引用信息记在评论上，正文的 markdown 一个字都没被改过，复制出去、用命令行拉下来，还是你写的那一份。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/anchored-comments/shot-1-anchored-thread.png" alt="任务正文里的高亮句子，右侧边栏是对应的锚定评论线程" class="img_ev3q"></p>
<p>一条讨论谈完，点 Resolve，高亮从正文里消失，线程折叠成一行灰字，需要时能重新打开。别人回复的时候你这边的线程会自己更新，不用刷新页面。手机上不画高亮，评论排在正文下面。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="需求评审像-review-代码一样">需求评审，像 review 代码一样<a href="https://udctl.com/zh-Hans/blog/2026/07/25/anchored-comments/#%E9%9C%80%E6%B1%82%E8%AF%84%E5%AE%A1%E5%83%8F-review-%E4%BB%A3%E7%A0%81%E4%B8%80%E6%A0%B7" class="hash-link" aria-label="需求评审，像 review 代码一样的直接链接" title="需求评审，像 review 代码一样的直接链接">​</a></h3>
<p>评审的人不用再写"第三段第二句那里……"。选中"支持批量导入"这一行，问一句"上限是多少"，作者在同一条线程里回，谈拢就 resolve 掉。</p>
<p>一份需求过完一遍，页面上还亮着的高亮就是没谈拢的地方。不用再单独整理一份会议纪要，剩下的未解决线程本身就是待办清单。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/anchored-comments/shot-2-review-pass.png" alt="评审过一遍之后的评论侧边栏：已解决的线程折叠成灰字，剩下的是还没谈拢的" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="指着一行让-agent-去改">指着一行让 agent 去改<a href="https://udctl.com/zh-Hans/blog/2026/07/25/anchored-comments/#%E6%8C%87%E7%9D%80%E4%B8%80%E8%A1%8C%E8%AE%A9-agent-%E5%8E%BB%E6%94%B9" class="hash-link" aria-label="指着一行让 agent 去改的直接链接" title="指着一行让 agent 去改的直接链接">​</a></h3>
<p>这是和普通评论区最不一样的地方。选中要改的那段需求，留一条评论 @ 上你的 agent，写清楚改成什么样。这个 @ 会拉起一个 agent 会话（本来在跑的就接着用），它干完活之后，回复就是这条线程里的下一条评论，和同事的回复排在一起，commit hash 也贴在里面。你点一下高亮，就回到了被改的那段原文，能直接对照。</p>
<p>人和 agent 在同一条线程里说话，不用你在两个工具之间来回转述。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/anchored-comments/shot-3-agent-thread.png" alt="人在评论里 @ 了 agent，agent 的回复带着 commit hash 排在同一条线程里" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="长文档的分段讨论">长文档的分段讨论<a href="https://udctl.com/zh-Hans/blog/2026/07/25/anchored-comments/#%E9%95%BF%E6%96%87%E6%A1%A3%E7%9A%84%E5%88%86%E6%AE%B5%E8%AE%A8%E8%AE%BA" class="hash-link" aria-label="长文档的分段讨论的直接链接" title="长文档的分段讨论的直接链接">​</a></h3>
<p>任务正文当设计文档写，每次迭代记一条 Note。讨论各自锚在自己那一段上：正文的评论一组，每条 Note 的评论各一组，没有锚点的闲聊单独一组。三个月后回来看，某个决定当初为什么这么定，就写在那句话旁边。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="终端里也能回同一条线程">终端里也能回同一条线程<a href="https://udctl.com/zh-Hans/blog/2026/07/25/anchored-comments/#%E7%BB%88%E7%AB%AF%E9%87%8C%E4%B9%9F%E8%83%BD%E5%9B%9E%E5%90%8C%E4%B8%80%E6%9D%A1%E7%BA%BF%E7%A8%8B" class="hash-link" aria-label="终端里也能回同一条线程的直接链接" title="终端里也能回同一条线程的直接链接">​</a></h3>
<p>评论在终端里同样能读能写。ud CLI 把评论当成和任务一样的资源，人在网页上留的讨论，agent 在终端里读得到，也能回：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 读一个任务下的所有讨论</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud get comments --task 02c137b3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 回复某条线程</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt;'EOF' | ud apply -f -</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">task_id: 02c137b3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">parent_id: 7a1c9f2e</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">按用户维度限流改完了，commit a1b2c3d4。</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<p>Claude Code、Codex、OpenCode 或者任何跑在终端里的 agent 都不需要额外的适配层。谈完了用 <code>ud patch comment &lt;id&gt; --status resolved</code> 收尾。</p>
<p>锚定评论从 v0.88.0 起可用，网页端、桌面端和 CLI 都已经支持。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="collaboration" term="collaboration"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[把任务当文件快递柜——发文件到不能登录的设备]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/24/file-drop-share/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/24/file-drop-share/"/>
        <updated>2026-07-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[把文件挂在任务上,生成公开分享链接:链接 + 6 位取件码 + 二维码,任何设备不登录即可查看下载,附件走 15 分钟预签名 URL。]]></summary>
        <content type="html"><![CDATA[<p>去打印店打印文件,店里的电脑不是你的,你不会在上面登录任何账号。这时候把文件弄过去的常见办法都有点别扭:微信文件传输助手要扫码登录,网盘分享页一半功能在催你装客户端,U 盘不一定随身带。</p>
<p>UnDercontrol 里的任务本来就是个容器——正文、笔记、附件都挂在上面。给任务生成一个公开分享链接,拿到链接的人不用登录、不用装任何东西,打开就能看内容、下载文件。用起来像小区里的快递柜:你把东西放进去,拿到一个取件码,凭码取件。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/file-drop-share/concept-locker.png" alt="概念图:任务作为文件快递柜" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="第一步把文件挂到任务上">第一步:把文件挂到任务上<a href="https://udctl.com/zh-Hans/blog/2026/07/24/file-drop-share/#%E7%AC%AC%E4%B8%80%E6%AD%A5%E6%8A%8A%E6%96%87%E4%BB%B6%E6%8C%82%E5%88%B0%E4%BB%BB%E5%8A%A1%E4%B8%8A" class="hash-link" aria-label="第一步:把文件挂到任务上的直接链接" title="第一步:把文件挂到任务上的直接链接">​</a></h3>
<p>新建一个任务,在附件区上传文件。任务正文是 Markdown,顺手把说明写清楚——取件的人打开链接,看到的不只是文件列表,还有你写的打印要求。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/file-drop-share/task-detail.png" alt="任务详情:附件与打印说明" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="第二步生成分享链接">第二步:生成分享链接<a href="https://udctl.com/zh-Hans/blog/2026/07/24/file-drop-share/#%E7%AC%AC%E4%BA%8C%E6%AD%A5%E7%94%9F%E6%88%90%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5" class="hash-link" aria-label="第二步:生成分享链接的直接链接" title="第二步:生成分享链接的直接链接">​</a></h3>
<p>任务菜单里选 <strong>Share to Public</strong>,两个设置:</p>
<ul>
<li><strong>有效期</strong>:1 小时 / 1 天 / 7 天 / 永久</li>
<li><strong>是否允许下载附件</strong>:默认关,发文件记得勾上</li>
</ul>
<p>点 Generate Link,一次拿到三样东西:完整链接、6 位取件码,以及一段拼好的分享消息(Copy Share Message,链接和取件码都在里面,直接粘贴发给对方)。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/file-drop-share/share-modal.png" alt="分享弹窗:链接、取件码、有效期" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="第三步在任何设备上取件">第三步:在任何设备上取件<a href="https://udctl.com/zh-Hans/blog/2026/07/24/file-drop-share/#%E7%AC%AC%E4%B8%89%E6%AD%A5%E5%9C%A8%E4%BB%BB%E4%BD%95%E8%AE%BE%E5%A4%87%E4%B8%8A%E5%8F%96%E4%BB%B6" class="hash-link" aria-label="第三步:在任何设备上取件的直接链接" title="第三步:在任何设备上取件的直接链接">​</a></h3>
<p>三种打开方式,对应三种场合:</p>
<ul>
<li><strong>有链接</strong>:直接打开,只读页面,标题、正文、笔记、附件都在,每个附件一个下载按钮</li>
<li><strong>只有取件码</strong>:打开分享入口页,输 6 位字母码。适合口头转述,或者在公用电脑上不想敲长网址</li>
<li><strong>面对面</strong>:分享页右上角自带二维码,手机扫一下就到同一个页面</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/file-drop-share/share-viewer.png" alt="公开分享页:二维码、倒计时、附件下载" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/file-drop-share/code-entry.png" alt="取件码入口页" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="安全边界">安全边界<a href="https://udctl.com/zh-Hans/blog/2026/07/24/file-drop-share/#%E5%AE%89%E5%85%A8%E8%BE%B9%E7%95%8C" class="hash-link" aria-label="安全边界的直接链接" title="安全边界的直接链接">​</a></h3>
<ul>
<li>链接里的 token 是 32 字节随机数(64 个十六进制字符),枚举不出来</li>
<li>到期自动失效;弹窗里能看到每个链接的剩余时间,也能随时撤销单个链接</li>
<li>附件下载走预签名 URL,每次点击现生成,15 分钟内有效——文件本身在存储里从不公开</li>
<li>分享页只读,只包含标题、正文、笔记和附件,不暴露账号信息,评论不会被分享</li>
<li>自部署实例可以在系统配置里整体关闭公开分享功能</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="也能当文档快递柜">也能当文档快递柜<a href="https://udctl.com/zh-Hans/blog/2026/07/24/file-drop-share/#%E4%B9%9F%E8%83%BD%E5%BD%93%E6%96%87%E6%A1%A3%E5%BF%AB%E9%80%92%E6%9F%9C" class="hash-link" aria-label="也能当文档快递柜的直接链接" title="也能当文档快递柜的直接链接">​</a></h3>
<p>任务正文和笔记都是 Markdown,和 UnDercontrol 里其他地方(笔记、账本)用的是同一套编辑器。所以分享出去的不一定是文件:会议纪要、装机清单、旅行攻略,对方打开就是排版好的只读页面。文件和文档,走的是同一个链接。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="典型场景">典型场景<a href="https://udctl.com/zh-Hans/blog/2026/07/24/file-drop-share/#%E5%85%B8%E5%9E%8B%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="典型场景的直接链接" title="典型场景的直接链接">​</a></h3>
<ul>
<li><strong>打印店</strong>:PDF<!-- --> 挂到任务上,到店输取件码下载打印,回家撤销链接</li>
<li><strong>给没有账号的人发文件</strong>:Copy<!-- --> Share Message 粘贴过去,一条消息带齐链接和取件码</li>
<li><strong>会议室公用电脑</strong>:在电脑上输 6 位码打开只读文档页;散会时谁想带走一份,扫页面上的二维码就到同一页</li>
<li><strong>外部合作方交付</strong>:选 7 天有效期,到期自动断,不用记得回来删</li>
<li><strong>终端 AI agent 的产物</strong>(Claude Code、Codex、OpenCode 或任何终端 agent):跑完把报告和文件挂到任务上,你审完拿链接转发给相关的人</li>
</ul>
<p>功能入口在任务详情页的 ⋮ 菜单里,名字叫 Share to Public;自部署用户需要确认系统配置里的 public share 开关处于打开状态(默认打开)。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="sharing" term="sharing"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Agent 的 Agent：当 AI 学会带团队]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/20/agent-of-agents-team-lead/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/20/agent-of-agents-team-lead/"/>
        <updated>2026-07-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[配了三个 agent，产出没翻三倍，你自己成了路由器。lead 会话一启动就被注入花名册，靠两条通道委派，组织树还能递归生长——你的活从指挥变成写岗位说明书。]]></summary>
        <content type="html"><![CDATA[<p>你配了三个 agent，本以为效率会翻三倍。结果一天下来，你在做这几件事：把 A 的结论复制给 B、想起来该催 C 了、同时读三份产出、在脑子里维护"谁在干什么"。</p>
<p><strong>agent 变多了，瓶颈从"干活的人不够"变成了"管事的人只有你一个"。</strong></p>
<p>这不是配置问题，是组织结构问题。一个人直接带十个下属，在人类公司里也不成立——解法从来不是招更强的员工，而是<strong>多一层管理</strong>。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/agent-of-agents-team-lead/concept-hero.png" alt="一个人指挥 N 个 agent，和让 lead 替你路由" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="lead-不是一个更聪明的-agent是一个知道花名册的-agent">lead 不是一个更聪明的 agent，是一个知道花名册的 agent<a href="https://udctl.com/zh-Hans/blog/2026/07/20/agent-of-agents-team-lead/#lead-%E4%B8%8D%E6%98%AF%E4%B8%80%E4%B8%AA%E6%9B%B4%E8%81%AA%E6%98%8E%E7%9A%84-agent%E6%98%AF%E4%B8%80%E4%B8%AA%E7%9F%A5%E9%81%93%E8%8A%B1%E5%90%8D%E5%86%8C%E7%9A%84-agent" class="hash-link" aria-label="lead 不是一个更聪明的 agent，是一个知道花名册的 agent的直接链接" title="lead 不是一个更聪明的 agent，是一个知道花名册的 agent的直接链接">​</a></h3>
<p>在 UnDercontrol 里，你可以把若干 agent 编成一个 <strong>Team</strong>：一个 <strong>lead</strong>，若干 <strong>成员</strong>，每个成员带一条 <strong>delegation hint</strong>——"什么活该路由到这儿"。</p>
<p>这条 hint 就是岗位说明书。它不长，但它决定了 lead 会不会把活派对人：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">- @db-expert [reviewer] — schema 变更、慢查询、迁移脚本</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- @ud-tester — 需求验收、黑盒回归，别让它改代码</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- @infra-ops [remote — mention only] — 服务器、部署、DNS</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/agent-of-agents-team-lead/app-teams.png" alt="Team 配置 — 一个 lead、四个成员，每人一条分工提示" class="img_ev3q"></p>
<p>关键在于：<strong>lead 的会话一启动，这份花名册就被注入到它的初始提示词里</strong>。它不需要你在对话里介绍团队，也不需要每次现学——它天生就知道自己手下有谁、谁擅长什么、以及该怎么把活交出去。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/agent-of-agents-team-lead/app-injected-prompt.png" alt="花名册进入 lead 提示词时的样子" class="img_ev3q"></p>
<p>同时被注入的还有一句朴素但要命的指令：<strong>"作为 lead，能交给成员的就别自己写。你的工作是协调、跟用户沟通、审专家的产出。只有没人覆盖这块活的时候，才自己动手。"</strong></p>
<p>如果你带过团队你会知道，这句话正是新晋 leader 最难学会的一课。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="两条委派通道一条快一条留痕">两条委派通道：一条快，一条留痕<a href="https://udctl.com/zh-Hans/blog/2026/07/20/agent-of-agents-team-lead/#%E4%B8%A4%E6%9D%A1%E5%A7%94%E6%B4%BE%E9%80%9A%E9%81%93%E4%B8%80%E6%9D%A1%E5%BF%AB%E4%B8%80%E6%9D%A1%E7%95%99%E7%97%95" class="hash-link" aria-label="两条委派通道：一条快，一条留痕的直接链接" title="两条委派通道：一条快，一条留痕的直接链接">​</a></h3>
<p>这是很多人想不到的一层设计。委派并不只有一种做法，lead 手上有两条通道，默认走快的那条：</p>
<p><strong>通道一：原生子 agent（默认）</strong></p>
<p>lead 先跑一句 <code>ud describe agent &lt;name&gt; -o prompt</code> 把成员的身份提示词取出来，再用宿主工具<strong>自己原生的子 agent 能力</strong>把它拉起来，附上任务上下文。子 agent 跑在 lead 自己的会话和权限里，结果由 lead 汇总，并在汇报时注明"via @某成员"。</p>
<p>分工很清楚：<strong>ud 提供身份、技能和上下文，宿主工具负责执行。</strong> 快、同步、没有额外开销——适合"帮我看一眼这段 SQL"这种一问一答的活。</p>
<p><strong>通道二：子任务 + @提及（升级通道）</strong></p>
<p>当出现下面任一情况，lead 会升级到第二条通道：</p>
<ul>
<li>成员被钉在<strong>另一台机器</strong>上（比如只在生产跳板机上跑的运维 agent，花名册里标着 <code>[remote — mention only]</code>）</li>
<li>活是<strong>长跑</strong>，要活得比 lead 这次会话更久</li>
<li>你需要这件事有<strong>独立的问责线程</strong>，事后能单独翻账</li>
</ul>
<p>做法是：建子任务 → <code>ud link task &lt;parent&gt; &lt;child&gt; --subtask</code> → 在子任务的评论里 @ 那个成员。@提及会自动为对方拉起一个 workspace 会话，<strong>并且委派链保留你的权限范围</strong>——成员拿到的访问权限不会比你更大。它干完把结论回到线程里。</p>
<p>一句话记：<strong>要快就用子 agent，要留痕、要跨机器、要跑得久就用子任务 + @。</strong></p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/agent-of-agents-team-lead/concept-channels.png" alt="两条委派通道对比" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="递归agent-的-agent-的-agent">递归：agent 的 agent 的 agent<a href="https://udctl.com/zh-Hans/blog/2026/07/20/agent-of-agents-team-lead/#%E9%80%92%E5%BD%92agent-%E7%9A%84-agent-%E7%9A%84-agent" class="hash-link" aria-label="递归：agent 的 agent 的 agent的直接链接" title="递归：agent 的 agent 的 agent的直接链接">​</a></h3>
<p>Team 可以嵌套——一个成员自己也可以是另一个 Team 的 lead。于是层级自然生长，而<strong>每个 agent 只需要认识自己的直属下属</strong>，不需要知道整棵树。这跟真实组织的运作方式是一样的。</p>
<p>更进一步：默认的 <code>ud</code> agent 是整个组织的<strong>根管理者</strong>。除了它自己明确带的队，<strong>所有顶层 team lead 都会被自动注入成它的直属下属</strong>——于是不管你的组织长到几层，从 <code>ud</code> 出发整棵树都是可达的。</p>
<p>你不需要记住该 @ 谁。@ 一下 <code>ud</code>，它自己往下找。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/agent-of-agents-team-lead/concept-tree.png" alt="组织树 — 每个节点只认识自己的直属下属" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="于是你的工作变了">于是你的工作变了<a href="https://udctl.com/zh-Hans/blog/2026/07/20/agent-of-agents-team-lead/#%E4%BA%8E%E6%98%AF%E4%BD%A0%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8F%98%E4%BA%86" class="hash-link" aria-label="于是你的工作变了的直接链接" title="于是你的工作变了的直接链接">​</a></h3>
<p>从"指挥 N 个 agent"变成了三件更像管理者的事：</p>
<ol>
<li><strong>写岗位说明书。</strong> delegation hint 写得含糊，活就派错人。写清楚"什么活来这儿、什么活别来"，比给 agent 换个更贵的模型有用得多。</li>
<li><strong>定验收标准。</strong> 你审的是产出，不是过程——标准写在任务描述里，lead 拿它去验成员，你拿它去验 lead。</li>
<li><strong>守住 review 关口。</strong> 委派链再深，最后签字的还是你。</li>
</ol>
<p>顺带一提，agent 之间来回写的这些东西——任务描述、评论、备注——跟你手写的内容共用同一套 Markdown 系统。而且不只是任务：备注、账目、账户……UnDercontrol 里所有能写字的地方，编辑体验都是同一个。人和 agent 读写的是同一份东西，没有"给 AI 看的格式"和"给人看的格式"之分。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="几个容易踩的坑">几个容易踩的坑<a href="https://udctl.com/zh-Hans/blog/2026/07/20/agent-of-agents-team-lead/#%E5%87%A0%E4%B8%AA%E5%AE%B9%E6%98%93%E8%B8%A9%E7%9A%84%E5%9D%91" class="hash-link" aria-label="几个容易踩的坑的直接链接" title="几个容易踩的坑的直接链接">​</a></h3>
<ul>
<li><strong>lead 自己把活干了。</strong> 最常见的失败模式。通常是因为 delegation hint 太空泛，lead 判断不出该给谁，索性自己上。</li>
<li><strong>对方机器不在线，@ 就静默失效。</strong> 提及<strong>不会排队等机器上线</strong>——投递会被直接判定为 daemon 离线丢掉。派远程活之前先确认那台机器连着。</li>
<li><strong>同一个任务上，同一个成员只能有一个活跃会话。</strong> 想让一个 agent 并行推两件事，就得拆成两个任务，而不是在一个任务里 @ 它两次。</li>
<li><strong>嵌套团队目前没有环检测。</strong> A 带 B、B 又带 A 这种配置，系统不会拦你。层级自己心里有数——三层基本到顶了，再深你在 review 时就看不懂链条中间发生了什么。</li>
<li><strong>review 债。</strong> 委派得越顺，待审的产出堆得越快。管理的瓶颈会从"派活"转移到"验收"——这一段目前没法外包。</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="说到底">说到底<a href="https://udctl.com/zh-Hans/blog/2026/07/20/agent-of-agents-team-lead/#%E8%AF%B4%E5%88%B0%E5%BA%95" class="hash-link" aria-label="说到底的直接链接" title="说到底的直接链接">​</a></h3>
<p>选什么 agent 当 lead 都行。Claude Code、Codex、OpenCode，任何终端 agent 都可以是 lead 或成员——角色由提示词和技能定义，工具由 Agent CLI 配置决定，而它们全都通过同一个 <a href="https://udctl.com/zh-Hans/docs/cli-ai-integration/">AI Agent CLI</a> 与这个组织对话。UnDercontrol 不绑定任何一家 AI。</p>
<p>它只负责一件事：<strong>当 agent 多到需要被管理的时候，给你一层管理结构，而不是让你自己去当那层结构。</strong></p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="agents" term="agents"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[让 Claude 规划任务，Codex 去执行]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/19/claude-plans-codex-executes/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/19/claude-plans-codex-executes/"/>
        <updated>2026-07-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[把两份 AI 订阅编成一个团队：planner 拆解需求写验收标准，@提及自动委派，executor 写代码提交，你只管审核。Agent Teams 让 lead 自动认识自己的花名册。]]></summary>
        <content type="html"><![CDATA[<p>很多人已经在这么用 AI 了：在 Claude Code 里把方案讨论清楚，再把结论复制到 Codex 里执行。理由也很实际——两家的订阅都买了，Claude 擅长规划，Codex 干活不占 Claude 的额度，两份订阅都物尽其用。唯一的问题是：中间那个"复制粘贴"的角色，是你自己。</p>
<p><strong>在 UnDercontrol 里，这条流水线是开箱即用的</strong>：Claude 负责规划，Codex 负责执行，任务系统就是它们之间共享的白板——你只需要在关键节点审核。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/claude-plans-codex-executes/concept-hero.png" alt="Claude 规划、Codex 执行 — 任务是 AI 团队共享的白板" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="五步跑通">五步跑通<a href="https://udctl.com/zh-Hans/blog/2026/07/19/claude-plans-codex-executes/#%E4%BA%94%E6%AD%A5%E8%B7%91%E9%80%9A" class="hash-link" aria-label="五步跑通的直接链接" title="五步跑通的直接链接">​</a></h3>
<ol>
<li><strong>配两个 agent。</strong> Claude Code、Codex、OpenCode 都是内置的 Agent CLI 模板，选一下就行（也支持任意自定义命令）。给 planner 配 Claude Code，给 executor 配 Codex。嫌配置麻烦？连这一步都可以外包——系统内置了 <strong>Agent Creator</strong>，@一下它、说清你要的组合，它帮你把 agent 建好。</li>
<li><strong>把需求丢给 planner。</strong> 任务写得糙一点没关系，@planner 一下。守护进程会在你的开发机上拉起 Claude Code 会话：读描述、澄清需求、拆成带验收标准的子任务。</li>
<li><strong>planner 委派给 executor。</strong> 在子任务上 @executor —— agent 之间的 @提及 会自动为对方拉起新的 workspace session，委派链还保留你的权限范围。</li>
<li><strong>Codex 执行。</strong> 每个会话领一个子任务：写代码、跑测试、提交 commit、把进度写回备注，完成后置为 <code>pending</code>。</li>
<li><strong>你只在两个点介入。</strong> 确认拆解、审核产出。</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/claude-plans-codex-executes/concept-flow.png" alt="从一句需求到待审核的 commit — 一条流水线" class="img_ev3q"></p>
<p>整个协作过程就发生在任务上——描述是 spec，评论是对话，备注是进度日志。下面是一个真实的任务详情：左边是 planner 拆解后的验收标准，右边是三方的评论线程：</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/claude-plans-codex-executes/app-task.png" alt="任务详情 — planner 拆解 spec，executor 汇报 commit，全程留痕" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="编队agent-teams">编队：Agent Teams<a href="https://udctl.com/zh-Hans/blog/2026/07/19/claude-plans-codex-executes/#%E7%BC%96%E9%98%9Fagent-teams" class="hash-link" aria-label="编队：Agent Teams的直接链接" title="编队：Agent Teams的直接链接">​</a></h3>
<p>不想每次手动指挥两个 agent？把它们编成一个 <strong>Team</strong>。</p>
<p>一个 Team 有一个 <strong>lead</strong> 和若干 <strong>成员</strong>，每个成员带一条"什么活路由给它"的分工提示（delegation hint）。当 lead 的会话启动时，它的花名册会自动注入到提示词里——<strong>lead 天生就知道自己手下有谁、谁擅长什么、怎么委派</strong>。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/claude-plans-codex-executes/app-teams.png" alt="Agent Teams — lead + 成员分工提示，花名册自动注入 lead 的会话" class="img_ev3q"></p>
<p>于是"Claude 规划、Codex 执行"变成一句话的事：@一下 dev-team 的 lead，剩下的它自己安排。团队还可以嵌套——某个成员自己也可以是另一个 Team 的 lead，层级自然生长，而每个 agent 只需要认识自己的直属下属。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="全程可观测">全程可观测<a href="https://udctl.com/zh-Hans/blog/2026/07/19/claude-plans-codex-executes/#%E5%85%A8%E7%A8%8B%E5%8F%AF%E8%A7%82%E6%B5%8B" class="hash-link" aria-label="全程可观测的直接链接" title="全程可观测的直接链接">​</a></h3>
<p>开一个看板盯全局：拆了多少、执行到哪、几个待审核，一眼看清。每个 agent 会话的终端输出也会实时流回 Web 端——在电脑上、甚至手机上都能看。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/claude-plans-codex-executes/app-board.png" alt="AI 团队交付看板 — Planning / Executing / Pending Review / Done" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="典型场景">典型场景<a href="https://udctl.com/zh-Hans/blog/2026/07/19/claude-plans-codex-executes/#%E5%85%B8%E5%9E%8B%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="典型场景的直接链接" title="典型场景的直接链接">​</a></h3>
<ul>
<li><strong>睡前排产，醒来验收。</strong> 晚上把三个需求丢给 planner，早上收到拆好的子任务和几个 <code>pending</code> 的实现，每个都带独立 commit。</li>
<li><strong>大重构切片。</strong> Claude 出迁移方案拆成 20 个小任务，Codex 逐个执行，哪步出问题回滚哪步。</li>
<li><strong>Bug 分诊流水线。</strong> planner 复现、定位、写清 root cause，再委派修复——执行方拿到的是"改哪里、为什么、怎么验证"。</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="组合是你定的">组合是你定的<a href="https://udctl.com/zh-Hans/blog/2026/07/19/claude-plans-codex-executes/#%E7%BB%84%E5%90%88%E6%98%AF%E4%BD%A0%E5%AE%9A%E7%9A%84" class="hash-link" aria-label="组合是你定的的直接链接" title="组合是你定的的直接链接">​</a></h3>
<p>Claude + Codex 只是一种搭配。UnDercontrol 不绑定任何 AI 工具——Claude Code、Codex、OpenCode 或任意终端 agent 都能当 lead 或成员，因为它们都通过同一个 <a href="https://udctl.com/zh-Hans/docs/cli-ai-integration/">AI Agent CLI</a> 接上那块共享任务板；角色由 prompt 和 skill 决定，工具由 Agent CLI 配置决定。手里有哪些订阅、哪个模型擅长什么，编排权都在你。</p>
<p>顺带一提：agent 写回的进度和你手写的内容在同一套 Markdown 体系里——任务、备注、评论、账目，所有文本面共用同一个编辑体验。</p>
<hr>
<p>想试试？<a href="https://oatnil.com/docs/download" target="_blank" rel="noopener noreferrer">下载 UnDercontrol</a>，跑起守护进程，建一个 Team，然后 @lead 你的第一个任务。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="agents" term="agents"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Virtual Path：让任务沉淀成知识和文档]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/18/virtual-path-tasks-become-knowledge/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/18/virtual-path-tasks-become-knowledge/"/>
        <updated>2026-07-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[任务不该死在 Done 那一刻。Virtual Path 把做完的任务归档进策展过的知识树，Agent 能把一批任务浓缩成一篇文档，git 风格的 vault sync 把整棵树同步成本地离线的 Obsidian 原生 Markdown。]]></summary>
        <content type="html"><![CDATA[<p>我见过太多任务在"Done"的那一刻死掉。卡片一归档，里面的东西就再也没人看了——当时为什么这么设计、踩了什么坑、最后怎么验证的，全埋在里面。过两个月有人问起，只能凭记忆再讲一遍。</p>
<p>做 UnDercontrol 的时候我一直有个执念：任务不该是一次性的。它的正文本来就是一份 Markdown 文档，Notes 记着它怎么一步步演进，评论里留着当时的争论。这些东西做完就扔，太浪费了。</p>
<p>所以有了 Virtual Path。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/virtual-path-tasks-become-knowledge/concept-1.png" alt="任务的终点不是 Done，而是知识树里的一个位置" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="给任务一个位置">给任务一个位置<a href="https://udctl.com/zh-Hans/blog/2026/07/18/virtual-path-tasks-become-knowledge/#%E7%BB%99%E4%BB%BB%E5%8A%A1%E4%B8%80%E4%B8%AA%E4%BD%8D%E7%BD%AE" class="hash-link" aria-label="给任务一个位置的直接链接" title="给任务一个位置的直接链接">​</a></h3>
<p>想法很简单：每个任务、每个上传的文件、甚至每个 Skill，都可以有一个虚拟路径，比如 <code>/engineering/backend/</code>。有路径的东西汇成一棵树，这就是 Explorer——长得像文件管理器，但底下没有真的目录。路径在哪，"文件夹"就在哪；内容删光了，文件夹自己消失。你永远不用维护一堆空目录。</p>
<p>任务做完了，值得留，就拖进 <code>/decisions/</code> 或者 <code>/engineering/</code>——它从一张完成的卡片变成了知识库里的一篇文档。不用复制粘贴到别的笔记软件，Notes 和评论都还挂在上面。</p>
<p>不值得留的呢？不用管。没归类的任务待在树根和"未归档"区，不会混进你整理好的目录。所以这棵树是挑出来的，不是堆出来的——什么东西配得上一个位置，你说了算。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/virtual-path-tasks-become-knowledge/explorer.jpg" alt="Explorer 树 + 任务即文档：左侧是知识树，右侧是一篇沉淀下来的架构文档" class="img_ev3q"></p>
<p>架构图、截图、白板照片这些文件也能放进同一个路径，一个专题的文字和素材挨在一起，省得回头去网盘里翻"那张图到底存哪了"。</p>
<p>顺带说一句，这套逻辑不只对任务成立。UnDercontrol 里所有能写字的地方——任务、笔记、记账、账户备注——用的是同一个 Markdown 编辑器，所以任何内容都能走这条路沉淀下来。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="多个任务一篇文档">多个任务，一篇文档<a href="https://udctl.com/zh-Hans/blog/2026/07/18/virtual-path-tasks-become-knowledge/#%E5%A4%9A%E4%B8%AA%E4%BB%BB%E5%8A%A1%E4%B8%80%E7%AF%87%E6%96%87%E6%A1%A3" class="hash-link" aria-label="多个任务，一篇文档的直接链接" title="多个任务，一篇文档的直接链接">​</a></h3>
<p>更常见的情况其实是这样：一个专题做下来七八个任务，每个里面都有点零散的发现，但没有一个能直接当文档用。</p>
<p>这种活我现在都交给 Agent。Claude Code、Codex、OpenCode，随便哪个终端 Agent，都能通过 ud CLI 把这批任务通读一遍，提炼成一篇新文档，放进知识树对应的目录。原始任务一个不动，留着当原材料，想追溯随时能翻。知识库里存的是蒸馏过的结论，不是过程的堆积。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="同步到本地离线也是你的">同步到本地，离线也是你的<a href="https://udctl.com/zh-Hans/blog/2026/07/18/virtual-path-tasks-become-knowledge/#%E5%90%8C%E6%AD%A5%E5%88%B0%E6%9C%AC%E5%9C%B0%E7%A6%BB%E7%BA%BF%E4%B9%9F%E6%98%AF%E4%BD%A0%E7%9A%84" class="hash-link" aria-label="同步到本地，离线也是你的的直接链接" title="同步到本地，离线也是你的的直接链接">​</a></h3>
<p>知识放在服务器上只是第一步。我自己的习惯是：文档一定要能落到自己的磁盘上，用自己顺手的编辑器改。</p>
<p>ud CLI 的同步是 git 风格的（基础用法之前写过一篇：<a href="https://oatnil.com/zh-Hans/blog/2026/07/07/ud-pull-push/" target="_blank" rel="noopener noreferrer">像 git 一样管理任务：ud pull / push</a>）：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud init /Knowledge/   # 把当前文件夹绑定到一个虚拟路径</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud pull               # 拉取：任务变成本地 .md 文件</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud push               # 推送：本地修改写回服务器</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud status             # 看看两边差了什么</span><br></span></code></pre></div></div>
<p>现在默认的 vault layout 是 Obsidian 原生的：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">MyVault/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├─ engineering/backend/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│  └─ SSE reconnect backoff.md     ← 一个 .md = 一个任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├─ notes/SSE reconnect backoff/    ← 任务的 Notes，按任务分组</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│  └─ soak test results.md</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├─ attachments/diagram.png         ← 资源文件（--fetch-attachments 拉取）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">└─ .udignore</span><br></span></code></pre></div></div>
<p>pull 下来之后，这就是一个完全离线的本地知识库。纯 Markdown 躺在磁盘上，断网能读能改，飞机上照样写，落地一句 <code>ud push</code> 同步回去。数据是你的，两份都是。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/virtual-path-tasks-become-knowledge/concept-2.png" alt="vault sync：服务器上的虚拟路径树 ⇄ 本地 Obsidian vault" class="img_ev3q"></p>
<p>细节上花了不少功夫，挑几个说：ud 只在文件头上写一行 <code>ud-id</code>，其余 frontmatter 原样保留；本地把文件改个名，push 之后任务标题跟着变；Obsidian 里写的 <code>[[双链]]</code>，push 时自动翻译成 ud 的 <code>task://</code> 链接，pull 时再翻回来，两边点击都能跳转；不想同步的文件写进 <code>.udignore</code>，gitignore 语法；冲突处理也是 git 式的——两边都改过的条目会被标记出来默认跳过，绝不悄悄覆盖你的修改。老用户想从旧目录结构切过来，<code>ud migrate-layout</code> 一条命令的事。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="让-agent-整理你的知识树">让 Agent 整理你的知识树<a href="https://udctl.com/zh-Hans/blog/2026/07/18/virtual-path-tasks-become-knowledge/#%E8%AE%A9-agent-%E6%95%B4%E7%90%86%E4%BD%A0%E7%9A%84%E7%9F%A5%E8%AF%86%E6%A0%91" class="hash-link" aria-label="让 Agent 整理你的知识树的直接链接" title="让 Agent 整理你的知识树的直接链接">​</a></h3>
<p>知识库用久了都会乱。目录越挖越深，命名各写各的，过时的和新的混在一起——我的也一样。</p>
<p>但整理这种批量活，恰好是 Agent 最擅长的。vault 就是个本地文件夹，<code>ud pull</code> 之后让 Agent 通读整棵树：重组目录、合并重复、统一命名、把过时的挪进归档。它做的全是普通文件操作，你用 git 或 <code>ud status</code> 把改动过一遍目，满意了再 <code>ud push</code>。Web 端的 Explorer 立刻变成整理后的样子。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/virtual-path-tasks-become-knowledge/concept-3.png" alt="分层模型：内容层是你的 vault，协作层是 ud 的 overlay" class="img_ev3q"></p>
<p>敢这么放手让 Agent 动，是因为分层分得清楚：vault 这层是内容——纯文本、可 git、谁都能改；状态、看板、评论、Notes 时间线是 ud 那层的协作元数据，挂在内容之上，就像 PR 评论不会写进 git tree。Agent 动的是内容层，协作历史一点不丢。</p>
<p>整理好的树也不会锁在某台电脑里。网页端、桌面端、手机端看到的是同一棵树：通勤路上手机翻一篇 ADR，工位上网页端慢慢改，终端里交给 CLI 和 Agent 批量处理。整理一次，哪儿都能用。</p>
<hr>
<p>任务的终点不该是"Done"。值得留下的东西，给它在知识树里一个位置——一次拖拽，一条命令，或者，交给你的 Agent。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="Guide" term="Guide"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[和 UnDercontrol 一起成长：时间沉淀出力量，知识产生复利]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/17/knowledge-compounding/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/17/knowledge-compounding/"/>
        <updated>2026-07-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[建筑学转行程序员，出海新加坡做全栈/DevOps。这些年唯一没变的习惯是把想明白的东西写下来——从 Joplin 到 Obsidian，再到自己写一个 UnDercontrol。聊聊知识的复利。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/knowledge-compounding/concept-1.png" alt="知识的复利曲线" class="img_ev3q"></p>
<p>我是建筑学出身，后来转行做了程序员，再后来离开熟悉的环境，出海新加坡——现在在这边做全栈 / DevOps 工程师。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="转行的人最懂积累两个字">转行的人，最懂"积累"两个字<a href="https://udctl.com/zh-Hans/blog/2026/07/17/knowledge-compounding/#%E8%BD%AC%E8%A1%8C%E7%9A%84%E4%BA%BA%E6%9C%80%E6%87%82%E7%A7%AF%E7%B4%AF%E4%B8%A4%E4%B8%AA%E5%AD%97" class="hash-link" aria-label="转行的人，最懂&quot;积累&quot;两个字的直接链接" title="转行的人，最懂&quot;积累&quot;两个字的直接链接">​</a></h3>
<p>转行意味着从零开始：没有科班四年打底，每一个知识点都要自己啃。那段时间我养成了一个习惯——把踩过的坑、看懂的概念、调通的配置，全都记下来。不是因为勤奋，是因为怕：今天不记，明天就得再花一遍时间。</p>
<p>现在回头看，这个习惯是我做过的回报率最高的投资。转行靠它，跳槽靠它，出海靠它，后来做自己的产品还是靠它。知识和钱一样，是有复利的——前提是你得有一个能让它"生息"的账户。</p>
<p>这些年里，我的"账户"换了三次：Joplin → Obsidian → UnDercontrol。每一次迁移，都不是喜新厌旧，而是我对"知识应该怎么工作"的理解升了一级。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/knowledge-compounding/concept-2.png" alt="两段旅程，一个习惯" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="joplin-时代先把东西存下来collect">Joplin 时代：先把东西存下来（Collect）<a href="https://udctl.com/zh-Hans/blog/2026/07/17/knowledge-compounding/#joplin-%E6%97%B6%E4%BB%A3%E5%85%88%E6%8A%8A%E4%B8%9C%E8%A5%BF%E5%AD%98%E4%B8%8B%E6%9D%A5collect" class="hash-link" aria-label="Joplin 时代：先把东西存下来（Collect）的直接链接" title="Joplin 时代：先把东西存下来（Collect）的直接链接">​</a></h3>
<p>我的第一站是 Joplin。开源、免费、支持 Markdown、能加密同步——对一个刚开始认真做笔记的人来说，它解决了最重要的问题：<strong>让"记下来"这件事发生</strong>。</p>
<p>转行初期的学习笔记、面试准备、工作中排查问题的记录，都是从这里开始堆起来的。</p>
<p>但用久了会发现，笔记像一个个抽屉：存进去容易，但每条笔记都是孤立的。笔记本身存在应用的数据库里，找东西靠文件夹和关键词搜索，笔记之间没有真正的关联。知识存下来了，但只是"躺着"——它不增值。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="obsidian-时代让知识连接起来connect">Obsidian 时代：让知识连接起来（Connect）<a href="https://udctl.com/zh-Hans/blog/2026/07/17/knowledge-compounding/#obsidian-%E6%97%B6%E4%BB%A3%E8%AE%A9%E7%9F%A5%E8%AF%86%E8%BF%9E%E6%8E%A5%E8%B5%B7%E6%9D%A5connect" class="hash-link" aria-label="Obsidian 时代：让知识连接起来（Connect）的直接链接" title="Obsidian 时代：让知识连接起来（Connect）的直接链接">​</a></h3>
<p>于是我搬到了 Obsidian：纯本地 Markdown 文件、双向链接、知识图谱、丰富的插件生态。</p>
<p>这是一次真正的升级。笔记第一次从"一堆文件"变成了"一张网络"——写新东西的时候，旧笔记会自然地被链接进来。我的笔记数量在这个阶段破了千，复盘、写作、做技术决策，都能感觉到过去的积累在托着我。</p>
<p>转行和出海，这两次人生里最大的跳跃，背后都是同一批笔记在支撑。这是我第一次真切地感受到"知识的复利"不是一句鸡汤。</p>
<p>但积累越深，新的裂缝也越明显：</p>
<ul>
<li><strong>知识被锁在本地文件夹里。</strong> 最典型的场景：在公司电脑前 debug 一个棘手问题，我突然想起这个坑我踩过，详细记录在家里那台电脑的 Obsidian 里——然后就没有然后了。我不可能把整个知识库同步到每台设备上。</li>
<li><strong>图谱是个美丽的谎言。</strong> 只要正文里提到 <code>[[某个概念]]</code>，它就进图谱。随手一提的临时引用和精心构建的知识关联，在图上是等权重的边。节点越多，图越好看，信息量却越低。</li>
<li><strong>附件散落各处。</strong> 架构图、流程图、PDF 散在各个 assets 文件夹里，文件夹一挪，链接就断。</li>
<li><strong>同步方案各有各的痛。</strong> 付费同步依然要装客户端；Syncthing 没法装在公司电脑上；Git 同步技术上可行，体验上是折磨。</li>
</ul>
<p>我需要的其实很简单：<strong>一个网页，登录即用，完整的编辑能力，知识随时可及。</strong></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="undercontrol让知识可以被调用compound">UnDercontrol：让知识可以被调用（Compound）<a href="https://udctl.com/zh-Hans/blog/2026/07/17/knowledge-compounding/#undercontrol%E8%AE%A9%E7%9F%A5%E8%AF%86%E5%8F%AF%E4%BB%A5%E8%A2%AB%E8%B0%83%E7%94%A8compound" class="hash-link" aria-label="UnDercontrol：让知识可以被调用（Compound）的直接链接" title="UnDercontrol：让知识可以被调用（Compound）的直接链接">​</a></h3>
<p>市面上没有刚好长成我想要的样子的工具，所以我决定自己造一个。这也是一个实验：一个人加上 AI，能把一个产品做到什么程度？</p>
<p>UnDercontrol 是我对前两个阶段所有裂缝的回答：</p>
<ul>
<li><strong>随处可及</strong>：浏览器登录即用；同时有 CLI 和桌面端，<code>ud pull / ud push</code> 可以把任务双向同步成本地 Markdown 文件——Web 的便利和本地文件的安心，两个都要。</li>
<li><strong>链接由我显式构建</strong>：只有主动建立的关联（peer / parent-child）才进知识图谱，每条边都有意义，信噪比高。</li>
<li><strong>资源集中管理</strong>：图片、PDF、附件跟着具体的上下文走，但可以在统一的资源库里浏览、检索、复用。</li>
<li><strong>不只是任务</strong>：UnDercontrol 的 Markdown 编辑器是所有文本表面共用的——任务、笔记、财务记录、文档，同一套编辑体验。一条任务可以是一篇文档、一次决策记录、一篇博客草稿。</li>
<li><strong>AI 原生</strong>：知识库对 AI 是开放的。Claude Code、Codex、OpenCode 或任何终端 Agent，都能通过 CLI 读写同一份知识。事实上，你现在读的这篇博客，就是一个 AI Agent 在 UnDercontrol 的任务里，读着我过去的笔记写出来的。</li>
</ul>
<p>这是复利的第三个阶段：知识不再只是"存着"或"连着"，而是能<strong>被调用</strong>——被未来的我调用，也被替我干活的 AI 调用。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="时间沉淀出力量">时间沉淀出力量<a href="https://udctl.com/zh-Hans/blog/2026/07/17/knowledge-compounding/#%E6%97%B6%E9%97%B4%E6%B2%89%E6%B7%80%E5%87%BA%E5%8A%9B%E9%87%8F" class="hash-link" aria-label="时间沉淀出力量的直接链接" title="时间沉淀出力量的直接链接">​</a></h3>
<p>从建筑图纸到代码，从国内到新加坡，从 Joplin 到 Obsidian 再到自己写一个工具——变的是职业、城市和软件，不变的是那个习惯：<strong>把今天想明白的东西写下来</strong>。</p>
<p>复利的本质不是收益率，是不中断。工具的意义，就是让"不中断"这件事变得足够容易。</p>
<p>UnDercontrol 对我来说，既是产品，也是这个习惯的容器。我每天用它管理自己的任务、笔记和财务，也用它管理"做 UnDercontrol"这件事本身。我和它一起成长，它替我记住时间沉淀下来的一切。</p>
<p>如果你也在 Joplin、Obsidian 和无数工具之间迁移过，如果你也相信知识有复利——欢迎来试试 <a href="https://oatnil.com/" target="_blank" rel="noopener noreferrer">UnDercontrol</a>。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Story" term="Story"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[你的看板,为什么显示不出你在意的东西]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/"/>
        <updated>2026-07-16T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[在大部分看板工具里,列就是状态枚举,写死的,所以每个看板都长一个样。UnDercontrol 里列是一条查询,拖拽动作从查询自动推导,而且你不用学语法:描述一句,终端里的 Agent 就建给你,落地前还能 dry-run 预览。]]></summary>
        <content type="html"><![CDATA[<p>周一早上打开看板,你想知道的是哪些事情要炸了:已经逾期的、这周要交的。看板只会告诉你哪些在进行中。</p>
<p>你还有一堆看不到的东西。你想按客户看、按 sprint 看、按文档类型看,没有这个选项。你想把没人认领的活挑出来单独放一列,也没有。你想一列盯逾期的、一列盯本周紧急的、一列就单纯放最近动过的,混着排,做不到。真正能改的只有列的名字和顺序。</p>
<p>于是看板显示的是工具眼里你的工作,不是你的。你在意的那些维度,它一个都装不下。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-boards/concept-hero.png" alt="左边是每个工具都给你的三列看板,装不下你在意的维度;右边是你一句话描述、Agent 建出来的贴合需求的看板" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="为什么大部分工具都这样">为什么大部分工具都这样<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E4%B8%BA%E4%BB%80%E4%B9%88%E5%A4%A7%E9%83%A8%E5%88%86%E5%B7%A5%E5%85%B7%E9%83%BD%E8%BF%99%E6%A0%B7" class="hash-link" aria-label="为什么大部分工具都这样的直接链接" title="为什么大部分工具都这样的直接链接">​</a></h3>
<p>这不是哪一个工具的问题。你换一个,多半还是待办、进行中、已完成那三列。大部分工具长一个样,是因为它们的底层假设一致:<strong>列不是你定义的东西,它就是状态本身,写死的。</strong> 你能拖动的是一个枚举值,不是一个你说了算的视角。</p>
<p>工具这么做也有它的理由。灵活通常是要收学费的。一个允许列可以是任何东西的工具,往往会顺手塞给你一套查询语法、一个文档站,和一个周末。大多数人不想付这个学费,这很合理,所以工具替你做了决定:就三列,别多想。</p>
<p>UnDercontrol 走的是另一条路。它是一个任务和知识管理工具,日常操作可以交给终端里的 AI Agent 和一套命令行来做。放到看板上,这意味着列不再等于状态,而是一个条件:什么样的任务该出现在这儿。状态只是众多条件里的一种,它没有任何特权。你想按到期时间分、按人分、按标签分、按你自己的字段分,都行,因为对系统来说这些是同一件事。</p>
<p>至于那笔学费,后面会讲它是怎么免掉的。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="连开箱的默认看板都不老实">连开箱的默认看板都不老实<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E8%BF%9E%E5%BC%80%E7%AE%B1%E7%9A%84%E9%BB%98%E8%AE%A4%E7%9C%8B%E6%9D%BF%E9%83%BD%E4%B8%8D%E8%80%81%E5%AE%9E" class="hash-link" aria-label="连开箱的默认看板都不老实的直接链接" title="连开箱的默认看板都不老实的直接链接">​</a></h3>
<p>这话听起来像在画饼。但你新建一个看板,默认那六列里就有两列不是状态:</p>
<table><thead><tr><th>列</th><th>这一列装什么</th></tr></thead><tbody><tr><td>To Do</td><td>待办的</td></tr><tr><td>In Progress</td><td>进行中的</td></tr><tr><td>Done</td><td>完成的</td></tr><tr><td>Archive</td><td>归档的</td></tr><tr><td>Recent Created</td><td>最近建的,不管什么状态</td></tr><tr><td>Recent Updated</td><td>最近动过的,不管什么状态</td></tr></tbody></table>
<p>最后两列没有任何筛选条件,它们只是按时间倒着排。它们不属于任何状态,它们就是一个视角。</p>
<p>在一个列就等于状态的工具里,这样两列很难出现。而在这里它们平平无奇,就是默认配置的一部分。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-boards/default-board.png" alt="默认看板的六列,最右边的 Recent Created 和 Recent Updated 两列只按时间排序,不属于任何状态" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="那学费呢">那学费呢?<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E9%82%A3%E5%AD%A6%E8%B4%B9%E5%91%A2" class="hash-link" aria-label="那学费呢?的直接链接" title="那学费呢?的直接链接">​</a></h3>
<p>这么自由,是不是意味着我得先学一套语法,才能把想要的看板拼出来。</p>
<p><strong>不用。你只要把想要的说出来。</strong></p>
<blockquote>
<p>你:给我建个看板盯截止日期,逾期的、这周的、下周的、还没定期限的,四列。</p>
</blockquote>
<p>就这样。Claude Code、Codex、OpenCode,或者任何跑在终端里的 Agent,接下来的事它办。字段叫什么、条件怎么写、YAML 长什么样、命令怎么敲,这些全是它的事。</p>
<p>查询语法确实存在,而且很完整,但它不是你的作业。它是 Agent 的工作语言。你请人装修,不需要自己会砌墙。</p>
<p>好奇的话,这是它替你写的东西,你一个字都没敲过:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Board</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deadline Radar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">columns</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Overdue</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">query</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"deadline &lt; now AND status != 'done'"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> This Week</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">query</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"deadline BETWEEN now AND +7d"</span><br></span></code></pre></div></div>
<p>看得懂当然好,看不懂也完全不影响你用。这正是重点。</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="而且你也不用教-agent">而且你也不用教 Agent<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E8%80%8C%E4%B8%94%E4%BD%A0%E4%B9%9F%E4%B8%8D%E7%94%A8%E6%95%99-agent" class="hash-link" aria-label="而且你也不用教 Agent的直接链接" title="而且你也不用教 Agent的直接链接">​</a></h4>
<p>通常你想让 Agent 用某个工具,得先给它写一大段说明书:怎么调、字段叫啥、格式咋样。教一遍,工具一升级,说明书就过期了,Agent 开始胡说。</p>
<p>UnDercontrol 的命令行自带一套配方目录。Agent 不用读源码,也不用你喂文档,它自己问一句 <code>ud cook board</code>,工具就把怎么建看板连同能直接跑的完整示例告诉它。它的学习过程就是敲一条命令。工具自己知道怎么用自己,所以你既不用学,也不用教。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="拖一张卡进去看板让它成真">拖一张卡进去,看板让它成真<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E6%8B%96%E4%B8%80%E5%BC%A0%E5%8D%A1%E8%BF%9B%E5%8E%BB%E7%9C%8B%E6%9D%BF%E8%AE%A9%E5%AE%83%E6%88%90%E7%9C%9F" class="hash-link" aria-label="拖一张卡进去,看板让它成真的直接链接" title="拖一张卡进去,看板让它成真的直接链接">​</a></h3>
<p>自由度还有另一半:这些列该怎么响应拖拽,没有人需要配置它。</p>
<p>道理很直白。一列说了我装的是进行中的任务,那你把一张卡拖进来,意思无非是让这句话变成真的,所以状态自动就改成进行中了。另一列说我装带 blog 标签的,拖进来自动打上标签,拖出去自动摘掉。</p>
<p>你只说了每列装什么,怎么做是系统自己推出来的。这还带来一个不太显眼的好处:列的说明和它的行为不可能对不上,因为后者就是从前者推出来的,没有第二份配置可以走偏。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="它知道什么时候不该动手">它知道什么时候不该动手<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E5%AE%83%E7%9F%A5%E9%81%93%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E4%B8%8D%E8%AF%A5%E5%8A%A8%E6%89%8B" class="hash-link" aria-label="它知道什么时候不该动手的直接链接" title="它知道什么时候不该动手的直接链接">​</a></h3>
<p>假设有一列是已经逾期的。你把一张卡拖进去,系统该把截止日期改成昨天吗?</p>
<p>那是瞎猜。你想要的多半根本不是这个,你可能只是随手拖错了。</p>
<p>所以它什么都不做。凡是没法用一次诚实的写入来满足的条件,比如时间范围、排序、模糊匹配,系统一律不编造动作。这些列于是成了纯粹用来看的视角,你拖不坏它们。</p>
<p>下面是真实跑出来的结果,四列的一个截止日期雷达:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">  Columns:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    1. Overdue</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       query: deadline &lt; now AND status != 'done'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    2. This Week</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       query: deadline BETWEEN now AND +7d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    3. Recently Touched</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       query: ORDER BY updated_at DESC</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    4. Urgent</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       query: tags CONTAINS 'urgent'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       on enter: ADD tags 'urgent'  (auto)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       on exit: REMOVE tags 'urgent'  (auto)</span><br></span></code></pre></div></div>
<p>前三列逾期、本周、最近动过,一个动作都没有,它们是只读的视角。第四列紧急是靠标签定义的,一次写入就能满足,所以拖进来自动打标签,拖出去自动摘掉,全是 <code>(auto)</code>,没人配过。</p>
<p>系统不会编造它讲不出理由的写入。这份克制不是能力不足,恰恰是列可以是任何东西这件事能安全交到你手上的原因。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="在东西真的存在之前你先看见它">在东西真的存在之前,你先看见它<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E5%9C%A8%E4%B8%9C%E8%A5%BF%E7%9C%9F%E7%9A%84%E5%AD%98%E5%9C%A8%E4%B9%8B%E5%89%8D%E4%BD%A0%E5%85%88%E7%9C%8B%E8%A7%81%E5%AE%83" class="hash-link" aria-label="在东西真的存在之前,你先看见它的直接链接" title="在东西真的存在之前,你先看见它的直接链接">​</a></h3>
<p>把建看板交给 Agent,最自然的担心是它理解错了怎么办,会不会把工作区搞得一团糟。</p>
<p>不会,因为中间有一道闸门。Agent 会先跑一次预演:系统把看板完整渲染成它将会被创建的样子,每一列装什么、拖拽会发生什么,全都摆出来,然后什么都不存。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Dry run — board would be created (nothing persisted):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Name:  Blog Pipeline</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Type:  shared  (a group "Board: Blog Pipeline" would be created for sharing)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Default tags: blog</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Columns:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    1. Idea</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       query: status = 'todo' AND tags CONTAINS 'blog'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       on enter: SET status = 'todo'  (auto)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       on enter: ADD tags 'blog'  (auto)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       on exit: REMOVE tags 'blog'  (auto)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    2. Drafting</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       query: status = 'in-progress' AND tags CONTAINS 'blog'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       on enter: SET status = 'in-progress'  (auto)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       ...</span><br></span></code></pre></div></div>
<p>注意那句 <code>nothing persisted</code>。这时候什么都还没发生。</p>
<p>于是整件事变成:你描述,Agent 预演,你看真东西,不对就说审阅那列改成待确认,它改完再演一遍,你点头,落地。</p>
<p>关键在于<strong>你审阅的是系统给出的结果,不是 Agent 复述它的计划</strong>。Agent 可以说错话,但它没法伪造这一步的输出。所以这件事的可信不是建立在相信 Agent 不会出错上,而是它出错也没关系,因为你先看得见。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-boards/deadline-radar.png" alt="由 Agent 建出来的截止日期雷达看板,逾期、本周、下周三列按时间窗口分组,卡片带截止日期和负责人" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="典型场景">典型场景<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E5%85%B8%E5%9E%8B%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="典型场景的直接链接" title="典型场景的直接链接">​</a></h3>
<p>讲了这么多机制,不如看看它实际长在什么地方。</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="周一早上的截止日期雷达">周一早上的截止日期雷达<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E5%91%A8%E4%B8%80%E6%97%A9%E4%B8%8A%E7%9A%84%E6%88%AA%E6%AD%A2%E6%97%A5%E6%9C%9F%E9%9B%B7%E8%BE%BE" class="hash-link" aria-label="周一早上的截止日期雷达的直接链接" title="周一早上的截止日期雷达的直接链接">​</a></h4>
<p>你手上同时压着七八个事情,周一早上想知道的只有一件:哪些要炸了。</p>
<p>跟 Agent 说一句建个看板盯截止日期,逾期、本周、下周、还没定期限,四列就位。以后每周一打开它,逾期的那列有没有东西,一眼的事。</p>
<p>这个看板还有个让人安心的地方:那几列都是按时间算的,拖不坏。你手滑把卡片拖进逾期,系统不会自作主张把你的截止日期改成昨天,它什么都不会做。这种列天生就是拿来看的。</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="团队负载视图">团队负载视图<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E5%9B%A2%E9%98%9F%E8%B4%9F%E8%BD%BD%E8%A7%86%E5%9B%BE" class="hash-link" aria-label="团队负载视图的直接链接" title="团队负载视图的直接链接">​</a></h4>
<p>带团队的人都有过这个疑问:谁手上压着活,谁其实已经空了。</p>
<p>一个每列是一个队友的看板就解决了,末尾再加一列没人认领。站会时打开,谁的列堆成山、哪些活还悬着,不用问,看得见。</p>
<p>这类看板是用来看的,它把散在各处的分工摊平在一个屏幕上。真要调整谁做什么,回到任务本身去改。</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="内容流水线">内容流水线<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E5%86%85%E5%AE%B9%E6%B5%81%E6%B0%B4%E7%BA%BF" class="hash-link" aria-label="内容流水线的直接链接" title="内容流水线的直接链接">​</a></h4>
<p>写博客、做视频、发通告,本质上都是同一条流水线:选题、撰写、审阅、已发布。</p>
<p>这种看板就是拿来拖的。卡片从撰写拖到审阅,状态自动跟着变,标签自动补上,因为每列都说清楚了自己装什么,剩下的系统自己推。你只管拖,它负责让你说的话成真。</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="需求变了就再说一句">需求变了,就再说一句<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E9%9C%80%E6%B1%82%E5%8F%98%E4%BA%86%E5%B0%B1%E5%86%8D%E8%AF%B4%E4%B8%80%E5%8F%A5" class="hash-link" aria-label="需求变了,就再说一句的直接链接" title="需求变了,就再说一句的直接链接">​</a></h4>
<p>用了两周,你发现少了个已归档的去处。跟 Agent 说一句加一列归档的,它拿到看板的当前完整状态,改完先演一遍给你看,你点头才落地。</p>
<p>在别的工具里,改看板意味着重新点一遍。在这里它就是一句话,而且改错了也不要紧,因为你在它生效之前就看见了。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="建完了也还是你的">建完了也还是你的<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E5%BB%BA%E5%AE%8C%E4%BA%86%E4%B9%9F%E8%BF%98%E6%98%AF%E4%BD%A0%E7%9A%84" class="hash-link" aria-label="建完了也还是你的的直接链接" title="建完了也还是你的的直接链接">​</a></h3>
<p>上面那句加一列归档的之所以成立,是因为看板不是一堆点击留下的痕迹,它是一份可以整个拿出来的东西。</p>
<p>导出、改、放回去,都行。这也是 Agent 每次改它都不用猜的原因:它拿到的是完整的当前状态,不是从界面上反推。</p>
<p>顺带的好处是看板能进 git、能传给同事、能复制到另一个项目。它是你的资产,不是锁在某个界面里的配置。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="你的看板你的设计不用做作业">你的看板,你的设计,不用做作业<a href="https://udctl.com/zh-Hans/blog/2026/07/16/kanban-boards/#%E4%BD%A0%E7%9A%84%E7%9C%8B%E6%9D%BF%E4%BD%A0%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8D%E7%94%A8%E5%81%9A%E4%BD%9C%E4%B8%9A" class="hash-link" aria-label="你的看板,你的设计,不用做作业的直接链接" title="你的看板,你的设计,不用做作业的直接链接">​</a></h3>
<p>看板长得都一样,是因为工具替你做了那个取舍:要么简单但笨,要么强大但得学。</p>
<p>UnDercontrol 的答案是你负责想要什么,Agent 负责怎么做到,系统负责别让它做错。</p>
<p>于是那四列的截止日期雷达、按人分栏的团队视图、按你自己业务维度切分的看板,这些都不是谁提前设计好的功能,它们只是你说了一句话的结果。</p>
<p>想试的话,把想要的看板描述给你的 Agent 听就行。剩下的它知道怎么办。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="kanban" term="kanban"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[AI 不只是在 UnDercontrol 里干活，它还能操控 UnDercontrol 本身]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/14/agent-workbench-everything-as-code/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/14/agent-workbench-everything-as-code/"/>
        <updated>2026-07-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[「Agent 工作台」大多止步于执行层。UnDercontrol 用 Everything as Code 补上另一半——任务、看板、技能全是 Markdown + CLI，同一个 Agent 既是工人，也是能操控平台本身的管理员。]]></summary>
        <content type="html"><![CDATA[<p>「Agent 工作台」这个概念正在升温。越来越多人认同：AI 代理应该像队友一样领任务、报进度、沉淀产出，而不只是聊天框里一问一答的工具。</p>
<p>但有个更进一步的问题很少被谈到——<strong>AI 到底能不能操控平台本身？</strong> 不只是在系统里干活，而是帮你打理这个系统：整理看板、归档旧任务、维护标签、把经验沉淀成可复用的能力。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/agent-workbench-everything-as-code/concept-hero.png" alt="Not just a worker — an operator of the platform itself" class="img_ev3q"></p>
<p>UnDercontrol 的答案来自它的底层理念：<strong>一切皆代码（Everything as Code）</strong>。正因如此，AI 在这里既是干活的队友，也能当打理系统的管理员。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="核心everything-as-code--平台本身就是-ai-的操作面">核心：Everything as Code —— 平台本身就是 AI 的操作面<a href="https://udctl.com/zh-Hans/blog/2026/07/14/agent-workbench-everything-as-code/#%E6%A0%B8%E5%BF%83everything-as-code--%E5%B9%B3%E5%8F%B0%E6%9C%AC%E8%BA%AB%E5%B0%B1%E6%98%AF-ai-%E7%9A%84%E6%93%8D%E4%BD%9C%E9%9D%A2" class="hash-link" aria-label="核心：Everything as Code —— 平台本身就是 AI 的操作面的直接链接" title="核心：Everything as Code —— 平台本身就是 AI 的操作面的直接链接">​</a></h3>
<p>在 UnDercontrol 里，任务、笔记、看板、技能、自定义字段……<strong>全都是纯 Markdown + YAML frontmatter</strong>，通过同一个 kubectl 风格的 <code>ud</code> CLI 声明式管理：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 建/改一个任务，就是 apply 一段文本</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt;'EOF' | ud apply -f -</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">title: 发布 v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">status: in-progress</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tags: [release, urgent]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  priority: high</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">发布前检查清单……</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/agent-workbench-everything-as-code/concept-eoc.png" alt="Everything as Code — tasks, boards, skills, metadata are all Markdown + one CLI" class="img_ev3q"></p>
<p>关键在于：<strong>AI Agent 用来读写代码的那套能力（跑命令、读写文本），原封不动就能读写整个平台。</strong> 它能建任务、拆子任务、改状态、打标签、建看板（看板就是保存的查询）、把一段重复的提示词沉淀成新的 Skill、patch 自定义字段……这些管理动作本身，也是代码。</p>
<p>一句话：AI 不只是平台里的一个「<strong>工人</strong>」，它还能当「<strong>管理员</strong>」——因为管理这件事本身就是可编程的。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/agent-workbench-everything-as-code/app-board.png" alt="An Engineering board — columns are saved queries the agent can build and reshape" class="img_ev3q"></p>
<p><strong>典型场景</strong></p>
<ul>
<li>「把这周所有 done 的任务归档，再按标签给 <code>release</code> 建一个新看板」——Agent 自己用 <code>ud apply</code> / <code>ud patch</code> 就做了，不用你点来点去。</li>
<li>「你刚才那套排查步骤，存成一个 Skill」——Agent 把对话里的方法 <code>ud apply</code> 成可复用技能，下次全队都能用。</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="把它拆开看undercontrol-的-agent-工作台">把它拆开看：UnDercontrol 的 Agent 工作台<a href="https://udctl.com/zh-Hans/blog/2026/07/14/agent-workbench-everything-as-code/#%E6%8A%8A%E5%AE%83%E6%8B%86%E5%BC%80%E7%9C%8Bundercontrol-%E7%9A%84-agent-%E5%B7%A5%E4%BD%9C%E5%8F%B0" class="hash-link" aria-label="把它拆开看：UnDercontrol 的 Agent 工作台的直接链接" title="把它拆开看：UnDercontrol 的 Agent 工作台的直接链接">​</a></h3>
<p><strong>Agent 即队友。</strong> 你把需求写成任务、@提及一个 Agent，它自动领取，读取任务描述、Notes、Links 作为上下文，编写代码、运行测试、提交 commit，并把进度写回 Notes。它领到的不是一个黑盒 issue，而是<strong>它自己也能增删改的结构化 Markdown</strong>。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/agent-workbench-everything-as-code/app-task.png" alt="Task detail — the agent reads the description and writes progress + commits back into the notes timeline" class="img_ev3q"></p>
<p><strong>守护进程 / 运行时。</strong> Agent 跑在你自己的开发机上（一个后台 <strong>daemon</strong> 通过 SSE 保持连接、实时回传终端输出）。这套远程工作区从 2026 年 4 月起就在打磨，如今 daemon 注册、SSE、多端触发、会话查看都已落地——发起和监控可以来自 Web、桌面应用、CLI，甚至手机。</p>
<p><strong>可复用的技能（Skills）。</strong> Skill 是存在服务器上的可复用 prompt 模板，通过 <code>ud prompt</code> 注入给任意 Agent。而因为一切皆代码，<strong>Agent 能用 <code>ud apply</code> 自己写、自己改 Skill</strong>——技能库本身也是可编程的资产，越用越厚。</p>
<p><strong>不绑定单一 Agent。</strong> UnDercontrol 是 agent-agnostic 的：通过 Agent CLI 命令模板接入 Claude Code、Codex、Cursor、Hermes 或任意终端智能体。不管你选哪个，它们驱动的都是同一个 <a href="https://udctl.com/zh-Hans/docs/cli-ai-integration/">AI Agent CLI</a>——所以换模型、换工具，不用换工作台。</p>
<p><strong>数据自主。</strong> 任务和笔记就是带 YAML frontmatter 的纯 Markdown 文件，格式定义为开放的 <a href="https://github.com/oatnil-top/ud-schemas" target="_blank" rel="noopener noreferrer">JSON Schema</a>（MIT）——可以用 Git 管版本、随时导出，也支持自托管（Docker Compose / K8s，SQLite / Postgres）。</p>
<blockquote>
<p><strong>不只是任务</strong>：这套 Markdown 编辑器和「一切皆代码」的底座贯穿 UnDercontrol 的所有界面——任务、笔记，甚至账单和资源。Agent 工作台不是一个独立模块，而是这个底座长出来的能力。</p>
</blockquote>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="为什么ai-能操控平台本身重要">为什么「AI 能操控平台本身」重要<a href="https://udctl.com/zh-Hans/blog/2026/07/14/agent-workbench-everything-as-code/#%E4%B8%BA%E4%BB%80%E4%B9%88ai-%E8%83%BD%E6%93%8D%E6%8E%A7%E5%B9%B3%E5%8F%B0%E6%9C%AC%E8%BA%AB%E9%87%8D%E8%A6%81" class="hash-link" aria-label="为什么「AI 能操控平台本身」重要的直接链接" title="为什么「AI 能操控平台本身」重要的直接链接">​</a></h3>
<p>大部分工具里，AI 负责<strong>执行层</strong>（写代码、跑测试），而<strong>管理层</strong>——整理看板、归档旧任务、维护标签体系、把经验沉淀成 SOP——还是压在人身上。</p>
<p>当平台本身就是代码，这条界线消失了：AI 能同时帮你分担执行和管理。你的知识库既是<strong>内容</strong>，也是一个<strong>可编程的操作面</strong>。你多一个会干活的队友，也多一个会打理系统的管理员。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/agent-workbench-everything-as-code/concept-layers.png" alt="Most tools stop at execution; here AI shares the management layer too" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="小结">小结<a href="https://udctl.com/zh-Hans/blog/2026/07/14/agent-workbench-everything-as-code/#%E5%B0%8F%E7%BB%93" class="hash-link" aria-label="小结的直接链接" title="小结的直接链接">​</a></h3>
<p>「Agent 工作台」的核心，是让 AI 稳定地干活、沉淀产出、可被指挥。UnDercontrol 更进一步——靠 <strong>Everything as Code</strong>，让 AI 能操控平台本身：</p>
<ul>
<li>任务、看板、技能、元数据都是纯 Markdown + CLI 可管的代码；</li>
<li>于是同一个 Agent，既能<strong>在平台里干活</strong>（写代码、报进度），又能<strong>操控平台本身</strong>（建看板、归档、写 Skill、维护结构）；</li>
<li>全程 agent-agnostic、多端可触发、数据自主。</li>
</ul>
<p>AI 在 UnDercontrol 里，既是队友，也是管理员。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/agent-workbench-everything-as-code/concept-cta.png" alt="Everything as Code — so AI can run the platform, not just work in it" class="img_ev3q"></p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="agents" term="agents"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[一块看板,看清整个项目 —— 你的一周]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/13/one-board-whole-project/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/13/one-board-whole-project/"/>
        <updated>2026-07-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[用故事的方式走一遍 UnDercontrol 的看板:拖动即改状态、卡片背后是完整的 Markdown 正文加笔记与评论、自定义列自动打标签与通知、列不一定是状态(看板即保存的查询)、共享看板让团队对齐。]]></summary>
        <content type="html"><![CDATA[<p>周一早上九点,你打开 UnDercontrol,只看了一眼看板,就知道这周该从哪儿下手了。</p>
<p>这是你的副业项目「晨跑」——一个跑步社交 App,现在正处在 Sprint 12。看板上五列卡片一字排开<!-- -->:Todo<!-- -->、进行中、评审中、已上线、完成。哪些还没开始、哪些卡住了、哪些等着别人回复——不用点开任何一个任务,一眼就清楚。这篇文章,我们就跟着你用这一块看板走完一整周。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-story/hero.png" alt="一块看板,看清整个项目 —— 拖动卡片,状态自动更新" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="周一从待办拖到进行中">周一:从"待办"拖到"进行中"<a href="https://udctl.com/zh-Hans/blog/2026/07/13/one-board-whole-project/#%E5%91%A8%E4%B8%80%E4%BB%8E%E5%BE%85%E5%8A%9E%E6%8B%96%E5%88%B0%E8%BF%9B%E8%A1%8C%E4%B8%AD" class="hash-link" aria-label="周一:从&quot;待办&quot;拖到&quot;进行中&quot;的直接链接" title="周一:从&quot;待办&quot;拖到&quot;进行中&quot;的直接链接">​</a></h3>
<p>你选中一张卡片——"实现 Google OAuth 登录",从 <strong>Todo</strong> 列拖到 <strong>进行中</strong> 列。松手的那一刻,任务的状态自动就变成了 "In Progress"。你没有点任何下拉菜单、没有填任何表单。</p>
<p>这就是 UnDercontrol 看板的核心:<strong>列就是工作流的阶段,拖动就是状态变更</strong>。卡片挪到哪一列,任务的状态、标签、甚至自定义字段就自动跟着更新。而且这个变化是即时的——你的任务列表里那条任务,同一秒就同步好了。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-story/board-overview.png" alt="Sprint 12 看板全景 —— Todo / In Progress / Review / Deployed / Done 五列卡片" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="周二卡片不只是标题">周二:卡片不只是标题<a href="https://udctl.com/zh-Hans/blog/2026/07/13/one-board-whole-project/#%E5%91%A8%E4%BA%8C%E5%8D%A1%E7%89%87%E4%B8%8D%E5%8F%AA%E6%98%AF%E6%A0%87%E9%A2%98" class="hash-link" aria-label="周二:卡片不只是标题的直接链接" title="周二:卡片不只是标题的直接链接">​</a></h3>
<p>你点开那张 OAuth 卡片。里面不是一行干巴巴的文字——而是一整篇 Markdown 文档:需求清单、技术方案、几个待确认的问题。这是任务的"活文档",是唯一可信来源。</p>
<p>你在下面追加了一条<strong>笔记</strong>:"回调地址在 staging 环境跑通了 —— commit a1b2c3d"。笔记是追加式的时间线,记录这件事怎么一步步推进的,还保留完整的编辑历史,随时能回退。</p>
<blockquote>
<p><strong>不只是任务</strong>:UnDercontrol<!-- --> 的编辑器是所有文本界面共享的。任务正文、笔记、支出记录、账户备注——你在任何地方写的东西,都是同一套 Markdown 体验。看板上的每一张卡片,背后都是一个能装下完整信息的容器。</p>
</blockquote>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-story/card-detail.png" alt="卡片详情页 —— Markdown 正文(需求清单、待确认问题)+ 右侧笔记时间线" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="周三自定义一列让看板替你做杂活">周三:自定义一列,让看板替你做杂活<a href="https://udctl.com/zh-Hans/blog/2026/07/13/one-board-whole-project/#%E5%91%A8%E4%B8%89%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%80%E5%88%97%E8%AE%A9%E7%9C%8B%E6%9D%BF%E6%9B%BF%E4%BD%A0%E5%81%9A%E6%9D%82%E6%B4%BB" class="hash-link" aria-label="周三:自定义一列,让看板替你做杂活的直接链接" title="周三:自定义一列,让看板替你做杂活的直接链接">​</a></h3>
<p>默认的看板列是按状态分的。但你的团队多了一道"代码评审"的环节,于是你<strong>自定义了一列</strong>。</p>
<p>点开"编辑列",这一列只需要填三样东西:</p>
<ul>
<li><strong>列名</strong>:评审中</li>
<li><strong>筛选条件</strong>:<code>status = 'pending' AND tags CONTAINS 'needs-review'</code> —— 哪些卡片会落到这一列</li>
<li><strong>自动动作</strong>:卡片进入本列时,自动打上 <code>needs-review</code> 标签、并 @ 上你的搭档</li>
</ul>
<p>于是当你把 OAuth 卡片拖进"评审中",几件事自动发生了:标签打上、搭档收到提醒。<strong>列的自动动作是从筛选条件自动生成的</strong>,永远和条件保持同步;需要更复杂的流程,也可以手动再加动作。</p>
<p>几分钟后,搭档在这张卡片下留了一条<strong>评论</strong>:"回调 URL 少配了 staging 域名"。你回了一句:"收到,补上了 👍"。评论是轻量的、可串联的讨论——短小、聚焦,你一句我一句就把问题聊清楚了,不用切到 IM,也不会淹没在正文里。正文写"是什么",笔记记"怎么演进",评论则是"就这张卡片临时对齐一下"。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-story/comments.png" alt="卡片右侧的评论串 —— 搭档指出回调 URL 问题,你回复已修复" class="img_ev3q"></p>
<p>一列还不够。你顺手又自定义了一列 <strong>"已上线"</strong>:筛选条件 <code>status = 'pending' AND tags CONTAINS 'deployed'</code>,进入时自动把状态置为 <code>done</code>。两列自定义下来,从评审到上线的流程就在看板上跑通了。</p>
<p>这就是看板真正强大的地方——它不只是"看",它替你干活。拖一下卡片,所有的改状态、打标签、通知,一次搞定。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-story/edit-column.png" alt="编辑列对话框 —— 列名、匹配条件、自动生成的查询与动作" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="周四列不一定是状态">周四:列不一定是状态<a href="https://udctl.com/zh-Hans/blog/2026/07/13/one-board-whole-project/#%E5%91%A8%E5%9B%9B%E5%88%97%E4%B8%8D%E4%B8%80%E5%AE%9A%E6%98%AF%E7%8A%B6%E6%80%81" class="hash-link" aria-label="周四:列不一定是状态的直接链接" title="周四:列不一定是状态的直接链接">​</a></h3>
<p>到了周中,你发现光有状态列不够用。「晨跑」下周就要发版,你更关心的是"这周必须搞定哪些"。于是你又建了一块"发版冲刺"看板——列不再是 Todo / Done,而是 <strong>本周 / 下周 / Backlog</strong>。</p>
<p>因为每一列背后其实是一个查询条件。"本周"列筛的是 <code>deadline &lt;= 本周日</code>,"Backlog"筛的是 <code>status = 'todo' AND deadline IS NULL</code>。看板本质上是<strong>保存下来的查询</strong>,不是一个装东西的文件夹——所以同一张任务可以同时出现在"Sprint 12"和"发版冲刺"两块看板里,没有任何重复数据。</p>
<p><strong>典型场景</strong>:按状态推进日常开发用一块看板;按时间规划优先级用另一块;给某个客户项目再单独开一块。同一批任务,几种视角,各看各的。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-story/board-release.png" alt="发版冲刺看板 —— 列改成 This Week / Next Week / Backlog,同一批卡片换个视角" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="周五把看板分享给团队">周五:把看板分享给团队<a href="https://udctl.com/zh-Hans/blog/2026/07/13/one-board-whole-project/#%E5%91%A8%E4%BA%94%E6%8A%8A%E7%9C%8B%E6%9D%BF%E5%88%86%E4%BA%AB%E7%BB%99%E5%9B%A2%E9%98%9F" class="hash-link" aria-label="周五:把看板分享给团队的直接链接" title="周五:把看板分享给团队的直接链接">​</a></h3>
<p>「晨跑」的设计师这周要接手 UI 相关的卡片。你打开看板设置,点"分享",选中团队群组。她立刻就能看到这块看板——但<strong>只看得到属于这个群组的任务</strong>,你自己的私人任务不会露出来。谁能只读、谁能拖动卡片,由权限控制。</p>
<p>没有额外的同步会议,没有"现在做到哪了"的追问。一块共享看板,就是团队对齐的现场。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-story/share.png" alt="分享看板给团队群组 —— 成员只看得到属于群组的任务,读写权限可控" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="一周的小结">一周的小结<a href="https://udctl.com/zh-Hans/blog/2026/07/13/one-board-whole-project/#%E4%B8%80%E5%91%A8%E7%9A%84%E5%B0%8F%E7%BB%93" class="hash-link" aria-label="一周的小结的直接链接" title="一周的小结的直接链接">​</a></h3>
<p>这一周,你没开过一次"同步会",没在几个 App 之间来回切。一块看板,替你看清了整个「晨跑」项目:</p>
<ul>
<li><strong>拖动即状态变更</strong>——列是工作流阶段,松手即更新,即时同步</li>
<li><strong>卡片是完整的信息容器</strong>——每张卡背后是 Markdown 正文 + 笔记时间线 + 评论</li>
<li><strong>自定义列会替你干活</strong>——按筛选条件自动改状态、打标签、通知,两列就跑通了评审到上线的流程</li>
<li><strong>列不一定是状态</strong>——本周 / 下周 / Backlog,看板是保存的查询,不是文件夹</li>
<li><strong>共享看板 = 团队对齐</strong>——只露出群组任务,权限可控</li>
</ul>
<p>想试试?打开 UnDercontrol,拖动你的第一张卡片——看着状态自己变过去。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="kanban" term="kanban"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[凌晨一点,AI 在我的任务系统里写博客]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/12/ai-blog-pipeline/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/12/ai-blog-pipeline/"/>
        <updated>2026-07-12T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[每日定时任务创建一个"描述即 Prompt"的任务,AI Agent 夜里完成草稿并挂为笔记,人早上在评论区一句话反馈——没有任何 AI 专属设施,只用任务系统本身。]]></summary>
        <content type="html"><![CDATA[<p>早上八点,我端着咖啡打开看板。Pending 列里躺着一个新任务:</p>
<blockquote>
<p><strong>2026-07-12 博客草稿 (Blog Draft)</strong></p>
</blockquote>
<p>点开,里面是一篇写好的中文博客草稿。评论区有两条凌晨留下的消息:</p>
<blockquote>
<p><strong>01:01</strong> — On it — topic scan done, picking task-management-overview. Drafting ZH now.</p>
<p><strong>01:03</strong> — Done — ZH draft is in the note. Task set to pending for review.</p>
</blockquote>
<p>写它的不是人,是一个 AI Agent。我睡觉的时候,它挑了选题、查了产品文档、写完草稿、把任务挪到"待审核",然后在评论区跟我打了声招呼。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/ai-blog-pipeline/kanban-pending.png" alt="Kanban board with the blog draft task sitting in the Pending Review column" class="img_ev3q"></p>
<p>我读完草稿,在评论区回了一句:"范围太大,写故事细节。"几分钟后,草稿被重写了——你现在读的这篇,就是那次重写的产物。</p>
<p>这篇文章讲讲这条流水线是怎么搭起来的,以及为什么"任务系统"是人和 AI 之间最舒服的交接点。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="任务不只是待办事项">任务不只是"待办事项"<a href="https://udctl.com/zh-Hans/blog/2026/07/12/ai-blog-pipeline/#%E4%BB%BB%E5%8A%A1%E4%B8%8D%E5%8F%AA%E6%98%AF%E5%BE%85%E5%8A%9E%E4%BA%8B%E9%A1%B9" class="hash-link" aria-label="任务不只是&quot;待办事项&quot;的直接链接" title="任务不只是&quot;待办事项&quot;的直接链接">​</a></h3>
<p>先说一个核心设计理念:在 UnDercontrol 里,<strong>任务是通用的信息容器</strong>,不只是一条待办。</p>
<p>一个任务可以是:</p>
<ul>
<li>一条真正的待办("修复登录页的校验 bug")</li>
<li>一篇文档(设计方案、会议记录、Wiki 页面)</li>
<li>一个决策记录(为什么选了方案 B)</li>
<li>一篇博客草稿(本文的草稿就是一个任务的笔记)</li>
</ul>
<p>任务的正文是 Markdown,配合标签和自定义元数据,你可以自由定义"这是什么"——而不是被工具的数据结构绑架。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="问题我想持续写博客但不想每天从零开始">问题:我想持续写博客,但不想每天从零开始<a href="https://udctl.com/zh-Hans/blog/2026/07/12/ai-blog-pipeline/#%E9%97%AE%E9%A2%98%E6%88%91%E6%83%B3%E6%8C%81%E7%BB%AD%E5%86%99%E5%8D%9A%E5%AE%A2%E4%BD%86%E4%B8%8D%E6%83%B3%E6%AF%8F%E5%A4%A9%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B" class="hash-link" aria-label="问题:我想持续写博客,但不想每天从零开始的直接链接" title="问题:我想持续写博客,但不想每天从零开始的直接链接">​</a></h3>
<p>做产品的人都懂:内容要持续输出,但写作的启动成本高得离谱。选题、查资料、组织结构、写初稿——每一步都在消耗本来该拿去写代码的精力。</p>
<p>让 AI 全自动写完直接发布?不行。没有人把关的内容,发出去砸的是自己的招牌。</p>
<p>我要的是一条<strong>人机协作流水线</strong>:AI<!-- --> 负责脏活累活(选题去重、查文档、写初稿),我只负责最值钱的那一步——判断和反馈。</p>
<p>于是问题变成:<strong>AI 干完的活放哪?我在哪 review?反馈怎么传回去?</strong></p>
<p>答案是:全放在任务系统里。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="流水线全景一个任务的-24-小时">流水线全景:一个任务的 24 小时<a href="https://udctl.com/zh-Hans/blog/2026/07/12/ai-blog-pipeline/#%E6%B5%81%E6%B0%B4%E7%BA%BF%E5%85%A8%E6%99%AF%E4%B8%80%E4%B8%AA%E4%BB%BB%E5%8A%A1%E7%9A%84-24-%E5%B0%8F%E6%97%B6" class="hash-link" aria-label="流水线全景:一个任务的 24 小时的直接链接" title="流水线全景:一个任务的 24 小时的直接链接">​</a></h3>
<p>整条流水线没有专门的"AI 平台",就是任务系统自己的四个原生能力拼起来的:定时任务、@提及、笔记、评论。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/ai-blog-pipeline/concept-hero.png" alt="The 24-hour handoff loop: scheduled job creates the task, mention wakes the agent, draft lands as a note, human reviews in the morning" class="img_ev3q"></p>
<p><strong>01:00 — 定时任务开闸</strong></p>
<p>一个每日定时任务(CRON)自动创建当天的草稿任务。任务描述就是给 AI 的完整指令,开头 @ 了我的 Agent:</p>
<div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token url" style="color:#36acaa">[</span><span class="token url content" style="color:#36acaa">@ud-agent</span><span class="token url" style="color:#36acaa">](</span><span class="token url" style="color:#36acaa">mention://member/...</span><span class="token url" style="color:#36acaa">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Generate ONE Chinese blog DRAFT for review — do NOT run the full publish pipeline.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">1.</span><span class="token plain"> Pick a topic from </span><span class="token code-snippet code keyword" style="color:#00009f">`auto/blog-topics.json`</span><span class="token plain">. Skip topics already covered —</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   check with </span><span class="token code-snippet code keyword" style="color:#00009f">`ud grep task "&lt;slug&gt;"`</span><span class="token plain">. Choose the first uncovered topic.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">2.</span><span class="token plain"> Write ONLY the ZH text draft as a NOTE on THIS task.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">3.</span><span class="token plain"> Set THIS task status to </span><span class="token code-snippet code keyword" style="color:#00009f">`pending`</span><span class="token plain"> and reply in the comment thread.</span><br></span></code></pre></div></div>
<p>注意这里的设计:<strong>任务描述本身就是 Prompt</strong>。指令、边界("不要发布")、验收标准("置为 pending"),全写在任务里。不需要单独的配置系统——任务就是配置。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/ai-blog-pipeline/scheduled-job.png" alt="Scheduled Jobs page showing the daily 01:00 blog-draft job" class="img_ev3q"></p>
<p><strong>01:01 — Agent 被 @ 醒</strong></p>
<p>@提及触发了 Agent 的工作会话。它在我的 Mac 上醒来,做的第一件事不是写作,而是干侦探活:</p>
<ul>
<li>读取选题清单(仓库里的一个 JSON 文件,18 个候选选题)</li>
<li>用 <code>ud grep task</code> 和 <code>ud query "tags CONTAINS 'blog'"</code> 翻旧任务,查哪些选题已经写过</li>
<li>27 个历史博客任务过一遍,发现只剩 2 个选题没写——选了排在前面的那个</li>
</ul>
<p>然后它在评论区留了第一条消息("On it — picking task-management-overview"),才开始动笔。写作之前,它还去翻了产品文档核对功能细节——AI 最大的毛病是一本正经地胡说,让它先读文档能治大半。</p>
<p><strong>01:03 — 草稿落地,任务转 pending</strong></p>
<p>草稿写完,不是发到什么"AI 输出面板",而是作为一条<strong>笔记</strong>挂在任务上。任务状态被置为 <code>pending</code>——在我们的状态模型里,<code>pending</code> 的意思是"我做完了,等别人"。它在评论区留下第二条消息,收工。</p>
<p>整个过程两分钟,发生在我睡着之后。</p>
<p><strong>08:00 — 我上线,只做判断</strong></p>
<p>我的 review 界面就是任务详情页:草稿在笔记里,上下文在描述里,Agent 的工作日志在评论区。我不需要打开任何别的工具。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/ai-blog-pipeline/task-detail.png" alt="Task detail page: the scheduled prompt as description, the draft as a note below" class="img_ev3q"></p>
<p>读完草稿,我在评论线程里回了一句话。这句话又触发了 Agent 的新一轮会话——它读到反馈,<strong>用 <code>note_id</code> 更新同一条笔记</strong>(不是新建一条,笔记有完整编辑历史,改坏了随时回滚),然后在线程里回复我改了什么。</p>
<p>一来一回,像跟一个异地同事在工单里协作。区别是这个同事凌晨一点上班,而且从不抱怨返工。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/ai-blog-pipeline/comment-thread.png" alt="The comment thread: agent reports progress, human replies with one-line feedback, agent confirms the rewrite" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="为什么交接点是任务而不是聊天窗口">为什么交接点是"任务",而不是聊天窗口<a href="https://udctl.com/zh-Hans/blog/2026/07/12/ai-blog-pipeline/#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BA%A4%E6%8E%A5%E7%82%B9%E6%98%AF%E4%BB%BB%E5%8A%A1%E8%80%8C%E4%B8%8D%E6%98%AF%E8%81%8A%E5%A4%A9%E7%AA%97%E5%8F%A3" class="hash-link" aria-label="为什么交接点是&quot;任务&quot;,而不是聊天窗口的直接链接" title="为什么交接点是&quot;任务&quot;,而不是聊天窗口的直接链接">​</a></h3>
<p>跟 AI 在聊天窗口里协作,最大的问题是<strong>产出会沉底</strong>。昨天让它写的东西,今天要往上翻五十屏;换个会话,上下文全丢。</p>
<p>把交接点换成任务,每个环节都有了原生的容器:</p>
<ul>
<li><strong>任务描述</strong> = 需求和指令(living document,永远是最新版)</li>
<li><strong>笔记</strong> = 产出物和过程记录(追加式时间线,带编辑历史)</li>
<li><strong>评论</strong> = 轻量对话(短平快,带 @ 通知)</li>
<li><strong>状态</strong> = 工作流位置(<code>pending</code> 就是"等人审",看板一眼扫出所有待审项)</li>
</ul>
<p>而且这些东西 AI 和人访问的是<strong>同一个入口</strong>。我在网页和手机上看,Agent 在终端里用 CLI 读写:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud describe task 51c4981e      # 读任务(含笔记、评论、附件)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cat draft.md | ud apply -f -   # 写笔记</span><br></span></code></pre></div></div>
<p>因为入口是 CLI + Markdown,这条流水线不绑定任何特定的 AI 工具——Claude Code、Codex、OpenCode,或者任何能跑终端命令的 Agent,都能接进来。今天换个 Agent,流水线一行不用改。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="这个模式能复制到哪">这个模式能复制到哪<a href="https://udctl.com/zh-Hans/blog/2026/07/12/ai-blog-pipeline/#%E8%BF%99%E4%B8%AA%E6%A8%A1%E5%BC%8F%E8%83%BD%E5%A4%8D%E5%88%B6%E5%88%B0%E5%93%AA" class="hash-link" aria-label="这个模式能复制到哪的直接链接" title="这个模式能复制到哪的直接链接">​</a></h3>
<p>博客草稿只是最顺手的例子。同样的"定时任务 / @提及 → Agent 干活 → 笔记留档 → pending 等审 → 评论反馈"回路,我们还用在:</p>
<ul>
<li><strong>发布验证</strong>:CI<!-- --> 发完版,自动建任务附上 release notes,Agent 跑冒烟测试、把结果写进笔记</li>
<li><strong>每日站会</strong>:定时任务每天建一个站会任务,Agent 汇总昨天所有任务的变更写成摘要</li>
<li><strong>代码调研</strong>:把"调研 X 方案"扔给 Agent,醒来看笔记里的对比结论,评论区追问细节</li>
</ul>
<p>共同点只有一个:<strong>AI 的每一步工作都留在任务里,人只在关键节点出现</strong>。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="小结">小结<a href="https://udctl.com/zh-Hans/blog/2026/07/12/ai-blog-pipeline/#%E5%B0%8F%E7%BB%93" class="hash-link" aria-label="小结的直接链接" title="小结的直接链接">​</a></h3>
<p>这条流水线没有用到任何为 AI 专门发明的概念。定时任务、@提及、笔记、评论、状态——都是任务系统本来就有的东西。只是当你的任务系统同时满足三个条件时,它自然就成了人机协作的工作台:</p>
<ol>
<li><strong>Markdown 原生</strong> — AI 读写无障碍,不需要转换层</li>
<li><strong>CLI 入口</strong> — 任何终端 Agent 都能接入,不绑定厂商</li>
<li><strong>状态 + 评论 + 笔记齐全</strong> — 交接、审核、留档各有其位</li>
</ol>
<p>顺便说一句:这篇文章自己,就是这条流水线在 2026 年 7 月 12 日凌晨的产物。你读到的版本,是人类评论一句"范围太大,写故事细节"之后的第二稿。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="workflow" term="workflow"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[一键把网页变成干净的 Markdown,直接喂给 AI —— UnDercontrol Web Clipper]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/11/web-clipper/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/11/web-clipper/"/>
        <updated>2026-07-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Web Clipper Chrome 扩展无需登录即可把任意网页一键复制成干净的 Markdown 喂给 AI,支持本地保存完整 HTML 快照,也能把网页直接剪成 UnDercontrol 任务供 AI Agent 读取。]]></summary>
        <content type="html"><![CDATA[<p>想把一篇网页文章丢给 AI 总结、翻译、提问,你现在是怎么做的?</p>
<p>全选复制?粘过去的是一堆导航栏、广告、推荐位和乱掉的排版。发链接?很多 AI 工具抓不了页面,抓到的也常常是残缺的。</p>
<p>UnDercontrol 的 <strong>Web Clipper</strong> Chrome 扩展给了一个更干净的答案:点一下 <strong>Copy Markdown</strong>,当前页面的正文被提取成整洁的 Markdown 进入剪贴板——<strong>无需登录、无需账号、无需任何配置</strong>,装上就能在任何页面用。粘给 Claude、ChatGPT 或任何 AI,它拿到的是纯正文,没有一点噪音。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/web-clipper/popup-setup.png" alt="Web Clipper 弹窗 — 无需登录即可 Copy Markdown 和本地保存" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="copy-markdown为-ai-时代准备的复制">Copy Markdown:为 AI 时代准备的复制<a href="https://udctl.com/zh-Hans/blog/2026/07/11/web-clipper/#copy-markdown%E4%B8%BA-ai-%E6%97%B6%E4%BB%A3%E5%87%86%E5%A4%87%E7%9A%84%E5%A4%8D%E5%88%B6" class="hash-link" aria-label="Copy Markdown:为 AI 时代准备的复制的直接链接" title="Copy Markdown:为 AI 时代准备的复制的直接链接">​</a></h2>
<p>这是整个扩展最高频的功能,也完全不依赖 UnDercontrol 服务器:</p>
<ul>
<li><strong>一键复制</strong>:页面正文以干净的 Markdown 格式进入剪贴板</li>
<li><strong>智能提取</strong>:基于 Readability + Turndown,自动剥离广告、导航、侧边栏等非正文元素</li>
<li><strong>零门槛</strong>:不用注册、不用登录,任何页面即点即用</li>
<li><strong>稳定可靠</strong>:自动跳过 video/canvas 等重型元素,复杂页面也不会把浏览器拖崩</li>
</ul>
<p>Markdown 是 AI 的母语——结构清晰、无冗余标记。同一篇文章,贴 Markdown 比贴网页全选复制的内容,让 AI 的理解和回答质量高得多,也省 token。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/web-clipper/concept-copy-markdown.png" alt="网页 → 干净 Markdown → 你的 AI —— 一键完成,无需登录" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="save-to-local不登录也能存档">Save to Local:不登录也能存档<a href="https://udctl.com/zh-Hans/blog/2026/07/11/web-clipper/#save-to-local%E4%B8%8D%E7%99%BB%E5%BD%95%E4%B9%9F%E8%83%BD%E5%AD%98%E6%A1%A3" class="hash-link" aria-label="Save to Local:不登录也能存档的直接链接" title="Save to Local:不登录也能存档的直接链接">​</a></h2>
<p>同样无需登录的还有本地保存:一键下载两个文件——</p>
<ol>
<li><strong>完整 HTML 快照</strong>:基于开源的 <a href="https://github.com/gildas-lormeau/SingleFile" target="_blank" rel="noopener noreferrer">SingleFile</a>,把 HTML、CSS、图片、字体全部内联进一个自包含文件,离线原样打开</li>
<li><strong>提取好的 Markdown</strong>:和 Copy Markdown 同源的干净正文</li>
</ol>
<p>链接会失效、页面会改版,但快照永远是你按下保存那一刻的样子。归档文章、调研资料、时效性页面,不需要任何服务器。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="save-to-undercontrol剪藏进入你的工作流">Save to UnDercontrol:剪藏进入你的工作流<a href="https://udctl.com/zh-Hans/blog/2026/07/11/web-clipper/#save-to-undercontrol%E5%89%AA%E8%97%8F%E8%BF%9B%E5%85%A5%E4%BD%A0%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81" class="hash-link" aria-label="Save to UnDercontrol:剪藏进入你的工作流的直接链接" title="Save to UnDercontrol:剪藏进入你的工作流的直接链接">​</a></h2>
<p>如果你在用 UnDercontrol(支持登录或 API Key 两种方式连接,自部署实例天然支持),剪藏会更进一步——网页直接变成一条<strong>任务</strong>:</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/web-clipper/popup-ready.png" alt="Web Clipper 就绪视图 — 标题预填、标签、Save Page" class="img_ev3q"></p>
<ul>
<li><strong>Markdown 正文写进任务描述</strong>,在任务里直接可读、可编辑</li>
<li><strong>HTML 快照作为附件</strong>挂在任务上,随时还原原页面</li>
<li>之后就是完整的任务能力:打标签、进看板、和其他任务链接、加笔记写结论</li>
<li><strong>AI Agent 可访问</strong>:通过 ud CLI,Claude Code、Codex、OpenCode 或任何基于终端的 AI Agent 都能读到这条任务——描述里就是现成的 Markdown 正文,Agent 不需要再去抓网页</li>
</ul>
<p>下面就是一条真实剪藏出来的任务:描述开头是来源链接,正文是提取好的 Markdown,右侧附件里挂着完整的 HTML 快照:</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/web-clipper/task-detail.png" alt="剪藏生成的任务 — 描述中是来源链接和 Markdown 正文,附件是 HTML 快照" class="img_ev3q"></p>
<p>在 UnDercontrol 里,任务本来就不只是待办事项——它是通用的信息容器,网页剪藏只是又一种进入这个容器的方式。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="彩蛋-站视频字幕一键变文稿">彩蛋<!-- -->:B<!-- --> 站视频字幕一键变文稿<a href="https://udctl.com/zh-Hans/blog/2026/07/11/web-clipper/#%E5%BD%A9%E8%9B%8B-%E7%AB%99%E8%A7%86%E9%A2%91%E5%AD%97%E5%B9%95%E4%B8%80%E9%94%AE%E5%8F%98%E6%96%87%E7%A8%BF" class="hash-link" aria-label="彩蛋-站视频字幕一键变文稿的直接链接" title="彩蛋-站视频字幕一键变文稿的直接链接">​</a></h2>
<p>Web Clipper 还内置了 <strong>Bilibili 字幕提取</strong>:在 B 站视频页上,扩展会自动识别并亮出 <strong>Save Transcript</strong> 按钮,把 CC 字幕或 AI 生成字幕提取成格式化的 Markdown 文稿——可以直接下载到本地,或存成 UnDercontrol 任务。看完一个技术分享视频,文稿顺手就归档了,丢给 AI 做摘要也是一句话的事。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/web-clipper/popup-bilibili.png" alt="检测到 Bilibili 视频 — 一键 Save Transcript" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="典型场景">典型场景<a href="https://udctl.com/zh-Hans/blog/2026/07/11/web-clipper/#%E5%85%B8%E5%9E%8B%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="典型场景的直接链接" title="典型场景的直接链接">​</a></h2>
<ul>
<li><strong>喂给 AI</strong>:长文复制成 Markdown 贴给任何 AI 总结、翻译、提问——比贴链接或全选复制干净得多</li>
<li><strong>技术文章稍后读 + 行动项</strong>:值得跟进的文章剪成任务、设截止日期,读完的实践计划直接写进任务笔记</li>
<li><strong>竞品调研</strong>:竞品定价页、功能页逐个剪进调研看板,对方改版后你手里还有当天的快照</li>
<li><strong>Bug 存证</strong>:第三方页面出问题时剪一份完整快照附在 bug 任务上,页面恢复后依然能还原现场</li>
<li><strong>视频学习笔记</strong>:B<!-- --> 站技术视频的字幕一键变 Markdown 文稿,归档或交给 AI 提炼要点</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="安装与配置">安装与配置<a href="https://udctl.com/zh-Hans/blog/2026/07/11/web-clipper/#%E5%AE%89%E8%A3%85%E4%B8%8E%E9%85%8D%E7%BD%AE" class="hash-link" aria-label="安装与配置的直接链接" title="安装与配置的直接链接">​</a></h2>
<p>从 <a href="https://chromewebstore.google.com/detail/undercontrol-web-clipper/mckkbigikfkoeddpcbhdmpncoljoagog" target="_blank" rel="noopener noreferrer">Chrome Web Store</a> 安装即可。<strong>Copy Markdown 和本地保存装上就能用,零配置</strong>;只有"存为任务"需要连接你的 UnDercontrol 服务器——直接登录,或在网页端 <strong>Settings → API Keys</strong> 创建一个 <code>ak_</code> 开头的 API Key 填入扩展。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/web-clipper/popup-login.png" alt="Login to server — 用户名密码或 API Key,仅&quot;存为任务&quot;需要" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="诚实的边界">诚实的边界<a href="https://udctl.com/zh-Hans/blog/2026/07/11/web-clipper/#%E8%AF%9A%E5%AE%9E%E7%9A%84%E8%BE%B9%E7%95%8C" class="hash-link" aria-label="诚实的边界的直接链接" title="诚实的边界的直接链接">​</a></h2>
<ul>
<li>Chrome 内部页面(<code>chrome://</code>、<code>chrome-extension://</code>)无法捕获</li>
<li>动态内容很重的复杂页面,完整快照的捕获可能需要最长 2 分钟</li>
<li>快照是时间点副本——原页面更新后,快照不会跟着变(这也正是它的价值所在)</li>
</ul>
<p>Web Clipper 浏览器扩展本身以 AGPL-3.0 协议开源,源码在 <a href="https://github.com/oatnil-top/ud-chrome-extension" target="_blank" rel="noopener noreferrer">github.com/oatnil-top/ud-chrome-extension</a>。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="小结">小结<a href="https://udctl.com/zh-Hans/blog/2026/07/11/web-clipper/#%E5%B0%8F%E7%BB%93" class="hash-link" aria-label="小结的直接链接" title="小结的直接链接">​</a></h2>
<p>不登录,它是一个干净的"网页转 Markdown + 本地存档"工具,天生为把内容喂给 AI 而设计;登录后,剪藏直接进入一个能打标签、能进看板、能被 AI Agent 读取的任务系统。下次想把网页内容交给 AI 的时候,先点一下 Copy Markdown 试试。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="Guide" term="Guide"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[标签与自定义字段:用你自己的维度组织任务]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/09/tags-custom-fields/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/09/tags-custom-fields/"/>
        <updated>2026-07-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[在 UnDercontrol,标签、带类型的自定义字段、任意键值对三层自由组合,全都能用 cf.* 查询、固化成看板列。同一批任务,你想怎么切就怎么切。]]></summary>
        <content type="html"><![CDATA[<p>大多数工具会替你决定任务「长什么样」：几个固定的下拉框、一套写死的字段、一种别人设计好的分类法。可现实里，每个人、每个团队、每种工作流想追踪的维度都不一样 —— 有人要「优先级」，有人要「客户」「预估工时」「所属部门」，有人只想随手贴个标签晚点再筛。</p>
<p>UnDercontrol 不强加结构。它给你三层可自由组合的工具：<strong>自由标签</strong>、<strong>带类型的自定义字段</strong>、以及<strong>任意键值对</strong>。三者都能被查询语法直接筛选，让你按自己的维度切分同一批任务，而不是迁就工具的分类法。</p>
<blockquote>
<p>这背后是 UnDercontrol 的一个核心理念：<strong>任务不只是待办</strong>，它是承载信息的统一容器。同一个 Markdown 编辑器贯穿任务、备注、账目、账户等所有文本界面 —— 而标签和自定义字段，就是让你在这个容器上叠加「你自己的结构」的方式。</p>
</blockquote>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/tags-custom-fields/concept-1.png" alt="同一批任务，按标签 / 自定义字段 / 键值对三种维度自由切分" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="一自由标签最轻的组织方式">一、自由标签：最轻的组织方式<a href="https://udctl.com/zh-Hans/blog/2026/07/09/tags-custom-fields/#%E4%B8%80%E8%87%AA%E7%94%B1%E6%A0%87%E7%AD%BE%E6%9C%80%E8%BD%BB%E7%9A%84%E7%BB%84%E7%BB%87%E6%96%B9%E5%BC%8F" class="hash-link" aria-label="一、自由标签：最轻的组织方式的直接链接" title="一、自由标签：最轻的组织方式的直接链接">​</a></h2>
<p>标签就是你随手贴的自由文本 —— 没有预定义词表，想写什么写什么（<code>work</code>、<code>urgent</code>、<code>客户-acme</code>、<code>bug</code> 都行）。</p>
<ul>
<li><strong>有输入建议</strong>：输入时会从你以前用过的标签里给出补全建议（按最近使用排序），既自由又不会拼错、不会一个概念冒出十种写法。</li>
<li><strong>批量打标签</strong>：在任务搜索页可以对一批任务批量增删标签，不用一个个点开。</li>
<li><strong>不只挂在任务上</strong>：标签也能贴到资源（上传的文件）上，帮你把文件和任务用同一套词表串起来。</li>
</ul>
<p>标签本身就是可查询的数组字段：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">tags CONTAINS 'work'                   -- 含 work 标签</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tags IN ('work', 'personal')           -- 含 work 或 personal</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tags CONTAINS_ALL ('work', 'urgent')   -- 同时含 work 和 urgent</span><br></span></code></pre></div></div>
<p><strong>典型场景</strong>：临时来了一批相关任务，先统一贴个 <code>q3-launch</code> 标签，之后不管在哪个看板都能用 <code>tags CONTAINS 'q3-launch'</code> 一键聚合；等这波做完，标签留着当归档线索，或批量删掉。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/tags-custom-fields/screenshot-tags.png" alt="任务详情的标签输入框，展开的历史标签补全建议" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="二自定义字段带类型的结构化维度">二、自定义字段：带类型的结构化维度<a href="https://udctl.com/zh-Hans/blog/2026/07/09/tags-custom-fields/#%E4%BA%8C%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AD%97%E6%AE%B5%E5%B8%A6%E7%B1%BB%E5%9E%8B%E7%9A%84%E7%BB%93%E6%9E%84%E5%8C%96%E7%BB%B4%E5%BA%A6" class="hash-link" aria-label="二、自定义字段：带类型的结构化维度的直接链接" title="二、自定义字段：带类型的结构化维度的直接链接">​</a></h2>
<p>标签解决「贴个词」的问题，但当你要追踪的是<strong>有类型、有取值范围</strong>的东西 —— 优先级是数字、部门是几个固定选项、负责人是某个成员 —— 就该上自定义字段。</p>
<p>在 <strong>自定义字段</strong> 页（<code>/custom-fields</code>，也能从任务详情的属性区、看板菜单或命令面板进入）里定义一个字段，你可以选它的类型：</p>
<ul>
<li><strong>文本（Text）</strong> —— 自由文本值</li>
<li><strong>数字（Number）</strong> —— 数值，支持大小比较</li>
<li><strong>单选（Select）</strong> —— 你预先列好的一组固定选项</li>
<li><strong>复选框（Checkbox）</strong> —— 是 / 否的布尔值</li>
<li><strong>用户（User）</strong> —— 从工作区成员里选一个人</li>
</ul>
<p>定义时还能设「是否必填」。定义好之后，字段会出现在任务详情里，直接内联填写，值就存在这个任务上。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/tags-custom-fields/screenshot-cf-types.png" alt="自定义字段页与「新建字段」抽屉中的 5 种类型：文本 / 数字 / 单选 / 复选 / 用户" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="按标签限定字段的适用范围">按标签限定字段的适用范围<a href="https://udctl.com/zh-Hans/blog/2026/07/09/tags-custom-fields/#%E6%8C%89%E6%A0%87%E7%AD%BE%E9%99%90%E5%AE%9A%E5%AD%97%E6%AE%B5%E7%9A%84%E9%80%82%E7%94%A8%E8%8C%83%E5%9B%B4" class="hash-link" aria-label="按标签限定字段的适用范围的直接链接" title="按标签限定字段的适用范围的直接链接">​</a></h3>
<p>这是一个很关键的设计：自定义字段不必对所有任务生效。新建字段时可以选<strong>作用范围</strong>：</p>
<ul>
<li><strong>全部任务</strong> —— 每个任务都显示这个字段</li>
<li><strong>仅限带某些标签的任务</strong> —— 只有打了指定标签的任务才会出现这个字段</li>
</ul>
<p>于是「标签」和「自定义字段」自然咬合：任务详情页只会显示<strong>跟当前任务标签匹配</strong>的字段，不相关的字段不会来打扰你。给任务贴上 <code>bug</code> 标签，「复现步骤」「严重级别」这些字段才冒出来；贴上 <code>sales</code>，「客户」「金额」才出现。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/tags-custom-fields/screenshot-cf-on-task.png" alt="打了 bug 标签的任务，属性区自动显示 Severity、Reviewer 等 bug 专属字段" class="img_ev3q"></p>
<p><strong>典型场景</strong>：团队同时跑「工程 bug」和「销售跟进」两类任务。给 bug 相关字段设作用范围 = <code>bug</code> 标签，给销售字段设 = <code>sales</code> 标签。同一个看板里，两类任务各自只显示自己那套字段，互不干扰 —— 不用为此拆成两个系统。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="三任意键值对留给临时和结构外的信息">三、任意键值对：留给临时和结构外的信息<a href="https://udctl.com/zh-Hans/blog/2026/07/09/tags-custom-fields/#%E4%B8%89%E4%BB%BB%E6%84%8F%E9%94%AE%E5%80%BC%E5%AF%B9%E7%95%99%E7%BB%99%E4%B8%B4%E6%97%B6%E5%92%8C%E7%BB%93%E6%9E%84%E5%A4%96%E7%9A%84%E4%BF%A1%E6%81%AF" class="hash-link" aria-label="三、任意键值对：留给临时和结构外的信息的直接链接" title="三、任意键值对：留给临时和结构外的信息的直接链接">​</a></h2>
<p>不是所有元数据都值得先去定义一个字段。任务详情里还有一块<strong>额外元数据</strong>区域，让你直接往任务上加任意的键值对（比如 <code>jira: PROJ-123</code>、<code>source: 客户邮件</code>），无需事先在设置里声明。</p>
<p>这层是「自由标签」和「正式自定义字段」之间的缓冲：先随手记下，用多了、成了固定维度，再把它提升成一个带类型的自定义字段。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/tags-custom-fields/screenshot-extra-metadata.png" alt="任务上的自由键值对：jira、external_id、source，无需事先定义" class="img_ev3q"></p>
<p><strong>典型场景</strong>：从外部系统同步进来的任务带了一堆零散字段（工单号、来源链接、外部 ID），先原样塞进键值对保留下来；哪个字段后来发现要经常按它筛选，再正式建一个自定义字段。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="四把它们查出来cf-前缀与看板列">四、把它们查出来：<code>cf.</code> 前缀与看板列<a href="https://udctl.com/zh-Hans/blog/2026/07/09/tags-custom-fields/#%E5%9B%9B%E6%8A%8A%E5%AE%83%E4%BB%AC%E6%9F%A5%E5%87%BA%E6%9D%A5cf-%E5%89%8D%E7%BC%80%E4%B8%8E%E7%9C%8B%E6%9D%BF%E5%88%97" class="hash-link" aria-label="四把它们查出来cf-前缀与看板列的直接链接" title="四把它们查出来cf-前缀与看板列的直接链接">​</a></h2>
<p>自定义字段能力真正的杠杆，在于<strong>它们和内置字段一样可查询</strong>。在查询里用 <code>cf.</code> 前缀引用你的自定义字段：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cf.priority &gt; 5                          -- 优先级大于 5</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cf.priority IN (1, 2, 3)                 -- 优先级是 1、2 或 3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cf.department = 'engineering'            -- 工程部门</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cf.priority IS NULL                      -- 还没设优先级的任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cf.priority &gt; 5 AND status = 'todo'      -- 高优先级的待办</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cf.priority &gt; 5 ORDER BY updated_at DESC -- 高优先级按更新时间排</span><br></span></code></pre></div></div>
<p>自定义字段能和内置字段（<code>status</code>、<code>tags</code>、<code>deadline</code> 等）在同一条查询里自由组合。而在 UnDercontrol 里，<strong>看板的每一列本质上就是一条保存下来的查询</strong> —— 所以你可以直接建一列叫「高优先级待办」，条件是 <code>cf.priority &gt; 5 AND status = 'todo'</code>，任务只要满足条件就自动落进这一列，不用手动拖。任务搜索页也能把自定义字段作为列显示出来。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/tags-custom-fields/screenshot-board-cf-query.png" alt="看板列条件构建器：Tags has #bug AND Custom: Severity = critical，生成查询 tags CONTAINS 'bug' AND cf.severity = 'critical'" class="img_ev3q"></p>
<p><strong>典型场景</strong>：给团队搭一个「按客户看」的看板，每列一个客户，列条件用 <code>cf.client = '...'</code>；新任务只要填了客户字段就自动归位。同一批任务，换个查询就是「按优先级看」「按部门看」—— 视图随维度切换，数据只有一份。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="小结">小结<a href="https://udctl.com/zh-Hans/blog/2026/07/09/tags-custom-fields/#%E5%B0%8F%E7%BB%93" class="hash-link" aria-label="小结的直接链接" title="小结的直接链接">​</a></h2>
<p>UnDercontrol 不替你决定任务该有哪些维度，而是给你三层能力，按需从轻到重叠加：</p>
<ul>
<li><strong>自由标签</strong> —— 零成本、随手贴、可批量、有补全</li>
<li><strong>自定义字段</strong> —— 带类型（文本/数字/单选/复选/用户）、可必填、可<strong>按标签限定作用范围</strong></li>
<li><strong>任意键值对</strong> —— 留给临时和结构外的信息，随时可提升为正式字段</li>
</ul>
<p>而这三层的共同底座，是一套统一的查询语法：<code>tags CONTAINS_ALL(...)</code>、<code>cf.priority &gt; 5</code>、和内置字段自由组合，再把查询固化成看板列。同一批任务，你想怎么切就怎么切 —— 结构是你的，不是工具的。</p>
<p>这一切都建立在 UnDercontrol「任务不只是待办」的理念之上：一个统一的信息容器，一套贯穿所有文本界面的编辑器，加上让你自定义维度的标签与字段。你可以搭配 Claude Code、Codex、OpenCode 或任何基于终端的 Agent 来读写这些结构化数据 —— 但组织方式的主导权，始终在你手里。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/tags-custom-fields/concept-2.png" alt="一份数据，多种切法：同一批任务按标签 / cf.priority / cf.client 切成不同看板视图" class="img_ev3q"></p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="Guide" term="Guide"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[一切皆任务：待办、文档、笔记、知识网络共用一个容器]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/"/>
        <updated>2026-07-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[在 UnDercontrol 里，任务不只是待办，而是通用信息容器——装下文档、笔记、提醒、决策记录。多视图、六种状态、虚拟文件夹 + AI、双向链接，一个编辑器贯穿全应用。]]></summary>
        <content type="html"><![CDATA[<p>Unix 有一句著名的设计哲学：<strong>一切皆文件</strong>。目录、设备、管道、socket——统统用同一套接口去读写，简单、统一、可组合。</p>
<p>UnDercontrol 借用了同样的思路，只不过换了个基本单位：<strong>一切皆任务（Everything is a Task）</strong>。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/task-management-overview/concept-hero.png" alt="一切皆任务" class="img_ev3q"></p>
<p>大多数工具把"任务"理解成一行带勾选框的待办。UnDercontrol 从一开始就不这么想。在这里，<strong>任务（Task）是通用的信息容器</strong>——它可以是一条待办、一份文档、一个提醒、一篇博客草稿、一次会议记录、一份决策记录，甚至一个 wiki 页面。一份数据源，按你喜欢的方式组织，所有信息都在一处。</p>
<p>你不用在多个 App 之间来回切换；AI Agent 也只需要一个工具，就能访问你的全部信息。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="三层内容结构">三层内容结构<a href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/#%E4%B8%89%E5%B1%82%E5%86%85%E5%AE%B9%E7%BB%93%E6%9E%84" class="hash-link" aria-label="三层内容结构的直接链接" title="三层内容结构的直接链接">​</a></h3>
<p>每个任务由三层内容组成，各有设计意图,但你可以按自己的习惯自由使用：</p>
<table><thead><tr><th>层级</th><th>格式</th><th>设计意图</th></tr></thead><tbody><tr><td><strong>任务正文（Body）</strong></td><td>Markdown</td><td>会持续演进的权威文档——单一事实来源（Single Source of Truth）</td></tr><tr><td><strong>笔记（Notes）</strong></td><td>Markdown</td><td>只追加的时间线——记录这个信息单元是如何一步步演变的</td></tr><tr><td><strong>评论（Comments）</strong></td><td>纯文本</td><td>轻量的线程化讨论——简短、聚焦</td></tr></tbody></table>
<p>正文是活文档,你在这里维护完整、连贯的叙述；笔记记录进展的时间线（会话开始、每次提交、遇到的阻塞）；评论则是围绕内容展开的即时对话。三层分工清晰，又都是纯文本，人和 AI 都能直接读懂,不需要任何转换。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/task-management-overview/task-detail.png" alt="任务详情页：Markdown 正文、标签、状态、截止日期、笔记时间线与关系图一览" class="img_ev3q"></p>
<p>一个任务详情页就说明了问题：它不是一行待办，而是一份带 Markdown 正文、标签、状态、截止日期、笔记时间线、子任务与关系图的活文档。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="一份内容多种视图">一份内容，多种视图<a href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/#%E4%B8%80%E4%BB%BD%E5%86%85%E5%AE%B9%E5%A4%9A%E7%A7%8D%E8%A7%86%E5%9B%BE" class="hash-link" aria-label="一份内容，多种视图的直接链接" title="一份内容，多种视图的直接链接">​</a></h3>
<p>正因为底层只有"任务"这一个基本单位，UnDercontrol 才能提供多种视图来观察和操作同一批数据,你随时切换：</p>
<ul>
<li><strong>列表视图</strong>：线性、可筛选，适合快速浏览与批量处理</li>
<li><strong>看板视图</strong>：拖拽卡片改状态，直观管理进行中的工作流</li>
<li><strong>日历视图</strong>：按截止日期铺开，一眼看清时间安排</li>
<li><strong>树状视图</strong>：沿父子关系展开，梳理任务的层级结构</li>
<li><strong>关系图视图</strong>：把任务之间的链接、父子、子任务当作图来遍历，看清知识的关联</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/task-management-overview/multi-view.png" alt="同一份数据，四种视角：看板、列表、日历、关系图" class="img_ev3q"></p>
<p>看板不是"另一个容器"——它本质上是一个<strong>保存下来的查询</strong>。同一个任务可以同时出现在多个看板里，没有任何重复。你换的是视角，不是数据。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="六种状态覆盖真实工作流">六种状态，覆盖真实工作流<a href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/#%E5%85%AD%E7%A7%8D%E7%8A%B6%E6%80%81%E8%A6%86%E7%9B%96%E7%9C%9F%E5%AE%9E%E5%B7%A5%E4%BD%9C%E6%B5%81" class="hash-link" aria-label="六种状态，覆盖真实工作流的直接链接" title="六种状态，覆盖真实工作流的直接链接">​</a></h3>
<p>任务支持六种状态，贴合真实工作的推进节奏：<code>todo</code>（待办）、<code>in-progress</code>（进行中）、<code>pending</code>（待确认/待评审）、<code>done</code>（完成）、<code>archived</code>（归档）、<code>stale</code>（搁置）。看板拖拽会自动更新状态,状态本身就是信息——一眼看清每件事此刻的位置。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="用标签和元数据区分而不是用文件夹">用标签和元数据区分，而不是用文件夹<a href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/#%E7%94%A8%E6%A0%87%E7%AD%BE%E5%92%8C%E5%85%83%E6%95%B0%E6%8D%AE%E5%8C%BA%E5%88%86%E8%80%8C%E4%B8%8D%E6%98%AF%E7%94%A8%E6%96%87%E4%BB%B6%E5%A4%B9" class="hash-link" aria-label="用标签和元数据区分，而不是用文件夹的直接链接" title="用标签和元数据区分，而不是用文件夹的直接链接">​</a></h3>
<p>既然一切皆任务，那"文档"和"待办"在数据结构上其实是同一种东西。让它们彼此区别的，从来不是结构，而是<strong>标签、元数据和视图</strong>。UnDercontrol 不强制你遵守某种打标签的规范，也不逼你建一层层的文件夹：</p>
<ul>
<li><strong>标签（Tags）</strong>：你自己定义的自由标签（<code>doc</code>、<code>meeting</code>、<code>reminder</code>……随你）</li>
<li><strong>一等元数据</strong>：标题、状态、标签、截止日期、负责人、链接——用 20% 的字段覆盖 80% 的需求</li>
<li><strong>自定义元数据</strong>：无限的键值对（<code>doc_type: rfc</code>、<code>client: acme</code>），同样可以被查询</li>
</ul>
<p>需要更精确的检索时，UnDercontrol 还提供类 SQL 的查询语法与自然语言查询，把常用条件保存成看板列,复杂筛选一步到位。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="虚拟文件夹--ai像整理文件一样整理信息">虚拟文件夹 + AI：像整理文件一样整理信息<a href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/#%E8%99%9A%E6%8B%9F%E6%96%87%E4%BB%B6%E5%A4%B9--ai%E5%83%8F%E6%95%B4%E7%90%86%E6%96%87%E4%BB%B6%E4%B8%80%E6%A0%B7%E6%95%B4%E7%90%86%E4%BF%A1%E6%81%AF" class="hash-link" aria-label="虚拟文件夹 + AI：像整理文件一样整理信息的直接链接" title="虚拟文件夹 + AI：像整理文件一样整理信息的直接链接">​</a></h3>
<p>如果你更习惯"文件夹"式的组织，UnDercontrol 也有——<strong>虚拟文件夹</strong>。每个任务都带一个 <code>path</code> 字段（虚拟路径，是 schema 里的一等公民，如 <code>/Work/Alpha/</code>），在应用里就汇成一棵熟悉的文件树，拖拽即可归类。路径为空表示"未归档"，<code>/</code> 是根，<code>/Work/Alpha/</code> 是具体目录——不止任务，资源（附件）、技能等实体也共用这套虚拟路径，摆进同一棵树里。</p>
<p>它还能像 Git 一样在本地和云端之间同步：<code>ud pull /Work/Alpha/</code> 把某个虚拟目录检出到本地文件夹（会生成一个 <code>.ud/</code> 保存同步状态，就像 <code>.git/</code>），本地随便用编辑器改，<code>ud push</code> 再推回云端。想换目录，本地把文件夹挪个位置，<code>ud push</code> 就更新云端路径。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/task-management-overview/virtual-folders.png" alt="虚拟文件夹 + AI：用文件树组织，让 AI 帮你重排" class="img_ev3q"></p>
<p>真正让组织"超方便"的，是<strong>配上 AI</strong>。因为整个结构就是纯文本 + 路径，AI Agent 能直接读懂你的信息全貌，替你动手整理：</p>
<ul>
<li>"把这些散落的会议记录都归到 <code>/Meetings/2026-Q3/</code> 下" —— AI 批量改路径，一句话搞定</li>
<li>"帮我把这个项目相关的任务整理成清晰的目录结构" —— AI 读内容、判断归属、重新组织</li>
<li>"这些任务应该拆成子任务并归档旧的" —— AI 一并完成拆分、归类、状态流转</li>
</ul>
<p>Claude Code、Codex、OpenCode 或任何终端型 Agent 都能通过统一的 CLI 做这些事。你负责思考，AI 负责把信息摆到该在的位置。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="双向链接把任务连成知识网络">双向链接：把任务连成知识网络<a href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/#%E5%8F%8C%E5%90%91%E9%93%BE%E6%8E%A5%E6%8A%8A%E4%BB%BB%E5%8A%A1%E8%BF%9E%E6%88%90%E7%9F%A5%E8%AF%86%E7%BD%91%E7%BB%9C" class="hash-link" aria-label="双向链接：把任务连成知识网络的直接链接" title="双向链接：把任务连成知识网络的直接链接">​</a></h3>
<p>任务之间可以建立多种连接：<strong>对等链接</strong>（peer link）把两件相关的事关联起来；<strong>父子/子任务</strong>关系把大目标拆成可执行的小步骤。配合关系图视图，你的任务库就成了一张可遍历的知识网络，而不是一堆孤立的条目。</p>
<p>在正文、笔记、评论里，你还能用 Markdown 链接直接引用其他实体——引用另一个任务 <code>[标题](task://...)</code>、引用某条笔记 <code>[## 小标题](note://...)</code>、@提到某个成员 <code>[@name](mention://member/...)</code>，甚至内嵌上传的图片 <code>![截图](resource://...)</code>。整个信息库因此是互相打通的。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="不只是任务同一个编辑器贯穿所有文本界面">不只是任务：同一个编辑器，贯穿所有文本界面<a href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/#%E4%B8%8D%E5%8F%AA%E6%98%AF%E4%BB%BB%E5%8A%A1%E5%90%8C%E4%B8%80%E4%B8%AA%E7%BC%96%E8%BE%91%E5%99%A8%E8%B4%AF%E7%A9%BF%E6%89%80%E6%9C%89%E6%96%87%E6%9C%AC%E7%95%8C%E9%9D%A2" class="hash-link" aria-label="不只是任务：同一个编辑器，贯穿所有文本界面的直接链接" title="不只是任务：同一个编辑器，贯穿所有文本界面的直接链接">​</a></h3>
<p>"一切皆任务"还有一层延伸<!-- -->:UnDercontrol<!-- --> 的 Markdown 编辑器<strong>并非任务专属</strong>——它被复用在应用里所有需要写文字的地方：任务正文、笔记、甚至财务记录（支出、账户）的备注。你在任务里熟悉的编辑体验、Markdown 语法、内嵌链接与图片能力，在其他界面完全一致。学一次，处处适用。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="附件与协作">附件与协作<a href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/#%E9%99%84%E4%BB%B6%E4%B8%8E%E5%8D%8F%E4%BD%9C" class="hash-link" aria-label="附件与协作的直接链接" title="附件与协作的直接链接">​</a></h3>
<p>任务可以直接<strong>拖拽上传文件、从剪贴板粘贴</strong>图片附件——截图、设计稿、文档就近保存在它归属的任务上。存储可以是本地，也可以是 S3 兼容对象存储，数据始终在你掌控之中。</p>
<p>协作方面，任务通过**群组（Group）**共享，支持基于角色的读写权限。同一个任务可以出现在共享看板上，团队成员围绕它讨论、更新进展——共享的是这份信息，而不是你的整个账户。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="典型场景">典型场景<a href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/#%E5%85%B8%E5%9E%8B%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="典型场景的直接链接" title="典型场景的直接链接">​</a></h3>
<ul>
<li><strong>个人知识库</strong>：把会议记录、决策记录、读书笔记都当作带标签的任务，用查询和看板随时检索——一个 App 收纳所有。</li>
<li><strong>AI Agent 协作</strong>：Claude Code、Codex、OpenCode 或任何终端型 Agent，都能通过一个统一的 CLI 读写任务、追加笔记、记录进度——人和 AI 共用同一份事实来源。</li>
<li><strong>产品/工程流水线</strong>：需求写在任务正文里，进展记在笔记里，评审意见留在评论里，用看板跟踪状态流转，用关系图看清任务依赖。</li>
<li><strong>内容创作</strong>：一篇博客的中文稿、英文稿、社交文案各存为一条笔记，任务本身就是"单一事实来源"，对外发布的文件都是从它派生出来的。</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/task-management-overview/graph.png" alt="关系图：任务连成一张可遍历的知识网络" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="小结">小结<a href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/#%E5%B0%8F%E7%BB%93" class="hash-link" aria-label="小结的直接链接" title="小结的直接链接">​</a></h3>
<p><strong>一切皆任务</strong>——这不只是一句口号,而是 UnDercontrol 的底层设计：一个 Markdown 原生、AI 就绪的通用容器，配上多视图、六种状态、双向链接、灵活的标签与元数据，以及贯穿全应用的同一个编辑器。你不再需要在待办工具、笔记工具、文档工具之间反复搬运——所有东西，本就该在一处。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="从这里开始">从这里开始<a href="https://udctl.com/zh-Hans/blog/2026/07/08/task-management-overview/#%E4%BB%8E%E8%BF%99%E9%87%8C%E5%BC%80%E5%A7%8B" class="hash-link" aria-label="从这里开始的直接链接" title="从这里开始的直接链接">​</a></h3>
<ul>
<li>🏠 <strong>官网 &amp; 文档</strong>：<a href="https://oatnil.com/" target="_blank" rel="noopener noreferrer">oatnil.com</a></li>
<li>📦 <strong>CLI 安装指南</strong>：<a href="https://oatnil.com/docs/cli" target="_blank" rel="noopener noreferrer">oatnil.com/docs/cli</a></li>
<li>💻 <strong>桌面 App &amp; 自托管</strong>：<a href="https://oatnil.com/subscribe" target="_blank" rel="noopener noreferrer">oatnil.com/subscribe</a></li>
</ul>
<p>一个通用的信息容器,一处收纳你的全部工作——现在就 <code>npm install -g @oatnil/ud</code> 试试。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="Guide" term="Guide"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[像 git 一样管理你的任务:ud pull / push 本地双向同步]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/"/>
        <updated>2026-07-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[ud pull 把云端的任务、笔记、附件拉成本地 Markdown 文件,ud push 把本地修改推回去。UnDercontrol server 与任意文件夹之间的 git 风格双向同步——用 Obsidian、VS Code 或任何 AI Agent 编辑。]]></summary>
        <content type="html"><![CDATA[<blockquote>
<p>一句话:<code>ud pull</code> 把云端的任务、笔记、附件拉成本地的 Markdown 文件,<code>ud push</code> 把你在本地的修改推回去。像 <code>git clone</code> 和 <code>git push</code> 一样自然。</p>
<p><strong>多个仓库,一个 server,随时随地查看编辑。</strong></p>
</blockquote>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/ud-pull-push/concept-1.png" alt="UnDercontrol 云端与本地文件夹通过 ud pull / ud push 双向同步,本地可用 Obsidian、VS Code、AI Agent 等任意工具编辑" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="为什么要做这个功能">为什么要做这个功能<a href="https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%81%9A%E8%BF%99%E4%B8%AA%E5%8A%9F%E8%83%BD" class="hash-link" aria-label="为什么要做这个功能的直接链接" title="为什么要做这个功能的直接链接">​</a></h3>
<p>UnDercontrol 的核心理念一直是:<strong>你的数据就是你的</strong>。任务、笔记、账目、资源——所有文字内容都是纯 Markdown,不锁定在任何一款软件里。</p>
<p>但"能导出"还不够。开发者真正想要的是:<strong>把这些内容当成本地文件来用</strong>——用自己顺手的编辑器改,用 AI 工具读,还能改完再同步回去。</p>
<p>所以我们做了一套 git 风格的命令行同步:<code>ud init</code> / <code>ud pull</code> / <code>ud push</code> / <code>ud status</code>。没有新概念要学——如果你会用 git,你已经会用它了。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="三条命令和-git-一模一样的心智模型">三条命令,和 git 一模一样的心智模型<a href="https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/#%E4%B8%89%E6%9D%A1%E5%91%BD%E4%BB%A4%E5%92%8C-git-%E4%B8%80%E6%A8%A1%E4%B8%80%E6%A0%B7%E7%9A%84%E5%BF%83%E6%99%BA%E6%A8%A1%E5%9E%8B" class="hash-link" aria-label="三条命令,和 git 一模一样的心智模型的直接链接" title="三条命令,和 git 一模一样的心智模型的直接链接">​</a></h3>
<table><thead><tr><th>ud 命令</th><th>对应的 git</th><th>作用</th></tr></thead><tbody><tr><td><code>ud pull /Work/Alpha/</code></td><td><code>git clone</code></td><td>把云端某个虚拟目录检出到当前文件夹,并绑定</td></tr><tr><td><code>ud status</code></td><td><code>git status</code></td><td>看本地和云端各自改了什么</td></tr><tr><td><code>ud push</code></td><td><code>git push</code></td><td>把本地改动推回云端</td></tr><tr><td><code>ud init /Work/Alpha/</code></td><td><code>git init</code></td><td>把一个已有的本地文件夹发布成云端目录</td></tr></tbody></table>
<p>第一次 <code>ud pull /Work/Alpha/</code> 会把当前目录<strong>绑定</strong>到虚拟路径 <code>/Work/Alpha/</code>:目录里会出现一个 <code>.ud/</code> 文件夹保存同步状态(就像 <code>.git/</code>),<code>/Work/Alpha/</code> 下的内容直接落在这个文件夹里。之后再 <code>ud pull</code> 就是<strong>增量</strong>的——只下载变化的部分。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 在一个空文件夹里,一步完成 init + 检出</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud pull /Work/Alpha/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 之后随时增量拉取所有已跟踪的路径</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud pull</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 看看两边都改了什么</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud status</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 把本地改动推回去(会先列出改动清单让你确认)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud push</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/ud-pull-push/concept-2.png" alt="本地文件夹结构:一个任务是一个文件夹,index.md 是任务正文,每条笔记是独立的 .md 文件,.ud/ 保存同步状态" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="本地长什么样任务就是文件夹笔记就是文件">本地长什么样:任务就是文件夹,笔记就是文件<a href="https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/#%E6%9C%AC%E5%9C%B0%E9%95%BF%E4%BB%80%E4%B9%88%E6%A0%B7%E4%BB%BB%E5%8A%A1%E5%B0%B1%E6%98%AF%E6%96%87%E4%BB%B6%E5%A4%B9%E7%AC%94%E8%AE%B0%E5%B0%B1%E6%98%AF%E6%96%87%E4%BB%B6" class="hash-link" aria-label="本地长什么样:任务就是文件夹,笔记就是文件的直接链接" title="本地长什么样:任务就是文件夹,笔记就是文件的直接链接">​</a></h3>
<p>拉下来的结构完全镜像了 UnDercontrol 里的<strong>虚拟文件夹</strong>层级:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Alpha/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── .ud/                      # 同步状态(相当于 .git/)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── tracking.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── Design-Doc/               # 一个任务 = 一个文件夹</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── index.md              # 任务正文(YAML frontmatter + 描述)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── Kickoff-notes.md      # 每条笔记 = 一个 .md 文件</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── Review-notes.md</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── Sprint-Plan/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── index.md</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">└── architecture.png          # 资源 = 普通文件,按虚拟路径摆放</span><br></span></code></pre></div></div>
<p><code>index.md</code> 打开就是一份干净的 Markdown:</p>
<div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token front-matter-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">id</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> 8f3c</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">...</span><span class="token front-matter-block front-matter yaml language-yaml"></span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">title</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> Design Doc</span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">status</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> in</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">-</span><span class="token front-matter-block front-matter yaml language-yaml">progress</span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">tags</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> </span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">[</span><span class="token front-matter-block front-matter yaml language-yaml">design</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> alpha</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">]</span><span class="token front-matter-block front-matter yaml language-yaml"></span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">deadline</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> </span><span class="token front-matter-block front-matter yaml language-yaml datetime number" style="color:#36acaa">2026-07-20</span><span class="token front-matter-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> 背景</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">这里是任务描述正文……</span><br></span></code></pre></div></div>
<p>几个刻意的设计:</p>
<ul>
<li><strong>正文按原样(verbatim)保存</strong>——包括 <code>resource://</code>、<code>task://</code>、<code>note://</code> 这些应用内链接都原封不动,做到<strong>字节级往返</strong>,内容不会在同步中被改写。</li>
<li><strong>虚拟路径不写进 frontmatter</strong>——文件夹所在的位置本身就是路径的唯一来源。想给任务换个目录?在本地把文件夹挪个位置,<code>ud push</code> 就会更新云端的路径。</li>
<li><strong>文件名会被安全化</strong>,原始名字存在 <code>.ud/tracking.json</code> 里,不用担心特殊字符或中文名。</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="push-时ud-会智能识别你做了什么">push 时,ud 会智能识别你做了什么<a href="https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/#push-%E6%97%B6ud-%E4%BC%9A%E6%99%BA%E8%83%BD%E8%AF%86%E5%88%AB%E4%BD%A0%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88" class="hash-link" aria-label="push 时,ud 会智能识别你做了什么的直接链接" title="push 时,ud 会智能识别你做了什么的直接链接">​</a></h3>
<p><code>ud push</code> 会拿工作区和 <code>.ud/tracking.json</code> 里的"上次已知状态"逐项对比,自动判断:</p>
<table><thead><tr><th>你在本地做的事</th><th>ud push 的动作</th></tr></thead><tbody><tr><td>新建一个任务文件夹(带 index.md),或一个散落的 <code>.md</code></td><td>创建任务</td></tr><tr><td>在任务文件夹里新加一个 <code>.md</code></td><td>给这个任务创建笔记</td></tr><tr><td>加一个非 <code>.md</code> 文件</td><td>上传成资源</td></tr><tr><td>改了 index.md / 笔记 / 资源文件</td><td>更新到云端</td></tr><tr><td>把文件/文件夹挪到别的目录</td><td>更新云端的虚拟路径</td></tr><tr><td>删除文件/文件夹</td><td>云端软删除(可恢复)</td></tr></tbody></table>
<p>也就是说,<strong>你在本地新建一个 <code>My-Idea.md</code> 文件,<code>ud push</code> 之后它就是 UnDercontrol 里的一条任务</strong>。反过来,在云端新建的任务,<code>ud pull</code> 之后就出现在本地。真正的双向。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="冲突处理和-git-一样不会偷偷覆盖">冲突处理:和 git 一样,不会偷偷覆盖<a href="https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/#%E5%86%B2%E7%AA%81%E5%A4%84%E7%90%86%E5%92%8C-git-%E4%B8%80%E6%A0%B7%E4%B8%8D%E4%BC%9A%E5%81%B7%E5%81%B7%E8%A6%86%E7%9B%96" class="hash-link" aria-label="冲突处理:和 git 一样,不会偷偷覆盖的直接链接" title="冲突处理:和 git 一样,不会偷偷覆盖的直接链接">​</a></h3>
<p>如果同一个任务在本地和云端都被改过,<code>ud</code> 会把它标为<strong>冲突</strong>:</p>
<ul>
<li><code>ud push</code> 默认<strong>跳过</strong>冲突项(提示你先 pull),或者用 <code>--force</code> 强推覆盖云端。</li>
<li><code>ud pull</code> 默认<strong>保留本地</strong>版本,也可以用 <code>--keep-remote</code> 让云端覆盖本地。</li>
<li>交互式运行时会<strong>逐项</strong>问你保留哪一版。</li>
</ul>
<p>先 <code>ud status</code> 看清楚,再决定策略——不会有任何数据被悄悄丢掉。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="多个仓库一个-server随时随地">多个仓库,一个 server,随时随地<a href="https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/#%E5%A4%9A%E4%B8%AA%E4%BB%93%E5%BA%93%E4%B8%80%E4%B8%AA-server%E9%9A%8F%E6%97%B6%E9%9A%8F%E5%9C%B0" class="hash-link" aria-label="多个仓库,一个 server,随时随地的直接链接" title="多个仓库,一个 server,随时随地的直接链接">​</a></h3>
<p>git 的模型里,一个 origin 可以对应任意多个本地克隆。<code>ud</code> 也一样——<strong>同一个 server 之上,你可以有任意多个本地跟踪文件夹</strong>,各自绑定到不同的虚拟路径:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">笔记本电脑          台式机             另一台 Mac / 服务器</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">~/work/alpha  →     D:\notes\alpha  →   ~/proj-x</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  绑定 /Work/Alpha/    绑定 /Work/Alpha/   绑定 /Projects/proj-x/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        \                 |                    /</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         \                |                   /</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          ┌──────────────────────────────────┐</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          │      一个 UnDercontrol server      │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          │   (虚拟文件夹 = 唯一真相来源)       │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          └──────────────────────────────────┘</span><br></span></code></pre></div></div>
<p>这带来几个很实在的好处:</p>
<ul>
<li><strong>一处一个仓库,互不干扰</strong>:<code>/Work/Alpha/</code> 和 <code>/Projects/proj-x/</code> 是两个独立的跟踪根,各有自己的 <code>.ud/</code> 状态,互不影响。你可以只 checkout 你现在关心的那一小块,而不是把整个知识库拉下来。</li>
<li><strong>随时随地查看编辑</strong>:任何一台设备上 <code>ud pull</code> 就是最新版,改完 <code>ud push</code> 就同步给所有人/所有端。手机上想看?打开 Web / 桌面 App 就行——<strong>同一个 server,同一份数据</strong>。</li>
<li><strong>人和 AI 共用一个真相来源</strong>:你在笔记本上用 Obsidian 改,AI Agent 在服务器上用终端 CLI 改,团队成员在 App 里改——全都汇聚到同一个 server 的同一个虚拟路径,<code>ud status</code> 随时能看清谁改了什么。</li>
</ul>
<p>一句话:<strong>server 是唯一真相来源,本地文件夹只是它的一个个"工作副本"</strong>。这正是 git 二十年验证过的模型,只不过对象从代码换成了你的任务和知识库。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="不只是任务整个知识库都能当文件用">不只是任务:整个知识库都能当文件用<a href="https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/#%E4%B8%8D%E5%8F%AA%E6%98%AF%E4%BB%BB%E5%8A%A1%E6%95%B4%E4%B8%AA%E7%9F%A5%E8%AF%86%E5%BA%93%E9%83%BD%E8%83%BD%E5%BD%93%E6%96%87%E4%BB%B6%E7%94%A8" class="hash-link" aria-label="不只是任务:整个知识库都能当文件用的直接链接" title="不只是任务:整个知识库都能当文件用的直接链接">​</a></h3>
<p>这一点很关键<!-- -->:UnDercontrol<!-- --> 的 Markdown 编辑器是<strong>所有文字界面共用的</strong>——任务、笔记、甚至账目和账户的备注,都是同一套 Markdown。所以 <code>ud pull</code> 拉下来的不是一个孤立的"待办列表导出",而是<strong>你整个知识库的本地镜像</strong>,可以用任何工具打开、编辑、再推回去。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="典型场景">典型场景<a href="https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/#%E5%85%B8%E5%9E%8B%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="典型场景的直接链接" title="典型场景的直接链接">​</a></h3>
<ul>
<li><strong>用 Obsidian / VS Code 管理任务</strong>:<code>ud pull</code> 到你的 vault 里,享受你熟悉的编辑器、快捷键和插件,写完 <code>ud push</code>。</li>
<li><strong>让 AI Agent 直接读写你的任务</strong>:把同步文件夹交给 Claude Code、Codex、OpenCode 或任何终端里的 agent,它就能直接读取任务上下文、追加笔记、甚至批量创建任务——因为对它来说这些就是普通的本地 <code>.md</code> 文件。</li>
<li><strong>代码仓库和任务放在一起</strong>:在项目根目录 <code>ud init /Projects/proj-x/</code>,把设计文档、技术决策记录直接和代码一起版本化、一起 push。</li>
<li><strong>离线也能写</strong>:飞机上、地铁里照样改本地文件,联网后 <code>ud push</code> 一次性同步。</li>
<li><strong>本地备份</strong>:一条 <code>ud pull</code> 就是一份纯文本、可读、永不过期的全量备份。</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="三步开始用">三步开始用<a href="https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/#%E4%B8%89%E6%AD%A5%E5%BC%80%E5%A7%8B%E7%94%A8" class="hash-link" aria-label="三步开始用的直接链接" title="三步开始用的直接链接">​</a></h3>
<p>UnDercontrol 是一个把任务、知识库和 AI agent 放在一起的工作台,支持<strong>桌面 App</strong> 和<strong>自托管</strong>——数据私有、可移植、始终属于你。</p>
<p><strong>1️⃣ 装命令行(30 秒)</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm install -g @oatnil/ud</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 不想装?直接用 npx:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">npx @oatnil/ud --help</span><br></span></code></pre></div></div>
<p><strong>2️⃣ 拉下来,随便用什么工具改</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud pull /Work/     # 把一个虚拟目录检出到本地</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud status          # 看看两边改了什么</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud push            # 推回去</span><br></span></code></pre></div></div>
<p><strong>3️⃣ 拿桌面 App / 自托管你自己的 server</strong></p>
<p>想要图形界面、多端查看、或者把整个 server 跑在自己机器上?看下面的链接。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="从这里开始">从这里开始<a href="https://udctl.com/zh-Hans/blog/2026/07/07/ud-pull-push/#%E4%BB%8E%E8%BF%99%E9%87%8C%E5%BC%80%E5%A7%8B" class="hash-link" aria-label="从这里开始的直接链接" title="从这里开始的直接链接">​</a></h3>
<ul>
<li>🏠 <strong>官网 &amp; 文档</strong>:<a href="https://oatnil.com/" target="_blank" rel="noopener noreferrer">oatnil.com</a></li>
<li>📦 <strong>CLI 安装指南</strong>:<a href="https://oatnil.com/docs/cli" target="_blank" rel="noopener noreferrer">oatnil.com/docs/cli</a></li>
<li>💻 <strong>桌面 App &amp; 自托管</strong>:<a href="https://oatnil.com/subscribe" target="_blank" rel="noopener noreferrer">oatnil.com/subscribe</a></li>
</ul>
<p>你的数据,你的格式,你的工具。UnDercontrol 只是让它们无缝流动起来——现在就 <code>npm install -g @oatnil/ud</code> 试试。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="Guide" term="Guide"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Everything as Code:当你的任务变成一段可以提交进 Git 的 Markdown]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/07/06/everything-as-code/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/07/06/everything-as-code/"/>
        <updated>2026-07-06T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[在 UnDercontrol,一个任务就是带 YAML frontmatter 的 Markdown——可版本控制、用 kubectl 式 CLI apply、任何终端 AI agent 都能读写。给你的待办办 GitOps。]]></summary>
        <content type="html"><![CDATA[<p>我们已经习惯了 "X as Code":基础设施即代码、配置即代码、文档即代码。原因很简单——纯文本可以 diff、可以 review、可以版本控制、可以被脚本和 AI 直接读写。</p>
<p>那为什么你的<strong>任务、笔记、待办</strong>,还锁在某个 SaaS 的数据库里,只能通过它的 UI 一个个点?</p>
<p>UnDercontrol 的答案是:<strong>Everything as Code</strong>。在这里,一个任务就是一段带 YAML frontmatter 的 Markdown。仅此而已。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/everything-as-code/concept-1.png" alt="任务即 Markdown:一段带 frontmatter 的纯文本,apply 后渲染成任务卡片" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="一个任务长什么样">一个任务长什么样<a href="https://udctl.com/zh-Hans/blog/2026/07/06/everything-as-code/#%E4%B8%80%E4%B8%AA%E4%BB%BB%E5%8A%A1%E9%95%BF%E4%BB%80%E4%B9%88%E6%A0%B7" class="hash-link" aria-label="一个任务长什么样的直接链接" title="一个任务长什么样的直接链接">​</a></h3>
<div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token front-matter-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">title</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> 给发布流程加上冒烟测试</span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">status</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> in</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">-</span><span class="token front-matter-block front-matter yaml language-yaml">progress</span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">tags</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> </span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">[</span><span class="token front-matter-block front-matter yaml language-yaml">release</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> testing</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">]</span><span class="token front-matter-block front-matter yaml language-yaml"></span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">deadline</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> </span><span class="token front-matter-block front-matter yaml language-yaml datetime number" style="color:#36acaa">2026-07-10</span><span class="token front-matter-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> 目标</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">每次发版前自动跑一遍关键路径,失败就阻断 tag。</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> 步骤</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> [x] 登录 + 建任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> [ ] 浏览三个核心页面</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> [ ] 断言无 console error</span><br></span></code></pre></div></div>
<p>frontmatter 是这个任务的<strong>结构化元数据</strong>:标题、状态、标签、截止日期、指派人、链接——覆盖 80% 场景的那 20% 字段都是一等公民,并且可查询。frontmatter 下面的正文,是这个任务的<strong>正规文档(Single Source of Truth)</strong>:一段能长期演进的 Markdown。</p>
<p>没有专有格式,没有需要导出的二进制,没有 vendor lock-in。你看到的就是全部。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="apply就像-kubectl">apply,就像 kubectl<a href="https://udctl.com/zh-Hans/blog/2026/07/06/everything-as-code/#apply%E5%B0%B1%E5%83%8F-kubectl" class="hash-link" aria-label="apply,就像 kubectl的直接链接" title="apply,就像 kubectl的直接链接">​</a></h3>
<p>把这段 Markdown 存成文件,然后:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud apply -f task.md</span><br></span></code></pre></div></div>
<p>规则和 <code>kubectl apply</code> 一模一样<!-- -->:frontmatter<!-- --> 里<strong>没有 id 就是创建,有 id 就是更新</strong>。apply 是<strong>整体替换</strong>——文件即真相。</p>
<p>想批量导出、改完再灌回去?<code>ud get task -o apply</code> 会把任务输出成 apply 能吃的同一种格式,round-trip 无损。这意味着你的任务列表可以像代码一样被 sed / jq / 脚本处理,再一次性 apply 回去。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/everything-as-code/concept-2.png" alt="无损 round-trip get -o apply 导出成本地 md,脚本处理后再 ud apply 回去" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="为什么这对开发者和-ai是解锁级的">为什么这对开发者(和 AI)是解锁级的<a href="https://udctl.com/zh-Hans/blog/2026/07/06/everything-as-code/#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E5%AF%B9%E5%BC%80%E5%8F%91%E8%80%85%E5%92%8C-ai%E6%98%AF%E8%A7%A3%E9%94%81%E7%BA%A7%E7%9A%84" class="hash-link" aria-label="为什么这对开发者(和 AI)是解锁级的的直接链接" title="为什么这对开发者(和 AI)是解锁级的的直接链接">​</a></h3>
<p><strong>1. 可版本控制、GitOps 友好。</strong> 任务是纯文本,自然可以 diff、可以进 Git、可以走 PR review。你的项目计划和你的代码可以住在同一个 commit history 里。</p>
<p><strong>2. AI agent 的原生输入输出。</strong> Markdown 是人类可读、也是 AI 可读的。任何终端里的编码 agent——Claude Code、Codex、OpenCode 或任意 terminal-based agent——都能通过 <code>ud</code> CLI 直接读写你的任务,不需要专门的插件或 SDK。整个知识库是 plain-text-native、AI-ready 的。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># agent 在你的终端里就能干这些</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud get task 'status = in-progress'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud apply -f new-task.md</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud describe task 8b30140e</span><br></span></code></pre></div></div>
<p><strong>3. 可脚本化、可组合。</strong> 类 SQL 的查询语法 + kubectl 式的动词(get / describe / apply / delete / patch),让任务管理变成一件能塞进任何自动化流程的事。CI 里建任务、脚本里批量改标签、cron 里生成周报——都只是几行 shell。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/everything-as-code/concept-3.png" alt="多入口单一真相、AI agent、CI 脚本都通过 ud apply 写入同一份 Markdown 数据源" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="不只是任务">不只是任务<a href="https://udctl.com/zh-Hans/blog/2026/07/06/everything-as-code/#%E4%B8%8D%E5%8F%AA%E6%98%AF%E4%BB%BB%E5%8A%A1" class="hash-link" aria-label="不只是任务的直接链接" title="不只是任务的直接链接">​</a></h3>
<p>这里有个容易被忽略的点<!-- -->:UnDercontrol<!-- --> 的 Markdown 编辑器<strong>不是任务专属的</strong>。它是一个共享在所有文本表面上的编辑器——任务、笔记、甚至开支和账户的备注,用的都是同一套编辑体验、同一套 Markdown 内核。</p>
<p>所以 "Everything as Code" 里的 "Everything" 是字面意义的:你的待办是 Markdown,你的笔记是 Markdown,你记的一笔账的说明也是 Markdown。一个数据源,按你喜欢的方式组织,all-in-one。不用在多个 app 之间切换,AI agent 也只需要学会一个工具就能访问你的全部信息。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="三层内容结构">三层内容结构<a href="https://udctl.com/zh-Hans/blog/2026/07/06/everything-as-code/#%E4%B8%89%E5%B1%82%E5%86%85%E5%AE%B9%E7%BB%93%E6%9E%84" class="hash-link" aria-label="三层内容结构的直接链接" title="三层内容结构的直接链接">​</a></h3>
<p>每个信息单元有三层,各有设计意图,但你完全可以按自己的习惯用:</p>
<table><thead><tr><th>层</th><th>格式</th><th>设计意图</th></tr></thead><tbody><tr><td><strong>任务正文</strong></td><td>Markdown</td><td>正规的、持续演进的文档——Single Source of Truth</td></tr><tr><td><strong>笔记 (notes)</strong></td><td>Markdown</td><td>append-only 时间线——记录这个信息单元如何演进</td></tr><tr><td><strong>评论 (comments)</strong></td><td>纯文本</td><td>轻量的、线程式的对话——短而聚焦</td></tr></tbody></table>
<p>区分一个 "文档" 和一个 "任务" 的,不是数据结构,而是<strong>标签、元数据和视图</strong>。UnDercontrol 不强制你用某种目录层级或打标签规范——同一个任务可以出现在多个 board(board 本质是"保存的查询",不是容器,所以没有重复)。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="开始用">开始用<a href="https://udctl.com/zh-Hans/blog/2026/07/06/everything-as-code/#%E5%BC%80%E5%A7%8B%E7%94%A8" class="hash-link" aria-label="开始用的直接链接" title="开始用的直接链接">​</a></h3>
<p>如果你也受够了把想法困在某个 UI 里,试试把它们变成可以 apply 的 Markdown:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud apply -f my-first-task.md</span><br></span></code></pre></div></div>
<p>Everything as Code,不只是一句口号——它是让你的任务、你的脚本、你的 AI agent 终于说同一种语言的方式。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="Guide" term="Guide"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[随时随地访问 — Web、桌面、CLI 全平台覆盖]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/04/28/multi-platform/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/04/28/multi-platform/"/>
        <updated>2026-04-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[UnDercontrol 提供四种官方客户端 — Web、Electron 桌面端、CLI、Chrome 扩展 — 共享同一个可自部署的数据源。开放 API，构建你自己的客户端。]]></summary>
        <content type="html"><![CDATA[<p>在选择生产力工具时，一个常见的困境是：Web 端功能强大但离线无法使用，桌面端体验好但数据被锁在本地，CLI 工具对开发者友好但缺少可视化界面。问题的本质是缺少一个统一的数据真实来源（Single Source of Truth）——每个平台各自为政，数据散落在不同角落。UnDercontrol 的答案是：四种形态，一个数据源。而这个数据源完全由你掌控——支持自部署，数据存储在你自己的服务器或本地磁盘，不经过任何第三方，隐私与安全由你自己定义。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/slide-sst.png" alt="Single Source of Truth 架构" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="web-应用--远程访问零安装">Web 应用 — 远程访问，零安装<a href="https://udctl.com/zh-Hans/blog/2026/04/28/multi-platform/#web-%E5%BA%94%E7%94%A8--%E8%BF%9C%E7%A8%8B%E8%AE%BF%E9%97%AE%E9%9B%B6%E5%AE%89%E8%A3%85" class="hash-link" aria-label="Web 应用 — 远程访问，零安装的直接链接" title="Web 应用 — 远程访问，零安装的直接链接">​</a></h3>
<p>UnDercontrol 的 Web 端基于 Vite + React + TypeScript 构建，是整个平台的基础界面。打开浏览器即可访问，无需安装任何软件。</p>
<p>核心能力：</p>
<ul>
<li>完整的任务、预算、支出管理</li>
<li>Tiptap 富文本编辑器（代码块、Mermaid 图表、表格、清单）</li>
<li>AI 聊天集成（支持 Claude、OpenAI 等多种 Provider）</li>
<li>SSE 实时通知，多标签页同步更新</li>
<li>响应式设计，移动端同样可用</li>
<li>中英双语界面</li>
</ul>
<p>Web 端同时也是 Electron 桌面端的渲染层——同一套代码，零重复。</p>
<p><strong>典型场景</strong></p>
<ul>
<li>出门在外，用手机浏览器快速查看任务进展、审批支出</li>
<li>团队成员无需安装任何软件，打开链接即可协作</li>
<li>作为所有客户端的数据汇聚点——CLI 推送的文档、桌面端创建的任务、扩展剪藏的网页，全部在 Web 端统一查看和管理</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/web-app-dashboard.png" alt="UnDercontrol Web 应用 Dashboard" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="electron-桌面端--离线优先本地数据">Electron 桌面端 — 离线优先，本地数据<a href="https://udctl.com/zh-Hans/blog/2026/04/28/multi-platform/#electron-%E6%A1%8C%E9%9D%A2%E7%AB%AF--%E7%A6%BB%E7%BA%BF%E4%BC%98%E5%85%88%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE" class="hash-link" aria-label="Electron 桌面端 — 离线优先，本地数据的直接链接" title="Electron 桌面端 — 离线优先，本地数据的直接链接">​</a></h3>
<p>桌面端不仅仅是把 Web 应用套了一层壳。它内嵌了完整的 Go 后端和 SQLite 数据库，开箱即用，无需配置服务器。</p>
<p><strong>内嵌后端架构</strong></p>
<ul>
<li>应用启动时自动拉起 Go 后端进程</li>
<li>动态端口分配，避免冲突</li>
<li>数据存储在 <code>~/Library/Application Support/UnDercontrol/</code>（macOS）</li>
<li>完全离线可用——断网也不影响使用</li>
</ul>
<p><strong>远程后端模式</strong></p>
<ul>
<li>桌面端同样支持配置连接远程服务器，与 Web 端共享同一个数据源</li>
<li>在设置中切换 API 地址即可，无需重装</li>
</ul>
<p><strong>Daemon 模式</strong></p>
<ul>
<li>后台守护进程，通过 SSE 监听任务队列</li>
<li>接收并自动执行远程派发的任务——非常适合与 AI Agent 配合使用</li>
</ul>
<p><strong>桌面专属功能</strong></p>
<ul>
<li>系统托盘（Windows）— 最小化到托盘，快速访问</li>
<li><code>.md</code> 文件关联 — 双击 Markdown 文件直接在 UnDercontrol 中打开</li>
<li>多窗口编辑 — 同时打开多个编辑器窗口、任务窗口、便签窗口</li>
<li>工作区管理 — 本地终端窗口，执行命令</li>
</ul>
<p><strong>构建目标</strong></p>
<ul>
<li>macOS — DMG 安装包，Universal Binary（x64 + ARM64）</li>
<li>Windows — NSIS 安装包（x64）</li>
<li>Linux — AppImage（x64）</li>
</ul>
<p><strong>典型场景</strong></p>
<ul>
<li>飞机上、高铁上没有网络，照常创建任务、编辑文档，联网后自动同步</li>
<li>在家用桌面端连接公司服务器，和 Web 端看到的数据完全一致</li>
<li>通过 Daemon 接收远程派发的 AI 任务，本地机器自动执行</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/web-app-task-detail.png" alt="UnDercontrol 任务详情页" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="cli-工具--kubectl-风格ai-agent-友好">CLI 工具 — kubectl 风格，AI Agent 友好<a href="https://udctl.com/zh-Hans/blog/2026/04/28/multi-platform/#cli-%E5%B7%A5%E5%85%B7--kubectl-%E9%A3%8E%E6%A0%BCai-agent-%E5%8F%8B%E5%A5%BD" class="hash-link" aria-label="CLI 工具 — kubectl 风格，AI Agent 友好的直接链接" title="CLI 工具 — kubectl 风格，AI Agent 友好的直接链接">​</a></h3>
<p><code>ud</code> CLI 是为开发者和自动化场景设计的终端工具，采用 kubectl 风格的动词-资源命令体系。</p>
<p><strong>交互式 TUI</strong></p>
<p>直接运行 <code>ud</code> 进入全屏 TUI 界面，用键盘浏览和管理任务。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/slide-sst.png" alt="多平台架构 — Single Source of Truth" class="img_ev3q"></p>
<p><strong>一行命令完成操作</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud get task                          # 列出任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud describe task abc123              # 查看详情</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud apply -f task.md                  # 从文件创建/更新</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task query "status:todo tag:api"  # 查询过滤</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task nl "上周完成的任务"            # 自然语言查询</span><br></span></code></pre></div></div>
<p><strong>AI Agent 集成</strong></p>
<p><code>ud prompt &lt;skill-name&gt;</code> 输出技能文档，让 AI Agent（Claude Code、Codex、OpenCode 等）学会使用 ud CLI 操作任务。结构化的命令输出和 Markdown 格式的输入，天然适合 AI Agent 读写。</p>
<p><strong>典型场景</strong></p>
<ul>
<li>CI/CD Pipeline 中自动将发布说明推送到 ud，团队在 Web 端直接查看，不用翻 Git log</li>
<li>代码仓库里的技术文档（架构设计、API 规范、运维手册）通过脚本同步到 ud，非开发人员也能在 Web 端阅读，不用访问代码仓库</li>
<li>AI Agent 在编码过程中自动将进度、决策记录写入任务笔记，团队实时可见</li>
<li>运维脚本定期采集巡检结果，<code>ud apply</code> 写入任务，形成可追溯的运维日志</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="chrome-扩展--一键剪藏">Chrome 扩展 — 一键剪藏<a href="https://udctl.com/zh-Hans/blog/2026/04/28/multi-platform/#chrome-%E6%89%A9%E5%B1%95--%E4%B8%80%E9%94%AE%E5%89%AA%E8%97%8F" class="hash-link" aria-label="Chrome 扩展 — 一键剪藏的直接链接" title="Chrome 扩展 — 一键剪藏的直接链接">​</a></h3>
<p>Chrome 扩展让你把任何网页变成 UnDercontrol 中的任务。</p>
<ul>
<li><strong>一键保存</strong> — 捕获完整网页快照（HTML + 所有嵌入资源）</li>
<li><strong>Markdown 提取</strong> — 自动提取页面正文为干净的 Markdown</li>
<li><strong>离线模式</strong> — 无需登录，直接保存到本地磁盘</li>
<li><strong>远程模式</strong> — 登录后直接创建为 UnDercontrol 任务，快照作为附件上传</li>
</ul>
<p><strong>典型场景</strong></p>
<ul>
<li>调研竞品时，一键保存产品页面为任务，稍后在 Web 端整理对比</li>
<li>看到一篇技术文章，剪藏为 Markdown 存入 ud，团队共享阅读</li>
<li>收到客户反馈的网页截图，直接剪藏为 Bug 任务，附带完整上下文</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="sse-实时同步">SSE 实时同步<a href="https://udctl.com/zh-Hans/blog/2026/04/28/multi-platform/#sse-%E5%AE%9E%E6%97%B6%E5%90%8C%E6%AD%A5" class="hash-link" aria-label="SSE 实时同步的直接链接" title="SSE 实时同步的直接链接">​</a></h3>
<p>所有连接到同一后端的客户端——无论是 Web 标签页、桌面应用还是另一台设备——通过 SSE（Server-Sent Events）实时同步。</p>
<ul>
<li>任务状态变更、新评论、附件上传，所有事件实时推送</li>
<li>Daemon SSE Hub 专门用于向桌面端 Daemon 派发命令</li>
<li>工作区会话状态在桌面端和后端之间自动协调</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/slide-clients.png" alt="四种官方客户端" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="同一套代码一致的体验">同一套代码，一致的体验<a href="https://udctl.com/zh-Hans/blog/2026/04/28/multi-platform/#%E5%90%8C%E4%B8%80%E5%A5%97%E4%BB%A3%E7%A0%81%E4%B8%80%E8%87%B4%E7%9A%84%E4%BD%93%E9%AA%8C" class="hash-link" aria-label="同一套代码，一致的体验的直接链接" title="同一套代码，一致的体验的直接链接">​</a></h3>
<p>UnDercontrol 的多平台策略不是"每个平台写一套"，而是：</p>
<ul>
<li><strong>Web 应用</strong>是唯一的前端代码库</li>
<li><strong>Electron</strong> 直接加载 Web 构建产物，零代码重复</li>
<li><strong>CLI</strong> 共享同一套 API 和数据模型</li>
<li><strong>Chrome 扩展</strong>使用相同的 API 端点</li>
</ul>
<table><thead><tr><th>能力</th><th>Web</th><th>桌面端</th><th>CLI</th><th>扩展</th></tr></thead><tbody><tr><td>远程 API 访问</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>内嵌后端</td><td>❌</td><td>✅（可切换远程）</td><td>❌</td><td>❌</td></tr><tr><td>离线模式</td><td>❌</td><td>✅</td><td>❌</td><td>✅</td></tr><tr><td>系统托盘</td><td>❌</td><td>✅</td><td>❌</td><td>❌</td></tr><tr><td>TUI 界面</td><td>❌</td><td>❌</td><td>✅</td><td>❌</td></tr><tr><td>后台 Daemon</td><td>❌</td><td>✅</td><td>❌</td><td>❌</td></tr><tr><td>网页剪藏</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>实时 SSE</td><td>✅</td><td>✅</td><td>❌</td><td>❌</td></tr><tr><td>AI Agent 友好</td><td>✅</td><td>✅</td><td>✅</td><td>❌</td></tr></tbody></table>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/slide-capabilities.png" alt="能力对比表" class="img_ev3q"></p>
<p>不止于此——开放的 API 让你构建任何你想要的客户端。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="更多可能--自定义客户端">更多可能 — 自定义客户端<a href="https://udctl.com/zh-Hans/blog/2026/04/28/multi-platform/#%E6%9B%B4%E5%A4%9A%E5%8F%AF%E8%83%BD--%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%A2%E6%88%B7%E7%AB%AF" class="hash-link" aria-label="更多可能 — 自定义客户端的直接链接" title="更多可能 — 自定义客户端的直接链接">​</a></h3>
<p>四种官方客户端之外，UnDercontrol 开放了完整的 RESTful API。在个人设置中生成 API Key，配合 Swagger 文档，你可以用任何语言构建自己的客户端——Python 脚本、自动化 Bot、内部工具集成，甚至是你自己的移动端 App。</p>
<ul>
<li><strong>API Key 认证</strong> — 在 Profile → API Key 中生成，Bearer Token 方式调用</li>
<li><strong>权限范围可控</strong> — 按模块（任务、支出、预算、文件、AI）独立授权</li>
<li><strong>Swagger 文档</strong> — <code>https://your-server/swagger/index.html</code> 查看所有端点</li>
<li><strong>X-UD-Channel 审计</strong> — 自定义 Channel 标识，追踪每个请求来源</li>
<li><strong>CI/CD 友好</strong> — 环境变量配置，无缝接入自动化流水线</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/slide-custom.png" alt="构建自定义客户端" class="img_ev3q"></p>
<p><strong>典型场景</strong></p>
<ul>
<li>用 Python 写一个 Slack Bot，团队在 Slack 中 <code>/task</code> 即可创建和查询 ud 任务</li>
<li>内部管理后台通过 API 将工单系统与 ud 打通，双向同步状态</li>
<li>移动端原生 App 调用相同 API，实现手机端的个性化体验</li>
<li>监控系统告警自动创建 ud 任务，附带上下文信息，值班人员在 Web 端处理</li>
</ul>
<hr>
<p>不同的场景，选择最适合的工具形态——数据始终同步，体验始终一致。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="platform" term="platform"/>
        <category label="self-hosted" term="self-hosted"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[任务中的富文本 Markdown — 代码、图表，一应俱全]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/"/>
        <updated>2026-04-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[UnDercontrol 任务支持完整的 Markdown 渲染，包括语法高亮代码块、Mermaid 图表、表格、清单，以及斜杠命令菜单实现快速排版。]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorWithStickyNavbar_LWe7" id="什么是任务task">什么是任务（Task）？<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#%E4%BB%80%E4%B9%88%E6%98%AF%E4%BB%BB%E5%8A%A1task" class="hash-link" aria-label="什么是任务（Task）？的直接链接" title="什么是任务（Task）？的直接链接">​</a></h3>
<p>在 UnDercontrol 中，Task（任务） 是信息的核心载体。如果你用过 Jira，可以把它理解为 Issue；如果你用过 Obsidian，可以把它理解为一篇 Note。Task 的本质是一段绑定了状态的主体内容，围绕它有一系列一等公民属性（标题、标签、链接关系等），同时支持无限的自定义元数据字段（键值对），让你可以将任何信息附加其上。通过笔记（Notes）机制，Task 还能不断演进——记录进展、讨论和决策，随时间沉淀为完整的知识上下文。</p>
<p>而这篇文章要讲的，是 UnDercontrol 中所有文本输入场景共享的富文本能力。说"任务"，但不只是任务——无论是任务描述、笔记、支出备注还是账户说明，都使用同一套 Markdown 编辑器。你在任务里用到的代码块、Mermaid 图表、表格、清单，在笔记和其他场景中同样可用。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="所见即所得--基于-tiptap-3-的-markdown-编辑器">所见即所得 — 基于 Tiptap 3 的 Markdown 编辑器<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#%E6%89%80%E8%A7%81%E5%8D%B3%E6%89%80%E5%BE%97--%E5%9F%BA%E4%BA%8E-tiptap-3-%E7%9A%84-markdown-%E7%BC%96%E8%BE%91%E5%99%A8" class="hash-link" aria-label="所见即所得 — 基于 Tiptap 3 的 Markdown 编辑器的直接链接" title="所见即所得 — 基于 Tiptap 3 的 Markdown 编辑器的直接链接">​</a></h3>
<p>UnDercontrol 的编辑器基于 <a href="https://tiptap.dev/" target="_blank" rel="noopener noreferrer">Tiptap 3</a> 构建。你可以直接用 Markdown 语法书写，也可以通过可视化工具栏操作——编辑器实时渲染，所写即所见。不需要在"编辑模式"和"预览模式"之间来回切换，输入的内容即最终呈现的样子。</p>
<p>同时，编辑器也支持切换到源码模式（Source Mode），直接查看和编辑原始 Markdown 代码——对于习惯纯文本编辑或需要精确控制格式的用户，随时可以在所见即所得和 Raw Markdown 之间自由切换。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="斜杠命令--无需离开键盘即可排版">斜杠命令 — 无需离开键盘即可排版<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#%E6%96%9C%E6%9D%A0%E5%91%BD%E4%BB%A4--%E6%97%A0%E9%9C%80%E7%A6%BB%E5%BC%80%E9%94%AE%E7%9B%98%E5%8D%B3%E5%8F%AF%E6%8E%92%E7%89%88" class="hash-link" aria-label="斜杠命令 — 无需离开键盘即可排版的直接链接" title="斜杠命令 — 无需离开键盘即可排版的直接链接">​</a></h3>
<p>在任意位置输入 <code>/</code>，即可打开命令菜单。从这里可以插入标题、代码块、表格、Mermaid 图表、清单等——全程无需使用鼠标。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/slide-2.png" alt="编辑器中的斜杠命令菜单" class="img_ev3q"></p>
<p>斜杠菜单支持：</p>
<ul>
<li><strong>标题</strong>（H1–H5）用于文档结构</li>
<li><strong>任务列表</strong>，带有可交互的复选框</li>
<li><strong>代码块</strong>，支持语言选择和语法高亮</li>
<li><strong>Mermaid 图表</strong>，用于流程图、时序图等</li>
<li><strong>表格</strong>，支持完整的单元格编辑</li>
<li><strong>引用块</strong>、分割线和图片</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="语法高亮代码块">语法高亮代码块<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#%E8%AF%AD%E6%B3%95%E9%AB%98%E4%BA%AE%E4%BB%A3%E7%A0%81%E5%9D%97" class="hash-link" aria-label="语法高亮代码块的直接链接" title="语法高亮代码块的直接链接">​</a></h3>
<p>直接将代码粘贴到任务中。编辑器支持 100+ 种编程语言的语法高亮——从 TypeScript、Go 到 SQL、YAML。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/api-task-top.png" alt="带语法高亮 TypeScript 代码块的任务" class="img_ev3q"></p>
<p>代码块配有语言选择下拉框和一键复制按钮。无论是记录 API 接口还是保存常用的 Shell 命令，代码都保持清晰可读。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="mermaid-图表--在任务中直接可视化架构">Mermaid 图表 — 在任务中直接可视化架构<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#mermaid-%E5%9B%BE%E8%A1%A8--%E5%9C%A8%E4%BB%BB%E5%8A%A1%E4%B8%AD%E7%9B%B4%E6%8E%A5%E5%8F%AF%E8%A7%86%E5%8C%96%E6%9E%B6%E6%9E%84" class="hash-link" aria-label="Mermaid 图表 — 在任务中直接可视化架构的直接链接" title="Mermaid 图表 — 在任务中直接可视化架构的直接链接">​</a></h3>
<p>最强大的功能之一：在任务描述中直接嵌入 Mermaid 图表。通过斜杠菜单插入 Mermaid 块，编写图表语法，即可实时渲染。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/arch-task-top.png" alt="Mermaid 流程图展示微服务架构" class="img_ev3q"></p>
<p>支持的图表类型包括：</p>
<ul>
<li><strong>流程图</strong> — 系统架构、决策树</li>
<li><strong>时序图</strong> — API 交互流程、认证握手</li>
<li><strong>类图</strong> — 数据模型、实体关系</li>
<li><strong>状态图</strong> — 工作流状态、生命周期追踪</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/api-task-mermaid.png" alt="Mermaid 时序图展示认证流程" class="img_ev3q"></p>
<p>图表查看器支持全屏预览、SVG 下载，以及自动深色/浅色主题切换。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="结构化数据表格">结构化数据表格<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#%E7%BB%93%E6%9E%84%E5%8C%96%E6%95%B0%E6%8D%AE%E8%A1%A8%E6%A0%BC" class="hash-link" aria-label="结构化数据表格的直接链接" title="结构化数据表格的直接链接">​</a></h3>
<p>需要记录状态码、对比指标或追踪功能矩阵？插入表格后可直接编辑单元格。通过右键菜单添加或删除行列、切换表头行、合并单元格。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/api-task-table-checklist.png" alt="状态码表格和可交互清单" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="真正可用的清单">真正可用的清单<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#%E7%9C%9F%E6%AD%A3%E5%8F%AF%E7%94%A8%E7%9A%84%E6%B8%85%E5%8D%95" class="hash-link" aria-label="真正可用的清单的直接链接" title="真正可用的清单的直接链接">​</a></h3>
<p>任务列表渲染为可交互的复选框。在渲染视图中直接勾选完成项——无需切换到编辑模式。非常适合追踪子步骤、验收标准或部署清单。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/slide-5.png" alt="任务描述中的可交互清单" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="图片与附件--告别-obsidian-式的资源管理噩梦">图片与附件 — 告别 Obsidian 式的资源管理噩梦<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#%E5%9B%BE%E7%89%87%E4%B8%8E%E9%99%84%E4%BB%B6--%E5%91%8A%E5%88%AB-obsidian-%E5%BC%8F%E7%9A%84%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E5%99%A9%E6%A2%A6" class="hash-link" aria-label="图片与附件 — 告别 Obsidian 式的资源管理噩梦的直接链接" title="图片与附件 — 告别 Obsidian 式的资源管理噩梦的直接链接">​</a></h3>
<p>如果你用过 Obsidian 管理带图片的笔记，一定经历过这些痛苦：图片散落在本地文件夹里，路径一变就全部裂图；换一台设备打开，附件全都找不到；多端同步时冲突不断，不知道哪个版本是对的。本质问题是——Obsidian 把资源管理交给了文件系统，而文件系统天然不擅长跨设备同步。</p>
<p>UnDercontrol 从根本上解决了这个问题。所有图片和附件通过 <code>resource://</code> 协议统一管理，上传即入库，不依赖任何本地路径。无论你在 Web、桌面端还是分享链接中查看，图片始终可用——因为资源跟随数据库走，而不是跟随文件系统。</p>
<p>此外，UnDercontrol 还提供了本地文件夹的双向同步能力，可以将本地目录中的文件与服务端资源库保持同步。这个功能的细节我们会在后续的博文中专门介绍。</p>
<p><strong>图片大小调整</strong></p>
<p>插入的图片支持三档大小预设，编辑模式下悬停即可切换：</p>
<ul>
<li><strong>小（25%）</strong> — 缩略图，适合行内展示</li>
<li><strong>中（50%）</strong> — 适中尺寸</li>
<li><strong>大（100%）</strong> — 全宽展示</li>
</ul>
<p>尺寸信息以 Obsidian 兼容格式存储（<code>![描述|s](resource://id)</code>），点击图片可全屏预览。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/image-size-controls.png" alt="图片大小控件 — S、M、L 预设" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="drawio-图表--内置可视化绘图">Draw.io 图表 — 内置可视化绘图<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#drawio-%E5%9B%BE%E8%A1%A8--%E5%86%85%E7%BD%AE%E5%8F%AF%E8%A7%86%E5%8C%96%E7%BB%98%E5%9B%BE" class="hash-link" aria-label="Draw.io 图表 — 内置可视化绘图的直接链接" title="Draw.io 图表 — 内置可视化绘图的直接链接">​</a></h3>
<p>UnDercontrol 内置 Draw.io 编辑器，支持 <code>.drawio</code> 和 <code>.drawio.png</code> 格式。上传或创建 Draw.io 文件后，可直接在应用内打开编辑——无需安装任何桌面软件。编辑完成后保存为 PNG（内嵌 XML 源数据），既可渲染预览，也可随时重新编辑。</p>
<p>同样得益于 <code>resource://</code> 协议，Draw.io 图表作为资源统一管理，不存在本地路径依赖或同步丢失的问题。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="ai-驱动的文本操作">AI 驱动的文本操作<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#ai-%E9%A9%B1%E5%8A%A8%E7%9A%84%E6%96%87%E6%9C%AC%E6%93%8D%E4%BD%9C" class="hash-link" aria-label="AI 驱动的文本操作的直接链接" title="AI 驱动的文本操作的直接链接">​</a></h3>
<p>选中任务描述中的任意文本，即可显示气泡菜单。除了标准排版（加粗、斜体、删除线），还有 AI 驱动的操作：</p>
<ul>
<li><strong>润色</strong> — 重写选中文本，提升表达清晰度</li>
<li><strong>翻译</strong> — 即时翻译为其他语言</li>
<li><strong>对话</strong> — 针对选中内容提出问题</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/slide-7.png" alt="功能概览 — 文档编写的全部工具" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="实体链接--像-wiki-一样连接任务">实体链接 — 像 Wiki 一样连接任务<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#%E5%AE%9E%E4%BD%93%E9%93%BE%E6%8E%A5--%E5%83%8F-wiki-%E4%B8%80%E6%A0%B7%E8%BF%9E%E6%8E%A5%E4%BB%BB%E5%8A%A1" class="hash-link" aria-label="实体链接 — 像 Wiki 一样连接任务的直接链接" title="实体链接 — 像 Wiki 一样连接任务的直接链接">​</a></h3>
<p>在描述中直接链接到其他任务、笔记、支出、预算和账户，使用 <code>task://</code>、<code>note://</code> 等自定义协议。链接渲染为可点击的引用——点击即可跳转到关联的实体。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/entity-links-rendered.png" alt="任务描述中渲染后的实体链接" class="img_ev3q"></p>
<p>切换到源码模式可以看到原始 markdown——每个链接使用自定义协议，如 <code>[API Integration Guide](task://189571b0-...)</code>：</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/entity-links-raw.png" alt="原始 markdown 展示 task:// 协议链接" class="img_ev3q"></p>
<p>这将你的任务描述转变为一个互联的 Wiki，上下文在相关项目之间自然流转。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="cli--ai-agent--让-ai-帮你写内容">CLI + AI Agent — 让 AI 帮你写内容<a href="https://udctl.com/zh-Hans/blog/2026/04/27/markdown-features/#cli--ai-agent--%E8%AE%A9-ai-%E5%B8%AE%E4%BD%A0%E5%86%99%E5%86%85%E5%AE%B9" class="hash-link" aria-label="CLI + AI Agent — 让 AI 帮你写内容的直接链接" title="CLI + AI Agent — 让 AI 帮你写内容的直接链接">​</a></h3>
<p>因为 Markdown 是纯文本，它天然适合与 AI Agent 协作。通过 <code>ud</code> CLI，你可以让 Claude Code、Codex、OpenCode 等终端 AI 工具直接读取、撰写和更新任务内容：</p>
<ul>
<li><strong>总结归纳</strong> — 让 AI 读取一组任务，自动生成周报或迭代回顾</li>
<li><strong>提炼重构</strong> — 把零散的笔记整理成结构化的文档</li>
<li><strong>分类打标</strong> — 根据内容自动添加标签和分类</li>
<li><strong>批量创建</strong> — 从会议纪要或需求文档一键生成多个任务</li>
</ul>
<p>内容通过管道直接写入：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt;'EOF' | ud apply -f -</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">title: API Integration Guide</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">status: in-progress</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tags: [api, backend]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">## Authentication Flow</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">1. Client sends credentials to `/auth/login`</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2. Server returns JWT token</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">3. Include in `Authorization: Bearer &lt;token&gt;` header</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<p>AI 生成的 Markdown 与手写的 Markdown 在 Web、桌面、分享视图中的渲染效果完全一致。你的内容始终保持可移植、可版本控制。</p>
<hr>
<p>UnDercontrol 的 Markdown 支持意味着你的任务可以承载任意深度的技术细节——而无需离开跟踪工作进展的工具。代码块、图表、表格和清单与任务状态、截止日期和协作上下文共存。</p>
<p>立即尝试——创建一个任务，输入 <code>/</code> 看看有什么可能。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="markdown" term="markdown"/>
        <category label="productivity" term="productivity"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[远程工作区 — 从浏览器在你的机器上运行 AI 代理]]></title>
        <id>https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/</id>
        <link href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/"/>
        <updated>2026-04-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[UnDercontrol 的远程工作区功能让你直接从桌面应用触发本地机器上的 AI 代理（如 Claude Code），并实时追踪执行进度。]]></summary>
        <content type="html"><![CDATA[<p>你在 Web 界面写好任务描述，点击一个按钮。三十秒后，你笔记本上的 AI 代理已经在读取任务、编写代码、运行测试，并把进度备注实时回传到同一个任务页面——而你只需要在浏览器里看着就好。</p>
<p>这就是远程工作区。它在云端任务管理器和本地开发环境之间架起了一座桥梁，让 UnDercontrol 成为 AI 编程代理的远程控制器。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="问题所在">问题所在<a href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/#%E9%97%AE%E9%A2%98%E6%89%80%E5%9C%A8" class="hash-link" aria-label="问题所在的直接链接" title="问题所在的直接链接">​</a></h2>
<p>大多数任务管理工具完全运行在浏览器里，而你的代码在本地机器上。当你想让 AI 代理帮忙处理某件事时，你得切换到终端、粘贴上下文、盯着过程、最后手动更新任务状态。任务追踪器和执行环境是两个完全割裂的世界。</p>
<p>远程工作区消除了这个距离。你的任务描述变成了可执行指令，你的浏览器变成了控制面板，你的机器负责执行。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="工作原理">工作原理<a href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86" class="hash-link" aria-label="工作原理的直接链接" title="工作原理的直接链接">​</a></h2>
<p>架构由四个部分组成：</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/slide-2.png" alt="架构：Web UI 通过 REST 连接服务器，服务器通过 SSE 连接守护进程，守护进程通过 PTY 生成 AI 代理" class="img_ev3q"></p>
<p><strong>1. Electron 桌面应用</strong> — 运行在你的机器上，充当守护进程。当你在工作区页面注册设备时，应用通过 Server-Sent Events（SSE）连接到 UnDercontrol 服务器，等待接收指令。</p>
<p><strong>2. 服务器</strong> — 在 Web 界面和守护进程之间中转命令，持久化会话状态、备注和状态更新。</p>
<p><strong>3. 守护进程核心</strong> — Electron 渲染进程中的"大脑"。它持有认证令牌，管理 SSE 连接，驱动所有 API 调用。当工作区会话启动时，它将初始化事件转发给 Electron 主进程。</p>
<p><strong>4. AI 代理</strong> — 由 Electron 主进程在伪终端中生成的编程工具（如 Claude Code）。它读取任务、完成工作，并通过任务备注报告进度。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="设置步骤">设置步骤<a href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/#%E8%AE%BE%E7%BD%AE%E6%AD%A5%E9%AA%A4" class="hash-link" aria-label="设置步骤的直接链接" title="设置步骤的直接链接">​</a></h2>
<p>打开 UnDercontrol 桌面应用，导航到<strong>工作区</strong>页面，点击<strong>注册此设备</strong>——应用会自动检测你的机器名称和平台。就这么简单。你的机器现在是一个守护进程，已连接并准备就绪。</p>
<p>Electron 应用在后台处理一切：守护进程注册、SSE 连接、心跳和重连。不需要任何终端命令。</p>
<p>要让 AI 代理工作，你需要在机器上安装 <strong>Claude Code</strong>：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm install -g @anthropic-ai/claude-code</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="触发会话">触发会话<a href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/#%E8%A7%A6%E5%8F%91%E4%BC%9A%E8%AF%9D" class="hash-link" aria-label="触发会话的直接链接" title="触发会话的直接链接">​</a></h2>
<p>设备注册并上线后，在 Web 界面（或桌面应用本身）打开任意任务，点击操作栏中的地球图标。你会看到已连接的守护进程列表——选择一个，会话即刻开始。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/task-detail.png" alt="任务详情页展示任务描述和工作区控制" class="img_ev3q"></p>
<p>守护进程收到初始化事件后，Electron 主进程在 PTY 窗口中生成 AI 代理。接下来一切自动进行：</p>
<ol>
<li>代理读取任务描述和备注</li>
<li>规划并执行工作</li>
<li>进度备注实时出现在任务页面上</li>
<li>完成后更新任务状态</li>
</ol>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="实时观察">实时观察<a href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/#%E5%AE%9E%E6%97%B6%E8%A7%82%E5%AF%9F" class="hash-link" aria-label="实时观察的直接链接" title="实时观察的直接链接">​</a></h2>
<p>这是最让人满足的部分。当代理在你的机器上运行时，你在浏览器里实时看到它的进度。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/slide-3.png" alt="工作流程：从启动守护进程到实时观察进度的五个步骤" class="img_ev3q"></p>
<p>备注随着代理的工作不断涌入：它在读什么、改了什么、提交了哪些文件。你得到了整个会话的运行日志，而完全不需要碰终端。</p>
<p>会话面板显示：</p>
<ul>
<li><strong>状态</strong> — 运行中、规划中、等待输入、空闲或已停止</li>
<li><strong>持续时间</strong> — 会话已运行多长时间</li>
<li><strong>守护进程信息</strong> — 哪台机器在执行</li>
<li><strong>备注流</strong> — 代理的实时更新</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="交互控制">交互控制<a href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/#%E4%BA%A4%E4%BA%92%E6%8E%A7%E5%88%B6" class="hash-link" aria-label="交互控制的直接链接" title="交互控制的直接链接">​</a></h2>
<p>远程工作区不是「发射后不管」的模式。你可以在代理运行时与它交互：</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/slide-4.png" alt="交互控制：发送指令、截图、中断和停止" class="img_ev3q"></p>
<ul>
<li><strong>发送指令</strong> — 输入额外的上下文或在任务中途调整代理的方向</li>
<li><strong>截图</strong> — 捕获工作区的当前状态</li>
<li><strong>中断</strong> — 发送 Ctrl+C 信号暂停执行</li>
<li><strong>停止</strong> — 完全结束会话</li>
</ul>
<p>还有一个提示词系统。把常用的指令保存为模板，一键应用。如果你在 UnDercontrol 中设置了技能（Skills），也可以引用它们——把技能内容直接传递给代理。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="工作区仪表盘">工作区仪表盘<a href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/#%E5%B7%A5%E4%BD%9C%E5%8C%BA%E4%BB%AA%E8%A1%A8%E7%9B%98" class="hash-link" aria-label="工作区仪表盘的直接链接" title="工作区仪表盘的直接链接">​</a></h2>
<p>工作区页面提供所有活动会话和已连接守护进程的全局视图。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/workspaces-dashboard.png" alt="工作区仪表盘展示活动会话和已连接守护进程" class="img_ev3q"></p>
<p>每个会话卡片显示它正在处理的任务、哪个守护进程在运行、已运行多长时间以及最新的备注。守护进程列出了在线/离线状态、机器名称、平台和共享权限。</p>
<p>你可以直接在这个页面注册设备——一键上线。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="安全机制">安全机制<a href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/#%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B6" class="hash-link" aria-label="安全机制的直接链接" title="安全机制的直接链接">​</a></h2>
<p>远程运行 AI 代理自然会引发安全顾虑。远程工作区通过分层黑名单系统来应对：</p>
<p><strong>内置保护：</strong></p>
<ul>
<li><code>bash</code>、<code>sh</code>、<code>zsh</code> 等工具被禁止作为实现工具——代理通过受控接口运行，而不是原始 shell</li>
<li>破坏性命令（<code>rm -rf</code>、<code>mkfs</code>、<code>dd</code>）在守护进程层被阻止</li>
</ul>
<p><strong>自定义配置：</strong> 在 <code>~/.config/ud/workspace-blacklist.yml</code> 放置 YAML 文件来定义你自己的规则：</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">blocked_tools</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> bash</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> sh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">allowed_tools</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> claude</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">blocked_commands</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"rm -rf"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mkfs"</span><br></span></code></pre></div></div>
<p>守护进程还需要正确的身份验证——Electron 应用使用你已登录的会话令牌，没有你的凭据就无法进行未授权访问。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="共享守护进程">共享守护进程<a href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/#%E5%85%B1%E4%BA%AB%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B" class="hash-link" aria-label="共享守护进程的直接链接" title="共享守护进程的直接链接">​</a></h2>
<p>在团队场景中，你可以将守护进程共享给你的组。设置共享权限后，团队成员可以在你的机器上触发工作区会话——这对共享构建服务器或专用 CI 机器很有用。</p>
<p>共享通过守护进程的权限控制：只读权限让其他人查看守护进程状态，读写权限则允许他们发起会话。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="为什么这很重要">为什么这很重要<a href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E5%BE%88%E9%87%8D%E8%A6%81" class="hash-link" aria-label="为什么这很重要的直接链接" title="为什么这很重要的直接链接">​</a></h2>
<p>大多数开发者工具将任务管理和代码执行视为独立的事情。你在一个应用里计划，在另一个应用里执行，然后通过复制粘贴和上下文切换来手动弥合差距。</p>
<p>远程工作区将它们合并。你的任务描述<em>就是</em>指令。你的浏览器<em>就是</em>控制面板。工作在你的机器上发生，使用你的工具，在你的环境中——你只是不需要在终端里才能启动它。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/slide-5.png" alt="安全层：工具黑名单、命令黑名单、API Key 认证、自定义配置" class="img_ev3q"></p>
<p>对于个人开发者，这意味着更少的上下文切换。写任务、触发代理、审查输出——全在同一个界面。对于团队，这意味着一个共享的工作队列，机器可以接手并执行，同时对正在发生的事情具有完全的可见性。</p>
<p>架构还意味着你不会被锁定在任何特定的 AI 工具上。今天它与 Claude Code 配合使用；明天它可以与任何在终端中运行的代理配合。协议很简单：接收任务、完成工作、报告进度。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="开始使用">开始使用<a href="https://udctl.com/zh-Hans/blog/2026/04/25/remote-workspace/#%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8" class="hash-link" aria-label="开始使用的直接链接" title="开始使用的直接链接">​</a></h2>
<ol>
<li>下载 <a href="https://undercontrol.app/" target="_blank" rel="noopener noreferrer">UnDercontrol 桌面应用</a></li>
<li>安装 Claude Code：<code>npm install -g @anthropic-ai/claude-code</code></li>
<li>打开工作区页面，点击<strong>注册此设备</strong></li>
<li>打开任意任务，点击地球图标，选择你的守护进程</li>
</ol>
<p>查看<a href="https://udctl.com/zh-Hans/docs/workspace-terminal/">文档</a>了解完整的设置指南，包括安全配置和故障排查。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
</feed>