@maxiaochao/pi-toolkit 0.9.3 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +40 -90
- package/extensions/{btw.ts → btw/index.ts} +1 -1
- package/extensions/{tests/btw-history.mjs → btw/tests/history.mjs} +1 -1
- package/package.json +19 -10
- package/packages/cache-export/CHANGELOG.md +5 -0
- package/packages/cache-export/README.md +70 -0
- package/packages/cache-export/TESTING.md +17 -0
- package/packages/cache-export/docs/demo.gif +0 -0
- package/packages/cache-export/docs/screenshot.png +0 -0
- package/{extensions/cache-export/index.ts → packages/cache-export/extensions/cache-export.ts} +1 -1
- package/packages/cache-export/package.json +22 -0
- package/{extensions/cache-export → packages/cache-export/src}/render.ts +1 -1
- package/{extensions → packages}/cache-export/tests/e2e-browser.mjs +2 -2
- package/{extensions → packages}/cache-export/tests/run-tests.mjs +2 -2
- package/packages/codex-edit/CHANGELOG.md +32 -0
- package/packages/codex-edit/README.md +123 -0
- package/packages/codex-edit/TESTING.md +20 -0
- package/{extensions → packages}/codex-edit/codex-edit-explainer.html +41 -20
- package/packages/codex-edit/docs/demo.gif +0 -0
- package/packages/codex-edit/docs/screenshot.png +0 -0
- package/{extensions/codex-edit/index.ts → packages/codex-edit/extensions/codex-edit.ts} +3 -3
- package/packages/codex-edit/package.json +48 -0
- package/{extensions/codex-edit → packages/codex-edit/src}/parser.ts +1 -2
- package/{extensions → packages}/codex-edit/summary.md +1 -1
- package/{extensions → packages}/codex-edit/tests/package.test.mjs +12 -7
- package/{extensions → packages}/codex-edit/tests/parser.test.mjs +100 -3
- package/packages/tiny-subagent/CHANGELOG.md +12 -0
- package/packages/tiny-subagent/README.md +128 -0
- package/packages/tiny-subagent/TESTING.md +20 -0
- package/packages/tiny-subagent/agents/worker.md +9 -0
- package/packages/tiny-subagent/docs/demo.gif +0 -0
- package/packages/tiny-subagent/docs/screenshot.png +0 -0
- package/packages/tiny-subagent/extensions/tiny-subagent.ts +228 -0
- package/packages/tiny-subagent/package.json +44 -0
- package/packages/tiny-subagent/src/personas.ts +110 -0
- package/packages/tiny-subagent/src/runner.ts +319 -0
- package/packages/tiny-subagent/tests/run-tests.mjs +111 -0
- package/packages/todo/CHANGELOG.md +5 -0
- package/packages/todo/README.md +63 -0
- package/packages/todo/TESTING.md +20 -0
- package/packages/todo/docs/demo.gif +0 -0
- package/packages/todo/docs/screenshot.png +0 -0
- package/{extensions → packages/todo/extensions}/todo.ts +1 -1
- package/packages/todo/package.json +25 -0
- package/{extensions → packages/todo}/tests/todo-state.mjs +1 -1
- package/packages/worktree/CHANGELOG.md +5 -0
- package/packages/worktree/README.md +60 -0
- package/packages/worktree/TESTING.md +21 -0
- package/packages/worktree/docs/demo.gif +0 -0
- package/packages/worktree/docs/screenshot.png +0 -0
- package/packages/worktree/package.json +20 -0
- package/packages/worktree/scripts/install.mjs +26 -0
- package/packages/worktree/tests/install.test.mjs +25 -0
- package/packages/worktree/tests/run-tests.sh +46 -0
- package/scripts/install-agents.mjs +8 -18
- package/extensions/codex-edit/README.md +0 -53
- /package/extensions/{btw-scroll.ts → btw/scroll.ts} +0 -0
- /package/{extensions/cache-export → packages/cache-export/src}/tau-assets.ts +0 -0
- /package/{extensions → packages}/codex-edit/config.json +0 -0
- /package/{extensions/codex-edit → packages/codex-edit/src}/model-routing.ts +0 -0
- /package/{extensions → packages/todo/src}/todo-state.ts +0 -0
- /package/{bin → packages/worktree/bin}/pi-worktree +0 -0
- /package/{bin → packages/worktree/bin}/pi-worktree-completion.bash +0 -0
- /package/{skills → packages/worktree/skills}/pi-worktree/SKILL.md +0 -0
|
@@ -135,20 +135,20 @@ footer { max-width: 1180px; margin: 0 auto; padding: 0 26px 54px; color: var(--m
|
|
|
135
135
|
<div class="topbar-inner">
|
|
136
136
|
<div class="brand">Pi <span>apply_patch</span> 方案说明</div>
|
|
137
137
|
<nav class="nav" aria-label="页面导航">
|
|
138
|
-
<a href="#answer">结论</a><a href="#architecture">架构</a><a href="#config">配置</a><a href="#principle">原理</a><a href="#evidence">数据</a>
|
|
138
|
+
<a href="#answer">结论</a><a href="#install">安装</a><a href="#architecture">架构</a><a href="#config">配置</a><a href="#principle">原理</a><a href="#evidence">数据</a>
|
|
139
139
|
</nav>
|
|
140
140
|
</div>
|
|
141
141
|
</header>
|
|
142
142
|
<main>
|
|
143
143
|
<section class="hero-grid" id="answer">
|
|
144
144
|
<div>
|
|
145
|
-
<div class="eyebrow">GPT-5.6 / Pi / Codex
|
|
145
|
+
<div class="eyebrow">GPT-5.6 / Pi / Codex harness apply_patch</div>
|
|
146
146
|
<h1>它变好了,但改善主要在“第一次编辑能不能正确开始”。</h1>
|
|
147
|
-
<p class="lede"
|
|
147
|
+
<p class="lede">这个扩展让选中的 GPT 模型在 Pi 中使用与 Codex harness 相同的 apply_patch 协议;其他模型仍使用 Pi 原生 edit。公平对照下,GPT-5.6 的首次编辑成功率从 85% 提到 95%,并消除了观察到的编辑协议失败。</p>
|
|
148
148
|
</div>
|
|
149
149
|
<aside class="signal">
|
|
150
|
-
<strong
|
|
151
|
-
<p
|
|
150
|
+
<strong>当前状态:根包内置,也可单独安装</strong>
|
|
151
|
+
<p><code>@maxiaochao/pi-toolkit</code> 默认包含本扩展;只需要 apply_patch 时可安装 <code>@maxiaochao/pi-codex-edit</code>。两种方式使用同一份实现。</p>
|
|
152
152
|
</aside>
|
|
153
153
|
</section>
|
|
154
154
|
|
|
@@ -161,6 +161,26 @@ footer { max-width: 1180px; margin: 0 auto; padding: 0 26px 54px; color: var(--m
|
|
|
161
161
|
</div>
|
|
162
162
|
</section>
|
|
163
163
|
|
|
164
|
+
<section class="section" id="install">
|
|
165
|
+
<div class="section-head"><div><div class="eyebrow">Distribution</div><h2>完整工具包与独立子包二选一</h2></div><p class="section-note">同时安装没有额外收益,还可能重复注册 <code>apply_patch</code>。</p></div>
|
|
166
|
+
<div class="explain">
|
|
167
|
+
<article class="panel">
|
|
168
|
+
<span class="badge green">完整工具包</span>
|
|
169
|
+
<h3 style="margin-top:16px">随 pi-toolkit 默认加载</h3>
|
|
170
|
+
<div class="code-wrap"><button class="copy" data-copy="toolkit-install">复制</button><pre id="toolkit-install">pi install npm:@maxiaochao/pi-toolkit
|
|
171
|
+
pi update npm:@maxiaochao/pi-toolkit</pre></div>
|
|
172
|
+
<p>根 manifest 直接加载 <code>packages/codex-edit/extensions/codex-edit.ts</code>。只有发布了新的 toolkit 版本,根包用户才会收到 Codex Edit 更新。</p>
|
|
173
|
+
</article>
|
|
174
|
+
<article class="panel">
|
|
175
|
+
<span class="badge amber">独立子包</span>
|
|
176
|
+
<h3 style="margin-top:16px">只安装 Codex Edit</h3>
|
|
177
|
+
<div class="code-wrap"><button class="copy" data-copy="child-install">复制</button><pre id="child-install">pi install npm:@maxiaochao/pi-codex-edit
|
|
178
|
+
pi update npm:@maxiaochao/pi-codex-edit</pre></div>
|
|
179
|
+
<p>子包拥有独立版本和发布流程。只发布子包不会更新已安装的 toolkit;实现或配置变更需要分别发布两个 npm 包。</p>
|
|
180
|
+
</article>
|
|
181
|
+
</div>
|
|
182
|
+
</section>
|
|
183
|
+
|
|
164
184
|
<section class="section">
|
|
165
185
|
<div class="section-head"><div><div class="eyebrow">先解释指标</div><h2>“编辑协议失败:1 → 0”到底是什么?</h2></div><p class="section-note">这是本次 benchmark 自定义的分类名,不是 HTTP、Responses API 或 custom tool 协议错误。</p></div>
|
|
166
186
|
<div class="explain">
|
|
@@ -187,7 +207,7 @@ footer { max-width: 1180px; margin: 0 auto; padding: 0 26px 54px; color: var(--m
|
|
|
187
207
|
<div class="arch-row">
|
|
188
208
|
<div class="arch-node"><small>01 · Model state</small><strong>当前模型配置</strong><p>ctx.model.provider、api、id、compat 能力标记。</p></div>
|
|
189
209
|
<div class="arch-arrow">→</div>
|
|
190
|
-
<div class="arch-node"><small>02 · Routing</small><strong>syncEditorTools()</strong><p
|
|
210
|
+
<div class="arch-node"><small>02 · Routing</small><strong>syncEditorTools()</strong><p>ID glob 和 Responses API 均命中时启用 apply_patch;其他模型启用 Pi edit。</p></div>
|
|
191
211
|
<div class="arch-arrow">→</div>
|
|
192
212
|
<div class="arch-node"><small>03 · Provider</small><strong>Responses serializer</strong><p>Grammar 支持时序列化为 type=custom + Lark definition,否则回退为普通 function tool。</p></div>
|
|
193
213
|
<div class="arch-arrow">→</div>
|
|
@@ -210,51 +230,52 @@ footer { max-width: 1180px; margin: 0 auto; padding: 0 26px 54px; color: var(--m
|
|
|
210
230
|
</section>
|
|
211
231
|
|
|
212
232
|
<section class="section" id="config">
|
|
213
|
-
<div class="section-head"><div><div class="eyebrow">Configuration</div><h2
|
|
233
|
+
<div class="section-head"><div><div class="eyebrow">Configuration</div><h2>当前模型路由</h2></div><p class="section-note">默认覆盖 Responses API 上的全部 <code>gpt-5.6-*</code> 变体;provider 不参与匹配。</p></div>
|
|
214
234
|
<div class="tabs" role="tablist" aria-label="配置视图">
|
|
215
235
|
<button class="tab" role="tab" aria-selected="true" aria-controls="config-current" id="tab-current">当前实际配置</button>
|
|
216
|
-
<button class="tab" role="tab" aria-selected="false" aria-controls="config-recommended" id="tab-recommended"
|
|
236
|
+
<button class="tab" role="tab" aria-selected="false" aria-controls="config-recommended" id="tab-recommended">添加模型</button>
|
|
217
237
|
<button class="tab" role="tab" aria-selected="false" aria-controls="config-route" id="tab-route">路由规则</button>
|
|
218
238
|
</div>
|
|
219
239
|
<div class="tab-panel active" id="config-current" role="tabpanel" aria-labelledby="tab-current">
|
|
220
240
|
<div class="code-wrap"><button class="copy" data-copy="current-config">复制</button><pre id="current-config">{
|
|
221
241
|
"enabled": true,
|
|
222
242
|
"models": [
|
|
223
|
-
|
|
243
|
+
"gpt-5.6-*"
|
|
224
244
|
]
|
|
225
245
|
}</pre></div>
|
|
226
|
-
<div class="callout">
|
|
246
|
+
<div class="callout">路由还要求 <code>model.api</code> 为 <code>openai-responses</code> 或 <code>openai-codex-responses</code>。grammar 不可用时 Pi 回退为普通 function tool。</div>
|
|
227
247
|
</div>
|
|
228
248
|
<div class="tab-panel" id="config-recommended" role="tabpanel" aria-labelledby="tab-recommended">
|
|
229
249
|
<div class="code-wrap"><button class="copy" data-copy="recommended-config">复制</button><pre id="recommended-config">{
|
|
230
250
|
"enabled": true,
|
|
231
251
|
"models": [
|
|
232
|
-
|
|
233
|
-
|
|
252
|
+
"gpt-5.6-*",
|
|
253
|
+
"gpt-5.7-*",
|
|
254
|
+
"my-codex-model"
|
|
234
255
|
]
|
|
235
256
|
}</pre></div>
|
|
236
|
-
<p>
|
|
257
|
+
<p>每项按完整 <code>model.id</code> 做区分大小写的匹配,<code>*</code> 是通配符。精确 ID 也可以直接作为规则。添加规则只改变路由,不代表模型已经通过协议验证;未命中规则的模型继续使用 Pi 原生 <code>edit</code>。</p>
|
|
237
258
|
</div>
|
|
238
259
|
<div class="tab-panel" id="config-route" role="tabpanel" aria-labelledby="tab-route">
|
|
239
260
|
<div class="code-wrap"><button class="copy" data-copy="route-rule">复制</button><pre id="route-rule">model = ctx.model
|
|
240
261
|
|
|
241
262
|
if enabled
|
|
242
263
|
and model.api in ["openai-responses", "openai-codex-responses"]
|
|
243
|
-
and
|
|
264
|
+
and model.id matches any models glob:
|
|
244
265
|
active = ["apply_patch"]
|
|
245
266
|
else:
|
|
246
267
|
active = ["edit"]
|
|
247
268
|
|
|
248
269
|
# Preserve all unrelated active tools.
|
|
249
270
|
# Register both tools permanently for session replay.</pre></div>
|
|
250
|
-
<p
|
|
271
|
+
<p><code>provider</code> 不参与匹配;即使 ID 命中,其他 API 上的模型仍使用 Pi <code>edit</code>。切换发生在 <code>session_start</code> 和 <code>model_select</code>。独立包修改 <code>config.json</code>,完整 toolkit 修改 <code>packages/codex-edit/config.json</code>,但包更新可能覆盖本地配置。</p>
|
|
251
272
|
</div>
|
|
252
273
|
</section>
|
|
253
274
|
|
|
254
275
|
<section class="section" id="principle">
|
|
255
276
|
<div class="section-head"><div><div class="eyebrow">Design principles</div><h2>这个工具的三个核心原则</h2></div></div>
|
|
256
277
|
<div class="principles">
|
|
257
|
-
<article class="principle"><h3>模型协议对齐</h3><p
|
|
278
|
+
<article class="principle"><h3>模型协议对齐</h3><p>选中的 GPT 模型不再被要求适应 Pi 自定义的 <code>oldText/newText</code> JSON;它看到与 Codex harness 相同的 <code>apply_patch</code> 名称和 patch grammar。</p></article>
|
|
258
279
|
<article class="principle"><h3>执行端独立防护</h3><p>grammar 不是安全策略。执行端仍要做 workspace 边界、符号链接、目录、覆盖冲突、权限和取消检查。</p></article>
|
|
259
280
|
<article class="principle"><h3>按能力选择,不靠 AGENTS 对抗</h3><p>不在 AGENTS 中要求模型“不要使用 apply_patch”。扩展只让模型看到适合自己的一个编辑工具,避免两套协议同时暴露。</p></article>
|
|
260
281
|
</div>
|
|
@@ -276,7 +297,7 @@ else:
|
|
|
276
297
|
</tbody>
|
|
277
298
|
</table>
|
|
278
299
|
<div class="panel" style="margin-top:18px">
|
|
279
|
-
<div class="status"><i></i
|
|
300
|
+
<div class="status"><i></i>最终判断:已投入使用,但只对 allowlist 中的 GPT/Codex 模型路由 apply_patch</div>
|
|
280
301
|
<p>本页的耗时比较优先使用同一 fixture 的配对差值,避免两组成功样本构成不同造成中位数偏差。runner 的 180s 进程上限是删失边界,不是 API timeout;本次有效 Luna 重测没有触发该上限。</p>
|
|
281
302
|
</div>
|
|
282
303
|
</section>
|
|
@@ -284,9 +305,9 @@ else:
|
|
|
284
305
|
<section class="section">
|
|
285
306
|
<div class="section-head"><div><div class="eyebrow">Files</div><h2>实现位置</h2></div></div>
|
|
286
307
|
<div class="panel">
|
|
287
|
-
<p><code>extensions/codex-edit
|
|
288
|
-
<p><code>
|
|
289
|
-
<p><code>
|
|
308
|
+
<p><code>packages/codex-edit/extensions/codex-edit.ts</code>:工具注册、执行、防护、renderer 和模型路由。</p>
|
|
309
|
+
<p><code>packages/codex-edit/src/parser.ts</code>:patch grammar 对应的 parser、hunk matcher 和 workspace path resolver。</p>
|
|
310
|
+
<p><code>packages/codex-edit/tests/parser.test.mjs</code>:12 个 parser、matcher、路径与结构重构测试,当前全部通过。</p>
|
|
290
311
|
<p><code>/home/maxiaochao/.pi/agent/evaluations/gpt-edit-benchmark/report-final.md</code>:最终 benchmark 报告。</p>
|
|
291
312
|
</div>
|
|
292
313
|
</section>
|
|
Binary file
|
|
Binary file
|
|
@@ -13,11 +13,11 @@ import {
|
|
|
13
13
|
applyUpdate,
|
|
14
14
|
parsePatch,
|
|
15
15
|
resolveWorkspacePath,
|
|
16
|
-
} from "
|
|
17
|
-
import { modelIsAllowed } from "
|
|
16
|
+
} from "../src/parser.ts";
|
|
17
|
+
import { modelIsAllowed } from "../src/model-routing.ts";
|
|
18
18
|
|
|
19
19
|
const editorToolNames = new Set(["edit", "apply_patch"]);
|
|
20
|
-
const settingsUrl = new URL("
|
|
20
|
+
const settingsUrl = new URL("../config.json", import.meta.url);
|
|
21
21
|
|
|
22
22
|
async function loadSettings() {
|
|
23
23
|
try {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@maxiaochao/pi-codex-edit",
|
|
3
|
+
"version": "0.1.7",
|
|
4
|
+
"description": "Pi extension that exposes Codex-style apply_patch for GPT/Codex models with model-aware edit fallback.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi-extension",
|
|
9
|
+
"codex",
|
|
10
|
+
"gpt-5",
|
|
11
|
+
"apply-patch"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
"extensions",
|
|
15
|
+
"src",
|
|
16
|
+
"docs",
|
|
17
|
+
"config.json",
|
|
18
|
+
"summary.md",
|
|
19
|
+
"codex-edit-explainer.html",
|
|
20
|
+
"README.md",
|
|
21
|
+
"TESTING.md",
|
|
22
|
+
"CHANGELOG.md",
|
|
23
|
+
"package.json"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/chaochaoxiaochao/pi-toolkit.git",
|
|
28
|
+
"directory": "packages/codex-edit"
|
|
29
|
+
},
|
|
30
|
+
"pi": {
|
|
31
|
+
"extensions": [
|
|
32
|
+
"./extensions/codex-edit.ts"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@earendil-works/pi-ai": "*",
|
|
37
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
38
|
+
"@earendil-works/pi-tui": "*",
|
|
39
|
+
"typebox": "*"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"test": "node --experimental-strip-types --test tests/*.test.mjs",
|
|
43
|
+
"load-test": "pi -ne -e ./extensions/codex-edit.ts"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -115,8 +115,7 @@ function locate(lines, pattern, start, eof, label) {
|
|
|
115
115
|
];
|
|
116
116
|
for (const compare of comparisons) {
|
|
117
117
|
const candidates = findCandidates(lines, pattern, start, eof, compare);
|
|
118
|
-
if (candidates.length
|
|
119
|
-
if (candidates.length > 1) throw new Error(`Ambiguous ${label || "patch"} match: ${candidates.length} regions found`);
|
|
118
|
+
if (candidates.length > 0) return candidates[0];
|
|
120
119
|
}
|
|
121
120
|
throw new Error(`Failed to find expected lines${label ? ` in ${label}` : ""}: ${pattern.join("\\n")}`);
|
|
122
121
|
}
|
|
@@ -51,6 +51,6 @@ The probe conditions above reflect v0.1.5, when the route still required grammar
|
|
|
51
51
|
|
|
52
52
|
## Decision
|
|
53
53
|
|
|
54
|
-
No-go for universal
|
|
54
|
+
No-go for universal routing in the original benchmark environment. The evidence did not justify routing every model through `apply_patch` or making it the default editor for the whole Pi workflow. Ship the extension both with `@maxiaochao/pi-toolkit` and as the standalone `@maxiaochao/pi-codex-edit` child package, while bounding activation with a model-ID allowlist (`gpt-5.6-*` plus an `openai-responses`/`openai-codex-responses` API check) and keeping `edit` as the fallback everywhere else.
|
|
55
55
|
|
|
56
56
|
The production route was validated with `pudu-openai-proxy/gpt-5.6-sol:high` and `pudu-openai-proxy/gpt-5.6-luna:high`. Sol's 20-case fair run improved first editor-call success from 85% to 95% with equal final correctness. A corrected Luna rerun used the fully qualified `pudu-openai-proxy/gpt-5.6-luna:high` in both arms, with 20/20 normally completed runs in each arm: both reached 18/20 final correctness, while first editor-call success was 18/20 for `edit` and 20/20 for `apply_patch`. Among the 17 cases correct in both arms, the paired median total-duration delta was +2.3s for `apply_patch`, with 8 cases faster and 9 slower; across all 20 paired runs, the median delta was +1.2s, with 9 cases faster and 11 slower. The measured tradeoff justified validating Sol first; the shipped `gpt-5.6-*` glob additionally enables Luna and Terra, whose gap is protocol adaptation rather than a stable duration win.
|
|
@@ -2,21 +2,23 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import test from "node:test";
|
|
5
|
-
import { modelIsAllowed } from "../model-routing.ts";
|
|
5
|
+
import { modelIsAllowed } from "../src/model-routing.ts";
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const packageDir = dirname(dirname(new URL(import.meta.url).pathname));
|
|
8
8
|
|
|
9
|
-
test("
|
|
10
|
-
const
|
|
11
|
-
assert.
|
|
9
|
+
test("package points Pi at the extension and config is reachable from it", async () => {
|
|
10
|
+
const manifest = JSON.parse(await readFile(join(packageDir, "package.json"), "utf8"));
|
|
11
|
+
assert.deepEqual(manifest.pi.extensions, ["./extensions/codex-edit.ts"]);
|
|
12
|
+
const source = await readFile(join(packageDir, "extensions/codex-edit.ts"), "utf8");
|
|
13
|
+
assert.match(source, /new URL\("\.\.\/config\.json", import\.meta\.url\)/);
|
|
12
14
|
assert.match(source, /model-routing\.ts/);
|
|
13
|
-
const config = JSON.parse(await readFile(join(
|
|
15
|
+
const config = JSON.parse(await readFile(join(packageDir, "config.json"), "utf8"));
|
|
14
16
|
assert.equal(config.enabled, true);
|
|
15
17
|
assert.deepEqual(config.models, ["gpt-5.6-*"]);
|
|
16
18
|
});
|
|
17
19
|
|
|
18
20
|
test("matches allowed model IDs with globs independently of provider and grammar support", () => {
|
|
19
|
-
const settings = { enabled: true, models: ["gpt-5.6-*"] };
|
|
21
|
+
const settings = { enabled: true, models: ["gpt-5.6-*", "my-codex-model"] };
|
|
20
22
|
const baseModel = {
|
|
21
23
|
id: "gpt-5.6-terra",
|
|
22
24
|
api: "openai-codex-responses",
|
|
@@ -34,5 +36,8 @@ test("matches allowed model IDs with globs independently of provider and grammar
|
|
|
34
36
|
);
|
|
35
37
|
assert.equal(modelIsAllowed({ ...baseModel, api: "openai-completions" }, settings), false);
|
|
36
38
|
assert.equal(modelIsAllowed({ ...baseModel, id: "gpt-5.5" }, settings), false);
|
|
39
|
+
assert.equal(modelIsAllowed({ ...baseModel, id: "my-codex-model" }, settings), true);
|
|
40
|
+
assert.equal(modelIsAllowed({ ...baseModel, id: "my-codex-model-v2" }, settings), false);
|
|
41
|
+
assert.equal(modelIsAllowed({ ...baseModel, id: "GPT-5.6-terra" }, settings), false);
|
|
37
42
|
assert.equal(modelIsAllowed(baseModel, { enabled: false, models: ["gpt-5.6-*"] }), false);
|
|
38
43
|
});
|
|
@@ -3,7 +3,7 @@ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import test from "node:test";
|
|
6
|
-
import { applyUpdate, parsePatch, resolveWorkspacePath } from "../parser.ts";
|
|
6
|
+
import { applyUpdate, parsePatch, resolveWorkspacePath } from "../src/parser.ts";
|
|
7
7
|
|
|
8
8
|
test("parses add, delete, update, and move operations", () => {
|
|
9
9
|
const operations = parsePatch(`*** Begin Patch
|
|
@@ -58,14 +58,111 @@ test("supports insertion chunks and EOF matching", () => {
|
|
|
58
58
|
assert.equal(applyUpdate("", operations[0].chunks, "file.txt"), "first\n");
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
test("
|
|
61
|
+
test("uses the first matching region at or after the cursor", () => {
|
|
62
62
|
const operations = parsePatch(`*** Begin Patch
|
|
63
63
|
*** Update File: file.txt
|
|
64
64
|
@@
|
|
65
65
|
-value
|
|
66
66
|
+changed
|
|
67
67
|
*** End Patch`);
|
|
68
|
-
assert.
|
|
68
|
+
assert.equal(applyUpdate("value\nvalue\n", operations[0].chunks, "file.txt"), "changed\nvalue\n");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("uses earlier hunks to advance the cursor through repeated HTML regions", () => {
|
|
72
|
+
const operations = parsePatch(`*** Begin Patch
|
|
73
|
+
*** Update File: page.html
|
|
74
|
+
@@
|
|
75
|
+
-old
|
|
76
|
+
+new
|
|
77
|
+
@@
|
|
78
|
+
</section>
|
|
79
|
+
+<section id="inserted"></section>
|
|
80
|
+
<section>
|
|
81
|
+
*** End Patch`);
|
|
82
|
+
const before = `<header>
|
|
83
|
+
old
|
|
84
|
+
</header>
|
|
85
|
+
<section>first</section>
|
|
86
|
+
</section>
|
|
87
|
+
<section>
|
|
88
|
+
one
|
|
89
|
+
</section>
|
|
90
|
+
<section>
|
|
91
|
+
two
|
|
92
|
+
</section>
|
|
93
|
+
`;
|
|
94
|
+
const after = `<header>
|
|
95
|
+
new
|
|
96
|
+
</header>
|
|
97
|
+
<section>first</section>
|
|
98
|
+
</section>
|
|
99
|
+
<section id="inserted"></section>
|
|
100
|
+
<section>
|
|
101
|
+
one
|
|
102
|
+
</section>
|
|
103
|
+
<section>
|
|
104
|
+
two
|
|
105
|
+
</section>
|
|
106
|
+
`;
|
|
107
|
+
assert.equal(applyUpdate(before, operations[0].chunks, "page.html"), after);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("applies a hunk at the first repeated Markdown separator after its cursor", () => {
|
|
111
|
+
const operations = parsePatch(`*** Begin Patch
|
|
112
|
+
*** Update File: SKILL.md
|
|
113
|
+
@@
|
|
114
|
+
-description: old
|
|
115
|
+
+description: new
|
|
116
|
+
@@
|
|
117
|
+
---
|
|
118
|
+
+Inserted after frontmatter.
|
|
119
|
+
*** End Patch`);
|
|
120
|
+
const before = `---
|
|
121
|
+
name: demo
|
|
122
|
+
description: old
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
# Demo
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
Footer
|
|
129
|
+
`;
|
|
130
|
+
const after = `---
|
|
131
|
+
name: demo
|
|
132
|
+
description: new
|
|
133
|
+
---
|
|
134
|
+
Inserted after frontmatter.
|
|
135
|
+
|
|
136
|
+
# Demo
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
Footer
|
|
140
|
+
`;
|
|
141
|
+
assert.equal(applyUpdate(before, operations[0].chunks, "SKILL.md"), after);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("parses multiple file operations used by structural refactors", () => {
|
|
145
|
+
const operations = parsePatch(`*** Begin Patch
|
|
146
|
+
*** Update File: extensions/tool.ts
|
|
147
|
+
*** Move to: packages/tool/extensions/tool.ts
|
|
148
|
+
@@
|
|
149
|
+
-export const value = 1;
|
|
150
|
+
+export const value = 2;
|
|
151
|
+
*** Add File: extensions/tool-wrapper.ts
|
|
152
|
+
+export { value } from "../packages/tool/extensions/tool.ts";
|
|
153
|
+
*** Delete File: extensions/tool.test.ts
|
|
154
|
+
*** End Patch`);
|
|
155
|
+
assert.deepEqual(
|
|
156
|
+
operations.map(({ action, path, moveTo }) => ({ action, path, moveTo })),
|
|
157
|
+
[
|
|
158
|
+
{ action: "update", path: "extensions/tool.ts", moveTo: "packages/tool/extensions/tool.ts" },
|
|
159
|
+
{ action: "add", path: "extensions/tool-wrapper.ts", moveTo: undefined },
|
|
160
|
+
{ action: "delete", path: "extensions/tool.test.ts", moveTo: undefined },
|
|
161
|
+
],
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("rejects workspace escapes", () => {
|
|
69
166
|
assert.equal(resolveWorkspacePath("/tmp/project", "/tmp/project/src/a.js"), "/tmp/project/src/a.js");
|
|
70
167
|
assert.throws(() => resolveWorkspacePath("/tmp/project", "/tmp/other/a.js"), /escapes/);
|
|
71
168
|
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
## 2026-09-30 - v0.1.1
|
|
2
|
+
|
|
3
|
+
- Add standalone testing documentation and real production-runner screenshot/demo media.
|
|
4
|
+
- Align the package manifest and root bundle with the shared workspace source.
|
|
5
|
+
|
|
6
|
+
## 2026-09-01 - v0.1.0
|
|
7
|
+
|
|
8
|
+
- Add the `tiny_subagents` Pi tool for one focused prompt in a fresh child Pi context.
|
|
9
|
+
- Add package-local `worker.md` personas with model and built-in tool selection.
|
|
10
|
+
- Return structured process, Pi protocol, stderr, stop-reason, exit, signal, and cancellation diagnostics.
|
|
11
|
+
- Preserve oversized stdout, stderr, and final assistant output in private temporary artifacts.
|
|
12
|
+
- Stream child event activity to the TUI and expose persona, prompt, and transcript details when expanded.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# @maxiaochao/pi-tiny-subagent
|
|
2
|
+
|
|
3
|
+
A small Pi extension that runs one fresh Pi child process for one focused prompt. It does not use the `pi-subagents` package and does not inherit the parent conversation or extensions.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Why this package exists
|
|
8
|
+
|
|
9
|
+
This is the toolkit's deliberately small subagent model: one persona, one focused prompt, one isolated child context. It avoids a general orchestration framework and keeps child transcript noise out of the parent context while preserving process and protocol diagnostics.
|
|
10
|
+
|
|
11
|
+
## Demo
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
tiny_subagents({ action: "list" })
|
|
17
|
+
tiny_subagents({
|
|
18
|
+
agent: "worker",
|
|
19
|
+
prompt: "Inspect the authentication flow and summarize the risks"
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The child receives package-local persona instructions, selected built-in tools, and a fresh context; the parent receives the focused result or structured failure details.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pi install npm:@maxiaochao/pi-tiny-subagent
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The full `@maxiaochao/pi-toolkit` package also bundles this extension. Install the full toolkit or this standalone child package, not both.
|
|
32
|
+
|
|
33
|
+
Update the distribution you installed:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pi update npm:@maxiaochao/pi-toolkit # bundled copy
|
|
37
|
+
pi update npm:@maxiaochao/pi-tiny-subagent # standalone child
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The versions are independent. Publishing the child does not update an already published toolkit version.
|
|
41
|
+
|
|
42
|
+
The package registers one tool: `tiny_subagents`.
|
|
43
|
+
|
|
44
|
+
## Personas
|
|
45
|
+
|
|
46
|
+
The default `agents/worker.md` has this shape:
|
|
47
|
+
|
|
48
|
+
```md
|
|
49
|
+
---
|
|
50
|
+
name: worker
|
|
51
|
+
description: General-purpose worker for one focused task
|
|
52
|
+
tools: read, bash, edit, write, grep, find, ls
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
You are a focused worker subagent.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The supported frontmatter fields are:
|
|
59
|
+
|
|
60
|
+
- `name` (required): Persona name passed in the `agent` field.
|
|
61
|
+
- `description` (required): Short description returned by `action: "list"` so the parent model can choose a persona.
|
|
62
|
+
- `model` (optional): Default child model, such as `provider/model:high`. A call-level `model` overrides it; if both are absent, the child inherits the current parent-session model.
|
|
63
|
+
- `tools` (optional): Comma-separated allowlist of Pi built-in tools. Supported values are `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`, and `powershell` on Windows. Extension tools such as `contact_supervisor` are not allowed. If omitted, the child uses Pi's default built-in tools.
|
|
64
|
+
|
|
65
|
+
For example, a read-only reviewer persona can restrict its tools and select a default model:
|
|
66
|
+
|
|
67
|
+
```md
|
|
68
|
+
---
|
|
69
|
+
name: reviewer
|
|
70
|
+
description: Review code and report correctness risks without modifying files
|
|
71
|
+
model: provider/model:high
|
|
72
|
+
tools: read, grep, find, ls
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
Review the assigned code. Do not edit files.
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Persona files are loaded only from this package's `agents/*.md`; they do not share configuration with `pi-subagents`.
|
|
79
|
+
|
|
80
|
+
Before execution, the parent model should discover the package-local personas:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
tiny_subagents({ action: "list" })
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
It can then match the task to a persona by name or description and execute without `action`:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
tiny_subagents({
|
|
90
|
+
agent: "worker",
|
|
91
|
+
prompt: "Inspect the authentication flow and summarize the risks"
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Execution
|
|
96
|
+
|
|
97
|
+
The child is started in the current working directory with a fresh context:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
pi --mode json --print --no-session --no-extensions --no-skills
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The selected persona body is passed as an additional system prompt. Only built-in Pi tools can be enabled by `tools` in the persona file.
|
|
104
|
+
|
|
105
|
+
## Live output
|
|
106
|
+
|
|
107
|
+
`tiny_subagents` sends a TUI update when the child starts and after each child `message_end` or `tool_result_end` event. While the child is running, the collapsed row shows the latest activity.
|
|
108
|
+
|
|
109
|
+
Press Pi's `Ctrl+O` tool-output expansion key to inspect the current child execution. The expanded `tiny_subagents` view shows:
|
|
110
|
+
|
|
111
|
+
- selected persona name and description
|
|
112
|
+
- selected model
|
|
113
|
+
- the task prompt
|
|
114
|
+
- the persona system prompt
|
|
115
|
+
- child assistant tool calls, tool results, and assistant text
|
|
116
|
+
- the final output and errors
|
|
117
|
+
|
|
118
|
+
This uses Pi's standard tool-output expansion state, so `Ctrl+O` also changes the expanded/collapsed display state of other tool rows. The added content and event tracking are local to `tiny_subagents`; they do not change other tools or the parent model context.
|
|
119
|
+
|
|
120
|
+
## Errors and large output
|
|
121
|
+
|
|
122
|
+
Successful calls return the final assistant text. Failures return the Pi error message, stop reason, exit code, signal, stderr, protocol errors, and output produced before failure. Inline output is limited to 16 KiB. If stdout, stderr, or the final answer exceeds that size, the complete files are retained in a private temporary artifact directory and its path, byte counts, and `cleanup: "remove directory when no longer needed"` instruction are returned. Small-result temporary directories are removed automatically.
|
|
123
|
+
|
|
124
|
+
The child uses the same Pi installation, provider configuration, model catalog, and authentication environment as the parent process. The package only selects the model; it does not define providers or credentials.
|
|
125
|
+
|
|
126
|
+
## Development and tests
|
|
127
|
+
|
|
128
|
+
The authoritative implementation is under `extensions/` and `src/`. See [TESTING.md](TESTING.md) for the test strategy, fixture cases, and latest baseline.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Tiny Subagent test strategy
|
|
2
|
+
|
|
3
|
+
## Flow
|
|
4
|
+
|
|
5
|
+
1. `npm --prefix packages/tiny-subagent test` runs deterministic process and persona tests.
|
|
6
|
+
2. `pi -ne -ns --no-session -e ./packages/tiny-subagent/extensions/tiny-subagent.ts` verifies extension loading.
|
|
7
|
+
3. `npm pack ./packages/tiny-subagent --dry-run` verifies package contents.
|
|
8
|
+
4. Verify the README example and the production-runner media `docs/screenshot.png` and `docs/demo.gif` are present in the tarball.
|
|
9
|
+
|
|
10
|
+
## Cases
|
|
11
|
+
|
|
12
|
+
- Resolve package-local personas and validate frontmatter.
|
|
13
|
+
- Start an isolated child Pi process with selected model and tools.
|
|
14
|
+
- Stream child activity while keeping it outside the parent conversation.
|
|
15
|
+
- Preserve detailed process/protocol failures and oversized artifacts.
|
|
16
|
+
- Render collapsed and expanded tool output.
|
|
17
|
+
|
|
18
|
+
## Latest baseline
|
|
19
|
+
|
|
20
|
+
All deterministic child-process and persona cases pass. Release validation also requires the extension load check.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: worker
|
|
3
|
+
description: General-purpose worker for one focused task
|
|
4
|
+
tools: read, bash, edit, write, grep, find, ls
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a focused worker subagent.
|
|
8
|
+
|
|
9
|
+
Work only on the task in the user prompt. Inspect the current project before making claims. Use the available built-in tools when needed. Do not delegate to another agent. Finish with a concise result, including changes made, validation performed, and any remaining risks.
|
|
Binary file
|
|
Binary file
|