@tyvm/knowhow 0.0.60 → 0.0.62
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/package.json +1 -1
- package/src/agents/setup/setup.ts +9 -2
- package/src/agents/tools/textSearch.ts +4 -1
- package/src/chat/CliChatService.ts +5 -6
- package/src/chat/modules/SystemModule.ts +1 -1
- package/src/clients/anthropic.ts +12 -0
- package/src/config.ts +5 -0
- package/src/plugins/language.ts +4 -0
- package/src/processors/JsonCompressor.ts +496 -0
- package/src/processors/TokenCompressor.ts +194 -125
- package/src/processors/index.ts +1 -0
- package/src/services/Mcp.ts +1 -0
- package/src/services/Tools.ts +5 -3
- package/src/types.ts +1 -0
- package/src/utils/InputQueueManager.ts +119 -95
- package/tests/compressor/bigstring.test.ts +352 -2
- package/tests/compressor/githubjson.txt +1 -0
- package/ts_build/package.json +1 -1
- package/ts_build/src/agents/setup/setup.js +9 -2
- package/ts_build/src/agents/setup/setup.js.map +1 -1
- package/ts_build/src/agents/tools/textSearch.js +2 -1
- package/ts_build/src/agents/tools/textSearch.js.map +1 -1
- package/ts_build/src/chat/CliChatService.d.ts +1 -1
- package/ts_build/src/chat/CliChatService.js +6 -6
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/SystemModule.js +1 -1
- package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
- package/ts_build/src/clients/anthropic.js +12 -0
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/config.js +5 -0
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/plugins/language.js +4 -0
- package/ts_build/src/plugins/language.js.map +1 -1
- package/ts_build/src/processors/JsonCompressor.d.ts +36 -0
- package/ts_build/src/processors/JsonCompressor.js +295 -0
- package/ts_build/src/processors/JsonCompressor.js.map +1 -0
- package/ts_build/src/processors/TokenCompressor.d.ts +23 -5
- package/ts_build/src/processors/TokenCompressor.js +106 -70
- package/ts_build/src/processors/TokenCompressor.js.map +1 -1
- package/ts_build/src/processors/index.d.ts +1 -0
- package/ts_build/src/processors/index.js +3 -1
- package/ts_build/src/processors/index.js.map +1 -1
- package/ts_build/src/services/Mcp.js.map +1 -1
- package/ts_build/src/services/Tools.js +1 -1
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/types.d.ts +1 -0
- package/ts_build/src/types.js +1 -0
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/utils/InputQueueManager.d.ts +4 -1
- package/ts_build/src/utils/InputQueueManager.js +93 -78
- package/ts_build/src/utils/InputQueueManager.js.map +1 -1
- package/ts_build/tests/compressor/bigstring.test.js +209 -0
- package/ts_build/tests/compressor/bigstring.test.js.map +1 -1
|
@@ -12,7 +12,9 @@ type AskOptions = {
|
|
|
12
12
|
|
|
13
13
|
export class InputQueueManager {
|
|
14
14
|
private stack: AskOptions[] = [];
|
|
15
|
-
private
|
|
15
|
+
private static instance: InputQueueManager | null = null;
|
|
16
|
+
private static rl: readline.Interface | null = null;
|
|
17
|
+
private static keypressListenerSetup = false;
|
|
16
18
|
|
|
17
19
|
// We keep one "live" buffer shared across stacked questions
|
|
18
20
|
// (so typing is preserved when questions change)
|
|
@@ -31,6 +33,11 @@ export class InputQueueManager {
|
|
|
31
33
|
// This allows CliChatService to update inputHistory immediately
|
|
32
34
|
private onNewEntry?: OnNewHistoryEntry;
|
|
33
35
|
|
|
36
|
+
constructor() {
|
|
37
|
+
// Store the current instance as the singleton
|
|
38
|
+
InputQueueManager.instance = this;
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
/**
|
|
35
42
|
* Set a callback to be notified when user enters a new history entry.
|
|
36
43
|
* This allows the caller to update their history source immediately.
|
|
@@ -40,9 +47,9 @@ export class InputQueueManager {
|
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
private ensureRl(): readline.Interface {
|
|
43
|
-
if (
|
|
50
|
+
if (InputQueueManager.rl) return InputQueueManager.rl;
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
InputQueueManager.rl = readline.createInterface({
|
|
46
53
|
input: process.stdin,
|
|
47
54
|
output: process.stdout,
|
|
48
55
|
terminal: true,
|
|
@@ -74,7 +81,7 @@ export class InputQueueManager {
|
|
|
74
81
|
});
|
|
75
82
|
|
|
76
83
|
// When user presses Enter, buffer the line for paste detection
|
|
77
|
-
|
|
84
|
+
InputQueueManager.rl.on("line", (line) => {
|
|
78
85
|
const current = this.peek();
|
|
79
86
|
if (!current) return;
|
|
80
87
|
|
|
@@ -84,25 +91,32 @@ export class InputQueueManager {
|
|
|
84
91
|
// Already in paste mode, add to buffer
|
|
85
92
|
this.pasteBuffer.push(line);
|
|
86
93
|
clearTimeout(this.pasteTimeout);
|
|
87
|
-
this.pasteTimeout = setTimeout(
|
|
94
|
+
this.pasteTimeout = setTimeout(
|
|
95
|
+
() => this.flushPasteBuffer(),
|
|
96
|
+
this.PASTE_DELAY_MS
|
|
97
|
+
);
|
|
88
98
|
return;
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
// Start paste detection mode - buffer this line and wait to see if more come
|
|
92
102
|
this.pasteBuffer.push(line);
|
|
93
|
-
this.pasteTimeout = setTimeout(
|
|
103
|
+
this.pasteTimeout = setTimeout(
|
|
104
|
+
() => this.flushPasteBuffer(),
|
|
105
|
+
this.PASTE_DELAY_MS
|
|
106
|
+
);
|
|
94
107
|
});
|
|
95
108
|
|
|
96
|
-
|
|
109
|
+
InputQueueManager.rl.on("close", () => {
|
|
97
110
|
// Flush any remaining paste buffer on close
|
|
98
111
|
if (this.pasteTimeout) {
|
|
99
112
|
clearTimeout(this.pasteTimeout);
|
|
100
113
|
this.flushPasteBuffer();
|
|
114
|
+
this.historyIndex = -1;
|
|
101
115
|
}
|
|
102
116
|
});
|
|
103
117
|
|
|
104
118
|
// Handle Ctrl+C (readline SIGINT)
|
|
105
|
-
|
|
119
|
+
InputQueueManager.rl.on("SIGINT", () => {
|
|
106
120
|
// If there's an active question, cancel it (like Esc)
|
|
107
121
|
if (this.stack.length > 0) {
|
|
108
122
|
const cancelled = this.stack.pop();
|
|
@@ -120,91 +134,98 @@ export class InputQueueManager {
|
|
|
120
134
|
|
|
121
135
|
// Capture keypresses for ESC + history nav while still using readline.
|
|
122
136
|
// Tab is handled by rl completer.
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
this.syncFromReadline();
|
|
143
|
-
|
|
144
|
-
// Any "real typing" should exit history mode
|
|
145
|
-
// (we'll treat left/right as not exiting; you can tweak)
|
|
146
|
-
const exitsHistoryMode =
|
|
147
|
-
key &&
|
|
148
|
-
(key.name === "return" ||
|
|
149
|
-
key.name === "enter" ||
|
|
150
|
-
key.name === "backspace" ||
|
|
151
|
-
(key.sequence &&
|
|
152
|
-
key.sequence.length === 1 &&
|
|
153
|
-
!key.ctrl &&
|
|
154
|
-
!key.meta));
|
|
155
|
-
if (exitsHistoryMode && this.historyIndex !== -1) {
|
|
156
|
-
this.historyIndex = -1;
|
|
157
|
-
this.savedLineBeforeHistory = "";
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Custom Up/Down history navigation using only passed-in history
|
|
161
|
-
if (key?.name === "up") {
|
|
162
|
-
const history = this.getHistory();
|
|
163
|
-
if (history.length === 0) return;
|
|
164
|
-
|
|
165
|
-
if (this.historyIndex === -1) {
|
|
166
|
-
// entering history mode: remember current typed text
|
|
167
|
-
this.savedLineBeforeHistory = this.currentLine;
|
|
137
|
+
// Only set up keypress listener once to avoid multiple handlers
|
|
138
|
+
if (!InputQueueManager.keypressListenerSetup) {
|
|
139
|
+
InputQueueManager.keypressListenerSetup = true;
|
|
140
|
+
readline.emitKeypressEvents(process.stdin);
|
|
141
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
142
|
+
|
|
143
|
+
process.stdin.on("keypress", (_str, key) => {
|
|
144
|
+
// Handle Ctrl+C in raw mode (some terminals deliver this here instead of SIGINT)
|
|
145
|
+
const instance = InputQueueManager.instance;
|
|
146
|
+
if (!instance) return;
|
|
147
|
+
|
|
148
|
+
if (key?.ctrl && key?.name === "c") {
|
|
149
|
+
if (instance.stack.length > 0) {
|
|
150
|
+
const cancelled = instance.stack.pop();
|
|
151
|
+
cancelled?.resolve("");
|
|
152
|
+
instance.currentLine = "";
|
|
153
|
+
}
|
|
154
|
+
instance.close();
|
|
155
|
+
process.exit(0);
|
|
168
156
|
}
|
|
169
157
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
158
|
+
// If RL is closed or nothing to ask, ignore
|
|
159
|
+
if (!InputQueueManager.rl || instance.stack.length === 0) return;
|
|
160
|
+
|
|
161
|
+
// Keep our buffer in sync with readline's live line
|
|
162
|
+
instance.syncFromReadline();
|
|
163
|
+
|
|
164
|
+
// Any "real typing" should exit history mode
|
|
165
|
+
// (we'll treat left/right as not exiting; you can tweak)
|
|
166
|
+
const exitsHistoryMode =
|
|
167
|
+
key &&
|
|
168
|
+
(key.name === "return" ||
|
|
169
|
+
key.name === "enter" ||
|
|
170
|
+
key.name === "backspace" ||
|
|
171
|
+
(key.sequence &&
|
|
172
|
+
key.sequence.length === 1 &&
|
|
173
|
+
!key.ctrl &&
|
|
174
|
+
!key.meta));
|
|
175
|
+
if (exitsHistoryMode && instance.historyIndex !== -1) {
|
|
176
|
+
instance.historyIndex = -1;
|
|
177
|
+
instance.savedLineBeforeHistory = "";
|
|
176
178
|
}
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (key?.name === "down") {
|
|
181
|
-
const history = this.getHistory();
|
|
182
|
-
if (history.length === 0) return;
|
|
183
179
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
180
|
+
// Custom Up/Down history navigation using only passed-in history
|
|
181
|
+
if (key?.name === "up") {
|
|
182
|
+
const history = instance.getHistory();
|
|
183
|
+
if (history.length === 0) return;
|
|
184
|
+
|
|
185
|
+
if (instance.historyIndex === -1) {
|
|
186
|
+
// entering history mode: remember current typed text
|
|
187
|
+
instance.savedLineBeforeHistory = instance.currentLine;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (instance.historyIndex < history.length - 1) {
|
|
191
|
+
instance.historyIndex++;
|
|
192
|
+
const index = history.length - 1 - instance.historyIndex;
|
|
193
|
+
const next = history[index] ?? "";
|
|
194
|
+
instance.replaceLine(next);
|
|
195
|
+
instance.currentLine = next;
|
|
196
|
+
}
|
|
190
197
|
return;
|
|
191
198
|
}
|
|
192
199
|
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
+
if (key?.name === "down") {
|
|
201
|
+
const history = instance.getHistory();
|
|
202
|
+
if (history.length === 0) return;
|
|
203
|
+
|
|
204
|
+
if (instance.historyIndex > 0) {
|
|
205
|
+
instance.historyIndex--;
|
|
206
|
+
const index = history.length - 1 - instance.historyIndex;
|
|
207
|
+
const next = history[index] ?? "";
|
|
208
|
+
instance.replaceLine(next);
|
|
209
|
+
instance.currentLine = next;
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (instance.historyIndex === 0) {
|
|
214
|
+
// leave history mode, restore what user was typing
|
|
215
|
+
instance.historyIndex = -1;
|
|
216
|
+
const restore = instance.savedLineBeforeHistory ?? "";
|
|
217
|
+
instance.savedLineBeforeHistory = "";
|
|
218
|
+
instance.replaceLine(restore);
|
|
219
|
+
instance.currentLine = restore;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
200
223
|
return;
|
|
201
224
|
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
202
227
|
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
return this.rl;
|
|
228
|
+
return InputQueueManager.rl;
|
|
208
229
|
}
|
|
209
230
|
|
|
210
231
|
private flushPasteBuffer(): void {
|
|
@@ -238,6 +259,7 @@ export class InputQueueManager {
|
|
|
238
259
|
async ask(question: string, options: string[] = [], history: string[] = []) {
|
|
239
260
|
return new Promise<string>((resolve) => {
|
|
240
261
|
this.stack.push({ question, options, history, resolve });
|
|
262
|
+
this.historyIndex = -1; // reset history nav when a new question is asked
|
|
241
263
|
|
|
242
264
|
const rl = this.ensureRl();
|
|
243
265
|
|
|
@@ -260,8 +282,8 @@ export class InputQueueManager {
|
|
|
260
282
|
}
|
|
261
283
|
|
|
262
284
|
private syncFromReadline(): void {
|
|
263
|
-
if (!
|
|
264
|
-
this.currentLine = (
|
|
285
|
+
if (!InputQueueManager.rl) return;
|
|
286
|
+
this.currentLine = (InputQueueManager.rl as any).line ?? "";
|
|
265
287
|
}
|
|
266
288
|
|
|
267
289
|
private sanitizeHistoryEntry(value: string): string {
|
|
@@ -290,13 +312,13 @@ export class InputQueueManager {
|
|
|
290
312
|
}
|
|
291
313
|
|
|
292
314
|
private render(): void {
|
|
293
|
-
if (!
|
|
315
|
+
if (!InputQueueManager.rl) return;
|
|
294
316
|
const current = this.peek();
|
|
295
317
|
if (!current) return;
|
|
296
318
|
|
|
297
319
|
// Make prompt be the question (readline manages wrapping/cursor)
|
|
298
|
-
|
|
299
|
-
|
|
320
|
+
InputQueueManager.rl.setPrompt(current.question);
|
|
321
|
+
InputQueueManager.rl.prompt(true);
|
|
300
322
|
}
|
|
301
323
|
|
|
302
324
|
private renderTopOrClose(): void {
|
|
@@ -311,17 +333,17 @@ export class InputQueueManager {
|
|
|
311
333
|
|
|
312
334
|
this.render();
|
|
313
335
|
this.replaceLine(this.currentLine);
|
|
314
|
-
|
|
336
|
+
InputQueueManager.rl?.prompt(true);
|
|
315
337
|
}
|
|
316
338
|
|
|
317
339
|
private replaceLine(next: string): void {
|
|
318
|
-
if (!
|
|
340
|
+
if (!InputQueueManager.rl) return;
|
|
319
341
|
|
|
320
342
|
const safe = this.sanitizeHistoryEntry(next);
|
|
321
343
|
|
|
322
344
|
// Clear current line and write next input without affecting terminal scrollback
|
|
323
|
-
|
|
324
|
-
if (safe)
|
|
345
|
+
InputQueueManager.rl.write(null, { ctrl: true, name: "u" }); // Ctrl+U clears the line
|
|
346
|
+
if (safe) InputQueueManager.rl.write(safe);
|
|
325
347
|
}
|
|
326
348
|
|
|
327
349
|
/**
|
|
@@ -343,9 +365,11 @@ export class InputQueueManager {
|
|
|
343
365
|
}
|
|
344
366
|
|
|
345
367
|
private close(): void {
|
|
346
|
-
if (!
|
|
347
|
-
|
|
348
|
-
|
|
368
|
+
if (!InputQueueManager.rl) return;
|
|
369
|
+
InputQueueManager.rl.close();
|
|
370
|
+
InputQueueManager.rl = null;
|
|
371
|
+
// Note: We don't reset keypressListenerSetup because the listener stays attached to process.stdin
|
|
372
|
+
// and will continue to work for the next readline interface
|
|
349
373
|
|
|
350
374
|
if (process.stdin.isTTY) {
|
|
351
375
|
try {
|