pi-advisor-flow 0.6.0 → 0.8.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/CHANGELOG.md +34 -17
- package/README.md +10 -8
- package/dist/index.js +2852 -868
- package/extensions/index.ts +2 -1
- package/package.json +55 -40
- package/src/attachments.ts +11 -10
- package/src/commands/activation-preparation.ts +1 -0
- package/src/commands/activation.ts +2 -0
- package/src/commands/lifecycle.ts +1 -0
- package/src/commands/manual-command.ts +7 -1
- package/src/commands/manual-consultation.ts +103 -79
- package/src/commands/manual-progress.ts +7 -5
- package/src/commands/model-commands.ts +1 -1
- package/src/commands/model-options.ts +14 -3
- package/src/commands/model-picker.ts +1 -0
- package/src/commands/registration.ts +1 -0
- package/src/commands/renderers.ts +67 -55
- package/src/commands/runtime.ts +16 -14
- package/src/commands/settings-commands.ts +10 -4
- package/src/commands/settings-persistence.ts +64 -2
- package/src/commands/types.ts +2 -0
- package/src/commands.ts +0 -1
- package/src/config/args.ts +1 -1
- package/src/config/defaults.ts +83 -1
- package/src/config/schema.ts +152 -15
- package/src/config/state.ts +80 -2
- package/src/config/storage.ts +38 -22
- package/src/config/types.ts +45 -3
- package/src/config/validation.ts +33 -21
- package/src/config.ts +3 -0
- package/src/content-utils.ts +24 -4
- package/src/conversation.ts +16 -15
- package/src/git.ts +10 -3
- package/src/herdr-block.ts +83 -0
- package/src/herdr-shared.ts +108 -0
- package/src/herdr.ts +30 -192
- package/src/jev/client.ts +338 -0
- package/src/jev/failure.ts +19 -0
- package/src/jev/key-store.ts +267 -0
- package/src/jev/ledger.ts +228 -0
- package/src/jev/questions.ts +122 -0
- package/src/jev/state.ts +50 -0
- package/src/jev/transport.ts +63 -0
- package/src/model-stream.ts +35 -37
- package/src/outcomes.ts +20 -12
- package/src/pi-settings.ts +4 -9
- package/src/preferences.ts +4 -2
- package/src/redaction.ts +10 -12
- package/src/scout-context.ts +59 -60
- package/src/scout-curation.ts +4 -13
- package/src/scout-groups.ts +178 -175
- package/src/scout-protocol.ts +74 -70
- package/src/scout-reconstruct.ts +1 -1
- package/src/scout.ts +115 -73
- package/src/session-state.ts +148 -39
- package/src/tool-result-cap.ts +18 -17
- package/src/tools/consult-context.ts +7 -15
- package/src/tools/consultation.ts +14 -4
- package/src/tools/gate-policy.ts +1 -0
- package/src/tools/gate-protocol.ts +36 -23
- package/src/tools/jev-filter.ts +204 -0
- package/src/tools/jev-turn-gate.ts +203 -0
- package/src/tools/loop-gate.ts +32 -20
- package/src/tools/model-access.ts +46 -0
- package/src/tools/outage-notifier.ts +30 -0
- package/src/tools/prompts.ts +20 -25
- package/src/tools/register-ask-advisor.ts +73 -17
- package/src/tools/register-lifecycle.ts +22 -0
- package/src/tools/register-outcome.ts +2 -0
- package/src/tools/register-renderers.ts +149 -73
- package/src/tools/registration.ts +15 -0
- package/src/tools/render-advisor-result.ts +77 -35
- package/src/tools/render-common.ts +16 -19
- package/src/tools/scout-status.ts +42 -27
- package/src/tools/types.ts +11 -0
- package/src/tools.ts +0 -1
- package/src/ui/jev-setup-submenu.ts +350 -0
- package/src/ui/manual-dialog-render.ts +1 -0
- package/src/ui/manual-dialog.ts +39 -43
- package/src/ui/masked-input.ts +62 -0
- package/src/ui/model-multi-selector.ts +9 -0
- package/src/ui/model-selector-adapter.ts +31 -0
- package/src/ui/model-selector.ts +14 -161
- package/src/ui/searchable-model-list.ts +230 -0
- package/src/ui/settings-formatting.ts +24 -14
- package/src/ui/settings-items.ts +200 -2
- package/src/ui/settings-list-adapter.ts +15 -12
- package/src/ui/settings-mutations.ts +169 -47
- package/src/ui/settings-selector.ts +18 -9
- package/src/ui/text-setting-submenu.ts +3 -6
- package/src/ui/types.ts +25 -0
- package/src/ui.ts +0 -1
- package/src/usage.ts +29 -20
package/src/ui/model-selector.ts
CHANGED
|
@@ -1,167 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
type Component,
|
|
4
|
-
type Focusable,
|
|
5
|
-
fuzzyFilter,
|
|
6
|
-
Input,
|
|
7
|
-
type Keybindings,
|
|
8
|
-
type KeybindingsManager,
|
|
9
|
-
truncateToWidth,
|
|
10
|
-
} from "@earendil-works/pi-tui";
|
|
1
|
+
import { ModelSelectorAdapter } from "./model-selector-adapter.ts";
|
|
2
|
+
import { SearchableModelList } from "./searchable-model-list.ts";
|
|
11
3
|
import type { SearchableModelSelectorOptions } from "./types.ts";
|
|
12
4
|
|
|
13
|
-
export class SearchableModelSelector
|
|
14
|
-
private readonly tui: SearchableModelSelectorOptions["tui"];
|
|
15
|
-
private readonly searchInput: Input;
|
|
16
|
-
private readonly allOptions: string[];
|
|
17
|
-
private readonly currentOption: string | undefined;
|
|
18
|
-
private filteredOptions: string[];
|
|
19
|
-
private selectedIndex = 0;
|
|
20
|
-
private readonly title: string;
|
|
21
|
-
private readonly onSelect: (value: string) => void;
|
|
22
|
-
private readonly onCancel: () => void;
|
|
23
|
-
private readonly theme: Theme;
|
|
24
|
-
private readonly keybindings: KeybindingsManager;
|
|
25
|
-
private _focused = false;
|
|
26
|
-
|
|
27
|
-
get focused(): boolean {
|
|
28
|
-
return this._focused;
|
|
29
|
-
}
|
|
30
|
-
set focused(val: boolean) {
|
|
31
|
-
this._focused = val;
|
|
32
|
-
this.searchInput.focused = val;
|
|
33
|
-
}
|
|
34
|
-
|
|
5
|
+
export class SearchableModelSelector extends ModelSelectorAdapter {
|
|
35
6
|
constructor(options: SearchableModelSelectorOptions) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
]
|
|
48
|
-
: options.allOptions;
|
|
49
|
-
this.theme = options.theme;
|
|
50
|
-
this.keybindings = options.keybindings;
|
|
51
|
-
this.onSelect = options.onSelect;
|
|
52
|
-
this.onCancel = options.onCancel;
|
|
53
|
-
this.searchInput = new Input();
|
|
54
|
-
this.filteredOptions = this.allOptions;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
invalidate(): void {
|
|
58
|
-
this.searchInput.invalidate();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
render(width: number): string[] {
|
|
62
|
-
const lines: string[] = [this.theme.fg("border", "─".repeat(width))];
|
|
63
|
-
lines.push(` ${this.theme.fg("accent", this.theme.bold(this.title))}`);
|
|
64
|
-
const inputLines = this.searchInput.render(Math.max(1, width - 10));
|
|
65
|
-
lines.push(
|
|
66
|
-
` ${this.theme.fg("accent", "Search: ")}${inputLines[0] || ""}`
|
|
67
|
-
);
|
|
68
|
-
lines.push("");
|
|
69
|
-
|
|
70
|
-
const query = this.searchInput.getValue().trim();
|
|
71
|
-
this.filteredOptions = query
|
|
72
|
-
? fuzzyFilter(this.allOptions, query, (item) => item)
|
|
73
|
-
: this.allOptions;
|
|
74
|
-
this.selectedIndex = Math.min(
|
|
75
|
-
this.selectedIndex,
|
|
76
|
-
Math.max(0, this.filteredOptions.length - 1)
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const maxVisible = 10;
|
|
80
|
-
const total = this.filteredOptions.length;
|
|
81
|
-
if (total === 0) {
|
|
82
|
-
lines.push(` ${this.theme.fg("muted", "No matching models found.")}`);
|
|
83
|
-
} else {
|
|
84
|
-
const startIndex = Math.max(
|
|
85
|
-
0,
|
|
86
|
-
Math.min(
|
|
87
|
-
this.selectedIndex - Math.floor(maxVisible / 2),
|
|
88
|
-
total - maxVisible
|
|
89
|
-
)
|
|
90
|
-
);
|
|
91
|
-
const endIndex = Math.min(startIndex + maxVisible, total);
|
|
92
|
-
for (let i = startIndex; i < endIndex; i += 1) {
|
|
93
|
-
const item = this.filteredOptions[i];
|
|
94
|
-
const tick = item === this.currentOption ? "✓ " : " ";
|
|
95
|
-
if (i === this.selectedIndex) {
|
|
96
|
-
lines.push(
|
|
97
|
-
` ${this.theme.fg("accent", "→ ")}${this.theme.fg("accent", `${tick}${item}`)}`
|
|
98
|
-
);
|
|
99
|
-
} else {
|
|
100
|
-
lines.push(` ${this.theme.fg("text", `${tick}${item}`)}`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (total > maxVisible) {
|
|
104
|
-
lines.push(
|
|
105
|
-
" " +
|
|
106
|
-
this.theme.fg("muted", ` (${this.selectedIndex + 1}/${total})`)
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
lines.push("");
|
|
111
|
-
lines.push(
|
|
112
|
-
` ${this.theme.fg("dim", "Type to search · ↑↓: navigate · Enter: select · Esc: cancel")}`
|
|
7
|
+
super(
|
|
8
|
+
new SearchableModelList({
|
|
9
|
+
...options,
|
|
10
|
+
currentOptions: options.currentOption ? [options.currentOption] : [],
|
|
11
|
+
multiSelect: false,
|
|
12
|
+
onSelect: ([value]) => {
|
|
13
|
+
if (value !== undefined) {
|
|
14
|
+
options.onSelect(value);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
})
|
|
113
18
|
);
|
|
114
|
-
lines.push(this.theme.fg("border", "─".repeat(width)));
|
|
115
|
-
return lines.map((line) => truncateToWidth(line, width));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
handleInput(keyData: string): void {
|
|
119
|
-
if (this.matchesAction(keyData, "tui.select.up", "\u001b[A")) {
|
|
120
|
-
this.moveSelection(-1);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
if (this.matchesAction(keyData, "tui.select.down", "\u001b[B")) {
|
|
124
|
-
this.moveSelection(1);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (
|
|
128
|
-
this.matchesAction(keyData, "tui.select.confirm", "\n") ||
|
|
129
|
-
keyData === "\r"
|
|
130
|
-
) {
|
|
131
|
-
if (this.filteredOptions.length > 0) {
|
|
132
|
-
this.onSelect(this.filteredOptions[this.selectedIndex]);
|
|
133
|
-
}
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
if (this.matchesAction(keyData, "tui.select.cancel", "\u001b")) {
|
|
137
|
-
this.onCancel();
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
this.searchInput.handleInput(keyData);
|
|
141
|
-
this.selectedIndex = 0;
|
|
142
|
-
this.tui.requestRender();
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private matchesAction(
|
|
146
|
-
keyData: string,
|
|
147
|
-
action: keyof Keybindings,
|
|
148
|
-
fallback: string
|
|
149
|
-
) {
|
|
150
|
-
return this.keybindings.matches(keyData, action) || keyData === fallback;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
private moveSelection(direction: -1 | 1) {
|
|
154
|
-
if (this.filteredOptions.length > 0) {
|
|
155
|
-
const lastIndex = this.filteredOptions.length - 1;
|
|
156
|
-
const nextIndex = this.selectedIndex + direction;
|
|
157
|
-
if (nextIndex < 0) {
|
|
158
|
-
this.selectedIndex = lastIndex;
|
|
159
|
-
} else if (nextIndex > lastIndex) {
|
|
160
|
-
this.selectedIndex = 0;
|
|
161
|
-
} else {
|
|
162
|
-
this.selectedIndex = nextIndex;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
this.tui.requestRender();
|
|
166
19
|
}
|
|
167
20
|
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { fuzzyFilter, Input, truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
import type {
|
|
4
|
+
Component,
|
|
5
|
+
Focusable,
|
|
6
|
+
Keybindings,
|
|
7
|
+
KeybindingsManager,
|
|
8
|
+
} from "@earendil-works/pi-tui";
|
|
9
|
+
|
|
10
|
+
import type { RenderRequester } from "./types.ts";
|
|
11
|
+
|
|
12
|
+
interface SearchableModelListOptions {
|
|
13
|
+
allOptions: string[];
|
|
14
|
+
currentOptions: string[];
|
|
15
|
+
keybindings: KeybindingsManager;
|
|
16
|
+
multiSelect: boolean;
|
|
17
|
+
onCancel: () => void;
|
|
18
|
+
onSelect: (values: string[]) => void;
|
|
19
|
+
theme: Theme;
|
|
20
|
+
title: string;
|
|
21
|
+
tui: RenderRequester;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class SearchableModelList implements Component, Focusable {
|
|
25
|
+
private readonly tui: RenderRequester;
|
|
26
|
+
private readonly searchInput: Input;
|
|
27
|
+
private readonly allOptions: string[];
|
|
28
|
+
private readonly currentOption: string | undefined;
|
|
29
|
+
private readonly multiSelect: boolean;
|
|
30
|
+
private readonly selected = new Set<string>();
|
|
31
|
+
private filteredOptions: string[];
|
|
32
|
+
private selectedIndex = 0;
|
|
33
|
+
private readonly title: string;
|
|
34
|
+
private readonly onSelect: (values: string[]) => void;
|
|
35
|
+
private readonly onCancel: () => void;
|
|
36
|
+
private readonly theme: Theme;
|
|
37
|
+
private readonly keybindings: KeybindingsManager;
|
|
38
|
+
private _focused = false;
|
|
39
|
+
|
|
40
|
+
get focused(): boolean {
|
|
41
|
+
return this._focused;
|
|
42
|
+
}
|
|
43
|
+
set focused(val: boolean) {
|
|
44
|
+
this._focused = val;
|
|
45
|
+
this.searchInput.focused = val;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
constructor(options: SearchableModelListOptions) {
|
|
49
|
+
this.multiSelect = options.multiSelect;
|
|
50
|
+
this.tui = options.tui;
|
|
51
|
+
this.title = options.title;
|
|
52
|
+
const [requestedCurrentOption] = options.currentOptions;
|
|
53
|
+
this.currentOption =
|
|
54
|
+
!this.multiSelect &&
|
|
55
|
+
requestedCurrentOption &&
|
|
56
|
+
options.allOptions.includes(requestedCurrentOption)
|
|
57
|
+
? requestedCurrentOption
|
|
58
|
+
: undefined;
|
|
59
|
+
let allOptions: string[];
|
|
60
|
+
if (this.multiSelect) {
|
|
61
|
+
allOptions = [...new Set(options.allOptions)].toSorted((left, right) =>
|
|
62
|
+
left.localeCompare(right)
|
|
63
|
+
);
|
|
64
|
+
} else if (this.currentOption) {
|
|
65
|
+
allOptions = [
|
|
66
|
+
this.currentOption,
|
|
67
|
+
...options.allOptions.filter((item) => item !== this.currentOption),
|
|
68
|
+
];
|
|
69
|
+
} else {
|
|
70
|
+
allOptions = [...options.allOptions];
|
|
71
|
+
}
|
|
72
|
+
this.allOptions = allOptions;
|
|
73
|
+
if (this.multiSelect) {
|
|
74
|
+
for (const value of options.currentOptions) {
|
|
75
|
+
if (this.allOptions.includes(value)) {
|
|
76
|
+
this.selected.add(value);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
this.theme = options.theme;
|
|
81
|
+
this.keybindings = options.keybindings;
|
|
82
|
+
this.onSelect = options.onSelect;
|
|
83
|
+
this.onCancel = options.onCancel;
|
|
84
|
+
this.searchInput = new Input();
|
|
85
|
+
this.filteredOptions = this.allOptions;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
invalidate(): void {
|
|
89
|
+
this.searchInput.invalidate();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
render(width: number): string[] {
|
|
93
|
+
const lines: string[] = [this.theme.fg("border", "─".repeat(width))];
|
|
94
|
+
lines.push(` ${this.theme.fg("accent", this.theme.bold(this.title))}`);
|
|
95
|
+
const inputLines = this.searchInput.render(Math.max(1, width - 10));
|
|
96
|
+
lines.push(
|
|
97
|
+
` ${this.theme.fg("accent", "Search: ")}${inputLines[0] || ""}`,
|
|
98
|
+
""
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const query = this.searchInput.getValue().trim();
|
|
102
|
+
this.filteredOptions = query
|
|
103
|
+
? fuzzyFilter(this.allOptions, query, (item) => item)
|
|
104
|
+
: this.allOptions;
|
|
105
|
+
this.selectedIndex = Math.min(
|
|
106
|
+
this.selectedIndex,
|
|
107
|
+
Math.max(0, this.filteredOptions.length - 1)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const maxVisible = 10;
|
|
111
|
+
const total = this.filteredOptions.length;
|
|
112
|
+
if (total === 0) {
|
|
113
|
+
lines.push(` ${this.theme.fg("muted", "No matching models found.")}`);
|
|
114
|
+
} else {
|
|
115
|
+
const startIndex = Math.max(
|
|
116
|
+
0,
|
|
117
|
+
Math.min(
|
|
118
|
+
this.selectedIndex - Math.floor(maxVisible / 2),
|
|
119
|
+
total - maxVisible
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
const endIndex = Math.min(startIndex + maxVisible, total);
|
|
123
|
+
for (let i = startIndex; i < endIndex; i += 1) {
|
|
124
|
+
lines.push(this.renderOption(this.filteredOptions[i], i));
|
|
125
|
+
}
|
|
126
|
+
if (total > maxVisible) {
|
|
127
|
+
lines.push(
|
|
128
|
+
` ${this.theme.fg(
|
|
129
|
+
"muted",
|
|
130
|
+
` (${this.selectedIndex + 1}/${total})`
|
|
131
|
+
)}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
lines.push(
|
|
136
|
+
"",
|
|
137
|
+
` ${this.theme.fg("dim", this.interactionHint())}`,
|
|
138
|
+
this.theme.fg("border", "─".repeat(width))
|
|
139
|
+
);
|
|
140
|
+
return lines.map((line) => truncateToWidth(line, width));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
handleInput(keyData: string): void {
|
|
144
|
+
if (this.matchesAction(keyData, "tui.select.up", "\u001B[A")) {
|
|
145
|
+
this.moveSelection(-1);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (this.matchesAction(keyData, "tui.select.down", "\u001B[B")) {
|
|
149
|
+
this.moveSelection(1);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (this.multiSelect && keyData === " ") {
|
|
153
|
+
const item = this.filteredOptions[this.selectedIndex];
|
|
154
|
+
if (item) {
|
|
155
|
+
if (this.selected.has(item)) {
|
|
156
|
+
this.selected.delete(item);
|
|
157
|
+
} else {
|
|
158
|
+
this.selected.add(item);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
this.tui.requestRender();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (
|
|
165
|
+
this.matchesAction(keyData, "tui.select.confirm", "\n") ||
|
|
166
|
+
keyData === "\r"
|
|
167
|
+
) {
|
|
168
|
+
if (this.multiSelect) {
|
|
169
|
+
this.onSelect(
|
|
170
|
+
this.allOptions.filter((item) => this.selected.has(item))
|
|
171
|
+
);
|
|
172
|
+
} else {
|
|
173
|
+
const selectedOption = this.filteredOptions[this.selectedIndex];
|
|
174
|
+
if (selectedOption !== undefined) {
|
|
175
|
+
this.onSelect([selectedOption]);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (this.matchesAction(keyData, "tui.select.cancel", "\u001B")) {
|
|
181
|
+
this.onCancel();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this.searchInput.handleInput(keyData);
|
|
185
|
+
this.selectedIndex = 0;
|
|
186
|
+
this.tui.requestRender();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private interactionHint(): string {
|
|
190
|
+
return this.multiSelect
|
|
191
|
+
? "Type to search · ↑↓: navigate · Space: toggle · Enter: apply · Esc: cancel"
|
|
192
|
+
: "Type to search · ↑↓: navigate · Enter: select · Esc: cancel";
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private renderOption(item: string, index: number): string {
|
|
196
|
+
let tick = " ";
|
|
197
|
+
if (
|
|
198
|
+
this.multiSelect ? this.selected.has(item) : item === this.currentOption
|
|
199
|
+
) {
|
|
200
|
+
tick = "✓ ";
|
|
201
|
+
}
|
|
202
|
+
if (index === this.selectedIndex) {
|
|
203
|
+
return ` ${this.theme.fg("accent", `→ ${tick}${item}`)}`;
|
|
204
|
+
}
|
|
205
|
+
return ` ${this.theme.fg("text", `${tick}${item}`)}`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private matchesAction(
|
|
209
|
+
keyData: string,
|
|
210
|
+
action: keyof Keybindings,
|
|
211
|
+
fallback: string
|
|
212
|
+
) {
|
|
213
|
+
return this.keybindings.matches(keyData, action) || keyData === fallback;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private moveSelection(direction: -1 | 1) {
|
|
217
|
+
if (this.filteredOptions.length > 0) {
|
|
218
|
+
const lastIndex = this.filteredOptions.length - 1;
|
|
219
|
+
const nextIndex = this.selectedIndex + direction;
|
|
220
|
+
if (nextIndex < 0) {
|
|
221
|
+
this.selectedIndex = lastIndex;
|
|
222
|
+
} else if (nextIndex > lastIndex) {
|
|
223
|
+
this.selectedIndex = 0;
|
|
224
|
+
} else {
|
|
225
|
+
this.selectedIndex = nextIndex;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
this.tui.requestRender();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
2
|
+
|
|
2
3
|
import { DEFAULT_EFFORT_LEVEL } from "../commands/model-options.ts";
|
|
3
4
|
import type { ContextPreset } from "./types.ts";
|
|
4
5
|
|
|
@@ -20,7 +21,7 @@ export const withCurrentValue = (current: string, values: string[]) =>
|
|
|
20
21
|
|
|
21
22
|
export const numericValues = (current: number, values: number[]) => {
|
|
22
23
|
const all = values.includes(current) ? values : [...values, current];
|
|
23
|
-
return all.
|
|
24
|
+
return all.toSorted((a, b) => a - b).map(String);
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
export const maxCallValues = (current: string) => {
|
|
@@ -53,6 +54,22 @@ export const currentContextLabel = (
|
|
|
53
54
|
presets.find((preset) => preset.value === contextMaxChars)?.label ??
|
|
54
55
|
String(contextMaxChars);
|
|
55
56
|
|
|
57
|
+
const closestPresetIndex = (
|
|
58
|
+
presets: ContextPreset[],
|
|
59
|
+
contextMaxChars: number
|
|
60
|
+
): number => {
|
|
61
|
+
let closestIndex = 0;
|
|
62
|
+
for (let index = 0; index < presets.length; index += 1) {
|
|
63
|
+
if (
|
|
64
|
+
Math.abs(presets[index].value - contextMaxChars) <
|
|
65
|
+
Math.abs(presets[closestIndex].value - contextMaxChars)
|
|
66
|
+
) {
|
|
67
|
+
closestIndex = index;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return closestIndex;
|
|
71
|
+
};
|
|
72
|
+
|
|
56
73
|
export const contextDescription = (
|
|
57
74
|
presets: ContextPreset[],
|
|
58
75
|
contextMaxChars: number
|
|
@@ -61,16 +78,9 @@ export const contextDescription = (
|
|
|
61
78
|
(preset) => preset.value === contextMaxChars
|
|
62
79
|
);
|
|
63
80
|
const selectedIndex =
|
|
64
|
-
exactIndex
|
|
65
|
-
?
|
|
66
|
-
:
|
|
67
|
-
(closestIndex, preset, index) =>
|
|
68
|
-
Math.abs(preset.value - contextMaxChars) <
|
|
69
|
-
Math.abs(presets[closestIndex].value - contextMaxChars)
|
|
70
|
-
? index
|
|
71
|
-
: closestIndex,
|
|
72
|
-
0
|
|
73
|
-
);
|
|
81
|
+
exactIndex === -1
|
|
82
|
+
? closestPresetIndex(presets, contextMaxChars)
|
|
83
|
+
: exactIndex;
|
|
74
84
|
const selectedPreset = presets[selectedIndex];
|
|
75
85
|
const isFullContext =
|
|
76
86
|
selectedPreset?.value === Number.MAX_SAFE_INTEGER ||
|
|
@@ -100,7 +110,7 @@ export const contextDescription = (
|
|
|
100
110
|
);
|
|
101
111
|
const markerLabel = `${" ".repeat(labelStart)}${label}`;
|
|
102
112
|
const description =
|
|
103
|
-
exactIndex
|
|
113
|
+
exactIndex === -1 ? "Custom context limit." : selectedPreset?.description;
|
|
104
114
|
return `${description ?? "Custom context limit."}\n${meterPrefix}${meter} full\n${markerLabel}`;
|
|
105
115
|
};
|
|
106
116
|
|
|
@@ -126,8 +136,8 @@ export const rainbowGradient = (text: string, startedAt: number): string => {
|
|
|
126
136
|
const red = Math.round(baseRed + (255 - baseRed) * brightness);
|
|
127
137
|
const green = Math.round(baseGreen + (255 - baseGreen) * brightness);
|
|
128
138
|
const blue = Math.round(baseBlue + (255 - baseBlue) * brightness);
|
|
129
|
-
return `\
|
|
139
|
+
return `\u001B[38;2;${red};${green};${blue}m${character}`;
|
|
130
140
|
})
|
|
131
141
|
.join("")
|
|
132
|
-
.concat("\
|
|
142
|
+
.concat("\u001B[0m");
|
|
133
143
|
};
|