@indiccoder/mentis-cli 1.0.3

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 (46) hide show
  1. package/README.md +90 -0
  2. package/console.log(tick) +0 -0
  3. package/debug_fs.js +12 -0
  4. package/dist/checkpoint/CheckpointManager.js +53 -0
  5. package/dist/config/ConfigManager.js +55 -0
  6. package/dist/context/ContextManager.js +55 -0
  7. package/dist/context/RepoMapper.js +112 -0
  8. package/dist/index.js +12 -0
  9. package/dist/llm/AnthropicClient.js +70 -0
  10. package/dist/llm/ModelInterface.js +2 -0
  11. package/dist/llm/OpenAIClient.js +58 -0
  12. package/dist/mcp/JsonRpcClient.js +117 -0
  13. package/dist/mcp/McpClient.js +59 -0
  14. package/dist/repl/PersistentShell.js +75 -0
  15. package/dist/repl/ReplManager.js +813 -0
  16. package/dist/tools/FileTools.js +100 -0
  17. package/dist/tools/GitTools.js +127 -0
  18. package/dist/tools/PersistentShellTool.js +30 -0
  19. package/dist/tools/SearchTools.js +83 -0
  20. package/dist/tools/Tool.js +2 -0
  21. package/dist/tools/WebSearchTool.js +60 -0
  22. package/dist/ui/UIManager.js +40 -0
  23. package/package.json +63 -0
  24. package/screenshot_1765779883482_9b30.png +0 -0
  25. package/scripts/test_features.ts +48 -0
  26. package/scripts/test_glm.ts +53 -0
  27. package/scripts/test_models.ts +38 -0
  28. package/src/checkpoint/CheckpointManager.ts +61 -0
  29. package/src/config/ConfigManager.ts +77 -0
  30. package/src/context/ContextManager.ts +63 -0
  31. package/src/context/RepoMapper.ts +119 -0
  32. package/src/index.ts +12 -0
  33. package/src/llm/ModelInterface.ts +47 -0
  34. package/src/llm/OpenAIClient.ts +64 -0
  35. package/src/mcp/JsonRpcClient.ts +103 -0
  36. package/src/mcp/McpClient.ts +75 -0
  37. package/src/repl/PersistentShell.ts +85 -0
  38. package/src/repl/ReplManager.ts +842 -0
  39. package/src/tools/FileTools.ts +89 -0
  40. package/src/tools/GitTools.ts +113 -0
  41. package/src/tools/PersistentShellTool.ts +32 -0
  42. package/src/tools/SearchTools.ts +74 -0
  43. package/src/tools/Tool.ts +6 -0
  44. package/src/tools/WebSearchTool.ts +63 -0
  45. package/src/ui/UIManager.ts +41 -0
  46. package/tsconfig.json +21 -0
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # Mentis-CLI 🧠
2
+ > **An Agentic, Multi-Model CLI Coding Assistant.**
3
+
4
+ Mentis is a powerful terminal-based AI coding assistant that lives in your command line. It supports multiple LLM providers (Gemini, Ollama, OpenAI), agentic file operations, MCP (Model Context Protocol), and more.
5
+
6
+ ## ✨ Features
7
+
8
+ * **πŸ€– Multi-Model Support**: Switch seamlessly between **Gemini**, **Ollama** (Local), and **OpenAI**.
9
+ * **πŸ› οΈ Agentic Capabilities**: Mentis can read, write, list files, and search your codebase to understand context.
10
+ * **🌐 Web Intelligence**: Hybrid **Google** + **DuckDuckGo** search for documentation and error fixing (Zero-Config).
11
+ * **πŸ—ΊοΈ Smart Context**: Automatically maps your repository structure for better spatial awareness.
12
+ * **πŸ’° Cost Awareness**: Real-time token usage and cost estimation displayed after every response.
13
+ * **🧠 Persistent Memory**: Auto-saves sessions (`/resume`) and supports manual checkpoints (`/checkpoint`).
14
+ * **πŸ”Œ MCP Support**: Full [Model Context Protocol](https://github.com/modelcontextprotocol) client. Connect external tools like databases or memory servers.
15
+ * **πŸ” Codebase Search**: Built-in `grep` tool to find code across your project.
16
+ * **🐚 Shell Integration**: Run shell commands (`/run`) and commit changes (`/commit`) directly from the agent.
17
+ * **🎨 Premium UI**: Beautiful terminal interface with modes (`PLAN` vs `BUILD`).
18
+
19
+ ## πŸš€ Installation
20
+
21
+ ### From GitHub (Recommended)
22
+ You can install Mentis directly from the repository, ensuring you always get the latest version (bypassing NPM delays).
23
+
24
+ ```bash
25
+ npm install -g git+https://github.com/CoderVLSI/Mentis-CLI.git
26
+ ```
27
+
28
+ ### From Source (Dev)
29
+ ```bash
30
+ git clone https://github.com/CoderVLSI/Mentis-CLI.git
31
+ cd Mentis-CLI
32
+ npm install
33
+ npm run build
34
+ npm link
35
+ ```
36
+
37
+ ## βš™οΈ Configuration
38
+
39
+ Start Mentis by typing:
40
+ ```bash
41
+ mentis
42
+ ```
43
+
44
+ ### Setting up Models
45
+ Type `/model` inside the CLI to launch the interactive configuration wizard:
46
+ 1. **Select Provider**: Gemini, Ollama, or OpenAI.
47
+ 2. **Select Model**: e.g., `gemini-2.5-flash`, `llama3`.
48
+ 3. **Enter Credentials**: API Key (for cloud) or Base URL (for local).
49
+
50
+ *Credentials are stored securely in `~/.mentisrc`.*
51
+
52
+ ## πŸ“– Usage
53
+
54
+ ### Modes
55
+ * **/plan**: Switch to high-level planning mode (Architecture, requirements).
56
+ * **/build**: Switch to code generation mode (Implementation).
57
+
58
+ ### Commands
59
+ | Command | Description |
60
+ | :--- | :--- |
61
+ | `/help` | Show available commands |
62
+ | `/model` | Configure AI provider and model (Interactive) |
63
+ | `/resume` | Resume the last session |
64
+ | `/checkpoint` | Manage saved sessions (`save`, `load`, `list`) |
65
+ | `/search <query>` | Search for code in the current directory |
66
+ | `/mcp connect <cmd>` | Connect an MCP Server (e.g., `npx -y @modelcontextprotocol/server-time`) |
67
+ | `/run <cmd>` | Execute a shell command |
68
+ | `/commit [msg]` | Stage and commit changes to Git |
69
+ | `/add <file>` | Add a specific file to context |
70
+ | `/drop <file>` | Remove a file from context |
71
+ | `/clear` | Clear chat history |
72
+ | `/exit` | Save and exit |
73
+
74
+ ### Example Workflow
75
+ ```text
76
+ Mentis > /plan
77
+ [PLAN] > Analyze this project structure and suggest improvements.
78
+
79
+ Mentis > /build
80
+ [BUILD] > Implement the FolderManager class in src/utils.ts.
81
+ ```
82
+
83
+ ## πŸ”’ Safety
84
+ Mentis asks for **explicit approval** before writing or modifying any files, keeping you in control.
85
+
86
+ ## 🀝 Contributing
87
+ Pull requests are welcome!
88
+
89
+ ## πŸ“„ License
90
+ ISC
File without changes
package/debug_fs.js ADDED
@@ -0,0 +1,12 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ const target = path.join(os.homedir(), 'mentis_debug_write_test.txt');
6
+ console.log(`Attempting to write to: ${target}`);
7
+ try {
8
+ fs.writeFileSync(target, 'Hello world');
9
+ console.log('Success!');
10
+ } catch (e) {
11
+ console.error('Failed:', e);
12
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CheckpointManager = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ class CheckpointManager {
11
+ constructor() {
12
+ this.checkpointDir = path_1.default.join(os_1.default.homedir(), '.mentis', 'checkpoints');
13
+ fs_extra_1.default.ensureDirSync(this.checkpointDir);
14
+ }
15
+ save(name, history, files) {
16
+ const checkpoint = {
17
+ timestamp: Date.now(),
18
+ name,
19
+ history,
20
+ files
21
+ };
22
+ const filePath = path_1.default.join(this.checkpointDir, `${name}.json`);
23
+ fs_extra_1.default.writeJsonSync(filePath, checkpoint, { spaces: 2 });
24
+ return filePath;
25
+ }
26
+ load(name) {
27
+ const filePath = path_1.default.join(this.checkpointDir, `${name}.json`);
28
+ if (fs_extra_1.default.existsSync(filePath)) {
29
+ return fs_extra_1.default.readJsonSync(filePath);
30
+ }
31
+ return null;
32
+ }
33
+ list() {
34
+ if (!fs_extra_1.default.existsSync(this.checkpointDir))
35
+ return [];
36
+ return fs_extra_1.default.readdirSync(this.checkpointDir)
37
+ .filter(f => f.endsWith('.json'))
38
+ .map(f => f.replace('.json', ''));
39
+ }
40
+ delete(name) {
41
+ const filePath = path_1.default.join(this.checkpointDir, `${name}.json`);
42
+ if (fs_extra_1.default.existsSync(filePath)) {
43
+ fs_extra_1.default.removeSync(filePath);
44
+ return true;
45
+ }
46
+ return false;
47
+ }
48
+ exists(name) {
49
+ const filePath = path_1.default.join(this.checkpointDir, `${name}.json`);
50
+ return fs_extra_1.default.existsSync(filePath);
51
+ }
52
+ }
53
+ exports.CheckpointManager = CheckpointManager;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ConfigManager = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ class ConfigManager {
11
+ constructor() {
12
+ this.configPath = path_1.default.join(os_1.default.homedir(), '.mentisrc');
13
+ this.config = {
14
+ defaultProvider: 'ollama',
15
+ ollama: {
16
+ baseUrl: 'http://localhost:11434/v1',
17
+ model: 'llama3:latest'
18
+ },
19
+ gemini: {
20
+ model: 'gemini-2.5-flash'
21
+ },
22
+ glm: {
23
+ model: 'glm-4.6',
24
+ },
25
+ };
26
+ this.loadConfig();
27
+ }
28
+ loadConfig() {
29
+ try {
30
+ if (fs_extra_1.default.existsSync(this.configPath)) {
31
+ const fileContent = fs_extra_1.default.readFileSync(this.configPath, 'utf-8');
32
+ this.config = { ...this.config, ...JSON.parse(fileContent) };
33
+ }
34
+ }
35
+ catch (error) {
36
+ console.error('Error loading config:', error);
37
+ }
38
+ }
39
+ saveConfig() {
40
+ try {
41
+ fs_extra_1.default.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
42
+ }
43
+ catch (error) {
44
+ console.error('Error saving config:', error);
45
+ }
46
+ }
47
+ getConfig() {
48
+ return this.config;
49
+ }
50
+ updateConfig(newConfig) {
51
+ this.config = { ...this.config, ...newConfig };
52
+ this.saveConfig();
53
+ }
54
+ }
55
+ exports.ConfigManager = ConfigManager;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ContextManager = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const RepoMapper_1 = require("./RepoMapper");
10
+ class ContextManager {
11
+ constructor() {
12
+ this.files = new Map();
13
+ this.repoMapper = new RepoMapper_1.RepoMapper(process.cwd());
14
+ }
15
+ async addFile(filePath) {
16
+ try {
17
+ const absolutePath = path_1.default.resolve(process.cwd(), filePath);
18
+ if (!fs_extra_1.default.existsSync(absolutePath)) {
19
+ throw new Error(`File not found: ${filePath}`);
20
+ }
21
+ const content = await fs_extra_1.default.readFile(absolutePath, 'utf-8');
22
+ this.files.set(absolutePath, content);
23
+ return `Added ${filePath} to context.`;
24
+ }
25
+ catch (error) {
26
+ return `Error adding file: ${error.message}`;
27
+ }
28
+ }
29
+ removeFile(filePath) {
30
+ const absolutePath = path_1.default.resolve(process.cwd(), filePath);
31
+ if (this.files.has(absolutePath)) {
32
+ this.files.delete(absolutePath);
33
+ return `Removed ${filePath} from context.`;
34
+ }
35
+ return `File not in context: ${filePath}`;
36
+ }
37
+ clear() {
38
+ this.files.clear();
39
+ }
40
+ getContextString() {
41
+ const repoMap = this.repoMapper.generateTree();
42
+ let context = `Repository Structure:\n${repoMap}\n\n`;
43
+ if (this.files.size > 0) {
44
+ context += 'Current File Context:\n\n';
45
+ for (const [filePath, content] of this.files.entries()) {
46
+ context += `--- File: ${path_1.default.basename(filePath)} ---\n${content}\n\n`;
47
+ }
48
+ }
49
+ return context;
50
+ }
51
+ getFiles() {
52
+ return Array.from(this.files.keys());
53
+ }
54
+ }
55
+ exports.ContextManager = ContextManager;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RepoMapper = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const child_process_1 = require("child_process");
10
+ class RepoMapper {
11
+ constructor(rootDir) {
12
+ this.rootDir = rootDir;
13
+ this.ignorePatterns = new Set(['.git', 'node_modules', 'dist', 'coverage', '.DS_Store']);
14
+ }
15
+ generateTree() {
16
+ try {
17
+ // Try using git first for speed
18
+ const stdout = (0, child_process_1.execSync)('git ls-files --cached --others --exclude-standard', {
19
+ cwd: this.rootDir,
20
+ encoding: 'utf-8',
21
+ stdio: ['ignore', 'pipe', 'ignore'] // Ignore stderr to avoid noise
22
+ });
23
+ const files = stdout.split('\n').filter(Boolean);
24
+ return this.buildTreeFromPaths(files);
25
+ }
26
+ catch (error) {
27
+ // Fallback to manual walk if not a git repo or git fails
28
+ return this.walk(this.rootDir, '');
29
+ }
30
+ }
31
+ buildTreeFromPaths(paths) {
32
+ const tree = {};
33
+ // Build object tree
34
+ for (const p of paths) {
35
+ const parts = p.split('/');
36
+ let current = tree;
37
+ for (const part of parts) {
38
+ if (!current[part]) {
39
+ current[part] = {};
40
+ }
41
+ current = current[part];
42
+ }
43
+ }
44
+ // Convert object tree to string
45
+ return this.renderTree(tree, '');
46
+ }
47
+ renderTree(node, indent) {
48
+ let result = '';
49
+ const keys = Object.keys(node).sort((a, b) => {
50
+ const aIsLeaf = Object.keys(node[a]).length === 0;
51
+ const bIsLeaf = Object.keys(node[b]).length === 0;
52
+ // Dirs first
53
+ if (!aIsLeaf && bIsLeaf)
54
+ return -1;
55
+ if (aIsLeaf && !bIsLeaf)
56
+ return 1;
57
+ return a.localeCompare(b);
58
+ });
59
+ keys.forEach((key, index) => {
60
+ const isLast = index === keys.length - 1;
61
+ const isDir = Object.keys(node[key]).length > 0;
62
+ const prefix = isLast ? '└── ' : 'β”œβ”€β”€ ';
63
+ const childIndent = isLast ? ' ' : 'β”‚ ';
64
+ result += `${indent}${prefix}${key}${isDir ? '/' : ''}\n`;
65
+ if (isDir) {
66
+ result += this.renderTree(node[key], indent + childIndent);
67
+ }
68
+ });
69
+ return result;
70
+ }
71
+ walk(currentPath, indent) {
72
+ try {
73
+ const files = fs_extra_1.default.readdirSync(currentPath);
74
+ let result = '';
75
+ // Cache stats to avoid repeated calls during sort
76
+ const fileStats = files.map(file => {
77
+ try {
78
+ return {
79
+ name: file,
80
+ isDir: fs_extra_1.default.statSync(path_1.default.join(currentPath, file)).isDirectory()
81
+ };
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ }).filter(f => f);
87
+ // Sort: Directories first, then files
88
+ fileStats.sort((a, b) => {
89
+ if (a.isDir && !b.isDir)
90
+ return -1;
91
+ if (!a.isDir && b.isDir)
92
+ return 1;
93
+ return a.name.localeCompare(b.name);
94
+ });
95
+ const filteredFiles = fileStats.filter(f => !this.ignorePatterns.has(f.name));
96
+ filteredFiles.forEach((fileObj, index) => {
97
+ const isLast = index === filteredFiles.length - 1;
98
+ const prefix = isLast ? '└── ' : 'β”œβ”€β”€ ';
99
+ const childIndent = isLast ? ' ' : 'β”‚ ';
100
+ result += `${indent}${prefix}${fileObj.name}${fileObj.isDir ? '/' : ''}\n`;
101
+ if (fileObj.isDir) {
102
+ result += this.walk(path_1.default.join(currentPath, fileObj.name), indent + childIndent);
103
+ }
104
+ });
105
+ return result;
106
+ }
107
+ catch (e) {
108
+ return `${indent}Error reading directory: ${e}\n`;
109
+ }
110
+ }
111
+ }
112
+ exports.RepoMapper = RepoMapper;
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const ReplManager_1 = require("./repl/ReplManager");
5
+ async function main() {
6
+ const repl = new ReplManager_1.ReplManager();
7
+ await repl.start();
8
+ }
9
+ main().catch((error) => {
10
+ console.error('Fatal error:', error);
11
+ process.exit(1);
12
+ });
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AnthropicClient = void 0;
7
+ const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
8
+ class AnthropicClient {
9
+ constructor(apiKey, model, baseUrl) {
10
+ this.client = new sdk_1.default({
11
+ apiKey: apiKey,
12
+ baseURL: baseUrl, // Optional, allows pointing to Z.ai or other proxies
13
+ });
14
+ this.model = model;
15
+ }
16
+ async chat(messages, tools) {
17
+ try {
18
+ // Convert ChatMessage (system, user, assistant) to Anthropic format
19
+ // Anthropic handles 'system' prompt via a separate parameter, not in the messages array.
20
+ let systemPrompt = undefined;
21
+ const anthropicMessages = [];
22
+ for (const msg of messages) {
23
+ if (msg.role === 'system') {
24
+ systemPrompt = msg.content;
25
+ }
26
+ else {
27
+ anthropicMessages.push({
28
+ role: msg.role,
29
+ content: msg.content
30
+ });
31
+ }
32
+ }
33
+ // If no system prompt found in messages, use default
34
+ if (!systemPrompt) {
35
+ systemPrompt = 'You are Mentis, an expert AI coding assistant. You help users write code, debug issues, and explain concepts.';
36
+ }
37
+ const params = {
38
+ model: this.model,
39
+ messages: anthropicMessages,
40
+ system: systemPrompt,
41
+ max_tokens: 4096,
42
+ };
43
+ // TODO: Map tools if supported (Z.ai docs say they support tools)
44
+ // For now, simplicity first.
45
+ const response = await this.client.messages.create(params);
46
+ // Calculate usage (approximate if not provided, but Anthropic provides it)
47
+ const inputTokens = response.usage.input_tokens;
48
+ const outputTokens = response.usage.output_tokens;
49
+ // Extract content (handling text blocks)
50
+ let content = '';
51
+ for (const block of response.content) {
52
+ if (block.type === 'text') {
53
+ content += block.text;
54
+ }
55
+ }
56
+ return {
57
+ content: content,
58
+ usage: {
59
+ input_tokens: inputTokens,
60
+ output_tokens: outputTokens
61
+ }
62
+ };
63
+ }
64
+ catch (error) {
65
+ console.error('Error calling Anthropic API:', error.message);
66
+ throw error;
67
+ }
68
+ }
69
+ }
70
+ exports.AnthropicClient = AnthropicClient;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.OpenAIClient = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ class OpenAIClient {
9
+ constructor(baseUrl, apiKey, model) {
10
+ this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
11
+ this.apiKey = apiKey;
12
+ this.model = model;
13
+ }
14
+ async chat(messages, tools, signal) {
15
+ try {
16
+ const requestBody = {
17
+ model: this.model,
18
+ messages: [
19
+ { role: 'system', content: 'You are Mentis, an expert AI coding assistant. You help users write code, debug issues, and explain concepts. You are concise, accurate, and professional.' },
20
+ ...messages
21
+ ],
22
+ temperature: 0.7,
23
+ };
24
+ if (tools && tools.length > 0) {
25
+ requestBody.tools = tools;
26
+ requestBody.tool_choice = 'auto';
27
+ }
28
+ const response = await axios_1.default.post(`${this.baseUrl}/chat/completions`, requestBody, {
29
+ headers: {
30
+ 'Content-Type': 'application/json',
31
+ 'Authorization': `Bearer ${this.apiKey}`,
32
+ },
33
+ signal: signal // Pass AbortSignal to Axios
34
+ });
35
+ const choice = response.data.choices[0];
36
+ return {
37
+ content: response.data.choices[0].message.content,
38
+ tool_calls: response.data.choices[0].message.tool_calls,
39
+ usage: {
40
+ input_tokens: response.data.usage?.prompt_tokens || 0,
41
+ output_tokens: response.data.usage?.completion_tokens || 0
42
+ }
43
+ };
44
+ }
45
+ catch (error) {
46
+ if (axios_1.default.isCancel(error) || error.name === 'AbortError' || error.code === 'ERR_CANCELED') {
47
+ // Rethrow as specific cancellation to be handled by caller
48
+ throw new Error('Request cancelled by user');
49
+ }
50
+ console.error('Error calling model API:', error.message);
51
+ if (error.response) {
52
+ console.error('Response data:', error.response.data);
53
+ }
54
+ throw error;
55
+ }
56
+ }
57
+ }
58
+ exports.OpenAIClient = OpenAIClient;
@@ -0,0 +1,117 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.JsonRpcClient = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const readline = __importStar(require("readline"));
39
+ class JsonRpcClient {
40
+ constructor(command, args) {
41
+ this.sequence = 0;
42
+ this.pendingRequests = new Map();
43
+ this.process = (0, child_process_1.spawn)(command, args, {
44
+ stdio: ['pipe', 'pipe', 'inherit'], // stdin, stdout, stderr
45
+ });
46
+ if (!this.process.stdout) {
47
+ throw new Error('Failed to open stdout for MCP process');
48
+ }
49
+ const rl = readline.createInterface({ input: this.process.stdout });
50
+ rl.on('line', (line) => this.handleMessage(line));
51
+ this.process.on('error', (err) => console.error('MCP Process Error:', err));
52
+ this.process.on('exit', (code) => console.log('MCP Process Exited:', code));
53
+ }
54
+ handleMessage(line) {
55
+ try {
56
+ if (!line.trim())
57
+ return;
58
+ const message = JSON.parse(line);
59
+ // Handle Response
60
+ if (message.id !== undefined && (message.result !== undefined || message.error !== undefined)) {
61
+ const handler = this.pendingRequests.get(message.id);
62
+ if (handler) {
63
+ this.pendingRequests.delete(message.id);
64
+ if (message.error) {
65
+ handler.reject(new Error(message.error.message));
66
+ }
67
+ else {
68
+ handler.resolve(message.result);
69
+ }
70
+ }
71
+ }
72
+ else {
73
+ // Handle Notification or Request from server (not implemented deep yet)
74
+ // console.log('Received notification from MCP:', message);
75
+ }
76
+ }
77
+ catch (e) {
78
+ console.error('Failed to parse MCP message:', line, e);
79
+ }
80
+ }
81
+ async sendRequest(method, params) {
82
+ const id = this.sequence++;
83
+ const request = {
84
+ jsonrpc: '2.0',
85
+ method,
86
+ params,
87
+ id,
88
+ };
89
+ return new Promise((resolve, reject) => {
90
+ this.pendingRequests.set(id, { resolve, reject });
91
+ try {
92
+ if (!this.process.stdin)
93
+ throw new Error('Stdin not available');
94
+ const data = JSON.stringify(request) + '\n';
95
+ this.process.stdin.write(data);
96
+ }
97
+ catch (e) {
98
+ this.pendingRequests.delete(id);
99
+ reject(e);
100
+ }
101
+ });
102
+ }
103
+ sendNotification(method, params) {
104
+ if (!this.process.stdin)
105
+ return;
106
+ const notification = {
107
+ jsonrpc: '2.0',
108
+ method,
109
+ params,
110
+ };
111
+ this.process.stdin.write(JSON.stringify(notification) + '\n');
112
+ }
113
+ disconnect() {
114
+ this.process.kill();
115
+ }
116
+ }
117
+ exports.JsonRpcClient = JsonRpcClient;