pi-model-sort 0.1.4 → 0.2.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 +7 -2
- package/model-sort.ts +42 -1
- package/package.json +1 -1
- package/src/index.ts +7 -0
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ Pi's `/model` selector sorts models alphabetically by provider. If you have Anth
|
|
|
23
23
|
|
|
24
24
|
- **Automatic tracking** — every `/model` switch, `Ctrl+P` cycle, and session restore is recorded with a Unix timestamp
|
|
25
25
|
- **Sort order** — current model first → most recently used descending → provider/id alphabetical fallback
|
|
26
|
+
- **MRU on startup** — new sessions start on your most recently used model instead of `scopedModels[0]` or the hardcoded provider default order
|
|
26
27
|
- **Persistent** — usage data lives in `~/.pi/agent/extensions/pi-model-sort.json`, survives restarts
|
|
27
28
|
- **No config needed** — install and forget; the extension starts tracking on first use
|
|
28
29
|
- **Zero setup** — with no recorded usage, models fall back to the default alphabetical order
|
|
@@ -99,7 +100,7 @@ model_select event fires
|
|
|
99
100
|
→ Writes to pi-model-sort.json
|
|
100
101
|
→ Next /model opens with updated sort
|
|
101
102
|
|
|
102
|
-
Session starts
|
|
103
|
+
Session starts (startup / new)
|
|
103
104
|
→ Extension reads pi-model-sort.json
|
|
104
105
|
→ Monkey-patches ModelSelectorComponent.prototype:
|
|
105
106
|
sortModels — sorts "Scope: all" view
|
|
@@ -108,9 +109,12 @@ Session starts
|
|
|
108
109
|
→ Monkey-patches AgentSession.prototype._cycleScopedModel
|
|
109
110
|
→ Sort order: current model first → most recent → provider/id alphabetical
|
|
110
111
|
→ Patches survive modelRegistry.refresh()
|
|
112
|
+
→ Overrides initial model to MRU via pi.setModel()
|
|
113
|
+
if pi core chose a different model (scopedModels[0], defaultModelPerProvider)
|
|
114
|
+
only on startup and new session starts — not resume, reload, or fork
|
|
111
115
|
```
|
|
112
116
|
|
|
113
|
-
**Five patches, full coverage:**
|
|
117
|
+
**Five patches + MRU startup override, full coverage:**
|
|
114
118
|
|
|
115
119
|
| Patch | What it affects |
|
|
116
120
|
|-------|----------------|
|
|
@@ -119,6 +123,7 @@ Session starts
|
|
|
119
123
|
| `AgentSession.prototype._cycleScopedModel` | `Ctrl+P` / `Ctrl+Shift+P` cycling order (non-destructive swap, cycling does not update last-used to avoid feedback loop) |
|
|
120
124
|
| `ModelRegistry.prototype.getAvailable()` | `/scoped-models` config selector, model resolution |
|
|
121
125
|
| `ModelRegistry.prototype.getAll()` | `--list-models` CLI output |
|
|
126
|
+
| `pi.setModel()` on `session_start` | MRU model selection on startup and `/new` — overrides pi core's default |
|
|
122
127
|
|
|
123
128
|
When no scoped models are configured, Ctrl+P falls through to `_cycleAvailableModel` which calls `getAvailable()` — already sorted by the registry patch.
|
|
124
129
|
|
package/model-sort.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
buildModelKey,
|
|
27
27
|
CONFIG_FILENAME,
|
|
28
28
|
type ModelSortConfig,
|
|
29
|
+
parseModelKey,
|
|
29
30
|
sortByLastUsed,
|
|
30
31
|
} from "./src/index.js";
|
|
31
32
|
|
|
@@ -302,12 +303,32 @@ function unpatchCycleScopedModel(): void {
|
|
|
302
303
|
origCycleScopedModel = null;
|
|
303
304
|
}
|
|
304
305
|
|
|
306
|
+
// MRU model lookup — finds the most recently used model that exists in the
|
|
307
|
+
// registry and has auth configured. Returns undefined if no usable model found.
|
|
308
|
+
|
|
309
|
+
function findMruModel(
|
|
310
|
+
lastUsed: Record<string, number>,
|
|
311
|
+
registry: { find(provider: string, modelId: string): unknown; hasConfiguredAuth(model: unknown): boolean },
|
|
312
|
+
): unknown | undefined {
|
|
313
|
+
const sorted = Object.entries(lastUsed).sort(([, a], [, b]) => b - a);
|
|
314
|
+
for (const [key] of sorted) {
|
|
315
|
+
const parsed = parseModelKey(key);
|
|
316
|
+
if (!parsed) continue;
|
|
317
|
+
const [provider, modelId] = parsed;
|
|
318
|
+
const model = registry.find(provider, modelId);
|
|
319
|
+
if (model && registry.hasConfiguredAuth(model)) {
|
|
320
|
+
return model;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
|
|
305
326
|
// Extension
|
|
306
327
|
|
|
307
328
|
export default function (pi: ExtensionAPI) {
|
|
308
329
|
let lastUsed: Record<string, number> = {};
|
|
309
330
|
|
|
310
|
-
pi.on("session_start", async (
|
|
331
|
+
pi.on("session_start", async (event, ctx) => {
|
|
311
332
|
const config = readConfig();
|
|
312
333
|
lastUsed = config.lastUsed;
|
|
313
334
|
|
|
@@ -317,6 +338,26 @@ export default function (pi: ExtensionAPI) {
|
|
|
317
338
|
patchFilterModels(() => lastUsed);
|
|
318
339
|
patchCycleScopedModel(() => lastUsed);
|
|
319
340
|
|
|
341
|
+
// Override initial model to MRU on new sessions.
|
|
342
|
+
// Pi core picks the saved default if in scope, otherwise scopedModels[0].
|
|
343
|
+
// This hijack switches to the most recently used model instead, so your
|
|
344
|
+
// actual usage history determines the default — not alphabetical scope order.
|
|
345
|
+
if (
|
|
346
|
+
(event.reason === "startup" || event.reason === "new") &&
|
|
347
|
+
Object.keys(lastUsed).length > 0
|
|
348
|
+
) {
|
|
349
|
+
const mruModel = findMruModel(lastUsed, ctx.modelRegistry);
|
|
350
|
+
const currentModel = ctx.model as { provider: string; id: string } | undefined;
|
|
351
|
+
if (
|
|
352
|
+
mruModel &&
|
|
353
|
+
(!currentModel ||
|
|
354
|
+
currentModel.provider !== (mruModel as { provider: string }).provider ||
|
|
355
|
+
currentModel.id !== (mruModel as { id: string }).id)
|
|
356
|
+
) {
|
|
357
|
+
await pi.setModel(mruModel as Parameters<typeof pi.setModel>[0]);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
320
361
|
if (ctx.hasUI) {
|
|
321
362
|
const count = Object.keys(lastUsed).length;
|
|
322
363
|
ctx.ui.notify(
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -10,6 +10,13 @@ export interface ModelSortConfig {
|
|
|
10
10
|
lastUsed: Record<string, number>;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/** Parse a model key into [provider, modelId]. Returns undefined if malformed. */
|
|
14
|
+
export function parseModelKey(key: string): [provider: string, modelId: string] | undefined {
|
|
15
|
+
const idx = key.indexOf("/");
|
|
16
|
+
if (idx === -1) return undefined;
|
|
17
|
+
return [key.substring(0, idx), key.substring(idx + 1)];
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
/** Build a stable model key from provider and model id. */
|
|
14
21
|
export function buildModelKey(provider: string, modelId: string): string {
|
|
15
22
|
return `${provider}/${modelId}`;
|