@indiccoder/mentis-cli 1.1.3 → 1.1.5
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/.claude/settings.local.json +8 -0
- package/.mentis/session.json +15 -0
- package/.mentis/sessions/1769189035730.json +23 -0
- package/.mentis/sessions/1769189569160.json +23 -0
- package/.mentis/sessions/1769767538672.json +23 -0
- package/.mentis/sessions/1769767785155.json +23 -0
- package/.mentis/sessions/1769768745802.json +23 -0
- package/.mentis/sessions/1769769600884.json +31 -0
- package/.mentis/sessions/1769770030160.json +31 -0
- package/.mentis/sessions/1769770606004.json +78 -0
- package/.mentis/sessions/1769771084515.json +141 -0
- package/.mentis/sessions/1769881926630.json +57 -0
- package/ARCHITECTURE.md +267 -0
- package/CONTRIBUTING.md +209 -0
- package/README.md +17 -0
- package/dist/checkpoint/CheckpointManager.js +92 -0
- package/dist/commands/Command.js +15 -1
- package/dist/commands/CommandManager.js +30 -5
- package/dist/commands/__tests__/CommandManager.test.js +70 -0
- package/dist/debug_google.js +61 -0
- package/dist/debug_lite.js +49 -0
- package/dist/debug_lite_headers.js +57 -0
- package/dist/debug_search.js +16 -0
- package/dist/index.js +33 -0
- package/dist/mcp/JsonRpcClient.js +16 -0
- package/dist/mcp/McpConfig.js +132 -0
- package/dist/mcp/McpManager.js +189 -0
- package/dist/repl/PersistentShell.js +20 -1
- package/dist/repl/ReplManager.js +410 -138
- package/dist/skills/Skill.js +17 -2
- package/dist/skills/SkillsManager.js +28 -3
- package/dist/skills/__tests__/SkillsManager.test.js +62 -0
- package/dist/tools/AskQuestionTool.js +172 -0
- package/dist/tools/EditFileTool.js +141 -0
- package/dist/tools/FileTools.js +7 -1
- package/dist/tools/PlanModeTool.js +53 -0
- package/dist/tools/WebSearchTool.js +190 -27
- package/dist/ui/DiffViewer.js +110 -0
- package/dist/ui/InputBox.js +16 -2
- package/dist/ui/MultiFileSelector.js +123 -0
- package/dist/ui/PlanModeUI.js +105 -0
- package/dist/ui/ToolExecutor.js +154 -0
- package/dist/ui/UIManager.js +12 -2
- package/dist/utils/__mocks__/chalk.js +20 -0
- package/dist/utils/__tests__/ContextVisualizer.test.js +95 -0
- package/docs/MCP_INTEGRATION.md +290 -0
- package/google_dump.html +18 -0
- package/lite_dump.html +176 -0
- package/lite_headers_dump.html +176 -0
- package/package.json +34 -2
- package/scripts/test_exa_mcp.ts +90 -0
- package/src/checkpoint/CheckpointManager.ts +102 -0
- package/src/commands/Command.ts +64 -13
- package/src/commands/CommandManager.ts +26 -5
- package/src/commands/__tests__/CommandManager.test.ts +83 -0
- package/src/debug_google.ts +30 -0
- package/src/debug_lite.ts +18 -0
- package/src/debug_lite_headers.ts +25 -0
- package/src/debug_search.ts +18 -0
- package/src/index.ts +45 -1
- package/src/mcp/JsonRpcClient.ts +19 -0
- package/src/mcp/McpConfig.ts +153 -0
- package/src/mcp/McpManager.ts +224 -0
- package/src/repl/PersistentShell.ts +24 -1
- package/src/repl/ReplManager.ts +1521 -1204
- package/src/skills/Skill.ts +91 -11
- package/src/skills/SkillsManager.ts +25 -3
- package/src/skills/__tests__/SkillsManager.test.ts +73 -0
- package/src/tools/AskQuestionTool.ts +197 -0
- package/src/tools/EditFileTool.ts +172 -0
- package/src/tools/FileTools.ts +3 -0
- package/src/tools/PlanModeTool.ts +50 -0
- package/src/tools/WebSearchTool.ts +235 -63
- package/src/ui/DiffViewer.ts +117 -0
- package/src/ui/InputBox.ts +17 -2
- package/src/ui/MultiFileSelector.ts +135 -0
- package/src/ui/PlanModeUI.ts +121 -0
- package/src/ui/ToolExecutor.ts +182 -0
- package/src/ui/UIManager.ts +15 -2
- package/src/utils/__mocks__/chalk.ts +19 -0
- package/src/utils/__tests__/ContextVisualizer.test.ts +118 -0
- package/console.log(tick) +0 -0
|
@@ -57,9 +57,19 @@ class CommandManager {
|
|
|
57
57
|
async discoverCommands() {
|
|
58
58
|
const discovered = [];
|
|
59
59
|
// Personal commands
|
|
60
|
-
|
|
60
|
+
try {
|
|
61
|
+
discovered.push(...await this.discoverCommandsInDirectory(this.personalCommandsDir, 'personal'));
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.warn(`Warning: Failed to load personal commands from ${this.personalCommandsDir}: ${error.message}`);
|
|
65
|
+
}
|
|
61
66
|
// Project commands
|
|
62
|
-
|
|
67
|
+
try {
|
|
68
|
+
discovered.push(...await this.discoverCommandsInDirectory(this.projectCommandsDir, 'project'));
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.warn(`Warning: Failed to load project commands from ${this.projectCommandsDir}: ${error.message}`);
|
|
72
|
+
}
|
|
63
73
|
// Store commands in map (project commands override personal)
|
|
64
74
|
for (const command of discovered) {
|
|
65
75
|
this.commands.set(command.name, command);
|
|
@@ -102,6 +112,7 @@ class CommandManager {
|
|
|
102
112
|
const frontmatter = this.extractFrontmatter(content);
|
|
103
113
|
const commandName = this.getCommandName(commandPath, type);
|
|
104
114
|
if (!commandName) {
|
|
115
|
+
console.warn(`Warning: Invalid command name in ${commandPath} (skipping)`);
|
|
105
116
|
return null;
|
|
106
117
|
}
|
|
107
118
|
// Get namespace (subdirectory)
|
|
@@ -121,7 +132,15 @@ class CommandManager {
|
|
|
121
132
|
return command;
|
|
122
133
|
}
|
|
123
134
|
catch (error) {
|
|
124
|
-
|
|
135
|
+
if (error.code === 'ENOENT') {
|
|
136
|
+
console.warn(`Warning: Command file not found: ${commandPath}`);
|
|
137
|
+
}
|
|
138
|
+
else if (error.code === 'EACCES') {
|
|
139
|
+
console.warn(`Warning: Permission denied reading command: ${commandPath}`);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
console.error(`Error parsing command ${commandPath}: ${error.message}`);
|
|
143
|
+
}
|
|
125
144
|
return null;
|
|
126
145
|
}
|
|
127
146
|
}
|
|
@@ -217,7 +236,10 @@ class CommandManager {
|
|
|
217
236
|
const bashRegex = /!`([^`]+)`/g;
|
|
218
237
|
let bashMatch;
|
|
219
238
|
while ((bashMatch = bashRegex.exec(content)) !== null) {
|
|
220
|
-
|
|
239
|
+
const bashCommand = bashMatch[1].trim();
|
|
240
|
+
if (bashCommand) {
|
|
241
|
+
bashCommands.push(bashCommand);
|
|
242
|
+
}
|
|
221
243
|
}
|
|
222
244
|
// Remove bash command markers
|
|
223
245
|
content = content.replace(/!`[^`]+`/g, '[BASH_OUTPUT]');
|
|
@@ -225,7 +247,10 @@ class CommandManager {
|
|
|
225
247
|
const fileRegex = /@([^\s]+)/g;
|
|
226
248
|
let fileMatch;
|
|
227
249
|
while ((fileMatch = fileRegex.exec(content)) !== null) {
|
|
228
|
-
|
|
250
|
+
const fileRef = fileMatch[1].trim();
|
|
251
|
+
if (fileRef && !fileReferences.includes(fileRef)) {
|
|
252
|
+
fileReferences.push(fileRef);
|
|
253
|
+
}
|
|
229
254
|
}
|
|
230
255
|
return { content, bashCommands, fileReferences };
|
|
231
256
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for CommandManager
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const CommandManager_1 = require("../CommandManager");
|
|
7
|
+
describe('CommandManager', () => {
|
|
8
|
+
let manager;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
manager = new CommandManager_1.CommandManager();
|
|
11
|
+
});
|
|
12
|
+
describe('getAllCommands', () => {
|
|
13
|
+
it('should return empty array initially', () => {
|
|
14
|
+
const commands = manager.getAllCommands();
|
|
15
|
+
expect(commands).toEqual([]);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe('getCommandsContext', () => {
|
|
19
|
+
it('should return empty string when no commands', () => {
|
|
20
|
+
const context = manager.getCommandsContext();
|
|
21
|
+
expect(context).toBe('');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
describe('parseCommand', () => {
|
|
25
|
+
it('should replace $ARGUMENTS placeholder', async () => {
|
|
26
|
+
const command = {
|
|
27
|
+
name: 'echo',
|
|
28
|
+
type: 'personal',
|
|
29
|
+
path: '/echo.md',
|
|
30
|
+
directory: '/commands',
|
|
31
|
+
description: 'Echo arguments',
|
|
32
|
+
frontmatter: {},
|
|
33
|
+
content: 'You said: $ARGUMENTS',
|
|
34
|
+
hasParameters: true
|
|
35
|
+
};
|
|
36
|
+
const parsed = await manager.parseCommand(command, ['hello', 'world']);
|
|
37
|
+
expect(parsed.content).toContain('hello world');
|
|
38
|
+
});
|
|
39
|
+
it('should replace $1, $2 placeholders', async () => {
|
|
40
|
+
const command = {
|
|
41
|
+
name: 'greet',
|
|
42
|
+
type: 'personal',
|
|
43
|
+
path: '/greet.md',
|
|
44
|
+
directory: '/commands',
|
|
45
|
+
description: 'Greet user',
|
|
46
|
+
frontmatter: {},
|
|
47
|
+
content: 'Hello $1, welcome to $2',
|
|
48
|
+
hasParameters: true
|
|
49
|
+
};
|
|
50
|
+
const parsed = await manager.parseCommand(command, ['Alice', 'Wonderland']);
|
|
51
|
+
expect(parsed.content).toContain('Hello Alice');
|
|
52
|
+
expect(parsed.content).toContain('welcome to Wonderland');
|
|
53
|
+
});
|
|
54
|
+
it('should extract bash commands from content', async () => {
|
|
55
|
+
const command = {
|
|
56
|
+
name: 'run-test',
|
|
57
|
+
type: 'personal',
|
|
58
|
+
path: '/run-test.md',
|
|
59
|
+
directory: '/commands',
|
|
60
|
+
description: 'Run tests',
|
|
61
|
+
frontmatter: {},
|
|
62
|
+
content: 'Run tests with !`npm test`',
|
|
63
|
+
hasParameters: false
|
|
64
|
+
};
|
|
65
|
+
const parsed = await manager.parseCommand(command, []);
|
|
66
|
+
expect(parsed.bashCommands).toHaveLength(1);
|
|
67
|
+
expect(parsed.bashCommands[0]).toBe('npm test');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
const child_process_1 = require("child_process");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const query = "expo go latest sdk";
|
|
39
|
+
const url = `https://www.google.com/search?q=${encodeURIComponent(query)}&hl=en`;
|
|
40
|
+
console.log(`Fetching Google: ${url}`);
|
|
41
|
+
try {
|
|
42
|
+
// Mimic standard browser
|
|
43
|
+
const userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
44
|
+
const cmd = `curl -s -L -A "${userAgent}" "${url}"`;
|
|
45
|
+
const html = (0, child_process_1.execSync)(cmd, { encoding: 'utf-8' });
|
|
46
|
+
fs.writeFileSync('google_dump.html', html);
|
|
47
|
+
console.log('Dumped HTML to google_dump.html. Length:', html.length);
|
|
48
|
+
if (html.includes('Captcha') || html.includes('unusual traffic')) {
|
|
49
|
+
console.log('BLOCKED by Google Captcha');
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log('Seems OK? Checking for result markers...');
|
|
53
|
+
// Google uses complex class names, but often "h3" is title
|
|
54
|
+
if (html.includes('<h3')) {
|
|
55
|
+
console.log('Found h3 tags (likely titles).');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
console.error('Error:', e.message);
|
|
61
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
const child_process_1 = require("child_process");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const query = "expo go latest sdk";
|
|
39
|
+
const url = `https://lite.duckduckgo.com/lite/?q=${encodeURIComponent(query)}`;
|
|
40
|
+
console.log(`Fetching Lite DDG: ${url}`);
|
|
41
|
+
try {
|
|
42
|
+
const cmd = `curl -s -L -A "Mozilla/5.0" "${url}"`;
|
|
43
|
+
const html = (0, child_process_1.execSync)(cmd, { encoding: 'utf-8' });
|
|
44
|
+
fs.writeFileSync('lite_dump.html', html);
|
|
45
|
+
console.log('Dumped HTML to lite_dump.html');
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
console.error('Error:', e.message);
|
|
49
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
const child_process_1 = require("child_process");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const query = "expo go latest sdk";
|
|
39
|
+
const url = `https://lite.duckduckgo.com/lite/?q=${encodeURIComponent(query)}`;
|
|
40
|
+
console.log(`Testing Lite Headers: ${url}`);
|
|
41
|
+
try {
|
|
42
|
+
const cmd = `curl -s -L -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" -H "Referer: https://duckduckgo.com/" -H "Accept-Language: en-US,en;q=0.9" "${url}"`;
|
|
43
|
+
const html = (0, child_process_1.execSync)(cmd, { encoding: 'utf-8' });
|
|
44
|
+
fs.writeFileSync('lite_headers_dump.html', html);
|
|
45
|
+
if (html.includes('anomaly-modal')) {
|
|
46
|
+
console.log('STILL BLOCKED by Captcha');
|
|
47
|
+
}
|
|
48
|
+
else if (html.includes('result-link')) {
|
|
49
|
+
console.log('SUCCESS! Found result links.');
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log('Unknown response. Check dump.');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
console.error('Error:', e.message);
|
|
57
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const child_process_1 = require("child_process");
|
|
4
|
+
const query = "test";
|
|
5
|
+
const url = `https://lite.duckduckgo.com/lite/?q=${encodeURIComponent(query)}`;
|
|
6
|
+
console.log(`Testing Lite DDG: ${url}`);
|
|
7
|
+
try {
|
|
8
|
+
const cmd = `curl -s -L -A "Mozilla/5.0" "${url}"`;
|
|
9
|
+
const html = (0, child_process_1.execSync)(cmd, { encoding: 'utf-8' });
|
|
10
|
+
console.log('HTML Length:', html.length);
|
|
11
|
+
console.log('Snippet (first 2000 chars):');
|
|
12
|
+
console.log(html.substring(0, 2000));
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
console.error('Error:', e.message);
|
|
16
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Mentis CLI - An Agentic, Multi-Model CLI Coding Assistant
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
3
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
9
|
const ReplManager_1 = require("./repl/ReplManager");
|
|
10
|
+
/**
|
|
11
|
+
* Parse command line arguments
|
|
12
|
+
*
|
|
13
|
+
* @returns Parsed command and options
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```bash
|
|
17
|
+
* mentis --resume
|
|
18
|
+
* mentis -p "fix the bug"
|
|
19
|
+
* mentis --yolo
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
5
22
|
function parseArgs() {
|
|
6
23
|
const args = process.argv.slice(2);
|
|
7
24
|
const options = {
|
|
@@ -59,6 +76,11 @@ Commands (in REPL):
|
|
|
59
76
|
}
|
|
60
77
|
return { command, options };
|
|
61
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Main entry point for Mentis CLI
|
|
81
|
+
*
|
|
82
|
+
* Parses arguments and starts the REPL or update manager
|
|
83
|
+
*/
|
|
62
84
|
async function main() {
|
|
63
85
|
const { command, options } = parseArgs();
|
|
64
86
|
// Handle update command
|
|
@@ -72,6 +94,17 @@ async function main() {
|
|
|
72
94
|
const repl = new ReplManager_1.ReplManager(options);
|
|
73
95
|
await repl.start();
|
|
74
96
|
}
|
|
97
|
+
// Global error handlers to prevent silent crashes
|
|
98
|
+
process.on('uncaughtException', (error) => {
|
|
99
|
+
console.error('Uncaught Exception:', error.message);
|
|
100
|
+
console.error(error.stack);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
});
|
|
103
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
104
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
});
|
|
107
|
+
// Start the application
|
|
75
108
|
main().catch((error) => {
|
|
76
109
|
console.error('Fatal error:', error);
|
|
77
110
|
process.exit(1);
|
|
@@ -40,6 +40,7 @@ class JsonRpcClient {
|
|
|
40
40
|
constructor(command, args) {
|
|
41
41
|
this.sequence = 0;
|
|
42
42
|
this.pendingRequests = new Map();
|
|
43
|
+
this.REQUEST_TIMEOUT = 30000; // 30 seconds timeout
|
|
43
44
|
this.process = (0, child_process_1.spawn)(command, args, {
|
|
44
45
|
stdio: ['pipe', 'pipe', 'inherit'], // stdin, stdout, stderr
|
|
45
46
|
});
|
|
@@ -61,6 +62,10 @@ class JsonRpcClient {
|
|
|
61
62
|
const handler = this.pendingRequests.get(message.id);
|
|
62
63
|
if (handler) {
|
|
63
64
|
this.pendingRequests.delete(message.id);
|
|
65
|
+
// Clear timeout if present
|
|
66
|
+
if (handler.timeout) {
|
|
67
|
+
clearTimeout(handler.timeout);
|
|
68
|
+
}
|
|
64
69
|
if (message.error) {
|
|
65
70
|
handler.reject(new Error(message.error.message));
|
|
66
71
|
}
|
|
@@ -88,6 +93,16 @@ class JsonRpcClient {
|
|
|
88
93
|
};
|
|
89
94
|
return new Promise((resolve, reject) => {
|
|
90
95
|
this.pendingRequests.set(id, { resolve, reject });
|
|
96
|
+
// Set timeout to prevent indefinite hangs
|
|
97
|
+
const timeout = setTimeout(() => {
|
|
98
|
+
this.pendingRequests.delete(id);
|
|
99
|
+
reject(new Error(`MCP request timeout after ${this.REQUEST_TIMEOUT}ms`));
|
|
100
|
+
}, this.REQUEST_TIMEOUT);
|
|
101
|
+
// Store timeout with the request for cleanup
|
|
102
|
+
const handler = this.pendingRequests.get(id);
|
|
103
|
+
if (handler) {
|
|
104
|
+
handler.timeout = timeout;
|
|
105
|
+
}
|
|
91
106
|
try {
|
|
92
107
|
if (!this.process.stdin)
|
|
93
108
|
throw new Error('Stdin not available');
|
|
@@ -96,6 +111,7 @@ class JsonRpcClient {
|
|
|
96
111
|
}
|
|
97
112
|
catch (e) {
|
|
98
113
|
this.pendingRequests.delete(id);
|
|
114
|
+
clearTimeout(timeout);
|
|
99
115
|
reject(e);
|
|
100
116
|
}
|
|
101
117
|
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.McpConfigManager = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const os_1 = require("os");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
class McpConfigManager {
|
|
8
|
+
constructor() {
|
|
9
|
+
// Config path: ~/.mentis/mcp.json
|
|
10
|
+
this.configPath = (0, path_1.join)((0, os_1.homedir)(), '.mentis', 'mcp.json');
|
|
11
|
+
this.config = this.loadConfig();
|
|
12
|
+
}
|
|
13
|
+
loadConfig() {
|
|
14
|
+
if ((0, fs_1.existsSync)(this.configPath)) {
|
|
15
|
+
try {
|
|
16
|
+
const content = (0, fs_1.readFileSync)(this.configPath, 'utf-8');
|
|
17
|
+
return JSON.parse(content);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
console.warn('Failed to load MCP config, using defaults:', error);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// Default configuration with popular MCP servers
|
|
24
|
+
return this.getDefaultConfig();
|
|
25
|
+
}
|
|
26
|
+
getDefaultConfig() {
|
|
27
|
+
return {
|
|
28
|
+
servers: [
|
|
29
|
+
{
|
|
30
|
+
name: 'Exa Search',
|
|
31
|
+
command: 'npx',
|
|
32
|
+
args: ['-y', '@exa-labs/mcp-server-exa'],
|
|
33
|
+
description: 'Web search via Exa API (requires EXA_API_KEY)',
|
|
34
|
+
autoConnect: false,
|
|
35
|
+
env: {
|
|
36
|
+
EXA_API_KEY: process.env.EXA_API_KEY || ''
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'Memory',
|
|
41
|
+
command: 'npx',
|
|
42
|
+
args: ['-y', '@modelcontextprotocol/server-memory'],
|
|
43
|
+
description: 'Persistent memory storage for conversations',
|
|
44
|
+
autoConnect: false
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'Filesystem',
|
|
48
|
+
command: 'npx',
|
|
49
|
+
args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()],
|
|
50
|
+
description: 'Enhanced filesystem operations',
|
|
51
|
+
autoConnect: false
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'GitHub',
|
|
55
|
+
command: 'npx',
|
|
56
|
+
args: ['-y', '@modelcontextprotocol/server-github'],
|
|
57
|
+
description: 'GitHub repository management and operations',
|
|
58
|
+
autoConnect: false,
|
|
59
|
+
env: {
|
|
60
|
+
GITHUB_PERSONAL_ACCESS_TOKEN: process.env.GITHUB_PERSONAL_ACCESS_TOKEN || ''
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'Puppeteer',
|
|
65
|
+
command: 'npx',
|
|
66
|
+
args: ['-y', '@modelcontextprotocol/server-puppeteer'],
|
|
67
|
+
description: 'Web browser automation and scraping',
|
|
68
|
+
autoConnect: false
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'Brave Search',
|
|
72
|
+
command: 'npx',
|
|
73
|
+
args: ['-y', '@modelcontextprotocol/server-brave-search'],
|
|
74
|
+
description: 'Web search via Brave Search API',
|
|
75
|
+
autoConnect: false,
|
|
76
|
+
env: {
|
|
77
|
+
BRAVE_API_KEY: process.env.BRAVE_API_KEY || ''
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'Slack',
|
|
82
|
+
command: 'npx',
|
|
83
|
+
args: ['-y', '@modelcontextprotocol/server-slack'],
|
|
84
|
+
description: 'Slack workspace integration',
|
|
85
|
+
autoConnect: false,
|
|
86
|
+
env: {
|
|
87
|
+
SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN || ''
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
getConfig() {
|
|
94
|
+
return this.config;
|
|
95
|
+
}
|
|
96
|
+
saveConfig() {
|
|
97
|
+
try {
|
|
98
|
+
const dir = (0, path_1.join)((0, os_1.homedir)(), '.mentis');
|
|
99
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
100
|
+
require('fs').mkdirSync(dir, { recursive: true });
|
|
101
|
+
}
|
|
102
|
+
(0, fs_1.writeFileSync)(this.configPath, JSON.stringify(this.config, null, 2));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error('Failed to save MCP config:', error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
addServer(server) {
|
|
109
|
+
// Remove existing server with same name
|
|
110
|
+
this.config.servers = this.config.servers.filter(s => s.name !== server.name);
|
|
111
|
+
this.config.servers.push(server);
|
|
112
|
+
this.saveConfig();
|
|
113
|
+
}
|
|
114
|
+
removeServer(name) {
|
|
115
|
+
this.config.servers = this.config.servers.filter(s => s.name !== name);
|
|
116
|
+
this.saveConfig();
|
|
117
|
+
}
|
|
118
|
+
getServer(name) {
|
|
119
|
+
return this.config.servers.find(s => s.name === name);
|
|
120
|
+
}
|
|
121
|
+
getAutoConnectServers() {
|
|
122
|
+
return this.config.servers.filter(s => s.autoConnect === true);
|
|
123
|
+
}
|
|
124
|
+
updateServer(name, updates) {
|
|
125
|
+
const server = this.config.servers.find(s => s.name === name);
|
|
126
|
+
if (server) {
|
|
127
|
+
Object.assign(server, updates);
|
|
128
|
+
this.saveConfig();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
exports.McpConfigManager = McpConfigManager;
|