@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.
Files changed (53) hide show
  1. package/package.json +1 -1
  2. package/src/agents/setup/setup.ts +9 -2
  3. package/src/agents/tools/textSearch.ts +4 -1
  4. package/src/chat/CliChatService.ts +5 -6
  5. package/src/chat/modules/SystemModule.ts +1 -1
  6. package/src/clients/anthropic.ts +12 -0
  7. package/src/config.ts +5 -0
  8. package/src/plugins/language.ts +4 -0
  9. package/src/processors/JsonCompressor.ts +496 -0
  10. package/src/processors/TokenCompressor.ts +194 -125
  11. package/src/processors/index.ts +1 -0
  12. package/src/services/Mcp.ts +1 -0
  13. package/src/services/Tools.ts +5 -3
  14. package/src/types.ts +1 -0
  15. package/src/utils/InputQueueManager.ts +119 -95
  16. package/tests/compressor/bigstring.test.ts +352 -2
  17. package/tests/compressor/githubjson.txt +1 -0
  18. package/ts_build/package.json +1 -1
  19. package/ts_build/src/agents/setup/setup.js +9 -2
  20. package/ts_build/src/agents/setup/setup.js.map +1 -1
  21. package/ts_build/src/agents/tools/textSearch.js +2 -1
  22. package/ts_build/src/agents/tools/textSearch.js.map +1 -1
  23. package/ts_build/src/chat/CliChatService.d.ts +1 -1
  24. package/ts_build/src/chat/CliChatService.js +6 -6
  25. package/ts_build/src/chat/CliChatService.js.map +1 -1
  26. package/ts_build/src/chat/modules/SystemModule.js +1 -1
  27. package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
  28. package/ts_build/src/clients/anthropic.js +12 -0
  29. package/ts_build/src/clients/anthropic.js.map +1 -1
  30. package/ts_build/src/config.js +5 -0
  31. package/ts_build/src/config.js.map +1 -1
  32. package/ts_build/src/plugins/language.js +4 -0
  33. package/ts_build/src/plugins/language.js.map +1 -1
  34. package/ts_build/src/processors/JsonCompressor.d.ts +36 -0
  35. package/ts_build/src/processors/JsonCompressor.js +295 -0
  36. package/ts_build/src/processors/JsonCompressor.js.map +1 -0
  37. package/ts_build/src/processors/TokenCompressor.d.ts +23 -5
  38. package/ts_build/src/processors/TokenCompressor.js +106 -70
  39. package/ts_build/src/processors/TokenCompressor.js.map +1 -1
  40. package/ts_build/src/processors/index.d.ts +1 -0
  41. package/ts_build/src/processors/index.js +3 -1
  42. package/ts_build/src/processors/index.js.map +1 -1
  43. package/ts_build/src/services/Mcp.js.map +1 -1
  44. package/ts_build/src/services/Tools.js +1 -1
  45. package/ts_build/src/services/Tools.js.map +1 -1
  46. package/ts_build/src/types.d.ts +1 -0
  47. package/ts_build/src/types.js +1 -0
  48. package/ts_build/src/types.js.map +1 -1
  49. package/ts_build/src/utils/InputQueueManager.d.ts +4 -1
  50. package/ts_build/src/utils/InputQueueManager.js +93 -78
  51. package/ts_build/src/utils/InputQueueManager.js.map +1 -1
  52. package/ts_build/tests/compressor/bigstring.test.js +209 -0
  53. 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 rl: readline.Interface | null = null;
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 (this.rl) return this.rl;
50
+ if (InputQueueManager.rl) return InputQueueManager.rl;
44
51
 
45
- this.rl = readline.createInterface({
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
- this.rl.on("line", (line) => {
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(() => this.flushPasteBuffer(), this.PASTE_DELAY_MS);
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(() => this.flushPasteBuffer(), this.PASTE_DELAY_MS);
103
+ this.pasteTimeout = setTimeout(
104
+ () => this.flushPasteBuffer(),
105
+ this.PASTE_DELAY_MS
106
+ );
94
107
  });
95
108
 
96
- this.rl.on("close", () => {
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
- this.rl.on("SIGINT", () => {
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
- readline.emitKeypressEvents(process.stdin);
124
- if (process.stdin.isTTY) process.stdin.setRawMode(true);
125
-
126
- process.stdin.on("keypress", (_str, key) => {
127
- // Handle Ctrl+C in raw mode (some terminals deliver this here instead of SIGINT)
128
- if (key?.ctrl && key?.name === "c") {
129
- if (this.stack.length > 0) {
130
- const cancelled = this.stack.pop();
131
- cancelled?.resolve("");
132
- this.currentLine = "";
133
- }
134
- this.close();
135
- process.exit(0);
136
- }
137
-
138
- // If RL is closed or nothing to ask, ignore
139
- if (!this.rl || this.stack.length === 0) return;
140
-
141
- // Keep our buffer in sync with readline's live line
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
- if (this.historyIndex < history.length - 1) {
171
- this.historyIndex++;
172
- const next =
173
- history[history.length - 1 - this.historyIndex] ?? "";
174
- this.replaceLine(next);
175
- this.currentLine = next;
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
- if (this.historyIndex > 0) {
185
- this.historyIndex--;
186
- const next =
187
- history[history.length - 1 - this.historyIndex] ?? "";
188
- this.replaceLine(next);
189
- this.currentLine = next;
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 (this.historyIndex === 0) {
194
- // leave history mode, restore what user was typing
195
- this.historyIndex = -1;
196
- const restore = this.savedLineBeforeHistory ?? "";
197
- this.savedLineBeforeHistory = "";
198
- this.replaceLine(restore);
199
- this.currentLine = restore;
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
- return;
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 (!this.rl) return;
264
- this.currentLine = (this.rl as any).line ?? "";
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 (!this.rl) return;
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
- this.rl.setPrompt(current.question);
299
- this.rl.prompt(true);
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
- this.rl?.prompt(true);
336
+ InputQueueManager.rl?.prompt(true);
315
337
  }
316
338
 
317
339
  private replaceLine(next: string): void {
318
- if (!this.rl) return;
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
- this.rl.write(null, { ctrl: true, name: "u" }); // Ctrl+U clears the line
324
- if (safe) this.rl.write(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 (!this.rl) return;
347
- this.rl.close();
348
- this.rl = null;
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 {