<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Extensions on yzchen.space</title>
    <link>https://www.yzchen.space/tags/extensions/</link>
    <description>Recent content in Extensions on yzchen.space</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 06 Jul 2026 12:00:00 +0800</lastBuildDate>
    <atom:link href="https://www.yzchen.space/tags/extensions/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Pi Web Access 介绍</title>
      <link>https://www.yzchen.space/posts/20260706/pi-web-access/</link>
      <pubDate>Mon, 06 Jul 2026 12:00:00 +0800</pubDate>
      <guid>https://www.yzchen.space/posts/20260706/pi-web-access/</guid>
      <description>&lt;h2 id=&#34;这是什么&#34;&gt;这是什么&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;pi-web-access&lt;/strong&gt; 是 Pi 生态里最常用的第三方 package，给 Pi 接入网络。本质上是把 web_search、网页抓取、GitHub 仓库、YouTube/本地视频分析、PDF 提取等能力，封装成 Pi 的 extension + skill，安装后 Pi 可以直接上网查资料。&lt;/p&gt;&#xA;&lt;p&gt;作者是 &lt;a href=&#34;https://github.com/nicobailon&#34;&gt;Nico Bailon&lt;/a&gt;，仓库在 &lt;a href=&#34;https://github.com/nicobailon/pi-web-access&#34;&gt;github.com/nicobailon/pi-web-access&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pi install npm:pi-web-access&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;安装后放在 &lt;code&gt;~/.pi/agent/npm/node_modules/pi-web-access/&lt;/code&gt;，不是 &lt;code&gt;~/.pi/agent/extensions/&lt;/code&gt; 目录。它作为 Pi Package 安装，不需要手动管理扩展文件。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;零配置可用：&lt;/strong&gt; 不需要任何 API key。默认走 Exa MCP（免费），如果你恰好有 Codex 订阅，OpenAI 搜索也能复用认证。&lt;/p&gt;&#xA;&lt;p&gt;如果觉得不如意，可以给别的搜索提供商配 API key。&lt;/p&gt;&#xA;&lt;h2 id=&#34;三个工具&#34;&gt;三个工具&lt;/h2&gt;&#xA;&lt;p&gt;安装后 Pi 会多出三个 LLM 可调用的工具。&lt;/p&gt;&#xA;&lt;h3 id=&#34;1-web_search--联网搜索&#34;&gt;1. web_search — 联网搜索&lt;/h3&gt;&#xA;&lt;p&gt;最常用。支持单条 &lt;code&gt;query&lt;/code&gt; 或批量 &lt;code&gt;queries&lt;/code&gt;（推荐一次搜 2-4 条不同角度，覆盖面更宽）：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;web_search({ query: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;rust async programming&amp;#34;&lt;/span&gt; })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;web_search({ queries: [&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;react 性能优化 2025&amp;#34;&lt;/span&gt;, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;react concurrent mode 原理&amp;#34;&lt;/span&gt;] })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;web_search({ query: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;, numResults: &lt;span style=&#34;&#34;&gt;10&lt;/span&gt;, recencyFilter: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;week&amp;#34;&lt;/span&gt; })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;web_search({ query: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;, domainFilter: [&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;github.com&amp;#34;&lt;/span&gt;, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;-reddit.com&amp;#34;&lt;/span&gt;] })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;web_search({ query: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;, provider: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;openai&amp;#34;&lt;/span&gt; })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;web_search({ query: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;, includeContent: &lt;span style=&#34;&#34;&gt;true&lt;/span&gt; })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;工作流选项：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Pi Extensions 进阶：从安装社区扩展到自己定制</title>
      <link>https://www.yzchen.space/posts/20260704/pi-extensions-advanced/</link>
      <pubDate>Sat, 04 Jul 2026 12:00:00 +0800</pubDate>
      <guid>https://www.yzchen.space/posts/20260704/pi-extensions-advanced/</guid>
      <description>&lt;p&gt;前一篇笔记讲了 extension 是什么、有什么示例、怎么写。这篇记录实际使用——怎么从官方和社区安装、以及怎么根据需求自己改造。&lt;/p&gt;&#xA;&lt;h2 id=&#34;一先自己写个简单的show-system-prompt&#34;&gt;一、先自己写个简单的：show-system-prompt&lt;/h2&gt;&#xA;&lt;h3 id=&#34;动机&#34;&gt;动机&lt;/h3&gt;&#xA;&lt;p&gt;Agent 的系统提示词由多个来源拼合而成——角色定位、工具列表、AGENTS.md、项目上下文、技能清单。Claude/Openclaw 这样的 Agent 内置系统提示词就能吃掉几千个 token。但是像 Pi 这种我们可以轻松写一个 Extention 调试提示词效果，看一眼&amp;quot;LLM 当前实际收到的 prompt 到底是什么&amp;quot;。一个 &lt;code&gt;/system-prompt&lt;/code&gt; 命令，把完整内容打印到终端。写&lt;/p&gt;&#xA;&lt;p&gt;主要就两步：&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;捕获提示词。&lt;/strong&gt; 通过 &lt;code&gt;before_agent_start&lt;/code&gt; 事件捕获 Pi 发给 LLM 的完整 system prompt，存到变量里。这是&amp;quot;post-chained&amp;quot;的值——即所有 extension 修改之后、实际发送之前的版本。如果命令被调用时还没有 turn 跑过，就降级用 &lt;code&gt;ctx.getSystemPrompt()&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;全屏覆盖层显示。&lt;/strong&gt; 用 &lt;code&gt;ctx.ui.custom()&lt;/code&gt; 打开一个覆盖层，渲染 prompt 全文。&lt;code&gt;handleInput&lt;/code&gt; 监听 &lt;code&gt;Escape&lt;/code&gt; 或 &lt;code&gt;q&lt;/code&gt; 关闭，&lt;code&gt;render&lt;/code&gt; 用 &lt;code&gt;wrapTextWithAnsi&lt;/code&gt; 处理终端宽度换行。加了宽度缓存，只在终端尺寸变化时重新计算。&lt;/p&gt;&#xA;&lt;h3 id=&#34;示例代码&#34;&gt;示例代码&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; { ExtensionAPI } &lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;@earendil-works/pi-coding-agent&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; { Key, matchesKey, wrapTextWithAnsi } &lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;@earendil-works/pi-tui&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;function&lt;/span&gt; (pi: &lt;span style=&#34;&#34;&gt;ExtensionAPI&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;let&lt;/span&gt; lastPrompt: &lt;span style=&#34;&#34;&gt;string&lt;/span&gt; | &lt;span style=&#34;font-weight:bold&#34;&gt;undefined&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  pi.on(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;before_agent_start&amp;#34;&lt;/span&gt;, (event) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lastPrompt = event.systemPrompt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  pi.registerCommand(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;system-prompt&amp;#34;&lt;/span&gt;, {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;Print the current session&amp;#39;s system prompt&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    handler: &lt;span style=&#34;&#34;&gt;async&lt;/span&gt; (_args, ctx) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;const&lt;/span&gt; prompt = lastPrompt ?? ctx.getSystemPrompt();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (ctx.mode !== &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;tui&amp;#34;&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ctx.ui.notify(prompt, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;info&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;await&lt;/span&gt; ctx.ui.custom&amp;lt;&lt;span style=&#34;font-weight:bold&#34;&gt;void&lt;/span&gt;&amp;gt;((_tui, _theme, _kb, done) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;let&lt;/span&gt; cachedWidth = 0;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;let&lt;/span&gt; cachedLines: &lt;span style=&#34;&#34;&gt;string&lt;/span&gt;[] = [];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          render(width: &lt;span style=&#34;&#34;&gt;number&lt;/span&gt;): &lt;span style=&#34;&#34;&gt;string&lt;/span&gt;[] {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (width !== cachedWidth) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              cachedWidth = width;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              cachedLines = wrapTextWithAnsi(prompt, width);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; cachedLines;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          handleInput(data: &lt;span style=&#34;&#34;&gt;string&lt;/span&gt;): &lt;span style=&#34;font-weight:bold&#34;&gt;void&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (matchesKey(data, Key.escape) || data === &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;q&amp;#34;&lt;/span&gt;) done();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          invalidate(): &lt;span style=&#34;font-weight:bold&#34;&gt;void&lt;/span&gt; { cachedWidth = 0; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;结果&#34;&gt;结果&lt;/h3&gt;&#xA;&lt;p&gt;输入 &lt;code&gt;/system-prompt&lt;/code&gt; 后，终端全屏显示 Pi 当前 session 的完整系统提示词：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Pi Extensions 入门指南</title>
      <link>https://www.yzchen.space/posts/20260702/pi-extensions-intro/</link>
      <pubDate>Thu, 02 Jul 2026 12:00:00 +0800</pubDate>
      <guid>https://www.yzchen.space/posts/20260702/pi-extensions-intro/</guid>
      <description>&lt;h2 id=&#34;pi-的-extension-是什么&#34;&gt;Pi 的 Extension 是什么&lt;/h2&gt;&#xA;&lt;p&gt;Extension（扩展）是 Pi 的 TypeScript 插件系统，也是它可扩展性体系中最核心的一环。你可以把它理解成给 Pi 写&amp;quot;插件&amp;quot;，用来定制它的行为、添加新能力，或者接入外部系统。&lt;/p&gt;&#xA;&lt;p&gt;Pi 的哲学是&amp;quot;不把工作流强加给你&amp;quot;，所以像权限控制、子代理、plan mode 这些其他工具内置的功能，Pi 全都不做——而是把能力交给 extension，让你自己决定怎么做。写一个 extension、或者装一个别人写好的 Pi Package，就能把 Pi 调整成你想要的形状，不需要 fork 源码。&lt;/p&gt;&#xA;&lt;p&gt;具体来说，extension 能做这些事情：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;注册自定义工具&lt;/strong&gt;，让 LLM 在对话中可以调用 —— 比如一个 &lt;code&gt;deploy&lt;/code&gt; 工具、一个 &lt;code&gt;todo&lt;/code&gt; 工具&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;拦截事件&lt;/strong&gt;，在关键节点插手 —— 比如 LLM 想执行 &lt;code&gt;rm -rf&lt;/code&gt; 的时候弹窗问你要不要放行&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;和用户交互&lt;/strong&gt;，弹确认框、选择列表、输入框、通知&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;构建自定义 UI&lt;/strong&gt;，从简单的状态栏文字，到完全接管编辑器，到游戏覆盖层&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;注册命令&lt;/strong&gt;，添加 &lt;code&gt;/mycommand&lt;/code&gt; 这样的自定义斜杠命令&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;持久化状态&lt;/strong&gt;，让数据跨重启存活，还能随着 session 的分支自动正确还原&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;自定义渲染&lt;/strong&gt;，控制工具调用的结果在终端里怎么展示&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;它还覆盖了 Pi 运行的完整生命周期——从 session 启动、LLM 对话开始、每个 turn、每次工具调用、到 session 关闭，几乎所有环节都可以插入逻辑。所以 extension 不光能&amp;quot;加功能&amp;quot;，也能&amp;quot;改行为&amp;quot;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;快速看一眼代码&#34;&gt;快速看一眼代码&lt;/h2&gt;&#xA;&lt;p&gt;一个最简 extension 长这样。三件事：拦截危险命令、注册一个工具、注册一个命令：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; { ExtensionAPI } &lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;@earendil-works/pi-coding-agent&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; { Type } &lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;typebox&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;function&lt;/span&gt; (pi: &lt;span style=&#34;&#34;&gt;ExtensionAPI&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-style:italic&#34;&gt;// 拦截危险 bash 命令&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;  pi.on(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;tool_call&amp;#34;&lt;/span&gt;, &lt;span style=&#34;font-weight:bold&#34;&gt;async&lt;/span&gt; (event, ctx) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (event.toolName === &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;bash&amp;#34;&lt;/span&gt; &amp;amp;&amp;amp; event.input.command?.includes(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;rm -rf&amp;#34;&lt;/span&gt;)) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;const&lt;/span&gt; ok = &lt;span style=&#34;font-weight:bold&#34;&gt;await&lt;/span&gt; ctx.ui.confirm(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;危险操作!&amp;#34;&lt;/span&gt;, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;允许 rm -rf 吗?&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (!ok) &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; { block: &lt;span style=&#34;&#34;&gt;true&lt;/span&gt;, reason: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;用户拒绝&amp;#34;&lt;/span&gt; };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-style:italic&#34;&gt;// 注册一个 LLM 可调用的工具&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;  pi.registerTool({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;greet&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;向某人打招呼&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parameters: &lt;span style=&#34;&#34;&gt;Type.Object&lt;/span&gt;({ name: &lt;span style=&#34;&#34;&gt;Type.String&lt;/span&gt;() }),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;async&lt;/span&gt; execute(_id, params) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; { content: [{ &lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt;: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;, text: &lt;span style=&#34;font-style:italic&#34;&gt;`Hello, &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;${&lt;/span&gt;params.name&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;!`&lt;/span&gt; }] };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-style:italic&#34;&gt;// 注册一个用户可用的命令&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;  pi.registerCommand(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;, {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;打招呼&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    handler: &lt;span style=&#34;&#34;&gt;async&lt;/span&gt; (args, ctx) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ctx.ui.notify(&lt;span style=&#34;font-style:italic&#34;&gt;`Hello &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;${&lt;/span&gt;args || &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;world&amp;#34;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;!`&lt;/span&gt;, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;info&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;放在 &lt;code&gt;~/.pi/agent/extensions/my-ext.ts&lt;/code&gt;，或者用 &lt;code&gt;pi -e ./my-ext.ts&lt;/code&gt; 立即测试。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
