@sheepbun/yips 0.1.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/dist/agent/commands/command-catalog.js +243 -0
- package/dist/agent/commands/commands.js +418 -0
- package/dist/agent/conductor.js +118 -0
- package/dist/agent/context/code-context.js +68 -0
- package/dist/agent/context/memory-store.js +159 -0
- package/dist/agent/context/session-store.js +211 -0
- package/dist/agent/protocol/tool-protocol.js +160 -0
- package/dist/agent/skills/skills.js +327 -0
- package/dist/agent/tools/tool-executor.js +415 -0
- package/dist/agent/tools/tool-safety.js +52 -0
- package/dist/app/index.js +35 -0
- package/dist/app/repl.js +105 -0
- package/dist/app/update-check.js +132 -0
- package/dist/app/version.js +51 -0
- package/dist/code-context.js +68 -0
- package/dist/colors.js +204 -0
- package/dist/command-catalog.js +242 -0
- package/dist/commands.js +350 -0
- package/dist/conductor.js +94 -0
- package/dist/config/config.js +335 -0
- package/dist/config/hooks.js +187 -0
- package/dist/config.js +335 -0
- package/dist/downloader-state.js +302 -0
- package/dist/downloader-ui.js +289 -0
- package/dist/gateway/adapters/discord.js +108 -0
- package/dist/gateway/adapters/formatting.js +96 -0
- package/dist/gateway/adapters/telegram.js +106 -0
- package/dist/gateway/adapters/types.js +2 -0
- package/dist/gateway/adapters/whatsapp.js +124 -0
- package/dist/gateway/auth-policy.js +66 -0
- package/dist/gateway/core.js +87 -0
- package/dist/gateway/headless-conductor.js +328 -0
- package/dist/gateway/message-router.js +23 -0
- package/dist/gateway/rate-limiter.js +48 -0
- package/dist/gateway/runtime/backend-policy.js +18 -0
- package/dist/gateway/runtime/discord-bot.js +104 -0
- package/dist/gateway/runtime/discord-main.js +69 -0
- package/dist/gateway/session-manager.js +77 -0
- package/dist/gateway/types.js +2 -0
- package/dist/hardware.js +92 -0
- package/dist/hooks.js +187 -0
- package/dist/index.js +34 -0
- package/dist/input-engine.js +250 -0
- package/dist/llama-client.js +227 -0
- package/dist/llama-server.js +620 -0
- package/dist/llm/llama-client.js +227 -0
- package/dist/llm/llama-server.js +620 -0
- package/dist/llm/token-counter.js +47 -0
- package/dist/memory-store.js +159 -0
- package/dist/messages.js +59 -0
- package/dist/model-downloader.js +382 -0
- package/dist/model-manager-state.js +118 -0
- package/dist/model-manager-ui.js +194 -0
- package/dist/model-manager.js +190 -0
- package/dist/models/hardware.js +92 -0
- package/dist/models/model-downloader.js +382 -0
- package/dist/models/model-manager.js +190 -0
- package/dist/prompt-box.js +78 -0
- package/dist/prompt-composer.js +498 -0
- package/dist/repl.js +105 -0
- package/dist/session-store.js +211 -0
- package/dist/spinner.js +76 -0
- package/dist/title-box.js +388 -0
- package/dist/token-counter.js +47 -0
- package/dist/tool-executor.js +415 -0
- package/dist/tool-protocol.js +121 -0
- package/dist/tool-safety.js +52 -0
- package/dist/tui/app.js +2553 -0
- package/dist/tui/startup.js +56 -0
- package/dist/tui-input-routing.js +53 -0
- package/dist/tui.js +51 -0
- package/dist/types/app-types.js +2 -0
- package/dist/types.js +2 -0
- package/dist/ui/colors.js +204 -0
- package/dist/ui/downloader/downloader-state.js +302 -0
- package/dist/ui/downloader/downloader-ui.js +289 -0
- package/dist/ui/input/input-engine.js +250 -0
- package/dist/ui/input/tui-input-routing.js +53 -0
- package/dist/ui/input/vt-session.js +168 -0
- package/dist/ui/messages.js +59 -0
- package/dist/ui/model-manager/model-manager-state.js +118 -0
- package/dist/ui/model-manager/model-manager-ui.js +194 -0
- package/dist/ui/prompt/prompt-box.js +78 -0
- package/dist/ui/prompt/prompt-composer.js +498 -0
- package/dist/ui/spinner.js +76 -0
- package/dist/ui/title-box.js +388 -0
- package/dist/ui/tui/app.js +6 -0
- package/dist/ui/tui/autocomplete.js +85 -0
- package/dist/ui/tui/constants.js +18 -0
- package/dist/ui/tui/history.js +29 -0
- package/dist/ui/tui/layout.js +341 -0
- package/dist/ui/tui/runtime-core.js +2584 -0
- package/dist/ui/tui/runtime-utils.js +53 -0
- package/dist/ui/tui/start-tui.js +54 -0
- package/dist/ui/tui/startup.js +56 -0
- package/dist/version.js +51 -0
- package/dist/vt-session.js +168 -0
- package/install.sh +457 -0
- package/package.json +128 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Bounded multiline prompt composer state machine. */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.PromptComposer = void 0;
|
|
5
|
+
exports.buildPromptComposerLayout = buildPromptComposerLayout;
|
|
6
|
+
const DEFAULT_PREFIX = ">>> ";
|
|
7
|
+
function toChars(text) {
|
|
8
|
+
return Array.from(text);
|
|
9
|
+
}
|
|
10
|
+
function charLength(text) {
|
|
11
|
+
return Array.from(text).length;
|
|
12
|
+
}
|
|
13
|
+
function clamp(value, min, max) {
|
|
14
|
+
return Math.max(min, Math.min(max, value));
|
|
15
|
+
}
|
|
16
|
+
function takeLeftChars(text, maxWidth) {
|
|
17
|
+
if (maxWidth <= 0)
|
|
18
|
+
return "";
|
|
19
|
+
const chars = toChars(text);
|
|
20
|
+
return chars.slice(0, maxWidth).join("");
|
|
21
|
+
}
|
|
22
|
+
function rowCapacity(rowIndex, firstRowContentWidth, interiorWidth) {
|
|
23
|
+
return rowIndex === 0 ? firstRowContentWidth : interiorWidth;
|
|
24
|
+
}
|
|
25
|
+
function buildPromptComposerLayout(text, cursor, interiorWidth, prefix = DEFAULT_PREFIX) {
|
|
26
|
+
const safeInteriorWidth = Math.max(0, interiorWidth);
|
|
27
|
+
const visiblePrefix = takeLeftChars(prefix, safeInteriorWidth);
|
|
28
|
+
const firstRowContentWidth = Math.max(0, safeInteriorWidth - charLength(visiblePrefix));
|
|
29
|
+
const textChars = toChars(text);
|
|
30
|
+
const safeCursor = clamp(cursor, 0, textChars.length);
|
|
31
|
+
const rowChars = [[]];
|
|
32
|
+
const rowStarts = [0];
|
|
33
|
+
const rowEnds = [];
|
|
34
|
+
if (safeInteriorWidth > 0) {
|
|
35
|
+
let rowIndex = 0;
|
|
36
|
+
for (let textIndex = 0; textIndex < textChars.length; textIndex++) {
|
|
37
|
+
const char = textChars[textIndex] ?? "";
|
|
38
|
+
if (char === "\n") {
|
|
39
|
+
rowEnds[rowIndex] = textIndex;
|
|
40
|
+
rowIndex += 1;
|
|
41
|
+
rowChars[rowIndex] = [];
|
|
42
|
+
rowStarts[rowIndex] = textIndex + 1;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
let placed = false;
|
|
46
|
+
while (!placed) {
|
|
47
|
+
const capacity = rowCapacity(rowIndex, firstRowContentWidth, safeInteriorWidth);
|
|
48
|
+
if (capacity <= 0) {
|
|
49
|
+
rowEnds[rowIndex] = textIndex;
|
|
50
|
+
rowIndex += 1;
|
|
51
|
+
rowChars[rowIndex] = rowChars[rowIndex] ?? [];
|
|
52
|
+
rowStarts[rowIndex] = rowStarts[rowIndex] ?? textIndex;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if ((rowChars[rowIndex]?.length ?? 0) < capacity) {
|
|
56
|
+
rowChars[rowIndex]?.push(char);
|
|
57
|
+
placed = true;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
rowEnds[rowIndex] = textIndex;
|
|
61
|
+
rowIndex += 1;
|
|
62
|
+
rowChars[rowIndex] = rowChars[rowIndex] ?? [];
|
|
63
|
+
rowStarts[rowIndex] = rowStarts[rowIndex] ?? textIndex;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
rowEnds[rowIndex] = textChars.length;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
for (let textIndex = 0; textIndex < textChars.length; textIndex++) {
|
|
70
|
+
if (textChars[textIndex] !== "\n")
|
|
71
|
+
continue;
|
|
72
|
+
rowEnds[rowStarts.length - 1] = textIndex;
|
|
73
|
+
rowStarts.push(textIndex + 1);
|
|
74
|
+
rowChars.push([]);
|
|
75
|
+
}
|
|
76
|
+
rowEnds[rowStarts.length - 1] = textChars.length;
|
|
77
|
+
}
|
|
78
|
+
const rows = rowChars.map((chars) => chars.join(""));
|
|
79
|
+
if (rows.length === 0)
|
|
80
|
+
rows.push("");
|
|
81
|
+
let cursorRow = rows.length - 1;
|
|
82
|
+
let cursorCol = charLength(rows[cursorRow] ?? "");
|
|
83
|
+
for (let i = 0; i < rows.length; i++) {
|
|
84
|
+
const rowStart = rowStarts[i] ?? 0;
|
|
85
|
+
const rowEnd = rowEnds[i] ?? 0;
|
|
86
|
+
if (safeCursor >= rowStart && safeCursor <= rowEnd) {
|
|
87
|
+
cursorRow = i;
|
|
88
|
+
cursorCol = safeCursor - rowStart;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
text,
|
|
94
|
+
cursor: safeCursor,
|
|
95
|
+
rows,
|
|
96
|
+
rowStarts,
|
|
97
|
+
rowEnds,
|
|
98
|
+
rowCount: rows.length,
|
|
99
|
+
cursorRow,
|
|
100
|
+
cursorCol,
|
|
101
|
+
interiorWidth: safeInteriorWidth,
|
|
102
|
+
prefix: visiblePrefix,
|
|
103
|
+
firstRowContentWidth
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function textFromChars(chars) {
|
|
107
|
+
return chars.join("");
|
|
108
|
+
}
|
|
109
|
+
function whitespace(char) {
|
|
110
|
+
return char === undefined || /\s/.test(char);
|
|
111
|
+
}
|
|
112
|
+
function noneEvent() {
|
|
113
|
+
return { type: "none" };
|
|
114
|
+
}
|
|
115
|
+
function menuStateKey(token, options) {
|
|
116
|
+
return `${token}\u0000${options.join("\u0001")}`;
|
|
117
|
+
}
|
|
118
|
+
class PromptComposer {
|
|
119
|
+
chars;
|
|
120
|
+
cursor;
|
|
121
|
+
prefix;
|
|
122
|
+
interiorWidth;
|
|
123
|
+
history;
|
|
124
|
+
commandAutoComplete;
|
|
125
|
+
modelAutoComplete;
|
|
126
|
+
historyIndex;
|
|
127
|
+
historyDraft;
|
|
128
|
+
preferredColumn;
|
|
129
|
+
layout;
|
|
130
|
+
autocompleteSelection;
|
|
131
|
+
suppressAutocompleteMenu;
|
|
132
|
+
constructor(options) {
|
|
133
|
+
const initialText = options.text ?? "";
|
|
134
|
+
this.chars = toChars(initialText);
|
|
135
|
+
this.cursor = clamp(options.cursor ?? this.chars.length, 0, this.chars.length);
|
|
136
|
+
this.prefix = options.prefix ?? DEFAULT_PREFIX;
|
|
137
|
+
this.interiorWidth = Math.max(0, options.interiorWidth);
|
|
138
|
+
this.history = options.history;
|
|
139
|
+
this.commandAutoComplete = options.commandAutoComplete ?? options.autoComplete ?? [];
|
|
140
|
+
this.modelAutoComplete = options.modelAutoComplete ?? [];
|
|
141
|
+
this.historyIndex = this.history.length;
|
|
142
|
+
this.historyDraft = initialText;
|
|
143
|
+
this.preferredColumn = null;
|
|
144
|
+
this.autocompleteSelection = null;
|
|
145
|
+
this.suppressAutocompleteMenu = false;
|
|
146
|
+
this.layout = buildPromptComposerLayout(textFromChars(this.chars), this.cursor, this.interiorWidth, this.prefix);
|
|
147
|
+
}
|
|
148
|
+
getText() {
|
|
149
|
+
return textFromChars(this.chars);
|
|
150
|
+
}
|
|
151
|
+
getCursor() {
|
|
152
|
+
return this.cursor;
|
|
153
|
+
}
|
|
154
|
+
getLayout() {
|
|
155
|
+
return this.layout;
|
|
156
|
+
}
|
|
157
|
+
setInteriorWidth(width) {
|
|
158
|
+
this.interiorWidth = Math.max(0, width);
|
|
159
|
+
this.recomputeLayout();
|
|
160
|
+
}
|
|
161
|
+
setModelAutocompleteCandidates(candidates) {
|
|
162
|
+
this.modelAutoComplete = [...candidates];
|
|
163
|
+
this.markAutocompleteDirty();
|
|
164
|
+
}
|
|
165
|
+
applyAutocompleteChoice(tokenStart, tokenEnd, selection) {
|
|
166
|
+
this.replaceRange(tokenStart, tokenEnd, selection);
|
|
167
|
+
}
|
|
168
|
+
clearAutocompleteSelection() {
|
|
169
|
+
this.autocompleteSelection = null;
|
|
170
|
+
this.suppressAutocompleteMenu = false;
|
|
171
|
+
}
|
|
172
|
+
moveAutocompleteSelection(direction) {
|
|
173
|
+
const menu = this.getAutocompleteMenuState();
|
|
174
|
+
if (!menu || menu.options.length === 0) {
|
|
175
|
+
return noneEvent();
|
|
176
|
+
}
|
|
177
|
+
const nextIndex = (menu.selectedIndex + direction + menu.options.length) % menu.options.length;
|
|
178
|
+
this.autocompleteSelection = {
|
|
179
|
+
key: menuStateKey(menu.token, menu.options),
|
|
180
|
+
selectedIndex: nextIndex
|
|
181
|
+
};
|
|
182
|
+
return noneEvent();
|
|
183
|
+
}
|
|
184
|
+
acceptAutocompleteSelection() {
|
|
185
|
+
const menu = this.getAutocompleteMenuState();
|
|
186
|
+
if (!menu) {
|
|
187
|
+
return noneEvent();
|
|
188
|
+
}
|
|
189
|
+
const selection = menu.options[menu.selectedIndex] ?? menu.token;
|
|
190
|
+
this.applyAutocompleteChoice(menu.tokenStart, menu.tokenEnd, selection);
|
|
191
|
+
this.autocompleteSelection = null;
|
|
192
|
+
this.suppressAutocompleteMenu = true;
|
|
193
|
+
return noneEvent();
|
|
194
|
+
}
|
|
195
|
+
getAutocompleteMenuState() {
|
|
196
|
+
if (this.suppressAutocompleteMenu) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
const suggestions = this.computeAutocompleteSuggestions();
|
|
200
|
+
if (!suggestions) {
|
|
201
|
+
this.autocompleteSelection = null;
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
const key = menuStateKey(suggestions.token, suggestions.options);
|
|
205
|
+
if (!this.autocompleteSelection || this.autocompleteSelection.key !== key) {
|
|
206
|
+
this.autocompleteSelection = { key, selectedIndex: 0 };
|
|
207
|
+
}
|
|
208
|
+
const maxIndex = Math.max(0, suggestions.options.length - 1);
|
|
209
|
+
const selectedIndex = clamp(this.autocompleteSelection.selectedIndex, 0, maxIndex);
|
|
210
|
+
this.autocompleteSelection = { key, selectedIndex };
|
|
211
|
+
return {
|
|
212
|
+
...suggestions,
|
|
213
|
+
selectedIndex
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
getAutocompleteSuggestions() {
|
|
217
|
+
const menu = this.getAutocompleteMenuState();
|
|
218
|
+
if (!menu) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
token: menu.token,
|
|
223
|
+
tokenStart: menu.tokenStart,
|
|
224
|
+
tokenEnd: menu.tokenEnd,
|
|
225
|
+
options: [...menu.options]
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
handleKey(key, data) {
|
|
229
|
+
if (data?.isCharacter) {
|
|
230
|
+
this.detachFromHistory();
|
|
231
|
+
this.preferredColumn = null;
|
|
232
|
+
this.insertText(key);
|
|
233
|
+
return noneEvent();
|
|
234
|
+
}
|
|
235
|
+
switch (key) {
|
|
236
|
+
case "ENTER":
|
|
237
|
+
case "KP_ENTER":
|
|
238
|
+
case "CTRL_M":
|
|
239
|
+
return { type: "submit", value: this.getText() };
|
|
240
|
+
case "CTRL_ENTER":
|
|
241
|
+
this.detachFromHistory();
|
|
242
|
+
this.preferredColumn = null;
|
|
243
|
+
this.insertText("\n");
|
|
244
|
+
return noneEvent();
|
|
245
|
+
case "ESCAPE":
|
|
246
|
+
return { type: "cancel" };
|
|
247
|
+
case "BACKSPACE":
|
|
248
|
+
this.detachFromHistory();
|
|
249
|
+
this.preferredColumn = null;
|
|
250
|
+
this.backspace();
|
|
251
|
+
return noneEvent();
|
|
252
|
+
case "DELETE":
|
|
253
|
+
this.detachFromHistory();
|
|
254
|
+
this.preferredColumn = null;
|
|
255
|
+
this.deleteForward();
|
|
256
|
+
return noneEvent();
|
|
257
|
+
case "LEFT":
|
|
258
|
+
this.preferredColumn = null;
|
|
259
|
+
this.moveCursor(-1);
|
|
260
|
+
return noneEvent();
|
|
261
|
+
case "RIGHT":
|
|
262
|
+
this.preferredColumn = null;
|
|
263
|
+
this.moveCursor(1);
|
|
264
|
+
return noneEvent();
|
|
265
|
+
case "HOME":
|
|
266
|
+
this.preferredColumn = null;
|
|
267
|
+
this.moveToLineBoundary("start");
|
|
268
|
+
return noneEvent();
|
|
269
|
+
case "END":
|
|
270
|
+
this.preferredColumn = null;
|
|
271
|
+
this.moveToLineBoundary("end");
|
|
272
|
+
return noneEvent();
|
|
273
|
+
case "UP":
|
|
274
|
+
if (this.moveVertical(-1))
|
|
275
|
+
return noneEvent();
|
|
276
|
+
this.historyPrevious();
|
|
277
|
+
return noneEvent();
|
|
278
|
+
case "DOWN":
|
|
279
|
+
if (this.moveVertical(1))
|
|
280
|
+
return noneEvent();
|
|
281
|
+
this.historyNext();
|
|
282
|
+
return noneEvent();
|
|
283
|
+
case "TAB":
|
|
284
|
+
return this.handleAutoComplete();
|
|
285
|
+
default:
|
|
286
|
+
return noneEvent();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
recomputeLayout() {
|
|
290
|
+
this.layout = buildPromptComposerLayout(textFromChars(this.chars), this.cursor, this.interiorWidth, this.prefix);
|
|
291
|
+
}
|
|
292
|
+
detachFromHistory() {
|
|
293
|
+
if (this.historyIndex === this.history.length)
|
|
294
|
+
return;
|
|
295
|
+
this.historyIndex = this.history.length;
|
|
296
|
+
this.historyDraft = this.getText();
|
|
297
|
+
}
|
|
298
|
+
insertText(text) {
|
|
299
|
+
const insert = toChars(text);
|
|
300
|
+
if (insert.length === 0)
|
|
301
|
+
return;
|
|
302
|
+
this.markAutocompleteDirty();
|
|
303
|
+
this.chars.splice(this.cursor, 0, ...insert);
|
|
304
|
+
this.cursor += insert.length;
|
|
305
|
+
this.recomputeLayout();
|
|
306
|
+
}
|
|
307
|
+
replaceRange(start, end, replacement) {
|
|
308
|
+
const safeStart = clamp(start, 0, this.chars.length);
|
|
309
|
+
const safeEnd = clamp(end, safeStart, this.chars.length);
|
|
310
|
+
const replacementChars = toChars(replacement);
|
|
311
|
+
this.detachFromHistory();
|
|
312
|
+
this.markAutocompleteDirty();
|
|
313
|
+
this.preferredColumn = null;
|
|
314
|
+
this.chars.splice(safeStart, safeEnd - safeStart, ...replacementChars);
|
|
315
|
+
this.cursor = safeStart + replacementChars.length;
|
|
316
|
+
this.recomputeLayout();
|
|
317
|
+
}
|
|
318
|
+
backspace() {
|
|
319
|
+
if (this.cursor <= 0)
|
|
320
|
+
return;
|
|
321
|
+
this.markAutocompleteDirty();
|
|
322
|
+
this.chars.splice(this.cursor - 1, 1);
|
|
323
|
+
this.cursor -= 1;
|
|
324
|
+
this.recomputeLayout();
|
|
325
|
+
}
|
|
326
|
+
deleteForward() {
|
|
327
|
+
if (this.cursor >= this.chars.length)
|
|
328
|
+
return;
|
|
329
|
+
this.markAutocompleteDirty();
|
|
330
|
+
this.chars.splice(this.cursor, 1);
|
|
331
|
+
this.recomputeLayout();
|
|
332
|
+
}
|
|
333
|
+
moveCursor(delta) {
|
|
334
|
+
this.markAutocompleteDirty();
|
|
335
|
+
this.cursor = clamp(this.cursor + delta, 0, this.chars.length);
|
|
336
|
+
this.recomputeLayout();
|
|
337
|
+
}
|
|
338
|
+
moveToLineBoundary(target) {
|
|
339
|
+
this.markAutocompleteDirty();
|
|
340
|
+
const row = this.layout.cursorRow;
|
|
341
|
+
if (target === "start") {
|
|
342
|
+
this.cursor = this.layout.rowStarts[row] ?? this.cursor;
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
this.cursor = this.layout.rowEnds[row] ?? this.cursor;
|
|
346
|
+
}
|
|
347
|
+
this.recomputeLayout();
|
|
348
|
+
}
|
|
349
|
+
moveVertical(direction) {
|
|
350
|
+
const targetRow = this.layout.cursorRow + direction;
|
|
351
|
+
if (targetRow < 0 || targetRow >= this.layout.rowCount) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
this.markAutocompleteDirty();
|
|
355
|
+
const preferred = this.preferredColumn ?? this.layout.cursorCol;
|
|
356
|
+
const targetRowLen = charLength(this.layout.rows[targetRow] ?? "");
|
|
357
|
+
const targetCol = Math.min(preferred, targetRowLen);
|
|
358
|
+
const targetStart = this.layout.rowStarts[targetRow] ?? 0;
|
|
359
|
+
this.cursor = targetStart + targetCol;
|
|
360
|
+
this.preferredColumn = preferred;
|
|
361
|
+
this.recomputeLayout();
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
historyPrevious() {
|
|
365
|
+
if (this.history.length === 0 || this.historyIndex <= 0)
|
|
366
|
+
return;
|
|
367
|
+
if (this.historyIndex === this.history.length) {
|
|
368
|
+
this.historyDraft = this.getText();
|
|
369
|
+
}
|
|
370
|
+
this.historyIndex -= 1;
|
|
371
|
+
this.loadHistoryValue(this.history[this.historyIndex] ?? "");
|
|
372
|
+
}
|
|
373
|
+
historyNext() {
|
|
374
|
+
if (this.history.length === 0 || this.historyIndex >= this.history.length)
|
|
375
|
+
return;
|
|
376
|
+
this.historyIndex += 1;
|
|
377
|
+
if (this.historyIndex === this.history.length) {
|
|
378
|
+
this.loadHistoryValue(this.historyDraft);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
this.loadHistoryValue(this.history[this.historyIndex] ?? "");
|
|
382
|
+
}
|
|
383
|
+
loadHistoryValue(value) {
|
|
384
|
+
this.markAutocompleteDirty();
|
|
385
|
+
this.chars = toChars(value);
|
|
386
|
+
this.cursor = this.chars.length;
|
|
387
|
+
this.preferredColumn = null;
|
|
388
|
+
this.recomputeLayout();
|
|
389
|
+
}
|
|
390
|
+
markAutocompleteDirty() {
|
|
391
|
+
this.autocompleteSelection = null;
|
|
392
|
+
this.suppressAutocompleteMenu = false;
|
|
393
|
+
}
|
|
394
|
+
tokenBounds() {
|
|
395
|
+
let start = this.cursor;
|
|
396
|
+
while (start > 0 && !whitespace(this.chars[start - 1])) {
|
|
397
|
+
start -= 1;
|
|
398
|
+
}
|
|
399
|
+
let end = this.cursor;
|
|
400
|
+
while (end < this.chars.length && !whitespace(this.chars[end])) {
|
|
401
|
+
end += 1;
|
|
402
|
+
}
|
|
403
|
+
const token = this.chars.slice(start, end).join("");
|
|
404
|
+
return { start, end, token };
|
|
405
|
+
}
|
|
406
|
+
tokenSpans() {
|
|
407
|
+
const spans = [];
|
|
408
|
+
let index = 0;
|
|
409
|
+
while (index < this.chars.length) {
|
|
410
|
+
while (index < this.chars.length && whitespace(this.chars[index])) {
|
|
411
|
+
index += 1;
|
|
412
|
+
}
|
|
413
|
+
if (index >= this.chars.length) {
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
const start = index;
|
|
417
|
+
while (index < this.chars.length && !whitespace(this.chars[index])) {
|
|
418
|
+
index += 1;
|
|
419
|
+
}
|
|
420
|
+
const end = index;
|
|
421
|
+
spans.push({
|
|
422
|
+
start,
|
|
423
|
+
end,
|
|
424
|
+
token: this.chars.slice(start, end).join("")
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
return spans;
|
|
428
|
+
}
|
|
429
|
+
currentTokenIndex(bounds) {
|
|
430
|
+
const spans = this.tokenSpans();
|
|
431
|
+
if (bounds.token.length > 0) {
|
|
432
|
+
const index = spans.findIndex((span) => this.cursor >= span.start && this.cursor <= span.end);
|
|
433
|
+
if (index >= 0) {
|
|
434
|
+
return index;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return spans.filter((span) => span.end <= bounds.start).length;
|
|
438
|
+
}
|
|
439
|
+
firstToken() {
|
|
440
|
+
const spans = this.tokenSpans();
|
|
441
|
+
return spans[0]?.token ?? "";
|
|
442
|
+
}
|
|
443
|
+
modelOptionsForToken(token) {
|
|
444
|
+
const needle = token.trim().toLowerCase();
|
|
445
|
+
const options = [];
|
|
446
|
+
const seen = new Set();
|
|
447
|
+
for (const candidate of this.modelAutoComplete) {
|
|
448
|
+
const value = candidate.value.trim();
|
|
449
|
+
if (value.length === 0 || seen.has(value)) {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
const aliases = [value, ...candidate.aliases].map((entry) => entry.trim().toLowerCase());
|
|
453
|
+
const matches = needle.length === 0 || aliases.some((alias) => alias.length > 0 && alias.startsWith(needle));
|
|
454
|
+
if (!matches) {
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
seen.add(value);
|
|
458
|
+
options.push(value);
|
|
459
|
+
}
|
|
460
|
+
return options;
|
|
461
|
+
}
|
|
462
|
+
computeAutocompleteSuggestions() {
|
|
463
|
+
const bounds = this.tokenBounds();
|
|
464
|
+
const firstToken = this.firstToken().toLowerCase();
|
|
465
|
+
const tokenIndex = this.currentTokenIndex(bounds);
|
|
466
|
+
if (bounds.token.startsWith("/")) {
|
|
467
|
+
const options = this.commandAutoComplete.filter((candidate) => candidate.startsWith(bounds.token));
|
|
468
|
+
if (options.length === 0) {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
return {
|
|
472
|
+
token: bounds.token,
|
|
473
|
+
tokenStart: bounds.start,
|
|
474
|
+
tokenEnd: bounds.end,
|
|
475
|
+
options: [...options]
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
const isModelArg = firstToken === "/model" && tokenIndex === 1;
|
|
479
|
+
const isNickTargetArg = firstToken === "/nick" && tokenIndex === 1;
|
|
480
|
+
if (!isModelArg && !isNickTargetArg) {
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
const options = this.modelOptionsForToken(bounds.token);
|
|
484
|
+
if (options.length === 0) {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
return {
|
|
488
|
+
token: bounds.token,
|
|
489
|
+
tokenStart: bounds.start,
|
|
490
|
+
tokenEnd: bounds.end,
|
|
491
|
+
options
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
handleAutoComplete() {
|
|
495
|
+
return noneEvent();
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
exports.PromptComposer = PromptComposer;
|
package/dist/repl.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderHelpText = renderHelpText;
|
|
4
|
+
exports.handleInput = handleInput;
|
|
5
|
+
exports.applyAction = applyAction;
|
|
6
|
+
exports.startRepl = startRepl;
|
|
7
|
+
const node_process_1 = require("node:process");
|
|
8
|
+
const promises_1 = require("node:readline/promises");
|
|
9
|
+
function renderHelpText() {
|
|
10
|
+
return [
|
|
11
|
+
"Available commands:",
|
|
12
|
+
" /help - Show this help",
|
|
13
|
+
" /exit - Exit the REPL",
|
|
14
|
+
" /restart - Restart Yips",
|
|
15
|
+
" /quit - Exit the REPL"
|
|
16
|
+
].join("\n");
|
|
17
|
+
}
|
|
18
|
+
function handleInput(input, state) {
|
|
19
|
+
if (!state.running) {
|
|
20
|
+
return { type: "exit" };
|
|
21
|
+
}
|
|
22
|
+
const trimmed = input.trim();
|
|
23
|
+
if (trimmed.startsWith("/")) {
|
|
24
|
+
const [commandToken = ""] = trimmed.split(/\s+/, 1);
|
|
25
|
+
const normalizedCommand = commandToken.toLowerCase();
|
|
26
|
+
if (normalizedCommand === "/help") {
|
|
27
|
+
return { type: "help" };
|
|
28
|
+
}
|
|
29
|
+
if (normalizedCommand === "/exit" || normalizedCommand === "/quit") {
|
|
30
|
+
return { type: "exit" };
|
|
31
|
+
}
|
|
32
|
+
if (normalizedCommand === "/restart") {
|
|
33
|
+
return { type: "restart" };
|
|
34
|
+
}
|
|
35
|
+
return { type: "unknown", command: normalizedCommand.slice(1) };
|
|
36
|
+
}
|
|
37
|
+
return { type: "echo", text: input };
|
|
38
|
+
}
|
|
39
|
+
function streamIsTTY(stream) {
|
|
40
|
+
if (typeof stream !== "object" || stream === null || !("isTTY" in stream)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return stream.isTTY === true;
|
|
44
|
+
}
|
|
45
|
+
function writeLine(output, message) {
|
|
46
|
+
output.write(`${message}\n`);
|
|
47
|
+
}
|
|
48
|
+
function assertNever(value) {
|
|
49
|
+
throw new Error(`Unexpected action: ${JSON.stringify(value)}`);
|
|
50
|
+
}
|
|
51
|
+
function applyAction(action, state, output) {
|
|
52
|
+
switch (action.type) {
|
|
53
|
+
case "help":
|
|
54
|
+
writeLine(output, renderHelpText());
|
|
55
|
+
return;
|
|
56
|
+
case "exit":
|
|
57
|
+
state.running = false;
|
|
58
|
+
writeLine(output, "Goodbye.");
|
|
59
|
+
return;
|
|
60
|
+
case "restart":
|
|
61
|
+
state.running = false;
|
|
62
|
+
writeLine(output, "Restarting Yips.");
|
|
63
|
+
return;
|
|
64
|
+
case "echo":
|
|
65
|
+
if (action.text.trim().length > 0) {
|
|
66
|
+
state.messageCount += 1;
|
|
67
|
+
writeLine(output, `Yips: ${action.text}`);
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
case "unknown":
|
|
71
|
+
writeLine(output, `Unknown command: /${action.command}. Type /help for help.`);
|
|
72
|
+
return;
|
|
73
|
+
default:
|
|
74
|
+
assertNever(action);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function startRepl(options) {
|
|
78
|
+
const input = options.input ?? node_process_1.stdin;
|
|
79
|
+
const output = options.output ?? node_process_1.stdout;
|
|
80
|
+
const prompt = options.prompt ?? "> ";
|
|
81
|
+
const terminal = streamIsTTY(input) && streamIsTTY(output);
|
|
82
|
+
const state = {
|
|
83
|
+
messageCount: 0,
|
|
84
|
+
running: true,
|
|
85
|
+
config: options.config
|
|
86
|
+
};
|
|
87
|
+
writeLine(output, "Yips Milestone 0 REPL");
|
|
88
|
+
writeLine(output, "Type /help for commands.");
|
|
89
|
+
const readline = (0, promises_1.createInterface)({ input, output, terminal });
|
|
90
|
+
let restartRequested = false;
|
|
91
|
+
try {
|
|
92
|
+
while (state.running) {
|
|
93
|
+
const inputLine = await readline.question(prompt);
|
|
94
|
+
const action = handleInput(inputLine, state);
|
|
95
|
+
if (action.type === "restart") {
|
|
96
|
+
restartRequested = true;
|
|
97
|
+
}
|
|
98
|
+
applyAction(action, state, output);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
readline.close();
|
|
103
|
+
}
|
|
104
|
+
return restartRequested ? "restart" : "exit";
|
|
105
|
+
}
|