pi-advisor-flow 0.2.0 → 0.2.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/extensions/index.ts +15 -6
- package/package.json +12 -4
- package/src/commands.ts +123 -97
- package/src/config.ts +233 -178
- package/src/conversation.ts +43 -24
- package/src/herdr.ts +3 -6
- package/src/session-state.ts +14 -6
- package/src/tools.ts +282 -185
- package/src/ui.ts +107 -73
package/src/ui.ts
CHANGED
|
@@ -5,12 +5,36 @@ import {
|
|
|
5
5
|
fuzzyFilter,
|
|
6
6
|
Input,
|
|
7
7
|
Key,
|
|
8
|
+
type Keybindings,
|
|
9
|
+
type KeybindingsManager,
|
|
8
10
|
matchesKey,
|
|
9
11
|
truncateToWidth,
|
|
10
12
|
} from "@earendil-works/pi-tui";
|
|
11
13
|
|
|
14
|
+
interface RenderRequester {
|
|
15
|
+
requestRender: () => void;
|
|
16
|
+
}
|
|
17
|
+
interface SearchableModelSelectorOptions {
|
|
18
|
+
allOptions: string[];
|
|
19
|
+
keybindings: KeybindingsManager;
|
|
20
|
+
onCancel: () => void;
|
|
21
|
+
onSelect: (value: string) => void;
|
|
22
|
+
theme: Theme;
|
|
23
|
+
title: string;
|
|
24
|
+
tui: RenderRequester;
|
|
25
|
+
}
|
|
26
|
+
interface AdvisorSettingsSelectorOptions {
|
|
27
|
+
effortLevels: string[];
|
|
28
|
+
initial: AdvisorSettings;
|
|
29
|
+
onCancel: () => void;
|
|
30
|
+
onSave: (settings: AdvisorSettings) => void;
|
|
31
|
+
presets: ContextPreset[];
|
|
32
|
+
theme: Theme;
|
|
33
|
+
tui: RenderRequester;
|
|
34
|
+
}
|
|
35
|
+
|
|
12
36
|
export class SearchableModelSelector implements Component, Focusable {
|
|
13
|
-
private readonly tui:
|
|
37
|
+
private readonly tui: RenderRequester;
|
|
14
38
|
private readonly searchInput: Input;
|
|
15
39
|
private readonly allOptions: string[];
|
|
16
40
|
private filteredOptions: string[];
|
|
@@ -19,26 +43,18 @@ export class SearchableModelSelector implements Component, Focusable {
|
|
|
19
43
|
private readonly onSelect: (value: string) => void;
|
|
20
44
|
private readonly onCancel: () => void;
|
|
21
45
|
private readonly theme: Theme;
|
|
22
|
-
private readonly keybindings:
|
|
46
|
+
private readonly keybindings: KeybindingsManager;
|
|
23
47
|
private _focused = false;
|
|
24
48
|
|
|
25
|
-
|
|
49
|
+
get focused(): boolean {
|
|
26
50
|
return this._focused;
|
|
27
51
|
}
|
|
28
|
-
|
|
52
|
+
set focused(val: boolean) {
|
|
29
53
|
this._focused = val;
|
|
30
54
|
this.searchInput.focused = val;
|
|
31
55
|
}
|
|
32
56
|
|
|
33
|
-
constructor(options: {
|
|
34
|
-
tui: any;
|
|
35
|
-
title: string;
|
|
36
|
-
allOptions: string[];
|
|
37
|
-
theme: Theme;
|
|
38
|
-
keybindings: any;
|
|
39
|
-
onSelect: (value: string) => void;
|
|
40
|
-
onCancel: () => void;
|
|
41
|
-
}) {
|
|
57
|
+
constructor(options: SearchableModelSelectorOptions) {
|
|
42
58
|
this.tui = options.tui;
|
|
43
59
|
this.title = options.title;
|
|
44
60
|
this.allOptions = options.allOptions;
|
|
@@ -85,7 +101,7 @@ export class SearchableModelSelector implements Component, Focusable {
|
|
|
85
101
|
)
|
|
86
102
|
);
|
|
87
103
|
const endIndex = Math.min(startIndex + maxVisible, total);
|
|
88
|
-
for (let i = startIndex; i < endIndex; i
|
|
104
|
+
for (let i = startIndex; i < endIndex; i += 1) {
|
|
89
105
|
const item = this.filteredOptions[i];
|
|
90
106
|
if (i === this.selectedIndex) {
|
|
91
107
|
lines.push(
|
|
@@ -111,44 +127,53 @@ export class SearchableModelSelector implements Component, Focusable {
|
|
|
111
127
|
}
|
|
112
128
|
|
|
113
129
|
handleInput(keyData: string): void {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
kb.matches(keyData, "tui.select.down") ||
|
|
125
|
-
keyData === "\u001b[B"
|
|
126
|
-
) {
|
|
127
|
-
if (this.filteredOptions.length > 0) {
|
|
128
|
-
this.selectedIndex =
|
|
129
|
-
this.selectedIndex === this.filteredOptions.length - 1
|
|
130
|
-
? 0
|
|
131
|
-
: this.selectedIndex + 1;
|
|
132
|
-
}
|
|
133
|
-
this.tui.requestRender();
|
|
134
|
-
} else if (
|
|
135
|
-
kb.matches(keyData, "tui.select.confirm") ||
|
|
136
|
-
keyData === "\n" ||
|
|
130
|
+
if (this.matchesAction(keyData, "tui.select.up", "\u001b[A")) {
|
|
131
|
+
this.moveSelection(-1);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (this.matchesAction(keyData, "tui.select.down", "\u001b[B")) {
|
|
135
|
+
this.moveSelection(1);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (
|
|
139
|
+
this.matchesAction(keyData, "tui.select.confirm", "\n") ||
|
|
137
140
|
keyData === "\r"
|
|
138
141
|
) {
|
|
139
142
|
if (this.filteredOptions.length > 0) {
|
|
140
143
|
this.onSelect(this.filteredOptions[this.selectedIndex]);
|
|
141
144
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (this.matchesAction(keyData, "tui.select.cancel", "\u001b")) {
|
|
146
148
|
this.onCancel();
|
|
147
|
-
|
|
148
|
-
this.searchInput.handleInput(keyData);
|
|
149
|
-
this.selectedIndex = 0;
|
|
150
|
-
this.tui.requestRender();
|
|
149
|
+
return;
|
|
151
150
|
}
|
|
151
|
+
this.searchInput.handleInput(keyData);
|
|
152
|
+
this.selectedIndex = 0;
|
|
153
|
+
this.tui.requestRender();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private matchesAction(
|
|
157
|
+
keyData: string,
|
|
158
|
+
action: keyof Keybindings,
|
|
159
|
+
fallback: string
|
|
160
|
+
) {
|
|
161
|
+
return this.keybindings.matches(keyData, action) || keyData === fallback;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private moveSelection(direction: -1 | 1) {
|
|
165
|
+
if (this.filteredOptions.length > 0) {
|
|
166
|
+
const lastIndex = this.filteredOptions.length - 1;
|
|
167
|
+
const nextIndex = this.selectedIndex + direction;
|
|
168
|
+
if (nextIndex < 0) {
|
|
169
|
+
this.selectedIndex = lastIndex;
|
|
170
|
+
} else if (nextIndex > lastIndex) {
|
|
171
|
+
this.selectedIndex = 0;
|
|
172
|
+
} else {
|
|
173
|
+
this.selectedIndex = nextIndex;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
this.tui.requestRender();
|
|
152
177
|
}
|
|
153
178
|
}
|
|
154
179
|
|
|
@@ -178,33 +203,27 @@ export interface AdvisorSettings {
|
|
|
178
203
|
}
|
|
179
204
|
|
|
180
205
|
export class AdvisorSettingsSelector implements Component, Focusable {
|
|
181
|
-
private selectedRow
|
|
206
|
+
private selectedRow: number;
|
|
182
207
|
private contextIndex: number;
|
|
183
208
|
private effortIndex: number;
|
|
184
209
|
private readonly settings: AdvisorSettings;
|
|
185
210
|
private readonly customInput = new Input();
|
|
186
|
-
private editingCustom
|
|
211
|
+
private editingCustom: boolean;
|
|
187
212
|
private _focused = false;
|
|
213
|
+
private readonly options: AdvisorSettingsSelectorOptions;
|
|
188
214
|
|
|
189
|
-
|
|
215
|
+
get focused(): boolean {
|
|
190
216
|
return this._focused;
|
|
191
217
|
}
|
|
192
|
-
|
|
218
|
+
set focused(value: boolean) {
|
|
193
219
|
this._focused = value;
|
|
194
220
|
this.customInput.focused = value && this.editingCustom;
|
|
195
221
|
}
|
|
196
222
|
|
|
197
|
-
constructor(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
presets: ContextPreset[];
|
|
202
|
-
effortLevels: string[];
|
|
203
|
-
initial: AdvisorSettings;
|
|
204
|
-
onSave: (settings: AdvisorSettings) => void;
|
|
205
|
-
onCancel: () => void;
|
|
206
|
-
}
|
|
207
|
-
) {
|
|
223
|
+
constructor(options: AdvisorSettingsSelectorOptions) {
|
|
224
|
+
this.selectedRow = 0;
|
|
225
|
+
this.editingCustom = false;
|
|
226
|
+
this.options = options;
|
|
208
227
|
this.settings = { ...options.initial };
|
|
209
228
|
this.contextIndex = Math.max(
|
|
210
229
|
0,
|
|
@@ -235,8 +254,20 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
235
254
|
this.options.tui.requestRender();
|
|
236
255
|
}
|
|
237
256
|
|
|
238
|
-
private currentContext() {
|
|
239
|
-
|
|
257
|
+
private currentContext(): ContextPreset {
|
|
258
|
+
const preset = this.options.presets.find(
|
|
259
|
+
(item) => item.value === this.settings.contextMaxChars
|
|
260
|
+
);
|
|
261
|
+
return (
|
|
262
|
+
preset ?? {
|
|
263
|
+
description: "Current custom context limit",
|
|
264
|
+
label: String(this.settings.contextMaxChars),
|
|
265
|
+
value: this.settings.contextMaxChars,
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
private currentEffort() {
|
|
270
|
+
return this.settings.effort || "Default (Model Default)";
|
|
240
271
|
}
|
|
241
272
|
private row(label: string, value: string, index: number) {
|
|
242
273
|
const { theme } = this.options;
|
|
@@ -256,8 +287,8 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
256
287
|
const track = Array.from({ length: trackWidth }, () => "─");
|
|
257
288
|
track[positions[this.contextIndex]] = "▲";
|
|
258
289
|
const labels = Array.from({ length: trackWidth }, () => " ");
|
|
259
|
-
for (let index = 0; index < presets.length; index
|
|
260
|
-
const label = presets[index]
|
|
290
|
+
for (let index = 0; index < presets.length; index += 1) {
|
|
291
|
+
const { label } = presets[index];
|
|
261
292
|
const start = Math.max(
|
|
262
293
|
0,
|
|
263
294
|
Math.min(
|
|
@@ -265,7 +296,7 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
265
296
|
positions[index] - Math.floor(label.length / 2)
|
|
266
297
|
)
|
|
267
298
|
);
|
|
268
|
-
for (let char = 0; char < label.length; char
|
|
299
|
+
for (let char = 0; char < label.length; char += 1) {
|
|
269
300
|
labels[start + char] = label[char];
|
|
270
301
|
}
|
|
271
302
|
}
|
|
@@ -273,11 +304,7 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
273
304
|
const onOff = (value: boolean) => (value ? "On" : "Off");
|
|
274
305
|
const rows = [
|
|
275
306
|
this.row("Context window", this.currentContext().label, 0),
|
|
276
|
-
this.row(
|
|
277
|
-
"Advisor reasoning",
|
|
278
|
-
this.options.effortLevels[this.effortIndex],
|
|
279
|
-
1
|
|
280
|
-
),
|
|
307
|
+
this.row("Advisor reasoning", this.currentEffort(), 1),
|
|
281
308
|
this.row("Plan gate", onOff(this.settings.planGate), 2),
|
|
282
309
|
this.row("Failure gate", onOff(this.settings.failureGate), 3),
|
|
283
310
|
this.row("Completion gate", onOff(this.settings.completionGate), 4),
|
|
@@ -377,15 +404,17 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
377
404
|
return;
|
|
378
405
|
}
|
|
379
406
|
if (this.selectedRow === 16) {
|
|
380
|
-
|
|
407
|
+
this.options.onSave({
|
|
381
408
|
...this.settings,
|
|
382
409
|
contextMaxChars: this.currentContext().value,
|
|
383
|
-
effort: this.
|
|
410
|
+
effort: this.currentEffort(),
|
|
384
411
|
});
|
|
412
|
+
return;
|
|
385
413
|
}
|
|
386
414
|
this.adjust(1);
|
|
387
415
|
} else if (matchesKey(keyData, Key.escape)) {
|
|
388
|
-
|
|
416
|
+
this.options.onCancel();
|
|
417
|
+
return;
|
|
389
418
|
} else {
|
|
390
419
|
return;
|
|
391
420
|
}
|
|
@@ -402,6 +431,8 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
402
431
|
this.contextIndex + direction
|
|
403
432
|
)
|
|
404
433
|
);
|
|
434
|
+
this.settings.contextMaxChars =
|
|
435
|
+
this.options.presets[this.contextIndex].value;
|
|
405
436
|
break;
|
|
406
437
|
case 1:
|
|
407
438
|
this.effortIndex = Math.max(
|
|
@@ -411,6 +442,7 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
411
442
|
this.effortIndex + direction
|
|
412
443
|
)
|
|
413
444
|
);
|
|
445
|
+
this.settings.effort = this.options.effortLevels[this.effortIndex];
|
|
414
446
|
break;
|
|
415
447
|
case 2:
|
|
416
448
|
this.settings.planGate = !this.settings.planGate;
|
|
@@ -488,6 +520,8 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
488
520
|
values[Math.max(0, Math.min(values.length - 1, index + direction))];
|
|
489
521
|
break;
|
|
490
522
|
}
|
|
523
|
+
default:
|
|
524
|
+
break;
|
|
491
525
|
}
|
|
492
526
|
}
|
|
493
527
|
}
|