@gagik.co/snippet-agent 0.1.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.
Files changed (61) hide show
  1. package/.eslintrc.js +13 -0
  2. package/.prettierrc.json +1 -0
  3. package/README.md +23 -0
  4. package/dist/agent-class.d.ts +47 -0
  5. package/dist/agent-class.js +314 -0
  6. package/dist/agent.d.ts +1 -0
  7. package/dist/agent.js +392 -0
  8. package/dist/banner.d.ts +1 -0
  9. package/dist/banner.js +23 -0
  10. package/dist/confirmation-extension.d.ts +10 -0
  11. package/dist/confirmation-extension.js +213 -0
  12. package/dist/index.d.ts +3 -0
  13. package/dist/index.js +141 -0
  14. package/dist/mongosh-interactive-mode.d.ts +33 -0
  15. package/dist/mongosh-interactive-mode.js +244 -0
  16. package/dist/project-agent.d.ts +1 -0
  17. package/dist/project-agent.js +36 -0
  18. package/dist/shell-context.d.ts +17 -0
  19. package/dist/shell-context.js +75 -0
  20. package/dist/skills-loader.d.ts +2 -0
  21. package/dist/skills-loader.js +69 -0
  22. package/dist/src/index.d.ts +1 -0
  23. package/dist/src/index.js +8 -0
  24. package/dist/src/project-agent.d.ts +1 -0
  25. package/dist/src/project-agent.js +36 -0
  26. package/dist/stdout-patcher.d.ts +5 -0
  27. package/dist/stdout-patcher.js +41 -0
  28. package/dist/tools/index.d.ts +4 -0
  29. package/dist/tools/index.js +7 -0
  30. package/dist/tools/mongosh-eval.d.ts +7 -0
  31. package/dist/tools/mongosh-eval.js +84 -0
  32. package/dist/tools/search-docs.d.ts +2 -0
  33. package/dist/tools/search-docs.js +106 -0
  34. package/dist/tools/types.d.ts +12 -0
  35. package/dist/tools/types.js +2 -0
  36. package/dist/tools.d.ts +7 -0
  37. package/dist/tools.js +189 -0
  38. package/dist/types.d.ts +21 -0
  39. package/dist/types.js +2 -0
  40. package/package.json +38 -0
  41. package/skills/mongodb-connection.md +208 -0
  42. package/skills/mongodb-natural-language-querying.md +202 -0
  43. package/skills/mongodb-query-optimizer.md +265 -0
  44. package/skills/mongodb-schema-design.md +455 -0
  45. package/skills/mongodb-search-and-ai.md +357 -0
  46. package/skills/mongosh-shell.md +227 -0
  47. package/src/agent-class.ts +393 -0
  48. package/src/banner.ts +36 -0
  49. package/src/confirmation-extension.ts +297 -0
  50. package/src/index.ts +137 -0
  51. package/src/mongosh-interactive-mode.ts +420 -0
  52. package/src/shell-context.ts +97 -0
  53. package/src/skills-loader.ts +37 -0
  54. package/src/stdout-patcher.ts +48 -0
  55. package/src/tools/index.ts +4 -0
  56. package/src/tools/mongosh-eval.ts +115 -0
  57. package/src/tools/search-docs.ts +115 -0
  58. package/src/tools/types.ts +15 -0
  59. package/src/types.ts +23 -0
  60. package/tsconfig-lint.json +4 -0
  61. package/tsconfig.json +20 -0
