{"_id":"@cluski/pi-by-the-way","name":"@cluski/pi-by-the-way","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@cluski/pi-by-the-way","version":"0.1.0","description":"A /btw (by-the-way) side-question extension for pi, ported from kimi-code's btw feature","type":"module","keywords":["pi-package","pi-extension"],"license":"MIT","publishConfig":{"access":"public"},"pi":{"extensions":["./extensions/btw/index.ts"]},"scripts":{"typecheck":"tsc --noEmit","format":"prettier --write .","format:check":"prettier --check ."},"peerDependencies":{"@earendil-works/pi-agent-core":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-coding-agent":"*","@earendil-works/pi-tui":"*","typebox":"*"},"devDependencies":{"prettier":"^3.9.6","typescript":"^5.5.0"},"engines":{"node":">=22.19.0"},"gitHead":"599cd28620f2eb64286ab6563194352fd079340c","_id":"@cluski/pi-by-the-way@0.1.0","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-jJfGTkylzqsmjVZFRNI4blRDLcg2T7lcGGTp1L7vd83vSJ+U4e5UJDjku4baZyquPsMzGRzfwyS4FahLg92Iuw==","shasum":"061e1dd2150cb914595060983df96ca6ce97402c","tarball":"https://registry.npmjs.org/@cluski/pi-by-the-way/-/pi-by-the-way-0.1.0.tgz","fileCount":6,"unpackedSize":41330,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC+K41u0MOPcqyxOHByoHgsHRN4M757BEdZ2DbcQX/PXwIgergWekMo0NaH9FtmepLe6REzGxKGFTjHqjMsw0ED+jE="}]},"_npmUser":{"name":"cluski","email":"824537320@qq.com"},"directories":{},"maintainers":[{"name":"cluski","email":"824537320@qq.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-by-the-way_0.1.0_1784725701182_0.27885373464814167"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-22T13:08:21.008Z","0.1.0":"2026-07-22T13:08:21.313Z","modified":"2026-07-22T13:08:21.533Z"},"maintainers":[{"name":"cluski","email":"824537320@qq.com"}],"description":"A /btw (by-the-way) side-question extension for pi, ported from kimi-code's btw feature","keywords":["pi-package","pi-extension"],"license":"MIT","readme":"# pi-by-the-way\n\n为 [`pi`](https://github.com/earendil-works/pi) 开发的 **`/btw`（by the way）** 侧问扩展，移植自 kimi-code 的 `btw` 功能（`packages/agent-core-v2/src/session/btw/`）。\n\n`/btw` 会打开一个**侧信道**：当前对话的一个轻量 fork，继承完整上下文但**禁用所有工具调用**。这样你就能在主 agent 不受影响、继续干活的同时，随口问一句\"对了，这段为什么这么写？\"。后续追问会在同一条侧信道里继续；关闭面板后状态保留，下次 `/btw` 还能接着用。\n\n```\n╭─ 💬 by the way — side channel, tools disabled ─────────────────╮\n│ Q: 这段代码为什么用 reduce 而不是 for 循环?                       │\n│                                                                 │\n│ 这里的 reduce 是为了把 map+filter 两步合成一次遍历，避免中间      │\n│ 数组。如果后续要加 break 语义，再换成 for-of 也可以…             │\n│ …                                                               │\n│ ❯ _                                                             │\n│ Enter 发送 · Esc 关闭 · ↑/↓ 滚动 · /btw reset 重新 fork          │\n╰─────────────────────────────────────────────────────────────────╯\n```\n\n## 安装\n\n这是一个 npm pi 包（`package.json` 里带 `pi` manifest 和 `pi-package` 关键字）。有三种用法：\n\n**1) 本地开发 / 试跑（不发布）** —— 直接用本地路径加载：\n\n```bash\npi -e .                       # 临时加载（本次运行有效，不写入设置）\npi install .                  # 安装到用户设置（~/.pi/agent/settings.json）\npi install -l .               # 安装到项目设置（.pi/settings.json，可随仓库共享给团队）\n```\n\n**2) 发布到 npm 后安装**：\n\n```bash\nnpm publish --access public              # 先登录拥有 @cluski scope 的 npm 账号\npi install npm:@cluski/pi-by-the-way      # 别人/别的项目这样装\n```\n\n**3) 从 git 安装**（推到 GitHub 后）：\n\n```bash\npi install git:github.com/<user>/pi-by-the-way@v0.1.0\n```\n\n> 包里的 5 个 `@earendil-works/*` + `typebox` 是 **peerDependencies**（`*`），不打包进 tarball —— pi 运行时自带这些核心包，版本自动对齐。\n\n## 用法\n\n| 命令          | 行为                                                                                        |\n| ------------- | ------------------------------------------------------------------------------------------- |\n| `/btw <问题>` | **重新 fork 一份快照**并提问（与 kimi-code 一致：每次 `/btw <q>` 都启动一个新的侧 agent）。 |\n| `/btw`        | 直接打开面板：若已有侧信道对话则继续，否则打开一个空面板，在面板内输入问题。                |\n| `/btw reset`  | 丢弃快照和对话记录，下次 `/btw` 从当前主对话重新 fork。                                     |\n\n面板内操作：\n\n- **Enter** —— 发送追问\n- **Esc**（流式输出中）→ **取消**当前这轮；**Esc**（空闲时）→ **关闭**面板\n- **↑ / ↓ / PgUp / PgDn** —— 滚动对话记录\n\n## 从 kimi-code 到 pi 的映射\n\nkimi-code 有进程内的 agent 运行时，提供 fork + 权限策略服务。pi 没有把这些暴露给扩展，但扩展 API 提供了等价的积木：\n\n| kimi-code                                                         | 本扩展                                                                                                                                                                |\n| ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `IAgentLifecycleService.fork('main')` 复制 profile + 全量消息历史 | `ctx.sessionManager.getBranch()` → `convertToLlm()` 生成一个定格的 `Message[]` 快照；model = `ctx.model`，thinking = `pi.getThinkingLevel()`                          |\n| 给子 agent 追加 `SIDE_QUESTION_SYSTEM_REMINDER`                   | 追加到 `ctx.getSystemPrompt()` 末尾，从而**保持 provider 的 prompt-cache 前缀**                                                                                       |\n| `DenyAllPermissionPolicyService`（工具可见，但每次调用都拒绝）    | 工具定义**照发**（前缀相同、缓存命中）；每个 `toolUse` 响应回灌一个 `TOOL_CALL_DISABLED_MESSAGE` 的 `toolResult`，然后重试（由 `MAX_TOOL_REJECTION_ROUNDS` 限制次数） |\n| 子 `agent_id` + prompt 路由（追问复用同一个子 agent）             | 内存里的 `state.messages` 在面板存活期内随追问累积                                                                                                                    |\n| `BtwPanelController` + 按 `agentId` 路由 `assistant.delta` 事件   | `BtwPanelComponent` overlay，直接消费 pi-ai 的 `AssistantMessageEventStream`（`text_delta` / `thinking_delta`）                                                       |\n| `session.cancel()`                                                | 每轮一个独立的 `AbortController`                                                                                                                                      |\n\nLLM 调用本身用的是 `@earendil-works/pi-ai/compat` 的 `streamSimple()`，鉴权通过 `ctx.modelRegistry.getApiKeyAndHeaders(ctx.model)` 解析 —— 与 pi 官方 `handoff.ts` 示例同一套模式。\n\n### 两个值得说明的设计决策\n\n1. **上下文快照是定格的。** 和 kimi-code 的 fork 一样，侧信道只在（首次 `/btw <q>` 时）抓取一次分支；之后主 agent 的新对话**不会**流入侧信道。`/btw reset` 重新抓取；`/btw`（无参）则重新打开已有的对话记录。\n\n2. **工具照发但拒绝调用（kimi 的\"方案 B\"）。** 发送真实工具定义能让请求前缀与主 agent **逐字节一致**，从而命中 Anthropic 等 provider 的 prompt cache。如果模型仍然发起工具调用，我们就回一条 disabled-message 的 `toolResult`，让它改用文本回答 —— 完全复刻 kimi-code 的行为。更简单的做法（完全不传工具）也能跑，但会损失这层缓存命中率。\n\n## 项目结构\n\n```\npi-by-the-way/\n├── package.json   npm 包清单（name/version/keywords=pi-package/pi.extensions manifest/peerDependencies）\n├── extensions/\n│   └── btw/\n│       ├── index.ts     扩展工厂、/btw 命令、侧信道引擎\n│       ├── context.ts   会话分支 → pi-ai Message[] 快照（处理 compaction）\n│       └── panel.ts     overlay 面板组件：对话记录 + markdown + 输入框 + 滚动\n├── tsconfig.json\n└── README.md\n```\n\n> `pi.extensions` 指向 `./extensions/btw/index.ts` 这个**文件**（而不是目录），加载零歧义；`context.ts`/`panel.ts` 由 `index.ts` 通过相对 import 引入，不会被当成独立扩展加载。\n\n## 类型检查\n\n```bash\nnpm install        # dev 依赖：typescript；pi 核心包通过软链提供类型\nnpm run typecheck\n```\n\n> 注意：`node_modules/@earendil-works/*` 和 `node_modules/typebox` 是指向全局安装的 `pi-coding-agent` 的软链，用来在本地对齐运行时类型（因为它们是 peerDependencies，`npm install` 不会自动装）。如果 `npm install` 把软链 prune 掉了，重新执行一遍 `ln -sfn` 即可。\n\n## 注意事项\n\n- 仅支持交互式（TUI）模式 —— 在 `rpc`/`json`/`print` 模式下 `/btw` 只会通知并直接返回。\n- 侧信道对话是**易失的**（内存中），和 kimi-code 一样；不会写入 session 文件。`/reload` 和切换会话都会清空它。\n","readmeFilename":"README.md","_rev":"1-532216349b1af14f86de793bef95c7bf"}