bermudis-pi-goodies 0.6.0 → 0.7.0
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/README.md +22 -39
- package/clean-tui.ts +334 -0
- package/index.ts +10 -5
- package/keep-model-on-new.ts +23 -6
- package/package.json +5 -5
- package/model-thinking.ts +0 -1156
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ extensions. One entry point, ten independent features.
|
|
|
11
11
|
| `zed` | `/z` | Open Zed editor on the current working directory. |
|
|
12
12
|
| `prefer-tools` | hook (no command) | Nudge toward modern CLIs: `rg` over `grep`, `fd` over `find`, `uv` over bare `python`/`pip`/`pytest`/`mypy`. |
|
|
13
13
|
| `keep-model-on-new` | hook (no command) | Keep the active model when `/new` starts a fresh session instead of reverting to pi's saved default model. |
|
|
14
|
-
| `
|
|
14
|
+
| `clean-tui` | tool overrides (no command) | Collapse built-in tool output for a cleaner TUI: keeps a one-line call header, hides results/diffs until you expand a row with ctrl+e or click. |
|
|
15
15
|
| `kilo` | provider | Access Kilo Gateway models via `/login kilo` or `KILO_API_KEY`. |
|
|
16
16
|
| `provider-balance` | footer (no command) | Show remaining Kilo or OpenRouter credits, z.ai token-plan quota, or OpenAI Codex quota on the right side of the working-directory footer line. |
|
|
17
17
|
| `tps` | hook (no command) | Notify tokens/sec and in/out/cache token usage at the end of each agent turn. |
|
|
@@ -21,50 +21,33 @@ extensions. One entry point, ten independent features.
|
|
|
21
21
|
After publishing the package to npm:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
pi install npm:bermudis-pi-goodies@0.
|
|
24
|
+
pi install npm:bermudis-pi-goodies@0.7.0
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Remove any old `bermudis-pi-goodies.ts` symlink before reloading Pi. Each
|
|
28
28
|
feature is independent — disabling one is a one-line edit in `index.ts`.
|
|
29
29
|
Kilo's provider and its balance footer are bundled here.
|
|
30
30
|
|
|
31
|
-
##
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Hooks apply the stored level whenever a model becomes active: full-picker
|
|
53
|
-
selection, Ctrl+P cycling, startup, and `/new` (where this package carries
|
|
54
|
-
the previous session's active model forward). A native scoped level for the session — via
|
|
55
|
-
`--models "x:high"` or a hand-suffixed enabledModels entry — always wins;
|
|
56
|
-
the sidecar only fills in where pi itself has no level. Explicit
|
|
57
|
-
`--thinking` or `--model ...:<level>` still
|
|
58
|
-
wins for that launched session — a model whose registered id genuinely ends
|
|
59
|
-
in `:<level>` is indistinguishable from that shorthand without the registry,
|
|
60
|
-
so it opts out of its stored level for that startup; switch or cycle to
|
|
61
|
-
re-apply. Resumed and forked sessions keep the level restored by pi.
|
|
62
|
-
|
|
63
|
-
One gap is inherent to Pi: it emits no `model_select` when you pick the
|
|
64
|
-
model that is already active, so re-selecting it in the full picker leaves
|
|
65
|
-
a manual thinking level in place — switch models (or cycle) to snap back.
|
|
66
|
-
Saving the `/scoped-models` screen in pi 0.84.2 writes bare model ids, which
|
|
67
|
-
is now harmless: bare ids are exactly what this design expects.
|
|
31
|
+
## Per-model thinking levels (retired in favor of Pi 0.84.3)
|
|
32
|
+
|
|
33
|
+
This package previously shipped a `model-thinking` module with a `/levels`
|
|
34
|
+
command and an extension-owned sidecar at
|
|
35
|
+
`~/.pi/agent/data/bermudis-pi-goodies/thinking-levels.json`. It existed
|
|
36
|
+
because Pi's `/scoped-models` screen rewrote `enabledModels` with bare model
|
|
37
|
+
ids (wiping any `:level` suffix) and because every `setThinkingLevel()` call
|
|
38
|
+
persisted as the global default.
|
|
39
|
+
|
|
40
|
+
Pi [0.84.3](https://github.com/earendil-works/pi/releases/tag/v0.84.3) fixed
|
|
41
|
+
both: in-session model and thinking changes are now ephemeral by default
|
|
42
|
+
(persist only via `/settings` or Ctrl+S, [#5263](https://github.com/earendil-works/pi/issues/5263)),
|
|
43
|
+
and Pi added a native per-model thinking-level override keyed by
|
|
44
|
+
`provider/modelId`, stored in `settings.json` `modelThinkingLevels` and
|
|
45
|
+
edited via `/settings` → "Default thinking level per model". That is exactly
|
|
46
|
+
what `model-thinking` provided, so the module is retired.
|
|
47
|
+
|
|
48
|
+
If you had levels in the old sidecar
|
|
49
|
+
(`~/.pi/agent/data/bermudis-pi-goodies/thinking-levels.json`), re-enter them
|
|
50
|
+
via `/settings` → "Default thinking level per model".
|
|
68
51
|
|
|
69
52
|
## Provider and balance details
|
|
70
53
|
|
package/clean-tui.ts
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collapse built-in tool output for a cleaner TUI focused on agent prose.
|
|
3
|
+
*
|
|
4
|
+
* Re-registers pi's built-in tools (read, bash, edit, write, find, grep, ls)
|
|
5
|
+
* with the same names, delegating `execute()` to the original factory
|
|
6
|
+
* implementations so behavior is unchanged. The only override is rendering:
|
|
7
|
+
* each tool keeps a terse one-line call header (command, path, or pattern)
|
|
8
|
+
* but produces no visible output when collapsed. Expand a row with ctrl+e or
|
|
9
|
+
* click to see the full result/diff.
|
|
10
|
+
*
|
|
11
|
+
* This is the display target the user chose: hide output, keep the call line.
|
|
12
|
+
* No global fold toggle — folding stays per-row via pi's native expand.
|
|
13
|
+
*/
|
|
14
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import {
|
|
16
|
+
createBashTool,
|
|
17
|
+
createEditTool,
|
|
18
|
+
createFindTool,
|
|
19
|
+
createGrepTool,
|
|
20
|
+
createLsTool,
|
|
21
|
+
createReadTool,
|
|
22
|
+
createWriteTool,
|
|
23
|
+
} from "@earendil-works/pi-coding-agent";
|
|
24
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
25
|
+
import { homedir } from "node:os";
|
|
26
|
+
|
|
27
|
+
type BuiltInTools = {
|
|
28
|
+
read: ReturnType<typeof createReadTool>;
|
|
29
|
+
bash: ReturnType<typeof createBashTool>;
|
|
30
|
+
edit: ReturnType<typeof createEditTool>;
|
|
31
|
+
write: ReturnType<typeof createWriteTool>;
|
|
32
|
+
find: ReturnType<typeof createFindTool>;
|
|
33
|
+
grep: ReturnType<typeof createGrepTool>;
|
|
34
|
+
ls: ReturnType<typeof createLsTool>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const toolCache = new Map<string, BuiltInTools>();
|
|
38
|
+
|
|
39
|
+
function createBuiltInTools(cwd: string): BuiltInTools {
|
|
40
|
+
return {
|
|
41
|
+
read: createReadTool(cwd),
|
|
42
|
+
bash: createBashTool(cwd),
|
|
43
|
+
edit: createEditTool(cwd),
|
|
44
|
+
write: createWriteTool(cwd),
|
|
45
|
+
find: createFindTool(cwd),
|
|
46
|
+
grep: createGrepTool(cwd),
|
|
47
|
+
ls: createLsTool(cwd),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getBuiltInTools(cwd: string): BuiltInTools {
|
|
52
|
+
let tools = toolCache.get(cwd);
|
|
53
|
+
if (!tools) {
|
|
54
|
+
tools = createBuiltInTools(cwd);
|
|
55
|
+
toolCache.set(cwd, tools);
|
|
56
|
+
}
|
|
57
|
+
return tools;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function shortenPath(path: string): string {
|
|
61
|
+
const home = homedir();
|
|
62
|
+
if (path.startsWith(home)) return `~${path.slice(home.length)}`;
|
|
63
|
+
return path;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Extract the text content from a tool result, or undefined if none. */
|
|
67
|
+
function resultText(result: {
|
|
68
|
+
content: Array<{ type: string; text?: string }>;
|
|
69
|
+
}): string | undefined {
|
|
70
|
+
const textContent = result.content.find((c) => c.type === "text");
|
|
71
|
+
return textContent?.type === "text" ? textContent.text : undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default function cleanTui(pi: ExtensionAPI): void {
|
|
75
|
+
// parameters is a static TypeBox schema; any cwd instance yields the same
|
|
76
|
+
// schema. execute() uses ctx.cwd for the real working directory.
|
|
77
|
+
const schemaTools = getBuiltInTools(process.cwd());
|
|
78
|
+
|
|
79
|
+
pi.registerTool({
|
|
80
|
+
name: "read",
|
|
81
|
+
label: "read",
|
|
82
|
+
description: schemaTools.read.description,
|
|
83
|
+
parameters: schemaTools.read.parameters,
|
|
84
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
85
|
+
return getBuiltInTools(ctx.cwd).read.execute(
|
|
86
|
+
toolCallId,
|
|
87
|
+
params,
|
|
88
|
+
signal,
|
|
89
|
+
onUpdate,
|
|
90
|
+
);
|
|
91
|
+
},
|
|
92
|
+
renderCall(args, theme) {
|
|
93
|
+
const path = shortenPath(args.path || "");
|
|
94
|
+
let display = path
|
|
95
|
+
? theme.fg("accent", path)
|
|
96
|
+
: theme.fg("toolOutput", "...");
|
|
97
|
+
if (args.offset !== undefined || args.limit !== undefined) {
|
|
98
|
+
const start = args.offset ?? 1;
|
|
99
|
+
const end = args.limit !== undefined ? start + args.limit - 1 : "";
|
|
100
|
+
display += theme.fg("warning", `:${start}${end ? `-${end}` : ""}`);
|
|
101
|
+
}
|
|
102
|
+
return new Text(
|
|
103
|
+
`${theme.fg("toolTitle", theme.bold("read"))} ${display}`,
|
|
104
|
+
0,
|
|
105
|
+
0,
|
|
106
|
+
);
|
|
107
|
+
},
|
|
108
|
+
renderResult(result, { expanded }, theme) {
|
|
109
|
+
if (!expanded) return new Text("", 0, 0);
|
|
110
|
+
const text = resultText(result);
|
|
111
|
+
if (!text) return new Text("", 0, 0);
|
|
112
|
+
return new Text(
|
|
113
|
+
`\n${text
|
|
114
|
+
.split("\n")
|
|
115
|
+
.map((l) => theme.fg("toolOutput", l))
|
|
116
|
+
.join("\n")}`,
|
|
117
|
+
0,
|
|
118
|
+
0,
|
|
119
|
+
);
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
pi.registerTool({
|
|
124
|
+
name: "bash",
|
|
125
|
+
label: "bash",
|
|
126
|
+
description: schemaTools.bash.description,
|
|
127
|
+
parameters: schemaTools.bash.parameters,
|
|
128
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
129
|
+
return getBuiltInTools(ctx.cwd).bash.execute(
|
|
130
|
+
toolCallId,
|
|
131
|
+
params,
|
|
132
|
+
signal,
|
|
133
|
+
onUpdate,
|
|
134
|
+
);
|
|
135
|
+
},
|
|
136
|
+
renderCall(args, theme) {
|
|
137
|
+
const command = args.command || "...";
|
|
138
|
+
const timeout = args.timeout as number | undefined;
|
|
139
|
+
const suffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
|
|
140
|
+
return new Text(
|
|
141
|
+
theme.fg("toolTitle", theme.bold(`$ ${command}`)) + suffix,
|
|
142
|
+
0,
|
|
143
|
+
0,
|
|
144
|
+
);
|
|
145
|
+
},
|
|
146
|
+
renderResult(result, { expanded }, theme) {
|
|
147
|
+
if (!expanded) return new Text("", 0, 0);
|
|
148
|
+
const text = resultText(result)?.trim();
|
|
149
|
+
if (!text) return new Text("", 0, 0);
|
|
150
|
+
return new Text(
|
|
151
|
+
`\n${text
|
|
152
|
+
.split("\n")
|
|
153
|
+
.map((l) => theme.fg("toolOutput", l))
|
|
154
|
+
.join("\n")}`,
|
|
155
|
+
0,
|
|
156
|
+
0,
|
|
157
|
+
);
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
pi.registerTool({
|
|
162
|
+
name: "write",
|
|
163
|
+
label: "write",
|
|
164
|
+
description: schemaTools.write.description,
|
|
165
|
+
parameters: schemaTools.write.parameters,
|
|
166
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
167
|
+
return getBuiltInTools(ctx.cwd).write.execute(
|
|
168
|
+
toolCallId,
|
|
169
|
+
params,
|
|
170
|
+
signal,
|
|
171
|
+
onUpdate,
|
|
172
|
+
);
|
|
173
|
+
},
|
|
174
|
+
renderCall(args, theme) {
|
|
175
|
+
const path = shortenPath(args.path || "");
|
|
176
|
+
const display = path
|
|
177
|
+
? theme.fg("accent", path)
|
|
178
|
+
: theme.fg("toolOutput", "...");
|
|
179
|
+
const lines = args.content ? args.content.split("\n").length : 0;
|
|
180
|
+
const info = lines > 0 ? theme.fg("muted", ` (${lines} lines)`) : "";
|
|
181
|
+
return new Text(
|
|
182
|
+
`${theme.fg("toolTitle", theme.bold("write"))} ${display}${info}`,
|
|
183
|
+
0,
|
|
184
|
+
0,
|
|
185
|
+
);
|
|
186
|
+
},
|
|
187
|
+
renderResult(result, { expanded }, theme) {
|
|
188
|
+
if (!expanded) return new Text("", 0, 0);
|
|
189
|
+
const text = resultText(result);
|
|
190
|
+
if (text) return new Text(`\n${theme.fg("error", text)}`, 0, 0);
|
|
191
|
+
return new Text("", 0, 0);
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
pi.registerTool({
|
|
196
|
+
name: "edit",
|
|
197
|
+
label: "edit",
|
|
198
|
+
description: schemaTools.edit.description,
|
|
199
|
+
parameters: schemaTools.edit.parameters,
|
|
200
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
201
|
+
return getBuiltInTools(ctx.cwd).edit.execute(
|
|
202
|
+
toolCallId,
|
|
203
|
+
params,
|
|
204
|
+
signal,
|
|
205
|
+
onUpdate,
|
|
206
|
+
);
|
|
207
|
+
},
|
|
208
|
+
renderCall(args, theme) {
|
|
209
|
+
const path = shortenPath(args.path || "");
|
|
210
|
+
const display = path
|
|
211
|
+
? theme.fg("accent", path)
|
|
212
|
+
: theme.fg("toolOutput", "...");
|
|
213
|
+
return new Text(
|
|
214
|
+
`${theme.fg("toolTitle", theme.bold("edit"))} ${display}`,
|
|
215
|
+
0,
|
|
216
|
+
0,
|
|
217
|
+
);
|
|
218
|
+
},
|
|
219
|
+
renderResult(result, { expanded }, theme) {
|
|
220
|
+
if (!expanded) return new Text("", 0, 0);
|
|
221
|
+
const text = resultText(result);
|
|
222
|
+
if (!text) return new Text("", 0, 0);
|
|
223
|
+
return new Text(`\n${theme.fg("toolOutput", text)}`, 0, 0);
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
pi.registerTool({
|
|
228
|
+
name: "find",
|
|
229
|
+
label: "find",
|
|
230
|
+
description: schemaTools.find.description,
|
|
231
|
+
parameters: schemaTools.find.parameters,
|
|
232
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
233
|
+
return getBuiltInTools(ctx.cwd).find.execute(
|
|
234
|
+
toolCallId,
|
|
235
|
+
params,
|
|
236
|
+
signal,
|
|
237
|
+
onUpdate,
|
|
238
|
+
);
|
|
239
|
+
},
|
|
240
|
+
renderCall(args, theme) {
|
|
241
|
+
const pattern = args.pattern || "";
|
|
242
|
+
const path = shortenPath(args.path || ".");
|
|
243
|
+
let text = `${theme.fg("toolTitle", theme.bold("find"))} ${theme.fg("accent", pattern)}`;
|
|
244
|
+
text += theme.fg("toolOutput", ` in ${path}`);
|
|
245
|
+
return new Text(text, 0, 0);
|
|
246
|
+
},
|
|
247
|
+
renderResult(result, { expanded }, theme) {
|
|
248
|
+
if (!expanded) return new Text("", 0, 0);
|
|
249
|
+
const text = resultText(result)?.trim();
|
|
250
|
+
if (!text) return new Text("", 0, 0);
|
|
251
|
+
return new Text(
|
|
252
|
+
`\n${text
|
|
253
|
+
.split("\n")
|
|
254
|
+
.map((l) => theme.fg("toolOutput", l))
|
|
255
|
+
.join("\n")}`,
|
|
256
|
+
0,
|
|
257
|
+
0,
|
|
258
|
+
);
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
pi.registerTool({
|
|
263
|
+
name: "grep",
|
|
264
|
+
label: "grep",
|
|
265
|
+
description: schemaTools.grep.description,
|
|
266
|
+
parameters: schemaTools.grep.parameters,
|
|
267
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
268
|
+
return getBuiltInTools(ctx.cwd).grep.execute(
|
|
269
|
+
toolCallId,
|
|
270
|
+
params,
|
|
271
|
+
signal,
|
|
272
|
+
onUpdate,
|
|
273
|
+
);
|
|
274
|
+
},
|
|
275
|
+
renderCall(args, theme) {
|
|
276
|
+
const pattern = args.pattern || "";
|
|
277
|
+
const path = shortenPath(args.path || ".");
|
|
278
|
+
const glob = args.glob;
|
|
279
|
+
let text = `${theme.fg("toolTitle", theme.bold("grep"))} ${theme.fg("accent", `/${pattern}/`)}`;
|
|
280
|
+
text += theme.fg("toolOutput", ` in ${path}`);
|
|
281
|
+
if (glob) text += theme.fg("toolOutput", ` (${glob})`);
|
|
282
|
+
return new Text(text, 0, 0);
|
|
283
|
+
},
|
|
284
|
+
renderResult(result, { expanded }, theme) {
|
|
285
|
+
if (!expanded) return new Text("", 0, 0);
|
|
286
|
+
const text = resultText(result)?.trim();
|
|
287
|
+
if (!text) return new Text("", 0, 0);
|
|
288
|
+
return new Text(
|
|
289
|
+
`\n${text
|
|
290
|
+
.split("\n")
|
|
291
|
+
.map((l) => theme.fg("toolOutput", l))
|
|
292
|
+
.join("\n")}`,
|
|
293
|
+
0,
|
|
294
|
+
0,
|
|
295
|
+
);
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
pi.registerTool({
|
|
300
|
+
name: "ls",
|
|
301
|
+
label: "ls",
|
|
302
|
+
description: schemaTools.ls.description,
|
|
303
|
+
parameters: schemaTools.ls.parameters,
|
|
304
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
305
|
+
return getBuiltInTools(ctx.cwd).ls.execute(
|
|
306
|
+
toolCallId,
|
|
307
|
+
params,
|
|
308
|
+
signal,
|
|
309
|
+
onUpdate,
|
|
310
|
+
);
|
|
311
|
+
},
|
|
312
|
+
renderCall(args, theme) {
|
|
313
|
+
const path = shortenPath(args.path || ".");
|
|
314
|
+
return new Text(
|
|
315
|
+
`${theme.fg("toolTitle", theme.bold("ls"))} ${theme.fg("accent", path)}`,
|
|
316
|
+
0,
|
|
317
|
+
0,
|
|
318
|
+
);
|
|
319
|
+
},
|
|
320
|
+
renderResult(result, { expanded }, theme) {
|
|
321
|
+
if (!expanded) return new Text("", 0, 0);
|
|
322
|
+
const text = resultText(result)?.trim();
|
|
323
|
+
if (!text) return new Text("", 0, 0);
|
|
324
|
+
return new Text(
|
|
325
|
+
`\n${text
|
|
326
|
+
.split("\n")
|
|
327
|
+
.map((l) => theme.fg("toolOutput", l))
|
|
328
|
+
.join("\n")}`,
|
|
329
|
+
0,
|
|
330
|
+
0,
|
|
331
|
+
);
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
}
|
package/index.ts
CHANGED
|
@@ -9,17 +9,24 @@
|
|
|
9
9
|
* - zed /z open Zed on cwd
|
|
10
10
|
* - prefer-tools hook block legacy tools (use trash/rg/fd/uv)
|
|
11
11
|
* - keep-model hook preserve the active model across /new
|
|
12
|
-
* -
|
|
12
|
+
* - clean-tui tool overrides collapse built-in tool output; keep a one-line call header, hide results/diffs until expanded
|
|
13
13
|
* - kilo provider access Kilo Gateway models
|
|
14
14
|
* - provider-balance footer show Kilo credits or Codex quota in the footer
|
|
15
15
|
* - tps hook notify tokens/sec and usage at each agent turn end
|
|
16
|
+
*
|
|
17
|
+
* Per-model thinking levels were previously provided here by a `model-thinking`
|
|
18
|
+
* module with a `/levels` command and an extension-owned sidecar. Pi 0.84.3
|
|
19
|
+
* added native per-model thinking-level overrides (settings.json
|
|
20
|
+
* `modelThinkingLevels`, edited via `/settings` → "Default thinking level per
|
|
21
|
+
* model") and made in-session model/thinking changes ephemeral by default, so
|
|
22
|
+
* that module is retired.
|
|
16
23
|
*/
|
|
17
24
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
18
25
|
import copyTrajectory from "./copy-trajectory.ts";
|
|
19
26
|
import copyWithModel from "./copy-with-model.ts";
|
|
20
27
|
import kilo from "./kilo.ts";
|
|
21
28
|
import keepModelOnNew from "./keep-model-on-new.ts";
|
|
22
|
-
import
|
|
29
|
+
import cleanTui from "./clean-tui.ts";
|
|
23
30
|
import providerBalance from "./provider-balance.ts";
|
|
24
31
|
import tps from "./tps.ts";
|
|
25
32
|
import nameWithAi from "./name-with-ai.ts";
|
|
@@ -32,10 +39,8 @@ export default function bermudisPiGoodies(pi: ExtensionAPI): void {
|
|
|
32
39
|
nameWithAi(pi);
|
|
33
40
|
zed(pi);
|
|
34
41
|
preferTools(pi);
|
|
35
|
-
// Register this before model-thinking so /new restores the model first and
|
|
36
|
-
// model-thinking then sees and configures the final active model.
|
|
37
42
|
keepModelOnNew(pi);
|
|
38
|
-
|
|
43
|
+
cleanTui(pi);
|
|
39
44
|
providerBalance(pi);
|
|
40
45
|
kilo(pi);
|
|
41
46
|
tps(pi);
|
package/keep-model-on-new.ts
CHANGED
|
@@ -9,11 +9,23 @@ interface ModelIdentity {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* Pi normally caches extension modules across a session-runtime replacement,
|
|
13
|
+
* but that cache can be invalidated by another resource loader or /reload.
|
|
14
|
+
* Keep the handoff on globalThis so a fresh import in the same Pi process
|
|
15
|
+
* cannot lose it. The state is still transient and never written to disk.
|
|
15
16
|
*/
|
|
16
|
-
const
|
|
17
|
+
const pendingModelsKey = Symbol.for(
|
|
18
|
+
"bermudis-pi-goodies.keep-model-on-new.pending-models",
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
type GlobalWithPendingModels = typeof globalThis & {
|
|
22
|
+
[pendingModelsKey]?: Map<string, ModelIdentity>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function getPendingModels(): Map<string, ModelIdentity> {
|
|
26
|
+
const globalState = globalThis as GlobalWithPendingModels;
|
|
27
|
+
return (globalState[pendingModelsKey] ??= new Map());
|
|
28
|
+
}
|
|
17
29
|
|
|
18
30
|
function handoffKey(
|
|
19
31
|
sessionFile: string | undefined,
|
|
@@ -31,7 +43,7 @@ export default function keepModelOnNew(
|
|
|
31
43
|
pi: ExtensionAPI,
|
|
32
44
|
options: KeepModelOnNewOptions = {},
|
|
33
45
|
): void {
|
|
34
|
-
const handoffs = options.pendingModels ??
|
|
46
|
+
const handoffs = options.pendingModels ?? getPendingModels();
|
|
35
47
|
|
|
36
48
|
pi.on("session_before_switch", (event, ctx) => {
|
|
37
49
|
if (event.reason !== "new" || !ctx.model) return;
|
|
@@ -64,7 +76,12 @@ export default function keepModelOnNew(
|
|
|
64
76
|
}
|
|
65
77
|
|
|
66
78
|
try {
|
|
67
|
-
await pi.setModel(model);
|
|
79
|
+
const restored = await pi.setModel(model);
|
|
80
|
+
if (!restored) {
|
|
81
|
+
const message = `Could not keep model after /new: ${previous.provider}/${previous.id} is not authenticated`;
|
|
82
|
+
console.warn(`[keep-model-on-new] ${message}`);
|
|
83
|
+
ctx.ui.notify(message, "warning");
|
|
84
|
+
}
|
|
68
85
|
} catch (error) {
|
|
69
86
|
const detail = error instanceof Error ? error.message : String(error);
|
|
70
87
|
console.warn(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bermudis-pi-goodies",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A bundle of small, frequently-used Pi extensions.",
|
|
6
6
|
"keywords": ["pi-package"],
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"format": "prettier --write \"*.ts\" \"!*.bundle.ts\""
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@earendil-works/pi-agent-core": "^0.84.
|
|
25
|
-
"@earendil-works/pi-ai": "^0.84.
|
|
26
|
-
"@earendil-works/pi-coding-agent": "^0.84.
|
|
27
|
-
"@earendil-works/pi-tui": "^0.84.
|
|
24
|
+
"@earendil-works/pi-agent-core": "^0.84.3",
|
|
25
|
+
"@earendil-works/pi-ai": "^0.84.3",
|
|
26
|
+
"@earendil-works/pi-coding-agent": "^0.84.3",
|
|
27
|
+
"@earendil-works/pi-tui": "^0.84.3",
|
|
28
28
|
"@marcfargas/pi-test-harness": "0.6.1",
|
|
29
29
|
"esbuild": "^0.27.0",
|
|
30
30
|
"prettier": "^3.8.4"
|