@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
package/.eslintrc.js ADDED
@@ -0,0 +1,13 @@
1
+
2
+ module.exports = {
3
+ root: true,
4
+ extends: ['@mongodb-js/eslint-config-devtools'],
5
+ parserOptions: {
6
+ tsconfigRootDir: __dirname,
7
+ project: ['./tsconfig-lint.json'],
8
+ },
9
+ rules: {
10
+ '@typescript-eslint/consistent-type-imports': 'off',
11
+ '@typescript-eslint/no-var-requires': 'off',
12
+ },
13
+ };
@@ -0,0 +1 @@
1
+ "@mongodb-js/prettier-config-devtools"
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # Project Agent Snippet
2
+
3
+ Integrates Pi coding agent with mongosh for programmatic agent usage.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```javascript
14
+ // Chat with the Pi agent
15
+ projectAgent.chat("Analyze this codebase")
16
+
17
+ // Show help
18
+ projectAgent.help()
19
+ ```
20
+
21
+ ## Configuration
22
+
23
+ The snippet uses the `@earendil-works/pi-coding-agent` package for programmatic agent integration with mongosh.
@@ -0,0 +1,47 @@
1
+ import type { Tool } from './tools';
2
+ import type { Skill } from './types';
3
+ import type { StdoutPatcher } from './stdout-patcher';
4
+ import type { ShellContext } from './shell-context';
5
+ export type AgentServices = {
6
+ createAgentSessionRuntime: typeof import('@earendil-works/pi-coding-agent').createAgentSessionRuntime;
7
+ createAgentSessionServices: typeof import('@earendil-works/pi-coding-agent').createAgentSessionServices;
8
+ createAgentSessionFromServices: typeof import('@earendil-works/pi-coding-agent').createAgentSessionFromServices;
9
+ SessionManager: typeof import('@earendil-works/pi-coding-agent').SessionManager;
10
+ InteractiveMode: typeof import('@earendil-works/pi-coding-agent').InteractiveMode;
11
+ SettingsManager: typeof import('@earendil-works/pi-coding-agent').SettingsManager;
12
+ getAgentDir: typeof import('@earendil-works/pi-coding-agent').getAgentDir;
13
+ AuthStorage: typeof import('@earendil-works/pi-coding-agent').AuthStorage;
14
+ ModelRegistry: typeof import('@earendil-works/pi-coding-agent').ModelRegistry;
15
+ };
16
+ export type AgentOptions = {
17
+ services: AgentServices;
18
+ mongoshEvalTool: Tool;
19
+ searchDocsTool: Tool;
20
+ loadedSkills: Skill[];
21
+ skillsDir: string;
22
+ debugLogging: boolean;
23
+ stdoutPatcher: StdoutPatcher;
24
+ shellContext: ShellContext;
25
+ };
26
+ export declare class Agent {
27
+ private static isRunning;
28
+ private sessionManager;
29
+ private services;
30
+ private mongoshEvalTool;
31
+ private searchDocsTool;
32
+ private loadedSkills;
33
+ private skillsDir;
34
+ private debugLogging;
35
+ private stdoutPatcher;
36
+ private shellContext;
37
+ private sessionId;
38
+ private resumeSessionId;
39
+ constructor(options: AgentOptions);
40
+ static getIsRunning(): boolean;
41
+ getCurrentSessionId(): string | undefined;
42
+ setResumeSessionId(sessionId: string): void;
43
+ resume(sessionId: string): Promise<void>;
44
+ run(options?: {
45
+ resumeSessionId?: string;
46
+ }): Promise<void>;
47
+ }
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.Agent = void 0;
40
+ const os = __importStar(require("os"));
41
+ const path = __importStar(require("path"));
42
+ const banner_1 = require("./banner");
43
+ const confirmation_extension_1 = __importDefault(require("./confirmation-extension"));
44
+ const mongosh_interactive_mode_1 = require("./mongosh-interactive-mode");
45
+ const chalk_1 = __importDefault(require("chalk"));
46
+ class Agent {
47
+ constructor(options) {
48
+ this.services = options.services;
49
+ this.mongoshEvalTool = options.mongoshEvalTool;
50
+ this.searchDocsTool = options.searchDocsTool;
51
+ this.loadedSkills = options.loadedSkills;
52
+ this.skillsDir = options.skillsDir;
53
+ this.debugLogging = options.debugLogging;
54
+ this.stdoutPatcher = options.stdoutPatcher;
55
+ this.shellContext = options.shellContext;
56
+ this.sessionManager = this.services.SessionManager.create(process.cwd());
57
+ }
58
+ static getIsRunning() {
59
+ return Agent.isRunning;
60
+ }
61
+ getCurrentSessionId() {
62
+ return this.sessionId;
63
+ }
64
+ setResumeSessionId(sessionId) {
65
+ this.resumeSessionId = sessionId;
66
+ }
67
+ async resume(sessionId) {
68
+ this.resumeSessionId = sessionId;
69
+ await this.run({ resumeSessionId: sessionId });
70
+ }
71
+ async run(options) {
72
+ if (Agent.isRunning) {
73
+ return;
74
+ }
75
+ Agent.isRunning = true;
76
+ const resumeSessionId = options?.resumeSessionId ?? this.resumeSessionId;
77
+ // Save and remove mongosh's stdin listeners to prevent interference with TUI
78
+ // Note: We intentionally do NOT pause() stdin as the TUI needs it for input handling
79
+ const savedListeners = process.stdin.rawListeners('data');
80
+ process.stdin.removeAllListeners('data');
81
+ // Ensure stdin is in flowing mode for the TUI
82
+ if (process.stdin.isPaused()) {
83
+ process.stdin.resume();
84
+ }
85
+ // Save and set raw mode to enable capturing special keys (Ctrl+O, etc.)
86
+ const originalRawMode = process.stdin.isTTY &&
87
+ process.stdin.isRaw;
88
+ if (process.stdin.isTTY) {
89
+ process.stdin.setRawMode(true);
90
+ }
91
+ const originalExit = process.exit.bind(process);
92
+ try {
93
+ const createRuntime = async (runtimeOptions) => {
94
+ const { SettingsManager, createAgentSessionServices, createAgentSessionFromServices, AuthStorage, ModelRegistry, } = this.services;
95
+ const settingsManager = SettingsManager.inMemory({
96
+ quietStartup: true,
97
+ enableInstallTelemetry: false,
98
+ });
99
+ // Create auth storage and model registry with MongoDB provider pre-configured
100
+ const authStorage = AuthStorage.create();
101
+ const modelRegistry = ModelRegistry.create(authStorage);
102
+ // Register the MongoDB Docs provider (same as the ai snippet's mongodb provider)
103
+ // Note: The MongoDB Knowledge API doesn't require authentication, but Pi SDK requires apiKey field
104
+ modelRegistry.registerProvider('mongodb', {
105
+ name: 'MongoDB',
106
+ baseUrl: 'https://knowledge.mongodb.com/api/v1',
107
+ api: 'openai-responses',
108
+ apiKey: 'mongodb', // Dummy key - the actual API doesn't require authentication
109
+ authHeader: false, // Don't send Authorization header
110
+ headers: {
111
+ 'X-Request-Origin': 'mongodb-mongosh',
112
+ 'user-agent': 'mongodb-mongosh',
113
+ },
114
+ models: [
115
+ {
116
+ id: 'mongodb-chat-latest',
117
+ name: 'MongoDB Assistant',
118
+ reasoning: false,
119
+ input: ['text'],
120
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
121
+ contextWindow: 128000,
122
+ maxTokens: 4000, // MongoDB Knowledge API max allowed
123
+ },
124
+ ],
125
+ });
126
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
+ const mongoshSkills = this.loadedSkills.map((skill) => ({
128
+ name: skill.name,
129
+ description: skill.description,
130
+ filePath: skill.source,
131
+ baseDir: this.skillsDir,
132
+ source: 'custom',
133
+ sourceInfo: {
134
+ source: 'custom',
135
+ path: skill.source,
136
+ },
137
+ disableModelInvocation: false,
138
+ }));
139
+ const sessionServices = await createAgentSessionServices({
140
+ cwd: runtimeOptions.cwd,
141
+ settingsManager,
142
+ authStorage,
143
+ modelRegistry,
144
+ resourceLoaderOptions: {
145
+ extensionFactories: [confirmation_extension_1.default],
146
+ skillsOverride: (base) => ({
147
+ skills: [...base.skills, ...mongoshSkills],
148
+ diagnostics: base.diagnostics,
149
+ }),
150
+ systemPromptOverride: () => {
151
+ const basePrompt = `You are a MongoDB assistant running inside mongosh.
152
+
153
+ You are connected to a live MongoDB instance and can execute queries and commands using the mongosh_eval tool.
154
+
155
+ Guidelines:
156
+ - Always explain what you're about to do before running queries
157
+ - Use mongosh_eval for queries, inspections, and admin commands
158
+ - For destructive operations (drop, delete, update, insert), ask for confirmation first
159
+ - Suggest optimizations when you see inefficient patterns
160
+ - Use aggregation pipelines for complex data analysis
161
+ - Check indexes before suggesting queries on large collections
162
+
163
+ Available skills:
164
+ ${this.loadedSkills.map((s) => `- ${s.name}: ${s.description}`).join('\n')}
165
+
166
+ When responding:
167
+ 1. For simple questions, answer directly
168
+ 2. For database queries, use mongosh_eval to check and show results
169
+ 3. For performance questions, use explain plans to verify
170
+ 4. Always format JSON results for readability`;
171
+ return basePrompt;
172
+ },
173
+ },
174
+ });
175
+ // Determine if MongoDB should be the default model
176
+ // Only default to mongodb-chat-latest if it's the only available model
177
+ // Otherwise, require manual selection by the user
178
+ const availableModels = modelRegistry.getAvailable();
179
+ const mongodbModel = modelRegistry.find('mongodb', 'mongodb-chat-latest');
180
+ // Check if MongoDB is the only available model (no other providers configured)
181
+ const otherModelsExist = availableModels.some((m) => m.provider !== 'mongodb');
182
+ const shouldUseMongoDbAsDefault = mongodbModel && !otherModelsExist && availableModels.length > 0;
183
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
184
+ const sessionFromServicesOptions = {
185
+ services: sessionServices,
186
+ sessionManager: runtimeOptions.sessionManager,
187
+ sessionStartEvent: runtimeOptions.sessionStartEvent,
188
+ customTools: [this.mongoshEvalTool, this.searchDocsTool],
189
+ };
190
+ // Only set the model if MongoDB should be the default
191
+ if (shouldUseMongoDbAsDefault) {
192
+ sessionFromServicesOptions.model = mongodbModel;
193
+ }
194
+ return {
195
+ ...(await createAgentSessionFromServices(sessionFromServicesOptions)),
196
+ services: sessionServices,
197
+ diagnostics: sessionServices.diagnostics,
198
+ };
199
+ };
200
+ const { createAgentSessionRuntime } = this.services;
201
+ // Use custom MongoDB agent directory
202
+ const agentDir = path.join(os.homedir(), '.mongodb', 'mongosh', 'agent');
203
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
204
+ const sessionStartEvent = resumeSessionId
205
+ ? { type: 'session-resume', sessionId: resumeSessionId }
206
+ : undefined;
207
+ const runtime = await createAgentSessionRuntime(createRuntime, {
208
+ cwd: process.cwd(),
209
+ agentDir,
210
+ sessionManager: this.sessionManager,
211
+ ...(sessionStartEvent && { sessionStartEvent }),
212
+ });
213
+ // Create mongosh eval function for the interactive mode
214
+ const mongoshEval = async (expression) => {
215
+ const { shellEvaluator, originalEval, formatResultValue, instanceState, capturedPrintOutput, } = this.shellContext;
216
+ // Clear captured output before execution
217
+ capturedPrintOutput.length = 0;
218
+ try {
219
+ let rawValue = await shellEvaluator.customEval(originalEval, expression, instanceState.context, 'mongosh_interactive');
220
+ // Auto-call functions that take no arguments (e.g., `history` -> `history()`)
221
+ // This provides shell-like behavior for zero-argument functions
222
+ if (typeof rawValue === 'function') {
223
+ try {
224
+ rawValue = await rawValue();
225
+ }
226
+ catch {
227
+ // If calling fails, keep the original function reference
228
+ }
229
+ }
230
+ const formatted = await formatResultValue(rawValue);
231
+ // Build output: captured print output takes priority, then add formatted result if present
232
+ let output;
233
+ if (capturedPrintOutput.length > 0) {
234
+ // Has captured print output - use it as primary output
235
+ output = capturedPrintOutput.join('\n');
236
+ // Also append formatted result if it's meaningful (not empty/undefined)
237
+ if (formatted) {
238
+ output += '\n' + formatted;
239
+ }
240
+ }
241
+ else if (formatted) {
242
+ // No captured output, but has formatted result
243
+ output = formatted;
244
+ }
245
+ else {
246
+ // Nothing to show
247
+ output = '(no output)';
248
+ }
249
+ return { output };
250
+ }
251
+ catch (err) {
252
+ const errorMsg = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
253
+ return { output: '', error: errorMsg };
254
+ }
255
+ };
256
+ // Create our custom interactive mode with $ mongosh support
257
+ const mode = new mongosh_interactive_mode_1.MongoshInteractiveMode(runtime, {
258
+ migratedProviders: [],
259
+ initialImages: [],
260
+ initialMessages: [],
261
+ verbose: this.debugLogging,
262
+ shellContext: this.shellContext,
263
+ mongoshEval,
264
+ debugLogging: this.debugLogging,
265
+ InteractiveMode: this.services.InteractiveMode,
266
+ });
267
+ this.stdoutPatcher.enable();
268
+ await (0, banner_1.printBanner)();
269
+ // Capture session ID from sessionManager
270
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
271
+ this.sessionId = this.sessionManager.sessionId;
272
+ // Initialize the mode (sets up onSubmit handler)
273
+ await mode.init();
274
+ await new Promise((resolve) => {
275
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
276
+ process.exit = (() => {
277
+ resolve();
278
+ });
279
+ mode.run().catch(() => {
280
+ resolve();
281
+ });
282
+ });
283
+ this.stdoutPatcher.disable();
284
+ }
285
+ catch (err) {
286
+ if (this.debugLogging) {
287
+ process.stderr.write(`[agent] Error: ${String(err)}\n`);
288
+ }
289
+ }
290
+ finally {
291
+ Agent.isRunning = false;
292
+ process.exit = originalExit;
293
+ // Restore terminal state
294
+ if (process.stdin.isTTY) {
295
+ process.stdin.setRawMode(originalRawMode ?? false);
296
+ }
297
+ for (const listener of savedListeners) {
298
+ process.stdin.on('data', listener);
299
+ }
300
+ process.stdin.resume();
301
+ const sessionId = this.sessionId || this.resumeSessionId;
302
+ if (sessionId) {
303
+ process.stdout.write(`Exited agent mode, resume your session with:\n${chalk_1.default.green(`agent.resume ${sessionId}`)}`);
304
+ }
305
+ else {
306
+ process.stdout.write('\n[Exited agent mode]');
307
+ }
308
+ // Force prompt redraw - emit newline then move up to trigger readline refresh
309
+ process.stdin.emit('data', '\n');
310
+ }
311
+ }
312
+ }
313
+ exports.Agent = Agent;
314
+ Agent.isRunning = false;
@@ -0,0 +1 @@
1
+ export {};