{"_id":"@aiwayds/pi-turbo","_rev":"3-c2254f3ecfdac31012d9aeb85d4be87f","name":"@aiwayds/pi-turbo","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@aiwayds/pi-turbo","version":"0.1.0","license":"MIT","_id":"@aiwayds/pi-turbo@0.1.0","maintainers":[{"name":"aiwayds","email":"aiway.ds@qq.com"}],"homepage":"https://github.com/fan56/pi-turbo#readme","bugs":{"url":"https://github.com/fan56/pi-turbo/issues"},"bin":{"pi-tb":"bin/pi-tb.js"},"dist":{"shasum":"7209f8ea3d64455bfb1b32233835bf88827a1c91","tarball":"https://registry.npmjs.org/@aiwayds/pi-turbo/-/pi-turbo-0.1.0.tgz","fileCount":12,"integrity":"sha512-yi+EZiSr+sF+EIJ5h/GPQGE7jx+VsuoOsLxvKQt7LbNMcwGJWNq26Bmi71HN2tKpA6unX07ZTmLE/rokP7oYSg==","signatures":[{"sig":"MEYCIQDuy3t9tabTEEiah5D37SMdBpecN1r59xc0Eo1hmJSIHwIhAMzqx2VfcTZxp9kOvSAxIb7G9Wbw2l3WJhyU3cnE8WJp","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":37948},"type":"module","engines":{"node":">=22.19.0"},"gitHead":"cad11e11cbf1a047185c3242c1344243f2a43846","_npmUser":{"name":"aiwayds","email":"aiway.ds@qq.com"},"repository":{"url":"git+https://github.com/fan56/pi-turbo.git","type":"git"},"_npmVersion":"11.17.0","description":"pi-turbo — optimized pi launcher with parallel extension loading and footer caching","directories":{},"_nodeVersion":"26.4.0","_hasShrinkwrap":false,"peerDependencies":{"@earendil-works/pi-coding-agent":">=0.50.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-turbo_0.1.0_1785806336096_0.36160006759958563","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aiwayds/pi-turbo","version":"0.1.1","license":"MIT","_id":"@aiwayds/pi-turbo@0.1.1","maintainers":[{"name":"aiwayds","email":"aiway.ds@qq.com"}],"homepage":"https://github.com/fan56/pi-turbo#readme","bugs":{"url":"https://github.com/fan56/pi-turbo/issues"},"bin":{"pi-tb":"bin/pi-tb.js"},"dist":{"shasum":"3e976d760623034cc088d299a63795d976d8aa21","tarball":"https://registry.npmjs.org/@aiwayds/pi-turbo/-/pi-turbo-0.1.1.tgz","fileCount":12,"integrity":"sha512-t4wZMMDxSzpuigi82NfdZN8bbfwRbi5B2kF5uBUPfcIqX97oTgv1oQX92sKM3ER/9sGPhI43vWwjwaeB74QZPA==","signatures":[{"sig":"MEUCIHzDD8M5vihar2Y4TM3KImAaZZaQPOiU08asv8AfkepLAiEAzFQ2g2Cb7tedCbk58l6r6EZzAumoVosb2N2xQz9IRJY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":39655},"type":"module","engines":{"node":">=22.19.0"},"gitHead":"62ac0e48ebc3e7b608dea35da9ab762dbf471561","_npmUser":{"name":"aiwayds","email":"aiway.ds@qq.com"},"repository":{"url":"git+https://github.com/fan56/pi-turbo.git","type":"git"},"_npmVersion":"11.17.0","description":"pi-turbo — optimized pi launcher with parallel extension loading and footer caching","directories":{},"_nodeVersion":"26.4.0","_hasShrinkwrap":false,"peerDependencies":{"@earendil-works/pi-coding-agent":">=0.50.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-turbo_0.1.1_1785808861937_0.6738900744244976","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aiwayds/pi-turbo","version":"0.2.0","description":"pi-turbo — optimized pi launcher with parallel extension loading and footer caching","type":"module","bin":{"pi-tb":"bin/pi-tb.js"},"engines":{"node":">=22.19.0"},"peerDependencies":{"@earendil-works/pi-coding-agent":">=0.50.0"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/fan56/pi-turbo.git"},"homepage":"https://github.com/fan56/pi-turbo#readme","bugs":{"url":"https://github.com/fan56/pi-turbo/issues"},"gitHead":"bc8956566c53266563361b58641859cb652e7d77","_id":"@aiwayds/pi-turbo@0.2.0","_nodeVersion":"26.4.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-FniNlvmjHx0GTR8zo1NAHO/OsmsGpVyDzOrsX0XO3yKHaU85nAomDyEp0zSnyD8284oZuUS2b361AXCoxpkdeA==","shasum":"f334e2c9825e7d1804526c4e16122b71956f09e5","tarball":"https://registry.npmjs.org/@aiwayds/pi-turbo/-/pi-turbo-0.2.0.tgz","fileCount":13,"unpackedSize":43173,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC0qhOO8stonCvrxWuBPs3diMuH3kcxjcbYFD2XxNCC1QIgXM7zijfCyF4Vxuk2cxzPK+eA9k89KrrGz/JgcjlC+gI="}]},"_npmUser":{"name":"aiwayds","email":"aiway.ds@qq.com"},"directories":{},"maintainers":[{"name":"aiwayds","email":"aiway.ds@qq.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-turbo_0.2.0_1785985427437_0.006728425775094582"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-04T01:18:55.962Z","modified":"2026-08-06T03:03:47.728Z","0.1.0":"2026-08-04T01:18:56.247Z","0.1.1":"2026-08-04T02:01:02.081Z","0.2.0":"2026-08-06T03:03:47.586Z"},"bugs":{"url":"https://github.com/fan56/pi-turbo/issues"},"license":"MIT","homepage":"https://github.com/fan56/pi-turbo#readme","repository":{"type":"git","url":"git+https://github.com/fan56/pi-turbo.git"},"description":"pi-turbo — optimized pi launcher with parallel extension loading and footer caching","maintainers":[{"name":"aiwayds","email":"aiway.ds@qq.com"}],"readme":"[English](README.md) | 中文\n\n# pi-turbo\n\n[pi](https://github.com/earendil-works/pi) 的性能加速包装器 — 两个运行时 monkey-patch，对 pi 本身零修改。\n\n- **启动加速**：并行加载扩展（冷启动提速 33%）\n- **运行时优化**：Footer 渲染缓存（消除长对话中按 Enter 的卡顿）\n\n## 为什么做 pi-turbo\n\n这个仓库源于日常使用 pi 的两个痛点：\n\n1. **扩展加载又慢又串行。** pi 逐个加载扩展，不支持异步 — 每次冷启动都要盯着空白终端等上几秒。pi-turbo 先为每个扩展记录耗时画像，之后把 I/O 密集扩展（如 MCP 握手）放到后台加载，其余扩展照常加载，让 I/O 等待与 CPU 工作重叠（A/B 测试约提速 30%）。\n2. **长会话卡得像 hang 住。** 消息超过约 100 条后，按回车发送会出现延迟，而且消息越多越卡。一个重要原因：Footer 每帧渲染都会重新扫描全部会话条目。pi-turbo 按条目数缓存这些结果（`getEntries()` / `getContextUsage()`），后续渲染直接命中 O(1) 缓存，不再重复 O(n) 扫描 — 削掉了随会话增长而放大的每帧渲染开销。\n\n## 它解决什么问题（Problems it solves）\n\n### 1. 启动慢（扩展加载）\n\n**症状**：pi 启动需要 4-5 秒。你只能盯着空白终端，等扩展一个一个加载完。\n\n**原因**：pi 串行加载所有扩展。部分扩展（如 MCP 握手）是 I/O 密集型的，CPU 全程空转等待。\n\n**方案**：pi-turbo 对 `DefaultResourceLoader.loadFinalExtensionSet` 打补丁：\n\n1. **画像**（首次运行）：逐个加载扩展，记录每个扩展的耗时（EMA）\n2. **优化**（后续运行）：识别 I/O 密集扩展（EMA > 1000ms，如 MCP 握手），放入后台加载，CPU 密集扩展继续串行加载\n3. **重叠**：I/O 等待与 CPU 工作并行执行 → 总耗时 = max(后台, 串行)\n\n| 模式 | 扩展加载耗时 | 提速 |\n| --- | --- | --- |\n| 串行（pi 默认） | ~4660ms | — |\n| **定向并行** | **~3125ms** | **33%** |\n\n### 2. Enter 键卡顿（Footer O(n) 扫描）\n\n**症状**：对话超过 100 条消息后，按 Enter 有明显延迟。对话越长，卡顿越严重。\n\n**原因**：pi 的 Footer 每次渲染都重新扫描全部会话条目（O(n)）来计算 token 用量统计，加上 `getContextUsage()` 要做树遍历和对每条消息执行 `JSON.stringify`。\n\n**方案**：pi-turbo 对 `SessionManager.getEntries()` 和 `AgentSession.getContextUsage()` 加智能缓存：\n\n- `getEntries()`：返回缓存数组，仅在追加新条目时失效（O(1) vs O(n) 过滤 + 新数组分配）\n- `getContextUsage()`：按条目数缓存，避免重复树遍历和 token 估算（O(1) vs O(n) + O(内容大小)）\n\n## 对 pi 的影响（Impact on pi）\n\n**零。** pi-turbo 不碰 pi 的源码、node_modules 或配置文件。\n\n- 所有补丁都是原型方法上的运行时 monkey-patch\n- 每个补丁都包在 try-catch 里 — 任何错误都回退到 pi 的原始行为\n- kill-switch 可以独立禁用任何优化\n- 卸载后不留痕迹；pi 和之前完全一样\n\n唯一可观察到的区别：pi 启动更快，Footer 不再卡顿。\n\n## 证据（Evidence）\n\n### 启动：A/B 基准测试\n\n20 个扩展，每种模式各跑 3 次：\n\n串行基线 (`PI_TURBO_SERIAL=1`)：5140ms, 5000ms, 9251ms\n\n并行（默认）：\n\n```\n⚡ pi-turbo: 20 exts in 6507ms (serial ~9432ms, saved 31%, 1 chunk)\n⚡ pi-turbo: 20 exts in 8387ms (serial ~11489ms, saved 27%, 1 chunk)\n⚡ pi-turbo: 20 exts in 9796ms (serial ~13797ms, saved 29%, 1 chunk)\n```\n\n> **注**：\"saved %\" 按每次运行计算：(估算串行时间 - 实际并行时间) / 估算串行时间。估算串行时间 = 各扩展 EMA 画像之和。绝对时间随系统负载波动；比率才是稳定指标。\n\n### 启动：EMA 画像\n\n`pi-tb --status` 输出示例：\n\n```\nextensions/index.ts    2527ms  ← lean-ctx MCP 桥接（I/O 密集）\ndist/index.js           309ms\n其余 18 个扩展          <100ms\n```\n\nI/O 密集扩展（2527ms）放入后台块，与 CPU 密集扩展的串行加载并行执行。I/O 等待与 CPU 工作重叠，总耗时取决于两者中较慢的那个，而非两者之和。\n\n### Footer：pi 源码中的 O(n) 扫描\n\n代码级证据（无法通过基准测试量化，以下为 pi 源码路径）：\n\n- `footer.js:80` — 每帧渲染调用 `sessionManager.getEntries()`\n- `session-manager.js:980` — `getEntries()` 执行 `this.fileEntries.filter(e => e.type !== \"session\")` — 每次调用 O(n) 过滤 + 新数组分配\n- `agent-session.js:2534` — `getContextUsage()` 执行 `getBranch()` O(n) 树遍历 + `estimateContextTokens` 对所有消息 `JSON.stringify`\n\n`fileEntries` 只追加不删除，n 随对话增长。100+ 条消息后，每次按 Enter 触发 O(n) 扫描 + O(内容大小) 序列化 → 可感知卡顿。\n\n**修复方式**：WeakMap 缓存，以 `fileEntries.length` 为键（利用只追加不变量）。缓存命中 = O(1)。缓存未命中（新条目追加）= 一次 O(n)，之后重新缓存。\n\n## 安装（Install）\n\n```bash\nnpm i -g @aiwayds/pi-turbo\n```\n\n或从源码安装：\n\n```bash\ngit clone https://github.com/fan56/pi-turbo.git\ncd pi-turbo\n./install.sh\n```\n\n### 从 pi-opt 升级\n\n如果你之前安装过 pi-opt，直接重新运行 `./install.sh` 即可。它会：\n\n- 移除旧的 `pi-opt` 全局命令\n- 安装新的 `pi-tb` 命令\n- 将时序数据从 `~/.pi-opt/` 迁移到 `~/.pi-turbo/`\n\n## 使用（Usage）\n\n```bash\npi-tb                          # 启动 pi，启用所有优化\npi-tb --status                 # 查看各扩展的时序统计\nPI_TURBO_SERIAL=1 pi-tb        # 禁用启动优化（A/B 基线对比）\nPI_TURBO_NO_FOOTER=1 pi-tb     # 仅禁用 Footer 缓存\n```\n\n## 启动日志（Startup logging）\n\npi-turbo 会往 stderr 输出启动信息日志。可通过 `PI_TURBO_STATS` 控制：\n\n- `PI_TURBO_STATS=0` — 关闭所有启动信息性日志（错误与回退日志仍会显示）。默认开启一行启动摘要。\n- `PI_TURBO_STATS=1` — 额外显示 chunk 细节等详细统计。\n- `PI_TURBO_SERIAL=1` — 禁用并行加载（A/B 基线测试用）。\n\n## 卸载（Uninstall）\n\n```bash\n./uninstall.sh\n```\n\n## 工作原理（How it works）\n\npi-turbo 是一个轻量包装器，在运行时打两个 monkey-patch：\n\n1. **扩展加载器** — 替换 `DefaultResourceLoader.prototype.loadFinalExtensionSet`，使用 EMA 时序画像识别 I/O 密集扩展并行加载。\n\n2. **Footer 渲染缓存** — 包装 `SessionManager.prototype.getEntries` 和 `AgentSession.prototype.getContextUsage`，以条目数为键缓存结果，消除每帧渲染的 O(n) 扫描。\n\n两个补丁都是 fail-safe 的：任何错误都回退到 pi 的原始行为。\n\n## 文件（Files）\n\n- `bin/pi-tb.js` — CLI 入口\n- `src/patch.js` — monkey-patch 编排器\n- `src/footer-patch.js` — Footer 渲染缓存\n- `src/targeted-loader.js` — 定向并行加载算法\n- `src/timing.js` — EMA 时序记录器\n- `src/config.js` — 路径和常量\n- `~/.pi-turbo/per-ext-timings.json` — 各扩展时序数据（自动创建）\n\n## 环境变量（Environment variables）\n\n| 变量 | 默认值 | 作用 |\n| --- | --- | --- |\n| `PI_TURBO_SERIAL=1` | — | kill-switch：强制串行加载扩展 |\n| `PI_TURBO_OFF=1` | — | 完全跳过所有补丁 |\n| `PI_TURBO_CHUNK` | 4 | 每个并行块包含的扩展数 |\n| `PI_TURBO_DEBUG=1` | — | 输出详细加载日志 |\n| `PI_TURBO_PROFILE=1` | — | 强制重新画像（忽略现有 EMA 数据） |\n| `PI_TURBO_NO_FOOTER=1` | — | 仅禁用 Footer 缓存 |\n| `PI_TURBO_STATS` | 默认开启摘要 | `=0` 关闭所有启动信息性日志（错误与回退日志仍会显示）；`=1` 额外显示详细统计 |\n\n## 系统要求（Requirements）\n\n- Node.js >= 22.19.0\n- pi (`@earendil-works/pi-coding-agent`) >= 0.50.0\n","readmeFilename":"README.zh-CN.md"}