@@ -0,0 +1,420 @@
1
+ import type { ShellContext } from './shell-context';
2
+ import type { InteractiveModeOptions } from '@earendil-works/pi-coding-agent';
3
+ import type { AgentSessionRuntime } from '@earendil-works/pi-coding-agent';
4
+ import chalk from 'chalk';
5
+ import { truncateToWidth } from '@earendil-works/pi-tui';
6
+
7
+ // Extended interface to access private/protected members
8
+ interface ExtendedInteractiveMode {
9
+ init(): Promise<void>;
10
+ run(): Promise<void>;
11
+ stop(): void;
12
+ showError(message: string): void;
13
+ showWarning(message: string): void;
14
+ // Editor related
15
+ editor: {
16
+ onSubmit?: (text: string) => Promise<void> | void;
17
+ onChange?: (text: string) => void;
18
+ getText(): string;
19
+ setText(text: string): void;
20
+ addToHistory?(text: string): void;
21
+ borderColor?: (str: string) => string;
22
+ };
23
+ defaultEditor: {
24
+ onSubmit?: (text: string) => Promise<void> | void;
25
+ onChange?: (text: string) => void;
26
+ getText(): string;
27
+ setText(text: string): void;
28
+ addToHistory?(text: string): void;
29
+ borderColor?: (str: string) => string;
30
+ };
31
+ editorContainer: {
32
+ clear(): void;
33
+ addChild(child: unknown): void;
34
+ };
35
+ ui: {
36
+ requestRender(): void;
37
+ setFocus(target: unknown): void;
38
+ };
39
+ // Private methods we'll need to access
40
+ updateEditorBorderColor(): void;
41
+ flushPendingBashComponents(): void;
42
+ onInputCallback?: (text: string) => void;
43
+ chatContainer: {
44
+ addChild(child: unknown): void;
45
+ };
46
+ pendingMessagesContainer: {
47
+ addChild(child: unknown): void;
48
+ };
49
+ session: {
50
+ isStreaming: boolean;
51
+ isCompacting: boolean;
52
+ isBashRunning: boolean;
53
+ prompt(
54
+ text: string,
55
+ options?: { streamingBehavior?: string },
56
+ ): Promise<void>;
57
+ recordBashResult?(
58
+ command: string,
59
+ result: { exitCode: number; output: string; cancelled?: boolean },
60
+ options?: { excludeFromContext?: boolean },
61
+ ): void;
62
+ };
63
+ sessionManager: {
64
+ getCwd(): string;
65
+ };
66
+ isExtensionCommand?(text: string): boolean;
67
+ queueCompactionMessage?(text: string, behavior: string): void;
68
+ updatePendingMessagesDisplay?(): void;
69
+ // Extension UI methods
70
+ setExtensionWidget?(
71
+ key: string,
72
+ content: string[] | undefined,
73
+ options?: { position?: 'above' | 'below' },
74
+ ): void;
75
+ // For mongosh mode tracking
76
+ isMongoshMode?: boolean;
77
+ }
78
+
79
+ type MongoshEvalFunction = (expression: string) => Promise<{
80
+ output: string;
81
+ error?: string;
82
+ }>;
83
+
84
+ type MongoshInteractiveModeOptions = InteractiveModeOptions & {
85
+ shellContext: ShellContext;
86
+ mongoshEval: MongoshEvalFunction;
87
+ debugLogging?: boolean;
88
+ // The InteractiveMode class from @earendil-works/pi-coding-agent
89
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
+ InteractiveMode: any;
91
+ };
92
+
93
+ // Mongosh execution component for UI rendering
94
+ class MongoshExecutionComponent {
95
+ private command: string;
96
+ private ui: ExtendedInteractiveMode['ui'];
97
+ private excludeFromContext: boolean;
98
+ private output: string[] = [];
99
+ private isComplete = false;
100
+ private exitCode?: number;
101
+ private cancelled = false;
102
+
103
+ constructor(
104
+ command: string,
105
+ ui: ExtendedInteractiveMode['ui'],
106
+ excludeFromContext: boolean,
107
+ ) {
108
+ this.command = command;
109
+ this.ui = ui;
110
+ this.excludeFromContext = excludeFromContext;
111
+ }
112
+
113
+ appendOutput(chunk: string): void {
114
+ this.output.push(chunk);
115
+ this.ui.requestRender();
116
+ }
117
+
118
+ setComplete(exitCode?: number, cancelled = false): void {
119
+ this.isComplete = true;
120
+ this.exitCode = exitCode;
121
+ this.cancelled = cancelled;
122
+ this.ui.requestRender();
123
+ }
124
+
125
+ render(width: number): string[] {
126
+ const lines: string[] = [];
127
+ const prefix = this.excludeFromContext ? '$$' : '$';
128
+ // Simple syntax highlighting with chalk - just show the command
129
+ // Full highlighting is done via the confirmation extension for tool calls
130
+ const header = `${chalk.gray(prefix)} ${chalk.cyan(this.command)}`;
131
+ lines.push(truncateToWidth(header, width));
132
+
133
+ if (this.output.length > 0) {
134
+ const outputText = this.output.join('');
135
+ const outputLines = outputText.split('\n');
136
+ const prefixStr = chalk.gray('│ ');
137
+ const prefixWidth = 2; // '│ ' is 2 visible characters
138
+ for (const line of outputLines.slice(0, 100)) {
139
+ const availableWidth = Math.max(0, width - prefixWidth);
140
+ lines.push(prefixStr + truncateToWidth(line, availableWidth));
141
+ }
142
+ if (outputLines.length > 100) {
143
+ lines.push(
144
+ chalk.gray(`... ${outputLines.length - 100} more lines`),
145
+ );
146
+ }
147
+ }
148
+
149
+ if (this.isComplete) {
150
+ if (this.cancelled) {
151
+ lines.push(chalk.yellow('◼ cancelled'));
152
+ } else if (this.exitCode !== undefined && this.exitCode !== 0) {
153
+ lines.push(chalk.red(`✗ exit ${this.exitCode}`));
154
+ } else {
155
+ lines.push(chalk.green('✓ done'));
156
+ }
157
+ } else {
158
+ lines.push(chalk.cyan('◌ running...'));
159
+ }
160
+
161
+ return lines;
162
+ }
163
+
164
+ invalidate(): void {
165
+ this.ui.requestRender();
166
+ }
167
+ }
168
+
169
+ // Wrapper class that adds mongosh mode to InteractiveMode
170
+ export class MongoshInteractiveMode {
171
+ private baseMode: ExtendedInteractiveMode;
172
+ private shellContext: ShellContext;
173
+ private mongoshEval: MongoshEvalFunction;
174
+ private debugLogging: boolean;
175
+ private mongoshComponent?: MongoshExecutionComponent;
176
+ private pendingMongoshComponents: MongoshExecutionComponent[] = [];
177
+ private originalOnSubmit?: (text: string) => Promise<void> | void;
178
+ private originalBorderColor?: (str: string) => string;
179
+
180
+ constructor(
181
+ runtimeHost: AgentSessionRuntime,
182
+ options: MongoshInteractiveModeOptions,
183
+ ) {
184
+ // Store our custom options
185
+ this.shellContext = options.shellContext;
186
+ this.mongoshEval = options.mongoshEval;
187
+ this.debugLogging = options.debugLogging ?? false;
188
+
189
+ // Create the base InteractiveMode using the passed class
190
+ const baseOptions: InteractiveModeOptions = {
191
+ migratedProviders: options.migratedProviders,
192
+ modelFallbackMessage: options.modelFallbackMessage,
193
+ initialMessage: options.initialMessage,
194
+ initialImages: options.initialImages,
195
+ initialMessages: options.initialMessages,
196
+ verbose: options.verbose,
197
+ };
198
+
199
+ this.baseMode = new options.InteractiveMode(
200
+ runtimeHost,
201
+ baseOptions,
202
+ ) as ExtendedInteractiveMode;
203
+ }
204
+
205
+ async init(): Promise<void> {
206
+ await this.baseMode.init();
207
+
208
+ // Now that init is done, we can override the onSubmit handler
209
+ this.overrideOnSubmit();
210
+ }
211
+
212
+ private overrideOnSubmit(): void {
213
+ // Store the original onSubmit from the default editor
214
+ this.originalOnSubmit = this.baseMode.defaultEditor.onSubmit;
215
+
216
+ // Store the original onChange if it exists
217
+ const originalOnChange = this.baseMode.defaultEditor.onChange;
218
+
219
+ if (this.debugLogging) {
220
+ process.stderr.write('[mongosh mode] overrideOnSubmit called\n');
221
+ }
222
+
223
+ // Create our wrapper onChange that detects $ prefix for visual feedback
224
+ const wrappedOnChange = (text: string): void => {
225
+ // Check if we're in mongosh mode (starts with $)
226
+ const wasMongoshMode = this.baseMode.isMongoshMode;
227
+ const isMongoshMode = text.startsWith('$');
228
+
229
+ // Update the mode flag
230
+ this.baseMode.isMongoshMode = isMongoshMode;
231
+
232
+ // Update indicator if mode changed
233
+ if (wasMongoshMode !== isMongoshMode) {
234
+ this.updateMongoshModeIndicator();
235
+ }
236
+
237
+ // Call original onChange if it exists
238
+ if (originalOnChange) {
239
+ originalOnChange(text);
240
+ }
241
+ };
242
+
243
+ // Create our wrapper onSubmit that checks for $ prefix
244
+ const wrappedOnSubmit = async (text: string): Promise<void> => {
245
+ if (this.debugLogging) {
246
+ process.stderr.write(
247
+ `[mongosh mode] wrappedOnSubmit called with: "${text.substring(0, 50)}..."\n`,
248
+ );
249
+ }
250
+
251
+ // Reset mongosh mode flag
252
+ this.baseMode.isMongoshMode = false;
253
+ this.updateMongoshModeIndicator();
254
+
255
+ // Handle mongosh command ($ for normal, $$ for excluded from context)
256
+ if (text.startsWith('$')) {
257
+ if (this.debugLogging) {
258
+ process.stderr.write('[mongosh mode] Detected $ prefix\n');
259
+ }
260
+ const isExcluded = text.startsWith('$$');
261
+ const command = isExcluded
262
+ ? text.slice(2).trim()
263
+ : text.slice(1).trim();
264
+
265
+ if (command) {
266
+ // Add to history
267
+ this.baseMode.editor.addToHistory?.(text);
268
+
269
+ // Handle the mongosh command
270
+ await this.handleMongoshCommand(command, isExcluded);
271
+ return;
272
+ }
273
+ }
274
+
275
+ // Not a mongosh command - call the original handler
276
+ // We need to call the original onSubmit which handles ! for bash and normal messages
277
+ if (this.originalOnSubmit) {
278
+ await this.originalOnSubmit(text);
279
+ }
280
+ };
281
+
282
+ // Apply the wrapped handlers to both editors
283
+ this.baseMode.defaultEditor.onSubmit = wrappedOnSubmit;
284
+ this.baseMode.editor.onSubmit = wrappedOnSubmit;
285
+ this.baseMode.defaultEditor.onChange = wrappedOnChange;
286
+ this.baseMode.editor.onChange = wrappedOnChange;
287
+
288
+ if (this.debugLogging) {
289
+ process.stderr.write(
290
+ '[mongosh mode] wrappedOnSubmit assigned to editors\n',
291
+ );
292
+ }
293
+ }
294
+
295
+ private updateMongoshModeIndicator(): void {
296
+ // Store original border color on first call
297
+ if (!this.originalBorderColor) {
298
+ this.originalBorderColor = this.baseMode.editor.borderColor;
299
+ }
300
+
301
+ // Use green border for mongosh mode (distinct from bash mode which uses yellow/orange)
302
+ // When not in mongosh mode, restore the original color
303
+ this.baseMode.editor.borderColor = this.baseMode.isMongoshMode
304
+ ? chalk.green
305
+ : (this.originalBorderColor ?? chalk.gray);
306
+
307
+ // Show/hide the mongosh mode text widget
308
+ if (this.baseMode.setExtensionWidget) {
309
+ if (this.baseMode.isMongoshMode) {
310
+ this.baseMode.setExtensionWidget(
311
+ 'mongosh-mode-indicator',
312
+ [chalk.green('mongosh mode')],
313
+ { position: 'above' },
314
+ );
315
+ } else {
316
+ this.baseMode.setExtensionWidget('mongosh-mode-indicator', undefined);
317
+ }
318
+ }
319
+
320
+ this.baseMode.ui.requestRender();
321
+ }
322
+
323
+ private async handleMongoshCommand(
324
+ command: string,
325
+ excludeFromContext = false,
326
+ ): Promise<void> {
327
+ // Clear the editor
328
+ this.baseMode.editor.setText('');
329
+
330
+ // Create UI component for display
331
+ this.mongoshComponent = new MongoshExecutionComponent(
332
+ command,
333
+ this.baseMode.ui,
334
+ excludeFromContext,
335
+ );
336
+
337
+ const isDeferred = this.baseMode.session.isStreaming;
338
+
339
+ if (isDeferred) {
340
+ // Show in pending area when agent is streaming
341
+ this.baseMode.pendingMessagesContainer.addChild(
342
+ this.mongoshComponent as Parameters<
343
+ ExtendedInteractiveMode['pendingMessagesContainer']['addChild']
344
+ >[0],
345
+ );
346
+ this.pendingMongoshComponents.push(this.mongoshComponent);
347
+ } else {
348
+ // Show in chat immediately when agent is idle
349
+ this.baseMode.chatContainer.addChild(
350
+ this.mongoshComponent as Parameters<
351
+ ExtendedInteractiveMode['chatContainer']['addChild']
352
+ >[0],
353
+ );
354
+ }
355
+
356
+ this.baseMode.ui.requestRender();
357
+
358
+ try {
359
+ // Execute the mongosh command
360
+ const result = await this.mongoshEval(command);
361
+
362
+ if (this.mongoshComponent) {
363
+ if (result.error) {
364
+ this.mongoshComponent.appendOutput(`Error: ${result.error}`);
365
+ this.mongoshComponent.setComplete(1, false);
366
+ } else {
367
+ this.mongoshComponent.appendOutput(result.output);
368
+ this.mongoshComponent.setComplete(0, false);
369
+ }
370
+ }
371
+
372
+ // Record result in session for context (unless excluded)
373
+ if (!excludeFromContext && this.baseMode.session.recordBashResult) {
374
+ this.baseMode.session.recordBashResult(
375
+ command,
376
+ {
377
+ exitCode: result.error ? 1 : 0,
378
+ output: result.output,
379
+ },
380
+ { excludeFromContext: false },
381
+ );
382
+ }
383
+ } catch (error) {
384
+ if (this.mongoshComponent) {
385
+ const errorMsg =
386
+ error instanceof Error ? error.message : 'Unknown error';
387
+ this.mongoshComponent.appendOutput(`Error: ${errorMsg}`);
388
+ this.mongoshComponent.setComplete(1, false);
389
+ }
390
+
391
+ if (this.debugLogging) {
392
+ process.stderr.write(
393
+ `[mongosh mode] Error executing command: ${error instanceof Error ? error.message : String(error)}\n`,
394
+ );
395
+ }
396
+ }
397
+
398
+ this.mongoshComponent = undefined;
399
+ this.baseMode.ui.requestRender();
400
+ }
401
+
402
+ async run(): Promise<void> {
403
+ // The base mode's run() method handles the main loop
404
+ // Our onSubmit override will intercept $ commands
405
+ await this.baseMode.run();
406
+ }
407
+
408
+ stop(): void {
409
+ this.baseMode.stop();
410
+ }
411
+
412
+ // Delegate other methods to base mode
413
+ showError(message: string): void {
414
+ this.baseMode.showError(message);
415
+ }
416
+
417
+ showWarning(message: string): void {
418
+ this.baseMode.showWarning(message);
419
+ }
420
+ }
@@ -0,0 +1,97 @@
1
+ import { Script } from 'vm';
2
+ import type { ShellInstanceState } from './types';
3
+
4
+ export type ShellContext = {
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ shellEvaluator: any;
7
+ originalEval: (input: string, context: object, filename: string) => unknown;
8
+ formatResultValue: (value: unknown) => Promise<string>;
9
+ instanceState: ShellInstanceState;
10
+ capturedPrintOutput: string[];
11
+ };
12
+
13
+ export function createShellContext(options: {
14
+ shellContext: { db: { _mongo: { _instanceState: ShellInstanceState } } };
15
+ }): ShellContext {
16
+ const { shellContext } = options;
17
+
18
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
19
+ const { ShellEvaluator } = require('@mongosh/shell-evaluator');
20
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
21
+ const { toShellResult } = require('@mongosh/shell-api');
22
+
23
+ const instanceState = shellContext.db._mongo._instanceState;
24
+
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ const shellEvaluator = new ShellEvaluator(instanceState, (value: any) => value);
27
+
28
+ const originalEval = (input: string, context: object, filename: string) => {
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
30
+ return new Script(input, { filename }).runInContext(context as any);
31
+ };
32
+
33
+ const capturedPrintOutput: string[] = [];
34
+
35
+ // Patch the context's print function to capture output
36
+ if (instanceState.context) {
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ const originalPrint = instanceState.context.print as (...args: any[]) => unknown;
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ instanceState.context.print = function(...args: any[]) {
41
+ const output = args.map((arg) =>
42
+ typeof arg === 'string' ? arg : JSON.stringify(arg)
43
+ ).join(' ');
44
+ capturedPrintOutput.push(output);
45
+ // Also call original print if it exists
46
+ if (originalPrint) {
47
+ return originalPrint.apply(this, args);
48
+ }
49
+ return undefined;
50
+ };
51
+
52
+ // Also patch printjson
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ const originalPrintJson = instanceState.context.printjson as (value: any) => unknown;
55
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ instanceState.context.printjson = function(value: any) {
57
+ const output = typeof value === 'string'
58
+ ? value
59
+ : JSON.stringify(value, null, 2);
60
+ capturedPrintOutput.push(output);
61
+ if (originalPrintJson) {
62
+ return originalPrintJson.call(this, value);
63
+ }
64
+ return undefined;
65
+ };
66
+ }
67
+
68
+ const formatResultValue = async (value: unknown): Promise<string> => {
69
+ if (value === undefined) return '';
70
+
71
+ const shellResult = await toShellResult(value);
72
+ const printable = shellResult.printable;
73
+
74
+ if (printable === undefined || printable === null) {
75
+ return String(printable);
76
+ }
77
+
78
+ if (typeof printable === 'string') return printable;
79
+
80
+ try {
81
+ if (typeof printable.toJSON === 'function') {
82
+ return JSON.stringify(printable.toJSON(), null, 2);
83
+ }
84
+ return JSON.stringify(printable, null, 2);
85
+ } catch {
86
+ return String(printable);
87
+ }
88
+ };
89
+
90
+ return {
91
+ shellEvaluator,
92
+ originalEval,
93
+ formatResultValue,
94
+ instanceState,
95
+ capturedPrintOutput,
96
+ };
97
+ }
@@ -0,0 +1,37 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import type { Skill } from './types';
4
+
5
+ export function loadSkillsFromDir(skillsDir: string): Skill[] {
6
+ const skills: Skill[] = [];
7
+ if (!fs.existsSync(skillsDir)) return skills;
8
+
9
+ const files = fs.readdirSync(skillsDir).filter(f => f.endsWith('.md'));
10
+ for (const file of files) {
11
+ const filePath = path.join(skillsDir, file);
12
+ const content = fs.readFileSync(filePath, 'utf-8');
13
+
14
+ const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
15
+ let name = file.replace('.md', '');
16
+ let description = '';
17
+ let skillContent = content;
18
+
19
+ if (frontmatterMatch) {
20
+ const frontmatter = frontmatterMatch[1];
21
+ skillContent = content.slice(frontmatterMatch[0].length);
22
+
23
+ const nameMatch = frontmatter.match(/name:\s*(.+)/);
24
+ const descMatch = frontmatter.match(/description:\s*(.+)/);
25
+ if (nameMatch) name = nameMatch[1].trim();
26
+ if (descMatch) description = descMatch[1].trim();
27
+ }
28
+
29
+ skills.push({
30
+ name,
31
+ description,
32
+ content: skillContent.trim(),
33
+ source: filePath,
34
+ });
35
+ }
36
+ return skills;
37
+ }
@@ -0,0 +1,48 @@
1
+ export type StdoutPatcher = {
2
+ enable: () => void;
3
+ disable: () => void;
4
+ };
5
+
6
+ // Specific problematic Kitty protocol sequences to suppress
7
+ const KITTY_QUERY = '\x1b[?u';
8
+ const KITTY_ENABLE_7U = '\x1b[>7u';
9
+ const KITTY_ENABLE_4_2M = '\x1b[>4;2m';
10
+
11
+ export function createStdoutPatcher(): StdoutPatcher {
12
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
13
+ const originalStderrWrite = process.stderr.write.bind(process.stderr);
14
+ let suppressKittyQueries = false;
15
+
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ const filterWrite = (original: any) =>
18
+ function (chunk: any, encoding?: any, callback?: any) {
19
+ if (suppressKittyQueries) {
20
+ const str = typeof chunk === 'string' ? chunk : chunk.toString();
21
+ // Only suppress specific Kitty protocol sequences (not general color codes)
22
+ const isKittySeq =
23
+ str === KITTY_QUERY ||
24
+ str === KITTY_ENABLE_7U ||
25
+ str === KITTY_ENABLE_4_2M ||
26
+ str.endsWith(KITTY_QUERY) ||
27
+ str.endsWith(KITTY_ENABLE_7U) ||
28
+ str.endsWith(KITTY_ENABLE_4_2M);
29
+ if (isKittySeq) {
30
+ if (typeof callback === 'function') callback();
31
+ return true;
32
+ }
33
+ }
34
+ return original(chunk, encoding, callback);
35
+ };
36
+
37
+ process.stdout.write = filterWrite(originalStdoutWrite);
38
+ process.stderr.write = filterWrite(originalStderrWrite);
39
+
40
+ return {
41
+ enable: () => {
42
+ suppressKittyQueries = true;
43
+ },
44
+ disable: () => {
45
+ suppressKittyQueries = false;
46
+ },
47
+ };
48
+ }
@@ -0,0 +1,4 @@
1
+ export type { Tool, SearchDocsResult } from './types';
2
+ export { createSearchDocsTool } from './search-docs';
3
+ export { createMongoshEvalTool } from './mongosh-eval';
4
+ export type { CreateMongoshEvalToolOptions } from './mongosh-eval';