@pi-unipi/unipi 2.6.0 → 2.6.1
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 +19 -0
- package/package.json +21 -21
- package/packages/ask-user/package.json +2 -2
- package/packages/autocomplete/package.json +1 -1
- package/packages/autocomplete/src/constants.ts +2 -0
- package/packages/btw/package.json +2 -2
- package/packages/cocoindex/package.json +2 -2
- package/packages/compactor/package.json +3 -3
- package/packages/core/constants.ts +1 -0
- package/packages/core/model-cache.ts +18 -12
- package/packages/core/package.json +1 -1
- package/packages/footer/package.json +2 -2
- package/packages/image/package.json +2 -2
- package/packages/info-screen/package.json +2 -2
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/kanboard/package.json +2 -2
- package/packages/mcp/package.json +2 -2
- package/packages/memory/package.json +3 -3
- package/packages/milestone/package.json +2 -2
- package/packages/notify/README.md +1 -0
- package/packages/notify/commands.ts +61 -3
- package/packages/notify/package.json +2 -2
- package/packages/notify/tui/recap-model-selector.ts +72 -31
- package/packages/notify/tui/settings-overlay.ts +45 -32
- package/packages/ralph/package.json +3 -3
- package/packages/subagents/package.json +1 -1
- package/packages/unipi/bundled.js +217 -141
- package/packages/updater/package.json +2 -2
- package/packages/utility/package.json +2 -2
- package/packages/web-api/package.json +2 -2
- package/packages/workflow/package.json +2 -2
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* @pi-unipi/notify — Recap Model Selector TUI
|
|
3
3
|
*
|
|
4
4
|
* Interactive overlay for selecting the recap summarization model.
|
|
5
|
-
* Uses
|
|
5
|
+
* Uses models injected from Pi's live model registry (preferred), falling back
|
|
6
|
+
* to the project-wide cached model list from ~/.unipi/config/models-cache.json.
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
import type { Component } from "@earendil-works/pi-tui";
|
|
@@ -28,9 +29,16 @@ export class RecapModelSelectorOverlay implements Component {
|
|
|
28
29
|
requestRender?: () => void;
|
|
29
30
|
private theme: Theme | null = null;
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
/**
|
|
33
|
+
* @param models Optional model list, preferably collected from Pi's live
|
|
34
|
+
* model registry by the command handler. Falls back to the project-wide
|
|
35
|
+
* cache file (`~/.unipi/config/models-cache.json`), which may not exist
|
|
36
|
+
* (issue #27: selector showed "No models found" while Pi itself had
|
|
37
|
+
* models configured in `~/.pi/agent/models.json`).
|
|
38
|
+
*/
|
|
39
|
+
constructor(models?: CachedModel[]) {
|
|
40
|
+
// Prefer models injected from Pi's live model registry; fall back to cache.
|
|
41
|
+
this.models = models ?? readModelCache();
|
|
34
42
|
this.applyFilter();
|
|
35
43
|
|
|
36
44
|
// Pre-select current config model
|
|
@@ -49,23 +57,42 @@ export class RecapModelSelectorOverlay implements Component {
|
|
|
49
57
|
invalidate(): void {}
|
|
50
58
|
|
|
51
59
|
handleInput(data: string): void {
|
|
60
|
+
// Ctrl+C must always close, even mid-filter — without this the overlay
|
|
61
|
+
// could trap the user on terminals with unexpected key encodings.
|
|
62
|
+
if (matchesKey(data, "ctrl+c")) {
|
|
63
|
+
this.onClose?.();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
52
67
|
// Filter mode: type to search
|
|
53
68
|
if (this.filterMode) {
|
|
54
|
-
if (data
|
|
69
|
+
if (matchesKey(data, "enter")) {
|
|
55
70
|
// Enter — exit filter mode
|
|
56
71
|
this.filterMode = false;
|
|
57
72
|
return;
|
|
58
73
|
}
|
|
59
74
|
if (matchesKey(data, "escape")) {
|
|
60
|
-
// Escape — clear filter and exit filter mode
|
|
75
|
+
// Escape — clear filter and exit filter mode (does NOT close overlay)
|
|
61
76
|
this.filter = "";
|
|
62
77
|
this.filterMode = false;
|
|
63
78
|
this.applyFilter();
|
|
64
79
|
this.selectedIndex = 0;
|
|
65
80
|
return;
|
|
66
81
|
}
|
|
67
|
-
|
|
68
|
-
|
|
82
|
+
// Let the list be navigated without leaving filter mode.
|
|
83
|
+
if (matchesKey(data, "up")) {
|
|
84
|
+
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (matchesKey(data, "down")) {
|
|
88
|
+
this.selectedIndex = Math.min(
|
|
89
|
+
Math.max(0, this.filteredModels.length - 1),
|
|
90
|
+
this.selectedIndex + 1
|
|
91
|
+
);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (matchesKey(data, "backspace") || data === "\x7f" || data === "\b") {
|
|
95
|
+
// Backspace (legacy or kitty "\x1b[127u")
|
|
69
96
|
this.filter = this.filter.slice(0, -1);
|
|
70
97
|
this.applyFilter();
|
|
71
98
|
this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));
|
|
@@ -80,28 +107,36 @@ export class RecapModelSelectorOverlay implements Component {
|
|
|
80
107
|
return;
|
|
81
108
|
}
|
|
82
109
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
// Keys are matched via matchesKey, never raw byte comparison: under the
|
|
111
|
+
// kitty keyboard protocol / enhanced encodings (Ghostty, Herdr) Escape
|
|
112
|
+
// arrives as "\x1b[27u" and arrows as "\x1b[57419u"/"\x1b[57420u", so
|
|
113
|
+
// exact legacy comparisons silently fail there (issue #27).
|
|
114
|
+
if (matchesKey(data, "up") || data === "k") {
|
|
115
|
+
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (matchesKey(data, "down") || data === "j") {
|
|
119
|
+
this.selectedIndex = Math.min(
|
|
120
|
+
Math.max(0, this.filteredModels.length - 1),
|
|
121
|
+
this.selectedIndex + 1
|
|
122
|
+
);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (data === "/") {
|
|
126
|
+
// Start filter
|
|
127
|
+
this.filterMode = true;
|
|
128
|
+
this.filter = "";
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (matchesKey(data, "enter")) {
|
|
132
|
+
// Enter — select and save
|
|
133
|
+
this.selectModel();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (matchesKey(data, "escape")) {
|
|
137
|
+
// Escape — close
|
|
138
|
+
this.onClose?.();
|
|
139
|
+
return;
|
|
105
140
|
}
|
|
106
141
|
}
|
|
107
142
|
|
|
@@ -226,9 +261,15 @@ export class RecapModelSelectorOverlay implements Component {
|
|
|
226
261
|
);
|
|
227
262
|
|
|
228
263
|
if (this.filteredModels.length === 0) {
|
|
264
|
+
const emptyMsg =
|
|
265
|
+
this.filter.length > 0
|
|
266
|
+
? `No models match "${this.filter}"`
|
|
267
|
+
: this.models.length === 0
|
|
268
|
+
? "No models — check ~/.pi/agent/models.json or API keys, then reopen"
|
|
269
|
+
: "No models found";
|
|
229
270
|
lines.push(
|
|
230
271
|
this.frameLine(
|
|
231
|
-
` ${this.fg("dim",
|
|
272
|
+
` ${this.fg("dim", emptyMsg)}`,
|
|
232
273
|
innerWidth
|
|
233
274
|
)
|
|
234
275
|
);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { Component } from "@earendil-works/pi-tui";
|
|
9
|
-
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
9
|
+
import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
10
10
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import {
|
|
12
12
|
loadConfig,
|
|
@@ -51,37 +51,50 @@ export class NotifySettingsOverlay implements Component {
|
|
|
51
51
|
invalidate(): void {}
|
|
52
52
|
|
|
53
53
|
handleInput(data: string): void {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
54
|
+
// Ctrl+C always closes — escape hatch for terminals with key encodings
|
|
55
|
+
// this overlay does not understand (issue #27).
|
|
56
|
+
if (matchesKey(data, "ctrl+c")) {
|
|
57
|
+
this.onClose?.();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// Navigation keys are matched via matchesKey, never raw byte comparison:
|
|
61
|
+
// under the kitty keyboard protocol / enhanced encodings (Ghostty, Herdr)
|
|
62
|
+
// Escape arrives as "\x1b[27u" (or "\x1b[27;1;27~" with modifyOtherKeys)
|
|
63
|
+
// and arrows as "\x1b[57419u"/"\x1b[57420u" — exact legacy comparisons
|
|
64
|
+
// like data === "\x1b[A" silently fail there.
|
|
65
|
+
if (matchesKey(data, "up") || data === "k") {
|
|
66
|
+
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (matchesKey(data, "down") || data === "j") {
|
|
70
|
+
this.selectedIndex = Math.min(this.maxItems - 1, this.selectedIndex + 1);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (matchesKey(data, "space")) {
|
|
74
|
+
this.toggleCurrent();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (matchesKey(data, "tab")) {
|
|
78
|
+
const sections: Section[] = ["platforms", "events", "recap"];
|
|
79
|
+
const idx = sections.indexOf(this.section);
|
|
80
|
+
this.section = sections[(idx + 1) % sections.length];
|
|
81
|
+
this.selectedIndex = 0;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (data === "m" || data === "M") {
|
|
85
|
+
// Open model selector (only in recap section)
|
|
86
|
+
if (this.section === "recap") {
|
|
87
|
+
this.onOpenModelSelector?.();
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (matchesKey(data, "enter")) {
|
|
92
|
+
this.save();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (matchesKey(data, "escape")) {
|
|
96
|
+
this.onClose?.();
|
|
97
|
+
return;
|
|
85
98
|
}
|
|
86
99
|
}
|
|
87
100
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/ralph",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "Long-running iterative development loops for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"test": "npx tsx --test reminder.test.ts"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@pi-unipi/core": "2.6.
|
|
34
|
-
"@pi-unipi/info-screen": "2.6.
|
|
33
|
+
"@pi-unipi/core": "2.6.1",
|
|
34
|
+
"@pi-unipi/info-screen": "2.6.1"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@earendil-works/pi-ai": "^0.80.0",
|